@rallycry/conveyor-mcp 5.0.3 → 5.0.4
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 +6 -0
- package/dist/chunk-5KU3QS6A.js +45 -0
- package/dist/{chunk-XLDG5QEX.js → chunk-EUCQ26F7.js} +32 -3
- package/dist/cli.js +7 -10
- package/dist/{connection-B7CwszOV.d.ts → connection-jZ8nSRwk.d.ts} +20 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/tunnel-cli.js +5 -3
- package/dist/tunnel.d.ts +2 -1
- package/dist/wait-cli.js +5 -3
- package/dist/wait-runner.d.ts +2 -1
- package/dist/wait.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,6 +76,12 @@ CONVEYOR_PROJECT_ID = "<project-id>"
|
|
|
76
76
|
| `CONVEYOR_PROJECT_ID` | no | Default project for unqualified tools. Omit it for multi-project mode. |
|
|
77
77
|
| `CONVEYOR_SUBPROJECT_ID` | no | Default board. Unqualified `create_task`/`list_tasks`/`search_tasks` target it. |
|
|
78
78
|
|
|
79
|
+
## How the server exits
|
|
80
|
+
|
|
81
|
+
The server's lifetime is its stdin pipe. It exits when stdin reaches EOF, on
|
|
82
|
+
`SIGINT` or `SIGTERM`, and when its parent process changes — so a client that
|
|
83
|
+
force-quits, crashes, or is torn down never leaves a server behind.
|
|
84
|
+
|
|
79
85
|
## Why `npx -y …@latest`
|
|
80
86
|
|
|
81
87
|
Every form above launches the server through
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/lifecycle.ts
|
|
2
|
+
function watchForClientExit(onExit, deps = {}) {
|
|
3
|
+
const stdin = deps.stdin ?? process.stdin;
|
|
4
|
+
const signals = deps.signals ?? process;
|
|
5
|
+
const getPpid = deps.getPpid ?? (() => process.ppid);
|
|
6
|
+
const intervalMs = deps.intervalMs ?? 3e4;
|
|
7
|
+
const watchStdin = deps.watchStdin ?? true;
|
|
8
|
+
let fired = false;
|
|
9
|
+
const fire = () => {
|
|
10
|
+
if (fired) return;
|
|
11
|
+
fired = true;
|
|
12
|
+
onExit();
|
|
13
|
+
};
|
|
14
|
+
signals.on("SIGINT", fire);
|
|
15
|
+
signals.on("SIGTERM", fire);
|
|
16
|
+
signals.on("SIGHUP", fire);
|
|
17
|
+
if (watchStdin && stdin.isTTY !== true) {
|
|
18
|
+
if (deps.resumeStdin) stdin.resume?.();
|
|
19
|
+
stdin.on("end", fire);
|
|
20
|
+
stdin.on("close", fire);
|
|
21
|
+
}
|
|
22
|
+
const initialPpid = getPpid();
|
|
23
|
+
const orphanCheck = setInterval(() => {
|
|
24
|
+
if (getPpid() !== initialPpid) fire();
|
|
25
|
+
}, intervalMs);
|
|
26
|
+
orphanCheck.unref?.();
|
|
27
|
+
return fire;
|
|
28
|
+
}
|
|
29
|
+
function installLifecycle(conn, deps = {}) {
|
|
30
|
+
const exit = deps.exit ?? ((code) => process.exit(code));
|
|
31
|
+
let stopping = false;
|
|
32
|
+
function stop(code = 0) {
|
|
33
|
+
if (stopping) return;
|
|
34
|
+
stopping = true;
|
|
35
|
+
conn.disconnect();
|
|
36
|
+
exit(code);
|
|
37
|
+
}
|
|
38
|
+
watchForClientExit(() => stop(), deps);
|
|
39
|
+
return stop;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
watchForClientExit,
|
|
44
|
+
installLifecycle
|
|
45
|
+
};
|
|
@@ -49,10 +49,14 @@ function enrichToolError(error, fallback) {
|
|
|
49
49
|
var ConveyorConnection = class {
|
|
50
50
|
socket = null;
|
|
51
51
|
config;
|
|
52
|
+
socketFactory;
|
|
52
53
|
/** project slug → id, for resolving `<project>/<card>` card paths. */
|
|
53
54
|
projectSlugIds = /* @__PURE__ */ new Map();
|
|
54
|
-
|
|
55
|
+
/** Session rooms this connection has joined, replayed after every reconnect. */
|
|
56
|
+
subscribedSessionIds = /* @__PURE__ */ new Set();
|
|
57
|
+
constructor(config, socketFactory = io) {
|
|
55
58
|
this.config = config;
|
|
59
|
+
this.socketFactory = socketFactory;
|
|
56
60
|
}
|
|
57
61
|
get projectId() {
|
|
58
62
|
return this.resolveProjectId();
|
|
@@ -86,12 +90,32 @@ var ConveyorConnection = class {
|
|
|
86
90
|
if (Array.isArray(record.data)) return record.data;
|
|
87
91
|
return [];
|
|
88
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Re-join every room this connection needs.
|
|
95
|
+
*
|
|
96
|
+
* Runs on the FIRST connect and on every reconnect. A socket.io reconnect
|
|
97
|
+
* starts a fresh server-side socket that belongs to no rooms, and Cloud Run
|
|
98
|
+
* cuts each websocket at its 60-minute request limit, so a subscribe sent
|
|
99
|
+
* only once left long-lived MCP connections silently outside the project room
|
|
100
|
+
* for the rest of the day.
|
|
101
|
+
*/
|
|
102
|
+
rejoinRooms() {
|
|
103
|
+
const socket = this.socket;
|
|
104
|
+
if (!socket) return;
|
|
105
|
+
socket.emit("projectService:subscribe", { id: this.config.projectId });
|
|
106
|
+
for (const sessionId of this.subscribedSessionIds) {
|
|
107
|
+
socket.emit("agentSessionService:subscribe", {
|
|
108
|
+
entryId: sessionId,
|
|
109
|
+
requiredLevel: "Read"
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
89
113
|
connect() {
|
|
90
114
|
return new Promise((resolve, reject) => {
|
|
91
115
|
let settled = false;
|
|
92
116
|
let attempts = 0;
|
|
93
117
|
const maxAttempts = 15;
|
|
94
|
-
this.socket =
|
|
118
|
+
this.socket = this.socketFactory(
|
|
95
119
|
this.config.apiUrl,
|
|
96
120
|
buildConveyorSocketOptions({
|
|
97
121
|
projectToken: this.config.projectToken,
|
|
@@ -99,12 +123,15 @@ var ConveyorConnection = class {
|
|
|
99
123
|
})
|
|
100
124
|
);
|
|
101
125
|
this.socket.on("connect", () => {
|
|
126
|
+
this.rejoinRooms();
|
|
102
127
|
if (!settled) {
|
|
103
128
|
settled = true;
|
|
104
|
-
this.socket?.emit("projectService:subscribe", { id: this.config.projectId });
|
|
105
129
|
resolve();
|
|
106
130
|
}
|
|
107
131
|
});
|
|
132
|
+
this.socket.on("disconnect", (reason) => {
|
|
133
|
+
if (reason === "io server disconnect") this.socket?.connect();
|
|
134
|
+
});
|
|
108
135
|
this.socket.on("connect_error", (err) => {
|
|
109
136
|
const message = err?.message ?? "unknown error";
|
|
110
137
|
if (!settled && AUTH_ERROR_RE.test(message)) {
|
|
@@ -943,6 +970,7 @@ var ConveyorConnection = class {
|
|
|
943
970
|
subscribeToSession(sessionId) {
|
|
944
971
|
const socket = this.socket;
|
|
945
972
|
if (!socket) throw new Error("Not connected");
|
|
973
|
+
this.subscribedSessionIds.add(sessionId);
|
|
946
974
|
socket.emit("agentSessionService:subscribe", {
|
|
947
975
|
entryId: sessionId,
|
|
948
976
|
requiredLevel: "Read"
|
|
@@ -1013,6 +1041,7 @@ var ConveyorConnection = class {
|
|
|
1013
1041
|
disconnect() {
|
|
1014
1042
|
this.socket?.disconnect();
|
|
1015
1043
|
this.socket = null;
|
|
1044
|
+
this.subscribedSessionIds.clear();
|
|
1016
1045
|
}
|
|
1017
1046
|
};
|
|
1018
1047
|
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
installLifecycle
|
|
4
|
+
} from "./chunk-5KU3QS6A.js";
|
|
2
5
|
import {
|
|
3
6
|
ConveyorConnection
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EUCQ26F7.js";
|
|
5
8
|
|
|
6
9
|
// src/cli.ts
|
|
7
10
|
import { createRequire } from "module";
|
|
@@ -69,7 +72,7 @@ function registerProjectTools(server2, conn2) {
|
|
|
69
72
|
// src/tools/connection.ts
|
|
70
73
|
import { z as z4 } from "zod";
|
|
71
74
|
|
|
72
|
-
// ../shared/dist/chunk-
|
|
75
|
+
// ../shared/dist/chunk-VQSAISHG.js
|
|
73
76
|
import { z as z2 } from "zod";
|
|
74
77
|
var DEFAULT_SONNET_MODEL = "claude-sonnet-5";
|
|
75
78
|
var DEFAULT_OPUS_MODEL = "claude-opus-5";
|
|
@@ -5497,11 +5500,5 @@ var server = new McpServer({
|
|
|
5497
5500
|
registerAllTools(server, conn);
|
|
5498
5501
|
var transport = new StdioServerTransport();
|
|
5499
5502
|
await server.connect(transport);
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
process.exit(0);
|
|
5503
|
-
});
|
|
5504
|
-
process.on("SIGTERM", () => {
|
|
5505
|
-
conn.disconnect();
|
|
5506
|
-
process.exit(0);
|
|
5507
|
-
});
|
|
5503
|
+
var stop = installLifecycle(conn);
|
|
5504
|
+
server.server.onclose = () => stop();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { io, Socket } from 'socket.io-client';
|
|
1
2
|
import { ListDriveFilesResponse, ReadDriveFileResponse, DriveFileDTO, DeleteDriveFileResponse, ProjectIntegrationsSummary, GaAnalyticsSummaryDTO, ProjectChannelDTO, ReadChannelMessagesResponse, PostChannelMessageResponse, ListMeetingsResponse, MeetingForAgent, ReadMeetingTranscriptResponse, MeetingChecklistItemForAgent } from '@project/shared';
|
|
2
3
|
|
|
3
4
|
interface ConveyorMcpConfig {
|
|
@@ -310,12 +311,20 @@ interface PrioritySummary {
|
|
|
310
311
|
color: string;
|
|
311
312
|
description?: string | null;
|
|
312
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Creates the underlying socket. Defaults to socket.io's `io`; tests inject a
|
|
316
|
+
* fake so the reconnect wiring in `connect()` can be driven without a server.
|
|
317
|
+
*/
|
|
318
|
+
type ConveyorSocketFactory = (url: string, options: Parameters<typeof io>[1]) => Pick<Socket, "on" | "emit" | "close" | "connect" | "disconnect">;
|
|
313
319
|
declare class ConveyorConnection {
|
|
314
320
|
private socket;
|
|
315
321
|
private config;
|
|
322
|
+
private socketFactory;
|
|
316
323
|
/** project slug → id, for resolving `<project>/<card>` card paths. */
|
|
317
324
|
private projectSlugIds;
|
|
318
|
-
|
|
325
|
+
/** Session rooms this connection has joined, replayed after every reconnect. */
|
|
326
|
+
private subscribedSessionIds;
|
|
327
|
+
constructor(config: ConveyorMcpConfig, socketFactory?: ConveyorSocketFactory);
|
|
319
328
|
get projectId(): string;
|
|
320
329
|
private resolveProjectId;
|
|
321
330
|
/** The configured default board (CONVEYOR_SUBPROJECT_ID), if any. */
|
|
@@ -329,6 +338,16 @@ declare class ConveyorConnection {
|
|
|
329
338
|
*/
|
|
330
339
|
private resolveSubProjectId;
|
|
331
340
|
private normalizeProjectList;
|
|
341
|
+
/**
|
|
342
|
+
* Re-join every room this connection needs.
|
|
343
|
+
*
|
|
344
|
+
* Runs on the FIRST connect and on every reconnect. A socket.io reconnect
|
|
345
|
+
* starts a fresh server-side socket that belongs to no rooms, and Cloud Run
|
|
346
|
+
* cuts each websocket at its 60-minute request limit, so a subscribe sent
|
|
347
|
+
* only once left long-lived MCP connections silently outside the project room
|
|
348
|
+
* for the rest of the day.
|
|
349
|
+
*/
|
|
350
|
+
private rejoinRooms;
|
|
332
351
|
connect(): Promise<void>;
|
|
333
352
|
private call;
|
|
334
353
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { A as ActivePtySession, d as ConveyorConnection, e as ConveyorMcpConfig, P as PtyAttachSnapshot, c as PtyDataChunk } from './connection-
|
|
1
|
+
export { A as ActivePtySession, d as ConveyorConnection, e as ConveyorMcpConfig, P as PtyAttachSnapshot, c as PtyDataChunk } from './connection-jZ8nSRwk.js';
|
|
2
2
|
export { AttachTunnelOptions, ResolvedPtySession, RunTunnelOptions, TunnelConnection, TunnelHandle, TunnelSession, TunnelTty, WaitForPtySessionOptions, attachTunnel, runTunnel, waitForPtySession } from './tunnel.js';
|
|
3
|
+
import 'socket.io-client';
|
|
3
4
|
import '@project/shared';
|
package/dist/index.js
CHANGED
package/dist/tunnel-cli.js
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runTunnel
|
|
4
4
|
} from "./chunk-HIVOGGE3.js";
|
|
5
|
+
import {
|
|
6
|
+
watchForClientExit
|
|
7
|
+
} from "./chunk-5KU3QS6A.js";
|
|
5
8
|
import {
|
|
6
9
|
ConveyorConnection
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-EUCQ26F7.js";
|
|
8
11
|
|
|
9
12
|
// src/tunnel-cli.ts
|
|
10
13
|
var HELP = `conveyor-tunnel \u2014 attach your local terminal to a cloud Claude Code session.
|
|
@@ -125,8 +128,7 @@ function cleanupAndExit(code) {
|
|
|
125
128
|
conn.disconnect();
|
|
126
129
|
process.exit(code);
|
|
127
130
|
}
|
|
128
|
-
|
|
129
|
-
process.on("SIGTERM", () => cleanupAndExit(0));
|
|
131
|
+
watchForClientExit(() => cleanupAndExit(0), { watchStdin: false });
|
|
130
132
|
try {
|
|
131
133
|
await conn.connect();
|
|
132
134
|
log("Connected to Conveyor API.");
|
package/dist/tunnel.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { A as ActivePtySession, P as PtyAttachSnapshot, c as PtyDataChunk } from './connection-
|
|
1
|
+
import { A as ActivePtySession, P as PtyAttachSnapshot, c as PtyDataChunk } from './connection-jZ8nSRwk.js';
|
|
2
|
+
import 'socket.io-client';
|
|
2
3
|
import '@project/shared';
|
|
3
4
|
|
|
4
5
|
/**
|
package/dist/wait-cli.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
watchForClientExit
|
|
4
|
+
} from "./chunk-5KU3QS6A.js";
|
|
2
5
|
import {
|
|
3
6
|
ConveyorConnection
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EUCQ26F7.js";
|
|
5
8
|
import {
|
|
6
9
|
runWait
|
|
7
10
|
} from "./chunk-OPZL4NDT.js";
|
|
@@ -257,8 +260,7 @@ function interrupt() {
|
|
|
257
260
|
exiting = true;
|
|
258
261
|
emitResult({ reason: "interrupted" }, () => shutdown(0));
|
|
259
262
|
}
|
|
260
|
-
|
|
261
|
-
process.on("SIGTERM", interrupt);
|
|
263
|
+
watchForClientExit(interrupt, { resumeStdin: true });
|
|
262
264
|
try {
|
|
263
265
|
await conn.connect();
|
|
264
266
|
} catch (err) {
|
package/dist/wait-runner.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { C as CardCollectionPage, a as CardCollectionDelta } from './connection-
|
|
1
|
+
import { C as CardCollectionPage, a as CardCollectionDelta } from './connection-jZ8nSRwk.js';
|
|
2
2
|
import { WaitFilter, WaitResult } from './wait.js';
|
|
3
|
+
import 'socket.io-client';
|
|
3
4
|
import '@project/shared';
|
|
4
5
|
|
|
5
6
|
/**
|
package/dist/wait.d.ts
CHANGED