@silbercue/chrome 0.2.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/LICENSE +21 -0
- package/README.md +229 -0
- package/build/cache/a11y-tree.d.ts +252 -0
- package/build/cache/a11y-tree.js +1956 -0
- package/build/cache/index.d.ts +8 -0
- package/build/cache/index.js +4 -0
- package/build/cache/selector-cache.d.ts +47 -0
- package/build/cache/selector-cache.js +119 -0
- package/build/cache/session-defaults.d.ts +27 -0
- package/build/cache/session-defaults.js +130 -0
- package/build/cache/tab-state-cache.d.ts +39 -0
- package/build/cache/tab-state-cache.js +171 -0
- package/build/cdp/cdp-client.d.ts +25 -0
- package/build/cdp/cdp-client.js +146 -0
- package/build/cdp/chrome-launcher.d.ts +85 -0
- package/build/cdp/chrome-launcher.js +502 -0
- package/build/cdp/console-collector.d.ts +53 -0
- package/build/cdp/console-collector.js +147 -0
- package/build/cdp/debug.d.ts +1 -0
- package/build/cdp/debug.js +6 -0
- package/build/cdp/dialog-handler.d.ts +54 -0
- package/build/cdp/dialog-handler.js +129 -0
- package/build/cdp/dom-watcher.d.ts +45 -0
- package/build/cdp/dom-watcher.js +195 -0
- package/build/cdp/emulation.d.ts +12 -0
- package/build/cdp/emulation.js +17 -0
- package/build/cdp/index.d.ts +11 -0
- package/build/cdp/index.js +6 -0
- package/build/cdp/network-collector.d.ts +77 -0
- package/build/cdp/network-collector.js +257 -0
- package/build/cdp/protocol.d.ts +20 -0
- package/build/cdp/protocol.js +1 -0
- package/build/cdp/session-manager.d.ts +62 -0
- package/build/cdp/session-manager.js +205 -0
- package/build/cdp/settle.d.ts +16 -0
- package/build/cdp/settle.js +71 -0
- package/build/cli/license-commands.d.ts +19 -0
- package/build/cli/license-commands.js +199 -0
- package/build/cli/top-level-commands.d.ts +49 -0
- package/build/cli/top-level-commands.js +222 -0
- package/build/hooks/index.d.ts +2 -0
- package/build/hooks/index.js +1 -0
- package/build/hooks/pro-hooks.d.ts +126 -0
- package/build/hooks/pro-hooks.js +17 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +86 -0
- package/build/license/free-tier-config.d.ts +14 -0
- package/build/license/free-tier-config.js +18 -0
- package/build/license/index.d.ts +4 -0
- package/build/license/index.js +2 -0
- package/build/license/license-status.d.ts +15 -0
- package/build/license/license-status.js +9 -0
- package/build/overlay/session-overlay.d.ts +22 -0
- package/build/overlay/session-overlay.js +372 -0
- package/build/plan/index.d.ts +7 -0
- package/build/plan/index.js +4 -0
- package/build/plan/plan-conditions.d.ts +12 -0
- package/build/plan/plan-conditions.js +242 -0
- package/build/plan/plan-executor.d.ts +49 -0
- package/build/plan/plan-executor.js +259 -0
- package/build/plan/plan-state-store.d.ts +24 -0
- package/build/plan/plan-state-store.js +43 -0
- package/build/plan/plan-variables.d.ts +16 -0
- package/build/plan/plan-variables.js +71 -0
- package/build/registry.d.ts +124 -0
- package/build/registry.js +884 -0
- package/build/server.d.ts +1 -0
- package/build/server.js +245 -0
- package/build/tools/click.d.ts +34 -0
- package/build/tools/click.js +293 -0
- package/build/tools/configure-session.d.ts +15 -0
- package/build/tools/configure-session.js +45 -0
- package/build/tools/console-logs.d.ts +18 -0
- package/build/tools/console-logs.js +44 -0
- package/build/tools/dom-snapshot.d.ts +13 -0
- package/build/tools/dom-snapshot.js +259 -0
- package/build/tools/element-utils.d.ts +23 -0
- package/build/tools/element-utils.js +133 -0
- package/build/tools/error-utils.d.ts +8 -0
- package/build/tools/error-utils.js +27 -0
- package/build/tools/evaluate.d.ts +34 -0
- package/build/tools/evaluate.js +217 -0
- package/build/tools/file-upload.d.ts +20 -0
- package/build/tools/file-upload.js +174 -0
- package/build/tools/fill-form.d.ts +39 -0
- package/build/tools/fill-form.js +256 -0
- package/build/tools/handle-dialog.d.ts +15 -0
- package/build/tools/handle-dialog.js +48 -0
- package/build/tools/index.d.ts +35 -0
- package/build/tools/index.js +18 -0
- package/build/tools/navigate.d.ts +18 -0
- package/build/tools/navigate.js +111 -0
- package/build/tools/network-monitor.d.ts +18 -0
- package/build/tools/network-monitor.js +66 -0
- package/build/tools/observe.d.ts +44 -0
- package/build/tools/observe.js +339 -0
- package/build/tools/press-key.d.ts +33 -0
- package/build/tools/press-key.js +155 -0
- package/build/tools/read-page.d.ts +22 -0
- package/build/tools/read-page.js +100 -0
- package/build/tools/run-plan.d.ts +205 -0
- package/build/tools/run-plan.js +215 -0
- package/build/tools/screenshot.d.ts +16 -0
- package/build/tools/screenshot.js +283 -0
- package/build/tools/scroll.d.ts +28 -0
- package/build/tools/scroll.js +143 -0
- package/build/tools/switch-tab.d.ts +26 -0
- package/build/tools/switch-tab.js +355 -0
- package/build/tools/tab-status.d.ts +7 -0
- package/build/tools/tab-status.js +50 -0
- package/build/tools/type.d.ts +31 -0
- package/build/tools/type.js +247 -0
- package/build/tools/virtual-desk.d.ts +7 -0
- package/build/tools/virtual-desk.js +108 -0
- package/build/tools/visual-constants.d.ts +3 -0
- package/build/tools/visual-constants.js +10 -0
- package/build/tools/wait-for.d.ts +26 -0
- package/build/tools/wait-for.js +323 -0
- package/build/transport/index.d.ts +3 -0
- package/build/transport/index.js +2 -0
- package/build/transport/pipe-transport.d.ts +18 -0
- package/build/transport/pipe-transport.js +63 -0
- package/build/transport/transport.d.ts +8 -0
- package/build/transport/transport.js +1 -0
- package/build/transport/websocket-transport.d.ts +22 -0
- package/build/transport/websocket-transport.js +200 -0
- package/build/types.d.ts +21 -0
- package/build/types.js +1 -0
- package/package.json +62 -0
package/build/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { startServer } from "./server.js";
|
|
6
|
+
import { runLicenseCommand } from "./cli/license-commands.js";
|
|
7
|
+
import { dispatchTopLevelCli } from "./cli/top-level-commands.js";
|
|
8
|
+
export { startServer } from "./server.js";
|
|
9
|
+
export { runLicenseCommand } from "./cli/license-commands.js";
|
|
10
|
+
export { dispatchTopLevelCli } from "./cli/top-level-commands.js";
|
|
11
|
+
// SEA-Detection (Phase 3b): In einem Single-Executable-Application-Bundle
|
|
12
|
+
// liefert esbuild fuer `import.meta.url` ein leeres Objekt, wodurch
|
|
13
|
+
// `fileURLToPath(import.meta.url)` wirft und die bisherige isMainModule-
|
|
14
|
+
// Pruefung immer `false` lieferte — das Binary beendete sich dann still
|
|
15
|
+
// mit Exit 0, weil weder dispatchTopLevelCli noch startServer aufgerufen
|
|
16
|
+
// wurde.
|
|
17
|
+
//
|
|
18
|
+
// Loesung: `node:sea.isSea()` via createRequire(process.execPath) pruefen.
|
|
19
|
+
// process.execPath ist in JEDEM Modus gesetzt (Source: `node`-Binary-Pfad,
|
|
20
|
+
// SEA: das Binary selbst). createRequire akzeptiert jeden existierenden
|
|
21
|
+
// Pfad und liefert eine Funktion, die Core-Module aufloesen kann. Damit
|
|
22
|
+
// funktioniert der Check synchron in BEIDEN Modi (ESM-Source + CJS-SEA).
|
|
23
|
+
const isSeaBuild = (() => {
|
|
24
|
+
try {
|
|
25
|
+
const req = createRequire(process.execPath);
|
|
26
|
+
const sea = req("node:sea");
|
|
27
|
+
if (sea && typeof sea.isSea === "function")
|
|
28
|
+
return sea.isSea();
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
/* fall through */
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
})();
|
|
35
|
+
const FREE_PACKAGE_NAME = "@silbercue/chrome";
|
|
36
|
+
// Nur als CLI ausfuehren, wenn die Datei direkt gestartet wurde
|
|
37
|
+
// (nicht wenn sie als Library importiert wird — z.B. vom Pro-Repo).
|
|
38
|
+
// Robuste, symlink- und Windows-sichere Pruefung via realpath.
|
|
39
|
+
const isMainModule = (() => {
|
|
40
|
+
if (isSeaBuild) {
|
|
41
|
+
// Im SEA-Bundle: Wenn ein Build-Time-Marker gesetzt ist, der NICHT auf
|
|
42
|
+
// das Free-Package zeigt (z.B. "@silbercue/chrome-pro"), dann sind
|
|
43
|
+
// wir nur eine ein-gebundelte Library — NICHT der Entry-Point.
|
|
44
|
+
if (typeof __SCC_NAME__ === "string" && __SCC_NAME__ !== "" && __SCC_NAME__ !== FREE_PACKAGE_NAME) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
// Sonst: das Binary IST der Free-Repo-Entry. `import.meta.url` ist leer
|
|
48
|
+
// und `fileURLToPath` wuerde werfen — also direkt true zurueckgeben.
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (!process.argv[1])
|
|
52
|
+
return false;
|
|
53
|
+
try {
|
|
54
|
+
const modulePath = fileURLToPath(import.meta.url);
|
|
55
|
+
const argv1Path = realpathSync(process.argv[1]);
|
|
56
|
+
return modulePath === argv1Path;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
if (isMainModule) {
|
|
63
|
+
const command = process.argv[2];
|
|
64
|
+
if (command === "license") {
|
|
65
|
+
// Bestehender `license <sub>`-Pfad — siehe src/cli/license-commands.ts
|
|
66
|
+
runLicenseCommand(process.argv.slice(3)).catch((err) => {
|
|
67
|
+
console.error(err.message);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Phase 2: Top-Level Subcommands (version/status/activate/deactivate/help).
|
|
73
|
+
// Wenn dispatchTopLevelCli einen Subcommand erkennt, beendet es den
|
|
74
|
+
// Prozess via process.exit(). Sonst → false zurueck → Server starten.
|
|
75
|
+
dispatchTopLevelCli(process.argv, import.meta.url)
|
|
76
|
+
.then((handled) => {
|
|
77
|
+
if (handled)
|
|
78
|
+
return;
|
|
79
|
+
return startServer();
|
|
80
|
+
})
|
|
81
|
+
.catch((err) => {
|
|
82
|
+
console.error("Fatal:", err);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Free-Tier configuration.
|
|
3
|
+
* Story 9.1: Configurable limits for free-tier usage.
|
|
4
|
+
*/
|
|
5
|
+
export interface FreeTierConfig {
|
|
6
|
+
/** Maximum number of steps allowed in a single run_plan call (Free Tier) */
|
|
7
|
+
runPlanLimit: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const DEFAULT_FREE_TIER_CONFIG: FreeTierConfig;
|
|
10
|
+
/**
|
|
11
|
+
* Load FreeTierConfig from environment variables with fallback to defaults.
|
|
12
|
+
* Env: SILBERCUECHROME_FREE_TIER_RUN_PLAN_LIMIT (positive integer)
|
|
13
|
+
*/
|
|
14
|
+
export declare function loadFreeTierConfig(): FreeTierConfig;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const DEFAULT_FREE_TIER_CONFIG = {
|
|
2
|
+
runPlanLimit: 3,
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Load FreeTierConfig from environment variables with fallback to defaults.
|
|
6
|
+
* Env: SILBERCUECHROME_FREE_TIER_RUN_PLAN_LIMIT (positive integer)
|
|
7
|
+
*/
|
|
8
|
+
export function loadFreeTierConfig() {
|
|
9
|
+
const raw = process.env.SILBERCUECHROME_FREE_TIER_RUN_PLAN_LIMIT;
|
|
10
|
+
if (raw === undefined || raw === "") {
|
|
11
|
+
return { ...DEFAULT_FREE_TIER_CONFIG };
|
|
12
|
+
}
|
|
13
|
+
const parsed = parseInt(raw, 10);
|
|
14
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
15
|
+
return { ...DEFAULT_FREE_TIER_CONFIG };
|
|
16
|
+
}
|
|
17
|
+
return { runPlanLimit: parsed };
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License status abstraction.
|
|
3
|
+
* Story 9.1: Minimal interface — Story 9.2 will provide the real implementation.
|
|
4
|
+
*/
|
|
5
|
+
export interface LicenseStatus {
|
|
6
|
+
/** Returns true when the user has an active Pro license */
|
|
7
|
+
isPro(): boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Default implementation: always Free Tier.
|
|
11
|
+
* Replaced by a real validator in Story 9.2.
|
|
12
|
+
*/
|
|
13
|
+
export declare class FreeTierLicenseStatus implements LicenseStatus {
|
|
14
|
+
isPro(): boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CdpClient } from "../cdp/cdp-client.js";
|
|
2
|
+
export declare function setTierLabel(isPro: boolean): void;
|
|
3
|
+
/** Set license details for the info panel. Call once after validation. */
|
|
4
|
+
export declare function setLicenseInfo(key: string | undefined, lastCheck: string | undefined, customerName: string | undefined): void;
|
|
5
|
+
/** Track token savings (call after each tool that saves tokens via ambient context) */
|
|
6
|
+
export declare function addTokensSaved(tokens: number): void;
|
|
7
|
+
/** Track last tool elapsed time */
|
|
8
|
+
export declare function setLastElapsed(ms: number): void;
|
|
9
|
+
export declare function injectOverlay(cdpClient: CdpClient, sessionId: string): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Update the overlay.
|
|
12
|
+
* Empty text = idle state (tier + metrics, logo shows image).
|
|
13
|
+
* Non-empty text = busy state (tier + metrics + action, logo = black dot).
|
|
14
|
+
*/
|
|
15
|
+
export declare function updateOverlayStatus(cdpClient: CdpClient, sessionId: string, text: string): Promise<void>;
|
|
16
|
+
export declare function removeOverlay(cdpClient: CdpClient, sessionId: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Show an expanding, fading circle at the given viewport coordinates.
|
|
19
|
+
* Call after a click to visualize where the agent clicked.
|
|
20
|
+
*/
|
|
21
|
+
export declare function showClickIndicator(cdpClient: CdpClient, sessionId: string, x: number, y: number): Promise<void>;
|
|
22
|
+
export declare function getToolLabel(toolName: string): string;
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
// 48x48 Second Truth logo (PNG, base64)
|
|
2
|
+
const LOGO_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAIAAADYYG7QAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAMKADAAQAAAABAAAAMAAAAADbN2wMAAADOElEQVRYCe1Xv0/qUBQWxOcPjFGJiiEMDBIdQAdC1BD4AzRENhcdWYG4MjCZGDZGVzaYddJIICESoy7CJkZNSICYoFHkh8L7YpM+Hu3tpbbd2oHcfuf0fB+n5557OjSkXmoG1AyoGRCVAY1GMzc3p9VqRT0l4Cw1ULfb3d3dnZycFOAQZZIqCGROp9Pj8YhiFXCWQZDBYPB6vQIcokwyCJqYmHA4HHq9XhQxyZkiiFqtcMC1srJis9lIHKJwiqCpqSmfz6fT6UhBR0ZGIGh0dHRra4vkIwqnCHp7ezOZTPF4HIXCG/fPzwXTzs7O+Pg4r4/MoNFovL+/Pzs7W1pa4oaG0NvbW2z+ZrO5urrKdVAE2d/fB+Xd3Z3b7e4jWFxczOfzsOIKh8N9VqVu0fcymQwoX19fDw4O0J1ZJovFgvz96Onmcrnp6WnWpOxie3v7/f0dxJ1OJxqNzszMMHzLy8vPz8+MIDhsbm4qq4ONjq10cnLCEOP39PTUarXCirqpVqssHggE2EcUX6CAPj8/WW68qY2NjbW1NexEFjw/Px8eHoYUdIqxsTFlNaF0YrEYy41FuVxOJBKNRoMFoRhdG6fbxcUF3qyyghDdbre/vLyw9NwFKuzy8rJSqcDUarX29vZcLhdaq1wHC88/PDw85OogIUjYx8fHw8PDwsICTyxZIIQulUokBbx4oVAgNXquJMrRwX0Ar+Po6IiLCyDFYrFWqwk4SDXNzs7e3NzwJoMXDIVCUimpz/v9ftQvL30f+P39jX1HDSjVAQc7Wk4fN+/t4+MjMjo4n+gaYkK32230w0FostnsgJ5MtF8KwiG6vr4+iCB0yK+vr0E8JQkym83z8/NUGnTRq6srqpsMDqihSCTCWzS94PX1tYyfbBTdOA2Oj4976blrnHSUKPKaMU8nk0muDhYJBoNiGX9Z1AwNjs9UKkWiRAfCnEmyknBJghAUIywpNMZ+DJMkKwmXJAhjpMAxjmkJoxKJmIRLEoSxEPufFBqCRHUgJg7xk5RE04tjhnx6esLeRhX34lgjeel0GkXWh1Nv/33QUF25DhCENoMhmlcQaqher3OfUhE1A2oG1Az8l4G/PP2PrT2y2dMAAAAASUVORK5CYII=";
|
|
3
|
+
const OVERLAY_ID = "__sc_session_overlay__";
|
|
4
|
+
function buildOverlayScript() {
|
|
5
|
+
// Static template — safe for shadow DOM injection, no user input.
|
|
6
|
+
const tmpl = `
|
|
7
|
+
<style>
|
|
8
|
+
:host { all: initial; }
|
|
9
|
+
* { box-sizing: border-box; }
|
|
10
|
+
|
|
11
|
+
@keyframes sc-fadeIn {
|
|
12
|
+
0% { opacity: 0; transform: translateY(8px); }
|
|
13
|
+
100% { opacity: 1; transform: translateY(0); }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:host {
|
|
17
|
+
--sc-font: 'SF Mono', 'Menlo', 'Monaco', 'Consolas', monospace;
|
|
18
|
+
--sc-font-size: 10px;
|
|
19
|
+
--sc-font-weight: 400;
|
|
20
|
+
--sc-letter-spacing: 0.5px;
|
|
21
|
+
--sc-color: rgba(255,255,255,0.65);
|
|
22
|
+
--sc-bar-height: 36px;
|
|
23
|
+
--sc-bar-bg: linear-gradient(0deg, rgba(0,0,0,0.65) 0%, rgba(0,0,0,0.35) 55%, transparent 100%);
|
|
24
|
+
--sc-gap: 10px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.sc-bar {
|
|
28
|
+
position: fixed;
|
|
29
|
+
bottom: 0; left: 0; right: 0;
|
|
30
|
+
height: var(--sc-bar-height);
|
|
31
|
+
background: var(--sc-bar-bg);
|
|
32
|
+
pointer-events: none;
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
padding: 0 14px;
|
|
36
|
+
gap: var(--sc-gap);
|
|
37
|
+
font-family: var(--sc-font);
|
|
38
|
+
font-size: var(--sc-font-size);
|
|
39
|
+
font-weight: var(--sc-font-weight);
|
|
40
|
+
letter-spacing: var(--sc-letter-spacing);
|
|
41
|
+
text-transform: uppercase;
|
|
42
|
+
color: var(--sc-color);
|
|
43
|
+
animation: sc-fadeIn 0.6s ease-out both;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.sc-logo {
|
|
47
|
+
width: 20px;
|
|
48
|
+
height: 20px;
|
|
49
|
+
border-radius: 50%;
|
|
50
|
+
background-color: #000;
|
|
51
|
+
background-size: cover;
|
|
52
|
+
background-position: center;
|
|
53
|
+
background-repeat: no-repeat;
|
|
54
|
+
flex-shrink: 0;
|
|
55
|
+
opacity: 0;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
pointer-events: auto;
|
|
58
|
+
animation: sc-fadeIn 0.5s ease-out 0.3s both;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.sc-logo.ready {
|
|
62
|
+
opacity: 0.85;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.sc-logo.busy {
|
|
66
|
+
background-image: none !important;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.sc-sep {
|
|
70
|
+
color: var(--sc-color);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.sc-text-area {
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
gap: var(--sc-gap);
|
|
77
|
+
opacity: 0;
|
|
78
|
+
animation: sc-fadeIn 0.4s ease-out 0.5s both;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.sc-license {
|
|
82
|
+
display: none;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: var(--sc-gap);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.sc-license.open {
|
|
88
|
+
display: flex;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.sc-license-key {
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
pointer-events: auto;
|
|
94
|
+
text-decoration: underline;
|
|
95
|
+
text-decoration-color: rgba(255,255,255,0.3);
|
|
96
|
+
text-underline-offset: 2px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.sc-license-key:hover {
|
|
100
|
+
text-decoration-color: rgba(255,255,255,0.7);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.sc-copied {
|
|
104
|
+
color: rgba(140,255,140,0.9);
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
107
|
+
<div class="sc-bar">
|
|
108
|
+
<div class="sc-logo"></div>
|
|
109
|
+
<span class="sc-text-area" id="sc-text-area"></span>
|
|
110
|
+
<span class="sc-license" id="sc-license"></span>
|
|
111
|
+
</div>`;
|
|
112
|
+
const escaped = tmpl.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
113
|
+
return `(() => {
|
|
114
|
+
if (document.getElementById('${OVERLAY_ID}')) return;
|
|
115
|
+
var LOGO = 'data:image/png;base64,` + LOGO_BASE64 + `';
|
|
116
|
+
var host = document.createElement('div');
|
|
117
|
+
host.id = '${OVERLAY_ID}';
|
|
118
|
+
host.style.cssText = 'position:fixed;bottom:0;left:0;right:0;height:0;z-index:2147483647;pointer-events:none;';
|
|
119
|
+
var sr = host.attachShadow({ mode: 'open' });
|
|
120
|
+
var t = document.createElement('template');
|
|
121
|
+
t.innerHTML = \`${escaped}\`;
|
|
122
|
+
sr.appendChild(t.content.cloneNode(true));
|
|
123
|
+
var logo = sr.querySelector('.sc-logo');
|
|
124
|
+
if (logo) {
|
|
125
|
+
logo.style.backgroundImage = 'url(' + LOGO + ')';
|
|
126
|
+
logo.addEventListener('click', function() {
|
|
127
|
+
var ta = sr.getElementById('sc-text-area');
|
|
128
|
+
var lp = sr.getElementById('sc-license');
|
|
129
|
+
var bar = sr.querySelector('.sc-bar');
|
|
130
|
+
if (!ta || !lp || !bar) return;
|
|
131
|
+
if (lp.classList.contains('open')) {
|
|
132
|
+
lp.classList.remove('open');
|
|
133
|
+
ta.style.display = 'flex';
|
|
134
|
+
bar.style.background = '';
|
|
135
|
+
} else {
|
|
136
|
+
ta.style.display = 'none';
|
|
137
|
+
lp.classList.add('open');
|
|
138
|
+
bar.style.background = 'linear-gradient(0deg, rgba(0,0,0,0.92) 0%, rgba(0,0,0,0.75) 55%, rgba(0,0,0,0.3) 85%, transparent 100%)';
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
document.documentElement.appendChild(host);
|
|
143
|
+
})()`;
|
|
144
|
+
}
|
|
145
|
+
const OVERLAY_SCRIPT = buildOverlayScript();
|
|
146
|
+
const OVERLAY_REMOVE_SCRIPT = `(() => {
|
|
147
|
+
var el = document.getElementById('${OVERLAY_ID}');
|
|
148
|
+
if (el) el.remove();
|
|
149
|
+
})()`;
|
|
150
|
+
let _scriptIdentifier;
|
|
151
|
+
let _tierLabel = "FREE";
|
|
152
|
+
let _isPro = false;
|
|
153
|
+
// Cumulative token savings for Pro overlay display
|
|
154
|
+
let _tokensSaved = 0;
|
|
155
|
+
// Last tool elapsed time
|
|
156
|
+
let _lastElapsedMs = 0;
|
|
157
|
+
// License info for the panel
|
|
158
|
+
let _licenseKey = "";
|
|
159
|
+
let _licenseSince = "";
|
|
160
|
+
let _licenseName = "";
|
|
161
|
+
export function setTierLabel(isPro) {
|
|
162
|
+
_tierLabel = isPro ? "PRO" : "FREE";
|
|
163
|
+
_isPro = isPro;
|
|
164
|
+
}
|
|
165
|
+
/** Set license details for the info panel. Call once after validation. */
|
|
166
|
+
export function setLicenseInfo(key, lastCheck, customerName) {
|
|
167
|
+
_licenseKey = key ?? "";
|
|
168
|
+
_licenseName = customerName ?? "";
|
|
169
|
+
if (lastCheck) {
|
|
170
|
+
try {
|
|
171
|
+
const d = new Date(lastCheck);
|
|
172
|
+
_licenseSince = d.toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", year: "numeric" });
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
_licenseSince = lastCheck;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** Track token savings (call after each tool that saves tokens via ambient context) */
|
|
180
|
+
export function addTokensSaved(tokens) {
|
|
181
|
+
_tokensSaved += tokens;
|
|
182
|
+
}
|
|
183
|
+
/** Track last tool elapsed time */
|
|
184
|
+
export function setLastElapsed(ms) {
|
|
185
|
+
_lastElapsedMs = ms;
|
|
186
|
+
}
|
|
187
|
+
export async function injectOverlay(cdpClient, sessionId) {
|
|
188
|
+
try {
|
|
189
|
+
if (_scriptIdentifier) {
|
|
190
|
+
try {
|
|
191
|
+
await cdpClient.send("Page.removeScriptToEvaluateOnNewDocument", { identifier: _scriptIdentifier }, sessionId);
|
|
192
|
+
}
|
|
193
|
+
catch { /* ignore */ }
|
|
194
|
+
}
|
|
195
|
+
const { identifier } = await cdpClient.send("Page.addScriptToEvaluateOnNewDocument", { source: OVERLAY_SCRIPT }, sessionId);
|
|
196
|
+
_scriptIdentifier = identifier;
|
|
197
|
+
await cdpClient.send("Runtime.evaluate", { expression: OVERLAY_SCRIPT, awaitPromise: false }, sessionId);
|
|
198
|
+
await populateLicensePanel(cdpClient, sessionId);
|
|
199
|
+
await updateOverlayStatus(cdpClient, sessionId, "");
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// Non-critical
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** Populate the license info panel. Called after inject and self-healing re-inject. */
|
|
206
|
+
async function populateLicensePanel(cdpClient, sessionId) {
|
|
207
|
+
let panelHtml;
|
|
208
|
+
let copyScript = "";
|
|
209
|
+
if (_licenseKey) {
|
|
210
|
+
const masked = _licenseKey.slice(0, 8) + "\u2026" + _licenseKey.slice(-4);
|
|
211
|
+
const escapedMasked = masked.replace(/'/g, "\\'");
|
|
212
|
+
const fullKey = _licenseKey.replace(/'/g, "\\'");
|
|
213
|
+
const since = _licenseSince.replace(/'/g, "\\'");
|
|
214
|
+
const name = _licenseName.replace(/'/g, "\\'");
|
|
215
|
+
// Order: PRO | date | key (clickable) | name
|
|
216
|
+
panelHtml = `<span>${_tierLabel}</span>`;
|
|
217
|
+
if (since)
|
|
218
|
+
panelHtml += `<span class="sc-sep">|</span><span>${since}</span>`;
|
|
219
|
+
panelHtml += `<span class="sc-sep">|</span><span>License: <span class="sc-license-key" id="sc-lk">${escapedMasked}</span></span>`;
|
|
220
|
+
if (name)
|
|
221
|
+
panelHtml += `<span class="sc-sep">|</span><span>${name}</span>`;
|
|
222
|
+
panelHtml += `<span class="sc-copied" id="sc-copied" style="display:none">COPIED</span>`;
|
|
223
|
+
copyScript = `var lk = sr.getElementById('sc-lk');
|
|
224
|
+
if (lk) lk.addEventListener('click', function() {
|
|
225
|
+
navigator.clipboard.writeText('${fullKey}');
|
|
226
|
+
var cp = sr.getElementById('sc-copied');
|
|
227
|
+
if (cp) { cp.style.display = 'inline'; setTimeout(function() { cp.style.display = 'none'; }, 1500); }
|
|
228
|
+
});`;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
panelHtml = `<span>${_tierLabel}</span><span class="sc-sep">|</span><span>No license key</span>`;
|
|
232
|
+
}
|
|
233
|
+
const panelScript = `(() => {
|
|
234
|
+
var host = document.getElementById('${OVERLAY_ID}');
|
|
235
|
+
if (!host || !host.shadowRoot) return;
|
|
236
|
+
var sr = host.shadowRoot;
|
|
237
|
+
var lp = sr.getElementById('sc-license');
|
|
238
|
+
if (!lp) return;
|
|
239
|
+
lp.innerHTML = '${panelHtml.replace(/'/g, "\\'")}';
|
|
240
|
+
${copyScript}
|
|
241
|
+
})()`;
|
|
242
|
+
try {
|
|
243
|
+
await cdpClient.send("Runtime.evaluate", { expression: panelScript, awaitPromise: false }, sessionId);
|
|
244
|
+
}
|
|
245
|
+
catch { /* non-critical */ }
|
|
246
|
+
}
|
|
247
|
+
function formatMs(ms) {
|
|
248
|
+
return String(ms).padStart(4, "0") + "ms";
|
|
249
|
+
}
|
|
250
|
+
function formatSaved(tokens) {
|
|
251
|
+
if (tokens >= 1000)
|
|
252
|
+
return Math.round(tokens / 1000) + "k saved";
|
|
253
|
+
return tokens + " saved";
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Update the overlay.
|
|
257
|
+
* Empty text = idle state (tier + metrics, logo shows image).
|
|
258
|
+
* Non-empty text = busy state (tier + metrics + action, logo = black dot).
|
|
259
|
+
*/
|
|
260
|
+
export async function updateOverlayStatus(cdpClient, sessionId, text) {
|
|
261
|
+
// Self-healing: re-inject overlay if it was removed (e.g. SPA rebuild, page reload)
|
|
262
|
+
try {
|
|
263
|
+
const { result } = await cdpClient.send("Runtime.evaluate", { expression: `!!document.getElementById('${OVERLAY_ID}')`, returnByValue: true }, sessionId);
|
|
264
|
+
if (!result.value) {
|
|
265
|
+
await cdpClient.send("Runtime.evaluate", { expression: OVERLAY_SCRIPT, awaitPromise: false }, sessionId);
|
|
266
|
+
// Set logo image after re-injection
|
|
267
|
+
await cdpClient.send("Runtime.evaluate", {
|
|
268
|
+
expression: `(() => { var h = document.getElementById('${OVERLAY_ID}'); if (h && h.shadowRoot) { var l = h.shadowRoot.querySelector('.sc-logo'); if (l) l.style.backgroundImage = 'url(data:image/png;base64,` + LOGO_BASE64 + `)'; } })()`,
|
|
269
|
+
awaitPromise: false,
|
|
270
|
+
}, sessionId);
|
|
271
|
+
// Re-populate license panel after self-healing
|
|
272
|
+
await populateLicensePanel(cdpClient, sessionId);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch { /* non-critical */ }
|
|
276
|
+
const isIdle = text === "";
|
|
277
|
+
const tier = _tierLabel.replace(/'/g, "\\'");
|
|
278
|
+
const action = text.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
279
|
+
const timeStr = _lastElapsedMs > 0 ? formatMs(_lastElapsedMs) : "";
|
|
280
|
+
const savedStr = _isPro && _tokensSaved > 0 ? formatSaved(_tokensSaved) : "";
|
|
281
|
+
// Build segments: tier is always shown, others conditional
|
|
282
|
+
const segments = [tier];
|
|
283
|
+
if (savedStr)
|
|
284
|
+
segments.push(savedStr);
|
|
285
|
+
if (timeStr)
|
|
286
|
+
segments.push(timeStr);
|
|
287
|
+
if (!isIdle && action)
|
|
288
|
+
segments.push(action);
|
|
289
|
+
// Join with " | " — rendered as separate spans so gap handles spacing
|
|
290
|
+
const segmentsJson = JSON.stringify(segments);
|
|
291
|
+
const script = `(() => {
|
|
292
|
+
var host = document.getElementById('${OVERLAY_ID}');
|
|
293
|
+
if (!host || !host.shadowRoot) return;
|
|
294
|
+
var sr = host.shadowRoot;
|
|
295
|
+
var logo = sr.querySelector('.sc-logo');
|
|
296
|
+
var textArea = sr.getElementById('sc-text-area');
|
|
297
|
+
if (!logo || !textArea) return;
|
|
298
|
+
logo.classList.add('ready');
|
|
299
|
+
${isIdle ? "logo.classList.remove('busy');" : "logo.classList.add('busy');"}
|
|
300
|
+
var segs = ${segmentsJson};
|
|
301
|
+
var html = '';
|
|
302
|
+
for (var i = 0; i < segs.length; i++) {
|
|
303
|
+
if (i > 0) html += '<span class="sc-sep">|</span>';
|
|
304
|
+
html += '<span>' + segs[i] + '</span>';
|
|
305
|
+
}
|
|
306
|
+
textArea.innerHTML = html;
|
|
307
|
+
})()`;
|
|
308
|
+
try {
|
|
309
|
+
await cdpClient.send("Runtime.evaluate", { expression: script, awaitPromise: false }, sessionId);
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
// Non-critical
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
export async function removeOverlay(cdpClient, sessionId) {
|
|
316
|
+
try {
|
|
317
|
+
if (_scriptIdentifier) {
|
|
318
|
+
await cdpClient.send("Page.removeScriptToEvaluateOnNewDocument", { identifier: _scriptIdentifier }, sessionId);
|
|
319
|
+
_scriptIdentifier = undefined;
|
|
320
|
+
}
|
|
321
|
+
await cdpClient.send("Runtime.evaluate", { expression: OVERLAY_REMOVE_SCRIPT, awaitPromise: false }, sessionId);
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// Non-critical
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Show an expanding, fading circle at the given viewport coordinates.
|
|
329
|
+
* Call after a click to visualize where the agent clicked.
|
|
330
|
+
*/
|
|
331
|
+
export async function showClickIndicator(cdpClient, sessionId, x, y) {
|
|
332
|
+
const script = `(() => {
|
|
333
|
+
var d = document.createElement('div');
|
|
334
|
+
d.style.cssText = 'position:fixed;left:${Math.round(x) - 6}px;top:${Math.round(y) - 6}px;width:12px;height:12px;border-radius:50%;background:#000;border:1px solid rgba(200,200,200,0.5);pointer-events:none;z-index:2147483646;opacity:0.8;transition:opacity 0.8s ease-out;';
|
|
335
|
+
document.documentElement.appendChild(d);
|
|
336
|
+
requestAnimationFrame(function() { d.style.opacity = '0'; });
|
|
337
|
+
setTimeout(function() { d.remove(); }, 900);
|
|
338
|
+
})()`;
|
|
339
|
+
try {
|
|
340
|
+
await cdpClient.send("Runtime.evaluate", { expression: script, awaitPromise: false }, sessionId);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// Non-critical
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const TOOL_LABELS = {
|
|
347
|
+
navigate: "Navigating\u2026",
|
|
348
|
+
read_page: "Reading page\u2026",
|
|
349
|
+
screenshot: "Taking screenshot\u2026",
|
|
350
|
+
click: "Clicking\u2026",
|
|
351
|
+
type: "Typing\u2026",
|
|
352
|
+
fill_form: "Filling form\u2026",
|
|
353
|
+
scroll: "Scrolling\u2026",
|
|
354
|
+
press_key: "Pressing key\u2026",
|
|
355
|
+
wait_for: "Waiting\u2026",
|
|
356
|
+
evaluate: "Evaluating JS\u2026",
|
|
357
|
+
observe: "Observing\u2026",
|
|
358
|
+
inspect_element: "Inspecting\u2026",
|
|
359
|
+
dom_snapshot: "Reading DOM\u2026",
|
|
360
|
+
file_upload: "Uploading file\u2026",
|
|
361
|
+
run_plan: "Running plan\u2026",
|
|
362
|
+
switch_tab: "Switching tab\u2026",
|
|
363
|
+
virtual_desk: "Listing tabs\u2026",
|
|
364
|
+
console_logs: "Reading console\u2026",
|
|
365
|
+
network_monitor: "Monitoring network\u2026",
|
|
366
|
+
configure_session: "Configuring\u2026",
|
|
367
|
+
handle_dialog: "Handling dialog\u2026",
|
|
368
|
+
tab_status: "Checking tab\u2026",
|
|
369
|
+
};
|
|
370
|
+
export function getToolLabel(toolName) {
|
|
371
|
+
return TOOL_LABELS[toolName] ?? "Working\u2026";
|
|
372
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { executePlan } from "./plan-executor.js";
|
|
2
|
+
export type { PlanStep, StepResult, PlanOptions, ErrorStrategy, SuspendConfig, SuspendedPlanResponse, PlanExecutionResult } from "./plan-executor.js";
|
|
3
|
+
export { substituteVars, extractResultValue } from "./plan-variables.js";
|
|
4
|
+
export type { VarsMap } from "./plan-variables.js";
|
|
5
|
+
export { evaluateCondition } from "./plan-conditions.js";
|
|
6
|
+
export { PlanStateStore } from "./plan-state-store.js";
|
|
7
|
+
export type { SuspendedPlanState } from "./plan-state-store.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { VarsMap } from "./plan-variables.js";
|
|
2
|
+
/**
|
|
3
|
+
* Evaluate a simple condition expression against the vars map.
|
|
4
|
+
* Supports: ==, ===, !=, !==, >, <, >=, <=, &&, ||, !
|
|
5
|
+
* Variables via $varName syntax.
|
|
6
|
+
* String literals via single or double quotes.
|
|
7
|
+
* Number literals, boolean literals (true/false), null.
|
|
8
|
+
*
|
|
9
|
+
* SECURITY: No dynamic code evaluation. Parser-based.
|
|
10
|
+
* Unknown variables evaluate to undefined.
|
|
11
|
+
*/
|
|
12
|
+
export declare function evaluateCondition(expression: string, vars: VarsMap): boolean;
|