@kb-labs/marketplace-entry 2.111.0 → 2.112.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/install.js +71 -27
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/plugins/disable.js +71 -27
- package/dist/commands/plugins/disable.js.map +1 -1
- package/dist/commands/plugins/doctor.js +71 -21
- package/dist/commands/plugins/doctor.js.map +1 -1
- package/dist/commands/plugins/enable.js +71 -27
- package/dist/commands/plugins/enable.js.map +1 -1
- package/dist/commands/plugins/link.js +71 -27
- package/dist/commands/plugins/link.js.map +1 -1
- package/dist/commands/plugins/list.js +71 -21
- package/dist/commands/plugins/list.js.map +1 -1
- package/dist/commands/plugins/unlink.js +71 -27
- package/dist/commands/plugins/unlink.js.map +1 -1
- package/dist/commands/sync.js +71 -27
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/uninstall.js +71 -27
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +71 -27
- package/dist/commands/update.js.map +1 -1
- package/package.json +5 -4
|
@@ -1,45 +1,89 @@
|
|
|
1
1
|
import { defineCommand, validationError, handleError, useEnv } from '@kb-labs/sdk';
|
|
2
|
+
import { SessionManager, CredentialsManager } from '@kb-labs/cli-runtime/gateway';
|
|
2
3
|
import { findProjectConfigRoot } from '@kb-labs/core-workspace';
|
|
3
4
|
|
|
4
5
|
// src/commands/plugins/unlink.ts
|
|
5
6
|
var DEFAULT_GATEWAY_URL = "http://127.0.0.1:4000";
|
|
6
7
|
var MARKETPLACE_PREFIX = "/api/v1/marketplace";
|
|
7
8
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
8
|
-
function
|
|
9
|
+
async function loadAuth() {
|
|
10
|
+
const sessionManager = new SessionManager();
|
|
11
|
+
const session = await sessionManager.load();
|
|
12
|
+
if (session) {
|
|
13
|
+
const current2 = sessionManager.isExpired(session) ? await sessionManager.refresh(session) : session;
|
|
14
|
+
return authState(current2, () => sessionManager.refresh(current2));
|
|
15
|
+
}
|
|
16
|
+
const credentialsManager = new CredentialsManager();
|
|
17
|
+
const credentials = await credentialsManager.load();
|
|
18
|
+
if (!credentials) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const current = credentialsManager.isExpired(credentials) ? await credentialsManager.refresh(credentials) : credentials;
|
|
22
|
+
return authState(current, () => credentialsManager.refresh(current));
|
|
23
|
+
}
|
|
24
|
+
function authState(credentials, refresh) {
|
|
25
|
+
return {
|
|
26
|
+
gatewayUrl: credentials.gatewayUrl,
|
|
27
|
+
accessToken: credentials.accessToken,
|
|
28
|
+
refresh: async () => {
|
|
29
|
+
const updated = await refresh();
|
|
30
|
+
return authState(updated, refresh);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function getBaseUrl(gatewayUrl) {
|
|
9
35
|
const marketplaceUrl = useEnv("KB_MARKETPLACE_URL");
|
|
10
36
|
if (marketplaceUrl) {
|
|
11
37
|
return `${marketplaceUrl}${MARKETPLACE_PREFIX}`;
|
|
12
38
|
}
|
|
13
|
-
const gateway = useEnv("KB_GATEWAY_URL") ?? DEFAULT_GATEWAY_URL;
|
|
39
|
+
const gateway = gatewayUrl ?? useEnv("KB_GATEWAY_URL") ?? DEFAULT_GATEWAY_URL;
|
|
14
40
|
return `${gateway}${MARKETPLACE_PREFIX}`;
|
|
15
41
|
}
|
|
16
|
-
async function
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
headers
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
async function request(method, path, body, params) {
|
|
43
|
+
let auth = await loadAuth();
|
|
44
|
+
const url = new URL(`${getBaseUrl(auth?.gatewayUrl)}${path}`);
|
|
45
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
48
|
+
try {
|
|
49
|
+
const headers = {};
|
|
50
|
+
if (body) {
|
|
51
|
+
headers["Content-Type"] = "application/json";
|
|
52
|
+
}
|
|
53
|
+
if (auth) {
|
|
54
|
+
headers.Authorization = `Bearer ${auth.accessToken}`;
|
|
55
|
+
}
|
|
56
|
+
const response = await fetch(url.toString(), {
|
|
57
|
+
method,
|
|
58
|
+
headers,
|
|
59
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
60
|
+
signal: controller.signal
|
|
61
|
+
});
|
|
62
|
+
if (response.status === 401 && auth && attempt === 0) {
|
|
63
|
+
auth = await auth.refresh();
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const text = await response.text();
|
|
68
|
+
throw new Error(`Marketplace ${path} failed (${response.status}): ${text}`);
|
|
69
|
+
}
|
|
70
|
+
if (response.status === 204) {
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
return await response.json();
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (err.name === "AbortError") {
|
|
76
|
+
throw new Error(`Marketplace ${path} timed out \u2014 is the marketplace service running? (kb-dev start marketplace)`);
|
|
77
|
+
}
|
|
78
|
+
throw err;
|
|
79
|
+
} finally {
|
|
80
|
+
clearTimeout(timer);
|
|
38
81
|
}
|
|
39
|
-
throw err;
|
|
40
|
-
} finally {
|
|
41
|
-
clearTimeout(timer);
|
|
42
82
|
}
|
|
83
|
+
throw new Error(`Marketplace ${path} failed after refreshing authentication`);
|
|
84
|
+
}
|
|
85
|
+
async function post(path, body) {
|
|
86
|
+
return request("POST", path, body);
|
|
43
87
|
}
|
|
44
88
|
var SCOPE_FLAG_CHOICES = ["platform", "project"];
|
|
45
89
|
async function resolveCliScope(cwd, flag) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/http.ts","../../../src/scope.ts","../../../src/commands/plugins/unlink.ts"],"names":["projectRoot"],"mappings":"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","file":"unlink.js","sourcesContent":["/**\n * HTTP client for marketplace service via Gateway.\n */\n\nimport { useEnv } from '@kb-labs/sdk';\n\nconst DEFAULT_GATEWAY_URL = 'http://127.0.0.1:4000';\nconst MARKETPLACE_PREFIX = '/api/v1/marketplace';\nconst FETCH_TIMEOUT_MS = 30_000;\n\nfunction getBaseUrl(): string {\n const marketplaceUrl = useEnv('KB_MARKETPLACE_URL');\n if (marketplaceUrl) {\n return `${marketplaceUrl}${MARKETPLACE_PREFIX}`;\n }\n const gateway = useEnv('KB_GATEWAY_URL') ?? DEFAULT_GATEWAY_URL;\n return `${gateway}${MARKETPLACE_PREFIX}`;\n}\n\nexport async function post<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n // 204 No Content (e.g. unlink, uninstall) has no body — don't call\n // res.json() or it throws \"Unexpected end of JSON input\".\n if (res.status === 204) { return undefined as T; }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function patch<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function del<T = unknown>(path: string, body?: Record<string, unknown>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'DELETE',\n headers: body ? { 'Content-Type': 'application/json' } : {},\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n if (res.status === 204) { return undefined as T; }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function get<T = unknown>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${getBaseUrl()}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {url.searchParams.set(k, v);}\n }\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url.toString(), { signal: controller.signal });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n","/**\n * @module @kb-labs/marketplace-entry/scope\n *\n * Client-side scope resolution for marketplace CLI commands. Uses the\n * canonical `findProjectConfigRoot` helper from `@kb-labs/core-workspace`\n * so the detection rules (which filenames count, how walk-up stops) match\n * what the config loader and the marketplace daemon enforce.\n *\n * Rules:\n * - `project` is the default if cwd (or any ancestor up to the filesystem\n * root) contains `.kb/kb.config.{json,jsonc}`.\n * - Otherwise the default is `platform`.\n * - `--scope` always overrides detection.\n * - For scope=\"project\" we return the absolute projectRoot so the daemon\n * doesn't need to re-discover it.\n */\n\nimport { findProjectConfigRoot } from '@kb-labs/core-workspace';\nimport type { MarketplaceScope, MarketplaceQueryScope } from '@kb-labs/marketplace-contracts';\n\nexport const SCOPE_FLAG_CHOICES: readonly MarketplaceScope[] = ['platform', 'project'];\nexport const QUERY_SCOPE_FLAG_CHOICES: readonly MarketplaceQueryScope[] = ['platform', 'project', 'all'];\n\nexport interface ResolvedCliScope {\n scope: MarketplaceScope;\n projectRoot?: string;\n /** How the scope was determined — surfaces in --verbose logs. */\n reason: 'flag' | 'auto-detect' | 'fallback';\n}\n\nexport interface ResolvedCliQueryScope extends Omit<ResolvedCliScope, 'scope'> {\n scope: MarketplaceQueryScope;\n}\n\n/**\n * Resolve the effective scope for a mutating command (`link`, `unlink`,\n * `install`, ...). `flag` is the value of `--scope` (if supplied). The\n * helper never returns `'all'` for mutating commands — callers restrict\n * choices via `SCOPE_FLAG_CHOICES`.\n */\nexport async function resolveCliScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliScope> {\n if (flag) {\n assertMutatingScope(flag);\n if (flag === 'project') {\n const projectRoot = await findProjectConfigRoot(cwd);\n if (!projectRoot) {\n throw new CliScopeError(\n 'SCOPE_PROJECT_ROOT_NOT_FOUND',\n `--scope=project requires a .kb/kb.config.{json,jsonc} ancestor of ${cwd} — none found.`,\n );\n }\n return { scope: 'project', projectRoot, reason: 'flag' };\n }\n return { scope: 'platform', reason: 'flag' };\n }\n\n const projectRoot = await findProjectConfigRoot(cwd);\n if (projectRoot) {\n return { scope: 'project', projectRoot, reason: 'auto-detect' };\n }\n return { scope: 'platform', reason: 'fallback' };\n}\n\n/**\n * Resolve the effective scope for a read-only command (`list`). Accepts\n * `'all'` as an explicit flag value.\n */\nexport async function resolveCliQueryScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliQueryScope> {\n if (flag === 'all') {\n const projectRoot = await findProjectConfigRoot(cwd);\n return { scope: 'all', projectRoot, reason: 'flag' };\n }\n const base = await resolveCliScope(cwd, flag);\n return { scope: base.scope, projectRoot: base.projectRoot, reason: base.reason };\n}\n\nfunction assertMutatingScope(value: string): asserts value is MarketplaceScope {\n if (!SCOPE_FLAG_CHOICES.includes(value as MarketplaceScope)) {\n throw new CliScopeError(\n 'SCOPE_INVALID',\n `--scope must be one of: ${SCOPE_FLAG_CHOICES.join(', ')} (got ${JSON.stringify(value)})`,\n );\n }\n}\n\nexport class CliScopeError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n this.name = 'CliScopeError';\n }\n}\n\n/**\n * Build the body-payload fragment expected by `parseMutatingScope` on the\n * API side: `{ scope, projectRoot? }`. Kept as a helper so every command\n * sends the same shape and nothing drifts out-of-sync with the server.\n */\nexport function scopeBody(ctx: ResolvedCliScope | ResolvedCliQueryScope): {\n scope: MarketplaceScope | MarketplaceQueryScope;\n projectRoot?: string;\n} {\n return {\n scope: ctx.scope,\n ...(ctx.projectRoot ? { projectRoot: ctx.projectRoot } : {}),\n };\n}\n","import { defineCommand, validationError, handleError, type PluginContextV3, type CommandResult } from '@kb-labs/sdk';\nimport { post } from '../../http.js';\nimport { resolveCliScope, scopeBody, CliScopeError } from '../../scope.js';\n\ninterface UnlinkFlags {\n json?: boolean;\n scope?: string;\n 'dry-run'?: boolean;\n}\n\ninterface UnlinkInput {\n argv?: string[];\n flags?: UnlinkFlags;\n}\n\nexport default defineCommand<unknown, UnlinkInput, { packageId: string; scope: string }>({\n id: 'marketplace:plugins:unlink',\n description: 'Unlink a plugin',\n\n handler: {\n async intent(_ctx: PluginContextV3, input: UnlinkInput) {\n const packageId = input.argv?.[0] ?? '(unknown)';\n return {\n summary: `Unlink plugin \"${packageId}\"`,\n operations: [{ type: 'delete' as const, resource: 'plugin-link', details: { packageId } }],\n };\n },\n\n async execute(ctx: PluginContextV3, input: UnlinkInput): Promise<CommandResult<{ packageId: string; scope: string }>> {\n const packageId = input.argv?.[0];\n const flags = (input.flags ?? input) as UnlinkFlags;\n\n if (!packageId) {\n validationError(ctx, 'Specify a package ID to unlink', 'Usage: kb marketplace plugins unlink <plugin-id>', flags.json);\n return { ok: false, error: 'A package id is required', result: { packageId: '', scope: '' } };\n }\n\n let scopeCtx;\n try {\n scopeCtx = await resolveCliScope(ctx.cwd, flags.scope);\n } catch (err) {\n if (err instanceof CliScopeError) {\n validationError(ctx, err.message, undefined, flags.json);\n } else {\n handleError(ctx, err, flags.json);\n }\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { packageId: '', scope: '' } };\n }\n\n try {\n await post(`/packages/unlink`, { packageId, ...scopeBody(scopeCtx) });\n if (flags.json) {\n ctx.ui?.json?.({ ok: true, packageId, scope: scopeCtx.scope });\n } else {\n ctx.ui?.success?.(`Unlinked ${packageId} (${scopeCtx.scope})`);\n }\n return { ok: true, result: { packageId, scope: scopeCtx.scope } };\n } catch (err) {\n handleError(ctx, err, flags.json);\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { packageId: '', scope: '' } };\n }\n },\n },\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/http.ts","../../../src/scope.ts","../../../src/commands/plugins/unlink.ts"],"names":["current","projectRoot"],"mappings":"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","file":"unlink.js","sourcesContent":["/**\n * HTTP client for marketplace service via Gateway.\n */\n\nimport { useEnv } from '@kb-labs/sdk';\nimport { CredentialsManager, SessionManager } from '@kb-labs/cli-runtime/gateway';\nimport type { GatewayCredentials, SessionCredentials } from '@kb-labs/cli-runtime/gateway';\n\nconst DEFAULT_GATEWAY_URL = 'http://127.0.0.1:4000';\nconst MARKETPLACE_PREFIX = '/api/v1/marketplace';\nconst FETCH_TIMEOUT_MS = 30_000;\n\ntype AuthState = {\n gatewayUrl: string;\n accessToken: string;\n refresh: () => Promise<AuthState>;\n};\n\nasync function loadAuth(): Promise<AuthState | null> {\n const sessionManager = new SessionManager();\n const session = await sessionManager.load();\n if (session) {\n const current = sessionManager.isExpired(session) ? await sessionManager.refresh(session) : session;\n return authState(current, () => sessionManager.refresh(current));\n }\n\n const credentialsManager = new CredentialsManager();\n const credentials = await credentialsManager.load();\n if (!credentials) {\n return null;\n }\n const current = credentialsManager.isExpired(credentials) ? await credentialsManager.refresh(credentials) : credentials;\n return authState(current, () => credentialsManager.refresh(current));\n}\n\nfunction authState(credentials: GatewayCredentials | SessionCredentials, refresh: () => Promise<GatewayCredentials | SessionCredentials>): AuthState {\n return {\n gatewayUrl: credentials.gatewayUrl,\n accessToken: credentials.accessToken,\n refresh: async () => {\n const updated = await refresh();\n return authState(updated, refresh);\n },\n };\n}\n\nfunction getBaseUrl(gatewayUrl?: string): string {\n const marketplaceUrl = useEnv('KB_MARKETPLACE_URL');\n if (marketplaceUrl) {\n return `${marketplaceUrl}${MARKETPLACE_PREFIX}`;\n }\n const gateway = gatewayUrl ?? useEnv('KB_GATEWAY_URL') ?? DEFAULT_GATEWAY_URL;\n return `${gateway}${MARKETPLACE_PREFIX}`;\n}\n\nasync function request<T>(method: string, path: string, body?: Record<string, unknown>, params?: Record<string, string>): Promise<T> {\n let auth = await loadAuth();\n const url = new URL(`${getBaseUrl(auth?.gatewayUrl)}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) { url.searchParams.set(key, value); }\n }\n\n for (let attempt = 0; attempt < 2; attempt += 1) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const headers: Record<string, string> = {};\n if (body) { headers['Content-Type'] = 'application/json'; }\n if (auth) { headers.Authorization = `Bearer ${auth.accessToken}`; }\n const response = await fetch(url.toString(), {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n if (response.status === 401 && auth && attempt === 0) {\n auth = await auth.refresh();\n continue;\n }\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Marketplace ${path} failed (${response.status}): ${text}`);\n }\n if (response.status === 204) { return undefined as T; }\n return await response.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n }\n throw new Error(`Marketplace ${path} failed after refreshing authentication`);\n}\n\nexport async function post<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n return request<T>('POST', path, body);\n}\n\nexport async function patch<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n return request<T>('PATCH', path, body);\n}\n\nexport async function del<T = unknown>(path: string, body?: Record<string, unknown>): Promise<T> {\n return request<T>('DELETE', path, body);\n}\n\nexport async function get<T = unknown>(path: string, params?: Record<string, string>): Promise<T> {\n return request<T>('GET', path, undefined, params);\n}\n","/**\n * @module @kb-labs/marketplace-entry/scope\n *\n * Client-side scope resolution for marketplace CLI commands. Uses the\n * canonical `findProjectConfigRoot` helper from `@kb-labs/core-workspace`\n * so the detection rules (which filenames count, how walk-up stops) match\n * what the config loader and the marketplace daemon enforce.\n *\n * Rules:\n * - `project` is the default if cwd (or any ancestor up to the filesystem\n * root) contains `.kb/kb.config.{json,jsonc}`.\n * - Otherwise the default is `platform`.\n * - `--scope` always overrides detection.\n * - For scope=\"project\" we return the absolute projectRoot so the daemon\n * doesn't need to re-discover it.\n */\n\nimport { findProjectConfigRoot } from '@kb-labs/core-workspace';\nimport type { MarketplaceScope, MarketplaceQueryScope } from '@kb-labs/marketplace-contracts';\n\nexport const SCOPE_FLAG_CHOICES: readonly MarketplaceScope[] = ['platform', 'project'];\nexport const QUERY_SCOPE_FLAG_CHOICES: readonly MarketplaceQueryScope[] = ['platform', 'project', 'all'];\n\nexport interface ResolvedCliScope {\n scope: MarketplaceScope;\n projectRoot?: string;\n /** How the scope was determined — surfaces in --verbose logs. */\n reason: 'flag' | 'auto-detect' | 'fallback';\n}\n\nexport interface ResolvedCliQueryScope extends Omit<ResolvedCliScope, 'scope'> {\n scope: MarketplaceQueryScope;\n}\n\n/**\n * Resolve the effective scope for a mutating command (`link`, `unlink`,\n * `install`, ...). `flag` is the value of `--scope` (if supplied). The\n * helper never returns `'all'` for mutating commands — callers restrict\n * choices via `SCOPE_FLAG_CHOICES`.\n */\nexport async function resolveCliScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliScope> {\n if (flag) {\n assertMutatingScope(flag);\n if (flag === 'project') {\n const projectRoot = await findProjectConfigRoot(cwd);\n if (!projectRoot) {\n throw new CliScopeError(\n 'SCOPE_PROJECT_ROOT_NOT_FOUND',\n `--scope=project requires a .kb/kb.config.{json,jsonc} ancestor of ${cwd} — none found.`,\n );\n }\n return { scope: 'project', projectRoot, reason: 'flag' };\n }\n return { scope: 'platform', reason: 'flag' };\n }\n\n const projectRoot = await findProjectConfigRoot(cwd);\n if (projectRoot) {\n return { scope: 'project', projectRoot, reason: 'auto-detect' };\n }\n return { scope: 'platform', reason: 'fallback' };\n}\n\n/**\n * Resolve the effective scope for a read-only command (`list`). Accepts\n * `'all'` as an explicit flag value.\n */\nexport async function resolveCliQueryScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliQueryScope> {\n if (flag === 'all') {\n const projectRoot = await findProjectConfigRoot(cwd);\n return { scope: 'all', projectRoot, reason: 'flag' };\n }\n const base = await resolveCliScope(cwd, flag);\n return { scope: base.scope, projectRoot: base.projectRoot, reason: base.reason };\n}\n\nfunction assertMutatingScope(value: string): asserts value is MarketplaceScope {\n if (!SCOPE_FLAG_CHOICES.includes(value as MarketplaceScope)) {\n throw new CliScopeError(\n 'SCOPE_INVALID',\n `--scope must be one of: ${SCOPE_FLAG_CHOICES.join(', ')} (got ${JSON.stringify(value)})`,\n );\n }\n}\n\nexport class CliScopeError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n this.name = 'CliScopeError';\n }\n}\n\n/**\n * Build the body-payload fragment expected by `parseMutatingScope` on the\n * API side: `{ scope, projectRoot? }`. Kept as a helper so every command\n * sends the same shape and nothing drifts out-of-sync with the server.\n */\nexport function scopeBody(ctx: ResolvedCliScope | ResolvedCliQueryScope): {\n scope: MarketplaceScope | MarketplaceQueryScope;\n projectRoot?: string;\n} {\n return {\n scope: ctx.scope,\n ...(ctx.projectRoot ? { projectRoot: ctx.projectRoot } : {}),\n };\n}\n","import { defineCommand, validationError, handleError, type PluginContextV3, type CommandResult } from '@kb-labs/sdk';\nimport { post } from '../../http.js';\nimport { resolveCliScope, scopeBody, CliScopeError } from '../../scope.js';\n\ninterface UnlinkFlags {\n json?: boolean;\n scope?: string;\n 'dry-run'?: boolean;\n}\n\ninterface UnlinkInput {\n argv?: string[];\n flags?: UnlinkFlags;\n}\n\nexport default defineCommand<unknown, UnlinkInput, { packageId: string; scope: string }>({\n id: 'marketplace:plugins:unlink',\n description: 'Unlink a plugin',\n\n handler: {\n async intent(_ctx: PluginContextV3, input: UnlinkInput) {\n const packageId = input.argv?.[0] ?? '(unknown)';\n return {\n summary: `Unlink plugin \"${packageId}\"`,\n operations: [{ type: 'delete' as const, resource: 'plugin-link', details: { packageId } }],\n };\n },\n\n async execute(ctx: PluginContextV3, input: UnlinkInput): Promise<CommandResult<{ packageId: string; scope: string }>> {\n const packageId = input.argv?.[0];\n const flags = (input.flags ?? input) as UnlinkFlags;\n\n if (!packageId) {\n validationError(ctx, 'Specify a package ID to unlink', 'Usage: kb marketplace plugins unlink <plugin-id>', flags.json);\n return { ok: false, error: 'A package id is required', result: { packageId: '', scope: '' } };\n }\n\n let scopeCtx;\n try {\n scopeCtx = await resolveCliScope(ctx.cwd, flags.scope);\n } catch (err) {\n if (err instanceof CliScopeError) {\n validationError(ctx, err.message, undefined, flags.json);\n } else {\n handleError(ctx, err, flags.json);\n }\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { packageId: '', scope: '' } };\n }\n\n try {\n await post(`/packages/unlink`, { packageId, ...scopeBody(scopeCtx) });\n if (flags.json) {\n ctx.ui?.json?.({ ok: true, packageId, scope: scopeCtx.scope });\n } else {\n ctx.ui?.success?.(`Unlinked ${packageId} (${scopeCtx.scope})`);\n }\n return { ok: true, result: { packageId, scope: scopeCtx.scope } };\n } catch (err) {\n handleError(ctx, err, flags.json);\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { packageId: '', scope: '' } };\n }\n },\n },\n});\n"]}
|
package/dist/commands/sync.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineCommand, validationError, handleError, useEnv } from '@kb-labs/sdk';
|
|
2
|
+
import { SessionManager, CredentialsManager } from '@kb-labs/cli-runtime/gateway';
|
|
2
3
|
import { findProjectConfigRoot } from '@kb-labs/core-workspace';
|
|
3
4
|
import * as fs from 'fs/promises';
|
|
4
5
|
import * as path from 'path';
|
|
@@ -7,41 +8,84 @@ import * as path from 'path';
|
|
|
7
8
|
var DEFAULT_GATEWAY_URL = "http://127.0.0.1:4000";
|
|
8
9
|
var MARKETPLACE_PREFIX = "/api/v1/marketplace";
|
|
9
10
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
10
|
-
function
|
|
11
|
+
async function loadAuth() {
|
|
12
|
+
const sessionManager = new SessionManager();
|
|
13
|
+
const session = await sessionManager.load();
|
|
14
|
+
if (session) {
|
|
15
|
+
const current2 = sessionManager.isExpired(session) ? await sessionManager.refresh(session) : session;
|
|
16
|
+
return authState(current2, () => sessionManager.refresh(current2));
|
|
17
|
+
}
|
|
18
|
+
const credentialsManager = new CredentialsManager();
|
|
19
|
+
const credentials = await credentialsManager.load();
|
|
20
|
+
if (!credentials) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const current = credentialsManager.isExpired(credentials) ? await credentialsManager.refresh(credentials) : credentials;
|
|
24
|
+
return authState(current, () => credentialsManager.refresh(current));
|
|
25
|
+
}
|
|
26
|
+
function authState(credentials, refresh) {
|
|
27
|
+
return {
|
|
28
|
+
gatewayUrl: credentials.gatewayUrl,
|
|
29
|
+
accessToken: credentials.accessToken,
|
|
30
|
+
refresh: async () => {
|
|
31
|
+
const updated = await refresh();
|
|
32
|
+
return authState(updated, refresh);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function getBaseUrl(gatewayUrl) {
|
|
11
37
|
const marketplaceUrl = useEnv("KB_MARKETPLACE_URL");
|
|
12
38
|
if (marketplaceUrl) {
|
|
13
39
|
return `${marketplaceUrl}${MARKETPLACE_PREFIX}`;
|
|
14
40
|
}
|
|
15
|
-
const gateway = useEnv("KB_GATEWAY_URL") ?? DEFAULT_GATEWAY_URL;
|
|
41
|
+
const gateway = gatewayUrl ?? useEnv("KB_GATEWAY_URL") ?? DEFAULT_GATEWAY_URL;
|
|
16
42
|
return `${gateway}${MARKETPLACE_PREFIX}`;
|
|
17
43
|
}
|
|
18
|
-
async function
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
headers
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
async function request(method, path2, body, params) {
|
|
45
|
+
let auth = await loadAuth();
|
|
46
|
+
const url = new URL(`${getBaseUrl(auth?.gatewayUrl)}${path2}`);
|
|
47
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
48
|
+
const controller = new AbortController();
|
|
49
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
50
|
+
try {
|
|
51
|
+
const headers = {};
|
|
52
|
+
if (body) {
|
|
53
|
+
headers["Content-Type"] = "application/json";
|
|
54
|
+
}
|
|
55
|
+
if (auth) {
|
|
56
|
+
headers.Authorization = `Bearer ${auth.accessToken}`;
|
|
57
|
+
}
|
|
58
|
+
const response = await fetch(url.toString(), {
|
|
59
|
+
method,
|
|
60
|
+
headers,
|
|
61
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
62
|
+
signal: controller.signal
|
|
63
|
+
});
|
|
64
|
+
if (response.status === 401 && auth && attempt === 0) {
|
|
65
|
+
auth = await auth.refresh();
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const text = await response.text();
|
|
70
|
+
throw new Error(`Marketplace ${path2} failed (${response.status}): ${text}`);
|
|
71
|
+
}
|
|
72
|
+
if (response.status === 204) {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
return await response.json();
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (err.name === "AbortError") {
|
|
78
|
+
throw new Error(`Marketplace ${path2} timed out \u2014 is the marketplace service running? (kb-dev start marketplace)`);
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
81
|
+
} finally {
|
|
82
|
+
clearTimeout(timer);
|
|
40
83
|
}
|
|
41
|
-
throw err;
|
|
42
|
-
} finally {
|
|
43
|
-
clearTimeout(timer);
|
|
44
84
|
}
|
|
85
|
+
throw new Error(`Marketplace ${path2} failed after refreshing authentication`);
|
|
86
|
+
}
|
|
87
|
+
async function post(path2, body) {
|
|
88
|
+
return request("POST", path2, body);
|
|
45
89
|
}
|
|
46
90
|
var SCOPE_FLAG_CHOICES = ["platform", "project"];
|
|
47
91
|
async function resolveCliScope(cwd, flag) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/http.ts","../../src/scope.ts","../../src/commands/sync.ts"],"names":["path","projectRoot","useEnv"],"mappings":"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","file":"sync.js","sourcesContent":["/**\n * HTTP client for marketplace service via Gateway.\n */\n\nimport { useEnv } from '@kb-labs/sdk';\n\nconst DEFAULT_GATEWAY_URL = 'http://127.0.0.1:4000';\nconst MARKETPLACE_PREFIX = '/api/v1/marketplace';\nconst FETCH_TIMEOUT_MS = 30_000;\n\nfunction getBaseUrl(): string {\n const marketplaceUrl = useEnv('KB_MARKETPLACE_URL');\n if (marketplaceUrl) {\n return `${marketplaceUrl}${MARKETPLACE_PREFIX}`;\n }\n const gateway = useEnv('KB_GATEWAY_URL') ?? DEFAULT_GATEWAY_URL;\n return `${gateway}${MARKETPLACE_PREFIX}`;\n}\n\nexport async function post<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n // 204 No Content (e.g. unlink, uninstall) has no body — don't call\n // res.json() or it throws \"Unexpected end of JSON input\".\n if (res.status === 204) { return undefined as T; }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function patch<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function del<T = unknown>(path: string, body?: Record<string, unknown>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'DELETE',\n headers: body ? { 'Content-Type': 'application/json' } : {},\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n if (res.status === 204) { return undefined as T; }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function get<T = unknown>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${getBaseUrl()}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {url.searchParams.set(k, v);}\n }\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url.toString(), { signal: controller.signal });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n","/**\n * @module @kb-labs/marketplace-entry/scope\n *\n * Client-side scope resolution for marketplace CLI commands. Uses the\n * canonical `findProjectConfigRoot` helper from `@kb-labs/core-workspace`\n * so the detection rules (which filenames count, how walk-up stops) match\n * what the config loader and the marketplace daemon enforce.\n *\n * Rules:\n * - `project` is the default if cwd (or any ancestor up to the filesystem\n * root) contains `.kb/kb.config.{json,jsonc}`.\n * - Otherwise the default is `platform`.\n * - `--scope` always overrides detection.\n * - For scope=\"project\" we return the absolute projectRoot so the daemon\n * doesn't need to re-discover it.\n */\n\nimport { findProjectConfigRoot } from '@kb-labs/core-workspace';\nimport type { MarketplaceScope, MarketplaceQueryScope } from '@kb-labs/marketplace-contracts';\n\nexport const SCOPE_FLAG_CHOICES: readonly MarketplaceScope[] = ['platform', 'project'];\nexport const QUERY_SCOPE_FLAG_CHOICES: readonly MarketplaceQueryScope[] = ['platform', 'project', 'all'];\n\nexport interface ResolvedCliScope {\n scope: MarketplaceScope;\n projectRoot?: string;\n /** How the scope was determined — surfaces in --verbose logs. */\n reason: 'flag' | 'auto-detect' | 'fallback';\n}\n\nexport interface ResolvedCliQueryScope extends Omit<ResolvedCliScope, 'scope'> {\n scope: MarketplaceQueryScope;\n}\n\n/**\n * Resolve the effective scope for a mutating command (`link`, `unlink`,\n * `install`, ...). `flag` is the value of `--scope` (if supplied). The\n * helper never returns `'all'` for mutating commands — callers restrict\n * choices via `SCOPE_FLAG_CHOICES`.\n */\nexport async function resolveCliScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliScope> {\n if (flag) {\n assertMutatingScope(flag);\n if (flag === 'project') {\n const projectRoot = await findProjectConfigRoot(cwd);\n if (!projectRoot) {\n throw new CliScopeError(\n 'SCOPE_PROJECT_ROOT_NOT_FOUND',\n `--scope=project requires a .kb/kb.config.{json,jsonc} ancestor of ${cwd} — none found.`,\n );\n }\n return { scope: 'project', projectRoot, reason: 'flag' };\n }\n return { scope: 'platform', reason: 'flag' };\n }\n\n const projectRoot = await findProjectConfigRoot(cwd);\n if (projectRoot) {\n return { scope: 'project', projectRoot, reason: 'auto-detect' };\n }\n return { scope: 'platform', reason: 'fallback' };\n}\n\n/**\n * Resolve the effective scope for a read-only command (`list`). Accepts\n * `'all'` as an explicit flag value.\n */\nexport async function resolveCliQueryScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliQueryScope> {\n if (flag === 'all') {\n const projectRoot = await findProjectConfigRoot(cwd);\n return { scope: 'all', projectRoot, reason: 'flag' };\n }\n const base = await resolveCliScope(cwd, flag);\n return { scope: base.scope, projectRoot: base.projectRoot, reason: base.reason };\n}\n\nfunction assertMutatingScope(value: string): asserts value is MarketplaceScope {\n if (!SCOPE_FLAG_CHOICES.includes(value as MarketplaceScope)) {\n throw new CliScopeError(\n 'SCOPE_INVALID',\n `--scope must be one of: ${SCOPE_FLAG_CHOICES.join(', ')} (got ${JSON.stringify(value)})`,\n );\n }\n}\n\nexport class CliScopeError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n this.name = 'CliScopeError';\n }\n}\n\n/**\n * Build the body-payload fragment expected by `parseMutatingScope` on the\n * API side: `{ scope, projectRoot? }`. Kept as a helper so every command\n * sends the same shape and nothing drifts out-of-sync with the server.\n */\nexport function scopeBody(ctx: ResolvedCliScope | ResolvedCliQueryScope): {\n scope: MarketplaceScope | MarketplaceQueryScope;\n projectRoot?: string;\n} {\n return {\n scope: ctx.scope,\n ...(ctx.projectRoot ? { projectRoot: ctx.projectRoot } : {}),\n };\n}\n","import { defineCommand, useEnv, validationError, handleError, type PluginContextV3, type CommandResult } from '@kb-labs/sdk';\nimport { post } from '../http.js';\nimport { resolveCliScope, scopeBody, CliScopeError } from '../scope.js';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\ninterface SyncFlags {\n 'auto-enable'?: boolean;\n json?: boolean;\n scope?: string;\n 'dry-run'?: boolean;\n}\n\ninterface SyncInput {\n argv?: string[];\n flags?: SyncFlags;\n}\n\ninterface SyncEntry {\n id: string;\n primaryKind: string;\n version: string;\n}\n\ninterface SyncResultData {\n added: SyncEntry[];\n skipped: Array<{ id: string; reason: string }>;\n total: number;\n}\n\nexport default defineCommand<unknown, SyncInput, SyncResultData>({\n id: 'marketplace:sync',\n description: 'Sync workspace — scan for entities and populate lock',\n\n handler: {\n async intent(_ctx: PluginContextV3, _input: SyncInput) {\n return {\n summary: 'Sync workspace — scan for entities and populate lock file',\n operations: [\n { type: 'update' as const, resource: 'marketplace-lock', details: { action: 'sync-workspace' } },\n ],\n };\n },\n\n async execute(ctx: PluginContextV3, input: SyncInput): Promise<CommandResult<SyncResultData>> {\n const flags = (input.flags ?? input) as SyncFlags;\n const cwd = ctx.cwd ?? process.cwd();\n\n let scopeCtx;\n try {\n scopeCtx = await resolveCliScope(cwd, flags.scope);\n } catch (err) {\n if (err instanceof CliScopeError) {\n validationError(ctx, err.message, undefined, flags.json);\n } else {\n handleError(ctx, err, flags.json);\n }\n return { ok: false, error: 'Marketplace scope is not configured', result: { added: [], skipped: [], total: 0 } };\n }\n\n // Sync reads include/exclude patterns from the config file located at\n // the scope root, not from the CLI cwd. This keeps semantics consistent:\n // `--scope project` syncs using the project's config, `--scope platform`\n // uses the platform config.\n const configRoot = scopeCtx.scope === 'project' && scopeCtx.projectRoot\n ? scopeCtx.projectRoot\n : cwd;\n const syncConfig = await loadSyncConfig(configRoot);\n\n if (!syncConfig.include?.length) {\n validationError(\n ctx,\n `No marketplace.sync.include configured.\\n\\nAdd to ${configRoot}/.kb/kb.config.json:\\n\\n \"marketplace\": {\\n \"sync\": {\\n \"include\": [\"plugins/*/entry\", \"plugins/*/core\", \"adapters/*\"]\\n }\\n }`,\n undefined,\n flags.json,\n );\n return { ok: false, error: 'No marketplace.sync.include configured', result: { added: [], skipped: [], total: 0 } };\n }\n\n try {\n const isDev = (useEnv('NODE_ENV') ?? 'development') === 'development';\n const result = await post<SyncResultData>('/workspace/sync', {\n include: syncConfig.include,\n exclude: syncConfig.exclude,\n autoEnable: flags['auto-enable'] !== undefined ? Boolean(flags['auto-enable']) : isDev,\n ...scopeBody(scopeCtx),\n });\n\n if (flags.json) {\n ctx.ui?.json?.(result);\n } else if (result.added.length === 0) {\n ctx.ui?.info?.(`Lock is up to date — ${scopeCtx.scope} (${result.total} entries)`);\n } else {\n ctx.ui?.success?.(`Synced ${result.added.length} new entries to ${scopeCtx.scope} (${result.total} total)`, {\n sections: [{\n header: 'Added',\n items: result.added.map(e => `+ ${e.id} (${e.primaryKind}) v${e.version}`),\n }],\n });\n }\n\n return { ok: true, result };\n } catch (err) {\n handleError(ctx, err, flags.json);\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { added: [], skipped: [], total: 0 } };\n }\n },\n },\n});\n\nasync function loadSyncConfig(root: string): Promise<{ include?: string[]; exclude?: string[] }> {\n for (const name of ['kb.config.jsonc', 'kb.config.json']) {\n const p = path.join(root, '.kb', name);\n try {\n const raw = await fs.readFile(p, 'utf-8');\n // .json files are strict JSON — strip only for .jsonc to avoid mangling URLs (e.g. ws://).\n const parsed = JSON.parse(name.endsWith('.jsonc') ? stripJsonc(raw) : raw);\n return parsed?.marketplace?.sync ?? {};\n } catch { continue; }\n }\n return {};\n}\n\n/** Minimal JSONC stripper — removes // line and /* block comments plus trailing commas. */\nfunction stripJsonc(src: string): string {\n return src\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/(^|[^:])\\/\\/.*$/gm, '$1')\n .replace(/,(\\s*[}\\]])/g, '$1');\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/http.ts","../../src/scope.ts","../../src/commands/sync.ts"],"names":["current","path","projectRoot","useEnv"],"mappings":"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","file":"sync.js","sourcesContent":["/**\n * HTTP client for marketplace service via Gateway.\n */\n\nimport { useEnv } from '@kb-labs/sdk';\nimport { CredentialsManager, SessionManager } from '@kb-labs/cli-runtime/gateway';\nimport type { GatewayCredentials, SessionCredentials } from '@kb-labs/cli-runtime/gateway';\n\nconst DEFAULT_GATEWAY_URL = 'http://127.0.0.1:4000';\nconst MARKETPLACE_PREFIX = '/api/v1/marketplace';\nconst FETCH_TIMEOUT_MS = 30_000;\n\ntype AuthState = {\n gatewayUrl: string;\n accessToken: string;\n refresh: () => Promise<AuthState>;\n};\n\nasync function loadAuth(): Promise<AuthState | null> {\n const sessionManager = new SessionManager();\n const session = await sessionManager.load();\n if (session) {\n const current = sessionManager.isExpired(session) ? await sessionManager.refresh(session) : session;\n return authState(current, () => sessionManager.refresh(current));\n }\n\n const credentialsManager = new CredentialsManager();\n const credentials = await credentialsManager.load();\n if (!credentials) {\n return null;\n }\n const current = credentialsManager.isExpired(credentials) ? await credentialsManager.refresh(credentials) : credentials;\n return authState(current, () => credentialsManager.refresh(current));\n}\n\nfunction authState(credentials: GatewayCredentials | SessionCredentials, refresh: () => Promise<GatewayCredentials | SessionCredentials>): AuthState {\n return {\n gatewayUrl: credentials.gatewayUrl,\n accessToken: credentials.accessToken,\n refresh: async () => {\n const updated = await refresh();\n return authState(updated, refresh);\n },\n };\n}\n\nfunction getBaseUrl(gatewayUrl?: string): string {\n const marketplaceUrl = useEnv('KB_MARKETPLACE_URL');\n if (marketplaceUrl) {\n return `${marketplaceUrl}${MARKETPLACE_PREFIX}`;\n }\n const gateway = gatewayUrl ?? useEnv('KB_GATEWAY_URL') ?? DEFAULT_GATEWAY_URL;\n return `${gateway}${MARKETPLACE_PREFIX}`;\n}\n\nasync function request<T>(method: string, path: string, body?: Record<string, unknown>, params?: Record<string, string>): Promise<T> {\n let auth = await loadAuth();\n const url = new URL(`${getBaseUrl(auth?.gatewayUrl)}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) { url.searchParams.set(key, value); }\n }\n\n for (let attempt = 0; attempt < 2; attempt += 1) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const headers: Record<string, string> = {};\n if (body) { headers['Content-Type'] = 'application/json'; }\n if (auth) { headers.Authorization = `Bearer ${auth.accessToken}`; }\n const response = await fetch(url.toString(), {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n if (response.status === 401 && auth && attempt === 0) {\n auth = await auth.refresh();\n continue;\n }\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Marketplace ${path} failed (${response.status}): ${text}`);\n }\n if (response.status === 204) { return undefined as T; }\n return await response.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n }\n throw new Error(`Marketplace ${path} failed after refreshing authentication`);\n}\n\nexport async function post<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n return request<T>('POST', path, body);\n}\n\nexport async function patch<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n return request<T>('PATCH', path, body);\n}\n\nexport async function del<T = unknown>(path: string, body?: Record<string, unknown>): Promise<T> {\n return request<T>('DELETE', path, body);\n}\n\nexport async function get<T = unknown>(path: string, params?: Record<string, string>): Promise<T> {\n return request<T>('GET', path, undefined, params);\n}\n","/**\n * @module @kb-labs/marketplace-entry/scope\n *\n * Client-side scope resolution for marketplace CLI commands. Uses the\n * canonical `findProjectConfigRoot` helper from `@kb-labs/core-workspace`\n * so the detection rules (which filenames count, how walk-up stops) match\n * what the config loader and the marketplace daemon enforce.\n *\n * Rules:\n * - `project` is the default if cwd (or any ancestor up to the filesystem\n * root) contains `.kb/kb.config.{json,jsonc}`.\n * - Otherwise the default is `platform`.\n * - `--scope` always overrides detection.\n * - For scope=\"project\" we return the absolute projectRoot so the daemon\n * doesn't need to re-discover it.\n */\n\nimport { findProjectConfigRoot } from '@kb-labs/core-workspace';\nimport type { MarketplaceScope, MarketplaceQueryScope } from '@kb-labs/marketplace-contracts';\n\nexport const SCOPE_FLAG_CHOICES: readonly MarketplaceScope[] = ['platform', 'project'];\nexport const QUERY_SCOPE_FLAG_CHOICES: readonly MarketplaceQueryScope[] = ['platform', 'project', 'all'];\n\nexport interface ResolvedCliScope {\n scope: MarketplaceScope;\n projectRoot?: string;\n /** How the scope was determined — surfaces in --verbose logs. */\n reason: 'flag' | 'auto-detect' | 'fallback';\n}\n\nexport interface ResolvedCliQueryScope extends Omit<ResolvedCliScope, 'scope'> {\n scope: MarketplaceQueryScope;\n}\n\n/**\n * Resolve the effective scope for a mutating command (`link`, `unlink`,\n * `install`, ...). `flag` is the value of `--scope` (if supplied). The\n * helper never returns `'all'` for mutating commands — callers restrict\n * choices via `SCOPE_FLAG_CHOICES`.\n */\nexport async function resolveCliScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliScope> {\n if (flag) {\n assertMutatingScope(flag);\n if (flag === 'project') {\n const projectRoot = await findProjectConfigRoot(cwd);\n if (!projectRoot) {\n throw new CliScopeError(\n 'SCOPE_PROJECT_ROOT_NOT_FOUND',\n `--scope=project requires a .kb/kb.config.{json,jsonc} ancestor of ${cwd} — none found.`,\n );\n }\n return { scope: 'project', projectRoot, reason: 'flag' };\n }\n return { scope: 'platform', reason: 'flag' };\n }\n\n const projectRoot = await findProjectConfigRoot(cwd);\n if (projectRoot) {\n return { scope: 'project', projectRoot, reason: 'auto-detect' };\n }\n return { scope: 'platform', reason: 'fallback' };\n}\n\n/**\n * Resolve the effective scope for a read-only command (`list`). Accepts\n * `'all'` as an explicit flag value.\n */\nexport async function resolveCliQueryScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliQueryScope> {\n if (flag === 'all') {\n const projectRoot = await findProjectConfigRoot(cwd);\n return { scope: 'all', projectRoot, reason: 'flag' };\n }\n const base = await resolveCliScope(cwd, flag);\n return { scope: base.scope, projectRoot: base.projectRoot, reason: base.reason };\n}\n\nfunction assertMutatingScope(value: string): asserts value is MarketplaceScope {\n if (!SCOPE_FLAG_CHOICES.includes(value as MarketplaceScope)) {\n throw new CliScopeError(\n 'SCOPE_INVALID',\n `--scope must be one of: ${SCOPE_FLAG_CHOICES.join(', ')} (got ${JSON.stringify(value)})`,\n );\n }\n}\n\nexport class CliScopeError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n this.name = 'CliScopeError';\n }\n}\n\n/**\n * Build the body-payload fragment expected by `parseMutatingScope` on the\n * API side: `{ scope, projectRoot? }`. Kept as a helper so every command\n * sends the same shape and nothing drifts out-of-sync with the server.\n */\nexport function scopeBody(ctx: ResolvedCliScope | ResolvedCliQueryScope): {\n scope: MarketplaceScope | MarketplaceQueryScope;\n projectRoot?: string;\n} {\n return {\n scope: ctx.scope,\n ...(ctx.projectRoot ? { projectRoot: ctx.projectRoot } : {}),\n };\n}\n","import { defineCommand, useEnv, validationError, handleError, type PluginContextV3, type CommandResult } from '@kb-labs/sdk';\nimport { post } from '../http.js';\nimport { resolveCliScope, scopeBody, CliScopeError } from '../scope.js';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\ninterface SyncFlags {\n 'auto-enable'?: boolean;\n json?: boolean;\n scope?: string;\n 'dry-run'?: boolean;\n}\n\ninterface SyncInput {\n argv?: string[];\n flags?: SyncFlags;\n}\n\ninterface SyncEntry {\n id: string;\n primaryKind: string;\n version: string;\n}\n\ninterface SyncResultData {\n added: SyncEntry[];\n skipped: Array<{ id: string; reason: string }>;\n total: number;\n}\n\nexport default defineCommand<unknown, SyncInput, SyncResultData>({\n id: 'marketplace:sync',\n description: 'Sync workspace — scan for entities and populate lock',\n\n handler: {\n async intent(_ctx: PluginContextV3, _input: SyncInput) {\n return {\n summary: 'Sync workspace — scan for entities and populate lock file',\n operations: [\n { type: 'update' as const, resource: 'marketplace-lock', details: { action: 'sync-workspace' } },\n ],\n };\n },\n\n async execute(ctx: PluginContextV3, input: SyncInput): Promise<CommandResult<SyncResultData>> {\n const flags = (input.flags ?? input) as SyncFlags;\n const cwd = ctx.cwd ?? process.cwd();\n\n let scopeCtx;\n try {\n scopeCtx = await resolveCliScope(cwd, flags.scope);\n } catch (err) {\n if (err instanceof CliScopeError) {\n validationError(ctx, err.message, undefined, flags.json);\n } else {\n handleError(ctx, err, flags.json);\n }\n return { ok: false, error: 'Marketplace scope is not configured', result: { added: [], skipped: [], total: 0 } };\n }\n\n // Sync reads include/exclude patterns from the config file located at\n // the scope root, not from the CLI cwd. This keeps semantics consistent:\n // `--scope project` syncs using the project's config, `--scope platform`\n // uses the platform config.\n const configRoot = scopeCtx.scope === 'project' && scopeCtx.projectRoot\n ? scopeCtx.projectRoot\n : cwd;\n const syncConfig = await loadSyncConfig(configRoot);\n\n if (!syncConfig.include?.length) {\n validationError(\n ctx,\n `No marketplace.sync.include configured.\\n\\nAdd to ${configRoot}/.kb/kb.config.json:\\n\\n \"marketplace\": {\\n \"sync\": {\\n \"include\": [\"plugins/*/entry\", \"plugins/*/core\", \"adapters/*\"]\\n }\\n }`,\n undefined,\n flags.json,\n );\n return { ok: false, error: 'No marketplace.sync.include configured', result: { added: [], skipped: [], total: 0 } };\n }\n\n try {\n const isDev = (useEnv('NODE_ENV') ?? 'development') === 'development';\n const result = await post<SyncResultData>('/workspace/sync', {\n include: syncConfig.include,\n exclude: syncConfig.exclude,\n autoEnable: flags['auto-enable'] !== undefined ? Boolean(flags['auto-enable']) : isDev,\n ...scopeBody(scopeCtx),\n });\n\n if (flags.json) {\n ctx.ui?.json?.(result);\n } else if (result.added.length === 0) {\n ctx.ui?.info?.(`Lock is up to date — ${scopeCtx.scope} (${result.total} entries)`);\n } else {\n ctx.ui?.success?.(`Synced ${result.added.length} new entries to ${scopeCtx.scope} (${result.total} total)`, {\n sections: [{\n header: 'Added',\n items: result.added.map(e => `+ ${e.id} (${e.primaryKind}) v${e.version}`),\n }],\n });\n }\n\n return { ok: true, result };\n } catch (err) {\n handleError(ctx, err, flags.json);\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { added: [], skipped: [], total: 0 } };\n }\n },\n },\n});\n\nasync function loadSyncConfig(root: string): Promise<{ include?: string[]; exclude?: string[] }> {\n for (const name of ['kb.config.jsonc', 'kb.config.json']) {\n const p = path.join(root, '.kb', name);\n try {\n const raw = await fs.readFile(p, 'utf-8');\n // .json files are strict JSON — strip only for .jsonc to avoid mangling URLs (e.g. ws://).\n const parsed = JSON.parse(name.endsWith('.jsonc') ? stripJsonc(raw) : raw);\n return parsed?.marketplace?.sync ?? {};\n } catch { continue; }\n }\n return {};\n}\n\n/** Minimal JSONC stripper — removes // line and /* block comments plus trailing commas. */\nfunction stripJsonc(src: string): string {\n return src\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/(^|[^:])\\/\\/.*$/gm, '$1')\n .replace(/,(\\s*[}\\]])/g, '$1');\n}\n"]}
|
|
@@ -1,45 +1,89 @@
|
|
|
1
1
|
import { defineCommand, validationError, handleError, useEnv } from '@kb-labs/sdk';
|
|
2
|
+
import { SessionManager, CredentialsManager } from '@kb-labs/cli-runtime/gateway';
|
|
2
3
|
import { findProjectConfigRoot } from '@kb-labs/core-workspace';
|
|
3
4
|
|
|
4
5
|
// src/commands/uninstall.ts
|
|
5
6
|
var DEFAULT_GATEWAY_URL = "http://127.0.0.1:4000";
|
|
6
7
|
var MARKETPLACE_PREFIX = "/api/v1/marketplace";
|
|
7
8
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
8
|
-
function
|
|
9
|
+
async function loadAuth() {
|
|
10
|
+
const sessionManager = new SessionManager();
|
|
11
|
+
const session = await sessionManager.load();
|
|
12
|
+
if (session) {
|
|
13
|
+
const current2 = sessionManager.isExpired(session) ? await sessionManager.refresh(session) : session;
|
|
14
|
+
return authState(current2, () => sessionManager.refresh(current2));
|
|
15
|
+
}
|
|
16
|
+
const credentialsManager = new CredentialsManager();
|
|
17
|
+
const credentials = await credentialsManager.load();
|
|
18
|
+
if (!credentials) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const current = credentialsManager.isExpired(credentials) ? await credentialsManager.refresh(credentials) : credentials;
|
|
22
|
+
return authState(current, () => credentialsManager.refresh(current));
|
|
23
|
+
}
|
|
24
|
+
function authState(credentials, refresh) {
|
|
25
|
+
return {
|
|
26
|
+
gatewayUrl: credentials.gatewayUrl,
|
|
27
|
+
accessToken: credentials.accessToken,
|
|
28
|
+
refresh: async () => {
|
|
29
|
+
const updated = await refresh();
|
|
30
|
+
return authState(updated, refresh);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function getBaseUrl(gatewayUrl) {
|
|
9
35
|
const marketplaceUrl = useEnv("KB_MARKETPLACE_URL");
|
|
10
36
|
if (marketplaceUrl) {
|
|
11
37
|
return `${marketplaceUrl}${MARKETPLACE_PREFIX}`;
|
|
12
38
|
}
|
|
13
|
-
const gateway = useEnv("KB_GATEWAY_URL") ?? DEFAULT_GATEWAY_URL;
|
|
39
|
+
const gateway = gatewayUrl ?? useEnv("KB_GATEWAY_URL") ?? DEFAULT_GATEWAY_URL;
|
|
14
40
|
return `${gateway}${MARKETPLACE_PREFIX}`;
|
|
15
41
|
}
|
|
16
|
-
async function
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
headers
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
async function request(method, path, body, params) {
|
|
43
|
+
let auth = await loadAuth();
|
|
44
|
+
const url = new URL(`${getBaseUrl(auth?.gatewayUrl)}${path}`);
|
|
45
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
48
|
+
try {
|
|
49
|
+
const headers = {};
|
|
50
|
+
if (body) {
|
|
51
|
+
headers["Content-Type"] = "application/json";
|
|
52
|
+
}
|
|
53
|
+
if (auth) {
|
|
54
|
+
headers.Authorization = `Bearer ${auth.accessToken}`;
|
|
55
|
+
}
|
|
56
|
+
const response = await fetch(url.toString(), {
|
|
57
|
+
method,
|
|
58
|
+
headers,
|
|
59
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
60
|
+
signal: controller.signal
|
|
61
|
+
});
|
|
62
|
+
if (response.status === 401 && auth && attempt === 0) {
|
|
63
|
+
auth = await auth.refresh();
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const text = await response.text();
|
|
68
|
+
throw new Error(`Marketplace ${path} failed (${response.status}): ${text}`);
|
|
69
|
+
}
|
|
70
|
+
if (response.status === 204) {
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
return await response.json();
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (err.name === "AbortError") {
|
|
76
|
+
throw new Error(`Marketplace ${path} timed out \u2014 is the marketplace service running? (kb-dev start marketplace)`);
|
|
77
|
+
}
|
|
78
|
+
throw err;
|
|
79
|
+
} finally {
|
|
80
|
+
clearTimeout(timer);
|
|
38
81
|
}
|
|
39
|
-
throw err;
|
|
40
|
-
} finally {
|
|
41
|
-
clearTimeout(timer);
|
|
42
82
|
}
|
|
83
|
+
throw new Error(`Marketplace ${path} failed after refreshing authentication`);
|
|
84
|
+
}
|
|
85
|
+
async function post(path, body) {
|
|
86
|
+
return request("POST", path, body);
|
|
43
87
|
}
|
|
44
88
|
var SCOPE_FLAG_CHOICES = ["platform", "project"];
|
|
45
89
|
async function resolveCliScope(cwd, flag) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/http.ts","../../src/scope.ts","../../src/commands/uninstall.ts"],"names":["projectRoot"],"mappings":"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","file":"uninstall.js","sourcesContent":["/**\n * HTTP client for marketplace service via Gateway.\n */\n\nimport { useEnv } from '@kb-labs/sdk';\n\nconst DEFAULT_GATEWAY_URL = 'http://127.0.0.1:4000';\nconst MARKETPLACE_PREFIX = '/api/v1/marketplace';\nconst FETCH_TIMEOUT_MS = 30_000;\n\nfunction getBaseUrl(): string {\n const marketplaceUrl = useEnv('KB_MARKETPLACE_URL');\n if (marketplaceUrl) {\n return `${marketplaceUrl}${MARKETPLACE_PREFIX}`;\n }\n const gateway = useEnv('KB_GATEWAY_URL') ?? DEFAULT_GATEWAY_URL;\n return `${gateway}${MARKETPLACE_PREFIX}`;\n}\n\nexport async function post<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n // 204 No Content (e.g. unlink, uninstall) has no body — don't call\n // res.json() or it throws \"Unexpected end of JSON input\".\n if (res.status === 204) { return undefined as T; }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function patch<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function del<T = unknown>(path: string, body?: Record<string, unknown>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'DELETE',\n headers: body ? { 'Content-Type': 'application/json' } : {},\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n if (res.status === 204) { return undefined as T; }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function get<T = unknown>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${getBaseUrl()}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {url.searchParams.set(k, v);}\n }\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(url.toString(), { signal: controller.signal });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Marketplace ${path} failed (${res.status}): ${text}`);\n }\n return await res.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n","/**\n * @module @kb-labs/marketplace-entry/scope\n *\n * Client-side scope resolution for marketplace CLI commands. Uses the\n * canonical `findProjectConfigRoot` helper from `@kb-labs/core-workspace`\n * so the detection rules (which filenames count, how walk-up stops) match\n * what the config loader and the marketplace daemon enforce.\n *\n * Rules:\n * - `project` is the default if cwd (or any ancestor up to the filesystem\n * root) contains `.kb/kb.config.{json,jsonc}`.\n * - Otherwise the default is `platform`.\n * - `--scope` always overrides detection.\n * - For scope=\"project\" we return the absolute projectRoot so the daemon\n * doesn't need to re-discover it.\n */\n\nimport { findProjectConfigRoot } from '@kb-labs/core-workspace';\nimport type { MarketplaceScope, MarketplaceQueryScope } from '@kb-labs/marketplace-contracts';\n\nexport const SCOPE_FLAG_CHOICES: readonly MarketplaceScope[] = ['platform', 'project'];\nexport const QUERY_SCOPE_FLAG_CHOICES: readonly MarketplaceQueryScope[] = ['platform', 'project', 'all'];\n\nexport interface ResolvedCliScope {\n scope: MarketplaceScope;\n projectRoot?: string;\n /** How the scope was determined — surfaces in --verbose logs. */\n reason: 'flag' | 'auto-detect' | 'fallback';\n}\n\nexport interface ResolvedCliQueryScope extends Omit<ResolvedCliScope, 'scope'> {\n scope: MarketplaceQueryScope;\n}\n\n/**\n * Resolve the effective scope for a mutating command (`link`, `unlink`,\n * `install`, ...). `flag` is the value of `--scope` (if supplied). The\n * helper never returns `'all'` for mutating commands — callers restrict\n * choices via `SCOPE_FLAG_CHOICES`.\n */\nexport async function resolveCliScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliScope> {\n if (flag) {\n assertMutatingScope(flag);\n if (flag === 'project') {\n const projectRoot = await findProjectConfigRoot(cwd);\n if (!projectRoot) {\n throw new CliScopeError(\n 'SCOPE_PROJECT_ROOT_NOT_FOUND',\n `--scope=project requires a .kb/kb.config.{json,jsonc} ancestor of ${cwd} — none found.`,\n );\n }\n return { scope: 'project', projectRoot, reason: 'flag' };\n }\n return { scope: 'platform', reason: 'flag' };\n }\n\n const projectRoot = await findProjectConfigRoot(cwd);\n if (projectRoot) {\n return { scope: 'project', projectRoot, reason: 'auto-detect' };\n }\n return { scope: 'platform', reason: 'fallback' };\n}\n\n/**\n * Resolve the effective scope for a read-only command (`list`). Accepts\n * `'all'` as an explicit flag value.\n */\nexport async function resolveCliQueryScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliQueryScope> {\n if (flag === 'all') {\n const projectRoot = await findProjectConfigRoot(cwd);\n return { scope: 'all', projectRoot, reason: 'flag' };\n }\n const base = await resolveCliScope(cwd, flag);\n return { scope: base.scope, projectRoot: base.projectRoot, reason: base.reason };\n}\n\nfunction assertMutatingScope(value: string): asserts value is MarketplaceScope {\n if (!SCOPE_FLAG_CHOICES.includes(value as MarketplaceScope)) {\n throw new CliScopeError(\n 'SCOPE_INVALID',\n `--scope must be one of: ${SCOPE_FLAG_CHOICES.join(', ')} (got ${JSON.stringify(value)})`,\n );\n }\n}\n\nexport class CliScopeError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n this.name = 'CliScopeError';\n }\n}\n\n/**\n * Build the body-payload fragment expected by `parseMutatingScope` on the\n * API side: `{ scope, projectRoot? }`. Kept as a helper so every command\n * sends the same shape and nothing drifts out-of-sync with the server.\n */\nexport function scopeBody(ctx: ResolvedCliScope | ResolvedCliQueryScope): {\n scope: MarketplaceScope | MarketplaceQueryScope;\n projectRoot?: string;\n} {\n return {\n scope: ctx.scope,\n ...(ctx.projectRoot ? { projectRoot: ctx.projectRoot } : {}),\n };\n}\n","import { defineCommand, validationError, handleError, type PluginContextV3, type CommandResult } from '@kb-labs/sdk';\nimport { post } from '../http.js';\nimport { resolveCliScope, scopeBody, CliScopeError } from '../scope.js';\n\ninterface UninstallFlags {\n json?: boolean;\n scope?: string;\n 'dry-run'?: boolean;\n}\n\ninterface UninstallInput {\n argv?: string[];\n flags?: UninstallFlags;\n}\n\nexport default defineCommand<unknown, UninstallInput, { removed: string[]; scope: string }>({\n id: 'marketplace:uninstall',\n description: 'Uninstall package(s) from marketplace',\n\n handler: {\n async intent(_ctx: PluginContextV3, input: UninstallInput) {\n const argv = input.argv ?? [];\n const packages = argv.length > 0 ? argv : ['(no packages specified)'];\n return {\n summary: `Uninstall ${packages.join(', ')} from marketplace`,\n operations: packages.map(pkg => ({\n type: 'delete' as const,\n resource: 'marketplace-package',\n details: { package: pkg },\n })),\n };\n },\n\n async execute(ctx: PluginContextV3, input: UninstallInput): Promise<CommandResult<{ removed: string[]; scope: string }>> {\n const argv = input.argv ?? [];\n const flags = (input.flags ?? input) as UninstallFlags;\n\n if (argv.length === 0) {\n validationError(ctx, 'Please specify at least one package to uninstall', 'Usage: kb marketplace uninstall <package>', flags.json);\n return { ok: false, error: 'Please specify at least one package to uninstall', result: { removed: [], scope: '' } };\n }\n\n let scopeCtx;\n try {\n scopeCtx = await resolveCliScope(ctx.cwd, flags.scope);\n } catch (err) {\n if (err instanceof CliScopeError) {\n validationError(ctx, err.message, undefined, flags.json);\n } else {\n handleError(ctx, err, flags.json);\n }\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { removed: [], scope: '' } };\n }\n\n try {\n // Server returns 204 on success; treat that as \"everything you asked for\n // was removed\" so the CLI has something to render.\n await post('/packages/uninstall', {\n packageIds: argv,\n ...scopeBody(scopeCtx),\n });\n\n if (flags.json) {\n ctx.ui?.json?.({ ok: true, removed: argv, scope: scopeCtx.scope });\n } else {\n ctx.ui?.success?.(`Removed from ${scopeCtx.scope}: ${argv.join(', ')}`);\n }\n return { ok: true, result: { removed: argv, scope: scopeCtx.scope } };\n } catch (err) {\n handleError(ctx, err, flags.json);\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { removed: [], scope: '' } };\n }\n },\n },\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/http.ts","../../src/scope.ts","../../src/commands/uninstall.ts"],"names":["current","projectRoot"],"mappings":"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","file":"uninstall.js","sourcesContent":["/**\n * HTTP client for marketplace service via Gateway.\n */\n\nimport { useEnv } from '@kb-labs/sdk';\nimport { CredentialsManager, SessionManager } from '@kb-labs/cli-runtime/gateway';\nimport type { GatewayCredentials, SessionCredentials } from '@kb-labs/cli-runtime/gateway';\n\nconst DEFAULT_GATEWAY_URL = 'http://127.0.0.1:4000';\nconst MARKETPLACE_PREFIX = '/api/v1/marketplace';\nconst FETCH_TIMEOUT_MS = 30_000;\n\ntype AuthState = {\n gatewayUrl: string;\n accessToken: string;\n refresh: () => Promise<AuthState>;\n};\n\nasync function loadAuth(): Promise<AuthState | null> {\n const sessionManager = new SessionManager();\n const session = await sessionManager.load();\n if (session) {\n const current = sessionManager.isExpired(session) ? await sessionManager.refresh(session) : session;\n return authState(current, () => sessionManager.refresh(current));\n }\n\n const credentialsManager = new CredentialsManager();\n const credentials = await credentialsManager.load();\n if (!credentials) {\n return null;\n }\n const current = credentialsManager.isExpired(credentials) ? await credentialsManager.refresh(credentials) : credentials;\n return authState(current, () => credentialsManager.refresh(current));\n}\n\nfunction authState(credentials: GatewayCredentials | SessionCredentials, refresh: () => Promise<GatewayCredentials | SessionCredentials>): AuthState {\n return {\n gatewayUrl: credentials.gatewayUrl,\n accessToken: credentials.accessToken,\n refresh: async () => {\n const updated = await refresh();\n return authState(updated, refresh);\n },\n };\n}\n\nfunction getBaseUrl(gatewayUrl?: string): string {\n const marketplaceUrl = useEnv('KB_MARKETPLACE_URL');\n if (marketplaceUrl) {\n return `${marketplaceUrl}${MARKETPLACE_PREFIX}`;\n }\n const gateway = gatewayUrl ?? useEnv('KB_GATEWAY_URL') ?? DEFAULT_GATEWAY_URL;\n return `${gateway}${MARKETPLACE_PREFIX}`;\n}\n\nasync function request<T>(method: string, path: string, body?: Record<string, unknown>, params?: Record<string, string>): Promise<T> {\n let auth = await loadAuth();\n const url = new URL(`${getBaseUrl(auth?.gatewayUrl)}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) { url.searchParams.set(key, value); }\n }\n\n for (let attempt = 0; attempt < 2; attempt += 1) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const headers: Record<string, string> = {};\n if (body) { headers['Content-Type'] = 'application/json'; }\n if (auth) { headers.Authorization = `Bearer ${auth.accessToken}`; }\n const response = await fetch(url.toString(), {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n if (response.status === 401 && auth && attempt === 0) {\n auth = await auth.refresh();\n continue;\n }\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Marketplace ${path} failed (${response.status}): ${text}`);\n }\n if (response.status === 204) { return undefined as T; }\n return await response.json() as T;\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Marketplace ${path} timed out — is the marketplace service running? (kb-dev start marketplace)`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n }\n throw new Error(`Marketplace ${path} failed after refreshing authentication`);\n}\n\nexport async function post<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n return request<T>('POST', path, body);\n}\n\nexport async function patch<T = unknown>(path: string, body: Record<string, unknown>): Promise<T> {\n return request<T>('PATCH', path, body);\n}\n\nexport async function del<T = unknown>(path: string, body?: Record<string, unknown>): Promise<T> {\n return request<T>('DELETE', path, body);\n}\n\nexport async function get<T = unknown>(path: string, params?: Record<string, string>): Promise<T> {\n return request<T>('GET', path, undefined, params);\n}\n","/**\n * @module @kb-labs/marketplace-entry/scope\n *\n * Client-side scope resolution for marketplace CLI commands. Uses the\n * canonical `findProjectConfigRoot` helper from `@kb-labs/core-workspace`\n * so the detection rules (which filenames count, how walk-up stops) match\n * what the config loader and the marketplace daemon enforce.\n *\n * Rules:\n * - `project` is the default if cwd (or any ancestor up to the filesystem\n * root) contains `.kb/kb.config.{json,jsonc}`.\n * - Otherwise the default is `platform`.\n * - `--scope` always overrides detection.\n * - For scope=\"project\" we return the absolute projectRoot so the daemon\n * doesn't need to re-discover it.\n */\n\nimport { findProjectConfigRoot } from '@kb-labs/core-workspace';\nimport type { MarketplaceScope, MarketplaceQueryScope } from '@kb-labs/marketplace-contracts';\n\nexport const SCOPE_FLAG_CHOICES: readonly MarketplaceScope[] = ['platform', 'project'];\nexport const QUERY_SCOPE_FLAG_CHOICES: readonly MarketplaceQueryScope[] = ['platform', 'project', 'all'];\n\nexport interface ResolvedCliScope {\n scope: MarketplaceScope;\n projectRoot?: string;\n /** How the scope was determined — surfaces in --verbose logs. */\n reason: 'flag' | 'auto-detect' | 'fallback';\n}\n\nexport interface ResolvedCliQueryScope extends Omit<ResolvedCliScope, 'scope'> {\n scope: MarketplaceQueryScope;\n}\n\n/**\n * Resolve the effective scope for a mutating command (`link`, `unlink`,\n * `install`, ...). `flag` is the value of `--scope` (if supplied). The\n * helper never returns `'all'` for mutating commands — callers restrict\n * choices via `SCOPE_FLAG_CHOICES`.\n */\nexport async function resolveCliScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliScope> {\n if (flag) {\n assertMutatingScope(flag);\n if (flag === 'project') {\n const projectRoot = await findProjectConfigRoot(cwd);\n if (!projectRoot) {\n throw new CliScopeError(\n 'SCOPE_PROJECT_ROOT_NOT_FOUND',\n `--scope=project requires a .kb/kb.config.{json,jsonc} ancestor of ${cwd} — none found.`,\n );\n }\n return { scope: 'project', projectRoot, reason: 'flag' };\n }\n return { scope: 'platform', reason: 'flag' };\n }\n\n const projectRoot = await findProjectConfigRoot(cwd);\n if (projectRoot) {\n return { scope: 'project', projectRoot, reason: 'auto-detect' };\n }\n return { scope: 'platform', reason: 'fallback' };\n}\n\n/**\n * Resolve the effective scope for a read-only command (`list`). Accepts\n * `'all'` as an explicit flag value.\n */\nexport async function resolveCliQueryScope(\n cwd: string,\n flag: string | undefined,\n): Promise<ResolvedCliQueryScope> {\n if (flag === 'all') {\n const projectRoot = await findProjectConfigRoot(cwd);\n return { scope: 'all', projectRoot, reason: 'flag' };\n }\n const base = await resolveCliScope(cwd, flag);\n return { scope: base.scope, projectRoot: base.projectRoot, reason: base.reason };\n}\n\nfunction assertMutatingScope(value: string): asserts value is MarketplaceScope {\n if (!SCOPE_FLAG_CHOICES.includes(value as MarketplaceScope)) {\n throw new CliScopeError(\n 'SCOPE_INVALID',\n `--scope must be one of: ${SCOPE_FLAG_CHOICES.join(', ')} (got ${JSON.stringify(value)})`,\n );\n }\n}\n\nexport class CliScopeError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n this.name = 'CliScopeError';\n }\n}\n\n/**\n * Build the body-payload fragment expected by `parseMutatingScope` on the\n * API side: `{ scope, projectRoot? }`. Kept as a helper so every command\n * sends the same shape and nothing drifts out-of-sync with the server.\n */\nexport function scopeBody(ctx: ResolvedCliScope | ResolvedCliQueryScope): {\n scope: MarketplaceScope | MarketplaceQueryScope;\n projectRoot?: string;\n} {\n return {\n scope: ctx.scope,\n ...(ctx.projectRoot ? { projectRoot: ctx.projectRoot } : {}),\n };\n}\n","import { defineCommand, validationError, handleError, type PluginContextV3, type CommandResult } from '@kb-labs/sdk';\nimport { post } from '../http.js';\nimport { resolveCliScope, scopeBody, CliScopeError } from '../scope.js';\n\ninterface UninstallFlags {\n json?: boolean;\n scope?: string;\n 'dry-run'?: boolean;\n}\n\ninterface UninstallInput {\n argv?: string[];\n flags?: UninstallFlags;\n}\n\nexport default defineCommand<unknown, UninstallInput, { removed: string[]; scope: string }>({\n id: 'marketplace:uninstall',\n description: 'Uninstall package(s) from marketplace',\n\n handler: {\n async intent(_ctx: PluginContextV3, input: UninstallInput) {\n const argv = input.argv ?? [];\n const packages = argv.length > 0 ? argv : ['(no packages specified)'];\n return {\n summary: `Uninstall ${packages.join(', ')} from marketplace`,\n operations: packages.map(pkg => ({\n type: 'delete' as const,\n resource: 'marketplace-package',\n details: { package: pkg },\n })),\n };\n },\n\n async execute(ctx: PluginContextV3, input: UninstallInput): Promise<CommandResult<{ removed: string[]; scope: string }>> {\n const argv = input.argv ?? [];\n const flags = (input.flags ?? input) as UninstallFlags;\n\n if (argv.length === 0) {\n validationError(ctx, 'Please specify at least one package to uninstall', 'Usage: kb marketplace uninstall <package>', flags.json);\n return { ok: false, error: 'Please specify at least one package to uninstall', result: { removed: [], scope: '' } };\n }\n\n let scopeCtx;\n try {\n scopeCtx = await resolveCliScope(ctx.cwd, flags.scope);\n } catch (err) {\n if (err instanceof CliScopeError) {\n validationError(ctx, err.message, undefined, flags.json);\n } else {\n handleError(ctx, err, flags.json);\n }\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { removed: [], scope: '' } };\n }\n\n try {\n // Server returns 204 on success; treat that as \"everything you asked for\n // was removed\" so the CLI has something to render.\n await post('/packages/uninstall', {\n packageIds: argv,\n ...scopeBody(scopeCtx),\n });\n\n if (flags.json) {\n ctx.ui?.json?.({ ok: true, removed: argv, scope: scopeCtx.scope });\n } else {\n ctx.ui?.success?.(`Removed from ${scopeCtx.scope}: ${argv.join(', ')}`);\n }\n return { ok: true, result: { removed: argv, scope: scopeCtx.scope } };\n } catch (err) {\n handleError(ctx, err, flags.json);\n return { ok: false, error: err instanceof Error ? err.message : String(err), result: { removed: [], scope: '' } };\n }\n },\n },\n});\n"]}
|