@nylorun/cli 0.2.1-beta → 0.3.0-beta
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/CHANGELOG.md +34 -0
- package/README.md +87 -40
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +132 -220
- package/dist/dev-entry.d.ts +5 -1
- package/dist/dev-entry.js +7 -6
- package/dist/dev.d.ts +45 -4
- package/dist/dev.js +245 -32
- package/dist/doctor.d.ts +25 -3
- package/dist/doctor.js +104 -16
- package/dist/environment.d.ts +4 -1
- package/dist/environment.js +12 -3
- package/dist/launcher.d.ts +5 -5
- package/dist/launcher.js +6 -3
- package/dist/model/configure.d.ts +30 -0
- package/dist/model/configure.js +117 -11
- package/dist/model/host-model.d.ts +5 -3
- package/dist/model/host-model.js +36 -15
- package/dist/project/attach.d.ts +32 -0
- package/dist/project/attach.js +241 -0
- package/dist/project/create-tenant.d.ts +26 -0
- package/dist/project/create-tenant.js +65 -0
- package/dist/project/credentials.d.ts +18 -0
- package/dist/project/credentials.js +64 -0
- package/dist/project/link.d.ts +20 -0
- package/dist/project/link.js +77 -0
- package/dist/project/root.d.ts +9 -0
- package/dist/project/root.js +51 -0
- package/dist/project/seed.d.ts +19 -0
- package/dist/project/seed.js +94 -0
- package/dist/runtime/baseline.d.ts +2 -0
- package/dist/runtime/baseline.js +11 -0
- package/dist/runtime/commands.d.ts +16 -0
- package/dist/runtime/commands.js +292 -0
- package/dist/runtime/ephemeral.d.ts +37 -0
- package/dist/runtime/ephemeral.js +84 -0
- package/dist/runtime/exit-codes.d.ts +9 -0
- package/dist/runtime/exit-codes.js +43 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +6 -0
- package/dist/runtime/launcher.d.ts +81 -0
- package/dist/runtime/launcher.js +243 -0
- package/dist/runtime/progress.d.ts +10 -0
- package/dist/runtime/progress.js +8 -0
- package/dist/runtime/version.d.ts +6 -0
- package/dist/runtime/version.js +27 -0
- package/dist/tenant/commands.d.ts +4 -0
- package/dist/tenant/commands.js +262 -0
- package/package.json +6 -3
- package/dist/project-runner.d.ts +0 -20
- package/dist/project-runner.js +0 -141
- package/dist/runtime-host.d.ts +0 -39
- package/dist/runtime-host.js +0 -355
- package/dist/scope.d.ts +0 -65
- package/dist/scope.js +0 -191
package/dist/dev.d.ts
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
|
+
export interface DevelopOptions {
|
|
2
|
+
entry?: string;
|
|
3
|
+
flags?: readonly string[];
|
|
4
|
+
projectRoot?: string;
|
|
5
|
+
home?: string;
|
|
6
|
+
}
|
|
7
|
+
export type DevelopmentPreflight = {
|
|
8
|
+
tsx: string;
|
|
9
|
+
entry: string;
|
|
10
|
+
ephemeral: boolean;
|
|
11
|
+
localUi: boolean;
|
|
12
|
+
studio: boolean;
|
|
13
|
+
open: boolean;
|
|
14
|
+
};
|
|
1
15
|
/**
|
|
2
|
-
* Everything that can be checked without contacting the
|
|
3
|
-
* never causes a Runtime to be started on its behalf.
|
|
16
|
+
* Everything that can be checked without contacting the Host, so a Project that
|
|
17
|
+
* cannot run never causes a Runtime Host to be started on its behalf.
|
|
4
18
|
*/
|
|
5
|
-
export declare function developmentPreflight(args
|
|
6
|
-
|
|
19
|
+
export declare function developmentPreflight(args?: readonly string[]): DevelopmentPreflight;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve `@nylorun/studio` from the Project and call `startStudio`.
|
|
22
|
+
* Used by `nylorun studio` and `nylorun dev --local-ui`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function startStudio(options: {
|
|
25
|
+
runtimeUrl: string;
|
|
26
|
+
serverKey: string;
|
|
27
|
+
tenant: {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
};
|
|
31
|
+
open: boolean;
|
|
32
|
+
port?: number;
|
|
33
|
+
/** When true, force local UI. When false/omit, use startStudio default (hosted). */
|
|
34
|
+
localUi?: boolean;
|
|
35
|
+
cacheDir?: string;
|
|
36
|
+
projectRoot?: string;
|
|
37
|
+
}): Promise<{
|
|
38
|
+
address: string;
|
|
39
|
+
launchUrl: string;
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* `nylorun dev [entry]` — D§12 steps 1–4.
|
|
44
|
+
* Spawns `tsx watch <entry>` with the three Project environment variables.
|
|
45
|
+
* `--local-ui` also starts the Studio proxy with a local dashboard.
|
|
46
|
+
*/
|
|
47
|
+
export declare function develop(options?: DevelopOptions): Promise<number>;
|
package/dist/dev.js
CHANGED
|
@@ -1,15 +1,41 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import {
|
|
3
|
+
import { basename, join, resolve } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { loadProjectEnvironment } from "./environment.js";
|
|
6
|
+
import { attachProject } from "./project/attach.js";
|
|
7
|
+
import { seedTenantFromProject } from "./project/seed.js";
|
|
8
|
+
import { requireProjectRoot } from "./project/root.js";
|
|
9
|
+
import { resolveHome } from "./runtime/launcher.js";
|
|
10
|
+
const DEV_FLAGS = ["--ephemeral", "--local-ui", "--no-studio", "--no-open"];
|
|
5
11
|
/**
|
|
6
|
-
* Everything that can be checked without contacting the
|
|
7
|
-
* never causes a Runtime to be started on its behalf.
|
|
12
|
+
* Everything that can be checked without contacting the Host, so a Project that
|
|
13
|
+
* cannot run never causes a Runtime Host to be started on its behalf.
|
|
8
14
|
*/
|
|
9
|
-
export function developmentPreflight(args) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
export function developmentPreflight(args = []) {
|
|
16
|
+
const flags = [];
|
|
17
|
+
let entry;
|
|
18
|
+
for (const arg of args) {
|
|
19
|
+
if (DEV_FLAGS.includes(arg)) {
|
|
20
|
+
flags.push(arg);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (arg.startsWith("-")) {
|
|
24
|
+
throw new Error("Usage: nylorun dev [entry] [--ephemeral] [--local-ui] [--no-studio] [--no-open]\nDefault entry: src/main.ts");
|
|
25
|
+
}
|
|
26
|
+
if (entry !== undefined) {
|
|
27
|
+
throw new Error("Usage: nylorun dev [entry] [--ephemeral] [--local-ui] [--no-studio] [--no-open]\nDefault entry: src/main.ts");
|
|
28
|
+
}
|
|
29
|
+
entry = arg;
|
|
30
|
+
}
|
|
31
|
+
if (new Set(flags).size !== flags.length) {
|
|
32
|
+
throw new Error("Usage: nylorun dev [entry] [--ephemeral] [--local-ui] [--no-studio] [--no-open]\nDefault entry: src/main.ts");
|
|
33
|
+
}
|
|
34
|
+
const localUi = flags.includes("--local-ui");
|
|
35
|
+
const noStudio = flags.includes("--no-studio");
|
|
36
|
+
if (localUi && noStudio) {
|
|
37
|
+
throw new Error("--local-ui cannot be combined with --no-studio.");
|
|
38
|
+
}
|
|
13
39
|
const require = createRequire(join(process.cwd(), "package.json"));
|
|
14
40
|
let tsx;
|
|
15
41
|
try {
|
|
@@ -18,48 +44,235 @@ export function developmentPreflight(args) {
|
|
|
18
44
|
catch {
|
|
19
45
|
throw new Error("Install tsx to use nylorun dev");
|
|
20
46
|
}
|
|
21
|
-
if (
|
|
47
|
+
if (localUi) {
|
|
22
48
|
try {
|
|
23
49
|
require.resolve("@nylorun/studio");
|
|
24
50
|
}
|
|
25
51
|
catch {
|
|
26
|
-
throw new Error("Install @nylorun/studio
|
|
52
|
+
throw new Error("Install @nylorun/studio to use the Studio dashboard.");
|
|
27
53
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export async function develop(args) {
|
|
31
|
-
const tsx = developmentPreflight(args);
|
|
32
|
-
const child = spawn(process.execPath, [
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
33
56
|
tsx,
|
|
34
|
-
"
|
|
35
|
-
"--
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
entry: entry ?? "src/main.ts",
|
|
58
|
+
ephemeral: flags.includes("--ephemeral"),
|
|
59
|
+
localUi,
|
|
60
|
+
studio: localUi,
|
|
61
|
+
open: !flags.includes("--no-open"),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolve `@nylorun/studio` from the Project and call `startStudio`.
|
|
66
|
+
* Used by `nylorun studio` and `nylorun dev --local-ui`.
|
|
67
|
+
*/
|
|
68
|
+
export async function startStudio(options) {
|
|
69
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
70
|
+
let entry;
|
|
71
|
+
try {
|
|
72
|
+
entry = createRequire(join(projectRoot, "package.json")).resolve("@nylorun/studio");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
throw new Error("Install @nylorun/studio to use the Studio dashboard.");
|
|
76
|
+
}
|
|
77
|
+
const studio = await import(pathToFileURL(entry).href);
|
|
78
|
+
const cacheDir = options.cacheDir ?? resolveHome();
|
|
79
|
+
const ui = options.localUi ? "local" : undefined;
|
|
80
|
+
return studio.startStudio({
|
|
81
|
+
runtimeUrl: options.runtimeUrl,
|
|
82
|
+
serverKey: options.serverKey,
|
|
83
|
+
tenant: options.tenant,
|
|
84
|
+
open: options.open,
|
|
85
|
+
cacheDir,
|
|
86
|
+
...(ui === undefined ? {} : { ui }),
|
|
87
|
+
...(options.port === undefined ? {} : { port: options.port }),
|
|
44
88
|
});
|
|
45
|
-
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* `nylorun dev [entry]` — D§12 steps 1–4.
|
|
92
|
+
* Spawns `tsx watch <entry>` with the three Project environment variables.
|
|
93
|
+
* `--local-ui` also starts the Studio proxy with a local dashboard.
|
|
94
|
+
*/
|
|
95
|
+
export async function develop(options = {}) {
|
|
96
|
+
const normalized = options;
|
|
97
|
+
const projectRoot = normalized.projectRoot ?? requireProjectRoot();
|
|
98
|
+
const previous = process.cwd();
|
|
99
|
+
process.chdir(projectRoot);
|
|
100
|
+
let preflight;
|
|
101
|
+
try {
|
|
102
|
+
preflight = developmentPreflight([
|
|
103
|
+
...(normalized.entry ? [normalized.entry] : []),
|
|
104
|
+
...(normalized.flags ?? []),
|
|
105
|
+
]);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
process.chdir(previous);
|
|
109
|
+
}
|
|
110
|
+
if (preflight.ephemeral) {
|
|
111
|
+
const { startEphemeral } = await import("./runtime/ephemeral.js");
|
|
112
|
+
const { writeLink } = await import("./project/link.js");
|
|
113
|
+
const { writeCredentials } = await import("./project/credentials.js");
|
|
114
|
+
const ephemeral = await startEphemeral({
|
|
115
|
+
name: basename(projectRoot),
|
|
116
|
+
});
|
|
117
|
+
try {
|
|
118
|
+
await writeCredentials(projectRoot, {
|
|
119
|
+
applicationKey: ephemeral.applicationKey,
|
|
120
|
+
principalId: "pr_ephemeral",
|
|
121
|
+
});
|
|
122
|
+
await writeLink(projectRoot, {
|
|
123
|
+
hostUrl: ephemeral.url,
|
|
124
|
+
hostId: ephemeral.hostId,
|
|
125
|
+
tenantId: ephemeral.tenant.id,
|
|
126
|
+
});
|
|
127
|
+
const envMap = loadProjectEnvironment(projectRoot);
|
|
128
|
+
await seedTenantFromProject({
|
|
129
|
+
hostUrl: ephemeral.url,
|
|
130
|
+
tenantId: ephemeral.tenant.id,
|
|
131
|
+
applicationKey: ephemeral.applicationKey,
|
|
132
|
+
projectRoot,
|
|
133
|
+
env: envMap,
|
|
134
|
+
});
|
|
135
|
+
return await spawnWatcher({
|
|
136
|
+
projectRoot,
|
|
137
|
+
entry: preflight.entry,
|
|
138
|
+
tsx: preflight.tsx,
|
|
139
|
+
envMap,
|
|
140
|
+
hostUrl: ephemeral.url,
|
|
141
|
+
tenantId: ephemeral.tenant.id,
|
|
142
|
+
applicationKey: ephemeral.applicationKey,
|
|
143
|
+
tenantName: ephemeral.tenant.name,
|
|
144
|
+
hostStarted: true,
|
|
145
|
+
ephemeral: true,
|
|
146
|
+
localUi: preflight.localUi,
|
|
147
|
+
openStudio: preflight.open,
|
|
148
|
+
home: normalized.home,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
await ephemeral.close();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const attached = await attachProject({
|
|
156
|
+
projectRoot,
|
|
157
|
+
...(normalized.home ? { home: normalized.home } : {}),
|
|
158
|
+
});
|
|
159
|
+
const envMap = loadProjectEnvironment(projectRoot);
|
|
160
|
+
await seedTenantFromProject({
|
|
161
|
+
hostUrl: attached.link.hostUrl,
|
|
162
|
+
tenantId: attached.link.tenantId,
|
|
163
|
+
applicationKey: attached.credentials.applicationKey,
|
|
164
|
+
projectRoot,
|
|
165
|
+
env: envMap,
|
|
166
|
+
});
|
|
167
|
+
return spawnWatcher({
|
|
168
|
+
projectRoot,
|
|
169
|
+
entry: preflight.entry,
|
|
170
|
+
tsx: preflight.tsx,
|
|
171
|
+
envMap,
|
|
172
|
+
hostUrl: attached.link.hostUrl,
|
|
173
|
+
tenantId: attached.link.tenantId,
|
|
174
|
+
applicationKey: attached.credentials.applicationKey,
|
|
175
|
+
tenantName: attached.tenantName,
|
|
176
|
+
hostStarted: attached.hostStarted,
|
|
177
|
+
ephemeral: false,
|
|
178
|
+
localUi: preflight.localUi,
|
|
179
|
+
openStudio: preflight.open,
|
|
180
|
+
home: normalized.home ?? attached.home,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
async function spawnWatcher(options) {
|
|
184
|
+
const entryPath = resolve(options.projectRoot, options.entry);
|
|
185
|
+
const childEnv = {
|
|
186
|
+
...process.env,
|
|
187
|
+
...options.envMap,
|
|
188
|
+
NYLORUN_RUNTIME_URL: options.hostUrl,
|
|
189
|
+
NYLORUN_TENANT: options.tenantId,
|
|
190
|
+
NYLORUN_SERVER_KEY: options.applicationKey,
|
|
191
|
+
};
|
|
192
|
+
let studio;
|
|
193
|
+
let studioMode;
|
|
194
|
+
if (options.localUi) {
|
|
195
|
+
studio = await startStudio({
|
|
196
|
+
runtimeUrl: options.hostUrl,
|
|
197
|
+
serverKey: options.applicationKey,
|
|
198
|
+
tenant: { id: options.tenantId, name: options.tenantName },
|
|
199
|
+
open: options.openStudio,
|
|
200
|
+
localUi: true,
|
|
201
|
+
cacheDir: resolveHome(options.home),
|
|
202
|
+
projectRoot: options.projectRoot,
|
|
203
|
+
});
|
|
204
|
+
studioMode = "local";
|
|
205
|
+
}
|
|
206
|
+
// Handle signals before the banner: a supervisor may stop us as soon as it
|
|
207
|
+
// reads it, and an unhandled SIGTERM would kill this process and orphan the
|
|
208
|
+
// detached watcher.
|
|
209
|
+
let child;
|
|
210
|
+
let stopping;
|
|
46
211
|
const stop = (signal) => {
|
|
47
212
|
if (stopping)
|
|
48
213
|
return;
|
|
49
|
-
stopping =
|
|
50
|
-
child
|
|
214
|
+
stopping = signal;
|
|
215
|
+
child?.kill(signal);
|
|
51
216
|
};
|
|
52
|
-
const interrupt = () => stop("SIGINT")
|
|
217
|
+
const interrupt = () => stop("SIGINT");
|
|
218
|
+
const terminate = () => stop("SIGTERM");
|
|
53
219
|
process.on("SIGINT", interrupt);
|
|
54
220
|
process.on("SIGTERM", terminate);
|
|
55
221
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
222
|
+
printBanner({
|
|
223
|
+
hostUrl: options.hostUrl,
|
|
224
|
+
hostStarted: options.hostStarted,
|
|
225
|
+
ephemeral: options.ephemeral,
|
|
226
|
+
tenantName: options.tenantName,
|
|
227
|
+
tenantId: options.tenantId,
|
|
228
|
+
entry: options.entry,
|
|
229
|
+
studioLaunchUrl: studio?.launchUrl,
|
|
230
|
+
studioMode,
|
|
231
|
+
});
|
|
232
|
+
if (stopping)
|
|
233
|
+
return stopping === "SIGINT" ? 130 : 143;
|
|
234
|
+
const watcher = spawn(process.execPath, [options.tsx, "watch", "--clear-screen=false", entryPath], {
|
|
235
|
+
cwd: options.projectRoot,
|
|
236
|
+
stdio: "inherit",
|
|
237
|
+
env: childEnv,
|
|
238
|
+
detached: process.platform !== "win32",
|
|
239
|
+
});
|
|
240
|
+
child = watcher;
|
|
241
|
+
return await new Promise((resolvePromise, reject) => {
|
|
242
|
+
watcher.once("error", reject);
|
|
243
|
+
watcher.once("exit", (code, signal) => resolvePromise(code ?? (signal === "SIGINT" ? 130 : 143)));
|
|
59
244
|
});
|
|
60
245
|
}
|
|
61
246
|
finally {
|
|
62
247
|
process.removeListener("SIGINT", interrupt);
|
|
63
248
|
process.removeListener("SIGTERM", terminate);
|
|
249
|
+
await studio?.close().catch(() => { });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function printBanner(options) {
|
|
253
|
+
const hostNote = options.ephemeral
|
|
254
|
+
? "(ephemeral; removed on exit)"
|
|
255
|
+
: options.hostStarted
|
|
256
|
+
? "(started; stays running)"
|
|
257
|
+
: "(already running)";
|
|
258
|
+
const short = options.tenantId.length > 12
|
|
259
|
+
? `${options.tenantId.slice(0, 12)}…`
|
|
260
|
+
: options.tenantId;
|
|
261
|
+
console.log(`Host ${options.hostUrl} ${hostNote}`);
|
|
262
|
+
console.log(`Tenant ${options.tenantName} ${short}`);
|
|
263
|
+
console.log(`Entry ${options.entry}`);
|
|
264
|
+
if (options.studioLaunchUrl) {
|
|
265
|
+
console.log(`Studio ${options.studioLaunchUrl}`);
|
|
266
|
+
if (options.studioMode === "hosted") {
|
|
267
|
+
console.log(`Safari or offline: nylorun studio --local-ui`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
console.log(`Studio: npm run studio`);
|
|
272
|
+
}
|
|
273
|
+
console.log("");
|
|
274
|
+
console.log("Ctrl-C stops this Project only.");
|
|
275
|
+
if (!options.ephemeral) {
|
|
276
|
+
console.log("nylorun runtime down stops the Host.");
|
|
64
277
|
}
|
|
65
278
|
}
|
package/dist/doctor.d.ts
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
1
|
+
import { type AgentManifest } from "@nylorun/agents";
|
|
2
|
+
export type SandboxReport = {
|
|
3
|
+
preference: string;
|
|
4
|
+
backend: string | null;
|
|
5
|
+
isolation?: string;
|
|
6
|
+
reason?: string;
|
|
7
|
+
probes: {
|
|
8
|
+
name: string;
|
|
9
|
+
isolation: string;
|
|
10
|
+
available: boolean;
|
|
11
|
+
reason?: string;
|
|
12
|
+
version?: string;
|
|
13
|
+
}[];
|
|
14
|
+
defaultImage?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* `nylorun doctor runtime`: check the two prerequisites (Node 24+ and an
|
|
18
|
+
* installed `@nylorun/runtime`) and print the fix for each one that fails.
|
|
19
|
+
* Exits 1 when a prerequisite is missing; installs nothing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function doctorRuntime(options: {
|
|
22
|
+
json: boolean;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
/** `nylorun doctor sandbox`: Tenant sandbox report via the Tenant API (F2-4). */
|
|
3
25
|
export declare function doctorSandbox(options: {
|
|
4
26
|
json: boolean;
|
|
5
27
|
}): Promise<void>;
|
|
6
28
|
/** One line for the dev banner, or undefined when no connected agent declares a sandbox. */
|
|
7
|
-
export declare function sandboxBanner(runtimeUrl: string, serverKey: string, manifests: readonly AgentManifest[]): Promise<string | undefined>;
|
|
29
|
+
export declare function sandboxBanner(runtimeUrl: string, serverKey: string, manifests: readonly AgentManifest[], tenantId: string): Promise<string | undefined>;
|
package/dist/doctor.js
CHANGED
|
@@ -1,24 +1,110 @@
|
|
|
1
1
|
import { release } from "node:os";
|
|
2
|
-
import {
|
|
2
|
+
import { PROTOCOL_HEADER, PROTOCOL_VERSION, TENANT_HEADER, createClient, resolveConnection, } from "@nylorun/agents";
|
|
3
|
+
import { CliError } from "./errors.js";
|
|
4
|
+
import { launcher, resolveHome, runtimeInstallCommand } from "./runtime/launcher.js";
|
|
5
|
+
/** Oldest Node the Runtime runs on (it needs node:sqlite). */
|
|
6
|
+
const MIN_NODE_MAJOR = 24;
|
|
3
7
|
const LABEL = {
|
|
4
8
|
microsandbox: "microsandbox VM",
|
|
5
9
|
virtual: "virtual shell",
|
|
6
10
|
};
|
|
7
|
-
|
|
11
|
+
function platformLine() {
|
|
12
|
+
const os = process.platform === "darwin"
|
|
13
|
+
? "macOS"
|
|
14
|
+
: process.platform === "win32"
|
|
15
|
+
? "Windows"
|
|
16
|
+
: process.platform;
|
|
17
|
+
return `${os} (kernel ${release()}) · ${process.arch}`;
|
|
18
|
+
}
|
|
19
|
+
async function fetchSandboxReport(options) {
|
|
20
|
+
const connection = await resolveConnection(options);
|
|
21
|
+
const client = createClient({
|
|
22
|
+
url: connection.url,
|
|
23
|
+
key: connection.key,
|
|
24
|
+
tenant: connection.tenant,
|
|
25
|
+
});
|
|
26
|
+
return client.transport.json("/v1/tenant/sandbox", "GET", undefined);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `nylorun doctor runtime`: check the two prerequisites (Node 24+ and an
|
|
30
|
+
* installed `@nylorun/runtime`) and print the fix for each one that fails.
|
|
31
|
+
* Exits 1 when a prerequisite is missing; installs nothing.
|
|
32
|
+
*/
|
|
33
|
+
export async function doctorRuntime(options) {
|
|
34
|
+
const nodeOk = Number(process.versions.node.split(".")[0]) >= MIN_NODE_MAJOR;
|
|
35
|
+
let runtime;
|
|
36
|
+
try {
|
|
37
|
+
const handle = await launcher(resolveHome());
|
|
38
|
+
runtime = { ok: true, ...handle.runtime };
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
runtime = {
|
|
42
|
+
ok: false,
|
|
43
|
+
problem: error instanceof Error ? error.message : String(error),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (options.json) {
|
|
47
|
+
console.log(JSON.stringify({
|
|
48
|
+
node: { version: process.versions.node, ok: nodeOk },
|
|
49
|
+
runtime,
|
|
50
|
+
install: runtimeInstallCommand(),
|
|
51
|
+
}, null, 2));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const rows = [
|
|
55
|
+
[
|
|
56
|
+
"node",
|
|
57
|
+
nodeOk
|
|
58
|
+
? `✓ ${process.versions.node}`
|
|
59
|
+
: `✗ ${process.versions.node}: install Node ${MIN_NODE_MAJOR} or newer`,
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
"runtime",
|
|
63
|
+
runtime.ok
|
|
64
|
+
? `✓ ${runtime.version} · ${runtime.bin}`
|
|
65
|
+
: `✗ ${runtime.problem.split("\n")[0]}\n ${runtimeInstallCommand()}`,
|
|
66
|
+
],
|
|
67
|
+
];
|
|
68
|
+
const width = Math.max(...rows.map(([key]) => key.length)) + 2;
|
|
69
|
+
for (const [key, value] of rows)
|
|
70
|
+
console.log(` ${key.padEnd(width)}${value}`);
|
|
71
|
+
}
|
|
72
|
+
if (!nodeOk || !runtime.ok)
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
}
|
|
75
|
+
/** `nylorun doctor sandbox`: Tenant sandbox report via the Tenant API (F2-4). */
|
|
8
76
|
export async function doctorSandbox(options) {
|
|
9
|
-
|
|
77
|
+
let report;
|
|
78
|
+
try {
|
|
79
|
+
report = await fetchSandboxReport();
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
throw new CliError(error instanceof Error
|
|
83
|
+
? error.message
|
|
84
|
+
: `Could not read Tenant sandbox status: ${String(error)}`, 1);
|
|
85
|
+
}
|
|
10
86
|
if (options.json) {
|
|
11
|
-
console.log(JSON.stringify({ platform:
|
|
87
|
+
console.log(JSON.stringify({ platform: platformLine(), ...report }, null, 2));
|
|
12
88
|
return;
|
|
13
89
|
}
|
|
14
|
-
const rows = [["platform",
|
|
90
|
+
const rows = [["platform", platformLine()]];
|
|
15
91
|
for (const probe of report.probes)
|
|
16
92
|
rows.push([
|
|
17
93
|
probe.name,
|
|
18
94
|
`${probe.available ? "✓" : "✗"} ${probe.reason ?? ""}${probe.version ? ` · ${probe.version}` : ""}`,
|
|
19
95
|
]);
|
|
20
|
-
rows.push([
|
|
21
|
-
|
|
96
|
+
rows.push([
|
|
97
|
+
"preference",
|
|
98
|
+
report.preference === "auto"
|
|
99
|
+
? "auto (seed Tenant sandbox.backend via nylorun dev / .env NYLORUN_SANDBOX)"
|
|
100
|
+
: report.preference,
|
|
101
|
+
]);
|
|
102
|
+
rows.push([
|
|
103
|
+
"selected",
|
|
104
|
+
report.backend
|
|
105
|
+
? `${report.backend} (${report.isolation} isolation)`
|
|
106
|
+
: `none: ${report.reason}`,
|
|
107
|
+
]);
|
|
22
108
|
const width = Math.max(...rows.map(([key]) => key.length)) + 2;
|
|
23
109
|
for (const [key, value] of rows)
|
|
24
110
|
console.log(` ${key.padEnd(width)}${value}`);
|
|
@@ -27,7 +113,7 @@ export async function doctorSandbox(options) {
|
|
|
27
113
|
" For hardware isolation use macOS on Apple Silicon or Linux with KVM.");
|
|
28
114
|
}
|
|
29
115
|
/** One line for the dev banner, or undefined when no connected agent declares a sandbox. */
|
|
30
|
-
export async function sandboxBanner(runtimeUrl, serverKey, manifests) {
|
|
116
|
+
export async function sandboxBanner(runtimeUrl, serverKey, manifests, tenantId) {
|
|
31
117
|
const capability = manifests
|
|
32
118
|
.flatMap((manifest) => manifest.capabilities)
|
|
33
119
|
.find((item) => item.sandbox);
|
|
@@ -35,14 +121,20 @@ export async function sandboxBanner(runtimeUrl, serverKey, manifests) {
|
|
|
35
121
|
return undefined;
|
|
36
122
|
let report;
|
|
37
123
|
try {
|
|
38
|
-
const response = await fetch(`${runtimeUrl}/v1/
|
|
39
|
-
headers: {
|
|
124
|
+
const response = await fetch(`${runtimeUrl}/v1/tenant/sandbox`, {
|
|
125
|
+
headers: {
|
|
126
|
+
authorization: `Bearer ${serverKey}`,
|
|
127
|
+
[TENANT_HEADER]: tenantId,
|
|
128
|
+
[PROTOCOL_HEADER]: String(PROTOCOL_VERSION),
|
|
129
|
+
},
|
|
40
130
|
signal: AbortSignal.timeout(10_000),
|
|
41
131
|
});
|
|
42
132
|
if (response.ok)
|
|
43
|
-
report = await response.json();
|
|
133
|
+
report = (await response.json());
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
/* ignore */
|
|
44
137
|
}
|
|
45
|
-
catch { }
|
|
46
138
|
if (!report)
|
|
47
139
|
return undefined;
|
|
48
140
|
const doctor = "run `npx nylorun doctor sandbox` for options";
|
|
@@ -55,7 +147,3 @@ export async function sandboxBanner(runtimeUrl, serverKey, manifests) {
|
|
|
55
147
|
? `sandbox: ${LABEL[report.backend] ?? report.backend} (${report.reason}) · ${doctor}`
|
|
56
148
|
: `sandbox: ${LABEL[report.backend] ?? report.backend}${report.backend === "microsandbox" && image ? ` · image ${image}` : ""} · network: ${network}`;
|
|
57
149
|
}
|
|
58
|
-
function platform() {
|
|
59
|
-
const os = process.platform === "darwin" ? "macOS" : process.platform === "win32" ? "Windows" : process.platform;
|
|
60
|
-
return `${os} (kernel ${release()}) · ${process.arch}`;
|
|
61
|
-
}
|
package/dist/environment.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Read the Project `.env` into a value map. Does not mutate `process.env` (F3).
|
|
3
|
+
*/
|
|
4
|
+
export declare function loadProjectEnvironment(root?: string): Record<string, string>;
|
|
2
5
|
export declare function saveEnvironment(root: string, updates: Record<string, string | undefined>, signal?: AbortSignal): Promise<void>;
|
package/dist/environment.js
CHANGED
|
@@ -2,7 +2,10 @@ import { readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { writeFile, rename, rm } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import {
|
|
5
|
+
import { parseEnv } from "node:util";
|
|
6
|
+
/**
|
|
7
|
+
* Read the Project `.env` into a value map. Does not mutate `process.env` (F3).
|
|
8
|
+
*/
|
|
6
9
|
export function loadProjectEnvironment(root = process.cwd()) {
|
|
7
10
|
const file = join(root, ".env");
|
|
8
11
|
try {
|
|
@@ -11,10 +14,16 @@ export function loadProjectEnvironment(root = process.cwd()) {
|
|
|
11
14
|
}
|
|
12
15
|
catch (error) {
|
|
13
16
|
if (error.code === "ENOENT")
|
|
14
|
-
return;
|
|
17
|
+
return {};
|
|
15
18
|
throw error;
|
|
16
19
|
}
|
|
17
|
-
|
|
20
|
+
const parsed = parseEnv(readFileSync(file, "utf8"));
|
|
21
|
+
const result = {};
|
|
22
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
23
|
+
if (value !== undefined)
|
|
24
|
+
result[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
18
27
|
}
|
|
19
28
|
// Match complete dotenv assignments, including quoted multiline values.
|
|
20
29
|
const assignment = /^(?:export\s+)?([\w]+)[\t ]*=[\t ]*(?:"[^"]*"|'[^']*'|`[^`]*`|[^#\r\n]*)([^\r\n]*)(?:\r?\n|$)/gm;
|
package/dist/launcher.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `nylorun serve` was removed (D§12 / F2-5). Use `nylorun dev` in development
|
|
3
|
+
* and `node dist/src/main.js` (with the three environment variables) in production.
|
|
4
|
+
*/
|
|
5
|
+
export declare function serve(): Promise<never>;
|
package/dist/launcher.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `nylorun serve` was removed (D§12 / F2-5). Use `nylorun dev` in development
|
|
3
|
+
* and `node dist/src/main.js` (with the three environment variables) in production.
|
|
4
|
+
*/
|
|
5
|
+
export async function serve() {
|
|
6
|
+
throw new Error("nylorun serve was removed. Use nylorun dev [entry] in development, or node dist/src/main.js with NYLORUN_RUNTIME_URL, NYLORUN_TENANT and NYLORUN_SERVER_KEY.");
|
|
4
7
|
}
|
|
@@ -11,9 +11,39 @@ export type PromptedModel = {
|
|
|
11
11
|
baseUrl?: string;
|
|
12
12
|
auth: Credential;
|
|
13
13
|
};
|
|
14
|
+
export type ModelCatalog = {
|
|
15
|
+
providers: {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
models: {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
}[];
|
|
22
|
+
auth?: {
|
|
23
|
+
apiKey?: unknown;
|
|
24
|
+
oauth?: unknown;
|
|
25
|
+
};
|
|
26
|
+
}[];
|
|
27
|
+
};
|
|
28
|
+
export type Selection = Readonly<{
|
|
29
|
+
provider: string;
|
|
30
|
+
model: string;
|
|
31
|
+
custom?: Readonly<{
|
|
32
|
+
baseUrl: string;
|
|
33
|
+
}>;
|
|
34
|
+
}>;
|
|
35
|
+
/** Fetch GET /v1/tenant/models when a connection is available. */
|
|
36
|
+
export declare function fetchModelCatalog(options: {
|
|
37
|
+
url: string;
|
|
38
|
+
key: string;
|
|
39
|
+
tenantId: string;
|
|
40
|
+
fetchImpl?: typeof fetch;
|
|
41
|
+
}): Promise<ModelCatalog>;
|
|
14
42
|
export declare function configureProvider(options?: {
|
|
15
43
|
signal?: AbortSignal;
|
|
16
44
|
root?: string;
|
|
17
45
|
input?: Readable;
|
|
18
46
|
output?: Writable;
|
|
47
|
+
/** Tenant API catalog from GET /v1/tenant/models. */
|
|
48
|
+
catalog?: ModelCatalog;
|
|
19
49
|
}): Promise<PromptedModel>;
|