@threadbase-sh/streamer 1.31.3 → 1.33.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/cli.cjs CHANGED
@@ -123460,6 +123460,97 @@ var require_semver2 = __commonJS({
123460
123460
  }
123461
123461
  });
123462
123462
 
123463
+ // src/lifecycle/conflict-check.ts
123464
+ var conflict_check_exports = {};
123465
+ __export(conflict_check_exports, {
123466
+ KNOWN_STREAMER_LABELS: () => KNOWN_STREAMER_LABELS,
123467
+ detectConflictingAgents: () => detectConflictingAgents,
123468
+ formatConflictMessage: () => formatConflictMessage
123469
+ });
123470
+ function uidScope() {
123471
+ return `gui/${process.getuid?.() ?? 501}`;
123472
+ }
123473
+ function resolveOwnLabel() {
123474
+ if (process.env.LAUNCHD_LABEL) return process.env.LAUNCHD_LABEL;
123475
+ try {
123476
+ (0, import_node_child_process4.execFileSync)("launchctl", ["print", `${uidScope()}/homebrew.mxcl.tb-streamer`], {
123477
+ stdio: "ignore"
123478
+ });
123479
+ return "homebrew.mxcl.tb-streamer";
123480
+ } catch {
123481
+ return LAUNCHD_LABEL;
123482
+ }
123483
+ }
123484
+ function isLabelLoaded(label) {
123485
+ try {
123486
+ (0, import_node_child_process4.execFileSync)("launchctl", ["list", label], { stdio: "ignore" });
123487
+ return true;
123488
+ } catch {
123489
+ return false;
123490
+ }
123491
+ }
123492
+ function detectConflictingAgents() {
123493
+ if (process.platform !== "darwin") return [];
123494
+ const ownLabel = resolveOwnLabel();
123495
+ const conflicts = [];
123496
+ for (const label of KNOWN_STREAMER_LABELS) {
123497
+ if (label === ownLabel) continue;
123498
+ if (isLabelLoaded(label)) {
123499
+ conflicts.push({
123500
+ label,
123501
+ resolution: label === "homebrew.mxcl.tb-streamer" ? "uninstall-homebrew" : "bootout"
123502
+ });
123503
+ }
123504
+ }
123505
+ try {
123506
+ const out = (0, import_node_child_process4.execFileSync)("launchctl", ["list"], {
123507
+ stdio: ["ignore", "pipe", "ignore"]
123508
+ }).toString();
123509
+ for (const line of out.split("\n")) {
123510
+ const parts = line.trim().split(/\s+/);
123511
+ const label = parts[2];
123512
+ if (!label) continue;
123513
+ if (LEGACY_PATTERN.test(label) && label !== ownLabel && !conflicts.some((c) => c.label === label)) {
123514
+ conflicts.push({ label, resolution: "bootout" });
123515
+ }
123516
+ }
123517
+ } catch {
123518
+ }
123519
+ return conflicts;
123520
+ }
123521
+ function formatConflictMessage(conflicts) {
123522
+ if (conflicts.length === 0) return "";
123523
+ const lines = ["Conflicting Threadbase streamer agents detected:", ""];
123524
+ for (const c of conflicts) {
123525
+ if (c.resolution === "uninstall-homebrew") {
123526
+ lines.push(
123527
+ ` \u2022 ${c.label} \u2014 run: brew services stop tb-streamer && brew uninstall tb-streamer`
123528
+ );
123529
+ } else {
123530
+ lines.push(` \u2022 ${c.label} \u2014 run: launchctl bootout gui/$(id -u)/${c.label}`);
123531
+ }
123532
+ }
123533
+ lines.push(
123534
+ "",
123535
+ "Only one Threadbase streamer can bind port 8766. Remove the conflicting agent(s) to proceed."
123536
+ );
123537
+ return lines.join("\n");
123538
+ }
123539
+ var import_node_child_process4, KNOWN_STREAMER_LABELS, LEGACY_PATTERN;
123540
+ var init_conflict_check = __esm({
123541
+ "src/lifecycle/conflict-check.ts"() {
123542
+ "use strict";
123543
+ import_node_child_process4 = require("child_process");
123544
+ init_constants();
123545
+ KNOWN_STREAMER_LABELS = [
123546
+ "com.ronen.threadbase",
123547
+ "homebrew.mxcl.tb-streamer",
123548
+ "com.threadbase.streamer"
123549
+ ];
123550
+ LEGACY_PATTERN = /^com\.threadbase\.streamer/;
123551
+ }
123552
+ });
123553
+
123463
123554
  // src/lifecycle/launchd.ts
123464
123555
  var launchd_exports = {};
123465
123556
  __export(launchd_exports, {
@@ -123471,13 +123562,13 @@ __export(launchd_exports, {
123471
123562
  isAgentLoaded: () => isAgentLoaded,
123472
123563
  kickstartAgent: () => kickstartAgent
123473
123564
  });
123474
- function uidScope() {
123565
+ function uidScope2() {
123475
123566
  return `gui/${process.getuid?.() ?? 501}`;
123476
123567
  }
123477
123568
  function resolveLoadedLabel() {
123478
123569
  if (process.env.LAUNCHD_LABEL) return process.env.LAUNCHD_LABEL;
123479
123570
  try {
123480
- (0, import_node_child_process4.execFileSync)("launchctl", ["print", `${uidScope()}/${BREW_LAUNCHD_LABEL2}`], {
123571
+ (0, import_node_child_process5.execFileSync)("launchctl", ["print", `${uidScope2()}/${BREW_LAUNCHD_LABEL2}`], {
123481
123572
  stdio: "ignore"
123482
123573
  });
123483
123574
  return BREW_LAUNCHD_LABEL2;
@@ -123486,11 +123577,11 @@ function resolveLoadedLabel() {
123486
123577
  }
123487
123578
  }
123488
123579
  function fullTarget() {
123489
- return `${uidScope()}/${resolveLoadedLabel()}`;
123580
+ return `${uidScope2()}/${resolveLoadedLabel()}`;
123490
123581
  }
123491
123582
  function isAgentLoaded() {
123492
123583
  try {
123493
- (0, import_node_child_process4.execFileSync)("launchctl", ["list", resolveLoadedLabel()], { stdio: "ignore" });
123584
+ (0, import_node_child_process5.execFileSync)("launchctl", ["list", resolveLoadedLabel()], { stdio: "ignore" });
123494
123585
  return true;
123495
123586
  } catch {
123496
123587
  return false;
@@ -123498,7 +123589,7 @@ function isAgentLoaded() {
123498
123589
  }
123499
123590
  function bootoutAgent(timeoutMs = 2e3) {
123500
123591
  try {
123501
- (0, import_node_child_process4.execFileSync)("launchctl", ["bootout", fullTarget()], { stdio: "ignore" });
123592
+ (0, import_node_child_process5.execFileSync)("launchctl", ["bootout", fullTarget()], { stdio: "ignore" });
123502
123593
  } catch {
123503
123594
  }
123504
123595
  const deadline = Date.now() + timeoutMs;
@@ -123510,7 +123601,7 @@ function bootoutAgent(timeoutMs = 2e3) {
123510
123601
  }
123511
123602
  function bootstrapAgent(plistPath) {
123512
123603
  try {
123513
- (0, import_node_child_process4.execFileSync)("launchctl", ["bootstrap", uidScope(), plistPath], {
123604
+ (0, import_node_child_process5.execFileSync)("launchctl", ["bootstrap", uidScope2(), plistPath], {
123514
123605
  stdio: ["ignore", "ignore", "pipe"]
123515
123606
  });
123516
123607
  } catch (err) {
@@ -123523,7 +123614,7 @@ function bootstrapAgent(plistPath) {
123523
123614
  }
123524
123615
  }
123525
123616
  function kickstartAgent() {
123526
- (0, import_node_child_process4.execFileSync)("launchctl", ["kickstart", "-k", fullTarget()], { stdio: "ignore" });
123617
+ (0, import_node_child_process5.execFileSync)("launchctl", ["kickstart", "-k", fullTarget()], { stdio: "ignore" });
123527
123618
  }
123528
123619
  function darwinPlistPath() {
123529
123620
  return `${process.env.HOME}/Library/LaunchAgents/${resolveLoadedLabel()}.plist`;
@@ -123534,7 +123625,7 @@ function getLogPaths() {
123534
123625
  }
123535
123626
  function getAgentPid() {
123536
123627
  try {
123537
- const out = (0, import_node_child_process4.execFileSync)("launchctl", ["list"], {
123628
+ const out = (0, import_node_child_process5.execFileSync)("launchctl", ["list"], {
123538
123629
  stdio: ["ignore", "pipe", "ignore"]
123539
123630
  }).toString();
123540
123631
  const label = resolveLoadedLabel();
@@ -123550,11 +123641,11 @@ function getAgentPid() {
123550
123641
  return null;
123551
123642
  }
123552
123643
  }
123553
- var import_node_child_process4, import_node_path25, BREW_LAUNCHD_LABEL2;
123644
+ var import_node_child_process5, import_node_path25, BREW_LAUNCHD_LABEL2;
123554
123645
  var init_launchd = __esm({
123555
123646
  "src/lifecycle/launchd.ts"() {
123556
123647
  "use strict";
123557
- import_node_child_process4 = require("child_process");
123648
+ import_node_child_process5 = require("child_process");
123558
123649
  import_node_path25 = require("path");
123559
123650
  init_constants();
123560
123651
  BREW_LAUNCHD_LABEL2 = "homebrew.mxcl.tb-streamer";
@@ -123627,7 +123718,7 @@ __export(task_scheduler_exports, {
123627
123718
  kickstartAgent: () => kickstartAgent2
123628
123719
  });
123629
123720
  function ps2(command) {
123630
- return (0, import_node_child_process5.execFileSync)("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
123721
+ return (0, import_node_child_process6.execFileSync)("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
123631
123722
  stdio: ["ignore", "pipe", "ignore"]
123632
123723
  }).toString();
123633
123724
  }
@@ -123673,11 +123764,11 @@ function getAgentPid2() {
123673
123764
  return null;
123674
123765
  }
123675
123766
  }
123676
- var import_node_child_process5;
123767
+ var import_node_child_process6;
123677
123768
  var init_task_scheduler = __esm({
123678
123769
  "src/lifecycle/task-scheduler.ts"() {
123679
123770
  "use strict";
123680
- import_node_child_process5 = require("child_process");
123771
+ import_node_child_process6 = require("child_process");
123681
123772
  init_constants();
123682
123773
  }
123683
123774
  });
@@ -124021,7 +124112,7 @@ __export(repo_exports, {
124021
124112
  });
124022
124113
  function getGitToplevel(cwd) {
124023
124114
  try {
124024
- return (0, import_node_child_process7.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
124115
+ return (0, import_node_child_process8.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
124025
124116
  cwd,
124026
124117
  stdio: ["ignore", "pipe", "ignore"]
124027
124118
  }).toString().trim();
@@ -124029,11 +124120,11 @@ function getGitToplevel(cwd) {
124029
124120
  return null;
124030
124121
  }
124031
124122
  }
124032
- var import_node_child_process7;
124123
+ var import_node_child_process8;
124033
124124
  var init_repo = __esm({
124034
124125
  "src/lifecycle/repo.ts"() {
124035
124126
  "use strict";
124036
- import_node_child_process7 = require("child_process");
124127
+ import_node_child_process8 = require("child_process");
124037
124128
  }
124038
124129
  });
124039
124130
 
@@ -141039,6 +141130,10 @@ var PTYManager = class {
141039
141130
  options.permissionMode ?? "acceptEdits",
141040
141131
  "--settings",
141041
141132
  '{"spinnerTipsEnabled":false}',
141133
+ "--model",
141134
+ options.model ?? "sonnet",
141135
+ "--effort",
141136
+ options.effort ?? "low",
141042
141137
  "--resume",
141043
141138
  sessionId
141044
141139
  ],
@@ -141089,6 +141184,10 @@ var PTYManager = class {
141089
141184
  options.permissionMode ?? "acceptEdits",
141090
141185
  "--settings",
141091
141186
  '{"spinnerTipsEnabled":false}',
141187
+ "--model",
141188
+ options.model ?? "sonnet",
141189
+ "--effort",
141190
+ options.effort ?? "low",
141092
141191
  "--session-id",
141093
141192
  sessionId
141094
141193
  ];
@@ -144787,6 +144886,8 @@ var StreamerServer = class {
144787
144886
  ptyGracePeriodMs;
144788
144887
  defaultSystemPrompt;
144789
144888
  defaultPermissionMode;
144889
+ defaultModel;
144890
+ defaultEffort;
144790
144891
  // Map of sessionId → grace timer; fires to kill PTY after WS disconnect
144791
144892
  ptyGraceTimers = /* @__PURE__ */ new Map();
144792
144893
  // Map of sessionId → set of subscribed WS clients
@@ -144836,6 +144937,8 @@ var StreamerServer = class {
144836
144937
  this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
144837
144938
  this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
144838
144939
  this.defaultPermissionMode = config2.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
144940
+ this.defaultModel = config2.defaultModel ?? "sonnet";
144941
+ this.defaultEffort = config2.defaultEffort ?? "low";
144839
144942
  this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path23.join)((0, import_os11.homedir)(), ".threadbase", "cache");
144840
144943
  this.tailSize = config2.tailSize ?? loadTailSize() ?? 10;
144841
144944
  this.directoryDebounceMs = parseDirScanDebounceEnv(process.env.THREADBASE_DIR_SCAN_DEBOUNCE_MS) ?? config2.directoryScanDebounceMs ?? 1e3;
@@ -146501,7 +146604,9 @@ var StreamerServer = class {
146501
146604
  projectPath,
146502
146605
  projectName: body.projectName,
146503
146606
  branch: body.branch,
146504
- permissionMode: this.defaultPermissionMode
146607
+ permissionMode: this.defaultPermissionMode,
146608
+ model: this.defaultModel,
146609
+ effort: this.defaultEffort
146505
146610
  });
146506
146611
  this.sessionStore.addManaged(session);
146507
146612
  void this.watchConversationFile(sessionId);
@@ -146831,7 +146936,9 @@ var StreamerServer = class {
146831
146936
  projectPath,
146832
146937
  projectName,
146833
146938
  branch,
146834
- permissionMode: this.defaultPermissionMode
146939
+ permissionMode: this.defaultPermissionMode,
146940
+ model: this.defaultModel,
146941
+ effort: this.defaultEffort
146835
146942
  });
146836
146943
  this.sessionStore.addManaged(session);
146837
146944
  void this.watchConversationFile(session.id);
@@ -146902,7 +147009,9 @@ var StreamerServer = class {
146902
147009
  projectPath: resolvedPath,
146903
147010
  projectName: body.projectName,
146904
147011
  systemPrompt: systemPromptParts.join("\n"),
146905
- permissionMode: this.defaultPermissionMode
147012
+ permissionMode: this.defaultPermissionMode,
147013
+ model: this.defaultModel,
147014
+ effort: this.defaultEffort
146906
147015
  });
146907
147016
  this.sessionStore.addManaged(session);
146908
147017
  const readyOrFailed = new Promise((resolve6) => {
@@ -151006,8 +151115,9 @@ async function runInstall(opts) {
151006
151115
  }
151007
151116
 
151008
151117
  // cli/prod.ts
151009
- var import_node_child_process6 = require("child_process");
151118
+ var import_node_child_process7 = require("child_process");
151010
151119
  var import_node_fs22 = require("fs");
151120
+ init_conflict_check();
151011
151121
  init_constants();
151012
151122
  init_launchd();
151013
151123
  init_marker();
@@ -151090,6 +151200,18 @@ async function runProdDoctor(opts) {
151090
151200
  if (!getSupervisor().isAgentLoaded()) {
151091
151201
  findings.push("launchd agent is not loaded \u2014 prod is fully down");
151092
151202
  }
151203
+ const conflicts = detectConflictingAgents();
151204
+ for (const c of conflicts) {
151205
+ if (c.resolution === "uninstall-homebrew") {
151206
+ findings.push(
151207
+ `conflicting agent: ${c.label} \u2014 run: brew services stop tb-streamer && brew uninstall tb-streamer`
151208
+ );
151209
+ } else {
151210
+ findings.push(
151211
+ `conflicting agent: ${c.label} \u2014 run: launchctl bootout gui/$(id -u)/${c.label}`
151212
+ );
151213
+ }
151214
+ }
151093
151215
  return { findings, repairs };
151094
151216
  }
151095
151217
  var defaultSpawnTail = ({ files, lines, follow }) => {
@@ -151104,7 +151226,7 @@ var defaultSpawnTail = ({ files, lines, follow }) => {
151104
151226
  if (follow) args.push("-F");
151105
151227
  args.push(...existing);
151106
151228
  return new Promise((resolve6) => {
151107
- const child = (0, import_node_child_process6.spawn)("tail", args, { stdio: ["ignore", "inherit", "inherit"] });
151229
+ const child = (0, import_node_child_process7.spawn)("tail", args, { stdio: ["ignore", "inherit", "inherit"] });
151108
151230
  child.on("error", (err) => resolve6({ ok: false, message: `tail failed: ${err.message}` }));
151109
151231
  child.on("exit", (code) => resolve6({ ok: code === 0 }));
151110
151232
  });
@@ -151210,6 +151332,12 @@ program2.command("serve").description("Start the streamer server").option("-p, -
151210
151332
  ).option(
151211
151333
  "--default-permission-mode <mode>",
151212
151334
  "Claude Code permission mode for spawned sessions: acceptEdits (auto-approve file edits, default) or manual (prompt for everything). Falls back to default_permission_mode: in ~/.threadbase/server.yaml, or a first-run interactive prompt on a human TTY invocation (skip with THREADBASE_SKIP_PERMISSION_MODE_PROMPT=true)."
151335
+ ).option(
151336
+ "--default-model <model>",
151337
+ "Claude Code --model for spawned sessions (alias like 'sonnet'/'opus' or a full model name). Default: sonnet."
151338
+ ).option(
151339
+ "--default-effort <level>",
151340
+ "Claude Code --effort for spawned sessions: low, medium, high, xhigh, or max. Default: low."
151213
151341
  ).option(
151214
151342
  "--pty-grace-period-ms <ms>",
151215
151343
  "Ms to keep a PTY alive after the last WebSocket subscriber disconnects before auto-holding it (default 270000, 4.5 min). 0 disables auto-hold entirely (an explicit hold_session still works). Falls back to pty_grace_period_ms: in ~/.threadbase/server.yaml."
@@ -151240,6 +151368,15 @@ program2.command("serve").description("Start the streamer server").option("-p, -
151240
151368
  );
151241
151369
  process.exit(1);
151242
151370
  }
151371
+ const validEfforts = ["low", "medium", "high", "xhigh", "max"];
151372
+ if (opts.defaultEffort !== void 0 && !validEfforts.includes(opts.defaultEffort)) {
151373
+ log7.error(
151374
+ `Invalid --default-effort: ${opts.defaultEffort} (expected one of ${validEfforts.join(", ")})`,
151375
+ void 0,
151376
+ "console"
151377
+ );
151378
+ process.exit(1);
151379
+ }
151243
151380
  let ptyGracePeriodMs;
151244
151381
  if (opts.ptyGracePeriodMs !== void 0) {
151245
151382
  const parsed = Number(opts.ptyGracePeriodMs);
@@ -151260,6 +151397,13 @@ program2.command("serve").description("Start the streamer server").option("-p, -
151260
151397
  const publicUrl = opts.publicUrl ?? loadPublicUrl() ?? null;
151261
151398
  const isProdInvocation = opts.prod === true || process.ppid === 1;
151262
151399
  if (!isProdInvocation) appendDevSessionMarker();
151400
+ if (process.platform === "darwin") {
151401
+ const { detectConflictingAgents: detectConflictingAgents2, formatConflictMessage: formatConflictMessage2 } = await Promise.resolve().then(() => (init_conflict_check(), conflict_check_exports));
151402
+ const conflicts = detectConflictingAgents2();
151403
+ if (conflicts.length > 0) {
151404
+ log7.warn(formatConflictMessage2(conflicts), void 0, "console");
151405
+ }
151406
+ }
151263
151407
  let resolvedDefaultPermissionMode = opts.defaultPermissionMode;
151264
151408
  if (resolvedDefaultPermissionMode === void 0 && !isProdInvocation && process.env.THREADBASE_SKIP_PERMISSION_MODE_PROMPT !== "true" && loadDefaultPermissionMode() === void 0 && import_node_process3.stdin.isTTY) {
151265
151409
  const { interactivePermissionModePrompt: interactivePermissionModePrompt2 } = await Promise.resolve().then(() => (init_prompt(), prompt_exports));
@@ -151299,6 +151443,8 @@ program2.command("serve").description("Start the streamer server").option("-p, -
151299
151443
  browseRoot: opts.browseRoot,
151300
151444
  publicUrl: opts.publicUrl,
151301
151445
  defaultPermissionMode: resolvedDefaultPermissionMode,
151446
+ defaultModel: opts.defaultModel,
151447
+ defaultEffort: opts.defaultEffort,
151302
151448
  ptyGracePeriodMs
151303
151449
  });
151304
151450
  await server.listen(resolvedPort);