@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 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.0";
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 arguments from stdin (the standard way AI CLIs pass tool args).
280
- STDIN_DATA=""
281
- if [ ! -t 0 ]; then
282
- STDIN_DATA=$(cat)
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 stdinData = process.argv[1];
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 stdin as JSON, fall back to wrapping as {input: ...}
299
+ // Parse the payload as JSON, fall back to wrapping as {input: ...}
296
300
  let args = {};
297
- if (stdinData) {
298
- try { args = JSON.parse(stdinData); } catch { args = { input: stdinData }; }
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
- ' "$STDIN_DATA" "${tool.name}" "tc_\${RANDOM}\${RANDOM}" "\${AI_BRIDGE_REQUEST_ID:-}" "${secretArg}"
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
- "workspace-write",
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 ? buildToolPromptNote(context.tools) : "";
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("--allowedTools=bash");
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(userMessage);
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.warn("Claude rate limit event", { type });
2130
- onEvent({
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
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
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