driggsby 0.1.12 → 0.1.13
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/.gitignore +2 -0
- package/README.md +16 -11
- package/binary-install.js +212 -0
- package/binary.js +128 -0
- package/install.js +4 -0
- package/npm-shrinkwrap.json +545 -0
- package/package.json +53 -41
- package/run-driggsby.js +4 -0
- package/dist/auth/browser.js +0 -31
- package/dist/auth/config.js +0 -23
- package/dist/auth/discovery.js +0 -42
- package/dist/auth/dpop.js +0 -44
- package/dist/auth/login.js +0 -126
- package/dist/auth/loopback.js +0 -157
- package/dist/auth/oauth.js +0 -136
- package/dist/auth/pkce.js +0 -12
- package/dist/auth/url-security.js +0 -16
- package/dist/broker/authentication.js +0 -49
- package/dist/broker/client.js +0 -148
- package/dist/broker/daemon.js +0 -65
- package/dist/broker/file-secret-store.js +0 -130
- package/dist/broker/installation.js +0 -154
- package/dist/broker/ipc.js +0 -12
- package/dist/broker/keyring-secret-store.js +0 -42
- package/dist/broker/launch.js +0 -35
- package/dist/broker/lock.js +0 -84
- package/dist/broker/remote-mcp.js +0 -129
- package/dist/broker/remote-session.js +0 -173
- package/dist/broker/resolve-secret-store.js +0 -52
- package/dist/broker/secret-store.js +0 -13
- package/dist/broker/server.js +0 -177
- package/dist/broker/session.js +0 -31
- package/dist/broker/test-support.js +0 -258
- package/dist/broker/types.js +0 -1
- package/dist/cli/commands/broker-daemon.js +0 -4
- package/dist/cli/commands/login.js +0 -35
- package/dist/cli/commands/logout.js +0 -20
- package/dist/cli/commands/status.js +0 -13
- package/dist/cli/format.js +0 -84
- package/dist/index.js +0 -39
- package/dist/lib/json-file.js +0 -36
- package/dist/lib/retry.js +0 -22
- package/dist/lib/runtime-paths.js +0 -62
- package/dist/lib/user-guidance.js +0 -19
- package/dist/shim/server.js +0 -143
- package/dist/shim/stdio-transport.js +0 -106
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-deprecated */
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdtemp } from "node:fs/promises";
|
|
4
|
-
import http from "node:http";
|
|
5
|
-
import os from "node:os";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { decodeProtectedHeader, importJWK, jwtVerify, } from "jose";
|
|
8
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
9
|
-
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
10
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
-
export async function makeBrokerRuntimePaths(prefix) {
|
|
12
|
-
const baseDir = await mkdtemp(path.join(os.tmpdir(), prefix));
|
|
13
|
-
return {
|
|
14
|
-
configDir: path.join(baseDir, "config"),
|
|
15
|
-
stateDir: path.join(baseDir, "state"),
|
|
16
|
-
metadataPath: path.join(baseDir, "config", "broker-metadata.json"),
|
|
17
|
-
lockPath: path.join(baseDir, "state", "broker.lock"),
|
|
18
|
-
socketPath: path.join(baseDir, "state", "broker.sock"),
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
export async function startFakeRemoteService() {
|
|
22
|
-
let currentAccessToken = "refreshed-access-token";
|
|
23
|
-
let tokenRequestCount = 0;
|
|
24
|
-
let listToolsCount = 0;
|
|
25
|
-
let callToolCount = 0;
|
|
26
|
-
let exposeSavingsTool = false;
|
|
27
|
-
const server = http.createServer((request, response) => {
|
|
28
|
-
void handleRequest(request, response);
|
|
29
|
-
});
|
|
30
|
-
async function handleRequest(request, response) {
|
|
31
|
-
const requestUrl = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
32
|
-
if (request.method === "GET" &&
|
|
33
|
-
requestUrl.pathname === "/.well-known/oauth-authorization-server") {
|
|
34
|
-
response.setHeader("content-type", "application/json");
|
|
35
|
-
response.end(JSON.stringify({
|
|
36
|
-
issuer: originForServer(server),
|
|
37
|
-
authorization_endpoint: `${originForServer(server)}/authorize`,
|
|
38
|
-
token_endpoint: `${originForServer(server)}/token`,
|
|
39
|
-
registration_endpoint: `${originForServer(server)}/register`,
|
|
40
|
-
response_types_supported: ["code"],
|
|
41
|
-
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
42
|
-
token_endpoint_auth_methods_supported: ["none"],
|
|
43
|
-
dpop_signing_alg_values_supported: ["ES256"],
|
|
44
|
-
code_challenge_methods_supported: ["S256"],
|
|
45
|
-
scopes_supported: ["driggsby.default"],
|
|
46
|
-
}));
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (request.method === "POST" && requestUrl.pathname === "/token") {
|
|
50
|
-
tokenRequestCount += 1;
|
|
51
|
-
const body = await readRequestBody(request);
|
|
52
|
-
const params = new URLSearchParams(body);
|
|
53
|
-
assert.equal(params.get("client_id"), "client-123");
|
|
54
|
-
assert.equal(params.get("grant_type"), "refresh_token");
|
|
55
|
-
assert.equal(params.get("refresh_token"), "refresh-token-1");
|
|
56
|
-
assert.equal(params.get("resource"), `${originForServer(server)}/mcp`);
|
|
57
|
-
await assertDpopProof(request.headers["dpop"], {
|
|
58
|
-
authorizationHeader: undefined,
|
|
59
|
-
expectedMethod: "POST",
|
|
60
|
-
expectedUrl: `${originForServer(server)}/token`,
|
|
61
|
-
});
|
|
62
|
-
currentAccessToken = "refreshed-access-token";
|
|
63
|
-
response.setHeader("content-type", "application/json");
|
|
64
|
-
response.end(JSON.stringify({
|
|
65
|
-
access_token: currentAccessToken,
|
|
66
|
-
token_type: "DPoP",
|
|
67
|
-
expires_in: 3600,
|
|
68
|
-
refresh_token: "refresh-token-2",
|
|
69
|
-
scope: "driggsby.default",
|
|
70
|
-
}));
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
if (requestUrl.pathname === "/mcp" && request.method === "GET") {
|
|
74
|
-
response.statusCode = 405;
|
|
75
|
-
response.setHeader("content-type", "application/json");
|
|
76
|
-
response.end(JSON.stringify({
|
|
77
|
-
error: {
|
|
78
|
-
code: -32000,
|
|
79
|
-
message: "Method not allowed.",
|
|
80
|
-
},
|
|
81
|
-
id: null,
|
|
82
|
-
jsonrpc: "2.0",
|
|
83
|
-
}));
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
if (requestUrl.pathname === "/mcp" && request.method === "POST") {
|
|
87
|
-
if (request.headers.authorization !== `DPoP ${currentAccessToken}`) {
|
|
88
|
-
response.statusCode = 401;
|
|
89
|
-
response.end();
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
await assertDpopProof(request.headers["dpop"], {
|
|
93
|
-
authorizationHeader: request.headers.authorization,
|
|
94
|
-
expectedMethod: "POST",
|
|
95
|
-
expectedUrl: `${originForServer(server)}/mcp`,
|
|
96
|
-
});
|
|
97
|
-
const mcpServer = new Server({
|
|
98
|
-
name: "fake-remote-mcp",
|
|
99
|
-
version: "0.1.0",
|
|
100
|
-
}, {
|
|
101
|
-
capabilities: {
|
|
102
|
-
tools: {},
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
mcpServer.setRequestHandler(ListToolsRequestSchema, () => {
|
|
106
|
-
listToolsCount += 1;
|
|
107
|
-
return {
|
|
108
|
-
tools: [
|
|
109
|
-
{
|
|
110
|
-
description: "Echo a balance amount back to the caller.",
|
|
111
|
-
inputSchema: {
|
|
112
|
-
additionalProperties: false,
|
|
113
|
-
properties: {
|
|
114
|
-
amount: {
|
|
115
|
-
type: "string",
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
required: ["amount"],
|
|
119
|
-
type: "object",
|
|
120
|
-
},
|
|
121
|
-
name: "echo_balance",
|
|
122
|
-
},
|
|
123
|
-
...(exposeSavingsTool
|
|
124
|
-
? [
|
|
125
|
-
{
|
|
126
|
-
description: "Project a savings target over time.",
|
|
127
|
-
inputSchema: {
|
|
128
|
-
additionalProperties: false,
|
|
129
|
-
properties: {},
|
|
130
|
-
type: "object",
|
|
131
|
-
},
|
|
132
|
-
name: "savings_projection",
|
|
133
|
-
},
|
|
134
|
-
]
|
|
135
|
-
: []),
|
|
136
|
-
],
|
|
137
|
-
};
|
|
138
|
-
});
|
|
139
|
-
mcpServer.setRequestHandler(CallToolRequestSchema, (mcpRequest) => {
|
|
140
|
-
callToolCount += 1;
|
|
141
|
-
const amount = readAmountArgument(mcpRequest.params.arguments);
|
|
142
|
-
return {
|
|
143
|
-
content: [
|
|
144
|
-
{
|
|
145
|
-
text: `echoed ${amount}`,
|
|
146
|
-
type: "text",
|
|
147
|
-
},
|
|
148
|
-
],
|
|
149
|
-
};
|
|
150
|
-
});
|
|
151
|
-
const transport = new StreamableHTTPServerTransport({});
|
|
152
|
-
await mcpServer.connect(toTransport(transport));
|
|
153
|
-
await transport.handleRequest(request, response, await readJsonBody(request));
|
|
154
|
-
response.once("close", () => {
|
|
155
|
-
void transport.close();
|
|
156
|
-
void mcpServer.close();
|
|
157
|
-
});
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
response.statusCode = 404;
|
|
161
|
-
response.end();
|
|
162
|
-
}
|
|
163
|
-
await new Promise((resolve, reject) => {
|
|
164
|
-
server.once("error", reject);
|
|
165
|
-
server.listen(0, "127.0.0.1", resolve);
|
|
166
|
-
});
|
|
167
|
-
return {
|
|
168
|
-
async close() {
|
|
169
|
-
await new Promise((resolve, reject) => {
|
|
170
|
-
server.close((error) => {
|
|
171
|
-
if (error) {
|
|
172
|
-
reject(error);
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
resolve();
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
},
|
|
179
|
-
get callToolCount() {
|
|
180
|
-
return callToolCount;
|
|
181
|
-
},
|
|
182
|
-
get listToolsCount() {
|
|
183
|
-
return listToolsCount;
|
|
184
|
-
},
|
|
185
|
-
get origin() {
|
|
186
|
-
return originForServer(server);
|
|
187
|
-
},
|
|
188
|
-
setExposeSavingsTool(value) {
|
|
189
|
-
exposeSavingsTool = value;
|
|
190
|
-
},
|
|
191
|
-
get tokenRequestCount() {
|
|
192
|
-
return tokenRequestCount;
|
|
193
|
-
},
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
function readAmountArgument(argumentsValue) {
|
|
197
|
-
if (typeof argumentsValue !== "object" ||
|
|
198
|
-
argumentsValue === null ||
|
|
199
|
-
Array.isArray(argumentsValue)) {
|
|
200
|
-
throw new Error("Expected object arguments.");
|
|
201
|
-
}
|
|
202
|
-
const amount = argumentsValue["amount"];
|
|
203
|
-
if (typeof amount !== "string") {
|
|
204
|
-
throw new Error("Expected a string amount argument.");
|
|
205
|
-
}
|
|
206
|
-
return amount;
|
|
207
|
-
}
|
|
208
|
-
async function readJsonBody(request) {
|
|
209
|
-
const body = await readRequestBody(request);
|
|
210
|
-
if (body.length === 0) {
|
|
211
|
-
return undefined;
|
|
212
|
-
}
|
|
213
|
-
return JSON.parse(body);
|
|
214
|
-
}
|
|
215
|
-
async function assertDpopProof(proof, options) {
|
|
216
|
-
if (typeof proof !== "string") {
|
|
217
|
-
throw new Error("Expected a DPoP proof header.");
|
|
218
|
-
}
|
|
219
|
-
const protectedHeader = decodeProtectedHeader(proof);
|
|
220
|
-
const verificationKey = await importJWK(protectedHeader.jwk, "ES256");
|
|
221
|
-
const { payload } = await jwtVerify(proof, verificationKey, {
|
|
222
|
-
algorithms: ["ES256"],
|
|
223
|
-
typ: "dpop+jwt",
|
|
224
|
-
});
|
|
225
|
-
assert.equal(payload["htm"], options.expectedMethod);
|
|
226
|
-
assert.equal(payload["htu"], options.expectedUrl);
|
|
227
|
-
if (options.authorizationHeader !== undefined) {
|
|
228
|
-
const [, accessToken] = options.authorizationHeader.split(" ");
|
|
229
|
-
if (accessToken === undefined) {
|
|
230
|
-
throw new Error("Expected an access token in the authorization header.");
|
|
231
|
-
}
|
|
232
|
-
assert.ok(typeof payload["ath"] === "string");
|
|
233
|
-
assert.equal(payload["ath"], await sha256Base64Url(accessToken));
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
async function sha256Base64Url(value) {
|
|
237
|
-
return (await import("node:crypto"))
|
|
238
|
-
.createHash("sha256")
|
|
239
|
-
.update(value)
|
|
240
|
-
.digest("base64url");
|
|
241
|
-
}
|
|
242
|
-
async function readRequestBody(request) {
|
|
243
|
-
let body = "";
|
|
244
|
-
for await (const chunk of request) {
|
|
245
|
-
body += String(chunk);
|
|
246
|
-
}
|
|
247
|
-
return body;
|
|
248
|
-
}
|
|
249
|
-
function originForServer(server) {
|
|
250
|
-
const address = server.address();
|
|
251
|
-
if (address === null || typeof address === "string") {
|
|
252
|
-
throw new Error("Expected an HTTP server address.");
|
|
253
|
-
}
|
|
254
|
-
return `http://127.0.0.1:${address.port}`;
|
|
255
|
-
}
|
|
256
|
-
function toTransport(transport) {
|
|
257
|
-
return transport;
|
|
258
|
-
}
|
package/dist/broker/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { loginBroker } from "../../auth/login.js";
|
|
2
|
-
import { resolveSecretStore } from "../../broker/resolve-secret-store.js";
|
|
3
|
-
import { ensureRuntimeDirectories } from "../../lib/runtime-paths.js";
|
|
4
|
-
export async function runLoginCommand(runtimePaths) {
|
|
5
|
-
await ensureRuntimeDirectories(runtimePaths);
|
|
6
|
-
const resolvedSecretStore = await resolveSecretStore(runtimePaths);
|
|
7
|
-
const secretStore = resolvedSecretStore.store;
|
|
8
|
-
process.stdout.write("Opening Driggsby sign-in in your browser...\n");
|
|
9
|
-
if (resolvedSecretStore.notice !== null) {
|
|
10
|
-
process.stdout.write(`${resolvedSecretStore.notice}\n`);
|
|
11
|
-
}
|
|
12
|
-
const result = await loginBroker(runtimePaths, {
|
|
13
|
-
onBrowserPrompt: ({ browserOpened, signInUrl }) => {
|
|
14
|
-
if (!browserOpened) {
|
|
15
|
-
process.stdout.write([
|
|
16
|
-
"Your browser did not open automatically.",
|
|
17
|
-
"Open this URL to finish connecting Driggsby:",
|
|
18
|
-
signInUrl,
|
|
19
|
-
"",
|
|
20
|
-
].join("\n"));
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
secretStore,
|
|
24
|
-
});
|
|
25
|
-
process.stdout.write([
|
|
26
|
-
"Local Driggsby broker is connected.",
|
|
27
|
-
`broker id: ${result.brokerId}`,
|
|
28
|
-
`dpop thumbprint: ${result.dpopThumbprint}`,
|
|
29
|
-
`server: ${result.session.issuer}`,
|
|
30
|
-
`resource: ${result.session.resource}`,
|
|
31
|
-
`scope: ${result.session.scope}`,
|
|
32
|
-
`access token expires: ${result.session.accessTokenExpiresAt}`,
|
|
33
|
-
"",
|
|
34
|
-
].join("\n"));
|
|
35
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { getBrokerStatus, shutdownBroker } from "../../broker/client.js";
|
|
2
|
-
import { clearBrokerInstallation } from "../../broker/installation.js";
|
|
3
|
-
import { resolveSecretStoreForLogout } from "../../broker/resolve-secret-store.js";
|
|
4
|
-
export async function runLogoutCommand(runtimePaths) {
|
|
5
|
-
const resolvedSecretStore = await resolveSecretStoreForLogout(runtimePaths);
|
|
6
|
-
const secretStore = resolvedSecretStore.store;
|
|
7
|
-
const clientOptions = {
|
|
8
|
-
runtimePaths,
|
|
9
|
-
secretStore,
|
|
10
|
-
};
|
|
11
|
-
const runningStatus = await getBrokerStatus(clientOptions);
|
|
12
|
-
if (runningStatus !== null && !(await shutdownBroker(clientOptions))) {
|
|
13
|
-
throw new Error("The local Driggsby broker did not shut down cleanly. Close active MCP sessions and try again.");
|
|
14
|
-
}
|
|
15
|
-
await clearBrokerInstallation(runtimePaths, secretStore);
|
|
16
|
-
if (resolvedSecretStore.notice !== null) {
|
|
17
|
-
process.stdout.write(`${resolvedSecretStore.notice}\n`);
|
|
18
|
-
}
|
|
19
|
-
process.stdout.write("Local Driggsby broker state cleared.\n");
|
|
20
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { getBrokerStatus } from "../../broker/client.js";
|
|
2
|
-
import { resolveBrokerStatusForDisplay } from "../../broker/installation.js";
|
|
3
|
-
import { resolveSecretStore } from "../../broker/resolve-secret-store.js";
|
|
4
|
-
import { formatStatusText } from "../format.js";
|
|
5
|
-
export async function runStatusCommand(runtimePaths) {
|
|
6
|
-
const secretStore = (await resolveSecretStore(runtimePaths)).store;
|
|
7
|
-
const liveStatus = await getBrokerStatus({
|
|
8
|
-
runtimePaths,
|
|
9
|
-
secretStore,
|
|
10
|
-
});
|
|
11
|
-
const status = await resolveBrokerStatusForDisplay(runtimePaths, secretStore, liveStatus);
|
|
12
|
-
process.stdout.write(formatStatusText(status));
|
|
13
|
-
}
|
package/dist/cli/format.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { DRIGGSBY_LOGIN_COMMAND, DRIGGSBY_STATUS_COMMAND, } from "../lib/user-guidance.js";
|
|
2
|
-
export function formatStatusText(status) {
|
|
3
|
-
const remoteAccessState = resolveRemoteAccessState(status);
|
|
4
|
-
const remoteAccessDetail = resolveRemoteAccessDetail(status, remoteAccessState);
|
|
5
|
-
const nextStepCommand = resolveNextStepCommand(status, remoteAccessState);
|
|
6
|
-
const lines = [
|
|
7
|
-
"Local Driggsby broker",
|
|
8
|
-
`remote MCP forwarding: ${status.remoteMcpReady ? "ready" : "not ready"}`,
|
|
9
|
-
`remote access state: ${formatRemoteAccessState(remoteAccessState)}`,
|
|
10
|
-
`what this means: ${remoteAccessDetail}`,
|
|
11
|
-
];
|
|
12
|
-
if (!status.installed) {
|
|
13
|
-
lines.push("local broker setup: incomplete");
|
|
14
|
-
}
|
|
15
|
-
if (!status.brokerRunning) {
|
|
16
|
-
lines.push("local broker service: not running");
|
|
17
|
-
}
|
|
18
|
-
if (nextStepCommand) {
|
|
19
|
-
lines.push(`next step: run \`${nextStepCommand}\``);
|
|
20
|
-
}
|
|
21
|
-
if (status.remoteSession) {
|
|
22
|
-
lines.push(`saved access token expires at: ${status.remoteSession.accessTokenExpiresAt}`);
|
|
23
|
-
}
|
|
24
|
-
return `${lines.join("\n")}\n`;
|
|
25
|
-
}
|
|
26
|
-
function formatRemoteAccessState(remoteAccessState) {
|
|
27
|
-
switch (remoteAccessState) {
|
|
28
|
-
case "ready":
|
|
29
|
-
return "authenticated";
|
|
30
|
-
case "not_connected":
|
|
31
|
-
return "not connected";
|
|
32
|
-
case "reauth_required":
|
|
33
|
-
return "re-authentication required";
|
|
34
|
-
case "temporarily_unavailable":
|
|
35
|
-
return "temporarily unavailable";
|
|
36
|
-
}
|
|
37
|
-
return remoteAccessState;
|
|
38
|
-
}
|
|
39
|
-
function resolveRemoteAccessState(status) {
|
|
40
|
-
const runtimeStatus = status;
|
|
41
|
-
return runtimeStatus.remoteAccessState ?? inferLegacyRemoteAccessState(status);
|
|
42
|
-
}
|
|
43
|
-
function resolveRemoteAccessDetail(status, remoteAccessState) {
|
|
44
|
-
const runtimeStatus = status;
|
|
45
|
-
return runtimeStatus.remoteAccessDetail ??
|
|
46
|
-
inferLegacyRemoteAccessDetail(status, remoteAccessState);
|
|
47
|
-
}
|
|
48
|
-
function resolveNextStepCommand(status, remoteAccessState) {
|
|
49
|
-
const runtimeStatus = status;
|
|
50
|
-
return runtimeStatus.nextStepCommand ??
|
|
51
|
-
inferLegacyNextStepCommand(remoteAccessState);
|
|
52
|
-
}
|
|
53
|
-
function inferLegacyRemoteAccessState(status) {
|
|
54
|
-
if (status.remoteMcpReady) {
|
|
55
|
-
return "ready";
|
|
56
|
-
}
|
|
57
|
-
if (status.remoteSession) {
|
|
58
|
-
return "temporarily_unavailable";
|
|
59
|
-
}
|
|
60
|
-
return "not_connected";
|
|
61
|
-
}
|
|
62
|
-
function inferLegacyRemoteAccessDetail(status, remoteAccessState) {
|
|
63
|
-
if (remoteAccessState === "ready") {
|
|
64
|
-
return "Remote MCP forwarding is ready to use.";
|
|
65
|
-
}
|
|
66
|
-
if (remoteAccessState === "not_connected") {
|
|
67
|
-
return "Driggsby does not have a saved remote session yet.";
|
|
68
|
-
}
|
|
69
|
-
if (status.remoteSession) {
|
|
70
|
-
return "Remote MCP forwarding is not ready yet. Driggsby may still need to refresh the saved remote session before forwarding can run.";
|
|
71
|
-
}
|
|
72
|
-
return "Remote MCP forwarding is not ready to use yet.";
|
|
73
|
-
}
|
|
74
|
-
function inferLegacyNextStepCommand(remoteAccessState) {
|
|
75
|
-
switch (remoteAccessState) {
|
|
76
|
-
case "ready":
|
|
77
|
-
return undefined;
|
|
78
|
-
case "not_connected":
|
|
79
|
-
case "reauth_required":
|
|
80
|
-
return DRIGGSBY_LOGIN_COMMAND;
|
|
81
|
-
case "temporarily_unavailable":
|
|
82
|
-
return DRIGGSBY_STATUS_COMMAND;
|
|
83
|
-
}
|
|
84
|
-
}
|
package/dist/index.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { runBrokerDaemonCommand } from "./cli/commands/broker-daemon.js";
|
|
4
|
-
import { runLoginCommand } from "./cli/commands/login.js";
|
|
5
|
-
import { runLogoutCommand } from "./cli/commands/logout.js";
|
|
6
|
-
import { runStatusCommand } from "./cli/commands/status.js";
|
|
7
|
-
import { runMcpServerCommand } from "./shim/server.js";
|
|
8
|
-
import { resolveRuntimePaths } from "./lib/runtime-paths.js";
|
|
9
|
-
const ENTRYPOINT_PATH = fileURLToPath(import.meta.url);
|
|
10
|
-
async function main() {
|
|
11
|
-
const [, , ...args] = process.argv;
|
|
12
|
-
const command = args[0];
|
|
13
|
-
const runtimePaths = resolveRuntimePaths();
|
|
14
|
-
switch (command) {
|
|
15
|
-
case "login":
|
|
16
|
-
await runLoginCommand(runtimePaths);
|
|
17
|
-
return;
|
|
18
|
-
case "logout":
|
|
19
|
-
await runLogoutCommand(runtimePaths);
|
|
20
|
-
return;
|
|
21
|
-
case "status":
|
|
22
|
-
await runStatusCommand(runtimePaths);
|
|
23
|
-
return;
|
|
24
|
-
case "mcp-server":
|
|
25
|
-
await runMcpServerCommand(runtimePaths, ENTRYPOINT_PATH);
|
|
26
|
-
return;
|
|
27
|
-
case "broker-daemon":
|
|
28
|
-
await runBrokerDaemonCommand(runtimePaths);
|
|
29
|
-
return;
|
|
30
|
-
default:
|
|
31
|
-
process.stderr.write("Usage: driggsby <mcp-server|login|logout|status|broker-daemon>\n");
|
|
32
|
-
process.exitCode = 1;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
void main().catch((error) => {
|
|
36
|
-
const message = error instanceof Error ? error.message : "Driggsby command failed.";
|
|
37
|
-
process.stderr.write(`${message}\n`);
|
|
38
|
-
process.exitCode = 1;
|
|
39
|
-
});
|
package/dist/lib/json-file.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
export async function readJsonFile(filePath) {
|
|
4
|
-
try {
|
|
5
|
-
const contents = await fs.readFile(filePath, "utf8");
|
|
6
|
-
return JSON.parse(contents);
|
|
7
|
-
}
|
|
8
|
-
catch (error) {
|
|
9
|
-
if (isNotFoundError(error)) {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
throw error;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
export async function writeJsonFile(filePath, value) {
|
|
16
|
-
const tempPath = `${filePath}.${process.pid}.tmp`;
|
|
17
|
-
const contents = `${JSON.stringify(value, null, 2)}\n`;
|
|
18
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
19
|
-
await fs.writeFile(tempPath, contents, { encoding: "utf8", mode: 0o600 });
|
|
20
|
-
await fs.rename(tempPath, filePath);
|
|
21
|
-
}
|
|
22
|
-
export async function removeFileIfPresent(filePath) {
|
|
23
|
-
try {
|
|
24
|
-
await fs.rm(filePath);
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
if (!isNotFoundError(error)) {
|
|
28
|
-
throw error;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function isNotFoundError(error) {
|
|
33
|
-
return (error instanceof Error &&
|
|
34
|
-
"code" in error &&
|
|
35
|
-
error.code === "ENOENT");
|
|
36
|
-
}
|
package/dist/lib/retry.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
|
-
export async function retryOperation(operation, options) {
|
|
3
|
-
let lastError;
|
|
4
|
-
const totalAttempts = options.delaysMs.length;
|
|
5
|
-
for (const [index, delayMs] of options.delaysMs.entries()) {
|
|
6
|
-
if (delayMs > 0) {
|
|
7
|
-
await sleep(delayMs);
|
|
8
|
-
}
|
|
9
|
-
try {
|
|
10
|
-
return await operation(index + 1, totalAttempts);
|
|
11
|
-
}
|
|
12
|
-
catch (error) {
|
|
13
|
-
lastError = error;
|
|
14
|
-
if (index === totalAttempts - 1 || !options.shouldRetry(error)) {
|
|
15
|
-
throw error;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
throw lastError instanceof Error
|
|
20
|
-
? lastError
|
|
21
|
-
: new Error("The retry operation did not complete successfully.");
|
|
22
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
export function resolveRuntimePaths(options = {}) {
|
|
6
|
-
const env = options.env ?? process.env;
|
|
7
|
-
const platform = options.platform ?? process.platform;
|
|
8
|
-
const homeDir = options.homeDir ?? os.homedir();
|
|
9
|
-
const username = options.username ?? os.userInfo().username;
|
|
10
|
-
const allowEnvOverrides = options.allowEnvOverrides ?? false;
|
|
11
|
-
const configDir = allowEnvOverrides && env["DRIGGSBY_CONFIG_DIR"]
|
|
12
|
-
? env["DRIGGSBY_CONFIG_DIR"]
|
|
13
|
-
: defaultConfigDir(platform, homeDir, env);
|
|
14
|
-
const stateDir = allowEnvOverrides && env["DRIGGSBY_STATE_DIR"]
|
|
15
|
-
? env["DRIGGSBY_STATE_DIR"]
|
|
16
|
-
: defaultStateDir(platform, homeDir, env);
|
|
17
|
-
const pathModule = platform === "win32" ? path.win32 : path;
|
|
18
|
-
return {
|
|
19
|
-
configDir,
|
|
20
|
-
stateDir,
|
|
21
|
-
metadataPath: pathModule.join(configDir, "broker-metadata.json"),
|
|
22
|
-
socketPath: defaultSocketPath(platform, stateDir, username),
|
|
23
|
-
lockPath: pathModule.join(stateDir, "broker.lock"),
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
export async function ensureRuntimeDirectories(runtimePaths) {
|
|
27
|
-
await fs.mkdir(runtimePaths.configDir, { recursive: true, mode: 0o700 });
|
|
28
|
-
await fs.mkdir(runtimePaths.stateDir, { recursive: true, mode: 0o700 });
|
|
29
|
-
}
|
|
30
|
-
function defaultConfigDir(platform, homeDir, env) {
|
|
31
|
-
const pathModule = platform === "win32" ? path.win32 : path;
|
|
32
|
-
switch (platform) {
|
|
33
|
-
case "darwin":
|
|
34
|
-
return pathModule.join(homeDir, "Library", "Application Support", "driggsby");
|
|
35
|
-
case "win32":
|
|
36
|
-
return pathModule.join(env["APPDATA"] ?? path.join(homeDir, "AppData", "Roaming"), "Driggsby");
|
|
37
|
-
default:
|
|
38
|
-
return pathModule.join(env["XDG_CONFIG_HOME"] ?? path.join(homeDir, ".config"), "driggsby");
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
function defaultStateDir(platform, homeDir, env) {
|
|
42
|
-
const pathModule = platform === "win32" ? path.win32 : path;
|
|
43
|
-
switch (platform) {
|
|
44
|
-
case "darwin":
|
|
45
|
-
return pathModule.join(homeDir, "Library", "Application Support", "driggsby");
|
|
46
|
-
case "win32":
|
|
47
|
-
return pathModule.join(env["LOCALAPPDATA"] ?? path.join(homeDir, "AppData", "Local"), "Driggsby");
|
|
48
|
-
default:
|
|
49
|
-
return pathModule.join(env["XDG_STATE_HOME"] ?? path.join(homeDir, ".local", "state"), "driggsby");
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function defaultSocketPath(platform, stateDir, username) {
|
|
53
|
-
if (platform === "win32") {
|
|
54
|
-
const win32Path = path.win32;
|
|
55
|
-
const userHash = createHash("sha256")
|
|
56
|
-
.update(username)
|
|
57
|
-
.digest("hex")
|
|
58
|
-
.slice(0, 16);
|
|
59
|
-
return win32Path.join("\\\\.\\pipe", `driggsby-broker-${userHash}`);
|
|
60
|
-
}
|
|
61
|
-
return path.join(stateDir, "broker.sock");
|
|
62
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export const DRIGGSBY_LOGIN_COMMAND = "npx driggsby@latest login";
|
|
2
|
-
export const DRIGGSBY_STATUS_COMMAND = "npx driggsby@latest status";
|
|
3
|
-
export function buildReauthenticationRequiredMessage(reason) {
|
|
4
|
-
return `${ensureTrailingPeriod(reason)} Please re-authenticate by running \`${DRIGGSBY_LOGIN_COMMAND}\`.`;
|
|
5
|
-
}
|
|
6
|
-
export function buildBrokerInvestigationMessage(reason) {
|
|
7
|
-
return `${ensureTrailingPeriod(reason)} Investigate broker readiness by running \`${DRIGGSBY_STATUS_COMMAND}\`. If authentication expired, re-authenticate with \`${DRIGGSBY_LOGIN_COMMAND}\`.`;
|
|
8
|
-
}
|
|
9
|
-
export function errorMessageIncludesReauthenticationCommand(error) {
|
|
10
|
-
return (error instanceof Error &&
|
|
11
|
-
error.message.includes(DRIGGSBY_LOGIN_COMMAND));
|
|
12
|
-
}
|
|
13
|
-
export function formatRetryWindow(delaysMs) {
|
|
14
|
-
const totalDelayMs = delaysMs.reduce((sum, delay) => sum + delay, 0);
|
|
15
|
-
return `${(totalDelayMs / 1_000).toFixed(1)} seconds`;
|
|
16
|
-
}
|
|
17
|
-
function ensureTrailingPeriod(value) {
|
|
18
|
-
return /[.!?]$/.test(value) ? value : `${value}.`;
|
|
19
|
-
}
|