@adhdev/daemon-core 0.9.28 → 0.9.29

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.mjs CHANGED
@@ -65,6 +65,29 @@ function asOptionalString(value) {
65
65
  function asBoolean(value, fallback) {
66
66
  return typeof value === "boolean" ? value : fallback;
67
67
  }
68
+ function normalizeMachineProviders(value) {
69
+ if (!isPlainObject(value)) return {};
70
+ const result = {};
71
+ for (const [providerType, raw] of Object.entries(value)) {
72
+ if (!isPlainObject(raw)) continue;
73
+ const entry = {};
74
+ if (raw.enabled === true) entry.enabled = true;
75
+ if (typeof raw.executable === "string" && raw.executable.trim()) {
76
+ entry.executable = raw.executable.trim();
77
+ }
78
+ if (Array.isArray(raw.args)) {
79
+ entry.args = raw.args.filter((arg) => typeof arg === "string");
80
+ }
81
+ if (isPlainObject(raw.lastDetection)) {
82
+ entry.lastDetection = raw.lastDetection;
83
+ }
84
+ if (isPlainObject(raw.lastVerification)) {
85
+ entry.lastVerification = raw.lastVerification;
86
+ }
87
+ result[providerType] = entry;
88
+ }
89
+ return result;
90
+ }
68
91
  function normalizeConfig(raw) {
69
92
  const parsed = isPlainObject(raw) ? raw : {};
70
93
  return {
@@ -85,6 +108,7 @@ function normalizeConfig(raw) {
85
108
  machineSecret: parsed.machineSecret === null ? null : asOptionalString(parsed.machineSecret),
86
109
  registeredMachineId: asOptionalString(parsed.registeredMachineId),
87
110
  providerSettings: isPlainObject(parsed.providerSettings) ? parsed.providerSettings : {},
111
+ machineProviders: normalizeMachineProviders(parsed.machineProviders),
88
112
  ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
89
113
  providerSourceMode: resolveProviderSourceMode(parsed.providerSourceMode, parsed.disableUpstream),
90
114
  providerDir: asOptionalString(parsed.providerDir),
@@ -230,6 +254,7 @@ var init_config = __esm({
230
254
  machineSecret: null,
231
255
  registeredMachineId: void 0,
232
256
  providerSettings: {},
257
+ machineProviders: {},
233
258
  ideSettings: {},
234
259
  providerSourceMode: "normal",
235
260
  terminalSizingMode: "measured"
@@ -12753,6 +12778,8 @@ init_provider_cli_adapter();
12753
12778
  import * as os12 from "os";
12754
12779
  import * as path12 from "path";
12755
12780
  import * as crypto4 from "crypto";
12781
+ import { existsSync as existsSync10 } from "fs";
12782
+ import { execFileSync } from "child_process";
12756
12783
  import chalk from "chalk";
12757
12784
  init_config();
12758
12785
 
@@ -14826,6 +14853,27 @@ function shouldRestoreHostedRuntime(record, managerTag) {
14826
14853
  }
14827
14854
 
14828
14855
  // src/commands/cli-manager.ts
14856
+ function isExplicitCommand(command) {
14857
+ const trimmed = command.trim();
14858
+ return path12.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
14859
+ }
14860
+ function expandExecutable(command) {
14861
+ const trimmed = command.trim();
14862
+ return trimmed.startsWith("~") ? path12.join(os12.homedir(), trimmed.slice(1)) : trimmed;
14863
+ }
14864
+ function commandExists(command) {
14865
+ const trimmed = command.trim();
14866
+ if (!trimmed) return false;
14867
+ if (isExplicitCommand(trimmed)) {
14868
+ return existsSync10(expandExecutable(trimmed));
14869
+ }
14870
+ try {
14871
+ execFileSync(process.platform === "win32" ? "where" : "which", [trimmed], { stdio: "ignore" });
14872
+ return true;
14873
+ } catch {
14874
+ return false;
14875
+ }
14876
+ }
14829
14877
  var chalkModule = chalk;
14830
14878
  var chalkApi = typeof chalkModule.yellow === "function" ? chalkModule : chalkModule.default || null;
14831
14879
  function colorize(color, text) {
@@ -15091,29 +15139,33 @@ var DaemonCliManager = class {
15091
15139
  if (!trimmed) throw new Error("working directory required");
15092
15140
  const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os12.homedir()) : path12.resolve(trimmed);
15093
15141
  const normalizedType = this.providerLoader.resolveAlias(cliType);
15094
- const provider = this.providerLoader.getByAlias(cliType);
15142
+ const rawProvider = this.providerLoader.getByAlias(cliType);
15143
+ const provider = rawProvider ? this.providerLoader.resolve(normalizedType) || rawProvider : void 0;
15144
+ if (provider && (provider.category === "cli" || provider.category === "acp") && !this.providerLoader.isMachineProviderEnabled(normalizedType)) {
15145
+ const displayName = provider.displayName || provider.name || normalizedType;
15146
+ throw new Error(
15147
+ `${displayName} is disabled on this machine.
15148
+ Enable and detect this provider from the Machine Providers page before starting a runtime.`
15149
+ );
15150
+ }
15095
15151
  const key = crypto4.randomUUID();
15096
15152
  const sessionRegistry = this.deps.getSessionRegistry?.() || null;
15097
15153
  if (provider && provider.category === "acp") {
15098
15154
  const instanceManager2 = this.deps.getInstanceManager();
15099
15155
  if (!instanceManager2) throw new Error("InstanceManager not available");
15100
- const spawnCmd = provider.spawn?.command;
15101
- if (spawnCmd) {
15102
- try {
15103
- const { execSync: execSync7 } = __require("child_process");
15104
- execSync7(`which ${spawnCmd}`, { stdio: "ignore" });
15105
- } catch {
15106
- const installInfo = provider.install || `Install: check ${provider.displayName || provider.name} documentation`;
15107
- throw new Error(
15108
- `${provider.displayName || provider.name} is not installed.
15109
- Command '${spawnCmd}' not found in PATH.
15156
+ const resolvedProvider = this.providerLoader.resolve(normalizedType) || provider;
15157
+ const spawnCmd = resolvedProvider.spawn?.command;
15158
+ if (spawnCmd && !commandExists(spawnCmd)) {
15159
+ const installInfo = provider.install || `Install: check ${provider.displayName || provider.name} documentation`;
15160
+ throw new Error(
15161
+ `${provider.displayName || provider.name} is not installed.
15162
+ Command '${spawnCmd}' not found.
15110
15163
 
15111
15164
  ${installInfo}`
15112
- );
15113
- }
15165
+ );
15114
15166
  }
15115
15167
  console.log(colorize("cyan", ` \u{1F50C} Starting ACP agent: ${provider.name} (${provider.type}) in ${resolvedDir}`));
15116
- const acpInstance = new AcpProviderInstance(provider, resolvedDir, cliArgs);
15168
+ const acpInstance = new AcpProviderInstance(resolvedProvider, resolvedDir, cliArgs);
15117
15169
  await instanceManager2.addInstance(key, acpInstance, {
15118
15170
  settings: this.providerLoader.getSettings(normalizedType)
15119
15171
  });
@@ -16054,15 +16106,18 @@ var ProviderLoader = class _ProviderLoader {
16054
16106
  getCliDetectionList() {
16055
16107
  const result = [];
16056
16108
  for (const p of this.providers.values()) {
16057
- if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
16109
+ if ((p.category === "cli" || p.category === "acp") && p.spawn?.command && this.isMachineProviderEnabled(p.type)) {
16058
16110
  const versionCommand = this.getPlatformVersionCommand(p.versionCommand);
16059
16111
  const command = this.getSpawnCommand(p.type, p.spawn.command);
16112
+ const args = this.getSpawnArgs(p.type, p.spawn.args || []);
16060
16113
  result.push({
16061
16114
  id: p.type,
16062
16115
  displayName: p.displayName || p.name,
16063
16116
  icon: p.icon || "\u{1F527}",
16064
16117
  command,
16118
+ ...args.length > 0 ? { args } : {},
16065
16119
  category: p.category,
16120
+ enabled: true,
16066
16121
  ...typeof versionCommand === "string" && versionCommand.trim() ? { versionCommand: versionCommand.trim() } : {}
16067
16122
  });
16068
16123
  }
@@ -16191,9 +16246,10 @@ var ProviderLoader = class _ProviderLoader {
16191
16246
  return [...this.providers.values()].filter((p) => p.category === "ide" && p.cdpPorts).map((p) => p.type);
16192
16247
  }
16193
16248
  getSpawnCommand(type, fallback) {
16194
- const override = this.getOptionalStringSetting(type, "executablePath");
16195
- if (override) return override;
16196
- return fallback || this.providers.get(type)?.spawn?.command || type;
16249
+ const providerType = this.resolveAlias(type);
16250
+ const machineConfig = this.getMachineProviderConfig(providerType);
16251
+ if (machineConfig.executable) return machineConfig.executable;
16252
+ return fallback || this.providers.get(providerType)?.spawn?.command || providerType;
16197
16253
  }
16198
16254
  getIdeCliCommand(type, fallback) {
16199
16255
  const override = this.getOptionalStringSetting(type, "cliPathOverride");
@@ -16207,6 +16263,131 @@ var ProviderLoader = class _ProviderLoader {
16207
16263
  const osPaths = this.providers.get(type)?.paths?.[process.platform];
16208
16264
  return Array.isArray(osPaths) ? [...osPaths] : [];
16209
16265
  }
16266
+ isMachineProviderEnabled(type) {
16267
+ const providerType = this.resolveAlias(type);
16268
+ const config = this.readConfig();
16269
+ return config?.machineProviders?.[providerType]?.enabled === true;
16270
+ }
16271
+ getMachineProviderConfig(type) {
16272
+ const providerType = this.resolveAlias(type);
16273
+ const raw = this.readConfig()?.machineProviders?.[providerType];
16274
+ if (!raw || typeof raw !== "object") return {};
16275
+ const executable = typeof raw.executable === "string" && raw.executable.trim() ? raw.executable.trim() : void 0;
16276
+ return {
16277
+ ...raw.enabled === true ? { enabled: true } : {},
16278
+ ...executable ? { executable } : {},
16279
+ ...Array.isArray(raw.args) ? { args: raw.args.filter((arg) => typeof arg === "string") } : {},
16280
+ ...raw.lastDetection && typeof raw.lastDetection === "object" ? { lastDetection: raw.lastDetection } : {},
16281
+ ...raw.lastVerification && typeof raw.lastVerification === "object" ? { lastVerification: raw.lastVerification } : {}
16282
+ };
16283
+ }
16284
+ setMachineProviderConfig(type, patch) {
16285
+ const providerType = this.resolveAlias(type);
16286
+ if (!this.providers.has(providerType)) return false;
16287
+ const config = this.readConfig();
16288
+ if (!config) return false;
16289
+ try {
16290
+ if (!config.machineProviders) config.machineProviders = {};
16291
+ const current = config.machineProviders[providerType] || {};
16292
+ const next = { ...current };
16293
+ const enabledChanged = "enabled" in patch && current.enabled !== (patch.enabled === true);
16294
+ const executableChanged = "executable" in patch;
16295
+ const argsChanged = "args" in patch;
16296
+ if ("enabled" in patch) next.enabled = patch.enabled === true;
16297
+ if ("executable" in patch) {
16298
+ const executable = typeof patch.executable === "string" ? patch.executable.trim() : "";
16299
+ if (executable) next.executable = executable;
16300
+ else delete next.executable;
16301
+ }
16302
+ if ("args" in patch) {
16303
+ if (Array.isArray(patch.args)) next.args = patch.args.filter((arg) => typeof arg === "string");
16304
+ else delete next.args;
16305
+ }
16306
+ if (enabledChanged || executableChanged || argsChanged) {
16307
+ delete next.lastDetection;
16308
+ delete next.lastVerification;
16309
+ }
16310
+ if ("lastDetection" in patch) {
16311
+ if (patch.lastDetection) next.lastDetection = patch.lastDetection;
16312
+ else delete next.lastDetection;
16313
+ }
16314
+ if ("lastVerification" in patch) {
16315
+ if (patch.lastVerification) next.lastVerification = patch.lastVerification;
16316
+ else delete next.lastVerification;
16317
+ }
16318
+ config.machineProviders[providerType] = next;
16319
+ if (next.enabled !== true) {
16320
+ this.providerAvailability.set(providerType, { installed: false, detectedPath: null });
16321
+ }
16322
+ this.writeConfig(config);
16323
+ this.log(`Machine provider config updated: ${providerType}`);
16324
+ return true;
16325
+ } catch (e) {
16326
+ this.log(`Failed to save machine provider config: ${e.message}`);
16327
+ return false;
16328
+ }
16329
+ }
16330
+ setMachineProviderEnabled(type, enabled) {
16331
+ return this.setMachineProviderConfig(type, { enabled });
16332
+ }
16333
+ getMachineProviderStatus(type) {
16334
+ const providerType = this.resolveAlias(type);
16335
+ if (!this.isMachineProviderEnabled(providerType)) return "disabled";
16336
+ const availability = this.providerAvailability.get(providerType);
16337
+ if (!availability) return "enabled_unchecked";
16338
+ return availability.installed ? "detected" : "not_detected";
16339
+ }
16340
+ getSpawnArgs(type, fallback = []) {
16341
+ const machineConfig = this.getMachineProviderConfig(type);
16342
+ if (machineConfig.args) return [...machineConfig.args];
16343
+ return [...fallback];
16344
+ }
16345
+ parseArgsSetting(value) {
16346
+ const args = [];
16347
+ let current = "";
16348
+ let quote = null;
16349
+ let escaping = false;
16350
+ for (const ch of value.trim()) {
16351
+ if (escaping) {
16352
+ current += ch;
16353
+ escaping = false;
16354
+ continue;
16355
+ }
16356
+ if (ch === "\\") {
16357
+ escaping = true;
16358
+ continue;
16359
+ }
16360
+ if (quote === "single") {
16361
+ if (ch === "'") quote = null;
16362
+ else current += ch;
16363
+ continue;
16364
+ }
16365
+ if (quote === "double") {
16366
+ if (ch === '"') quote = null;
16367
+ else current += ch;
16368
+ continue;
16369
+ }
16370
+ if (ch === "'") {
16371
+ quote = "single";
16372
+ continue;
16373
+ }
16374
+ if (ch === '"') {
16375
+ quote = "double";
16376
+ continue;
16377
+ }
16378
+ if (/\s/.test(ch)) {
16379
+ if (current) {
16380
+ args.push(current);
16381
+ current = "";
16382
+ }
16383
+ continue;
16384
+ }
16385
+ current += ch;
16386
+ }
16387
+ if (escaping) current += "\\";
16388
+ if (current) args.push(current);
16389
+ return args;
16390
+ }
16210
16391
  setProviderAvailability(type, state) {
16211
16392
  this.providerAvailability.set(type, {
16212
16393
  installed: !!state.installed,
@@ -16214,18 +16395,53 @@ var ProviderLoader = class _ProviderLoader {
16214
16395
  });
16215
16396
  }
16216
16397
  setCliDetectionResults(results, replace = true) {
16398
+ const resultByType = /* @__PURE__ */ new Map();
16399
+ for (const result of results) {
16400
+ resultByType.set(this.resolveAlias(result.id), result);
16401
+ }
16217
16402
  if (replace) {
16218
16403
  for (const provider of this.providers.values()) {
16219
16404
  if (provider.category === "cli" || provider.category === "acp") {
16220
- this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
16405
+ const result = resultByType.get(provider.type);
16406
+ const installed = !!result?.installed;
16407
+ const detectedPath = result?.path || null;
16408
+ this.providerAvailability.set(provider.type, { installed, detectedPath });
16409
+ if (this.isMachineProviderEnabled(provider.type)) {
16410
+ this.setMachineProviderConfig(provider.type, {
16411
+ lastDetection: {
16412
+ ok: installed,
16413
+ stage: "detection",
16414
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
16415
+ command: this.getSpawnCommand(provider.type, provider.spawn?.command),
16416
+ path: detectedPath,
16417
+ message: installed ? "Provider command detected" : "Provider command was not detected"
16418
+ }
16419
+ });
16420
+ }
16221
16421
  }
16222
16422
  }
16423
+ return;
16223
16424
  }
16224
16425
  for (const result of results) {
16225
- this.setProviderAvailability(result.id, {
16426
+ const providerType = this.resolveAlias(result.id);
16427
+ const provider = this.providers.get(providerType);
16428
+ const detectedPath = result.path || null;
16429
+ this.setProviderAvailability(providerType, {
16226
16430
  installed: !!result.installed,
16227
- detectedPath: result.path || null
16431
+ detectedPath
16228
16432
  });
16433
+ if (provider && (provider.category === "cli" || provider.category === "acp") && this.isMachineProviderEnabled(providerType)) {
16434
+ this.setMachineProviderConfig(providerType, {
16435
+ lastDetection: {
16436
+ ok: !!result.installed,
16437
+ stage: "detection",
16438
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
16439
+ command: this.getSpawnCommand(providerType, provider.spawn?.command),
16440
+ path: detectedPath,
16441
+ message: result.installed ? "Provider command detected" : "Provider command was not detected"
16442
+ }
16443
+ });
16444
+ }
16229
16445
  }
16230
16446
  }
16231
16447
  setIdeDetectionResults(results, replace = true) {
@@ -16246,8 +16462,14 @@ var ProviderLoader = class _ProviderLoader {
16246
16462
  getAvailableProviderInfos() {
16247
16463
  return this.getAll().map((provider) => {
16248
16464
  const availability = this.providerAvailability.get(provider.type);
16465
+ const enabled = this.isMachineProviderEnabled(provider.type);
16466
+ const machineConfig = this.getMachineProviderConfig(provider.type);
16249
16467
  return {
16250
16468
  ...provider,
16469
+ enabled,
16470
+ machineStatus: this.getMachineProviderStatus(provider.type),
16471
+ ...machineConfig.lastDetection ? { lastDetection: machineConfig.lastDetection } : {},
16472
+ ...machineConfig.lastVerification ? { lastVerification: machineConfig.lastVerification } : {},
16251
16473
  ...availability ? {
16252
16474
  installed: availability.installed,
16253
16475
  detectedPath: availability.detectedPath
@@ -16394,6 +16616,13 @@ var ProviderLoader = class _ProviderLoader {
16394
16616
  }
16395
16617
  }
16396
16618
  }
16619
+ if ((resolved.category === "cli" || resolved.category === "acp") && resolved.spawn?.command) {
16620
+ resolved.spawn = {
16621
+ ...resolved.spawn,
16622
+ command: this.getSpawnCommand(type, resolved.spawn.command),
16623
+ args: this.getSpawnArgs(type, resolved.spawn.args || [])
16624
+ };
16625
+ }
16397
16626
  return resolved;
16398
16627
  }
16399
16628
  /**
@@ -16716,20 +16945,33 @@ var ProviderLoader = class _ProviderLoader {
16716
16945
  * Resolved setting value for a provider (default + user override)
16717
16946
  */
16718
16947
  getSettingValue(type, key) {
16719
- const schemaDef = this.getSettingsSchema(type)[key];
16948
+ const providerType = this.resolveAlias(type);
16949
+ const machineConfig = this.getMachineProviderConfig(providerType);
16950
+ if (key === "enabled") {
16951
+ return machineConfig.enabled === true;
16952
+ }
16953
+ if (key === "executablePath") {
16954
+ return machineConfig.executable || "";
16955
+ }
16956
+ if (key === "executableArgs") {
16957
+ const args = machineConfig.args;
16958
+ return args ? args.map((arg) => /\s/.test(arg) ? JSON.stringify(arg) : arg).join(" ") : "";
16959
+ }
16960
+ const schemaDef = this.getSettingsSchema(providerType)[key];
16720
16961
  const defaultVal = schemaDef ? key === "autoApprove" && schemaDef.type === "boolean" ? true : schemaDef.default : void 0;
16721
16962
  const config = this.readConfig();
16722
- const userVal = config?.providerSettings?.[type]?.[key];
16963
+ const userVal = config?.providerSettings?.[providerType]?.[key];
16723
16964
  return userVal !== void 0 ? userVal : defaultVal;
16724
16965
  }
16725
16966
  /**
16726
16967
  * All resolved settings for a provider (default + user override)
16727
16968
  */
16728
16969
  getSettings(type) {
16729
- const settings = this.getSettingsSchema(type);
16970
+ const providerType = this.resolveAlias(type);
16971
+ const settings = this.getSettingsSchema(providerType);
16730
16972
  const result = {};
16731
16973
  for (const [key] of Object.entries(settings)) {
16732
- result[key] = this.getSettingValue(type, key);
16974
+ result[key] = this.getSettingValue(providerType, key);
16733
16975
  }
16734
16976
  return result;
16735
16977
  }
@@ -16737,7 +16979,8 @@ var ProviderLoader = class _ProviderLoader {
16737
16979
  * Save provider setting value (writes to config.json)
16738
16980
  */
16739
16981
  setSetting(type, key, value) {
16740
- const schemaDef = this.getSettingsSchema(type)[key];
16982
+ const providerType = this.resolveAlias(type);
16983
+ const schemaDef = this.getSettingsSchema(providerType)[key];
16741
16984
  if (!schemaDef) return false;
16742
16985
  if (!schemaDef.public) return false;
16743
16986
  if (schemaDef.type === "boolean" && typeof value !== "boolean") return false;
@@ -16748,14 +16991,25 @@ var ProviderLoader = class _ProviderLoader {
16748
16991
  if (schemaDef.max !== void 0 && value > schemaDef.max) return false;
16749
16992
  }
16750
16993
  if (schemaDef.type === "select" && schemaDef.options && !schemaDef.options.includes(value)) return false;
16994
+ if (key === "enabled") {
16995
+ return this.setMachineProviderEnabled(providerType, value);
16996
+ }
16997
+ if (key === "executablePath") {
16998
+ return this.setMachineProviderConfig(providerType, { executable: value });
16999
+ }
17000
+ if (key === "executableArgs") {
17001
+ return this.setMachineProviderConfig(providerType, {
17002
+ args: value.trim() ? this.parseArgsSetting(value) : void 0
17003
+ });
17004
+ }
16751
17005
  const config = this.readConfig();
16752
17006
  if (!config) return false;
16753
17007
  try {
16754
17008
  if (!config.providerSettings) config.providerSettings = {};
16755
- if (!config.providerSettings[type]) config.providerSettings[type] = {};
16756
- config.providerSettings[type][key] = value;
17009
+ if (!config.providerSettings[providerType]) config.providerSettings[providerType] = {};
17010
+ config.providerSettings[providerType][key] = value;
16757
17011
  this.writeConfig(config);
16758
- this.log(`Setting updated: ${type}.${key} = ${JSON.stringify(value)}`);
17012
+ this.log(`Setting updated: ${providerType}.${key} = ${JSON.stringify(value)}`);
16759
17013
  return true;
16760
17014
  } catch (e) {
16761
17015
  this.log(`Failed to save setting: ${e.message}`);
@@ -16816,6 +17070,15 @@ var ProviderLoader = class _ProviderLoader {
16816
17070
  }
16817
17071
  getSyntheticSettings(type, provider) {
16818
17072
  const result = {};
17073
+ if (provider.category === "cli" || provider.category === "acp") {
17074
+ result.enabled = {
17075
+ type: "boolean",
17076
+ default: false,
17077
+ public: true,
17078
+ label: "Enabled on this machine",
17079
+ description: "Opt in before ADHDev detects, launches, or verifies this provider on this machine."
17080
+ };
17081
+ }
16819
17082
  if (!provider.settings?.autoApprove) {
16820
17083
  result.autoApprove = {
16821
17084
  type: "boolean",
@@ -16834,6 +17097,15 @@ var ProviderLoader = class _ProviderLoader {
16834
17097
  description: "Optional absolute path for this provider binary. Leave blank to use the default PATH lookup."
16835
17098
  };
16836
17099
  }
17100
+ if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executableArgs) {
17101
+ result.executableArgs = {
17102
+ type: "string",
17103
+ default: "",
17104
+ public: true,
17105
+ label: "Executable arguments",
17106
+ description: "Optional replacement for provider default command arguments. Leave blank to use the provider default."
17107
+ };
17108
+ }
16837
17109
  if (provider.category === "ide") {
16838
17110
  if (provider.cli && !provider.settings?.cliPathOverride) {
16839
17111
  result.cliPathOverride = {
@@ -17902,7 +18174,7 @@ function buildStatusSnapshot(options) {
17902
18174
  }
17903
18175
 
17904
18176
  // src/commands/upgrade-helper.ts
17905
- import { execFileSync } from "child_process";
18177
+ import { execFileSync as execFileSync2 } from "child_process";
17906
18178
  import { spawn as spawn3 } from "child_process";
17907
18179
  import * as fs8 from "fs";
17908
18180
  import * as os17 from "os";
@@ -18004,7 +18276,7 @@ function getNpmExecOptions() {
18004
18276
  function killPid(pid) {
18005
18277
  try {
18006
18278
  if (process.platform === "win32") {
18007
- execFileSync("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore" });
18279
+ execFileSync2("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore" });
18008
18280
  } else {
18009
18281
  process.kill(pid, "SIGTERM");
18010
18282
  }
@@ -18042,7 +18314,7 @@ function stopSessionHostProcesses(appName) {
18042
18314
  }
18043
18315
  if (process.platform !== "win32") {
18044
18316
  try {
18045
- const raw = execFileSync("pgrep", ["-f", "session-host-daemon"], { encoding: "utf8" }).trim();
18317
+ const raw = execFileSync2("pgrep", ["-f", "session-host-daemon"], { encoding: "utf8" }).trim();
18046
18318
  for (const line of raw.split("\n")) {
18047
18319
  const pid = Number.parseInt(line.trim(), 10);
18048
18320
  if (Number.isFinite(pid)) {
@@ -18063,9 +18335,9 @@ function removeDaemonPidFile() {
18063
18335
  function cleanupStaleGlobalInstallDirs(pkgName, surface) {
18064
18336
  const npmExecOpts = getNpmExecOptions();
18065
18337
  const prefixArgs = surface.installPrefix ? ["--prefix", surface.installPrefix] : [];
18066
- const npmRoot = execFileSync(surface.npmExecutable, ["root", "-g", ...prefixArgs], { encoding: "utf8", ...npmExecOpts }).trim();
18338
+ const npmRoot = execFileSync2(surface.npmExecutable, ["root", "-g", ...prefixArgs], { encoding: "utf8", ...npmExecOpts }).trim();
18067
18339
  if (!npmRoot) return;
18068
- const npmPrefix = surface.installPrefix || execFileSync(surface.npmExecutable, ["prefix", "-g", ...prefixArgs], { encoding: "utf8", ...npmExecOpts }).trim();
18340
+ const npmPrefix = surface.installPrefix || execFileSync2(surface.npmExecutable, ["prefix", "-g", ...prefixArgs], { encoding: "utf8", ...npmExecOpts }).trim();
18069
18341
  const binDir = process.platform === "win32" ? npmPrefix : path16.join(npmPrefix, "bin");
18070
18342
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
18071
18343
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
@@ -18128,7 +18400,7 @@ async function runDaemonUpgradeHelper(payload) {
18128
18400
  cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
18129
18401
  const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
18130
18402
  appendUpgradeLog(`Installing ${spec}`);
18131
- const installOutput = execFileSync(
18403
+ const installOutput = execFileSync2(
18132
18404
  installCommand.command,
18133
18405
  installCommand.args,
18134
18406
  {
@@ -18685,6 +18957,33 @@ var DaemonCommandRouter = class {
18685
18957
  }
18686
18958
  return { ...result };
18687
18959
  }
18960
+ // ─── Detect providers ───
18961
+ case "detect_provider": {
18962
+ const providerType = typeof args?.providerType === "string" ? args.providerType.trim() : "";
18963
+ if (!providerType) return { success: false, error: "providerType is required" };
18964
+ const normalizedType = this.deps.providerLoader.resolveAlias(providerType);
18965
+ const provider = this.deps.providerLoader.getByAlias(providerType);
18966
+ if (!provider) return { success: false, error: `Provider not found: ${providerType}` };
18967
+ if (provider.category !== "cli" && provider.category !== "acp") {
18968
+ return { success: false, error: `Provider detection is only supported for CLI/ACP providers: ${providerType}` };
18969
+ }
18970
+ if (!this.deps.providerLoader.isMachineProviderEnabled(normalizedType)) {
18971
+ return { success: false, error: `Provider is disabled on this machine: ${providerType}` };
18972
+ }
18973
+ const detected = await detectCLI(normalizedType, this.deps.providerLoader, { includeVersion: false });
18974
+ this.deps.providerLoader.setCliDetectionResults([{
18975
+ id: normalizedType,
18976
+ installed: !!detected,
18977
+ path: detected?.path
18978
+ }], false);
18979
+ this.deps.onStatusChange?.();
18980
+ return {
18981
+ success: true,
18982
+ providerType: normalizedType,
18983
+ detected: !!detected,
18984
+ path: detected?.path || null
18985
+ };
18986
+ }
18688
18987
  // ─── Detect IDEs ───
18689
18988
  case "detect_ides": {
18690
18989
  const results = await detectIDEs(this.deps.providerLoader);
@@ -18730,13 +19029,16 @@ var DaemonCommandRouter = class {
18730
19029
  this.deps.cdpManagers
18731
19030
  );
18732
19031
  const targetSession = sessionEntries.find((entry) => entry.id === sessionId);
18733
- const completionMarker = targetSession ? getSessionCompletionMarker(targetSession) : "";
19032
+ const requestedCompletionMarker = typeof args?.completionMarker === "string" ? args.completionMarker.trim() : "";
19033
+ const completionMarker = requestedCompletionMarker || (targetSession ? getSessionCompletionMarker(targetSession) : "");
19034
+ const requestedProviderSessionId = typeof args?.providerSessionId === "string" ? args.providerSessionId.trim() : "";
19035
+ const providerSessionId = requestedProviderSessionId || targetSession?.providerSessionId;
18734
19036
  const next = markSessionSeen(
18735
19037
  currentState,
18736
19038
  sessionId,
18737
19039
  typeof args?.seenAt === "number" ? args.seenAt : Date.now(),
18738
19040
  completionMarker,
18739
- targetSession?.providerSessionId
19041
+ providerSessionId
18740
19042
  );
18741
19043
  if (READ_DEBUG_ENABLED2) {
18742
19044
  LOG.info("RecentRead", `mark_session_seen sessionId=${sessionId} seenAt=${String(args?.seenAt || "")} prevSeenAt=${String(prevSeenAt)} nextSeenAt=${String(next.sessionReads?.[sessionId] || 0)} marker=${completionMarker || "-"}`);
@@ -26605,10 +26907,11 @@ async function initDaemonComponents(config) {
26605
26907
  if (!providerType || targetCategory === "cli" || targetCategory === "acp") {
26606
26908
  if (providerType && targetProvider) {
26607
26909
  const detected = await detectCLI(targetProvider.type, providerLoader, { includeVersion: false });
26608
- providerLoader.setProviderAvailability(targetProvider.type, {
26910
+ providerLoader.setCliDetectionResults([{
26911
+ id: targetProvider.type,
26609
26912
  installed: !!detected,
26610
- detectedPath: detected?.path || null
26611
- });
26913
+ path: detected?.path
26914
+ }], false);
26612
26915
  } else {
26613
26916
  providerLoader.setCliDetectionResults(await detectCLIs(providerLoader, { includeVersion: false }), true);
26614
26917
  }