@dontcode2/backend 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -2
- package/dist/auth-device.d.ts +43 -0
- package/dist/auth.d.ts +102 -0
- package/dist/cache.d.ts +36 -0
- package/dist/chunk-6DFTM26Y.js +448 -0
- package/dist/chunk-6DFTM26Y.js.map +1 -0
- package/dist/chunk-LBN4R3GH.js +716 -0
- package/dist/chunk-LBN4R3GH.js.map +1 -0
- package/dist/cli.cjs +1206 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/{mock/cli.d.cts → cli.d.ts} +1 -0
- package/dist/cli.js +95 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +40 -0
- package/dist/cookies.d.ts +36 -0
- package/dist/credentials.d.ts +36 -0
- package/dist/db.d.ts +48 -0
- package/dist/errors.d.ts +38 -0
- package/dist/http.d.ts +52 -0
- package/dist/index.cjs +164 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -588
- package/dist/index.js +26 -536
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.cjs +1116 -0
- package/dist/mcp/index.cjs.map +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +10 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +6 -0
- package/dist/mock/cli.d.ts +1 -0
- package/dist/mock/db-query.d.ts +67 -0
- package/dist/mock/index.d.ts +17 -36
- package/dist/mock/{index.d.cts → server.d.ts} +3 -5
- package/dist/node.cjs +1160 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.ts +8 -0
- package/dist/node.js +28 -0
- package/dist/node.js.map +1 -0
- package/dist/realtime.d.ts +29 -0
- package/dist/session.d.ts +115 -0
- package/dist/storage.d.ts +46 -0
- package/dist/types.d.ts +184 -0
- package/package.json +19 -2
- package/dist/index.d.cts +0 -588
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DontCodeError,
|
|
3
|
+
Transport,
|
|
4
|
+
dontcode,
|
|
5
|
+
isDontCodeError
|
|
6
|
+
} from "./chunk-LBN4R3GH.js";
|
|
7
|
+
|
|
8
|
+
// src/credentials.ts
|
|
9
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { dirname, join } from "path";
|
|
12
|
+
function configDir() {
|
|
13
|
+
return process.env.DONTCODE_CONFIG_DIR || join(homedir(), ".dontcode");
|
|
14
|
+
}
|
|
15
|
+
function credentialsPath() {
|
|
16
|
+
return join(configDir(), "credentials.json");
|
|
17
|
+
}
|
|
18
|
+
function readStore() {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(credentialsPath(), "utf8"));
|
|
21
|
+
} catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function writeStore(store) {
|
|
26
|
+
const path = credentialsPath();
|
|
27
|
+
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
28
|
+
writeFileSync(path, JSON.stringify(store, null, 2), { mode: 384 });
|
|
29
|
+
}
|
|
30
|
+
function loadCredential(baseUrl2) {
|
|
31
|
+
return readStore()[baseUrl2] ?? null;
|
|
32
|
+
}
|
|
33
|
+
function saveCredential(cred) {
|
|
34
|
+
const store = readStore();
|
|
35
|
+
store[cred.base_url] = cred;
|
|
36
|
+
writeStore(store);
|
|
37
|
+
}
|
|
38
|
+
function clearCredential(baseUrl2) {
|
|
39
|
+
const store = readStore();
|
|
40
|
+
delete store[baseUrl2];
|
|
41
|
+
writeStore(store);
|
|
42
|
+
}
|
|
43
|
+
function isExpired(cred, skewMs = 3e4) {
|
|
44
|
+
return new Date(cred.expires_at).getTime() - skewMs <= Date.now();
|
|
45
|
+
}
|
|
46
|
+
function resolveActiveToken(baseUrl2) {
|
|
47
|
+
const env = process.env.DONTCODE_API_KEY;
|
|
48
|
+
if (env) return { token: env, source: "env" };
|
|
49
|
+
const cred = loadCredential(baseUrl2);
|
|
50
|
+
if (cred && !isExpired(cred)) {
|
|
51
|
+
return {
|
|
52
|
+
token: cred.access_token,
|
|
53
|
+
source: "device",
|
|
54
|
+
projectId: cred.project_id,
|
|
55
|
+
expiresAt: cred.expires_at
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return { source: "none" };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/auth-device.ts
|
|
62
|
+
var START_PATH = "/api/v1/auth/device/start";
|
|
63
|
+
var TOKEN_PATH = "/api/v1/auth/device/token";
|
|
64
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
65
|
+
async function startDeviceAuth(baseUrl2, clientName) {
|
|
66
|
+
const transport = new Transport({ baseUrl: baseUrl2 });
|
|
67
|
+
return transport.json(START_PATH, { client_name: clientName });
|
|
68
|
+
}
|
|
69
|
+
async function pollDeviceToken(baseUrl2, start, opts = {}) {
|
|
70
|
+
const transport = new Transport({ baseUrl: baseUrl2 });
|
|
71
|
+
let intervalMs = Math.max(1, start.interval) * 1e3;
|
|
72
|
+
const expiry = Date.now() + start.expires_in * 1e3;
|
|
73
|
+
const deadline = opts.maxWaitMs && opts.maxWaitMs > 0 ? Math.min(expiry, Date.now() + opts.maxWaitMs) : expiry;
|
|
74
|
+
while (Date.now() < deadline) {
|
|
75
|
+
await sleep(intervalMs);
|
|
76
|
+
try {
|
|
77
|
+
return await transport.json(TOKEN_PATH, {
|
|
78
|
+
device_code: start.device_code
|
|
79
|
+
});
|
|
80
|
+
} catch (err) {
|
|
81
|
+
if (err instanceof DontCodeError) {
|
|
82
|
+
const message = err.body?.error ?? err.message;
|
|
83
|
+
if (err.status === 428 || message.includes("authorization_pending")) {
|
|
84
|
+
opts.onPending?.();
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (message.includes("slow_down")) {
|
|
88
|
+
intervalMs += 2e3;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const stillOpen = Date.now() < expiry;
|
|
96
|
+
throw new DontCodeError(408, {
|
|
97
|
+
error: stillOpen ? "Still waiting for browser approval." : "Device login timed out before approval. Start again.",
|
|
98
|
+
code: stillOpen ? "WaitTimeout" : "Timeout"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async function openBrowser(url) {
|
|
102
|
+
try {
|
|
103
|
+
const { spawn } = await import("child_process");
|
|
104
|
+
const platform = process.platform;
|
|
105
|
+
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
106
|
+
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
107
|
+
spawn(command, args, { stdio: "ignore", detached: true }).unref();
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function login(options) {
|
|
112
|
+
const log = options.log ?? (() => {
|
|
113
|
+
});
|
|
114
|
+
const start = await startDeviceAuth(options.baseUrl, options.clientName);
|
|
115
|
+
log(
|
|
116
|
+
`
|
|
117
|
+
Open this URL to connect:
|
|
118
|
+
${start.verification_uri_complete}
|
|
119
|
+
|
|
120
|
+
Confirm this code matches:
|
|
121
|
+
${start.user_code}
|
|
122
|
+
|
|
123
|
+
Waiting for approval...
|
|
124
|
+
`
|
|
125
|
+
);
|
|
126
|
+
if (options.open !== false) await openBrowser(start.verification_uri_complete);
|
|
127
|
+
const token = await pollDeviceToken(options.baseUrl, start);
|
|
128
|
+
const cred = {
|
|
129
|
+
access_token: token.access_token,
|
|
130
|
+
project_id: token.project_id,
|
|
131
|
+
expires_at: new Date(Date.now() + token.expires_in * 1e3).toISOString(),
|
|
132
|
+
base_url: options.baseUrl
|
|
133
|
+
};
|
|
134
|
+
saveCredential(cred);
|
|
135
|
+
return cred;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/mcp/server.ts
|
|
139
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
140
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
141
|
+
import { z } from "zod";
|
|
142
|
+
var SERVER_NAME = "dontcode-backend";
|
|
143
|
+
var SERVER_VERSION = "0.3.0";
|
|
144
|
+
function baseUrl() {
|
|
145
|
+
return (process.env.DONTCODE_API_URL || "https://backend.dontcode.co").replace(/\/+$/, "");
|
|
146
|
+
}
|
|
147
|
+
var pendingFlow = null;
|
|
148
|
+
function text(value) {
|
|
149
|
+
const body = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
150
|
+
return { content: [{ type: "text", text: body }] };
|
|
151
|
+
}
|
|
152
|
+
function failure(message) {
|
|
153
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
154
|
+
}
|
|
155
|
+
function describeError(err) {
|
|
156
|
+
if (isDontCodeError(err)) {
|
|
157
|
+
if (err.status === 401) {
|
|
158
|
+
return "Not signed in or the session expired. Use the `auth_login` tool, approve in the browser, then `auth_wait`.";
|
|
159
|
+
}
|
|
160
|
+
if (err.status === 403) {
|
|
161
|
+
return `Your project role does not allow that. (${err.message})`;
|
|
162
|
+
}
|
|
163
|
+
if (err.rateLimited) {
|
|
164
|
+
return `Rate limited. ${err.message}`;
|
|
165
|
+
}
|
|
166
|
+
return err.message;
|
|
167
|
+
}
|
|
168
|
+
return err instanceof Error ? err.message : "Unknown error";
|
|
169
|
+
}
|
|
170
|
+
function requireClient() {
|
|
171
|
+
const active = resolveActiveToken(baseUrl());
|
|
172
|
+
if (!active.token) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
"Not signed in. Use the `auth_login` tool first (or set DONTCODE_API_KEY for non-interactive use)."
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return dontcode({ apiKey: active.token, baseUrl: baseUrl() });
|
|
178
|
+
}
|
|
179
|
+
async function run(fn) {
|
|
180
|
+
try {
|
|
181
|
+
return text(await fn());
|
|
182
|
+
} catch (err) {
|
|
183
|
+
return failure(describeError(err));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function createMcpServer() {
|
|
187
|
+
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
|
|
188
|
+
const tool = (name, config, handler) => server.registerTool(name, config, handler);
|
|
189
|
+
tool(
|
|
190
|
+
"auth_login",
|
|
191
|
+
{
|
|
192
|
+
title: "Sign in to DontCode",
|
|
193
|
+
description: "Start a browser sign-in. Returns a URL and a short code; tell the user to open the URL, confirm the code matches, pick a project, and approve. Then call `auth_wait`. Not needed if DONTCODE_API_KEY is set.",
|
|
194
|
+
inputSchema: {
|
|
195
|
+
client_name: z.string().optional().describe('Label shown to the user on the approval screen, e.g. "Claude Code".')
|
|
196
|
+
},
|
|
197
|
+
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
198
|
+
},
|
|
199
|
+
async ({ client_name }) => run(async () => {
|
|
200
|
+
const start = await startDeviceAuth(baseUrl(), client_name ?? "Claude Code (MCP)");
|
|
201
|
+
pendingFlow = start;
|
|
202
|
+
await openBrowser(start.verification_uri_complete);
|
|
203
|
+
return {
|
|
204
|
+
message: "Ask the user to open this URL, confirm the code, choose a project, and approve. Then call auth_wait.",
|
|
205
|
+
verification_uri: start.verification_uri_complete,
|
|
206
|
+
user_code: start.user_code,
|
|
207
|
+
expires_in_seconds: start.expires_in
|
|
208
|
+
};
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
tool(
|
|
212
|
+
"auth_wait",
|
|
213
|
+
{
|
|
214
|
+
title: "Wait for sign-in approval",
|
|
215
|
+
description: "Poll for the result of `auth_login`. Returns connected once the user approves in the browser, or asks you to call it again if still pending. Safe to call repeatedly.",
|
|
216
|
+
inputSchema: {},
|
|
217
|
+
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
218
|
+
},
|
|
219
|
+
async () => run(async () => {
|
|
220
|
+
if (!pendingFlow) {
|
|
221
|
+
return { status: "no_login_in_progress", hint: "Call auth_login first." };
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const token = await pollDeviceToken(baseUrl(), pendingFlow, {
|
|
225
|
+
maxWaitMs: 5e4
|
|
226
|
+
});
|
|
227
|
+
saveCredential({
|
|
228
|
+
access_token: token.access_token,
|
|
229
|
+
project_id: token.project_id,
|
|
230
|
+
expires_at: new Date(Date.now() + token.expires_in * 1e3).toISOString(),
|
|
231
|
+
base_url: baseUrl()
|
|
232
|
+
});
|
|
233
|
+
pendingFlow = null;
|
|
234
|
+
return { status: "connected", project_id: token.project_id };
|
|
235
|
+
} catch (err) {
|
|
236
|
+
if (isDontCodeError(err) && err.code === "WaitTimeout") {
|
|
237
|
+
return {
|
|
238
|
+
status: "pending",
|
|
239
|
+
hint: "Still waiting for approval. Ask the user to approve, then call auth_wait again."
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
pendingFlow = null;
|
|
243
|
+
throw err;
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
);
|
|
247
|
+
tool(
|
|
248
|
+
"auth_status",
|
|
249
|
+
{
|
|
250
|
+
title: "Check the current session",
|
|
251
|
+
description: "Validate the current credential and report the project, your role, and what you are allowed to do.",
|
|
252
|
+
inputSchema: {},
|
|
253
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
254
|
+
},
|
|
255
|
+
async () => run(async () => {
|
|
256
|
+
const active = resolveActiveToken(baseUrl());
|
|
257
|
+
if (!active.token) {
|
|
258
|
+
return { signed_in: false, hint: "Use auth_login to sign in." };
|
|
259
|
+
}
|
|
260
|
+
const client = dontcode({ apiKey: active.token, baseUrl: baseUrl() });
|
|
261
|
+
const info = await client.auth.info();
|
|
262
|
+
return { signed_in: true, source: active.source, ...info };
|
|
263
|
+
})
|
|
264
|
+
);
|
|
265
|
+
tool(
|
|
266
|
+
"auth_logout",
|
|
267
|
+
{
|
|
268
|
+
title: "Forget the cached session",
|
|
269
|
+
description: "Remove the locally cached device token for this gateway.",
|
|
270
|
+
inputSchema: {},
|
|
271
|
+
annotations: { readOnlyHint: false, destructiveHint: true }
|
|
272
|
+
},
|
|
273
|
+
async () => run(async () => {
|
|
274
|
+
clearCredential(baseUrl());
|
|
275
|
+
pendingFlow = null;
|
|
276
|
+
return { ok: true };
|
|
277
|
+
})
|
|
278
|
+
);
|
|
279
|
+
tool(
|
|
280
|
+
"db_query",
|
|
281
|
+
{
|
|
282
|
+
title: "Query the database",
|
|
283
|
+
description: "Read rows from a table with a structured query (no raw SQL). Supports where/select/orderBy/limit/offset and count.",
|
|
284
|
+
inputSchema: {
|
|
285
|
+
table: z.string(),
|
|
286
|
+
operation: z.enum(["find", "findMany", "findFirst", "findOne", "count"]).default("find"),
|
|
287
|
+
where: z.record(z.string(), z.any()).optional(),
|
|
288
|
+
select: z.array(z.string()).optional(),
|
|
289
|
+
orderBy: z.record(z.string(), z.enum(["asc", "desc"])).optional(),
|
|
290
|
+
limit: z.number().int().positive().max(1e3).optional(),
|
|
291
|
+
offset: z.number().int().nonnegative().optional()
|
|
292
|
+
},
|
|
293
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
294
|
+
},
|
|
295
|
+
async ({ table, operation, where, select, orderBy, limit, offset }) => run(async () => {
|
|
296
|
+
const t = requireClient().db(table);
|
|
297
|
+
if (operation === "count") return { count: await t.count({ where }) };
|
|
298
|
+
const options = { where, select, orderBy, limit, offset };
|
|
299
|
+
if (operation === "findFirst" || operation === "findOne") {
|
|
300
|
+
return { row: await t.findFirst(options) };
|
|
301
|
+
}
|
|
302
|
+
return { rows: await t.find(options) };
|
|
303
|
+
})
|
|
304
|
+
);
|
|
305
|
+
tool(
|
|
306
|
+
"db_insert",
|
|
307
|
+
{
|
|
308
|
+
title: "Insert a row",
|
|
309
|
+
description: "Insert one row into a table. Returns the new row id.",
|
|
310
|
+
inputSchema: { table: z.string(), data: z.record(z.string(), z.any()) },
|
|
311
|
+
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
312
|
+
},
|
|
313
|
+
async ({ table, data }) => run(async () => requireClient().db(table).insert(data))
|
|
314
|
+
);
|
|
315
|
+
tool(
|
|
316
|
+
"db_update",
|
|
317
|
+
{
|
|
318
|
+
title: "Update rows",
|
|
319
|
+
description: "Update rows matching a where clause. Returns the number of rows changed.",
|
|
320
|
+
inputSchema: {
|
|
321
|
+
table: z.string(),
|
|
322
|
+
where: z.record(z.string(), z.any()),
|
|
323
|
+
data: z.record(z.string(), z.any())
|
|
324
|
+
},
|
|
325
|
+
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
326
|
+
},
|
|
327
|
+
async ({ table, where, data }) => run(async () => requireClient().db(table).update({ where, data }))
|
|
328
|
+
);
|
|
329
|
+
tool(
|
|
330
|
+
"db_delete",
|
|
331
|
+
{
|
|
332
|
+
title: "Delete rows",
|
|
333
|
+
description: "Delete rows matching a where clause. Destructive: confirm with the user before calling. Returns the number of rows deleted.",
|
|
334
|
+
inputSchema: { table: z.string(), where: z.record(z.string(), z.any()) },
|
|
335
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
|
|
336
|
+
},
|
|
337
|
+
async ({ table, where }) => run(async () => requireClient().db(table).delete({ where }))
|
|
338
|
+
);
|
|
339
|
+
tool(
|
|
340
|
+
"db_migrate",
|
|
341
|
+
{
|
|
342
|
+
title: "Run a schema migration",
|
|
343
|
+
description: "Apply DDL (CREATE/ALTER/DROP TABLE, indexes, etc.) to the project database. Destructive and schema-shaping: confirm with the user, and note it needs an admin/owner role on device-token sessions.",
|
|
344
|
+
inputSchema: { sql: z.string() },
|
|
345
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
|
|
346
|
+
},
|
|
347
|
+
async ({ sql }) => run(async () => requireClient().db.migrate({ sql }))
|
|
348
|
+
);
|
|
349
|
+
const bucketArg = z.enum(["public", "private"]).default("private");
|
|
350
|
+
const bucketOf = (client, bucket) => bucket === "public" ? client.storage.public : client.storage.private;
|
|
351
|
+
tool(
|
|
352
|
+
"storage_list",
|
|
353
|
+
{
|
|
354
|
+
title: "List files",
|
|
355
|
+
description: "List objects in a storage bucket, optionally under a prefix.",
|
|
356
|
+
inputSchema: { bucket: bucketArg, prefix: z.string().optional() },
|
|
357
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
358
|
+
},
|
|
359
|
+
async ({ bucket, prefix }) => run(async () => bucketOf(requireClient(), bucket).list(prefix))
|
|
360
|
+
);
|
|
361
|
+
tool(
|
|
362
|
+
"storage_get_url",
|
|
363
|
+
{
|
|
364
|
+
title: "Get a public URL",
|
|
365
|
+
description: "Get the permanent public URL for an object in the public bucket.",
|
|
366
|
+
inputSchema: { path: z.string() },
|
|
367
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
368
|
+
},
|
|
369
|
+
async ({ path }) => run(async () => requireClient().storage.public.getUrl(path))
|
|
370
|
+
);
|
|
371
|
+
tool(
|
|
372
|
+
"storage_temporary_url",
|
|
373
|
+
{
|
|
374
|
+
title: "Get a temporary URL",
|
|
375
|
+
description: "Get a short-lived signed URL for an object (default 300s, max 7 days).",
|
|
376
|
+
inputSchema: {
|
|
377
|
+
bucket: bucketArg,
|
|
378
|
+
path: z.string(),
|
|
379
|
+
expires_in: z.number().int().positive().optional()
|
|
380
|
+
},
|
|
381
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
382
|
+
},
|
|
383
|
+
async ({ bucket, path, expires_in }) => run(async () => bucketOf(requireClient(), bucket).getTemporaryUrl(path, expires_in))
|
|
384
|
+
);
|
|
385
|
+
tool(
|
|
386
|
+
"storage_upload",
|
|
387
|
+
{
|
|
388
|
+
title: "Upload a text file",
|
|
389
|
+
description: "Upload UTF-8 text content to a path. For binary or large files, use storage_temporary_url + a direct PUT instead.",
|
|
390
|
+
inputSchema: {
|
|
391
|
+
bucket: bucketArg,
|
|
392
|
+
path: z.string(),
|
|
393
|
+
content: z.string(),
|
|
394
|
+
content_type: z.string().optional()
|
|
395
|
+
},
|
|
396
|
+
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
397
|
+
},
|
|
398
|
+
async ({ bucket, path, content, content_type }) => run(
|
|
399
|
+
async () => bucketOf(requireClient(), bucket).upload(
|
|
400
|
+
path,
|
|
401
|
+
content,
|
|
402
|
+
content_type ?? "text/plain"
|
|
403
|
+
)
|
|
404
|
+
)
|
|
405
|
+
);
|
|
406
|
+
tool(
|
|
407
|
+
"storage_remove",
|
|
408
|
+
{
|
|
409
|
+
title: "Delete files",
|
|
410
|
+
description: "Delete one or more objects. Destructive: confirm with the user.",
|
|
411
|
+
inputSchema: { bucket: bucketArg, paths: z.array(z.string()).min(1) },
|
|
412
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
|
|
413
|
+
},
|
|
414
|
+
async ({ bucket, paths }) => run(async () => bucketOf(requireClient(), bucket).remove(paths))
|
|
415
|
+
);
|
|
416
|
+
tool(
|
|
417
|
+
"storage_move",
|
|
418
|
+
{
|
|
419
|
+
title: "Move or rename a file",
|
|
420
|
+
description: "Move/rename an object within a bucket.",
|
|
421
|
+
inputSchema: { bucket: bucketArg, from: z.string(), to: z.string() },
|
|
422
|
+
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
423
|
+
},
|
|
424
|
+
async ({ bucket, from, to }) => run(async () => bucketOf(requireClient(), bucket).move(from, to))
|
|
425
|
+
);
|
|
426
|
+
return server;
|
|
427
|
+
}
|
|
428
|
+
async function startMcpServer() {
|
|
429
|
+
const server = createMcpServer();
|
|
430
|
+
const transport = new StdioServerTransport();
|
|
431
|
+
await server.connect(transport);
|
|
432
|
+
console.error(`[${SERVER_NAME}] MCP server ready on stdio (gateway: ${baseUrl()})`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export {
|
|
436
|
+
loadCredential,
|
|
437
|
+
saveCredential,
|
|
438
|
+
clearCredential,
|
|
439
|
+
isExpired,
|
|
440
|
+
resolveActiveToken,
|
|
441
|
+
startDeviceAuth,
|
|
442
|
+
pollDeviceToken,
|
|
443
|
+
openBrowser,
|
|
444
|
+
login,
|
|
445
|
+
createMcpServer,
|
|
446
|
+
startMcpServer
|
|
447
|
+
};
|
|
448
|
+
//# sourceMappingURL=chunk-6DFTM26Y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/credentials.ts","../src/auth-device.ts","../src/mcp/server.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\n\n/**\n * Local credential cache for the CLI / MCP server (Node only — never import\n * from the browser entry).\n *\n * The browser device-authorization flow hands back a short-lived,\n * project-scoped, user-bound `dct_` token. We cache it on disk so the MCP\n * server can reuse it across tool calls and restarts until it expires, then\n * the user signs in again. The file is written 0600 and lives under\n * `~/.dontcode` (override with `DONTCODE_CONFIG_DIR`).\n */\n\nexport interface StoredCredential {\n /** The `dct_` device token. Sent to the gateway as `Authorization: Bearer`. */\n access_token: string\n project_id: string\n /** ISO timestamp. */\n expires_at: string\n /** Gateway origin this credential is valid for. */\n base_url: string\n}\n\n/** Credentials keyed by gateway origin, so one machine can hold several. */\ntype Store = Record<string, StoredCredential>\n\nfunction configDir(): string {\n return process.env.DONTCODE_CONFIG_DIR || join(homedir(), '.dontcode')\n}\n\nfunction credentialsPath(): string {\n return join(configDir(), 'credentials.json')\n}\n\nfunction readStore(): Store {\n try {\n return JSON.parse(readFileSync(credentialsPath(), 'utf8')) as Store\n } catch {\n return {}\n }\n}\n\nfunction writeStore(store: Store): void {\n const path = credentialsPath()\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 })\n writeFileSync(path, JSON.stringify(store, null, 2), { mode: 0o600 })\n}\n\nexport function loadCredential(baseUrl: string): StoredCredential | null {\n return readStore()[baseUrl] ?? null\n}\n\nexport function saveCredential(cred: StoredCredential): void {\n const store = readStore()\n store[cred.base_url] = cred\n writeStore(store)\n}\n\nexport function clearCredential(baseUrl: string): void {\n const store = readStore()\n delete store[baseUrl]\n writeStore(store)\n}\n\n/** A small skew so we don't hand back a token that expires mid-request. */\nexport function isExpired(cred: StoredCredential, skewMs = 30_000): boolean {\n return new Date(cred.expires_at).getTime() - skewMs <= Date.now()\n}\n\nexport interface ActiveToken {\n token?: string\n source: 'env' | 'device' | 'none'\n projectId?: string\n expiresAt?: string\n}\n\n/**\n * The credential the MCP server should use right now. An explicit\n * `DONTCODE_API_KEY` (e.g. CI) always wins; otherwise a cached, unexpired\n * device token; otherwise nothing (the user needs to run `login`).\n */\nexport function resolveActiveToken(baseUrl: string): ActiveToken {\n const env = process.env.DONTCODE_API_KEY\n if (env) return { token: env, source: 'env' }\n\n const cred = loadCredential(baseUrl)\n if (cred && !isExpired(cred)) {\n return {\n token: cred.access_token,\n source: 'device',\n projectId: cred.project_id,\n expiresAt: cred.expires_at,\n }\n }\n return { source: 'none' }\n}\n","import { saveCredential, type StoredCredential } from './credentials'\nimport { DontCodeError } from './errors'\nimport { Transport } from './http'\n\n/**\n * Client side of the browser device-authorization flow.\n *\n * The tool starts a flow, shows the user a short code and a URL, the user\n * approves in the browser while signed in, and the tool polls until it\n * receives a short-lived `dct_` access token. No long-lived secret ever\n * touches the terminal until the human has explicitly approved.\n */\n\nconst START_PATH = '/api/v1/auth/device/start'\nconst TOKEN_PATH = '/api/v1/auth/device/token'\n\nexport interface DeviceStartResponse {\n device_code: string\n user_code: string\n verification_uri: string\n verification_uri_complete: string\n interval: number\n expires_in: number\n}\n\nexport interface DeviceTokenResponse {\n access_token: string\n token_type: string\n expires_in: number\n project_id: string\n}\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms))\n\nexport async function startDeviceAuth(\n baseUrl: string,\n clientName?: string\n): Promise<DeviceStartResponse> {\n // No credential yet — this is the bootstrap step.\n const transport = new Transport({ baseUrl })\n return transport.json<DeviceStartResponse>(START_PATH, { client_name: clientName })\n}\n\nexport interface PollOptions {\n onPending?: () => void\n /** Stop polling after this many ms (throws code `WaitTimeout`), so a caller\n * like an MCP tool can poll in bounded slices instead of blocking for the\n * full 10-minute window. Defaults to the request's own expiry. */\n maxWaitMs?: number\n}\n\n/**\n * Poll until the request is approved (token), denied/expired (throws), or the\n * wait budget closes (throws 408). Honors the server's interval and `slow_down`.\n */\nexport async function pollDeviceToken(\n baseUrl: string,\n start: DeviceStartResponse,\n opts: PollOptions = {}\n): Promise<DeviceTokenResponse> {\n const transport = new Transport({ baseUrl })\n let intervalMs = Math.max(1, start.interval) * 1000\n const expiry = Date.now() + start.expires_in * 1000\n const deadline =\n opts.maxWaitMs && opts.maxWaitMs > 0\n ? Math.min(expiry, Date.now() + opts.maxWaitMs)\n : expiry\n\n while (Date.now() < deadline) {\n await sleep(intervalMs)\n try {\n return await transport.json<DeviceTokenResponse>(TOKEN_PATH, {\n device_code: start.device_code,\n })\n } catch (err) {\n if (err instanceof DontCodeError) {\n const message = err.body?.error ?? err.message\n if (err.status === 428 || message.includes('authorization_pending')) {\n opts.onPending?.()\n continue\n }\n if (message.includes('slow_down')) {\n intervalMs += 2_000\n continue\n }\n }\n throw err\n }\n }\n // Distinguish \"your slice ended, keep waiting\" from \"the whole window closed\".\n const stillOpen = Date.now() < expiry\n throw new DontCodeError(408, {\n error: stillOpen\n ? 'Still waiting for browser approval.'\n : 'Device login timed out before approval. Start again.',\n code: stillOpen ? 'WaitTimeout' : 'Timeout',\n })\n}\n\n/** Best-effort: open the verification URL in the user's browser. */\nexport async function openBrowser(url: string): Promise<void> {\n try {\n const { spawn } = await import('node:child_process')\n const platform = process.platform\n const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'cmd' : 'xdg-open'\n const args = platform === 'win32' ? ['/c', 'start', '', url] : [url]\n spawn(command, args, { stdio: 'ignore', detached: true }).unref()\n } catch {\n // Opening the browser is a convenience; the URL is also printed.\n }\n}\n\nexport interface LoginOptions {\n baseUrl: string\n clientName?: string\n /** Open the browser automatically. Default true. */\n open?: boolean\n /** Where human-facing prompts go. Default: no-op. */\n log?: (message: string) => void\n}\n\n/**\n * Run the full device flow and cache the resulting credential. Returns the\n * stored credential (token, project, expiry).\n */\nexport async function login(options: LoginOptions): Promise<StoredCredential> {\n const log = options.log ?? (() => {})\n\n const start = await startDeviceAuth(options.baseUrl, options.clientName)\n log(\n `\\nOpen this URL to connect:\\n ${start.verification_uri_complete}\\n\\n` +\n `Confirm this code matches:\\n ${start.user_code}\\n\\nWaiting for approval...\\n`\n )\n if (options.open !== false) await openBrowser(start.verification_uri_complete)\n\n const token = await pollDeviceToken(options.baseUrl, start)\n const cred: StoredCredential = {\n access_token: token.access_token,\n project_id: token.project_id,\n expires_at: new Date(Date.now() + token.expires_in * 1000).toISOString(),\n base_url: options.baseUrl,\n }\n saveCredential(cred)\n return cred\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport {\n login as runLogin,\n openBrowser,\n pollDeviceToken,\n startDeviceAuth,\n type DeviceStartResponse,\n} from '../auth-device'\nimport { dontcode, type DontCodeClient } from '../client'\nimport {\n clearCredential,\n resolveActiveToken,\n saveCredential,\n} from '../credentials'\nimport { isDontCodeError } from '../errors'\n\n/**\n * DontCode Backend MCP server (stdio).\n *\n * Exposes the v1 gateway to an AI agent (Claude Code and others) as tools:\n * sign in by browser, query and mutate the project database, run migrations,\n * manage storage, and check the current session. Every tool is a thin call\n * onto the public SDK, so the agent can only do what the gateway allows — and,\n * for device-token sessions, only what the signed-in user's project role\n * allows.\n *\n * IMPORTANT: stdout is the MCP transport. All human-facing logging goes to\n * stderr (console.error), never console.log.\n */\n\nconst SERVER_NAME = 'dontcode-backend'\nconst SERVER_VERSION = '0.3.0'\n\nfunction baseUrl(): string {\n return (process.env.DONTCODE_API_URL || 'https://backend.dontcode.co').replace(/\\/+$/, '')\n}\n\n/** A login flow waiting to be polled by `auth_wait`, kept off the wire. */\nlet pendingFlow: DeviceStartResponse | null = null\n\nfunction text(value: unknown) {\n const body = typeof value === 'string' ? value : JSON.stringify(value, null, 2)\n return { content: [{ type: 'text' as const, text: body }] }\n}\n\nfunction failure(message: string) {\n return { content: [{ type: 'text' as const, text: message }], isError: true }\n}\n\n/** Turn SDK errors into agent-friendly guidance without leaking internals. */\nfunction describeError(err: unknown): string {\n if (isDontCodeError(err)) {\n if (err.status === 401) {\n return 'Not signed in or the session expired. Use the `auth_login` tool, approve in the browser, then `auth_wait`.'\n }\n if (err.status === 403) {\n return `Your project role does not allow that. (${err.message})`\n }\n if (err.rateLimited) {\n return `Rate limited. ${err.message}`\n }\n return err.message\n }\n return err instanceof Error ? err.message : 'Unknown error'\n}\n\nfunction requireClient(): DontCodeClient {\n const active = resolveActiveToken(baseUrl())\n if (!active.token) {\n throw new Error(\n 'Not signed in. Use the `auth_login` tool first (or set DONTCODE_API_KEY for non-interactive use).'\n )\n }\n return dontcode({ apiKey: active.token, baseUrl: baseUrl() })\n}\n\n/** Run a tool body, mapping thrown errors to an MCP error result. */\nasync function run(fn: () => Promise<unknown>) {\n try {\n return text(await fn())\n } catch (err) {\n return failure(describeError(err))\n }\n}\n\nexport function createMcpServer(): McpServer {\n const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION })\n\n // The MCP SDK infers each tool's argument type from its `inputSchema`\n // through deep conditional types. Across this many tools that inference\n // exhausts tsc's heap during declaration emit (TS2589). The gateway\n // re-validates every call, so these handlers never rely on the inferred\n // types — register through a thin wrapper that keeps inference shallow.\n const tool = (\n name: string,\n config: {\n title: string\n description: string\n inputSchema?: z.ZodRawShape\n annotations?: Record<string, boolean>\n },\n handler: (args: Record<string, any>) => Promise<unknown>\n ) => server.registerTool(name, config as never, handler as never)\n\n // --- Authentication ----------------------------------------------------\n\n tool(\n 'auth_login',\n {\n title: 'Sign in to DontCode',\n description:\n 'Start a browser sign-in. Returns a URL and a short code; tell the user to open the URL, confirm the code matches, pick a project, and approve. Then call `auth_wait`. Not needed if DONTCODE_API_KEY is set.',\n inputSchema: {\n client_name: z\n .string()\n .optional()\n .describe('Label shown to the user on the approval screen, e.g. \"Claude Code\".'),\n },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ client_name }) =>\n run(async () => {\n const start = await startDeviceAuth(baseUrl(), client_name ?? 'Claude Code (MCP)')\n pendingFlow = start\n await openBrowser(start.verification_uri_complete)\n return {\n message:\n 'Ask the user to open this URL, confirm the code, choose a project, and approve. Then call auth_wait.',\n verification_uri: start.verification_uri_complete,\n user_code: start.user_code,\n expires_in_seconds: start.expires_in,\n }\n })\n )\n\n tool(\n 'auth_wait',\n {\n title: 'Wait for sign-in approval',\n description:\n 'Poll for the result of `auth_login`. Returns connected once the user approves in the browser, or asks you to call it again if still pending. Safe to call repeatedly.',\n inputSchema: {},\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async () =>\n run(async () => {\n if (!pendingFlow) {\n return { status: 'no_login_in_progress', hint: 'Call auth_login first.' }\n }\n try {\n const token = await pollDeviceToken(baseUrl(), pendingFlow, {\n maxWaitMs: 50_000,\n })\n saveCredential({\n access_token: token.access_token,\n project_id: token.project_id,\n expires_at: new Date(Date.now() + token.expires_in * 1000).toISOString(),\n base_url: baseUrl(),\n })\n pendingFlow = null\n return { status: 'connected', project_id: token.project_id }\n } catch (err) {\n if (isDontCodeError(err) && err.code === 'WaitTimeout') {\n return {\n status: 'pending',\n hint: 'Still waiting for approval. Ask the user to approve, then call auth_wait again.',\n }\n }\n pendingFlow = null\n throw err\n }\n })\n )\n\n tool(\n 'auth_status',\n {\n title: 'Check the current session',\n description:\n 'Validate the current credential and report the project, your role, and what you are allowed to do.',\n inputSchema: {},\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async () =>\n run(async () => {\n const active = resolveActiveToken(baseUrl())\n if (!active.token) {\n return { signed_in: false, hint: 'Use auth_login to sign in.' }\n }\n const client = dontcode({ apiKey: active.token, baseUrl: baseUrl() })\n // /api/v1/info is a thin GET; reuse the transport via a raw call.\n const info = await client.auth.info()\n return { signed_in: true, source: active.source, ...info }\n })\n )\n\n tool(\n 'auth_logout',\n {\n title: 'Forget the cached session',\n description: 'Remove the locally cached device token for this gateway.',\n inputSchema: {},\n annotations: { readOnlyHint: false, destructiveHint: true },\n },\n async () =>\n run(async () => {\n clearCredential(baseUrl())\n pendingFlow = null\n return { ok: true }\n })\n )\n\n // --- Database ----------------------------------------------------------\n\n tool(\n 'db_query',\n {\n title: 'Query the database',\n description:\n 'Read rows from a table with a structured query (no raw SQL). Supports where/select/orderBy/limit/offset and count.',\n inputSchema: {\n table: z.string(),\n operation: z\n .enum(['find', 'findMany', 'findFirst', 'findOne', 'count'])\n .default('find'),\n where: z.record(z.string(), z.any()).optional(),\n select: z.array(z.string()).optional(),\n orderBy: z.record(z.string(), z.enum(['asc', 'desc'])).optional(),\n limit: z.number().int().positive().max(1000).optional(),\n offset: z.number().int().nonnegative().optional(),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ table, operation, where, select, orderBy, limit, offset }) =>\n run(async () => {\n const t = requireClient().db(table)\n if (operation === 'count') return { count: await t.count({ where }) }\n const options = { where, select, orderBy, limit, offset }\n if (operation === 'findFirst' || operation === 'findOne') {\n return { row: await t.findFirst(options) }\n }\n return { rows: await t.find(options) }\n })\n )\n\n tool(\n 'db_insert',\n {\n title: 'Insert a row',\n description: 'Insert one row into a table. Returns the new row id.',\n inputSchema: { table: z.string(), data: z.record(z.string(), z.any()) },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ table, data }) => run(async () => requireClient().db(table).insert(data))\n )\n\n tool(\n 'db_update',\n {\n title: 'Update rows',\n description: 'Update rows matching a where clause. Returns the number of rows changed.',\n inputSchema: {\n table: z.string(),\n where: z.record(z.string(), z.any()),\n data: z.record(z.string(), z.any()),\n },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ table, where, data }) =>\n run(async () => requireClient().db(table).update({ where, data }))\n )\n\n tool(\n 'db_delete',\n {\n title: 'Delete rows',\n description:\n 'Delete rows matching a where clause. Destructive: confirm with the user before calling. Returns the number of rows deleted.',\n inputSchema: { table: z.string(), where: z.record(z.string(), z.any()) },\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },\n },\n async ({ table, where }) => run(async () => requireClient().db(table).delete({ where }))\n )\n\n tool(\n 'db_migrate',\n {\n title: 'Run a schema migration',\n description:\n 'Apply DDL (CREATE/ALTER/DROP TABLE, indexes, etc.) to the project database. Destructive and schema-shaping: confirm with the user, and note it needs an admin/owner role on device-token sessions.',\n inputSchema: { sql: z.string() },\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },\n },\n async ({ sql }) => run(async () => requireClient().db.migrate({ sql }))\n )\n\n // --- Storage -----------------------------------------------------------\n\n const bucketArg = z.enum(['public', 'private']).default('private')\n const bucketOf = (client: DontCodeClient, bucket: 'public' | 'private') =>\n bucket === 'public' ? client.storage.public : client.storage.private\n\n tool(\n 'storage_list',\n {\n title: 'List files',\n description: 'List objects in a storage bucket, optionally under a prefix.',\n inputSchema: { bucket: bucketArg, prefix: z.string().optional() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ bucket, prefix }) =>\n run(async () => bucketOf(requireClient(), bucket).list(prefix))\n )\n\n tool(\n 'storage_get_url',\n {\n title: 'Get a public URL',\n description: 'Get the permanent public URL for an object in the public bucket.',\n inputSchema: { path: z.string() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ path }) => run(async () => requireClient().storage.public.getUrl(path))\n )\n\n tool(\n 'storage_temporary_url',\n {\n title: 'Get a temporary URL',\n description: 'Get a short-lived signed URL for an object (default 300s, max 7 days).',\n inputSchema: {\n bucket: bucketArg,\n path: z.string(),\n expires_in: z.number().int().positive().optional(),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ bucket, path, expires_in }) =>\n run(async () => bucketOf(requireClient(), bucket).getTemporaryUrl(path, expires_in))\n )\n\n tool(\n 'storage_upload',\n {\n title: 'Upload a text file',\n description:\n 'Upload UTF-8 text content to a path. For binary or large files, use storage_temporary_url + a direct PUT instead.',\n inputSchema: {\n bucket: bucketArg,\n path: z.string(),\n content: z.string(),\n content_type: z.string().optional(),\n },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ bucket, path, content, content_type }) =>\n run(async () =>\n bucketOf(requireClient(), bucket).upload(\n path,\n content,\n content_type ?? 'text/plain'\n )\n )\n )\n\n tool(\n 'storage_remove',\n {\n title: 'Delete files',\n description: 'Delete one or more objects. Destructive: confirm with the user.',\n inputSchema: { bucket: bucketArg, paths: z.array(z.string()).min(1) },\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },\n },\n async ({ bucket, paths }) => run(async () => bucketOf(requireClient(), bucket).remove(paths))\n )\n\n tool(\n 'storage_move',\n {\n title: 'Move or rename a file',\n description: 'Move/rename an object within a bucket.',\n inputSchema: { bucket: bucketArg, from: z.string(), to: z.string() },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ bucket, from, to }) =>\n run(async () => bucketOf(requireClient(), bucket).move(from, to))\n )\n\n return server\n}\n\n/** Entry point used by the `dontcode mcp` CLI command. */\nexport async function startMcpServer(): Promise<void> {\n const server = createMcpServer()\n const transport = new StdioServerTransport()\n await server.connect(transport)\n console.error(`[${SERVER_NAME}] MCP server ready on stdio (gateway: ${baseUrl()})`)\n}\n\n// Re-export so a programmatic caller can drive the device flow too.\nexport { runLogin as login }\n"],"mappings":"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","names":["baseUrl","baseUrl"]}
|