freebuff-autocontinue 0.2.3 → 0.2.5

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.
@@ -3,8 +3,8 @@ class FreebuffAutocontinue < Formula
3
3
  homepage "https://github.com/kelvindesman/freebuff-autocontinue"
4
4
  # NOTE: the tag-driven release workflow patches `url` + `sha256` on every
5
5
  # version tag. The placeholder below is only for local `brew install --build-from-source` checks.
6
- url "https://github.com/kelvindesman/freebuff-autocontinue/releases/download/v0.1.8/freebuff-autocontinue-v0.1.8.tar.gz"
7
- sha256 "77ef88dced2b327a63495f504812af0f49ee05da95593b8d9efd9685c4150c41"
6
+ url "https://github.com/kelvindesman/freebuff-autocontinue/releases/download/v0.2.4/freebuff-autocontinue-v0.2.4.tar.gz"
7
+ sha256 "b2ddf2ae79c7a17f3ed482aa6ad2cd9e28f63ace626befab7c27fc94d7bd2aa3"
8
8
  license "MIT"
9
9
  head "https://github.com/kelvindesman/freebuff-autocontinue.git", branch: "main"
10
10
 
package/dist/cli.js CHANGED
@@ -85,6 +85,25 @@ function isIdleReady(text) {
85
85
  }
86
86
  return false;
87
87
  }
88
+ var QUESTION_OPTION_RE = /^([☐☑○●]|\(\s*\)|\[\s*\]|\[x\]|\(\*\))\s*(.+)$/i;
89
+ function extractQuestionOptions(text) {
90
+ const cleaned = stripAnsi(text);
91
+ if (!cleaned.includes("Some questions for you") && !cleaned.includes("Enter select")) {
92
+ return [];
93
+ }
94
+ const options = [];
95
+ for (const line of cleaned.split("\n")) {
96
+ const clean = line.replace(/^[│\s]+|[│\s]+$/g, "");
97
+ if (!clean) {
98
+ continue;
99
+ }
100
+ const m = clean.match(QUESTION_OPTION_RE);
101
+ if (m) {
102
+ options.push((m[2] ?? "").trim());
103
+ }
104
+ }
105
+ return options;
106
+ }
88
107
  function extractQuestion(text) {
89
108
  const cleaned = stripAnsi(text);
90
109
  if (!cleaned.includes("Some questions for you") && !cleaned.includes("Enter select")) {
@@ -1102,18 +1121,49 @@ ${qText}`);
1102
1121
  continue;
1103
1122
  }
1104
1123
  const parsed = Number.parseInt(choice, 10);
1124
+ const optionCount = extractQuestionOptions(pane).length || 5;
1105
1125
  const optNum = Number.isFinite(parsed) && parsed >= 1 ? parsed : 1;
1106
- for (let i = 0; i < optNum - 1; i++) {
1126
+ const target = Math.min(optNum, optionCount);
1127
+ for (let i = 0; i < target - 1; i++) {
1107
1128
  sendEnter(name, "Down");
1108
1129
  await new Promise((r) => setTimeout(r, 300));
1109
1130
  }
1110
- console.log(`[autocontinue] Submitting option ${optNum}\u2026`);
1131
+ console.log(`[autocontinue] Submitting option ${target} (of ${optionCount})\u2026`);
1111
1132
  sendEnter(name, enterKey);
1112
- await new Promise((r) => setTimeout(r, 1e3));
1133
+ await new Promise((r) => setTimeout(r, 400));
1134
+ const downsToSubmit = Math.max(1, optionCount - target + 1);
1135
+ for (let i = 0; i < downsToSubmit; i++) {
1136
+ sendEnter(name, "Down");
1137
+ await new Promise((r) => setTimeout(r, 250));
1138
+ }
1113
1139
  sendEnter(name, enterKey);
1140
+ await new Promise((r) => setTimeout(r, 1e3));
1141
+ let dismissed = classify(capture(name)).action !== "question";
1142
+ for (let attempt = 0; attempt < 3 && !dismissed; attempt++) {
1143
+ sendEnter(name, enterKey);
1144
+ await new Promise((r) => setTimeout(r, 800));
1145
+ dismissed = classify(capture(name)).action !== "question";
1146
+ if (dismissed) {
1147
+ break;
1148
+ }
1149
+ sendEnter(name, "Down");
1150
+ await new Promise((r) => setTimeout(r, 200));
1151
+ sendEnter(name, enterKey);
1152
+ await new Promise((r) => setTimeout(r, 800));
1153
+ dismissed = classify(capture(name)).action !== "question";
1154
+ }
1155
+ if (dismissed) {
1156
+ console.log(
1157
+ `[autocontinue] Option ${target} submitted successfully, modal dismissed`
1158
+ );
1159
+ } else {
1160
+ console.log(
1161
+ `[autocontinue] Warning: modal may still be open after submitting option ${target}`
1162
+ );
1163
+ }
1114
1164
  acted.question = now;
1115
1165
  lastSend = now;
1116
- logSnapshot(logFile, name, `answered-question: option ${optNum}`);
1166
+ logSnapshot(logFile, name, `answered-question: option ${target}`);
1117
1167
  await new Promise((r) => setTimeout(r, 2e3));
1118
1168
  continue;
1119
1169
  }
@@ -1261,7 +1311,7 @@ ${qText}`);
1261
1311
  }
1262
1312
 
1263
1313
  // src/cli.ts
1264
- var VERSION = "0.2.3";
1314
+ var VERSION = "0.2.5";
1265
1315
  var SAMPLE_TRANSCRIPT = [
1266
1316
  "agent finished editing src/payroll.ts",
1267
1317
  "Your free session ended, so the agent stopped here. Send a message to start a new session and continue.",
@@ -1382,6 +1432,12 @@ function selfTest() {
1382
1432
  "\u256D\u2500\u2500 Some questions for you \u2500\u2500\u256E\n\u2502 Which ticket? \u2502\n\u2502 \u2191\u2193 navigate \u2022 Enter select \u2502\n\u2570\u2500\u2500 Submit \u2500\u2500\u256F"
1383
1433
  ).action === "question"
1384
1434
  );
1435
+ check(
1436
+ "question options counted",
1437
+ extractQuestionOptions(
1438
+ "\u256D\u2500\u2500 Some questions for you \u2500\u2500\u256E\n\u2502 \u25CB Option A \u2502\n\u2502 \u25CF Option B \u2502\n\u2502 \u2191\u2193 navigate \u2022 Enter select \u2502\n\u2570\u2500\u2500 Submit \u2500\u2500\u256F"
1439
+ ).length === 2
1440
+ );
1385
1441
  check("login gate -> login", classify("Press ENTER to login...").action === "login");
1386
1442
  check(
1387
1443
  "update notice -> update",
package/dist/index.js CHANGED
@@ -78,6 +78,25 @@ function isIdleReady(text) {
78
78
  }
79
79
  return false;
80
80
  }
81
+ var QUESTION_OPTION_RE = /^([☐☑○●]|\(\s*\)|\[\s*\]|\[x\]|\(\*\))\s*(.+)$/i;
82
+ function extractQuestionOptions(text) {
83
+ const cleaned = stripAnsi(text);
84
+ if (!cleaned.includes("Some questions for you") && !cleaned.includes("Enter select")) {
85
+ return [];
86
+ }
87
+ const options = [];
88
+ for (const line of cleaned.split("\n")) {
89
+ const clean = line.replace(/^[│\s]+|[│\s]+$/g, "");
90
+ if (!clean) {
91
+ continue;
92
+ }
93
+ const m = clean.match(QUESTION_OPTION_RE);
94
+ if (m) {
95
+ options.push((m[2] ?? "").trim());
96
+ }
97
+ }
98
+ return options;
99
+ }
81
100
  function extractQuestion(text) {
82
101
  const cleaned = stripAnsi(text);
83
102
  if (!cleaned.includes("Some questions for you") && !cleaned.includes("Enter select")) {
@@ -1106,18 +1125,49 @@ ${qText}`);
1106
1125
  continue;
1107
1126
  }
1108
1127
  const parsed = Number.parseInt(choice, 10);
1128
+ const optionCount = extractQuestionOptions(pane).length || 5;
1109
1129
  const optNum = Number.isFinite(parsed) && parsed >= 1 ? parsed : 1;
1110
- for (let i = 0; i < optNum - 1; i++) {
1130
+ const target = Math.min(optNum, optionCount);
1131
+ for (let i = 0; i < target - 1; i++) {
1111
1132
  sendEnter(name, "Down");
1112
1133
  await new Promise((r) => setTimeout(r, 300));
1113
1134
  }
1114
- console.log(`[autocontinue] Submitting option ${optNum}\u2026`);
1135
+ console.log(`[autocontinue] Submitting option ${target} (of ${optionCount})\u2026`);
1115
1136
  sendEnter(name, enterKey);
1116
- await new Promise((r) => setTimeout(r, 1e3));
1137
+ await new Promise((r) => setTimeout(r, 400));
1138
+ const downsToSubmit = Math.max(1, optionCount - target + 1);
1139
+ for (let i = 0; i < downsToSubmit; i++) {
1140
+ sendEnter(name, "Down");
1141
+ await new Promise((r) => setTimeout(r, 250));
1142
+ }
1117
1143
  sendEnter(name, enterKey);
1144
+ await new Promise((r) => setTimeout(r, 1e3));
1145
+ let dismissed = classify(capture(name)).action !== "question";
1146
+ for (let attempt = 0; attempt < 3 && !dismissed; attempt++) {
1147
+ sendEnter(name, enterKey);
1148
+ await new Promise((r) => setTimeout(r, 800));
1149
+ dismissed = classify(capture(name)).action !== "question";
1150
+ if (dismissed) {
1151
+ break;
1152
+ }
1153
+ sendEnter(name, "Down");
1154
+ await new Promise((r) => setTimeout(r, 200));
1155
+ sendEnter(name, enterKey);
1156
+ await new Promise((r) => setTimeout(r, 800));
1157
+ dismissed = classify(capture(name)).action !== "question";
1158
+ }
1159
+ if (dismissed) {
1160
+ console.log(
1161
+ `[autocontinue] Option ${target} submitted successfully, modal dismissed`
1162
+ );
1163
+ } else {
1164
+ console.log(
1165
+ `[autocontinue] Warning: modal may still be open after submitting option ${target}`
1166
+ );
1167
+ }
1118
1168
  acted.question = now;
1119
1169
  lastSend = now;
1120
- logSnapshot(logFile, name, `answered-question: option ${optNum}`);
1170
+ logSnapshot(logFile, name, `answered-question: option ${target}`);
1121
1171
  await new Promise((r) => setTimeout(r, 2e3));
1122
1172
  continue;
1123
1173
  }
@@ -1265,7 +1315,7 @@ ${qText}`);
1265
1315
  }
1266
1316
 
1267
1317
  // src/cli.ts
1268
- var VERSION = "0.2.3";
1318
+ var VERSION = "0.2.5";
1269
1319
  var SAMPLE_TRANSCRIPT = [
1270
1320
  "agent finished editing src/payroll.ts",
1271
1321
  "Your free session ended, so the agent stopped here. Send a message to start a new session and continue.",
@@ -1386,6 +1436,12 @@ function selfTest() {
1386
1436
  "\u256D\u2500\u2500 Some questions for you \u2500\u2500\u256E\n\u2502 Which ticket? \u2502\n\u2502 \u2191\u2193 navigate \u2022 Enter select \u2502\n\u2570\u2500\u2500 Submit \u2500\u2500\u256F"
1387
1437
  ).action === "question"
1388
1438
  );
1439
+ check(
1440
+ "question options counted",
1441
+ extractQuestionOptions(
1442
+ "\u256D\u2500\u2500 Some questions for you \u2500\u2500\u256E\n\u2502 \u25CB Option A \u2502\n\u2502 \u25CF Option B \u2502\n\u2502 \u2191\u2193 navigate \u2022 Enter select \u2502\n\u2570\u2500\u2500 Submit \u2500\u2500\u256F"
1443
+ ).length === 2
1444
+ );
1389
1445
  check("login gate -> login", classify("Press ENTER to login...").action === "login");
1390
1446
  check(
1391
1447
  "update notice -> update",
@@ -1682,6 +1738,7 @@ export {
1682
1738
  LOGIN_URL_RE,
1683
1739
  PAYWALL_PATTERNS,
1684
1740
  PRICE_RE,
1741
+ QUESTION_OPTION_RE,
1685
1742
  QUESTION_PATTERNS,
1686
1743
  STOP_PATTERNS,
1687
1744
  TELEMETRY_ENABLED_ENV,
@@ -1700,6 +1757,7 @@ export {
1700
1757
  classify,
1701
1758
  extractLoginUrl,
1702
1759
  extractQuestion,
1760
+ extractQuestionOptions,
1703
1761
  extractStatus,
1704
1762
  findModel,
1705
1763
  flagNames,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freebuff-autocontinue",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Zero-dependency autonomous session supervisor for Freebuff CLI with smart model fallback, Pacific Midnight refill scheduling, and graceful account switching.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",