@nyte-ai/host 0.0.0-stage → 0.0.19
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/LICENSE +21 -0
- package/README.md +6 -2
- package/THIRD-PARTY-NOTICES.md +866 -0
- package/dist/chunks/canvas-preview-0cmbkp2j.js +65 -0
- package/dist/chunks/canvas-preview-0nq75h9k.js +7 -0
- package/dist/chunks/canvas-preview-11nadadc.js +241 -0
- package/dist/chunks/canvas-preview-1sq7z9jq.js +17 -0
- package/dist/chunks/canvas-preview-2jwbe14k.js +130 -0
- package/dist/chunks/canvas-preview-2zdyh9ny.js +26 -0
- package/dist/chunks/canvas-preview-31w7xm8z.js +34 -0
- package/dist/chunks/canvas-preview-4b6sayhg.js +295 -0
- package/dist/chunks/canvas-preview-4e0pxa32.js +63 -0
- package/dist/chunks/canvas-preview-518dh8by.js +169 -0
- package/dist/chunks/canvas-preview-55s0y68g.js +387 -0
- package/dist/chunks/canvas-preview-5ekjx6sw.js +613 -0
- package/dist/chunks/canvas-preview-5mex725c.js +14 -0
- package/dist/chunks/canvas-preview-5z63am63.js +295 -0
- package/dist/chunks/canvas-preview-65xqjw7f.js +20 -0
- package/dist/chunks/canvas-preview-69fkk9yn.js +95 -0
- package/dist/chunks/canvas-preview-8xc0vn7v.js +458 -0
- package/dist/chunks/canvas-preview-9aqczacs.js +73 -0
- package/dist/chunks/canvas-preview-af86jz8t.js +141 -0
- package/dist/chunks/canvas-preview-aq5j4p25.js +240 -0
- package/dist/chunks/canvas-preview-b2v8wfnz.js +36 -0
- package/dist/chunks/canvas-preview-befkjea2.js +60 -0
- package/dist/chunks/canvas-preview-bxnqt8cd.js +103 -0
- package/dist/chunks/canvas-preview-cawy6vbz.js +352 -0
- package/dist/chunks/canvas-preview-crmzxxt8.js +518 -0
- package/dist/chunks/canvas-preview-drctegz4.js +591 -0
- package/dist/chunks/canvas-preview-e3vnkkc5.js +134 -0
- package/dist/chunks/canvas-preview-ec2mt7es.js +108 -0
- package/dist/chunks/canvas-preview-fxa0rfb0.js +61 -0
- package/dist/chunks/canvas-preview-g5fkjs34.js +598 -0
- package/dist/chunks/canvas-preview-jeddjex8.js +166 -0
- package/dist/chunks/canvas-preview-k1yg4fgp.js +571 -0
- package/dist/chunks/canvas-preview-kdqgrstc.js +194 -0
- package/dist/chunks/canvas-preview-kn7kvbfj.js +12 -0
- package/dist/chunks/canvas-preview-kp75gb63.js +141 -0
- package/dist/chunks/canvas-preview-mgcx2b2p.js +271 -0
- package/dist/chunks/canvas-preview-n2a35zv5.js +81 -0
- package/dist/chunks/canvas-preview-pkzqg96v.js +63 -0
- package/dist/chunks/canvas-preview-rp4x6y1h.js +144 -0
- package/dist/chunks/canvas-preview-s94ebwf5.js +112 -0
- package/dist/chunks/canvas-preview-skzwvw4d.js +23 -0
- package/dist/chunks/canvas-preview-srgfpdmf.js +60 -0
- package/dist/chunks/canvas-preview-tjhabynd.js +252 -0
- package/dist/chunks/canvas-preview-tnqe7k0b.js +374 -0
- package/dist/chunks/canvas-preview-vaatfznm.js +189 -0
- package/dist/chunks/canvas-preview-vm9zdp2t.js +398 -0
- package/dist/chunks/canvas-preview-w54ymtb3.js +272 -0
- package/dist/chunks/canvas-preview-wg80rbc2.js +46 -0
- package/dist/chunks/canvas-preview-y5y6ghaw.js +125 -0
- package/dist/chunks/canvas-preview-ycepb05s.js +176 -0
- package/dist/chunks/canvas-preview-yjz0f7n4.js +50 -0
- package/dist/chunks/canvas-preview-zaaj99wd.js +244 -0
- package/dist/src/canvas-preview.d.ts +29 -0
- package/dist/src/canvas-preview.js +271 -0
- package/dist/src/credentials.d.ts +44 -0
- package/dist/src/credentials.js +10 -0
- package/dist/src/deploy/cloudflare/worker.d.ts +50 -0
- package/dist/src/deploy/cloudflare/worker.js +196 -0
- package/dist/src/deploy/vercel/nitro.config.d.ts +2 -0
- package/dist/src/deploy/vercel/nitro.config.js +30 -0
- package/dist/src/deploy/vercel/server/routes/[...].d.ts +2 -0
- package/dist/src/deploy/vercel/server/routes/[...].js +18 -0
- package/dist/src/deploy/vercel/server/utils/host.d.ts +13 -0
- package/dist/src/deploy/vercel/server/utils/host.js +7 -0
- package/dist/src/deploy/vercel/server/utils/repair.d.ts +1 -0
- package/dist/src/deploy/vercel/server/utils/repair.js +6 -0
- package/dist/src/deploy/vercel/server/utils/step.d.ts +10 -0
- package/dist/src/deploy/vercel/server/utils/step.js +6 -0
- package/dist/src/deploy/vercel/server/workflows/drive.d.ts +14 -0
- package/dist/src/deploy/vercel/server/workflows/drive.js +13 -0
- package/dist/src/durable-object.d.ts +14 -0
- package/dist/src/durable-object.js +231 -0
- package/dist/src/hash.d.ts +1 -0
- package/dist/src/hash.js +6 -0
- package/dist/src/identity-key.d.ts +11 -0
- package/dist/src/identity-key.js +9 -0
- package/dist/src/identity.d.ts +12 -0
- package/dist/src/identity.js +6 -0
- package/dist/src/images/exif.d.ts +1 -0
- package/dist/src/images/exif.js +6 -0
- package/dist/src/images/node.d.ts +37 -0
- package/dist/src/images/node.js +12 -0
- package/dist/src/images/photon.d.ts +3 -0
- package/dist/src/images/photon.js +7 -0
- package/dist/src/images/worker.d.ts +1 -0
- package/dist/src/images/worker.js +23 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.js +121 -0
- package/dist/src/key-file.d.ts +1 -0
- package/dist/src/key-file.js +7 -0
- package/dist/src/listen-fetch.d.ts +8 -0
- package/dist/src/listen-fetch.js +9 -0
- package/dist/src/listen.d.ts +4 -0
- package/dist/src/listen.js +10 -0
- package/dist/src/local/command.d.ts +20 -0
- package/dist/src/local/command.js +12 -0
- package/dist/src/local/feed.d.ts +7 -0
- package/dist/src/local/feed.js +6 -0
- package/dist/src/local/files.d.ts +18 -0
- package/dist/src/local/files.js +13 -0
- package/dist/src/local/git-watch.d.ts +15 -0
- package/dist/src/local/git-watch.js +7 -0
- package/dist/src/local/git.d.ts +5 -0
- package/dist/src/local/git.js +13 -0
- package/dist/src/local/github.d.ts +30 -0
- package/dist/src/local/github.js +9 -0
- package/dist/src/local/identity.d.ts +3 -0
- package/dist/src/local/identity.js +7 -0
- package/dist/src/local/jobs.d.ts +7 -0
- package/dist/src/local/jobs.js +7 -0
- package/dist/src/local/legacy-trust.d.ts +10 -0
- package/dist/src/local/legacy-trust.js +6 -0
- package/dist/src/local/patch.d.ts +16 -0
- package/dist/src/local/patch.js +17 -0
- package/dist/src/local/provider.d.ts +11 -0
- package/dist/src/local/provider.js +20 -0
- package/dist/src/local/snapshots.d.ts +7 -0
- package/dist/src/local/snapshots.js +11 -0
- package/dist/src/local/text.d.ts +1 -0
- package/dist/src/local/text.js +6 -0
- package/dist/src/local/tree.d.ts +50 -0
- package/dist/src/local/tree.js +16 -0
- package/dist/src/local/workspace-migration.d.ts +2 -0
- package/dist/src/local/workspace-migration.js +7 -0
- package/dist/src/local/worktrees.d.ts +46 -0
- package/dist/src/local/worktrees.js +14 -0
- package/dist/src/mcp/config.d.ts +75 -0
- package/dist/src/mcp/config.js +12 -0
- package/dist/src/mcp/connect.d.ts +15 -0
- package/dist/src/mcp/connect.js +9 -0
- package/dist/src/mcp/hub.d.ts +19 -0
- package/dist/src/mcp/hub.js +11 -0
- package/dist/src/mcp/oauth.d.ts +46 -0
- package/dist/src/mcp/oauth.js +12 -0
- package/dist/src/mcp/tools.d.ts +29 -0
- package/dist/src/mcp/tools.js +12 -0
- package/dist/src/open.d.ts +14 -0
- package/dist/src/open.js +46 -0
- package/dist/src/plugins/loader.d.ts +21 -0
- package/dist/src/plugins/loader.js +11 -0
- package/dist/src/postgres.d.ts +19 -0
- package/dist/src/postgres.js +8 -0
- package/dist/src/providers.d.ts +16 -0
- package/dist/src/providers.js +112 -0
- package/dist/src/secrets.d.ts +12 -0
- package/dist/src/secrets.js +8 -0
- package/dist/src/serve/binding.d.ts +39 -0
- package/dist/src/serve/binding.js +6 -0
- package/dist/src/serve/handler.d.ts +15 -0
- package/dist/src/serve/handler.js +8 -0
- package/dist/src/serve/stream-server.d.ts +6 -0
- package/dist/src/serve/stream-server.js +6 -0
- package/dist/src/session-index.d.ts +11 -0
- package/dist/src/session-index.js +11 -0
- package/dist/src/shell-env.d.ts +14 -0
- package/dist/src/shell-env.js +108 -0
- package/dist/src/sqlite-workers.d.ts +6 -0
- package/dist/src/sqlite-workers.js +6 -0
- package/dist/src/sqlite.d.ts +8 -0
- package/dist/src/sqlite.js +11 -0
- package/dist/src/store-rows.d.ts +13 -0
- package/dist/src/store-rows.js +26 -0
- package/dist/src/store-usage.d.ts +25 -0
- package/dist/src/store-usage.js +19 -0
- package/dist/src/usage-history.d.ts +54 -0
- package/dist/src/usage-history.js +14 -0
- package/dist/src/usage-scan.d.ts +24 -0
- package/dist/src/usage-scan.js +65 -0
- package/dist/src/usage-service.d.ts +23 -0
- package/dist/src/usage-service.js +16 -0
- package/dist/src/usage-summary.d.ts +9 -0
- package/dist/src/usage-summary.js +16 -0
- package/dist/src/usage-types.d.ts +86 -0
- package/dist/src/usage-types.js +0 -0
- package/dist/src/usage-worker.d.ts +1 -0
- package/dist/src/usage-worker.js +29 -0
- package/dist/src/usage.d.ts +195 -0
- package/dist/src/usage.js +23 -0
- package/package.json +140 -3
- package/src/canvas-preview.ts +403 -0
- package/src/credentials.ts +294 -0
- package/src/deploy/cloudflare/README.md +3 -0
- package/src/deploy/cloudflare/worker.ts +306 -0
- package/src/deploy/cloudflare/wrangler.jsonc +12 -0
- package/src/deploy/vercel/README.md +3 -0
- package/src/deploy/vercel/nitro.config.ts +29 -0
- package/src/deploy/vercel/server/routes/[...].ts +10 -0
- package/src/deploy/vercel/server/utils/host.ts +104 -0
- package/src/deploy/vercel/server/utils/repair.ts +35 -0
- package/src/deploy/vercel/server/utils/step.ts +25 -0
- package/src/deploy/vercel/server/workflows/drive.ts +46 -0
- package/src/deploy/vercel/tsconfig.json +9 -0
- package/src/deploy/vercel/types/bun.d.ts +2 -0
- package/src/durable-object.ts +386 -0
- package/src/hash.ts +5 -0
- package/src/identity-key.ts +20 -0
- package/src/identity.ts +66 -0
- package/src/images/exif.ts +69 -0
- package/src/images/node.ts +163 -0
- package/src/images/photon.ts +141 -0
- package/src/images/worker.ts +29 -0
- package/src/index.ts +39 -0
- package/src/key-file.ts +53 -0
- package/src/listen-fetch.ts +409 -0
- package/src/listen.ts +228 -0
- package/src/local/command.ts +161 -0
- package/src/local/feed.ts +70 -0
- package/src/local/files.ts +817 -0
- package/src/local/git-watch.ts +199 -0
- package/src/local/git.ts +919 -0
- package/src/local/github.ts +691 -0
- package/src/local/identity.ts +21 -0
- package/src/local/jobs.ts +573 -0
- package/src/local/legacy-trust.ts +105 -0
- package/src/local/patch.ts +334 -0
- package/src/local/provider.ts +184 -0
- package/src/local/snapshots.ts +778 -0
- package/src/local/text.ts +9 -0
- package/src/local/tree.ts +398 -0
- package/src/local/workspace-migration.ts +95 -0
- package/src/local/worktrees.ts +239 -0
- package/src/mcp/config.ts +330 -0
- package/src/mcp/connect.ts +92 -0
- package/src/mcp/hub.ts +836 -0
- package/src/mcp/oauth.ts +436 -0
- package/src/mcp/tools.ts +333 -0
- package/src/open.ts +449 -0
- package/src/plugins/loader.ts +186 -0
- package/src/postgres.ts +492 -0
- package/src/providers.ts +135 -0
- package/src/secrets.ts +31 -0
- package/src/serve/binding.ts +629 -0
- package/src/serve/handler.ts +362 -0
- package/src/serve/stream-server.ts +176 -0
- package/src/session-index.ts +99 -0
- package/src/shell-env.ts +153 -0
- package/src/sqlite-workers.ts +283 -0
- package/src/sqlite.ts +553 -0
- package/src/store-rows.ts +110 -0
- package/src/store-usage.ts +347 -0
- package/src/usage-history.ts +219 -0
- package/src/usage-scan.ts +81 -0
- package/src/usage-service.ts +82 -0
- package/src/usage-summary.ts +79 -0
- package/src/usage-types.ts +87 -0
- package/src/usage-worker.ts +24 -0
- package/src/usage.ts +776 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// src/mcp/config.ts
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { mkdir, readFile, realpath, rename, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
|
+
import { isJsonObject, McpExposureNameType, McpOAuthConfigType } from "@nyte-ai/schema";
|
|
6
|
+
import { isJson } from "@nyte-ai/schema";
|
|
7
|
+
import { Type } from "typebox";
|
|
8
|
+
import { Value } from "typebox/value";
|
|
9
|
+
var ExposureName = McpExposureNameType;
|
|
10
|
+
var Entry = Type.Object({
|
|
11
|
+
type: Type.Optional(Type.Enum(["stdio", "http", "streamable-http", "sse"])),
|
|
12
|
+
command: Type.Optional(Type.String({ minLength: 1 })),
|
|
13
|
+
args: Type.Optional(Type.Array(Type.String())),
|
|
14
|
+
env: Type.Optional(Type.Record(Type.String(), Type.String())),
|
|
15
|
+
cwd: Type.Optional(Type.String()),
|
|
16
|
+
url: Type.Optional(Type.String({ minLength: 1 })),
|
|
17
|
+
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
|
|
18
|
+
oauth: Type.Optional(McpOAuthConfigType),
|
|
19
|
+
enabled: Type.Optional(Type.Boolean()),
|
|
20
|
+
timeout: Type.Optional(Type.Number({ exclusiveMinimum: 0 })),
|
|
21
|
+
description: Type.Optional(Type.String()),
|
|
22
|
+
exposure: Type.Optional(ExposureName),
|
|
23
|
+
toolExposure: Type.Optional(Type.Record(Type.String(), ExposureName))
|
|
24
|
+
});
|
|
25
|
+
var ConfigFile = Type.Object({
|
|
26
|
+
mcpServers: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
|
|
27
|
+
});
|
|
28
|
+
var OVERRIDE_KEYS = new Set(["enabled", "exposure", "toolExposure"]);
|
|
29
|
+
var EXPOSURE = {
|
|
30
|
+
direct: "model",
|
|
31
|
+
deferred: "deferred",
|
|
32
|
+
codemode: "code",
|
|
33
|
+
"codemode-deferred": "code",
|
|
34
|
+
hidden: "hidden"
|
|
35
|
+
};
|
|
36
|
+
async function readMcpFile2(path) {
|
|
37
|
+
try {
|
|
38
|
+
return { kind: "text", text: await readFile(path, "utf8") };
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
41
|
+
return { kind: "missing" };
|
|
42
|
+
return { kind: "unreadable", message: error instanceof Error ? error.message : String(error) };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function parseMcpConfig2(files) {
|
|
46
|
+
const servers = new Map;
|
|
47
|
+
const canonical = new Map;
|
|
48
|
+
const issues = [];
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
if (file.contents.kind === "missing")
|
|
51
|
+
continue;
|
|
52
|
+
const fail = (path, message) => {
|
|
53
|
+
issues.push({
|
|
54
|
+
severity: "error",
|
|
55
|
+
path: ["mcpServers", ...path],
|
|
56
|
+
message,
|
|
57
|
+
source: { plugin: "mcp", file: file.path }
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
if (file.contents.kind === "unreadable") {
|
|
61
|
+
fail([], `The file cannot be read: ${file.contents.message}`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
let parsed;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(file.contents.text);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
fail([], `The file is not valid JSON: ${error instanceof Error ? error.message : ""}`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (!Value.Check(ConfigFile, parsed)) {
|
|
72
|
+
fail([], 'The file must be an object with an "mcpServers" object.');
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
for (const [name, raw] of Object.entries(parsed.mcpServers ?? {})) {
|
|
76
|
+
if (!/^[A-Za-z0-9_-]+$/.test(name)) {
|
|
77
|
+
fail([name], `Server names may contain only letters, digits, "_" and "-": ${name}.`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const key = name.replaceAll("-", "_");
|
|
81
|
+
const taken = canonical.get(key);
|
|
82
|
+
if (taken !== undefined && taken !== name) {
|
|
83
|
+
fail([name], `${name} and ${taken} name the same server.`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (!Value.Check(Entry, raw)) {
|
|
87
|
+
const [first] = Value.Errors(Entry, raw);
|
|
88
|
+
fail([name], `${name}${first?.instancePath ?? ""} ${first?.message ?? "is invalid"}.`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const base = servers.get(name);
|
|
92
|
+
if (raw.command === undefined && raw.url === undefined && raw.type === undefined) {
|
|
93
|
+
const extra = Object.keys(raw).filter((field) => !OVERRIDE_KEYS.has(field));
|
|
94
|
+
if (file.scope !== "project" || base === undefined)
|
|
95
|
+
fail([name], `${name} needs "command" or "url".`);
|
|
96
|
+
else if (extra.length > 0)
|
|
97
|
+
fail([name], `An override of ${name} can only set enabled, exposure and toolExposure.`);
|
|
98
|
+
else
|
|
99
|
+
servers.set(name, { ...base, ...settings(raw, base), override: file.path });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const transport = readTransport(raw);
|
|
103
|
+
if (typeof transport === "string") {
|
|
104
|
+
fail([name], `${name}: ${transport}`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
canonical.set(key, name);
|
|
108
|
+
servers.set(name, {
|
|
109
|
+
name,
|
|
110
|
+
scope: file.scope,
|
|
111
|
+
path: file.path,
|
|
112
|
+
override: null,
|
|
113
|
+
transport,
|
|
114
|
+
timeoutMs: (raw.timeout ?? 60) * 1000,
|
|
115
|
+
description: raw.description ?? null,
|
|
116
|
+
...settings(raw, null)
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { servers, issues };
|
|
121
|
+
}
|
|
122
|
+
function readTransport(raw) {
|
|
123
|
+
if (raw.type === "sse")
|
|
124
|
+
return `the SSE transport is not supported; use the server's streamable HTTP url, often ending in "/mcp".`;
|
|
125
|
+
if (raw.command !== undefined && raw.url !== undefined)
|
|
126
|
+
return 'set "command" or "url", not both.';
|
|
127
|
+
if (raw.command !== undefined && raw.type !== undefined && raw.type !== "stdio")
|
|
128
|
+
return `type ${raw.type} needs "url", not "command".`;
|
|
129
|
+
if (raw.command !== undefined)
|
|
130
|
+
return {
|
|
131
|
+
kind: "stdio",
|
|
132
|
+
command: raw.command,
|
|
133
|
+
args: raw.args ?? [],
|
|
134
|
+
env: raw.env ?? {},
|
|
135
|
+
cwd: raw.cwd ?? null
|
|
136
|
+
};
|
|
137
|
+
if (raw.url === undefined)
|
|
138
|
+
return `type ${raw.type ?? "stdio"} needs "command" or "url".`;
|
|
139
|
+
if (raw.type === "stdio")
|
|
140
|
+
return 'type stdio needs "command", not "url".';
|
|
141
|
+
const url = URL.parse(raw.url);
|
|
142
|
+
if (url === null || url.protocol !== "https:" && url.protocol !== "http:")
|
|
143
|
+
return `${raw.url} is not an http or https URL.`;
|
|
144
|
+
if (raw.command === undefined && raw.oauth?.callbackUrl !== undefined) {
|
|
145
|
+
const callback = URL.parse(raw.oauth.callbackUrl);
|
|
146
|
+
if (callback === null || callback.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(callback.hostname))
|
|
147
|
+
return "oauth.callbackUrl must be an http URL on localhost, 127.0.0.1 or [::1].";
|
|
148
|
+
}
|
|
149
|
+
if (raw.oauth?.authServerMetadataUrl !== undefined) {
|
|
150
|
+
const metadata = URL.parse(raw.oauth.authServerMetadataUrl);
|
|
151
|
+
if (metadata === null || metadata.protocol !== "https:" && !["localhost", "127.0.0.1", "[::1]"].includes(metadata.hostname))
|
|
152
|
+
return "oauth.authServerMetadataUrl must use https, except on localhost.";
|
|
153
|
+
}
|
|
154
|
+
return { kind: "http", url: raw.url, headers: raw.headers ?? {}, oauth: raw.oauth ?? {} };
|
|
155
|
+
}
|
|
156
|
+
function settings(raw, base) {
|
|
157
|
+
return {
|
|
158
|
+
enabled: raw.enabled ?? base?.enabled ?? true,
|
|
159
|
+
exposure: raw.exposure === undefined ? base?.exposure ?? "code" : EXPOSURE[raw.exposure],
|
|
160
|
+
toolExposure: raw.toolExposure === undefined ? base?.toolExposure ?? [] : Object.entries(raw.toolExposure).map(([pattern, name]) => [pattern, EXPOSURE[name]])
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async function planMcpEdit2(path, change) {
|
|
164
|
+
const contents = await readMcpFile2(path);
|
|
165
|
+
if (contents.kind === "unreadable")
|
|
166
|
+
return { kind: "invalid", message: `${path} cannot be read: ${contents.message}` };
|
|
167
|
+
let parsed = {};
|
|
168
|
+
if (contents.kind === "text") {
|
|
169
|
+
try {
|
|
170
|
+
parsed = JSON.parse(contents.text);
|
|
171
|
+
} catch {
|
|
172
|
+
return { kind: "invalid", message: `${path} is not valid JSON; fix it first.` };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (!isJson(parsed) || !isJsonObject(parsed))
|
|
176
|
+
return { kind: "invalid", message: `${path} is not a JSON object.` };
|
|
177
|
+
const servers = parsed.mcpServers ?? {};
|
|
178
|
+
if (!isJsonObject(servers))
|
|
179
|
+
return { kind: "invalid", message: `${path} has an "mcpServers" that is not an object.` };
|
|
180
|
+
const next = change(servers);
|
|
181
|
+
if (next === null)
|
|
182
|
+
return { kind: "missing" };
|
|
183
|
+
return { kind: "planned", text: `${JSON.stringify({ ...parsed, mcpServers: next }, null, 2)}
|
|
184
|
+
` };
|
|
185
|
+
}
|
|
186
|
+
async function writeMcpFile2(path, text) {
|
|
187
|
+
const target = await realpath(path).catch(() => path);
|
|
188
|
+
const temporary = `${target}.${randomUUID()}.tmp`;
|
|
189
|
+
await mkdir(dirname(target), { recursive: true });
|
|
190
|
+
await writeFile(temporary, text, { mode: 384 });
|
|
191
|
+
await rename(temporary, target);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export { readMcpFile2, parseMcpConfig2, planMcpEdit2, writeMcpFile2 };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// src/usage-history.ts
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { constants } from "node:fs";
|
|
5
|
+
import { mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { dirname } from "node:path";
|
|
7
|
+
import { Type } from "typebox";
|
|
8
|
+
function fileScanSchema2(scan) {
|
|
9
|
+
return Type.Object({
|
|
10
|
+
size: Type.Number({ minimum: 0 }),
|
|
11
|
+
mtimeMs: Type.Number(),
|
|
12
|
+
position: Type.Number({ minimum: 0 }),
|
|
13
|
+
committed: scan
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class UsageScanCache2 {
|
|
18
|
+
#files;
|
|
19
|
+
#changed = false;
|
|
20
|
+
constructor(files = []) {
|
|
21
|
+
this.#files = new Map(files);
|
|
22
|
+
}
|
|
23
|
+
keys() {
|
|
24
|
+
return this.#files.keys();
|
|
25
|
+
}
|
|
26
|
+
entries() {
|
|
27
|
+
return this.#files.entries();
|
|
28
|
+
}
|
|
29
|
+
get(path) {
|
|
30
|
+
return this.#files.get(path);
|
|
31
|
+
}
|
|
32
|
+
set(path, scan) {
|
|
33
|
+
if (this.#files.get(path) === scan)
|
|
34
|
+
return;
|
|
35
|
+
this.#files.set(path, scan);
|
|
36
|
+
this.#changed = true;
|
|
37
|
+
}
|
|
38
|
+
keepOnly(seen) {
|
|
39
|
+
for (const path of this.#files.keys()) {
|
|
40
|
+
if (seen.has(path))
|
|
41
|
+
continue;
|
|
42
|
+
this.#files.delete(path);
|
|
43
|
+
this.#changed = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
get changed() {
|
|
47
|
+
return this.#changed;
|
|
48
|
+
}
|
|
49
|
+
markSaved() {
|
|
50
|
+
this.#changed = false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function readHistory2(path, info, parser, cache, signal) {
|
|
54
|
+
const hit = cache?.get(path);
|
|
55
|
+
const unchanged = hit !== undefined && hit.size === info.size && hit.mtimeMs === info.mtimeMs;
|
|
56
|
+
if (unchanged && hit.position === info.size)
|
|
57
|
+
return hit.committed;
|
|
58
|
+
const from = unchanged || hit !== undefined && info.size > hit.size ? hit : undefined;
|
|
59
|
+
const fold = parser(from?.committed);
|
|
60
|
+
let position = from?.position ?? 0;
|
|
61
|
+
let unterminated;
|
|
62
|
+
for await (const line of historyLines(path, position, signal)) {
|
|
63
|
+
signal?.throwIfAborted();
|
|
64
|
+
if (!line.terminated) {
|
|
65
|
+
unterminated = line;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
fold.line(line);
|
|
69
|
+
position = line.end;
|
|
70
|
+
}
|
|
71
|
+
const committed = fold.scan();
|
|
72
|
+
cache?.set(path, unchanged ? hit : { size: info.size, mtimeMs: info.mtimeMs, position, committed });
|
|
73
|
+
if (unterminated === undefined)
|
|
74
|
+
return committed;
|
|
75
|
+
fold.line(unterminated);
|
|
76
|
+
return fold.scan();
|
|
77
|
+
}
|
|
78
|
+
async function* historyLines(path, start, signal) {
|
|
79
|
+
const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
80
|
+
try {
|
|
81
|
+
const stat = await file.stat();
|
|
82
|
+
if (!stat.isFile())
|
|
83
|
+
throw new Error("History is not a regular file.");
|
|
84
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
85
|
+
let pending = Buffer.alloc(0);
|
|
86
|
+
let pendingStart = start;
|
|
87
|
+
let position = start;
|
|
88
|
+
while (position < stat.size) {
|
|
89
|
+
signal?.throwIfAborted();
|
|
90
|
+
const { bytesRead } = await file.read(buffer, 0, Math.min(buffer.length, stat.size - position), position);
|
|
91
|
+
if (bytesRead === 0)
|
|
92
|
+
break;
|
|
93
|
+
position += bytesRead;
|
|
94
|
+
const read = buffer.subarray(0, bytesRead);
|
|
95
|
+
const chunk = pending.length === 0 ? read : Buffer.concat([pending, read]);
|
|
96
|
+
let from = 0;
|
|
97
|
+
for (let newline = chunk.indexOf(10);newline !== -1; newline = chunk.indexOf(10, from)) {
|
|
98
|
+
yield {
|
|
99
|
+
text: chunk.toString("utf8", from, newline),
|
|
100
|
+
terminated: true,
|
|
101
|
+
end: pendingStart + newline + 1
|
|
102
|
+
};
|
|
103
|
+
from = newline + 1;
|
|
104
|
+
}
|
|
105
|
+
pendingStart += from;
|
|
106
|
+
pending = Buffer.from(chunk.subarray(from));
|
|
107
|
+
}
|
|
108
|
+
if (pending.length > 0)
|
|
109
|
+
yield {
|
|
110
|
+
text: pending.toString("utf8"),
|
|
111
|
+
terminated: false,
|
|
112
|
+
end: pendingStart + pending.length
|
|
113
|
+
};
|
|
114
|
+
} finally {
|
|
115
|
+
await file.close();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function readCacheDocument2(path, validator) {
|
|
119
|
+
try {
|
|
120
|
+
const value = JSON.parse(await readFile(path, "utf8"));
|
|
121
|
+
return validator.Check(value) ? value : undefined;
|
|
122
|
+
} catch {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function writeCacheDocument2(path, document) {
|
|
127
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
128
|
+
try {
|
|
129
|
+
await mkdir(dirname(path), { recursive: true });
|
|
130
|
+
await writeFile(temporary, JSON.stringify(document), { mode: 384 });
|
|
131
|
+
await rename(temporary, path);
|
|
132
|
+
return true;
|
|
133
|
+
} catch {
|
|
134
|
+
await rm(temporary, { force: true }).catch(() => {
|
|
135
|
+
return;
|
|
136
|
+
});
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export { fileScanSchema2, UsageScanCache2, readHistory2, readCacheDocument2, writeCacheDocument2 };
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// src/local/tree.ts
|
|
2
|
+
import { watch } from "node:fs";
|
|
3
|
+
import { lstat, readFile, readdir } from "node:fs/promises";
|
|
4
|
+
import { basename, dirname, join, sep } from "node:path";
|
|
5
|
+
import ignore from "ignore";
|
|
6
|
+
var WALK_LIMIT2 = 50000;
|
|
7
|
+
var WALK_PAGE = 500;
|
|
8
|
+
function createIgnores2(root) {
|
|
9
|
+
const rules = new Map;
|
|
10
|
+
const loadRules = (directory) => {
|
|
11
|
+
const known = rules.get(directory);
|
|
12
|
+
if (known !== undefined)
|
|
13
|
+
return known;
|
|
14
|
+
const sources = [join(root, directory, ".gitignore")];
|
|
15
|
+
if (directory === "")
|
|
16
|
+
sources.push(join(root, ".git", "info", "exclude"));
|
|
17
|
+
const loaded = Promise.all(sources.map((source) => readFile(source, "utf8").catch(() => null))).then((texts) => {
|
|
18
|
+
const found = texts.filter((text) => text !== null);
|
|
19
|
+
return found.length === 0 ? null : found.reduce((matcher, text) => matcher.add(text), ignore());
|
|
20
|
+
});
|
|
21
|
+
rules.set(directory, loaded);
|
|
22
|
+
return loaded;
|
|
23
|
+
};
|
|
24
|
+
const matches = async (path, directory) => {
|
|
25
|
+
const segments = path.split("/");
|
|
26
|
+
if (segments.includes(".git"))
|
|
27
|
+
return true;
|
|
28
|
+
let result = false;
|
|
29
|
+
for (let depth = 0;depth < segments.length; depth += 1) {
|
|
30
|
+
const matcher = await loadRules(segments.slice(0, depth).join("/"));
|
|
31
|
+
if (matcher === null)
|
|
32
|
+
continue;
|
|
33
|
+
const relative = segments.slice(depth).join("/") + (directory ? "/" : "");
|
|
34
|
+
if (!ignore.isPathValid(relative.replace(/\/$/, "")))
|
|
35
|
+
continue;
|
|
36
|
+
const test = matcher.test(relative);
|
|
37
|
+
if (test.ignored)
|
|
38
|
+
result = true;
|
|
39
|
+
else if (test.unignored)
|
|
40
|
+
result = false;
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
matches,
|
|
46
|
+
async ignored(path, directory) {
|
|
47
|
+
const segments = path.split("/");
|
|
48
|
+
for (let depth = 1;depth < segments.length; depth += 1)
|
|
49
|
+
if (await matches(segments.slice(0, depth).join("/"), true))
|
|
50
|
+
return true;
|
|
51
|
+
return matches(path, directory);
|
|
52
|
+
},
|
|
53
|
+
forget(directory) {
|
|
54
|
+
rules.delete(directory);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function* walkTree2(root, options) {
|
|
59
|
+
const ignores = options.ignores ?? createIgnores2(root);
|
|
60
|
+
const limit = options.limit ?? WALK_LIMIT2;
|
|
61
|
+
const size = options.page ?? WALK_PAGE;
|
|
62
|
+
const queue = [trimSlashes(options.directory ?? "")];
|
|
63
|
+
let page = [];
|
|
64
|
+
let count = 0;
|
|
65
|
+
let denied = false;
|
|
66
|
+
for (let next = queue.shift();next !== undefined; next = queue.shift()) {
|
|
67
|
+
const directory = next;
|
|
68
|
+
const listed = await readdir(join(root, directory), { withFileTypes: true }).catch((error) => {
|
|
69
|
+
if (readErrorCode2(error) === "EACCES" || readErrorCode2(error) === "EPERM")
|
|
70
|
+
denied = true;
|
|
71
|
+
return null;
|
|
72
|
+
});
|
|
73
|
+
if (listed === null)
|
|
74
|
+
continue;
|
|
75
|
+
listed.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
76
|
+
const entries = await Promise.all(listed.map(async (dirent) => {
|
|
77
|
+
const path = directory === "" ? dirent.name : `${directory}/${dirent.name}`;
|
|
78
|
+
const kind = dirent.isSymbolicLink() ? "symlink" : dirent.isDirectory() ? "directory" : dirent.isFile() ? "file" : null;
|
|
79
|
+
if (kind === null || dirent.name === ".git")
|
|
80
|
+
return null;
|
|
81
|
+
if (options.ignored === "exclude" && await ignores.matches(path, kind === "directory"))
|
|
82
|
+
return null;
|
|
83
|
+
if (kind === "directory")
|
|
84
|
+
return { path, kind, size: null };
|
|
85
|
+
const stats = await lstat(join(root, path)).catch(() => null);
|
|
86
|
+
return stats === null ? null : { path, kind, size: stats.size };
|
|
87
|
+
}));
|
|
88
|
+
for (const entry of entries) {
|
|
89
|
+
if (entry === null)
|
|
90
|
+
continue;
|
|
91
|
+
page.push(entry);
|
|
92
|
+
count += 1;
|
|
93
|
+
if (entry.kind === "directory")
|
|
94
|
+
queue.push(entry.path);
|
|
95
|
+
if (count >= limit) {
|
|
96
|
+
yield { kind: "entries", entries: page };
|
|
97
|
+
yield { kind: "incomplete", reason: "limit" };
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (page.length >= size) {
|
|
101
|
+
yield { kind: "entries", entries: page };
|
|
102
|
+
page = [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (page.length > 0)
|
|
107
|
+
yield { kind: "entries", entries: page };
|
|
108
|
+
yield denied ? { kind: "incomplete", reason: "permission" } : { kind: "complete" };
|
|
109
|
+
}
|
|
110
|
+
function watchTree2(root, options, changed) {
|
|
111
|
+
let pending = new Set;
|
|
112
|
+
let timer = null;
|
|
113
|
+
let polling = null;
|
|
114
|
+
let watcher = null;
|
|
115
|
+
let fellBack = false;
|
|
116
|
+
let stopped = false;
|
|
117
|
+
const flush = () => {
|
|
118
|
+
timer = null;
|
|
119
|
+
const paths = pending;
|
|
120
|
+
pending = new Set;
|
|
121
|
+
if (!stopped && paths.size > 0)
|
|
122
|
+
changed(paths);
|
|
123
|
+
};
|
|
124
|
+
const note = (path) => {
|
|
125
|
+
pending.add(path);
|
|
126
|
+
timer ??= setTimeout(flush, options.delay);
|
|
127
|
+
};
|
|
128
|
+
const poll = (every) => {
|
|
129
|
+
let known = null;
|
|
130
|
+
const tick = async () => {
|
|
131
|
+
const seen = new Map;
|
|
132
|
+
for await (const frame of walkTree2(root, { ignored: "include" }))
|
|
133
|
+
if (frame.kind === "entries")
|
|
134
|
+
for (const entry of frame.entries) {
|
|
135
|
+
const stats = await lstat(join(root, entry.path)).catch(() => null);
|
|
136
|
+
seen.set(entry.path, stats === null ? "" : `${stats.size}:${stats.mtimeMs}`);
|
|
137
|
+
}
|
|
138
|
+
if (known !== null) {
|
|
139
|
+
for (const [path, stamp] of seen)
|
|
140
|
+
if (known.get(path) !== stamp)
|
|
141
|
+
note(path);
|
|
142
|
+
for (const path of known.keys())
|
|
143
|
+
if (!seen.has(path))
|
|
144
|
+
note(path);
|
|
145
|
+
}
|
|
146
|
+
known = seen;
|
|
147
|
+
};
|
|
148
|
+
const again = () => {
|
|
149
|
+
if (stopped)
|
|
150
|
+
return;
|
|
151
|
+
polling = setTimeout(() => {
|
|
152
|
+
tick().then(again, again);
|
|
153
|
+
}, every);
|
|
154
|
+
};
|
|
155
|
+
tick().then(again, again);
|
|
156
|
+
};
|
|
157
|
+
const fallback = () => {
|
|
158
|
+
watcher?.close();
|
|
159
|
+
watcher = null;
|
|
160
|
+
if (stopped || fellBack)
|
|
161
|
+
return;
|
|
162
|
+
fellBack = true;
|
|
163
|
+
options.unwatched?.();
|
|
164
|
+
if (options.poll !== null)
|
|
165
|
+
poll(options.poll);
|
|
166
|
+
};
|
|
167
|
+
try {
|
|
168
|
+
watcher = watch(root, { recursive: true }, (_event, filename) => note(filename === null ? "" : filename.split(sep).join("/")));
|
|
169
|
+
watcher.on("error", fallback);
|
|
170
|
+
} catch {
|
|
171
|
+
fallback();
|
|
172
|
+
}
|
|
173
|
+
return () => {
|
|
174
|
+
stopped = true;
|
|
175
|
+
watcher?.close();
|
|
176
|
+
if (timer !== null)
|
|
177
|
+
clearTimeout(timer);
|
|
178
|
+
if (polling !== null)
|
|
179
|
+
clearTimeout(polling);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function watchPath2(target, options, changed) {
|
|
183
|
+
let pending = new Set;
|
|
184
|
+
let timer = null;
|
|
185
|
+
let watchers = [];
|
|
186
|
+
let generation = 0;
|
|
187
|
+
let moved = false;
|
|
188
|
+
let stopped = false;
|
|
189
|
+
const flush = () => {
|
|
190
|
+
timer = null;
|
|
191
|
+
const paths = pending;
|
|
192
|
+
pending = new Set;
|
|
193
|
+
if (stopped)
|
|
194
|
+
return;
|
|
195
|
+
if (moved) {
|
|
196
|
+
moved = false;
|
|
197
|
+
arm();
|
|
198
|
+
}
|
|
199
|
+
if (paths.size > 0)
|
|
200
|
+
changed(paths);
|
|
201
|
+
};
|
|
202
|
+
const note = (path, replaced) => {
|
|
203
|
+
pending.add(path);
|
|
204
|
+
moved ||= replaced;
|
|
205
|
+
timer ??= setTimeout(flush, options.delay);
|
|
206
|
+
};
|
|
207
|
+
const listen = (at, recursive, heard) => {
|
|
208
|
+
try {
|
|
209
|
+
const watcher = watch(at, { recursive, persistent: false }, (event, name) => heard(event, name === null ? null : name.split(sep).join("/")));
|
|
210
|
+
watcher.on("error", () => note("", true));
|
|
211
|
+
watchers.push(watcher);
|
|
212
|
+
} catch {}
|
|
213
|
+
};
|
|
214
|
+
const arm = async () => {
|
|
215
|
+
generation += 1;
|
|
216
|
+
const armed = generation;
|
|
217
|
+
for (const watcher of watchers)
|
|
218
|
+
watcher.close();
|
|
219
|
+
watchers = [];
|
|
220
|
+
let existing = target;
|
|
221
|
+
let missing = null;
|
|
222
|
+
let stats = await lstat(existing).catch(() => null);
|
|
223
|
+
while (stats === null) {
|
|
224
|
+
const parent = dirname(existing);
|
|
225
|
+
if (parent === existing)
|
|
226
|
+
return;
|
|
227
|
+
missing = basename(existing);
|
|
228
|
+
existing = parent;
|
|
229
|
+
stats = await lstat(existing).catch(() => null);
|
|
230
|
+
}
|
|
231
|
+
if (stopped || armed !== generation)
|
|
232
|
+
return;
|
|
233
|
+
if (missing !== null) {
|
|
234
|
+
const awaited = missing;
|
|
235
|
+
if (stats.isDirectory())
|
|
236
|
+
listen(existing, false, (_event, name) => {
|
|
237
|
+
if (name === null || name === awaited)
|
|
238
|
+
note("", true);
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!stats.isDirectory()) {
|
|
243
|
+
listen(target, false, (event) => note("", event === "rename"));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const own = basename(target);
|
|
247
|
+
listen(target, true, (_event, name) => note(name ?? "", false));
|
|
248
|
+
listen(dirname(target), false, (event, name) => {
|
|
249
|
+
if (name === null || name === own)
|
|
250
|
+
note("", event === "rename");
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
arm();
|
|
254
|
+
return () => {
|
|
255
|
+
stopped = true;
|
|
256
|
+
generation += 1;
|
|
257
|
+
for (const watcher of watchers)
|
|
258
|
+
watcher.close();
|
|
259
|
+
watchers = [];
|
|
260
|
+
if (timer !== null)
|
|
261
|
+
clearTimeout(timer);
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function readErrorCode2(error) {
|
|
265
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
266
|
+
}
|
|
267
|
+
function trimSlashes(path) {
|
|
268
|
+
return path.replace(/\\/g, "/").replace(/^\.?\/+|\/+$/g, "").replace(/^\.$/, "");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export { WALK_LIMIT2, createIgnores2, walkTree2, watchTree2, watchPath2, readErrorCode2 };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
answerRepair2
|
|
3
|
+
} from "./canvas-preview-2zdyh9ny.js";
|
|
4
|
+
import {
|
|
5
|
+
package_default
|
|
6
|
+
} from "./canvas-preview-jeddjex8.js";
|
|
7
|
+
|
|
8
|
+
// src/deploy/vercel/server/utils/host.ts
|
|
9
|
+
import process from "node:process";
|
|
10
|
+
import { createModels } from "@nyte-ai/ai/models";
|
|
11
|
+
import { SdkError } from "@nyte-ai/schema";
|
|
12
|
+
import { composeHost } from "@nyte-ai/core/server";
|
|
13
|
+
import { postgresStore } from "@nyte-ai/host";
|
|
14
|
+
import { credentialStore } from "@nyte-ai/host/credentials";
|
|
15
|
+
import { seedSigner } from "@nyte-ai/host/identity";
|
|
16
|
+
import { listenFetch } from "@nyte-ai/host/listen-fetch";
|
|
17
|
+
import { sandboxProvider } from "@nyte-ai/host/providers";
|
|
18
|
+
var PAGE = 100;
|
|
19
|
+
var boot = async () => {
|
|
20
|
+
throw new SdkError("unavailable", "Supply a boot over your sandbox SDK in server/utils/host.ts.");
|
|
21
|
+
};
|
|
22
|
+
var building = null;
|
|
23
|
+
function vercelHost2(dispatch) {
|
|
24
|
+
const built = building ??= build(dispatch);
|
|
25
|
+
built.catch(() => {
|
|
26
|
+
if (building === built)
|
|
27
|
+
building = null;
|
|
28
|
+
});
|
|
29
|
+
return built;
|
|
30
|
+
}
|
|
31
|
+
async function build(dispatch) {
|
|
32
|
+
const connectionString = process.env.DATABASE_URL;
|
|
33
|
+
if (connectionString === undefined || connectionString === "")
|
|
34
|
+
throw new SdkError("unavailable", "Set DATABASE_URL to the Postgres database the host keeps.");
|
|
35
|
+
const identityKey = process.env.NYTE_IDENTITY_KEY;
|
|
36
|
+
const store = postgresStore({ connectionString });
|
|
37
|
+
const secrets = secretsFrom(process.env.NYTE_SECRET_KEY);
|
|
38
|
+
const description = { name: "nyte-host", platform: "vercel", version: package_default.version };
|
|
39
|
+
const host = await composeHost({
|
|
40
|
+
store,
|
|
41
|
+
models: createModels({ credentials: credentialStore(store, secrets) }),
|
|
42
|
+
plugins: [{ id: "sandbox", environment: sandboxProvider({ boot }) }],
|
|
43
|
+
workspace: { kind: "sandbox", locator: { name: process.env.NYTE_SANDBOX ?? "nyte" } },
|
|
44
|
+
execution: { kind: "dispatch", dispatch },
|
|
45
|
+
poll: 1000,
|
|
46
|
+
secrets,
|
|
47
|
+
home: null,
|
|
48
|
+
description
|
|
49
|
+
});
|
|
50
|
+
const repair = async () => {
|
|
51
|
+
let cursor;
|
|
52
|
+
do {
|
|
53
|
+
const page = await host.execution.repair({ cursor, limit: PAGE });
|
|
54
|
+
cursor = page.next ?? undefined;
|
|
55
|
+
} while (cursor !== undefined);
|
|
56
|
+
};
|
|
57
|
+
const listener = await listenFetch(host, {
|
|
58
|
+
transport: { kind: "fetch" },
|
|
59
|
+
auth: { kind: "token", token: process.env.NYTE_TOKEN ?? "" },
|
|
60
|
+
execution: { kind: "admit-only" },
|
|
61
|
+
describe: () => description,
|
|
62
|
+
...identityKey === undefined || identityKey === "" ? {} : { identity: await seedSigner(host.hostId, Uint8Array.fromBase64(identityKey)) },
|
|
63
|
+
before: (request) => answerRepair2(request, process.env.CRON_SECRET, repair)
|
|
64
|
+
}).catch(async (error) => {
|
|
65
|
+
await host.close();
|
|
66
|
+
throw error;
|
|
67
|
+
});
|
|
68
|
+
return { host, listener };
|
|
69
|
+
}
|
|
70
|
+
function secretsFrom(key) {
|
|
71
|
+
if (key === undefined || key === "")
|
|
72
|
+
return {
|
|
73
|
+
key: async () => {
|
|
74
|
+
throw new SdkError("unavailable", "Set NYTE_SECRET_KEY to a base64 32-byte key.");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const bytes = new Uint8Array(Buffer.from(key, "base64"));
|
|
78
|
+
return { key: async () => bytes };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { vercelHost2 };
|