@ovrdev/cli 0.1.0-alpha.16
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/README.md +111 -0
- package/dist/cli/index.js +6153 -0
- package/dist/core/config.d.ts +138 -0
- package/dist/core/core.d.ts +748 -0
- package/dist/core/core.js +58 -0
- package/dist/engine/localbin.d.ts +2 -0
- package/dist/env/dotenv-plugin.d.ts +6 -0
- package/dist/index-34kn28k6.js +358 -0
- package/dist/index-3kvnzs8b.js +340 -0
- package/dist/index-4m3gj5te.js +638 -0
- package/dist/index-83n937pv.js +174 -0
- package/dist/index-85xjm9cd.js +349 -0
- package/dist/index-9wb3pkgv.js +384 -0
- package/dist/index-as2kwj3d.js +174 -0
- package/dist/index-bj59btcy.js +181 -0
- package/dist/index-dfy2jqk5.js +179 -0
- package/dist/index-grafpw62.js +340 -0
- package/dist/index-gxamezdw.js +341 -0
- package/dist/index-kdse4p7d.js +387 -0
- package/dist/index-mcgazrah.js +439 -0
- package/dist/index-pchhjv88.js +637 -0
- package/dist/index-rh33sm1y.js +370 -0
- package/dist/index-sqkwnp9b.js +618 -0
- package/dist/index-w64536he.js +431 -0
- package/dist/index-x4hj4et6.js +393 -0
- package/dist/plugins/shell.d.ts +57 -0
- package/dist/plugins/shell.js +8 -0
- package/dist/plugins/supabase.d.ts +79 -0
- package/dist/plugins/supabase.js +356 -0
- package/dist/util/dotenv.d.ts +2 -0
- package/dist/util/random.d.ts +11 -0
- package/dist/util/ready.d.ts +18 -0
- package/package.json +45 -0
- package/scripts/postinstall.mjs +59 -0
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
export { waitHttp, waitTcp, waitUntil, type WaitOptions } from "../util/ready.js";
|
|
2
|
+
export { CONFIG_DIR, CONFIG_PATH } from "./config.js";
|
|
3
|
+
export { randomString, randomPassword, randomHex } from "../util/random.js";
|
|
4
|
+
export { parseDotenv } from "../util/dotenv.js";
|
|
5
|
+
export { dotenv, type DotenvOptions } from "../env/dotenv-plugin.js";
|
|
6
|
+
declare const REF: unique symbol;
|
|
7
|
+
export type RefTarget = {
|
|
8
|
+
repo: string;
|
|
9
|
+
service: string;
|
|
10
|
+
key: string;
|
|
11
|
+
};
|
|
12
|
+
/** Any JSON-serializable value — what an export (and therefore a resolved ref) may carry. */
|
|
13
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
14
|
+
[key: string]: JsonValue;
|
|
15
|
+
};
|
|
16
|
+
declare const REF_T: unique symbol;
|
|
17
|
+
export type Ref<T = JsonValue> = {
|
|
18
|
+
readonly [REF]: RefTarget;
|
|
19
|
+
readonly [REF_T]?: T;
|
|
20
|
+
};
|
|
21
|
+
export declare const isRef: (v: unknown) => v is Ref;
|
|
22
|
+
export declare const refTarget: (r: Ref) => RefTarget;
|
|
23
|
+
/** Collect every service a spec references (deduped by repo.service). */
|
|
24
|
+
export declare function collectRefTargets(spec: unknown, out?: RefTarget[]): RefTarget[];
|
|
25
|
+
/**
|
|
26
|
+
* Deep-clone a spec, replacing each Ref via `resolve` (which may yield any JSON value). A Ref
|
|
27
|
+
* that resolves to `undefined` drops its object key entirely (so the env var is left unset →
|
|
28
|
+
* Infisical wins). `null` is a real resolved value and is kept.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveRefs(spec: unknown, resolve: (t: RefTarget) => JsonValue | undefined): unknown;
|
|
31
|
+
/**
|
|
32
|
+
* Coerce resolved values into process-env strings (env vars are strings): primitives → `String`,
|
|
33
|
+
* arrays/objects → `JSON.stringify`. `undefined`/`null` drop the key (the env var stays unset).
|
|
34
|
+
*/
|
|
35
|
+
export declare function coerceEnv(obj: Record<string, unknown>): Record<string, string>;
|
|
36
|
+
export type EnvMap = Record<string, string | Ref>;
|
|
37
|
+
/** A process-env map — self-contained (no @types/node) so configs type-check without it. */
|
|
38
|
+
export type EnvRecord = Record<string, string | undefined>;
|
|
39
|
+
/** Minimal readable-stream shape; Node's `Readable` satisfies it (avoids a hard @types/node dep). */
|
|
40
|
+
export type ReadableLike = {
|
|
41
|
+
on(event: string, listener: (...args: any[]) => void): unknown;
|
|
42
|
+
};
|
|
43
|
+
/** The value a provider publishes for others to reference. Ports are known at start time. */
|
|
44
|
+
export type ExportsFn = (ctx: {
|
|
45
|
+
host: string;
|
|
46
|
+
port: number;
|
|
47
|
+
ports: Record<string, number>;
|
|
48
|
+
}) => Record<string, string>;
|
|
49
|
+
/** A payload a consumer announces to a provider (allowlist URL, CORS flag, …) — JSON values. */
|
|
50
|
+
export type AnnouncePayload = Record<string, JsonValue>;
|
|
51
|
+
/** What `service.announce(payload)` produces: the target `repo.service` + the payload. */
|
|
52
|
+
export type Announcement = {
|
|
53
|
+
to: string;
|
|
54
|
+
payload: AnnouncePayload;
|
|
55
|
+
};
|
|
56
|
+
/** What a provider's `discovery` receives: each live announcement + who sent it (`repo.service`). */
|
|
57
|
+
export type IncomingAnnouncement = {
|
|
58
|
+
from: string;
|
|
59
|
+
payload: AnnouncePayload;
|
|
60
|
+
};
|
|
61
|
+
/** The `.announce` member on a service node — callable, and (phantom) a Ref so the index accepts it. */
|
|
62
|
+
type Announcer = Ref & ((payload: AnnouncePayload) => Announcement);
|
|
63
|
+
export type ServiceExports = Ref & {
|
|
64
|
+
readonly announce: Announcer;
|
|
65
|
+
readonly [key: string]: Ref;
|
|
66
|
+
};
|
|
67
|
+
export type ServicesProxy = {
|
|
68
|
+
[service: string]: ServiceExports;
|
|
69
|
+
};
|
|
70
|
+
export type ReposProxy = {
|
|
71
|
+
[repo: string]: {
|
|
72
|
+
services: ServicesProxy;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
export type WorkspaceContext = {
|
|
76
|
+
name: string;
|
|
77
|
+
/** This repo's own services: `workspace.services.<svc>.<key>`. */
|
|
78
|
+
services: ServicesProxy;
|
|
79
|
+
/** Cross-repo: `workspace.repos['<repo>'].services.<svc>.<key>`. */
|
|
80
|
+
repos: ReposProxy;
|
|
81
|
+
};
|
|
82
|
+
export type ConfigContext = {
|
|
83
|
+
workspace: WorkspaceContext;
|
|
84
|
+
/** Built-in kinds (no plugins in the single-arg form) — so `kind.shell` is always available. */
|
|
85
|
+
kind: {
|
|
86
|
+
shell: import("../plugins/shell.js").ShellKind<PrepareCtx>;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/** A provisioning step for a fresh worktree — a command, or a function of the repo context. */
|
|
90
|
+
export type SetupStep = string | ((ctx: {
|
|
91
|
+
workspace: string;
|
|
92
|
+
repo: string;
|
|
93
|
+
dir: string;
|
|
94
|
+
}) => string);
|
|
95
|
+
export type RepoConfig = {
|
|
96
|
+
setup?: SetupStep[];
|
|
97
|
+
services: Record<string, ServiceDef>;
|
|
98
|
+
/** Config-level plugins (from the two-arg defineConfig head), provided once per run. */
|
|
99
|
+
plugins?: OvrPlugin[];
|
|
100
|
+
/** Config-declared environment actions (the `all` tab's menu) — seeds, one-offs. */
|
|
101
|
+
actions?: ServiceAction[];
|
|
102
|
+
};
|
|
103
|
+
export type ConfigFactory = (ctx: ConfigContext) => RepoConfig;
|
|
104
|
+
/** Build the `{ workspace, kind }` context handed to a single-arg config factory (Ref-minting). */
|
|
105
|
+
export declare function makeContext(workspaceName: string, repo: string): ConfigContext;
|
|
106
|
+
/** A `workspace` whose leaves resolve to real export values — handed to `prepare` at start time. */
|
|
107
|
+
export declare function resolvingWorkspace(name: string, ownRepo: string, resolve: (t: RefTarget) => JsonValue | undefined): WorkspaceContext;
|
|
108
|
+
/** A `workspace` that records every ref its leaves are asked for (returns "") — for dry-run discovery. */
|
|
109
|
+
export declare function recordingWorkspace(name: string, ownRepo: string): {
|
|
110
|
+
workspace: WorkspaceContext;
|
|
111
|
+
taken(): RefTarget[];
|
|
112
|
+
};
|
|
113
|
+
/** What the head's `plugins` factory receives — everything known before services exist. */
|
|
114
|
+
export type HeadCtx = {
|
|
115
|
+
workspace: {
|
|
116
|
+
name: string;
|
|
117
|
+
};
|
|
118
|
+
repo: string;
|
|
119
|
+
dir: string;
|
|
120
|
+
};
|
|
121
|
+
export type ProvideCtx = {
|
|
122
|
+
/** Where the plugin is provided: env scope (`service` unset) or one service. */
|
|
123
|
+
scope: {
|
|
124
|
+
workspace: string;
|
|
125
|
+
repo: string;
|
|
126
|
+
dir: string;
|
|
127
|
+
service?: string;
|
|
128
|
+
};
|
|
129
|
+
/** Reserve a sticky port under a plugin-chosen key (e.g. a route name → stable URL). */
|
|
130
|
+
allocPort(key: string, label?: string): Promise<number>;
|
|
131
|
+
log(line: string): void;
|
|
132
|
+
};
|
|
133
|
+
export type InputOptionsCtx = {
|
|
134
|
+
exports: Record<string, string>;
|
|
135
|
+
helpers: Record<string, unknown>;
|
|
136
|
+
};
|
|
137
|
+
/** Choice options: a static list, or a resolver run when the form opens (autocomplete/dynamic). */
|
|
138
|
+
export type ChoiceOptions = readonly string[] | ((ctx: InputOptionsCtx) => Promise<readonly string[]> | readonly string[]);
|
|
139
|
+
export type ActionInput = {
|
|
140
|
+
kind: "text";
|
|
141
|
+
label: string;
|
|
142
|
+
default?: string;
|
|
143
|
+
placeholder?: string;
|
|
144
|
+
} | {
|
|
145
|
+
kind: "choice";
|
|
146
|
+
label: string;
|
|
147
|
+
options: ChoiceOptions;
|
|
148
|
+
default?: string;
|
|
149
|
+
} | {
|
|
150
|
+
kind: "confirm";
|
|
151
|
+
label: string;
|
|
152
|
+
default?: boolean;
|
|
153
|
+
};
|
|
154
|
+
/** Builders for action inputs (mirrors `ask.*`, but choice options may be dynamic). Each returns
|
|
155
|
+
* its SPECIFIC kind (not the widened union) so `action.*` can infer the value type per input. */
|
|
156
|
+
export declare const input: {
|
|
157
|
+
text: (label: string, opts?: {
|
|
158
|
+
default?: string;
|
|
159
|
+
placeholder?: string;
|
|
160
|
+
}) => {
|
|
161
|
+
kind: "text";
|
|
162
|
+
label: string;
|
|
163
|
+
default?: string;
|
|
164
|
+
placeholder?: string;
|
|
165
|
+
};
|
|
166
|
+
choice: <const T extends ChoiceOptions>(label: string, options: T, opts?: {
|
|
167
|
+
default?: string;
|
|
168
|
+
}) => {
|
|
169
|
+
kind: "choice";
|
|
170
|
+
label: string;
|
|
171
|
+
options: T;
|
|
172
|
+
default?: string;
|
|
173
|
+
};
|
|
174
|
+
confirm: (label: string, opts?: {
|
|
175
|
+
default?: boolean;
|
|
176
|
+
}) => {
|
|
177
|
+
kind: "confirm";
|
|
178
|
+
label: string;
|
|
179
|
+
default?: boolean;
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
/** Collected input values, keyed by the `inputs` record key. */
|
|
183
|
+
export type InputValues = Record<string, string | boolean>;
|
|
184
|
+
/** The value type a single input yields (text→string, choice→its option union, confirm→boolean). */
|
|
185
|
+
type InputValue<I> = I extends {
|
|
186
|
+
kind: "text";
|
|
187
|
+
} ? string : I extends {
|
|
188
|
+
kind: "confirm";
|
|
189
|
+
} ? boolean : I extends {
|
|
190
|
+
kind: "choice";
|
|
191
|
+
options: infer T;
|
|
192
|
+
} ? T extends readonly string[] ? T[number] : string : string | boolean;
|
|
193
|
+
/** Map an `inputs` record to its typed values — what `run`/`command`/`args`/`env` receive. */
|
|
194
|
+
export type InputsValues<R extends Record<string, ActionInput>> = {
|
|
195
|
+
[K in keyof R]: InputValue<R[K]>;
|
|
196
|
+
};
|
|
197
|
+
/** Context handed to a `function` action's `run` — inputs + the service's exports + helpers. */
|
|
198
|
+
export type ActionRunCtx = {
|
|
199
|
+
inputs: InputValues;
|
|
200
|
+
exports: Record<string, string>;
|
|
201
|
+
/** Capability helpers for this scope (what a plugin's `provide` returned), keyed by name. */
|
|
202
|
+
helpers: Record<string, unknown>;
|
|
203
|
+
service: string;
|
|
204
|
+
scope: {
|
|
205
|
+
workspace: string;
|
|
206
|
+
repo: string;
|
|
207
|
+
service: string;
|
|
208
|
+
dir: string;
|
|
209
|
+
};
|
|
210
|
+
log(line: string): void;
|
|
211
|
+
};
|
|
212
|
+
/** A plugin health/config check result, surfaced in the TUI's diagnostics panel. */
|
|
213
|
+
export type Diagnostic = {
|
|
214
|
+
id: string;
|
|
215
|
+
status: "ok" | "warn" | "error";
|
|
216
|
+
message: string;
|
|
217
|
+
/** Id of one of this plugin's actions that fixes the problem (panel: enter runs it). */
|
|
218
|
+
fix?: string;
|
|
219
|
+
};
|
|
220
|
+
export type OvrPlugin<H = unknown> = {
|
|
221
|
+
name: string;
|
|
222
|
+
/** Service-kind constructors contributed to the factory's `kind.<name>` namespace. */
|
|
223
|
+
kinds?: Record<string, unknown>;
|
|
224
|
+
/** Action builders contributed to the factory's `action.<name>.*` namespace — plugin-scoped
|
|
225
|
+
* counterparts to action.session/fn. Each is a pure spec-constructor returning a ServiceAction;
|
|
226
|
+
* the plugin's provided helper reaches it at run via `ActionRunCtx.helpers`. */
|
|
227
|
+
actionKinds?: Record<string, unknown>;
|
|
228
|
+
/** Provision resources; the returned helpers appear in prepare ctx under `name`. */
|
|
229
|
+
provide?(ctx: ProvideCtx): H | Promise<H>;
|
|
230
|
+
/** Effect-free stand-in for the helpers, used while dry-running `prepare` for refs. */
|
|
231
|
+
stub?(): H;
|
|
232
|
+
/** Release provisioned resources (run end; service stop/restart when service-scoped). */
|
|
233
|
+
teardown?(helpers: H): void | Promise<void>;
|
|
234
|
+
/** Actions contributed at this plugin's scope. */
|
|
235
|
+
actions?(helpers: H): ServiceAction[];
|
|
236
|
+
/** Health/config checks — run after provide and on panel refresh; non-ok notify the user. */
|
|
237
|
+
diagnose?(helpers: H): Diagnostic[] | Promise<Diagnostic[]>;
|
|
238
|
+
/** A base-env source this plugin contributes (Infisical, dotenv, …), merged for each repo. */
|
|
239
|
+
envSource?: EnvSource;
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Return types for plugin factories. The surfaces must be REQUIRED (not optional) on the
|
|
243
|
+
* returned type — `ContextOf`/`KindsOf` match `extends { provide/kinds: … }`, and an
|
|
244
|
+
* optional property never satisfies that.
|
|
245
|
+
*/
|
|
246
|
+
export type CapabilityPlugin<N extends string, H> = OvrPlugin<H> & {
|
|
247
|
+
name: N;
|
|
248
|
+
provide(ctx: ProvideCtx): H | Promise<H>;
|
|
249
|
+
};
|
|
250
|
+
export type KindsPlugin<N extends string, K> = OvrPlugin & {
|
|
251
|
+
name: N;
|
|
252
|
+
kinds: K;
|
|
253
|
+
};
|
|
254
|
+
/** A plugin contributing action builders under `action.<N>.*`. `A` must be REQUIRED so `ActionsOf`
|
|
255
|
+
* matches `extends { actionKinds: … }`. Combine with `CapabilityPlugin` when the builders read a
|
|
256
|
+
* provided helper: `CapabilityPlugin<"pg", PgApi> & ActionsPlugin<"pg", typeof actionKinds>`. */
|
|
257
|
+
export type ActionsPlugin<N extends string, A> = OvrPlugin & {
|
|
258
|
+
name: N;
|
|
259
|
+
actionKinds: A;
|
|
260
|
+
};
|
|
261
|
+
type UnionToIntersection<U> = (U extends unknown ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
|
|
262
|
+
/** The prepare-ctx helpers a plugins tuple contributes, keyed by plugin name. */
|
|
263
|
+
export type ContextOf<P extends readonly OvrPlugin[]> = UnionToIntersection<P[number] extends infer Pl ? Pl extends {
|
|
264
|
+
name: infer N extends string;
|
|
265
|
+
provide(ctx: ProvideCtx): infer R;
|
|
266
|
+
} ? {
|
|
267
|
+
[K in N]: Awaited<R>;
|
|
268
|
+
} : never : never> extends infer C ? C extends object ? C : object : object;
|
|
269
|
+
/** What every service's `prepare` receives (capabilities merge in on top). */
|
|
270
|
+
/** Options for a sticky secret: length, whether to include symbols, or hex encoding. */
|
|
271
|
+
export type SecretOptions = {
|
|
272
|
+
length?: number;
|
|
273
|
+
symbols?: boolean;
|
|
274
|
+
hex?: boolean;
|
|
275
|
+
};
|
|
276
|
+
export type PrepareCtx = {
|
|
277
|
+
workspace: WorkspaceContext;
|
|
278
|
+
scope: {
|
|
279
|
+
workspace: string;
|
|
280
|
+
repo: string;
|
|
281
|
+
service: string;
|
|
282
|
+
dir: string;
|
|
283
|
+
};
|
|
284
|
+
/** Builtins live under namespaces so plugin names can't collide with them. */
|
|
285
|
+
ports: {
|
|
286
|
+
alloc(label?: string): Promise<number>;
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Sticky secrets — a generated password/token that STAYS STABLE across runs and restarts
|
|
290
|
+
* (persisted per workspace/service/key, like ports). Use for a value a service stores, e.g.
|
|
291
|
+
* `prepare: async ({ secrets }) => ({ pw: secrets.get("db-password") })`.
|
|
292
|
+
*/
|
|
293
|
+
secrets: {
|
|
294
|
+
get(key: string, opts?: SecretOptions): string;
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* Sticky data paths — a per-environment directory unique to this (workspace, service), created
|
|
298
|
+
* on ask and stable across runs (so a file-backed service keeps its data). Non-clashing across
|
|
299
|
+
* workspaces/forks by construction. Labels may nest: `paths.get("surrealkv/data")`.
|
|
300
|
+
*/
|
|
301
|
+
paths: {
|
|
302
|
+
get(label?: string): string;
|
|
303
|
+
};
|
|
304
|
+
/** Ephemeral per-run dir (wiped each run) — build artifacts/fixtures you do NOT want to keep. */
|
|
305
|
+
scratch: {
|
|
306
|
+
get(label?: string): string;
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* Render a `{{var}}` template into a file, return its path. `template` is inline or `{ from }`
|
|
310
|
+
* (relative to the repo); output lands in the data dir unless `out` is given. For a config file
|
|
311
|
+
* a service needs with dynamic ports/paths baked in.
|
|
312
|
+
*/
|
|
313
|
+
files: {
|
|
314
|
+
render(template: string | {
|
|
315
|
+
from: string;
|
|
316
|
+
}, vars?: Record<string, string | number>, opts?: {
|
|
317
|
+
name?: string;
|
|
318
|
+
out?: string;
|
|
319
|
+
}): string;
|
|
320
|
+
};
|
|
321
|
+
/** Run a side-effect once per environment (marker under the data dir; re-armed by `--reset-data`). */
|
|
322
|
+
once(key: string, fn: () => void | Promise<void>): Promise<void>;
|
|
323
|
+
/** Base env (env source + process env) with a fail-fast `require`. */
|
|
324
|
+
env: {
|
|
325
|
+
get(key: string, fallback?: string): string | undefined;
|
|
326
|
+
require(key: string): string;
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* Sticky cryptographic keys — a keypair generated once and persisted (like secrets), so a
|
|
330
|
+
* signing key (JWT/OIDC, SSH) stays stable across restarts. Returns PEM strings + `write()` to
|
|
331
|
+
* drop them in the data volume as files (for `--key-file`-style tools). ed25519 by default.
|
|
332
|
+
*/
|
|
333
|
+
keys: {
|
|
334
|
+
pair(name: string, opts?: {
|
|
335
|
+
type?: "ed25519" | "rsa" | "ec";
|
|
336
|
+
modulusLength?: number;
|
|
337
|
+
curve?: string;
|
|
338
|
+
}): {
|
|
339
|
+
type: "ed25519" | "rsa" | "ec";
|
|
340
|
+
privateKey: string;
|
|
341
|
+
publicKey: string;
|
|
342
|
+
/** Public key as a JWK Set, for a `/.well-known/jwks.json` verify endpoint. */
|
|
343
|
+
jwks(opts?: {
|
|
344
|
+
use?: string;
|
|
345
|
+
}): {
|
|
346
|
+
keys: Record<string, string>[];
|
|
347
|
+
};
|
|
348
|
+
/** OpenSSH form: `publicKey` is an authorized_keys line; `privateKey` stays PEM (ed25519). */
|
|
349
|
+
ssh(opts?: {
|
|
350
|
+
comment?: string;
|
|
351
|
+
}): {
|
|
352
|
+
publicKey: string;
|
|
353
|
+
privateKey: string;
|
|
354
|
+
};
|
|
355
|
+
write(opts?: {
|
|
356
|
+
name?: string;
|
|
357
|
+
}): {
|
|
358
|
+
privateKeyPath: string;
|
|
359
|
+
publicKeyPath: string;
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
};
|
|
364
|
+
/** A service is "ready" (dependents may start) when this resolves. `bag` = prepare's return. */
|
|
365
|
+
export type ReadyFn<Bag = unknown> = (bag: Bag) => Promise<void>;
|
|
366
|
+
/** Context for a provider's `discovery` reconcile — log + a self-restart (e.g. supabase reloading). */
|
|
367
|
+
export type DiscoveryCtx = {
|
|
368
|
+
log(line: string): void;
|
|
369
|
+
restart(): void;
|
|
370
|
+
};
|
|
371
|
+
/** A provider's reconcile over the LIVE set of announcements targeting it (called on every change). */
|
|
372
|
+
export type DiscoveryFn = (announcements: IncomingAnnouncement[], ctx: DiscoveryCtx) => void | Promise<void>;
|
|
373
|
+
/** The engine-recognized envelope on every kind's spec (the rest stays plugin-opaque). */
|
|
374
|
+
export type ServiceEnvelope = {
|
|
375
|
+
prepare?: (ctx: never) => unknown;
|
|
376
|
+
plugins?: readonly OvrPlugin[];
|
|
377
|
+
/** Config-declared actions, merged into the service's TUI menu after the plugin's own. */
|
|
378
|
+
actions?: ServiceAction[];
|
|
379
|
+
/** Resolves when the service is up; gates dependents. Use waitHttp/waitTcp or any async fn. */
|
|
380
|
+
ready?: ReadyFn;
|
|
381
|
+
/** Services this one needs UP before it starts — typed readiness refs (`x.ready`). */
|
|
382
|
+
waitFor?: readonly Ref[];
|
|
383
|
+
/** Announcements this service pushes to others (`svc.announce({...})`) — a function of the bag. */
|
|
384
|
+
announce?: (bag: never) => Announcement[];
|
|
385
|
+
/** Reconcile the live set of announcements targeting this service (allowlist/CORS/redirect). */
|
|
386
|
+
discovery?: DiscoveryFn;
|
|
387
|
+
};
|
|
388
|
+
export type AskSpec = {
|
|
389
|
+
kind: "choice";
|
|
390
|
+
question: string;
|
|
391
|
+
options: readonly string[];
|
|
392
|
+
default?: string;
|
|
393
|
+
} | {
|
|
394
|
+
kind: "confirm";
|
|
395
|
+
question: string;
|
|
396
|
+
default?: boolean;
|
|
397
|
+
};
|
|
398
|
+
export declare const ask: {
|
|
399
|
+
/** A one-of choice (numbered prompt). */
|
|
400
|
+
choice: <const O extends readonly string[]>(question: string, options: O, opts?: {
|
|
401
|
+
default?: O[number];
|
|
402
|
+
}) => {
|
|
403
|
+
kind: "choice";
|
|
404
|
+
question: string;
|
|
405
|
+
options: O;
|
|
406
|
+
default?: O[number];
|
|
407
|
+
};
|
|
408
|
+
/** A yes/no confirmation. */
|
|
409
|
+
confirm: (question: string, opts?: {
|
|
410
|
+
default?: boolean;
|
|
411
|
+
}) => {
|
|
412
|
+
kind: "confirm";
|
|
413
|
+
question: string;
|
|
414
|
+
default?: boolean;
|
|
415
|
+
};
|
|
416
|
+
};
|
|
417
|
+
type AnswerOf<A> = A extends {
|
|
418
|
+
kind: "choice";
|
|
419
|
+
options: infer O extends readonly string[];
|
|
420
|
+
} ? O[number] : A extends {
|
|
421
|
+
kind: "confirm";
|
|
422
|
+
} ? boolean : never;
|
|
423
|
+
export type AnswersOf<A> = {
|
|
424
|
+
[K in keyof A]: AnswerOf<A[K]>;
|
|
425
|
+
};
|
|
426
|
+
export type Answers = Record<string, string | boolean>;
|
|
427
|
+
/** Fill unanswered asks from spec defaults; a missing default is a hard, actionable error. */
|
|
428
|
+
export declare function applyAskDefaults(asks: Record<string, AskSpec>, answers: Answers, repo: string): Answers;
|
|
429
|
+
/**
|
|
430
|
+
* Augmentable kind registry: a kind-contributing plugin module declares
|
|
431
|
+
* `interface KindRegistry<Ctx> { <name>: <ItsKinds<Ctx>> }` so `kind.<name>.*` is typed
|
|
432
|
+
* with the config's capability context. Presence stays sound — a plugin's kinds only
|
|
433
|
+
* appear when its factory is actually in the `plugins` tuple.
|
|
434
|
+
*/
|
|
435
|
+
export interface KindRegistry<Ctx> {
|
|
436
|
+
}
|
|
437
|
+
type KindsOf<P extends readonly OvrPlugin[], Ctx> = UnionToIntersection<P[number] extends infer Pl ? Pl extends {
|
|
438
|
+
name: infer N extends string;
|
|
439
|
+
kinds: infer K;
|
|
440
|
+
} ? N extends keyof KindRegistry<Ctx> ? Pick<KindRegistry<Ctx>, N> : {
|
|
441
|
+
[Key in N]: K;
|
|
442
|
+
} : never : never> extends infer M ? M extends object ? M : object : object;
|
|
443
|
+
/** The action builders a plugins tuple contributes, keyed by plugin name — exposed as `action.<name>.*`. */
|
|
444
|
+
type ActionsOf<P extends readonly OvrPlugin[]> = UnionToIntersection<P[number] extends infer Pl ? Pl extends {
|
|
445
|
+
name: infer N extends string;
|
|
446
|
+
actionKinds: infer A;
|
|
447
|
+
} ? {
|
|
448
|
+
[K in N]: A;
|
|
449
|
+
} : never : never> extends infer M ? M extends object ? M : object : object;
|
|
450
|
+
/** What the config factory receives in the two-arg form. */
|
|
451
|
+
export type FactoryCtx<P extends readonly OvrPlugin[]> = {
|
|
452
|
+
workspace: WorkspaceContext;
|
|
453
|
+
kind: {
|
|
454
|
+
shell: import("../plugins/shell.js").ShellKind<PrepareCtx & ContextOf<P>>;
|
|
455
|
+
} & KindsOf<P, PrepareCtx & ContextOf<P>>;
|
|
456
|
+
/** action.session/fn/handover plus each plugin's `action.<name>.*` builders. */
|
|
457
|
+
action: typeof action & ActionsOf<P>;
|
|
458
|
+
};
|
|
459
|
+
declare const FACTORY: unique symbol;
|
|
460
|
+
declare const HEAD: unique symbol;
|
|
461
|
+
/** Internal evaluated-config factory: receives full identity, returns the config + plugins. */
|
|
462
|
+
type EvalFactory = (ctx: {
|
|
463
|
+
workspace: string;
|
|
464
|
+
repo: string;
|
|
465
|
+
dir: string;
|
|
466
|
+
answers?: Answers;
|
|
467
|
+
}) => RepoConfig;
|
|
468
|
+
export type ConfigModule = {
|
|
469
|
+
[FACTORY]: EvalFactory;
|
|
470
|
+
[HEAD]?: {
|
|
471
|
+
asks: Record<string, AskSpec>;
|
|
472
|
+
};
|
|
473
|
+
};
|
|
474
|
+
/** The asks a config module declares (modes included) — the loader resolves them pre-eval. */
|
|
475
|
+
export declare const configAsks: (mod: unknown) => Record<string, AskSpec>;
|
|
476
|
+
/** The body a two-arg factory returns (plugins come from the head). */
|
|
477
|
+
export type RepoConfigBody = {
|
|
478
|
+
setup?: SetupStep[];
|
|
479
|
+
services: Record<string, ServiceDef>;
|
|
480
|
+
/** Environment-wide actions (the `all` tab's menu) — seeds, provisioning, one-offs. */
|
|
481
|
+
actions?: ServiceAction[];
|
|
482
|
+
};
|
|
483
|
+
/**
|
|
484
|
+
* Author a repo's services.
|
|
485
|
+
* - `defineConfig(factory)` / `defineConfig(config)` — the plugin-less form.
|
|
486
|
+
* - `defineConfig({ plugins }, ({ workspace, kind }) => body)` — plugins (or a factory of
|
|
487
|
+
* HeadCtx returning them) thread their capabilities into every prepare ctx and their
|
|
488
|
+
* kinds into `kind.*`, fully typed from the tuple.
|
|
489
|
+
*/
|
|
490
|
+
export declare function defineConfig(input: ConfigFactory | RepoConfig): ConfigModule;
|
|
491
|
+
export declare function defineConfig<const P extends readonly OvrPlugin[], const M extends readonly string[] = readonly string[], const A extends Record<string, AskSpec> = Record<never, AskSpec>>(head: {
|
|
492
|
+
plugins?: P | ((ctx: HeadCtx) => P);
|
|
493
|
+
/** Named run modes — sugar for an `asks.mode` choice; lands as `mode` in the factory ctx. */
|
|
494
|
+
modes?: M;
|
|
495
|
+
/** Mode used without prompting when nothing was answered yet. */
|
|
496
|
+
defaultMode?: M[number];
|
|
497
|
+
/** Interactive inputs, resolved once before the factory runs; sticky per workspace. */
|
|
498
|
+
asks?: A;
|
|
499
|
+
}, factory: (ctx: FactoryCtx<P> & {
|
|
500
|
+
mode: M[number];
|
|
501
|
+
answers: AnswersOf<A>;
|
|
502
|
+
}) => RepoConfigBody): ConfigModule;
|
|
503
|
+
export declare const isConfigModule: (v: unknown) => v is ConfigModule;
|
|
504
|
+
/** Evaluate a loaded config module (or bare factory/plain object) into a RepoConfig. */
|
|
505
|
+
export declare function evalConfig(mod: unknown, workspace: string, repo: string, dir?: string, answers?: Answers): RepoConfig;
|
|
506
|
+
/** What every plugin helper returns. `spec` is opaque to the engine (Refs aside). */
|
|
507
|
+
export type ServiceDef = {
|
|
508
|
+
plugin: Plugin;
|
|
509
|
+
spec: unknown;
|
|
510
|
+
};
|
|
511
|
+
export type Registry = {
|
|
512
|
+
register(name: string, value: string): void;
|
|
513
|
+
discover(name: string): string | undefined;
|
|
514
|
+
};
|
|
515
|
+
export type StartCtx = {
|
|
516
|
+
scope: {
|
|
517
|
+
workspace: string;
|
|
518
|
+
repo: string;
|
|
519
|
+
service: string;
|
|
520
|
+
dir: string;
|
|
521
|
+
};
|
|
522
|
+
/**
|
|
523
|
+
* Reserve a port. Sticky per service slot: the same slot gets the same port across
|
|
524
|
+
* starts AND restarts (re-discovered only if taken), so exports stay stable. Unlabeled
|
|
525
|
+
* calls slot by call ordinal; multi-port plugins should pass a label (`allocPort("db")`)
|
|
526
|
+
* so slots survive call-order changes.
|
|
527
|
+
*/
|
|
528
|
+
allocPort(label?: string): Promise<number>;
|
|
529
|
+
/**
|
|
530
|
+
* Resolve a cross-service Ref to its value (local export, or undefined when it bubbles to
|
|
531
|
+
* the env source). The engine pre-resolves refs in the static spec; plugins with
|
|
532
|
+
* function-valued fields (e.g. shell's `env`) resolve refs in the function's output here.
|
|
533
|
+
*/
|
|
534
|
+
resolve(target: RefTarget): JsonValue | undefined;
|
|
535
|
+
/**
|
|
536
|
+
* A `workspace` whose refs resolve to real sibling values — handed to `prepare` so it can
|
|
537
|
+
* read a started sibling's export (a local override; a bubbled ref reads as undefined,
|
|
538
|
+
* since that value lives in the env source, not ovr's resolver).
|
|
539
|
+
*/
|
|
540
|
+
workspace: WorkspaceContext;
|
|
541
|
+
/** The value `prepare` returned (undefined when the spec has none) — engine-run. */
|
|
542
|
+
bag: unknown;
|
|
543
|
+
/** Base env from the env source (e.g. Infisical); the plugin merges its resolved spec.env on top. */
|
|
544
|
+
env: EnvRecord;
|
|
545
|
+
/** Multiplexed, prefixed log sink for this service. */
|
|
546
|
+
log(line: string): void;
|
|
547
|
+
/** Line-split a child stream into the prefixed log sink; `map` rewrites each line. */
|
|
548
|
+
pipe(stream: ReadableLike | null, map?: (line: string) => string): void;
|
|
549
|
+
/**
|
|
550
|
+
* A named child stream of this service — its own pane/row in the TUI (nested under the
|
|
551
|
+
* service; the parent pane shows all children merged). Lifecycle stays with the parent;
|
|
552
|
+
* subs carry logs, a status dot, and optionally their own actions. Idempotent per name.
|
|
553
|
+
*/
|
|
554
|
+
sub(name: string, opts?: {
|
|
555
|
+
actions?: ServiceAction[];
|
|
556
|
+
}): SubStream;
|
|
557
|
+
registry: Registry;
|
|
558
|
+
};
|
|
559
|
+
/** What `StartCtx.sub()` hands back — a scoped sink for one child stream. */
|
|
560
|
+
export type SubStream = {
|
|
561
|
+
log(line: string): void;
|
|
562
|
+
pipe(stream: ReadableLike | null, map?: (line: string) => string): void;
|
|
563
|
+
setStatus(status: "running" | "exited" | "failed"): void;
|
|
564
|
+
};
|
|
565
|
+
/**
|
|
566
|
+
* A source of base (hosted) env — Infisical today, but pluggable (doppler, dotenv, …). The
|
|
567
|
+
* engine asks the configured source for the base env before starting services; overridden
|
|
568
|
+
* services then override specific keys via their resolved refs.
|
|
569
|
+
*/
|
|
570
|
+
export type EnvSource = {
|
|
571
|
+
name: string;
|
|
572
|
+
/**
|
|
573
|
+
* Load base env for one repo. `env` is the abstract environment NAME the workspace is running
|
|
574
|
+
* as (dev/prod/staging — sticky per workspace, set by `ovr run --env <name>`); each source
|
|
575
|
+
* translates it (infisical → a slug; dotenv → `.env.<name>`). Undefined → the source's default.
|
|
576
|
+
*/
|
|
577
|
+
load(ctx: {
|
|
578
|
+
workspace: string;
|
|
579
|
+
repo: string;
|
|
580
|
+
dir: string;
|
|
581
|
+
env?: string;
|
|
582
|
+
}): Promise<Record<string, string>> | Record<string, string>;
|
|
583
|
+
};
|
|
584
|
+
/**
|
|
585
|
+
* A plugin-contributed, per-service action (surfaced in the TUI's action menu).
|
|
586
|
+
* - session (DEFAULT): a declarative command spec — the TUI runs it in a PTY-backed
|
|
587
|
+
* session tab (full terminal emulation: attach/detach, background, concurrent).
|
|
588
|
+
* - handover: explicit opt-in for programs that must own the real terminal; `run()`
|
|
589
|
+
* blocks until done.
|
|
590
|
+
*/
|
|
591
|
+
/** Fields common to every action variant. */
|
|
592
|
+
type ActionBase = {
|
|
593
|
+
id: string;
|
|
594
|
+
label: string;
|
|
595
|
+
/** Menu grouping (e.g. plugin name, or a custom "Tasks" group). Ungrouped if omitted. */
|
|
596
|
+
category?: string;
|
|
597
|
+
/** A form the TUI collects before running; values thread into command/args/env or run. */
|
|
598
|
+
inputs?: Record<string, ActionInput>;
|
|
599
|
+
};
|
|
600
|
+
/** A field that is a value or a (pure) function of the collected inputs. */
|
|
601
|
+
type OrInputs<T> = T | ((values: InputValues) => T);
|
|
602
|
+
export type ServiceAction = (ActionBase & {
|
|
603
|
+
mode?: "session";
|
|
604
|
+
command: OrInputs<string>;
|
|
605
|
+
args?: OrInputs<string[]>;
|
|
606
|
+
/** Working directory; config-declared actions default to the repo's dir. */
|
|
607
|
+
cwd?: string;
|
|
608
|
+
/**
|
|
609
|
+
* Run through your login shell (`$SHELL -lic`) so it sources your profile
|
|
610
|
+
* (`~/.zshrc`, `~/.zprofile`, …). Use when the command isn't on the engine's PATH —
|
|
611
|
+
* `bunx`, `nvm`/`rbenv`/`pyenv` shims, Homebrew paths. Default: exec the command directly.
|
|
612
|
+
*/
|
|
613
|
+
login?: boolean;
|
|
614
|
+
env?: Record<string, string | Ref | undefined> | ((values: InputValues) => EnvRecord);
|
|
615
|
+
/** Tab title; defaults to the action id. */
|
|
616
|
+
title?: string;
|
|
617
|
+
/**
|
|
618
|
+
* Remove the session tab after a CLEAN exit once it's no longer being viewed
|
|
619
|
+
* (default). Failures always stay for inspection. Set false for sessions whose
|
|
620
|
+
* output should persist as a record.
|
|
621
|
+
*/
|
|
622
|
+
autoclose?: boolean;
|
|
623
|
+
}) | (ActionBase & {
|
|
624
|
+
mode: "handover";
|
|
625
|
+
run(values: InputValues): void;
|
|
626
|
+
}) | (ActionBase & {
|
|
627
|
+
/** A JS action — no process. Gets inputs + the service's exports + plugin helpers. */
|
|
628
|
+
mode: "function";
|
|
629
|
+
run(ctx: ActionRunCtx): void | Promise<void>;
|
|
630
|
+
});
|
|
631
|
+
/** Fields shared by every `action.*` builder — the base + typed `inputs`. */
|
|
632
|
+
type ActionSpecBase<R extends Record<string, ActionInput>> = {
|
|
633
|
+
id: string;
|
|
634
|
+
label: string;
|
|
635
|
+
/** Menu grouping (e.g. plugin name, or a custom "Tasks" group). Ungrouped if omitted. */
|
|
636
|
+
category?: string;
|
|
637
|
+
/** A form the TUI collects before running; each key's value type flows into the callbacks. */
|
|
638
|
+
inputs?: R;
|
|
639
|
+
};
|
|
640
|
+
/**
|
|
641
|
+
* Builders for actions — the typed counterpart to raw `ServiceAction` literals. Their payoff is
|
|
642
|
+
* INFERENCE: `inputs` value types thread into `command`/`args`/`env` (session) and `run` (fn/
|
|
643
|
+
* handover), so `ctx.inputs.kind` is the choice's option union, not `string | boolean`. The
|
|
644
|
+
* engine still consumes plain `ServiceAction` — these just build one.
|
|
645
|
+
*/
|
|
646
|
+
export declare const action: {
|
|
647
|
+
/** A PTY command (default). Set `login` to source your shell profile (bunx, nvm, …). */
|
|
648
|
+
session: <const R extends Record<string, ActionInput> = Record<never, ActionInput>>(spec: ActionSpecBase<R> & {
|
|
649
|
+
command: string | ((values: InputsValues<R>) => string);
|
|
650
|
+
args?: string[] | ((values: InputsValues<R>) => string[]);
|
|
651
|
+
env?: Record<string, string | Ref | undefined> | ((values: InputsValues<R>) => EnvRecord);
|
|
652
|
+
cwd?: string;
|
|
653
|
+
login?: boolean;
|
|
654
|
+
title?: string;
|
|
655
|
+
autoclose?: boolean;
|
|
656
|
+
}) => ServiceAction;
|
|
657
|
+
/** A JS action — `run(ctx)` gets typed `inputs` + the service's exports + plugin helpers. */
|
|
658
|
+
fn: <const R extends Record<string, ActionInput> = Record<never, ActionInput>>(spec: ActionSpecBase<R> & {
|
|
659
|
+
run: (ctx: Omit<ActionRunCtx, "inputs"> & {
|
|
660
|
+
inputs: InputsValues<R>;
|
|
661
|
+
}) => void | Promise<void>;
|
|
662
|
+
}) => ServiceAction;
|
|
663
|
+
/** An action that owns the real terminal — `run(values)` blocks until done. */
|
|
664
|
+
handover: <const R extends Record<string, ActionInput> = Record<never, ActionInput>>(spec: ActionSpecBase<R> & {
|
|
665
|
+
run: (values: InputsValues<R>) => void;
|
|
666
|
+
}) => ServiceAction;
|
|
667
|
+
};
|
|
668
|
+
export type Started = {
|
|
669
|
+
/** Named values other services reference (ports now known). Any JSON value. */
|
|
670
|
+
exports?: Record<string, JsonValue>;
|
|
671
|
+
/** Short display hint for UIs (e.g. ":52662"). */
|
|
672
|
+
info?: string;
|
|
673
|
+
/** Actions the TUI offers for this service (shell into container, etc.). */
|
|
674
|
+
actions?: ServiceAction[];
|
|
675
|
+
/** Resolves when healthy; gates dependents. */
|
|
676
|
+
ready?: Promise<void>;
|
|
677
|
+
/** Resolves when the service's process exits (lets the engine end cleanly). */
|
|
678
|
+
exited?: Promise<void>;
|
|
679
|
+
/**
|
|
680
|
+
* A persistable identity of the running instance (e.g. `{ pid }`, a container name) — recorded
|
|
681
|
+
* in the running-services registry so another session can check liveness (`Plugin.status`) and,
|
|
682
|
+
* later, reuse it. Omit for kinds with no external instance to track.
|
|
683
|
+
*/
|
|
684
|
+
handle?: JsonValue;
|
|
685
|
+
stop(): void | Promise<void>;
|
|
686
|
+
};
|
|
687
|
+
/** Dry-run tools handed to `Plugin.refs` for discovering refs inside function/prepare fields. */
|
|
688
|
+
export type RefTools = {
|
|
689
|
+
/** A recording workspace + a getter for the refs it logged (for dry-running `prepare`). */
|
|
690
|
+
recordWorkspace(): {
|
|
691
|
+
workspace: WorkspaceContext;
|
|
692
|
+
taken(): RefTarget[];
|
|
693
|
+
};
|
|
694
|
+
/** Stub port allocator (returns a placeholder; no real reservation during discovery). */
|
|
695
|
+
allocPort(label?: string): Promise<number>;
|
|
696
|
+
scope: StartCtx["scope"];
|
|
697
|
+
};
|
|
698
|
+
/** Context for `Plugin.plan` — enough to run prepare+exports with real ports, no runtime. */
|
|
699
|
+
export type PlanCtx = {
|
|
700
|
+
scope: StartCtx["scope"];
|
|
701
|
+
/** Real sticky allocation (same ports the eventual start will reuse). */
|
|
702
|
+
allocPort(label?: string): Promise<number>;
|
|
703
|
+
/** Real sticky secret (same value the eventual start will reuse). */
|
|
704
|
+
secret(key: string, opts?: SecretOptions): string;
|
|
705
|
+
/** Real sticky data dir (same path the eventual start will reuse). */
|
|
706
|
+
path(label?: string): string;
|
|
707
|
+
/** A workspace resolving already-planned sibling exports; unknown → the pending sentinel. */
|
|
708
|
+
workspace: WorkspaceContext;
|
|
709
|
+
/** Effect-free capability helpers (stubs) for prepare. */
|
|
710
|
+
caps: Record<string, unknown>;
|
|
711
|
+
/** Marker returned for anything not resolvable in the plan phase (→ the export is runtime). */
|
|
712
|
+
pending: string;
|
|
713
|
+
};
|
|
714
|
+
export type Plugin<Spec = unknown> = {
|
|
715
|
+
name: string;
|
|
716
|
+
start(spec: Spec, ctx: StartCtx): Promise<Started>;
|
|
717
|
+
/**
|
|
718
|
+
* Surface the refs this spec consumes. Optional — omit it and the engine deep-walks the
|
|
719
|
+
* spec (correct for static specs). A plugin with ref-bearing function fields implements
|
|
720
|
+
* this: invoke `env` with a placeholder bag and/or dry-run `prepare` with `tools`'
|
|
721
|
+
* recording workspace + stub caps, then return the refs collected/recorded.
|
|
722
|
+
*/
|
|
723
|
+
refs?(spec: Spec, tools: RefTools): RefTarget[] | Promise<RefTarget[]>;
|
|
724
|
+
/**
|
|
725
|
+
* Compute the exports knowable BEFORE start (ports/routes allocated in prepare) — so a ref
|
|
726
|
+
* to one of them resolves from the plan and never gates start-ordering. Return only clean,
|
|
727
|
+
* plan-resolvable values; drop (or leave `pending`-tainted) anything that needs the service
|
|
728
|
+
* running (e.g. a key read from a live process). Omit the method → all exports are treated
|
|
729
|
+
* as runtime (dependents wait for readiness). Not port-specific: a portless `url` is just as
|
|
730
|
+
* plan-resolvable as a numeric port.
|
|
731
|
+
*/
|
|
732
|
+
plan?(spec: Spec, ctx: PlanCtx): Record<string, JsonValue> | Promise<Record<string, JsonValue>>;
|
|
733
|
+
/**
|
|
734
|
+
* Authoritative liveness for a persisted instance `handle` (from a prior `Started.handle`) —
|
|
735
|
+
* how THIS kind checks whether its instance is still running (shell: is the pid alive; docker:
|
|
736
|
+
* is the container up). Omit → the engine falls back to a generic pid check on the handle/owner.
|
|
737
|
+
*/
|
|
738
|
+
status?(handle: JsonValue): "up" | "down" | Promise<"up" | "down">;
|
|
739
|
+
/**
|
|
740
|
+
* Stop an instance from its persisted `handle` — used when the LAST consumer of a shared
|
|
741
|
+
* service is not the run that started it (so there's no in-memory `Started` to call `stop()`
|
|
742
|
+
* on). shell kills the process group; docker stops the container. Omit → the shared service
|
|
743
|
+
* can't be torn down from a handle (it lingers until its owner exits or `ovr stop`).
|
|
744
|
+
*/
|
|
745
|
+
stop?(handle: JsonValue): void | Promise<void>;
|
|
746
|
+
};
|
|
747
|
+
/** Refs a service consumes — plugin-provided (function/prepare fields) or a static spec walk. */
|
|
748
|
+
export declare function serviceRefs(def: ServiceDef, tools: RefTools): Promise<RefTarget[]>;
|