@samrahimi/smol-js 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +31 -27
- package/dist/cli.js.map +1 -1
- package/dist/index.d.mts +20 -14
- package/dist/index.d.ts +20 -14
- package/dist/index.js +28 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +28 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/toolHarness.ts +73 -0
package/dist/index.d.mts
CHANGED
|
@@ -1191,27 +1191,31 @@ declare class ExaResearchTool extends Tool {
|
|
|
1191
1191
|
|
|
1192
1192
|
/**
|
|
1193
1193
|
* ProxyTool - Bridges the smol-js agent runtime to an external standalone tool
|
|
1194
|
-
* executed in an isolated Bun process.
|
|
1194
|
+
* executed in an isolated Bun process via the toolHarness adapter.
|
|
1195
1195
|
*
|
|
1196
|
-
* The tool file is never imported into the main Node.js process.
|
|
1197
|
-
*
|
|
1198
|
-
* via a lightweight stdin/stdout JSON protocol.
|
|
1196
|
+
* The tool file is never imported into the main Node.js process. When an agent
|
|
1197
|
+
* invokes this proxy, it spawns:
|
|
1199
1198
|
*
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1199
|
+
* bun run <toolHarness.ts> <toolPath> <argsJson>
|
|
1200
|
+
*
|
|
1201
|
+
* The harness is the ONLY place that speaks the stdout protocol. The tool
|
|
1202
|
+
* itself simply exports { TOOL_METADATA, execute } and knows nothing about
|
|
1203
|
+
* how it is being called.
|
|
1204
|
+
*
|
|
1205
|
+
* Protocol (stdout of harness/child):
|
|
1202
1206
|
* - A line prefixed with `[TOOL_RESULT]` contains the JSON-serialized return value.
|
|
1203
1207
|
* - A line prefixed with `[TOOL_ERROR]` means the tool threw; payload is the message.
|
|
1204
|
-
* - Any other stdout line
|
|
1208
|
+
* - Any other stdout line (e.g. console.log inside execute()) is streaming output.
|
|
1205
1209
|
*
|
|
1206
|
-
* Extensibility
|
|
1207
|
-
*
|
|
1208
|
-
*
|
|
1210
|
+
* Extensibility: The spawn+stdio transport is fully encapsulated here. A future
|
|
1211
|
+
* variant (HTTP, gRPC, IPC) only needs to replace execute() — the tool file and
|
|
1212
|
+
* YAML definition stay identical.
|
|
1209
1213
|
*/
|
|
1210
1214
|
|
|
1211
1215
|
interface ProxyToolConfig {
|
|
1212
1216
|
/** Absolute path to the .ts or .js tool file */
|
|
1213
1217
|
toolPath: string;
|
|
1214
|
-
/** Tool name (must match the
|
|
1218
|
+
/** Tool name (must match the file's base name) */
|
|
1215
1219
|
name: string;
|
|
1216
1220
|
/** Human-readable description exposed to agents */
|
|
1217
1221
|
description: string;
|
|
@@ -1230,14 +1234,16 @@ declare class ProxyTool extends Tool {
|
|
|
1230
1234
|
private readonly toolPath;
|
|
1231
1235
|
private readonly timeout;
|
|
1232
1236
|
private bunPath;
|
|
1237
|
+
private harnessPath;
|
|
1233
1238
|
constructor(config: ProxyToolConfig);
|
|
1234
1239
|
/**
|
|
1235
|
-
* Ensure Bun is available before first invocation.
|
|
1240
|
+
* Ensure Bun is available and locate the harness before first invocation.
|
|
1236
1241
|
*/
|
|
1237
1242
|
setup(): Promise<void>;
|
|
1238
1243
|
/**
|
|
1239
|
-
* Spawn the
|
|
1240
|
-
*
|
|
1244
|
+
* Spawn the harness in a Bun child process. The harness imports the tool,
|
|
1245
|
+
* calls execute(args), and writes the protocol lines. Any console.log from
|
|
1246
|
+
* the tool flows through stdout as plain lines.
|
|
1241
1247
|
*/
|
|
1242
1248
|
execute(args: Record<string, unknown>): Promise<unknown>;
|
|
1243
1249
|
private processLine;
|
package/dist/index.d.ts
CHANGED
|
@@ -1191,27 +1191,31 @@ declare class ExaResearchTool extends Tool {
|
|
|
1191
1191
|
|
|
1192
1192
|
/**
|
|
1193
1193
|
* ProxyTool - Bridges the smol-js agent runtime to an external standalone tool
|
|
1194
|
-
* executed in an isolated Bun process.
|
|
1194
|
+
* executed in an isolated Bun process via the toolHarness adapter.
|
|
1195
1195
|
*
|
|
1196
|
-
* The tool file is never imported into the main Node.js process.
|
|
1197
|
-
*
|
|
1198
|
-
* via a lightweight stdin/stdout JSON protocol.
|
|
1196
|
+
* The tool file is never imported into the main Node.js process. When an agent
|
|
1197
|
+
* invokes this proxy, it spawns:
|
|
1199
1198
|
*
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1199
|
+
* bun run <toolHarness.ts> <toolPath> <argsJson>
|
|
1200
|
+
*
|
|
1201
|
+
* The harness is the ONLY place that speaks the stdout protocol. The tool
|
|
1202
|
+
* itself simply exports { TOOL_METADATA, execute } and knows nothing about
|
|
1203
|
+
* how it is being called.
|
|
1204
|
+
*
|
|
1205
|
+
* Protocol (stdout of harness/child):
|
|
1202
1206
|
* - A line prefixed with `[TOOL_RESULT]` contains the JSON-serialized return value.
|
|
1203
1207
|
* - A line prefixed with `[TOOL_ERROR]` means the tool threw; payload is the message.
|
|
1204
|
-
* - Any other stdout line
|
|
1208
|
+
* - Any other stdout line (e.g. console.log inside execute()) is streaming output.
|
|
1205
1209
|
*
|
|
1206
|
-
* Extensibility
|
|
1207
|
-
*
|
|
1208
|
-
*
|
|
1210
|
+
* Extensibility: The spawn+stdio transport is fully encapsulated here. A future
|
|
1211
|
+
* variant (HTTP, gRPC, IPC) only needs to replace execute() — the tool file and
|
|
1212
|
+
* YAML definition stay identical.
|
|
1209
1213
|
*/
|
|
1210
1214
|
|
|
1211
1215
|
interface ProxyToolConfig {
|
|
1212
1216
|
/** Absolute path to the .ts or .js tool file */
|
|
1213
1217
|
toolPath: string;
|
|
1214
|
-
/** Tool name (must match the
|
|
1218
|
+
/** Tool name (must match the file's base name) */
|
|
1215
1219
|
name: string;
|
|
1216
1220
|
/** Human-readable description exposed to agents */
|
|
1217
1221
|
description: string;
|
|
@@ -1230,14 +1234,16 @@ declare class ProxyTool extends Tool {
|
|
|
1230
1234
|
private readonly toolPath;
|
|
1231
1235
|
private readonly timeout;
|
|
1232
1236
|
private bunPath;
|
|
1237
|
+
private harnessPath;
|
|
1233
1238
|
constructor(config: ProxyToolConfig);
|
|
1234
1239
|
/**
|
|
1235
|
-
* Ensure Bun is available before first invocation.
|
|
1240
|
+
* Ensure Bun is available and locate the harness before first invocation.
|
|
1236
1241
|
*/
|
|
1237
1242
|
setup(): Promise<void>;
|
|
1238
1243
|
/**
|
|
1239
|
-
* Spawn the
|
|
1240
|
-
*
|
|
1244
|
+
* Spawn the harness in a Bun child process. The harness imports the tool,
|
|
1245
|
+
* calls execute(args), and writes the protocol lines. Any console.log from
|
|
1246
|
+
* the tool flows through stdout as plain lines.
|
|
1241
1247
|
*/
|
|
1242
1248
|
execute(args: Record<string, unknown>): Promise<unknown>;
|
|
1243
1249
|
private processLine;
|
package/dist/index.js
CHANGED
|
@@ -792,7 +792,7 @@ Total time: ${(duration / 1e3).toFixed(2)}s`);
|
|
|
792
792
|
}
|
|
793
793
|
/** Sleep for a specified duration */
|
|
794
794
|
sleep(ms) {
|
|
795
|
-
return new Promise((
|
|
795
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
796
796
|
}
|
|
797
797
|
};
|
|
798
798
|
|
|
@@ -1461,12 +1461,12 @@ var UserInputTool = class extends Tool {
|
|
|
1461
1461
|
input: process.stdin,
|
|
1462
1462
|
output: process.stdout
|
|
1463
1463
|
});
|
|
1464
|
-
return new Promise((
|
|
1464
|
+
return new Promise((resolve7) => {
|
|
1465
1465
|
rl.question(`
|
|
1466
1466
|
[Agent asks]: ${question}
|
|
1467
1467
|
Your response: `, (answer) => {
|
|
1468
1468
|
rl.close();
|
|
1469
|
-
|
|
1469
|
+
resolve7(answer);
|
|
1470
1470
|
});
|
|
1471
1471
|
});
|
|
1472
1472
|
}
|
|
@@ -2795,7 +2795,7 @@ var ExaResearchTool = class extends Tool {
|
|
|
2795
2795
|
while (Date.now() - startTime < this.maxPollTime) {
|
|
2796
2796
|
attempts++;
|
|
2797
2797
|
if (attempts > 1) {
|
|
2798
|
-
await new Promise((
|
|
2798
|
+
await new Promise((resolve7) => setTimeout(resolve7, this.pollInterval));
|
|
2799
2799
|
}
|
|
2800
2800
|
const statusResponse = await fetch(`https://api.exa.ai/research/v1/${researchId}`, {
|
|
2801
2801
|
method: "GET",
|
|
@@ -2848,6 +2848,7 @@ var ExaResearchTool = class extends Tool {
|
|
|
2848
2848
|
|
|
2849
2849
|
// src/tools/ProxyTool.ts
|
|
2850
2850
|
var import_child_process2 = require("child_process");
|
|
2851
|
+
var path6 = __toESM(require("path"));
|
|
2851
2852
|
|
|
2852
2853
|
// src/utils/bunInstaller.ts
|
|
2853
2854
|
var import_child_process = require("child_process");
|
|
@@ -2906,10 +2907,12 @@ function whichBun() {
|
|
|
2906
2907
|
}
|
|
2907
2908
|
|
|
2908
2909
|
// src/tools/ProxyTool.ts
|
|
2909
|
-
var TOOL_OUTPUT_PREFIX = "[TOOL_OUTPUT]";
|
|
2910
2910
|
var TOOL_RESULT_PREFIX = "[TOOL_RESULT]";
|
|
2911
2911
|
var TOOL_ERROR_PREFIX = "[TOOL_ERROR]";
|
|
2912
2912
|
var DEFAULT_TOOL_TIMEOUT_MS = 6e4;
|
|
2913
|
+
function resolveHarnessPath() {
|
|
2914
|
+
return path6.resolve(__dirname, "..", "toolHarness.ts");
|
|
2915
|
+
}
|
|
2913
2916
|
var ProxyTool = class extends Tool {
|
|
2914
2917
|
name;
|
|
2915
2918
|
description;
|
|
@@ -2918,6 +2921,7 @@ var ProxyTool = class extends Tool {
|
|
|
2918
2921
|
toolPath;
|
|
2919
2922
|
timeout;
|
|
2920
2923
|
bunPath = null;
|
|
2924
|
+
harnessPath = null;
|
|
2921
2925
|
constructor(config) {
|
|
2922
2926
|
super();
|
|
2923
2927
|
this.name = config.name;
|
|
@@ -2928,23 +2932,25 @@ var ProxyTool = class extends Tool {
|
|
|
2928
2932
|
this.timeout = config.timeout ?? DEFAULT_TOOL_TIMEOUT_MS;
|
|
2929
2933
|
}
|
|
2930
2934
|
/**
|
|
2931
|
-
* Ensure Bun is available before first invocation.
|
|
2935
|
+
* Ensure Bun is available and locate the harness before first invocation.
|
|
2932
2936
|
*/
|
|
2933
2937
|
async setup() {
|
|
2934
2938
|
this.bunPath = await ensureBunAvailable();
|
|
2939
|
+
this.harnessPath = resolveHarnessPath();
|
|
2935
2940
|
this.isSetup = true;
|
|
2936
2941
|
}
|
|
2937
2942
|
/**
|
|
2938
|
-
* Spawn the
|
|
2939
|
-
*
|
|
2943
|
+
* Spawn the harness in a Bun child process. The harness imports the tool,
|
|
2944
|
+
* calls execute(args), and writes the protocol lines. Any console.log from
|
|
2945
|
+
* the tool flows through stdout as plain lines.
|
|
2940
2946
|
*/
|
|
2941
2947
|
async execute(args) {
|
|
2942
|
-
if (!this.bunPath) {
|
|
2948
|
+
if (!this.bunPath || !this.harnessPath) {
|
|
2943
2949
|
await this.setup();
|
|
2944
2950
|
}
|
|
2945
2951
|
const serializedArgs = JSON.stringify(args);
|
|
2946
|
-
return new Promise((
|
|
2947
|
-
const child = (0, import_child_process2.spawn)(this.bunPath, ["run", this.toolPath, serializedArgs], {
|
|
2952
|
+
return new Promise((resolve7, reject) => {
|
|
2953
|
+
const child = (0, import_child_process2.spawn)(this.bunPath, ["run", this.harnessPath, this.toolPath, serializedArgs], {
|
|
2948
2954
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2949
2955
|
env: { ...process.env }
|
|
2950
2956
|
});
|
|
@@ -3009,12 +3015,12 @@ ${logBuffer.join("\n")}
|
|
|
3009
3015
|
[Tool result]
|
|
3010
3016
|
`;
|
|
3011
3017
|
if (typeof result === "string") {
|
|
3012
|
-
|
|
3018
|
+
resolve7(logPrefix + result);
|
|
3013
3019
|
} else {
|
|
3014
|
-
|
|
3020
|
+
resolve7({ logs: logBuffer.join("\n"), result });
|
|
3015
3021
|
}
|
|
3016
3022
|
} else {
|
|
3017
|
-
|
|
3023
|
+
resolve7(result);
|
|
3018
3024
|
}
|
|
3019
3025
|
return;
|
|
3020
3026
|
}
|
|
@@ -3024,7 +3030,7 @@ ${logBuffer.join("\n")}
|
|
|
3024
3030
|
`Custom tool "${this.name}" exited with code ${code}. Output: ${combined || "(none)"}`
|
|
3025
3031
|
));
|
|
3026
3032
|
} else {
|
|
3027
|
-
|
|
3033
|
+
resolve7(combined || `Tool "${this.name}" produced no output.`);
|
|
3028
3034
|
}
|
|
3029
3035
|
});
|
|
3030
3036
|
child.on("error", (err) => {
|
|
@@ -3035,7 +3041,7 @@ ${logBuffer.join("\n")}
|
|
|
3035
3041
|
});
|
|
3036
3042
|
});
|
|
3037
3043
|
}
|
|
3038
|
-
// ---
|
|
3044
|
+
// --- line parser: protocol is spoken by harness, interpreted here ---
|
|
3039
3045
|
processLine(line, handlers) {
|
|
3040
3046
|
const trimmed = line.trimEnd();
|
|
3041
3047
|
if (!trimmed) return;
|
|
@@ -3048,8 +3054,6 @@ ${logBuffer.join("\n")}
|
|
|
3048
3054
|
}
|
|
3049
3055
|
} else if (trimmed.startsWith(TOOL_ERROR_PREFIX)) {
|
|
3050
3056
|
handlers.onError(trimmed.slice(TOOL_ERROR_PREFIX.length).trim());
|
|
3051
|
-
} else if (trimmed.startsWith(TOOL_OUTPUT_PREFIX)) {
|
|
3052
|
-
handlers.onOutput(trimmed.slice(TOOL_OUTPUT_PREFIX.length).trim());
|
|
3053
3057
|
} else {
|
|
3054
3058
|
handlers.onOutput(trimmed);
|
|
3055
3059
|
}
|
|
@@ -3058,7 +3062,7 @@ ${logBuffer.join("\n")}
|
|
|
3058
3062
|
|
|
3059
3063
|
// src/tools/CustomToolScanner.ts
|
|
3060
3064
|
var fs6 = __toESM(require("fs"));
|
|
3061
|
-
var
|
|
3065
|
+
var path7 = __toESM(require("path"));
|
|
3062
3066
|
var METADATA_REGEX = /export\s+const\s+TOOL_METADATA\s*=\s*(\{[\s\S]*?\});\s*$/m;
|
|
3063
3067
|
function scanCustomTools(folderPath) {
|
|
3064
3068
|
if (!fs6.existsSync(folderPath)) {
|
|
@@ -3070,10 +3074,10 @@ function scanCustomTools(folderPath) {
|
|
|
3070
3074
|
const discovered = [];
|
|
3071
3075
|
for (const entry of entries) {
|
|
3072
3076
|
if (entry.isDirectory()) continue;
|
|
3073
|
-
const ext =
|
|
3077
|
+
const ext = path7.extname(entry.name).toLowerCase();
|
|
3074
3078
|
if (ext !== ".ts" && ext !== ".js") continue;
|
|
3075
|
-
const filePath =
|
|
3076
|
-
const baseName =
|
|
3079
|
+
const filePath = path7.resolve(folderPath, entry.name);
|
|
3080
|
+
const baseName = path7.basename(entry.name, ext);
|
|
3077
3081
|
let metadata;
|
|
3078
3082
|
try {
|
|
3079
3083
|
metadata = extractMetadata(filePath);
|
|
@@ -3141,7 +3145,7 @@ function loadCustomTools(folderPath) {
|
|
|
3141
3145
|
|
|
3142
3146
|
// src/orchestrator/YAMLLoader.ts
|
|
3143
3147
|
var fs7 = __toESM(require("fs"));
|
|
3144
|
-
var
|
|
3148
|
+
var path8 = __toESM(require("path"));
|
|
3145
3149
|
var import_yaml = __toESM(require("yaml"));
|
|
3146
3150
|
var TOOL_REGISTRY = {
|
|
3147
3151
|
read_file: ReadFileTool,
|
|
@@ -3173,7 +3177,7 @@ var YAMLLoader = class {
|
|
|
3173
3177
|
* Load a workflow from a YAML file path.
|
|
3174
3178
|
*/
|
|
3175
3179
|
loadFromFile(filePath) {
|
|
3176
|
-
const absolutePath =
|
|
3180
|
+
const absolutePath = path8.isAbsolute(filePath) ? filePath : path8.resolve(process.cwd(), filePath);
|
|
3177
3181
|
if (!fs7.existsSync(absolutePath)) {
|
|
3178
3182
|
throw new Error(`Workflow file not found: ${absolutePath}`);
|
|
3179
3183
|
}
|