linkshell-cli 0.2.88 → 0.2.90

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.
@@ -1,3 +1,5 @@
1
+ import { execSync } from "node:child_process";
2
+
1
3
  export type AgentProvider = "codex" | "claude" | "custom";
2
4
  export type AgentProtocol = "acp" | "codex-app-server";
3
5
  export type AgentFraming = "content-length" | "newline";
@@ -33,5 +35,32 @@ export function resolveAgentCommand(input: {
33
35
  };
34
36
  }
35
37
 
38
+ if (input.provider === "claude") {
39
+ return {
40
+ provider: "claude",
41
+ command: "claude --acp",
42
+ protocol: "acp",
43
+ framing: "content-length",
44
+ };
45
+ }
46
+
47
+ // custom: caller must provide --agent-command
36
48
  return null;
37
49
  }
50
+
51
+ export function detectAvailableProviders(): AgentProvider[] {
52
+ const available: AgentProvider[] = [];
53
+ const bins = [
54
+ ["claude", "claude"] as const,
55
+ ["codex", "codex"] as const,
56
+ ];
57
+ for (const [, bin] of bins) {
58
+ try {
59
+ execSync(`which ${bin}`, { stdio: "ignore" });
60
+ available.push(bin as AgentProvider);
61
+ } catch {
62
+ // not installed
63
+ }
64
+ }
65
+ return available.length > 0 ? available : [];
66
+ }
@@ -21,7 +21,7 @@ import { getLanIp } from "../utils/lan-ip.js";
21
21
  import { startKeepAwake, type KeepAwakeHandle } from "../utils/keep-awake.js";
22
22
  import { AgentSessionProxy } from "./acp/agent-session.js";
23
23
  import { AgentWorkspaceProxy } from "./acp/agent-workspace.js";
24
- import type { AgentProvider } from "./acp/provider-resolver.js";
24
+ import { detectAvailableProviders, type AgentProvider } from "./acp/provider-resolver.js";
25
25
 
26
26
  export interface BridgeSessionOptions {
27
27
  gatewayUrl: string;
@@ -330,13 +330,16 @@ export class BridgeSession {
330
330
  }
331
331
  if (this.options.agentUi) {
332
332
  process.env.LINKSHELL_ID = this.terminalHookMarker(DEFAULT_TERMINAL_ID);
333
- const agentProvider = normalizeAgentProvider(
334
- this.options.agentProvider ?? "codex",
335
- );
333
+ const availableProviders = this.options.agentProvider
334
+ ? [normalizeAgentProvider(this.options.agentProvider)]
335
+ : detectAvailableProviders();
336
+ if (availableProviders.length === 0) {
337
+ availableProviders.push("codex"); // last-resort fallback
338
+ }
336
339
  const agentOptions = {
337
340
  sessionId: this.sessionId,
338
341
  cwd: process.cwd(),
339
- provider: agentProvider,
342
+ availableProviders,
340
343
  command: this.options.agentCommand,
341
344
  verbose: this.options.verbose,
342
345
  send: (envelope: Envelope) => this.send(envelope),
@@ -347,7 +350,7 @@ export class BridgeSession {
347
350
  this.agentWorkspace = new AgentWorkspaceProxy({
348
351
  ...agentOptions,
349
352
  });
350
- process.stderr.write("[bridge] agent workspace channel enabled\n");
353
+ process.stderr.write(`[bridge] agent workspace channel enabled (providers: ${availableProviders.join(", ")})\n`);
351
354
  }
352
355
  await this.spawnTerminal(DEFAULT_TERMINAL_ID, process.cwd());
353
356
  this.connectGateway();
@@ -1274,13 +1277,15 @@ export class BridgeSession {
1274
1277
  if (!term?.hookPort) return;
1275
1278
  const marker = term.hookMarker;
1276
1279
  const curlCmd = `curl -s -X POST "http://127.0.0.1:${term.hookPort}/hook?m=${marker}&lid=$LINKSHELL_ID" -H 'Content-Type: application/json' --data-binary @-`;
1277
- const agentProvider = normalizeAgentProvider(this.options.agentProvider ?? "codex");
1280
+ const providers = this.options.agentProvider
1281
+ ? [normalizeAgentProvider(this.options.agentProvider)]
1282
+ : detectAvailableProviders();
1278
1283
  try {
1279
- if (agentProvider === "claude") {
1280
- this.setupClaudeHooks(DEFAULT_TERMINAL_ID, curlCmd, [], marker);
1281
- } else {
1282
- this.setupCodexHooks(DEFAULT_TERMINAL_ID, curlCmd, marker);
1283
- if (agentProvider === "custom") {
1284
+ for (const provider of providers) {
1285
+ if (provider === "codex") {
1286
+ this.setupCodexHooks(DEFAULT_TERMINAL_ID, curlCmd, marker);
1287
+ } else {
1288
+ // claude, custom
1284
1289
  this.setupClaudeHooks(DEFAULT_TERMINAL_ID, curlCmd, [], marker);
1285
1290
  }
1286
1291
  }