effect-codemode 0.0.1 → 0.1.0-beta.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 +638 -2
- package/dist/bridge.d.ts +46 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/codemode.d.ts +22 -0
- package/dist/codemode.d.ts.map +1 -0
- package/dist/executor-base.d.ts +25 -0
- package/dist/executor-base.d.ts.map +1 -0
- package/dist/executor.d.ts +18 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/group.d.ts +71 -0
- package/dist/group.d.ts.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1365 -0
- package/dist/index.js.map +25 -0
- package/dist/middleware.d.ts +18 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/registry.d.ts +52 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/sanitize.d.ts +10 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/search.d.ts +35 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/serve.d.ts +35 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/testing.d.ts +26 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +890 -0
- package/dist/testing.js.map +18 -0
- package/dist/tool.d.ts +69 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/transport.d.ts +10 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/typegen.d.ts +33 -0
- package/dist/typegen.d.ts.map +1 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +44 -10
package/dist/testing.js
ADDED
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
// src/testing.ts
|
|
2
|
+
import { Effect as Effect4, Layer as Layer3 } from "effect";
|
|
3
|
+
|
|
4
|
+
// src/bridge.ts
|
|
5
|
+
import { Effect, Exit, Runtime, Schedule, Schema } from "effect";
|
|
6
|
+
|
|
7
|
+
// src/sanitize.ts
|
|
8
|
+
var JS_RESERVED = new Set([
|
|
9
|
+
"break",
|
|
10
|
+
"case",
|
|
11
|
+
"catch",
|
|
12
|
+
"class",
|
|
13
|
+
"const",
|
|
14
|
+
"continue",
|
|
15
|
+
"debugger",
|
|
16
|
+
"default",
|
|
17
|
+
"delete",
|
|
18
|
+
"do",
|
|
19
|
+
"else",
|
|
20
|
+
"enum",
|
|
21
|
+
"export",
|
|
22
|
+
"extends",
|
|
23
|
+
"false",
|
|
24
|
+
"finally",
|
|
25
|
+
"for",
|
|
26
|
+
"function",
|
|
27
|
+
"if",
|
|
28
|
+
"import",
|
|
29
|
+
"in",
|
|
30
|
+
"instanceof",
|
|
31
|
+
"let",
|
|
32
|
+
"new",
|
|
33
|
+
"null",
|
|
34
|
+
"return",
|
|
35
|
+
"super",
|
|
36
|
+
"switch",
|
|
37
|
+
"this",
|
|
38
|
+
"throw",
|
|
39
|
+
"true",
|
|
40
|
+
"try",
|
|
41
|
+
"typeof",
|
|
42
|
+
"undefined",
|
|
43
|
+
"var",
|
|
44
|
+
"void",
|
|
45
|
+
"while",
|
|
46
|
+
"with",
|
|
47
|
+
"yield",
|
|
48
|
+
"await",
|
|
49
|
+
"static",
|
|
50
|
+
"implements",
|
|
51
|
+
"interface",
|
|
52
|
+
"package",
|
|
53
|
+
"private",
|
|
54
|
+
"protected",
|
|
55
|
+
"public"
|
|
56
|
+
]);
|
|
57
|
+
function sanitizeToolName(name) {
|
|
58
|
+
let result = name.replace(/[-.\s]/g, "_");
|
|
59
|
+
result = result.replace(/[^a-zA-Z0-9_$]/g, "");
|
|
60
|
+
if (result.length === 0)
|
|
61
|
+
return "_";
|
|
62
|
+
if (/^\d/.test(result)) {
|
|
63
|
+
result = `_${result}`;
|
|
64
|
+
}
|
|
65
|
+
if (JS_RESERVED.has(result)) {
|
|
66
|
+
result = `${result}_`;
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/bridge.ts
|
|
72
|
+
var OptionTypeId = Symbol.for("effect/Option");
|
|
73
|
+
var EitherTypeId = Symbol.for("effect/Either");
|
|
74
|
+
var unwrapEffectTypes = (value, seen) => {
|
|
75
|
+
if (value === null || value === undefined || typeof value !== "object") {
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
const obj = value;
|
|
79
|
+
const tracking = seen ?? new WeakSet;
|
|
80
|
+
if (tracking.has(obj)) {
|
|
81
|
+
return "[Circular]";
|
|
82
|
+
}
|
|
83
|
+
tracking.add(obj);
|
|
84
|
+
if (OptionTypeId in obj) {
|
|
85
|
+
if (obj["_tag"] === "None") {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
if (obj["_tag"] === "Some") {
|
|
89
|
+
return unwrapEffectTypes(obj["value"], tracking);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (EitherTypeId in obj) {
|
|
93
|
+
if (obj["_tag"] === "Right") {
|
|
94
|
+
return unwrapEffectTypes(obj["right"], tracking);
|
|
95
|
+
}
|
|
96
|
+
if (obj["_tag"] === "Left") {
|
|
97
|
+
return { _tag: "Left", left: unwrapEffectTypes(obj["left"], tracking) };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(obj)) {
|
|
101
|
+
return obj.map((item) => unwrapEffectTypes(item, tracking));
|
|
102
|
+
}
|
|
103
|
+
const result = {};
|
|
104
|
+
for (const key of Object.keys(obj)) {
|
|
105
|
+
result[key] = unwrapEffectTypes(obj[key], tracking);
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
};
|
|
109
|
+
var MAX_RETRIES = 3;
|
|
110
|
+
var isRateLimitError = (err) => {
|
|
111
|
+
if (typeof err !== "object" || err === null)
|
|
112
|
+
return false;
|
|
113
|
+
const obj = err;
|
|
114
|
+
if (obj["_tag"] === "RateLimitError")
|
|
115
|
+
return true;
|
|
116
|
+
if (obj["status"] === 429)
|
|
117
|
+
return true;
|
|
118
|
+
return false;
|
|
119
|
+
};
|
|
120
|
+
var backoff = (attempt, retryAfterSeconds) => {
|
|
121
|
+
const ms = retryAfterSeconds !== undefined ? retryAfterSeconds * 1000 : Math.pow(2, attempt) * 1000;
|
|
122
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
123
|
+
};
|
|
124
|
+
var applyMiddleware = (middlewares, invocation, base) => {
|
|
125
|
+
let current = base;
|
|
126
|
+
for (let i = middlewares.length - 1;i >= 0; i--) {
|
|
127
|
+
const mw = middlewares[i];
|
|
128
|
+
const next = current;
|
|
129
|
+
current = mw(invocation, next);
|
|
130
|
+
}
|
|
131
|
+
return current;
|
|
132
|
+
};
|
|
133
|
+
var effectToAsyncFn = (tool, runtime, options) => {
|
|
134
|
+
const shouldValidateOutput = options?.validateOutput !== false;
|
|
135
|
+
const middlewares = options?.middleware ?? [];
|
|
136
|
+
return async (rawInput) => {
|
|
137
|
+
const decoded = Schema.decodeUnknownSync(tool.inputSchema)(rawInput);
|
|
138
|
+
let lastError;
|
|
139
|
+
for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
|
|
140
|
+
if (attempt > 0) {
|
|
141
|
+
const retryAfter = isRateLimitError(lastError) ? lastError.retryAfter : undefined;
|
|
142
|
+
await backoff(attempt - 1, retryAfter);
|
|
143
|
+
}
|
|
144
|
+
let effect = tool.execute(decoded);
|
|
145
|
+
if (middlewares.length > 0) {
|
|
146
|
+
const invocation = { name: tool.name, input: decoded };
|
|
147
|
+
const provided = Effect.provide(effect, runtime.context);
|
|
148
|
+
effect = applyMiddleware(middlewares, invocation, provided);
|
|
149
|
+
}
|
|
150
|
+
const exit = await Runtime.runPromiseExit(runtime)(effect);
|
|
151
|
+
if (Exit.isSuccess(exit)) {
|
|
152
|
+
const unwrapped = unwrapEffectTypes(exit.value);
|
|
153
|
+
if (shouldValidateOutput && tool.outputSchema) {
|
|
154
|
+
try {
|
|
155
|
+
Schema.decodeUnknownSync(tool.outputSchema, {
|
|
156
|
+
onExcessProperty: "preserve"
|
|
157
|
+
})(unwrapped);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
console.warn(`[codemode] Output validation warning for tool "${tool.name}":`, e instanceof Error ? e.message : String(e));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return unwrapped;
|
|
163
|
+
}
|
|
164
|
+
const cause = exit.cause;
|
|
165
|
+
const failure = cause._tag === "Fail" ? cause.error : cause;
|
|
166
|
+
const error = {
|
|
167
|
+
_tag: typeof failure === "object" && failure !== null && "_tag" in failure ? failure._tag : "ExecutionError",
|
|
168
|
+
message: typeof failure === "object" && failure !== null && "message" in failure ? failure.message : String(failure)
|
|
169
|
+
};
|
|
170
|
+
if (typeof failure === "object" && failure !== null) {
|
|
171
|
+
for (const [k, v] of Object.entries(failure)) {
|
|
172
|
+
if (k !== "_tag" && k !== "message") {
|
|
173
|
+
error[k] = v;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (isRateLimitError(error) && attempt < MAX_RETRIES) {
|
|
178
|
+
lastError = error;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
throw lastError;
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
var effectToEffectFn = (tool, runtime, options) => {
|
|
187
|
+
const shouldValidateOutput = options?.validateOutput !== false;
|
|
188
|
+
const middlewares = options?.middleware ?? [];
|
|
189
|
+
return (rawInput) => {
|
|
190
|
+
const decoded = Schema.decodeUnknownSync(tool.inputSchema)(rawInput);
|
|
191
|
+
const baseEffect = Effect.flatMap(Effect.provide(tool.execute(decoded), runtime.context), (value) => {
|
|
192
|
+
const unwrapped = unwrapEffectTypes(value);
|
|
193
|
+
if (shouldValidateOutput && tool.outputSchema) {
|
|
194
|
+
try {
|
|
195
|
+
Schema.decodeUnknownSync(tool.outputSchema, {
|
|
196
|
+
onExcessProperty: "preserve"
|
|
197
|
+
})(unwrapped);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
console.warn(`[codemode] Output validation warning for tool "${tool.name}":`, e instanceof Error ? e.message : String(e));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return Effect.succeed(unwrapped);
|
|
203
|
+
});
|
|
204
|
+
const withMiddleware = middlewares.length > 0 ? applyMiddleware(middlewares, { name: tool.name, input: decoded }, baseEffect) : baseEffect;
|
|
205
|
+
const retrySchedule = Schedule.exponential("1 second").pipe(Schedule.compose(Schedule.recurs(MAX_RETRIES)));
|
|
206
|
+
return withMiddleware.pipe(Effect.retry({
|
|
207
|
+
schedule: retrySchedule,
|
|
208
|
+
while: (err) => isRateLimitError(err)
|
|
209
|
+
}));
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
var buildEffectFnTable = (tools, runtime, options) => {
|
|
213
|
+
const table = {};
|
|
214
|
+
for (const tool of tools) {
|
|
215
|
+
const fn = effectToEffectFn(tool, runtime, options);
|
|
216
|
+
const key = sanitizeToolName(tool.name);
|
|
217
|
+
table[key] = (...args) => fn(args[0]);
|
|
218
|
+
}
|
|
219
|
+
return table;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/typegen.ts
|
|
223
|
+
import { Option } from "effect";
|
|
224
|
+
import * as SchemaAST from "effect/SchemaAST";
|
|
225
|
+
function astToTypeScript(ast, seen = new Set) {
|
|
226
|
+
switch (ast._tag) {
|
|
227
|
+
case "StringKeyword":
|
|
228
|
+
return "string";
|
|
229
|
+
case "NumberKeyword":
|
|
230
|
+
return "number";
|
|
231
|
+
case "BooleanKeyword":
|
|
232
|
+
return "boolean";
|
|
233
|
+
case "BigIntKeyword":
|
|
234
|
+
return "bigint";
|
|
235
|
+
case "UndefinedKeyword":
|
|
236
|
+
return "undefined";
|
|
237
|
+
case "VoidKeyword":
|
|
238
|
+
return "void";
|
|
239
|
+
case "NeverKeyword":
|
|
240
|
+
return "never";
|
|
241
|
+
case "UnknownKeyword":
|
|
242
|
+
return "unknown";
|
|
243
|
+
case "AnyKeyword":
|
|
244
|
+
return "any";
|
|
245
|
+
case "ObjectKeyword":
|
|
246
|
+
return "object";
|
|
247
|
+
case "SymbolKeyword":
|
|
248
|
+
return "symbol";
|
|
249
|
+
case "UniqueSymbol":
|
|
250
|
+
return `typeof ${String(ast.symbol)}`;
|
|
251
|
+
case "Literal": {
|
|
252
|
+
if (ast.literal === null)
|
|
253
|
+
return "null";
|
|
254
|
+
if (typeof ast.literal === "string")
|
|
255
|
+
return JSON.stringify(ast.literal);
|
|
256
|
+
return String(ast.literal);
|
|
257
|
+
}
|
|
258
|
+
case "Enums":
|
|
259
|
+
return ast.enums.map(([_, v]) => JSON.stringify(v)).join(" | ") || "never";
|
|
260
|
+
case "TemplateLiteral": {
|
|
261
|
+
const spans = ast.spans.map((s) => {
|
|
262
|
+
const t = s.type._tag === "StringKeyword" ? "string" : s.type._tag === "NumberKeyword" ? "number" : astToTypeScript(s.type, seen);
|
|
263
|
+
return `\${${t}}${s.literal}`;
|
|
264
|
+
}).join("");
|
|
265
|
+
return `\`${ast.head}${spans}\``;
|
|
266
|
+
}
|
|
267
|
+
case "Union":
|
|
268
|
+
return ast.types.map((t) => astToTypeScript(t, seen)).join(" | ");
|
|
269
|
+
case "TupleType": {
|
|
270
|
+
const elems = ast.elements.map((e) => {
|
|
271
|
+
const t = astToTypeScript(e.type, seen);
|
|
272
|
+
return e.isOptional ? `${t}?` : t;
|
|
273
|
+
});
|
|
274
|
+
if (ast.rest.length > 0) {
|
|
275
|
+
const restType = astToTypeScript(ast.rest[0].type, seen);
|
|
276
|
+
if (ast.elements.length === 0)
|
|
277
|
+
return `Array<${restType}>`;
|
|
278
|
+
elems.push(`...Array<${restType}>`);
|
|
279
|
+
}
|
|
280
|
+
return `[${elems.join(", ")}]`;
|
|
281
|
+
}
|
|
282
|
+
case "TypeLiteral": {
|
|
283
|
+
if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) {
|
|
284
|
+
return "{}";
|
|
285
|
+
}
|
|
286
|
+
const props = ast.propertySignatures.map((ps) => {
|
|
287
|
+
const key = typeof ps.name === "string" ? /^[a-zA-Z_$][\w$]*$/.test(ps.name) ? ps.name : JSON.stringify(ps.name) : `[${String(ps.name)}]`;
|
|
288
|
+
const opt = ps.isOptional ? "?" : "";
|
|
289
|
+
return ` ${key}${opt}: ${astToTypeScript(ps.type, seen)}`;
|
|
290
|
+
});
|
|
291
|
+
const idxSigs = ast.indexSignatures.map((is) => {
|
|
292
|
+
const param = astToTypeScript(is.parameter, seen);
|
|
293
|
+
return ` [key: ${param}]: ${astToTypeScript(is.type, seen)}`;
|
|
294
|
+
});
|
|
295
|
+
return `{
|
|
296
|
+
${[...props, ...idxSigs].join(`
|
|
297
|
+
`)}
|
|
298
|
+
}`;
|
|
299
|
+
}
|
|
300
|
+
case "Refinement":
|
|
301
|
+
return astToTypeScript(ast.from, seen);
|
|
302
|
+
case "Transformation":
|
|
303
|
+
return astToTypeScript(ast.to, seen);
|
|
304
|
+
case "Suspend": {
|
|
305
|
+
const id = Option.getOrUndefined(SchemaAST.getIdentifierAnnotation(ast));
|
|
306
|
+
if (id)
|
|
307
|
+
return id;
|
|
308
|
+
if (seen.has(ast))
|
|
309
|
+
return "unknown";
|
|
310
|
+
seen.add(ast);
|
|
311
|
+
return astToTypeScript(ast.f(), seen);
|
|
312
|
+
}
|
|
313
|
+
case "Declaration": {
|
|
314
|
+
const id = Option.getOrUndefined(SchemaAST.getIdentifierAnnotation(ast));
|
|
315
|
+
return id ?? "unknown";
|
|
316
|
+
}
|
|
317
|
+
default:
|
|
318
|
+
return "unknown";
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function toPascalCase(name) {
|
|
322
|
+
return name.split(/[-_]/).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
323
|
+
}
|
|
324
|
+
function fieldDescription(ast) {
|
|
325
|
+
const direct = Option.getOrUndefined(SchemaAST.getDescriptionAnnotation(ast));
|
|
326
|
+
if (direct)
|
|
327
|
+
return direct;
|
|
328
|
+
if (ast._tag === "Union") {
|
|
329
|
+
for (const member of ast.types) {
|
|
330
|
+
if (member._tag === "UndefinedKeyword")
|
|
331
|
+
continue;
|
|
332
|
+
const desc = Option.getOrUndefined(SchemaAST.getDescriptionAnnotation(member));
|
|
333
|
+
if (desc)
|
|
334
|
+
return desc;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
function resolveTypeLiteral(ast) {
|
|
340
|
+
if (ast._tag === "Transformation")
|
|
341
|
+
return ast.to;
|
|
342
|
+
return ast;
|
|
343
|
+
}
|
|
344
|
+
function buildJSDoc(description, inputAst, errors) {
|
|
345
|
+
const lines = [`/** ${description}`];
|
|
346
|
+
if (inputAst) {
|
|
347
|
+
const resolved = resolveTypeLiteral(inputAst);
|
|
348
|
+
if (resolved._tag === "TypeLiteral") {
|
|
349
|
+
for (const ps of resolved.propertySignatures) {
|
|
350
|
+
const desc = fieldDescription(ps.type);
|
|
351
|
+
if (desc)
|
|
352
|
+
lines.push(` * @param ${String(ps.name)} - ${desc}`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (errors && errors.length > 0) {
|
|
357
|
+
for (const err of errors) {
|
|
358
|
+
const tag = errorTag(err);
|
|
359
|
+
lines.push(` * @error ${tag} — catchable with Effect.catchTag("${tag}", ...)`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
lines.push(" */");
|
|
363
|
+
return lines.join(`
|
|
364
|
+
`);
|
|
365
|
+
}
|
|
366
|
+
function errorTag(error) {
|
|
367
|
+
return typeof error === "string" ? error : error.tag;
|
|
368
|
+
}
|
|
369
|
+
function buildErrorType(error) {
|
|
370
|
+
const tag = errorTag(error);
|
|
371
|
+
if (typeof error === "string") {
|
|
372
|
+
return `type ${tag} = { readonly _tag: "${tag}"; readonly message: string }`;
|
|
373
|
+
}
|
|
374
|
+
const typeString = astToTypeScript(error.ast);
|
|
375
|
+
return `type ${tag} = ${typeString}`;
|
|
376
|
+
}
|
|
377
|
+
function resolveSharedOutputName(ast, firstToolKey) {
|
|
378
|
+
if (ast) {
|
|
379
|
+
const id = Option.getOrUndefined(SchemaAST.getIdentifierAnnotation(ast));
|
|
380
|
+
if (id)
|
|
381
|
+
return id;
|
|
382
|
+
if (ast._tag === "Transformation") {
|
|
383
|
+
const toId = Option.getOrUndefined(SchemaAST.getIdentifierAnnotation(ast.to));
|
|
384
|
+
if (toId)
|
|
385
|
+
return toId;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return `${toPascalCase(sanitizeToolName(firstToolKey))}Output`;
|
|
389
|
+
}
|
|
390
|
+
function generateDeclarations(tools) {
|
|
391
|
+
const entries = Object.entries(tools);
|
|
392
|
+
const outputTypeStringToGroup = new Map;
|
|
393
|
+
const toolOutputTypeName = new Map;
|
|
394
|
+
for (const [key, tool] of entries) {
|
|
395
|
+
const typeString = tool.outputSchema ? astToTypeScript(tool.outputSchema) : "unknown";
|
|
396
|
+
const existing = outputTypeStringToGroup.get(typeString);
|
|
397
|
+
if (existing) {
|
|
398
|
+
existing.keys.push(key);
|
|
399
|
+
} else {
|
|
400
|
+
outputTypeStringToGroup.set(typeString, { name: "", keys: [key] });
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
for (const [_typeString, group] of outputTypeStringToGroup) {
|
|
404
|
+
if (group.keys.length > 1) {
|
|
405
|
+
const firstTool = tools[group.keys[0]];
|
|
406
|
+
group.name = resolveSharedOutputName(firstTool.outputSchema, group.keys[0]);
|
|
407
|
+
} else {
|
|
408
|
+
const key = group.keys[0];
|
|
409
|
+
group.name = `${toPascalCase(sanitizeToolName(key))}Output`;
|
|
410
|
+
}
|
|
411
|
+
for (const key of group.keys) {
|
|
412
|
+
toolOutputTypeName.set(key, group.name);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const parts = [];
|
|
416
|
+
const methods = [];
|
|
417
|
+
const emittedErrors = new Set;
|
|
418
|
+
const emittedOutputTypes = new Set;
|
|
419
|
+
for (const [key, tool] of entries) {
|
|
420
|
+
const safeName = sanitizeToolName(key);
|
|
421
|
+
const pascal = toPascalCase(safeName);
|
|
422
|
+
const inputType = `${pascal}Input`;
|
|
423
|
+
const outputType = toolOutputTypeName.get(key);
|
|
424
|
+
parts.push(`type ${inputType} = ${astToTypeScript(tool.inputSchema)}`);
|
|
425
|
+
if (!emittedOutputTypes.has(outputType)) {
|
|
426
|
+
emittedOutputTypes.add(outputType);
|
|
427
|
+
const typeString = tool.outputSchema ? astToTypeScript(tool.outputSchema) : "unknown";
|
|
428
|
+
parts.push(`type ${outputType} = ${typeString}`);
|
|
429
|
+
}
|
|
430
|
+
if (tool.errors && tool.errors.length > 0) {
|
|
431
|
+
for (const err of tool.errors) {
|
|
432
|
+
const tag = errorTag(err);
|
|
433
|
+
if (!emittedErrors.has(tag)) {
|
|
434
|
+
emittedErrors.add(tag);
|
|
435
|
+
parts.push(buildErrorType(err));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
const errorType = tool.errors && tool.errors.length > 0 ? tool.errors.map((e) => errorTag(e)).join(" | ") : "never";
|
|
440
|
+
const jsdoc = buildJSDoc(tool.description, tool.inputSchema, tool.errors);
|
|
441
|
+
methods.push(` ${jsdoc}
|
|
442
|
+
${safeName}(input: ${inputType}): Effect<${outputType}, ${errorType}>`);
|
|
443
|
+
}
|
|
444
|
+
parts.push("");
|
|
445
|
+
parts.push(`declare const codemode: {
|
|
446
|
+
${methods.join(`
|
|
447
|
+
`)}
|
|
448
|
+
}`);
|
|
449
|
+
return parts.join(`
|
|
450
|
+
|
|
451
|
+
`);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// src/types.ts
|
|
455
|
+
function toTypegenDescriptor(tool) {
|
|
456
|
+
return {
|
|
457
|
+
name: tool.name,
|
|
458
|
+
description: tool.description,
|
|
459
|
+
inputSchema: tool.inputSchema.ast,
|
|
460
|
+
outputSchema: tool.outputSchema ? tool.outputSchema.ast : undefined,
|
|
461
|
+
errors: tool.errors
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// src/codemode.ts
|
|
466
|
+
function buildExamplesBlock(tools, max = 3) {
|
|
467
|
+
const withExamples = Object.entries(tools).filter(([_, t]) => t.example);
|
|
468
|
+
if (withExamples.length === 0)
|
|
469
|
+
return "";
|
|
470
|
+
const seen = new Set;
|
|
471
|
+
const picked = [];
|
|
472
|
+
for (const [key, tool] of withExamples) {
|
|
473
|
+
const svc = tool.service ?? "unknown";
|
|
474
|
+
if (!seen.has(svc) && picked.length < max) {
|
|
475
|
+
seen.add(svc);
|
|
476
|
+
picked.push([key, tool]);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
for (const entry of withExamples) {
|
|
480
|
+
if (picked.length >= max)
|
|
481
|
+
break;
|
|
482
|
+
if (!picked.includes(entry)) {
|
|
483
|
+
picked.push(entry);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const lines = picked.map(([key, tool]) => `**${key}:**
|
|
487
|
+
\`\`\`typescript
|
|
488
|
+
${tool.example}
|
|
489
|
+
\`\`\``);
|
|
490
|
+
return `
|
|
491
|
+
|
|
492
|
+
Examples:
|
|
493
|
+
|
|
494
|
+
${lines.join(`
|
|
495
|
+
|
|
496
|
+
`)}`;
|
|
497
|
+
}
|
|
498
|
+
var COMPACT_THRESHOLD = 7;
|
|
499
|
+
function createCodemodeTool(sandboxTools, runtime, executor, serviceStatuses, middleware, groupNames) {
|
|
500
|
+
const typegenTools = {};
|
|
501
|
+
for (const [key, tool] of Object.entries(sandboxTools)) {
|
|
502
|
+
typegenTools[key] = toTypegenDescriptor(tool);
|
|
503
|
+
}
|
|
504
|
+
const declarations = generateDeclarations(typegenTools);
|
|
505
|
+
const tools = Object.values(sandboxTools);
|
|
506
|
+
const fns = buildEffectFnTable(tools, runtime, { middleware });
|
|
507
|
+
const statusBlock = serviceStatuses ? `
|
|
508
|
+
Service status:
|
|
509
|
+
${serviceStatuses}
|
|
510
|
+
` : "";
|
|
511
|
+
const serviceList = groupNames && groupNames.length > 0 ? groupNames.join(", ") : "configured services";
|
|
512
|
+
const examplesBlock = buildExamplesBlock(sandboxTools);
|
|
513
|
+
const toolCount = Object.keys(sandboxTools).length;
|
|
514
|
+
const isCompact = toolCount > COMPACT_THRESHOLD;
|
|
515
|
+
let apiBlock;
|
|
516
|
+
if (isCompact) {
|
|
517
|
+
const catalog = Object.entries(sandboxTools).map(([key, tool]) => ` ${key}(input): ${tool.description}`).join(`
|
|
518
|
+
`);
|
|
519
|
+
apiBlock = `Available API (${toolCount} tools — use \`search_tools\` for full type signatures):
|
|
520
|
+
|
|
521
|
+
${catalog}`;
|
|
522
|
+
} else {
|
|
523
|
+
apiBlock = `Available API:
|
|
524
|
+
|
|
525
|
+
\`\`\`typescript
|
|
526
|
+
${declarations}
|
|
527
|
+
\`\`\``;
|
|
528
|
+
}
|
|
529
|
+
const description = `Execute TypeScript code using Effect to orchestrate operations across ${serviceList}.
|
|
530
|
+
|
|
531
|
+
The sandbox has \`Effect\`, \`pipe\`, \`Duration\`, \`Schedule\`, \`Option\`, \`Either\`, \`Match\`, \`Schema\`, \`Arr\` (Effect's Array module), \`Ref\`, \`Stream\`, \`Chunk\`, \`Data\`, \`Order\`, and \`Predicate\` available as globals.
|
|
532
|
+
Each method on \`codemode\` returns an \`Effect\` — use \`yield*\` inside \`Effect.gen\` to unwrap values.
|
|
533
|
+
Return an Effect as the final value and the runtime will execute it.
|
|
534
|
+
${statusBlock}
|
|
535
|
+
|
|
536
|
+
${apiBlock}
|
|
537
|
+
|
|
538
|
+
Write an \`Effect.gen\` program that calls methods on the \`codemode\` object using \`yield*\`.
|
|
539
|
+
Return a value from the generator to send results back.
|
|
540
|
+
${examplesBlock}
|
|
541
|
+
|
|
542
|
+
**Error handling with catchTag** — gracefully catch service-specific errors:
|
|
543
|
+
\`\`\`typescript
|
|
544
|
+
Effect.gen(function* () {
|
|
545
|
+
return yield* codemode.some_tool({ id: "nonexistent" })
|
|
546
|
+
}).pipe(
|
|
547
|
+
Effect.catchTag("NotFoundError", (e) => Effect.succeed({ error: e.message }))
|
|
548
|
+
)
|
|
549
|
+
\`\`\`
|
|
550
|
+
|
|
551
|
+
**Retry with backoff** — retry a flaky operation:
|
|
552
|
+
\`\`\`typescript
|
|
553
|
+
Effect.gen(function* () {
|
|
554
|
+
return yield* codemode.some_tool({ id: "ID" })
|
|
555
|
+
}).pipe(
|
|
556
|
+
Effect.retry({ times: 3, schedule: Schedule.exponential("1 second") })
|
|
557
|
+
)
|
|
558
|
+
\`\`\`
|
|
559
|
+
|
|
560
|
+
**Timeout** — bail if an operation takes too long:
|
|
561
|
+
\`\`\`typescript
|
|
562
|
+
Effect.gen(function* () {
|
|
563
|
+
return yield* codemode.some_tool({})
|
|
564
|
+
}).pipe(
|
|
565
|
+
Effect.timeout("10 seconds")
|
|
566
|
+
)
|
|
567
|
+
\`\`\`
|
|
568
|
+
|
|
569
|
+
**Backward compatibility** — async/await still works:
|
|
570
|
+
\`\`\`typescript
|
|
571
|
+
async () => {
|
|
572
|
+
const result = await Effect.runPromise(codemode.some_tool({}))
|
|
573
|
+
return result
|
|
574
|
+
}
|
|
575
|
+
\`\`\``;
|
|
576
|
+
return {
|
|
577
|
+
name: "execute_code",
|
|
578
|
+
description,
|
|
579
|
+
declarations,
|
|
580
|
+
execute: (code, options) => executor.execute(code, fns, options)
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/executor.ts
|
|
585
|
+
import * as vm from "node:vm";
|
|
586
|
+
import { Context, Layer } from "effect";
|
|
587
|
+
|
|
588
|
+
// src/executor-base.ts
|
|
589
|
+
import * as acorn from "acorn";
|
|
590
|
+
import {
|
|
591
|
+
Array as Arr,
|
|
592
|
+
Chunk,
|
|
593
|
+
Data,
|
|
594
|
+
Duration,
|
|
595
|
+
Effect as Effect2,
|
|
596
|
+
Either,
|
|
597
|
+
Match,
|
|
598
|
+
Option as Option2,
|
|
599
|
+
Order,
|
|
600
|
+
Predicate,
|
|
601
|
+
Ref,
|
|
602
|
+
Schedule as Schedule2,
|
|
603
|
+
Schema as Schema2,
|
|
604
|
+
Stream,
|
|
605
|
+
pipe
|
|
606
|
+
} from "effect";
|
|
607
|
+
var EffectTypeId = Symbol.for("effect/Effect");
|
|
608
|
+
var DEFAULT_TIMEOUT_MS = 30000;
|
|
609
|
+
var stripTypeScript = (() => {
|
|
610
|
+
const g = globalThis;
|
|
611
|
+
if (typeof g["Bun"] === "object" && g["Bun"] !== null) {
|
|
612
|
+
const BunNs = g["Bun"];
|
|
613
|
+
const transpiler = new BunNs.Transpiler({ loader: "ts" });
|
|
614
|
+
const WRAP_PREFIX = "var __codemode__ = ";
|
|
615
|
+
return (code) => {
|
|
616
|
+
try {
|
|
617
|
+
const wrapped = `${WRAP_PREFIX}${code}`;
|
|
618
|
+
const result = transpiler.transformSync(wrapped).trim();
|
|
619
|
+
if (result.startsWith(WRAP_PREFIX)) {
|
|
620
|
+
let body = result.slice(WRAP_PREFIX.length);
|
|
621
|
+
if (body.endsWith(";"))
|
|
622
|
+
body = body.slice(0, -1);
|
|
623
|
+
return body.trim();
|
|
624
|
+
}
|
|
625
|
+
} catch {
|
|
626
|
+
}
|
|
627
|
+
try {
|
|
628
|
+
return transpiler.transformSync(code).trim();
|
|
629
|
+
} catch {
|
|
630
|
+
return code;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
return (code) => code;
|
|
635
|
+
})();
|
|
636
|
+
var _globals = globalThis;
|
|
637
|
+
var _fetch = _globals["fetch"];
|
|
638
|
+
var _setTimeout = _globals["setTimeout"];
|
|
639
|
+
var _clearTimeout = _globals["clearTimeout"];
|
|
640
|
+
var _URL = _globals["URL"];
|
|
641
|
+
var _Response = _globals["Response"];
|
|
642
|
+
var _Request = _globals["Request"];
|
|
643
|
+
var _Headers = _globals["Headers"];
|
|
644
|
+
var _Promise = _globals["Promise"];
|
|
645
|
+
function parallel(fns) {
|
|
646
|
+
return _Promise.all(fns.map((fn) => fn()));
|
|
647
|
+
}
|
|
648
|
+
function sleep(ms) {
|
|
649
|
+
return new _Promise((resolve) => _setTimeout(resolve, ms));
|
|
650
|
+
}
|
|
651
|
+
function isEffect(value) {
|
|
652
|
+
return typeof value === "object" && value !== null && EffectTypeId in value;
|
|
653
|
+
}
|
|
654
|
+
function normalizeCode(code) {
|
|
655
|
+
const trimmed = code.trim();
|
|
656
|
+
const stripped = stripTypeScript(trimmed);
|
|
657
|
+
const jsCode = stripped || trimmed;
|
|
658
|
+
try {
|
|
659
|
+
const ast = acorn.parse(jsCode, { ecmaVersion: 2022, sourceType: "module" });
|
|
660
|
+
const body = ast.body;
|
|
661
|
+
if (body.length === 0) {
|
|
662
|
+
return `return (async () => { ${jsCode} })()`;
|
|
663
|
+
}
|
|
664
|
+
const first = body[0];
|
|
665
|
+
if (body.length === 1 && first.type === "ExpressionStatement") {
|
|
666
|
+
const expr = first.expression;
|
|
667
|
+
if (expr.type === "ArrowFunctionExpression") {
|
|
668
|
+
const arrowExpr = jsCode.slice(expr.start, expr.end);
|
|
669
|
+
return `return (${arrowExpr})()`;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
const last = body[body.length - 1];
|
|
673
|
+
if (last.type === "ExpressionStatement") {
|
|
674
|
+
const before = jsCode.slice(0, last.start);
|
|
675
|
+
const expr = jsCode.slice(last.start, last.end);
|
|
676
|
+
const cleanExpr = expr.replace(/;\s*$/, "");
|
|
677
|
+
return `return (async () => { ${before}return ${cleanExpr} })()`;
|
|
678
|
+
}
|
|
679
|
+
return `return (async () => { ${jsCode} })()`;
|
|
680
|
+
} catch {
|
|
681
|
+
return normalizeCodeFallback(jsCode);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function normalizeCodeFallback(code) {
|
|
685
|
+
if (isArrowFallback(code)) {
|
|
686
|
+
return `return (${code})()`;
|
|
687
|
+
}
|
|
688
|
+
return `return (async () => { ${code} })()`;
|
|
689
|
+
}
|
|
690
|
+
function isArrowFallback(code) {
|
|
691
|
+
if (/^async\s*\(/.test(code))
|
|
692
|
+
return true;
|
|
693
|
+
if (/^async\s+[a-zA-Z_$]\w*\s*=>/.test(code))
|
|
694
|
+
return true;
|
|
695
|
+
if (code.startsWith("(")) {
|
|
696
|
+
let depth = 0;
|
|
697
|
+
for (let i = 0;i < code.length; i++) {
|
|
698
|
+
if (code[i] === "(")
|
|
699
|
+
depth++;
|
|
700
|
+
else if (code[i] === ")") {
|
|
701
|
+
depth--;
|
|
702
|
+
if (depth === 0) {
|
|
703
|
+
const rest = code.slice(i + 1).trimStart();
|
|
704
|
+
return rest.startsWith("=>");
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (/^[a-zA-Z_$]\w*\s*=>/.test(code))
|
|
710
|
+
return true;
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
713
|
+
var networkBlockedFn = () => {
|
|
714
|
+
throw new Error("Network access is disabled (allowNetwork: false)");
|
|
715
|
+
};
|
|
716
|
+
var NetworkBlockedClass = new Proxy(class {
|
|
717
|
+
}, {
|
|
718
|
+
construct() {
|
|
719
|
+
throw new Error("Network access is disabled (allowNetwork: false)");
|
|
720
|
+
},
|
|
721
|
+
apply() {
|
|
722
|
+
throw new Error("Network access is disabled (allowNetwork: false)");
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
function buildSandboxGlobals(fns, options, logs) {
|
|
726
|
+
const onLog = options?.onLog;
|
|
727
|
+
const emit = (level, msg) => {
|
|
728
|
+
logs.push(level === "log" ? msg : `[${level}] ${msg}`);
|
|
729
|
+
onLog?.(level, msg);
|
|
730
|
+
};
|
|
731
|
+
const capturedConsole = {
|
|
732
|
+
log: (...args) => emit("log", args.map(String).join(" ")),
|
|
733
|
+
warn: (...args) => emit("warn", args.map(String).join(" ")),
|
|
734
|
+
error: (...args) => emit("error", args.map(String).join(" ")),
|
|
735
|
+
info: (...args) => emit("info", args.map(String).join(" ")),
|
|
736
|
+
debug: (...args) => emit("debug", args.map(String).join(" "))
|
|
737
|
+
};
|
|
738
|
+
const allowNetwork = options?.allowNetwork !== false;
|
|
739
|
+
const globals = {
|
|
740
|
+
codemode: Object.freeze(fns),
|
|
741
|
+
console: Object.freeze(capturedConsole),
|
|
742
|
+
fetch: allowNetwork ? _fetch : networkBlockedFn,
|
|
743
|
+
setTimeout: _setTimeout,
|
|
744
|
+
clearTimeout: _clearTimeout,
|
|
745
|
+
URL: allowNetwork ? _URL : NetworkBlockedClass,
|
|
746
|
+
Response: allowNetwork ? _Response : NetworkBlockedClass,
|
|
747
|
+
Request: allowNetwork ? _Request : NetworkBlockedClass,
|
|
748
|
+
Headers: allowNetwork ? _Headers : NetworkBlockedClass,
|
|
749
|
+
Promise: _Promise,
|
|
750
|
+
parallel,
|
|
751
|
+
sleep,
|
|
752
|
+
Effect: Effect2,
|
|
753
|
+
pipe,
|
|
754
|
+
Duration,
|
|
755
|
+
Schedule: Schedule2,
|
|
756
|
+
Option: Option2,
|
|
757
|
+
Either,
|
|
758
|
+
Match,
|
|
759
|
+
Schema: Schema2,
|
|
760
|
+
Arr,
|
|
761
|
+
Ref,
|
|
762
|
+
Stream,
|
|
763
|
+
Chunk,
|
|
764
|
+
Data,
|
|
765
|
+
Order,
|
|
766
|
+
Predicate
|
|
767
|
+
};
|
|
768
|
+
return globals;
|
|
769
|
+
}
|
|
770
|
+
function createBaseExecutor(runCode) {
|
|
771
|
+
return {
|
|
772
|
+
execute: async (code, fns, options) => {
|
|
773
|
+
const logs = [];
|
|
774
|
+
const globals = buildSandboxGlobals(fns, options, logs);
|
|
775
|
+
try {
|
|
776
|
+
const trimmed = code.trim();
|
|
777
|
+
const wrappedCode = normalizeCode(trimmed);
|
|
778
|
+
const timeout = options?.["timeoutMs"] ?? DEFAULT_TIMEOUT_MS;
|
|
779
|
+
let result = await _Promise.race([
|
|
780
|
+
runCode(wrappedCode, globals),
|
|
781
|
+
new _Promise((_, reject) => _setTimeout(() => reject(new Error(`Execution timed out after ${timeout}ms`)), timeout))
|
|
782
|
+
]);
|
|
783
|
+
if (isEffect(result)) {
|
|
784
|
+
result = await Effect2.runPromise(result);
|
|
785
|
+
}
|
|
786
|
+
return { result, logs };
|
|
787
|
+
} catch (err) {
|
|
788
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
789
|
+
return { result: undefined, error: message, logs };
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/executor.ts
|
|
796
|
+
var createDirectExecutor = () => createBaseExecutor(async (wrappedCode, globals) => {
|
|
797
|
+
const paramNames = Object.keys(globals);
|
|
798
|
+
const paramValues = Object.values(globals);
|
|
799
|
+
const fn = new Function(...paramNames, wrappedCode);
|
|
800
|
+
return fn(...paramValues);
|
|
801
|
+
});
|
|
802
|
+
var createVmExecutor = () => createBaseExecutor(async (wrappedCode, globals) => {
|
|
803
|
+
const scriptCode = wrappedCode.startsWith("return ") ? wrappedCode.slice(7) : wrappedCode;
|
|
804
|
+
const context = vm.createContext(globals);
|
|
805
|
+
const script = new vm.Script(scriptCode);
|
|
806
|
+
return script.runInContext(context, { timeout: 35000 });
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
class CodeExecutor extends Context.Tag("@effect-codemode/Executor")() {
|
|
810
|
+
static Direct = Layer.succeed(CodeExecutor, createDirectExecutor());
|
|
811
|
+
static Vm = Layer.succeed(CodeExecutor, createVmExecutor());
|
|
812
|
+
static Default = CodeExecutor.Vm;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/registry.ts
|
|
816
|
+
import { Context as Context2, Effect as Effect3, Layer as Layer2, Ref as Ref2 } from "effect";
|
|
817
|
+
|
|
818
|
+
class CodemodeRegistry extends Context2.Tag("@effect-codemode/Registry")() {
|
|
819
|
+
static make = Effect3.gen(function* () {
|
|
820
|
+
const groupsRef = yield* Ref2.make([]);
|
|
821
|
+
const failedRef = yield* Ref2.make([]);
|
|
822
|
+
const globalMiddlewareRef = yield* Ref2.make([]);
|
|
823
|
+
return CodemodeRegistry.of({
|
|
824
|
+
registerGroup: (name, tools, middleware) => Ref2.update(groupsRef, (groups) => [...groups, { name, tools, middleware: middleware ?? [] }]),
|
|
825
|
+
registerFailure: (name, error) => Ref2.update(failedRef, (failed) => [...failed, { name, error }]),
|
|
826
|
+
registerMiddleware: (mw) => Ref2.update(globalMiddlewareRef, (mws) => [...mws, mw]),
|
|
827
|
+
snapshot: Effect3.all({
|
|
828
|
+
available: Ref2.get(groupsRef),
|
|
829
|
+
failed: Ref2.get(failedRef),
|
|
830
|
+
globalMiddleware: Ref2.get(globalMiddlewareRef)
|
|
831
|
+
})
|
|
832
|
+
});
|
|
833
|
+
});
|
|
834
|
+
static layer = Layer2.effect(CodemodeRegistry, CodemodeRegistry.make);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// src/testing.ts
|
|
838
|
+
function snapshotGroups(groups) {
|
|
839
|
+
return Effect4.gen(function* () {
|
|
840
|
+
const registryImpl = yield* CodemodeRegistry.make;
|
|
841
|
+
const registryLayer = Layer3.succeed(CodemodeRegistry, registryImpl);
|
|
842
|
+
const mergedGroups = groups.length === 1 ? groups[0] : Layer3.mergeAll(...groups);
|
|
843
|
+
const fullLayer = Layer3.provide(mergedGroups, registryLayer);
|
|
844
|
+
yield* Layer3.build(fullLayer).pipe(Effect4.scoped);
|
|
845
|
+
const { available, failed } = yield* registryImpl.snapshot;
|
|
846
|
+
const allTools = {};
|
|
847
|
+
for (const group of available) {
|
|
848
|
+
for (const [key, tool] of Object.entries(group.tools)) {
|
|
849
|
+
allTools[key] = { ...tool, service: group.name };
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
const statusParts = [];
|
|
853
|
+
for (const g of available) {
|
|
854
|
+
statusParts.push(` ${g.name}: available`);
|
|
855
|
+
}
|
|
856
|
+
for (const f of failed) {
|
|
857
|
+
statusParts.push(` ${f.name}: unavailable (${f.error})`);
|
|
858
|
+
}
|
|
859
|
+
const statusText = statusParts.join(`
|
|
860
|
+
`);
|
|
861
|
+
const groupNames = available.map((g) => g.name);
|
|
862
|
+
return { allTools, statusText, groupNames };
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
function buildTestTool(...groups) {
|
|
866
|
+
return Effect4.runSync(Effect4.gen(function* () {
|
|
867
|
+
const { allTools, statusText, groupNames } = yield* snapshotGroups(groups);
|
|
868
|
+
const rt = yield* Effect4.runtime();
|
|
869
|
+
const exec = yield* Effect4.provide(CodeExecutor, CodeExecutor.Default);
|
|
870
|
+
return createCodemodeTool(allTools, rt, exec, statusText, [], groupNames);
|
|
871
|
+
}));
|
|
872
|
+
}
|
|
873
|
+
function buildInspector(...groups) {
|
|
874
|
+
return Effect4.runSync(Effect4.gen(function* () {
|
|
875
|
+
const { allTools } = yield* snapshotGroups(groups);
|
|
876
|
+
const typegenTools = {};
|
|
877
|
+
for (const [key, tool] of Object.entries(allTools)) {
|
|
878
|
+
typegenTools[key] = toTypegenDescriptor(tool);
|
|
879
|
+
}
|
|
880
|
+
return generateDeclarations(typegenTools);
|
|
881
|
+
}));
|
|
882
|
+
}
|
|
883
|
+
export {
|
|
884
|
+
buildTestTool as test,
|
|
885
|
+
buildInspector as inspect,
|
|
886
|
+
buildTestTool,
|
|
887
|
+
buildInspector
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
//# debugId=5529472230DF2A5864756E2164756E21
|