ai-browser-bridge 0.5.1 → 0.5.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/README.md CHANGED
@@ -163,17 +163,21 @@ Then validate it and start the autonomous queue:
163
163
 
164
164
  ```bash
165
165
  bridge chat organize --plan @organization-plan.json --dry-run
166
- bridge chat organize --plan @organization-plan.json --interval 10-20 --cooldown 300
166
+ bridge chat organize --plan @organization-plan.json \
167
+ --interval 60-90 --cooldown 600 --max-attempts 4
167
168
  ```
168
169
 
169
170
  The queue verifies every move from ChatGPT's loaded Project label, with the destination Project page
170
- as a fallback. It spaces UI operations by a fixed `--interval 30` or randomized range such as
171
- `--interval 10-20`, and acknowledges ChatGPT's “Too many requests” modal before waiting for
172
- `--cooldown`; rate-limit cooldowns do not consume retry attempts. Progress is persisted under
173
- `.bridge/chat-organization-queues/`; rerunning the same plan resumes pending work without repeating
174
- completed moves. Use `--restart` only when you intentionally want to replay the full plan, and
175
- `--max-attempts` to change the default of three attempts per Conversation. `--json` keeps the final
176
- summary on stdout and writes progress to stderr.
171
+ as a fallback. It spaces UI operations by a fixed interval or randomized range; the defaults are a
172
+ 60-second interval, a 600-second rate-limit cooldown, and four attempts per Conversation. For large
173
+ history cleanups, the randomized `--interval 60-90` profile above is recommended. The queue
174
+ acknowledges ChatGPT's “Too many requests” modal before cooling down, and automatically relaunches a
175
+ closed browser session. Neither infrastructure condition consumes a move attempt. Progress is
176
+ persisted under `.bridge/chat-organization-queues/`; rerunning the same plan resumes pending work
177
+ without repeating completed moves. Once the queue has started and processed or deferred one item,
178
+ it can continue unattended without an agent watching it. Use `--restart` only when you intentionally
179
+ want to replay the full plan. `--json` keeps the final summary on stdout and writes progress to
180
+ stderr.
177
181
 
178
182
  ## Agents & fan-out
179
183
 
package/dist/bridge.js CHANGED
@@ -11195,6 +11195,11 @@ var ChatOrganizationQueueSchema = Schema8.Struct({
11195
11195
  items: Schema8.Array(ChatOrganizationQueueItemSchema).pipe(Schema8.minItems(1))
11196
11196
  });
11197
11197
  var ORGANIZATION_INTERVAL = /^(?<minimum>\d+)(?:-(?<maximum>\d+))?$/u;
11198
+ var CLOSED_BROWSER_SESSION = /(?:target page, context or browser has been closed|browser has been closed|context has been closed|page has been closed)/iu;
11199
+ var chatOrganizationSessionWasClosed = (reason) => {
11200
+ if (reason === void 0) return false;
11201
+ return CLOSED_BROWSER_SESSION.test(reason);
11202
+ };
11198
11203
  var chatOrganizationIntervalFrom = (value, defaultSeconds) => {
11199
11204
  if (value === void 0) {
11200
11205
  return { minimumSeconds: defaultSeconds, maximumSeconds: defaultSeconds };
@@ -15076,11 +15081,12 @@ var runChatMove = async (chat, options) => {
15076
15081
  writeMoveOutcomes(outcomes, options);
15077
15082
  process.exit(outcomes.every((outcome) => outcome.moved || outcome.alreadyFiled) ? 0 : 1);
15078
15083
  };
15079
- var DEFAULT_ORGANIZATION_INTERVAL_SECONDS = 30;
15080
- var DEFAULT_ORGANIZATION_COOLDOWN_SECONDS = 300;
15081
- var DEFAULT_ORGANIZATION_MAX_ATTEMPTS = 3;
15084
+ var DEFAULT_ORGANIZATION_INTERVAL_SECONDS = 60;
15085
+ var DEFAULT_ORGANIZATION_COOLDOWN_SECONDS = 600;
15086
+ var DEFAULT_ORGANIZATION_MAX_ATTEMPTS = 4;
15082
15087
  var MAX_CONSECUTIVE_ORGANIZATION_COOLDOWNS = 12;
15083
15088
  var RATE_LIMIT_REASON = "ChatGPT temporarily limited access to Conversation history.";
15089
+ var CLOSED_SESSION_REASON = "ChatGPT browser session closed; reopening before retry.";
15084
15090
  var positiveIntegerOption = (value, optionName, defaultValue) => {
15085
15091
  if (value === void 0) return defaultValue;
15086
15092
  if (!/^[1-9]\d*$/u.test(value.trim())) {
@@ -15159,7 +15165,14 @@ var writeChatOrganizationDryRun = (input) => {
15159
15165
  var recordChatOrganizationAttempt = async (input) => {
15160
15166
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
15161
15167
  let queue;
15162
- if (input.rateLimited) {
15168
+ if (input.sessionClosed) {
15169
+ queue = deferChatOrganizationQueueItem({
15170
+ queue: input.queue,
15171
+ index: input.index,
15172
+ reason: CLOSED_SESSION_REASON,
15173
+ timestamp
15174
+ });
15175
+ } else if (input.rateLimited) {
15163
15176
  queue = deferChatOrganizationQueueItem({
15164
15177
  queue: input.queue,
15165
15178
  index: input.index,
@@ -15269,7 +15282,7 @@ var runChatOrganize = async (options) => {
15269
15282
  await releaseQueueLock();
15270
15283
  throw error;
15271
15284
  }
15272
- const { engine, page } = workspace;
15285
+ let workspaceWasShutdown = false;
15273
15286
  try {
15274
15287
  let index = nextChatOrganizationQueueIndex(queue);
15275
15288
  let consecutiveRateLimitCooldowns = 0;
@@ -15279,7 +15292,7 @@ var runChatOrganize = async (options) => {
15279
15292
  index = nextChatOrganizationQueueIndex(queue);
15280
15293
  continue;
15281
15294
  }
15282
- if (await acknowledgeChatGptHistoryRateLimit(page)) {
15295
+ if (await acknowledgeChatGptHistoryRateLimit(workspace.page)) {
15283
15296
  consecutiveRateLimitCooldowns += 1;
15284
15297
  if (consecutiveRateLimitCooldowns >= MAX_CONSECUTIVE_ORGANIZATION_COOLDOWNS) {
15285
15298
  writeChatOrganizationProgress(
@@ -15303,23 +15316,45 @@ var runChatOrganize = async (options) => {
15303
15316
  let moveOutcome;
15304
15317
  let thrownReason;
15305
15318
  try {
15306
- moveOutcome = await moveChatToProject(page, {
15319
+ moveOutcome = await moveChatToProject(workspace.page, {
15307
15320
  chat: item.conversation,
15308
15321
  project: item.project
15309
15322
  });
15310
15323
  } catch (error) {
15311
15324
  thrownReason = error instanceof Error ? error.message : String(error);
15312
15325
  }
15313
- const rateLimited = await acknowledgeChatGptHistoryRateLimit(page);
15326
+ const sessionClosed = chatOrganizationSessionWasClosed(thrownReason);
15327
+ const rateLimited = await acknowledgeChatGptHistoryRateLimit(workspace.page);
15314
15328
  queue = await recordChatOrganizationAttempt({
15315
15329
  queue,
15316
15330
  queuePath,
15317
15331
  index,
15318
15332
  maxAttempts,
15333
+ sessionClosed,
15319
15334
  rateLimited,
15320
15335
  moveOutcome,
15321
15336
  thrownReason
15322
15337
  });
15338
+ if (sessionClosed) {
15339
+ writeChatOrganizationProgress(
15340
+ "ChatGPT browser session closed; reopening it without consuming a retry attempt.",
15341
+ options
15342
+ );
15343
+ await workspace.engine.shutdown({ closeBrowser: false });
15344
+ workspaceWasShutdown = true;
15345
+ try {
15346
+ workspace = await startWorkspaceSession(options);
15347
+ workspaceWasShutdown = false;
15348
+ } catch (error) {
15349
+ writeChatOrganizationProgress(
15350
+ `Could not reopen the ChatGPT browser session; leaving the queue pending: ${error instanceof Error ? error.message : String(error)}`,
15351
+ options
15352
+ );
15353
+ break;
15354
+ }
15355
+ index = nextChatOrganizationQueueIndex(queue);
15356
+ continue;
15357
+ }
15323
15358
  const recorded = queue.items[index];
15324
15359
  if (recorded?.status === "moved") {
15325
15360
  writeChatOrganizationProgress(`Moved "${recorded.conversation}".`, options);
@@ -15351,7 +15386,7 @@ var runChatOrganize = async (options) => {
15351
15386
  }
15352
15387
  } finally {
15353
15388
  try {
15354
- await engine.shutdown({ closeBrowser: false });
15389
+ if (!workspaceWasShutdown) await workspace.engine.shutdown({ closeBrowser: false });
15355
15390
  } finally {
15356
15391
  await releaseQueueLock();
15357
15392
  }
@@ -15820,7 +15855,7 @@ var subcommandOpts = (command) => {
15820
15855
  // src/features/terminal/registerCli.ts
15821
15856
  var PROVIDER_OPTION = `Browser provider: ${PROVIDER_IDS.join(", ")} (default: ${DEFAULT_PROVIDER})`;
15822
15857
  var registerCliCommands = (program) => {
15823
- program.name("bridge").description("Terminal CLI that bridges ChatGPT or Gemini with local tools via MCP").version("0.5.1").option("-r, --repo <path>", "Path to the target repository (default: cwd)").option("-p, --port <number>", "MCP server port (default: 8765)").option("--provider <name>", PROVIDER_OPTION).option("--no-browser", "Skip Chrome browser connection").action(async (_options, command) => {
15858
+ program.name("bridge").description("Terminal CLI that bridges ChatGPT or Gemini with local tools via MCP").version("0.5.2").option("-r, --repo <path>", "Path to the target repository (default: cwd)").option("-p, --port <number>", "MCP server port (default: 8765)").option("--provider <name>", PROVIDER_OPTION).option("--no-browser", "Skip Chrome browser connection").action(async (_options, command) => {
15824
15859
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
15825
15860
  command.outputHelp({ error: true });
15826
15861
  process.exitCode = 1;
@@ -15954,8 +15989,8 @@ var registerWorkspaceCommands = (program) => {
15954
15989
  "JSON array of {conversation, project} tasks (inline, @file, or a path)"
15955
15990
  ).option(
15956
15991
  "--interval <secondsOrRange>",
15957
- "Seconds between UI operations, fixed or random range such as 10-20 (default: 30)"
15958
- ).option("--cooldown <seconds>", "Seconds to wait after rate limiting (default: 300)").option("--max-attempts <count>", "Attempts per Conversation before failing (default: 3)").option("--restart", "Restart this plan instead of resuming its persisted queue").option("--dry-run", "Validate and show the queue without opening Chrome or writing state").action(
15992
+ "Seconds between UI operations, fixed or random range such as 60-90 (default: 60)"
15993
+ ).option("--cooldown <seconds>", "Seconds to wait after rate limiting (default: 600)").option("--max-attempts <count>", "Attempts per Conversation before failing (default: 4)").option("--restart", "Restart this plan instead of resuming its persisted queue").option("--dry-run", "Validate and show the queue without opening Chrome or writing state").action(
15959
15994
  (_options, command) => runChatOrganize(command.optsWithGlobals())
15960
15995
  );
15961
15996
  const task = program.command("task").description("List or schedule ChatGPT Tasks (ChatGPT only)");