linkshell-cli 0.5.0 → 0.5.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/src/index.js +1 -1
- package/dist/cli/src/index.js.map +1 -1
- package/dist/cli/src/runtime/acp/acp-client.d.ts +16 -0
- package/dist/cli/src/runtime/acp/acp-client.js +40 -2
- package/dist/cli/src/runtime/acp/acp-client.js.map +1 -1
- package/dist/cli/src/runtime/acp/agent-workspace-git.d.ts +63 -0
- package/dist/cli/src/runtime/acp/agent-workspace-git.js +297 -0
- package/dist/cli/src/runtime/acp/agent-workspace-git.js.map +1 -0
- package/dist/cli/src/runtime/acp/agent-workspace.d.ts +6 -0
- package/dist/cli/src/runtime/acp/agent-workspace.js +728 -98
- package/dist/cli/src/runtime/acp/agent-workspace.js.map +1 -1
- package/dist/cli/src/runtime/acp/claude-sdk-client.d.ts +17 -1
- package/dist/cli/src/runtime/acp/claude-sdk-client.js +331 -19
- package/dist/cli/src/runtime/acp/claude-sdk-client.js.map +1 -1
- package/dist/cli/src/runtime/acp/claude-stream-json-client.js +69 -14
- package/dist/cli/src/runtime/acp/claude-stream-json-client.js.map +1 -1
- package/dist/cli/src/runtime/acp/json-rpc.d.ts +1 -0
- package/dist/cli/src/runtime/acp/json-rpc.js +6 -0
- package/dist/cli/src/runtime/acp/json-rpc.js.map +1 -1
- package/dist/cli/src/runtime/acp-relay.d.ts +23 -0
- package/dist/cli/src/runtime/acp-relay.js +73 -0
- package/dist/cli/src/runtime/acp-relay.js.map +1 -0
- package/dist/cli/src/runtime/bridge-session.d.ts +5 -0
- package/dist/cli/src/runtime/bridge-session.js +104 -57
- package/dist/cli/src/runtime/bridge-session.js.map +1 -1
- package/dist/cli/tsconfig.tsbuildinfo +1 -1
- package/dist/shared-protocol/src/index.d.ts +2505 -2479
- package/dist/shared-protocol/src/index.js +46 -0
- package/dist/shared-protocol/src/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -1
- package/src/runtime/acp/acp-client.ts +53 -2
- package/src/runtime/acp/agent-workspace.ts +784 -89
- package/src/runtime/acp/claude-sdk-client.ts +352 -31
- package/src/runtime/acp/claude-stream-json-client.ts +66 -16
- package/src/runtime/acp/json-rpc.ts +5 -0
- package/src/runtime/bridge-session.ts +114 -61
- package/dist/cli/src/runtime/acp/claude-sessions.d.ts +0 -57
- package/dist/cli/src/runtime/acp/claude-sessions.js +0 -506
- package/dist/cli/src/runtime/acp/claude-sessions.js.map +0 -1
- package/dist/cli/src/runtime/acp/codex-rpc-bridge.d.ts +0 -34
- package/dist/cli/src/runtime/acp/codex-rpc-bridge.js +0 -204
- package/dist/cli/src/runtime/acp/codex-rpc-bridge.js.map +0 -1
- package/dist/cli/src/runtime/acp/codex-sessions.d.ts +0 -60
- package/dist/cli/src/runtime/acp/codex-sessions.js +0 -731
- package/dist/cli/src/runtime/acp/codex-sessions.js.map +0 -1
|
@@ -6,6 +6,7 @@ import { writeFileSync, readFileSync, readdirSync, statSync, unlinkSync, mkdirSy
|
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { join, basename, resolve } from "node:path";
|
|
8
8
|
import {
|
|
9
|
+
agentV2MessageRoute,
|
|
9
10
|
createEnvelope,
|
|
10
11
|
parseEnvelope,
|
|
11
12
|
parseTypedPayload,
|
|
@@ -211,9 +212,23 @@ function hookPermissionOptions(suggestions: unknown[]): Array<{
|
|
|
211
212
|
];
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
function stripGatewayBase(url: URL): string {
|
|
216
|
+
url.hash = "";
|
|
217
|
+
url.search = "";
|
|
218
|
+
url.pathname = "";
|
|
219
|
+
return url.toString().replace(/\/+$/, "");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function isHttpGatewayUrl(url: URL): boolean {
|
|
223
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
224
|
+
}
|
|
225
|
+
|
|
214
226
|
function getPairingGatewayParam(gatewayHttpUrl: string): string | undefined {
|
|
215
227
|
try {
|
|
216
228
|
const url = new URL(gatewayHttpUrl);
|
|
229
|
+
if (!isHttpGatewayUrl(url)) {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
217
232
|
const hostname = url.hostname.trim().toLowerCase();
|
|
218
233
|
if (
|
|
219
234
|
hostname === "localhost" ||
|
|
@@ -227,15 +242,15 @@ function getPairingGatewayParam(gatewayHttpUrl: string): string | undefined {
|
|
|
227
242
|
return undefined; // No LAN interface found, can't help
|
|
228
243
|
}
|
|
229
244
|
url.hostname = lanIp;
|
|
230
|
-
return url
|
|
245
|
+
return stripGatewayBase(url);
|
|
231
246
|
}
|
|
232
|
-
return url
|
|
247
|
+
return stripGatewayBase(url);
|
|
233
248
|
} catch {
|
|
234
249
|
return gatewayHttpUrl.replace(/\/+$/, "") || undefined;
|
|
235
250
|
}
|
|
236
251
|
}
|
|
237
252
|
|
|
238
|
-
function resolvePairingGateway(
|
|
253
|
+
export function resolvePairingGateway(
|
|
239
254
|
gatewayHttpUrl: string,
|
|
240
255
|
pairingGateway?: string,
|
|
241
256
|
): string | undefined {
|
|
@@ -246,24 +261,36 @@ function resolvePairingGateway(
|
|
|
246
261
|
|
|
247
262
|
try {
|
|
248
263
|
const absoluteUrl = new URL(override);
|
|
249
|
-
|
|
264
|
+
if (isHttpGatewayUrl(absoluteUrl)) {
|
|
265
|
+
return stripGatewayBase(absoluteUrl);
|
|
266
|
+
}
|
|
250
267
|
} catch {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const normalizedHost = override
|
|
254
|
-
.replace(/^https?:\/\//i, "")
|
|
255
|
-
.replace(/\/.*$/, "")
|
|
256
|
-
.trim();
|
|
257
|
-
|
|
258
|
-
if (!normalizedHost) {
|
|
259
|
-
return getPairingGatewayParam(gatewayHttpUrl);
|
|
260
|
-
}
|
|
268
|
+
// Fall through and treat the override as a host[:port] value.
|
|
269
|
+
}
|
|
261
270
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
271
|
+
try {
|
|
272
|
+
const baseUrl = new URL(gatewayHttpUrl);
|
|
273
|
+
if (!isHttpGatewayUrl(baseUrl)) {
|
|
265
274
|
return override.replace(/\/+$/, "") || undefined;
|
|
266
275
|
}
|
|
276
|
+
const normalizedHost = override
|
|
277
|
+
.replace(/^https?:\/\//i, "")
|
|
278
|
+
.replace(/\/.*$/, "")
|
|
279
|
+
.trim();
|
|
280
|
+
|
|
281
|
+
if (!normalizedHost) {
|
|
282
|
+
return getPairingGatewayParam(gatewayHttpUrl);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
baseUrl.host = normalizedHost;
|
|
286
|
+
return stripGatewayBase(baseUrl);
|
|
287
|
+
} catch {
|
|
288
|
+
try {
|
|
289
|
+
const prefixed = new URL(`http://${override.replace(/^\/+/, "")}`);
|
|
290
|
+
return stripGatewayBase(prefixed);
|
|
291
|
+
} catch {
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
267
294
|
}
|
|
268
295
|
}
|
|
269
296
|
|
|
@@ -274,6 +301,28 @@ function normalizeAgentProvider(provider: unknown): AgentProvider {
|
|
|
274
301
|
return "codex";
|
|
275
302
|
}
|
|
276
303
|
|
|
304
|
+
export function resolveAgentWorkspaceProviders(options: {
|
|
305
|
+
agentProvider?: AgentProvider;
|
|
306
|
+
agentCommand?: string;
|
|
307
|
+
}): AgentProvider[] {
|
|
308
|
+
if (options.agentCommand?.trim()) {
|
|
309
|
+
return [normalizeAgentProvider(options.agentProvider ?? "custom")];
|
|
310
|
+
}
|
|
311
|
+
const defaultProviders: AgentProvider[] = ["codex", "claude"];
|
|
312
|
+
const detected = detectAvailableProviders();
|
|
313
|
+
const requested = options.agentProvider ? normalizeAgentProvider(options.agentProvider) : undefined;
|
|
314
|
+
const ordered = [
|
|
315
|
+
...(requested ? [requested] : []),
|
|
316
|
+
...detected,
|
|
317
|
+
...defaultProviders,
|
|
318
|
+
];
|
|
319
|
+
const unique: AgentProvider[] = [];
|
|
320
|
+
for (const provider of ordered) {
|
|
321
|
+
if (!unique.includes(provider)) unique.push(provider);
|
|
322
|
+
}
|
|
323
|
+
return unique;
|
|
324
|
+
}
|
|
325
|
+
|
|
277
326
|
export class BridgeSession {
|
|
278
327
|
private readonly options: BridgeSessionOptions;
|
|
279
328
|
private socket: WebSocket | undefined;
|
|
@@ -334,9 +383,7 @@ export class BridgeSession {
|
|
|
334
383
|
}
|
|
335
384
|
if (this.options.agentUi) {
|
|
336
385
|
process.env.LINKSHELL_ID = this.terminalHookMarker(DEFAULT_TERMINAL_ID);
|
|
337
|
-
const availableProviders = this.options
|
|
338
|
-
? [normalizeAgentProvider(this.options.agentProvider)]
|
|
339
|
-
: detectAvailableProviders();
|
|
386
|
+
const availableProviders = resolveAgentWorkspaceProviders(this.options);
|
|
340
387
|
const agentOptions = {
|
|
341
388
|
sessionId: this.sessionId,
|
|
342
389
|
cwd: process.cwd(),
|
|
@@ -516,6 +563,52 @@ export class BridgeSession {
|
|
|
516
563
|
|
|
517
564
|
private async handleMessage(envelope: Envelope): Promise<void> {
|
|
518
565
|
const tid = envelope.terminalId ?? DEFAULT_TERMINAL_ID;
|
|
566
|
+
const agentV2Route = agentV2MessageRoute(envelope.type);
|
|
567
|
+
if (agentV2Route === "client_write" || agentV2Route === "client_read") {
|
|
568
|
+
if (!this.agentWorkspace) {
|
|
569
|
+
this.send(
|
|
570
|
+
createEnvelope({
|
|
571
|
+
type: "agent.v2.capabilities",
|
|
572
|
+
sessionId: this.sessionId,
|
|
573
|
+
payload: {
|
|
574
|
+
enabled: false,
|
|
575
|
+
provider: normalizeAgentProvider(
|
|
576
|
+
this.options.agentProvider ?? "codex",
|
|
577
|
+
),
|
|
578
|
+
machineId: this.machineIdentity?.machineId,
|
|
579
|
+
workspaceProtocolVersion: 2,
|
|
580
|
+
error: "Agent Workspace is not enabled. Start CLI with --agent-ui.",
|
|
581
|
+
supportsSessionList: false,
|
|
582
|
+
supportsSessionLoad: false,
|
|
583
|
+
supportsImages: false,
|
|
584
|
+
supportsAudio: false,
|
|
585
|
+
supportsPermission: false,
|
|
586
|
+
supportsPlan: false,
|
|
587
|
+
supportsCancel: false,
|
|
588
|
+
},
|
|
589
|
+
}),
|
|
590
|
+
);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (envelope.type === "agent.v2.prompt" || envelope.type === "agent.v2.command.execute") {
|
|
594
|
+
this.refreshAgentPermissionHooks();
|
|
595
|
+
}
|
|
596
|
+
try {
|
|
597
|
+
await this.agentWorkspace.handleEnvelope(envelope);
|
|
598
|
+
} catch (error) {
|
|
599
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
600
|
+
this.log(`agent.v2 invalid message: ${message}`);
|
|
601
|
+
this.send(createEnvelope({
|
|
602
|
+
type: "session.error",
|
|
603
|
+
sessionId: this.sessionId,
|
|
604
|
+
payload: {
|
|
605
|
+
code: "invalid_message",
|
|
606
|
+
message: `Invalid Agent Workspace message: ${message}`,
|
|
607
|
+
},
|
|
608
|
+
}));
|
|
609
|
+
}
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
519
612
|
switch (envelope.type) {
|
|
520
613
|
case "terminal.input": {
|
|
521
614
|
const p = parseTypedPayload("terminal.input", envelope.payload);
|
|
@@ -842,44 +935,6 @@ export class BridgeSession {
|
|
|
842
935
|
await this.agentSession.handleEnvelope(envelope);
|
|
843
936
|
break;
|
|
844
937
|
}
|
|
845
|
-
case "agent.v2.capabilities.request":
|
|
846
|
-
case "agent.v2.conversation.open":
|
|
847
|
-
case "agent.v2.conversation.list":
|
|
848
|
-
case "agent.v2.prompt":
|
|
849
|
-
case "agent.v2.command.execute":
|
|
850
|
-
case "agent.v2.cancel":
|
|
851
|
-
case "agent.v2.permission.respond":
|
|
852
|
-
case "agent.v2.structured_input.respond":
|
|
853
|
-
case "agent.v2.snapshot.request": {
|
|
854
|
-
if (!this.agentWorkspace) {
|
|
855
|
-
this.send(
|
|
856
|
-
createEnvelope({
|
|
857
|
-
type: "agent.v2.capabilities",
|
|
858
|
-
sessionId: this.sessionId,
|
|
859
|
-
payload: {
|
|
860
|
-
enabled: false,
|
|
861
|
-
provider: normalizeAgentProvider(
|
|
862
|
-
this.options.agentProvider ?? "codex",
|
|
863
|
-
),
|
|
864
|
-
machineId: this.machineIdentity?.machineId,
|
|
865
|
-
workspaceProtocolVersion: 2,
|
|
866
|
-
error: "Agent Workspace is not enabled. Start CLI with --agent-ui.",
|
|
867
|
-
supportsSessionList: false,
|
|
868
|
-
supportsSessionLoad: false,
|
|
869
|
-
supportsImages: false,
|
|
870
|
-
supportsAudio: false,
|
|
871
|
-
supportsPermission: false,
|
|
872
|
-
supportsPlan: false,
|
|
873
|
-
supportsCancel: false,
|
|
874
|
-
},
|
|
875
|
-
}),
|
|
876
|
-
);
|
|
877
|
-
break;
|
|
878
|
-
}
|
|
879
|
-
if (envelope.type === "agent.v2.prompt" || envelope.type === "agent.v2.command.execute") this.refreshAgentPermissionHooks();
|
|
880
|
-
await this.agentWorkspace.handleEnvelope(envelope);
|
|
881
|
-
break;
|
|
882
|
-
}
|
|
883
938
|
case "file.upload": {
|
|
884
939
|
const p = parseTypedPayload("file.upload", envelope.payload);
|
|
885
940
|
const ext = p.filename.split(".").pop() || "png";
|
|
@@ -1397,9 +1452,7 @@ export class BridgeSession {
|
|
|
1397
1452
|
if (!term?.hookPort) return;
|
|
1398
1453
|
const marker = term.hookMarker;
|
|
1399
1454
|
const curlCmd = `curl -s --connect-timeout 1 --max-time ${Math.ceil((PERMISSION_REQUEST_TIMEOUT_MS + 30_000) / 1000)} -X POST "http://127.0.0.1:${term.hookPort}/hook?m=${marker}&lid=$LINKSHELL_ID" -H 'Content-Type: application/json' --data-binary @- || true`;
|
|
1400
|
-
const providers = this.options
|
|
1401
|
-
? [normalizeAgentProvider(this.options.agentProvider)]
|
|
1402
|
-
: detectAvailableProviders();
|
|
1455
|
+
const providers = resolveAgentWorkspaceProviders(this.options);
|
|
1403
1456
|
try {
|
|
1404
1457
|
for (const provider of providers) {
|
|
1405
1458
|
if (provider === "codex") {
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export interface ClaudeStoredSession {
|
|
2
|
-
id: string;
|
|
3
|
-
cwd: string;
|
|
4
|
-
title?: string;
|
|
5
|
-
createdAt?: number;
|
|
6
|
-
lastModified: number;
|
|
7
|
-
}
|
|
8
|
-
export interface StoredAgentTimelineItem {
|
|
9
|
-
id: string;
|
|
10
|
-
conversationId: string;
|
|
11
|
-
type: "message" | "tool_call";
|
|
12
|
-
kind?: "tool_activity" | "command_execution" | "file_change";
|
|
13
|
-
itemId?: string;
|
|
14
|
-
role?: "user" | "assistant" | "system";
|
|
15
|
-
content?: Array<{
|
|
16
|
-
type: "text";
|
|
17
|
-
text: string;
|
|
18
|
-
}>;
|
|
19
|
-
text?: string;
|
|
20
|
-
toolCall?: {
|
|
21
|
-
id: string;
|
|
22
|
-
name: string;
|
|
23
|
-
input?: string;
|
|
24
|
-
output?: string;
|
|
25
|
-
createdAt?: number;
|
|
26
|
-
status: "pending" | "running" | "completed" | "failed";
|
|
27
|
-
};
|
|
28
|
-
commandExecution?: {
|
|
29
|
-
command?: string;
|
|
30
|
-
cwd?: string;
|
|
31
|
-
output?: string;
|
|
32
|
-
exitCode?: number | null;
|
|
33
|
-
status?: "pending" | "running" | "completed" | "failed";
|
|
34
|
-
};
|
|
35
|
-
fileChange?: {
|
|
36
|
-
entries: Array<{
|
|
37
|
-
path: string;
|
|
38
|
-
kind?: string;
|
|
39
|
-
added?: number;
|
|
40
|
-
removed?: number;
|
|
41
|
-
}>;
|
|
42
|
-
diff?: string;
|
|
43
|
-
summary?: string;
|
|
44
|
-
status?: "pending" | "running" | "completed" | "failed";
|
|
45
|
-
};
|
|
46
|
-
createdAt: number;
|
|
47
|
-
updatedAt?: number;
|
|
48
|
-
metadata?: Record<string, unknown>;
|
|
49
|
-
}
|
|
50
|
-
export declare function listClaudeStoredSessions(inputCwd: string): {
|
|
51
|
-
sessions: ClaudeStoredSession[];
|
|
52
|
-
};
|
|
53
|
-
export declare function loadClaudeStoredTimeline(sessionId: string, conversationId: string, options?: {
|
|
54
|
-
maxItems?: number;
|
|
55
|
-
}): {
|
|
56
|
-
items: StoredAgentTimelineItem[];
|
|
57
|
-
};
|