agentsw 0.5.4 → 0.6.0

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.
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { backupFile, home, readJsonIfExists, writeFileAtomic } from "../fsutil.js";
4
4
  import { slugFromBaseUrl } from "../slug.js";
5
+ import { stripApiVersion } from "./wire.js";
5
6
  const dir = path.join(home, ".claude");
6
7
  const settingsFile = path.join(dir, "settings.json");
7
8
  /**
@@ -18,7 +19,7 @@ export const claudecode = {
18
19
  const notes = [];
19
20
  const settings = readJsonIfExists(settingsFile) ?? {};
20
21
  const env = { ...settings.env };
21
- env.ANTHROPIC_BASE_URL = provider.baseUrl;
22
+ env.ANTHROPIC_BASE_URL = stripApiVersion(provider.baseUrl);
22
23
  env.ANTHROPIC_AUTH_TOKEN = provider.apiKey;
23
24
  delete env.ANTHROPIC_API_KEY; // AUTH_TOKEN and API_KEY conflict; keep one
24
25
  env.ANTHROPIC_MODEL = provider.defaultModel;
@@ -38,7 +39,7 @@ export const claudecode = {
38
39
  async prune(provider) {
39
40
  const settings = readJsonIfExists(settingsFile);
40
41
  const env = settings?.env;
41
- if (!settings || !env || env.ANTHROPIC_BASE_URL !== provider.baseUrl) {
42
+ if (!settings || !env || env.ANTHROPIC_BASE_URL !== stripApiVersion(provider.baseUrl)) {
42
43
  return { app: this.id, changed: [], notes: [], skipped: "claude env does not point at this provider" };
43
44
  }
44
45
  for (const key of [
@@ -2,7 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import YAML from "yaml";
4
4
  import { backupFile, expandHome, home, localAppDataDir, readTextIfExists, writeFileAtomic } from "../fsutil.js";
5
- import { apiValue, classifyApi, mergeModels } from "./wire.js";
5
+ import { apiValue, classifyApi, mergeModels, stripApiVersion } from "./wire.js";
6
6
  /** `$DSH_HOME`, or `~/.dsh` (packages/util/home-paths `resolveDshHome`). */
7
7
  function dshHome() {
8
8
  const env = process.env.DSH_HOME?.trim();
@@ -91,7 +91,7 @@ export const dsh = {
91
91
  displayName: provider.name,
92
92
  apiKeyEnv: ref, // credential reference, never the secret
93
93
  api: apiValue(provider.protocol, provider.openaiApi, prev?.api),
94
- baseURL: provider.baseUrl,
94
+ baseURL: stripApiVersion(provider.baseUrl),
95
95
  models: mergeModels(prev?.models, provider.models.map(modelEntry), OWNED_MODEL_KEYS),
96
96
  };
97
97
  if (YAML.isMap(doc.getIn(at))) {
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import YAML from "yaml";
4
4
  import { backupFile, home, localAppDataDir, readTextIfExists, writeFileAtomic } from "../fsutil.js";
5
+ import { stripApiVersion } from "./wire.js";
5
6
  function hermesHome() {
6
7
  const env = process.env.HERMES_HOME?.trim();
7
8
  if (env)
@@ -46,7 +47,7 @@ export const hermes = {
46
47
  }
47
48
  const entry = {
48
49
  name: provider.name,
49
- api: provider.baseUrl,
50
+ api: stripApiVersion(provider.baseUrl),
50
51
  key_env: keyVar,
51
52
  transport: provider.protocol === "anthropic" ? "anthropic_messages" : "chat_completions",
52
53
  default_model: provider.defaultModel,
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import YAML from "yaml";
4
4
  import { backupFile, home, readTextIfExists, writeFileAtomic } from "../fsutil.js";
5
5
  import { looksLikeEnvName } from "../slug.js";
6
+ import { stripApiVersion } from "./wire.js";
6
7
  import { apiValue, classifyApi, entryApi, mergeModels, stripConflictingOverrides } from "./wire.js";
7
8
  /** Per-model keys this adapter writes; one that stops being emitted is cleared, not inherited. */
8
9
  const OWNED_MODEL_KEYS = ["id", "name", "reasoning", "input", "contextWindow", "maxTokens", "cost"];
@@ -55,7 +56,7 @@ export const omp = {
55
56
  if (conflicts.length)
56
57
  notes.push(`dropped model overrides pointing elsewhere: ${conflicts.join(", ")}`);
57
58
  const entry = {
58
- baseUrl: provider.baseUrl,
59
+ baseUrl: stripApiVersion(provider.baseUrl),
59
60
  apiKey: provider.apiKey, // omp treats value as env-var name first, then literal
60
61
  api,
61
62
  models,
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { appDataDir, backupFile, readJsonIfExists, writeFileAtomic } from "../fsutil.js";
4
+ import { stripApiVersion } from "./wire.js";
4
5
  const configDir = process.env.OPENCODE_CONFIG_DIR?.trim() || appDataDir("opencode");
5
6
  const configFile = path.join(configDir, "opencode.json");
6
7
  /**
@@ -63,7 +64,7 @@ export const opencode = {
63
64
  name: provider.name,
64
65
  options: {
65
66
  ...prev.options,
66
- baseURL: provider.baseUrl,
67
+ baseURL: stripApiVersion(provider.baseUrl),
67
68
  apiKey: provider.apiKey,
68
69
  },
69
70
  models,
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { backupFile, home, readJsonIfExists, writeFileAtomic } from "../fsutil.js";
4
4
  import { looksLikeEnvName } from "../slug.js";
5
+ import { stripApiVersion } from "./wire.js";
5
6
  import { apiValue, classifyApi, entryApi, mergeModels, stripConflictingOverrides } from "./wire.js";
6
7
  /** Per-model keys this adapter writes; one that stops being emitted is cleared, not inherited. */
7
8
  const OWNED_MODEL_KEYS = [
@@ -86,7 +87,7 @@ export function piStyleTarget(opts) {
86
87
  providers[provider.id] = {
87
88
  ...prev,
88
89
  name: provider.name,
89
- baseUrl: provider.baseUrl,
90
+ baseUrl: stripApiVersion(provider.baseUrl),
90
91
  apiKey: provider.apiKey, // literal key; pi treats "$VAR"/"!cmd" as indirection, prime also accepts bare env names
91
92
  api,
92
93
  models,
@@ -107,3 +107,12 @@ export function stripConflictingOverrides(models, api, baseUrl) {
107
107
  }
108
108
  return dropped;
109
109
  }
110
+ /**
111
+ * Strip a trailing API version segment (e.g. `/v1`, `/v2beta`) from a base URL.
112
+ * Use this when writing to apps whose own SDK appends `/v1/...` to the base URL
113
+ * (Anthropic SDK, OpenAI SDK, omp, pi, hermes, dsh). Codex is the exception —
114
+ * it appends `/responses` directly, so it keeps the `/v1` segment.
115
+ */
116
+ export function stripApiVersion(baseUrl) {
117
+ return baseUrl.replace(/\/v\d+(?:beta\d*)?\/?$/i, "");
118
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentsw",
3
- "version": "0.5.4",
3
+ "version": "0.6.0",
4
4
  "description": "CLI provider switcher for coding agents: manage OpenAI/Anthropic-protocol providers in one place and sync them, with models.dev metadata, into Claude Code, Codex, omp, pi, prime-agent, opencode, Hermes, WorkBuddy and DeepSeek Harness",
5
5
  "type": "module",
6
6
  "repository": {