@cjhyy/code-shell-server 0.8.0
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/README.md +77 -0
- package/dist/attachment-service.d.ts +80 -0
- package/dist/attachment-service.js +587 -0
- package/dist/bin/code-shell-serve.d.ts +2 -0
- package/dist/bin/code-shell-serve.js +7 -0
- package/dist/client-message-id.d.ts +1 -0
- package/dist/client-message-id.js +8 -0
- package/dist/image-byte-probe.d.ts +17 -0
- package/dist/image-byte-probe.js +330 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/index.mobile-remote.d.ts +21 -0
- package/dist/index.mobile-remote.js +21 -0
- package/dist/index.serve.d.ts +3 -0
- package/dist/index.serve.js +3 -0
- package/dist/index.storage.d.ts +10 -0
- package/dist/index.storage.js +10 -0
- package/dist/index.worker.d.ts +2 -0
- package/dist/index.worker.js +2 -0
- package/dist/mobile-remote/access-passcode.d.ts +80 -0
- package/dist/mobile-remote/access-passcode.js +354 -0
- package/dist/mobile-remote/cloudflared-binary.d.ts +76 -0
- package/dist/mobile-remote/cloudflared-binary.js +242 -0
- package/dist/mobile-remote/codex-parse.d.ts +34 -0
- package/dist/mobile-remote/codex-parse.js +131 -0
- package/dist/mobile-remote/codex-room-agent.d.ts +94 -0
- package/dist/mobile-remote/codex-room-agent.js +213 -0
- package/dist/mobile-remote/mobile-attachments.d.ts +21 -0
- package/dist/mobile-remote/mobile-attachments.js +119 -0
- package/dist/mobile-remote/mobile-chat-turn.d.ts +32 -0
- package/dist/mobile-remote/mobile-chat-turn.js +67 -0
- package/dist/mobile-remote/mobile-history.d.ts +22 -0
- package/dist/mobile-remote/mobile-history.js +27 -0
- package/dist/mobile-remote/mobile-run-dispatch.d.ts +17 -0
- package/dist/mobile-remote/mobile-run-dispatch.js +56 -0
- package/dist/mobile-remote/mobile-static.d.ts +43 -0
- package/dist/mobile-remote/mobile-static.js +154 -0
- package/dist/mobile-remote/mobile-upload-service.d.ts +57 -0
- package/dist/mobile-remote/mobile-upload-service.js +370 -0
- package/dist/mobile-remote/pairing.d.ts +8 -0
- package/dist/mobile-remote/pairing.js +23 -0
- package/dist/mobile-remote/path-bins.d.ts +7 -0
- package/dist/mobile-remote/path-bins.js +16 -0
- package/dist/mobile-remote/pending-approvals.d.ts +14 -0
- package/dist/mobile-remote/pending-approvals.js +54 -0
- package/dist/mobile-remote/remote-host-manager.d.ts +117 -0
- package/dist/mobile-remote/remote-host-manager.js +345 -0
- package/dist/mobile-remote/resident-agent.d.ts +98 -0
- package/dist/mobile-remote/resident-agent.js +216 -0
- package/dist/mobile-remote/room-manager.d.ts +286 -0
- package/dist/mobile-remote/room-manager.js +765 -0
- package/dist/mobile-remote/trusted-device-store.d.ts +19 -0
- package/dist/mobile-remote/trusted-device-store.js +118 -0
- package/dist/mobile-remote/tunnel-manager.d.ts +105 -0
- package/dist/mobile-remote/tunnel-manager.js +375 -0
- package/dist/mobile-remote/types.d.ts +1 -0
- package/dist/mobile-remote/types.js +1 -0
- package/dist/mobile-remote/viewer-identity.d.ts +5 -0
- package/dist/mobile-remote/viewer-identity.js +5 -0
- package/dist/serve/cli.d.ts +15 -0
- package/dist/serve/cli.js +115 -0
- package/dist/serve/headless-server.d.ts +40 -0
- package/dist/serve/headless-server.js +276 -0
- package/dist/sessions-service.d.ts +73 -0
- package/dist/sessions-service.js +303 -0
- package/dist/static-files.d.ts +1 -0
- package/dist/static-files.js +25 -0
- package/dist/worker-bridge-core.d.ts +154 -0
- package/dist/worker-bridge-core.js +315 -0
- package/package.json +63 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import type { ResidentAgentEvent } from "./resident-agent.js";
|
|
2
|
+
import type { InputAttachmentMeta } from "../attachment-service.js";
|
|
3
|
+
export declare function isValidRoomId(id: unknown): id is string;
|
|
4
|
+
export type RoomPermissionMode = "default" | "acceptEdits" | "bypassPermissions";
|
|
5
|
+
/**
|
|
6
|
+
* Build the `updatedInput` that answers an AskUserQuestion `can_use_tool`
|
|
7
|
+
* request. Empirically (claude CLI, verified against the live control protocol
|
|
8
|
+
* + the Agent SDK docs), the answer must go in an `answers` RECORD keyed by each
|
|
9
|
+
* question's `question` text, with STRING values (arrays/objects fail schema
|
|
10
|
+
* validation). For multiSelect the caller joins chosen labels with ", ". The
|
|
11
|
+
* original `questions` array is passed through (claude validates against it).
|
|
12
|
+
* `answersByQuestion` maps question text → answer string.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildAskUserUpdatedInput(input: unknown, answersByQuestion: Record<string, string>): Record<string, unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* Parse an AskUserQuestion input into the first question's prompt/header/options
|
|
17
|
+
* so the UI can render a choice card. The room UI answers one question at a time
|
|
18
|
+
* (the first); returns undefined for a non-AskUser / malformed input (no
|
|
19
|
+
* questions), which the caller treats as "fall back to auto-allow".
|
|
20
|
+
*/
|
|
21
|
+
export declare function askUserPrompt(input: unknown): {
|
|
22
|
+
question: string;
|
|
23
|
+
header?: string;
|
|
24
|
+
options: string[];
|
|
25
|
+
multiSelect: boolean;
|
|
26
|
+
} | undefined;
|
|
27
|
+
/** Which external CLI backs the room. "claude-code" drives `claude` (long-lived
|
|
28
|
+
* stdin process with per-tool approval); "codex" drives `codex exec` (one
|
|
29
|
+
* process per turn, resumed by thread id, no per-tool approval — the sandbox
|
|
30
|
+
* tier chosen at open is the only guardrail). */
|
|
31
|
+
export type RoomKind = "claude-code" | "codex";
|
|
32
|
+
/** Exact external-session identity proved by the host's transcript locator.
|
|
33
|
+
* RoomManager deliberately does not know CLI-specific transcript paths; the
|
|
34
|
+
* host resolves those paths and returns the identity read from disk. */
|
|
35
|
+
export interface LinkedSessionTarget {
|
|
36
|
+
externalSessionId: string;
|
|
37
|
+
cwd: string;
|
|
38
|
+
kind: RoomKind;
|
|
39
|
+
}
|
|
40
|
+
export interface RoomMeta {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
cwd: string;
|
|
44
|
+
kind: RoomKind;
|
|
45
|
+
permissionMode: RoomPermissionMode;
|
|
46
|
+
createdAt: number;
|
|
47
|
+
lastActiveAt: number;
|
|
48
|
+
/** Session/thread id to resume: claude's session_id OR codex's thread_id.
|
|
49
|
+
* (Named claudeSessionId for back-compat with persisted room.json files.) */
|
|
50
|
+
claudeSessionId?: string;
|
|
51
|
+
/** Persisted capability boundary for a room bound from an external CLI
|
|
52
|
+
* transcript. While set, ordinary open/send calls cannot start an agent;
|
|
53
|
+
* only takeOverLinkedSession may clear it after revalidating the tuple. */
|
|
54
|
+
linkedSessionMode?: "observe-only";
|
|
55
|
+
}
|
|
56
|
+
export type RoomOpenStatus = "running" | "missing" | "observing";
|
|
57
|
+
export interface RoomMessage {
|
|
58
|
+
seq: number;
|
|
59
|
+
ts: number;
|
|
60
|
+
from: "user" | "agent" | "system";
|
|
61
|
+
type: string;
|
|
62
|
+
text?: string;
|
|
63
|
+
tool?: string;
|
|
64
|
+
summary?: string;
|
|
65
|
+
reason?: string;
|
|
66
|
+
isError?: boolean;
|
|
67
|
+
/** claude's tool_use block id — present on `tool` (the start) and
|
|
68
|
+
* `tool_result` (the matching result) so the UI can pair them by id rather
|
|
69
|
+
* than guessing "the last open tool". Absent on legacy messages. */
|
|
70
|
+
toolId?: string;
|
|
71
|
+
/** Full structured tool_use input (e.g. a sub-agent's `prompt`) on `tool`
|
|
72
|
+
* messages. `summary` is a lossy one-field preview; `args` is what the tool
|
|
73
|
+
* card expands to so the real parameters are visible. Absent on legacy
|
|
74
|
+
* messages that predate args persistence. */
|
|
75
|
+
args?: Record<string, unknown>;
|
|
76
|
+
attachments?: Array<{
|
|
77
|
+
name: string;
|
|
78
|
+
mime?: string;
|
|
79
|
+
size: number;
|
|
80
|
+
path: string;
|
|
81
|
+
}>;
|
|
82
|
+
}
|
|
83
|
+
export declare function roomTurnText(text: string, attachments: InputAttachmentMeta[]): string;
|
|
84
|
+
export declare function stripRoomAttachmentBlock(text: string): string;
|
|
85
|
+
/**
|
|
86
|
+
* Minimal interface a resident agent must satisfy, so RoomManager can be unit
|
|
87
|
+
* tested with a fake (no real claude process).
|
|
88
|
+
*/
|
|
89
|
+
export interface RoomAgent {
|
|
90
|
+
start(): void;
|
|
91
|
+
send(text: string): boolean;
|
|
92
|
+
isRunning(): boolean;
|
|
93
|
+
stop(): void;
|
|
94
|
+
respondControl?(requestId: string, decision: {
|
|
95
|
+
behavior: "allow";
|
|
96
|
+
updatedInput?: unknown;
|
|
97
|
+
} | {
|
|
98
|
+
behavior: "deny";
|
|
99
|
+
message: string;
|
|
100
|
+
}): void;
|
|
101
|
+
}
|
|
102
|
+
export interface RoomAgentFactory {
|
|
103
|
+
(room: RoomMeta, onEvent: (event: ResidentAgentEvent) => void): RoomAgent;
|
|
104
|
+
}
|
|
105
|
+
export interface RoomManagerOptions {
|
|
106
|
+
rootDir: string;
|
|
107
|
+
createAgent: RoomAgentFactory;
|
|
108
|
+
/** Locate an already-running external CLI transcript without starting a CLI.
|
|
109
|
+
* Returning null means the transcript is absent. The returned identity is
|
|
110
|
+
* checked again here so a stale or mismatched locator fails closed. */
|
|
111
|
+
resolveLinkedSession?: (target: Readonly<LinkedSessionTarget>) => LinkedSessionTarget | null | undefined;
|
|
112
|
+
/** Called whenever a room gains a new persisted message (push to phone). */
|
|
113
|
+
onMessage: (roomId: string, msg: RoomMessage) => void;
|
|
114
|
+
/** Called when a room's resident agent requests tool-use approval. For
|
|
115
|
+
* AskUserQuestion, `askUser` carries the parsed prompt + options so the UI
|
|
116
|
+
* renders a choice card; the user's pick is routed back via respondApproval's
|
|
117
|
+
* `answer` field and baked into updatedInput.answers here in main. */
|
|
118
|
+
onApprovalRequest?: (roomId: string, req: {
|
|
119
|
+
requestId: string;
|
|
120
|
+
toolName: string;
|
|
121
|
+
displayName?: string;
|
|
122
|
+
input: unknown;
|
|
123
|
+
description?: string;
|
|
124
|
+
askUser?: {
|
|
125
|
+
question: string;
|
|
126
|
+
header?: string;
|
|
127
|
+
options: string[];
|
|
128
|
+
multiSelect: boolean;
|
|
129
|
+
};
|
|
130
|
+
}) => void;
|
|
131
|
+
/** Called after a room's resident process exits or the room is explicitly
|
|
132
|
+
* closed. Live transcript followers use it as their natural terminal hook. */
|
|
133
|
+
onRoomEnded?: (roomId: string) => void;
|
|
134
|
+
now?: () => number;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Owns room lifecycle and the on-disk message log (the authoritative source).
|
|
138
|
+
* A room's resident agent is started on open() and its normalized events are
|
|
139
|
+
* appended to messages.jsonl and mirrored via onMessage. seq is monotonic per
|
|
140
|
+
* room so the phone can sync incrementally.
|
|
141
|
+
*/
|
|
142
|
+
export declare class RoomManager {
|
|
143
|
+
private readonly opts;
|
|
144
|
+
private agents;
|
|
145
|
+
/** Rooms whose visible output is currently sourced from the external CLI's
|
|
146
|
+
* transcript tail. While present, the resident agent's stdout mirror is
|
|
147
|
+
* suppressed so the same CLI output is not pushed twice. */
|
|
148
|
+
private transcriptFollowedRooms;
|
|
149
|
+
/** Pending AskUserQuestion control requests, keyed by `${roomId}:${requestId}`.
|
|
150
|
+
* Holds the raw tool input so respondApproval can bake the user's answer into
|
|
151
|
+
* the `answers` record the CLI expects. Cleared on response. */
|
|
152
|
+
private pendingAskUser;
|
|
153
|
+
/** User turns are echoed immediately for responsive room UX. When a
|
|
154
|
+
* transcript follower later sees the same CLI-written user line, this queue
|
|
155
|
+
* suppresses that duplicate. */
|
|
156
|
+
private pendingTranscriptUserEchoes;
|
|
157
|
+
/**
|
|
158
|
+
* Rooms whose agent events must be buffered instead of handled immediately.
|
|
159
|
+
*
|
|
160
|
+
* Only set for the duration of `agent.send()` inside `send()`: acceptance has
|
|
161
|
+
* to be known before the user turn is persisted, but the user turn must still
|
|
162
|
+
* come FIRST in the history. An agent that emits synchronously from send()
|
|
163
|
+
* would otherwise have its reply appended ahead of the prompt it answers.
|
|
164
|
+
*/
|
|
165
|
+
private deferredEmitRooms;
|
|
166
|
+
private deferredEmits;
|
|
167
|
+
/**
|
|
168
|
+
* Last assigned message seq per room, so appends do not re-read the whole
|
|
169
|
+
* JSONL. Seeded lazily from disk on the first append after startup; dropped
|
|
170
|
+
* when the room's messages are deleted so the next append re-derives it.
|
|
171
|
+
*/
|
|
172
|
+
private lastSeqByRoom;
|
|
173
|
+
/** Throttled lastActiveAt bookkeeping — see touchLastActive. */
|
|
174
|
+
private pendingLastActive;
|
|
175
|
+
private lastActiveWrittenAt;
|
|
176
|
+
private now;
|
|
177
|
+
/** Drop all pending AskUser entries for a room. Called when its agent goes
|
|
178
|
+
* away (exit/close) so a request that can no longer be answered doesn't leak
|
|
179
|
+
* in the map (small memory leak) and its approval card doesn't hang until the
|
|
180
|
+
* 5-min timeout. Keys are `${roomId}:${requestId}`. */
|
|
181
|
+
private clearPendingAskUser;
|
|
182
|
+
constructor(opts: RoomManagerOptions);
|
|
183
|
+
private roomDir;
|
|
184
|
+
private metaPath;
|
|
185
|
+
private msgPath;
|
|
186
|
+
createRoom(input: {
|
|
187
|
+
name?: string;
|
|
188
|
+
cwd: string;
|
|
189
|
+
kind?: RoomKind;
|
|
190
|
+
permissionMode?: RoomPermissionMode;
|
|
191
|
+
claudeSessionId?: string;
|
|
192
|
+
linkedSessionMode?: "observe-only";
|
|
193
|
+
}): RoomMeta;
|
|
194
|
+
listRooms(): RoomMeta[];
|
|
195
|
+
getRoom(id: string): RoomMeta | undefined;
|
|
196
|
+
private nextSeq;
|
|
197
|
+
private append;
|
|
198
|
+
/**
|
|
199
|
+
* Refresh the room's `lastActiveAt`, throttled.
|
|
200
|
+
*
|
|
201
|
+
* This re-read and rewrote the whole room.json on EVERY appended message. The
|
|
202
|
+
* field only feeds idle-based pruning (measured in hours), so second-level
|
|
203
|
+
* precision is worthless while the write cost is paid per message — together
|
|
204
|
+
* with the old full-history seq scan it made a busy room progressively slower.
|
|
205
|
+
*
|
|
206
|
+
* Writes at most once per THROTTLE window per room; `flushLastActive` forces
|
|
207
|
+
* one out at close/prune time so a room that goes quiet still records its real
|
|
208
|
+
* last activity.
|
|
209
|
+
*/
|
|
210
|
+
private touchLastActive;
|
|
211
|
+
/** Persist a pending lastActiveAt immediately, if there is one. */
|
|
212
|
+
private flushLastActive;
|
|
213
|
+
/** Persist the room's resume id (claude session_id / codex thread_id) so the
|
|
214
|
+
* next open() can continue the same conversation. No-op if unchanged. */
|
|
215
|
+
setRoomSessionId(id: string, sessionId: string): void;
|
|
216
|
+
getMessages(id: string, sinceSeq?: number): RoomMessage[];
|
|
217
|
+
latestSeq(id: string): number;
|
|
218
|
+
beginTranscriptFollow(id: string): void;
|
|
219
|
+
endTranscriptFollow(id: string): void;
|
|
220
|
+
/** Persist normalized transcript-tail messages through the same seq/history/
|
|
221
|
+
* broadcast path as resident-agent output. */
|
|
222
|
+
ingestTranscriptMessages(id: string, messages: Omit<RoomMessage, "seq" | "ts">[]): void;
|
|
223
|
+
/**
|
|
224
|
+
* Open (or create) the room bound to a claude session id, deduping by that id
|
|
225
|
+
* so a given claude session maps to exactly one room. Returns the room id.
|
|
226
|
+
*/
|
|
227
|
+
openForSession(claudeSessionId: string, cwd: string, mode: RoomPermissionMode, kind?: RoomKind): {
|
|
228
|
+
roomId: string;
|
|
229
|
+
status: RoomOpenStatus;
|
|
230
|
+
};
|
|
231
|
+
/** Bind an external CLI transcript to a room for observation only.
|
|
232
|
+
*
|
|
233
|
+
* This method never creates or starts a resident agent. The host must first
|
|
234
|
+
* prove that an on-disk transcript exists for the exact kind + session id +
|
|
235
|
+
* cwd tuple. A missing locator, missing transcript, or mismatched identity
|
|
236
|
+
* fails closed before a room is created.
|
|
237
|
+
*/
|
|
238
|
+
openLinkedSession(externalSessionId: string, cwd: string, kind: RoomKind): {
|
|
239
|
+
roomId: string;
|
|
240
|
+
status: "observing" | "running";
|
|
241
|
+
mode: RoomPermissionMode;
|
|
242
|
+
cwd: string;
|
|
243
|
+
};
|
|
244
|
+
/** Explicitly take control of a previously observed external CLI room.
|
|
245
|
+
* This is the linked-session operation that may create/start an agent. The
|
|
246
|
+
* external identity is revalidated at takeover time so stale links cannot
|
|
247
|
+
* silently start a different session. */
|
|
248
|
+
takeOverLinkedSession(roomId: string, externalSessionId: string, cwd: string, kind: RoomKind): {
|
|
249
|
+
roomId: string;
|
|
250
|
+
status: "running";
|
|
251
|
+
mode: RoomPermissionMode;
|
|
252
|
+
cwd: string;
|
|
253
|
+
};
|
|
254
|
+
private requireLinkedSessionTarget;
|
|
255
|
+
/**
|
|
256
|
+
* Forward a phone-side approval decision to the room's resident agent.
|
|
257
|
+
* Returns false if the room has no live agent (or it can't take control
|
|
258
|
+
* responses).
|
|
259
|
+
*/
|
|
260
|
+
respondApproval(roomId: string, requestId: string, decision: {
|
|
261
|
+
behavior: "allow";
|
|
262
|
+
updatedInput?: unknown;
|
|
263
|
+
answer?: string;
|
|
264
|
+
} | {
|
|
265
|
+
behavior: "deny";
|
|
266
|
+
message: string;
|
|
267
|
+
}): boolean;
|
|
268
|
+
/** Open a room: start its resident agent if not already running. Observe-only
|
|
269
|
+
* linked rooms fail closed until takeOverLinkedSession clears the boundary. */
|
|
270
|
+
open(id: string): {
|
|
271
|
+
status: RoomOpenStatus;
|
|
272
|
+
};
|
|
273
|
+
private onAgentEvent;
|
|
274
|
+
/** Post a user message: persist it, ensure agent running, feed it. */
|
|
275
|
+
send(id: string, text: string, attachments?: InputAttachmentMeta[]): boolean;
|
|
276
|
+
close(id: string): void;
|
|
277
|
+
closeAll(): void;
|
|
278
|
+
isOpen(id: string): boolean;
|
|
279
|
+
/**
|
|
280
|
+
* Delete rooms whose last activity is older than maxAgeMs (idle-based GC,
|
|
281
|
+
* replacing the removed one-shot /cc path's lack of cleanup). A room with a
|
|
282
|
+
* currently running resident agent is NEVER reaped, regardless of age — only
|
|
283
|
+
* truly dormant rooms (whole directory) are removed. Returns the ids deleted.
|
|
284
|
+
*/
|
|
285
|
+
pruneStaleRooms(maxAgeMs: number): string[];
|
|
286
|
+
}
|