sliccy 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/cli/chrome-launch.js +9 -4
- package/dist/cli/cli-log-dedup.js +3 -3
- package/dist/cli/electron-controller.js +5 -5
- package/dist/cli/electron-main.js +1 -1
- package/dist/cli/electron-runtime.js +6 -5
- package/dist/cli/file-logger.js +14 -5
- package/dist/cli/index.js +66 -28
- package/dist/cli/release-package.js +4 -6
- package/dist/ui/assets/{___vite-browser-external_commonjs-proxy-DI43bbQw.js → ___vite-browser-external_commonjs-proxy-C4US5Rir.js} +1 -1
- package/dist/ui/assets/{bsh-watchdog-uk_ACjOz.js → bsh-watchdog-ClU8MZGz.js} +1 -1
- package/dist/ui/assets/{index-C1lgMSN2.js → index-BOh9kRaE.js} +59 -33
- package/dist/ui/assets/{index-bo18OtLo.js → index-BQQ3oExA.js} +1 -1
- package/dist/ui/assets/{index-DxLOLOfX.js → index-BSkuZoNL.js} +1 -1
- package/dist/ui/assets/{index-g_Oj1Xd7.js → index-BVktLK0e.js} +1 -1
- package/dist/ui/assets/{index-ChxPM3KL.js → index-CEcKH6Ga.js} +1 -1
- package/dist/ui/assets/{index-xzVzlu9M.js → index-CJv-O0EK.js} +1 -1
- package/dist/ui/assets/{index-9crYr7pn.js → index-COWGW4FB.js} +1 -1
- package/dist/ui/assets/{index-DJA4jgWl.js → index-Cg4KUhhA.js} +1 -1
- package/dist/ui/assets/{index-COMQCYh-.js → index-vXNfi5ps.js} +1 -1
- package/dist/ui/assets/{offscreen-client-B_ASD-T8.js → offscreen-client-B_X62xJc.js} +1 -1
- package/dist/ui/assets/{sql-wasm-8C3CnQsV.js → sql-wasm-D66sddMs.js} +1 -1
- package/dist/ui/index.html +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -108,8 +108,6 @@ For the full Electron workflow, see [docs/electron.md](docs/electron.md).
|
|
|
108
108
|
|
|
109
109
|
## Screenshots and proof
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
111
|
## How it works
|
|
114
112
|
|
|
115
113
|
SLICC shares one core across the CLI, extension, and Electron modes. The browser is not just where you view the product — it is where the agent runtime lives.
|
|
@@ -136,6 +134,7 @@ Why the name? SLICC stands for **Self-Licking Ice Cream Cone**: a recursive syst
|
|
|
136
134
|
## API Keys and Providers
|
|
137
135
|
|
|
138
136
|
To use SLICC, you need an LLM provider. SLICC is very much a BYOT (bring your own tokens) affair. We have built-in support for many providers, and these have actually been tested.
|
|
137
|
+
|
|
139
138
|
- Adobe (for AEM customers. Talk to the team to get enabled)
|
|
140
139
|
- AWS Bedrock (because enterprise)
|
|
141
140
|
- AWS Bedrock CAMP (this is Adobe-internal. Did I say "because enterprise" already?)
|
|
@@ -76,7 +76,9 @@ export function resolveChromeLaunchProfile(options) {
|
|
|
76
76
|
id: profile,
|
|
77
77
|
displayName: definition.displayName,
|
|
78
78
|
userDataDir: join(resolveQaProfilesRoot(options.projectRoot), profile),
|
|
79
|
-
extensionPath: definition.loadsExtension
|
|
79
|
+
extensionPath: definition.loadsExtension
|
|
80
|
+
? join(options.projectRoot, 'dist', 'extension')
|
|
81
|
+
: null,
|
|
80
82
|
};
|
|
81
83
|
}
|
|
82
84
|
export function buildChromeLaunchArgs(options) {
|
|
@@ -142,7 +144,10 @@ function resolveMacAppBundle(appPath, platform, existsSyncImpl) {
|
|
|
142
144
|
return null;
|
|
143
145
|
// Derive the binary name from the bundle name:
|
|
144
146
|
// "Google Chrome.app" → "Google Chrome"
|
|
145
|
-
const bundleName = appPath
|
|
147
|
+
const bundleName = appPath
|
|
148
|
+
.split('/')
|
|
149
|
+
.pop()
|
|
150
|
+
.replace(/\.app$/, '');
|
|
146
151
|
const candidate = join(appPath, 'Contents', 'MacOS', bundleName);
|
|
147
152
|
return existsSyncImpl(candidate) ? candidate : null;
|
|
148
153
|
}
|
|
@@ -197,8 +202,8 @@ export function findChromeExecutable(options = {}) {
|
|
|
197
202
|
readdirSyncImpl,
|
|
198
203
|
});
|
|
199
204
|
return executablePreference === 'installed'
|
|
200
|
-
? installedChrome ?? chromeForTesting
|
|
201
|
-
: chromeForTesting ?? installedChrome;
|
|
205
|
+
? (installedChrome ?? chromeForTesting)
|
|
206
|
+
: (chromeForTesting ?? installedChrome);
|
|
202
207
|
}
|
|
203
208
|
async function readJsonFile(filePath) {
|
|
204
209
|
try {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const BUFFER_SIZE = 10;
|
|
9
9
|
const WINDOW_MS = 60_000; // 1 minute
|
|
10
10
|
function makeFingerprint(message) {
|
|
11
|
-
return message
|
|
11
|
+
return (message
|
|
12
12
|
// UUIDs
|
|
13
13
|
.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '<id>')
|
|
14
14
|
// Hex strings (8+ chars, e.g. session IDs, target IDs)
|
|
@@ -17,7 +17,7 @@ function makeFingerprint(message) {
|
|
|
17
17
|
.replace(/\{[^}]{20,}\}/g, '{…}')
|
|
18
18
|
.replace(/\[[^\]]{20,}\]/g, '[…]')
|
|
19
19
|
// Numbers (integers and floats)
|
|
20
|
-
.replace(/\b\d+(\.\d+)?\b/g, '<n>');
|
|
20
|
+
.replace(/\b\d+(\.\d+)?\b/g, '<n>'));
|
|
21
21
|
}
|
|
22
22
|
export class CliLogDedup {
|
|
23
23
|
entries = [];
|
|
@@ -35,7 +35,7 @@ export class CliLogDedup {
|
|
|
35
35
|
// Evict stale entries
|
|
36
36
|
this.evict(now);
|
|
37
37
|
// Check for existing match
|
|
38
|
-
const existing = this.entries.find(e => e.fingerprint === fp);
|
|
38
|
+
const existing = this.entries.find((e) => e.fingerprint === fp);
|
|
39
39
|
if (existing) {
|
|
40
40
|
existing.count++;
|
|
41
41
|
return false;
|
|
@@ -11,9 +11,9 @@ function commandLineExecutableMatchesPattern(commandLine, pattern) {
|
|
|
11
11
|
// Only match when the target app path is the executable itself, not an argument —
|
|
12
12
|
// this avoids false positives when the path appears as a CLI flag (e.g. --kill /App.app).
|
|
13
13
|
const executable = commandLine.trimStart().split(/\s+/)[0] ?? '';
|
|
14
|
-
return executable === pattern
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
return (executable === pattern ||
|
|
15
|
+
executable.startsWith(pattern + '/') ||
|
|
16
|
+
executable.startsWith(pattern + '\\'));
|
|
17
17
|
}
|
|
18
18
|
export function findMatchingElectronAppPids(runningProcesses, processMatchPatterns, currentPid = process.pid) {
|
|
19
19
|
const matches = runningProcesses.filter((processInfo) => {
|
|
@@ -26,8 +26,8 @@ export function findMatchingElectronAppPids(runningProcesses, processMatchPatter
|
|
|
26
26
|
if (/^(\/\S*\/)?(node|npx|tsx|npm|open|bash|zsh|sh|csh|fish|dash|timeout|env|sudo|caffeinate)\b/i.test(cmdTrimmed))
|
|
27
27
|
return false;
|
|
28
28
|
return processMatchPatterns.some((pattern) => {
|
|
29
|
-
return commandLineExecutableMatchesPattern(processInfo.commandLine, pattern)
|
|
30
|
-
|
|
29
|
+
return (commandLineExecutableMatchesPattern(processInfo.commandLine, pattern) ||
|
|
30
|
+
(processInfo.executablePath?.includes(pattern) ?? false));
|
|
31
31
|
});
|
|
32
32
|
});
|
|
33
33
|
return Array.from(new Set(matches.map((processInfo) => processInfo.pid).filter((pid) => pid !== currentPid)));
|
|
@@ -175,7 +175,7 @@ app.on('will-quit', () => {
|
|
|
175
175
|
void stopCliServer();
|
|
176
176
|
});
|
|
177
177
|
main().catch((error) => {
|
|
178
|
-
const message = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
178
|
+
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
179
179
|
console.error('[electron-float] Fatal error:', message);
|
|
180
180
|
void stopCliServer().finally(() => {
|
|
181
181
|
app.exit(1);
|
|
@@ -21,10 +21,7 @@ export function resolveElectronAppExecutablePath(appPath, platform = process.pla
|
|
|
21
21
|
return resolvedAppPath;
|
|
22
22
|
}
|
|
23
23
|
export function buildElectronAppProcessMatchPatterns(appPath, platform = process.platform) {
|
|
24
|
-
return Array.from(new Set([
|
|
25
|
-
resolve(appPath),
|
|
26
|
-
resolveElectronAppExecutablePath(appPath, platform),
|
|
27
|
-
]));
|
|
24
|
+
return Array.from(new Set([resolve(appPath), resolveElectronAppExecutablePath(appPath, platform)]));
|
|
28
25
|
}
|
|
29
26
|
export function buildElectronAppLaunchSpec(appPath, options) {
|
|
30
27
|
const platform = options.platform ?? process.platform;
|
|
@@ -83,7 +80,11 @@ export function buildElectronServerSpawnConfig(projectRoot, options) {
|
|
|
83
80
|
}
|
|
84
81
|
return {
|
|
85
82
|
command: options.nodePath ?? process.env['npm_node_execpath'] ?? 'node',
|
|
86
|
-
args: [
|
|
83
|
+
args: [
|
|
84
|
+
resolve(projectRoot, 'dist/cli/index.js'),
|
|
85
|
+
'--serve-only',
|
|
86
|
+
`--cdp-port=${options.cdpPort}`,
|
|
87
|
+
],
|
|
87
88
|
};
|
|
88
89
|
}
|
|
89
90
|
export function getElectronServeOrigin(servePort) {
|
package/dist/cli/file-logger.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync, openSync, writeSync, closeSync, readdirSync, statSync, unlinkSync } from 'node:fs';
|
|
1
|
+
import { mkdirSync, openSync, writeSync, closeSync, readdirSync, statSync, unlinkSync, } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
@@ -45,7 +45,10 @@ function safeStringify(value) {
|
|
|
45
45
|
}
|
|
46
46
|
/** Generates a filename-safe ISO timestamp + PID string. */
|
|
47
47
|
export function generateLogFilename() {
|
|
48
|
-
const ts = new Date()
|
|
48
|
+
const ts = new Date()
|
|
49
|
+
.toISOString()
|
|
50
|
+
.replace(/:/g, '-')
|
|
51
|
+
.replace(/\.\d{3}Z$/, '');
|
|
49
52
|
return `${ts}_${process.pid}.log`;
|
|
50
53
|
}
|
|
51
54
|
// ---------------------------------------------------------------------------
|
|
@@ -141,7 +144,9 @@ export class FileLogger {
|
|
|
141
144
|
try {
|
|
142
145
|
closeSync(this.fd);
|
|
143
146
|
}
|
|
144
|
-
catch {
|
|
147
|
+
catch {
|
|
148
|
+
/* already closed */
|
|
149
|
+
}
|
|
145
150
|
this.fd = null;
|
|
146
151
|
this.restoreConsole();
|
|
147
152
|
}
|
|
@@ -191,9 +196,13 @@ export class FileLogger {
|
|
|
191
196
|
try {
|
|
192
197
|
writeSync(this.fd, stripAnsi(line) + '\n');
|
|
193
198
|
}
|
|
194
|
-
catch {
|
|
199
|
+
catch {
|
|
200
|
+
/* fd may be invalid */
|
|
201
|
+
}
|
|
195
202
|
}
|
|
196
|
-
onExit = () => {
|
|
203
|
+
onExit = () => {
|
|
204
|
+
this.close();
|
|
205
|
+
};
|
|
197
206
|
registerShutdownHandlers() {
|
|
198
207
|
process.once('SIGINT', this.onExit);
|
|
199
208
|
process.once('SIGTERM', this.onExit);
|
package/dist/cli/index.js
CHANGED
|
@@ -124,7 +124,8 @@ async function findAvailablePort(preferred) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
function formatPreviewProperties(properties) {
|
|
127
|
-
return properties
|
|
127
|
+
return properties
|
|
128
|
+
.map((p) => {
|
|
128
129
|
let val;
|
|
129
130
|
if (p.type === 'object')
|
|
130
131
|
val = p.subtype === 'array' ? '[...]' : '{...}';
|
|
@@ -133,7 +134,8 @@ function formatPreviewProperties(properties) {
|
|
|
133
134
|
else
|
|
134
135
|
val = p.value;
|
|
135
136
|
return `${p.name}: ${val}`;
|
|
136
|
-
})
|
|
137
|
+
})
|
|
138
|
+
.join(', ');
|
|
137
139
|
}
|
|
138
140
|
function formatRemoteObject(obj) {
|
|
139
141
|
if (obj.type === 'undefined')
|
|
@@ -158,9 +160,12 @@ function formatRemoteObject(obj) {
|
|
|
158
160
|
}
|
|
159
161
|
function colorForType(type) {
|
|
160
162
|
switch (type) {
|
|
161
|
-
case 'error':
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
case 'error':
|
|
164
|
+
return ANSI_RED;
|
|
165
|
+
case 'warning':
|
|
166
|
+
return ANSI_YELLOW;
|
|
167
|
+
default:
|
|
168
|
+
return ANSI_CYAN;
|
|
164
169
|
}
|
|
165
170
|
}
|
|
166
171
|
async function findPageTarget(cdpPort, pageUrl) {
|
|
@@ -225,7 +230,9 @@ async function attachConsoleForwarder(cdpPort, pageUrl) {
|
|
|
225
230
|
});
|
|
226
231
|
ws.on('close', () => {
|
|
227
232
|
// Reconnect after a short delay (page may have reloaded)
|
|
228
|
-
setTimeout(() => {
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
connect();
|
|
235
|
+
}, 1000);
|
|
229
236
|
});
|
|
230
237
|
ws.on('error', () => {
|
|
231
238
|
// Error will trigger close, which handles reconnection
|
|
@@ -249,9 +256,7 @@ async function main() {
|
|
|
249
256
|
// Electron mode keeps the preferred port (external CDP, not launched by us).
|
|
250
257
|
const REQUESTED_CDP_PORT = ELECTRON_MODE ? PREFERRED_CDP_PORT : 0;
|
|
251
258
|
let CDP_PORT = ELECTRON_MODE ? PREFERRED_CDP_PORT : 0;
|
|
252
|
-
const HMR_PORT = DEV_MODE
|
|
253
|
-
? await findAvailablePort(PREFERRED_HMR_PORT)
|
|
254
|
-
: PREFERRED_HMR_PORT;
|
|
259
|
+
const HMR_PORT = DEV_MODE ? await findAvailablePort(PREFERRED_HMR_PORT) : PREFERRED_HMR_PORT;
|
|
255
260
|
const SERVE_ORIGIN = `http://localhost:${SERVE_PORT}`;
|
|
256
261
|
if (SERVE_PORT !== PREFERRED_SERVE_PORT) {
|
|
257
262
|
console.log(`Port ${PREFERRED_SERVE_PORT} in use, serving on port ${SERVE_PORT}`);
|
|
@@ -305,16 +310,18 @@ async function main() {
|
|
|
305
310
|
const leaderOrigin = `http://localhost:${PREFERRED_SERVE_PORT}`;
|
|
306
311
|
for (let attempt = 0; attempt < 5 && !discoveredTrayJoinUrl; attempt++) {
|
|
307
312
|
try {
|
|
308
|
-
const resp = await fetch(`${leaderOrigin}/api/tray-status`, {
|
|
313
|
+
const resp = await fetch(`${leaderOrigin}/api/tray-status`, {
|
|
314
|
+
signal: AbortSignal.timeout(3000),
|
|
315
|
+
});
|
|
309
316
|
if (resp.ok) {
|
|
310
|
-
const status = await resp.json();
|
|
317
|
+
const status = (await resp.json());
|
|
311
318
|
if (status.joinUrl) {
|
|
312
319
|
discoveredTrayJoinUrl = status.joinUrl;
|
|
313
320
|
console.log(`Discovered leader tray join URL: ${status.joinUrl}`);
|
|
314
321
|
}
|
|
315
322
|
else if (status.state === 'connecting') {
|
|
316
323
|
// Leader is still setting up — wait and retry
|
|
317
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
324
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
318
325
|
}
|
|
319
326
|
else {
|
|
320
327
|
console.log(`Leader on port ${PREFERRED_SERVE_PORT} has no active tray (state: ${status.state ?? 'unknown'})`);
|
|
@@ -465,7 +472,7 @@ async function main() {
|
|
|
465
472
|
const requestId = `req_${++requestIdCounter}`;
|
|
466
473
|
const msg = JSON.stringify({ type, requestId, ...data });
|
|
467
474
|
// Find a connected client
|
|
468
|
-
const client = Array.from(lickClients).find(c => c.readyState === WebSocket.OPEN);
|
|
475
|
+
const client = Array.from(lickClients).find((c) => c.readyState === WebSocket.OPEN);
|
|
469
476
|
if (!client) {
|
|
470
477
|
reject(new Error('No browser connected'));
|
|
471
478
|
return;
|
|
@@ -592,7 +599,7 @@ async function main() {
|
|
|
592
599
|
});
|
|
593
600
|
app.delete('/api/webhooks/:id', async (req, res) => {
|
|
594
601
|
try {
|
|
595
|
-
const data = await sendLickRequest('delete_webhook', { id: req.params.id });
|
|
602
|
+
const data = (await sendLickRequest('delete_webhook', { id: req.params.id }));
|
|
596
603
|
if (data.error) {
|
|
597
604
|
res.status(404).json({ error: data.error });
|
|
598
605
|
}
|
|
@@ -659,12 +666,14 @@ async function main() {
|
|
|
659
666
|
}
|
|
660
667
|
catch (err) {
|
|
661
668
|
const msg = err instanceof Error ? err.message : String(err);
|
|
662
|
-
res
|
|
669
|
+
res
|
|
670
|
+
.status(msg.includes('Invalid') || msg.includes('required') ? 400 : 503)
|
|
671
|
+
.json({ error: msg });
|
|
663
672
|
}
|
|
664
673
|
});
|
|
665
674
|
app.delete('/api/crontasks/:id', async (req, res) => {
|
|
666
675
|
try {
|
|
667
|
-
const data = await sendLickRequest('delete_crontask', { id: req.params.id });
|
|
676
|
+
const data = (await sendLickRequest('delete_crontask', { id: req.params.id }));
|
|
668
677
|
if (data.error) {
|
|
669
678
|
res.status(404).json({ error: data.error });
|
|
670
679
|
}
|
|
@@ -705,7 +714,13 @@ async function main() {
|
|
|
705
714
|
redirect: 'follow', // Follow redirects for git protocol compatibility
|
|
706
715
|
};
|
|
707
716
|
// Forward relevant headers (excluding hop-by-hop and proxy headers)
|
|
708
|
-
const skipHeaders = new Set([
|
|
717
|
+
const skipHeaders = new Set([
|
|
718
|
+
'host',
|
|
719
|
+
'connection',
|
|
720
|
+
'x-target-url',
|
|
721
|
+
'content-length',
|
|
722
|
+
'transfer-encoding',
|
|
723
|
+
]);
|
|
709
724
|
const headers = {};
|
|
710
725
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
711
726
|
if (!skipHeaders.has(key) && typeof value === 'string') {
|
|
@@ -732,11 +747,13 @@ async function main() {
|
|
|
732
747
|
// handles 401s through its own onAuth callback)
|
|
733
748
|
upstream.headers.forEach((v, k) => {
|
|
734
749
|
const lower = k.toLowerCase();
|
|
735
|
-
if (lower !== 'transfer-encoding' &&
|
|
750
|
+
if (lower !== 'transfer-encoding' &&
|
|
751
|
+
lower !== 'content-encoding' &&
|
|
752
|
+
lower !== 'www-authenticate') {
|
|
736
753
|
res.setHeader(k, v);
|
|
737
754
|
}
|
|
738
755
|
});
|
|
739
|
-
// Send body as raw binary - explicitly set content-length and use end()
|
|
756
|
+
// Send body as raw binary - explicitly set content-length and use end()
|
|
740
757
|
// instead of send() to avoid any Express middleware transformations
|
|
741
758
|
const body = await upstream.arrayBuffer();
|
|
742
759
|
const buffer = Buffer.from(body);
|
|
@@ -815,14 +832,18 @@ async function main() {
|
|
|
815
832
|
try {
|
|
816
833
|
chromeWs.close();
|
|
817
834
|
}
|
|
818
|
-
catch {
|
|
835
|
+
catch {
|
|
836
|
+
/* ignore */
|
|
837
|
+
}
|
|
819
838
|
chromeWs = null;
|
|
820
839
|
}
|
|
821
840
|
if (activeClientWs) {
|
|
822
841
|
try {
|
|
823
842
|
activeClientWs.close();
|
|
824
843
|
}
|
|
825
|
-
catch {
|
|
844
|
+
catch {
|
|
845
|
+
/* ignore */
|
|
846
|
+
}
|
|
826
847
|
activeClientWs = null;
|
|
827
848
|
}
|
|
828
849
|
for (const client of wss.clients) {
|
|
@@ -833,7 +854,9 @@ async function main() {
|
|
|
833
854
|
server.close();
|
|
834
855
|
if (launchedBrowserProcess) {
|
|
835
856
|
let browserExited = false;
|
|
836
|
-
launchedBrowserProcess.on('exit', () => {
|
|
857
|
+
launchedBrowserProcess.on('exit', () => {
|
|
858
|
+
browserExited = true;
|
|
859
|
+
});
|
|
837
860
|
try {
|
|
838
861
|
const res = await fetch(`http://127.0.0.1:${CDP_PORT}/json/version`);
|
|
839
862
|
const json = (await res.json());
|
|
@@ -857,21 +880,29 @@ async function main() {
|
|
|
857
880
|
try {
|
|
858
881
|
launchedBrowserProcess.kill('SIGKILL');
|
|
859
882
|
}
|
|
860
|
-
catch {
|
|
883
|
+
catch {
|
|
884
|
+
/* ignore */
|
|
885
|
+
}
|
|
861
886
|
}
|
|
862
887
|
console.log(`${launchedBrowserLabel} closed`);
|
|
863
888
|
}
|
|
864
889
|
process.exit(0);
|
|
865
890
|
};
|
|
866
|
-
process.on('SIGINT', () => {
|
|
867
|
-
|
|
891
|
+
process.on('SIGINT', () => {
|
|
892
|
+
gracefulShutdown();
|
|
893
|
+
});
|
|
894
|
+
process.on('SIGTERM', () => {
|
|
895
|
+
gracefulShutdown();
|
|
896
|
+
});
|
|
868
897
|
process.on('exit', () => {
|
|
869
898
|
// Synchronous last-resort cleanup — kill the launched browser if it is still running.
|
|
870
899
|
if (!shuttingDown && launchedBrowserProcess) {
|
|
871
900
|
try {
|
|
872
901
|
launchedBrowserProcess.kill();
|
|
873
902
|
}
|
|
874
|
-
catch {
|
|
903
|
+
catch {
|
|
904
|
+
/* ignore */
|
|
905
|
+
}
|
|
875
906
|
}
|
|
876
907
|
});
|
|
877
908
|
function ensureChromeConnection(url) {
|
|
@@ -892,7 +923,9 @@ async function main() {
|
|
|
892
923
|
try {
|
|
893
924
|
chromeWs.close();
|
|
894
925
|
}
|
|
895
|
-
catch {
|
|
926
|
+
catch {
|
|
927
|
+
/* ignore */
|
|
928
|
+
}
|
|
896
929
|
}
|
|
897
930
|
messageBuffer = [];
|
|
898
931
|
chromeWs = new WebSocket(url);
|
|
@@ -989,7 +1022,12 @@ async function main() {
|
|
|
989
1022
|
server.listen(SERVE_PORT, '127.0.0.1', () => {
|
|
990
1023
|
console.log(`Serving UI at ${SERVE_ORIGIN}`);
|
|
991
1024
|
console.log(`CDP proxy at ws://localhost:${SERVE_PORT}/cdp`);
|
|
992
|
-
fileLogger.log('info', 'CLI server started', {
|
|
1025
|
+
fileLogger.log('info', 'CLI server started', {
|
|
1026
|
+
port: SERVE_PORT,
|
|
1027
|
+
cdpPort: CDP_PORT,
|
|
1028
|
+
devMode: DEV_MODE,
|
|
1029
|
+
electronMode: ELECTRON_MODE,
|
|
1030
|
+
});
|
|
993
1031
|
// Pre-connect to Chrome's CDP so the proxy is warm when the first client connects.
|
|
994
1032
|
// Without this, the first browser automation command has to wait for CDP discovery + WS handshake.
|
|
995
1033
|
(async () => {
|
|
@@ -134,12 +134,10 @@ function crc32(buffer) {
|
|
|
134
134
|
}
|
|
135
135
|
function encodeDosDateTime(value) {
|
|
136
136
|
const year = Math.max(value.getUTCFullYear(), 1980);
|
|
137
|
-
const dosDate = ((year - 1980) << 9)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
| (value.getUTCMinutes() << 5)
|
|
142
|
-
| Math.floor(value.getUTCSeconds() / 2);
|
|
137
|
+
const dosDate = ((year - 1980) << 9) | ((value.getUTCMonth() + 1) << 5) | value.getUTCDate();
|
|
138
|
+
const dosTime = (value.getUTCHours() << 11) |
|
|
139
|
+
(value.getUTCMinutes() << 5) |
|
|
140
|
+
Math.floor(value.getUTCSeconds() / 2);
|
|
143
141
|
return { dosDate, dosTime };
|
|
144
142
|
}
|
|
145
143
|
function readJsonFile(path) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as e}from"./__vite-browser-external-D7Ct-6yo.js";import{g as r}from"./index-
|
|
1
|
+
import{_ as e}from"./__vite-browser-external-D7Ct-6yo.js";import{g as r}from"./index-BOh9kRaE.js";const a=r(e);export{a as r};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{c as u}from"./index-
|
|
1
|
+
import{c as u}from"./index-BOh9kRaE.js";const l=["/workspace","/shared"];async function d(s){const t=[],e=new Set;for(const r of l)await s.exists(r)&&await p(s,r,t,e);return t}async function p(s,t,e,r){for await(const n of s.walk(t)){if(!n.endsWith(".bsh")||r.has(n))continue;r.add(n);const i=g(n);if(!i)continue;const a=await s.readFile(n,{encoding:"utf-8"}),c=typeof a=="string"?a:new TextDecoder().decode(a),f=m(c);e.push({path:n,hostnamePattern:i,matchPatterns:f})}}function g(s){const t=s.split("/").pop()??"";if(!t.endsWith(".bsh"))return null;const e=t.slice(0,-4);return e?e.startsWith("-.")?"*"+e.slice(1):e:null}function m(s){const t=s.split(`
|
|
2
2
|
`).slice(0,10),e=[];for(const r of t){const n=r.match(/^\s*\/\/\s*@match\s+(.+)$/);n&&e.push(n[1].trim())}return e}function h(s,t){if(t.startsWith("*.")){const e=t.slice(1);return s.endsWith(e)&&s.length>e.length}return s===t}function v(s,t){try{const e=new URL(s),r=t.match(/^(\*|https?):\/\/([^/]+)(\/.*)?$/);if(!r)return!1;const[,n,i,a]=r;return n!=="*"&&e.protocol.slice(0,-1)!==n||!h(e.hostname,i)?!1:a?x(e.pathname+e.search,a):!0}catch{return!1}}function x(s,t){const e="^"+t.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")+"$";return new RegExp(e).test(s)}function w(s,t){try{const e=new URL(t);return s.filter(r=>h(e.hostname,r.hostnamePattern)?r.matchPatterns.length>0?r.matchPatterns.some(n=>v(t,n)):!0:!1)}catch{return[]}}const o=u("bsh-watchdog");class S{transport;fs;execute;discoveryIntervalMs;entries=[];discoveryTimer=null;running=!1;executing=new Set;constructor(t){this.transport=t.transport,this.fs=t.fs,this.execute=t.execute,this.discoveryIntervalMs=t.discoveryIntervalMs??3e4}async start(){this.running||(this.running=!0,await this.discover(),this.discoveryTimer=setInterval(()=>{this.discover()},this.discoveryIntervalMs),this.transport.on("Page.frameNavigated",this.onFrameNavigated),o.info("BSH watchdog started",{scriptCount:this.entries.length}))}stop(){this.running&&(this.running=!1,this.transport.off("Page.frameNavigated",this.onFrameNavigated),this.discoveryTimer&&(clearInterval(this.discoveryTimer),this.discoveryTimer=null),this.entries=[],this.executing.clear(),o.info("BSH watchdog stopped"))}async discover(){try{this.entries=await d(this.fs),o.debug("BSH discovery complete",{count:this.entries.length})}catch(t){o.error("BSH discovery failed",{error:t instanceof Error?t.message:String(t)})}}getEntries(){return this.entries}onFrameNavigated=t=>{const e=t.frame;if(e?.parentId||!e?.url)return;const r=e.url;if(!r.startsWith("http://")&&!r.startsWith("https://")||this.entries.length===0)return;const n=w(this.entries,r);if(n.length!==0)for(const i of n){const a=`${i.path}::${r}`;this.executing.has(a)||(this.executing.add(a),o.info("BSH watchdog executing script",{script:i.path,url:r}),this.execute(i.path).then(c=>{c.exitCode!==0?o.warn("BSH script failed",{script:i.path,url:r,exitCode:c.exitCode,stderr:c.stderr.slice(0,200)}):o.info("BSH script completed",{script:i.path,url:r})}).catch(c=>{o.error("BSH script execution error",{script:i.path,url:r,error:c instanceof Error?c.message:String(c)})}).finally(()=>{this.executing.delete(a)}))}}}export{S as BshWatchdog};
|