stelz 0.2.0 → 0.2.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 (32) hide show
  1. package/README.md +12 -6
  2. package/THIRD_PARTY_NOTICES.md +3 -1
  3. package/dist/cli.js +4295 -1291
  4. package/dist/plugin.js +92 -17
  5. package/dist/supervisor.js +137 -34
  6. package/dist/ui/assets/index-7xWId3LL.js +11 -0
  7. package/dist/ui/assets/index-By17r8AD.css +1 -0
  8. package/dist/ui/index.html +4 -3
  9. package/docs/README.md +24 -7
  10. package/docs/architecture.md +56 -5
  11. package/docs/bootstrap.md +195 -0
  12. package/docs/clients.md +5 -1
  13. package/docs/distribution.md +26 -17
  14. package/docs/onboarding-acceptance.md +79 -0
  15. package/docs/profile-transfer.md +89 -0
  16. package/docs/tests.md +76 -27
  17. package/docs/ui.md +31 -15
  18. package/examples/onboarding-fixture.html +22 -0
  19. package/package.json +62 -8
  20. package/skills/stelz-browser/SKILL.md +7 -3
  21. package/skills/stelz-browser/references/bootstrap.md +16 -0
  22. package/dist/ui/chunk-6y88c98t.css +0 -1
  23. package/dist/ui/chunk-jt6ajg4c.js +0 -11
  24. /package/dist/ui/{font-328d50b6ece9ca25.woff → assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff} +0 -0
  25. /package/dist/ui/{font-08949f728dc52d52.woff2 → assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2} +0 -0
  26. /package/dist/ui/{font-3b646991d30055a9.woff2 → assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2} +0 -0
  27. /package/dist/ui/{font-828907bfd14855c8.woff → assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff} +0 -0
  28. /package/dist/ui/{font-0717336fb31fcdcd.woff2 → assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2} +0 -0
  29. /package/dist/ui/{font-8d2c7b2eca91869d.woff → assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff} +0 -0
  30. /package/dist/ui/{font-7861a349af1e925a.woff → assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff} +0 -0
  31. /package/dist/ui/{font-8960851d691c054e.woff2 → assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2} +0 -0
  32. /package/dist/ui/{theme-j8bgvgsv.js → theme-fqsa3r76.js} +0 -0
package/dist/plugin.js CHANGED
@@ -14,12 +14,21 @@ var ROOT = fileURLToPath(new URL("..", import.meta.url));
14
14
  // package.json
15
15
  var package_default = {
16
16
  name: "stelz",
17
- version: "0.2.0",
17
+ version: "0.2.1",
18
18
  description: "Standalone persistent Camoufox profiles with exclusive MCP control",
19
19
  license: "MIT",
20
20
  homepage: "https://github.com/souljorje/stelz#readme",
21
- bugs: { url: "https://github.com/souljorje/stelz/issues" },
22
- keywords: ["browser", "automation", "camoufox", "playwright", "mcp", "persistent-profiles"],
21
+ bugs: {
22
+ url: "https://github.com/souljorje/stelz/issues"
23
+ },
24
+ keywords: [
25
+ "browser",
26
+ "automation",
27
+ "camoufox",
28
+ "playwright",
29
+ "mcp",
30
+ "persistent-profiles"
31
+ ],
23
32
  bin: {
24
33
  stelz: "dist/cli.js"
25
34
  },
@@ -41,15 +50,45 @@ var package_default = {
41
50
  },
42
51
  repository: {
43
52
  type: "git",
44
- url: "https://github.com/souljorje/stelz.git",
45
- directory: "packages/stelz"
53
+ url: "https://github.com/souljorje/stelz.git"
54
+ },
55
+ publishConfig: {
56
+ access: "public",
57
+ registry: "https://registry.npmjs.org"
46
58
  },
47
- publishConfig: { access: "public", registry: "https://registry.npmjs.org" },
48
59
  type: "module",
49
60
  packageManager: "bun@1.4.4",
50
61
  scripts: {
51
- build: "bun ../../scripts/build.ts",
52
- prepack: "bun run build"
62
+ build: "bun scripts/build.ts",
63
+ pack: "bun scripts/pack.ts",
64
+ setup: "bun src/cli/cli.ts setup",
65
+ start: "bun src/cli/cli.ts start",
66
+ status: "bun src/cli/cli.ts status",
67
+ stop: "bun src/cli/cli.ts stop",
68
+ pause: "bun src/cli/cli.ts pause",
69
+ resume: "bun src/cli/cli.ts resume",
70
+ mcp: "bun src/cli/cli.ts mcp",
71
+ ui: "bun --no-env-file src/cli/cli.ts ui",
72
+ browser: "bun scripts/call.ts",
73
+ fixture: "bun tests/helpers/fixture.ts",
74
+ test: "bun test ./tests/unit ./tests/integration && bun run test:regression",
75
+ "test:ui": "bun test tests/integration/catalog-limits.test.ts tests/unit/profile-windows.test.ts tests/unit/ui-server.test.ts tests/integration/ui-model.test.ts tests/integration/profile-configuration.test.ts tests/integration/profile-rename.test.ts tests/unit/ui-api.test.ts tests/unit/ui-display.test.ts",
76
+ "test:ui:browser": "bun tests/browser/ui-browser.ts",
77
+ "test:regression": "node --experimental-transform-types --test tests/regression/*.test.ts",
78
+ "test:integration": "bun tests/browser/integration.ts",
79
+ "test:native": "bun tests/browser/native-integration.ts",
80
+ "test:window-close": "bun tests/browser/window-close.ts",
81
+ "test:windows": "bun --no-env-file tests/browser/profile-windows-browser.ts",
82
+ "test:proxy": "bun tests/browser/proxy-integration.ts",
83
+ "test:manual": "bun tests/qa/manual-integration.ts",
84
+ "test:typing": "bun --no-env-file tests/browser/typing-browser.ts",
85
+ "test:public": "bun --no-env-file tests/qa/public-sites.ts",
86
+ "test:package": "bun tests/qa/package.ts",
87
+ typecheck: "tsc --noEmit",
88
+ "verify:release": "bun scripts/verify-release.ts",
89
+ "test:concurrency": "bun --no-env-file tests/browser/concurrency.ts",
90
+ check: "bun run build && bun run typecheck && bun run test",
91
+ "test:transfer": "bun --no-env-file tests/browser/profile-transfer.ts"
53
92
  },
54
93
  dependencies: {
55
94
  "@askjo/camofox-browser": "1.18.1",
@@ -58,12 +97,32 @@ var package_default = {
58
97
  hono: "4.13.16",
59
98
  "playwright-core": "1.62.1",
60
99
  "better-sqlite3": "13.0.1",
61
- impit: "0.14.8"
100
+ impit: "0.14.8",
101
+ "stream-json": "3.8.0",
102
+ yauzl: "3.4.0",
103
+ yazl: "3.3.1"
62
104
  },
63
105
  bundleDependencies: [
64
106
  "@askjo/camofox-browser",
65
107
  "camoufox-js"
66
- ]
108
+ ],
109
+ devDependencies: {
110
+ "@fontsource/ibm-plex-mono": "5.3.0",
111
+ "@fontsource/ibm-plex-sans": "5.3.0",
112
+ "@types/bun": "1.4.4",
113
+ "@types/react": "19.3.0",
114
+ "@types/react-dom": "19.3.0",
115
+ react: "19.3.1",
116
+ "react-dom": "19.3.1",
117
+ typescript: "5.9.3",
118
+ vite: "8.3.6",
119
+ "@types/yauzl": "3.4.0",
120
+ "@types/yazl": "3.3.1"
121
+ },
122
+ patchedDependencies: {
123
+ "@askjo/camofox-browser@1.18.1": "patches/@askjo%2Fcamofox-browser@1.18.1.patch",
124
+ "camoufox-js@0.11.5": "patches/camoufox-js@0.11.5.patch"
125
+ }
67
126
  };
68
127
 
69
128
  // src/versions.ts
@@ -365,6 +424,23 @@ function replayFingerprint(saved, generated) {
365
424
  }
366
425
  return effective;
367
426
  }
427
+ function applyProxyLocale(config, proxied) {
428
+ const region = config["locale:region"];
429
+ if (!proxied || typeof region !== "string" || !/^[A-Z]{2}$/.test(region) || config["locale:all"])
430
+ return config;
431
+ const current = new Intl.Locale(`und-${region}`), maximized = current.maximize();
432
+ if (maximized.region !== current.region)
433
+ return config;
434
+ const locale = maximized.minimize();
435
+ if (locale.language === "und")
436
+ return config;
437
+ const effective = { ...config, "locale:language": locale.language };
438
+ if (locale.script)
439
+ effective["locale:script"] = locale.script;
440
+ else
441
+ delete effective["locale:script"];
442
+ return effective;
443
+ }
368
444
  function launchPreferences(current, proxied, device = {}) {
369
445
  const capabilities = "webGl:parameters" in device ? { "webgl.enable-webgl2": "webGl2:parameters" in device } : {};
370
446
  return { ...current, ...capabilities, ...proxied ? {
@@ -397,7 +473,7 @@ function applyFingerprint(options, stateDir, binary, proxied) {
397
473
  pin = { version: 2, engineSha256, config: freezeDevice(generated) };
398
474
  writeFileSync(path, JSON.stringify(pin, null, 2), { mode: 384, flag: "wx" });
399
475
  }
400
- const effective = replayFingerprint(pin.config, generated);
476
+ const effective = applyProxyLocale(replayFingerprint(pin.config, generated), proxied);
401
477
  for (const [key] of chunks)
402
478
  delete env[key];
403
479
  const serialized = JSON.stringify(effective);
@@ -882,16 +958,15 @@ async function register(app, ctx, options = {}) {
882
958
  if (!id || !stateDir)
883
959
  throw Error("local-profile requires STELZ_PROFILE_ID and STELZ_PROFILE_DIR");
884
960
  const file = join6(stateDir, "status.json");
885
- const mode = process.env.STELZ_MODE === "proxy" ? "proxy" : "direct";
961
+ const proxied = !!process.env.PROXY_HOST;
886
962
  const interval = parseIpCheckInterval(options.ipCheckIntervalMs === undefined ? process.env.STELZ_IP_CHECK_INTERVAL_MS : String(options.ipCheckIntervalMs));
887
963
  const now = options.now ?? (() => performance.now());
888
964
  let lastVerifiedAt;
889
965
  const expected = process.env.STELZ_EXPECTED_IP || "";
890
- const requiresCheck = mode === "proxy" || !!expected;
966
+ const requiresCheck = proxied || !!expected;
891
967
  let state = {
892
968
  profileId: id,
893
969
  instanceId: randomUUID2(),
894
- mode,
895
970
  phase: "starting",
896
971
  exitIp: null,
897
972
  verificationIntervalMs: interval,
@@ -1001,7 +1076,7 @@ async function register(app, ctx, options = {}) {
1001
1076
  exitIp: ip,
1002
1077
  expectedIp: expected || null,
1003
1078
  checkedAt: new Date().toISOString(),
1004
- fixedIpVerified: mode === "proxy" && !!expected,
1079
+ fixedIpVerified: proxied && !!expected,
1005
1080
  verificationStatus: "verified",
1006
1081
  error: null,
1007
1082
  checkFailure: null
@@ -1017,7 +1092,7 @@ async function register(app, ctx, options = {}) {
1017
1092
  verificationStatus: "verification_failed",
1018
1093
  exitIp: null,
1019
1094
  fixedIpVerified: false,
1020
- error: mode === "proxy" ? "Proxy exit IP verification failed. Correct the network and run stelz recheck. Direct fallback is disabled." : "Expected exit IP verification failed. Correct the network or expected IP and run stelz recheck.",
1095
+ error: proxied ? "Proxy exit IP verification failed. Correct the network and run stelz recheck. Direct fallback is disabled." : "Expected exit IP verification failed. Correct the network or expected IP and run stelz recheck.",
1021
1096
  checkFailure: error.message.slice(0, 500)
1022
1097
  });
1023
1098
  }
@@ -1041,7 +1116,7 @@ async function register(app, ctx, options = {}) {
1041
1116
  throw Error(error);
1042
1117
  }
1043
1118
  launchAdmitted = true;
1044
- const hashes = applyFingerprint(launchOptions, stateDir, process.env.CAMOUFOX_EXECUTABLE, mode === "proxy");
1119
+ const hashes = applyFingerprint(launchOptions, stateDir, process.env.CAMOUFOX_EXECUTABLE, proxied);
1045
1120
  addProfileExtension(launchOptions, windows.extensionPath);
1046
1121
  persist({ phase: "starting", ...hashes });
1047
1122
  });
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  // src/runtime/supervisor.ts
3
3
  import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
4
- import { join as join7 } from "path";
4
+ import { join as join9 } from "path";
5
5
 
6
6
  // src/profiles/config.ts
7
7
  import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync } from "fs";
@@ -18,12 +18,21 @@ import { createHash } from "crypto";
18
18
  // package.json
19
19
  var package_default = {
20
20
  name: "stelz",
21
- version: "0.2.0",
21
+ version: "0.2.1",
22
22
  description: "Standalone persistent Camoufox profiles with exclusive MCP control",
23
23
  license: "MIT",
24
24
  homepage: "https://github.com/souljorje/stelz#readme",
25
- bugs: { url: "https://github.com/souljorje/stelz/issues" },
26
- keywords: ["browser", "automation", "camoufox", "playwright", "mcp", "persistent-profiles"],
25
+ bugs: {
26
+ url: "https://github.com/souljorje/stelz/issues"
27
+ },
28
+ keywords: [
29
+ "browser",
30
+ "automation",
31
+ "camoufox",
32
+ "playwright",
33
+ "mcp",
34
+ "persistent-profiles"
35
+ ],
27
36
  bin: {
28
37
  stelz: "dist/cli.js"
29
38
  },
@@ -45,15 +54,45 @@ var package_default = {
45
54
  },
46
55
  repository: {
47
56
  type: "git",
48
- url: "https://github.com/souljorje/stelz.git",
49
- directory: "packages/stelz"
57
+ url: "https://github.com/souljorje/stelz.git"
58
+ },
59
+ publishConfig: {
60
+ access: "public",
61
+ registry: "https://registry.npmjs.org"
50
62
  },
51
- publishConfig: { access: "public", registry: "https://registry.npmjs.org" },
52
63
  type: "module",
53
64
  packageManager: "bun@1.4.4",
54
65
  scripts: {
55
- build: "bun ../../scripts/build.ts",
56
- prepack: "bun run build"
66
+ build: "bun scripts/build.ts",
67
+ pack: "bun scripts/pack.ts",
68
+ setup: "bun src/cli/cli.ts setup",
69
+ start: "bun src/cli/cli.ts start",
70
+ status: "bun src/cli/cli.ts status",
71
+ stop: "bun src/cli/cli.ts stop",
72
+ pause: "bun src/cli/cli.ts pause",
73
+ resume: "bun src/cli/cli.ts resume",
74
+ mcp: "bun src/cli/cli.ts mcp",
75
+ ui: "bun --no-env-file src/cli/cli.ts ui",
76
+ browser: "bun scripts/call.ts",
77
+ fixture: "bun tests/helpers/fixture.ts",
78
+ test: "bun test ./tests/unit ./tests/integration && bun run test:regression",
79
+ "test:ui": "bun test tests/integration/catalog-limits.test.ts tests/unit/profile-windows.test.ts tests/unit/ui-server.test.ts tests/integration/ui-model.test.ts tests/integration/profile-configuration.test.ts tests/integration/profile-rename.test.ts tests/unit/ui-api.test.ts tests/unit/ui-display.test.ts",
80
+ "test:ui:browser": "bun tests/browser/ui-browser.ts",
81
+ "test:regression": "node --experimental-transform-types --test tests/regression/*.test.ts",
82
+ "test:integration": "bun tests/browser/integration.ts",
83
+ "test:native": "bun tests/browser/native-integration.ts",
84
+ "test:window-close": "bun tests/browser/window-close.ts",
85
+ "test:windows": "bun --no-env-file tests/browser/profile-windows-browser.ts",
86
+ "test:proxy": "bun tests/browser/proxy-integration.ts",
87
+ "test:manual": "bun tests/qa/manual-integration.ts",
88
+ "test:typing": "bun --no-env-file tests/browser/typing-browser.ts",
89
+ "test:public": "bun --no-env-file tests/qa/public-sites.ts",
90
+ "test:package": "bun tests/qa/package.ts",
91
+ typecheck: "tsc --noEmit",
92
+ "verify:release": "bun scripts/verify-release.ts",
93
+ "test:concurrency": "bun --no-env-file tests/browser/concurrency.ts",
94
+ check: "bun run build && bun run typecheck && bun run test",
95
+ "test:transfer": "bun --no-env-file tests/browser/profile-transfer.ts"
57
96
  },
58
97
  dependencies: {
59
98
  "@askjo/camofox-browser": "1.18.1",
@@ -62,12 +101,32 @@ var package_default = {
62
101
  hono: "4.13.16",
63
102
  "playwright-core": "1.62.1",
64
103
  "better-sqlite3": "13.0.1",
65
- impit: "0.14.8"
104
+ impit: "0.14.8",
105
+ "stream-json": "3.8.0",
106
+ yauzl: "3.4.0",
107
+ yazl: "3.3.1"
66
108
  },
67
109
  bundleDependencies: [
68
110
  "@askjo/camofox-browser",
69
111
  "camoufox-js"
70
- ]
112
+ ],
113
+ devDependencies: {
114
+ "@fontsource/ibm-plex-mono": "5.3.0",
115
+ "@fontsource/ibm-plex-sans": "5.3.0",
116
+ "@types/bun": "1.4.4",
117
+ "@types/react": "19.3.0",
118
+ "@types/react-dom": "19.3.0",
119
+ react: "19.3.1",
120
+ "react-dom": "19.3.1",
121
+ typescript: "5.9.3",
122
+ vite: "8.3.6",
123
+ "@types/yauzl": "3.4.0",
124
+ "@types/yazl": "3.3.1"
125
+ },
126
+ patchedDependencies: {
127
+ "@askjo/camofox-browser@1.18.1": "patches/@askjo%2Fcamofox-browser@1.18.1.patch",
128
+ "camoufox-js@0.11.5": "patches/camoufox-js@0.11.5.patch"
129
+ }
71
130
  };
72
131
 
73
132
  // src/versions.ts
@@ -129,11 +188,33 @@ function normalizeProfileId(value) {
129
188
  return { ...record, profileId: identityId };
130
189
  }
131
190
 
191
+ // src/profiles/network-compat.ts
192
+ function legacyConnectionMode(profile) {
193
+ if (!Object.hasOwn(profile, "mode"))
194
+ return;
195
+ const { mode } = profile;
196
+ if (mode !== "direct" && mode !== "proxy")
197
+ throw Error("Legacy connection mode must be direct or proxy.");
198
+ return mode;
199
+ }
200
+ function hasProxy(profile) {
201
+ const legacy = legacyConnectionMode(profile);
202
+ return legacy === "proxy" || legacy !== "direct" && profile.proxyEnv !== undefined;
203
+ }
204
+
132
205
  // src/profiles/config.ts
133
206
  var HOME_DIR = resolve(process.env.STELZ_HOME || join2(homedir(), ".local/share/stelz"));
134
207
  var CONFIG_FILE = existingProfilePath(HOME_DIR, "profiles.json", "identities.json");
135
208
  var STATE = existingProfilePath(HOME_DIR, "profiles", "identities");
136
209
  var UPSTREAM = join2(HOME_DIR, "runtime", `camofox-${UPSTREAM_VERSION}`);
210
+ function parseProfileName(value) {
211
+ if (typeof value !== "string" || /[\p{Cc}\p{Bidi_Control}\u2028\u2029\u206a-\u206f]/u.test(value))
212
+ throw Error("Use a display name with 1\u201380 characters and no control or directional format characters.");
213
+ const name = value.trim();
214
+ if (!name || name.length > 80)
215
+ throw Error("Use a display name with 1\u201380 characters and no control or directional format characters.");
216
+ return name;
217
+ }
137
218
  function settings() {
138
219
  const value = json(CONFIG_FILE);
139
220
  if (!value)
@@ -151,10 +232,13 @@ function validateSettings(input) {
151
232
  throw Error("Configure a profiles array.");
152
233
  const ids = new Set, ports = new Set;
153
234
  for (const p of value.profiles) {
154
- if (!p || typeof p !== "object" || typeof p.id !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(p.id) || !Number.isInteger(p.port) || p.port < 1024 || p.port > 65535 || !["direct", "proxy"].includes(p.mode) || ids.has(p.id) || ports.has(p.port))
235
+ if (!p || typeof p !== "object" || typeof p.id !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(p.id) || !Number.isInteger(p.port) || p.port < 1024 || p.port > 65535 || ids.has(p.id) || ports.has(p.port))
155
236
  throw Error("Profile IDs and ports must be valid and unique.");
237
+ legacyConnectionMode(p);
156
238
  if (p.storage !== undefined && !["native", "checkpoint"].includes(p.storage))
157
239
  throw Error("Storage must be native or checkpoint.");
240
+ if (p.name !== undefined && parseProfileName(p.name) !== p.name)
241
+ throw Error("Profile display names must have no leading or trailing whitespace.");
158
242
  if (p.typingMode !== undefined && !isTypingMode(p.typingMode))
159
243
  throw Error("Typing mode must be auto, keyboard, or instant.");
160
244
  for (const name of [p.proxyEnv, p.expectedIPEnv]) {
@@ -207,7 +291,7 @@ function alive(pid) {
207
291
  }
208
292
  }
209
293
  function proxyEnvironment(p) {
210
- if (p.mode === "direct")
294
+ if (!hasProxy(p))
211
295
  return {};
212
296
  const raw = p.proxyEnv && secret(p.proxyEnv);
213
297
  if (!raw)
@@ -235,7 +319,7 @@ function validateProxy(raw, id = "Profile") {
235
319
  return u;
236
320
  }
237
321
  function configurationHash(p) {
238
- const { typingMode: _typingMode, ...launchProfile } = p;
322
+ const { name: _name, typingMode: _typingMode, ...launchProfile } = p;
239
323
  const interval = ipCheckIntervalMs(), typing = typingMode();
240
324
  return createHash("sha256").update(JSON.stringify({
241
325
  ...launchProfile,
@@ -253,13 +337,13 @@ import {
253
337
  fstatSync as fstatSync2,
254
338
  openSync as openSync2,
255
339
  readdirSync,
256
- readFileSync as readFileSync3,
340
+ readFileSync as readFileSync4,
257
341
  readSync as readSync2,
258
342
  renameSync as renameSync2,
259
343
  statSync,
260
344
  writeFileSync as writeFileSync2
261
345
  } from "fs";
262
- import { join as join6 } from "path";
346
+ import { join as join8 } from "path";
263
347
 
264
348
  // src/runtime/profile-process.ts
265
349
  import { closeSync, constants, fstatSync, ftruncateSync, lstatSync as lstatSync2, openSync, readFileSync as readFileSync2, readSync, writeSync } from "fs";
@@ -363,7 +447,7 @@ function processStart(pid) {
363
447
  return processInfo(pid)?.start;
364
448
  }
365
449
  // src/profiles/profile-configuration.ts
366
- import { join as join5 } from "path";
450
+ import { join as join6 } from "path";
367
451
 
368
452
  // src/profiles/profile-catalog.ts
369
453
  import { join as join3 } from "path";
@@ -377,11 +461,30 @@ import { join as join4 } from "path";
377
461
  var NETWORK_JOURNAL = join4(HOME_DIR, ".network-transaction.json");
378
462
  var secretsFile2 = join4(HOME_DIR, "secrets.env");
379
463
  var LIMIT2 = 1024 * 1024;
380
- // src/profiles/profile-configuration.ts
464
+
465
+ // src/profiles/profile-import-transaction.ts
466
+ import { dirname, join as join5 } from "path";
467
+ var PROFILE_IMPORT_JOURNAL = join5(HOME_DIR, ".profile-import-transaction.json");
381
468
  var secretsFile3 = join5(HOME_DIR, "secrets.env");
469
+ var LIMIT3 = 1024 * 1024;
470
+ // src/profiles/profile-configuration.ts
471
+ var secretsFile4 = join6(HOME_DIR, "secrets.env");
472
+
473
+ // src/runtime/runtime-plugin.ts
474
+ import { lstatSync as lstatSync3, readFileSync as readFileSync3 } from "fs";
475
+ import { join as join7 } from "path";
476
+ function assertRuntimePlugin(root = ROOT, upstream = UPSTREAM) {
477
+ try {
478
+ const current = join7(root, "dist/plugin.js"), installed = join7(upstream, "plugins/local-profile/index.js");
479
+ if (![current, installed].every((file) => lstatSync3(file).isFile()) || !readFileSync3(current).equals(readFileSync3(installed)))
480
+ throw Error();
481
+ } catch {
482
+ throw Error("The installed browser plugin does not match this package. Stop all profiles and run stelz setup before starting a new browser.");
483
+ }
484
+ }
382
485
 
383
486
  // src/runtime/runtime.ts
384
- var key = (id) => readFileSync3(join6(dir(id), "access-key"), "utf8").trim();
487
+ var key = (id) => readFileSync4(join8(dir(id), "access-key"), "utf8").trim();
385
488
  var LOG_LIMIT = 1024 * 1024;
386
489
  function rotateLog(file, incoming = 0) {
387
490
  try {
@@ -402,7 +505,7 @@ var instanceId = process.argv[3];
402
505
  if (!instanceId)
403
506
  throw Error("A supervisor requires a reserved service instance.");
404
507
  for (const name of ["storage", "cookies", "uploads", "traces", "tmp"])
405
- mkdirSync2(join7(runtimeDir, name), { recursive: true, mode: 448 });
508
+ mkdirSync2(join9(runtimeDir, name), { recursive: true, mode: 448 });
406
509
  var proxy = proxyEnvironment(p);
407
510
  var secrets = [proxy.PROXY_USERNAME, proxy.PROXY_PASSWORD].filter(Boolean).flatMap((s) => [s, decodeURIComponent(s)]);
408
511
  function redact(s) {
@@ -413,7 +516,7 @@ function redact(s) {
413
516
  return value;
414
517
  }
415
518
  function log(line) {
416
- const file = join7(runtimeDir, "server.log"), value = `${redact(line)}
519
+ const file = join9(runtimeDir, "server.log"), value = `${redact(line)}
417
520
  `;
418
521
  rotateLog(file, Buffer.byteLength(value));
419
522
  appendFileSync(file, value, { mode: 384 });
@@ -431,10 +534,10 @@ var env = {
431
534
  CAMOFOX_INTERACTIVE: "desktop",
432
535
  CAMOFOX_ACCESS_KEY: key(p.id),
433
536
  CAMOFOX_API_KEY: key(p.id),
434
- CAMOFOX_PROFILE_DIR: join7(runtimeDir, "storage"),
435
- CAMOFOX_COOKIES_DIR: join7(runtimeDir, "cookies"),
436
- CAMOFOX_UPLOADS_DIR: join7(runtimeDir, "uploads"),
437
- CAMOFOX_TRACES_DIR: join7(runtimeDir, "traces"),
537
+ CAMOFOX_PROFILE_DIR: join9(runtimeDir, "storage"),
538
+ CAMOFOX_COOKIES_DIR: join9(runtimeDir, "cookies"),
539
+ CAMOFOX_UPLOADS_DIR: join9(runtimeDir, "uploads"),
540
+ CAMOFOX_TRACES_DIR: join9(runtimeDir, "traces"),
438
541
  CAMOFOX_DISABLE_DEFAULT_ADDONS: "1",
439
542
  CAMOFOX_CRASH_REPORT_ENABLED: "false",
440
543
  SESSION_TIMEOUT_MS: "0",
@@ -446,7 +549,6 @@ var env = {
446
549
  STELZ_IP_CHECK_INTERVAL_MS: String(ipCheckIntervalMs()),
447
550
  STELZ_PROFILE_ID: p.id,
448
551
  STELZ_PROFILE_DIR: runtimeDir,
449
- STELZ_MODE: p.mode,
450
552
  STELZ_EXPECTED_IP: p.expectedIPEnv ? secret(p.expectedIPEnv) || "" : "",
451
553
  STELZ_HOME: HOME_DIR,
452
554
  STELZ_UPSTREAM: UPSTREAM
@@ -458,15 +560,16 @@ process.stdin.once('data', () => {
458
560
  });
459
561
  process.stdin.once('end', () => { if (!started) process.exit(1); });
460
562
  process.stdin.resume();`;
461
- var server = await lock(join7(STATE, "admission.lock"), async () => {
462
- const file = join7(runtimeDir, "running.json"), record = json(file), supervisorStart = processStart(process.pid);
563
+ var server = await lock(join9(STATE, "admission.lock"), async () => {
564
+ const file = join9(runtimeDir, "running.json"), record = json(file), supervisorStart = processStart(process.pid);
463
565
  if (!record || record.instanceId !== instanceId || record.serverState !== "not-started" || !supervisorStart || record.pid && (record.pid !== process.pid || record.supervisorStart !== supervisorStart))
464
566
  throw Error("The service reservation is no longer valid.");
465
567
  if (record.configurationHash !== configurationHash(p))
466
568
  throw Error("Profile configuration changed during startup.");
569
+ assertRuntimePlugin();
467
570
  const reserved = { ...record, pid: process.pid, supervisorStart };
468
571
  save(file, reserved);
469
- const child = Bun.spawn(["node", "-e", bootstrap, join7(UPSTREAM, "server.js")], {
572
+ const child = Bun.spawn(["node", "-e", bootstrap, join9(UPSTREAM, "server.js")], {
470
573
  cwd: UPSTREAM,
471
574
  env,
472
575
  stdin: "pipe",
@@ -524,7 +627,7 @@ function stop() {
524
627
  if (stopping)
525
628
  return;
526
629
  stopping = true;
527
- const record = json(join7(runtimeDir, "running.json"));
630
+ const record = json(join9(runtimeDir, "running.json"));
528
631
  if (record?.instanceId === instanceId && record.serverPid === server.pid && record.serverStart && processStart(server.pid) === record.serverStart)
529
632
  server.kill("SIGTERM");
530
633
  else
@@ -549,7 +652,7 @@ var initialize = async () => {
549
652
  });
550
653
  if (!r.ok)
551
654
  throw Error("Original browser context could not be initialized");
552
- save(join7(runtimeDir, "initialized.json"), { instanceId });
655
+ save(join9(runtimeDir, "initialized.json"), { instanceId });
553
656
  return;
554
657
  }
555
658
  } catch (e) {
@@ -564,14 +667,14 @@ var initialize = async () => {
564
667
  initialize();
565
668
  var exitCode = await server.exited;
566
669
  await Promise.race([logs, Bun.sleep(1000)]);
567
- var old = normalizeProfileId(json(join7(runtimeDir, "status.json")) || {});
670
+ var old = normalizeProfileId(json(join9(runtimeDir, "status.json")) || {});
568
671
  var clean = exitCode === 0 && old?.phase === "stopped";
569
- save(join7(runtimeDir, "status.json"), {
672
+ save(join9(runtimeDir, "status.json"), {
570
673
  ...old,
571
674
  profileId: p.id,
572
675
  phase: clean ? "stopped" : "unclean",
573
676
  exitCode
574
677
  });
575
678
  if (clean)
576
- save(join7(runtimeDir, "stopped.json"), { instanceId });
679
+ save(join9(runtimeDir, "stopped.json"), { instanceId });
577
680
  process.exit(exitCode);