@staff0rd/assist 0.752.0 → 0.753.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.752.0",
9
+ version: "0.753.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -839,6 +839,7 @@ var assistConfigShape = {
839
839
  ])
840
840
  ).optional(),
841
841
  linkVersionCheck: z3.enum(["block", "warn", "off"]).default("block"),
842
+ tailscaleServe: z3.boolean().default(true),
842
843
  includeCommittedChanges: z3.boolean().default(true),
843
844
  topBar: z3.boolean().default(true),
844
845
  floatWaiting: z3.boolean().default(true),
@@ -10950,6 +10951,117 @@ function hostedGroup(origin, clone) {
10950
10951
  return { origin, clone };
10951
10952
  }
10952
10953
 
10954
+ // src/commands/sessions/nodes/tailscaleStatus.ts
10955
+ import { execFile as execFile3 } from "child_process";
10956
+ import { promisify as promisify2 } from "util";
10957
+ var execFileAsync2 = promisify2(execFile3);
10958
+ var STATUS_TIMEOUT_MS = 1e4;
10959
+ function tailscaleCli() {
10960
+ return detectPlatform() === "wsl" ? "tailscale.exe" : "tailscale";
10961
+ }
10962
+ async function tailscaleStatus() {
10963
+ const { stdout } = await execFileAsync2(tailscaleCli(), ["status", "--json"], {
10964
+ encoding: "utf8",
10965
+ timeout: STATUS_TIMEOUT_MS,
10966
+ windowsHide: true
10967
+ });
10968
+ return JSON.parse(stdout);
10969
+ }
10970
+
10971
+ // src/commands/sessions/nodes/tailnetSuffix.ts
10972
+ function tailnetSuffix(status2) {
10973
+ if (status2.BackendState !== "Running")
10974
+ throw new Error(
10975
+ `Tailscale is not running on this node (${status2.BackendState ?? "unknown state"}) \u2014 run \`${tailscaleCli()} up\``
10976
+ );
10977
+ const suffix = status2.CurrentTailnet?.MagicDNSSuffix ?? status2.MagicDNSSuffix ?? "";
10978
+ if (status2.CurrentTailnet?.MagicDNSEnabled === false || !suffix)
10979
+ throw new Error(
10980
+ "MagicDNS is off in this tailnet \u2014 turn on MagicDNS and HTTPS certificates on the DNS page of the Tailscale admin console"
10981
+ );
10982
+ return suffix.replace(/\.$/, "");
10983
+ }
10984
+
10985
+ // src/commands/sessions/nodes/planServe.ts
10986
+ function planServe(status2, serveConfig, port) {
10987
+ tailnetSuffix(status2);
10988
+ const fqdn = status2.Self?.DNSName?.replace(/\.$/, "");
10989
+ if (!fqdn)
10990
+ throw new Error(
10991
+ `\`${tailscaleCli()} status --json\` reports no DNS name for this machine`
10992
+ );
10993
+ const target = `http://127.0.0.1:${port}`;
10994
+ const served = serveConfig.Web?.[`${fqdn}:${port}`]?.Handlers?.["/"]?.Proxy;
10995
+ return {
10996
+ host: fqdn.split(".")[0],
10997
+ url: `https://${fqdn}:${port}`,
10998
+ target,
10999
+ alreadyServing: served?.replace(/\/$/, "") === target,
11000
+ args: ["serve", "--bg", `--https=${port}`, target]
11001
+ };
11002
+ }
11003
+
11004
+ // src/commands/sessions/nodes/runningStatus.ts
11005
+ async function runningStatus(status2) {
11006
+ let current;
11007
+ try {
11008
+ current = await status2();
11009
+ } catch (error) {
11010
+ if (error.code === "ENOENT")
11011
+ return `skipped, ${tailscaleCli()} is not installed`;
11012
+ throw error;
11013
+ }
11014
+ return current.BackendState === "Running" ? current : `skipped, Tailscale is ${current.BackendState ?? "not running"}`;
11015
+ }
11016
+
11017
+ // src/commands/sessions/nodes/runTailscale.ts
11018
+ import { execFile as execFile4 } from "child_process";
11019
+ import { promisify as promisify3 } from "util";
11020
+ var execFileAsync3 = promisify3(execFile4);
11021
+ async function runTailscale(args) {
11022
+ const { stdout } = await execFileAsync3(tailscaleCli(), args, {
11023
+ encoding: "utf8",
11024
+ timeout: 1e4,
11025
+ windowsHide: true
11026
+ });
11027
+ return stdout;
11028
+ }
11029
+
11030
+ // src/commands/sessions/nodes/ensureTailscaleServe.ts
11031
+ var SERVE_WRITE_ATTEMPTS = 5;
11032
+ var defaultDeps = {
11033
+ enabled: () => loadConfig().sessions?.tailscaleServe !== false,
11034
+ status: tailscaleStatus,
11035
+ serveStatus: async () => {
11036
+ const stdout = (await runTailscale(["serve", "status", "--json"])).trim();
11037
+ return stdout ? JSON.parse(stdout) : {};
11038
+ },
11039
+ serve: async (args) => {
11040
+ await runTailscale(args);
11041
+ },
11042
+ sleep: (ms) => new Promise((resolve26) => setTimeout(resolve26, ms))
11043
+ };
11044
+ async function ensureTailscaleServe(port, deps2 = {}) {
11045
+ const { enabled, status: status2, serveStatus, serve, sleep: sleep2 } = {
11046
+ ...defaultDeps,
11047
+ ...deps2
11048
+ };
11049
+ if (!enabled()) return "skipped, sessions.tailscaleServe is false";
11050
+ const current = await runningStatus(status2);
11051
+ if (typeof current === "string") return current;
11052
+ for (let attempt = 1; ; attempt++) {
11053
+ const plan2 = planServe(current, await serveStatus(), port);
11054
+ if (plan2.alreadyServing) return `${plan2.url} already serves ${plan2.target}`;
11055
+ try {
11056
+ await serve(plan2.args);
11057
+ return `serving ${plan2.target} at ${plan2.url}`;
11058
+ } catch (error) {
11059
+ if (attempt >= SERVE_WRITE_ATTEMPTS) throw error;
11060
+ await sleep2(attempt * 500 + Math.random() * 500);
11061
+ }
11062
+ }
11063
+ }
11064
+
10953
11065
  // src/commands/sessions/web/attachSessionSocket.ts
10954
11066
  import { WebSocketServer } from "ws";
10955
11067
 
@@ -11578,14 +11690,14 @@ function getSessionParam(req) {
11578
11690
  }
11579
11691
 
11580
11692
  // src/commands/sessions/web/execGit.ts
11581
- import { execFile as execFile3 } from "child_process";
11582
- import { promisify as promisify2 } from "util";
11583
- var execFileAsync2 = promisify2(execFile3);
11693
+ import { execFile as execFile5 } from "child_process";
11694
+ import { promisify as promisify4 } from "util";
11695
+ var execFileAsync4 = promisify4(execFile5);
11584
11696
  async function execGit(cwd, args, opts = {}) {
11585
11697
  const { maxBuffer, allowFailure } = opts;
11586
11698
  const { file, argv } = /^[A-Za-z]:[\\/]/.test(cwd) ? { file: "git.exe", argv: ["-C", cwd, ...args] } : { file: "git", argv: args };
11587
11699
  try {
11588
- const { stdout } = await execFileAsync2(file, argv, {
11700
+ const { stdout } = await execFileAsync4(file, argv, {
11589
11701
  encoding: "utf8",
11590
11702
  windowsHide: true,
11591
11703
  ...maxBuffer ? { maxBuffer } : {},
@@ -12958,6 +13070,15 @@ var sessionsConfigHelp = [
12958
13070
  }
12959
13071
  ];
12960
13072
 
13073
+ // src/commands/sessions/webConfigHelp.ts
13074
+ var webConfigHelp = [
13075
+ {
13076
+ key: "sessions.tailscaleServe",
13077
+ setter: "assist config set sessions.tailscaleServe false -g",
13078
+ note: "default on: the web server exposes its port on the tailnet with tailscale serve when it starts, so other nodes can link it by Tailscale name; set false to keep it off the tailnet"
13079
+ }
13080
+ ];
13081
+
12961
13082
  // src/commands/slack/slackConfigHelp.ts
12962
13083
  var slackConfigHelp = [
12963
13084
  {
@@ -13050,6 +13171,7 @@ var configHelpEntries = [
13050
13171
  ...seqConfigHelp,
13051
13172
  ...sessionsConfigHelp,
13052
13173
  ...nodesConfigHelp,
13174
+ ...webConfigHelp,
13053
13175
  ...slackConfigHelp,
13054
13176
  ...sqlConfigHelp,
13055
13177
  ...transcriptConfigHelp,
@@ -13168,11 +13290,11 @@ function handleServerRuns(req, res) {
13168
13290
  }
13169
13291
 
13170
13292
  // src/commands/sessions/web/getReviewSynthesis.ts
13171
- import { execFile as execFile4 } from "child_process";
13293
+ import { execFile as execFile6 } from "child_process";
13172
13294
  import { readFileSync as readFileSync22 } from "fs";
13173
13295
  import { homedir as homedir14 } from "os";
13174
13296
  import { basename as basename11, join as join37 } from "path";
13175
- import { promisify as promisify3 } from "util";
13297
+ import { promisify as promisify5 } from "util";
13176
13298
 
13177
13299
  // src/commands/sessions/web/findSynthesisForBranch.ts
13178
13300
  import { existsSync as existsSync34, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
@@ -13187,9 +13309,9 @@ function findSynthesisForBranch(repoReviewsDir, branch2) {
13187
13309
  }
13188
13310
 
13189
13311
  // src/commands/sessions/web/getReviewSynthesis.ts
13190
- var execFileAsync3 = promisify3(execFile4);
13312
+ var execFileAsync5 = promisify5(execFile6);
13191
13313
  function runGit2(cwd, args) {
13192
- return execFileAsync3("git", args, {
13314
+ return execFileAsync5("git", args, {
13193
13315
  encoding: "utf8",
13194
13316
  windowsHide: true,
13195
13317
  cwd
@@ -13690,9 +13812,9 @@ function newsNavConfig(_req, res) {
13690
13812
  }
13691
13813
 
13692
13814
  // src/commands/sessions/web/releases/ghJson.ts
13693
- import { execFile as execFile5 } from "child_process";
13694
- import { promisify as promisify4 } from "util";
13695
- var execFileAsync4 = promisify4(execFile5);
13815
+ import { execFile as execFile7 } from "child_process";
13816
+ import { promisify as promisify6 } from "util";
13817
+ var execFileAsync6 = promisify6(execFile7);
13696
13818
  var CACHE_TTL_MS4 = 3e4;
13697
13819
  var cache5 = /* @__PURE__ */ new Map();
13698
13820
  function partialGraphqlData(error) {
@@ -13707,7 +13829,7 @@ function partialGraphqlData(error) {
13707
13829
  }
13708
13830
  async function runGh(cwd, args) {
13709
13831
  try {
13710
- const { stdout } = await execFileAsync4("gh", args, {
13832
+ const { stdout } = await execFileAsync6("gh", args, {
13711
13833
  encoding: "utf8",
13712
13834
  windowsHide: true,
13713
13835
  cwd,
@@ -14793,8 +14915,8 @@ function listUsageItems(req, res) {
14793
14915
 
14794
14916
  // src/commands/sessions/web/openInCode.ts
14795
14917
  import { exec } from "child_process";
14796
- import { promisify as promisify5 } from "util";
14797
- var execAsync = promisify5(exec);
14918
+ import { promisify as promisify7 } from "util";
14919
+ var execAsync = promisify7(exec);
14798
14920
  async function openInCode(req, res) {
14799
14921
  const cwd = getCwdParam(req, res);
14800
14922
  if (!cwd) return;
@@ -14810,9 +14932,9 @@ async function openInCode(req, res) {
14810
14932
  }
14811
14933
 
14812
14934
  // src/commands/sessions/web/createCachedGhJson.ts
14813
- import { execFile as execFile6 } from "child_process";
14814
- import { promisify as promisify6 } from "util";
14815
- var execFileAsync5 = promisify6(execFile6);
14935
+ import { execFile as execFile8 } from "child_process";
14936
+ import { promisify as promisify8 } from "util";
14937
+ var execFileAsync7 = promisify8(execFile8);
14816
14938
  var CACHE_TTL_MS5 = 3e4;
14817
14939
  function createCachedGhJson(args, parse5, fallback, options2 = {}) {
14818
14940
  const { cacheFallback = true } = options2;
@@ -14824,7 +14946,7 @@ function createCachedGhJson(args, parse5, fallback, options2 = {}) {
14824
14946
  if (cached && cached.expires > now) return cached.value;
14825
14947
  let value = fallback;
14826
14948
  try {
14827
- const { stdout } = await execFileAsync5("gh", [...args, ...extraArgs], {
14949
+ const { stdout } = await execFileAsync7("gh", [...args, ...extraArgs], {
14828
14950
  encoding: "utf8",
14829
14951
  windowsHide: true,
14830
14952
  cwd
@@ -15001,11 +15123,11 @@ async function proxyToNode(req, res) {
15001
15123
  }
15002
15124
 
15003
15125
  // src/commands/sessions/web/ghAuthToken.ts
15004
- import { execFile as execFile7 } from "child_process";
15005
- import { promisify as promisify7 } from "util";
15006
- var execFileAsync6 = promisify7(execFile7);
15126
+ import { execFile as execFile9 } from "child_process";
15127
+ import { promisify as promisify9 } from "util";
15128
+ var execFileAsync8 = promisify9(execFile9);
15007
15129
  async function ghAuthToken(cwd) {
15008
- const { stdout } = await execFileAsync6("gh", ["auth", "token"], {
15130
+ const { stdout } = await execFileAsync8("gh", ["auth", "token"], {
15009
15131
  cwd,
15010
15132
  windowsHide: true
15011
15133
  });
@@ -15055,9 +15177,9 @@ async function previewImage(req, res) {
15055
15177
  }
15056
15178
 
15057
15179
  // src/commands/sessions/web/prStatus.ts
15058
- import { execFile as execFile8 } from "child_process";
15059
- import { promisify as promisify8 } from "util";
15060
- var execFileAsync7 = promisify8(execFile8);
15180
+ import { execFile as execFile10 } from "child_process";
15181
+ import { promisify as promisify10 } from "util";
15182
+ var execFileAsync9 = promisify10(execFile10);
15061
15183
  function toPrSummary(parsed) {
15062
15184
  if (typeof parsed.number !== "number") return null;
15063
15185
  return {
@@ -15087,7 +15209,7 @@ var getOpenPrForBranch = createCachedGhJson(
15087
15209
  );
15088
15210
  async function getCurrentBranch(cwd) {
15089
15211
  try {
15090
- const { stdout } = await execFileAsync7(
15212
+ const { stdout } = await execFileAsync9(
15091
15213
  "git",
15092
15214
  ["rev-parse", "--abbrev-ref", "HEAD"],
15093
15215
  { encoding: "utf8", windowsHide: true, cwd }
@@ -16399,9 +16521,9 @@ function unsetConfig(req, res) {
16399
16521
  import { rm } from "fs/promises";
16400
16522
 
16401
16523
  // src/commands/sessions/web/runGhImage.ts
16402
- import { execFile as execFile9 } from "child_process";
16403
- import { promisify as promisify9 } from "util";
16404
- var execFileAsync8 = promisify9(execFile9);
16524
+ import { execFile as execFile11 } from "child_process";
16525
+ import { promisify as promisify11 } from "util";
16526
+ var execFileAsync10 = promisify11(execFile11);
16405
16527
  var GhImageUnavailableError = class extends Error {
16406
16528
  name = "GhImageUnavailableError";
16407
16529
  };
@@ -16410,7 +16532,7 @@ var INSTALL_HINT = `Install the gh-image extension: ${GH_IMAGE_INSTALL_COMMAND}`
16410
16532
  async function runGhImage(filePath, cwd) {
16411
16533
  let stdout;
16412
16534
  try {
16413
- ({ stdout } = await execFileAsync8("gh", ["image", filePath], {
16535
+ ({ stdout } = await execFileAsync10("gh", ["image", filePath], {
16414
16536
  cwd,
16415
16537
  windowsHide: true,
16416
16538
  maxBuffer: 10 * 1024 * 1024
@@ -16554,11 +16676,11 @@ async function uploadPrImage(req, res) {
16554
16676
  import { readFile as readFile3, stat as stat3, writeFile as writeFile3 } from "fs/promises";
16555
16677
 
16556
16678
  // src/commands/sessions/web/formatWithOxfmt.ts
16557
- import { execFile as execFile10 } from "child_process";
16679
+ import { execFile as execFile12 } from "child_process";
16558
16680
  import { existsSync as existsSync39 } from "fs";
16559
16681
  import { dirname as dirname24, join as join40 } from "path";
16560
- import { promisify as promisify10 } from "util";
16561
- var execFileAsync9 = promisify10(execFile10);
16682
+ import { promisify as promisify12 } from "util";
16683
+ var execFileAsync11 = promisify12(execFile12);
16562
16684
  var TIMEOUT_MS = 15e3;
16563
16685
  function findOxfmtScript(root2) {
16564
16686
  let dir = root2;
@@ -16573,7 +16695,7 @@ function findOxfmtScript(root2) {
16573
16695
  async function formatWithOxfmt(target, root2) {
16574
16696
  const script = findOxfmtScript(root2);
16575
16697
  if (!script) return;
16576
- await execFileAsync9(process.execPath, [script, target], {
16698
+ await execFileAsync11(process.execPath, [script, target], {
16577
16699
  cwd: root2,
16578
16700
  windowsHide: true,
16579
16701
  timeout: TIMEOUT_MS
@@ -17022,6 +17144,14 @@ async function web(options2) {
17022
17144
  )
17023
17145
  );
17024
17146
  });
17147
+ void ensureTailscaleServe(port).then(
17148
+ (outcome) => console.log(chalk108.dim(`tailscale serve: ${outcome}`)),
17149
+ (error) => console.error(
17150
+ chalk108.yellow(
17151
+ `tailscale serve failed: ${error instanceof Error ? error.message : String(error)}`
17152
+ )
17153
+ )
17154
+ );
17025
17155
  }
17026
17156
 
17027
17157
  // src/commands/backlog/web.ts
@@ -25308,7 +25438,7 @@ function quoteForPosixShell(value) {
25308
25438
  }
25309
25439
 
25310
25440
  // src/commands/sessions/daemon/worktree/stopInstall.ts
25311
- import { execFile as execFile11 } from "child_process";
25441
+ import { execFile as execFile13 } from "child_process";
25312
25442
  var running2 = /* @__PURE__ */ new Map();
25313
25443
  function trackInstall(worktreePath, child) {
25314
25444
  running2.set(worktreePath, child);
@@ -25329,7 +25459,7 @@ function stopInstall(worktreePath) {
25329
25459
  }
25330
25460
  function killTree(pid) {
25331
25461
  if (process.platform === "win32") {
25332
- execFile11(
25462
+ execFile13(
25333
25463
  "taskkill",
25334
25464
  ["/T", "/F", "/PID", String(pid)],
25335
25465
  { windowsHide: true },
@@ -40120,14 +40250,14 @@ function listDaemonPids() {
40120
40250
 
40121
40251
  // src/commands/sessions/daemon/queryDaemon.ts
40122
40252
  import { createInterface as createInterface8 } from "readline";
40123
- var STATUS_TIMEOUT_MS = 5e3;
40253
+ var STATUS_TIMEOUT_MS2 = 5e3;
40124
40254
  function queryDaemon(socket) {
40125
40255
  socket.write(`${JSON.stringify({ type: "ping" })}
40126
40256
  `);
40127
40257
  return new Promise((resolve26) => {
40128
40258
  const result = { sessions: [] };
40129
40259
  const pending = /* @__PURE__ */ new Set(["sessions", "pong"]);
40130
- const timer = setTimeout(() => resolve26(result), STATUS_TIMEOUT_MS);
40260
+ const timer = setTimeout(() => resolve26(result), STATUS_TIMEOUT_MS2);
40131
40261
  const lines2 = createInterface8({ input: socket });
40132
40262
  lines2.on("error", () => {
40133
40263
  });
@@ -41693,13 +41823,13 @@ function isBacklogItemId(text17) {
41693
41823
  }
41694
41824
 
41695
41825
  // src/commands/sessions/daemon/generateSessionTitle.ts
41696
- import { execFile as execFile12 } from "child_process";
41697
- import { promisify as promisify11 } from "util";
41698
- var execFileAsync10 = promisify11(execFile12);
41826
+ import { execFile as execFile14 } from "child_process";
41827
+ import { promisify as promisify13 } from "util";
41828
+ var execFileAsync12 = promisify13(execFile14);
41699
41829
  var SESSION_TITLE_MAX_LENGTH = 48;
41700
41830
  async function generateSessionTitle(prompt) {
41701
41831
  try {
41702
- const { stdout } = await execFileAsync10(
41832
+ const { stdout } = await execFileAsync12(
41703
41833
  "claude",
41704
41834
  ["-p", "--model", "haiku", buildPrompt5(prompt)],
41705
41835
  { encoding: "utf8", windowsHide: true, timeout: 3e4 }
@@ -46792,23 +46922,6 @@ async function sshHop(ssh, probes) {
46792
46922
  );
46793
46923
  }
46794
46924
 
46795
- // src/commands/sessions/nodes/tailscaleStatus.ts
46796
- import { execFile as execFile13 } from "child_process";
46797
- import { promisify as promisify12 } from "util";
46798
- var execFileAsync11 = promisify12(execFile13);
46799
- var STATUS_TIMEOUT_MS2 = 1e4;
46800
- function tailscaleCli() {
46801
- return detectPlatform() === "wsl" ? "tailscale.exe" : "tailscale";
46802
- }
46803
- async function tailscaleStatus() {
46804
- const { stdout } = await execFileAsync11(tailscaleCli(), ["status", "--json"], {
46805
- encoding: "utf8",
46806
- timeout: STATUS_TIMEOUT_MS2,
46807
- windowsHide: true
46808
- });
46809
- return JSON.parse(stdout);
46810
- }
46811
-
46812
46925
  // src/commands/sessions/nodes/findTailscalePeer.ts
46813
46926
  function findTailscalePeer(status2, hostname2) {
46814
46927
  const nodes = [
@@ -46877,7 +46990,7 @@ function tailscaleServeRemediation(spec, error) {
46877
46990
  if (status2 !== void 0 && status2 !== 502) return void 0;
46878
46991
  const { hostname: hostname2, port } = new URL(spec.url);
46879
46992
  const servePort = port || "443";
46880
- return status2 === 502 ? `tailscale serve on ${hostname2} answered but nothing listens on 127.0.0.1:${servePort} behind it \u2014 is ${spec.name}'s web server running (project-switch webservers)?` : `nothing serves https on ${servePort} on ${hostname2} \u2014 run \`assist sessions nodes serve --port ${servePort}\` on ${spec.name}`;
46993
+ return status2 === 502 ? `tailscale serve on ${hostname2} answered but nothing listens on 127.0.0.1:${servePort} behind it \u2014 is ${spec.name}'s web server running (project-switch webservers)?` : `nothing serves https on ${servePort} on ${hostname2} \u2014 ${spec.name}'s web server runs tailscale serve when it starts: check the \`tailscale serve:\` line in its log (project-switch "View logs") and that sessions.tailscaleServe is not false there`;
46881
46994
  }
46882
46995
 
46883
46996
  // src/commands/sessions/nodes/isTailscaleLink.ts
@@ -47242,20 +47355,6 @@ async function writeLinks(links2) {
47242
47355
  saveGlobalConfig(updated2);
47243
47356
  }
47244
47357
 
47245
- // src/commands/sessions/nodes/tailnetSuffix.ts
47246
- function tailnetSuffix(status2) {
47247
- if (status2.BackendState !== "Running")
47248
- throw new Error(
47249
- `Tailscale is not running on this node (${status2.BackendState ?? "unknown state"}) \u2014 run \`${tailscaleCli()} up\``
47250
- );
47251
- const suffix = status2.CurrentTailnet?.MagicDNSSuffix ?? status2.MagicDNSSuffix ?? "";
47252
- if (status2.CurrentTailnet?.MagicDNSEnabled === false || !suffix)
47253
- throw new Error(
47254
- "MagicDNS is off in this tailnet \u2014 turn on MagicDNS and HTTPS certificates on the DNS page of the Tailscale admin console"
47255
- );
47256
- return suffix.replace(/\.$/, "");
47257
- }
47258
-
47259
47358
  // src/commands/sessions/nodes/tailscaleLinkUrl.ts
47260
47359
  async function localTailnetSuffix() {
47261
47360
  return tailnetSuffix(await tailscaleStatus());
@@ -47349,73 +47448,17 @@ function registerLinkCommand(nodes) {
47349
47448
  ).action(linkNode);
47350
47449
  }
47351
47450
 
47352
- // src/commands/sessions/nodes/serveNode.ts
47353
- import { execFile as execFile14, spawnSync as spawnSync10 } from "child_process";
47354
- import { promisify as promisify13 } from "util";
47355
- import chalk258 from "chalk";
47356
-
47357
- // src/commands/sessions/nodes/planServe.ts
47358
- function planServe(status2, serveConfig2, port) {
47359
- tailnetSuffix(status2);
47360
- const fqdn = status2.Self?.DNSName?.replace(/\.$/, "");
47361
- if (!fqdn)
47362
- throw new Error(
47363
- `\`${tailscaleCli()} status --json\` reports no DNS name for this machine`
47364
- );
47365
- const target = `http://127.0.0.1:${port}`;
47366
- const served = serveConfig2.Web?.[`${fqdn}:${port}`]?.Handlers?.["/"]?.Proxy;
47367
- return {
47368
- host: fqdn.split(".")[0],
47369
- url: `https://${fqdn}:${port}`,
47370
- target,
47371
- alreadyServing: served?.replace(/\/$/, "") === target,
47372
- args: ["serve", "--bg", `--https=${port}`, target]
47373
- };
47374
- }
47375
-
47376
- // src/commands/sessions/nodes/serveNode.ts
47377
- var execFileAsync12 = promisify13(execFile14);
47378
- async function serveConfig() {
47379
- const { stdout } = await execFileAsync12(
47380
- tailscaleCli(),
47381
- ["serve", "status", "--json"],
47382
- { encoding: "utf8", timeout: 1e4, windowsHide: true }
47383
- );
47384
- return stdout.trim() ? JSON.parse(stdout) : {};
47385
- }
47386
- function runServe(args) {
47387
- console.log(chalk258.dim(`${tailscaleCli()} ${args.join(" ")}`));
47388
- const result = spawnSync10(tailscaleCli(), args, { stdio: "inherit" });
47389
- if (result.error) throw result.error;
47390
- if (result.status !== 0)
47391
- throw new Error(`tailscale serve exited with ${result.status}`);
47392
- }
47393
- async function serveNode(options2) {
47394
- const port = parsePort("--port", options2.port);
47395
- const plan2 = planServe(await tailscaleStatus(), await serveConfig(), port);
47396
- if (plan2.alreadyServing) {
47397
- console.log(chalk258.dim(`${plan2.url} already serves ${plan2.target}`));
47398
- } else {
47399
- runServe(plan2.args);
47400
- console.log(chalk258.green(`Serving ${plan2.target} at ${plan2.url}`));
47401
- }
47402
- console.log("Link this node from another node with:");
47403
- console.log(
47404
- ` assist sessions nodes link ${resolveNodeName()} --tailscale ${plan2.host} --port ${port}`
47405
- );
47406
- }
47407
-
47408
47451
  // src/commands/sessions/nodes/unlinkNode.ts
47409
- import chalk259 from "chalk";
47452
+ import chalk258 from "chalk";
47410
47453
  async function unlinkNode(name) {
47411
47454
  const links2 = readLinks();
47412
47455
  const remaining = links2.filter((link3) => link3.name !== name);
47413
47456
  if (remaining.length === links2.length) {
47414
- console.log(chalk259.yellow(`No link named ${name}`));
47457
+ console.log(chalk258.yellow(`No link named ${name}`));
47415
47458
  return;
47416
47459
  }
47417
47460
  await writeLinks(remaining);
47418
- console.log(chalk259.green(`Unlinked ${name}`));
47461
+ console.log(chalk258.green(`Unlinked ${name}`));
47419
47462
  await notifyDaemonLinks();
47420
47463
  }
47421
47464
 
@@ -47424,9 +47467,6 @@ function registerNodes(sessions) {
47424
47467
  const cmd = sessions.command("nodes").description("List this node and every linked node with its link state").option("--json", "Output as JSON").action(listNodes);
47425
47468
  registerLinkCommand(cmd);
47426
47469
  cmd.command("unlink <name>").description("Remove a linked node").action(unlinkNode);
47427
- cmd.command("serve").description(
47428
- "Expose this node's web server on the tailnet with tailscale serve (idempotent) and print the command that links it"
47429
- ).option("-p, --port <port>", "This node's web server port", "3100").action(serveNode);
47430
47470
  cmd.command("doctor [name]").description(
47431
47471
  "Probe each hop of every link (or one) in order and stop at the first failure with a remediation"
47432
47472
  ).option("--json", "Output as JSON").action(
@@ -47549,7 +47589,7 @@ async function renameSession(title) {
47549
47589
 
47550
47590
  // src/commands/sessions/summarise.ts
47551
47591
  import * as fs58 from "fs";
47552
- import chalk260 from "chalk";
47592
+ import chalk259 from "chalk";
47553
47593
 
47554
47594
  // src/commands/sessions/summarise/shared.ts
47555
47595
  import * as fs57 from "fs";
@@ -47608,22 +47648,22 @@ ${firstMessage}`);
47608
47648
  async function summarise2(options2) {
47609
47649
  const files = await discoverSessionFiles();
47610
47650
  if (files.length === 0) {
47611
- console.log(chalk260.yellow("No sessions found."));
47651
+ console.log(chalk259.yellow("No sessions found."));
47612
47652
  return;
47613
47653
  }
47614
47654
  const toProcess = selectCandidates(files, options2);
47615
47655
  if (toProcess.length === 0) {
47616
- console.log(chalk260.green("All sessions already summarised."));
47656
+ console.log(chalk259.green("All sessions already summarised."));
47617
47657
  return;
47618
47658
  }
47619
47659
  console.log(
47620
- chalk260.cyan(
47660
+ chalk259.cyan(
47621
47661
  `Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
47622
47662
  )
47623
47663
  );
47624
47664
  const { succeeded, failed: failed3 } = processSessions(toProcess);
47625
47665
  console.log(
47626
- chalk260.green(`Done: ${succeeded} summarised`) + (failed3 > 0 ? chalk260.yellow(`, ${failed3} skipped`) : "")
47666
+ chalk259.green(`Done: ${succeeded} summarised`) + (failed3 > 0 ? chalk259.yellow(`, ${failed3} skipped`) : "")
47627
47667
  );
47628
47668
  }
47629
47669
  function selectCandidates(files, options2) {
@@ -47643,16 +47683,16 @@ function processSessions(files) {
47643
47683
  let failed3 = 0;
47644
47684
  for (let i = 0; i < files.length; i++) {
47645
47685
  const file = files[i];
47646
- process.stdout.write(chalk260.dim(` [${i + 1}/${files.length}] `));
47686
+ process.stdout.write(chalk259.dim(` [${i + 1}/${files.length}] `));
47647
47687
  const summary = summariseSession(file);
47648
47688
  if (summary) {
47649
47689
  writeSummary(file, summary);
47650
47690
  succeeded++;
47651
- process.stdout.write(`${chalk260.green("\u2713")} ${summary}
47691
+ process.stdout.write(`${chalk259.green("\u2713")} ${summary}
47652
47692
  `);
47653
47693
  } else {
47654
47694
  failed3++;
47655
- process.stdout.write(` ${chalk260.yellow("skip")}
47695
+ process.stdout.write(` ${chalk259.yellow("skip")}
47656
47696
  `);
47657
47697
  }
47658
47698
  }
@@ -47667,7 +47707,7 @@ function registerSessions(program2) {
47667
47707
  const webCmd = cmd.command("web").description("Start the sessions web dashboard").option("-p, --port <number>", "Port to listen on", "3100").option("--no-open", "Do not open a browser on startup").action(
47668
47708
  (options2, command) => web({ ...options2, open: command.optsWithGlobals().open })
47669
47709
  );
47670
- configHelp(webCmd, nextConfigHelp);
47710
+ configHelp(webCmd, [...nextConfigHelp, ...webConfigHelp]);
47671
47711
  cmd.command("summarise").description("Generate one-line summaries for Claude sessions").option("-f, --force", "Re-generate all summaries, even existing ones").option("-n, --limit <count>", "Maximum number of sessions to summarise").action(summarise2);
47672
47712
  cmd.command("close").description(
47673
47713
  "Dismiss the current daemon-managed session, ending it and reaping its worktree"
@@ -47682,10 +47722,10 @@ function registerSessions(program2) {
47682
47722
  }
47683
47723
 
47684
47724
  // src/reportCliError.ts
47685
- import chalk261 from "chalk";
47725
+ import chalk260 from "chalk";
47686
47726
  function reportCliError(error) {
47687
47727
  if (error instanceof InvalidItemIdError || error instanceof AmbiguousRepoConfigError || error instanceof UnknownRepoConfigError || error instanceof MissingRunCwdError || error instanceof MiroExtractError) {
47688
- console.error(chalk261.red(error.message));
47728
+ console.error(chalk260.red(error.message));
47689
47729
  } else {
47690
47730
  console.error(error);
47691
47731
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.752.0",
3
+ "version": "0.753.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {