agentsw 0.5.5 → 0.6.1
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/targets/claudecode.js +3 -2
- package/dist/targets/dsh.js +2 -2
- package/dist/targets/hermes.js +14 -4
- package/dist/targets/omp.js +4 -3
- package/dist/targets/opencode.js +12 -9
- package/dist/targets/pistyle.js +4 -3
- package/dist/targets/wire.js +12 -0
- package/package.json +1 -1
|
@@ -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 [
|
package/dist/targets/dsh.js
CHANGED
|
@@ -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, sdkBaseUrl } 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: sdkBaseUrl(provider.protocol, 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))) {
|
package/dist/targets/hermes.js
CHANGED
|
@@ -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 { sdkBaseUrl } from "./wire.js";
|
|
5
6
|
function hermesHome() {
|
|
6
7
|
const env = process.env.HERMES_HOME?.trim();
|
|
7
8
|
if (env)
|
|
@@ -46,9 +47,13 @@ export const hermes = {
|
|
|
46
47
|
}
|
|
47
48
|
const entry = {
|
|
48
49
|
name: provider.name,
|
|
49
|
-
api: provider.baseUrl,
|
|
50
|
+
api: sdkBaseUrl(provider.protocol, provider.baseUrl),
|
|
50
51
|
key_env: keyVar,
|
|
51
|
-
transport: provider.protocol === "anthropic"
|
|
52
|
+
transport: provider.protocol === "anthropic"
|
|
53
|
+
? "anthropic_messages"
|
|
54
|
+
: provider.openaiApi === "responses"
|
|
55
|
+
? "codex_responses"
|
|
56
|
+
: "chat_completions",
|
|
52
57
|
default_model: provider.defaultModel,
|
|
53
58
|
models,
|
|
54
59
|
};
|
|
@@ -157,9 +162,14 @@ export const hermes = {
|
|
|
157
162
|
return Object.entries(parsed.providers).flatMap(([id, entry]) => {
|
|
158
163
|
if (!entry || typeof entry.api !== "string" || !entry.api)
|
|
159
164
|
return [];
|
|
160
|
-
const protocol = entry.transport === "anthropic_messages"
|
|
165
|
+
const protocol = entry.transport === "anthropic_messages"
|
|
166
|
+
? "anthropic"
|
|
167
|
+
: ["chat_completions", "codex_responses"].includes(entry.transport ?? "")
|
|
168
|
+
? "openai"
|
|
169
|
+
: undefined;
|
|
161
170
|
if (!protocol)
|
|
162
171
|
return [];
|
|
172
|
+
const openaiApi = protocol === "openai" ? (entry.transport === "codex_responses" ? "responses" : "completions") : undefined;
|
|
163
173
|
const keyEnv = typeof entry.key_env === "string" && entry.key_env ? entry.key_env : undefined;
|
|
164
174
|
const apiKey = keyEnv ? readEnv(keyEnv) : undefined;
|
|
165
175
|
const models = Object.keys(entry.models ?? {});
|
|
@@ -167,7 +177,7 @@ export const hermes = {
|
|
|
167
177
|
const defaultModel = entry.default_model ?? active;
|
|
168
178
|
if (defaultModel && !models.includes(defaultModel))
|
|
169
179
|
models.unshift(defaultModel);
|
|
170
|
-
return [{ id, name: entry.name ?? id, protocol, baseUrl: entry.api, apiKey, keyEnv, models, defaultModel, source: self }];
|
|
180
|
+
return [{ id, name: entry.name ?? id, protocol, openaiApi, baseUrl: entry.api, apiKey, keyEnv, models, defaultModel, source: self }];
|
|
171
181
|
});
|
|
172
182
|
},
|
|
173
183
|
};
|
package/dist/targets/omp.js
CHANGED
|
@@ -3,7 +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 { apiValue, classifyApi, entryApi, mergeModels, stripConflictingOverrides } from "./wire.js";
|
|
6
|
+
import { apiValue, classifyApi, entryApi, mergeModels, sdkBaseUrl, stripConflictingOverrides } from "./wire.js";
|
|
7
7
|
/** Per-model keys this adapter writes; one that stops being emitted is cleared, not inherited. */
|
|
8
8
|
const OWNED_MODEL_KEYS = ["id", "name", "reasoning", "input", "contextWindow", "maxTokens", "cost"];
|
|
9
9
|
const agentDir = path.join(home, ".omp", "agent");
|
|
@@ -33,6 +33,7 @@ export const omp = {
|
|
|
33
33
|
const prev = doc.getIn(["providers", provider.id])?.toJSON?.();
|
|
34
34
|
const anthropic = provider.protocol === "anthropic";
|
|
35
35
|
const api = apiValue(provider.protocol, provider.openaiApi, entryApi(prev ?? {}));
|
|
36
|
+
const baseUrl = sdkBaseUrl(provider.protocol, provider.baseUrl);
|
|
36
37
|
const models = mergeModels(prev?.models, provider.models.map((m) => ({
|
|
37
38
|
id: m.id,
|
|
38
39
|
...(m.name ? { name: m.name } : {}),
|
|
@@ -51,11 +52,11 @@ export const omp = {
|
|
|
51
52
|
}
|
|
52
53
|
: {}),
|
|
53
54
|
})), OWNED_MODEL_KEYS);
|
|
54
|
-
const conflicts = stripConflictingOverrides(models, api,
|
|
55
|
+
const conflicts = stripConflictingOverrides(models, api, baseUrl);
|
|
55
56
|
if (conflicts.length)
|
|
56
57
|
notes.push(`dropped model overrides pointing elsewhere: ${conflicts.join(", ")}`);
|
|
57
58
|
const entry = {
|
|
58
|
-
baseUrl
|
|
59
|
+
baseUrl,
|
|
59
60
|
apiKey: provider.apiKey, // omp treats value as env-var name first, then literal
|
|
60
61
|
api,
|
|
61
62
|
models,
|
package/dist/targets/opencode.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { appDataDir, backupFile, readJsonIfExists, writeFileAtomic } from "../fsutil.js";
|
|
4
|
-
const
|
|
5
|
-
const configFile =
|
|
3
|
+
import { appDataDir, backupFile, expandHome, readJsonIfExists, writeFileAtomic } from "../fsutil.js";
|
|
4
|
+
const customDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
5
|
+
const configFile = customDir
|
|
6
|
+
? path.join(expandHome(customDir), "opencode.json")
|
|
7
|
+
: path.join(appDataDir("opencode"), "opencode.json");
|
|
6
8
|
/**
|
|
7
9
|
* opencode: custom providers in ~/.config/opencode/opencode.json "provider" map,
|
|
8
10
|
* backed by @ai-sdk/openai-compatible or @ai-sdk/anthropic npm loaders.
|
|
@@ -47,11 +49,11 @@ export const opencode = {
|
|
|
47
49
|
},
|
|
48
50
|
}
|
|
49
51
|
: {}),
|
|
50
|
-
...(m.contextWindow
|
|
52
|
+
...(m.contextWindow && m.maxOutput
|
|
51
53
|
? {
|
|
52
54
|
limit: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
context: m.contextWindow,
|
|
56
|
+
output: m.maxOutput,
|
|
55
57
|
},
|
|
56
58
|
}
|
|
57
59
|
: {}),
|
|
@@ -59,11 +61,11 @@ export const opencode = {
|
|
|
59
61
|
}
|
|
60
62
|
providerMap[provider.id] = {
|
|
61
63
|
...prev,
|
|
62
|
-
npm: anthropic ? "@ai-sdk/anthropic" : "@ai-sdk/openai-compatible",
|
|
64
|
+
npm: anthropic ? "@ai-sdk/anthropic" : provider.openaiApi === "responses" ? "@ai-sdk/openai" : "@ai-sdk/openai-compatible",
|
|
63
65
|
name: provider.name,
|
|
64
66
|
options: {
|
|
65
67
|
...prev.options,
|
|
66
|
-
baseURL: provider.baseUrl,
|
|
68
|
+
baseURL: provider.baseUrl.replace(/\/+$/, ""),
|
|
67
69
|
apiKey: provider.apiKey,
|
|
68
70
|
},
|
|
69
71
|
models,
|
|
@@ -119,6 +121,7 @@ export const opencode = {
|
|
|
119
121
|
if (!entry.npm)
|
|
120
122
|
return []; // built-in provider without a custom npm loader
|
|
121
123
|
const protocol = entry.npm.includes("anthropic") ? "anthropic" : "openai";
|
|
124
|
+
const openaiApi = protocol === "openai" ? (entry.npm === "@ai-sdk/openai" ? "responses" : "completions") : undefined;
|
|
122
125
|
let apiKey = entry.options?.apiKey;
|
|
123
126
|
let keyEnv;
|
|
124
127
|
const ref = apiKey?.match(/^\{env:([A-Za-z0-9_]+)\}$/);
|
|
@@ -130,7 +133,7 @@ export const opencode = {
|
|
|
130
133
|
const defaultModel = activeProvider === id ? activeModel : undefined;
|
|
131
134
|
if (defaultModel && !models.includes(defaultModel))
|
|
132
135
|
models.unshift(defaultModel);
|
|
133
|
-
return [{ id, name: entry.name ?? id, protocol, baseUrl, apiKey, keyEnv, models, defaultModel, source: self }];
|
|
136
|
+
return [{ id, name: entry.name ?? id, protocol, openaiApi, baseUrl, apiKey, keyEnv, models, defaultModel, source: self }];
|
|
134
137
|
});
|
|
135
138
|
},
|
|
136
139
|
};
|
package/dist/targets/pistyle.js
CHANGED
|
@@ -2,7 +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 { apiValue, classifyApi, entryApi, mergeModels, stripConflictingOverrides } from "./wire.js";
|
|
5
|
+
import { apiValue, classifyApi, entryApi, mergeModels, sdkBaseUrl, stripConflictingOverrides } from "./wire.js";
|
|
6
6
|
/** Per-model keys this adapter writes; one that stops being emitted is cleared, not inherited. */
|
|
7
7
|
const OWNED_MODEL_KEYS = [
|
|
8
8
|
"id",
|
|
@@ -59,6 +59,7 @@ export function piStyleTarget(opts) {
|
|
|
59
59
|
? existing
|
|
60
60
|
: {});
|
|
61
61
|
const api = apiValue(provider.protocol, provider.openaiApi, entryApi(prev));
|
|
62
|
+
const baseUrl = sdkBaseUrl(provider.protocol, provider.baseUrl);
|
|
62
63
|
const models = mergeModels(prev.models, provider.models.map((m) => ({
|
|
63
64
|
id: m.id,
|
|
64
65
|
...(m.name ? { name: m.name } : {}),
|
|
@@ -80,13 +81,13 @@ export function piStyleTarget(opts) {
|
|
|
80
81
|
}
|
|
81
82
|
: {}),
|
|
82
83
|
})), OWNED_MODEL_KEYS);
|
|
83
|
-
const conflicts = stripConflictingOverrides(models, api,
|
|
84
|
+
const conflicts = stripConflictingOverrides(models, api, baseUrl);
|
|
84
85
|
if (conflicts.length)
|
|
85
86
|
notes.push(`dropped model overrides pointing elsewhere: ${conflicts.join(", ")}`);
|
|
86
87
|
providers[provider.id] = {
|
|
87
88
|
...prev,
|
|
88
89
|
name: provider.name,
|
|
89
|
-
baseUrl
|
|
90
|
+
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,
|
package/dist/targets/wire.js
CHANGED
|
@@ -107,3 +107,15 @@ export function stripConflictingOverrides(models, api, baseUrl) {
|
|
|
107
107
|
}
|
|
108
108
|
return dropped;
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Return the base URL expected by SDK-backed clients. Anthropic SDK methods
|
|
112
|
+
* append `/v1/...`, while OpenAI-compatible clients append only the operation
|
|
113
|
+
* path (`/responses` or `/chat/completions`) and therefore keep `/v1`.
|
|
114
|
+
*/
|
|
115
|
+
export function sdkBaseUrl(protocol, baseUrl) {
|
|
116
|
+
return protocol === "anthropic" ? stripApiVersion(baseUrl) : baseUrl.replace(/\/+$/, "");
|
|
117
|
+
}
|
|
118
|
+
/** Strip a trailing API version segment (e.g. `/v1`, `/v2beta`). */
|
|
119
|
+
export function stripApiVersion(baseUrl) {
|
|
120
|
+
return baseUrl.replace(/\/v\d+(?:beta\d*)?\/?$/i, "");
|
|
121
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentsw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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": {
|