agent-phonon 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4624,7 +4624,12 @@ function fileSize(p) {
4624
4624
  import { spawn as spawn3 } from "node:child_process";
4625
4625
  import { homedir as homedir5 } from "node:os";
4626
4626
  import { join as join7 } from "node:path";
4627
- import { existsSync as existsSync7, readdirSync, statSync as statSync2 } from "node:fs";
4627
+ import { existsSync as existsSync7, readdirSync, statSync as statSync2, readFileSync as readFileSync4 } from "node:fs";
4628
+ var CODEX_FALLBACK_MODELS = [
4629
+ { id: "gpt-5.5", available: true },
4630
+ { id: "gpt-5.4", available: true },
4631
+ { id: "gpt-5.3-codex", available: true }
4632
+ ];
4628
4633
  var CAPABILITIES2 = {
4629
4634
  nativeSession: true,
4630
4635
  // exec resume <thread_id>
@@ -4674,6 +4679,60 @@ function resolveCodexSessionFile(threadId, codexHome = join7(homedir5(), ".codex
4674
4679
  hits.sort((a, b) => b.mtime - a.mtime);
4675
4680
  return hits[0].path;
4676
4681
  }
4682
+ function parseCodexConfig(configPath = join7(homedir5(), ".codex", "config.toml")) {
4683
+ if (!existsSync7(configPath))
4684
+ return {};
4685
+ const text = readFileSync4(configPath, "utf8");
4686
+ const model = text.match(/^model\s*=\s*"([^"]+)"/m)?.[1];
4687
+ const providerId = text.match(/^model_provider\s*=\s*"([^"]+)"/m)?.[1];
4688
+ let baseUrl;
4689
+ if (providerId) {
4690
+ const escaped = providerId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4691
+ const section = text.match(new RegExp(`\\[model_providers\\.${escaped}\\]([\\s\\S]*?)(?:\\n\\[|$)`))?.[1];
4692
+ baseUrl = section?.match(/^base_url\s*=\s*"([^"]+)"/m)?.[1];
4693
+ }
4694
+ return { model, providerId, baseUrl };
4695
+ }
4696
+ function normalizeOpenAiModels(data) {
4697
+ const rows = Array.isArray(data.data) ? data.data : Array.isArray(data) ? data : [];
4698
+ const out = [];
4699
+ const seen = /* @__PURE__ */ new Set();
4700
+ for (const row of rows) {
4701
+ const obj = row;
4702
+ const id = typeof obj.id === "string" ? obj.id : void 0;
4703
+ if (!id || seen.has(id))
4704
+ continue;
4705
+ seen.add(id);
4706
+ out.push({ id, ...typeof obj.name === "string" ? { displayName: obj.name } : {}, available: true });
4707
+ }
4708
+ return out;
4709
+ }
4710
+ function fetchJson(url, headers) {
4711
+ return new Promise((resolve5, reject) => {
4712
+ const u = new URL(url);
4713
+ const req = (u.protocol === "http:" ? import("node:http") : import("node:https")).then((mod) => mod.request(url, { method: "GET", headers, timeout: 5e3 }, (res) => {
4714
+ let body = "";
4715
+ res.setEncoding("utf8");
4716
+ res.on("data", (d) => body += d);
4717
+ res.on("end", () => {
4718
+ if ((res.statusCode ?? 500) < 200 || (res.statusCode ?? 500) >= 300) {
4719
+ reject(new Error(`GET ${url} failed: ${res.statusCode}`));
4720
+ return;
4721
+ }
4722
+ try {
4723
+ resolve5(JSON.parse(body));
4724
+ } catch (e) {
4725
+ reject(e);
4726
+ }
4727
+ });
4728
+ }));
4729
+ req.then((r) => {
4730
+ r.on("timeout", () => r.destroy(new Error(`GET ${url} timeout`)));
4731
+ r.on("error", reject);
4732
+ r.end();
4733
+ }, reject);
4734
+ });
4735
+ }
4677
4736
  var CodexSession = class {
4678
4737
  sessionId;
4679
4738
  model;
@@ -4726,6 +4785,7 @@ ${prompt}`;
4726
4785
  return new Promise((resolve5) => {
4727
4786
  const child = spawn3(this.env.binPath ?? "codex", args2, {
4728
4787
  cwd: this.cwd,
4788
+ shell: process.platform === "win32",
4729
4789
  env: { ...process.env, ...opts.environment ?? {}, ...this.env.apiKey ? { OPENAI_API_KEY: this.env.apiKey } : {} }
4730
4790
  });
4731
4791
  this.current = child;
@@ -4836,6 +4896,7 @@ var CodexAdapter = class {
4836
4896
  async discoverAgents() {
4837
4897
  const version = await this.probeVersion();
4838
4898
  const available = version !== null;
4899
+ const models = await this.discoverModels();
4839
4900
  return [{
4840
4901
  agentId: "codex",
4841
4902
  displayName: "Codex",
@@ -4843,7 +4904,7 @@ var CodexAdapter = class {
4843
4904
  available,
4844
4905
  ...available ? {} : { unavailableReason: "codex CLI not found" },
4845
4906
  ...version ? { version } : {},
4846
- models: this.env.models?.length ? this.env.models : [{ id: this.env.defaultModel, available: true }],
4907
+ models,
4847
4908
  capabilities: CAPABILITIES2,
4848
4909
  scannedAt: (/* @__PURE__ */ new Date()).toISOString()
4849
4910
  }];
@@ -4851,9 +4912,31 @@ var CodexAdapter = class {
4851
4912
  async createSession(params) {
4852
4913
  return new CodexSession(params.sessionId, params.model, params.cwd, this.env);
4853
4914
  }
4915
+ async discoverModels() {
4916
+ if (this.env.models?.length)
4917
+ return this.env.models;
4918
+ const cfg = parseCodexConfig();
4919
+ const baseUrl = this.env.baseUrl ?? cfg.baseUrl;
4920
+ if (baseUrl) {
4921
+ try {
4922
+ const data = await fetchJson(`${baseUrl.replace(/\/$/, "")}/models`, this.env.apiKey ? { Authorization: `Bearer ${this.env.apiKey}` } : void 0);
4923
+ const models = normalizeOpenAiModels(data);
4924
+ if (models.length > 0)
4925
+ return models;
4926
+ } catch {
4927
+ }
4928
+ }
4929
+ const configured = this.env.defaultModel !== "default" ? this.env.defaultModel : cfg.model;
4930
+ const base = configured ? [{ id: configured, available: true }] : [];
4931
+ const seen = new Set(base.map((m) => m.id));
4932
+ for (const m of CODEX_FALLBACK_MODELS)
4933
+ if (!seen.has(m.id))
4934
+ base.push(m);
4935
+ return base.length > 0 ? base : CODEX_FALLBACK_MODELS;
4936
+ }
4854
4937
  probeVersion() {
4855
4938
  return new Promise((resolve5) => {
4856
- const child = spawn3(this.env.binPath ?? "codex", ["--version"]);
4939
+ const child = spawn3(this.env.binPath ?? "codex", ["--version"], { shell: process.platform === "win32" });
4857
4940
  let out = "";
4858
4941
  child.stdout.on("data", (d) => out += d.toString());
4859
4942
  child.on("error", () => resolve5(null));
@@ -5097,7 +5180,29 @@ import { randomUUID as randomUUID3 } from "node:crypto";
5097
5180
  import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
5098
5181
  import { homedir as homedir7 } from "node:os";
5099
5182
  import { join as join9 } from "node:path";
5100
- import { existsSync as existsSync9 } from "node:fs";
5183
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "node:fs";
5184
+ var HERMES_PROVIDER_FALLBACK_MODELS = {
5185
+ // Hermes' built-in Copilot picker exposes these account/integrator-safe model ids.
5186
+ copilot: [
5187
+ "gpt-5.4",
5188
+ "gpt-5.4-mini",
5189
+ "gpt-5-mini",
5190
+ "gpt-5.3-codex",
5191
+ "gpt-5.2-codex",
5192
+ "gpt-4.1",
5193
+ "gpt-4o",
5194
+ "gpt-4o-mini",
5195
+ "claude-sonnet-4.6",
5196
+ "claude-sonnet-4",
5197
+ "claude-sonnet-4.5",
5198
+ "claude-haiku-4.5",
5199
+ "gemini-3.1-pro-preview",
5200
+ "gemini-3-pro-preview",
5201
+ "gemini-3-flash-preview",
5202
+ "gemini-2.5-pro"
5203
+ ],
5204
+ zai: ["glm-4.5", "glm-4.5-air", "glm-4.6", "glm-4.7", "glm-5", "glm-5-turbo", "glm-5.1", "glm-5.2"]
5205
+ };
5101
5206
  var CAPABILITIES4 = {
5102
5207
  nativeSession: true,
5103
5208
  // --resume / --pass-session-id
@@ -5117,6 +5222,49 @@ var CAPABILITIES4 = {
5117
5222
  // -z 是纯文本一次性输出,非流式 → 用 final 事件
5118
5223
  limits: { maxConcurrentSessions: 4 }
5119
5224
  };
5225
+ function parseHermesConfig(configPath = join9(homedir7(), ".hermes", "config.yaml")) {
5226
+ if (!existsSync9(configPath))
5227
+ return {};
5228
+ const text = readFileSync5(configPath, "utf8");
5229
+ const modelBlock = text.match(/(?:^|\n)model:\n([\s\S]*?)(?:\n[A-Za-z_][A-Za-z0-9_-]*:|$)/)?.[1] ?? "";
5230
+ const defaultModel = modelBlock.match(/^\s+default:\s*['"]?([^'"\n]+)['"]?/m)?.[1]?.trim();
5231
+ const provider = modelBlock.match(/^\s+provider:\s*['"]?([^'"\n]+)['"]?/m)?.[1]?.trim();
5232
+ const catalogBlock = text.match(/(?:^|\n)model_catalog:\n([\s\S]*?)(?:\n[A-Za-z_][A-Za-z0-9_-]*:|$)/)?.[1] ?? "";
5233
+ const catalogUrl = catalogBlock.match(/^\s+url:\s*['"]?([^'"\n]+)['"]?/m)?.[1]?.trim();
5234
+ return { defaultModel, provider, catalogUrl };
5235
+ }
5236
+ function fetchHermesCatalogModels(url, provider) {
5237
+ return new Promise((resolve5) => {
5238
+ const u = new URL(url);
5239
+ const reqMod = u.protocol === "http:" ? import("node:http") : import("node:https");
5240
+ reqMod.then((mod) => {
5241
+ const req = mod.request(url, { method: "GET", timeout: 5e3 }, (res) => {
5242
+ let body = "";
5243
+ res.setEncoding("utf8");
5244
+ res.on("data", (d) => body += d);
5245
+ res.on("end", () => {
5246
+ try {
5247
+ const data = JSON.parse(body);
5248
+ const providers = data.providers ?? {};
5249
+ const rows = provider ? providers[provider]?.models ?? [] : Object.values(providers).flatMap((p) => p.models ?? []);
5250
+ const seen = /* @__PURE__ */ new Set();
5251
+ resolve5(rows.flatMap((m) => {
5252
+ if (!m.id || seen.has(m.id))
5253
+ return [];
5254
+ seen.add(m.id);
5255
+ return [{ id: m.id, ...m.description ? { displayName: m.description } : {}, available: true }];
5256
+ }));
5257
+ } catch {
5258
+ resolve5([]);
5259
+ }
5260
+ });
5261
+ });
5262
+ req.on("timeout", () => req.destroy());
5263
+ req.on("error", () => resolve5([]));
5264
+ req.end();
5265
+ }, () => resolve5([]));
5266
+ });
5267
+ }
5120
5268
  var HermesSession = class {
5121
5269
  sessionId;
5122
5270
  model;
@@ -5165,6 +5313,7 @@ ${prompt}`;
5165
5313
  return new Promise((resolve5) => {
5166
5314
  const child = spawn5(this.env.binPath ?? "hermes", args2, {
5167
5315
  cwd: this.cwd,
5316
+ shell: process.platform === "win32",
5168
5317
  env: { ...process.env, ...opts.environment ?? {}, HERMES_PROFILE: this.profile }
5169
5318
  });
5170
5319
  this.current = child;
@@ -5309,6 +5458,10 @@ var HermesAdapter = class {
5309
5458
  }];
5310
5459
  }
5311
5460
  const profiles = await this.listProfiles();
5461
+ const cfg = parseHermesConfig();
5462
+ const provider = this.env.provider ?? cfg.provider;
5463
+ const catalogModels = cfg.catalogUrl ? await fetchHermesCatalogModels(cfg.catalogUrl, provider) : [];
5464
+ const providerFallbackModels = provider ? (HERMES_PROVIDER_FALLBACK_MODELS[provider] ?? []).map((id) => ({ id, available: true })) : [];
5312
5465
  const now = (/* @__PURE__ */ new Date()).toISOString();
5313
5466
  return profiles.map((p) => ({
5314
5467
  agentId: `hermes:${p.name}`,
@@ -5316,11 +5469,23 @@ var HermesAdapter = class {
5316
5469
  adapter: "hermes",
5317
5470
  available: true,
5318
5471
  ...version ? { version } : {},
5319
- models: p.model ? [{ id: p.model, available: true }] : this.env.defaultModel ? [{ id: this.env.defaultModel, available: true }] : [{ id: "default", displayName: "Hermes default", available: true }],
5472
+ models: this.modelsForProfile(p.model, [...catalogModels, ...providerFallbackModels], cfg.defaultModel),
5320
5473
  capabilities: CAPABILITIES4,
5321
5474
  scannedAt: now
5322
5475
  }));
5323
5476
  }
5477
+ modelsForProfile(profileModel, catalogModels, configDefault) {
5478
+ const preferred = profileModel ?? this.env.defaultModel ?? configDefault;
5479
+ const models = preferred ? [{ id: preferred, available: true }] : [];
5480
+ const seen = new Set(models.map((m) => m.id));
5481
+ for (const m of catalogModels) {
5482
+ if (!seen.has(m.id)) {
5483
+ models.push(m);
5484
+ seen.add(m.id);
5485
+ }
5486
+ }
5487
+ return models.length > 0 ? models : [{ id: "default", displayName: "Hermes default", available: true }];
5488
+ }
5324
5489
  async createSession(params) {
5325
5490
  const profile = params.agentId.includes(":") ? params.agentId.split(":")[1] : this.defaultProfile;
5326
5491
  const model = params.model && params.model !== "default" ? params.model : this.env.defaultModel ?? "";
@@ -5329,7 +5494,7 @@ var HermesAdapter = class {
5329
5494
  /** 枚举 Hermes profile(去 ANSI 色解析 profile list)。 */
5330
5495
  listProfiles() {
5331
5496
  return new Promise((resolve5) => {
5332
- const child = spawn5(this.env.binPath ?? "hermes", ["profile", "list"]);
5497
+ const child = spawn5(this.env.binPath ?? "hermes", ["profile", "list"], { shell: process.platform === "win32" });
5333
5498
  let out = "";
5334
5499
  child.stdout.on("data", (d) => out += d.toString());
5335
5500
  child.on("error", () => resolve5([{ name: this.defaultProfile }]));
@@ -5349,7 +5514,7 @@ var HermesAdapter = class {
5349
5514
  }
5350
5515
  probeVersion() {
5351
5516
  return new Promise((resolve5) => {
5352
- const child = spawn5(this.env.binPath ?? "hermes", ["--version"]);
5517
+ const child = spawn5(this.env.binPath ?? "hermes", ["--version"], { shell: process.platform === "win32" });
5353
5518
  let out = "";
5354
5519
  child.stdout.on("data", (d) => out += d.toString());
5355
5520
  child.on("error", () => resolve5(null));
@@ -5584,7 +5749,7 @@ ${prompt}`;
5584
5749
  if (v !== void 0)
5585
5750
  childEnv[k] = v;
5586
5751
  }
5587
- const child = spawn6(this.env.binPath ?? "claude", args2, { cwd: this.cwd, env: { ...childEnv, ...opts.environment ?? {} } });
5752
+ const child = spawn6(this.env.binPath ?? "claude", args2, { cwd: this.cwd, shell: process.platform === "win32", env: { ...childEnv, ...opts.environment ?? {} } });
5588
5753
  this.current = child;
5589
5754
  let buf = "";
5590
5755
  let acc = "";
@@ -5726,7 +5891,7 @@ var ClaudeCodeAdapter = class {
5726
5891
  }
5727
5892
  probeVersion() {
5728
5893
  return new Promise((resolve5) => {
5729
- const child = spawn6(this.env.binPath ?? "claude", ["--version"]);
5894
+ const child = spawn6(this.env.binPath ?? "claude", ["--version"], { shell: process.platform === "win32" });
5730
5895
  let out = "";
5731
5896
  child.stdout.on("data", (d) => out += d.toString());
5732
5897
  child.on("error", () => resolve5(null));
@@ -6068,7 +6233,7 @@ var PhononConnection = class {
6068
6233
  };
6069
6234
 
6070
6235
  // src/config.ts
6071
- import { readFileSync as readFileSync4, existsSync as existsSync11, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, chmodSync as chmodSync2 } from "node:fs";
6236
+ import { readFileSync as readFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, chmodSync as chmodSync2 } from "node:fs";
6072
6237
  import { homedir as homedir9, hostname as hostname2 } from "node:os";
6073
6238
  import { join as join11, dirname as dirname4 } from "node:path";
6074
6239
  var DEFAULT_DIR = join11(homedir9(), ".agent-phonon");
@@ -6088,7 +6253,7 @@ function loadConfig(path = DEFAULT_CONFIG_PATH) {
6088
6253
  if (!existsSync11(path)) {
6089
6254
  throw new Error(`config not found at ${path} \u2014 run 'agent-phonon init' first`);
6090
6255
  }
6091
- const raw = JSON.parse(readFileSync4(path, "utf8"));
6256
+ const raw = JSON.parse(readFileSync6(path, "utf8"));
6092
6257
  const d = defaultConfig();
6093
6258
  return {
6094
6259
  deviceId: raw.deviceId ?? d.deviceId,
@@ -6127,7 +6292,7 @@ function redactConfig(cfg) {
6127
6292
  function readOpenClawGatewayToken() {
6128
6293
  try {
6129
6294
  const p = join11(homedir9(), ".openclaw", "openclaw.json");
6130
- const cfg = JSON.parse(readFileSync4(p, "utf8"));
6295
+ const cfg = JSON.parse(readFileSync6(p, "utf8"));
6131
6296
  return cfg?.gateway?.auth?.token;
6132
6297
  } catch {
6133
6298
  return void 0;
@@ -6136,27 +6301,43 @@ function readOpenClawGatewayToken() {
6136
6301
 
6137
6302
  // src/commands.ts
6138
6303
  import { spawnSync } from "node:child_process";
6139
- import { existsSync as existsSync12, cpSync, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync5 } from "node:fs";
6140
- import { homedir as homedir10 } from "node:os";
6141
- import { join as join12, dirname as dirname5 } from "node:path";
6304
+ import { existsSync as existsSync12, cpSync, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync7 } from "node:fs";
6305
+ import { homedir as homedir10, platform as platform2 } from "node:os";
6306
+ import { join as join12, dirname as dirname5, delimiter } from "node:path";
6142
6307
  import { fileURLToPath } from "node:url";
6143
6308
  function probe(bin, args2 = ["--version"]) {
6144
- const r = spawnSync(bin, args2, { timeout: 8e3 });
6309
+ const r = spawnSync(bin, args2, { timeout: 8e3, shell: platform2() === "win32" });
6145
6310
  return { ok: r.status === 0, out: (r.stdout?.toString() ?? "").trim().split("\n")[0] ?? "" };
6146
6311
  }
6312
+ function executableNames(bin) {
6313
+ if (platform2() !== "win32") return [bin];
6314
+ const exts = (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.toLowerCase());
6315
+ return [bin, ...exts.map((e) => `${bin}${e}`), ...exts.map((e) => `${bin}${e.toUpperCase()}`)];
6316
+ }
6147
6317
  function commandPath(bin) {
6148
- const candidates = [
6149
- join12(homedir10(), ".npm-global", "bin", bin),
6150
- join12(homedir10(), ".local", "bin", bin),
6151
- join12(homedir10(), ".bun", "bin", bin),
6152
- join12(homedir10(), ".cargo", "bin", bin),
6153
- `/opt/homebrew/bin/${bin}`,
6154
- `/usr/local/bin/${bin}`,
6155
- `/usr/bin/${bin}`
6318
+ const home = homedir10();
6319
+ const appData = process.env.APPDATA;
6320
+ const localAppData = process.env.LOCALAPPDATA;
6321
+ const dirs = [
6322
+ join12(home, ".npm-global", "bin"),
6323
+ join12(home, ".local", "bin"),
6324
+ join12(home, ".bun", "bin"),
6325
+ join12(home, ".cargo", "bin"),
6326
+ ...appData ? [join12(appData, "npm")] : [],
6327
+ ...localAppData ? [join12(localAppData, "Programs"), join12(localAppData, "Microsoft", "WindowsApps")] : [],
6328
+ "/opt/homebrew/bin",
6329
+ "/usr/local/bin",
6330
+ "/usr/bin",
6331
+ ...(process.env.PATH ?? "").split(delimiter).filter(Boolean)
6156
6332
  ];
6157
- for (const p of candidates) if (existsSync12(p)) return p;
6158
- const r = spawnSync("bash", ["-lc", `command -v ${bin}`], { timeout: 3e3 });
6159
- const out = (r.stdout?.toString() ?? "").trim().split("\n")[0];
6333
+ for (const dir of dirs) {
6334
+ for (const name of executableNames(bin)) {
6335
+ const p = join12(dir, name);
6336
+ if (existsSync12(p)) return p;
6337
+ }
6338
+ }
6339
+ const r = platform2() === "win32" ? spawnSync("where.exe", [bin], { timeout: 3e3 }) : spawnSync("sh", ["-lc", `command -v ${bin}`], { timeout: 3e3 });
6340
+ const out = (r.stdout?.toString() ?? "").trim().split(/\r?\n/)[0];
6160
6341
  return r.status === 0 && out ? out : void 0;
6161
6342
  }
6162
6343
  function gatewayReachable(url) {
@@ -6169,8 +6350,12 @@ function gatewayReachable(url) {
6169
6350
  }
6170
6351
  }
6171
6352
  function openCodeBin() {
6172
- const bundled = join12(homedir10(), ".opencode", "bin", "opencode");
6173
- return existsSync12(bundled) ? bundled : "opencode";
6353
+ const bundledDir = join12(homedir10(), ".opencode", "bin");
6354
+ for (const name of executableNames("opencode")) {
6355
+ const p = join12(bundledDir, name);
6356
+ if (existsSync12(p)) return p;
6357
+ }
6358
+ return commandPath("opencode") ?? "opencode";
6174
6359
  }
6175
6360
  function autoDetectAdapters(adapters) {
6176
6361
  const out = [...adapters];
@@ -6310,7 +6495,7 @@ function cmdPluginInstall(which) {
6310
6495
  mkdirSync4(dist, { recursive: true });
6311
6496
  cpSync(join12(pluginSrc, "dist"), join12(dist, "dist"), { recursive: true });
6312
6497
  cpSync(join12(pluginSrc, "openclaw.plugin.json"), join12(dist, "openclaw.plugin.json"));
6313
- const pkg = JSON.parse(readFileSync5(join12(pluginSrc, "package.json"), "utf8"));
6498
+ const pkg = JSON.parse(readFileSync7(join12(pluginSrc, "package.json"), "utf8"));
6314
6499
  delete pkg.devDependencies;
6315
6500
  writeFileSync4(join12(dist, "package.json"), JSON.stringify(pkg, null, 2));
6316
6501
  console.log(`exported clean plugin \u2192 ${dist}`);