@treeseed/cli 0.5.1 → 0.6.1

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.
@@ -73,7 +73,7 @@ function normalizedClusterKey(entry) {
73
73
  return `cloudflare-${cluster}`;
74
74
  }
75
75
  if (provider === "railway") {
76
- if (entry.id.includes("API_TOKEN") || entry.id === "RAILWAY_TOKEN") {
76
+ if (entry.id.includes("API_TOKEN")) {
77
77
  return "railway-access";
78
78
  }
79
79
  return `railway-${cluster}`;
@@ -110,9 +110,10 @@ function renderConfigResult(commandName, result) {
110
110
  const configContext = payload.context;
111
111
  const configReadiness = configContext?.configReadinessByScope?.local ?? {};
112
112
  const readinessByScope = payload.result?.readinessByScope ?? {};
113
+ const resourceInventoryByScope = payload.result?.resourceInventoryByScope ?? payload.resourceInventoryByScope ?? {};
113
114
  const secretSession = payload.secretSession;
114
115
  const sharedStorageMigrations = payload.result?.sharedStorageMigrations;
115
- const summary = payload.mode === "print-env-only" ? "Treeseed config environment report completed." : payload.mode === "rotate-machine-key" ? "Treeseed machine key rotated successfully." : payload.mode === "connect-market" ? "Knowledge Coop pairing completed successfully." : "Treeseed config completed successfully.";
116
+ const summary = payload.mode === "print-env-only" ? "Treeseed config environment report completed." : payload.mode === "rotate-machine-key" ? "Treeseed machine key rotated successfully." : payload.mode === "connect-market" ? "Knowledge Coop pairing completed successfully." : payload.mode === "bootstrap-preflight" ? "Treeseed bootstrap verification preflight completed." : payload.mode === "bootstrap" ? "Treeseed platform bootstrap completed successfully." : "Treeseed config completed successfully.";
116
117
  const market = payload.market;
117
118
  return guidedResult({
118
119
  command: commandName,
@@ -124,11 +125,17 @@ function renderConfigResult(commandName, result) {
124
125
  { label: "Safe repairs", value: Array.isArray(payload.repairs) ? payload.repairs.length : 0 },
125
126
  { label: "Machine config", value: payload.configPath },
126
127
  { label: "Machine key", value: payload.keyPath },
128
+ { label: "Passphrase env", value: payload.passphraseEnv?.configured ? "configured" : "unset" },
127
129
  { label: "Secrets session", value: describeSecretBootstrap(secretSession) },
128
130
  { label: "Shared consolidations", value: describeSharedStorageMigrations(sharedStorageMigrations) },
131
+ { label: "Deployment key", value: resourceInventoryByScope.staging?.identity?.deploymentKey ?? resourceInventoryByScope.prod?.identity?.deploymentKey ?? "(unset)" },
132
+ { label: "Team", value: resourceInventoryByScope.staging?.identity?.teamId ?? resourceInventoryByScope.prod?.identity?.teamId ?? "(unset)" },
133
+ { label: "Project", value: resourceInventoryByScope.staging?.identity?.projectId ?? resourceInventoryByScope.prod?.identity?.projectId ?? "(unset)" },
129
134
  { label: "Local readiness", value: configReadinessLabel(readinessByScope.local) },
130
135
  { label: "Staging readiness", value: configReadinessLabel(readinessByScope.staging) },
131
136
  { label: "Prod readiness", value: configReadinessLabel(readinessByScope.prod) },
137
+ { label: "Pages project", value: resourceInventoryByScope.staging?.resources?.pagesProject ?? resourceInventoryByScope.prod?.resources?.pagesProject ?? "(unset)" },
138
+ { label: "R2 bucket", value: resourceInventoryByScope.staging?.resources?.contentBucket ?? resourceInventoryByScope.prod?.resources?.contentBucket ?? "(unset)" },
132
139
  { label: "GitHub token/config", value: configReadiness.github?.configured ? "configured" : "missing" },
133
140
  { label: "Cloudflare token/config", value: configReadiness.cloudflare?.configured ? "configured" : "missing" },
134
141
  { label: "Railway token/config", value: configReadiness.railway?.configured ? "configured" : "missing" },
@@ -147,7 +154,10 @@ function renderConfigResult(commandName, result) {
147
154
  { label: "Runtime ready", value: market.runtimeReady ? "yes" : "no" }
148
155
  ] : []
149
156
  ],
150
- nextSteps: renderWorkflowNextSteps(result),
157
+ nextSteps: [
158
+ ...payload.passphraseEnv?.configured ? [] : [payload.passphraseEnv?.recommendedLaunch].filter(Boolean),
159
+ ...renderWorkflowNextSteps(result)
160
+ ],
151
161
  report: payload
152
162
  });
153
163
  }
@@ -161,9 +171,9 @@ const handleConfig = async (invocation, context) => {
161
171
  const sync = invocation.args.sync;
162
172
  const interactive = context.outputFormat !== "json" && context.interactiveUi !== false && process.stdin.isTTY && process.stdout.isTTY;
163
173
  const nonInteractive = invocation.args.nonInteractive === true || context.outputFormat === "json";
164
- const operationalMode = invocation.args.printEnvOnly === true || invocation.args.rotateMachineKey === true || invocation.args.connectMarket === true;
174
+ const operationalMode = invocation.args.printEnvOnly === true || invocation.args.rotateMachineKey === true || invocation.args.connectMarket === true || invocation.args.bootstrap === true;
165
175
  if (!interactive && !nonInteractive && !operationalMode) {
166
- return fail("Treeseed config requires a TTY for the interactive editor. Re-run in a terminal, or use --non-interactive, --json, --print-env-only, --rotate-machine-key, or --connect-market.");
176
+ return fail("Treeseed config requires a TTY for the interactive editor. Re-run in a terminal, or use --non-interactive, --json, --bootstrap, --print-env-only, --rotate-machine-key, or --connect-market.");
167
177
  }
168
178
  if (interactive && !nonInteractive && !operationalMode) {
169
179
  const tenantRoot = findNearestTreeseedRoot(context.cwd) ?? context.cwd;
@@ -247,6 +257,8 @@ const handleConfig = async (invocation, context) => {
247
257
  const result = await workflow.config({
248
258
  environment: invocation.args.environment,
249
259
  sync,
260
+ bootstrap: invocation.args.bootstrap === true,
261
+ preflight: invocation.args.preflight === true,
250
262
  printEnv: invocation.args.printEnv === true,
251
263
  printEnvOnly: invocation.args.printEnvOnly === true,
252
264
  showSecrets: invocation.args.showSecrets === true,
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  inspectTreeseedKeyAgentStatus,
3
+ inspectTreeseedKeyAgentTransportDiagnostic,
4
+ inspectTreeseedPassphraseEnvDiagnostic,
3
5
  lockTreeseedSecretSession,
4
6
  migrateTreeseedMachineKeyToWrapped,
5
7
  rotateTreeseedMachineKey,
@@ -11,8 +13,10 @@ import {
11
13
  } from "@treeseed/sdk/workflow-support";
12
14
  import { fail, guidedResult } from "./utils.js";
13
15
  import { promptForNewPassphrase } from "./secret-prompts.js";
14
- function renderStatus(command, tenantRoot) {
16
+ async function renderStatus(command, tenantRoot) {
15
17
  const status = inspectTreeseedKeyAgentStatus(tenantRoot);
18
+ const passphraseEnv = inspectTreeseedPassphraseEnvDiagnostic();
19
+ const transport = await inspectTreeseedKeyAgentTransportDiagnostic();
16
20
  return guidedResult({
17
21
  command,
18
22
  summary: status.unlocked ? "Treeseed secrets are unlocked." : "Treeseed secrets are locked.",
@@ -20,14 +24,24 @@ function renderStatus(command, tenantRoot) {
20
24
  { label: "Key agent", value: status.running ? "running" : "stopped" },
21
25
  { label: "Wrapped key", value: status.wrappedKeyPresent ? "present" : "missing" },
22
26
  { label: "Migration required", value: status.migrationRequired ? "yes" : "no" },
27
+ { label: "Socket", value: transport.socketPresent ? "present" : "missing" },
28
+ { label: "Socket connect", value: transport.socketConnectable ? "yes" : "no" },
29
+ { label: "Socket health", value: transport.healthOk ? "ok" : "failed" },
23
30
  { label: "Idle timeout", value: `${Math.round(status.idleTimeoutMs / 1e3)}s` },
24
31
  { label: "Idle remaining", value: `${Math.round(status.idleRemainingMs / 1e3)}s` },
25
- { label: "Passphrase env", value: process.env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV]?.trim() ? "configured" : "unset" },
26
- { label: "Key path", value: status.keyPath }
32
+ { label: "Passphrase env", value: passphraseEnv.configured ? "configured" : "unset" },
33
+ { label: "Key path", value: status.keyPath },
34
+ { label: "Socket path", value: transport.socketPath }
27
35
  ],
28
36
  report: {
29
- status
30
- }
37
+ status,
38
+ passphraseEnv,
39
+ transport
40
+ },
41
+ nextSteps: [
42
+ ...passphraseEnv.configured ? [] : [passphraseEnv.recommendedLaunch],
43
+ ...transport.lastError ? [`Key-agent transport error: ${transport.lastError}`] : []
44
+ ]
31
45
  });
32
46
  }
33
47
  function keyErrorResult(command, error) {
@@ -702,6 +702,8 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
702
702
  { name: "mouse", flags: "--mouse", description: "Opt into mouse capture for the config UI. Keyboard-first terminal behavior remains the default.", kind: "boolean" },
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
+ { name: "bootstrap", flags: "--bootstrap", description: "Skip the editor and run platform reconciliation/bootstrap from the currently saved required values.", kind: "boolean" },
706
+ { name: "preflight", flags: "--preflight", description: "Inspect bootstrap verification readiness and planned checks without mutating provider resources.", kind: "boolean" },
705
707
  { 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" },
706
708
  { 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" },
707
709
  { name: "printEnv", flags: "--print-env", description: "Print resolved environment values before remote initialization.", kind: "boolean" },
@@ -719,7 +721,7 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
719
721
  { name: "rotateRunnerToken", flags: "--rotate-runner-token", description: "Rotate the project runner credential while pairing the local hybrid repo.", kind: "boolean" },
720
722
  { name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
721
723
  ],
722
- examples: ["treeseed config", "treeseed config --full --mouse", "treeseed config --environment all", "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"],
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"],
723
725
  notes: ["Does not create branch preview deployments. Use `treeseed switch <branch> --preview` for that."],
724
726
  help: {
725
727
  workflowPosition: "configure runtime",
@@ -745,6 +747,8 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
745
747
  example("treeseed config", "Run the startup wizard", "Open the newcomer-friendly configuration wizard in human TTY mode."),
746
748
  example("treeseed config --full", "Open the advanced editor directly", "Skip the startup wizard and go straight to the full configuration surface."),
747
749
  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
+ 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."),
751
+ 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."),
748
752
  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."),
749
753
  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."),
750
754
  example("treeseed config --environment staging --print-env-only --show-secrets", "Inspect a resolved environment report", "Print the resolved staging environment with full secret visibility and exit."),
@@ -756,6 +760,8 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
756
760
  detail("--mouse", "Opt into terminal mouse capture for clicking, scrolling, and focus changes inside the config UI."),
757
761
  detail("--environment <scope>", "Filter configuration to `all`, `local`, `staging`, or `prod`."),
758
762
  detail("--sync <mode>", "Choose which provider surfaces should receive synchronized values after local updates are applied."),
763
+ detail("--bootstrap", "Skip the editor and run the reconcile/bootstrap path from the values already stored in machine config."),
764
+ detail("--preflight", "When combined with `--bootstrap`, inspect verification readiness and planned reconcile actions without mutating provider resources."),
759
765
  detail("--non-interactive", "Apply resolved values without opening the interactive editor. Use this for automation when you do not want `--json` output."),
760
766
  detail("--install-missing-tooling", "Allow config to install missing verification helpers such as the GitHub `gh-act` extension instead of only reporting them."),
761
767
  detail("--print-env", "Print the resolved environment values before remote initialization continues."),
@@ -765,7 +771,7 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
765
771
  ],
766
772
  automationNotes: [
767
773
  "Use `--json` for machine-readable automation, or `--non-interactive` when you want deterministic application without interactive UI.",
768
- "`--print-env-only`, `--rotate-machine-key`, and `--connect-market` are operational paths that bypass the interactive UI.",
774
+ "`--bootstrap`, `--bootstrap --preflight`, `--print-env-only`, `--rotate-machine-key`, and `--connect-market` are operational paths that bypass the interactive UI.",
769
775
  "Config reports missing tooling by default. Use `--install-missing-tooling` when you want the command to attempt installation.",
770
776
  "Shared versus scoped environment semantics are resolved inside the SDK; the CLI help should be treated as the operator-facing explanation layer."
771
777
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/cli",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
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.5.3",
47
+ "@treeseed/sdk": "^0.6.1",
48
48
  "ink": "^7.0.0",
49
49
  "react": "^19.2.5"
50
50
  },