@nurix/nustack 0.24.0 → 0.25.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/commands/desktopAgent.js +1 -1
- package/dist/commands/doctor.js +8 -11
- package/dist/commands/obligationList.js +23 -49
- package/dist/commands/records.js +6 -88
- package/dist/commands/store.js +199 -0
- package/dist/commands/sync.js +2 -2
- package/dist/commands/toolkit.js +2 -2
- package/dist/lib/context/register.js +1 -1
- package/dist/lib/desktop-agents/checkout.js +1 -1
- package/dist/lib/desktop-agents/claudeRun.js +4 -4
- package/dist/lib/etna/reconcile.js +4 -4
- package/dist/lib/graph/library.js +1 -1
- package/dist/lib/harness/hookCheck.js +2 -8
- package/dist/lib/harness/storeWaivers.js +1 -4
- package/dist/lib/local_store/database.js +12 -92
- package/dist/lib/local_store/device_state.js +2 -2
- package/dist/lib/local_store/index.js +2 -2
- package/dist/lib/local_store/paths.js +0 -6
- package/dist/lib/local_store/routing.js +8 -44
- package/dist/lib/onboarding/folder_state.js +2 -2
- package/dist/lib/onboarding/init_fresh_folder.js +1 -1
- package/dist/lib/onboarding/manifest_writer.js +6 -6
- package/dist/lib/origin_guard.js +1 -1
- package/dist/lib/project_context.js +5 -5
- package/dist/lib/project_contract.js +2 -2
- package/dist/lib/records/drain.js +3 -153
- package/dist/lib/records/kinds.js +16 -0
- package/dist/lib/records/recordInput.js +1 -1
- package/dist/lib/records/recordsTable.js +119 -161
- package/dist/lib/records/refusalLog.js +7 -7
- package/dist/lib/sessions/brief.js +1 -1
- package/dist/lib/sessions/sessionEvents.js +5 -49
- package/dist/lib/store/open.js +136 -0
- package/dist/lib/store/wipe.js +34 -0
- package/dist/lib/sync/apply.js +113 -0
- package/dist/lib/sync/client.js +327 -0
- package/dist/lib/sync/endpoint.js +26 -0
- package/dist/lib/sync/manifest.js +80 -0
- package/dist/lib/sync/outbox.js +104 -0
- package/dist/machine.sql +934 -0
- package/dist/program.js +8 -6
- package/dist/records.schema.json +4 -0
- package/dist/spec_schema.json +5 -5
- package/dist/vendor/harness/rules/docsRoot.js +2 -2
- package/dist/vendor/harness/rules/index.js +1 -1
- package/package.json +5 -5
- package/dist/lib/local_store/migrations.js +0 -399
- package/dist/lib/records/door.js +0 -79
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { sharedTable } from "./manifest.js";
|
|
2
|
+
const RETRY_BASE_MS = 30_000;
|
|
3
|
+
const RETRY_CEILING_MS = 60 * 60 * 1000;
|
|
4
|
+
export function retryDelayMs(attempts) {
|
|
5
|
+
return Math.min(RETRY_BASE_MS * 2 ** Math.max(0, attempts - 1), RETRY_CEILING_MS);
|
|
6
|
+
}
|
|
7
|
+
function entryOf(row) {
|
|
8
|
+
let changed = [];
|
|
9
|
+
try {
|
|
10
|
+
changed = JSON.parse(String(row.changed_json));
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
changed = [];
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
tableName: row.table_name,
|
|
17
|
+
rowKey: row.row_key,
|
|
18
|
+
op: row.op,
|
|
19
|
+
baseVersion: Number(row.base_version),
|
|
20
|
+
changed,
|
|
21
|
+
enqueuedAt: Number(row.enqueued_at),
|
|
22
|
+
attempts: Number(row.attempts),
|
|
23
|
+
nextAttemptAt: row.next_attempt_at ?? null,
|
|
24
|
+
lastError: row.last_error ?? null,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function selectDueEntries(db, now) {
|
|
28
|
+
const rows = db
|
|
29
|
+
.prepare("SELECT * FROM sync_outbox WHERE next_attempt_at IS NULL OR next_attempt_at <= ? ORDER BY enqueued_at, rowid")
|
|
30
|
+
.all(now);
|
|
31
|
+
return rows.map(entryOf);
|
|
32
|
+
}
|
|
33
|
+
export function entryFor(db, tableName, rowKey) {
|
|
34
|
+
const row = db.prepare("SELECT * FROM sync_outbox WHERE table_name = ? AND row_key = ?").get(tableName, rowKey);
|
|
35
|
+
return row ? entryOf(row) : null;
|
|
36
|
+
}
|
|
37
|
+
export function hasDueEntry(db, now, canSendDevice) {
|
|
38
|
+
return (db
|
|
39
|
+
.prepare(`SELECT 1 FROM sync_outbox o WHERE (o.next_attempt_at IS NULL OR o.next_attempt_at <= ?)
|
|
40
|
+
AND (? OR NOT EXISTS (SELECT 1 FROM harness_records r
|
|
41
|
+
WHERE o.table_name = 'harness_records' AND r.id = o.row_key AND r.written_by_kind = 'agent'))
|
|
42
|
+
LIMIT 1`)
|
|
43
|
+
.get(now, canSendDevice ? 1 : 0) !== undefined);
|
|
44
|
+
}
|
|
45
|
+
export function currentRow(db, tableName, rowKey) {
|
|
46
|
+
const table = sharedTable(tableName);
|
|
47
|
+
if (!table)
|
|
48
|
+
return null;
|
|
49
|
+
const row = db.prepare(`SELECT * FROM "${table.name}" WHERE "${table.key}" = ?`).get(rowKey);
|
|
50
|
+
return row ?? null;
|
|
51
|
+
}
|
|
52
|
+
export function deltaOf(db, entry) {
|
|
53
|
+
const table = sharedTable(entry.tableName);
|
|
54
|
+
if (!table?.machineColumns)
|
|
55
|
+
return null;
|
|
56
|
+
if (entry.op === "delete")
|
|
57
|
+
return { table: entry.tableName, key: entry.rowKey, baseVersion: entry.baseVersion, op: "delete", columns: {} };
|
|
58
|
+
const row = currentRow(db, entry.tableName, entry.rowKey);
|
|
59
|
+
if (!row)
|
|
60
|
+
return null;
|
|
61
|
+
const isUnacknowledged = row.sync_seq === null || row.sync_seq === undefined;
|
|
62
|
+
const allowed = new Set(table.machineColumns);
|
|
63
|
+
const names = isUnacknowledged ? table.machineColumns : entry.changed;
|
|
64
|
+
const columns = {};
|
|
65
|
+
for (const name of names)
|
|
66
|
+
if (allowed.has(name) && Object.hasOwn(row, name))
|
|
67
|
+
columns[name] = row[name] ?? null;
|
|
68
|
+
return { table: entry.tableName, key: entry.rowKey, baseVersion: isUnacknowledged ? 0 : entry.baseVersion, op: "upsert", columns };
|
|
69
|
+
}
|
|
70
|
+
const CAS = "table_name = :table_name AND row_key = :row_key AND enqueued_at = :enqueued_at";
|
|
71
|
+
function casOf(entry) {
|
|
72
|
+
return { table_name: entry.tableName, row_key: entry.rowKey, enqueued_at: entry.enqueuedAt };
|
|
73
|
+
}
|
|
74
|
+
export function settleEntry(db, entry) {
|
|
75
|
+
return Number(db.prepare(`DELETE FROM sync_outbox WHERE ${CAS}`).run(casOf(entry)).changes) > 0;
|
|
76
|
+
}
|
|
77
|
+
export function backoffEntry(db, entry, error, now) {
|
|
78
|
+
db.prepare(`UPDATE sync_outbox SET attempts = attempts + 1, next_attempt_at = :next_attempt_at, last_error = :last_error WHERE ${CAS}`).run({
|
|
79
|
+
...casOf(entry),
|
|
80
|
+
next_attempt_at: now + retryDelayMs(entry.attempts + 1),
|
|
81
|
+
last_error: error,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
export function deferEntry(db, entry, now) {
|
|
85
|
+
db.prepare(`UPDATE sync_outbox SET next_attempt_at = :next_attempt_at WHERE ${CAS}`).run({
|
|
86
|
+
...casOf(entry),
|
|
87
|
+
next_attempt_at: now + retryDelayMs(Math.max(1, entry.attempts)),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
export function dropEntry(db, tableName, rowKey) {
|
|
91
|
+
db.prepare("DELETE FROM sync_outbox WHERE table_name = ? AND row_key = ?").run(tableName, rowKey);
|
|
92
|
+
}
|
|
93
|
+
export function countOutbox(db, now) {
|
|
94
|
+
return db
|
|
95
|
+
.prepare(`SELECT table_name AS "table", count(*) AS pending,
|
|
96
|
+
sum(CASE WHEN next_attempt_at IS NULL OR next_attempt_at <= ? THEN 1 ELSE 0 END) AS due,
|
|
97
|
+
sum(CASE WHEN last_error IS NOT NULL THEN 1 ELSE 0 END) AS failing
|
|
98
|
+
FROM sync_outbox GROUP BY table_name ORDER BY table_name`)
|
|
99
|
+
.all(now).map((row) => ({ table: row.table, pending: Number(row.pending), due: Number(row.due), failing: Number(row.failing) }));
|
|
100
|
+
}
|
|
101
|
+
export function newestOutboxError(db) {
|
|
102
|
+
const row = db.prepare("SELECT last_error FROM sync_outbox WHERE last_error IS NOT NULL ORDER BY enqueued_at DESC LIMIT 1").get();
|
|
103
|
+
return row?.last_error ?? null;
|
|
104
|
+
}
|