nylorun 0.2.1-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 +126 -0
- package/README.md +156 -46
- package/dist/cli.js +13 -11
- package/dist/doctor.d.ts +3 -1
- package/dist/doctor.js +42 -13
- package/dist/project/derived-key.d.ts +8 -0
- package/dist/project/derived-key.js +18 -0
- package/dist/project/link.d.ts +32 -4
- package/dist/project/link.js +83 -14
- package/dist/project/seed.d.ts +27 -0
- package/dist/project/seed.js +111 -0
- package/dist/stack/commands.d.ts +45 -15
- package/dist/stack/commands.js +577 -125
- package/dist/stack/compose-file.d.ts +30 -8
- package/dist/stack/compose-file.js +99 -15
- package/dist/stack/env-file.d.ts +32 -4
- package/dist/stack/env-file.js +71 -3
- package/dist/stack/host-files.d.ts +16 -3
- package/dist/stack/host-files.js +29 -4
- package/dist/stack/images.d.ts +2 -0
- package/dist/stack/images.js +10 -6
- package/dist/stack/index.d.ts +3 -3
- package/dist/stack/index.js +8 -3
- package/dist/stack/paths.d.ts +11 -3
- package/dist/stack/paths.js +9 -6
- package/dist/stack/ports.d.ts +2 -0
- package/dist/stack/ports.js +2 -0
- package/dist/stack/prepare.d.ts +20 -4
- package/dist/stack/prepare.js +39 -10
- package/dist/stack/restate-identity.d.ts +1 -1
- package/dist/stack/restate-identity.js +1 -1
- package/dist/stack/stacks.d.ts +47 -0
- package/dist/stack/stacks.js +116 -0
- package/package.json +4 -4
package/dist/project/link.js
CHANGED
|
@@ -1,21 +1,90 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { chmod, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
3
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
import { ProjectLinkFileSchema } from "@nylorun/core/contracts";
|
|
5
|
+
import { CliError } from "../errors.js";
|
|
6
|
+
export function linkPath(projectRoot) {
|
|
7
|
+
return join(projectRoot, ".nylorun", "link.json");
|
|
8
|
+
}
|
|
9
|
+
export function credentialsPath(projectRoot) {
|
|
10
|
+
return join(projectRoot, ".nylorun", "credentials.json");
|
|
11
|
+
}
|
|
12
|
+
/** The Project's link, or undefined when it has none. An unreadable link is an error. */
|
|
13
|
+
export async function readProjectLink(projectRoot) {
|
|
14
|
+
let text;
|
|
15
|
+
try {
|
|
16
|
+
text = await readFile(linkPath(projectRoot), "utf8");
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
if (error.code === "ENOENT")
|
|
20
|
+
return undefined;
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
let value;
|
|
24
|
+
try {
|
|
25
|
+
value = JSON.parse(text);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
value = undefined;
|
|
29
|
+
}
|
|
30
|
+
const parsed = ProjectLinkFileSchema.safeParse(value);
|
|
31
|
+
if (!parsed.success)
|
|
32
|
+
throw new CliError(`Invalid or newer Project link at ${linkPath(projectRoot)}. Upgrade nylorun, or remove the file and run "npx nylorun start" again.`, 1);
|
|
33
|
+
const link = parsed.data;
|
|
34
|
+
return {
|
|
35
|
+
format: link.format,
|
|
36
|
+
...(link.stack ? { stack: link.stack } : {}),
|
|
37
|
+
hostUrl: link.hostUrl.replace(/\/$/, ""),
|
|
38
|
+
hostId: link.hostId,
|
|
39
|
+
...(link.tenantId ? { tenantId: link.tenantId } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async function writePrivate(path, value) {
|
|
43
|
+
const temporary = `${path}.${randomBytes(8).toString("hex")}.tmp`;
|
|
14
44
|
try {
|
|
15
|
-
|
|
16
|
-
|
|
45
|
+
await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
46
|
+
await rename(temporary, path);
|
|
47
|
+
await chmod(path, 0o600);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await rm(temporary, { force: true });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** `.nylorun/` mode 0700 with its own `.gitignore` of `*`; the Project's is never edited. */
|
|
54
|
+
async function ensureProjectDir(projectRoot) {
|
|
55
|
+
const dir = join(projectRoot, ".nylorun");
|
|
56
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
57
|
+
await chmod(dir, 0o700);
|
|
58
|
+
await writeFile(join(dir, ".gitignore"), "*\n", { mode: 0o600 });
|
|
59
|
+
}
|
|
60
|
+
export async function writeProjectLink(projectRoot, link) {
|
|
61
|
+
await ensureProjectDir(projectRoot);
|
|
62
|
+
await writePrivate(linkPath(projectRoot), {
|
|
63
|
+
format: 2,
|
|
64
|
+
stack: link.stack,
|
|
65
|
+
hostUrl: link.hostUrl.replace(/\/$/, ""),
|
|
66
|
+
hostId: link.hostId,
|
|
67
|
+
tenantId: link.tenantId,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** The Project's application key and principal, or undefined when absent or unreadable. */
|
|
71
|
+
export async function readProjectCredentials(projectRoot) {
|
|
72
|
+
try {
|
|
73
|
+
const value = JSON.parse(await readFile(credentialsPath(projectRoot), "utf8"));
|
|
74
|
+
return typeof value.applicationKey === "string" && typeof value.principalId === "string"
|
|
75
|
+
? { applicationKey: value.applicationKey, principalId: value.principalId }
|
|
76
|
+
: undefined;
|
|
17
77
|
}
|
|
18
78
|
catch {
|
|
19
79
|
return undefined;
|
|
20
80
|
}
|
|
21
81
|
}
|
|
82
|
+
/** `.nylorun/credentials.json` (format 1, mode 0600). */
|
|
83
|
+
export async function writeProjectCredentials(projectRoot, credentials) {
|
|
84
|
+
await ensureProjectDir(projectRoot);
|
|
85
|
+
await writePrivate(credentialsPath(projectRoot), {
|
|
86
|
+
format: 1,
|
|
87
|
+
applicationKey: credentials.applicationKey,
|
|
88
|
+
principalId: credentials.principalId,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
2
|
+
export interface SeedResult {
|
|
3
|
+
/** Settings the seed set, and the ones the Tenant already had. */
|
|
4
|
+
applied: string[];
|
|
5
|
+
kept: string[];
|
|
6
|
+
/** `provider/model` stored in the Tenant's vault. */
|
|
7
|
+
model?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The Project's `.env` as a value map. It is never applied to this process or written back:
|
|
11
|
+
* a Project's `.env` never configures the Host.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readProjectEnv(projectRoot: string): Record<string, string>;
|
|
14
|
+
/**
|
|
15
|
+
* Seed a new Project's Tenant from the Project's `.env`: the sandbox backend
|
|
16
|
+
* (`NYLORUN_SANDBOX=auto|virtual`, `PUT /v1/tenant/config/seed`, which keeps settings the
|
|
17
|
+
* Tenant has) and the model credential (`MODEL_PROVIDER`, `MODEL`, `MODEL_PROVIDER_API_KEY`,
|
|
18
|
+
* `MODEL_PROVIDER_BASE_URL`, or `.nylorun/auth.json`), stored in the Tenant's vault unless the
|
|
19
|
+
* Tenant has a model or `NYLORUN_DEV_MODEL=fixture`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function seedTenant(input: {
|
|
22
|
+
fetch: FetchLike;
|
|
23
|
+
runtimeUrl: string;
|
|
24
|
+
applicationKey: string;
|
|
25
|
+
projectRoot: string;
|
|
26
|
+
}): Promise<SeedResult>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { parseEnv } from "node:util";
|
|
5
|
+
import { PROTOCOL_HEADER, PROTOCOL_VERSION } from "@nylorun/core/compatibility";
|
|
6
|
+
/**
|
|
7
|
+
* The Project's `.env` as a value map. It is never applied to this process or written back:
|
|
8
|
+
* a Project's `.env` never configures the Host.
|
|
9
|
+
*/
|
|
10
|
+
export function readProjectEnv(projectRoot) {
|
|
11
|
+
const file = join(projectRoot, ".env");
|
|
12
|
+
try {
|
|
13
|
+
if (!statSync(file).isFile())
|
|
14
|
+
return {};
|
|
15
|
+
const result = {};
|
|
16
|
+
for (const [key, value] of Object.entries(parseEnv(readFileSync(file, "utf8"))))
|
|
17
|
+
if (value !== undefined)
|
|
18
|
+
result[key] = value;
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function modelFromEnv(env, projectRoot) {
|
|
26
|
+
const provider = env.MODEL_PROVIDER?.trim();
|
|
27
|
+
const model = env.MODEL?.trim();
|
|
28
|
+
if (!provider || !model)
|
|
29
|
+
return undefined;
|
|
30
|
+
const baseUrl = env.MODEL_PROVIDER_BASE_URL?.trim();
|
|
31
|
+
const base = { provider, model, ...(baseUrl ? { baseUrl } : {}) };
|
|
32
|
+
const key = env.MODEL_PROVIDER_API_KEY;
|
|
33
|
+
if (key)
|
|
34
|
+
return { ...base, auth: { type: "api_key", key } };
|
|
35
|
+
for (const relative of [".nylorun/auth.json", ".env/auth.json"]) {
|
|
36
|
+
try {
|
|
37
|
+
const all = JSON.parse(readFileSync(join(projectRoot, relative), "utf8"));
|
|
38
|
+
const credential = all[provider];
|
|
39
|
+
if (credential?.type === "api_key" || credential?.type === "oauth")
|
|
40
|
+
return { ...base, auth: credential };
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
/* absent */
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
async function call(fetch, url, applicationKey, init = {}) {
|
|
49
|
+
const response = await fetch(url, {
|
|
50
|
+
...(init.method ? { method: init.method } : {}),
|
|
51
|
+
headers: {
|
|
52
|
+
authorization: `Bearer ${applicationKey}`,
|
|
53
|
+
[PROTOCOL_HEADER]: String(PROTOCOL_VERSION),
|
|
54
|
+
accept: "application/json",
|
|
55
|
+
...(init.body === undefined ? {} : { "content-type": "application/json" }),
|
|
56
|
+
},
|
|
57
|
+
...(init.body === undefined ? {} : { body: JSON.stringify(init.body) }),
|
|
58
|
+
redirect: "error",
|
|
59
|
+
signal: AbortSignal.timeout(15_000),
|
|
60
|
+
});
|
|
61
|
+
const body = (await response.json().catch(() => ({})));
|
|
62
|
+
if (!response.ok)
|
|
63
|
+
throw new Error(`${init.method ?? "GET"} ${new URL(url).pathname} returned ${response.status}${typeof body.message === "string" ? `: ${body.message}` : ""}`);
|
|
64
|
+
return body;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Seed a new Project's Tenant from the Project's `.env`: the sandbox backend
|
|
68
|
+
* (`NYLORUN_SANDBOX=auto|virtual`, `PUT /v1/tenant/config/seed`, which keeps settings the
|
|
69
|
+
* Tenant has) and the model credential (`MODEL_PROVIDER`, `MODEL`, `MODEL_PROVIDER_API_KEY`,
|
|
70
|
+
* `MODEL_PROVIDER_BASE_URL`, or `.nylorun/auth.json`), stored in the Tenant's vault unless the
|
|
71
|
+
* Tenant has a model or `NYLORUN_DEV_MODEL=fixture`.
|
|
72
|
+
*/
|
|
73
|
+
export async function seedTenant(input) {
|
|
74
|
+
const env = readProjectEnv(input.projectRoot);
|
|
75
|
+
const base = input.runtimeUrl.replace(/\/$/, "");
|
|
76
|
+
const result = { applied: [], kept: [] };
|
|
77
|
+
const sandbox = env.NYLORUN_SANDBOX?.trim();
|
|
78
|
+
if (sandbox === "auto" || sandbox === "virtual") {
|
|
79
|
+
const body = (await call(input.fetch, `${base}/v1/tenant/config/seed`, input.applicationKey, {
|
|
80
|
+
method: "PUT",
|
|
81
|
+
body: { requestId: randomUUID(), sandbox: { backend: sandbox } },
|
|
82
|
+
}));
|
|
83
|
+
if (Array.isArray(body.applied))
|
|
84
|
+
result.applied = body.applied.map(String);
|
|
85
|
+
if (Array.isArray(body.kept))
|
|
86
|
+
result.kept = body.kept.map(String);
|
|
87
|
+
}
|
|
88
|
+
if (env.NYLORUN_DEV_MODEL?.trim() === "fixture")
|
|
89
|
+
return result;
|
|
90
|
+
const seeded = modelFromEnv(env, input.projectRoot);
|
|
91
|
+
if (!seeded)
|
|
92
|
+
return result;
|
|
93
|
+
const current = (await call(input.fetch, `${base}/v1/tenant/model`, input.applicationKey));
|
|
94
|
+
if (current.configured === true) {
|
|
95
|
+
result.kept.push("model");
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
await call(input.fetch, `${base}/v1/tenant/model`, input.applicationKey, {
|
|
99
|
+
method: "PUT",
|
|
100
|
+
body: {
|
|
101
|
+
requestId: randomUUID(),
|
|
102
|
+
idempotencyKey: randomUUID(),
|
|
103
|
+
provider: seeded.provider,
|
|
104
|
+
model: seeded.model,
|
|
105
|
+
...(seeded.baseUrl ? { baseUrl: seeded.baseUrl } : {}),
|
|
106
|
+
auth: seeded.auth,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
result.model = `${seeded.provider}/${seeded.model}`;
|
|
110
|
+
return result;
|
|
111
|
+
}
|
package/dist/stack/commands.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import type { HostTenant } from "@nylorun/core/contracts";
|
|
1
2
|
import { type ComposeService, type DockerRunner } from "./docker.js";
|
|
2
3
|
import type { PortProbe } from "./ports.js";
|
|
3
4
|
import { type FetchLike } from "./studio-login.js";
|
|
4
|
-
export declare const STACK_SERVICES: readonly ["postgres", "restate", "s2", "runtime", "studio"];
|
|
5
|
+
export declare const STACK_SERVICES: readonly ["postgres", "restate", "s2", "gateway", "runtime", "studio"];
|
|
5
6
|
export declare const stackUsage: string;
|
|
6
7
|
export interface StackDeps {
|
|
7
|
-
/** Environment snapshot (NYLORUN_HOME, image overrides, NYLORUN_STACK_PROJECT). */
|
|
8
|
+
/** Environment snapshot (NYLORUN_HOME, NYLORUN_STACK, image overrides, NYLORUN_STACK_PROJECT). */
|
|
8
9
|
env: Readonly<Record<string, string | undefined>>;
|
|
9
10
|
docker: DockerRunner;
|
|
10
11
|
fetch: FetchLike;
|
|
@@ -23,56 +24,84 @@ export interface StackDeps {
|
|
|
23
24
|
openBrowser(url: string): Promise<boolean>;
|
|
24
25
|
/** Is this process id alive? (launcher-managed Runtime detection) */
|
|
25
26
|
pidAlive(pid: number): boolean;
|
|
27
|
+
/** Where the project is looked for (default: the process's working directory). */
|
|
28
|
+
cwd?: string;
|
|
29
|
+
/** `~/.nylorun`: the stacks and the legacy stack (tests use a temporary directory). */
|
|
30
|
+
nylorunRoot?: string;
|
|
26
31
|
/** Delay between health polls; tests shorten it. */
|
|
27
32
|
pollMs?: number;
|
|
28
|
-
/** How long `start` waits for the Runtime's /health after Compose. */
|
|
33
|
+
/** How long `start` waits for the Runtime's /health, and then its Tenant, after Compose. */
|
|
29
34
|
healthTimeoutMs?: number;
|
|
30
35
|
/** How long a Studio login is retried before warning. */
|
|
31
36
|
loginTimeoutMs?: number;
|
|
32
37
|
}
|
|
33
|
-
|
|
38
|
+
/** The Compose project of stack `name`: `nylorun-<name>`, or `NYLORUN_STACK_PROJECT`. */
|
|
39
|
+
export declare function stackProject(env: StackDeps["env"], name: string): string;
|
|
34
40
|
interface Flags {
|
|
35
41
|
rest: string[];
|
|
36
42
|
booleans: Set<string>;
|
|
37
43
|
values: Map<string, string>;
|
|
44
|
+
/** Options that may repeat, in order. */
|
|
45
|
+
lists: Map<string, string[]>;
|
|
38
46
|
}
|
|
39
47
|
export declare function parseStackFlags(args: readonly string[], allowed: {
|
|
40
48
|
booleans?: readonly string[];
|
|
41
49
|
values?: readonly string[];
|
|
50
|
+
lists?: readonly string[];
|
|
42
51
|
aliases?: Record<string, string>;
|
|
43
52
|
}, usage: string): Flags;
|
|
53
|
+
/** The Host's one Tenant as `/v1/admin/status` reports it (no envelope). */
|
|
54
|
+
export type StackTenant = Pick<HostTenant, "id" | "name" | "state" | "cause">;
|
|
44
55
|
/** Printed by `start` when it does not open Studio itself. */
|
|
45
56
|
export declare const STUDIO_SIGN_IN_HINT = "To sign a browser in to Studio, run \"npx nylorun studio\".";
|
|
46
|
-
/** Printed by `start` while the Host has no Tenant: nylorun never creates one. */
|
|
47
|
-
export declare const TENANT_HINT = "No Tenant yet. Create one in Studio, or run `npx @nylorun/cli tenant create` in your project.";
|
|
48
57
|
export interface StackStatus {
|
|
58
|
+
/** The stack's name. */
|
|
59
|
+
name: string;
|
|
60
|
+
/** The Compose project. */
|
|
49
61
|
project: string;
|
|
62
|
+
/** The Host root. */
|
|
50
63
|
home: string;
|
|
51
64
|
state: "running" | "stopped" | "absent";
|
|
52
65
|
runtime: {
|
|
53
66
|
url?: string;
|
|
67
|
+
/** The Admin API (operator listener), when the stack publishes one. */
|
|
68
|
+
adminUrl?: string;
|
|
54
69
|
healthy: boolean;
|
|
55
70
|
version?: string;
|
|
56
71
|
hostId?: string;
|
|
57
|
-
tenants?: number;
|
|
58
72
|
};
|
|
73
|
+
/** The stack's one Tenant, while the Runtime answers. */
|
|
74
|
+
tenant?: StackTenant;
|
|
59
75
|
studio: {
|
|
60
76
|
url?: string;
|
|
61
77
|
state: string;
|
|
78
|
+
/** Exact origins that may show Studio in a frame (Studio §8.9). */
|
|
79
|
+
embedOrigins?: string[];
|
|
62
80
|
};
|
|
63
81
|
restate: {
|
|
64
82
|
url?: string;
|
|
65
83
|
};
|
|
84
|
+
/** The gateway container (the Model Gate), in the combined packing. */
|
|
85
|
+
gateway: {
|
|
86
|
+
state: string;
|
|
87
|
+
healthy: boolean;
|
|
88
|
+
};
|
|
66
89
|
services: ComposeService[];
|
|
67
90
|
}
|
|
68
91
|
/** What `nylorun status` reports, as data (`nylorun doctor`). */
|
|
69
|
-
export declare function readStackStatus(deps: StackDeps
|
|
92
|
+
export declare function readStackStatus(deps: StackDeps, options?: {
|
|
93
|
+
name?: string;
|
|
94
|
+
}): Promise<StackStatus>;
|
|
70
95
|
/** The running stack as clients reach it. */
|
|
71
96
|
export interface StackEndpoints {
|
|
72
|
-
/** The
|
|
97
|
+
/** The stack's name. */
|
|
98
|
+
name: string;
|
|
99
|
+
/** The Host root. */
|
|
73
100
|
home: string;
|
|
74
101
|
/** `http://localhost:<port>` */
|
|
75
102
|
runtimeUrl: string;
|
|
103
|
+
/** The operator listener (Admin API). */
|
|
104
|
+
adminUrl: string;
|
|
76
105
|
hostId: string;
|
|
77
106
|
adminKey: string;
|
|
78
107
|
studioPort: number;
|
|
@@ -82,27 +111,28 @@ export interface StackEndpoints {
|
|
|
82
111
|
started: boolean;
|
|
83
112
|
}
|
|
84
113
|
/**
|
|
85
|
-
* Start the stack unless it is already running (`nylorun studio`): the
|
|
86
|
-
* `start` code path without its
|
|
87
|
-
* still streams, since the first run pulls images.
|
|
114
|
+
* Start the selected stack unless it is already running (`nylorun studio`): the
|
|
115
|
+
* `start` code path without its output, its Tenant wait or the Project link. Compose
|
|
116
|
+
* progress still streams, since the first run pulls images.
|
|
88
117
|
*/
|
|
89
118
|
export declare function ensureStack(deps: StackDeps, options: {
|
|
90
119
|
studio: boolean;
|
|
120
|
+
name?: string;
|
|
91
121
|
}): Promise<StackEndpoints>;
|
|
92
122
|
/** Add Studio's `next` path (e.g. `/tenants/<id>`) to a login URL. */
|
|
93
123
|
export declare function withNext(loginUrl: string, next: string | undefined): string;
|
|
94
|
-
/** The Studio page
|
|
124
|
+
/** The Studio page of the stack's Tenant, as a login `next` path. */
|
|
95
125
|
export declare function tenantStudioPath(tenantId: string): string;
|
|
96
126
|
/**
|
|
97
127
|
* Mint a fresh Studio login URL on a running stack, landing on `next`.
|
|
98
128
|
* Undefined (after a warning) when Studio does not answer.
|
|
99
129
|
*/
|
|
100
130
|
export declare function studioLoginUrl(deps: StackDeps, stack: Pick<StackEndpoints, "studioPort" | "adminKey">, next?: string): Promise<string | undefined>;
|
|
101
|
-
/** `nylorun studio`, landing on `next`
|
|
131
|
+
/** `nylorun studio`, landing on `next` when given, else on the stack's Tenant. */
|
|
102
132
|
export declare function runStudioCommand(args: readonly string[], deps: StackDeps, options?: {
|
|
103
133
|
next?: string;
|
|
104
134
|
}): Promise<number>;
|
|
105
135
|
export declare function isStackCommand(name: string | undefined): boolean;
|
|
106
|
-
/** Run one stack command (`up`/`start`, `down`/`stop`, `status`, `logs`, `reset`, `studio`). */
|
|
136
|
+
/** Run one stack command (`up`/`start`, `down`/`stop`, `status`, `logs`, `reset`, `studio`, `ls`, `delete`, `legacy`). */
|
|
107
137
|
export declare function runStackCommand(name: string, args: readonly string[], deps: StackDeps): Promise<number>;
|
|
108
138
|
export {};
|