@wrongstack/webui-server 0.306.4 → 0.307.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/dist/index.js +3567 -2746
- package/dist/protocol/client-integrations.d.ts +1 -1
- package/dist/protocol/client-operations.d.ts +1 -1
- package/dist/protocol/client-workspace.d.ts +1 -1
- package/dist/protocol/index.js +14 -0
- package/dist/protocol/registry.d.ts +2 -2
- package/dist/protocol/server-integrations.d.ts +1 -1
- package/dist/protocol/server-workspace.d.ts +1 -1
- package/dist/server/collaboration-ws-handler.d.ts +18 -0
- package/dist/server/connections/collector.d.ts +15 -0
- package/dist/server/connections/helpers.d.ts +18 -0
- package/dist/server/connections/index.d.ts +5 -0
- package/dist/server/connections/service-actions.d.ts +16 -0
- package/dist/server/connections/types.d.ts +55 -0
- package/dist/server/connections-health-route.d.ts +5 -66
- package/dist/server/entry.js +3525 -2716
- package/dist/server/file-handlers.d.ts +37 -0
- package/dist/server/goal-ws-handler.d.ts +16 -0
- package/dist/server/http-server/api-router.d.ts +22 -0
- package/dist/server/http-server/index.d.ts +6 -0
- package/dist/server/http-server/security-helpers.d.ts +24 -0
- package/dist/server/http-server/static-file-handler.d.ts +10 -0
- package/dist/server/http-server.d.ts +28 -87
- package/dist/server/index.d.ts +3 -2
- package/dist/server/network-info.d.ts +2 -3
- package/dist/server/project-watcher.d.ts +31 -0
- package/dist/server/prompts-handlers.d.ts +2 -0
- package/dist/server/sdd-board-ws-handler.d.ts +9 -0
- package/dist/server/sdd-wizard-ws-handler.d.ts +1 -0
- package/dist/server/setup-events-pattern-handlers.d.ts +10 -0
- package/dist/server/setup-events-status-watcher.d.ts +1 -1
- package/dist/server/setup-events-subagent-handlers.d.ts +14 -0
- package/dist/server/setup-events-tool-handlers.d.ts +18 -0
- package/dist/server/setup-events.d.ts +1 -2
- package/dist/server/shell-open.d.ts +9 -8
- package/dist/server/start-webui-companion.d.ts +16 -0
- package/dist/server/start-webui-credential-watcher.d.ts +13 -0
- package/dist/server/start-webui-remediation.d.ts +14 -0
- package/dist/server/start-webui-shutdown.d.ts +43 -0
- package/dist/server/start-webui-todos.d.ts +13 -0
- package/dist/server/start-webui.d.ts +2 -29
- package/dist/server/techstack-handlers.d.ts +2 -2
- package/dist/server/worktree-ws-handler.d.ts +7 -0
- package/package.json +13 -13
|
@@ -27,6 +27,11 @@ export declare function handleFilesTree(ws: WebSocket, msg: unknown, projectRoot
|
|
|
27
27
|
* `{ type: 'files.read', payload: { filePath, content } }`.
|
|
28
28
|
*/
|
|
29
29
|
export declare function handleFilesRead(ws: WebSocket, msg: unknown, projectRoot: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Extract an AST-based skeleton for a file.
|
|
32
|
+
* Responds with `{ type: 'files.skeleton_result', payload: { filePath, lang, skeleton, stats } }`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function handleFilesSkeleton(ws: WebSocket, msg: unknown, projectRoot: string): Promise<void>;
|
|
30
35
|
/**
|
|
31
36
|
* Write file content back to disk (atomic write via tmp + rename).
|
|
32
37
|
*
|
|
@@ -42,4 +47,36 @@ export declare function handleFilesWrite(ws: WebSocket, msg: unknown, projectRoo
|
|
|
42
47
|
* `{ type: 'files.list', payload: { files } }`.
|
|
43
48
|
*/
|
|
44
49
|
export declare function handleFilesList(ws: WebSocket, msg: unknown, projectRoot: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Create a new file or directory inside the project root.
|
|
52
|
+
*
|
|
53
|
+
* Guards against path traversal via `resolveFileInsideProject`. Rejects
|
|
54
|
+
* if the target already exists. Responds with
|
|
55
|
+
* `{ type: 'files.created', payload: { filePath, success } }`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function handleFilesCreate(ws: WebSocket, msg: unknown, projectRoot: string): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Delete a file or directory inside the project root.
|
|
60
|
+
*
|
|
61
|
+
* Guards against path traversal via `resolveFileInsideProject`. Directories
|
|
62
|
+
* require `recursive: true` to delete non-empty contents. Responds with
|
|
63
|
+
* `{ type: 'files.deleted', payload: { filePath, success } }`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function handleFilesDelete(ws: WebSocket, msg: unknown, projectRoot: string): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Rename or move a file/directory within the project root.
|
|
68
|
+
*
|
|
69
|
+
* Guards both source and destination via `resolveFileInsideProject`.
|
|
70
|
+
* Rejects if the destination already exists or the source doesn't.
|
|
71
|
+
* Responds with `{ type: 'files.renamed', payload: { oldPath, newPath, success } }`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function handleFilesRename(ws: WebSocket, msg: unknown, projectRoot: string): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Move a file/directory into a destination directory within the project root.
|
|
76
|
+
*
|
|
77
|
+
* Guards both source and destination via `resolveFileInsideProject`.
|
|
78
|
+
* The file keeps its basename — it is placed inside `destDir`.
|
|
79
|
+
* Responds with `{ type: 'files.moved', payload: { srcPath, destPath, success } }`.
|
|
80
|
+
*/
|
|
81
|
+
export declare function handleFilesMove(ws: WebSocket, msg: unknown, projectRoot: string): Promise<void>;
|
|
45
82
|
//# sourceMappingURL=file-handlers.d.ts.map
|
|
@@ -37,6 +37,14 @@ export declare class GoalWebSocketHandler {
|
|
|
37
37
|
private store;
|
|
38
38
|
private clients;
|
|
39
39
|
private broadcastInterval;
|
|
40
|
+
/**
|
|
41
|
+
* Change-detection state for the 2s broadcast tick: a cheap content
|
|
42
|
+
* fingerprint of the graph and the last serialized progress payload, so an
|
|
43
|
+
* idle run costs one small string build per tick instead of a full
|
|
44
|
+
* buildState + serialize + fan-out every 2 seconds.
|
|
45
|
+
*/
|
|
46
|
+
private lastGraphFingerprint;
|
|
47
|
+
private lastProgressJson;
|
|
40
48
|
/** Aborts in-flight task agents AND the planning turn when the run is stopped. */
|
|
41
49
|
private abort;
|
|
42
50
|
/** Per-assessment AbortController so a newer assessment can abort the prior
|
|
@@ -129,6 +137,14 @@ export declare class GoalWebSocketHandler {
|
|
|
129
137
|
private startBroadcast;
|
|
130
138
|
private stopBroadcast;
|
|
131
139
|
private broadcastState;
|
|
140
|
+
/**
|
|
141
|
+
* Cheap content fingerprint covering exactly what `buildState` renders:
|
|
142
|
+
* graph identity/flags plus per-phase status, timestamps, assignees, and
|
|
143
|
+
* task status counts with the newest task update. Any mutation that would
|
|
144
|
+
* change the projection changes this string, so the 2s tick can skip the
|
|
145
|
+
* full buildState + serialize + fan-out while the graph is idle.
|
|
146
|
+
*/
|
|
147
|
+
private graphFingerprint;
|
|
132
148
|
private buildState;
|
|
133
149
|
private sendState;
|
|
134
150
|
private broadcast;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as http from 'node:http';
|
|
2
|
+
import { type TechStackEvent } from '../techstack-handlers.js';
|
|
3
|
+
export interface ApiRouterDeps {
|
|
4
|
+
globalRoot?: string | undefined;
|
|
5
|
+
projectRoot?: string | undefined;
|
|
6
|
+
indexDir?: string | undefined;
|
|
7
|
+
intakeService?: import('@wrongstack/requirement-intake').RequirementIntakeService | undefined;
|
|
8
|
+
onFleetPing?: (() => void) | undefined;
|
|
9
|
+
watcherMetrics?: import('../setup-events.js').FileWatcherMetrics | undefined;
|
|
10
|
+
onTechStackEvent?: ((event: TechStackEvent) => void) | undefined;
|
|
11
|
+
getLlm?: (() => {
|
|
12
|
+
provider: import('@wrongstack/core/types').Provider;
|
|
13
|
+
model: string;
|
|
14
|
+
} | undefined) | undefined;
|
|
15
|
+
executePackageOperation?: import('../techstack-handlers.js').TechStackHandlerDeps['executePackageOperation'];
|
|
16
|
+
}
|
|
17
|
+
export declare function handleApiRoutes(req: http.IncomingMessage, res: http.ServerResponse, url: URL, deps: ApiRouterDeps, requireAccessToken: boolean, accessTokenOk: boolean, getTechStackRuntime: () => Promise<{
|
|
18
|
+
store: import('@wrongstack/techstack').TechStackStore;
|
|
19
|
+
engine: import('@wrongstack/techstack').TechStackEngine;
|
|
20
|
+
runningJobs: Map<string, AbortController>;
|
|
21
|
+
}>): Promise<boolean>;
|
|
22
|
+
//# sourceMappingURL=api-router.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type * as http from 'node:http';
|
|
2
|
+
export declare const MIME_TYPES: Record<string, string>;
|
|
3
|
+
export declare function escapeHtmlAttr(value: string): string;
|
|
4
|
+
export declare function injectWsConfig(html: string, opts: {
|
|
5
|
+
publicWsUrl?: string | undefined;
|
|
6
|
+
}): string;
|
|
7
|
+
export declare function firstHeader(value: string | string[] | undefined): string | undefined;
|
|
8
|
+
export declare const WS_TOKEN_COOKIE = "ws_token";
|
|
9
|
+
export declare const WS_TOKEN_COOKIE_SECURE = "__Host-ws_token";
|
|
10
|
+
export declare function wsTokenCookie(token: string, secure: boolean): string;
|
|
11
|
+
export declare function setAuthCookieHeaders(res: http.ServerResponse, token: string, secure: boolean): void;
|
|
12
|
+
export declare function setStaticSecurityHeaders(res: http.ServerResponse): void;
|
|
13
|
+
export declare function requestToken(req: http.IncomingMessage, url: URL, opts?: {
|
|
14
|
+
allowQuery?: boolean;
|
|
15
|
+
}): string | undefined;
|
|
16
|
+
export declare function isLoopbackPeer(req: http.IncomingMessage): boolean;
|
|
17
|
+
export declare function formatCspHostname(hostname: string): string;
|
|
18
|
+
export declare function cspSourceFromUrl(rawUrl: string): string | undefined;
|
|
19
|
+
export declare const EXTRA_SCRIPT_SOURCES: readonly string[];
|
|
20
|
+
export declare function buildCspHeader(publicWsUrl?: string | undefined, host?: string, port?: number): string;
|
|
21
|
+
export declare function isInsideDist(candidate: string, distDir: string): boolean;
|
|
22
|
+
export declare function decodeSessionId(segment: string): string;
|
|
23
|
+
export declare function strictDecodeParam(segment: string, res: http.ServerResponse): string | null;
|
|
24
|
+
//# sourceMappingURL=security-helpers.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type * as http from 'node:http';
|
|
2
|
+
export declare function handleStaticFileRequest(_req: http.IncomingMessage, res: http.ServerResponse, distDir: string, url: URL, opts: {
|
|
3
|
+
publicWsUrl?: string | undefined;
|
|
4
|
+
host: string;
|
|
5
|
+
}, port: number, shouldSetAuthCookie: boolean): Promise<void>;
|
|
6
|
+
export declare function handleSpaFallback(res: http.ServerResponse, distDir: string, opts: {
|
|
7
|
+
publicWsUrl?: string | undefined;
|
|
8
|
+
host: string;
|
|
9
|
+
}, port: number): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=static-file-handler.d.ts.map
|
|
@@ -1,6 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static-file HTTP server for the WebUI / SimpleUI React frontends.
|
|
3
|
+
*
|
|
4
|
+
* Design:
|
|
5
|
+
* - **Single port — shared HTTP + WebSocket.** A single port serves both the
|
|
6
|
+
* static frontend and the WebSocket protocol. See the port-utils module.
|
|
7
|
+
* - **CSP**: `connect-src 'self'` covers the core same-origin case. For loopback
|
|
8
|
+
* binds (127.0.0.1 / localhost) we also add explicit `ws://`/`wss://` entries
|
|
9
|
+
* so browsers that strictly separate `ws:` from `http:` in CSP `connect-src`
|
|
10
|
+
* matching still allow WS upgrades. These loopback entries are safe because
|
|
11
|
+
* they only open connections to the local machine. Tunnel/proxy setups use
|
|
12
|
+
* `publicWsUrl` for the single external origin.
|
|
13
|
+
* - **Path-traversal guard**: `path.join` alone does NOT prevent
|
|
14
|
+
* `%2e%2e%2f` escapes (the `URL` constructor decodes percent-encoding
|
|
15
|
+
* before we see the path). We re-`resolve` the candidate and verify it
|
|
16
|
+
* stays under `distDir`.
|
|
17
|
+
* - **Access auth**: on non-loopback binds, all HTTP routes require the same
|
|
18
|
+
* shared token as the WS upgrade, accepted via `?token=...`, `X-WS-Token`,
|
|
19
|
+
* or the `ws_token` HttpOnly cookie. This protects the React UI and the
|
|
20
|
+
* `/api/*` control/read endpoints when `WS_HOST=0.0.0.0`.
|
|
21
|
+
*
|
|
22
|
+
* Extracted from `index.ts` so the static-serve concern can be tested
|
|
23
|
+
* with a tiny fake `distDir` and asserted on path-traversal, MIME
|
|
24
|
+
* matching, and CSP header presence.
|
|
25
|
+
*/
|
|
1
26
|
import * as http from 'node:http';
|
|
2
27
|
import type { FileWatcherMetrics } from './setup-events.js';
|
|
3
|
-
import {
|
|
28
|
+
import type { TechStackEvent } from './techstack-handlers.js';
|
|
29
|
+
import { buildCspHeader, decodeSessionId, injectWsConfig, isInsideDist, requestToken, WS_TOKEN_COOKIE, WS_TOKEN_COOKIE_SECURE } from './http-server/security-helpers.js';
|
|
30
|
+
export { buildCspHeader, decodeSessionId, injectWsConfig, isInsideDist, requestToken, WS_TOKEN_COOKIE, WS_TOKEN_COOKIE_SECURE, };
|
|
4
31
|
export interface CreateHttpServerOptions {
|
|
5
32
|
/** Port to listen on. Defaults to 3456 (or the `PORT` env var). */
|
|
6
33
|
port?: number | undefined;
|
|
@@ -95,92 +122,6 @@ export interface CreateHttpServerOptions {
|
|
|
95
122
|
*/
|
|
96
123
|
intakeService?: import('@wrongstack/requirement-intake').RequirementIntakeService | undefined;
|
|
97
124
|
}
|
|
98
|
-
export declare function injectWsConfig(html: string, opts: {
|
|
99
|
-
publicWsUrl?: string | undefined;
|
|
100
|
-
}): string;
|
|
101
|
-
/**
|
|
102
|
-
* WS-107: the auth cookie never carried `Secure`, on the stated grounds that
|
|
103
|
-
* "the dev server is plain HTTP on loopback". True for the default bind — and
|
|
104
|
-
* wrong for the deployment this server explicitly supports, where a tunnel
|
|
105
|
-
* terminates TLS in front of it (`publicWsUrl: wss://…`). There the browser is
|
|
106
|
-
* on HTTPS, so a cookie set without `Secure` is one the browser will also
|
|
107
|
-
* attach to a plaintext request to the same host: a downgrade or a stray
|
|
108
|
-
* `http://` link leaks the token that authenticates the whole control plane.
|
|
109
|
-
*
|
|
110
|
-
* So the flag follows the deployment. On an HTTPS-fronted server the cookie is
|
|
111
|
-
* additionally `__Host-`-prefixed, which browsers only accept when it is
|
|
112
|
-
* Secure, Path=/, and Domain-less — the same hardening the HQ server adopted.
|
|
113
|
-
* On loopback HTTP neither is usable (a `Secure` cookie would simply not be
|
|
114
|
-
* sent), so the plain name is kept there.
|
|
115
|
-
*/
|
|
116
|
-
export declare const WS_TOKEN_COOKIE = "ws_token";
|
|
117
|
-
export declare const WS_TOKEN_COOKIE_SECURE = "__Host-ws_token";
|
|
118
|
-
/**
|
|
119
|
-
* Resolve the access token presented on an HTTP request.
|
|
120
|
-
*
|
|
121
|
-
* WS-108: the `?token=` query source was accepted on EVERY route regardless of
|
|
122
|
-
* bind. On a tunnel that is the C-598 exposure the WS path already closed —
|
|
123
|
-
* the token reaches reverse-proxy access logs, browser history, and any
|
|
124
|
-
* `Referer` the page emits to a third-party origin. The header and cookie
|
|
125
|
-
* sources have none of those channels.
|
|
126
|
-
*
|
|
127
|
-
* `allowQuery` is therefore opt-in and reserved for the two places the token
|
|
128
|
-
* legitimately rides the URL: `/ws-auth`, whose entire job is to exchange it
|
|
129
|
-
* for the cookie, and the HTML page load the operator opens from the printed
|
|
130
|
-
* access URL. Everywhere else it is refused off-loopback — and when refused,
|
|
131
|
-
* resolution falls through to the header and cookie rather than returning
|
|
132
|
-
* early, so a client that sent both still authenticates.
|
|
133
|
-
*/
|
|
134
|
-
export declare function requestToken(req: http.IncomingMessage, url: URL, opts?: {
|
|
135
|
-
allowQuery?: boolean;
|
|
136
|
-
}): string | undefined;
|
|
137
|
-
/**
|
|
138
|
-
* Build the Content-Security-Policy value for the WebUI.
|
|
139
|
-
*
|
|
140
|
-
* Adds explicit `ws://`/`wss://` entries for loopback addresses (`127.0.0.1`,
|
|
141
|
-
* `localhost`) so `connect-src` covers WebSocket even in strict CSP
|
|
142
|
-
* implementations that distinguish `ws:` from `http:` origins.
|
|
143
|
-
*
|
|
144
|
-
* @param publicWsUrl - Optional public-facing WS URL (tunnel/reverse-proxy).
|
|
145
|
-
* When set, this origin is added to `connect-src` as a `ws://`/`wss://` entry.
|
|
146
|
-
* @param host - The server bind host. When it matches a known loopback address
|
|
147
|
-
* (`127.0.0.1`, `::1`, `[::1]`, or `localhost`), explicit `ws://`/`wss://`
|
|
148
|
-
* entries for `127.0.0.1` and `localhost` are added to `connect-src`.
|
|
149
|
-
* IPv6 loopback (`[::1]`) is excluded — it produces an invalid CSP source
|
|
150
|
-
* and is covered by `'self'` (CSP maps ws:→http:, wss:→https:).
|
|
151
|
-
* @param port - The server listen port. Defaults to `3456`. Unnecessary when
|
|
152
|
-
* only publicWsUrl is used (no loopback branch).
|
|
153
|
-
*/
|
|
154
|
-
export declare function buildCspHeader(publicWsUrl?: string | undefined, host?: string, port?: number): string;
|
|
155
|
-
/**
|
|
156
|
-
* Returns true when `candidate` (a fully-resolved absolute path) lies
|
|
157
|
-
* strictly inside `distDir` (or equals it). Used to reject path-traversal
|
|
158
|
-
* attempts after `path.resolve` has normalised any `..` segments.
|
|
159
|
-
*
|
|
160
|
-
* Exported so tests can assert the guard's contract without having to
|
|
161
|
-
* also defeat the WHATWG URL normaliser (which strips `..` from the
|
|
162
|
-
* path string *before* the request even reaches the server, making a
|
|
163
|
-
* black-box test via fetch impossible).
|
|
164
|
-
*/
|
|
165
|
-
export declare function isInsideDist(candidate: string, distDir: string): boolean;
|
|
166
|
-
/**
|
|
167
|
-
* Decode a `:id` path segment captured by the `/api/sessions/:id/*` routes.
|
|
168
|
-
*
|
|
169
|
-
* Session ids are `YYYY-MM-DD/sess_<ULID>` — they contain a literal
|
|
170
|
-
* `/`. The frontend builds the URL with `encodeURIComponent(sessionId)`, so
|
|
171
|
-
* that slash arrives as `%2F`. The route regex `([^/]+)` correctly captures
|
|
172
|
-
* the whole percent-encoded segment (there is no real `/` in `%2F`), but the
|
|
173
|
-
* SessionRegistry is keyed by the *decoded* id — so the capture must be
|
|
174
|
-
* `decodeURIComponent`d before lookup. Without this, every
|
|
175
|
-
* `/api/sessions/:id/{events,message,agents}` request 404s (the registry has
|
|
176
|
-
* `2026-…/…` but we looked up `2026-…%2F…`), which broke the Fleet HQ
|
|
177
|
-
* watch-stream and the steer-message composer.
|
|
178
|
-
*
|
|
179
|
-
* Malformed percent-encoding (a lone `%`) makes `decodeURIComponent` throw;
|
|
180
|
-
* fall back to the raw segment so the caller still gets a clean 404 rather
|
|
181
|
-
* than a 500.
|
|
182
|
-
*/
|
|
183
|
-
export declare function decodeSessionId(segment: string): string;
|
|
184
125
|
/**
|
|
185
126
|
* Create the static-file HTTP server. Returns the `http.Server` (not
|
|
186
127
|
* listening yet) so the caller can attach to a `shutdown()` hook and
|
package/dist/server/index.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export { collectConnectionsHealth, type ConnectionHealthService, type Connection
|
|
|
30
30
|
export { type CustomContextMode, type CustomModeStore, createCustomModeStore, } from './custom-context-modes.js';
|
|
31
31
|
export { type DesignContext, handleDesignList, handleDesignMaterialize, handleDesignSet, handleDesignState, handleDesignSwap, handleDesignTune, handleDesignUse, handleDesignVerify, } from './design-handlers.js';
|
|
32
32
|
export { createEternalSubscription, type EternalBroadcast, type EternalSubscribe, type EternalSubscription, } from './eternal-iteration-broadcast.js';
|
|
33
|
-
export { handleFilesList, handleFilesRead, handleFilesTree, handleFilesWrite, } from './file-handlers.js';
|
|
33
|
+
export { handleFilesCreate, handleFilesDelete, handleFilesList, handleFilesMove, handleFilesRead, handleFilesRename, handleFilesSkeleton, handleFilesTree, handleFilesWrite, } from './file-handlers.js';
|
|
34
34
|
export { isHiddenEntry, rankFiles, SKIP_DIRS } from './file-picker.js';
|
|
35
35
|
export { handleGitChanges, handleGitDiff, handleGitInfo } from './git-handlers.js';
|
|
36
36
|
export { handleGoalGet } from './goal-handlers.js';
|
|
@@ -90,7 +90,7 @@ export type { ProjectRouteHandlers } from './project-routes.js';
|
|
|
90
90
|
export { handleProjectRoute } from './project-routes.js';
|
|
91
91
|
export { createProjectHandlers, type ProjectHandlersContext } from './project-handlers.js';
|
|
92
92
|
export { ensureProjectDataDir, loadManifest, projectsJsonPath, saveManifest, touchProjectInManifest, } from './projects-manifest.js';
|
|
93
|
-
export { handlePromptsContent, handlePromptsCreate, handlePromptsFavorite, handlePromptsList, handlePromptsRecent, handlePromptsSearch, handlePromptsUsed, type PromptsContext, } from './prompts-handlers.js';
|
|
93
|
+
export { handlePromptsContent, handlePromptsCreate, handlePromptsFavorite, handlePromptsJournal, handlePromptsList, handlePromptsRecent, handlePromptsSearch, handlePromptsUsed, type PromptsContext, } from './prompts-handlers.js';
|
|
94
94
|
export { loadSavedProviders, saveProviders } from './provider-config-io.js';
|
|
95
95
|
export { createProviderConfigIO } from './provider-config-standalone.js';
|
|
96
96
|
export { createProviderHandlers, createProviderOperations, type ProviderOperationsDeps, type ProviderPersistence, probeModelDescriptors, projectSavedProviders, type SavedProviderView, } from './provider-handlers.js';
|
|
@@ -115,6 +115,7 @@ export { handleShellOpen, type ShellOpenOptions, type ShellOpenRequest, type She
|
|
|
115
115
|
export { handleSkillsContent, handleSkillsCreate, handleSkillsEdit, handleSkillsExport, handleSkillsInstall, handleSkillsList, handleSkillsUninstall, handleSkillsUpdate, type SkillsContext, } from './skills-handlers.js';
|
|
116
116
|
export { handleSpecsRoute, type SpecsRouteHandlers } from './specs-routes.js';
|
|
117
117
|
export { SpecsWebSocketHandler } from './specs-ws-handler.js';
|
|
118
|
+
export { startProjectWatcher } from './project-watcher.js';
|
|
118
119
|
export { startWebUI } from './start-webui.js';
|
|
119
120
|
export { TerminalWebSocketHandler } from './terminal-ws-handler.js';
|
|
120
121
|
export { type ContextBreakdown, estimateContextBreakdown, estimateTokens, type MessageTokenEntry, messagePreview, messageTokens, stringifyContent, type ToolTokenEntry, } from './token-estimator.js';
|
|
@@ -37,13 +37,12 @@ export declare function getExternalAddresses(getInterfaces?: () => NodeJS.Dict<o
|
|
|
37
37
|
* Format the extra access-URL lines for a wildcard bind.
|
|
38
38
|
*
|
|
39
39
|
* Returns a list of indented, ready-to-print lines showing the external
|
|
40
|
-
* IPs and their
|
|
41
|
-
*
|
|
40
|
+
* IPs and their access URLs. Authentication tokens are deliberately excluded
|
|
41
|
+
* because these lines are written to terminal and session logs.
|
|
42
42
|
*/
|
|
43
43
|
export declare function formatExternalAccessUrls(opts: {
|
|
44
44
|
bindHost: string;
|
|
45
45
|
port: number;
|
|
46
|
-
token?: string | undefined;
|
|
47
46
|
publicUrl?: string | undefined;
|
|
48
47
|
getInterfaces?: () => NodeJS.Dict<os.NetworkInterfaceInfo[]>;
|
|
49
48
|
}): string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project source-tree watcher — broadcasts `files.tree.changed` when
|
|
3
|
+
* files are created, modified, or deleted inside the project root, so
|
|
4
|
+
* the WebUI file explorer refreshes its tree without manual navigation.
|
|
5
|
+
*
|
|
6
|
+
* Uses Node's native `fs.watch` with `recursive: true` (supported on
|
|
7
|
+
* Windows and macOS; Linux support landed in Node 22). Events are
|
|
8
|
+
* debounced (400ms) to coalesce bursts from build tools, git
|
|
9
|
+
* checkouts, and agent writes. Heavyweight directories
|
|
10
|
+
* (`node_modules`, `.git`, `dist`, …) are ignored to avoid event
|
|
11
|
+
* storms — the same `SKIP_DIRS` set the tree builder uses.
|
|
12
|
+
*
|
|
13
|
+
* The watcher is `persistent: false` and `unref`'d on each handle so
|
|
14
|
+
* it does not keep the process alive. The returned disposer closes
|
|
15
|
+
* all native handles.
|
|
16
|
+
*/
|
|
17
|
+
import type { ConnectedClient, WSServerMessage } from './types.js';
|
|
18
|
+
export interface ProjectWatcherDeps {
|
|
19
|
+
/** Absolute project root to watch. */
|
|
20
|
+
projectRoot: string;
|
|
21
|
+
/** Broadcast a message to all connected WS clients. */
|
|
22
|
+
broadcast: (clients: Map<import('ws').WebSocket, ConnectedClient>, msg: WSServerMessage) => void;
|
|
23
|
+
/** Live client map (same reference the rest of setupEvents uses). */
|
|
24
|
+
clients: Map<import('ws').WebSocket, ConnectedClient>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Start watching `projectRoot` for filesystem changes. Returns a
|
|
28
|
+
* disposer that closes all native watch handles.
|
|
29
|
+
*/
|
|
30
|
+
export declare function startProjectWatcher(deps: ProjectWatcherDeps): () => void;
|
|
31
|
+
//# sourceMappingURL=project-watcher.d.ts.map
|
|
@@ -33,6 +33,8 @@ export declare function handlePromptsCreate(ws: WSLike, ctx: PromptsContext, msg
|
|
|
33
33
|
export declare function handlePromptsUsed(ws: WSLike, ctx: PromptsContext, msg: unknown): Promise<void>;
|
|
34
34
|
/** Return recently-inserted prompt slugs (most-recent first) for the modal's Recent view. */
|
|
35
35
|
export declare function handlePromptsRecent(ws: WSLike, ctx: PromptsContext): Promise<void>;
|
|
36
|
+
/** Read the project's hierarchical prompt journal (`<projectRoot>/.wrongstack/prompts/`). */
|
|
37
|
+
export declare function handlePromptsJournal(ws: WSLike, msg: unknown, projectRoot: string): Promise<void>;
|
|
36
38
|
/** Minimal structural type for the ws.send sink (matches `ws` WebSocket). */
|
|
37
39
|
type WSLike = Parameters<typeof send>[0];
|
|
38
40
|
export {};
|
|
@@ -61,6 +61,15 @@ export declare class SddBoardWebSocketHandler {
|
|
|
61
61
|
private applyLifecycle;
|
|
62
62
|
dispose(): void;
|
|
63
63
|
private pollLatest;
|
|
64
|
+
/**
|
|
65
|
+
* Cheap freshness probe used before the full snapshot load. In kanban mode
|
|
66
|
+
* the workflow-state listing IS the snapshot payload (no cheaper probe
|
|
67
|
+
* exists), so fall through to the load — the post-load `updatedAt` guard
|
|
68
|
+
* still dedups the broadcast. In legacy-file mode the store index carries
|
|
69
|
+
* per-run `updatedAt`, letting an unchanged board skip the snapshot file
|
|
70
|
+
* entirely. Probe failures fall through to the load path.
|
|
71
|
+
*/
|
|
72
|
+
private hasNewerSnapshot;
|
|
64
73
|
private startPolling;
|
|
65
74
|
private stopPolling;
|
|
66
75
|
private sendCurrent;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EventBus } from '@wrongstack/core/kernel';
|
|
2
|
+
import type { WebSocket } from 'ws';
|
|
3
|
+
import type { ConnectedClient, WSServerMessage } from './types.js';
|
|
4
|
+
export declare function registerSetupEventsPatternHandlers(options: {
|
|
5
|
+
events: EventBus;
|
|
6
|
+
broadcast: (clients: Map<WebSocket, ConnectedClient>, msg: WSServerMessage) => void;
|
|
7
|
+
clients: Map<WebSocket, ConnectedClient>;
|
|
8
|
+
sessionPayload: <T extends Record<string, unknown>>(payload: T) => T;
|
|
9
|
+
}): Array<() => void>;
|
|
10
|
+
//# sourceMappingURL=setup-events-pattern-handlers.d.ts.map
|
|
@@ -8,7 +8,7 @@ export interface SetupEventsStatusWatcherDeps {
|
|
|
8
8
|
watcherMetrics?: FileWatcherMetrics | undefined;
|
|
9
9
|
clients: Map<WebSocket, ConnectedClient>;
|
|
10
10
|
broadcast: (clients: Map<WebSocket, ConnectedClient>, msg: WSServerMessage) => void;
|
|
11
|
-
on: <E extends EventName>(event: E, listener: Listener<E>) => void;
|
|
11
|
+
on: <E extends EventName>(event: E, listener: Listener<E>) => (() => void) | void;
|
|
12
12
|
isDisposed: () => boolean;
|
|
13
13
|
}
|
|
14
14
|
export declare function registerSetupEventsStatusWatcher(deps: SetupEventsStatusWatcherDeps): (() => void) | undefined;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context } from '@wrongstack/core/agent';
|
|
2
|
+
import type { EventName, Listener } from '@wrongstack/core/kernel';
|
|
3
|
+
import type { WebSocket } from 'ws';
|
|
4
|
+
import type { SetupEventProjection } from './setup-event-projection.js';
|
|
5
|
+
import type { ConnectedClient, WSServerMessage } from './types.js';
|
|
6
|
+
export declare function registerSetupEventsSubagentHandlers(options: {
|
|
7
|
+
on: <E extends EventName>(event: E, listener: Listener<E>) => void;
|
|
8
|
+
broadcast: (clients: Map<WebSocket, ConnectedClient>, msg: WSServerMessage) => void;
|
|
9
|
+
clients: Map<WebSocket, ConnectedClient>;
|
|
10
|
+
context: Context;
|
|
11
|
+
projection?: SetupEventProjection | undefined;
|
|
12
|
+
sessionPayload: <T extends Record<string, unknown>>(payload: T) => T;
|
|
13
|
+
}): void;
|
|
14
|
+
//# sourceMappingURL=setup-events-subagent-handlers.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Context } from '@wrongstack/core/agent';
|
|
2
|
+
import type { EventName, Listener } from '@wrongstack/core/kernel';
|
|
3
|
+
import type { SessionEventBridge } from '@wrongstack/core/storage';
|
|
4
|
+
import type { WebSocket } from 'ws';
|
|
5
|
+
import type { PendingConfirm } from './pending-confirms.js';
|
|
6
|
+
import type { SetupEventProjection } from './setup-event-projection.js';
|
|
7
|
+
import type { ConnectedClient, WSServerMessage } from './types.js';
|
|
8
|
+
export declare function registerSetupEventsToolHandlers(options: {
|
|
9
|
+
on: <E extends EventName>(event: E, listener: Listener<E>) => void;
|
|
10
|
+
broadcast: (clients: Map<WebSocket, ConnectedClient>, msg: WSServerMessage) => void;
|
|
11
|
+
clients: Map<WebSocket, ConnectedClient>;
|
|
12
|
+
context: Context;
|
|
13
|
+
pendingConfirms: Map<string, PendingConfirm>;
|
|
14
|
+
projection?: SetupEventProjection | undefined;
|
|
15
|
+
sessionPayload: <T extends Record<string, unknown>>(payload: T) => T;
|
|
16
|
+
appendForCurrentSession: (sessionId: string | undefined, event: Parameters<SessionEventBridge['append']>[0]) => void;
|
|
17
|
+
}): void;
|
|
18
|
+
//# sourceMappingURL=setup-events-tool-handlers.d.ts.map
|
|
@@ -50,8 +50,7 @@ export { statusProjectHashFromWatchFilename } from './setup-events-watcher.js';
|
|
|
50
50
|
* intervals, and flushes pending debounce timers. Callers MUST invoke it on
|
|
51
51
|
* shutdown — the watcher is `persistent: true` and the metrics interval is not
|
|
52
52
|
* `unref`'d, so without disposal they keep the process alive and leak across
|
|
53
|
-
* server restarts.
|
|
54
|
-
* `process.on('cleanup')` event that never fired.)
|
|
53
|
+
* server restarts.
|
|
55
54
|
*/
|
|
56
55
|
export declare function setupEvents(deps: SetupEventsDeps): () => void;
|
|
57
56
|
//# sourceMappingURL=setup-events.d.ts.map
|
|
@@ -17,20 +17,21 @@ export interface ShellOpenRequest {
|
|
|
17
17
|
*/
|
|
18
18
|
export declare function normalizeShellOpenTarget(target: string | undefined): ShellOpenTarget;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* Security options for `handleShellOpen`.
|
|
21
21
|
*
|
|
22
|
-
* `projectRoot
|
|
23
|
-
* directory
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* `projectRoot` is **required** — it confines the resolved path to the
|
|
23
|
+
* project directory. Without it a WebSocket client could open the OS
|
|
24
|
+
* file manager or terminal at ANY existing path on the system
|
|
25
|
+
* (/etc, ~/.ssh, C:\Windows). The TrustBoundary authorization may use
|
|
26
|
+
* a permissive compatibility policy, so path containment is enforced
|
|
27
|
+
* here as defense-in-depth.
|
|
27
28
|
*/
|
|
28
29
|
export interface ShellOpenOptions {
|
|
29
|
-
projectRoot
|
|
30
|
+
projectRoot: string;
|
|
30
31
|
}
|
|
31
32
|
export interface ShellOpenResult {
|
|
32
33
|
success: boolean;
|
|
33
34
|
message: string;
|
|
34
35
|
}
|
|
35
|
-
export declare function handleShellOpen(req: ShellOpenRequest, logger: Logger, options
|
|
36
|
+
export declare function handleShellOpen(req: ShellOpenRequest, logger: Logger, options: ShellOpenOptions): Promise<ShellOpenResult>;
|
|
36
37
|
//# sourceMappingURL=shell-open.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
/**
|
|
3
|
+
* Dual-stack / IPv6 companion listener.
|
|
4
|
+
*
|
|
5
|
+
* When the primary bind is IPv4-only, also try the IPv6 equivalent so
|
|
6
|
+
* peers using IPv6 can connect. Tailscale assigns both v4 (100.x.x.x)
|
|
7
|
+
* and v6 (fd7a:…) addresses; Chrome/Edge on Windows resolve `localhost`
|
|
8
|
+
* to [::1] before 127.0.0.1. Without the companion listener, a v4-only
|
|
9
|
+
* bind causes ECONNREFUSED for all IPv6 peers.
|
|
10
|
+
*
|
|
11
|
+
* When the primary bind is IPv6-only (::), try the IPv4 companion as a
|
|
12
|
+
* fallback for systems where `::` does not accept IPv4-mapped
|
|
13
|
+
* connections (net.ipv6.bindv6only=1, some Windows configs).
|
|
14
|
+
*/
|
|
15
|
+
export declare function setupCompanionServer(httpServer: http.Server, wsHost: string | undefined, httpPort: number): http.Server | null;
|
|
16
|
+
//# sourceMappingURL=start-webui-companion.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WebSocket } from 'ws';
|
|
2
|
+
import type { WebuiDeps, WebuiMutableState } from './routes.js';
|
|
3
|
+
import type { ConnectedClient } from './types.js';
|
|
4
|
+
export declare function setupWebuiCredentialWatcher(options: {
|
|
5
|
+
watchConfigPath: string;
|
|
6
|
+
vault: WebuiDeps['vault'];
|
|
7
|
+
logger: WebuiDeps['logger'];
|
|
8
|
+
state: WebuiMutableState;
|
|
9
|
+
deps: WebuiDeps;
|
|
10
|
+
clients: Map<WebSocket, ConnectedClient>;
|
|
11
|
+
updateAutoCompactionMaxContext: (provider: any) => Promise<void>;
|
|
12
|
+
}): (() => void) | undefined;
|
|
13
|
+
//# sourceMappingURL=start-webui-credential-watcher.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context } from '@wrongstack/core/agent';
|
|
2
|
+
import type { ToolExecutor } from '@wrongstack/core/execution';
|
|
3
|
+
import type { EventBus } from '@wrongstack/core/kernel';
|
|
4
|
+
import type { PermissionPolicy } from '@wrongstack/core/types';
|
|
5
|
+
import { type PackageOperation } from '@wrongstack/techstack';
|
|
6
|
+
export declare function createPackageOperationExecutor(options: {
|
|
7
|
+
toolExecutor: ToolExecutor;
|
|
8
|
+
context: Context;
|
|
9
|
+
events: EventBus;
|
|
10
|
+
permissionPolicy: PermissionPolicy;
|
|
11
|
+
}): (operation: PackageOperation, workspace?: string) => Promise<{
|
|
12
|
+
detail: string;
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=start-webui-remediation.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type * as http from 'node:http';
|
|
2
|
+
import type { WebSocket, WebSocketServer } from 'ws';
|
|
3
|
+
import type { ConnectedClient } from './types.js';
|
|
4
|
+
export declare function setupWebuiShutdown(options: {
|
|
5
|
+
session: any;
|
|
6
|
+
tokenCounter: any;
|
|
7
|
+
clients: Map<WebSocket, ConnectedClient>;
|
|
8
|
+
httpServer: http.Server;
|
|
9
|
+
companionServer: http.Server | null;
|
|
10
|
+
wssPrimary: WebSocketServer;
|
|
11
|
+
wssSecondary?: WebSocketServer | null | undefined;
|
|
12
|
+
stopEmptySessionCleanup: {
|
|
13
|
+
dispose: () => Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
getKanbanSupervisorDispose: () => (() => void) | null;
|
|
16
|
+
todosCheckpoint: {
|
|
17
|
+
detach: () => Promise<void>;
|
|
18
|
+
};
|
|
19
|
+
stopHeapWatchdog: () => Promise<void>;
|
|
20
|
+
getCredentialWatcherClose: () => (() => void) | undefined;
|
|
21
|
+
disposeRealtimeHandlers: () => void;
|
|
22
|
+
governanceHandle?: {
|
|
23
|
+
close: () => Promise<{
|
|
24
|
+
ok: boolean;
|
|
25
|
+
action?: string;
|
|
26
|
+
message?: string;
|
|
27
|
+
} | undefined>;
|
|
28
|
+
} | undefined;
|
|
29
|
+
logger: any;
|
|
30
|
+
brainMonitor: any;
|
|
31
|
+
agentServices: any;
|
|
32
|
+
mcpRegistry: any;
|
|
33
|
+
sessionIdentity: any;
|
|
34
|
+
eventArming: any;
|
|
35
|
+
getEternalSubscription: () => {
|
|
36
|
+
dispose: () => void;
|
|
37
|
+
} | null;
|
|
38
|
+
clearEternalSubscription: () => void;
|
|
39
|
+
codebaseIndexing: any;
|
|
40
|
+
memoryStore: any;
|
|
41
|
+
globalConfigPath: string;
|
|
42
|
+
}): () => void;
|
|
43
|
+
//# sourceMappingURL=start-webui-shutdown.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { attachTodosCheckpoint } from '@wrongstack/core/storage';
|
|
2
|
+
export declare function createStandaloneTodosCheckpointLifecycle(input: {
|
|
3
|
+
state: Parameters<typeof attachTodosCheckpoint>[0];
|
|
4
|
+
sessionsDir: string;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
events?: Parameters<typeof attachTodosCheckpoint>[3];
|
|
7
|
+
traceId?: string | undefined;
|
|
8
|
+
warn?: ((message: string) => void) | undefined;
|
|
9
|
+
}): {
|
|
10
|
+
rebind: (sessionId: string, sessionsDir: string) => Promise<void>;
|
|
11
|
+
detach: () => Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=start-webui-todos.d.ts.map
|
|
@@ -7,19 +7,9 @@
|
|
|
7
7
|
* service construction (Phase 1c), route/dispatcher/connection wiring
|
|
8
8
|
* (Phase 1b/1a), WS + HTTP server creation, and graceful shutdown.
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { createStandaloneTodosCheckpointLifecycle } from './start-webui-todos.js';
|
|
11
11
|
import type { WebUIOptions } from './types.js';
|
|
12
|
-
export
|
|
13
|
-
state: Parameters<typeof attachTodosCheckpoint>[0];
|
|
14
|
-
sessionsDir: string;
|
|
15
|
-
sessionId: string;
|
|
16
|
-
events?: Parameters<typeof attachTodosCheckpoint>[3];
|
|
17
|
-
traceId?: string | undefined;
|
|
18
|
-
warn?: ((message: string) => void) | undefined;
|
|
19
|
-
}): {
|
|
20
|
-
rebind: (sessionId: string, sessionsDir: string) => Promise<void>;
|
|
21
|
-
detach: () => Promise<void>;
|
|
22
|
-
};
|
|
12
|
+
export { createStandaloneTodosCheckpointLifecycle };
|
|
23
13
|
export declare function startWebUI(opts?: WebUIOptions & {
|
|
24
14
|
wsHost?: string | undefined;
|
|
25
15
|
httpPort?: number | undefined;
|
|
@@ -29,21 +19,4 @@ export declare function startWebUI(opts?: WebUIOptions & {
|
|
|
29
19
|
requireToken?: boolean | undefined;
|
|
30
20
|
open?: boolean | undefined;
|
|
31
21
|
}): Promise<void>;
|
|
32
|
-
/**
|
|
33
|
-
* Webui-side mailbox bridge discovery.
|
|
34
|
-
*
|
|
35
|
-
* The webui doesn't spawn a bridge — the bridge (`wstack mailbox serve`)
|
|
36
|
-
* is spawned by any CLI surface via the auto-bootstrap wiring. We just
|
|
37
|
-
* probe the per-project lock for an already-running instance and stash
|
|
38
|
-
* the discovered handle on `ctx.meta['mailboxBridge']` so any later
|
|
39
|
-
* code (the `/mailbox` HTTP surface, agent-status broadcasters,
|
|
40
|
-
* external-agent proxy) can find it without re-running discovery.
|
|
41
|
-
*
|
|
42
|
-
* If no bridge is running, we log a breadcrumb so the user knows
|
|
43
|
-
* to start one (`wstack --repl`, `wstack --webui`, or
|
|
44
|
-
* `wstack mailbox serve` standalone).
|
|
45
|
-
*
|
|
46
|
-
* Best-effort: never throws. A failure (missing lock dir, ENOENT,
|
|
47
|
-
* etc.) logs at warn level and returns — the webui keeps running.
|
|
48
|
-
*/
|
|
49
22
|
//# sourceMappingURL=start-webui.d.ts.map
|