@celestea/studio 2.7.1
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/LICENSE +21 -0
- package/dist/app.d.ts +111 -0
- package/dist/app.js +219 -0
- package/dist/auth/api-token.d.ts +76 -0
- package/dist/auth/api-token.js +172 -0
- package/dist/auth/htpasswd.d.ts +22 -0
- package/dist/auth/htpasswd.js +61 -0
- package/dist/auth/index.d.ts +14 -0
- package/dist/auth/index.js +14 -0
- package/dist/auth/page.d.ts +19 -0
- package/dist/auth/page.js +73 -0
- package/dist/auth/rate-limit.d.ts +27 -0
- package/dist/auth/rate-limit.js +47 -0
- package/dist/auth/token.d.ts +49 -0
- package/dist/auth/token.js +112 -0
- package/dist/config.d.ts +78 -0
- package/dist/config.js +72 -0
- package/dist/deployment.d.ts +92 -0
- package/dist/deployment.js +154 -0
- package/dist/fake-runtime-adapter.d.ts +37 -0
- package/dist/fake-runtime-adapter.js +276 -0
- package/dist/handlers/auth.d.ts +24 -0
- package/dist/handlers/auth.js +155 -0
- package/dist/handlers/common.d.ts +76 -0
- package/dist/handlers/common.js +127 -0
- package/dist/handlers/config-shape.d.ts +76 -0
- package/dist/handlers/config-shape.js +227 -0
- package/dist/handlers/config.d.ts +26 -0
- package/dist/handlers/config.js +161 -0
- package/dist/handlers/context-shape.d.ts +33 -0
- package/dist/handlers/context-shape.js +59 -0
- package/dist/handlers/dialog.d.ts +22 -0
- package/dist/handlers/dialog.js +292 -0
- package/dist/handlers/exec.d.ts +22 -0
- package/dist/handlers/exec.js +131 -0
- package/dist/handlers/fs-read.d.ts +63 -0
- package/dist/handlers/fs-read.js +159 -0
- package/dist/handlers/fs.d.ts +57 -0
- package/dist/handlers/fs.js +153 -0
- package/dist/handlers/grants-shape.d.ts +45 -0
- package/dist/handlers/grants-shape.js +130 -0
- package/dist/handlers/grants.d.ts +21 -0
- package/dist/handlers/grants.js +267 -0
- package/dist/handlers/health.d.ts +37 -0
- package/dist/handlers/health.js +139 -0
- package/dist/handlers/index.d.ts +40 -0
- package/dist/handlers/index.js +85 -0
- package/dist/handlers/permissions.d.ts +9 -0
- package/dist/handlers/permissions.js +183 -0
- package/dist/handlers/plugins.d.ts +18 -0
- package/dist/handlers/plugins.js +23 -0
- package/dist/handlers/prompts.d.ts +12 -0
- package/dist/handlers/prompts.js +120 -0
- package/dist/handlers/providers.d.ts +12 -0
- package/dist/handlers/providers.js +178 -0
- package/dist/handlers/questions.d.ts +23 -0
- package/dist/handlers/questions.js +115 -0
- package/dist/handlers/session-model.d.ts +38 -0
- package/dist/handlers/session-model.js +105 -0
- package/dist/handlers/session-move.d.ts +22 -0
- package/dist/handlers/session-move.js +188 -0
- package/dist/handlers/session-tools.d.ts +19 -0
- package/dist/handlers/session-tools.js +75 -0
- package/dist/handlers/sessions.d.ts +32 -0
- package/dist/handlers/sessions.js +242 -0
- package/dist/handlers/usage.d.ts +26 -0
- package/dist/handlers/usage.js +77 -0
- package/dist/handlers/worker.d.ts +12 -0
- package/dist/handlers/worker.js +82 -0
- package/dist/handlers/workspaces.d.ts +12 -0
- package/dist/handlers/workspaces.js +88 -0
- package/dist/harness.test-util.d.ts +101 -0
- package/dist/harness.test-util.js +176 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +33 -0
- package/dist/main.d.ts +13 -0
- package/dist/main.js +35 -0
- package/dist/plugins.d.ts +105 -0
- package/dist/plugins.js +97 -0
- package/dist/question-registry.d.ts +124 -0
- package/dist/question-registry.js +212 -0
- package/dist/question-rows.d.ts +32 -0
- package/dist/question-rows.js +43 -0
- package/dist/replay/compare.d.ts +47 -0
- package/dist/replay/compare.js +89 -0
- package/dist/replay/e2e-replay.d.ts +46 -0
- package/dist/replay/e2e-replay.js +91 -0
- package/dist/replay/expect-compact.d.ts +31 -0
- package/dist/replay/expect-compact.js +92 -0
- package/dist/replay/fixtures.d.ts +52 -0
- package/dist/replay/fixtures.js +53 -0
- package/dist/replay/host.d.ts +32 -0
- package/dist/replay/host.js +62 -0
- package/dist/replay/index.d.ts +16 -0
- package/dist/replay/index.js +16 -0
- package/dist/replay/probes.d.ts +55 -0
- package/dist/replay/probes.js +225 -0
- package/dist/replay/report.d.ts +13 -0
- package/dist/replay/report.js +103 -0
- package/dist/replay/session-e2e.d.ts +44 -0
- package/dist/replay/session-e2e.js +91 -0
- package/dist/routes.d.ts +47 -0
- package/dist/routes.js +59 -0
- package/dist/runtime/attachments-llm.d.ts +13 -0
- package/dist/runtime/attachments-llm.js +37 -0
- package/dist/runtime/boot-recovery.d.ts +32 -0
- package/dist/runtime/boot-recovery.js +71 -0
- package/dist/runtime/context-snapshot.d.ts +59 -0
- package/dist/runtime/context-snapshot.js +113 -0
- package/dist/runtime/engine-grants.d.ts +121 -0
- package/dist/runtime/engine-grants.js +338 -0
- package/dist/runtime/engine-permissions.d.ts +29 -0
- package/dist/runtime/engine-permissions.js +92 -0
- package/dist/runtime/engine-plugins.d.ts +189 -0
- package/dist/runtime/engine-plugins.js +292 -0
- package/dist/runtime/engine-profile.d.ts +28 -0
- package/dist/runtime/engine-profile.js +88 -0
- package/dist/runtime/engine-session.d.ts +9 -0
- package/dist/runtime/engine-session.js +9 -0
- package/dist/runtime/fallback-contract.d.ts +32 -0
- package/dist/runtime/fallback-contract.js +10 -0
- package/dist/runtime/fallback-host.d.ts +134 -0
- package/dist/runtime/fallback-host.js +299 -0
- package/dist/runtime/host-autowake.d.ts +79 -0
- package/dist/runtime/host-autowake.js +86 -0
- package/dist/runtime/image-downgrade.d.ts +49 -0
- package/dist/runtime/image-downgrade.js +90 -0
- package/dist/runtime/inbox-message.d.ts +11 -0
- package/dist/runtime/inbox-message.js +20 -0
- package/dist/runtime/index.d.ts +17 -0
- package/dist/runtime/index.js +17 -0
- package/dist/runtime/ledger-view.d.ts +40 -0
- package/dist/runtime/ledger-view.js +56 -0
- package/dist/runtime/llm-assembly.d.ts +66 -0
- package/dist/runtime/llm-assembly.js +114 -0
- package/dist/runtime/offline-llm.d.ts +71 -0
- package/dist/runtime/offline-llm.js +132 -0
- package/dist/runtime/provider-target.d.ts +10 -0
- package/dist/runtime/provider-target.js +10 -0
- package/dist/runtime/question-host.d.ts +33 -0
- package/dist/runtime/question-host.js +41 -0
- package/dist/runtime/question-view.d.ts +48 -0
- package/dist/runtime/question-view.js +76 -0
- package/dist/runtime/real-runtime-adapter.d.ts +95 -0
- package/dist/runtime/real-runtime-adapter.js +553 -0
- package/dist/runtime/recovery-audit.d.ts +81 -0
- package/dist/runtime/recovery-audit.js +95 -0
- package/dist/runtime/recovery-view.d.ts +36 -0
- package/dist/runtime/recovery-view.js +63 -0
- package/dist/runtime/session-compose.d.ts +235 -0
- package/dist/runtime/session-compose.js +350 -0
- package/dist/runtime/session-grants.d.ts +36 -0
- package/dist/runtime/session-grants.js +69 -0
- package/dist/runtime/session-lifecycle.d.ts +40 -0
- package/dist/runtime/session-lifecycle.js +86 -0
- package/dist/runtime/session-publisher.d.ts +40 -0
- package/dist/runtime/session-publisher.js +48 -0
- package/dist/runtime/session-release.d.ts +53 -0
- package/dist/runtime/session-release.js +80 -0
- package/dist/runtime/test-util.d.ts +78 -0
- package/dist/runtime/test-util.js +162 -0
- package/dist/runtime/watchdog-view.d.ts +33 -0
- package/dist/runtime/watchdog-view.js +45 -0
- package/dist/runtime/worker-bridge.d.ts +76 -0
- package/dist/runtime/worker-bridge.js +156 -0
- package/dist/runtime/worker-live.d.ts +22 -0
- package/dist/runtime/worker-live.js +25 -0
- package/dist/runtime/worker-recovery.d.ts +35 -0
- package/dist/runtime/worker-recovery.js +64 -0
- package/dist/runtime/worker-table.d.ts +58 -0
- package/dist/runtime/worker-table.js +70 -0
- package/dist/runtime-adapter.d.ts +421 -0
- package/dist/runtime-adapter.js +76 -0
- package/dist/serial-queue.d.ts +16 -0
- package/dist/serial-queue.js +22 -0
- package/dist/server.d.ts +44 -0
- package/dist/server.js +119 -0
- package/dist/settings.d.ts +23 -0
- package/dist/settings.js +31 -0
- package/dist/sse.d.ts +67 -0
- package/dist/sse.js +186 -0
- package/dist/static.d.ts +20 -0
- package/dist/static.js +120 -0
- package/dist/store/builtin-sections.d.ts +52 -0
- package/dist/store/builtin-sections.js +118 -0
- package/dist/store/celestea-home.d.ts +12 -0
- package/dist/store/celestea-home.js +11 -0
- package/dist/store/fs-json.d.ts +51 -0
- package/dist/store/fs-json.js +119 -0
- package/dist/store/grants-audit.d.ts +84 -0
- package/dist/store/grants-audit.js +109 -0
- package/dist/store/grants-service.d.ts +44 -0
- package/dist/store/grants-service.js +41 -0
- package/dist/store/grants-tokens.d.ts +62 -0
- package/dist/store/grants-tokens.js +105 -0
- package/dist/store/grants.d.ts +113 -0
- package/dist/store/grants.js +238 -0
- package/dist/store/index.d.ts +36 -0
- package/dist/store/index.js +36 -0
- package/dist/store/mode.d.ts +35 -0
- package/dist/store/mode.js +41 -0
- package/dist/store/permissions.d.ts +61 -0
- package/dist/store/permissions.js +139 -0
- package/dist/store/prompts-compose.d.ts +56 -0
- package/dist/store/prompts-compose.js +62 -0
- package/dist/store/prompts-template.d.ts +26 -0
- package/dist/store/prompts-template.js +99 -0
- package/dist/store/prompts.d.ts +142 -0
- package/dist/store/prompts.js +273 -0
- package/dist/store/provider-probe.d.ts +81 -0
- package/dist/store/provider-probe.js +154 -0
- package/dist/store/providers.d.ts +97 -0
- package/dist/store/providers.js +219 -0
- package/dist/store/result.d.ts +29 -0
- package/dist/store/result.js +30 -0
- package/dist/store/session-id.d.ts +119 -0
- package/dist/store/session-id.js +220 -0
- package/dist/store/session-meta.d.ts +41 -0
- package/dist/store/session-meta.js +66 -0
- package/dist/store/session-ops.d.ts +109 -0
- package/dist/store/session-ops.js +264 -0
- package/dist/store/session-tools.d.ts +42 -0
- package/dist/store/session-tools.js +69 -0
- package/dist/store/sessions.d.ts +162 -0
- package/dist/store/sessions.js +307 -0
- package/dist/store/validate.d.ts +13 -0
- package/dist/store/validate.js +38 -0
- package/dist/store/workspaces.d.ts +67 -0
- package/dist/store/workspaces.js +228 -0
- package/dist/user-questions.d.ts +58 -0
- package/dist/user-questions.js +157 -0
- package/dist/version.d.ts +14 -0
- package/dist/version.js +52 -0
- package/package.json +38 -0
- package/webdist/assets/index-Bngs7gUm.js +98 -0
- package/webdist/assets/index-Bu6ci_rN.css +1 -0
- package/webdist/assets/katex-CCbS2qAY.js +257 -0
- package/webdist/assets/mhchem-CnUN8HwY.js +1 -0
- package/webdist/build-meta.json +7 -0
- package/webdist/index.html +261 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RuntimeAdapter` — the ONE seam between the Studio host and the engine.
|
|
3
|
+
*
|
|
4
|
+
* P4 ships the Hono layer plus the data stores; the real runtime
|
|
5
|
+
* (`packages/runtime` compose + agent-loop + llm + tools) lands on a separate
|
|
6
|
+
* workstream. Everything the engine owns is therefore expressed here as an
|
|
7
|
+
* injected interface, and P4 verifies the contract against a fake adapter:
|
|
8
|
+
*
|
|
9
|
+
* POST /api/turn -> startTurn() / inject() (W513: a busy
|
|
10
|
+
* session takes an interjection instead
|
|
11
|
+
* of a 409; the turn is never restarted)
|
|
12
|
+
* GET /api/events -> attach(bus) (the adapter emits,
|
|
13
|
+
* one envelope per session)
|
|
14
|
+
* POST /api/cancel -> cancel(session)
|
|
15
|
+
* POST /api/clear -> clear(session)
|
|
16
|
+
* POST /api/sessions/{id}/activate -> ensureSession(id) (W513: never 409)
|
|
17
|
+
* POST /api/sessions/{id}/compact -> compact(session)
|
|
18
|
+
* GET /api/status -> statusline(session?) + isBusy(session?)
|
|
19
|
+
* GET /api/tools -> tools()
|
|
20
|
+
* GET /api/sessions/{id}/context -> sessionContext(session) (W725)
|
|
21
|
+
* GET+POST /api/config -> profile() / configure(patch)
|
|
22
|
+
* POST /api/worker/{spawn,send} -> workerSpawn() / workerSend()
|
|
23
|
+
* GET /api/worker/status -> workerStatus(wid?)
|
|
24
|
+
* GET /api/sessions (worker rows) -> workerSessions()
|
|
25
|
+
* GET /api/sessions/worker:<sid>/… -> workerMessages(sid)
|
|
26
|
+
*
|
|
27
|
+
* W513 (session independence): busy, turn numbering, status/usage trackers and
|
|
28
|
+
* the session inbox are PER SESSION. `isBusy()` with no argument keeps the
|
|
29
|
+
* legacy "is anything running" reading for the handlers that guard process-wide
|
|
30
|
+
* operations; every session-scoped handler passes the target session id.
|
|
31
|
+
*
|
|
32
|
+
* Replacing the fake with the real runtime is a one-line change in
|
|
33
|
+
* `createStudioApp({ runtime })` — no handler changes, no route changes.
|
|
34
|
+
*/
|
|
35
|
+
import type { AskUserQuestionAnswerItem, AskUserQuestionItem, ImageRef, InjectionPlacement, Statusline } from "@celestea/core";
|
|
36
|
+
/**
|
|
37
|
+
* W737: the busy-slot error is part of the ENGINE contract, so it has exactly
|
|
38
|
+
* one definition — `packages/runtime/src/errors.ts`. It is imported (never
|
|
39
|
+
* redefined) here and re-exported, so every studio-side import of
|
|
40
|
+
* `TurnBusyError` resolves to the very class object the real engine throws.
|
|
41
|
+
*/
|
|
42
|
+
import { TurnBusyError } from "@celestea/runtime";
|
|
43
|
+
/**
|
|
44
|
+
* W785 (E-P1, capability 3): the aggregate views of the usage ledger. Types only
|
|
45
|
+
* — the value (`queryLedger`/`ledgerCostBlock`) is called by the REAL adapter.
|
|
46
|
+
*/
|
|
47
|
+
import type { LedgerCostBlock, LedgerQuery, LedgerQueryResult } from "@celestea/runtime";
|
|
48
|
+
import type { FallbackStatusView } from "./runtime/fallback-contract.js";
|
|
49
|
+
import type { StudioBus } from "./sse.js";
|
|
50
|
+
/** Verbatim engine error text (`{e}` placeholders). */
|
|
51
|
+
export declare class EngineError extends Error {
|
|
52
|
+
readonly kind = "engine";
|
|
53
|
+
constructor(message: string);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Thrown by `startTurn` when the single-concurrency slot is occupied, and by
|
|
57
|
+
* `clear` while the target session's turn is in flight (409).
|
|
58
|
+
*
|
|
59
|
+
* W737: SINGLE SOURCE — `@celestea/runtime`'s `errors.ts` (`StudioError`
|
|
60
|
+
* subclass: `status: 409`, `kind: "turn_busy"`). This file used to declare a
|
|
61
|
+
* second, `extends Error` copy; the handlers branched on that copy, so the real
|
|
62
|
+
* engine's error failed `instanceof` and the busy race surfaced as a 500
|
|
63
|
+
* instead of a 409 / an interjection. Only the fake adapter threw the copy,
|
|
64
|
+
* which is what kept the contract tests green. Do not redeclare it here.
|
|
65
|
+
*/
|
|
66
|
+
export { TurnBusyError };
|
|
67
|
+
/** Thrown when the live-session / concurrent-turn cap is reached (503). */
|
|
68
|
+
export declare class CapacityError extends Error {
|
|
69
|
+
readonly kind = "capacity";
|
|
70
|
+
/** Seconds the client should wait before retrying (Retry-After). */
|
|
71
|
+
readonly retryAfterSeconds: number;
|
|
72
|
+
constructor(message: string, retryAfterSeconds?: number);
|
|
73
|
+
}
|
|
74
|
+
export interface EngineProfile {
|
|
75
|
+
model: string;
|
|
76
|
+
base_url: string;
|
|
77
|
+
reasoning_effort: string | null;
|
|
78
|
+
max_steps: number;
|
|
79
|
+
max_parallel_tool_calls: number;
|
|
80
|
+
max_output_tokens: number | null;
|
|
81
|
+
context_window: number;
|
|
82
|
+
api_key_env: string;
|
|
83
|
+
/** Registry-assembled (or overridden) system prompt. */
|
|
84
|
+
system_prompt: string;
|
|
85
|
+
}
|
|
86
|
+
/** `POST /api/config` accepted patch: the host validates, the engine applies. */
|
|
87
|
+
export interface ProfilePatch {
|
|
88
|
+
model?: string;
|
|
89
|
+
reasoning_effort?: string | null;
|
|
90
|
+
base_url?: string;
|
|
91
|
+
/** Goes into the process env only: never persisted, echoed or logged. */
|
|
92
|
+
api_key?: string;
|
|
93
|
+
max_steps?: number;
|
|
94
|
+
max_output_tokens?: number | null;
|
|
95
|
+
context_window?: number;
|
|
96
|
+
system_prompt?: string;
|
|
97
|
+
}
|
|
98
|
+
export interface ToolInfo {
|
|
99
|
+
name: string;
|
|
100
|
+
description: string;
|
|
101
|
+
}
|
|
102
|
+
/** One model-visible message, flattened for the context viewer (W725). */
|
|
103
|
+
export interface ContextMessageView {
|
|
104
|
+
role: string;
|
|
105
|
+
content: string;
|
|
106
|
+
/** Name of the tool this message calls (assistant) or answers (tool). */
|
|
107
|
+
tool_name?: string;
|
|
108
|
+
/** Provider call id, set on a `tool` result (and on the call it answers). */
|
|
109
|
+
tool_call_id?: string;
|
|
110
|
+
}
|
|
111
|
+
/** `registry.schemas()` row -> the two-field view the host exposes (W729). */
|
|
112
|
+
export declare function toolSpecView(spec: {
|
|
113
|
+
name: string;
|
|
114
|
+
description: string;
|
|
115
|
+
}): ToolInfo;
|
|
116
|
+
/** One tool schema the model is offered (W725) — `registry.schemas()` verbatim. */
|
|
117
|
+
export interface ContextToolView {
|
|
118
|
+
name: string;
|
|
119
|
+
description: string;
|
|
120
|
+
parameters: Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* W725: one session's model-visible context, as the ENGINE assembles it —
|
|
124
|
+
* system prompt, the messages the next step would send (already trimmed by the
|
|
125
|
+
* loop) and the tool schemas. Read-only: taking a snapshot never drives a turn.
|
|
126
|
+
*/
|
|
127
|
+
export interface SessionContextView {
|
|
128
|
+
model: string;
|
|
129
|
+
system: string;
|
|
130
|
+
tools: ContextToolView[];
|
|
131
|
+
messages: ContextMessageView[];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* W847: requested delivery lane for a BUSY session (POST /api/turn body
|
|
135
|
+
* `mode`). `steer` (default) = inject into the RUNNING turn at its next step
|
|
136
|
+
* boundary; `queue` = park on the next-turn lane for the NEXT turn start. On an
|
|
137
|
+
* IDLE session the field is ignored (the input IS the new turn).
|
|
138
|
+
*/
|
|
139
|
+
export type TurnDeliveryMode = "steer" | "queue";
|
|
140
|
+
export interface TurnRequest {
|
|
141
|
+
input: string;
|
|
142
|
+
/** Active session id, or null when nothing is activated. */
|
|
143
|
+
session: string | null;
|
|
144
|
+
/**
|
|
145
|
+
* W847: optional lane request. Omitted behaves exactly like `steer` (the
|
|
146
|
+
* pre-W847 request byte for byte). Only the host's busy path reads it.
|
|
147
|
+
*/
|
|
148
|
+
mode?: TurnDeliveryMode;
|
|
149
|
+
/**
|
|
150
|
+
* W804: content-addressed image references for THIS turn's user message
|
|
151
|
+
* (already stored by the host). Omitted = the pre-W804 request byte for byte.
|
|
152
|
+
*/
|
|
153
|
+
attachments?: readonly ImageRef[];
|
|
154
|
+
}
|
|
155
|
+
export interface TurnStart {
|
|
156
|
+
turn: number;
|
|
157
|
+
/** W515 §2: the placement of this turn's own input (`context` = it IS the turn). */
|
|
158
|
+
placement?: InjectionPlacement;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Result of delivering a message into a session (W513 interjection).
|
|
162
|
+
*
|
|
163
|
+
* W515 §2: `placement` is the client-visible landing state —
|
|
164
|
+
* `steering` = will be injected into the RUNNING turn at its next step
|
|
165
|
+
* boundary, `queued` = accepted and waiting for the next turn start,
|
|
166
|
+
* `context` = already appended to the model-visible log.
|
|
167
|
+
*/
|
|
168
|
+
export interface InjectOutcome {
|
|
169
|
+
/** Session-local turn number the message was (or will be) injected into. */
|
|
170
|
+
turn: number;
|
|
171
|
+
/** True = delivered into a RUNNING turn; false = queued for the next one. */
|
|
172
|
+
injected: boolean;
|
|
173
|
+
/** Messages still waiting on the target lane after this delivery. */
|
|
174
|
+
pending: number;
|
|
175
|
+
placement: InjectionPlacement;
|
|
176
|
+
/** True when the idempotency key was already accepted (nothing was queued). */
|
|
177
|
+
duplicate: boolean;
|
|
178
|
+
}
|
|
179
|
+
/** `POST /api/sessions/{id}/activate` — "open the view + ensure the runtime". */
|
|
180
|
+
export interface SessionRuntimeInfo {
|
|
181
|
+
/** `created` = this call composed the instance, `reused` = it already existed. */
|
|
182
|
+
runtime: "created" | "reused";
|
|
183
|
+
/** Whether the session has an in-flight turn right now. */
|
|
184
|
+
busy: boolean;
|
|
185
|
+
/** True when the instance was recomposed (profile epoch had moved on). */
|
|
186
|
+
rebuilt: boolean;
|
|
187
|
+
}
|
|
188
|
+
export interface ClearOutcome {
|
|
189
|
+
cleared: boolean;
|
|
190
|
+
}
|
|
191
|
+
export interface CompactOutcome {
|
|
192
|
+
compacted: boolean;
|
|
193
|
+
/** Present only when `compacted === true`. */
|
|
194
|
+
kept_turns?: number;
|
|
195
|
+
note: string;
|
|
196
|
+
/** Canonical session id the compact ran against. */
|
|
197
|
+
session: string;
|
|
198
|
+
rebound: boolean;
|
|
199
|
+
}
|
|
200
|
+
export interface WorkerSpawnRequest {
|
|
201
|
+
wid: string;
|
|
202
|
+
brief: string;
|
|
203
|
+
title?: string;
|
|
204
|
+
model?: string;
|
|
205
|
+
report_to?: string;
|
|
206
|
+
/** Host session whose registry spawns the worker (default: active session). */
|
|
207
|
+
session?: string | null;
|
|
208
|
+
}
|
|
209
|
+
export interface WorkerSpawnOutcome {
|
|
210
|
+
ok: boolean;
|
|
211
|
+
sessionId?: string;
|
|
212
|
+
title?: string;
|
|
213
|
+
wid?: string;
|
|
214
|
+
error?: string;
|
|
215
|
+
/** Tool envelope passthrough (`{ok:false, value:…}`). */
|
|
216
|
+
value?: unknown;
|
|
217
|
+
}
|
|
218
|
+
export interface WorkerSendRequest {
|
|
219
|
+
target: string;
|
|
220
|
+
content: string;
|
|
221
|
+
}
|
|
222
|
+
import type { RecoveryView } from "./runtime/recovery-view.js";
|
|
223
|
+
/** E §1.3 P1 ②: the `/api/status.recovery` block (see `runtime/recovery-view.ts`). */
|
|
224
|
+
export type { RecoveryView };
|
|
225
|
+
export interface WorkerStatusReport {
|
|
226
|
+
ok: boolean;
|
|
227
|
+
total: number;
|
|
228
|
+
by_status: Record<string, number>;
|
|
229
|
+
by_state?: Record<string, number>;
|
|
230
|
+
workers: unknown[];
|
|
231
|
+
wid?: string;
|
|
232
|
+
error?: string;
|
|
233
|
+
/**
|
|
234
|
+
* E §2.3 P0 ③ (W787): RUNNING rows of the PERSISTED table whose owning process
|
|
235
|
+
* is gone, and RUNNING rows whose `host=` session no longer exists. Observation
|
|
236
|
+
* only — the studio never re-dispatches at boot (P2, `CELESTEA_WORKER_RECOVER`).
|
|
237
|
+
*/
|
|
238
|
+
stale?: unknown[];
|
|
239
|
+
orphans?: unknown[];
|
|
240
|
+
/**
|
|
241
|
+
* W740: how many live instances are sweeping their worker rows (the count of
|
|
242
|
+
* RUNNING watchdog timers). Absent from an engine that mounts no watchdog.
|
|
243
|
+
*/
|
|
244
|
+
watchdogs?: number;
|
|
245
|
+
}
|
|
246
|
+
/** Engine-memory worker session row (`kind: "worker"`, workspace "engine"). */
|
|
247
|
+
export interface WorkerSessionRow {
|
|
248
|
+
id: string;
|
|
249
|
+
workspace: string;
|
|
250
|
+
kind: "worker";
|
|
251
|
+
title: string;
|
|
252
|
+
model: string | null;
|
|
253
|
+
size: number;
|
|
254
|
+
modified: number;
|
|
255
|
+
active: boolean;
|
|
256
|
+
/** W729: the mode the worker inherited (or was spawned with). */
|
|
257
|
+
mode: string;
|
|
258
|
+
/** Worker id (`W513`) — the same `wid` the registry row carries. */
|
|
259
|
+
wid?: string;
|
|
260
|
+
/** Registry status: `RUNNING` / `DONE` / `FAILED`. */
|
|
261
|
+
status?: string;
|
|
262
|
+
/** Driver state: `idle` / `in-turn`. */
|
|
263
|
+
state?: string;
|
|
264
|
+
/** Host session that owns this worker's registry. */
|
|
265
|
+
host_session?: string | null;
|
|
266
|
+
/** E §2.3 P1 ③ (W787): which try this row is (first spawn = 1, re-dispatch +1). */
|
|
267
|
+
attempt?: number;
|
|
268
|
+
/** E §2.3 P1 ③ (W787): idempotency key of the delivered receipt (`wid:attempt`). */
|
|
269
|
+
last_receipt?: string | null;
|
|
270
|
+
/** Whether the OWNING host session has an in-flight turn. */
|
|
271
|
+
busy?: boolean;
|
|
272
|
+
}
|
|
273
|
+
export interface RuntimeAdapter {
|
|
274
|
+
/** Diagnostic name, surfaced by tests and logs (never by the HTTP API). */
|
|
275
|
+
readonly name: string;
|
|
276
|
+
/** Hand the adapter the bus it emits engine frames into. */
|
|
277
|
+
attach(bus: StudioBus): void;
|
|
278
|
+
/**
|
|
279
|
+
* Optional host hook: hand the engine the system prompt the HOST assembled
|
|
280
|
+
* (prompt registry + settings override). The real adapter applies it to the
|
|
281
|
+
* next composed generation; an adapter without a prompt registry ignores it.
|
|
282
|
+
*/
|
|
283
|
+
primeSystemPrompt?(prompt: string): void;
|
|
284
|
+
/**
|
|
285
|
+
* Busy probe. No argument = "is ANY session running" (legacy reading, used by
|
|
286
|
+
* the process-wide guards); with a session id = that session's own slot.
|
|
287
|
+
*/
|
|
288
|
+
isBusy(session?: string | null): boolean;
|
|
289
|
+
/** Grab the session's slot, emit `status:start`, return; rest goes over SSE. */
|
|
290
|
+
startTurn(req: TurnRequest): Promise<TurnStart>;
|
|
291
|
+
/**
|
|
292
|
+
* Deliver `input` into the session's RUNNING turn: it is appended as a
|
|
293
|
+
* `user_message` at the next step boundary (no new turn, no interruption).
|
|
294
|
+
*/
|
|
295
|
+
inject(req: TurnRequest): InjectOutcome;
|
|
296
|
+
/** Ensure the session has a runtime instance (activate; never fails on busy). */
|
|
297
|
+
ensureSession(session: string | null): SessionRuntimeInfo;
|
|
298
|
+
/**
|
|
299
|
+
* W516: this session's security boundary changed (its `grants.json` was
|
|
300
|
+
* written) — drop its instance so the next turn recomposes. The turn in
|
|
301
|
+
* flight keeps the boundary it started with; returns false when the session
|
|
302
|
+
* has no live instance (nothing to invalidate).
|
|
303
|
+
*/
|
|
304
|
+
invalidateSession?(session: string | null): boolean;
|
|
305
|
+
/**
|
|
306
|
+
* W794: the session's DIRECTORY is going away (delete / archive) — cut the
|
|
307
|
+
* model response it may be streaming right now and hand back the engine
|
|
308
|
+
* instance it owned.
|
|
309
|
+
*
|
|
310
|
+
* Order is the contract: the in-flight turn is aborted through the SAME
|
|
311
|
+
* cooperative path `POST /api/cancel` uses, the host then waits (bounded) for
|
|
312
|
+
* it to settle, and only then is THAT instance disposed and forgotten (never
|
|
313
|
+
* the process-wide generation, never a neighbour's instance). `true` = an
|
|
314
|
+
* instance was released; `false` = this session had none (nothing to do).
|
|
315
|
+
*
|
|
316
|
+
* Optional: an adapter with no per-session registry has nothing to release.
|
|
317
|
+
*/
|
|
318
|
+
releaseSession?(session: string | null): Promise<boolean>;
|
|
319
|
+
/** Session ids with a live runtime instance. */
|
|
320
|
+
liveSessions(): string[];
|
|
321
|
+
/** Session ids with an in-flight turn. */
|
|
322
|
+
busySessions(): string[];
|
|
323
|
+
/** Cooperative cancel of the target session's turn: true = signal sent. */
|
|
324
|
+
cancel(session?: string | null): boolean;
|
|
325
|
+
/** Truncate the active session log + reset the turn counter. */
|
|
326
|
+
clear(session: string | null): Promise<ClearOutcome>;
|
|
327
|
+
compact(session: string): Promise<CompactOutcome>;
|
|
328
|
+
profile(): EngineProfile;
|
|
329
|
+
/** Apply an accepted patch (hot compose); throws EngineError on failure. */
|
|
330
|
+
configure(patch: ProfilePatch): Promise<EngineProfile>;
|
|
331
|
+
statusline(session?: string | null): Statusline;
|
|
332
|
+
/**
|
|
333
|
+
* E §4.2.3 #4 (W785): the model-fallback view of one session — which target
|
|
334
|
+
* chain is armed, which model is ACTUALLY serving and what went wrong.
|
|
335
|
+
* `null`/absent = the capability is off for this process, and the handler then
|
|
336
|
+
* reports `effective_model = model` with `fallback.active = false`.
|
|
337
|
+
*/
|
|
338
|
+
fallbackView?(session: string | null): FallbackStatusView | null;
|
|
339
|
+
/**
|
|
340
|
+
* E-P1 (capability 3, W785): the aggregate view behind `GET /api/usage/ledger`
|
|
341
|
+
* — the ONE append-only ledger of the process, filtered and folded by
|
|
342
|
+
* `session`/`turn`/`model`/`day`. Optional: an adapter without a ledger answers
|
|
343
|
+
* `{ok:false, error:"usage ledger unavailable"}` through the handler, and a
|
|
344
|
+
* ledger that is switched OFF reports `{ok:false, error:"usage ledger disabled"}`
|
|
345
|
+
* (both HTTP 200 — the key is "no ledger here", not a client error).
|
|
346
|
+
*/
|
|
347
|
+
usageLedger?(q: LedgerQuery): LedgerQueryResult | {
|
|
348
|
+
ok: false;
|
|
349
|
+
error: string;
|
|
350
|
+
};
|
|
351
|
+
/**
|
|
352
|
+
* E-P1 (capability 3, W785): this session's cost block for `/api/status`
|
|
353
|
+
* (`{session_total, turn_total, attempts, currency, priced_by, …}`). `null` =
|
|
354
|
+
* no ledger configured; an absent method means the same, and the handler then
|
|
355
|
+
* omits the `cost` key entirely (the field is a pure addition, §3.2.4).
|
|
356
|
+
*/
|
|
357
|
+
costBlock?(session: string | null): LedgerCostBlock | null;
|
|
358
|
+
/**
|
|
359
|
+
* E §1.3 P1 ② (W787): the session's `recovery` block for `GET /api/status`
|
|
360
|
+
* (checkpoint repairs, dangling turns, log degradation, last outcome).
|
|
361
|
+
* Optional: an adapter without checkpointing answers the empty block.
|
|
362
|
+
*/
|
|
363
|
+
recoveryView?(session: string | null): RecoveryView;
|
|
364
|
+
tools(): ToolInfo[];
|
|
365
|
+
/**
|
|
366
|
+
* W729 (S2): the tool face of ONE session, used to render the `{{tools}}`
|
|
367
|
+
* variable of that session's own system prompt. Optional: an adapter without
|
|
368
|
+
* per-session generations falls back to [tools]. The real adapter answers from
|
|
369
|
+
* the session's LIVE instance and never composes one (the composer calls this
|
|
370
|
+
* while composing that very session — peeking keeps that non-recursive).
|
|
371
|
+
*/
|
|
372
|
+
sessionTools?(session: string | null): ToolInfo[];
|
|
373
|
+
/**
|
|
374
|
+
* W725: the session's OWN model-visible context (`GET /api/sessions/{id}/
|
|
375
|
+
* context`). The instance is composed on demand, exactly like activate does;
|
|
376
|
+
* an unknown session is the handler's 404, never this seam's guess.
|
|
377
|
+
*/
|
|
378
|
+
sessionContext(session: string | null): SessionContextView;
|
|
379
|
+
workerSpawn(req: WorkerSpawnRequest): Promise<WorkerSpawnOutcome>;
|
|
380
|
+
workerSend(req: WorkerSendRequest): Promise<Record<string, unknown>>;
|
|
381
|
+
workerStatus(wid?: string): WorkerStatusReport;
|
|
382
|
+
workerSessions(): WorkerSessionRow[];
|
|
383
|
+
/** Transcript of an engine-memory worker session, or null when unknown. */
|
|
384
|
+
workerMessages(sessionId: string): unknown[] | null;
|
|
385
|
+
/**
|
|
386
|
+
* W783: answer one pending user question (`POST /api/questions/{id}/answer`).
|
|
387
|
+
* Optional — an embedded adapter without the user-question feature simply does
|
|
388
|
+
* not implement it, and the handler then reports the question as unknown.
|
|
389
|
+
*/
|
|
390
|
+
answerQuestion?(requestId: string, answers: AskUserQuestionAnswerItem[], sessionId?: string): QuestionAnswerOutcome;
|
|
391
|
+
/** W783: every question still answerable (`GET /api/questions`, §7 recovery). */
|
|
392
|
+
pendingQuestions?(sessionId?: string | null): PendingQuestionView[];
|
|
393
|
+
}
|
|
394
|
+
/** W783: why an answer was refused (`ok:false`), never a silent no-op. */
|
|
395
|
+
export type QuestionAnswerRefusal = "unknown" | "settled" | "timed_out" | "mismatch";
|
|
396
|
+
/**
|
|
397
|
+
* W783: the outcome of one answer attempt. The accepted case echoes the asking
|
|
398
|
+
* session, because the pending entry is gone by the time the handler answers the
|
|
399
|
+
* HTTP request (reading it back from the table would always find nothing).
|
|
400
|
+
*/
|
|
401
|
+
export type QuestionAnswerOutcome = {
|
|
402
|
+
ok: true;
|
|
403
|
+
session: string | null;
|
|
404
|
+
} | {
|
|
405
|
+
ok: false;
|
|
406
|
+
reason: QuestionAnswerRefusal;
|
|
407
|
+
};
|
|
408
|
+
/**
|
|
409
|
+
* W783: one still-answerable question as the recovery endpoint reports it.
|
|
410
|
+
* `expired`/`remaining_ms` are computed at READ time from `expires_at`, so the
|
|
411
|
+
* client never has to trust its own clock (§6.1).
|
|
412
|
+
*/
|
|
413
|
+
export interface PendingQuestionView {
|
|
414
|
+
id: string;
|
|
415
|
+
session: string | null;
|
|
416
|
+
questions: readonly AskUserQuestionItem[];
|
|
417
|
+
expires_at: number;
|
|
418
|
+
timeout_ms: number;
|
|
419
|
+
remaining_ms: number;
|
|
420
|
+
expired: boolean;
|
|
421
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RuntimeAdapter` — the ONE seam between the Studio host and the engine.
|
|
3
|
+
*
|
|
4
|
+
* P4 ships the Hono layer plus the data stores; the real runtime
|
|
5
|
+
* (`packages/runtime` compose + agent-loop + llm + tools) lands on a separate
|
|
6
|
+
* workstream. Everything the engine owns is therefore expressed here as an
|
|
7
|
+
* injected interface, and P4 verifies the contract against a fake adapter:
|
|
8
|
+
*
|
|
9
|
+
* POST /api/turn -> startTurn() / inject() (W513: a busy
|
|
10
|
+
* session takes an interjection instead
|
|
11
|
+
* of a 409; the turn is never restarted)
|
|
12
|
+
* GET /api/events -> attach(bus) (the adapter emits,
|
|
13
|
+
* one envelope per session)
|
|
14
|
+
* POST /api/cancel -> cancel(session)
|
|
15
|
+
* POST /api/clear -> clear(session)
|
|
16
|
+
* POST /api/sessions/{id}/activate -> ensureSession(id) (W513: never 409)
|
|
17
|
+
* POST /api/sessions/{id}/compact -> compact(session)
|
|
18
|
+
* GET /api/status -> statusline(session?) + isBusy(session?)
|
|
19
|
+
* GET /api/tools -> tools()
|
|
20
|
+
* GET /api/sessions/{id}/context -> sessionContext(session) (W725)
|
|
21
|
+
* GET+POST /api/config -> profile() / configure(patch)
|
|
22
|
+
* POST /api/worker/{spawn,send} -> workerSpawn() / workerSend()
|
|
23
|
+
* GET /api/worker/status -> workerStatus(wid?)
|
|
24
|
+
* GET /api/sessions (worker rows) -> workerSessions()
|
|
25
|
+
* GET /api/sessions/worker:<sid>/… -> workerMessages(sid)
|
|
26
|
+
*
|
|
27
|
+
* W513 (session independence): busy, turn numbering, status/usage trackers and
|
|
28
|
+
* the session inbox are PER SESSION. `isBusy()` with no argument keeps the
|
|
29
|
+
* legacy "is anything running" reading for the handlers that guard process-wide
|
|
30
|
+
* operations; every session-scoped handler passes the target session id.
|
|
31
|
+
*
|
|
32
|
+
* Replacing the fake with the real runtime is a one-line change in
|
|
33
|
+
* `createStudioApp({ runtime })` — no handler changes, no route changes.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* W737: the busy-slot error is part of the ENGINE contract, so it has exactly
|
|
37
|
+
* one definition — `packages/runtime/src/errors.ts`. It is imported (never
|
|
38
|
+
* redefined) here and re-exported, so every studio-side import of
|
|
39
|
+
* `TurnBusyError` resolves to the very class object the real engine throws.
|
|
40
|
+
*/
|
|
41
|
+
import { TurnBusyError } from "@celestea/runtime";
|
|
42
|
+
/** Verbatim engine error text (`{e}` placeholders). */
|
|
43
|
+
export class EngineError extends Error {
|
|
44
|
+
kind = "engine";
|
|
45
|
+
constructor(message) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = "EngineError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Thrown by `startTurn` when the single-concurrency slot is occupied, and by
|
|
52
|
+
* `clear` while the target session's turn is in flight (409).
|
|
53
|
+
*
|
|
54
|
+
* W737: SINGLE SOURCE — `@celestea/runtime`'s `errors.ts` (`StudioError`
|
|
55
|
+
* subclass: `status: 409`, `kind: "turn_busy"`). This file used to declare a
|
|
56
|
+
* second, `extends Error` copy; the handlers branched on that copy, so the real
|
|
57
|
+
* engine's error failed `instanceof` and the busy race surfaced as a 500
|
|
58
|
+
* instead of a 409 / an interjection. Only the fake adapter threw the copy,
|
|
59
|
+
* which is what kept the contract tests green. Do not redeclare it here.
|
|
60
|
+
*/
|
|
61
|
+
export { TurnBusyError };
|
|
62
|
+
/** Thrown when the live-session / concurrent-turn cap is reached (503). */
|
|
63
|
+
export class CapacityError extends Error {
|
|
64
|
+
kind = "capacity";
|
|
65
|
+
/** Seconds the client should wait before retrying (Retry-After). */
|
|
66
|
+
retryAfterSeconds;
|
|
67
|
+
constructor(message, retryAfterSeconds = 1) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = "CapacityError";
|
|
70
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** `registry.schemas()` row -> the two-field view the host exposes (W729). */
|
|
74
|
+
export function toolSpecView(spec) {
|
|
75
|
+
return { name: spec.name, description: spec.description };
|
|
76
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W815-N2: a minimal promise-chain mutex for the Studio's HOT-APPLY writes.
|
|
3
|
+
*
|
|
4
|
+
* Both `POST /api/prompts` (registry persist -> engine configure -> rollback)
|
|
5
|
+
* and `POST /api/config` (host overrides -> engine configure) read a snapshot,
|
|
6
|
+
* mutate shared state and `await` the engine; without a lock two concurrent
|
|
7
|
+
* writers interleave their snapshot/rollback windows and the earlier failure can
|
|
8
|
+
* erase the later success. Every task runs strictly after the previous one
|
|
9
|
+
* settled (resolved OR rejected), so a caller still observes its own outcome
|
|
10
|
+
* while the chain stays alive.
|
|
11
|
+
*/
|
|
12
|
+
export declare class SerialQueue {
|
|
13
|
+
private tail;
|
|
14
|
+
/** Run `task` after every previously enqueued task has settled. */
|
|
15
|
+
run<T>(task: () => Promise<T>): Promise<T>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W815-N2: a minimal promise-chain mutex for the Studio's HOT-APPLY writes.
|
|
3
|
+
*
|
|
4
|
+
* Both `POST /api/prompts` (registry persist -> engine configure -> rollback)
|
|
5
|
+
* and `POST /api/config` (host overrides -> engine configure) read a snapshot,
|
|
6
|
+
* mutate shared state and `await` the engine; without a lock two concurrent
|
|
7
|
+
* writers interleave their snapshot/rollback windows and the earlier failure can
|
|
8
|
+
* erase the later success. Every task runs strictly after the previous one
|
|
9
|
+
* settled (resolved OR rejected), so a caller still observes its own outcome
|
|
10
|
+
* while the chain stays alive.
|
|
11
|
+
*/
|
|
12
|
+
export class SerialQueue {
|
|
13
|
+
tail = Promise.resolve();
|
|
14
|
+
/** Run `task` after every previously enqueued task has settled. */
|
|
15
|
+
run(task) {
|
|
16
|
+
const result = this.tail.then(task, task);
|
|
17
|
+
// A rejected task must not poison the chain: swallow it on the tail only —
|
|
18
|
+
// the caller gets the real rejection through `result`.
|
|
19
|
+
this.tail = result.then(() => undefined, () => undefined);
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `startStudioServer` (H): the ONE reusable server bootstrap.
|
|
3
|
+
*
|
|
4
|
+
* `apps/studio/src/main.ts` (source checkout) and the `celestea` CLI
|
|
5
|
+
* (`packages/cli`) both call this, so the startup log lines and the W742
|
|
6
|
+
* graceful-teardown order exist in exactly one place. `main.ts` stays a thin
|
|
7
|
+
* wrapper that pins the historical source defaults; the CLI passes its own
|
|
8
|
+
* flags.
|
|
9
|
+
*
|
|
10
|
+
* Teardown order is the W742 contract (do not reorder):
|
|
11
|
+
* signal -> stop accepting traffic (bounded grace) -> flush grants audit ->
|
|
12
|
+
* engine down (workers settled, logs closed) -> let the loop drain.
|
|
13
|
+
*/
|
|
14
|
+
import { type StudioAppOptions } from "./app.js";
|
|
15
|
+
/** Env knob: drain window before leftover sockets are cut. */
|
|
16
|
+
export declare const ENV_DRAIN_MS = "CELESTEA_SHUTDOWN_DRAIN_MS";
|
|
17
|
+
/** Env knob: ceiling for the whole teardown. */
|
|
18
|
+
export declare const ENV_TEARDOWN_MS = "CELESTEA_SHUTDOWN_TIMEOUT_MS";
|
|
19
|
+
export interface StudioServerOptions extends StudioAppOptions {
|
|
20
|
+
port: number;
|
|
21
|
+
hostname: string;
|
|
22
|
+
/** Emit the startup banner (default true; tests may silence it). */
|
|
23
|
+
log?: boolean;
|
|
24
|
+
/** Called once the listener is up, with the ACTUAL bound port. */
|
|
25
|
+
onListening?: (info: {
|
|
26
|
+
port: number;
|
|
27
|
+
hostname: string;
|
|
28
|
+
endpointCount: number;
|
|
29
|
+
}) => void;
|
|
30
|
+
}
|
|
31
|
+
export interface StudioServerHandle {
|
|
32
|
+
port: number;
|
|
33
|
+
hostname: string;
|
|
34
|
+
endpointCount: number;
|
|
35
|
+
/** Resolves once the socket is bound (the ACTUAL address is then known). */
|
|
36
|
+
listening: Promise<{
|
|
37
|
+
port: number;
|
|
38
|
+
hostname: string;
|
|
39
|
+
}>;
|
|
40
|
+
/** Graceful stop (idempotent); resolves once the loop may drain. */
|
|
41
|
+
stop(signal: string): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
/** Boot the studio HTTP server; returns a handle whose `stop` is the teardown. */
|
|
44
|
+
export declare function startStudioServer(options: StudioServerOptions): StudioServerHandle;
|