@nyte-ai/host 0.0.0-stage → 0.0.18
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 +800 -0
- package/dist/chunks/canvas-preview-0cmbkp2j.js +65 -0
- package/dist/chunks/canvas-preview-0nq75h9k.js +7 -0
- package/dist/chunks/canvas-preview-0nrnbt76.js +497 -0
- package/dist/chunks/canvas-preview-1sq7z9jq.js +17 -0
- package/dist/chunks/canvas-preview-1ta3avah.js +56 -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-5hxat7hh.js +239 -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-75db60xy.js +457 -0
- package/dist/chunks/canvas-preview-76fwvd6e.js +272 -0
- package/dist/chunks/canvas-preview-8xgt67ka.js +598 -0
- package/dist/chunks/canvas-preview-98aj5p34.js +176 -0
- package/dist/chunks/canvas-preview-9aqczacs.js +73 -0
- package/dist/chunks/canvas-preview-a3yw0j70.js +72 -0
- package/dist/chunks/canvas-preview-azg56359.js +68 -0
- package/dist/chunks/canvas-preview-d9wwhbtz.js +240 -0
- package/dist/chunks/canvas-preview-ehf3vp31.js +411 -0
- package/dist/chunks/canvas-preview-evh92ywd.js +369 -0
- package/dist/chunks/canvas-preview-ewgspmsd.js +81 -0
- package/dist/chunks/canvas-preview-g3qj3y3k.js +156 -0
- package/dist/chunks/canvas-preview-h8hshnff.js +63 -0
- package/dist/chunks/canvas-preview-j1tnf51w.js +36 -0
- package/dist/chunks/canvas-preview-kdqgrstc.js +194 -0
- package/dist/chunks/canvas-preview-kn7kvbfj.js +12 -0
- package/dist/chunks/canvas-preview-m02v4yy6.js +386 -0
- package/dist/chunks/canvas-preview-mgcx2b2p.js +271 -0
- package/dist/chunks/canvas-preview-rp4x6y1h.js +144 -0
- package/dist/chunks/canvas-preview-rp6fz0w5.js +130 -0
- package/dist/chunks/canvas-preview-s94ebwf5.js +112 -0
- package/dist/chunks/canvas-preview-senpjwep.js +518 -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-vaatfznm.js +189 -0
- package/dist/chunks/canvas-preview-vax9ne4e.js +341 -0
- package/dist/chunks/canvas-preview-wg80rbc2.js +46 -0
- package/dist/chunks/canvas-preview-xn10d4f8.js +608 -0
- package/dist/chunks/canvas-preview-yjz0f7n4.js +50 -0
- package/dist/chunks/canvas-preview-ymgz0937.js +587 -0
- package/dist/chunks/canvas-preview-yrfz2ak6.js +134 -0
- package/dist/chunks/canvas-preview-zaaj99wd.js +244 -0
- package/dist/chunks/canvas-preview-zn2d0k9d.js +133 -0
- package/dist/src/canvas-preview.d.ts +29 -0
- package/dist/src/canvas-preview.js +267 -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/index.d.ts +21 -0
- package/dist/src/index.js +120 -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 +15 -0
- package/dist/src/local/command.js +8 -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 +6 -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 +28 -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 +42 -0
- package/dist/src/open.js +44 -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 +7 -0
- package/dist/src/sqlite.js +10 -0
- package/dist/src/store-owner.d.ts +15 -0
- package/dist/src/store-owner.js +9 -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 +16 -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 +22 -0
- package/dist/src/usage-service.js +12 -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 +25 -0
- package/dist/src/usage.d.ts +72 -0
- package/dist/src/usage.js +13 -0
- package/package.json +132 -3
- package/src/canvas-preview.ts +402 -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/index.ts +21 -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 +109 -0
- package/src/local/feed.ts +70 -0
- package/src/local/files.ts +812 -0
- package/src/local/git-watch.ts +189 -0
- package/src/local/git.ts +914 -0
- package/src/local/github.ts +692 -0
- package/src/local/identity.ts +21 -0
- package/src/local/jobs.ts +587 -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 +776 -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 +105 -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 +469 -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 +628 -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 +528 -0
- package/src/store-owner.ts +98 -0
- package/src/store-rows.ts +110 -0
- package/src/store-usage.ts +344 -0
- package/src/usage-scan.ts +81 -0
- package/src/usage-service.ts +73 -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 +701 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sha2562
|
|
3
|
+
} from "./canvas-preview-0nq75h9k.js";
|
|
4
|
+
import {
|
|
5
|
+
run2
|
|
6
|
+
} from "./canvas-preview-azg56359.js";
|
|
7
|
+
import {
|
|
8
|
+
PATCH_LIMIT2,
|
|
9
|
+
textEntry2,
|
|
10
|
+
readPatches2,
|
|
11
|
+
lineStat2,
|
|
12
|
+
parseNumstat2,
|
|
13
|
+
parseNameStatus2
|
|
14
|
+
} from "./canvas-preview-tjhabynd.js";
|
|
15
|
+
import {
|
|
16
|
+
decodeText2
|
|
17
|
+
} from "./canvas-preview-kn7kvbfj.js";
|
|
18
|
+
import {
|
|
19
|
+
WALK_LIMIT2,
|
|
20
|
+
walkTree2
|
|
21
|
+
} from "./canvas-preview-mgcx2b2p.js";
|
|
22
|
+
|
|
23
|
+
// src/local/snapshots.ts
|
|
24
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
25
|
+
import {
|
|
26
|
+
copyFile,
|
|
27
|
+
lstat,
|
|
28
|
+
mkdir,
|
|
29
|
+
open,
|
|
30
|
+
readFile,
|
|
31
|
+
readlink,
|
|
32
|
+
rename,
|
|
33
|
+
rm,
|
|
34
|
+
writeFile
|
|
35
|
+
} from "node:fs/promises";
|
|
36
|
+
import { tmpdir } from "node:os";
|
|
37
|
+
import { join } from "node:path";
|
|
38
|
+
import { asId, SdkError } from "@nyte-ai/schema";
|
|
39
|
+
import { dev } from "@nyte-ai/core";
|
|
40
|
+
var GENERATION_NAME = /^g(\d{6})$/;
|
|
41
|
+
var NOTHING_KEPT = { id: "", root: [], last: null };
|
|
42
|
+
var ENV = {
|
|
43
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
44
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
45
|
+
GIT_LITERAL_PATHSPECS: "1",
|
|
46
|
+
LC_ALL: "C"
|
|
47
|
+
};
|
|
48
|
+
var CONFIG = ["-c", "core.quotepath=off", "-c", "color.ui=false", "-c", "diff.noprefix=false"];
|
|
49
|
+
var DIFF = [
|
|
50
|
+
"--no-color",
|
|
51
|
+
"--no-ext-diff",
|
|
52
|
+
"--no-textconv",
|
|
53
|
+
"--src-prefix=a/",
|
|
54
|
+
"--dst-prefix=b/",
|
|
55
|
+
"-M"
|
|
56
|
+
];
|
|
57
|
+
var BEFORE_KEPT = 1000;
|
|
58
|
+
var KEPT_REF = "refs/nyte/kept";
|
|
59
|
+
var LEGACY_REFS = "refs/nyte/snapshots/";
|
|
60
|
+
var GENERATION = 1000;
|
|
61
|
+
var KEEP_TRIES = 3;
|
|
62
|
+
var CHUNK = 64 * 1024;
|
|
63
|
+
function localSnapshots2(env, options) {
|
|
64
|
+
const binary = options.git ?? "git";
|
|
65
|
+
const befores = new Map;
|
|
66
|
+
const stamps = new Map;
|
|
67
|
+
const fresh = [];
|
|
68
|
+
let repositoryLookup;
|
|
69
|
+
let emptyTree;
|
|
70
|
+
let keeping;
|
|
71
|
+
let kept = null;
|
|
72
|
+
const keptTrees = new Set;
|
|
73
|
+
const git = (cwd, args, extra) => run2(binary, [...CONFIG, ...args], {
|
|
74
|
+
cwd,
|
|
75
|
+
env: { ...ENV, ...extra?.env },
|
|
76
|
+
input: extra?.input,
|
|
77
|
+
limit: extra?.limit
|
|
78
|
+
});
|
|
79
|
+
const findRepository = async () => {
|
|
80
|
+
const ran = await git(env.cwd, [
|
|
81
|
+
"rev-parse",
|
|
82
|
+
"--show-toplevel",
|
|
83
|
+
"--absolute-git-dir",
|
|
84
|
+
"--show-prefix"
|
|
85
|
+
]).catch(() => null);
|
|
86
|
+
if (ran === null || ran.code !== 0)
|
|
87
|
+
return null;
|
|
88
|
+
const [root, gitDir, prefix = ""] = ran.stdout.toString("utf8").split(`
|
|
89
|
+
`);
|
|
90
|
+
return root === undefined || gitDir === undefined ? null : { root, gitDir, prefix };
|
|
91
|
+
};
|
|
92
|
+
const repository = () => {
|
|
93
|
+
repositoryLookup ??= findRepository();
|
|
94
|
+
return repositoryLookup;
|
|
95
|
+
};
|
|
96
|
+
const checked = async (ran) => {
|
|
97
|
+
if (ran.code !== 0)
|
|
98
|
+
throw new SdkError("unavailable", ran.stderr.trim() || "git failed");
|
|
99
|
+
return ran.stdout.toString("utf8").trim();
|
|
100
|
+
};
|
|
101
|
+
const gitSnapshot = async (repo) => {
|
|
102
|
+
const write = async (seeded) => {
|
|
103
|
+
const index = join(tmpdir(), `nyte-index-${randomUUID()}`);
|
|
104
|
+
try {
|
|
105
|
+
if (seeded)
|
|
106
|
+
await copyFile(join(repo.gitDir, "index"), index).catch(() => {
|
|
107
|
+
return;
|
|
108
|
+
});
|
|
109
|
+
const indexEnv = { GIT_INDEX_FILE: index };
|
|
110
|
+
await checked(await git(env.cwd, ["add", "-A", "--", "."], { env: indexEnv }));
|
|
111
|
+
return await git(repo.root, repo.prefix === "" ? ["write-tree"] : ["write-tree", `--prefix=${repo.prefix}`], { env: indexEnv });
|
|
112
|
+
} finally {
|
|
113
|
+
await rm(index, { force: true });
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const first = await write(true);
|
|
117
|
+
const tree = await checked(first.code === 0 ? first : await write(false));
|
|
118
|
+
const id = asId(`git:${tree}`);
|
|
119
|
+
if (fresh.includes(tree) || keptTrees.has(tree))
|
|
120
|
+
return id;
|
|
121
|
+
fresh.push(tree);
|
|
122
|
+
keeping ??= keepTrees(repo).finally(() => {
|
|
123
|
+
keeping = undefined;
|
|
124
|
+
});
|
|
125
|
+
return id;
|
|
126
|
+
};
|
|
127
|
+
const keepTrees = async (repo) => {
|
|
128
|
+
for (let tries = 0;fresh.length > 0 && tries < KEEP_TRIES; tries += 1) {
|
|
129
|
+
const taken = fresh.splice(0);
|
|
130
|
+
let landed = false;
|
|
131
|
+
try {
|
|
132
|
+
landed = await rewriteKept(repo, taken);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
dev("snapshots", "keep failed", { error: String(error) });
|
|
135
|
+
}
|
|
136
|
+
if (!landed)
|
|
137
|
+
fresh.unshift(...taken);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const rewriteKept = async (repo, taken) => {
|
|
141
|
+
const listed = await checked(await git(repo.root, ["for-each-ref", "--format=%(objectname) %(refname)", "refs/nyte/"]));
|
|
142
|
+
const refs = listed.split(`
|
|
143
|
+
`).filter((line) => line !== "").map((line) => {
|
|
144
|
+
const [id = "", name = ""] = line.split(" ");
|
|
145
|
+
return { id, name };
|
|
146
|
+
});
|
|
147
|
+
const current = refs.find((ref) => ref.name === KEPT_REF)?.id ?? null;
|
|
148
|
+
const legacy = refs.filter((ref) => ref.name.startsWith(LEGACY_REFS));
|
|
149
|
+
const known = current === null ? NOTHING_KEPT : await readKept(repo, current);
|
|
150
|
+
const open = known.last !== null && known.last.trees.length < GENERATION ? known.last : null;
|
|
151
|
+
const openTrees = open?.trees ?? [];
|
|
152
|
+
const adding = [...new Set([...legacy.map((ref) => ref.id), ...taken])].filter((tree) => !keptTrees.has(tree) && !openTrees.includes(tree));
|
|
153
|
+
if (adding.length === 0 && legacy.length === 0)
|
|
154
|
+
return true;
|
|
155
|
+
const filled = [...openTrees, ...adding];
|
|
156
|
+
const first = open?.number ?? (known.last === null ? 0 : known.last.number + 1);
|
|
157
|
+
const generations = await Promise.all(Array.from({ length: Math.ceil(filled.length / GENERATION) }, async (_, index) => {
|
|
158
|
+
const trees = filled.slice(index * GENERATION, (index + 1) * GENERATION);
|
|
159
|
+
return {
|
|
160
|
+
number: first + index,
|
|
161
|
+
trees,
|
|
162
|
+
tree: await mktree(repo, trees.map((tree) => ({ tree, name: tree })))
|
|
163
|
+
};
|
|
164
|
+
}));
|
|
165
|
+
const root = [
|
|
166
|
+
...known.root.filter((entry) => entry.name !== open?.name),
|
|
167
|
+
...generations.map((generation) => ({
|
|
168
|
+
tree: generation.tree,
|
|
169
|
+
name: generationName(generation.number)
|
|
170
|
+
}))
|
|
171
|
+
];
|
|
172
|
+
const next = await mktree(repo, root);
|
|
173
|
+
const commands = [
|
|
174
|
+
current === null ? `create ${KEPT_REF} ${next}` : `update ${KEPT_REF} ${next} ${current}`,
|
|
175
|
+
...legacy.map((ref) => `delete ${ref.name} ${ref.id}`)
|
|
176
|
+
];
|
|
177
|
+
const updated = await git(repo.root, ["update-ref", "--stdin"], {
|
|
178
|
+
input: `${commands.join(`
|
|
179
|
+
`)}
|
|
180
|
+
`
|
|
181
|
+
});
|
|
182
|
+
if (updated.code !== 0) {
|
|
183
|
+
kept = null;
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
const newest = generations.at(-1);
|
|
187
|
+
kept = {
|
|
188
|
+
id: next,
|
|
189
|
+
root,
|
|
190
|
+
last: newest === undefined ? known.last : {
|
|
191
|
+
name: generationName(newest.number),
|
|
192
|
+
number: newest.number,
|
|
193
|
+
trees: newest.trees
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
for (const tree of filled)
|
|
197
|
+
keptTrees.add(tree);
|
|
198
|
+
return true;
|
|
199
|
+
};
|
|
200
|
+
const mktree = async (repo, entries) => checked(await git(repo.root, ["mktree"], {
|
|
201
|
+
input: entries.map((entry) => `040000 tree ${entry.tree} ${entry.name}
|
|
202
|
+
`).join("")
|
|
203
|
+
}));
|
|
204
|
+
const readTree = async (repo, id) => {
|
|
205
|
+
const listed = await checked(await git(repo.root, ["ls-tree", id]));
|
|
206
|
+
return listed.split(`
|
|
207
|
+
`).flatMap((line) => {
|
|
208
|
+
const [mode = "", type = "", rest = ""] = line.split(" ");
|
|
209
|
+
const [tree = "", name = ""] = rest.split("\t");
|
|
210
|
+
return mode === "040000" && type === "tree" ? [{ tree, name }] : [];
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
const readKept = async (repo, id) => {
|
|
214
|
+
if (kept?.id === id)
|
|
215
|
+
return kept;
|
|
216
|
+
const root = await readTree(repo, id);
|
|
217
|
+
const last = root.flatMap((entry) => {
|
|
218
|
+
const number = GENERATION_NAME.exec(entry.name)?.[1];
|
|
219
|
+
return number === undefined ? [] : [{ ...entry, number: Number(number) }];
|
|
220
|
+
}).toSorted((left, right) => left.number - right.number).at(-1);
|
|
221
|
+
if (last === undefined)
|
|
222
|
+
return { id, root, last: null };
|
|
223
|
+
const trees = (await readTree(repo, last.tree)).map((entry) => entry.tree);
|
|
224
|
+
for (const tree of trees)
|
|
225
|
+
keptTrees.add(tree);
|
|
226
|
+
return { id, root, last: { name: last.name, number: last.number, trees } };
|
|
227
|
+
};
|
|
228
|
+
const walkSnapshot = async () => {
|
|
229
|
+
const manifest = {};
|
|
230
|
+
let incomplete = false;
|
|
231
|
+
for await (const frame of walkTree2(env.cwd, {
|
|
232
|
+
ignored: "exclude",
|
|
233
|
+
limit: options.limit ?? WALK_LIMIT2
|
|
234
|
+
})) {
|
|
235
|
+
if (frame.kind === "incomplete")
|
|
236
|
+
incomplete = true;
|
|
237
|
+
if (frame.kind !== "entries")
|
|
238
|
+
continue;
|
|
239
|
+
for (const entry of frame.entries) {
|
|
240
|
+
if (entry.kind === "directory")
|
|
241
|
+
continue;
|
|
242
|
+
const hash = await storeEntry(options.store, join(env.cwd, entry.path), entry.kind, stamps).catch(() => null);
|
|
243
|
+
if (hash !== null)
|
|
244
|
+
manifest[entry.path] = hash;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const text = JSON.stringify({
|
|
248
|
+
incomplete,
|
|
249
|
+
files: Object.fromEntries(Object.entries(manifest).sort(([left], [right]) => left < right ? -1 : 1))
|
|
250
|
+
});
|
|
251
|
+
const id = sha2562(text);
|
|
252
|
+
await keep(join(options.store, "trees", `${id}.json`), text);
|
|
253
|
+
return asId(`tree:${id}`);
|
|
254
|
+
};
|
|
255
|
+
const take = async () => {
|
|
256
|
+
const started = Date.now();
|
|
257
|
+
const repo = await repository();
|
|
258
|
+
const id = await (repo === null ? walkSnapshot() : gitSnapshot(repo));
|
|
259
|
+
dev("snapshots", "take", { kind: repo === null ? "tree" : "git", ms: Date.now() - started });
|
|
260
|
+
return id;
|
|
261
|
+
};
|
|
262
|
+
const pair = async (from, to) => {
|
|
263
|
+
const kinds = new Set([from, to].flatMap((id) => id === "" ? [] : id.split(":", 1)));
|
|
264
|
+
if (kinds.size > 1)
|
|
265
|
+
throw new SdkError("invalid", `Snapshots ${from} and ${to} are of two kinds.`);
|
|
266
|
+
if (kinds.has("tree") || kinds.size === 0)
|
|
267
|
+
return {
|
|
268
|
+
kind: "tree",
|
|
269
|
+
from: await readWalkedTree(options.store, from),
|
|
270
|
+
to: await readWalkedTree(options.store, to)
|
|
271
|
+
};
|
|
272
|
+
const repo = await repository();
|
|
273
|
+
if (repo === null)
|
|
274
|
+
throw new SdkError("unavailable", `${env.cwd} is no longer in a git repository.`);
|
|
275
|
+
const empty = from === "" || to === "" ? await (emptyTree ??= git(repo.root, ["hash-object", "-t", "tree", "--stdin"], {
|
|
276
|
+
input: ""
|
|
277
|
+
}).then(checked)) : "";
|
|
278
|
+
const tree = (id) => id === "" ? empty : id.slice("git:".length);
|
|
279
|
+
return { kind: "git", repo, from: tree(from), to: tree(to) };
|
|
280
|
+
};
|
|
281
|
+
const changed = async (from, to) => {
|
|
282
|
+
if (from === to)
|
|
283
|
+
return [];
|
|
284
|
+
const compared = await pair(from, to);
|
|
285
|
+
if (compared.kind === "git") {
|
|
286
|
+
const [names, numstat] = await Promise.all([
|
|
287
|
+
git(compared.repo.root, [
|
|
288
|
+
"diff-tree",
|
|
289
|
+
"-r",
|
|
290
|
+
"-z",
|
|
291
|
+
"-M",
|
|
292
|
+
"--name-status",
|
|
293
|
+
compared.from,
|
|
294
|
+
compared.to
|
|
295
|
+
]),
|
|
296
|
+
git(compared.repo.root, [
|
|
297
|
+
"diff-tree",
|
|
298
|
+
"-r",
|
|
299
|
+
"-z",
|
|
300
|
+
"-M",
|
|
301
|
+
"--numstat",
|
|
302
|
+
compared.from,
|
|
303
|
+
compared.to
|
|
304
|
+
])
|
|
305
|
+
]);
|
|
306
|
+
const stats = parseNumstat2(await checked(numstat));
|
|
307
|
+
return parseNameStatus2(await checked(names)).map((file) => ({
|
|
308
|
+
...file,
|
|
309
|
+
stat: stats.get(file.path) ?? { kind: "unknown" }
|
|
310
|
+
}));
|
|
311
|
+
}
|
|
312
|
+
const { files, incomplete } = compareTrees(compared.from, compared.to);
|
|
313
|
+
return Promise.all(files.map(async (file) => ({
|
|
314
|
+
...file,
|
|
315
|
+
stat: incomplete.has(file.path) ? { kind: "unknown" } : await manifestStat(options.store, compared.from.files, compared.to.files, file)
|
|
316
|
+
})));
|
|
317
|
+
};
|
|
318
|
+
return {
|
|
319
|
+
before(run) {
|
|
320
|
+
const known = befores.get(run);
|
|
321
|
+
if (known !== undefined)
|
|
322
|
+
return known;
|
|
323
|
+
const taken = take();
|
|
324
|
+
befores.set(run, taken);
|
|
325
|
+
taken.catch(() => befores.delete(run));
|
|
326
|
+
for (const old of befores.keys()) {
|
|
327
|
+
if (befores.size <= BEFORE_KEPT)
|
|
328
|
+
break;
|
|
329
|
+
befores.delete(old);
|
|
330
|
+
}
|
|
331
|
+
return taken;
|
|
332
|
+
},
|
|
333
|
+
after: () => take(),
|
|
334
|
+
async diff(from, to) {
|
|
335
|
+
const started = Date.now();
|
|
336
|
+
const files = await changed(from, to);
|
|
337
|
+
dev("snapshots", "diff", { entries: files.length, ms: Date.now() - started });
|
|
338
|
+
return { revision: to, files };
|
|
339
|
+
},
|
|
340
|
+
async patches(from, to, paths, diff) {
|
|
341
|
+
const compared = await pair(from, to);
|
|
342
|
+
const reported = new Map((await changed(from, to)).map((file) => [file.path, file]));
|
|
343
|
+
const wanted = [...new Set(paths)].flatMap((path) => {
|
|
344
|
+
const file = reported.get(path);
|
|
345
|
+
return file === undefined ? [] : [file];
|
|
346
|
+
});
|
|
347
|
+
if (wanted.length === 0)
|
|
348
|
+
return [];
|
|
349
|
+
if (compared.kind === "tree") {
|
|
350
|
+
const { incomplete } = compareTrees(compared.from, compared.to);
|
|
351
|
+
return Promise.all(wanted.map(async (file) => incomplete.has(file.path) ? {
|
|
352
|
+
path: file.path,
|
|
353
|
+
status: file.status,
|
|
354
|
+
digest: "",
|
|
355
|
+
kind: "failed",
|
|
356
|
+
reason: "incomplete"
|
|
357
|
+
} : manifestEntry(options.store, compared.from.files, compared.to.files, file, diff)));
|
|
358
|
+
}
|
|
359
|
+
return readPatches2(wanted, (files, limit) => git(compared.repo.root, [
|
|
360
|
+
"diff",
|
|
361
|
+
...DIFF,
|
|
362
|
+
"-U3",
|
|
363
|
+
...diff?.ignoreWhitespace === true ? ["--ignore-all-space"] : [],
|
|
364
|
+
compared.from,
|
|
365
|
+
compared.to,
|
|
366
|
+
"--",
|
|
367
|
+
...files.flatMap((file) => file.from === undefined ? [file.path] : [file.from, file.path])
|
|
368
|
+
], { limit }), (file) => sha2562(`${to}\x00${file.path}`));
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
async function storeEntry(store, path, kind, stamps) {
|
|
373
|
+
const stats = await lstat(path);
|
|
374
|
+
const stamp = `${kind}:${stats.size}:${stats.mtimeMs}:${stats.ino}`;
|
|
375
|
+
const known = stamps.get(path);
|
|
376
|
+
if (known?.stamp === stamp)
|
|
377
|
+
return known.hash;
|
|
378
|
+
if (kind === "symlink") {
|
|
379
|
+
const target = new TextEncoder().encode(await readlink(path));
|
|
380
|
+
const hash = sha2562(target);
|
|
381
|
+
await keep(join(store, "objects", hash), target);
|
|
382
|
+
stamps.set(path, { stamp, hash });
|
|
383
|
+
return hash;
|
|
384
|
+
}
|
|
385
|
+
if (stats.size > PATCH_LIMIT2) {
|
|
386
|
+
const hash = await hashFile(path);
|
|
387
|
+
stamps.set(path, { stamp, hash });
|
|
388
|
+
return hash;
|
|
389
|
+
}
|
|
390
|
+
const bytes = await readFile(path);
|
|
391
|
+
const hash = sha2562(bytes);
|
|
392
|
+
await keep(join(store, "objects", hash), bytes);
|
|
393
|
+
stamps.set(path, { stamp, hash });
|
|
394
|
+
return hash;
|
|
395
|
+
}
|
|
396
|
+
async function hashFile(path) {
|
|
397
|
+
const hash = createHash("sha256");
|
|
398
|
+
const handle = await open(path, "r");
|
|
399
|
+
try {
|
|
400
|
+
const buffer = new Uint8Array(CHUNK);
|
|
401
|
+
for (;; ) {
|
|
402
|
+
const { bytesRead } = await handle.read(buffer, 0, CHUNK, null);
|
|
403
|
+
if (bytesRead === 0)
|
|
404
|
+
break;
|
|
405
|
+
hash.update(buffer.subarray(0, bytesRead));
|
|
406
|
+
}
|
|
407
|
+
} finally {
|
|
408
|
+
await handle.close();
|
|
409
|
+
}
|
|
410
|
+
return hash.digest("hex");
|
|
411
|
+
}
|
|
412
|
+
async function keep(path, contents) {
|
|
413
|
+
if (await lstat(path).then(() => true, () => false))
|
|
414
|
+
return;
|
|
415
|
+
await mkdir(join(path, ".."), { recursive: true });
|
|
416
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
417
|
+
try {
|
|
418
|
+
await writeFile(temporary, contents);
|
|
419
|
+
await rename(temporary, path);
|
|
420
|
+
} finally {
|
|
421
|
+
await rm(temporary, { force: true });
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async function readWalkedTree(store, id) {
|
|
425
|
+
if (id === "")
|
|
426
|
+
return { files: {}, incomplete: false };
|
|
427
|
+
if (!id.startsWith("tree:"))
|
|
428
|
+
throw new SdkError("invalid", `${id} is not a walked snapshot.`);
|
|
429
|
+
const text = await readFile(join(store, "trees", `${id.slice("tree:".length)}.json`), "utf8").catch(() => null);
|
|
430
|
+
if (text === null)
|
|
431
|
+
throw new SdkError("unknown", `No snapshot ${id} is stored.`);
|
|
432
|
+
const value = JSON.parse(text);
|
|
433
|
+
if (typeof value !== "object" || value === null || !("files" in value) || typeof value.files !== "object" || value.files === null || Array.isArray(value.files))
|
|
434
|
+
throw new SdkError("internal", `Snapshot ${id} is malformed.`);
|
|
435
|
+
return {
|
|
436
|
+
incomplete: "incomplete" in value && value.incomplete === true,
|
|
437
|
+
files: Object.fromEntries(Object.entries(value.files).flatMap(([path, hash]) => typeof hash === "string" ? [[path, hash]] : []))
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function compareTrees(before, after) {
|
|
441
|
+
const from = before.files;
|
|
442
|
+
const to = after.files;
|
|
443
|
+
const gone = Object.keys(from).filter((path) => !(path in to));
|
|
444
|
+
const fresh = Object.keys(to).filter((path) => !(path in from));
|
|
445
|
+
const incomplete = new Set([
|
|
446
|
+
...after.incomplete ? gone : [],
|
|
447
|
+
...before.incomplete ? fresh : []
|
|
448
|
+
]);
|
|
449
|
+
const deleted = after.incomplete ? [] : gone;
|
|
450
|
+
const added = before.incomplete ? [] : fresh;
|
|
451
|
+
const moved = new Map;
|
|
452
|
+
const byHash = new Map;
|
|
453
|
+
for (const path of deleted)
|
|
454
|
+
byHash.set(from[path] ?? "", [...byHash.get(from[path] ?? "") ?? [], path]);
|
|
455
|
+
for (const path of added) {
|
|
456
|
+
const source = byHash.get(to[path] ?? "")?.shift();
|
|
457
|
+
if (source !== undefined)
|
|
458
|
+
moved.set(path, source);
|
|
459
|
+
}
|
|
460
|
+
const sources = new Set(moved.values());
|
|
461
|
+
const files = [
|
|
462
|
+
...deleted.filter((path) => !sources.has(path)).map((path) => ({ path, status: "deleted" })),
|
|
463
|
+
...added.map((path) => {
|
|
464
|
+
const source = moved.get(path);
|
|
465
|
+
return source === undefined ? { path, status: "added" } : { path, status: "renamed", from: source };
|
|
466
|
+
}),
|
|
467
|
+
...Object.keys(to).filter((path) => (path in from) && from[path] !== to[path]).map((path) => ({ path, status: "modified" })),
|
|
468
|
+
...[...incomplete].map((path) => ({ path, status: "modified" }))
|
|
469
|
+
];
|
|
470
|
+
return {
|
|
471
|
+
files: files.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0),
|
|
472
|
+
incomplete
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
async function sideText(store, hash) {
|
|
476
|
+
if (hash === undefined)
|
|
477
|
+
return null;
|
|
478
|
+
const bytes = await readFile(join(store, "objects", hash)).catch(() => {
|
|
479
|
+
return;
|
|
480
|
+
});
|
|
481
|
+
return bytes === undefined ? undefined : decodeText2(bytes) ?? undefined;
|
|
482
|
+
}
|
|
483
|
+
async function stored(store, hash) {
|
|
484
|
+
if (hash === undefined)
|
|
485
|
+
return "absent";
|
|
486
|
+
const bytes = await readFile(join(store, "objects", hash)).catch(() => {
|
|
487
|
+
return;
|
|
488
|
+
});
|
|
489
|
+
if (bytes === undefined)
|
|
490
|
+
return "missing";
|
|
491
|
+
return decodeText2(bytes) === null ? "binary" : "text";
|
|
492
|
+
}
|
|
493
|
+
async function manifestStat(store, from, to, file) {
|
|
494
|
+
const before = from[file.from ?? file.path];
|
|
495
|
+
const after = to[file.path];
|
|
496
|
+
const kinds = await Promise.all([stored(store, before), stored(store, after)]);
|
|
497
|
+
if (kinds.includes("missing"))
|
|
498
|
+
return { kind: "unknown" };
|
|
499
|
+
if (kinds.includes("binary"))
|
|
500
|
+
return { kind: "binary" };
|
|
501
|
+
return lineStat2(await sideText(store, before) ?? "", await sideText(store, after) ?? "");
|
|
502
|
+
}
|
|
503
|
+
async function manifestEntry(store, from, to, file, diff) {
|
|
504
|
+
const before = from[file.from ?? file.path];
|
|
505
|
+
const after = to[file.path];
|
|
506
|
+
const kinds = await Promise.all([stored(store, before), stored(store, after)]);
|
|
507
|
+
const digest = sha2562(`${before ?? ""}\x00${after ?? ""}`);
|
|
508
|
+
if (kinds.includes("missing"))
|
|
509
|
+
return { path: file.path, status: file.status, digest, kind: "too-large", limit: PATCH_LIMIT2 };
|
|
510
|
+
if (kinds.includes("binary"))
|
|
511
|
+
return { path: file.path, status: file.status, digest, kind: "binary" };
|
|
512
|
+
return textEntry2(file.path, file.status, await sideText(store, before) ?? null, await sideText(store, after) ?? null, diff);
|
|
513
|
+
}
|
|
514
|
+
function generationName(number) {
|
|
515
|
+
return `g${String(number).padStart(6, "0")}`;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export { localSnapshots2 };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readKeyFile2
|
|
3
|
+
} from "./canvas-preview-wg80rbc2.js";
|
|
4
|
+
|
|
5
|
+
// src/secrets.ts
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
function defaultSecrets2(home) {
|
|
8
|
+
const path = join(home, ".nyte", "secret.key");
|
|
9
|
+
let key = null;
|
|
10
|
+
return {
|
|
11
|
+
key: () => {
|
|
12
|
+
const known = key ?? readKeyFile2(path, "secrets key");
|
|
13
|
+
key = known;
|
|
14
|
+
known.catch(() => {
|
|
15
|
+
if (key === known)
|
|
16
|
+
key = null;
|
|
17
|
+
});
|
|
18
|
+
return known;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { defaultSecrets2 };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/usage-summary.ts
|
|
2
|
+
import { addUsage } from "@nyte-ai/core/server";
|
|
3
|
+
function emptyModelUsage2() {
|
|
4
|
+
return {
|
|
5
|
+
input: 0,
|
|
6
|
+
output: 0,
|
|
7
|
+
cacheRead: 0,
|
|
8
|
+
cacheWrite: 0,
|
|
9
|
+
totalTokens: 0,
|
|
10
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function recordedUsage2(usage) {
|
|
14
|
+
return {
|
|
15
|
+
input: usage.input,
|
|
16
|
+
output: usage.output,
|
|
17
|
+
cacheRead: usage.cacheRead,
|
|
18
|
+
cacheWrite: usage.cacheWrite,
|
|
19
|
+
reasoning: usage.reasoning ?? null,
|
|
20
|
+
total: usage.totalTokens,
|
|
21
|
+
cost: { ...usage.cost }
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function emptyUsageSummary2() {
|
|
25
|
+
return {
|
|
26
|
+
models: [],
|
|
27
|
+
compaction: recordedUsage2(emptyModelUsage2()),
|
|
28
|
+
tools: recordedUsage2(emptyModelUsage2()),
|
|
29
|
+
total: recordedUsage2(emptyModelUsage2()),
|
|
30
|
+
cacheSavings: 0
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function summarizeModels2(rows) {
|
|
34
|
+
const models = new Map;
|
|
35
|
+
let total = recordedUsage2(emptyModelUsage2());
|
|
36
|
+
let savings = 0;
|
|
37
|
+
for (const row of rows) {
|
|
38
|
+
savings += cacheSavings2(row.usage);
|
|
39
|
+
const key = JSON.stringify([row.provider, row.model]);
|
|
40
|
+
const previous = models.get(key);
|
|
41
|
+
models.set(key, previous === undefined ? row : { ...row, turns: previous.turns + row.turns, usage: addUsage(previous.usage, row.usage) });
|
|
42
|
+
total = addUsage(total, row.usage);
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
...emptyUsageSummary2(),
|
|
46
|
+
models: [...models.values()].toSorted((left, right) => right.usage.cost.total - left.usage.cost.total || right.usage.total - left.usage.total || left.model.localeCompare(right.model) || left.provider.localeCompare(right.provider)),
|
|
47
|
+
total,
|
|
48
|
+
cacheSavings: savings
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function cacheSavings2(usage) {
|
|
52
|
+
if (usage.input === 0)
|
|
53
|
+
return 0;
|
|
54
|
+
return usage.cacheRead * (usage.cost.input / usage.input) - usage.cost.cacheRead;
|
|
55
|
+
}
|
|
56
|
+
function usageTokens2(usage) {
|
|
57
|
+
return usage.total || usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { emptyModelUsage2, recordedUsage2, emptyUsageSummary2, summarizeModels2, cacheSavings2, usageTokens2 };
|