@lumi.ai/runner 0.5.6 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +67 -43
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
collection as collection5,
|
|
10
10
|
deleteField as deleteField2,
|
|
11
11
|
doc as doc7,
|
|
12
|
-
getDoc as
|
|
12
|
+
getDoc as getDoc6,
|
|
13
13
|
getDocs as getDocs4,
|
|
14
14
|
onSnapshot as onSnapshot2,
|
|
15
15
|
orderBy as orderBy4,
|
|
@@ -242,6 +242,9 @@ function clampStepText(text, max) {
|
|
|
242
242
|
function firstLine(text) {
|
|
243
243
|
return text.split("\n").find((l) => l.trim()) ?? "";
|
|
244
244
|
}
|
|
245
|
+
function step(kind, label, detail) {
|
|
246
|
+
return detail ? { kind, label, detail } : { kind, label };
|
|
247
|
+
}
|
|
245
248
|
function str(input, key) {
|
|
246
249
|
if (!input || typeof input !== "object")
|
|
247
250
|
return void 0;
|
|
@@ -354,36 +357,28 @@ function builtinDetail(tool, input) {
|
|
|
354
357
|
function describeToolUse(name, input, opts) {
|
|
355
358
|
if (name.startsWith(WORKSPACE_PREFIX)) {
|
|
356
359
|
const tool = name.slice(WORKSPACE_PREFIX.length);
|
|
357
|
-
return
|
|
358
|
-
kind: "tool",
|
|
359
|
-
label: WORKSPACE_LABELS[tool] ?? "Working",
|
|
360
|
-
detail: workspaceDetail(tool, input)
|
|
361
|
-
};
|
|
360
|
+
return step("tool", WORKSPACE_LABELS[tool] ?? "Working", workspaceDetail(tool, input));
|
|
362
361
|
}
|
|
363
362
|
if (name.startsWith(MCP_PREFIX)) {
|
|
364
363
|
const rest = name.slice(MCP_PREFIX.length);
|
|
365
364
|
const split = rest.indexOf("__");
|
|
366
365
|
const key = split < 0 ? rest : rest.slice(0, split);
|
|
367
366
|
const tool = split < 0 ? void 0 : rest.slice(split + 2);
|
|
368
|
-
return {
|
|
369
|
-
kind: "tool",
|
|
370
|
-
label: `Using ${opts?.mcpNames?.[key] ?? key}`,
|
|
371
|
-
detail: tool || void 0
|
|
372
|
-
};
|
|
367
|
+
return step("tool", `Using ${opts?.mcpNames?.[key] ?? key}`, tool);
|
|
373
368
|
}
|
|
374
369
|
const label = BUILTIN_LABELS[name];
|
|
375
370
|
if (label)
|
|
376
|
-
return
|
|
377
|
-
return
|
|
371
|
+
return step("tool", label, builtinDetail(name, input));
|
|
372
|
+
return step("tool", "Working");
|
|
378
373
|
}
|
|
379
374
|
function describeAssistantText(text) {
|
|
380
|
-
return
|
|
375
|
+
return step("saying", "Working it out", firstLine(text));
|
|
381
376
|
}
|
|
382
|
-
function pushJobStep(progress,
|
|
383
|
-
const steps = [...progress?.steps ?? [],
|
|
377
|
+
function pushJobStep(progress, step2, max = MAX_JOB_STEPS) {
|
|
378
|
+
const steps = [...progress?.steps ?? [], step2];
|
|
384
379
|
return {
|
|
385
380
|
seq: (progress?.seq ?? 0) + 1,
|
|
386
|
-
updatedAt:
|
|
381
|
+
updatedAt: step2.at,
|
|
387
382
|
// Keep the NEWEST `max`, oldest first — a reader renders top to bottom.
|
|
388
383
|
steps: steps.slice(Math.max(0, steps.length - max))
|
|
389
384
|
};
|
|
@@ -739,7 +734,7 @@ function mcpUrl(config2) {
|
|
|
739
734
|
}
|
|
740
735
|
|
|
741
736
|
// src/version.ts
|
|
742
|
-
var RUNNER_VERSION = true ? "0.5.
|
|
737
|
+
var RUNNER_VERSION = true ? "0.5.7" : "0.0.0-dev";
|
|
743
738
|
|
|
744
739
|
// src/auth.ts
|
|
745
740
|
import { signInWithCustomToken } from "firebase/auth";
|
|
@@ -1777,7 +1772,7 @@ async function runSession(input, bin, dirs) {
|
|
|
1777
1772
|
if (event.type === "result") resultEvent = event;
|
|
1778
1773
|
if (event.type === "assistant") input.log("claude: assistant turn");
|
|
1779
1774
|
if (input.onStep) {
|
|
1780
|
-
for (const
|
|
1775
|
+
for (const step2 of stepsFromClaudeEvent(event, { mcpNames })) input.onStep(step2);
|
|
1781
1776
|
}
|
|
1782
1777
|
} catch {
|
|
1783
1778
|
}
|
|
@@ -2054,6 +2049,7 @@ import {
|
|
|
2054
2049
|
collection as collection4,
|
|
2055
2050
|
deleteField,
|
|
2056
2051
|
doc as doc6,
|
|
2052
|
+
getDoc as getDoc5,
|
|
2057
2053
|
getDocs as getDocs3,
|
|
2058
2054
|
limit as fsLimit,
|
|
2059
2055
|
orderBy as orderBy3,
|
|
@@ -2190,16 +2186,27 @@ ${error.slice(0, 800)}
|
|
|
2190
2186
|
}
|
|
2191
2187
|
async function markTaskStopped(db, shipId, job, stoppedBy) {
|
|
2192
2188
|
const taskRef = doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.tasks, job.taskId);
|
|
2189
|
+
const who = await actorName(db, shipId, stoppedBy);
|
|
2193
2190
|
await addDoc(collection4(taskRef, COLLECTIONS.activity), {
|
|
2194
2191
|
author: { type: "agent", id: job.agentId },
|
|
2195
2192
|
createdAt: Date.now(),
|
|
2196
2193
|
kind: "comment",
|
|
2197
|
-
content: `This run was stopped by ${
|
|
2194
|
+
content: `This run was stopped by ${who} before it finished. The task is unchanged.`
|
|
2198
2195
|
});
|
|
2199
2196
|
}
|
|
2197
|
+
async function actorName(db, shipId, actorId) {
|
|
2198
|
+
try {
|
|
2199
|
+
const snap = await getDoc5(doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.members, actorId));
|
|
2200
|
+
const m = snap.data();
|
|
2201
|
+
return m?.displayName || m?.email || actorId;
|
|
2202
|
+
} catch {
|
|
2203
|
+
return actorId;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2200
2206
|
async function markChatStopped(db, shipId, job, stoppedBy) {
|
|
2201
2207
|
const chatRef = doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.chats, job.chatId);
|
|
2202
|
-
const
|
|
2208
|
+
const who = await actorName(db, shipId, stoppedBy);
|
|
2209
|
+
const content = `Stopped by ${who} before I finished. Write again to start a fresh run.`;
|
|
2203
2210
|
await addDoc(collection4(chatRef, COLLECTIONS.chatMessages), {
|
|
2204
2211
|
author: { type: "agent", id: job.agentId },
|
|
2205
2212
|
content,
|
|
@@ -2264,16 +2271,24 @@ async function ensureChatReply(db, shipId, job, resultText) {
|
|
|
2264
2271
|
// src/jobs/progress.ts
|
|
2265
2272
|
var PROGRESS_FLUSH_MS = 3e3;
|
|
2266
2273
|
var PROGRESS_DENIALS_BEFORE_PROBE = 2;
|
|
2267
|
-
function redactStep(
|
|
2268
|
-
const label = clampStepText(redactTranscript(
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2274
|
+
function redactStep(step2, secrets) {
|
|
2275
|
+
const label = clampStepText(redactTranscript(step2.label, secrets), MAX_STEP_LABEL);
|
|
2276
|
+
const base = { at: step2.at, kind: step2.kind, label };
|
|
2277
|
+
if (step2.detail === void 0) return base;
|
|
2278
|
+
const detail = clampStepText(redactTranscript(step2.detail, secrets), MAX_STEP_DETAIL);
|
|
2279
|
+
return detail ? { ...base, detail } : base;
|
|
2280
|
+
}
|
|
2281
|
+
function errorCode(error) {
|
|
2282
|
+
return error?.code ?? "";
|
|
2272
2283
|
}
|
|
2273
2284
|
function isDenied(error) {
|
|
2274
|
-
const code = error
|
|
2285
|
+
const code = errorCode(error);
|
|
2275
2286
|
return code === "permission-denied" || code.endsWith("/permission-denied");
|
|
2276
2287
|
}
|
|
2288
|
+
function isUnwritable(error) {
|
|
2289
|
+
const code = errorCode(error);
|
|
2290
|
+
return code === "invalid-argument" || code.endsWith("/invalid-argument");
|
|
2291
|
+
}
|
|
2277
2292
|
function createProgressWriter(deps) {
|
|
2278
2293
|
const now = deps.now ?? Date.now;
|
|
2279
2294
|
const flushMs = deps.flushMs ?? PROGRESS_FLUSH_MS;
|
|
@@ -2292,12 +2307,18 @@ function createProgressWriter(deps) {
|
|
|
2292
2307
|
}
|
|
2293
2308
|
if (closed || inFlight || pending.length === 0) return;
|
|
2294
2309
|
const secrets = deps.secrets();
|
|
2295
|
-
for (const
|
|
2310
|
+
for (const step2 of pending) progress = pushJobStep(progress, redactStep(step2, secrets));
|
|
2296
2311
|
pending = [];
|
|
2297
2312
|
lastWriteAt = now();
|
|
2298
2313
|
inFlight = true;
|
|
2299
2314
|
const snapshot = progress;
|
|
2300
|
-
|
|
2315
|
+
let written;
|
|
2316
|
+
try {
|
|
2317
|
+
written = deps.write(snapshot);
|
|
2318
|
+
} catch (e) {
|
|
2319
|
+
written = Promise.reject(e);
|
|
2320
|
+
}
|
|
2321
|
+
void written.then(() => {
|
|
2301
2322
|
denials = 0;
|
|
2302
2323
|
}).catch((e) => {
|
|
2303
2324
|
if (isDenied(e)) {
|
|
@@ -2309,9 +2330,12 @@ function createProgressWriter(deps) {
|
|
|
2309
2330
|
return;
|
|
2310
2331
|
}
|
|
2311
2332
|
denials = 0;
|
|
2333
|
+
if (isUnwritable(e)) closed = true;
|
|
2312
2334
|
if (!loggedFailure) {
|
|
2313
2335
|
loggedFailure = true;
|
|
2314
|
-
deps.log(
|
|
2336
|
+
deps.log(
|
|
2337
|
+
`progress write failed${closed ? " \u2014 live steps are off for this job" : " (further ones silent)"}: ${e instanceof Error ? e.message : e}`
|
|
2338
|
+
);
|
|
2315
2339
|
}
|
|
2316
2340
|
}).finally(() => {
|
|
2317
2341
|
inFlight = false;
|
|
@@ -2329,9 +2353,9 @@ function createProgressWriter(deps) {
|
|
|
2329
2353
|
timer.unref?.();
|
|
2330
2354
|
}
|
|
2331
2355
|
return {
|
|
2332
|
-
push(
|
|
2356
|
+
push(step2) {
|
|
2333
2357
|
if (closed) return;
|
|
2334
|
-
pending.push({ ...
|
|
2358
|
+
pending.push({ ...step2, at: now() });
|
|
2335
2359
|
schedule();
|
|
2336
2360
|
},
|
|
2337
2361
|
stop() {
|
|
@@ -2415,7 +2439,7 @@ async function startDaemon() {
|
|
|
2415
2439
|
try {
|
|
2416
2440
|
for (const shipId of [...serving]) {
|
|
2417
2441
|
try {
|
|
2418
|
-
const snap = await
|
|
2442
|
+
const snap = await getDoc6(shipRunnerRef(shipId));
|
|
2419
2443
|
if (!snap.exists()) {
|
|
2420
2444
|
forgetShipLocally(shipId, "a captain removed this machine on the Daemons page");
|
|
2421
2445
|
continue;
|
|
@@ -2741,7 +2765,7 @@ async function startDaemon() {
|
|
|
2741
2765
|
void (async () => {
|
|
2742
2766
|
if (slot.stop) return;
|
|
2743
2767
|
try {
|
|
2744
|
-
const snap = await
|
|
2768
|
+
const snap = await getDoc6(
|
|
2745
2769
|
doc7(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id)
|
|
2746
2770
|
);
|
|
2747
2771
|
const fresh = snap.data();
|
|
@@ -2796,7 +2820,7 @@ async function startDaemon() {
|
|
|
2796
2820
|
onDenied: () => {
|
|
2797
2821
|
void (async () => {
|
|
2798
2822
|
try {
|
|
2799
|
-
const snap = await
|
|
2823
|
+
const snap = await getDoc6(
|
|
2800
2824
|
doc7(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id)
|
|
2801
2825
|
);
|
|
2802
2826
|
const fresh = snap.data();
|
|
@@ -2889,7 +2913,7 @@ async function startDaemon() {
|
|
|
2889
2913
|
// §15.36. The driver emits Crew's step vocabulary; the writer coalesces, redacts and
|
|
2890
2914
|
// persists. A throw here must never reach the driver's stdout loop, which is why `push`
|
|
2891
2915
|
// swallows everything.
|
|
2892
|
-
onStep: (
|
|
2916
|
+
onStep: (step2) => progress.push(step2)
|
|
2893
2917
|
});
|
|
2894
2918
|
transcript = redactTranscript(session.transcript, knownSecrets);
|
|
2895
2919
|
usage = session.usage;
|
|
@@ -3335,7 +3359,7 @@ function setParallel(config2, value, ship2) {
|
|
|
3335
3359
|
|
|
3336
3360
|
// src/cli/commands/doctor.ts
|
|
3337
3361
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
3338
|
-
import { collection as collection6, doc as doc8, getDoc as
|
|
3362
|
+
import { collection as collection6, doc as doc8, getDoc as getDoc7, getDocs as getDocs5 } from "firebase/firestore";
|
|
3339
3363
|
|
|
3340
3364
|
// src/service.ts
|
|
3341
3365
|
import { spawnSync } from "node:child_process";
|
|
@@ -3754,7 +3778,7 @@ async function checkShips(config2) {
|
|
|
3754
3778
|
continue;
|
|
3755
3779
|
}
|
|
3756
3780
|
try {
|
|
3757
|
-
const snap = await
|
|
3781
|
+
const snap = await getDoc7(
|
|
3758
3782
|
doc8(fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.runners, config2.runnerId)
|
|
3759
3783
|
);
|
|
3760
3784
|
if (!snap.exists()) {
|
|
@@ -4179,7 +4203,7 @@ async function runUninstall(options) {
|
|
|
4179
4203
|
}
|
|
4180
4204
|
|
|
4181
4205
|
// src/cli/commands/ship.ts
|
|
4182
|
-
import { doc as doc9, getDoc as
|
|
4206
|
+
import { doc as doc9, getDoc as getDoc8 } from "firebase/firestore";
|
|
4183
4207
|
async function listMyShips() {
|
|
4184
4208
|
const config2 = requireConfig();
|
|
4185
4209
|
const ids = Object.keys(config2.shipKeys ?? {});
|
|
@@ -4187,7 +4211,7 @@ async function listMyShips() {
|
|
|
4187
4211
|
ids.map(async (id) => {
|
|
4188
4212
|
try {
|
|
4189
4213
|
const { fb } = await openShipSession(id);
|
|
4190
|
-
const snap = await
|
|
4214
|
+
const snap = await getDoc8(doc9(fb.db, COLLECTIONS.ships, id));
|
|
4191
4215
|
return { id, ...snap.data() };
|
|
4192
4216
|
} catch {
|
|
4193
4217
|
return null;
|
|
@@ -4314,7 +4338,7 @@ import {
|
|
|
4314
4338
|
collection as collection7,
|
|
4315
4339
|
doc as doc10,
|
|
4316
4340
|
getCountFromServer as getCountFromServer2,
|
|
4317
|
-
getDoc as
|
|
4341
|
+
getDoc as getDoc9,
|
|
4318
4342
|
getDocs as getDocs6,
|
|
4319
4343
|
query as query5,
|
|
4320
4344
|
where as where5
|
|
@@ -4352,7 +4376,7 @@ async function runStatus() {
|
|
|
4352
4376
|
continue;
|
|
4353
4377
|
}
|
|
4354
4378
|
const shipRef = doc10(fb.db, COLLECTIONS.ships, shipId);
|
|
4355
|
-
const mirrorSnap = await
|
|
4379
|
+
const mirrorSnap = await getDoc9(doc10(shipRef, COLLECTIONS.runners, config2.runnerId));
|
|
4356
4380
|
const mirror = mirrorSnap.data();
|
|
4357
4381
|
let queued = 0;
|
|
4358
4382
|
try {
|
|
@@ -4363,7 +4387,7 @@ async function runStatus() {
|
|
|
4363
4387
|
} catch {
|
|
4364
4388
|
queued = 0;
|
|
4365
4389
|
}
|
|
4366
|
-
const usageSnap = await
|
|
4390
|
+
const usageSnap = await getDoc9(doc10(shipRef, COLLECTIONS.usageDaily, utcDay(Date.now())));
|
|
4367
4391
|
const today = { ...EMPTY_USAGE_TOTALS, ...usageSnap.data()?.totals ?? {} };
|
|
4368
4392
|
const now = Date.now();
|
|
4369
4393
|
const limitsSnap = await getDocs6(collection7(shipRef, COLLECTIONS.engineLimits));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumi.ai/runner",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lumi Crew runner daemon — claims jobs from your Ships and executes them as headless Claude sessions on your own machine.",
|
|
6
6
|
"//name": "The ONLY package in this monorepo published to the public registry, so it is the one that does not follow the internal @lumi/crew-* convention: `@lumi` is not a scope we own, `@lumi.ai` is (the npm org). The workspace DIRECTORY stays packages/crew/runner — renaming the package is not renaming the folder.",
|