companion-for-agy 1.2.0-alpha.2 → 1.2.0
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/CHANGELOG.md +9 -0
- package/README_de.md +26 -0
- package/ROADMAP.md +9 -0
- package/package.json +1 -1
- package/src/agy-companion.mjs +167 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0] - 2026-06-07
|
|
4
|
+
|
|
5
|
+
### Fixed (Bugsweep)
|
|
6
|
+
- **Security:** Stale temp workspace from crashed run with same PID could leak permissions to new run — now cleaned on startup (`e8c5230`)
|
|
7
|
+
- Temp directory leak in sandbox/skip-permissions modes when no custom rules are set (`d406299`)
|
|
8
|
+
- Temp cleanup race on Windows: post-kill delay + rmSync retries for CWD lock (`41412d6`)
|
|
9
|
+
- ConPTY text extraction: stale cursor position, bold SGR false-positive, dedup scope too narrow (`c2194bb`)
|
|
10
|
+
- isNoiseLine false positives for blockquotes (`>`) and lines containing "tokens" keyword (`f6a8e7b`)
|
|
11
|
+
|
|
3
12
|
## [1.2.0-alpha.2] - 2026-06-07
|
|
4
13
|
|
|
5
14
|
### Changed
|
package/README_de.md
CHANGED
|
@@ -41,6 +41,18 @@ npm install -g companion-for-agy
|
|
|
41
41
|
- **macOS:** `xcode-select --install`
|
|
42
42
|
- **Linux:** `sudo apt install build-essential python3` (Debian/Ubuntu)
|
|
43
43
|
|
|
44
|
+
### Fehlerbehebung bei `node-pty` Build-Fehlern
|
|
45
|
+
|
|
46
|
+
Falls `npm install` mit nativen Kompilierungsfehlern fehlschlägt:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Alle Plattformen: native Module neu bauen
|
|
50
|
+
npm rebuild node-pty
|
|
51
|
+
|
|
52
|
+
# Windows: falls cl.exe nicht gefunden wird, Visual Studio Build Tools installieren
|
|
53
|
+
# dann "Developer Command Prompt" oder "x64 Native Tools" verwenden
|
|
54
|
+
```
|
|
55
|
+
|
|
44
56
|
## Verwendung
|
|
45
57
|
|
|
46
58
|
```bash
|
|
@@ -73,6 +85,13 @@ companion-for-agy [optionen] "prompt"
|
|
|
73
85
|
| `--json` | Ausgabe als JSON-Objekt |
|
|
74
86
|
| `--debug` | Rohen PTY-Output in `agy-debug.log` speichern |
|
|
75
87
|
|
|
88
|
+
### Umgebungsvariablen
|
|
89
|
+
|
|
90
|
+
| Variable | Beschreibung |
|
|
91
|
+
|----------|-------------|
|
|
92
|
+
| `AGY_COMPANION_AGY_PATH` | Pfad zur agy-Binary (wird automatisch erkannt wenn nicht gesetzt) |
|
|
93
|
+
| `AGY_PATH` | Alternativer Pfad zur agy-Binary |
|
|
94
|
+
|
|
76
95
|
### Beispiele
|
|
77
96
|
|
|
78
97
|
```bash
|
|
@@ -85,6 +104,9 @@ companion-for-agy --no-tools "Überprüfe diesen Code: ..."
|
|
|
85
104
|
# Web-Recherche
|
|
86
105
|
companion-for-agy --researcher "Neueste Infos zu Node.js 24"
|
|
87
106
|
|
|
107
|
+
# Nur-Lesen mit zusätzlicher Git-Berechtigung
|
|
108
|
+
companion-for-agy --read-only --allow "command(git log)" "prompt"
|
|
109
|
+
|
|
88
110
|
# JSON-Output für programmatische Nutzung
|
|
89
111
|
companion-for-agy --json --model gemini-3.5-pro "prompt"
|
|
90
112
|
```
|
|
@@ -121,6 +143,10 @@ companion-for-agy --json --model gemini-3.5-pro "prompt"
|
|
|
121
143
|
- **CI/CD-Pipelines:** Automatisierte Gemini-Abfragen in Build-Scripts
|
|
122
144
|
- **Scripting:** Jedes Szenario wo agys Antwort als Text benötigt wird
|
|
123
145
|
|
|
146
|
+
## Hintergrund
|
|
147
|
+
|
|
148
|
+
Dieses Tool entstand, weil drei CLI-Agenten — **Claude Code**, **Codex** und **agy** — sich gegenseitig als Fallback-Berater aufrufen können müssen. Claude → Codex und agy → Claude/Codex funktionieren bereits, aber Claude → agy war durch den TUI-stdout-Bug blockiert.
|
|
149
|
+
|
|
124
150
|
## Lizenz
|
|
125
151
|
|
|
126
152
|
MIT
|
package/ROADMAP.md
CHANGED
|
@@ -63,6 +63,15 @@ Emit response tokens as they arrive (line-by-line or chunk-by-chunk) instead of
|
|
|
63
63
|
### Response Format Detection
|
|
64
64
|
Detect whether agy's response is Markdown, JSON, or plain text and expose this in the JSON output (`"format": "markdown"`).
|
|
65
65
|
|
|
66
|
+
### Robustness Improvements (from Bugsweep 2026-06-07)
|
|
67
|
+
|
|
68
|
+
Items identified during the systematic bug sweep that are design improvements, not defects:
|
|
69
|
+
|
|
70
|
+
- **Response idle timer minimum-progress threshold:** Currently, any single byte within the idle window resets the timer. A very slow stream (1 char/10s) keeps the timer alive indefinitely — only the global timeout catches it. Add a "minimum bytes since last check" threshold.
|
|
71
|
+
- **Signal handling for external kill:** Register `process.on('SIGTERM')` and `process.on('SIGINT')` to ensure temp workspace cleanup when the process is killed externally (e.g., by a parent orchestrator or Ctrl+C in a pipeline).
|
|
72
|
+
- **Dead code cleanup:** `tempSettingsCreated` variable is set but never read. Cleanup works unconditionally via `cleanupTemp()`.
|
|
73
|
+
- **Prompt-echo filter edge case:** Very short prompts (≤2 chars) identical to the response text are incorrectly filtered as prompt echoes. Rare in practice (requires the user's question to be the same as the answer), but theoretically possible.
|
|
74
|
+
|
|
66
75
|
## Completed (v1.2.0-alpha.1)
|
|
67
76
|
|
|
68
77
|
- Trust dialog auto-confirmation (5-phase state machine)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "companion-for-agy",
|
|
3
|
-
"version": "1.2.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Unofficial PTY-based wrapper for agy (Antigravity CLI / Gemini CLI) — captures Gemini responses that agy writes to the terminal buffer instead of stdout",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/agy-companion.mjs",
|
package/src/agy-companion.mjs
CHANGED
|
@@ -71,6 +71,38 @@ export function findAgyPath() {
|
|
|
71
71
|
|
|
72
72
|
export const AGY_PATH = findAgyPath();
|
|
73
73
|
|
|
74
|
+
// ---------- Go-style Duration Parser ----------
|
|
75
|
+
|
|
76
|
+
export function parseDurationToMs(str) {
|
|
77
|
+
if (!str) return null;
|
|
78
|
+
const regex = /(\d+(?:\.\d+)?)(ns|us|µs|ms|s|m|h)/g;
|
|
79
|
+
let match;
|
|
80
|
+
let totalMs = 0;
|
|
81
|
+
let hasMatch = false;
|
|
82
|
+
while ((match = regex.exec(str)) !== null) {
|
|
83
|
+
hasMatch = true;
|
|
84
|
+
const value = parseFloat(match[1]);
|
|
85
|
+
const unit = match[2];
|
|
86
|
+
switch (unit) {
|
|
87
|
+
case 'ns': totalMs += value / 1e6; break;
|
|
88
|
+
case 'us':
|
|
89
|
+
case 'µs': totalMs += value / 1000; break;
|
|
90
|
+
case 'ms': totalMs += value; break;
|
|
91
|
+
case 's': totalMs += value * 1000; break;
|
|
92
|
+
case 'm': totalMs += value * 60000; break;
|
|
93
|
+
case 'h': totalMs += value * 3600000; break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!hasMatch) {
|
|
97
|
+
const plain = parseFloat(str);
|
|
98
|
+
if (!isNaN(plain) && plain > 0) {
|
|
99
|
+
return Math.round(plain * 1000);
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return Math.round(totalMs);
|
|
104
|
+
}
|
|
105
|
+
|
|
74
106
|
// ---------- Permission-Presets ----------
|
|
75
107
|
|
|
76
108
|
export const PERMISSION_PRESETS = {
|
|
@@ -105,6 +137,7 @@ export const PERMISSION_PRESETS = {
|
|
|
105
137
|
// ---------- State-Machine-Patterns ----------
|
|
106
138
|
|
|
107
139
|
export const TRUST_DIALOG_PATTERN = /Do you trust/;
|
|
140
|
+
export const LOGIN_PROMPT_PATTERN = /Select login method/;
|
|
108
141
|
export const BANNER_MODEL_PATTERN = /Gemini \d[\d.]* \w+(?:\s*\([^)]*\))?/;
|
|
109
142
|
|
|
110
143
|
export const STARTUP_DONE_PATTERNS = [
|
|
@@ -151,9 +184,9 @@ export function isNoiseLine(line, promptFilter = '') {
|
|
|
151
184
|
const t = line.trim();
|
|
152
185
|
if (!t) return true;
|
|
153
186
|
if (/^[│┌└┐┘├┤┬┴┼─═╔╗╚╝╠╣╦╩╬▸►◉●▲▼◆□■╭╮╯╰]+$/.test(t)) return true;
|
|
154
|
-
if (t
|
|
187
|
+
if (t === '>') return true;
|
|
155
188
|
if (/[⣾⣷⣯⣟⡿⢿⣻⣽⠿⠾⠽⠼⠻⠺⠹⠸⠷⠶⠵⠴⠳⠲⠱⠰]/.test(t)) return true;
|
|
156
|
-
if (/Generating|esc to cancel|for shortcuts
|
|
189
|
+
if (/Generating|esc to cancel|for shortcuts/i.test(t)) return true;
|
|
157
190
|
if (t === '?' || t === '? for shortcuts') return true;
|
|
158
191
|
if (/^Gemini \d/.test(t)) return true;
|
|
159
192
|
if (/^\d+\s*tokens$/.test(t)) return true;
|
|
@@ -172,6 +205,12 @@ export function extractByResponseColor(rawSection) {
|
|
|
172
205
|
let inResponseColor = false;
|
|
173
206
|
let pos = 0;
|
|
174
207
|
const src = rawSection;
|
|
208
|
+
let hadCursorPos = false;
|
|
209
|
+
let cursorRow = null;
|
|
210
|
+
let cursorCol = null;
|
|
211
|
+
let gapHadNewline = false;
|
|
212
|
+
let currentGapNewline = false;
|
|
213
|
+
let preColorSpaces = 0;
|
|
175
214
|
|
|
176
215
|
while (pos < src.length) {
|
|
177
216
|
if (src[pos] === '\x1b') {
|
|
@@ -182,8 +221,26 @@ export function extractByResponseColor(rawSection) {
|
|
|
182
221
|
const params = src.slice(pos + 2, end);
|
|
183
222
|
if (params === '38;2;232;234;237' && cmd === 'm') {
|
|
184
223
|
inResponseColor = true;
|
|
224
|
+
currentGapNewline = gapHadNewline;
|
|
225
|
+
hadCursorPos = false;
|
|
226
|
+
cursorRow = null;
|
|
227
|
+
cursorCol = null;
|
|
228
|
+
gapHadNewline = false;
|
|
185
229
|
} else if (cmd === 'm') {
|
|
186
|
-
|
|
230
|
+
if (params === '' || params === '0' || params === '39' ||
|
|
231
|
+
(params.startsWith('38;') && params !== '38;2;232;234;237') ||
|
|
232
|
+
/^3[0-7]$/.test(params) || /^9[0-7]$/.test(params)) {
|
|
233
|
+
inResponseColor = false;
|
|
234
|
+
}
|
|
235
|
+
} else if (inResponseColor && (cmd === 'H' || cmd === 'f')) {
|
|
236
|
+
hadCursorPos = true;
|
|
237
|
+
const parts = params.split(';');
|
|
238
|
+
if (parts.length >= 2) {
|
|
239
|
+
const r = parseInt(parts[0], 10);
|
|
240
|
+
const c = parseInt(parts[1], 10);
|
|
241
|
+
if (!isNaN(r)) cursorRow = r;
|
|
242
|
+
if (!isNaN(c)) cursorCol = c;
|
|
243
|
+
}
|
|
187
244
|
}
|
|
188
245
|
pos = end + 1;
|
|
189
246
|
} else if (src[pos + 1] === ']') {
|
|
@@ -199,9 +256,29 @@ export function extractByResponseColor(rawSection) {
|
|
|
199
256
|
while (textEnd < src.length && src[textEnd] !== '\x1b') textEnd++;
|
|
200
257
|
const rawText = src.slice(pos, textEnd);
|
|
201
258
|
const text = rawText.replace(/[\x00-\x08\x0b\x0e-\x1f\x7f]/g, '');
|
|
202
|
-
if (text.length > 0)
|
|
259
|
+
if (text.length > 0) {
|
|
260
|
+
segments.push({
|
|
261
|
+
text,
|
|
262
|
+
newLine: currentGapNewline && !hadCursorPos,
|
|
263
|
+
startCol: hadCursorPos ? cursorCol : null,
|
|
264
|
+
startRow: hadCursorPos ? cursorRow : null,
|
|
265
|
+
estimatedCol: !hadCursorPos ? preColorSpaces + 1 : null,
|
|
266
|
+
});
|
|
267
|
+
currentGapNewline = false;
|
|
268
|
+
if (hadCursorPos && cursorCol !== null) {
|
|
269
|
+
cursorCol += text.length;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
203
272
|
pos = textEnd;
|
|
204
273
|
} else {
|
|
274
|
+
if (src[pos] === '\n') {
|
|
275
|
+
gapHadNewline = true;
|
|
276
|
+
preColorSpaces = 0;
|
|
277
|
+
} else if (src[pos] === ' ') {
|
|
278
|
+
preColorSpaces++;
|
|
279
|
+
} else {
|
|
280
|
+
preColorSpaces = 0;
|
|
281
|
+
}
|
|
205
282
|
pos++;
|
|
206
283
|
}
|
|
207
284
|
}
|
|
@@ -209,10 +286,58 @@ export function extractByResponseColor(rawSection) {
|
|
|
209
286
|
if (segments.length === 0) return null;
|
|
210
287
|
|
|
211
288
|
const deduped = segments.filter((s, i) =>
|
|
212
|
-
!segments.some((o, j) => j !== i &&
|
|
289
|
+
!segments.some((o, j) => j !== i &&
|
|
290
|
+
s.startCol === null &&
|
|
291
|
+
o.text.length > s.text.length && o.text.startsWith(s.text))
|
|
213
292
|
);
|
|
214
293
|
|
|
215
|
-
|
|
294
|
+
let combined = '';
|
|
295
|
+
let lastEndCol = null;
|
|
296
|
+
let lastRow = null;
|
|
297
|
+
for (let i = 0; i < deduped.length; i++) {
|
|
298
|
+
const seg = deduped[i];
|
|
299
|
+
const rowChanged = seg.startRow !== null && lastRow !== null && seg.startRow !== lastRow;
|
|
300
|
+
if (i === 0) {
|
|
301
|
+
combined = seg.text;
|
|
302
|
+
if (seg.startCol !== null) {
|
|
303
|
+
lastEndCol = seg.startCol + seg.text.length;
|
|
304
|
+
} else if (seg.estimatedCol !== null) {
|
|
305
|
+
lastEndCol = seg.estimatedCol + seg.text.length;
|
|
306
|
+
}
|
|
307
|
+
if (seg.startRow !== null) lastRow = seg.startRow;
|
|
308
|
+
} else if (seg.startCol !== null && lastEndCol !== null && !rowChanged) {
|
|
309
|
+
const gap = seg.startCol - lastEndCol;
|
|
310
|
+
if (gap > 0) combined += ' '.repeat(gap);
|
|
311
|
+
else if (gap < 0) combined = combined.slice(0, Math.max(0, combined.length + gap));
|
|
312
|
+
combined += seg.text;
|
|
313
|
+
lastEndCol = seg.startCol + seg.text.length;
|
|
314
|
+
if (seg.startRow !== null) lastRow = seg.startRow;
|
|
315
|
+
} else if (seg.startCol !== null && !rowChanged) {
|
|
316
|
+
combined += seg.text;
|
|
317
|
+
lastEndCol = seg.startCol + seg.text.length;
|
|
318
|
+
if (seg.startRow !== null) lastRow = seg.startRow;
|
|
319
|
+
} else if (rowChanged || seg.newLine) {
|
|
320
|
+
if (seg.startCol !== null && lastEndCol !== null && seg.startCol === lastEndCol) {
|
|
321
|
+
combined += seg.text;
|
|
322
|
+
} else {
|
|
323
|
+
combined = combined.trimEnd() + ' ' + seg.text;
|
|
324
|
+
}
|
|
325
|
+
if (seg.startCol !== null) {
|
|
326
|
+
lastEndCol = seg.startCol + seg.text.length;
|
|
327
|
+
} else if (seg.estimatedCol !== null) {
|
|
328
|
+
lastEndCol = seg.estimatedCol + seg.text.length;
|
|
329
|
+
} else {
|
|
330
|
+
lastEndCol = null;
|
|
331
|
+
}
|
|
332
|
+
if (seg.startRow !== null) lastRow = seg.startRow;
|
|
333
|
+
} else {
|
|
334
|
+
combined += seg.text;
|
|
335
|
+
if (lastEndCol !== null) {
|
|
336
|
+
lastEndCol += seg.text.length;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
combined = combined.replace(/ {2,}/g, ' ').trim();
|
|
216
341
|
return combined.length > 0 ? combined : null;
|
|
217
342
|
}
|
|
218
343
|
|
|
@@ -264,14 +389,16 @@ export function extractResponse(stripped, rawSection, promptFilter = '', effecti
|
|
|
264
389
|
if (meaningful.length === 0) return null;
|
|
265
390
|
|
|
266
391
|
let best = meaningful.join('\n').trim();
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
.join('\n')
|
|
272
|
-
.trim();
|
|
392
|
+
const echoFilter = effectiveFilter || promptFilter;
|
|
393
|
+
if (echoFilter) {
|
|
394
|
+
const cleaned = stripPromptEcho(best, echoFilter);
|
|
395
|
+
if (cleaned !== null) best = cleaned || '';
|
|
273
396
|
}
|
|
274
|
-
|
|
397
|
+
if (promptFilter && promptFilter !== echoFilter) {
|
|
398
|
+
const cleaned = stripPromptEcho(best, promptFilter);
|
|
399
|
+
if (cleaned !== null) best = cleaned || '';
|
|
400
|
+
}
|
|
401
|
+
return best.trim() || null;
|
|
275
402
|
}
|
|
276
403
|
|
|
277
404
|
// ---------- CLI Main ----------
|
|
@@ -362,7 +489,7 @@ if (isMainModule()) {
|
|
|
362
489
|
jsonOutput = true;
|
|
363
490
|
} else if (arg === '--sandbox') {
|
|
364
491
|
permissionMode = 'sandbox';
|
|
365
|
-
} else if (arg === '--skip-permissions') {
|
|
492
|
+
} else if (arg === '--skip-permissions' || arg === '--dangerously-skip-permissions') {
|
|
366
493
|
permissionMode = 'skip-permissions';
|
|
367
494
|
} else if (arg === '--no-tools') {
|
|
368
495
|
permissionMode = 'no-tools';
|
|
@@ -370,6 +497,16 @@ if (isMainModule()) {
|
|
|
370
497
|
permissionMode = 'researcher';
|
|
371
498
|
} else if (arg === '--read-only') {
|
|
372
499
|
permissionMode = 'read-only';
|
|
500
|
+
} else if (arg === '--print-timeout' && rawArgs[i + 1]) {
|
|
501
|
+
const ms = parseDurationToMs(rawArgs[++i]);
|
|
502
|
+
if (ms !== null) timeoutMs = ms;
|
|
503
|
+
} else if (arg.startsWith('--print-timeout=')) {
|
|
504
|
+
const ms = parseDurationToMs(arg.slice(16));
|
|
505
|
+
if (ms !== null) timeoutMs = ms;
|
|
506
|
+
} else if (arg === '-p' || arg === '--print' || arg === '--prompt') {
|
|
507
|
+
// Ignored: agy-companion runs in interactive mode internally to capture PTY/ANSI
|
|
508
|
+
} else if (arg === '-i' || arg === '--prompt-interactive') {
|
|
509
|
+
// Ignored: agy-companion runs interactive by default
|
|
373
510
|
} else if (arg === '--allow' && rawArgs[i + 1]) {
|
|
374
511
|
customAllow.push(rawArgs[++i]);
|
|
375
512
|
} else if (arg === '--deny' && rawArgs[i + 1]) {
|
|
@@ -396,6 +533,9 @@ if (isMainModule()) {
|
|
|
396
533
|
const tempWorkspace = path.join(os.tmpdir(), `agy-companion-${process.pid}`);
|
|
397
534
|
let tempSettingsCreated = false;
|
|
398
535
|
|
|
536
|
+
// Clean stale workspace from a previous crashed run with same PID
|
|
537
|
+
try { fs.rmSync(tempWorkspace, { recursive: true, force: true }); } catch (_) {}
|
|
538
|
+
|
|
399
539
|
if (allAllow.length > 0 || allDeny.length > 0) {
|
|
400
540
|
const geminiDir = path.join(tempWorkspace, '.gemini');
|
|
401
541
|
fs.mkdirSync(geminiDir, { recursive: true });
|
|
@@ -491,7 +631,7 @@ if (isMainModule()) {
|
|
|
491
631
|
cols: 220,
|
|
492
632
|
rows: 50,
|
|
493
633
|
cwd: tempWorkspace,
|
|
494
|
-
env: { ...process.env, TERM: 'xterm-256color' },
|
|
634
|
+
env: { ...process.env, TERM: 'xterm-256color', COLORTERM: 'truecolor' },
|
|
495
635
|
});
|
|
496
636
|
|
|
497
637
|
let rawBuffer = '';
|
|
@@ -514,11 +654,7 @@ if (isMainModule()) {
|
|
|
514
654
|
|
|
515
655
|
function cleanupTemp() {
|
|
516
656
|
try {
|
|
517
|
-
|
|
518
|
-
fs.rmSync(tempWorkspace, { recursive: true, force: true });
|
|
519
|
-
} else {
|
|
520
|
-
fs.rmdirSync(tempWorkspace);
|
|
521
|
-
}
|
|
657
|
+
fs.rmSync(tempWorkspace, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
|
522
658
|
} catch (_) {}
|
|
523
659
|
}
|
|
524
660
|
|
|
@@ -539,8 +675,11 @@ if (isMainModule()) {
|
|
|
539
675
|
process.stderr.write(`[agy-companion] Debug log: ${debugPath}\n`);
|
|
540
676
|
}
|
|
541
677
|
|
|
542
|
-
|
|
543
|
-
|
|
678
|
+
// Windows holds a CWD lock on tempWorkspace until child processes fully terminate
|
|
679
|
+
setTimeout(() => {
|
|
680
|
+
cleanupTemp();
|
|
681
|
+
process.exit(code);
|
|
682
|
+
}, 1000);
|
|
544
683
|
}, 500);
|
|
545
684
|
}
|
|
546
685
|
|
|
@@ -599,6 +738,12 @@ if (isMainModule()) {
|
|
|
599
738
|
}
|
|
600
739
|
}
|
|
601
740
|
|
|
741
|
+
if (LOGIN_PROMPT_PATTERN.test(recentStripped)) {
|
|
742
|
+
process.stderr.write(`[agy-companion] agy is not signed in. Please run 'agy' manually and complete sign-in first.\n`);
|
|
743
|
+
shutdown(3);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
|
|
602
747
|
if (!trustHandled && TRUST_DIALOG_PATTERN.test(recentStripped)) {
|
|
603
748
|
trustHandled = true;
|
|
604
749
|
process.stderr.write(`[agy-companion] Trust dialog detected. Auto-confirming...\n`);
|