bareloop 0.31.0 → 0.33.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/CHANGELOG.md +151 -0
- package/README.md +4 -1
- package/bareloop.context.md +232 -85
- package/bin/bareloop.mjs +0 -1
- package/package.json +1 -1
- package/src/authorflow.js +5 -3
- package/src/authorjob.js +3 -1
- package/src/authorrun.js +49 -11
- package/src/authorscout.js +10 -6
- package/src/bundlerun.js +266 -0
- package/src/cli.js +35 -357
- package/src/config.js +135 -0
- package/src/interviewrun.js +13 -4
- package/src/invoke.js +22 -0
- package/src/judged.js +5 -3
- package/src/keysfile.js +156 -0
- package/src/ledger.js +26 -0
- package/src/monthly.js +422 -0
- package/src/panel/authorroutes.js +61 -16
- package/src/panel/authorsession.js +46 -28
- package/src/panel/index.html +287 -13
- package/src/panel/server.js +58 -51
- package/src/panel/settingsroutes.js +230 -0
- package/src/planrun.js +7 -3
- package/src/providerrows.js +301 -0
- package/src/providers.js +3 -1
- package/src/run.js +7 -2
- package/src/runlist.js +92 -7
- package/src/source.js +8 -3
- package/src/u-watchdog.mjs +296 -0
- package/src/userrun.js +230 -54
- package/types/authorflow.d.ts +8 -2
- package/types/authorjob.d.ts +6 -1
- package/types/authorrun.d.ts +5 -0
- package/types/authorscout.d.ts +11 -2
- package/types/bundlerun.d.ts +26 -0
- package/types/cli.d.ts +2 -1
- package/types/config.d.ts +48 -0
- package/types/interviewrun.d.ts +5 -0
- package/types/invoke.d.ts +8 -0
- package/types/judged.d.ts +5 -1
- package/types/keysfile.d.ts +90 -0
- package/types/ledger.d.ts +14 -0
- package/types/monthly.d.ts +227 -0
- package/types/panel/authorroutes.d.ts +4 -2
- package/types/panel/authorsession.d.ts +7 -15
- package/types/panel/server.d.ts +10 -3
- package/types/panel/settingsroutes.d.ts +14 -0
- package/types/planrun.d.ts +13 -1
- package/types/providerrows.d.ts +229 -0
- package/types/run.d.ts +14 -1
- package/types/runlist.d.ts +66 -3
- package/types/source.d.ts +8 -3
- package/types/userrun.d.ts +54 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {{ port: number, token: string, home?: string, env?: Record<string,string|undefined>,
|
|
3
|
+
* fetchImpl?: typeof fetch, now?: () => number }} opts
|
|
4
|
+
*/
|
|
5
|
+
export function createSettingsRoutes(opts: {
|
|
6
|
+
port: number;
|
|
7
|
+
token: string;
|
|
8
|
+
home?: string;
|
|
9
|
+
env?: Record<string, string | undefined>;
|
|
10
|
+
fetchImpl?: typeof fetch;
|
|
11
|
+
now?: () => number;
|
|
12
|
+
}): {
|
|
13
|
+
handle: (req: import("node:http").IncomingMessage, res: import("node:http").ServerResponse, pathname: string, body: any) => boolean;
|
|
14
|
+
};
|
package/types/planrun.d.ts
CHANGED
|
@@ -195,6 +195,10 @@ export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxS
|
|
|
195
195
|
* `false` → the drafter has no tools, so a native session would report NO cost — return a
|
|
196
196
|
* metered claude-json TEXT provider (`--output-format json`, `parse:'claude-json'`) instead,
|
|
197
197
|
* so its spend is never invisible. The Loop path (`anthropic-api`) never touches this.
|
|
198
|
+
* @param {{in: number, out: number}|null} [opts.rates] the customer's own price for the worker's key row (USD per
|
|
199
|
+
* 1K tokens, `ratesFor`) — handed to the one Loop that drives worker, scout and planner rounds, so they read
|
|
200
|
+
* `rateSource:'caller'`; null = bare-agent's built-in guess. Not applied to a native (CLI) session, which reports its own cost.
|
|
201
|
+
* @param {{in: number, out: number}|null} [opts.judgeRates] the same, for the judge's Loop
|
|
198
202
|
* @param {string|null} [opts.judgeModel] softgreen — WHICH model that judge provider drives
|
|
199
203
|
* (PRD item 32.1). Resolved by the CALLER (`resolveJudge`, src/judged.js: the spec's signed
|
|
200
204
|
* `judge` override, else the job's own worker model), never pinned in this library. It
|
|
@@ -416,7 +420,7 @@ export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxS
|
|
|
416
420
|
* 'branch-red' | 'cap-halt' | 'wall-halt' | 'provider-red' | 'interpreter-red' |
|
|
417
421
|
* 'step-stalled' | 'hitl-pause' | 'hitl-decision-red' | `step-red:<id>`
|
|
418
422
|
*/
|
|
419
|
-
export function runPlan(job: any, { workdir, provider, nativeProvider, providerFor, judgeProvider, judgeModel, emit, remainingUsd, isUnpriced, spendComplete, capRuns, strikeLimit, closeTimeoutMs, closeDir, maxStepRounds, layerRoot, readShim, scout, scoutRounds, bridge, now, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, priorSpentUsd, reviewDoor, doorRerun, resumable }: {
|
|
423
|
+
export function runPlan(job: any, { workdir, provider, nativeProvider, providerFor, judgeProvider, judgeModel, rates, judgeRates, emit, remainingUsd, isUnpriced, spendComplete, capRuns, strikeLimit, closeTimeoutMs, closeDir, maxStepRounds, layerRoot, readShim, scout, scoutRounds, bridge, now, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, priorSpentUsd, reviewDoor, doorRerun, resumable }: {
|
|
420
424
|
workdir: string;
|
|
421
425
|
provider: any;
|
|
422
426
|
nativeProvider?: ((o: {
|
|
@@ -425,6 +429,14 @@ export function runPlan(job: any, { workdir, provider, nativeProvider, providerF
|
|
|
425
429
|
maxTurns: number;
|
|
426
430
|
hasTools: boolean;
|
|
427
431
|
}) => any) | undefined;
|
|
432
|
+
rates?: {
|
|
433
|
+
in: number;
|
|
434
|
+
out: number;
|
|
435
|
+
} | null | undefined;
|
|
436
|
+
judgeRates?: {
|
|
437
|
+
in: number;
|
|
438
|
+
out: number;
|
|
439
|
+
} | null | undefined;
|
|
428
440
|
judgeModel?: string | null | undefined;
|
|
429
441
|
judgeProvider?: any;
|
|
430
442
|
emit: (type: string, data?: object) => object;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The key value to hand a provider: the `null` marker becomes the placeholder, anything else
|
|
3
|
+
* passes through unchanged.
|
|
4
|
+
* @template {string|undefined} T
|
|
5
|
+
* @param {T} raw
|
|
6
|
+
* @returns {T|string}
|
|
7
|
+
*/
|
|
8
|
+
export function usableKey<T extends string | undefined>(raw: T): T | string;
|
|
9
|
+
/**
|
|
10
|
+
* What a key with no saved entry shows. Any name not listed is OpenAI-compatible with a
|
|
11
|
+
* blank URL and a blank Name.
|
|
12
|
+
* @param {string} envName
|
|
13
|
+
* @returns {{ name: string, shape: string, baseUrl: string }}
|
|
14
|
+
*/
|
|
15
|
+
export function defaultsFor(envName: string): {
|
|
16
|
+
name: string;
|
|
17
|
+
shape: string;
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {object} KeyRow
|
|
22
|
+
* @property {string} envName the key variable NAME (read-only label; never a value)
|
|
23
|
+
* @property {string} name the model id Chat uses ('' = not offered in Chat)
|
|
24
|
+
* @property {string} provider the `src/providers.js` table entry (the API shape's id)
|
|
25
|
+
* @property {string} baseUrl the saved Base URL ('' = the shape's default host)
|
|
26
|
+
* @property {unknown} [priceInPerM] the customer's own price, USD per 1M input tokens — carried RAW
|
|
27
|
+
* (present only when the config row names it); {@link ratesFor} is the one place that validates it
|
|
28
|
+
* @property {unknown} [priceOutPerM] the same, for output tokens
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* The rows: one per name in `filled`, saved entry over defaults.
|
|
32
|
+
* @param {{ filled: readonly string[], config: Record<string, any> }} src
|
|
33
|
+
* @returns {KeyRow[]}
|
|
34
|
+
*/
|
|
35
|
+
export function keyRows({ filled, config }: {
|
|
36
|
+
filled: readonly string[];
|
|
37
|
+
config: Record<string, any>;
|
|
38
|
+
}): KeyRow[];
|
|
39
|
+
/**
|
|
40
|
+
* The rows for a keys home. `undefined` home (a door that skipped the keys file) = no
|
|
41
|
+
* rows, so nothing resolves through the file and the provider's built-in variable stands.
|
|
42
|
+
* @param {string|undefined} home
|
|
43
|
+
* @returns {KeyRow[]}
|
|
44
|
+
*/
|
|
45
|
+
export function rowsForHome(home: string | undefined): KeyRow[];
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} provider
|
|
48
|
+
* @returns {string} the shape's default host ('' = the vendor's own)
|
|
49
|
+
*/
|
|
50
|
+
export function defaultUrlOf(provider: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* An endpoint in comparable form: blank = the shape's default host; scheme + host lower-cased,
|
|
53
|
+
* trailing slashes dropped.
|
|
54
|
+
* @param {string} provider
|
|
55
|
+
* @param {string|null|undefined} baseUrl
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
export function endpointOf(provider: string, baseUrl: string | null | undefined): string;
|
|
59
|
+
/**
|
|
60
|
+
* The URL a row talks to, for display ('' = the vendor's own host, which has no URL to show).
|
|
61
|
+
* @param {KeyRow} row
|
|
62
|
+
* @returns {string}
|
|
63
|
+
*/
|
|
64
|
+
export function shownUrl(row: KeyRow): string;
|
|
65
|
+
/**
|
|
66
|
+
* Which row a `(provider, baseUrl[, model])` identity belongs to: the rows on that shape and
|
|
67
|
+
* endpoint; when several share it, the one whose Name is the model, else the first.
|
|
68
|
+
* @param {readonly KeyRow[]} rows
|
|
69
|
+
* @param {{ provider?: string|null, baseUrl?: string|null, model?: string|null }} id
|
|
70
|
+
* @returns {KeyRow|null}
|
|
71
|
+
*/
|
|
72
|
+
export function findRow(rows: readonly KeyRow[], { provider, baseUrl, model }: {
|
|
73
|
+
provider?: string | null;
|
|
74
|
+
baseUrl?: string | null;
|
|
75
|
+
model?: string | null;
|
|
76
|
+
}): KeyRow | null;
|
|
77
|
+
/**
|
|
78
|
+
* What Chat's Model menu offers and how a pick resolves: the row whose Name is the model
|
|
79
|
+
* (a blank Name is never offered). `baseUrl` is absent when blank, exactly as a spec omits it.
|
|
80
|
+
* @param {readonly KeyRow[]} rows
|
|
81
|
+
* @param {string} model
|
|
82
|
+
* @returns {{ provider: string, baseUrl?: string, envName: string, name: string }|null}
|
|
83
|
+
*/
|
|
84
|
+
export function modelChoiceFor(rows: readonly KeyRow[], model: string): {
|
|
85
|
+
provider: string;
|
|
86
|
+
baseUrl?: string;
|
|
87
|
+
envName: string;
|
|
88
|
+
name: string;
|
|
89
|
+
} | null;
|
|
90
|
+
/**
|
|
91
|
+
* The models Chat offers, in keys-file order.
|
|
92
|
+
* @param {readonly KeyRow[]} rows
|
|
93
|
+
* @returns {{ id: string, envName: string, shape: string }[]}
|
|
94
|
+
*/
|
|
95
|
+
export function chatModels(rows: readonly KeyRow[]): {
|
|
96
|
+
id: string;
|
|
97
|
+
envName: string;
|
|
98
|
+
shape: string;
|
|
99
|
+
}[];
|
|
100
|
+
/**
|
|
101
|
+
* The key variable NAME a job on `(provider, baseUrl[, model])` reads: the matching row's
|
|
102
|
+
* variable, else the provider's built-in one (byte-identical to before for anything no row
|
|
103
|
+
* claims).
|
|
104
|
+
* @param {string|null|undefined} provider
|
|
105
|
+
* @param {string|null|undefined} baseUrl
|
|
106
|
+
* @param {readonly KeyRow[]} rows
|
|
107
|
+
* @param {string|null|undefined} [model]
|
|
108
|
+
* @returns {{ name: string, builtIn: string, chosen: boolean }}
|
|
109
|
+
*/
|
|
110
|
+
export function keyNameFor(provider: string | null | undefined, baseUrl: string | null | undefined, rows: readonly KeyRow[], model?: string | null | undefined): {
|
|
111
|
+
name: string;
|
|
112
|
+
builtIn: string;
|
|
113
|
+
chosen: boolean;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* The env a door hands to provider construction, with the matching row's key variable
|
|
117
|
+
* standing in for the provider's built-in one: `env[builtIn] = env[row.envName]` (the `null`
|
|
118
|
+
* no-key marker becomes {@link NO_KEY_PLACEHOLDER}). A copy — never mutates the input.
|
|
119
|
+
* No matching row and no marker = the env unchanged.
|
|
120
|
+
* @param {Record<string,string|undefined>} env
|
|
121
|
+
* @param {string|null|undefined} provider
|
|
122
|
+
* @param {string|null|undefined} baseUrl
|
|
123
|
+
* @param {readonly KeyRow[]} rows
|
|
124
|
+
* @param {string|null|undefined} [model]
|
|
125
|
+
* @returns {Record<string,string|undefined>}
|
|
126
|
+
*/
|
|
127
|
+
export function applyConfiguredKey(env: Record<string, string | undefined>, provider: string | null | undefined, baseUrl: string | null | undefined, rows: readonly KeyRow[], model?: string | null | undefined): Record<string, string | undefined>;
|
|
128
|
+
/**
|
|
129
|
+
* The one readout line for a resolved customer price, preview and run tail alike:
|
|
130
|
+
* `yours: in $0.006 / out $1.20 per 1M tokens (DEEPSEEK_API_KEY row)`.
|
|
131
|
+
* @param {{ inPerM: number, outPerM: number, envName: string }} price a {@link ratesFor} result
|
|
132
|
+
* @returns {string}
|
|
133
|
+
*/
|
|
134
|
+
export function priceReadout(price: {
|
|
135
|
+
inPerM: number;
|
|
136
|
+
outPerM: number;
|
|
137
|
+
envName: string;
|
|
138
|
+
}): string;
|
|
139
|
+
/**
|
|
140
|
+
* The customer's own price for the row a `(provider, baseUrl[, model])` identity belongs to —
|
|
141
|
+
* the SAME row match {@link keyNameFor} uses (`findRow`), so the price and the key always come
|
|
142
|
+
* from one row. `null` = no price set (the run keeps bare-agent's built-in guess, byte-identical
|
|
143
|
+
* to before). Both fields or neither, each a finite number >= 0, USD per 1M tokens; anything
|
|
144
|
+
* else throws `ConfigError` naming the row and field — a bad price never silently falls back to
|
|
145
|
+
* the guess. `rates` is bare-agent's `Loop({ rates })` shape (USD per 1K); `inPerM`/`outPerM`
|
|
146
|
+
* are what a readout prints.
|
|
147
|
+
* @param {string|null|undefined} provider
|
|
148
|
+
* @param {string|null|undefined} baseUrl
|
|
149
|
+
* @param {readonly KeyRow[]} rows
|
|
150
|
+
* @param {string|null|undefined} [model]
|
|
151
|
+
* @returns {{ rates: { in: number, out: number }, inPerM: number, outPerM: number, envName: string }|null}
|
|
152
|
+
*/
|
|
153
|
+
export function ratesFor(provider: string | null | undefined, baseUrl: string | null | undefined, rows: readonly KeyRow[], model?: string | null | undefined): {
|
|
154
|
+
rates: {
|
|
155
|
+
in: number;
|
|
156
|
+
out: number;
|
|
157
|
+
};
|
|
158
|
+
inPerM: number;
|
|
159
|
+
outPerM: number;
|
|
160
|
+
envName: string;
|
|
161
|
+
} | null;
|
|
162
|
+
/**
|
|
163
|
+
* The price a judge's rounds are booked at. A price belongs to the MODEL's row: when the judge is
|
|
164
|
+
* the worker's own provider it calls the worker's endpoint, so a row on that endpoint NAMED for the
|
|
165
|
+
* judge's model prices it ({@link ratesFor}); a judge model that is absent or names no row there
|
|
166
|
+
* keeps the worker's price (the worker's row, or none = the guess). A different provider resolves
|
|
167
|
+
* on its own default endpoint, as before. A bad price on the resolved row throws like `ratesFor`.
|
|
168
|
+
* The judge's KEY is chosen elsewhere (`applyConfiguredKey`, by the worker's model), so the two
|
|
169
|
+
* can come from different rows — same provider + endpoint, judge model naming another row.
|
|
170
|
+
* @param {{ provider: string, model?: string|null }} judge
|
|
171
|
+
* @param {string|null|undefined} workerProvider
|
|
172
|
+
* @param {string|null|undefined} workerBaseUrl
|
|
173
|
+
* @param {ReturnType<typeof ratesFor>} workerPrice what the worker's own rounds are booked at
|
|
174
|
+
* @param {readonly KeyRow[]} rows
|
|
175
|
+
* @returns {ReturnType<typeof ratesFor>}
|
|
176
|
+
*/
|
|
177
|
+
export function judgeRatesFor(judge: {
|
|
178
|
+
provider: string;
|
|
179
|
+
model?: string | null;
|
|
180
|
+
}, workerProvider: string | null | undefined, workerBaseUrl: string | null | undefined, workerPrice: ReturnType<typeof ratesFor>, rows: readonly KeyRow[]): ReturnType<typeof ratesFor>;
|
|
181
|
+
/** DeepSeek's OpenAI-shaped endpoint (the same string the panel's Model menu used). */
|
|
182
|
+
export const DEEPSEEK_BASE_URL: "https://api.deepseek.com/v1";
|
|
183
|
+
/**
|
|
184
|
+
* The API shapes the dropdown offers. `id` is the `src/providers.js` table name;
|
|
185
|
+
* `defaultUrl` is the host a blank Base URL means ('' = the vendor's own, unshown).
|
|
186
|
+
* @type {readonly {id: string, label: string, defaultUrl: string}[]}
|
|
187
|
+
*/
|
|
188
|
+
export const SHAPES: readonly {
|
|
189
|
+
id: string;
|
|
190
|
+
label: string;
|
|
191
|
+
defaultUrl: string;
|
|
192
|
+
}[];
|
|
193
|
+
/** The five empty lines a fresh keys file is created with, in this order. */
|
|
194
|
+
export const PRESET_KEY_NAMES: readonly string[];
|
|
195
|
+
/** A local server's default endpoint: Ollama's OpenAI-compatible host (LM Studio / llama.cpp: change the URL). */
|
|
196
|
+
export const LOCAL_BASE_URL: "http://127.0.0.1:11434/v1";
|
|
197
|
+
/**
|
|
198
|
+
* A key line whose value is the word `null` is SET and means "no real key": the row exists and
|
|
199
|
+
* the provider is handed this harmless stand-in (local servers ignore auth). It is not a
|
|
200
|
+
* secret and is never shown or logged as a key.
|
|
201
|
+
*/
|
|
202
|
+
export const NO_KEY_PLACEHOLDER: "no-key-needed";
|
|
203
|
+
export type KeyRow = {
|
|
204
|
+
/**
|
|
205
|
+
* the key variable NAME (read-only label; never a value)
|
|
206
|
+
*/
|
|
207
|
+
envName: string;
|
|
208
|
+
/**
|
|
209
|
+
* the model id Chat uses ('' = not offered in Chat)
|
|
210
|
+
*/
|
|
211
|
+
name: string;
|
|
212
|
+
/**
|
|
213
|
+
* the `src/providers.js` table entry (the API shape's id)
|
|
214
|
+
*/
|
|
215
|
+
provider: string;
|
|
216
|
+
/**
|
|
217
|
+
* the saved Base URL ('' = the shape's default host)
|
|
218
|
+
*/
|
|
219
|
+
baseUrl: string;
|
|
220
|
+
/**
|
|
221
|
+
* the customer's own price, USD per 1M input tokens — carried RAW
|
|
222
|
+
* (present only when the config row names it); {@link ratesFor} is the one place that validates it
|
|
223
|
+
*/
|
|
224
|
+
priceInPerM?: unknown;
|
|
225
|
+
/**
|
|
226
|
+
* the same, for output tokens
|
|
227
|
+
*/
|
|
228
|
+
priceOutPerM?: unknown;
|
|
229
|
+
};
|
package/types/run.d.ts
CHANGED
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
* metered claude-json text provider for the toolless drafter); ignored on every Loop-driven provider
|
|
35
35
|
* @param {(type: string, data?: object) => object} opts.emit spine emitter
|
|
36
36
|
* @param {(tier: string) => any} [opts.providerFor] P: per-step model-tier provider factory (forwarded to the plan flow)
|
|
37
|
+
* @param {{in: number, out: number}|null} [opts.rates] the CUSTOMER's own price for the worker's key row
|
|
38
|
+
* (`ratesFor`, src/providerrows.js — USD per 1K tokens), resolved once by the door and handed to every
|
|
39
|
+
* worker/planner Loop as bare-agent's `rates` (rounds then read `rateSource:'caller'`). Absent = the
|
|
40
|
+
* built-in guess, exactly as before. A Loop-driven provider only: a native session reports its own cost.
|
|
41
|
+
* @param {{in: number, out: number}|null} [opts.judgeRates] the same, for the judge's own key row
|
|
37
42
|
* @param {string|null} [opts.judgeModel] SOFTGREEN — WHICH model the judge provider drives
|
|
38
43
|
* (PRD item 32.1), resolved by the CALLER (`resolveJudge`, src/judged.js) and passed
|
|
39
44
|
* through untouched. Travels with `judgeProvider`; absent, a judged stage stops as a
|
|
@@ -204,7 +209,7 @@
|
|
|
204
209
|
* deliberately NOT in the resumable/checkpoint set (`src/reuse.js`) — an
|
|
205
210
|
* unknown in-flight state is not a known-safe resume point.
|
|
206
211
|
*/
|
|
207
|
-
export function runJob(rawSpec: object | string, { approvals, workdir, provider, nativeProvider, providerFor, judgeProvider, judgeModel, emit, capRuns, strikeLimit, shellCapUsd, closeTimeoutMs, closeDir, layerRoot, readShim, scout, bridge, draftSpentUsd, draftSpendComplete, priorSpentUsd, priorSpendComplete, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, reviewDoor, doorRerun, resumable }: {
|
|
212
|
+
export function runJob(rawSpec: object | string, { approvals, workdir, provider, nativeProvider, providerFor, judgeProvider, judgeModel, rates, judgeRates, emit, capRuns, strikeLimit, shellCapUsd, closeTimeoutMs, closeDir, layerRoot, readShim, scout, bridge, draftSpentUsd, draftSpendComplete, priorSpentUsd, priorSpendComplete, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, reviewDoor, doorRerun, resumable }: {
|
|
208
213
|
approvals: unknown;
|
|
209
214
|
workdir: string;
|
|
210
215
|
provider: any;
|
|
@@ -216,6 +221,14 @@ export function runJob(rawSpec: object | string, { approvals, workdir, provider,
|
|
|
216
221
|
}) => any) | undefined;
|
|
217
222
|
emit: (type: string, data?: object) => object;
|
|
218
223
|
providerFor?: ((tier: string) => any) | undefined;
|
|
224
|
+
rates?: {
|
|
225
|
+
in: number;
|
|
226
|
+
out: number;
|
|
227
|
+
} | null | undefined;
|
|
228
|
+
judgeRates?: {
|
|
229
|
+
in: number;
|
|
230
|
+
out: number;
|
|
231
|
+
} | null | undefined;
|
|
219
232
|
judgeModel?: string | null | undefined;
|
|
220
233
|
judgeProvider?: any;
|
|
221
234
|
capRuns?: number | undefined;
|
package/types/runlist.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ export function runlistHome(override?: string): string;
|
|
|
13
13
|
*/
|
|
14
14
|
export function runlistPath(home?: string): string;
|
|
15
15
|
/**
|
|
16
|
-
* @typedef {{ at: string, runid: string, job: string, spine: string, patient: string|null, via: 'run-u'|'bundle'|'backfill' }} RunRow
|
|
16
|
+
* @typedef {{ at: string, runid: string, job: string, spine: string, patient: string|null, via: 'run-u'|'bundle'|'backfill', pid?: number, capUsd?: number }} RunRow
|
|
17
|
+
* `pid`/`capUsd` (optional; older and backfilled rows carry neither): the runner's process id and the
|
|
18
|
+
* leg's $ cap — what the monthly limit holds while that pid is a live bareloop runner (src/monthly.js).
|
|
17
19
|
*/
|
|
18
20
|
/**
|
|
19
21
|
* Append one row to the run list, mkdir -p'ing the home directory first.
|
|
@@ -42,19 +44,57 @@ export function appendRun(row: RunRow, opts?: {
|
|
|
42
44
|
appended: boolean;
|
|
43
45
|
reason?: "duplicate-runid";
|
|
44
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {{ runid: string, type: 'settled'|'released', by: string, at: string, reason?: string, spentUsd?: number, spendComplete?: boolean }} RunEvent
|
|
49
|
+
* A claim's write-back, appended (never rewritten) beside the run rows. `settled` = the run's claim on the
|
|
50
|
+
* monthly limit is over (`spentUsd`/`spendComplete` are its final figure, `by` is WHO wrote it: the run itself
|
|
51
|
+
* at its `job-end`, or the next run that found its process gone, `reason: 'process gone'`). `released` = the
|
|
52
|
+
* claim was taken and then given back unspent, its run never started. The month's totals keep reading real
|
|
53
|
+
* spend off the spines; an event only releases the hold and records who wrote it.
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* Append one {@link RunEvent}. Not locked: one short line, one append. THROWS on IO failure.
|
|
57
|
+
* @param {RunEvent} event
|
|
58
|
+
* @param {{ home?: string }} [opts]
|
|
59
|
+
* @returns {void}
|
|
60
|
+
*/
|
|
61
|
+
export function appendRunEvent(event: RunEvent, opts?: {
|
|
62
|
+
home?: string;
|
|
63
|
+
}): void;
|
|
45
64
|
/**
|
|
46
65
|
* Tolerant reader — reuses {@link parseJsonl} (never a second hand-rolled
|
|
47
66
|
* parser). An absent file reads as an empty list, not an error: a fresh
|
|
48
|
-
* install has never run `appendRun` yet.
|
|
67
|
+
* install has never run `appendRun` yet. FOLDS the file: `rows` are the RUNS
|
|
68
|
+
* only (an event line is never listed as a run, and a run whose claim was
|
|
69
|
+
* `released` never started, so it is not listed either); `events` are the
|
|
70
|
+
* write-backs, in file order.
|
|
49
71
|
* @param {{ home?: string }} [opts]
|
|
50
|
-
* @returns {{ rows: RunRow[], skipped: number }}
|
|
72
|
+
* @returns {{ rows: RunRow[], skipped: number, events: RunEvent[] }}
|
|
51
73
|
*/
|
|
52
74
|
export function readRunList(opts?: {
|
|
53
75
|
home?: string;
|
|
54
76
|
}): {
|
|
55
77
|
rows: RunRow[];
|
|
56
78
|
skipped: number;
|
|
79
|
+
events: RunEvent[];
|
|
57
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Is this pid a live bareloop runner? `process.kill(pid, 0)` says whether the
|
|
83
|
+
* pid exists (EPERM = it exists, just not ours); `/proc/<pid>/cmdline` says whether it is a
|
|
84
|
+
* bareloop runner — a recycled pid that now belongs to some other program is NOT. A runner is
|
|
85
|
+
* a node executable (argv[0] is `node`, `nodejs`, `node22`, ...) with a later argv entry named
|
|
86
|
+
* `bareloop`, `bareloop.mjs`, `run-u.mjs` or `u-watchdog.mjs` (the basename, so the npm bin
|
|
87
|
+
* symlink counts), or a process whose argv[0] is itself one of those names. A non-node program
|
|
88
|
+
* that merely has such a name in its arguments (`nvim /x/bareloop`, `git -C /x/bareloop`) is not
|
|
89
|
+
* a runner. Residual, accepted: a recycled pid that is a node program with such an argument
|
|
90
|
+
* still reads as a runner. The wrong direction to err is "not a runner" on a real live run — it
|
|
91
|
+
* would release that run's cap. Where `/proc` is unreadable (not Linux, or not ours) the
|
|
92
|
+
* existence test alone decides: alive. The ONE spelling — the monthly limit's holds, and
|
|
93
|
+
* `--resume`'s old-pid check share it.
|
|
94
|
+
* @param {number} pid
|
|
95
|
+
* @returns {boolean}
|
|
96
|
+
*/
|
|
97
|
+
export function isLiveRunner(pid: number): boolean;
|
|
58
98
|
/**
|
|
59
99
|
* `bareloop runs backfill <dir>` — recursively scan `dir` (bounded to
|
|
60
100
|
* {@link MAX_BACKFILL_DEPTH} levels, never into `node_modules`/`.git`,
|
|
@@ -101,7 +141,12 @@ export function backfillRuns(dir: string, opts?: {
|
|
|
101
141
|
* @returns {string}
|
|
102
142
|
*/
|
|
103
143
|
export function formatRunRow(row: RunRow): string;
|
|
144
|
+
export const DIED_MTIME_MS: number;
|
|
104
145
|
export const MAX_BACKFILL_DEPTH: 6;
|
|
146
|
+
/**
|
|
147
|
+
* `pid`/`capUsd` (optional; older and backfilled rows carry neither): the runner's process id and the
|
|
148
|
+
* leg's $ cap — what the monthly limit holds while that pid is a live bareloop runner (src/monthly.js).
|
|
149
|
+
*/
|
|
105
150
|
export type RunRow = {
|
|
106
151
|
at: string;
|
|
107
152
|
runid: string;
|
|
@@ -109,4 +154,22 @@ export type RunRow = {
|
|
|
109
154
|
spine: string;
|
|
110
155
|
patient: string | null;
|
|
111
156
|
via: "run-u" | "bundle" | "backfill";
|
|
157
|
+
pid?: number;
|
|
158
|
+
capUsd?: number;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* A claim's write-back, appended (never rewritten) beside the run rows. `settled` = the run's claim on the
|
|
162
|
+
* monthly limit is over (`spentUsd`/`spendComplete` are its final figure, `by` is WHO wrote it: the run itself
|
|
163
|
+
* at its `job-end`, or the next run that found its process gone, `reason: 'process gone'`). `released` = the
|
|
164
|
+
* claim was taken and then given back unspent, its run never started. The month's totals keep reading real
|
|
165
|
+
* spend off the spines; an event only releases the hold and records who wrote it.
|
|
166
|
+
*/
|
|
167
|
+
export type RunEvent = {
|
|
168
|
+
runid: string;
|
|
169
|
+
type: "settled" | "released";
|
|
170
|
+
by: string;
|
|
171
|
+
at: string;
|
|
172
|
+
reason?: string;
|
|
173
|
+
spentUsd?: number;
|
|
174
|
+
spendComplete?: boolean;
|
|
112
175
|
};
|
package/types/source.d.ts
CHANGED
|
@@ -72,15 +72,20 @@ export function looksLikeRepoSource(source: string): boolean;
|
|
|
72
72
|
* never hands a repo source's destination to `copyOut` — wiring it into
|
|
73
73
|
* `writeScope` is M3/M4's job, not this one.
|
|
74
74
|
* @param {{source: string, into: string, destination?: string,
|
|
75
|
-
* fetchTimeoutMs?: number
|
|
76
|
-
* callers omit it and get
|
|
75
|
+
* fetchTimeoutMs?: number, afterScan?: () => (void|Promise<void>)}} args
|
|
76
|
+
* `fetchTimeoutMs` is test-only — production callers omit it and get
|
|
77
|
+
* `PROVIDER_TIMEOUT_MS`. `afterScan` is a test seam too: called exactly once,
|
|
78
|
+
* after the secret scan finished and before the freeze loop re-reads any
|
|
79
|
+
* file's bytes, so a test can swap a file inside that window
|
|
80
|
+
* deterministically. Production never sets it; absent, behaviour is unchanged.
|
|
77
81
|
* @returns {Promise<{stop: null, into: string, tree: string, manifestPath: string, manifest: object}|SourceRefusal>}
|
|
78
82
|
*/
|
|
79
|
-
export function prepareSource({ source, into, destination, fetchTimeoutMs }: {
|
|
83
|
+
export function prepareSource({ source, into, destination, fetchTimeoutMs, afterScan }: {
|
|
80
84
|
source: string;
|
|
81
85
|
into: string;
|
|
82
86
|
destination?: string;
|
|
83
87
|
fetchTimeoutMs?: number;
|
|
88
|
+
afterScan?: () => (void | Promise<void>);
|
|
84
89
|
}): Promise<{
|
|
85
90
|
stop: null;
|
|
86
91
|
into: string;
|
package/types/userrun.d.ts
CHANGED
|
@@ -43,6 +43,16 @@ export function answerDoor(runId: string, decision: {
|
|
|
43
43
|
export function main(argv: string[], deps?: Deps): Promise<number>;
|
|
44
44
|
export type Deps = {
|
|
45
45
|
env?: Record<string, string | undefined> | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* P4a — a test seam naming the home whose `.env` keys file is loaded
|
|
48
|
+
* even when `env` is injected; production never sets it.
|
|
49
|
+
*/
|
|
50
|
+
keysHome?: string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* the command this run was actually reached through (`src/invoke.js`);
|
|
53
|
+
* `src/cli.js` passes `'bareloop run-u'`, `scripts/run-u.mjs` passes none.
|
|
54
|
+
*/
|
|
55
|
+
invokedAs?: string | undefined;
|
|
46
56
|
out?: ((s: string) => void) | undefined;
|
|
47
57
|
err?: ((s: string) => void) | undefined;
|
|
48
58
|
/**
|
|
@@ -60,6 +70,45 @@ export type Deps = {
|
|
|
60
70
|
*/
|
|
61
71
|
runlistHome?: string | undefined;
|
|
62
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* The one-runner door's hand-in (`src/bundlerun.js`, the exported bundle's `bareloop run`):
|
|
75
|
+
* where a bundle run's books live and how it prints its own re-invocation. Set ONLY by
|
|
76
|
+
* that caller through `startRun`/`resumeRun` opts, in-process — `main`'s argv never builds
|
|
77
|
+
* it, so a local job cannot reach it. Absent, every use below is today's expression.
|
|
78
|
+
*/
|
|
79
|
+
export type BundleRun = {
|
|
80
|
+
/**
|
|
81
|
+
* the run id (also the `runs/<runid>` directory name)
|
|
82
|
+
*/
|
|
83
|
+
runid: string;
|
|
84
|
+
/**
|
|
85
|
+
* `<bundle>/runs/<runid>`: spine.jsonl, gate-audit.jsonl and close/ live here
|
|
86
|
+
*/
|
|
87
|
+
runDir: string;
|
|
88
|
+
/**
|
|
89
|
+
* the bare re-invocation prefix printed in resume hints (`bareloop run <bundle> --repo <path>`), no ceiling flags
|
|
90
|
+
*/
|
|
91
|
+
invoke: string;
|
|
92
|
+
/**
|
|
93
|
+
* the ceilings the person tightened on THIS leg; printed after `invoke` on every resume line except a money halt's (which never prints the exhausted `--budget`)
|
|
94
|
+
*/
|
|
95
|
+
tightened: {
|
|
96
|
+
budgetUsd?: number;
|
|
97
|
+
wallMin?: number;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* the bundle's SIGNED spec budgetUsd (the tightened leg ceiling is `spec.budgetUsd`); printed only, never enforced from here
|
|
101
|
+
*/
|
|
102
|
+
signedBudgetUsd: number;
|
|
103
|
+
/**
|
|
104
|
+
* the hash printed after `--approve` in those hints (the bundle's, not the run spec's)
|
|
105
|
+
*/
|
|
106
|
+
printApprove: string;
|
|
107
|
+
/**
|
|
108
|
+
* creates the run's tree (fresh worktree) and its books; the engine calls it once every $0 refusal has passed, so a refusal leaks nothing
|
|
109
|
+
*/
|
|
110
|
+
prepareTree: () => void;
|
|
111
|
+
};
|
|
63
112
|
export type RunOpts = {
|
|
64
113
|
/**
|
|
65
114
|
* the parsed, not-yet-validated job spec — read by
|
|
@@ -131,6 +180,10 @@ export type RunOpts = {
|
|
|
131
180
|
* how a "go edit the spec" hint should read
|
|
132
181
|
*/
|
|
133
182
|
specDesc?: string | undefined;
|
|
183
|
+
/**
|
|
184
|
+
* set only by the bundle door (see `BundleRun`)
|
|
185
|
+
*/
|
|
186
|
+
bundle?: BundleRun | undefined;
|
|
134
187
|
deps?: Deps | undefined;
|
|
135
188
|
};
|
|
136
189
|
export type RunCtx = {
|
|
@@ -154,5 +207,6 @@ export type RunCtx = {
|
|
|
154
207
|
door: string | null;
|
|
155
208
|
decide: string | null;
|
|
156
209
|
text: string | null;
|
|
210
|
+
bundle?: BundleRun | undefined;
|
|
157
211
|
deps: Deps;
|
|
158
212
|
};
|