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.
- package/Formula/freebuff-autocontinue.rb +2 -2
- package/dist/cli.js +61 -5
- package/dist/index.js +63 -5
- package/package.json +1 -1
|
@@ -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.
|
|
7
|
-
sha256 "
|
|
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
|
-
|
|
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 ${
|
|
1131
|
+
console.log(`[autocontinue] Submitting option ${target} (of ${optionCount})\u2026`);
|
|
1111
1132
|
sendEnter(name, enterKey);
|
|
1112
|
-
await new Promise((r) => setTimeout(r,
|
|
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 ${
|
|
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.
|
|
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
|
-
|
|
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 ${
|
|
1135
|
+
console.log(`[autocontinue] Submitting option ${target} (of ${optionCount})\u2026`);
|
|
1115
1136
|
sendEnter(name, enterKey);
|
|
1116
|
-
await new Promise((r) => setTimeout(r,
|
|
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 ${
|
|
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.
|
|
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
|
+
"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",
|