@factiii/runner 0.9.7 → 0.9.8
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 +1517 -152
- package/index/index.js +2 -2
- package/package.json +1 -1
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 path17 = require("node:path");
|
|
967
|
+
var fs14 = 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 = path17.resolve(baseDir, baseName);
|
|
1900
|
+
if (fs14.existsSync(localBin)) return localBin;
|
|
1901
|
+
if (sourceExt.includes(path17.extname(baseName))) return void 0;
|
|
1902
1902
|
const foundExt = sourceExt.find(
|
|
1903
|
-
(ext) =>
|
|
1903
|
+
(ext) => fs14.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 = fs14.realpathSync(this._scriptPath);
|
|
1916
1916
|
} catch (err) {
|
|
1917
1917
|
resolvedScriptPath = this._scriptPath;
|
|
1918
1918
|
}
|
|
1919
|
-
executableDir =
|
|
1920
|
-
|
|
1919
|
+
executableDir = path17.resolve(
|
|
1920
|
+
path17.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 = path17.basename(
|
|
1928
1928
|
this._scriptPath,
|
|
1929
|
-
|
|
1929
|
+
path17.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(path17.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 = path17.basename(filename, path17.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(path18) {
|
|
2795
|
+
if (path18 === void 0) return this._executableDir;
|
|
2796
|
+
this._executableDir = path18;
|
|
2797
2797
|
return this;
|
|
2798
2798
|
}
|
|
2799
2799
|
/**
|
|
@@ -3026,9 +3026,9 @@ 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 fs14 = require("fs");
|
|
3030
3030
|
var Url = require("url");
|
|
3031
|
-
var
|
|
3031
|
+
var spawn5 = require("child_process").spawn;
|
|
3032
3032
|
module2.exports = XMLHttpRequest3;
|
|
3033
3033
|
XMLHttpRequest3.XMLHttpRequest = XMLHttpRequest3;
|
|
3034
3034
|
function XMLHttpRequest3(opts) {
|
|
@@ -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
|
+
fs14.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 = fs14.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
|
+
fs14.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
|
-
var syncProc =
|
|
3327
|
+
var syncProc = spawn5(process.argv[0], ["-e", execString]);
|
|
3328
3328
|
var statusText;
|
|
3329
|
-
while (
|
|
3329
|
+
while (fs14.existsSync(syncFile)) {
|
|
3330
3330
|
}
|
|
3331
|
-
self.responseText =
|
|
3331
|
+
self.responseText = fs14.readFileSync(contentFile, "utf8");
|
|
3332
3332
|
syncProc.stdin.end();
|
|
3333
|
-
|
|
3333
|
+
fs14.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);
|
|
@@ -3984,7 +3984,7 @@ var require_has_flag = __commonJS({
|
|
|
3984
3984
|
var require_supports_color = __commonJS({
|
|
3985
3985
|
"../../node_modules/debug/node_modules/supports-color/index.js"(exports2, module2) {
|
|
3986
3986
|
"use strict";
|
|
3987
|
-
var
|
|
3987
|
+
var os6 = require("os");
|
|
3988
3988
|
var hasFlag = require_has_flag();
|
|
3989
3989
|
var env = process.env;
|
|
3990
3990
|
var forceColor;
|
|
@@ -4022,7 +4022,7 @@ var require_supports_color = __commonJS({
|
|
|
4022
4022
|
}
|
|
4023
4023
|
const min = forceColor ? 1 : 0;
|
|
4024
4024
|
if (process.platform === "win32") {
|
|
4025
|
-
const osRelease =
|
|
4025
|
+
const osRelease = os6.release().split(".");
|
|
4026
4026
|
if (Number(process.versions.node.split(".")[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
4027
4027
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
4028
4028
|
}
|
|
@@ -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: path17, errorMaps, issueData } = params;
|
|
8432
|
+
const fullPath = [...path17, ...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, path17, key) {
|
|
8549
8549
|
this._cachedPath = [];
|
|
8550
8550
|
this.parent = parent;
|
|
8551
8551
|
this.data = value2;
|
|
8552
|
-
this._path =
|
|
8552
|
+
this._path = path17;
|
|
8553
8553
|
this._key = key;
|
|
8554
8554
|
}
|
|
8555
8555
|
get path() {
|
|
@@ -12908,7 +12908,7 @@ ${diff.slice(0, 8e3)}`,
|
|
|
12908
12908
|
}
|
|
12909
12909
|
|
|
12910
12910
|
// ../../shared/all/helpers/board-agent-core/engine.ts
|
|
12911
|
-
var
|
|
12911
|
+
var import_path9 = require("path");
|
|
12912
12912
|
|
|
12913
12913
|
// ../../shared/all/domains/electron.ts
|
|
12914
12914
|
var SETUP_LOG_PREFIX = "log ";
|
|
@@ -12922,6 +12922,7 @@ function hostSpacePaths(spaceDir) {
|
|
|
12922
12922
|
repo: import_path.default.join(spaceDir, "repo"),
|
|
12923
12923
|
worktrees: import_path.default.join(spaceDir, "worktrees"),
|
|
12924
12924
|
workspace: import_path.default.join(spaceDir, "workspace"),
|
|
12925
|
+
skills: import_path.default.join(spaceDir, "skills"),
|
|
12925
12926
|
state: import_path.default.join(spaceDir, "state"),
|
|
12926
12927
|
backups: import_path.default.join(spaceDir, "backups"),
|
|
12927
12928
|
builds: import_path.default.join(spaceDir, "builds"),
|
|
@@ -12951,9 +12952,23 @@ function buildCloneUrl(repoUrl, githubToken) {
|
|
|
12951
12952
|
async function gitInDir(target, workdir, ...args) {
|
|
12952
12953
|
return target.run(["git", "-C", workdir, ...args]);
|
|
12953
12954
|
}
|
|
12954
|
-
var
|
|
12955
|
-
|
|
12956
|
-
|
|
12955
|
+
var WORK_BRANCH_PREFIXES = {
|
|
12956
|
+
card: "card/",
|
|
12957
|
+
terminal: "terminal/"
|
|
12958
|
+
};
|
|
12959
|
+
var LEGACY_WORK_BRANCH_PREFIX = "factiii/card-";
|
|
12960
|
+
var ALL_WORK_BRANCH_PREFIXES = [
|
|
12961
|
+
...Object.values(WORK_BRANCH_PREFIXES),
|
|
12962
|
+
LEGACY_WORK_BRANCH_PREFIX
|
|
12963
|
+
].sort((a, b) => b.length - a.length);
|
|
12964
|
+
function workBranch(workName, kind) {
|
|
12965
|
+
return `${WORK_BRANCH_PREFIXES[kind]}${workName.replace(/[^a-zA-Z0-9._-]/g, "-")}`;
|
|
12966
|
+
}
|
|
12967
|
+
function workNameFromBranch(branch) {
|
|
12968
|
+
for (const prefix of ALL_WORK_BRANCH_PREFIXES) {
|
|
12969
|
+
if (branch.startsWith(prefix)) return branch.slice(prefix.length);
|
|
12970
|
+
}
|
|
12971
|
+
return null;
|
|
12957
12972
|
}
|
|
12958
12973
|
async function listWorkNames(target) {
|
|
12959
12974
|
const dirs = await target.sh(`ls -1 ${target.paths.worktrees} 2>/dev/null || true`).catch(() => "");
|
|
@@ -12966,10 +12981,8 @@ async function listWorkNames(target) {
|
|
|
12966
12981
|
).catch(() => "");
|
|
12967
12982
|
const names = new Set(dirs.split("\n").map((line) => line.trim()));
|
|
12968
12983
|
for (const line of branches.split("\n")) {
|
|
12969
|
-
const
|
|
12970
|
-
if (
|
|
12971
|
-
names.add(name.slice(WORK_BRANCH_PREFIX.length));
|
|
12972
|
-
}
|
|
12984
|
+
const workName = workNameFromBranch(line.trim());
|
|
12985
|
+
if (workName) names.add(workName);
|
|
12973
12986
|
}
|
|
12974
12987
|
names.delete("");
|
|
12975
12988
|
return [...names];
|
|
@@ -13003,10 +13016,18 @@ async function ensurePrimaryClone(target, cloneUrl, branch) {
|
|
|
13003
13016
|
});
|
|
13004
13017
|
await target.sh(`mkdir -p ${target.paths.worktrees}`);
|
|
13005
13018
|
}
|
|
13006
|
-
async function addWorktree(target, workName, baseBranch) {
|
|
13007
|
-
const
|
|
13008
|
-
const branch =
|
|
13009
|
-
await
|
|
13019
|
+
async function addWorktree(target, workName, baseBranch, kind) {
|
|
13020
|
+
const path17 = worktreeFor(target.paths, workName);
|
|
13021
|
+
const branch = workBranch(workName, kind);
|
|
13022
|
+
await gitInDir(
|
|
13023
|
+
target,
|
|
13024
|
+
target.paths.repo,
|
|
13025
|
+
"fetch",
|
|
13026
|
+
"origin",
|
|
13027
|
+
baseBranch
|
|
13028
|
+
).catch(() => {
|
|
13029
|
+
});
|
|
13030
|
+
await removeWorktree(target, path17).catch(() => {
|
|
13010
13031
|
});
|
|
13011
13032
|
await gitInDir(
|
|
13012
13033
|
target,
|
|
@@ -13015,29 +13036,45 @@ async function addWorktree(target, workName, baseBranch) {
|
|
|
13015
13036
|
"add",
|
|
13016
13037
|
"-B",
|
|
13017
13038
|
branch,
|
|
13018
|
-
|
|
13039
|
+
path17,
|
|
13019
13040
|
`origin/${baseBranch}`
|
|
13020
13041
|
);
|
|
13021
|
-
return { path:
|
|
13042
|
+
return { path: path17, branch };
|
|
13022
13043
|
}
|
|
13023
|
-
async function removeWorktree(target,
|
|
13044
|
+
async function removeWorktree(target, path17) {
|
|
13024
13045
|
await gitInDir(
|
|
13025
13046
|
target,
|
|
13026
13047
|
target.paths.repo,
|
|
13027
13048
|
"worktree",
|
|
13028
13049
|
"remove",
|
|
13029
13050
|
"--force",
|
|
13030
|
-
|
|
13051
|
+
path17
|
|
13031
13052
|
);
|
|
13032
13053
|
}
|
|
13054
|
+
async function removeWorktreeAndBranch(target, path17) {
|
|
13055
|
+
const branch = await gitInDir(
|
|
13056
|
+
target,
|
|
13057
|
+
path17,
|
|
13058
|
+
"rev-parse",
|
|
13059
|
+
"--abbrev-ref",
|
|
13060
|
+
"HEAD"
|
|
13061
|
+
).then((out) => out.trim()).catch(() => "");
|
|
13062
|
+
await removeWorktree(target, path17);
|
|
13063
|
+
if (branch && branch !== "HEAD") {
|
|
13064
|
+
await gitInDir(target, target.paths.repo, "branch", "-D", branch).catch(
|
|
13065
|
+
() => {
|
|
13066
|
+
}
|
|
13067
|
+
);
|
|
13068
|
+
}
|
|
13069
|
+
}
|
|
13033
13070
|
async function pruneWorktrees(target) {
|
|
13034
13071
|
await gitInDir(target, target.paths.repo, "worktree", "prune").catch(
|
|
13035
13072
|
() => {
|
|
13036
13073
|
}
|
|
13037
13074
|
);
|
|
13038
13075
|
}
|
|
13039
|
-
function isWorktreePath(target,
|
|
13040
|
-
return
|
|
13076
|
+
function isWorktreePath(target, path17) {
|
|
13077
|
+
return path17.startsWith(`${target.paths.worktrees}/`);
|
|
13041
13078
|
}
|
|
13042
13079
|
async function setGitIdentity(target, name, email, workdir = target.paths.workspace) {
|
|
13043
13080
|
if (name) await gitInDir(target, workdir, "config", "user.name", name);
|
|
@@ -13218,7 +13255,8 @@ async function putSessionCard(s, target, prompt2 = "") {
|
|
|
13218
13255
|
}
|
|
13219
13256
|
async function teardownSession(s, target) {
|
|
13220
13257
|
if (isWorktreeSession(s, target)) {
|
|
13221
|
-
|
|
13258
|
+
const drop = s.mode === "bare" ? removeWorktreeAndBranch : removeWorktree;
|
|
13259
|
+
await drop(target, s.workspacePath).catch(() => {
|
|
13222
13260
|
});
|
|
13223
13261
|
}
|
|
13224
13262
|
await deleteCard(target, s.postId).catch(() => {
|
|
@@ -14064,17 +14102,18 @@ var ClaudeModeEngine = class _ClaudeModeEngine {
|
|
|
14064
14102
|
await this.core.ensureSpaceWorkspace();
|
|
14065
14103
|
await assertAgentSignedIn(session.provider, this.core.target());
|
|
14066
14104
|
if (hasGit) {
|
|
14067
|
-
const { path:
|
|
14105
|
+
const { path: path17 } = await addWorktree(
|
|
14068
14106
|
this.core.target(),
|
|
14069
14107
|
session.taskId,
|
|
14070
|
-
baseBranch || config.mainBranch || "main"
|
|
14108
|
+
baseBranch || config.mainBranch || "main",
|
|
14109
|
+
"card"
|
|
14071
14110
|
);
|
|
14072
|
-
session.workspacePath =
|
|
14111
|
+
session.workspacePath = path17;
|
|
14073
14112
|
await setGitIdentity(
|
|
14074
14113
|
this.core.target(),
|
|
14075
14114
|
config.gitName,
|
|
14076
14115
|
config.gitEmail,
|
|
14077
|
-
|
|
14116
|
+
path17
|
|
14078
14117
|
);
|
|
14079
14118
|
this.emitter.addLog(session, "system", "Worktree ready.");
|
|
14080
14119
|
} else {
|
|
@@ -14296,17 +14335,18 @@ var ClaudeModeEngine = class _ClaudeModeEngine {
|
|
|
14296
14335
|
this.emitter.addLog(session, "system", "Preparing environment...");
|
|
14297
14336
|
await this.core.ensureSpaceWorkspace();
|
|
14298
14337
|
await assertAgentSignedIn(session.provider, this.core.target());
|
|
14299
|
-
const { path:
|
|
14338
|
+
const { path: path17 } = await addWorktree(
|
|
14300
14339
|
this.core.target(),
|
|
14301
14340
|
session.taskId,
|
|
14302
|
-
config.mainBranch || "main"
|
|
14341
|
+
config.mainBranch || "main",
|
|
14342
|
+
"card"
|
|
14303
14343
|
);
|
|
14304
|
-
session.workspacePath =
|
|
14344
|
+
session.workspacePath = path17;
|
|
14305
14345
|
await setGitIdentity(
|
|
14306
14346
|
this.core.target(),
|
|
14307
14347
|
config.gitName,
|
|
14308
14348
|
config.gitEmail,
|
|
14309
|
-
|
|
14349
|
+
path17
|
|
14310
14350
|
);
|
|
14311
14351
|
await this.putCard(session);
|
|
14312
14352
|
const patchDir = `${this.core.target().paths.root}/patches/${session.taskId}`;
|
|
@@ -14733,19 +14773,19 @@ var WorkspaceWatcher = class {
|
|
|
14733
14773
|
}
|
|
14734
14774
|
record(kind, relPath) {
|
|
14735
14775
|
if (this.closed) return;
|
|
14736
|
-
const
|
|
14737
|
-
const expiry = this.selfWrites.get(
|
|
14776
|
+
const path17 = "/" + relPath;
|
|
14777
|
+
const expiry = this.selfWrites.get(path17);
|
|
14738
14778
|
if (expiry !== void 0) {
|
|
14739
14779
|
if (expiry > Date.now()) return;
|
|
14740
|
-
this.selfWrites.delete(
|
|
14780
|
+
this.selfWrites.delete(path17);
|
|
14741
14781
|
}
|
|
14742
14782
|
const { added, changed, removed } = this.pending;
|
|
14743
|
-
added.delete(
|
|
14744
|
-
changed.delete(
|
|
14745
|
-
removed.delete(
|
|
14746
|
-
if (kind === "added") added.add(
|
|
14747
|
-
else if (kind === "changed") changed.add(
|
|
14748
|
-
else removed.add(
|
|
14783
|
+
added.delete(path17);
|
|
14784
|
+
changed.delete(path17);
|
|
14785
|
+
removed.delete(path17);
|
|
14786
|
+
if (kind === "added") added.add(path17);
|
|
14787
|
+
else if (kind === "changed") changed.add(path17);
|
|
14788
|
+
else removed.add(path17);
|
|
14749
14789
|
if (added.size + changed.size + removed.size > MAX_BATCH) {
|
|
14750
14790
|
this.pending = { ...emptyPending(), resync: true };
|
|
14751
14791
|
}
|
|
@@ -14764,8 +14804,8 @@ var WorkspaceWatcher = class {
|
|
|
14764
14804
|
this.pending = emptyPending();
|
|
14765
14805
|
if (!resync && !added.size && !changed.size && !removed.size) return;
|
|
14766
14806
|
const now = Date.now();
|
|
14767
|
-
for (const [
|
|
14768
|
-
if (expiry <= now) this.selfWrites.delete(
|
|
14807
|
+
for (const [path17, expiry] of this.selfWrites) {
|
|
14808
|
+
if (expiry <= now) this.selfWrites.delete(path17);
|
|
14769
14809
|
}
|
|
14770
14810
|
this.emit({
|
|
14771
14811
|
added: resync ? [] : [...added],
|
|
@@ -14850,8 +14890,8 @@ var TerminalModeEngine = class {
|
|
|
14850
14890
|
/** Suppress the change event our own write is about to produce, so a save
|
|
14851
14891
|
* never comes back to the editor as an outside edit. Normalized to the form
|
|
14852
14892
|
* the watcher emits, since callers spell the path either way. */
|
|
14853
|
-
expectWrite(postId,
|
|
14854
|
-
this.watchers.get(postId)?.expectWrite(posixNormalize(
|
|
14893
|
+
expectWrite(postId, path17) {
|
|
14894
|
+
this.watchers.get(postId)?.expectWrite(posixNormalize(path17));
|
|
14855
14895
|
}
|
|
14856
14896
|
// ── Lifecycle / ModeEngine ──
|
|
14857
14897
|
start(base, args) {
|
|
@@ -14923,8 +14963,32 @@ var TerminalModeEngine = class {
|
|
|
14923
14963
|
this.reservedNames.clear();
|
|
14924
14964
|
this.sessions.clear();
|
|
14925
14965
|
}
|
|
14926
|
-
|
|
14927
|
-
|
|
14966
|
+
/** Every tmux session this post owns, by the `terminal-<postId>-<tab>` name
|
|
14967
|
+
* tmuxName() builds. */
|
|
14968
|
+
async terminalSessionNames(target, postId) {
|
|
14969
|
+
const prefix = `terminal-${postId.replace(/[^a-zA-Z0-9._-]/g, "_")}-`;
|
|
14970
|
+
const stdout = await target.sh(
|
|
14971
|
+
`${tmuxCli(target)} list-sessions -F '#{session_name}' 2>/dev/null || true`
|
|
14972
|
+
).catch(() => "");
|
|
14973
|
+
return stdout.split("\n").map((line) => line.trim()).filter((name) => name.startsWith(prefix));
|
|
14974
|
+
}
|
|
14975
|
+
/** Kill the post's terminals and everything running inside them. Killing the
|
|
14976
|
+
* tmux session alone leaves a process that reparented away from the shell,
|
|
14977
|
+
* so each pane's process tree is killed first. */
|
|
14978
|
+
async killTerminals(postId) {
|
|
14979
|
+
const target = this.core.target();
|
|
14980
|
+
const names = await this.terminalSessionNames(target, postId);
|
|
14981
|
+
for (const name of names) {
|
|
14982
|
+
await target.sh(
|
|
14983
|
+
`for p in $(${tmuxCli(target)} list-panes -t ${name} -F '#{pane_pid}' 2>/dev/null); do pkill -TERM -P "$p" 2>/dev/null || true; done; ${tmuxCli(target)} kill-session -t ${name} 2>/dev/null || true`
|
|
14984
|
+
).catch(() => {
|
|
14985
|
+
});
|
|
14986
|
+
}
|
|
14987
|
+
}
|
|
14988
|
+
async teardown(session) {
|
|
14989
|
+
await this.killTerminals(session.postId).catch(() => {
|
|
14990
|
+
});
|
|
14991
|
+
await teardownSession(session, this.core.target());
|
|
14928
14992
|
}
|
|
14929
14993
|
putCard(s) {
|
|
14930
14994
|
return putSessionCard(s, this.core.target());
|
|
@@ -15025,13 +15089,18 @@ var TerminalModeEngine = class {
|
|
|
15025
15089
|
this.reservedNames.values()
|
|
15026
15090
|
);
|
|
15027
15091
|
this.reservedNames.set(session.postId, workName);
|
|
15028
|
-
const { path:
|
|
15029
|
-
|
|
15092
|
+
const { path: path17 } = await addWorktree(
|
|
15093
|
+
this.core.target(),
|
|
15094
|
+
workName,
|
|
15095
|
+
branch,
|
|
15096
|
+
"terminal"
|
|
15097
|
+
);
|
|
15098
|
+
session.workspacePath = path17;
|
|
15030
15099
|
await setGitIdentity(
|
|
15031
15100
|
this.core.target(),
|
|
15032
15101
|
config.gitName,
|
|
15033
15102
|
config.gitEmail,
|
|
15034
|
-
|
|
15103
|
+
path17
|
|
15035
15104
|
);
|
|
15036
15105
|
this.emitter.addLog(session, "system", "Worktree ready.");
|
|
15037
15106
|
} else {
|
|
@@ -15093,11 +15162,11 @@ var TerminalModeEngine = class {
|
|
|
15093
15162
|
}
|
|
15094
15163
|
async bareFsList({
|
|
15095
15164
|
postId,
|
|
15096
|
-
path:
|
|
15165
|
+
path: path17
|
|
15097
15166
|
}) {
|
|
15098
15167
|
const target = this.getBareTarget(postId);
|
|
15099
15168
|
const root = this.getBareRoot(postId);
|
|
15100
|
-
const abs = this.resolveBarePath(
|
|
15169
|
+
const abs = this.resolveBarePath(path17, root);
|
|
15101
15170
|
const relRoot = abs.slice(root.length) || "/";
|
|
15102
15171
|
const out = await target.sh(`cd ${shEscape(abs)} && for f in .* *; do
|
|
15103
15172
|
case "$f" in
|
|
@@ -15143,32 +15212,32 @@ var TerminalModeEngine = class {
|
|
|
15143
15212
|
}
|
|
15144
15213
|
async bareFsRead({
|
|
15145
15214
|
postId,
|
|
15146
|
-
path:
|
|
15215
|
+
path: path17
|
|
15147
15216
|
}) {
|
|
15148
15217
|
const target = this.getBareTarget(postId);
|
|
15149
|
-
const abs = this.resolveBarePath(
|
|
15218
|
+
const abs = this.resolveBarePath(path17, this.getBareRoot(postId));
|
|
15150
15219
|
const b64 = (await target.sh(`base64 < ${shEscape(abs)} | tr -d '
|
|
15151
15220
|
'`)).trim();
|
|
15152
15221
|
return { content: b64, encoding: "base64" };
|
|
15153
15222
|
}
|
|
15154
15223
|
async bareFsWrite({
|
|
15155
15224
|
postId,
|
|
15156
|
-
path:
|
|
15225
|
+
path: path17,
|
|
15157
15226
|
content,
|
|
15158
15227
|
encoding
|
|
15159
15228
|
}) {
|
|
15160
15229
|
const target = this.getBareTarget(postId);
|
|
15161
|
-
const abs = this.resolveBarePath(
|
|
15230
|
+
const abs = this.resolveBarePath(path17, this.getBareRoot(postId));
|
|
15162
15231
|
const b64 = encoding === "base64" ? content : Buffer.from(content, "utf-8").toString("base64");
|
|
15163
15232
|
if (!/^[A-Za-z0-9+/=\n\r]*$/.test(b64)) {
|
|
15164
15233
|
throw new Error("Invalid base64 payload.");
|
|
15165
15234
|
}
|
|
15166
|
-
this.expectWrite(String(postId),
|
|
15235
|
+
this.expectWrite(String(postId), path17);
|
|
15167
15236
|
await target.sh(
|
|
15168
15237
|
`mkdir -p ${shEscape(posixDirname(abs))} && printf %s ${shEscape(b64)} | base64 -d > ${shEscape(abs)}`
|
|
15169
15238
|
);
|
|
15170
15239
|
const session = this.sessions.get(String(postId));
|
|
15171
|
-
if (session) delete session.buffers[
|
|
15240
|
+
if (session) delete session.buffers[path17];
|
|
15172
15241
|
}
|
|
15173
15242
|
/** Write a dropped image to the per-space drops dir (`paths.root`, OUTSIDE the
|
|
15174
15243
|
* card worktree so it never touches the repo) and return its absolute path. */
|
|
@@ -15213,39 +15282,39 @@ var TerminalModeEngine = class {
|
|
|
15213
15282
|
}
|
|
15214
15283
|
bareOpenFile({
|
|
15215
15284
|
postId: rawPostId,
|
|
15216
|
-
path:
|
|
15285
|
+
path: path17
|
|
15217
15286
|
}) {
|
|
15218
15287
|
const session = this.sessions.get(String(rawPostId));
|
|
15219
15288
|
if (!session || session.mode !== "bare") return;
|
|
15220
|
-
if (!session.openedFiles.includes(
|
|
15221
|
-
session.openedFiles.push(
|
|
15289
|
+
if (!session.openedFiles.includes(path17)) {
|
|
15290
|
+
session.openedFiles.push(path17);
|
|
15222
15291
|
}
|
|
15223
15292
|
}
|
|
15224
15293
|
bareCloseFile({
|
|
15225
15294
|
postId: rawPostId,
|
|
15226
|
-
path:
|
|
15295
|
+
path: path17
|
|
15227
15296
|
}) {
|
|
15228
15297
|
const session = this.sessions.get(String(rawPostId));
|
|
15229
15298
|
if (!session || session.mode !== "bare") return;
|
|
15230
|
-
session.openedFiles = session.openedFiles.filter((p) => p !==
|
|
15231
|
-
delete session.buffers[
|
|
15299
|
+
session.openedFiles = session.openedFiles.filter((p) => p !== path17);
|
|
15300
|
+
delete session.buffers[path17];
|
|
15232
15301
|
}
|
|
15233
15302
|
bareBufferSet({
|
|
15234
15303
|
postId: rawPostId,
|
|
15235
|
-
path:
|
|
15304
|
+
path: path17,
|
|
15236
15305
|
content
|
|
15237
15306
|
}) {
|
|
15238
15307
|
const session = this.sessions.get(String(rawPostId));
|
|
15239
15308
|
if (!session || session.mode !== "bare") return;
|
|
15240
|
-
session.buffers[
|
|
15309
|
+
session.buffers[path17] = content;
|
|
15241
15310
|
}
|
|
15242
15311
|
async bareFsDelete({
|
|
15243
15312
|
postId,
|
|
15244
|
-
path:
|
|
15313
|
+
path: path17
|
|
15245
15314
|
}) {
|
|
15246
15315
|
const target = this.getBareTarget(postId);
|
|
15247
15316
|
const root = this.getBareRoot(postId);
|
|
15248
|
-
const abs = this.resolveBarePath(
|
|
15317
|
+
const abs = this.resolveBarePath(path17, root);
|
|
15249
15318
|
if (abs === root) {
|
|
15250
15319
|
throw new Error("Refusing to delete the repo root.");
|
|
15251
15320
|
}
|
|
@@ -15253,10 +15322,10 @@ var TerminalModeEngine = class {
|
|
|
15253
15322
|
}
|
|
15254
15323
|
async bareFsMkdir({
|
|
15255
15324
|
postId,
|
|
15256
|
-
path:
|
|
15325
|
+
path: path17
|
|
15257
15326
|
}) {
|
|
15258
15327
|
const target = this.getBareTarget(postId);
|
|
15259
|
-
const abs = this.resolveBarePath(
|
|
15328
|
+
const abs = this.resolveBarePath(path17, this.getBareRoot(postId));
|
|
15260
15329
|
await target.sh(`mkdir -p ${shEscape(abs)}`);
|
|
15261
15330
|
}
|
|
15262
15331
|
async bareReview({
|
|
@@ -15825,8 +15894,8 @@ async function pollGithubDeviceCode(clientId, deviceCode) {
|
|
|
15825
15894
|
};
|
|
15826
15895
|
}
|
|
15827
15896
|
}
|
|
15828
|
-
async function githubApi(token,
|
|
15829
|
-
const res = await fetch(`${API_BASE}${
|
|
15897
|
+
async function githubApi(token, path17) {
|
|
15898
|
+
const res = await fetch(`${API_BASE}${path17}`, {
|
|
15830
15899
|
headers: {
|
|
15831
15900
|
Authorization: `Bearer ${token}`,
|
|
15832
15901
|
Accept: "application/vnd.github+json",
|
|
@@ -15835,7 +15904,7 @@ async function githubApi(token, path16) {
|
|
|
15835
15904
|
});
|
|
15836
15905
|
if (!res.ok) {
|
|
15837
15906
|
throw new Error(
|
|
15838
|
-
`GitHub ${
|
|
15907
|
+
`GitHub ${path17} failed (${res.status}): ${await res.text()}`
|
|
15839
15908
|
);
|
|
15840
15909
|
}
|
|
15841
15910
|
return await res.json();
|
|
@@ -15911,7 +15980,39 @@ async function hostGit(cwd, ...args) {
|
|
|
15911
15980
|
});
|
|
15912
15981
|
return stdout;
|
|
15913
15982
|
}
|
|
15914
|
-
async function
|
|
15983
|
+
async function gitCatFileBatch(cwd, shas) {
|
|
15984
|
+
const out = /* @__PURE__ */ new Map();
|
|
15985
|
+
const wanted = [...new Set(shas.filter(Boolean))];
|
|
15986
|
+
if (!wanted.length) return out;
|
|
15987
|
+
const child = (0, import_child_process3.spawn)("git", [...GIT_NO_HELPER, "cat-file", "--batch"], {
|
|
15988
|
+
cwd,
|
|
15989
|
+
env: GIT_ENV
|
|
15990
|
+
});
|
|
15991
|
+
const chunks = [];
|
|
15992
|
+
child.stdout.on("data", (c) => chunks.push(c));
|
|
15993
|
+
const done = new Promise((resolve2) => {
|
|
15994
|
+
child.on("close", () => resolve2());
|
|
15995
|
+
child.on("error", () => resolve2());
|
|
15996
|
+
});
|
|
15997
|
+
child.stdin.end(`${wanted.join("\n")}
|
|
15998
|
+
`);
|
|
15999
|
+
await done;
|
|
16000
|
+
let buf = Buffer.concat(chunks);
|
|
16001
|
+
while (buf.length) {
|
|
16002
|
+
const nl = buf.indexOf(10);
|
|
16003
|
+
if (nl === -1) break;
|
|
16004
|
+
const header = buf.subarray(0, nl).toString("utf-8");
|
|
16005
|
+
buf = buf.subarray(nl + 1);
|
|
16006
|
+
const [sha, type, sizeText] = header.split(" ");
|
|
16007
|
+
if (type === void 0 || sizeText === void 0) continue;
|
|
16008
|
+
const size = Number(sizeText);
|
|
16009
|
+
if (!Number.isFinite(size)) break;
|
|
16010
|
+
out.set(sha, buf.subarray(0, size).toString("utf-8"));
|
|
16011
|
+
buf = buf.subarray(size + 1);
|
|
16012
|
+
}
|
|
16013
|
+
return out;
|
|
16014
|
+
}
|
|
16015
|
+
async function ensureWorkspaceClone(opts) {
|
|
15915
16016
|
const workspace = import_path5.default.join(opts.spaceDir, "workspace");
|
|
15916
16017
|
const branch = (opts.mainBranch || "main").replace(/[^\w./-]/g, "");
|
|
15917
16018
|
const url2 = buildCloneUrl(opts.repoUrl, opts.githubToken);
|
|
@@ -15920,6 +16021,15 @@ async function checkDeployStatus(opts) {
|
|
|
15920
16021
|
await hostGit(workspace, "rev-parse", "--git-dir");
|
|
15921
16022
|
await hostGit(workspace, "remote", "set-url", "origin", url2);
|
|
15922
16023
|
} catch {
|
|
16024
|
+
const leftovers = await import_promises3.default.readdir(workspace).catch(() => []);
|
|
16025
|
+
if (leftovers.includes(".git")) {
|
|
16026
|
+
throw new Error(
|
|
16027
|
+
`${workspace} holds a git repository that cannot be read. Remove it and retry.`
|
|
16028
|
+
);
|
|
16029
|
+
}
|
|
16030
|
+
for (const name of leftovers) {
|
|
16031
|
+
await import_promises3.default.rm(import_path5.default.join(workspace, name), { recursive: true, force: true });
|
|
16032
|
+
}
|
|
15923
16033
|
await execFileAsync2(
|
|
15924
16034
|
"git",
|
|
15925
16035
|
[...GIT_NO_HELPER, "clone", "--branch", branch, url2, workspace],
|
|
@@ -15928,6 +16038,10 @@ async function checkDeployStatus(opts) {
|
|
|
15928
16038
|
cloned = true;
|
|
15929
16039
|
}
|
|
15930
16040
|
if (!cloned) await hostGit(workspace, "fetch", "origin", branch);
|
|
16041
|
+
return { workspace, branch };
|
|
16042
|
+
}
|
|
16043
|
+
async function checkDeployStatus(opts) {
|
|
16044
|
+
const { workspace, branch } = await ensureWorkspaceClone(opts);
|
|
15931
16045
|
const has = async (refPath) => {
|
|
15932
16046
|
try {
|
|
15933
16047
|
await hostGit(workspace, "cat-file", "-e", refPath);
|
|
@@ -16621,6 +16735,1151 @@ ${clean}`;
|
|
|
16621
16735
|
}
|
|
16622
16736
|
}
|
|
16623
16737
|
|
|
16738
|
+
// ../../shared/all/helpers/board-agent-core/host-skills.ts
|
|
16739
|
+
var import_promises5 = __toESM(require("fs/promises"));
|
|
16740
|
+
var import_os2 = __toESM(require("os"));
|
|
16741
|
+
var import_path8 = __toESM(require("path"));
|
|
16742
|
+
|
|
16743
|
+
// ../../shared/all/domains/posts.ts
|
|
16744
|
+
var postDataSort = /* @__PURE__ */ ((postDataSort2) => {
|
|
16745
|
+
postDataSort2["DAILY"] = "DAILY";
|
|
16746
|
+
postDataSort2["WEEKLY"] = "WEEKLY";
|
|
16747
|
+
postDataSort2["MONTHLY"] = "MONTHLY";
|
|
16748
|
+
return postDataSort2;
|
|
16749
|
+
})(postDataSort || {});
|
|
16750
|
+
|
|
16751
|
+
// ../../shared/all/domains/user.ts
|
|
16752
|
+
var USER_TAG_VALUES = [
|
|
16753
|
+
"HUMAN",
|
|
16754
|
+
"BOT",
|
|
16755
|
+
"GOVERNMENT",
|
|
16756
|
+
"ACADEMIA",
|
|
16757
|
+
"BUSINESS",
|
|
16758
|
+
"NEW",
|
|
16759
|
+
"WAITLIST",
|
|
16760
|
+
"VERIFIED"
|
|
16761
|
+
];
|
|
16762
|
+
var detailsCookieSchema = external_exports.object({
|
|
16763
|
+
userId: external_exports.number(),
|
|
16764
|
+
updatedAt: external_exports.string(),
|
|
16765
|
+
username: external_exports.string(),
|
|
16766
|
+
name: external_exports.string().nullable(),
|
|
16767
|
+
email: external_exports.string().nullable(),
|
|
16768
|
+
tag: external_exports.enum(USER_TAG_VALUES),
|
|
16769
|
+
avatarKey: external_exports.string().nullable(),
|
|
16770
|
+
robohash: external_exports.string().nullable(),
|
|
16771
|
+
hasPassword: external_exports.boolean(),
|
|
16772
|
+
// Defaulted so a client cookie minted before these fields existed still parses
|
|
16773
|
+
// (the server re-issues it with real values on the next refresh) instead of
|
|
16774
|
+
// failing the whole payload and flashing the user as logged out.
|
|
16775
|
+
hasPasskey: external_exports.boolean().default(false),
|
|
16776
|
+
twoFaEnabled: external_exports.boolean(),
|
|
16777
|
+
oauthProviders: external_exports.array(external_exports.enum(["GOOGLE", "APPLE"])).default([]),
|
|
16778
|
+
// Defaulted, not just nullable: a required-but-missing key is what breaks old
|
|
16779
|
+
// clients, so this stays parseable once the server stops sending it.
|
|
16780
|
+
oauthProvider: external_exports.enum(["GOOGLE", "APPLE"]).nullable().default(null),
|
|
16781
|
+
emailVerificationStatus: external_exports.enum(["UNVERIFIED", "PENDING", "VERIFIED"]),
|
|
16782
|
+
isPrivate: external_exports.boolean(),
|
|
16783
|
+
isIncognito: external_exports.boolean(),
|
|
16784
|
+
onboardingPath: external_exports.string().nullable(),
|
|
16785
|
+
bio: external_exports.string().nullable(),
|
|
16786
|
+
bannerKey: external_exports.string().nullable(),
|
|
16787
|
+
tronsBalance: external_exports.number(),
|
|
16788
|
+
verificationRequestedAt: external_exports.string().nullable(),
|
|
16789
|
+
deletionScheduledAt: external_exports.string().nullable()
|
|
16790
|
+
});
|
|
16791
|
+
var UserType = /* @__PURE__ */ ((UserType2) => {
|
|
16792
|
+
UserType2["PRIVATE"] = "PRIVATE";
|
|
16793
|
+
UserType2["INCOGNITO"] = "INCOGNITO";
|
|
16794
|
+
UserType2["PREMIUM"] = "PREMIUM";
|
|
16795
|
+
UserType2["PAID"] = "PAID";
|
|
16796
|
+
UserType2["INVITE"] = "INVITE";
|
|
16797
|
+
return UserType2;
|
|
16798
|
+
})(UserType || {});
|
|
16799
|
+
|
|
16800
|
+
// ../../shared/all/uploadTypes.ts
|
|
16801
|
+
var _EXT_TO_CONTENT_TYPE = {
|
|
16802
|
+
jpg: "image/jpeg",
|
|
16803
|
+
jpeg: "image/jpeg",
|
|
16804
|
+
png: "image/png",
|
|
16805
|
+
gif: "image/gif",
|
|
16806
|
+
webp: "image/webp",
|
|
16807
|
+
svg: "image/svg+xml",
|
|
16808
|
+
heic: "image/heic",
|
|
16809
|
+
heif: "image/heif",
|
|
16810
|
+
mp4: "video/mp4",
|
|
16811
|
+
mov: "video/quicktime",
|
|
16812
|
+
mpeg: "video/mpeg",
|
|
16813
|
+
pdf: "application/pdf",
|
|
16814
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
16815
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
16816
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
16817
|
+
};
|
|
16818
|
+
var EXT_TO_CONTENT_TYPE = _EXT_TO_CONTENT_TYPE;
|
|
16819
|
+
var ALLOWED_CONTENT_TYPES = [
|
|
16820
|
+
...new Set(Object.values(EXT_TO_CONTENT_TYPE))
|
|
16821
|
+
];
|
|
16822
|
+
var ALLOWED_CONTENT_TYPES_TUPLE = ALLOWED_CONTENT_TYPES;
|
|
16823
|
+
var CONTENT_TYPE_TO_EXT = Object.fromEntries(
|
|
16824
|
+
Object.entries(EXT_TO_CONTENT_TYPE).map(([ext, mime]) => [mime, ext])
|
|
16825
|
+
);
|
|
16826
|
+
var DROPZONE_ACCEPT = Object.entries(
|
|
16827
|
+
EXT_TO_CONTENT_TYPE
|
|
16828
|
+
).reduce((acc, [ext, mime]) => {
|
|
16829
|
+
(acc[mime] ??= []).push(`.${ext}`);
|
|
16830
|
+
return acc;
|
|
16831
|
+
}, {});
|
|
16832
|
+
var FILE_INPUT_ACCEPT = ALLOWED_CONTENT_TYPES.join(",");
|
|
16833
|
+
|
|
16834
|
+
// ../../shared/all/validators.ts
|
|
16835
|
+
var usernameValidationRegex = /^[a-zA-Z0-9_]+$/;
|
|
16836
|
+
var signupSchema = external_exports.object({
|
|
16837
|
+
username: external_exports.string().min(1).regex(usernameValidationRegex, {
|
|
16838
|
+
message: "Username can only contain letters, numbers, and underscores"
|
|
16839
|
+
}),
|
|
16840
|
+
email: external_exports.string().email(),
|
|
16841
|
+
password: external_exports.string().min(8, { message: "Password must contain at least 8 characters" }),
|
|
16842
|
+
types: external_exports.array(external_exports.nativeEnum(UserType)),
|
|
16843
|
+
referrerUserId: external_exports.number().optional(),
|
|
16844
|
+
instanceId: external_exports.number()
|
|
16845
|
+
});
|
|
16846
|
+
var usernameSchema = external_exports.string().min(1).regex(usernameValidationRegex, {
|
|
16847
|
+
message: "Username can only contain letters, numbers, and underscores"
|
|
16848
|
+
});
|
|
16849
|
+
var baseRegisterSchema = external_exports.object({
|
|
16850
|
+
username: usernameSchema,
|
|
16851
|
+
types: external_exports.array(external_exports.nativeEnum(UserType)).default([]),
|
|
16852
|
+
referrerUserId: external_exports.number().optional(),
|
|
16853
|
+
instanceId: external_exports.number(),
|
|
16854
|
+
platform: external_exports.enum(["web", "mobile"]).optional()
|
|
16855
|
+
});
|
|
16856
|
+
var passwordRegisterSchema = baseRegisterSchema.extend({
|
|
16857
|
+
password: external_exports.string().min(8, { message: "Password must contain at least 8 characters" }),
|
|
16858
|
+
recaptchaToken: external_exports.string().optional()
|
|
16859
|
+
});
|
|
16860
|
+
var createSourceSchema = external_exports.object({
|
|
16861
|
+
name: external_exports.string().min(1),
|
|
16862
|
+
description: external_exports.string().optional(),
|
|
16863
|
+
requirements: external_exports.array(
|
|
16864
|
+
external_exports.enum([
|
|
16865
|
+
"GOVERNMENT_SOURCE" /* GOVERNMENT_SOURCE */,
|
|
16866
|
+
"ENTERPRISE_SOURCE" /* ENTERPRISE_SOURCE */,
|
|
16867
|
+
"ANONYMOUS_SOURCE" /* ANONYMOUS_SOURCE */,
|
|
16868
|
+
"HUMAN_SOURCE" /* HUMAN_SOURCE */
|
|
16869
|
+
])
|
|
16870
|
+
)
|
|
16871
|
+
});
|
|
16872
|
+
var contactFormSchema = external_exports.object({
|
|
16873
|
+
name: external_exports.string().min(1).max(100),
|
|
16874
|
+
email: external_exports.string().max(254).optional(),
|
|
16875
|
+
phone: external_exports.string().max(20).optional(),
|
|
16876
|
+
message: external_exports.string().min(1).max(2e3),
|
|
16877
|
+
age: external_exports.string().optional()
|
|
16878
|
+
}).refine((data) => !!data.email || !!data.phone, {
|
|
16879
|
+
message: "Either email or phone is required"
|
|
16880
|
+
});
|
|
16881
|
+
var donationSchema = external_exports.object({
|
|
16882
|
+
amount: external_exports.number().int().min(100).max(1e6),
|
|
16883
|
+
// Amount in cents (integer)
|
|
16884
|
+
email: external_exports.string().email()
|
|
16885
|
+
});
|
|
16886
|
+
var getUploadUrlsSchema = external_exports.object({
|
|
16887
|
+
contentType: external_exports.enum(ALLOWED_CONTENT_TYPES_TUPLE),
|
|
16888
|
+
name: external_exports.string().optional(),
|
|
16889
|
+
width: external_exports.number().int().positive().max(25e3).optional(),
|
|
16890
|
+
height: external_exports.number().int().positive().max(25e3).optional(),
|
|
16891
|
+
isPrivate: external_exports.boolean().optional().default(false),
|
|
16892
|
+
// Chat attachments pass the conversation instead of an isPrivate flag: the
|
|
16893
|
+
// server resolves privacy from the channel's space so the client can't
|
|
16894
|
+
// decide to make a PRIVATE space's files public. See uploads.getUploadUrls.
|
|
16895
|
+
conversationId: external_exports.string().uuid().optional(),
|
|
16896
|
+
isFactiii: external_exports.boolean().default(false),
|
|
16897
|
+
factiiiId: external_exports.number().int().positive().optional(),
|
|
16898
|
+
rawSha256: external_exports.string().regex(/^[a-f0-9]{64}$/i, "rawSha256 must be 64 hex chars")
|
|
16899
|
+
}).refine(
|
|
16900
|
+
(d) => d.width === void 0 && d.height === void 0 || d.width !== void 0 && d.height !== void 0,
|
|
16901
|
+
{ message: "width and height must be provided together" }
|
|
16902
|
+
).refine((d) => !d.isFactiii || d.factiiiId !== void 0, {
|
|
16903
|
+
message: "factiiiId is required when isFactiii is true"
|
|
16904
|
+
});
|
|
16905
|
+
var finalizeUploadSchema = external_exports.object({
|
|
16906
|
+
uploadId: external_exports.string(),
|
|
16907
|
+
isFactiii: external_exports.boolean(),
|
|
16908
|
+
raw: external_exports.object({
|
|
16909
|
+
// Bare-uuid keys; variant kind is the DB-level discriminator, no S3 prefix.
|
|
16910
|
+
key: external_exports.string().min(1),
|
|
16911
|
+
size: external_exports.number().int().positive(),
|
|
16912
|
+
sha256: external_exports.string().regex(/^[a-f0-9]{64}$/i),
|
|
16913
|
+
width: external_exports.number().int().positive().optional(),
|
|
16914
|
+
height: external_exports.number().int().positive().optional()
|
|
16915
|
+
}),
|
|
16916
|
+
view: external_exports.object({
|
|
16917
|
+
key: external_exports.string().min(1),
|
|
16918
|
+
size: external_exports.number().int().positive(),
|
|
16919
|
+
sha256: external_exports.string().regex(/^[a-f0-9]{64}$/i),
|
|
16920
|
+
width: external_exports.number().int().positive(),
|
|
16921
|
+
height: external_exports.number().int().positive()
|
|
16922
|
+
}).nullable()
|
|
16923
|
+
// null for non-image uploads
|
|
16924
|
+
});
|
|
16925
|
+
var variantKindSchema = external_exports.enum([
|
|
16926
|
+
"RAW",
|
|
16927
|
+
"VIEW",
|
|
16928
|
+
"PREMIUM_HD",
|
|
16929
|
+
"PREMIUM_RAW",
|
|
16930
|
+
"THUMBNAIL"
|
|
16931
|
+
]);
|
|
16932
|
+
var adminUploadsGetVariantUrlSchema = external_exports.object({
|
|
16933
|
+
uploadId: external_exports.string(),
|
|
16934
|
+
kind: variantKindSchema,
|
|
16935
|
+
expiresIn: external_exports.number().int().positive().max(3600).default(300)
|
|
16936
|
+
});
|
|
16937
|
+
var getUploadSchema = external_exports.object({
|
|
16938
|
+
id: external_exports.string()
|
|
16939
|
+
});
|
|
16940
|
+
var priceSchema = external_exports.number().int().min(0, "Price must be a positive integer in cents");
|
|
16941
|
+
var checkPurchaseSchema = external_exports.object({
|
|
16942
|
+
productId: external_exports.string(),
|
|
16943
|
+
type: external_exports.enum(["STRIPE", "GOOGLE", "APPLE"]),
|
|
16944
|
+
receivedPrice: external_exports.number().int().min(0, "Received price must be a positive integer in cents"),
|
|
16945
|
+
currency: external_exports.string().length(3).default("USD")
|
|
16946
|
+
});
|
|
16947
|
+
var creditPurchaseSchema = external_exports.object({
|
|
16948
|
+
orderId: external_exports.string(),
|
|
16949
|
+
transactionDate: external_exports.number(),
|
|
16950
|
+
receivedPrice: external_exports.number().int().min(0, "Received price must be a positive integer in cents"),
|
|
16951
|
+
type: external_exports.enum(["STRIPE", "GOOGLE", "APPLE"]),
|
|
16952
|
+
secret: external_exports.string(),
|
|
16953
|
+
// Required for APPLE/GOOGLE IAP — used for server-side receipt verification
|
|
16954
|
+
purchaseToken: external_exports.string().optional()
|
|
16955
|
+
});
|
|
16956
|
+
var dsarFormSchema = external_exports.object({
|
|
16957
|
+
name: external_exports.string().trim().min(1, "Please enter your name.").max(100, "Please keep your name under 100 characters."),
|
|
16958
|
+
email: external_exports.string().trim().min(1, "Please enter your email address.").email("Please enter a valid email address."),
|
|
16959
|
+
selectedAs: external_exports.enum(["person", "agent"]),
|
|
16960
|
+
requestUnder: external_exports.enum(["GDPR", "CCPA", "OTHER"]),
|
|
16961
|
+
selectedTo: external_exports.enum(["knowledge", "delete", "optOut", "optIn", "other"]),
|
|
16962
|
+
details: external_exports.string().trim().min(1, "Please provide details about your request.").max(5e3, "Please keep details under 5000 characters."),
|
|
16963
|
+
confirmAccuracy: external_exports.boolean().refine((value2) => value2, "Please confirm your information is accurate."),
|
|
16964
|
+
confirmConsequences: external_exports.boolean().refine(
|
|
16965
|
+
(value2) => value2,
|
|
16966
|
+
"Please acknowledge the deletion/restriction consequences."
|
|
16967
|
+
),
|
|
16968
|
+
confirmVerification: external_exports.boolean().refine((value2) => value2, "Please acknowledge the email verification step.")
|
|
16969
|
+
});
|
|
16970
|
+
var mapLinkSchema = external_exports.object({
|
|
16971
|
+
startPosition: external_exports.object({
|
|
16972
|
+
postId: external_exports.string().optional(),
|
|
16973
|
+
latitude: external_exports.number().min(-90).max(90, { message: "Latitude must be between -90 and 90" }),
|
|
16974
|
+
longitude: external_exports.number().min(-180).max(180, { message: "Longitude must be between -180 and 180" })
|
|
16975
|
+
}),
|
|
16976
|
+
pins: external_exports.array(
|
|
16977
|
+
external_exports.object({
|
|
16978
|
+
postId: external_exports.string().uuid({ message: "Pin ID must be a valid UUID" }),
|
|
16979
|
+
latitude: external_exports.number().min(-90).max(90, { message: "Pin latitude must be between -90 and 90" }),
|
|
16980
|
+
longitude: external_exports.number().min(-180).max(180, { message: "Pin longitude must be between -180 and 180" })
|
|
16981
|
+
})
|
|
16982
|
+
).optional().default([])
|
|
16983
|
+
});
|
|
16984
|
+
var mapLinkPostIds = mapLinkSchema.transform((data) => {
|
|
16985
|
+
return {
|
|
16986
|
+
startPosition: {
|
|
16987
|
+
postId: data.startPosition.postId,
|
|
16988
|
+
latitude: data.startPosition.latitude,
|
|
16989
|
+
longitude: data.startPosition.longitude
|
|
16990
|
+
},
|
|
16991
|
+
pins: data.pins.map((pin) => ({
|
|
16992
|
+
postId: pin.postId,
|
|
16993
|
+
latitude: pin.latitude,
|
|
16994
|
+
longitude: pin.longitude
|
|
16995
|
+
}))
|
|
16996
|
+
};
|
|
16997
|
+
});
|
|
16998
|
+
var mapLinkHref = mapLinkSchema.transform((data) => {
|
|
16999
|
+
const latParam = `latitude=${encodeURIComponent(data.startPosition.latitude)}`;
|
|
17000
|
+
const lonParam = `longitude=${encodeURIComponent(
|
|
17001
|
+
data.startPosition.longitude
|
|
17002
|
+
)}`;
|
|
17003
|
+
const pinsParam = data.pins.length > 0 ? `pins=${encodeURIComponent(JSON.stringify(data.pins))}` : "";
|
|
17004
|
+
return {
|
|
17005
|
+
startPosition: {
|
|
17006
|
+
postId: data.startPosition.postId,
|
|
17007
|
+
latitude: data.startPosition.latitude,
|
|
17008
|
+
longitude: data.startPosition.longitude
|
|
17009
|
+
},
|
|
17010
|
+
href: `${data.startPosition.postId ? `postId=${data.startPosition.postId}&` : ""}${latParam}&${lonParam}${pinsParam ? `&${pinsParam}` : ""}`
|
|
17011
|
+
};
|
|
17012
|
+
});
|
|
17013
|
+
var wikipediaUrlSchema = external_exports.string().refine(
|
|
17014
|
+
(url2) => {
|
|
17015
|
+
try {
|
|
17016
|
+
const parsedUrl = new URL(url2);
|
|
17017
|
+
return parsedUrl.hostname.endsWith(".wikipedia.org") && parsedUrl.protocol === "https:" && parsedUrl.pathname.startsWith("/wiki/") && parsedUrl.pathname.length > 6;
|
|
17018
|
+
} catch {
|
|
17019
|
+
return false;
|
|
17020
|
+
}
|
|
17021
|
+
},
|
|
17022
|
+
{
|
|
17023
|
+
message: "Invalid Wikipedia URL. Must be a valid HTTPS URL from wikipedia.org with /wiki/ path and article title"
|
|
17024
|
+
}
|
|
17025
|
+
);
|
|
17026
|
+
var localPreferenceSchema = external_exports.object({
|
|
17027
|
+
postFilterType: external_exports.enum(["trending", "new", "popular"]),
|
|
17028
|
+
postSortBy: external_exports.nativeEnum(postDataSort),
|
|
17029
|
+
filterBot: external_exports.boolean(),
|
|
17030
|
+
filterNew: external_exports.boolean(),
|
|
17031
|
+
showBoards: external_exports.boolean(),
|
|
17032
|
+
displayMode: external_exports.enum(["data", "posts", "sortByFactiii", "mediaSearch"]),
|
|
17033
|
+
postModeType: external_exports.enum(["home", "explore"]),
|
|
17034
|
+
muteVideo: external_exports.boolean(),
|
|
17035
|
+
theme: external_exports.enum(["dark", "light"]),
|
|
17036
|
+
selectedFactiiis: external_exports.array(external_exports.number()),
|
|
17037
|
+
dataFilter: external_exports.string(),
|
|
17038
|
+
mediaSearch: external_exports.string()
|
|
17039
|
+
});
|
|
17040
|
+
var localPreferenceUrlParamsSchema = external_exports.object({
|
|
17041
|
+
postFilterType: external_exports.enum(["trending", "new", "popular"]).optional(),
|
|
17042
|
+
postSortBy: external_exports.nativeEnum(postDataSort).optional(),
|
|
17043
|
+
filterBot: external_exports.preprocess((val) => val === "true", external_exports.boolean()).optional(),
|
|
17044
|
+
showBoards: external_exports.preprocess((val) => val === "true", external_exports.boolean()).optional(),
|
|
17045
|
+
displayMode: external_exports.enum(["data", "posts", "sortByFactiii", "mediaSearch"]).optional(),
|
|
17046
|
+
postModeType: external_exports.enum(["home", "explore"]).optional(),
|
|
17047
|
+
selectedFactiiis: external_exports.preprocess((val) => {
|
|
17048
|
+
if (!val || typeof val !== "string") return [];
|
|
17049
|
+
return val.split(",").map(Number).filter((id) => !isNaN(id));
|
|
17050
|
+
}, external_exports.array(external_exports.number())).optional()
|
|
17051
|
+
});
|
|
17052
|
+
var productSchema = external_exports.object({
|
|
17053
|
+
id: external_exports.number(),
|
|
17054
|
+
title: external_exports.string(),
|
|
17055
|
+
description: external_exports.string().nullable(),
|
|
17056
|
+
price: external_exports.number(),
|
|
17057
|
+
createdAt: external_exports.date(),
|
|
17058
|
+
active: external_exports.boolean(),
|
|
17059
|
+
originalInventory: external_exports.number().optional().nullable().transform((val) => val === void 0 ? null : val),
|
|
17060
|
+
inventory: external_exports.number(),
|
|
17061
|
+
discount: external_exports.number(),
|
|
17062
|
+
type: external_exports.enum(["TRON", "MONTHLY_SUBSCRIPTION", "FOUNDERS_TOKEN", "DONATION"]),
|
|
17063
|
+
appStoreProductId: external_exports.string().nullable(),
|
|
17064
|
+
playStoreProductId: external_exports.string().nullable(),
|
|
17065
|
+
stripePriceId: external_exports.string().nullable(),
|
|
17066
|
+
spaceSlug: external_exports.string(),
|
|
17067
|
+
images: external_exports.array(external_exports.string()),
|
|
17068
|
+
// Trons granted to the buyer on purchase + premium days granted. Only meaningful
|
|
17069
|
+
// for TRON and FOUNDERS_TOKEN; other product types ignore these. Optional so the
|
|
17070
|
+
// admin form can omit them when editing subscription/donation rows.
|
|
17071
|
+
tronQuantity: external_exports.number().int().min(0).optional(),
|
|
17072
|
+
tronPremiumDays: external_exports.number().int().min(0).optional()
|
|
17073
|
+
});
|
|
17074
|
+
var adminTransferSpaceOwnershipSchema = external_exports.object({
|
|
17075
|
+
spaceId: external_exports.number(),
|
|
17076
|
+
newOwnerUserId: external_exports.number()
|
|
17077
|
+
});
|
|
17078
|
+
var baseSchema = {
|
|
17079
|
+
cursor: external_exports.union([external_exports.string(), external_exports.number()]).optional(),
|
|
17080
|
+
type: external_exports.enum(["new", "trending", "popular", "board"]).optional(),
|
|
17081
|
+
filterBots: external_exports.boolean().optional(),
|
|
17082
|
+
// Omitted = show; hiding new accounts is opt-in via the quick filter.
|
|
17083
|
+
filterNewAccounts: external_exports.boolean().optional(),
|
|
17084
|
+
showBoards: external_exports.boolean().optional(),
|
|
17085
|
+
factiiiIds: external_exports.number().array().optional(),
|
|
17086
|
+
strictFactiiiIds: external_exports.number().array().optional(),
|
|
17087
|
+
//used for getting posts by relatedfactiiicountgroup
|
|
17088
|
+
dataFilter: external_exports.string().optional(),
|
|
17089
|
+
start: external_exports.date().optional(),
|
|
17090
|
+
end: external_exports.date().optional()
|
|
17091
|
+
};
|
|
17092
|
+
var postFeedInputSchema = external_exports.discriminatedUnion("source", [
|
|
17093
|
+
external_exports.object({
|
|
17094
|
+
source: external_exports.literal("home"),
|
|
17095
|
+
...baseSchema
|
|
17096
|
+
}),
|
|
17097
|
+
external_exports.object({
|
|
17098
|
+
source: external_exports.literal("explore"),
|
|
17099
|
+
...baseSchema
|
|
17100
|
+
}),
|
|
17101
|
+
external_exports.object({
|
|
17102
|
+
source: external_exports.literal("user"),
|
|
17103
|
+
username: external_exports.string(),
|
|
17104
|
+
...baseSchema
|
|
17105
|
+
}),
|
|
17106
|
+
external_exports.object({
|
|
17107
|
+
source: external_exports.literal("space"),
|
|
17108
|
+
spaceSlug: external_exports.string(),
|
|
17109
|
+
spaceRenderType: external_exports.enum(["USER_FACING", "SPACE_FACING", "BOTH"]).optional(),
|
|
17110
|
+
...baseSchema
|
|
17111
|
+
}),
|
|
17112
|
+
external_exports.object({
|
|
17113
|
+
source: external_exports.literal("saved"),
|
|
17114
|
+
...baseSchema
|
|
17115
|
+
}),
|
|
17116
|
+
external_exports.object({
|
|
17117
|
+
source: external_exports.literal("factiiis"),
|
|
17118
|
+
...baseSchema
|
|
17119
|
+
}),
|
|
17120
|
+
// Every anonymous post, author masked. Backs /user/Anonymous%20User, which is
|
|
17121
|
+
// where anonymous posts' author links point.
|
|
17122
|
+
external_exports.object({
|
|
17123
|
+
source: external_exports.literal("anonymous"),
|
|
17124
|
+
...baseSchema
|
|
17125
|
+
}),
|
|
17126
|
+
external_exports.object({
|
|
17127
|
+
source: external_exports.literal("search"),
|
|
17128
|
+
query: external_exports.string().optional(),
|
|
17129
|
+
spaceRenderType: external_exports.enum(["USER_FACING", "SPACE_FACING", "BOTH"]).optional(),
|
|
17130
|
+
userIds: external_exports.number().array().optional(),
|
|
17131
|
+
spaceIds: external_exports.number().array().optional(),
|
|
17132
|
+
...baseSchema
|
|
17133
|
+
})
|
|
17134
|
+
]);
|
|
17135
|
+
var donateTronsSchema = external_exports.object({
|
|
17136
|
+
amount: external_exports.number().int().positive()
|
|
17137
|
+
});
|
|
17138
|
+
var communityPoolModeSchema = external_exports.enum(["OFF", "WHITELIST", "OPEN"]);
|
|
17139
|
+
var communityPoolConfigSchema = external_exports.object({
|
|
17140
|
+
mode: communityPoolModeSchema,
|
|
17141
|
+
userCapPer4h: external_exports.number().int().min(0),
|
|
17142
|
+
poolCapPer4h: external_exports.number().int().min(0)
|
|
17143
|
+
});
|
|
17144
|
+
var crossPostPlatformEnum = external_exports.enum([
|
|
17145
|
+
"TIKTOK",
|
|
17146
|
+
"YOUTUBE",
|
|
17147
|
+
"INSTAGRAM",
|
|
17148
|
+
"FACEBOOK",
|
|
17149
|
+
"TWITTER",
|
|
17150
|
+
"LINKEDIN",
|
|
17151
|
+
"REDDIT"
|
|
17152
|
+
]);
|
|
17153
|
+
var crossPostSchema = external_exports.object({
|
|
17154
|
+
postId: external_exports.number().int().positive(),
|
|
17155
|
+
platforms: external_exports.array(crossPostPlatformEnum).min(1)
|
|
17156
|
+
});
|
|
17157
|
+
var tiktokPostOptionsSchema = external_exports.object({
|
|
17158
|
+
postId: external_exports.number().int().positive(),
|
|
17159
|
+
privacyLevel: external_exports.enum([
|
|
17160
|
+
"PUBLIC_TO_EVERYONE",
|
|
17161
|
+
"MUTUAL_FOLLOW_FRIENDS",
|
|
17162
|
+
"FOLLOWER_OF_CREATOR",
|
|
17163
|
+
"SELF_ONLY"
|
|
17164
|
+
]),
|
|
17165
|
+
disableComment: external_exports.boolean().default(false),
|
|
17166
|
+
disableDuet: external_exports.boolean().default(false),
|
|
17167
|
+
disableStitch: external_exports.boolean().default(false),
|
|
17168
|
+
videoCoverTimestampMs: external_exports.number().int().min(0).optional(),
|
|
17169
|
+
isAiGenerated: external_exports.boolean().default(false)
|
|
17170
|
+
});
|
|
17171
|
+
var crossPostSubmitSchema = external_exports.object({
|
|
17172
|
+
postId: external_exports.string(),
|
|
17173
|
+
platforms: external_exports.array(crossPostPlatformEnum).min(1).max(5)
|
|
17174
|
+
});
|
|
17175
|
+
var apiCostUpdateSchema = external_exports.object({
|
|
17176
|
+
platform: crossPostPlatformEnum,
|
|
17177
|
+
readCostTrons: external_exports.number().int().min(0),
|
|
17178
|
+
pushCostTrons: external_exports.number().int().min(0),
|
|
17179
|
+
costSource: external_exports.enum(["manual", "api"]),
|
|
17180
|
+
notes: external_exports.string().max(500).optional()
|
|
17181
|
+
});
|
|
17182
|
+
var startSessionSchema = external_exports.object({
|
|
17183
|
+
isDaily: external_exports.boolean().default(false)
|
|
17184
|
+
});
|
|
17185
|
+
var submitAnswerSchema = external_exports.object({
|
|
17186
|
+
sessionId: external_exports.number().int(),
|
|
17187
|
+
postId: external_exports.number().int(),
|
|
17188
|
+
answeredFact: external_exports.boolean(),
|
|
17189
|
+
timeMs: external_exports.number().int().min(0).max(15e3)
|
|
17190
|
+
});
|
|
17191
|
+
var endSessionSchema = external_exports.object({
|
|
17192
|
+
sessionId: external_exports.number().int()
|
|
17193
|
+
});
|
|
17194
|
+
var getLeaderboardSchema = external_exports.object({
|
|
17195
|
+
period: external_exports.enum(["daily", "weekly", "allTime"]),
|
|
17196
|
+
limit: external_exports.number().int().min(1).max(50).default(50)
|
|
17197
|
+
});
|
|
17198
|
+
var getSessionResultSchema = external_exports.object({
|
|
17199
|
+
sessionId: external_exports.number().int()
|
|
17200
|
+
});
|
|
17201
|
+
var forgeElementName = external_exports.string().trim().min(1).max(24);
|
|
17202
|
+
var forgeCombineSchema = external_exports.object({
|
|
17203
|
+
a: forgeElementName,
|
|
17204
|
+
b: forgeElementName
|
|
17205
|
+
});
|
|
17206
|
+
var forgeLeaderboardSchema = external_exports.object({
|
|
17207
|
+
limit: external_exports.number().int().min(1).max(50).default(25)
|
|
17208
|
+
});
|
|
17209
|
+
var arcadeGameSlug = external_exports.enum([
|
|
17210
|
+
"circle",
|
|
17211
|
+
"password",
|
|
17212
|
+
"forge",
|
|
17213
|
+
"spend",
|
|
17214
|
+
"dilemmas",
|
|
17215
|
+
"clicker",
|
|
17216
|
+
"deep",
|
|
17217
|
+
"robot",
|
|
17218
|
+
"space"
|
|
17219
|
+
]);
|
|
17220
|
+
var recordGameStatSchema = external_exports.object({
|
|
17221
|
+
game: arcadeGameSlug,
|
|
17222
|
+
/** Canonical sortable score for that game; direction is decided client-side. */
|
|
17223
|
+
best: external_exports.number().finite(),
|
|
17224
|
+
/** Per-game extras, kept small — this lands in a JSON column. */
|
|
17225
|
+
detail: external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number()])).optional()
|
|
17226
|
+
});
|
|
17227
|
+
var dilemmaVoteSchema = external_exports.object({
|
|
17228
|
+
dilemmaId: external_exports.number().int().min(1).max(200),
|
|
17229
|
+
pulled: external_exports.boolean()
|
|
17230
|
+
});
|
|
17231
|
+
var SKILL_NAME_REGEX = /^[a-zA-Z0-9._-]+$/;
|
|
17232
|
+
var skillNameSchema = external_exports.string().min(1).max(64).regex(SKILL_NAME_REGEX, {
|
|
17233
|
+
message: "Use only letters, numbers, dots, dashes and underscores"
|
|
17234
|
+
});
|
|
17235
|
+
var skillFrontmatterSchema = external_exports.object({
|
|
17236
|
+
name: skillNameSchema,
|
|
17237
|
+
description: external_exports.string().min(1, { message: "Description is required" }),
|
|
17238
|
+
// Both CLIs accept a comma-separated string or a YAML list; normalised to
|
|
17239
|
+
// a list on parse and written back as a list.
|
|
17240
|
+
"allowed-tools": external_exports.array(external_exports.string()).optional(),
|
|
17241
|
+
model: external_exports.string().optional(),
|
|
17242
|
+
license: external_exports.string().optional(),
|
|
17243
|
+
metadata: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
17244
|
+
"disable-model-invocation": external_exports.literal(false).optional()
|
|
17245
|
+
}).strict();
|
|
17246
|
+
var skillAgentSchema = external_exports.enum(["claude", "codex"]);
|
|
17247
|
+
var skillScopeSchema = external_exports.enum(["global", "repo"]);
|
|
17248
|
+
|
|
17249
|
+
// ../../shared/all/helpers/board-agent-core/skill-format.ts
|
|
17250
|
+
var EMPTY_FRONTMATTER = { name: "", description: "" };
|
|
17251
|
+
function splitKey(line) {
|
|
17252
|
+
const at = line.indexOf(":");
|
|
17253
|
+
if (at < 1) return null;
|
|
17254
|
+
return { key: line.slice(0, at).trim(), value: line.slice(at + 1).trim() };
|
|
17255
|
+
}
|
|
17256
|
+
function stripQuotes(value2) {
|
|
17257
|
+
const quoted = value2.startsWith('"') && value2.endsWith('"') || value2.startsWith("'") && value2.endsWith("'");
|
|
17258
|
+
return quoted && value2.length > 1 ? value2.slice(1, -1) : value2;
|
|
17259
|
+
}
|
|
17260
|
+
function parseSkill(raw) {
|
|
17261
|
+
const text = raw.replace(/^/, "");
|
|
17262
|
+
if (!text.startsWith("---")) {
|
|
17263
|
+
return {
|
|
17264
|
+
frontmatter: EMPTY_FRONTMATTER,
|
|
17265
|
+
body: text,
|
|
17266
|
+
errors: ["Must start with YAML frontmatter (---)."]
|
|
17267
|
+
};
|
|
17268
|
+
}
|
|
17269
|
+
const lines = text.split("\n");
|
|
17270
|
+
const end = lines.findIndex((line, i) => i > 0 && line.trim() === "---");
|
|
17271
|
+
if (end === -1) {
|
|
17272
|
+
return {
|
|
17273
|
+
frontmatter: EMPTY_FRONTMATTER,
|
|
17274
|
+
body: text,
|
|
17275
|
+
errors: ["Frontmatter is never closed (missing the second ---)."]
|
|
17276
|
+
};
|
|
17277
|
+
}
|
|
17278
|
+
const errors = [];
|
|
17279
|
+
const fields = {};
|
|
17280
|
+
let pendingKey = "";
|
|
17281
|
+
let list = [];
|
|
17282
|
+
let map = {};
|
|
17283
|
+
const flush = () => {
|
|
17284
|
+
if (pendingKey) {
|
|
17285
|
+
if (list.length) fields[pendingKey] = list;
|
|
17286
|
+
else if (Object.keys(map).length) fields[pendingKey] = map;
|
|
17287
|
+
}
|
|
17288
|
+
pendingKey = "";
|
|
17289
|
+
list = [];
|
|
17290
|
+
map = {};
|
|
17291
|
+
};
|
|
17292
|
+
for (const line of lines.slice(1, end)) {
|
|
17293
|
+
if (!line.trim() || line.trim().startsWith("#")) continue;
|
|
17294
|
+
const indented = /^\s/.test(line);
|
|
17295
|
+
if (indented && pendingKey) {
|
|
17296
|
+
if (line.trim().startsWith("- ")) {
|
|
17297
|
+
list.push(stripQuotes(line.trim().slice(2).trim()));
|
|
17298
|
+
} else {
|
|
17299
|
+
const nested = splitKey(line);
|
|
17300
|
+
if (nested) map[nested.key] = stripQuotes(nested.value);
|
|
17301
|
+
else errors.push(`Cannot read indented line: ${line.trim()}`);
|
|
17302
|
+
}
|
|
17303
|
+
continue;
|
|
17304
|
+
}
|
|
17305
|
+
if (indented) {
|
|
17306
|
+
errors.push(`Cannot read indented line: ${line.trim()}`);
|
|
17307
|
+
continue;
|
|
17308
|
+
}
|
|
17309
|
+
flush();
|
|
17310
|
+
const pair = splitKey(line);
|
|
17311
|
+
if (!pair) {
|
|
17312
|
+
errors.push(`Cannot read line: ${line.trim()}`);
|
|
17313
|
+
continue;
|
|
17314
|
+
}
|
|
17315
|
+
if (pair.value === "") {
|
|
17316
|
+
pendingKey = pair.key;
|
|
17317
|
+
continue;
|
|
17318
|
+
}
|
|
17319
|
+
fields[pair.key] = stripQuotes(pair.value);
|
|
17320
|
+
}
|
|
17321
|
+
flush();
|
|
17322
|
+
const tools = fields["allowed-tools"];
|
|
17323
|
+
if (typeof tools === "string") {
|
|
17324
|
+
fields["allowed-tools"] = tools.split(",").map((tool) => tool.trim()).filter(Boolean);
|
|
17325
|
+
}
|
|
17326
|
+
if (fields["disable-model-invocation"] === "true") {
|
|
17327
|
+
errors.push("disable-model-invocation must be false.");
|
|
17328
|
+
delete fields["disable-model-invocation"];
|
|
17329
|
+
}
|
|
17330
|
+
if (fields["disable-model-invocation"] === "false") {
|
|
17331
|
+
fields["disable-model-invocation"] = false;
|
|
17332
|
+
}
|
|
17333
|
+
const body = lines.slice(end + 1).join("\n").replace(/^\n/, "");
|
|
17334
|
+
const parsed = skillFrontmatterSchema.safeParse(fields);
|
|
17335
|
+
if (!parsed.success) {
|
|
17336
|
+
for (const issue of parsed.error.issues) {
|
|
17337
|
+
errors.push(`${issue.path.join(".") || "frontmatter"}: ${issue.message}`);
|
|
17338
|
+
}
|
|
17339
|
+
return {
|
|
17340
|
+
frontmatter: {
|
|
17341
|
+
name: typeof fields.name === "string" ? fields.name : "",
|
|
17342
|
+
description: typeof fields.description === "string" ? fields.description : ""
|
|
17343
|
+
},
|
|
17344
|
+
body,
|
|
17345
|
+
errors
|
|
17346
|
+
};
|
|
17347
|
+
}
|
|
17348
|
+
return { frontmatter: parsed.data, body, errors };
|
|
17349
|
+
}
|
|
17350
|
+
function resolveSkillDir(root, name) {
|
|
17351
|
+
if (!SKILL_NAME_REGEX.test(name) || name === "." || name === "..") {
|
|
17352
|
+
throw new Error(`Invalid skill name: ${name}`);
|
|
17353
|
+
}
|
|
17354
|
+
return `${root.replace(/[/\\]+$/, "")}/${name}`;
|
|
17355
|
+
}
|
|
17356
|
+
|
|
17357
|
+
// ../../shared/all/helpers/board-agent-core/host-skills.ts
|
|
17358
|
+
var AGENTS_ROOT = ".agents/skills";
|
|
17359
|
+
var CLAUDE_ROOT = ".claude/skills";
|
|
17360
|
+
var CLI_ROOTS = [CLAUDE_ROOT, ".codex/skills"];
|
|
17361
|
+
function globalAgentsRoot() {
|
|
17362
|
+
return import_path8.default.join(import_os2.default.homedir(), ".agents", "skills");
|
|
17363
|
+
}
|
|
17364
|
+
var AGENTS_LINK_TARGET = "../.agents/skills";
|
|
17365
|
+
var BRANCH_PREFIX = "skill/";
|
|
17366
|
+
var SYMLINK_MODE = "120000";
|
|
17367
|
+
function summarize(raw, base) {
|
|
17368
|
+
const { frontmatter, errors } = parseSkill(raw);
|
|
17369
|
+
return { ...base, description: frontmatter.description, errors };
|
|
17370
|
+
}
|
|
17371
|
+
async function rootLinkState(root) {
|
|
17372
|
+
const linkPath = import_path8.default.join(import_os2.default.homedir(), ...root.split("/"));
|
|
17373
|
+
const stat = await import_promises5.default.lstat(linkPath).catch(() => null);
|
|
17374
|
+
if (!stat) return "missing";
|
|
17375
|
+
if (!stat.isSymbolicLink()) return "blocked";
|
|
17376
|
+
const target = await import_promises5.default.readlink(linkPath).catch(() => "");
|
|
17377
|
+
return target.replace(/\/+$/, "") === AGENTS_LINK_TARGET ? "linked" : "missing";
|
|
17378
|
+
}
|
|
17379
|
+
async function globalLinkState() {
|
|
17380
|
+
const states = await Promise.all(CLI_ROOTS.map(rootLinkState));
|
|
17381
|
+
if (states.includes("blocked")) return "blocked";
|
|
17382
|
+
return states.includes("missing") ? "missing" : "linked";
|
|
17383
|
+
}
|
|
17384
|
+
async function listGlobal() {
|
|
17385
|
+
const root = globalAgentsRoot();
|
|
17386
|
+
const entries = await import_promises5.default.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
17387
|
+
const out = [];
|
|
17388
|
+
for (const entry of entries) {
|
|
17389
|
+
if (entry.isFile()) continue;
|
|
17390
|
+
const dir = import_path8.default.join(root, entry.name);
|
|
17391
|
+
const raw = await import_promises5.default.readFile(import_path8.default.join(dir, "SKILL.md"), "utf-8").catch(() => "");
|
|
17392
|
+
if (!raw) continue;
|
|
17393
|
+
out.push(
|
|
17394
|
+
summarize(raw, {
|
|
17395
|
+
name: entry.name,
|
|
17396
|
+
scope: "global",
|
|
17397
|
+
dir,
|
|
17398
|
+
status: "live",
|
|
17399
|
+
pendingBranch: ""
|
|
17400
|
+
})
|
|
17401
|
+
);
|
|
17402
|
+
}
|
|
17403
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
17404
|
+
}
|
|
17405
|
+
function strayRepoSkills(liveBlobs) {
|
|
17406
|
+
return [...liveBlobs.keys()].filter((key) => CLI_ROOTS.some((root) => key.startsWith(`${root}/`))).sort();
|
|
17407
|
+
}
|
|
17408
|
+
async function treeAt(repo, ref) {
|
|
17409
|
+
const raw = await hostGit(
|
|
17410
|
+
repo,
|
|
17411
|
+
"ls-tree",
|
|
17412
|
+
"-r",
|
|
17413
|
+
ref,
|
|
17414
|
+
"--",
|
|
17415
|
+
AGENTS_ROOT,
|
|
17416
|
+
...CLI_ROOTS
|
|
17417
|
+
).catch(() => "");
|
|
17418
|
+
return raw.split("\n").filter(Boolean).map((line) => {
|
|
17419
|
+
const [meta, filePath] = line.split(" ");
|
|
17420
|
+
const [mode, , sha] = meta.split(/\s+/);
|
|
17421
|
+
return { mode, sha, filePath };
|
|
17422
|
+
}).filter((entry) => Boolean(entry.filePath));
|
|
17423
|
+
}
|
|
17424
|
+
function skillBlobs(entries) {
|
|
17425
|
+
const blobs = /* @__PURE__ */ new Map();
|
|
17426
|
+
for (const entry of entries) {
|
|
17427
|
+
const match = /^(.+\/skills)\/([^/]+)\/SKILL\.md$/.exec(entry.filePath);
|
|
17428
|
+
if (match) blobs.set(`${match[1]}/${match[2]}`, entry.sha);
|
|
17429
|
+
}
|
|
17430
|
+
return blobs;
|
|
17431
|
+
}
|
|
17432
|
+
async function skillLinks(repo, entries) {
|
|
17433
|
+
const links = /* @__PURE__ */ new Map();
|
|
17434
|
+
const linkEntries = entries.filter(
|
|
17435
|
+
(entry) => entry.mode === SYMLINK_MODE && /^.+\/skills(\/[^/]+)?$/.test(entry.filePath)
|
|
17436
|
+
);
|
|
17437
|
+
const targets = await gitCatFileBatch(
|
|
17438
|
+
repo,
|
|
17439
|
+
linkEntries.map((entry) => entry.sha)
|
|
17440
|
+
);
|
|
17441
|
+
for (const entry of linkEntries) {
|
|
17442
|
+
const target = targets.get(entry.sha)?.trim();
|
|
17443
|
+
if (target) links.set(entry.filePath, target);
|
|
17444
|
+
}
|
|
17445
|
+
return links;
|
|
17446
|
+
}
|
|
17447
|
+
function resolveRepoSkills(liveBlobs, pending) {
|
|
17448
|
+
const keys = /* @__PURE__ */ new Set();
|
|
17449
|
+
for (const key of liveBlobs.keys()) {
|
|
17450
|
+
if (key.startsWith(`${AGENTS_ROOT}/`)) keys.add(key);
|
|
17451
|
+
}
|
|
17452
|
+
for (const entry of pending) {
|
|
17453
|
+
for (const key of entry.blobs.keys()) {
|
|
17454
|
+
if (key.startsWith(`${AGENTS_ROOT}/`)) keys.add(key);
|
|
17455
|
+
}
|
|
17456
|
+
}
|
|
17457
|
+
const out = [];
|
|
17458
|
+
for (const key of [...keys].sort()) {
|
|
17459
|
+
const name = key.slice(AGENTS_ROOT.length + 1);
|
|
17460
|
+
const live = liveBlobs.get(key) || "";
|
|
17461
|
+
const onBranch = pending.find((entry) => {
|
|
17462
|
+
const sha = entry.blobs.get(key);
|
|
17463
|
+
return Boolean(sha) && sha !== live;
|
|
17464
|
+
});
|
|
17465
|
+
const status = live ? onBranch ? "pending-edit" : "live" : "pending-new";
|
|
17466
|
+
out.push({
|
|
17467
|
+
name,
|
|
17468
|
+
dir: key,
|
|
17469
|
+
status,
|
|
17470
|
+
pendingBranch: status === "live" ? "" : onBranch?.branch || "",
|
|
17471
|
+
sha: live || onBranch?.blobs.get(key) || ""
|
|
17472
|
+
});
|
|
17473
|
+
}
|
|
17474
|
+
return out;
|
|
17475
|
+
}
|
|
17476
|
+
async function repoSkills(ctx) {
|
|
17477
|
+
const skills = resolveRepoSkills(ctx.liveBlobs, ctx.pending);
|
|
17478
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
17479
|
+
const missing = skills.map((skill) => skill.sha).filter((sha) => sha && !cache2.has(sha));
|
|
17480
|
+
for (const [sha, text] of await gitCatFileBatch(ctx.repo, missing)) {
|
|
17481
|
+
cache2.set(sha, text);
|
|
17482
|
+
}
|
|
17483
|
+
return skills.map(
|
|
17484
|
+
(skill) => summarize(cache2.get(skill.sha) ?? "", {
|
|
17485
|
+
name: skill.name,
|
|
17486
|
+
scope: "repo",
|
|
17487
|
+
dir: skill.dir,
|
|
17488
|
+
status: skill.status,
|
|
17489
|
+
pendingBranch: skill.pendingBranch
|
|
17490
|
+
})
|
|
17491
|
+
);
|
|
17492
|
+
}
|
|
17493
|
+
async function pendingBranches(repo, mainBranch) {
|
|
17494
|
+
const refs = await hostGit(
|
|
17495
|
+
repo,
|
|
17496
|
+
"for-each-ref",
|
|
17497
|
+
"--sort=-committerdate",
|
|
17498
|
+
"--format=%(refname:short)",
|
|
17499
|
+
`refs/remotes/origin/${BRANCH_PREFIX}*`
|
|
17500
|
+
).catch(() => "");
|
|
17501
|
+
const branches = await Promise.all(
|
|
17502
|
+
refs.split("\n").filter(Boolean).map(async (ref) => {
|
|
17503
|
+
const merged = await hostGit(
|
|
17504
|
+
repo,
|
|
17505
|
+
"merge-base",
|
|
17506
|
+
"--is-ancestor",
|
|
17507
|
+
ref,
|
|
17508
|
+
`origin/${mainBranch}`
|
|
17509
|
+
).then(
|
|
17510
|
+
() => true,
|
|
17511
|
+
() => false
|
|
17512
|
+
);
|
|
17513
|
+
if (merged) return null;
|
|
17514
|
+
return {
|
|
17515
|
+
branch: ref.replace(/^origin\//, ""),
|
|
17516
|
+
blobs: skillBlobs(await treeAt(repo, ref))
|
|
17517
|
+
};
|
|
17518
|
+
})
|
|
17519
|
+
);
|
|
17520
|
+
return branches.filter((entry) => entry !== null);
|
|
17521
|
+
}
|
|
17522
|
+
function requireRepo(opts) {
|
|
17523
|
+
if (!opts.repoUrl || !opts.githubToken) {
|
|
17524
|
+
throw new Error("Set the repository URL and GitHub token first.");
|
|
17525
|
+
}
|
|
17526
|
+
return {
|
|
17527
|
+
spaceDir: opts.spaceDir,
|
|
17528
|
+
repoUrl: opts.repoUrl,
|
|
17529
|
+
mainBranch: opts.mainBranch,
|
|
17530
|
+
githubToken: opts.githubToken
|
|
17531
|
+
};
|
|
17532
|
+
}
|
|
17533
|
+
async function ensureSkillsRepo(opts) {
|
|
17534
|
+
const { spaceDir, repoUrl, mainBranch, githubToken } = requireRepo(opts);
|
|
17535
|
+
const paths = hostSpacePaths(spaceDir);
|
|
17536
|
+
const branch = (mainBranch || "main").replace(/[^\w./-]/g, "");
|
|
17537
|
+
const url2 = buildCloneUrl(repoUrl, githubToken);
|
|
17538
|
+
try {
|
|
17539
|
+
await hostGit(paths.repo, "rev-parse", "--git-dir");
|
|
17540
|
+
await hostGit(paths.repo, "remote", "set-url", "origin", url2);
|
|
17541
|
+
} catch {
|
|
17542
|
+
await hostGit(paths.root, "clone", "--branch", branch, url2, paths.repo);
|
|
17543
|
+
}
|
|
17544
|
+
await hostGit(
|
|
17545
|
+
paths.repo,
|
|
17546
|
+
"fetch",
|
|
17547
|
+
"origin",
|
|
17548
|
+
branch,
|
|
17549
|
+
`+refs/heads/${BRANCH_PREFIX}*:refs/remotes/origin/${BRANCH_PREFIX}*`
|
|
17550
|
+
);
|
|
17551
|
+
return { repo: paths.repo, branch };
|
|
17552
|
+
}
|
|
17553
|
+
async function ensureSkillsWorktree(spaceDir, branch) {
|
|
17554
|
+
const { repo, skills: worktree } = hostSpacePaths(spaceDir);
|
|
17555
|
+
try {
|
|
17556
|
+
await hostGit(worktree, "rev-parse", "--git-dir");
|
|
17557
|
+
} catch {
|
|
17558
|
+
await hostGit(repo, "worktree", "prune").catch(() => {
|
|
17559
|
+
});
|
|
17560
|
+
await import_promises5.default.rm(worktree, { recursive: true, force: true });
|
|
17561
|
+
await hostGit(
|
|
17562
|
+
repo,
|
|
17563
|
+
"worktree",
|
|
17564
|
+
"add",
|
|
17565
|
+
"--detach",
|
|
17566
|
+
worktree,
|
|
17567
|
+
`origin/${branch}`
|
|
17568
|
+
);
|
|
17569
|
+
}
|
|
17570
|
+
return worktree;
|
|
17571
|
+
}
|
|
17572
|
+
async function repoContext(opts) {
|
|
17573
|
+
const { repo, branch } = await ensureSkillsRepo(opts);
|
|
17574
|
+
const liveTree = await treeAt(repo, `origin/${branch}`);
|
|
17575
|
+
const [links, pending] = await Promise.all([
|
|
17576
|
+
skillLinks(repo, liveTree),
|
|
17577
|
+
pendingBranches(repo, branch)
|
|
17578
|
+
]);
|
|
17579
|
+
return {
|
|
17580
|
+
repo,
|
|
17581
|
+
branch,
|
|
17582
|
+
liveTree,
|
|
17583
|
+
liveBlobs: skillBlobs(liveTree),
|
|
17584
|
+
links,
|
|
17585
|
+
pending
|
|
17586
|
+
};
|
|
17587
|
+
}
|
|
17588
|
+
function findRepoSkill(ctx, name) {
|
|
17589
|
+
return resolveRepoSkills(ctx.liveBlobs, ctx.pending).find(
|
|
17590
|
+
(skill) => skill.name === name
|
|
17591
|
+
) ?? null;
|
|
17592
|
+
}
|
|
17593
|
+
var workspaceQueues = /* @__PURE__ */ new Map();
|
|
17594
|
+
function withWorkspace(spaceDir, run2) {
|
|
17595
|
+
const next = (workspaceQueues.get(spaceDir) ?? Promise.resolve()).then(
|
|
17596
|
+
run2,
|
|
17597
|
+
run2
|
|
17598
|
+
);
|
|
17599
|
+
workspaceQueues.set(
|
|
17600
|
+
spaceDir,
|
|
17601
|
+
next.catch(() => {
|
|
17602
|
+
})
|
|
17603
|
+
);
|
|
17604
|
+
return next;
|
|
17605
|
+
}
|
|
17606
|
+
function repoLinkState(ctx) {
|
|
17607
|
+
const target = ctx.links.get(CLAUDE_ROOT);
|
|
17608
|
+
if (target) return target === AGENTS_LINK_TARGET ? "linked" : "missing";
|
|
17609
|
+
return [...ctx.liveBlobs.keys()].some(
|
|
17610
|
+
(key) => key.startsWith(`${CLAUDE_ROOT}/`)
|
|
17611
|
+
) ? "blocked" : "missing";
|
|
17612
|
+
}
|
|
17613
|
+
async function listSkillsImpl(opts) {
|
|
17614
|
+
const [global2, globalLink] = await Promise.all([
|
|
17615
|
+
listGlobal(),
|
|
17616
|
+
globalLinkState()
|
|
17617
|
+
]);
|
|
17618
|
+
if (!opts.repoUrl || !opts.githubToken) {
|
|
17619
|
+
return {
|
|
17620
|
+
global: global2,
|
|
17621
|
+
repo: [],
|
|
17622
|
+
globalLink,
|
|
17623
|
+
repoLink: "linked",
|
|
17624
|
+
repoBranch: "",
|
|
17625
|
+
repoAvailable: false
|
|
17626
|
+
};
|
|
17627
|
+
}
|
|
17628
|
+
const ctx = await repoContext(opts);
|
|
17629
|
+
const repo = await repoSkills(ctx);
|
|
17630
|
+
return {
|
|
17631
|
+
global: global2,
|
|
17632
|
+
repo,
|
|
17633
|
+
globalLink,
|
|
17634
|
+
repoLink: repoLinkState(ctx),
|
|
17635
|
+
repoBranch: ctx.branch,
|
|
17636
|
+
repoAvailable: true
|
|
17637
|
+
};
|
|
17638
|
+
}
|
|
17639
|
+
async function bundleFilesOnDisk(dir) {
|
|
17640
|
+
const out = [];
|
|
17641
|
+
const walk = async (current, prefix, depth) => {
|
|
17642
|
+
if (depth > 3 || out.length >= 200) return;
|
|
17643
|
+
const entries = await import_promises5.default.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
17644
|
+
for (const entry of entries) {
|
|
17645
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
17646
|
+
if (entry.isDirectory()) {
|
|
17647
|
+
await walk(import_path8.default.join(current, entry.name), rel, depth + 1);
|
|
17648
|
+
} else if (rel !== "SKILL.md") {
|
|
17649
|
+
out.push(rel);
|
|
17650
|
+
}
|
|
17651
|
+
}
|
|
17652
|
+
};
|
|
17653
|
+
await walk(dir, "", 0);
|
|
17654
|
+
return out.sort();
|
|
17655
|
+
}
|
|
17656
|
+
async function readSkillImpl(opts) {
|
|
17657
|
+
if (opts.scope === "global") {
|
|
17658
|
+
const dir = resolveSkillDir(globalAgentsRoot(), opts.name);
|
|
17659
|
+
const content2 = await import_promises5.default.readFile(import_path8.default.join(dir, "SKILL.md"), "utf-8");
|
|
17660
|
+
const { frontmatter: frontmatter2, errors: errors2 } = parseSkill(content2);
|
|
17661
|
+
return {
|
|
17662
|
+
name: opts.name,
|
|
17663
|
+
scope: "global",
|
|
17664
|
+
content: content2,
|
|
17665
|
+
frontmatter: frontmatter2,
|
|
17666
|
+
errors: errors2,
|
|
17667
|
+
bundleFiles: await bundleFilesOnDisk(dir),
|
|
17668
|
+
status: "live",
|
|
17669
|
+
pendingBranch: ""
|
|
17670
|
+
};
|
|
17671
|
+
}
|
|
17672
|
+
const ctx = await repoContext(opts);
|
|
17673
|
+
const skill = findRepoSkill(ctx, opts.name);
|
|
17674
|
+
if (!skill) throw new Error(`No repo skill named ${opts.name}.`);
|
|
17675
|
+
const draft = skill.pendingBranch ? ctx.pending.find((entry) => entry.branch === skill.pendingBranch)?.blobs.get(skill.dir) : "";
|
|
17676
|
+
const content = await hostGit(ctx.repo, "cat-file", "-p", draft || skill.sha);
|
|
17677
|
+
const { frontmatter, errors } = parseSkill(content);
|
|
17678
|
+
const tree = draft ? await treeAt(ctx.repo, `origin/${skill.pendingBranch}`) : ctx.liveTree;
|
|
17679
|
+
const bundleFiles = tree.filter(
|
|
17680
|
+
(entry) => entry.filePath.startsWith(`${skill.dir}/`) && entry.filePath !== `${skill.dir}/SKILL.md`
|
|
17681
|
+
).map((entry) => entry.filePath.slice(skill.dir.length + 1)).sort();
|
|
17682
|
+
return {
|
|
17683
|
+
name: skill.name,
|
|
17684
|
+
scope: "repo",
|
|
17685
|
+
content,
|
|
17686
|
+
frontmatter,
|
|
17687
|
+
errors,
|
|
17688
|
+
bundleFiles,
|
|
17689
|
+
status: skill.status,
|
|
17690
|
+
pendingBranch: skill.pendingBranch
|
|
17691
|
+
};
|
|
17692
|
+
}
|
|
17693
|
+
async function moveIntoAgents(from, to) {
|
|
17694
|
+
if (await import_promises5.default.access(to).then(
|
|
17695
|
+
() => true,
|
|
17696
|
+
() => false
|
|
17697
|
+
)) {
|
|
17698
|
+
throw new Error(
|
|
17699
|
+
`${import_path8.default.basename(to)} exists in both roots. Remove one copy, then migrate.`
|
|
17700
|
+
);
|
|
17701
|
+
}
|
|
17702
|
+
await import_promises5.default.mkdir(import_path8.default.dirname(to), { recursive: true });
|
|
17703
|
+
try {
|
|
17704
|
+
await import_promises5.default.rename(from, to);
|
|
17705
|
+
} catch {
|
|
17706
|
+
await import_promises5.default.cp(from, to, { recursive: true });
|
|
17707
|
+
await import_promises5.default.rm(from, { recursive: true, force: true });
|
|
17708
|
+
}
|
|
17709
|
+
}
|
|
17710
|
+
async function migrateGlobal() {
|
|
17711
|
+
const moved = [];
|
|
17712
|
+
for (const root of CLI_ROOTS) {
|
|
17713
|
+
const dir = import_path8.default.join(import_os2.default.homedir(), ...root.split("/"));
|
|
17714
|
+
const stat = await import_promises5.default.lstat(dir).catch(() => null);
|
|
17715
|
+
if (!stat || stat.isSymbolicLink()) continue;
|
|
17716
|
+
const entries = await import_promises5.default.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
17717
|
+
for (const entry of entries) {
|
|
17718
|
+
if (entry.isFile() || entry.isSymbolicLink()) continue;
|
|
17719
|
+
const src = import_path8.default.join(dir, entry.name);
|
|
17720
|
+
if (!await import_promises5.default.access(import_path8.default.join(src, "SKILL.md")).then(
|
|
17721
|
+
() => true,
|
|
17722
|
+
() => false
|
|
17723
|
+
)) {
|
|
17724
|
+
continue;
|
|
17725
|
+
}
|
|
17726
|
+
await moveIntoAgents(src, import_path8.default.join(globalAgentsRoot(), entry.name));
|
|
17727
|
+
moved.push(entry.name);
|
|
17728
|
+
}
|
|
17729
|
+
await import_promises5.default.rmdir(dir).catch(() => {
|
|
17730
|
+
});
|
|
17731
|
+
}
|
|
17732
|
+
for (const root of CLI_ROOTS) {
|
|
17733
|
+
const linkPath = import_path8.default.join(import_os2.default.homedir(), ...root.split("/"));
|
|
17734
|
+
const after = await import_promises5.default.lstat(linkPath).catch(() => null);
|
|
17735
|
+
if (after?.isSymbolicLink()) await import_promises5.default.rm(linkPath, { force: true });
|
|
17736
|
+
else if (after) {
|
|
17737
|
+
throw new Error(
|
|
17738
|
+
`~/${root} still has files that are not skills. Empty it, then migrate.`
|
|
17739
|
+
);
|
|
17740
|
+
}
|
|
17741
|
+
await import_promises5.default.mkdir(import_path8.default.dirname(linkPath), { recursive: true });
|
|
17742
|
+
await import_promises5.default.symlink(AGENTS_LINK_TARGET, linkPath, "dir");
|
|
17743
|
+
}
|
|
17744
|
+
return moved;
|
|
17745
|
+
}
|
|
17746
|
+
async function migrateSkillsImpl(opts) {
|
|
17747
|
+
if (opts.scope === "global") {
|
|
17748
|
+
return { branch: "", moved: await migrateGlobal() };
|
|
17749
|
+
}
|
|
17750
|
+
const ctx = await repoContext(opts);
|
|
17751
|
+
const strays = strayRepoSkills(ctx.liveBlobs);
|
|
17752
|
+
const linked = ctx.links.get(CLAUDE_ROOT);
|
|
17753
|
+
if (!strays.length && linked === AGENTS_LINK_TARGET) {
|
|
17754
|
+
return { branch: "", moved: [] };
|
|
17755
|
+
}
|
|
17756
|
+
const { branch } = ctx;
|
|
17757
|
+
const workspace = await ensureSkillsWorktree(opts.spaceDir, branch);
|
|
17758
|
+
const target = `${BRANCH_PREFIX}migrate`;
|
|
17759
|
+
await hostGit(workspace, "checkout", "-B", target, `origin/${branch}`);
|
|
17760
|
+
await hostGit(workspace, "clean", "-fd");
|
|
17761
|
+
if (opts.gitName) {
|
|
17762
|
+
await hostGit(workspace, "config", "user.name", opts.gitName);
|
|
17763
|
+
}
|
|
17764
|
+
if (opts.gitEmail) {
|
|
17765
|
+
await hostGit(workspace, "config", "user.email", opts.gitEmail);
|
|
17766
|
+
}
|
|
17767
|
+
const moved = [];
|
|
17768
|
+
for (const stray of strays) {
|
|
17769
|
+
const name = stray.slice(stray.lastIndexOf("/") + 1);
|
|
17770
|
+
const dest = `${AGENTS_ROOT}/${name}`;
|
|
17771
|
+
if (ctx.liveBlobs.has(dest)) {
|
|
17772
|
+
throw new Error(
|
|
17773
|
+
`${name} exists in both ${AGENTS_ROOT} and ${stray}. Remove one copy, then migrate.`
|
|
17774
|
+
);
|
|
17775
|
+
}
|
|
17776
|
+
await import_promises5.default.mkdir(import_path8.default.join(workspace, AGENTS_ROOT), { recursive: true });
|
|
17777
|
+
await hostGit(workspace, "mv", stray, dest);
|
|
17778
|
+
moved.push(name);
|
|
17779
|
+
}
|
|
17780
|
+
const linkPath = import_path8.default.join(workspace, CLAUDE_ROOT);
|
|
17781
|
+
await import_promises5.default.rm(linkPath, { recursive: true, force: true });
|
|
17782
|
+
await import_promises5.default.symlink(AGENTS_LINK_TARGET, linkPath, "dir");
|
|
17783
|
+
await hostGit(workspace, "add", "-A");
|
|
17784
|
+
const dirty = await hostGit(workspace, "status", "--porcelain");
|
|
17785
|
+
if (!dirty.trim()) return { branch: "", moved: [] };
|
|
17786
|
+
await hostGit(
|
|
17787
|
+
workspace,
|
|
17788
|
+
"commit",
|
|
17789
|
+
"-m",
|
|
17790
|
+
`Migrate ${moved.length} skill(s) to ${AGENTS_ROOT} and link ${CLAUDE_ROOT}`
|
|
17791
|
+
);
|
|
17792
|
+
await hostGit(workspace, "push", "origin", target);
|
|
17793
|
+
return { branch: target, moved };
|
|
17794
|
+
}
|
|
17795
|
+
function assertWritable(name, content) {
|
|
17796
|
+
const parsed = parseSkill(content);
|
|
17797
|
+
if (parsed.errors.length) {
|
|
17798
|
+
throw new Error(`Fix the skill first: ${parsed.errors.join(" ")}`);
|
|
17799
|
+
}
|
|
17800
|
+
if (parsed.frontmatter.name !== name) {
|
|
17801
|
+
throw new Error(
|
|
17802
|
+
`Frontmatter name (${parsed.frontmatter.name}) must match the skill directory (${name}).`
|
|
17803
|
+
);
|
|
17804
|
+
}
|
|
17805
|
+
}
|
|
17806
|
+
async function writeGlobalSkill(opts) {
|
|
17807
|
+
assertWritable(opts.name, opts.content);
|
|
17808
|
+
const dir = resolveSkillDir(globalAgentsRoot(), opts.name);
|
|
17809
|
+
await import_promises5.default.mkdir(dir, { recursive: true });
|
|
17810
|
+
await import_promises5.default.writeFile(import_path8.default.join(dir, "SKILL.md"), opts.content, "utf-8");
|
|
17811
|
+
}
|
|
17812
|
+
async function deleteGlobalSkill(opts) {
|
|
17813
|
+
const dir = resolveSkillDir(globalAgentsRoot(), opts.name);
|
|
17814
|
+
await import_promises5.default.rm(dir, { recursive: true, force: true });
|
|
17815
|
+
}
|
|
17816
|
+
async function proposeRepoSkillImpl(opts) {
|
|
17817
|
+
assertWritable(opts.name, opts.content);
|
|
17818
|
+
if (!SKILL_NAME_REGEX.test(opts.name)) {
|
|
17819
|
+
throw new Error(`Invalid skill name: ${opts.name}`);
|
|
17820
|
+
}
|
|
17821
|
+
const ctx = await repoContext(opts);
|
|
17822
|
+
const known = findRepoSkill(ctx, opts.name);
|
|
17823
|
+
const dir = known?.dir || `${AGENTS_ROOT}/${opts.name}`;
|
|
17824
|
+
const { branch } = ctx;
|
|
17825
|
+
const workspace = await ensureSkillsWorktree(opts.spaceDir, branch);
|
|
17826
|
+
const target = `${BRANCH_PREFIX}${opts.name}`;
|
|
17827
|
+
const existing = await hostGit(
|
|
17828
|
+
workspace,
|
|
17829
|
+
"ls-remote",
|
|
17830
|
+
"--heads",
|
|
17831
|
+
"origin",
|
|
17832
|
+
target
|
|
17833
|
+
).catch(() => "");
|
|
17834
|
+
if (existing.trim()) {
|
|
17835
|
+
await hostGit(
|
|
17836
|
+
workspace,
|
|
17837
|
+
"fetch",
|
|
17838
|
+
"origin",
|
|
17839
|
+
`+refs/heads/${target}:refs/remotes/origin/${target}`
|
|
17840
|
+
);
|
|
17841
|
+
await hostGit(workspace, "checkout", "-B", target, `origin/${target}`);
|
|
17842
|
+
} else {
|
|
17843
|
+
await hostGit(workspace, "checkout", "-B", target, `origin/${branch}`);
|
|
17844
|
+
}
|
|
17845
|
+
await hostGit(workspace, "clean", "-fd");
|
|
17846
|
+
if (opts.gitName) {
|
|
17847
|
+
await hostGit(workspace, "config", "user.name", opts.gitName);
|
|
17848
|
+
}
|
|
17849
|
+
if (opts.gitEmail) {
|
|
17850
|
+
await hostGit(workspace, "config", "user.email", opts.gitEmail);
|
|
17851
|
+
}
|
|
17852
|
+
await import_promises5.default.mkdir(import_path8.default.join(workspace, dir), { recursive: true });
|
|
17853
|
+
await import_promises5.default.writeFile(
|
|
17854
|
+
import_path8.default.join(workspace, dir, "SKILL.md"),
|
|
17855
|
+
opts.content,
|
|
17856
|
+
"utf-8"
|
|
17857
|
+
);
|
|
17858
|
+
const dirty = await hostGit(workspace, "status", "--porcelain");
|
|
17859
|
+
if (!dirty.trim()) return { branch: target };
|
|
17860
|
+
await hostGit(workspace, "add", "-A");
|
|
17861
|
+
await hostGit(
|
|
17862
|
+
workspace,
|
|
17863
|
+
"commit",
|
|
17864
|
+
"-m",
|
|
17865
|
+
`${known ? "Update" : "Add"} skill: ${opts.name}`
|
|
17866
|
+
);
|
|
17867
|
+
await hostGit(workspace, "push", "origin", target);
|
|
17868
|
+
return { branch: target };
|
|
17869
|
+
}
|
|
17870
|
+
function listSkills(opts) {
|
|
17871
|
+
return withWorkspace(opts.spaceDir, () => listSkillsImpl(opts));
|
|
17872
|
+
}
|
|
17873
|
+
function readSkill(opts) {
|
|
17874
|
+
return withWorkspace(opts.spaceDir, () => readSkillImpl(opts));
|
|
17875
|
+
}
|
|
17876
|
+
function migrateSkills(opts) {
|
|
17877
|
+
return withWorkspace(opts.spaceDir, () => migrateSkillsImpl(opts));
|
|
17878
|
+
}
|
|
17879
|
+
function proposeRepoSkill(opts) {
|
|
17880
|
+
return withWorkspace(opts.spaceDir, () => proposeRepoSkillImpl(opts));
|
|
17881
|
+
}
|
|
17882
|
+
|
|
16624
17883
|
// ../../shared/all/helpers/board-agent-core/onedrive-auth.ts
|
|
16625
17884
|
var DEVICECODE_URL = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode";
|
|
16626
17885
|
var TOKEN_URL2 = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
|
|
@@ -17365,6 +18624,48 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
17365
18624
|
githubToken: config.githubToken
|
|
17366
18625
|
});
|
|
17367
18626
|
}
|
|
18627
|
+
// ── Agent skills (SKILL.md) ──
|
|
18628
|
+
// Repo opts are optional here, unlike deploy: the global roots are readable
|
|
18629
|
+
// with no repo configured, and listSkills reports repoAvailable: false.
|
|
18630
|
+
skillOpts() {
|
|
18631
|
+
const config = this.core.readConfig();
|
|
18632
|
+
return {
|
|
18633
|
+
spaceDir: this.core.spaceDir(),
|
|
18634
|
+
repoUrl: config.repoUrl,
|
|
18635
|
+
mainBranch: config.mainBranch,
|
|
18636
|
+
githubToken: config.githubToken
|
|
18637
|
+
};
|
|
18638
|
+
}
|
|
18639
|
+
skillsList() {
|
|
18640
|
+
return listSkills(this.skillOpts());
|
|
18641
|
+
}
|
|
18642
|
+
skillRead(payload) {
|
|
18643
|
+
return readSkill({ ...this.skillOpts(), ...payload });
|
|
18644
|
+
}
|
|
18645
|
+
skillWriteGlobal(payload) {
|
|
18646
|
+
return writeGlobalSkill(payload);
|
|
18647
|
+
}
|
|
18648
|
+
skillDeleteGlobal(payload) {
|
|
18649
|
+
return deleteGlobalSkill(payload);
|
|
18650
|
+
}
|
|
18651
|
+
skillMigrate(payload) {
|
|
18652
|
+
const config = this.core.readConfig();
|
|
18653
|
+
return migrateSkills({
|
|
18654
|
+
...this.skillOpts(),
|
|
18655
|
+
...payload,
|
|
18656
|
+
gitName: config.gitName,
|
|
18657
|
+
gitEmail: config.gitEmail
|
|
18658
|
+
});
|
|
18659
|
+
}
|
|
18660
|
+
skillProposeRepo(payload) {
|
|
18661
|
+
const config = this.core.readConfig();
|
|
18662
|
+
return proposeRepoSkill({
|
|
18663
|
+
...this.skillOpts(),
|
|
18664
|
+
...payload,
|
|
18665
|
+
gitName: config.gitName,
|
|
18666
|
+
gitEmail: config.gitEmail
|
|
18667
|
+
});
|
|
18668
|
+
}
|
|
17368
18669
|
deployCreatorState() {
|
|
17369
18670
|
return this.creatorState;
|
|
17370
18671
|
}
|
|
@@ -17747,7 +19048,7 @@ var BoardAgentEngine = class _BoardAgentEngine {
|
|
|
17747
19048
|
// ── Runner secrets store (secrets.sh contract; see host-secrets.ts) ──
|
|
17748
19049
|
secretsDirs() {
|
|
17749
19050
|
const spaceDir = this.core.spaceDir();
|
|
17750
|
-
return { configDir: (0,
|
|
19051
|
+
return { configDir: (0, import_path9.dirname)(spaceDir), spaceDir };
|
|
17751
19052
|
}
|
|
17752
19053
|
async deploySecretsStatus() {
|
|
17753
19054
|
return {
|
|
@@ -18537,6 +19838,28 @@ ${c.content.trim() || "(no description)"}`
|
|
|
18537
19838
|
return await this.bareWorkNames();
|
|
18538
19839
|
case "deployStatus":
|
|
18539
19840
|
return await this.deployStatus();
|
|
19841
|
+
case "skillsList":
|
|
19842
|
+
return await this.skillsList();
|
|
19843
|
+
case "skillRead":
|
|
19844
|
+
return await this.skillRead(
|
|
19845
|
+
payload
|
|
19846
|
+
);
|
|
19847
|
+
case "skillWriteGlobal":
|
|
19848
|
+
return await this.skillWriteGlobal(
|
|
19849
|
+
payload
|
|
19850
|
+
);
|
|
19851
|
+
case "skillDeleteGlobal":
|
|
19852
|
+
return await this.skillDeleteGlobal(
|
|
19853
|
+
payload
|
|
19854
|
+
);
|
|
19855
|
+
case "skillMigrate":
|
|
19856
|
+
return await this.skillMigrate(
|
|
19857
|
+
payload
|
|
19858
|
+
);
|
|
19859
|
+
case "skillProposeRepo":
|
|
19860
|
+
return await this.skillProposeRepo(
|
|
19861
|
+
payload
|
|
19862
|
+
);
|
|
18540
19863
|
case "deployCreateSkill":
|
|
18541
19864
|
return await this.deployCreateSkill(
|
|
18542
19865
|
payload,
|
|
@@ -18748,9 +20071,9 @@ ${c.content.trim() || "(no description)"}`
|
|
|
18748
20071
|
var import_child_process5 = require("child_process");
|
|
18749
20072
|
var import_crypto3 = require("crypto");
|
|
18750
20073
|
var import_fs3 = require("fs");
|
|
18751
|
-
var
|
|
20074
|
+
var import_promises6 = __toESM(require("fs/promises"));
|
|
18752
20075
|
var import_net2 = require("net");
|
|
18753
|
-
var
|
|
20076
|
+
var import_path10 = require("path");
|
|
18754
20077
|
var import_util7 = require("util");
|
|
18755
20078
|
var execFileAsync4 = (0, import_util7.promisify)(import_child_process5.execFile);
|
|
18756
20079
|
var IndexService = class {
|
|
@@ -18809,7 +20132,7 @@ var IndexService = class {
|
|
|
18809
20132
|
const log = await this.openLog();
|
|
18810
20133
|
this.proc = (0, import_child_process5.spawn)(
|
|
18811
20134
|
process.execPath,
|
|
18812
|
-
[(0,
|
|
20135
|
+
[(0, import_path10.join)(this.opts.indexDir, "index.js")],
|
|
18813
20136
|
{
|
|
18814
20137
|
cwd: this.opts.indexDir,
|
|
18815
20138
|
env: {
|
|
@@ -18837,8 +20160,8 @@ var IndexService = class {
|
|
|
18837
20160
|
* opened (a missing log must never stop the service from starting). */
|
|
18838
20161
|
async openLog() {
|
|
18839
20162
|
try {
|
|
18840
|
-
await
|
|
18841
|
-
return (0, import_fs3.openSync)((0,
|
|
20163
|
+
await import_promises6.default.mkdir(this.opts.dataDir, { recursive: true });
|
|
20164
|
+
return (0, import_fs3.openSync)((0, import_path10.join)(this.opts.dataDir, "index.log"), "a");
|
|
18842
20165
|
} catch {
|
|
18843
20166
|
return null;
|
|
18844
20167
|
}
|
|
@@ -18847,10 +20170,10 @@ var IndexService = class {
|
|
|
18847
20170
|
* platform needing node-gyp fails here rather than at query time. */
|
|
18848
20171
|
async installDeps() {
|
|
18849
20172
|
const dir = this.opts.indexDir;
|
|
18850
|
-
if ((0, import_fs3.existsSync)((0,
|
|
18851
|
-
await
|
|
18852
|
-
(0,
|
|
18853
|
-
(0,
|
|
20173
|
+
if ((0, import_fs3.existsSync)((0, import_path10.join)(dir, "node_modules"))) return;
|
|
20174
|
+
await import_promises6.default.copyFile(
|
|
20175
|
+
(0, import_path10.join)(dir, "image-package.json"),
|
|
20176
|
+
(0, import_path10.join)(dir, "package.json")
|
|
18854
20177
|
);
|
|
18855
20178
|
await execFileAsync4(
|
|
18856
20179
|
"npm",
|
|
@@ -18862,15 +20185,15 @@ var IndexService = class {
|
|
|
18862
20185
|
* CLIs' persistent MCP config, so a fresh port each start would leave every
|
|
18863
20186
|
* registration pointing at a dead one. */
|
|
18864
20187
|
async stablePort() {
|
|
18865
|
-
const file = (0,
|
|
18866
|
-
const saved = Number(await
|
|
20188
|
+
const file = (0, import_path10.join)(this.opts.dataDir, ".port");
|
|
20189
|
+
const saved = Number(await import_promises6.default.readFile(file, "utf-8").catch(() => ""));
|
|
18867
20190
|
if (Number.isInteger(saved) && saved > 0 && await isFree(saved)) {
|
|
18868
20191
|
return saved;
|
|
18869
20192
|
}
|
|
18870
20193
|
const port = await freePort();
|
|
18871
|
-
await
|
|
20194
|
+
await import_promises6.default.mkdir(this.opts.dataDir, { recursive: true }).catch(() => {
|
|
18872
20195
|
});
|
|
18873
|
-
await
|
|
20196
|
+
await import_promises6.default.writeFile(file, String(port), "utf-8").catch(() => {
|
|
18874
20197
|
});
|
|
18875
20198
|
return port;
|
|
18876
20199
|
}
|
|
@@ -19038,24 +20361,24 @@ async function pairWithBrowser(serverUrl) {
|
|
|
19038
20361
|
|
|
19039
20362
|
// src/config.ts
|
|
19040
20363
|
var import_fs7 = __toESM(require("fs"));
|
|
19041
|
-
var
|
|
19042
|
-
var
|
|
20364
|
+
var import_os5 = __toESM(require("os"));
|
|
20365
|
+
var import_path15 = __toESM(require("path"));
|
|
19043
20366
|
|
|
19044
20367
|
// src/secureFile.ts
|
|
19045
20368
|
var import_crypto6 = __toESM(require("crypto"));
|
|
19046
20369
|
var import_fs6 = __toESM(require("fs"));
|
|
19047
|
-
var
|
|
20370
|
+
var import_path14 = __toESM(require("path"));
|
|
19048
20371
|
|
|
19049
20372
|
// src/keychain.ts
|
|
19050
20373
|
var import_child_process7 = require("child_process");
|
|
19051
20374
|
var import_crypto4 = __toESM(require("crypto"));
|
|
19052
20375
|
var import_fs4 = __toESM(require("fs"));
|
|
19053
|
-
var
|
|
19054
|
-
var
|
|
20376
|
+
var import_os3 = __toESM(require("os"));
|
|
20377
|
+
var import_path11 = __toESM(require("path"));
|
|
19055
20378
|
var SERVICE = "factiii-runner";
|
|
19056
20379
|
var ACCOUNT = "config-encryption-key";
|
|
19057
|
-
var DPAPI_KEY_FILE =
|
|
19058
|
-
|
|
20380
|
+
var DPAPI_KEY_FILE = import_path11.default.join(
|
|
20381
|
+
import_os3.default.homedir(),
|
|
19059
20382
|
".factiii-runner",
|
|
19060
20383
|
"config-key.dpapi"
|
|
19061
20384
|
);
|
|
@@ -19160,7 +20483,7 @@ function winWrite(key) {
|
|
|
19160
20483
|
"-Command",
|
|
19161
20484
|
`ConvertTo-SecureString -String '${key.toString("base64")}' -AsPlainText -Force | ConvertFrom-SecureString`
|
|
19162
20485
|
]);
|
|
19163
|
-
import_fs4.default.mkdirSync(
|
|
20486
|
+
import_fs4.default.mkdirSync(import_path11.default.dirname(DPAPI_KEY_FILE), { recursive: true });
|
|
19164
20487
|
import_fs4.default.writeFileSync(DPAPI_KEY_FILE, `${out.trim()}
|
|
19165
20488
|
`, { mode: 384 });
|
|
19166
20489
|
}
|
|
@@ -19324,21 +20647,21 @@ function keychainStatus() {
|
|
|
19324
20647
|
// src/secureStore.ts
|
|
19325
20648
|
var import_crypto5 = __toESM(require("crypto"));
|
|
19326
20649
|
var import_fs5 = __toESM(require("fs"));
|
|
19327
|
-
var
|
|
20650
|
+
var import_path13 = __toESM(require("path"));
|
|
19328
20651
|
|
|
19329
20652
|
// src/paths.ts
|
|
19330
|
-
var
|
|
19331
|
-
var
|
|
19332
|
-
var CONFIG_DIR =
|
|
20653
|
+
var import_os4 = __toESM(require("os"));
|
|
20654
|
+
var import_path12 = __toESM(require("path"));
|
|
20655
|
+
var CONFIG_DIR = import_path12.default.join(import_os4.default.homedir(), ".factiii-runner");
|
|
19333
20656
|
function safeSlug(spaceSlug) {
|
|
19334
20657
|
return spaceSlug.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
19335
20658
|
}
|
|
19336
20659
|
function spaceDirPath(spaceSlug) {
|
|
19337
|
-
return
|
|
20660
|
+
return import_path12.default.join(CONFIG_DIR, safeSlug(spaceSlug));
|
|
19338
20661
|
}
|
|
19339
20662
|
|
|
19340
20663
|
// src/secureStore.ts
|
|
19341
|
-
var VAULT_KEY_FILE =
|
|
20664
|
+
var VAULT_KEY_FILE = import_path13.default.join(CONFIG_DIR, "vault-key.json");
|
|
19342
20665
|
var SCRYPT_N2 = 1 << 15;
|
|
19343
20666
|
var MIN_PASSWORD_LENGTH = 8;
|
|
19344
20667
|
var SecureStoreError = class extends Error {
|
|
@@ -19660,7 +20983,7 @@ function parseEnvelope(raw) {
|
|
|
19660
20983
|
}
|
|
19661
20984
|
}
|
|
19662
20985
|
function atomicWrite(filePath, body) {
|
|
19663
|
-
import_fs6.default.mkdirSync(
|
|
20986
|
+
import_fs6.default.mkdirSync(import_path14.default.dirname(filePath), { recursive: true });
|
|
19664
20987
|
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
19665
20988
|
import_fs6.default.writeFileSync(tmp, body, { mode: 384 });
|
|
19666
20989
|
import_fs6.default.renameSync(tmp, filePath);
|
|
@@ -19775,8 +21098,8 @@ function writeBootJson(filePath, value2) {
|
|
|
19775
21098
|
}
|
|
19776
21099
|
|
|
19777
21100
|
// src/config.ts
|
|
19778
|
-
var CONFIG_DIR2 =
|
|
19779
|
-
var CONFIG_FILE =
|
|
21101
|
+
var CONFIG_DIR2 = import_path15.default.join(import_os5.default.homedir(), ".factiii-runner");
|
|
21102
|
+
var CONFIG_FILE = import_path15.default.join(CONFIG_DIR2, "config.json");
|
|
19780
21103
|
function readRunnerConfig() {
|
|
19781
21104
|
return readBootJson(CONFIG_FILE);
|
|
19782
21105
|
}
|
|
@@ -19948,7 +21271,7 @@ function makeChunkReassembler() {
|
|
|
19948
21271
|
var import_crypto7 = require("crypto");
|
|
19949
21272
|
var import_fs9 = __toESM(require("fs"));
|
|
19950
21273
|
var import_node_datachannel = require("node-datachannel");
|
|
19951
|
-
var
|
|
21274
|
+
var import_path17 = __toESM(require("path"));
|
|
19952
21275
|
|
|
19953
21276
|
// ../../node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.js
|
|
19954
21277
|
var XMLHttpRequestModule = __toESM(require_XMLHttpRequest(), 1);
|
|
@@ -21128,12 +22451,12 @@ function parse2(str) {
|
|
|
21128
22451
|
uri.queryKey = queryKey(uri, uri["query"]);
|
|
21129
22452
|
return uri;
|
|
21130
22453
|
}
|
|
21131
|
-
function pathNames(obj,
|
|
21132
|
-
const regx = /\/{2,9}/g, names =
|
|
21133
|
-
if (
|
|
22454
|
+
function pathNames(obj, path17) {
|
|
22455
|
+
const regx = /\/{2,9}/g, names = path17.replace(regx, "/").split("/");
|
|
22456
|
+
if (path17.slice(0, 1) == "/" || path17.length === 0) {
|
|
21134
22457
|
names.splice(0, 1);
|
|
21135
22458
|
}
|
|
21136
|
-
if (
|
|
22459
|
+
if (path17.slice(-1) == "/") {
|
|
21137
22460
|
names.splice(names.length - 1, 1);
|
|
21138
22461
|
}
|
|
21139
22462
|
return names;
|
|
@@ -21752,7 +23075,7 @@ var protocol2 = Socket.protocol;
|
|
|
21752
23075
|
// ../../node_modules/socket.io-client/build/esm-debug/url.js
|
|
21753
23076
|
var import_debug7 = __toESM(require_src(), 1);
|
|
21754
23077
|
var debug7 = (0, import_debug7.default)("socket.io-client:url");
|
|
21755
|
-
function url(uri,
|
|
23078
|
+
function url(uri, path17 = "", loc) {
|
|
21756
23079
|
let obj = uri;
|
|
21757
23080
|
loc = loc || typeof location !== "undefined" && location;
|
|
21758
23081
|
if (null == uri)
|
|
@@ -21786,7 +23109,7 @@ function url(uri, path16 = "", loc) {
|
|
|
21786
23109
|
obj.path = obj.path || "/";
|
|
21787
23110
|
const ipv6 = obj.host.indexOf(":") !== -1;
|
|
21788
23111
|
const host = ipv6 ? "[" + obj.host + "]" : obj.host;
|
|
21789
|
-
obj.id = obj.protocol + "://" + host + ":" + obj.port +
|
|
23112
|
+
obj.id = obj.protocol + "://" + host + ":" + obj.port + path17;
|
|
21790
23113
|
obj.href = obj.protocol + "://" + host + (loc && loc.port === obj.port ? "" : ":" + obj.port);
|
|
21791
23114
|
return obj;
|
|
21792
23115
|
}
|
|
@@ -23420,8 +24743,8 @@ function lookup(uri, opts) {
|
|
|
23420
24743
|
const parsed = url(uri, opts.path || "/socket.io");
|
|
23421
24744
|
const source = parsed.source;
|
|
23422
24745
|
const id = parsed.id;
|
|
23423
|
-
const
|
|
23424
|
-
const sameNamespace = cache[id] &&
|
|
24746
|
+
const path17 = parsed.path;
|
|
24747
|
+
const sameNamespace = cache[id] && path17 in cache[id]["nsps"];
|
|
23425
24748
|
const newConnection = opts.forceNew || opts["force new connection"] || false === opts.multiplex || sameNamespace;
|
|
23426
24749
|
let io;
|
|
23427
24750
|
if (newConnection) {
|
|
@@ -23463,8 +24786,8 @@ function cardContextPath(target, postId) {
|
|
|
23463
24786
|
const safe = postId.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80) || "card";
|
|
23464
24787
|
return `${target.paths.root}/.cards/${safe}.md`;
|
|
23465
24788
|
}
|
|
23466
|
-
function cardContextPrompt(
|
|
23467
|
-
return `Read ${
|
|
24789
|
+
function cardContextPrompt(path17) {
|
|
24790
|
+
return `Read ${path17} - it is the Factiii card this session was opened from. Use it as the context and start working on it.`;
|
|
23468
24791
|
}
|
|
23469
24792
|
var CLAUDE_PERMISSION_MODE = `--permission-mode auto`;
|
|
23470
24793
|
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.";
|
|
@@ -23629,10 +24952,10 @@ var TerminalManager = class {
|
|
|
23629
24952
|
|
|
23630
24953
|
// src/agent-adapter.ts
|
|
23631
24954
|
var import_fs8 = __toESM(require("fs"));
|
|
23632
|
-
var
|
|
23633
|
-
var RUNNER_CONFIG_PATH =
|
|
24955
|
+
var import_path16 = __toESM(require("path"));
|
|
24956
|
+
var RUNNER_CONFIG_PATH = import_path16.default.join(CONFIG_DIR, "runner-config.json");
|
|
23634
24957
|
function connectionPath(spaceSlug) {
|
|
23635
|
-
return
|
|
24958
|
+
return import_path16.default.join(CONFIG_DIR, `space-${safeSlug(spaceSlug)}.onedrive.json`);
|
|
23636
24959
|
}
|
|
23637
24960
|
function writeJson(filePath, value2) {
|
|
23638
24961
|
writeSecureJson(filePath, value2);
|
|
@@ -23666,7 +24989,7 @@ function migrateLegacyConfigs() {
|
|
|
23666
24989
|
for (const name of legacy) {
|
|
23667
24990
|
const slug = name.slice("space-".length, -".json".length);
|
|
23668
24991
|
const parsed = readSecureJson(
|
|
23669
|
-
|
|
24992
|
+
import_path16.default.join(CONFIG_DIR, name)
|
|
23670
24993
|
);
|
|
23671
24994
|
if (!parsed) continue;
|
|
23672
24995
|
if (!file.defaultSlug) {
|
|
@@ -23691,7 +25014,7 @@ function encryptStrayPlaintextConfigs() {
|
|
|
23691
25014
|
return;
|
|
23692
25015
|
}
|
|
23693
25016
|
for (const name of names) {
|
|
23694
|
-
const filePath =
|
|
25017
|
+
const filePath = import_path16.default.join(CONFIG_DIR, name);
|
|
23695
25018
|
try {
|
|
23696
25019
|
const raw = import_fs8.default.readFileSync(filePath, "utf-8");
|
|
23697
25020
|
if (!raw.startsWith("{")) continue;
|
|
@@ -23820,7 +25143,7 @@ var localConfigProvider = {
|
|
|
23820
25143
|
"claude",
|
|
23821
25144
|
"codex"
|
|
23822
25145
|
]) {
|
|
23823
|
-
import_fs8.default.mkdirSync(
|
|
25146
|
+
import_fs8.default.mkdirSync(import_path16.default.join(dir, sub), { recursive: true });
|
|
23824
25147
|
}
|
|
23825
25148
|
return dir;
|
|
23826
25149
|
}
|
|
@@ -23842,6 +25165,7 @@ function toRtcIceServers(servers) {
|
|
|
23842
25165
|
continue;
|
|
23843
25166
|
}
|
|
23844
25167
|
const [, scheme, hostname, port, transport] = turn;
|
|
25168
|
+
if (scheme === "turns" || transport === "tcp") continue;
|
|
23845
25169
|
out.push({
|
|
23846
25170
|
hostname,
|
|
23847
25171
|
port: Number(port ?? (scheme === "turns" ? 5349 : 3478)),
|
|
@@ -23993,15 +25317,15 @@ async function startDaemon(config) {
|
|
|
23993
25317
|
}
|
|
23994
25318
|
);
|
|
23995
25319
|
const indexCandidates = [
|
|
23996
|
-
|
|
23997
|
-
|
|
25320
|
+
import_path17.default.join(__dirname, "..", "index"),
|
|
25321
|
+
import_path17.default.join(__dirname, "..", "..", "index", "image")
|
|
23998
25322
|
];
|
|
23999
25323
|
const indexDir = indexCandidates.find((p) => import_fs9.default.existsSync(p)) ?? indexCandidates[0];
|
|
24000
25324
|
const odIndex = new IndexService({
|
|
24001
25325
|
controlToken: (0, import_crypto7.createHash)("sha256").update(config.authToken).digest("hex"),
|
|
24002
25326
|
embedUrl: () => config.serverUrl,
|
|
24003
25327
|
indexDir,
|
|
24004
|
-
dataDir:
|
|
25328
|
+
dataDir: import_path17.default.join(CONFIG_DIR, "index-data")
|
|
24005
25329
|
});
|
|
24006
25330
|
void odIndex.ensureRunning().catch((err) => {
|
|
24007
25331
|
console.error(
|
|
@@ -24151,9 +25475,37 @@ async function startDaemon(config) {
|
|
|
24151
25475
|
).join(", ")}`
|
|
24152
25476
|
);
|
|
24153
25477
|
const peer = new import_node_datachannel.PeerConnection("runner", { iceServers });
|
|
25478
|
+
const view = {
|
|
25479
|
+
local: /* @__PURE__ */ new Set(),
|
|
25480
|
+
remote: /* @__PURE__ */ new Set(),
|
|
25481
|
+
relays: 0,
|
|
25482
|
+
gathering: "new",
|
|
25483
|
+
ice: "new"
|
|
25484
|
+
};
|
|
25485
|
+
const candidateType = (candidate) => /\btyp (\w+)/.exec(candidate)?.[1] ?? "unknown";
|
|
25486
|
+
let reported = false;
|
|
25487
|
+
const report = (outcome) => {
|
|
25488
|
+
if (reported || !data.linkId) return;
|
|
25489
|
+
reported = true;
|
|
25490
|
+
socket.emit("runnerLinkReport", {
|
|
25491
|
+
linkId: data.linkId,
|
|
25492
|
+
peerSocketId: data.fromSocketId,
|
|
25493
|
+
outcome,
|
|
25494
|
+
detail: [
|
|
25495
|
+
`ice=${view.ice} gathering=${view.gathering} relays=${view.relays}`,
|
|
25496
|
+
`local=${[...view.local].join(",") || "none"}`,
|
|
25497
|
+
`remote=${[...view.remote].join(",") || "none"}`,
|
|
25498
|
+
// Hostname and port only — the credential never leaves the runner.
|
|
25499
|
+
`turn=${iceServers.map(
|
|
25500
|
+
(u) => typeof u === "string" ? u : `${u.relayType}:${u.hostname}:${u.port}`
|
|
25501
|
+
).join(" ")}`
|
|
25502
|
+
].join("\n")
|
|
25503
|
+
});
|
|
25504
|
+
};
|
|
24154
25505
|
const iceCandidateHandler = (msg) => {
|
|
24155
25506
|
if (msg.fromSocketId === data.fromSocketId) {
|
|
24156
25507
|
console.log(`[webrtc] remote ICE candidate: ${msg.candidate.candidate.slice(0, 60)}...`);
|
|
25508
|
+
view.remote.add(candidateType(msg.candidate.candidate));
|
|
24157
25509
|
peer.addRemoteCandidate(
|
|
24158
25510
|
msg.candidate.candidate,
|
|
24159
25511
|
msg.candidate.sdpMid || ""
|
|
@@ -24169,18 +25521,31 @@ async function startDaemon(config) {
|
|
|
24169
25521
|
};
|
|
24170
25522
|
peer.onStateChange((state) => {
|
|
24171
25523
|
console.log(`[webrtc] peer state: ${state}`);
|
|
25524
|
+
view.ice = state;
|
|
25525
|
+
if (state === "connected") report("connected");
|
|
24172
25526
|
if (state === "failed" || state === "closed" || state === "disconnected") {
|
|
25527
|
+
if (state === "failed") report("failed");
|
|
24173
25528
|
detachIceHandler();
|
|
24174
25529
|
}
|
|
24175
25530
|
});
|
|
24176
25531
|
peer.onGatheringStateChange((state) => {
|
|
24177
25532
|
console.log(`[webrtc] gathering state: ${state}`);
|
|
25533
|
+
view.gathering = state;
|
|
24178
25534
|
});
|
|
24179
25535
|
peer.onLocalCandidate((candidate, mid) => {
|
|
24180
25536
|
console.log(`[webrtc] local ICE candidate: ${candidate.slice(0, 60)}...`);
|
|
25537
|
+
const type = candidateType(candidate);
|
|
25538
|
+
view.local.add(type);
|
|
25539
|
+
if (type === "relay") view.relays += 1;
|
|
24181
25540
|
socket.emit("runnerIceCandidate", {
|
|
24182
25541
|
targetSocketId: data.fromSocketId,
|
|
24183
|
-
|
|
25542
|
+
// 0 rather than null. A browser accepts a null index, but a mobile
|
|
25543
|
+
// client on the New Architecture passes this straight into codegen'd
|
|
25544
|
+
// JSI bindings that type-check it as a number, and null throws there —
|
|
25545
|
+
// rejecting every candidate we send and stranding the link at
|
|
25546
|
+
// checking. sdpMid identifies the m-line on its own, so the index is
|
|
25547
|
+
// redundant here anyway.
|
|
25548
|
+
candidate: { candidate, sdpMid: mid, sdpMLineIndex: 0 }
|
|
24184
25549
|
});
|
|
24185
25550
|
});
|
|
24186
25551
|
peer.onLocalDescription((sdp, _type) => {
|