@lumi.ai/runner 0.3.3 → 0.3.5
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 +58 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -533,7 +533,7 @@ function mcpUrl(config2) {
|
|
|
533
533
|
}
|
|
534
534
|
|
|
535
535
|
// src/version.ts
|
|
536
|
-
var RUNNER_VERSION = true ? "0.3.
|
|
536
|
+
var RUNNER_VERSION = true ? "0.3.5" : "0.0.0-dev";
|
|
537
537
|
|
|
538
538
|
// src/auth.ts
|
|
539
539
|
import { signInWithCustomToken } from "firebase/auth";
|
|
@@ -580,7 +580,39 @@ async function request(baseUrl, name, data, headers) {
|
|
|
580
580
|
|
|
581
581
|
// src/auth.ts
|
|
582
582
|
var AuthError = class extends Error {
|
|
583
|
+
constructor(message, gone = false) {
|
|
584
|
+
super(message);
|
|
585
|
+
this.gone = gone;
|
|
586
|
+
}
|
|
587
|
+
gone;
|
|
583
588
|
};
|
|
589
|
+
function orphanShipKeys(config2) {
|
|
590
|
+
return Object.keys(config2.shipKeys ?? {}).filter((id) => !config2.ships.includes(id));
|
|
591
|
+
}
|
|
592
|
+
async function probeShipKey(config2, shipId) {
|
|
593
|
+
const key = config2.shipKeys?.[shipId];
|
|
594
|
+
if (!key) return null;
|
|
595
|
+
try {
|
|
596
|
+
await callPublicFunction(functionsBaseUrl(config2), "exchangeRunnerKey", { key });
|
|
597
|
+
return null;
|
|
598
|
+
} catch (e) {
|
|
599
|
+
return {
|
|
600
|
+
shipId,
|
|
601
|
+
message: `Ship ${shipId}: ${e instanceof Error ? e.message : String(e)}`,
|
|
602
|
+
gone: e instanceof CallableError && e.status === "PERMISSION_DENIED"
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function pruneDeadShips(config2, failures) {
|
|
607
|
+
const dropped = failures.filter((f) => f.gone).map((f) => f.shipId);
|
|
608
|
+
if (dropped.length === 0) return { config: config2, dropped };
|
|
609
|
+
const shipKeys = { ...config2.shipKeys ?? {} };
|
|
610
|
+
for (const shipId of dropped) delete shipKeys[shipId];
|
|
611
|
+
return {
|
|
612
|
+
config: { ...config2, shipKeys, ships: config2.ships.filter((id) => !dropped.includes(id)) },
|
|
613
|
+
dropped
|
|
614
|
+
};
|
|
615
|
+
}
|
|
584
616
|
async function signInToShip(fb, config2, shipId) {
|
|
585
617
|
const key = config2.shipKeys?.[shipId];
|
|
586
618
|
if (!key) {
|
|
@@ -593,7 +625,8 @@ async function signInToShip(fb, config2, shipId) {
|
|
|
593
625
|
session = await callPublicFunction(functionsBaseUrl(config2), "exchangeRunnerKey", { key });
|
|
594
626
|
} catch (e) {
|
|
595
627
|
throw new AuthError(
|
|
596
|
-
`Ship ${shipId}: ${e instanceof Error ? e.message : String(e)}
|
|
628
|
+
`Ship ${shipId}: ${e instanceof Error ? e.message : String(e)}`,
|
|
629
|
+
e instanceof CallableError && e.status === "PERMISSION_DENIED"
|
|
597
630
|
);
|
|
598
631
|
}
|
|
599
632
|
const cred = await signInWithCustomToken(fb.auth, session.customToken);
|
|
@@ -608,7 +641,11 @@ async function openShipSessions(config2, build) {
|
|
|
608
641
|
const user = await signInToShip(fb, config2, shipId);
|
|
609
642
|
sessions.push({ shipId, fb, user });
|
|
610
643
|
} catch (e) {
|
|
611
|
-
failures.push({
|
|
644
|
+
failures.push({
|
|
645
|
+
shipId,
|
|
646
|
+
message: e instanceof Error ? e.message : String(e),
|
|
647
|
+
gone: e instanceof AuthError && e.gone
|
|
648
|
+
});
|
|
612
649
|
}
|
|
613
650
|
}
|
|
614
651
|
return { sessions, failures };
|
|
@@ -1754,7 +1791,7 @@ var MAX_ATTEMPTS = 2;
|
|
|
1754
1791
|
var SHUTDOWN_GRACE_MS = 1e4;
|
|
1755
1792
|
async function startDaemon() {
|
|
1756
1793
|
const moved = migrateLegacyDir();
|
|
1757
|
-
|
|
1794
|
+
let config2 = requireConfig();
|
|
1758
1795
|
if (config2.ships.length === 0) {
|
|
1759
1796
|
console.error("No Ships assigned. Run: lumi-runner ship add <shipId>");
|
|
1760
1797
|
process.exit(1);
|
|
@@ -1764,6 +1801,15 @@ async function startDaemon() {
|
|
|
1764
1801
|
(shipId) => initFirebase(config2, `ship-${shipId}`)
|
|
1765
1802
|
);
|
|
1766
1803
|
for (const f of failures) console.error(f.message);
|
|
1804
|
+
const orphanFailures = (await Promise.all(orphanShipKeys(config2).map((shipId) => probeShipKey(config2, shipId)))).filter((f) => f !== null);
|
|
1805
|
+
const pruned = pruneDeadShips(config2, [...failures, ...orphanFailures]);
|
|
1806
|
+
if (pruned.dropped.length > 0) {
|
|
1807
|
+
saveConfig(pruned.config);
|
|
1808
|
+
config2 = pruned.config;
|
|
1809
|
+
console.log(
|
|
1810
|
+
`Forgot ${pruned.dropped.length} Ship(s) this machine can no longer open: ${pruned.dropped.join(", ")}. Deleted, or this machine was revoked \u2014 run \`lumi-runner login\` if that was not expected.`
|
|
1811
|
+
);
|
|
1812
|
+
}
|
|
1767
1813
|
if (sessions.length === 0) {
|
|
1768
1814
|
console.error("No Ship could be signed in to. Run: lumi-runner login");
|
|
1769
1815
|
process.exit(1);
|
|
@@ -3147,6 +3193,9 @@ function report2(result) {
|
|
|
3147
3193
|
`Awaiting captain approval on: ${result.pendingShips.join(", ")} \u2014 a captain approves this machine on the Ship's Daemons page.`
|
|
3148
3194
|
);
|
|
3149
3195
|
}
|
|
3196
|
+
if (serviceStatus().state === "running") {
|
|
3197
|
+
say.info("Restart the daemon for this to take effect: `lumi-runner service restart`.");
|
|
3198
|
+
}
|
|
3150
3199
|
return 0;
|
|
3151
3200
|
}
|
|
3152
3201
|
|
|
@@ -3377,7 +3426,8 @@ async function runSetup(options) {
|
|
|
3377
3426
|
}
|
|
3378
3427
|
say.step("Checking this machine\u2026");
|
|
3379
3428
|
const doctorExit = await runDoctor();
|
|
3380
|
-
|
|
3429
|
+
const service2 = serviceStatus().state;
|
|
3430
|
+
if (service2 === "not-installed") {
|
|
3381
3431
|
const install = await promptConfirm({
|
|
3382
3432
|
message: "Start the daemon automatically whenever this machine is on?",
|
|
3383
3433
|
initialValue: true,
|
|
@@ -3385,6 +3435,9 @@ async function runSetup(options) {
|
|
|
3385
3435
|
});
|
|
3386
3436
|
if (install) await runServiceInstall();
|
|
3387
3437
|
else say.info("Skipped. Run `lumi-runner start` manually, or `lumi-runner service install` later.");
|
|
3438
|
+
} else if (service2 === "running" || service2 === "installed") {
|
|
3439
|
+
say.step("Restarting the daemon so it picks up this configuration\u2026");
|
|
3440
|
+
await runServiceRestart();
|
|
3388
3441
|
}
|
|
3389
3442
|
say.outro(
|
|
3390
3443
|
doctorExit === 0 ? "Ready. This machine will claim jobs for its Ships." : "Setup finished, but some checks failed \u2014 fix those and re-run `lumi-runner doctor`."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumi.ai/runner",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
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.",
|