runwork 0.25.2 → 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.
Files changed (2) hide show
  1. package/dist/index.js +393 -86
  2. 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 pickTransport() {
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);
@@ -7983,7 +8022,7 @@ function createKeyboardListener() {
7983
8022
  }
7984
8023
 
7985
8024
  // src/generated/version.ts
7986
- var VERSION = "0.25.2";
8025
+ var VERSION = "0.25.3";
7987
8026
 
7988
8027
  // src/commands/dev.ts
7989
8028
  var exports_dev = {};
@@ -8912,6 +8951,32 @@ var init_dev = __esm(() => {
8912
8951
  devCommand.addCommand(devAttachCommand);
8913
8952
  });
8914
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
+
8915
8980
  // src/utils/which.ts
8916
8981
  import { platform } from "os";
8917
8982
  import { isAbsolute } from "path";
@@ -8932,11 +8997,25 @@ function whichAllLines(name) {
8932
8997
  return [];
8933
8998
  }
8934
8999
  }
8935
- function isBinaryRunnable(binary) {
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) {
8936
9015
  const cached = runnableCache.get(binary);
8937
9016
  if (cached !== undefined)
8938
9017
  return cached;
8939
- let ok = false;
9018
+ let probe;
8940
9019
  try {
8941
9020
  const spec = toSpawnSpec(binary, ["--version"]);
8942
9021
  execFileSync(spec.command, spec.args, {
@@ -8944,12 +9023,16 @@ function isBinaryRunnable(binary) {
8944
9023
  timeout: RUNNABLE_PROBE_TIMEOUT_MS,
8945
9024
  ...spec.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
8946
9025
  });
8947
- ok = true;
8948
- } catch {
8949
- ok = false;
9026
+ probe = { runnable: true, reason: "ran" };
9027
+ } catch (err) {
9028
+ probe = { runnable: false, reason: classifyProbeError(err) };
8950
9029
  }
8951
- runnableCache.set(binary, ok);
8952
- return ok;
9030
+ if (probe.reason !== "timeout")
9031
+ runnableCache.set(binary, probe);
9032
+ return probe;
9033
+ }
9034
+ function isBinaryRunnable(binary) {
9035
+ return probeBinaryRunnable(binary).runnable;
8953
9036
  }
8954
9037
  function toSpawnSpec(binary, args) {
8955
9038
  if (isAbsolute(binary)) {
@@ -8997,6 +9080,7 @@ function isAppRunning(appName) {
8997
9080
  var RUNNABLE_PROBE_TIMEOUT_MS = 1e4, runnableCache;
8998
9081
  var init_which = __esm(() => {
8999
9082
  init_subprocess();
9083
+ init_detection_probes();
9000
9084
  runnableCache = new Map;
9001
9085
  });
9002
9086
 
@@ -9820,7 +9904,7 @@ var init_registry_data = __esm(() => {
9820
9904
  name: "GitHub Copilot CLI",
9821
9905
  description: "GitHub Copilot in the terminal",
9822
9906
  category: "cli",
9823
- detection: { method: "binary", target: "gh" },
9907
+ detection: { method: "binary", target: "copilot" },
9824
9908
  logo: "vscode",
9825
9909
  skillsPaths: { global: ".copilot/skills", project: ".github/skills" },
9826
9910
  mcpConfigPath: ".copilot/mcp-config.json",
@@ -9841,7 +9925,7 @@ var init_registry_data = __esm(() => {
9841
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" } },
9842
9926
  { slug: "firebender", name: "Firebender", description: "AI coding agent", category: "cli", detection: { method: "binary", target: "firebender" }, skillsPaths: { global: ".firebender/skills", project: ".firebender/skills" } },
9843
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" } },
9844
- { 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" } },
9845
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" } },
9846
9930
  { slug: "junie", name: "Junie", description: "JetBrains AI coding agent", category: "ide", detection: { method: "binary", target: "junie" }, skillsPaths: { global: ".junie/skills", project: ".junie/skills" } },
9847
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" } },
@@ -11034,11 +11118,33 @@ function readJsonConfig(filePath) {
11034
11118
  return {};
11035
11119
  try {
11036
11120
  return JSON.parse(readFileSync23(filePath, "utf-8"));
11037
- } catch {
11121
+ } catch (err) {
11122
+ console.warn(` [config] ${filePath} is not valid JSON: ${err instanceof Error ? err.message : err}`);
11038
11123
  return {};
11039
11124
  }
11040
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
+ }
11041
11144
  function writeJsonConfig(filePath, config) {
11145
+ const { bad, cause } = existingContentIsUnparseable(filePath);
11146
+ if (bad)
11147
+ throw new ConfigParseError(filePath, cause);
11042
11148
  mkdirSync14(dirname7(filePath), { recursive: true });
11043
11149
  writeFileSync14(filePath, JSON.stringify(config, null, 2) + `
11044
11150
  `);
@@ -11082,9 +11188,20 @@ function mergeJsonMcpServers(filePath, servers, topKey) {
11082
11188
  writeJsonConfig(filePath, config);
11083
11189
  return true;
11084
11190
  }
11191
+ var ConfigParseError;
11085
11192
  var init_json_config = __esm(() => {
11086
11193
  init_types();
11087
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
+ };
11088
11205
  });
11089
11206
 
11090
11207
  // src/agents/utils/skill-removal.ts
@@ -11639,24 +11756,6 @@ function vlog(...args) {
11639
11756
  }
11640
11757
  var verbose = false;
11641
11758
 
11642
- // src/agents/detection-probes.ts
11643
- function powershellQuote(value) {
11644
- return `'${value.replace(/'/g, "''")}'`;
11645
- }
11646
- function isValidBundleId(id) {
11647
- return /^[A-Za-z0-9][A-Za-z0-9.-]*$/.test(id);
11648
- }
11649
- function macosBundleIdProbeScript(id) {
11650
- 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`;
11651
- }
11652
- function appxPackageProbeScript(pkg) {
11653
- return `$p = Get-AppxPackage -Name ${powershellQuote(pkg)} -ErrorAction SilentlyContinue; if ($null -ne $p) { exit 0 } exit 1`;
11654
- }
11655
- function startAppProbeScript(pattern) {
11656
- const p = powershellQuote(pattern);
11657
- 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`;
11658
- }
11659
-
11660
11759
  // src/agents/detection.ts
11661
11760
  import { execFile } from "child_process";
11662
11761
  import { existsSync as existsSync31 } from "fs";
@@ -11762,6 +11861,7 @@ var init_detection = __esm(() => {
11762
11861
  init_which();
11763
11862
  init_registry_data();
11764
11863
  init_registry();
11864
+ init_detection_probes();
11765
11865
  NOT_DETECTED = { detected: false };
11766
11866
  });
11767
11867
 
@@ -11986,8 +12086,7 @@ ${instructions}`;
11986
12086
  }
11987
12087
  if (!hadFile && !config.modelPreference && !config.permissionRules)
11988
12088
  return;
11989
- mkdirSync16(join26(settingsPath, ".."), { recursive: true });
11990
- writeFileSync16(settingsPath, JSON.stringify(settings, null, 2));
12089
+ writeJsonConfig(settingsPath, settings);
11991
12090
  }
11992
12091
  async readManagedBlock(_scope) {
11993
12092
  return;
@@ -12034,7 +12133,7 @@ ${instructions}`;
12034
12133
  delete settings.enabledPlugins[key];
12035
12134
  }
12036
12135
  }
12037
- writeFileSync16(settingsPath, JSON.stringify(settings, null, 2));
12136
+ writeJsonConfig(settingsPath, settings);
12038
12137
  } catch {}
12039
12138
  }
12040
12139
  const pluginDir = this.getPluginDir();
@@ -12926,7 +13025,7 @@ var init_claude_desktop = __esm(() => {
12926
13025
  prefs.ccdScheduledTasksEnabled = false;
12927
13026
  }
12928
13027
  }
12929
- writeFileSync18(configPath, JSON.stringify(desktopConfig, null, 2));
13028
+ writeJsonConfig(configPath, desktopConfig);
12930
13029
  }
12931
13030
  async cleanup(_scope, _manifest) {
12932
13031
  removeRunworkMcpServers(getMcpConfigPath(), "mcpServers");
@@ -14468,7 +14567,7 @@ var init_cline = __esm(() => {
14468
14567
  }
14469
14568
  }
14470
14569
  mkdirSync22(join32(globalStatePath, ".."), { recursive: true });
14471
- writeFileSync22(globalStatePath, JSON.stringify(state, null, 2));
14570
+ writeJsonConfig(globalStatePath, state);
14472
14571
  }
14473
14572
  async removeSkills(skillFilenames, scope) {
14474
14573
  if (scope !== "project")
@@ -14581,7 +14680,7 @@ var init_gemini = __esm(() => {
14581
14680
  settings.tools.exclude = config.permissionRules.deny;
14582
14681
  }
14583
14682
  mkdirSync23(join33(settingsPath, ".."), { recursive: true });
14584
- writeFileSync23(settingsPath, JSON.stringify(settings, null, 2));
14683
+ writeJsonConfig(settingsPath, settings);
14585
14684
  }
14586
14685
  async readUsageStats(lastSyncAt) {
14587
14686
  try {
@@ -14775,7 +14874,7 @@ var init_gemini = __esm(() => {
14775
14874
  });
14776
14875
 
14777
14876
  // src/agents/generic-adapter.ts
14778
- import { existsSync as existsSync40, mkdirSync as mkdirSync24, rmSync as rmSync12, writeFileSync as writeFileSync24 } from "fs";
14877
+ import { mkdirSync as mkdirSync24, writeFileSync as writeFileSync24 } from "fs";
14779
14878
  import { join as join34 } from "path";
14780
14879
  import { homedir as homedir16 } from "os";
14781
14880
  var GenericAgentAdapter;
@@ -14786,6 +14885,7 @@ var init_generic_adapter = __esm(() => {
14786
14885
  init_instruction_hint();
14787
14886
  init_registry();
14788
14887
  init_detection();
14888
+ init_trash();
14789
14889
  GenericAgentAdapter = class GenericAgentAdapter extends RegistryDetectedAdapter {
14790
14890
  name;
14791
14891
  slug;
@@ -14832,12 +14932,7 @@ var init_generic_adapter = __esm(() => {
14832
14932
  return 0;
14833
14933
  for (const skill of skills) {
14834
14934
  if (skill.name !== skill.filename) {
14835
- const oldDir = join34(baseDir, skill.name);
14836
- if (existsSync40(oldDir)) {
14837
- try {
14838
- rmSync12(oldDir, { recursive: true, force: true });
14839
- } catch {}
14840
- }
14935
+ moveToTrash(join34(baseDir, skill.name), `skill renamed to ${skill.filename}`);
14841
14936
  }
14842
14937
  const skillDir = join34(baseDir, skill.filename);
14843
14938
  mkdirSync24(skillDir, { recursive: true });
@@ -15594,10 +15689,12 @@ function selectAnalyst(requestedSlug) {
15594
15689
  const resolved = resolve3(requested);
15595
15690
  if (!resolved)
15596
15691
  return { candidates: [], broken };
15597
- if (isBinaryRunnable(resolved.command)) {
15692
+ const probe = probeBinaryRunnable(resolved.command);
15693
+ if (probe.runnable) {
15598
15694
  return { chosen: resolved, candidates: [resolved], broken };
15599
15695
  }
15600
- broken.push(requested.binary);
15696
+ if (probe.reason !== "timeout")
15697
+ broken.push(requested.binary);
15601
15698
  return { candidates: [], broken };
15602
15699
  }
15603
15700
  const candidates = [];
@@ -15605,9 +15702,10 @@ function selectAnalyst(requestedSlug) {
15605
15702
  const resolved = resolve3(a);
15606
15703
  if (!resolved)
15607
15704
  continue;
15608
- if (isBinaryRunnable(resolved.command))
15705
+ const probe = probeBinaryRunnable(resolved.command);
15706
+ if (probe.runnable)
15609
15707
  candidates.push(resolved);
15610
- else
15708
+ else if (probe.reason !== "timeout")
15611
15709
  broken.push(a.binary);
15612
15710
  }
15613
15711
  return { chosen: candidates[0], candidates, broken };
@@ -23896,9 +23994,15 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
23896
23994
  };
23897
23995
  const mcpFailedAdapters = new Set;
23898
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();
23899
24002
  for (const adapter2 of adapters) {
23900
24003
  if (isConnectOnlyAgent(getRegistryAgent(adapter2.slug))) {
23901
24004
  vlog(` [${adapter2.name}] Connect-only agent: no local files to sync`);
24005
+ agentOutcomes[adapter2.slug] = { at: syncStartedAt, ok: true, skipped: true };
23902
24006
  summary.adaptersProcessed++;
23903
24007
  continue;
23904
24008
  }
@@ -23960,15 +24064,32 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
23960
24064
  await adapter2.writeInstructionHint(instructionHint, scope);
23961
24065
  summary.instructionHintWrites++;
23962
24066
  vlog(` [${adapter2.name}] Updated instruction hints (${scope})`);
23963
- if (adapter2.writeBuiltInHooks) {
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 {
23964
24074
  await adapter2.writeBuiltInHooks(scope);
23965
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}`);
23966
24080
  }
23967
- } catch (err) {
23968
- adapterFailedAnyScope = true;
23969
- console.warn(` [${adapter2.name}] Failed (${scope}): ${err instanceof Error ? err.message : err}`);
23970
24081
  }
23971
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 };
23972
24093
  summary.adaptersProcessed++;
23973
24094
  if (adapterFailedAnyScope)
23974
24095
  summary.adaptersFailed++;
@@ -23999,7 +24120,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
23999
24120
  await adapter2.writeTeamInstructions(fullInstructions, scope);
24000
24121
  teamInstructionsApplied = true;
24001
24122
  vlog(` [${adapter2.name}] Updated team instructions (${scope})`);
24002
- } 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
+ }
24003
24127
  }
24004
24128
  }
24005
24129
  if (agentConfigs) {
@@ -24020,7 +24144,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
24020
24144
  agentConfigsApplied++;
24021
24145
  const configKeys = Object.keys(configWithoutInstructions).join(", ");
24022
24146
  vlog(` [${adapter2.name}] Updated agent config: ${configKeys} (${scope})`);
24023
- } 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
+ }
24024
24151
  }
24025
24152
  }
24026
24153
  }
@@ -24102,7 +24229,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
24102
24229
  }
24103
24230
  if (team)
24104
24231
  agentConfigsApplied++;
24105
- } catch {}
24232
+ } catch (err) {
24233
+ configFailedAdapters.add(adapter2.slug);
24234
+ console.warn(` [${adapter2.name}] Failed agent config: ${err instanceof Error ? err.message : err}`);
24235
+ }
24106
24236
  }
24107
24237
  state.agentDefaultsVersion = AGENT_DEFAULTS_SCHEMA_VERSION;
24108
24238
  }
@@ -24118,6 +24248,21 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
24118
24248
  }
24119
24249
  const prevMcpNames = state.mcpServers ?? [];
24120
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;
24121
24266
  state.mcpServers = mcpEntries.map((e) => e.name);
24122
24267
  state.skills = remoteSkills.map((s) => s.name);
24123
24268
  state.skillFilenames = [
@@ -24700,10 +24845,11 @@ var buildPluginCommand = new Command29("build-plugin").description("Build an ins
24700
24845
 
24701
24846
  // src/commands/uninstall.ts
24702
24847
  init_prompt();
24848
+ init_subprocess();
24703
24849
  await init_detect();
24704
24850
  import { Command as Command30 } from "commander";
24705
- import { existsSync as existsSync55, readFileSync as readFileSync44, rmSync as rmSync13, unlinkSync as unlinkSync9 } from "fs";
24706
- import { join as join52 } from "path";
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";
24707
24853
  import { homedir as homedir31 } from "os";
24708
24854
  function loadSetupState4(filePath) {
24709
24855
  if (!existsSync55(filePath))
@@ -24714,6 +24860,125 @@ function loadSetupState4(filePath) {
24714
24860
  return null;
24715
24861
  }
24716
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
+ }
24717
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) => {
24718
24983
  const projectStatePath = join52(process.cwd(), ".runwork", "setup.json");
24719
24984
  const userStatePath = join52(homedir31(), ".runwork", "setup.json");
@@ -24754,6 +25019,13 @@ This will remove all Runwork configuration from your local agents:
24754
25019
  } else {
24755
25020
  console.log(" - Setup state and auth credentials (~/.runwork/)");
24756
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");
24757
25029
  console.log("");
24758
25030
  if (!opts.yes) {
24759
25031
  const confirmed = await promptConfirm("Proceed with uninstall?");
@@ -24772,7 +25044,7 @@ This will remove all Runwork configuration from your local agents:
24772
25044
  for (const { state, label } of entries) {
24773
25045
  const scopes = state.scope === "both" ? ["project", "user"] : [state.scope];
24774
25046
  const manifest = {
24775
- 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, "-")),
24776
25048
  mcpServerNames: state.mcpServers ?? []
24777
25049
  };
24778
25050
  for (const slug of state.configuredAgents) {
@@ -24797,27 +25069,50 @@ This will remove all Runwork configuration from your local agents:
24797
25069
  }
24798
25070
  }
24799
25071
  const stateDir = label === "project" ? join52(process.cwd(), ".runwork") : join52(homedir31(), ".runwork");
24800
- if (opts.keepAuth && label === "user") {
24801
- const setupFile = join52(stateDir, "setup.json");
24802
- if (existsSync55(setupFile)) {
24803
- try {
24804
- unlinkSync9(setupFile);
24805
- console.log(` Removed ${setupFile} (kept credentials)`);
24806
- } catch (err) {
24807
- console.warn(` Failed to remove ${setupFile}: ${err instanceof Error ? err.message : err}`);
24808
- errors++;
24809
- }
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}`);
24810
25078
  }
24811
- } else if (existsSync55(stateDir)) {
24812
- try {
24813
- rmSync13(stateDir, { recursive: true, force: true });
24814
- console.log(` Removed ${stateDir}`);
24815
- } catch (err) {
24816
- 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}`);
24817
25081
  errors++;
24818
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
+ }
24819
25089
  }
24820
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
+ }
24821
25116
  console.log("");
24822
25117
  if (errors > 0) {
24823
25118
  console.log(`Uninstall completed with ${errors} warning${errors > 1 ? "s" : ""}. ${cleanedAgents} agent${cleanedAgents > 1 ? "s" : ""} cleaned.`);
@@ -25041,6 +25336,7 @@ import { Command as Command34 } from "commander";
25041
25336
 
25042
25337
  // src/health/checks.ts
25043
25338
  init_subprocess();
25339
+ init_http();
25044
25340
  init_store();
25045
25341
  init_client();
25046
25342
  init_http();
@@ -25242,9 +25538,20 @@ async function checkAuthAndNetwork(ctx) {
25242
25538
  network: { name: "network", status: "pass", message: `API reachable (${elapsed}ms)` }
25243
25539
  };
25244
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}` };
25245
25552
  return {
25246
25553
  auth: { name: "auth", status: "skip", message: "could not verify (network error)" },
25247
- network: { name: "network", status: "fail", message: `API unreachable: ${message}` }
25554
+ network
25248
25555
  };
25249
25556
  }
25250
25557
  }
@@ -25794,7 +26101,7 @@ async function applyDoctorFixes(ctx, failingNames) {
25794
26101
  }
25795
26102
 
25796
26103
  // src/agents/runtime-detection.ts
25797
- import { existsSync as existsSync58, readFileSync as readFileSync47, statSync as statSync10, readdirSync as readdirSync16 } from "fs";
26104
+ import { existsSync as existsSync58, readFileSync as readFileSync47, statSync as statSync10, readdirSync as readdirSync17 } from "fs";
25798
26105
  import { homedir as homedir33 } from "os";
25799
26106
  import { join as join55 } from "path";
25800
26107
  var RUNWORK_SESSIONS_DIR = join55(homedir33(), ".runwork", "sessions");
@@ -25877,7 +26184,7 @@ function findClaudeCodeSessionFile(sessionId) {
25877
26184
  return null;
25878
26185
  let projectDirs;
25879
26186
  try {
25880
- projectDirs = readdirSync16(root);
26187
+ projectDirs = readdirSync17(root);
25881
26188
  } catch {
25882
26189
  return null;
25883
26190
  }
@@ -25897,7 +26204,7 @@ function findCodexRolloutFile(threadId) {
25897
26204
  const dir = stack.pop();
25898
26205
  let entries;
25899
26206
  try {
25900
- entries = readdirSync16(dir);
26207
+ entries = readdirSync17(dir);
25901
26208
  } catch {
25902
26209
  continue;
25903
26210
  }
@@ -25924,7 +26231,7 @@ function findNewestClaudeCodeSession() {
25924
26231
  return null;
25925
26232
  let projectDirs;
25926
26233
  try {
25927
- projectDirs = readdirSync16(root);
26234
+ projectDirs = readdirSync17(root);
25928
26235
  } catch {
25929
26236
  return null;
25930
26237
  }
@@ -25933,7 +26240,7 @@ function findNewestClaudeCodeSession() {
25933
26240
  const projectPath = join55(root, dir);
25934
26241
  let files;
25935
26242
  try {
25936
- files = readdirSync16(projectPath);
26243
+ files = readdirSync17(projectPath);
25937
26244
  } catch {
25938
26245
  continue;
25939
26246
  }
@@ -25967,7 +26274,7 @@ function findNewestCodexRollout() {
25967
26274
  const dir = stack.pop();
25968
26275
  let entries;
25969
26276
  try {
25970
- entries = readdirSync16(dir);
26277
+ entries = readdirSync17(dir);
25971
26278
  } catch {
25972
26279
  continue;
25973
26280
  }
@@ -26204,7 +26511,7 @@ init_store();
26204
26511
  init_client();
26205
26512
  init_resolve();
26206
26513
  import { Command as Command35 } from "commander";
26207
- import { readFileSync as readFileSync48, writeFileSync as writeFileSync32, existsSync as existsSync59, mkdtempSync as mkdtempSync4 } from "fs";
26514
+ import { readFileSync as readFileSync48, writeFileSync as writeFileSync33, existsSync as existsSync59, mkdtempSync as mkdtempSync4 } from "fs";
26208
26515
  import { join as join56 } from "path";
26209
26516
  import { tmpdir as tmpdir4 } from "os";
26210
26517
  import { createHash as createHash6 } from "crypto";
@@ -26334,7 +26641,7 @@ function resolveLocalSessionShare(opts, conversation) {
26334
26641
  }
26335
26642
  const tempDir = mkdtempSync4(join56(tmpdir4(), "runwork-share-"));
26336
26643
  const transcriptFile = join56(tempDir, "transcript.md");
26337
- writeFileSync32(transcriptFile, markdown);
26644
+ writeFileSync33(transcriptFile, markdown);
26338
26645
  opts.transcriptFile = transcriptFile;
26339
26646
  opts.nativeFile = opts.nativeFile ?? conversation.transcriptPath;
26340
26647
  opts.sourceAgent = opts.sourceAgent ?? conversation.agentSlug;
@@ -26542,7 +26849,7 @@ init_client();
26542
26849
  init_resolve();
26543
26850
  init_registry_data();
26544
26851
  import { Command as Command38 } from "commander";
26545
- import { writeFileSync as writeFileSync33, mkdirSync as mkdirSync29, realpathSync } from "fs";
26852
+ import { writeFileSync as writeFileSync34, mkdirSync as mkdirSync29, realpathSync } from "fs";
26546
26853
  import { homedir as homedir34 } from "os";
26547
26854
  import { join as join57 } from "path";
26548
26855
  import { spawn as spawn5 } from "child_process";
@@ -26586,7 +26893,7 @@ function placeClaudeJsonl(uuid, content, recipientCwd) {
26586
26893
  const projectDir = join57(homedir34(), ".claude", "projects", encoded);
26587
26894
  mkdirSync29(projectDir, { recursive: true });
26588
26895
  const placedAt = join57(projectDir, `${uuid}.jsonl`);
26589
- writeFileSync33(placedAt, content);
26896
+ writeFileSync34(placedAt, content);
26590
26897
  return { placedAt, runFromCwd: recipientCwd };
26591
26898
  }
26592
26899
  function placeCodexRollout(uuid, content) {
@@ -26598,7 +26905,7 @@ function placeCodexRollout(uuid, content) {
26598
26905
  mkdirSync29(dir, { recursive: true });
26599
26906
  const ts = now.toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
26600
26907
  const placedAt = join57(dir, `rollout-${ts}-${uuid}.jsonl`);
26601
- writeFileSync33(placedAt, content);
26908
+ writeFileSync34(placedAt, content);
26602
26909
  return { placedAt };
26603
26910
  }
26604
26911
  function pickTargetAgent(opts, sourceAgent) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runwork",
3
- "version": "0.25.2",
3
+ "version": "0.25.3",
4
4
  "description": "CLI for Runwork: develop, preview, and deploy Runwork apps from your local machine.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Runwork, Inc. <info@runwork.ai> (https://www.runwork.ai)",