@nylorun/cli 0.3.0-beta → 0.4.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 +50 -1
- package/README.md +55 -78
- package/dist/{runtime/baseline.js → baseline.js} +0 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +34 -115
- package/dist/doctor.d.ts +1 -12
- package/dist/doctor.js +6 -96
- package/dist/home.d.ts +6 -0
- package/dist/home.js +15 -0
- package/dist/model/host-model.js +1 -1
- package/dist/project/create-tenant.d.ts +3 -1
- package/dist/project/create-tenant.js +3 -2
- package/dist/project/credentials.js +1 -1
- package/dist/project/env.d.ts +3 -0
- package/dist/project/env.js +21 -0
- package/dist/project/link.js +1 -1
- package/dist/project/root.js +2 -1
- package/dist/project/seed.d.ts +6 -0
- package/dist/project/seed.js +9 -7
- package/dist/tenant/commands.js +73 -14
- package/package.json +6 -9
- package/dist/dev-entry.d.ts +0 -5
- package/dist/dev-entry.js +0 -7
- package/dist/dev.d.ts +0 -47
- package/dist/dev.js +0 -278
- package/dist/launcher.d.ts +0 -5
- package/dist/launcher.js +0 -7
- package/dist/project/attach.d.ts +0 -32
- package/dist/project/attach.js +0 -241
- package/dist/runtime/commands.d.ts +0 -16
- package/dist/runtime/commands.js +0 -292
- package/dist/runtime/ephemeral.d.ts +0 -37
- package/dist/runtime/ephemeral.js +0 -84
- package/dist/runtime/exit-codes.d.ts +0 -9
- package/dist/runtime/exit-codes.js +0 -43
- package/dist/runtime/index.d.ts +0 -8
- package/dist/runtime/index.js +0 -6
- package/dist/runtime/launcher.d.ts +0 -81
- package/dist/runtime/launcher.js +0 -243
- package/dist/runtime/progress.d.ts +0 -10
- package/dist/runtime/progress.js +0 -8
- package/dist/runtime/version.d.ts +0 -6
- package/dist/runtime/version.js +0 -27
- /package/dist/{runtime/baseline.d.ts → baseline.d.ts} +0 -0
package/dist/dev.js
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
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"];
|
|
11
|
-
/**
|
|
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.
|
|
14
|
-
*/
|
|
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
|
-
}
|
|
39
|
-
const require = createRequire(join(process.cwd(), "package.json"));
|
|
40
|
-
let tsx;
|
|
41
|
-
try {
|
|
42
|
-
tsx = require.resolve("tsx/cli");
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
throw new Error("Install tsx to use nylorun dev");
|
|
46
|
-
}
|
|
47
|
-
if (localUi) {
|
|
48
|
-
try {
|
|
49
|
-
require.resolve("@nylorun/studio");
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
throw new Error("Install @nylorun/studio to use the Studio dashboard.");
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return {
|
|
56
|
-
tsx,
|
|
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 }),
|
|
88
|
-
});
|
|
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;
|
|
211
|
-
const stop = (signal) => {
|
|
212
|
-
if (stopping)
|
|
213
|
-
return;
|
|
214
|
-
stopping = signal;
|
|
215
|
-
child?.kill(signal);
|
|
216
|
-
};
|
|
217
|
-
const interrupt = () => stop("SIGINT");
|
|
218
|
-
const terminate = () => stop("SIGTERM");
|
|
219
|
-
process.on("SIGINT", interrupt);
|
|
220
|
-
process.on("SIGTERM", terminate);
|
|
221
|
-
try {
|
|
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)));
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
finally {
|
|
247
|
-
process.removeListener("SIGINT", interrupt);
|
|
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.");
|
|
277
|
-
}
|
|
278
|
-
}
|
package/dist/launcher.d.ts
DELETED
package/dist/launcher.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
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.");
|
|
7
|
-
}
|
package/dist/project/attach.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { type ProjectCredentials } from "./credentials.js";
|
|
2
|
-
import { type ProjectLink } from "./link.js";
|
|
3
|
-
export interface UpResult {
|
|
4
|
-
url: string;
|
|
5
|
-
hostId: string;
|
|
6
|
-
pid: number;
|
|
7
|
-
version: string;
|
|
8
|
-
started: boolean;
|
|
9
|
-
}
|
|
10
|
-
export interface AttachedProject {
|
|
11
|
-
projectRoot: string;
|
|
12
|
-
link: ProjectLink;
|
|
13
|
-
credentials: ProjectCredentials;
|
|
14
|
-
host: UpResult;
|
|
15
|
-
tenantName: string;
|
|
16
|
-
hostStarted: boolean;
|
|
17
|
-
home: string;
|
|
18
|
-
}
|
|
19
|
-
export interface AttachOptions {
|
|
20
|
-
projectRoot: string;
|
|
21
|
-
home?: string;
|
|
22
|
-
/** Interactive stdin; defaults to process.stdin when TTY. */
|
|
23
|
-
input?: NodeJS.ReadableStream;
|
|
24
|
-
output?: NodeJS.WritableStream;
|
|
25
|
-
fetchImpl?: typeof fetch;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Ensure the local Runtime is up and a Project link authenticates (D§12 step 2).
|
|
29
|
-
*/
|
|
30
|
-
export declare function attachProject(options: AttachOptions): Promise<AttachedProject>;
|
|
31
|
-
/** Three `export` lines for `runtime status --env` (F2-7). */
|
|
32
|
-
export declare function printLinkedEnvExports(projectRoot?: string): Promise<void>;
|
package/dist/project/attach.js
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import { createInterface } from "node:readline/promises";
|
|
2
|
-
import { createAdmin } from "@nylorun/admin";
|
|
3
|
-
import { PROTOCOL_HEADER, PROTOCOL_VERSION, TENANT_HEADER, } from "@nylorun/agents";
|
|
4
|
-
import { CliError } from "../errors.js";
|
|
5
|
-
import { launcher, resolveHome, throwOnLauncherFailure, } from "../runtime/launcher.js";
|
|
6
|
-
import { readCredentials, removeCredentials, } from "./credentials.js";
|
|
7
|
-
import { createProjectTenant } from "./create-tenant.js";
|
|
8
|
-
import { readLink, removeLink, writeLink, } from "./link.js";
|
|
9
|
-
function tenantHeaders(key, tenantId) {
|
|
10
|
-
return {
|
|
11
|
-
authorization: `Bearer ${key}`,
|
|
12
|
-
[TENANT_HEADER]: tenantId,
|
|
13
|
-
[PROTOCOL_HEADER]: String(PROTOCOL_VERSION),
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
async function probeTenant(hostUrl, tenantId, applicationKey, fetchImpl) {
|
|
17
|
-
try {
|
|
18
|
-
const response = await fetchImpl(`${hostUrl}/v1/tenant`, {
|
|
19
|
-
headers: tenantHeaders(applicationKey, tenantId),
|
|
20
|
-
signal: AbortSignal.timeout(5_000),
|
|
21
|
-
});
|
|
22
|
-
return response.ok ? "ok" : "rejected";
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
return "rejected";
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
async function healthHostId(hostUrl, fetchImpl) {
|
|
29
|
-
try {
|
|
30
|
-
const response = await fetchImpl(`${hostUrl}/health`, {
|
|
31
|
-
signal: AbortSignal.timeout(3_000),
|
|
32
|
-
});
|
|
33
|
-
if (!response.ok)
|
|
34
|
-
return undefined;
|
|
35
|
-
const body = (await response.json());
|
|
36
|
-
return typeof body.hostId === "string" ? body.hostId : undefined;
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Ensure the local Runtime is up and a Project link authenticates (D§12 step 2).
|
|
44
|
-
*/
|
|
45
|
-
export async function attachProject(options) {
|
|
46
|
-
const fetchImpl = options.fetchImpl ?? fetch;
|
|
47
|
-
const projectRoot = options.projectRoot;
|
|
48
|
-
const home = resolveHome(options.home);
|
|
49
|
-
const handle = await launcher(home);
|
|
50
|
-
const upOutcome = await handle.invoke(["up"]);
|
|
51
|
-
throwOnLauncherFailure(upOutcome);
|
|
52
|
-
const host = upResultFromInvoke(upOutcome);
|
|
53
|
-
const hostUrl = host.url.replace(/\/$/, "");
|
|
54
|
-
const hostId = host.hostId ||
|
|
55
|
-
(await healthHostId(hostUrl, fetchImpl)) ||
|
|
56
|
-
"";
|
|
57
|
-
if (!hostId) {
|
|
58
|
-
throw new CliError(`Runtime Host at ${hostUrl} did not report a hostId. Restart with "nylorun runtime restart".`, 1);
|
|
59
|
-
}
|
|
60
|
-
const admin = createAdmin({ home });
|
|
61
|
-
let link = await readLink(projectRoot);
|
|
62
|
-
let credentials = await readCredentials(projectRoot);
|
|
63
|
-
if (!link || !credentials) {
|
|
64
|
-
const created = await createProjectTenant({
|
|
65
|
-
admin,
|
|
66
|
-
hostId,
|
|
67
|
-
hostUrl,
|
|
68
|
-
projectRoot,
|
|
69
|
-
});
|
|
70
|
-
return {
|
|
71
|
-
projectRoot,
|
|
72
|
-
link: created.link,
|
|
73
|
-
credentials: created.credentials,
|
|
74
|
-
host,
|
|
75
|
-
tenantName: created.envelope.name,
|
|
76
|
-
hostStarted: host.started,
|
|
77
|
-
home,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
if (link.hostUrl.replace(/\/$/, "") !== hostUrl) {
|
|
81
|
-
const liveId = await healthHostId(hostUrl, fetchImpl);
|
|
82
|
-
if (liveId && liveId === link.hostId) {
|
|
83
|
-
link = { ...link, hostUrl, format: 1 };
|
|
84
|
-
await writeLink(projectRoot, link);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const liveId = await healthHostId(hostUrl, fetchImpl);
|
|
88
|
-
if (liveId && liveId !== link.hostId) {
|
|
89
|
-
throw new CliError(`Project link points at Host ${link.hostId}, but ${hostUrl} is ${liveId}. Relink: remove .nylorun/link.json and run nylorun dev.`, 1);
|
|
90
|
-
}
|
|
91
|
-
const auth = await probeTenant(hostUrl, link.tenantId, credentials.applicationKey, fetchImpl);
|
|
92
|
-
if (auth === "ok") {
|
|
93
|
-
const tenants = await admin.listTenants();
|
|
94
|
-
const match = tenants.find((t) => t.id === link.tenantId);
|
|
95
|
-
return {
|
|
96
|
-
projectRoot,
|
|
97
|
-
link: { ...link, hostUrl, hostId: liveId ?? link.hostId, format: 1 },
|
|
98
|
-
credentials,
|
|
99
|
-
host,
|
|
100
|
-
tenantName: match?.name ?? match?.envelope?.name ?? link.tenantId,
|
|
101
|
-
hostStarted: host.started,
|
|
102
|
-
home,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
const tenants = await admin.listTenants();
|
|
106
|
-
const known = tenants.some((t) => t.id === link.tenantId);
|
|
107
|
-
if (known) {
|
|
108
|
-
throw new CliError(`Application credential rejected for Tenant ${link.tenantId} on Host ${link.hostId}. Relink: remove .nylorun/link.json and .nylorun/credentials.json, then run nylorun dev.`, 1);
|
|
109
|
-
}
|
|
110
|
-
const choice = await chooseUnknownTenantRecovery({
|
|
111
|
-
tenantId: link.tenantId,
|
|
112
|
-
tenants,
|
|
113
|
-
input: options.input,
|
|
114
|
-
output: options.output,
|
|
115
|
-
});
|
|
116
|
-
if (choice === "remove") {
|
|
117
|
-
await removeLink(projectRoot);
|
|
118
|
-
await removeCredentials(projectRoot);
|
|
119
|
-
throw new CliError("Removed Project link. Run nylorun dev again to create a new Tenant.", 1);
|
|
120
|
-
}
|
|
121
|
-
if (choice === "create") {
|
|
122
|
-
await removeLink(projectRoot);
|
|
123
|
-
await removeCredentials(projectRoot);
|
|
124
|
-
const created = await createProjectTenant({
|
|
125
|
-
admin,
|
|
126
|
-
hostId: liveId ?? hostId,
|
|
127
|
-
hostUrl,
|
|
128
|
-
projectRoot,
|
|
129
|
-
});
|
|
130
|
-
return {
|
|
131
|
-
projectRoot,
|
|
132
|
-
link: created.link,
|
|
133
|
-
credentials: created.credentials,
|
|
134
|
-
host,
|
|
135
|
-
tenantName: created.envelope.name,
|
|
136
|
-
hostStarted: host.started,
|
|
137
|
-
home,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
const selected = choice.tenant;
|
|
141
|
-
await writeLink(projectRoot, {
|
|
142
|
-
format: 1,
|
|
143
|
-
hostUrl,
|
|
144
|
-
hostId: liveId ?? hostId,
|
|
145
|
-
tenantId: selected.id,
|
|
146
|
-
});
|
|
147
|
-
const retry = await probeTenant(hostUrl, selected.id, credentials.applicationKey, fetchImpl);
|
|
148
|
-
if (retry !== "ok") {
|
|
149
|
-
throw new CliError(`Credentials do not authorize Tenant ${selected.id}. Remove .nylorun/credentials.json and create a new Tenant with nylorun dev.`, 1);
|
|
150
|
-
}
|
|
151
|
-
return {
|
|
152
|
-
projectRoot,
|
|
153
|
-
link: {
|
|
154
|
-
format: 1,
|
|
155
|
-
hostUrl,
|
|
156
|
-
hostId: liveId ?? hostId,
|
|
157
|
-
tenantId: selected.id,
|
|
158
|
-
},
|
|
159
|
-
credentials,
|
|
160
|
-
host,
|
|
161
|
-
tenantName: selected.name ?? selected.envelope?.name ?? selected.id,
|
|
162
|
-
hostStarted: host.started,
|
|
163
|
-
home,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
async function chooseUnknownTenantRecovery(options) {
|
|
167
|
-
const message = `Linked Tenant ${options.tenantId} is unknown on this Host.`;
|
|
168
|
-
const tty = options.input === undefined
|
|
169
|
-
? Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
170
|
-
: true;
|
|
171
|
-
if (!tty) {
|
|
172
|
-
throw new CliError(`${message} Create a new Tenant (remove .nylorun/link.json), choose another with "nylorun tenant use", or remove the link.`, 1);
|
|
173
|
-
}
|
|
174
|
-
const input = options.input ?? process.stdin;
|
|
175
|
-
const output = options.output ?? process.stdout;
|
|
176
|
-
const rl = createInterface({ input, output });
|
|
177
|
-
try {
|
|
178
|
-
output.write(`${message}\n`);
|
|
179
|
-
output.write(" 1) Create a new Tenant\n");
|
|
180
|
-
output.write(" 2) Choose another Tenant on this Host\n");
|
|
181
|
-
output.write(" 3) Remove the Project link\n");
|
|
182
|
-
const answer = (await rl.question("Choose [1/2/3]: ")).trim();
|
|
183
|
-
if (answer === "1")
|
|
184
|
-
return "create";
|
|
185
|
-
if (answer === "3")
|
|
186
|
-
return "remove";
|
|
187
|
-
if (answer === "2") {
|
|
188
|
-
const open = options.tenants.filter((t) => t.state === "open" && t.name);
|
|
189
|
-
if (open.length === 0) {
|
|
190
|
-
throw new CliError("No open Tenants on this Host to choose. Create a new one instead.", 1);
|
|
191
|
-
}
|
|
192
|
-
for (let i = 0; i < open.length; i += 1) {
|
|
193
|
-
const t = open[i];
|
|
194
|
-
output.write(` ${i + 1}) ${t.name ?? "(unnamed)"} ${t.id}\n`);
|
|
195
|
-
}
|
|
196
|
-
const pick = Number((await rl.question(`Choose Tenant [1-${open.length}]: `)).trim());
|
|
197
|
-
if (!Number.isInteger(pick) || pick < 1 || pick > open.length) {
|
|
198
|
-
throw new CliError("Invalid Tenant choice.", 2);
|
|
199
|
-
}
|
|
200
|
-
return { tenant: open[pick - 1] };
|
|
201
|
-
}
|
|
202
|
-
throw new CliError("Invalid choice.", 2);
|
|
203
|
-
}
|
|
204
|
-
finally {
|
|
205
|
-
rl.close();
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/** Three `export` lines for `runtime status --env` (F2-7). */
|
|
209
|
-
export async function printLinkedEnvExports(projectRoot = process.cwd()) {
|
|
210
|
-
const { findProjectRoot } = await import("./root.js");
|
|
211
|
-
const root = findProjectRoot(projectRoot) ?? projectRoot;
|
|
212
|
-
const link = await readLink(root);
|
|
213
|
-
const credentials = await readCredentials(root);
|
|
214
|
-
if (!link || !credentials) {
|
|
215
|
-
console.log("# No Project link in this directory. Run nylorun dev to create one.");
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
console.log(`export NYLORUN_RUNTIME_URL=${shellQuote(link.hostUrl)}`);
|
|
219
|
-
console.log(`export NYLORUN_SERVER_KEY=${shellQuote(credentials.applicationKey)}`);
|
|
220
|
-
console.log(`export NYLORUN_TENANT=${shellQuote(link.tenantId)}`);
|
|
221
|
-
}
|
|
222
|
-
function upResultFromInvoke(outcome) {
|
|
223
|
-
const result = outcome.result ?? {};
|
|
224
|
-
const url = typeof result.url === "string" ? result.url : "";
|
|
225
|
-
const hostId = typeof result.hostId === "string" ? result.hostId : "";
|
|
226
|
-
if (!url) {
|
|
227
|
-
throw new CliError("Runtime up succeeded without a URL.", 1);
|
|
228
|
-
}
|
|
229
|
-
return {
|
|
230
|
-
url,
|
|
231
|
-
hostId,
|
|
232
|
-
pid: typeof result.pid === "number" ? result.pid : 0,
|
|
233
|
-
version: typeof result.version === "string" ? result.version : "",
|
|
234
|
-
started: Boolean(result.started),
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
function shellQuote(value) {
|
|
238
|
-
if (/^[A-Za-z0-9_./:-]+$/.test(value))
|
|
239
|
-
return value;
|
|
240
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
241
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type LauncherHandle, type LauncherOptions } from "./launcher.js";
|
|
2
|
-
export declare const runtimeUsage = " runtime up [--port <n>]\n runtime down [--wait] [--force] [--timeout <seconds>]\n runtime status [--json] [--env]\n runtime logs [-f|--follow] [-n <lines>] [--tenant <id>|--all]\n runtime restart [--port <n>] [--allow-downgrade] [--wait|--force]\n runtime run [--port <n>]\n up alias for \"runtime up\"\n down alias for \"runtime down\"\n logs alias for \"runtime logs\"";
|
|
3
|
-
export interface RuntimeCommandOptions extends LauncherOptions {
|
|
4
|
-
home?: string;
|
|
5
|
-
/** Injected launcher handle (tests). */
|
|
6
|
-
handle?: LauncherHandle;
|
|
7
|
-
/** Hook for `status --env` (F2 wires Project link exports). */
|
|
8
|
-
envHook?: () => void | Promise<void>;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* `nylorun runtime …` and the `up` / `down` / `logs` aliases.
|
|
12
|
-
* Dispatch from `cli.ts` is owned by WS-F2 (one-line import change).
|
|
13
|
-
*/
|
|
14
|
-
export declare function runtimeCommand(args: readonly string[], options?: RuntimeCommandOptions): Promise<void>;
|
|
15
|
-
export { runtimeVersion } from "./version.js";
|
|
16
|
-
export { launcher } from "./launcher.js";
|