@openagents-org/agent-launcher 0.2.171 → 0.2.173
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/package.json +2 -2
- package/registry.json +49 -2
- package/src/adapters/antigravity-stream.js +242 -0
- package/src/adapters/antigravity.js +390 -0
- package/src/adapters/base.js +13 -0
- package/src/adapters/claude.js +10 -2
- package/src/adapters/index.js +4 -1
- package/src/cli.js +33 -0
- package/src/daemon.js +116 -1
- package/src/index.js +9 -0
- package/src/probe.js +318 -0
- package/src/registry.js +2 -0
- package/src/workspace-client.js +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openagents-org/agent-launcher",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "OpenAgents Launcher
|
|
3
|
+
"version": "0.2.173",
|
|
4
|
+
"description": "OpenAgents Launcher — install, configure, and run AI coding agents from your terminal",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"agn": "bin/agent-connector.js",
|
package/registry.json
CHANGED
|
@@ -152,6 +152,7 @@
|
|
|
152
152
|
"login_command": "claude login",
|
|
153
153
|
"alt_check": "claude --print hi"
|
|
154
154
|
},
|
|
155
|
+
"probe": { "args": ["--print", "hi"], "timeout_s": 120 },
|
|
155
156
|
"env_config": []
|
|
156
157
|
},
|
|
157
158
|
{
|
|
@@ -417,7 +418,7 @@
|
|
|
417
418
|
{
|
|
418
419
|
"name": "gemini",
|
|
419
420
|
"label": "Gemini CLI",
|
|
420
|
-
"description": "Google's
|
|
421
|
+
"description": "Google's Gemini CLI (legacy — superseded by Antigravity CLI; needs a Gemini API key or Code Assist license since June 2026)",
|
|
421
422
|
"homepage": "https://github.com/google-gemini/gemini-cli",
|
|
422
423
|
"tags": [
|
|
423
424
|
"coding",
|
|
@@ -462,13 +463,59 @@
|
|
|
462
463
|
],
|
|
463
464
|
"saved_env_key": "GEMINI_API_KEY",
|
|
464
465
|
"login_command": "gemini",
|
|
465
|
-
"not_ready_message": "
|
|
466
|
+
"not_ready_message": "Set GEMINI_API_KEY (Google ended individual sign-in for Gemini CLI in June 2026), or run `gemini` if your org has a Code Assist license.",
|
|
466
467
|
"unreadable_message": "Gemini credentials were found but could not be read. Check permissions on ~/.gemini, or run `gemini` to sign in again.",
|
|
467
468
|
"auth_detected_labels": {
|
|
468
469
|
"cli_login": "Google account sign-in detected",
|
|
469
470
|
"api_key": "API key detected"
|
|
470
471
|
}
|
|
471
472
|
},
|
|
473
|
+
"probe": { "args": ["-p", "hi"], "timeout_s": 120 },
|
|
474
|
+
"env_config": []
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"name": "antigravity",
|
|
478
|
+
"label": "Antigravity CLI",
|
|
479
|
+
"description": "Google's agent-first CLI (agy) — the successor to Gemini CLI",
|
|
480
|
+
"homepage": "https://antigravity.google",
|
|
481
|
+
"tags": [
|
|
482
|
+
"coding",
|
|
483
|
+
"google",
|
|
484
|
+
"cli"
|
|
485
|
+
],
|
|
486
|
+
"builtin": true,
|
|
487
|
+
"support": { "install": true, "workspace": true, "collaboration": true },
|
|
488
|
+
"install": {
|
|
489
|
+
"binary": "agy",
|
|
490
|
+
"macos": "curl -fsSL https://antigravity.google/cli/install.sh | bash",
|
|
491
|
+
"linux": "curl -fsSL https://antigravity.google/cli/install.sh | bash",
|
|
492
|
+
"windows": "irm https://antigravity.google/cli/install.ps1 | iex"
|
|
493
|
+
},
|
|
494
|
+
"adapter": {
|
|
495
|
+
"module": "openagents.adapters.antigravity",
|
|
496
|
+
"class": "AntigravityAdapter"
|
|
497
|
+
},
|
|
498
|
+
"launch": {
|
|
499
|
+
"args": [
|
|
500
|
+
"-p",
|
|
501
|
+
"Your agent name is '{agent_name}'.",
|
|
502
|
+
"--dangerously-skip-permissions",
|
|
503
|
+
"--output-format",
|
|
504
|
+
"stream-json"
|
|
505
|
+
]
|
|
506
|
+
},
|
|
507
|
+
"check_ready": {
|
|
508
|
+
"env_vars": [
|
|
509
|
+
"GEMINI_API_KEY"
|
|
510
|
+
],
|
|
511
|
+
"saved_env_key": "GEMINI_API_KEY",
|
|
512
|
+
"login_command": "agy",
|
|
513
|
+
"not_ready_message": "Needs sign-in — run `agy` to sign in with Google, or set GEMINI_API_KEY.",
|
|
514
|
+
"auth_detected_labels": {
|
|
515
|
+
"cli_login": "Google account sign-in detected",
|
|
516
|
+
"api_key": "API key detected"
|
|
517
|
+
}
|
|
518
|
+
},
|
|
472
519
|
"env_config": []
|
|
473
520
|
},
|
|
474
521
|
{
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pure helpers for the Antigravity CLI (agy) adapter — argv construction,
|
|
5
|
+
* stream-json event interpretation, and failure classification. No I/O and no
|
|
6
|
+
* process state, so every branch is unit-testable (see
|
|
7
|
+
* test/antigravity-stream.test.js).
|
|
8
|
+
*
|
|
9
|
+
* agy's headless protocol (`--output-format stream-json`) is NDJSON with an
|
|
10
|
+
* `event` field per line: exactly one `init`, any number of `step_update`,
|
|
11
|
+
* exactly one `result`. That differs from the old Gemini CLI stream (`type`
|
|
12
|
+
* field, `message`/`tool_use` events), which is why this is a new module
|
|
13
|
+
* rather than a tweak to the gemini adapter.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Build the argv tail (everything after the binary) for one headless run. */
|
|
17
|
+
function buildAgyArgv({ prompt, model, conversationId, skipResume = false } = {}) {
|
|
18
|
+
const args = [
|
|
19
|
+
'-p', prompt || '',
|
|
20
|
+
// Counterpart of gemini's `-y`: headless runs have no interactive
|
|
21
|
+
// approval prompt, and a soft-denied tool call silently degrades the
|
|
22
|
+
// answer. The daemon is the approval boundary here, same as every other
|
|
23
|
+
// CLI adapter.
|
|
24
|
+
'--dangerously-skip-permissions',
|
|
25
|
+
'--output-format', 'stream-json',
|
|
26
|
+
];
|
|
27
|
+
if (model) args.push('--model', model);
|
|
28
|
+
if (conversationId && !skipResume) args.push('--conversation', conversationId);
|
|
29
|
+
return args;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Parse one NDJSON line. Returns the event object, or null for non-events. */
|
|
33
|
+
function parseAgyEvent(line) {
|
|
34
|
+
const trimmed = (line || '').trim();
|
|
35
|
+
if (!trimmed) return null;
|
|
36
|
+
let obj;
|
|
37
|
+
try { obj = JSON.parse(trimmed); } catch { return null; }
|
|
38
|
+
if (!obj || typeof obj !== 'object' || typeof obj.event !== 'string') return null;
|
|
39
|
+
return obj;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One-line preview of a tool call's parameters for the status ticker. */
|
|
43
|
+
function toolPreview(parameters) {
|
|
44
|
+
if (!parameters || typeof parameters !== 'object') return '';
|
|
45
|
+
// agy uses PascalCase parameter names (e.g. CommandLine); check both.
|
|
46
|
+
const pick = (...names) => {
|
|
47
|
+
for (const n of names) {
|
|
48
|
+
const v = parameters[n];
|
|
49
|
+
if (typeof v === 'string' && v) return v;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
};
|
|
53
|
+
return (
|
|
54
|
+
pick('CommandLine', 'command') ||
|
|
55
|
+
pick('AbsolutePath', 'file_path', 'path') ||
|
|
56
|
+
pick('Query', 'query', 'pattern') ||
|
|
57
|
+
JSON.stringify(parameters).slice(0, 150)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Reducer over one run's event stream. `consume(event)` returns display
|
|
63
|
+
* actions for the adapter to translate into workspace calls:
|
|
64
|
+
*
|
|
65
|
+
* {type:'conversation', id} — persist for --conversation resume
|
|
66
|
+
* {type:'thinking', text} — a finished agent_response block
|
|
67
|
+
* {type:'tool', name, preview} — a tool step began
|
|
68
|
+
* {type:'result', status, response, error}
|
|
69
|
+
*
|
|
70
|
+
* `finalResponse()` afterwards yields the text to post: the result envelope's
|
|
71
|
+
* `response` when present (authoritative), else the last completed
|
|
72
|
+
* agent_response block — mirroring the gemini adapter's "text after the last
|
|
73
|
+
* tool use" semantics for streams that die before their result event.
|
|
74
|
+
*/
|
|
75
|
+
class AgyRunState {
|
|
76
|
+
constructor() {
|
|
77
|
+
this.conversationId = null;
|
|
78
|
+
this.status = null;
|
|
79
|
+
this.error = null;
|
|
80
|
+
this._resultResponse = '';
|
|
81
|
+
this._blocks = []; // completed agent_response texts since last tool
|
|
82
|
+
this._pending = ''; // text_delta accumulator for the ACTIVE block
|
|
83
|
+
this._toolSteps = new Set(); // step_index values already announced
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
consume(event) {
|
|
87
|
+
if (!event || typeof event !== 'object') return [];
|
|
88
|
+
const actions = [];
|
|
89
|
+
|
|
90
|
+
if (event.event === 'init') {
|
|
91
|
+
if (event.conversation_id) {
|
|
92
|
+
this.conversationId = event.conversation_id;
|
|
93
|
+
actions.push({ type: 'conversation', id: event.conversation_id });
|
|
94
|
+
}
|
|
95
|
+
return actions;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (event.event === 'step_update') {
|
|
99
|
+
const step = event.step_update || {};
|
|
100
|
+
if (step.step_type === 'agent_response') {
|
|
101
|
+
if (typeof step.text_delta === 'string') this._pending += step.text_delta;
|
|
102
|
+
if (step.state === 'DONE') {
|
|
103
|
+
const text = this._pending.trim();
|
|
104
|
+
this._pending = '';
|
|
105
|
+
if (text) {
|
|
106
|
+
this._blocks.push(text);
|
|
107
|
+
actions.push({ type: 'thinking', text });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} else if (step.step_type === 'tool') {
|
|
111
|
+
// Announce each tool step once, on first sight — agy repeats the step
|
|
112
|
+
// across ACTIVE/DONE updates.
|
|
113
|
+
const key = step.step_index != null ? step.step_index : `t${this._toolSteps.size}`;
|
|
114
|
+
if (!this._toolSteps.has(key)) {
|
|
115
|
+
this._toolSteps.add(key);
|
|
116
|
+
const info = step.tool_info || {};
|
|
117
|
+
const name = info.name || step.tool_name || 'tool';
|
|
118
|
+
actions.push({ type: 'tool', name, preview: toolPreview(info.parameters) });
|
|
119
|
+
// A tool ran: any text before it was narration, not the answer.
|
|
120
|
+
this._blocks = [];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return actions;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (event.event === 'result') {
|
|
127
|
+
const result = event.result || {};
|
|
128
|
+
if (result.conversation_id && !this.conversationId) {
|
|
129
|
+
this.conversationId = result.conversation_id;
|
|
130
|
+
actions.push({ type: 'conversation', id: result.conversation_id });
|
|
131
|
+
}
|
|
132
|
+
this.status = result.status || null;
|
|
133
|
+
this.error = result.error || null;
|
|
134
|
+
if (typeof result.response === 'string') this._resultResponse = result.response;
|
|
135
|
+
actions.push({
|
|
136
|
+
type: 'result',
|
|
137
|
+
status: this.status,
|
|
138
|
+
response: this._resultResponse,
|
|
139
|
+
error: this.error,
|
|
140
|
+
});
|
|
141
|
+
return actions;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return actions;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** True once the stream produced any user-visible text. */
|
|
148
|
+
sawText() {
|
|
149
|
+
return this._blocks.length > 0 || !!this._resultResponse.trim();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
finalResponse() {
|
|
153
|
+
const fromResult = this._resultResponse.trim();
|
|
154
|
+
if (fromResult) return fromResult;
|
|
155
|
+
// Stream died before its result event — fall back to the accumulated
|
|
156
|
+
// agent_response text (plus any half-flushed delta).
|
|
157
|
+
return [...this._blocks, this._pending.trim()].filter(Boolean).join('\n\n').trim();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const AUTH_RE = /authentication (required|failed)|not authenticated|sign[ -]?in|log[ -]?in required|no valid credentials|GEMINI_API_KEY/i;
|
|
162
|
+
const PROVIDER_RE = /modelProvider|model provider/i;
|
|
163
|
+
const MODEL_RE = /unknown model|invalid model/i;
|
|
164
|
+
const TIMEOUT_RE = /print-timeout|timed? ?out/i;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Turn a failed run (non-zero exit, or a result event with status ERROR) into
|
|
168
|
+
* a { kind, message } the adapter can post verbatim. This is the fix for the
|
|
169
|
+
* "No response generated. Please try again." dead end: the real reason was in
|
|
170
|
+
* stderr all along, so surface it.
|
|
171
|
+
*/
|
|
172
|
+
function classifyAgyFailure({ code, stderr, error } = {}) {
|
|
173
|
+
// The docs describe `result.error` as {type, message}; agy 1.1.17 actually
|
|
174
|
+
// emits a plain string ("authentication failed or timed out"). Take both.
|
|
175
|
+
const err = error && typeof error === 'object' ? error : {};
|
|
176
|
+
const errText =
|
|
177
|
+
typeof error === 'string' ? error : [err.type, err.message].filter(Boolean).join(' ');
|
|
178
|
+
const text = [errText, stderr].filter(Boolean).join('\n');
|
|
179
|
+
|
|
180
|
+
if (AUTH_RE.test(text)) {
|
|
181
|
+
return {
|
|
182
|
+
kind: 'auth',
|
|
183
|
+
message:
|
|
184
|
+
'Antigravity CLI needs authentication. Run `agy` once in a terminal to ' +
|
|
185
|
+
'sign in with Google, or set GEMINI_API_KEY for this agent (the ' +
|
|
186
|
+
'connector configures the provider automatically).',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (PROVIDER_RE.test(text)) {
|
|
190
|
+
return {
|
|
191
|
+
kind: 'config',
|
|
192
|
+
message:
|
|
193
|
+
'Antigravity CLI is configured for API-key auth but no key is set. ' +
|
|
194
|
+
'Set GEMINI_API_KEY for this agent, or remove "modelProvider" from ' +
|
|
195
|
+
'~/.gemini/antigravity-cli/settings.json to use Google sign-in.',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (MODEL_RE.test(text)) {
|
|
199
|
+
return {
|
|
200
|
+
kind: 'model',
|
|
201
|
+
message:
|
|
202
|
+
'Antigravity CLI rejected the configured model. Check ANTIGRAVITY_MODEL ' +
|
|
203
|
+
'(list valid slugs with `agy models`).',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (TIMEOUT_RE.test(text)) {
|
|
207
|
+
return { kind: 'timeout', message: 'Antigravity CLI timed out before producing a response.' };
|
|
208
|
+
}
|
|
209
|
+
const detail = (errText || (stderr || '').trim().split('\n').pop() || '').slice(0, 200);
|
|
210
|
+
return {
|
|
211
|
+
kind: 'unknown',
|
|
212
|
+
message: detail
|
|
213
|
+
? `Antigravity CLI failed (exit ${code ?? '?'}): ${detail}`
|
|
214
|
+
: `Antigravity CLI exited with code ${code ?? '?'} without a response.`,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Filesystem candidates for the agy binary, beyond a PATH lookup. The install
|
|
220
|
+
* script targets ~/.local/bin on macOS/Linux and %LOCALAPPDATA%\agy\bin on
|
|
221
|
+
* Windows. Pure: the adapter applies fs.existsSync.
|
|
222
|
+
*/
|
|
223
|
+
function agyBinaryCandidates({ home, isWindows, localAppData } = {}) {
|
|
224
|
+
if (isWindows) {
|
|
225
|
+
const base = localAppData || '';
|
|
226
|
+
return base ? [`${base}\\agy\\bin\\agy.exe`] : [];
|
|
227
|
+
}
|
|
228
|
+
return [
|
|
229
|
+
`${home}/.local/bin/agy`,
|
|
230
|
+
'/opt/homebrew/bin/agy',
|
|
231
|
+
'/usr/local/bin/agy',
|
|
232
|
+
];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = {
|
|
236
|
+
buildAgyArgv,
|
|
237
|
+
parseAgyEvent,
|
|
238
|
+
toolPreview,
|
|
239
|
+
AgyRunState,
|
|
240
|
+
classifyAgyFailure,
|
|
241
|
+
agyBinaryCandidates,
|
|
242
|
+
};
|