@staff0rd/assist 0.413.0 → 0.415.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.415.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());
535
+ function getConfigDirFrom(cwd) {
536
+ return dirname(getConfigPathFrom(cwd));
532
537
  }
533
- function getProjectRoot() {
534
- const found = findConfigUp(process.cwd());
535
- return found?.rootDir ?? process.cwd();
536
- }
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
  }
@@ -3581,9 +3597,9 @@ function forbiddenStrings() {
3581
3597
  console.log("No forbidden-strings rules configured.");
3582
3598
  process.exit(0);
3583
3599
  }
3584
- const cache = /* @__PURE__ */ new Map();
3600
+ const cache2 = /* @__PURE__ */ new Map();
3585
3601
  const readJson = (file) => {
3586
- if (cache.has(file)) return cache.get(file);
3602
+ if (cache2.has(file)) return cache2.get(file);
3587
3603
  if (!existsSync12(file)) {
3588
3604
  console.log(`Forbidden-strings file not found: ${file}`);
3589
3605
  process.exit(1);
@@ -3595,7 +3611,7 @@ function forbiddenStrings() {
3595
3611
  console.log(`Could not parse ${file}: ${error.message}`);
3596
3612
  process.exit(1);
3597
3613
  }
3598
- cache.set(file, parsed);
3614
+ cache2.set(file, parsed);
3599
3615
  return parsed;
3600
3616
  };
3601
3617
  const violations = findForbiddenStrings(rules, readJson);
@@ -7987,18 +8003,18 @@ import { dirname as dirname19, join as join22 } from "path";
7987
8003
  import { fileURLToPath as fileURLToPath4 } from "url";
7988
8004
  function createBundleHandler(importMetaUrl, bundlePath) {
7989
8005
  const dir = dirname19(fileURLToPath4(importMetaUrl));
7990
- let cache;
8006
+ let cache2;
7991
8007
  return (req, res) => {
7992
- if (!cache) {
8008
+ if (!cache2) {
7993
8009
  const body = readFileSync18(join22(dir, bundlePath), "utf8");
7994
8010
  const etag = `"${createHash("sha256").update(body).digest("hex").slice(0, 16)}"`;
7995
- cache = { body, etag };
8011
+ cache2 = { body, etag };
7996
8012
  }
7997
8013
  const headers = {
7998
- ETag: cache.etag,
8014
+ ETag: cache2.etag,
7999
8015
  "Cache-Control": "no-cache"
8000
8016
  };
8001
- if (req.headers["if-none-match"] === cache.etag) {
8017
+ if (req.headers["if-none-match"] === cache2.etag) {
8002
8018
  res.writeHead(304, headers);
8003
8019
  res.end();
8004
8020
  return;
@@ -8007,7 +8023,7 @@ function createBundleHandler(importMetaUrl, bundlePath) {
8007
8023
  "Content-Type": "application/javascript",
8008
8024
  ...headers
8009
8025
  });
8010
- res.end(cache.body);
8026
+ res.end(cache2.body);
8011
8027
  };
8012
8028
  }
8013
8029
 
@@ -8504,25 +8520,6 @@ function getHtml() {
8504
8520
  </html>`;
8505
8521
  }
8506
8522
 
8507
- // src/commands/sessions/web/getReviewSynthesis.ts
8508
- import { execFile as execFile2 } from "child_process";
8509
- import { readFileSync as readFileSync19 } from "fs";
8510
- import { homedir as homedir11 } from "os";
8511
- import { basename as basename6, join as join25 } from "path";
8512
- import { promisify as promisify2 } from "util";
8513
-
8514
- // src/commands/sessions/web/findSynthesisForBranch.ts
8515
- import { existsSync as existsSync26, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
8516
- import { basename as basename5, dirname as dirname20, join as join24 } from "path";
8517
- function findSynthesisForBranch(repoReviewsDir, branch2) {
8518
- const branchKeyPath = join24(repoReviewsDir, `${branch2}-`);
8519
- const parent = dirname20(branchKeyPath);
8520
- const branchPrefix = basename5(branchKeyPath);
8521
- if (!existsSync26(parent)) return null;
8522
- const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join24(parent, name, "synthesis.md")).filter((path71) => existsSync26(path71)).map((path71) => ({ path: path71, mtime: statSync2(path71).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
8523
- return synthesisFiles[0]?.path ?? null;
8524
- }
8525
-
8526
8523
  // src/commands/sessions/web/getCwdParam.ts
8527
8524
  function getCwdParam(req, res) {
8528
8525
  const url = new URL(req.url ?? "/", "http://localhost");
@@ -8548,6 +8545,41 @@ function toGitCwd(cwd) {
8548
8545
  return detectPlatform() === "wsl" ? windowsCwdToWslPath(cwd) : cwd;
8549
8546
  }
8550
8547
 
8548
+ // src/commands/sessions/web/handleServerRuns.ts
8549
+ function getServerRuns(rawCwd) {
8550
+ try {
8551
+ const cwd = toGitCwd(rawCwd);
8552
+ const { run: run4 } = loadConfigFrom(cwd);
8553
+ return resolveRunConfigs(run4, getConfigDirFrom(cwd)).filter((r) => r.server).map((r) => ({ name: r.name, port: r.port }));
8554
+ } catch {
8555
+ return [];
8556
+ }
8557
+ }
8558
+ function handleServerRuns(req, res) {
8559
+ const cwd = getCwdParam(req, res);
8560
+ if (!cwd) return;
8561
+ respondJson(res, 200, { runs: getServerRuns(cwd) });
8562
+ }
8563
+
8564
+ // src/commands/sessions/web/getReviewSynthesis.ts
8565
+ import { execFile as execFile2 } from "child_process";
8566
+ import { readFileSync as readFileSync19 } from "fs";
8567
+ import { homedir as homedir11 } from "os";
8568
+ import { basename as basename6, join as join25 } from "path";
8569
+ import { promisify as promisify2 } from "util";
8570
+
8571
+ // src/commands/sessions/web/findSynthesisForBranch.ts
8572
+ import { existsSync as existsSync26, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
8573
+ import { basename as basename5, dirname as dirname20, join as join24 } from "path";
8574
+ function findSynthesisForBranch(repoReviewsDir, branch2) {
8575
+ const branchKeyPath = join24(repoReviewsDir, `${branch2}-`);
8576
+ const parent = dirname20(branchKeyPath);
8577
+ const branchPrefix = basename5(branchKeyPath);
8578
+ if (!existsSync26(parent)) return null;
8579
+ const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join24(parent, name, "synthesis.md")).filter((path71) => existsSync26(path71)).map((path71) => ({ path: path71, mtime: statSync2(path71).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
8580
+ return synthesisFiles[0]?.path ?? null;
8581
+ }
8582
+
8551
8583
  // src/commands/sessions/web/getReviewSynthesis.ts
8552
8584
  var execFileAsync2 = promisify2(execFile2);
8553
8585
  function runGit(cwd, args) {
@@ -8948,11 +8980,11 @@ var execFileAsync4 = promisify5(execFile4);
8948
8980
  var CACHE_TTL_MS = 3e4;
8949
8981
  function createCachedGhJson(args, parse3, fallback, options2 = {}) {
8950
8982
  const { cacheFallback = true } = options2;
8951
- const cache = /* @__PURE__ */ new Map();
8983
+ const cache2 = /* @__PURE__ */ new Map();
8952
8984
  return async (cwd, extraArgs = []) => {
8953
8985
  const key = extraArgs.length ? `${cwd}\0${extraArgs.join("\0")}` : cwd;
8954
8986
  const now = Date.now();
8955
- const cached = cache.get(key);
8987
+ const cached = cache2.get(key);
8956
8988
  if (cached && cached.expires > now) return cached.value;
8957
8989
  let value = fallback;
8958
8990
  try {
@@ -8965,7 +8997,7 @@ function createCachedGhJson(args, parse3, fallback, options2 = {}) {
8965
8997
  value = fallback;
8966
8998
  }
8967
8999
  if (cacheFallback || value !== fallback)
8968
- cache.set(key, { value, expires: now + CACHE_TTL_MS });
9000
+ cache2.set(key, { value, expires: now + CACHE_TTL_MS });
8969
9001
  return value;
8970
9002
  };
8971
9003
  }
@@ -9165,22 +9197,22 @@ import { readFileSync as readFileSync21 } from "fs";
9165
9197
  import { createRequire as createRequire2 } from "module";
9166
9198
  var require3 = createRequire2(import.meta.url);
9167
9199
  function createCssHandler(packageEntry) {
9168
- let cache;
9200
+ let cache2;
9169
9201
  return (req, res) => {
9170
- if (!cache) {
9202
+ if (!cache2) {
9171
9203
  const resolved = require3.resolve(packageEntry);
9172
9204
  const body = readFileSync21(resolved, "utf8");
9173
9205
  const etag = `"${createHash2("sha256").update(body).digest("hex").slice(0, 16)}"`;
9174
- cache = { body, etag };
9206
+ cache2 = { body, etag };
9175
9207
  }
9176
- const headers = { ETag: cache.etag, "Cache-Control": "no-cache" };
9177
- if (req.headers["if-none-match"] === cache.etag) {
9208
+ const headers = { ETag: cache2.etag, "Cache-Control": "no-cache" };
9209
+ if (req.headers["if-none-match"] === cache2.etag) {
9178
9210
  res.writeHead(304, headers);
9179
9211
  res.end();
9180
9212
  return;
9181
9213
  }
9182
9214
  res.writeHead(200, { "Content-Type": "text/css", ...headers });
9183
- res.end(cache.body);
9215
+ res.end(cache2.body);
9184
9216
  };
9185
9217
  }
9186
9218
 
@@ -9202,6 +9234,7 @@ var routes2 = {
9202
9234
  "GET /api/jira-site": jiraSite,
9203
9235
  "GET /api/harness": harnessCapabilities,
9204
9236
  "GET /api/pr-status": prStatus,
9237
+ "GET /api/server-runs": handleServerRuns,
9205
9238
  "GET /api/pr-list": prList,
9206
9239
  "GET /api/news/items": listNewsItems,
9207
9240
  "GET /api/usage/history": listUsageHistory,
@@ -17691,20 +17724,20 @@ function resolveThread(threadId) {
17691
17724
  }
17692
17725
  }
17693
17726
  function requireCache(prNumber) {
17694
- const cache = loadCommentsCache(prNumber);
17695
- if (!cache) {
17727
+ const cache2 = loadCommentsCache(prNumber);
17728
+ if (!cache2) {
17696
17729
  console.error(
17697
17730
  `Error: No cached comments found for PR #${prNumber}. Run "assist prs list-comments" first.`
17698
17731
  );
17699
17732
  process.exit(1);
17700
17733
  }
17701
- return cache;
17734
+ return cache2;
17702
17735
  }
17703
17736
  function findLineComment(comments3, commentId) {
17704
17737
  return comments3.find((c) => c.type === "line" && c.id === commentId);
17705
17738
  }
17706
- function requireLineComment(cache, commentId) {
17707
- const comment3 = findLineComment(cache.comments, commentId);
17739
+ function requireLineComment(cache2, commentId) {
17740
+ const comment3 = findLineComment(cache2.comments, commentId);
17708
17741
  if (!comment3 || comment3.type !== "line" || !comment3.threadId) {
17709
17742
  console.error(
17710
17743
  `Error: Comment #${commentId} not found or has no thread ID.`
@@ -17713,8 +17746,8 @@ function requireLineComment(cache, commentId) {
17713
17746
  }
17714
17747
  return comment3;
17715
17748
  }
17716
- function cleanupCacheIfDone(cache, prNumber, commentId) {
17717
- const hasRemaining = cache.comments.some(
17749
+ function cleanupCacheIfDone(cache2, prNumber, commentId) {
17750
+ const hasRemaining = cache2.comments.some(
17718
17751
  (c) => c.type === "line" && c.id !== commentId
17719
17752
  );
17720
17753
  if (!hasRemaining) deleteCommentsCache(prNumber);
@@ -17722,13 +17755,13 @@ function cleanupCacheIfDone(cache, prNumber, commentId) {
17722
17755
  function resolveCommentWithReply(commentId, message) {
17723
17756
  const prNumber = getCurrentPrNumber();
17724
17757
  const { org, repo } = getRepoInfo();
17725
- const cache = requireCache(prNumber);
17726
- const comment3 = requireLineComment(cache, commentId);
17758
+ const cache2 = requireCache(prNumber);
17759
+ const comment3 = requireLineComment(cache2, commentId);
17727
17760
  replyToComment(org, repo, prNumber, commentId, message);
17728
17761
  console.log("Reply posted successfully.");
17729
17762
  resolveThread(comment3.threadId);
17730
17763
  console.log("Thread resolved successfully.");
17731
- cleanupCacheIfDone(cache, prNumber, commentId);
17764
+ cleanupCacheIfDone(cache2, prNumber, commentId);
17732
17765
  }
17733
17766
 
17734
17767
  // src/commands/prs/fixed.ts
@@ -24745,6 +24778,17 @@ function broadcast(clients, msg) {
24745
24778
  }
24746
24779
  }
24747
24780
 
24781
+ // src/commands/sessions/daemon/originForCwd.ts
24782
+ var cache = /* @__PURE__ */ new Map();
24783
+ function originForCwd(cwd) {
24784
+ if (!cwd) return void 0;
24785
+ const cached = cache.get(cwd);
24786
+ if (cached !== void 0) return cached;
24787
+ const origin = getCurrentOrigin(cwd);
24788
+ cache.set(cwd, origin);
24789
+ return origin;
24790
+ }
24791
+
24748
24792
  // src/commands/sessions/daemon/toSessionInfo.ts
24749
24793
  function toSessionInfo({
24750
24794
  id,
@@ -24759,6 +24803,9 @@ function toSessionInfo({
24759
24803
  runningSince,
24760
24804
  runName,
24761
24805
  runArgs,
24806
+ server,
24807
+ serverPort,
24808
+ serverOrigin,
24762
24809
  assistArgs,
24763
24810
  cwd,
24764
24811
  restored,
@@ -24784,6 +24831,9 @@ function toSessionInfo({
24784
24831
  runningSince,
24785
24832
  runName,
24786
24833
  runArgs,
24834
+ server,
24835
+ port: serverPort,
24836
+ remoteOrigin: serverOrigin ?? originForCwd(cwd),
24787
24837
  assistArgs,
24788
24838
  cwd,
24789
24839
  restored,
@@ -24957,6 +25007,28 @@ function createAssistSession(id, assistArgs, cwd, meta) {
24957
25007
  // src/commands/sessions/daemon/createSession.ts
24958
25008
  import { randomUUID as randomUUID4 } from "crypto";
24959
25009
 
25010
+ // src/commands/sessions/daemon/resolveRunConfig.ts
25011
+ function resolveRunConfig(name, cwd) {
25012
+ try {
25013
+ const { run: run4 } = loadConfigFrom(cwd);
25014
+ const configs = resolveRunConfigs(run4, getConfigDirFrom(cwd));
25015
+ const exact = configs.find((r) => r.name === name);
25016
+ if (exact) return exact;
25017
+ const suffixMatches = configs.filter((r) => r.name.endsWith(`:${name}`));
25018
+ return suffixMatches.length === 1 ? suffixMatches[0] : void 0;
25019
+ } catch {
25020
+ return void 0;
25021
+ }
25022
+ }
25023
+
25024
+ // src/commands/sessions/daemon/serverRunMeta.ts
25025
+ function serverRunMeta(runName, cwd) {
25026
+ const dir = cwd ?? process.cwd();
25027
+ const config = resolveRunConfig(runName, dir);
25028
+ if (!config?.server) return { server: false };
25029
+ return { server: true, port: config.port, origin: getCurrentOrigin(dir) };
25030
+ }
25031
+
24960
25032
  // src/commands/sessions/daemon/readDesignSystemPrompt.ts
24961
25033
  import * as fs35 from "fs";
24962
25034
  import * as path59 from "path";
@@ -25037,7 +25109,7 @@ function createPiSession(id, prompt, cwd) {
25037
25109
  initialPrompt: prompt
25038
25110
  };
25039
25111
  }
25040
- function createRunSession(id, runName, runArgs, cwd) {
25112
+ function createRunSession(id, runName, runArgs, cwd, meta = serverRunMeta(runName, cwd)) {
25041
25113
  return {
25042
25114
  ...sessionBase(id, "running"),
25043
25115
  name: `run: ${runName}`,
@@ -25045,7 +25117,10 @@ function createRunSession(id, runName, runArgs, cwd) {
25045
25117
  pty: spawnRun({ name: runName, args: runArgs, cwd }),
25046
25118
  runName,
25047
25119
  runArgs,
25048
- cwd
25120
+ cwd,
25121
+ server: meta.server || void 0,
25122
+ serverPort: meta.port,
25123
+ serverOrigin: meta.origin
25049
25124
  };
25050
25125
  }
25051
25126
 
@@ -25442,6 +25517,18 @@ function errorTail(scrollback) {
25442
25517
  return tail.length > MAX_TAIL_CHARS ? tail.slice(-MAX_TAIL_CHARS) : tail;
25443
25518
  }
25444
25519
 
25520
+ // src/commands/sessions/daemon/handleFailedResume.ts
25521
+ function handleFailedResume(session, exitCode, onStatusChange) {
25522
+ const failed2 = session.restored === true && exitCode !== 0 && session.scrollback.length === 0;
25523
+ if (!failed2) return false;
25524
+ session.error = `resume process exited with code ${exitCode} before producing any output`;
25525
+ daemonLog(
25526
+ `could not resume restored session "${session.name}" (id ${session.id}): ${session.error}`
25527
+ );
25528
+ onStatusChange(session, "error", exitCode);
25529
+ return true;
25530
+ }
25531
+
25445
25532
  // src/commands/sessions/daemon/watchActivity.ts
25446
25533
  import { existsSync as existsSync58, mkdirSync as mkdirSync25, watch } from "fs";
25447
25534
  import { dirname as dirname29 } from "path";
@@ -25535,6 +25622,18 @@ function refreshActivity(session) {
25535
25622
  session.claudeSessionId = activity2.claudeSessionId;
25536
25623
  }
25537
25624
 
25625
+ // src/commands/sessions/daemon/handleStoppedExit.ts
25626
+ function handleStoppedExit(session, exitCode, onStatusChange) {
25627
+ if (!session.stopping) return false;
25628
+ session.stopping = void 0;
25629
+ refreshActivity(session);
25630
+ daemonLog(
25631
+ `session ${session.id} ("${session.name}") pty exited (code ${exitCode}) after stop request; marking done`
25632
+ );
25633
+ onStatusChange(session, "done", 0);
25634
+ return true;
25635
+ }
25636
+
25538
25637
  // src/commands/sessions/daemon/handlePtyExit.ts
25539
25638
  function handlePtyExit(session, exitCode, onStatusChange) {
25540
25639
  if (session.pendingRestart) {
@@ -25546,16 +25645,9 @@ function handlePtyExit(session, exitCode, onStatusChange) {
25546
25645
  resume();
25547
25646
  return;
25548
25647
  }
25648
+ if (handleStoppedExit(session, exitCode, onStatusChange)) return;
25549
25649
  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
- }
25650
+ if (handleFailedResume(session, exitCode, onStatusChange)) return;
25559
25651
  const priorStatus = session.status;
25560
25652
  if (exitCode !== 0) {
25561
25653
  const tail = errorTail(session.scrollback);
@@ -26170,6 +26262,59 @@ function respawnThunk(session) {
26170
26262
  return null;
26171
26263
  }
26172
26264
 
26265
+ // src/commands/sessions/daemon/serverConflictInfo.ts
26266
+ function serverConflictInfo(session) {
26267
+ return {
26268
+ id: session.id,
26269
+ name: session.name,
26270
+ cwd: session.cwd,
26271
+ port: session.serverPort
26272
+ };
26273
+ }
26274
+
26275
+ // src/commands/sessions/daemon/liveServerRun.ts
26276
+ function liveServerRun(sessions, origin, excludeId) {
26277
+ for (const s of sessions.values()) {
26278
+ if (s.id === excludeId || !s.server || s.serverOrigin !== origin) continue;
26279
+ if (s.status !== "done" && s.status !== "error") return s;
26280
+ }
26281
+ return void 0;
26282
+ }
26283
+ function stopServerSession(sessions, id) {
26284
+ const s = sessions.get(id);
26285
+ if (!s?.pty || s.status === "done" || s.status === "error") return;
26286
+ s.stopping = true;
26287
+ daemonLog(`session ${id} stopped (${s.name})`);
26288
+ s.pty.kill();
26289
+ }
26290
+
26291
+ // src/commands/sessions/daemon/runRetry.ts
26292
+ function tagServerSession(session, meta) {
26293
+ session.server = meta.server || void 0;
26294
+ session.serverPort = meta.port;
26295
+ session.serverOrigin = meta.origin;
26296
+ }
26297
+ function findRetryConflict(sessions, session, replace, dismiss) {
26298
+ const meta = serverRunMeta(session.runName ?? "", session.cwd);
26299
+ if (meta.server && meta.origin) {
26300
+ const existing = liveServerRun(sessions, meta.origin, session.id);
26301
+ if (existing && !replace) return serverConflictInfo(existing);
26302
+ if (existing) dismiss(existing.id);
26303
+ }
26304
+ tagServerSession(session, meta);
26305
+ return null;
26306
+ }
26307
+ function runRetry(sessions, id, replace, deps2) {
26308
+ const s = sessions.get(id);
26309
+ if (!s) return null;
26310
+ if (s.commandType === "run" && s.runName) {
26311
+ const conflict = findRetryConflict(sessions, s, replace, deps2.dismiss);
26312
+ if (conflict) return conflict;
26313
+ }
26314
+ if (retrySession(s, deps2.clients, deps2.onStatusChange)) deps2.notify();
26315
+ return null;
26316
+ }
26317
+
26173
26318
  // src/commands/sessions/daemon/reuseSessionForRun.ts
26174
26319
  function reuseSessionForRun(session, itemId2, clients, onStatusChange) {
26175
26320
  const assistArgs = ["backlog", "run", String(itemId2)];
@@ -27044,8 +27189,13 @@ var SessionManager = class {
27044
27189
  (id) => createSession(id, prompt, cwd, design, harness)
27045
27190
  );
27046
27191
  }
27047
- spawnRun(runName, runArgs, cwd) {
27048
- return this.spawnWith((id) => createRunSession(id, runName, runArgs, cwd));
27192
+ spawnRun(runName, runArgs, cwd, meta) {
27193
+ return this.spawnWith(
27194
+ (id) => createRunSession(id, runName, runArgs, cwd, meta)
27195
+ );
27196
+ }
27197
+ liveServerRun(origin, excludeId) {
27198
+ return liveServerRun(this.sessions, origin, excludeId);
27049
27199
  }
27050
27200
  spawnAssist(assistArgs, cwd, meta) {
27051
27201
  return this.spawnWith(
@@ -27071,9 +27221,13 @@ var SessionManager = class {
27071
27221
  resizeSession(id, cols, rows) {
27072
27222
  resizeSession(this.sessions, id, cols, rows);
27073
27223
  }
27074
- retrySession(id) {
27075
- const s = this.sessions.get(id);
27076
- if (s && retrySession(s, this.clients, this.onStatusChange)) this.notify();
27224
+ retrySession(id, replace = false) {
27225
+ return runRetry(this.sessions, id, replace, {
27226
+ clients: this.clients,
27227
+ onStatusChange: this.onStatusChange,
27228
+ dismiss: this.dismissSession,
27229
+ notify: this.notify
27230
+ });
27077
27231
  }
27078
27232
  restart(id) {
27079
27233
  const result = restartManagedSession(
@@ -27088,6 +27242,9 @@ var SessionManager = class {
27088
27242
  dismissSession = (id) => {
27089
27243
  if (dismissSession(this.sessions, id)) this.notify();
27090
27244
  };
27245
+ stopSession(id) {
27246
+ stopServerSession(this.sessions, id);
27247
+ }
27091
27248
  setAutoRun(id, enabled) {
27092
27249
  if (setAutoRun(this.sessions, id, enabled)) this.notify();
27093
27250
  }
@@ -27139,6 +27296,41 @@ function creator(isNew, spawn12) {
27139
27296
  };
27140
27297
  }
27141
27298
 
27299
+ // src/commands/sessions/daemon/handleCreateRun.ts
27300
+ function handleCreateRun(client, m, d) {
27301
+ if (m.windowsProxy.route(client, d)) return;
27302
+ const runName = d.runName;
27303
+ const cwd = d.cwd;
27304
+ const runArgs = d.runArgs ?? [];
27305
+ const meta = serverRunMeta(runName, cwd);
27306
+ if (meta.server && meta.origin) {
27307
+ const existing = m.liveServerRun(meta.origin);
27308
+ if (existing && d.replace !== true) {
27309
+ daemonLog(
27310
+ `create-run ${runName} rejected: server ${existing.id} already live for ${meta.origin}`
27311
+ );
27312
+ sendTo(client, {
27313
+ type: "run-conflict",
27314
+ runName,
27315
+ cwd,
27316
+ existing: serverConflictInfo(existing)
27317
+ });
27318
+ return;
27319
+ }
27320
+ if (existing) {
27321
+ daemonLog(
27322
+ `create-run ${runName} replacing live server ${existing.id} for ${meta.origin}`
27323
+ );
27324
+ m.dismissSession(existing.id);
27325
+ }
27326
+ }
27327
+ sendTo(client, {
27328
+ type: "created",
27329
+ sessionId: m.spawnRun(runName, runArgs, cwd, meta),
27330
+ isNew: true
27331
+ });
27332
+ }
27333
+
27142
27334
  // src/commands/sessions/daemon/handleSetStatus.ts
27143
27335
  function handleSetStatus(client, m, d) {
27144
27336
  daemonLog(
@@ -27280,14 +27472,7 @@ var messageHandlers = {
27280
27472
  "subscribe-logs": (client, m, d) => m.clients.subscribeLogs(client, d.replay !== false),
27281
27473
  hello: (client) => sendTo(client, buildHello()),
27282
27474
  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
- ),
27475
+ "create-run": handleCreateRun,
27291
27476
  "create-assist": creator(
27292
27477
  true,
27293
27478
  (m, d) => m.spawnAssist(
@@ -27321,13 +27506,22 @@ var messageHandlers = {
27321
27506
  resize: routed(
27322
27507
  (_client, m, d) => m.resizeSession(d.sessionId, d.cols, d.rows)
27323
27508
  ),
27324
- retry: routed((_client, m, d) => m.retrySession(d.sessionId)),
27509
+ retry: routed((client, m, d) => {
27510
+ const conflict = m.retrySession(d.sessionId, d.replace === true);
27511
+ if (conflict)
27512
+ sendTo(client, {
27513
+ type: "run-conflict",
27514
+ sessionId: d.sessionId,
27515
+ existing: conflict
27516
+ });
27517
+ }),
27325
27518
  restart: routed((client, m, d) => {
27326
27519
  const result = m.restart(d.sessionId);
27327
27520
  if (!result.ok && result.reason)
27328
27521
  sendTo(client, { type: "error", message: result.reason });
27329
27522
  }),
27330
27523
  dismiss: routed((_client, m, d) => m.dismissSession(d.sessionId)),
27524
+ stop: routed((_client, m, d) => m.stopSession(d.sessionId)),
27331
27525
  "set-autorun": routed(
27332
27526
  (_client, m, d) => m.setAutoRun(d.sessionId, d.enabled)
27333
27527
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.413.0",
3
+ "version": "0.415.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {