@treeseed/cli 0.8.19 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/handlers/capacity.js +273 -129
- package/dist/cli/handlers/dev.js +14 -33
- package/dist/cli/handlers/seed.js +114 -23
- package/dist/cli/operations-registry.js +33 -81
- package/dist/cli/registry.d.ts +0 -2
- package/dist/cli/registry.js +0 -2
- package/dist/cli/runtime.js +1 -0
- package/package.json +2 -2
|
@@ -1,144 +1,288 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { resolveCapacityProviderLaunchEnvironment } from "@treeseed/sdk/capacity-provider";
|
|
5
|
+
import { resolveMarketProfile } from "@treeseed/sdk/market-client";
|
|
6
|
+
import { findNearestTreeseedRoot, findNearestTreeseedWorkspaceRoot } from "@treeseed/sdk/workflow-support";
|
|
7
|
+
import { fail, guidedResult } from "./utils.js";
|
|
8
|
+
const ENTRYPOINT_RELATIVE_PATH = ["dist", "provider", "entrypoint.js"];
|
|
9
|
+
const COMPOSE_FILE_NAME = "compose.capacity-provider.yml";
|
|
10
|
+
const DEFAULT_PROJECT_NAME = "treeseed-capacity-provider";
|
|
11
|
+
const DEFAULT_HOST_DATA_DIR = ".treeseed/local-capacity-provider/data";
|
|
12
|
+
const PROVIDER_LIFECYCLE_ACTIONS = /* @__PURE__ */ new Set(["build", "up", "down", "restart", "logs", "status", "test-local"]);
|
|
13
|
+
const PROVIDER_ENTRYPOINT_ACTIONS = /* @__PURE__ */ new Set(["doctor", "register", "plan"]);
|
|
14
|
+
function stringArg(invocation, name) {
|
|
15
|
+
const value = invocation.args[name];
|
|
16
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
17
|
+
}
|
|
18
|
+
function boolArg(invocation, name) {
|
|
19
|
+
return invocation.args[name] === true;
|
|
20
|
+
}
|
|
21
|
+
function readPackageName(packageRoot) {
|
|
22
|
+
const packageJsonPath = resolve(packageRoot, "package.json");
|
|
23
|
+
if (!existsSync(packageJsonPath)) return null;
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
26
|
+
return parsed.name ?? null;
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function agentEntrypoint(packageRoot) {
|
|
32
|
+
return resolve(packageRoot, ...ENTRYPOINT_RELATIVE_PATH);
|
|
33
|
+
}
|
|
34
|
+
function resolveAgentPackageRoot(invocation, context) {
|
|
35
|
+
const explicitRoot = stringArg(invocation, "agentPackageRoot");
|
|
36
|
+
if (explicitRoot) {
|
|
37
|
+
return resolve(context.cwd, explicitRoot);
|
|
38
|
+
}
|
|
39
|
+
if (readPackageName(context.cwd) === "@treeseed/agent") {
|
|
40
|
+
return context.cwd;
|
|
41
|
+
}
|
|
42
|
+
const workspaceRoot = findNearestTreeseedWorkspaceRoot(context.cwd);
|
|
43
|
+
const workspaceAgentRoot = workspaceRoot ? resolve(workspaceRoot, "packages", "agent") : null;
|
|
44
|
+
if (workspaceAgentRoot && existsSync(resolve(workspaceAgentRoot, "package.json"))) {
|
|
45
|
+
return workspaceAgentRoot;
|
|
46
|
+
}
|
|
47
|
+
const nearestProjectRoot = findNearestTreeseedRoot(context.cwd);
|
|
48
|
+
const projectAgentRoot = nearestProjectRoot ? resolve(nearestProjectRoot, "packages", "agent") : null;
|
|
49
|
+
if (projectAgentRoot && existsSync(resolve(projectAgentRoot, "package.json"))) {
|
|
50
|
+
return projectAgentRoot;
|
|
51
|
+
}
|
|
52
|
+
const installedRoot = resolve(context.cwd, "node_modules", "@treeseed", "agent");
|
|
53
|
+
if (existsSync(resolve(installedRoot, "package.json"))) {
|
|
54
|
+
return installedRoot;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function resolveAgentPackage(invocation, context, options = {}) {
|
|
59
|
+
const packageRoot = resolveAgentPackageRoot(invocation, context);
|
|
60
|
+
if (!packageRoot) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"Unable to locate @treeseed/agent. Build the workspace package, install @treeseed/agent, or pass --agent-package-root."
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const entrypointPath = agentEntrypoint(packageRoot);
|
|
66
|
+
if (options.requireEntrypoint !== false && !existsSync(entrypointPath)) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Missing provider runtime at ${entrypointPath}. Run npm -w packages/agent run build:dist or pass --agent-package-root to a built package.`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const composeFilePath = resolve(packageRoot, COMPOSE_FILE_NAME);
|
|
72
|
+
return { packageRoot, entrypointPath, composeFilePath };
|
|
73
|
+
}
|
|
74
|
+
function providerSelector(invocation) {
|
|
75
|
+
return stringArg(invocation, "provider") ?? "local";
|
|
76
|
+
}
|
|
77
|
+
function environmentSelector(invocation) {
|
|
78
|
+
return stringArg(invocation, "environment") ?? "local";
|
|
79
|
+
}
|
|
80
|
+
function resolveMarket(invocation) {
|
|
81
|
+
return resolveMarketProfile(stringArg(invocation, "market") ?? "local");
|
|
82
|
+
}
|
|
83
|
+
function resolveTenantRoot(context, agentPackageRoot) {
|
|
84
|
+
return findNearestTreeseedRoot(context.cwd) ?? (readPackageName(agentPackageRoot) === "@treeseed/agent" ? agentPackageRoot : context.cwd);
|
|
85
|
+
}
|
|
86
|
+
function defaultHostDataDir(context) {
|
|
87
|
+
const tenantRoot = findNearestTreeseedRoot(context.cwd) ?? context.cwd;
|
|
88
|
+
return resolve(tenantRoot, DEFAULT_HOST_DATA_DIR);
|
|
89
|
+
}
|
|
90
|
+
function providerProjectName(invocation) {
|
|
91
|
+
const provider = providerSelector(invocation).replace(/[^A-Za-z0-9_.-]+/gu, "-").replace(/^-+|-+$/gu, "") || "local";
|
|
92
|
+
return `${DEFAULT_PROJECT_NAME}-${provider}`;
|
|
93
|
+
}
|
|
94
|
+
function composeCommandArgs(composeFilePath, projectName, action) {
|
|
95
|
+
const base = ["compose", "-f", composeFilePath, "-p", projectName];
|
|
96
|
+
switch (action) {
|
|
97
|
+
case "up":
|
|
98
|
+
return [...base, "up", "-d"];
|
|
99
|
+
case "down":
|
|
100
|
+
return [...base, "down"];
|
|
101
|
+
case "restart":
|
|
102
|
+
return [...base, "restart"];
|
|
103
|
+
case "logs":
|
|
104
|
+
return [...base, "logs", "--tail", "200"];
|
|
105
|
+
case "status":
|
|
106
|
+
return [...base, "ps"];
|
|
107
|
+
default:
|
|
108
|
+
return base;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function lifecycleActionRequiresConnection(action) {
|
|
112
|
+
return action === "up" || action === "restart";
|
|
113
|
+
}
|
|
114
|
+
function runLifecycleAction(action, invocation, context) {
|
|
115
|
+
const agentPackage = resolveAgentPackage(invocation, context, { requireEntrypoint: action !== "build" });
|
|
116
|
+
if (action !== "build" && action !== "test-local" && !existsSync(agentPackage.composeFilePath)) {
|
|
117
|
+
return fail(`Missing ${COMPOSE_FILE_NAME} in ${agentPackage.packageRoot}. Build or reinstall @treeseed/agent with Phase 3 container assets.`);
|
|
118
|
+
}
|
|
119
|
+
if (action === "build" || action === "test-local") {
|
|
120
|
+
const script = action === "build" ? "capacity-provider:build" : "capacity-provider:test-local";
|
|
121
|
+
const result2 = context.spawn("npm", ["run", script], {
|
|
122
|
+
cwd: agentPackage.packageRoot,
|
|
123
|
+
env: context.env,
|
|
124
|
+
stdio: "inherit"
|
|
125
|
+
});
|
|
15
126
|
return guidedResult({
|
|
16
|
-
command:
|
|
17
|
-
summary: "
|
|
127
|
+
command: `capacity ${action}`,
|
|
128
|
+
summary: result2.status === 0 ? `Capacity provider ${action === "build" ? "image build" : "container smoke test"} completed.` : `Capacity provider ${action === "build" ? "image build" : "container smoke test"} failed.`,
|
|
18
129
|
facts: [
|
|
19
|
-
{ label: "
|
|
20
|
-
{ label: "
|
|
21
|
-
{ label: "
|
|
22
|
-
{ label: "Daily remaining", value: summary?.dailyRemainingCredits },
|
|
23
|
-
{ label: "Active providers", value: summary?.activeProviderCount }
|
|
130
|
+
{ label: "Agent package", value: agentPackage.packageRoot },
|
|
131
|
+
{ label: "Script", value: script },
|
|
132
|
+
{ label: "Exit code", value: result2.status ?? 1 }
|
|
24
133
|
],
|
|
25
|
-
|
|
134
|
+
exitCode: result2.status ?? 1,
|
|
135
|
+
report: {
|
|
136
|
+
action,
|
|
137
|
+
agentPackageRoot: agentPackage.packageRoot,
|
|
138
|
+
script
|
|
139
|
+
}
|
|
26
140
|
});
|
|
27
141
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const response = await client.launchManagedCapacityProvider(team, { launchSource: "cli" });
|
|
46
|
-
return guidedResult({
|
|
47
|
-
command: "capacity",
|
|
48
|
-
summary: "TreeSeed-managed helper capacity is connected.",
|
|
49
|
-
facts: [
|
|
50
|
-
{ label: "Provider", value: response.payload.provider?.id },
|
|
51
|
-
{ label: "Security code prefix", value: response.payload.apiKey?.keyPrefix }
|
|
52
|
-
],
|
|
53
|
-
report: { marketId: profile.id, teamId: team, result: response.payload }
|
|
54
|
-
});
|
|
142
|
+
const diagnostic = boolArg(invocation, "diagnostic") || action === "test-local";
|
|
143
|
+
const market = resolveMarket(invocation);
|
|
144
|
+
const hostDataDirInput = stringArg(invocation, "dataDir") ?? context.env.TREESEED_PROVIDER_HOST_DATA_DIR ?? defaultHostDataDir(context);
|
|
145
|
+
const resolvedHostDataDir = resolve(context.cwd, hostDataDirInput);
|
|
146
|
+
const tenantRoot = resolveTenantRoot(context, agentPackage.packageRoot);
|
|
147
|
+
const launch = resolveCapacityProviderLaunchEnvironment({
|
|
148
|
+
tenantRoot,
|
|
149
|
+
scope: environmentSelector(invocation),
|
|
150
|
+
env: context.env,
|
|
151
|
+
diagnostic,
|
|
152
|
+
requireConnection: lifecycleActionRequiresConnection(action),
|
|
153
|
+
overrides: {
|
|
154
|
+
TREESEED_MARKET_URL: market.baseUrl,
|
|
155
|
+
TREESEED_MARKET_ID: market.id,
|
|
156
|
+
TREESEED_PROVIDER_HOST_DATA_DIR: resolvedHostDataDir,
|
|
157
|
+
TREESEED_PROVIDER_ENVIRONMENT: providerSelector(invocation),
|
|
158
|
+
...diagnostic ? { TREESEED_PROVIDER_STARTUP_MODE: "diagnostic" } : {}
|
|
55
159
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
160
|
+
});
|
|
161
|
+
const hostDataDir = resolvedHostDataDir;
|
|
162
|
+
mkdirSync(hostDataDir, { recursive: true });
|
|
163
|
+
const projectName = providerProjectName(invocation);
|
|
164
|
+
const args = composeCommandArgs(agentPackage.composeFilePath, projectName, action);
|
|
165
|
+
const result = context.spawn("docker", args, {
|
|
166
|
+
cwd: agentPackage.packageRoot,
|
|
167
|
+
env: {
|
|
168
|
+
...context.env,
|
|
169
|
+
...launch.env
|
|
170
|
+
},
|
|
171
|
+
stdio: "inherit"
|
|
172
|
+
});
|
|
173
|
+
return guidedResult({
|
|
174
|
+
command: `capacity ${action}`,
|
|
175
|
+
summary: result.status === 0 ? `Capacity provider ${action} completed${diagnostic ? " in diagnostic mode" : ""}.` : `Capacity provider ${action} failed.`,
|
|
176
|
+
facts: [
|
|
177
|
+
{ label: "Market", value: `${market.id} (${market.baseUrl})` },
|
|
178
|
+
{ label: "Provider", value: providerSelector(invocation) },
|
|
179
|
+
{ label: "Mode", value: diagnostic ? "diagnostic" : "live" },
|
|
180
|
+
{ label: "Compose project", value: projectName },
|
|
181
|
+
{ label: "Agent package", value: agentPackage.packageRoot },
|
|
182
|
+
{ label: "Data directory", value: hostDataDir },
|
|
183
|
+
{ label: "Exit code", value: result.status ?? 1 }
|
|
184
|
+
],
|
|
185
|
+
sections: [
|
|
186
|
+
{
|
|
187
|
+
title: "Environment",
|
|
188
|
+
lines: Object.entries(launch.redactedEnv).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}=${value}`)
|
|
84
189
|
}
|
|
190
|
+
],
|
|
191
|
+
exitCode: result.status ?? 1,
|
|
192
|
+
report: {
|
|
193
|
+
action,
|
|
194
|
+
agentPackageRoot: agentPackage.packageRoot,
|
|
195
|
+
composeFile: agentPackage.composeFilePath,
|
|
196
|
+
composeProject: projectName,
|
|
197
|
+
market: { id: market.id, baseUrl: market.baseUrl },
|
|
198
|
+
provider: providerSelector(invocation),
|
|
199
|
+
diagnostic,
|
|
200
|
+
redactedEnv: launch.redactedEnv
|
|
85
201
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function invokeProviderEntrypoint(action, invocation, context) {
|
|
205
|
+
const agentPackage = resolveAgentPackage(invocation, context);
|
|
206
|
+
const market = resolveMarket(invocation);
|
|
207
|
+
const args = [agentPackage.entrypointPath, action, "--market", market.id, "--provider", providerSelector(invocation)];
|
|
208
|
+
if (boolArg(invocation, "dryRun") || action === "doctor" || action === "plan") {
|
|
209
|
+
args.push("--dry-run");
|
|
210
|
+
}
|
|
211
|
+
if (context.outputFormat === "json" || boolArg(invocation, "json")) {
|
|
212
|
+
args.push("--json");
|
|
213
|
+
}
|
|
214
|
+
const result = spawnSync(process.execPath, args, {
|
|
215
|
+
cwd: agentPackage.packageRoot,
|
|
216
|
+
env: {
|
|
217
|
+
...context.env,
|
|
218
|
+
TREESEED_MARKET_URL: market.baseUrl,
|
|
219
|
+
TREESEED_MARKET_ID: market.id,
|
|
220
|
+
TREESEED_PROVIDER_ENVIRONMENT: providerSelector(invocation)
|
|
221
|
+
},
|
|
222
|
+
encoding: "utf8"
|
|
223
|
+
});
|
|
224
|
+
const stdout = result.stdout.trim();
|
|
225
|
+
const stderr = result.stderr.trim();
|
|
226
|
+
let report = null;
|
|
227
|
+
if (stdout.startsWith("{")) {
|
|
228
|
+
try {
|
|
229
|
+
report = JSON.parse(stdout);
|
|
230
|
+
} catch {
|
|
231
|
+
report = null;
|
|
102
232
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
233
|
+
}
|
|
234
|
+
if (context.outputFormat === "json") {
|
|
235
|
+
return {
|
|
236
|
+
exitCode: result.status ?? 1,
|
|
237
|
+
stdout: stdout ? [stdout] : [],
|
|
238
|
+
stderr: stderr ? [stderr] : [],
|
|
239
|
+
report: report ?? {
|
|
240
|
+
ok: result.status === 0,
|
|
241
|
+
action,
|
|
242
|
+
stdout,
|
|
243
|
+
stderr,
|
|
244
|
+
agentPackageRoot: agentPackage.packageRoot
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return guidedResult({
|
|
249
|
+
command: `capacity ${action}`,
|
|
250
|
+
summary: result.status === 0 ? `Capacity provider ${action} completed.` : `Capacity provider ${action} failed.`,
|
|
251
|
+
facts: [
|
|
252
|
+
{ label: "Market", value: `${market.id} (${market.baseUrl})` },
|
|
253
|
+
{ label: "Provider", value: providerSelector(invocation) },
|
|
254
|
+
{ label: "Agent package", value: agentPackage.packageRoot },
|
|
255
|
+
{ label: "Exit code", value: result.status ?? 1 }
|
|
256
|
+
],
|
|
257
|
+
sections: [
|
|
258
|
+
{ title: "Output", lines: stdout ? stdout.split(/\r?\n/u) : [] },
|
|
259
|
+
{ title: "Errors", lines: stderr ? stderr.split(/\r?\n/u) : [] }
|
|
260
|
+
],
|
|
261
|
+
exitCode: result.status ?? 1,
|
|
262
|
+
report: report ?? {
|
|
263
|
+
ok: result.status === 0,
|
|
264
|
+
action,
|
|
265
|
+
agentPackageRoot: agentPackage.packageRoot
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const handleCapacity = (invocation, context) => {
|
|
270
|
+
const action = invocation.positionals[0] ?? "doctor";
|
|
271
|
+
if (PROVIDER_LIFECYCLE_ACTIONS.has(action)) {
|
|
272
|
+
try {
|
|
273
|
+
return runLifecycleAction(action, invocation, context);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
122
276
|
}
|
|
123
|
-
return { exitCode: 1, stderr: [`Unknown capacity grants action: ${subcommand}`] };
|
|
124
277
|
}
|
|
125
|
-
if (action
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
return guidedResult({
|
|
132
|
-
command: "capacity",
|
|
133
|
-
summary: "Budgeted agent task enqueued.",
|
|
134
|
-
facts: [
|
|
135
|
-
{ label: "Task", value: response.payload.task?.id },
|
|
136
|
-
{ label: "Reserved credits", value: response.payload.reservation?.reservedCredits }
|
|
137
|
-
],
|
|
138
|
-
report: { marketId: profile.id, projectId, result: response.payload }
|
|
139
|
-
});
|
|
278
|
+
if (PROVIDER_ENTRYPOINT_ACTIONS.has(action)) {
|
|
279
|
+
try {
|
|
280
|
+
return invokeProviderEntrypoint(action, invocation, context);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
283
|
+
}
|
|
140
284
|
}
|
|
141
|
-
return
|
|
285
|
+
return fail(`Unknown capacity action "${action}". Use doctor, register, plan, build, up, down, restart, logs, status, or test-local.`);
|
|
142
286
|
};
|
|
143
287
|
export {
|
|
144
288
|
handleCapacity
|
package/dist/cli/handlers/dev.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { ensureLocalWorkspaceLinks, findNearestTreeseedWorkspaceRoot, resolveTreeseedLaunchEnvironment } from "@treeseed/sdk/workflow-support";
|
|
5
5
|
import { workflowErrorResult } from "./workflow.js";
|
|
6
|
+
import { fail } from "./utils.js";
|
|
6
7
|
const require2 = createRequire(import.meta.url);
|
|
7
8
|
function resolveCoreDevEntrypoint(cwd) {
|
|
8
9
|
const workspacePackageJsonPath = resolve(cwd, "packages", "core", "package.json");
|
|
@@ -42,10 +43,16 @@ function resolveCoreDevEntrypoint(cwd) {
|
|
|
42
43
|
}
|
|
43
44
|
const handleDev = async (invocation, context) => {
|
|
44
45
|
try {
|
|
45
|
-
|
|
46
|
+
if (invocation.commandName !== "dev") {
|
|
47
|
+
return fail("`trsd dev` only starts the Market web/API dev runtime. Use `trsd capacity ...` for capacity provider lifecycle commands.");
|
|
48
|
+
}
|
|
49
|
+
const removedOptions = ["surface", "surfaces", "withWorker"].filter((name) => invocation.args[name] !== void 0);
|
|
50
|
+
if (removedOptions.length > 0) {
|
|
51
|
+
return fail(`\`trsd dev\` no longer accepts ${removedOptions.map((name) => `--${name.replace(/[A-Z]/gu, (char) => `-${char.toLowerCase()}`)}`).join(", ")}. It always starts fixed Market web/API surfaces; use \`trsd capacity ...\` for providers.`);
|
|
52
|
+
}
|
|
46
53
|
const feedback = typeof invocation.args.feedback === "string" ? invocation.args.feedback : void 0;
|
|
47
54
|
const watch = feedback !== "off";
|
|
48
|
-
const passthroughArgs = [];
|
|
55
|
+
const passthroughArgs = ["--surfaces", "web,api"];
|
|
49
56
|
const forwardStringOption = (name, flag) => {
|
|
50
57
|
const value = invocation.args[name];
|
|
51
58
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
@@ -57,38 +64,18 @@ const handleDev = async (invocation, context) => {
|
|
|
57
64
|
passthroughArgs.push(flag);
|
|
58
65
|
}
|
|
59
66
|
};
|
|
60
|
-
if (managerMode) {
|
|
61
|
-
const explicitSurfaces = typeof invocation.args.surfaces === "string" && invocation.args.surfaces.trim() ? invocation.args.surfaces.trim() : typeof invocation.args.surface === "string" && invocation.args.surface.trim() ? invocation.args.surface.trim() : null;
|
|
62
|
-
const surfaces = explicitSurfaces ?? (invocation.args.withWorker === true ? "manager,worker" : "manager");
|
|
63
|
-
passthroughArgs.push("--surfaces", surfaces);
|
|
64
|
-
} else {
|
|
65
|
-
forwardStringOption("surface", "--surface");
|
|
66
|
-
forwardStringOption("surfaces", "--surfaces");
|
|
67
|
-
}
|
|
68
67
|
forwardStringOption("host", "--host");
|
|
69
68
|
forwardStringOption("port", "--port");
|
|
69
|
+
forwardStringOption("webRuntime", "--web-runtime");
|
|
70
70
|
forwardStringOption("apiHost", "--api-host");
|
|
71
71
|
forwardStringOption("apiPort", "--api-port");
|
|
72
|
-
forwardStringOption("managerPort", "--manager-port");
|
|
73
72
|
forwardStringOption("setup", "--setup");
|
|
74
73
|
forwardStringOption("feedback", "--feedback");
|
|
75
74
|
forwardStringOption("open", "--open");
|
|
76
75
|
forwardBooleanOption("plan", "--plan");
|
|
77
76
|
forwardBooleanOption("reset", "--reset");
|
|
77
|
+
forwardBooleanOption("force", "--force");
|
|
78
78
|
forwardBooleanOption("json", "--json");
|
|
79
|
-
const docsAutomationMode = typeof invocation.args.docsAutomation === "string" ? invocation.args.docsAutomation.trim() : "";
|
|
80
|
-
const workdayId = typeof invocation.args.workdayId === "string" ? invocation.args.workdayId.trim() : "";
|
|
81
|
-
const capacityBudget = typeof invocation.args.capacityBudget === "string" ? invocation.args.capacityBudget.trim() : "";
|
|
82
|
-
const approvalPolicy = typeof invocation.args.approvalPolicy === "string" ? invocation.args.approvalPolicy.trim() : "";
|
|
83
|
-
const devManagerEnv = managerMode ? {
|
|
84
|
-
TREESEED_DOCS_AUTOMATION_MODE: docsAutomationMode || "on",
|
|
85
|
-
...workdayId ? { TREESEED_WORKDAY_ID: workdayId } : {},
|
|
86
|
-
...capacityBudget ? {
|
|
87
|
-
TREESEED_CAPACITY_BUDGET: capacityBudget,
|
|
88
|
-
TREESEED_WORKDAY_TASK_CREDIT_BUDGET: capacityBudget
|
|
89
|
-
} : {},
|
|
90
|
-
TREESEED_APPROVAL_POLICY: approvalPolicy || "manual"
|
|
91
|
-
} : {};
|
|
92
79
|
const workspaceRoot = findNearestTreeseedWorkspaceRoot(context.cwd);
|
|
93
80
|
const workspaceLinksMode = typeof invocation.args.workspaceLinks === "string" ? invocation.args.workspaceLinks : void 0;
|
|
94
81
|
const workspaceLinks = workspaceRoot ? ensureLocalWorkspaceLinks(workspaceRoot, { env: context.env, mode: workspaceLinksMode }) : null;
|
|
@@ -102,7 +89,8 @@ const handleDev = async (invocation, context) => {
|
|
|
102
89
|
env: resolveTreeseedLaunchEnvironment({
|
|
103
90
|
tenantRoot: context.cwd,
|
|
104
91
|
scope: "local",
|
|
105
|
-
baseEnv: { ...process.env, ...context.env ?? {}
|
|
92
|
+
baseEnv: { ...process.env, ...context.env ?? {} },
|
|
93
|
+
overrides: {}
|
|
106
94
|
}),
|
|
107
95
|
stdio: "inherit"
|
|
108
96
|
});
|
|
@@ -111,18 +99,11 @@ const handleDev = async (invocation, context) => {
|
|
|
111
99
|
suppressJsonResult: invocation.args.json === true,
|
|
112
100
|
report: {
|
|
113
101
|
command: "dev",
|
|
114
|
-
alias:
|
|
102
|
+
alias: invocation.commandName,
|
|
115
103
|
ok: (result.status ?? 1) === 0,
|
|
116
104
|
watch,
|
|
117
105
|
executable: resolved.command,
|
|
118
106
|
args,
|
|
119
|
-
docsAutomation: managerMode ? {
|
|
120
|
-
mode: docsAutomationMode || "on",
|
|
121
|
-
workdayId: workdayId || null,
|
|
122
|
-
capacityBudget: capacityBudget || null,
|
|
123
|
-
approvalPolicy: approvalPolicy || "manual",
|
|
124
|
-
withWorker: invocation.args.withWorker === true
|
|
125
|
-
} : void 0,
|
|
126
107
|
workspaceLinks
|
|
127
108
|
}
|
|
128
109
|
};
|
|
@@ -2,21 +2,57 @@ import { pathToFileURL } from "node:url";
|
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { formatSeedDiagnostics, formatSeedPlan, loadAndPlanSeed } from "@treeseed/sdk/seeds";
|
|
5
|
+
import { persistCapacityProviderConnectionToTreeseedConfig } from "@treeseed/sdk/capacity-provider";
|
|
5
6
|
import { MarketApiError } from "@treeseed/sdk/market-client";
|
|
6
7
|
import { createMarketClientForInvocation, marketAuthRoot, marketSelector } from "./market-utils.js";
|
|
7
|
-
import { resolveMarketProfile, resolveMarketSession } from "@treeseed/sdk/market-client";
|
|
8
|
+
import { MarketClient, resolveMarketProfile, resolveMarketSession, setMarketSession } from "@treeseed/sdk/market-client";
|
|
8
9
|
async function loadLocalSeedModule(projectRoot) {
|
|
9
10
|
const apiModulePath = resolve(projectRoot, "src", "lib", "market", "seeds", "local-api.js");
|
|
10
|
-
const
|
|
11
|
-
|
|
11
|
+
const applyModulePath = resolve(projectRoot, "src", "lib", "market", "seeds", "apply.js");
|
|
12
|
+
const applyModule = await import(pathToFileURL(applyModulePath).href);
|
|
13
|
+
if (!existsSync(apiModulePath)) {
|
|
14
|
+
return applyModule;
|
|
15
|
+
}
|
|
16
|
+
const apiModule = await import(pathToFileURL(apiModulePath).href);
|
|
17
|
+
return {
|
|
18
|
+
...applyModule,
|
|
19
|
+
...apiModule
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function sessionExpiresSoon(session) {
|
|
23
|
+
if (!session.expiresAt) return false;
|
|
24
|
+
const expiresAt = Date.parse(session.expiresAt);
|
|
25
|
+
return Number.isFinite(expiresAt) && expiresAt <= Date.now() + 6e4;
|
|
12
26
|
}
|
|
13
|
-
function requireLocalSeedSession(invocation, context) {
|
|
27
|
+
async function requireLocalSeedSession(invocation, context) {
|
|
14
28
|
const selector = marketSelector(invocation) ?? "local";
|
|
15
29
|
const profile = resolveMarketProfile(selector);
|
|
16
|
-
const
|
|
30
|
+
const tenantRoot = marketAuthRoot(context);
|
|
31
|
+
const session = resolveMarketSession(tenantRoot, profile.id);
|
|
17
32
|
if (!session?.accessToken) {
|
|
18
33
|
throw new Error(`Not logged in to market "${profile.id}". Run treeseed auth:login --market ${profile.id}.`);
|
|
19
34
|
}
|
|
35
|
+
if (sessionExpiresSoon(session) && session.refreshToken) {
|
|
36
|
+
try {
|
|
37
|
+
const refreshed = await new MarketClient({ profile, userAgent: "treeseed-cli" }).refreshToken({ refreshToken: session.refreshToken });
|
|
38
|
+
if (refreshed.ok) {
|
|
39
|
+
const nextSession = {
|
|
40
|
+
marketId: profile.id,
|
|
41
|
+
accessToken: refreshed.accessToken,
|
|
42
|
+
refreshToken: refreshed.refreshToken,
|
|
43
|
+
expiresAt: refreshed.expiresAt,
|
|
44
|
+
principal: refreshed.principal
|
|
45
|
+
};
|
|
46
|
+
setMarketSession(tenantRoot, nextSession);
|
|
47
|
+
return { profile, session: nextSession };
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
throw new Error(`Login for market "${profile.id}" expired. Run treeseed auth:login --market ${profile.id}.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (sessionExpiresSoon(session)) {
|
|
54
|
+
throw new Error(`Login for market "${profile.id}" expired. Run treeseed auth:login --market ${profile.id}.`);
|
|
55
|
+
}
|
|
20
56
|
return { profile, session };
|
|
21
57
|
}
|
|
22
58
|
function seedRequestBody(invocation) {
|
|
@@ -70,11 +106,44 @@ function formatCapacityProviderKeyNotes(result) {
|
|
|
70
106
|
if (created.length === 0) return [];
|
|
71
107
|
return [
|
|
72
108
|
"",
|
|
73
|
-
"Provider
|
|
74
|
-
...created.map((entry) => ` ${String(entry.providerName ?? entry.providerKey ?? entry.providerId)} (${String(entry.keyPrefix ?? "new key")}):
|
|
75
|
-
"
|
|
109
|
+
"Provider connection:",
|
|
110
|
+
...created.map((entry) => ` ${String(entry.providerName ?? entry.providerKey ?? entry.providerId)} (${String(entry.keyPrefix ?? "new key")}): stored in encrypted Treeseed config`),
|
|
111
|
+
" Use `treeseed capacity up --market local --provider local` to launch the provider."
|
|
76
112
|
];
|
|
77
113
|
}
|
|
114
|
+
function redactLocalCapacityProviderKeys(result) {
|
|
115
|
+
const capacityProviderKeys = result.capacityProviderKeys && typeof result.capacityProviderKeys === "object" ? result.capacityProviderKeys : null;
|
|
116
|
+
if (!capacityProviderKeys) return result;
|
|
117
|
+
return {
|
|
118
|
+
...result,
|
|
119
|
+
capacityProviderKeys: {
|
|
120
|
+
...capacityProviderKeys,
|
|
121
|
+
created: Array.isArray(capacityProviderKeys.created) ? capacityProviderKeys.created.map((entry) => {
|
|
122
|
+
if (!entry || typeof entry !== "object") return entry;
|
|
123
|
+
const { plaintextKey: _plaintextKey, ...safeEntry } = entry;
|
|
124
|
+
return {
|
|
125
|
+
...safeEntry,
|
|
126
|
+
storedInTreeseedConfig: true
|
|
127
|
+
};
|
|
128
|
+
}) : []
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function storeLocalCapacityProviderConnection(input) {
|
|
133
|
+
const capacityProviderKeys = input.result.capacityProviderKeys && typeof input.result.capacityProviderKeys === "object" ? input.result.capacityProviderKeys : null;
|
|
134
|
+
const created = Array.isArray(capacityProviderKeys?.created) ? capacityProviderKeys.created : [];
|
|
135
|
+
const first = created.find((entry) => typeof entry.plaintextKey === "string" && String(entry.plaintextKey).length > 0);
|
|
136
|
+
if (!first) return null;
|
|
137
|
+
return persistCapacityProviderConnectionToTreeseedConfig({
|
|
138
|
+
tenantRoot: input.context.cwd,
|
|
139
|
+
scope: "local",
|
|
140
|
+
marketUrl: input.profile.baseUrl,
|
|
141
|
+
marketId: input.profile.id,
|
|
142
|
+
apiKey: String(first.plaintextKey),
|
|
143
|
+
providerHostDataDir: ".treeseed/local-capacity-provider/data",
|
|
144
|
+
providerEnvironment: "local"
|
|
145
|
+
});
|
|
146
|
+
}
|
|
78
147
|
function remoteSeedError(error, command) {
|
|
79
148
|
if (error instanceof MarketApiError) {
|
|
80
149
|
const payload = error.payload && typeof error.payload === "object" ? error.payload : { error: error.message };
|
|
@@ -127,7 +196,7 @@ async function handleSeedExport(invocation, context) {
|
|
|
127
196
|
const { client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
128
197
|
payload = await client.exportSeed(team, body);
|
|
129
198
|
} else {
|
|
130
|
-
const { session } = requireLocalSeedSession(invocation, context);
|
|
199
|
+
const { session } = await requireLocalSeedSession(invocation, context);
|
|
131
200
|
const localModule = await loadLocalSeedModule(context.cwd);
|
|
132
201
|
const exporter = localModule.exportLocalSeedViaApiFromCli ?? localModule.exportSeedFromCli;
|
|
133
202
|
if (typeof exporter !== "function") {
|
|
@@ -201,27 +270,16 @@ const handleSeed = async (invocation, context) => {
|
|
|
201
270
|
}
|
|
202
271
|
};
|
|
203
272
|
}
|
|
204
|
-
const localAuth = planned.plan.environments.every((environment) => environment === "local") && !wantsValidate ? (() => {
|
|
205
|
-
try {
|
|
206
|
-
return requireLocalSeedSession(invocation, context);
|
|
207
|
-
} catch (error) {
|
|
208
|
-
return { error };
|
|
209
|
-
}
|
|
210
|
-
})() : null;
|
|
211
|
-
if (localAuth && "error" in localAuth) {
|
|
212
|
-
return remoteSeedError(localAuth.error, "seed");
|
|
213
|
-
}
|
|
214
273
|
if (!wantsApply && !wantsValidate && planned.plan.environments.length === 1 && planned.plan.environments[0] === "local") {
|
|
215
274
|
try {
|
|
216
275
|
const localModule = await loadLocalSeedModule(context.cwd);
|
|
217
|
-
const localPlanner = localModule.
|
|
276
|
+
const localPlanner = localModule.planLocalSeedFromCli ?? localModule.planLocalSeedViaApiFromCli;
|
|
218
277
|
if (typeof localPlanner === "function") {
|
|
219
278
|
const localPlanned = await localPlanner({
|
|
220
279
|
projectRoot: context.cwd,
|
|
221
280
|
seedName,
|
|
222
281
|
environments: typeof invocation.args.environments === "string" ? invocation.args.environments : void 0,
|
|
223
282
|
mode,
|
|
224
|
-
accessToken: localAuth?.session.accessToken,
|
|
225
283
|
env: context.env
|
|
226
284
|
});
|
|
227
285
|
if (localPlanned.plan) {
|
|
@@ -250,6 +308,12 @@ const handleSeed = async (invocation, context) => {
|
|
|
250
308
|
return remoteSeedError(error, "seed");
|
|
251
309
|
}
|
|
252
310
|
}
|
|
311
|
+
let localAuth;
|
|
312
|
+
try {
|
|
313
|
+
localAuth = await requireLocalSeedSession(invocation, context);
|
|
314
|
+
} catch (error) {
|
|
315
|
+
return remoteSeedError(error, "seed");
|
|
316
|
+
}
|
|
253
317
|
const localModule = await loadLocalSeedModule(context.cwd);
|
|
254
318
|
const runner = localModule.applyLocalSeedViaApiFromCli ?? localModule.applyLocalSeedFromCli;
|
|
255
319
|
if (typeof runner !== "function") {
|
|
@@ -263,6 +327,26 @@ const handleSeed = async (invocation, context) => {
|
|
|
263
327
|
accessToken: localAuth?.session.accessToken,
|
|
264
328
|
env: context.env
|
|
265
329
|
});
|
|
330
|
+
let providerConnection = null;
|
|
331
|
+
try {
|
|
332
|
+
providerConnection = storeLocalCapacityProviderConnection({
|
|
333
|
+
context,
|
|
334
|
+
profile: localAuth.profile,
|
|
335
|
+
result: applied.result
|
|
336
|
+
});
|
|
337
|
+
} catch (error) {
|
|
338
|
+
const message2 = `Unable to store local capacity provider connection in encrypted Treeseed config. ${error instanceof Error ? error.message : String(error)}`;
|
|
339
|
+
return {
|
|
340
|
+
exitCode: 4,
|
|
341
|
+
stderr: [message2],
|
|
342
|
+
report: {
|
|
343
|
+
command: "seed",
|
|
344
|
+
ok: false,
|
|
345
|
+
error: message2
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
const safeResult = redactLocalCapacityProviderKeys(applied.result);
|
|
266
350
|
const message = "Local seed apply completed.";
|
|
267
351
|
return {
|
|
268
352
|
exitCode: 0,
|
|
@@ -275,7 +359,7 @@ const handleSeed = async (invocation, context) => {
|
|
|
275
359
|
` unchanged: ${applied.plan.summary.unchanged}`,
|
|
276
360
|
` skipped: ${applied.plan.summary.skip}`,
|
|
277
361
|
` failed: ${applied.plan.summary.error}`,
|
|
278
|
-
...formatCapacityProviderKeyNotes(
|
|
362
|
+
...formatCapacityProviderKeyNotes(safeResult)
|
|
279
363
|
],
|
|
280
364
|
report: {
|
|
281
365
|
...applied.plan,
|
|
@@ -283,7 +367,14 @@ const handleSeed = async (invocation, context) => {
|
|
|
283
367
|
command: "seed",
|
|
284
368
|
result: {
|
|
285
369
|
message,
|
|
286
|
-
...
|
|
370
|
+
...safeResult,
|
|
371
|
+
...providerConnection ? {
|
|
372
|
+
capacityProviderConnection: {
|
|
373
|
+
scope: providerConnection.scope,
|
|
374
|
+
writtenKeys: providerConnection.writtenKeys,
|
|
375
|
+
redactedEnv: providerConnection.redactedEnv
|
|
376
|
+
}
|
|
377
|
+
} : {}
|
|
287
378
|
}
|
|
288
379
|
}
|
|
289
380
|
};
|
|
@@ -21,30 +21,20 @@ function related(name, why) {
|
|
|
21
21
|
return { name, why };
|
|
22
22
|
}
|
|
23
23
|
const DEV_RUNTIME_OPTIONS = [
|
|
24
|
-
{ name: "surface", flags: "--surface <surface>", description: "Select one local dev surface to run. Compatibility alias for --surfaces.", kind: "enum", values: ["integrated", "web", "api", "manager", "worker", "agents", "services"] },
|
|
25
|
-
{ name: "surfaces", flags: "--surfaces <surfaces>", description: "Select comma-separated local dev surfaces to run.", kind: "string" },
|
|
26
24
|
{ name: "host", flags: "--host <host>", description: "Host for the web dev server.", kind: "string" },
|
|
27
25
|
{ name: "port", flags: "--port <port>", description: "Port for the web dev server.", kind: "string" },
|
|
26
|
+
{ name: "webRuntime", flags: "--web-runtime <mode>", description: "Choose the local web runtime. Use local for Astro hot reload or provider for provider parity.", kind: "enum", values: ["auto", "local", "provider"] },
|
|
28
27
|
{ name: "apiHost", flags: "--api-host <host>", description: "Host used to construct the local API URL.", kind: "string" },
|
|
29
28
|
{ name: "apiPort", flags: "--api-port <port>", description: "Port for the local API server.", kind: "string" },
|
|
30
|
-
{ name: "managerPort", flags: "--manager-port <port>", description: "Port used for the local manager service URL.", kind: "string" },
|
|
31
29
|
{ name: "setup", flags: "--setup <mode>", description: "Control automatic local runtime setup.", kind: "enum", values: ["auto", "check", "off"] },
|
|
32
30
|
{ name: "feedback", flags: "--feedback <mode>", description: "Control live feedback, service restarts, and browser reload stamps.", kind: "enum", values: ["live", "restart", "off"] },
|
|
33
31
|
{ name: "open", flags: "--open <mode>", description: "Control whether dev opens the browser after readiness. Defaults to off; use --open on to launch it.", kind: "enum", values: ["auto", "on", "off"] },
|
|
34
32
|
{ name: "plan", flags: "--plan", description: "Print the dev runtime plan and exit without starting services.", kind: "boolean" },
|
|
35
33
|
{ name: "reset", flags: "--reset", description: "Clear local dev runtime state before setup, migrations, and service startup.", kind: "boolean" },
|
|
34
|
+
{ name: "force", flags: "--force", description: "Terminate overlapping Treeseed dev runtimes and listeners on required local dev ports before startup.", kind: "boolean" },
|
|
36
35
|
{ name: "json", flags: "--json", description: "Emit structured JSON or newline-delimited dev events.", kind: "boolean" },
|
|
37
|
-
{ name: "watch", flags: "--watch", description: "Enable live watch behavior. `dev` defaults to live feedback; this remains for compatibility.", kind: "boolean" },
|
|
38
36
|
{ name: "workspaceLinks", flags: "--workspace-links <mode>", description: "Control local workspace package links.", kind: "enum", values: ["auto", "off"] }
|
|
39
37
|
];
|
|
40
|
-
const DEV_MANAGER_OPTIONS = [
|
|
41
|
-
...DEV_RUNTIME_OPTIONS,
|
|
42
|
-
{ name: "docsAutomation", flags: "--docs-automation <mode>", description: "Control governed documentation automation for the local manager.", kind: "enum", values: ["on", "dry-run", "off"] },
|
|
43
|
-
{ name: "withWorker", flags: "--with-worker", description: "Run the local worker alongside the manager in the same dev supervisor.", kind: "boolean" },
|
|
44
|
-
{ name: "workdayId", flags: "--workday-id <id>", description: "Start or resume a specific local documentation automation workday.", kind: "string" },
|
|
45
|
-
{ name: "capacityBudget", flags: "--capacity-budget <credits>", description: "Override the local workday task credit budget.", kind: "string" },
|
|
46
|
-
{ name: "approvalPolicy", flags: "--approval-policy <policy>", description: "Set the local docs automation approval policy.", kind: "enum", values: ["manual", "low-risk-auto"] }
|
|
47
|
-
];
|
|
48
38
|
function genericWorkflowPosition(spec) {
|
|
49
39
|
if (spec.group === "Workflow") {
|
|
50
40
|
if (spec.name === "switch") return "start work";
|
|
@@ -1172,34 +1162,32 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
1172
1162
|
})],
|
|
1173
1163
|
["dev", command({
|
|
1174
1164
|
options: DEV_RUNTIME_OPTIONS,
|
|
1175
|
-
examples: ["treeseed dev", "treeseed dev --reset", "treeseed dev --reset --plan --json", "treeseed dev --
|
|
1165
|
+
examples: ["treeseed dev", "treeseed dev --reset", "treeseed dev --reset --plan --json", "treeseed dev --web-runtime local --plan --json", "treeseed dev --port 4322"],
|
|
1176
1166
|
help: {
|
|
1177
1167
|
longSummary: [
|
|
1178
|
-
"Dev starts the
|
|
1179
|
-
"
|
|
1168
|
+
"Dev starts the local Treeseed Market web/API runtime as a foreground supervisor.",
|
|
1169
|
+
"Capacity provider lifecycle is package-owned and runs through `treeseed capacity ...`, not through `treeseed dev`."
|
|
1180
1170
|
],
|
|
1181
1171
|
beforeYouRun: [
|
|
1182
1172
|
"Run from the tenant or workspace root you want to develop.",
|
|
1183
|
-
"Use `--plan --json` when you want to inspect
|
|
1173
|
+
"Use `--plan --json` when you want to inspect fixed web/API commands, setup steps, readiness checks, watched paths, and restart policy without starting services.",
|
|
1184
1174
|
"Use `--reset` when you want a fresh local D1 database, Mailpit inbox, generated worker bundle, and Wrangler temp output without deleting configuration.",
|
|
1185
1175
|
"Dev prints the local web URL after readiness; it does not open a browser unless you pass `--open on` or `--open auto`.",
|
|
1186
1176
|
"Keep the foreground process running while you test. Press Ctrl+C to stop the supervised stack and free the local ports."
|
|
1187
1177
|
],
|
|
1188
1178
|
examples: [
|
|
1189
|
-
example("treeseed dev", "Start
|
|
1179
|
+
example("treeseed dev", "Start local Market development", "Run web and API locally and keep supervising them in the foreground."),
|
|
1190
1180
|
example("treeseed dev --reset", "Start from a fresh local runtime", "Clear disposable local dev state, rerun setup and D1 migrations, then start the dev supervisor."),
|
|
1191
1181
|
example("treeseed dev --reset --plan --json", "Inspect reset actions", "Emit the reset, setup, readiness, command, and watch plan without deleting local state or starting services."),
|
|
1192
1182
|
example("treeseed dev --plan --json", "Inspect the runtime plan", "Emit a structured plan with setup steps, commands, ports, URLs, readiness checks, and watch entries."),
|
|
1193
|
-
example("treeseed dev --
|
|
1194
|
-
example("treeseed dev --
|
|
1195
|
-
example("treeseed dev --surfaces integrated,agents", "Opt into agents diagnostics", "Start the integrated local platform plus the agents diagnostic loop."),
|
|
1196
|
-
example("treeseed dev --surface web --port 4322", "Run only the web surface", "Start the Astro UI on a specific port and print the URL when ready."),
|
|
1183
|
+
example("treeseed dev --web-runtime local --plan --json", "Inspect local web runtime", "Plan fixed web/API startup using the local Astro web runtime."),
|
|
1184
|
+
example("treeseed dev --port 4322", "Change the web port", "Start the fixed web/API runtime with the Astro UI on a specific port."),
|
|
1197
1185
|
example("treeseed dev --open on", "Open the browser explicitly", "Start the integrated runtime and launch the local web URL after readiness."),
|
|
1198
1186
|
example("trsd dev", "Use the short alias", "Start the same local runtime through the shorter entrypoint."),
|
|
1199
1187
|
example("treeseed dev --json", "Stream dev events", "Emit newline-delimited events while the long-running dev process supervises local services.")
|
|
1200
1188
|
],
|
|
1201
1189
|
outcomes: [
|
|
1202
|
-
"Starts
|
|
1190
|
+
"Starts fixed local web/API surfaces, waits for readiness, prints the local URL, and then remains attached as the live supervisor.",
|
|
1203
1191
|
"Restarts required crashed surfaces with capped exponential backoff and keeps setup/readiness failures alive for retry.",
|
|
1204
1192
|
"Stops watchers first and then terminates service process groups when the foreground command exits."
|
|
1205
1193
|
]
|
|
@@ -1207,45 +1195,6 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
1207
1195
|
executionMode: "handler",
|
|
1208
1196
|
handlerName: "dev"
|
|
1209
1197
|
})],
|
|
1210
|
-
["dev:manager", command({
|
|
1211
|
-
options: DEV_MANAGER_OPTIONS,
|
|
1212
|
-
examples: ["treeseed dev:manager", "treeseed dev:manager --with-worker", "treeseed dev:manager --docs-automation dry-run --plan --json"],
|
|
1213
|
-
help: {
|
|
1214
|
-
longSummary: [
|
|
1215
|
-
"Dev:manager starts the governed local documentation automation manager through the same integrated dev supervisor used by `treeseed dev`.",
|
|
1216
|
-
"It selects the manager surface by default, can supervise a local worker with `--with-worker`, and passes docs automation policy into the existing manager and worker runtime."
|
|
1217
|
-
],
|
|
1218
|
-
examples: [
|
|
1219
|
-
example("treeseed dev:manager", "Start the local manager", "Run the governed workday manager in the foreground."),
|
|
1220
|
-
example("treeseed dev:manager --with-worker", "Start manager plus worker", "Run scheduling and task execution in the same local supervision session."),
|
|
1221
|
-
example("treeseed dev:manager --docs-automation dry-run --plan --json", "Inspect dry-run manager mode", "Show the manager plan and docs automation settings without starting the supervisor.")
|
|
1222
|
-
],
|
|
1223
|
-
outcomes: [
|
|
1224
|
-
"Starts or plans the existing manager surface, with optional worker supervision.",
|
|
1225
|
-
"Keeps manual approval as the local default and forwards docs automation mode, workday id, capacity budget, and approval policy through runtime environment variables."
|
|
1226
|
-
]
|
|
1227
|
-
},
|
|
1228
|
-
executionMode: "handler",
|
|
1229
|
-
handlerName: "dev"
|
|
1230
|
-
})],
|
|
1231
|
-
["dev:watch", command({
|
|
1232
|
-
options: DEV_RUNTIME_OPTIONS,
|
|
1233
|
-
examples: ["treeseed dev:watch", "treeseed dev:watch --json"],
|
|
1234
|
-
help: {
|
|
1235
|
-
longSummary: [
|
|
1236
|
-
"Dev:watch is a compatibility alias for foreground dev supervision with live feedback enabled.",
|
|
1237
|
-
"It stays attached to the terminal and cleans up supervised services on Ctrl+C just like `dev`."
|
|
1238
|
-
],
|
|
1239
|
-
examples: [
|
|
1240
|
-
example("treeseed dev:watch", "Start watch mode", "Run the local runtime with watch and rebuild behavior enabled in the foreground."),
|
|
1241
|
-
example("trsd dev:watch", "Use the short alias", "Start the same watch-mode runtime through the shorter entrypoint."),
|
|
1242
|
-
example("treeseed dev:watch --feedback restart", "Restart services without browser reload", "Use watcher-driven service restarts while leaving browser refresh to your own tooling."),
|
|
1243
|
-
example("treeseed dev:watch --help", "Inspect watch help", "Read the help surface before starting a longer watch session.")
|
|
1244
|
-
]
|
|
1245
|
-
},
|
|
1246
|
-
executionMode: "handler",
|
|
1247
|
-
handlerName: "dev"
|
|
1248
|
-
})],
|
|
1249
1198
|
["build", command({ examples: ["treeseed build"], help: { longSummary: ["Build runs the tenant build path and produces the generated output for the current project."], examples: [example("treeseed build", "Build the tenant", "Run the packaged build flow for the current project."), example("trsd build", "Use the short alias", "Run the same build through the shorter entrypoint."), example("treeseed build && treeseed export", "Build before packaging context", "Produce build artifacts first and then capture a code export if needed.")] }, executionMode: "adapter" })],
|
|
1250
1199
|
["check", command({ examples: ["treeseed check"], help: { longSummary: ["Check runs the project validation path against the current tenant and shared fixture model."], examples: [example("treeseed check", "Validate the tenant", "Run the project check flow."), example("trsd check", "Use the short alias", "Run the same validation via the short entrypoint."), example("treeseed check && treeseed doctor", "Pair validation with diagnostics", "Follow failed checks with the broader doctor surface.")] }, executionMode: "adapter" })],
|
|
1251
1200
|
["preview", command({ examples: ["treeseed preview"], help: { longSummary: ["Preview serves the built tenant output locally so you can inspect the built site rather than the live dev runtime."], examples: [example("treeseed preview", "Preview the built site", "Run the packaged preview flow for the built tenant."), example("treeseed preview -- --help", "Forward preview help", "Pass through additional args when the preview runtime supports them."), example("treeseed build && treeseed preview", "Build then preview", "Generate the build output first and then serve it locally.")] }, executionMode: "adapter", buildAdapterInput: PASS_THROUGH_ARGS })],
|
|
@@ -1571,36 +1520,39 @@ const CLI_ONLY_OPERATION_SPECS = [
|
|
|
1571
1520
|
name: "capacity",
|
|
1572
1521
|
aliases: [],
|
|
1573
1522
|
group: "Utilities",
|
|
1574
|
-
summary: "
|
|
1575
|
-
description: "
|
|
1523
|
+
summary: "Operate the package-owned capacity provider runtime.",
|
|
1524
|
+
description: "Run provider diagnostics, registration, and planning through the built @treeseed/agent capacity provider entrypoint.",
|
|
1576
1525
|
provider: "default",
|
|
1577
1526
|
related: ["teams", "projects", "agents"],
|
|
1578
|
-
usage: "treeseed capacity [status|
|
|
1527
|
+
usage: "treeseed capacity [doctor|register|plan|up|down|restart|logs|status|build|test-local]",
|
|
1579
1528
|
arguments: [{ name: "action", description: "Capacity action.", required: false }],
|
|
1580
1529
|
options: [
|
|
1581
1530
|
{ name: "market", flags: "--market <id-or-url>", description: "Select a configured market id or direct market API URL.", kind: "string" },
|
|
1582
|
-
{ name: "
|
|
1583
|
-
{ name: "
|
|
1584
|
-
{ name: "
|
|
1585
|
-
{ name: "
|
|
1586
|
-
{ name: "
|
|
1587
|
-
{ name: "
|
|
1588
|
-
{ name: "daily", flags: "--daily <credits>", description: "Daily credit limit for a created grant.", kind: "string" },
|
|
1589
|
-
{ name: "monthly", flags: "--monthly <credits>", description: "Monthly credit limit for a created grant.", kind: "string" },
|
|
1531
|
+
{ name: "provider", flags: "--provider <provider-id>", description: "Provider id or local provider selector for diagnostics.", kind: "string" },
|
|
1532
|
+
{ name: "environment", flags: "--environment <scope>", description: "Treeseed config scope used when resolving encrypted provider launch values.", kind: "enum", values: ["local", "staging", "prod"] },
|
|
1533
|
+
{ name: "dataDir", flags: "--data-dir <path>", description: "Host data directory mounted into the provider container at /data.", kind: "string" },
|
|
1534
|
+
{ name: "agentPackageRoot", flags: "--agent-package-root <path>", description: "Path to a built @treeseed/agent package root.", kind: "string" },
|
|
1535
|
+
{ name: "diagnostic", flags: "--diagnostic", description: "Start lifecycle commands without live Market registration or provider credentials.", kind: "boolean" },
|
|
1536
|
+
{ name: "dryRun", flags: "--dry-run", description: "Ask the provider runtime to render deterministic output without provider secrets or Market mutation.", kind: "boolean" },
|
|
1590
1537
|
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
1591
1538
|
],
|
|
1592
1539
|
examples: [
|
|
1593
|
-
"treeseed capacity
|
|
1594
|
-
"treeseed capacity
|
|
1595
|
-
"treeseed capacity
|
|
1596
|
-
"treeseed capacity
|
|
1597
|
-
"treeseed capacity
|
|
1540
|
+
"treeseed capacity doctor --market local --provider local",
|
|
1541
|
+
"treeseed capacity register --market local --provider local --dry-run --json",
|
|
1542
|
+
"treeseed capacity plan --market local --provider local --dry-run --json",
|
|
1543
|
+
"treeseed capacity build",
|
|
1544
|
+
"treeseed capacity up --market local --provider local",
|
|
1545
|
+
"treeseed capacity up --market local --provider local --diagnostic",
|
|
1546
|
+
"treeseed capacity status --market local --provider local",
|
|
1547
|
+
"treeseed capacity logs --market local --provider local",
|
|
1548
|
+
"treeseed capacity down --market local --provider local",
|
|
1549
|
+
"treeseed capacity test-local"
|
|
1598
1550
|
],
|
|
1599
1551
|
help: {
|
|
1600
|
-
longSummary: ["Capacity
|
|
1601
|
-
whenToUse: ["Use this
|
|
1602
|
-
beforeYouRun: ["
|
|
1603
|
-
automationNotes: ["Use `--json`
|
|
1552
|
+
longSummary: ["Capacity invokes the package-owned @treeseed/agent provider runtime for local diagnostics, registration previews, portfolio planning, and Docker/Compose lifecycle commands."],
|
|
1553
|
+
whenToUse: ["Use this when validating a self-hosted capacity provider install, checking provider runtime output, or running the local provider stack."],
|
|
1554
|
+
beforeYouRun: ["Build @treeseed/agent first, or pass --agent-package-root to a built package. Use `trsd config` for encrypted provider values; capacity commands do not write plaintext env files."],
|
|
1555
|
+
automationNotes: ["Use `--json` for stable provider runtime output. `up` runs live local provider mode by default; pass `--diagnostic` or use `test-local` for diagnostics without provider secrets."]
|
|
1604
1556
|
},
|
|
1605
1557
|
helpVisible: true,
|
|
1606
1558
|
helpFeatured: false,
|
package/dist/cli/registry.d.ts
CHANGED
|
@@ -10,8 +10,6 @@ export declare const COMMAND_HANDLERS: {
|
|
|
10
10
|
readonly status: import("./operations-types.js").TreeseedCommandHandler;
|
|
11
11
|
readonly ci: import("./operations-types.js").TreeseedCommandHandler;
|
|
12
12
|
readonly dev: import("./operations-types.js").TreeseedCommandHandler;
|
|
13
|
-
readonly 'dev:manager': import("./operations-types.js").TreeseedCommandHandler;
|
|
14
|
-
readonly 'dev:watch': import("./operations-types.js").TreeseedCommandHandler;
|
|
15
13
|
readonly doctor: import("./operations-types.js").TreeseedCommandHandler;
|
|
16
14
|
readonly rollback: import("./operations-types.js").TreeseedCommandHandler;
|
|
17
15
|
readonly template: import("./operations-types.js").TreeseedCommandHandler;
|
package/dist/cli/registry.js
CHANGED
package/dist/cli/runtime.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "Operator-facing Treeseed CLI package.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"release:publish": "node ./scripts/run-ts.mjs ./scripts/publish-package.ts"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@treeseed/sdk": "0.
|
|
48
|
+
"@treeseed/sdk": "github:treeseed-ai/sdk#0.10.6",
|
|
49
49
|
"ink": "^7.0.0",
|
|
50
50
|
"react": "^19.2.5"
|
|
51
51
|
},
|