@factiii/runner 0.12.0 → 0.12.2
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 +329 -161
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3984,7 +3984,7 @@ var require_has_flag = __commonJS({
|
|
|
3984
3984
|
var require_supports_color = __commonJS({
|
|
3985
3985
|
"../../node_modules/debug/node_modules/supports-color/index.js"(exports2, module2) {
|
|
3986
3986
|
"use strict";
|
|
3987
|
-
var
|
|
3987
|
+
var os8 = require("os");
|
|
3988
3988
|
var hasFlag = require_has_flag();
|
|
3989
3989
|
var env = process.env;
|
|
3990
3990
|
var forceColor;
|
|
@@ -4022,7 +4022,7 @@ var require_supports_color = __commonJS({
|
|
|
4022
4022
|
}
|
|
4023
4023
|
const min = forceColor ? 1 : 0;
|
|
4024
4024
|
if (process.platform === "win32") {
|
|
4025
|
-
const osRelease =
|
|
4025
|
+
const osRelease = os8.release().split(".");
|
|
4026
4026
|
if (Number(process.versions.node.split(".")[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
4027
4027
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
4028
4028
|
}
|
|
@@ -6499,7 +6499,7 @@ var require_websocket = __commonJS({
|
|
|
6499
6499
|
var http = require("http");
|
|
6500
6500
|
var net3 = require("net");
|
|
6501
6501
|
var tls = require("tls");
|
|
6502
|
-
var { randomBytes, createHash: createHash2 } = require("crypto");
|
|
6502
|
+
var { randomBytes: randomBytes2, createHash: createHash2 } = require("crypto");
|
|
6503
6503
|
var { Duplex, Readable } = require("stream");
|
|
6504
6504
|
var { URL: URL2 } = require("url");
|
|
6505
6505
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -7037,7 +7037,7 @@ var require_websocket = __commonJS({
|
|
|
7037
7037
|
}
|
|
7038
7038
|
}
|
|
7039
7039
|
const defaultPort = isSecure ? 443 : 80;
|
|
7040
|
-
const key =
|
|
7040
|
+
const key = randomBytes2(16).toString("base64");
|
|
7041
7041
|
const request = isSecure ? https.request : http.request;
|
|
7042
7042
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
7043
7043
|
let perMessageDeflate;
|
|
@@ -13805,6 +13805,7 @@ async function teardownSession(s, target) {
|
|
|
13805
13805
|
|
|
13806
13806
|
// ../../shared/all/helpers/board-agent-core/exec.ts
|
|
13807
13807
|
var import_child_process2 = require("child_process");
|
|
13808
|
+
var import_crypto = require("crypto");
|
|
13808
13809
|
var import_fs2 = require("fs");
|
|
13809
13810
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
13810
13811
|
var import_net = __toESM(require("net"));
|
|
@@ -13845,6 +13846,22 @@ async function allocateSpacePorts(spaceDir) {
|
|
|
13845
13846
|
await import_promises2.default.writeFile(portsFilePath(spaceDir), ports.join(","), "utf-8");
|
|
13846
13847
|
return ports;
|
|
13847
13848
|
}
|
|
13849
|
+
var SECRET_FILE = ".secret";
|
|
13850
|
+
function spaceSecret(spaceDir) {
|
|
13851
|
+
const file = import_path4.default.join(spaceDir, SECRET_FILE);
|
|
13852
|
+
try {
|
|
13853
|
+
const existing = (0, import_fs2.readFileSync)(file, "utf-8").trim();
|
|
13854
|
+
if (existing) return existing;
|
|
13855
|
+
} catch {
|
|
13856
|
+
}
|
|
13857
|
+
const secret = (0, import_crypto.randomBytes)(24).toString("hex");
|
|
13858
|
+
try {
|
|
13859
|
+
(0, import_fs2.mkdirSync)(spaceDir, { recursive: true });
|
|
13860
|
+
(0, import_fs2.writeFileSync)(file, secret, { encoding: "utf-8", mode: 384 });
|
|
13861
|
+
} catch {
|
|
13862
|
+
}
|
|
13863
|
+
return secret;
|
|
13864
|
+
}
|
|
13848
13865
|
function readSpacePorts(spaceDir) {
|
|
13849
13866
|
try {
|
|
13850
13867
|
return (0, import_fs2.readFileSync)(portsFilePath(spaceDir), "utf-8").split(",").map((p) => Number(p.trim())).filter((p) => Number.isInteger(p) && p > 0);
|
|
@@ -14144,6 +14161,18 @@ var import_promises5 = __toESM(require("fs/promises"));
|
|
|
14144
14161
|
var import_os2 = __toESM(require("os"));
|
|
14145
14162
|
var import_path7 = __toESM(require("path"));
|
|
14146
14163
|
|
|
14164
|
+
// ../../shared/all/domains/products.ts
|
|
14165
|
+
var TRONS_PER_USD = 1e6;
|
|
14166
|
+
var TRONS_PER_CENT = TRONS_PER_USD / 100;
|
|
14167
|
+
var FOUNDERS_REFUND_WINDOW_DAYS = 60;
|
|
14168
|
+
var FOUNDERS_REFUND_POLICY = `Refundable within ${FOUNDERS_REFUND_WINDOW_DAYS} days of purchase \u2014 after that, all sales are final.`;
|
|
14169
|
+
|
|
14170
|
+
// ../../shared/all/domains/autoTopUp.ts
|
|
14171
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
14172
|
+
var MONTH_MS = 30 * DAY_MS;
|
|
14173
|
+
var CLAIM_STALE_MS = 5 * 60 * 1e3;
|
|
14174
|
+
var MIN_THRESHOLD_TRONS = TRONS_PER_CENT;
|
|
14175
|
+
|
|
14147
14176
|
// ../../shared/all/domains/posts.ts
|
|
14148
14177
|
var postDataSort = /* @__PURE__ */ ((postDataSort2) => {
|
|
14149
14178
|
postDataSort2["DAILY"] = "DAILY";
|
|
@@ -14346,7 +14375,11 @@ var checkPurchaseSchema = external_exports.object({
|
|
|
14346
14375
|
productId: external_exports.string(),
|
|
14347
14376
|
type: external_exports.enum(["STRIPE", "GOOGLE", "APPLE"]),
|
|
14348
14377
|
receivedPrice: external_exports.number().int().min(0, "Received price must be a positive integer in cents"),
|
|
14349
|
-
currency: external_exports.string().length(3).default("USD")
|
|
14378
|
+
currency: external_exports.string().length(3).default("USD"),
|
|
14379
|
+
// Explicit consent to keep this card for auto top-up. Card-network rules
|
|
14380
|
+
// require the buyer to agree before a card may be charged while they are not
|
|
14381
|
+
// present, so this must come from a checkbox they ticked — never a default.
|
|
14382
|
+
saveCardForAutoTopUp: external_exports.boolean().default(false)
|
|
14350
14383
|
});
|
|
14351
14384
|
var creditPurchaseSchema = external_exports.object({
|
|
14352
14385
|
orderId: external_exports.string(),
|
|
@@ -14649,6 +14682,19 @@ var skillFrontmatterSchema = external_exports.object({
|
|
|
14649
14682
|
}).strict();
|
|
14650
14683
|
var skillAgentSchema = external_exports.enum(["claude", "codex"]);
|
|
14651
14684
|
var skillScopeSchema = external_exports.enum(["global", "repo"]);
|
|
14685
|
+
var autoTopUpSchema = external_exports.object({
|
|
14686
|
+
enabled: external_exports.boolean(),
|
|
14687
|
+
productId: external_exports.number().int().positive(),
|
|
14688
|
+
thresholdTrons: external_exports.number().int().min(MIN_THRESHOLD_TRONS),
|
|
14689
|
+
dailyCapCents: external_exports.number().int().min(0),
|
|
14690
|
+
monthlyCapCents: external_exports.number().int().min(0),
|
|
14691
|
+
// Alerts. `alertBalanceTrons` is null when the low-balance warning is off;
|
|
14692
|
+
// it works independently of `enabled`, since with auto top-up off it is the
|
|
14693
|
+
// only safety net there is.
|
|
14694
|
+
alertOnTopUp: external_exports.boolean(),
|
|
14695
|
+
alertBalanceTrons: external_exports.number().int().min(MIN_THRESHOLD_TRONS).nullable(),
|
|
14696
|
+
alertNearCap: external_exports.boolean()
|
|
14697
|
+
});
|
|
14652
14698
|
|
|
14653
14699
|
// ../../shared/all/helpers/board-agent-core/host-deploy.ts
|
|
14654
14700
|
var import_child_process3 = require("child_process");
|
|
@@ -16903,7 +16949,7 @@ ${buildPrompt(prompts_default.implement)}`;
|
|
|
16903
16949
|
};
|
|
16904
16950
|
|
|
16905
16951
|
// ../../shared/all/helpers/terminal-ai/terminal-mode.ts
|
|
16906
|
-
var
|
|
16952
|
+
var import_crypto4 = require("crypto");
|
|
16907
16953
|
|
|
16908
16954
|
// ../../shared/all/helpers/board-agent-core/boardTerminal.ts
|
|
16909
16955
|
function boardTerminalKey(spaceSlug) {
|
|
@@ -16959,12 +17005,12 @@ function drainStateMessages(buffer) {
|
|
|
16959
17005
|
}
|
|
16960
17006
|
|
|
16961
17007
|
// ../../shared/all/helpers/terminal-ai/digest.ts
|
|
16962
|
-
var
|
|
17008
|
+
var import_crypto3 = __toESM(require("crypto"));
|
|
16963
17009
|
var import_promises8 = require("fs/promises");
|
|
16964
17010
|
var import_path10 = __toESM(require("path"));
|
|
16965
17011
|
|
|
16966
17012
|
// ../../shared/all/helpers/board-agent-core/plugin.ts
|
|
16967
|
-
var
|
|
17013
|
+
var import_crypto2 = __toESM(require("crypto"));
|
|
16968
17014
|
var import_fs4 = require("fs");
|
|
16969
17015
|
var import_promises7 = require("fs/promises");
|
|
16970
17016
|
var import_os3 = __toESM(require("os"));
|
|
@@ -17013,7 +17059,7 @@ async function treeFiles(dir) {
|
|
|
17013
17059
|
return files.sort();
|
|
17014
17060
|
}
|
|
17015
17061
|
async function fingerprint(source, url2, version = "") {
|
|
17016
|
-
const hash =
|
|
17062
|
+
const hash = import_crypto2.default.createHash("sha256").update(url2).update(version);
|
|
17017
17063
|
for (const file of await treeFiles(source)) {
|
|
17018
17064
|
hash.update(import_path9.default.relative(source, file));
|
|
17019
17065
|
hash.update(await (0, import_promises7.readFile)(file));
|
|
@@ -17131,7 +17177,7 @@ async function pluginContribution() {
|
|
|
17131
17177
|
return value2;
|
|
17132
17178
|
}
|
|
17133
17179
|
async function terminalDigest(target) {
|
|
17134
|
-
const hash =
|
|
17180
|
+
const hash = import_crypto3.default.createHash("sha256").update(TMUX_CONF).update(terminalPrelude(CWD_PLACEHOLDER, target.paths.bin));
|
|
17135
17181
|
const env = target.stateEnv();
|
|
17136
17182
|
for (const key of Object.keys(env).sort()) {
|
|
17137
17183
|
if (VOLATILE_ENV_KEYS.has(key)) continue;
|
|
@@ -18330,7 +18376,7 @@ var TerminalModeEngine = class {
|
|
|
18330
18376
|
}
|
|
18331
18377
|
const safeExt = (ext || "png").toLowerCase().replace(/[^a-z0-9]/g, "") || "png";
|
|
18332
18378
|
const oneShot = !dropId;
|
|
18333
|
-
const safeId2 = oneShot ? (0,
|
|
18379
|
+
const safeId2 = oneShot ? (0, import_crypto4.randomUUID)() : dropId.toLowerCase().replace(/[^a-z0-9-]/g, "");
|
|
18334
18380
|
if (!safeId2) throw new Error("Invalid drop id.");
|
|
18335
18381
|
const dropsDir = `${target.paths.root}/drops`;
|
|
18336
18382
|
const abs = `${dropsDir}/${safeId2}.${safeExt}`;
|
|
@@ -18531,15 +18577,7 @@ var TerminalModeEngine = class {
|
|
|
18531
18577
|
`${tmuxCli(target)} list-panes -a -F '${TAB_LIST_FORMAT}' 2>/dev/null || true`
|
|
18532
18578
|
);
|
|
18533
18579
|
const rows = parseTerminalTabs(stdout, prefix);
|
|
18534
|
-
|
|
18535
|
-
return rows.map(({ id, name, fp, dead }) => ({
|
|
18536
|
-
id,
|
|
18537
|
-
name,
|
|
18538
|
-
// No stamp means a session older than the stamp, as blind to the
|
|
18539
|
-
// current build as a mismatched one.
|
|
18540
|
-
stale: !!current && fp !== current,
|
|
18541
|
-
dead
|
|
18542
|
-
}));
|
|
18580
|
+
return rows.map(({ id, name, dead }) => ({ id, name, dead }));
|
|
18543
18581
|
} catch {
|
|
18544
18582
|
return [];
|
|
18545
18583
|
}
|
|
@@ -19195,7 +19233,7 @@ async function listGithubBranches(token, fullName) {
|
|
|
19195
19233
|
var import_path13 = __toESM(require("path"));
|
|
19196
19234
|
|
|
19197
19235
|
// ../../shared/all/helpers/board-agent-core/secrets-gate.ts
|
|
19198
|
-
var
|
|
19236
|
+
var import_crypto5 = __toESM(require("crypto"));
|
|
19199
19237
|
var DEPLOY_ORIGIN = "Deploy run";
|
|
19200
19238
|
var UNATTRIBUTED = "This runner did not start the session that asked, so the request was refused. `factiii-secrets` only works inside a session the runner spawned.";
|
|
19201
19239
|
var SecretsGate = class {
|
|
@@ -19220,7 +19258,14 @@ var SecretsGate = class {
|
|
|
19220
19258
|
/** Mint a token for one session, labelled with what the owner should see on
|
|
19221
19259
|
* the prompt - the label is most of the decision, so name the session. */
|
|
19222
19260
|
issueToken(origin) {
|
|
19223
|
-
const token =
|
|
19261
|
+
const token = import_crypto5.default.randomBytes(24).toString("hex");
|
|
19262
|
+
this.tokens.set(token, origin);
|
|
19263
|
+
return token;
|
|
19264
|
+
}
|
|
19265
|
+
/** Register a durable token the caller already holds, instead of minting a
|
|
19266
|
+
* fresh one. For the space token: terminals outlive this process, so a
|
|
19267
|
+
* minted one leaves them unattributed after a restart. Idempotent. */
|
|
19268
|
+
adoptToken(token, origin) {
|
|
19224
19269
|
this.tokens.set(token, origin);
|
|
19225
19270
|
return token;
|
|
19226
19271
|
}
|
|
@@ -19244,7 +19289,7 @@ var SecretsGate = class {
|
|
|
19244
19289
|
return new Promise((resolve2) => {
|
|
19245
19290
|
this.waiting = {
|
|
19246
19291
|
request: {
|
|
19247
|
-
id:
|
|
19292
|
+
id: import_crypto5.default.randomUUID(),
|
|
19248
19293
|
command: opts.command,
|
|
19249
19294
|
keys: opts.keys,
|
|
19250
19295
|
origin: opts.origin,
|
|
@@ -19288,7 +19333,7 @@ var import_path12 = __toESM(require("path"));
|
|
|
19288
19333
|
|
|
19289
19334
|
// ../../shared/all/helpers/board-agent-core/host-secrets.ts
|
|
19290
19335
|
var import_child_process4 = require("child_process");
|
|
19291
|
-
var
|
|
19336
|
+
var import_crypto6 = __toESM(require("crypto"));
|
|
19292
19337
|
var import_promises9 = __toESM(require("fs/promises"));
|
|
19293
19338
|
var import_path11 = __toESM(require("path"));
|
|
19294
19339
|
var import_util6 = require("util");
|
|
@@ -19316,7 +19361,7 @@ async function assertBinaries() {
|
|
|
19316
19361
|
}
|
|
19317
19362
|
}
|
|
19318
19363
|
function deriveKey(password, salt) {
|
|
19319
|
-
return
|
|
19364
|
+
return import_crypto6.default.scryptSync(password, salt, 32, {
|
|
19320
19365
|
N: SCRYPT_N,
|
|
19321
19366
|
maxmem: 64 * 1024 * 1024
|
|
19322
19367
|
});
|
|
@@ -19345,9 +19390,9 @@ async function initSecrets(configDir, password) {
|
|
|
19345
19390
|
if (!identity || !recipient) {
|
|
19346
19391
|
throw new Error("age-keygen returned no usable keypair.");
|
|
19347
19392
|
}
|
|
19348
|
-
const salt =
|
|
19349
|
-
const iv =
|
|
19350
|
-
const cipher =
|
|
19393
|
+
const salt = import_crypto6.default.randomBytes(16);
|
|
19394
|
+
const iv = import_crypto6.default.randomBytes(12);
|
|
19395
|
+
const cipher = import_crypto6.default.createCipheriv(
|
|
19351
19396
|
"aes-256-gcm",
|
|
19352
19397
|
deriveKey(password, salt),
|
|
19353
19398
|
iv
|
|
@@ -19372,7 +19417,7 @@ async function initSecrets(configDir, password) {
|
|
|
19372
19417
|
return { success: true };
|
|
19373
19418
|
}
|
|
19374
19419
|
function decryptIdentity(box, password) {
|
|
19375
|
-
const decipher =
|
|
19420
|
+
const decipher = import_crypto6.default.createDecipheriv(
|
|
19376
19421
|
"aes-256-gcm",
|
|
19377
19422
|
deriveKey(password, Buffer.from(box.salt, "base64")),
|
|
19378
19423
|
Buffer.from(box.iv, "base64")
|
|
@@ -20037,6 +20082,7 @@ var SpaceCore = class {
|
|
|
20037
20082
|
this.runnerToken = options.runnerToken;
|
|
20038
20083
|
this.serverUrl = options.serverUrl;
|
|
20039
20084
|
this.mcp = options.mcp;
|
|
20085
|
+
this.mcpToken = options.mcpToken;
|
|
20040
20086
|
this.requestSudo = options.requestSudo;
|
|
20041
20087
|
this.activity = new ActivityTracker(this.emitter);
|
|
20042
20088
|
this.gate.subscribe(() => {
|
|
@@ -20131,7 +20177,11 @@ var SpaceCore = class {
|
|
|
20131
20177
|
* that DOES know itself - the deploy run - mints its own and overrides this.
|
|
20132
20178
|
*/
|
|
20133
20179
|
secretsToken() {
|
|
20134
|
-
this.spaceSecretsToken
|
|
20180
|
+
if (this.spaceSecretsToken) return this.spaceSecretsToken;
|
|
20181
|
+
this.spaceSecretsToken = this.gate.adoptToken(
|
|
20182
|
+
spaceSecret(this.spaceDir()),
|
|
20183
|
+
"A session on this board"
|
|
20184
|
+
);
|
|
20135
20185
|
return this.spaceSecretsToken;
|
|
20136
20186
|
}
|
|
20137
20187
|
readConfig() {
|
|
@@ -20142,7 +20192,8 @@ var SpaceCore = class {
|
|
|
20142
20192
|
target() {
|
|
20143
20193
|
this.execTarget ??= hostTarget(this.spaceDir(), () => {
|
|
20144
20194
|
const env = {};
|
|
20145
|
-
|
|
20195
|
+
const onedrive = this.mcpToken?.();
|
|
20196
|
+
if (onedrive) env[MCP_TOKEN_ENV] = onedrive;
|
|
20146
20197
|
const board = this.boardMcp();
|
|
20147
20198
|
if (board) env[BOARD_MCP_TOKEN_ENV] = board.token;
|
|
20148
20199
|
env.FACTIII_SECRETS_TOKEN = this.secretsToken();
|
|
@@ -22306,12 +22357,59 @@ ${c.content.trim() || "(no description)"}`
|
|
|
22306
22357
|
|
|
22307
22358
|
// ../../shared/all/helpers/board-agent-core/index-service.ts
|
|
22308
22359
|
var import_child_process5 = require("child_process");
|
|
22309
|
-
var
|
|
22310
|
-
var
|
|
22360
|
+
var import_crypto7 = require("crypto");
|
|
22361
|
+
var import_fs6 = require("fs");
|
|
22311
22362
|
var import_promises11 = __toESM(require("fs/promises"));
|
|
22312
22363
|
var import_net3 = require("net");
|
|
22313
|
-
var
|
|
22364
|
+
var import_path17 = require("path");
|
|
22314
22365
|
var import_util7 = require("util");
|
|
22366
|
+
|
|
22367
|
+
// ../../shared/all/helpers/board-agent-core/node-version.ts
|
|
22368
|
+
var import_fs5 = require("fs");
|
|
22369
|
+
var import_os4 = __toESM(require("os"));
|
|
22370
|
+
var import_path16 = require("path");
|
|
22371
|
+
var MIN_INDEX_NODE_MAJOR = 20;
|
|
22372
|
+
var MAX_INDEX_NODE_MAJOR = 24;
|
|
22373
|
+
var NODE_OVERRIDE_ENV = "FACTIII_INDEX_NODE";
|
|
22374
|
+
function nodeMajor(version) {
|
|
22375
|
+
return Number(version.replace(/^v/, "").split(".")[0]);
|
|
22376
|
+
}
|
|
22377
|
+
function indexNodeSupported(version = process.versions.node) {
|
|
22378
|
+
const major = nodeMajor(version);
|
|
22379
|
+
return !Number.isNaN(major) && major >= MIN_INDEX_NODE_MAJOR && major <= MAX_INDEX_NODE_MAJOR;
|
|
22380
|
+
}
|
|
22381
|
+
function candidateNodes() {
|
|
22382
|
+
const paths = [];
|
|
22383
|
+
const home = import_os4.default.homedir();
|
|
22384
|
+
const nvmRoot = (0, import_path16.join)(home, ".nvm", "versions", "node");
|
|
22385
|
+
try {
|
|
22386
|
+
const versions = (0, import_fs5.readdirSync)(nvmRoot).filter((v) => indexNodeSupported(v)).sort((a, b) => nodeMajor(b) - nodeMajor(a));
|
|
22387
|
+
for (const v of versions) paths.push((0, import_path16.join)(nvmRoot, v, "bin", "node"));
|
|
22388
|
+
} catch {
|
|
22389
|
+
}
|
|
22390
|
+
for (const prefix of ["/opt/homebrew/opt", "/usr/local/opt"]) {
|
|
22391
|
+
for (let major = MAX_INDEX_NODE_MAJOR; major >= MIN_INDEX_NODE_MAJOR; major -= 1) {
|
|
22392
|
+
paths.push((0, import_path16.join)(prefix, `node@${major}`, "bin", "node"));
|
|
22393
|
+
}
|
|
22394
|
+
}
|
|
22395
|
+
return paths;
|
|
22396
|
+
}
|
|
22397
|
+
function findIndexNode() {
|
|
22398
|
+
const override = process.env[NODE_OVERRIDE_ENV];
|
|
22399
|
+
if (override && (0, import_fs5.existsSync)(override)) return override;
|
|
22400
|
+
if (indexNodeSupported()) return process.execPath;
|
|
22401
|
+
return candidateNodes().find((p) => (0, import_fs5.existsSync)(p)) ?? null;
|
|
22402
|
+
}
|
|
22403
|
+
function unsupportedNodeMessage() {
|
|
22404
|
+
return `The OneDrive index needs Node ${MIN_INDEX_NODE_MAJOR}-${MAX_INDEX_NODE_MAJOR} (pinned 24.11.1); this host runs Node ${process.versions.node} and has no supported Node installed. It compiles better-sqlite3 from source, and that build fails on Node ${MAX_INDEX_NODE_MAJOR + 1}+.
|
|
22405
|
+
|
|
22406
|
+
Install one: nvm install 24.11.1
|
|
22407
|
+
or point the runner at an existing one: export ${NODE_OVERRIDE_ENV}=/path/to/node
|
|
22408
|
+
|
|
22409
|
+
Everything else on this runner works meanwhile \u2014 only OneDrive search is off.`;
|
|
22410
|
+
}
|
|
22411
|
+
|
|
22412
|
+
// ../../shared/all/helpers/board-agent-core/index-service.ts
|
|
22315
22413
|
var execFileAsync4 = (0, import_util7.promisify)(import_child_process5.execFile);
|
|
22316
22414
|
var IndexService = class {
|
|
22317
22415
|
constructor(opts) {
|
|
@@ -22324,7 +22422,7 @@ var IndexService = class {
|
|
|
22324
22422
|
/** Per-space bearer for MCP + file, derived from the control token (the
|
|
22325
22423
|
* service computes the same). */
|
|
22326
22424
|
bearerToken(spaceSlug) {
|
|
22327
|
-
return (0,
|
|
22425
|
+
return (0, import_crypto7.createHmac)("sha256", this.opts.controlToken).update(`mcp:${spaceSlug}`).digest("hex");
|
|
22328
22426
|
}
|
|
22329
22427
|
/** MCP bridge for a space. Null until the service is up (which it kicks
|
|
22330
22428
|
* off), so a bad :0 URL is never baked in; provisionMcpLane re-runs. */
|
|
@@ -22364,27 +22462,25 @@ var IndexService = class {
|
|
|
22364
22462
|
await this.waitReady(this.port);
|
|
22365
22463
|
return;
|
|
22366
22464
|
}
|
|
22367
|
-
|
|
22465
|
+
const nodeBin = findIndexNode();
|
|
22466
|
+
if (!nodeBin) throw new Error(unsupportedNodeMessage());
|
|
22467
|
+
await this.installDeps(nodeBin);
|
|
22368
22468
|
const port = await this.stablePort();
|
|
22369
22469
|
const log = await this.openLog();
|
|
22370
|
-
this.proc = (0, import_child_process5.spawn)(
|
|
22371
|
-
|
|
22372
|
-
|
|
22373
|
-
|
|
22374
|
-
|
|
22375
|
-
|
|
22376
|
-
|
|
22377
|
-
|
|
22378
|
-
|
|
22379
|
-
|
|
22380
|
-
|
|
22381
|
-
|
|
22382
|
-
|
|
22383
|
-
|
|
22384
|
-
// Own process group so stop() reaps it; nothing else reaps orphans here.
|
|
22385
|
-
detached: true
|
|
22386
|
-
}
|
|
22387
|
-
);
|
|
22470
|
+
this.proc = (0, import_child_process5.spawn)(nodeBin, [(0, import_path17.join)(this.opts.indexDir, "index.js")], {
|
|
22471
|
+
cwd: this.opts.indexDir,
|
|
22472
|
+
env: {
|
|
22473
|
+
...process.env,
|
|
22474
|
+
NODE_ENV: "production",
|
|
22475
|
+
PORT: String(port),
|
|
22476
|
+
CONTROL_TOKEN: this.opts.controlToken,
|
|
22477
|
+
EMBED_URL: this.opts.embedUrl(),
|
|
22478
|
+
INDEX_DATA_DIR: this.opts.dataDir
|
|
22479
|
+
},
|
|
22480
|
+
stdio: log ? ["ignore", log, log] : "ignore",
|
|
22481
|
+
// Own process group so stop() reaps it; nothing else reaps orphans here.
|
|
22482
|
+
detached: true
|
|
22483
|
+
});
|
|
22388
22484
|
this.proc.on("exit", () => {
|
|
22389
22485
|
this.proc = null;
|
|
22390
22486
|
this.ready = null;
|
|
@@ -22398,31 +22494,58 @@ var IndexService = class {
|
|
|
22398
22494
|
async openLog() {
|
|
22399
22495
|
try {
|
|
22400
22496
|
await import_promises11.default.mkdir(this.opts.dataDir, { recursive: true });
|
|
22401
|
-
return (0,
|
|
22497
|
+
return (0, import_fs6.openSync)((0, import_path17.join)(this.opts.dataDir, "index.log"), "a");
|
|
22402
22498
|
} catch {
|
|
22403
22499
|
return null;
|
|
22404
22500
|
}
|
|
22405
22501
|
}
|
|
22406
|
-
/** Install the index's native deps against
|
|
22407
|
-
* platform needing node-gyp fails here rather than at query time.
|
|
22408
|
-
|
|
22502
|
+
/** Install the index's native deps against the node that will RUN it, so a
|
|
22503
|
+
* platform needing node-gyp fails here rather than at query time.
|
|
22504
|
+
*
|
|
22505
|
+
* The deps are compiled per ABI, so they are only reusable under the node
|
|
22506
|
+
* that built them. The stamp records which ABI is on disk and forces a
|
|
22507
|
+
* rebuild when the interpreter changes — otherwise a host that switches
|
|
22508
|
+
* node versions loads a binding built for the old one and dies with
|
|
22509
|
+
* "compiled against a different Node.js version". */
|
|
22510
|
+
async installDeps(nodeBin) {
|
|
22409
22511
|
const dir = this.opts.indexDir;
|
|
22410
|
-
|
|
22512
|
+
const { stdout } = await execFileAsync4(nodeBin, [
|
|
22513
|
+
"-p",
|
|
22514
|
+
"process.versions.modules"
|
|
22515
|
+
]);
|
|
22516
|
+
const abi = stdout.trim();
|
|
22517
|
+
const stamp = (0, import_path17.join)(dir, ".node-abi");
|
|
22518
|
+
if ((0, import_fs6.existsSync)((0, import_path17.join)(dir, "node_modules"))) {
|
|
22519
|
+
const built = await import_promises11.default.readFile(stamp, "utf-8").catch(() => "");
|
|
22520
|
+
if (built.trim() === abi) return;
|
|
22521
|
+
await import_promises11.default.rm((0, import_path17.join)(dir, "node_modules"), { recursive: true, force: true });
|
|
22522
|
+
}
|
|
22411
22523
|
await import_promises11.default.copyFile(
|
|
22412
|
-
(0,
|
|
22413
|
-
(0,
|
|
22524
|
+
(0, import_path17.join)(dir, "image-package.json"),
|
|
22525
|
+
(0, import_path17.join)(dir, "package.json")
|
|
22414
22526
|
);
|
|
22415
22527
|
await execFileAsync4(
|
|
22416
22528
|
"npm",
|
|
22417
22529
|
["install", "--omit=dev", "--no-audit", "--no-fund"],
|
|
22418
|
-
{
|
|
22530
|
+
{
|
|
22531
|
+
cwd: dir,
|
|
22532
|
+
// npm resolves node-gyp's target from the node running it, so the
|
|
22533
|
+
// chosen interpreter has to lead PATH or the build targets the wrong ABI.
|
|
22534
|
+
env: {
|
|
22535
|
+
...process.env,
|
|
22536
|
+
PATH: `${(0, import_path17.dirname)(nodeBin)}:${process.env.PATH ?? ""}`
|
|
22537
|
+
}
|
|
22538
|
+
}
|
|
22419
22539
|
);
|
|
22540
|
+
await import_promises11.default.writeFile(stamp, `${abi}
|
|
22541
|
+
`, "utf-8").catch(() => {
|
|
22542
|
+
});
|
|
22420
22543
|
}
|
|
22421
22544
|
/** The port to listen on, kept stable across restarts. The URL goes into the
|
|
22422
22545
|
* CLIs' persistent MCP config, so a fresh port each start would leave every
|
|
22423
22546
|
* registration pointing at a dead one. */
|
|
22424
22547
|
async stablePort() {
|
|
22425
|
-
const file = (0,
|
|
22548
|
+
const file = (0, import_path17.join)(this.opts.dataDir, ".port");
|
|
22426
22549
|
const saved = Number(await import_promises11.default.readFile(file, "utf-8").catch(() => ""));
|
|
22427
22550
|
if (Number.isInteger(saved) && saved > 0 && await isFree(saved)) {
|
|
22428
22551
|
return saved;
|
|
@@ -22597,25 +22720,25 @@ async function pairWithBrowser(serverUrl) {
|
|
|
22597
22720
|
}
|
|
22598
22721
|
|
|
22599
22722
|
// src/config.ts
|
|
22600
|
-
var
|
|
22601
|
-
var
|
|
22602
|
-
var
|
|
22723
|
+
var import_fs10 = __toESM(require("fs"));
|
|
22724
|
+
var import_os7 = __toESM(require("os"));
|
|
22725
|
+
var import_path22 = __toESM(require("path"));
|
|
22603
22726
|
|
|
22604
22727
|
// src/secureFile.ts
|
|
22605
|
-
var
|
|
22606
|
-
var
|
|
22607
|
-
var
|
|
22728
|
+
var import_crypto10 = __toESM(require("crypto"));
|
|
22729
|
+
var import_fs9 = __toESM(require("fs"));
|
|
22730
|
+
var import_path21 = __toESM(require("path"));
|
|
22608
22731
|
|
|
22609
22732
|
// src/keychain.ts
|
|
22610
22733
|
var import_child_process7 = require("child_process");
|
|
22611
|
-
var
|
|
22612
|
-
var
|
|
22613
|
-
var
|
|
22614
|
-
var
|
|
22734
|
+
var import_crypto8 = __toESM(require("crypto"));
|
|
22735
|
+
var import_fs7 = __toESM(require("fs"));
|
|
22736
|
+
var import_os5 = __toESM(require("os"));
|
|
22737
|
+
var import_path18 = __toESM(require("path"));
|
|
22615
22738
|
var SERVICE = "factiii-runner";
|
|
22616
22739
|
var ACCOUNT = "config-encryption-key";
|
|
22617
|
-
var DPAPI_KEY_FILE =
|
|
22618
|
-
|
|
22740
|
+
var DPAPI_KEY_FILE = import_path18.default.join(
|
|
22741
|
+
import_os5.default.homedir(),
|
|
22619
22742
|
".factiii-runner",
|
|
22620
22743
|
"config-key.dpapi"
|
|
22621
22744
|
);
|
|
@@ -22695,7 +22818,7 @@ function linuxWrite(key) {
|
|
|
22695
22818
|
function winRead() {
|
|
22696
22819
|
let wrapped;
|
|
22697
22820
|
try {
|
|
22698
|
-
wrapped =
|
|
22821
|
+
wrapped = import_fs7.default.readFileSync(DPAPI_KEY_FILE, "utf-8").trim();
|
|
22699
22822
|
} catch {
|
|
22700
22823
|
return { state: "missing" };
|
|
22701
22824
|
}
|
|
@@ -22720,8 +22843,8 @@ function winWrite(key) {
|
|
|
22720
22843
|
"-Command",
|
|
22721
22844
|
`ConvertTo-SecureString -String '${key.toString("base64")}' -AsPlainText -Force | ConvertFrom-SecureString`
|
|
22722
22845
|
]);
|
|
22723
|
-
|
|
22724
|
-
|
|
22846
|
+
import_fs7.default.mkdirSync(import_path18.default.dirname(DPAPI_KEY_FILE), { recursive: true });
|
|
22847
|
+
import_fs7.default.writeFileSync(DPAPI_KEY_FILE, `${out.trim()}
|
|
22725
22848
|
`, { mode: 384 });
|
|
22726
22849
|
}
|
|
22727
22850
|
function keychainBackend() {
|
|
@@ -22860,7 +22983,7 @@ function resolve() {
|
|
|
22860
22983
|
cached = { key: null, status: diagnose() };
|
|
22861
22984
|
return cached;
|
|
22862
22985
|
}
|
|
22863
|
-
const fresh =
|
|
22986
|
+
const fresh = import_crypto8.default.randomBytes(32);
|
|
22864
22987
|
writeKey(fresh);
|
|
22865
22988
|
const verify = readKey();
|
|
22866
22989
|
if (verify.state !== "found" || !verify.key.equals(fresh)) {
|
|
@@ -22882,23 +23005,23 @@ function keychainStatus() {
|
|
|
22882
23005
|
}
|
|
22883
23006
|
|
|
22884
23007
|
// src/secureStore.ts
|
|
22885
|
-
var
|
|
22886
|
-
var
|
|
22887
|
-
var
|
|
23008
|
+
var import_crypto9 = __toESM(require("crypto"));
|
|
23009
|
+
var import_fs8 = __toESM(require("fs"));
|
|
23010
|
+
var import_path20 = __toESM(require("path"));
|
|
22888
23011
|
|
|
22889
23012
|
// src/paths.ts
|
|
22890
|
-
var
|
|
22891
|
-
var
|
|
22892
|
-
var CONFIG_DIR =
|
|
23013
|
+
var import_os6 = __toESM(require("os"));
|
|
23014
|
+
var import_path19 = __toESM(require("path"));
|
|
23015
|
+
var CONFIG_DIR = import_path19.default.join(import_os6.default.homedir(), ".factiii-runner");
|
|
22893
23016
|
function safeSlug(spaceSlug) {
|
|
22894
23017
|
return spaceSlug.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
22895
23018
|
}
|
|
22896
23019
|
function spaceDirPath(spaceSlug) {
|
|
22897
|
-
return
|
|
23020
|
+
return import_path19.default.join(CONFIG_DIR, safeSlug(spaceSlug));
|
|
22898
23021
|
}
|
|
22899
23022
|
|
|
22900
23023
|
// src/secureStore.ts
|
|
22901
|
-
var VAULT_KEY_FILE =
|
|
23024
|
+
var VAULT_KEY_FILE = import_path20.default.join(CONFIG_DIR, "vault-key.json");
|
|
22902
23025
|
var SCRYPT_N2 = 1 << 15;
|
|
22903
23026
|
var MIN_PASSWORD_LENGTH = 8;
|
|
22904
23027
|
var SecureStoreError = class extends Error {
|
|
@@ -22925,7 +23048,7 @@ function requireKeychainKey() {
|
|
|
22925
23048
|
);
|
|
22926
23049
|
}
|
|
22927
23050
|
function passwordKey(password, salt) {
|
|
22928
|
-
return
|
|
23051
|
+
return import_crypto9.default.scryptSync(password, salt, 32, {
|
|
22929
23052
|
N: SCRYPT_N2,
|
|
22930
23053
|
maxmem: 64 * 1024 * 1024
|
|
22931
23054
|
});
|
|
@@ -22933,7 +23056,7 @@ function passwordKey(password, salt) {
|
|
|
22933
23056
|
function wrapKey(machineKey, pwKey) {
|
|
22934
23057
|
const ikm = pwKey ? Buffer.concat([machineKey, pwKey]) : machineKey;
|
|
22935
23058
|
return Buffer.from(
|
|
22936
|
-
|
|
23059
|
+
import_crypto9.default.hkdfSync(
|
|
22937
23060
|
"sha256",
|
|
22938
23061
|
ikm,
|
|
22939
23062
|
Buffer.alloc(0),
|
|
@@ -22943,8 +23066,8 @@ function wrapKey(machineKey, pwKey) {
|
|
|
22943
23066
|
);
|
|
22944
23067
|
}
|
|
22945
23068
|
function seal(plaintext, key) {
|
|
22946
|
-
const iv =
|
|
22947
|
-
const cipher =
|
|
23069
|
+
const iv = import_crypto9.default.randomBytes(12);
|
|
23070
|
+
const cipher = import_crypto9.default.createCipheriv("aes-256-gcm", key, iv);
|
|
22948
23071
|
const data = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
22949
23072
|
return {
|
|
22950
23073
|
iv: iv.toString("base64"),
|
|
@@ -22954,7 +23077,7 @@ function seal(plaintext, key) {
|
|
|
22954
23077
|
}
|
|
22955
23078
|
function unseal(box, key) {
|
|
22956
23079
|
try {
|
|
22957
|
-
const decipher =
|
|
23080
|
+
const decipher = import_crypto9.default.createDecipheriv(
|
|
22958
23081
|
"aes-256-gcm",
|
|
22959
23082
|
key,
|
|
22960
23083
|
Buffer.from(box.iv, "base64")
|
|
@@ -22971,7 +23094,7 @@ function unseal(box, key) {
|
|
|
22971
23094
|
function readVaultFile() {
|
|
22972
23095
|
try {
|
|
22973
23096
|
const parsed = JSON.parse(
|
|
22974
|
-
|
|
23097
|
+
import_fs8.default.readFileSync(VAULT_KEY_FILE, "utf-8")
|
|
22975
23098
|
);
|
|
22976
23099
|
return parsed.v === 1 ? parsed : null;
|
|
22977
23100
|
} catch {
|
|
@@ -22979,15 +23102,15 @@ function readVaultFile() {
|
|
|
22979
23102
|
}
|
|
22980
23103
|
}
|
|
22981
23104
|
function writeVaultFile(file) {
|
|
22982
|
-
|
|
23105
|
+
import_fs8.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
22983
23106
|
const tmp = `${VAULT_KEY_FILE}.${process.pid}.tmp`;
|
|
22984
|
-
|
|
23107
|
+
import_fs8.default.writeFileSync(tmp, `${JSON.stringify(file, null, 2)}
|
|
22985
23108
|
`, { mode: 384 });
|
|
22986
|
-
|
|
23109
|
+
import_fs8.default.renameSync(tmp, VAULT_KEY_FILE);
|
|
22987
23110
|
}
|
|
22988
23111
|
var unlockedDek = null;
|
|
22989
23112
|
function createVault(machineKey) {
|
|
22990
|
-
const dek =
|
|
23113
|
+
const dek = import_crypto9.default.randomBytes(32);
|
|
22991
23114
|
writeVaultFile({
|
|
22992
23115
|
v: 1,
|
|
22993
23116
|
passwordEnabled: false,
|
|
@@ -23082,7 +23205,7 @@ function enablePassword(password) {
|
|
|
23082
23205
|
);
|
|
23083
23206
|
}
|
|
23084
23207
|
const dek = dataKey();
|
|
23085
|
-
const salt =
|
|
23208
|
+
const salt = import_crypto9.default.randomBytes(16);
|
|
23086
23209
|
writeVaultFile({
|
|
23087
23210
|
v: 1,
|
|
23088
23211
|
passwordEnabled: true,
|
|
@@ -23110,7 +23233,7 @@ function changePassword(current, next) {
|
|
|
23110
23233
|
"The current password is not correct."
|
|
23111
23234
|
);
|
|
23112
23235
|
}
|
|
23113
|
-
const salt =
|
|
23236
|
+
const salt = import_crypto9.default.randomBytes(16);
|
|
23114
23237
|
writeVaultFile({
|
|
23115
23238
|
v: 1,
|
|
23116
23239
|
passwordEnabled: true,
|
|
@@ -23187,8 +23310,8 @@ function storeStatus() {
|
|
|
23187
23310
|
// src/secureFile.ts
|
|
23188
23311
|
var MAGIC = "FACTIII1";
|
|
23189
23312
|
function encrypt(plain, key, version) {
|
|
23190
|
-
const iv =
|
|
23191
|
-
const cipher =
|
|
23313
|
+
const iv = import_crypto10.default.randomBytes(12);
|
|
23314
|
+
const cipher = import_crypto10.default.createCipheriv("aes-256-gcm", key, iv);
|
|
23192
23315
|
const data = Buffer.concat([cipher.update(plain), cipher.final()]);
|
|
23193
23316
|
const envelope = {
|
|
23194
23317
|
v: version,
|
|
@@ -23199,7 +23322,7 @@ function encrypt(plain, key, version) {
|
|
|
23199
23322
|
return `${MAGIC}${JSON.stringify(envelope)}`;
|
|
23200
23323
|
}
|
|
23201
23324
|
function decrypt(envelope, key) {
|
|
23202
|
-
const decipher =
|
|
23325
|
+
const decipher = import_crypto10.default.createDecipheriv(
|
|
23203
23326
|
"aes-256-gcm",
|
|
23204
23327
|
key,
|
|
23205
23328
|
Buffer.from(envelope.iv, "base64")
|
|
@@ -23220,15 +23343,15 @@ function parseEnvelope(raw) {
|
|
|
23220
23343
|
}
|
|
23221
23344
|
}
|
|
23222
23345
|
function atomicWrite(filePath, body) {
|
|
23223
|
-
|
|
23346
|
+
import_fs9.default.mkdirSync(import_path21.default.dirname(filePath), { recursive: true });
|
|
23224
23347
|
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
23225
|
-
|
|
23226
|
-
|
|
23348
|
+
import_fs9.default.writeFileSync(tmp, body, { mode: 384 });
|
|
23349
|
+
import_fs9.default.renameSync(tmp, filePath);
|
|
23227
23350
|
}
|
|
23228
23351
|
function readSecureBuffer(filePath) {
|
|
23229
23352
|
let raw;
|
|
23230
23353
|
try {
|
|
23231
|
-
raw =
|
|
23354
|
+
raw = import_fs9.default.readFileSync(filePath, "utf-8");
|
|
23232
23355
|
} catch {
|
|
23233
23356
|
return null;
|
|
23234
23357
|
}
|
|
@@ -23285,7 +23408,7 @@ function writeSecureJson(filePath, value2) {
|
|
|
23285
23408
|
function readBootJson(filePath) {
|
|
23286
23409
|
let raw;
|
|
23287
23410
|
try {
|
|
23288
|
-
raw =
|
|
23411
|
+
raw = import_fs9.default.readFileSync(filePath, "utf-8");
|
|
23289
23412
|
} catch {
|
|
23290
23413
|
return null;
|
|
23291
23414
|
}
|
|
@@ -23335,8 +23458,8 @@ function writeBootJson(filePath, value2) {
|
|
|
23335
23458
|
}
|
|
23336
23459
|
|
|
23337
23460
|
// src/config.ts
|
|
23338
|
-
var CONFIG_DIR2 =
|
|
23339
|
-
var CONFIG_FILE =
|
|
23461
|
+
var CONFIG_DIR2 = import_path22.default.join(import_os7.default.homedir(), ".factiii-runner");
|
|
23462
|
+
var CONFIG_FILE = import_path22.default.join(CONFIG_DIR2, "config.json");
|
|
23340
23463
|
function readRunnerConfig() {
|
|
23341
23464
|
return readBootJson(CONFIG_FILE);
|
|
23342
23465
|
}
|
|
@@ -23344,7 +23467,7 @@ function writeRunnerConfig(config) {
|
|
|
23344
23467
|
writeBootJson(CONFIG_FILE, config);
|
|
23345
23468
|
}
|
|
23346
23469
|
function configExists() {
|
|
23347
|
-
return
|
|
23470
|
+
return import_fs10.default.existsSync(CONFIG_FILE);
|
|
23348
23471
|
}
|
|
23349
23472
|
|
|
23350
23473
|
// src/promptPassword.ts
|
|
@@ -23437,7 +23560,54 @@ function currentVersion() {
|
|
|
23437
23560
|
return "0.0.0";
|
|
23438
23561
|
}
|
|
23439
23562
|
}
|
|
23563
|
+
function hostSatisfiesTarget() {
|
|
23564
|
+
let engines = "";
|
|
23565
|
+
try {
|
|
23566
|
+
const res = (0, import_node_child_process.spawnSync)(
|
|
23567
|
+
"npm",
|
|
23568
|
+
["view", `${PACKAGE_NAME}@latest`, "engines.node"],
|
|
23569
|
+
{ encoding: "utf-8", shell: process.platform === "win32" }
|
|
23570
|
+
);
|
|
23571
|
+
if (res.status !== 0) return { ok: true, reason: "" };
|
|
23572
|
+
engines = (res.stdout ?? "").trim();
|
|
23573
|
+
} catch {
|
|
23574
|
+
return { ok: true, reason: "" };
|
|
23575
|
+
}
|
|
23576
|
+
if (!engines) return { ok: true, reason: "" };
|
|
23577
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
23578
|
+
if (Number.isNaN(major)) return { ok: true, reason: "" };
|
|
23579
|
+
const min = /(?:>=|\^|~)\s*v?(\d+)/.exec(engines);
|
|
23580
|
+
if (min && major < Number(min[1])) {
|
|
23581
|
+
return {
|
|
23582
|
+
ok: false,
|
|
23583
|
+
reason: `it needs Node ${engines} and this host runs Node ${process.versions.node}`
|
|
23584
|
+
};
|
|
23585
|
+
}
|
|
23586
|
+
const lessThan = /<\s*v?(\d+)/.exec(engines);
|
|
23587
|
+
if (lessThan && major >= Number(lessThan[1])) {
|
|
23588
|
+
return {
|
|
23589
|
+
ok: false,
|
|
23590
|
+
reason: `it needs Node ${engines} and this host runs Node ${process.versions.node}`
|
|
23591
|
+
};
|
|
23592
|
+
}
|
|
23593
|
+
const atMost = /<=\s*v?(\d+)/.exec(engines);
|
|
23594
|
+
if (atMost && major > Number(atMost[1])) {
|
|
23595
|
+
return {
|
|
23596
|
+
ok: false,
|
|
23597
|
+
reason: `it needs Node ${engines} and this host runs Node ${process.versions.node}`
|
|
23598
|
+
};
|
|
23599
|
+
}
|
|
23600
|
+
return { ok: true, reason: "" };
|
|
23601
|
+
}
|
|
23440
23602
|
function installLatest() {
|
|
23603
|
+
const fit = hostSatisfiesTarget();
|
|
23604
|
+
if (!fit.ok) {
|
|
23605
|
+
console.error(
|
|
23606
|
+
`[update] refusing to install ${PACKAGE_NAME}@latest: ${fit.reason}.
|
|
23607
|
+
[update] Staying on ${currentVersion()}. Install a supported Node (nvm install 24.11.1) and restart the runner to update.`
|
|
23608
|
+
);
|
|
23609
|
+
return false;
|
|
23610
|
+
}
|
|
23441
23611
|
console.log(`[update] installing ${PACKAGE_NAME}@latest...`);
|
|
23442
23612
|
const res = (0, import_node_child_process.spawnSync)("npm", ["install", "-g", `${PACKAGE_NAME}@latest`], {
|
|
23443
23613
|
stdio: "inherit",
|
|
@@ -23457,10 +23627,10 @@ function installLatest() {
|
|
|
23457
23627
|
var import_node_child_process2 = require("node:child_process");
|
|
23458
23628
|
|
|
23459
23629
|
// src/daemon.ts
|
|
23460
|
-
var
|
|
23461
|
-
var
|
|
23630
|
+
var import_crypto11 = require("crypto");
|
|
23631
|
+
var import_fs12 = __toESM(require("fs"));
|
|
23462
23632
|
var import_node_datachannel = require("node-datachannel");
|
|
23463
|
-
var
|
|
23633
|
+
var import_path24 = __toESM(require("path"));
|
|
23464
23634
|
|
|
23465
23635
|
// ../../node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.js
|
|
23466
23636
|
var XMLHttpRequestModule = __toESM(require_XMLHttpRequest(), 1);
|
|
@@ -26959,18 +27129,18 @@ Object.assign(lookup, {
|
|
|
26959
27129
|
});
|
|
26960
27130
|
|
|
26961
27131
|
// src/agent-adapter.ts
|
|
26962
|
-
var
|
|
26963
|
-
var
|
|
27132
|
+
var import_fs11 = __toESM(require("fs"));
|
|
27133
|
+
var import_path23 = __toESM(require("path"));
|
|
26964
27134
|
function credsDir(spaceSlug) {
|
|
26965
|
-
return
|
|
27135
|
+
return import_path23.default.join(spaceDirPath(spaceSlug), ".creds");
|
|
26966
27136
|
}
|
|
26967
27137
|
function githubPath(spaceSlug) {
|
|
26968
|
-
return
|
|
27138
|
+
return import_path23.default.join(credsDir(spaceSlug), "github.json");
|
|
26969
27139
|
}
|
|
26970
27140
|
function connectionPath(spaceSlug) {
|
|
26971
|
-
return
|
|
27141
|
+
return import_path23.default.join(credsDir(spaceSlug), "onedrive.json");
|
|
26972
27142
|
}
|
|
26973
|
-
var RUNNER_CONFIG_PATH =
|
|
27143
|
+
var RUNNER_CONFIG_PATH = import_path23.default.join(CONFIG_DIR, "runner-config.json");
|
|
26974
27144
|
function writeJson(filePath, value2) {
|
|
26975
27145
|
writeSecureJson(filePath, value2);
|
|
26976
27146
|
}
|
|
@@ -26986,14 +27156,14 @@ function fillDefaults(config) {
|
|
|
26986
27156
|
}
|
|
26987
27157
|
function configDirNames() {
|
|
26988
27158
|
try {
|
|
26989
|
-
return
|
|
27159
|
+
return import_fs11.default.readdirSync(CONFIG_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
26990
27160
|
} catch {
|
|
26991
27161
|
return [];
|
|
26992
27162
|
}
|
|
26993
27163
|
}
|
|
26994
27164
|
function provisionedSpaceSlugs() {
|
|
26995
27165
|
return configDirNames().filter(
|
|
26996
|
-
(name) =>
|
|
27166
|
+
(name) => import_fs11.default.existsSync(import_path23.default.join(CONFIG_DIR, name, "bin"))
|
|
26997
27167
|
);
|
|
26998
27168
|
}
|
|
26999
27169
|
var migrated = false;
|
|
@@ -27007,7 +27177,7 @@ function migrateToSpaceFolders() {
|
|
|
27007
27177
|
}
|
|
27008
27178
|
}
|
|
27009
27179
|
function readLegacy(filePath) {
|
|
27010
|
-
if (!
|
|
27180
|
+
if (!import_fs11.default.existsSync(filePath)) return { readable: true, value: null };
|
|
27011
27181
|
const value2 = readSecureJson(filePath);
|
|
27012
27182
|
return { readable: value2 !== null, value: value2 };
|
|
27013
27183
|
}
|
|
@@ -27025,14 +27195,14 @@ function migrateOnce() {
|
|
|
27025
27195
|
const consumed = [];
|
|
27026
27196
|
let strayFiles = [];
|
|
27027
27197
|
try {
|
|
27028
|
-
strayFiles =
|
|
27198
|
+
strayFiles = import_fs11.default.readdirSync(CONFIG_DIR).filter((f) => f.startsWith("space-") && f.endsWith(".json")).filter((f) => !f.endsWith(".onedrive.json"));
|
|
27029
27199
|
} catch {
|
|
27030
27200
|
strayFiles = [];
|
|
27031
27201
|
}
|
|
27032
27202
|
for (const name of strayFiles) {
|
|
27033
27203
|
const slug = name.slice("space-".length, -".json".length);
|
|
27034
27204
|
const parsed = readLegacy(
|
|
27035
|
-
|
|
27205
|
+
import_path23.default.join(CONFIG_DIR, name)
|
|
27036
27206
|
);
|
|
27037
27207
|
if (!parsed.readable) continue;
|
|
27038
27208
|
if (parsed.value) strays[slug] = parsed.value;
|
|
@@ -27045,7 +27215,7 @@ function migrateOnce() {
|
|
|
27045
27215
|
...provisionedSpaceSlugs()
|
|
27046
27216
|
]);
|
|
27047
27217
|
for (const slug of slugs) {
|
|
27048
|
-
if (
|
|
27218
|
+
if (import_fs11.default.existsSync(githubPath(slug))) continue;
|
|
27049
27219
|
const over = boards[slug] ?? strays[slug] ?? {};
|
|
27050
27220
|
if (!Object.keys(over).length && !Object.keys(base).length) continue;
|
|
27051
27221
|
const merged = fillDefaults({
|
|
@@ -27056,32 +27226,32 @@ function migrateOnce() {
|
|
|
27056
27226
|
githubToken: over.githubToken || base.githubToken,
|
|
27057
27227
|
provider: over.provider || base.provider
|
|
27058
27228
|
});
|
|
27059
|
-
|
|
27229
|
+
import_fs11.default.mkdirSync(credsDir(slug), { recursive: true });
|
|
27060
27230
|
writeJson(githubPath(slug), merged);
|
|
27061
27231
|
}
|
|
27062
27232
|
let odFiles = [];
|
|
27063
27233
|
try {
|
|
27064
|
-
odFiles =
|
|
27234
|
+
odFiles = import_fs11.default.readdirSync(CONFIG_DIR).filter((f) => f.startsWith("space-") && f.endsWith(".onedrive.json"));
|
|
27065
27235
|
} catch {
|
|
27066
27236
|
odFiles = [];
|
|
27067
27237
|
}
|
|
27068
27238
|
for (const name of odFiles) {
|
|
27069
27239
|
const slug = name.slice("space-".length, -".onedrive.json".length);
|
|
27070
|
-
if (
|
|
27240
|
+
if (import_fs11.default.existsSync(connectionPath(slug))) {
|
|
27071
27241
|
consumed.push(name);
|
|
27072
27242
|
continue;
|
|
27073
27243
|
}
|
|
27074
|
-
const conn = readLegacy(
|
|
27244
|
+
const conn = readLegacy(import_path23.default.join(CONFIG_DIR, name));
|
|
27075
27245
|
if (!conn.readable) continue;
|
|
27076
27246
|
if (conn.value) {
|
|
27077
|
-
|
|
27247
|
+
import_fs11.default.mkdirSync(credsDir(slug), { recursive: true });
|
|
27078
27248
|
writeJson(connectionPath(slug), conn.value);
|
|
27079
27249
|
}
|
|
27080
27250
|
consumed.push(name);
|
|
27081
27251
|
}
|
|
27082
27252
|
for (const name of [...consumed, "runner-config.json"]) {
|
|
27083
27253
|
try {
|
|
27084
|
-
|
|
27254
|
+
import_fs11.default.rmSync(import_path23.default.join(CONFIG_DIR, name), { force: true });
|
|
27085
27255
|
} catch {
|
|
27086
27256
|
}
|
|
27087
27257
|
}
|
|
@@ -27093,14 +27263,14 @@ function encryptStrayPlaintextConfigs() {
|
|
|
27093
27263
|
const OURS = /^(config|runner-config|space-.*)\.json(\.bak-\d+)?$/;
|
|
27094
27264
|
let names = [];
|
|
27095
27265
|
try {
|
|
27096
|
-
names =
|
|
27266
|
+
names = import_fs11.default.readdirSync(CONFIG_DIR).filter((f) => OURS.test(f));
|
|
27097
27267
|
} catch {
|
|
27098
27268
|
return;
|
|
27099
27269
|
}
|
|
27100
27270
|
for (const name of names) {
|
|
27101
|
-
const filePath =
|
|
27271
|
+
const filePath = import_path23.default.join(CONFIG_DIR, name);
|
|
27102
27272
|
try {
|
|
27103
|
-
const raw =
|
|
27273
|
+
const raw = import_fs11.default.readFileSync(filePath, "utf-8");
|
|
27104
27274
|
if (!raw.startsWith("{")) continue;
|
|
27105
27275
|
writeSecureJson(filePath, JSON.parse(raw));
|
|
27106
27276
|
} catch {
|
|
@@ -27123,7 +27293,7 @@ async function getConfiguredConnections() {
|
|
|
27123
27293
|
for (const slug of listConfiguredSlugs()) {
|
|
27124
27294
|
if (kinds.has("git") && kinds.has("onedrive")) break;
|
|
27125
27295
|
if (readBoard(slug).githubToken) kinds.add("git");
|
|
27126
|
-
if (
|
|
27296
|
+
if (import_fs11.default.existsSync(connectionPath(slug))) kinds.add("onedrive");
|
|
27127
27297
|
}
|
|
27128
27298
|
} catch {
|
|
27129
27299
|
return [];
|
|
@@ -27135,7 +27305,7 @@ function listConfiguredSlugs() {
|
|
|
27135
27305
|
try {
|
|
27136
27306
|
migrateToSpaceFolders();
|
|
27137
27307
|
return configDirNames().filter(
|
|
27138
|
-
(slug) =>
|
|
27308
|
+
(slug) => import_fs11.default.existsSync(githubPath(slug)) || import_fs11.default.existsSync(connectionPath(slug))
|
|
27139
27309
|
);
|
|
27140
27310
|
} catch {
|
|
27141
27311
|
return [];
|
|
@@ -27154,7 +27324,7 @@ var localConfigProvider = {
|
|
|
27154
27324
|
// which is what makes disconnect stick.
|
|
27155
27325
|
writeConfig(spaceSlug, config) {
|
|
27156
27326
|
migrateToSpaceFolders();
|
|
27157
|
-
|
|
27327
|
+
import_fs11.default.mkdirSync(credsDir(spaceSlug), { recursive: true });
|
|
27158
27328
|
writeJson(githubPath(spaceSlug), config);
|
|
27159
27329
|
},
|
|
27160
27330
|
readOneDriveConnection(spaceSlug) {
|
|
@@ -27168,7 +27338,7 @@ var localConfigProvider = {
|
|
|
27168
27338
|
writeOneDriveConnection(spaceSlug, connection) {
|
|
27169
27339
|
const filePath = connectionPath(spaceSlug);
|
|
27170
27340
|
if (!connection) {
|
|
27171
|
-
|
|
27341
|
+
import_fs11.default.rmSync(filePath, { force: true });
|
|
27172
27342
|
return;
|
|
27173
27343
|
}
|
|
27174
27344
|
writeJson(filePath, connection);
|
|
@@ -27193,7 +27363,7 @@ var localConfigProvider = {
|
|
|
27193
27363
|
"claude",
|
|
27194
27364
|
"codex"
|
|
27195
27365
|
]) {
|
|
27196
|
-
|
|
27366
|
+
import_fs11.default.mkdirSync(import_path23.default.join(dir, sub), { recursive: true });
|
|
27197
27367
|
}
|
|
27198
27368
|
return dir;
|
|
27199
27369
|
}
|
|
@@ -27367,15 +27537,15 @@ async function startDaemon(config) {
|
|
|
27367
27537
|
}
|
|
27368
27538
|
);
|
|
27369
27539
|
const indexCandidates = [
|
|
27370
|
-
|
|
27371
|
-
|
|
27540
|
+
import_path24.default.join(__dirname, "..", "index"),
|
|
27541
|
+
import_path24.default.join(__dirname, "..", "..", "index", "image")
|
|
27372
27542
|
];
|
|
27373
|
-
const indexDir = indexCandidates.find((p) =>
|
|
27543
|
+
const indexDir = indexCandidates.find((p) => import_fs12.default.existsSync(p)) ?? indexCandidates[0];
|
|
27374
27544
|
const odIndex = new IndexService({
|
|
27375
|
-
controlToken: (0,
|
|
27545
|
+
controlToken: (0, import_crypto11.createHash)("sha256").update(config.authToken).digest("hex"),
|
|
27376
27546
|
embedUrl: () => config.serverUrl,
|
|
27377
27547
|
indexDir,
|
|
27378
|
-
dataDir:
|
|
27548
|
+
dataDir: import_path24.default.join(CONFIG_DIR, "index-data")
|
|
27379
27549
|
});
|
|
27380
27550
|
void odIndex.ensureRunning().catch((err) => {
|
|
27381
27551
|
console.error(
|
|
@@ -27472,7 +27642,9 @@ async function startDaemon(config) {
|
|
|
27472
27642
|
// Board MCP is server-hosted, so the base URL is all it needs.
|
|
27473
27643
|
serverUrl: config.serverUrl,
|
|
27474
27644
|
// Point the agent at the index MCP endpoint (lazy for the port).
|
|
27475
|
-
mcp: () => odIndex.mcpBridge(spaceSlug)
|
|
27645
|
+
mcp: () => odIndex.mcpBridge(spaceSlug),
|
|
27646
|
+
// The bearer alone, available before the service has a port for a URL.
|
|
27647
|
+
mcpToken: () => odIndex.bearerToken(spaceSlug)
|
|
27476
27648
|
});
|
|
27477
27649
|
engines.set(spaceSlug, engine);
|
|
27478
27650
|
void engine.reattachAndHydrate();
|
|
@@ -27989,27 +28161,24 @@ ${err.fix}`);
|
|
|
27989
28161
|
console.error("");
|
|
27990
28162
|
process.exit(1);
|
|
27991
28163
|
}
|
|
27992
|
-
|
|
27993
|
-
|
|
27994
|
-
const
|
|
27995
|
-
|
|
27996
|
-
console.error(
|
|
28164
|
+
function warnUnsupportedNode() {
|
|
28165
|
+
if (indexNodeSupported()) return;
|
|
28166
|
+
const alternative = findIndexNode();
|
|
28167
|
+
console.warn(
|
|
27997
28168
|
`
|
|
27998
|
-
|
|
27999
|
-
The
|
|
28000
|
-
|
|
28001
|
-
|
|
28002
|
-
|
|
28003
|
-
`
|
|
28169
|
+
Node ${process.versions.node} is outside the range the OneDrive index supports (${MIN_INDEX_NODE_MAJOR}-${MAX_INDEX_NODE_MAJOR}, pinned 24.11.1).` + (alternative ? `
|
|
28170
|
+
The index will run under ${alternative}. Everything else is unaffected.
|
|
28171
|
+
` : `
|
|
28172
|
+
OneDrive search will be unavailable until a supported Node is installed (nvm install 24.11.1). Everything else works.
|
|
28173
|
+
`)
|
|
28004
28174
|
);
|
|
28005
|
-
process.exit(1);
|
|
28006
28175
|
}
|
|
28007
28176
|
import_node_dns.default.setDefaultResultOrder("ipv4first");
|
|
28008
28177
|
var program2 = new Command();
|
|
28009
28178
|
program2.name("factiii-runner").description("Factiii Runner - run Board AI agents on remote servers").version(currentVersion());
|
|
28010
28179
|
program2.command("setup").description("Verify the host toolchain and pair this runner").option("-s, --server <url>", "API server URL", "https://api.factiii.com").action(async (opts) => {
|
|
28011
28180
|
try {
|
|
28012
|
-
|
|
28181
|
+
warnUnsupportedNode();
|
|
28013
28182
|
await setup(opts.server);
|
|
28014
28183
|
} catch (err) {
|
|
28015
28184
|
console.error(
|
|
@@ -28038,7 +28207,7 @@ program2.command("connect").description("Pair this runner with a Factiii account
|
|
|
28038
28207
|
}
|
|
28039
28208
|
});
|
|
28040
28209
|
program2.command("start").description("Start the runner daemon").action(async () => {
|
|
28041
|
-
|
|
28210
|
+
warnUnsupportedNode();
|
|
28042
28211
|
if (!configExists()) {
|
|
28043
28212
|
console.error('No config found. Run "factiii-runner setup" first.');
|
|
28044
28213
|
process.exit(1);
|
|
@@ -28084,8 +28253,7 @@ program2.command("status").description("Show current runner configuration").acti
|
|
|
28084
28253
|
console.log(`Token: \xB7\xB7\xB7\xB7${config.authToken.slice(-4)}`);
|
|
28085
28254
|
});
|
|
28086
28255
|
program2.command("doctor").description("Show where each host tool resolves, for a failed environment").action(async () => {
|
|
28087
|
-
const
|
|
28088
|
-
const nodeNote = nodeMajor > MAX_SUPPORTED_NODE_MAJOR ? " (UNSUPPORTED \u2014 use 24.11.1)" : "";
|
|
28256
|
+
const nodeNote = indexNodeSupported() ? "" : ` (OneDrive index needs ${MIN_INDEX_NODE_MAJOR}-${MAX_INDEX_NODE_MAJOR}; ${findIndexNode() ?? "no supported node found"})`;
|
|
28089
28257
|
console.log(`${"node".padEnd(14)} ${process.execPath} v${process.versions.node}${nodeNote}`);
|
|
28090
28258
|
const report = await reportToolchain(hostTarget(CONFIG_DIR));
|
|
28091
28259
|
for (const tool of report.tools) {
|
package/package.json
CHANGED