@kitsy/cnos-cli 1.5.0 → 1.6.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/index.js +484 -159
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ var COMMAND_OPTION_KEYS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
|
17
17
|
"--provider",
|
|
18
18
|
"--passphrase",
|
|
19
19
|
"--vault",
|
|
20
|
+
"--package-root",
|
|
20
21
|
"--inherit",
|
|
21
22
|
"--as",
|
|
22
23
|
"--set",
|
|
@@ -250,94 +251,169 @@ function printJson(value) {
|
|
|
250
251
|
return JSON.stringify(value, null, 2);
|
|
251
252
|
}
|
|
252
253
|
|
|
253
|
-
// src/services/
|
|
254
|
+
// src/services/projections.ts
|
|
254
255
|
import { mkdir, writeFile } from "fs/promises";
|
|
255
256
|
import path2 from "path";
|
|
257
|
+
import { resolveBrowserData, resolveFrameworkEnv, resolveServerProjection } from "@kitsy/cnos/build";
|
|
258
|
+
import { stringifyYaml } from "@kitsy/cnos/internal";
|
|
256
259
|
|
|
257
260
|
// src/services/runtime.ts
|
|
258
261
|
import { createCnos } from "@kitsy/cnos/configure";
|
|
259
262
|
async function createRuntimeService(options = {}) {
|
|
260
263
|
return createCnos({
|
|
264
|
+
...options.cwd ? { cwd: options.cwd } : {},
|
|
261
265
|
...options.root ? { root: options.root } : {},
|
|
262
266
|
...options.workspace ? { workspace: options.workspace } : {},
|
|
263
267
|
...options.profile ? { profile: options.profile } : {},
|
|
264
268
|
...options.globalRoot ? { globalRoot: options.globalRoot } : {},
|
|
265
269
|
...options.cliArgs && options.cliArgs.length > 0 ? { cliArgs: options.cliArgs } : {},
|
|
266
270
|
...options.secretResolution ? { secretResolution: options.secretResolution } : {},
|
|
271
|
+
...typeof options.secretRefreshTtl === "number" ? { secretRefreshTtl: options.secretRefreshTtl } : {},
|
|
267
272
|
processEnv: options.processEnv ?? process.env
|
|
268
273
|
});
|
|
269
274
|
}
|
|
270
275
|
|
|
271
|
-
// src/services/
|
|
272
|
-
function
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
276
|
+
// src/services/projections.ts
|
|
277
|
+
function stringifyScalar(value) {
|
|
278
|
+
if (value === void 0 || value === null) {
|
|
279
|
+
return "";
|
|
280
|
+
}
|
|
281
|
+
if (typeof value === "string") {
|
|
282
|
+
return value;
|
|
283
|
+
}
|
|
284
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
285
|
+
return String(value);
|
|
286
|
+
}
|
|
287
|
+
return JSON.stringify(value);
|
|
281
288
|
}
|
|
282
|
-
function
|
|
283
|
-
return
|
|
289
|
+
function escapeShell(value) {
|
|
290
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
284
291
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
...options,
|
|
288
|
-
cliArgs: [...options.cliArgs ?? []]
|
|
289
|
-
});
|
|
290
|
-
const env = resolveEnvFromRuntime(runtime, options.cliArgs ?? []);
|
|
291
|
-
return {
|
|
292
|
-
runtime,
|
|
293
|
-
env,
|
|
294
|
-
output: formatEnvOutput(env)
|
|
295
|
-
};
|
|
292
|
+
function quoteToml(value) {
|
|
293
|
+
return `"${escapeShell(value)}"`;
|
|
296
294
|
}
|
|
297
|
-
function
|
|
298
|
-
|
|
295
|
+
function formatKeyValueMap(values, format) {
|
|
296
|
+
const entries = Object.entries(values).sort(([left], [right]) => left.localeCompare(right));
|
|
297
|
+
switch (format) {
|
|
298
|
+
case "json":
|
|
299
|
+
return `${JSON.stringify(Object.fromEntries(entries), null, 2)}
|
|
300
|
+
`;
|
|
301
|
+
case "yaml":
|
|
302
|
+
return stringifyYaml(Object.fromEntries(entries));
|
|
303
|
+
case "shell":
|
|
304
|
+
return entries.map(([key, value]) => `export ${key}="${escapeShell(stringifyScalar(value))}"`).join("\n");
|
|
305
|
+
case "toml":
|
|
306
|
+
return entries.map(([key, value]) => `${key} = ${quoteToml(stringifyScalar(value))}`).join("\n");
|
|
307
|
+
case "docker-env":
|
|
308
|
+
case "dotenv":
|
|
309
|
+
default:
|
|
310
|
+
return entries.map(([key, value]) => `${key}=${stringifyScalar(value)}`).join("\n");
|
|
311
|
+
}
|
|
299
312
|
}
|
|
300
|
-
async function
|
|
301
|
-
const targetPath =
|
|
313
|
+
async function writeProjectionFile(to, output, root = process.cwd()) {
|
|
314
|
+
const targetPath = path2.resolve(root, to);
|
|
302
315
|
await mkdir(path2.dirname(targetPath), { recursive: true });
|
|
303
316
|
await writeFile(targetPath, output, "utf8");
|
|
304
317
|
return targetPath;
|
|
305
318
|
}
|
|
306
|
-
async function
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
319
|
+
async function buildServerProjectionArtifact(to, options = {}, format = "json") {
|
|
320
|
+
const projection = await resolveServerProjection(options);
|
|
321
|
+
const output = format === "yaml" ? stringifyYaml(projection) : `${JSON.stringify(projection, null, 2)}
|
|
322
|
+
`;
|
|
323
|
+
const targetPath = await writeProjectionFile(to, output, options.root ?? process.cwd());
|
|
324
|
+
return { targetPath, output };
|
|
325
|
+
}
|
|
326
|
+
async function buildBrowserProjectionArtifact(to, options = {}, format = "json") {
|
|
327
|
+
const projection = await resolveBrowserData(options);
|
|
328
|
+
const output = format === "yaml" ? stringifyYaml(projection) : `${JSON.stringify(projection, null, 2)}
|
|
329
|
+
`;
|
|
330
|
+
const targetPath = await writeProjectionFile(to, output, options.root ?? process.cwd());
|
|
331
|
+
return { targetPath, output };
|
|
332
|
+
}
|
|
333
|
+
async function buildPublicProjectionArtifact(to, options = {}, format = "dotenv") {
|
|
334
|
+
const cliArgs = [...options.cliArgs ?? []];
|
|
335
|
+
const frameworkIndex = cliArgs.indexOf("--framework");
|
|
336
|
+
const framework = frameworkIndex >= 0 && cliArgs[frameworkIndex + 1] ? cliArgs[frameworkIndex + 1] : "generic";
|
|
337
|
+
const env = await resolveFrameworkEnv(options, framework);
|
|
338
|
+
const output = formatKeyValueMap(env, format);
|
|
339
|
+
const targetPath = await writeProjectionFile(to, output, options.root ?? process.cwd());
|
|
340
|
+
return { targetPath, output, env };
|
|
341
|
+
}
|
|
342
|
+
async function buildEnvProjectionArtifact(to, options = {}, format = "dotenv") {
|
|
343
|
+
const runtime = await createRuntimeService({
|
|
344
|
+
...options,
|
|
345
|
+
cliArgs: [...options.cliArgs ?? []]
|
|
346
|
+
});
|
|
347
|
+
const env = runtime.toEnv();
|
|
348
|
+
for (const [envVar, logicalKey] of Object.entries(runtime.manifest.envMapping.explicit)) {
|
|
349
|
+
const entry = runtime.graph.entries.get(logicalKey);
|
|
350
|
+
if (entry?.namespace === "secret" && !(envVar in env)) {
|
|
351
|
+
env[envVar] = "****";
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const output = formatKeyValueMap(env, format);
|
|
355
|
+
const targetPath = await writeProjectionFile(to, output, options.root ?? process.cwd());
|
|
356
|
+
return { targetPath, output, env };
|
|
313
357
|
}
|
|
314
358
|
|
|
315
359
|
// src/commands/build.ts
|
|
316
360
|
async function runBuild(subcommand, options = {}) {
|
|
317
|
-
if (subcommand !== "env") {
|
|
318
|
-
throw new Error(`Unsupported build target: ${subcommand ?? "(missing)"}`);
|
|
319
|
-
}
|
|
320
361
|
const infoArgs = [...options.cliArgs ?? []];
|
|
362
|
+
const format = consumeOption(infoArgs, "--format") ?? void 0;
|
|
321
363
|
const isPublic = consumeFlag(infoArgs, "--public");
|
|
322
364
|
const framework = consumeOption(infoArgs, "--framework");
|
|
323
365
|
consumeOption(infoArgs, "--prefix");
|
|
324
366
|
const to = consumeOption(infoArgs, "--to");
|
|
367
|
+
const provenanceTarget = consumeOption(infoArgs, "--with-provenance");
|
|
325
368
|
if (!to) {
|
|
326
|
-
throw new Error(
|
|
369
|
+
throw new Error(`build ${subcommand ?? "(missing)"} requires --to <path>`);
|
|
370
|
+
}
|
|
371
|
+
let targetPath;
|
|
372
|
+
let count = 0;
|
|
373
|
+
switch (subcommand) {
|
|
374
|
+
case "env": {
|
|
375
|
+
const result = await buildEnvProjectionArtifact(to, {
|
|
376
|
+
...options,
|
|
377
|
+
cliArgs: [...options.cliArgs ?? []]
|
|
378
|
+
}, format ?? "dotenv");
|
|
379
|
+
targetPath = result.targetPath;
|
|
380
|
+
count = Object.keys(result.env).length;
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
case "public": {
|
|
384
|
+
const result = await buildPublicProjectionArtifact(to, {
|
|
385
|
+
...options,
|
|
386
|
+
cliArgs: [...options.cliArgs ?? []]
|
|
387
|
+
}, format ?? "dotenv");
|
|
388
|
+
targetPath = result.targetPath;
|
|
389
|
+
count = Object.keys(result.env).length;
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
case "server": {
|
|
393
|
+
const result = await buildServerProjectionArtifact(to, options, format ?? "json");
|
|
394
|
+
targetPath = result.targetPath;
|
|
395
|
+
count = 1;
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
case "browser": {
|
|
399
|
+
const result = await buildBrowserProjectionArtifact(to, options, format ?? "json");
|
|
400
|
+
targetPath = result.targetPath;
|
|
401
|
+
count = 1;
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
default:
|
|
405
|
+
throw new Error(`Unsupported build target: ${subcommand ?? "(missing)"}`);
|
|
327
406
|
}
|
|
328
|
-
const result = await materializeEnvToFile(to, {
|
|
329
|
-
...options,
|
|
330
|
-
cliArgs: [...options.cliArgs ?? []]
|
|
331
|
-
});
|
|
332
407
|
if (options.json) {
|
|
333
408
|
return printJson({
|
|
334
|
-
to:
|
|
335
|
-
count
|
|
409
|
+
to: targetPath,
|
|
410
|
+
count,
|
|
336
411
|
public: isPublic,
|
|
337
|
-
...framework ? { framework } : {}
|
|
412
|
+
...framework ? { framework } : {},
|
|
413
|
+
...provenanceTarget ? { provenance: provenanceTarget } : {}
|
|
338
414
|
});
|
|
339
415
|
}
|
|
340
|
-
return `built
|
|
416
|
+
return `built ${subcommand} artifact at ${displayPath(targetPath, options.root ?? process.cwd())}`;
|
|
341
417
|
}
|
|
342
418
|
|
|
343
419
|
// src/commands/define.ts
|
|
@@ -352,7 +428,7 @@ import {
|
|
|
352
428
|
parseYaml,
|
|
353
429
|
resolveConfigDocumentPath,
|
|
354
430
|
resolveVaultAuth,
|
|
355
|
-
stringifyYaml
|
|
431
|
+
stringifyYaml as stringifyYaml2
|
|
356
432
|
} from "@kitsy/cnos/internal";
|
|
357
433
|
function setNestedValue(target, pathSegments, value) {
|
|
358
434
|
const [head, ...tail] = pathSegments;
|
|
@@ -454,7 +530,7 @@ async function defineValue(namespace, configPath, rawValue, options = {}) {
|
|
|
454
530
|
const parsedValue = parseScalarValue(rawValue);
|
|
455
531
|
setNestedValue(document, configPath.split("."), parsedValue);
|
|
456
532
|
await mkdir2(path3.dirname(filePath), { recursive: true });
|
|
457
|
-
await writeFile2(filePath,
|
|
533
|
+
await writeFile2(filePath, stringifyYaml2(document), "utf8");
|
|
458
534
|
return {
|
|
459
535
|
filePath,
|
|
460
536
|
value: parsedValue
|
|
@@ -492,7 +568,7 @@ async function setSecret(configPath, rawValue, options = {}) {
|
|
|
492
568
|
}
|
|
493
569
|
setNestedValue(document, configPath.split("."), reference);
|
|
494
570
|
await mkdir2(path3.dirname(filePath), { recursive: true });
|
|
495
|
-
await writeFile2(filePath,
|
|
571
|
+
await writeFile2(filePath, stringifyYaml2(document), "utf8");
|
|
496
572
|
return {
|
|
497
573
|
filePath,
|
|
498
574
|
provider: reference.provider,
|
|
@@ -514,7 +590,7 @@ async function deleteSecret(configPath, options = {}) {
|
|
|
514
590
|
deleted: false
|
|
515
591
|
};
|
|
516
592
|
}
|
|
517
|
-
await writeFile2(filePath,
|
|
593
|
+
await writeFile2(filePath, stringifyYaml2(document), "utf8");
|
|
518
594
|
const secretRef = metadata?.secretRef;
|
|
519
595
|
if (isSecretReference(secretRef) && secretRef.provider === "local") {
|
|
520
596
|
const definition = runtime.manifest.vaults[secretRef.vault ?? "default"];
|
|
@@ -559,7 +635,7 @@ async function deleteValue(namespace, configPath, options = {}) {
|
|
|
559
635
|
deleted: false
|
|
560
636
|
};
|
|
561
637
|
}
|
|
562
|
-
await writeFile2(filePath,
|
|
638
|
+
await writeFile2(filePath, stringifyYaml2(document), "utf8");
|
|
563
639
|
return {
|
|
564
640
|
filePath,
|
|
565
641
|
deleted: true
|
|
@@ -598,6 +674,52 @@ async function runDefine(namespace, configPath, rawValue, options = {}) {
|
|
|
598
674
|
return `defined ${namespace}.${configPath} in ${displayPath(result.filePath, root)}`;
|
|
599
675
|
}
|
|
600
676
|
|
|
677
|
+
// src/services/envMaterialization.ts
|
|
678
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
679
|
+
import path5 from "path";
|
|
680
|
+
function resolveEnvFromRuntime(runtime, cliArgs = []) {
|
|
681
|
+
const args = [...cliArgs];
|
|
682
|
+
const isPublic = consumeFlag(args, "--public");
|
|
683
|
+
const framework = consumeOption(args, "--framework");
|
|
684
|
+
const prefix = consumeOption(args, "--prefix");
|
|
685
|
+
return isPublic ? runtime.toPublicEnv({
|
|
686
|
+
...framework ? { framework } : {},
|
|
687
|
+
...prefix ? { prefix } : {}
|
|
688
|
+
}) : runtime.toEnv();
|
|
689
|
+
}
|
|
690
|
+
function formatEnvOutput(env) {
|
|
691
|
+
return Object.entries(env).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
692
|
+
}
|
|
693
|
+
async function resolveMaterializedEnv(options = {}) {
|
|
694
|
+
const runtime = await createRuntimeService({
|
|
695
|
+
...options,
|
|
696
|
+
cliArgs: [...options.cliArgs ?? []]
|
|
697
|
+
});
|
|
698
|
+
const env = resolveEnvFromRuntime(runtime, options.cliArgs ?? []);
|
|
699
|
+
return {
|
|
700
|
+
runtime,
|
|
701
|
+
env,
|
|
702
|
+
output: formatEnvOutput(env)
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
function resolveMaterializedEnvTarget(to, root = process.cwd()) {
|
|
706
|
+
return path5.resolve(root, to);
|
|
707
|
+
}
|
|
708
|
+
async function writeMaterializedEnvFile(to, output, root = process.cwd()) {
|
|
709
|
+
const targetPath = resolveMaterializedEnvTarget(to, root);
|
|
710
|
+
await mkdir3(path5.dirname(targetPath), { recursive: true });
|
|
711
|
+
await writeFile3(targetPath, output, "utf8");
|
|
712
|
+
return targetPath;
|
|
713
|
+
}
|
|
714
|
+
async function materializeEnvToFile(to, options = {}) {
|
|
715
|
+
const result = await resolveMaterializedEnv(options);
|
|
716
|
+
const targetPath = await writeMaterializedEnvFile(to, result.output, options.root ?? process.cwd());
|
|
717
|
+
return {
|
|
718
|
+
...result,
|
|
719
|
+
targetPath
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
601
723
|
// src/services/spawn.ts
|
|
602
724
|
import { spawn } from "child_process";
|
|
603
725
|
function shouldUseShellForCommand(command) {
|
|
@@ -835,7 +957,7 @@ async function runDiff(leftProfile, rightProfile, options = {}) {
|
|
|
835
957
|
|
|
836
958
|
// src/services/doctor.ts
|
|
837
959
|
import { readdir, readFile as readFile2 } from "fs/promises";
|
|
838
|
-
import
|
|
960
|
+
import path6 from "path";
|
|
839
961
|
import {
|
|
840
962
|
detectLegacyVaultFormat,
|
|
841
963
|
isSecretReference as isSecretReference2,
|
|
@@ -857,7 +979,7 @@ async function createValidationSummary(options = {}) {
|
|
|
857
979
|
|
|
858
980
|
// src/services/doctor.ts
|
|
859
981
|
async function checkGitignore(root) {
|
|
860
|
-
const gitignorePath =
|
|
982
|
+
const gitignorePath = path6.join(root, ".gitignore");
|
|
861
983
|
const expected = [
|
|
862
984
|
".cnos/env/.env",
|
|
863
985
|
".cnos/env/.env.*",
|
|
@@ -892,12 +1014,12 @@ async function collectYamlFiles(root) {
|
|
|
892
1014
|
const entries = await readdir(root, { withFileTypes: true });
|
|
893
1015
|
const results = [];
|
|
894
1016
|
for (const entry of entries) {
|
|
895
|
-
const target =
|
|
1017
|
+
const target = path6.join(root, entry.name);
|
|
896
1018
|
if (entry.isDirectory()) {
|
|
897
1019
|
results.push(...await collectYamlFiles(target));
|
|
898
1020
|
continue;
|
|
899
1021
|
}
|
|
900
|
-
if (entry.isFile() && [".yml", ".yaml"].includes(
|
|
1022
|
+
if (entry.isFile() && [".yml", ".yaml"].includes(path6.extname(entry.name).toLowerCase())) {
|
|
901
1023
|
results.push(target);
|
|
902
1024
|
}
|
|
903
1025
|
}
|
|
@@ -922,7 +1044,7 @@ async function checkSecretSecurity(options, runtime) {
|
|
|
922
1044
|
);
|
|
923
1045
|
const legacyDetected = legacyPaths.filter((entry) => Boolean(entry.path));
|
|
924
1046
|
const secretFiles = await Promise.all(
|
|
925
|
-
runtime.graph.workspace.workspaceRoots.filter((root) => root.scope === "local").map((root) => collectYamlFiles(
|
|
1047
|
+
runtime.graph.workspace.workspaceRoots.filter((root) => root.scope === "local").map((root) => collectYamlFiles(path6.join(root.path, "secrets")))
|
|
926
1048
|
);
|
|
927
1049
|
const plaintextFiles = [];
|
|
928
1050
|
for (const file of secretFiles.flat()) {
|
|
@@ -952,7 +1074,7 @@ async function checkSecretSecurity(options, runtime) {
|
|
|
952
1074
|
};
|
|
953
1075
|
}
|
|
954
1076
|
async function evaluateDoctor(options = {}) {
|
|
955
|
-
const root =
|
|
1077
|
+
const root = path6.resolve(options.root ?? process.cwd());
|
|
956
1078
|
const { runtime, summary } = await createValidationSummary(options);
|
|
957
1079
|
const localRoot = runtime.graph.workspace.workspaceRoots.find((entry) => entry.scope === "local");
|
|
958
1080
|
const globalRoot = runtime.graph.workspace.workspaceRoots.find((entry) => entry.scope === "global");
|
|
@@ -1145,7 +1267,7 @@ var COMMANDS = [
|
|
|
1145
1267
|
id: "init",
|
|
1146
1268
|
summary: "Scaffold a workspace-aware CNOS tree in the current project.",
|
|
1147
1269
|
usage: "cnos init [--workspace <id>] [--root <path>] [--json]",
|
|
1148
|
-
description: "Creates .cnos/cnos.yml, optional .cnos-workspace.yml, config folders, and .gitignore entries without overwriting existing files.",
|
|
1270
|
+
description: "Creates .cnos/cnos.yml, .cnosrc.yml, optional .cnos-workspace.yml, config folders, and .gitignore entries without overwriting existing files.",
|
|
1149
1271
|
examples: ["cnos init", "cnos init --workspace api", "cnos init --root ./apps/api --workspace api --json"]
|
|
1150
1272
|
},
|
|
1151
1273
|
{
|
|
@@ -1563,23 +1685,39 @@ var COMMANDS = [
|
|
|
1563
1685
|
id: "build",
|
|
1564
1686
|
summary: "Build derived configuration artifacts from CNOS.",
|
|
1565
1687
|
usage: "cnos build <subcommand> [options] [global-options]",
|
|
1566
|
-
description: "Builds deterministic derived outputs from the selected workspace
|
|
1688
|
+
description: "Builds deterministic derived outputs from the selected workspace, including server projections, browser projections, env files, and framework-prefixed public env.",
|
|
1567
1689
|
arguments: [
|
|
1568
1690
|
{
|
|
1569
1691
|
name: "subcommand",
|
|
1570
|
-
description: "Supported
|
|
1692
|
+
description: "Supported values: server, browser, env, public.",
|
|
1571
1693
|
required: true
|
|
1572
1694
|
}
|
|
1573
1695
|
],
|
|
1574
1696
|
examples: [
|
|
1697
|
+
"cnos build server --to .cnos-server.json",
|
|
1698
|
+
"cnos build browser --to .cnos-browser.json",
|
|
1575
1699
|
"cnos build env --profile local --to .env.local",
|
|
1576
|
-
"cnos build
|
|
1700
|
+
"cnos build public --framework vite --profile prod --to .env.production"
|
|
1577
1701
|
]
|
|
1578
1702
|
},
|
|
1703
|
+
{
|
|
1704
|
+
id: "build server",
|
|
1705
|
+
summary: "Build a server runtime projection artifact.",
|
|
1706
|
+
usage: "cnos build server --to <path> [--format <json|yaml>] [global-options]",
|
|
1707
|
+
description: "Builds a flat server projection for runtime auto-loading. Non-secret values are embedded, while secret refs remain refs and hydrate at runtime.",
|
|
1708
|
+
examples: ["cnos build server --to .cnos-server.json", "cnos build server --profile prod --to dist/.cnos-server.json"]
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
id: "build browser",
|
|
1712
|
+
summary: "Build a browser projection artifact.",
|
|
1713
|
+
usage: "cnos build browser --to <path> [--format <json|yaml>] [global-options]",
|
|
1714
|
+
description: "Builds a public-only browser projection for tooling and offline packaging flows. secret.* keys are excluded entirely.",
|
|
1715
|
+
examples: ["cnos build browser --to .cnos-browser.json"]
|
|
1716
|
+
},
|
|
1579
1717
|
{
|
|
1580
1718
|
id: "build env",
|
|
1581
1719
|
summary: "Build a flat env-file artifact from CNOS.",
|
|
1582
|
-
usage: "cnos build env --to <path> [--
|
|
1720
|
+
usage: "cnos build env --to <path> [--format <dotenv|docker-env|json|shell|toml|yaml>] [global-options]",
|
|
1583
1721
|
description: "Builds a deterministic KEY=VALUE artifact for legacy build and runtime workflows. The target file is derived output, not the CNOS source of truth.",
|
|
1584
1722
|
options: [
|
|
1585
1723
|
{
|
|
@@ -1587,24 +1725,23 @@ var COMMANDS = [
|
|
|
1587
1725
|
description: "Write the rendered KEY=VALUE output to a file. Required."
|
|
1588
1726
|
},
|
|
1589
1727
|
{
|
|
1590
|
-
flag: "--
|
|
1591
|
-
description: "
|
|
1592
|
-
},
|
|
1593
|
-
{
|
|
1594
|
-
flag: "--framework <name>",
|
|
1595
|
-
description: "Apply framework-specific public env conventions such as vite or next."
|
|
1596
|
-
},
|
|
1597
|
-
{
|
|
1598
|
-
flag: "--prefix <prefix>",
|
|
1599
|
-
description: "Override the generated public env prefix."
|
|
1728
|
+
flag: "--format <dotenv|docker-env|json|shell|toml|yaml>",
|
|
1729
|
+
description: "Select the output format. Defaults to dotenv."
|
|
1600
1730
|
}
|
|
1601
1731
|
],
|
|
1602
1732
|
examples: [
|
|
1603
1733
|
"cnos build env --profile local --to .env.local",
|
|
1604
1734
|
"cnos build env --profile stage --to .env.stage",
|
|
1605
|
-
"cnos build env --
|
|
1735
|
+
"cnos build env --profile prod --format yaml --to env.yaml"
|
|
1606
1736
|
]
|
|
1607
1737
|
},
|
|
1738
|
+
{
|
|
1739
|
+
id: "build public",
|
|
1740
|
+
summary: "Build a public env artifact with optional framework prefixing.",
|
|
1741
|
+
usage: "cnos build public --to <path> [--framework <name>] [--format <dotenv|docker-env|json|shell|toml|yaml>] [global-options]",
|
|
1742
|
+
description: "Builds env-style public artifacts from promoted keys only, with framework-specific prefixes such as vite or next when requested.",
|
|
1743
|
+
examples: ["cnos build public --framework vite --to .env.vite", "cnos build public --framework next --format json --to public.json"]
|
|
1744
|
+
},
|
|
1608
1745
|
{
|
|
1609
1746
|
id: "export env",
|
|
1610
1747
|
summary: "Render environment variables for the selected workspace.",
|
|
@@ -1735,6 +1872,27 @@ var COMMANDS = [
|
|
|
1735
1872
|
"cnos run --public --framework vite -- pnpm build"
|
|
1736
1873
|
]
|
|
1737
1874
|
},
|
|
1875
|
+
{
|
|
1876
|
+
id: "workspace",
|
|
1877
|
+
summary: "Attach or detach package-local workspace config from a parent CNOS root.",
|
|
1878
|
+
usage: "cnos workspace <attach|detach> [options] [global-options]",
|
|
1879
|
+
description: "Detaches a child package into a standalone .cnos root or reattaches a detached package back into a parent workspace.",
|
|
1880
|
+
examples: ["cnos workspace detach", "cnos workspace attach --package-root apps/travel"]
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
id: "workspace detach",
|
|
1884
|
+
summary: "Detach a package workspace into a standalone .cnos root.",
|
|
1885
|
+
usage: "cnos workspace detach [--package-root <path>] [--force] [global-options]",
|
|
1886
|
+
description: "Materializes the effective local workspace chain into a package-local .cnos directory, rewrites .cnosrc.yml to root: ./.cnos, and records the original parent binding in .cnos/.detached.",
|
|
1887
|
+
examples: ["cnos workspace detach", "cnos workspace detach --package-root apps/travel --force"]
|
|
1888
|
+
},
|
|
1889
|
+
{
|
|
1890
|
+
id: "workspace attach",
|
|
1891
|
+
summary: "Attach a detached package back to its original parent CNOS root.",
|
|
1892
|
+
usage: "cnos workspace attach [--package-root <path>] [--force] [global-options]",
|
|
1893
|
+
description: "Imports a detached package-local .cnos directory back into the original parent workspace, archives the detached snapshot, and restores .cnosrc.yml to the parent root/workspace binding.",
|
|
1894
|
+
examples: ["cnos workspace attach", "cnos workspace attach --package-root apps/travel --force"]
|
|
1895
|
+
},
|
|
1738
1896
|
{
|
|
1739
1897
|
id: "diff",
|
|
1740
1898
|
summary: "Diff two profiles for the same workspace.",
|
|
@@ -2007,11 +2165,11 @@ function runHelpAi(topic, cliArgs = []) {
|
|
|
2007
2165
|
}
|
|
2008
2166
|
|
|
2009
2167
|
// src/commands/init.ts
|
|
2010
|
-
import
|
|
2168
|
+
import path8 from "path";
|
|
2011
2169
|
|
|
2012
2170
|
// src/services/scaffold.ts
|
|
2013
|
-
import { mkdir as
|
|
2014
|
-
import
|
|
2171
|
+
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
2172
|
+
import path7 from "path";
|
|
2015
2173
|
function scaffoldManifest(projectName, workspace) {
|
|
2016
2174
|
const lines = [
|
|
2017
2175
|
"version: 1",
|
|
@@ -2045,12 +2203,12 @@ async function ensureFile(filePath, content) {
|
|
|
2045
2203
|
await readFile3(filePath, "utf8");
|
|
2046
2204
|
return false;
|
|
2047
2205
|
} catch {
|
|
2048
|
-
await
|
|
2206
|
+
await writeFile4(filePath, content, "utf8");
|
|
2049
2207
|
return true;
|
|
2050
2208
|
}
|
|
2051
2209
|
}
|
|
2052
2210
|
async function ensureGitignore(root) {
|
|
2053
|
-
const gitignorePath =
|
|
2211
|
+
const gitignorePath = path7.join(root, ".gitignore");
|
|
2054
2212
|
const requiredEntries = [
|
|
2055
2213
|
".cnos/env/.env",
|
|
2056
2214
|
".cnos/env/.env.*",
|
|
@@ -2073,18 +2231,18 @@ async function ensureGitignore(root) {
|
|
|
2073
2231
|
}
|
|
2074
2232
|
const prefix = current.trim().length > 0 ? `${current.trimEnd()}
|
|
2075
2233
|
` : "";
|
|
2076
|
-
await
|
|
2234
|
+
await writeFile4(gitignorePath, `${prefix}${missingEntries.join("\n")}
|
|
2077
2235
|
`, "utf8");
|
|
2078
2236
|
return true;
|
|
2079
2237
|
}
|
|
2080
2238
|
async function scaffoldWorkspace(root, workspace) {
|
|
2081
|
-
const cnosRoot =
|
|
2082
|
-
const workspaceRoot = workspace ?
|
|
2239
|
+
const cnosRoot = path7.join(root, ".cnos");
|
|
2240
|
+
const workspaceRoot = workspace ? path7.join(cnosRoot, "workspaces", workspace) : cnosRoot;
|
|
2083
2241
|
const createdPaths = [];
|
|
2084
|
-
await
|
|
2085
|
-
await
|
|
2086
|
-
await
|
|
2087
|
-
await
|
|
2242
|
+
await mkdir4(path7.join(workspaceRoot, "profiles"), { recursive: true });
|
|
2243
|
+
await mkdir4(path7.join(workspaceRoot, "values"), { recursive: true });
|
|
2244
|
+
await mkdir4(path7.join(workspaceRoot, "secrets"), { recursive: true });
|
|
2245
|
+
await mkdir4(path7.join(workspaceRoot, "env"), { recursive: true });
|
|
2088
2246
|
const relativePaths = workspace ? [
|
|
2089
2247
|
["workspaces", workspace, "profiles", ".gitkeep"],
|
|
2090
2248
|
["workspaces", workspace, "values", ".gitkeep"],
|
|
@@ -2097,15 +2255,23 @@ async function scaffoldWorkspace(root, workspace) {
|
|
|
2097
2255
|
["env", ".gitkeep"]
|
|
2098
2256
|
];
|
|
2099
2257
|
for (const relativePath of relativePaths) {
|
|
2100
|
-
const filePath =
|
|
2258
|
+
const filePath = path7.join(cnosRoot, ...relativePath);
|
|
2101
2259
|
if (await ensureFile(filePath, "")) {
|
|
2102
|
-
createdPaths.push(
|
|
2260
|
+
createdPaths.push(path7.relative(root, filePath).replace(/\\/g, "/"));
|
|
2103
2261
|
}
|
|
2104
2262
|
}
|
|
2105
|
-
if (await ensureFile(
|
|
2263
|
+
if (await ensureFile(path7.join(cnosRoot, "cnos.yml"), scaffoldManifest(path7.basename(root), workspace))) {
|
|
2106
2264
|
createdPaths.push(".cnos/cnos.yml");
|
|
2107
2265
|
}
|
|
2108
|
-
if (
|
|
2266
|
+
if (await ensureFile(
|
|
2267
|
+
path7.join(root, ".cnosrc.yml"),
|
|
2268
|
+
workspace ? `root: ./.cnos
|
|
2269
|
+
workspace: ${workspace}
|
|
2270
|
+
` : "root: ./.cnos\n"
|
|
2271
|
+
)) {
|
|
2272
|
+
createdPaths.push(".cnosrc.yml");
|
|
2273
|
+
}
|
|
2274
|
+
if (workspace && await ensureFile(path7.join(root, ".cnos-workspace.yml"), `workspace: ${workspace}
|
|
2109
2275
|
globalRoot: ~/.cnos
|
|
2110
2276
|
`)) {
|
|
2111
2277
|
createdPaths.push(".cnos-workspace.yml");
|
|
@@ -2122,7 +2288,7 @@ globalRoot: ~/.cnos
|
|
|
2122
2288
|
|
|
2123
2289
|
// src/commands/init.ts
|
|
2124
2290
|
async function runInit(options = {}) {
|
|
2125
|
-
const root =
|
|
2291
|
+
const root = path8.resolve(options.root ?? process.cwd());
|
|
2126
2292
|
const result = await scaffoldWorkspace(root, options.workspace);
|
|
2127
2293
|
if (options.json) {
|
|
2128
2294
|
return printJson(result);
|
|
@@ -2317,7 +2483,7 @@ async function runList(args = [], options = {}) {
|
|
|
2317
2483
|
}
|
|
2318
2484
|
|
|
2319
2485
|
// src/commands/migrate.ts
|
|
2320
|
-
import
|
|
2486
|
+
import path9 from "path";
|
|
2321
2487
|
import {
|
|
2322
2488
|
applyManifestMappings,
|
|
2323
2489
|
loadManifest,
|
|
@@ -2335,7 +2501,7 @@ async function runMigrate(options = {}) {
|
|
|
2335
2501
|
throw new Error(`Unknown migrate options: ${cliArgs.join(" ")}`);
|
|
2336
2502
|
}
|
|
2337
2503
|
const manifest = await loadManifest(options.root ? { root: options.root } : {});
|
|
2338
|
-
const scanRoot =
|
|
2504
|
+
const scanRoot = path9.resolve(manifest.repoRoot, scan ?? "src");
|
|
2339
2505
|
const usages = await scanEnvUsage(scanRoot);
|
|
2340
2506
|
const uniqueProposals = new Map(usages.map((usage) => [usage.envVar, proposeMapping(usage.envVar)]));
|
|
2341
2507
|
const proposals = Array.from(uniqueProposals.values()).sort((left, right) => left.envVar.localeCompare(right.envVar));
|
|
@@ -2397,7 +2563,7 @@ async function runMigrate(options = {}) {
|
|
|
2397
2563
|
}
|
|
2398
2564
|
|
|
2399
2565
|
// src/commands/namespace.ts
|
|
2400
|
-
import
|
|
2566
|
+
import path10 from "path";
|
|
2401
2567
|
function normalizeCommand2(args) {
|
|
2402
2568
|
const [actionOrPath, ...tail] = args;
|
|
2403
2569
|
if (!actionOrPath) {
|
|
@@ -2420,7 +2586,7 @@ function normalizeCommand2(args) {
|
|
|
2420
2586
|
async function runNamespace(namespace, args = [], options = {}) {
|
|
2421
2587
|
const { action, tail } = normalizeCommand2(args);
|
|
2422
2588
|
const cliArgs = [...options.cliArgs ?? []];
|
|
2423
|
-
const root =
|
|
2589
|
+
const root = path10.resolve(options.root ?? process.cwd());
|
|
2424
2590
|
if (action === "list") {
|
|
2425
2591
|
const prefix = consumeOption(cliArgs, "--prefix");
|
|
2426
2592
|
const entries = await listConfigEntries(namespace, {
|
|
@@ -2483,31 +2649,31 @@ async function runNamespace(namespace, args = [], options = {}) {
|
|
|
2483
2649
|
|
|
2484
2650
|
// src/commands/onboard.ts
|
|
2485
2651
|
import { copyFile, readdir as readdir2, rm } from "fs/promises";
|
|
2486
|
-
import
|
|
2652
|
+
import path11 from "path";
|
|
2487
2653
|
var ROOT_ENV_FILE_PATTERN = /^\.env(?:\.[A-Za-z0-9_-]+)*(?:\.example)?$/;
|
|
2488
2654
|
async function listRootEnvFiles(root) {
|
|
2489
2655
|
const entries = await readdir2(root, { withFileTypes: true });
|
|
2490
2656
|
return entries.filter((entry) => entry.isFile() && ROOT_ENV_FILE_PATTERN.test(entry.name)).map((entry) => entry.name).sort((left, right) => left.localeCompare(right));
|
|
2491
2657
|
}
|
|
2492
2658
|
async function runOnboard(options = {}) {
|
|
2493
|
-
const root =
|
|
2494
|
-
const workspace = options.workspace ??
|
|
2659
|
+
const root = path11.resolve(options.root ?? process.cwd());
|
|
2660
|
+
const workspace = options.workspace ?? path11.basename(root);
|
|
2495
2661
|
const cliArgs = [...options.cliArgs ?? []];
|
|
2496
2662
|
const move = consumeFlag(cliArgs, "--move");
|
|
2497
2663
|
if (cliArgs.length > 0) {
|
|
2498
2664
|
throw new Error(`Unsupported onboard arguments: ${cliArgs.join(" ")}`);
|
|
2499
2665
|
}
|
|
2500
2666
|
const scaffold = await scaffoldWorkspace(root, workspace);
|
|
2501
|
-
const envRoot =
|
|
2667
|
+
const envRoot = path11.join(root, ".cnos", "workspaces", workspace, "env");
|
|
2502
2668
|
const rootFiles = await listRootEnvFiles(root);
|
|
2503
2669
|
const imported = [];
|
|
2504
2670
|
const skipped = [];
|
|
2505
2671
|
for (const fileName of rootFiles) {
|
|
2506
|
-
const sourcePath =
|
|
2507
|
-
const targetPath =
|
|
2672
|
+
const sourcePath = path11.join(root, fileName);
|
|
2673
|
+
const targetPath = path11.join(envRoot, fileName);
|
|
2508
2674
|
try {
|
|
2509
2675
|
await copyFile(sourcePath, targetPath);
|
|
2510
|
-
imported.push(
|
|
2676
|
+
imported.push(path11.relative(root, targetPath).replace(/\\/g, "/"));
|
|
2511
2677
|
if (move) {
|
|
2512
2678
|
await rm(sourcePath);
|
|
2513
2679
|
}
|
|
@@ -2532,14 +2698,14 @@ async function runOnboard(options = {}) {
|
|
|
2532
2698
|
}
|
|
2533
2699
|
|
|
2534
2700
|
// src/commands/profile.ts
|
|
2535
|
-
import
|
|
2701
|
+
import path14 from "path";
|
|
2536
2702
|
|
|
2537
2703
|
// src/services/context.ts
|
|
2538
|
-
import { readFile as readFile4, writeFile as
|
|
2539
|
-
import
|
|
2540
|
-
import { parseYaml as parseYaml3, stringifyYaml as
|
|
2704
|
+
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
2705
|
+
import path12 from "path";
|
|
2706
|
+
import { parseYaml as parseYaml3, stringifyYaml as stringifyYaml3 } from "@kitsy/cnos/internal";
|
|
2541
2707
|
async function loadCliContext(root = process.cwd()) {
|
|
2542
|
-
const filePath =
|
|
2708
|
+
const filePath = path12.join(path12.resolve(root), ".cnos-workspace.yml");
|
|
2543
2709
|
try {
|
|
2544
2710
|
const source = await readFile4(filePath, "utf8");
|
|
2545
2711
|
const parsed = parseYaml3(source);
|
|
@@ -2552,8 +2718,8 @@ async function loadCliContext(root = process.cwd()) {
|
|
|
2552
2718
|
}
|
|
2553
2719
|
}
|
|
2554
2720
|
async function saveCliContext(options = {}) {
|
|
2555
|
-
const root =
|
|
2556
|
-
const filePath =
|
|
2721
|
+
const root = path12.resolve(options.root ?? process.cwd());
|
|
2722
|
+
const filePath = path12.join(root, ".cnos-workspace.yml");
|
|
2557
2723
|
const current = await loadCliContext(root);
|
|
2558
2724
|
const next = {
|
|
2559
2725
|
...current.workspace ? { workspace: current.workspace } : {},
|
|
@@ -2563,7 +2729,7 @@ async function saveCliContext(options = {}) {
|
|
|
2563
2729
|
...options.profile ? { profile: options.profile } : {},
|
|
2564
2730
|
...options.globalRoot ? { globalRoot: options.globalRoot } : {}
|
|
2565
2731
|
};
|
|
2566
|
-
await
|
|
2732
|
+
await writeFile5(filePath, stringifyYaml3(next), "utf8");
|
|
2567
2733
|
return {
|
|
2568
2734
|
filePath,
|
|
2569
2735
|
context: next
|
|
@@ -2571,12 +2737,12 @@ async function saveCliContext(options = {}) {
|
|
|
2571
2737
|
}
|
|
2572
2738
|
|
|
2573
2739
|
// src/services/profiles.ts
|
|
2574
|
-
import { mkdir as
|
|
2575
|
-
import
|
|
2576
|
-
import { parseYaml as parseYaml4, stringifyYaml as
|
|
2740
|
+
import { mkdir as mkdir5, readdir as readdir3, readFile as readFile5, rm as rm2, writeFile as writeFile6 } from "fs/promises";
|
|
2741
|
+
import path13 from "path";
|
|
2742
|
+
import { parseYaml as parseYaml4, stringifyYaml as stringifyYaml4 } from "@kitsy/cnos/internal";
|
|
2577
2743
|
async function createProfileDefinition(root = process.cwd(), profile, inherit, options = {}) {
|
|
2578
|
-
const filePath =
|
|
2579
|
-
await
|
|
2744
|
+
const filePath = path13.join(path13.resolve(root), ".cnos", "profiles", `${profile}.yml`);
|
|
2745
|
+
await mkdir5(path13.dirname(filePath), { recursive: true });
|
|
2580
2746
|
const document = options.noInherit ? {
|
|
2581
2747
|
name: profile,
|
|
2582
2748
|
activate: {
|
|
@@ -2590,7 +2756,7 @@ async function createProfileDefinition(root = process.cwd(), profile, inherit, o
|
|
|
2590
2756
|
} : {
|
|
2591
2757
|
name: profile
|
|
2592
2758
|
};
|
|
2593
|
-
await
|
|
2759
|
+
await writeFile6(filePath, stringifyYaml4(document), "utf8");
|
|
2594
2760
|
return {
|
|
2595
2761
|
filePath,
|
|
2596
2762
|
profile,
|
|
@@ -2599,7 +2765,7 @@ async function createProfileDefinition(root = process.cwd(), profile, inherit, o
|
|
|
2599
2765
|
};
|
|
2600
2766
|
}
|
|
2601
2767
|
async function listProfiles(root = process.cwd()) {
|
|
2602
|
-
const profilesRoot =
|
|
2768
|
+
const profilesRoot = path13.join(path13.resolve(root), ".cnos", "profiles");
|
|
2603
2769
|
try {
|
|
2604
2770
|
const entries = await readdir3(profilesRoot, { withFileTypes: true });
|
|
2605
2771
|
const discovered = /* @__PURE__ */ new Set(["base"]);
|
|
@@ -2614,7 +2780,7 @@ async function listProfiles(root = process.cwd()) {
|
|
|
2614
2780
|
}
|
|
2615
2781
|
}
|
|
2616
2782
|
async function deleteProfileDefinition(root = process.cwd(), profile) {
|
|
2617
|
-
const filePath =
|
|
2783
|
+
const filePath = path13.join(path13.resolve(root), ".cnos", "profiles", `${profile}.yml`);
|
|
2618
2784
|
try {
|
|
2619
2785
|
await rm2(filePath);
|
|
2620
2786
|
return {
|
|
@@ -2634,7 +2800,7 @@ async function readProfileDefinition(root = process.cwd(), profile = "base") {
|
|
|
2634
2800
|
name: "base"
|
|
2635
2801
|
};
|
|
2636
2802
|
}
|
|
2637
|
-
const filePath =
|
|
2803
|
+
const filePath = path13.join(path13.resolve(root), ".cnos", "profiles", `${profile}.yml`);
|
|
2638
2804
|
try {
|
|
2639
2805
|
return parseYaml4(await readFile5(filePath, "utf8")) ?? void 0;
|
|
2640
2806
|
} catch {
|
|
@@ -2658,7 +2824,7 @@ function normalizeProfileAction(args) {
|
|
|
2658
2824
|
}
|
|
2659
2825
|
async function runProfile(args, options = {}) {
|
|
2660
2826
|
const { action, tail } = normalizeProfileAction(args);
|
|
2661
|
-
const root =
|
|
2827
|
+
const root = path14.resolve(options.root ?? process.cwd());
|
|
2662
2828
|
const cliArgs = [...options.cliArgs ?? []];
|
|
2663
2829
|
if (action === "create") {
|
|
2664
2830
|
const profile = tail[0] ?? "stage";
|
|
@@ -2709,12 +2875,12 @@ async function runProfile(args, options = {}) {
|
|
|
2709
2875
|
}
|
|
2710
2876
|
|
|
2711
2877
|
// src/commands/promote.ts
|
|
2712
|
-
import
|
|
2713
|
-
import { writeFile as
|
|
2878
|
+
import path15 from "path";
|
|
2879
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
2714
2880
|
import {
|
|
2715
2881
|
ensureProjectionAllowed,
|
|
2716
2882
|
loadManifest as loadManifest2,
|
|
2717
|
-
stringifyYaml as
|
|
2883
|
+
stringifyYaml as stringifyYaml5
|
|
2718
2884
|
} from "@kitsy/cnos/internal";
|
|
2719
2885
|
function normalizeTarget(value) {
|
|
2720
2886
|
if (value === "public" || value === "env") {
|
|
@@ -2726,7 +2892,7 @@ function sortRecord(record) {
|
|
|
2726
2892
|
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
|
|
2727
2893
|
}
|
|
2728
2894
|
async function runPromote(args = [], options = {}) {
|
|
2729
|
-
const root =
|
|
2895
|
+
const root = path15.resolve(options.root ?? process.cwd());
|
|
2730
2896
|
const cliArgs = [...options.cliArgs ?? []];
|
|
2731
2897
|
const target = normalizeTarget(consumeOption(cliArgs, "--to"));
|
|
2732
2898
|
const alias = consumeOption(cliArgs, "--as");
|
|
@@ -2765,7 +2931,7 @@ async function runPromote(args = [], options = {}) {
|
|
|
2765
2931
|
})
|
|
2766
2932
|
};
|
|
2767
2933
|
}
|
|
2768
|
-
await
|
|
2934
|
+
await writeFile7(loadedManifest.manifestPath, stringifyYaml5(rawManifest), "utf8");
|
|
2769
2935
|
if (options.json) {
|
|
2770
2936
|
return printJson({
|
|
2771
2937
|
target,
|
|
@@ -2797,9 +2963,8 @@ async function runRead(key, options = {}) {
|
|
|
2797
2963
|
// src/commands/run.ts
|
|
2798
2964
|
import {
|
|
2799
2965
|
CNOS_GRAPH_ENV_VAR,
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
serializeSecretPayload,
|
|
2966
|
+
CNOS_PROJECTION_ENV_VAR,
|
|
2967
|
+
serializeServerProjection,
|
|
2803
2968
|
serializeRuntimeGraph
|
|
2804
2969
|
} from "@kitsy/cnos/internal";
|
|
2805
2970
|
function consumeOptions(args, flag) {
|
|
@@ -2838,7 +3003,7 @@ async function runCommand(command, options = {}) {
|
|
|
2838
3003
|
}
|
|
2839
3004
|
const cliArgs = [...options.cliArgs ?? []];
|
|
2840
3005
|
const isPublic = consumeFlag(cliArgs, "--public");
|
|
2841
|
-
|
|
3006
|
+
consumeFlag(cliArgs, "--auth");
|
|
2842
3007
|
const framework = consumeOption(cliArgs, "--framework");
|
|
2843
3008
|
const prefix = consumeOption(cliArgs, "--prefix");
|
|
2844
3009
|
const setOverrides = normalizeSetOverrides(consumeOptions(cliArgs, "--set"));
|
|
@@ -2846,21 +3011,14 @@ async function runCommand(command, options = {}) {
|
|
|
2846
3011
|
...options,
|
|
2847
3012
|
cliArgs: [...cliArgs, ...setOverrides]
|
|
2848
3013
|
});
|
|
2849
|
-
const authenticatedSecrets = isAuthenticated ? Object.fromEntries(
|
|
2850
|
-
Array.from(runtime.graph.entries.values()).filter((entry) => entry.namespace === "secret").map((entry) => [entry.key, runtime.read(entry.key)])
|
|
2851
|
-
) : void 0;
|
|
2852
|
-
const secretPayload = authenticatedSecrets ? serializeSecretPayload(authenticatedSecrets) : void 0;
|
|
2853
3014
|
const env = {
|
|
2854
3015
|
...process.env,
|
|
2855
3016
|
...isPublic ? runtime.toPublicEnv({
|
|
2856
3017
|
...framework ? { framework } : {},
|
|
2857
3018
|
...prefix ? { prefix } : {}
|
|
2858
3019
|
}) : runtime.toEnv(),
|
|
2859
|
-
[
|
|
2860
|
-
|
|
2861
|
-
[CNOS_SECRET_PAYLOAD_ENV_VAR]: secretPayload.payload,
|
|
2862
|
-
[CNOS_SESSION_KEY_ENV_VAR]: secretPayload.sessionKey
|
|
2863
|
-
} : {}
|
|
3020
|
+
[CNOS_PROJECTION_ENV_VAR]: serializeServerProjection(runtime.toServerProjection()),
|
|
3021
|
+
[CNOS_GRAPH_ENV_VAR]: serializeRuntimeGraph(runtime.graph)
|
|
2864
3022
|
};
|
|
2865
3023
|
return new Promise((resolve, reject) => {
|
|
2866
3024
|
const executable = command[0];
|
|
@@ -2895,14 +3053,14 @@ async function runCommand(command, options = {}) {
|
|
|
2895
3053
|
}
|
|
2896
3054
|
|
|
2897
3055
|
// src/commands/secret.ts
|
|
2898
|
-
import
|
|
3056
|
+
import path18 from "path";
|
|
2899
3057
|
|
|
2900
3058
|
// src/commands/vault.ts
|
|
2901
|
-
import
|
|
3059
|
+
import path17 from "path";
|
|
2902
3060
|
|
|
2903
3061
|
// src/services/vaults.ts
|
|
2904
|
-
import { rm as rm3, writeFile as
|
|
2905
|
-
import
|
|
3062
|
+
import { rm as rm3, writeFile as writeFile8 } from "fs/promises";
|
|
3063
|
+
import path16 from "path";
|
|
2906
3064
|
import {
|
|
2907
3065
|
clearAllVaultSessionKeys,
|
|
2908
3066
|
clearVaultSessionKey,
|
|
@@ -2915,7 +3073,7 @@ import {
|
|
|
2915
3073
|
resolveSecretStoreRoot as resolveSecretStoreRoot2,
|
|
2916
3074
|
resolveVaultAuth as resolveVaultAuth2,
|
|
2917
3075
|
resolveVaultDefinition,
|
|
2918
|
-
stringifyYaml as
|
|
3076
|
+
stringifyYaml as stringifyYaml6,
|
|
2919
3077
|
writeKeychain,
|
|
2920
3078
|
writeVaultSessionKey
|
|
2921
3079
|
} from "@kitsy/cnos/internal";
|
|
@@ -2956,7 +3114,7 @@ async function createVaultDefinition(name, options = {}) {
|
|
|
2956
3114
|
[vault]: vaultDefinition
|
|
2957
3115
|
}
|
|
2958
3116
|
};
|
|
2959
|
-
await
|
|
3117
|
+
await writeFile8(loadedManifest.manifestPath, stringifyYaml6(rawManifest), "utf8");
|
|
2960
3118
|
const definition = resolveVaultDefinition({ [vault]: vaultDefinition }, vault);
|
|
2961
3119
|
if (provider === "local") {
|
|
2962
3120
|
const auth = await resolveVaultAuth2(vault, vaultDefinition, options.processEnv ?? process.env);
|
|
@@ -3007,8 +3165,8 @@ async function removeVaultDefinition(name, options = {}) {
|
|
|
3007
3165
|
if (Object.keys(nextVaults).length === 0) {
|
|
3008
3166
|
delete rawManifest.vaults;
|
|
3009
3167
|
}
|
|
3010
|
-
await
|
|
3011
|
-
const vaultRoot =
|
|
3168
|
+
await writeFile8(loadedManifest.manifestPath, stringifyYaml6(rawManifest), "utf8");
|
|
3169
|
+
const vaultRoot = path16.join(resolveSecretStoreRoot2(options.processEnv), "vaults", vault);
|
|
3012
3170
|
let removedStore;
|
|
3013
3171
|
try {
|
|
3014
3172
|
await rm3(vaultRoot, { recursive: true, force: true });
|
|
@@ -3102,7 +3260,7 @@ function normalizeVaultAction(args) {
|
|
|
3102
3260
|
async function runVault(args = [], options = {}) {
|
|
3103
3261
|
const { action, tail } = normalizeVaultAction(args);
|
|
3104
3262
|
const cliArgs = [...options.cliArgs ?? []];
|
|
3105
|
-
const root =
|
|
3263
|
+
const root = path17.resolve(options.root ?? process.cwd());
|
|
3106
3264
|
if (consumeOption(cliArgs, "--passphrase")) {
|
|
3107
3265
|
throw new Error("The --passphrase option is not supported in CNOS 1.4. Use env, keychain, or prompt-based auth.");
|
|
3108
3266
|
}
|
|
@@ -3208,7 +3366,7 @@ async function runSecret(argsOrPath, options = {}) {
|
|
|
3208
3366
|
const args = Array.isArray(argsOrPath) ? argsOrPath : [argsOrPath];
|
|
3209
3367
|
const { action, tail } = normalizeSecretCommand(args);
|
|
3210
3368
|
const cliArgs = [...options.cliArgs ?? []];
|
|
3211
|
-
const root =
|
|
3369
|
+
const root = path18.resolve(options.root ?? process.cwd());
|
|
3212
3370
|
if (consumeOption(cliArgs, "--passphrase")) {
|
|
3213
3371
|
throw new Error("The --passphrase option is not supported in CNOS 1.4. Use env, keychain, or prompt-based auth.");
|
|
3214
3372
|
}
|
|
@@ -3304,9 +3462,9 @@ async function runSecret(argsOrPath, options = {}) {
|
|
|
3304
3462
|
}
|
|
3305
3463
|
|
|
3306
3464
|
// src/commands/use.ts
|
|
3307
|
-
import
|
|
3465
|
+
import path19 from "path";
|
|
3308
3466
|
async function runUse(args = [], options = {}) {
|
|
3309
|
-
const root =
|
|
3467
|
+
const root = path19.resolve(options.root ?? process.cwd());
|
|
3310
3468
|
const action = args[0];
|
|
3311
3469
|
const hasUpdates = Boolean(options.workspace || options.profile || options.globalRoot);
|
|
3312
3470
|
if (action === "show" || !action && !hasUpdates) {
|
|
@@ -3343,7 +3501,7 @@ async function runValidate(options = {}) {
|
|
|
3343
3501
|
// package.json
|
|
3344
3502
|
var package_default = {
|
|
3345
3503
|
name: "@kitsy/cnos-cli",
|
|
3346
|
-
version: "1.
|
|
3504
|
+
version: "1.6.0",
|
|
3347
3505
|
description: "CLI entry point and developer tooling for CNOS.",
|
|
3348
3506
|
type: "module",
|
|
3349
3507
|
main: "./dist/index.js",
|
|
@@ -3398,7 +3556,7 @@ function runVersion() {
|
|
|
3398
3556
|
}
|
|
3399
3557
|
|
|
3400
3558
|
// src/commands/value.ts
|
|
3401
|
-
import
|
|
3559
|
+
import path20 from "path";
|
|
3402
3560
|
function normalizeValueCommand(args) {
|
|
3403
3561
|
const [actionOrPath, ...tail] = args;
|
|
3404
3562
|
if (!actionOrPath) {
|
|
@@ -3422,7 +3580,7 @@ async function runValue(argsOrPath, options = {}) {
|
|
|
3422
3580
|
const args = Array.isArray(argsOrPath) ? argsOrPath : [argsOrPath];
|
|
3423
3581
|
const { action, tail } = normalizeValueCommand(args);
|
|
3424
3582
|
const cliArgs = [...options.cliArgs ?? []];
|
|
3425
|
-
const root =
|
|
3583
|
+
const root = path20.resolve(options.root ?? process.cwd());
|
|
3426
3584
|
if (action === "list") {
|
|
3427
3585
|
const prefix = consumeOption(cliArgs, "--prefix");
|
|
3428
3586
|
const entries = await listConfigEntries("value", {
|
|
@@ -3485,10 +3643,10 @@ async function runValue(argsOrPath, options = {}) {
|
|
|
3485
3643
|
// src/commands/watch.ts
|
|
3486
3644
|
import {
|
|
3487
3645
|
CNOS_GRAPH_ENV_VAR as CNOS_GRAPH_ENV_VAR2,
|
|
3488
|
-
CNOS_SECRET_PAYLOAD_ENV_VAR
|
|
3489
|
-
CNOS_SESSION_KEY_ENV_VAR
|
|
3646
|
+
CNOS_SECRET_PAYLOAD_ENV_VAR,
|
|
3647
|
+
CNOS_SESSION_KEY_ENV_VAR,
|
|
3490
3648
|
serializeRuntimeGraph as serializeRuntimeGraph2,
|
|
3491
|
-
serializeSecretPayload
|
|
3649
|
+
serializeSecretPayload
|
|
3492
3650
|
} from "@kitsy/cnos/internal";
|
|
3493
3651
|
async function buildRunEnvironment(options) {
|
|
3494
3652
|
const cliArgs = [...options.cliArgs ?? []];
|
|
@@ -3503,7 +3661,7 @@ async function buildRunEnvironment(options) {
|
|
|
3503
3661
|
const authenticatedSecrets = isAuthenticated ? Object.fromEntries(
|
|
3504
3662
|
Array.from(runtime.graph.entries.values()).filter((entry) => entry.namespace === "secret").map((entry) => [entry.key, runtime.read(entry.key)])
|
|
3505
3663
|
) : void 0;
|
|
3506
|
-
const secretPayload = authenticatedSecrets ?
|
|
3664
|
+
const secretPayload = authenticatedSecrets ? serializeSecretPayload(authenticatedSecrets) : void 0;
|
|
3507
3665
|
return {
|
|
3508
3666
|
runtime,
|
|
3509
3667
|
env: {
|
|
@@ -3514,8 +3672,8 @@ async function buildRunEnvironment(options) {
|
|
|
3514
3672
|
}) : runtime.toEnv(),
|
|
3515
3673
|
[CNOS_GRAPH_ENV_VAR2]: serializeRuntimeGraph2(runtime.graph),
|
|
3516
3674
|
...secretPayload ? {
|
|
3517
|
-
[
|
|
3518
|
-
[
|
|
3675
|
+
[CNOS_SECRET_PAYLOAD_ENV_VAR]: secretPayload.payload,
|
|
3676
|
+
[CNOS_SESSION_KEY_ENV_VAR]: secretPayload.sessionKey
|
|
3519
3677
|
} : {}
|
|
3520
3678
|
}
|
|
3521
3679
|
};
|
|
@@ -3605,6 +3763,163 @@ async function runWatch(command, options = {}) {
|
|
|
3605
3763
|
return isSignal ? "watching config changes in signal mode" : "watching config changes in restart mode";
|
|
3606
3764
|
}
|
|
3607
3765
|
|
|
3766
|
+
// src/commands/workspace.ts
|
|
3767
|
+
import { cp, mkdir as mkdir6, rename, rm as rm4, stat, writeFile as writeFile9, readFile as readFile6 } from "fs/promises";
|
|
3768
|
+
import path21 from "path";
|
|
3769
|
+
import { loadManifest as loadManifest4, parseYaml as parseYaml5, stringifyYaml as stringifyYaml7 } from "@kitsy/cnos/internal";
|
|
3770
|
+
async function exists(targetPath) {
|
|
3771
|
+
try {
|
|
3772
|
+
await stat(targetPath);
|
|
3773
|
+
return true;
|
|
3774
|
+
} catch {
|
|
3775
|
+
return false;
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
async function copyIfExists(source, target) {
|
|
3779
|
+
if (!await exists(source)) {
|
|
3780
|
+
return;
|
|
3781
|
+
}
|
|
3782
|
+
await mkdir6(path21.dirname(target), { recursive: true });
|
|
3783
|
+
await cp(source, target, { recursive: true, force: true });
|
|
3784
|
+
}
|
|
3785
|
+
async function mergeWorkspaceRootsIntoStandalone(targetCnosRoot, sourceRoots) {
|
|
3786
|
+
for (const sourceRoot of sourceRoots) {
|
|
3787
|
+
for (const folderName of ["values", "secrets", "env", "profiles"]) {
|
|
3788
|
+
await copyIfExists(
|
|
3789
|
+
path21.join(sourceRoot, folderName),
|
|
3790
|
+
path21.join(targetCnosRoot, folderName)
|
|
3791
|
+
);
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
async function writeCnosrc(packageRoot, config) {
|
|
3796
|
+
await writeFile9(
|
|
3797
|
+
path21.join(packageRoot, ".cnosrc.yml"),
|
|
3798
|
+
stringifyYaml7({
|
|
3799
|
+
root: config.root,
|
|
3800
|
+
...config.workspace ? { workspace: config.workspace } : {}
|
|
3801
|
+
}),
|
|
3802
|
+
"utf8"
|
|
3803
|
+
);
|
|
3804
|
+
}
|
|
3805
|
+
function createDetachedManifest(rawManifest) {
|
|
3806
|
+
const next = structuredClone(rawManifest);
|
|
3807
|
+
if ("workspaces" in next) {
|
|
3808
|
+
delete next.workspaces;
|
|
3809
|
+
}
|
|
3810
|
+
return next;
|
|
3811
|
+
}
|
|
3812
|
+
async function runDetach(packageRoot, options = {}) {
|
|
3813
|
+
const loaded = await loadManifest4({ cwd: packageRoot });
|
|
3814
|
+
if (!loaded.anchorPath || !loaded.anchoredWorkspace) {
|
|
3815
|
+
throw new Error("workspace detach requires a package-local .cnosrc.yml with a workspace binding");
|
|
3816
|
+
}
|
|
3817
|
+
const targetCnosRoot = path21.join(packageRoot, ".cnos");
|
|
3818
|
+
const force = consumeFlag([...options.cliArgs ?? []], "--force");
|
|
3819
|
+
if (await exists(targetCnosRoot) && !force) {
|
|
3820
|
+
throw new Error(`Refusing to detach because ${displayPath(targetCnosRoot, packageRoot)} already exists. Use --force to overwrite.`);
|
|
3821
|
+
}
|
|
3822
|
+
if (force) {
|
|
3823
|
+
await rm4(targetCnosRoot, { recursive: true, force: true });
|
|
3824
|
+
}
|
|
3825
|
+
const runtime = await createRuntimeService({
|
|
3826
|
+
...options,
|
|
3827
|
+
root: loaded.manifestRoot,
|
|
3828
|
+
workspace: loaded.anchoredWorkspace
|
|
3829
|
+
});
|
|
3830
|
+
const localRoots = runtime.graph.workspace.workspaceRoots.filter((root) => root.scope === "local").map((root) => root.path);
|
|
3831
|
+
await mkdir6(targetCnosRoot, { recursive: true });
|
|
3832
|
+
await mergeWorkspaceRootsIntoStandalone(targetCnosRoot, localRoots);
|
|
3833
|
+
await writeFile9(
|
|
3834
|
+
path21.join(targetCnosRoot, "cnos.yml"),
|
|
3835
|
+
stringifyYaml7(createDetachedManifest(loaded.rawManifest)),
|
|
3836
|
+
"utf8"
|
|
3837
|
+
);
|
|
3838
|
+
const relativeRoot = path21.relative(packageRoot, loaded.manifestRoot).replace(/\\/g, "/");
|
|
3839
|
+
const marker = {
|
|
3840
|
+
detachedFrom: relativeRoot || ".",
|
|
3841
|
+
detachedWorkspace: loaded.anchoredWorkspace,
|
|
3842
|
+
detachedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3843
|
+
originalCnosrc: {
|
|
3844
|
+
root: relativeRoot || ".",
|
|
3845
|
+
workspace: loaded.anchoredWorkspace
|
|
3846
|
+
}
|
|
3847
|
+
};
|
|
3848
|
+
await writeFile9(path21.join(targetCnosRoot, ".detached"), stringifyYaml7(marker), "utf8");
|
|
3849
|
+
await writeCnosrc(packageRoot, { root: "./.cnos" });
|
|
3850
|
+
if (options.json) {
|
|
3851
|
+
return printJson({
|
|
3852
|
+
packageRoot,
|
|
3853
|
+
detachedWorkspace: loaded.anchoredWorkspace,
|
|
3854
|
+
cnosRoot: targetCnosRoot
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3857
|
+
return `detached workspace ${loaded.anchoredWorkspace} into ${displayPath(targetCnosRoot, packageRoot)}`;
|
|
3858
|
+
}
|
|
3859
|
+
async function runAttach(packageRoot, options = {}) {
|
|
3860
|
+
const cliArgs = [...options.cliArgs ?? []];
|
|
3861
|
+
const force = consumeFlag(cliArgs, "--force");
|
|
3862
|
+
const childCnosRoot = path21.join(packageRoot, ".cnos");
|
|
3863
|
+
const markerPath = path21.join(childCnosRoot, ".detached");
|
|
3864
|
+
if (!await exists(markerPath)) {
|
|
3865
|
+
throw new Error("workspace attach requires a detached package with .cnos/.detached");
|
|
3866
|
+
}
|
|
3867
|
+
const marker = parseYaml5(await readFile6(markerPath, "utf8"));
|
|
3868
|
+
if (!marker?.originalCnosrc?.root || !marker.detachedWorkspace) {
|
|
3869
|
+
throw new Error("Invalid .detached marker");
|
|
3870
|
+
}
|
|
3871
|
+
const parentManifestRoot = path21.resolve(packageRoot, marker.originalCnosrc.root);
|
|
3872
|
+
const parentLoaded = await loadManifest4({ root: parentManifestRoot });
|
|
3873
|
+
const workspaceId = marker.originalCnosrc.workspace ?? marker.detachedWorkspace;
|
|
3874
|
+
const parentWorkspaceRoot = path21.join(parentLoaded.manifestRoot, "workspaces", workspaceId);
|
|
3875
|
+
if (await exists(parentWorkspaceRoot) && !force) {
|
|
3876
|
+
throw new Error(`workspace "${workspaceId}" already exists in parent root. Use --force to overwrite.`);
|
|
3877
|
+
}
|
|
3878
|
+
if (force) {
|
|
3879
|
+
await rm4(parentWorkspaceRoot, { recursive: true, force: true });
|
|
3880
|
+
}
|
|
3881
|
+
await mkdir6(parentWorkspaceRoot, { recursive: true });
|
|
3882
|
+
for (const folderName of ["values", "secrets", "env", "profiles"]) {
|
|
3883
|
+
await copyIfExists(path21.join(childCnosRoot, folderName), path21.join(parentWorkspaceRoot, folderName));
|
|
3884
|
+
}
|
|
3885
|
+
const rawManifest = parentLoaded.rawManifest;
|
|
3886
|
+
const workspaces = rawManifest.workspaces ?? {};
|
|
3887
|
+
const items = workspaces.items ?? {};
|
|
3888
|
+
items[workspaceId] = items[workspaceId] ?? {};
|
|
3889
|
+
workspaces.items = items;
|
|
3890
|
+
rawManifest.workspaces = workspaces;
|
|
3891
|
+
await writeFile9(path21.join(parentLoaded.manifestRoot, "cnos.yml"), stringifyYaml7(rawManifest), "utf8");
|
|
3892
|
+
const archivePath = path21.join(packageRoot, ".cnos.detached.bak");
|
|
3893
|
+
await rm4(archivePath, { recursive: true, force: true });
|
|
3894
|
+
await rename(childCnosRoot, archivePath);
|
|
3895
|
+
await writeCnosrc(packageRoot, {
|
|
3896
|
+
root: marker.originalCnosrc.root,
|
|
3897
|
+
...workspaceId ? { workspace: workspaceId } : {}
|
|
3898
|
+
});
|
|
3899
|
+
if (options.json) {
|
|
3900
|
+
return printJson({
|
|
3901
|
+
packageRoot,
|
|
3902
|
+
workspace: workspaceId,
|
|
3903
|
+
parentRoot: parentLoaded.manifestRoot,
|
|
3904
|
+
archivedTo: archivePath
|
|
3905
|
+
});
|
|
3906
|
+
}
|
|
3907
|
+
return `attached workspace ${workspaceId} to ${displayPath(parentLoaded.manifestRoot, packageRoot)}`;
|
|
3908
|
+
}
|
|
3909
|
+
async function runWorkspace(args = [], options = {}) {
|
|
3910
|
+
const [action] = args;
|
|
3911
|
+
const cliArgs = [...options.cliArgs ?? []];
|
|
3912
|
+
const packageRoot = path21.resolve(consumeOption(cliArgs, "--package-root") ?? options.root ?? process.cwd());
|
|
3913
|
+
switch (action) {
|
|
3914
|
+
case "detach":
|
|
3915
|
+
return runDetach(packageRoot, { ...options, cliArgs });
|
|
3916
|
+
case "attach":
|
|
3917
|
+
return runAttach(packageRoot, { ...options, cliArgs });
|
|
3918
|
+
default:
|
|
3919
|
+
throw new Error(`Unsupported workspace action: ${action ?? "(missing)"}`);
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3608
3923
|
// src/index.ts
|
|
3609
3924
|
function resolveHelpTopic(command, args) {
|
|
3610
3925
|
if (command === "help" || command === "help-ai") {
|
|
@@ -3616,9 +3931,15 @@ function resolveHelpTopic(command, args) {
|
|
|
3616
3931
|
if (command === "build" && args[0] === "env") {
|
|
3617
3932
|
return normalizeHelpTopic([command, args[0]]);
|
|
3618
3933
|
}
|
|
3934
|
+
if (command === "build" && args[0] && ["env", "server", "browser", "public"].includes(args[0])) {
|
|
3935
|
+
return normalizeHelpTopic([command, args[0]]);
|
|
3936
|
+
}
|
|
3619
3937
|
if (command === "dev" && args[0] === "env") {
|
|
3620
3938
|
return normalizeHelpTopic([command, args[0]]);
|
|
3621
3939
|
}
|
|
3940
|
+
if (command === "workspace" && args[0] && ["attach", "detach"].includes(args[0])) {
|
|
3941
|
+
return normalizeHelpTopic([command, args[0]]);
|
|
3942
|
+
}
|
|
3622
3943
|
if (command === "vault" && args[0] && ["create", "add", "list", "delete", "remove"].includes(args[0])) {
|
|
3623
3944
|
return normalizeHelpTopic([command, args[0] === "delete" ? "remove" : args[0] === "add" ? "create" : args[0]]);
|
|
3624
3945
|
}
|
|
@@ -3773,6 +4094,10 @@ async function main(argv) {
|
|
|
3773
4094
|
}
|
|
3774
4095
|
case "watch":
|
|
3775
4096
|
process.stdout.write(`${await runWatch(passthrough.length > 0 ? passthrough : args, runtimeOptions)}
|
|
4097
|
+
`);
|
|
4098
|
+
return;
|
|
4099
|
+
case "workspace":
|
|
4100
|
+
process.stdout.write(`${await runWorkspace(args, runtimeOptions)}
|
|
3776
4101
|
`);
|
|
3777
4102
|
return;
|
|
3778
4103
|
case "diff":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kitsy/cnos-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "CLI entry point and developer tooling for CNOS.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@kitsy/cnos": "1.
|
|
39
|
+
"@kitsy/cnos": "1.6.0"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsup src/index.ts --format esm --dts",
|