threadnote 1.1.3 → 1.1.4

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.
@@ -33081,7 +33081,6 @@ var StreamableHTTPClientTransport = class {
33081
33081
 
33082
33082
  // src/mcp_server.ts
33083
33083
  var import_promises5 = require("node:fs/promises");
33084
- var import_node_os3 = require("node:os");
33085
33084
  var import_node_path5 = require("node:path");
33086
33085
 
33087
33086
  // src/constants.ts
@@ -35724,6 +35723,7 @@ var jsYaml = {
35724
35723
  // src/utils.ts
35725
35724
  var import_node_child_process = require("node:child_process");
35726
35725
  var import_node_crypto = require("node:crypto");
35726
+ var import_node_fs = require("node:fs");
35727
35727
  var import_promises = require("node:fs/promises");
35728
35728
  var import_node_os = require("node:os");
35729
35729
  var import_node_path = require("node:path");
@@ -35778,18 +35778,57 @@ function redactText(content) {
35778
35778
  ).replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [REDACTED]").replace(/sk-[A-Za-z0-9_-]{16,}/g, "sk-[REDACTED]").replace(/gh[pousr]_[A-Za-z0-9_]{16,}/g, "gh_[REDACTED]");
35779
35779
  }
35780
35780
  async function requiredOpenVikingCli() {
35781
- const command2 = await findExecutable(["ov", "openviking"]);
35781
+ const command2 = await findOpenVikingCli();
35782
35782
  if (!command2) {
35783
- throw new Error("Neither ov nor openviking was found in PATH. Run threadnote install first.");
35783
+ throw new Error(
35784
+ "Neither ov nor openviking was found in PATH, uv tool bin dir, $UV_TOOL_BIN_DIR, or ~/.local/bin. Run threadnote install first."
35785
+ );
35784
35786
  }
35785
35787
  return command2;
35786
35788
  }
35787
35789
  async function openVikingCliForMode(dryRun) {
35788
35790
  if (dryRun) {
35789
- return await findExecutable(["ov", "openviking"]) ?? "ov";
35791
+ return await findOpenVikingCli() ?? "ov";
35790
35792
  }
35791
35793
  return requiredOpenVikingCli();
35792
35794
  }
35795
+ async function findOpenVikingCli() {
35796
+ const override = process.env.THREADNOTE_OV?.trim();
35797
+ if (override) {
35798
+ return override;
35799
+ }
35800
+ const onPath = await findExecutable(["ov", "openviking"]);
35801
+ if (onPath) {
35802
+ return onPath;
35803
+ }
35804
+ for (const candidateDir of await openVikingToolCandidateDirs()) {
35805
+ for (const command2 of ["ov", "openviking"]) {
35806
+ const candidate = (0, import_node_path.join)(candidateDir, command2);
35807
+ if (await isExecutable(candidate)) {
35808
+ return candidate;
35809
+ }
35810
+ }
35811
+ }
35812
+ return void 0;
35813
+ }
35814
+ async function openVikingToolCandidateDirs() {
35815
+ const dirs = [];
35816
+ const uv = await findExecutable(["uv"]);
35817
+ if (uv) {
35818
+ const result = await runCommand(uv, ["tool", "dir", "--bin"], { allowFailure: true });
35819
+ if (result.exitCode === 0) {
35820
+ const dir = result.stdout.trim();
35821
+ if (dir) {
35822
+ dirs.push(dir);
35823
+ }
35824
+ }
35825
+ }
35826
+ if (process.env.UV_TOOL_BIN_DIR) {
35827
+ dirs.push(process.env.UV_TOOL_BIN_DIR);
35828
+ }
35829
+ dirs.push((0, import_node_path.join)((0, import_node_os.homedir)(), ".local", "bin"));
35830
+ return Array.from(new Set(dirs));
35831
+ }
35793
35832
  async function requiredExecutable(command2) {
35794
35833
  const executable = await findExecutable([command2]);
35795
35834
  if (!executable) {
@@ -35946,6 +35985,14 @@ async function exists(path) {
35946
35985
  return false;
35947
35986
  }
35948
35987
  }
35988
+ async function isExecutable(path) {
35989
+ try {
35990
+ await (0, import_promises.access)(path, import_node_fs.constants.X_OK);
35991
+ return true;
35992
+ } catch (_err) {
35993
+ return false;
35994
+ }
35995
+ }
35949
35996
  async function isFile(path) {
35950
35997
  try {
35951
35998
  return (await (0, import_promises.stat)(path)).isFile();
@@ -40006,23 +40053,12 @@ function withIdentity2(config2, args) {
40006
40053
  return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
40007
40054
  }
40008
40055
  async function requiredOpenVikingCli2() {
40009
- const command2 = process.env.THREADNOTE_OV ?? await findExecutable(["ov", "openviking"]) ?? await firstExistingPath([(0, import_node_path5.join)((0, import_node_os3.homedir)(), ".local", "bin", "ov"), (0, import_node_path5.join)((0, import_node_os3.homedir)(), ".local", "bin", "openviking")]);
40056
+ const command2 = await findOpenVikingCli();
40010
40057
  if (!command2) {
40011
40058
  throw new Error("Neither ov nor openviking was found. Run threadnote install first.");
40012
40059
  }
40013
40060
  return command2;
40014
40061
  }
40015
- async function firstExistingPath(paths) {
40016
- for (const path of paths) {
40017
- try {
40018
- await (0, import_promises5.access)(path);
40019
- return await (0, import_promises5.realpath)(path);
40020
- } catch (_err) {
40021
- continue;
40022
- }
40023
- }
40024
- return void 0;
40025
- }
40026
40062
  main().catch((err) => {
40027
40063
  process.stderr.write(`${errorMessage(err)}
40028
40064
  `);
@@ -3484,7 +3484,7 @@ var PYTHON_SYSTEM_CERTS_PACKAGE = "pip-system-certs";
3484
3484
  var LAUNCHD_LABEL = "io.threadnote.openviking";
3485
3485
  var MAX_SECRET_MATCHES_TO_PRINT = 5;
3486
3486
  var START_HEALTH_POLL_INTERVAL_MS = 500;
3487
- var START_HEALTH_TIMEOUT_MS = 15e3;
3487
+ var START_HEALTH_TIMEOUT_MS = 6e4;
3488
3488
  var SHIM_MARKER = "Generated by threadnote";
3489
3489
  var USER_INSTRUCTIONS_START_MARKER = "<!-- BEGIN THREADNOTE USER INSTRUCTIONS -->";
3490
3490
  var USER_INSTRUCTIONS_END_MARKER = "<!-- END THREADNOTE USER INSTRUCTIONS -->";
@@ -3743,18 +3743,57 @@ function escapeRegExp(value) {
3743
3743
  return value.replace(/[\\^$+?.()|[\]{}]/g, "\\$&");
3744
3744
  }
3745
3745
  async function requiredOpenVikingCli() {
3746
- const command2 = await findExecutable(["ov", "openviking"]);
3746
+ const command2 = await findOpenVikingCli();
3747
3747
  if (!command2) {
3748
- throw new Error("Neither ov nor openviking was found in PATH. Run threadnote install first.");
3748
+ throw new Error(
3749
+ "Neither ov nor openviking was found in PATH, uv tool bin dir, $UV_TOOL_BIN_DIR, or ~/.local/bin. Run threadnote install first."
3750
+ );
3749
3751
  }
3750
3752
  return command2;
3751
3753
  }
3752
3754
  async function openVikingCliForMode(dryRun) {
3753
3755
  if (dryRun) {
3754
- return await findExecutable(["ov", "openviking"]) ?? "ov";
3756
+ return await findOpenVikingCli() ?? "ov";
3755
3757
  }
3756
3758
  return requiredOpenVikingCli();
3757
3759
  }
3760
+ async function findOpenVikingCli() {
3761
+ const override = process.env.THREADNOTE_OV?.trim();
3762
+ if (override) {
3763
+ return override;
3764
+ }
3765
+ const onPath = await findExecutable(["ov", "openviking"]);
3766
+ if (onPath) {
3767
+ return onPath;
3768
+ }
3769
+ for (const candidateDir of await openVikingToolCandidateDirs()) {
3770
+ for (const command2 of ["ov", "openviking"]) {
3771
+ const candidate = (0, import_node_path.join)(candidateDir, command2);
3772
+ if (await isExecutable(candidate)) {
3773
+ return candidate;
3774
+ }
3775
+ }
3776
+ }
3777
+ return void 0;
3778
+ }
3779
+ async function openVikingToolCandidateDirs() {
3780
+ const dirs = [];
3781
+ const uv = await findExecutable(["uv"]);
3782
+ if (uv) {
3783
+ const result = await runCommand(uv, ["tool", "dir", "--bin"], { allowFailure: true });
3784
+ if (result.exitCode === 0) {
3785
+ const dir = result.stdout.trim();
3786
+ if (dir) {
3787
+ dirs.push(dir);
3788
+ }
3789
+ }
3790
+ }
3791
+ if (process.env.UV_TOOL_BIN_DIR) {
3792
+ dirs.push(process.env.UV_TOOL_BIN_DIR);
3793
+ }
3794
+ dirs.push((0, import_node_path.join)((0, import_node_os.homedir)(), ".local", "bin"));
3795
+ return Array.from(new Set(dirs));
3796
+ }
3758
3797
  async function requiredExecutable(command2) {
3759
3798
  const executable = await findExecutable([command2]);
3760
3799
  if (!executable) {
@@ -12381,7 +12420,7 @@ async function maybeRunPostUpdateAfterRepair(config, options) {
12381
12420
  });
12382
12421
  }
12383
12422
  async function ensurePinnedOpenVikingInstalled(config, options) {
12384
- const ov = await findExecutable(["ov", "openviking"]);
12423
+ const ov = await findOpenVikingCli();
12385
12424
  if (!ov) {
12386
12425
  return;
12387
12426
  }
@@ -12813,7 +12852,7 @@ async function collectDoctorChecks(config, options = {}) {
12813
12852
  checks.push(await commandCheck("node", ["--version"]));
12814
12853
  checks.push(await commandCheck("python3", ["--version"]));
12815
12854
  checks.push(await openVikingServerCheck());
12816
- checks.push(await firstCommandCheck("openviking cli", ["ov", "openviking"], ["--help"]));
12855
+ checks.push(await openVikingCliCheck());
12817
12856
  checks.push(await localEmbeddingCheck());
12818
12857
  checks.push(await pythonSystemCertificatesCheck());
12819
12858
  checks.push(await firstCommandCheck("python installer", ["pipx", "uv", "pip3"], ["--version"]));
@@ -13018,9 +13057,9 @@ async function repairManifest(config, dryRun) {
13018
13057
  }
13019
13058
  async function repairRecallIndex(config, dryRun) {
13020
13059
  console.log("\nRepairing recall index freshness.");
13021
- const ov = dryRun ? await findExecutable(["ov", "openviking"]) ?? "ov" : await findExecutable(["ov", "openviking"]);
13060
+ const ov = dryRun ? await findOpenVikingCli() ?? "ov" : await findOpenVikingCli();
13022
13061
  if (!ov) {
13023
- console.log("Skipping recall index repair: neither ov nor openviking was found in PATH.");
13062
+ console.log("Skipping recall index repair: neither ov nor openviking was found.");
13024
13063
  return;
13025
13064
  }
13026
13065
  const progress = startProgress("Scanning recall index freshness across memories and seeded resources.");
@@ -13071,7 +13110,7 @@ async function repairRecallIndex(config, dryRun) {
13071
13110
  }
13072
13111
  }
13073
13112
  async function configureOpenVikingCliLanguage(config, dryRun) {
13074
- const ov = dryRun ? await findExecutable(["ov", "openviking"]) ?? "ov" : await findExecutable(["ov", "openviking"]);
13113
+ const ov = dryRun ? await findOpenVikingCli() ?? "ov" : await findOpenVikingCli();
13075
13114
  if (!ov) {
13076
13115
  return;
13077
13116
  }
@@ -13197,7 +13236,11 @@ async function runStart(config, options) {
13197
13236
  child.unref();
13198
13237
  await (0, import_promises12.writeFile)((0, import_node_path13.join)(config.agentContextHome, "openviking-server.pid"), `${child.pid}
13199
13238
  `, "utf8");
13200
- const health = await waitForOpenVikingHealth(config, START_HEALTH_TIMEOUT_MS);
13239
+ const health = await waitForOpenVikingHealth(
13240
+ config,
13241
+ START_HEALTH_TIMEOUT_MS,
13242
+ `Waiting for OpenViking health at ${healthUrl}.`
13243
+ );
13201
13244
  if (health) {
13202
13245
  console.log(`Started OpenViking with pid ${child.pid}. Health OK at ${healthUrl}. Logs: ${logPath}`);
13203
13246
  return;
@@ -13308,6 +13351,20 @@ async function firstCommandCheck(name, commands, args) {
13308
13351
  }
13309
13352
  return { name, status: "fail", detail: `none found: ${commands.join(", ")}` };
13310
13353
  }
13354
+ async function openVikingCliCheck() {
13355
+ const executable = await findOpenVikingCli();
13356
+ if (!executable) {
13357
+ return { name: "openviking cli", status: "fail", detail: "none found: ov, openviking" };
13358
+ }
13359
+ const result = await runCommand(executable, ["--help"], { allowFailure: true });
13360
+ const onPath = await findExecutable(["ov", "openviking"]);
13361
+ const detail = onPath ? executable : `${executable} (found outside PATH; add ${(0, import_node_path13.dirname)(executable)} to PATH)`;
13362
+ return {
13363
+ name: "openviking cli",
13364
+ status: result.exitCode === 0 ? "ok" : "warn",
13365
+ detail: result.exitCode === 0 ? detail : firstLine(result.stderr || result.stdout) || detail
13366
+ };
13367
+ }
13311
13368
  async function localEmbeddingCheck() {
13312
13369
  const serverPath = await findOpenVikingServer();
13313
13370
  if (!serverPath) {
@@ -13475,21 +13532,26 @@ async function readOpenVikingHealthIfAvailable(config, timeoutMs) {
13475
13532
  return void 0;
13476
13533
  }
13477
13534
  }
13478
- async function waitForOpenVikingHealth(config, timeoutMs) {
13479
- const deadline = Date.now() + timeoutMs;
13480
- while (Date.now() <= deadline) {
13481
- const requestTimeoutMs = Math.max(100, Math.min(1e3, deadline - Date.now()));
13482
- const health = await readOpenVikingHealthIfAvailable(config, requestTimeoutMs);
13483
- if (health) {
13484
- return health;
13485
- }
13486
- const remainingMs = deadline - Date.now();
13487
- if (remainingMs <= 0) {
13488
- break;
13535
+ async function waitForOpenVikingHealth(config, timeoutMs, progressMessage) {
13536
+ const progress = progressMessage ? startProgress(progressMessage) : void 0;
13537
+ try {
13538
+ const deadline = Date.now() + timeoutMs;
13539
+ while (Date.now() <= deadline) {
13540
+ const requestTimeoutMs = Math.max(100, Math.min(1e3, deadline - Date.now()));
13541
+ const health = await readOpenVikingHealthIfAvailable(config, requestTimeoutMs);
13542
+ if (health) {
13543
+ return health;
13544
+ }
13545
+ const remainingMs = deadline - Date.now();
13546
+ if (remainingMs <= 0) {
13547
+ break;
13548
+ }
13549
+ await sleep(Math.min(START_HEALTH_POLL_INTERVAL_MS, remainingMs));
13489
13550
  }
13490
- await sleep(Math.min(START_HEALTH_POLL_INTERVAL_MS, remainingMs));
13551
+ return void 0;
13552
+ } finally {
13553
+ progress?.stop();
13491
13554
  }
13492
- return void 0;
13493
13555
  }
13494
13556
  async function runInstallCommands(config, preferred, force, dryRun) {
13495
13557
  let manager = preferred;
@@ -13950,7 +14012,11 @@ async function installLaunchAgent(config, dryRun) {
13950
14012
  await maybeRun(false, "launchctl", ["load", destination]);
13951
14013
  await maybeRun(false, "launchctl", ["start", LAUNCHD_LABEL]);
13952
14014
  const healthUrl = openVikingHealthUrl(config);
13953
- const health = await waitForOpenVikingHealth(config, START_HEALTH_TIMEOUT_MS);
14015
+ const health = await waitForOpenVikingHealth(
14016
+ config,
14017
+ START_HEALTH_TIMEOUT_MS,
14018
+ `Waiting for OpenViking health at ${healthUrl}.`
14019
+ );
13954
14020
  if (health) {
13955
14021
  console.log(`Installed and started ${LAUNCHD_LABEL}. Health OK at ${healthUrl}`);
13956
14022
  return;
@@ -8,7 +8,9 @@ Run:
8
8
  threadnote install
9
9
  ```
10
10
 
11
- The installer prefers `uv`, then `pipx`, then `python3 -m pip install --user`.
11
+ The installer prefers `uv`, then `pipx`, then `python3 -m pip install --user`. For `curl | sh` installs, the wrapper
12
+ reattaches `threadnote install` to your terminal when possible so it can prompt to install `uv` and continue instead of
13
+ falling straight through to the pip fallback.
12
14
 
13
15
  ## `uv` Fails With `UnknownIssuer`
14
16
 
@@ -83,8 +85,14 @@ For detached starts, logs are written to:
83
85
  ~/.openviking/logs/server.log
84
86
  ```
85
87
 
86
- If `start` reports that OpenViking did not become healthy, open that log. Certificate failures during the first embedding
87
- model download are covered above.
88
+ If `start` reports that OpenViking did not become healthy, first check whether it finished shortly after the timeout:
89
+
90
+ ```bash
91
+ threadnote doctor --dry-run
92
+ ```
93
+
94
+ If it still is not healthy, open that log. Certificate failures during the first embedding model download are covered
95
+ above.
88
96
 
89
97
  ## Port Already In Use
90
98
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadnote",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "type": "commonjs",
5
5
  "main": "dist/threadnote.cjs",
6
6
  "description": "Shared local context and handoffs for development agents",
@@ -116,7 +116,11 @@ install_package "$runtime"
116
116
  threadnote_bin="$(find_threadnote "$runtime")" || die "Installed $PACKAGE, but could not find the threadnote command."
117
117
 
118
118
  say "Running threadnote install"
119
- "$threadnote_bin" install "$@"
119
+ if { true </dev/tty; } 2>/dev/null; then
120
+ "$threadnote_bin" install "$@" </dev/tty
121
+ else
122
+ "$threadnote_bin" install "$@"
123
+ fi
120
124
 
121
125
  if ! have threadnote; then
122
126
  say ""