@treeseed/cli 0.8.19 → 0.9.0

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.
@@ -67,6 +67,7 @@ const handleDev = async (invocation, context) => {
67
67
  }
68
68
  forwardStringOption("host", "--host");
69
69
  forwardStringOption("port", "--port");
70
+ forwardStringOption("webRuntime", "--web-runtime");
70
71
  forwardStringOption("apiHost", "--api-host");
71
72
  forwardStringOption("apiPort", "--api-port");
72
73
  forwardStringOption("managerPort", "--manager-port");
@@ -75,6 +76,7 @@ const handleDev = async (invocation, context) => {
75
76
  forwardStringOption("open", "--open");
76
77
  forwardBooleanOption("plan", "--plan");
77
78
  forwardBooleanOption("reset", "--reset");
79
+ forwardBooleanOption("force", "--force");
78
80
  forwardBooleanOption("json", "--json");
79
81
  const docsAutomationMode = typeof invocation.args.docsAutomation === "string" ? invocation.args.docsAutomation.trim() : "";
80
82
  const workdayId = typeof invocation.args.workdayId === "string" ? invocation.args.workdayId.trim() : "";
@@ -87,7 +89,9 @@ const handleDev = async (invocation, context) => {
87
89
  TREESEED_CAPACITY_BUDGET: capacityBudget,
88
90
  TREESEED_WORKDAY_TASK_CREDIT_BUDGET: capacityBudget
89
91
  } : {},
90
- TREESEED_APPROVAL_POLICY: approvalPolicy || "manual"
92
+ TREESEED_APPROVAL_POLICY: approvalPolicy || "manual",
93
+ TREESEED_MANAGER_CONSOLE_SUMMARY: "true",
94
+ TREESEED_WORKER_CONSOLE_SUMMARY: "true"
91
95
  } : {};
92
96
  const workspaceRoot = findNearestTreeseedWorkspaceRoot(context.cwd);
93
97
  const workspaceLinksMode = typeof invocation.args.workspaceLinks === "string" ? invocation.args.workspaceLinks : void 0;
@@ -102,7 +106,8 @@ const handleDev = async (invocation, context) => {
102
106
  env: resolveTreeseedLaunchEnvironment({
103
107
  tenantRoot: context.cwd,
104
108
  scope: "local",
105
- baseEnv: { ...process.env, ...context.env ?? {}, ...devManagerEnv }
109
+ baseEnv: { ...process.env, ...context.env ?? {} },
110
+ overrides: devManagerEnv
106
111
  }),
107
112
  stdio: "inherit"
108
113
  });
@@ -4,19 +4,54 @@ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
4
4
  import { formatSeedDiagnostics, formatSeedPlan, loadAndPlanSeed } from "@treeseed/sdk/seeds";
5
5
  import { MarketApiError } from "@treeseed/sdk/market-client";
6
6
  import { createMarketClientForInvocation, marketAuthRoot, marketSelector } from "./market-utils.js";
7
- import { resolveMarketProfile, resolveMarketSession } from "@treeseed/sdk/market-client";
7
+ import { MarketClient, resolveMarketProfile, resolveMarketSession, setMarketSession } from "@treeseed/sdk/market-client";
8
8
  async function loadLocalSeedModule(projectRoot) {
9
9
  const apiModulePath = resolve(projectRoot, "src", "lib", "market", "seeds", "local-api.js");
10
- const moduleUrl = pathToFileURL(existsSync(apiModulePath) ? apiModulePath : resolve(projectRoot, "src", "lib", "market", "seeds", "apply.js")).href;
11
- return await import(moduleUrl);
10
+ const applyModulePath = resolve(projectRoot, "src", "lib", "market", "seeds", "apply.js");
11
+ const applyModule = await import(pathToFileURL(applyModulePath).href);
12
+ if (!existsSync(apiModulePath)) {
13
+ return applyModule;
14
+ }
15
+ const apiModule = await import(pathToFileURL(apiModulePath).href);
16
+ return {
17
+ ...applyModule,
18
+ ...apiModule
19
+ };
12
20
  }
13
- function requireLocalSeedSession(invocation, context) {
21
+ function sessionExpiresSoon(session) {
22
+ if (!session.expiresAt) return false;
23
+ const expiresAt = Date.parse(session.expiresAt);
24
+ return Number.isFinite(expiresAt) && expiresAt <= Date.now() + 6e4;
25
+ }
26
+ async function requireLocalSeedSession(invocation, context) {
14
27
  const selector = marketSelector(invocation) ?? "local";
15
28
  const profile = resolveMarketProfile(selector);
16
- const session = resolveMarketSession(marketAuthRoot(context), profile.id);
29
+ const tenantRoot = marketAuthRoot(context);
30
+ const session = resolveMarketSession(tenantRoot, profile.id);
17
31
  if (!session?.accessToken) {
18
32
  throw new Error(`Not logged in to market "${profile.id}". Run treeseed auth:login --market ${profile.id}.`);
19
33
  }
34
+ if (sessionExpiresSoon(session) && session.refreshToken) {
35
+ try {
36
+ const refreshed = await new MarketClient({ profile, userAgent: "treeseed-cli" }).refreshToken({ refreshToken: session.refreshToken });
37
+ if (refreshed.ok) {
38
+ const nextSession = {
39
+ marketId: profile.id,
40
+ accessToken: refreshed.accessToken,
41
+ refreshToken: refreshed.refreshToken,
42
+ expiresAt: refreshed.expiresAt,
43
+ principal: refreshed.principal
44
+ };
45
+ setMarketSession(tenantRoot, nextSession);
46
+ return { profile, session: nextSession };
47
+ }
48
+ } catch {
49
+ throw new Error(`Login for market "${profile.id}" expired. Run treeseed auth:login --market ${profile.id}.`);
50
+ }
51
+ }
52
+ if (sessionExpiresSoon(session)) {
53
+ throw new Error(`Login for market "${profile.id}" expired. Run treeseed auth:login --market ${profile.id}.`);
54
+ }
20
55
  return { profile, session };
21
56
  }
22
57
  function seedRequestBody(invocation) {
@@ -127,7 +162,7 @@ async function handleSeedExport(invocation, context) {
127
162
  const { client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
128
163
  payload = await client.exportSeed(team, body);
129
164
  } else {
130
- const { session } = requireLocalSeedSession(invocation, context);
165
+ const { session } = await requireLocalSeedSession(invocation, context);
131
166
  const localModule = await loadLocalSeedModule(context.cwd);
132
167
  const exporter = localModule.exportLocalSeedViaApiFromCli ?? localModule.exportSeedFromCli;
133
168
  if (typeof exporter !== "function") {
@@ -201,27 +236,16 @@ const handleSeed = async (invocation, context) => {
201
236
  }
202
237
  };
203
238
  }
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
239
  if (!wantsApply && !wantsValidate && planned.plan.environments.length === 1 && planned.plan.environments[0] === "local") {
215
240
  try {
216
241
  const localModule = await loadLocalSeedModule(context.cwd);
217
- const localPlanner = localModule.planLocalSeedViaApiFromCli ?? localModule.planLocalSeedFromCli;
242
+ const localPlanner = localModule.planLocalSeedFromCli ?? localModule.planLocalSeedViaApiFromCli;
218
243
  if (typeof localPlanner === "function") {
219
244
  const localPlanned = await localPlanner({
220
245
  projectRoot: context.cwd,
221
246
  seedName,
222
247
  environments: typeof invocation.args.environments === "string" ? invocation.args.environments : void 0,
223
248
  mode,
224
- accessToken: localAuth?.session.accessToken,
225
249
  env: context.env
226
250
  });
227
251
  if (localPlanned.plan) {
@@ -250,6 +274,12 @@ const handleSeed = async (invocation, context) => {
250
274
  return remoteSeedError(error, "seed");
251
275
  }
252
276
  }
277
+ let localAuth;
278
+ try {
279
+ localAuth = await requireLocalSeedSession(invocation, context);
280
+ } catch (error) {
281
+ return remoteSeedError(error, "seed");
282
+ }
253
283
  const localModule = await loadLocalSeedModule(context.cwd);
254
284
  const runner = localModule.applyLocalSeedViaApiFromCli ?? localModule.applyLocalSeedFromCli;
255
285
  if (typeof runner !== "function") {
@@ -25,6 +25,7 @@ const DEV_RUNTIME_OPTIONS = [
25
25
  { name: "surfaces", flags: "--surfaces <surfaces>", description: "Select comma-separated local dev surfaces to run.", kind: "string" },
26
26
  { name: "host", flags: "--host <host>", description: "Host for the web dev server.", kind: "string" },
27
27
  { name: "port", flags: "--port <port>", description: "Port for the web dev server.", kind: "string" },
28
+ { 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
29
  { name: "apiHost", flags: "--api-host <host>", description: "Host used to construct the local API URL.", kind: "string" },
29
30
  { name: "apiPort", flags: "--api-port <port>", description: "Port for the local API server.", kind: "string" },
30
31
  { name: "managerPort", flags: "--manager-port <port>", description: "Port used for the local manager service URL.", kind: "string" },
@@ -33,6 +34,7 @@ const DEV_RUNTIME_OPTIONS = [
33
34
  { 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
35
  { name: "plan", flags: "--plan", description: "Print the dev runtime plan and exit without starting services.", kind: "boolean" },
35
36
  { name: "reset", flags: "--reset", description: "Clear local dev runtime state before setup, migrations, and service startup.", kind: "boolean" },
37
+ { name: "force", flags: "--force", description: "Terminate overlapping Treeseed dev runtimes and listeners on required local dev ports before startup.", kind: "boolean" },
36
38
  { name: "json", flags: "--json", description: "Emit structured JSON or newline-delimited dev events.", kind: "boolean" },
37
39
  { name: "watch", flags: "--watch", description: "Enable live watch behavior. `dev` defaults to live feedback; this remains for compatibility.", kind: "boolean" },
38
40
  { name: "workspaceLinks", flags: "--workspace-links <mode>", description: "Control local workspace package links.", kind: "enum", values: ["auto", "off"] }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/cli",
3
- "version": "0.8.19",
3
+ "version": "0.9.0",
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.8.19",
48
+ "@treeseed/sdk": "0.9.0",
49
49
  "ink": "^7.0.0",
50
50
  "react": "^19.2.5"
51
51
  },