@threadbase-sh/streamer 1.47.0 → 1.47.2
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/README.md +42 -2
- package/dist/cli.cjs +76 -53
- package/dist/cli.cjs.map +1 -1
- package/dist/ensure-demo-project-dirs.cjs +1 -1
- package/dist/ensure-demo-project-dirs.cjs.map +1 -1
- package/dist/index.cjs +17 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +17 -5
- package/dist/index.js.map +1 -1
- package/dist/launchd-entry.cjs.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -43,7 +43,9 @@ npm and Homebrew installs can auto-update: [docs/guides/auto-update.md](docs/gui
|
|
|
43
43
|
|
|
44
44
|
## Remote Access
|
|
45
45
|
|
|
46
|
-
The server
|
|
46
|
+
The server listens on port 8766 on all interfaces, so devices on the same LAN can already reach it. To let the mobile app reach it from *outside* your LAN, expose it via a tunnel — the fastest is a Cloudflare quick-tunnel (no account needed):
|
|
47
|
+
|
|
48
|
+
> **Before you do this, read [Security](#security--read-this-before-exposing-the-server).** A tunnel makes the server reachable from the public internet, and the API key is the only thing between a stranger and a shell on this machine.
|
|
47
49
|
|
|
48
50
|
```bash
|
|
49
51
|
bash scripts/remote-access/cloudflare.sh # macOS/Linux/WSL/Git Bash
|
|
@@ -64,9 +66,47 @@ npm run migrate # apply SQLite migrations
|
|
|
64
66
|
npm run db:validate # check for missing/duplicate/orphaned project_id data
|
|
65
67
|
```
|
|
66
68
|
|
|
69
|
+
## Security — read this before exposing the server
|
|
70
|
+
|
|
71
|
+
**The API key is equivalent to a shell on this machine.** The streamer exists to start Claude Code sessions and type into them, so anyone holding the key can run commands as your user, in any project the server can reach. Treat it like an SSH private key, not like a web session token.
|
|
72
|
+
|
|
73
|
+
The key lives at `~/.threadbase/server.yaml` as plaintext (`chmod 0600`), which is the same posture as `~/.aws/credentials` or `~/.npmrc`. Rotate it with `POST /api/auth/rotate` if it leaks; re-pair your devices afterwards.
|
|
74
|
+
|
|
75
|
+
**The server listens on all network interfaces**, not just loopback — `httpServer.listen(port)` is called without a host, which is Node's listen-everywhere default, and there is currently no flag to narrow it. So every device on your LAN can already reach port 8766, and the API key is the only thing stopping them. On a home network that is usually fine; on café Wi-Fi, a co-working space, or a corporate VLAN it is not.
|
|
76
|
+
|
|
77
|
+
**A tunnel extends that to the entire internet.** From that moment the key is the only thing between a stranger and your filesystem.
|
|
78
|
+
|
|
79
|
+
If you need it strictly local today, bind it yourself at the firewall or run it inside a network namespace — the server will not do it for you. Tracked as [#517](https://github.com/RonenMars/threadbase-streamer/issues/517).
|
|
80
|
+
|
|
81
|
+
### There is no spend limit
|
|
82
|
+
|
|
83
|
+
Nothing in this server caps what a session costs. There is no per-session, per-day, or total ceiling, and no way to configure one — a runaway agent bills your provider account until you stop it. (`--max-budget-usd` is not a workaround: it only applies to `claude --print`, which this server never uses, so it was silently inert and has been removed.)
|
|
84
|
+
|
|
85
|
+
The practical bound is that a session waits for input between turns. Auto-resume and bypass modes both weaken that bound — see below.
|
|
86
|
+
|
|
87
|
+
### Permission modes decide how much a session can do unattended
|
|
88
|
+
|
|
89
|
+
Sessions spawn with `--permission-mode`, default `acceptEdits`: file edits are auto-approved, shell commands still prompt. The first-run prompt offers only `acceptEdits` and `manual`, so the unattended modes are never something you pick by accident.
|
|
90
|
+
|
|
91
|
+
`bypassPermissions` and `dontAsk` are available through `--default-permission-mode` and `PUT /api/config/claude-flags`, and they remove the confirmation step entirely. **A session in either mode is unattended arbitrary code execution**: a leaked key no longer stops at a human-in-the-loop prompt. `--add-dir` compounds this by widening filesystem scope beyond the project.
|
|
92
|
+
|
|
93
|
+
If you enable one, you are accepting that the auth boundary is the *only* remaining control. Two guardrails exist and neither is a substitute for that judgement: `PUT /api/config/claude-flags` is refused with 403 while `--local-no-auth` is active, and every flag change is logged with its old and new value.
|
|
94
|
+
|
|
95
|
+
### Auto-resume starts agents without you
|
|
96
|
+
|
|
97
|
+
`auto_resume_on_boot` in `server.yaml` re-starts sessions that a previous run was interrupted mid-flight. It defaults to `false` and is never enabled implicitly — it is the one setting that lets the streamer start an agent nobody asked for in that moment, and combined with a bypass permission mode that is unattended code execution after a reboot.
|
|
98
|
+
|
|
99
|
+
Leave it off unless you have a reason, and do not combine it with a bypass mode unless you fully trust everything that can reach the API.
|
|
100
|
+
|
|
101
|
+
### Reporting a vulnerability
|
|
102
|
+
|
|
103
|
+
See [SECURITY.md](https://github.com/RonenMars/threadbase/blob/main/SECURITY.md) in the umbrella repo.
|
|
104
|
+
|
|
67
105
|
## Persistence
|
|
68
106
|
|
|
69
|
-
Conversation metadata is cached in SQLite at `~/.threadbase/cache/cache.db`, created and migrated automatically — no setup needed.
|
|
107
|
+
Conversation metadata is cached in SQLite at `~/.threadbase/cache/cache.db`, created and migrated automatically — no setup needed.
|
|
108
|
+
|
|
109
|
+
Live PTYs do not survive a restart, but sessions are not lost: the managed-session registry (`~/.threadbase/runtime.db`) brings interrupted sessions back in `GET /api/sessions` as resumable stubs, and any conversation can be resumed with `POST /api/sessions/resume`. History itself is always on disk, written by the provider.
|
|
70
110
|
|
|
71
111
|
PostgreSQL is optional and only stores upload records today. Enable it by setting `THREADBASE_DATABASE_URL`; migrations run automatically.
|
|
72
112
|
|
package/dist/cli.cjs
CHANGED
|
@@ -3604,7 +3604,7 @@ var require_thread_stream = __commonJS({
|
|
|
3604
3604
|
var { version: version2 } = require_package();
|
|
3605
3605
|
var { EventEmitter: EventEmitter5 } = require("events");
|
|
3606
3606
|
var { Worker } = require("worker_threads");
|
|
3607
|
-
var { join:
|
|
3607
|
+
var { join: join29 } = require("path");
|
|
3608
3608
|
var { pathToFileURL } = require("url");
|
|
3609
3609
|
var { wait } = require_wait();
|
|
3610
3610
|
var {
|
|
@@ -3655,7 +3655,7 @@ var require_thread_stream = __commonJS({
|
|
|
3655
3655
|
function createWorker(stream, opts) {
|
|
3656
3656
|
const { filename, workerData } = opts;
|
|
3657
3657
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
3658
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
3658
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join29(__dirname, "lib", "worker.js");
|
|
3659
3659
|
const worker = new Worker(toExecute, {
|
|
3660
3660
|
...opts.workerOpts,
|
|
3661
3661
|
name: opts.workerOpts?.name || "thread-stream",
|
|
@@ -4123,7 +4123,7 @@ var require_transport = __commonJS({
|
|
|
4123
4123
|
var { createRequire } = require("module");
|
|
4124
4124
|
var { existsSync: existsSync16 } = require("fs");
|
|
4125
4125
|
var getCallers = require_caller();
|
|
4126
|
-
var { join:
|
|
4126
|
+
var { join: join29, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
|
|
4127
4127
|
var { fileURLToPath: fileURLToPath3 } = require("url");
|
|
4128
4128
|
var sleep = require_atomic_sleep();
|
|
4129
4129
|
var onExit = require_on_exit_leak_free();
|
|
@@ -4276,7 +4276,7 @@ var require_transport = __commonJS({
|
|
|
4276
4276
|
throw new Error("only one of target or targets can be specified");
|
|
4277
4277
|
}
|
|
4278
4278
|
if (targets) {
|
|
4279
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
4279
|
+
target = bundlerOverrides["pino-worker"] || join29(__dirname, "worker.js");
|
|
4280
4280
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
4281
4281
|
return {
|
|
4282
4282
|
...dest,
|
|
@@ -4294,7 +4294,7 @@ var require_transport = __commonJS({
|
|
|
4294
4294
|
});
|
|
4295
4295
|
});
|
|
4296
4296
|
} else if (pipeline2) {
|
|
4297
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
4297
|
+
target = bundlerOverrides["pino-worker"] || join29(__dirname, "worker.js");
|
|
4298
4298
|
options.pipelines = [pipeline2.map((dest) => {
|
|
4299
4299
|
return {
|
|
4300
4300
|
...dest,
|
|
@@ -4317,7 +4317,7 @@ var require_transport = __commonJS({
|
|
|
4317
4317
|
return origin;
|
|
4318
4318
|
}
|
|
4319
4319
|
if (origin === "pino/file") {
|
|
4320
|
-
return
|
|
4320
|
+
return join29(__dirname, "..", "file.js");
|
|
4321
4321
|
}
|
|
4322
4322
|
let fixTarget2;
|
|
4323
4323
|
for (const filePath of callers) {
|
|
@@ -5298,7 +5298,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
5298
5298
|
return circularValue;
|
|
5299
5299
|
}
|
|
5300
5300
|
let res = "";
|
|
5301
|
-
let
|
|
5301
|
+
let join29 = ",";
|
|
5302
5302
|
const originalIndentation = indentation;
|
|
5303
5303
|
if (Array.isArray(value)) {
|
|
5304
5304
|
if (value.length === 0) {
|
|
@@ -5312,7 +5312,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
5312
5312
|
indentation += spacer;
|
|
5313
5313
|
res += `
|
|
5314
5314
|
${indentation}`;
|
|
5315
|
-
|
|
5315
|
+
join29 = `,
|
|
5316
5316
|
${indentation}`;
|
|
5317
5317
|
}
|
|
5318
5318
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -5320,13 +5320,13 @@ ${indentation}`;
|
|
|
5320
5320
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
5321
5321
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
5322
5322
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
5323
|
-
res +=
|
|
5323
|
+
res += join29;
|
|
5324
5324
|
}
|
|
5325
5325
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
5326
5326
|
res += tmp !== void 0 ? tmp : "null";
|
|
5327
5327
|
if (value.length - 1 > maximumBreadth) {
|
|
5328
5328
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
5329
|
-
res += `${
|
|
5329
|
+
res += `${join29}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
5330
5330
|
}
|
|
5331
5331
|
if (spacer !== "") {
|
|
5332
5332
|
res += `
|
|
@@ -5347,7 +5347,7 @@ ${originalIndentation}`;
|
|
|
5347
5347
|
let separator = "";
|
|
5348
5348
|
if (spacer !== "") {
|
|
5349
5349
|
indentation += spacer;
|
|
5350
|
-
|
|
5350
|
+
join29 = `,
|
|
5351
5351
|
${indentation}`;
|
|
5352
5352
|
whitespace = " ";
|
|
5353
5353
|
}
|
|
@@ -5361,13 +5361,13 @@ ${indentation}`;
|
|
|
5361
5361
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
5362
5362
|
if (tmp !== void 0) {
|
|
5363
5363
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
5364
|
-
separator =
|
|
5364
|
+
separator = join29;
|
|
5365
5365
|
}
|
|
5366
5366
|
}
|
|
5367
5367
|
if (keyLength > maximumBreadth) {
|
|
5368
5368
|
const removedKeys = keyLength - maximumBreadth;
|
|
5369
5369
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
5370
|
-
separator =
|
|
5370
|
+
separator = join29;
|
|
5371
5371
|
}
|
|
5372
5372
|
if (spacer !== "" && separator.length > 1) {
|
|
5373
5373
|
res = `
|
|
@@ -5408,7 +5408,7 @@ ${originalIndentation}`;
|
|
|
5408
5408
|
}
|
|
5409
5409
|
const originalIndentation = indentation;
|
|
5410
5410
|
let res = "";
|
|
5411
|
-
let
|
|
5411
|
+
let join29 = ",";
|
|
5412
5412
|
if (Array.isArray(value)) {
|
|
5413
5413
|
if (value.length === 0) {
|
|
5414
5414
|
return "[]";
|
|
@@ -5421,7 +5421,7 @@ ${originalIndentation}`;
|
|
|
5421
5421
|
indentation += spacer;
|
|
5422
5422
|
res += `
|
|
5423
5423
|
${indentation}`;
|
|
5424
|
-
|
|
5424
|
+
join29 = `,
|
|
5425
5425
|
${indentation}`;
|
|
5426
5426
|
}
|
|
5427
5427
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -5429,13 +5429,13 @@ ${indentation}`;
|
|
|
5429
5429
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
5430
5430
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
5431
5431
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
5432
|
-
res +=
|
|
5432
|
+
res += join29;
|
|
5433
5433
|
}
|
|
5434
5434
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
5435
5435
|
res += tmp !== void 0 ? tmp : "null";
|
|
5436
5436
|
if (value.length - 1 > maximumBreadth) {
|
|
5437
5437
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
5438
|
-
res += `${
|
|
5438
|
+
res += `${join29}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
5439
5439
|
}
|
|
5440
5440
|
if (spacer !== "") {
|
|
5441
5441
|
res += `
|
|
@@ -5448,7 +5448,7 @@ ${originalIndentation}`;
|
|
|
5448
5448
|
let whitespace = "";
|
|
5449
5449
|
if (spacer !== "") {
|
|
5450
5450
|
indentation += spacer;
|
|
5451
|
-
|
|
5451
|
+
join29 = `,
|
|
5452
5452
|
${indentation}`;
|
|
5453
5453
|
whitespace = " ";
|
|
5454
5454
|
}
|
|
@@ -5457,7 +5457,7 @@ ${indentation}`;
|
|
|
5457
5457
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
5458
5458
|
if (tmp !== void 0) {
|
|
5459
5459
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
5460
|
-
separator =
|
|
5460
|
+
separator = join29;
|
|
5461
5461
|
}
|
|
5462
5462
|
}
|
|
5463
5463
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -5515,20 +5515,20 @@ ${originalIndentation}`;
|
|
|
5515
5515
|
indentation += spacer;
|
|
5516
5516
|
let res2 = `
|
|
5517
5517
|
${indentation}`;
|
|
5518
|
-
const
|
|
5518
|
+
const join30 = `,
|
|
5519
5519
|
${indentation}`;
|
|
5520
5520
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
5521
5521
|
let i = 0;
|
|
5522
5522
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
5523
5523
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
5524
5524
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
5525
|
-
res2 +=
|
|
5525
|
+
res2 += join30;
|
|
5526
5526
|
}
|
|
5527
5527
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
5528
5528
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
5529
5529
|
if (value.length - 1 > maximumBreadth) {
|
|
5530
5530
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
5531
|
-
res2 += `${
|
|
5531
|
+
res2 += `${join30}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
5532
5532
|
}
|
|
5533
5533
|
res2 += `
|
|
5534
5534
|
${originalIndentation}`;
|
|
@@ -5544,16 +5544,16 @@ ${originalIndentation}`;
|
|
|
5544
5544
|
return '"[Object]"';
|
|
5545
5545
|
}
|
|
5546
5546
|
indentation += spacer;
|
|
5547
|
-
const
|
|
5547
|
+
const join29 = `,
|
|
5548
5548
|
${indentation}`;
|
|
5549
5549
|
let res = "";
|
|
5550
5550
|
let separator = "";
|
|
5551
5551
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
5552
5552
|
if (isTypedArrayWithEntries(value)) {
|
|
5553
|
-
res += stringifyTypedArray(value,
|
|
5553
|
+
res += stringifyTypedArray(value, join29, maximumBreadth);
|
|
5554
5554
|
keys = keys.slice(value.length);
|
|
5555
5555
|
maximumPropertiesToStringify -= value.length;
|
|
5556
|
-
separator =
|
|
5556
|
+
separator = join29;
|
|
5557
5557
|
}
|
|
5558
5558
|
if (deterministic) {
|
|
5559
5559
|
keys = sort(keys, comparator);
|
|
@@ -5564,13 +5564,13 @@ ${indentation}`;
|
|
|
5564
5564
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
5565
5565
|
if (tmp !== void 0) {
|
|
5566
5566
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
5567
|
-
separator =
|
|
5567
|
+
separator = join29;
|
|
5568
5568
|
}
|
|
5569
5569
|
}
|
|
5570
5570
|
if (keyLength > maximumBreadth) {
|
|
5571
5571
|
const removedKeys = keyLength - maximumBreadth;
|
|
5572
5572
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
5573
|
-
separator =
|
|
5573
|
+
separator = join29;
|
|
5574
5574
|
}
|
|
5575
5575
|
if (separator !== "") {
|
|
5576
5576
|
res = `
|
|
@@ -28983,6 +28983,10 @@ function markerPath() {
|
|
|
28983
28983
|
function prefsPath() {
|
|
28984
28984
|
return (0, import_node_path3.join)(installDir(), "dev-prefs.json");
|
|
28985
28985
|
}
|
|
28986
|
+
function logPaths() {
|
|
28987
|
+
const dir = (0, import_node_path3.join)(installDir(), "logs");
|
|
28988
|
+
return { stdout: (0, import_node_path3.join)(dir, "stdout.log"), stderr: (0, import_node_path3.join)(dir, "stderr.log") };
|
|
28989
|
+
}
|
|
28986
28990
|
var import_node_os2, import_node_path3, LAUNCHD_LABEL, TASK_NAME;
|
|
28987
28991
|
var init_constants = __esm({
|
|
28988
28992
|
"src/lifecycle/constants.ts"() {
|
|
@@ -126967,8 +126971,7 @@ function darwinPlistPath() {
|
|
|
126967
126971
|
return `${process.env.HOME}/Library/LaunchAgents/${resolveLoadedLabel()}.plist`;
|
|
126968
126972
|
}
|
|
126969
126973
|
function getLogPaths() {
|
|
126970
|
-
|
|
126971
|
-
return { stdout: (0, import_node_path25.join)(dir, "stdout.log"), stderr: (0, import_node_path25.join)(dir, "stderr.log") };
|
|
126974
|
+
return logPaths();
|
|
126972
126975
|
}
|
|
126973
126976
|
function getAgentPid() {
|
|
126974
126977
|
try {
|
|
@@ -126988,12 +126991,11 @@ function getAgentPid() {
|
|
|
126988
126991
|
return null;
|
|
126989
126992
|
}
|
|
126990
126993
|
}
|
|
126991
|
-
var import_node_child_process6,
|
|
126994
|
+
var import_node_child_process6, BREW_LAUNCHD_LABEL2;
|
|
126992
126995
|
var init_launchd = __esm({
|
|
126993
126996
|
"src/lifecycle/launchd.ts"() {
|
|
126994
126997
|
"use strict";
|
|
126995
126998
|
import_node_child_process6 = require("child_process");
|
|
126996
|
-
import_node_path25 = require("path");
|
|
126997
126999
|
init_constants();
|
|
126998
127000
|
BREW_LAUNCHD_LABEL2 = "homebrew.mxcl.tb-streamer";
|
|
126999
127001
|
}
|
|
@@ -127032,7 +127034,7 @@ function readMarker() {
|
|
|
127032
127034
|
}
|
|
127033
127035
|
function writeMarker(marker) {
|
|
127034
127036
|
MarkerSchema.parse(marker);
|
|
127035
|
-
(0, import_node_fs22.mkdirSync)((0,
|
|
127037
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path25.dirname)(markerPath()), { recursive: true });
|
|
127036
127038
|
const tmp = `${markerPath()}.tmp`;
|
|
127037
127039
|
(0, import_node_fs22.writeFileSync)(tmp, `${JSON.stringify(marker, null, 2)}
|
|
127038
127040
|
`, { mode: 384 });
|
|
@@ -127041,12 +127043,12 @@ function writeMarker(marker) {
|
|
|
127041
127043
|
function clearMarker() {
|
|
127042
127044
|
if ((0, import_node_fs22.existsSync)(markerPath())) (0, import_node_fs22.rmSync)(markerPath());
|
|
127043
127045
|
}
|
|
127044
|
-
var import_node_fs22,
|
|
127046
|
+
var import_node_fs22, import_node_path25, log8;
|
|
127045
127047
|
var init_marker = __esm({
|
|
127046
127048
|
"src/lifecycle/marker.ts"() {
|
|
127047
127049
|
"use strict";
|
|
127048
127050
|
import_node_fs22 = require("fs");
|
|
127049
|
-
|
|
127051
|
+
import_node_path25 = require("path");
|
|
127050
127052
|
init_logger();
|
|
127051
127053
|
init_constants();
|
|
127052
127054
|
init_marker_schema();
|
|
@@ -127096,9 +127098,7 @@ function kickstartAgent2() {
|
|
|
127096
127098
|
ps2(`Start-ScheduledTask -TaskName '${TASK_NAME}' -ErrorAction Stop`);
|
|
127097
127099
|
}
|
|
127098
127100
|
function getLogPaths2() {
|
|
127099
|
-
|
|
127100
|
-
"prod logs is not yet wired on Windows \u2014 launch.cmd does not currently redirect stdout/stderr to a file. Track the fix in docs/troubleshooting.md."
|
|
127101
|
-
);
|
|
127101
|
+
return logPaths();
|
|
127102
127102
|
}
|
|
127103
127103
|
function getAgentPid2() {
|
|
127104
127104
|
try {
|
|
@@ -127188,7 +127188,7 @@ __export(log_cap_exports, {
|
|
|
127188
127188
|
LOG_CAP_BYTES: () => LOG_CAP_BYTES,
|
|
127189
127189
|
truncateOversizedLogs: () => truncateOversizedLogs
|
|
127190
127190
|
});
|
|
127191
|
-
function truncateOversizedLogs(maxBytes = LOG_CAP_BYTES, paths = Object.values(
|
|
127191
|
+
function truncateOversizedLogs(maxBytes = LOG_CAP_BYTES, paths = Object.values(logPaths())) {
|
|
127192
127192
|
const truncated = [];
|
|
127193
127193
|
for (const file2 of paths) {
|
|
127194
127194
|
try {
|
|
@@ -127205,7 +127205,7 @@ var init_log_cap = __esm({
|
|
|
127205
127205
|
"src/lifecycle/log-cap.ts"() {
|
|
127206
127206
|
"use strict";
|
|
127207
127207
|
import_node_fs24 = require("fs");
|
|
127208
|
-
|
|
127208
|
+
init_constants();
|
|
127209
127209
|
LOG_CAP_BYTES = 32 * 1024 * 1024;
|
|
127210
127210
|
}
|
|
127211
127211
|
});
|
|
@@ -127300,7 +127300,7 @@ function readPrefs() {
|
|
|
127300
127300
|
}
|
|
127301
127301
|
function savePrefs(p2) {
|
|
127302
127302
|
const path2 = prefsPath();
|
|
127303
|
-
(0, import_node_fs25.mkdirSync)((0,
|
|
127303
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path26.dirname)(path2), { recursive: true });
|
|
127304
127304
|
const tmp = `${path2}.tmp`;
|
|
127305
127305
|
(0, import_node_fs25.writeFileSync)(tmp, `${JSON.stringify(p2, null, 2)}
|
|
127306
127306
|
`, { mode: 384 });
|
|
@@ -127327,12 +127327,12 @@ function forgetAll() {
|
|
|
127327
127327
|
const path2 = prefsPath();
|
|
127328
127328
|
if ((0, import_node_fs25.existsSync)(path2)) (0, import_node_fs25.rmSync)(path2);
|
|
127329
127329
|
}
|
|
127330
|
-
var import_node_fs25,
|
|
127330
|
+
var import_node_fs25, import_node_path26, log10, PrefsSchema;
|
|
127331
127331
|
var init_prefs = __esm({
|
|
127332
127332
|
"src/lifecycle/prefs.ts"() {
|
|
127333
127333
|
"use strict";
|
|
127334
127334
|
import_node_fs25 = require("fs");
|
|
127335
|
-
|
|
127335
|
+
import_node_path26 = require("path");
|
|
127336
127336
|
init_zod();
|
|
127337
127337
|
init_logger();
|
|
127338
127338
|
init_constants();
|
|
@@ -142973,6 +142973,7 @@ init_providers();
|
|
|
142973
142973
|
|
|
142974
142974
|
// src/services/providers/providerHealth.ts
|
|
142975
142975
|
var import_child_process2 = require("child_process");
|
|
142976
|
+
init_platform();
|
|
142976
142977
|
init_providers();
|
|
142977
142978
|
|
|
142978
142979
|
// src/services/providers/capabilities.ts
|
|
@@ -143013,11 +143014,18 @@ function parseVersionOutput(output) {
|
|
|
143013
143014
|
return match2 ? match2[0] : null;
|
|
143014
143015
|
}
|
|
143015
143016
|
function runVersion(exe) {
|
|
143017
|
+
const viaShell = isWindows2 && /\.(?:cmd|bat)$/i.test(exe);
|
|
143018
|
+
const file2 = viaShell ? `"${exe}"` : exe;
|
|
143016
143019
|
return new Promise((resolve4) => {
|
|
143017
|
-
(0, import_child_process2.execFile)(
|
|
143018
|
-
|
|
143019
|
-
|
|
143020
|
-
|
|
143020
|
+
(0, import_child_process2.execFile)(
|
|
143021
|
+
file2,
|
|
143022
|
+
["--version"],
|
|
143023
|
+
{ timeout: VERSION_TIMEOUT_MS, shell: viaShell, windowsHide: true },
|
|
143024
|
+
(err, stdout2, stderr) => {
|
|
143025
|
+
if (err && !stdout2 && !stderr) return resolve4(null);
|
|
143026
|
+
resolve4(parseVersionOutput(`${stdout2}${stderr}`));
|
|
143027
|
+
}
|
|
143028
|
+
);
|
|
143021
143029
|
});
|
|
143022
143030
|
}
|
|
143023
143031
|
function compareToVerified(version2, verified) {
|
|
@@ -143080,7 +143088,12 @@ async function providerHealth(name, resolveExe, detect = runVersion) {
|
|
|
143080
143088
|
]
|
|
143081
143089
|
};
|
|
143082
143090
|
}
|
|
143083
|
-
|
|
143091
|
+
let version2 = null;
|
|
143092
|
+
try {
|
|
143093
|
+
version2 = await detect(exe);
|
|
143094
|
+
} catch {
|
|
143095
|
+
version2 = null;
|
|
143096
|
+
}
|
|
143084
143097
|
const warning = compareToVerified(version2, verifiedAgainst);
|
|
143085
143098
|
return {
|
|
143086
143099
|
name,
|
|
@@ -156914,6 +156927,9 @@ async function runProdDoctor(opts, deps = {}) {
|
|
|
156914
156927
|
}
|
|
156915
156928
|
return { findings, repairs, ...ptyHost ? { ptyHost } : {} };
|
|
156916
156929
|
}
|
|
156930
|
+
function toPowerShellLiteral(value) {
|
|
156931
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
156932
|
+
}
|
|
156917
156933
|
var defaultSpawnTail = ({ files, lines, follow }) => {
|
|
156918
156934
|
const existing = files.filter((f2) => (0, import_node_fs23.existsSync)(f2));
|
|
156919
156935
|
if (existing.length === 0) {
|
|
@@ -156922,11 +156938,18 @@ var defaultSpawnTail = ({ files, lines, follow }) => {
|
|
|
156922
156938
|
message: `no log files found at: ${files.join(", ")}. The streamer may not have started yet, or the deploy uses a different log layout.`
|
|
156923
156939
|
});
|
|
156924
156940
|
}
|
|
156925
|
-
const
|
|
156926
|
-
|
|
156927
|
-
|
|
156941
|
+
const isWindows3 = process.platform === "win32";
|
|
156942
|
+
const args = isWindows3 ? [
|
|
156943
|
+
"-NoLogo",
|
|
156944
|
+
"-NoProfile",
|
|
156945
|
+
"-NonInteractive",
|
|
156946
|
+
"-Command",
|
|
156947
|
+
`Get-Content -LiteralPath @(${existing.map(toPowerShellLiteral).join(", ")}) -Tail ${lines}${follow ? " -Wait" : ""}`
|
|
156948
|
+
] : ["-n", String(lines), ...follow ? ["-F"] : [], ...existing];
|
|
156928
156949
|
return new Promise((resolve4) => {
|
|
156929
|
-
const child = (0, import_node_child_process8.spawn)("
|
|
156950
|
+
const child = (0, import_node_child_process8.spawn)(isWindows3 ? "powershell.exe" : "tail", args, {
|
|
156951
|
+
stdio: ["ignore", "inherit", "inherit"]
|
|
156952
|
+
});
|
|
156930
156953
|
child.on("error", (err) => resolve4({ ok: false, message: `tail failed: ${err.message}` }));
|
|
156931
156954
|
child.on("exit", (code) => resolve4({ ok: code === 0 }));
|
|
156932
156955
|
});
|
|
@@ -157313,8 +157336,8 @@ program2.command("cache").description("Manage the local SQLite conversation cach
|
|
|
157313
157336
|
`${process.env.HOME}/.threadbase/cache`
|
|
157314
157337
|
).action((opts) => {
|
|
157315
157338
|
const { rmSync: rmSync6, existsSync: existsSync16 } = require("fs");
|
|
157316
|
-
const { join:
|
|
157317
|
-
const dbPath =
|
|
157339
|
+
const { join: join29 } = require("path");
|
|
157340
|
+
const dbPath = join29(opts.cacheDir, "cache.db");
|
|
157318
157341
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
157319
157342
|
const f2 = dbPath + suffix;
|
|
157320
157343
|
if (existsSync16(f2)) {
|