@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.
Files changed (38) hide show
  1. package/README.md +21 -0
  2. package/dist/api-client.d.ts +250 -93
  3. package/dist/api-client.js +187 -163
  4. package/dist/api-types.d.ts +280 -162
  5. package/dist/artifact-api-client.d.ts +28 -1
  6. package/dist/artifact-api-client.js +33 -0
  7. package/dist/artifact-commands.d.ts +5 -0
  8. package/dist/artifact-commands.js +172 -1
  9. package/dist/audit-commands.d.ts +12 -0
  10. package/dist/audit-commands.js +74 -0
  11. package/dist/auth-commands.d.ts +1 -0
  12. package/dist/auth-commands.js +116 -29
  13. package/dist/command-builders.d.ts +1 -0
  14. package/dist/command-builders.js +1 -0
  15. package/dist/command-groups.js +10 -12
  16. package/dist/command-registry.js +548 -243
  17. package/dist/commands.js +652 -602
  18. package/dist/environment-context.d.ts +9 -0
  19. package/dist/environment-context.js +32 -0
  20. package/dist/{integration-commands.d.ts → extension-commands.d.ts} +3 -2
  21. package/dist/extension-commands.js +446 -0
  22. package/dist/files.d.ts +33 -0
  23. package/dist/files.js +247 -12
  24. package/dist/format.d.ts +5 -0
  25. package/dist/format.js +78 -1
  26. package/dist/runner.js +14 -5
  27. package/dist/schema-registry-data.d.ts +272 -569
  28. package/dist/schema-registry-data.js +307 -700
  29. package/dist/session.d.ts +1 -0
  30. package/dist/transport.d.ts +10 -0
  31. package/dist/transport.js +22 -0
  32. package/dist/types.d.ts +2 -1
  33. package/dist/workspace-source.d.ts +1 -0
  34. package/dist/workspace-source.js +13 -0
  35. package/package.json +2 -1
  36. package/dist/integration-api-client.d.ts +0 -29
  37. package/dist/integration-api-client.js +0 -50
  38. 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 executeIntegrations(parsed: ParsedCommand, context: CommandExecutionContext, api: ReturnType<typeof createPlatformApiClient>, resolveOrgScope: CliOrgScopeResolver): Promise<ExecutedCommand>;
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;