@the-forge-flow/camoufox-pi 0.1.1

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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +209 -0
  3. package/dist/client/camoufox-client.d.ts +62 -0
  4. package/dist/client/camoufox-client.d.ts.map +1 -0
  5. package/dist/client/camoufox-client.js +277 -0
  6. package/dist/client/camoufox-client.js.map +1 -0
  7. package/dist/client/launcher.d.ts +28 -0
  8. package/dist/client/launcher.d.ts.map +1 -0
  9. package/dist/client/launcher.js +30 -0
  10. package/dist/client/launcher.js.map +1 -0
  11. package/dist/client/signal.d.ts +6 -0
  12. package/dist/client/signal.d.ts.map +1 -0
  13. package/dist/client/signal.js +29 -0
  14. package/dist/client/signal.js.map +1 -0
  15. package/dist/commands/index.d.ts +5 -0
  16. package/dist/commands/index.d.ts.map +1 -0
  17. package/dist/commands/index.js +4 -0
  18. package/dist/commands/index.js.map +1 -0
  19. package/dist/commands/types.d.ts +13 -0
  20. package/dist/commands/types.d.ts.map +1 -0
  21. package/dist/commands/types.js +2 -0
  22. package/dist/commands/types.js.map +1 -0
  23. package/dist/errors.d.ts +36 -0
  24. package/dist/errors.d.ts.map +1 -0
  25. package/dist/errors.js +56 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/hooks/index.d.ts +7 -0
  28. package/dist/hooks/index.d.ts.map +1 -0
  29. package/dist/hooks/index.js +4 -0
  30. package/dist/hooks/index.js.map +1 -0
  31. package/dist/index.d.ts +53 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +98 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/search/adapters/duckduckgo.d.ts +3 -0
  36. package/dist/search/adapters/duckduckgo.d.ts.map +1 -0
  37. package/dist/search/adapters/duckduckgo.js +52 -0
  38. package/dist/search/adapters/duckduckgo.js.map +1 -0
  39. package/dist/search/types.d.ts +16 -0
  40. package/dist/search/types.d.ts.map +1 -0
  41. package/dist/search/types.js +2 -0
  42. package/dist/search/types.js.map +1 -0
  43. package/dist/security/ssrf.d.ts +6 -0
  44. package/dist/security/ssrf.d.ts.map +1 -0
  45. package/dist/security/ssrf.js +82 -0
  46. package/dist/security/ssrf.js.map +1 -0
  47. package/dist/services/camoufox-service.d.ts +19 -0
  48. package/dist/services/camoufox-service.d.ts.map +1 -0
  49. package/dist/services/camoufox-service.js +49 -0
  50. package/dist/services/camoufox-service.js.map +1 -0
  51. package/dist/tools/fetch-url.d.ts +11 -0
  52. package/dist/tools/fetch-url.d.ts.map +1 -0
  53. package/dist/tools/fetch-url.js +53 -0
  54. package/dist/tools/fetch-url.js.map +1 -0
  55. package/dist/tools/formats.d.ts +2 -0
  56. package/dist/tools/formats.d.ts.map +1 -0
  57. package/dist/tools/formats.js +12 -0
  58. package/dist/tools/formats.js.map +1 -0
  59. package/dist/tools/index.d.ts +6 -0
  60. package/dist/tools/index.d.ts.map +1 -0
  61. package/dist/tools/index.js +8 -0
  62. package/dist/tools/index.js.map +1 -0
  63. package/dist/tools/search-web.d.ts +11 -0
  64. package/dist/tools/search-web.d.ts.map +1 -0
  65. package/dist/tools/search-web.js +55 -0
  66. package/dist/tools/search-web.js.map +1 -0
  67. package/dist/tools/types.d.ts +22 -0
  68. package/dist/tools/types.d.ts.map +1 -0
  69. package/dist/tools/types.js +2 -0
  70. package/dist/tools/types.js.map +1 -0
  71. package/dist/types.d.ts +7 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +8 -0
  74. package/dist/types.js.map +1 -0
  75. package/dist/update-check.d.ts +24 -0
  76. package/dist/update-check.d.ts.map +1 -0
  77. package/dist/update-check.js +94 -0
  78. package/dist/update-check.js.map +1 -0
  79. package/package.json +90 -0
@@ -0,0 +1,6 @@
1
+ export interface CombinedSignal {
2
+ readonly signal: AbortSignal;
3
+ cleanup(): void;
4
+ }
5
+ export declare function combineSignals(external: AbortSignal | undefined, timeoutMs: number): CombinedSignal;
6
+ //# sourceMappingURL=signal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/client/signal.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,OAAO,IAAI,IAAI,CAAC;CAChB;AAED,wBAAgB,cAAc,CAC7B,QAAQ,EAAE,WAAW,GAAG,SAAS,EACjC,SAAS,EAAE,MAAM,GACf,cAAc,CA0BhB"}
@@ -0,0 +1,29 @@
1
+ // Combines an optional external AbortSignal with an internal timeout.
2
+ // Returns an AbortSignal plus a cleanup that clears the timer and the
3
+ // external-signal listener. Spec: §4.8, §5.4.
4
+ export function combineSignals(external, timeoutMs) {
5
+ const ctrl = new AbortController();
6
+ const timer = setTimeout(() => {
7
+ ctrl.abort();
8
+ }, timeoutMs);
9
+ let externalListener;
10
+ if (external) {
11
+ if (external.aborted) {
12
+ ctrl.abort();
13
+ }
14
+ else {
15
+ externalListener = () => ctrl.abort();
16
+ external.addEventListener("abort", externalListener, { once: true });
17
+ }
18
+ }
19
+ return {
20
+ signal: ctrl.signal,
21
+ cleanup: () => {
22
+ clearTimeout(timer);
23
+ if (external && externalListener) {
24
+ external.removeEventListener("abort", externalListener);
25
+ }
26
+ },
27
+ };
28
+ }
29
+ //# sourceMappingURL=signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/client/signal.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,sEAAsE;AACtE,8CAA8C;AAO9C,MAAM,UAAU,cAAc,CAC7B,QAAiC,EACjC,SAAiB;IAEjB,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IAEnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;IACd,CAAC,EAAE,SAAS,CAAC,CAAC;IAEd,IAAI,gBAA0C,CAAC;IAC/C,IAAI,QAAQ,EAAE,CAAC;QACd,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QACd,CAAC;aAAM,CAAC;YACP,gBAAgB,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC;IACF,CAAC;IAED,OAAO;QACN,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,GAAG,EAAE;YACb,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,QAAQ,IAAI,gBAAgB,EAAE,CAAC;gBAClC,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;YACzD,CAAC;QACF,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { CamoufoxService } from "../services/camoufox-service.js";
2
+ import type { CommandDefinition } from "./types.js";
3
+ export type { CommandContext, CommandDefinition, CommandUI } from "./types.js";
4
+ export declare function createAllCommands(_service: CamoufoxService): CommandDefinition[];
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE/E,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,iBAAiB,EAAE,CAEhF"}
@@ -0,0 +1,4 @@
1
+ export function createAllCommands(_service) {
2
+ return [];
3
+ }
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,iBAAiB,CAAC,QAAyB;IAC1D,OAAO,EAAE,CAAC;AACX,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface CommandUI {
2
+ notify(message: string, level?: string): void;
3
+ }
4
+ export interface CommandContext {
5
+ cwd: string;
6
+ ui: CommandUI;
7
+ }
8
+ export interface CommandDefinition {
9
+ name: string;
10
+ description?: string;
11
+ handler(args: string, ctx: CommandContext): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACzB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,SAAS,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ export type CamoufoxError = {
2
+ type: "timeout";
3
+ phase: "nav" | "wait_ready";
4
+ elapsedMs: number;
5
+ } | {
6
+ type: "network";
7
+ cause: string;
8
+ url: string;
9
+ } | {
10
+ type: "http";
11
+ status: number;
12
+ url: string;
13
+ } | {
14
+ type: "browser_launch_failed";
15
+ stderr: string;
16
+ } | {
17
+ type: "playwright_disconnected";
18
+ } | {
19
+ type: "aborted";
20
+ } | {
21
+ type: "config_invalid";
22
+ field: string;
23
+ reason: string;
24
+ };
25
+ export declare class CamoufoxErrorBox extends Error {
26
+ readonly err: CamoufoxError;
27
+ constructor(err: CamoufoxError);
28
+ }
29
+ export interface MapContext {
30
+ readonly url?: string;
31
+ readonly phase?: "nav" | "wait_ready";
32
+ readonly elapsedMs?: number;
33
+ readonly signal?: AbortSignal;
34
+ }
35
+ export declare function mapPlaywrightError(err: unknown, ctx: MapContext): CamoufoxError;
36
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GACtB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,KAAK,GAAG,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,uBAAuB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,yBAAyB,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAwB7D,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,SAAgB,GAAG,EAAE,aAAa,CAAC;gBAEvB,GAAG,EAAE,aAAa;CAK9B;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC9B;AAID,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,CAoB/E"}
package/dist/errors.js ADDED
@@ -0,0 +1,56 @@
1
+ // Single seam for turning Playwright / camoufox-js exceptions into typed
2
+ // CamoufoxError values. Spec: §4.2, §5.1.
3
+ function sanitizeForMessage(err) {
4
+ // Cap stderr and redact URL query strings from error payloads before
5
+ // serializing into .message (which can surface in logs / stack traces).
6
+ const redacted = { ...err };
7
+ if (typeof redacted.stderr === "string" && redacted.stderr.length > 500) {
8
+ redacted.stderr = `${redacted.stderr.slice(0, 500)}…[${redacted.stderr.length} bytes]`;
9
+ }
10
+ if (typeof redacted.url === "string") {
11
+ try {
12
+ const u = new URL(redacted.url);
13
+ redacted.url = `${u.origin}${u.pathname}`;
14
+ }
15
+ catch {
16
+ // leave as-is if unparseable
17
+ }
18
+ }
19
+ try {
20
+ return JSON.stringify(redacted);
21
+ }
22
+ catch {
23
+ return "[unserializable error payload]";
24
+ }
25
+ }
26
+ export class CamoufoxErrorBox extends Error {
27
+ err;
28
+ constructor(err) {
29
+ super(`${err.type}: ${sanitizeForMessage(err)}`);
30
+ this.name = "CamoufoxError";
31
+ this.err = err;
32
+ }
33
+ }
34
+ const NETWORK_PATTERN = /net::ERR_|NS_ERROR_NET_|getaddrinfo/;
35
+ export function mapPlaywrightError(err, ctx) {
36
+ if (!(err instanceof Error)) {
37
+ // Unknown non-Error throwable — can't safely classify. Wrap as unknown.
38
+ return { type: "browser_launch_failed", stderr: String(err) };
39
+ }
40
+ // TimeoutError is a distinct signal even when abort also fired — classify
41
+ // it first so internal-timeout-via-combined-signal doesn't swallow a
42
+ // genuine page timeout.
43
+ if (err.name === "TimeoutError") {
44
+ return { type: "timeout", phase: ctx.phase ?? "nav", elapsedMs: ctx.elapsedMs ?? 0 };
45
+ }
46
+ if (err.name === "AbortError" || ctx.signal?.aborted) {
47
+ return { type: "aborted" };
48
+ }
49
+ if (NETWORK_PATTERN.test(err.message)) {
50
+ return { type: "network", cause: err.message, url: ctx.url ?? "" };
51
+ }
52
+ // Unknown Error — classify as launch-failed with stderr for
53
+ // observability. Tool callers see CamoufoxErrorBox uniformly.
54
+ return { type: "browser_launch_failed", stderr: err.message };
55
+ }
56
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,0CAA0C;AAW1C,SAAS,kBAAkB,CAAC,GAAkB;IAC7C,qEAAqE;IACrE,wEAAwE;IACxE,MAAM,QAAQ,GAA4B,EAAE,GAAG,GAAG,EAAE,CAAC;IACrD,IAAI,OAAO,QAAQ,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACzE,QAAQ,CAAC,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,MAAM,SAAS,CAAC;IACxF,CAAC;IACD,IAAI,OAAO,QAAQ,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QACtC,IAAI,CAAC;YACJ,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAChC,QAAQ,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACR,6BAA6B;QAC9B,CAAC;IACF,CAAC;IACD,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,gCAAgC,CAAC;IACzC,CAAC;AACF,CAAC;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAC1B,GAAG,CAAgB;IAEnC,YAAY,GAAkB;QAC7B,KAAK,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IAChB,CAAC;CACD;AASD,MAAM,eAAe,GAAG,qCAAqC,CAAC;AAE9D,MAAM,UAAU,kBAAkB,CAAC,GAAY,EAAE,GAAe;IAC/D,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,EAAE,CAAC;QAC7B,wEAAwE;QACxE,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC/D,CAAC;IACD,0EAA0E;IAC1E,qEAAqE;IACrE,wBAAwB;IACxB,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;IACtF,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QACtD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC5B,CAAC;IACD,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC;IACpE,CAAC;IACD,4DAA4D;IAC5D,8DAA8D;IAC9D,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { CamoufoxService } from "../services/camoufox-service.js";
2
+ export interface HookDefinition {
3
+ event: string;
4
+ handler: (event: unknown, ctx: unknown) => unknown | Promise<unknown>;
5
+ }
6
+ export declare function createAllHooks(_service: CamoufoxService): HookDefinition[];
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAEvE,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,cAAc,EAAE,CAE1E"}
@@ -0,0 +1,4 @@
1
+ export function createAllHooks(_service) {
2
+ return [];
3
+ }
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,cAAc,CAAC,QAAyB;IACvD,OAAO,EAAE,CAAC;AACX,CAAC"}
@@ -0,0 +1,53 @@
1
+ import type { TObject } from "@sinclair/typebox";
2
+ import type { ToolDefinition } from "./tools/index.js";
3
+ export { CamoufoxService } from "./services/camoufox-service.js";
4
+ export { createAllTools } from "./tools/index.js";
5
+ export { createAllCommands } from "./commands/index.js";
6
+ export { createAllHooks } from "./hooks/index.js";
7
+ export type { ToolDefinition } from "./tools/index.js";
8
+ export type { CommandDefinition, CommandContext } from "./commands/index.js";
9
+ export type { HookDefinition } from "./hooks/index.js";
10
+ export type { CamoufoxConfig } from "./types.js";
11
+ type PiEventHandler = (event: unknown, ctx: unknown) => unknown | Promise<unknown>;
12
+ interface PiToolExecuteResult {
13
+ content: Array<{
14
+ type: "text";
15
+ text: string;
16
+ }>;
17
+ details: unknown;
18
+ }
19
+ interface PiRegisteredTool {
20
+ name: string;
21
+ label: string;
22
+ description: string;
23
+ promptSnippet: string;
24
+ promptGuidelines: string[];
25
+ parameters: unknown;
26
+ execute(toolCallId: string, input: unknown, signal?: AbortSignal, onUpdate?: unknown, ctx?: unknown): Promise<PiToolExecuteResult>;
27
+ }
28
+ interface PiRegisteredCommand {
29
+ description?: string | undefined;
30
+ handler(args: string, ctx: PiCommandContext): Promise<void>;
31
+ }
32
+ interface PiCommandContext {
33
+ ui?: {
34
+ notify?: (message: string, level?: string) => void;
35
+ };
36
+ cwd?: string;
37
+ }
38
+ export interface PiExtensionApi {
39
+ on(event: string, handler: PiEventHandler): void;
40
+ registerTool(tool: PiRegisteredTool): void;
41
+ registerCommand(name: string, config: PiRegisteredCommand): void;
42
+ exec: (cmd: string, args: string[], opts?: {
43
+ timeout?: number;
44
+ }) => Promise<{
45
+ stdout: string;
46
+ code: number;
47
+ }>;
48
+ cwd?: string;
49
+ }
50
+ declare function wrapTool<S extends TObject>(def: ToolDefinition<S>): PiRegisteredTool;
51
+ export declare const __test_wrapTool__: typeof wrapTool;
52
+ export default function camoufoxExtension(pi: PiExtensionApi): void;
53
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AASjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAWvD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC7E,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAQjD,KAAK,cAAc,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEnF,UAAU,mBAAmB;IAC5B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,gBAAgB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,CACN,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,OAAO,EAClB,GAAG,CAAC,EAAE,OAAO,GACX,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAChC;AAED,UAAU,mBAAmB;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,UAAU,gBAAgB;IACzB,EAAE,CAAC,EAAE;QACJ,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;KACnD,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC9B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACjD,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3C,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACjE,IAAI,EAAE,CACL,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KACvB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAQD,iBAAS,QAAQ,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,gBAAgB,CA0B7E;AAGD,eAAO,MAAM,iBAAiB,iBAAW,CAAC;AAuB1C,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI,CAkClE"}
package/dist/index.js ADDED
@@ -0,0 +1,98 @@
1
+ import { Value } from "@sinclair/typebox/value";
2
+ import { RealLauncher } from "./client/launcher.js";
3
+ import { createAllCommands } from "./commands/index.js";
4
+ import { CamoufoxErrorBox } from "./errors.js";
5
+ import { createAllHooks } from "./hooks/index.js";
6
+ import { CamoufoxService } from "./services/camoufox-service.js";
7
+ import { createAllTools } from "./tools/index.js";
8
+ import { checkForUpdates } from "./update-check.js";
9
+ // ---------------------------------------------------------------------------
10
+ // Library-style named exports. Let other PI extensions (e.g. TFF) import
11
+ // CamoufoxService and factories directly instead of spawning a child `pi`
12
+ // process. The default export (camoufoxExtension) remains the canonical
13
+ // PI-extension entry.
14
+ // ---------------------------------------------------------------------------
15
+ export { CamoufoxService } from "./services/camoufox-service.js";
16
+ export { createAllTools } from "./tools/index.js";
17
+ export { createAllCommands } from "./commands/index.js";
18
+ export { createAllHooks } from "./hooks/index.js";
19
+ // ---------------------------------------------------------------------------
20
+ // Boundary adapters: bridge tool/command definitions to PI's structural shape
21
+ // without casts. `wrapTool` uses TypeBox's runtime Value.Check to narrow the
22
+ // unknown input to Static<S> before delegating to the typed execute().
23
+ // ---------------------------------------------------------------------------
24
+ function wrapTool(def) {
25
+ const guidelines = [...def.promptGuidelines];
26
+ if (def.readOnly) {
27
+ guidelines.push("This tool is read-only (no side effects). Safe to call in parallel with other read-only tools.");
28
+ }
29
+ return {
30
+ name: def.name,
31
+ label: def.label,
32
+ description: def.description,
33
+ promptSnippet: def.promptSnippet,
34
+ promptGuidelines: guidelines,
35
+ parameters: def.parameters,
36
+ async execute(toolCallId, input, signal) {
37
+ if (!Value.Check(def.parameters, input)) {
38
+ const first = [...Value.Errors(def.parameters, input)][0];
39
+ throw new CamoufoxErrorBox({
40
+ type: "config_invalid",
41
+ field: first?.path ?? "(root)",
42
+ reason: first?.message ?? "validation failed",
43
+ });
44
+ }
45
+ return def.execute(toolCallId, input, signal);
46
+ },
47
+ };
48
+ }
49
+ // Exposed for unit tests only. Not part of the public API.
50
+ export const __test_wrapTool__ = wrapTool;
51
+ function wrapCommand(def) {
52
+ return {
53
+ description: def.description,
54
+ async handler(args, piCtx) {
55
+ const ctx = {
56
+ cwd: piCtx.cwd ?? process.cwd(),
57
+ ui: {
58
+ notify: (message, level = "info") => {
59
+ piCtx.ui?.notify?.(message, level);
60
+ },
61
+ },
62
+ };
63
+ await def.handler(args, ctx);
64
+ },
65
+ };
66
+ }
67
+ // ---------------------------------------------------------------------------
68
+ // Default export — called by PI with its ExtensionAPI instance at startup.
69
+ // ---------------------------------------------------------------------------
70
+ export default function camoufoxExtension(pi) {
71
+ const service = new CamoufoxService({
72
+ launcherFactory: () => new RealLauncher(),
73
+ });
74
+ pi.on("session_start", async (_event, ctx) => {
75
+ const cwd = ctx?.cwd ?? pi.cwd ?? process.cwd();
76
+ await service.initialize(cwd);
77
+ // Register tools + commands AFTER initialize so createAllTools can
78
+ // access service.getClient(). Per @mariozechner/pi-coding-agent docs,
79
+ // pi.registerTool() works after startup as well as during load.
80
+ for (const def of createAllTools(service)) {
81
+ pi.registerTool(wrapTool(def));
82
+ }
83
+ for (const def of createAllCommands(service)) {
84
+ pi.registerCommand(def.name, wrapCommand(def));
85
+ }
86
+ for (const hook of createAllHooks(service)) {
87
+ pi.on(hook.event, hook.handler);
88
+ }
89
+ const updateInfo = await checkForUpdates(pi);
90
+ if (updateInfo?.updateAvailable) {
91
+ ctx.ui?.notify?.(`📦 Update available: ${updateInfo.latestVersion} (you have ${updateInfo.currentVersion}). Run: pi install npm:@the-forge-flow/camoufox-pi`, "info");
92
+ }
93
+ });
94
+ pi.on("session_shutdown", async () => {
95
+ await service.shutdown();
96
+ });
97
+ }
98
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { SearchEngineAdapter } from "../types.js";
2
+ export declare const duckduckgoAdapter: SearchEngineAdapter;
3
+ //# sourceMappingURL=duckduckgo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duckduckgo.d.ts","sourceRoot":"","sources":["../../../src/search/adapters/duckduckgo.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElE,eAAO,MAAM,iBAAiB,EAAE,mBAqD/B,CAAC"}
@@ -0,0 +1,52 @@
1
+ export const duckduckgoAdapter = {
2
+ name: "duckduckgo",
3
+ buildUrl(query) {
4
+ const q = encodeURIComponent(query).replace(/%20/g, "+");
5
+ return `https://html.duckduckgo.com/html/?q=${q}`;
6
+ },
7
+ waitStrategy: { readyState: "domcontentloaded" },
8
+ async parseResults(page, maxResults) {
9
+ const raw = await page.$$eval("div.result, div.web-result", (els, max) => {
10
+ const out = [];
11
+ const limit = Math.max(0, Math.min(50, Number(max) || 10));
12
+ for (const el of els) {
13
+ if (out.length >= limit)
14
+ break;
15
+ const a = el.querySelector("a.result__a");
16
+ if (!a)
17
+ continue;
18
+ const title = (a.textContent ?? "").trim();
19
+ let url = a.getAttribute("href") ?? "";
20
+ const m = url.match(/[?&]uddg=([^&]+)/);
21
+ if (m?.[1]) {
22
+ try {
23
+ url = decodeURIComponent(m[1]);
24
+ }
25
+ catch {
26
+ // ignore
27
+ }
28
+ }
29
+ if (url.startsWith("//"))
30
+ url = `https:${url}`;
31
+ // Defense-in-depth: reject non-http(s) URLs that might surface
32
+ // from adversarial SERPs (javascript:, data:, etc.).
33
+ try {
34
+ const u = new URL(url);
35
+ if (u.protocol !== "http:" && u.protocol !== "https:")
36
+ continue;
37
+ }
38
+ catch {
39
+ continue;
40
+ }
41
+ const snippetEl = el.querySelector("a.result__snippet, .result__snippet");
42
+ const snippet = (snippetEl?.textContent ?? "").trim();
43
+ if (!title || !url)
44
+ continue;
45
+ out.push({ title, url, snippet });
46
+ }
47
+ return out;
48
+ }, maxResults);
49
+ return raw.map((r, i) => ({ ...r, rank: i + 1 }));
50
+ },
51
+ };
52
+ //# sourceMappingURL=duckduckgo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duckduckgo.js","sourceRoot":"","sources":["../../../src/search/adapters/duckduckgo.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,iBAAiB,GAAwB;IACrD,IAAI,EAAE,YAAY;IAElB,QAAQ,CAAC,KAAa;QACrB,MAAM,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACzD,OAAO,uCAAuC,CAAC,EAAE,CAAC;IACnD,CAAC;IAED,YAAY,EAAE,EAAE,UAAU,EAAE,kBAAkB,EAAE;IAEhD,KAAK,CAAC,YAAY,CAAC,IAAU,EAAE,UAAkB;QAChD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAC5B,4BAA4B,EAC5B,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACZ,MAAM,GAAG,GAAsD,EAAE,CAAC;YAClE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3D,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;gBACtB,IAAI,GAAG,CAAC,MAAM,IAAI,KAAK;oBAAE,MAAM;gBAC/B,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,aAAa,CAGhC,CAAC;gBACT,IAAI,CAAC,CAAC;oBAAE,SAAS;gBACjB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC3C,IAAI,GAAG,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACvC,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;gBACxC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACZ,IAAI,CAAC;wBACJ,GAAG,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBAChC,CAAC;oBAAC,MAAM,CAAC;wBACR,SAAS;oBACV,CAAC;gBACF,CAAC;gBACD,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;oBAAE,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC;gBAC/C,+DAA+D;gBAC/D,qDAAqD;gBACrD,IAAI,CAAC;oBACJ,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;oBACvB,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ;wBAAE,SAAS;gBACjE,CAAC;gBAAC,MAAM,CAAC;oBACR,SAAS;gBACV,CAAC;gBACD,MAAM,SAAS,GAAG,EAAE,CAAC,aAAa,CAAC,qCAAqC,CAAC,CAAC;gBAC1E,MAAM,OAAO,GAAI,CAAC,SAAS,EAAE,WAAW,IAAI,EAAE,CAAY,CAAC,IAAI,EAAE,CAAC;gBAClE,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG;oBAAE,SAAS;gBAC7B,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YACnC,CAAC;YACD,OAAO,GAAG,CAAC;QACZ,CAAC,EACD,UAAU,CACV,CAAC;QACF,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,CAAC;CACD,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { Page } from "playwright-core";
2
+ export interface RawResult {
3
+ readonly title: string;
4
+ readonly url: string;
5
+ readonly snippet: string;
6
+ readonly rank: number;
7
+ }
8
+ export interface SearchEngineAdapter {
9
+ readonly name: "duckduckgo";
10
+ buildUrl(query: string): string;
11
+ readonly waitStrategy: {
12
+ readyState: "domcontentloaded" | "load" | "networkidle";
13
+ };
14
+ parseResults(page: Page, maxResults: number): Promise<RawResult[]>;
15
+ }
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/search/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE;QAAE,UAAU,EAAE,kBAAkB,GAAG,MAAM,GAAG,aAAa,CAAA;KAAE,CAAC;IACnF,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;CACnE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/search/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import { promises as dns } from "node:dns";
2
+ export type LookupFn = typeof dns.lookup;
3
+ export declare function assertSafeTarget(url: string, opts?: {
4
+ lookup?: LookupFn;
5
+ }): Promise<void>;
6
+ //# sourceMappingURL=ssrf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf.d.ts","sourceRoot":"","sources":["../../src/security/ssrf.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,GAAG,EAAE,MAAM,UAAU,CAAC;AA8C3C,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,MAAM,CAAC;AAEzC,wBAAsB,gBAAgB,CACrC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,QAAQ,CAAA;CAAO,GAC9B,OAAO,CAAC,IAAI,CAAC,CAqCf"}
@@ -0,0 +1,82 @@
1
+ import { promises as dns } from "node:dns";
2
+ // Private / loopback / link-local ranges for IPv4.
3
+ const IPV4_PRIVATE = [
4
+ { prefix: [127], mask: 0xff000000 }, // 127.0.0.0/8 loopback
5
+ { prefix: [10], mask: 0xff000000 }, // 10.0.0.0/8
6
+ { prefix: [172, 16], mask: 0xfff00000 }, // 172.16.0.0/12
7
+ { prefix: [192, 168], mask: 0xffff0000 }, // 192.168.0.0/16
8
+ { prefix: [169, 254], mask: 0xffff0000 }, // 169.254.0.0/16 link-local + AWS/GCP metadata
9
+ { prefix: [0], mask: 0xff000000 }, // 0.0.0.0/8
10
+ { prefix: [100, 64], mask: 0xffc00000 }, // 100.64.0.0/10 CGNAT
11
+ ];
12
+ function ipv4ToUint(parts) {
13
+ return (((parts[0] ?? 0) << 24) | ((parts[1] ?? 0) << 16) | ((parts[2] ?? 0) << 8) | (parts[3] ?? 0));
14
+ }
15
+ function isPrivateIPv4(ip) {
16
+ const parts = ip.split(".").map((p) => Number.parseInt(p, 10));
17
+ if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) {
18
+ return true; // malformed = fail safe
19
+ }
20
+ const ipInt = ipv4ToUint(parts) >>> 0;
21
+ for (const { prefix, mask } of IPV4_PRIVATE) {
22
+ const prefixInt = ipv4ToUint([...prefix, 0, 0, 0, 0].slice(0, 4)) >>> 0;
23
+ if ((ipInt & mask) === (prefixInt & mask))
24
+ return true;
25
+ }
26
+ return false;
27
+ }
28
+ function isPrivateIPv6(ip) {
29
+ // ::1 loopback
30
+ if (ip === "::1" || ip === "0:0:0:0:0:0:0:1")
31
+ return true;
32
+ const lower = ip.toLowerCase();
33
+ // fc00::/7 unique-local
34
+ if (/^f[cd][0-9a-f]{2}:/.test(lower))
35
+ return true;
36
+ // fe80::/10 link-local
37
+ if (/^fe[89ab][0-9a-f]:/.test(lower))
38
+ return true;
39
+ // ::ffff:IPv4 mapped — check the IPv4 portion
40
+ const mapped = lower.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
41
+ if (mapped?.[1])
42
+ return isPrivateIPv4(mapped[1]);
43
+ return false;
44
+ }
45
+ export async function assertSafeTarget(url, opts = {}) {
46
+ const parsed = new URL(url);
47
+ const hostname = parsed.hostname;
48
+ // Literal IPv4 in the URL — check directly.
49
+ if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
50
+ if (isPrivateIPv4(hostname)) {
51
+ throw new Error(`SSRF: target ${hostname} is a private IPv4`);
52
+ }
53
+ return;
54
+ }
55
+ // IPv6 literal — hostname will have brackets stripped by URL parser.
56
+ if (hostname.includes(":")) {
57
+ if (isPrivateIPv6(hostname.replace(/^\[|\]$/g, ""))) {
58
+ throw new Error(`SSRF: target ${hostname} is a private IPv6`);
59
+ }
60
+ return;
61
+ }
62
+ // DNS resolve and check every address.
63
+ const lookup = opts.lookup ?? dns.lookup;
64
+ let addrs;
65
+ try {
66
+ // Using overload: lookup(hostname, { all: true, verbatim: true })
67
+ const result = (await lookup(hostname, { all: true, verbatim: true }));
68
+ addrs = result;
69
+ }
70
+ catch (err) {
71
+ throw new Error(`SSRF: cannot resolve ${hostname}: ${err instanceof Error ? err.message : String(err)}`);
72
+ }
73
+ for (const { address, family } of addrs) {
74
+ if (family === 4 && isPrivateIPv4(address)) {
75
+ throw new Error(`SSRF: ${hostname} resolves to private IPv4 ${address}`);
76
+ }
77
+ if (family === 6 && isPrivateIPv6(address)) {
78
+ throw new Error(`SSRF: ${hostname} resolves to private IPv6 ${address}`);
79
+ }
80
+ }
81
+ }
82
+ //# sourceMappingURL=ssrf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf.js","sourceRoot":"","sources":["../../src/security/ssrf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { CamoufoxClient } from "../client/camoufox-client.js";
2
+ import type { Launcher } from "../client/launcher.js";
3
+ import type { CamoufoxConfig } from "../types.js";
4
+ export declare class CamoufoxService {
5
+ private readonly config;
6
+ private readonly launcherFactory;
7
+ private basePath;
8
+ private client;
9
+ constructor(opts?: {
10
+ config?: CamoufoxConfig;
11
+ launcherFactory?: () => Launcher;
12
+ });
13
+ initialize(cwd: string, _signal?: AbortSignal): Promise<void>;
14
+ shutdown(): Promise<void>;
15
+ getConfig(): CamoufoxConfig;
16
+ getBasePath(): string | null;
17
+ getClient(): CamoufoxClient;
18
+ }
19
+ //# sourceMappingURL=camoufox-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camoufox-service.d.ts","sourceRoot":"","sources":["../../src/services/camoufox-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAMlD,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,MAAM,CAA+B;gBAEjC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,cAAc,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,QAAQ,CAAA;KAAE;IAW1E,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAO/B,SAAS,IAAI,cAAc;IAI3B,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B,SAAS,IAAI,cAAc;CAM3B"}