@staff0rd/assist 0.413.0 → 0.414.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.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.413.0",
9
+ version: "0.414.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -134,12 +134,15 @@ import chalk2 from "chalk";
134
134
  import { Pool } from "pg";
135
135
 
136
136
  // src/shared/loadConfig.ts
137
- import { existsSync as existsSync2, writeFileSync } from "fs";
138
- import { homedir } from "os";
139
- import { dirname, join } from "path";
137
+ import { writeFileSync } from "fs";
140
138
  import chalk from "chalk";
141
139
  import { stringify as stringifyYaml } from "yaml";
142
140
 
141
+ // src/shared/loadConfigFrom.ts
142
+ import { existsSync as existsSync2 } from "fs";
143
+ import { homedir } from "os";
144
+ import { dirname, join } from "path";
145
+
143
146
  // src/commands/backlog/getCurrentOrigin.ts
144
147
  import { execFileSync } from "child_process";
145
148
  function stripLeadingSlashes(path71) {
@@ -332,7 +335,9 @@ var runConfigSchema = z.strictObject({
332
335
  filter: z.string().optional(),
333
336
  pre: z.array(z.string()).optional(),
334
337
  cwd: z.string().optional(),
335
- quiet: z.boolean().optional()
338
+ quiet: z.boolean().optional(),
339
+ server: z.boolean().optional(),
340
+ port: z.number().optional()
336
341
  });
337
342
  var runLinkSchema = z.strictObject({
338
343
  link: z.string(),
@@ -506,7 +511,7 @@ var assistConfigSchema = z2.strictObject({
506
511
  })
507
512
  });
508
513
 
509
- // src/shared/loadConfig.ts
514
+ // src/shared/loadConfigFrom.ts
510
515
  function findConfigUp(startDir) {
511
516
  let current = startDir;
512
517
  while (current !== dirname(current)) {
@@ -519,31 +524,42 @@ function findConfigUp(startDir) {
519
524
  }
520
525
  return null;
521
526
  }
522
- function getConfigPath() {
523
- const found = findConfigUp(process.cwd());
527
+ function getConfigPathFrom(cwd) {
528
+ const found = findConfigUp(cwd);
524
529
  if (found) return found.configPath;
525
- return join(process.cwd(), "assist.yml");
530
+ return join(cwd, "assist.yml");
526
531
  }
527
532
  function getGlobalConfigPath() {
528
533
  return join(homedir(), ".assist.yml");
529
534
  }
530
- function getConfigDir() {
531
- return dirname(getConfigPath());
532
- }
533
- function getProjectRoot() {
534
- const found = findConfigUp(process.cwd());
535
- return found?.rootDir ?? process.cwd();
535
+ function getConfigDirFrom(cwd) {
536
+ return dirname(getConfigPathFrom(cwd));
536
537
  }
537
- function loadConfig() {
538
+ function loadConfigFrom(cwd) {
538
539
  const globalRaw = loadRawYaml(getGlobalConfigPath());
539
- const projectRaw = loadRawYaml(getConfigPath());
540
- const repoOverride = globalRaw.repos ? resolveRepoOverride(globalRaw, getCurrentOrigin(process.cwd())) : {};
540
+ const projectRaw = loadRawYaml(getConfigPathFrom(cwd));
541
+ const repoOverride = globalRaw.repos ? resolveRepoOverride(globalRaw, getCurrentOrigin(cwd)) : {};
541
542
  const globalWithRepo = mergeRawConfigs(globalRaw, repoOverride);
542
543
  const merged = mergeRawConfigs(globalWithRepo, projectRaw);
543
544
  delete merged.repos;
544
545
  delete merged.news;
545
546
  return assistConfigSchema.parse(merged);
546
547
  }
548
+
549
+ // src/shared/loadConfig.ts
550
+ function getConfigPath() {
551
+ return getConfigPathFrom(process.cwd());
552
+ }
553
+ function getConfigDir() {
554
+ return getConfigDirFrom(process.cwd());
555
+ }
556
+ function getProjectRoot() {
557
+ const found = findConfigUp(process.cwd());
558
+ return found?.rootDir ?? process.cwd();
559
+ }
560
+ function loadConfig() {
561
+ return loadConfigFrom(process.cwd());
562
+ }
547
563
  function loadProjectConfig() {
548
564
  return loadRawYaml(getConfigPath());
549
565
  }
@@ -8504,6 +8520,32 @@ function getHtml() {
8504
8520
  </html>`;
8505
8521
  }
8506
8522
 
8523
+ // src/commands/sessions/web/getCwdParam.ts
8524
+ function getCwdParam(req, res) {
8525
+ const url = new URL(req.url ?? "/", "http://localhost");
8526
+ const cwd = url.searchParams.get("cwd");
8527
+ if (!cwd) {
8528
+ respondJson(res, 400, { error: "Missing cwd" });
8529
+ return null;
8530
+ }
8531
+ return cwd;
8532
+ }
8533
+
8534
+ // src/commands/sessions/web/handleServerRuns.ts
8535
+ function getServerRuns(cwd) {
8536
+ try {
8537
+ const { run: run4 } = loadConfigFrom(cwd);
8538
+ return resolveRunConfigs(run4, getConfigDirFrom(cwd)).filter((r) => r.server).map((r) => ({ name: r.name, port: r.port }));
8539
+ } catch {
8540
+ return [];
8541
+ }
8542
+ }
8543
+ function handleServerRuns(req, res) {
8544
+ const cwd = getCwdParam(req, res);
8545
+ if (!cwd) return;
8546
+ respondJson(res, 200, { runs: getServerRuns(cwd) });
8547
+ }
8548
+
8507
8549
  // src/commands/sessions/web/getReviewSynthesis.ts
8508
8550
  import { execFile as execFile2 } from "child_process";
8509
8551
  import { readFileSync as readFileSync19 } from "fs";
@@ -8523,17 +8565,6 @@ function findSynthesisForBranch(repoReviewsDir, branch2) {
8523
8565
  return synthesisFiles[0]?.path ?? null;
8524
8566
  }
8525
8567
 
8526
- // src/commands/sessions/web/getCwdParam.ts
8527
- function getCwdParam(req, res) {
8528
- const url = new URL(req.url ?? "/", "http://localhost");
8529
- const cwd = url.searchParams.get("cwd");
8530
- if (!cwd) {
8531
- respondJson(res, 400, { error: "Missing cwd" });
8532
- return null;
8533
- }
8534
- return cwd;
8535
- }
8536
-
8537
8568
  // src/commands/sessions/web/windowsCwdToWslPath.ts
8538
8569
  function windowsCwdToWslPath(cwd) {
8539
8570
  const match = /^([A-Za-z]):[\\/](.*)$/.exec(cwd);
@@ -9202,6 +9233,7 @@ var routes2 = {
9202
9233
  "GET /api/jira-site": jiraSite,
9203
9234
  "GET /api/harness": harnessCapabilities,
9204
9235
  "GET /api/pr-status": prStatus,
9236
+ "GET /api/server-runs": handleServerRuns,
9205
9237
  "GET /api/pr-list": prList,
9206
9238
  "GET /api/news/items": listNewsItems,
9207
9239
  "GET /api/usage/history": listUsageHistory,
@@ -24759,6 +24791,8 @@ function toSessionInfo({
24759
24791
  runningSince,
24760
24792
  runName,
24761
24793
  runArgs,
24794
+ server,
24795
+ serverPort,
24762
24796
  assistArgs,
24763
24797
  cwd,
24764
24798
  restored,
@@ -24784,6 +24818,8 @@ function toSessionInfo({
24784
24818
  runningSince,
24785
24819
  runName,
24786
24820
  runArgs,
24821
+ server,
24822
+ port: serverPort,
24787
24823
  assistArgs,
24788
24824
  cwd,
24789
24825
  restored,
@@ -24957,6 +24993,28 @@ function createAssistSession(id, assistArgs, cwd, meta) {
24957
24993
  // src/commands/sessions/daemon/createSession.ts
24958
24994
  import { randomUUID as randomUUID4 } from "crypto";
24959
24995
 
24996
+ // src/commands/sessions/daemon/resolveRunConfig.ts
24997
+ function resolveRunConfig(name, cwd) {
24998
+ try {
24999
+ const { run: run4 } = loadConfigFrom(cwd);
25000
+ const configs = resolveRunConfigs(run4, getConfigDirFrom(cwd));
25001
+ const exact = configs.find((r) => r.name === name);
25002
+ if (exact) return exact;
25003
+ const suffixMatches = configs.filter((r) => r.name.endsWith(`:${name}`));
25004
+ return suffixMatches.length === 1 ? suffixMatches[0] : void 0;
25005
+ } catch {
25006
+ return void 0;
25007
+ }
25008
+ }
25009
+
25010
+ // src/commands/sessions/daemon/serverRunMeta.ts
25011
+ function serverRunMeta(runName, cwd) {
25012
+ const dir = cwd ?? process.cwd();
25013
+ const config = resolveRunConfig(runName, dir);
25014
+ if (!config?.server) return { server: false };
25015
+ return { server: true, port: config.port, origin: getCurrentOrigin(dir) };
25016
+ }
25017
+
24960
25018
  // src/commands/sessions/daemon/readDesignSystemPrompt.ts
24961
25019
  import * as fs35 from "fs";
24962
25020
  import * as path59 from "path";
@@ -25037,7 +25095,7 @@ function createPiSession(id, prompt, cwd) {
25037
25095
  initialPrompt: prompt
25038
25096
  };
25039
25097
  }
25040
- function createRunSession(id, runName, runArgs, cwd) {
25098
+ function createRunSession(id, runName, runArgs, cwd, meta = serverRunMeta(runName, cwd)) {
25041
25099
  return {
25042
25100
  ...sessionBase(id, "running"),
25043
25101
  name: `run: ${runName}`,
@@ -25045,7 +25103,10 @@ function createRunSession(id, runName, runArgs, cwd) {
25045
25103
  pty: spawnRun({ name: runName, args: runArgs, cwd }),
25046
25104
  runName,
25047
25105
  runArgs,
25048
- cwd
25106
+ cwd,
25107
+ server: meta.server || void 0,
25108
+ serverPort: meta.port,
25109
+ serverOrigin: meta.origin
25049
25110
  };
25050
25111
  }
25051
25112
 
@@ -25442,6 +25503,18 @@ function errorTail(scrollback) {
25442
25503
  return tail.length > MAX_TAIL_CHARS ? tail.slice(-MAX_TAIL_CHARS) : tail;
25443
25504
  }
25444
25505
 
25506
+ // src/commands/sessions/daemon/handleFailedResume.ts
25507
+ function handleFailedResume(session, exitCode, onStatusChange) {
25508
+ const failed2 = session.restored === true && exitCode !== 0 && session.scrollback.length === 0;
25509
+ if (!failed2) return false;
25510
+ session.error = `resume process exited with code ${exitCode} before producing any output`;
25511
+ daemonLog(
25512
+ `could not resume restored session "${session.name}" (id ${session.id}): ${session.error}`
25513
+ );
25514
+ onStatusChange(session, "error", exitCode);
25515
+ return true;
25516
+ }
25517
+
25445
25518
  // src/commands/sessions/daemon/watchActivity.ts
25446
25519
  import { existsSync as existsSync58, mkdirSync as mkdirSync25, watch } from "fs";
25447
25520
  import { dirname as dirname29 } from "path";
@@ -25535,6 +25608,18 @@ function refreshActivity(session) {
25535
25608
  session.claudeSessionId = activity2.claudeSessionId;
25536
25609
  }
25537
25610
 
25611
+ // src/commands/sessions/daemon/handleStoppedExit.ts
25612
+ function handleStoppedExit(session, exitCode, onStatusChange) {
25613
+ if (!session.stopping) return false;
25614
+ session.stopping = void 0;
25615
+ refreshActivity(session);
25616
+ daemonLog(
25617
+ `session ${session.id} ("${session.name}") pty exited (code ${exitCode}) after stop request; marking done`
25618
+ );
25619
+ onStatusChange(session, "done", 0);
25620
+ return true;
25621
+ }
25622
+
25538
25623
  // src/commands/sessions/daemon/handlePtyExit.ts
25539
25624
  function handlePtyExit(session, exitCode, onStatusChange) {
25540
25625
  if (session.pendingRestart) {
@@ -25546,16 +25631,9 @@ function handlePtyExit(session, exitCode, onStatusChange) {
25546
25631
  resume();
25547
25632
  return;
25548
25633
  }
25634
+ if (handleStoppedExit(session, exitCode, onStatusChange)) return;
25549
25635
  refreshActivity(session);
25550
- const failedResume = session.restored === true && exitCode !== 0 && session.scrollback.length === 0;
25551
- if (failedResume) {
25552
- session.error = `resume process exited with code ${exitCode} before producing any output`;
25553
- daemonLog(
25554
- `could not resume restored session "${session.name}" (id ${session.id}): ${session.error}`
25555
- );
25556
- onStatusChange(session, "error", exitCode);
25557
- return;
25558
- }
25636
+ if (handleFailedResume(session, exitCode, onStatusChange)) return;
25559
25637
  const priorStatus = session.status;
25560
25638
  if (exitCode !== 0) {
25561
25639
  const tail = errorTail(session.scrollback);
@@ -26170,6 +26248,59 @@ function respawnThunk(session) {
26170
26248
  return null;
26171
26249
  }
26172
26250
 
26251
+ // src/commands/sessions/daemon/serverConflictInfo.ts
26252
+ function serverConflictInfo(session) {
26253
+ return {
26254
+ id: session.id,
26255
+ name: session.name,
26256
+ cwd: session.cwd,
26257
+ port: session.serverPort
26258
+ };
26259
+ }
26260
+
26261
+ // src/commands/sessions/daemon/liveServerRun.ts
26262
+ function liveServerRun(sessions, origin, excludeId) {
26263
+ for (const s of sessions.values()) {
26264
+ if (s.id === excludeId || !s.server || s.serverOrigin !== origin) continue;
26265
+ if (s.status !== "done" && s.status !== "error") return s;
26266
+ }
26267
+ return void 0;
26268
+ }
26269
+ function stopServerSession(sessions, id) {
26270
+ const s = sessions.get(id);
26271
+ if (!s?.pty || s.status === "done" || s.status === "error") return;
26272
+ s.stopping = true;
26273
+ daemonLog(`session ${id} stopped (${s.name})`);
26274
+ s.pty.kill();
26275
+ }
26276
+
26277
+ // src/commands/sessions/daemon/runRetry.ts
26278
+ function tagServerSession(session, meta) {
26279
+ session.server = meta.server || void 0;
26280
+ session.serverPort = meta.port;
26281
+ session.serverOrigin = meta.origin;
26282
+ }
26283
+ function findRetryConflict(sessions, session, replace, dismiss) {
26284
+ const meta = serverRunMeta(session.runName ?? "", session.cwd);
26285
+ if (meta.server && meta.origin) {
26286
+ const existing = liveServerRun(sessions, meta.origin, session.id);
26287
+ if (existing && !replace) return serverConflictInfo(existing);
26288
+ if (existing) dismiss(existing.id);
26289
+ }
26290
+ tagServerSession(session, meta);
26291
+ return null;
26292
+ }
26293
+ function runRetry(sessions, id, replace, deps2) {
26294
+ const s = sessions.get(id);
26295
+ if (!s) return null;
26296
+ if (s.commandType === "run" && s.runName) {
26297
+ const conflict = findRetryConflict(sessions, s, replace, deps2.dismiss);
26298
+ if (conflict) return conflict;
26299
+ }
26300
+ if (retrySession(s, deps2.clients, deps2.onStatusChange)) deps2.notify();
26301
+ return null;
26302
+ }
26303
+
26173
26304
  // src/commands/sessions/daemon/reuseSessionForRun.ts
26174
26305
  function reuseSessionForRun(session, itemId2, clients, onStatusChange) {
26175
26306
  const assistArgs = ["backlog", "run", String(itemId2)];
@@ -27044,8 +27175,13 @@ var SessionManager = class {
27044
27175
  (id) => createSession(id, prompt, cwd, design, harness)
27045
27176
  );
27046
27177
  }
27047
- spawnRun(runName, runArgs, cwd) {
27048
- return this.spawnWith((id) => createRunSession(id, runName, runArgs, cwd));
27178
+ spawnRun(runName, runArgs, cwd, meta) {
27179
+ return this.spawnWith(
27180
+ (id) => createRunSession(id, runName, runArgs, cwd, meta)
27181
+ );
27182
+ }
27183
+ liveServerRun(origin, excludeId) {
27184
+ return liveServerRun(this.sessions, origin, excludeId);
27049
27185
  }
27050
27186
  spawnAssist(assistArgs, cwd, meta) {
27051
27187
  return this.spawnWith(
@@ -27071,9 +27207,13 @@ var SessionManager = class {
27071
27207
  resizeSession(id, cols, rows) {
27072
27208
  resizeSession(this.sessions, id, cols, rows);
27073
27209
  }
27074
- retrySession(id) {
27075
- const s = this.sessions.get(id);
27076
- if (s && retrySession(s, this.clients, this.onStatusChange)) this.notify();
27210
+ retrySession(id, replace = false) {
27211
+ return runRetry(this.sessions, id, replace, {
27212
+ clients: this.clients,
27213
+ onStatusChange: this.onStatusChange,
27214
+ dismiss: this.dismissSession,
27215
+ notify: this.notify
27216
+ });
27077
27217
  }
27078
27218
  restart(id) {
27079
27219
  const result = restartManagedSession(
@@ -27088,6 +27228,9 @@ var SessionManager = class {
27088
27228
  dismissSession = (id) => {
27089
27229
  if (dismissSession(this.sessions, id)) this.notify();
27090
27230
  };
27231
+ stopSession(id) {
27232
+ stopServerSession(this.sessions, id);
27233
+ }
27091
27234
  setAutoRun(id, enabled) {
27092
27235
  if (setAutoRun(this.sessions, id, enabled)) this.notify();
27093
27236
  }
@@ -27139,6 +27282,41 @@ function creator(isNew, spawn12) {
27139
27282
  };
27140
27283
  }
27141
27284
 
27285
+ // src/commands/sessions/daemon/handleCreateRun.ts
27286
+ function handleCreateRun(client, m, d) {
27287
+ if (m.windowsProxy.route(client, d)) return;
27288
+ const runName = d.runName;
27289
+ const cwd = d.cwd;
27290
+ const runArgs = d.runArgs ?? [];
27291
+ const meta = serverRunMeta(runName, cwd);
27292
+ if (meta.server && meta.origin) {
27293
+ const existing = m.liveServerRun(meta.origin);
27294
+ if (existing && d.replace !== true) {
27295
+ daemonLog(
27296
+ `create-run ${runName} rejected: server ${existing.id} already live for ${meta.origin}`
27297
+ );
27298
+ sendTo(client, {
27299
+ type: "run-conflict",
27300
+ runName,
27301
+ cwd,
27302
+ existing: serverConflictInfo(existing)
27303
+ });
27304
+ return;
27305
+ }
27306
+ if (existing) {
27307
+ daemonLog(
27308
+ `create-run ${runName} replacing live server ${existing.id} for ${meta.origin}`
27309
+ );
27310
+ m.dismissSession(existing.id);
27311
+ }
27312
+ }
27313
+ sendTo(client, {
27314
+ type: "created",
27315
+ sessionId: m.spawnRun(runName, runArgs, cwd, meta),
27316
+ isNew: true
27317
+ });
27318
+ }
27319
+
27142
27320
  // src/commands/sessions/daemon/handleSetStatus.ts
27143
27321
  function handleSetStatus(client, m, d) {
27144
27322
  daemonLog(
@@ -27280,14 +27458,7 @@ var messageHandlers = {
27280
27458
  "subscribe-logs": (client, m, d) => m.clients.subscribeLogs(client, d.replay !== false),
27281
27459
  hello: (client) => sendTo(client, buildHello()),
27282
27460
  create: creator(true, spawnCreate),
27283
- "create-run": creator(
27284
- true,
27285
- (m, d) => m.spawnRun(
27286
- d.runName,
27287
- d.runArgs ?? [],
27288
- d.cwd
27289
- )
27290
- ),
27461
+ "create-run": handleCreateRun,
27291
27462
  "create-assist": creator(
27292
27463
  true,
27293
27464
  (m, d) => m.spawnAssist(
@@ -27321,13 +27492,22 @@ var messageHandlers = {
27321
27492
  resize: routed(
27322
27493
  (_client, m, d) => m.resizeSession(d.sessionId, d.cols, d.rows)
27323
27494
  ),
27324
- retry: routed((_client, m, d) => m.retrySession(d.sessionId)),
27495
+ retry: routed((client, m, d) => {
27496
+ const conflict = m.retrySession(d.sessionId, d.replace === true);
27497
+ if (conflict)
27498
+ sendTo(client, {
27499
+ type: "run-conflict",
27500
+ sessionId: d.sessionId,
27501
+ existing: conflict
27502
+ });
27503
+ }),
27325
27504
  restart: routed((client, m, d) => {
27326
27505
  const result = m.restart(d.sessionId);
27327
27506
  if (!result.ok && result.reason)
27328
27507
  sendTo(client, { type: "error", message: result.reason });
27329
27508
  }),
27330
27509
  dismiss: routed((_client, m, d) => m.dismissSession(d.sessionId)),
27510
+ stop: routed((_client, m, d) => m.stopSession(d.sessionId)),
27331
27511
  "set-autorun": routed(
27332
27512
  (_client, m, d) => m.setAutoRun(d.sessionId, d.enabled)
27333
27513
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.413.0",
3
+ "version": "0.414.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {