@kky42/pievo-server 4.1.0 → 4.2.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/.output/nitro.json +1 -1
- package/.output/public/assets/{MarkdownView-BuFKr9Jg.js → MarkdownView-C9-AzIkl.js} +1 -1
- package/.output/public/assets/{actionUi-BZrLpgHT.js → actionUi-D47jwMfh.js} +1 -1
- package/.output/public/assets/app-C5xfpeJe.css +2 -0
- package/.output/public/assets/index-BgNjFXWE.js +10 -0
- package/.output/public/assets/loops._loopId-Bo63FXPQ.js +2 -0
- package/.output/public/assets/loops._loopId_.runs._runId-DW2HPjtj.js +3 -0
- package/.output/public/assets/{preload-helper-BSxGu4d0.js → preload-helper-CbYqBpY9.js} +2 -2
- package/.output/public/assets/routes-C88oSApK.js +1 -0
- package/.output/public/assets/routes-CTCh8xYm.js +1 -0
- package/.output/server/_ssr/{MachinesModal-CLBZxZkq.mjs → MachinesModal-DwHy2cis.mjs} +43 -24
- package/.output/server/_ssr/{MarkdownView-D1J7ImYF.mjs → MarkdownView-BOe75tli.mjs} +3 -6
- package/.output/server/_ssr/{actionUi-DE-Pb2tv.mjs → actionUi-CszQXDSN.mjs} +20 -2
- package/.output/server/_ssr/artifactFiles-DJiK99sF.mjs +67 -0
- package/.output/server/_ssr/{artifactKind-a2oxT2wQ.mjs → artifactKind-DYMU0q3N.mjs} +2 -7
- package/.output/server/_ssr/{boot-DmpKtb0A.mjs → boot-CecCDRoH.mjs} +218 -204
- package/.output/server/_ssr/{lifecycleUi-DEjM8Etm.mjs → lifecycleUi-Bd7_sthp.mjs} +8 -15
- package/.output/server/_ssr/{loopApi-DK1lQPrx.mjs → loopApi-uEiBQ5YX.mjs} +12 -18
- package/.output/server/_ssr/{loopProjection-DdFivf7-.mjs → loopProjection-Bo54jjGd.mjs} +3 -2
- package/.output/server/_ssr/{loops._loopId-DVCvGSEc.mjs → loops._loopId-c1oRP7cr.mjs} +15 -30
- package/.output/server/_ssr/{loops._loopId_.runs._runId-BXbTfLlC.mjs → loops._loopId_.runs._runId-Dl0SwOlC.mjs} +28 -19
- package/.output/server/_ssr/loops._loopId_.runs._runId-czMVvNsk.mjs +11 -0
- package/.output/server/_ssr/{machineFns-DLRHgDdS.mjs → machineFns-aw9xe5oz.mjs} +6 -20
- package/.output/server/_ssr/{router-BhHsTGqu.mjs → router-3OQ849sN.mjs} +22 -31
- package/.output/server/_ssr/{routes-CCallyCT.mjs → routes-CL4y6AEv.mjs} +2 -2
- package/.output/server/_ssr/{routes-CL2Kef-Q.mjs → routes-CT5IdoiP.mjs} +6 -6
- package/.output/server/_ssr/{routes-ngpX7h8D.mjs → routes-nU2PDK3W.mjs} +2 -2
- package/.output/server/_ssr/ssr.mjs +22 -22
- package/.output/server/{_tanstack-start-manifest_v-BPiJ4yWT.mjs → _tanstack-start-manifest_v-_nMS2ozp.mjs} +8 -8
- package/.output/server/drizzle/0002_run_attention.sql +1 -0
- package/.output/server/drizzle/meta/_journal.json +7 -0
- package/.output/server/index.mjs +49 -49
- package/drizzle/0002_run_attention.sql +1 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/.output/public/assets/app-x1CeeY7N.css +0 -2
- package/.output/public/assets/index-D0FIKiiW.js +0 -10
- package/.output/public/assets/loops._loopId-DTkIz4C0.js +0 -2
- package/.output/public/assets/loops._loopId_.runs._runId-CsV2Ih4Z.js +0 -3
- package/.output/public/assets/routes-BIOJZsig.js +0 -1
- package/.output/public/assets/routes-m4Z90DTR.js +0 -1
- package/.output/server/_ssr/artifactFiles-B0NPRDOR.mjs +0 -52
- package/.output/server/_ssr/loops._loopId_.runs._runId-Dr6E-FA5.mjs +0 -16
|
@@ -11,14 +11,14 @@ import { fileURLToPath } from "node:url";
|
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import os from "node:os";
|
|
13
13
|
import fs$1 from "node:fs/promises";
|
|
14
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/loopTags-
|
|
15
|
-
var
|
|
14
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/loopTags-Bi7Bm7v8.js
|
|
15
|
+
var loopTags_Bi7Bm7v8_exports = /* @__PURE__ */ __exportAll$1({
|
|
16
16
|
$: () => loopIdsWithSnapshots,
|
|
17
|
-
$t: () =>
|
|
17
|
+
$t: () => logger_exports,
|
|
18
18
|
A: () => enqueueRun,
|
|
19
19
|
At: () => updateMachine,
|
|
20
20
|
B: () => getRun,
|
|
21
|
-
Bt: () =>
|
|
21
|
+
Bt: () => loopFileIssue,
|
|
22
22
|
C: () => buildLoopManifest,
|
|
23
23
|
Ct: () => setMachineOnline,
|
|
24
24
|
D: () => createLoop,
|
|
@@ -26,47 +26,47 @@ var loopTags_CnVLi70h_exports = /* @__PURE__ */ __exportAll$1({
|
|
|
26
26
|
E: () => countRuns,
|
|
27
27
|
Et: () => stopLoop,
|
|
28
28
|
F: () => getArtifactFile,
|
|
29
|
-
Ft: () =>
|
|
29
|
+
Ft: () => LoopFiles,
|
|
30
30
|
G: () => initializeCronCadence,
|
|
31
|
-
Gt: () =>
|
|
31
|
+
Gt: () => createBlobStore,
|
|
32
32
|
H: () => getTerminalReportIncidents,
|
|
33
|
-
Ht: () =>
|
|
33
|
+
Ht: () => loopFiles_exports,
|
|
34
34
|
I: () => getExactTerminalReportIncident,
|
|
35
|
-
It: () =>
|
|
35
|
+
It: () => MEMORY_CHAR_CAP,
|
|
36
36
|
J: () => listLoops,
|
|
37
|
-
Jt: () =>
|
|
37
|
+
Jt: () => runMigrations,
|
|
38
38
|
K: () => lastRun,
|
|
39
|
-
Kt: () =>
|
|
39
|
+
Kt: () => closeClient,
|
|
40
40
|
L: () => getLoop,
|
|
41
|
-
Lt: () =>
|
|
41
|
+
Lt: () => MEMORY_FILE,
|
|
42
42
|
M: () => finalizeRunningRun,
|
|
43
|
-
Mt: () =>
|
|
43
|
+
Mt: () => ARTIFACT_EXTENSIONS,
|
|
44
44
|
N: () => forceDeleteLoop,
|
|
45
|
-
Nt: () =>
|
|
45
|
+
Nt: () => EMPTY_SHA256,
|
|
46
46
|
O: () => createMachine,
|
|
47
47
|
Ot: () => tryDeleteLoop,
|
|
48
48
|
P: () => forceDeleteMachine,
|
|
49
|
-
Pt: () =>
|
|
49
|
+
Pt: () => LOOP_FILE_BYTE_CAP,
|
|
50
50
|
Q: () => liveBlobRefs,
|
|
51
|
-
Qt: () =>
|
|
51
|
+
Qt: () => logger,
|
|
52
52
|
R: () => getMachine,
|
|
53
|
-
Rt: () =>
|
|
53
|
+
Rt: () => fileLimitLabel,
|
|
54
54
|
S: () => blobSizes,
|
|
55
55
|
St: () => runningRunsForMachine,
|
|
56
56
|
T: () => countRunSnapshots,
|
|
57
57
|
Tt: () => startLoop,
|
|
58
58
|
U: () => hasPendingRun,
|
|
59
|
-
Ut: () =>
|
|
59
|
+
Ut: () => sha256Buf,
|
|
60
60
|
V: () => getRunSnapshot,
|
|
61
|
-
Vt: () =>
|
|
61
|
+
Vt: () => loopFileNames,
|
|
62
62
|
W: () => hasRunningRun,
|
|
63
|
-
Wt: () =>
|
|
63
|
+
Wt: () => validArtifactName,
|
|
64
64
|
X: () => listRuns,
|
|
65
|
-
Xt: () =>
|
|
65
|
+
Xt: () => CODING_AGENTS,
|
|
66
66
|
Y: () => listMachines,
|
|
67
|
-
Yt: () =>
|
|
67
|
+
Yt: () => runs,
|
|
68
68
|
Z: () => listRunsBefore,
|
|
69
|
-
Zt: () =>
|
|
69
|
+
Zt: () => coerceCodingAgent,
|
|
70
70
|
_: () => acknowledgeRetiredReport,
|
|
71
71
|
_t: () => releaseAbsentReconciliations,
|
|
72
72
|
a: () => TERMINAL_GRACE_MS,
|
|
@@ -77,7 +77,7 @@ var loopTags_CnVLi70h_exports = /* @__PURE__ */ __exportAll$1({
|
|
|
77
77
|
ct: () => putRunSnapshot,
|
|
78
78
|
d: () => readNewIdempotency,
|
|
79
79
|
dt: () => reclaimRun,
|
|
80
|
-
en: () =>
|
|
80
|
+
en: () => BLOB_GC_GRACE_MS,
|
|
81
81
|
et: () => loopsForMachine,
|
|
82
82
|
f: () => recordNewIdempotency,
|
|
83
83
|
ft: () => reconcileReclaimedRun,
|
|
@@ -96,19 +96,20 @@ var loopTags_CnVLi70h_exports = /* @__PURE__ */ __exportAll$1({
|
|
|
96
96
|
m: () => retireLease,
|
|
97
97
|
mt: () => recordBlob,
|
|
98
98
|
n: () => isOutdated,
|
|
99
|
+
nn: () => __exportAll,
|
|
99
100
|
nt: () => pauseLoop,
|
|
100
101
|
o: () => connectKeyValid,
|
|
101
102
|
ot: () => pruneRunSnapshots,
|
|
102
103
|
p: () => resolveLease,
|
|
103
104
|
pt: () => reconciliationStatesForRuns,
|
|
104
105
|
q: () => listArtifacts,
|
|
105
|
-
qt: () =>
|
|
106
|
+
qt: () => db,
|
|
106
107
|
r: () => isValidSemver,
|
|
107
108
|
rt: () => pauseLoopState,
|
|
108
109
|
s: () => isDeviceTokenShape,
|
|
109
110
|
st: () => putReportReceiptIfAbsent,
|
|
110
111
|
t: () => validateLoopTags,
|
|
111
|
-
tn: () =>
|
|
112
|
+
tn: () => GC_INTERVAL_MS,
|
|
112
113
|
tt: () => openRuns,
|
|
113
114
|
u: () => pruneExpiredLeases,
|
|
114
115
|
ut: () => recentLoopUsage,
|
|
@@ -121,7 +122,7 @@ var loopTags_CnVLi70h_exports = /* @__PURE__ */ __exportAll$1({
|
|
|
121
122
|
y: () => advanceDueSchedules,
|
|
122
123
|
yt: () => requestDeleteLoop,
|
|
123
124
|
z: () => getReportReceipt,
|
|
124
|
-
zt: () =>
|
|
125
|
+
zt: () => isValidHash
|
|
125
126
|
});
|
|
126
127
|
var import_pino = /* @__PURE__ */ __toESM(require_pino());
|
|
127
128
|
var import_pino_pretty = /* @__PURE__ */ __toESM(require_pino_pretty());
|
|
@@ -153,9 +154,9 @@ var BLOB_GC_GRACE_MS = 3600 * 1e3;
|
|
|
153
154
|
/** Storage-maintenance cadence (prune snapshots, then GC blobs). 15 minutes. */
|
|
154
155
|
var GC_INTERVAL_MS = 900 * 1e3;
|
|
155
156
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
157
|
+
* pino-pretty as a synchronous destination stream (not a worker-thread transport)
|
|
158
|
+
* so logs flush before process.exit() in the one-shot CLIs. Writes to stderr so
|
|
159
|
+
* stdout carries only CLI output.
|
|
159
160
|
*/
|
|
160
161
|
var logger_exports = /* @__PURE__ */ __exportAll({ logger: () => logger });
|
|
161
162
|
var level = (process.env.PIEVO_LOG_LEVEL || "info").toLowerCase();
|
|
@@ -166,17 +167,9 @@ var stream = (0, import_pino_pretty.default)({
|
|
|
166
167
|
ignore: "pid,hostname"
|
|
167
168
|
});
|
|
168
169
|
var logger = (0, import_pino.default)({ level }, stream);
|
|
169
|
-
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* integration lands; execution itself is real for every value.
|
|
173
|
-
*
|
|
174
|
-
* Runtime SINGLE SOURCE (anti-drift): every server consumer DERIVES from this
|
|
175
|
-
* array — the `CodingAgent` type here, the `db/schema.ts` `CodingAgent` type AND
|
|
176
|
-
* the `loops.agent` column enum, the edit validator (`coerceCodingAgent`), and the
|
|
177
|
-
* web agent `<select>` (LoopForm). So widening the set is a one-line edit HERE with
|
|
178
|
-
* no other server change (the daemon's own enum in `packages/daemon/src/create.ts`
|
|
179
|
-
* is a separate package, widened alongside). */
|
|
170
|
+
/** Single source for coding agents: server types, the `loops.agent` column enum,
|
|
171
|
+
* validators, and the dashboard select all derive from this array. The daemon
|
|
172
|
+
* keeps its own copy in `packages/daemon/src/create.ts`; widen both together. */
|
|
180
173
|
var CODING_AGENTS = [
|
|
181
174
|
"claude-code",
|
|
182
175
|
"codex",
|
|
@@ -196,10 +189,6 @@ var RESERVED_LOOP_TAGS = [
|
|
|
196
189
|
* Pievo is single-user: no row carries an owner. Timestamp values are ISO
|
|
197
190
|
* strings; JSON columns use `jsonb().$type<>()`.
|
|
198
191
|
*/
|
|
199
|
-
/** `skipped`: a pending run retired without executing (pause/retention cleanup,
|
|
200
|
-
* queue coalescing, or the offline catch-up window elapsed).
|
|
201
|
-
* Neither success nor failure — excluded from the failure streak (it rides
|
|
202
|
-
* phase `canceled`, and the streak counts only phase `error`). */
|
|
203
192
|
var machines = pgTable("machines", {
|
|
204
193
|
id: text("id").primaryKey(),
|
|
205
194
|
name: text("name").notNull(),
|
|
@@ -231,8 +220,8 @@ var loops = pgTable("loops", {
|
|
|
231
220
|
statusKeep: text("status_keep").notNull(),
|
|
232
221
|
statusNoChange: text("status_no_change").notNull(),
|
|
233
222
|
statusBlock: text("status_block").notNull(),
|
|
234
|
-
/** Configured artifact
|
|
235
|
-
*
|
|
223
|
+
/** Configured artifact names, in addition to the always-present MEMORY.md.
|
|
224
|
+
* See `gateway/loopFiles.ts`. */
|
|
236
225
|
artifacts: jsonb("artifacts").$type().notNull().default([]),
|
|
237
226
|
cron: text("cron").notNull(),
|
|
238
227
|
scheduleMode: text("schedule_mode", { enum: ["cron", "continuous"] }).notNull(),
|
|
@@ -249,11 +238,8 @@ var loops = pgTable("loops", {
|
|
|
249
238
|
pauseCause: jsonb("pause_cause").$type(),
|
|
250
239
|
model: text("model"),
|
|
251
240
|
reasoningEffort: text("reasoning_effort"),
|
|
252
|
-
/**
|
|
253
|
-
*
|
|
254
|
-
* `codex` → `codex exec`, `pi` → Pi print mode). Measured from the creating CLI's env when
|
|
255
|
-
* detectable, else the declared/selected value. The text column carries
|
|
256
|
-
* both a TypeScript enum and a database CHECK constraint. */
|
|
241
|
+
/** The daemon launches this agent for every run (`buildAgentSpawn`). Enforced by
|
|
242
|
+
* both the TypeScript enum and a database CHECK constraint. */
|
|
257
243
|
agent: text("agent", { enum: CODING_AGENTS }).notNull(),
|
|
258
244
|
enabled: boolean("enabled").notNull().default(true),
|
|
259
245
|
nextRunAt: text("next_run_at"),
|
|
@@ -281,6 +267,9 @@ var runs = pgTable("runs", {
|
|
|
281
267
|
model: text("model"),
|
|
282
268
|
reasoningEffort: text("reasoning_effort"),
|
|
283
269
|
runIndex: integer("run_index"),
|
|
270
|
+
/** A pending run retired without executing (pause, retention cleanup, queue
|
|
271
|
+
* coalescing, or an elapsed offline catch-up window) is `canceled`, not `error`,
|
|
272
|
+
* so it never counts toward the failure streak. */
|
|
284
273
|
phase: text("phase", { enum: [
|
|
285
274
|
"pending",
|
|
286
275
|
"running",
|
|
@@ -300,6 +289,9 @@ var runs = pgTable("runs", {
|
|
|
300
289
|
"block"
|
|
301
290
|
] }),
|
|
302
291
|
message: text("message"),
|
|
292
|
+
/** Owner-facing reason from the final response's ATTENTION line. Independent
|
|
293
|
+
* of status: it never pauses the loop. */
|
|
294
|
+
attention: text("attention"),
|
|
303
295
|
durationMs: integer("duration_ms"),
|
|
304
296
|
exitCode: integer("exit_code"),
|
|
305
297
|
finalText: text("final_text"),
|
|
@@ -403,7 +395,6 @@ var blobs = pgTable("blobs", {
|
|
|
403
395
|
var artifactFiles = pgTable("artifact_files", {
|
|
404
396
|
id: text("id").primaryKey(),
|
|
405
397
|
loopId: text("loop_id").notNull(),
|
|
406
|
-
/** Loop file name: MEMORY.md or a configured artifact name. */
|
|
407
398
|
path: text("path").notNull(),
|
|
408
399
|
hash: text("hash"),
|
|
409
400
|
size: integer("size"),
|
|
@@ -486,7 +477,6 @@ function migrationsDir() {
|
|
|
486
477
|
if (fs.existsSync(builtDir)) return builtDir;
|
|
487
478
|
return devDir;
|
|
488
479
|
}
|
|
489
|
-
/** Apply generated migrations in-process (idempotent). */
|
|
490
480
|
async function runMigrations() {
|
|
491
481
|
const dir = migrationsDir();
|
|
492
482
|
if (!fs.existsSync(dir)) return;
|
|
@@ -637,7 +627,7 @@ function createBlobStore() {
|
|
|
637
627
|
* Loop files: a loop's server-owned memory and display artifacts.
|
|
638
628
|
*
|
|
639
629
|
* Every loop has the required `MEMORY.md` plus up to MAX_ARTIFACTS configured
|
|
640
|
-
*
|
|
630
|
+
* text/code artifact names. The server is authoritative: bytes live in the
|
|
641
631
|
* content-addressed BlobStore and the current version of each name is an
|
|
642
632
|
* `artifact_files` row. For each run the daemon downloads the current files into
|
|
643
633
|
* a private run directory, and the terminal report carries the changed versions
|
|
@@ -651,6 +641,7 @@ function createBlobStore() {
|
|
|
651
641
|
*/
|
|
652
642
|
var loopFiles_exports = /* @__PURE__ */ __exportAll({
|
|
653
643
|
ARTIFACT_BYTE_CAP: () => ARTIFACT_BYTE_CAP,
|
|
644
|
+
ARTIFACT_EXTENSIONS: () => ARTIFACT_EXTENSIONS,
|
|
654
645
|
EMPTY_SHA256: () => EMPTY_SHA256,
|
|
655
646
|
LOOP_FILE_BYTE_CAP: () => LOOP_FILE_BYTE_CAP,
|
|
656
647
|
LoopFiles: () => LoopFiles,
|
|
@@ -668,26 +659,107 @@ var MEMORY_FILE = "MEMORY.md";
|
|
|
668
659
|
/** Memory is re-read by every run, so it is bounded in characters (Unicode code
|
|
669
660
|
* points), roughly 2k tokens of prompt context per run. */
|
|
670
661
|
var MEMORY_CHAR_CAP = 8e3;
|
|
671
|
-
/**
|
|
672
|
-
var ARTIFACT_BYTE_CAP =
|
|
662
|
+
/** 256 KB bounds a run's upload and keeps dashboard rendering smooth. */
|
|
663
|
+
var ARTIFACT_BYTE_CAP = 256 * 1024;
|
|
664
|
+
var ARTIFACT_LIMIT_LABEL = `${ARTIFACT_BYTE_CAP / 1024} KB`;
|
|
673
665
|
/** sha256 of zero bytes: the version of a loop file that has never been written. */
|
|
674
666
|
var EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
|
675
|
-
|
|
667
|
+
/** Text/code formats accepted as artifacts. Loop files are NUL-free text, so
|
|
668
|
+
* binary formats (images, PDFs, archives) are deliberately absent; `.svg` is
|
|
669
|
+
* text and renders through the hardened inline image route. */
|
|
670
|
+
var ARTIFACT_EXTENSIONS = [
|
|
671
|
+
"md",
|
|
672
|
+
"markdown",
|
|
673
|
+
"mdx",
|
|
674
|
+
"html",
|
|
675
|
+
"htm",
|
|
676
|
+
"txt",
|
|
677
|
+
"text",
|
|
678
|
+
"rst",
|
|
679
|
+
"adoc",
|
|
680
|
+
"org",
|
|
681
|
+
"sh",
|
|
682
|
+
"bash",
|
|
683
|
+
"zsh",
|
|
684
|
+
"fish",
|
|
685
|
+
"ps1",
|
|
686
|
+
"bat",
|
|
687
|
+
"cmd",
|
|
688
|
+
"py",
|
|
689
|
+
"rb",
|
|
690
|
+
"pl",
|
|
691
|
+
"lua",
|
|
692
|
+
"r",
|
|
693
|
+
"js",
|
|
694
|
+
"mjs",
|
|
695
|
+
"cjs",
|
|
696
|
+
"ts",
|
|
697
|
+
"tsx",
|
|
698
|
+
"jsx",
|
|
699
|
+
"go",
|
|
700
|
+
"rs",
|
|
701
|
+
"java",
|
|
702
|
+
"kt",
|
|
703
|
+
"kts",
|
|
704
|
+
"scala",
|
|
705
|
+
"c",
|
|
706
|
+
"h",
|
|
707
|
+
"cc",
|
|
708
|
+
"cpp",
|
|
709
|
+
"hpp",
|
|
710
|
+
"cs",
|
|
711
|
+
"php",
|
|
712
|
+
"swift",
|
|
713
|
+
"dart",
|
|
714
|
+
"ex",
|
|
715
|
+
"exs",
|
|
716
|
+
"erl",
|
|
717
|
+
"hs",
|
|
718
|
+
"clj",
|
|
719
|
+
"cljs",
|
|
720
|
+
"json",
|
|
721
|
+
"jsonl",
|
|
722
|
+
"yaml",
|
|
723
|
+
"yml",
|
|
724
|
+
"toml",
|
|
725
|
+
"xml",
|
|
726
|
+
"csv",
|
|
727
|
+
"tsv",
|
|
728
|
+
"svg",
|
|
729
|
+
"css",
|
|
730
|
+
"scss",
|
|
731
|
+
"sass",
|
|
732
|
+
"less",
|
|
733
|
+
"sql",
|
|
734
|
+
"ini",
|
|
735
|
+
"conf",
|
|
736
|
+
"cfg",
|
|
737
|
+
"env",
|
|
738
|
+
"properties",
|
|
739
|
+
"diff",
|
|
740
|
+
"patch",
|
|
741
|
+
"tex",
|
|
742
|
+
"log"
|
|
743
|
+
];
|
|
744
|
+
var ARTIFACT_EXTENSION_SET = new Set(ARTIFACT_EXTENSIONS);
|
|
745
|
+
var ARTIFACT_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}\.([A-Za-z0-9]{1,16})$/;
|
|
676
746
|
function sha256Buf(bytes) {
|
|
677
747
|
return createHash("sha256").update(bytes).digest("hex");
|
|
678
748
|
}
|
|
679
749
|
function isValidHash(hash) {
|
|
680
750
|
return typeof hash === "string" && /^[0-9a-f]{64}$/.test(hash);
|
|
681
751
|
}
|
|
682
|
-
/** A plain file name (no directories)
|
|
752
|
+
/** A plain text/code file name (no directories) other than MEMORY.md. */
|
|
683
753
|
function validArtifactName(name) {
|
|
684
|
-
|
|
754
|
+
if (typeof name !== "string" || name.toLowerCase() === "MEMORY.md".toLowerCase()) return false;
|
|
755
|
+
const match = ARTIFACT_NAME_RE.exec(name);
|
|
756
|
+
return match !== null && ARTIFACT_EXTENSION_SET.has(match[1].toLowerCase());
|
|
685
757
|
}
|
|
686
758
|
function loopFileNames(loop) {
|
|
687
759
|
return [MEMORY_FILE, ...loop.artifacts];
|
|
688
760
|
}
|
|
689
761
|
function fileLimitLabel(name) {
|
|
690
|
-
return name === "MEMORY.md" ? `${MEMORY_CHAR_CAP} characters` :
|
|
762
|
+
return name === "MEMORY.md" ? `${MEMORY_CHAR_CAP} characters` : ARTIFACT_LIMIT_LABEL;
|
|
691
763
|
}
|
|
692
764
|
/** Why `bytes` cannot become the next version of `name`, or undefined if it can. */
|
|
693
765
|
function loopFileIssue(name, bytes) {
|
|
@@ -697,7 +769,7 @@ function loopFileIssue(name, bytes) {
|
|
|
697
769
|
if (chars > 8e3) return `${name}: ${chars} characters exceeds the ${MEMORY_CHAR_CAP}-character limit`;
|
|
698
770
|
return;
|
|
699
771
|
}
|
|
700
|
-
if (bytes.length >
|
|
772
|
+
if (bytes.length > 262144) return `${name}: ${(bytes.length / 1024).toFixed(1)} KB exceeds the ${ARTIFACT_LIMIT_LABEL} limit`;
|
|
701
773
|
}
|
|
702
774
|
/** The largest byte size any loop file can have (a code point is at most 4 bytes). */
|
|
703
775
|
var LOOP_FILE_BYTE_CAP = Math.max(ARTIFACT_BYTE_CAP, MEMORY_CHAR_CAP * 4);
|
|
@@ -721,10 +793,7 @@ var LoopFiles = class {
|
|
|
721
793
|
return hash === "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" || this.blobStore.has(hash);
|
|
722
794
|
}
|
|
723
795
|
};
|
|
724
|
-
/**
|
|
725
|
-
* Data-access layer over Drizzle. Loop-locked state transitions live here; every
|
|
726
|
-
* DB-touching function is async, while the pure helpers stay sync.
|
|
727
|
-
*/
|
|
796
|
+
/** Data-access layer over Drizzle. Loop-locked state transitions live here. */
|
|
728
797
|
var store_exports = /* @__PURE__ */ __exportAll({
|
|
729
798
|
TERMINAL_REPORT_GRACE_MS: () => TERMINAL_REPORT_GRACE_MS,
|
|
730
799
|
acknowledgeRetiredReport: () => acknowledgeRetiredReport,
|
|
@@ -1928,19 +1997,15 @@ async function countRunSnapshots() {
|
|
|
1928
1997
|
return Number(r?.n ?? 0);
|
|
1929
1998
|
}
|
|
1930
1999
|
/**
|
|
1931
|
-
* Machine
|
|
1932
|
-
*
|
|
1933
|
-
*
|
|
1934
|
-
*
|
|
1935
|
-
* against it. Its lifecycle is a small state machine (`active` →
|
|
1936
|
-
* `terminal-grace` → `reconciliation-only`/`retired`), not a mint→revoke pair;
|
|
1937
|
-
* see `RunLease` below.
|
|
2000
|
+
* Machine and run credentials. A device token (`dk_…`) identifies a machine whose
|
|
2001
|
+
* id is derived from it (`m-sha256(token)[:16]`). A run lease (`rk_…`) is minted per
|
|
2002
|
+
* delivery and authorizes only that run's loop-file transfer and terminal report;
|
|
2003
|
+
* its lifecycle is the state machine on `RunLease`.
|
|
1938
2004
|
*
|
|
1939
|
-
* Leases and connect-key bindings are
|
|
1940
|
-
*
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
1943
|
-
* losing it only degrades retry deduplication, never persisted data.
|
|
2005
|
+
* Leases and connect-key bindings are durable so a restart neither rejects
|
|
2006
|
+
* in-flight reports nor blocks a just-connected machine from enrolling. The
|
|
2007
|
+
* 15-minute `new` idempotency window stays in memory; losing it on restart only
|
|
2008
|
+
* weakens retry deduplication.
|
|
1944
2009
|
*/
|
|
1945
2010
|
var tokens_exports = /* @__PURE__ */ __exportAll({
|
|
1946
2011
|
CONNECT_KEY_TTL_MS: () => CONNECT_KEY_TTL_MS,
|
|
@@ -2203,8 +2268,8 @@ function validateLoopTags(value) {
|
|
|
2203
2268
|
};
|
|
2204
2269
|
}
|
|
2205
2270
|
//#endregion
|
|
2206
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/boot-
|
|
2207
|
-
var
|
|
2271
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/boot-CecCDRoH.js
|
|
2272
|
+
var boot_CecCDRoH_exports = /* @__PURE__ */ __exportAll$1({
|
|
2208
2273
|
_: () => validOptionalPollString,
|
|
2209
2274
|
a: () => POLL_HTTP_BODY_FIELDS,
|
|
2210
2275
|
c: () => MIN_DAEMON_VERSION,
|
|
@@ -2296,11 +2361,8 @@ function validOptionalPollString(value, cap) {
|
|
|
2296
2361
|
function nowIso() {
|
|
2297
2362
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
2298
2363
|
}
|
|
2299
|
-
/**
|
|
2300
|
-
*
|
|
2301
|
-
* throw mid-finalize on the DB write. The single sanitizing primitive behind
|
|
2302
|
-
* `clipText` - and used directly by `cli.ts`
|
|
2303
|
-
* (parseFlags/validateState, the same one-chokepoint discipline). */
|
|
2364
|
+
/** Postgres text/jsonb columns reject NUL, so a daemon-supplied string carrying
|
|
2365
|
+
* one would fail mid-finalize. The single sanitizer behind `clipText` and `cli.ts`. */
|
|
2304
2366
|
function stripNul(s) {
|
|
2305
2367
|
return s.replace(/\u0000/g, "");
|
|
2306
2368
|
}
|
|
@@ -2311,13 +2373,14 @@ function clipText(s, cap) {
|
|
|
2311
2373
|
* directory before launch (and exports as an env var of the same name). */
|
|
2312
2374
|
var LOOP_DIR_PLACEHOLDER = "$PIEVO_LOOP_DIR";
|
|
2313
2375
|
var STATUS_LINE_RE = /^STATUS:\s*(keep|no-change|block)\.?$/i;
|
|
2376
|
+
var ATTENTION_LINE_RE = /^ATTENTION:\s*(.*)$/i;
|
|
2314
2377
|
/**
|
|
2315
2378
|
* Deliver the user's prompt unchanged, followed by the status definitions, the
|
|
2316
2379
|
* loop-file contract, and the final-line status contract. The run index
|
|
2317
2380
|
* intentionally remains an internal history fact.
|
|
2318
2381
|
*/
|
|
2319
2382
|
function buildRunTask(loop, previousNotices = []) {
|
|
2320
|
-
const files = [`- ${MEMORY_FILE}: this loop's
|
|
2383
|
+
const files = [`- ${MEMORY_FILE}: this loop's long-term experience (max ${MEMORY_CHAR_CAP} characters). Read it first and follow it. Before finishing, record only lessons this run learned the hard way that make later runs faster or safer: a pitfall hit, a workaround found, an environment fact discovered. One line each; correct or delete lessons that proved wrong. Never restate the task or these instructions, and never store state, results, baselines, trends, or run logs (those go in an artifact). Most runs add nothing.`, ...loop.artifacts.map((name) => `- ${name}: artifact shown on the Pievo dashboard (max ${fileLimitLabel(name)}). Update it only as the task above asks.`)];
|
|
2321
2384
|
const notices = previousNotices.length ? `\n\nNotices from the previous run:\n${previousNotices.map((notice) => `- ${notice}`).join("\n")}` : "";
|
|
2322
2385
|
return `${loop.prompt}
|
|
2323
2386
|
|
|
@@ -2325,18 +2388,23 @@ Status definitions:
|
|
|
2325
2388
|
- keep: ${loop.statusKeep}
|
|
2326
2389
|
- no-change: ${loop.statusNoChange}
|
|
2327
2390
|
- block: ${loop.statusBlock}
|
|
2391
|
+
A block pauses the loop until the owner restarts it.
|
|
2328
2392
|
|
|
2329
2393
|
Loop files in ${LOOP_DIR_PLACEHOLDER}:
|
|
2330
2394
|
${files.join("\n")}
|
|
2331
2395
|
A file over its limit is rejected and its previous version kept. Other files in that directory are discarded.${notices}
|
|
2332
2396
|
|
|
2333
|
-
|
|
2397
|
+
Be concise in everything you write: lead with what changed or needs action; prefer tables and short lists; no filler or restated instructions.
|
|
2398
|
+
|
|
2399
|
+
Your final response is only: a summary of at most five short lines (no headings, no repeated tables or file contents); if the owner should look at something, one line \`ATTENTION: <reason>\`; then exactly one last line:
|
|
2334
2400
|
STATUS: keep|no-change|block`;
|
|
2335
2401
|
}
|
|
2402
|
+
var bareLine = (line) => line.trim().replace(/^[*_`>\s]+|[*_`\s]+$/g, "");
|
|
2336
2403
|
/**
|
|
2337
|
-
* Split a provider's final response into the reported status
|
|
2338
|
-
*
|
|
2339
|
-
*
|
|
2404
|
+
* Split a provider's final response into the reported status, an optional
|
|
2405
|
+
* attention reason from the ATTENTION lines just above it, and the summary. The final
|
|
2406
|
+
* non-empty line must be the status line; markdown emphasis or code ticks around
|
|
2407
|
+
* either line are tolerated because models add them unprompted.
|
|
2340
2408
|
*/
|
|
2341
2409
|
function parseFinalStatus(finalText) {
|
|
2342
2410
|
if (!finalText) return void 0;
|
|
@@ -2344,12 +2412,26 @@ function parseFinalStatus(finalText) {
|
|
|
2344
2412
|
let index = lines.length - 1;
|
|
2345
2413
|
while (index >= 0 && lines[index].trim() === "") index--;
|
|
2346
2414
|
if (index < 0) return void 0;
|
|
2347
|
-
const
|
|
2348
|
-
const match = STATUS_LINE_RE.exec(bare);
|
|
2415
|
+
const match = STATUS_LINE_RE.exec(bareLine(lines[index]));
|
|
2349
2416
|
if (!match) return void 0;
|
|
2417
|
+
const status = match[1].toLowerCase();
|
|
2418
|
+
const reasons = [];
|
|
2419
|
+
let end = index;
|
|
2420
|
+
for (;;) {
|
|
2421
|
+
let previous = end;
|
|
2422
|
+
while (previous > 0 && lines[previous - 1].trim() === "") previous--;
|
|
2423
|
+
const attentionMatch = previous > 0 ? ATTENTION_LINE_RE.exec(bareLine(lines[previous - 1])) : null;
|
|
2424
|
+
if (!attentionMatch) break;
|
|
2425
|
+
end = previous - 1;
|
|
2426
|
+
const reason = attentionMatch[1].trim();
|
|
2427
|
+
if (reason && !/^(none|n\/a|-)$/i.test(reason)) reasons.unshift(reason);
|
|
2428
|
+
}
|
|
2429
|
+
const joined = reasons.join("; ");
|
|
2430
|
+
const attention = joined ? joined.length > 200 ? `${joined.slice(0, 199)}…` : joined : null;
|
|
2350
2431
|
return {
|
|
2351
|
-
status
|
|
2352
|
-
message: lines.slice(0,
|
|
2432
|
+
status,
|
|
2433
|
+
message: lines.slice(0, end).join("\n").trim(),
|
|
2434
|
+
attention
|
|
2353
2435
|
};
|
|
2354
2436
|
}
|
|
2355
2437
|
async function buildDelivery(loop, queuedRun, runToken, roots) {
|
|
@@ -2373,31 +2455,11 @@ async function buildDelivery(loop, queuedRun, runToken, roots) {
|
|
|
2373
2455
|
};
|
|
2374
2456
|
}
|
|
2375
2457
|
/**
|
|
2376
|
-
* Loop-file storage retention
|
|
2377
|
-
*
|
|
2378
|
-
*
|
|
2379
|
-
*
|
|
2380
|
-
*
|
|
2381
|
-
* the OLD blob is no longer pointed at by the current file row — but nothing was
|
|
2382
|
-
* reclaiming it, so the byte store grew forever. This module closes that gap with two pieces:
|
|
2383
|
-
*
|
|
2384
|
-
* 1. pruneSnapshots() — bound the per-loop run-snapshot history to a window, so
|
|
2385
|
-
* old snapshots stop pinning the blobs they referenced for diffs.
|
|
2386
|
-
* 2. gcBlobs() — delete blob bytes no LIVE row needs: a blob is collected
|
|
2387
|
-
* only when NO artifact_files row AND NO retained run_snapshot references its
|
|
2388
|
-
* hash. CORRECTNESS over aggressiveness — a still-referenced blob is never
|
|
2389
|
-
* deleted; a leaked blob is only a cost bug the next pass reclaims.
|
|
2390
|
-
*
|
|
2391
|
-
* Concurrency: gcBlobs is safe alongside owner writes and run uploads. Three guards
|
|
2392
|
-
* combine — a grace window (a blob whose metadata row is younger than the window is
|
|
2393
|
-
* never collected, so a blob a run just uploaded is untouchable), a point re-check
|
|
2394
|
-
* of each candidate immediately before its bytes are deleted (so a blob
|
|
2395
|
-
* re-referenced before we touch it keeps both its bytes and metadata), and a
|
|
2396
|
-
* bytes-before-metadata delete ordering: each candidate's bytes are reclaimed
|
|
2397
|
-
* FIRST, then its metadata row is dropped unconditionally — so blobExists() goes
|
|
2398
|
-
* false and any writer that re-references the hash (even one that raced the byte
|
|
2399
|
-
* delete) re-uploads the bytes (self-healing). We never leave a live blobs row
|
|
2400
|
-
* pointing at deleted bytes.
|
|
2458
|
+
* Loop-file storage retention. Blob bytes are content-addressed and shared, so
|
|
2459
|
+
* replacing a file never frees its old blob by itself. pruneSnapshots() bounds
|
|
2460
|
+
* per-loop snapshot history; gcBlobs() then deletes bytes no file row and no
|
|
2461
|
+
* retained snapshot references. Correctness wins over aggressiveness: a leaked
|
|
2462
|
+
* blob is a storage cost the next pass reclaims, a deleted live blob is data loss.
|
|
2401
2463
|
*/
|
|
2402
2464
|
var log$2 = logger.child({ mod: "retention" });
|
|
2403
2465
|
/**
|
|
@@ -2411,28 +2473,14 @@ async function pruneSnapshots(keep = 20) {
|
|
|
2411
2473
|
return pruned;
|
|
2412
2474
|
}
|
|
2413
2475
|
/**
|
|
2414
|
-
* Reclaim unreferenced blob bytes
|
|
2415
|
-
*
|
|
2416
|
-
*
|
|
2417
|
-
*
|
|
2418
|
-
*
|
|
2419
|
-
*
|
|
2420
|
-
*
|
|
2421
|
-
*
|
|
2422
|
-
* a. Pre-delete guard: if a sync re-referenced it since the keep-set was
|
|
2423
|
-
* computed, skip it — leaving both its bytes AND metadata row intact
|
|
2424
|
-
* (a consistent live blob, nothing to heal). The guard is two O(1) checks:
|
|
2425
|
-
* an indexed artifact_files point query (always fresh) plus membership in a
|
|
2426
|
-
* snapshot-hash Set deserialized ONCE per pass and rebuilt only when the
|
|
2427
|
-
* snapshot row count changes (a report() raced us) — never the old
|
|
2428
|
-
* per-candidate full snapshot scan (which was O(garbage × snapshots), since
|
|
2429
|
-
* genuine garbage always misses the file check and fell through to it).
|
|
2430
|
-
* b. Otherwise delete the BYTES (the await), then drop the metadata row
|
|
2431
|
-
* UNCONDITIONALLY. Bytes-before-metadata is the data-loss fix: if a sync
|
|
2432
|
-
* raced the byte delete (re-referencing the hash + recreating the blobs
|
|
2433
|
-
* row mid-await), dropping the metadata still leaves blobExists()=false,
|
|
2434
|
-
* so that file re-uploads its bytes on the next sync. We never leave a
|
|
2435
|
-
* live blobs row pointing at deleted bytes.
|
|
2476
|
+
* Reclaim unreferenced blob bytes; returns the number collected. Safe alongside
|
|
2477
|
+
* owner writes and run uploads because of three guards:
|
|
2478
|
+
* - grace window: blobs whose metadata row is younger than `graceMs` are skipped;
|
|
2479
|
+
* - per-candidate re-check right before deletion: an indexed file-row lookup plus
|
|
2480
|
+
* a snapshot-hash set rebuilt only when the snapshot count changes (a concurrent
|
|
2481
|
+
* report), instead of an O(garbage x snapshots) scan;
|
|
2482
|
+
* - bytes before metadata: the metadata row is dropped after the bytes even if a
|
|
2483
|
+
* writer raced the delete, so blobExists() is false and that writer re-uploads.
|
|
2436
2484
|
*/
|
|
2437
2485
|
async function gcBlobs(blobStore, graceMs = BLOB_GC_GRACE_MS) {
|
|
2438
2486
|
const refs = await liveBlobRefs();
|
|
@@ -2816,7 +2864,7 @@ function validateArtifactNames(value) {
|
|
|
2816
2864
|
for (const entry of value) {
|
|
2817
2865
|
if (!validArtifactName(entry)) return {
|
|
2818
2866
|
ok: false,
|
|
2819
|
-
detail: `artifact must be a plain
|
|
2867
|
+
detail: `artifact must be a plain text/code file name other than ${MEMORY_FILE} (supported extensions: ${ARTIFACT_EXTENSIONS.join(", ")}; letters, digits, . _ -; at most 64 characters before the extension): ${JSON.stringify(entry)}`
|
|
2820
2868
|
};
|
|
2821
2869
|
const key = entry.toLowerCase();
|
|
2822
2870
|
if (seen.has(key)) return {
|
|
@@ -3077,22 +3125,16 @@ var validPollWireId = (value) => typeof value === "string" && value.length <= 20
|
|
|
3077
3125
|
var validPollCurrentRun = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 2 && Object.prototype.hasOwnProperty.call(value, "runId") && Object.prototype.hasOwnProperty.call(value, "stage") && validPollWireId(value.runId) && (value.stage === "executing" || value.stage === "reporting");
|
|
3078
3126
|
var validPollInfo = (value) => value === void 0 || value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).every((key) => POLL_INFO_FIELDS.has(key)) && validOptionalPollString(value.host, 524288) && validOptionalPollString(value.platform, 524288) && validOptionalPollString(value.arch, 524288) && validOptionalPollString(value.version, 64);
|
|
3079
3127
|
/**
|
|
3080
|
-
* Machine gateway
|
|
3081
|
-
*
|
|
3082
|
-
* long-poll while idle). Framework-agnostic like the rest of the gateway
|
|
3083
|
-
* (return `{ status, body }` so the methods can be mounted on a plain http
|
|
3084
|
-
* server or TanStack server routes):
|
|
3128
|
+
* Machine gateway: the run-lifecycle core the daemon talks to. Methods return
|
|
3129
|
+
* transport-free `{ status, body }` results for the thin route shells:
|
|
3085
3130
|
*
|
|
3086
|
-
* POST /api/machine/poll
|
|
3087
|
-
* POST /machine/report
|
|
3088
|
-
* GET|PUT /api/machine/run-files/:name
|
|
3131
|
+
* POST /api/machine/poll (device token) claim and deliver runs
|
|
3132
|
+
* POST /machine/report (run token) finalize a run
|
|
3133
|
+
* GET|PUT /api/machine/run-files/:name (run token) loop-file transfer
|
|
3089
3134
|
*
|
|
3090
|
-
*
|
|
3091
|
-
*
|
|
3092
|
-
*
|
|
3093
|
-
* CLI verb dispatch at `/api/machine/cli` lives in `gateway/cli.ts`
|
|
3094
|
-
* (`CliGateway`), which reuses this class's owner methods;
|
|
3095
|
-
* canonical loop configuration validation lives in `gateway/loopConfig.ts`.
|
|
3135
|
+
* Idle polls may be held open (long-poll) until the scheduler's dispatcher wakes
|
|
3136
|
+
* them. `sweep()` marks stale machines offline and reclaims silent runs. CLI verbs
|
|
3137
|
+
* live in `cli.ts`; loop config validation lives in `loopConfig.ts`.
|
|
3096
3138
|
*/
|
|
3097
3139
|
var log$1 = logger.child({ mod: "gateway" });
|
|
3098
3140
|
var ONLINE_TTL_MS = 3e4;
|
|
@@ -3269,6 +3311,7 @@ function reportOutcome(body) {
|
|
|
3269
3311
|
patch: {
|
|
3270
3312
|
status: parsed.status,
|
|
3271
3313
|
message: clipText(parsed.message, 2e3) || null,
|
|
3314
|
+
attention: parsed.attention,
|
|
3272
3315
|
error: null
|
|
3273
3316
|
}
|
|
3274
3317
|
};
|
|
@@ -3352,7 +3395,6 @@ var MachineGateway = class {
|
|
|
3352
3395
|
this.blobStore = blobStore;
|
|
3353
3396
|
this.loopFiles = new LoopFiles(blobStore);
|
|
3354
3397
|
}
|
|
3355
|
-
/** Loop-file bytes over the same blob store that maintenance garbage-collects. */
|
|
3356
3398
|
loopFiles;
|
|
3357
3399
|
/** In-flight latch: the maintenance pass is sequential and the first post-deploy
|
|
3358
3400
|
* backlog reclamation can overrun the interval, so a fresh tick skips rather than
|
|
@@ -3430,20 +3472,12 @@ var MachineGateway = class {
|
|
|
3430
3472
|
await pruneExpiredLeases(now);
|
|
3431
3473
|
for (const loop of await listLoops()) if (loop.deleteRequestedAt) await tryDeleteLoop(loop.id);
|
|
3432
3474
|
}
|
|
3433
|
-
/**
|
|
3434
|
-
*
|
|
3435
|
-
*
|
|
3436
|
-
*
|
|
3437
|
-
*
|
|
3438
|
-
*
|
|
3439
|
-
* (often SUCCESSFUL) result. Retiring the lease here would 401 that late report
|
|
3440
|
-
* and strand the run as a permanent false failure with its message lost (the
|
|
3441
|
-
* investigated bug). So the lease survives a bounded grace window
|
|
3442
|
-
* (`TERMINAL_GRACE_MS`) during which exactly ONE late wake-report may reconcile
|
|
3443
|
-
* the run — see `report()`'s terminal-grace branch. The credential is still
|
|
3444
|
-
* bounded: CLI mutations are refused while terminal-grace, and the
|
|
3445
|
-
* reconciliation retires the lease single-shot. Pending rows are durable inbox
|
|
3446
|
-
* entries and are never reclaimed by this path. */
|
|
3475
|
+
/** Reclaim one silent running run as an error, but terminalize its lease instead
|
|
3476
|
+
* of retiring it. The usual cause is a laptop that fell asleep mid-run; on wake
|
|
3477
|
+
* the daemon delivers the real (often successful) result, and a retired lease
|
|
3478
|
+
* would 401 it and leave a false failure. During TERMINAL_GRACE_MS exactly one
|
|
3479
|
+
* late report may reconcile the run (see `report()`); CLI mutations stay refused.
|
|
3480
|
+
* Pending rows are durable queue entries and never reach this path. */
|
|
3447
3481
|
async reclaimRun(run, reason) {
|
|
3448
3482
|
if (run.phase !== "running") return;
|
|
3449
3483
|
const at = nowIso();
|
|
@@ -3629,11 +3663,8 @@ var MachineGateway = class {
|
|
|
3629
3663
|
waiter.cancel();
|
|
3630
3664
|
}
|
|
3631
3665
|
}
|
|
3632
|
-
/**
|
|
3633
|
-
*
|
|
3634
|
-
* Claude Code can avoid starting a duplicate. `online` is fresh-checked against
|
|
3635
|
-
* the poll TTL, not just the stored flag.
|
|
3636
|
-
*/
|
|
3666
|
+
/** Whether this machine has a live daemon. `online` is fresh-checked against the
|
|
3667
|
+
* poll TTL, not just the stored flag. */
|
|
3637
3668
|
async status(deviceToken) {
|
|
3638
3669
|
const auth = await authenticateDeviceToken(deviceToken, { allowUnknown: true });
|
|
3639
3670
|
if (!auth.ok) return auth.response;
|
|
@@ -4694,6 +4725,7 @@ function normalizedListRun(run) {
|
|
|
4694
4725
|
terminalAt: run.ts,
|
|
4695
4726
|
phase: run.phase,
|
|
4696
4727
|
status: run.status ?? null,
|
|
4728
|
+
attention: run.attention ?? null,
|
|
4697
4729
|
durationMs: run.durationMs ?? null,
|
|
4698
4730
|
tokenUsage: totalTokenUsage(run.usage ?? null),
|
|
4699
4731
|
message: message.value,
|
|
@@ -4748,6 +4780,7 @@ async function listHistory(loop, q) {
|
|
|
4748
4780
|
"terminal",
|
|
4749
4781
|
"phase",
|
|
4750
4782
|
"status",
|
|
4783
|
+
"attention",
|
|
4751
4784
|
"durationMs",
|
|
4752
4785
|
"tokenUsage",
|
|
4753
4786
|
"message",
|
|
@@ -4757,6 +4790,7 @@ async function listHistory(loop, q) {
|
|
|
4757
4790
|
fmt(run.ts),
|
|
4758
4791
|
run.phase,
|
|
4759
4792
|
run.status ?? null,
|
|
4793
|
+
run.attention ?? null,
|
|
4760
4794
|
run.durationMs ?? null,
|
|
4761
4795
|
totalTokenUsage(run.usage ?? null),
|
|
4762
4796
|
run.message ? truncate(run.message, 500, "use --run for detail").value : null,
|
|
@@ -4804,6 +4838,7 @@ async function detailHistory(loop, q) {
|
|
|
4804
4838
|
terminalAt: TERMINAL_PHASES.includes(run.phase) ? run.ts : null,
|
|
4805
4839
|
phase: run.phase,
|
|
4806
4840
|
status: run.status ?? null,
|
|
4841
|
+
attention: run.attention ?? null,
|
|
4807
4842
|
durationMs: run.durationMs ?? null,
|
|
4808
4843
|
tokenUsage: totalTokenUsage(run.usage ?? null),
|
|
4809
4844
|
message: message.value,
|
|
@@ -4830,6 +4865,7 @@ async function detailHistory(loop, q) {
|
|
|
4830
4865
|
["terminalAt", detail.terminalAt],
|
|
4831
4866
|
["phase", detail.phase],
|
|
4832
4867
|
["status", detail.status],
|
|
4868
|
+
["attention", detail.attention],
|
|
4833
4869
|
["durationMs", detail.durationMs],
|
|
4834
4870
|
["tokenUsage", detail.tokenUsage],
|
|
4835
4871
|
["message", detail.message],
|
|
@@ -4853,18 +4889,10 @@ async function readLoopHistory(loop, flags) {
|
|
|
4853
4889
|
return parsed.value.mode === "detail" ? detailHistory(loop, parsed.value) : listHistory(loop, parsed.value);
|
|
4854
4890
|
}
|
|
4855
4891
|
/**
|
|
4856
|
-
*
|
|
4857
|
-
*
|
|
4858
|
-
*
|
|
4859
|
-
*
|
|
4860
|
-
*
|
|
4861
|
-
* POST /api/machine/cli (Bearer device OR run credential) → unified dispatch
|
|
4862
|
-
*
|
|
4863
|
-
* The verb router keys authority on credential type first (`dk_` device prefix
|
|
4864
|
-
* vs `rk_` run lease) and reuses the core gateway
|
|
4865
|
-
* methods (`createLoop`/`editLoop`) through the injected `MachineGateway`,
|
|
4866
|
-
* plus the credential-neutral history module. Run credentials authorize no CLI
|
|
4867
|
-
* verb: a run reports through its final response, not a callback.
|
|
4892
|
+
* `/api/machine/cli` verb router. Authority keys on credential type: a device
|
|
4893
|
+
* (`dk_`) token gets owner verbs over loops bound to its machine, reusing
|
|
4894
|
+
* `MachineGateway`'s owner methods; a run (`rk_`) token gets no verb, because a
|
|
4895
|
+
* run reports only through its final response.
|
|
4868
4896
|
*/
|
|
4869
4897
|
var cliLog = logger.child({ mod: "cli" });
|
|
4870
4898
|
var CliGateway = class {
|
|
@@ -4878,14 +4906,6 @@ var CliGateway = class {
|
|
|
4878
4906
|
this.forceDeleteLoop = deps.forceDeleteLoop ?? forceDeleteLoop;
|
|
4879
4907
|
this.destructiveLog = deps.destructiveLog ?? ((event) => cliLog.warn(event, "force-delete: destructive server authority removal"));
|
|
4880
4908
|
}
|
|
4881
|
-
/**
|
|
4882
|
-
* The unified CLI endpoint. It is a ROUTER in front of the gateway logic that
|
|
4883
|
-
* already exists — never a rewrite — that keys authority on the credential type:
|
|
4884
|
-
* · DEVICE credential (`dk_`-prefixed) → owner authority over any loop bound to
|
|
4885
|
-
* the machine: `new`→createLoop, `loops`→listLoops, `edit`→editLoop,
|
|
4886
|
-
* `log`→readLoopHistory, `show`→describe, `file`→loop files.
|
|
4887
|
-
* · RUN credential (`rk_` run lease) → rejected; runs have no CLI.
|
|
4888
|
-
*/
|
|
4889
4909
|
async cli(token, argv) {
|
|
4890
4910
|
return finalizeCli(token.startsWith("dk_") ? await this.deviceCli(token, argv) : await this.runCli(token, argv));
|
|
4891
4911
|
}
|
|
@@ -5648,14 +5668,8 @@ var Scheduler = class {
|
|
|
5648
5668
|
function msg(err) {
|
|
5649
5669
|
return err instanceof Error ? err.message : String(err);
|
|
5650
5670
|
}
|
|
5651
|
-
/**
|
|
5652
|
-
*
|
|
5653
|
-
* migrations, wire the machine gateway's dispatcher into the Scheduler, start
|
|
5654
|
-
* the Scheduler + an offline-sweep interval. Guarded on `globalThis` so it
|
|
5655
|
-
* survives dev HMR and runs at most once per process. Call `ensureServer()`
|
|
5656
|
-
* from any server-side entry (the standalone machine server / TanStack server
|
|
5657
|
-
* fns); the first call boots, the rest share the same instance.
|
|
5658
|
-
*/
|
|
5671
|
+
/** One-time, idempotent in-process backend boot (migrations, scheduler, sweep and
|
|
5672
|
+
* storage-maintenance intervals). Guarded on `globalThis` to survive dev HMR. */
|
|
5659
5673
|
var boot_exports = /* @__PURE__ */ __exportAll({
|
|
5660
5674
|
ensureServer: () => ensureServer,
|
|
5661
5675
|
getCliGateway: () => getCliGateway,
|
|
@@ -5713,4 +5727,4 @@ async function getCliGateway() {
|
|
|
5713
5727
|
return (await ensureServer()).cliGateway;
|
|
5714
5728
|
}
|
|
5715
5729
|
//#endregion
|
|
5716
|
-
export { listRuns as A,
|
|
5730
|
+
export { listRuns as A, reconciliationStatesForRuns as B, getRun as C, listArtifacts as D, isOutdated as E, machineIdFromToken as F, stopLoop as G, requestRunCancel as H, machinePresence as I, updateMachine as J, tryDeleteLoop as K, mintDeviceToken as L, loopFileNames as M, loopTags_Bi7Bm7v8_exports as N, listLoops as O, loopsForMachine as P, pauseLoop as R, getMachine as S, hasRunningRun as T, sha256 as U, requestDeleteLoop as V, startLoop as W, validateLoopTags as Y, createMachine as _, canonicalLoopSummary as a, getArtifactFile as b, getLoopFiles as c, validOptionalPollString as d, validPollCurrentRun as f, countRuns as g, CODING_AGENTS as h, canonicalLoopEnvelope as i, listRunsBefore as j, listMachines as k, readByteBody as l, validateLoopEdit as m, POLL_HTTP_BODY_FIELDS as n, daemonNeedsUpdate as o, validPollWireId as p, updateLoop as q, boot_CecCDRoH_exports as r, ensureServer as s, MIN_DAEMON_VERSION as t, readJsonBody as u, forceDeleteLoop as v, hasPendingRun as w, getLoop as x, forceDeleteMachine as y, recentLoopUsage as z };
|