@theford/runly 0.0.0-stage → 0.20.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 +202 -0
- package/README.md +157 -2
- package/dist/cli/client.d.ts +80 -0
- package/dist/cli/client.js +294 -0
- package/dist/cli/client.js.map +1 -0
- package/dist/cli/daemon-unit.d.ts +58 -0
- package/dist/cli/daemon-unit.js +289 -0
- package/dist/cli/daemon-unit.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +1482 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/install.d.ts +9 -0
- package/dist/cli/install.js +53 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/logs.d.ts +43 -0
- package/dist/cli/logs.js +232 -0
- package/dist/cli/logs.js.map +1 -0
- package/dist/cli/tether.d.ts +3 -0
- package/dist/cli/tether.js +65 -0
- package/dist/cli/tether.js.map +1 -0
- package/dist/cli/watchdog.d.ts +1 -0
- package/dist/cli/watchdog.js +81 -0
- package/dist/cli/watchdog.js.map +1 -0
- package/dist/core/builds.d.ts +20 -0
- package/dist/core/builds.js +93 -0
- package/dist/core/builds.js.map +1 -0
- package/dist/core/caller-env.d.ts +17 -0
- package/dist/core/caller-env.js +77 -0
- package/dist/core/caller-env.js.map +1 -0
- package/dist/core/diagnostics.d.ts +68 -0
- package/dist/core/diagnostics.js +34 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/env-vars.d.ts +35 -0
- package/dist/core/env-vars.js +32 -0
- package/dist/core/env-vars.js.map +1 -0
- package/dist/core/events.d.ts +10 -0
- package/dist/core/events.js +42 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/exec.d.ts +22 -0
- package/dist/core/exec.js +84 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/journal.d.ts +227 -0
- package/dist/core/journal.js +395 -0
- package/dist/core/journal.js.map +1 -0
- package/dist/core/logs.d.ts +56 -0
- package/dist/core/logs.js +259 -0
- package/dist/core/logs.js.map +1 -0
- package/dist/core/manifest.d.ts +292 -0
- package/dist/core/manifest.js +179 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/namespaces.d.ts +7 -0
- package/dist/core/namespaces.js +59 -0
- package/dist/core/namespaces.js.map +1 -0
- package/dist/core/paths.d.ts +31 -0
- package/dist/core/paths.js +65 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/policy.d.ts +94 -0
- package/dist/core/policy.js +122 -0
- package/dist/core/policy.js.map +1 -0
- package/dist/core/ports.d.ts +52 -0
- package/dist/core/ports.js +94 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/presets.d.ts +25 -0
- package/dist/core/presets.js +86 -0
- package/dist/core/presets.js.map +1 -0
- package/dist/core/procscan.d.ts +111 -0
- package/dist/core/procscan.js +342 -0
- package/dist/core/procscan.js.map +1 -0
- package/dist/core/retention.d.ts +97 -0
- package/dist/core/retention.js +322 -0
- package/dist/core/retention.js.map +1 -0
- package/dist/core/sleep.d.ts +51 -0
- package/dist/core/sleep.js +99 -0
- package/dist/core/sleep.js.map +1 -0
- package/dist/core/tree-ledger.d.ts +39 -0
- package/dist/core/tree-ledger.js +92 -0
- package/dist/core/tree-ledger.js.map +1 -0
- package/dist/core/types.d.ts +21 -0
- package/dist/core/types.js +6 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/units.d.ts +20 -0
- package/dist/core/units.js +69 -0
- package/dist/core/units.js.map +1 -0
- package/dist/core/upkeep.d.ts +90 -0
- package/dist/core/upkeep.js +176 -0
- package/dist/core/upkeep.js.map +1 -0
- package/dist/core/util.d.ts +46 -0
- package/dist/core/util.js +168 -0
- package/dist/core/util.js.map +1 -0
- package/dist/core/version.d.ts +39 -0
- package/dist/core/version.js +144 -0
- package/dist/core/version.js.map +1 -0
- package/dist/core/worktree.d.ts +51 -0
- package/dist/core/worktree.js +309 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/daemon/budget.d.ts +155 -0
- package/dist/daemon/budget.js +288 -0
- package/dist/daemon/budget.js.map +1 -0
- package/dist/daemon/election.d.ts +9 -0
- package/dist/daemon/election.js +140 -0
- package/dist/daemon/election.js.map +1 -0
- package/dist/daemon/engine.d.ts +1703 -0
- package/dist/daemon/engine.js +5305 -0
- package/dist/daemon/engine.js.map +1 -0
- package/dist/daemon/index.d.ts +2 -0
- package/dist/daemon/index.js +412 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/proxy.d.ts +108 -0
- package/dist/daemon/proxy.js +615 -0
- package/dist/daemon/proxy.js.map +1 -0
- package/dist/daemon/supervisor.d.ts +101 -0
- package/dist/daemon/supervisor.js +493 -0
- package/dist/daemon/supervisor.js.map +1 -0
- package/dist/drivers/appliances.d.ts +11 -0
- package/dist/drivers/appliances.js +174 -0
- package/dist/drivers/appliances.js.map +1 -0
- package/dist/drivers/datastores.d.ts +144 -0
- package/dist/drivers/datastores.js +775 -0
- package/dist/drivers/datastores.js.map +1 -0
- package/dist/drivers/preview.d.ts +69 -0
- package/dist/drivers/preview.js +605 -0
- package/dist/drivers/preview.js.map +1 -0
- package/dist/drivers/types.d.ts +70 -0
- package/dist/drivers/types.js +13 -0
- package/dist/drivers/types.js.map +1 -0
- package/package.json +63 -4
- package/schema/backlot.schema.json +549 -0
- package/schema/runly.schema.json +556 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-machine journal: disk is truth, daemon memory is a cache
|
|
3
|
+
* (decision 0009). node:sqlite — zero native deps.
|
|
4
|
+
*/
|
|
5
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
6
|
+
import { journalPath } from './paths.js';
|
|
7
|
+
import { BrokerError } from './util.js';
|
|
8
|
+
/**
|
|
9
|
+
* The journal schema this build understands, stamped into `PRAGMA user_version`.
|
|
10
|
+
*
|
|
11
|
+
* Bump it when a change makes an OLDER daemon misread this journal — not for
|
|
12
|
+
* every additive column: a bump is unnecessary when an old daemon selecting
|
|
13
|
+
* a known subset of columns still reads the journal correctly.
|
|
14
|
+
*
|
|
15
|
+
* Schemas 1-3 belong to runly before 0.16 and are refused: their migrations
|
|
16
|
+
* were retired in 0.19 (upgrade through 0.18 first).
|
|
17
|
+
*
|
|
18
|
+
* Schema 4 (decision 0034): `active_services = '[]'` means "no services are
|
|
19
|
+
* wanted" (an older daemon reads an empty list as the whole app and would boot
|
|
20
|
+
* it), the `db_copies` table holds database copies an older daemon would never
|
|
21
|
+
* reap, and data-only environments are gone (0.18 migrated them away).
|
|
22
|
+
*
|
|
23
|
+
* What this exists to stop is the DOWNGRADE, which has already cost once. The
|
|
24
|
+
* sha256 env-id migration stranded pre-upgrade rows that then counted against
|
|
25
|
+
* POOL_MAX_TOTAL and held their ports forever (BACKLOG.md), because nothing on
|
|
26
|
+
* disk said which build wrote them. A running example of the same shape:
|
|
27
|
+
* `data_only` defaults to 0, so a 0.7.0 daemon re-binding a 0.8.0 data-only
|
|
28
|
+
* environment reads "not data-only" and boots the whole application into a
|
|
29
|
+
* test lane's database. Disk is truth (decision 0009), so the truth has to say
|
|
30
|
+
* what wrote it.
|
|
31
|
+
*/
|
|
32
|
+
export const JOURNAL_SCHEMA_VERSION = 4;
|
|
33
|
+
/**
|
|
34
|
+
* service_pids was once `{"web": 1234}` and is now
|
|
35
|
+
* `{"web": {"pid":1234,"startTime":99}}`. Journals outlive releases, so read
|
|
36
|
+
* both shapes; a bare number simply has no identity pin (see ServicePid).
|
|
37
|
+
*/
|
|
38
|
+
function parseJson(raw) {
|
|
39
|
+
if (typeof raw !== 'string' || raw === '')
|
|
40
|
+
return undefined;
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(raw);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function parseServicePids(raw) {
|
|
49
|
+
const out = {};
|
|
50
|
+
let parsed;
|
|
51
|
+
try {
|
|
52
|
+
parsed = JSON.parse(raw);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
if (!parsed || typeof parsed !== 'object')
|
|
58
|
+
return out;
|
|
59
|
+
for (const [name, v] of Object.entries(parsed)) {
|
|
60
|
+
if (typeof v === 'number')
|
|
61
|
+
out[name] = { pid: v };
|
|
62
|
+
else if (v && typeof v === 'object' && typeof v.pid === 'number') {
|
|
63
|
+
out[name] = { pid: v.pid, startTime: v.startTime,
|
|
64
|
+
...(typeof v.pgid === 'number' ? { pgid: v.pgid } : {}),
|
|
65
|
+
...(Array.isArray(v.pgids) ? { pgids: v.pgids.filter(g => Number.isSafeInteger(g) && g > 0) } : {}) };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
function parseChild(raw) {
|
|
71
|
+
if (typeof raw !== 'string')
|
|
72
|
+
return undefined;
|
|
73
|
+
try {
|
|
74
|
+
const c = JSON.parse(raw);
|
|
75
|
+
if (typeof c.pid !== 'number')
|
|
76
|
+
return undefined;
|
|
77
|
+
return { pid: c.pid, ...(typeof c.start === 'number' ? { start: c.start } : {}), ...(typeof c.pgid === 'number' ? { pgid: c.pgid } : {}) };
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export class Journal {
|
|
84
|
+
db;
|
|
85
|
+
constructor(path = journalPath()) {
|
|
86
|
+
this.db = new DatabaseSync(path);
|
|
87
|
+
// Concurrent readers exist (tests and tools open the journal directly while
|
|
88
|
+
// the daemon runs), and without a busy timeout any overlap is an immediate
|
|
89
|
+
// SQLITE_BUSY rather than a short wait. Set before the first read below.
|
|
90
|
+
this.db.exec('PRAGMA busy_timeout = 5000');
|
|
91
|
+
// Refuse a journal from the FUTURE before touching it. A newer runly may
|
|
92
|
+
// have written rows whose semantics this build does not know, and the
|
|
93
|
+
// failure mode is silent: we would read a default where the newer build
|
|
94
|
+
// stored meaning, then write that misreading back as truth. Checked before
|
|
95
|
+
// any DDL so a journal we do not understand is never modified at all.
|
|
96
|
+
const stamped = Number(this.db.prepare('PRAGMA user_version').get()?.user_version ?? 0);
|
|
97
|
+
if (stamped > JOURNAL_SCHEMA_VERSION) {
|
|
98
|
+
throw new BrokerError('infra-error', `journal at ${path} was written by a newer runly (schema ${stamped}; this build understands ${JOURNAL_SCHEMA_VERSION}) — ` +
|
|
99
|
+
`run the newer runly, upgrade this one, or point BACKLOT_STATE_DIR at a different state root`, 'journal');
|
|
100
|
+
}
|
|
101
|
+
// Journals older than schema 4 (runly before 0.16) carried data-only
|
|
102
|
+
// environments, path-spelled stack identities and the projected worktree
|
|
103
|
+
// copies whose migrations lived here until 0.18. They are gone: such a
|
|
104
|
+
// journal is refused untouched rather than half-read.
|
|
105
|
+
const hasTables = this.db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'envs'").get() !== undefined;
|
|
106
|
+
if (hasTables && stamped < JOURNAL_SCHEMA_VERSION) {
|
|
107
|
+
throw new BrokerError('infra-error', `journal at ${path} has schema ${stamped}, older than this build reads (${JOURNAL_SCHEMA_VERSION}) — ` +
|
|
108
|
+
`upgrade through runly 0.18 first (it migrates the journal), or point BACKLOT_STATE_DIR at a fresh state root`, 'journal');
|
|
109
|
+
}
|
|
110
|
+
this.db.exec('PRAGMA journal_mode = WAL');
|
|
111
|
+
this.db.exec(`
|
|
112
|
+
CREATE TABLE IF NOT EXISTS envs (
|
|
113
|
+
id TEXT PRIMARY KEY, stack TEXT NOT NULL, stack_root TEXT NOT NULL,
|
|
114
|
+
state TEXT NOT NULL, root TEXT NOT NULL,
|
|
115
|
+
ports TEXT NOT NULL DEFAULT '{}', datastore_ns TEXT NOT NULL DEFAULT '{}',
|
|
116
|
+
fingerprints TEXT NOT NULL DEFAULT '{}', presets TEXT NOT NULL DEFAULT '{}',
|
|
117
|
+
bind_count INTEGER NOT NULL DEFAULT 0,
|
|
118
|
+
created_at INTEGER NOT NULL, last_used_at INTEGER NOT NULL,
|
|
119
|
+
service_pids TEXT NOT NULL DEFAULT '{}',
|
|
120
|
+
fail_streak INTEGER NOT NULL DEFAULT 0,
|
|
121
|
+
active_services TEXT, activity TEXT, drop_recipes TEXT, templates TEXT
|
|
122
|
+
);
|
|
123
|
+
CREATE TABLE IF NOT EXISTS leases (
|
|
124
|
+
id TEXT PRIMARY KEY, env_id TEXT NOT NULL, kind TEXT NOT NULL,
|
|
125
|
+
holder TEXT NOT NULL, hygiene TEXT NOT NULL, expires_at INTEGER NOT NULL,
|
|
126
|
+
holder_pid INTEGER, holder_start INTEGER,
|
|
127
|
+
preview_service TEXT, preview_url TEXT, preview_pid INTEGER, preview_start INTEGER, preview_port INTEGER,
|
|
128
|
+
preview_restore TEXT
|
|
129
|
+
);
|
|
130
|
+
CREATE TABLE IF NOT EXISTS counters (
|
|
131
|
+
stack TEXT PRIMARY KEY, next_env INTEGER NOT NULL DEFAULT 1
|
|
132
|
+
);
|
|
133
|
+
CREATE TABLE IF NOT EXISTS db_copies (
|
|
134
|
+
name TEXT PRIMARY KEY, stack TEXT NOT NULL, stack_root TEXT NOT NULL,
|
|
135
|
+
datastore TEXT NOT NULL, preset TEXT NOT NULL, ns TEXT NOT NULL, url TEXT NOT NULL,
|
|
136
|
+
state TEXT NOT NULL, holder TEXT NOT NULL, holder_pid INTEGER, holder_start INTEGER,
|
|
137
|
+
drop_cmd TEXT, drop_cwd TEXT, drop_path TEXT,
|
|
138
|
+
created_at INTEGER NOT NULL, drop_attempts INTEGER NOT NULL DEFAULT 0,
|
|
139
|
+
next_drop_at INTEGER NOT NULL DEFAULT 0, template TEXT, child TEXT
|
|
140
|
+
);
|
|
141
|
+
`);
|
|
142
|
+
// A schema-4 journal written by 0.16 or 0.17 lacks the columns added since
|
|
143
|
+
// (additive: no schema bump). Only the duplicate-column case is benign.
|
|
144
|
+
for (const [table, col] of [['envs', 'activity TEXT'], ['envs', 'drop_recipes TEXT'], ['envs', 'templates TEXT'], ['db_copies', 'template TEXT'], ['db_copies', 'child TEXT'], ['leases', 'preview_restore TEXT']]) {
|
|
145
|
+
try {
|
|
146
|
+
this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${col}`);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
if (!/duplicate column name/i.test(String(err.message ?? err)))
|
|
150
|
+
throw err;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (stamped < JOURNAL_SCHEMA_VERSION)
|
|
154
|
+
this.db.exec(`PRAGMA user_version = ${JOURNAL_SCHEMA_VERSION}`);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* One real sqlite transaction around a multi-statement write. Each of these
|
|
158
|
+
* sequences used to be N independent writes, so a daemon SIGKILLed between
|
|
159
|
+
* them left a half-state on disk — deleteEnv's env-gone-lease-left was the
|
|
160
|
+
* reviewed case. The sweeper tolerates those half-states (journals outlive
|
|
161
|
+
* releases); this stops new ones being minted. BEGIN IMMEDIATE takes the
|
|
162
|
+
* write lock up front, so the sequence can't interleave with the concurrent
|
|
163
|
+
* writers that busy_timeout exists for either.
|
|
164
|
+
*/
|
|
165
|
+
withTx(fn) {
|
|
166
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
167
|
+
try {
|
|
168
|
+
const out = fn();
|
|
169
|
+
this.db.exec('COMMIT');
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
try {
|
|
174
|
+
this.db.exec('ROLLBACK');
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
/* sqlite may already have rolled back on error */
|
|
178
|
+
}
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
rowToEnv(r) {
|
|
183
|
+
return {
|
|
184
|
+
id: r.id,
|
|
185
|
+
stack: r.stack,
|
|
186
|
+
stackRoot: r.stack_root,
|
|
187
|
+
state: r.state,
|
|
188
|
+
root: r.root,
|
|
189
|
+
ports: JSON.parse(r.ports),
|
|
190
|
+
datastoreNs: JSON.parse(r.datastore_ns),
|
|
191
|
+
fingerprints: JSON.parse(r.fingerprints),
|
|
192
|
+
presets: JSON.parse(r.presets),
|
|
193
|
+
bindCount: r.bind_count,
|
|
194
|
+
createdAt: r.created_at,
|
|
195
|
+
lastUsedAt: r.last_used_at,
|
|
196
|
+
servicePids: parseServicePids(r.service_pids),
|
|
197
|
+
failStreak: r.fail_streak ?? 0,
|
|
198
|
+
activeServices: r.active_services ? JSON.parse(r.active_services) : undefined,
|
|
199
|
+
activity: parseJson(r.activity),
|
|
200
|
+
dropRecipes: parseJson(r.drop_recipes),
|
|
201
|
+
templates: parseJson(r.templates),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
saveEnv(e) {
|
|
205
|
+
this.db
|
|
206
|
+
.prepare(`INSERT INTO envs (id, stack, stack_root, state, root, ports, datastore_ns, fingerprints, presets, bind_count, created_at, last_used_at, service_pids, fail_streak, active_services, drop_recipes, templates)
|
|
207
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
208
|
+
ON CONFLICT(id) DO UPDATE SET state=excluded.state, ports=excluded.ports,
|
|
209
|
+
datastore_ns=excluded.datastore_ns, fingerprints=excluded.fingerprints,
|
|
210
|
+
presets=excluded.presets, bind_count=excluded.bind_count,
|
|
211
|
+
last_used_at=excluded.last_used_at, service_pids=excluded.service_pids,
|
|
212
|
+
fail_streak=excluded.fail_streak, active_services=excluded.active_services,
|
|
213
|
+
drop_recipes=COALESCE(excluded.drop_recipes, envs.drop_recipes),
|
|
214
|
+
templates=COALESCE(excluded.templates, envs.templates)`)
|
|
215
|
+
.run(e.id, e.stack, e.stackRoot, e.state, e.root, JSON.stringify(e.ports), JSON.stringify(e.datastoreNs), JSON.stringify(e.fingerprints), JSON.stringify(e.presets), e.bindCount, e.createdAt, e.lastUsedAt, JSON.stringify(e.servicePids), e.failStreak, e.activeServices ? JSON.stringify(e.activeServices) : null, e.dropRecipes ? JSON.stringify(e.dropRecipes) : null, e.templates ? JSON.stringify(e.templates) : null);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Record the proxy's activity clocks (decision 0035). Its own statement, so
|
|
219
|
+
* a snapshot saved by a verb can never roll them back, and merged so a
|
|
220
|
+
* clock only moves forward.
|
|
221
|
+
*/
|
|
222
|
+
saveActivity(id, activity) {
|
|
223
|
+
const row = this.db.prepare('SELECT activity FROM envs WHERE id = ?').get(id);
|
|
224
|
+
if (!row)
|
|
225
|
+
return;
|
|
226
|
+
const merged = { ...(parseJson(row.activity) ?? {}) };
|
|
227
|
+
for (const [k, v] of Object.entries(activity))
|
|
228
|
+
merged[k] = Math.max(merged[k] ?? 0, v);
|
|
229
|
+
this.db.prepare('UPDATE envs SET activity = ? WHERE id = ?').run(JSON.stringify(merged), id);
|
|
230
|
+
}
|
|
231
|
+
getEnv(id) {
|
|
232
|
+
const r = this.db.prepare('SELECT * FROM envs WHERE id = ?').get(id);
|
|
233
|
+
return r ? this.rowToEnv(r) : undefined;
|
|
234
|
+
}
|
|
235
|
+
envsForStack(stack) {
|
|
236
|
+
return this.db.prepare('SELECT * FROM envs WHERE stack = ? ORDER BY id').all(stack).map((r) => this.rowToEnv(r));
|
|
237
|
+
}
|
|
238
|
+
allEnvs() {
|
|
239
|
+
return this.db.prepare('SELECT * FROM envs ORDER BY id').all().map((r) => this.rowToEnv(r));
|
|
240
|
+
}
|
|
241
|
+
rowToDbCopy(r) {
|
|
242
|
+
return {
|
|
243
|
+
name: r.name,
|
|
244
|
+
stack: r.stack,
|
|
245
|
+
stackRoot: r.stack_root,
|
|
246
|
+
datastore: r.datastore,
|
|
247
|
+
preset: r.preset,
|
|
248
|
+
ns: r.ns,
|
|
249
|
+
url: r.url,
|
|
250
|
+
state: r.state,
|
|
251
|
+
holder: r.holder,
|
|
252
|
+
holderPid: r.holder_pid ?? undefined,
|
|
253
|
+
holderStart: r.holder_start ?? undefined,
|
|
254
|
+
dropCmd: r.drop_cmd ?? undefined,
|
|
255
|
+
dropCwd: r.drop_cwd ?? undefined,
|
|
256
|
+
dropPath: r.drop_path ?? undefined,
|
|
257
|
+
createdAt: r.created_at,
|
|
258
|
+
dropAttempts: r.drop_attempts ?? 0,
|
|
259
|
+
nextDropAt: r.next_drop_at ?? 0,
|
|
260
|
+
template: r.template ?? undefined,
|
|
261
|
+
child: parseChild(r.child),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
saveDbCopy(c) {
|
|
265
|
+
this.db
|
|
266
|
+
.prepare(`INSERT INTO db_copies (name, stack, stack_root, datastore, preset, ns, url, state, holder, holder_pid, holder_start,
|
|
267
|
+
drop_cmd, drop_cwd, drop_path, created_at, drop_attempts, next_drop_at, template, child)
|
|
268
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
269
|
+
ON CONFLICT(name) DO UPDATE SET state=excluded.state, drop_attempts=excluded.drop_attempts,
|
|
270
|
+
next_drop_at=excluded.next_drop_at, template=COALESCE(excluded.template, db_copies.template),
|
|
271
|
+
child=COALESCE(excluded.child, db_copies.child)`)
|
|
272
|
+
.run(c.name, c.stack, c.stackRoot, c.datastore, c.preset, c.ns, c.url, c.state, c.holder, c.holderPid ?? null, c.holderStart ?? null, c.dropCmd ?? null, c.dropCwd ?? null, c.dropPath ?? null, c.createdAt, c.dropAttempts, c.nextDropAt, c.template ?? null, c.child ? JSON.stringify(c.child) : null);
|
|
273
|
+
}
|
|
274
|
+
getDbCopy(name) {
|
|
275
|
+
const r = this.db.prepare('SELECT * FROM db_copies WHERE name = ?').get(name);
|
|
276
|
+
return r ? this.rowToDbCopy(r) : undefined;
|
|
277
|
+
}
|
|
278
|
+
allDbCopies() {
|
|
279
|
+
return this.db.prepare('SELECT * FROM db_copies ORDER BY created_at, name').all().map((r) => this.rowToDbCopy(r));
|
|
280
|
+
}
|
|
281
|
+
deleteDbCopy(name) {
|
|
282
|
+
this.db.prepare('DELETE FROM db_copies WHERE name = ?').run(name);
|
|
283
|
+
}
|
|
284
|
+
deleteEnv(id) {
|
|
285
|
+
// Atomic: a kill between these two deletes left a lease naming an env row
|
|
286
|
+
// that no longer existed (the sweeper prunes that half-state as backstop).
|
|
287
|
+
this.withTx(() => {
|
|
288
|
+
this.db.prepare('DELETE FROM envs WHERE id = ?').run(id);
|
|
289
|
+
this.db.prepare('DELETE FROM leases WHERE env_id = ?').run(id);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* A per-stack env sequence that NEVER reuses a number, even after envs are
|
|
294
|
+
* reaped — so a recycled env's id can't collide with a live one. Monotonic
|
|
295
|
+
* in the journal, survives daemon restarts.
|
|
296
|
+
*/
|
|
297
|
+
nextEnvSeq(stack) {
|
|
298
|
+
// Atomic read-modify-write: a foreign writer landing between the SELECT
|
|
299
|
+
// and the UPDATE would hand the same "never reused" number out twice.
|
|
300
|
+
return this.withTx(() => {
|
|
301
|
+
this.db.prepare('INSERT INTO counters (stack, next_env) VALUES (?, 1) ON CONFLICT(stack) DO NOTHING').run(stack);
|
|
302
|
+
const row = this.db.prepare('SELECT next_env FROM counters WHERE stack = ?').get(stack);
|
|
303
|
+
const seq = row.next_env;
|
|
304
|
+
this.db.prepare('UPDATE counters SET next_env = next_env + 1 WHERE stack = ?').run(stack);
|
|
305
|
+
return seq;
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
/** Activity, NOT lease renewal: keeps idle reclamation honest without extending ownership. */
|
|
309
|
+
touchEnv(id) {
|
|
310
|
+
this.db.prepare('UPDATE envs SET last_used_at = ? WHERE id = ?').run(Date.now(), id);
|
|
311
|
+
}
|
|
312
|
+
/** Update just the recorded service pids (auto-restart keeps recovery honest). */
|
|
313
|
+
updateServicePids(id, pids) {
|
|
314
|
+
this.db.prepare('UPDATE envs SET service_pids = ? WHERE id = ?').run(JSON.stringify(pids), id);
|
|
315
|
+
}
|
|
316
|
+
saveLease(l) {
|
|
317
|
+
this.db
|
|
318
|
+
.prepare(`INSERT INTO leases (id, env_id, kind, holder, hygiene, expires_at, holder_pid, holder_start,
|
|
319
|
+
preview_service, preview_url, preview_pid, preview_start, preview_port)
|
|
320
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
321
|
+
ON CONFLICT(id) DO UPDATE SET expires_at=excluded.expires_at, hygiene=excluded.hygiene,
|
|
322
|
+
holder_pid=excluded.holder_pid, holder_start=excluded.holder_start,
|
|
323
|
+
preview_service=excluded.preview_service, preview_url=excluded.preview_url,
|
|
324
|
+
preview_pid=excluded.preview_pid, preview_start=excluded.preview_start,
|
|
325
|
+
preview_port=excluded.preview_port`)
|
|
326
|
+
.run(l.id, l.envId, l.kind, l.holder, l.hygiene, l.expiresAt, l.holderPid ?? null, l.holderStart ?? null, l.previewService ?? null, l.previewUrl ?? null, l.previewPid ?? null, l.previewStart ?? null, l.previewPort ?? null);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Forget a lease's preview tunnel — compare-and-swap on the pid.
|
|
330
|
+
*
|
|
331
|
+
* Every caller reached here holding a lease SNAPSHOT, so an unconditional
|
|
332
|
+
* clear let a slow one wipe the record of a tunnel someone else had already
|
|
333
|
+
* published in its place. That pid is then unnameable, and an unnameable
|
|
334
|
+
* preview pid is a public, unauthenticated URL that serves forever. Passing
|
|
335
|
+
* the pid that was actually stopped makes a stale caller a no-op; the
|
|
336
|
+
* pid-less form is only for a row that is being deleted outright.
|
|
337
|
+
*/
|
|
338
|
+
clearLeasePreview(id, stoppedPid) {
|
|
339
|
+
const cols = 'preview_service=NULL, preview_url=NULL, preview_pid=NULL, preview_start=NULL, preview_port=NULL';
|
|
340
|
+
if (stoppedPid === undefined) {
|
|
341
|
+
this.db.prepare(`UPDATE leases SET ${cols} WHERE id=?`).run(id);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
this.db.prepare(`UPDATE leases SET ${cols} WHERE id=? AND preview_pid=?`).run(id, stoppedPid);
|
|
345
|
+
}
|
|
346
|
+
/** Record (or, with null, forget) what a restart re-publishes for this lease (decision 0045). */
|
|
347
|
+
setLeasePreviewRestore(id, restore) {
|
|
348
|
+
this.db.prepare('UPDATE leases SET preview_restore = ? WHERE id = ?').run(restore ? JSON.stringify(restore) : null, id);
|
|
349
|
+
}
|
|
350
|
+
rowToLease(r) {
|
|
351
|
+
return {
|
|
352
|
+
id: r.id,
|
|
353
|
+
envId: r.env_id,
|
|
354
|
+
kind: 'session',
|
|
355
|
+
holder: r.holder,
|
|
356
|
+
hygiene: r.hygiene,
|
|
357
|
+
expiresAt: r.expires_at,
|
|
358
|
+
holderPid: r.holder_pid ?? undefined,
|
|
359
|
+
holderStart: r.holder_start ?? undefined,
|
|
360
|
+
previewService: r.preview_service ?? undefined,
|
|
361
|
+
previewUrl: r.preview_url ?? undefined,
|
|
362
|
+
previewPid: r.preview_pid ?? undefined,
|
|
363
|
+
previewStart: r.preview_start ?? undefined,
|
|
364
|
+
previewPort: r.preview_port ?? undefined,
|
|
365
|
+
previewRestore: parseJson(r.preview_restore),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
leaseForHolder(holder, stack) {
|
|
369
|
+
const rows = this.db.prepare(`SELECT l.* FROM leases l JOIN envs e ON e.id = l.env_id WHERE l.holder = ? AND e.stack = ?`).all(holder, stack);
|
|
370
|
+
if (rows.length > 1) {
|
|
371
|
+
throw new BrokerError('env-error', `ambiguous leases for holder '${holder}': ${rows.map((r) => r.env_id).join(', ')}; inspect 'runly status', then retire one with 'runly pool recycle <envId> --force' — that destroys the selected environment and its data and ends its lease`, 'lease');
|
|
372
|
+
}
|
|
373
|
+
return rows[0] ? this.rowToLease(rows[0]) : undefined;
|
|
374
|
+
}
|
|
375
|
+
leaseForEnv(envId) {
|
|
376
|
+
const r = this.db.prepare('SELECT * FROM leases WHERE env_id = ?').get(envId);
|
|
377
|
+
return r ? this.rowToLease(r) : undefined;
|
|
378
|
+
}
|
|
379
|
+
allLeases() {
|
|
380
|
+
return this.db.prepare('SELECT * FROM leases').all().map((r) => this.rowToLease(r));
|
|
381
|
+
}
|
|
382
|
+
deleteLease(id) {
|
|
383
|
+
this.db.prepare('DELETE FROM leases WHERE id = ?').run(id);
|
|
384
|
+
}
|
|
385
|
+
/** Shift every deadline by `ms` — the sleep pardon (decision 0009). */
|
|
386
|
+
pardon(ms) {
|
|
387
|
+
// Atomic: a kill between these left leases pardoned but idle clocks not,
|
|
388
|
+
// so a machine that slept woke to premature quiesces.
|
|
389
|
+
this.withTx(() => {
|
|
390
|
+
this.db.prepare('UPDATE leases SET expires_at = expires_at + ?').run(ms);
|
|
391
|
+
this.db.prepare('UPDATE envs SET last_used_at = last_used_at + ?').run(ms);
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
//# sourceMappingURL=journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/core/journal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGxC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAExC;;;;GAIG;AACH,SAAS,SAAS,CAAI,GAAY;IAChC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC5D,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAW;IACnC,MAAM,GAAG,GAA+B,EAAE,CAAC;IAC3C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACtD,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAiC,CAAC,EAAE,CAAC;QAC1E,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;aAC7C,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAQ,CAAgB,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YACjF,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAG,CAAgB,CAAC,GAAG,EAAE,SAAS,EAAG,CAAgB,CAAC,SAAS;gBAC9E,GAAG,CAAC,OAAQ,CAAgB,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAG,CAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvF,GAAG,CAAC,KAAK,CAAC,OAAO,CAAE,CAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAG,CAAgB,CAAC,KAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3I,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AA8ID,SAAS,UAAU,CAAC,GAAY;IAC9B,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuD,CAAC;QAChF,IAAI,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAChD,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAC7I,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,OAAO,OAAO;IACV,EAAE,CAAe;IAEzB,YAAY,IAAI,GAAG,WAAW,EAAE;QAC9B,IAAI,CAAC,EAAE,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACjC,4EAA4E;QAC5E,2EAA2E;QAC3E,yEAAyE;QACzE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC3C,yEAAyE;QACzE,sEAAsE;QACtE,wEAAwE;QACxE,2EAA2E;QAC3E,sEAAsE;QACtE,MAAM,OAAO,GAAG,MAAM,CACnB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,GAAG,EAA4C,EAAE,YAAY,IAAI,CAAC,CAC3G,CAAC;QACF,IAAI,OAAO,GAAG,sBAAsB,EAAE,CAAC;YACrC,MAAM,IAAI,WAAW,CACnB,aAAa,EACb,cAAc,IAAI,yCAAyC,OAAO,4BAA4B,sBAAsB,MAAM;gBACxH,6FAA6F,EAC/F,SAAS,CACV,CAAC;QACJ,CAAC;QACD,qEAAqE;QACrE,yEAAyE;QACzE,uEAAuE;QACvE,sDAAsD;QACtD,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,oEAAoE,CAAC,CAAC,GAAG,EAAE,KAAK,SAAS,CAAC;QAC5H,IAAI,SAAS,IAAI,OAAO,GAAG,sBAAsB,EAAE,CAAC;YAClD,MAAM,IAAI,WAAW,CACnB,aAAa,EACb,cAAc,IAAI,eAAe,OAAO,kCAAkC,sBAAsB,MAAM;gBACpG,8GAA8G,EAChH,SAAS,CACV,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC1C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8BZ,CAAC,CAAC;QACH,2EAA2E;QAC3E,wEAAwE;QACxE,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAU,EAAE,CAAC;YAC5N,IAAI,CAAC;gBACH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,eAAe,KAAK,eAAe,GAAG,EAAE,CAAC,CAAC;YACzD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAE,GAAa,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;oBAAE,MAAM,GAAG,CAAC;YACvF,CAAC;QACH,CAAC;QACD,IAAI,OAAO,GAAG,sBAAsB;YAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,yBAAyB,sBAAsB,EAAE,CAAC,CAAC;IACxG,CAAC;IAED;;;;;;;;OAQG;IACK,MAAM,CAAI,EAAW;QAC3B,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,EAAE,CAAC;YACjB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvB,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC3B,CAAC;YAAC,MAAM,CAAC;gBACP,kDAAkD;YACpD,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,CAA0B;QACzC,OAAO;YACL,EAAE,EAAE,CAAC,CAAC,EAAY;YAClB,KAAK,EAAE,CAAC,CAAC,KAAe;YACxB,SAAS,EAAE,CAAC,CAAC,UAAoB;YACjC,KAAK,EAAE,CAAC,CAAC,KAAiB;YAC1B,IAAI,EAAE,CAAC,CAAC,IAAc;YACtB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAe,CAAC;YACpC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAsB,CAAC;YACjD,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAsB,CAAC;YAClD,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAiB,CAAC;YACxC,SAAS,EAAE,CAAC,CAAC,UAAoB;YACjC,SAAS,EAAE,CAAC,CAAC,UAAoB;YACjC,UAAU,EAAE,CAAC,CAAC,YAAsB;YACpC,WAAW,EAAE,gBAAgB,CAAC,CAAC,CAAC,YAAsB,CAAC;YACvD,UAAU,EAAG,CAAC,CAAC,WAAsB,IAAI,CAAC;YAC1C,cAAc,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,eAAyB,CAAc,CAAC,CAAC,CAAC,SAAS;YACrG,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC/B,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC;YACtC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;SAClC,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,CAAS;QACf,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;;;;;;;;kEAQ0D,CAC3D;aACA,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,EAC3C,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,EACtF,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EAChG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,EACxE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CACvG,CAAC;IACN,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,EAAU,EAAE,QAAgC;QACvD,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC,GAAG,CAAC,EAAE,CAA6C,CAAC;QAC1H,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,MAAM,MAAM,GAA2B,EAAE,GAAG,CAAC,SAAS,CAAyB,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QACtG,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACvF,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/F,CAAC;IAED,MAAM,CAAC,EAAU;QACf,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAA4B,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,CAAC;IAED,YAAY,CAAC,KAAa;QACxB,OAAQ,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,GAAG,CAAC,KAAK,CAA+B,CAAC,GAAG,CACpH,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CACxB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAQ,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC,GAAG,EAAgC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACtG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CACjB,CAAC;IACJ,CAAC;IAEO,WAAW,CAAC,CAA0B;QAC5C,OAAO;YACL,IAAI,EAAE,CAAC,CAAC,IAAc;YACtB,KAAK,EAAE,CAAC,CAAC,KAAe;YACxB,SAAS,EAAE,CAAC,CAAC,UAAoB;YACjC,SAAS,EAAE,CAAC,CAAC,SAAmB;YAChC,MAAM,EAAE,CAAC,CAAC,MAAgB;YAC1B,EAAE,EAAE,CAAC,CAAC,EAAY;YAClB,GAAG,EAAE,CAAC,CAAC,GAAa;YACpB,KAAK,EAAE,CAAC,CAAC,KAA2B;YACpC,MAAM,EAAE,CAAC,CAAC,MAAgB;YAC1B,SAAS,EAAG,CAAC,CAAC,UAA4B,IAAI,SAAS;YACvD,WAAW,EAAG,CAAC,CAAC,YAA8B,IAAI,SAAS;YAC3D,OAAO,EAAG,CAAC,CAAC,QAA0B,IAAI,SAAS;YACnD,OAAO,EAAG,CAAC,CAAC,QAA0B,IAAI,SAAS;YACnD,QAAQ,EAAG,CAAC,CAAC,SAA2B,IAAI,SAAS;YACrD,SAAS,EAAE,CAAC,CAAC,UAAoB;YACjC,YAAY,EAAG,CAAC,CAAC,aAAwB,IAAI,CAAC;YAC9C,UAAU,EAAG,CAAC,CAAC,YAAuB,IAAI,CAAC;YAC3C,QAAQ,EAAG,CAAC,CAAC,QAA0B,IAAI,SAAS;YACpD,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;SAC3B,CAAC;IACJ,CAAC;IAED,UAAU,CAAC,CAAY;QACrB,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;;;;;2DAKmD,CACpD;aACA,GAAG,CACF,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,EACnF,CAAC,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC,CAAC,QAAQ,IAAI,IAAI,EACpG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CACxG,CAAC;IACN,CAAC;IAED,SAAS,CAAC,IAAY;QACpB,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9E,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAA4B,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,CAAC;IAED,WAAW;QACT,OAAQ,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC,GAAG,EAAgC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACzH,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CACpB,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,IAAY;QACvB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC;IAED,SAAS,CAAC,EAAU;QAClB,0EAA0E;QAC1E,2EAA2E;QAC3E,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;YACf,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,KAAa;QACtB,wEAAwE;QACxE,sEAAsE;QACtE,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;YACtB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,oFAAoF,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACjH,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC,GAAG,CAAC,KAAK,CAAyB,CAAC;YAChH,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC;YACzB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,6DAA6D,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1F,OAAO,GAAG,CAAC;QACb,CAAC,CAAC,CAAC;IACL,CAAC;IAED,8FAA8F;IAC9F,QAAQ,CAAC,EAAU;QACjB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,kFAAkF;IAClF,iBAAiB,CAAC,EAAU,EAAE,IAAgC;QAC5D,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACjG,CAAC;IAED,SAAS,CAAC,CAAW;QACnB,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;;;;;;;8CAOsC,CACvC;aACA,GAAG,CACF,CAAC,CAAC,EAAE,EACJ,CAAC,CAAC,KAAK,EACP,CAAC,CAAC,IAAI,EACN,CAAC,CAAC,MAAM,EACR,CAAC,CAAC,OAAO,EACT,CAAC,CAAC,SAAS,EACX,CAAC,CAAC,SAAS,IAAI,IAAI,EACnB,CAAC,CAAC,WAAW,IAAI,IAAI,EACrB,CAAC,CAAC,cAAc,IAAI,IAAI,EACxB,CAAC,CAAC,UAAU,IAAI,IAAI,EACpB,CAAC,CAAC,UAAU,IAAI,IAAI,EACpB,CAAC,CAAC,YAAY,IAAI,IAAI,EACtB,CAAC,CAAC,WAAW,IAAI,IAAI,CACtB,CAAC;IACN,CAAC;IAED;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAU,EAAE,UAAmB;QAC/C,MAAM,IAAI,GAAG,iGAAiG,CAAC;QAC/G,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,qBAAqB,IAAI,aAAa,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChE,OAAO;QACT,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,qBAAqB,IAAI,+BAA+B,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAChG,CAAC;IAED,iGAAiG;IACjG,sBAAsB,CAAC,EAAU,EAAE,OAA8B;QAC/D,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,oDAAoD,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC1H,CAAC;IAEO,UAAU,CAAC,CAA0B;QAC3C,OAAO;YACL,EAAE,EAAE,CAAC,CAAC,EAAY;YAClB,KAAK,EAAE,CAAC,CAAC,MAAgB;YACzB,IAAI,EAAE,SAAsB;YAC5B,MAAM,EAAE,CAAC,CAAC,MAAgB;YAC1B,OAAO,EAAE,CAAC,CAAC,OAAkB;YAC7B,SAAS,EAAE,CAAC,CAAC,UAAoB;YACjC,SAAS,EAAG,CAAC,CAAC,UAA4B,IAAI,SAAS;YACvD,WAAW,EAAG,CAAC,CAAC,YAA8B,IAAI,SAAS;YAC3D,cAAc,EAAG,CAAC,CAAC,eAAiC,IAAI,SAAS;YACjE,UAAU,EAAG,CAAC,CAAC,WAA6B,IAAI,SAAS;YACzD,UAAU,EAAG,CAAC,CAAC,WAA6B,IAAI,SAAS;YACzD,YAAY,EAAG,CAAC,CAAC,aAA+B,IAAI,SAAS;YAC7D,WAAW,EAAG,CAAC,CAAC,YAA8B,IAAI,SAAS;YAC3D,cAAc,EAAE,SAAS,CAAiB,CAAC,CAAC,eAAe,CAAC;SAC7D,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,MAAc,EAAE,KAAa;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC1B,4FAA4F,CAC7F,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAA8B,CAAC;QAClD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,WAAW,CACnB,WAAW,EACX,gCAAgC,MAAM,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,8JAA8J,EAC9O,OAAO,CACR,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxD,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9E,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAA4B,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,CAAC;IAED,SAAS;QACP,OAAQ,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC,GAAG,EAAgC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5F,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CACnB,CAAC;IACJ,CAAC;IAED,WAAW,CAAC,EAAU;QACpB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,uEAAuE;IACvE,MAAM,CAAC,EAAU;QACf,yEAAyE;QACzE,sDAAsD;QACtD,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;YACf,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzE,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,iDAAiD,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export declare const START_MARKER = "-- runly:";
|
|
2
|
+
export declare const logFileOf: (dir: string, service: string) => string;
|
|
3
|
+
export declare const buildLogOf: (dir: string, service: string) => string;
|
|
4
|
+
export declare const UPKEEP_LOG = "upkeep";
|
|
5
|
+
/** The marker written when a service process starts; `--since up` reads from the last one. */
|
|
6
|
+
export declare function startMarker(service: string, pid: number | undefined, reason: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Appends time-stamped lines to one service's log. Output arrives in chunks
|
|
9
|
+
* from two streams; each stream keeps its own partial line, so a line is
|
|
10
|
+
* stamped once, when it completes, and stdout and stderr never splice into
|
|
11
|
+
* one line.
|
|
12
|
+
*/
|
|
13
|
+
export declare class LogWriter {
|
|
14
|
+
readonly file: string;
|
|
15
|
+
private readonly cap;
|
|
16
|
+
private size;
|
|
17
|
+
private partial;
|
|
18
|
+
constructor(file: string, cap?: number);
|
|
19
|
+
/** A whole line from runly itself (a start marker). */
|
|
20
|
+
line(text: string): void;
|
|
21
|
+
/** A chunk of output from `stream` ('out'/'err'). */
|
|
22
|
+
write(stream: string, chunk: string): void;
|
|
23
|
+
/** The stream ended: whatever is left of its last line is written. */
|
|
24
|
+
end(stream: string): void;
|
|
25
|
+
private flushAll;
|
|
26
|
+
private append;
|
|
27
|
+
}
|
|
28
|
+
/** Rotate a log past the cap (the retention sweep's backstop for a writer that never rotated). */
|
|
29
|
+
export declare function rotateIfOver(file: string, cap: number): boolean;
|
|
30
|
+
export interface LogLine {
|
|
31
|
+
service: string;
|
|
32
|
+
/** Epoch ms; NaN for a line written before stamping existed (kept in place). */
|
|
33
|
+
at: number;
|
|
34
|
+
text: string;
|
|
35
|
+
/** A start marker. */
|
|
36
|
+
marker: boolean;
|
|
37
|
+
/** Written by an earlier process than the last start marker's (`--until` never matches it). */
|
|
38
|
+
prior?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** Parse one log (its rotation first, then the live file up to `end`) into lines. */
|
|
41
|
+
export declare function readLog(file: string, service: string, maxBytes?: number, end?: number): LogLine[];
|
|
42
|
+
/**
|
|
43
|
+
* The last lines of one log — at least `want` that are not start markers, or
|
|
44
|
+
* all there are — read BACKWARDS from `end` in growing chunks, into the
|
|
45
|
+
* rotation only when the live file runs out. `logs --lines 40` on a 20 MB log
|
|
46
|
+
* used to parse both whole files to print 40 lines.
|
|
47
|
+
*/
|
|
48
|
+
export declare function readLastLines(file: string, service: string, want: number, end?: number): LogLine[];
|
|
49
|
+
/** Mark every line before the last start marker as `prior` (an earlier process's). */
|
|
50
|
+
export declare function markPrior(lines: LogLine[]): LogLine[];
|
|
51
|
+
/** Only the lines of the CURRENT process: from the last start marker on. */
|
|
52
|
+
export declare function sinceLastStart(lines: LogLine[]): LogLine[];
|
|
53
|
+
/** Interleave several services' lines by time (stable within one service). */
|
|
54
|
+
export declare function interleave(groups: LogLine[][]): LogLine[];
|
|
55
|
+
/** Start a build log afresh for this build. */
|
|
56
|
+
export declare function beginBuildLog(file: string, header: string): void;
|