@jeffreyjyz/opencode-command-code 0.2.11 → 0.3.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.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +715 -5
- package/dist/provider/index.d.ts +22 -0
- package/dist/provider/stream.d.ts +21 -0
- package/dist/provider/wire.d.ts +22 -0
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Plugin as PluginV1 } from "@opencode-ai/plugin";
|
|
2
2
|
export declare const CommandCodePlugin: PluginV1;
|
|
3
|
+
export { createCommandCode } from "./provider";
|
|
3
4
|
declare const _default: {
|
|
4
5
|
/** v1 entrypoint (opencode >=1.18.29 object entrypoints). */
|
|
5
6
|
server: PluginV1;
|
package/dist/index.js
CHANGED
|
@@ -3054,6 +3054,654 @@ async function loadModels(key) {
|
|
|
3054
3054
|
return { claude, open };
|
|
3055
3055
|
}
|
|
3056
3056
|
|
|
3057
|
+
// src/provider/stream.ts
|
|
3058
|
+
function sseDecoder(onEvent) {
|
|
3059
|
+
let buffer = "";
|
|
3060
|
+
return (text) => {
|
|
3061
|
+
buffer += text;
|
|
3062
|
+
for (;; ) {
|
|
3063
|
+
const nl = buffer.indexOf(`
|
|
3064
|
+
`);
|
|
3065
|
+
if (nl < 0)
|
|
3066
|
+
return;
|
|
3067
|
+
const line = buffer.slice(0, nl).replace(/\r$/, "");
|
|
3068
|
+
buffer = buffer.slice(nl + 1);
|
|
3069
|
+
const trimmed = line.trim();
|
|
3070
|
+
if (!trimmed || trimmed.startsWith(":") || trimmed.startsWith("event:"))
|
|
3071
|
+
continue;
|
|
3072
|
+
const payload = trimmed.startsWith("data:") ? trimmed.slice(5).trim() : trimmed;
|
|
3073
|
+
if (!payload || payload === "[DONE]")
|
|
3074
|
+
continue;
|
|
3075
|
+
try {
|
|
3076
|
+
const parsed = JSON.parse(payload);
|
|
3077
|
+
if (parsed && typeof parsed === "object")
|
|
3078
|
+
onEvent(parsed);
|
|
3079
|
+
} catch {}
|
|
3080
|
+
}
|
|
3081
|
+
};
|
|
3082
|
+
}
|
|
3083
|
+
var text = (v) => typeof v === "string" ? v : "";
|
|
3084
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
3085
|
+
var rec = (v) => v && typeof v === "object" ? v : {};
|
|
3086
|
+
var TOOL_FINISH = new Set(["tool_use", "tool_calls", "tool-call", "function_call"]);
|
|
3087
|
+
var LENGTH_FINISH = new Set(["length", "max_tokens", "max_output_tokens", "model_context_window_exceeded"]);
|
|
3088
|
+
function finishReason(raw) {
|
|
3089
|
+
const value = (raw ?? "unknown").toLowerCase();
|
|
3090
|
+
if (TOOL_FINISH.has(value))
|
|
3091
|
+
return { unified: "tool-calls", raw };
|
|
3092
|
+
if (LENGTH_FINISH.has(value))
|
|
3093
|
+
return { unified: "length", raw };
|
|
3094
|
+
if (value === "error")
|
|
3095
|
+
return { unified: "error", raw };
|
|
3096
|
+
return { unified: "stop", raw };
|
|
3097
|
+
}
|
|
3098
|
+
function usageFrom(total, noCache, cacheRead, cacheWrite, output, reasoning) {
|
|
3099
|
+
return {
|
|
3100
|
+
inputTokens: { total, noCache, cacheRead, cacheWrite },
|
|
3101
|
+
outputTokens: { total: output, text: Math.max(0, output - reasoning), reasoning }
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
function anthropicUsage(raw) {
|
|
3105
|
+
const total = num(raw.input_tokens);
|
|
3106
|
+
const cacheRead = num(raw.cache_read_input_tokens);
|
|
3107
|
+
const cacheWrite = num(raw.cache_creation_input_tokens);
|
|
3108
|
+
const output = num(raw.output_tokens);
|
|
3109
|
+
return usageFrom(total, Math.max(0, total - cacheRead - cacheWrite), cacheRead, cacheWrite, output, 0);
|
|
3110
|
+
}
|
|
3111
|
+
function openaiUsage(raw) {
|
|
3112
|
+
const total = num(raw.prompt_tokens);
|
|
3113
|
+
const cached = num(rec(raw.prompt_tokens_details).cached_tokens);
|
|
3114
|
+
const output = num(raw.completion_tokens);
|
|
3115
|
+
const reasoning = num(rec(raw.completion_tokens_details).reasoning_tokens);
|
|
3116
|
+
return usageFrom(total, Math.max(0, total - cached), cached, 0, output, reasoning);
|
|
3117
|
+
}
|
|
3118
|
+
function anthropicReducer() {
|
|
3119
|
+
const blocks = new Map;
|
|
3120
|
+
let finished = false;
|
|
3121
|
+
let warningsEmitted = false;
|
|
3122
|
+
let last = { unified: "stop", raw: "unknown" };
|
|
3123
|
+
const open = (index, kind) => {
|
|
3124
|
+
const id = `${kind === "text" ? "text" : "reasoning"}-${index}`;
|
|
3125
|
+
blocks.set(index, { type: kind, id });
|
|
3126
|
+
return [{ type: kind === "text" ? "text-start" : "reasoning-start", id }];
|
|
3127
|
+
};
|
|
3128
|
+
return {
|
|
3129
|
+
feed(event) {
|
|
3130
|
+
const type = text(event.type);
|
|
3131
|
+
if (type === "message_start") {
|
|
3132
|
+
if (warningsEmitted)
|
|
3133
|
+
return [];
|
|
3134
|
+
warningsEmitted = true;
|
|
3135
|
+
return [{ type: "stream-start", warnings: [] }];
|
|
3136
|
+
}
|
|
3137
|
+
if (type === "content_block_start") {
|
|
3138
|
+
const index = num(event.index);
|
|
3139
|
+
const block = rec(event.content_block);
|
|
3140
|
+
const blockType = text(block.type);
|
|
3141
|
+
if (blockType === "text")
|
|
3142
|
+
return open(index, "text");
|
|
3143
|
+
if (blockType === "thinking")
|
|
3144
|
+
return open(index, "thinking");
|
|
3145
|
+
if (blockType === "tool_use") {
|
|
3146
|
+
const id = text(block.id) || `tool-${index}`;
|
|
3147
|
+
blocks.set(index, { type: "tool_use", id });
|
|
3148
|
+
return [{ type: "tool-input-start", id, toolName: text(block.name) }];
|
|
3149
|
+
}
|
|
3150
|
+
blocks.set(index, { type: "other", id: `block-${index}` });
|
|
3151
|
+
return [];
|
|
3152
|
+
}
|
|
3153
|
+
if (type === "content_block_delta") {
|
|
3154
|
+
const index = num(event.index);
|
|
3155
|
+
const delta = rec(event.delta);
|
|
3156
|
+
const block = blocks.get(index);
|
|
3157
|
+
const textDelta = text(delta.text);
|
|
3158
|
+
if (textDelta) {
|
|
3159
|
+
const parts = block?.type === "text" ? [] : open(index, "text");
|
|
3160
|
+
const id = blocks.get(index)?.id ?? `text-${index}`;
|
|
3161
|
+
return [...parts, { type: "text-delta", id, delta: textDelta }];
|
|
3162
|
+
}
|
|
3163
|
+
const thinking = text(delta.thinking);
|
|
3164
|
+
if (thinking) {
|
|
3165
|
+
const parts = block?.type === "thinking" ? [] : open(index, "thinking");
|
|
3166
|
+
const id = blocks.get(index)?.id ?? `reasoning-${index}`;
|
|
3167
|
+
return [...parts, { type: "reasoning-delta", id, delta: thinking }];
|
|
3168
|
+
}
|
|
3169
|
+
const partial = text(delta.partial_json);
|
|
3170
|
+
if (partial && block?.type === "tool_use") {
|
|
3171
|
+
return [{ type: "tool-input-delta", id: block.id, delta: partial }];
|
|
3172
|
+
}
|
|
3173
|
+
return [];
|
|
3174
|
+
}
|
|
3175
|
+
if (type === "content_block_stop") {
|
|
3176
|
+
const index = num(event.index);
|
|
3177
|
+
const block = blocks.get(index);
|
|
3178
|
+
if (!block)
|
|
3179
|
+
return [];
|
|
3180
|
+
blocks.delete(index);
|
|
3181
|
+
if (block.type === "text")
|
|
3182
|
+
return [{ type: "text-end", id: block.id }];
|
|
3183
|
+
if (block.type === "thinking")
|
|
3184
|
+
return [{ type: "reasoning-end", id: block.id }];
|
|
3185
|
+
if (block.type === "tool_use")
|
|
3186
|
+
return [{ type: "tool-input-end", id: block.id }];
|
|
3187
|
+
return [];
|
|
3188
|
+
}
|
|
3189
|
+
if (type === "message_delta") {
|
|
3190
|
+
const delta = rec(event.delta);
|
|
3191
|
+
last = finishReason(text(delta.stop_reason) || undefined);
|
|
3192
|
+
if (text(delta.stop_reason) === "pause_turn")
|
|
3193
|
+
last = { unified: "stop", raw: "pause_turn" };
|
|
3194
|
+
if (finished)
|
|
3195
|
+
return [];
|
|
3196
|
+
finished = true;
|
|
3197
|
+
return [{ type: "finish", finishReason: last, usage: anthropicUsage(rec(event.usage)) }];
|
|
3198
|
+
}
|
|
3199
|
+
if (type === "message_stop" && !finished) {
|
|
3200
|
+
finished = true;
|
|
3201
|
+
return [{ type: "finish", finishReason: last, usage: usageFrom(0, 0, 0, 0, 0, 0) }];
|
|
3202
|
+
}
|
|
3203
|
+
return [];
|
|
3204
|
+
},
|
|
3205
|
+
close() {
|
|
3206
|
+
const parts = [];
|
|
3207
|
+
for (const block of blocks.values()) {
|
|
3208
|
+
if (block.type === "text")
|
|
3209
|
+
parts.push({ type: "text-end", id: block.id });
|
|
3210
|
+
if (block.type === "thinking")
|
|
3211
|
+
parts.push({ type: "reasoning-end", id: block.id });
|
|
3212
|
+
if (block.type === "tool_use")
|
|
3213
|
+
parts.push({ type: "tool-input-end", id: block.id });
|
|
3214
|
+
}
|
|
3215
|
+
blocks.clear();
|
|
3216
|
+
return parts;
|
|
3217
|
+
}
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
function openaiReducer() {
|
|
3221
|
+
const tools = new Map;
|
|
3222
|
+
let textId;
|
|
3223
|
+
let reasoningId;
|
|
3224
|
+
let pending;
|
|
3225
|
+
let usage;
|
|
3226
|
+
let finished = false;
|
|
3227
|
+
const closeText = () => {
|
|
3228
|
+
if (!textId)
|
|
3229
|
+
return [];
|
|
3230
|
+
const id = textId;
|
|
3231
|
+
textId = undefined;
|
|
3232
|
+
return [{ type: "text-end", id }];
|
|
3233
|
+
};
|
|
3234
|
+
const closeReasoning = () => {
|
|
3235
|
+
if (!reasoningId)
|
|
3236
|
+
return [];
|
|
3237
|
+
const id = reasoningId;
|
|
3238
|
+
reasoningId = undefined;
|
|
3239
|
+
return [{ type: "reasoning-end", id }];
|
|
3240
|
+
};
|
|
3241
|
+
const completeIfParsed = (buffer) => {
|
|
3242
|
+
if (!buffer.opened || buffer.closed)
|
|
3243
|
+
return [];
|
|
3244
|
+
try {
|
|
3245
|
+
JSON.parse(buffer.input);
|
|
3246
|
+
} catch {
|
|
3247
|
+
return [];
|
|
3248
|
+
}
|
|
3249
|
+
buffer.closed = true;
|
|
3250
|
+
return [
|
|
3251
|
+
{ type: "tool-input-end", id: buffer.id },
|
|
3252
|
+
{ type: "tool-call", toolCallId: buffer.id, toolName: buffer.name, input: buffer.input }
|
|
3253
|
+
];
|
|
3254
|
+
};
|
|
3255
|
+
const flushTools = () => {
|
|
3256
|
+
const parts = [];
|
|
3257
|
+
for (const [index, buffer] of tools) {
|
|
3258
|
+
if (buffer.opened && !buffer.closed) {
|
|
3259
|
+
parts.push({ type: "tool-input-end", id: buffer.id });
|
|
3260
|
+
parts.push({ type: "tool-call", toolCallId: buffer.id, toolName: buffer.name, input: buffer.input });
|
|
3261
|
+
buffer.closed = true;
|
|
3262
|
+
}
|
|
3263
|
+
tools.delete(index);
|
|
3264
|
+
}
|
|
3265
|
+
return parts;
|
|
3266
|
+
};
|
|
3267
|
+
return {
|
|
3268
|
+
feed(event) {
|
|
3269
|
+
if (text(event.type) === "error" || event.error !== undefined) {
|
|
3270
|
+
const message = text(rec(event.error).message) || text(event.message) || "provider stream error";
|
|
3271
|
+
return [{ type: "error", error: new Error(message) }];
|
|
3272
|
+
}
|
|
3273
|
+
const parts = [];
|
|
3274
|
+
const choices = Array.isArray(event.choices) ? event.choices : [];
|
|
3275
|
+
const choice = rec(choices[0]);
|
|
3276
|
+
const delta = rec(choice.delta);
|
|
3277
|
+
const reasoning = text(delta.reasoning) || text(delta.reasoning_content);
|
|
3278
|
+
if (reasoning) {
|
|
3279
|
+
if (!reasoningId) {
|
|
3280
|
+
parts.push(...closeText());
|
|
3281
|
+
reasoningId = "reasoning-0";
|
|
3282
|
+
parts.push({ type: "reasoning-start", id: reasoningId });
|
|
3283
|
+
}
|
|
3284
|
+
parts.push({ type: "reasoning-delta", id: reasoningId, delta: reasoning });
|
|
3285
|
+
}
|
|
3286
|
+
const content = text(delta.content);
|
|
3287
|
+
if (content) {
|
|
3288
|
+
if (reasoningId)
|
|
3289
|
+
parts.push(...closeReasoning());
|
|
3290
|
+
if (!textId) {
|
|
3291
|
+
textId = "text-0";
|
|
3292
|
+
parts.push({ type: "text-start", id: textId });
|
|
3293
|
+
}
|
|
3294
|
+
parts.push({ type: "text-delta", id: textId, delta: content });
|
|
3295
|
+
}
|
|
3296
|
+
const calls = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
|
|
3297
|
+
if (calls.length > 0) {
|
|
3298
|
+
parts.push(...closeReasoning(), ...closeText());
|
|
3299
|
+
for (const raw of calls) {
|
|
3300
|
+
const call = rec(raw);
|
|
3301
|
+
const fn = rec(call.function);
|
|
3302
|
+
const index = typeof call.index === "number" ? call.index : tools.size;
|
|
3303
|
+
let buffer = tools.get(index);
|
|
3304
|
+
if (!buffer) {
|
|
3305
|
+
buffer = { id: text(call.id) || `tool-${index}`, name: text(fn.name), input: "", opened: false, closed: false };
|
|
3306
|
+
tools.set(index, buffer);
|
|
3307
|
+
}
|
|
3308
|
+
if (text(call.id))
|
|
3309
|
+
buffer.id = text(call.id);
|
|
3310
|
+
if (text(fn.name))
|
|
3311
|
+
buffer.name = text(fn.name);
|
|
3312
|
+
const args = text(fn.arguments);
|
|
3313
|
+
if (!buffer.opened && buffer.name) {
|
|
3314
|
+
buffer.opened = true;
|
|
3315
|
+
parts.push({ type: "tool-input-start", id: buffer.id, toolName: buffer.name });
|
|
3316
|
+
if (buffer.input)
|
|
3317
|
+
parts.push({ type: "tool-input-delta", id: buffer.id, delta: buffer.input });
|
|
3318
|
+
} else if (args && !buffer.closed) {
|
|
3319
|
+
parts.push({ type: "tool-input-delta", id: buffer.id, delta: args });
|
|
3320
|
+
}
|
|
3321
|
+
if (!buffer.closed) {
|
|
3322
|
+
buffer.input += args;
|
|
3323
|
+
parts.push(...completeIfParsed(buffer));
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
const rawFinish = text(choice.finish_reason) || text(choice.finishReason) || text(event.finish_reason) || text(event.finishReason);
|
|
3328
|
+
if (rawFinish)
|
|
3329
|
+
pending = finishReason(rawFinish);
|
|
3330
|
+
if (event.usage !== undefined && event.usage !== null)
|
|
3331
|
+
usage = openaiUsage(rec(event.usage));
|
|
3332
|
+
if (pending && !finished && (usage !== undefined || choices.length === 0)) {
|
|
3333
|
+
finished = true;
|
|
3334
|
+
parts.push(...closeReasoning(), ...closeText(), ...flushTools());
|
|
3335
|
+
parts.push({ type: "finish", finishReason: pending, usage: usage ?? usageFrom(0, 0, 0, 0, 0, 0) });
|
|
3336
|
+
}
|
|
3337
|
+
return parts;
|
|
3338
|
+
},
|
|
3339
|
+
close() {
|
|
3340
|
+
const parts = [...closeReasoning(), ...closeText(), ...flushTools()];
|
|
3341
|
+
if (pending && !finished) {
|
|
3342
|
+
finished = true;
|
|
3343
|
+
parts.push({ type: "finish", finishReason: pending, usage: usage ?? usageFrom(0, 0, 0, 0, 0, 0) });
|
|
3344
|
+
}
|
|
3345
|
+
return parts;
|
|
3346
|
+
}
|
|
3347
|
+
};
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
// src/provider/wire.ts
|
|
3351
|
+
var DEFAULT_MAX_TOKENS = 64000;
|
|
3352
|
+
function capTokens(value) {
|
|
3353
|
+
const n = typeof value === "number" && Number.isFinite(value) ? value : DEFAULT_MAX_TOKENS;
|
|
3354
|
+
return Math.min(n, DEFAULT_MAX_TOKENS);
|
|
3355
|
+
}
|
|
3356
|
+
function textOf(part) {
|
|
3357
|
+
const p = part;
|
|
3358
|
+
return typeof p.text === "string" ? p.text : "";
|
|
3359
|
+
}
|
|
3360
|
+
function resultText(part) {
|
|
3361
|
+
const output = part.output ?? part.result;
|
|
3362
|
+
if (typeof output === "string")
|
|
3363
|
+
return output;
|
|
3364
|
+
if (output && typeof output === "object") {
|
|
3365
|
+
const o = output;
|
|
3366
|
+
if (typeof o.value === "string")
|
|
3367
|
+
return o.value;
|
|
3368
|
+
if (o.value !== undefined)
|
|
3369
|
+
return JSON.stringify(o.value);
|
|
3370
|
+
if (typeof o.reason === "string")
|
|
3371
|
+
return o.reason;
|
|
3372
|
+
}
|
|
3373
|
+
return JSON.stringify(output ?? "");
|
|
3374
|
+
}
|
|
3375
|
+
function imageUrl(part) {
|
|
3376
|
+
const p = part;
|
|
3377
|
+
const mime = p.mediaType ?? p.mimeType ?? "application/octet-stream";
|
|
3378
|
+
const data = p.data;
|
|
3379
|
+
if (typeof data === "string")
|
|
3380
|
+
return data.startsWith("data:") ? data : `data:${mime};base64,${data}`;
|
|
3381
|
+
if (data instanceof Uint8Array)
|
|
3382
|
+
return `data:${mime};base64,${Buffer.from(data).toString("base64")}`;
|
|
3383
|
+
if (data instanceof URL)
|
|
3384
|
+
return data.href;
|
|
3385
|
+
return;
|
|
3386
|
+
}
|
|
3387
|
+
function pairedToolCallIds(prompt) {
|
|
3388
|
+
const calls = new Set;
|
|
3389
|
+
const results = new Set;
|
|
3390
|
+
for (const message of prompt) {
|
|
3391
|
+
for (const part of message.content) {
|
|
3392
|
+
if (part.type === "tool-call" && typeof part.toolCallId === "string")
|
|
3393
|
+
calls.add(part.toolCallId);
|
|
3394
|
+
if (part.type === "tool-result" && typeof part.toolCallId === "string")
|
|
3395
|
+
results.add(part.toolCallId);
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
return new Set([...calls].filter((id) => results.has(id)));
|
|
3399
|
+
}
|
|
3400
|
+
function systemText(prompt) {
|
|
3401
|
+
return prompt.filter((m) => m.role === "system").map((m) => m.content).join(`
|
|
3402
|
+
`);
|
|
3403
|
+
}
|
|
3404
|
+
function anthropicUserContent(content, images) {
|
|
3405
|
+
if (typeof content === "string")
|
|
3406
|
+
return content;
|
|
3407
|
+
const parts = content ?? [];
|
|
3408
|
+
const blocks = [];
|
|
3409
|
+
for (const part of parts) {
|
|
3410
|
+
if (part.type === "text")
|
|
3411
|
+
blocks.push({ type: "text", text: textOf(part) });
|
|
3412
|
+
else if (part.type === "file" && images) {
|
|
3413
|
+
const url = imageUrl(part);
|
|
3414
|
+
if (url?.startsWith("data:")) {
|
|
3415
|
+
const [head, b64] = url.slice(5).split(";base64,");
|
|
3416
|
+
blocks.push({ type: "image", source: { type: "base64", media_type: head, data: b64 } });
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
if (blocks.length === 1 && blocks[0].type === "text") {
|
|
3421
|
+
return blocks[0].text;
|
|
3422
|
+
}
|
|
3423
|
+
return blocks;
|
|
3424
|
+
}
|
|
3425
|
+
function openaiUserContent(content, images) {
|
|
3426
|
+
if (typeof content === "string")
|
|
3427
|
+
return content;
|
|
3428
|
+
const parts = content ?? [];
|
|
3429
|
+
const blocks = [];
|
|
3430
|
+
for (const part of parts) {
|
|
3431
|
+
if (part.type === "text")
|
|
3432
|
+
blocks.push({ type: "text", text: textOf(part) });
|
|
3433
|
+
else if (part.type === "file" && images) {
|
|
3434
|
+
const url = imageUrl(part);
|
|
3435
|
+
if (url)
|
|
3436
|
+
blocks.push({ type: "image_url", image_url: { url } });
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
if (blocks.every((b) => b.type === "text")) {
|
|
3440
|
+
return blocks.map((b) => b.text).join(`
|
|
3441
|
+
`);
|
|
3442
|
+
}
|
|
3443
|
+
return blocks;
|
|
3444
|
+
}
|
|
3445
|
+
function anthropicBody(model, prompt, options) {
|
|
3446
|
+
const paired = pairedToolCallIds(prompt);
|
|
3447
|
+
const messages = [];
|
|
3448
|
+
for (const message of prompt) {
|
|
3449
|
+
if (message.role === "system")
|
|
3450
|
+
continue;
|
|
3451
|
+
if (message.role === "user") {
|
|
3452
|
+
messages.push({ role: "user", content: anthropicUserContent(message.content, options.images) });
|
|
3453
|
+
continue;
|
|
3454
|
+
}
|
|
3455
|
+
if (message.role === "assistant") {
|
|
3456
|
+
const content = [];
|
|
3457
|
+
for (const part of message.content) {
|
|
3458
|
+
if (part.type === "text") {
|
|
3459
|
+
const text = textOf(part);
|
|
3460
|
+
if (text)
|
|
3461
|
+
content.push({ type: "text", text });
|
|
3462
|
+
} else if (part.type === "tool-call" && paired.has(String(part.toolCallId))) {
|
|
3463
|
+
content.push({ type: "tool_use", id: part.toolCallId, name: part.toolName, input: part.input ?? {} });
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
if (content.length > 0)
|
|
3467
|
+
messages.push({ role: "assistant", content });
|
|
3468
|
+
continue;
|
|
3469
|
+
}
|
|
3470
|
+
for (const part of message.content) {
|
|
3471
|
+
if (part.type !== "tool-result" || !paired.has(String(part.toolCallId)))
|
|
3472
|
+
continue;
|
|
3473
|
+
messages.push({
|
|
3474
|
+
role: "user",
|
|
3475
|
+
content: [
|
|
3476
|
+
{
|
|
3477
|
+
type: "tool_result",
|
|
3478
|
+
tool_use_id: part.toolCallId,
|
|
3479
|
+
content: resultText(part)
|
|
3480
|
+
}
|
|
3481
|
+
]
|
|
3482
|
+
});
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
const system = systemText(prompt);
|
|
3486
|
+
const body = { model, stream: true, messages, max_tokens: capTokens(options.maxOutputTokens) };
|
|
3487
|
+
if (system)
|
|
3488
|
+
body.system = [{ type: "text", text: system, cache_control: { type: "ephemeral" } }];
|
|
3489
|
+
if (options.tools?.length) {
|
|
3490
|
+
body.tools = options.tools.map((t) => ({ name: t.name, description: t.description, input_schema: t.inputSchema ?? {} }));
|
|
3491
|
+
}
|
|
3492
|
+
if (options.temperature !== undefined)
|
|
3493
|
+
body.temperature = options.temperature;
|
|
3494
|
+
if (options.reasoningEffort)
|
|
3495
|
+
body.reasoning_effort = options.reasoningEffort;
|
|
3496
|
+
return body;
|
|
3497
|
+
}
|
|
3498
|
+
function openaiBody(model, prompt, options) {
|
|
3499
|
+
const paired = pairedToolCallIds(prompt);
|
|
3500
|
+
const messages = [];
|
|
3501
|
+
const system = systemText(prompt);
|
|
3502
|
+
if (system)
|
|
3503
|
+
messages.push({ role: "system", content: system });
|
|
3504
|
+
for (const message of prompt) {
|
|
3505
|
+
if (message.role === "system")
|
|
3506
|
+
continue;
|
|
3507
|
+
if (message.role === "user") {
|
|
3508
|
+
messages.push({ role: "user", content: openaiUserContent(message.content, options.images) });
|
|
3509
|
+
continue;
|
|
3510
|
+
}
|
|
3511
|
+
if (message.role === "assistant") {
|
|
3512
|
+
const calls = message.content.filter((p) => p.type === "tool-call" && paired.has(String(p.toolCallId)));
|
|
3513
|
+
const texts = message.content.filter((p) => p.type === "text").map(textOf).filter(Boolean);
|
|
3514
|
+
const reasoning = message.content.filter((p) => p.type === "reasoning").map(textOf).join("");
|
|
3515
|
+
if (calls.length > 0) {
|
|
3516
|
+
messages.push({
|
|
3517
|
+
role: "assistant",
|
|
3518
|
+
...reasoning ? { reasoning_content: reasoning } : {},
|
|
3519
|
+
content: texts.length ? texts.join(`
|
|
3520
|
+
`) : null,
|
|
3521
|
+
tool_calls: calls.map((c) => ({
|
|
3522
|
+
id: c.toolCallId,
|
|
3523
|
+
type: "function",
|
|
3524
|
+
function: { name: c.toolName, arguments: JSON.stringify(c.input ?? {}) }
|
|
3525
|
+
}))
|
|
3526
|
+
});
|
|
3527
|
+
} else if (texts.length > 0) {
|
|
3528
|
+
messages.push({ role: "assistant", ...reasoning ? { reasoning_content: reasoning } : {}, content: texts.join(`
|
|
3529
|
+
`) });
|
|
3530
|
+
}
|
|
3531
|
+
continue;
|
|
3532
|
+
}
|
|
3533
|
+
for (const part of message.content) {
|
|
3534
|
+
if (part.type !== "tool-result" || !paired.has(String(part.toolCallId)))
|
|
3535
|
+
continue;
|
|
3536
|
+
messages.push({
|
|
3537
|
+
role: "tool",
|
|
3538
|
+
tool_call_id: part.toolCallId,
|
|
3539
|
+
content: resultText(part)
|
|
3540
|
+
});
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
const body = { model, stream: true, messages, max_tokens: capTokens(options.maxOutputTokens) };
|
|
3544
|
+
if (options.tools?.length) {
|
|
3545
|
+
body.tools = options.tools.map((t) => ({
|
|
3546
|
+
type: "function",
|
|
3547
|
+
function: { name: t.name, description: t.description, parameters: t.inputSchema ?? {} }
|
|
3548
|
+
}));
|
|
3549
|
+
}
|
|
3550
|
+
if (options.temperature !== undefined)
|
|
3551
|
+
body.temperature = options.temperature;
|
|
3552
|
+
if (options.reasoningEffort)
|
|
3553
|
+
body.reasoning_effort = options.reasoningEffort;
|
|
3554
|
+
body.stream_options = { include_usage: true };
|
|
3555
|
+
return body;
|
|
3556
|
+
}
|
|
3557
|
+
function requestBody(lane, model, prompt, options) {
|
|
3558
|
+
return lane === "anthropic" ? anthropicBody(model, prompt, options) : openaiBody(model, prompt, options);
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
// src/provider/index.ts
|
|
3562
|
+
var DEFAULT_BASE_URL = "https://api.commandcode.ai";
|
|
3563
|
+
function laneOf(modelId) {
|
|
3564
|
+
const bare = modelId.includes("/") ? modelId.slice(modelId.lastIndexOf("/") + 1) : modelId;
|
|
3565
|
+
return bare.toLowerCase().startsWith("claude") ? "anthropic" : "openai";
|
|
3566
|
+
}
|
|
3567
|
+
function endpoint(lane) {
|
|
3568
|
+
return lane === "anthropic" ? "/provider/v1/messages" : "/provider/v1/chat/completions";
|
|
3569
|
+
}
|
|
3570
|
+
function effortFrom(options) {
|
|
3571
|
+
const providerOptions = options.providerOptions ?? {};
|
|
3572
|
+
const top = providerOptions.reasoning ?? providerOptions.reasoningEffort;
|
|
3573
|
+
if (typeof top === "string")
|
|
3574
|
+
return top;
|
|
3575
|
+
const namespaced = providerOptions.commandcode;
|
|
3576
|
+
if (namespaced && typeof namespaced === "object") {
|
|
3577
|
+
const value = namespaced.reasoning ?? namespaced.reasoningEffort;
|
|
3578
|
+
if (typeof value === "string")
|
|
3579
|
+
return value;
|
|
3580
|
+
}
|
|
3581
|
+
return;
|
|
3582
|
+
}
|
|
3583
|
+
function toolsOf(options) {
|
|
3584
|
+
const tools = options.tools ?? [];
|
|
3585
|
+
if (tools.length === 0)
|
|
3586
|
+
return;
|
|
3587
|
+
return tools.map((tool) => ({
|
|
3588
|
+
name: tool.name,
|
|
3589
|
+
description: "description" in tool ? tool.description : undefined,
|
|
3590
|
+
inputSchema: tool.inputSchema ?? {}
|
|
3591
|
+
}));
|
|
3592
|
+
}
|
|
3593
|
+
function createCommandCode(options = {}, images = () => false) {
|
|
3594
|
+
const baseURL = (options.baseURL ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
3595
|
+
const doFetch = options.fetch ?? fetch;
|
|
3596
|
+
function model(modelId) {
|
|
3597
|
+
const lane = laneOf(modelId);
|
|
3598
|
+
return {
|
|
3599
|
+
specificationVersion: "v3",
|
|
3600
|
+
provider: "command-code",
|
|
3601
|
+
modelId,
|
|
3602
|
+
supportedUrls: {},
|
|
3603
|
+
async doStream(callOptions) {
|
|
3604
|
+
const body = requestBody(lane, modelId, callOptions.prompt, {
|
|
3605
|
+
maxOutputTokens: callOptions.maxOutputTokens,
|
|
3606
|
+
temperature: callOptions.temperature,
|
|
3607
|
+
tools: toolsOf(callOptions),
|
|
3608
|
+
reasoningEffort: effortFrom(callOptions) ?? options.reasoningEffort,
|
|
3609
|
+
images: images(modelId)
|
|
3610
|
+
});
|
|
3611
|
+
const response = await doFetch(`${baseURL}${endpoint(lane)}`, {
|
|
3612
|
+
method: "POST",
|
|
3613
|
+
headers: {
|
|
3614
|
+
"Content-Type": "application/json",
|
|
3615
|
+
Authorization: `Bearer ${options.apiKey ?? ""}`,
|
|
3616
|
+
...options.headers ?? {},
|
|
3617
|
+
...callOptions.headers ?? {}
|
|
3618
|
+
},
|
|
3619
|
+
body: JSON.stringify(body),
|
|
3620
|
+
signal: callOptions.abortSignal
|
|
3621
|
+
});
|
|
3622
|
+
if (!response.ok || !response.body) {
|
|
3623
|
+
const detail = await response.text().catch(() => "");
|
|
3624
|
+
throw new Error(`command-code ${lane} ${response.status}: ${detail.slice(0, 300)}`);
|
|
3625
|
+
}
|
|
3626
|
+
const reducer = lane === "anthropic" ? anthropicReducer() : openaiReducer();
|
|
3627
|
+
const stream = new ReadableStream({
|
|
3628
|
+
async start(controller) {
|
|
3629
|
+
const reader = response.body.getReader();
|
|
3630
|
+
const decoder = new TextDecoder;
|
|
3631
|
+
const emit = (parts) => {
|
|
3632
|
+
for (const part of parts)
|
|
3633
|
+
controller.enqueue(part);
|
|
3634
|
+
};
|
|
3635
|
+
const decode = sseDecoder((event) => emit(reducer.feed(event)));
|
|
3636
|
+
try {
|
|
3637
|
+
for (;; ) {
|
|
3638
|
+
const { done, value } = await reader.read();
|
|
3639
|
+
if (done)
|
|
3640
|
+
break;
|
|
3641
|
+
decode(decoder.decode(value, { stream: true }));
|
|
3642
|
+
}
|
|
3643
|
+
emit(reducer.close());
|
|
3644
|
+
controller.close();
|
|
3645
|
+
} catch (error) {
|
|
3646
|
+
controller.enqueue({ type: "error", error });
|
|
3647
|
+
controller.close();
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
});
|
|
3651
|
+
return { stream, response: { headers: Object.fromEntries(response.headers) } };
|
|
3652
|
+
},
|
|
3653
|
+
async doGenerate(callOptions) {
|
|
3654
|
+
const { stream } = await this.doStream(callOptions);
|
|
3655
|
+
const reader = stream.getReader();
|
|
3656
|
+
const content = [];
|
|
3657
|
+
let finish = { unified: "stop", raw: "unknown" };
|
|
3658
|
+
let usage = {
|
|
3659
|
+
inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
|
|
3660
|
+
outputTokens: { total: 0, text: 0, reasoning: 0 }
|
|
3661
|
+
};
|
|
3662
|
+
const textOf = {};
|
|
3663
|
+
const toolsById = new Map;
|
|
3664
|
+
for (;; ) {
|
|
3665
|
+
const { done, value } = await reader.read();
|
|
3666
|
+
if (done)
|
|
3667
|
+
break;
|
|
3668
|
+
const part = value;
|
|
3669
|
+
if (part.type === "text-delta")
|
|
3670
|
+
textOf[String(part.id)] = (textOf[String(part.id)] ?? "") + String(part.delta);
|
|
3671
|
+
else if (part.type === "reasoning-delta") {
|
|
3672
|
+
content.push({ type: "reasoning", text: String(part.delta) });
|
|
3673
|
+
} else if (part.type === "tool-input-start") {
|
|
3674
|
+
toolsById.set(String(part.id), { name: String(part.toolName), input: "" });
|
|
3675
|
+
} else if (part.type === "tool-input-delta") {
|
|
3676
|
+
const buffer = toolsById.get(String(part.id));
|
|
3677
|
+
if (buffer)
|
|
3678
|
+
buffer.input += String(part.delta);
|
|
3679
|
+
} else if (part.type === "tool-call") {
|
|
3680
|
+
content.push({
|
|
3681
|
+
type: "tool-call",
|
|
3682
|
+
toolCallId: part.toolCallId,
|
|
3683
|
+
toolName: part.toolName,
|
|
3684
|
+
input: part.input
|
|
3685
|
+
});
|
|
3686
|
+
} else if (part.type === "finish") {
|
|
3687
|
+
finish = part;
|
|
3688
|
+
usage = part.usage;
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
for (const [id, value] of Object.entries(textOf))
|
|
3692
|
+
content.push({ type: "text", text: value, id });
|
|
3693
|
+
return {
|
|
3694
|
+
content,
|
|
3695
|
+
finishReason: finish.finishReason,
|
|
3696
|
+
usage,
|
|
3697
|
+
warnings: []
|
|
3698
|
+
};
|
|
3699
|
+
}
|
|
3700
|
+
};
|
|
3701
|
+
}
|
|
3702
|
+
return { languageModel: model };
|
|
3703
|
+
}
|
|
3704
|
+
|
|
3057
3705
|
// src/v2.ts
|
|
3058
3706
|
var PLUGIN_ID = "command-code";
|
|
3059
3707
|
var PROVIDER_BASE = "https://api.commandcode.ai/provider/v1";
|
|
@@ -3149,6 +3797,17 @@ var commandCodeV2 = {
|
|
|
3149
3797
|
method: { type: "key", label: "Command Code API key" }
|
|
3150
3798
|
});
|
|
3151
3799
|
});
|
|
3800
|
+
const images = (modelId) => inputModalities(modelId).includes("image");
|
|
3801
|
+
for (const lane of LANES) {
|
|
3802
|
+
await ctx.aisdk.hook("sdk", (event) => {
|
|
3803
|
+
const options = event.options ?? {};
|
|
3804
|
+
event.sdk = createCommandCode({
|
|
3805
|
+
apiKey: typeof options.apiKey === "string" ? options.apiKey : undefined,
|
|
3806
|
+
baseURL: typeof options.baseURL === "string" ? options.baseURL : PROVIDER_BASE,
|
|
3807
|
+
headers: options.headers
|
|
3808
|
+
}, images);
|
|
3809
|
+
}, { providerID: lane.id });
|
|
3810
|
+
}
|
|
3152
3811
|
const seed = staticSeedModels();
|
|
3153
3812
|
await ctx.provider.transform((editor) => {
|
|
3154
3813
|
for (const lane of LANES) {
|
|
@@ -3221,8 +3880,58 @@ var commandCodeV2 = {
|
|
|
3221
3880
|
};
|
|
3222
3881
|
}
|
|
3223
3882
|
};
|
|
3883
|
+
// package.json
|
|
3884
|
+
var package_default = {
|
|
3885
|
+
name: "@jeffreyjyz/opencode-command-code",
|
|
3886
|
+
version: "0.3.0",
|
|
3887
|
+
description: "Command Code provider + usage dashboard for opencode: two providers (Anthropic wire for Claude, OpenAI wire for everything else) against api.commandcode.ai, live model list, plan gating, /cmd-usage. Requires the cmduse CLI for usage rendering.",
|
|
3888
|
+
type: "module",
|
|
3889
|
+
main: "dist/index.js",
|
|
3890
|
+
types: "dist/index.d.ts",
|
|
3891
|
+
exports: {
|
|
3892
|
+
".": "./dist/index.js",
|
|
3893
|
+
"./tui": "./dist/tui.js"
|
|
3894
|
+
},
|
|
3895
|
+
scripts: {
|
|
3896
|
+
build: "rm -rf dist && bun build src/index.ts --outdir dist --target bun --external @opencode-ai/plugin --external @opencode/plugin --external @opencode/plugin/tui && bun scripts/build-tui.ts && tsc -p tsconfig.build.json",
|
|
3897
|
+
test: "bun test",
|
|
3898
|
+
typecheck: "tsc --noEmit",
|
|
3899
|
+
"build:tui": "bun scripts/build-tui.ts",
|
|
3900
|
+
"extract:catalog": "bun scripts/extract-catalog.ts"
|
|
3901
|
+
},
|
|
3902
|
+
dependencies: {
|
|
3903
|
+
"@opentui/core": "^0.5.4",
|
|
3904
|
+
"@opentui/solid": "^0.5.4",
|
|
3905
|
+
"solid-js": "1.9.12",
|
|
3906
|
+
zod: "4.1.8"
|
|
3907
|
+
},
|
|
3908
|
+
devDependencies: {
|
|
3909
|
+
"@types/bun": "latest",
|
|
3910
|
+
typescript: "^5",
|
|
3911
|
+
"@opencode-ai/plugin": "*",
|
|
3912
|
+
"@opencode/plugin": "^2.0.11",
|
|
3913
|
+
"@ai-sdk/provider": "3.0.8"
|
|
3914
|
+
},
|
|
3915
|
+
files: [
|
|
3916
|
+
"dist",
|
|
3917
|
+
"tui.js",
|
|
3918
|
+
"LICENSE"
|
|
3919
|
+
],
|
|
3920
|
+
keywords: [
|
|
3921
|
+
"opencode",
|
|
3922
|
+
"command-code",
|
|
3923
|
+
"plugin",
|
|
3924
|
+
"provider",
|
|
3925
|
+
"ai-sdk"
|
|
3926
|
+
],
|
|
3927
|
+
license: "MIT",
|
|
3928
|
+
publishConfig: {
|
|
3929
|
+
access: "public"
|
|
3930
|
+
}
|
|
3931
|
+
};
|
|
3224
3932
|
|
|
3225
3933
|
// src/index.ts
|
|
3934
|
+
var PROVIDER_NPM = `${package_default.name}@${package_default.version}`;
|
|
3226
3935
|
function tool(input) {
|
|
3227
3936
|
return input;
|
|
3228
3937
|
}
|
|
@@ -3318,18 +4027,18 @@ var CommandCodePlugin = async (_input) => {
|
|
|
3318
4027
|
console.warn("[command-code] model list unavailable:", e);
|
|
3319
4028
|
}
|
|
3320
4029
|
}
|
|
3321
|
-
const claudeDefs = split ? toModelDefs(split.claude, "command-code-anthropic",
|
|
3322
|
-
const openDefs = split ? toModelDefs(split.open, "command-code-openai",
|
|
4030
|
+
const claudeDefs = split ? toModelDefs(split.claude, "command-code-anthropic", PROVIDER_NPM) : {};
|
|
4031
|
+
const openDefs = split ? toModelDefs(split.open, "command-code-openai", PROVIDER_NPM, { field: "reasoning_content" }) : {};
|
|
3323
4032
|
const userAnthropic = existing("command-code-anthropic");
|
|
3324
4033
|
cfg.provider["command-code-anthropic"] = {
|
|
3325
|
-
npm: userAnthropic.npm ??
|
|
4034
|
+
npm: userAnthropic.npm ?? PROVIDER_NPM,
|
|
3326
4035
|
name: userAnthropic.name ?? "Command Code (Anthropic)",
|
|
3327
4036
|
options: { baseURL: PROVIDER_BASE, ...userAnthropic.options },
|
|
3328
4037
|
models: { ...claudeDefs, ...userAnthropic.models }
|
|
3329
4038
|
};
|
|
3330
4039
|
const userOpenai = existing("command-code-openai");
|
|
3331
4040
|
cfg.provider["command-code-openai"] = {
|
|
3332
|
-
npm: userOpenai.npm ??
|
|
4041
|
+
npm: userOpenai.npm ?? PROVIDER_NPM,
|
|
3333
4042
|
name: userOpenai.name ?? "Command Code (OpenAI)",
|
|
3334
4043
|
options: {
|
|
3335
4044
|
baseURL: PROVIDER_BASE,
|
|
@@ -3352,7 +4061,7 @@ var CommandCodePlugin = async (_input) => {
|
|
|
3352
4061
|
return a?.type === "api" && a.key ? { key: a.key } : undefined;
|
|
3353
4062
|
});
|
|
3354
4063
|
const split = await loadModels(key);
|
|
3355
|
-
return toModelDefs(split.claude, "command-code-anthropic",
|
|
4064
|
+
return toModelDefs(split.claude, "command-code-anthropic", PROVIDER_NPM);
|
|
3356
4065
|
}
|
|
3357
4066
|
},
|
|
3358
4067
|
tool: {
|
|
@@ -3375,5 +4084,6 @@ var src_default = {
|
|
|
3375
4084
|
};
|
|
3376
4085
|
export {
|
|
3377
4086
|
CommandCodePlugin,
|
|
4087
|
+
createCommandCode,
|
|
3378
4088
|
src_default as default
|
|
3379
4089
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
2
|
+
import { type WireLane } from "./wire";
|
|
3
|
+
export declare const DEFAULT_BASE_URL = "https://api.commandcode.ai";
|
|
4
|
+
export interface CommandCodeProviderOptions {
|
|
5
|
+
/** Bearer key; the v2 host passes the resolved connection credential here. */
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
baseURL?: string;
|
|
8
|
+
/** Extra headers (host-supplied), merged last. */
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
/** Reasoning effort the host wants (`providerOptions.commandcode.reasoning`). */
|
|
11
|
+
reasoningEffort?: string;
|
|
12
|
+
/** Injected for tests. */
|
|
13
|
+
fetch?: typeof fetch;
|
|
14
|
+
}
|
|
15
|
+
export interface CommandCodeProvider {
|
|
16
|
+
languageModel(modelId: string): LanguageModelV3;
|
|
17
|
+
}
|
|
18
|
+
/** Claude models use the Anthropic wire; everything else chat completions. */
|
|
19
|
+
export declare function laneOf(modelId: string): WireLane;
|
|
20
|
+
/** True when the model advertises image input; the caller passes the table in. */
|
|
21
|
+
export type ImagesFor = (modelId: string) => boolean;
|
|
22
|
+
export declare function createCommandCode(options?: CommandCodeProviderOptions, images?: ImagesFor): CommandCodeProvider;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { LanguageModelV3FinishReason, LanguageModelV3StreamPart, LanguageModelV3Usage } from "@ai-sdk/provider";
|
|
2
|
+
type Part = LanguageModelV3StreamPart;
|
|
3
|
+
/** Split an SSE byte stream into JSON events, tolerating partial chunks. */
|
|
4
|
+
export declare function sseDecoder(onEvent: (event: Record<string, unknown>) => void): (text: string) => void;
|
|
5
|
+
/** Wire stop reason → unified v3 finish reason; unknown reasons completed. */
|
|
6
|
+
export declare function finishReason(raw: string | undefined): LanguageModelV3FinishReason;
|
|
7
|
+
/** Anthropic `message_start`/`message_delta` usage → v3 usage. */
|
|
8
|
+
export declare function anthropicUsage(raw: Record<string, unknown>): LanguageModelV3Usage;
|
|
9
|
+
/** OpenAI usage → v3 usage (`prompt_tokens_details.cached_tokens` is cache read). */
|
|
10
|
+
export declare function openaiUsage(raw: Record<string, unknown>): LanguageModelV3Usage;
|
|
11
|
+
export interface Reducer {
|
|
12
|
+
/** Feed one parsed SSE event; returns the parts it produced. */
|
|
13
|
+
feed(event: Record<string, unknown>): Part[];
|
|
14
|
+
/** Close open parts when the transport ends (abort/error). */
|
|
15
|
+
close(): Part[];
|
|
16
|
+
}
|
|
17
|
+
/** Anthropic Messages reducer. */
|
|
18
|
+
export declare function anthropicReducer(): Reducer;
|
|
19
|
+
/** OpenAI Chat Completions reducer. */
|
|
20
|
+
export declare function openaiReducer(): Reducer;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LanguageModelV3Prompt } from "@ai-sdk/provider";
|
|
2
|
+
export type WireLane = "anthropic" | "openai";
|
|
3
|
+
/** The subset of tools we serialize (V3 FunctionTool already matches). */
|
|
4
|
+
export interface WireTool {
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
inputSchema: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface WireOptions {
|
|
10
|
+
maxOutputTokens?: number;
|
|
11
|
+
temperature?: number;
|
|
12
|
+
tools?: WireTool[];
|
|
13
|
+
/** Wire extension both gates honour (`reasoning_effort`). */
|
|
14
|
+
reasoningEffort?: string;
|
|
15
|
+
/** Whether the model advertises image input (catalog-derived). */
|
|
16
|
+
images: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Anthropic body: POST /provider/v1/messages. */
|
|
19
|
+
export declare function anthropicBody(model: string, prompt: LanguageModelV3Prompt, options: WireOptions): Record<string, unknown>;
|
|
20
|
+
/** OpenAI body: POST /provider/v1/chat/completions. */
|
|
21
|
+
export declare function openaiBody(model: string, prompt: LanguageModelV3Prompt, options: WireOptions): Record<string, unknown>;
|
|
22
|
+
export declare function requestBody(lane: WireLane, model: string, prompt: LanguageModelV3Prompt, options: WireOptions): Record<string, unknown>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffreyjyz/opencode-command-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Command Code provider + usage dashboard for opencode: two providers (Anthropic wire for Claude, OpenAI wire for everything else) against api.commandcode.ai, live model list, plan gating, /cmd-usage. Requires the cmduse CLI for usage rendering.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"@types/bun": "latest",
|
|
27
27
|
"typescript": "^5",
|
|
28
28
|
"@opencode-ai/plugin": "*",
|
|
29
|
-
"@opencode/plugin": "^2.0.11"
|
|
29
|
+
"@opencode/plugin": "^2.0.11",
|
|
30
|
+
"@ai-sdk/provider": "3.0.8"
|
|
30
31
|
},
|
|
31
32
|
"files": [
|
|
32
33
|
"dist",
|