@kora-platform/cli 0.7.0-rc1 → 0.8.0-rc1
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/README.md +21 -0
- package/dist/api-client.d.ts +250 -93
- package/dist/api-client.js +187 -163
- package/dist/api-types.d.ts +280 -162
- package/dist/artifact-api-client.d.ts +28 -1
- package/dist/artifact-api-client.js +33 -0
- package/dist/artifact-commands.d.ts +5 -0
- package/dist/artifact-commands.js +172 -1
- package/dist/audit-commands.d.ts +12 -0
- package/dist/audit-commands.js +74 -0
- package/dist/auth-commands.d.ts +1 -0
- package/dist/auth-commands.js +116 -29
- package/dist/command-builders.d.ts +1 -0
- package/dist/command-builders.js +1 -0
- package/dist/command-groups.js +10 -12
- package/dist/command-registry.js +548 -243
- package/dist/commands.js +652 -602
- package/dist/environment-context.d.ts +9 -0
- package/dist/environment-context.js +32 -0
- package/dist/{integration-commands.d.ts → extension-commands.d.ts} +3 -2
- package/dist/extension-commands.js +446 -0
- package/dist/files.d.ts +33 -0
- package/dist/files.js +247 -12
- package/dist/format.d.ts +5 -0
- package/dist/format.js +78 -1
- package/dist/runner.js +14 -5
- package/dist/schema-registry-data.d.ts +272 -569
- package/dist/schema-registry-data.js +307 -700
- package/dist/session.d.ts +1 -0
- package/dist/transport.d.ts +10 -0
- package/dist/transport.js +22 -0
- package/dist/types.d.ts +2 -1
- package/dist/workspace-source.d.ts +1 -0
- package/dist/workspace-source.js +13 -0
- package/package.json +2 -1
- package/dist/integration-api-client.d.ts +0 -29
- package/dist/integration-api-client.js +0 -50
- package/dist/integration-commands.js +0 -208
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ParsedCommand } from "./runner.js";
|
|
2
|
+
import type { CliSessionState } from "./session.js";
|
|
3
|
+
export interface ResolvedCliEnvironment {
|
|
4
|
+
environment: string;
|
|
5
|
+
source: "flag" | "profile" | "production";
|
|
6
|
+
}
|
|
7
|
+
export declare function readCliEnvironmentFlag(parsed: ParsedCommand): string | undefined;
|
|
8
|
+
export declare function readRequiredCliEnvironmentFlag(parsed: ParsedCommand): string;
|
|
9
|
+
export declare function resolveCliEnvironment(parsed: ParsedCommand, session: CliSessionState): ResolvedCliEnvironment;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readOptionalStringFlag } from "./command-flags.js";
|
|
2
|
+
import { usageProblem } from "./cli-errors.js";
|
|
3
|
+
export function readCliEnvironmentFlag(parsed) {
|
|
4
|
+
return readOptionalStringFlag(parsed, "environment")?.trim() || undefined;
|
|
5
|
+
}
|
|
6
|
+
export function readRequiredCliEnvironmentFlag(parsed) {
|
|
7
|
+
const environment = readCliEnvironmentFlag(parsed);
|
|
8
|
+
if (!environment) {
|
|
9
|
+
throw usageProblem("Missing required flag --environment.", parsed.definition.id);
|
|
10
|
+
}
|
|
11
|
+
return environment;
|
|
12
|
+
}
|
|
13
|
+
export function resolveCliEnvironment(parsed, session) {
|
|
14
|
+
const fromFlag = readOptionalStringFlag(parsed, "environment")?.trim();
|
|
15
|
+
if (fromFlag) {
|
|
16
|
+
return {
|
|
17
|
+
environment: fromFlag,
|
|
18
|
+
source: "flag"
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const fromProfile = session.defaultEnvironment?.trim();
|
|
22
|
+
if (fromProfile) {
|
|
23
|
+
return {
|
|
24
|
+
environment: fromProfile,
|
|
25
|
+
source: "profile"
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
environment: "production",
|
|
30
|
+
source: "production"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { createPlatformApiClient } from "./api-client.js";
|
|
2
2
|
import type { CommandExecutionContext, ExecutedCommand } from "./command-types.js";
|
|
3
|
-
import type { ParsedCommand } from "./runner.js";
|
|
4
3
|
import type { CliSessionState } from "./session.js";
|
|
4
|
+
import type { ParsedCommand } from "./runner.js";
|
|
5
5
|
export type CliOrgScopeResolver = (parsed: ParsedCommand, context: CommandExecutionContext, api: ReturnType<typeof createPlatformApiClient>) => Promise<{
|
|
6
6
|
org: {
|
|
7
7
|
id: string;
|
|
8
|
+
slug: string;
|
|
8
9
|
};
|
|
9
10
|
session: CliSessionState;
|
|
10
11
|
}>;
|
|
11
|
-
export declare function
|
|
12
|
+
export declare function executeExtensions(parsed: ParsedCommand, context: CommandExecutionContext, api: ReturnType<typeof createPlatformApiClient>, resolveOrgScope: CliOrgScopeResolver): Promise<ExecutedCommand>;
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { readOptionalStringFlag, readRequiredStringFlag } from "./command-flags.js";
|
|
2
|
+
import { genericProblem, usageProblem } from "./cli-errors.js";
|
|
3
|
+
import { isZipArchivePath, readArchiveBytes, readJsonInputSpecifier, readPackageFileEntries, readWorkspaceExportMetadata, writeArchiveExport, writePackageExport } from "./files.js";
|
|
4
|
+
import { renderKeyValue, renderPrettyJson, renderSuccess, renderTable } from "./format.js";
|
|
5
|
+
import { confirmDestructive } from "./interaction.js";
|
|
6
|
+
import { readCliEnvironmentFlag, readRequiredCliEnvironmentFlag } from "./environment-context.js";
|
|
7
|
+
export async function executeExtensions(parsed, context, api, resolveOrgScope) {
|
|
8
|
+
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
9
|
+
switch (parsed.definition.id) {
|
|
10
|
+
case "extensions.export": {
|
|
11
|
+
const selector = resolveExtensionExportSelector(parsed);
|
|
12
|
+
const format = readOptionalStringFlag(parsed, "format");
|
|
13
|
+
const outPath = readOptionalStringFlag(parsed, "out");
|
|
14
|
+
if (format === "zip") {
|
|
15
|
+
if (!outPath) {
|
|
16
|
+
throw usageProblem("Missing required flag --out when --format zip is used.", parsed.definition.id);
|
|
17
|
+
}
|
|
18
|
+
const archive = await api.exportExtensionPackageArchive(session, org.id, selector);
|
|
19
|
+
await writeArchiveExport(outPath, archive.body, parsed.definition.id);
|
|
20
|
+
return {
|
|
21
|
+
data: { byteLength: archive.body.byteLength, outPath },
|
|
22
|
+
human: renderSuccess(`Exported zip archive to ${outPath}.`),
|
|
23
|
+
kind: "extensions_export",
|
|
24
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), orgId: org.id, outPath }, "Extension export completed.")
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const data = await api.exportExtensionPackage(session, org.id, selector);
|
|
28
|
+
if (format === "json") {
|
|
29
|
+
return {
|
|
30
|
+
data,
|
|
31
|
+
human: renderPrettyJson(data.exported),
|
|
32
|
+
kind: "extensions_export",
|
|
33
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), orgId: org.id }, "Extension export completed.")
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (!outPath) {
|
|
37
|
+
throw usageProblem("Missing required flag --out unless --format json is used.", parsed.definition.id);
|
|
38
|
+
}
|
|
39
|
+
await writePackageExport(outPath, {
|
|
40
|
+
files: data.exported.files,
|
|
41
|
+
metadata: data.exported.metadata
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
data,
|
|
45
|
+
human: renderSuccess(`Exported ${data.exported.files.length} files to ${outPath}.`),
|
|
46
|
+
kind: "extensions_export",
|
|
47
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), orgId: org.id, outPath }, "Extension export completed.")
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
case "extensions.validate": {
|
|
51
|
+
const path = readRequiredArg(parsed, "path");
|
|
52
|
+
const subdir = readOptionalStringFlag(parsed, "subdir");
|
|
53
|
+
const data = isZipArchivePath(path)
|
|
54
|
+
? await api.validateExtensionPackageArchive(session, org.id, await readArchiveBytes(path, parsed.definition.id), {
|
|
55
|
+
...(subdir ? { subdir } : {})
|
|
56
|
+
})
|
|
57
|
+
: await api.validateExtensionPackage(session, org.id, { files: await readPackageFileEntries(path) });
|
|
58
|
+
return {
|
|
59
|
+
data,
|
|
60
|
+
exitCode: data.ok ? 0 : 1,
|
|
61
|
+
human: renderValidationHuman(data),
|
|
62
|
+
kind: "extensions_validate",
|
|
63
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), orgId: org.id }, summarizeExtensionValidation(data))
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
case "extensions.publish": {
|
|
67
|
+
const path = readRequiredArg(parsed, "path");
|
|
68
|
+
const subdir = readOptionalStringFlag(parsed, "subdir");
|
|
69
|
+
const data = isZipArchivePath(path)
|
|
70
|
+
? await api.publishExtensionPackageArchive(session, org.id, await readArchiveBytes(path, parsed.definition.id), {
|
|
71
|
+
...(subdir ? { subdir } : {})
|
|
72
|
+
})
|
|
73
|
+
: await api.publishExtensionPackage(session, org.id, { files: await readPackageFileEntries(path) });
|
|
74
|
+
return {
|
|
75
|
+
data,
|
|
76
|
+
human: renderSuccess(`Published extension ${data.package.name} revision ${data.revision.id}.`),
|
|
77
|
+
kind: "extensions_publish",
|
|
78
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), mutated: true, orgId: org.id }, "Extension published.")
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
case "extensions.install": {
|
|
82
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
83
|
+
const permissions = await readOptionalPermissions(parsed, context);
|
|
84
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
85
|
+
const data = await api.installExtension(session, org.id, {
|
|
86
|
+
environment,
|
|
87
|
+
...(permissions ? { grantedPermissions: permissions } : {}),
|
|
88
|
+
packageRevisionId: readRequiredArg(parsed, "package-revision"),
|
|
89
|
+
slug: readRequiredArg(parsed, "slug")
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
data,
|
|
93
|
+
human: renderSuccess(`Installed extension ${data.install.slug} in ${environment}.`),
|
|
94
|
+
kind: "extensions_install",
|
|
95
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), environment, mutated: true, orgId: org.id }, "Extension installed.")
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
case "extensions.grant": {
|
|
99
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
100
|
+
const permissions = await readRequiredPermissions(parsed, context);
|
|
101
|
+
const installRef = readRequiredArg(parsed, "install");
|
|
102
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
103
|
+
const data = await api.grantExtensionPermissions(session, org.id, installRef, {
|
|
104
|
+
environment,
|
|
105
|
+
...await readInstallPreconditions(parsed, context, installRef),
|
|
106
|
+
grantedPermissions: permissions
|
|
107
|
+
});
|
|
108
|
+
return {
|
|
109
|
+
data,
|
|
110
|
+
human: renderSuccess(`Updated extension grants for ${data.install.slug} in ${environment}.`),
|
|
111
|
+
kind: "extensions_grant",
|
|
112
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), environment, mutated: true, orgId: org.id }, "Extension grants updated.")
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
case "extensions.update": {
|
|
116
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
117
|
+
const installRef = readRequiredArg(parsed, "install");
|
|
118
|
+
const input = {
|
|
119
|
+
environment,
|
|
120
|
+
...await readInstallPreconditions(parsed, context, installRef),
|
|
121
|
+
packageRevisionId: readRequiredStringFlag(parsed, "revision")
|
|
122
|
+
};
|
|
123
|
+
if (parsed.flags["dry-run"] === true) {
|
|
124
|
+
const data = await api.planExtensionInstallUpdate(session, org.id, installRef, input);
|
|
125
|
+
const plan = data.plan;
|
|
126
|
+
return {
|
|
127
|
+
data,
|
|
128
|
+
exitCode: plan.updateAllowed === false ? 1 : 0,
|
|
129
|
+
human: renderPrettyJson(data.plan),
|
|
130
|
+
kind: "extensions_update_plan",
|
|
131
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), dryRun: true, environment, mutated: false, orgId: org.id }, summarizeExtensionUpdatePlan(plan))
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
135
|
+
const data = await api.updateExtensionInstall(session, org.id, installRef, input);
|
|
136
|
+
return {
|
|
137
|
+
data,
|
|
138
|
+
human: renderSuccess(`Updated extension ${data.install.slug} to ${data.install.packageRevisionId} in ${environment}.`),
|
|
139
|
+
kind: "extensions_update",
|
|
140
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), environment, mutated: true, orgId: org.id }, "Extension updated.")
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
case "extensions.built-ins.list": {
|
|
144
|
+
const environment = readCliEnvironmentFlag(parsed);
|
|
145
|
+
const data = await api.listBuiltInExtensions(session, org.id, {
|
|
146
|
+
...(environment ? { environment } : {})
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
data,
|
|
150
|
+
human: renderBuiltInExtensionTable(data.builtIns),
|
|
151
|
+
kind: "extensions_built_ins_list",
|
|
152
|
+
meta: { command: parsed.definition.path.join(" "), ...(environment ? { environment } : {}), orgId: org.id }
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
case "extensions.built-ins.install": {
|
|
156
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
157
|
+
const permissions = await readOptionalPermissions(parsed, context);
|
|
158
|
+
const slug = readOptionalStringFlag(parsed, "slug");
|
|
159
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
160
|
+
const data = await api.installBuiltInExtension(session, org.id, readRequiredArg(parsed, "built-in"), {
|
|
161
|
+
environment,
|
|
162
|
+
...(permissions ? { grantedPermissions: permissions } : {}),
|
|
163
|
+
...(slug ? { slug } : {})
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
data,
|
|
167
|
+
human: renderSuccess(`Installed built-in extension ${data.install.slug} in ${environment}.`),
|
|
168
|
+
kind: "extensions_built_ins_install",
|
|
169
|
+
meta: withExtensionSummary({ command: parsed.definition.path.join(" "), environment, mutated: true, orgId: org.id }, "Built-in extension installed.")
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
case "extensions.list": {
|
|
173
|
+
const environment = readCliEnvironmentFlag(parsed);
|
|
174
|
+
const data = await api.listExtensionInstalls(session, org.id, {
|
|
175
|
+
...(environment ? { environment } : {})
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
data,
|
|
179
|
+
human: renderExtensionInstallTable(data.installs),
|
|
180
|
+
kind: "extensions_list",
|
|
181
|
+
meta: { command: parsed.definition.path.join(" "), ...(environment ? { environment } : {}), orgId: org.id }
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
case "extensions.inspect": {
|
|
185
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
186
|
+
const data = await api.getExtensionInstall(session, org.id, readRequiredArg(parsed, "install"), {
|
|
187
|
+
environment
|
|
188
|
+
});
|
|
189
|
+
return {
|
|
190
|
+
data,
|
|
191
|
+
human: renderPrettyJson(data.install),
|
|
192
|
+
kind: "extensions_inspect",
|
|
193
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
case "extensions.detail": {
|
|
197
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
198
|
+
const data = await api.getExtensionInstallDetail(session, org.id, readRequiredArg(parsed, "install"), {
|
|
199
|
+
environment
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
data,
|
|
203
|
+
human: renderExtensionInstallDetailHuman(data.detail),
|
|
204
|
+
kind: "extensions_detail",
|
|
205
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
case "extensions.disable": {
|
|
209
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
210
|
+
const installRef = readRequiredArg(parsed, "install");
|
|
211
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
212
|
+
const data = await api.disableExtensionInstall(session, org.id, installRef, {
|
|
213
|
+
environment
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
data,
|
|
217
|
+
human: renderSuccess(`Disabled extension ${data.install.slug} in ${environment}.`),
|
|
218
|
+
kind: "extensions_disable",
|
|
219
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
case "extensions.enable": {
|
|
223
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
224
|
+
const installRef = readRequiredArg(parsed, "install");
|
|
225
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
226
|
+
const data = await api.enableExtensionInstall(session, org.id, installRef, {
|
|
227
|
+
environment
|
|
228
|
+
});
|
|
229
|
+
return {
|
|
230
|
+
data,
|
|
231
|
+
human: renderSuccess(`Enabled extension ${data.install.slug} in ${environment}.`),
|
|
232
|
+
kind: "extensions_enable",
|
|
233
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
case "extensions.delete": {
|
|
237
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
238
|
+
const installRef = readRequiredArg(parsed, "install");
|
|
239
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
240
|
+
await confirmDestructive(parsed, context, `Delete extension install ${installRef} from ${environment}?`);
|
|
241
|
+
await api.deleteExtensionInstall(session, org.id, installRef, {
|
|
242
|
+
environment
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
data: {},
|
|
246
|
+
human: renderSuccess(`Deleted extension install ${installRef} from ${environment}.`),
|
|
247
|
+
kind: "extensions_delete",
|
|
248
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
default:
|
|
252
|
+
throw genericProblem(`Unhandled extensions command ${parsed.definition.id}.`, parsed.definition.id);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function withExtensionSummary(meta, summary) {
|
|
256
|
+
const trimmed = summary?.trim();
|
|
257
|
+
return trimmed && trimmed.length > 0 ? { ...meta, summary: trimmed } : meta;
|
|
258
|
+
}
|
|
259
|
+
function summarizeExtensionValidation(input) {
|
|
260
|
+
return input.ok
|
|
261
|
+
? `Extension validation passed${formatExtensionDiagnosticSuffix(input.diagnostics)}.`
|
|
262
|
+
: `Extension validation failed${formatExtensionDiagnosticSuffix(input.diagnostics)}.`;
|
|
263
|
+
}
|
|
264
|
+
function summarizeExtensionUpdatePlan(plan) {
|
|
265
|
+
const diagnostics = Array.isArray(plan.diagnostics)
|
|
266
|
+
? plan.diagnostics.filter((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry))
|
|
267
|
+
: [];
|
|
268
|
+
return plan.updateAllowed === false
|
|
269
|
+
? `Extension update dry run blocked${formatExtensionDiagnosticSuffix(diagnostics)}.`
|
|
270
|
+
: `Extension update dry run passed${formatExtensionDiagnosticSuffix(diagnostics)}.`;
|
|
271
|
+
}
|
|
272
|
+
function formatExtensionDiagnosticSuffix(diagnostics) {
|
|
273
|
+
if (diagnostics.length === 0) {
|
|
274
|
+
return "";
|
|
275
|
+
}
|
|
276
|
+
const message = diagnostics
|
|
277
|
+
.map((diagnostic) => typeof diagnostic.message === "string" ? diagnostic.message.trim() : "")
|
|
278
|
+
.find((entry) => entry.length > 0);
|
|
279
|
+
return ` with ${String(diagnostics.length)} diagnostic${diagnostics.length === 1 ? "" : "s"}${message ? `: ${truncateExtensionSummary(message)}` : ""}`;
|
|
280
|
+
}
|
|
281
|
+
function truncateExtensionSummary(value) {
|
|
282
|
+
return value.length <= 180 ? value : `${value.slice(0, 177)}...`;
|
|
283
|
+
}
|
|
284
|
+
function renderValidationHuman(input) {
|
|
285
|
+
const summary = renderKeyValue([
|
|
286
|
+
{ label: "Valid", value: input.ok },
|
|
287
|
+
{ label: "Files", value: input.fileCount },
|
|
288
|
+
{ label: "Bytes", value: input.totalBytes },
|
|
289
|
+
{ label: "Revision hash", value: input.revisionHash ?? "" }
|
|
290
|
+
]);
|
|
291
|
+
if (input.diagnostics.length === 0) {
|
|
292
|
+
return summary;
|
|
293
|
+
}
|
|
294
|
+
return [
|
|
295
|
+
summary,
|
|
296
|
+
"",
|
|
297
|
+
renderTable(input.diagnostics, [
|
|
298
|
+
{ key: "code", label: "Code" },
|
|
299
|
+
{ key: "path", label: "Path" },
|
|
300
|
+
{ key: "message", label: "Message" }
|
|
301
|
+
])
|
|
302
|
+
].join("\n");
|
|
303
|
+
}
|
|
304
|
+
function renderExtensionInstallTable(installs) {
|
|
305
|
+
return renderTable(installs, [
|
|
306
|
+
{ key: "slug", label: "Slug" },
|
|
307
|
+
{ key: "enabled", label: "Enabled" },
|
|
308
|
+
{ key: "packageRevisionId", label: "Revision" },
|
|
309
|
+
{ key: "grantedPermissions", label: "Grants" }
|
|
310
|
+
]);
|
|
311
|
+
}
|
|
312
|
+
function renderBuiltInExtensionTable(builtIns) {
|
|
313
|
+
return renderTable(builtIns, [
|
|
314
|
+
{ key: "slug", label: "Slug" },
|
|
315
|
+
{ key: "title", label: "Title" },
|
|
316
|
+
{ key: "availability", label: "Availability" },
|
|
317
|
+
{ key: "installedSlug", label: "Installed" },
|
|
318
|
+
{ key: "description", label: "Description" }
|
|
319
|
+
]);
|
|
320
|
+
}
|
|
321
|
+
function renderExtensionInstallDetailHuman(detail) {
|
|
322
|
+
const registrations = readRecord(detail.registrations?.registrations);
|
|
323
|
+
const functions = Object.keys(readRecord(registrations?.functions) ?? {});
|
|
324
|
+
const tools = Object.keys(readRecord(registrations?.tools) ?? {});
|
|
325
|
+
return [
|
|
326
|
+
renderKeyValue([
|
|
327
|
+
{ label: "Slug", value: detail.install.slug },
|
|
328
|
+
{ label: "Package", value: detail.package.name },
|
|
329
|
+
{ label: "Revision", value: detail.install.packageRevisionId },
|
|
330
|
+
{ label: "Functions", value: functions.join(", ") },
|
|
331
|
+
{ label: "Tools", value: tools.join(", ") }
|
|
332
|
+
]),
|
|
333
|
+
"",
|
|
334
|
+
renderPrettyJson(detail)
|
|
335
|
+
].join("\n");
|
|
336
|
+
}
|
|
337
|
+
function resolveExtensionExportSelector(parsed) {
|
|
338
|
+
const install = readOptionalStringFlag(parsed, "install");
|
|
339
|
+
const revision = readOptionalStringFlag(parsed, "revision");
|
|
340
|
+
const packageName = readOptionalStringFlag(parsed, "package");
|
|
341
|
+
const selectedCount = [Boolean(install), Boolean(revision), Boolean(packageName)].filter(Boolean).length;
|
|
342
|
+
if (selectedCount !== 1) {
|
|
343
|
+
throw usageProblem("Use exactly one of --install, --revision, or --package --latest.", parsed.definition.id);
|
|
344
|
+
}
|
|
345
|
+
if (install) {
|
|
346
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
347
|
+
return { install, ...(environment ? { environment } : {}) };
|
|
348
|
+
}
|
|
349
|
+
if (revision) {
|
|
350
|
+
return { revision };
|
|
351
|
+
}
|
|
352
|
+
if (packageName && parsed.flags.latest === true) {
|
|
353
|
+
return { latest: true, package: packageName };
|
|
354
|
+
}
|
|
355
|
+
throw usageProblem("Use --latest when exporting by package name.", parsed.definition.id);
|
|
356
|
+
}
|
|
357
|
+
async function readInstallPreconditions(parsed, context, installRef) {
|
|
358
|
+
const metadata = await readExtensionInstallExportMetadata(context.cwd, installRef);
|
|
359
|
+
const explicitBasePackageRevision = readOptionalStringFlag(parsed, "base-package-revision");
|
|
360
|
+
const explicitBaseUpdatedAt = readOptionalStringFlag(parsed, "base-updated-at");
|
|
361
|
+
return {
|
|
362
|
+
...(metadata?.packageRevisionId
|
|
363
|
+
? { basePackageRevisionId: metadata.packageRevisionId }
|
|
364
|
+
: {}),
|
|
365
|
+
...(metadata?.installUpdatedAt
|
|
366
|
+
? { baseUpdatedAt: metadata.installUpdatedAt }
|
|
367
|
+
: {}),
|
|
368
|
+
...(explicitBasePackageRevision
|
|
369
|
+
? { basePackageRevisionId: explicitBasePackageRevision }
|
|
370
|
+
: {}),
|
|
371
|
+
...(explicitBaseUpdatedAt
|
|
372
|
+
? { baseUpdatedAt: explicitBaseUpdatedAt }
|
|
373
|
+
: {})
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
async function readExtensionInstallExportMetadata(cwd, installRef) {
|
|
377
|
+
const metadata = await readWorkspaceExportMetadata(cwd);
|
|
378
|
+
if (!metadata ||
|
|
379
|
+
metadata.resourceKind !== "extension_package" ||
|
|
380
|
+
metadata.source !== "install" ||
|
|
381
|
+
(metadata.installId !== installRef && metadata.installSlug !== installRef)) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
...(typeof metadata.installUpdatedAt === "string"
|
|
386
|
+
? { installUpdatedAt: metadata.installUpdatedAt }
|
|
387
|
+
: {}),
|
|
388
|
+
...(typeof metadata.packageRevisionId === "string"
|
|
389
|
+
? { packageRevisionId: metadata.packageRevisionId }
|
|
390
|
+
: {})
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
async function readOptionalPermissions(parsed, context) {
|
|
394
|
+
const specifier = readOptionalStringFlag(parsed, "permissions");
|
|
395
|
+
if (!specifier) {
|
|
396
|
+
return undefined;
|
|
397
|
+
}
|
|
398
|
+
return normalizePermissions(await readJsonInputSpecifier(specifier, context.stdin, parsed.definition.id), parsed.definition.id);
|
|
399
|
+
}
|
|
400
|
+
async function readRequiredPermissions(parsed, context) {
|
|
401
|
+
const specifier = readRequiredStringFlag(parsed, "permissions");
|
|
402
|
+
return normalizePermissions(await readJsonInputSpecifier(specifier, context.stdin, parsed.definition.id), parsed.definition.id);
|
|
403
|
+
}
|
|
404
|
+
function normalizePermissions(input, instance) {
|
|
405
|
+
const permissions = input.grantedPermissions && isRecord(input.grantedPermissions)
|
|
406
|
+
? input.grantedPermissions
|
|
407
|
+
: input;
|
|
408
|
+
const normalized = {};
|
|
409
|
+
for (const key of ["callbacks", "cloud", "schedules", "secrets", "storage"]) {
|
|
410
|
+
if (permissions[key] !== undefined) {
|
|
411
|
+
if (typeof permissions[key] !== "boolean") {
|
|
412
|
+
throw usageProblem(`Permission '${key}' must be a boolean.`, instance);
|
|
413
|
+
}
|
|
414
|
+
normalized[key] = permissions[key];
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (permissions.egress !== undefined) {
|
|
418
|
+
if (!Array.isArray(permissions.egress) || permissions.egress.some((entry) => typeof entry !== "string")) {
|
|
419
|
+
throw usageProblem("Permission 'egress' must be an array of host strings.", instance);
|
|
420
|
+
}
|
|
421
|
+
normalized.egress = [...new Set(permissions.egress)].sort();
|
|
422
|
+
}
|
|
423
|
+
const unknownKeys = Object.keys(permissions).filter((key) => !["callbacks", "cloud", "egress", "schedules", "secrets", "storage"].includes(key));
|
|
424
|
+
if (unknownKeys.length > 0) {
|
|
425
|
+
throw usageProblem(`Unknown extension permission: ${unknownKeys.join(", ")}.`, instance);
|
|
426
|
+
}
|
|
427
|
+
return normalized;
|
|
428
|
+
}
|
|
429
|
+
function isRecord(value) {
|
|
430
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
431
|
+
}
|
|
432
|
+
function readRecord(value) {
|
|
433
|
+
return isRecord(value) ? value : null;
|
|
434
|
+
}
|
|
435
|
+
function announceResolvedEnvironment(parsed, context, environment) {
|
|
436
|
+
if (!parsed.json) {
|
|
437
|
+
context.stdout.write(`Environment: ${environment}\n`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function readRequiredArg(parsed, name) {
|
|
441
|
+
const value = parsed.args[name];
|
|
442
|
+
if (!value) {
|
|
443
|
+
throw usageProblem(`Missing required argument <${name}>.`, parsed.definition.id);
|
|
444
|
+
}
|
|
445
|
+
return value;
|
|
446
|
+
}
|
package/dist/files.d.ts
CHANGED
|
@@ -5,6 +5,39 @@ export declare function readImportEntries(pathValue: string): Promise<Array<{
|
|
|
5
5
|
content: string;
|
|
6
6
|
path: string;
|
|
7
7
|
}>>;
|
|
8
|
+
export declare function isZipArchivePath(pathValue: string): boolean;
|
|
9
|
+
export declare function readArchiveBytes(pathValue: string, instance: string): Promise<Uint8Array>;
|
|
10
|
+
export declare function readWorkspaceTestEntries(pathValue: string): Promise<Array<{
|
|
11
|
+
content: string;
|
|
12
|
+
path: string;
|
|
13
|
+
}>>;
|
|
14
|
+
export declare function readWorkspaceExportMetadata(pathValue: string): Promise<Record<string, unknown> | null>;
|
|
15
|
+
export declare function writeWorkspaceExport(outPath: string, envelope: {
|
|
16
|
+
files: Array<{
|
|
17
|
+
content: string;
|
|
18
|
+
path: string;
|
|
19
|
+
}>;
|
|
20
|
+
metadata: Record<string, unknown>;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
export declare function writeReleaseSourceFiles(outPath: string, envelope: {
|
|
23
|
+
files: Array<{
|
|
24
|
+
content: string;
|
|
25
|
+
path: string;
|
|
26
|
+
}>;
|
|
27
|
+
metadata: Record<string, unknown>;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
export declare function writeArchiveExport(outPath: string, archive: Uint8Array, instance: string): Promise<void>;
|
|
30
|
+
export declare function writePackageExport(outPath: string, envelope: {
|
|
31
|
+
files: Array<{
|
|
32
|
+
contentBase64: string;
|
|
33
|
+
path: string;
|
|
34
|
+
}>;
|
|
35
|
+
metadata: Record<string, unknown>;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
export declare function readPackageFileEntries(pathValue: string): Promise<Array<{
|
|
38
|
+
contentBase64: string;
|
|
39
|
+
path: string;
|
|
40
|
+
}>>;
|
|
8
41
|
export declare function parseEnvFile(pathValue: string): Promise<Array<{
|
|
9
42
|
name: string;
|
|
10
43
|
value: string;
|