linkshell-cli 0.3.27 → 0.3.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linkshell-cli",
3
- "version": "0.3.27",
3
+ "version": "0.3.28",
4
4
  "description": "Remote terminal bridge for Claude Code / Codex / Gemini / Copilot — control from your phone",
5
5
  "type": "module",
6
6
  "main": "dist/cli/src/index.js",
@@ -29,13 +29,6 @@
29
29
  "type": "git",
30
30
  "url": "git+https://github.com/LiuTianjie/LinkShell.git"
31
31
  },
32
- "scripts": {
33
- "build": "tsc -p tsconfig.json",
34
- "postinstall": "node scripts/postinstall.mjs",
35
- "typecheck": "tsc -p tsconfig.json --noEmit",
36
- "dev": "tsx src/index.ts",
37
- "test": "vitest run"
38
- },
39
32
  "dependencies": {
40
33
  "@linkshell/gateway": "0.3.9",
41
34
  "@linkshell/protocol": "0.3.11",
@@ -53,5 +46,12 @@
53
46
  "@types/ws": "^8.5.14",
54
47
  "tsx": "^4.19.3",
55
48
  "vitest": "^3.2.1"
49
+ },
50
+ "scripts": {
51
+ "build": "tsc -p tsconfig.json",
52
+ "postinstall": "node scripts/postinstall.mjs",
53
+ "typecheck": "tsc -p tsconfig.json --noEmit",
54
+ "dev": "tsx src/index.ts",
55
+ "test": "vitest run"
56
56
  }
57
- }
57
+ }
@@ -186,7 +186,6 @@ export class AcpClient {
186
186
  clientInfo: { name: "LinkShell", version: "0.1" },
187
187
  capabilities: { experimentalApi: true, optOutNotificationMethods: [] },
188
188
  });
189
- this.transport.notify("initialized", {});
190
189
  return result;
191
190
  }
192
191
  return this.transport.request("initialize", {
@@ -1514,6 +1514,8 @@ export class AgentWorkspaceProxy {
1514
1514
  private providerCapabilityErrors = new Map<AgentProvider, string>();
1515
1515
  private capabilitiesRevision = 0;
1516
1516
  private initialized = false;
1517
+ private initializePromise: Promise<void> | undefined;
1518
+ private providerStartPromises = new Map<AgentProvider, Promise<AcpClient | ClaudeSdkClient | ClaudeStreamJsonClient | undefined>>();
1517
1519
  private status: AgentStatus = "unavailable";
1518
1520
  private error: string | undefined;
1519
1521
  private activeConversationId: string | undefined;
@@ -1652,6 +1654,8 @@ export class AgentWorkspaceProxy {
1652
1654
  client.stop();
1653
1655
  }
1654
1656
  this.clients.clear();
1657
+ this.providerStartPromises.clear();
1658
+ this.initializePromise = undefined;
1655
1659
  }
1656
1660
 
1657
1661
  private clientForProvider(provider: AgentProvider): AcpClient | ClaudeSdkClient | ClaudeStreamJsonClient | undefined {
@@ -1675,21 +1679,37 @@ export class AgentWorkspaceProxy {
1675
1679
 
1676
1680
  private async initialize(): Promise<void> {
1677
1681
  if (this.initialized) return;
1678
- this.initialized = true;
1679
- // Eagerly start all detected providers so capabilities report real status
1680
- const startPromises = this.input.availableProviders
1681
- .filter((provider) => provider !== "codex")
1682
- .map((p) => this.ensureProviderClient(p));
1683
- await Promise.allSettled(startPromises);
1684
- this.status = "idle";
1685
- this.error = undefined;
1686
- this.sendCapabilities();
1682
+ if (this.initializePromise) return this.initializePromise;
1683
+ this.initializePromise = (async () => {
1684
+ // Eagerly start all detected providers so capabilities report real status.
1685
+ const startPromises = this.input.availableProviders
1686
+ .filter((provider) => provider !== "codex")
1687
+ .map((p) => this.ensureProviderClient(p));
1688
+ await Promise.allSettled(startPromises);
1689
+ this.initialized = true;
1690
+ this.status = "idle";
1691
+ this.error = undefined;
1692
+ this.sendCapabilities();
1693
+ })().finally(() => {
1694
+ this.initializePromise = undefined;
1695
+ });
1696
+ return this.initializePromise;
1687
1697
  }
1688
1698
 
1689
1699
  private async ensureProviderClient(provider: AgentProvider): Promise<AcpClient | ClaudeSdkClient | ClaudeStreamJsonClient | undefined> {
1690
1700
  const existing = this.clients.get(provider);
1691
1701
  if (existing) return existing;
1702
+ const starting = this.providerStartPromises.get(provider);
1703
+ if (starting) return starting;
1704
+
1705
+ const startPromise = this.startProviderClient(provider).finally(() => {
1706
+ this.providerStartPromises.delete(provider);
1707
+ });
1708
+ this.providerStartPromises.set(provider, startPromise);
1709
+ return startPromise;
1710
+ }
1692
1711
 
1712
+ private async startProviderClient(provider: AgentProvider): Promise<AcpClient | ClaudeSdkClient | ClaudeStreamJsonClient | undefined> {
1693
1713
  const resolved = resolveAgentCommand({
1694
1714
  provider,
1695
1715
  command: this.input.command,
@@ -29,8 +29,14 @@ export class CodexRpcBridge {
29
29
  ) {}
30
30
 
31
31
  send(message: unknown): void {
32
- this.ensureTransport();
33
32
  const jsonMessage = message as JsonRpcMessage;
33
+ if (isJsonRpcNotification(jsonMessage) && jsonMessage.method === "initialized") {
34
+ if (this.input.verbose) {
35
+ process.stderr.write("[agent:codex-rpc] dropped initialized notification for Codex app-server\n");
36
+ }
37
+ return;
38
+ }
39
+ this.ensureTransport();
34
40
  if (!this.transportReady) {
35
41
  this.queuedMessages.push(jsonMessage);
36
42
  return;
@@ -219,3 +225,9 @@ function isJsonRpcResponse(message: JsonRpcMessage): message is JsonRpcMessage &
219
225
  (typeof message.id === "string" || typeof message.id === "number") &&
220
226
  ("result" in message || "error" in message);
221
227
  }
228
+
229
+ function isJsonRpcNotification(message: JsonRpcMessage): message is JsonRpcMessage & { method: string } {
230
+ return !("id" in message) &&
231
+ "method" in message &&
232
+ typeof message.method === "string";
233
+ }