@nyte-ai/host 0.0.18 → 0.0.21
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/THIRD-PARTY-NOTICES.md +66 -0
- package/dist/chunks/{canvas-preview-rp6fz0w5.js → canvas-preview-2jwbe14k.js} +5 -5
- package/dist/chunks/{canvas-preview-j1tnf51w.js → canvas-preview-3a6tst4d.js} +1 -1
- package/dist/chunks/{canvas-preview-m02v4yy6.js → canvas-preview-55s0y68g.js} +3 -2
- package/dist/chunks/{canvas-preview-xn10d4f8.js → canvas-preview-5ekjx6sw.js} +9 -4
- package/dist/chunks/{canvas-preview-g3qj3y3k.js → canvas-preview-8hr08aeh.js} +13 -3
- package/dist/chunks/{canvas-preview-75db60xy.js → canvas-preview-8xc0vn7v.js} +5 -4
- package/dist/chunks/{canvas-preview-zn2d0k9d.js → canvas-preview-af86jz8t.js} +14 -6
- package/dist/chunks/{canvas-preview-d9wwhbtz.js → canvas-preview-aq5j4p25.js} +1 -1
- package/dist/chunks/{canvas-preview-ewgspmsd.js → canvas-preview-b3r1tna2.js} +1 -1
- package/dist/chunks/canvas-preview-befkjea2.js +60 -0
- package/dist/chunks/canvas-preview-bxnqt8cd.js +103 -0
- package/dist/chunks/{canvas-preview-senpjwep.js → canvas-preview-crmzxxt8.js} +3 -3
- package/dist/chunks/{canvas-preview-ymgz0937.js → canvas-preview-drctegz4.js} +8 -4
- package/dist/chunks/{canvas-preview-yrfz2ak6.js → canvas-preview-e3vnkkc5.js} +6 -6
- package/dist/chunks/canvas-preview-ec2mt7es.js +108 -0
- package/dist/chunks/{canvas-preview-0nrnbt76.js → canvas-preview-eehzfefa.js} +199 -125
- package/dist/chunks/{canvas-preview-5hxat7hh.js → canvas-preview-geg2gckp.js} +8 -6
- package/dist/chunks/{canvas-preview-8xgt67ka.js → canvas-preview-gh7c9831.js} +20 -16
- package/dist/chunks/{canvas-preview-1ta3avah.js → canvas-preview-k2xaz0ym.js} +10 -5
- package/dist/chunks/canvas-preview-kp75gb63.js +141 -0
- package/dist/chunks/{canvas-preview-evh92ywd.js → canvas-preview-mstbvwz3.js} +26 -11
- package/dist/chunks/{canvas-preview-h8hshnff.js → canvas-preview-pkzqg96v.js} +1 -1
- package/dist/chunks/{canvas-preview-ehf3vp31.js → canvas-preview-vm9zdp2t.js} +45 -58
- package/dist/chunks/{canvas-preview-76fwvd6e.js → canvas-preview-w54ymtb3.js} +1 -1
- package/dist/chunks/canvas-preview-y5y6ghaw.js +125 -0
- package/dist/chunks/{canvas-preview-vax9ne4e.js → canvas-preview-y7kq0c8q.js} +22 -11
- package/dist/chunks/{canvas-preview-98aj5p34.js → canvas-preview-ycepb05s.js} +1 -1
- package/dist/src/canvas-preview.js +7 -3
- package/dist/src/deploy/cloudflare/worker.js +1 -1
- package/dist/src/deploy/vercel/server/routes/[...].js +2 -2
- package/dist/src/deploy/vercel/server/utils/host.js +1 -1
- package/dist/src/deploy/vercel/server/workflows/drive.js +3 -3
- 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 +0 -1
- package/dist/src/index.js +23 -22
- package/dist/src/listen-fetch.js +3 -3
- package/dist/src/listen.js +4 -4
- package/dist/src/local/command.d.ts +5 -0
- package/dist/src/local/command.js +6 -2
- package/dist/src/local/files.js +2 -2
- package/dist/src/local/git-watch.d.ts +1 -1
- package/dist/src/local/git-watch.js +1 -1
- package/dist/src/local/git.js +3 -3
- package/dist/src/local/github.js +2 -2
- package/dist/src/local/jobs.d.ts +1 -1
- package/dist/src/local/jobs.js +2 -1
- package/dist/src/local/provider.js +9 -9
- package/dist/src/local/snapshots.js +2 -2
- package/dist/src/local/worktrees.js +2 -2
- package/dist/src/mcp/connect.d.ts +0 -13
- package/dist/src/mcp/connect.js +1 -1
- package/dist/src/mcp/hub.js +2 -2
- package/dist/src/open.d.ts +2 -30
- package/dist/src/open.js +20 -18
- package/dist/src/serve/binding.js +1 -1
- package/dist/src/serve/handler.js +2 -2
- package/dist/src/sqlite.d.ts +2 -1
- package/dist/src/sqlite.js +4 -3
- package/dist/src/store-usage.js +4 -1
- package/dist/src/usage-history.d.ts +54 -0
- package/dist/src/usage-history.js +14 -0
- package/dist/src/usage-service.d.ts +2 -1
- package/dist/src/usage-service.js +7 -3
- package/dist/src/usage-worker.js +8 -4
- package/dist/src/usage.d.ts +150 -27
- package/dist/src/usage.js +12 -2
- package/package.json +16 -8
- package/src/canvas-preview.ts +4 -3
- 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 +19 -1
- package/src/local/command.ts +64 -12
- package/src/local/files.ts +8 -3
- package/src/local/git-watch.ts +16 -6
- package/src/local/git.ts +7 -2
- package/src/local/github.ts +3 -4
- package/src/local/jobs.ts +55 -69
- package/src/local/snapshots.ts +4 -2
- package/src/local/worktrees.ts +4 -4
- package/src/mcp/connect.ts +1 -14
- package/src/mcp/hub.ts +18 -9
- package/src/open.ts +16 -36
- package/src/serve/binding.ts +4 -3
- package/src/sqlite.ts +46 -9
- package/src/store-usage.ts +5 -2
- package/src/usage-history.ts +219 -0
- package/src/usage-service.ts +12 -3
- package/src/usage.ts +251 -176
- package/dist/chunks/canvas-preview-a3yw0j70.js +0 -72
- package/dist/chunks/canvas-preview-azg56359.js +0 -68
- package/dist/src/store-owner.d.ts +0 -15
- package/dist/src/store-owner.js +0 -9
- package/src/store-owner.ts +0 -98
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
connectServer2,
|
|
17
17
|
oauthSettings2
|
|
18
|
-
} from "./canvas-preview-
|
|
18
|
+
} from "./canvas-preview-pkzqg96v.js";
|
|
19
19
|
import {
|
|
20
20
|
toolExposure2,
|
|
21
21
|
checkToolSchemas2,
|
|
@@ -44,7 +44,18 @@ function hub(input) {
|
|
|
44
44
|
const contents = new Map;
|
|
45
45
|
const watched = new Map;
|
|
46
46
|
const userPath = join(input.home, ".nyte", "mcp.json");
|
|
47
|
+
const retiring = new Set;
|
|
47
48
|
let closed = false;
|
|
49
|
+
const retire = (client) => {
|
|
50
|
+
if (client === null)
|
|
51
|
+
return Promise.resolve();
|
|
52
|
+
const done = client.close().catch(() => {
|
|
53
|
+
return;
|
|
54
|
+
});
|
|
55
|
+
retiring.add(done);
|
|
56
|
+
done.then(() => retiring.delete(done));
|
|
57
|
+
return done;
|
|
58
|
+
};
|
|
48
59
|
const notify = (pool) => {
|
|
49
60
|
for (const listener of pool.listeners)
|
|
50
61
|
listener();
|
|
@@ -73,6 +84,7 @@ function hub(input) {
|
|
|
73
84
|
let listed = null;
|
|
74
85
|
let bridged = null;
|
|
75
86
|
let client = null;
|
|
87
|
+
let opening = null;
|
|
76
88
|
let stderr = () => null;
|
|
77
89
|
let challenge = null;
|
|
78
90
|
let ended = false;
|
|
@@ -104,9 +116,7 @@ function hub(input) {
|
|
|
104
116
|
const drop = (message) => {
|
|
105
117
|
const previous = client;
|
|
106
118
|
client = null;
|
|
107
|
-
previous
|
|
108
|
-
return;
|
|
109
|
-
});
|
|
119
|
+
retire(previous);
|
|
110
120
|
if (ended)
|
|
111
121
|
return;
|
|
112
122
|
set({ kind: "failed", message, stderr: stderr() });
|
|
@@ -118,11 +128,7 @@ function hub(input) {
|
|
|
118
128
|
await processEnvironment();
|
|
119
129
|
if (ended)
|
|
120
130
|
return;
|
|
121
|
-
const next = new TracingMcpClient2({
|
|
122
|
-
name: "nyte",
|
|
123
|
-
version: "0.1.0",
|
|
124
|
-
requestTimeoutMs: link.entry.timeoutMs
|
|
125
|
-
});
|
|
131
|
+
const next = new TracingMcpClient2({ name: "nyte", version: "0.1.0" });
|
|
126
132
|
next.onClose(() => {
|
|
127
133
|
if (client === next)
|
|
128
134
|
drop(`The connection to ${entry.name} closed.`);
|
|
@@ -143,9 +149,10 @@ function hub(input) {
|
|
|
143
149
|
}
|
|
144
150
|
try {
|
|
145
151
|
stderr = () => transport instanceof StdioTransport && transport.stderr !== "" ? transport.stderr.slice(-STDERR_TAIL) : null;
|
|
152
|
+
opening = next;
|
|
146
153
|
await next.connect(transport);
|
|
147
154
|
if (ended) {
|
|
148
|
-
await next
|
|
155
|
+
await retire(next);
|
|
149
156
|
return;
|
|
150
157
|
}
|
|
151
158
|
const refresh = async () => {
|
|
@@ -171,9 +178,7 @@ function hub(input) {
|
|
|
171
178
|
} catch (error) {
|
|
172
179
|
if (client === next)
|
|
173
180
|
client = null;
|
|
174
|
-
await next
|
|
175
|
-
return;
|
|
176
|
-
});
|
|
181
|
+
await retire(next);
|
|
177
182
|
if (ended)
|
|
178
183
|
return;
|
|
179
184
|
if (error instanceof McpOAuthAuthorizationRequiredError) {
|
|
@@ -223,9 +228,7 @@ function hub(input) {
|
|
|
223
228
|
clearTimeout(timer);
|
|
224
229
|
const previous = client;
|
|
225
230
|
client = null;
|
|
226
|
-
await
|
|
227
|
-
return;
|
|
228
|
-
});
|
|
231
|
+
await Promise.all([retire(previous), retire(opening)]);
|
|
229
232
|
}
|
|
230
233
|
};
|
|
231
234
|
connectTraced();
|
|
@@ -553,6 +556,7 @@ function hub(input) {
|
|
|
553
556
|
for (const pool of live)
|
|
554
557
|
shut(pool);
|
|
555
558
|
await Promise.all(links.map((link) => link.close()));
|
|
559
|
+
await Promise.all(retiring);
|
|
556
560
|
}
|
|
557
561
|
};
|
|
558
562
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readNyteUsage2
|
|
3
|
-
} from "./canvas-preview-
|
|
3
|
+
} from "./canvas-preview-geg2gckp.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
loadUsageScanCaches2,
|
|
6
|
+
saveUsageScanCaches2,
|
|
6
7
|
readLocalUsage2
|
|
7
|
-
} from "./canvas-preview-
|
|
8
|
+
} from "./canvas-preview-eehzfefa.js";
|
|
8
9
|
|
|
9
10
|
// src/usage-service.ts
|
|
10
11
|
import { join } from "node:path";
|
|
@@ -17,11 +18,13 @@ function catalogForUsage2(models) {
|
|
|
17
18
|
|
|
18
19
|
class UsageScanner2 {
|
|
19
20
|
home;
|
|
20
|
-
|
|
21
|
+
cachePath;
|
|
22
|
+
caches;
|
|
21
23
|
models;
|
|
22
24
|
scans = serial();
|
|
23
25
|
constructor(home) {
|
|
24
26
|
this.home = home;
|
|
27
|
+
this.cachePath = join(home, ".nyte", "usage-scan-cache.json");
|
|
25
28
|
this.models = createModels({
|
|
26
29
|
modelsStore: new FileModelsStore(join(home, ".nyte", "models-store.json"))
|
|
27
30
|
});
|
|
@@ -39,8 +42,10 @@ class UsageScanner2 {
|
|
|
39
42
|
const models = catalog === undefined ? this.models : {
|
|
40
43
|
getModels: (provider) => catalog.filter((model) => provider === undefined || model.provider === provider)
|
|
41
44
|
};
|
|
42
|
-
const
|
|
45
|
+
const caches = this.caches ??= await loadUsageScanCaches2(this.cachePath);
|
|
46
|
+
const local = await readLocalUsage2({ models, window: request.window, caches });
|
|
43
47
|
const report = readNyteUsage2(this.home, request.window);
|
|
48
|
+
this.scans.run(() => saveUsageScanCaches2(this.cachePath, caches));
|
|
44
49
|
return {
|
|
45
50
|
...report,
|
|
46
51
|
...local,
|
|
@@ -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 };
|
|
@@ -18,13 +18,11 @@ import {
|
|
|
18
18
|
sessionIndexChanges2,
|
|
19
19
|
sqlSessionIndex2
|
|
20
20
|
} from "./canvas-preview-0cmbkp2j.js";
|
|
21
|
-
import {
|
|
22
|
-
claimStore2
|
|
23
|
-
} from "./canvas-preview-a3yw0j70.js";
|
|
24
21
|
|
|
25
22
|
// src/sqlite.ts
|
|
26
23
|
import { mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
27
24
|
import { dirname, join } from "node:path";
|
|
25
|
+
import process from "node:process";
|
|
28
26
|
import { DatabaseSync } from "node:sqlite";
|
|
29
27
|
import { asSeq } from "@nyte-ai/schema";
|
|
30
28
|
import { dev } from "@nyte-ai/core";
|
|
@@ -43,14 +41,15 @@ import {
|
|
|
43
41
|
var BUSY_TIMEOUT_MS = 5000;
|
|
44
42
|
var BACKUPS_KEPT = 3;
|
|
45
43
|
var PUT_BODY = "INSERT INTO objects (id, body) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET body = excluded.body WHERE objects.body IS NULL";
|
|
44
|
+
function storePath2(home) {
|
|
45
|
+
return process.env["NYTE_DB"] ?? join(home, ".nyte", "nyte.db");
|
|
46
|
+
}
|
|
46
47
|
function sqliteStore2(input) {
|
|
47
48
|
let database = null;
|
|
48
49
|
let opening = null;
|
|
49
50
|
let closing = null;
|
|
50
51
|
let workers = null;
|
|
51
|
-
let release = null;
|
|
52
52
|
const open = async () => {
|
|
53
|
-
const claim = "path" in input ? claimStore2(input.path) : null;
|
|
54
53
|
let created;
|
|
55
54
|
try {
|
|
56
55
|
created = new DatabaseSync("path" in input ? input.path : ":memory:", {
|
|
@@ -65,11 +64,9 @@ function sqliteStore2(input) {
|
|
|
65
64
|
created.exec("PRAGMA optimize = 0x10012");
|
|
66
65
|
} catch (error) {
|
|
67
66
|
created?.close();
|
|
68
|
-
claim?.();
|
|
69
67
|
throw error;
|
|
70
68
|
}
|
|
71
69
|
database = created;
|
|
72
|
-
release = claim;
|
|
73
70
|
workers = "path" in input ? sqliteWorkers2(created, input.path) : null;
|
|
74
71
|
return connect(created, workers);
|
|
75
72
|
};
|
|
@@ -90,11 +87,10 @@ function sqliteStore2(input) {
|
|
|
90
87
|
return;
|
|
91
88
|
});
|
|
92
89
|
await workers?.close();
|
|
93
|
-
database
|
|
94
|
-
|
|
90
|
+
if (database !== null)
|
|
91
|
+
closeDatabase(database);
|
|
95
92
|
workers = null;
|
|
96
93
|
database = null;
|
|
97
|
-
release = null;
|
|
98
94
|
opening = null;
|
|
99
95
|
})().finally(() => {
|
|
100
96
|
closing = null;
|
|
@@ -103,6 +99,15 @@ function sqliteStore2(input) {
|
|
|
103
99
|
}
|
|
104
100
|
};
|
|
105
101
|
}
|
|
102
|
+
function closeDatabase(database) {
|
|
103
|
+
if ("bun" in process.versions) {
|
|
104
|
+
const empty = new DatabaseSync(":memory:");
|
|
105
|
+
empty.exec("CREATE TABLE closed (id INTEGER)");
|
|
106
|
+
database.deserialize(empty.serialize());
|
|
107
|
+
empty.close();
|
|
108
|
+
}
|
|
109
|
+
database.close();
|
|
110
|
+
}
|
|
106
111
|
function hasTable(database, name) {
|
|
107
112
|
return database.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(name) !== undefined;
|
|
108
113
|
}
|
|
@@ -219,6 +224,7 @@ function connect(database, workers) {
|
|
|
219
224
|
const listRange = database.prepare("SELECT name, target, revision FROM refs WHERE name >= ? AND name < ? ORDER BY name");
|
|
220
225
|
const putRef = database.prepare("INSERT INTO refs (name, target, revision) VALUES (?, ?, ?) ON CONFLICT (name) DO UPDATE SET target = excluded.target, revision = excluded.revision");
|
|
221
226
|
const deleteRef = database.prepare("DELETE FROM refs WHERE name = ?");
|
|
227
|
+
const audit = auditing(database);
|
|
222
228
|
const append = database.prepare("INSERT INTO log (session, seq, at, event) VALUES (?, ?, ?, ?)");
|
|
223
229
|
const readLog = database.prepare("SELECT seq, at, event FROM log WHERE session = ? AND seq > ? ORDER BY seq LIMIT ?");
|
|
224
230
|
const readTail = database.prepare("SELECT MAX(seq) AS tail FROM log WHERE session = ?");
|
|
@@ -295,12 +301,15 @@ function connect(database, workers) {
|
|
|
295
301
|
};
|
|
296
302
|
}
|
|
297
303
|
const touched = new Map;
|
|
304
|
+
const current = new Map([...before].map(([name, row]) => [name, row?.revision ?? null]));
|
|
298
305
|
const revisions = updates.map((update) => {
|
|
299
306
|
const revision = mintRevision2();
|
|
300
307
|
if (update.target === null)
|
|
301
308
|
deleteRef.run(update.name);
|
|
302
309
|
else
|
|
303
310
|
putRef.run(update.name, update.target, revision);
|
|
311
|
+
audit?.run(Date.now(), process.pid, fenceNames([update]).find((name) => name.startsWith("processes/")) ?? null, update.name, current.get(update.name) ?? null, update.target === null ? null : revision, update.target);
|
|
312
|
+
current.set(update.name, update.target === null ? null : revision);
|
|
304
313
|
updateSessionIndex(database, update.name, update.target, revision);
|
|
305
314
|
const session = parseSessionRef(update.name);
|
|
306
315
|
if (session !== null)
|
|
@@ -354,6 +363,12 @@ function connect(database, workers) {
|
|
|
354
363
|
}
|
|
355
364
|
};
|
|
356
365
|
}
|
|
366
|
+
function auditing(database) {
|
|
367
|
+
if (process.env.NYTE_STORE_AUDIT !== "1")
|
|
368
|
+
return null;
|
|
369
|
+
database.exec("CREATE TABLE IF NOT EXISTS store_audit (id INTEGER PRIMARY KEY, at INTEGER NOT NULL, pid INTEGER NOT NULL, writer TEXT, name TEXT NOT NULL, before TEXT, after TEXT, target TEXT)");
|
|
370
|
+
return database.prepare("INSERT INTO store_audit (at, pid, writer, name, before, after, target) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
|
371
|
+
}
|
|
357
372
|
function updateSessionIndex(database, name, target, revision) {
|
|
358
373
|
if (sessionIndexRef(name) === null)
|
|
359
374
|
return;
|
|
@@ -366,4 +381,4 @@ function write(database, statement, ...params) {
|
|
|
366
381
|
return (size > 64 * 1024 ? database.prepare(statement.sourceSQL) : statement).run(...params);
|
|
367
382
|
}
|
|
368
383
|
|
|
369
|
-
export { sqliteStore2 };
|
|
384
|
+
export { storePath2, sqliteStore2 };
|
|
@@ -54,7 +54,7 @@ function expand(values, server) {
|
|
|
54
54
|
value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, name) => {
|
|
55
55
|
const found = process.env[name];
|
|
56
56
|
if (found === undefined)
|
|
57
|
-
throw new Error(`${server} needs the environment variable ${name}. Set it where Nyte runs, then restart Nyte
|
|
57
|
+
throw new Error(`${server} needs the environment variable ${name}. Set it where Nyte runs, then restart Nyte.`);
|
|
58
58
|
return found;
|
|
59
59
|
})
|
|
60
60
|
]));
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
run2,
|
|
3
|
+
spawnCommand2,
|
|
4
|
+
killTree2,
|
|
5
|
+
findExecutable2
|
|
6
|
+
} from "./canvas-preview-bxnqt8cd.js";
|
|
7
|
+
|
|
1
8
|
// src/local/jobs.ts
|
|
2
9
|
import { spawn } from "node:child_process";
|
|
3
10
|
import { createRequire } from "node:module";
|
|
4
|
-
import { resolve } from "node:path";
|
|
11
|
+
import { basename, resolve } from "node:path";
|
|
5
12
|
import { asId, SdkError } from "@nyte-ai/schema";
|
|
6
13
|
import {
|
|
7
14
|
processEnvironment,
|
|
@@ -16,11 +23,7 @@ var PAUSE_BYTES = 256 * 1024;
|
|
|
16
23
|
var RESUME_BYTES = 64 * 1024;
|
|
17
24
|
var STOP_GRACE_MS = 2000;
|
|
18
25
|
var CLOSE_GRACE_MS = 2000;
|
|
19
|
-
var QUIET_MS = 300;
|
|
20
26
|
var DONE = { done: true, value: undefined };
|
|
21
|
-
var ESC = "\x1B";
|
|
22
|
-
var BEL = "\x07";
|
|
23
|
-
var PROMPT_MARKER = new RegExp(`${ESC}\\]133;([ABCD])`, "g");
|
|
24
27
|
var loaded;
|
|
25
28
|
function localExec2(root, options) {
|
|
26
29
|
const readers = new Set;
|
|
@@ -106,7 +109,7 @@ function localExec2(root, options) {
|
|
|
106
109
|
return { kind: "not-pty" };
|
|
107
110
|
return finished === null && driver.resize(size) ? { kind: "resized" } : { kind: "missing" };
|
|
108
111
|
},
|
|
109
|
-
idle: async () => finished
|
|
112
|
+
idle: async () => driver.pty && (finished !== null || driver.idle()),
|
|
110
113
|
stop: async () => ({ kind: "settled", outcome: await stop() }),
|
|
111
114
|
settled: (request) => waitEnd(ended, request),
|
|
112
115
|
close,
|
|
@@ -116,16 +119,16 @@ function localExec2(root, options) {
|
|
|
116
119
|
};
|
|
117
120
|
}
|
|
118
121
|
async function pipe(command, options, log, finish) {
|
|
119
|
-
const
|
|
122
|
+
const windows = process.platform === "win32";
|
|
120
123
|
const [file, ...args] = typeof command === "string" ? [command] : command;
|
|
121
124
|
const settings = {
|
|
122
125
|
cwd: options.cwd,
|
|
123
126
|
env: options.env,
|
|
124
|
-
detached:
|
|
127
|
+
detached: !windows,
|
|
125
128
|
stdio: "pipe",
|
|
126
129
|
windowsHide: true
|
|
127
130
|
};
|
|
128
|
-
const child = typeof command === "string" ? spawn(command, { ...settings, shell: true }) :
|
|
131
|
+
const child = typeof command === "string" ? spawn(command, { ...settings, shell: true }) : spawnCommand2(windows ? await findExecutable2(file ?? "") ?? file ?? "" : file ?? "", args, settings);
|
|
129
132
|
await new Promise((started, failed) => {
|
|
130
133
|
child.once("spawn", started);
|
|
131
134
|
child.once("error", (error) => failed(new SdkError("unavailable", `Cannot start ${file ?? ""}: ${error.message}`)));
|
|
@@ -146,12 +149,13 @@ async function pipe(command, options, log, finish) {
|
|
|
146
149
|
child.stdin?.on("error", () => {
|
|
147
150
|
return;
|
|
148
151
|
});
|
|
149
|
-
|
|
152
|
+
let killed = false;
|
|
153
|
+
child.on("close", (code) => finish(killed ? null : code));
|
|
150
154
|
child.on("exit", (code) => {
|
|
151
155
|
setTimeout(() => {
|
|
152
156
|
child.stdout?.destroy();
|
|
153
157
|
child.stderr?.destroy();
|
|
154
|
-
finish(code);
|
|
158
|
+
finish(killed ? null : code);
|
|
155
159
|
}, CLOSE_GRACE_MS).unref();
|
|
156
160
|
});
|
|
157
161
|
return {
|
|
@@ -165,8 +169,13 @@ async function pipe(command, options, log, finish) {
|
|
|
165
169
|
resize: () => false,
|
|
166
170
|
idle: () => false,
|
|
167
171
|
kill(signal) {
|
|
172
|
+
if (windows) {
|
|
173
|
+
killed = true;
|
|
174
|
+
killTree2(child, signal);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
168
177
|
try {
|
|
169
|
-
if (
|
|
178
|
+
if (child.pid !== undefined)
|
|
170
179
|
process.kill(-child.pid, signal);
|
|
171
180
|
else
|
|
172
181
|
child.kill(signal);
|
|
@@ -200,21 +209,7 @@ async function terminal(command, options, log, finish) {
|
|
|
200
209
|
throw new SdkError("unavailable", `Cannot start ${file ?? shell} in a pty: ${error instanceof Error ? error.message : String(error)}`);
|
|
201
210
|
}
|
|
202
211
|
const encoder = new TextEncoder;
|
|
203
|
-
|
|
204
|
-
let line = "";
|
|
205
|
-
let quietSince = 0;
|
|
206
|
-
term.onData((text) => {
|
|
207
|
-
log.push(encoder.encode(text));
|
|
208
|
-
quietSince = Date.now();
|
|
209
|
-
marker = lastMarker(text) ?? marker;
|
|
210
|
-
const plain = withoutEscapes(text);
|
|
211
|
-
const broken = plain.lastIndexOf(`
|
|
212
|
-
`);
|
|
213
|
-
line = (broken < 0 ? line + plain : plain.slice(broken + 1)).slice(-1024);
|
|
214
|
-
const returned = line.replace(/\r+$/, "").lastIndexOf("\r");
|
|
215
|
-
if (returned >= 0)
|
|
216
|
-
line = line.slice(returned + 1);
|
|
217
|
-
});
|
|
212
|
+
term.onData((text) => log.push(encoder.encode(text)));
|
|
218
213
|
term.onExit(({ exitCode, signal }) => finish(signal ? null : exitCode));
|
|
219
214
|
log.throttle({ pause: () => term.pause(), resume: () => term.resume() });
|
|
220
215
|
return {
|
|
@@ -226,10 +221,29 @@ async function terminal(command, options, log, finish) {
|
|
|
226
221
|
term.resize(size.columns, size.rows);
|
|
227
222
|
return true;
|
|
228
223
|
},
|
|
229
|
-
idle() {
|
|
230
|
-
if (
|
|
231
|
-
return
|
|
232
|
-
|
|
224
|
+
async idle() {
|
|
225
|
+
if (typeof command === "string" || command.length !== 0)
|
|
226
|
+
return false;
|
|
227
|
+
const result = await run2(windows ? "powershell.exe" : "ps", windows ? [
|
|
228
|
+
"-NoProfile",
|
|
229
|
+
"-NonInteractive",
|
|
230
|
+
"-Command",
|
|
231
|
+
`Get-CimInstance Win32_Process -Filter "ParentProcessId = ${term.pid} AND Name != 'conhost.exe' AND Name != 'OpenConsole.exe'" | Select-Object -ExpandProperty ProcessId`
|
|
232
|
+
] : ["-A", "-o", "pid=,ppid=,stat=,comm="], { cwd: options.cwd, timeoutMs: 1500 });
|
|
233
|
+
if (result.code !== 0)
|
|
234
|
+
throw new SdkError("unavailable", "Cannot inspect terminal processes");
|
|
235
|
+
const processes = result.stdout.toString("utf8").trim();
|
|
236
|
+
if (windows)
|
|
237
|
+
return processes === "";
|
|
238
|
+
return !processes.split(`
|
|
239
|
+
`).some((line) => {
|
|
240
|
+
const [pid, parent, state, ...name] = line.trim().split(/\s+/);
|
|
241
|
+
if (state?.startsWith("Z"))
|
|
242
|
+
return false;
|
|
243
|
+
if (Number(parent) === term.pid)
|
|
244
|
+
return true;
|
|
245
|
+
return Number(pid) === term.pid && basename(name.join(" ")).replace(/^-/, "") !== basename(shell);
|
|
246
|
+
});
|
|
233
247
|
},
|
|
234
248
|
kill(signal) {
|
|
235
249
|
try {
|
|
@@ -238,33 +252,6 @@ async function terminal(command, options, log, finish) {
|
|
|
238
252
|
}
|
|
239
253
|
};
|
|
240
254
|
}
|
|
241
|
-
function lastMarker(text) {
|
|
242
|
-
return Array.from(text.matchAll(PROMPT_MARKER), (match) => match[1]).at(-1) ?? null;
|
|
243
|
-
}
|
|
244
|
-
function withoutEscapes(text) {
|
|
245
|
-
let plain = "";
|
|
246
|
-
let at = 0;
|
|
247
|
-
while (at < text.length) {
|
|
248
|
-
const char = text[at] ?? "";
|
|
249
|
-
if (char !== ESC) {
|
|
250
|
-
plain += char;
|
|
251
|
-
at += 1;
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
const kind = text[at + 1] ?? "";
|
|
255
|
-
at += 2;
|
|
256
|
-
if (kind === "[") {
|
|
257
|
-
while (at < text.length && !/[@-~]/.test(text[at] ?? ""))
|
|
258
|
-
at += 1;
|
|
259
|
-
at += 1;
|
|
260
|
-
} else if (kind === "]") {
|
|
261
|
-
while (at < text.length && text[at] !== BEL && !(text[at] === ESC && text[at + 1] === "\\"))
|
|
262
|
-
at += 1;
|
|
263
|
-
at += text[at] === BEL ? 1 : 2;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
return plain;
|
|
267
|
-
}
|
|
268
255
|
function loadPty() {
|
|
269
256
|
if (loaded === undefined)
|
|
270
257
|
loaded = ptyFrom(() => createRequire(import.meta.url)("@lydell/node-pty"));
|