mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-05 08:05:56 +00:00
fix(cli): add namespace sync manifest endpoint
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
parent
c11a51c75f
commit
2babc0935b
11 changed files with 359 additions and 1 deletions
|
|
@ -0,0 +1,69 @@
|
|||
package com.iflytek.skillhub.controller.cli;
|
||||
|
||||
import com.iflytek.skillhub.controller.BaseApiController;
|
||||
import com.iflytek.skillhub.domain.namespace.NamespaceRole;
|
||||
import com.iflytek.skillhub.dto.ApiResponse;
|
||||
import com.iflytek.skillhub.dto.ApiResponseFactory;
|
||||
import com.iflytek.skillhub.dto.cli.CliNamespaceSyncResponse;
|
||||
import com.iflytek.skillhub.ratelimit.RateLimit;
|
||||
import com.iflytek.skillhub.service.cli.CliSkillAppService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* CLI namespace-scoped read endpoints.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/cli/v1/namespaces")
|
||||
public class CliNamespaceController extends BaseApiController {
|
||||
|
||||
private static final int DEFAULT_LIMIT = 100;
|
||||
private static final int MAX_LIMIT = 100;
|
||||
|
||||
private final CliSkillAppService cliSkillAppService;
|
||||
|
||||
public CliNamespaceController(CliSkillAppService cliSkillAppService, ApiResponseFactory responseFactory) {
|
||||
super(responseFactory);
|
||||
this.cliSkillAppService = cliSkillAppService;
|
||||
}
|
||||
|
||||
@GetMapping("/{namespace}/skills")
|
||||
@RateLimit(category = "skills", authenticated = 60, anonymous = 0)
|
||||
public ApiResponse<CliNamespaceSyncResponse> listSkills(
|
||||
@PathVariable String namespace,
|
||||
@RequestParam(required = false) String cursor,
|
||||
@RequestParam(defaultValue = "100") int limit,
|
||||
@RequestAttribute(value = "userId", required = false) String userId,
|
||||
@RequestAttribute(value = "userNsRoles", required = false) Map<Long, NamespaceRole> userNsRoles) {
|
||||
int page = parseCursor(cursor);
|
||||
int normalizedLimit = Math.min(Math.max(limit, 1), MAX_LIMIT);
|
||||
return ok("response.success.read", cliSkillAppService.listNamespaceSkills(
|
||||
namespace,
|
||||
page,
|
||||
normalizedLimit,
|
||||
userId,
|
||||
userNsRoles
|
||||
));
|
||||
}
|
||||
|
||||
private int parseCursor(String cursor) {
|
||||
if (cursor == null || cursor.isBlank()) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
int page = Integer.parseInt(cursor);
|
||||
if (page < 0) {
|
||||
throw new NumberFormatException("negative cursor");
|
||||
}
|
||||
return page;
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException("cursor must be a non-negative page number", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.iflytek.skillhub.dto.cli;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* Installable skill metadata used by the CLI namespace workspace synchronizer.
|
||||
*/
|
||||
public record CliNamespaceSyncItemResponse(
|
||||
String namespace,
|
||||
String slug,
|
||||
String version,
|
||||
Long versionId,
|
||||
String fingerprint,
|
||||
Instant updatedAt,
|
||||
String visibility,
|
||||
String downloadUrl
|
||||
) {}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.iflytek.skillhub.dto.cli;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Cursor-shaped response consumed by the CLI namespace workspace synchronizer.
|
||||
*/
|
||||
public record CliNamespaceSyncResponse(
|
||||
List<CliNamespaceSyncItemResponse> items,
|
||||
String nextCursor
|
||||
) {}
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
package com.iflytek.skillhub.service.cli;
|
||||
|
||||
import com.iflytek.skillhub.domain.namespace.NamespaceRole;
|
||||
import com.iflytek.skillhub.domain.skill.Skill;
|
||||
import com.iflytek.skillhub.domain.skill.SkillVisibility;
|
||||
import com.iflytek.skillhub.domain.skill.service.SkillDownloadService;
|
||||
import com.iflytek.skillhub.domain.skill.service.SkillPublishService;
|
||||
import com.iflytek.skillhub.domain.skill.service.SkillQueryService;
|
||||
import com.iflytek.skillhub.domain.skill.validation.PackageEntry;
|
||||
import com.iflytek.skillhub.dto.SkillSummaryResponse;
|
||||
import com.iflytek.skillhub.dto.cli.CliDeleteResponse;
|
||||
import com.iflytek.skillhub.dto.cli.CliDryRunResponse;
|
||||
import com.iflytek.skillhub.dto.cli.CliNamespaceSyncItemResponse;
|
||||
import com.iflytek.skillhub.dto.cli.CliNamespaceSyncResponse;
|
||||
import com.iflytek.skillhub.dto.cli.CliPublishResponse;
|
||||
import com.iflytek.skillhub.dto.cli.CliResolveResponse;
|
||||
import com.iflytek.skillhub.service.AuditRequestContext;
|
||||
|
|
@ -16,6 +18,8 @@ import com.iflytek.skillhub.service.SkillDeleteAppService;
|
|||
import com.iflytek.skillhub.service.SkillSearchAppService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
|
@ -82,6 +86,42 @@ public class CliSkillAppService {
|
|||
);
|
||||
}
|
||||
|
||||
public CliNamespaceSyncResponse listNamespaceSkills(
|
||||
String namespace,
|
||||
int page,
|
||||
int limit,
|
||||
String userId,
|
||||
Map<Long, NamespaceRole> userNsRoles) {
|
||||
Map<Long, NamespaceRole> roles = userNsRoles != null ? userNsRoles : Map.of();
|
||||
Page<Skill> skills = skillQueryService.listInstallableSkillsByNamespace(
|
||||
namespace, userId, roles, PageRequest.of(page, limit));
|
||||
|
||||
List<CliNamespaceSyncItemResponse> items = skills.getContent().stream()
|
||||
.map(skill -> toNamespaceSyncItem(skill, namespace, userId, roles))
|
||||
.toList();
|
||||
String nextCursor = skills.hasNext() ? String.valueOf(page + 1) : null;
|
||||
return new CliNamespaceSyncResponse(items, nextCursor);
|
||||
}
|
||||
|
||||
private CliNamespaceSyncItemResponse toNamespaceSyncItem(
|
||||
Skill skill,
|
||||
String namespace,
|
||||
String userId,
|
||||
Map<Long, NamespaceRole> userNsRoles) {
|
||||
SkillQueryService.ResolvedVersionDTO resolved = skillQueryService.resolveVersion(
|
||||
namespace, skill.getSlug(), null, null, null, userId, userNsRoles);
|
||||
return new CliNamespaceSyncItemResponse(
|
||||
namespace,
|
||||
skill.getSlug(),
|
||||
resolved.version(),
|
||||
resolved.versionId(),
|
||||
resolved.fingerprint(),
|
||||
skill.getUpdatedAt(),
|
||||
skill.getVisibility().name(),
|
||||
resolved.downloadUrl()
|
||||
);
|
||||
}
|
||||
|
||||
public ResponseEntity<InputStreamResource> downloadLatest(String namespace, String slug, HttpServletRequest request) {
|
||||
String userId = (String) request.getAttribute("userId");
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
|||
|
|
@ -132,6 +132,38 @@ class CliSkillControllerTest {
|
|||
verify(apiTokenService).touchLastUsed(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
void namespaceSyncReturnsCursorResponseForValidBearer() throws Exception {
|
||||
ApiToken token = new ApiToken("sync-user", "cli", "sk_test", "hash", "[]");
|
||||
UserAccount user = new UserAccount("sync-user", "Sync User", "sync@example.com", "");
|
||||
var response = new com.iflytek.skillhub.dto.cli.CliNamespaceSyncResponse(
|
||||
List.of(new com.iflytek.skillhub.dto.cli.CliNamespaceSyncItemResponse(
|
||||
"team-a", "demo", "1.0.0", 42L, "sha256:fingerprint",
|
||||
java.time.Instant.parse("2026-08-20T00:00:00Z"), "NAMESPACE_ONLY",
|
||||
"/api/v1/skills/team-a/demo/versions/1.0.0/download"
|
||||
)),
|
||||
"2"
|
||||
);
|
||||
|
||||
given(apiTokenService.validateToken("sync-token")).willReturn(Optional.of(token));
|
||||
given(userAccountRepository.findById("sync-user")).willReturn(Optional.of(user));
|
||||
given(userRoleBindingRepository.findByUserId("sync-user")).willReturn(List.of());
|
||||
given(cliSkillAppService.listNamespaceSkills("team-a", 1, 25, "sync-user", Map.of()))
|
||||
.willReturn(response);
|
||||
|
||||
mockMvc.perform(get("/api/cli/v1/namespaces/team-a/skills")
|
||||
.param("cursor", "1")
|
||||
.param("limit", "25")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer sync-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.items[0].namespace").value("team-a"))
|
||||
.andExpect(jsonPath("$.data.items[0].slug").value("demo"))
|
||||
.andExpect(jsonPath("$.data.items[0].version").value("1.0.0"))
|
||||
.andExpect(jsonPath("$.data.nextCursor").value("2"));
|
||||
|
||||
verify(cliSkillAppService).listNamespaceSkills("team-a", 1, 25, "sync-user", Map.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveReturnsCliResolveResponse() throws Exception {
|
||||
given(cliSkillAppService.resolve("global", "demo", null, null, null)).willReturn(
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
|
@ -222,6 +224,30 @@ class CliSkillAppServiceTest {
|
|||
assertEquals("abc123", response.fingerprint());
|
||||
}
|
||||
|
||||
@Test
|
||||
void listNamespaceSkills_mapsInstallableVersionsToCliManifest() {
|
||||
Skill skill = new Skill(7L, "demo", "user-1", SkillVisibility.NAMESPACE_ONLY);
|
||||
setField(skill, "id", 10L);
|
||||
skill.setLatestVersionId(42L);
|
||||
skill.setUpdatedBy("user-1");
|
||||
|
||||
given(skillQueryService.listInstallableSkillsByNamespace(
|
||||
eq("team-a"), eq("user-1"), eq(Map.of()), eq(PageRequest.of(0, 100))))
|
||||
.willReturn(new PageImpl<>(List.of(skill), PageRequest.of(0, 100), 1));
|
||||
given(skillQueryService.resolveVersion("team-a", "demo", null, null, null, "user-1", Map.of()))
|
||||
.willReturn(new SkillQueryService.ResolvedVersionDTO(
|
||||
10L, "team-a", "demo", "1.0.0", 42L, "sha256:fingerprint", null,
|
||||
"/api/v1/skills/team-a/demo/versions/1.0.0/download"));
|
||||
|
||||
var result = service.listNamespaceSkills("team-a", 0, 100, "user-1", Map.of());
|
||||
|
||||
assertEquals(1, result.items().size());
|
||||
assertEquals("demo", result.items().getFirst().slug());
|
||||
assertEquals("1.0.0", result.items().getFirst().version());
|
||||
assertEquals("sha256:fingerprint", result.items().getFirst().fingerprint());
|
||||
assertNull(result.nextCursor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteRemote_delegatesToDeleteAppService() {
|
||||
var auditContext = new AuditRequestContext("127.0.0.1", "CLI/1.0");
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ public class RouteSecurityPolicyRegistry {
|
|||
RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/web/namespaces/*"),
|
||||
RouteAuthorizationPolicy.authenticated(null, "/api/v1/admin/**"),
|
||||
RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/cli/v1/auth/whoami"),
|
||||
RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/cli/v1/namespaces/*/skills"),
|
||||
RouteAuthorizationPolicy.permitAll(HttpMethod.GET, "/api/cli/v1/skills/search"),
|
||||
RouteAuthorizationPolicy.permitAll(HttpMethod.GET, "/api/cli/v1/skills/*/*/resolve"),
|
||||
RouteAuthorizationPolicy.permitAll(HttpMethod.GET, "/api/cli/v1/skills/*/*/download"),
|
||||
|
|
@ -133,6 +134,7 @@ public class RouteSecurityPolicyRegistry {
|
|||
ApiTokenPolicy.require(HttpMethod.POST, "/api/web/skills/*/publish", "skill:publish"),
|
||||
ApiTokenPolicy.require(HttpMethod.POST, "/api/v1/publish", "skill:publish"),
|
||||
ApiTokenPolicy.allow(HttpMethod.GET, "/api/cli/v1/auth/whoami"),
|
||||
ApiTokenPolicy.allow(HttpMethod.GET, "/api/cli/v1/namespaces/*/skills"),
|
||||
ApiTokenPolicy.allow(HttpMethod.GET, "/api/cli/v1/skills/search"),
|
||||
ApiTokenPolicy.allow(HttpMethod.GET, "/api/cli/v1/skills/*/*/resolve"),
|
||||
ApiTokenPolicy.allow(HttpMethod.GET, "/api/cli/v1/skills/*/*/download"),
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ class RouteSecurityPolicyRegistryTest {
|
|||
@Test
|
||||
void apiTokenPolicySupportsNativeCliRoutes() {
|
||||
assertTrue(registry.authorizeApiToken("GET", "/api/cli/v1/auth/whoami", Set.of()).allowed());
|
||||
assertTrue(registry.authorizeApiToken("GET", "/api/cli/v1/namespaces/team-a/skills", Set.of()).allowed());
|
||||
assertTrue(registry.authorizeApiToken("GET", "/api/cli/v1/skills/search", Set.of()).allowed());
|
||||
assertTrue(registry.authorizeApiToken("GET", "/api/cli/v1/skills/global/demo/resolve", Set.of()).allowed());
|
||||
assertFalse(registry.authorizeApiToken("POST", "/api/cli/v1/skills/global/publish", Set.of()).allowed());
|
||||
|
|
@ -107,6 +108,16 @@ class RouteSecurityPolicyRegistryTest {
|
|||
assertTrue(registry.authorizeApiToken("DELETE", "/api/cli/v1/skills/global/demo", Set.of("skill:delete")).allowed());
|
||||
}
|
||||
|
||||
@Test
|
||||
void routeAuthorizationRequiresAuthenticationForNativeCliNamespaceSync() {
|
||||
boolean matched = registry.authorizationPolicies().stream()
|
||||
.anyMatch(policy -> policy.method() == HttpMethod.GET
|
||||
&& "/api/cli/v1/namespaces/*/skills".equals(policy.pattern())
|
||||
&& policy.accessLevel() == RouteSecurityPolicyRegistry.AccessLevel.AUTHENTICATED);
|
||||
|
||||
assertTrue(matched);
|
||||
}
|
||||
|
||||
@Test
|
||||
void routeAuthorizationProtectsNativeCliRemoteDeleteByAuthenticationNotSuperAdminRole() {
|
||||
boolean matched = registry.authorizationPolicies().stream()
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import java.util.Objects;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -286,6 +287,47 @@ public class SkillQueryService {
|
|||
return new PageImpl<>(pageContent, pageable, accessibleSkills.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists only active, visible skills whose latest version can be installed.
|
||||
*
|
||||
* <p>This is intentionally separate from {@link #listSkillsByNamespace}:
|
||||
* the portal discovery method also exposes skills without a published
|
||||
* version, while the CLI sync manifest must contain concrete downloadable
|
||||
* versions. Filtering happens before pagination so cursors remain stable.
|
||||
*/
|
||||
public Page<Skill> listInstallableSkillsByNamespace(
|
||||
String namespaceSlug,
|
||||
String currentUserId,
|
||||
Map<Long, NamespaceRole> userNsRoles,
|
||||
Pageable pageable) {
|
||||
|
||||
Namespace namespace = findNamespace(namespaceSlug);
|
||||
List<Skill> accessibleSkills = skillRepository
|
||||
.findByNamespaceIdAndStatus(namespace.getId(), SkillStatus.ACTIVE)
|
||||
.stream()
|
||||
.filter(skill -> visibilityChecker.canAccess(skill, currentUserId, userNsRoles))
|
||||
.toList();
|
||||
|
||||
Map<Long, SkillVersion> latestVersions = skillVersionRepository.findByIdIn(
|
||||
accessibleSkills.stream()
|
||||
.map(Skill::getLatestVersionId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(SkillVersion::getId, Function.identity()));
|
||||
|
||||
List<Skill> installableSkills = accessibleSkills.stream()
|
||||
.filter(skill -> SkillInstallability.isInstallableVersion(latestVersions.get(skill.getLatestVersionId())))
|
||||
.sorted(Comparator.comparing(Skill::getSlug)
|
||||
.thenComparing(Skill::getId, Comparator.nullsLast(Comparator.naturalOrder())))
|
||||
.toList();
|
||||
|
||||
int start = Math.min((int) pageable.getOffset(), installableSkills.size());
|
||||
int end = Math.min(start + pageable.getPageSize(), installableSkills.size());
|
||||
return new PageImpl<>(installableSkills.subList(start, end), pageable, installableSkills.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metadata for a visible version, including the stored manifest and
|
||||
* parsed metadata payload.
|
||||
|
|
|
|||
|
|
@ -239,6 +239,46 @@ class SkillQueryServiceTest {
|
|||
verify(skillRepository).findByNamespaceIdAndStatus(1L, SkillStatus.ACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void installableNamespaceDiscoveryFiltersBeforePaginationAndSortsBySlug() throws Exception {
|
||||
Namespace namespace = new Namespace("team-a", "Team A", "owner-1");
|
||||
setId(namespace, 1L);
|
||||
Skill ready = new Skill(1L, "ready", "owner-1", SkillVisibility.NAMESPACE_ONLY);
|
||||
setId(ready, 10L);
|
||||
ready.setLatestVersionId(100L);
|
||||
Skill draft = new Skill(1L, "draft", "owner-1", SkillVisibility.NAMESPACE_ONLY);
|
||||
setId(draft, 11L);
|
||||
draft.setLatestVersionId(101L);
|
||||
Skill publicReady = new Skill(1L, "public-ready", "owner-2", SkillVisibility.PUBLIC);
|
||||
setId(publicReady, 12L);
|
||||
publicReady.setLatestVersionId(102L);
|
||||
|
||||
SkillVersion readyVersion = new SkillVersion(10L, "1.0.0", "owner-1");
|
||||
setId(readyVersion, 100L);
|
||||
readyVersion.setStatus(SkillVersionStatus.PUBLISHED);
|
||||
readyVersion.setDownloadReady(true);
|
||||
SkillVersion draftVersion = new SkillVersion(11L, "1.0.0", "owner-1");
|
||||
setId(draftVersion, 101L);
|
||||
draftVersion.setStatus(SkillVersionStatus.DRAFT);
|
||||
SkillVersion publicReadyVersion = new SkillVersion(12L, "2.0.0", "owner-2");
|
||||
setId(publicReadyVersion, 102L);
|
||||
publicReadyVersion.setStatus(SkillVersionStatus.PUBLISHED);
|
||||
publicReadyVersion.setDownloadReady(true);
|
||||
|
||||
when(namespaceRepository.findBySlug("team-a")).thenReturn(Optional.of(namespace));
|
||||
when(skillRepository.findByNamespaceIdAndStatus(1L, SkillStatus.ACTIVE))
|
||||
.thenReturn(List.of(ready, draft, publicReady));
|
||||
when(skillVersionRepository.findByIdIn(List.of(100L, 101L, 102L)))
|
||||
.thenReturn(List.of(readyVersion, draftVersion, publicReadyVersion));
|
||||
|
||||
Page<Skill> result = service.listInstallableSkillsByNamespace(
|
||||
"team-a", "viewer", Map.of(1L, NamespaceRole.MEMBER), PageRequest.of(0, 1));
|
||||
|
||||
assertEquals(2, result.getTotalElements());
|
||||
assertEquals("public-ready", result.getContent().getFirst().getSlug());
|
||||
assertTrue(result.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
void versionContentRejectsPublicArchivedSkillForNonManager() throws Exception {
|
||||
Namespace namespace = new Namespace("global", "Global", "owner-1");
|
||||
|
|
|
|||
68
web/src/api/generated/schema.d.ts
vendored
68
web/src/api/generated/schema.d.ts
vendored
|
|
@ -3348,6 +3348,22 @@ export interface paths {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/cli/v1/namespaces/{namespace}/skills": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["listSkills_1"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/cli/v1/auth/whoami": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -4347,6 +4363,8 @@ export interface components {
|
|||
namespace?: string;
|
||||
/** Format: date-time */
|
||||
updatedAt?: string;
|
||||
ownerId?: string;
|
||||
ownerDisplayName?: string;
|
||||
canSubmitPromotion?: boolean;
|
||||
headlineVersion?: components["schemas"]["SkillLifecycleVersionResponse"];
|
||||
publishedVersion?: components["schemas"]["SkillLifecycleVersionResponse"];
|
||||
|
|
@ -5330,6 +5348,31 @@ export interface components {
|
|||
/** Format: int32 */
|
||||
limit?: number;
|
||||
};
|
||||
ApiResponseCliNamespaceSyncResponse: {
|
||||
/** Format: int32 */
|
||||
code?: number;
|
||||
msg?: string;
|
||||
data?: components["schemas"]["CliNamespaceSyncResponse"];
|
||||
/** Format: date-time */
|
||||
timestamp?: string;
|
||||
requestId?: string;
|
||||
};
|
||||
CliNamespaceSyncItemResponse: {
|
||||
namespace?: string;
|
||||
slug?: string;
|
||||
version?: string;
|
||||
/** Format: int64 */
|
||||
versionId?: number;
|
||||
fingerprint?: string;
|
||||
/** Format: date-time */
|
||||
updatedAt?: string;
|
||||
visibility?: string;
|
||||
downloadUrl?: string;
|
||||
};
|
||||
CliNamespaceSyncResponse: {
|
||||
items?: components["schemas"]["CliNamespaceSyncItemResponse"][];
|
||||
nextCursor?: string;
|
||||
};
|
||||
ApiResponseCliWhoAmIResponse: {
|
||||
/** Format: int32 */
|
||||
code?: number;
|
||||
|
|
@ -11357,6 +11400,31 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
listSkills_1: {
|
||||
parameters: {
|
||||
query?: {
|
||||
cursor?: string;
|
||||
limit?: number;
|
||||
};
|
||||
header?: never;
|
||||
path: {
|
||||
namespace: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description OK */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"*/*": components["schemas"]["ApiResponseCliNamespaceSyncResponse"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
whoami_1: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue