@treeseed/cli 0.4.7 → 0.4.9
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 +10 -2
- package/dist/cli/handlers/close.js +3 -1
- package/dist/cli/handlers/config-ui.d.ts +72 -0
- package/dist/cli/handlers/config-ui.js +785 -0
- package/dist/cli/handlers/config.js +105 -35
- package/dist/cli/handlers/export.d.ts +2 -0
- package/dist/cli/handlers/export.js +28 -0
- package/dist/cli/handlers/release.js +3 -1
- package/dist/cli/handlers/save.js +5 -3
- package/dist/cli/handlers/stage.js +3 -1
- package/dist/cli/help-ui.d.ts +3 -0
- package/dist/cli/help-ui.js +490 -0
- package/dist/cli/help.d.ts +26 -0
- package/dist/cli/help.js +332 -91
- package/dist/cli/operations-registry.js +682 -29
- package/dist/cli/operations-types.d.ts +37 -2
- package/dist/cli/registry.d.ts +1 -0
- package/dist/cli/registry.js +2 -0
- package/dist/cli/runtime.js +22 -9
- package/dist/cli/ui/framework.d.ts +159 -0
- package/dist/cli/ui/framework.js +296 -0
- package/dist/cli/ui/mouse.d.ts +11 -0
- package/dist/cli/ui/mouse.js +72 -0
- package/package.json +7 -4
|
@@ -1,54 +1,124 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
applyTreeseedSafeRepairs,
|
|
3
|
+
collectTreeseedConfigContext,
|
|
4
|
+
findNearestTreeseedRoot
|
|
5
|
+
} from "@treeseed/sdk/workflow-support";
|
|
6
|
+
import { fail, guidedResult } from "./utils.js";
|
|
7
|
+
import { buildCliConfigPages, runCliConfigEditor } from "./config-ui.js";
|
|
4
8
|
import { createWorkflowSdk, renderWorkflowNextSteps, workflowErrorResult } from "./workflow.js";
|
|
9
|
+
function normalizeConfigScopes(value) {
|
|
10
|
+
const requested = Array.isArray(value) ? value.map(String) : typeof value === "string" ? [value] : ["all"];
|
|
11
|
+
if (requested.includes("all")) {
|
|
12
|
+
return ["local", "staging", "prod"];
|
|
13
|
+
}
|
|
14
|
+
return ["local", "staging", "prod"].filter((scope) => requested.includes(scope));
|
|
15
|
+
}
|
|
16
|
+
function formatPrintEnvReports(payload) {
|
|
17
|
+
const lines = [];
|
|
18
|
+
for (const report of payload.reports ?? []) {
|
|
19
|
+
lines.push(`Resolved environment values for ${report.scope}`);
|
|
20
|
+
lines.push(payload.secretsRevealed ? "Secrets are shown." : "Secret values are masked.");
|
|
21
|
+
for (const entry of report.environment?.entries ?? []) {
|
|
22
|
+
lines.push(`${entry.id}=${entry.displayValue} (${entry.source})`);
|
|
23
|
+
}
|
|
24
|
+
lines.push("");
|
|
25
|
+
lines.push(`Provider connection checks for ${report.scope}`);
|
|
26
|
+
for (const check of report.provider?.checks ?? []) {
|
|
27
|
+
const status = check.ready ? "ready" : check.skipped ? "skipped" : "failed";
|
|
28
|
+
lines.push(`${check.provider}: ${status} - ${check.detail}`);
|
|
29
|
+
}
|
|
30
|
+
lines.push("");
|
|
31
|
+
}
|
|
32
|
+
return lines.filter((line, index, all) => !(line === "" && all[index - 1] === ""));
|
|
33
|
+
}
|
|
34
|
+
function renderConfigResult(commandName, result) {
|
|
35
|
+
const payload = result.payload;
|
|
36
|
+
const toolHealth = payload.toolHealth;
|
|
37
|
+
const summary = payload.mode === "print-env-only" ? "Treeseed config environment report completed." : payload.mode === "rotate-machine-key" ? "Treeseed machine key rotated successfully." : "Treeseed config completed successfully.";
|
|
38
|
+
return guidedResult({
|
|
39
|
+
command: commandName,
|
|
40
|
+
summary,
|
|
41
|
+
facts: [
|
|
42
|
+
{ label: "Mode", value: payload.mode },
|
|
43
|
+
{ label: "Scopes", value: Array.isArray(payload.scopes) ? payload.scopes.join(", ") : "(none)" },
|
|
44
|
+
{ label: "Sync", value: payload.sync ?? "all" },
|
|
45
|
+
{ label: "Safe repairs", value: Array.isArray(payload.repairs) ? payload.repairs.length : 0 },
|
|
46
|
+
{ label: "Machine config", value: payload.configPath },
|
|
47
|
+
{ label: "Machine key", value: payload.keyPath },
|
|
48
|
+
{ label: "GitHub CLI", value: toolHealth?.githubCli?.available ? "ready" : "missing" },
|
|
49
|
+
{ label: "gh act", value: toolHealth?.ghActExtension?.available ? "ready" : "missing" },
|
|
50
|
+
{ label: "Docker", value: toolHealth?.dockerDaemon?.available ? "ready" : "missing" },
|
|
51
|
+
{ label: "ACT verify", value: toolHealth?.actVerificationReady ? "ready" : "not ready" }
|
|
52
|
+
],
|
|
53
|
+
nextSteps: renderWorkflowNextSteps(result),
|
|
54
|
+
report: payload
|
|
55
|
+
});
|
|
56
|
+
}
|
|
5
57
|
const handleConfig = async (invocation, context) => {
|
|
6
|
-
const rl = readline.createInterface({ input, output });
|
|
7
58
|
try {
|
|
8
59
|
const workflow = createWorkflowSdk(context, {
|
|
9
60
|
write: context.outputFormat === "json" ? (() => {
|
|
10
|
-
}) : context.write
|
|
11
|
-
prompt: async (message) => {
|
|
12
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
13
|
-
return "";
|
|
14
|
-
}
|
|
15
|
-
return rl.question(message);
|
|
16
|
-
}
|
|
61
|
+
}) : context.write
|
|
17
62
|
});
|
|
63
|
+
const scopes = normalizeConfigScopes(invocation.args.environment);
|
|
64
|
+
const sync = invocation.args.sync;
|
|
65
|
+
const interactive = context.outputFormat !== "json" && process.stdin.isTTY && process.stdout.isTTY;
|
|
66
|
+
if (interactive && invocation.args.printEnvOnly !== true && invocation.args.rotateMachineKey !== true) {
|
|
67
|
+
const tenantRoot = findNearestTreeseedRoot(context.cwd) ?? context.cwd;
|
|
68
|
+
if (!tenantRoot) {
|
|
69
|
+
return fail("Treeseed config requires a Treeseed project. Run the command from inside a tenant or initialize one first.");
|
|
70
|
+
}
|
|
71
|
+
applyTreeseedSafeRepairs(tenantRoot);
|
|
72
|
+
const configContext = collectTreeseedConfigContext({
|
|
73
|
+
tenantRoot,
|
|
74
|
+
scopes,
|
|
75
|
+
env: context.env
|
|
76
|
+
});
|
|
77
|
+
const editorResult = await runCliConfigEditor(configContext, {
|
|
78
|
+
initialViewMode: invocation.args.full === true ? "full" : "startup"
|
|
79
|
+
});
|
|
80
|
+
if (editorResult === null) {
|
|
81
|
+
return fail("Treeseed config canceled.");
|
|
82
|
+
}
|
|
83
|
+
const updates = buildCliConfigPages(configContext, "all", editorResult.overrides, "full").map((page) => ({
|
|
84
|
+
scope: page.scope,
|
|
85
|
+
entryId: page.entry.id,
|
|
86
|
+
value: page.finalValue,
|
|
87
|
+
reused: !(page.key in editorResult.overrides)
|
|
88
|
+
}));
|
|
89
|
+
const result2 = await workflow.config({
|
|
90
|
+
environment: scopes,
|
|
91
|
+
sync,
|
|
92
|
+
printEnv: invocation.args.printEnv === true,
|
|
93
|
+
showSecrets: invocation.args.showSecrets === true,
|
|
94
|
+
nonInteractive: true,
|
|
95
|
+
updates
|
|
96
|
+
});
|
|
97
|
+
return renderConfigResult(invocation.commandName || "config", result2);
|
|
98
|
+
}
|
|
18
99
|
const result = await workflow.config({
|
|
19
100
|
environment: invocation.args.environment,
|
|
20
|
-
sync
|
|
101
|
+
sync,
|
|
21
102
|
printEnv: invocation.args.printEnv === true,
|
|
22
103
|
printEnvOnly: invocation.args.printEnvOnly === true,
|
|
23
104
|
showSecrets: invocation.args.showSecrets === true,
|
|
24
105
|
rotateMachineKey: invocation.args.rotateMachineKey === true,
|
|
25
106
|
nonInteractive: context.outputFormat === "json"
|
|
26
107
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{ label: "Machine key", value: payload.keyPath },
|
|
40
|
-
{ label: "GitHub CLI", value: toolHealth?.githubCli?.available ? "ready" : "missing" },
|
|
41
|
-
{ label: "gh act", value: toolHealth?.ghActExtension?.available ? "ready" : "missing" },
|
|
42
|
-
{ label: "Docker", value: toolHealth?.dockerDaemon?.available ? "ready" : "missing" },
|
|
43
|
-
{ label: "ACT verify", value: toolHealth?.actVerificationReady ? "ready" : "not ready" }
|
|
44
|
-
],
|
|
45
|
-
nextSteps: renderWorkflowNextSteps(result),
|
|
46
|
-
report: payload
|
|
47
|
-
});
|
|
108
|
+
if (context.outputFormat !== "json" && result.payload.mode === "print-env-only") {
|
|
109
|
+
return {
|
|
110
|
+
exitCode: 0,
|
|
111
|
+
stdout: formatPrintEnvReports(result.payload),
|
|
112
|
+
report: {
|
|
113
|
+
command: invocation.commandName || "config",
|
|
114
|
+
ok: true,
|
|
115
|
+
...result.payload
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return renderConfigResult(invocation.commandName || "config", result);
|
|
48
120
|
} catch (error) {
|
|
49
121
|
return workflowErrorResult(error);
|
|
50
|
-
} finally {
|
|
51
|
-
rl.close();
|
|
52
122
|
}
|
|
53
123
|
};
|
|
54
124
|
export {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { guidedResult } from "./utils.js";
|
|
2
|
+
import { createWorkflowSdk, workflowErrorResult } from "./workflow.js";
|
|
3
|
+
const handleExport = async (invocation, context) => {
|
|
4
|
+
try {
|
|
5
|
+
const directory = typeof invocation.positionals[0] === "string" && invocation.positionals[0].trim().length > 0 ? invocation.positionals[0] : void 0;
|
|
6
|
+
const result = await createWorkflowSdk(context).export({ directory });
|
|
7
|
+
const exported = result.payload;
|
|
8
|
+
return guidedResult({
|
|
9
|
+
command: "export",
|
|
10
|
+
summary: "Treeseed export completed successfully.",
|
|
11
|
+
facts: [
|
|
12
|
+
{ label: "Directory", value: exported.directory },
|
|
13
|
+
{ label: "Output", value: exported.outputPath },
|
|
14
|
+
{ label: "Branch", value: exported.branch },
|
|
15
|
+
{ label: "Timestamp", value: exported.timestamp },
|
|
16
|
+
{ label: "Files", value: exported.summary?.totalFiles },
|
|
17
|
+
{ label: "Tokens", value: exported.summary?.totalTokens },
|
|
18
|
+
{ label: "Bundled paths", value: Array.isArray(exported.includedBundlePaths) ? exported.includedBundlePaths.length : 0 }
|
|
19
|
+
],
|
|
20
|
+
report: exported
|
|
21
|
+
});
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return workflowErrorResult(error);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export {
|
|
27
|
+
handleExport
|
|
28
|
+
};
|
|
@@ -14,7 +14,9 @@ const handleRelease = async (invocation, context) => {
|
|
|
14
14
|
{ label: "Release level", value: payload.level },
|
|
15
15
|
{ label: "Root version", value: payload.rootVersion },
|
|
16
16
|
{ label: "Release tag", value: payload.releaseTag },
|
|
17
|
-
{ label: "
|
|
17
|
+
{ label: "Released commit", value: payload.releasedCommit.slice(0, 12) },
|
|
18
|
+
{ label: "Updated packages", value: payload.touchedPackages.length },
|
|
19
|
+
{ label: "Final branch", value: payload.finalBranch }
|
|
18
20
|
],
|
|
19
21
|
nextSteps: renderWorkflowNextSteps(result),
|
|
20
22
|
report: payload
|
|
@@ -4,18 +4,20 @@ const handleSave = async (invocation, context) => {
|
|
|
4
4
|
try {
|
|
5
5
|
const result = await createWorkflowSdk(context).save({
|
|
6
6
|
message: invocation.positionals.join(" ").trim(),
|
|
7
|
-
hotfix: invocation.args.hotfix === true
|
|
7
|
+
hotfix: invocation.args.hotfix === true,
|
|
8
|
+
preview: invocation.args.preview === true
|
|
8
9
|
});
|
|
9
10
|
const payload = result.payload;
|
|
10
11
|
return guidedResult({
|
|
11
12
|
command: invocation.commandName || "save",
|
|
12
|
-
summary: "Treeseed save completed successfully.",
|
|
13
|
+
summary: payload.noChanges ? "Treeseed save found no new changes and confirmed branch sync." : "Treeseed save completed successfully.",
|
|
13
14
|
facts: [
|
|
14
15
|
{ label: "Branch", value: payload.branch },
|
|
15
16
|
{ label: "Environment scope", value: payload.scope },
|
|
16
17
|
{ label: "Hotfix", value: payload.hotfix ? "yes" : "no" },
|
|
17
18
|
{ label: "Commit", value: payload.commitSha.slice(0, 12) },
|
|
18
|
-
{ label: "
|
|
19
|
+
{ label: "Commit created", value: payload.commitCreated ? "yes" : "no" },
|
|
20
|
+
{ label: "Preview action", value: payload.previewAction?.status ?? "skipped" }
|
|
19
21
|
],
|
|
20
22
|
nextSteps: renderWorkflowNextSteps(result),
|
|
21
23
|
report: payload
|
|
@@ -12,9 +12,11 @@ const handleStage = async (invocation, context) => {
|
|
|
12
12
|
facts: [
|
|
13
13
|
{ label: "Merged branch", value: payload.branchName },
|
|
14
14
|
{ label: "Merge target", value: payload.mergeTarget },
|
|
15
|
+
{ label: "Auto-saved", value: payload.autoSaved ? "yes" : "no" },
|
|
15
16
|
{ label: "Deprecated tag", value: payload.deprecatedTag.tagName },
|
|
16
17
|
{ label: "Staging wait", value: payload.stagingWait.status },
|
|
17
|
-
{ label: "Preview cleanup", value: payload.previewCleanup.performed ? "performed" : "not needed" }
|
|
18
|
+
{ label: "Preview cleanup", value: payload.previewCleanup.performed ? "performed" : "not needed" },
|
|
19
|
+
{ label: "Final branch", value: payload.finalBranch }
|
|
18
20
|
],
|
|
19
21
|
nextSteps: renderWorkflowNextSteps(result),
|
|
20
22
|
report: payload
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { TreeseedCommandContext } from './operations-types.js';
|
|
2
|
+
export declare function renderTreeseedHelpInk(commandName: string | null | undefined, context?: Pick<TreeseedCommandContext, 'outputFormat' | 'interactiveUi'>): Promise<number | null>;
|
|
3
|
+
export declare function shouldUseInkHelp(context: Pick<TreeseedCommandContext, 'outputFormat' | 'interactiveUi'>): boolean;
|