otacon 0.1.0
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/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/cli/client.js +188 -0
- package/dist/cli/client.js.map +1 -0
- package/dist/cli/commands/answer.js +63 -0
- package/dist/cli/commands/answer.js.map +1 -0
- package/dist/cli/commands/ask.js +117 -0
- package/dist/cli/commands/ask.js.map +1 -0
- package/dist/cli/commands/clean.js +48 -0
- package/dist/cli/commands/clean.js.map +1 -0
- package/dist/cli/commands/doctor.js +86 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/expose.js +104 -0
- package/dist/cli/commands/expose.js.map +1 -0
- package/dist/cli/commands/implement-done.js +53 -0
- package/dist/cli/commands/implement-done.js.map +1 -0
- package/dist/cli/commands/install.js +113 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/open.js +37 -0
- package/dist/cli/commands/open.js.map +1 -0
- package/dist/cli/commands/progress.js +45 -0
- package/dist/cli/commands/progress.js.map +1 -0
- package/dist/cli/commands/start.js +66 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.js +44 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/submit.js +64 -0
- package/dist/cli/commands/submit.js.map +1 -0
- package/dist/cli/commands/wait.js +66 -0
- package/dist/cli/commands/wait.js.map +1 -0
- package/dist/cli/install/assets.js +285 -0
- package/dist/cli/install/assets.js.map +1 -0
- package/dist/cli/install/locations.js +92 -0
- package/dist/cli/install/locations.js.map +1 -0
- package/dist/cli/install/tailscale.js +39 -0
- package/dist/cli/install/tailscale.js.map +1 -0
- package/dist/cli/main.js +73 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.js +39 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/session.js +77 -0
- package/dist/cli/session.js.map +1 -0
- package/dist/daemon/activity.js +56 -0
- package/dist/daemon/activity.js.map +1 -0
- package/dist/daemon/anchor.js +143 -0
- package/dist/daemon/anchor.js.map +1 -0
- package/dist/daemon/app.js +1081 -0
- package/dist/daemon/app.js.map +1 -0
- package/dist/daemon/approve.js +71 -0
- package/dist/daemon/approve.js.map +1 -0
- package/dist/daemon/desktop-notify.js +69 -0
- package/dist/daemon/desktop-notify.js.map +1 -0
- package/dist/daemon/diff.js +187 -0
- package/dist/daemon/diff.js.map +1 -0
- package/dist/daemon/linter/index.js +19 -0
- package/dist/daemon/linter/index.js.map +1 -0
- package/dist/daemon/linter/parse.js +350 -0
- package/dist/daemon/linter/parse.js.map +1 -0
- package/dist/daemon/linter/rules.js +359 -0
- package/dist/daemon/linter/rules.js.map +1 -0
- package/dist/daemon/main.js +48 -0
- package/dist/daemon/main.js.map +1 -0
- package/dist/daemon/notify.js +23 -0
- package/dist/daemon/notify.js.map +1 -0
- package/dist/daemon/presence.js +37 -0
- package/dist/daemon/presence.js.map +1 -0
- package/dist/daemon/queue.js +160 -0
- package/dist/daemon/queue.js.map +1 -0
- package/dist/daemon/store.js +393 -0
- package/dist/daemon/store.js.map +1 -0
- package/dist/daemon/threads.js +153 -0
- package/dist/daemon/threads.js.map +1 -0
- package/dist/daemon/transcript.js +89 -0
- package/dist/daemon/transcript.js.map +1 -0
- package/dist/daemon/ui.js +175 -0
- package/dist/daemon/ui.js.map +1 -0
- package/dist/shared/config.js +93 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/gwt.js +69 -0
- package/dist/shared/gwt.js.map +1 -0
- package/dist/shared/paths.js +67 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/question-spec.js +44 -0
- package/dist/shared/question-spec.js.map +1 -0
- package/dist/shared/types.js +35 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/shared/version.js +5 -0
- package/dist/shared/version.js.map +1 -0
- package/dist/ui/assets/arc-HhPfdCPZ.js +1 -0
- package/dist/ui/assets/architecture-7EHR7CIX-BPLblcyi.js +1 -0
- package/dist/ui/assets/architectureDiagram-3BPJPVTR-D2PIxGOb.js +36 -0
- package/dist/ui/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/assets/blockDiagram-GPEHLZMM-DQ3Dn17h.js +132 -0
- package/dist/ui/assets/c4Diagram-AAUBKEIU-DxITrQgS.js +10 -0
- package/dist/ui/assets/channel-ipcU8ZNI.js +1 -0
- package/dist/ui/assets/chunk-2J33WTMH-Du1JoPx5.js +1 -0
- package/dist/ui/assets/chunk-3OPIFGDE-Dn7x2Yqf.js +62 -0
- package/dist/ui/assets/chunk-4BX2VUAB-DVnrE-4n.js +1 -0
- package/dist/ui/assets/chunk-55IACEB6-BAhFAimA.js +1 -0
- package/dist/ui/assets/chunk-5ZQYHXKU-0hEZptem.js +2 -0
- package/dist/ui/assets/chunk-727SXJPM-C1FN_cI3.js +206 -0
- package/dist/ui/assets/chunk-AQP2D5EJ-A656OBd4.js +231 -0
- package/dist/ui/assets/chunk-BSJP7CBP-D8oMbjm8.js +1 -0
- package/dist/ui/assets/chunk-CSCIHK7Q-DjIL8GLi.js +122 -0
- package/dist/ui/assets/chunk-FMBD7UC4-Otblfqvz.js +15 -0
- package/dist/ui/assets/chunk-KSCS5N6A-BOjTvm3H.js +10 -0
- package/dist/ui/assets/chunk-L5ZTLDWV-CaTLaw6L.js +1 -0
- package/dist/ui/assets/chunk-LZXEDZCA-Dq5p7qrD.js +2 -0
- package/dist/ui/assets/chunk-ND2GUHAM-jZ_NNnWi.js +1 -0
- package/dist/ui/assets/chunk-NNHCCRGN-DlpIbxXb.js +159 -0
- package/dist/ui/assets/chunk-NZK2D7GU-U_7l_sCh.js +1 -0
- package/dist/ui/assets/chunk-O5CBEL6O-MewqqNB7.js +70 -0
- package/dist/ui/assets/chunk-QZHKN3VN-DzGPH44B.js +1 -0
- package/dist/ui/assets/chunk-WU5MYG2G-DyEIVjoo.js +1 -0
- package/dist/ui/assets/chunk-XPW4576I-D5ArxNEF.js +32 -0
- package/dist/ui/assets/classDiagram-4FO5ZUOK-Byg2Hl9D.js +1 -0
- package/dist/ui/assets/classDiagram-v2-Q7XG4LA2-Byg2Hl9D.js +1 -0
- package/dist/ui/assets/cose-bilkent-S5V4N54A-PFXzf7WV.js +1 -0
- package/dist/ui/assets/cytoscape.esm-h6BdjjI9.js +321 -0
- package/dist/ui/assets/dagre-BM42HDAG-xrCfjZuZ.js +4 -0
- package/dist/ui/assets/dagre-Bx709z4p.js +1 -0
- package/dist/ui/assets/defaultLocale-C8Fc0cco.js +1 -0
- package/dist/ui/assets/diagram-2AECGRRQ-BFf-cyKY.js +43 -0
- package/dist/ui/assets/diagram-5GNKFQAL-kNPV4NfV.js +10 -0
- package/dist/ui/assets/diagram-KO2AKTUF-ByC1IUwG.js +3 -0
- package/dist/ui/assets/diagram-LMA3HP47-DZIJMPK0.js +24 -0
- package/dist/ui/assets/diagram-OG6HWLK6-CSDED9A-.js +24 -0
- package/dist/ui/assets/dist-YwjsDswi.js +1 -0
- package/dist/ui/assets/erDiagram-TEJ5UH35-yuzvjE6J.js +85 -0
- package/dist/ui/assets/eventmodeling-FCH6USID-CZR4eNG-.js +1 -0
- package/dist/ui/assets/flowDiagram-I6XJVG4X-ApPtVyYM.js +162 -0
- package/dist/ui/assets/ganttDiagram-6RSMTGT7-BeMLXtAr.js +292 -0
- package/dist/ui/assets/gitGraph-WXDBUCRP-JmTTBa7j.js +1 -0
- package/dist/ui/assets/gitGraphDiagram-PVQCEYII-Cjjnjs71.js +106 -0
- package/dist/ui/assets/graphlib-B8gBHxth.js +1 -0
- package/dist/ui/assets/index-BFQVRcSI.js +11 -0
- package/dist/ui/assets/index-Bj_kTrwP.css +1 -0
- package/dist/ui/assets/info-J43DQDTF-8vZ3gome.js +1 -0
- package/dist/ui/assets/infoDiagram-5YYISTIA-CnMk1cA-.js +2 -0
- package/dist/ui/assets/init-D6jRqBbL.js +1 -0
- package/dist/ui/assets/ishikawaDiagram-YF4QCWOH-Bl8z6huD.js +70 -0
- package/dist/ui/assets/journeyDiagram-JHISSGLW-DYIVfMpS.js +139 -0
- package/dist/ui/assets/kanban-definition-UN3LZRKU-BnR0ZzOz.js +89 -0
- package/dist/ui/assets/katex-Vhh-h91d.js +257 -0
- package/dist/ui/assets/line-DcBdQit6.js +1 -0
- package/dist/ui/assets/linear-HKjRHFAO.js +1 -0
- package/dist/ui/assets/mermaid-parser.core-DkYXrPlA.js +4 -0
- package/dist/ui/assets/mermaid.core-BmkfCI3b.js +9 -0
- package/dist/ui/assets/mindmap-definition-RKZ34NQL-sIAd4nDi.js +96 -0
- package/dist/ui/assets/ordinal-hYBb2elL.js +1 -0
- package/dist/ui/assets/otacon-DPXGiaVj.svg +11 -0
- package/dist/ui/assets/packet-YPE3B663-BxbxcfXN.js +1 -0
- package/dist/ui/assets/path-BWPyau1x.js +1 -0
- package/dist/ui/assets/pie-LRSECV5Y-BJxazjNs.js +1 -0
- package/dist/ui/assets/pieDiagram-4H26LBE5-BiOhc9GR.js +30 -0
- package/dist/ui/assets/plan-view-CH6NzUDb.js +74 -0
- package/dist/ui/assets/purify.es-CDvCXckx.js +3 -0
- package/dist/ui/assets/quadrantDiagram-W4KKPZXB-CVyHbWgo.js +7 -0
- package/dist/ui/assets/radar-GUYGQ44K-D9ohbnbV.js +1 -0
- package/dist/ui/assets/requirementDiagram-4Y6WPE33-Ba24_hqc.js +84 -0
- package/dist/ui/assets/rough.esm-CSKSodPl.js +1 -0
- package/dist/ui/assets/sankeyDiagram-5OEKKPKP-CxD4wiPL.js +40 -0
- package/dist/ui/assets/sequenceDiagram-3UESZ5HK-7qA7lD61.js +162 -0
- package/dist/ui/assets/src-IM8AE8MK.js +1 -0
- package/dist/ui/assets/stateDiagram-AJRCARHV-DNElRCuH.js +1 -0
- package/dist/ui/assets/stateDiagram-v2-BHNVJYJU-D6qTYpY3.js +1 -0
- package/dist/ui/assets/timeline-definition-PNZ67QCA-ChYC4Grd.js +120 -0
- package/dist/ui/assets/treeView-BLDUP644-Il0KnMi_.js +1 -0
- package/dist/ui/assets/treemap-LRROVOQU-CIiKcdRo.js +1 -0
- package/dist/ui/assets/vennDiagram-CIIHVFJN-Ulhkum9i.js +34 -0
- package/dist/ui/assets/wardley-L42UT6IY-BNd4ljz7.js +1 -0
- package/dist/ui/assets/wardleyDiagram-YWT4CUSO-BicXxh84.js +78 -0
- package/dist/ui/assets/xychartDiagram-2RQKCTM6-Duf-m_th.js +7 -0
- package/dist/ui/index.html +20 -0
- package/package.json +66 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Per-session disk-backed FIFO event queue with waiter parking (DESIGN.md §6, §7).
|
|
2
|
+
//
|
|
3
|
+
// Every method is synchronous: Node's single thread therefore cannot interleave
|
|
4
|
+
// an enqueue between a queue-empty check and a waiter parking — the whole
|
|
5
|
+
// no-lost-wakeup argument (DECISIONS.md "SessionQueue: synchronous methods only").
|
|
6
|
+
//
|
|
7
|
+
// Delivery is at-least-once (DECISIONS.md "Event delivery: at-least-once"):
|
|
8
|
+
// take() and waiter wake-ups move the event to an in-flight list — still
|
|
9
|
+
// persisted by every flush — and the caller acks it with flush(event) only
|
|
10
|
+
// after the response goes out. A crash inside that window re-delivers the event
|
|
11
|
+
// on the next daemon start instead of losing it, even if other enqueues flushed
|
|
12
|
+
// in between. An event seq is minted by the caller (Store.bumpCounter
|
|
13
|
+
// "eventSeq") so seqs survive queue drains.
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { quarantineCorruptFile, stringify, writeFileAtomic } from "./store.js";
|
|
16
|
+
export class SessionQueue {
|
|
17
|
+
filePath;
|
|
18
|
+
events = [];
|
|
19
|
+
/** Dequeued (taken or delivered to a waiter) but not yet acked via flush(event). */
|
|
20
|
+
inFlight = [];
|
|
21
|
+
waiters = [];
|
|
22
|
+
closed = false;
|
|
23
|
+
constructor(filePath) {
|
|
24
|
+
this.filePath = filePath;
|
|
25
|
+
if (!existsSync(filePath))
|
|
26
|
+
return;
|
|
27
|
+
let raw;
|
|
28
|
+
try {
|
|
29
|
+
raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
raw = undefined;
|
|
33
|
+
}
|
|
34
|
+
const file = raw;
|
|
35
|
+
if (file?.version !== 1 || !Array.isArray(file.events)) {
|
|
36
|
+
// Quarantine, never wedge (DECISIONS.md "Corrupt state files are
|
|
37
|
+
// quarantined, not fatal"): pending events are preserved in the
|
|
38
|
+
// quarantined file for manual recovery, and the session keeps working
|
|
39
|
+
// instead of every events call throwing forever.
|
|
40
|
+
quarantineCorruptFile(filePath, "events queue");
|
|
41
|
+
this.flush(); // re-seed an empty file so the next instance reads clean
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.events = file.events;
|
|
45
|
+
}
|
|
46
|
+
/** Undelivered events currently queued in memory. */
|
|
47
|
+
get size() {
|
|
48
|
+
return this.events.length;
|
|
49
|
+
}
|
|
50
|
+
/** Delivered events awaiting their post-response flush(event) ack. */
|
|
51
|
+
get inFlightCount() {
|
|
52
|
+
return this.inFlight.length;
|
|
53
|
+
}
|
|
54
|
+
get waiterCount() {
|
|
55
|
+
return this.waiters.length;
|
|
56
|
+
}
|
|
57
|
+
/** Durably append (flushes before any waiter sees it), then wake the first waiter. */
|
|
58
|
+
enqueue(payload, seq) {
|
|
59
|
+
const event = { seq, queuedAt: new Date().toISOString(), payload };
|
|
60
|
+
this.events.push(event);
|
|
61
|
+
this.flush();
|
|
62
|
+
this.dispatch();
|
|
63
|
+
return event;
|
|
64
|
+
}
|
|
65
|
+
/** Fast path: dequeue into the in-flight list. Respond, then flush(event) to ack. */
|
|
66
|
+
take() {
|
|
67
|
+
const event = this.events.shift();
|
|
68
|
+
if (event)
|
|
69
|
+
this.inFlight.push(event);
|
|
70
|
+
return event;
|
|
71
|
+
}
|
|
72
|
+
/** Park a waiter, FIFO behind earlier ones; wakes immediately if events are queued. */
|
|
73
|
+
park(waiter) {
|
|
74
|
+
const entry = { waiter };
|
|
75
|
+
this.waiters.push(entry);
|
|
76
|
+
this.dispatch();
|
|
77
|
+
return {
|
|
78
|
+
cancel: () => {
|
|
79
|
+
const index = this.waiters.indexOf(entry);
|
|
80
|
+
if (index !== -1)
|
|
81
|
+
this.waiters.splice(index, 1);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Detach from disk forever (DELETE /api/sessions/:id evicted this instance):
|
|
87
|
+
* flush and requeue become no-ops. A delivered-but-unacked event's
|
|
88
|
+
* post-response ack callback can fire after `otacon clean` archives the
|
|
89
|
+
* session dir — writing then would recreate .otacon/<id>/ next to the
|
|
90
|
+
* archive (writeFileAtomic mkdirs). The events file leaves with the dir.
|
|
91
|
+
*/
|
|
92
|
+
close() {
|
|
93
|
+
this.closed = true;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Detach from disk AND wake every parked waiter with one terminal event
|
|
97
|
+
* (DELETE of a *pending* session, DESIGN.md §12): set `closed` first so
|
|
98
|
+
* flush/requeue/dispatch all become no-ops — the synthetic event is never
|
|
99
|
+
* persisted or requeued, and a late post-response ack can't recreate the
|
|
100
|
+
* `.otacon/<id>/` dir the caller is about to hard-remove — then hand each
|
|
101
|
+
* parked waiter the same payload so its long-poll resolves cleanly instead of
|
|
102
|
+
* 404ing on the next call. No waiter parked = nobody to wake (an agent that
|
|
103
|
+
* is not parked discovers the delete via its next call's 404). The synthetic
|
|
104
|
+
* seq is -1: it never touches disk or the eventSeq counter.
|
|
105
|
+
*/
|
|
106
|
+
closeWith(payload) {
|
|
107
|
+
this.close(); // share the detach-from-disk semantics — closed first, see above
|
|
108
|
+
const event = { seq: -1, queuedAt: new Date().toISOString(), payload };
|
|
109
|
+
for (const { waiter } of this.waiters.splice(0))
|
|
110
|
+
waiter(event);
|
|
111
|
+
}
|
|
112
|
+
/** Return an undeliverable event (e.g. wait aborted after wake) to the head, durably. */
|
|
113
|
+
requeue(event) {
|
|
114
|
+
if (this.closed)
|
|
115
|
+
return;
|
|
116
|
+
this.dropInFlight(event);
|
|
117
|
+
this.events.unshift(event);
|
|
118
|
+
this.flush();
|
|
119
|
+
this.dispatch();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Persist the queue: in-flight events first (they were dequeued earlier, so
|
|
123
|
+
* FIFO order survives a restart), then the queued tail. Pass the event a
|
|
124
|
+
* response just went out for to ack it — only an ack removes it from disk.
|
|
125
|
+
*/
|
|
126
|
+
flush(acked) {
|
|
127
|
+
if (this.closed)
|
|
128
|
+
return;
|
|
129
|
+
if (acked)
|
|
130
|
+
this.dropInFlight(acked);
|
|
131
|
+
const file = { version: 1, events: [...this.inFlight, ...this.events] };
|
|
132
|
+
writeFileAtomic(this.filePath, stringify(file));
|
|
133
|
+
}
|
|
134
|
+
dropInFlight(event) {
|
|
135
|
+
const index = this.inFlight.indexOf(event);
|
|
136
|
+
if (index !== -1)
|
|
137
|
+
this.inFlight.splice(index, 1);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Pair queued events with parked waiters, FIFO on both sides, one event each.
|
|
141
|
+
* A waiter that throws gets its event back at the head (still durable) so the
|
|
142
|
+
* next dispatch can deliver it; the throw propagates to the caller.
|
|
143
|
+
*/
|
|
144
|
+
dispatch() {
|
|
145
|
+
while (this.waiters.length > 0 && this.events.length > 0) {
|
|
146
|
+
const entry = this.waiters.shift();
|
|
147
|
+
const event = this.events.shift();
|
|
148
|
+
this.inFlight.push(event);
|
|
149
|
+
try {
|
|
150
|
+
entry.waiter(event);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
this.dropInFlight(event);
|
|
154
|
+
this.events.unshift(event);
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/daemon/queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
// Global session registry, per-session daemon state, and revision snapshots
|
|
2
|
+
// (DESIGN.md §7, §12). All state is plain JSON written atomically — temp file +
|
|
3
|
+
// rename — so a crash can never leave a half-written file (DECISIONS.md
|
|
4
|
+
// "Storage: plain JSON files, not SQLite"). A file that is corrupt anyway
|
|
5
|
+
// (manual edit, disk fault) is quarantined, never fatal: it is renamed aside
|
|
6
|
+
// and the daemon continues with a fresh structure (DECISIONS.md "Corrupt state
|
|
7
|
+
// files are quarantined, not fatal") — a permanently wedged daemon is worse
|
|
8
|
+
// than one recoverable file.
|
|
9
|
+
import { randomBytes } from "node:crypto";
|
|
10
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import * as paths from "../shared/paths.js";
|
|
13
|
+
let tmpSerial = 0;
|
|
14
|
+
let quarantineSerial = 0;
|
|
15
|
+
/** Atomic write: temp file in the destination directory (created if needed) + rename. */
|
|
16
|
+
export function writeFileAtomic(path, data) {
|
|
17
|
+
const dir = dirname(path);
|
|
18
|
+
mkdirSync(dir, { recursive: true });
|
|
19
|
+
const tmp = join(dir, `.tmp-${process.pid}-${tmpSerial++}`);
|
|
20
|
+
writeFileSync(tmp, data);
|
|
21
|
+
renameSync(tmp, path);
|
|
22
|
+
}
|
|
23
|
+
/** Canonical on-disk JSON formatting for all otacon state files. */
|
|
24
|
+
export function stringify(value) {
|
|
25
|
+
return JSON.stringify(value, null, 2) + "\n";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Move a corrupt state file aside to `<name>.corrupt-<timestamp>` (atomic
|
|
29
|
+
* rename), log to stderr, and let the caller continue with a fresh structure.
|
|
30
|
+
* The serial suffix keeps two quarantines in the same millisecond from
|
|
31
|
+
* overwriting each other.
|
|
32
|
+
*/
|
|
33
|
+
export function quarantineCorruptFile(path, what) {
|
|
34
|
+
const aside = `${path}.corrupt-${Date.now()}-${quarantineSerial++}`;
|
|
35
|
+
try {
|
|
36
|
+
renameSync(path, aside);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// The file vanished (deleted out from under us mid-recovery) or the rename
|
|
40
|
+
// itself failed. Quarantine exists to keep the daemon alive — moving the
|
|
41
|
+
// evidence aside must never become the new fatal path; the caller's fresh
|
|
42
|
+
// rebuild overwrites whatever is (or is not) left at `path`.
|
|
43
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44
|
+
process.stderr.write(`otacond: ${what} at ${path} is corrupt and could not be moved aside (${message}); continuing with fresh state\n`);
|
|
45
|
+
return aside;
|
|
46
|
+
}
|
|
47
|
+
process.stderr.write(`otacond: ${what} at ${path} is corrupt; moved it to ${aside} and continuing with fresh state\n`);
|
|
48
|
+
return aside;
|
|
49
|
+
}
|
|
50
|
+
export function readJsonOr(path) {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined; // unreadable or not JSON — caller quarantines
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function parseRegistry(raw) {
|
|
59
|
+
const file = raw;
|
|
60
|
+
const valid = typeof file === "object" &&
|
|
61
|
+
file !== null &&
|
|
62
|
+
file.version === 1 &&
|
|
63
|
+
typeof file.sessions === "object" &&
|
|
64
|
+
file.sessions !== null;
|
|
65
|
+
return valid ? file : undefined;
|
|
66
|
+
}
|
|
67
|
+
function parseState(raw) {
|
|
68
|
+
const file = raw;
|
|
69
|
+
const counters = file?.counters;
|
|
70
|
+
const valid = typeof file === "object" &&
|
|
71
|
+
file !== null &&
|
|
72
|
+
typeof file.id === "string" &&
|
|
73
|
+
typeof file.revision === "number" &&
|
|
74
|
+
typeof counters === "object" &&
|
|
75
|
+
counters !== null &&
|
|
76
|
+
typeof counters.batch === "number" &&
|
|
77
|
+
typeof counters.thread === "number" &&
|
|
78
|
+
typeof counters.question === "number" &&
|
|
79
|
+
typeof counters.eventSeq === "number";
|
|
80
|
+
if (!valid)
|
|
81
|
+
return undefined;
|
|
82
|
+
// Pre-M3 state files lack the field; a hand-edited/restored file may carry a
|
|
83
|
+
// non-integer or a value beyond the current revision, which would poison the
|
|
84
|
+
// diff endpoint's default baseline (400 on a parameterless GET, or a 500 via
|
|
85
|
+
// readRevision(1.5)). Defaulting/clamping beats quarantining either way.
|
|
86
|
+
const reviewed = file.lastReviewedRevision;
|
|
87
|
+
file.lastReviewedRevision =
|
|
88
|
+
typeof reviewed === "number" && Number.isInteger(reviewed) && reviewed > 0
|
|
89
|
+
? Math.min(reviewed, file.revision)
|
|
90
|
+
: 0;
|
|
91
|
+
return file;
|
|
92
|
+
}
|
|
93
|
+
/** Highest r<N>.md snapshot on disk — the revision counter's source of truth. */
|
|
94
|
+
function recoverRevision(repo, id) {
|
|
95
|
+
let max = 0;
|
|
96
|
+
try {
|
|
97
|
+
for (const name of readdirSync(paths.sessionDir(repo, id))) {
|
|
98
|
+
const match = /^r(\d+)\.md$/.exec(name);
|
|
99
|
+
if (match)
|
|
100
|
+
max = Math.max(max, Number(match[1]));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// no session dir yet — revision 0
|
|
105
|
+
}
|
|
106
|
+
return max;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* High-water scan of threads.json and events.json so rebuilt counters never
|
|
110
|
+
* re-mint a live id (DECISIONS.md "Quarantine counter recovery") — duplicate
|
|
111
|
+
* thread ids would cross-wire resolutions now that threads carry state. Reads
|
|
112
|
+
* loosely (no thread validation): a half-corrupt file should still surrender
|
|
113
|
+
* every id it can.
|
|
114
|
+
*/
|
|
115
|
+
function recoverCounters(repo, id) {
|
|
116
|
+
const counters = { batch: 0, thread: 0, question: 0, eventSeq: 0 };
|
|
117
|
+
const see = (key, raw, re) => {
|
|
118
|
+
const match = typeof raw === "string" ? re.exec(raw) : null;
|
|
119
|
+
if (match)
|
|
120
|
+
counters[key] = Math.max(counters[key], Number(match[1]));
|
|
121
|
+
};
|
|
122
|
+
const threadsRaw = readJsonOr(paths.threadsPath(repo, id));
|
|
123
|
+
for (const t of Array.isArray(threadsRaw?.threads) ? threadsRaw.threads : []) {
|
|
124
|
+
const thread = t;
|
|
125
|
+
see("thread", thread?.id, /^t(\d+)$/);
|
|
126
|
+
see("question", thread?.id, /^q(\d+)$/);
|
|
127
|
+
see("batch", thread?.batch, /^b(\d+)$/);
|
|
128
|
+
}
|
|
129
|
+
// Agent grill questions share the q counter with user-question threads.
|
|
130
|
+
const transcriptRaw = readJsonOr(paths.transcriptPath(repo, id));
|
|
131
|
+
for (const e of Array.isArray(transcriptRaw?.entries) ? transcriptRaw.entries : []) {
|
|
132
|
+
see("question", e?.id, /^q(\d+)$/);
|
|
133
|
+
}
|
|
134
|
+
const eventsRaw = readJsonOr(paths.eventsPath(repo, id));
|
|
135
|
+
for (const e of Array.isArray(eventsRaw?.events) ? eventsRaw.events : []) {
|
|
136
|
+
const event = e;
|
|
137
|
+
if (typeof event?.seq === "number") {
|
|
138
|
+
counters.eventSeq = Math.max(counters.eventSeq, event.seq);
|
|
139
|
+
}
|
|
140
|
+
see("batch", event?.payload?.batch, /^b(\d+)$/);
|
|
141
|
+
see("question", event?.payload?.id, /^q(\d+)$/);
|
|
142
|
+
for (const item of Array.isArray(event?.payload?.items) ? event.payload.items : []) {
|
|
143
|
+
see("thread", item?.thread, /^t(\d+)$/);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return counters;
|
|
147
|
+
}
|
|
148
|
+
const BASE36 = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
149
|
+
/**
|
|
150
|
+
* Owns `$OTACON_HOME/registry.json` plus each session's `.otacon/<id>/` state in
|
|
151
|
+
* its repo. Event seqs and the b/t/q stable ids are minted here (bumpCounter)
|
|
152
|
+
* so they survive queue drains and daemon restarts.
|
|
153
|
+
*/
|
|
154
|
+
export class Store {
|
|
155
|
+
registryFile;
|
|
156
|
+
registry;
|
|
157
|
+
constructor() {
|
|
158
|
+
this.registryFile = paths.registryPath();
|
|
159
|
+
if (!existsSync(this.registryFile)) {
|
|
160
|
+
this.registry = { version: 1, sessions: {} };
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const parsed = parseRegistry(readJsonOr(this.registryFile));
|
|
164
|
+
if (parsed) {
|
|
165
|
+
this.registry = parsed;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
// Sessions registered there are forgotten (their .otacon/ dirs survive for
|
|
169
|
+
// manual recovery); the alternative is a daemon that can never boot again.
|
|
170
|
+
quarantineCorruptFile(this.registryFile, "session registry");
|
|
171
|
+
this.registry = { version: 1, sessions: {} };
|
|
172
|
+
this.flushRegistry();
|
|
173
|
+
}
|
|
174
|
+
listSessions() {
|
|
175
|
+
return Object.values(this.registry.sessions).map((s) => ({ ...s }));
|
|
176
|
+
}
|
|
177
|
+
getSession(id) {
|
|
178
|
+
const session = this.registry.sessions[id];
|
|
179
|
+
return session ? { ...session } : undefined;
|
|
180
|
+
}
|
|
181
|
+
createSession(input) {
|
|
182
|
+
const id = this.mintId();
|
|
183
|
+
const now = new Date().toISOString();
|
|
184
|
+
const session = {
|
|
185
|
+
id,
|
|
186
|
+
title: input.title,
|
|
187
|
+
repo: input.repo,
|
|
188
|
+
branch: input.branch ?? "",
|
|
189
|
+
quick: input.quick ?? false,
|
|
190
|
+
status: "draft",
|
|
191
|
+
createdAt: now,
|
|
192
|
+
updatedAt: now,
|
|
193
|
+
};
|
|
194
|
+
// State files first, registry entry last: the registry is the commit point.
|
|
195
|
+
// A crash in between leaves an orphan .otacon/<id>/ dir (harmless), never a
|
|
196
|
+
// registered session whose state files are missing (wedged forever).
|
|
197
|
+
const state = {
|
|
198
|
+
id,
|
|
199
|
+
revision: 0,
|
|
200
|
+
lastReviewedRevision: 0,
|
|
201
|
+
counters: { batch: 0, thread: 0, question: 0, eventSeq: 0 },
|
|
202
|
+
};
|
|
203
|
+
writeFileAtomic(paths.sessionStatePath(input.repo, id), stringify(state));
|
|
204
|
+
writeFileAtomic(paths.eventsPath(input.repo, id), stringify({ version: 1, events: [] }));
|
|
205
|
+
this.registry.sessions[id] = session;
|
|
206
|
+
this.flushRegistry();
|
|
207
|
+
return { ...session };
|
|
208
|
+
}
|
|
209
|
+
updateSession(id, patch) {
|
|
210
|
+
const session = this.require(id);
|
|
211
|
+
Object.assign(session, patch);
|
|
212
|
+
session.updatedAt = new Date().toISOString();
|
|
213
|
+
this.flushRegistry();
|
|
214
|
+
return { ...session };
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Remove a session from the registry (otacon clean, DESIGN.md §12); the
|
|
218
|
+
* .otacon/<id>/ dir in its repo is the CLI's to archive afterwards.
|
|
219
|
+
*/
|
|
220
|
+
deleteSession(id) {
|
|
221
|
+
const session = this.require(id);
|
|
222
|
+
delete this.registry.sessions[id];
|
|
223
|
+
this.flushRegistry();
|
|
224
|
+
return { ...session };
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Hard-remove a session's working dir `.otacon/<id>/` (the UI deleting a
|
|
228
|
+
* *pending* session, DESIGN.md §12): permanent, no archive — for a session
|
|
229
|
+
* with no committed artifact. `repo` is passed explicitly because the caller
|
|
230
|
+
* deregisters first, so require() would already throw. Idempotent: a missing
|
|
231
|
+
* dir is fine (force).
|
|
232
|
+
*/
|
|
233
|
+
removeSessionDir(repo, id) {
|
|
234
|
+
rmSync(paths.sessionDir(repo, id), { recursive: true, force: true });
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Archive a session's working dir `.otacon/<id>/` → `.otacon/archive/<id>/`
|
|
238
|
+
* in its repo (numeric suffix on name collision); returns the destination, or
|
|
239
|
+
* null when there was no dir. The recoverable counterpart to removeSessionDir,
|
|
240
|
+
* for a session whose plan is committed (approved): `otacon clean` and the
|
|
241
|
+
* UI's delete of an approved session both archive through here. `repo` is
|
|
242
|
+
* passed explicitly because the caller deregisters first (require() throws).
|
|
243
|
+
*/
|
|
244
|
+
archiveSessionDir(repo, id) {
|
|
245
|
+
const source = paths.sessionDir(repo, id);
|
|
246
|
+
if (!existsSync(source))
|
|
247
|
+
return null;
|
|
248
|
+
const base = join(paths.otaconDir(repo), "archive");
|
|
249
|
+
mkdirSync(base, { recursive: true });
|
|
250
|
+
let dest = join(base, id);
|
|
251
|
+
for (let n = 2; existsSync(dest); n++)
|
|
252
|
+
dest = join(base, `${id}-${n}`);
|
|
253
|
+
renameSync(source, dest);
|
|
254
|
+
return dest;
|
|
255
|
+
}
|
|
256
|
+
readState(id) {
|
|
257
|
+
const session = this.require(id);
|
|
258
|
+
const path = paths.sessionStatePath(session.repo, id);
|
|
259
|
+
if (existsSync(path)) {
|
|
260
|
+
const parsed = parseState(readJsonOr(path));
|
|
261
|
+
if (parsed && parsed.id === id)
|
|
262
|
+
return parsed;
|
|
263
|
+
quarantineCorruptFile(path, `session state for ${id}`);
|
|
264
|
+
}
|
|
265
|
+
// Rebuild: revision comes from the r<N>.md snapshots (they are the actual
|
|
266
|
+
// plan history — restarting at r1 would overwrite them); counters from a
|
|
267
|
+
// high-water scan of threads.json and events.json, so rebuilt counters
|
|
268
|
+
// cannot mint duplicate live ids (DECISIONS.md "Quarantine counter
|
|
269
|
+
// recovery"). lastReviewedRevision restarts at 0 — the diff baseline
|
|
270
|
+
// degrades to "previous revision", which the user can re-select.
|
|
271
|
+
const state = {
|
|
272
|
+
id,
|
|
273
|
+
revision: recoverRevision(session.repo, id),
|
|
274
|
+
lastReviewedRevision: 0,
|
|
275
|
+
counters: recoverCounters(session.repo, id),
|
|
276
|
+
};
|
|
277
|
+
writeFileAtomic(path, stringify(state));
|
|
278
|
+
return state;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Record that the user has reviewed revision n (a comment-batch flush, or
|
|
282
|
+
* the UI's explicit mark-reviewed). Monotonic: the stored value never moves
|
|
283
|
+
* backwards — older baselines stay reachable via the diff endpoint's ?from=.
|
|
284
|
+
*/
|
|
285
|
+
markReviewed(id, n) {
|
|
286
|
+
const session = this.require(id);
|
|
287
|
+
const state = this.readState(id);
|
|
288
|
+
if (n > state.lastReviewedRevision) {
|
|
289
|
+
state.lastReviewedRevision = Math.min(n, state.revision);
|
|
290
|
+
writeFileAtomic(paths.sessionStatePath(session.repo, id), stringify(state));
|
|
291
|
+
}
|
|
292
|
+
return state.lastReviewedRevision;
|
|
293
|
+
}
|
|
294
|
+
/** Increment one daemon-owned counter (DESIGN.md §6 stable ids) and persist it. */
|
|
295
|
+
bumpCounter(id, key) {
|
|
296
|
+
return this.bumpCounters(id, { [key]: 1 })[key];
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Increment several counters in one read + one atomic write (a comment batch
|
|
300
|
+
* mints N thread ids, a batch id, and an event seq together); returns the
|
|
301
|
+
* updated counter values.
|
|
302
|
+
*/
|
|
303
|
+
bumpCounters(id, by) {
|
|
304
|
+
const session = this.require(id);
|
|
305
|
+
const state = this.readState(id);
|
|
306
|
+
for (const key of Object.keys(by)) {
|
|
307
|
+
state.counters[key] += by[key] ?? 0;
|
|
308
|
+
}
|
|
309
|
+
writeFileAtomic(paths.sessionStatePath(session.repo, id), stringify(state));
|
|
310
|
+
return { ...state.counters };
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Store the next revision snapshot r<N>.md plus the lint warnings it was
|
|
314
|
+
* accepted with (r<N>.warnings.json — the UI's L6 badges; DESIGN.md §12)
|
|
315
|
+
* and the agent's changelog when one accompanied it (r<N>.changelog.md);
|
|
316
|
+
* returns N.
|
|
317
|
+
*/
|
|
318
|
+
saveRevision(id, content, warnings = [], changelog) {
|
|
319
|
+
const session = this.require(id);
|
|
320
|
+
const state = this.readState(id);
|
|
321
|
+
state.revision += 1;
|
|
322
|
+
writeFileAtomic(paths.revisionPath(session.repo, id, state.revision), content);
|
|
323
|
+
writeFileAtomic(paths.revisionWarningsPath(session.repo, id, state.revision), stringify(warnings));
|
|
324
|
+
if (changelog !== undefined && changelog.trim() !== "") {
|
|
325
|
+
writeFileAtomic(paths.revisionChangelogPath(session.repo, id, state.revision), changelog);
|
|
326
|
+
}
|
|
327
|
+
writeFileAtomic(paths.sessionStatePath(session.repo, id), stringify(state));
|
|
328
|
+
session.updatedAt = new Date().toISOString();
|
|
329
|
+
this.flushRegistry();
|
|
330
|
+
return state.revision;
|
|
331
|
+
}
|
|
332
|
+
readRevision(id, n) {
|
|
333
|
+
const session = this.require(id);
|
|
334
|
+
return readFileSync(paths.revisionPath(session.repo, id, n), "utf8");
|
|
335
|
+
}
|
|
336
|
+
/** The changelog submitted with r<n>.md; null when none was (r1, typically). */
|
|
337
|
+
readRevisionChangelog(id, n) {
|
|
338
|
+
const session = this.require(id);
|
|
339
|
+
try {
|
|
340
|
+
return readFileSync(paths.revisionChangelogPath(session.repo, id, n), "utf8");
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Warnings recorded with r<n>.md. Missing or corrupt files read as [] — the
|
|
348
|
+
* badges are presentation metadata, never worth quarantine machinery.
|
|
349
|
+
*/
|
|
350
|
+
readRevisionWarnings(id, n) {
|
|
351
|
+
const session = this.require(id);
|
|
352
|
+
const raw = readJsonOr(paths.revisionWarningsPath(session.repo, id, n));
|
|
353
|
+
return Array.isArray(raw) ? raw : [];
|
|
354
|
+
}
|
|
355
|
+
/** Where this session's SessionQueue persists — for the daemon to wire queues. */
|
|
356
|
+
eventsPath(id) {
|
|
357
|
+
return paths.eventsPath(this.require(id).repo, id);
|
|
358
|
+
}
|
|
359
|
+
/** Where this session's review threads persist (src/daemon/threads.ts). */
|
|
360
|
+
threadsPath(id) {
|
|
361
|
+
return paths.threadsPath(this.require(id).repo, id);
|
|
362
|
+
}
|
|
363
|
+
/** Where this session's grill transcript persists (src/daemon/transcript.ts). */
|
|
364
|
+
transcriptPath(id) {
|
|
365
|
+
return paths.transcriptPath(this.require(id).repo, id);
|
|
366
|
+
}
|
|
367
|
+
/** Where this session's live-activity feed persists (src/daemon/activity.ts). */
|
|
368
|
+
activityPath(id) {
|
|
369
|
+
return paths.activityPath(this.require(id).repo, id);
|
|
370
|
+
}
|
|
371
|
+
require(id) {
|
|
372
|
+
const session = this.registry.sessions[id];
|
|
373
|
+
if (!session)
|
|
374
|
+
throw new Error(`unknown session: ${id}`);
|
|
375
|
+
return session;
|
|
376
|
+
}
|
|
377
|
+
/** `otc_` + 6 base36 chars, collision-checked (DECISIONS.md "Session ids"). */
|
|
378
|
+
mintId() {
|
|
379
|
+
for (let attempt = 0; attempt < 64; attempt++) {
|
|
380
|
+
let suffix = "";
|
|
381
|
+
for (const byte of randomBytes(6))
|
|
382
|
+
suffix += BASE36[byte % 36];
|
|
383
|
+
const id = `otc_${suffix}`;
|
|
384
|
+
if (!(id in this.registry.sessions))
|
|
385
|
+
return id;
|
|
386
|
+
}
|
|
387
|
+
throw new Error("could not mint a unique session id");
|
|
388
|
+
}
|
|
389
|
+
flushRegistry() {
|
|
390
|
+
writeFileAtomic(this.registryFile, stringify(this.registry));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/daemon/store.ts"],"names":[],"mappings":"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"}
|