safari-pilot 0.1.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/.claude-plugin/plugin.json +35 -0
- package/.mcp.json +11 -0
- package/LICENSE +21 -0
- package/README.md +324 -0
- package/bin/.gitkeep +0 -0
- package/bin/Safari Pilot.app/Contents/CodeResources +0 -0
- package/bin/Safari Pilot.app/Contents/Info.plist +58 -0
- package/bin/Safari Pilot.app/Contents/MacOS/Safari Pilot +0 -0
- package/bin/Safari Pilot.app/Contents/PkgInfo +1 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Info.plist +55 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/MacOS/Safari Pilot Extension +0 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/background.js +294 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/content-isolated.js +80 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/content-main.js +310 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/icons/icon-128.png +0 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/icons/icon-48.png +0 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/icons/icon-96.png +0 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/manifest.json +39 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/_CodeSignature/CodeResources +194 -0
- package/bin/Safari Pilot.app/Contents/Resources/AppIcon.icns +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Assets.car +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.html +19 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/Info.plist +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/MainMenu.nib +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/NSWindowController-B8D-0N-5wS.nib +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/XfG-lQ-9wD-view-m2S-Jp-Qdl.nib +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Icon.png +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Script.js +22 -0
- package/bin/Safari Pilot.app/Contents/Resources/Style.css +45 -0
- package/bin/Safari Pilot.app/Contents/_CodeSignature/CodeResources +236 -0
- package/bin/Safari Pilot.zip +0 -0
- package/bin/SafariPilotd +0 -0
- package/dist/engine-selector.d.ts +10 -0
- package/dist/engine-selector.js +55 -0
- package/dist/engine-selector.js.map +1 -0
- package/dist/engines/applescript.d.ts +53 -0
- package/dist/engines/applescript.js +290 -0
- package/dist/engines/applescript.js.map +1 -0
- package/dist/engines/daemon.d.ts +19 -0
- package/dist/engines/daemon.js +187 -0
- package/dist/engines/daemon.js.map +1 -0
- package/dist/engines/engine.d.ts +15 -0
- package/dist/engines/engine.js +42 -0
- package/dist/engines/engine.js.map +1 -0
- package/dist/engines/extension.d.ts +34 -0
- package/dist/engines/extension.js +66 -0
- package/dist/engines/extension.js.map +1 -0
- package/dist/errors.d.ts +128 -0
- package/dist/errors.js +250 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/security/audit-log.d.ts +23 -0
- package/dist/security/audit-log.js +68 -0
- package/dist/security/audit-log.js.map +1 -0
- package/dist/security/circuit-breaker.d.ts +29 -0
- package/dist/security/circuit-breaker.js +114 -0
- package/dist/security/circuit-breaker.js.map +1 -0
- package/dist/security/domain-policy.d.ts +29 -0
- package/dist/security/domain-policy.js +96 -0
- package/dist/security/domain-policy.js.map +1 -0
- package/dist/security/human-approval.d.ts +20 -0
- package/dist/security/human-approval.js +150 -0
- package/dist/security/human-approval.js.map +1 -0
- package/dist/security/idpi-scanner.d.ts +20 -0
- package/dist/security/idpi-scanner.js +102 -0
- package/dist/security/idpi-scanner.js.map +1 -0
- package/dist/security/kill-switch.d.ts +51 -0
- package/dist/security/kill-switch.js +103 -0
- package/dist/security/kill-switch.js.map +1 -0
- package/dist/security/rate-limiter.d.ts +30 -0
- package/dist/security/rate-limiter.js +70 -0
- package/dist/security/rate-limiter.js.map +1 -0
- package/dist/security/screenshot-redaction.d.ts +42 -0
- package/dist/security/screenshot-redaction.js +134 -0
- package/dist/security/screenshot-redaction.js.map +1 -0
- package/dist/security/tab-ownership.d.ts +46 -0
- package/dist/security/tab-ownership.js +85 -0
- package/dist/security/tab-ownership.js.map +1 -0
- package/dist/server.d.ts +53 -0
- package/dist/server.js +347 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/clipboard.d.ts +15 -0
- package/dist/tools/clipboard.js +128 -0
- package/dist/tools/clipboard.js.map +1 -0
- package/dist/tools/compound.d.ts +68 -0
- package/dist/tools/compound.js +491 -0
- package/dist/tools/compound.js.map +1 -0
- package/dist/tools/extraction.d.ts +26 -0
- package/dist/tools/extraction.js +414 -0
- package/dist/tools/extraction.js.map +1 -0
- package/dist/tools/frames.d.ts +22 -0
- package/dist/tools/frames.js +165 -0
- package/dist/tools/frames.js.map +1 -0
- package/dist/tools/interaction.d.ts +30 -0
- package/dist/tools/interaction.js +651 -0
- package/dist/tools/interaction.js.map +1 -0
- package/dist/tools/navigation.d.ts +41 -0
- package/dist/tools/navigation.js +316 -0
- package/dist/tools/navigation.js.map +1 -0
- package/dist/tools/network.d.ts +27 -0
- package/dist/tools/network.js +721 -0
- package/dist/tools/network.js.map +1 -0
- package/dist/tools/performance.d.ts +16 -0
- package/dist/tools/performance.js +240 -0
- package/dist/tools/performance.js.map +1 -0
- package/dist/tools/permissions.d.ts +25 -0
- package/dist/tools/permissions.js +308 -0
- package/dist/tools/permissions.js.map +1 -0
- package/dist/tools/service-workers.d.ts +15 -0
- package/dist/tools/service-workers.js +136 -0
- package/dist/tools/service-workers.js.map +1 -0
- package/dist/tools/shadow.d.ts +21 -0
- package/dist/tools/shadow.js +126 -0
- package/dist/tools/shadow.js.map +1 -0
- package/dist/tools/storage.d.ts +30 -0
- package/dist/tools/storage.js +679 -0
- package/dist/tools/storage.js.map +1 -0
- package/dist/tools/structured-extraction.d.ts +22 -0
- package/dist/tools/structured-extraction.js +433 -0
- package/dist/tools/structured-extraction.js.map +1 -0
- package/dist/tools/wait.d.ts +18 -0
- package/dist/tools/wait.js +182 -0
- package/dist/tools/wait.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/extension/background.js +294 -0
- package/extension/content-isolated.js +80 -0
- package/extension/content-main.js +310 -0
- package/extension/icons/icon-128.png +0 -0
- package/extension/icons/icon-48.png +0 -0
- package/extension/icons/icon-96.png +0 -0
- package/extension/manifest.json +39 -0
- package/hooks/session-end.sh +67 -0
- package/hooks/session-start.sh +66 -0
- package/package.json +46 -0
- package/scripts/build-extension.sh +135 -0
- package/scripts/postinstall.sh +91 -0
- package/scripts/preuninstall.sh +25 -0
- package/scripts/update-daemon.sh +62 -0
- package/skills/safari-pilot/SKILL.md +157 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { BaseEngine } from './engine.js';
|
|
2
|
+
import type { DaemonEngine } from './daemon.js';
|
|
3
|
+
import type { Engine, EngineResult } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* ExtensionEngine routes JavaScript execution through the Safari extension via the daemon.
|
|
6
|
+
*
|
|
7
|
+
* Execution path: MCP server → DaemonEngine → daemon ExtensionBridge → Safari extension
|
|
8
|
+
* background.js → content script (MAIN world) → result flows back the same way.
|
|
9
|
+
*
|
|
10
|
+
* The engine is only available when:
|
|
11
|
+
* 1. The daemon is running (DaemonEngine.isAvailable() returns true).
|
|
12
|
+
* 2. The Safari extension is installed and has established a native messaging connection.
|
|
13
|
+
*
|
|
14
|
+
* When the extension is not connected, isAvailable() returns false and the engine
|
|
15
|
+
* selector will fall back to the daemon (AppleScript) or applescript engine.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ExtensionEngine extends BaseEngine {
|
|
18
|
+
readonly name: Engine;
|
|
19
|
+
private daemon;
|
|
20
|
+
constructor(daemon: DaemonEngine);
|
|
21
|
+
/**
|
|
22
|
+
* Returns true only when the daemon is running AND the extension is connected.
|
|
23
|
+
* The status check is routed through the daemon's extension_status command.
|
|
24
|
+
*/
|
|
25
|
+
isAvailable(): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Execute a JavaScript string in the Safari extension's MAIN world content script.
|
|
28
|
+
*
|
|
29
|
+
* The script is forwarded through the daemon's ExtensionBridge to background.js,
|
|
30
|
+
* which injects it into the active tab's MAIN world via chrome.scripting.executeScript.
|
|
31
|
+
* The result is returned as a string (JSON-serialised if the script returns an object).
|
|
32
|
+
*/
|
|
33
|
+
execute(script: string, timeout?: number): Promise<EngineResult>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { BaseEngine } from './engine.js';
|
|
2
|
+
// Internal sentinel prefix used to distinguish extension bridge commands from
|
|
3
|
+
// normal AppleScript execution commands routed through the same DaemonEngine.
|
|
4
|
+
const INTERNAL_PREFIX = '__SAFARI_PILOT_INTERNAL__';
|
|
5
|
+
/**
|
|
6
|
+
* ExtensionEngine routes JavaScript execution through the Safari extension via the daemon.
|
|
7
|
+
*
|
|
8
|
+
* Execution path: MCP server → DaemonEngine → daemon ExtensionBridge → Safari extension
|
|
9
|
+
* background.js → content script (MAIN world) → result flows back the same way.
|
|
10
|
+
*
|
|
11
|
+
* The engine is only available when:
|
|
12
|
+
* 1. The daemon is running (DaemonEngine.isAvailable() returns true).
|
|
13
|
+
* 2. The Safari extension is installed and has established a native messaging connection.
|
|
14
|
+
*
|
|
15
|
+
* When the extension is not connected, isAvailable() returns false and the engine
|
|
16
|
+
* selector will fall back to the daemon (AppleScript) or applescript engine.
|
|
17
|
+
*/
|
|
18
|
+
export class ExtensionEngine extends BaseEngine {
|
|
19
|
+
name = 'extension';
|
|
20
|
+
daemon;
|
|
21
|
+
constructor(daemon) {
|
|
22
|
+
super();
|
|
23
|
+
this.daemon = daemon;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns true only when the daemon is running AND the extension is connected.
|
|
27
|
+
* The status check is routed through the daemon's extension_status command.
|
|
28
|
+
*/
|
|
29
|
+
async isAvailable() {
|
|
30
|
+
try {
|
|
31
|
+
const result = await this.daemon.execute(`${INTERNAL_PREFIX} extension_status`);
|
|
32
|
+
return result.ok && result.value === 'connected';
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Execute a JavaScript string in the Safari extension's MAIN world content script.
|
|
40
|
+
*
|
|
41
|
+
* The script is forwarded through the daemon's ExtensionBridge to background.js,
|
|
42
|
+
* which injects it into the active tab's MAIN world via chrome.scripting.executeScript.
|
|
43
|
+
* The result is returned as a string (JSON-serialised if the script returns an object).
|
|
44
|
+
*/
|
|
45
|
+
async execute(script, timeout) {
|
|
46
|
+
const start = Date.now();
|
|
47
|
+
try {
|
|
48
|
+
const payload = JSON.stringify({ script });
|
|
49
|
+
const result = await this.daemon.execute(`${INTERNAL_PREFIX} extension_execute ${payload}`, timeout);
|
|
50
|
+
return { ...result, elapsed_ms: Date.now() - start };
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
error: {
|
|
57
|
+
code: 'EXTENSION_ERROR',
|
|
58
|
+
message,
|
|
59
|
+
retryable: true,
|
|
60
|
+
},
|
|
61
|
+
elapsed_ms: Date.now() - start,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../../src/engines/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC,8EAA8E;AAC9E,8EAA8E;AAC9E,MAAM,eAAe,GAAG,2BAA2B,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,eAAgB,SAAQ,UAAU;IACpC,IAAI,GAAW,WAAW,CAAC;IAC5B,MAAM,CAAe;IAE7B,YAAY,MAAoB;QAC9B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CACtC,GAAG,eAAe,mBAAmB,CACtC,CAAC;YACF,OAAO,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK,KAAK,WAAW,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,OAAgB;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CACtC,GAAG,eAAe,sBAAsB,OAAO,EAAE,EACjD,OAAO,CACR,CAAC;YACF,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QACvD,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,IAAI,EAAE,iBAAiB;oBACvB,OAAO;oBACP,SAAS,EAAE,IAAI;iBAChB;gBACD,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC/B,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { Engine, ToolError } from './types.js';
|
|
2
|
+
export declare const ERROR_CODES: {
|
|
3
|
+
readonly ELEMENT_NOT_FOUND: "ELEMENT_NOT_FOUND";
|
|
4
|
+
readonly ELEMENT_NOT_VISIBLE: "ELEMENT_NOT_VISIBLE";
|
|
5
|
+
readonly ELEMENT_NOT_INTERACTABLE: "ELEMENT_NOT_INTERACTABLE";
|
|
6
|
+
readonly TIMEOUT: "TIMEOUT";
|
|
7
|
+
readonly NAVIGATION_FAILED: "NAVIGATION_FAILED";
|
|
8
|
+
readonly CSP_BLOCKED: "CSP_BLOCKED";
|
|
9
|
+
readonly SHADOW_DOM_CLOSED: "SHADOW_DOM_CLOSED";
|
|
10
|
+
readonly CROSS_ORIGIN_FRAME: "CROSS_ORIGIN_FRAME";
|
|
11
|
+
readonly SAFARI_NOT_RUNNING: "SAFARI_NOT_RUNNING";
|
|
12
|
+
readonly SAFARI_CRASHED: "SAFARI_CRASHED";
|
|
13
|
+
readonly PERMISSION_DENIED: "PERMISSION_DENIED";
|
|
14
|
+
readonly TAB_NOT_FOUND: "TAB_NOT_FOUND";
|
|
15
|
+
readonly TAB_NOT_OWNED: "TAB_NOT_OWNED";
|
|
16
|
+
readonly DOMAIN_NOT_ALLOWED: "DOMAIN_NOT_ALLOWED";
|
|
17
|
+
readonly RATE_LIMITED: "RATE_LIMITED";
|
|
18
|
+
readonly EXTENSION_REQUIRED: "EXTENSION_REQUIRED";
|
|
19
|
+
readonly KILL_SWITCH_ACTIVE: "KILL_SWITCH_ACTIVE";
|
|
20
|
+
readonly HUMAN_APPROVAL_REQUIRED: "HUMAN_APPROVAL_REQUIRED";
|
|
21
|
+
readonly DIALOG_UNEXPECTED: "DIALOG_UNEXPECTED";
|
|
22
|
+
readonly FRAME_NOT_FOUND: "FRAME_NOT_FOUND";
|
|
23
|
+
readonly CIRCUIT_BREAKER_OPEN: "CIRCUIT_BREAKER_OPEN";
|
|
24
|
+
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
25
|
+
};
|
|
26
|
+
export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
27
|
+
export declare abstract class SafariPilotError extends Error {
|
|
28
|
+
abstract readonly code: ErrorCode;
|
|
29
|
+
abstract readonly retryable: boolean;
|
|
30
|
+
abstract readonly hints: string[];
|
|
31
|
+
readonly url?: string;
|
|
32
|
+
readonly selector?: string;
|
|
33
|
+
constructor(message: string, options?: {
|
|
34
|
+
url?: string;
|
|
35
|
+
selector?: string;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export declare class ElementNotFoundError extends SafariPilotError {
|
|
39
|
+
readonly code: "ELEMENT_NOT_FOUND";
|
|
40
|
+
readonly retryable = true;
|
|
41
|
+
readonly hints: string[];
|
|
42
|
+
constructor(selector: string, url: string);
|
|
43
|
+
}
|
|
44
|
+
export declare class ElementNotVisibleError extends SafariPilotError {
|
|
45
|
+
readonly code: "ELEMENT_NOT_VISIBLE";
|
|
46
|
+
readonly retryable = true;
|
|
47
|
+
readonly hints: string[];
|
|
48
|
+
constructor(selector: string, url: string);
|
|
49
|
+
}
|
|
50
|
+
export declare class TimeoutError extends SafariPilotError {
|
|
51
|
+
readonly code: "TIMEOUT";
|
|
52
|
+
readonly retryable = true;
|
|
53
|
+
readonly hints: string[];
|
|
54
|
+
constructor(operation: string, timeoutMs: number);
|
|
55
|
+
}
|
|
56
|
+
export declare class TabNotFoundError extends SafariPilotError {
|
|
57
|
+
readonly code: "TAB_NOT_FOUND";
|
|
58
|
+
readonly retryable = false;
|
|
59
|
+
readonly hints: string[];
|
|
60
|
+
constructor(tabUrl: string);
|
|
61
|
+
}
|
|
62
|
+
export declare class TabNotOwnedError extends SafariPilotError {
|
|
63
|
+
readonly code: "TAB_NOT_OWNED";
|
|
64
|
+
readonly retryable = false;
|
|
65
|
+
readonly hints: string[];
|
|
66
|
+
constructor(tabId: number);
|
|
67
|
+
}
|
|
68
|
+
export declare class DomainNotAllowedError extends SafariPilotError {
|
|
69
|
+
readonly code: "DOMAIN_NOT_ALLOWED";
|
|
70
|
+
readonly retryable = false;
|
|
71
|
+
readonly hints: string[];
|
|
72
|
+
constructor(domain: string);
|
|
73
|
+
}
|
|
74
|
+
export declare class RateLimitedError extends SafariPilotError {
|
|
75
|
+
readonly code: "RATE_LIMITED";
|
|
76
|
+
readonly retryable = true;
|
|
77
|
+
readonly hints: string[];
|
|
78
|
+
constructor(domain: string, maxPerMinute: number);
|
|
79
|
+
}
|
|
80
|
+
export declare class CspBlockedError extends SafariPilotError {
|
|
81
|
+
readonly code: "CSP_BLOCKED";
|
|
82
|
+
readonly retryable = false;
|
|
83
|
+
readonly hints: string[];
|
|
84
|
+
constructor(url: string);
|
|
85
|
+
}
|
|
86
|
+
export declare class ShadowDomClosedError extends SafariPilotError {
|
|
87
|
+
readonly code: "SHADOW_DOM_CLOSED";
|
|
88
|
+
readonly retryable = false;
|
|
89
|
+
readonly hints: string[];
|
|
90
|
+
constructor(selector: string);
|
|
91
|
+
}
|
|
92
|
+
export declare class KillSwitchActiveError extends SafariPilotError {
|
|
93
|
+
readonly code: "KILL_SWITCH_ACTIVE";
|
|
94
|
+
readonly retryable = false;
|
|
95
|
+
readonly hints: string[];
|
|
96
|
+
constructor(reason: string);
|
|
97
|
+
}
|
|
98
|
+
export declare class HumanApprovalRequiredError extends SafariPilotError {
|
|
99
|
+
readonly code: "HUMAN_APPROVAL_REQUIRED";
|
|
100
|
+
readonly retryable = true;
|
|
101
|
+
readonly hints: string[];
|
|
102
|
+
constructor(action: string, domain: string);
|
|
103
|
+
}
|
|
104
|
+
export declare class EngineRequiredError extends SafariPilotError {
|
|
105
|
+
readonly code: "EXTENSION_REQUIRED";
|
|
106
|
+
readonly retryable = false;
|
|
107
|
+
readonly hints: string[];
|
|
108
|
+
constructor(capability: string);
|
|
109
|
+
}
|
|
110
|
+
export declare class CircuitBreakerOpenError extends SafariPilotError {
|
|
111
|
+
readonly code: "CIRCUIT_BREAKER_OPEN";
|
|
112
|
+
readonly retryable = true;
|
|
113
|
+
readonly hints: string[];
|
|
114
|
+
constructor(domain: string, cooldownSeconds: number);
|
|
115
|
+
}
|
|
116
|
+
export declare class NavigationFailedError extends SafariPilotError {
|
|
117
|
+
readonly code: "NAVIGATION_FAILED";
|
|
118
|
+
readonly retryable = true;
|
|
119
|
+
readonly hints: string[];
|
|
120
|
+
constructor(url: string, reason?: string);
|
|
121
|
+
}
|
|
122
|
+
export declare class InternalError extends SafariPilotError {
|
|
123
|
+
readonly code: "INTERNAL_ERROR";
|
|
124
|
+
readonly retryable = false;
|
|
125
|
+
readonly hints: string[];
|
|
126
|
+
constructor(message: string);
|
|
127
|
+
}
|
|
128
|
+
export declare function formatToolError(error: SafariPilotError, engine: Engine, elapsed_ms: number): ToolError;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// ─── Error Codes ─────────────────────────────────────────────────────────────
|
|
2
|
+
export const ERROR_CODES = {
|
|
3
|
+
ELEMENT_NOT_FOUND: 'ELEMENT_NOT_FOUND',
|
|
4
|
+
ELEMENT_NOT_VISIBLE: 'ELEMENT_NOT_VISIBLE',
|
|
5
|
+
ELEMENT_NOT_INTERACTABLE: 'ELEMENT_NOT_INTERACTABLE',
|
|
6
|
+
TIMEOUT: 'TIMEOUT',
|
|
7
|
+
NAVIGATION_FAILED: 'NAVIGATION_FAILED',
|
|
8
|
+
CSP_BLOCKED: 'CSP_BLOCKED',
|
|
9
|
+
SHADOW_DOM_CLOSED: 'SHADOW_DOM_CLOSED',
|
|
10
|
+
CROSS_ORIGIN_FRAME: 'CROSS_ORIGIN_FRAME',
|
|
11
|
+
SAFARI_NOT_RUNNING: 'SAFARI_NOT_RUNNING',
|
|
12
|
+
SAFARI_CRASHED: 'SAFARI_CRASHED',
|
|
13
|
+
PERMISSION_DENIED: 'PERMISSION_DENIED',
|
|
14
|
+
TAB_NOT_FOUND: 'TAB_NOT_FOUND',
|
|
15
|
+
TAB_NOT_OWNED: 'TAB_NOT_OWNED',
|
|
16
|
+
DOMAIN_NOT_ALLOWED: 'DOMAIN_NOT_ALLOWED',
|
|
17
|
+
RATE_LIMITED: 'RATE_LIMITED',
|
|
18
|
+
EXTENSION_REQUIRED: 'EXTENSION_REQUIRED',
|
|
19
|
+
KILL_SWITCH_ACTIVE: 'KILL_SWITCH_ACTIVE',
|
|
20
|
+
HUMAN_APPROVAL_REQUIRED: 'HUMAN_APPROVAL_REQUIRED',
|
|
21
|
+
DIALOG_UNEXPECTED: 'DIALOG_UNEXPECTED',
|
|
22
|
+
FRAME_NOT_FOUND: 'FRAME_NOT_FOUND',
|
|
23
|
+
CIRCUIT_BREAKER_OPEN: 'CIRCUIT_BREAKER_OPEN',
|
|
24
|
+
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
25
|
+
};
|
|
26
|
+
// ─── Abstract Base ────────────────────────────────────────────────────────────
|
|
27
|
+
export class SafariPilotError extends Error {
|
|
28
|
+
url;
|
|
29
|
+
selector;
|
|
30
|
+
constructor(message, options) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = this.constructor.name;
|
|
33
|
+
this.url = options?.url;
|
|
34
|
+
this.selector = options?.selector;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// ─── Concrete Error Classes ───────────────────────────────────────────────────
|
|
38
|
+
export class ElementNotFoundError extends SafariPilotError {
|
|
39
|
+
code = ERROR_CODES.ELEMENT_NOT_FOUND;
|
|
40
|
+
retryable = true;
|
|
41
|
+
hints;
|
|
42
|
+
constructor(selector, url) {
|
|
43
|
+
super(`Element not found: ${selector}`, { url, selector });
|
|
44
|
+
this.hints = [
|
|
45
|
+
`Selector "${selector}" matched no elements on the page`,
|
|
46
|
+
'Check if the element is inside a shadow DOM or cross-origin iframe',
|
|
47
|
+
'Wait for page to fully load before querying',
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export class ElementNotVisibleError extends SafariPilotError {
|
|
52
|
+
code = ERROR_CODES.ELEMENT_NOT_VISIBLE;
|
|
53
|
+
retryable = true;
|
|
54
|
+
hints;
|
|
55
|
+
constructor(selector, url) {
|
|
56
|
+
super(`Element not visible: ${selector}`, { url, selector });
|
|
57
|
+
this.hints = [
|
|
58
|
+
`Element "${selector}" exists but is not visible`,
|
|
59
|
+
'Element may be hidden, off-screen, or have zero dimensions',
|
|
60
|
+
'Scroll element into view or wait for visibility',
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export class TimeoutError extends SafariPilotError {
|
|
65
|
+
code = ERROR_CODES.TIMEOUT;
|
|
66
|
+
retryable = true;
|
|
67
|
+
hints;
|
|
68
|
+
constructor(operation, timeoutMs) {
|
|
69
|
+
super(`Timeout after ${timeoutMs}ms waiting for: ${operation}`);
|
|
70
|
+
this.hints = [
|
|
71
|
+
`Operation "${operation}" exceeded ${timeoutMs}ms limit`,
|
|
72
|
+
'Consider increasing the timeout for slow pages',
|
|
73
|
+
'Check network conditions and page load state',
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export class TabNotFoundError extends SafariPilotError {
|
|
78
|
+
code = ERROR_CODES.TAB_NOT_FOUND;
|
|
79
|
+
retryable = false;
|
|
80
|
+
hints;
|
|
81
|
+
constructor(tabUrl) {
|
|
82
|
+
super(`Tab not found: ${tabUrl}`, { url: tabUrl });
|
|
83
|
+
this.hints = [
|
|
84
|
+
`No tab found matching URL: ${tabUrl}`,
|
|
85
|
+
'The tab may have been closed or navigated away',
|
|
86
|
+
'Use list_tabs to find current open tabs',
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export class TabNotOwnedError extends SafariPilotError {
|
|
91
|
+
code = ERROR_CODES.TAB_NOT_OWNED;
|
|
92
|
+
retryable = false;
|
|
93
|
+
hints;
|
|
94
|
+
constructor(tabId) {
|
|
95
|
+
super(`Tab not owned by agent: ${tabId}`);
|
|
96
|
+
this.hints = [
|
|
97
|
+
`Tab ${tabId} was not opened by this agent session`,
|
|
98
|
+
'Only tabs opened via open_tab can be controlled',
|
|
99
|
+
'Use open_tab to create a new controllable tab',
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export class DomainNotAllowedError extends SafariPilotError {
|
|
104
|
+
code = ERROR_CODES.DOMAIN_NOT_ALLOWED;
|
|
105
|
+
retryable = false;
|
|
106
|
+
hints;
|
|
107
|
+
constructor(domain) {
|
|
108
|
+
super(`Domain not in allowlist: ${domain}`);
|
|
109
|
+
this.hints = [
|
|
110
|
+
`Domain "${domain}" is not permitted by policy`,
|
|
111
|
+
'Contact your administrator to add this domain to the allowlist',
|
|
112
|
+
'Use list_allowed_domains to see permitted domains',
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export class RateLimitedError extends SafariPilotError {
|
|
117
|
+
code = ERROR_CODES.RATE_LIMITED;
|
|
118
|
+
retryable = true;
|
|
119
|
+
hints;
|
|
120
|
+
constructor(domain, maxPerMinute) {
|
|
121
|
+
super(`Rate limit exceeded for domain: ${domain} (max ${maxPerMinute}/min)`);
|
|
122
|
+
this.hints = [
|
|
123
|
+
`Domain "${domain}" has exceeded ${maxPerMinute} actions per minute`,
|
|
124
|
+
'Slow down request frequency to stay within rate limits',
|
|
125
|
+
'Wait at least 60 seconds before retrying',
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export class CspBlockedError extends SafariPilotError {
|
|
130
|
+
code = ERROR_CODES.CSP_BLOCKED;
|
|
131
|
+
retryable = false;
|
|
132
|
+
hints;
|
|
133
|
+
constructor(url) {
|
|
134
|
+
super(`Content Security Policy blocked script execution on: ${url}`, { url });
|
|
135
|
+
this.hints = [
|
|
136
|
+
`Page at "${url}" has a strict CSP that blocks injection`,
|
|
137
|
+
'Use the extension engine which has CSP bypass capability',
|
|
138
|
+
'Switch to a non-injected approach if extension is unavailable',
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export class ShadowDomClosedError extends SafariPilotError {
|
|
143
|
+
code = ERROR_CODES.SHADOW_DOM_CLOSED;
|
|
144
|
+
retryable = false;
|
|
145
|
+
hints;
|
|
146
|
+
constructor(selector) {
|
|
147
|
+
super(`Closed shadow DOM at selector: ${selector}`, { selector });
|
|
148
|
+
this.hints = [
|
|
149
|
+
`Element "${selector}" is inside a closed shadow root`,
|
|
150
|
+
'Closed shadow roots cannot be penetrated by standard scripts',
|
|
151
|
+
'The extension engine may be able to access closed shadow DOM',
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export class KillSwitchActiveError extends SafariPilotError {
|
|
156
|
+
code = ERROR_CODES.KILL_SWITCH_ACTIVE;
|
|
157
|
+
retryable = false;
|
|
158
|
+
hints;
|
|
159
|
+
constructor(reason) {
|
|
160
|
+
super(`Kill switch active: ${reason}`);
|
|
161
|
+
this.hints = [
|
|
162
|
+
`All automation is halted: ${reason}`,
|
|
163
|
+
'Human intervention is required to deactivate the kill switch',
|
|
164
|
+
'Contact your administrator for resolution',
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export class HumanApprovalRequiredError extends SafariPilotError {
|
|
169
|
+
code = ERROR_CODES.HUMAN_APPROVAL_REQUIRED;
|
|
170
|
+
retryable = true;
|
|
171
|
+
hints;
|
|
172
|
+
constructor(action, domain) {
|
|
173
|
+
super(`Human approval required for "${action}" on ${domain}`);
|
|
174
|
+
this.hints = [
|
|
175
|
+
`Action "${action}" on domain "${domain}" requires human sign-off`,
|
|
176
|
+
'Wait for user to approve this action before retrying',
|
|
177
|
+
'Use request_approval to initiate the approval flow',
|
|
178
|
+
];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export class EngineRequiredError extends SafariPilotError {
|
|
182
|
+
code = ERROR_CODES.EXTENSION_REQUIRED;
|
|
183
|
+
retryable = false;
|
|
184
|
+
hints;
|
|
185
|
+
constructor(capability) {
|
|
186
|
+
super(`Extension engine required for capability: ${capability}`);
|
|
187
|
+
this.hints = [
|
|
188
|
+
`Capability "${capability}" is only available via the extension engine`,
|
|
189
|
+
'Install and activate the Safari Pilot browser extension',
|
|
190
|
+
'See docs for extension installation instructions',
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export class CircuitBreakerOpenError extends SafariPilotError {
|
|
195
|
+
code = ERROR_CODES.CIRCUIT_BREAKER_OPEN;
|
|
196
|
+
retryable = true;
|
|
197
|
+
hints;
|
|
198
|
+
constructor(domain, cooldownSeconds) {
|
|
199
|
+
super(`Circuit breaker open for domain: ${domain} (cooldown: ${cooldownSeconds}s)`);
|
|
200
|
+
this.hints = [
|
|
201
|
+
`Domain "${domain}" circuit breaker is open due to repeated failures`,
|
|
202
|
+
`Retry after ${cooldownSeconds} seconds cooldown`,
|
|
203
|
+
'Investigate the root cause before the circuit auto-closes',
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export class NavigationFailedError extends SafariPilotError {
|
|
208
|
+
code = ERROR_CODES.NAVIGATION_FAILED;
|
|
209
|
+
retryable = true;
|
|
210
|
+
hints;
|
|
211
|
+
constructor(url, reason) {
|
|
212
|
+
const detail = reason ? `: ${reason}` : '';
|
|
213
|
+
super(`Navigation failed to ${url}${detail}`, { url });
|
|
214
|
+
this.hints = [
|
|
215
|
+
`Failed to navigate to "${url}"${detail}`,
|
|
216
|
+
'Check that the URL is reachable and valid',
|
|
217
|
+
'Verify network connectivity and DNS resolution',
|
|
218
|
+
];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
export class InternalError extends SafariPilotError {
|
|
222
|
+
code = ERROR_CODES.INTERNAL_ERROR;
|
|
223
|
+
retryable = false;
|
|
224
|
+
hints;
|
|
225
|
+
constructor(message) {
|
|
226
|
+
super(`Internal error: ${message}`);
|
|
227
|
+
this.hints = [
|
|
228
|
+
'An unexpected internal error occurred',
|
|
229
|
+
'This is likely a bug — please report it',
|
|
230
|
+
'Include the full error message and stack trace in the report',
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// ─── formatToolError ──────────────────────────────────────────────────────────
|
|
235
|
+
export function formatToolError(error, engine, elapsed_ms) {
|
|
236
|
+
return {
|
|
237
|
+
code: error.code,
|
|
238
|
+
message: error.message,
|
|
239
|
+
retryable: error.retryable,
|
|
240
|
+
hints: error.hints,
|
|
241
|
+
context: {
|
|
242
|
+
engine,
|
|
243
|
+
url: error.url ?? '',
|
|
244
|
+
...(error.selector !== undefined ? { selector: error.selector } : {}),
|
|
245
|
+
elapsed_ms,
|
|
246
|
+
},
|
|
247
|
+
...(error.cause ? { cause_chain: [String(error.cause)] } : {}),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createServer } from './server.js';
|
|
3
|
+
async function main() {
|
|
4
|
+
const server = await createServer();
|
|
5
|
+
await server.start();
|
|
6
|
+
}
|
|
7
|
+
main().catch((error) => {
|
|
8
|
+
console.error('Safari Pilot failed to start:', error);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE,CAAC;IACpC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AuditEntry } from '../types.js';
|
|
2
|
+
interface AuditLogOptions {
|
|
3
|
+
/** Maximum number of entries to keep in memory (FIFO eviction). Default 10 000. */
|
|
4
|
+
maxEntries?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class AuditLog {
|
|
7
|
+
private entries;
|
|
8
|
+
private readonly maxEntries;
|
|
9
|
+
constructor(options?: AuditLogOptions);
|
|
10
|
+
/**
|
|
11
|
+
* Record a tool invocation. timestamp is auto-injected; all other fields
|
|
12
|
+
* must be provided by the caller.
|
|
13
|
+
*/
|
|
14
|
+
record(entry: Omit<AuditEntry, 'timestamp'>): void;
|
|
15
|
+
/**
|
|
16
|
+
* Return entries, optionally limited to the most-recent `limit` entries.
|
|
17
|
+
*/
|
|
18
|
+
getEntries(limit?: number): AuditEntry[];
|
|
19
|
+
getEntriesForSession(session: string): AuditEntry[];
|
|
20
|
+
clear(): void;
|
|
21
|
+
private redactParams;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Tools that pass a cleartext value that should always be redacted.
|
|
2
|
+
const REDACT_VALUE_TOOLS = new Set([
|
|
3
|
+
'safari_fill',
|
|
4
|
+
'safari_set_cookie',
|
|
5
|
+
'safari_clipboard_write',
|
|
6
|
+
]);
|
|
7
|
+
// Tools whose script payloads are truncated (they can be megabytes long).
|
|
8
|
+
const TRUNCATE_SCRIPT_TOOLS = new Set([
|
|
9
|
+
'safari_evaluate',
|
|
10
|
+
]);
|
|
11
|
+
const SCRIPT_MAX_LEN = 200;
|
|
12
|
+
export class AuditLog {
|
|
13
|
+
entries = [];
|
|
14
|
+
maxEntries;
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.maxEntries = options.maxEntries ?? 10_000;
|
|
17
|
+
}
|
|
18
|
+
// ── Write ───────────────────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Record a tool invocation. timestamp is auto-injected; all other fields
|
|
21
|
+
* must be provided by the caller.
|
|
22
|
+
*/
|
|
23
|
+
record(entry) {
|
|
24
|
+
const redactedParams = this.redactParams(entry.tool, { ...entry.params });
|
|
25
|
+
const fullEntry = {
|
|
26
|
+
...entry,
|
|
27
|
+
params: redactedParams,
|
|
28
|
+
timestamp: new Date().toISOString(),
|
|
29
|
+
};
|
|
30
|
+
this.entries.push(fullEntry);
|
|
31
|
+
// Evict oldest entries if over the cap
|
|
32
|
+
if (this.entries.length > this.maxEntries) {
|
|
33
|
+
this.entries = this.entries.slice(-this.maxEntries);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// ── Read ────────────────────────────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Return entries, optionally limited to the most-recent `limit` entries.
|
|
39
|
+
*/
|
|
40
|
+
getEntries(limit) {
|
|
41
|
+
if (limit !== undefined && limit > 0) {
|
|
42
|
+
return this.entries.slice(-limit);
|
|
43
|
+
}
|
|
44
|
+
return [...this.entries];
|
|
45
|
+
}
|
|
46
|
+
getEntriesForSession(session) {
|
|
47
|
+
return this.entries.filter((e) => e.session === session);
|
|
48
|
+
}
|
|
49
|
+
clear() {
|
|
50
|
+
this.entries = [];
|
|
51
|
+
}
|
|
52
|
+
// ── Redaction ───────────────────────────────────────────────────────────────
|
|
53
|
+
redactParams(tool, params) {
|
|
54
|
+
// Redact cleartext value fields (passwords, cookie values, clipboard)
|
|
55
|
+
if (REDACT_VALUE_TOOLS.has(tool) && 'value' in params) {
|
|
56
|
+
params['value'] = '[REDACTED]';
|
|
57
|
+
}
|
|
58
|
+
// Truncate long script payloads to keep log files manageable
|
|
59
|
+
if (TRUNCATE_SCRIPT_TOOLS.has(tool) && typeof params['script'] === 'string') {
|
|
60
|
+
const script = params['script'];
|
|
61
|
+
if (script.length > SCRIPT_MAX_LEN) {
|
|
62
|
+
params['script'] = script.slice(0, SCRIPT_MAX_LEN) + '...';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return params;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=audit-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.js","sourceRoot":"","sources":["../../src/security/audit-log.ts"],"names":[],"mappings":"AAaA,oEAAoE;AACpE,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,aAAa;IACb,mBAAmB;IACnB,wBAAwB;CACzB,CAAC,CAAC;AAEH,0EAA0E;AAC1E,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,iBAAiB;CAClB,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,MAAM,OAAO,QAAQ;IACX,OAAO,GAAiB,EAAE,CAAC;IAClB,UAAU,CAAS;IAEpC,YAAY,UAA2B,EAAE;QACvC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC;IACjD,CAAC;IAED,+EAA+E;IAE/E;;;OAGG;IACH,MAAM,CAAC,KAAoC;QACzC,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAE1E,MAAM,SAAS,GAAe;YAC5B,GAAG,KAAK;YACR,MAAM,EAAE,cAAc;YACtB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;QAEF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAE7B,uCAAuC;QACvC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAC1C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED,+EAA+E;IAE/E;;OAEG;IACH,UAAU,CAAC,KAAc;QACvB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACrC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAED,oBAAoB,CAAC,OAAe;QAClC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;IACpB,CAAC;IAED,+EAA+E;IAEvE,YAAY,CAClB,IAAY,EACZ,MAA+B;QAE/B,sEAAsE;QACtE,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;YACtD,MAAM,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC;QACjC,CAAC;QAED,6DAA6D;QAC7D,IAAI,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC5E,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAW,CAAC;YAC1C,IAAI,MAAM,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;gBACnC,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,GAAG,KAAK,CAAC;YAC7D,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
2
|
+
export declare class CircuitBreaker {
|
|
3
|
+
private states;
|
|
4
|
+
/**
|
|
5
|
+
* Record a successful call. Resets the failure counter and closes the circuit.
|
|
6
|
+
*/
|
|
7
|
+
recordSuccess(domain: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Record a failed call. May open the circuit if the threshold is reached.
|
|
10
|
+
*/
|
|
11
|
+
recordFailure(domain: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* Returns true when the circuit is open (calls should be rejected).
|
|
14
|
+
* Half-open circuits return false — a probe is permitted.
|
|
15
|
+
*/
|
|
16
|
+
isOpen(domain: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Compute the current circuit state for a domain.
|
|
19
|
+
*/
|
|
20
|
+
getState(domain: string): CircuitState;
|
|
21
|
+
/**
|
|
22
|
+
* Assert the circuit is not open before executing a call.
|
|
23
|
+
* Call this at the entry point of any guarded operation.
|
|
24
|
+
* Throws CircuitBreakerOpenError when the circuit is open.
|
|
25
|
+
* In half-open state, marks the probe as issued and allows one call through.
|
|
26
|
+
*/
|
|
27
|
+
assertClosed(domain: string): void;
|
|
28
|
+
private getState_;
|
|
29
|
+
}
|