@treeseed/cli 0.4.11 → 0.5.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.
- package/README.md +35 -8
- package/dist/cli/handlers/auth-login.js +58 -46
- package/dist/cli/handlers/auth-logout.js +23 -11
- package/dist/cli/handlers/auth-whoami.js +27 -16
- package/dist/cli/handlers/close.js +9 -4
- package/dist/cli/handlers/config-ui.d.ts +65 -9
- package/dist/cli/handlers/config-ui.js +561 -175
- package/dist/cli/handlers/config.js +164 -10
- package/dist/cli/handlers/destroy.js +3 -2
- package/dist/cli/handlers/dev.js +6 -1
- package/dist/cli/handlers/doctor.js +82 -47
- package/dist/cli/handlers/recover.d.ts +2 -0
- package/dist/cli/handlers/recover.js +25 -0
- package/dist/cli/handlers/release.js +14 -4
- package/dist/cli/handlers/resume.d.ts +2 -0
- package/dist/cli/handlers/resume.js +23 -0
- package/dist/cli/handlers/save.js +9 -3
- package/dist/cli/handlers/secret-prompts.d.ts +2 -0
- package/dist/cli/handlers/secret-prompts.js +54 -0
- package/dist/cli/handlers/secrets.d.ts +7 -0
- package/dist/cli/handlers/secrets.js +161 -0
- package/dist/cli/handlers/stage.js +10 -4
- package/dist/cli/handlers/status.js +37 -5
- package/dist/cli/handlers/switch.js +11 -4
- package/dist/cli/handlers/tasks.js +1 -0
- package/dist/cli/handlers/utils.js +15 -8
- package/dist/cli/handlers/workflow.js +74 -3
- package/dist/cli/help-ui.js +1 -1
- package/dist/cli/operations-registry.js +200 -21
- package/dist/cli/registry.d.ts +8 -0
- package/dist/cli/registry.js +19 -1
- package/dist/cli/repair.js +5 -9
- package/dist/cli/ui/framework.d.ts +2 -0
- package/dist/cli/ui/framework.js +53 -22
- package/dist/cli/ui/mouse.d.ts +3 -1
- package/dist/cli/ui/mouse.js +3 -3
- package/package.json +7 -6
- package/scripts/verify-driver.mjs +34 -0
- package/dist/cli/workflow-state.d.ts +0 -58
- package/dist/cli/workflow-state.js +0 -195
|
@@ -202,9 +202,11 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
202
202
|
arguments: [{ name: "branch-name", description: "Task branch to create or resume.", required: true }],
|
|
203
203
|
options: [
|
|
204
204
|
{ name: "preview", flags: "--preview", description: "Provision or refresh a branch-scoped Cloudflare preview environment.", kind: "boolean" },
|
|
205
|
+
{ name: "plan", flags: "--plan", description: "Compute the recursive branch switch plan without mutating any repo.", kind: "boolean" },
|
|
206
|
+
{ name: "dryRun", flags: "--dry-run", description: "Alias for --plan.", kind: "boolean" },
|
|
205
207
|
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
206
208
|
],
|
|
207
|
-
examples: ["treeseed switch feature/search-improvements", "treeseed switch feature/search-improvements --preview"],
|
|
209
|
+
examples: ["treeseed switch feature/search-improvements", "treeseed switch feature/search-improvements --preview", "treeseed switch feature/search-improvements --plan"],
|
|
208
210
|
help: {
|
|
209
211
|
workflowPosition: "start work",
|
|
210
212
|
longSummary: [
|
|
@@ -252,9 +254,11 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
252
254
|
options: [
|
|
253
255
|
{ name: "hotfix", flags: "--hotfix", description: "Allow save on main for an explicit hotfix.", kind: "boolean" },
|
|
254
256
|
{ name: "preview", flags: "--preview", description: "Create or refresh the branch preview during save.", kind: "boolean" },
|
|
257
|
+
{ name: "plan", flags: "--plan", description: "Compute the recursive save plan without mutating any repo.", kind: "boolean" },
|
|
258
|
+
{ name: "dryRun", flags: "--dry-run", description: "Alias for --plan.", kind: "boolean" },
|
|
255
259
|
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
256
260
|
],
|
|
257
|
-
examples: ['treeseed save "feat: add search filters"', 'treeseed save --preview "feat: add search filters"', 'treeseed save --hotfix "fix: unblock production form submit"'],
|
|
261
|
+
examples: ['treeseed save "feat: add search filters"', 'treeseed save --preview "feat: add search filters"', 'treeseed save --plan "feat: add search filters"', 'treeseed save --hotfix "fix: unblock production form submit"'],
|
|
258
262
|
help: {
|
|
259
263
|
workflowPosition: "checkpoint work",
|
|
260
264
|
longSummary: [
|
|
@@ -294,8 +298,12 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
294
298
|
})],
|
|
295
299
|
["close", command({
|
|
296
300
|
arguments: [{ name: "message", description: "Reason for closing the task without staging it.", required: true, kind: "message_tail" }],
|
|
297
|
-
options: [
|
|
298
|
-
|
|
301
|
+
options: [
|
|
302
|
+
{ name: "plan", flags: "--plan", description: "Compute the recursive close plan without mutating any repo.", kind: "boolean" },
|
|
303
|
+
{ name: "dryRun", flags: "--dry-run", description: "Alias for --plan.", kind: "boolean" },
|
|
304
|
+
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
305
|
+
],
|
|
306
|
+
examples: ['treeseed close "superseded by feature/search-v2"', 'treeseed close --plan "superseded by feature/search-v2"'],
|
|
299
307
|
notes: ["Auto-saves meaningful uncommitted task-branch changes before cleanup unless disabled in the workflow API."],
|
|
300
308
|
help: {
|
|
301
309
|
workflowPosition: "abandon task",
|
|
@@ -328,8 +336,12 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
328
336
|
})],
|
|
329
337
|
["stage", command({
|
|
330
338
|
arguments: [{ name: "message", description: "Resolution message for the staged task.", required: true, kind: "message_tail" }],
|
|
331
|
-
options: [
|
|
332
|
-
|
|
339
|
+
options: [
|
|
340
|
+
{ name: "plan", flags: "--plan", description: "Compute the recursive staging plan without mutating any repo.", kind: "boolean" },
|
|
341
|
+
{ name: "dryRun", flags: "--dry-run", description: "Alias for --plan.", kind: "boolean" },
|
|
342
|
+
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
343
|
+
],
|
|
344
|
+
examples: ['treeseed stage "feat: add search filters"', 'treeseed stage --plan "feat: add search filters"'],
|
|
333
345
|
notes: ["Auto-saves meaningful uncommitted task-branch changes before merging into staging."],
|
|
334
346
|
help: {
|
|
335
347
|
workflowPosition: "merge to staging",
|
|
@@ -358,6 +370,56 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
358
370
|
executionMode: "handler",
|
|
359
371
|
handlerName: "stage"
|
|
360
372
|
})],
|
|
373
|
+
["resume", command({
|
|
374
|
+
arguments: [{ name: "run-id", description: "Interrupted workflow run id to resume.", required: true }],
|
|
375
|
+
options: [{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }],
|
|
376
|
+
examples: ["treeseed resume save-abcd12", "treeseed resume stage-ef3456 --json"],
|
|
377
|
+
help: {
|
|
378
|
+
workflowPosition: "recover",
|
|
379
|
+
longSummary: [
|
|
380
|
+
"Resume continues a previously interrupted journaled workflow run from its next incomplete step."
|
|
381
|
+
],
|
|
382
|
+
whenToUse: [
|
|
383
|
+
"Use this after `treeseed recover` or a workflow failure tells you a run is resumable."
|
|
384
|
+
],
|
|
385
|
+
beforeYouRun: [
|
|
386
|
+
"Confirm the run id from `treeseed recover` and repair any missing remotes, credentials, or package drift that caused the interruption."
|
|
387
|
+
],
|
|
388
|
+
outcomes: [
|
|
389
|
+
"Re-enters the original workflow command using its recorded input and journal."
|
|
390
|
+
],
|
|
391
|
+
automationNotes: [
|
|
392
|
+
"`resume --json` preserves the versioned workflow result envelope so agents can continue from a known run id without reparsing a different shape."
|
|
393
|
+
]
|
|
394
|
+
},
|
|
395
|
+
executionMode: "handler",
|
|
396
|
+
handlerName: "resume"
|
|
397
|
+
})],
|
|
398
|
+
["recover", command({
|
|
399
|
+
options: [{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }],
|
|
400
|
+
examples: ["treeseed recover", "treeseed recover --json"],
|
|
401
|
+
help: {
|
|
402
|
+
workflowPosition: "recover",
|
|
403
|
+
longSummary: [
|
|
404
|
+
"Recover lists the active workflow lock plus resumable interrupted runs so humans and agents can decide whether to resume, wait, or repair manually."
|
|
405
|
+
],
|
|
406
|
+
whenToUse: [
|
|
407
|
+
"Use this before starting a new mutating workflow when you suspect another run may already hold the workspace lock.",
|
|
408
|
+
"Use this after any interrupted recursive save, stage, close, release, or destroy command."
|
|
409
|
+
],
|
|
410
|
+
beforeYouRun: [
|
|
411
|
+
"Run it from the market workspace root or anywhere inside the tenant so the CLI can inspect the correct `.treeseed/workflow` journal directory."
|
|
412
|
+
],
|
|
413
|
+
outcomes: [
|
|
414
|
+
"Reports the active workflow lock, interrupted runs, and the exact `treeseed resume <run-id>` command for resumable runs."
|
|
415
|
+
],
|
|
416
|
+
automationNotes: [
|
|
417
|
+
"`recover --json` is the supported discovery entrypoint for agents that need to inspect lock state and resumable run ids safely before mutating the workspace."
|
|
418
|
+
]
|
|
419
|
+
},
|
|
420
|
+
executionMode: "handler",
|
|
421
|
+
handlerName: "recover"
|
|
422
|
+
})],
|
|
361
423
|
["rollback", command({
|
|
362
424
|
arguments: [{ name: "environment", description: "The persistent environment to roll back.", required: true }],
|
|
363
425
|
options: [
|
|
@@ -478,6 +540,83 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
478
540
|
executionMode: "handler",
|
|
479
541
|
handlerName: "auth:whoami"
|
|
480
542
|
})],
|
|
543
|
+
["secrets:status", command({
|
|
544
|
+
options: [{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }],
|
|
545
|
+
examples: ["treeseed secrets:status"],
|
|
546
|
+
help: {
|
|
547
|
+
longSummary: ["Secrets:status shows whether the local key agent is running, whether the wrapped machine key exists, and whether the in-memory secret session is currently unlocked."],
|
|
548
|
+
whenToUse: ["Use this before secret-backed local commands when you need to confirm whether the machine key is already unlocked."],
|
|
549
|
+
beforeYouRun: ["Decide whether you want the human-readable summary or `--json` for automation."],
|
|
550
|
+
automationNotes: ["This command is read-only and safe for agents to call before deciding whether an unlock step is required."]
|
|
551
|
+
},
|
|
552
|
+
executionMode: "handler",
|
|
553
|
+
handlerName: "secrets:status"
|
|
554
|
+
})],
|
|
555
|
+
["secrets:unlock", command({
|
|
556
|
+
options: [
|
|
557
|
+
{ name: "fromEnv", flags: "--from-env", description: "Unlock from TREESEED_KEY_PASSPHRASE instead of prompting.", kind: "boolean" },
|
|
558
|
+
{ name: "createIfMissing", flags: "--create-if-missing", description: "Create a wrapped machine key when one does not exist yet.", kind: "boolean" },
|
|
559
|
+
{ name: "allowMigration", flags: "--allow-migration", description: "Allow migration from the legacy plaintext machine-key file.", kind: "boolean" },
|
|
560
|
+
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
561
|
+
],
|
|
562
|
+
examples: ["treeseed secrets:unlock", "treeseed secrets:unlock --from-env"],
|
|
563
|
+
help: {
|
|
564
|
+
longSummary: ["Secrets:unlock starts or reuses the host-local key agent and unlocks the in-memory machine key from an interactive passphrase prompt or the TREESEED_KEY_PASSPHRASE startup env var."],
|
|
565
|
+
whenToUse: ["Use this before running local dev, config, deployment, or runner commands that need encrypted local secrets."],
|
|
566
|
+
beforeYouRun: ["Use a TTY for the interactive prompt path, or set TREESEED_KEY_PASSPHRASE before using `--from-env` in backend startup automation."],
|
|
567
|
+
automationNotes: ["Backend servers should use `--from-env` only during the explicit startup unlock step, not during arbitrary job execution."]
|
|
568
|
+
},
|
|
569
|
+
executionMode: "handler",
|
|
570
|
+
handlerName: "secrets:unlock"
|
|
571
|
+
})],
|
|
572
|
+
["secrets:lock", command({
|
|
573
|
+
options: [{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }],
|
|
574
|
+
examples: ["treeseed secrets:lock"],
|
|
575
|
+
help: {
|
|
576
|
+
longSummary: ["Secrets:lock clears the in-memory machine key from the host-local key agent."],
|
|
577
|
+
whenToUse: ["Use this when you want to end the current local secret session before the idle timeout expires."],
|
|
578
|
+
beforeYouRun: ["No additional setup is required."],
|
|
579
|
+
automationNotes: ["This command is safe to run in automation when a runner host should explicitly clear local secret access."]
|
|
580
|
+
},
|
|
581
|
+
executionMode: "handler",
|
|
582
|
+
handlerName: "secrets:lock"
|
|
583
|
+
})],
|
|
584
|
+
["secrets:migrate-key", command({
|
|
585
|
+
options: [{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }],
|
|
586
|
+
examples: ["treeseed secrets:migrate-key"],
|
|
587
|
+
help: {
|
|
588
|
+
longSummary: ["Secrets:migrate-key replaces the legacy plaintext machine-key file with the wrapped passphrase-protected format used by the Treeseed key agent."],
|
|
589
|
+
whenToUse: ["Use this when status or doctor reports that machine-key migration is still required."],
|
|
590
|
+
beforeYouRun: ["Run this in a TTY so you can create and confirm the new wrapping passphrase."],
|
|
591
|
+
automationNotes: ["Prefer this as a one-time operator action rather than an unattended automation step."]
|
|
592
|
+
},
|
|
593
|
+
executionMode: "handler",
|
|
594
|
+
handlerName: "secrets:migrate-key"
|
|
595
|
+
})],
|
|
596
|
+
["secrets:rotate-passphrase", command({
|
|
597
|
+
options: [{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }],
|
|
598
|
+
examples: ["treeseed secrets:rotate-passphrase"],
|
|
599
|
+
help: {
|
|
600
|
+
longSummary: ["Secrets:rotate-passphrase re-wraps the existing machine key with a newly entered passphrase without changing the underlying machine key."],
|
|
601
|
+
whenToUse: ["Use this when the local wrapping passphrase should be changed without re-encrypting the stored secret payloads."],
|
|
602
|
+
beforeYouRun: ["Unlock the current secret session first, then run this in a TTY so you can enter and confirm the new passphrase."],
|
|
603
|
+
automationNotes: ["Treat passphrase rotation as an operator-controlled maintenance action, not a normal background job."]
|
|
604
|
+
},
|
|
605
|
+
executionMode: "handler",
|
|
606
|
+
handlerName: "secrets:rotate-passphrase"
|
|
607
|
+
})],
|
|
608
|
+
["secrets:rotate-machine-key", command({
|
|
609
|
+
options: [{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }],
|
|
610
|
+
examples: ["treeseed secrets:rotate-machine-key"],
|
|
611
|
+
help: {
|
|
612
|
+
longSummary: ["Secrets:rotate-machine-key generates a new machine key, re-encrypts stored local secrets, and re-wraps the result with the configured passphrase."],
|
|
613
|
+
whenToUse: ["Use this when the underlying machine key itself must be rotated, such as after a local secret-hygiene event."],
|
|
614
|
+
beforeYouRun: ["Unlock the current secret session and make sure TREESEED_KEY_PASSPHRASE is set for the non-interactive re-wrap step."],
|
|
615
|
+
automationNotes: ["This command mutates local encrypted state and should be run intentionally rather than as part of routine startup automation."]
|
|
616
|
+
},
|
|
617
|
+
executionMode: "handler",
|
|
618
|
+
handlerName: "secrets:rotate-machine-key"
|
|
619
|
+
})],
|
|
481
620
|
["template", command({
|
|
482
621
|
usage: "treeseed template [list|show|validate] [id]",
|
|
483
622
|
arguments: [
|
|
@@ -560,15 +699,27 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
560
699
|
["config", command({
|
|
561
700
|
options: [
|
|
562
701
|
{ name: "full", flags: "--full", description: "Open the advanced full editor directly in human interactive mode.", kind: "boolean" },
|
|
702
|
+
{ name: "mouse", flags: "--mouse", description: "Opt into mouse capture for the config UI. Keyboard-first terminal behavior remains the default.", kind: "boolean" },
|
|
563
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"] },
|
|
564
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: "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
|
+
{ 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" },
|
|
565
707
|
{ name: "printEnv", flags: "--print-env", description: "Print resolved environment values before remote initialization.", kind: "boolean" },
|
|
566
708
|
{ name: "printEnvOnly", flags: "--print-env-only", description: "Print resolved environment values, check provider connections, and exit without prompting or initializing remote resources.", kind: "boolean" },
|
|
567
709
|
{ name: "showSecrets", flags: "--show-secrets", description: "Print full secret values in environment reports instead of masking them.", kind: "boolean" },
|
|
568
710
|
{ name: "rotateMachineKey", flags: "--rotate-machine-key", description: "Regenerate the local home machine key and re-encrypt stored Treeseed secrets and remote auth sessions.", kind: "boolean" },
|
|
711
|
+
{ name: "connectMarket", flags: "--connect-market", description: "Pair the current local repo to a Knowledge Coop project and register the hybrid runner connection.", kind: "boolean" },
|
|
712
|
+
{ name: "marketBaseUrl", flags: "--market-base-url <url>", description: "Knowledge Coop control-plane base URL for --connect-market. Defaults to the active remote host.", kind: "string" },
|
|
713
|
+
{ name: "marketTeamId", flags: "--market-team-id <id>", description: "Team ID to record in the local Knowledge Coop pairing metadata.", kind: "string" },
|
|
714
|
+
{ name: "marketTeamSlug", flags: "--market-team-slug <slug>", description: "Team slug to record in the local Knowledge Coop pairing metadata.", kind: "string" },
|
|
715
|
+
{ name: "marketProjectId", flags: "--market-project-id <id>", description: "Project ID to pair with when using --connect-market.", kind: "string" },
|
|
716
|
+
{ name: "marketProjectSlug", flags: "--market-project-slug <slug>", description: "Project slug to record in the local Knowledge Coop pairing metadata.", kind: "string" },
|
|
717
|
+
{ name: "marketProjectApiBaseUrl", flags: "--market-project-api-base-url <url>", description: "Override the project API base URL recorded on the Knowledge Coop project connection.", kind: "string" },
|
|
718
|
+
{ name: "marketAccessToken", flags: "--market-access-token <token>", description: "Explicit Knowledge Coop access token to use for pairing. Prefer an existing remote session when possible.", kind: "string" },
|
|
719
|
+
{ name: "rotateRunnerToken", flags: "--rotate-runner-token", description: "Rotate the project runner credential while pairing the local hybrid repo.", kind: "boolean" },
|
|
569
720
|
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
570
721
|
],
|
|
571
|
-
examples: ["treeseed config", "treeseed config --full", "treeseed config --environment all", "treeseed config --environment local --sync none", "treeseed config --environment staging --print-env-only --show-secrets", "treeseed config --rotate-machine-key"],
|
|
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"],
|
|
572
723
|
notes: ["Does not create branch preview deployments. Use `treeseed switch <branch> --preview` for that."],
|
|
573
724
|
help: {
|
|
574
725
|
workflowPosition: "configure runtime",
|
|
@@ -578,10 +729,10 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
578
729
|
],
|
|
579
730
|
whenToUse: [
|
|
580
731
|
"Use this during first-run setup, after new required environment variables are introduced, or when provider-backed configuration drift must be repaired.",
|
|
581
|
-
"Use the startup wizard for onboarding and the full editor when you need complete per-variable control."
|
|
732
|
+
"Use the startup wizard for onboarding and the full editor when you need complete per-variable control. Terminal-native copy, selection, and paste are the default interaction model."
|
|
582
733
|
],
|
|
583
734
|
beforeYouRun: [
|
|
584
|
-
"Decide whether you want human interactive mode or machine-readable `--json` output before invoking the command.",
|
|
735
|
+
"Decide whether you want human interactive mode, explicit `--non-interactive` application, or machine-readable `--json` output before invoking the command.",
|
|
585
736
|
"Choose the environment scope you care about: all, local, staging, or prod.",
|
|
586
737
|
"If you plan to sync hosted state, make sure GitHub, Cloudflare, and Railway authentication is already configured or be ready to log in first."
|
|
587
738
|
],
|
|
@@ -593,21 +744,29 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
593
744
|
examples: [
|
|
594
745
|
example("treeseed config", "Run the startup wizard", "Open the newcomer-friendly configuration wizard in human TTY mode."),
|
|
595
746
|
example("treeseed config --full", "Open the advanced editor directly", "Skip the startup wizard and go straight to the full configuration surface."),
|
|
747
|
+
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."),
|
|
596
748
|
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
|
+
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."),
|
|
597
750
|
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."),
|
|
598
|
-
example("treeseed config --rotate-machine-key", "Rotate the local secret encryption key", "Regenerate the machine key and re-encrypt locally stored Treeseed secrets.")
|
|
751
|
+
example("treeseed config --rotate-machine-key", "Rotate the local secret encryption key", "Regenerate the machine key and re-encrypt locally stored Treeseed secrets."),
|
|
752
|
+
example("treeseed config --connect-market --market-project-id kc_proj_123", "Pair a hybrid repo to Knowledge Coop", "Register the current local repo as the hybrid runner connection for an existing Knowledge Coop project.")
|
|
599
753
|
],
|
|
600
754
|
optionDetails: [
|
|
601
755
|
detail("--full", "Enter the advanced editor directly instead of the startup wizard."),
|
|
756
|
+
detail("--mouse", "Opt into terminal mouse capture for clicking, scrolling, and focus changes inside the config UI."),
|
|
602
757
|
detail("--environment <scope>", "Filter configuration to `all`, `local`, `staging`, or `prod`."),
|
|
603
758
|
detail("--sync <mode>", "Choose which provider surfaces should receive synchronized values after local updates are applied."),
|
|
759
|
+
detail("--non-interactive", "Apply resolved values without opening the interactive editor. Use this for automation when you do not want `--json` output."),
|
|
760
|
+
detail("--install-missing-tooling", "Allow config to install missing verification helpers such as the GitHub `gh-act` extension instead of only reporting them."),
|
|
604
761
|
detail("--print-env", "Print the resolved environment values before remote initialization continues."),
|
|
605
762
|
detail("--print-env-only", "Print the environment report and exit without interactive editing or remote initialization."),
|
|
606
|
-
detail("--rotate-machine-key", "Rotate the local machine key used for encrypted Treeseed secret storage.")
|
|
763
|
+
detail("--rotate-machine-key", "Rotate the local machine key used for encrypted Treeseed secret storage."),
|
|
764
|
+
detail("--connect-market", "Pair the current repo to a Knowledge Coop project and store the resulting market connection metadata locally.")
|
|
607
765
|
],
|
|
608
766
|
automationNotes: [
|
|
609
|
-
"Use `--json` for
|
|
610
|
-
"`--print-env-only
|
|
767
|
+
"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.",
|
|
769
|
+
"Config reports missing tooling by default. Use `--install-missing-tooling` when you want the command to attempt installation.",
|
|
611
770
|
"Shared versus scoped environment semantics are resolved inside the SDK; the CLI help should be treated as the operator-facing explanation layer."
|
|
612
771
|
],
|
|
613
772
|
warnings: [
|
|
@@ -668,9 +827,11 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
668
827
|
{ name: "major", flags: "--major", description: "Bump to the next major version.", kind: "boolean" },
|
|
669
828
|
{ name: "minor", flags: "--minor", description: "Bump to the next minor version.", kind: "boolean" },
|
|
670
829
|
{ name: "patch", flags: "--patch", description: "Bump to the next patch version.", kind: "boolean" },
|
|
830
|
+
{ name: "plan", flags: "--plan", description: "Compute the recursive release plan without mutating any repo.", kind: "boolean" },
|
|
831
|
+
{ name: "dryRun", flags: "--dry-run", description: "Alias for --plan.", kind: "boolean" },
|
|
671
832
|
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
672
833
|
],
|
|
673
|
-
examples: ["treeseed release --patch", "treeseed release --minor"],
|
|
834
|
+
examples: ["treeseed release --patch", "treeseed release --minor", "treeseed release --patch --plan"],
|
|
674
835
|
notes: ["Requires exactly one bump flag."],
|
|
675
836
|
help: {
|
|
676
837
|
workflowPosition: "promote to production",
|
|
@@ -707,17 +868,18 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
707
868
|
handlerName: "release"
|
|
708
869
|
})],
|
|
709
870
|
["destroy", command({
|
|
710
|
-
usage: "treeseed destroy --environment <local|staging|prod> [--dry-run] [--force] [--skip-confirmation] [--confirm <slug>] [--remove-build-artifacts]",
|
|
871
|
+
usage: "treeseed destroy --environment <local|staging|prod> [--plan|--dry-run] [--force] [--skip-confirmation] [--confirm <slug>] [--remove-build-artifacts]",
|
|
711
872
|
options: [
|
|
712
873
|
{ name: "environment", flags: "--environment <scope>", description: "Select the persistent environment to destroy.", kind: "enum", values: ["local", "staging", "prod"] },
|
|
713
|
-
{ name: "
|
|
874
|
+
{ name: "plan", flags: "--plan", description: "Compute the destroy plan without mutating the environment.", kind: "boolean" },
|
|
875
|
+
{ name: "dryRun", flags: "--dry-run", description: "Alias for --plan.", kind: "boolean" },
|
|
714
876
|
{ name: "force", flags: "--force", description: "Force worker deletion when supported.", kind: "boolean" },
|
|
715
877
|
{ name: "skipConfirmation", flags: "--skip-confirmation", description: "Skip the interactive confirmation prompt.", kind: "boolean" },
|
|
716
878
|
{ name: "confirm", flags: "--confirm <slug>", description: "Provide the expected slug confirmation non-interactively.", kind: "string" },
|
|
717
879
|
{ name: "removeBuildArtifacts", flags: "--remove-build-artifacts", description: "Also remove local build artifacts after destroy.", kind: "boolean" },
|
|
718
880
|
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
719
881
|
],
|
|
720
|
-
examples: ["treeseed destroy --environment staging --
|
|
882
|
+
examples: ["treeseed destroy --environment staging --plan", "treeseed destroy --environment prod --confirm example --skip-confirmation"],
|
|
721
883
|
notes: ["Only for persistent environments. Task cleanup belongs to treeseed close.", "This command is destructive and requires explicit confirmation."],
|
|
722
884
|
help: {
|
|
723
885
|
workflowPosition: "tear down environment",
|
|
@@ -726,7 +888,7 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
726
888
|
],
|
|
727
889
|
whenToUse: [
|
|
728
890
|
"Use this when a persistent environment should be intentionally removed rather than rolled back or updated.",
|
|
729
|
-
"Use `--
|
|
891
|
+
"Use `--plan` first when you want to inspect the destroy plan without committing to it."
|
|
730
892
|
],
|
|
731
893
|
beforeYouRun: [
|
|
732
894
|
"Confirm the environment scope exactly. This command does not target task-branch cleanup; it targets persistent environments only.",
|
|
@@ -737,7 +899,7 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
737
899
|
"Optionally removes local build artifacts if requested."
|
|
738
900
|
],
|
|
739
901
|
examples: [
|
|
740
|
-
example("treeseed destroy --environment staging --
|
|
902
|
+
example("treeseed destroy --environment staging --plan", "Preview the destroy plan", "Inspect what would be removed from staging without actually performing the destroy."),
|
|
741
903
|
example("treeseed destroy --environment prod --confirm example --skip-confirmation", "Run a deliberate non-interactive destroy", "Provide the expected slug explicitly when operating in a scripted or no-prompt environment."),
|
|
742
904
|
example("treeseed destroy --environment local --remove-build-artifacts", "Remove a local environment and its artifacts", "Destroy the local environment and also delete local build outputs.")
|
|
743
905
|
],
|
|
@@ -785,7 +947,25 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
785
947
|
["lint", command({ examples: ["treeseed lint"], help: { longSummary: ["Lint runs the project linting and related surface checks for the current tenant."], examples: [example("treeseed lint", "Run lint", "Execute the lint checks for the current project."), example("trsd lint", "Use the short alias", "Run the same lint checks through the shorter entrypoint."), example("treeseed lint && treeseed test:unit", "Lint before unit tests", "Use lint as a first local verification step.")] }, executionMode: "adapter" })],
|
|
786
948
|
["test", command({ examples: ["treeseed test"], help: { longSummary: ["Test runs the default Treeseed test surface for the current project."], examples: [example("treeseed test", "Run the default test suite", "Execute the standard project test flow."), example("trsd test", "Use the short alias", "Run the same test surface with the shorter entrypoint."), example("treeseed test && treeseed build", "Verify before building", "Run tests before the build step in a local verification loop.")] }, executionMode: "adapter" })],
|
|
787
949
|
["test:unit", command({ examples: ["treeseed test:unit"], help: { longSummary: ["Test:unit runs workspace unit tests in dependency order."], examples: [example("treeseed test:unit", "Run unit tests", "Execute the package unit test flow."), example("trsd test:unit", "Use the short alias", "Run the same unit tests via the short entrypoint."), example("treeseed test:unit && treeseed check", "Unit tests then validation", "Combine focused tests with broader tenant validation.")] }, executionMode: "adapter" })],
|
|
788
|
-
["preflight", command({
|
|
950
|
+
["preflight", command({
|
|
951
|
+
options: [
|
|
952
|
+
{ name: "launch", flags: "--launch", description: "Validate managed Knowledge Coop launch prerequisites, provider auth, and required live configuration.", kind: "boolean" }
|
|
953
|
+
],
|
|
954
|
+
examples: ["treeseed preflight", "treeseed preflight --launch"],
|
|
955
|
+
help: {
|
|
956
|
+
longSummary: ["Preflight checks local prerequisites and authentication state before heavier workflows run."],
|
|
957
|
+
examples: [
|
|
958
|
+
example("treeseed preflight", "Run the preflight checklist", "Inspect local prerequisites and auth readiness."),
|
|
959
|
+
example("treeseed preflight --launch", "Validate live launch readiness", "Check managed Knowledge Coop launch prerequisites before creating live GitHub, Cloudflare, and Railway resources."),
|
|
960
|
+
example("trsd preflight", "Use the short alias", "Run the same readiness check via the short entrypoint."),
|
|
961
|
+
example("treeseed preflight && treeseed dev", "Validate before starting local runtime", "Confirm readiness before launching the integrated dev surface.")
|
|
962
|
+
]
|
|
963
|
+
},
|
|
964
|
+
executionMode: "adapter",
|
|
965
|
+
buildAdapterInput: (invocation) => ({
|
|
966
|
+
launch: invocation.args.launch === true
|
|
967
|
+
})
|
|
968
|
+
})],
|
|
789
969
|
["auth:check", command({ examples: ["treeseed auth:check"], executionMode: "adapter", buildAdapterInput: () => ({ requireAuth: true }) })],
|
|
790
970
|
["test:e2e", command({ examples: ["treeseed test:e2e"], executionMode: "adapter" })],
|
|
791
971
|
["test:e2e:local", command({ examples: ["treeseed test:e2e:local"], executionMode: "adapter" })],
|
|
@@ -795,7 +975,6 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
795
975
|
["test:release:full", command({ examples: ["treeseed test:release:full", "treeseed release:verify"], executionMode: "adapter" })],
|
|
796
976
|
["release:publish:changed", command({ examples: ["treeseed release:publish:changed"], executionMode: "adapter" })],
|
|
797
977
|
["astro", command({ examples: ["treeseed astro -- --help"], executionMode: "adapter", buildAdapterInput: PASS_THROUGH_ARGS })],
|
|
798
|
-
["sync:devvars", command({ examples: ["treeseed sync:devvars"], executionMode: "adapter" })],
|
|
799
978
|
["mailpit:up", command({ examples: ["treeseed mailpit:up"], executionMode: "adapter" })],
|
|
800
979
|
["mailpit:down", command({ examples: ["treeseed mailpit:down"], executionMode: "adapter" })],
|
|
801
980
|
["mailpit:logs", command({ examples: ["treeseed mailpit:logs"], executionMode: "adapter" })],
|
package/dist/cli/registry.d.ts
CHANGED
|
@@ -16,10 +16,18 @@ export declare const COMMAND_HANDLERS: {
|
|
|
16
16
|
readonly tasks: import("./operations-types.js").TreeseedCommandHandler;
|
|
17
17
|
readonly switch: import("./operations-types.js").TreeseedCommandHandler;
|
|
18
18
|
readonly stage: import("./operations-types.js").TreeseedCommandHandler;
|
|
19
|
+
readonly resume: import("./operations-types.js").TreeseedCommandHandler;
|
|
20
|
+
readonly recover: import("./operations-types.js").TreeseedCommandHandler;
|
|
19
21
|
readonly export: import("./operations-types.js").TreeseedCommandHandler;
|
|
20
22
|
readonly 'auth:login': import("./operations-types.js").TreeseedCommandHandler;
|
|
21
23
|
readonly 'auth:logout': import("./operations-types.js").TreeseedCommandHandler;
|
|
22
24
|
readonly 'auth:whoami': import("./operations-types.js").TreeseedCommandHandler;
|
|
25
|
+
readonly 'secrets:status': import("./operations-types.js").TreeseedCommandHandler;
|
|
26
|
+
readonly 'secrets:unlock': import("./operations-types.js").TreeseedCommandHandler;
|
|
27
|
+
readonly 'secrets:lock': import("./operations-types.js").TreeseedCommandHandler;
|
|
28
|
+
readonly 'secrets:migrate-key': import("./operations-types.js").TreeseedCommandHandler;
|
|
29
|
+
readonly 'secrets:rotate-passphrase': import("./operations-types.js").TreeseedCommandHandler;
|
|
30
|
+
readonly 'secrets:rotate-machine-key': import("./operations-types.js").TreeseedCommandHandler;
|
|
23
31
|
};
|
|
24
32
|
export declare const TRESEED_COMMAND_SPECS: TreeseedCommandSpec[];
|
|
25
33
|
export declare function findCommandSpec(name: string | null | undefined): import("./operations-types.js").TreeseedOperationSpec | null;
|
package/dist/cli/registry.js
CHANGED
|
@@ -18,10 +18,20 @@ import { handleSync } from "./handlers/sync.js";
|
|
|
18
18
|
import { handleAuthLogin } from "./handlers/auth-login.js";
|
|
19
19
|
import { handleAuthLogout } from "./handlers/auth-logout.js";
|
|
20
20
|
import { handleAuthWhoAmI } from "./handlers/auth-whoami.js";
|
|
21
|
+
import {
|
|
22
|
+
handleSecretsLock,
|
|
23
|
+
handleSecretsMigrateKey,
|
|
24
|
+
handleSecretsRotateMachineKey,
|
|
25
|
+
handleSecretsRotatePassphrase,
|
|
26
|
+
handleSecretsStatus,
|
|
27
|
+
handleSecretsUnlock
|
|
28
|
+
} from "./handlers/secrets.js";
|
|
21
29
|
import { handleTasks } from "./handlers/tasks.js";
|
|
22
30
|
import { handleSwitch } from "./handlers/switch.js";
|
|
23
31
|
import { handleStage } from "./handlers/stage.js";
|
|
24
32
|
import { handleExport } from "./handlers/export.js";
|
|
33
|
+
import { handleResume } from "./handlers/resume.js";
|
|
34
|
+
import { handleRecover } from "./handlers/recover.js";
|
|
25
35
|
const COMMAND_HANDLERS = {
|
|
26
36
|
init: handleInit,
|
|
27
37
|
config: handleConfig,
|
|
@@ -39,10 +49,18 @@ const COMMAND_HANDLERS = {
|
|
|
39
49
|
tasks: handleTasks,
|
|
40
50
|
switch: handleSwitch,
|
|
41
51
|
stage: handleStage,
|
|
52
|
+
resume: handleResume,
|
|
53
|
+
recover: handleRecover,
|
|
42
54
|
export: handleExport,
|
|
43
55
|
"auth:login": handleAuthLogin,
|
|
44
56
|
"auth:logout": handleAuthLogout,
|
|
45
|
-
"auth:whoami": handleAuthWhoAmI
|
|
57
|
+
"auth:whoami": handleAuthWhoAmI,
|
|
58
|
+
"secrets:status": handleSecretsStatus,
|
|
59
|
+
"secrets:unlock": handleSecretsUnlock,
|
|
60
|
+
"secrets:lock": handleSecretsLock,
|
|
61
|
+
"secrets:migrate-key": handleSecretsMigrateKey,
|
|
62
|
+
"secrets:rotate-passphrase": handleSecretsRotatePassphrase,
|
|
63
|
+
"secrets:rotate-machine-key": handleSecretsRotateMachineKey
|
|
46
64
|
};
|
|
47
65
|
const TRESEED_COMMAND_SPECS = TRESEED_OPERATION_SPECS;
|
|
48
66
|
function findCommandSpec(name) {
|
package/dist/cli/repair.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
createDefaultTreeseedMachineConfig,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
loadDeployState,
|
|
9
9
|
loadTreeseedMachineConfig,
|
|
10
10
|
resolveTreeseedMachineEnvironmentValues,
|
|
11
|
-
|
|
11
|
+
warnDeprecatedTreeseedLocalEnvFiles,
|
|
12
12
|
writeTreeseedMachineConfig,
|
|
13
13
|
createPersistentDeployTarget,
|
|
14
14
|
ensureGeneratedWranglerConfig
|
|
@@ -17,11 +17,9 @@ function applyTreeseedSafeRepairs(tenantRoot) {
|
|
|
17
17
|
const actions = [];
|
|
18
18
|
ensureTreeseedGitignoreEntries(tenantRoot);
|
|
19
19
|
actions.push({ id: "gitignore", detail: "Ensured Treeseed gitignore entries are present." });
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
copyFileSync(envLocalExamplePath, envLocalPath);
|
|
24
|
-
actions.push({ id: "env-local", detail: "Created .env.local from .env.local.example." });
|
|
20
|
+
const deprecatedFiles = warnDeprecatedTreeseedLocalEnvFiles(tenantRoot);
|
|
21
|
+
if (deprecatedFiles.length > 0) {
|
|
22
|
+
actions.push({ id: "deprecated-local-env", detail: "Detected deprecated .env.local/.dev.vars files that Treeseed now ignores." });
|
|
25
23
|
}
|
|
26
24
|
const deployConfig = loadCliDeployConfig(tenantRoot);
|
|
27
25
|
const { configPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
@@ -38,8 +36,6 @@ function applyTreeseedSafeRepairs(tenantRoot) {
|
|
|
38
36
|
actions.push({ id: "machine-key", detail: "Ensured the Treeseed machine key exists." });
|
|
39
37
|
const machineConfig = loadTreeseedMachineConfig(tenantRoot);
|
|
40
38
|
writeTreeseedMachineConfig(tenantRoot, machineConfig);
|
|
41
|
-
writeTreeseedLocalEnvironmentFiles(tenantRoot);
|
|
42
|
-
actions.push({ id: "local-env", detail: "Regenerated .env.local and .dev.vars from the current machine config." });
|
|
43
39
|
const stateRoot = resolve(tenantRoot, ".treeseed", "state", "environments");
|
|
44
40
|
if (existsSync(stateRoot)) {
|
|
45
41
|
for (const scope of ["local", "staging", "prod"]) {
|
|
@@ -53,6 +53,7 @@ export declare function scrollOffsetByDelta(state: ScrollRegionState, delta: num
|
|
|
53
53
|
export declare function scrollOffsetByPage(state: ScrollRegionState, pages: number): number;
|
|
54
54
|
export declare function ensureVisible(index: number, offset: number, viewportSize: number): number;
|
|
55
55
|
export declare function truncateLine(value: string, width: number): string;
|
|
56
|
+
export declare function formatSecretMaskedValue(value: string): string;
|
|
56
57
|
export declare function wrapText(value: string, width: number): string[];
|
|
57
58
|
export declare function containsPoint(rect: UiRect, x: number, y: number): boolean;
|
|
58
59
|
export declare function findClickableRegion(regions: UiClickRegion[], x: number, y: number): UiClickRegion | null;
|
|
@@ -120,6 +121,7 @@ type TextInputFieldProps = {
|
|
|
120
121
|
secret?: boolean;
|
|
121
122
|
placeholder?: string;
|
|
122
123
|
cursorPosition?: number;
|
|
124
|
+
helperText?: string;
|
|
123
125
|
};
|
|
124
126
|
export declare function TextInputField(props: TextInputFieldProps): any;
|
|
125
127
|
type TextAreaFieldProps = {
|
package/dist/cli/ui/framework.js
CHANGED
|
@@ -48,6 +48,25 @@ function truncateLine(value, width) {
|
|
|
48
48
|
}
|
|
49
49
|
return `${value.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
50
50
|
}
|
|
51
|
+
function formatSecretMaskedValue(value) {
|
|
52
|
+
if (!value) {
|
|
53
|
+
return "(unset)";
|
|
54
|
+
}
|
|
55
|
+
const normalized = value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "").replace(/\n+/g, " ").trim();
|
|
56
|
+
if (!normalized) {
|
|
57
|
+
return "(unset)";
|
|
58
|
+
}
|
|
59
|
+
if (normalized.length === 1) {
|
|
60
|
+
return `${normalized} [1]`;
|
|
61
|
+
}
|
|
62
|
+
const maxRevealedTotal = Math.max(2, Math.floor(normalized.length * 0.25));
|
|
63
|
+
const revealPerSide = Math.min(3, Math.max(1, Math.floor(maxRevealedTotal / 2)));
|
|
64
|
+
const leading = normalized.slice(0, revealPerSide);
|
|
65
|
+
const trailing = normalized.slice(Math.max(revealPerSide, normalized.length - revealPerSide));
|
|
66
|
+
const maskedCount = Math.max(0, normalized.length - leading.length - trailing.length);
|
|
67
|
+
const maskedMiddle = "*".repeat(maskedCount);
|
|
68
|
+
return `${leading}${maskedMiddle}${trailing} [${normalized.length}]`;
|
|
69
|
+
}
|
|
51
70
|
function wrapText(value, width) {
|
|
52
71
|
if (width <= 0) {
|
|
53
72
|
return [""];
|
|
@@ -186,29 +205,40 @@ function FieldCard(props) {
|
|
|
186
205
|
))
|
|
187
206
|
);
|
|
188
207
|
}
|
|
189
|
-
function
|
|
190
|
-
const height = props.height ?? 4;
|
|
208
|
+
function renderTextInputContent(props) {
|
|
191
209
|
const safeCursor = Math.max(0, Math.min(props.cursorPosition ?? props.value.length, props.value.length));
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const beforeCursor = visibleValue.slice(0, safeCursor);
|
|
197
|
-
const afterCursor = visibleValue.slice(safeCursor);
|
|
198
|
-
inputLine = `${beforeCursor}\u2588${afterCursor}`;
|
|
199
|
-
} else if (!inputLine) {
|
|
200
|
-
inputLine = placeholder;
|
|
210
|
+
const placeholder = props.placeholder ?? "";
|
|
211
|
+
const contentWidth = Math.max(1, props.width - 2);
|
|
212
|
+
if (!props.focused && !props.value) {
|
|
213
|
+
return React.createElement(Text, { color: "gray" }, truncateLine(placeholder || " ".repeat(contentWidth), contentWidth));
|
|
201
214
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
215
|
+
const visibleValue = props.secret && props.value.length > 0 ? formatSecretMaskedValue(props.value) : props.value;
|
|
216
|
+
if (!props.focused) {
|
|
217
|
+
return React.createElement(Text, null, truncateLine(visibleValue || placeholder, contentWidth));
|
|
218
|
+
}
|
|
219
|
+
const preservedPrefix = visibleValue.slice(0, safeCursor);
|
|
220
|
+
const visiblePrefix = preservedPrefix.slice(Math.max(0, preservedPrefix.length - Math.max(0, contentWidth - 1)));
|
|
221
|
+
const cursorCell = visibleValue[safeCursor] ?? " ";
|
|
222
|
+
const padding = " ".repeat(Math.max(0, contentWidth - visiblePrefix.length - 1));
|
|
223
|
+
return React.createElement(
|
|
224
|
+
Text,
|
|
225
|
+
null,
|
|
226
|
+
visiblePrefix,
|
|
227
|
+
React.createElement(Text, { color: "black", backgroundColor: "cyan" }, cursorCell),
|
|
228
|
+
padding
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
function TextInputField(props) {
|
|
232
|
+
const height = props.height ?? 4;
|
|
233
|
+
const placeholder = props.placeholder ?? "";
|
|
234
|
+
const helperText = props.helperText ?? (props.value.length > 0 ? props.secret ? "Secret value captured. Paste or type a replacement, or leave this as-is." : "Type a replacement value or leave this as-is." : placeholder);
|
|
235
|
+
return React.createElement(
|
|
236
|
+
Box,
|
|
237
|
+
{ flexDirection: "column", width: props.width, height, borderStyle: "round", borderColor: props.focused ? "cyan" : "blue", overflow: "hidden" },
|
|
238
|
+
React.createElement(Text, { color: "blue", bold: true }, truncateLine(props.label, props.width - 2)),
|
|
239
|
+
renderTextInputContent(props),
|
|
240
|
+
React.createElement(Text, { color: "gray" }, truncateLine(helperText || " ", props.width - 2))
|
|
241
|
+
);
|
|
212
242
|
}
|
|
213
243
|
function TextAreaField(props) {
|
|
214
244
|
return React.createElement(FieldCard, {
|
|
@@ -216,7 +246,7 @@ function TextAreaField(props) {
|
|
|
216
246
|
height: props.height,
|
|
217
247
|
title: props.label,
|
|
218
248
|
focused: props.focused,
|
|
219
|
-
lines: wrapText(props.value || "
|
|
249
|
+
lines: wrapText(props.value || "Value is unset.", Math.max(1, props.width - 2))
|
|
220
250
|
});
|
|
221
251
|
}
|
|
222
252
|
function ActionButton(props) {
|
|
@@ -286,6 +316,7 @@ export {
|
|
|
286
316
|
ensureVisible,
|
|
287
317
|
findClickableRegion,
|
|
288
318
|
findScrollRegion,
|
|
319
|
+
formatSecretMaskedValue,
|
|
289
320
|
popNavigationEntry,
|
|
290
321
|
pushNavigationEntry,
|
|
291
322
|
routeWheelDeltaToScrollRegion,
|
package/dist/cli/ui/mouse.d.ts
CHANGED
|
@@ -8,4 +8,6 @@ export type TerminalMouseEvent = {
|
|
|
8
8
|
ctrl: boolean;
|
|
9
9
|
};
|
|
10
10
|
export declare function parseTerminalMouseInput(input: string): TerminalMouseEvent[];
|
|
11
|
-
export declare function useTerminalMouse(onEvent: (event: TerminalMouseEvent) => void
|
|
11
|
+
export declare function useTerminalMouse(onEvent: (event: TerminalMouseEvent) => void, options?: {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
}): void;
|