sliccy 5.17.0 → 5.19.0
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/node-server/electron-controller.d.ts +0 -1
- package/dist/node-server/electron-controller.js +4 -10
- package/dist/node-server/electron-main.js +5 -13
- package/dist/node-server/electron-runtime.d.ts +0 -4
- package/dist/node-server/electron-runtime.js +4 -21
- package/dist/node-server/index.js +28 -178
- package/dist/node-server/runtime-flags.d.ts +0 -1
- package/dist/node-server/runtime-flags.js +184 -170
- package/dist/ui/assets/{account-store-CBk_RRLV.js → account-store-CdHh89UC.js} +2 -2
- package/dist/ui/assets/account-store-D-lYehD5.js +38 -0
- package/dist/ui/assets/adobe-DYgkYvXI.js +2 -0
- package/dist/ui/assets/adobe-DaGPd68K.js +1 -0
- package/dist/ui/assets/{adobe-oauth-state-Cc5ypNv9.js → adobe-oauth-state-BlS8V3w-.js} +1 -1
- package/dist/ui/assets/{agent-message-to-chat-BgpA9D4h.js → agent-message-to-chat-x0D_SMXW.js} +1 -1
- package/dist/ui/assets/{apps-BxGLa7A3.js → apps-B3366gbc.js} +1 -1
- package/dist/ui/assets/{azure-openai-B8toZrBe.js → azure-openai-BXN7jl7Y.js} +1 -1
- package/dist/ui/assets/{azure-openai-DDF5YvGp.js → azure-openai-WLLaaJ4G.js} +1 -1
- package/dist/ui/assets/{connect-surface-DcDKugo-.js → connect-surface-CAOR-Lbw.js} +1 -1
- package/dist/ui/assets/dip-Z97FbsKw.js +1 -0
- package/dist/ui/assets/{dist-BxRMuqnq.js → dist-B9dM2Gnv.js} +1 -1
- package/dist/ui/assets/{dist-CT7xqqT4.js → dist-Wq4L5_18.js} +1 -1
- package/dist/ui/assets/{ensure-speech-assets-CqE0151_.js → ensure-speech-assets-D950uENa.js} +1 -1
- package/dist/ui/assets/esp8266-BME0hcg8.js +1 -0
- package/dist/ui/assets/{esptool-operations-92BYsecU.js → esptool-operations-C0atH-wW.js} +2 -2
- package/dist/ui/assets/{fs-6uBFvM8G.js → fs-C5X17x_V.js} +1 -1
- package/dist/ui/assets/{fs-Dryw3sKr.js → fs-Dw4x6jTJ.js} +2 -2
- package/dist/ui/assets/{github-Cj3ICqIW.js → github-BrZWqtfj.js} +2 -2
- package/dist/ui/assets/{github-0RYtpc7E.js → github-CniSwDIM.js} +1 -1
- package/dist/ui/assets/{github-copilot--zbJvr9N.js → github-copilot-DSNvyQFo.js} +1 -1
- package/dist/ui/assets/{github-copilot-V-nsAvuB.js → github-copilot-DlQcUdz6.js} +1 -1
- package/dist/ui/assets/{hear-CrPVmxWH.js → hear-Blf_iRt7.js} +1 -1
- package/dist/ui/assets/{kernel-worker-ER23oF3K.js → kernel-worker-BR2fhRnc.js} +18 -18
- package/dist/ui/assets/{kokoro-engine-C3b5hDH2.js → kokoro-engine-JtWrOUh7.js} +1 -1
- package/dist/ui/assets/{lick-ws-bridge-BXGP-CXC.js → lick-ws-bridge-Cz1MQwG4.js} +1 -1
- package/dist/ui/assets/{lick-ws-bridge-BZM8Qfo_.js → lick-ws-bridge-D5KoFA4T.js} +1 -1
- package/dist/ui/assets/{local-llm-B6ianb7W.js → local-llm-CD4uI8cC.js} +1 -1
- package/dist/ui/assets/{main-DxjacmnX.js → main-lBt01oh5.js} +4 -4
- package/dist/ui/assets/{mount-BHesNrsF.js → mount-CSPi4pLr.js} +1 -1
- package/dist/ui/assets/{mount-BQDv4xZZ.js → mount-DmOv4Rkt.js} +2 -2
- package/dist/ui/assets/new-session-B3gJ8pdc.js +2 -0
- package/dist/ui/assets/oauth-bootstrap-ByFiTlpo.js +2 -0
- package/dist/ui/assets/{openai-codex-14betOS2.js → openai-codex-CBPD1JHV.js} +1 -1
- package/dist/ui/assets/{openai-codex-BKtJHhlt.js → openai-codex-CPTe2ogQ.js} +1 -1
- package/dist/ui/assets/{page-follower-tray-_5YHuZDk.js → page-follower-tray-CO9V0tzy.js} +1 -1
- package/dist/ui/assets/panel-rpc-handlers-CLDCbbgv.js +2 -0
- package/dist/ui/assets/{provider-OHczH2qf.js → provider-DDA91px5.js} +1 -1
- package/dist/ui/assets/provider-store-access-BfKVeXXl.js +1 -0
- package/dist/ui/assets/provider-store-access-Cx_YtwU2.js +1 -0
- package/dist/ui/assets/provider-y1CXWNwX.js +2 -0
- package/dist/ui/assets/{providers-D885eKVy.js → providers-1bfXNKfi.js} +1 -1
- package/dist/ui/assets/providers-DzAysipv.js +2 -0
- package/dist/ui/assets/{quick-llm-B-hEdLQl.js → quick-llm-DcPsoqlO.js} +1 -1
- package/dist/ui/assets/session-freezer-WYzNeHKI.js +1 -0
- package/dist/ui/assets/{speak-BzUDF--m.js → speak-Dt6M0Cc2.js} +1 -1
- package/dist/ui/assets/{sprinkle-manager-CaMt7q2E.js → sprinkle-manager-D44imqpP.js} +1 -1
- package/dist/ui/assets/{store-DROPqnTs.js → store-D9iZjzy_.js} +1 -1
- package/dist/ui/assets/{sudo-DlMyKM9v.js → sudo-Cxr8VdOT.js} +1 -1
- package/dist/ui/assets/{transformers-env-BstAowJV.js → transformers-env-C47rAFns.js} +1 -1
- package/dist/ui/assets/{tray-leader-Cy_sa2iJ.js → tray-leader-CAkM_eBb.js} +1 -1
- package/dist/ui/assets/{tray-leave-runtime-AaVCYWrw.js → tray-leave-runtime-REUItGDj.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-wHDMN0aa.js → upgrade-detection-z0gJwK38.js} +1 -1
- package/dist/ui/assets/{voice-reply-BDR5cDvo.js → voice-reply-Bdya6LMn.js} +1 -1
- package/dist/ui/assets/wc-attach-B9FQyb2S.js +13 -0
- package/dist/ui/assets/{wc-detached-CM9ubFYt.js → wc-detached-_7ecP6_N.js} +1 -1
- package/dist/ui/assets/wc-extension-DwBIGDcV.js +2 -0
- package/dist/ui/assets/wc-follower-DrNEeYmM.js +2 -0
- package/dist/ui/assets/{wc-live-CjWoWLQ-.js → wc-live-6B-nT2B9.js} +5 -5
- package/dist/ui/assets/wc-nav-BDnSNrtL.js +2 -0
- package/dist/ui/assets/{wc-onboarding-N6tWuuCk.js → wc-onboarding-KOUGFy8r.js} +2 -2
- package/dist/ui/assets/wc-placeholder-COrPl7lN.js +4 -0
- package/dist/ui/assets/{wc-settings-BYIw_9Pe.js → wc-settings-B9ROF6Uh.js} +2 -2
- package/dist/ui/assets/{wc-shell-BA3EumXB.js → wc-shell-BlM8nHAz.js} +897 -897
- package/dist/ui/assets/wc-sprinkles-D2BblZdj.js +2 -0
- package/dist/ui/assets/wc-tray-B25a8f-q.js +2 -0
- package/dist/ui/assets/{whisper-engine-BhhYv79q.js → whisper-engine-C3n7b_qt.js} +1 -1
- package/dist/ui/assets/{xai-grok-B3W-lLkh.js → xai-grok-DDP-iJ0L.js} +1 -1
- package/dist/ui/assets/{xai-grok-BqFeqkII.js → xai-grok-PhC2UNO_.js} +1 -1
- package/dist/ui/electron-overlay-entry.js +298 -6356
- package/dist/ui/index.html +3 -2
- package/dist/ui/packages/webapp/index.html +3 -2
- package/package.json +8 -6
- package/dist/node-server/ui-serving.d.ts +0 -18
- package/dist/node-server/ui-serving.js +0 -91
- package/dist/ui/assets/account-store-CbXr_cFb.js +0 -38
- package/dist/ui/assets/adobe-2QErDUFJ.js +0 -2
- package/dist/ui/assets/adobe-BpxUeBn1.js +0 -1
- package/dist/ui/assets/dip-az2X5rT2.js +0 -1
- package/dist/ui/assets/esp8266-DIv0XWqr.js +0 -1
- package/dist/ui/assets/new-session-lZAXDAfU.js +0 -2
- package/dist/ui/assets/oauth-bootstrap-D3t30chW.js +0 -2
- package/dist/ui/assets/panel-rpc-handlers-BZN3D1Ho.js +0 -2
- package/dist/ui/assets/provider-C3ejoQ5_.js +0 -2
- package/dist/ui/assets/provider-store-access-D65njjdc.js +0 -1
- package/dist/ui/assets/provider-store-access-vmuNINTT.js +0 -1
- package/dist/ui/assets/session-freezer-DHHHmvzR.js +0 -1
- package/dist/ui/assets/wc-attach-C42nuWD4.js +0 -13
- package/dist/ui/assets/wc-extension-DXQVZ_Xv.js +0 -2
- package/dist/ui/assets/wc-follower-D_Crj4hj.js +0 -2
- package/dist/ui/assets/wc-nav-DFWDWh6n.js +0 -2
- package/dist/ui/assets/wc-placeholder-BdJLz1BN.js +0 -4
- package/dist/ui/assets/wc-sprinkles-Cnt20KE_.js +0 -2
- package/dist/ui/assets/wc-tray-DCMH2eXY.js +0 -2
- package/dist/ui/electron-overlay-entry.css +0 -37
|
@@ -7,7 +7,7 @@ import { promisify } from 'util';
|
|
|
7
7
|
import { WebSocket } from 'ws';
|
|
8
8
|
import { inflateSync } from 'zlib';
|
|
9
9
|
import { BRIDGE_TOKEN_QUERY_PARAM, BRIDGE_WS_QUERY_PARAM } from './bridge-security.js';
|
|
10
|
-
import { buildElectronAppLaunchSpec, buildElectronOverlayBootstrapScript,
|
|
10
|
+
import { buildElectronAppLaunchSpec, buildElectronOverlayBootstrapScript, ELECTRON_OVERLAY_APP_PATH, getElectronOverlayEntryDistPath, selectBestOverlayTargets, } from './electron-runtime.js';
|
|
11
11
|
const execFile = promisify(nodeExecFile);
|
|
12
12
|
const ELECTRON_OVERLAY_SYNC_INTERVAL_MS = 1500;
|
|
13
13
|
/**
|
|
@@ -420,14 +420,6 @@ function detectAppThemeFromScreenshot(ws, send) {
|
|
|
420
420
|
});
|
|
421
421
|
}
|
|
422
422
|
async function loadElectronOverlayBundleSource(options) {
|
|
423
|
-
const serveOrigin = getElectronServeOrigin(options.servePort);
|
|
424
|
-
if (options.dev) {
|
|
425
|
-
const response = await fetch(buildElectronOverlayEntryUrl(serveOrigin));
|
|
426
|
-
if (!response.ok) {
|
|
427
|
-
throw new Error(`Failed to fetch electron overlay entry: ${response.status} ${response.statusText}`);
|
|
428
|
-
}
|
|
429
|
-
return await response.text();
|
|
430
|
-
}
|
|
431
423
|
return await readFile(getElectronOverlayEntryDistPath(options.projectRoot), 'utf8');
|
|
432
424
|
}
|
|
433
425
|
/**
|
|
@@ -567,7 +559,9 @@ export class ElectronOverlayInjector {
|
|
|
567
559
|
this.presenceCheckIntervalMs = presenceCheckIntervalMs;
|
|
568
560
|
}
|
|
569
561
|
static async create(options) {
|
|
570
|
-
const bundleSource = await loadElectronOverlayBundleSource(
|
|
562
|
+
const bundleSource = await loadElectronOverlayBundleSource({
|
|
563
|
+
projectRoot: options.projectRoot,
|
|
564
|
+
});
|
|
571
565
|
const thinBootstraps = {
|
|
572
566
|
leader: buildElectronOverlayBootstrapScript({
|
|
573
567
|
bundleSource,
|
|
@@ -5,7 +5,7 @@ import { resolve } from 'path';
|
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { mintBridgeToken } from './bridge-security.js';
|
|
7
7
|
import { BRIDGE_ROLE_LEADER, buildThinOverlayAppUrl, resolveHostedLeaderOrigin, } from './electron-controller.js';
|
|
8
|
-
import {
|
|
8
|
+
import { buildElectronOverlayInjectionCall, buildElectronServerSpawnConfig, getElectronOverlayEntryDistPath, getElectronServeOrigin, parseElectronFloatFlags, } from './electron-runtime.js';
|
|
9
9
|
const Dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
10
10
|
const PROJECT_ROOT = resolve(Dirname, '..', '..');
|
|
11
11
|
const FLAGS = parseElectronFloatFlags(process.argv.slice(2));
|
|
@@ -61,13 +61,6 @@ async function waitForServerReady(origin, retries = 60, delayMs = 500) {
|
|
|
61
61
|
throw new Error(`Electron float server did not become ready at ${origin}`);
|
|
62
62
|
}
|
|
63
63
|
async function loadOverlayBundleSource() {
|
|
64
|
-
if (FLAGS.dev) {
|
|
65
|
-
const response = await fetch(buildElectronOverlayEntryUrl(SERVE_ORIGIN));
|
|
66
|
-
if (!response.ok) {
|
|
67
|
-
throw new Error(`Failed to fetch electron overlay entry: ${response.status} ${response.statusText}`);
|
|
68
|
-
}
|
|
69
|
-
return await response.text();
|
|
70
|
-
}
|
|
71
64
|
return await readFile(getElectronOverlayEntryDistPath(PROJECT_ROOT), 'utf8');
|
|
72
65
|
}
|
|
73
66
|
async function injectOverlay(window) {
|
|
@@ -132,7 +125,6 @@ async function createFloatWindow(targetUrl) {
|
|
|
132
125
|
}
|
|
133
126
|
function startCliServer() {
|
|
134
127
|
const spawnConfig = buildElectronServerSpawnConfig(PROJECT_ROOT, {
|
|
135
|
-
dev: FLAGS.dev,
|
|
136
128
|
cdpPort: FLAGS.cdpPort,
|
|
137
129
|
nodePath: process.env['npm_node_execpath'] ?? 'node',
|
|
138
130
|
});
|
|
@@ -141,10 +133,10 @@ function startCliServer() {
|
|
|
141
133
|
env: {
|
|
142
134
|
...process.env,
|
|
143
135
|
PORT: String(FLAGS.servePort),
|
|
144
|
-
// Forward the per-process bridge token to the child server. The
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
136
|
+
// Forward the per-process bridge token to the child server. The child
|
|
137
|
+
// runs as a thin /cdp bridge and gates its `/cdp` upgrade on this
|
|
138
|
+
// token (via `resolveServerBridgeToken`), so it matches the token
|
|
139
|
+
// carried by the float's overlay BrowserWindow.
|
|
148
140
|
SLICC_BRIDGE_TOKEN: BRIDGE_TOKEN,
|
|
149
141
|
SLICC_HOSTED_LEADER_ORIGIN: HOSTED_LEADER_ORIGIN,
|
|
150
142
|
},
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export interface ElectronFloatFlags {
|
|
2
|
-
dev: boolean;
|
|
3
2
|
cdpPort: number;
|
|
4
3
|
servePort: number;
|
|
5
4
|
targetUrl: string;
|
|
@@ -54,13 +53,10 @@ export declare function buildElectronAppLaunchSpec(appPath: string, options: {
|
|
|
54
53
|
}): ElectronAppLaunchSpec;
|
|
55
54
|
export declare function parseElectronFloatFlags(argv: string[], env?: Record<string, string | undefined>): ElectronFloatFlags;
|
|
56
55
|
export declare function buildElectronServerSpawnConfig(projectRoot: string, options: {
|
|
57
|
-
dev: boolean;
|
|
58
56
|
cdpPort: number;
|
|
59
|
-
platform?: NodeJS.Platform;
|
|
60
57
|
nodePath?: string;
|
|
61
58
|
}): ElectronServerSpawnConfig;
|
|
62
59
|
export declare function getElectronServeOrigin(servePort: number): string;
|
|
63
|
-
export declare function buildElectronOverlayEntryUrl(serveOrigin: string): string;
|
|
64
60
|
export declare function getElectronOverlayEntryDistPath(projectRoot: string): string;
|
|
65
61
|
export declare function buildElectronOverlayInjectionCall(options: {
|
|
66
62
|
appUrl: string;
|
|
@@ -196,14 +196,9 @@ function parsePositiveInt(value, fallback) {
|
|
|
196
196
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
197
197
|
}
|
|
198
198
|
export function parseElectronFloatFlags(argv, env = process.env) {
|
|
199
|
-
let dev = false;
|
|
200
199
|
let cdpPort = DEFAULT_ELECTRON_CDP_PORT;
|
|
201
200
|
let targetUrl = DEFAULT_ELECTRON_TARGET_URL;
|
|
202
201
|
for (const arg of argv) {
|
|
203
|
-
if (arg === '--dev') {
|
|
204
|
-
dev = true;
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
202
|
if (arg.startsWith('--cdp-port=')) {
|
|
208
203
|
cdpPort = parsePositiveInt(arg.slice('--cdp-port='.length), DEFAULT_ELECTRON_CDP_PORT);
|
|
209
204
|
continue;
|
|
@@ -218,25 +213,12 @@ export function parseElectronFloatFlags(argv, env = process.env) {
|
|
|
218
213
|
}
|
|
219
214
|
}
|
|
220
215
|
return {
|
|
221
|
-
dev,
|
|
222
216
|
cdpPort,
|
|
223
217
|
servePort: parsePositiveInt(env['PORT'], DEFAULT_ELECTRON_SERVE_PORT),
|
|
224
218
|
targetUrl,
|
|
225
219
|
};
|
|
226
220
|
}
|
|
227
221
|
export function buildElectronServerSpawnConfig(projectRoot, options) {
|
|
228
|
-
if (options.dev) {
|
|
229
|
-
return {
|
|
230
|
-
command: (options.platform ?? process.platform) === 'win32' ? 'npx.cmd' : 'npx',
|
|
231
|
-
args: [
|
|
232
|
-
'tsx',
|
|
233
|
-
'packages/node-server/src/index.ts',
|
|
234
|
-
'--dev',
|
|
235
|
-
'--serve-only',
|
|
236
|
-
`--cdp-port=${options.cdpPort}`,
|
|
237
|
-
],
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
222
|
return {
|
|
241
223
|
command: options.nodePath ?? process.env['npm_node_execpath'] ?? 'node',
|
|
242
224
|
args: [
|
|
@@ -249,9 +231,10 @@ export function buildElectronServerSpawnConfig(projectRoot, options) {
|
|
|
249
231
|
export function getElectronServeOrigin(servePort) {
|
|
250
232
|
return `http://${DEFAULT_ELECTRON_SERVE_HOST}:${servePort}`;
|
|
251
233
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
234
|
+
// The overlay bootstrap IIFE is produced by `@ai-ecoverse/spoon`'s build
|
|
235
|
+
// (`node packages/spoon/build.mjs`), which emits to this canonical `dist/ui`
|
|
236
|
+
// path — the same location the webapp build mirrors it to and swift-launcher
|
|
237
|
+
// copies from. node-server reads it from disk at runtime; the path is stable.
|
|
255
238
|
export function getElectronOverlayEntryDistPath(projectRoot) {
|
|
256
239
|
return resolve(projectRoot, 'dist/ui/electron-overlay-entry.js');
|
|
257
240
|
}
|
|
@@ -43,7 +43,6 @@ import { SecretProxyManager } from './secrets/proxy-manager.js';
|
|
|
43
43
|
import { readOrCreateSessionId } from './secrets/session-id-file.js';
|
|
44
44
|
import { registerSecretsReloadEndpoint } from './secrets-reload-endpoint.js';
|
|
45
45
|
import { registerSudoApproveEndpoint } from './sudo/endpoint.js';
|
|
46
|
-
import { attachUiServing } from './ui-serving.js';
|
|
47
46
|
const Dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
48
47
|
const PROJECT_ROOT = resolve(Dirname, '..', '..');
|
|
49
48
|
// ---------------------------------------------------------------------------
|
|
@@ -61,25 +60,28 @@ if (RUNTIME_FLAGS.version) {
|
|
|
61
60
|
console.log(pkg.version);
|
|
62
61
|
process.exit(0);
|
|
63
62
|
}
|
|
64
|
-
const DEV_MODE = RUNTIME_FLAGS.dev;
|
|
65
63
|
const SERVE_ONLY = RUNTIME_FLAGS.serveOnly;
|
|
66
64
|
const ELECTRON_MODE = RUNTIME_FLAGS.electron;
|
|
67
65
|
const ELECTRON_APP = RUNTIME_FLAGS.electronApp;
|
|
68
66
|
const KILL_EXISTING_ELECTRON_APP = RUNTIME_FLAGS.kill;
|
|
69
67
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
68
|
+
* Whether to default-mint a `/cdp` bridge token (and mount thin-bridge
|
|
69
|
+
* CORS) for this process. Active in every mode EXCEPT a direct
|
|
70
|
+
* `--serve-only` run with no forwarded token: serve-only reuses an
|
|
71
|
+
* external CDP target and never launches a browser or prints a hosted
|
|
72
|
+
* launch URL carrying a freshly minted token, so minting one would gate
|
|
73
|
+
* `/cdp` (and `/api/*` CORS) on a secret the already-open page can never
|
|
74
|
+
* present. A `--serve-only` reattach from Sliccstart still works because
|
|
75
|
+
* it forwards `SLICC_BRIDGE_TOKEN`, which `resolveServerBridgeToken` and
|
|
76
|
+
* `shouldMountThinBridgeCors` honor regardless of this flag.
|
|
74
77
|
*/
|
|
75
|
-
const THIN_BRIDGE_MODE = !
|
|
78
|
+
const THIN_BRIDGE_MODE = !SERVE_ONLY;
|
|
76
79
|
// ---------------------------------------------------------------------------
|
|
77
80
|
// File logger — persistent log file in ~/.slicc/logs/
|
|
78
81
|
// ---------------------------------------------------------------------------
|
|
79
82
|
const fileLogger = new FileLogger({
|
|
80
83
|
logDir: RUNTIME_FLAGS.logDir ?? undefined,
|
|
81
84
|
logLevel: RUNTIME_FLAGS.logLevel,
|
|
82
|
-
devMode: DEV_MODE,
|
|
83
85
|
});
|
|
84
86
|
if (fileLogger.logFile) {
|
|
85
87
|
console.log(`Log file: ${fileLogger.logFile}`);
|
|
@@ -115,13 +117,6 @@ async function waitForCDP(port, retries = 30, delayMs = 500) {
|
|
|
115
117
|
}
|
|
116
118
|
throw new Error(`CDP did not become available on port ${port}`);
|
|
117
119
|
}
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// ANSI color helpers
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
const ANSI_RED = '\x1b[31m';
|
|
122
|
-
const ANSI_YELLOW = '\x1b[33m';
|
|
123
|
-
const ANSI_CYAN = '\x1b[36m';
|
|
124
|
-
const ANSI_RESET = '\x1b[0m';
|
|
125
120
|
function pipeChildOutput(child, label) {
|
|
126
121
|
child.stdout?.on('data', (data) => {
|
|
127
122
|
process.stdout.write(`[${label}:out] ${data}`);
|
|
@@ -175,123 +170,6 @@ async function findAvailablePort(preferred) {
|
|
|
175
170
|
throw err;
|
|
176
171
|
}
|
|
177
172
|
}
|
|
178
|
-
function formatPreviewProperties(properties) {
|
|
179
|
-
return properties
|
|
180
|
-
.map((p) => {
|
|
181
|
-
let val;
|
|
182
|
-
if (p.type === 'object')
|
|
183
|
-
val = p.subtype === 'array' ? '[...]' : '{...}';
|
|
184
|
-
else if (p.type === 'string')
|
|
185
|
-
val = `"${p.value}"`;
|
|
186
|
-
else
|
|
187
|
-
val = p.value;
|
|
188
|
-
return `${p.name}: ${val}`;
|
|
189
|
-
})
|
|
190
|
-
.join(', ');
|
|
191
|
-
}
|
|
192
|
-
function formatRemoteObject(obj) {
|
|
193
|
-
if (obj.type === 'undefined')
|
|
194
|
-
return 'undefined';
|
|
195
|
-
if (obj.type === 'object' && obj.subtype === 'null')
|
|
196
|
-
return 'null';
|
|
197
|
-
// Format objects/arrays using preview properties when available
|
|
198
|
-
if (obj.type === 'object' && obj.preview?.properties && obj.preview.properties.length > 0) {
|
|
199
|
-
const inner = formatPreviewProperties(obj.preview.properties);
|
|
200
|
-
const suffix = obj.preview.overflow ? ', ...' : '';
|
|
201
|
-
if (obj.subtype === 'array')
|
|
202
|
-
return `[${inner}${suffix}]`;
|
|
203
|
-
return `{ ${inner}${suffix} }`;
|
|
204
|
-
}
|
|
205
|
-
if (obj.preview?.description)
|
|
206
|
-
return obj.preview.description;
|
|
207
|
-
if (obj.description !== undefined)
|
|
208
|
-
return obj.description;
|
|
209
|
-
if (obj.value !== undefined)
|
|
210
|
-
return String(obj.value);
|
|
211
|
-
return `[${obj.type}]`;
|
|
212
|
-
}
|
|
213
|
-
function colorForType(type) {
|
|
214
|
-
switch (type) {
|
|
215
|
-
case 'error':
|
|
216
|
-
return ANSI_RED;
|
|
217
|
-
case 'warning':
|
|
218
|
-
return ANSI_YELLOW;
|
|
219
|
-
default:
|
|
220
|
-
return ANSI_CYAN;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
async function findPageTarget(cdpPort, pageUrl) {
|
|
224
|
-
try {
|
|
225
|
-
const res = await fetch(`http://127.0.0.1:${cdpPort}/json`);
|
|
226
|
-
const targets = (await res.json());
|
|
227
|
-
const match = targets.find((t) => t.type === 'page' && t.url.includes(`localhost:${pageUrl}`) && t.webSocketDebuggerUrl);
|
|
228
|
-
return match ? { webSocketDebuggerUrl: match.webSocketDebuggerUrl } : null;
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
async function attachConsoleForwarder(cdpPort, pageUrl) {
|
|
235
|
-
const pageDedup = new CliLogDedup('[page]');
|
|
236
|
-
const connect = async () => {
|
|
237
|
-
// Poll for the page target
|
|
238
|
-
let target = null;
|
|
239
|
-
for (let i = 0; i < 20; i++) {
|
|
240
|
-
target = await findPageTarget(cdpPort, pageUrl);
|
|
241
|
-
if (target)
|
|
242
|
-
break;
|
|
243
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
244
|
-
}
|
|
245
|
-
if (!target) {
|
|
246
|
-
console.log('[page] Could not find page target — console forwarding disabled');
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const ws = new WebSocket(target.webSocketDebuggerUrl);
|
|
250
|
-
ws.on('open', () => {
|
|
251
|
-
ws.send(JSON.stringify({ id: 1, method: 'Runtime.enable' }));
|
|
252
|
-
});
|
|
253
|
-
ws.on('message', (raw) => {
|
|
254
|
-
try {
|
|
255
|
-
const msg = JSON.parse(String(raw));
|
|
256
|
-
if (msg.method === 'Runtime.consoleAPICalled') {
|
|
257
|
-
const params = msg.params;
|
|
258
|
-
const type = params.type;
|
|
259
|
-
const color = colorForType(type);
|
|
260
|
-
const argsStr = params.args.map(formatRemoteObject).join(' ');
|
|
261
|
-
const line = `[page:${type}] ${argsStr}`;
|
|
262
|
-
if (pageDedup.shouldLog(line)) {
|
|
263
|
-
console.log(`${color}[page:${type}]${ANSI_RESET} ${argsStr}`);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
if (msg.method === 'Runtime.exceptionThrown') {
|
|
267
|
-
const params = msg.params;
|
|
268
|
-
const details = params.exceptionDetails;
|
|
269
|
-
const desc = details.exception?.description ?? details.text;
|
|
270
|
-
console.log(`${ANSI_RED}[page:exception]${ANSI_RESET} ${desc}`);
|
|
271
|
-
if (details.stackTrace) {
|
|
272
|
-
for (const frame of details.stackTrace.callFrames) {
|
|
273
|
-
const fn = frame.functionName || '<anonymous>';
|
|
274
|
-
console.log(`${ANSI_RED} at ${fn} (${frame.url}:${frame.lineNumber}:${frame.columnNumber})${ANSI_RESET}`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
catch {
|
|
280
|
-
// Ignore malformed messages
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
ws.on('close', () => {
|
|
284
|
-
// Reconnect after a short delay (page may have reloaded)
|
|
285
|
-
setTimeout(() => {
|
|
286
|
-
connect();
|
|
287
|
-
}, 1000);
|
|
288
|
-
});
|
|
289
|
-
ws.on('error', () => {
|
|
290
|
-
// Error will trigger close, which handles reconnection
|
|
291
|
-
});
|
|
292
|
-
};
|
|
293
|
-
await connect();
|
|
294
|
-
}
|
|
295
173
|
// ---------------------------------------------------------------------------
|
|
296
174
|
// Main
|
|
297
175
|
// ---------------------------------------------------------------------------
|
|
@@ -349,8 +227,6 @@ async function resolvePorts(state) {
|
|
|
349
227
|
else if (state.servePort !== PREFERRED_SERVE_PORT) {
|
|
350
228
|
console.log(`Port ${PREFERRED_SERVE_PORT} in use, serving on port ${state.servePort}`);
|
|
351
229
|
}
|
|
352
|
-
if (DEV_MODE)
|
|
353
|
-
console.log('Starting in dev mode (Vite HMR enabled)');
|
|
354
230
|
if (SERVE_ONLY) {
|
|
355
231
|
console.log(`Starting in serve-only mode (reusing external CDP on port ${state.cdpPort})`);
|
|
356
232
|
}
|
|
@@ -491,7 +367,7 @@ function buildBrowserLaunchUrl(state) {
|
|
|
491
367
|
// Thin-bridge standalone: Chrome opens the hosted leader directly; the
|
|
492
368
|
// local node-server serves no UI at all. Bridge coordinates ride as
|
|
493
369
|
// query params so the leader can discover + authenticate /cdp.
|
|
494
|
-
const serveOriginForLaunch =
|
|
370
|
+
const serveOriginForLaunch = state.bridgeToken ? resolveThinLeaderOrigin() : state.serveOrigin;
|
|
495
371
|
let url = resolveCliBrowserLaunchUrl({
|
|
496
372
|
serveOrigin: serveOriginForLaunch,
|
|
497
373
|
lead: RUNTIME_FLAGS.lead,
|
|
@@ -499,7 +375,7 @@ function buildBrowserLaunchUrl(state) {
|
|
|
499
375
|
envWorkerBaseUrl: process.env['WORKER_BASE_URL'] ?? null,
|
|
500
376
|
join: RUNTIME_FLAGS.join,
|
|
501
377
|
joinUrl: RUNTIME_FLAGS.joinUrl,
|
|
502
|
-
bridgeWsUrl:
|
|
378
|
+
bridgeWsUrl: state.bridgeToken ? `ws://localhost:${state.servePort}/cdp` : null,
|
|
503
379
|
bridgeToken: state.bridgeToken,
|
|
504
380
|
});
|
|
505
381
|
if (RUNTIME_FLAGS.hosted) {
|
|
@@ -514,7 +390,7 @@ function buildBrowserLaunchUrl(state) {
|
|
|
514
390
|
else if (RUNTIME_FLAGS.lead) {
|
|
515
391
|
console.log(`Lead launch URL: ${url}`);
|
|
516
392
|
}
|
|
517
|
-
else
|
|
393
|
+
else {
|
|
518
394
|
// Print WITHOUT the bridgeToken — it's a session capability.
|
|
519
395
|
const sanitized = url.replace(/([?&])bridgeToken=[^&]+/, '$1bridgeToken=<redacted>');
|
|
520
396
|
console.log(`Thin-bridge launch URL: ${sanitized}`);
|
|
@@ -543,7 +419,7 @@ async function launchChromeTarget(state) {
|
|
|
543
419
|
const browserLaunchUrl = buildBrowserLaunchUrl(state);
|
|
544
420
|
const chromeProfile = resolveChromeProfileOrExit(state);
|
|
545
421
|
const chromePath = findChromeExecutable({
|
|
546
|
-
executablePreference: !
|
|
422
|
+
executablePreference: !chromeProfile.id ? 'installed' : 'chrome-for-testing',
|
|
547
423
|
});
|
|
548
424
|
if (!chromePath) {
|
|
549
425
|
console.error('Could not find Chrome/Chromium. Please install Chrome or set CHROME_PATH.');
|
|
@@ -996,7 +872,6 @@ async function startOverlayInjector(state, cdpPort, servePort) {
|
|
|
996
872
|
state.overlayInjector = await ElectronOverlayInjector.create({
|
|
997
873
|
cdpPort,
|
|
998
874
|
servePort,
|
|
999
|
-
dev: DEV_MODE,
|
|
1000
875
|
projectRoot: PROJECT_ROOT,
|
|
1001
876
|
thinBridge,
|
|
1002
877
|
});
|
|
@@ -1023,17 +898,11 @@ function startListening(deps) {
|
|
|
1023
898
|
const { server, fileLogger, servePort, serveOrigin, cdpPort } = deps;
|
|
1024
899
|
return new Promise((resolve) => {
|
|
1025
900
|
server.listen(servePort, '127.0.0.1', () => {
|
|
1026
|
-
|
|
1027
|
-
console.log(`Thin /cdp bridge + /api at ${serveOrigin}`);
|
|
1028
|
-
}
|
|
1029
|
-
else {
|
|
1030
|
-
console.log(`Serving UI at ${serveOrigin}`);
|
|
1031
|
-
}
|
|
901
|
+
console.log(`Thin /cdp bridge + /api at ${serveOrigin}`);
|
|
1032
902
|
console.log(`CDP proxy at ws://localhost:${servePort}/cdp`);
|
|
1033
903
|
fileLogger.log('info', 'CLI server started', {
|
|
1034
904
|
port: servePort,
|
|
1035
905
|
cdpPort,
|
|
1036
|
-
devMode: DEV_MODE,
|
|
1037
906
|
electronMode: ELECTRON_MODE,
|
|
1038
907
|
});
|
|
1039
908
|
resolve();
|
|
@@ -1042,10 +911,10 @@ function startListening(deps) {
|
|
|
1042
911
|
}
|
|
1043
912
|
/**
|
|
1044
913
|
* Post-listen warmup: pre-connects to Chrome's CDP (so the proxy is warm
|
|
1045
|
-
* before the first client), starts the
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
914
|
+
* before the first client) and, in Electron mode, starts the overlay
|
|
915
|
+
* injector. Reads `state.cdpPort` dynamically so the value populated by
|
|
916
|
+
* `launchChromeTarget` is used regardless of when this runs relative to
|
|
917
|
+
* the listen callback.
|
|
1049
918
|
*/
|
|
1050
919
|
function runCdpProxyWarmup(state, deps) {
|
|
1051
920
|
const { app, ctx, servePort } = deps;
|
|
@@ -1053,15 +922,6 @@ function runCdpProxyWarmup(state, deps) {
|
|
|
1053
922
|
if (ELECTRON_MODE) {
|
|
1054
923
|
void startOverlayInjector(state, state.cdpPort, servePort);
|
|
1055
924
|
}
|
|
1056
|
-
else if (!THIN_BRIDGE_MODE) {
|
|
1057
|
-
// Thin-bridge mode has no local UI page to forward console output from.
|
|
1058
|
-
const cdpPort = state.cdpPort;
|
|
1059
|
-
setTimeout(() => {
|
|
1060
|
-
attachConsoleForwarder(cdpPort, String(servePort)).catch((err) => {
|
|
1061
|
-
console.error('[page] Console forwarder error:', err);
|
|
1062
|
-
});
|
|
1063
|
-
}, 2500);
|
|
1064
|
-
}
|
|
1065
925
|
}
|
|
1066
926
|
/**
|
|
1067
927
|
* Build the secret stores + masking pipeline shared by the fetch-proxy, the
|
|
@@ -1171,7 +1031,7 @@ async function main() {
|
|
|
1171
1031
|
// shutdown handler) read it dynamically from `state`.
|
|
1172
1032
|
const state = createServerState();
|
|
1173
1033
|
await resolvePorts(state);
|
|
1174
|
-
const { servePort: SERVE_PORT
|
|
1034
|
+
const { servePort: SERVE_PORT } = state;
|
|
1175
1035
|
// 3. Set up express app with request logging
|
|
1176
1036
|
const { secretStore, secretProxy, oauthStore } = await bootstrapSecrets();
|
|
1177
1037
|
const app = express();
|
|
@@ -1206,9 +1066,7 @@ async function main() {
|
|
|
1206
1066
|
(process.env['SLICC_TRAY_WORKER_BASE_URL']?.trim() || null) ??
|
|
1207
1067
|
RUNTIME_FLAGS.leadWorkerBaseUrl ??
|
|
1208
1068
|
(process.env['WORKER_BASE_URL']?.trim() || null) ??
|
|
1209
|
-
|
|
1210
|
-
? 'https://slicc-tray-hub-staging.minivelos.workers.dev'
|
|
1211
|
-
: 'https://www.sliccy.ai'),
|
|
1069
|
+
'https://www.sliccy.ai',
|
|
1212
1070
|
// Read dynamically from state — populated by `launchElectronTarget`
|
|
1213
1071
|
// when an existing leader is discovered. `launchBrowser` now runs
|
|
1214
1072
|
// after `server.listen()`, so this endpoint must NOT close over a
|
|
@@ -1242,7 +1100,7 @@ async function main() {
|
|
|
1242
1100
|
registerHandoffRoute(app, { broadcastLickEvent });
|
|
1243
1101
|
// Secret management API — direct .env file access (no browser needed),
|
|
1244
1102
|
// plus the S3 / DA sign-and-forward and masked-secret endpoints.
|
|
1245
|
-
registerSecretRoutes(app, { secretStore, secretProxy, oauthStore, devMode:
|
|
1103
|
+
registerSecretRoutes(app, { secretStore, secretProxy, oauthStore, devMode: false });
|
|
1246
1104
|
// Cloud status endpoint (hosted-only) — writes join info to /tmp/slicc-join.json
|
|
1247
1105
|
// Register BEFORE Chromium launches. The webapp's first action after
|
|
1248
1106
|
// ?runtime=hosted-leader boot is to mint a tray and POST /api/cloud-status.
|
|
@@ -1259,21 +1117,13 @@ async function main() {
|
|
|
1259
1117
|
// Fetch proxy — forwards cross-origin requests from the browser to bypass CORS,
|
|
1260
1118
|
// injecting/unmasking secrets and streaming the response with a UTF-8-safe scrub.
|
|
1261
1119
|
registerFetchProxyRoute(app, { secretProxy });
|
|
1262
|
-
//
|
|
1120
|
+
// node-server serves no UI in any mode: it is a pure /cdp bridge + /api
|
|
1121
|
+
// surface. Chrome opens the sliccy.ai-hosted leader which talks to /cdp +
|
|
1122
|
+
// /api cross-origin. Create the HTTP server so we can attach the /cdp WS
|
|
1123
|
+
// upgrade handler to it.
|
|
1263
1124
|
const server = createServer(app);
|
|
1264
|
-
//
|
|
1265
|
-
//
|
|
1266
|
-
// leader which talks to /cdp + /api cross-origin.
|
|
1267
|
-
if (!THIN_BRIDGE_MODE) {
|
|
1268
|
-
await attachUiServing(app, server, {
|
|
1269
|
-
devMode: DEV_MODE,
|
|
1270
|
-
hosted: RUNTIME_FLAGS.hosted,
|
|
1271
|
-
serveOrigin: SERVE_ORIGIN,
|
|
1272
|
-
uiDir: resolve(Dirname, '..', 'ui'),
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
// 4. CDP WebSocket proxy at /cdp — noServer mode so Vite's dev middleware
|
|
1276
|
-
// doesn't intercept the upgrade.
|
|
1125
|
+
// 4. CDP WebSocket proxy at /cdp — noServer mode so we own the upgrade
|
|
1126
|
+
// handler routing for /cdp and /licks-ws.
|
|
1277
1127
|
const wss = createCdpWebSocketServer(state.bridgeToken);
|
|
1278
1128
|
attachCdpUpgradeRouting(server, wss, lickWss, state.bridgeToken);
|
|
1279
1129
|
const cdpCtx = {
|