@factiii/runner 0.10.2 → 0.11.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/dist/cli.js +698 -409
- package/package.json +1 -1
- package/plugin/factiii/bin/factiii-secrets +208 -0
- package/plugin/factiii/skills/factiii-secrets/SKILL.md +82 -0
package/dist/cli.js
CHANGED
|
@@ -963,8 +963,8 @@ var require_command = __commonJS({
|
|
|
963
963
|
"node_modules/commander/lib/command.js"(exports2) {
|
|
964
964
|
var EventEmitter = require("node:events").EventEmitter;
|
|
965
965
|
var childProcess = require("node:child_process");
|
|
966
|
-
var
|
|
967
|
-
var
|
|
966
|
+
var path20 = require("node:path");
|
|
967
|
+
var fs15 = require("node:fs");
|
|
968
968
|
var process2 = require("node:process");
|
|
969
969
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
970
970
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1896,11 +1896,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1896
1896
|
let launchWithNode = false;
|
|
1897
1897
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1898
1898
|
function findFile(baseDir, baseName) {
|
|
1899
|
-
const localBin =
|
|
1900
|
-
if (
|
|
1901
|
-
if (sourceExt.includes(
|
|
1899
|
+
const localBin = path20.resolve(baseDir, baseName);
|
|
1900
|
+
if (fs15.existsSync(localBin)) return localBin;
|
|
1901
|
+
if (sourceExt.includes(path20.extname(baseName))) return void 0;
|
|
1902
1902
|
const foundExt = sourceExt.find(
|
|
1903
|
-
(ext) =>
|
|
1903
|
+
(ext) => fs15.existsSync(`${localBin}${ext}`)
|
|
1904
1904
|
);
|
|
1905
1905
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1906
1906
|
return void 0;
|
|
@@ -1912,21 +1912,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1912
1912
|
if (this._scriptPath) {
|
|
1913
1913
|
let resolvedScriptPath;
|
|
1914
1914
|
try {
|
|
1915
|
-
resolvedScriptPath =
|
|
1915
|
+
resolvedScriptPath = fs15.realpathSync(this._scriptPath);
|
|
1916
1916
|
} catch (err) {
|
|
1917
1917
|
resolvedScriptPath = this._scriptPath;
|
|
1918
1918
|
}
|
|
1919
|
-
executableDir =
|
|
1920
|
-
|
|
1919
|
+
executableDir = path20.resolve(
|
|
1920
|
+
path20.dirname(resolvedScriptPath),
|
|
1921
1921
|
executableDir
|
|
1922
1922
|
);
|
|
1923
1923
|
}
|
|
1924
1924
|
if (executableDir) {
|
|
1925
1925
|
let localFile = findFile(executableDir, executableFile);
|
|
1926
1926
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1927
|
-
const legacyName =
|
|
1927
|
+
const legacyName = path20.basename(
|
|
1928
1928
|
this._scriptPath,
|
|
1929
|
-
|
|
1929
|
+
path20.extname(this._scriptPath)
|
|
1930
1930
|
);
|
|
1931
1931
|
if (legacyName !== this._name) {
|
|
1932
1932
|
localFile = findFile(
|
|
@@ -1937,7 +1937,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1937
1937
|
}
|
|
1938
1938
|
executableFile = localFile || executableFile;
|
|
1939
1939
|
}
|
|
1940
|
-
launchWithNode = sourceExt.includes(
|
|
1940
|
+
launchWithNode = sourceExt.includes(path20.extname(executableFile));
|
|
1941
1941
|
let proc;
|
|
1942
1942
|
if (process2.platform !== "win32") {
|
|
1943
1943
|
if (launchWithNode) {
|
|
@@ -2777,7 +2777,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2777
2777
|
* @return {Command}
|
|
2778
2778
|
*/
|
|
2779
2779
|
nameFromFilename(filename) {
|
|
2780
|
-
this._name =
|
|
2780
|
+
this._name = path20.basename(filename, path20.extname(filename));
|
|
2781
2781
|
return this;
|
|
2782
2782
|
}
|
|
2783
2783
|
/**
|
|
@@ -2791,9 +2791,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2791
2791
|
* @param {string} [path]
|
|
2792
2792
|
* @return {(string|null|Command)}
|
|
2793
2793
|
*/
|
|
2794
|
-
executableDir(
|
|
2795
|
-
if (
|
|
2796
|
-
this._executableDir =
|
|
2794
|
+
executableDir(path21) {
|
|
2795
|
+
if (path21 === void 0) return this._executableDir;
|
|
2796
|
+
this._executableDir = path21;
|
|
2797
2797
|
return this;
|
|
2798
2798
|
}
|
|
2799
2799
|
/**
|
|
@@ -3026,7 +3026,7 @@ var require_commander = __commonJS({
|
|
|
3026
3026
|
// ../../node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js
|
|
3027
3027
|
var require_XMLHttpRequest = __commonJS({
|
|
3028
3028
|
"../../node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js"(exports2, module2) {
|
|
3029
|
-
var
|
|
3029
|
+
var fs15 = require("fs");
|
|
3030
3030
|
var Url = require("url");
|
|
3031
3031
|
var spawn5 = require("child_process").spawn;
|
|
3032
3032
|
module2.exports = XMLHttpRequest3;
|
|
@@ -3184,7 +3184,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
3184
3184
|
throw new Error("XMLHttpRequest: Only GET method is supported");
|
|
3185
3185
|
}
|
|
3186
3186
|
if (settings.async) {
|
|
3187
|
-
|
|
3187
|
+
fs15.readFile(unescape(url2.pathname), function(error, data2) {
|
|
3188
3188
|
if (error) {
|
|
3189
3189
|
self.handleError(error, error.errno || -1);
|
|
3190
3190
|
} else {
|
|
@@ -3196,7 +3196,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
3196
3196
|
});
|
|
3197
3197
|
} else {
|
|
3198
3198
|
try {
|
|
3199
|
-
this.response =
|
|
3199
|
+
this.response = fs15.readFileSync(unescape(url2.pathname));
|
|
3200
3200
|
this.responseText = this.response.toString("utf8");
|
|
3201
3201
|
this.status = 200;
|
|
3202
3202
|
setState(self.DONE);
|
|
@@ -3322,15 +3322,15 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
3322
3322
|
} else {
|
|
3323
3323
|
var contentFile = ".node-xmlhttprequest-content-" + process.pid;
|
|
3324
3324
|
var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
|
|
3325
|
-
|
|
3325
|
+
fs15.writeFileSync(syncFile, "", "utf8");
|
|
3326
3326
|
var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
|
|
3327
3327
|
var syncProc = spawn5(process.argv[0], ["-e", execString]);
|
|
3328
3328
|
var statusText;
|
|
3329
|
-
while (
|
|
3329
|
+
while (fs15.existsSync(syncFile)) {
|
|
3330
3330
|
}
|
|
3331
|
-
self.responseText =
|
|
3331
|
+
self.responseText = fs15.readFileSync(contentFile, "utf8");
|
|
3332
3332
|
syncProc.stdin.end();
|
|
3333
|
-
|
|
3333
|
+
fs15.unlinkSync(contentFile);
|
|
3334
3334
|
if (self.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR:/)) {
|
|
3335
3335
|
var errorObj = JSON.parse(self.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/, ""));
|
|
3336
3336
|
self.handleError(errorObj, 503);
|
|
@@ -6497,7 +6497,7 @@ var require_websocket = __commonJS({
|
|
|
6497
6497
|
var EventEmitter = require("events");
|
|
6498
6498
|
var https = require("https");
|
|
6499
6499
|
var http = require("http");
|
|
6500
|
-
var
|
|
6500
|
+
var net3 = require("net");
|
|
6501
6501
|
var tls = require("tls");
|
|
6502
6502
|
var { randomBytes, createHash: createHash2 } = require("crypto");
|
|
6503
6503
|
var { Duplex, Readable } = require("stream");
|
|
@@ -7241,12 +7241,12 @@ var require_websocket = __commonJS({
|
|
|
7241
7241
|
}
|
|
7242
7242
|
function netConnect(options) {
|
|
7243
7243
|
options.path = options.socketPath;
|
|
7244
|
-
return
|
|
7244
|
+
return net3.connect(options);
|
|
7245
7245
|
}
|
|
7246
7246
|
function tlsConnect(options) {
|
|
7247
7247
|
options.path = void 0;
|
|
7248
7248
|
if (!options.servername && options.servername !== "") {
|
|
7249
|
-
options.servername =
|
|
7249
|
+
options.servername = net3.isIP(options.host) ? "" : options.host;
|
|
7250
7250
|
}
|
|
7251
7251
|
return tls.connect(options);
|
|
7252
7252
|
}
|
|
@@ -8428,8 +8428,8 @@ function getErrorMap() {
|
|
|
8428
8428
|
|
|
8429
8429
|
// ../../node_modules/zod/v3/helpers/parseUtil.js
|
|
8430
8430
|
var makeIssue = (params) => {
|
|
8431
|
-
const { data, path:
|
|
8432
|
-
const fullPath = [...
|
|
8431
|
+
const { data, path: path20, errorMaps, issueData } = params;
|
|
8432
|
+
const fullPath = [...path20, ...issueData.path || []];
|
|
8433
8433
|
const fullIssue = {
|
|
8434
8434
|
...issueData,
|
|
8435
8435
|
path: fullPath
|
|
@@ -8545,11 +8545,11 @@ var errorUtil;
|
|
|
8545
8545
|
|
|
8546
8546
|
// ../../node_modules/zod/v3/types.js
|
|
8547
8547
|
var ParseInputLazyPath = class {
|
|
8548
|
-
constructor(parent, value2,
|
|
8548
|
+
constructor(parent, value2, path20, key) {
|
|
8549
8549
|
this._cachedPath = [];
|
|
8550
8550
|
this.parent = parent;
|
|
8551
8551
|
this.data = value2;
|
|
8552
|
-
this._path =
|
|
8552
|
+
this._path = path20;
|
|
8553
8553
|
this._key = key;
|
|
8554
8554
|
}
|
|
8555
8555
|
get path() {
|
|
@@ -12197,9 +12197,9 @@ function processLine(trimmed, state, cb) {
|
|
|
12197
12197
|
if (e.type === "result" /* Result */) {
|
|
12198
12198
|
if (e.is_error) {
|
|
12199
12199
|
state.isError = true;
|
|
12200
|
-
const
|
|
12201
|
-
state.fullText =
|
|
12202
|
-
cb.onLog?.({ type: "error", content:
|
|
12200
|
+
const errorText2 = e.result || "Claude encountered an error";
|
|
12201
|
+
state.fullText = errorText2;
|
|
12202
|
+
cb.onLog?.({ type: "error", content: errorText2 });
|
|
12203
12203
|
return;
|
|
12204
12204
|
}
|
|
12205
12205
|
if (e.result && !state.fullText) {
|
|
@@ -12647,7 +12647,8 @@ var prompts_default = {
|
|
|
12647
12647
|
"- $WORKSPACE - the repo checkout (the working directory), reset to the default branch at run start",
|
|
12648
12648
|
"- $BACKUPS_DIR - durable backups; every backup (db dumps etc) is written here and nowhere else",
|
|
12649
12649
|
"- $BUILDS_DIR - build artifacts; each build writes into $BUILDS_DIR/<commit-hash>/",
|
|
12650
|
-
"Reference paths ONLY via these variables or relative to the working directory. NEVER hardcode absolute machine paths (like /Users/...) anywhere in the skill - it must run unchanged on any runner.
|
|
12650
|
+
"Reference paths ONLY via these variables or relative to the working directory. NEVER hardcode absolute machine paths (like /Users/...) anywhere in the skill - it must run unchanged on any runner.",
|
|
12651
|
+
"Secrets live in an encrypted store you never touch, and `factiii-secrets` is how a command gets them - it is on the PATH of every run. Load the `factiii-secrets` skill installed on this machine (it is not part of this repo) for exactly how it behaves, and write the skill's commands in that shape.",
|
|
12651
12652
|
"",
|
|
12652
12653
|
"## Produce exactly three files",
|
|
12653
12654
|
"1. .agents/skills/deploy/SKILL.md - the runbook, written for a competent operator. It MUST start with YAML frontmatter (--- name: deploy / description: one line saying this is the repository's release runbook and when to run it ---) so agent CLIs discover it as a skill, followed by: named steps in ship order, the exact commands, what success looks like per step, and explicit stop-and-ask points wherever human judgment or confirmation belongs (e.g. 'confirm before the production cutover'). Plain language; no secret values or key material anywhere in the file - reference secrets ONLY as $KEY env names.",
|
|
@@ -12658,7 +12659,7 @@ var prompts_default = {
|
|
|
12658
12659
|
"- Write ONLY those three files under .agents/skills/deploy/.",
|
|
12659
12660
|
"- Do NOT git commit or git push; the harness pushes for review.",
|
|
12660
12661
|
"- Cloud CLIs on this machine (aws, vercel, wrangler, gh...) may already be signed in. You may use them READ-ONLY to verify what exists (project names, repo names, regions) so the skill is accurate - but NEVER create, modify, deploy, or delete anything, and never echo a credential or token value.",
|
|
12661
|
-
|
|
12662
|
+
'- Secret-bearing commands: any command needing a variable declared in required-variables.json MUST be written in the skill as `factiii-secrets run -- <command>`, with the variables referenced unexpanded and quoted (e.g. "$DATABASE_URL"). The owner authorizes each one at run time and the runner injects the values. Wrap only the commands that need a secret. NEVER include steps that check whether variables are set in the environment, and never instruct anyone to export them or put them in a .env/shell/CI config.',
|
|
12662
12663
|
"- No secret values in any file, ever."
|
|
12663
12664
|
],
|
|
12664
12665
|
deployCreateRevise: [
|
|
@@ -12666,18 +12667,18 @@ var prompts_default = {
|
|
|
12666
12667
|
"",
|
|
12667
12668
|
"{{FEEDBACK}}",
|
|
12668
12669
|
"",
|
|
12669
|
-
"Apply them by editing .agents/skills/deploy/SKILL.md, .agents/skills/deploy/required-variables.json, and/or .agents/skills/deploy/check-environment.sh in your working directory - the same contract as before: only those three files, keys declared exactly when used, no secret values anywhere, no git commit/push, and paths referenced ONLY via $WORKSPACE / $BACKUPS_DIR / $BUILDS_DIR or relative to the working directory (never absolute machine paths). Keep everything the owner did NOT ask to change untouched."
|
|
12670
|
+
"Apply them by editing .agents/skills/deploy/SKILL.md, .agents/skills/deploy/required-variables.json, and/or .agents/skills/deploy/check-environment.sh in your working directory - the same contract as before: only those three files, keys declared exactly when used, every secret-bearing command still written as `factiii-secrets run -- <command>`, no secret values anywhere, no git commit/push, and paths referenced ONLY via $WORKSPACE / $BACKUPS_DIR / $BUILDS_DIR or relative to the working directory (never absolute machine paths). Keep everything the owner did NOT ask to change untouched."
|
|
12670
12671
|
],
|
|
12671
12672
|
deployRun: [
|
|
12672
12673
|
"## Deploy run protocol",
|
|
12673
12674
|
"You are executing this repository's deploy skill NOW, on the owner's machine, for real. Invoke it and follow it exactly - the skill is the program; do not improvise steps it doesn't contain or change any step's intent.",
|
|
12674
12675
|
"",
|
|
12675
|
-
"##
|
|
12676
|
-
"Every variable declared in required-variables.json is UNSET in your shell BY DESIGN, and will stay unset for the whole run.
|
|
12677
|
-
"-
|
|
12678
|
-
|
|
12679
|
-
"- If the skill has a step that verifies variables are set, SKIP it - it predates this protocol.",
|
|
12680
|
-
|
|
12676
|
+
"## Secrets (read this before anything else)",
|
|
12677
|
+
"Every variable declared in required-variables.json is UNSET in your shell BY DESIGN, and will stay unset for the whole run. The values are stored encrypted on this machine, and `factiii-secrets` is how a command gets them - it is on your PATH, and the `factiii-secrets` skill describes it in full.",
|
|
12678
|
+
"- To run a command that needs any declared variable, prefix it: `factiii-secrets run -- <command>`. The owner authorizes it with their password, the runner runs it with every declared value injected, and you get the output back with the values redacted. It exits with the command's own code, so `&&`, `||` and `set -e` behave normally. Exit 77 means the owner declined - adapt or stop, and never retry the same command.",
|
|
12679
|
+
'- Quote the variables (`"$DATABASE_URL"`): they expand on the runner\'s side, not in your shell.',
|
|
12680
|
+
"- NEVER check whether these variables are set (no `env`, `printenv`, `test -n`, echo checks), never report them as missing, and never abort because your shell lacks them. Their absence tells you nothing. If the skill has a step that verifies variables are set, SKIP it - it predates this protocol.",
|
|
12681
|
+
"- Wrap ONLY the commands that actually need a secret. Everything else runs unwrapped and needs no authorization.",
|
|
12681
12682
|
"",
|
|
12682
12683
|
"- Track the release with your todo/plan tool: one todo per step in ship order, updated as you go. This is the owner's live timeline.",
|
|
12683
12684
|
"- HUMAN GATES: wherever the skill says to stop and ask (and before anything irreversible the skill flags), print as the LAST line: AWAITING_INPUT: <the question> - then END your turn. The owner's answer arrives as your next message.",
|
|
@@ -12908,7 +12909,7 @@ ${diff.slice(0, 8e3)}`,
|
|
|
12908
12909
|
}
|
|
12909
12910
|
|
|
12910
12911
|
// ../../shared/all/helpers/board-agent-core/engine.ts
|
|
12911
|
-
var
|
|
12912
|
+
var import_path12 = require("path");
|
|
12912
12913
|
|
|
12913
12914
|
// ../../shared/all/domains/electron.ts
|
|
12914
12915
|
var SETUP_LOG_PREFIX = "log ";
|
|
@@ -13017,7 +13018,7 @@ async function ensurePrimaryClone(target, cloneUrl, branch) {
|
|
|
13017
13018
|
await target.sh(`mkdir -p ${target.paths.worktrees}`);
|
|
13018
13019
|
}
|
|
13019
13020
|
async function addWorktree(target, workName, baseBranch, kind) {
|
|
13020
|
-
const
|
|
13021
|
+
const path20 = worktreeFor(target.paths, workName);
|
|
13021
13022
|
const branch = workBranch(workName, kind);
|
|
13022
13023
|
await gitInDir(
|
|
13023
13024
|
target,
|
|
@@ -13027,7 +13028,7 @@ async function addWorktree(target, workName, baseBranch, kind) {
|
|
|
13027
13028
|
baseBranch
|
|
13028
13029
|
).catch(() => {
|
|
13029
13030
|
});
|
|
13030
|
-
await removeWorktree(target,
|
|
13031
|
+
await removeWorktree(target, path20).catch(() => {
|
|
13031
13032
|
});
|
|
13032
13033
|
await gitInDir(
|
|
13033
13034
|
target,
|
|
@@ -13036,30 +13037,30 @@ async function addWorktree(target, workName, baseBranch, kind) {
|
|
|
13036
13037
|
"add",
|
|
13037
13038
|
"-B",
|
|
13038
13039
|
branch,
|
|
13039
|
-
|
|
13040
|
+
path20,
|
|
13040
13041
|
`origin/${baseBranch}`
|
|
13041
13042
|
);
|
|
13042
|
-
return { path:
|
|
13043
|
+
return { path: path20, branch };
|
|
13043
13044
|
}
|
|
13044
|
-
async function removeWorktree(target,
|
|
13045
|
+
async function removeWorktree(target, path20) {
|
|
13045
13046
|
await gitInDir(
|
|
13046
13047
|
target,
|
|
13047
13048
|
target.paths.repo,
|
|
13048
13049
|
"worktree",
|
|
13049
13050
|
"remove",
|
|
13050
13051
|
"--force",
|
|
13051
|
-
|
|
13052
|
+
path20
|
|
13052
13053
|
);
|
|
13053
13054
|
}
|
|
13054
|
-
async function removeWorktreeAndBranch(target,
|
|
13055
|
+
async function removeWorktreeAndBranch(target, path20) {
|
|
13055
13056
|
const branch = await gitInDir(
|
|
13056
13057
|
target,
|
|
13057
|
-
|
|
13058
|
+
path20,
|
|
13058
13059
|
"rev-parse",
|
|
13059
13060
|
"--abbrev-ref",
|
|
13060
13061
|
"HEAD"
|
|
13061
13062
|
).then((out) => out.trim()).catch(() => "");
|
|
13062
|
-
await removeWorktree(target,
|
|
13063
|
+
await removeWorktree(target, path20);
|
|
13063
13064
|
if (branch && branch !== "HEAD") {
|
|
13064
13065
|
await gitInDir(target, target.paths.repo, "branch", "-D", branch).catch(
|
|
13065
13066
|
() => {
|
|
@@ -13073,8 +13074,8 @@ async function pruneWorktrees(target) {
|
|
|
13073
13074
|
}
|
|
13074
13075
|
);
|
|
13075
13076
|
}
|
|
13076
|
-
function isWorktreePath(target,
|
|
13077
|
-
return
|
|
13077
|
+
function isWorktreePath(target, path20) {
|
|
13078
|
+
return path20.startsWith(`${target.paths.worktrees}/`);
|
|
13078
13079
|
}
|
|
13079
13080
|
async function setGitIdentity(target, name, email, workdir = target.paths.workspace) {
|
|
13080
13081
|
if (name) await gitInDir(target, workdir, "config", "user.name", name);
|
|
@@ -13100,13 +13101,13 @@ function parseStatus(raw) {
|
|
|
13100
13101
|
const y = line[1];
|
|
13101
13102
|
const rest = line.slice(3);
|
|
13102
13103
|
const arrow = rest.indexOf(" -> ");
|
|
13103
|
-
const
|
|
13104
|
+
const path20 = arrow === -1 ? rest : rest.slice(arrow + 4);
|
|
13104
13105
|
if (x === "?" || y === "?") {
|
|
13105
|
-
unstaged.push({ path:
|
|
13106
|
+
unstaged.push({ path: path20, code: "A" });
|
|
13106
13107
|
continue;
|
|
13107
13108
|
}
|
|
13108
|
-
if (x !== " ") staged.push({ path:
|
|
13109
|
-
if (y !== " ") unstaged.push({ path:
|
|
13109
|
+
if (x !== " ") staged.push({ path: path20, code: x });
|
|
13110
|
+
if (y !== " ") unstaged.push({ path: path20, code: y });
|
|
13110
13111
|
}
|
|
13111
13112
|
return { staged, unstaged };
|
|
13112
13113
|
}
|
|
@@ -13848,13 +13849,13 @@ function createEventSink(send, spaceSlug, notify) {
|
|
|
13848
13849
|
|
|
13849
13850
|
// ../../shared/all/helpers/card-ai/frpc.ts
|
|
13850
13851
|
var import_fs2 = require("fs");
|
|
13851
|
-
var
|
|
13852
|
-
var
|
|
13852
|
+
var import_promises5 = __toESM(require("fs/promises"));
|
|
13853
|
+
var import_path7 = __toESM(require("path"));
|
|
13853
13854
|
|
|
13854
13855
|
// ../../shared/all/helpers/board-agent-core/host-skills.ts
|
|
13855
|
-
var
|
|
13856
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
13856
13857
|
var import_os2 = __toESM(require("os"));
|
|
13857
|
-
var
|
|
13858
|
+
var import_path6 = __toESM(require("path"));
|
|
13858
13859
|
|
|
13859
13860
|
// ../../shared/all/domains/posts.ts
|
|
13860
13861
|
var postDataSort = /* @__PURE__ */ ((postDataSort2) => {
|
|
@@ -14364,9 +14365,134 @@ var skillScopeSchema = external_exports.enum(["global", "repo"]);
|
|
|
14364
14365
|
|
|
14365
14366
|
// ../../shared/all/helpers/board-agent-core/host-deploy.ts
|
|
14366
14367
|
var import_child_process3 = require("child_process");
|
|
14368
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
14369
|
+
var import_path5 = __toESM(require("path"));
|
|
14370
|
+
var import_util5 = require("util");
|
|
14371
|
+
|
|
14372
|
+
// ../../shared/all/helpers/board-agent-core/secrets-bridge.ts
|
|
14367
14373
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
14374
|
+
var import_net2 = __toESM(require("net"));
|
|
14368
14375
|
var import_path4 = __toESM(require("path"));
|
|
14369
|
-
|
|
14376
|
+
function secretsSocketPath(spaceDir) {
|
|
14377
|
+
return import_path4.default.join(hostSpacePaths(spaceDir).state, "secrets.sock");
|
|
14378
|
+
}
|
|
14379
|
+
function redactor(values) {
|
|
14380
|
+
const significant = values.filter((v) => v.length >= 4);
|
|
14381
|
+
return (text) => {
|
|
14382
|
+
let out = text;
|
|
14383
|
+
for (const value2 of significant) {
|
|
14384
|
+
out = out.split(value2).join("[REDACTED]");
|
|
14385
|
+
}
|
|
14386
|
+
return out;
|
|
14387
|
+
};
|
|
14388
|
+
}
|
|
14389
|
+
function replier(conn) {
|
|
14390
|
+
return (message) => {
|
|
14391
|
+
if (conn.destroyed) return;
|
|
14392
|
+
conn.write(`${JSON.stringify(message)}
|
|
14393
|
+
`);
|
|
14394
|
+
};
|
|
14395
|
+
}
|
|
14396
|
+
async function handleRun(request, handlers, reply) {
|
|
14397
|
+
const command = typeof request.command === "string" ? request.command.trim() : "";
|
|
14398
|
+
if (!command) {
|
|
14399
|
+
reply({ type: "error", message: "No command was sent." });
|
|
14400
|
+
return;
|
|
14401
|
+
}
|
|
14402
|
+
const cwd = typeof request.cwd === "string" ? request.cwd : "";
|
|
14403
|
+
const result = await handlers.run({
|
|
14404
|
+
command,
|
|
14405
|
+
cwd,
|
|
14406
|
+
status: (text) => reply({ type: "status", text })
|
|
14407
|
+
});
|
|
14408
|
+
if ("denied" in result) {
|
|
14409
|
+
reply({ type: "denied" });
|
|
14410
|
+
return;
|
|
14411
|
+
}
|
|
14412
|
+
if (result.output) reply({ type: "output", chunk: result.output });
|
|
14413
|
+
reply({ type: "exit", code: result.code });
|
|
14414
|
+
}
|
|
14415
|
+
async function startSecretsBridge(opts) {
|
|
14416
|
+
const socketPath = secretsSocketPath(opts.spaceDir);
|
|
14417
|
+
await import_promises2.default.mkdir(import_path4.default.dirname(socketPath), { recursive: true });
|
|
14418
|
+
await import_promises2.default.rm(socketPath, { force: true });
|
|
14419
|
+
const { handlers } = opts;
|
|
14420
|
+
let queue = Promise.resolve();
|
|
14421
|
+
let queueDepth = 0;
|
|
14422
|
+
const live = /* @__PURE__ */ new Set();
|
|
14423
|
+
const server = import_net2.default.createServer((conn) => {
|
|
14424
|
+
live.add(conn);
|
|
14425
|
+
conn.on("close", () => live.delete(conn));
|
|
14426
|
+
const reply = replier(conn);
|
|
14427
|
+
let buffer = "";
|
|
14428
|
+
let handled = false;
|
|
14429
|
+
conn.on("error", () => {
|
|
14430
|
+
});
|
|
14431
|
+
conn.on("data", (chunk) => {
|
|
14432
|
+
if (handled) return;
|
|
14433
|
+
buffer += chunk.toString("utf-8");
|
|
14434
|
+
const newline = buffer.indexOf("\n");
|
|
14435
|
+
if (newline === -1) return;
|
|
14436
|
+
handled = true;
|
|
14437
|
+
let request;
|
|
14438
|
+
try {
|
|
14439
|
+
request = JSON.parse(buffer.slice(0, newline));
|
|
14440
|
+
} catch {
|
|
14441
|
+
reply({ type: "error", message: "Malformed request." });
|
|
14442
|
+
conn.end();
|
|
14443
|
+
return;
|
|
14444
|
+
}
|
|
14445
|
+
if (request.op === "list") {
|
|
14446
|
+
void handlers.list().then((keys) => reply({ type: "keys", keys })).catch(
|
|
14447
|
+
(err) => reply({ type: "error", message: errorText(err) })
|
|
14448
|
+
).finally(() => conn.end());
|
|
14449
|
+
return;
|
|
14450
|
+
}
|
|
14451
|
+
if (request.op !== "run") {
|
|
14452
|
+
reply({ type: "error", message: "Unknown operation." });
|
|
14453
|
+
conn.end();
|
|
14454
|
+
return;
|
|
14455
|
+
}
|
|
14456
|
+
if (queueDepth > 0) {
|
|
14457
|
+
reply({
|
|
14458
|
+
type: "status",
|
|
14459
|
+
text: "queued behind another command awaiting authorization"
|
|
14460
|
+
});
|
|
14461
|
+
}
|
|
14462
|
+
queueDepth += 1;
|
|
14463
|
+
queue = queue.then(() => {
|
|
14464
|
+
if (conn.destroyed) return void 0;
|
|
14465
|
+
return handleRun(request, handlers, reply);
|
|
14466
|
+
}).catch(
|
|
14467
|
+
(err) => reply({ type: "error", message: errorText(err) })
|
|
14468
|
+
).finally(() => {
|
|
14469
|
+
queueDepth -= 1;
|
|
14470
|
+
conn.end();
|
|
14471
|
+
});
|
|
14472
|
+
});
|
|
14473
|
+
});
|
|
14474
|
+
await new Promise((resolve2, reject) => {
|
|
14475
|
+
server.once("error", reject);
|
|
14476
|
+
server.listen(socketPath, () => {
|
|
14477
|
+
server.removeListener("error", reject);
|
|
14478
|
+
resolve2();
|
|
14479
|
+
});
|
|
14480
|
+
});
|
|
14481
|
+
await import_promises2.default.chmod(socketPath, 384).catch(() => void 0);
|
|
14482
|
+
return {
|
|
14483
|
+
close: async () => {
|
|
14484
|
+
for (const conn of live) conn.destroy();
|
|
14485
|
+
live.clear();
|
|
14486
|
+
await new Promise((resolve2) => server.close(() => resolve2()));
|
|
14487
|
+
await import_promises2.default.rm(socketPath, { force: true });
|
|
14488
|
+
}
|
|
14489
|
+
};
|
|
14490
|
+
}
|
|
14491
|
+
function errorText(err) {
|
|
14492
|
+
return err instanceof Error ? err.message : String(err);
|
|
14493
|
+
}
|
|
14494
|
+
|
|
14495
|
+
// ../../shared/all/helpers/board-agent-core/host-deploy.ts
|
|
14370
14496
|
var execFileAsync2 = (0, import_util5.promisify)(import_child_process3.execFile);
|
|
14371
14497
|
var SKILL_DIR = ".agents/skills/deploy";
|
|
14372
14498
|
var SKILL_PATH = `${SKILL_DIR}/SKILL.md`;
|
|
@@ -14375,14 +14501,14 @@ var CHECKER_PATH = `${SKILL_DIR}/check-environment.sh`;
|
|
|
14375
14501
|
var CLAUDE_SKILLS_DIR = ".claude/skills";
|
|
14376
14502
|
var CLAUDE_LINK = `${CLAUDE_SKILLS_DIR}/deploy`;
|
|
14377
14503
|
async function ensureClaudeSkillLink(workspace) {
|
|
14378
|
-
const linkPath =
|
|
14504
|
+
const linkPath = import_path5.default.join(workspace, CLAUDE_LINK);
|
|
14379
14505
|
try {
|
|
14380
|
-
await
|
|
14506
|
+
await import_promises3.default.lstat(linkPath);
|
|
14381
14507
|
return;
|
|
14382
14508
|
} catch {
|
|
14383
14509
|
}
|
|
14384
|
-
await
|
|
14385
|
-
await
|
|
14510
|
+
await import_promises3.default.mkdir(import_path5.default.join(workspace, CLAUDE_SKILLS_DIR), { recursive: true });
|
|
14511
|
+
await import_promises3.default.symlink("../../.agents/skills/deploy", linkPath, "dir");
|
|
14386
14512
|
}
|
|
14387
14513
|
var GIT_NO_HELPER = ["-c", "credential.helper="];
|
|
14388
14514
|
var GIT_ENV = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
@@ -14427,7 +14553,7 @@ async function gitCatFileBatch(cwd, shas) {
|
|
|
14427
14553
|
return out;
|
|
14428
14554
|
}
|
|
14429
14555
|
async function ensureWorkspaceClone(opts) {
|
|
14430
|
-
const workspace =
|
|
14556
|
+
const workspace = import_path5.default.join(opts.spaceDir, "workspace");
|
|
14431
14557
|
const branch = (opts.mainBranch || "main").replace(/[^\w./-]/g, "");
|
|
14432
14558
|
const url2 = buildCloneUrl(opts.repoUrl, opts.githubToken);
|
|
14433
14559
|
let cloned = false;
|
|
@@ -14435,14 +14561,14 @@ async function ensureWorkspaceClone(opts) {
|
|
|
14435
14561
|
await hostGit(workspace, "rev-parse", "--git-dir");
|
|
14436
14562
|
await hostGit(workspace, "remote", "set-url", "origin", url2);
|
|
14437
14563
|
} catch {
|
|
14438
|
-
const leftovers = await
|
|
14564
|
+
const leftovers = await import_promises3.default.readdir(workspace).catch(() => []);
|
|
14439
14565
|
if (leftovers.includes(".git")) {
|
|
14440
14566
|
throw new Error(
|
|
14441
14567
|
`${workspace} holds a git repository that cannot be read. Remove it and retry.`
|
|
14442
14568
|
);
|
|
14443
14569
|
}
|
|
14444
14570
|
for (const name of leftovers) {
|
|
14445
|
-
await
|
|
14571
|
+
await import_promises3.default.rm(import_path5.default.join(workspace, name), { recursive: true, force: true });
|
|
14446
14572
|
}
|
|
14447
14573
|
await execFileAsync2(
|
|
14448
14574
|
"git",
|
|
@@ -14526,6 +14652,10 @@ function spaceEnv(spaceDir) {
|
|
|
14526
14652
|
WORKSPACE: paths.workspace,
|
|
14527
14653
|
BACKUPS_DIR: paths.backups,
|
|
14528
14654
|
BUILDS_DIR: paths.builds,
|
|
14655
|
+
// Where `factiii-secrets` finds the runner. It can also walk up for the
|
|
14656
|
+
// socket, but only from inside the space tree - a step that cd'd into a
|
|
14657
|
+
// build dir elsewhere would not find it.
|
|
14658
|
+
FACTIII_SECRETS_SOCK: secretsSocketPath(spaceDir),
|
|
14529
14659
|
// No keychain credential helper and no interactive git prompts anywhere
|
|
14530
14660
|
// in the deploy surface (agent sessions + authorized commands): auth
|
|
14531
14661
|
// comes from the secrets store or the tokened URL, never the keychain.
|
|
@@ -14536,15 +14666,15 @@ function spaceEnv(spaceDir) {
|
|
|
14536
14666
|
};
|
|
14537
14667
|
}
|
|
14538
14668
|
async function readContract(workspace) {
|
|
14539
|
-
const skill = await
|
|
14669
|
+
const skill = await import_promises3.default.readFile(import_path5.default.join(workspace, SKILL_PATH), "utf-8").catch(() => {
|
|
14540
14670
|
throw new Error("The creator wrote no SKILL.md.");
|
|
14541
14671
|
});
|
|
14542
|
-
const checker = await
|
|
14672
|
+
const checker = await import_promises3.default.readFile(import_path5.default.join(workspace, CHECKER_PATH), "utf-8").catch(() => {
|
|
14543
14673
|
throw new Error("The creator wrote no check-environment.sh.");
|
|
14544
14674
|
});
|
|
14545
14675
|
try {
|
|
14546
14676
|
const raw = JSON.parse(
|
|
14547
|
-
await
|
|
14677
|
+
await import_promises3.default.readFile(import_path5.default.join(workspace, VARIABLES_PATH), "utf-8")
|
|
14548
14678
|
);
|
|
14549
14679
|
return {
|
|
14550
14680
|
skill,
|
|
@@ -14561,7 +14691,7 @@ async function generateDeploySkill(opts) {
|
|
|
14561
14691
|
throw new Error("Describe your deployment process first.");
|
|
14562
14692
|
}
|
|
14563
14693
|
const status = await checkDeployStatus(opts);
|
|
14564
|
-
const workspace =
|
|
14694
|
+
const workspace = import_path5.default.join(opts.spaceDir, "workspace");
|
|
14565
14695
|
sendLog({
|
|
14566
14696
|
type: "system",
|
|
14567
14697
|
content: `Resetting workspace to origin/${status.branch}...`
|
|
@@ -14586,13 +14716,12 @@ async function generateDeploySkill(opts) {
|
|
|
14586
14716
|
prompt: buildPrompt(prompts_default.deployCreate, {
|
|
14587
14717
|
INSTRUCTIONS: instructions
|
|
14588
14718
|
}),
|
|
14589
|
-
allowedTools: "Read,Grep,Glob,Bash,Edit,Write,WebSearch,WebFetch",
|
|
14719
|
+
allowedTools: "Read,Grep,Glob,Bash,Edit,Write,WebSearch,WebFetch,Skill",
|
|
14590
14720
|
// auto: the per-action classifier blocks escalations/unrecognized-infra
|
|
14591
14721
|
// actions - a host session with no human reviewing each command should
|
|
14592
14722
|
// not run bypassPermissions. (Codex maps this to a workspace-write
|
|
14593
14723
|
// sandbox; see agent.ts.)
|
|
14594
14724
|
permissionMode: "auto",
|
|
14595
|
-
model: "claude-opus-4-6",
|
|
14596
14725
|
includePartialMessages: true,
|
|
14597
14726
|
env: spaceEnv(opts.spaceDir),
|
|
14598
14727
|
callbacks: { onLog: (entry) => sendLog(entry) }
|
|
@@ -14605,7 +14734,7 @@ async function generateDeploySkill(opts) {
|
|
|
14605
14734
|
async function reviseDeploySkill(opts) {
|
|
14606
14735
|
const { feedback, sendLog } = opts;
|
|
14607
14736
|
if (!feedback.trim()) throw new Error("Corrections are empty.");
|
|
14608
|
-
const workspace =
|
|
14737
|
+
const workspace = import_path5.default.join(opts.spaceDir, "workspace");
|
|
14609
14738
|
sendLog({ type: "system", content: "Applying your corrections..." });
|
|
14610
14739
|
const { result } = await spawnAgent(
|
|
14611
14740
|
opts.provider,
|
|
@@ -14615,9 +14744,8 @@ async function reviseDeploySkill(opts) {
|
|
|
14615
14744
|
prompt: buildPrompt(prompts_default.deployCreateRevise, {
|
|
14616
14745
|
FEEDBACK: feedback
|
|
14617
14746
|
}),
|
|
14618
|
-
allowedTools: "Read,Grep,Glob,Bash,Edit,Write,WebSearch,WebFetch",
|
|
14747
|
+
allowedTools: "Read,Grep,Glob,Bash,Edit,Write,WebSearch,WebFetch,Skill",
|
|
14619
14748
|
permissionMode: "auto",
|
|
14620
|
-
model: "claude-opus-4-6",
|
|
14621
14749
|
includePartialMessages: true,
|
|
14622
14750
|
resumeSessionId: opts.resumeSessionId || void 0,
|
|
14623
14751
|
env: spaceEnv(opts.spaceDir),
|
|
@@ -14634,7 +14762,7 @@ async function loadSkillDraft(opts) {
|
|
|
14634
14762
|
throw new Error("Not a deploy-skill branch.");
|
|
14635
14763
|
}
|
|
14636
14764
|
await checkDeployStatus(opts);
|
|
14637
|
-
const workspace =
|
|
14765
|
+
const workspace = import_path5.default.join(opts.spaceDir, "workspace");
|
|
14638
14766
|
await hostGit(
|
|
14639
14767
|
workspace,
|
|
14640
14768
|
"fetch",
|
|
@@ -14646,7 +14774,7 @@ async function loadSkillDraft(opts) {
|
|
|
14646
14774
|
return readContract(workspace);
|
|
14647
14775
|
}
|
|
14648
14776
|
async function approveDeploySkill(opts) {
|
|
14649
|
-
const workspace =
|
|
14777
|
+
const workspace = import_path5.default.join(opts.spaceDir, "workspace");
|
|
14650
14778
|
await readContract(workspace);
|
|
14651
14779
|
await ensureClaudeSkillLink(workspace);
|
|
14652
14780
|
if (opts.gitName)
|
|
@@ -14689,19 +14817,19 @@ async function runEnvironmentCheck(opts) {
|
|
|
14689
14817
|
"No environment checker on the default branch. Generate the deploy skill first."
|
|
14690
14818
|
);
|
|
14691
14819
|
}
|
|
14692
|
-
const workspace =
|
|
14820
|
+
const workspace = import_path5.default.join(opts.spaceDir, "workspace");
|
|
14693
14821
|
const script = await hostGit(
|
|
14694
14822
|
workspace,
|
|
14695
14823
|
"cat-file",
|
|
14696
14824
|
"-p",
|
|
14697
14825
|
`origin/${status.branch}:${CHECKER_PATH}`
|
|
14698
14826
|
);
|
|
14699
|
-
const tmp =
|
|
14700
|
-
await
|
|
14827
|
+
const tmp = import_path5.default.join(opts.spaceDir, `.env-check.${process.pid}.sh`);
|
|
14828
|
+
await import_promises3.default.writeFile(tmp, script, { mode: 448 });
|
|
14701
14829
|
let ok = true;
|
|
14702
14830
|
let output = "";
|
|
14703
14831
|
try {
|
|
14704
|
-
const scriptArg = tmp.split(
|
|
14832
|
+
const scriptArg = tmp.split(import_path5.default.sep).join("/");
|
|
14705
14833
|
const res = await execLogin(`bash ${JSON.stringify(scriptArg)}`, {
|
|
14706
14834
|
cwd: workspace,
|
|
14707
14835
|
env: { ...process.env, ...spaceEnv(opts.spaceDir) },
|
|
@@ -14710,7 +14838,7 @@ async function runEnvironmentCheck(opts) {
|
|
|
14710
14838
|
output = res.output;
|
|
14711
14839
|
if (res.code !== 0) ok = false;
|
|
14712
14840
|
} finally {
|
|
14713
|
-
await
|
|
14841
|
+
await import_promises3.default.rm(tmp, { force: true });
|
|
14714
14842
|
}
|
|
14715
14843
|
const checks = [];
|
|
14716
14844
|
for (const line of output.split("\n")) {
|
|
@@ -14732,7 +14860,7 @@ async function fixEnvironment(opts) {
|
|
|
14732
14860
|
const before = await runEnvironmentCheck(opts);
|
|
14733
14861
|
if (before.ok) return before;
|
|
14734
14862
|
const status = await checkDeployStatus(opts);
|
|
14735
|
-
const workspace =
|
|
14863
|
+
const workspace = import_path5.default.join(opts.spaceDir, "workspace");
|
|
14736
14864
|
const script = await hostGit(
|
|
14737
14865
|
workspace,
|
|
14738
14866
|
"cat-file",
|
|
@@ -14757,7 +14885,6 @@ async function fixEnvironment(opts) {
|
|
|
14757
14885
|
// auto: installs are in-scope of the owner's explicit "fix environment"
|
|
14758
14886
|
// request; the classifier still blocks anything beyond it.
|
|
14759
14887
|
permissionMode: "auto",
|
|
14760
|
-
model: "claude-opus-4-6",
|
|
14761
14888
|
includePartialMessages: true,
|
|
14762
14889
|
env: spaceEnv(opts.spaceDir),
|
|
14763
14890
|
callbacks: { onLog: (entry) => sendLog(entry) }
|
|
@@ -14768,7 +14895,7 @@ async function fixEnvironment(opts) {
|
|
|
14768
14895
|
return runEnvironmentCheck(opts);
|
|
14769
14896
|
}
|
|
14770
14897
|
async function discardDeploySkill(opts) {
|
|
14771
|
-
const workspace =
|
|
14898
|
+
const workspace = import_path5.default.join(opts.spaceDir, "workspace");
|
|
14772
14899
|
await hostGit(workspace, "checkout", "--", ".").catch(() => null);
|
|
14773
14900
|
await hostGit(workspace, "clean", "-fd");
|
|
14774
14901
|
return { success: true };
|
|
@@ -14887,7 +15014,7 @@ var AGENTS_ROOT = ".agents/skills";
|
|
|
14887
15014
|
var CLAUDE_ROOT = ".claude/skills";
|
|
14888
15015
|
var CLI_ROOTS = [CLAUDE_ROOT, ".codex/skills"];
|
|
14889
15016
|
function globalAgentsRoot() {
|
|
14890
|
-
return
|
|
15017
|
+
return import_path6.default.join(import_os2.default.homedir(), ".agents", "skills");
|
|
14891
15018
|
}
|
|
14892
15019
|
var AGENTS_LINK_TARGET = "../.agents/skills";
|
|
14893
15020
|
var BRANCH_PREFIX = "skill/";
|
|
@@ -14897,11 +15024,11 @@ function summarize(raw, base) {
|
|
|
14897
15024
|
return { ...base, description: frontmatter.description, errors };
|
|
14898
15025
|
}
|
|
14899
15026
|
async function rootLinkState(root) {
|
|
14900
|
-
const linkPath =
|
|
14901
|
-
const
|
|
14902
|
-
if (!
|
|
14903
|
-
if (!
|
|
14904
|
-
const target = await
|
|
15027
|
+
const linkPath = import_path6.default.join(import_os2.default.homedir(), ...root.split("/"));
|
|
15028
|
+
const stat2 = await import_promises4.default.lstat(linkPath).catch(() => null);
|
|
15029
|
+
if (!stat2) return "missing";
|
|
15030
|
+
if (!stat2.isSymbolicLink()) return "blocked";
|
|
15031
|
+
const target = await import_promises4.default.readlink(linkPath).catch(() => "");
|
|
14905
15032
|
return target.replace(/\/+$/, "") === AGENTS_LINK_TARGET ? "linked" : "missing";
|
|
14906
15033
|
}
|
|
14907
15034
|
async function globalLinkState() {
|
|
@@ -14911,12 +15038,12 @@ async function globalLinkState() {
|
|
|
14911
15038
|
}
|
|
14912
15039
|
async function listGlobal() {
|
|
14913
15040
|
const root = globalAgentsRoot();
|
|
14914
|
-
const entries = await
|
|
15041
|
+
const entries = await import_promises4.default.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
14915
15042
|
const out = [];
|
|
14916
15043
|
for (const entry of entries) {
|
|
14917
15044
|
if (entry.isFile()) continue;
|
|
14918
|
-
const dir =
|
|
14919
|
-
const raw = await
|
|
15045
|
+
const dir = import_path6.default.join(root, entry.name);
|
|
15046
|
+
const raw = await import_promises4.default.readFile(import_path6.default.join(dir, "SKILL.md"), "utf-8").catch(() => "");
|
|
14920
15047
|
if (!raw) continue;
|
|
14921
15048
|
out.push(
|
|
14922
15049
|
summarize(raw, {
|
|
@@ -15085,7 +15212,7 @@ async function ensureSkillsWorktree(spaceDir, branch) {
|
|
|
15085
15212
|
} catch {
|
|
15086
15213
|
await hostGit(repo, "worktree", "prune").catch(() => {
|
|
15087
15214
|
});
|
|
15088
|
-
await
|
|
15215
|
+
await import_promises4.default.rm(worktree, { recursive: true, force: true });
|
|
15089
15216
|
await hostGit(
|
|
15090
15217
|
repo,
|
|
15091
15218
|
"worktree",
|
|
@@ -15168,11 +15295,11 @@ async function bundleFilesOnDisk(dir) {
|
|
|
15168
15295
|
const out = [];
|
|
15169
15296
|
const walk = async (current, prefix, depth) => {
|
|
15170
15297
|
if (depth > 3 || out.length >= 200) return;
|
|
15171
|
-
const entries = await
|
|
15298
|
+
const entries = await import_promises4.default.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
15172
15299
|
for (const entry of entries) {
|
|
15173
15300
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
15174
15301
|
if (entry.isDirectory()) {
|
|
15175
|
-
await walk(
|
|
15302
|
+
await walk(import_path6.default.join(current, entry.name), rel, depth + 1);
|
|
15176
15303
|
} else if (rel !== "SKILL.md") {
|
|
15177
15304
|
out.push(rel);
|
|
15178
15305
|
}
|
|
@@ -15184,7 +15311,7 @@ async function bundleFilesOnDisk(dir) {
|
|
|
15184
15311
|
async function readSkillImpl(opts) {
|
|
15185
15312
|
if (opts.scope === "global") {
|
|
15186
15313
|
const dir = resolveSkillDir(globalAgentsRoot(), opts.name);
|
|
15187
|
-
const content2 = await
|
|
15314
|
+
const content2 = await import_promises4.default.readFile(import_path6.default.join(dir, "SKILL.md"), "utf-8");
|
|
15188
15315
|
const { frontmatter: frontmatter2, errors: errors2 } = parseSkill(content2);
|
|
15189
15316
|
return {
|
|
15190
15317
|
name: opts.name,
|
|
@@ -15219,55 +15346,55 @@ async function readSkillImpl(opts) {
|
|
|
15219
15346
|
};
|
|
15220
15347
|
}
|
|
15221
15348
|
async function moveIntoAgents(from, to) {
|
|
15222
|
-
if (await
|
|
15349
|
+
if (await import_promises4.default.access(to).then(
|
|
15223
15350
|
() => true,
|
|
15224
15351
|
() => false
|
|
15225
15352
|
)) {
|
|
15226
15353
|
throw new Error(
|
|
15227
|
-
`${
|
|
15354
|
+
`${import_path6.default.basename(to)} exists in both roots. Remove one copy, then migrate.`
|
|
15228
15355
|
);
|
|
15229
15356
|
}
|
|
15230
|
-
await
|
|
15357
|
+
await import_promises4.default.mkdir(import_path6.default.dirname(to), { recursive: true });
|
|
15231
15358
|
try {
|
|
15232
|
-
await
|
|
15359
|
+
await import_promises4.default.rename(from, to);
|
|
15233
15360
|
} catch {
|
|
15234
|
-
await
|
|
15235
|
-
await
|
|
15361
|
+
await import_promises4.default.cp(from, to, { recursive: true });
|
|
15362
|
+
await import_promises4.default.rm(from, { recursive: true, force: true });
|
|
15236
15363
|
}
|
|
15237
15364
|
}
|
|
15238
15365
|
async function migrateGlobal() {
|
|
15239
15366
|
const moved = [];
|
|
15240
15367
|
for (const root of CLI_ROOTS) {
|
|
15241
|
-
const dir =
|
|
15242
|
-
const
|
|
15243
|
-
if (!
|
|
15244
|
-
const entries = await
|
|
15368
|
+
const dir = import_path6.default.join(import_os2.default.homedir(), ...root.split("/"));
|
|
15369
|
+
const stat2 = await import_promises4.default.lstat(dir).catch(() => null);
|
|
15370
|
+
if (!stat2 || stat2.isSymbolicLink()) continue;
|
|
15371
|
+
const entries = await import_promises4.default.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
15245
15372
|
for (const entry of entries) {
|
|
15246
15373
|
if (entry.isFile() || entry.isSymbolicLink()) continue;
|
|
15247
|
-
const src =
|
|
15248
|
-
if (!await
|
|
15374
|
+
const src = import_path6.default.join(dir, entry.name);
|
|
15375
|
+
if (!await import_promises4.default.access(import_path6.default.join(src, "SKILL.md")).then(
|
|
15249
15376
|
() => true,
|
|
15250
15377
|
() => false
|
|
15251
15378
|
)) {
|
|
15252
15379
|
continue;
|
|
15253
15380
|
}
|
|
15254
|
-
await moveIntoAgents(src,
|
|
15381
|
+
await moveIntoAgents(src, import_path6.default.join(globalAgentsRoot(), entry.name));
|
|
15255
15382
|
moved.push(entry.name);
|
|
15256
15383
|
}
|
|
15257
|
-
await
|
|
15384
|
+
await import_promises4.default.rmdir(dir).catch(() => {
|
|
15258
15385
|
});
|
|
15259
15386
|
}
|
|
15260
15387
|
for (const root of CLI_ROOTS) {
|
|
15261
|
-
const linkPath =
|
|
15262
|
-
const after = await
|
|
15263
|
-
if (after?.isSymbolicLink()) await
|
|
15388
|
+
const linkPath = import_path6.default.join(import_os2.default.homedir(), ...root.split("/"));
|
|
15389
|
+
const after = await import_promises4.default.lstat(linkPath).catch(() => null);
|
|
15390
|
+
if (after?.isSymbolicLink()) await import_promises4.default.rm(linkPath, { force: true });
|
|
15264
15391
|
else if (after) {
|
|
15265
15392
|
throw new Error(
|
|
15266
15393
|
`~/${root} still has files that are not skills. Empty it, then migrate.`
|
|
15267
15394
|
);
|
|
15268
15395
|
}
|
|
15269
|
-
await
|
|
15270
|
-
await
|
|
15396
|
+
await import_promises4.default.mkdir(import_path6.default.dirname(linkPath), { recursive: true });
|
|
15397
|
+
await import_promises4.default.symlink(AGENTS_LINK_TARGET, linkPath, "dir");
|
|
15271
15398
|
}
|
|
15272
15399
|
return moved;
|
|
15273
15400
|
}
|
|
@@ -15301,13 +15428,13 @@ async function migrateSkillsImpl(opts) {
|
|
|
15301
15428
|
`${name} exists in both ${AGENTS_ROOT} and ${stray}. Remove one copy, then migrate.`
|
|
15302
15429
|
);
|
|
15303
15430
|
}
|
|
15304
|
-
await
|
|
15431
|
+
await import_promises4.default.mkdir(import_path6.default.join(workspace, AGENTS_ROOT), { recursive: true });
|
|
15305
15432
|
await hostGit(workspace, "mv", stray, dest);
|
|
15306
15433
|
moved.push(name);
|
|
15307
15434
|
}
|
|
15308
|
-
const linkPath =
|
|
15309
|
-
await
|
|
15310
|
-
await
|
|
15435
|
+
const linkPath = import_path6.default.join(workspace, CLAUDE_ROOT);
|
|
15436
|
+
await import_promises4.default.rm(linkPath, { recursive: true, force: true });
|
|
15437
|
+
await import_promises4.default.symlink(AGENTS_LINK_TARGET, linkPath, "dir");
|
|
15311
15438
|
await hostGit(workspace, "add", "-A");
|
|
15312
15439
|
const dirty = await hostGit(workspace, "status", "--porcelain");
|
|
15313
15440
|
if (!dirty.trim()) return { branch: "", moved: [] };
|
|
@@ -15333,16 +15460,16 @@ function assertWritable(name, content) {
|
|
|
15333
15460
|
}
|
|
15334
15461
|
async function writeSkillFile(opts) {
|
|
15335
15462
|
const dir = resolveSkillDir(opts.root, opts.name);
|
|
15336
|
-
const file =
|
|
15463
|
+
const file = import_path6.default.join(dir, "SKILL.md");
|
|
15337
15464
|
if (opts.keepExisting) {
|
|
15338
|
-
const exists = await
|
|
15465
|
+
const exists = await import_promises4.default.access(file).then(
|
|
15339
15466
|
() => true,
|
|
15340
15467
|
() => false
|
|
15341
15468
|
);
|
|
15342
15469
|
if (exists) return;
|
|
15343
15470
|
}
|
|
15344
|
-
await
|
|
15345
|
-
await
|
|
15471
|
+
await import_promises4.default.mkdir(dir, { recursive: true });
|
|
15472
|
+
await import_promises4.default.writeFile(file, opts.content, "utf-8");
|
|
15346
15473
|
}
|
|
15347
15474
|
async function writeGlobalSkill(opts) {
|
|
15348
15475
|
assertWritable(opts.name, opts.content);
|
|
@@ -15357,14 +15484,14 @@ async function installBundledSkill(opts) {
|
|
|
15357
15484
|
content: opts.content,
|
|
15358
15485
|
keepExisting: true
|
|
15359
15486
|
});
|
|
15360
|
-
await write(
|
|
15487
|
+
await write(import_path6.default.join(home, ...AGENTS_ROOT.split("/")));
|
|
15361
15488
|
for (const root of CLI_ROOTS) {
|
|
15362
|
-
const linkPath =
|
|
15363
|
-
const
|
|
15364
|
-
if (
|
|
15365
|
-
if (!
|
|
15366
|
-
await
|
|
15367
|
-
await
|
|
15489
|
+
const linkPath = import_path6.default.join(home, ...root.split("/"));
|
|
15490
|
+
const stat2 = await import_promises4.default.lstat(linkPath).catch(() => null);
|
|
15491
|
+
if (stat2?.isSymbolicLink()) continue;
|
|
15492
|
+
if (!stat2) {
|
|
15493
|
+
await import_promises4.default.mkdir(import_path6.default.dirname(linkPath), { recursive: true });
|
|
15494
|
+
await import_promises4.default.symlink(AGENTS_LINK_TARGET, linkPath, "dir").catch(() => void 0);
|
|
15368
15495
|
continue;
|
|
15369
15496
|
}
|
|
15370
15497
|
await write(linkPath);
|
|
@@ -15372,7 +15499,7 @@ async function installBundledSkill(opts) {
|
|
|
15372
15499
|
}
|
|
15373
15500
|
async function deleteGlobalSkill(opts) {
|
|
15374
15501
|
const dir = resolveSkillDir(globalAgentsRoot(), opts.name);
|
|
15375
|
-
await
|
|
15502
|
+
await import_promises4.default.rm(dir, { recursive: true, force: true });
|
|
15376
15503
|
}
|
|
15377
15504
|
async function proposeRepoSkillImpl(opts) {
|
|
15378
15505
|
assertWritable(opts.name, opts.content);
|
|
@@ -15410,9 +15537,9 @@ async function proposeRepoSkillImpl(opts) {
|
|
|
15410
15537
|
if (opts.gitEmail) {
|
|
15411
15538
|
await hostGit(workspace, "config", "user.email", opts.gitEmail);
|
|
15412
15539
|
}
|
|
15413
|
-
await
|
|
15414
|
-
await
|
|
15415
|
-
|
|
15540
|
+
await import_promises4.default.mkdir(import_path6.default.join(workspace, dir), { recursive: true });
|
|
15541
|
+
await import_promises4.default.writeFile(
|
|
15542
|
+
import_path6.default.join(workspace, dir, "SKILL.md"),
|
|
15416
15543
|
opts.content,
|
|
15417
15544
|
"utf-8"
|
|
15418
15545
|
);
|
|
@@ -15519,18 +15646,18 @@ function vendoredBinaryName() {
|
|
|
15519
15646
|
async function installFrpcBinary(target) {
|
|
15520
15647
|
const p = frpcPaths(target);
|
|
15521
15648
|
const binaryName = vendoredBinaryName();
|
|
15522
|
-
const src =
|
|
15649
|
+
const src = import_path7.default.join(__dirname, "..", "vendor", binaryName);
|
|
15523
15650
|
if (!(0, import_fs2.existsSync)(src)) {
|
|
15524
15651
|
throw new Error(
|
|
15525
15652
|
`Vendored frpc missing (${src}). Run apps/runner/scripts/fetch-frpc.sh.`
|
|
15526
15653
|
);
|
|
15527
15654
|
}
|
|
15528
|
-
const staged = await
|
|
15529
|
-
const source = await
|
|
15655
|
+
const staged = await import_promises5.default.stat(p.binary).catch(() => null);
|
|
15656
|
+
const source = await import_promises5.default.stat(src);
|
|
15530
15657
|
if (staged?.size === source.size) return;
|
|
15531
|
-
await
|
|
15532
|
-
await
|
|
15533
|
-
await
|
|
15658
|
+
await import_promises5.default.mkdir(p.binDir, { recursive: true });
|
|
15659
|
+
await import_promises5.default.copyFile(src, p.binary);
|
|
15660
|
+
await import_promises5.default.chmod(p.binary, 493);
|
|
15534
15661
|
}
|
|
15535
15662
|
var EXPOSE_SKILL_NAME = "share-a-port";
|
|
15536
15663
|
var FENCE = "```";
|
|
@@ -15920,18 +16047,18 @@ var ClaudeModeEngine = class _ClaudeModeEngine {
|
|
|
15920
16047
|
await this.core.ensureSpaceWorkspace();
|
|
15921
16048
|
await assertAgentSignedIn(session.provider, this.core.target());
|
|
15922
16049
|
if (hasGit) {
|
|
15923
|
-
const { path:
|
|
16050
|
+
const { path: path20 } = await addWorktree(
|
|
15924
16051
|
this.core.target(),
|
|
15925
16052
|
session.taskId,
|
|
15926
16053
|
baseBranch || config.mainBranch || "main",
|
|
15927
16054
|
"card"
|
|
15928
16055
|
);
|
|
15929
|
-
session.workspacePath =
|
|
16056
|
+
session.workspacePath = path20;
|
|
15930
16057
|
await setGitIdentity(
|
|
15931
16058
|
this.core.target(),
|
|
15932
16059
|
config.gitName,
|
|
15933
16060
|
config.gitEmail,
|
|
15934
|
-
|
|
16061
|
+
path20
|
|
15935
16062
|
);
|
|
15936
16063
|
this.emitter.addLog(session, "system", "Worktree ready.");
|
|
15937
16064
|
} else {
|
|
@@ -16153,18 +16280,18 @@ var ClaudeModeEngine = class _ClaudeModeEngine {
|
|
|
16153
16280
|
this.emitter.addLog(session, "system", "Preparing environment...");
|
|
16154
16281
|
await this.core.ensureSpaceWorkspace();
|
|
16155
16282
|
await assertAgentSignedIn(session.provider, this.core.target());
|
|
16156
|
-
const { path:
|
|
16283
|
+
const { path: path20 } = await addWorktree(
|
|
16157
16284
|
this.core.target(),
|
|
16158
16285
|
session.taskId,
|
|
16159
16286
|
config.mainBranch || "main",
|
|
16160
16287
|
"card"
|
|
16161
16288
|
);
|
|
16162
|
-
session.workspacePath =
|
|
16289
|
+
session.workspacePath = path20;
|
|
16163
16290
|
await setGitIdentity(
|
|
16164
16291
|
this.core.target(),
|
|
16165
16292
|
config.gitName,
|
|
16166
16293
|
config.gitEmail,
|
|
16167
|
-
|
|
16294
|
+
path20
|
|
16168
16295
|
);
|
|
16169
16296
|
await this.putCard(session);
|
|
16170
16297
|
const patchDir = `${this.core.target().paths.root}/patches/${session.taskId}`;
|
|
@@ -16711,12 +16838,12 @@ var FLUSH_MS = 120;
|
|
|
16711
16838
|
var SELF_WRITE_MS = 2e3;
|
|
16712
16839
|
var dynamicImport = new Function("s", "return import(s)");
|
|
16713
16840
|
var importFs = new Function("s", "return import(s)");
|
|
16714
|
-
async function resolveReal(
|
|
16841
|
+
async function resolveReal(path20) {
|
|
16715
16842
|
try {
|
|
16716
16843
|
const { realpath } = await importFs("fs/promises");
|
|
16717
|
-
return await realpath(
|
|
16844
|
+
return await realpath(path20);
|
|
16718
16845
|
} catch {
|
|
16719
|
-
return
|
|
16846
|
+
return path20;
|
|
16720
16847
|
}
|
|
16721
16848
|
}
|
|
16722
16849
|
var loadSubscribe = null;
|
|
@@ -16830,19 +16957,19 @@ var WorkspaceWatcher = class {
|
|
|
16830
16957
|
}
|
|
16831
16958
|
record(kind, relPath) {
|
|
16832
16959
|
if (this.closed) return;
|
|
16833
|
-
const
|
|
16834
|
-
const expiry = this.selfWrites.get(
|
|
16960
|
+
const path20 = "/" + relPath;
|
|
16961
|
+
const expiry = this.selfWrites.get(path20);
|
|
16835
16962
|
if (expiry !== void 0) {
|
|
16836
16963
|
if (expiry > Date.now()) return;
|
|
16837
|
-
this.selfWrites.delete(
|
|
16964
|
+
this.selfWrites.delete(path20);
|
|
16838
16965
|
}
|
|
16839
16966
|
const { added, changed, removed } = this.pending;
|
|
16840
|
-
added.delete(
|
|
16841
|
-
changed.delete(
|
|
16842
|
-
removed.delete(
|
|
16843
|
-
if (kind === "added") added.add(
|
|
16844
|
-
else if (kind === "changed") changed.add(
|
|
16845
|
-
else removed.add(
|
|
16967
|
+
added.delete(path20);
|
|
16968
|
+
changed.delete(path20);
|
|
16969
|
+
removed.delete(path20);
|
|
16970
|
+
if (kind === "added") added.add(path20);
|
|
16971
|
+
else if (kind === "changed") changed.add(path20);
|
|
16972
|
+
else removed.add(path20);
|
|
16846
16973
|
if (added.size + changed.size + removed.size > MAX_BATCH) {
|
|
16847
16974
|
this.pending = { ...emptyPending(), resync: true };
|
|
16848
16975
|
}
|
|
@@ -16861,8 +16988,8 @@ var WorkspaceWatcher = class {
|
|
|
16861
16988
|
this.pending = emptyPending();
|
|
16862
16989
|
if (!resync && !added.size && !changed.size && !removed.size) return;
|
|
16863
16990
|
const now = Date.now();
|
|
16864
|
-
for (const [
|
|
16865
|
-
if (expiry <= now) this.selfWrites.delete(
|
|
16991
|
+
for (const [path20, expiry] of this.selfWrites) {
|
|
16992
|
+
if (expiry <= now) this.selfWrites.delete(path20);
|
|
16866
16993
|
}
|
|
16867
16994
|
this.emit({
|
|
16868
16995
|
added: resync ? [] : [...added],
|
|
@@ -17066,8 +17193,8 @@ var TerminalModeEngine = class {
|
|
|
17066
17193
|
/** Suppress the change event our own write is about to produce, so a save
|
|
17067
17194
|
* never comes back to the editor as an outside edit. Normalized to the form
|
|
17068
17195
|
* the watcher emits, since callers spell the path either way. */
|
|
17069
|
-
expectWrite(postId,
|
|
17070
|
-
this.watchers.get(postId)?.expectWrite(posixNormalize(
|
|
17196
|
+
expectWrite(postId, path20) {
|
|
17197
|
+
this.watchers.get(postId)?.expectWrite(posixNormalize(path20));
|
|
17071
17198
|
}
|
|
17072
17199
|
// ── Lifecycle / ModeEngine ──
|
|
17073
17200
|
start(base, args) {
|
|
@@ -17264,18 +17391,18 @@ var TerminalModeEngine = class {
|
|
|
17264
17391
|
this.reservedNames.values()
|
|
17265
17392
|
);
|
|
17266
17393
|
this.reservedNames.set(session.postId, workName);
|
|
17267
|
-
const { path:
|
|
17394
|
+
const { path: path20 } = await addWorktree(
|
|
17268
17395
|
this.core.target(),
|
|
17269
17396
|
workName,
|
|
17270
17397
|
branch,
|
|
17271
17398
|
"terminal"
|
|
17272
17399
|
);
|
|
17273
|
-
session.workspacePath =
|
|
17400
|
+
session.workspacePath = path20;
|
|
17274
17401
|
await setGitIdentity(
|
|
17275
17402
|
this.core.target(),
|
|
17276
17403
|
config.gitName,
|
|
17277
17404
|
config.gitEmail,
|
|
17278
|
-
|
|
17405
|
+
path20
|
|
17279
17406
|
);
|
|
17280
17407
|
this.emitter.addLog(session, "system", "Worktree ready.");
|
|
17281
17408
|
} else {
|
|
@@ -17337,11 +17464,11 @@ var TerminalModeEngine = class {
|
|
|
17337
17464
|
}
|
|
17338
17465
|
async bareFsList({
|
|
17339
17466
|
postId,
|
|
17340
|
-
path:
|
|
17467
|
+
path: path20
|
|
17341
17468
|
}) {
|
|
17342
17469
|
const target = this.getBareTarget(postId);
|
|
17343
17470
|
const root = this.getBareRoot(postId);
|
|
17344
|
-
const abs = this.resolveBarePath(
|
|
17471
|
+
const abs = this.resolveBarePath(path20, root);
|
|
17345
17472
|
const relRoot = abs.slice(root.length) || "/";
|
|
17346
17473
|
const out = await target.run([
|
|
17347
17474
|
"sh",
|
|
@@ -17391,30 +17518,30 @@ var TerminalModeEngine = class {
|
|
|
17391
17518
|
}
|
|
17392
17519
|
async bareFsRead({
|
|
17393
17520
|
postId,
|
|
17394
|
-
path:
|
|
17521
|
+
path: path20
|
|
17395
17522
|
}) {
|
|
17396
17523
|
const target = this.getBareTarget(postId);
|
|
17397
|
-
const abs = this.resolveBarePath(
|
|
17524
|
+
const abs = this.resolveBarePath(path20, this.getBareRoot(postId));
|
|
17398
17525
|
const b64 = (await target.sh(`base64 < ${shEscape(abs)} | tr -d '
|
|
17399
17526
|
'`)).trim();
|
|
17400
17527
|
return { content: b64, encoding: "base64" };
|
|
17401
17528
|
}
|
|
17402
17529
|
async bareFsWrite({
|
|
17403
17530
|
postId,
|
|
17404
|
-
path:
|
|
17531
|
+
path: path20,
|
|
17405
17532
|
content,
|
|
17406
17533
|
encoding
|
|
17407
17534
|
}) {
|
|
17408
17535
|
const target = this.getBareTarget(postId);
|
|
17409
|
-
const abs = this.resolveBarePath(
|
|
17536
|
+
const abs = this.resolveBarePath(path20, this.getBareRoot(postId));
|
|
17410
17537
|
const b64 = encoding === "base64" ? content : Buffer.from(content, "utf-8").toString("base64");
|
|
17411
17538
|
if (!/^[A-Za-z0-9+/=\n\r]*$/.test(b64)) {
|
|
17412
17539
|
throw new Error("Invalid base64 payload.");
|
|
17413
17540
|
}
|
|
17414
|
-
this.expectWrite(String(postId),
|
|
17541
|
+
this.expectWrite(String(postId), path20);
|
|
17415
17542
|
await writeBase64File(target, abs, b64);
|
|
17416
17543
|
const session = this.sessions.get(String(postId));
|
|
17417
|
-
if (session) delete session.buffers[
|
|
17544
|
+
if (session) delete session.buffers[path20];
|
|
17418
17545
|
}
|
|
17419
17546
|
/** Append one chunk of a dropped image to the per-space drops dir
|
|
17420
17547
|
* (`paths.root`, OUTSIDE the card worktree so it never touches the repo).
|
|
@@ -17464,39 +17591,39 @@ var TerminalModeEngine = class {
|
|
|
17464
17591
|
}
|
|
17465
17592
|
bareOpenFile({
|
|
17466
17593
|
postId: rawPostId,
|
|
17467
|
-
path:
|
|
17594
|
+
path: path20
|
|
17468
17595
|
}) {
|
|
17469
17596
|
const session = this.sessions.get(String(rawPostId));
|
|
17470
17597
|
if (!session || session.mode !== "bare") return;
|
|
17471
|
-
if (!session.openedFiles.includes(
|
|
17472
|
-
session.openedFiles.push(
|
|
17598
|
+
if (!session.openedFiles.includes(path20)) {
|
|
17599
|
+
session.openedFiles.push(path20);
|
|
17473
17600
|
}
|
|
17474
17601
|
}
|
|
17475
17602
|
bareCloseFile({
|
|
17476
17603
|
postId: rawPostId,
|
|
17477
|
-
path:
|
|
17604
|
+
path: path20
|
|
17478
17605
|
}) {
|
|
17479
17606
|
const session = this.sessions.get(String(rawPostId));
|
|
17480
17607
|
if (!session || session.mode !== "bare") return;
|
|
17481
|
-
session.openedFiles = session.openedFiles.filter((p) => p !==
|
|
17482
|
-
delete session.buffers[
|
|
17608
|
+
session.openedFiles = session.openedFiles.filter((p) => p !== path20);
|
|
17609
|
+
delete session.buffers[path20];
|
|
17483
17610
|
}
|
|
17484
17611
|
bareBufferSet({
|
|
17485
17612
|
postId: rawPostId,
|
|
17486
|
-
path:
|
|
17613
|
+
path: path20,
|
|
17487
17614
|
content
|
|
17488
17615
|
}) {
|
|
17489
17616
|
const session = this.sessions.get(String(rawPostId));
|
|
17490
17617
|
if (!session || session.mode !== "bare") return;
|
|
17491
|
-
session.buffers[
|
|
17618
|
+
session.buffers[path20] = content;
|
|
17492
17619
|
}
|
|
17493
17620
|
async bareFsDelete({
|
|
17494
17621
|
postId,
|
|
17495
|
-
path:
|
|
17622
|
+
path: path20
|
|
17496
17623
|
}) {
|
|
17497
17624
|
const target = this.getBareTarget(postId);
|
|
17498
17625
|
const root = this.getBareRoot(postId);
|
|
17499
|
-
const abs = this.resolveBarePath(
|
|
17626
|
+
const abs = this.resolveBarePath(path20, root);
|
|
17500
17627
|
if (abs === root) {
|
|
17501
17628
|
throw new Error("Refusing to delete the repo root.");
|
|
17502
17629
|
}
|
|
@@ -17504,10 +17631,10 @@ var TerminalModeEngine = class {
|
|
|
17504
17631
|
}
|
|
17505
17632
|
async bareFsMkdir({
|
|
17506
17633
|
postId,
|
|
17507
|
-
path:
|
|
17634
|
+
path: path20
|
|
17508
17635
|
}) {
|
|
17509
17636
|
const target = this.getBareTarget(postId);
|
|
17510
|
-
const abs = this.resolveBarePath(
|
|
17637
|
+
const abs = this.resolveBarePath(path20, this.getBareRoot(postId));
|
|
17511
17638
|
await target.sh(`mkdir -p ${shEscape(abs)}`);
|
|
17512
17639
|
}
|
|
17513
17640
|
async bareReview({
|
|
@@ -18189,8 +18316,8 @@ async function pollGithubDeviceCode(clientId, deviceCode) {
|
|
|
18189
18316
|
};
|
|
18190
18317
|
}
|
|
18191
18318
|
}
|
|
18192
|
-
async function githubApi(token,
|
|
18193
|
-
const res = await fetch(`${API_BASE}${
|
|
18319
|
+
async function githubApi(token, path20) {
|
|
18320
|
+
const res = await fetch(`${API_BASE}${path20}`, {
|
|
18194
18321
|
headers: {
|
|
18195
18322
|
Authorization: `Bearer ${token}`,
|
|
18196
18323
|
Accept: "application/vnd.github+json",
|
|
@@ -18199,7 +18326,7 @@ async function githubApi(token, path18) {
|
|
|
18199
18326
|
});
|
|
18200
18327
|
if (!res.ok) {
|
|
18201
18328
|
throw new Error(
|
|
18202
|
-
`GitHub ${
|
|
18329
|
+
`GitHub ${path20} failed (${res.status}): ${await res.text()}`
|
|
18203
18330
|
);
|
|
18204
18331
|
}
|
|
18205
18332
|
return await res.json();
|
|
@@ -18244,13 +18371,13 @@ async function listGithubBranches(token, fullName) {
|
|
|
18244
18371
|
}
|
|
18245
18372
|
|
|
18246
18373
|
// ../../shared/all/helpers/board-agent-core/host-run.ts
|
|
18247
|
-
var
|
|
18374
|
+
var import_path9 = __toESM(require("path"));
|
|
18248
18375
|
|
|
18249
18376
|
// ../../shared/all/helpers/board-agent-core/host-secrets.ts
|
|
18250
18377
|
var import_child_process4 = require("child_process");
|
|
18251
18378
|
var import_crypto2 = __toESM(require("crypto"));
|
|
18252
|
-
var
|
|
18253
|
-
var
|
|
18379
|
+
var import_promises6 = __toESM(require("fs/promises"));
|
|
18380
|
+
var import_path8 = __toESM(require("path"));
|
|
18254
18381
|
var import_util6 = require("util");
|
|
18255
18382
|
var execFileAsync3 = (0, import_util6.promisify)(import_child_process4.execFile);
|
|
18256
18383
|
var IDENTITY_FILE = "age-identity.enc";
|
|
@@ -18283,7 +18410,7 @@ function deriveKey(password, salt) {
|
|
|
18283
18410
|
}
|
|
18284
18411
|
async function secretsInitialized(configDir) {
|
|
18285
18412
|
try {
|
|
18286
|
-
await
|
|
18413
|
+
await import_promises6.default.access(import_path8.default.join(configDir, IDENTITY_FILE));
|
|
18287
18414
|
return true;
|
|
18288
18415
|
} catch {
|
|
18289
18416
|
return false;
|
|
@@ -18322,10 +18449,10 @@ async function initSecrets(configDir, password) {
|
|
|
18322
18449
|
tag: cipher.getAuthTag().toString("base64"),
|
|
18323
18450
|
data: data.toString("base64")
|
|
18324
18451
|
};
|
|
18325
|
-
await
|
|
18452
|
+
await import_promises6.default.writeFile(import_path8.default.join(configDir, IDENTITY_FILE), JSON.stringify(box), {
|
|
18326
18453
|
mode: 384
|
|
18327
18454
|
});
|
|
18328
|
-
await
|
|
18455
|
+
await import_promises6.default.writeFile(import_path8.default.join(configDir, RECIPIENT_FILE), `${recipient}
|
|
18329
18456
|
`, {
|
|
18330
18457
|
mode: 384
|
|
18331
18458
|
});
|
|
@@ -18348,17 +18475,17 @@ function decryptIdentity(box, password) {
|
|
|
18348
18475
|
}
|
|
18349
18476
|
}
|
|
18350
18477
|
function envPath(spaceDir) {
|
|
18351
|
-
return
|
|
18478
|
+
return import_path8.default.join(spaceDir, "secrets", ENV_FILE);
|
|
18352
18479
|
}
|
|
18353
18480
|
async function identityFor(configDir, password) {
|
|
18354
18481
|
const box = JSON.parse(
|
|
18355
|
-
await
|
|
18482
|
+
await import_promises6.default.readFile(import_path8.default.join(configDir, IDENTITY_FILE), "utf-8")
|
|
18356
18483
|
);
|
|
18357
18484
|
return decryptIdentity(box, password);
|
|
18358
18485
|
}
|
|
18359
18486
|
async function readEnv(spaceDir, identity) {
|
|
18360
18487
|
try {
|
|
18361
|
-
await
|
|
18488
|
+
await import_promises6.default.access(envPath(spaceDir));
|
|
18362
18489
|
} catch {
|
|
18363
18490
|
return {};
|
|
18364
18491
|
}
|
|
@@ -18387,14 +18514,14 @@ async function readEnv(spaceDir, identity) {
|
|
|
18387
18514
|
return values;
|
|
18388
18515
|
}
|
|
18389
18516
|
async function writeEnv(configDir, spaceDir, identity, values) {
|
|
18390
|
-
const recipient = (await
|
|
18517
|
+
const recipient = (await import_promises6.default.readFile(import_path8.default.join(configDir, RECIPIENT_FILE), "utf-8")).trim();
|
|
18391
18518
|
const plain = `${Object.entries(values).map(([k, v]) => `${k}=${v}`).join("\n")}
|
|
18392
18519
|
`;
|
|
18393
18520
|
const tmp = envPath(spaceDir).replace(
|
|
18394
18521
|
/\.enc\.env$/,
|
|
18395
18522
|
`.${process.pid}.tmp.env`
|
|
18396
18523
|
);
|
|
18397
|
-
await
|
|
18524
|
+
await import_promises6.default.writeFile(tmp, plain, { mode: 384 });
|
|
18398
18525
|
try {
|
|
18399
18526
|
const { stdout } = await execFileAsync3(
|
|
18400
18527
|
"sops",
|
|
@@ -18413,9 +18540,9 @@ async function writeEnv(configDir, spaceDir, identity, values) {
|
|
|
18413
18540
|
maxBuffer: 4 * 1024 * 1024
|
|
18414
18541
|
}
|
|
18415
18542
|
);
|
|
18416
|
-
await
|
|
18543
|
+
await import_promises6.default.writeFile(envPath(spaceDir), stdout, { mode: 384 });
|
|
18417
18544
|
} finally {
|
|
18418
|
-
await
|
|
18545
|
+
await import_promises6.default.rm(tmp, { force: true });
|
|
18419
18546
|
}
|
|
18420
18547
|
}
|
|
18421
18548
|
async function listSecretKeys(configDir, spaceDir, password) {
|
|
@@ -18453,16 +18580,6 @@ var FAILED_MARKER = "DEPLOY_FAILED:";
|
|
|
18453
18580
|
var MAX_MARKERLESS_TURNS = 3;
|
|
18454
18581
|
var COMMAND_TIMEOUT_MS = 45 * 6e4;
|
|
18455
18582
|
var tail = (s, max = 8e3) => s.length > max ? s.slice(-max) : s;
|
|
18456
|
-
function redactor(values) {
|
|
18457
|
-
const significant = values.filter((v) => v.length >= 4);
|
|
18458
|
-
return (text) => {
|
|
18459
|
-
let out = text;
|
|
18460
|
-
for (const value2 of significant) {
|
|
18461
|
-
out = out.split(value2).join("[REDACTED]");
|
|
18462
|
-
}
|
|
18463
|
-
return out;
|
|
18464
|
-
};
|
|
18465
|
-
}
|
|
18466
18583
|
function tailMarker(text) {
|
|
18467
18584
|
const zone = text.slice(-2e3);
|
|
18468
18585
|
let best = null;
|
|
@@ -18479,10 +18596,52 @@ function tailMarker(text) {
|
|
|
18479
18596
|
const value2 = zone.slice(best.at + best.marker.length).trim();
|
|
18480
18597
|
return value2 ? { marker: best.marker, value: value2 } : null;
|
|
18481
18598
|
}
|
|
18599
|
+
function containedCwd(cwd, spaceDir) {
|
|
18600
|
+
if (!cwd) return null;
|
|
18601
|
+
const resolved = import_path9.default.resolve(cwd);
|
|
18602
|
+
const root = import_path9.default.resolve(spaceDir);
|
|
18603
|
+
return resolved === root || resolved.startsWith(root + import_path9.default.sep) ? resolved : null;
|
|
18604
|
+
}
|
|
18605
|
+
async function authorizeAndRun(opts) {
|
|
18606
|
+
const { command, hooks } = opts;
|
|
18607
|
+
opts.status?.("waiting for the owner to authorize this command");
|
|
18608
|
+
let secrets = null;
|
|
18609
|
+
let authError = "";
|
|
18610
|
+
while (!secrets) {
|
|
18611
|
+
const auth = await hooks.requestAuth(command, authError);
|
|
18612
|
+
if ("denied" in auth) return { denied: true };
|
|
18613
|
+
try {
|
|
18614
|
+
secrets = await secretsForInjection(
|
|
18615
|
+
opts.configDir,
|
|
18616
|
+
opts.spaceDir,
|
|
18617
|
+
auth.password
|
|
18618
|
+
);
|
|
18619
|
+
} catch (err) {
|
|
18620
|
+
authError = err instanceof Error ? err.message : String(err);
|
|
18621
|
+
opts.status?.(`authorization failed: ${authError}`);
|
|
18622
|
+
}
|
|
18623
|
+
}
|
|
18624
|
+
const redact = redactor(Object.values(secrets));
|
|
18625
|
+
opts.status?.("authorized, running");
|
|
18626
|
+
hooks.sendLog({
|
|
18627
|
+
type: "system",
|
|
18628
|
+
content: "Authorized. Running the command with secrets injected..."
|
|
18629
|
+
});
|
|
18630
|
+
const res = await execLogin(command, {
|
|
18631
|
+
cwd: opts.cwd,
|
|
18632
|
+
env: { ...process.env, ...spaceEnv(opts.spaceDir), ...secrets },
|
|
18633
|
+
timeout: COMMAND_TIMEOUT_MS,
|
|
18634
|
+
maxBuffer: 32 * 1024 * 1024
|
|
18635
|
+
});
|
|
18636
|
+
const output = redact(res.output);
|
|
18637
|
+
hooks.sendLog({ type: "tool", content: `$ ${command}
|
|
18638
|
+
${tail(output)}` });
|
|
18639
|
+
return { code: res.code, output };
|
|
18640
|
+
}
|
|
18482
18641
|
async function startDeployRun(opts) {
|
|
18483
18642
|
const { hooks } = opts;
|
|
18484
|
-
const workspace =
|
|
18485
|
-
const configDir =
|
|
18643
|
+
const workspace = import_path9.default.join(opts.spaceDir, "workspace");
|
|
18644
|
+
const configDir = import_path9.default.dirname(opts.spaceDir);
|
|
18486
18645
|
const status = await checkDeployStatus(opts);
|
|
18487
18646
|
if (!status.hasSkill || !status.hasVariables || !status.hasChecker) {
|
|
18488
18647
|
throw new Error(
|
|
@@ -18503,7 +18662,7 @@ async function startDeployRun(opts) {
|
|
|
18503
18662
|
await hostGit(workspace, "clean", "-fd");
|
|
18504
18663
|
await ensureClaudeSkillLink(workspace);
|
|
18505
18664
|
const contract = prompts_default.deployRun.join("\n");
|
|
18506
|
-
const reminder = "Reminder: every declared deploy variable is intentionally unset in your shell. Never check for them or abort over them -
|
|
18665
|
+
const reminder = "Reminder: every declared deploy variable is intentionally unset in your shell. Never check for them or abort over them - prefix any command that needs one with `factiii-secrets run --` and the runner injects the values.";
|
|
18507
18666
|
let prompt2 = opts.provider === "codex" ? `Use the "deploy" skill: load it and execute it step by step.
|
|
18508
18667
|
|
|
18509
18668
|
${reminder}` : `/deploy
|
|
@@ -18512,109 +18671,105 @@ ${reminder}`;
|
|
|
18512
18671
|
let sessionId = "";
|
|
18513
18672
|
let markerless = 0;
|
|
18514
18673
|
let todos = [];
|
|
18515
|
-
|
|
18516
|
-
|
|
18517
|
-
|
|
18518
|
-
|
|
18519
|
-
|
|
18520
|
-
|
|
18521
|
-
|
|
18522
|
-
|
|
18523
|
-
|
|
18524
|
-
//
|
|
18525
|
-
|
|
18526
|
-
|
|
18527
|
-
|
|
18528
|
-
|
|
18529
|
-
|
|
18530
|
-
|
|
18531
|
-
|
|
18532
|
-
|
|
18533
|
-
|
|
18534
|
-
|
|
18535
|
-
|
|
18536
|
-
|
|
18537
|
-
|
|
18538
|
-
|
|
18674
|
+
const bridge = await startSecretsBridge({
|
|
18675
|
+
spaceDir: opts.spaceDir,
|
|
18676
|
+
handlers: {
|
|
18677
|
+
run: ({ command, cwd, status: report }) => authorizeAndRun({
|
|
18678
|
+
configDir,
|
|
18679
|
+
spaceDir: opts.spaceDir,
|
|
18680
|
+
// A skill legitimately cds into subdirectories, so the CLI's own cwd
|
|
18681
|
+
// is the right place to run - but only while it is still inside the
|
|
18682
|
+
// space. Anywhere else is a confused caller, not a step, and the
|
|
18683
|
+
// workspace is the safe reading of what it meant.
|
|
18684
|
+
cwd: containedCwd(cwd, opts.spaceDir) ?? workspace,
|
|
18685
|
+
command,
|
|
18686
|
+
hooks,
|
|
18687
|
+
status: report
|
|
18688
|
+
}),
|
|
18689
|
+
// Names only. The contract on the default branch already declares them
|
|
18690
|
+
// in public, so this reveals nothing the repo does not.
|
|
18691
|
+
list: () => Promise.resolve(status.variables.map((v) => v.key))
|
|
18692
|
+
}
|
|
18693
|
+
});
|
|
18694
|
+
try {
|
|
18695
|
+
for (; ; ) {
|
|
18696
|
+
if (hooks.isCancelled()) throw new Error("Cancelled by user.");
|
|
18697
|
+
const { proc, result } = await spawnAgent(
|
|
18698
|
+
opts.provider,
|
|
18699
|
+
hostTarget(opts.spaceDir),
|
|
18700
|
+
{
|
|
18701
|
+
cwd: workspace,
|
|
18702
|
+
prompt: prompt2,
|
|
18703
|
+
allowedTools: "Bash,Read,Grep,Glob,TodoWrite,WebSearch,WebFetch,Skill",
|
|
18704
|
+
// The run's guardrails are structural (merge-gated skill, per-command
|
|
18705
|
+
// authorization, runner-side injection); the auto classifier fights
|
|
18706
|
+
// legitimate infrastructure commands, so the session itself runs open.
|
|
18707
|
+
permissionMode: "bypassPermissions",
|
|
18708
|
+
includePartialMessages: true,
|
|
18709
|
+
resumeSessionId: sessionId || void 0,
|
|
18710
|
+
appendSystemPrompt: sessionId ? void 0 : contract,
|
|
18711
|
+
env: spaceEnv(opts.spaceDir),
|
|
18712
|
+
callbacks: {
|
|
18713
|
+
initialTodos: todos,
|
|
18714
|
+
onLog: (entry) => hooks.sendLog(entry),
|
|
18715
|
+
onTodos: (updated) => {
|
|
18716
|
+
todos = updated;
|
|
18717
|
+
hooks.onTodos(updated);
|
|
18718
|
+
}
|
|
18539
18719
|
}
|
|
18540
18720
|
}
|
|
18541
|
-
|
|
18542
|
-
|
|
18543
|
-
|
|
18544
|
-
|
|
18545
|
-
|
|
18546
|
-
|
|
18547
|
-
|
|
18548
|
-
|
|
18549
|
-
|
|
18550
|
-
|
|
18551
|
-
|
|
18552
|
-
|
|
18553
|
-
|
|
18554
|
-
|
|
18555
|
-
|
|
18556
|
-
|
|
18557
|
-
|
|
18558
|
-
|
|
18559
|
-
|
|
18560
|
-
try {
|
|
18561
|
-
secrets = await secretsForInjection(
|
|
18562
|
-
configDir,
|
|
18563
|
-
opts.spaceDir,
|
|
18564
|
-
auth.password
|
|
18565
|
-
);
|
|
18566
|
-
} catch (err) {
|
|
18567
|
-
authError = err instanceof Error ? err.message : String(err);
|
|
18721
|
+
);
|
|
18722
|
+
hooks.registerProc(proc);
|
|
18723
|
+
const { text, sessionId: sid } = await result;
|
|
18724
|
+
hooks.registerProc(null);
|
|
18725
|
+
sessionId = sid || sessionId;
|
|
18726
|
+
if (hooks.isCancelled()) throw new Error("Cancelled by user.");
|
|
18727
|
+
const found = tailMarker(text);
|
|
18728
|
+
if (found?.marker === SECRETS_MARKER) {
|
|
18729
|
+
const command = found.value.split("\n")[0].trim();
|
|
18730
|
+
const res = await authorizeAndRun({
|
|
18731
|
+
configDir,
|
|
18732
|
+
spaceDir: opts.spaceDir,
|
|
18733
|
+
cwd: workspace,
|
|
18734
|
+
command,
|
|
18735
|
+
hooks
|
|
18736
|
+
});
|
|
18737
|
+
if ("denied" in res) {
|
|
18738
|
+
prompt2 = "The owner declined to run that command. Adapt or stop and summarize where the release stands.";
|
|
18739
|
+
continue;
|
|
18568
18740
|
}
|
|
18741
|
+
markerless = 0;
|
|
18742
|
+
const clean = tail(res.output);
|
|
18743
|
+
prompt2 = `The authorized command finished (exit ${res.code}). Redacted output:
|
|
18744
|
+
|
|
18745
|
+
${clean}`;
|
|
18746
|
+
continue;
|
|
18569
18747
|
}
|
|
18570
|
-
if (
|
|
18571
|
-
|
|
18748
|
+
if (found?.marker === HUMAN_MARKER) {
|
|
18749
|
+
markerless = 0;
|
|
18750
|
+
prompt2 = await hooks.requestHuman(found.value);
|
|
18572
18751
|
continue;
|
|
18573
18752
|
}
|
|
18574
|
-
|
|
18753
|
+
if (found?.marker === COMPLETE_MARKER) {
|
|
18754
|
+
return { shipped: true, summary: found.value };
|
|
18755
|
+
}
|
|
18756
|
+
if (found?.marker === FAILED_MARKER) {
|
|
18757
|
+
return { shipped: false, summary: found.value };
|
|
18758
|
+
}
|
|
18759
|
+
markerless += 1;
|
|
18760
|
+
if (markerless >= MAX_MARKERLESS_TURNS) {
|
|
18761
|
+
throw new Error(
|
|
18762
|
+
"The run ended without declaring completion (no DEPLOY_COMPLETE after repeated turns)."
|
|
18763
|
+
);
|
|
18764
|
+
}
|
|
18575
18765
|
hooks.sendLog({
|
|
18576
18766
|
type: "system",
|
|
18577
|
-
content:
|
|
18578
|
-
});
|
|
18579
|
-
const res = await execLogin(command, {
|
|
18580
|
-
cwd: workspace,
|
|
18581
|
-
env: { ...process.env, ...spaceEnv(opts.spaceDir), ...secrets },
|
|
18582
|
-
timeout: COMMAND_TIMEOUT_MS,
|
|
18583
|
-
maxBuffer: 32 * 1024 * 1024
|
|
18767
|
+
content: "Turn ended without a protocol marker - resuming the run..."
|
|
18584
18768
|
});
|
|
18585
|
-
|
|
18586
|
-
const exitNote = `exit ${res.code}`;
|
|
18587
|
-
markerless = 0;
|
|
18588
|
-
const clean = redact(tail(output));
|
|
18589
|
-
hooks.sendLog({ type: "tool", content: `$ ${command}
|
|
18590
|
-
${clean}` });
|
|
18591
|
-
prompt2 = `The authorized command finished (${exitNote}). Redacted output:
|
|
18592
|
-
|
|
18593
|
-
${clean}`;
|
|
18594
|
-
continue;
|
|
18595
|
-
}
|
|
18596
|
-
if (found?.marker === HUMAN_MARKER) {
|
|
18597
|
-
markerless = 0;
|
|
18598
|
-
prompt2 = await hooks.requestHuman(found.value);
|
|
18599
|
-
continue;
|
|
18600
|
-
}
|
|
18601
|
-
if (found?.marker === COMPLETE_MARKER) {
|
|
18602
|
-
return { shipped: true, summary: found.value };
|
|
18769
|
+
prompt2 = "Your turn ended with no protocol marker. If the release fully shipped, end your message with DEPLOY_COMPLETE: followed by your run report; if you are stopping without shipping, end with DEPLOY_FAILED: followed by the report. Otherwise CONTINUE executing the skill from where you left off. If you had background tasks running, they were killed when your turn ended - rerun them, and reap every background task before ending a turn.";
|
|
18603
18770
|
}
|
|
18604
|
-
|
|
18605
|
-
|
|
18606
|
-
}
|
|
18607
|
-
markerless += 1;
|
|
18608
|
-
if (markerless >= MAX_MARKERLESS_TURNS) {
|
|
18609
|
-
throw new Error(
|
|
18610
|
-
"The run ended without declaring completion (no DEPLOY_COMPLETE after repeated turns)."
|
|
18611
|
-
);
|
|
18612
|
-
}
|
|
18613
|
-
hooks.sendLog({
|
|
18614
|
-
type: "system",
|
|
18615
|
-
content: "Turn ended without a protocol marker - resuming the run..."
|
|
18616
|
-
});
|
|
18617
|
-
prompt2 = "Your turn ended with no protocol marker. If the release fully shipped, end your message with DEPLOY_COMPLETE: followed by your run report; if you are stopping without shipping, end with DEPLOY_FAILED: followed by the report. Otherwise CONTINUE executing the skill from where you left off. If you had background tasks running, they were killed when your turn ended - rerun them, and reap every background task before ending a turn.";
|
|
18771
|
+
} finally {
|
|
18772
|
+
await bridge.close().catch(() => void 0);
|
|
18618
18773
|
}
|
|
18619
18774
|
}
|
|
18620
18775
|
|
|
@@ -18706,44 +18861,110 @@ async function getDriveRoot(accessToken) {
|
|
|
18706
18861
|
};
|
|
18707
18862
|
}
|
|
18708
18863
|
|
|
18864
|
+
// ../../shared/all/helpers/board-agent-core/space-core.ts
|
|
18865
|
+
var import_promises8 = require("fs/promises");
|
|
18866
|
+
var import_path11 = __toESM(require("path"));
|
|
18867
|
+
|
|
18709
18868
|
// ../../shared/all/helpers/board-agent-core/plugin.ts
|
|
18869
|
+
var import_crypto3 = __toESM(require("crypto"));
|
|
18710
18870
|
var import_fs3 = require("fs");
|
|
18711
|
-
var
|
|
18871
|
+
var import_promises7 = require("fs/promises");
|
|
18712
18872
|
var import_os3 = __toESM(require("os"));
|
|
18713
|
-
var
|
|
18714
|
-
var
|
|
18873
|
+
var import_path10 = __toESM(require("path"));
|
|
18874
|
+
var MARKETPLACE_NAME = "factiii-runner";
|
|
18875
|
+
var PLUGIN_ID = `factiii@${MARKETPLACE_NAME}`;
|
|
18876
|
+
var SECRETS_BIN = "factiii-secrets";
|
|
18715
18877
|
function candidates() {
|
|
18716
18878
|
return [
|
|
18717
|
-
|
|
18718
|
-
|
|
18719
|
-
|
|
18879
|
+
import_path10.default.join(__dirname, "..", "plugin"),
|
|
18880
|
+
import_path10.default.join(__dirname, "..", "..", "..", "..", "apps", "runner", "plugin"),
|
|
18881
|
+
import_path10.default.join(process.cwd(), "apps", "runner", "plugin")
|
|
18720
18882
|
];
|
|
18721
18883
|
}
|
|
18722
18884
|
function pluginRoot() {
|
|
18723
18885
|
for (const dir of candidates()) {
|
|
18724
|
-
if ((0, import_fs3.existsSync)(
|
|
18886
|
+
if ((0, import_fs3.existsSync)(import_path10.default.join(dir, ".claude-plugin", "marketplace.json"))) {
|
|
18725
18887
|
return dir;
|
|
18726
18888
|
}
|
|
18727
18889
|
}
|
|
18728
18890
|
return null;
|
|
18729
18891
|
}
|
|
18730
18892
|
function renderedPluginRoot() {
|
|
18731
|
-
return
|
|
18893
|
+
return import_path10.default.join(import_os3.default.homedir(), ".factiii-runner", "plugin");
|
|
18894
|
+
}
|
|
18895
|
+
async function runnerVersion(source) {
|
|
18896
|
+
const pkg = import_path10.default.join(source, "..", "package.json");
|
|
18897
|
+
const raw = await (0, import_promises7.readFile)(pkg, "utf8").catch(() => null);
|
|
18898
|
+
if (!raw) return FALLBACK_VERSION;
|
|
18899
|
+
try {
|
|
18900
|
+
const { version } = JSON.parse(raw);
|
|
18901
|
+
return version || FALLBACK_VERSION;
|
|
18902
|
+
} catch {
|
|
18903
|
+
return FALLBACK_VERSION;
|
|
18904
|
+
}
|
|
18905
|
+
}
|
|
18906
|
+
var FALLBACK_VERSION = "0.0.0-unknown";
|
|
18907
|
+
async function treeFiles(dir) {
|
|
18908
|
+
const entries = await (0, import_promises7.readdir)(dir, { withFileTypes: true });
|
|
18909
|
+
const files = [];
|
|
18910
|
+
for (const entry of entries) {
|
|
18911
|
+
const full = import_path10.default.join(dir, entry.name);
|
|
18912
|
+
if (entry.isDirectory()) files.push(...await treeFiles(full));
|
|
18913
|
+
else if (entry.isFile()) files.push(full);
|
|
18914
|
+
}
|
|
18915
|
+
return files.sort();
|
|
18732
18916
|
}
|
|
18733
|
-
async function
|
|
18917
|
+
async function fingerprint(source, url2, version = "") {
|
|
18918
|
+
const hash = import_crypto3.default.createHash("sha256").update(url2).update(version);
|
|
18919
|
+
for (const file of await treeFiles(source)) {
|
|
18920
|
+
hash.update(import_path10.default.relative(source, file));
|
|
18921
|
+
hash.update(await (0, import_promises7.readFile)(file));
|
|
18922
|
+
}
|
|
18923
|
+
return hash.digest("hex");
|
|
18924
|
+
}
|
|
18925
|
+
function stampPath(dest) {
|
|
18926
|
+
return `${dest}.stamp`;
|
|
18927
|
+
}
|
|
18928
|
+
var inFlight = /* @__PURE__ */ new Map();
|
|
18929
|
+
function renderPlugin(boardMcpUrl) {
|
|
18930
|
+
const running = inFlight.get(boardMcpUrl);
|
|
18931
|
+
if (running) return running;
|
|
18932
|
+
const started = renderPluginImpl(boardMcpUrl).finally(
|
|
18933
|
+
() => inFlight.delete(boardMcpUrl)
|
|
18934
|
+
);
|
|
18935
|
+
inFlight.set(boardMcpUrl, started);
|
|
18936
|
+
return started;
|
|
18937
|
+
}
|
|
18938
|
+
async function renderPluginImpl(boardMcpUrl) {
|
|
18734
18939
|
const source = pluginRoot();
|
|
18735
18940
|
if (!source) return null;
|
|
18736
18941
|
const dest = renderedPluginRoot();
|
|
18737
|
-
const
|
|
18738
|
-
const want =
|
|
18739
|
-
|
|
18740
|
-
const have = await (0, import_promises6.readFile)(manifest, "utf8").catch(() => null);
|
|
18942
|
+
const version = await runnerVersion(source);
|
|
18943
|
+
const want = await fingerprint(source, boardMcpUrl, version);
|
|
18944
|
+
const have = await (0, import_promises7.readFile)(stampPath(dest), "utf8").catch(() => null);
|
|
18741
18945
|
if (have === want) return dest;
|
|
18742
|
-
await (0,
|
|
18743
|
-
await (0,
|
|
18744
|
-
await (0,
|
|
18946
|
+
await (0, import_promises7.mkdir)(import_path10.default.dirname(dest), { recursive: true });
|
|
18947
|
+
await (0, import_promises7.cp)(source, dest, { recursive: true, force: true });
|
|
18948
|
+
await (0, import_promises7.writeFile)(
|
|
18949
|
+
import_path10.default.join(dest, "factiii", ".mcp.json"),
|
|
18950
|
+
`${JSON.stringify(boardMcpConfig(boardMcpUrl), null, 2)}
|
|
18951
|
+
`
|
|
18952
|
+
);
|
|
18953
|
+
await stampVersion(dest, version);
|
|
18954
|
+
await (0, import_promises7.writeFile)(stampPath(dest), want);
|
|
18745
18955
|
return dest;
|
|
18746
18956
|
}
|
|
18957
|
+
async function stampVersion(dest, version) {
|
|
18958
|
+
const manifest = import_path10.default.join(dest, "factiii", ".claude-plugin", "plugin.json");
|
|
18959
|
+
const raw = await (0, import_promises7.readFile)(manifest, "utf8").catch(() => null);
|
|
18960
|
+
if (!raw) return;
|
|
18961
|
+
const parsed = JSON.parse(raw);
|
|
18962
|
+
await (0, import_promises7.writeFile)(
|
|
18963
|
+
manifest,
|
|
18964
|
+
`${JSON.stringify({ ...parsed, version }, null, 2)}
|
|
18965
|
+
`
|
|
18966
|
+
);
|
|
18967
|
+
}
|
|
18747
18968
|
function boardMcpConfig(url2) {
|
|
18748
18969
|
return {
|
|
18749
18970
|
mcpServers: {
|
|
@@ -18987,6 +19208,8 @@ var SpaceCore = class {
|
|
|
18987
19208
|
this.execTarget ??= hostTarget(this.spaceDir(), () => {
|
|
18988
19209
|
const env = {};
|
|
18989
19210
|
for (const lane of this.mcpLanes()) env[lane.tokenEnv] = lane.mcp.token;
|
|
19211
|
+
const board = this.boardMcp();
|
|
19212
|
+
if (board) env[BOARD_MCP_TOKEN_ENV] = board.token;
|
|
18990
19213
|
return env;
|
|
18991
19214
|
});
|
|
18992
19215
|
return this.execTarget;
|
|
@@ -19167,6 +19390,7 @@ var SpaceCore = class {
|
|
|
19167
19390
|
this.provisionGitLane(status),
|
|
19168
19391
|
this.provisionMcpLane(),
|
|
19169
19392
|
this.provisionPluginLane(),
|
|
19393
|
+
this.provisionBinLane(),
|
|
19170
19394
|
// Covers a host signed in outside the one-click button, which would
|
|
19171
19395
|
// otherwise meet the wizard the first time it opens a terminal.
|
|
19172
19396
|
markOnboardingComplete(target).catch(() => {
|
|
@@ -19227,6 +19451,13 @@ var SpaceCore = class {
|
|
|
19227
19451
|
const cmd = [
|
|
19228
19452
|
`claude plugin marketplace add ${q} >/dev/null 2>&1 || true`,
|
|
19229
19453
|
`claude plugin install ${PLUGIN_ID} --scope user >/dev/null 2>&1 || true`,
|
|
19454
|
+
// Both CLIs install by COPYING the plugin into a cache keyed by its
|
|
19455
|
+
// version, and that copy is what loads. claude's `install` refuses once
|
|
19456
|
+
// something is installed ("already installed"), so without these two an
|
|
19457
|
+
// upgrade renders new files into a directory nothing reads again. Codex
|
|
19458
|
+
// needs no equivalent: its `add` re-installs from a local marketplace.
|
|
19459
|
+
`claude plugin marketplace update ${MARKETPLACE_NAME} >/dev/null 2>&1 || true`,
|
|
19460
|
+
`claude plugin update ${PLUGIN_ID} >/dev/null 2>&1 || true`,
|
|
19230
19461
|
`codex plugin marketplace add ${q} >/dev/null 2>&1 || true`,
|
|
19231
19462
|
`codex plugin add ${PLUGIN_ID} >/dev/null 2>&1 || true`
|
|
19232
19463
|
].join("; ");
|
|
@@ -19235,6 +19466,35 @@ var SpaceCore = class {
|
|
|
19235
19466
|
} catch {
|
|
19236
19467
|
}
|
|
19237
19468
|
}
|
|
19469
|
+
/** Stage the plugin's `factiii-secrets` onto the agent's PATH.
|
|
19470
|
+
*
|
|
19471
|
+
* A skill can only teach a command that exists. `paths.bin` is already the
|
|
19472
|
+
* PATH prefix `agentEnv()` hands every spawn (it is how `factiii-expose`
|
|
19473
|
+
* resolves), but nothing put anything there for a deploy run - so the
|
|
19474
|
+
* skill would name a command the shell could not find.
|
|
19475
|
+
*
|
|
19476
|
+
* Copied rather than symlinked: an `npm i -g @factiii/runner` upgrade
|
|
19477
|
+
* replaces the package directory, and a link into the old one dangles.
|
|
19478
|
+
* Re-copied whenever the sizes differ, which is what carries an upgrade
|
|
19479
|
+
* through without re-copying on every ensure. */
|
|
19480
|
+
async provisionBinLane() {
|
|
19481
|
+
const source = pluginRoot();
|
|
19482
|
+
if (!source) return;
|
|
19483
|
+
const { bin } = this.target().paths;
|
|
19484
|
+
const from = import_path11.default.join(source, "factiii", "bin", SECRETS_BIN);
|
|
19485
|
+
const to = import_path11.default.join(bin, SECRETS_BIN);
|
|
19486
|
+
try {
|
|
19487
|
+
const [src, staged] = await Promise.all([
|
|
19488
|
+
(0, import_promises8.stat)(from),
|
|
19489
|
+
(0, import_promises8.stat)(to).catch(() => null)
|
|
19490
|
+
]);
|
|
19491
|
+
if (staged?.size === src.size) return;
|
|
19492
|
+
await (0, import_promises8.mkdir)(bin, { recursive: true });
|
|
19493
|
+
await (0, import_promises8.copyFile)(from, to);
|
|
19494
|
+
await (0, import_promises8.chmod)(to, 493);
|
|
19495
|
+
} catch {
|
|
19496
|
+
}
|
|
19497
|
+
}
|
|
19238
19498
|
// Register this space's MCPs with both CLIs, user-scoped, and clear the ones
|
|
19239
19499
|
// it cannot currently offer. Registering regardless left every space holding
|
|
19240
19500
|
// an entry it never uses, which the CLI then fails to handshake with on every
|
|
@@ -19335,6 +19595,10 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19335
19595
|
// In-flight creator session, memory-only (the repo is the source of truth
|
|
19336
19596
|
// once merged). Survives modal reopen via deployCreatorState.
|
|
19337
19597
|
this.skillCreatorSessionId = null;
|
|
19598
|
+
// Which CLI produced that session. A resume id belongs to ONE provider, so
|
|
19599
|
+
// switching the picker between drafting and revising has to start fresh
|
|
19600
|
+
// rather than hand a claude session id to codex.
|
|
19601
|
+
this.skillCreatorProvider = null;
|
|
19338
19602
|
// Set when the draft was resumed from a pushed deploy-skill branch:
|
|
19339
19603
|
// approve commits onto that branch instead of cutting a new one.
|
|
19340
19604
|
this.skillDraftBranch = null;
|
|
@@ -19355,6 +19619,7 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19355
19619
|
// ── Deploy run (host executor; see host-run.ts + .specs/deploy.md) ──
|
|
19356
19620
|
// Memory-only: reopened clients reattach, a restart loses the run and says so.
|
|
19357
19621
|
this.runState = {
|
|
19622
|
+
provider: "claude",
|
|
19358
19623
|
phase: "idle",
|
|
19359
19624
|
todos: [],
|
|
19360
19625
|
logs: [],
|
|
@@ -19498,6 +19763,7 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19498
19763
|
});
|
|
19499
19764
|
const draft = await loadSkillDraft({ ...opts, branch: payload.branch });
|
|
19500
19765
|
this.skillCreatorSessionId = null;
|
|
19766
|
+
this.skillCreatorProvider = null;
|
|
19501
19767
|
this.skillDraftBranch = payload.branch;
|
|
19502
19768
|
this.creatorState = { phase: "review", logs: [], draft, error: "" };
|
|
19503
19769
|
return draft;
|
|
@@ -19543,8 +19809,8 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19543
19809
|
throw err;
|
|
19544
19810
|
}
|
|
19545
19811
|
}
|
|
19546
|
-
deployFixEnvironment(sendLog) {
|
|
19547
|
-
const opts = this.deployCreatorOpts(sendLog);
|
|
19812
|
+
deployFixEnvironment(sendLog, provider) {
|
|
19813
|
+
const opts = this.deployCreatorOpts(sendLog, provider);
|
|
19548
19814
|
return this.runEnvPass(
|
|
19549
19815
|
"fixing",
|
|
19550
19816
|
() => fixEnvironment({
|
|
@@ -19608,13 +19874,14 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19608
19874
|
return this.activity.clear("deploy-run");
|
|
19609
19875
|
}
|
|
19610
19876
|
}
|
|
19611
|
-
deployRunStart(sendLog) {
|
|
19877
|
+
deployRunStart(sendLog, provider) {
|
|
19612
19878
|
if (this.runState.phase === "running" || this.runState.phase === "awaiting-input" || this.runState.phase === "awaiting-auth") {
|
|
19613
19879
|
throw new Error("A deploy run is already in progress.");
|
|
19614
19880
|
}
|
|
19615
|
-
const opts = this.deployCreatorOpts(sendLog);
|
|
19881
|
+
const opts = this.deployCreatorOpts(sendLog, provider);
|
|
19616
19882
|
this.runCancelled = false;
|
|
19617
19883
|
this.runState = {
|
|
19884
|
+
provider: opts.provider,
|
|
19618
19885
|
phase: "running",
|
|
19619
19886
|
todos: [],
|
|
19620
19887
|
logs: [],
|
|
@@ -19735,6 +20002,9 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19735
20002
|
throw new Error("A deploy run is still in progress.");
|
|
19736
20003
|
}
|
|
19737
20004
|
this.runState = {
|
|
20005
|
+
// Unread while idle - the next run sets its own. Carried rather than
|
|
20006
|
+
// reset so nothing has to invent a provider that means nothing yet.
|
|
20007
|
+
provider: this.runState.provider,
|
|
19738
20008
|
phase: "idle",
|
|
19739
20009
|
todos: [],
|
|
19740
20010
|
logs: [],
|
|
@@ -19833,7 +20103,7 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19833
20103
|
throw err;
|
|
19834
20104
|
}
|
|
19835
20105
|
}
|
|
19836
|
-
deployCreatorOpts(sendLog) {
|
|
20106
|
+
deployCreatorOpts(sendLog, provider) {
|
|
19837
20107
|
const config = this.core.readConfig();
|
|
19838
20108
|
if (!config.repoUrl || !config.githubToken) {
|
|
19839
20109
|
throw new Error("Set the repository URL and GitHub token first.");
|
|
@@ -19843,36 +20113,51 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19843
20113
|
repoUrl: config.repoUrl,
|
|
19844
20114
|
mainBranch: config.mainBranch,
|
|
19845
20115
|
githubToken: config.githubToken,
|
|
19846
|
-
|
|
20116
|
+
// resolveProvider coerces anything unknown to claude, so it can only be
|
|
20117
|
+
// asked about a value that is actually there.
|
|
20118
|
+
provider: provider ? resolveProvider(provider) : this.core.agentProvider(),
|
|
19847
20119
|
sendLog
|
|
19848
20120
|
};
|
|
19849
20121
|
}
|
|
19850
20122
|
deployCreateSkill(payload, sendLog) {
|
|
19851
20123
|
this.skillDraftBranch = null;
|
|
19852
20124
|
return this.runCreatorPass(async () => {
|
|
20125
|
+
const opts = this.deployCreatorOpts(
|
|
20126
|
+
this.creatorLogger(sendLog),
|
|
20127
|
+
payload.provider
|
|
20128
|
+
);
|
|
19853
20129
|
const { sessionId, ...draft } = await generateDeploySkill({
|
|
19854
|
-
...
|
|
20130
|
+
...opts,
|
|
19855
20131
|
instructions: payload.instructions
|
|
19856
20132
|
});
|
|
19857
20133
|
this.skillCreatorSessionId = sessionId || null;
|
|
20134
|
+
this.skillCreatorProvider = opts.provider;
|
|
19858
20135
|
return draft;
|
|
19859
20136
|
});
|
|
19860
20137
|
}
|
|
19861
20138
|
deployReviseSkill(payload, sendLog) {
|
|
19862
20139
|
return this.runCreatorPass(async () => {
|
|
20140
|
+
const opts = this.deployCreatorOpts(
|
|
20141
|
+
this.creatorLogger(sendLog),
|
|
20142
|
+
payload.provider
|
|
20143
|
+
);
|
|
20144
|
+
const resumable = this.skillCreatorProvider === opts.provider;
|
|
19863
20145
|
const { sessionId, ...draft } = await reviseDeploySkill({
|
|
19864
|
-
...
|
|
20146
|
+
...opts,
|
|
19865
20147
|
feedback: payload.feedback,
|
|
19866
|
-
|
|
20148
|
+
// A different CLI than drafted this: its session id means nothing to
|
|
20149
|
+
// the new one, so the revision starts from the files on disk.
|
|
20150
|
+
resumeSessionId: resumable ? this.skillCreatorSessionId ?? void 0 : void 0
|
|
19867
20151
|
});
|
|
19868
20152
|
this.skillCreatorSessionId = sessionId || this.skillCreatorSessionId;
|
|
20153
|
+
this.skillCreatorProvider = opts.provider;
|
|
19869
20154
|
return draft;
|
|
19870
20155
|
});
|
|
19871
20156
|
}
|
|
19872
20157
|
// ── Runner secrets store (secrets.sh contract; see host-secrets.ts) ──
|
|
19873
20158
|
secretsDirs() {
|
|
19874
20159
|
const spaceDir = this.core.spaceDir();
|
|
19875
|
-
return { configDir: (0,
|
|
20160
|
+
return { configDir: (0, import_path12.dirname)(spaceDir), spaceDir };
|
|
19876
20161
|
}
|
|
19877
20162
|
async deploySecretsStatus() {
|
|
19878
20163
|
return {
|
|
@@ -19906,6 +20191,7 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19906
20191
|
const config = this.core.readConfig();
|
|
19907
20192
|
const onBranch = this.skillDraftBranch ?? void 0;
|
|
19908
20193
|
this.skillCreatorSessionId = null;
|
|
20194
|
+
this.skillCreatorProvider = null;
|
|
19909
20195
|
this.skillDraftBranch = null;
|
|
19910
20196
|
this.creatorState = { phase: "idle", logs: [], draft: null, error: "" };
|
|
19911
20197
|
this.activity.clear("deploy-skill");
|
|
@@ -19918,6 +20204,7 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
19918
20204
|
}
|
|
19919
20205
|
deployDiscardSkill() {
|
|
19920
20206
|
this.skillCreatorSessionId = null;
|
|
20207
|
+
this.skillCreatorProvider = null;
|
|
19921
20208
|
this.skillDraftBranch = null;
|
|
19922
20209
|
this.creatorState = { phase: "idle", logs: [], draft: null, error: "" };
|
|
19923
20210
|
this.activity.clear("deploy-skill");
|
|
@@ -20722,14 +21009,16 @@ ${c.content.trim() || "(no description)"}`
|
|
|
20722
21009
|
case "deployFixEnvironment":
|
|
20723
21010
|
return await this.deployFixEnvironment(
|
|
20724
21011
|
askLogSender ?? (() => {
|
|
20725
|
-
})
|
|
21012
|
+
}),
|
|
21013
|
+
payload?.provider
|
|
20726
21014
|
);
|
|
20727
21015
|
case "deployEnvState":
|
|
20728
21016
|
return this.deployEnvState();
|
|
20729
21017
|
case "deployRunStart":
|
|
20730
21018
|
return this.deployRunStart(
|
|
20731
21019
|
askLogSender ?? (() => {
|
|
20732
|
-
})
|
|
21020
|
+
}),
|
|
21021
|
+
payload?.provider
|
|
20733
21022
|
);
|
|
20734
21023
|
case "deployRunState":
|
|
20735
21024
|
return this.deployRunState();
|
|
@@ -20929,11 +21218,11 @@ ${c.content.trim() || "(no description)"}`
|
|
|
20929
21218
|
|
|
20930
21219
|
// ../../shared/all/helpers/board-agent-core/index-service.ts
|
|
20931
21220
|
var import_child_process5 = require("child_process");
|
|
20932
|
-
var
|
|
21221
|
+
var import_crypto4 = require("crypto");
|
|
20933
21222
|
var import_fs4 = require("fs");
|
|
20934
|
-
var
|
|
20935
|
-
var
|
|
20936
|
-
var
|
|
21223
|
+
var import_promises9 = __toESM(require("fs/promises"));
|
|
21224
|
+
var import_net3 = require("net");
|
|
21225
|
+
var import_path13 = require("path");
|
|
20937
21226
|
var import_util7 = require("util");
|
|
20938
21227
|
var execFileAsync4 = (0, import_util7.promisify)(import_child_process5.execFile);
|
|
20939
21228
|
var IndexService = class {
|
|
@@ -20947,7 +21236,7 @@ var IndexService = class {
|
|
|
20947
21236
|
/** Per-space bearer for MCP + file, derived from the control token (the
|
|
20948
21237
|
* service computes the same). */
|
|
20949
21238
|
bearerToken(spaceSlug) {
|
|
20950
|
-
return (0,
|
|
21239
|
+
return (0, import_crypto4.createHmac)("sha256", this.opts.controlToken).update(`mcp:${spaceSlug}`).digest("hex");
|
|
20951
21240
|
}
|
|
20952
21241
|
/** MCP bridge for a space. Null until the service is up (which it kicks
|
|
20953
21242
|
* off), so a bad :0 URL is never baked in; provisionMcpLane re-runs. */
|
|
@@ -20992,7 +21281,7 @@ var IndexService = class {
|
|
|
20992
21281
|
const log = await this.openLog();
|
|
20993
21282
|
this.proc = (0, import_child_process5.spawn)(
|
|
20994
21283
|
process.execPath,
|
|
20995
|
-
[(0,
|
|
21284
|
+
[(0, import_path13.join)(this.opts.indexDir, "index.js")],
|
|
20996
21285
|
{
|
|
20997
21286
|
cwd: this.opts.indexDir,
|
|
20998
21287
|
env: {
|
|
@@ -21020,8 +21309,8 @@ var IndexService = class {
|
|
|
21020
21309
|
* opened (a missing log must never stop the service from starting). */
|
|
21021
21310
|
async openLog() {
|
|
21022
21311
|
try {
|
|
21023
|
-
await
|
|
21024
|
-
return (0, import_fs4.openSync)((0,
|
|
21312
|
+
await import_promises9.default.mkdir(this.opts.dataDir, { recursive: true });
|
|
21313
|
+
return (0, import_fs4.openSync)((0, import_path13.join)(this.opts.dataDir, "index.log"), "a");
|
|
21025
21314
|
} catch {
|
|
21026
21315
|
return null;
|
|
21027
21316
|
}
|
|
@@ -21030,10 +21319,10 @@ var IndexService = class {
|
|
|
21030
21319
|
* platform needing node-gyp fails here rather than at query time. */
|
|
21031
21320
|
async installDeps() {
|
|
21032
21321
|
const dir = this.opts.indexDir;
|
|
21033
|
-
if ((0, import_fs4.existsSync)((0,
|
|
21034
|
-
await
|
|
21035
|
-
(0,
|
|
21036
|
-
(0,
|
|
21322
|
+
if ((0, import_fs4.existsSync)((0, import_path13.join)(dir, "node_modules"))) return;
|
|
21323
|
+
await import_promises9.default.copyFile(
|
|
21324
|
+
(0, import_path13.join)(dir, "image-package.json"),
|
|
21325
|
+
(0, import_path13.join)(dir, "package.json")
|
|
21037
21326
|
);
|
|
21038
21327
|
await execFileAsync4(
|
|
21039
21328
|
"npm",
|
|
@@ -21045,15 +21334,15 @@ var IndexService = class {
|
|
|
21045
21334
|
* CLIs' persistent MCP config, so a fresh port each start would leave every
|
|
21046
21335
|
* registration pointing at a dead one. */
|
|
21047
21336
|
async stablePort() {
|
|
21048
|
-
const file = (0,
|
|
21049
|
-
const saved = Number(await
|
|
21337
|
+
const file = (0, import_path13.join)(this.opts.dataDir, ".port");
|
|
21338
|
+
const saved = Number(await import_promises9.default.readFile(file, "utf-8").catch(() => ""));
|
|
21050
21339
|
if (Number.isInteger(saved) && saved > 0 && await isFree(saved)) {
|
|
21051
21340
|
return saved;
|
|
21052
21341
|
}
|
|
21053
21342
|
const port = await freePort();
|
|
21054
|
-
await
|
|
21343
|
+
await import_promises9.default.mkdir(this.opts.dataDir, { recursive: true }).catch(() => {
|
|
21055
21344
|
});
|
|
21056
|
-
await
|
|
21345
|
+
await import_promises9.default.writeFile(file, String(port), "utf-8").catch(() => {
|
|
21057
21346
|
});
|
|
21058
21347
|
return port;
|
|
21059
21348
|
}
|
|
@@ -21147,14 +21436,14 @@ var IndexService = class {
|
|
|
21147
21436
|
};
|
|
21148
21437
|
function isFree(port) {
|
|
21149
21438
|
return new Promise((resolve2) => {
|
|
21150
|
-
const srv = (0,
|
|
21439
|
+
const srv = (0, import_net3.createServer)();
|
|
21151
21440
|
srv.once("error", () => resolve2(false));
|
|
21152
21441
|
srv.listen(port, "127.0.0.1", () => srv.close(() => resolve2(true)));
|
|
21153
21442
|
});
|
|
21154
21443
|
}
|
|
21155
21444
|
function freePort() {
|
|
21156
21445
|
return new Promise((resolve2, reject) => {
|
|
21157
|
-
const srv = (0,
|
|
21446
|
+
const srv = (0, import_net3.createServer)();
|
|
21158
21447
|
srv.on("error", reject);
|
|
21159
21448
|
srv.listen(0, "127.0.0.1", () => {
|
|
21160
21449
|
const addr = srv.address();
|
|
@@ -21222,22 +21511,22 @@ async function pairWithBrowser(serverUrl) {
|
|
|
21222
21511
|
// src/config.ts
|
|
21223
21512
|
var import_fs8 = __toESM(require("fs"));
|
|
21224
21513
|
var import_os6 = __toESM(require("os"));
|
|
21225
|
-
var
|
|
21514
|
+
var import_path18 = __toESM(require("path"));
|
|
21226
21515
|
|
|
21227
21516
|
// src/secureFile.ts
|
|
21228
|
-
var
|
|
21517
|
+
var import_crypto7 = __toESM(require("crypto"));
|
|
21229
21518
|
var import_fs7 = __toESM(require("fs"));
|
|
21230
|
-
var
|
|
21519
|
+
var import_path17 = __toESM(require("path"));
|
|
21231
21520
|
|
|
21232
21521
|
// src/keychain.ts
|
|
21233
21522
|
var import_child_process7 = require("child_process");
|
|
21234
|
-
var
|
|
21523
|
+
var import_crypto5 = __toESM(require("crypto"));
|
|
21235
21524
|
var import_fs5 = __toESM(require("fs"));
|
|
21236
21525
|
var import_os4 = __toESM(require("os"));
|
|
21237
|
-
var
|
|
21526
|
+
var import_path14 = __toESM(require("path"));
|
|
21238
21527
|
var SERVICE = "factiii-runner";
|
|
21239
21528
|
var ACCOUNT = "config-encryption-key";
|
|
21240
|
-
var DPAPI_KEY_FILE =
|
|
21529
|
+
var DPAPI_KEY_FILE = import_path14.default.join(
|
|
21241
21530
|
import_os4.default.homedir(),
|
|
21242
21531
|
".factiii-runner",
|
|
21243
21532
|
"config-key.dpapi"
|
|
@@ -21343,7 +21632,7 @@ function winWrite(key) {
|
|
|
21343
21632
|
"-Command",
|
|
21344
21633
|
`ConvertTo-SecureString -String '${key.toString("base64")}' -AsPlainText -Force | ConvertFrom-SecureString`
|
|
21345
21634
|
]);
|
|
21346
|
-
import_fs5.default.mkdirSync(
|
|
21635
|
+
import_fs5.default.mkdirSync(import_path14.default.dirname(DPAPI_KEY_FILE), { recursive: true });
|
|
21347
21636
|
import_fs5.default.writeFileSync(DPAPI_KEY_FILE, `${out.trim()}
|
|
21348
21637
|
`, { mode: 384 });
|
|
21349
21638
|
}
|
|
@@ -21483,7 +21772,7 @@ function resolve() {
|
|
|
21483
21772
|
cached = { key: null, status: diagnose() };
|
|
21484
21773
|
return cached;
|
|
21485
21774
|
}
|
|
21486
|
-
const fresh =
|
|
21775
|
+
const fresh = import_crypto5.default.randomBytes(32);
|
|
21487
21776
|
writeKey(fresh);
|
|
21488
21777
|
const verify = readKey();
|
|
21489
21778
|
if (verify.state !== "found" || !verify.key.equals(fresh)) {
|
|
@@ -21505,23 +21794,23 @@ function keychainStatus() {
|
|
|
21505
21794
|
}
|
|
21506
21795
|
|
|
21507
21796
|
// src/secureStore.ts
|
|
21508
|
-
var
|
|
21797
|
+
var import_crypto6 = __toESM(require("crypto"));
|
|
21509
21798
|
var import_fs6 = __toESM(require("fs"));
|
|
21510
|
-
var
|
|
21799
|
+
var import_path16 = __toESM(require("path"));
|
|
21511
21800
|
|
|
21512
21801
|
// src/paths.ts
|
|
21513
21802
|
var import_os5 = __toESM(require("os"));
|
|
21514
|
-
var
|
|
21515
|
-
var CONFIG_DIR =
|
|
21803
|
+
var import_path15 = __toESM(require("path"));
|
|
21804
|
+
var CONFIG_DIR = import_path15.default.join(import_os5.default.homedir(), ".factiii-runner");
|
|
21516
21805
|
function safeSlug(spaceSlug) {
|
|
21517
21806
|
return spaceSlug.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
21518
21807
|
}
|
|
21519
21808
|
function spaceDirPath(spaceSlug) {
|
|
21520
|
-
return
|
|
21809
|
+
return import_path15.default.join(CONFIG_DIR, safeSlug(spaceSlug));
|
|
21521
21810
|
}
|
|
21522
21811
|
|
|
21523
21812
|
// src/secureStore.ts
|
|
21524
|
-
var VAULT_KEY_FILE =
|
|
21813
|
+
var VAULT_KEY_FILE = import_path16.default.join(CONFIG_DIR, "vault-key.json");
|
|
21525
21814
|
var SCRYPT_N2 = 1 << 15;
|
|
21526
21815
|
var MIN_PASSWORD_LENGTH = 8;
|
|
21527
21816
|
var SecureStoreError = class extends Error {
|
|
@@ -21548,7 +21837,7 @@ function requireKeychainKey() {
|
|
|
21548
21837
|
);
|
|
21549
21838
|
}
|
|
21550
21839
|
function passwordKey(password, salt) {
|
|
21551
|
-
return
|
|
21840
|
+
return import_crypto6.default.scryptSync(password, salt, 32, {
|
|
21552
21841
|
N: SCRYPT_N2,
|
|
21553
21842
|
maxmem: 64 * 1024 * 1024
|
|
21554
21843
|
});
|
|
@@ -21556,7 +21845,7 @@ function passwordKey(password, salt) {
|
|
|
21556
21845
|
function wrapKey(machineKey, pwKey) {
|
|
21557
21846
|
const ikm = pwKey ? Buffer.concat([machineKey, pwKey]) : machineKey;
|
|
21558
21847
|
return Buffer.from(
|
|
21559
|
-
|
|
21848
|
+
import_crypto6.default.hkdfSync(
|
|
21560
21849
|
"sha256",
|
|
21561
21850
|
ikm,
|
|
21562
21851
|
Buffer.alloc(0),
|
|
@@ -21566,8 +21855,8 @@ function wrapKey(machineKey, pwKey) {
|
|
|
21566
21855
|
);
|
|
21567
21856
|
}
|
|
21568
21857
|
function seal(plaintext, key) {
|
|
21569
|
-
const iv =
|
|
21570
|
-
const cipher =
|
|
21858
|
+
const iv = import_crypto6.default.randomBytes(12);
|
|
21859
|
+
const cipher = import_crypto6.default.createCipheriv("aes-256-gcm", key, iv);
|
|
21571
21860
|
const data = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
21572
21861
|
return {
|
|
21573
21862
|
iv: iv.toString("base64"),
|
|
@@ -21577,7 +21866,7 @@ function seal(plaintext, key) {
|
|
|
21577
21866
|
}
|
|
21578
21867
|
function unseal(box, key) {
|
|
21579
21868
|
try {
|
|
21580
|
-
const decipher =
|
|
21869
|
+
const decipher = import_crypto6.default.createDecipheriv(
|
|
21581
21870
|
"aes-256-gcm",
|
|
21582
21871
|
key,
|
|
21583
21872
|
Buffer.from(box.iv, "base64")
|
|
@@ -21610,7 +21899,7 @@ function writeVaultFile(file) {
|
|
|
21610
21899
|
}
|
|
21611
21900
|
var unlockedDek = null;
|
|
21612
21901
|
function createVault(machineKey) {
|
|
21613
|
-
const dek =
|
|
21902
|
+
const dek = import_crypto6.default.randomBytes(32);
|
|
21614
21903
|
writeVaultFile({
|
|
21615
21904
|
v: 1,
|
|
21616
21905
|
passwordEnabled: false,
|
|
@@ -21705,7 +21994,7 @@ function enablePassword(password) {
|
|
|
21705
21994
|
);
|
|
21706
21995
|
}
|
|
21707
21996
|
const dek = dataKey();
|
|
21708
|
-
const salt =
|
|
21997
|
+
const salt = import_crypto6.default.randomBytes(16);
|
|
21709
21998
|
writeVaultFile({
|
|
21710
21999
|
v: 1,
|
|
21711
22000
|
passwordEnabled: true,
|
|
@@ -21733,7 +22022,7 @@ function changePassword(current, next) {
|
|
|
21733
22022
|
"The current password is not correct."
|
|
21734
22023
|
);
|
|
21735
22024
|
}
|
|
21736
|
-
const salt =
|
|
22025
|
+
const salt = import_crypto6.default.randomBytes(16);
|
|
21737
22026
|
writeVaultFile({
|
|
21738
22027
|
v: 1,
|
|
21739
22028
|
passwordEnabled: true,
|
|
@@ -21810,8 +22099,8 @@ function storeStatus() {
|
|
|
21810
22099
|
// src/secureFile.ts
|
|
21811
22100
|
var MAGIC = "FACTIII1";
|
|
21812
22101
|
function encrypt(plain, key, version) {
|
|
21813
|
-
const iv =
|
|
21814
|
-
const cipher =
|
|
22102
|
+
const iv = import_crypto7.default.randomBytes(12);
|
|
22103
|
+
const cipher = import_crypto7.default.createCipheriv("aes-256-gcm", key, iv);
|
|
21815
22104
|
const data = Buffer.concat([cipher.update(plain), cipher.final()]);
|
|
21816
22105
|
const envelope = {
|
|
21817
22106
|
v: version,
|
|
@@ -21822,7 +22111,7 @@ function encrypt(plain, key, version) {
|
|
|
21822
22111
|
return `${MAGIC}${JSON.stringify(envelope)}`;
|
|
21823
22112
|
}
|
|
21824
22113
|
function decrypt(envelope, key) {
|
|
21825
|
-
const decipher =
|
|
22114
|
+
const decipher = import_crypto7.default.createDecipheriv(
|
|
21826
22115
|
"aes-256-gcm",
|
|
21827
22116
|
key,
|
|
21828
22117
|
Buffer.from(envelope.iv, "base64")
|
|
@@ -21843,7 +22132,7 @@ function parseEnvelope(raw) {
|
|
|
21843
22132
|
}
|
|
21844
22133
|
}
|
|
21845
22134
|
function atomicWrite(filePath, body) {
|
|
21846
|
-
import_fs7.default.mkdirSync(
|
|
22135
|
+
import_fs7.default.mkdirSync(import_path17.default.dirname(filePath), { recursive: true });
|
|
21847
22136
|
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
21848
22137
|
import_fs7.default.writeFileSync(tmp, body, { mode: 384 });
|
|
21849
22138
|
import_fs7.default.renameSync(tmp, filePath);
|
|
@@ -21958,8 +22247,8 @@ function writeBootJson(filePath, value2) {
|
|
|
21958
22247
|
}
|
|
21959
22248
|
|
|
21960
22249
|
// src/config.ts
|
|
21961
|
-
var CONFIG_DIR2 =
|
|
21962
|
-
var CONFIG_FILE =
|
|
22250
|
+
var CONFIG_DIR2 = import_path18.default.join(import_os6.default.homedir(), ".factiii-runner");
|
|
22251
|
+
var CONFIG_FILE = import_path18.default.join(CONFIG_DIR2, "config.json");
|
|
21963
22252
|
function readRunnerConfig() {
|
|
21964
22253
|
return readBootJson(CONFIG_FILE);
|
|
21965
22254
|
}
|
|
@@ -22128,10 +22417,10 @@ function makeChunkReassembler() {
|
|
|
22128
22417
|
}
|
|
22129
22418
|
|
|
22130
22419
|
// src/daemon.ts
|
|
22131
|
-
var
|
|
22420
|
+
var import_crypto8 = require("crypto");
|
|
22132
22421
|
var import_fs10 = __toESM(require("fs"));
|
|
22133
22422
|
var import_node_datachannel = require("node-datachannel");
|
|
22134
|
-
var
|
|
22423
|
+
var import_path20 = __toESM(require("path"));
|
|
22135
22424
|
|
|
22136
22425
|
// ../../node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.js
|
|
22137
22426
|
var XMLHttpRequestModule = __toESM(require_XMLHttpRequest(), 1);
|
|
@@ -23311,12 +23600,12 @@ function parse2(str) {
|
|
|
23311
23600
|
uri.queryKey = queryKey(uri, uri["query"]);
|
|
23312
23601
|
return uri;
|
|
23313
23602
|
}
|
|
23314
|
-
function pathNames(obj,
|
|
23315
|
-
const regx = /\/{2,9}/g, names =
|
|
23316
|
-
if (
|
|
23603
|
+
function pathNames(obj, path20) {
|
|
23604
|
+
const regx = /\/{2,9}/g, names = path20.replace(regx, "/").split("/");
|
|
23605
|
+
if (path20.slice(0, 1) == "/" || path20.length === 0) {
|
|
23317
23606
|
names.splice(0, 1);
|
|
23318
23607
|
}
|
|
23319
|
-
if (
|
|
23608
|
+
if (path20.slice(-1) == "/") {
|
|
23320
23609
|
names.splice(names.length - 1, 1);
|
|
23321
23610
|
}
|
|
23322
23611
|
return names;
|
|
@@ -23935,7 +24224,7 @@ var protocol2 = Socket.protocol;
|
|
|
23935
24224
|
// ../../node_modules/socket.io-client/build/esm-debug/url.js
|
|
23936
24225
|
var import_debug7 = __toESM(require_src(), 1);
|
|
23937
24226
|
var debug7 = (0, import_debug7.default)("socket.io-client:url");
|
|
23938
|
-
function url(uri,
|
|
24227
|
+
function url(uri, path20 = "", loc) {
|
|
23939
24228
|
let obj = uri;
|
|
23940
24229
|
loc = loc || typeof location !== "undefined" && location;
|
|
23941
24230
|
if (null == uri)
|
|
@@ -23969,7 +24258,7 @@ function url(uri, path18 = "", loc) {
|
|
|
23969
24258
|
obj.path = obj.path || "/";
|
|
23970
24259
|
const ipv6 = obj.host.indexOf(":") !== -1;
|
|
23971
24260
|
const host = ipv6 ? "[" + obj.host + "]" : obj.host;
|
|
23972
|
-
obj.id = obj.protocol + "://" + host + ":" + obj.port +
|
|
24261
|
+
obj.id = obj.protocol + "://" + host + ":" + obj.port + path20;
|
|
23973
24262
|
obj.href = obj.protocol + "://" + host + (loc && loc.port === obj.port ? "" : ":" + obj.port);
|
|
23974
24263
|
return obj;
|
|
23975
24264
|
}
|
|
@@ -25603,8 +25892,8 @@ function lookup(uri, opts) {
|
|
|
25603
25892
|
const parsed = url(uri, opts.path || "/socket.io");
|
|
25604
25893
|
const source = parsed.source;
|
|
25605
25894
|
const id = parsed.id;
|
|
25606
|
-
const
|
|
25607
|
-
const sameNamespace = cache[id] &&
|
|
25895
|
+
const path20 = parsed.path;
|
|
25896
|
+
const sameNamespace = cache[id] && path20 in cache[id]["nsps"];
|
|
25608
25897
|
const newConnection = opts.forceNew || opts["force new connection"] || false === opts.multiplex || sameNamespace;
|
|
25609
25898
|
let io;
|
|
25610
25899
|
if (newConnection) {
|
|
@@ -25646,8 +25935,8 @@ function cardContextPath(target, postId) {
|
|
|
25646
25935
|
const safe = postId.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80) || "card";
|
|
25647
25936
|
return `${target.paths.root}/.cards/${safe}.md`;
|
|
25648
25937
|
}
|
|
25649
|
-
function cardContextPrompt(
|
|
25650
|
-
return `Read ${
|
|
25938
|
+
function cardContextPrompt(path20) {
|
|
25939
|
+
return `Read ${path20} - it is the Factiii card this session was opened from. Use it as the context and start working on it.`;
|
|
25651
25940
|
}
|
|
25652
25941
|
var CLAUDE_PERMISSION_MODE = `--permission-mode auto`;
|
|
25653
25942
|
var RESUME_SEED = "You are now in an interactive terminal, not the board UI. Respond in plain text and use your normal tools; do not use openui-lang or component schemas anymore.";
|
|
@@ -25812,10 +26101,10 @@ var TerminalManager = class {
|
|
|
25812
26101
|
|
|
25813
26102
|
// src/agent-adapter.ts
|
|
25814
26103
|
var import_fs9 = __toESM(require("fs"));
|
|
25815
|
-
var
|
|
25816
|
-
var RUNNER_CONFIG_PATH =
|
|
26104
|
+
var import_path19 = __toESM(require("path"));
|
|
26105
|
+
var RUNNER_CONFIG_PATH = import_path19.default.join(CONFIG_DIR, "runner-config.json");
|
|
25817
26106
|
function connectionPath(spaceSlug) {
|
|
25818
|
-
return
|
|
26107
|
+
return import_path19.default.join(CONFIG_DIR, `space-${safeSlug(spaceSlug)}.onedrive.json`);
|
|
25819
26108
|
}
|
|
25820
26109
|
function writeJson(filePath, value2) {
|
|
25821
26110
|
writeSecureJson(filePath, value2);
|
|
@@ -25849,7 +26138,7 @@ function migrateLegacyConfigs() {
|
|
|
25849
26138
|
for (const name of legacy) {
|
|
25850
26139
|
const slug = name.slice("space-".length, -".json".length);
|
|
25851
26140
|
const parsed = readSecureJson(
|
|
25852
|
-
|
|
26141
|
+
import_path19.default.join(CONFIG_DIR, name)
|
|
25853
26142
|
);
|
|
25854
26143
|
if (!parsed) continue;
|
|
25855
26144
|
if (!file.defaultSlug) {
|
|
@@ -25874,7 +26163,7 @@ function encryptStrayPlaintextConfigs() {
|
|
|
25874
26163
|
return;
|
|
25875
26164
|
}
|
|
25876
26165
|
for (const name of names) {
|
|
25877
|
-
const filePath =
|
|
26166
|
+
const filePath = import_path19.default.join(CONFIG_DIR, name);
|
|
25878
26167
|
try {
|
|
25879
26168
|
const raw = import_fs9.default.readFileSync(filePath, "utf-8");
|
|
25880
26169
|
if (!raw.startsWith("{")) continue;
|
|
@@ -26003,7 +26292,7 @@ var localConfigProvider = {
|
|
|
26003
26292
|
"claude",
|
|
26004
26293
|
"codex"
|
|
26005
26294
|
]) {
|
|
26006
|
-
import_fs9.default.mkdirSync(
|
|
26295
|
+
import_fs9.default.mkdirSync(import_path19.default.join(dir, sub), { recursive: true });
|
|
26007
26296
|
}
|
|
26008
26297
|
return dir;
|
|
26009
26298
|
}
|
|
@@ -26177,15 +26466,15 @@ async function startDaemon(config) {
|
|
|
26177
26466
|
}
|
|
26178
26467
|
);
|
|
26179
26468
|
const indexCandidates = [
|
|
26180
|
-
|
|
26181
|
-
|
|
26469
|
+
import_path20.default.join(__dirname, "..", "index"),
|
|
26470
|
+
import_path20.default.join(__dirname, "..", "..", "index", "image")
|
|
26182
26471
|
];
|
|
26183
26472
|
const indexDir = indexCandidates.find((p) => import_fs10.default.existsSync(p)) ?? indexCandidates[0];
|
|
26184
26473
|
const odIndex = new IndexService({
|
|
26185
|
-
controlToken: (0,
|
|
26474
|
+
controlToken: (0, import_crypto8.createHash)("sha256").update(config.authToken).digest("hex"),
|
|
26186
26475
|
embedUrl: () => config.serverUrl,
|
|
26187
26476
|
indexDir,
|
|
26188
|
-
dataDir:
|
|
26477
|
+
dataDir: import_path20.default.join(CONFIG_DIR, "index-data")
|
|
26189
26478
|
});
|
|
26190
26479
|
void odIndex.ensureRunning().catch((err) => {
|
|
26191
26480
|
console.error(
|