browsentic 0.5.0 → 0.6.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.
package/dist/cli.js CHANGED
@@ -2131,7 +2131,7 @@ var require_extension = __commonJS({
2131
2131
  let inQuotes = false;
2132
2132
  let extensionName;
2133
2133
  let paramName;
2134
- let start = -1;
2134
+ let start2 = -1;
2135
2135
  let code = -1;
2136
2136
  let end = -1;
2137
2137
  let i = 0;
@@ -2139,45 +2139,45 @@ var require_extension = __commonJS({
2139
2139
  code = header.charCodeAt(i);
2140
2140
  if (extensionName === void 0) {
2141
2141
  if (end === -1 && tokenChars[code] === 1) {
2142
- if (start === -1) start = i;
2142
+ if (start2 === -1) start2 = i;
2143
2143
  } else if (i !== 0 && (code === 32 || code === 9)) {
2144
- if (end === -1 && start !== -1) end = i;
2144
+ if (end === -1 && start2 !== -1) end = i;
2145
2145
  } else if (code === 59 || code === 44) {
2146
- if (start === -1) {
2146
+ if (start2 === -1) {
2147
2147
  throw new SyntaxError(`Unexpected character at index ${i}`);
2148
2148
  }
2149
2149
  if (end === -1) end = i;
2150
- const name = header.slice(start, end);
2150
+ const name = header.slice(start2, end);
2151
2151
  if (code === 44) {
2152
2152
  push(offers, name, params);
2153
2153
  params = /* @__PURE__ */ Object.create(null);
2154
2154
  } else {
2155
2155
  extensionName = name;
2156
2156
  }
2157
- start = end = -1;
2157
+ start2 = end = -1;
2158
2158
  } else {
2159
2159
  throw new SyntaxError(`Unexpected character at index ${i}`);
2160
2160
  }
2161
2161
  } else if (paramName === void 0) {
2162
2162
  if (end === -1 && tokenChars[code] === 1) {
2163
- if (start === -1) start = i;
2163
+ if (start2 === -1) start2 = i;
2164
2164
  } else if (code === 32 || code === 9) {
2165
- if (end === -1 && start !== -1) end = i;
2165
+ if (end === -1 && start2 !== -1) end = i;
2166
2166
  } else if (code === 59 || code === 44) {
2167
- if (start === -1) {
2167
+ if (start2 === -1) {
2168
2168
  throw new SyntaxError(`Unexpected character at index ${i}`);
2169
2169
  }
2170
2170
  if (end === -1) end = i;
2171
- push(params, header.slice(start, end), true);
2171
+ push(params, header.slice(start2, end), true);
2172
2172
  if (code === 44) {
2173
2173
  push(offers, extensionName, params);
2174
2174
  params = /* @__PURE__ */ Object.create(null);
2175
2175
  extensionName = void 0;
2176
2176
  }
2177
- start = end = -1;
2178
- } else if (code === 61 && start !== -1 && end === -1) {
2179
- paramName = header.slice(start, i);
2180
- start = end = -1;
2177
+ start2 = end = -1;
2178
+ } else if (code === 61 && start2 !== -1 && end === -1) {
2179
+ paramName = header.slice(start2, i);
2180
+ start2 = end = -1;
2181
2181
  } else {
2182
2182
  throw new SyntaxError(`Unexpected character at index ${i}`);
2183
2183
  }
@@ -2186,13 +2186,13 @@ var require_extension = __commonJS({
2186
2186
  if (tokenChars[code] !== 1) {
2187
2187
  throw new SyntaxError(`Unexpected character at index ${i}`);
2188
2188
  }
2189
- if (start === -1) start = i;
2189
+ if (start2 === -1) start2 = i;
2190
2190
  else if (!mustUnescape) mustUnescape = true;
2191
2191
  isEscaping = false;
2192
2192
  } else if (inQuotes) {
2193
2193
  if (tokenChars[code] === 1) {
2194
- if (start === -1) start = i;
2195
- } else if (code === 34 && start !== -1) {
2194
+ if (start2 === -1) start2 = i;
2195
+ } else if (code === 34 && start2 !== -1) {
2196
2196
  inQuotes = false;
2197
2197
  end = i;
2198
2198
  } else if (code === 92) {
@@ -2203,15 +2203,15 @@ var require_extension = __commonJS({
2203
2203
  } else if (code === 34 && header.charCodeAt(i - 1) === 61) {
2204
2204
  inQuotes = true;
2205
2205
  } else if (end === -1 && tokenChars[code] === 1) {
2206
- if (start === -1) start = i;
2207
- } else if (start !== -1 && (code === 32 || code === 9)) {
2206
+ if (start2 === -1) start2 = i;
2207
+ } else if (start2 !== -1 && (code === 32 || code === 9)) {
2208
2208
  if (end === -1) end = i;
2209
2209
  } else if (code === 59 || code === 44) {
2210
- if (start === -1) {
2210
+ if (start2 === -1) {
2211
2211
  throw new SyntaxError(`Unexpected character at index ${i}`);
2212
2212
  }
2213
2213
  if (end === -1) end = i;
2214
- let value = header.slice(start, end);
2214
+ let value = header.slice(start2, end);
2215
2215
  if (mustUnescape) {
2216
2216
  value = value.replace(/\\/g, "");
2217
2217
  mustUnescape = false;
@@ -2223,17 +2223,17 @@ var require_extension = __commonJS({
2223
2223
  extensionName = void 0;
2224
2224
  }
2225
2225
  paramName = void 0;
2226
- start = end = -1;
2226
+ start2 = end = -1;
2227
2227
  } else {
2228
2228
  throw new SyntaxError(`Unexpected character at index ${i}`);
2229
2229
  }
2230
2230
  }
2231
2231
  }
2232
- if (start === -1 || inQuotes || code === 32 || code === 9) {
2232
+ if (start2 === -1 || inQuotes || code === 32 || code === 9) {
2233
2233
  throw new SyntaxError("Unexpected end of input");
2234
2234
  }
2235
2235
  if (end === -1) end = i;
2236
- const token = header.slice(start, end);
2236
+ const token = header.slice(start2, end);
2237
2237
  if (extensionName === void 0) {
2238
2238
  push(offers, token, params);
2239
2239
  } else {
@@ -3268,34 +3268,34 @@ var require_subprotocol = __commonJS({
3268
3268
  var { tokenChars } = require_validation();
3269
3269
  function parse3(header) {
3270
3270
  const protocols = /* @__PURE__ */ new Set();
3271
- let start = -1;
3271
+ let start2 = -1;
3272
3272
  let end = -1;
3273
3273
  let i = 0;
3274
3274
  for (i; i < header.length; i++) {
3275
3275
  const code = header.charCodeAt(i);
3276
3276
  if (end === -1 && tokenChars[code] === 1) {
3277
- if (start === -1) start = i;
3277
+ if (start2 === -1) start2 = i;
3278
3278
  } else if (i !== 0 && (code === 32 || code === 9)) {
3279
- if (end === -1 && start !== -1) end = i;
3279
+ if (end === -1 && start2 !== -1) end = i;
3280
3280
  } else if (code === 44) {
3281
- if (start === -1) {
3281
+ if (start2 === -1) {
3282
3282
  throw new SyntaxError(`Unexpected character at index ${i}`);
3283
3283
  }
3284
3284
  if (end === -1) end = i;
3285
- const protocol2 = header.slice(start, end);
3285
+ const protocol2 = header.slice(start2, end);
3286
3286
  if (protocols.has(protocol2)) {
3287
3287
  throw new SyntaxError(`The "${protocol2}" subprotocol is duplicated`);
3288
3288
  }
3289
3289
  protocols.add(protocol2);
3290
- start = end = -1;
3290
+ start2 = end = -1;
3291
3291
  } else {
3292
3292
  throw new SyntaxError(`Unexpected character at index ${i}`);
3293
3293
  }
3294
3294
  }
3295
- if (start === -1 || end !== -1) {
3295
+ if (start2 === -1 || end !== -1) {
3296
3296
  throw new SyntaxError("Unexpected end of input");
3297
3297
  }
3298
- const protocol = header.slice(start, i);
3298
+ const protocol = header.slice(start2, i);
3299
3299
  if (protocols.has(protocol)) {
3300
3300
  throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
3301
3301
  }
@@ -11285,9 +11285,9 @@ function nullish(input2) {
11285
11285
  return input2 === null || input2 === void 0;
11286
11286
  }
11287
11287
  function cleanRegex(source) {
11288
- const start = source.startsWith("^") ? 1 : 0;
11288
+ const start2 = source.startsWith("^") ? 1 : 0;
11289
11289
  const end = source.endsWith("$") ? source.length - 1 : source.length;
11290
- return source.slice(start, end);
11290
+ return source.slice(start2, end);
11291
11291
  }
11292
11292
  function floatSafeRemainder(val, step) {
11293
11293
  const ratio = val / step;
@@ -14773,9 +14773,9 @@ var $ZodTemplateLiteral = /* @__PURE__ */ $constructor("$ZodTemplateLiteral", (i
14773
14773
  const source = part._zod.pattern instanceof RegExp ? part._zod.pattern.source : part._zod.pattern;
14774
14774
  if (!source)
14775
14775
  throw new Error(`Invalid template literal part: ${part._zod.traits}`);
14776
- const start = source.startsWith("^") ? 1 : 0;
14776
+ const start2 = source.startsWith("^") ? 1 : 0;
14777
14777
  const end = source.endsWith("$") ? source.length - 1 : source.length;
14778
- regexParts.push(source.slice(start, end));
14778
+ regexParts.push(source.slice(start2, end));
14779
14779
  } else if (part === null || primitiveTypes.has(typeof part)) {
14780
14780
  regexParts.push(escapeRegex(`${part}`));
14781
14781
  } else {
@@ -27722,14 +27722,14 @@ async function travel(from, to, steps, buttons, onStep) {
27722
27722
  else firePointer(elementAt(at), "pointermove", "mousemove", at, buttons);
27723
27723
  }
27724
27724
  }
27725
- async function approachFrom(start, grip, over) {
27726
- await travel(start, grip, APPROACH_STEPS, 0);
27725
+ async function approachFrom(start2, grip, over) {
27726
+ await travel(start2, grip, APPROACH_STEPS, 0);
27727
27727
  hoverSequence(over ?? document.body, grip);
27728
27728
  }
27729
27729
  async function pointerDrag(source, plan) {
27730
- const { approach: start, grip, drop, steps, holdMs, settleMs } = plan;
27730
+ const { approach: start2, grip, drop, steps, holdMs, settleMs } = plan;
27731
27731
  const held = source ?? elementAt(grip);
27732
- await approachFrom(start, grip, held);
27732
+ await approachFrom(start2, grip, held);
27733
27733
  firePointer(held, "pointerdown", "mousedown", grip, 1);
27734
27734
  await settle(holdMs);
27735
27735
  await travel(grip, drop, steps, 1);
@@ -27739,9 +27739,9 @@ async function pointerDrag(source, plan) {
27739
27739
  return { landedOn: landing ? cssPath(landing) : void 0 };
27740
27740
  }
27741
27741
  async function nativeDrag(source, plan) {
27742
- const { approach: start, grip, drop, steps, holdMs, settleMs } = plan;
27742
+ const { approach: start2, grip, drop, steps, holdMs, settleMs } = plan;
27743
27743
  const dataTransfer = new DataTransfer();
27744
- await approachFrom(start, grip, source);
27744
+ await approachFrom(start2, grip, source);
27745
27745
  firePointer(source, "pointerdown", "mousedown", grip, 1);
27746
27746
  await settle(holdMs);
27747
27747
  const started = fireDrag(source, "dragstart", grip, dataTransfer);
@@ -27836,14 +27836,14 @@ function digestOf(text3) {
27836
27836
  }
27837
27837
  return (hash3 >>> 0).toString(16).padStart(8, "0");
27838
27838
  }
27839
- function groupEnd(text3, start, budget) {
27840
- if (text3.length - start <= budget) return text3.length;
27841
- const window2 = text3.slice(start, start + budget);
27839
+ function groupEnd(text3, start2, budget) {
27840
+ if (text3.length - start2 <= budget) return text3.length;
27841
+ const window2 = text3.slice(start2, start2 + budget);
27842
27842
  for (const { pattern, floor } of BOUNDARIES) {
27843
27843
  const cut = lastBoundary(window2, pattern, Math.floor(budget * floor));
27844
- if (cut > 0) return start + cut;
27844
+ if (cut > 0) return start2 + cut;
27845
27845
  }
27846
- return start + budget;
27846
+ return start2 + budget;
27847
27847
  }
27848
27848
  function writeCursor(offset, delivered) {
27849
27849
  return `${offset}.${digestOf(delivered)}`;
@@ -28967,12 +28967,12 @@ var pressKey = defineAction({
28967
28967
  el.dispatchEvent(new KeyboardEvent("keypress", init));
28968
28968
  }
28969
28969
  if (printable && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
28970
- const start = el.selectionStart ?? el.value.length;
28970
+ const start2 = el.selectionStart ?? el.value.length;
28971
28971
  const end = el.selectionEnd ?? el.value.length;
28972
- const next = el.value.slice(0, start) + key + el.value.slice(end);
28972
+ const next = el.value.slice(0, start2) + key + el.value.slice(end);
28973
28973
  const proto = el instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;
28974
28974
  Object.getOwnPropertyDescriptor(proto, "value")?.set?.call(el, next);
28975
- const caret = start + key.length;
28975
+ const caret = start2 + key.length;
28976
28976
  try {
28977
28977
  el.setSelectionRange(caret, caret);
28978
28978
  } catch {
@@ -29850,7 +29850,7 @@ var typeText = defineAction({
29850
29850
  }
29851
29851
  if (clear) clearContent(el);
29852
29852
  focusForTyping(el);
29853
- const start = performance.now();
29853
+ const start2 = performance.now();
29854
29854
  const characters = [...text3];
29855
29855
  for (const [index, char] of characters.entries()) {
29856
29856
  const wait = index ? Math.round(delays[index - 1] * (1 + (Math.random() * 2 - 1) * jitter)) : 0;
@@ -29872,7 +29872,7 @@ var typeText = defineAction({
29872
29872
  element: describeElement(el),
29873
29873
  typed: characters.length,
29874
29874
  charDelayMs: base,
29875
- elapsedMs: Math.round(performance.now() - start),
29875
+ elapsedMs: Math.round(performance.now() - start2),
29876
29876
  appended: !clear,
29877
29877
  submitted
29878
29878
  };
@@ -29906,10 +29906,10 @@ function focusForTyping(el) {
29906
29906
  }
29907
29907
  function insertCharacter(el, char) {
29908
29908
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
29909
- const start = el.selectionStart ?? el.value.length;
29909
+ const start2 = el.selectionStart ?? el.value.length;
29910
29910
  const end = el.selectionEnd ?? el.value.length;
29911
- setNativeValue(el, el.value.slice(0, start) + char + el.value.slice(end));
29912
- const caret = start + char.length;
29911
+ setNativeValue(el, el.value.slice(0, start2) + char + el.value.slice(end));
29912
+ const caret = start2 + char.length;
29913
29913
  try {
29914
29914
  el.setSelectionRange(caret, caret);
29915
29915
  } catch {
@@ -29955,7 +29955,7 @@ var waitForElement = defineAction({
29955
29955
  hidden: !el || !isVisible(el),
29956
29956
  detached: !el
29957
29957
  })[state];
29958
- const start = performance.now();
29958
+ const start2 = performance.now();
29959
29959
  let element = find();
29960
29960
  if (satisfied(element)) {
29961
29961
  return { state, elapsedMs: 0, element: element ? describeElement(element) : void 0 };
@@ -29988,7 +29988,7 @@ var waitForElement = defineAction({
29988
29988
  });
29989
29989
  return {
29990
29990
  state,
29991
- elapsedMs: Math.round(performance.now() - start),
29991
+ elapsedMs: Math.round(performance.now() - start2),
29992
29992
  element: element ? describeElement(element) : void 0
29993
29993
  };
29994
29994
  }
@@ -30079,7 +30079,7 @@ var AGENTS = {
30079
30079
  bin: "codex",
30080
30080
  install: "npm i -g @openai/codex",
30081
30081
  docs: "https://developers.openai.com/codex/cli",
30082
- models: ["gpt-5.6-terra", "gpt-5.1-codex-max", "gpt-5.1-codex", "gpt-5.1-codex-mini"]
30082
+ models: ["gpt-5.6-terra", "gpt-5.6-luna", "gpt-5.5"]
30083
30083
  },
30084
30084
  antigravity: {
30085
30085
  kind: "antigravity",
@@ -30291,6 +30291,21 @@ function settingsFor(stored, kind) {
30291
30291
  };
30292
30292
  }
30293
30293
  var text = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
30294
+ function writeAgentModel(kind, model) {
30295
+ const stored = readStored();
30296
+ const agents = { ...stored.agents ?? {} };
30297
+ const scoped = { ...agents[kind] ?? {} };
30298
+ const value = text(model);
30299
+ if (value) scoped.model = value;
30300
+ else delete scoped.model;
30301
+ if (Object.keys(scoped).length) agents[kind] = scoped;
30302
+ else delete agents[kind];
30303
+ const next = { ...stored };
30304
+ if (Object.keys(agents).length) next.agents = agents;
30305
+ else delete next.agents;
30306
+ if (kind === "claude") delete next.model;
30307
+ write(next);
30308
+ }
30294
30309
  function write(config2) {
30295
30310
  mkdirSync3(stateDir, { recursive: true, mode: 448 });
30296
30311
  writeFileSync2(configPath, `${JSON.stringify(config2, null, 2)}
@@ -30706,12 +30721,12 @@ var ownTool = (name) => /browsentic|^mcp/i.test(name);
30706
30721
  import { randomUUID as randomUUID3 } from "crypto";
30707
30722
  import { homedir as homedir4 } from "os";
30708
30723
  import { join as join7 } from "path";
30709
- var SANDBOX = ["--sandbox", "read-only", "--ask-for-approval", "never", "--skip-git-repo-check"];
30724
+ var SANDBOX = ["-c", 'sandbox_mode="read-only"', "-c", 'approval_policy="never"', "--skip-git-repo-check"];
30710
30725
  var WEB_TOOL = "web_search";
30711
30726
  var codexRunner = {
30712
30727
  kind: "codex",
30713
30728
  versionArgs: ["--version"],
30714
- efforts: ["minimal", "low", "medium", "high"],
30729
+ efforts: ["low", "medium", "high", "xhigh"],
30715
30730
  workspace: () => stateDir,
30716
30731
  skillDirs: () => [join7(homedir4(), ".codex", "skills"), join7(homedir4(), ".codex", "prompts")],
30717
30732
  stream(context) {
@@ -30734,6 +30749,9 @@ var codexRunner = {
30734
30749
  `${server}.env=${tomlTable(mcp.env)}`,
30735
30750
  "-c",
30736
30751
  `${server}.required=true`,
30752
+ // Headless Codex refuses an MCP call it would have asked about; the daemon gates these tools itself.
30753
+ "-c",
30754
+ `${server}.default_tools_approval_mode="approve"`,
30737
30755
  "-c",
30738
30756
  `developer_instructions=${tomlString(context.systemPrompt)}`,
30739
30757
  "-c",
@@ -30786,7 +30804,7 @@ var codexRunner = {
30786
30804
  case "task_complete":
30787
30805
  return sink.done("end_turn");
30788
30806
  case "error":
30789
- return sink.fail("AGENT_FAILED", msg.error || msg.message || "Codex reported an error");
30807
+ return sink.fail("AGENT_FAILED", explain(msg.error || msg.message) || "Codex reported an error");
30790
30808
  default:
30791
30809
  return;
30792
30810
  }
@@ -30817,9 +30835,9 @@ var codexRunner = {
30817
30835
  return sink.done("end_turn");
30818
30836
  }
30819
30837
  case "turn.failed":
30820
- return sink.fail("AGENT_FAILED", frame.error?.message || "Codex could not finish the turn");
30838
+ return sink.fail("AGENT_FAILED", explain(frame.error?.message) || "Codex could not finish the turn");
30821
30839
  case "error":
30822
- return sink.fail("AGENT_FAILED", frame.message || frame.error?.message || "Codex reported an error");
30840
+ return sink.fail("AGENT_FAILED", explain(frame.message || frame.error?.message) || "Codex reported an error");
30823
30841
  default:
30824
30842
  return;
30825
30843
  }
@@ -30875,6 +30893,11 @@ var codexRunner = {
30875
30893
  return null;
30876
30894
  }
30877
30895
  };
30896
+ function explain(raw) {
30897
+ if (!raw) return raw;
30898
+ const message = parseJsonLine(raw)?.error?.message ?? raw;
30899
+ return /model .*(not supported|does not exist|not found)/i.test(message) ? `${message} Pick another model for Codex in the Browsentic popup, then try again.` : message;
30900
+ }
30878
30901
  function kindOf(item) {
30879
30902
  return item?.type ?? item?.item_type;
30880
30903
  }
@@ -31614,19 +31637,13 @@ var CONTAINMENT = {
31614
31637
  keepsEnv: ["OPENAI_", "CODEX_", "AZURE_OPENAI_"],
31615
31638
  note: "no per-run tool list; the read-only sandbox is the whole containment, so the agent can still read any file the user can",
31616
31639
  run: {
31617
- required: [],
31618
- pairs: [
31619
- ["--sandbox", "read-only"],
31620
- ["--ask-for-approval", "never"]
31621
- ],
31640
+ required: ['sandbox_mode="read-only"', 'approval_policy="never"'],
31641
+ pairs: [],
31622
31642
  files: []
31623
31643
  },
31624
31644
  task: {
31625
- required: ["mcp_servers={}"],
31626
- pairs: [
31627
- ["--sandbox", "read-only"],
31628
- ["--ask-for-approval", "never"]
31629
- ],
31645
+ required: ['sandbox_mode="read-only"', 'approval_policy="never"', "mcp_servers={}"],
31646
+ pairs: [],
31630
31647
  files: []
31631
31648
  }
31632
31649
  },
@@ -31898,9 +31915,11 @@ import { homedir as homedir7 } from "os";
31898
31915
  import { dirname as dirname6, join as join15, resolve, sep as sep2 } from "path";
31899
31916
  import { fileURLToPath as fileURLToPath5 } from "url";
31900
31917
  var packageRoot = resolve(dirname6(fileURLToPath5(import.meta.url)), "..");
31918
+ var APP_MARKER = ".browsentic-app.json";
31901
31919
  var inNpxCache = (path) => path.split(sep2).includes("_npx");
31902
31920
  function installKind() {
31903
31921
  if (inNpxCache(packageRoot)) return "npx";
31922
+ if (existsSync5(join15(packageRoot, APP_MARKER))) return "app";
31904
31923
  if (existsSync5(join15(packageRoot, "tsup.config.ts"))) return "repo";
31905
31924
  return "global";
31906
31925
  }
@@ -32109,6 +32128,13 @@ async function upgradeCli(current, forward) {
32109
32128
  console.error(`
32110
32129
  ! browsentic ${latest} is published; this is ${current}, from a source checkout.`);
32111
32130
  console.error(` Update it with "git pull && yarn setup".
32131
+ `);
32132
+ return null;
32133
+ }
32134
+ if (kind === "app") {
32135
+ console.error(`
32136
+ ! browsentic ${latest} is published; this is ${current}, installed by Browsentic.app.`);
32137
+ console.error(` Update it from the app, which replaces the command and the extension together.
32112
32138
  `);
32113
32139
  return null;
32114
32140
  }
@@ -34246,8 +34272,8 @@ function purgeNpxCache(entries) {
34246
34272
  // package.json
34247
34273
  var package_default = {
34248
34274
  name: "browsentic",
34249
- version: "0.5.0",
34250
- description: "Hand your real, logged-in browser to the AI agent you already run. Installs the browser extension, runs the local daemon, and speaks MCP.",
34275
+ version: "0.6.2",
34276
+ description: "A browser extension with an AI side panel that hands your real, logged-in browser to the AI agent you already run. Installs the extension, runs the local daemon, and optionally speaks MCP.",
34251
34277
  type: "module",
34252
34278
  license: "MIT",
34253
34279
  packageManager: "yarn@4.17.1",
@@ -34259,8 +34285,12 @@ var package_default = {
34259
34285
  },
34260
34286
  bugs: "https://github.com/imshaikot/browsentic/issues",
34261
34287
  keywords: [
34262
- "browser-automation",
34288
+ "browser-extension",
34263
34289
  "chrome-extension",
34290
+ "ai-browser-extension",
34291
+ "side-panel",
34292
+ "browser-agent",
34293
+ "browser-automation",
34264
34294
  "ai-agent",
34265
34295
  "agentic-browsing",
34266
34296
  "mcp",
@@ -34313,6 +34343,7 @@ var USAGE = `browsentic ${package_default.version} \u2014 hand your real browser
34313
34343
  browsentic agent show which agent runs the side panel, and which are installed
34314
34344
  browsentic agent <name> switch to claude, codex or antigravity
34315
34345
  browsentic agent fix <name> let Browsentic fix what that agent still needs
34346
+ browsentic agent model <name> [model] pin that agent's model, or omit it for the CLI's default
34316
34347
 
34317
34348
  browsentic skills list the skills the agent can route to, and where they came from
34318
34349
  browsentic approvals list the \u201Calways on this site\u201D approvals you have granted
@@ -34321,9 +34352,12 @@ var USAGE = `browsentic ${package_default.version} \u2014 hand your real browser
34321
34352
  browsentic downloads clear delete all of them
34322
34353
  browsentic tools print the bundled tool manifest (no browser needed)
34323
34354
  browsentic logs print the daemon log
34355
+ browsentic start bring the background daemon up, if it is not already
34324
34356
  browsentic stop stop the background daemon
34325
34357
  browsentic restart stop the daemon and bring up a fresh one
34326
34358
  browsentic token print the control token (for MCP clients, not the browser)
34359
+
34360
+ agent, skills, approvals and downloads take --json, which is what the macOS app reads.
34327
34361
  browsentic --version print the version
34328
34362
 
34329
34363
  Getting started: browsentic setup
@@ -34335,6 +34369,8 @@ For MCP clients
34335
34369
  var invokedAs = basename3(process.argv[1] ?? "").replace(/\.(?:js|cjs|mjs|exe|cmd|ps1)$/i, "");
34336
34370
  var servesBare = invokedAs === "browsentic-mcp" || !!process.env.BROWSENTIC_AGENT_RUN;
34337
34371
  var [command] = process.argv.slice(2);
34372
+ var wantsJson = process.argv.includes("--json");
34373
+ var positional = process.argv.slice(3).filter((arg) => !arg.startsWith("--"));
34338
34374
  switch (command) {
34339
34375
  case void 0:
34340
34376
  if (servesBare) await serve();
@@ -34362,7 +34398,7 @@ switch (command) {
34362
34398
  await revoke(process.argv[3]);
34363
34399
  break;
34364
34400
  case "agent":
34365
- await chooseAgent(process.argv[3], process.argv[4]);
34401
+ await chooseAgent(positional[0], positional[1], positional[2]);
34366
34402
  break;
34367
34403
  case "tools":
34368
34404
  printTools();
@@ -34370,6 +34406,9 @@ switch (command) {
34370
34406
  case "status":
34371
34407
  await showStatus();
34372
34408
  break;
34409
+ case "start":
34410
+ await start();
34411
+ break;
34373
34412
  case "stop":
34374
34413
  await stop();
34375
34414
  break;
@@ -34380,10 +34419,10 @@ switch (command) {
34380
34419
  printSkills();
34381
34420
  break;
34382
34421
  case "approvals":
34383
- manageApprovals(process.argv[3], process.argv[4]);
34422
+ manageApprovals(positional[0], positional[1]);
34384
34423
  break;
34385
34424
  case "downloads":
34386
- manageDownloads(process.argv[3]);
34425
+ manageDownloads(positional[0]);
34387
34426
  break;
34388
34427
  case "logs":
34389
34428
  showLogs();
@@ -34432,6 +34471,16 @@ function printTools() {
34432
34471
  }
34433
34472
  function printSkills() {
34434
34473
  const skills = loadSkills();
34474
+ if (wantsJson) {
34475
+ const config3 = readAgentConfig();
34476
+ const listed = skills.map(({ body: _body, ...skill }) => ({
34477
+ ...skill,
34478
+ path: skill.provenance === "generated" ? join18(uploadedSkillsDir(), skill.name) : void 0
34479
+ }));
34480
+ const own2 = agentSkills(config3).map(({ name, description }) => ({ name, description }));
34481
+ console.log(JSON.stringify({ skills: listed, dirs: skillDirNames(), agent: config3.agent, agentSkills: own2 }, null, 2));
34482
+ return;
34483
+ }
34435
34484
  if (!skills.length) {
34436
34485
  console.log(`No skills found. Looked in:
34437
34486
  ${skillDirNames().join("\n ")}`);
@@ -34502,6 +34551,10 @@ async function stop() {
34502
34551
  if (!stopped.length && !stubborn.length) console.log("No daemon is answering; nothing to stop.");
34503
34552
  if (stubborn.length) process.exitCode = 1;
34504
34553
  }
34554
+ async function start() {
34555
+ const lock = await ensureDaemon();
34556
+ console.log(`Daemon running on 127.0.0.1:${lock.port} (pid ${lock.pid}, v${lock.daemonVersion}).`);
34557
+ }
34505
34558
  async function restart() {
34506
34559
  const { stubborn } = await stopDaemons();
34507
34560
  if (stubborn.length) {
@@ -34672,6 +34725,7 @@ async function uninstall(argv) {
34672
34725
  console.log(" live in extension storage rather than on disk.");
34673
34726
  if (kind === "global") console.log("\n the command itself: npm rm -g browsentic");
34674
34727
  if (kind === "repo") console.log("\n the global link: yarn daemon:unlink");
34728
+ if (kind === "app") console.log("\n the app itself: drag Browsentic.app from Applications to the Trash");
34675
34729
  console.log("\n the entry in your MCP client, e.g. claude mcp remove browsentic");
34676
34730
  if (flag("dry-run")) {
34677
34731
  console.log("\n --dry-run: nothing was removed.\n");
@@ -34727,7 +34781,15 @@ async function showSessions() {
34727
34781
  console.log(` extension v${session.extensionVersion}, paired ${session.pairedAt}, last seen ${session.lastSeenAt}`);
34728
34782
  }
34729
34783
  }
34730
- async function chooseAgent(first, second) {
34784
+ async function chooseAgent(first, second, third) {
34785
+ if (first === "model") {
34786
+ if (!isAgentKind(second)) {
34787
+ console.error(`Name the agent whose model to set. Pick one of: ${AGENT_KINDS.join(", ")}`);
34788
+ process.exit(1);
34789
+ }
34790
+ writeAgentModel(second, third ?? null);
34791
+ first = second = void 0;
34792
+ }
34731
34793
  const grant = first === "fix" || first === "setup";
34732
34794
  const named = grant ? second : first;
34733
34795
  if (named !== void 0 && !isAgentKind(named)) {
@@ -34738,6 +34800,10 @@ async function chooseAgent(first, second) {
34738
34800
  const bridge = await connect();
34739
34801
  const state = await bridge.agent(kind && (grant ? { grant: kind } : { set: kind }));
34740
34802
  await bridge.close();
34803
+ if (wantsJson) {
34804
+ console.log(JSON.stringify({ ...state, catalog: AGENT_LIST }, null, 2));
34805
+ return;
34806
+ }
34741
34807
  for (const runner of state.runners) {
34742
34808
  const agent = AGENTS[runner.kind];
34743
34809
  const mark = runner.kind === state.active ? "\u25CF" : "\u25CB";
@@ -34775,6 +34841,10 @@ function manageDownloads(sub) {
34775
34841
  return;
34776
34842
  }
34777
34843
  const downloads = storedDownloads();
34844
+ if (wantsJson) {
34845
+ console.log(JSON.stringify({ dir: downloadDir(), downloads }, null, 2));
34846
+ return;
34847
+ }
34778
34848
  if (!downloads.length) {
34779
34849
  console.log(`Nothing captured. Files land in ${downloadDir()} when an agent uses page.captureDownload.`);
34780
34850
  return;
@@ -34800,6 +34870,10 @@ function manageApprovals(sub, host) {
34800
34870
  return;
34801
34871
  }
34802
34872
  const grants = listGrants();
34873
+ if (wantsJson) {
34874
+ console.log(JSON.stringify({ grants }, null, 2));
34875
+ return;
34876
+ }
34803
34877
  if (!grants.length) {
34804
34878
  console.log("No standing approvals. Every gated action still asks.");
34805
34879
  return;