sliccy 1.4.1 → 1.6.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/README.md CHANGED
@@ -1,4 +1,13 @@
1
- ![slicc - A felt-toy of an anthropomorphized ice cream cone, with pink and mint-green colors for the scoop, googly eyes, an oversized mouth and tongue sticking out](hero-banner.png)
1
+ ![A screenshot of a macOS desktop](screenshots/full-desktop.png)
2
+
3
+ You are looking at a macOS desktop, with four windows running:
4
+
5
+ 1. Google Chrome, running SLICC as a web application. It shows a Welcome page, a hidden tab with meeting preparation notes that were created by the agent, and a terminal, showing that the operating system is of the unlikely `Mozilla/5.0` kind. What?
6
+ 2. Slack, the desktop app. Err. Slack the Electron app. It has on overlay injected, showing the ice cream logo asking to join a tray. If you do this, Slack can be remote-controlled by your agent. What the?
7
+ 3. Sliccstart, the desktop app. It's an actual macOS app, but one that controls browsers, and browsers that prentend to be native apps alike. What the ice cream?
8
+ 4. An image of an antropomorphized ice cream cone made out of felt and googly eyes. It's sticking out its tongue, half in astonishment, half in anticipation. What the ice cream truck?
9
+
10
+ If this scares, confuses, or excites you, keep reading.
2
11
 
3
12
  # slicc — Self-Licking Ice Cream Cone
4
13
 
@@ -8,13 +17,14 @@
8
17
 
9
18
  SLICC runs in a browser and controls the browser it runs in. It combines a shell, files, browser automation, and multi-agent delegation so you can do real work from one workspace — coding, web automation, authenticated app tasks, and the weird in-between jobs that do not fit neatly inside a chat panel. SLICC can orchestrate multiple browsers, and even some apps through telepathy, making it a powerful hub for your digital work.
10
19
 
11
- - Launch it from the CLI today (we also have a Chrome extension)
20
+ - Head over to [releases](https://github.com/ai-ecoverse/slicc/releases) and grab the latest `.dmg` file. No Windows or Linux UI yet
21
+ - Or launch it from the CLI today (we also have a Chrome extension)
12
22
  - Connect other browser windows or Electron apps
13
23
  - Install skills that teach it how to perform challenging tasks
14
24
  - Give it practical tools models already know how to use
15
25
  - Delegate parallel work so tasks get done faster
16
26
 
17
- > Status: active working prototype. The CLI is the easiest way in today; and we have submitted the extension to Chrome Web Store.
27
+ > Status: active working prototype. The macOS app is the easiest way in today; and we have submitted the extension to Chrome Web Store.
18
28
 
19
29
  ## Why SLICC is different
20
30
 
@@ -17,6 +17,20 @@ export declare function launchElectronApp(options: {
17
17
  child: ChildProcess;
18
18
  displayName: string;
19
19
  }>;
20
+ /**
21
+ * Decode a base64 PNG into raw RGBA pixel data by parsing chunks and inflating.
22
+ * Returns { width, height, pixels } where pixels is a Buffer of RGBA bytes.
23
+ */
24
+ export declare function decodePngPixels(base64Data: string): {
25
+ width: number;
26
+ height: number;
27
+ pixels: Buffer;
28
+ };
29
+ /**
30
+ * Compute the average perceived luminance (0–255) from RGBA pixel data,
31
+ * sampling a grid of pixels for performance.
32
+ */
33
+ export declare function computeAverageLuminance(pixels: Buffer, width: number, height: number, sampleStep?: number): number;
20
34
  export declare class ElectronOverlayInjector {
21
35
  private readonly cdpPort;
22
36
  private readonly bootstrapScript;
@@ -4,6 +4,7 @@ import { readFile } from 'fs/promises';
4
4
  import * as http from 'http';
5
5
  import * as https from 'https';
6
6
  import { promisify } from 'util';
7
+ import { inflateSync } from 'zlib';
7
8
  import { WebSocket } from 'ws';
8
9
  import { buildElectronAppLaunchSpec, buildElectronOverlayAppUrl, buildElectronOverlayBootstrapScript, buildElectronOverlayEntryUrl, getElectronOverlayEntryDistPath, getElectronServeOrigin, selectBestOverlayTargets, } from './electron-runtime.js';
9
10
  const execFile = promisify(nodeExecFile);
@@ -170,6 +171,175 @@ export async function launchElectronApp(options) {
170
171
  displayName: launchSpec.displayName,
171
172
  };
172
173
  }
174
+ // ---------------------------------------------------------------------------
175
+ // Theme detection — screenshot-based luminance analysis
176
+ // ---------------------------------------------------------------------------
177
+ /**
178
+ * Decode a base64 PNG into raw RGBA pixel data by parsing chunks and inflating.
179
+ * Returns { width, height, pixels } where pixels is a Buffer of RGBA bytes.
180
+ */
181
+ export function decodePngPixels(base64Data) {
182
+ const buf = Buffer.from(base64Data, 'base64');
183
+ // Validate PNG signature
184
+ const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
185
+ if (buf.subarray(0, 8).compare(PNG_SIGNATURE) !== 0) {
186
+ throw new Error('Not a valid PNG');
187
+ }
188
+ let width = 0;
189
+ let height = 0;
190
+ let bitDepth = 0;
191
+ let colorType = 0;
192
+ const idatChunks = [];
193
+ let offset = 8;
194
+ while (offset < buf.length) {
195
+ const chunkLength = buf.readUInt32BE(offset);
196
+ const chunkType = buf.subarray(offset + 4, offset + 8).toString('ascii');
197
+ const chunkData = buf.subarray(offset + 8, offset + 8 + chunkLength);
198
+ if (chunkType === 'IHDR') {
199
+ width = chunkData.readUInt32BE(0);
200
+ height = chunkData.readUInt32BE(4);
201
+ bitDepth = chunkData[8];
202
+ colorType = chunkData[9];
203
+ }
204
+ else if (chunkType === 'IDAT') {
205
+ idatChunks.push(chunkData);
206
+ }
207
+ else if (chunkType === 'IEND') {
208
+ break;
209
+ }
210
+ offset += 12 + chunkLength; // 4 (length) + 4 (type) + data + 4 (CRC)
211
+ }
212
+ if (width === 0 || height === 0)
213
+ throw new Error('Missing IHDR chunk');
214
+ if (bitDepth !== 8)
215
+ throw new Error(`Unsupported bit depth: ${bitDepth}`);
216
+ // Only support RGB (2) and RGBA (6) — CDP screenshots are always RGBA
217
+ const bytesPerPixel = colorType === 6 ? 4 : colorType === 2 ? 3 : 0;
218
+ if (bytesPerPixel === 0)
219
+ throw new Error(`Unsupported color type: ${colorType}`);
220
+ const compressed = Buffer.concat(idatChunks);
221
+ const inflated = inflateSync(compressed);
222
+ // Each row has a 1-byte filter prefix followed by pixel data
223
+ const rowBytes = width * bytesPerPixel;
224
+ const pixels = Buffer.alloc(width * height * 4); // Always output RGBA
225
+ let prevRow = Buffer.alloc(rowBytes);
226
+ for (let y = 0; y < height; y++) {
227
+ const rowStart = y * (1 + rowBytes);
228
+ const filter = inflated[rowStart];
229
+ const row = Buffer.from(inflated.subarray(rowStart + 1, rowStart + 1 + rowBytes));
230
+ // Apply PNG row filters
231
+ for (let i = 0; i < rowBytes; i++) {
232
+ const a = i >= bytesPerPixel ? row[i - bytesPerPixel] : 0;
233
+ const b = prevRow[i];
234
+ const c = i >= bytesPerPixel ? prevRow[i - bytesPerPixel] : 0;
235
+ switch (filter) {
236
+ case 1: // Sub
237
+ row[i] = (row[i] + a) & 0xff;
238
+ break;
239
+ case 2: // Up
240
+ row[i] = (row[i] + b) & 0xff;
241
+ break;
242
+ case 3: // Average
243
+ row[i] = (row[i] + ((a + b) >>> 1)) & 0xff;
244
+ break;
245
+ case 4: { // Paeth
246
+ const p = a + b - c;
247
+ const pa = Math.abs(p - a);
248
+ const pb = Math.abs(p - b);
249
+ const pc = Math.abs(p - c);
250
+ row[i] = (row[i] + (pa <= pb && pa <= pc ? a : pb <= pc ? b : c)) & 0xff;
251
+ break;
252
+ }
253
+ // case 0: None — no transformation needed
254
+ }
255
+ }
256
+ for (let x = 0; x < width; x++) {
257
+ const srcIdx = x * bytesPerPixel;
258
+ const dstIdx = (y * width + x) * 4;
259
+ pixels[dstIdx] = row[srcIdx]; // R
260
+ pixels[dstIdx + 1] = row[srcIdx + 1]; // G
261
+ pixels[dstIdx + 2] = row[srcIdx + 2]; // B
262
+ pixels[dstIdx + 3] = bytesPerPixel === 4 ? row[srcIdx + 3] : 255; // A
263
+ }
264
+ prevRow = row;
265
+ }
266
+ return { width, height, pixels };
267
+ }
268
+ /**
269
+ * Compute the average perceived luminance (0–255) from RGBA pixel data,
270
+ * sampling a grid of pixels for performance.
271
+ */
272
+ export function computeAverageLuminance(pixels, width, height, sampleStep = 4) {
273
+ let totalLuminance = 0;
274
+ let sampleCount = 0;
275
+ for (let y = 0; y < height; y += sampleStep) {
276
+ for (let x = 0; x < width; x += sampleStep) {
277
+ const idx = (y * width + x) * 4;
278
+ const r = pixels[idx];
279
+ const g = pixels[idx + 1];
280
+ const b = pixels[idx + 2];
281
+ // ITU-R BT.601 perceived luminance
282
+ totalLuminance += 0.299 * r + 0.587 * g + 0.114 * b;
283
+ sampleCount++;
284
+ }
285
+ }
286
+ return sampleCount > 0 ? totalLuminance / sampleCount : 128;
287
+ }
288
+ /**
289
+ * Detect whether the target app is using a light or dark theme by taking
290
+ * a CDP screenshot and analyzing the average luminance.
291
+ * Returns 'light' or 'dark'.
292
+ */
293
+ function detectAppThemeFromScreenshot(ws, send) {
294
+ return new Promise((resolve) => {
295
+ // Take a small JPEG screenshot for speed — we only need luminance
296
+ const screenshotId = send('Page.captureScreenshot', {
297
+ format: 'png',
298
+ quality: 30,
299
+ clip: { x: 0, y: 0, width: 160, height: 120, scale: 0.25 },
300
+ optimizeForSpeed: true,
301
+ });
302
+ const timeout = setTimeout(() => {
303
+ cleanup();
304
+ console.log('[electron-float] Theme detection timed out, defaulting to dark');
305
+ resolve('dark');
306
+ }, 5000);
307
+ const onMessage = (data) => {
308
+ try {
309
+ const msg = JSON.parse(data.toString());
310
+ if (msg.id !== screenshotId)
311
+ return;
312
+ cleanup();
313
+ const base64 = msg.result?.data;
314
+ if (!base64) {
315
+ console.log('[electron-float] Theme detection: no screenshot data, defaulting to dark');
316
+ resolve('dark');
317
+ return;
318
+ }
319
+ try {
320
+ const { width, height, pixels } = decodePngPixels(base64);
321
+ const luminance = computeAverageLuminance(pixels, width, height);
322
+ const theme = luminance > 128 ? 'light' : 'dark';
323
+ console.log(`[electron-float] Theme detection: luminance=${luminance.toFixed(1)}, theme=${theme} (${width}x${height})`);
324
+ resolve(theme);
325
+ }
326
+ catch (decodeError) {
327
+ const message = decodeError instanceof Error ? decodeError.message : String(decodeError);
328
+ console.error('[electron-float] Theme detection decode failed:', message);
329
+ resolve('dark');
330
+ }
331
+ }
332
+ catch {
333
+ /* ignore non-JSON messages */
334
+ }
335
+ };
336
+ const cleanup = () => {
337
+ clearTimeout(timeout);
338
+ ws.off('message', onMessage);
339
+ };
340
+ ws.on('message', onMessage);
341
+ });
342
+ }
173
343
  async function loadElectronOverlayBundleSource(options) {
174
344
  const serveOrigin = getElectronServeOrigin(options.servePort);
175
345
  if (options.dev) {
@@ -323,6 +493,14 @@ export class ElectronOverlayInjector {
323
493
  let pendingReload = false;
324
494
  let pendingCspEscalation = false;
325
495
  let fetchProxyActive = false;
496
+ /**
497
+ * Build a script that sets the SLICC theme preference in localStorage
498
+ * to match the target app's detected theme, then runs the bootstrap.
499
+ */
500
+ const buildThemedBootstrap = (theme) => {
501
+ const themeScript = `try{localStorage.setItem('slicc-theme',${JSON.stringify(theme)})}catch(e){}`;
502
+ return `${themeScript}\n${bootstrapScript}`;
503
+ };
326
504
  ws.on('open', () => {
327
505
  const isWebContent = target.url.startsWith('https://');
328
506
  const alreadyBypassed = cspBypassedTargets.has(target.url);
@@ -332,39 +510,48 @@ export class ElectronOverlayInjector {
332
510
  // Set CSP bypass — affects future resource loads on the current page
333
511
  send('Page.setBypassCSP', { enabled: true });
334
512
  if (alreadyBypassed) {
335
- // Already reloaded with CSP bypass previously — just inject
336
- console.log(`[electron-float] Injecting overlay (CSP already bypassed)...`);
337
- send('Runtime.evaluate', { expression: bootstrapScript, awaitPromise: false });
338
- return;
339
- }
340
- // First connection to this target URL: inject overlay immediately, then
341
- // check if the iframe loaded. If CSP blocked it, fall back to reload+proxy.
342
- console.log(`[electron-float] Injecting overlay (first attempt)...`);
343
- send('Runtime.evaluate', { expression: bootstrapScript, awaitPromise: false });
344
- if (!isWebContent) {
345
- // Local content (file://, app protocol) — CSP is not an issue
513
+ // Already reloaded with CSP bypass previously — detect theme and inject
514
+ console.log(`[electron-float] Detecting theme and injecting overlay (CSP already bypassed)...`);
515
+ void detectAppThemeFromScreenshot(ws, send).then((theme) => {
516
+ if (ws.readyState !== WebSocket.OPEN)
517
+ return;
518
+ send('Runtime.evaluate', { expression: buildThemedBootstrap(theme), awaitPromise: false });
519
+ });
346
520
  return;
347
521
  }
348
- // After a short delay, probe whether the overlay iframe loaded.
349
- // If CSP blocked it, reload the page so Page.setBypassCSP takes effect.
350
- // If that still doesn't work, escalate to the Fetch proxy.
351
- setTimeout(async () => {
522
+ // First connection to this target URL: detect theme, then inject overlay.
523
+ // After injection, check if the iframe loaded. If CSP blocked it, fall back to reload+proxy.
524
+ console.log(`[electron-float] Detecting theme before first overlay injection...`);
525
+ void detectAppThemeFromScreenshot(ws, send).then((theme) => {
352
526
  if (ws.readyState !== WebSocket.OPEN)
353
527
  return;
354
- const loaded = await this.probeOverlayIframeLoaded(ws, send);
355
- if (loaded) {
356
- console.log(`[electron-float] Overlay iframe loaded successfully — no CSP reload needed`);
357
- cspBypassedTargets.add(target.url);
528
+ console.log(`[electron-float] Injecting overlay (first attempt, theme=${theme})...`);
529
+ send('Runtime.evaluate', { expression: buildThemedBootstrap(theme), awaitPromise: false });
530
+ if (!isWebContent) {
531
+ // Local content (file://, app protocol) — CSP is not an issue
358
532
  return;
359
533
  }
360
- // Phase 2: Page.setBypassCSP was already set a simple reload should
361
- // make the browser ignore CSP headers on the fresh navigation.
362
- console.log(`[electron-float] Overlay iframe blocked by CSP, reloading with bypass: ${target.url}`);
363
- cspBypassedTargets.add(target.url);
364
- pendingReload = true;
365
- pendingCspEscalation = true;
366
- send('Page.reload', { ignoreCache: true });
367
- }, 1500);
534
+ // After a short delay, probe whether the overlay iframe loaded.
535
+ // If CSP blocked it, reload the page so Page.setBypassCSP takes effect.
536
+ // If that still doesn't work, escalate to the Fetch proxy.
537
+ setTimeout(async () => {
538
+ if (ws.readyState !== WebSocket.OPEN)
539
+ return;
540
+ const loaded = await this.probeOverlayIframeLoaded(ws, send);
541
+ if (loaded) {
542
+ console.log(`[electron-float] Overlay iframe loaded successfully — no CSP reload needed`);
543
+ cspBypassedTargets.add(target.url);
544
+ return;
545
+ }
546
+ // Phase 2: Page.setBypassCSP was already set — a simple reload should
547
+ // make the browser ignore CSP headers on the fresh navigation.
548
+ console.log(`[electron-float] Overlay iframe blocked by CSP, reloading with bypass: ${target.url}`);
549
+ cspBypassedTargets.add(target.url);
550
+ pendingReload = true;
551
+ pendingCspEscalation = true;
552
+ send('Page.reload', { ignoreCache: true });
553
+ }, 1500);
554
+ });
368
555
  });
369
556
  // Handle CDP events: lifecycle events and Fetch interception
370
557
  ws.on('message', (data) => {
@@ -373,10 +560,14 @@ export class ElectronOverlayInjector {
373
560
  // Inject overlay after page load completes (after CSP-bypass reload)
374
561
  if (msg.method === 'Page.loadEventFired' && pendingReload) {
375
562
  pendingReload = false;
376
- console.log(`[electron-float] Page loaded after CSP reload, injecting overlay...`);
563
+ console.log(`[electron-float] Page loaded after CSP reload, detecting theme and injecting overlay...`);
377
564
  if (ws.readyState !== WebSocket.OPEN)
378
565
  return;
379
- send('Runtime.evaluate', { expression: bootstrapScript, awaitPromise: false });
566
+ void detectAppThemeFromScreenshot(ws, send).then((theme) => {
567
+ if (ws.readyState !== WebSocket.OPEN)
568
+ return;
569
+ send('Runtime.evaluate', { expression: buildThemedBootstrap(theme), awaitPromise: false });
570
+ });
380
571
  // If this was a simple reload (no proxy), check if the iframe loads now.
381
572
  // If it still doesn't, escalate to the Fetch proxy as a last resort.
382
573
  if (pendingCspEscalation) {
@@ -2,7 +2,7 @@ import { spawn } from 'child_process';
2
2
  import { readFile } from 'fs/promises';
3
3
  import { resolve } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import { app, BrowserWindow, session } from 'electron';
5
+ import { app, BrowserWindow, nativeTheme, session } from 'electron';
6
6
  import { buildElectronOverlayAppUrl, buildElectronOverlayEntryUrl, buildElectronOverlayInjectionCall, buildElectronServerSpawnConfig, getElectronOverlayEntryDistPath, getElectronServeOrigin, parseElectronFloatFlags, } from './electron-runtime.js';
7
7
  const __dirname = fileURLToPath(new URL('.', import.meta.url));
8
8
  const PROJECT_ROOT = resolve(__dirname, '..', '..');
@@ -47,7 +47,10 @@ async function loadOverlayBundleSource() {
47
47
  }
48
48
  async function injectOverlay(window) {
49
49
  const bundleSource = await loadOverlayBundleSource();
50
- await window.webContents.executeJavaScript(bundleSource, true);
50
+ // Detect the app's effective theme and set SLICC's theme to match
51
+ const theme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
52
+ const themeScript = `try{localStorage.setItem('slicc-theme',${JSON.stringify(theme)})}catch(e){}`;
53
+ await window.webContents.executeJavaScript(`${themeScript}\n${bundleSource}`, true);
51
54
  await window.webContents.executeJavaScript(buildElectronOverlayInjectionCall({ appUrl: OVERLAY_APP_URL }), true);
52
55
  }
53
56
  function wireOverlayReinjection(window) {
@@ -1 +1 @@
1
- import{_ as e}from"./__vite-browser-external-D7Ct-6yo.js";import{g as r}from"./index-DLbRRCUW.js";const a=r(e);export{a as r};
1
+ import{_ as e}from"./__vite-browser-external-D7Ct-6yo.js";import{g as r}from"./index-C1tZMpET.js";const a=r(e);export{a as r};
@@ -1,2 +1,2 @@
1
- import{c as u}from"./index-DLbRRCUW.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(`
1
+ import{c as u}from"./index-C1tZMpET.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};
@@ -1 +1 @@
1
- import{t as z,f as x}from"./index-DLbRRCUW.js";class T{marshaller;serializer;deserializer;serdeContext;defaultContentType;constructor({marshaller:i,serializer:n,deserializer:o,serdeContext:c,defaultContentType:y}){this.marshaller=i,this.serializer=n,this.deserializer=o,this.serdeContext=c,this.defaultContentType=y}async serializeEventStream({eventStream:i,requestSchema:n,initialRequest:o}){const c=this.marshaller,y=n.getEventStreamMember(),p=n.getMemberSchema(y),d=this.serializer,u=this.defaultContentType,h=Symbol("initialRequestMarker"),S={async*[Symbol.asyncIterator](){if(o){const r={":event-type":{type:"string",value:"initial-request"},":message-type":{type:"string",value:"event"},":content-type":{type:"string",value:u}};d.write(n,o);const t=d.flush();yield{[h]:!0,headers:r,body:t}}for await(const r of i)yield r}};return c.serialize(S,r=>{if(r[h])return{headers:r.headers,body:r.body};const t=Object.keys(r).find(s=>s!=="__type")??"",{additionalHeaders:e,body:a,eventType:l,explicitPayloadContentType:m}=this.writeEventBody(t,p,r);return{headers:{":event-type":{type:"string",value:l},":message-type":{type:"string",value:"event"},":content-type":{type:"string",value:m??u},...e},body:a}})}async deserializeEventStream({response:i,responseSchema:n,initialResponseContainer:o}){const c=this.marshaller,y=n.getEventStreamMember(),d=n.getMemberSchema(y).getMemberSchemas(),u=Symbol("initialResponseMarker"),h=c.deserialize(i.body,async t=>{const e=Object.keys(t).find(l=>l!=="__type")??"",a=t[e].body;if(e==="initial-response"){const l=await this.deserializer.read(n,a);return delete l[y],{[u]:!0,...l}}else if(e in d){const l=d[e];if(l.isStructSchema()){const m={};let f=!1;for(const[s,g]of l.structIterator()){const{eventHeader:v,eventPayload:w}=g.getMergedTraits();if(f=f||!!(v||w),w)g.isBlobSchema()?m[s]=a:g.isStringSchema()?m[s]=(this.serdeContext?.utf8Encoder??z)(a):g.isStructSchema()&&(m[s]=await this.deserializer.read(g,a));else if(v){const b=t[e].headers[s]?.value;b!=null&&(g.isNumericSchema()?b&&typeof b=="object"&&"bytes"in b?m[s]=BigInt(b.toString()):m[s]=Number(b):m[s]=b)}}if(f)return{[e]:m};if(a.byteLength===0)return{[e]:{}}}return{[e]:await this.deserializer.read(l,a)}}else return{$unknown:t}}),S=h[Symbol.asyncIterator](),r=await S.next();if(r.done)return h;if(r.value?.[u]){if(!n)throw new Error("@smithy::core/protocols - initial-response event encountered in event stream but no response schema given.");for(const[t,e]of Object.entries(r.value))o[t]=e}return{async*[Symbol.asyncIterator](){for(r?.value?.[u]||(yield r.value);;){const{done:t,value:e}=await S.next();if(t)break;yield e}}}}writeEventBody(i,n,o){const c=this.serializer;let y=i,p=null,d;const u=n.getSchema()[4].includes(i),h={};if(u){const t=n.getMemberSchema(i);if(t.isStructSchema()){for(const[e,a]of t.structIterator()){const{eventHeader:l,eventPayload:m}=a.getMergedTraits();if(m)p=e;else if(l){const f=o[i][e];let s="binary";a.isNumericSchema()?(-2)**31<=f&&f<=2**31-1?s="integer":s="long":a.isTimestampSchema()?s="timestamp":a.isStringSchema()?s="string":a.isBooleanSchema()&&(s="boolean"),f!=null&&(h[e]={type:s,value:f},delete o[i][e])}}if(p!==null){const e=t.getMemberSchema(p);e.isBlobSchema()?d="application/octet-stream":e.isStringSchema()&&(d="text/plain"),c.write(e,o[i][p])}else c.write(t,o[i])}else if(t.isUnitSchema())c.write(t,{});else throw new Error("@smithy/core/event-streams - non-struct member not supported in event stream union.")}else{const[t,e]=o[i];y=t,c.write(15,e)}const S=c.flush();return{body:typeof S=="string"?(this.serdeContext?.utf8Decoder??x)(S):S,eventType:y,explicitPayloadContentType:d,additionalHeaders:h}}}export{T as EventStreamSerde};
1
+ import{t as z,f as x}from"./index-C1tZMpET.js";class T{marshaller;serializer;deserializer;serdeContext;defaultContentType;constructor({marshaller:i,serializer:n,deserializer:o,serdeContext:c,defaultContentType:y}){this.marshaller=i,this.serializer=n,this.deserializer=o,this.serdeContext=c,this.defaultContentType=y}async serializeEventStream({eventStream:i,requestSchema:n,initialRequest:o}){const c=this.marshaller,y=n.getEventStreamMember(),p=n.getMemberSchema(y),d=this.serializer,u=this.defaultContentType,h=Symbol("initialRequestMarker"),S={async*[Symbol.asyncIterator](){if(o){const r={":event-type":{type:"string",value:"initial-request"},":message-type":{type:"string",value:"event"},":content-type":{type:"string",value:u}};d.write(n,o);const t=d.flush();yield{[h]:!0,headers:r,body:t}}for await(const r of i)yield r}};return c.serialize(S,r=>{if(r[h])return{headers:r.headers,body:r.body};const t=Object.keys(r).find(s=>s!=="__type")??"",{additionalHeaders:e,body:a,eventType:l,explicitPayloadContentType:m}=this.writeEventBody(t,p,r);return{headers:{":event-type":{type:"string",value:l},":message-type":{type:"string",value:"event"},":content-type":{type:"string",value:m??u},...e},body:a}})}async deserializeEventStream({response:i,responseSchema:n,initialResponseContainer:o}){const c=this.marshaller,y=n.getEventStreamMember(),d=n.getMemberSchema(y).getMemberSchemas(),u=Symbol("initialResponseMarker"),h=c.deserialize(i.body,async t=>{const e=Object.keys(t).find(l=>l!=="__type")??"",a=t[e].body;if(e==="initial-response"){const l=await this.deserializer.read(n,a);return delete l[y],{[u]:!0,...l}}else if(e in d){const l=d[e];if(l.isStructSchema()){const m={};let f=!1;for(const[s,g]of l.structIterator()){const{eventHeader:v,eventPayload:w}=g.getMergedTraits();if(f=f||!!(v||w),w)g.isBlobSchema()?m[s]=a:g.isStringSchema()?m[s]=(this.serdeContext?.utf8Encoder??z)(a):g.isStructSchema()&&(m[s]=await this.deserializer.read(g,a));else if(v){const b=t[e].headers[s]?.value;b!=null&&(g.isNumericSchema()?b&&typeof b=="object"&&"bytes"in b?m[s]=BigInt(b.toString()):m[s]=Number(b):m[s]=b)}}if(f)return{[e]:m};if(a.byteLength===0)return{[e]:{}}}return{[e]:await this.deserializer.read(l,a)}}else return{$unknown:t}}),S=h[Symbol.asyncIterator](),r=await S.next();if(r.done)return h;if(r.value?.[u]){if(!n)throw new Error("@smithy::core/protocols - initial-response event encountered in event stream but no response schema given.");for(const[t,e]of Object.entries(r.value))o[t]=e}return{async*[Symbol.asyncIterator](){for(r?.value?.[u]||(yield r.value);;){const{done:t,value:e}=await S.next();if(t)break;yield e}}}}writeEventBody(i,n,o){const c=this.serializer;let y=i,p=null,d;const u=n.getSchema()[4].includes(i),h={};if(u){const t=n.getMemberSchema(i);if(t.isStructSchema()){for(const[e,a]of t.structIterator()){const{eventHeader:l,eventPayload:m}=a.getMergedTraits();if(m)p=e;else if(l){const f=o[i][e];let s="binary";a.isNumericSchema()?(-2)**31<=f&&f<=2**31-1?s="integer":s="long":a.isTimestampSchema()?s="timestamp":a.isStringSchema()?s="string":a.isBooleanSchema()&&(s="boolean"),f!=null&&(h[e]={type:s,value:f},delete o[i][e])}}if(p!==null){const e=t.getMemberSchema(p);e.isBlobSchema()?d="application/octet-stream":e.isStringSchema()&&(d="text/plain"),c.write(e,o[i][p])}else c.write(t,o[i])}else if(t.isUnitSchema())c.write(t,{});else throw new Error("@smithy/core/event-streams - non-struct member not supported in event stream union.")}else{const[t,e]=o[i];y=t,c.write(15,e)}const S=c.flush();return{body:typeof S=="string"?(this.serdeContext?.utf8Decoder??x)(S):S,eventType:y,explicitPayloadContentType:d,additionalHeaders:h}}}export{T as EventStreamSerde};