@threadbase-sh/streamer 1.30.0 → 1.31.1

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
@@ -137480,7 +137480,7 @@ function isLocalRequest(remoteAddr) {
137480
137480
  const addr = remoteAddr ?? "";
137481
137481
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
137482
137482
  }
137483
- var PUBLIC_PATHS = /* @__PURE__ */ new Set(["/healthz", "/ws"]);
137483
+ var PUBLIC_PATHS = /* @__PURE__ */ new Set(["/healthz"]);
137484
137484
  var LOCAL_ONLY_PATHS = /* @__PURE__ */ new Set(["/api/logs", "/api/logs/meta"]);
137485
137485
  var PUBLIC_POST_PATHS = /* @__PURE__ */ new Set(["/api/pair/exchange", "/api/__update"]);
137486
137486
  var PUBLIC_POST_PREFIXES = ["/internal/sessions/"];
@@ -137935,7 +137935,7 @@ function verifySignature(rawBody, signature, secret) {
137935
137935
  }
137936
137936
  }
137937
137937
  function isWithinSkew(timestampHeader, skewSeconds) {
137938
- if (!timestampHeader) return false;
137938
+ if (!timestampHeader) return true;
137939
137939
  const t = Number(timestampHeader);
137940
137940
  if (!Number.isFinite(t)) return false;
137941
137941
  const now = Math.floor(Date.now() / 1e3);
@@ -138146,21 +138146,18 @@ var createSessionRoutes = (deps) => {
138146
138146
  };
138147
138147
 
138148
138148
  // src/api/routes/ws.routes.ts
138149
- init_auth();
138150
138149
  var createWsRoutes = (deps, upgradeWebSocket) => {
138151
138150
  const app = new Hono2();
138152
138151
  app.get(
138153
138152
  "/ws",
138154
- upgradeWebSocket((c) => {
138155
- const key = c.req.query("key");
138156
- const preAuthed = typeof key === "string" && validateApiKey(key, deps.apiKey);
138153
+ upgradeWebSocket(() => {
138157
138154
  let openWs = null;
138158
138155
  return {
138159
138156
  onOpen(_evt, ws2) {
138160
138157
  const raw2 = ws2.raw;
138161
138158
  if (!raw2) return;
138162
138159
  openWs = raw2;
138163
- deps.handleWsOpen(raw2, preAuthed);
138160
+ deps.handleWsOpen(raw2);
138164
138161
  },
138165
138162
  onMessage(evt, _ws) {
138166
138163
  if (openWs) deps.handleWsMessage(openWs, evt.data);
@@ -140064,6 +140061,7 @@ function debounce(fn, waitMs) {
140064
140061
 
140065
140062
  // src/codex-pty-runner.ts
140066
140063
  var OUTPUT_BUFFER_MAX = 65536;
140064
+ var INPUT_HISTORY_MAX = 50;
140067
140065
  var PTY_COLS = 120;
140068
140066
  var PTY_ROWS = 40;
140069
140067
  var SCREEN_SCROLLBACK = 1e3;
@@ -140144,6 +140142,7 @@ var CodexPtyRunner = class {
140144
140142
  onPermissionChange;
140145
140143
  onLiveQuestion;
140146
140144
  onLiveQuestionGone;
140145
+ onUserMessage;
140147
140146
  log;
140148
140147
  // Tracks sessions whose PTY has spawned but Codex hasn't yet reached its
140149
140148
  // "Ready" status bar — i.e. onReady hasn't fired.
@@ -140174,6 +140173,7 @@ var CodexPtyRunner = class {
140174
140173
  this.onPermissionChange = options.onPermissionChange;
140175
140174
  this.onLiveQuestion = options.onLiveQuestion;
140176
140175
  this.onLiveQuestionGone = options.onLiveQuestionGone;
140176
+ this.onUserMessage = options.onUserMessage;
140177
140177
  this.log = options.logger ?? getLogger("codex-pty");
140178
140178
  }
140179
140179
  // Resume an existing Codex session. sessionId is the Codex-persisted
@@ -140217,7 +140217,8 @@ var CodexPtyRunner = class {
140217
140217
  lastOutput: "",
140218
140218
  process: proc,
140219
140219
  outputBuffer: Buffer.alloc(0),
140220
- screen: createScreen()
140220
+ screen: createScreen(),
140221
+ inputHistory: []
140221
140222
  };
140222
140223
  this.sessions.set(sessionId, session);
140223
140224
  this.pendingReady.add(sessionId);
@@ -140263,7 +140264,8 @@ var CodexPtyRunner = class {
140263
140264
  lastOutput: "",
140264
140265
  process: proc,
140265
140266
  outputBuffer: Buffer.alloc(0),
140266
- screen: createScreen()
140267
+ screen: createScreen(),
140268
+ inputHistory: []
140267
140269
  };
140268
140270
  this.sessions.set(sessionId, session);
140269
140271
  this.pendingReady.add(sessionId);
@@ -140381,6 +140383,7 @@ var CodexPtyRunner = class {
140381
140383
  // confirmed Codex accepts plain keystrokes), then submit \r after a short
140382
140384
  // delay so Codex's TUI gets an event-loop tick to process the input first.
140383
140385
  writeSubmit(sessionId, session, input, path2, promptCount) {
140386
+ this.recordUserMessage(session, input);
140384
140387
  this.log.info(
140385
140388
  `[codex.input.write] ${sessionId.slice(0, 8)} promptCount=${promptCount} bytes=${input.length} digest=${digestBytes(input)}`,
140386
140389
  {
@@ -140510,6 +140513,19 @@ var CodexPtyRunner = class {
140510
140513
  }
140511
140514
  return lines.slice(-maxLines);
140512
140515
  }
140516
+ getInputHistory(sessionId) {
140517
+ return this.sessions.get(sessionId)?.inputHistory ?? [];
140518
+ }
140519
+ // Record a submitted user message as ground truth and fire onUserMessage.
140520
+ // Called from writeSubmit (direct and flush paths) — never from sendKeys.
140521
+ recordUserMessage(session, text) {
140522
+ const ts2 = Date.now();
140523
+ session.inputHistory.push({ text, ts: ts2 });
140524
+ if (session.inputHistory.length > INPUT_HISTORY_MAX) {
140525
+ session.inputHistory.shift();
140526
+ }
140527
+ this.onUserMessage?.(session.id, text, ts2);
140528
+ }
140513
140529
  getSession(sessionId) {
140514
140530
  const session = this.sessions.get(sessionId);
140515
140531
  return session ? toPublicSession(session) : null;
@@ -140888,6 +140904,7 @@ function detectShellPrompt(lines) {
140888
140904
 
140889
140905
  // src/pty-manager.ts
140890
140906
  var OUTPUT_BUFFER_MAX2 = 65536;
140907
+ var INPUT_HISTORY_MAX2 = 50;
140891
140908
  var PTY_COLS2 = 120;
140892
140909
  var PTY_ROWS2 = 40;
140893
140910
  var SCREEN_SCROLLBACK2 = 1e3;
@@ -140946,6 +140963,7 @@ var PTYManager = class {
140946
140963
  onPermissionChange;
140947
140964
  onLiveQuestion;
140948
140965
  onLiveQuestionGone;
140966
+ onUserMessage;
140949
140967
  // Per-session permission-gate state. True between an OSC 777 (gate open) and
140950
140968
  // the next prompt-ready without a fresh 777 (gate closed). Prevents
140951
140969
  // re-broadcasting open/close on every chunk.
@@ -140991,6 +141009,7 @@ var PTYManager = class {
140991
141009
  this.onPermissionChange = options.onPermissionChange;
140992
141010
  this.onLiveQuestion = options.onLiveQuestion;
140993
141011
  this.onLiveQuestionGone = options.onLiveQuestionGone;
141012
+ this.onUserMessage = options.onUserMessage;
140994
141013
  this.log = options.logger ?? getLogger("pty");
140995
141014
  }
140996
141015
  // Resume an existing Claude conversation. sessionId is the JSONL UUID.
@@ -141053,7 +141072,8 @@ var PTYManager = class {
141053
141072
  lastOutput: "",
141054
141073
  process: proc,
141055
141074
  outputBuffer: Buffer.alloc(0),
141056
- screen: createScreen2()
141075
+ screen: createScreen2(),
141076
+ inputHistory: []
141057
141077
  };
141058
141078
  this.sessions.set(sessionId, session);
141059
141079
  this.pendingReady.add(sessionId);
@@ -141104,7 +141124,8 @@ var PTYManager = class {
141104
141124
  lastOutput: "",
141105
141125
  process: proc,
141106
141126
  outputBuffer: Buffer.alloc(0),
141107
- screen: createScreen2()
141127
+ screen: createScreen2(),
141128
+ inputHistory: []
141108
141129
  };
141109
141130
  this.sessions.set(sessionId, session);
141110
141131
  this.pendingReady.add(sessionId);
@@ -141184,6 +141205,7 @@ var PTYManager = class {
141184
141205
  // step gives the TUI as many extra ticks as it needs, capped at
141185
141206
  // SUBMIT_MAX_WAIT_MS so a silent/wedged PTY still gets its \r eventually.
141186
141207
  writeSubmit(sessionId, session, input, path2, promptCount) {
141208
+ this.recordUserMessage(session, input);
141187
141209
  const pasteBytes = buildPasteBytes(input);
141188
141210
  this.log.info(
141189
141211
  `[pty.input.write] ${sessionId.slice(0, 8)} promptCount=${promptCount} bytes=${pasteBytes.length} digest=${digestBytes2(pasteBytes)}`,
@@ -141314,6 +141336,20 @@ var PTYManager = class {
141314
141336
  }
141315
141337
  return lines.slice(-maxLines);
141316
141338
  }
141339
+ getInputHistory(sessionId) {
141340
+ return this.sessions.get(sessionId)?.inputHistory ?? [];
141341
+ }
141342
+ // Record a submitted user message as ground truth and fire onUserMessage.
141343
+ // Called from writeSubmit (both direct and flush paths) — never from
141344
+ // sendKeys, so raw keystrokes aren't logged as messages.
141345
+ recordUserMessage(session, text) {
141346
+ const ts2 = Date.now();
141347
+ session.inputHistory.push({ text, ts: ts2 });
141348
+ if (session.inputHistory.length > INPUT_HISTORY_MAX2) {
141349
+ session.inputHistory.shift();
141350
+ }
141351
+ this.onUserMessage?.(session.id, text, ts2);
141352
+ }
141317
141353
  getSession(sessionId) {
141318
141354
  const session = this.sessions.get(sessionId);
141319
141355
  return session ? toPublicSession2(session) : null;
@@ -141610,6 +141646,9 @@ var LiveSessionManager = class {
141610
141646
  getOutputLines(sessionId, maxLines) {
141611
141647
  return this.runnerFor(sessionId).getOutputLines(sessionId, maxLines);
141612
141648
  }
141649
+ getInputHistory(sessionId) {
141650
+ return this.runnerFor(sessionId).getInputHistory(sessionId);
141651
+ }
141613
141652
  getSession(sessionId) {
141614
141653
  for (const runner of this.runners.values()) {
141615
141654
  const session = runner.getSession(sessionId);
@@ -143833,13 +143872,6 @@ function deriveProjectChatTitle(input) {
143833
143872
  return `Untitled \xB7 ${input.id.slice(0, 8)}`;
143834
143873
  }
143835
143874
 
143836
- // src/services/questions/permissionAnswerKeys.ts
143837
- var ANSWER_KEYS_ALLOWLIST = /^(?:\r|[yn]\r|\x03|\d+\r)$/;
143838
- function sanitizeAnswerKeys(keys) {
143839
- if (keys === void 0) return void 0;
143840
- return ANSWER_KEYS_ALLOWLIST.test(keys) ? keys : void 0;
143841
- }
143842
-
143843
143875
  // src/services/questions/detectAskUserQuestion.ts
143844
143876
  function normalizeContent2(raw2) {
143845
143877
  if (Array.isArray(raw2)) return raw2;
@@ -144682,8 +144714,6 @@ var WSHub = class {
144682
144714
  var BROWSE_SYSTEM_PROMPT = (browseRoot) => `You are working within the project boundary: ${browseRoot}. Do not read, write, or execute commands that access files or directories outside this boundary.`;
144683
144715
  var DEFAULT_SYSTEM_PROMPT = "When presenting options or choices to the user, limit the options to at most 3.";
144684
144716
  var DEFAULT_PTY_GRACE_PERIOD_MS = 27e4;
144685
- var DEFAULT_WS_AUTH_TIMEOUT_MS = 5e3;
144686
- var WS_CLOSE_UNAUTHORIZED = 4401;
144687
144717
  var REFRESH_TTL_MS = 2e3;
144688
144718
  var START_READY_TIMEOUT_MS = 1e4;
144689
144719
  function parseIncludeAgentsEnv(raw2) {
@@ -144774,14 +144804,6 @@ var StreamerServer = class {
144774
144804
  clientIdToWs = /* @__PURE__ */ new Map();
144775
144805
  // Reverse map for cleanup on close
144776
144806
  wsToClientId = /* @__PURE__ */ new Map();
144777
- // M1 — WS auth. Sockets that have authenticated (via ?key= at upgrade OR a
144778
- // { type: "auth", token } first message). Only authed sockets are added to
144779
- // the hub and receive broadcasts.
144780
- // Plan: https://github.com/RonenMars/threadbase-streamer/blob/a251353bfa417bd48ce3f15086bc336a2c622629/docs/plans/2026-06-24-security-hardening.md#L40
144781
- wsAuthed = /* @__PURE__ */ new Set();
144782
- // Keyless sockets awaiting their first-message auth handshake → close timer.
144783
- wsAuthPending = /* @__PURE__ */ new Map();
144784
- wsAuthTimeoutMs;
144785
144807
  cache = null;
144786
144808
  projectsRepo = null;
144787
144809
  conversationsRepo = null;
@@ -144821,7 +144843,6 @@ var StreamerServer = class {
144821
144843
  this.scanProfiles = config2.scanProfiles;
144822
144844
  this.codexRoots = config2.codexRoots ?? [(0, import_path23.join)((0, import_os11.homedir)(), ".codex", "sessions")];
144823
144845
  this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
144824
- this.wsAuthTimeoutMs = config2.wsAuthTimeoutMs ?? DEFAULT_WS_AUTH_TIMEOUT_MS;
144825
144846
  this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
144826
144847
  this.defaultPermissionMode = config2.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
144827
144848
  this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path23.join)((0, import_os11.homedir)(), ".threadbase", "cache");
@@ -144950,6 +144971,9 @@ var StreamerServer = class {
144950
144971
  onOutput: (sessionId, data) => {
144951
144972
  this.wsHub.broadcast({ type: "terminal_output", sessionId, data });
144952
144973
  },
144974
+ onUserMessage: (sessionId, text, ts2) => {
144975
+ this.wsHub.broadcast({ type: "user_message", sessionId, text, ts: ts2 });
144976
+ },
144953
144977
  onPermissionChange: (sessionId, gate) => {
144954
144978
  this.handlePermissionChange(sessionId, gate);
144955
144979
  },
@@ -145079,39 +145103,17 @@ var StreamerServer = class {
145079
145103
  handlePairExchange: (req, res) => this.handlePairExchange(req, res),
145080
145104
  handleBrowse: (url2, res) => this.handleBrowse(url2, res),
145081
145105
  handleMkdir: (req, res) => this.handleMkdir(req, res),
145082
- handleWsOpen: (ws2, preAuthed) => {
145083
- if (preAuthed) {
145084
- this.completeWsAuth(ws2);
145085
- return;
145106
+ handleWsOpen: (ws2) => {
145107
+ this.wsHub.addClient(ws2);
145108
+ const sessions = this.sessionStore.list(this.ptyAttachedIds());
145109
+ ws2.send(JSON.stringify({ type: "session_list", sessions }));
145110
+ if (this.cacheReady) {
145111
+ ws2.send(JSON.stringify({ type: "cache_ready" }));
145086
145112
  }
145087
- const timer = setTimeout(() => {
145088
- this.wsAuthPending.delete(ws2);
145089
- try {
145090
- ws2.close(WS_CLOSE_UNAUTHORIZED, "auth timeout");
145091
- } catch {
145092
- }
145093
- }, this.wsAuthTimeoutMs);
145094
- this.wsAuthPending.set(ws2, timer);
145095
145113
  },
145096
145114
  handleWsMessage: async (ws2, raw2) => {
145097
145115
  try {
145098
145116
  const msg = JSON.parse(String(raw2));
145099
- if (!this.wsAuthed.has(ws2)) {
145100
- if (msg.type === "auth" && typeof msg.token === "string") {
145101
- const t = this.wsAuthPending.get(ws2);
145102
- if (t) clearTimeout(t);
145103
- this.wsAuthPending.delete(ws2);
145104
- if (validateApiKey(msg.token, this.apiKey)) {
145105
- this.completeWsAuth(ws2);
145106
- } else {
145107
- try {
145108
- ws2.close(WS_CLOSE_UNAUTHORIZED, "unauthorized");
145109
- } catch {
145110
- }
145111
- }
145112
- }
145113
- return;
145114
- }
145115
145117
  if (msg.type === "register" && typeof msg.clientId === "string") {
145116
145118
  const oldClientId = this.wsToClientId.get(ws2);
145117
145119
  if (oldClientId) this.clientIdToWs.delete(oldClientId);
@@ -145122,7 +145124,15 @@ var StreamerServer = class {
145122
145124
  this.addSessionSubscriber(msg.sessionId, ws2);
145123
145125
  if (this.ptyManager.hasSession(msg.sessionId)) {
145124
145126
  const lines = await this.ptyManager.getOutputLines(msg.sessionId, 200);
145125
- ws2.send(JSON.stringify({ type: "terminal_replay", sessionId: msg.sessionId, lines }));
145127
+ const userMessages = this.ptyManager.getInputHistory(msg.sessionId);
145128
+ ws2.send(
145129
+ JSON.stringify({
145130
+ type: "terminal_replay",
145131
+ sessionId: msg.sessionId,
145132
+ lines,
145133
+ userMessages
145134
+ })
145135
+ );
145126
145136
  }
145127
145137
  const pendingGate = this.pendingPermission.get(msg.sessionId);
145128
145138
  if (pendingGate) {
@@ -145158,20 +145168,12 @@ var StreamerServer = class {
145158
145168
  }
145159
145169
  }
145160
145170
  if (msg.type === "hold_session" && typeof msg.sessionId === "string") {
145161
- if (this.sessionSubscribers.get(msg.sessionId)?.has(ws2)) {
145162
- this.startGraceTimer(msg.sessionId, 0);
145163
- }
145171
+ this.startGraceTimer(msg.sessionId, 0);
145164
145172
  }
145165
145173
  } catch {
145166
145174
  }
145167
145175
  },
145168
145176
  handleWsClose: (ws2) => {
145169
- const pendingTimer = this.wsAuthPending.get(ws2);
145170
- if (pendingTimer) {
145171
- clearTimeout(pendingTimer);
145172
- this.wsAuthPending.delete(ws2);
145173
- }
145174
- this.wsAuthed.delete(ws2);
145175
145177
  const clientId = this.wsToClientId.get(ws2);
145176
145178
  if (clientId) {
145177
145179
  this.clientIdToWs.delete(clientId);
@@ -145241,20 +145243,6 @@ var StreamerServer = class {
145241
145243
  this.wsHub.broadcast(payload);
145242
145244
  }
145243
145245
  }
145244
- // M1: finalize a WebSocket auth (via ?key= at upgrade or a first-message
145245
- // handshake) — register it with the hub and send the initial snapshot. Only
145246
- // authed sockets reach this, so no unauthenticated client ever receives a
145247
- // broadcast.
145248
- // Plan: https://github.com/RonenMars/threadbase-streamer/blob/a251353bfa417bd48ce3f15086bc336a2c622629/docs/plans/2026-06-24-security-hardening.md#L40
145249
- completeWsAuth(ws2) {
145250
- this.wsAuthed.add(ws2);
145251
- this.wsHub.addClient(ws2);
145252
- const sessions = this.sessionStore.list(this.ptyAttachedIds());
145253
- ws2.send(JSON.stringify({ type: "session_list", sessions }));
145254
- if (this.cacheReady) {
145255
- ws2.send(JSON.stringify({ type: "cache_ready" }));
145256
- }
145257
- }
145258
145246
  addSessionSubscriber(sessionId, ws2) {
145259
145247
  let subs = this.sessionSubscribers.get(sessionId);
145260
145248
  if (!subs) {
@@ -145541,9 +145529,6 @@ var StreamerServer = class {
145541
145529
  this.ptyManager.dispose();
145542
145530
  this.fileWatcher.dispose();
145543
145531
  this.wsHub.dispose();
145544
- for (const timer of this.wsAuthPending.values()) clearTimeout(timer);
145545
- this.wsAuthPending.clear();
145546
- this.wsAuthed.clear();
145547
145532
  this.pairTokens.dispose();
145548
145533
  if (this.dbPool) {
145549
145534
  await this.dbPool.end();
@@ -146500,7 +146485,10 @@ var StreamerServer = class {
146500
146485
  const projectPath = jsonlCwd ?? conv?.projectPath;
146501
146486
  if (!projectPath) {
146502
146487
  if (!conv && !jsonlPath) {
146503
- json2(res, 404, { error: "Conversation not found" });
146488
+ json2(res, 404, {
146489
+ error: "Conversation history file is missing; it can no longer be resumed",
146490
+ code: "history_file_missing"
146491
+ });
146504
146492
  return;
146505
146493
  }
146506
146494
  json2(res, 400, { error: "Could not determine project path" });
@@ -146670,10 +146658,6 @@ var StreamerServer = class {
146670
146658
  return;
146671
146659
  }
146672
146660
  this.pendingPermission.set(sessionId, gate);
146673
- const safeOptions = gate.options.map((o) => {
146674
- const answerKeys = sanitizeAnswerKeys(o.answerKeys);
146675
- return answerKeys === void 0 ? { index: o.index, label: o.label } : { ...o, answerKeys };
146676
- });
146677
146661
  const subscriberCount = this.sessionSubscribers.get(sessionId)?.size ?? 0;
146678
146662
  this.log.info(
146679
146663
  `[ws.broadcast_permission] ${sessionId.slice(0, 8)} subscribers=${subscriberCount}`,
@@ -146684,7 +146668,7 @@ var StreamerServer = class {
146684
146668
  sessionId,
146685
146669
  ...gate.prompt ? { prompt: gate.prompt } : {},
146686
146670
  ...gate.detail ? { detail: gate.detail } : {},
146687
- options: safeOptions,
146671
+ options: gate.options,
146688
146672
  ...gate.cursor !== void 0 ? { cursor: gate.cursor } : {}
146689
146673
  });
146690
146674
  }
@@ -147782,8 +147766,11 @@ var import_node_path13 = require("path");
147782
147766
  function stampVersionTxt(destDir, version2) {
147783
147767
  const distDir = (0, import_node_path13.join)(destDir, "dist");
147784
147768
  (0, import_node_fs10.mkdirSync)(distDir, { recursive: true });
147785
- (0, import_node_fs10.writeFileSync)((0, import_node_path13.join)(distDir, "version.txt"), `${version2}+update
147786
- `);
147769
+ (0, import_node_fs10.writeFileSync)((0, import_node_path13.join)(distDir, "version.txt"), versionStamp(version2));
147770
+ }
147771
+ function versionStamp(version2) {
147772
+ return `${version2}+update
147773
+ `;
147787
147774
  }
147788
147775
 
147789
147776
  // src/updater/swap.ts
@@ -147805,6 +147792,7 @@ function swapCurrent(version2) {
147805
147792
  }
147806
147793
  (0, import_node_fs11.renameSync)(tmp, CURRENT_SYMLINK);
147807
147794
  (0, import_node_fs11.copyFileSync)((0, import_node_path14.join)(CURRENT_SYMLINK, "dist", "cli.cjs"), (0, import_node_path14.join)(THREADBASE_ROOT, "cli.js"));
147795
+ publishVersionTxt(target, version2);
147808
147796
  return;
147809
147797
  }
147810
147798
  const tmpLink = `${CURRENT_SYMLINK}.new`;
@@ -147818,6 +147806,15 @@ function swapCurrent(version2) {
147818
147806
  if ((0, import_node_fs11.existsSync)(tmpCliJs) || lstatSafeIsSymlink(tmpCliJs)) (0, import_node_fs11.unlinkSync)(tmpCliJs);
147819
147807
  (0, import_node_fs11.symlinkSync)((0, import_node_path14.join)(target, "dist", "cli.cjs"), tmpCliJs);
147820
147808
  (0, import_node_fs11.renameSync)(tmpCliJs, cliJs);
147809
+ publishVersionTxt(target, version2);
147810
+ }
147811
+ function publishVersionTxt(target, version2) {
147812
+ const dest = (0, import_node_path14.join)(THREADBASE_ROOT, "version.txt");
147813
+ try {
147814
+ (0, import_node_fs11.copyFileSync)((0, import_node_path14.join)(target, "dist", "version.txt"), dest);
147815
+ } catch {
147816
+ (0, import_node_fs11.writeFileSync)(dest, versionStamp(version2));
147817
+ }
147821
147818
  }
147822
147819
  function lstatSafeIsSymlink(path2) {
147823
147820
  try {