runwork 0.25.1 → 0.25.3
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/index.js +683 -233
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -61,7 +61,42 @@ import {
|
|
|
61
61
|
createInflate,
|
|
62
62
|
createInflateRaw
|
|
63
63
|
} from "node:zlib";
|
|
64
|
-
function
|
|
64
|
+
function firstEnv(names) {
|
|
65
|
+
for (const variable of names) {
|
|
66
|
+
const value = process.env[variable];
|
|
67
|
+
if (value && value.trim() !== "")
|
|
68
|
+
return { variable, value: value.trim() };
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
function isProxyExempt(hostname) {
|
|
73
|
+
const raw = process.env.NO_PROXY ?? process.env.no_proxy ?? "";
|
|
74
|
+
const host = hostname.toLowerCase();
|
|
75
|
+
for (const entry of raw.split(",")) {
|
|
76
|
+
const rule = entry.trim().toLowerCase().replace(/:\d+$/, "");
|
|
77
|
+
if (!rule)
|
|
78
|
+
continue;
|
|
79
|
+
if (rule === "*")
|
|
80
|
+
return true;
|
|
81
|
+
const bare = rule.startsWith(".") ? rule.slice(1) : rule;
|
|
82
|
+
if (host === bare || host.endsWith(`.${bare}`))
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
function proxyForUrl(url) {
|
|
88
|
+
let parsed;
|
|
89
|
+
try {
|
|
90
|
+
parsed = new URL2(url);
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
if (isProxyExempt(parsed.hostname))
|
|
95
|
+
return null;
|
|
96
|
+
const schemeVars = parsed.protocol === "http:" ? ["HTTP_PROXY", "http_proxy"] : ["HTTPS_PROXY", "https_proxy"];
|
|
97
|
+
return firstEnv([...schemeVars, "ALL_PROXY", "all_proxy"]);
|
|
98
|
+
}
|
|
99
|
+
function pickTransport(url) {
|
|
65
100
|
if (transportOverride === "curl")
|
|
66
101
|
return "curl";
|
|
67
102
|
if (transportOverride === "node")
|
|
@@ -69,10 +104,14 @@ function pickTransport() {
|
|
|
69
104
|
const env = (process.env.RUNWORK_HTTP_TRANSPORT || "").toLowerCase().trim();
|
|
70
105
|
if (env === "curl")
|
|
71
106
|
return "curl";
|
|
107
|
+
if (env === "node")
|
|
108
|
+
return "node";
|
|
109
|
+
if (url && proxyForUrl(url))
|
|
110
|
+
return "curl";
|
|
72
111
|
return "node";
|
|
73
112
|
}
|
|
74
113
|
async function httpFetch(url, init = {}) {
|
|
75
|
-
const transport = pickTransport();
|
|
114
|
+
const transport = pickTransport(url);
|
|
76
115
|
if (transport === "curl")
|
|
77
116
|
return curlFetch(url, init);
|
|
78
117
|
return doRequest(url, init, 0);
|
|
@@ -1020,26 +1059,36 @@ function buildHelperValue(execPath, scriptPath) {
|
|
|
1020
1059
|
return `!"${normalised}" git-credential-helper`;
|
|
1021
1060
|
}
|
|
1022
1061
|
async function configureGitCredentials(remoteUrl) {
|
|
1023
|
-
|
|
1062
|
+
let origin;
|
|
1063
|
+
try {
|
|
1064
|
+
origin = new URL(remoteUrl).origin;
|
|
1065
|
+
} catch {
|
|
1066
|
+
const message = `"${remoteUrl}" is not a valid URL.`;
|
|
1067
|
+
console.warn(`Note: ${message}`);
|
|
1068
|
+
return { ok: false, reason: "invalid-url", message };
|
|
1069
|
+
}
|
|
1024
1070
|
const key = `credential.${origin}.helper`;
|
|
1025
1071
|
const helperValue = buildHelperValue(process.execPath, process.argv[1]);
|
|
1026
1072
|
try {
|
|
1027
1073
|
try {
|
|
1028
1074
|
execFileSync("git", ["config", "--global", "--unset-all", key], { stdio: "pipe" });
|
|
1029
|
-
} catch
|
|
1030
|
-
if (unsetErr?.code === "ENOENT")
|
|
1031
|
-
throw unsetErr;
|
|
1032
|
-
}
|
|
1075
|
+
} catch {}
|
|
1033
1076
|
execFileSync("git", ["config", "--global", "--add", key, ""], { stdio: "pipe" });
|
|
1034
1077
|
execFileSync("git", ["config", "--global", "--add", key, helperValue], { stdio: "pipe" });
|
|
1078
|
+
return { ok: true };
|
|
1035
1079
|
} catch (err) {
|
|
1036
1080
|
const code = err?.code;
|
|
1037
1081
|
if (code === "ENOENT") {
|
|
1038
|
-
|
|
1039
|
-
console.warn(
|
|
1040
|
-
return;
|
|
1082
|
+
const message2 = "git is not installed. Install git before running `runwork init`, `clone`, `dev`, or `deploy`.";
|
|
1083
|
+
console.warn(`Note: ${message2}`);
|
|
1084
|
+
return { ok: false, reason: "missing", message: message2 };
|
|
1041
1085
|
}
|
|
1042
|
-
|
|
1086
|
+
const detail = err instanceof Error ? err.message.split(`
|
|
1087
|
+
`)[0] : String(err);
|
|
1088
|
+
const xcodeHint = process.platform === "darwin" ? " On macOS this usually means the Xcode Command Line Tools are missing; run `xcode-select --install`." : "";
|
|
1089
|
+
const message = `git is installed but could not be run (${detail}).${xcodeHint}`;
|
|
1090
|
+
console.warn(`Note: ${message}`);
|
|
1091
|
+
return { ok: false, reason: "unusable", message };
|
|
1043
1092
|
}
|
|
1044
1093
|
}
|
|
1045
1094
|
function lookupCredentialHelper(origin) {
|
|
@@ -1097,13 +1146,13 @@ async function ensureGitCredentialHelper(baseUrl) {
|
|
|
1097
1146
|
try {
|
|
1098
1147
|
origin = new URL(baseUrl).origin;
|
|
1099
1148
|
} catch {
|
|
1100
|
-
return;
|
|
1149
|
+
return { ok: false, reason: "invalid-url", message: `"${baseUrl}" is not a valid URL.` };
|
|
1101
1150
|
}
|
|
1102
1151
|
const lookup = lookupCredentialHelper(origin);
|
|
1103
1152
|
if (lookup.status === "registered" && helperBinaryStatus(lookup.value).ok && lookup.hasReset) {
|
|
1104
|
-
return;
|
|
1153
|
+
return { ok: true };
|
|
1105
1154
|
}
|
|
1106
|
-
|
|
1155
|
+
return configureGitCredentials(baseUrl);
|
|
1107
1156
|
}
|
|
1108
1157
|
async function removeGitCredentials(baseUrl) {
|
|
1109
1158
|
const origin = new URL(baseUrl).origin;
|
|
@@ -7973,7 +8022,7 @@ function createKeyboardListener() {
|
|
|
7973
8022
|
}
|
|
7974
8023
|
|
|
7975
8024
|
// src/generated/version.ts
|
|
7976
|
-
var VERSION = "0.25.
|
|
8025
|
+
var VERSION = "0.25.3";
|
|
7977
8026
|
|
|
7978
8027
|
// src/commands/dev.ts
|
|
7979
8028
|
var exports_dev = {};
|
|
@@ -8902,6 +8951,32 @@ var init_dev = __esm(() => {
|
|
|
8902
8951
|
devCommand.addCommand(devAttachCommand);
|
|
8903
8952
|
});
|
|
8904
8953
|
|
|
8954
|
+
// src/agents/detection-probes.ts
|
|
8955
|
+
function powershellQuote(value) {
|
|
8956
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
8957
|
+
}
|
|
8958
|
+
function isValidBundleId(id) {
|
|
8959
|
+
return /^[A-Za-z0-9][A-Za-z0-9.-]*$/.test(id);
|
|
8960
|
+
}
|
|
8961
|
+
function macosBundleIdProbeScript(id) {
|
|
8962
|
+
return `p=$(mdfind "kMDItemCFBundleIdentifier == '${id}'" 2>/dev/null | head -1); ` + `if [ -n "$p" ]; then exit 0; fi; ` + `for a in /Applications/*.app "$HOME"/Applications/*.app; do ` + `[ -e "$a" ] || continue; ` + `if [ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$a/Contents/Info.plist" 2>/dev/null)" = "${id}" ]; then exit 0; fi; ` + `done; exit 1`;
|
|
8963
|
+
}
|
|
8964
|
+
function appxPackageProbeScript(pkg) {
|
|
8965
|
+
return `$p = Get-AppxPackage -Name ${powershellQuote(pkg)} -ErrorAction SilentlyContinue; if ($null -ne $p) { exit 0 } exit 1`;
|
|
8966
|
+
}
|
|
8967
|
+
function startAppProbeScript(pattern) {
|
|
8968
|
+
const p = powershellQuote(pattern);
|
|
8969
|
+
return `$a = Get-StartApps -ErrorAction SilentlyContinue | Where-Object { $_.Name -like ${p} -or $_.AppID -like ${p} } | Select-Object -First 1; if ($null -ne $a) { exit 0 } exit 1`;
|
|
8970
|
+
}
|
|
8971
|
+
function isCommandNotFoundExit(code) {
|
|
8972
|
+
return typeof code === "number" && COMMAND_NOT_FOUND_EXIT_CODES.includes(code);
|
|
8973
|
+
}
|
|
8974
|
+
var PATH_REFRESH_FAILED_MARKER = "__runwork_path_refresh_failed__", WINDOWS_PATH_REFRESH, COMMAND_NOT_FOUND_EXIT_CODES;
|
|
8975
|
+
var init_detection_probes = __esm(() => {
|
|
8976
|
+
WINDOWS_PATH_REFRESH = "try { $env:Path = [Environment]::GetEnvironmentVariable('Path','Machine') + ';' + " + "[Environment]::GetEnvironmentVariable('Path','User') + ';' + $env:Path } " + `catch { Write-Output '${PATH_REFRESH_FAILED_MARKER}' }; `;
|
|
8977
|
+
COMMAND_NOT_FOUND_EXIT_CODES = [127, 9009];
|
|
8978
|
+
});
|
|
8979
|
+
|
|
8905
8980
|
// src/utils/which.ts
|
|
8906
8981
|
import { platform } from "os";
|
|
8907
8982
|
import { isAbsolute } from "path";
|
|
@@ -8922,11 +8997,25 @@ function whichAllLines(name) {
|
|
|
8922
8997
|
return [];
|
|
8923
8998
|
}
|
|
8924
8999
|
}
|
|
8925
|
-
function
|
|
9000
|
+
function classifyProbeError(err) {
|
|
9001
|
+
const e = err;
|
|
9002
|
+
if (!e || typeof e !== "object")
|
|
9003
|
+
return "spawn-error";
|
|
9004
|
+
if (e.signal || e.code === "ETIMEDOUT")
|
|
9005
|
+
return "timeout";
|
|
9006
|
+
if (e.code === "ENOENT")
|
|
9007
|
+
return "not-found";
|
|
9008
|
+
if (isCommandNotFoundExit(e.status))
|
|
9009
|
+
return "not-found";
|
|
9010
|
+
if (typeof e.status === "number")
|
|
9011
|
+
return "exit-nonzero";
|
|
9012
|
+
return "spawn-error";
|
|
9013
|
+
}
|
|
9014
|
+
function probeBinaryRunnable(binary) {
|
|
8926
9015
|
const cached = runnableCache.get(binary);
|
|
8927
9016
|
if (cached !== undefined)
|
|
8928
9017
|
return cached;
|
|
8929
|
-
let
|
|
9018
|
+
let probe;
|
|
8930
9019
|
try {
|
|
8931
9020
|
const spec = toSpawnSpec(binary, ["--version"]);
|
|
8932
9021
|
execFileSync(spec.command, spec.args, {
|
|
@@ -8934,12 +9023,16 @@ function isBinaryRunnable(binary) {
|
|
|
8934
9023
|
timeout: RUNNABLE_PROBE_TIMEOUT_MS,
|
|
8935
9024
|
...spec.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
8936
9025
|
});
|
|
8937
|
-
|
|
8938
|
-
} catch {
|
|
8939
|
-
|
|
9026
|
+
probe = { runnable: true, reason: "ran" };
|
|
9027
|
+
} catch (err) {
|
|
9028
|
+
probe = { runnable: false, reason: classifyProbeError(err) };
|
|
8940
9029
|
}
|
|
8941
|
-
|
|
8942
|
-
|
|
9030
|
+
if (probe.reason !== "timeout")
|
|
9031
|
+
runnableCache.set(binary, probe);
|
|
9032
|
+
return probe;
|
|
9033
|
+
}
|
|
9034
|
+
function isBinaryRunnable(binary) {
|
|
9035
|
+
return probeBinaryRunnable(binary).runnable;
|
|
8943
9036
|
}
|
|
8944
9037
|
function toSpawnSpec(binary, args) {
|
|
8945
9038
|
if (isAbsolute(binary)) {
|
|
@@ -8987,6 +9080,7 @@ function isAppRunning(appName) {
|
|
|
8987
9080
|
var RUNNABLE_PROBE_TIMEOUT_MS = 1e4, runnableCache;
|
|
8988
9081
|
var init_which = __esm(() => {
|
|
8989
9082
|
init_subprocess();
|
|
9083
|
+
init_detection_probes();
|
|
8990
9084
|
runnableCache = new Map;
|
|
8991
9085
|
});
|
|
8992
9086
|
|
|
@@ -9105,7 +9199,10 @@ var init_resolve = __esm(() => {
|
|
|
9105
9199
|
|
|
9106
9200
|
// ../../shared/skill/skill-canonical.ts
|
|
9107
9201
|
function toSkillSlug(value) {
|
|
9108
|
-
return value.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
9202
|
+
return transliterateLatin(value.trim()).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
9203
|
+
}
|
|
9204
|
+
function transliterateLatin(value) {
|
|
9205
|
+
return value.replace(/[ıßøØłŁđĐæÆœŒþÞðÐ]/g, (ch) => NON_DECOMPOSABLE_LATIN[ch] ?? ch).normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
9109
9206
|
}
|
|
9110
9207
|
function isStructuredYamlValue(value) {
|
|
9111
9208
|
if (value.includes(`
|
|
@@ -9150,11 +9247,13 @@ function parseSkillMd(content) {
|
|
|
9150
9247
|
const lines = content.split(`
|
|
9151
9248
|
`);
|
|
9152
9249
|
if (lines[0]?.trim() !== "---") {
|
|
9153
|
-
const
|
|
9250
|
+
const firstLine = lines[0]?.trim() ?? "";
|
|
9251
|
+
const isHeading = /^#+\s/.test(firstLine);
|
|
9252
|
+
const rawName = firstLine.replace(/^#+\s*/, "").trim() || "Untitled Skill";
|
|
9154
9253
|
return {
|
|
9155
9254
|
frontmatter: {
|
|
9156
9255
|
name: toSkillSlug(rawName) || "untitled-skill",
|
|
9157
|
-
description: ""
|
|
9256
|
+
description: isHeading ? rawName : ""
|
|
9158
9257
|
},
|
|
9159
9258
|
orderedKeys: [],
|
|
9160
9259
|
body: content,
|
|
@@ -9241,6 +9340,27 @@ function buildSkillMd(parts) {
|
|
|
9241
9340
|
return lines.join(`
|
|
9242
9341
|
`);
|
|
9243
9342
|
}
|
|
9343
|
+
var NON_DECOMPOSABLE_LATIN;
|
|
9344
|
+
var init_skill_canonical = __esm(() => {
|
|
9345
|
+
NON_DECOMPOSABLE_LATIN = {
|
|
9346
|
+
"ı": "i",
|
|
9347
|
+
"ß": "ss",
|
|
9348
|
+
"ø": "o",
|
|
9349
|
+
"Ø": "O",
|
|
9350
|
+
"ł": "l",
|
|
9351
|
+
"Ł": "L",
|
|
9352
|
+
"đ": "d",
|
|
9353
|
+
"Đ": "D",
|
|
9354
|
+
"æ": "ae",
|
|
9355
|
+
"Æ": "AE",
|
|
9356
|
+
"œ": "oe",
|
|
9357
|
+
"Œ": "OE",
|
|
9358
|
+
"þ": "th",
|
|
9359
|
+
"Þ": "TH",
|
|
9360
|
+
"ð": "d",
|
|
9361
|
+
"Ð": "D"
|
|
9362
|
+
};
|
|
9363
|
+
});
|
|
9244
9364
|
|
|
9245
9365
|
// src/agents/registry-data.ts
|
|
9246
9366
|
function chatgptConnectorSteps(confirmLead) {
|
|
@@ -9784,7 +9904,7 @@ var init_registry_data = __esm(() => {
|
|
|
9784
9904
|
name: "GitHub Copilot CLI",
|
|
9785
9905
|
description: "GitHub Copilot in the terminal",
|
|
9786
9906
|
category: "cli",
|
|
9787
|
-
detection: { method: "binary", target: "
|
|
9907
|
+
detection: { method: "binary", target: "copilot" },
|
|
9788
9908
|
logo: "vscode",
|
|
9789
9909
|
skillsPaths: { global: ".copilot/skills", project: ".github/skills" },
|
|
9790
9910
|
mcpConfigPath: ".copilot/mcp-config.json",
|
|
@@ -9805,7 +9925,7 @@ var init_registry_data = __esm(() => {
|
|
|
9805
9925
|
{ slug: "droid", name: "Droid", aliases: ["Droid (Factory AI)"], description: "Factory AI's coding agent", category: "cli", detection: { method: "binary", target: "droid" }, skillsPaths: { global: ".factory/skills", project: ".factory/skills" } },
|
|
9806
9926
|
{ slug: "firebender", name: "Firebender", description: "AI coding agent", category: "cli", detection: { method: "binary", target: "firebender" }, skillsPaths: { global: ".firebender/skills", project: ".firebender/skills" } },
|
|
9807
9927
|
{ slug: "goose", name: "Goose", description: "AI coding agent by Block", category: "cli", detection: { method: "binary", target: "goose" }, skillsPaths: { global: ".config/goose/skills", project: ".goose/skills" } },
|
|
9808
|
-
{ slug: "hermes", name: "Hermes", aliases: ["hermes-agent"], description: "AI coding agent", category: "cli", detection: { method: "binary", target: "hermes" }, skillsPaths: { global: ".hermes/skills", project: ".hermes/skills" } },
|
|
9928
|
+
{ slug: "hermes", name: "Hermes", aliases: ["hermes-agent"], description: "AI coding agent", category: "cli", detection: { method: "binary", target: "hermes" }, launch: { cli: "hermes" }, skillsPaths: { global: ".hermes/skills", project: ".hermes/skills" } },
|
|
9809
9929
|
{ slug: "iflow", name: "iFlow CLI", aliases: ["iflow-cli"], description: "AI coding agent", category: "cli", detection: { method: "binary", target: "iflow" }, skillsPaths: { global: ".iflow/skills", project: ".iflow/skills" } },
|
|
9810
9930
|
{ slug: "junie", name: "Junie", description: "JetBrains AI coding agent", category: "ide", detection: { method: "binary", target: "junie" }, skillsPaths: { global: ".junie/skills", project: ".junie/skills" } },
|
|
9811
9931
|
{ slug: "kilocode", name: "Kilo Code", aliases: ["kilo"], description: "AI coding agent", category: "extension", detection: { method: "binary", target: "kilocode" }, skillsPaths: { global: ".kilocode/skills", project: ".kilocode/skills" } },
|
|
@@ -10879,6 +10999,7 @@ function buildSkillMd2(skill) {
|
|
|
10879
10999
|
}
|
|
10880
11000
|
var RUNWORK_MCP_PREFIX = "Runwork: ", RUNWORK_MCP_PREFIX_LEGACY = "runwork-", RUNWORK_WORKSPACE_MCP_NAME = "Runwork", RUNWORK_PLUGIN_MARKETPLACE = "runwork", toSlug;
|
|
10881
11001
|
var init_types = __esm(() => {
|
|
11002
|
+
init_skill_canonical();
|
|
10882
11003
|
toSlug = toSkillSlug;
|
|
10883
11004
|
});
|
|
10884
11005
|
|
|
@@ -10997,11 +11118,33 @@ function readJsonConfig(filePath) {
|
|
|
10997
11118
|
return {};
|
|
10998
11119
|
try {
|
|
10999
11120
|
return JSON.parse(readFileSync23(filePath, "utf-8"));
|
|
11000
|
-
} catch {
|
|
11121
|
+
} catch (err) {
|
|
11122
|
+
console.warn(` [config] ${filePath} is not valid JSON: ${err instanceof Error ? err.message : err}`);
|
|
11001
11123
|
return {};
|
|
11002
11124
|
}
|
|
11003
11125
|
}
|
|
11126
|
+
function existingContentIsUnparseable(filePath) {
|
|
11127
|
+
if (!existsSync28(filePath))
|
|
11128
|
+
return { bad: false };
|
|
11129
|
+
let raw;
|
|
11130
|
+
try {
|
|
11131
|
+
raw = readFileSync23(filePath, "utf-8");
|
|
11132
|
+
} catch {
|
|
11133
|
+
return { bad: false };
|
|
11134
|
+
}
|
|
11135
|
+
if (raw.trim() === "")
|
|
11136
|
+
return { bad: false };
|
|
11137
|
+
try {
|
|
11138
|
+
JSON.parse(raw);
|
|
11139
|
+
return { bad: false };
|
|
11140
|
+
} catch (cause) {
|
|
11141
|
+
return { bad: true, cause };
|
|
11142
|
+
}
|
|
11143
|
+
}
|
|
11004
11144
|
function writeJsonConfig(filePath, config) {
|
|
11145
|
+
const { bad, cause } = existingContentIsUnparseable(filePath);
|
|
11146
|
+
if (bad)
|
|
11147
|
+
throw new ConfigParseError(filePath, cause);
|
|
11005
11148
|
mkdirSync14(dirname7(filePath), { recursive: true });
|
|
11006
11149
|
writeFileSync14(filePath, JSON.stringify(config, null, 2) + `
|
|
11007
11150
|
`);
|
|
@@ -11045,9 +11188,20 @@ function mergeJsonMcpServers(filePath, servers, topKey) {
|
|
|
11045
11188
|
writeJsonConfig(filePath, config);
|
|
11046
11189
|
return true;
|
|
11047
11190
|
}
|
|
11191
|
+
var ConfigParseError;
|
|
11048
11192
|
var init_json_config = __esm(() => {
|
|
11049
11193
|
init_types();
|
|
11050
11194
|
init_hash();
|
|
11195
|
+
ConfigParseError = class ConfigParseError extends Error {
|
|
11196
|
+
filePath;
|
|
11197
|
+
cause;
|
|
11198
|
+
constructor(filePath, cause) {
|
|
11199
|
+
super(`${filePath} is not valid JSON, so Runwork left it untouched. ` + `Fix the file (or move it aside) and run the command again.`);
|
|
11200
|
+
this.filePath = filePath;
|
|
11201
|
+
this.cause = cause;
|
|
11202
|
+
this.name = "ConfigParseError";
|
|
11203
|
+
}
|
|
11204
|
+
};
|
|
11051
11205
|
});
|
|
11052
11206
|
|
|
11053
11207
|
// src/agents/utils/skill-removal.ts
|
|
@@ -11602,24 +11756,6 @@ function vlog(...args) {
|
|
|
11602
11756
|
}
|
|
11603
11757
|
var verbose = false;
|
|
11604
11758
|
|
|
11605
|
-
// src/agents/detection-probes.ts
|
|
11606
|
-
function powershellQuote(value) {
|
|
11607
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
11608
|
-
}
|
|
11609
|
-
function isValidBundleId(id) {
|
|
11610
|
-
return /^[A-Za-z0-9][A-Za-z0-9.-]*$/.test(id);
|
|
11611
|
-
}
|
|
11612
|
-
function macosBundleIdProbeScript(id) {
|
|
11613
|
-
return `p=$(mdfind "kMDItemCFBundleIdentifier == '${id}'" 2>/dev/null | head -1); ` + `if [ -n "$p" ]; then exit 0; fi; ` + `for a in /Applications/*.app "$HOME"/Applications/*.app; do ` + `[ -e "$a" ] || continue; ` + `if [ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$a/Contents/Info.plist" 2>/dev/null)" = "${id}" ]; then exit 0; fi; ` + `done; exit 1`;
|
|
11614
|
-
}
|
|
11615
|
-
function appxPackageProbeScript(pkg) {
|
|
11616
|
-
return `$p = Get-AppxPackage -Name ${powershellQuote(pkg)} -ErrorAction SilentlyContinue; if ($null -ne $p) { exit 0 } exit 1`;
|
|
11617
|
-
}
|
|
11618
|
-
function startAppProbeScript(pattern) {
|
|
11619
|
-
const p = powershellQuote(pattern);
|
|
11620
|
-
return `$a = Get-StartApps -ErrorAction SilentlyContinue | Where-Object { $_.Name -like ${p} -or $_.AppID -like ${p} } | Select-Object -First 1; if ($null -ne $a) { exit 0 } exit 1`;
|
|
11621
|
-
}
|
|
11622
|
-
|
|
11623
11759
|
// src/agents/detection.ts
|
|
11624
11760
|
import { execFile } from "child_process";
|
|
11625
11761
|
import { existsSync as existsSync31 } from "fs";
|
|
@@ -11725,6 +11861,7 @@ var init_detection = __esm(() => {
|
|
|
11725
11861
|
init_which();
|
|
11726
11862
|
init_registry_data();
|
|
11727
11863
|
init_registry();
|
|
11864
|
+
init_detection_probes();
|
|
11728
11865
|
NOT_DETECTED = { detected: false };
|
|
11729
11866
|
});
|
|
11730
11867
|
|
|
@@ -11949,8 +12086,7 @@ ${instructions}`;
|
|
|
11949
12086
|
}
|
|
11950
12087
|
if (!hadFile && !config.modelPreference && !config.permissionRules)
|
|
11951
12088
|
return;
|
|
11952
|
-
|
|
11953
|
-
writeFileSync16(settingsPath, JSON.stringify(settings, null, 2));
|
|
12089
|
+
writeJsonConfig(settingsPath, settings);
|
|
11954
12090
|
}
|
|
11955
12091
|
async readManagedBlock(_scope) {
|
|
11956
12092
|
return;
|
|
@@ -11997,7 +12133,7 @@ ${instructions}`;
|
|
|
11997
12133
|
delete settings.enabledPlugins[key];
|
|
11998
12134
|
}
|
|
11999
12135
|
}
|
|
12000
|
-
|
|
12136
|
+
writeJsonConfig(settingsPath, settings);
|
|
12001
12137
|
} catch {}
|
|
12002
12138
|
}
|
|
12003
12139
|
const pluginDir = this.getPluginDir();
|
|
@@ -12889,7 +13025,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
12889
13025
|
prefs.ccdScheduledTasksEnabled = false;
|
|
12890
13026
|
}
|
|
12891
13027
|
}
|
|
12892
|
-
|
|
13028
|
+
writeJsonConfig(configPath, desktopConfig);
|
|
12893
13029
|
}
|
|
12894
13030
|
async cleanup(_scope, _manifest) {
|
|
12895
13031
|
removeRunworkMcpServers(getMcpConfigPath(), "mcpServers");
|
|
@@ -14431,7 +14567,7 @@ var init_cline = __esm(() => {
|
|
|
14431
14567
|
}
|
|
14432
14568
|
}
|
|
14433
14569
|
mkdirSync22(join32(globalStatePath, ".."), { recursive: true });
|
|
14434
|
-
|
|
14570
|
+
writeJsonConfig(globalStatePath, state);
|
|
14435
14571
|
}
|
|
14436
14572
|
async removeSkills(skillFilenames, scope) {
|
|
14437
14573
|
if (scope !== "project")
|
|
@@ -14544,7 +14680,7 @@ var init_gemini = __esm(() => {
|
|
|
14544
14680
|
settings.tools.exclude = config.permissionRules.deny;
|
|
14545
14681
|
}
|
|
14546
14682
|
mkdirSync23(join33(settingsPath, ".."), { recursive: true });
|
|
14547
|
-
|
|
14683
|
+
writeJsonConfig(settingsPath, settings);
|
|
14548
14684
|
}
|
|
14549
14685
|
async readUsageStats(lastSyncAt) {
|
|
14550
14686
|
try {
|
|
@@ -14738,7 +14874,7 @@ var init_gemini = __esm(() => {
|
|
|
14738
14874
|
});
|
|
14739
14875
|
|
|
14740
14876
|
// src/agents/generic-adapter.ts
|
|
14741
|
-
import {
|
|
14877
|
+
import { mkdirSync as mkdirSync24, writeFileSync as writeFileSync24 } from "fs";
|
|
14742
14878
|
import { join as join34 } from "path";
|
|
14743
14879
|
import { homedir as homedir16 } from "os";
|
|
14744
14880
|
var GenericAgentAdapter;
|
|
@@ -14749,6 +14885,7 @@ var init_generic_adapter = __esm(() => {
|
|
|
14749
14885
|
init_instruction_hint();
|
|
14750
14886
|
init_registry();
|
|
14751
14887
|
init_detection();
|
|
14888
|
+
init_trash();
|
|
14752
14889
|
GenericAgentAdapter = class GenericAgentAdapter extends RegistryDetectedAdapter {
|
|
14753
14890
|
name;
|
|
14754
14891
|
slug;
|
|
@@ -14795,12 +14932,7 @@ var init_generic_adapter = __esm(() => {
|
|
|
14795
14932
|
return 0;
|
|
14796
14933
|
for (const skill of skills) {
|
|
14797
14934
|
if (skill.name !== skill.filename) {
|
|
14798
|
-
|
|
14799
|
-
if (existsSync40(oldDir)) {
|
|
14800
|
-
try {
|
|
14801
|
-
rmSync12(oldDir, { recursive: true, force: true });
|
|
14802
|
-
} catch {}
|
|
14803
|
-
}
|
|
14935
|
+
moveToTrash(join34(baseDir, skill.name), `skill renamed to ${skill.filename}`);
|
|
14804
14936
|
}
|
|
14805
14937
|
const skillDir = join34(baseDir, skill.filename);
|
|
14806
14938
|
mkdirSync24(skillDir, { recursive: true });
|
|
@@ -15557,10 +15689,12 @@ function selectAnalyst(requestedSlug) {
|
|
|
15557
15689
|
const resolved = resolve3(requested);
|
|
15558
15690
|
if (!resolved)
|
|
15559
15691
|
return { candidates: [], broken };
|
|
15560
|
-
|
|
15692
|
+
const probe = probeBinaryRunnable(resolved.command);
|
|
15693
|
+
if (probe.runnable) {
|
|
15561
15694
|
return { chosen: resolved, candidates: [resolved], broken };
|
|
15562
15695
|
}
|
|
15563
|
-
|
|
15696
|
+
if (probe.reason !== "timeout")
|
|
15697
|
+
broken.push(requested.binary);
|
|
15564
15698
|
return { candidates: [], broken };
|
|
15565
15699
|
}
|
|
15566
15700
|
const candidates = [];
|
|
@@ -15568,9 +15702,10 @@ function selectAnalyst(requestedSlug) {
|
|
|
15568
15702
|
const resolved = resolve3(a);
|
|
15569
15703
|
if (!resolved)
|
|
15570
15704
|
continue;
|
|
15571
|
-
|
|
15705
|
+
const probe = probeBinaryRunnable(resolved.command);
|
|
15706
|
+
if (probe.runnable)
|
|
15572
15707
|
candidates.push(resolved);
|
|
15573
|
-
else
|
|
15708
|
+
else if (probe.reason !== "timeout")
|
|
15574
15709
|
broken.push(a.binary);
|
|
15575
15710
|
}
|
|
15576
15711
|
return { chosen: candidates[0], candidates, broken };
|
|
@@ -19825,6 +19960,7 @@ var infoCommand = new Command12("info").description("Show app context, registrie
|
|
|
19825
19960
|
init_store();
|
|
19826
19961
|
init_client();
|
|
19827
19962
|
init_resolve();
|
|
19963
|
+
init_skill_canonical();
|
|
19828
19964
|
import { Command as Command13 } from "commander";
|
|
19829
19965
|
import { readFileSync as readFileSync22, existsSync as existsSync25 } from "fs";
|
|
19830
19966
|
function truncate(text2, max) {
|
|
@@ -19899,7 +20035,7 @@ function buildSkillPushPayload(fileContent, nameArg) {
|
|
|
19899
20035
|
const name = toSkillSlug(nameArg || "") || docName;
|
|
19900
20036
|
if (!name)
|
|
19901
20037
|
return null;
|
|
19902
|
-
const description = parsed.
|
|
20038
|
+
const description = parsed.frontmatter.description;
|
|
19903
20039
|
const extra = {};
|
|
19904
20040
|
for (const key of parsed.orderedKeys) {
|
|
19905
20041
|
if (key === "name" || key === "description")
|
|
@@ -22571,8 +22707,8 @@ init_resolve();
|
|
|
22571
22707
|
init_prompt();
|
|
22572
22708
|
await init_detect();
|
|
22573
22709
|
import { Command as Command27 } from "commander";
|
|
22574
|
-
import { join as
|
|
22575
|
-
import { homedir as
|
|
22710
|
+
import { join as join49 } from "path";
|
|
22711
|
+
import { homedir as homedir28 } from "os";
|
|
22576
22712
|
|
|
22577
22713
|
// src/commands/sync.ts
|
|
22578
22714
|
init_store();
|
|
@@ -22583,9 +22719,9 @@ await __promiseAll([
|
|
|
22583
22719
|
init_codex()
|
|
22584
22720
|
]);
|
|
22585
22721
|
import { Command as Command26 } from "commander";
|
|
22586
|
-
import { readFileSync as
|
|
22587
|
-
import { join as
|
|
22588
|
-
import { homedir as
|
|
22722
|
+
import { readFileSync as readFileSync41, existsSync as existsSync52 } from "fs";
|
|
22723
|
+
import { join as join48 } from "path";
|
|
22724
|
+
import { homedir as homedir27 } from "os";
|
|
22589
22725
|
|
|
22590
22726
|
// src/commands/mcp-entries.ts
|
|
22591
22727
|
init_types();
|
|
@@ -23444,6 +23580,91 @@ function sameStringSet(a, b) {
|
|
|
23444
23580
|
return true;
|
|
23445
23581
|
}
|
|
23446
23582
|
|
|
23583
|
+
// src/utils/sync-lock.ts
|
|
23584
|
+
import { existsSync as existsSync51, mkdirSync as mkdirSync28, readFileSync as readFileSync40, unlinkSync as unlinkSync8, writeFileSync as writeFileSync30 } from "fs";
|
|
23585
|
+
import { join as join47 } from "path";
|
|
23586
|
+
import { homedir as homedir26 } from "os";
|
|
23587
|
+
var LOCK_PATH = join47(homedir26(), ".runwork", "sync.lock");
|
|
23588
|
+
var STALE_LOCK_MS = 5 * 60 * 1000;
|
|
23589
|
+
var DEFAULT_WAIT_MS = 30000;
|
|
23590
|
+
var exitHandlerRegistered = false;
|
|
23591
|
+
function ensureExitHandler() {
|
|
23592
|
+
if (exitHandlerRegistered)
|
|
23593
|
+
return;
|
|
23594
|
+
exitHandlerRegistered = true;
|
|
23595
|
+
process.once("exit", releaseSyncLock);
|
|
23596
|
+
}
|
|
23597
|
+
function isProcessAlive(pid) {
|
|
23598
|
+
try {
|
|
23599
|
+
process.kill(pid, 0);
|
|
23600
|
+
return true;
|
|
23601
|
+
} catch (err) {
|
|
23602
|
+
return err?.code === "EPERM";
|
|
23603
|
+
}
|
|
23604
|
+
}
|
|
23605
|
+
function readLock() {
|
|
23606
|
+
try {
|
|
23607
|
+
return JSON.parse(readFileSync40(LOCK_PATH, "utf-8"));
|
|
23608
|
+
} catch {
|
|
23609
|
+
return null;
|
|
23610
|
+
}
|
|
23611
|
+
}
|
|
23612
|
+
function writeLockExclusive() {
|
|
23613
|
+
try {
|
|
23614
|
+
if (!existsSync51(join47(homedir26(), ".runwork"))) {
|
|
23615
|
+
mkdirSync28(join47(homedir26(), ".runwork"), { recursive: true });
|
|
23616
|
+
}
|
|
23617
|
+
writeFileSync30(LOCK_PATH, JSON.stringify({ pid: process.pid, startedAt: Date.now() }), {
|
|
23618
|
+
flag: "wx"
|
|
23619
|
+
});
|
|
23620
|
+
return true;
|
|
23621
|
+
} catch {
|
|
23622
|
+
return false;
|
|
23623
|
+
}
|
|
23624
|
+
}
|
|
23625
|
+
function tryAcquireOnce() {
|
|
23626
|
+
if (writeLockExclusive()) {
|
|
23627
|
+
ensureExitHandler();
|
|
23628
|
+
return true;
|
|
23629
|
+
}
|
|
23630
|
+
const existing = readLock();
|
|
23631
|
+
const stale = !existing || Date.now() - existing.startedAt > STALE_LOCK_MS || !isProcessAlive(existing.pid);
|
|
23632
|
+
if (!stale)
|
|
23633
|
+
return false;
|
|
23634
|
+
try {
|
|
23635
|
+
unlinkSync8(LOCK_PATH);
|
|
23636
|
+
} catch {}
|
|
23637
|
+
if (writeLockExclusive()) {
|
|
23638
|
+
ensureExitHandler();
|
|
23639
|
+
return true;
|
|
23640
|
+
}
|
|
23641
|
+
return false;
|
|
23642
|
+
}
|
|
23643
|
+
function sleep2(ms) {
|
|
23644
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
23645
|
+
}
|
|
23646
|
+
async function acquireSyncLock(waitMs = DEFAULT_WAIT_MS) {
|
|
23647
|
+
const deadline = Date.now() + waitMs;
|
|
23648
|
+
let delay = 250;
|
|
23649
|
+
for (;; ) {
|
|
23650
|
+
if (tryAcquireOnce())
|
|
23651
|
+
return true;
|
|
23652
|
+
const remaining = deadline - Date.now();
|
|
23653
|
+
if (remaining <= 0)
|
|
23654
|
+
return false;
|
|
23655
|
+
await sleep2(Math.min(delay, remaining));
|
|
23656
|
+
delay = Math.min(delay * 2, 5000);
|
|
23657
|
+
}
|
|
23658
|
+
}
|
|
23659
|
+
function releaseSyncLock() {
|
|
23660
|
+
const existing = readLock();
|
|
23661
|
+
if (existing?.pid === process.pid) {
|
|
23662
|
+
try {
|
|
23663
|
+
unlinkSync8(LOCK_PATH);
|
|
23664
|
+
} catch {}
|
|
23665
|
+
}
|
|
23666
|
+
}
|
|
23667
|
+
|
|
23447
23668
|
// src/commands/sync.ts
|
|
23448
23669
|
async function printAdoptionHint(credentials, workspaceId) {
|
|
23449
23670
|
if (!workspaceId)
|
|
@@ -23460,10 +23681,10 @@ Tip: ${hint.title}`);
|
|
|
23460
23681
|
} catch {}
|
|
23461
23682
|
}
|
|
23462
23683
|
function loadSetupState(filePath) {
|
|
23463
|
-
if (!
|
|
23684
|
+
if (!existsSync52(filePath))
|
|
23464
23685
|
return null;
|
|
23465
23686
|
try {
|
|
23466
|
-
return JSON.parse(
|
|
23687
|
+
return JSON.parse(readFileSync41(filePath, "utf-8"));
|
|
23467
23688
|
} catch {
|
|
23468
23689
|
return null;
|
|
23469
23690
|
}
|
|
@@ -23484,15 +23705,15 @@ function readLocalSkills(state) {
|
|
|
23484
23705
|
if (!baseDir)
|
|
23485
23706
|
continue;
|
|
23486
23707
|
for (const skillName of state.skills) {
|
|
23487
|
-
const skillMdPath =
|
|
23488
|
-
if (
|
|
23489
|
-
results.push({ name: skillName, content:
|
|
23708
|
+
const skillMdPath = join48(baseDir, skillName, "SKILL.md");
|
|
23709
|
+
if (existsSync52(skillMdPath)) {
|
|
23710
|
+
results.push({ name: skillName, content: readFileSync41(skillMdPath, "utf-8") });
|
|
23490
23711
|
continue;
|
|
23491
23712
|
}
|
|
23492
23713
|
const filename = skillName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
23493
|
-
const flatPath =
|
|
23494
|
-
if (
|
|
23495
|
-
results.push({ name: skillName, content:
|
|
23714
|
+
const flatPath = join48(baseDir, `${filename}.md`);
|
|
23715
|
+
if (existsSync52(flatPath)) {
|
|
23716
|
+
results.push({ name: skillName, content: readFileSync41(flatPath, "utf-8") });
|
|
23496
23717
|
}
|
|
23497
23718
|
}
|
|
23498
23719
|
if (results.length > 0)
|
|
@@ -23546,6 +23767,19 @@ function ensureWorkspacePointer(state, statePath2, credentials) {
|
|
|
23546
23767
|
return true;
|
|
23547
23768
|
}
|
|
23548
23769
|
async function syncFromState(state, statePath2, credentials, opts) {
|
|
23770
|
+
const acquired = await acquireSyncLock();
|
|
23771
|
+
if (!acquired) {
|
|
23772
|
+
console.log(" Another sync appears to be in progress on this machine; proceeding anyway.");
|
|
23773
|
+
await runSyncFromState(state, statePath2, credentials, opts);
|
|
23774
|
+
return;
|
|
23775
|
+
}
|
|
23776
|
+
try {
|
|
23777
|
+
await runSyncFromState(state, statePath2, credentials, opts);
|
|
23778
|
+
} finally {
|
|
23779
|
+
releaseSyncLock();
|
|
23780
|
+
}
|
|
23781
|
+
}
|
|
23782
|
+
async function runSyncFromState(state, statePath2, credentials, opts) {
|
|
23549
23783
|
const client = new ApiClient(credentials);
|
|
23550
23784
|
setVerbose(!!opts.verbose);
|
|
23551
23785
|
if (!ensureWorkspacePointer(state, statePath2, credentials)) {
|
|
@@ -23733,9 +23967,9 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23733
23967
|
persona: state.persona
|
|
23734
23968
|
});
|
|
23735
23969
|
let projectAppSkillFilter = null;
|
|
23736
|
-
if (
|
|
23970
|
+
if (existsSync52(".runwork.json")) {
|
|
23737
23971
|
try {
|
|
23738
|
-
const config = JSON.parse(
|
|
23972
|
+
const config = JSON.parse(readFileSync41(".runwork.json", "utf-8"));
|
|
23739
23973
|
if (config.appName) {
|
|
23740
23974
|
projectAppSkillFilter = config.appName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
23741
23975
|
}
|
|
@@ -23760,9 +23994,15 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23760
23994
|
};
|
|
23761
23995
|
const mcpFailedAdapters = new Set;
|
|
23762
23996
|
const skillFailedAdapters = new Set;
|
|
23997
|
+
const instructionFailedAdapters = new Set;
|
|
23998
|
+
const configFailedAdapters = new Set;
|
|
23999
|
+
const hookFailedAdapters = new Set;
|
|
24000
|
+
const agentOutcomes = {};
|
|
24001
|
+
const syncStartedAt = new Date().toISOString();
|
|
23763
24002
|
for (const adapter2 of adapters) {
|
|
23764
24003
|
if (isConnectOnlyAgent(getRegistryAgent(adapter2.slug))) {
|
|
23765
24004
|
vlog(` [${adapter2.name}] Connect-only agent: no local files to sync`);
|
|
24005
|
+
agentOutcomes[adapter2.slug] = { at: syncStartedAt, ok: true, skipped: true };
|
|
23766
24006
|
summary.adaptersProcessed++;
|
|
23767
24007
|
continue;
|
|
23768
24008
|
}
|
|
@@ -23824,15 +24064,32 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23824
24064
|
await adapter2.writeInstructionHint(instructionHint, scope);
|
|
23825
24065
|
summary.instructionHintWrites++;
|
|
23826
24066
|
vlog(` [${adapter2.name}] Updated instruction hints (${scope})`);
|
|
23827
|
-
|
|
24067
|
+
} catch (err) {
|
|
24068
|
+
adapterFailedAnyScope = true;
|
|
24069
|
+
instructionFailedAdapters.add(adapter2.slug);
|
|
24070
|
+
console.warn(` [${adapter2.name}] Failed instructions (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
24071
|
+
}
|
|
24072
|
+
if (adapter2.writeBuiltInHooks) {
|
|
24073
|
+
try {
|
|
23828
24074
|
await adapter2.writeBuiltInHooks(scope);
|
|
23829
24075
|
summary.hookInstallCalls++;
|
|
24076
|
+
} catch (err) {
|
|
24077
|
+
adapterFailedAnyScope = true;
|
|
24078
|
+
hookFailedAdapters.add(adapter2.slug);
|
|
24079
|
+
console.warn(` [${adapter2.name}] Failed hooks (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
23830
24080
|
}
|
|
23831
|
-
} catch (err) {
|
|
23832
|
-
adapterFailedAnyScope = true;
|
|
23833
|
-
console.warn(` [${adapter2.name}] Failed (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
23834
24081
|
}
|
|
23835
24082
|
}
|
|
24083
|
+
const failedDimensions = [];
|
|
24084
|
+
if (skillFailedAdapters.has(adapter2.slug))
|
|
24085
|
+
failedDimensions.push("skills");
|
|
24086
|
+
if (mcpFailedAdapters.has(adapter2.slug))
|
|
24087
|
+
failedDimensions.push("mcp");
|
|
24088
|
+
if (instructionFailedAdapters.has(adapter2.slug))
|
|
24089
|
+
failedDimensions.push("instructions");
|
|
24090
|
+
if (hookFailedAdapters.has(adapter2.slug))
|
|
24091
|
+
failedDimensions.push("hooks");
|
|
24092
|
+
agentOutcomes[adapter2.slug] = failedDimensions.length > 0 ? { at: syncStartedAt, ok: false, failed: failedDimensions } : { at: syncStartedAt, ok: true };
|
|
23836
24093
|
summary.adaptersProcessed++;
|
|
23837
24094
|
if (adapterFailedAnyScope)
|
|
23838
24095
|
summary.adaptersFailed++;
|
|
@@ -23863,7 +24120,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23863
24120
|
await adapter2.writeTeamInstructions(fullInstructions, scope);
|
|
23864
24121
|
teamInstructionsApplied = true;
|
|
23865
24122
|
vlog(` [${adapter2.name}] Updated team instructions (${scope})`);
|
|
23866
|
-
} catch {
|
|
24123
|
+
} catch (err) {
|
|
24124
|
+
instructionFailedAdapters.add(adapter2.slug);
|
|
24125
|
+
console.warn(` [${adapter2.name}] Failed team instructions (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
24126
|
+
}
|
|
23867
24127
|
}
|
|
23868
24128
|
}
|
|
23869
24129
|
if (agentConfigs) {
|
|
@@ -23884,7 +24144,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23884
24144
|
agentConfigsApplied++;
|
|
23885
24145
|
const configKeys = Object.keys(configWithoutInstructions).join(", ");
|
|
23886
24146
|
vlog(` [${adapter2.name}] Updated agent config: ${configKeys} (${scope})`);
|
|
23887
|
-
} catch {
|
|
24147
|
+
} catch (err) {
|
|
24148
|
+
configFailedAdapters.add(adapter2.slug);
|
|
24149
|
+
console.warn(` [${adapter2.name}] Failed agent config (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
24150
|
+
}
|
|
23888
24151
|
}
|
|
23889
24152
|
}
|
|
23890
24153
|
}
|
|
@@ -23966,13 +24229,16 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23966
24229
|
}
|
|
23967
24230
|
if (team)
|
|
23968
24231
|
agentConfigsApplied++;
|
|
23969
|
-
} catch {
|
|
24232
|
+
} catch (err) {
|
|
24233
|
+
configFailedAdapters.add(adapter2.slug);
|
|
24234
|
+
console.warn(` [${adapter2.name}] Failed agent config: ${err instanceof Error ? err.message : err}`);
|
|
24235
|
+
}
|
|
23970
24236
|
}
|
|
23971
24237
|
state.agentDefaultsVersion = AGENT_DEFAULTS_SCHEMA_VERSION;
|
|
23972
24238
|
}
|
|
23973
24239
|
for (const adapter2 of adapters) {
|
|
23974
24240
|
if (adapter2 instanceof CodexAdapter) {
|
|
23975
|
-
const runworkDir =
|
|
24241
|
+
const runworkDir = join48(homedir27(), ".runwork");
|
|
23976
24242
|
const result = adapter2.registerDesktopWorkspace(runworkDir, "Runwork");
|
|
23977
24243
|
if (result === "written") {
|
|
23978
24244
|
vlog(` [${adapter2.name}] Registered workspace in Codex desktop app`);
|
|
@@ -23982,6 +24248,21 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23982
24248
|
}
|
|
23983
24249
|
const prevMcpNames = state.mcpServers ?? [];
|
|
23984
24250
|
state.lastSyncAt = new Date().toISOString();
|
|
24251
|
+
const lateFailures = [
|
|
24252
|
+
[configFailedAdapters, "config"],
|
|
24253
|
+
[instructionFailedAdapters, "instructions"],
|
|
24254
|
+
[hookFailedAdapters, "hooks"]
|
|
24255
|
+
];
|
|
24256
|
+
for (const [slugs, dimension] of lateFailures) {
|
|
24257
|
+
for (const slug of slugs) {
|
|
24258
|
+
const existing = agentOutcomes[slug];
|
|
24259
|
+
if (existing?.failed?.includes(dimension))
|
|
24260
|
+
continue;
|
|
24261
|
+
const failed = [...existing?.failed ?? [], dimension];
|
|
24262
|
+
agentOutcomes[slug] = { at: existing?.at ?? syncStartedAt, ok: false, failed };
|
|
24263
|
+
}
|
|
24264
|
+
}
|
|
24265
|
+
state.lastSyncAgents = agentOutcomes;
|
|
23985
24266
|
state.mcpServers = mcpEntries.map((e) => e.name);
|
|
23986
24267
|
state.skills = remoteSkills.map((s) => s.name);
|
|
23987
24268
|
state.skillFilenames = [
|
|
@@ -24114,8 +24395,8 @@ var syncCommand = new Command26("sync").description("Sync skills bidirectionally
|
|
|
24114
24395
|
verbose: !!opts.verbose,
|
|
24115
24396
|
redetect: !!opts.redetect
|
|
24116
24397
|
};
|
|
24117
|
-
const projectStatePath =
|
|
24118
|
-
const userStatePath =
|
|
24398
|
+
const projectStatePath = join48(process.cwd(), ".runwork", "setup.json");
|
|
24399
|
+
const userStatePath = join48(homedir27(), ".runwork", "setup.json");
|
|
24119
24400
|
const projectState = loadSetupState(projectStatePath);
|
|
24120
24401
|
const userState = loadSetupState(userStatePath);
|
|
24121
24402
|
if (!projectState && !userState) {
|
|
@@ -24184,7 +24465,7 @@ function toSkillFilename(name) {
|
|
|
24184
24465
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
24185
24466
|
}
|
|
24186
24467
|
function loadSetupStateForScope(scope) {
|
|
24187
|
-
const path4 = scope === "project" ?
|
|
24468
|
+
const path4 = scope === "project" ? join49(process.cwd(), ".runwork", "setup.json") : join49(homedir28(), ".runwork", "setup.json");
|
|
24188
24469
|
return readJsonOrNull(path4);
|
|
24189
24470
|
}
|
|
24190
24471
|
async function parkAndTeardownWorkspace(previous, scopes) {
|
|
@@ -24353,8 +24634,8 @@ Re-run without --dry-run to sync workspace data.`);
|
|
|
24353
24634
|
}
|
|
24354
24635
|
persistDefaultWorkspace(workspaceId, workspaceName);
|
|
24355
24636
|
for (const s of scopes) {
|
|
24356
|
-
const dir = s === "project" ? ".runwork" :
|
|
24357
|
-
writeJsonAtomic(
|
|
24637
|
+
const dir = s === "project" ? ".runwork" : join49(homedir28(), ".runwork");
|
|
24638
|
+
writeJsonAtomic(join49(dir, "setup.json"), state);
|
|
24358
24639
|
}
|
|
24359
24640
|
if (restored)
|
|
24360
24641
|
clearParkedState(workspaceId);
|
|
@@ -24362,7 +24643,7 @@ Re-run without --dry-run to sync workspace data.`);
|
|
|
24362
24643
|
Syncing workspace data...
|
|
24363
24644
|
`);
|
|
24364
24645
|
for (const s of scopes) {
|
|
24365
|
-
const statePath2 = s === "project" ?
|
|
24646
|
+
const statePath2 = s === "project" ? join49(process.cwd(), ".runwork", "setup.json") : join49(homedir28(), ".runwork", "setup.json");
|
|
24366
24647
|
await syncFromState(state, statePath2, credentials, {
|
|
24367
24648
|
dryRun: false,
|
|
24368
24649
|
pullOnly: true,
|
|
@@ -24382,16 +24663,16 @@ init_client();
|
|
|
24382
24663
|
import { Command as Command28 } from "commander";
|
|
24383
24664
|
|
|
24384
24665
|
// src/utils/setup-state.ts
|
|
24385
|
-
import { existsSync as
|
|
24386
|
-
import { join as
|
|
24387
|
-
import { homedir as
|
|
24666
|
+
import { existsSync as existsSync53, readFileSync as readFileSync42 } from "fs";
|
|
24667
|
+
import { join as join50 } from "path";
|
|
24668
|
+
import { homedir as homedir29 } from "os";
|
|
24388
24669
|
function loadSetupState2() {
|
|
24389
|
-
const projectPath =
|
|
24390
|
-
const userPath =
|
|
24670
|
+
const projectPath = join50(process.cwd(), ".runwork", "setup.json");
|
|
24671
|
+
const userPath = join50(homedir29(), ".runwork", "setup.json");
|
|
24391
24672
|
for (const p of [projectPath, userPath]) {
|
|
24392
|
-
if (
|
|
24673
|
+
if (existsSync53(p)) {
|
|
24393
24674
|
try {
|
|
24394
|
-
return JSON.parse(
|
|
24675
|
+
return JSON.parse(readFileSync42(p, "utf-8"));
|
|
24395
24676
|
} catch {
|
|
24396
24677
|
continue;
|
|
24397
24678
|
}
|
|
@@ -24450,14 +24731,14 @@ init_client();
|
|
|
24450
24731
|
init_types();
|
|
24451
24732
|
await init_detect();
|
|
24452
24733
|
import { Command as Command29 } from "commander";
|
|
24453
|
-
import { existsSync as
|
|
24454
|
-
import { resolve as resolve3, join as
|
|
24455
|
-
import { homedir as
|
|
24734
|
+
import { existsSync as existsSync54, readFileSync as readFileSync43 } from "fs";
|
|
24735
|
+
import { resolve as resolve3, join as join51 } from "path";
|
|
24736
|
+
import { homedir as homedir30 } from "os";
|
|
24456
24737
|
function loadSetupState3(filePath) {
|
|
24457
|
-
if (!
|
|
24738
|
+
if (!existsSync54(filePath))
|
|
24458
24739
|
return null;
|
|
24459
24740
|
try {
|
|
24460
|
-
return JSON.parse(
|
|
24741
|
+
return JSON.parse(readFileSync43(filePath, "utf-8"));
|
|
24461
24742
|
} catch {
|
|
24462
24743
|
return null;
|
|
24463
24744
|
}
|
|
@@ -24473,8 +24754,8 @@ var buildPluginCommand = new Command29("build-plugin").description("Build an ins
|
|
|
24473
24754
|
process.exit(1);
|
|
24474
24755
|
}
|
|
24475
24756
|
const credentials = requireAuth();
|
|
24476
|
-
const projectStatePath =
|
|
24477
|
-
const userStatePath =
|
|
24757
|
+
const projectStatePath = join51(process.cwd(), ".runwork", "setup.json");
|
|
24758
|
+
const userStatePath = join51(homedir30(), ".runwork", "setup.json");
|
|
24478
24759
|
const state = loadSetupState3(projectStatePath) ?? loadSetupState3(userStatePath);
|
|
24479
24760
|
if (!state) {
|
|
24480
24761
|
console.error("No setup state found. Run `runwork setup` first.");
|
|
@@ -24564,23 +24845,143 @@ var buildPluginCommand = new Command29("build-plugin").description("Build an ins
|
|
|
24564
24845
|
|
|
24565
24846
|
// src/commands/uninstall.ts
|
|
24566
24847
|
init_prompt();
|
|
24848
|
+
init_subprocess();
|
|
24567
24849
|
await init_detect();
|
|
24568
24850
|
import { Command as Command30 } from "commander";
|
|
24569
|
-
import { existsSync as
|
|
24570
|
-
import { join as
|
|
24571
|
-
import { homedir as
|
|
24851
|
+
import { existsSync as existsSync55, readFileSync as readFileSync44, writeFileSync as writeFileSync32, readdirSync as readdirSync16, rmSync as rmSync12, unlinkSync as unlinkSync9, lstatSync, readlinkSync } from "fs";
|
|
24852
|
+
import { join as join52, resolve as resolve4, relative as relative5, isAbsolute as isAbsolute5 } from "path";
|
|
24853
|
+
import { homedir as homedir31 } from "os";
|
|
24572
24854
|
function loadSetupState4(filePath) {
|
|
24573
|
-
if (!
|
|
24855
|
+
if (!existsSync55(filePath))
|
|
24574
24856
|
return null;
|
|
24575
24857
|
try {
|
|
24576
|
-
return JSON.parse(
|
|
24858
|
+
return JSON.parse(readFileSync44(filePath, "utf-8"));
|
|
24577
24859
|
} catch {
|
|
24578
24860
|
return null;
|
|
24579
24861
|
}
|
|
24580
24862
|
}
|
|
24863
|
+
var PRESERVED_ENTRIES = ["bin", "apps", "trash"];
|
|
24864
|
+
function removeRunworkState(stateDir, opts) {
|
|
24865
|
+
const result = { removed: [], preserved: [], errors: [] };
|
|
24866
|
+
if (!existsSync55(stateDir))
|
|
24867
|
+
return result;
|
|
24868
|
+
const preserve = new Set(PRESERVED_ENTRIES);
|
|
24869
|
+
if (opts.keepAuth)
|
|
24870
|
+
preserve.add(".credentials");
|
|
24871
|
+
let entries;
|
|
24872
|
+
try {
|
|
24873
|
+
entries = readdirSync16(stateDir);
|
|
24874
|
+
} catch (err) {
|
|
24875
|
+
result.errors.push(`${stateDir}: ${err instanceof Error ? err.message : err}`);
|
|
24876
|
+
return result;
|
|
24877
|
+
}
|
|
24878
|
+
for (const entry of entries) {
|
|
24879
|
+
const target = join52(stateDir, entry);
|
|
24880
|
+
if (preserve.has(entry)) {
|
|
24881
|
+
if (existsSync55(target))
|
|
24882
|
+
result.preserved.push(target);
|
|
24883
|
+
continue;
|
|
24884
|
+
}
|
|
24885
|
+
try {
|
|
24886
|
+
rmSync12(target, { recursive: true, force: true });
|
|
24887
|
+
result.removed.push(target);
|
|
24888
|
+
} catch (err) {
|
|
24889
|
+
result.errors.push(`${target}: ${err instanceof Error ? err.message : err}`);
|
|
24890
|
+
}
|
|
24891
|
+
}
|
|
24892
|
+
return result;
|
|
24893
|
+
}
|
|
24894
|
+
var SHELL_PROFILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile", ".profile"];
|
|
24895
|
+
var BIN_DIR_PATTERN = /\.runwork[\\/]bin/;
|
|
24896
|
+
function stripRunworkPathLines(file) {
|
|
24897
|
+
if (!existsSync55(file))
|
|
24898
|
+
return false;
|
|
24899
|
+
let content;
|
|
24900
|
+
try {
|
|
24901
|
+
content = readFileSync44(file, "utf-8");
|
|
24902
|
+
} catch {
|
|
24903
|
+
return false;
|
|
24904
|
+
}
|
|
24905
|
+
if (!content.includes("Added by Runwork"))
|
|
24906
|
+
return false;
|
|
24907
|
+
const lines = content.split(`
|
|
24908
|
+
`);
|
|
24909
|
+
const kept = [];
|
|
24910
|
+
for (let i = 0;i < lines.length; i++) {
|
|
24911
|
+
if (/^\s*#\s*Added by Runwork\b/.test(lines[i])) {
|
|
24912
|
+
if (i + 1 < lines.length && BIN_DIR_PATTERN.test(lines[i + 1]))
|
|
24913
|
+
i++;
|
|
24914
|
+
continue;
|
|
24915
|
+
}
|
|
24916
|
+
kept.push(lines[i]);
|
|
24917
|
+
}
|
|
24918
|
+
const next = kept.join(`
|
|
24919
|
+
`);
|
|
24920
|
+
if (next === content)
|
|
24921
|
+
return false;
|
|
24922
|
+
try {
|
|
24923
|
+
writeFileSync32(file, next);
|
|
24924
|
+
return true;
|
|
24925
|
+
} catch {
|
|
24926
|
+
return false;
|
|
24927
|
+
}
|
|
24928
|
+
}
|
|
24929
|
+
function cleanShellProfilePathEntries() {
|
|
24930
|
+
return SHELL_PROFILES.map((name) => join52(homedir31(), name)).filter(stripRunworkPathLines);
|
|
24931
|
+
}
|
|
24932
|
+
function cleanPowerShellProfilePathEntries() {
|
|
24933
|
+
if (process.platform !== "win32")
|
|
24934
|
+
return [];
|
|
24935
|
+
const touched = [];
|
|
24936
|
+
const seen = new Set;
|
|
24937
|
+
for (const host of ["powershell", "pwsh"]) {
|
|
24938
|
+
let profilePath;
|
|
24939
|
+
try {
|
|
24940
|
+
profilePath = execFileSync(host, ["-NoProfile", "-Command", "$PROFILE.CurrentUserCurrentHost"], {
|
|
24941
|
+
encoding: "utf-8",
|
|
24942
|
+
stdio: "pipe"
|
|
24943
|
+
}).trim();
|
|
24944
|
+
} catch {
|
|
24945
|
+
continue;
|
|
24946
|
+
}
|
|
24947
|
+
if (!profilePath || seen.has(profilePath))
|
|
24948
|
+
continue;
|
|
24949
|
+
seen.add(profilePath);
|
|
24950
|
+
if (stripRunworkPathLines(profilePath))
|
|
24951
|
+
touched.push(profilePath);
|
|
24952
|
+
}
|
|
24953
|
+
return touched;
|
|
24954
|
+
}
|
|
24955
|
+
function removeRunworkSymlink(linkPath) {
|
|
24956
|
+
let stat;
|
|
24957
|
+
try {
|
|
24958
|
+
stat = lstatSync(linkPath);
|
|
24959
|
+
} catch {
|
|
24960
|
+
return false;
|
|
24961
|
+
}
|
|
24962
|
+
if (!stat.isSymbolicLink())
|
|
24963
|
+
return false;
|
|
24964
|
+
let target;
|
|
24965
|
+
try {
|
|
24966
|
+
target = resolve4(linkPath, "..", readlinkSync(linkPath));
|
|
24967
|
+
} catch {
|
|
24968
|
+
return false;
|
|
24969
|
+
}
|
|
24970
|
+
const ours = resolve4(join52(homedir31(), ".runwork", "bin"));
|
|
24971
|
+
const rel = relative5(ours, resolve4(target));
|
|
24972
|
+
const insideOurs = rel === "" || !rel.startsWith("..") && !isAbsolute5(rel);
|
|
24973
|
+
if (!insideOurs)
|
|
24974
|
+
return false;
|
|
24975
|
+
try {
|
|
24976
|
+
unlinkSync9(linkPath);
|
|
24977
|
+
return true;
|
|
24978
|
+
} catch {
|
|
24979
|
+
return false;
|
|
24980
|
+
}
|
|
24981
|
+
}
|
|
24581
24982
|
var uninstallCommand = new Command30("uninstall").description("Remove all Runwork configuration from local agents (MCP servers, skills, instructions)").option("-y, --yes", "Skip confirmation prompt").option("--keep-auth", "Keep authentication credentials (only remove agent configs)").action(async (opts) => {
|
|
24582
|
-
const projectStatePath =
|
|
24583
|
-
const userStatePath =
|
|
24983
|
+
const projectStatePath = join52(process.cwd(), ".runwork", "setup.json");
|
|
24984
|
+
const userStatePath = join52(homedir31(), ".runwork", "setup.json");
|
|
24584
24985
|
const projectState = loadSetupState4(projectStatePath);
|
|
24585
24986
|
const userState = loadSetupState4(userStatePath);
|
|
24586
24987
|
if (!projectState && !userState) {
|
|
@@ -24618,6 +25019,13 @@ This will remove all Runwork configuration from your local agents:
|
|
|
24618
25019
|
} else {
|
|
24619
25020
|
console.log(" - Setup state and auth credentials (~/.runwork/)");
|
|
24620
25021
|
}
|
|
25022
|
+
console.log(" - The PATH line we added to your shell profiles");
|
|
25023
|
+
console.log(" - Symlinks that point at our own binary");
|
|
25024
|
+
console.log(`
|
|
25025
|
+
What will be KEPT:`);
|
|
25026
|
+
console.log(" - ~/.runwork/bin the CLI binary itself");
|
|
25027
|
+
console.log(" - ~/.runwork/apps your own app source");
|
|
25028
|
+
console.log(" - ~/.runwork/trash recoverable copies of removed files");
|
|
24621
25029
|
console.log("");
|
|
24622
25030
|
if (!opts.yes) {
|
|
24623
25031
|
const confirmed = await promptConfirm("Proceed with uninstall?");
|
|
@@ -24636,7 +25044,7 @@ This will remove all Runwork configuration from your local agents:
|
|
|
24636
25044
|
for (const { state, label } of entries) {
|
|
24637
25045
|
const scopes = state.scope === "both" ? ["project", "user"] : [state.scope];
|
|
24638
25046
|
const manifest = {
|
|
24639
|
-
skillFilenames: state.skillFilenames ?? state.skills.map((s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-")),
|
|
25047
|
+
skillFilenames: state.skillFilenames ?? (state.skills ?? []).map((s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-")),
|
|
24640
25048
|
mcpServerNames: state.mcpServers ?? []
|
|
24641
25049
|
};
|
|
24642
25050
|
for (const slug of state.configuredAgents) {
|
|
@@ -24660,28 +25068,51 @@ This will remove all Runwork configuration from your local agents:
|
|
|
24660
25068
|
}
|
|
24661
25069
|
}
|
|
24662
25070
|
}
|
|
24663
|
-
const stateDir = label === "project" ?
|
|
24664
|
-
if (
|
|
24665
|
-
const
|
|
24666
|
-
|
|
24667
|
-
|
|
24668
|
-
|
|
24669
|
-
|
|
24670
|
-
} catch (err) {
|
|
24671
|
-
console.warn(` Failed to remove ${setupFile}: ${err instanceof Error ? err.message : err}`);
|
|
24672
|
-
errors++;
|
|
24673
|
-
}
|
|
25071
|
+
const stateDir = label === "project" ? join52(process.cwd(), ".runwork") : join52(homedir31(), ".runwork");
|
|
25072
|
+
if (existsSync55(stateDir)) {
|
|
25073
|
+
const outcome = removeRunworkState(stateDir, {
|
|
25074
|
+
keepAuth: Boolean(opts.keepAuth) && label === "user"
|
|
25075
|
+
});
|
|
25076
|
+
if (outcome.removed.length > 0) {
|
|
25077
|
+
console.log(` Removed Runwork state from ${stateDir}`);
|
|
24674
25078
|
}
|
|
24675
|
-
|
|
24676
|
-
|
|
24677
|
-
rmSync13(stateDir, { recursive: true, force: true });
|
|
24678
|
-
console.log(` Removed ${stateDir}`);
|
|
24679
|
-
} catch (err) {
|
|
24680
|
-
console.warn(` Failed to remove ${stateDir}: ${err instanceof Error ? err.message : err}`);
|
|
25079
|
+
for (const problem of outcome.errors) {
|
|
25080
|
+
console.warn(` Failed to remove ${problem}`);
|
|
24681
25081
|
errors++;
|
|
24682
25082
|
}
|
|
25083
|
+
if (outcome.preserved.length > 0) {
|
|
25084
|
+
console.log("");
|
|
25085
|
+
console.log(" Kept (not Runwork's to delete):");
|
|
25086
|
+
for (const kept of outcome.preserved)
|
|
25087
|
+
console.log(` - ${kept}`);
|
|
25088
|
+
}
|
|
24683
25089
|
}
|
|
24684
25090
|
}
|
|
25091
|
+
const touchedProfiles = [
|
|
25092
|
+
...cleanShellProfilePathEntries(),
|
|
25093
|
+
...cleanPowerShellProfilePathEntries()
|
|
25094
|
+
];
|
|
25095
|
+
if (touchedProfiles.length > 0) {
|
|
25096
|
+
console.log("");
|
|
25097
|
+
console.log(" Removed the Runwork PATH line from:");
|
|
25098
|
+
for (const file of touchedProfiles)
|
|
25099
|
+
console.log(` - ${file}`);
|
|
25100
|
+
}
|
|
25101
|
+
const removedLinks = [
|
|
25102
|
+
join52(homedir31(), ".local", "bin", "runwork"),
|
|
25103
|
+
"/usr/local/bin/runwork"
|
|
25104
|
+
].filter(removeRunworkSymlink);
|
|
25105
|
+
if (removedLinks.length > 0) {
|
|
25106
|
+
console.log("");
|
|
25107
|
+
console.log(" Removed symlinks:");
|
|
25108
|
+
for (const link of removedLinks)
|
|
25109
|
+
console.log(` - ${link}`);
|
|
25110
|
+
}
|
|
25111
|
+
if (process.platform === "win32") {
|
|
25112
|
+
console.log("");
|
|
25113
|
+
console.log(" Still on your PATH (remove by hand if you want it gone):");
|
|
25114
|
+
console.log(` ${join52(homedir31(), ".runwork", "bin")} in your user PATH`);
|
|
25115
|
+
}
|
|
24685
25116
|
console.log("");
|
|
24686
25117
|
if (errors > 0) {
|
|
24687
25118
|
console.log(`Uninstall completed with ${errors} warning${errors > 1 ? "s" : ""}. ${cleanedAgents} agent${cleanedAgents > 1 ? "s" : ""} cleaned.`);
|
|
@@ -24806,7 +25237,7 @@ var membersCommand = new Command32("members").description("List workspace member
|
|
|
24806
25237
|
init_store();
|
|
24807
25238
|
init_client();
|
|
24808
25239
|
import { Command as Command33 } from "commander";
|
|
24809
|
-
import { readFileSync as
|
|
25240
|
+
import { readFileSync as readFileSync45 } from "fs";
|
|
24810
25241
|
function normalizeApiPath(rawPath, baseUrl) {
|
|
24811
25242
|
if (/^https?:\/\//i.test(rawPath)) {
|
|
24812
25243
|
const target = new URL(rawPath);
|
|
@@ -24839,7 +25270,7 @@ to be read or pasted manually. Prefer a dedicated command when one exists
|
|
|
24839
25270
|
let curlStr = opts.curl;
|
|
24840
25271
|
if (opts.curlFile) {
|
|
24841
25272
|
try {
|
|
24842
|
-
curlStr =
|
|
25273
|
+
curlStr = readFileSync45(opts.curlFile, "utf-8");
|
|
24843
25274
|
} catch (err) {
|
|
24844
25275
|
console.error(`Could not read --curl-file: ${err instanceof Error ? err.message : err}`);
|
|
24845
25276
|
process.exit(1);
|
|
@@ -24859,7 +25290,7 @@ to be read or pasted manually. Prefer a dedicated command when one exists
|
|
|
24859
25290
|
let raw = opts.body;
|
|
24860
25291
|
if (raw.startsWith("@")) {
|
|
24861
25292
|
try {
|
|
24862
|
-
raw =
|
|
25293
|
+
raw = readFileSync45(raw.slice(1), "utf-8");
|
|
24863
25294
|
} catch (err) {
|
|
24864
25295
|
console.error(`Could not read body file: ${err instanceof Error ? err.message : err}`);
|
|
24865
25296
|
process.exit(1);
|
|
@@ -24905,6 +25336,7 @@ import { Command as Command34 } from "commander";
|
|
|
24905
25336
|
|
|
24906
25337
|
// src/health/checks.ts
|
|
24907
25338
|
init_subprocess();
|
|
25339
|
+
init_http();
|
|
24908
25340
|
init_store();
|
|
24909
25341
|
init_client();
|
|
24910
25342
|
init_http();
|
|
@@ -24912,9 +25344,9 @@ init_preflight();
|
|
|
24912
25344
|
init_credentials();
|
|
24913
25345
|
await init_detect();
|
|
24914
25346
|
import { parse as parse2 } from "smol-toml";
|
|
24915
|
-
import { existsSync as
|
|
24916
|
-
import { join as
|
|
24917
|
-
import { homedir as
|
|
25347
|
+
import { existsSync as existsSync56, readFileSync as readFileSync46 } from "fs";
|
|
25348
|
+
import { join as join53, sep as sep4 } from "path";
|
|
25349
|
+
import { homedir as homedir32, platform as osPlatform2, arch as osArch } from "os";
|
|
24918
25350
|
var BASE_URL2 = process.env.RUNWORK_DOWNLOAD_BASE_URL || "https://runwork.ai";
|
|
24919
25351
|
var LATEST_JSON_URL2 = `${BASE_URL2}/cli/latest.json`;
|
|
24920
25352
|
function detectPlatform() {
|
|
@@ -24942,10 +25374,10 @@ function buildContext() {
|
|
|
24942
25374
|
const credentials = getCredentials();
|
|
24943
25375
|
const client = credentials ? new ApiClient(credentials) : null;
|
|
24944
25376
|
let config = null;
|
|
24945
|
-
const configPath =
|
|
24946
|
-
if (
|
|
25377
|
+
const configPath = join53(process.cwd(), ".runwork.json");
|
|
25378
|
+
if (existsSync56(configPath)) {
|
|
24947
25379
|
try {
|
|
24948
|
-
config = JSON.parse(
|
|
25380
|
+
config = JSON.parse(readFileSync46(configPath, "utf-8"));
|
|
24949
25381
|
} catch {}
|
|
24950
25382
|
}
|
|
24951
25383
|
return { credentials, client, config, cwd: process.cwd() };
|
|
@@ -25046,9 +25478,9 @@ async function checkCliArtifactReachable() {
|
|
|
25046
25478
|
}
|
|
25047
25479
|
async function checkCliInstallLocation() {
|
|
25048
25480
|
const isWindows2 = osPlatform2() === "win32";
|
|
25049
|
-
const home =
|
|
25050
|
-
const canonicalDir =
|
|
25051
|
-
const canonicalBinary = isWindows2 ?
|
|
25481
|
+
const home = homedir32();
|
|
25482
|
+
const canonicalDir = join53(home, ".runwork", "bin");
|
|
25483
|
+
const canonicalBinary = isWindows2 ? join53(canonicalDir, "runwork.exe") : join53(canonicalDir, "runwork");
|
|
25052
25484
|
const candidates = [process.execPath, process.argv[1] || ""].filter(Boolean);
|
|
25053
25485
|
const runsFromCanonical = candidates.some((p) => normalizePath(p) === normalizePath(canonicalBinary));
|
|
25054
25486
|
if (runsFromCanonical) {
|
|
@@ -25058,7 +25490,7 @@ async function checkCliInstallLocation() {
|
|
|
25058
25490
|
message: `canonical (${canonicalBinary})`
|
|
25059
25491
|
};
|
|
25060
25492
|
}
|
|
25061
|
-
if (
|
|
25493
|
+
if (existsSync56(canonicalBinary)) {
|
|
25062
25494
|
return {
|
|
25063
25495
|
name: "cli-install-location",
|
|
25064
25496
|
status: "warn",
|
|
@@ -25106,9 +25538,20 @@ async function checkAuthAndNetwork(ctx) {
|
|
|
25106
25538
|
network: { name: "network", status: "pass", message: `API reachable (${elapsed}ms)` }
|
|
25107
25539
|
};
|
|
25108
25540
|
}
|
|
25541
|
+
const proxy = proxyForUrl(ctx.credentials.baseUrl || BASE_URL2);
|
|
25542
|
+
const network = proxy ? {
|
|
25543
|
+
name: "network",
|
|
25544
|
+
status: "fail",
|
|
25545
|
+
message: `API unreachable via ${proxy.variable} (${proxy.value}): ${message}`,
|
|
25546
|
+
details: [
|
|
25547
|
+
`A proxy is configured through ${proxy.variable}.`,
|
|
25548
|
+
"Runwork routes requests through `curl` when a proxy is set, so curl must be installed and able to reach the proxy.",
|
|
25549
|
+
"If this host should bypass the proxy, add it to NO_PROXY."
|
|
25550
|
+
]
|
|
25551
|
+
} : { name: "network", status: "fail", message: `API unreachable: ${message}` };
|
|
25109
25552
|
return {
|
|
25110
25553
|
auth: { name: "auth", status: "skip", message: "could not verify (network error)" },
|
|
25111
|
-
network
|
|
25554
|
+
network
|
|
25112
25555
|
};
|
|
25113
25556
|
}
|
|
25114
25557
|
}
|
|
@@ -25180,8 +25623,8 @@ async function checkGitCredentialHelper(ctx) {
|
|
|
25180
25623
|
};
|
|
25181
25624
|
}
|
|
25182
25625
|
async function checkProjectConfig(ctx) {
|
|
25183
|
-
const configPath =
|
|
25184
|
-
if (!
|
|
25626
|
+
const configPath = join53(ctx.cwd, ".runwork.json");
|
|
25627
|
+
if (!existsSync56(configPath)) {
|
|
25185
25628
|
if (!ctx.credentials) {
|
|
25186
25629
|
return { name: "project-config", status: "skip", message: "no project (not logged in)" };
|
|
25187
25630
|
}
|
|
@@ -25243,7 +25686,7 @@ async function checkGitRemote(ctx) {
|
|
|
25243
25686
|
if (!ctx.config) {
|
|
25244
25687
|
return { name: "git-remote", status: "skip", message: "skipped (no project)" };
|
|
25245
25688
|
}
|
|
25246
|
-
if (!
|
|
25689
|
+
if (!existsSync56(join53(ctx.cwd, ".git"))) {
|
|
25247
25690
|
return {
|
|
25248
25691
|
name: "git-remote",
|
|
25249
25692
|
status: "fail",
|
|
@@ -25297,12 +25740,12 @@ async function checkDeployFreshness(ctx) {
|
|
|
25297
25740
|
return { name: "deploy-freshness", status: "skip", message: "local HEAD unknown" };
|
|
25298
25741
|
}
|
|
25299
25742
|
function loadSetupState5() {
|
|
25300
|
-
const projectPath =
|
|
25301
|
-
const userPath =
|
|
25743
|
+
const projectPath = join53(process.cwd(), ".runwork", "setup.json");
|
|
25744
|
+
const userPath = join53(homedir32(), ".runwork", "setup.json");
|
|
25302
25745
|
for (const p of [projectPath, userPath]) {
|
|
25303
|
-
if (
|
|
25746
|
+
if (existsSync56(p)) {
|
|
25304
25747
|
try {
|
|
25305
|
-
return JSON.parse(
|
|
25748
|
+
return JSON.parse(readFileSync46(p, "utf-8"));
|
|
25306
25749
|
} catch {
|
|
25307
25750
|
continue;
|
|
25308
25751
|
}
|
|
@@ -25317,13 +25760,13 @@ async function checkCodexNetwork() {
|
|
|
25317
25760
|
if (!state || !state.configuredAgents.includes("codex")) {
|
|
25318
25761
|
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
25319
25762
|
}
|
|
25320
|
-
const configPath =
|
|
25321
|
-
if (!
|
|
25763
|
+
const configPath = join53(homedir32(), ".codex", "config.toml");
|
|
25764
|
+
if (!existsSync56(configPath)) {
|
|
25322
25765
|
return { name, status: "skip", message: "no Codex config found" };
|
|
25323
25766
|
}
|
|
25324
25767
|
let parsed;
|
|
25325
25768
|
try {
|
|
25326
|
-
parsed = parse2(
|
|
25769
|
+
parsed = parse2(readFileSync46(configPath, "utf-8"));
|
|
25327
25770
|
} catch {
|
|
25328
25771
|
return { name, status: "warn", message: "could not parse ~/.codex/config.toml" };
|
|
25329
25772
|
}
|
|
@@ -25376,19 +25819,19 @@ async function checkCodexDesktopProject() {
|
|
|
25376
25819
|
if (!usesCodex) {
|
|
25377
25820
|
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
25378
25821
|
}
|
|
25379
|
-
const statePath2 =
|
|
25380
|
-
if (!
|
|
25822
|
+
const statePath2 = join53(homedir32(), ".codex", ".codex-global-state.json");
|
|
25823
|
+
if (!existsSync56(statePath2)) {
|
|
25381
25824
|
return { name, status: "skip", message: "Codex desktop app not detected" };
|
|
25382
25825
|
}
|
|
25383
25826
|
let savedRoots = [];
|
|
25384
25827
|
try {
|
|
25385
|
-
const parsed = JSON.parse(
|
|
25828
|
+
const parsed = JSON.parse(readFileSync46(statePath2, "utf-8"));
|
|
25386
25829
|
const roots = parsed["electron-saved-workspace-roots"];
|
|
25387
25830
|
savedRoots = Array.isArray(roots) ? roots.filter((r) => typeof r === "string") : [];
|
|
25388
25831
|
} catch {
|
|
25389
25832
|
return { name, status: "warn", message: "could not read Codex desktop state" };
|
|
25390
25833
|
}
|
|
25391
|
-
const runworkDir =
|
|
25834
|
+
const runworkDir = join53(homedir32(), ".runwork");
|
|
25392
25835
|
if (savedRoots.includes(runworkDir)) {
|
|
25393
25836
|
return { name, status: "pass", message: "Runwork project added to Codex desktop sidebar" };
|
|
25394
25837
|
}
|
|
@@ -25441,9 +25884,9 @@ async function checkAgentSetup() {
|
|
|
25441
25884
|
if (!adapter2 || !adapter2.supportsMcpScope("user"))
|
|
25442
25885
|
continue;
|
|
25443
25886
|
const mcpConfigPath = getMcpConfigPath2(slug, "user");
|
|
25444
|
-
if (mcpConfigPath &&
|
|
25887
|
+
if (mcpConfigPath && existsSync56(mcpConfigPath)) {
|
|
25445
25888
|
try {
|
|
25446
|
-
const content =
|
|
25889
|
+
const content = readFileSync46(mcpConfigPath, "utf-8");
|
|
25447
25890
|
const missingMcp = state.mcpServers.filter((name) => !content.includes(name));
|
|
25448
25891
|
if (missingMcp.length > 0) {
|
|
25449
25892
|
details.push(`${missingMcp.length} MCP server(s) missing from ${slug} config`);
|
|
@@ -25465,15 +25908,22 @@ async function checkAgentSetup() {
|
|
|
25465
25908
|
const skillsDir = getSkillsDir(slug, "user");
|
|
25466
25909
|
if (!skillsDir)
|
|
25467
25910
|
continue;
|
|
25911
|
+
const adapter2 = getAdapterBySlug(slug);
|
|
25912
|
+
const mcpCoversAppSkills = !!adapter2?.mcpProvidesSkills && state.mcpServers.length > 0;
|
|
25913
|
+
const isCoveredByMcp = (name) => mcpCoversAppSkills && state.skillHashes?.[name]?.source === "app";
|
|
25468
25914
|
const missingSkills = state.skills.filter((name) => {
|
|
25469
|
-
|
|
25470
|
-
|
|
25915
|
+
if (isCoveredByMcp(name))
|
|
25916
|
+
return false;
|
|
25917
|
+
const skillPath = join53(skillsDir, name, "SKILL.md");
|
|
25918
|
+
return !existsSync56(skillPath);
|
|
25471
25919
|
});
|
|
25472
25920
|
if (missingSkills.length > 0) {
|
|
25473
25921
|
details.push(`${missingSkills.length} skill(s) missing from ${slug}`);
|
|
25474
25922
|
upgrade("warn");
|
|
25475
25923
|
} else if (state.skills.length > 0) {
|
|
25476
|
-
|
|
25924
|
+
const mcpCoveredCount = state.skills.filter(isCoveredByMcp).length;
|
|
25925
|
+
const onDiskCount = state.skills.length - mcpCoveredCount;
|
|
25926
|
+
details.push(mcpCoveredCount > 0 ? `${state.skills.length} skill(s) installed (${onDiskCount} on disk, ${mcpCoveredCount} via MCP)` : `${state.skills.length} skill(s) installed`);
|
|
25477
25927
|
}
|
|
25478
25928
|
skillsChecked = true;
|
|
25479
25929
|
break;
|
|
@@ -25492,28 +25942,28 @@ async function checkAgentSetup() {
|
|
|
25492
25942
|
};
|
|
25493
25943
|
}
|
|
25494
25944
|
function getMcpConfigPath2(slug, scope) {
|
|
25495
|
-
const home =
|
|
25945
|
+
const home = homedir32();
|
|
25496
25946
|
switch (slug) {
|
|
25497
25947
|
case "claude-code":
|
|
25498
|
-
return scope === "project" ?
|
|
25948
|
+
return scope === "project" ? join53(process.cwd(), ".mcp.json") : join53(home, ".claude", "settings.json");
|
|
25499
25949
|
case "cursor":
|
|
25500
|
-
return scope === "project" ?
|
|
25950
|
+
return scope === "project" ? join53(process.cwd(), ".cursor", "mcp.json") : join53(home, ".cursor", "mcp.json");
|
|
25501
25951
|
case "windsurf":
|
|
25502
|
-
return scope === "project" ?
|
|
25952
|
+
return scope === "project" ? join53(process.cwd(), ".windsurf", "mcp.json") : join53(home, ".windsurf", "mcp.json");
|
|
25503
25953
|
case "codex":
|
|
25504
25954
|
case "codex-app":
|
|
25505
|
-
return scope === "user" ?
|
|
25955
|
+
return scope === "user" ? join53(home, ".codex", "config.toml") : null;
|
|
25506
25956
|
case "gemini":
|
|
25507
|
-
return scope === "user" ?
|
|
25957
|
+
return scope === "user" ? join53(home, ".gemini", "settings.json") : null;
|
|
25508
25958
|
default:
|
|
25509
25959
|
return null;
|
|
25510
25960
|
}
|
|
25511
25961
|
}
|
|
25512
25962
|
async function checkWorkspacePointers() {
|
|
25513
|
-
const userStatePath =
|
|
25514
|
-
const state =
|
|
25963
|
+
const userStatePath = join53(homedir32(), ".runwork", "setup.json");
|
|
25964
|
+
const state = existsSync56(userStatePath) ? (() => {
|
|
25515
25965
|
try {
|
|
25516
|
-
return JSON.parse(
|
|
25966
|
+
return JSON.parse(readFileSync46(userStatePath, "utf-8"));
|
|
25517
25967
|
} catch {
|
|
25518
25968
|
return null;
|
|
25519
25969
|
}
|
|
@@ -25543,15 +25993,15 @@ async function checkWorkspacePointers() {
|
|
|
25543
25993
|
};
|
|
25544
25994
|
}
|
|
25545
25995
|
function getSkillsDir(slug, scope) {
|
|
25546
|
-
const home =
|
|
25996
|
+
const home = homedir32();
|
|
25547
25997
|
switch (slug) {
|
|
25548
25998
|
case "claude-code":
|
|
25549
|
-
return scope === "project" ?
|
|
25999
|
+
return scope === "project" ? join53(process.cwd(), ".claude", "skills") : join53(home, ".claude", "skills");
|
|
25550
26000
|
case "codex":
|
|
25551
26001
|
case "codex-app":
|
|
25552
|
-
return scope === "project" ?
|
|
26002
|
+
return scope === "project" ? join53(process.cwd(), ".agents", "skills") : join53(home, ".agents", "skills");
|
|
25553
26003
|
case "gemini":
|
|
25554
|
-
return scope === "project" ?
|
|
26004
|
+
return scope === "project" ? join53(process.cwd(), ".gemini", "skills") : join53(home, ".gemini", "skills");
|
|
25555
26005
|
default:
|
|
25556
26006
|
return null;
|
|
25557
26007
|
}
|
|
@@ -25604,18 +26054,18 @@ async function runAllChecks(options) {
|
|
|
25604
26054
|
// src/health/fix.ts
|
|
25605
26055
|
init_credentials();
|
|
25606
26056
|
init_remote();
|
|
25607
|
-
import { existsSync as
|
|
25608
|
-
import { join as
|
|
26057
|
+
import { existsSync as existsSync57 } from "fs";
|
|
26058
|
+
import { join as join54 } from "path";
|
|
25609
26059
|
async function applyDoctorFixes(ctx, failingNames) {
|
|
25610
26060
|
const failing = new Set(failingNames);
|
|
25611
26061
|
const outcomes = [];
|
|
25612
26062
|
if (failing.has("git-credential-helper")) {
|
|
25613
26063
|
if (ctx.credentials?.baseUrl) {
|
|
25614
|
-
await ensureGitCredentialHelper(ctx.credentials.baseUrl);
|
|
26064
|
+
const result = await ensureGitCredentialHelper(ctx.credentials.baseUrl);
|
|
25615
26065
|
outcomes.push({
|
|
25616
26066
|
name: "git-credential-helper",
|
|
25617
|
-
applied:
|
|
25618
|
-
message: "registered the runwork git credential helper"
|
|
26067
|
+
applied: result.ok,
|
|
26068
|
+
message: result.ok ? "registered the runwork git credential helper" : result.message
|
|
25619
26069
|
});
|
|
25620
26070
|
} else {
|
|
25621
26071
|
outcomes.push({
|
|
@@ -25632,7 +26082,7 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
25632
26082
|
applied: false,
|
|
25633
26083
|
message: "no project config -- run inside an app directory"
|
|
25634
26084
|
});
|
|
25635
|
-
} else if (!
|
|
26085
|
+
} else if (!existsSync57(join54(ctx.cwd, ".git"))) {
|
|
25636
26086
|
outcomes.push({
|
|
25637
26087
|
name: "git-remote",
|
|
25638
26088
|
applied: false,
|
|
@@ -25651,10 +26101,10 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
25651
26101
|
}
|
|
25652
26102
|
|
|
25653
26103
|
// src/agents/runtime-detection.ts
|
|
25654
|
-
import { existsSync as
|
|
25655
|
-
import { homedir as
|
|
25656
|
-
import { join as
|
|
25657
|
-
var RUNWORK_SESSIONS_DIR =
|
|
26104
|
+
import { existsSync as existsSync58, readFileSync as readFileSync47, statSync as statSync10, readdirSync as readdirSync17 } from "fs";
|
|
26105
|
+
import { homedir as homedir33 } from "os";
|
|
26106
|
+
import { join as join55 } from "path";
|
|
26107
|
+
var RUNWORK_SESSIONS_DIR = join55(homedir33(), ".runwork", "sessions");
|
|
25658
26108
|
function detectCurrentAgent() {
|
|
25659
26109
|
const claudeCodeSessionId = process.env.CLAUDE_CODE_SESSION_ID;
|
|
25660
26110
|
if (claudeCodeSessionId) {
|
|
@@ -25717,11 +26167,11 @@ function detectCurrentAgent() {
|
|
|
25717
26167
|
return null;
|
|
25718
26168
|
}
|
|
25719
26169
|
function readHookSessionInfo(sessionId) {
|
|
25720
|
-
const path4 =
|
|
25721
|
-
if (!
|
|
26170
|
+
const path4 = join55(RUNWORK_SESSIONS_DIR, `${sessionId}.json`);
|
|
26171
|
+
if (!existsSync58(path4))
|
|
25722
26172
|
return null;
|
|
25723
26173
|
try {
|
|
25724
|
-
const raw =
|
|
26174
|
+
const raw = readFileSync47(path4, "utf8");
|
|
25725
26175
|
const parsed = JSON.parse(raw);
|
|
25726
26176
|
return parsed;
|
|
25727
26177
|
} catch {
|
|
@@ -25729,37 +26179,37 @@ function readHookSessionInfo(sessionId) {
|
|
|
25729
26179
|
}
|
|
25730
26180
|
}
|
|
25731
26181
|
function findClaudeCodeSessionFile(sessionId) {
|
|
25732
|
-
const root =
|
|
25733
|
-
if (!
|
|
26182
|
+
const root = join55(homedir33(), ".claude", "projects");
|
|
26183
|
+
if (!existsSync58(root))
|
|
25734
26184
|
return null;
|
|
25735
26185
|
let projectDirs;
|
|
25736
26186
|
try {
|
|
25737
|
-
projectDirs =
|
|
26187
|
+
projectDirs = readdirSync17(root);
|
|
25738
26188
|
} catch {
|
|
25739
26189
|
return null;
|
|
25740
26190
|
}
|
|
25741
26191
|
for (const dir of projectDirs) {
|
|
25742
|
-
const candidate =
|
|
25743
|
-
if (
|
|
26192
|
+
const candidate = join55(root, dir, `${sessionId}.jsonl`);
|
|
26193
|
+
if (existsSync58(candidate))
|
|
25744
26194
|
return candidate;
|
|
25745
26195
|
}
|
|
25746
26196
|
return null;
|
|
25747
26197
|
}
|
|
25748
26198
|
function findCodexRolloutFile(threadId) {
|
|
25749
|
-
const root =
|
|
25750
|
-
if (!
|
|
26199
|
+
const root = join55(homedir33(), ".codex", "sessions");
|
|
26200
|
+
if (!existsSync58(root))
|
|
25751
26201
|
return null;
|
|
25752
26202
|
const stack = [root];
|
|
25753
26203
|
while (stack.length > 0) {
|
|
25754
26204
|
const dir = stack.pop();
|
|
25755
26205
|
let entries;
|
|
25756
26206
|
try {
|
|
25757
|
-
entries =
|
|
26207
|
+
entries = readdirSync17(dir);
|
|
25758
26208
|
} catch {
|
|
25759
26209
|
continue;
|
|
25760
26210
|
}
|
|
25761
26211
|
for (const entry of entries) {
|
|
25762
|
-
const full =
|
|
26212
|
+
const full = join55(dir, entry);
|
|
25763
26213
|
let s;
|
|
25764
26214
|
try {
|
|
25765
26215
|
s = statSync10(full);
|
|
@@ -25776,28 +26226,28 @@ function findCodexRolloutFile(threadId) {
|
|
|
25776
26226
|
return null;
|
|
25777
26227
|
}
|
|
25778
26228
|
function findNewestClaudeCodeSession() {
|
|
25779
|
-
const root =
|
|
25780
|
-
if (!
|
|
26229
|
+
const root = join55(homedir33(), ".claude", "projects");
|
|
26230
|
+
if (!existsSync58(root))
|
|
25781
26231
|
return null;
|
|
25782
26232
|
let projectDirs;
|
|
25783
26233
|
try {
|
|
25784
|
-
projectDirs =
|
|
26234
|
+
projectDirs = readdirSync17(root);
|
|
25785
26235
|
} catch {
|
|
25786
26236
|
return null;
|
|
25787
26237
|
}
|
|
25788
26238
|
let best = null;
|
|
25789
26239
|
for (const dir of projectDirs) {
|
|
25790
|
-
const projectPath =
|
|
26240
|
+
const projectPath = join55(root, dir);
|
|
25791
26241
|
let files;
|
|
25792
26242
|
try {
|
|
25793
|
-
files =
|
|
26243
|
+
files = readdirSync17(projectPath);
|
|
25794
26244
|
} catch {
|
|
25795
26245
|
continue;
|
|
25796
26246
|
}
|
|
25797
26247
|
for (const file of files) {
|
|
25798
26248
|
if (!file.endsWith(".jsonl"))
|
|
25799
26249
|
continue;
|
|
25800
|
-
const full =
|
|
26250
|
+
const full = join55(projectPath, file);
|
|
25801
26251
|
try {
|
|
25802
26252
|
const s = statSync10(full);
|
|
25803
26253
|
if (!best || s.mtimeMs > best.mtime) {
|
|
@@ -25815,8 +26265,8 @@ function findNewestClaudeCodeSession() {
|
|
|
25815
26265
|
return best ? { sessionId: best.sessionId, path: best.path } : null;
|
|
25816
26266
|
}
|
|
25817
26267
|
function findNewestCodexRollout() {
|
|
25818
|
-
const root =
|
|
25819
|
-
if (!
|
|
26268
|
+
const root = join55(homedir33(), ".codex", "sessions");
|
|
26269
|
+
if (!existsSync58(root))
|
|
25820
26270
|
return null;
|
|
25821
26271
|
const stack = [root];
|
|
25822
26272
|
let best = null;
|
|
@@ -25824,12 +26274,12 @@ function findNewestCodexRollout() {
|
|
|
25824
26274
|
const dir = stack.pop();
|
|
25825
26275
|
let entries;
|
|
25826
26276
|
try {
|
|
25827
|
-
entries =
|
|
26277
|
+
entries = readdirSync17(dir);
|
|
25828
26278
|
} catch {
|
|
25829
26279
|
continue;
|
|
25830
26280
|
}
|
|
25831
26281
|
for (const entry of entries) {
|
|
25832
|
-
const full =
|
|
26282
|
+
const full = join55(dir, entry);
|
|
25833
26283
|
let s;
|
|
25834
26284
|
try {
|
|
25835
26285
|
s = statSync10(full);
|
|
@@ -26061,8 +26511,8 @@ init_store();
|
|
|
26061
26511
|
init_client();
|
|
26062
26512
|
init_resolve();
|
|
26063
26513
|
import { Command as Command35 } from "commander";
|
|
26064
|
-
import { readFileSync as
|
|
26065
|
-
import { join as
|
|
26514
|
+
import { readFileSync as readFileSync48, writeFileSync as writeFileSync33, existsSync as existsSync59, mkdtempSync as mkdtempSync4 } from "fs";
|
|
26515
|
+
import { join as join56 } from "path";
|
|
26066
26516
|
import { tmpdir as tmpdir4 } from "os";
|
|
26067
26517
|
import { createHash as createHash6 } from "crypto";
|
|
26068
26518
|
|
|
@@ -26184,14 +26634,14 @@ function resolveLocalSessionShare(opts, conversation) {
|
|
|
26184
26634
|
process.exit(1);
|
|
26185
26635
|
}
|
|
26186
26636
|
const title = opts.title ?? conversation.title ?? conversation.project;
|
|
26187
|
-
const markdown = renderTranscriptMarkdown(
|
|
26637
|
+
const markdown = renderTranscriptMarkdown(readFileSync48(conversation.transcriptPath, "utf8"), family, title);
|
|
26188
26638
|
if (!markdown) {
|
|
26189
26639
|
console.error("Error: this conversation has no shareable content.");
|
|
26190
26640
|
process.exit(1);
|
|
26191
26641
|
}
|
|
26192
|
-
const tempDir = mkdtempSync4(
|
|
26193
|
-
const transcriptFile =
|
|
26194
|
-
|
|
26642
|
+
const tempDir = mkdtempSync4(join56(tmpdir4(), "runwork-share-"));
|
|
26643
|
+
const transcriptFile = join56(tempDir, "transcript.md");
|
|
26644
|
+
writeFileSync33(transcriptFile, markdown);
|
|
26195
26645
|
opts.transcriptFile = transcriptFile;
|
|
26196
26646
|
opts.nativeFile = opts.nativeFile ?? conversation.transcriptPath;
|
|
26197
26647
|
opts.sourceAgent = opts.sourceAgent ?? conversation.agentSlug;
|
|
@@ -26228,7 +26678,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26228
26678
|
console.error("Error: --transcript-file is required. Pass the path to the LLM-emitted markdown transcript.");
|
|
26229
26679
|
process.exit(1);
|
|
26230
26680
|
}
|
|
26231
|
-
if (!
|
|
26681
|
+
if (!existsSync59(opts.transcriptFile)) {
|
|
26232
26682
|
console.error(`Error: transcript file does not exist: ${opts.transcriptFile}`);
|
|
26233
26683
|
process.exit(1);
|
|
26234
26684
|
}
|
|
@@ -26249,7 +26699,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26249
26699
|
const credentials = requireAuth();
|
|
26250
26700
|
const client = new ApiClient(credentials);
|
|
26251
26701
|
const { workspaceId } = await resolveWorkspace2(client, { workspace: opts.workspace });
|
|
26252
|
-
const transcriptContent =
|
|
26702
|
+
const transcriptContent = readFileSync48(opts.transcriptFile, "utf8");
|
|
26253
26703
|
const bundles = [
|
|
26254
26704
|
{
|
|
26255
26705
|
format: "transcript",
|
|
@@ -26262,19 +26712,19 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26262
26712
|
const sourceAgent = opts.sourceAgent ?? detected?.slug ?? "generic";
|
|
26263
26713
|
let nativeFilePath = null;
|
|
26264
26714
|
if (opts.nativeFile) {
|
|
26265
|
-
if (!
|
|
26715
|
+
if (!existsSync59(opts.nativeFile)) {
|
|
26266
26716
|
console.error(`Error: --native-file path does not exist: ${opts.nativeFile}`);
|
|
26267
26717
|
process.exit(1);
|
|
26268
26718
|
}
|
|
26269
26719
|
nativeFilePath = opts.nativeFile;
|
|
26270
|
-
} else if (detected?.sessionFilePath &&
|
|
26720
|
+
} else if (detected?.sessionFilePath && existsSync59(detected.sessionFilePath)) {
|
|
26271
26721
|
nativeFilePath = detected.sessionFilePath;
|
|
26272
26722
|
}
|
|
26273
26723
|
if (nativeFilePath) {
|
|
26274
26724
|
const nativeFormat = nativeBundleFormatForAgent(sourceAgent);
|
|
26275
26725
|
if (nativeFormat) {
|
|
26276
26726
|
try {
|
|
26277
|
-
const content =
|
|
26727
|
+
const content = readFileSync48(nativeFilePath, "utf8");
|
|
26278
26728
|
bundles.push({
|
|
26279
26729
|
format: nativeFormat,
|
|
26280
26730
|
content,
|
|
@@ -26290,7 +26740,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26290
26740
|
let metadata = {};
|
|
26291
26741
|
if (opts.metadataFile) {
|
|
26292
26742
|
try {
|
|
26293
|
-
metadata = JSON.parse(
|
|
26743
|
+
metadata = JSON.parse(readFileSync48(opts.metadataFile, "utf8"));
|
|
26294
26744
|
} catch (err) {
|
|
26295
26745
|
console.error(`Error: --metadata-file is not valid JSON: ${err instanceof Error ? err.message : err}`);
|
|
26296
26746
|
process.exit(1);
|
|
@@ -26399,9 +26849,9 @@ init_client();
|
|
|
26399
26849
|
init_resolve();
|
|
26400
26850
|
init_registry_data();
|
|
26401
26851
|
import { Command as Command38 } from "commander";
|
|
26402
|
-
import { writeFileSync as
|
|
26403
|
-
import { homedir as
|
|
26404
|
-
import { join as
|
|
26852
|
+
import { writeFileSync as writeFileSync34, mkdirSync as mkdirSync29, realpathSync } from "fs";
|
|
26853
|
+
import { homedir as homedir34 } from "os";
|
|
26854
|
+
import { join as join57 } from "path";
|
|
26405
26855
|
import { spawn as spawn5 } from "child_process";
|
|
26406
26856
|
init_registry();
|
|
26407
26857
|
init_which();
|
|
@@ -26440,10 +26890,10 @@ function extractCodexUuid(rolloutContent) {
|
|
|
26440
26890
|
}
|
|
26441
26891
|
function placeClaudeJsonl(uuid, content, recipientCwd) {
|
|
26442
26892
|
const encoded = encodeClaudeCodeCwd(recipientCwd);
|
|
26443
|
-
const projectDir =
|
|
26444
|
-
|
|
26445
|
-
const placedAt =
|
|
26446
|
-
|
|
26893
|
+
const projectDir = join57(homedir34(), ".claude", "projects", encoded);
|
|
26894
|
+
mkdirSync29(projectDir, { recursive: true });
|
|
26895
|
+
const placedAt = join57(projectDir, `${uuid}.jsonl`);
|
|
26896
|
+
writeFileSync34(placedAt, content);
|
|
26447
26897
|
return { placedAt, runFromCwd: recipientCwd };
|
|
26448
26898
|
}
|
|
26449
26899
|
function placeCodexRollout(uuid, content) {
|
|
@@ -26451,11 +26901,11 @@ function placeCodexRollout(uuid, content) {
|
|
|
26451
26901
|
const yyyy = String(now.getUTCFullYear());
|
|
26452
26902
|
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
26453
26903
|
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
26454
|
-
const dir =
|
|
26455
|
-
|
|
26904
|
+
const dir = join57(homedir34(), ".codex", "sessions", yyyy, mm, dd);
|
|
26905
|
+
mkdirSync29(dir, { recursive: true });
|
|
26456
26906
|
const ts = now.toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
26457
|
-
const placedAt =
|
|
26458
|
-
|
|
26907
|
+
const placedAt = join57(dir, `rollout-${ts}-${uuid}.jsonl`);
|
|
26908
|
+
writeFileSync34(placedAt, content);
|
|
26459
26909
|
return { placedAt };
|
|
26460
26910
|
}
|
|
26461
26911
|
function pickTargetAgent(opts, sourceAgent) {
|