threadnote 0.7.1 → 0.7.3
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/mcp_server.cjs +58 -25
- package/dist/threadnote.cjs +176 -81
- package/package.json +1 -1
package/dist/mcp_server.cjs
CHANGED
|
@@ -34108,10 +34108,8 @@ async function runShareSyncQuiet(config2, state, options) {
|
|
|
34108
34108
|
const git = await requiredExecutable("git");
|
|
34109
34109
|
const worktree = team.config.worktree;
|
|
34110
34110
|
const pendingChanges = state.pendingReindexes.get(team.name);
|
|
34111
|
-
if (pendingChanges) {
|
|
34112
|
-
await
|
|
34113
|
-
state.pendingReindexes.delete(team.name);
|
|
34114
|
-
await writePendingReindexes(config2, state);
|
|
34111
|
+
if (pendingChanges && pendingChanges.length > 0) {
|
|
34112
|
+
await applyAndPersistChanges(config2, team.config, state, pendingChanges, { quiet: true });
|
|
34115
34113
|
}
|
|
34116
34114
|
if (await hasUncommittedChanges(worktree)) {
|
|
34117
34115
|
return `Shared team "${team.name}" has uncommitted changes; skipped automatic sync. Run \`threadnote share sync --team ${team.name}\` to publish or resolve them.`;
|
|
@@ -34139,11 +34137,9 @@ async function runShareSyncQuiet(config2, state, options) {
|
|
|
34139
34137
|
if (beforeRev && afterRev && beforeRev !== afterRev) {
|
|
34140
34138
|
const changes = await listChangedFiles(worktree, beforeRev, afterRev);
|
|
34141
34139
|
if (changes.length > 0) {
|
|
34142
|
-
state.pendingReindexes.
|
|
34143
|
-
|
|
34144
|
-
await
|
|
34145
|
-
state.pendingReindexes.delete(team.name);
|
|
34146
|
-
await writePendingReindexes(config2, state);
|
|
34140
|
+
const stillPending = state.pendingReindexes.get(team.name) ?? [];
|
|
34141
|
+
const combined = mergeChanges(stillPending, changes);
|
|
34142
|
+
await applyAndPersistChanges(config2, team.config, state, combined, { quiet: true });
|
|
34147
34143
|
}
|
|
34148
34144
|
}
|
|
34149
34145
|
return void 0;
|
|
@@ -34339,8 +34335,9 @@ async function writeMemoryFile(config2, ov, uri, content, initialMode, dryRun, o
|
|
|
34339
34335
|
await (0, import_promises3.rm)(stagingDir, { force: true, recursive: true });
|
|
34340
34336
|
}
|
|
34341
34337
|
}
|
|
34338
|
+
var BUSY_RETRY_BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 3e4];
|
|
34342
34339
|
async function writeOvFileWithRetry(config2, ov, uri, fromFile, initialMode, options = {}) {
|
|
34343
|
-
const maxAttempts =
|
|
34340
|
+
const maxAttempts = BUSY_RETRY_BACKOFF_MS.length + 1;
|
|
34344
34341
|
const existedBeforeWrite = await vikingResourceExists(ov, config2, uri);
|
|
34345
34342
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
34346
34343
|
const existsNow = attempt === 0 ? existedBeforeWrite : await vikingResourceExists(ov, config2, uri);
|
|
@@ -34384,6 +34381,7 @@ async function writeOvFileWithRetry(config2, ov, uri, fromFile, initialMode, opt
|
|
|
34384
34381
|
}
|
|
34385
34382
|
if (isResourceBusyFailure(result.stderr, result.stdout)) {
|
|
34386
34383
|
await waitForOvQueue(ov, config2, options);
|
|
34384
|
+
await sleep(BUSY_RETRY_BACKOFF_MS[attempt] ?? 3e4);
|
|
34387
34385
|
} else {
|
|
34388
34386
|
await sleep(1e3 * (attempt + 1));
|
|
34389
34387
|
}
|
|
@@ -34461,7 +34459,7 @@ async function removeMemoryUri(config2, ov, uri, dryRun, options = {}) {
|
|
|
34461
34459
|
}
|
|
34462
34460
|
return;
|
|
34463
34461
|
}
|
|
34464
|
-
const maxAttempts =
|
|
34462
|
+
const maxAttempts = BUSY_RETRY_BACKOFF_MS.length + 1;
|
|
34465
34463
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
34466
34464
|
const result = await runCommand(ov, args, { allowFailure: true });
|
|
34467
34465
|
if (result.exitCode === 0) {
|
|
@@ -34475,6 +34473,7 @@ async function removeMemoryUri(config2, ov, uri, dryRun, options = {}) {
|
|
|
34475
34473
|
}
|
|
34476
34474
|
if (isResourceBusyFailure(result.stderr, result.stdout)) {
|
|
34477
34475
|
await waitForOvQueue(ov, config2, options);
|
|
34476
|
+
await sleep(BUSY_RETRY_BACKOFF_MS[attempt] ?? 3e4);
|
|
34478
34477
|
} else {
|
|
34479
34478
|
await sleep(1e3 * (attempt + 1));
|
|
34480
34479
|
}
|
|
@@ -34531,6 +34530,7 @@ async function listChangedFiles(worktree, beforeRev, afterRev) {
|
|
|
34531
34530
|
}
|
|
34532
34531
|
async function applyChangesToOpenViking(config2, team, changes, options = {}) {
|
|
34533
34532
|
const ov = await openVikingCliForMode(false);
|
|
34533
|
+
const failed = [];
|
|
34534
34534
|
for (const change of changes) {
|
|
34535
34535
|
if (!change.relativePath.endsWith(".md")) {
|
|
34536
34536
|
continue;
|
|
@@ -34540,24 +34540,57 @@ async function applyChangesToOpenViking(config2, team, changes, options = {}) {
|
|
|
34540
34540
|
continue;
|
|
34541
34541
|
}
|
|
34542
34542
|
const uri = workfileToVikingUri(config2, team, change.path);
|
|
34543
|
-
|
|
34544
|
-
|
|
34545
|
-
|
|
34546
|
-
|
|
34547
|
-
|
|
34548
|
-
|
|
34549
|
-
|
|
34550
|
-
|
|
34551
|
-
|
|
34552
|
-
|
|
34543
|
+
try {
|
|
34544
|
+
if (change.status === "removed") {
|
|
34545
|
+
await removeMemoryUri(config2, ov, uri, false, options);
|
|
34546
|
+
continue;
|
|
34547
|
+
}
|
|
34548
|
+
if (!await isFile(change.path)) {
|
|
34549
|
+
continue;
|
|
34550
|
+
}
|
|
34551
|
+
const ovHasResource = await vikingResourceExists(ov, config2, uri);
|
|
34552
|
+
if (ovHasResource) {
|
|
34553
|
+
const reason = change.status === "modified" ? "overwriting local with upstream (local edits to the shared subtree are not preserved across sync)" : "aligning OV to upstream (resource pre-existed in OV, likely from an earlier local publish or sync)";
|
|
34554
|
+
if (options.quiet !== true) {
|
|
34555
|
+
console.warn(`share sync: ${uri}: ${reason}.`);
|
|
34556
|
+
}
|
|
34557
|
+
}
|
|
34558
|
+
await ensureSharedDirectoryChain(config2, ov, uri, false, options);
|
|
34559
|
+
const writeMode = ovHasResource ? "replace" : "create";
|
|
34560
|
+
await ingestSingleFile(ov, config2, uri, change.path, writeMode, options);
|
|
34561
|
+
} catch (err) {
|
|
34562
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
34553
34563
|
if (options.quiet !== true) {
|
|
34554
|
-
console.warn(`share sync: ${uri}: ${
|
|
34564
|
+
console.warn(`share sync: ${uri}: ingest failed \u2014 will retry on the next sync. ${message}`);
|
|
34555
34565
|
}
|
|
34566
|
+
failed.push(change);
|
|
34567
|
+
}
|
|
34568
|
+
}
|
|
34569
|
+
return { failed };
|
|
34570
|
+
}
|
|
34571
|
+
function mergeChanges(...lists) {
|
|
34572
|
+
const map3 = /* @__PURE__ */ new Map();
|
|
34573
|
+
for (const list of lists) {
|
|
34574
|
+
for (const change of list) {
|
|
34575
|
+
map3.set(change.relativePath, change);
|
|
34556
34576
|
}
|
|
34557
|
-
await ensureSharedDirectoryChain(config2, ov, uri, false, options);
|
|
34558
|
-
const writeMode = ovHasResource ? "replace" : "create";
|
|
34559
|
-
await ingestSingleFile(ov, config2, uri, change.path, writeMode, options);
|
|
34560
34577
|
}
|
|
34578
|
+
return [...map3.values()];
|
|
34579
|
+
}
|
|
34580
|
+
async function applyAndPersistChanges(config2, team, state, changes, options = {}) {
|
|
34581
|
+
if (changes.length === 0) {
|
|
34582
|
+
return { failed: [] };
|
|
34583
|
+
}
|
|
34584
|
+
state.pendingReindexes.set(team.name, changes);
|
|
34585
|
+
await writePendingReindexes(config2, state);
|
|
34586
|
+
const result = await applyChangesToOpenViking(config2, team, changes, options);
|
|
34587
|
+
if (result.failed.length > 0) {
|
|
34588
|
+
state.pendingReindexes.set(team.name, result.failed);
|
|
34589
|
+
} else {
|
|
34590
|
+
state.pendingReindexes.delete(team.name);
|
|
34591
|
+
}
|
|
34592
|
+
await writePendingReindexes(config2, state);
|
|
34593
|
+
return result;
|
|
34561
34594
|
}
|
|
34562
34595
|
|
|
34563
34596
|
// src/mcp_server.ts
|
package/dist/threadnote.cjs
CHANGED
|
@@ -3472,7 +3472,7 @@ var {
|
|
|
3472
3472
|
// src/constants.ts
|
|
3473
3473
|
var DEFAULT_HOST = "127.0.0.1";
|
|
3474
3474
|
var DEFAULT_PORT = 1933;
|
|
3475
|
-
var DEFAULT_OPENVIKING_VERSION = "0.3.
|
|
3475
|
+
var DEFAULT_OPENVIKING_VERSION = "0.3.21";
|
|
3476
3476
|
var DEFAULT_ACCOUNT = "local";
|
|
3477
3477
|
var DEFAULT_AGENT_ID = "threadnote";
|
|
3478
3478
|
var OPENVIKING_PACKAGE_NAME = "openviking[local-embed]";
|
|
@@ -3747,6 +3747,38 @@ async function sleep(ms) {
|
|
|
3747
3747
|
setTimeout(resolvePromise, ms);
|
|
3748
3748
|
});
|
|
3749
3749
|
}
|
|
3750
|
+
function compareVersions(a, b) {
|
|
3751
|
+
const left = parseVersion(a);
|
|
3752
|
+
const right = parseVersion(b);
|
|
3753
|
+
for (let index = 0; index < 3; index += 1) {
|
|
3754
|
+
const difference = left.numbers[index] - right.numbers[index];
|
|
3755
|
+
if (difference !== 0) {
|
|
3756
|
+
return difference;
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
if (left.prerelease === right.prerelease) {
|
|
3760
|
+
return 0;
|
|
3761
|
+
}
|
|
3762
|
+
if (left.prerelease === void 0) {
|
|
3763
|
+
return 1;
|
|
3764
|
+
}
|
|
3765
|
+
if (right.prerelease === void 0) {
|
|
3766
|
+
return -1;
|
|
3767
|
+
}
|
|
3768
|
+
return left.prerelease.localeCompare(right.prerelease);
|
|
3769
|
+
}
|
|
3770
|
+
function parseVersion(version) {
|
|
3771
|
+
const normalized = version.trim().replace(/^v/, "");
|
|
3772
|
+
const [core2, prerelease] = normalized.split("-", 2);
|
|
3773
|
+
const parts = core2.split(".").map((part) => Number(part));
|
|
3774
|
+
return {
|
|
3775
|
+
numbers: [safeVersionNumber(parts[0]), safeVersionNumber(parts[1]), safeVersionNumber(parts[2])],
|
|
3776
|
+
prerelease
|
|
3777
|
+
};
|
|
3778
|
+
}
|
|
3779
|
+
function safeVersionNumber(value) {
|
|
3780
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : 0;
|
|
3781
|
+
}
|
|
3750
3782
|
async function readHttpStatus(url, timeoutMs) {
|
|
3751
3783
|
return new Promise((resolvePromise) => {
|
|
3752
3784
|
const request = (0, import_node_http.get)(url, (response) => {
|
|
@@ -7583,12 +7615,24 @@ Resolve the conflicts in-place, run \`git -C ${worktree} rebase --continue\` (or
|
|
|
7583
7615
|
);
|
|
7584
7616
|
}
|
|
7585
7617
|
const afterRev = await gitOutput(worktree, ["rev-parse", "HEAD"], dryRun);
|
|
7586
|
-
if (!dryRun
|
|
7587
|
-
const
|
|
7588
|
-
await
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7618
|
+
if (!dryRun) {
|
|
7619
|
+
const state = autoShareState(config);
|
|
7620
|
+
await loadPendingReindexes(config, state);
|
|
7621
|
+
const previouslyPending = state.pendingReindexes.get(team.name) ?? [];
|
|
7622
|
+
const newChanges = beforeRev && afterRev && beforeRev !== afterRev ? await listChangedFiles(worktree, beforeRev, afterRev) : [];
|
|
7623
|
+
const combined = mergeChanges(previouslyPending, newChanges);
|
|
7624
|
+
if (combined.length === 0) {
|
|
7625
|
+
console.log("No upstream changes to reindex.");
|
|
7626
|
+
} else {
|
|
7627
|
+
const result = await applyAndPersistChanges(config, team.config, state, combined);
|
|
7628
|
+
const succeeded = combined.length - result.failed.length;
|
|
7629
|
+
console.log(`Reindexed ${succeeded} file change(s) into OpenViking.`);
|
|
7630
|
+
if (result.failed.length > 0) {
|
|
7631
|
+
console.warn(
|
|
7632
|
+
`share sync: ${result.failed.length} file(s) could not be ingested on this run; they are persisted and will be retried on the next sync or agent recall/read.`
|
|
7633
|
+
);
|
|
7634
|
+
}
|
|
7635
|
+
}
|
|
7592
7636
|
}
|
|
7593
7637
|
if (options.push !== false) {
|
|
7594
7638
|
await maybeRun(dryRun, git, ["-C", worktree, "push", DEFAULT_GIT_REMOTE_NAME], { allowFailure: true });
|
|
@@ -7599,10 +7643,8 @@ async function runShareSyncQuiet(config, state, options) {
|
|
|
7599
7643
|
const git = await requiredExecutable("git");
|
|
7600
7644
|
const worktree = team.config.worktree;
|
|
7601
7645
|
const pendingChanges = state.pendingReindexes.get(team.name);
|
|
7602
|
-
if (pendingChanges) {
|
|
7603
|
-
await
|
|
7604
|
-
state.pendingReindexes.delete(team.name);
|
|
7605
|
-
await writePendingReindexes(config, state);
|
|
7646
|
+
if (pendingChanges && pendingChanges.length > 0) {
|
|
7647
|
+
await applyAndPersistChanges(config, team.config, state, pendingChanges, { quiet: true });
|
|
7606
7648
|
}
|
|
7607
7649
|
if (await hasUncommittedChanges(worktree)) {
|
|
7608
7650
|
return `Shared team "${team.name}" has uncommitted changes; skipped automatic sync. Run \`threadnote share sync --team ${team.name}\` to publish or resolve them.`;
|
|
@@ -7630,11 +7672,9 @@ async function runShareSyncQuiet(config, state, options) {
|
|
|
7630
7672
|
if (beforeRev && afterRev && beforeRev !== afterRev) {
|
|
7631
7673
|
const changes = await listChangedFiles(worktree, beforeRev, afterRev);
|
|
7632
7674
|
if (changes.length > 0) {
|
|
7633
|
-
state.pendingReindexes.
|
|
7634
|
-
|
|
7635
|
-
await
|
|
7636
|
-
state.pendingReindexes.delete(team.name);
|
|
7637
|
-
await writePendingReindexes(config, state);
|
|
7675
|
+
const stillPending = state.pendingReindexes.get(team.name) ?? [];
|
|
7676
|
+
const combined = mergeChanges(stillPending, changes);
|
|
7677
|
+
await applyAndPersistChanges(config, team.config, state, combined, { quiet: true });
|
|
7638
7678
|
}
|
|
7639
7679
|
}
|
|
7640
7680
|
return void 0;
|
|
@@ -8074,8 +8114,9 @@ async function writeMemoryFile(config, ov, uri, content, initialMode, dryRun, op
|
|
|
8074
8114
|
await (0, import_promises4.rm)(stagingDir, { force: true, recursive: true });
|
|
8075
8115
|
}
|
|
8076
8116
|
}
|
|
8117
|
+
var BUSY_RETRY_BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 3e4];
|
|
8077
8118
|
async function writeOvFileWithRetry(config, ov, uri, fromFile, initialMode, options = {}) {
|
|
8078
|
-
const maxAttempts =
|
|
8119
|
+
const maxAttempts = BUSY_RETRY_BACKOFF_MS.length + 1;
|
|
8079
8120
|
const existedBeforeWrite = await vikingResourceExists(ov, config, uri);
|
|
8080
8121
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
8081
8122
|
const existsNow = attempt === 0 ? existedBeforeWrite : await vikingResourceExists(ov, config, uri);
|
|
@@ -8119,6 +8160,7 @@ async function writeOvFileWithRetry(config, ov, uri, fromFile, initialMode, opti
|
|
|
8119
8160
|
}
|
|
8120
8161
|
if (isResourceBusyFailure(result.stderr, result.stdout)) {
|
|
8121
8162
|
await waitForOvQueue(ov, config, options);
|
|
8163
|
+
await sleep(BUSY_RETRY_BACKOFF_MS[attempt] ?? 3e4);
|
|
8122
8164
|
} else {
|
|
8123
8165
|
await sleep(1e3 * (attempt + 1));
|
|
8124
8166
|
}
|
|
@@ -8196,7 +8238,7 @@ async function removeMemoryUri(config, ov, uri, dryRun, options = {}) {
|
|
|
8196
8238
|
}
|
|
8197
8239
|
return;
|
|
8198
8240
|
}
|
|
8199
|
-
const maxAttempts =
|
|
8241
|
+
const maxAttempts = BUSY_RETRY_BACKOFF_MS.length + 1;
|
|
8200
8242
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
8201
8243
|
const result = await runCommand(ov, args, { allowFailure: true });
|
|
8202
8244
|
if (result.exitCode === 0) {
|
|
@@ -8210,6 +8252,7 @@ async function removeMemoryUri(config, ov, uri, dryRun, options = {}) {
|
|
|
8210
8252
|
}
|
|
8211
8253
|
if (isResourceBusyFailure(result.stderr, result.stdout)) {
|
|
8212
8254
|
await waitForOvQueue(ov, config, options);
|
|
8255
|
+
await sleep(BUSY_RETRY_BACKOFF_MS[attempt] ?? 3e4);
|
|
8213
8256
|
} else {
|
|
8214
8257
|
await sleep(1e3 * (attempt + 1));
|
|
8215
8258
|
}
|
|
@@ -8266,6 +8309,7 @@ async function listChangedFiles(worktree, beforeRev, afterRev) {
|
|
|
8266
8309
|
}
|
|
8267
8310
|
async function applyChangesToOpenViking(config, team, changes, options = {}) {
|
|
8268
8311
|
const ov = await openVikingCliForMode(false);
|
|
8312
|
+
const failed = [];
|
|
8269
8313
|
for (const change of changes) {
|
|
8270
8314
|
if (!change.relativePath.endsWith(".md")) {
|
|
8271
8315
|
continue;
|
|
@@ -8275,24 +8319,57 @@ async function applyChangesToOpenViking(config, team, changes, options = {}) {
|
|
|
8275
8319
|
continue;
|
|
8276
8320
|
}
|
|
8277
8321
|
const uri = workfileToVikingUri(config, team, change.path);
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8322
|
+
try {
|
|
8323
|
+
if (change.status === "removed") {
|
|
8324
|
+
await removeMemoryUri(config, ov, uri, false, options);
|
|
8325
|
+
continue;
|
|
8326
|
+
}
|
|
8327
|
+
if (!await isFile(change.path)) {
|
|
8328
|
+
continue;
|
|
8329
|
+
}
|
|
8330
|
+
const ovHasResource = await vikingResourceExists(ov, config, uri);
|
|
8331
|
+
if (ovHasResource) {
|
|
8332
|
+
const reason = change.status === "modified" ? "overwriting local with upstream (local edits to the shared subtree are not preserved across sync)" : "aligning OV to upstream (resource pre-existed in OV, likely from an earlier local publish or sync)";
|
|
8333
|
+
if (options.quiet !== true) {
|
|
8334
|
+
console.warn(`share sync: ${uri}: ${reason}.`);
|
|
8335
|
+
}
|
|
8336
|
+
}
|
|
8337
|
+
await ensureSharedDirectoryChain(config, ov, uri, false, options);
|
|
8338
|
+
const writeMode = ovHasResource ? "replace" : "create";
|
|
8339
|
+
await ingestSingleFile(ov, config, uri, change.path, writeMode, options);
|
|
8340
|
+
} catch (err) {
|
|
8341
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8288
8342
|
if (options.quiet !== true) {
|
|
8289
|
-
console.warn(`share sync: ${uri}: ${
|
|
8343
|
+
console.warn(`share sync: ${uri}: ingest failed \u2014 will retry on the next sync. ${message}`);
|
|
8290
8344
|
}
|
|
8345
|
+
failed.push(change);
|
|
8346
|
+
}
|
|
8347
|
+
}
|
|
8348
|
+
return { failed };
|
|
8349
|
+
}
|
|
8350
|
+
function mergeChanges(...lists) {
|
|
8351
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
8352
|
+
for (const list of lists) {
|
|
8353
|
+
for (const change of list) {
|
|
8354
|
+
map2.set(change.relativePath, change);
|
|
8291
8355
|
}
|
|
8292
|
-
await ensureSharedDirectoryChain(config, ov, uri, false, options);
|
|
8293
|
-
const writeMode = ovHasResource ? "replace" : "create";
|
|
8294
|
-
await ingestSingleFile(ov, config, uri, change.path, writeMode, options);
|
|
8295
8356
|
}
|
|
8357
|
+
return [...map2.values()];
|
|
8358
|
+
}
|
|
8359
|
+
async function applyAndPersistChanges(config, team, state, changes, options = {}) {
|
|
8360
|
+
if (changes.length === 0) {
|
|
8361
|
+
return { failed: [] };
|
|
8362
|
+
}
|
|
8363
|
+
state.pendingReindexes.set(team.name, changes);
|
|
8364
|
+
await writePendingReindexes(config, state);
|
|
8365
|
+
const result = await applyChangesToOpenViking(config, team, changes, options);
|
|
8366
|
+
if (result.failed.length > 0) {
|
|
8367
|
+
state.pendingReindexes.set(team.name, result.failed);
|
|
8368
|
+
} else {
|
|
8369
|
+
state.pendingReindexes.delete(team.name);
|
|
8370
|
+
}
|
|
8371
|
+
await writePendingReindexes(config, state);
|
|
8372
|
+
return result;
|
|
8296
8373
|
}
|
|
8297
8374
|
|
|
8298
8375
|
// src/memory.ts
|
|
@@ -9234,7 +9311,7 @@ async function checkForThreadnoteUpdate(args) {
|
|
|
9234
9311
|
}
|
|
9235
9312
|
const cached = await readUpdateCache(args.cachePath);
|
|
9236
9313
|
if (cached && isCacheFresh(cached)) {
|
|
9237
|
-
return
|
|
9314
|
+
return compareVersions2(args.currentVersion, cached.latestVersion);
|
|
9238
9315
|
}
|
|
9239
9316
|
const fresh = await fetchLatestVersion();
|
|
9240
9317
|
if (fresh) {
|
|
@@ -9243,10 +9320,10 @@ async function checkForThreadnoteUpdate(args) {
|
|
|
9243
9320
|
latestVersion: fresh,
|
|
9244
9321
|
version: 1
|
|
9245
9322
|
});
|
|
9246
|
-
return
|
|
9323
|
+
return compareVersions2(args.currentVersion, fresh);
|
|
9247
9324
|
}
|
|
9248
9325
|
if (cached) {
|
|
9249
|
-
return
|
|
9326
|
+
return compareVersions2(args.currentVersion, cached.latestVersion);
|
|
9250
9327
|
}
|
|
9251
9328
|
return void 0;
|
|
9252
9329
|
}
|
|
@@ -9264,7 +9341,7 @@ function spawnDetachedAutoUpdate() {
|
|
|
9264
9341
|
} catch {
|
|
9265
9342
|
}
|
|
9266
9343
|
}
|
|
9267
|
-
function
|
|
9344
|
+
function compareVersions2(currentVersion, latestVersion) {
|
|
9268
9345
|
return {
|
|
9269
9346
|
currentVersion,
|
|
9270
9347
|
latestVersion,
|
|
@@ -9272,8 +9349,8 @@ function compareVersions(currentVersion, latestVersion) {
|
|
|
9272
9349
|
};
|
|
9273
9350
|
}
|
|
9274
9351
|
function isNewerVersion(candidate, baseline) {
|
|
9275
|
-
const candidateParts =
|
|
9276
|
-
const baselineParts =
|
|
9352
|
+
const candidateParts = parseVersion2(candidate);
|
|
9353
|
+
const baselineParts = parseVersion2(baseline);
|
|
9277
9354
|
for (let index = 0; index < 3; index += 1) {
|
|
9278
9355
|
if (candidateParts[index] !== baselineParts[index]) {
|
|
9279
9356
|
return candidateParts[index] > baselineParts[index];
|
|
@@ -9281,7 +9358,7 @@ function isNewerVersion(candidate, baseline) {
|
|
|
9281
9358
|
}
|
|
9282
9359
|
return false;
|
|
9283
9360
|
}
|
|
9284
|
-
function
|
|
9361
|
+
function parseVersion2(value) {
|
|
9285
9362
|
const parts = value.split(".").map((part) => parseInt(part, 10));
|
|
9286
9363
|
return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
|
|
9287
9364
|
}
|
|
@@ -9999,7 +10076,7 @@ async function runUpdate(config, options) {
|
|
|
9999
10076
|
console.log(`Update available. Run: threadnote update`);
|
|
10000
10077
|
} else {
|
|
10001
10078
|
console.log(
|
|
10002
|
-
|
|
10079
|
+
compareVersions(info.currentVersion, info.latestVersion) > 0 ? "Current version is newer than npm latest." : "Threadnote is up to date."
|
|
10003
10080
|
);
|
|
10004
10081
|
}
|
|
10005
10082
|
return;
|
|
@@ -10046,10 +10123,16 @@ async function runPostUpdate(config, options) {
|
|
|
10046
10123
|
if (!options.fromVersion || !options.toVersion) {
|
|
10047
10124
|
throw new Error("Provide --from-version and --to-version for post-update.");
|
|
10048
10125
|
}
|
|
10126
|
+
const interactive = process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
10127
|
+
await ensurePinnedOpenVikingInstalled(config, {
|
|
10128
|
+
dryRun: options.dryRun === true,
|
|
10129
|
+
interactive,
|
|
10130
|
+
yes: options.yes === true
|
|
10131
|
+
});
|
|
10049
10132
|
await runApplicablePostUpdateMigrations(config, {
|
|
10050
10133
|
dryRun: options.dryRun === true,
|
|
10051
10134
|
fromVersion: options.fromVersion,
|
|
10052
|
-
interactive
|
|
10135
|
+
interactive,
|
|
10053
10136
|
markHandled: true,
|
|
10054
10137
|
toVersion: options.toVersion,
|
|
10055
10138
|
yes: options.yes === true
|
|
@@ -10057,6 +10140,8 @@ async function runPostUpdate(config, options) {
|
|
|
10057
10140
|
}
|
|
10058
10141
|
async function maybeRunPostUpdateAfterRepair(config, options) {
|
|
10059
10142
|
const toVersion = await currentPackageVersion();
|
|
10143
|
+
const interactive = process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
10144
|
+
await ensurePinnedOpenVikingInstalled(config, { dryRun: options.dryRun, interactive, yes: false });
|
|
10060
10145
|
const state = await readPostUpdateState(config);
|
|
10061
10146
|
const migrations = await applicablePostUpdateMigrations(config, {
|
|
10062
10147
|
fromVersion: "0.0.0",
|
|
@@ -10069,7 +10154,7 @@ async function maybeRunPostUpdateAfterRepair(config, options) {
|
|
|
10069
10154
|
console.log("");
|
|
10070
10155
|
console.log("Repair found package post-update migrations.");
|
|
10071
10156
|
console.log("This also covers updates launched by older Threadnote versions that only knew how to run repair.");
|
|
10072
|
-
if (
|
|
10157
|
+
if (!interactive) {
|
|
10073
10158
|
console.log(
|
|
10074
10159
|
"This process is non-interactive, so Threadnote will print the manual migration command instead of prompting."
|
|
10075
10160
|
);
|
|
@@ -10078,12 +10163,48 @@ async function maybeRunPostUpdateAfterRepair(config, options) {
|
|
|
10078
10163
|
await runApplicablePostUpdateMigrations(config, {
|
|
10079
10164
|
dryRun: options.dryRun,
|
|
10080
10165
|
fromVersion: "0.0.0",
|
|
10081
|
-
interactive
|
|
10166
|
+
interactive,
|
|
10082
10167
|
markHandled: true,
|
|
10083
10168
|
toVersion,
|
|
10084
10169
|
yes: false
|
|
10085
10170
|
});
|
|
10086
10171
|
}
|
|
10172
|
+
async function ensurePinnedOpenVikingInstalled(config, options) {
|
|
10173
|
+
const ov = await findExecutable(["ov", "openviking"]);
|
|
10174
|
+
if (!ov) {
|
|
10175
|
+
return;
|
|
10176
|
+
}
|
|
10177
|
+
const installedVersion = await readOpenVikingCliVersion(ov);
|
|
10178
|
+
if (!installedVersion) {
|
|
10179
|
+
console.log(`Could not detect OpenViking CLI version via \`${ov} version\`; skipping pinned-version check.`);
|
|
10180
|
+
return;
|
|
10181
|
+
}
|
|
10182
|
+
const pinned = config.openVikingVersion;
|
|
10183
|
+
if (compareVersions(installedVersion, pinned) >= 0) {
|
|
10184
|
+
return;
|
|
10185
|
+
}
|
|
10186
|
+
console.log("");
|
|
10187
|
+
console.log(`OpenViking ${installedVersion} is older than the pinned version ${pinned}.`);
|
|
10188
|
+
console.log(
|
|
10189
|
+
"Upgrading picks up upstream fixes for share-sync reliability (reindex lock acquisition, ov wait timeout)."
|
|
10190
|
+
);
|
|
10191
|
+
const threadnoteCommand = currentThreadnoteCommand() ?? await findExecutable([NPM_PACKAGE_NAME]) ?? NPM_PACKAGE_NAME;
|
|
10192
|
+
const installArgs = ["install", "--force", "--no-start"];
|
|
10193
|
+
const accepted = options.dryRun || options.yes || options.interactive && await confirmPostUpdateMigration(`Upgrade OpenViking to ${pinned} now? [Y/n] `, true);
|
|
10194
|
+
if (!accepted) {
|
|
10195
|
+
console.log(`Skipped. Run manually later: ${formatMigrationCommand(threadnoteCommand, installArgs)}`);
|
|
10196
|
+
return;
|
|
10197
|
+
}
|
|
10198
|
+
await maybeRun(options.dryRun, threadnoteCommand, installArgs);
|
|
10199
|
+
}
|
|
10200
|
+
async function readOpenVikingCliVersion(ov) {
|
|
10201
|
+
const result = await runCommand(ov, ["version"], { allowFailure: true });
|
|
10202
|
+
if (result.exitCode !== 0) {
|
|
10203
|
+
return void 0;
|
|
10204
|
+
}
|
|
10205
|
+
const match = result.stdout.match(/^\s*CLI:\s*(\S+)/m);
|
|
10206
|
+
return match ? match[1] : void 0;
|
|
10207
|
+
}
|
|
10087
10208
|
async function getUpdateInfo(config, options) {
|
|
10088
10209
|
const currentVersion = await currentPackageVersion();
|
|
10089
10210
|
const cached = options.preferFresh ? void 0 : await readFreshCache(config, options.registry);
|
|
@@ -10093,7 +10214,7 @@ async function getUpdateInfo(config, options) {
|
|
|
10093
10214
|
}
|
|
10094
10215
|
return {
|
|
10095
10216
|
currentVersion,
|
|
10096
|
-
isUpdateAvailable:
|
|
10217
|
+
isUpdateAvailable: compareVersions(currentVersion, latestVersion) < 0,
|
|
10097
10218
|
latestVersion,
|
|
10098
10219
|
registry: options.registry
|
|
10099
10220
|
};
|
|
@@ -10203,10 +10324,10 @@ async function applicablePostUpdateMigrations(config, options) {
|
|
|
10203
10324
|
if (handled.has(migration.id)) {
|
|
10204
10325
|
continue;
|
|
10205
10326
|
}
|
|
10206
|
-
if (
|
|
10327
|
+
if (compareVersions(options.fromVersion, migration.introducedIn) >= 0) {
|
|
10207
10328
|
continue;
|
|
10208
10329
|
}
|
|
10209
|
-
if (
|
|
10330
|
+
if (compareVersions(migration.introducedIn, options.toVersion) > 0) {
|
|
10210
10331
|
continue;
|
|
10211
10332
|
}
|
|
10212
10333
|
if (migration.requiresLegacyHandoffs === true && !await hasLegacyLifecycleHandoffCandidates(config)) {
|
|
@@ -10255,10 +10376,13 @@ function printPostUpdateMigration(migration) {
|
|
|
10255
10376
|
console.log(`- ${line}`);
|
|
10256
10377
|
}
|
|
10257
10378
|
}
|
|
10258
|
-
async function confirmPostUpdateMigration(prompt) {
|
|
10379
|
+
async function confirmPostUpdateMigration(prompt, defaultYes = false) {
|
|
10259
10380
|
const readline = (0, import_promises10.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
|
|
10260
10381
|
try {
|
|
10261
10382
|
const answer = (await readline.question(prompt)).trim().toLowerCase();
|
|
10383
|
+
if (answer === "") {
|
|
10384
|
+
return defaultYes;
|
|
10385
|
+
}
|
|
10262
10386
|
return answer === "y" || answer === "yes";
|
|
10263
10387
|
} finally {
|
|
10264
10388
|
readline.close();
|
|
@@ -10374,38 +10498,6 @@ function updateRegistry() {
|
|
|
10374
10498
|
function isUpdateNotificationDisabled() {
|
|
10375
10499
|
return process.env.CI !== void 0 || process.env.NO_UPDATE_NOTIFIER !== void 0 || process.env.THREADNOTE_NO_UPDATE_CHECK !== void 0;
|
|
10376
10500
|
}
|
|
10377
|
-
function compareVersions2(currentVersion, latestVersion) {
|
|
10378
|
-
const current = parseVersion2(currentVersion);
|
|
10379
|
-
const latest = parseVersion2(latestVersion);
|
|
10380
|
-
for (let index = 0; index < 3; index += 1) {
|
|
10381
|
-
const difference = current.numbers[index] - latest.numbers[index];
|
|
10382
|
-
if (difference !== 0) {
|
|
10383
|
-
return difference;
|
|
10384
|
-
}
|
|
10385
|
-
}
|
|
10386
|
-
if (current.prerelease === latest.prerelease) {
|
|
10387
|
-
return 0;
|
|
10388
|
-
}
|
|
10389
|
-
if (current.prerelease === void 0) {
|
|
10390
|
-
return 1;
|
|
10391
|
-
}
|
|
10392
|
-
if (latest.prerelease === void 0) {
|
|
10393
|
-
return -1;
|
|
10394
|
-
}
|
|
10395
|
-
return current.prerelease.localeCompare(latest.prerelease);
|
|
10396
|
-
}
|
|
10397
|
-
function parseVersion2(version) {
|
|
10398
|
-
const normalized = version.trim().replace(/^v/, "");
|
|
10399
|
-
const [core2, prerelease] = normalized.split("-", 2);
|
|
10400
|
-
const parts = core2.split(".").map((part) => Number(part));
|
|
10401
|
-
return {
|
|
10402
|
-
numbers: [safeVersionNumber(parts[0]), safeVersionNumber(parts[1]), safeVersionNumber(parts[2])],
|
|
10403
|
-
prerelease
|
|
10404
|
-
};
|
|
10405
|
-
}
|
|
10406
|
-
function safeVersionNumber(value) {
|
|
10407
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : 0;
|
|
10408
|
-
}
|
|
10409
10501
|
|
|
10410
10502
|
// src/lifecycle.ts
|
|
10411
10503
|
async function runDoctor(config, options) {
|
|
@@ -10453,7 +10545,10 @@ async function runInstall(config, options) {
|
|
|
10453
10545
|
console.log(`OpenViking server already installed: ${serverPath}`);
|
|
10454
10546
|
const localEmbeddingMissing = await hasLocalEmbeddingDependency(serverPath) === false;
|
|
10455
10547
|
const pythonSystemCertificatesMissing = await hasPythonSystemCertificatesPatch(serverPath) === false;
|
|
10456
|
-
if (
|
|
10548
|
+
if (options.force === true) {
|
|
10549
|
+
console.log(`Reinstalling OpenViking at pinned version ${config.openVikingVersion} (--force).`);
|
|
10550
|
+
await runInstallCommands(config, options.packageManager, true, dryRun);
|
|
10551
|
+
} else if (localEmbeddingMissing) {
|
|
10457
10552
|
const repairReasons = [];
|
|
10458
10553
|
repairReasons.push("local embedding extra is missing");
|
|
10459
10554
|
if (pythonSystemCertificatesMissing) {
|
|
@@ -11369,7 +11464,7 @@ async function main() {
|
|
|
11369
11464
|
await runDoctor(config, options);
|
|
11370
11465
|
await maybeNotifyUpdate(config, { dryRun: options.dryRun === true });
|
|
11371
11466
|
});
|
|
11372
|
-
program2.command("install").description("Install OpenViking, local config files, command shim, and user-level agent instructions").option("--dry-run", "Print the actions without making changes").option("--no-start", "Do not start OpenViking or check server health after installing").option("--package-manager <manager>", "uv, pipx, or pip", parsePackageManager).option(
|
|
11467
|
+
program2.command("install").description("Install OpenViking, local config files, command shim, and user-level agent instructions").option("--dry-run", "Print the actions without making changes").option("--force", "Reinstall OpenViking at the pinned version even if a working install is already present").option("--no-start", "Do not start OpenViking or check server health after installing").option("--package-manager <manager>", "uv, pipx, or pip", parsePackageManager).option(
|
|
11373
11468
|
"--with-hooks",
|
|
11374
11469
|
"Also install agent-side hooks (Claude PreCompact + SessionStart) for deterministic handoff snapshots and context preload"
|
|
11375
11470
|
).action(async (options) => {
|