@treeseed/cli 0.6.1 → 0.6.2
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.
|
@@ -17,6 +17,13 @@ function normalizeConfigScopes(value) {
|
|
|
17
17
|
}
|
|
18
18
|
return ["local", "staging", "prod"].filter((scope) => requested.includes(scope));
|
|
19
19
|
}
|
|
20
|
+
function normalizeBootstrapSystems(system, systems) {
|
|
21
|
+
const values = [
|
|
22
|
+
...Array.isArray(system) ? system.map(String) : typeof system === "string" ? [system] : [],
|
|
23
|
+
...typeof systems === "string" ? systems.split(",") : Array.isArray(systems) ? systems.flatMap((value) => String(value).split(",")) : []
|
|
24
|
+
].map((value) => value.trim()).filter(Boolean);
|
|
25
|
+
return values.length > 0 ? values : void 0;
|
|
26
|
+
}
|
|
20
27
|
function formatPrintEnvReports(payload) {
|
|
21
28
|
const lines = [];
|
|
22
29
|
for (const report of payload.reports ?? []) {
|
|
@@ -110,6 +117,8 @@ function renderConfigResult(commandName, result) {
|
|
|
110
117
|
const configContext = payload.context;
|
|
111
118
|
const configReadiness = configContext?.configReadinessByScope?.local ?? {};
|
|
112
119
|
const readinessByScope = payload.result?.readinessByScope ?? {};
|
|
120
|
+
const bootstrapSystemsByScope = payload.result?.bootstrapSystemsByScope ?? payload.bootstrapSystemsByScope ?? {};
|
|
121
|
+
const skippedSystems = Object.values(bootstrapSystemsByScope).flatMap((entry) => Array.isArray(entry?.skipped) ? entry.skipped : []);
|
|
113
122
|
const resourceInventoryByScope = payload.result?.resourceInventoryByScope ?? payload.resourceInventoryByScope ?? {};
|
|
114
123
|
const secretSession = payload.secretSession;
|
|
115
124
|
const sharedStorageMigrations = payload.result?.sharedStorageMigrations;
|
|
@@ -122,6 +131,8 @@ function renderConfigResult(commandName, result) {
|
|
|
122
131
|
{ label: "Mode", value: payload.mode },
|
|
123
132
|
{ label: "Scopes", value: Array.isArray(payload.scopes) ? payload.scopes.join(", ") : "(none)" },
|
|
124
133
|
{ label: "Sync", value: payload.sync ?? "all" },
|
|
134
|
+
{ label: "Bootstrap systems", value: Object.values(bootstrapSystemsByScope).flatMap((entry) => entry?.runnable ?? []).filter((value, index, all) => all.indexOf(value) === index).join(", ") || "(none)" },
|
|
135
|
+
{ label: "Skipped systems", value: skippedSystems.map((entry) => entry.system).filter((value, index, all) => all.indexOf(value) === index).join(", ") || "(none)" },
|
|
125
136
|
{ label: "Safe repairs", value: Array.isArray(payload.repairs) ? payload.repairs.length : 0 },
|
|
126
137
|
{ label: "Machine config", value: payload.configPath },
|
|
127
138
|
{ label: "Machine key", value: payload.keyPath },
|
|
@@ -169,6 +180,7 @@ const handleConfig = async (invocation, context) => {
|
|
|
169
180
|
});
|
|
170
181
|
const scopes = normalizeConfigScopes(invocation.args.environment);
|
|
171
182
|
const sync = invocation.args.sync;
|
|
183
|
+
const systems = normalizeBootstrapSystems(invocation.args.system, invocation.args.systems);
|
|
172
184
|
const interactive = context.outputFormat !== "json" && context.interactiveUi !== false && process.stdin.isTTY && process.stdout.isTTY;
|
|
173
185
|
const nonInteractive = invocation.args.nonInteractive === true || context.outputFormat === "json";
|
|
174
186
|
const operationalMode = invocation.args.printEnvOnly === true || invocation.args.rotateMachineKey === true || invocation.args.connectMarket === true || invocation.args.bootstrap === true;
|
|
@@ -245,6 +257,9 @@ const handleConfig = async (invocation, context) => {
|
|
|
245
257
|
context.write("Applying config updates, validating environments, and syncing managed providers...", "stdout");
|
|
246
258
|
const result2 = await workflow.config({
|
|
247
259
|
environment: scopes,
|
|
260
|
+
systems,
|
|
261
|
+
skipUnavailable: invocation.args.skipUnavailable === true ? true : void 0,
|
|
262
|
+
bootstrapExecution: invocation.args.bootstrapSequential === true ? "sequential" : "parallel",
|
|
248
263
|
sync,
|
|
249
264
|
printEnv: invocation.args.printEnv === true,
|
|
250
265
|
showSecrets: invocation.args.showSecrets === true,
|
|
@@ -256,6 +271,9 @@ const handleConfig = async (invocation, context) => {
|
|
|
256
271
|
}
|
|
257
272
|
const result = await workflow.config({
|
|
258
273
|
environment: invocation.args.environment,
|
|
274
|
+
systems,
|
|
275
|
+
skipUnavailable: invocation.args.skipUnavailable === true ? true : void 0,
|
|
276
|
+
bootstrapExecution: invocation.args.bootstrapSequential === true ? "sequential" : "parallel",
|
|
259
277
|
sync,
|
|
260
278
|
bootstrap: invocation.args.bootstrap === true,
|
|
261
279
|
preflight: invocation.args.preflight === true,
|
package/dist/cli/help.js
CHANGED
|
@@ -197,7 +197,8 @@ function buildTreeseedHelpView(commandName) {
|
|
|
197
197
|
lines: [
|
|
198
198
|
"Workspace-only commands must be run inside a Treeseed workspace; the CLI resolves the project root from ancestor directories when possible.",
|
|
199
199
|
"Help text is generated from the CLI command registry.",
|
|
200
|
-
"Use --json on supported workflow and utility commands when an AI agent or script needs machine-readable output."
|
|
200
|
+
"Use --json on supported workflow and utility commands when an AI agent or script needs machine-readable output.",
|
|
201
|
+
"Use --no-color, NO_COLOR, or TREESEED_NO_COLOR=1 to disable CLI color output globally."
|
|
201
202
|
]
|
|
202
203
|
});
|
|
203
204
|
return {
|
|
@@ -703,6 +703,10 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
703
703
|
{ name: "environment", flags: "--environment <scope>", description: "Select all environments or limit configuration to local, staging, or prod. Defaults to all.", kind: "enum", repeatable: true, values: ["all", "local", "staging", "prod"] },
|
|
704
704
|
{ name: "sync", flags: "--sync <mode>", description: "Sync hosted secrets/variables to GitHub, Cloudflare, Railway, or all providers. Defaults to all.", kind: "enum", values: ["none", "github", "cloudflare", "railway", "all"] },
|
|
705
705
|
{ name: "bootstrap", flags: "--bootstrap", description: "Skip the editor and run platform reconciliation/bootstrap from the currently saved required values.", kind: "boolean" },
|
|
706
|
+
{ name: "system", flags: "--system <system>", description: "Limit bootstrap to a system group. Repeatable. Values: all, github, data, web, api, agents.", kind: "enum", repeatable: true, values: ["all", "github", "data", "web", "api", "agents"] },
|
|
707
|
+
{ name: "systems", flags: "--systems <systems>", description: "Comma-separated bootstrap system groups. Values: all, github, data, web, api, agents.", kind: "string" },
|
|
708
|
+
{ name: "skipUnavailable", flags: "--skip-unavailable", description: "Skip selected bootstrap systems whose required provider credentials are not configured.", kind: "boolean" },
|
|
709
|
+
{ name: "bootstrapSequential", flags: "--bootstrap-sequential", description: "Run bootstrap DAG tasks sequentially for ordered debugging logs.", kind: "boolean" },
|
|
706
710
|
{ name: "preflight", flags: "--preflight", description: "Inspect bootstrap verification readiness and planned checks without mutating provider resources.", kind: "boolean" },
|
|
707
711
|
{ name: "nonInteractive", flags: "--non-interactive", description: "Apply resolved values without opening the interactive UI. Required for non-TTY automation unless using an operational mode such as --print-env-only.", kind: "boolean" },
|
|
708
712
|
{ name: "installMissingTooling", flags: "--install-missing-tooling", description: "Install missing config verification tooling such as `gh-act` during the run instead of only reporting it.", kind: "boolean" },
|
|
@@ -721,7 +725,7 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
721
725
|
{ name: "rotateRunnerToken", flags: "--rotate-runner-token", description: "Rotate the project runner credential while pairing the local hybrid repo.", kind: "boolean" },
|
|
722
726
|
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
723
727
|
],
|
|
724
|
-
examples: ["treeseed config", "treeseed config --full --mouse", "treeseed config --environment all", "treeseed config --environment staging --bootstrap", "treeseed config --environment staging --bootstrap --preflight", "treeseed config --environment local --sync none", "treeseed config --environment local --sync none --non-interactive", "treeseed config --environment staging --print-env-only --show-secrets", "treeseed config --rotate-machine-key", "treeseed config --connect-market --market-project-id kc_proj_123"],
|
|
728
|
+
examples: ["treeseed config", "treeseed config --full --mouse", "treeseed config --environment all", "treeseed config --environment staging --bootstrap", "treeseed config --environment staging --bootstrap --system web", "treeseed config --environment staging --bootstrap --preflight", "treeseed config --environment local --sync none", "treeseed config --environment local --sync none --non-interactive", "treeseed config --environment staging --print-env-only --show-secrets", "treeseed config --rotate-machine-key", "treeseed config --connect-market --market-project-id kc_proj_123"],
|
|
725
729
|
notes: ["Does not create branch preview deployments. Use `treeseed switch <branch> --preview` for that."],
|
|
726
730
|
help: {
|
|
727
731
|
workflowPosition: "configure runtime",
|
|
@@ -748,6 +752,7 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
748
752
|
example("treeseed config --full", "Open the advanced editor directly", "Skip the startup wizard and go straight to the full configuration surface."),
|
|
749
753
|
example("treeseed config --full --mouse", "Opt into mouse capture for the editor", "Keep the keyboard-first defaults unless you explicitly want click and wheel interaction inside the config UI."),
|
|
750
754
|
example("treeseed config --environment staging --bootstrap", "Bootstrap infrastructure from saved config", "Skip the editor and run reconciliation/bootstrap when the required values are already configured."),
|
|
755
|
+
example("treeseed config --environment staging --bootstrap --system web", "Bootstrap only the hosted hub", "Provision data and web systems while leaving optional API and agent Railway services alone."),
|
|
751
756
|
example("treeseed config --environment staging --bootstrap --preflight", "Inspect bootstrap verification readiness", "Show the resolved units, verification capabilities, and planned reconcile actions without mutating provider resources."),
|
|
752
757
|
example("treeseed config --environment local --sync none", "Edit local values without provider sync", "Limit the session to local values and avoid hosted synchronization while iterating locally."),
|
|
753
758
|
example("treeseed config --environment local --sync none --non-interactive", "Apply deterministic local config in automation", "Use the resolved current and suggested values without opening the interactive UI."),
|
|
@@ -761,6 +766,10 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
761
766
|
detail("--environment <scope>", "Filter configuration to `all`, `local`, `staging`, or `prod`."),
|
|
762
767
|
detail("--sync <mode>", "Choose which provider surfaces should receive synchronized values after local updates are applied."),
|
|
763
768
|
detail("--bootstrap", "Skip the editor and run the reconcile/bootstrap path from the values already stored in machine config."),
|
|
769
|
+
detail("--system <system>", "Limit bootstrap to a stable system group. Repeat it to select multiple groups."),
|
|
770
|
+
detail("--systems <systems>", "Comma-separated form of --system for automation."),
|
|
771
|
+
detail("--skip-unavailable", "Skip selected systems whose provider credentials are missing instead of failing the run."),
|
|
772
|
+
detail("--bootstrap-sequential", "Run bootstrap DAG tasks one at a time to preserve log order for debugging or LLM review."),
|
|
764
773
|
detail("--preflight", "When combined with `--bootstrap`, inspect verification readiness and planned reconcile actions without mutating provider resources."),
|
|
765
774
|
detail("--non-interactive", "Apply resolved values without opening the interactive editor. Use this for automation when you do not want `--json` output."),
|
|
766
775
|
detail("--install-missing-tooling", "Allow config to install missing verification helpers such as the GitHub `gh-act` extension instead of only reporting them."),
|
package/dist/cli/runtime.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TreeseedCommandContext, TreeseedHandlerResolver, TreeseedOperationRequest, TreeseedOperationResult, TreeseedOperationSpec } from './operations-types.ts';
|
|
2
|
+
export declare function colorizeTreeseedCliOutput(output: string, colorEnabled?: boolean): string;
|
|
2
3
|
export declare function createTreeseedCommandContext(overrides?: Partial<TreeseedCommandContext>): TreeseedCommandContext;
|
|
3
4
|
export declare function writeTreeseedResult(result: TreeseedOperationResult | {
|
|
4
5
|
exitCode?: number;
|
package/dist/cli/runtime.js
CHANGED
|
@@ -14,6 +14,54 @@ const require2 = createRequire(import.meta.url);
|
|
|
14
14
|
function isHelpFlag(value) {
|
|
15
15
|
return value === "--help" || value === "-h";
|
|
16
16
|
}
|
|
17
|
+
function isNoColorFlag(value) {
|
|
18
|
+
return value === "--no-color";
|
|
19
|
+
}
|
|
20
|
+
function stripGlobalFlags(argv) {
|
|
21
|
+
return argv.filter((value) => !isNoColorFlag(value));
|
|
22
|
+
}
|
|
23
|
+
function resolveColorEnabled(argv, env, override) {
|
|
24
|
+
if (typeof override === "boolean") {
|
|
25
|
+
return override;
|
|
26
|
+
}
|
|
27
|
+
if (argv.some(isNoColorFlag)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (env.NO_COLOR !== void 0 || env.TREESEED_NO_COLOR === "1" || env.TREESEED_NO_COLOR === "true") {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
function colorCodeForBootstrapSystem(system) {
|
|
36
|
+
switch (system) {
|
|
37
|
+
case "github":
|
|
38
|
+
return "35;1";
|
|
39
|
+
case "data":
|
|
40
|
+
return "34;1";
|
|
41
|
+
case "web":
|
|
42
|
+
return "36;1";
|
|
43
|
+
case "api":
|
|
44
|
+
return "32;1";
|
|
45
|
+
case "agents":
|
|
46
|
+
return "33;1";
|
|
47
|
+
case "skip":
|
|
48
|
+
return "90;1";
|
|
49
|
+
default:
|
|
50
|
+
return "37;1";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function colorizeTreeseedCliOutput(output, colorEnabled = true) {
|
|
54
|
+
if (!colorEnabled) {
|
|
55
|
+
return output;
|
|
56
|
+
}
|
|
57
|
+
return output.replace(/^((?:\[[^\]]+\]){3,4})(\s|$)/u, (match, prefix, suffix) => {
|
|
58
|
+
const segments = [...prefix.matchAll(/\[([^\]]+)\]/gu)].map((entry) => entry[1]);
|
|
59
|
+
const system = segments[1] ?? "";
|
|
60
|
+
const stage = segments[segments.length - 1] ?? "";
|
|
61
|
+
const code = /fail|error/iu.test(stage) ? "31;1" : /skip/iu.test(stage) ? "90;1" : colorCodeForBootstrapSystem(system);
|
|
62
|
+
return `\x1B[${code}m${prefix}\x1B[0m${suffix}`;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
17
65
|
function defaultWrite(output, stream = "stdout") {
|
|
18
66
|
if (!output) return;
|
|
19
67
|
(stream === "stderr" ? process.stderr : process.stdout).write(`${output}
|
|
@@ -68,13 +116,16 @@ function formatValidationError(spec, errors) {
|
|
|
68
116
|
].join("\n");
|
|
69
117
|
}
|
|
70
118
|
function createTreeseedCommandContext(overrides = {}) {
|
|
119
|
+
const colorEnabled = resolveColorEnabled([], overrides.env ?? process.env, overrides.colorEnabled);
|
|
120
|
+
const rawWrite = overrides.write ?? defaultWrite;
|
|
71
121
|
return {
|
|
72
122
|
cwd: overrides.cwd ?? process.cwd(),
|
|
73
123
|
env: overrides.env ?? process.env,
|
|
74
|
-
write: overrides.write
|
|
124
|
+
write: overrides.write ? rawWrite : ((output, stream) => rawWrite(colorizeTreeseedCliOutput(output, colorEnabled), stream)),
|
|
75
125
|
spawn: overrides.spawn ?? defaultSpawn,
|
|
76
126
|
outputFormat: overrides.outputFormat ?? "human",
|
|
77
127
|
interactiveUi: overrides.interactiveUi ?? overrides.write == null,
|
|
128
|
+
colorEnabled,
|
|
78
129
|
prompt: overrides.prompt,
|
|
79
130
|
confirm: overrides.confirm
|
|
80
131
|
};
|
|
@@ -292,14 +343,19 @@ const cliOperationsSdk = new TreeseedOperationsSdk({
|
|
|
292
343
|
resolveHandler: (handlerName) => COMMAND_HANDLERS[handlerName] ?? null
|
|
293
344
|
});
|
|
294
345
|
async function executeTreeseedCommand(commandName, argv, context) {
|
|
346
|
+
const cleanArgv = stripGlobalFlags(argv);
|
|
347
|
+
const commandContext = {
|
|
348
|
+
...context,
|
|
349
|
+
colorEnabled: resolveColorEnabled(argv, context.env ?? process.env, context.colorEnabled)
|
|
350
|
+
};
|
|
295
351
|
const spec = cliOperationsSdk.findOperation(commandName);
|
|
296
352
|
if (!spec) {
|
|
297
|
-
return cliOperationsSdk.executeOperation({ commandName, argv },
|
|
353
|
+
return cliOperationsSdk.executeOperation({ commandName, argv: cleanArgv }, commandContext);
|
|
298
354
|
}
|
|
299
|
-
if (
|
|
300
|
-
return cliOperationsSdk.executeOperation({ commandName, argv },
|
|
355
|
+
if (cleanArgv.some(isHelpFlag)) {
|
|
356
|
+
return cliOperationsSdk.executeOperation({ commandName, argv: cleanArgv }, commandContext);
|
|
301
357
|
}
|
|
302
|
-
const resolved = resolveTreeseedCommandCwd(spec,
|
|
358
|
+
const resolved = resolveTreeseedCommandCwd(spec, commandContext.cwd);
|
|
303
359
|
if (commandNeedsProjectRoot(spec) && !resolved.resolvedProjectRoot) {
|
|
304
360
|
return writeTreeseedResult({
|
|
305
361
|
exitCode: 1,
|
|
@@ -307,21 +363,23 @@ async function executeTreeseedCommand(commandName, argv, context) {
|
|
|
307
363
|
report: {
|
|
308
364
|
command: spec.name,
|
|
309
365
|
ok: false,
|
|
310
|
-
error: `No ancestor containing treeseed.site.yaml was found from ${
|
|
366
|
+
error: `No ancestor containing treeseed.site.yaml was found from ${commandContext.cwd}.`,
|
|
311
367
|
hint: `treeseed help ${spec.name}`
|
|
312
368
|
}
|
|
313
|
-
}, { ...
|
|
369
|
+
}, { ...commandContext, outputFormat: cleanArgv.includes("--json") ? "json" : commandContext.outputFormat ?? "human" });
|
|
314
370
|
}
|
|
315
|
-
return cliOperationsSdk.executeOperation({ commandName, argv }, { ...
|
|
371
|
+
return cliOperationsSdk.executeOperation({ commandName, argv: cleanArgv }, { ...commandContext, cwd: resolved.cwd });
|
|
316
372
|
}
|
|
317
373
|
async function runTreeseedCli(argv, overrides = {}) {
|
|
318
|
-
const
|
|
374
|
+
const cleanArgv = stripGlobalFlags(argv);
|
|
375
|
+
const colorEnabled = resolveColorEnabled(argv, overrides.env ?? process.env, overrides.colorEnabled);
|
|
376
|
+
const [firstArg] = cleanArgv;
|
|
319
377
|
const spec = firstArg ? cliOperationsSdk.findOperation(firstArg) : null;
|
|
320
378
|
if (!spec) {
|
|
321
|
-
return cliOperationsSdk.run(
|
|
379
|
+
return cliOperationsSdk.run(cleanArgv, { ...overrides, colorEnabled });
|
|
322
380
|
}
|
|
323
|
-
if (
|
|
324
|
-
return cliOperationsSdk.run(
|
|
381
|
+
if (cleanArgv.slice(1).some(isHelpFlag)) {
|
|
382
|
+
return cliOperationsSdk.run(cleanArgv, { ...overrides, colorEnabled });
|
|
325
383
|
}
|
|
326
384
|
const baseCwd = overrides.cwd ?? process.cwd();
|
|
327
385
|
const resolved = resolveTreeseedCommandCwd(spec, baseCwd);
|
|
@@ -337,14 +395,16 @@ async function runTreeseedCli(argv, overrides = {}) {
|
|
|
337
395
|
}
|
|
338
396
|
}, createTreeseedCommandContext({
|
|
339
397
|
...overrides,
|
|
340
|
-
|
|
398
|
+
colorEnabled,
|
|
399
|
+
outputFormat: cleanArgv.includes("--json") ? "json" : overrides.outputFormat ?? "human"
|
|
341
400
|
}));
|
|
342
401
|
}
|
|
343
|
-
const contextOverrides = commandNeedsProjectRoot(spec) && resolved.resolvedProjectRoot ? { ...overrides, cwd: resolved.cwd } : overrides;
|
|
344
|
-
return cliOperationsSdk.run(
|
|
402
|
+
const contextOverrides = commandNeedsProjectRoot(spec) && resolved.resolvedProjectRoot ? { ...overrides, cwd: resolved.cwd, colorEnabled } : { ...overrides, colorEnabled };
|
|
403
|
+
return cliOperationsSdk.run(cleanArgv, contextOverrides);
|
|
345
404
|
}
|
|
346
405
|
export {
|
|
347
406
|
TreeseedOperationsSdk,
|
|
407
|
+
colorizeTreeseedCliOutput,
|
|
348
408
|
createTreeseedCommandContext,
|
|
349
409
|
executeTreeseedCommand,
|
|
350
410
|
resolveTreeseedCommandCwd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Operator-facing Treeseed CLI package.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"release:publish": "node ./scripts/run-ts.mjs ./scripts/publish-package.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@treeseed/sdk": "^0.6.
|
|
47
|
+
"@treeseed/sdk": "^0.6.2",
|
|
48
48
|
"ink": "^7.0.0",
|
|
49
49
|
"react": "^19.2.5"
|
|
50
50
|
},
|