@clwnd/opencode 0.1.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/README.md +5 -0
- package/dist/index.js +649 -0
- package/package.json +47 -0
package/README.md
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
8
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
+
mod
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
// ../../node_modules/.bun/secure-json-parse@2.7.0/node_modules/secure-json-parse/index.js
|
|
28
|
+
var require_secure_json_parse = __commonJS({
|
|
29
|
+
"../../node_modules/.bun/secure-json-parse@2.7.0/node_modules/secure-json-parse/index.js"(exports, module) {
|
|
30
|
+
"use strict";
|
|
31
|
+
var hasBuffer = typeof Buffer !== "undefined";
|
|
32
|
+
var suspectProtoRx = /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/;
|
|
33
|
+
var suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
|
|
34
|
+
function _parse(text, reviver, options) {
|
|
35
|
+
if (options == null) {
|
|
36
|
+
if (reviver !== null && typeof reviver === "object") {
|
|
37
|
+
options = reviver;
|
|
38
|
+
reviver = void 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (hasBuffer && Buffer.isBuffer(text)) {
|
|
42
|
+
text = text.toString();
|
|
43
|
+
}
|
|
44
|
+
if (text && text.charCodeAt(0) === 65279) {
|
|
45
|
+
text = text.slice(1);
|
|
46
|
+
}
|
|
47
|
+
const obj = JSON.parse(text, reviver);
|
|
48
|
+
if (obj === null || typeof obj !== "object") {
|
|
49
|
+
return obj;
|
|
50
|
+
}
|
|
51
|
+
const protoAction = options && options.protoAction || "error";
|
|
52
|
+
const constructorAction = options && options.constructorAction || "error";
|
|
53
|
+
if (protoAction === "ignore" && constructorAction === "ignore") {
|
|
54
|
+
return obj;
|
|
55
|
+
}
|
|
56
|
+
if (protoAction !== "ignore" && constructorAction !== "ignore") {
|
|
57
|
+
if (suspectProtoRx.test(text) === false && suspectConstructorRx.test(text) === false) {
|
|
58
|
+
return obj;
|
|
59
|
+
}
|
|
60
|
+
} else if (protoAction !== "ignore" && constructorAction === "ignore") {
|
|
61
|
+
if (suspectProtoRx.test(text) === false) {
|
|
62
|
+
return obj;
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
if (suspectConstructorRx.test(text) === false) {
|
|
66
|
+
return obj;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return filter(obj, { protoAction, constructorAction, safe: options && options.safe });
|
|
70
|
+
}
|
|
71
|
+
function filter(obj, { protoAction = "error", constructorAction = "error", safe } = {}) {
|
|
72
|
+
let next = [obj];
|
|
73
|
+
while (next.length) {
|
|
74
|
+
const nodes = next;
|
|
75
|
+
next = [];
|
|
76
|
+
for (const node of nodes) {
|
|
77
|
+
if (protoAction !== "ignore" && Object.prototype.hasOwnProperty.call(node, "__proto__")) {
|
|
78
|
+
if (safe === true) {
|
|
79
|
+
return null;
|
|
80
|
+
} else if (protoAction === "error") {
|
|
81
|
+
throw new SyntaxError("Object contains forbidden prototype property");
|
|
82
|
+
}
|
|
83
|
+
delete node.__proto__;
|
|
84
|
+
}
|
|
85
|
+
if (constructorAction !== "ignore" && Object.prototype.hasOwnProperty.call(node, "constructor") && Object.prototype.hasOwnProperty.call(node.constructor, "prototype")) {
|
|
86
|
+
if (safe === true) {
|
|
87
|
+
return null;
|
|
88
|
+
} else if (constructorAction === "error") {
|
|
89
|
+
throw new SyntaxError("Object contains forbidden prototype property");
|
|
90
|
+
}
|
|
91
|
+
delete node.constructor;
|
|
92
|
+
}
|
|
93
|
+
for (const key in node) {
|
|
94
|
+
const value = node[key];
|
|
95
|
+
if (value && typeof value === "object") {
|
|
96
|
+
next.push(value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return obj;
|
|
102
|
+
}
|
|
103
|
+
function parse(text, reviver, options) {
|
|
104
|
+
const stackTraceLimit = Error.stackTraceLimit;
|
|
105
|
+
Error.stackTraceLimit = 0;
|
|
106
|
+
try {
|
|
107
|
+
return _parse(text, reviver, options);
|
|
108
|
+
} finally {
|
|
109
|
+
Error.stackTraceLimit = stackTraceLimit;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function safeParse(text, reviver) {
|
|
113
|
+
const stackTraceLimit = Error.stackTraceLimit;
|
|
114
|
+
Error.stackTraceLimit = 0;
|
|
115
|
+
try {
|
|
116
|
+
return _parse(text, reviver, { safe: true });
|
|
117
|
+
} catch (_e) {
|
|
118
|
+
return null;
|
|
119
|
+
} finally {
|
|
120
|
+
Error.stackTraceLimit = stackTraceLimit;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
module.exports = parse;
|
|
124
|
+
module.exports.default = parse;
|
|
125
|
+
module.exports.parse = parse;
|
|
126
|
+
module.exports.safeParse = safeParse;
|
|
127
|
+
module.exports.scan = filter;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ../../node_modules/.bun/@ai-sdk+provider@1.1.3/node_modules/@ai-sdk/provider/dist/index.mjs
|
|
132
|
+
var marker = "vercel.ai.error";
|
|
133
|
+
var symbol = Symbol.for(marker);
|
|
134
|
+
var _a;
|
|
135
|
+
var _AISDKError = class _AISDKError2 extends Error {
|
|
136
|
+
/**
|
|
137
|
+
* Creates an AI SDK Error.
|
|
138
|
+
*
|
|
139
|
+
* @param {Object} params - The parameters for creating the error.
|
|
140
|
+
* @param {string} params.name - The name of the error.
|
|
141
|
+
* @param {string} params.message - The error message.
|
|
142
|
+
* @param {unknown} [params.cause] - The underlying cause of the error.
|
|
143
|
+
*/
|
|
144
|
+
constructor({
|
|
145
|
+
name: name14,
|
|
146
|
+
message,
|
|
147
|
+
cause
|
|
148
|
+
}) {
|
|
149
|
+
super(message);
|
|
150
|
+
this[_a] = true;
|
|
151
|
+
this.name = name14;
|
|
152
|
+
this.cause = cause;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Checks if the given error is an AI SDK Error.
|
|
156
|
+
* @param {unknown} error - The error to check.
|
|
157
|
+
* @returns {boolean} True if the error is an AI SDK Error, false otherwise.
|
|
158
|
+
*/
|
|
159
|
+
static isInstance(error) {
|
|
160
|
+
return _AISDKError2.hasMarker(error, marker);
|
|
161
|
+
}
|
|
162
|
+
static hasMarker(error, marker15) {
|
|
163
|
+
const markerSymbol = Symbol.for(marker15);
|
|
164
|
+
return error != null && typeof error === "object" && markerSymbol in error && typeof error[markerSymbol] === "boolean" && error[markerSymbol] === true;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
_a = symbol;
|
|
168
|
+
var AISDKError = _AISDKError;
|
|
169
|
+
var name = "AI_APICallError";
|
|
170
|
+
var marker2 = `vercel.ai.error.${name}`;
|
|
171
|
+
var symbol2 = Symbol.for(marker2);
|
|
172
|
+
var _a2;
|
|
173
|
+
_a2 = symbol2;
|
|
174
|
+
var name2 = "AI_EmptyResponseBodyError";
|
|
175
|
+
var marker3 = `vercel.ai.error.${name2}`;
|
|
176
|
+
var symbol3 = Symbol.for(marker3);
|
|
177
|
+
var _a3;
|
|
178
|
+
_a3 = symbol3;
|
|
179
|
+
var name3 = "AI_InvalidArgumentError";
|
|
180
|
+
var marker4 = `vercel.ai.error.${name3}`;
|
|
181
|
+
var symbol4 = Symbol.for(marker4);
|
|
182
|
+
var _a4;
|
|
183
|
+
var InvalidArgumentError = class extends AISDKError {
|
|
184
|
+
constructor({
|
|
185
|
+
message,
|
|
186
|
+
cause,
|
|
187
|
+
argument
|
|
188
|
+
}) {
|
|
189
|
+
super({ name: name3, message, cause });
|
|
190
|
+
this[_a4] = true;
|
|
191
|
+
this.argument = argument;
|
|
192
|
+
}
|
|
193
|
+
static isInstance(error) {
|
|
194
|
+
return AISDKError.hasMarker(error, marker4);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
_a4 = symbol4;
|
|
198
|
+
var name4 = "AI_InvalidPromptError";
|
|
199
|
+
var marker5 = `vercel.ai.error.${name4}`;
|
|
200
|
+
var symbol5 = Symbol.for(marker5);
|
|
201
|
+
var _a5;
|
|
202
|
+
_a5 = symbol5;
|
|
203
|
+
var name5 = "AI_InvalidResponseDataError";
|
|
204
|
+
var marker6 = `vercel.ai.error.${name5}`;
|
|
205
|
+
var symbol6 = Symbol.for(marker6);
|
|
206
|
+
var _a6;
|
|
207
|
+
_a6 = symbol6;
|
|
208
|
+
var name6 = "AI_JSONParseError";
|
|
209
|
+
var marker7 = `vercel.ai.error.${name6}`;
|
|
210
|
+
var symbol7 = Symbol.for(marker7);
|
|
211
|
+
var _a7;
|
|
212
|
+
_a7 = symbol7;
|
|
213
|
+
var name7 = "AI_LoadAPIKeyError";
|
|
214
|
+
var marker8 = `vercel.ai.error.${name7}`;
|
|
215
|
+
var symbol8 = Symbol.for(marker8);
|
|
216
|
+
var _a8;
|
|
217
|
+
_a8 = symbol8;
|
|
218
|
+
var name8 = "AI_LoadSettingError";
|
|
219
|
+
var marker9 = `vercel.ai.error.${name8}`;
|
|
220
|
+
var symbol9 = Symbol.for(marker9);
|
|
221
|
+
var _a9;
|
|
222
|
+
_a9 = symbol9;
|
|
223
|
+
var name9 = "AI_NoContentGeneratedError";
|
|
224
|
+
var marker10 = `vercel.ai.error.${name9}`;
|
|
225
|
+
var symbol10 = Symbol.for(marker10);
|
|
226
|
+
var _a10;
|
|
227
|
+
_a10 = symbol10;
|
|
228
|
+
var name10 = "AI_NoSuchModelError";
|
|
229
|
+
var marker11 = `vercel.ai.error.${name10}`;
|
|
230
|
+
var symbol11 = Symbol.for(marker11);
|
|
231
|
+
var _a11;
|
|
232
|
+
_a11 = symbol11;
|
|
233
|
+
var name11 = "AI_TooManyEmbeddingValuesForCallError";
|
|
234
|
+
var marker12 = `vercel.ai.error.${name11}`;
|
|
235
|
+
var symbol12 = Symbol.for(marker12);
|
|
236
|
+
var _a12;
|
|
237
|
+
_a12 = symbol12;
|
|
238
|
+
var name12 = "AI_TypeValidationError";
|
|
239
|
+
var marker13 = `vercel.ai.error.${name12}`;
|
|
240
|
+
var symbol13 = Symbol.for(marker13);
|
|
241
|
+
var _a13;
|
|
242
|
+
_a13 = symbol13;
|
|
243
|
+
var name13 = "AI_UnsupportedFunctionalityError";
|
|
244
|
+
var marker14 = `vercel.ai.error.${name13}`;
|
|
245
|
+
var symbol14 = Symbol.for(marker14);
|
|
246
|
+
var _a14;
|
|
247
|
+
_a14 = symbol14;
|
|
248
|
+
|
|
249
|
+
// ../../node_modules/.bun/nanoid@3.3.11/node_modules/nanoid/non-secure/index.js
|
|
250
|
+
var customAlphabet = (alphabet, defaultSize = 21) => {
|
|
251
|
+
return (size = defaultSize) => {
|
|
252
|
+
let id = "";
|
|
253
|
+
let i = size | 0;
|
|
254
|
+
while (i--) {
|
|
255
|
+
id += alphabet[Math.random() * alphabet.length | 0];
|
|
256
|
+
}
|
|
257
|
+
return id;
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// ../../node_modules/.bun/@ai-sdk+provider-utils@2.2.8+d6123d32214422cb/node_modules/@ai-sdk/provider-utils/dist/index.mjs
|
|
262
|
+
var import_secure_json_parse = __toESM(require_secure_json_parse(), 1);
|
|
263
|
+
var createIdGenerator = ({
|
|
264
|
+
prefix,
|
|
265
|
+
size: defaultSize = 16,
|
|
266
|
+
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
|
267
|
+
separator = "-"
|
|
268
|
+
} = {}) => {
|
|
269
|
+
const generator = customAlphabet(alphabet, defaultSize);
|
|
270
|
+
if (prefix == null) {
|
|
271
|
+
return generator;
|
|
272
|
+
}
|
|
273
|
+
if (alphabet.includes(separator)) {
|
|
274
|
+
throw new InvalidArgumentError({
|
|
275
|
+
argument: "separator",
|
|
276
|
+
message: `The separator "${separator}" must not be part of the alphabet "${alphabet}".`
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return (size) => `${prefix}${separator}${generator(size)}`;
|
|
280
|
+
};
|
|
281
|
+
var generateId = createIdGenerator();
|
|
282
|
+
var { btoa, atob } = globalThis;
|
|
283
|
+
|
|
284
|
+
// provider.ts
|
|
285
|
+
function defaultSocketPath() {
|
|
286
|
+
const runtime = process.env.XDG_RUNTIME_DIR;
|
|
287
|
+
if (runtime) return `${runtime}/clwnd/clwnd.sock`;
|
|
288
|
+
return "/tmp/clwnd.sock";
|
|
289
|
+
}
|
|
290
|
+
var SOCK_PATH = (process.env.CLWND_SOCKET ?? defaultSocketPath()) + ".http";
|
|
291
|
+
function streamCall(msg) {
|
|
292
|
+
return fetch("http://localhost/stream", {
|
|
293
|
+
method: "POST",
|
|
294
|
+
headers: { "Content-Type": "application/json" },
|
|
295
|
+
body: JSON.stringify(msg),
|
|
296
|
+
unix: SOCK_PATH
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
async function ipcCall(msg) {
|
|
300
|
+
const r = await fetch("http://localhost/", {
|
|
301
|
+
method: "POST",
|
|
302
|
+
headers: { "Content-Type": "application/json" },
|
|
303
|
+
body: JSON.stringify(msg),
|
|
304
|
+
unix: SOCK_PATH
|
|
305
|
+
});
|
|
306
|
+
if (!r.ok) throw new Error(`clwnd IPC ${r.status}`);
|
|
307
|
+
}
|
|
308
|
+
function extractText(prompt) {
|
|
309
|
+
for (let i = prompt.length - 1; i >= 0; i--) {
|
|
310
|
+
const m = prompt[i];
|
|
311
|
+
if (m.role === "user") {
|
|
312
|
+
if (typeof m.content === "string") return m.content;
|
|
313
|
+
if (Array.isArray(m.content)) {
|
|
314
|
+
for (const p of m.content) {
|
|
315
|
+
if (p.type === "text" && p.text) return p.text;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return "";
|
|
321
|
+
}
|
|
322
|
+
function parseNDJSON(buffer, sessionId) {
|
|
323
|
+
const messages = [];
|
|
324
|
+
const lines = buffer.split("\n");
|
|
325
|
+
const remaining = lines.pop() ?? "";
|
|
326
|
+
for (const line of lines) {
|
|
327
|
+
if (!line.trim()) continue;
|
|
328
|
+
try {
|
|
329
|
+
const msg = JSON.parse(line);
|
|
330
|
+
messages.push(msg);
|
|
331
|
+
} catch {
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return { messages, remaining };
|
|
335
|
+
}
|
|
336
|
+
var ClwndModel = class {
|
|
337
|
+
constructor(modelId, config = {}) {
|
|
338
|
+
this.config = config;
|
|
339
|
+
this.modelId = modelId;
|
|
340
|
+
}
|
|
341
|
+
specificationVersion = "v2";
|
|
342
|
+
modelId;
|
|
343
|
+
provider = "clwnd";
|
|
344
|
+
supportedUrls = {};
|
|
345
|
+
async doGenerate(opts) {
|
|
346
|
+
const sid = opts.headers?.["x-opencode-session"] ?? generateId();
|
|
347
|
+
const text = extractText(opts.prompt);
|
|
348
|
+
const warnings = [];
|
|
349
|
+
const cwd = this.config.cwd ?? process.cwd();
|
|
350
|
+
let reasoning = "";
|
|
351
|
+
let responseText = "";
|
|
352
|
+
const toolCalls = [];
|
|
353
|
+
const toolInputAccum = /* @__PURE__ */ new Map();
|
|
354
|
+
let resolved = false;
|
|
355
|
+
const result = await new Promise((resolve, reject) => {
|
|
356
|
+
const abort = () => {
|
|
357
|
+
if (resolved) return;
|
|
358
|
+
resolved = true;
|
|
359
|
+
ipcCall({ action: "destroy", opencodeSessionId: sid }).catch(() => {
|
|
360
|
+
});
|
|
361
|
+
reject(new Error("aborted"));
|
|
362
|
+
};
|
|
363
|
+
opts.abortSignal?.addEventListener("abort", abort);
|
|
364
|
+
streamCall({
|
|
365
|
+
action: "stream",
|
|
366
|
+
opencodeSessionId: sid,
|
|
367
|
+
cwd,
|
|
368
|
+
modelId: this.modelId,
|
|
369
|
+
text
|
|
370
|
+
}).then(async (resp) => {
|
|
371
|
+
if (!resp.body) {
|
|
372
|
+
abort();
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const reader = resp.body.getReader();
|
|
376
|
+
const decoder = new TextDecoder();
|
|
377
|
+
let buffer = "";
|
|
378
|
+
try {
|
|
379
|
+
while (true) {
|
|
380
|
+
const { done, value } = await reader.read();
|
|
381
|
+
if (done) break;
|
|
382
|
+
buffer += decoder.decode(value, { stream: true });
|
|
383
|
+
const { messages, remaining } = parseNDJSON(buffer, sid);
|
|
384
|
+
buffer = remaining;
|
|
385
|
+
for (const msg of messages) {
|
|
386
|
+
if (msg.action === "chunk") {
|
|
387
|
+
const ct = msg.chunkType;
|
|
388
|
+
if (ct === "reasoning_delta" && typeof msg.delta === "string") reasoning += msg.delta;
|
|
389
|
+
if (ct === "text_delta" && typeof msg.delta === "string") responseText += msg.delta;
|
|
390
|
+
if (ct === "tool_input_start" && msg.toolCallId) toolInputAccum.set(msg.toolCallId, "");
|
|
391
|
+
if (ct === "tool_input_delta" && msg.toolCallId && msg.partialJson) {
|
|
392
|
+
const prev = toolInputAccum.get(msg.toolCallId) ?? "";
|
|
393
|
+
toolInputAccum.set(msg.toolCallId, prev + msg.partialJson);
|
|
394
|
+
}
|
|
395
|
+
if (ct === "tool_call" && msg.toolCallId && msg.toolName) {
|
|
396
|
+
const accumulated = toolInputAccum.get(msg.toolCallId) ?? "{}";
|
|
397
|
+
let input = {};
|
|
398
|
+
try {
|
|
399
|
+
input = JSON.parse(accumulated);
|
|
400
|
+
} catch {
|
|
401
|
+
input = {};
|
|
402
|
+
}
|
|
403
|
+
toolCalls.push({
|
|
404
|
+
type: "tool-call",
|
|
405
|
+
toolCallId: msg.toolCallId,
|
|
406
|
+
toolName: msg.toolName,
|
|
407
|
+
input
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (msg.action === "finish") {
|
|
412
|
+
if (resolved) return;
|
|
413
|
+
resolved = true;
|
|
414
|
+
const finishReason = msg.finishReason ?? "stop";
|
|
415
|
+
const usage = msg.usage ?? { inputTokens: void 0, outputTokens: void 0, totalTokens: void 0 };
|
|
416
|
+
const providerMetadata = msg.providerMetadata ?? {};
|
|
417
|
+
const content = [];
|
|
418
|
+
if (reasoning) content.push({ type: "reasoning", text: reasoning });
|
|
419
|
+
if (responseText) content.push({ type: "text", text: responseText });
|
|
420
|
+
content.push(...toolCalls);
|
|
421
|
+
opts.abortSignal?.removeEventListener("abort", abort);
|
|
422
|
+
reader.releaseLock();
|
|
423
|
+
resolve({ content, finishReason, usage, providerMetadata });
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (msg.action === "error") {
|
|
427
|
+
if (resolved) return;
|
|
428
|
+
resolved = true;
|
|
429
|
+
opts.abortSignal?.removeEventListener("abort", abort);
|
|
430
|
+
reader.releaseLock();
|
|
431
|
+
reject(new Error(msg.message));
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
} finally {
|
|
437
|
+
try {
|
|
438
|
+
reader.releaseLock();
|
|
439
|
+
} catch {
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (!resolved) {
|
|
443
|
+
resolved = true;
|
|
444
|
+
reject(new Error("stream ended without finish"));
|
|
445
|
+
}
|
|
446
|
+
}).catch((e) => {
|
|
447
|
+
if (!resolved) {
|
|
448
|
+
resolved = true;
|
|
449
|
+
reject(e);
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
setTimeout(() => {
|
|
453
|
+
if (!resolved) abort();
|
|
454
|
+
}, 12e4);
|
|
455
|
+
});
|
|
456
|
+
return {
|
|
457
|
+
...result,
|
|
458
|
+
warnings,
|
|
459
|
+
request: { body: { text } },
|
|
460
|
+
response: { id: sid, timestamp: /* @__PURE__ */ new Date(), modelId: this.modelId }
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
async doStream(opts) {
|
|
464
|
+
const sid = opts.headers?.["x-opencode-session"] ?? generateId();
|
|
465
|
+
const text = extractText(opts.prompt);
|
|
466
|
+
const warnings = [];
|
|
467
|
+
const cwd = this.config.cwd ?? process.cwd();
|
|
468
|
+
const self = this;
|
|
469
|
+
const toolInputAccum = /* @__PURE__ */ new Map();
|
|
470
|
+
const stream = new ReadableStream({
|
|
471
|
+
async start(controller) {
|
|
472
|
+
const textId = generateId();
|
|
473
|
+
const reasoningId = generateId();
|
|
474
|
+
let done = false;
|
|
475
|
+
let reader = null;
|
|
476
|
+
let textStarted = false;
|
|
477
|
+
let reasoningStarted = false;
|
|
478
|
+
function emit(part) {
|
|
479
|
+
if (done) return;
|
|
480
|
+
try {
|
|
481
|
+
controller.enqueue(part);
|
|
482
|
+
} catch {
|
|
483
|
+
done = true;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function close() {
|
|
487
|
+
if (done) return;
|
|
488
|
+
done = true;
|
|
489
|
+
try {
|
|
490
|
+
reader?.releaseLock();
|
|
491
|
+
} catch {
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
controller.close();
|
|
495
|
+
} catch {
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
opts.abortSignal?.addEventListener("abort", () => {
|
|
499
|
+
ipcCall({ action: "destroy", opencodeSessionId: sid }).catch(() => {
|
|
500
|
+
});
|
|
501
|
+
close();
|
|
502
|
+
});
|
|
503
|
+
let resp;
|
|
504
|
+
try {
|
|
505
|
+
resp = await streamCall({
|
|
506
|
+
action: "stream",
|
|
507
|
+
opencodeSessionId: sid,
|
|
508
|
+
cwd,
|
|
509
|
+
modelId: self.modelId,
|
|
510
|
+
text
|
|
511
|
+
});
|
|
512
|
+
} catch (e) {
|
|
513
|
+
emit({ type: "error", error: new Error(String(e)) });
|
|
514
|
+
close();
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
if (!resp.body) {
|
|
518
|
+
close();
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
reader = resp.body.getReader();
|
|
522
|
+
emit({ type: "stream-start", warnings });
|
|
523
|
+
const decoder = new TextDecoder();
|
|
524
|
+
let buffer = "";
|
|
525
|
+
try {
|
|
526
|
+
while (!done) {
|
|
527
|
+
const { done: rd, value } = await reader.read();
|
|
528
|
+
if (rd) {
|
|
529
|
+
close();
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
buffer += decoder.decode(value, { stream: true });
|
|
533
|
+
const { messages, remaining } = parseNDJSON(buffer, sid);
|
|
534
|
+
buffer = remaining;
|
|
535
|
+
for (const msg of messages) {
|
|
536
|
+
if (msg.action === "chunk") {
|
|
537
|
+
const ct = msg.chunkType;
|
|
538
|
+
if (ct === "text_start" || ct === "text_delta" && !textStarted) {
|
|
539
|
+
if (!textStarted) {
|
|
540
|
+
textStarted = true;
|
|
541
|
+
emit({ type: "text-start", id: textId });
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (ct === "text_delta" && msg.delta) {
|
|
545
|
+
emit({ type: "text-delta", id: textId, delta: msg.delta });
|
|
546
|
+
}
|
|
547
|
+
if (ct === "reasoning_start" || ct === "reasoning_delta" && !reasoningStarted) {
|
|
548
|
+
if (!reasoningStarted) {
|
|
549
|
+
reasoningStarted = true;
|
|
550
|
+
emit({ type: "reasoning-start", id: reasoningId });
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (ct === "reasoning_delta" && msg.delta) {
|
|
554
|
+
emit({ type: "reasoning-delta", id: reasoningId, delta: msg.delta });
|
|
555
|
+
}
|
|
556
|
+
if (ct === "reasoning_end") {
|
|
557
|
+
emit({ type: "reasoning-end", id: reasoningId });
|
|
558
|
+
reasoningStarted = false;
|
|
559
|
+
}
|
|
560
|
+
if (ct === "tool_input_start" && msg.toolCallId && msg.toolName) {
|
|
561
|
+
toolInputAccum.set(msg.toolCallId, "");
|
|
562
|
+
emit({ type: "tool-input-start", id: msg.toolCallId, toolName: msg.toolName });
|
|
563
|
+
}
|
|
564
|
+
if (ct === "tool_input_delta" && msg.toolCallId && msg.partialJson) {
|
|
565
|
+
const prev = toolInputAccum.get(msg.toolCallId) ?? "";
|
|
566
|
+
toolInputAccum.set(msg.toolCallId, prev + msg.partialJson);
|
|
567
|
+
emit({ type: "tool-input-delta", id: msg.toolCallId, delta: msg.partialJson });
|
|
568
|
+
}
|
|
569
|
+
if (ct === "tool_call" && msg.toolCallId && msg.toolName) {
|
|
570
|
+
const accumulated = toolInputAccum.get(msg.toolCallId) ?? "{}";
|
|
571
|
+
emit({
|
|
572
|
+
type: "tool-call",
|
|
573
|
+
toolCallId: msg.toolCallId,
|
|
574
|
+
toolName: msg.toolName,
|
|
575
|
+
input: accumulated,
|
|
576
|
+
providerExecuted: true
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
if (ct === "tool_result" && msg.toolCallId) {
|
|
580
|
+
emit({
|
|
581
|
+
type: "tool-result",
|
|
582
|
+
toolCallId: msg.toolCallId,
|
|
583
|
+
result: { output: msg.result ?? "", title: "", metadata: {} },
|
|
584
|
+
providerExecuted: true
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (msg.action === "finish") {
|
|
589
|
+
if (textStarted) {
|
|
590
|
+
emit({ type: "text-end", id: textId });
|
|
591
|
+
}
|
|
592
|
+
if (reasoningStarted) {
|
|
593
|
+
emit({ type: "reasoning-end", id: reasoningId });
|
|
594
|
+
}
|
|
595
|
+
const u = msg.usage;
|
|
596
|
+
emit({
|
|
597
|
+
type: "finish",
|
|
598
|
+
finishReason: msg.finishReason ?? "stop",
|
|
599
|
+
usage: {
|
|
600
|
+
inputTokens: u?.input_tokens ?? u?.inputTokens,
|
|
601
|
+
outputTokens: u?.output_tokens ?? u?.outputTokens,
|
|
602
|
+
totalTokens: void 0
|
|
603
|
+
},
|
|
604
|
+
providerMetadata: msg.providerMetadata ?? {}
|
|
605
|
+
});
|
|
606
|
+
close();
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
if (msg.action === "error") {
|
|
610
|
+
emit({ type: "error", error: new Error(msg.message) });
|
|
611
|
+
close();
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
} catch (e) {
|
|
617
|
+
emit({ type: "error", error: new Error(String(e)) });
|
|
618
|
+
close();
|
|
619
|
+
}
|
|
620
|
+
},
|
|
621
|
+
cancel() {
|
|
622
|
+
ipcCall({ action: "destroy", opencodeSessionId: sid }).catch(() => {
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
return {
|
|
627
|
+
stream,
|
|
628
|
+
rawCall: { raw: { text }, rawHeaders: {} },
|
|
629
|
+
warnings
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
function createClwnd(config = {}) {
|
|
634
|
+
const fn = (modelId) => new ClwndModel(modelId, config);
|
|
635
|
+
fn.languageModel = (modelId) => new ClwndModel(modelId, config);
|
|
636
|
+
return fn;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// index.ts
|
|
640
|
+
var clwndPlugin = async () => ({
|
|
641
|
+
models: {
|
|
642
|
+
clwnd: createClwnd()
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
export {
|
|
646
|
+
ClwndModel,
|
|
647
|
+
clwndPlugin,
|
|
648
|
+
createClwnd
|
|
649
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@clwnd/opencode",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "clwnd for opencode",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"prepublishOnly": "bun run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"opencode",
|
|
21
|
+
"claude",
|
|
22
|
+
"clwnd",
|
|
23
|
+
"plugin",
|
|
24
|
+
"ai"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/adiled/clwnd.git",
|
|
30
|
+
"directory": "plugins/opencode"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@ai-sdk/provider": "^2.0.0",
|
|
37
|
+
"@ai-sdk/provider-utils": "^2.0.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@opencode-ai/plugin": ">=1.2.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@opencode-ai/plugin": "^1.2.27",
|
|
44
|
+
"tsup": "^8.0.0",
|
|
45
|
+
"typescript": "^5.7.0"
|
|
46
|
+
}
|
|
47
|
+
}
|