@tetrixdev/ai-bridge 0.1.0 → 0.1.2
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 +81 -39
- package/dist/cli.js.map +1 -1
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
+
import { realpathSync } from "fs";
|
|
4
5
|
import { fileURLToPath } from "url";
|
|
5
6
|
import { Command } from "commander";
|
|
6
7
|
|
|
@@ -11,7 +12,7 @@ import WebSocket from "ws";
|
|
|
11
12
|
|
|
12
13
|
// src/protocol/version.ts
|
|
13
14
|
var PROTOCOL_VERSION = "0.1";
|
|
14
|
-
var BRIDGE_VERSION = "0.1.
|
|
15
|
+
var BRIDGE_VERSION = "0.1.2";
|
|
15
16
|
|
|
16
17
|
// src/tools/manager.ts
|
|
17
18
|
import fs from "fs";
|
|
@@ -276,26 +277,29 @@ var ToolManager = class {
|
|
|
276
277
|
|
|
277
278
|
set -euo pipefail
|
|
278
279
|
|
|
279
|
-
# Read
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
# Read the JSON payload from the first CLI argument when present; otherwise
|
|
281
|
+
# fall back to stdin (some AI CLIs pass tool args one way, some the other).
|
|
282
|
+
PAYLOAD_DATA=""
|
|
283
|
+
if [ "$#" -ge 1 ] && [ -n "\${1:-}" ]; then
|
|
284
|
+
PAYLOAD_DATA="$1"
|
|
285
|
+
elif [ ! -t 0 ]; then
|
|
286
|
+
PAYLOAD_DATA=$(cat)
|
|
283
287
|
fi
|
|
284
288
|
|
|
285
289
|
# Single Node.js invocation for input parsing, payload building, HTTP call,
|
|
286
290
|
# and output extraction.
|
|
287
291
|
node -e '
|
|
288
292
|
const http = require("http");
|
|
289
|
-
const
|
|
293
|
+
const payloadData = process.argv[1];
|
|
290
294
|
const toolName = process.argv[2];
|
|
291
295
|
const toolCallId = process.argv[3];
|
|
292
296
|
const requestId = process.argv[4];
|
|
293
297
|
const secret = process.argv[5];
|
|
294
298
|
|
|
295
|
-
// Parse
|
|
299
|
+
// Parse the payload as JSON, fall back to wrapping as {input: ...}
|
|
296
300
|
let args = {};
|
|
297
|
-
if (
|
|
298
|
-
try { args = JSON.parse(
|
|
301
|
+
if (payloadData) {
|
|
302
|
+
try { args = JSON.parse(payloadData); } catch { args = { input: payloadData }; }
|
|
299
303
|
}
|
|
300
304
|
|
|
301
305
|
const payload = JSON.stringify({
|
|
@@ -327,7 +331,7 @@ req.write(payload);
|
|
|
327
331
|
req.end();
|
|
328
332
|
// The $RANDOM-based ID here is discarded \u2014 the callback server overrides it
|
|
329
333
|
// with a cryptographically strong UUID before use.
|
|
330
|
-
' "$
|
|
334
|
+
' "$PAYLOAD_DATA" "${tool.name}" "tc_\${RANDOM}\${RANDOM}" "\${AI_BRIDGE_REQUEST_ID:-}" "${secretArg}"
|
|
331
335
|
`;
|
|
332
336
|
}
|
|
333
337
|
};
|
|
@@ -1662,14 +1666,47 @@ function createFinalizer(opts) {
|
|
|
1662
1666
|
var ProviderAdapter = class {
|
|
1663
1667
|
};
|
|
1664
1668
|
|
|
1669
|
+
// src/tools/prompt.ts
|
|
1670
|
+
function buildToolInstructions(tools, toolScriptDir) {
|
|
1671
|
+
if (tools.length === 0) return "";
|
|
1672
|
+
const cmd = (name) => toolScriptDir ? `${toolScriptDir}/${name}` : name;
|
|
1673
|
+
const sections = [
|
|
1674
|
+
"# Available Tools",
|
|
1675
|
+
"",
|
|
1676
|
+
'You have access to the following tools. Each tool is an executable script. Call a tool by running it as a shell command with a single argument: a JSON object containing the parameters. The command prints its result to stdout. Run each tool using the exact path shown in its "Call it like" example. Use these tools whenever they help fulfill the request.'
|
|
1677
|
+
];
|
|
1678
|
+
for (const tool of tools) {
|
|
1679
|
+
sections.push("", `## ${tool.name}`, "", tool.description);
|
|
1680
|
+
const parameters = tool.parameters ?? {};
|
|
1681
|
+
const properties = parameters["properties"] ?? {};
|
|
1682
|
+
const required = Array.isArray(parameters["required"]) ? parameters["required"].map((r) => String(r)) : [];
|
|
1683
|
+
const propNames = Object.keys(properties);
|
|
1684
|
+
if (propNames.length === 0) {
|
|
1685
|
+
sections.push("", "Parameters: No parameters");
|
|
1686
|
+
sections.push("", `Call it like: ${cmd(tool.name)} '{}'`);
|
|
1687
|
+
continue;
|
|
1688
|
+
}
|
|
1689
|
+
sections.push("", "Parameters:");
|
|
1690
|
+
for (const pname of propNames) {
|
|
1691
|
+
const raw = properties[pname];
|
|
1692
|
+
const prop = raw && typeof raw === "object" ? raw : {};
|
|
1693
|
+
const type = typeof prop.type === "string" ? prop.type : "any";
|
|
1694
|
+
const requiredLabel = required.includes(pname) ? "required" : "optional";
|
|
1695
|
+
const description = typeof prop.description === "string" && prop.description.length > 0 ? prop.description : "No description";
|
|
1696
|
+
sections.push(`- ${pname} (${type}, ${requiredLabel}): ${description}`);
|
|
1697
|
+
}
|
|
1698
|
+
const exampleParam = propNames[0];
|
|
1699
|
+
sections.push(
|
|
1700
|
+
"",
|
|
1701
|
+
`Call it like: ${cmd(tool.name)} '{"${exampleParam}":"<value>"}'`
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
return sections.join("\n");
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1665
1707
|
// src/providers/codex.ts
|
|
1666
1708
|
var log7 = createLogger("CodexAdapter");
|
|
1667
1709
|
var DEFAULT_MODEL = "gpt-5.3-codex";
|
|
1668
|
-
function buildToolPromptNote(tools) {
|
|
1669
|
-
if (tools.length === 0) return "";
|
|
1670
|
-
const lines = tools.map((t) => `- \`${t.name}\`: ${t.description}`);
|
|
1671
|
-
return "\n\n---\nThe following bridge tools are available to you as shell commands. Run a tool by executing its command name in the shell (passing any arguments it documents); the command performs the action and prints the result. Use them when they help answer the request:\n" + lines.join("\n");
|
|
1672
|
-
}
|
|
1673
1710
|
var CodexAdapter = class extends ProviderAdapter {
|
|
1674
1711
|
providerName = "codex";
|
|
1675
1712
|
async execute(context, onEvent) {
|
|
@@ -1704,14 +1741,12 @@ var CodexAdapter = class extends ProviderAdapter {
|
|
|
1704
1741
|
if (hasTools) {
|
|
1705
1742
|
args.push(
|
|
1706
1743
|
"-s",
|
|
1707
|
-
"
|
|
1708
|
-
"-c",
|
|
1709
|
-
"sandbox_workspace_write.network_access=true",
|
|
1744
|
+
"danger-full-access",
|
|
1710
1745
|
"-c",
|
|
1711
1746
|
"approval_policy=never"
|
|
1712
1747
|
);
|
|
1713
1748
|
}
|
|
1714
|
-
const toolNote = hasTools ?
|
|
1749
|
+
const toolNote = hasTools ? "\n\n" + buildToolInstructions(context.tools, context.toolScriptDir) : "";
|
|
1715
1750
|
if (!cliSessionId && request.system_prompt) {
|
|
1716
1751
|
args.push("--", buildCombinedPrompt(request.system_prompt, userMessage) + toolNote);
|
|
1717
1752
|
} else if (toolNote) {
|
|
@@ -1963,9 +1998,13 @@ var ClaudeAdapter = class extends ProviderAdapter {
|
|
|
1963
1998
|
args.push("--max-tokens", String(request.options.max_tokens));
|
|
1964
1999
|
}
|
|
1965
2000
|
if (context.tools.length > 0 && context.toolScriptDir) {
|
|
1966
|
-
args.push("--
|
|
2001
|
+
args.push("--permission-mode", "bypassPermissions");
|
|
2002
|
+
}
|
|
2003
|
+
let promptArg = userMessage;
|
|
2004
|
+
if (context.tools.length > 0) {
|
|
2005
|
+
promptArg += "\n\n" + buildToolInstructions(context.tools, context.toolScriptDir);
|
|
1967
2006
|
}
|
|
1968
|
-
args.push(
|
|
2007
|
+
args.push(promptArg);
|
|
1969
2008
|
if (isDebugEnabled()) {
|
|
1970
2009
|
log8.debug("Spawning claude", { args: args.map((a) => a.length > 50 ? a.substring(0, 50) + "..." : a) });
|
|
1971
2010
|
}
|
|
@@ -2126,13 +2165,8 @@ var ClaudeAdapter = class extends ProviderAdapter {
|
|
|
2126
2165
|
return;
|
|
2127
2166
|
}
|
|
2128
2167
|
if (type === "rate_limit_event") {
|
|
2129
|
-
log8.
|
|
2130
|
-
|
|
2131
|
-
event: "error",
|
|
2132
|
-
data: {
|
|
2133
|
-
code: "rate_limited",
|
|
2134
|
-
message: "Claude is currently rate-limited. The request may retry automatically, or you may need to try again in a moment."
|
|
2135
|
-
}
|
|
2168
|
+
log8.debug("Claude rate limit event (informational)", {
|
|
2169
|
+
status: parsed["rate_limit_info"]?.["status"]
|
|
2136
2170
|
});
|
|
2137
2171
|
return;
|
|
2138
2172
|
}
|
|
@@ -2191,6 +2225,10 @@ var GeminiAdapter = class extends ProviderAdapter {
|
|
|
2191
2225
|
if (request.system_prompt && !cliSessionId) {
|
|
2192
2226
|
prompt = buildCombinedPrompt(request.system_prompt, userMessage);
|
|
2193
2227
|
}
|
|
2228
|
+
const hasTools = context.tools.length > 0;
|
|
2229
|
+
if (hasTools) {
|
|
2230
|
+
prompt += "\n\n" + buildToolInstructions(context.tools, context.toolScriptDir);
|
|
2231
|
+
}
|
|
2194
2232
|
const args = [
|
|
2195
2233
|
"--prompt",
|
|
2196
2234
|
prompt,
|
|
@@ -2201,6 +2239,9 @@ var GeminiAdapter = class extends ProviderAdapter {
|
|
|
2201
2239
|
"--skip-trust"
|
|
2202
2240
|
// Required for headless/non-interactive mode
|
|
2203
2241
|
];
|
|
2242
|
+
if (hasTools) {
|
|
2243
|
+
args.push("--yolo");
|
|
2244
|
+
}
|
|
2204
2245
|
if (cliSessionId) {
|
|
2205
2246
|
args.push("--resume", cliSessionId);
|
|
2206
2247
|
log9.debug("Resuming session", { cliSessionId });
|
|
@@ -2384,16 +2425,6 @@ var GeminiAdapter = class extends ProviderAdapter {
|
|
|
2384
2425
|
});
|
|
2385
2426
|
onEvent({ event: "done", data: {} });
|
|
2386
2427
|
settled = true;
|
|
2387
|
-
} else if (severity === "warning") {
|
|
2388
|
-
const lowerMsg = (message ?? "").toLowerCase();
|
|
2389
|
-
const isRateLimit = lowerMsg.includes("rate limit") || lowerMsg.includes("ratelimit") || lowerMsg.includes("quota") || lowerMsg.includes("429") || lowerMsg.includes("too many requests");
|
|
2390
|
-
onEvent({
|
|
2391
|
-
event: "error",
|
|
2392
|
-
data: {
|
|
2393
|
-
code: isRateLimit ? "rate_limited" : "provider_warning",
|
|
2394
|
-
message: message ?? "Gemini warning"
|
|
2395
|
-
}
|
|
2396
|
-
});
|
|
2397
2428
|
}
|
|
2398
2429
|
return;
|
|
2399
2430
|
}
|
|
@@ -2677,10 +2708,21 @@ program.name("ai-bridge").description("Local CLI bridge for AI web apps \u2014 c
|
|
|
2677
2708
|
});
|
|
2678
2709
|
bridge.connect();
|
|
2679
2710
|
});
|
|
2680
|
-
|
|
2711
|
+
function isMainModule(argv1, moduleUrl) {
|
|
2712
|
+
if (!argv1) return false;
|
|
2713
|
+
try {
|
|
2714
|
+
return realpathSync(argv1) === realpathSync(fileURLToPath(moduleUrl));
|
|
2715
|
+
} catch {
|
|
2716
|
+
return false;
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
if (isMainModule(process.argv[1], import.meta.url)) {
|
|
2681
2720
|
program.parseAsync(process.argv).catch((err) => {
|
|
2682
2721
|
log11.error("Fatal error", { error: err instanceof Error ? err.message : String(err) });
|
|
2683
2722
|
process.exit(1);
|
|
2684
2723
|
});
|
|
2685
2724
|
}
|
|
2725
|
+
export {
|
|
2726
|
+
isMainModule
|
|
2727
|
+
};
|
|
2686
2728
|
//# sourceMappingURL=cli.js.map
|