@phi-code-admin/camofox-browser 1.0.0 → 1.0.2

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 (53) hide show
  1. package/AGENTS.md +571 -571
  2. package/Dockerfile +86 -86
  3. package/LICENSE +21 -21
  4. package/README.md +691 -691
  5. package/camofox.config.json +10 -10
  6. package/lib/auth.js +134 -134
  7. package/lib/camoufox-executable.js +189 -189
  8. package/lib/config.js +153 -153
  9. package/lib/cookies.js +119 -119
  10. package/lib/downloads.js +168 -168
  11. package/lib/extract.js +74 -74
  12. package/lib/fly.js +54 -54
  13. package/lib/images.js +88 -88
  14. package/lib/inflight.js +16 -16
  15. package/lib/launcher.js +47 -47
  16. package/lib/macros.js +31 -31
  17. package/lib/metrics.js +184 -184
  18. package/lib/openapi.js +105 -105
  19. package/lib/persistence.js +89 -89
  20. package/lib/plugins.js +178 -175
  21. package/lib/proxy.js +277 -277
  22. package/lib/reporter.js +1102 -1102
  23. package/lib/request-utils.js +59 -59
  24. package/lib/resources.js +76 -76
  25. package/lib/snapshot.js +41 -41
  26. package/lib/tmp-cleanup.js +108 -108
  27. package/lib/tracing.js +137 -137
  28. package/openclaw.plugin.json +268 -268
  29. package/package.json +148 -148
  30. package/plugin.ts +758 -758
  31. package/plugins/persistence/AGENTS.md +37 -37
  32. package/plugins/persistence/README.md +48 -48
  33. package/plugins/persistence/index.js +124 -124
  34. package/plugins/vnc/AGENTS.md +42 -42
  35. package/plugins/vnc/README.md +165 -165
  36. package/plugins/vnc/apt.txt +7 -7
  37. package/plugins/vnc/index.js +142 -142
  38. package/plugins/vnc/spawn.js +8 -8
  39. package/plugins/vnc/vnc-launcher.js +64 -64
  40. package/plugins/vnc/vnc-watcher.sh +82 -82
  41. package/plugins/youtube/AGENTS.md +25 -25
  42. package/plugins/youtube/apt.txt +1 -1
  43. package/plugins/youtube/index.js +206 -206
  44. package/plugins/youtube/post-install.sh +5 -5
  45. package/plugins/youtube/youtube.js +301 -301
  46. package/run.sh +37 -37
  47. package/scripts/exec.js +8 -8
  48. package/scripts/generate-openapi.js +24 -24
  49. package/scripts/install-plugin-deps.sh +63 -63
  50. package/scripts/plugin.js +342 -342
  51. package/scripts/sync-version.js +25 -25
  52. package/server.js +6062 -6059
  53. package/tsconfig.json +12 -12
@@ -1,37 +1,37 @@
1
- # Persistence Plugin — Agent Guide
2
-
3
- Saves and restores per-user browser storage state (cookies + localStorage) across session restarts using Playwright's `storageState` API. Enabled by default — profiles persist to `~/.camofox/profiles/`.
4
-
5
- ## How It Works
6
-
7
- - `session:creating` hook → loads saved `storage_state.json` into `contextOptions.storageState`
8
- - `session:created` hook → imports bootstrap cookies if no persisted state exists
9
- - `session:cookies:import` / `session:destroyed` / `server:shutdown` → checkpoints state to disk
10
-
11
- All hooks are async and awaited via `emitAsync()` — storage state is guaranteed loaded before the context is created.
12
-
13
- ## Key Files
14
-
15
- - `index.js` — lifecycle hooks (no routes, no `child_process`)
16
- - `persistence.test.js` — unit tests for `lib/persistence.js` helpers
17
- - `plugin.test.js` — integration tests for plugin lifecycle hooks
18
-
19
- ## Storage Layout
20
-
21
- ```
22
- ~/.camofox/profiles/
23
- └── <sha256(userId)>/
24
- └── storage_state.json
25
- ```
26
-
27
- ## Configuration
28
-
29
- Enabled by default. Override profile directory with `CAMOFOX_PROFILE_DIR` env var or `"profileDir"` in plugin config. To disable: `"persistence": { "enabled": false }` in `camofox.config.json`.
30
-
31
- ## Original Contributors
32
-
33
- - [@company8](https://github.com/company8) — original persistence concept ([PR #62](https://github.com/jo-inc/camofox-browser/pull/62))
34
- - [@eddieoz](https://github.com/eddieoz) — cookie auto-load on startup ([PR #55](https://github.com/jo-inc/camofox-browser/pull/55))
35
- - [@pradeepe](https://github.com/pradeepe) — plugin system integration, atomic writes, inflight coalescing
36
-
37
- For PRs touching this plugin, tag the contributors above for review.
1
+ # Persistence Plugin — Agent Guide
2
+
3
+ Saves and restores per-user browser storage state (cookies + localStorage) across session restarts using Playwright's `storageState` API. Enabled by default — profiles persist to `~/.camofox/profiles/`.
4
+
5
+ ## How It Works
6
+
7
+ - `session:creating` hook → loads saved `storage_state.json` into `contextOptions.storageState`
8
+ - `session:created` hook → imports bootstrap cookies if no persisted state exists
9
+ - `session:cookies:import` / `session:destroyed` / `server:shutdown` → checkpoints state to disk
10
+
11
+ All hooks are async and awaited via `emitAsync()` — storage state is guaranteed loaded before the context is created.
12
+
13
+ ## Key Files
14
+
15
+ - `index.js` — lifecycle hooks (no routes, no `child_process`)
16
+ - `persistence.test.js` — unit tests for `lib/persistence.js` helpers
17
+ - `plugin.test.js` — integration tests for plugin lifecycle hooks
18
+
19
+ ## Storage Layout
20
+
21
+ ```
22
+ ~/.camofox/profiles/
23
+ └── <sha256(userId)>/
24
+ └── storage_state.json
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ Enabled by default. Override profile directory with `CAMOFOX_PROFILE_DIR` env var or `"profileDir"` in plugin config. To disable: `"persistence": { "enabled": false }` in `camofox.config.json`.
30
+
31
+ ## Original Contributors
32
+
33
+ - [@company8](https://github.com/company8) — original persistence concept ([PR #62](https://github.com/jo-inc/camofox-browser/pull/62))
34
+ - [@eddieoz](https://github.com/eddieoz) — cookie auto-load on startup ([PR #55](https://github.com/jo-inc/camofox-browser/pull/55))
35
+ - [@pradeepe](https://github.com/pradeepe) — plugin system integration, atomic writes, inflight coalescing
36
+
37
+ For PRs touching this plugin, tag the contributors above for review.
@@ -1,48 +1,48 @@
1
- # persistence
2
-
3
- Optional per-user browser storage state persistence for camofox-browser.
4
-
5
- Saves and restores cookies + localStorage across session restarts, container deploys, and idle timeouts using Playwright's `storageState` API.
6
-
7
- ## Configuration
8
-
9
- In `camofox.config.json`:
10
-
11
- ```json
12
- {
13
- "plugins": {
14
- "persistence": {
15
- "enabled": true,
16
- "profileDir": "/data/profiles"
17
- }
18
- }
19
- }
20
- ```
21
-
22
- Or override via environment variable:
23
-
24
- ```
25
- CAMOFOX_PROFILE_DIR=/data/profiles
26
- ```
27
-
28
- ## How it works
29
-
30
- - **Session create**: If a persisted `storageState` exists for the `userId`, it's restored into the new Playwright context.
31
- - **First run**: If no persisted state exists, bootstrap cookies from `CAMOFOX_COOKIES_DIR/cookies.txt` are imported (if present).
32
- - **Cookie import / session close / shutdown**: Storage state is checkpointed to disk via atomic tmp-write + rename.
33
- - **User isolation**: Each `userId` maps to a deterministic SHA256-hashed subdirectory under `profileDir`, so arbitrary userIds are path-safe.
34
-
35
- ## Docker
36
-
37
- When running with Docker, mount the profile directory as a volume:
38
-
39
- ```bash
40
- docker run -d \
41
- -p 9377:9377 \
42
- -v /host/profiles:/data/profiles \
43
- camofox-browser
44
- ```
45
-
46
- ## Credits
47
-
48
- Based on PR #62 by [company8](https://github.com/company8).
1
+ # persistence
2
+
3
+ Optional per-user browser storage state persistence for camofox-browser.
4
+
5
+ Saves and restores cookies + localStorage across session restarts, container deploys, and idle timeouts using Playwright's `storageState` API.
6
+
7
+ ## Configuration
8
+
9
+ In `camofox.config.json`:
10
+
11
+ ```json
12
+ {
13
+ "plugins": {
14
+ "persistence": {
15
+ "enabled": true,
16
+ "profileDir": "/data/profiles"
17
+ }
18
+ }
19
+ }
20
+ ```
21
+
22
+ Or override via environment variable:
23
+
24
+ ```
25
+ CAMOFOX_PROFILE_DIR=/data/profiles
26
+ ```
27
+
28
+ ## How it works
29
+
30
+ - **Session create**: If a persisted `storageState` exists for the `userId`, it's restored into the new Playwright context.
31
+ - **First run**: If no persisted state exists, bootstrap cookies from `CAMOFOX_COOKIES_DIR/cookies.txt` are imported (if present).
32
+ - **Cookie import / session close / shutdown**: Storage state is checkpointed to disk via atomic tmp-write + rename.
33
+ - **User isolation**: Each `userId` maps to a deterministic SHA256-hashed subdirectory under `profileDir`, so arbitrary userIds are path-safe.
34
+
35
+ ## Docker
36
+
37
+ When running with Docker, mount the profile directory as a volume:
38
+
39
+ ```bash
40
+ docker run -d \
41
+ -p 9377:9377 \
42
+ -v /host/profiles:/data/profiles \
43
+ camofox-browser
44
+ ```
45
+
46
+ ## Credits
47
+
48
+ Based on PR #62 by [company8](https://github.com/company8).
@@ -1,124 +1,124 @@
1
- /**
2
- * Persistence plugin for camofox-browser.
3
- *
4
- * Saves and restores per-user browser storage state (cookies + localStorage)
5
- * across session restarts using Playwright's storageState API.
6
- *
7
- * Configuration (camofox.config.json):
8
- * {
9
- * "plugins": {
10
- * "persistence": {
11
- * "enabled": true,
12
- * "profileDir": "/data/profiles"
13
- * }
14
- * }
15
- * }
16
- *
17
- * Or via environment variables (overrides config file):
18
- * CAMOFOX_PROFILE_DIR=/data/profiles
19
- *
20
- * Each userId gets a deterministic SHA256-hashed subdirectory under profileDir.
21
- * Storage state is checkpointed on cookie import, session close, and shutdown.
22
- * On session creation, saved state is restored into the new Playwright context
23
- * via the session:creating hook (mutates contextOptions.storageState).
24
- */
25
-
26
- import {
27
- getUserPersistencePaths,
28
- loadPersistedStorageState,
29
- persistStorageState,
30
- } from '../../lib/persistence.js';
31
- import { importBootstrapCookies } from '../../lib/cookies.js';
32
-
33
- export async function register(app, ctx, pluginConfig = {}) {
34
- const { events, config, log } = ctx;
35
-
36
- // Resolve profileDir: env var > plugin config > global config default (~/.camofox/profiles)
37
- const profileDir = process.env.CAMOFOX_PROFILE_DIR || pluginConfig.profileDir || config.profileDir;
38
- if (!profileDir) {
39
- log('warn', 'persistence plugin: no profileDir configured, plugin disabled');
40
- return;
41
- }
42
-
43
- const logger = {
44
- warn: (msg, fields = {}) => log('warn', msg, fields),
45
- };
46
-
47
- log('info', 'persistence plugin enabled', { profileDir });
48
-
49
- // Track active sessions for checkpoint on close
50
- const activeSessions = new Map(); // userId -> context
51
-
52
- /**
53
- * Checkpoint storage state to disk for a userId.
54
- */
55
- async function checkpoint(userId, context, reason) {
56
- if (!context) return;
57
- const result = await persistStorageState({ profileDir, userId, context, logger });
58
- if (result.persisted) {
59
- log('info', 'storage state persisted', { userId, reason, path: result.storageStatePath });
60
- }
61
- return result;
62
- }
63
-
64
- // --- Lifecycle hooks ---
65
-
66
- // Before session context is created: inject storageState if we have one saved
67
- events.on('session:creating', async ({ userId, contextOptions }) => {
68
- const storageStatePath = await loadPersistedStorageState(profileDir, userId, logger);
69
- if (storageStatePath) {
70
- contextOptions.storageState = storageStatePath;
71
- log('info', 'restoring persisted storage state', { userId, storageStatePath });
72
- }
73
- });
74
-
75
- // After session is created: import bootstrap cookies if no persisted state,
76
- // and track the context for later checkpointing
77
- events.on('session:created', async ({ userId, context }) => {
78
- activeSessions.set(userId, context);
79
-
80
- // If no persisted state was restored, try bootstrap cookies
81
- const existingState = await loadPersistedStorageState(profileDir, userId, logger);
82
- if (!existingState) {
83
- const result = await importBootstrapCookies({
84
- cookiesDir: config.cookiesDir,
85
- context,
86
- logger,
87
- });
88
- if (result.imported > 0) {
89
- log('info', 'bootstrap cookies imported', { userId, count: result.imported, source: result.source });
90
- await checkpoint(userId, context, 'bootstrap_cookies');
91
- }
92
- }
93
- });
94
-
95
- // On cookie import: checkpoint
96
- events.on('session:cookies:import', async ({ userId }) => {
97
- const context = activeSessions.get(userId);
98
- if (context) {
99
- await checkpoint(userId, context, 'cookie_import');
100
- }
101
- });
102
-
103
- // On session destroying (pre-close): checkpoint while context is still alive
104
- events.on('session:destroying', async ({ userId, reason }) => {
105
- const context = activeSessions.get(userId);
106
- if (context) {
107
- await checkpoint(userId, context, reason).catch(() => {});
108
- activeSessions.delete(userId);
109
- }
110
- });
111
-
112
- // On session destroyed (post-close): cleanup tracking if not already done
113
- events.on('session:destroyed', async ({ userId }) => {
114
- activeSessions.delete(userId);
115
- });
116
-
117
- // On shutdown: checkpoint all remaining sessions
118
- events.on('server:shutdown', async () => {
119
- for (const [userId, context] of activeSessions) {
120
- await checkpoint(userId, context, 'shutdown').catch(() => {});
121
- }
122
- activeSessions.clear();
123
- });
124
- }
1
+ /**
2
+ * Persistence plugin for camofox-browser.
3
+ *
4
+ * Saves and restores per-user browser storage state (cookies + localStorage)
5
+ * across session restarts using Playwright's storageState API.
6
+ *
7
+ * Configuration (camofox.config.json):
8
+ * {
9
+ * "plugins": {
10
+ * "persistence": {
11
+ * "enabled": true,
12
+ * "profileDir": "/data/profiles"
13
+ * }
14
+ * }
15
+ * }
16
+ *
17
+ * Or via environment variables (overrides config file):
18
+ * CAMOFOX_PROFILE_DIR=/data/profiles
19
+ *
20
+ * Each userId gets a deterministic SHA256-hashed subdirectory under profileDir.
21
+ * Storage state is checkpointed on cookie import, session close, and shutdown.
22
+ * On session creation, saved state is restored into the new Playwright context
23
+ * via the session:creating hook (mutates contextOptions.storageState).
24
+ */
25
+
26
+ import {
27
+ getUserPersistencePaths,
28
+ loadPersistedStorageState,
29
+ persistStorageState,
30
+ } from '../../lib/persistence.js';
31
+ import { importBootstrapCookies } from '../../lib/cookies.js';
32
+
33
+ export async function register(app, ctx, pluginConfig = {}) {
34
+ const { events, config, log } = ctx;
35
+
36
+ // Resolve profileDir: env var > plugin config > global config default (~/.camofox/profiles)
37
+ const profileDir = process.env.CAMOFOX_PROFILE_DIR || pluginConfig.profileDir || config.profileDir;
38
+ if (!profileDir) {
39
+ log('warn', 'persistence plugin: no profileDir configured, plugin disabled');
40
+ return;
41
+ }
42
+
43
+ const logger = {
44
+ warn: (msg, fields = {}) => log('warn', msg, fields),
45
+ };
46
+
47
+ log('info', 'persistence plugin enabled', { profileDir });
48
+
49
+ // Track active sessions for checkpoint on close
50
+ const activeSessions = new Map(); // userId -> context
51
+
52
+ /**
53
+ * Checkpoint storage state to disk for a userId.
54
+ */
55
+ async function checkpoint(userId, context, reason) {
56
+ if (!context) return;
57
+ const result = await persistStorageState({ profileDir, userId, context, logger });
58
+ if (result.persisted) {
59
+ log('info', 'storage state persisted', { userId, reason, path: result.storageStatePath });
60
+ }
61
+ return result;
62
+ }
63
+
64
+ // --- Lifecycle hooks ---
65
+
66
+ // Before session context is created: inject storageState if we have one saved
67
+ events.on('session:creating', async ({ userId, contextOptions }) => {
68
+ const storageStatePath = await loadPersistedStorageState(profileDir, userId, logger);
69
+ if (storageStatePath) {
70
+ contextOptions.storageState = storageStatePath;
71
+ log('info', 'restoring persisted storage state', { userId, storageStatePath });
72
+ }
73
+ });
74
+
75
+ // After session is created: import bootstrap cookies if no persisted state,
76
+ // and track the context for later checkpointing
77
+ events.on('session:created', async ({ userId, context }) => {
78
+ activeSessions.set(userId, context);
79
+
80
+ // If no persisted state was restored, try bootstrap cookies
81
+ const existingState = await loadPersistedStorageState(profileDir, userId, logger);
82
+ if (!existingState) {
83
+ const result = await importBootstrapCookies({
84
+ cookiesDir: config.cookiesDir,
85
+ context,
86
+ logger,
87
+ });
88
+ if (result.imported > 0) {
89
+ log('info', 'bootstrap cookies imported', { userId, count: result.imported, source: result.source });
90
+ await checkpoint(userId, context, 'bootstrap_cookies');
91
+ }
92
+ }
93
+ });
94
+
95
+ // On cookie import: checkpoint
96
+ events.on('session:cookies:import', async ({ userId }) => {
97
+ const context = activeSessions.get(userId);
98
+ if (context) {
99
+ await checkpoint(userId, context, 'cookie_import');
100
+ }
101
+ });
102
+
103
+ // On session destroying (pre-close): checkpoint while context is still alive
104
+ events.on('session:destroying', async ({ userId, reason }) => {
105
+ const context = activeSessions.get(userId);
106
+ if (context) {
107
+ await checkpoint(userId, context, reason).catch(() => {});
108
+ activeSessions.delete(userId);
109
+ }
110
+ });
111
+
112
+ // On session destroyed (post-close): cleanup tracking if not already done
113
+ events.on('session:destroyed', async ({ userId }) => {
114
+ activeSessions.delete(userId);
115
+ });
116
+
117
+ // On shutdown: checkpoint all remaining sessions
118
+ events.on('server:shutdown', async () => {
119
+ for (const [userId, context] of activeSessions) {
120
+ await checkpoint(userId, context, 'shutdown').catch(() => {});
121
+ }
122
+ activeSessions.clear();
123
+ });
124
+ }
@@ -1,42 +1,42 @@
1
- # VNC Plugin — Agent Guide
2
-
3
- Interactive browser access via noVNC. Log into sites visually, solve CAPTCHAs, approve OAuth prompts — then export the authenticated storage state for agent reuse.
4
-
5
- ## Endpoints
6
-
7
- - `GET /vnc/status` — check if VNC is running (no auth)
8
- - `GET /sessions/:userId/storage_state` — export cookies + localStorage as JSON (requires auth)
9
-
10
- ## Activation
11
-
12
- Disabled by default. Enable with `ENABLE_VNC=1` env var or `"vnc": { "enabled": true }` in `camofox.config.json`.
13
-
14
- ## Key Files
15
-
16
- - `index.js` — route handlers only (no `child_process`, no `process.env` reads)
17
- - `vnc-launcher.js` — process management, config resolution from env vars (`child_process` isolated here)
18
- - `vnc-watcher.sh` — shell script that detects Xvfb, attaches x11vnc, starts noVNC
19
- - `vnc.test.js` — unit tests
20
- - `apt.txt` — system deps (x11vnc, novnc, websockify, etc.)
21
-
22
- ## Code Separation
23
-
24
- `child_process` is in `vnc-launcher.js`, route handlers are in `index.js`, env var reads are in `vnc-launcher.js` — separate files per project conventions.
25
-
26
- ## Security
27
-
28
- - noVNC binds to `127.0.0.1` by default — set `VNC_BIND=0.0.0.0` to expose externally
29
- - Set `VNC_PASSWORD` for password-protected access
30
- - `VIEW_ONLY=1` disables keyboard/mouse input (observation only)
31
- - Storage state export endpoint requires auth (API key or loopback)
32
-
33
- ## Architecture
34
-
35
- The plugin overrides `ctx.createVirtualDisplay` to use a higher-resolution display (default 1920x1080 instead of 1x1). `vnc-watcher.sh` polls for the Xvfb process, then attaches x11vnc + noVNC on top.
36
-
37
- ## Original Contributors
38
-
39
- - [@leoneparise](https://github.com/leoneparise) — original VNC implementation + keyboard mode ([PR #65](https://github.com/jo-inc/camofox-browser/pull/65), [PR #66](https://github.com/jo-inc/camofox-browser/pull/66))
40
- - [@pradeepe](https://github.com/pradeepe) — plugin system integration, code separation refactor, security hardening
41
-
42
- For PRs touching this plugin, tag the contributors above for review.
1
+ # VNC Plugin — Agent Guide
2
+
3
+ Interactive browser access via noVNC. Log into sites visually, solve CAPTCHAs, approve OAuth prompts — then export the authenticated storage state for agent reuse.
4
+
5
+ ## Endpoints
6
+
7
+ - `GET /vnc/status` — check if VNC is running (no auth)
8
+ - `GET /sessions/:userId/storage_state` — export cookies + localStorage as JSON (requires auth)
9
+
10
+ ## Activation
11
+
12
+ Disabled by default. Enable with `ENABLE_VNC=1` env var or `"vnc": { "enabled": true }` in `camofox.config.json`.
13
+
14
+ ## Key Files
15
+
16
+ - `index.js` — route handlers only (no `child_process`, no `process.env` reads)
17
+ - `vnc-launcher.js` — process management, config resolution from env vars (`child_process` isolated here)
18
+ - `vnc-watcher.sh` — shell script that detects Xvfb, attaches x11vnc, starts noVNC
19
+ - `vnc.test.js` — unit tests
20
+ - `apt.txt` — system deps (x11vnc, novnc, websockify, etc.)
21
+
22
+ ## Code Separation
23
+
24
+ `child_process` is in `vnc-launcher.js`, route handlers are in `index.js`, env var reads are in `vnc-launcher.js` — separate files per project conventions.
25
+
26
+ ## Security
27
+
28
+ - noVNC binds to `127.0.0.1` by default — set `VNC_BIND=0.0.0.0` to expose externally
29
+ - Set `VNC_PASSWORD` for password-protected access
30
+ - `VIEW_ONLY=1` disables keyboard/mouse input (observation only)
31
+ - Storage state export endpoint requires auth (API key or loopback)
32
+
33
+ ## Architecture
34
+
35
+ The plugin overrides `ctx.createVirtualDisplay` to use a higher-resolution display (default 1920x1080 instead of 1x1). `vnc-watcher.sh` polls for the Xvfb process, then attaches x11vnc + noVNC on top.
36
+
37
+ ## Original Contributors
38
+
39
+ - [@leoneparise](https://github.com/leoneparise) — original VNC implementation + keyboard mode ([PR #65](https://github.com/jo-inc/camofox-browser/pull/65), [PR #66](https://github.com/jo-inc/camofox-browser/pull/66))
40
+ - [@pradeepe](https://github.com/pradeepe) — plugin system integration, code separation refactor, security hardening
41
+
42
+ For PRs touching this plugin, tag the contributors above for review.