@rallycry/conveyor-mcp 2.1.1

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.
@@ -0,0 +1,119 @@
1
+ // src/tunnel.ts
2
+ var ETX = 3;
3
+ var defaultSleep = (ms) => new Promise((resolve) => {
4
+ setTimeout(resolve, ms);
5
+ });
6
+ async function waitForPtySession(conn, taskId, options = {}) {
7
+ const intervalMs = options.intervalMs ?? 2e3;
8
+ const timeoutMs = options.timeoutMs ?? 5 * 6e4;
9
+ const sleep = options.sleep ?? defaultSleep;
10
+ const deadline = Date.now() + timeoutMs;
11
+ for (; ; ) {
12
+ const active = await conn.getActivePtySession(taskId);
13
+ if (active.sessionId) {
14
+ return { sessionId: active.sessionId, cols: active.cols, rows: active.rows };
15
+ }
16
+ if (Date.now() >= deadline) {
17
+ throw new Error(
18
+ `Timed out after ${timeoutMs}ms waiting for the cloud PTY session to become ready`
19
+ );
20
+ }
21
+ await sleep(intervalMs);
22
+ }
23
+ }
24
+ async function attachTunnel(conn, session, tty, options = {}) {
25
+ const { sessionId } = session;
26
+ let attached = false;
27
+ let lastSeq = -1;
28
+ const liveBuffer = [];
29
+ const writeChunk = (chunk) => {
30
+ tty.write(chunk.data);
31
+ if (chunk.seq > lastSeq) lastSeq = chunk.seq;
32
+ };
33
+ const offData = conn.onPtyData((chunk) => {
34
+ if (chunk.sessionId !== sessionId) return;
35
+ if (!attached) {
36
+ liveBuffer.push(chunk);
37
+ return;
38
+ }
39
+ if (chunk.seq <= lastSeq) return;
40
+ writeChunk(chunk);
41
+ });
42
+ conn.subscribeToSession(sessionId);
43
+ const snapshot = await conn.ptyAttach(sessionId);
44
+ for (const chunk of snapshot.chunks) {
45
+ if (chunk.seq > lastSeq) writeChunk(chunk);
46
+ }
47
+ attached = true;
48
+ for (const chunk of liveBuffer) {
49
+ if (chunk.seq > lastSeq) writeChunk(chunk);
50
+ }
51
+ liveBuffer.length = 0;
52
+ const offInput = tty.onInput((data) => {
53
+ if (options.onDetachRequest) {
54
+ const idx = data.indexOf(ETX);
55
+ if (idx !== -1) {
56
+ if (idx > 0) conn.ptyInput(sessionId, data.subarray(0, idx).toString("utf8"));
57
+ options.onDetachRequest();
58
+ return;
59
+ }
60
+ }
61
+ conn.ptyInput(sessionId, data.toString("utf8"));
62
+ });
63
+ const offResize = tty.onResize((cols, rows) => {
64
+ conn.ptyResize(sessionId, cols, rows);
65
+ });
66
+ const initial = tty.size();
67
+ conn.ptyResize(sessionId, initial.cols, initial.rows);
68
+ let detached = false;
69
+ return {
70
+ sessionId,
71
+ detach() {
72
+ if (detached) return;
73
+ detached = true;
74
+ offInput();
75
+ offResize();
76
+ offData();
77
+ }
78
+ };
79
+ }
80
+ async function runTunnel(conn, tty, options = {}) {
81
+ const log = options.log ?? (() => {
82
+ });
83
+ let taskId = options.taskId;
84
+ if (!taskId) {
85
+ const title = options.title;
86
+ if (!title) throw new Error("A task title is required to create a new cloud card");
87
+ log("Creating cloud card\u2026");
88
+ const task = await conn.createTask({
89
+ title,
90
+ description: options.description,
91
+ plan: options.plan
92
+ });
93
+ taskId = task.id;
94
+ log(`Created card ${task.slug} (${task.id})`);
95
+ }
96
+ log("Starting cloud build\u2026");
97
+ await conn.startBuild(taskId);
98
+ log("Waiting for the cloud PTY session to become ready\u2026");
99
+ const session = await waitForPtySession(conn, taskId, {
100
+ intervalMs: options.pollIntervalMs,
101
+ timeoutMs: options.pollTimeoutMs,
102
+ log,
103
+ sleep: options.sleep
104
+ });
105
+ log(`Cloud PTY session ready: ${session.sessionId}`);
106
+ options.onReady?.(session);
107
+ const handle = await attachTunnel(conn, session, tty, {
108
+ onDetachRequest: options.onDetachRequest
109
+ });
110
+ log("Attached. Press Ctrl-C to detach (the cloud session keeps running).");
111
+ return { taskId, sessionId: session.sessionId, handle };
112
+ }
113
+
114
+ export {
115
+ waitForPtySession,
116
+ attachTunnel,
117
+ runTunnel
118
+ };
119
+ //# sourceMappingURL=chunk-N2XC2PGJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tunnel.ts"],"sourcesContent":["import type { ActivePtySession, PtyAttachSnapshot, PtyDataChunk } from \"./connection.js\";\n\n/** ETX (Ctrl-C) byte. In raw mode this arrives as data rather than SIGINT. */\nconst ETX = 0x03;\n\n/**\n * The slice of {@link ConveyorConnection} the tunnel needs. Defined as a\n * structural interface so tests can inject a mock — and, deliberately, so the\n * relay has NO reference to `stopBuild`. Detaching the local terminal must\n * never stop the cloud session, and the cleanest way to guarantee that is to\n * make stopping unreachable from this layer.\n */\nexport interface TunnelConnection {\n createTask(params: {\n title: string;\n description?: string;\n plan?: string;\n status?: string;\n }): Promise<{ id: string; slug: string }>;\n startBuild(taskId: string): Promise<{ taskId: string; status: string }>;\n getActivePtySession(taskId: string): Promise<ActivePtySession>;\n ptyAttach(sessionId: string): Promise<PtyAttachSnapshot>;\n subscribeToSession(sessionId: string): void;\n ptyInput(sessionId: string, data: string): void;\n ptyResize(sessionId: string, cols: number, rows: number): void;\n onPtyData(handler: (chunk: PtyDataChunk) => void): () => void;\n}\n\n/**\n * Local terminal abstraction. The CLI binds this to the real process TTY; tests\n * bind it to an in-memory fake. All output is raw utf8 (node-pty does not use\n * base64), matching the S2 relay encoding.\n */\nexport interface TunnelTty {\n /** Write raw PTY output to the local terminal. */\n write(data: string): void;\n /** Register a stdin handler. Returns an unsubscribe function. */\n onInput(handler: (data: Buffer) => void): () => void;\n /** Register a resize handler. Returns an unsubscribe function. */\n onResize(handler: (cols: number, rows: number) => void): () => void;\n /** Current local terminal dimensions. */\n size(): { cols: number; rows: number };\n}\n\n/** A live tunnel attachment. */\nexport interface TunnelHandle {\n sessionId: string;\n /**\n * Tear down the local relay (stdin/resize/output listeners). Does NOT stop\n * the cloud build — the session keeps running after detach. Idempotent.\n */\n detach(): void;\n}\n\n/** Resolved (non-null) active PTY session. */\nexport interface ResolvedPtySession {\n sessionId: string;\n cols?: number;\n rows?: number;\n}\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\nexport interface WaitForPtySessionOptions {\n intervalMs?: number;\n timeoutMs?: number;\n log?: (msg: string) => void;\n /** Injectable for tests; defaults to a real timer. */\n sleep?: (ms: number) => Promise<void>;\n}\n\n/**\n * Poll {@link TunnelConnection.getActivePtySession} until the cloud agent has\n * booted its PTY and produced at least one ring frame. `sessionId` is resolved\n * server-side from `taskId`, so the local side never invents or supplies one.\n */\nexport async function waitForPtySession(\n conn: TunnelConnection,\n taskId: string,\n options: WaitForPtySessionOptions = {},\n): Promise<ResolvedPtySession> {\n const intervalMs = options.intervalMs ?? 2000;\n const timeoutMs = options.timeoutMs ?? 5 * 60_000;\n const sleep = options.sleep ?? defaultSleep;\n const deadline = Date.now() + timeoutMs;\n\n for (;;) {\n const active = await conn.getActivePtySession(taskId);\n if (active.sessionId) {\n return { sessionId: active.sessionId, cols: active.cols, rows: active.rows };\n }\n if (Date.now() >= deadline) {\n throw new Error(\n `Timed out after ${timeoutMs}ms waiting for the cloud PTY session to become ready`,\n );\n }\n await sleep(intervalMs);\n }\n}\n\nexport interface AttachTunnelOptions {\n /**\n * Called when the local user requests a detach (Ctrl-C / ETX in the input\n * stream). When provided, the ETX byte is intercepted and NOT forwarded to\n * the cloud PTY; when omitted, all input passes through unmodified.\n */\n onDetachRequest?: () => void;\n}\n\n/**\n * Attach the local TTY to a cloud PTY session, reusing the S2 `pty:*` relay\n * envelope. Replays the ring snapshot then streams live frames, with the same\n * seq-based dedup the web `PtyTerminal` uses so no bytes are dropped or doubled\n * across the attach boundary.\n */\nexport async function attachTunnel(\n conn: TunnelConnection,\n session: ResolvedPtySession,\n tty: TunnelTty,\n options: AttachTunnelOptions = {},\n): Promise<TunnelHandle> {\n const { sessionId } = session;\n let attached = false;\n let lastSeq = -1;\n const liveBuffer: PtyDataChunk[] = [];\n\n const writeChunk = (chunk: { seq: number; data: string }): void => {\n tty.write(chunk.data);\n if (chunk.seq > lastSeq) lastSeq = chunk.seq;\n };\n\n // 1. Register the output handler and join the room BEFORE fetching the\n // snapshot. Frames that arrive during the fetch are buffered, never lost.\n const offData = conn.onPtyData((chunk) => {\n if (chunk.sessionId !== sessionId) return;\n if (!attached) {\n liveBuffer.push(chunk);\n return;\n }\n if (chunk.seq <= lastSeq) return;\n writeChunk(chunk);\n });\n conn.subscribeToSession(sessionId);\n\n // 2. Replay the ring snapshot (skip anything already seen).\n const snapshot = await conn.ptyAttach(sessionId);\n for (const chunk of snapshot.chunks) {\n if (chunk.seq > lastSeq) writeChunk(chunk);\n }\n\n // 3. Flush frames buffered during the fetch, then go live.\n attached = true;\n for (const chunk of liveBuffer) {\n if (chunk.seq > lastSeq) writeChunk(chunk);\n }\n liveBuffer.length = 0;\n\n // 4. Wire local stdin → cloud PTY (raw utf8). Intercept ETX for detach.\n const offInput = tty.onInput((data) => {\n if (options.onDetachRequest) {\n const idx = data.indexOf(ETX);\n if (idx !== -1) {\n if (idx > 0) conn.ptyInput(sessionId, data.subarray(0, idx).toString(\"utf8\"));\n options.onDetachRequest();\n return;\n }\n }\n conn.ptyInput(sessionId, data.toString(\"utf8\"));\n });\n\n // 5. Wire resize → cloud PTY, and push the current size now so the cloud TUI\n // repaints to the local viewport dimensions.\n const offResize = tty.onResize((cols, rows) => {\n conn.ptyResize(sessionId, cols, rows);\n });\n const initial = tty.size();\n conn.ptyResize(sessionId, initial.cols, initial.rows);\n\n let detached = false;\n return {\n sessionId,\n detach() {\n if (detached) return;\n detached = true;\n offInput();\n offResize();\n offData();\n // Intentionally NO stopBuild here: detach != stop. The cloud session\n // continues running so the agent's work is never interrupted by the\n // local terminal disconnecting.\n },\n };\n}\n\nexport interface RunTunnelOptions {\n /** Reuse an existing task instead of creating a new card. */\n taskId?: string;\n /** Card fields (used only when `taskId` is not supplied). */\n title?: string;\n description?: string;\n plan?: string;\n pollIntervalMs?: number;\n pollTimeoutMs?: number;\n log?: (msg: string) => void;\n sleep?: (ms: number) => Promise<void>;\n onDetachRequest?: () => void;\n /**\n * Invoked once the PTY session is ready, immediately before attaching. The\n * CLI uses this to enable raw TTY mode only at the last moment — before this\n * point Ctrl-C should still raise SIGINT so the user can abort the wait.\n */\n onReady?: (session: ResolvedPtySession) => void;\n}\n\nexport interface TunnelSession {\n taskId: string;\n sessionId: string;\n handle: TunnelHandle;\n}\n\n/**\n * End-to-end flow: (optionally) create a cloud card, start the build, wait for\n * the cloud PTY to come up, then attach the local terminal. Returns once the\n * relay is live; the caller keeps the process alive and calls `handle.detach()`.\n */\nexport async function runTunnel(\n conn: TunnelConnection,\n tty: TunnelTty,\n options: RunTunnelOptions = {},\n): Promise<TunnelSession> {\n const log = options.log ?? (() => {});\n\n let taskId = options.taskId;\n if (!taskId) {\n const title = options.title;\n if (!title) throw new Error(\"A task title is required to create a new cloud card\");\n log(\"Creating cloud card…\");\n const task = await conn.createTask({\n title,\n description: options.description,\n plan: options.plan,\n });\n taskId = task.id;\n log(`Created card ${task.slug} (${task.id})`);\n }\n\n log(\"Starting cloud build…\");\n await conn.startBuild(taskId);\n\n log(\"Waiting for the cloud PTY session to become ready…\");\n const session = await waitForPtySession(conn, taskId, {\n intervalMs: options.pollIntervalMs,\n timeoutMs: options.pollTimeoutMs,\n log,\n sleep: options.sleep,\n });\n log(`Cloud PTY session ready: ${session.sessionId}`);\n\n options.onReady?.(session);\n\n const handle = await attachTunnel(conn, session, tty, {\n onDetachRequest: options.onDetachRequest,\n });\n log(\"Attached. Press Ctrl-C to detach (the cloud session keeps running).\");\n\n return { taskId, sessionId: session.sessionId, handle };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,315 @@
1
+ // src/connection.ts
2
+ import { io } from "socket.io-client";
3
+ var ConveyorConnection = class {
4
+ socket = null;
5
+ config;
6
+ constructor(config) {
7
+ this.config = config;
8
+ }
9
+ get projectId() {
10
+ return this.config.projectId;
11
+ }
12
+ connect() {
13
+ return new Promise((resolve, reject) => {
14
+ let settled = false;
15
+ let attempts = 0;
16
+ const maxAttempts = 15;
17
+ this.socket = io(this.config.apiUrl, {
18
+ auth: {
19
+ projectToken: this.config.projectToken,
20
+ runnerMode: "project"
21
+ },
22
+ transports: ["websocket"],
23
+ reconnection: true,
24
+ reconnectionAttempts: Infinity,
25
+ reconnectionDelay: 2e3,
26
+ reconnectionDelayMax: 3e4,
27
+ randomizationFactor: 0.3,
28
+ extraHeaders: { "ngrok-skip-browser-warning": "true" }
29
+ });
30
+ this.socket.on("connect", () => {
31
+ if (!settled) {
32
+ settled = true;
33
+ this.socket?.emit("projectService:subscribe", { id: this.config.projectId });
34
+ resolve();
35
+ }
36
+ });
37
+ this.socket.on("connect_error", (err) => {
38
+ attempts++;
39
+ if (!settled && attempts >= maxAttempts) {
40
+ settled = true;
41
+ reject(new Error(`Failed to connect: ${err.message}`));
42
+ }
43
+ });
44
+ });
45
+ }
46
+ // ── Service method call (agentSessionService:*) ─────────────────────
47
+ call(method, data) {
48
+ const socket = this.socket;
49
+ if (!socket) throw new Error("Not connected");
50
+ const TIMEOUT_MS = 15e3;
51
+ return Promise.race([
52
+ new Promise((resolve, reject) => {
53
+ socket.emit(`agentSessionService:${method}`, data, ((response) => {
54
+ if (response.success) {
55
+ resolve(response.data);
56
+ } else {
57
+ reject(new Error(response.error ?? `${method} failed`));
58
+ }
59
+ }));
60
+ }),
61
+ new Promise((_, reject) => {
62
+ setTimeout(() => {
63
+ reject(new Error(`Request timed out: ${method}`));
64
+ }, TIMEOUT_MS);
65
+ })
66
+ ]);
67
+ }
68
+ /**
69
+ * Fire-and-forget emit (no ack). The quickdraw-core server method handler
70
+ * invokes the ack callback via optional chaining, so omitting it still runs
71
+ * the full auth/schema/ACL pipeline — it just skips the response round-trip.
72
+ * Used for high-frequency PTY input/resize so each keystroke does not arm a
73
+ * 15s timeout timer.
74
+ */
75
+ emit(method, data) {
76
+ const socket = this.socket;
77
+ if (!socket) throw new Error("Not connected");
78
+ socket.emit(`agentSessionService:${method}`, data);
79
+ }
80
+ // ── Task Queries ────────────────────────────────────────────────────
81
+ listTasks(params) {
82
+ return this.call("listProjectTasks", {
83
+ projectId: this.config.projectId,
84
+ ...params
85
+ });
86
+ }
87
+ getTask(taskId) {
88
+ return this.call("getProjectTask", {
89
+ projectId: this.config.projectId,
90
+ taskId
91
+ });
92
+ }
93
+ searchTasks(params) {
94
+ return this.call("searchProjectTasks", {
95
+ projectId: this.config.projectId,
96
+ ...params
97
+ });
98
+ }
99
+ // ── Task Mutations ──────────────────────────────────────────────────
100
+ createTask(params) {
101
+ return this.call("createProjectTask", {
102
+ projectId: this.config.projectId,
103
+ ...params
104
+ });
105
+ }
106
+ updateTask(params) {
107
+ return this.call("updateProjectTask", {
108
+ projectId: this.config.projectId,
109
+ ...params
110
+ });
111
+ }
112
+ // ── Build Management ────────────────────────────────────────────────
113
+ startBuild(taskId) {
114
+ return this.call("startProjectBuild", {
115
+ projectId: this.config.projectId,
116
+ taskId
117
+ });
118
+ }
119
+ stopBuild(taskId) {
120
+ return this.call("stopProjectBuild", {
121
+ projectId: this.config.projectId,
122
+ taskId
123
+ });
124
+ }
125
+ getBuildStatus(taskId) {
126
+ return this.call("getProjectTask", {
127
+ projectId: this.config.projectId,
128
+ taskId
129
+ }).then((task) => ({
130
+ session: task?.session ?? null
131
+ }));
132
+ }
133
+ // ── Chat ────────────────────────────────────────────────────────────
134
+ getTaskChat(taskId, limit) {
135
+ return this.call("getProjectTask", {
136
+ projectId: this.config.projectId,
137
+ taskId
138
+ }).then((task) => {
139
+ const messages = task?.chatMessages ?? [];
140
+ return limit ? messages.slice(-limit) : messages;
141
+ });
142
+ }
143
+ postToTaskChat(taskId, content) {
144
+ return this.call("postToProjectTaskChat", {
145
+ projectId: this.config.projectId,
146
+ taskId,
147
+ content
148
+ });
149
+ }
150
+ // ── CLI History ─────────────────────────────────────────────────────
151
+ getTaskCli(taskId, limit, source) {
152
+ return this.call("getProjectTaskCli", {
153
+ projectId: this.config.projectId,
154
+ taskId,
155
+ limit,
156
+ source
157
+ });
158
+ }
159
+ // ── Project Info ────────────────────────────────────────────────────
160
+ listTags() {
161
+ return this.call("listProjectTags", {
162
+ projectId: this.config.projectId
163
+ });
164
+ }
165
+ getProjectSummary() {
166
+ return this.call("getProjectSummary", {
167
+ projectId: this.config.projectId
168
+ });
169
+ }
170
+ // ── Review Flow ─────────────────────────────────────────────────────
171
+ async approveTask(taskId) {
172
+ const task = await this.call("getProjectTask", {
173
+ projectId: this.config.projectId,
174
+ taskId
175
+ });
176
+ if (!task) throw new Error("Task not found");
177
+ const nextStatus = task.status === "ReviewPR" ? "ReviewDev" : "Complete";
178
+ const result = await this.updateTask({ taskId, status: nextStatus });
179
+ return { status: result.status };
180
+ }
181
+ async requestChanges(taskId, feedback) {
182
+ await this.postToTaskChat(taskId, feedback);
183
+ await this.updateTask({ taskId, status: "InProgress" });
184
+ }
185
+ approveAndMergePR(childTaskId) {
186
+ return this.call("approveProjectMergePR", {
187
+ projectId: this.config.projectId,
188
+ childTaskId
189
+ });
190
+ }
191
+ // ── File Attachments ────────────────────────────────────────────────
192
+ listTaskFiles(taskId) {
193
+ return this.call("listProjectTaskFiles", {
194
+ projectId: this.config.projectId,
195
+ taskId
196
+ });
197
+ }
198
+ getAttachment(taskId, fileId) {
199
+ return this.call("getProjectAttachment", {
200
+ projectId: this.config.projectId,
201
+ taskId,
202
+ fileId
203
+ });
204
+ }
205
+ // ── Pull Requests ───────────────────────────────────────────────────
206
+ createPullRequest(params) {
207
+ return this.call("createProjectPullRequest", {
208
+ projectId: this.config.projectId,
209
+ ...params
210
+ });
211
+ }
212
+ // ── Subtasks ────────────────────────────────────────────────────────
213
+ createSubtask(params) {
214
+ return this.call("createProjectSubtask", {
215
+ projectId: this.config.projectId,
216
+ ...params
217
+ });
218
+ }
219
+ updateSubtask(params) {
220
+ return this.call("updateProjectSubtask", {
221
+ projectId: this.config.projectId,
222
+ ...params
223
+ });
224
+ }
225
+ listSubtasks(taskId) {
226
+ return this.call("listProjectSubtasks", {
227
+ projectId: this.config.projectId,
228
+ taskId
229
+ });
230
+ }
231
+ deleteSubtask(subtaskId) {
232
+ return this.call("deleteProjectSubtask", {
233
+ projectId: this.config.projectId,
234
+ subtaskId
235
+ });
236
+ }
237
+ // ── Dependencies ────────────────────────────────────────────────────
238
+ getDependencies(taskId) {
239
+ return this.call("getProjectTaskDependencies", {
240
+ projectId: this.config.projectId,
241
+ taskId
242
+ });
243
+ }
244
+ addDependency(params) {
245
+ return this.call("addProjectTaskDependency", {
246
+ projectId: this.config.projectId,
247
+ ...params
248
+ });
249
+ }
250
+ removeDependency(params) {
251
+ return this.call("removeProjectTaskDependency", {
252
+ projectId: this.config.projectId,
253
+ ...params
254
+ });
255
+ }
256
+ // ── Suggestions ─────────────────────────────────────────────────────
257
+ createSuggestion(params) {
258
+ return this.call("createProjectSuggestion", {
259
+ projectId: this.config.projectId,
260
+ ...params
261
+ });
262
+ }
263
+ // ── PTY Tunnel Relay (reuses the S2 pty:* envelope) ─────────────────
264
+ /**
265
+ * Poll target: returns the active cloud PTY session for a task once its ring
266
+ * buffer has frames. `sessionId` is resolved server-side from `taskId` (never
267
+ * accepted from the wire), preserving the one-active-session-per-task invariant.
268
+ */
269
+ getActivePtySession(taskId) {
270
+ return this.call("getActivePtySession", { taskId });
271
+ }
272
+ /** Fetch the ring-buffer snapshot for catch-up replay on (re)attach. */
273
+ ptyAttach(sessionId) {
274
+ return this.call("ptyAttach", { sessionId });
275
+ }
276
+ /**
277
+ * Join the session room so `pty:data` frames are delivered. Uses the standard
278
+ * quickdraw-core subscribe envelope; "Read" is sufficient for output streaming.
279
+ */
280
+ subscribeToSession(sessionId) {
281
+ const socket = this.socket;
282
+ if (!socket) throw new Error("Not connected");
283
+ socket.emit("agentSessionService:subscribe", {
284
+ entryId: sessionId,
285
+ requiredLevel: "Read"
286
+ });
287
+ }
288
+ /** Relay a stdin chunk to the cloud PTY (raw utf8, fire-and-forget). */
289
+ ptyInput(sessionId, data) {
290
+ this.emit("ptyInput", { sessionId, data });
291
+ }
292
+ /** Relay a terminal resize to the cloud PTY (fire-and-forget). */
293
+ ptyResize(sessionId, cols, rows) {
294
+ this.emit("ptyResize", { sessionId, cols, rows });
295
+ }
296
+ /** Subscribe to raw PTY output frames. Returns an unsubscribe function. */
297
+ onPtyData(handler) {
298
+ const socket = this.socket;
299
+ if (!socket) throw new Error("Not connected");
300
+ socket.on("pty:data", handler);
301
+ return () => {
302
+ socket.off("pty:data", handler);
303
+ };
304
+ }
305
+ // ── Connection lifecycle ────────────────────────────────────────────
306
+ disconnect() {
307
+ this.socket?.disconnect();
308
+ this.socket = null;
309
+ }
310
+ };
311
+
312
+ export {
313
+ ConveyorConnection
314
+ };
315
+ //# sourceMappingURL=chunk-UIDBFQNZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connection.ts"],"sourcesContent":["import { io, type Socket } from \"socket.io-client\";\n\nexport interface ConveyorMcpConfig {\n apiUrl: string;\n projectToken: string;\n projectId: string;\n}\n\n/** A single PTY output frame broadcast on the `pty:data` room event. */\nexport interface PtyDataChunk {\n sessionId: string;\n seq: number;\n data: string;\n cols?: number;\n rows?: number;\n}\n\n/** Ring-buffer snapshot returned by `ptyAttach` for catch-up replay. */\nexport interface PtyAttachSnapshot {\n sessionId: string;\n chunks: { seq: number; data: string }[];\n cols: number;\n rows: number;\n totalBytes: number;\n}\n\n/**\n * Result of `getActivePtySession`. `sessionId` is null until the cloud agent\n * has booted a PTY and produced at least one ring frame (readiness signal).\n */\nexport interface ActivePtySession {\n sessionId: string | null;\n cols?: number;\n rows?: number;\n}\n\ntype SocketCallback = (response: { success: boolean; data?: unknown; error?: string }) => void;\n\nexport class ConveyorConnection {\n private socket: Socket | null = null;\n private config: ConveyorMcpConfig;\n\n constructor(config: ConveyorMcpConfig) {\n this.config = config;\n }\n\n get projectId(): string {\n return this.config.projectId;\n }\n\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n let settled = false;\n let attempts = 0;\n const maxAttempts = 15;\n\n this.socket = io(this.config.apiUrl, {\n auth: {\n projectToken: this.config.projectToken,\n runnerMode: \"project\",\n },\n transports: [\"websocket\"],\n reconnection: true,\n reconnectionAttempts: Infinity,\n reconnectionDelay: 2000,\n reconnectionDelayMax: 30000,\n randomizationFactor: 0.3,\n extraHeaders: { \"ngrok-skip-browser-warning\": \"true\" },\n });\n\n this.socket.on(\"connect\", () => {\n if (!settled) {\n settled = true;\n // Subscribe to project room for events\n this.socket?.emit(\"projectService:subscribe\", { id: this.config.projectId });\n resolve();\n }\n });\n\n this.socket.on(\"connect_error\", (err) => {\n attempts++;\n if (!settled && attempts >= maxAttempts) {\n settled = true;\n reject(new Error(`Failed to connect: ${err.message}`));\n }\n });\n });\n }\n\n // ── Service method call (agentSessionService:*) ─────────────────────\n\n private call<T>(method: string, data: unknown): Promise<T> {\n const socket = this.socket;\n if (!socket) throw new Error(\"Not connected\");\n\n const TIMEOUT_MS = 15_000;\n\n return Promise.race([\n new Promise<T>((resolve, reject) => {\n socket.emit(`agentSessionService:${method}`, data, ((response: {\n success: boolean;\n data?: T;\n error?: string;\n }) => {\n if (response.success) {\n resolve(response.data as T);\n } else {\n reject(new Error(response.error ?? `${method} failed`));\n }\n }) as SocketCallback);\n }),\n new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`Request timed out: ${method}`));\n }, TIMEOUT_MS);\n }),\n ]);\n }\n\n /**\n * Fire-and-forget emit (no ack). The quickdraw-core server method handler\n * invokes the ack callback via optional chaining, so omitting it still runs\n * the full auth/schema/ACL pipeline — it just skips the response round-trip.\n * Used for high-frequency PTY input/resize so each keystroke does not arm a\n * 15s timeout timer.\n */\n private emit(method: string, data: unknown): void {\n const socket = this.socket;\n if (!socket) throw new Error(\"Not connected\");\n socket.emit(`agentSessionService:${method}`, data);\n }\n\n // ── Task Queries ────────────────────────────────────────────────────\n\n listTasks(params: { status?: string; assigneeId?: string; limit?: number }): Promise<unknown[]> {\n return this.call(\"listProjectTasks\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n getTask(taskId: string): Promise<unknown> {\n return this.call(\"getProjectTask\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n searchTasks(params: {\n tagNames?: string[];\n searchQuery?: string;\n statusFilters?: string[];\n limit?: number;\n }): Promise<unknown[]> {\n return this.call(\"searchProjectTasks\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n // ── Task Mutations ──────────────────────────────────────────────────\n\n createTask(params: {\n title: string;\n description?: string;\n plan?: string;\n status?: string;\n }): Promise<{ id: string; slug: string }> {\n return this.call(\"createProjectTask\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n updateTask(params: {\n taskId: string;\n title?: string;\n description?: string;\n plan?: string;\n status?: string;\n assignedUserId?: string | null;\n }): Promise<{ id: string; status: string }> {\n return this.call(\"updateProjectTask\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n // ── Build Management ────────────────────────────────────────────────\n\n startBuild(taskId: string): Promise<{ taskId: string; status: string }> {\n return this.call(\"startProjectBuild\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n stopBuild(taskId: string): Promise<{ taskId: string; stopped: boolean }> {\n return this.call(\"stopProjectBuild\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n getBuildStatus(taskId: string): Promise<{\n session: { status: string | null; agentRunnerStatus: string | null } | null;\n }> {\n // Uses getProjectTask and extracts the session field\n return this.call<Record<string, unknown>>(\"getProjectTask\", {\n projectId: this.config.projectId,\n taskId,\n }).then((task) => ({\n session:\n (task?.session as {\n status: string | null;\n agentRunnerStatus: string | null;\n }) ?? null,\n }));\n }\n\n // ── Chat ────────────────────────────────────────────────────────────\n\n getTaskChat(taskId: string, limit?: number): Promise<unknown[]> {\n // Uses getProjectTask and extracts chatMessages\n return this.call<Record<string, unknown>>(\"getProjectTask\", {\n projectId: this.config.projectId,\n taskId,\n }).then((task) => {\n const messages = (task?.chatMessages ?? []) as unknown[];\n return limit ? messages.slice(-limit) : messages;\n });\n }\n\n postToTaskChat(taskId: string, content: string): Promise<{ messageId: string }> {\n return this.call(\"postToProjectTaskChat\", {\n projectId: this.config.projectId,\n taskId,\n content,\n });\n }\n\n // ── CLI History ─────────────────────────────────────────────────────\n\n getTaskCli(\n taskId: string,\n limit?: number,\n source?: string,\n ): Promise<{ type: string; data: Record<string, unknown>; timestamp: string }[]> {\n return this.call(\"getProjectTaskCli\", {\n projectId: this.config.projectId,\n taskId,\n limit,\n source,\n });\n }\n\n // ── Project Info ────────────────────────────────────────────────────\n\n listTags(): Promise<{ id: string; name: string; color: string }[]> {\n return this.call(\"listProjectTags\", {\n projectId: this.config.projectId,\n });\n }\n\n getProjectSummary(): Promise<unknown> {\n return this.call(\"getProjectSummary\", {\n projectId: this.config.projectId,\n });\n }\n\n // ── Review Flow ─────────────────────────────────────────────────────\n\n async approveTask(taskId: string): Promise<{ status: string }> {\n // Determine next status based on current status\n const task = (await this.call(\"getProjectTask\", {\n projectId: this.config.projectId,\n taskId,\n })) as { status: string } | null;\n\n if (!task) throw new Error(\"Task not found\");\n\n const nextStatus = task.status === \"ReviewPR\" ? \"ReviewDev\" : \"Complete\";\n\n const result = await this.updateTask({ taskId, status: nextStatus });\n return { status: result.status };\n }\n\n async requestChanges(taskId: string, feedback: string): Promise<void> {\n // Post feedback to task chat then move to InProgress\n await this.postToTaskChat(taskId, feedback);\n await this.updateTask({ taskId, status: \"InProgress\" });\n }\n\n approveAndMergePR(\n childTaskId: string,\n ): Promise<{ merged: boolean; childTaskId: string; prNumber: number }> {\n return this.call(\"approveProjectMergePR\", {\n projectId: this.config.projectId,\n childTaskId,\n });\n }\n\n // ── File Attachments ────────────────────────────────────────────────\n\n listTaskFiles(taskId: string): Promise<unknown[]> {\n return this.call(\"listProjectTaskFiles\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n getAttachment(taskId: string, fileId: string): Promise<unknown> {\n return this.call(\"getProjectAttachment\", {\n projectId: this.config.projectId,\n taskId,\n fileId,\n });\n }\n\n // ── Pull Requests ───────────────────────────────────────────────────\n\n createPullRequest(params: {\n taskId: string;\n title: string;\n body: string;\n head?: string;\n base?: string;\n }): Promise<{ prNumber: number; prUrl: string }> {\n return this.call(\"createProjectPullRequest\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n // ── Subtasks ────────────────────────────────────────────────────────\n\n createSubtask(params: {\n parentTaskId: string;\n title: string;\n description?: string;\n plan?: string;\n ordinal?: number;\n storyPointValue?: number;\n }): Promise<{ id: string; slug: string }> {\n return this.call(\"createProjectSubtask\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n updateSubtask(params: {\n subtaskId: string;\n title?: string;\n description?: string;\n plan?: string;\n status?: string;\n ordinal?: number;\n storyPointValue?: number;\n }): Promise<{ id: string; status: string }> {\n return this.call(\"updateProjectSubtask\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n listSubtasks(taskId: string): Promise<unknown[]> {\n return this.call(\"listProjectSubtasks\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n deleteSubtask(subtaskId: string): Promise<{ deleted: boolean }> {\n return this.call(\"deleteProjectSubtask\", {\n projectId: this.config.projectId,\n subtaskId,\n });\n }\n\n // ── Dependencies ────────────────────────────────────────────────────\n\n getDependencies(taskId: string): Promise<unknown[]> {\n return this.call(\"getProjectTaskDependencies\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n addDependency(params: {\n taskId: string;\n dependsOnSlugOrId: string;\n }): Promise<{ success: boolean }> {\n return this.call(\"addProjectTaskDependency\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n removeDependency(params: {\n taskId: string;\n dependsOnSlugOrId: string;\n }): Promise<{ success: boolean }> {\n return this.call(\"removeProjectTaskDependency\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n // ── Suggestions ─────────────────────────────────────────────────────\n\n createSuggestion(params: {\n title: string;\n description?: string;\n tagNames?: string[];\n }): Promise<{ id: string; merged: boolean; mergedIntoId?: string }> {\n return this.call(\"createProjectSuggestion\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n // ── PTY Tunnel Relay (reuses the S2 pty:* envelope) ─────────────────\n\n /**\n * Poll target: returns the active cloud PTY session for a task once its ring\n * buffer has frames. `sessionId` is resolved server-side from `taskId` (never\n * accepted from the wire), preserving the one-active-session-per-task invariant.\n */\n getActivePtySession(taskId: string): Promise<ActivePtySession> {\n return this.call(\"getActivePtySession\", { taskId });\n }\n\n /** Fetch the ring-buffer snapshot for catch-up replay on (re)attach. */\n ptyAttach(sessionId: string): Promise<PtyAttachSnapshot> {\n return this.call(\"ptyAttach\", { sessionId });\n }\n\n /**\n * Join the session room so `pty:data` frames are delivered. Uses the standard\n * quickdraw-core subscribe envelope; \"Read\" is sufficient for output streaming.\n */\n subscribeToSession(sessionId: string): void {\n const socket = this.socket;\n if (!socket) throw new Error(\"Not connected\");\n socket.emit(\"agentSessionService:subscribe\", {\n entryId: sessionId,\n requiredLevel: \"Read\",\n });\n }\n\n /** Relay a stdin chunk to the cloud PTY (raw utf8, fire-and-forget). */\n ptyInput(sessionId: string, data: string): void {\n this.emit(\"ptyInput\", { sessionId, data });\n }\n\n /** Relay a terminal resize to the cloud PTY (fire-and-forget). */\n ptyResize(sessionId: string, cols: number, rows: number): void {\n this.emit(\"ptyResize\", { sessionId, cols, rows });\n }\n\n /** Subscribe to raw PTY output frames. Returns an unsubscribe function. */\n onPtyData(handler: (chunk: PtyDataChunk) => void): () => void {\n const socket = this.socket;\n if (!socket) throw new Error(\"Not connected\");\n socket.on(\"pty:data\", handler as (...args: unknown[]) => void);\n return () => {\n socket.off(\"pty:data\", handler as (...args: unknown[]) => void);\n };\n }\n\n // ── Connection lifecycle ────────────────────────────────────────────\n\n disconnect(): void {\n this.socket?.disconnect();\n this.socket = null;\n }\n}\n"],"mappings":"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","names":[]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node