@aibridge/cli 0.2.0 → 0.4.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/README.md +40 -9
- package/dist/cli.mjs +1 -1
- package/dist/{context-DQxvMUd2.mjs → context-v_ns4n_W.mjs} +186 -48
- package/dist/index.d.mts +4 -6
- package/dist/index.mjs +2 -2
- package/package.json +6 -6
- package/src/app.ts +3 -1
- package/src/commands/image-gen/command.ts +3 -4
- package/src/commands/image-gen/impl.ts +3 -4
- package/src/commands/implement/command.ts +2 -3
- package/src/commands/implement/impl.ts +3 -3
- package/src/commands/models/command.ts +18 -0
- package/src/commands/models/impl.test.ts +89 -0
- package/src/commands/models/impl.ts +71 -0
- package/src/commands/plan/command.ts +3 -5
- package/src/commands/plan/impl.ts +5 -9
- package/src/commands/quota/command.ts +3 -1
- package/src/commands/quota/impl.ts +24 -2
- package/src/commands/review/command.ts +3 -5
- package/src/commands/review/impl.ts +5 -9
- package/src/commands/subagent/command.ts +4 -5
- package/src/commands/subagent/impl.ts +3 -8
- package/src/driver.ts +6 -1
- package/src/drivers.ts +1 -0
- package/src/flagMapping.test.ts +27 -4
- package/src/index.ts +0 -3
- package/src/models.ts +48 -19
- package/src/quotaPreflight.test.ts +46 -1
- package/src/quotaPreflight.ts +29 -0
package/src/index.ts
CHANGED
package/src/models.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Models are registered by canonical, provider-qualified slug —
|
|
5
5
|
* `<vendor>-<cli>/<model>[-<effort>]`, e.g. `openai-codex/gpt-5.6-sol-high`.
|
|
6
6
|
* Canonical slugs only — no short aliases, by design. That holds on both sides:
|
|
7
|
-
* `backendModel` is a pinned model id (`claude-
|
|
7
|
+
* `backendModel` is a pinned model id (`claude-sonnet-5`), never a moving vendor
|
|
8
8
|
* alias (`opus`), so a seat never silently changes model under you.
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -31,7 +31,7 @@ export const MODELS: Record<string, ModelSpec> = {
|
|
|
31
31
|
backend: 'grok',
|
|
32
32
|
backendModel: 'grok-4.5',
|
|
33
33
|
efforts: ['low', 'medium', 'high'],
|
|
34
|
-
brief: 'xAI Grok 4.5 via grok CLI —
|
|
34
|
+
brief: 'xAI Grok 4.5 via grok CLI — own xAI login; ~30 req/min, ~1k msgs/day, single-flight',
|
|
35
35
|
},
|
|
36
36
|
'google-antigravity/gemini-3.6-flash': {
|
|
37
37
|
slug: 'google-antigravity/gemini-3.6-flash',
|
|
@@ -40,7 +40,17 @@ export const MODELS: Record<string, ModelSpec> = {
|
|
|
40
40
|
efforts: ['low', 'medium', 'high'],
|
|
41
41
|
defaultEffort: 'high',
|
|
42
42
|
brief:
|
|
43
|
-
'Google Gemini 3.6 Flash via agy —
|
|
43
|
+
'Google Gemini 3.6 Flash via agy — own Antigravity login; quota shared across all Gemini tiers',
|
|
44
|
+
},
|
|
45
|
+
'google-antigravity/gemini-3.1-pro': {
|
|
46
|
+
slug: 'google-antigravity/gemini-3.1-pro',
|
|
47
|
+
backend: 'agy',
|
|
48
|
+
// agy exposes only -high and -low for this class — there is no medium tier.
|
|
49
|
+
efforts: ['low', 'high'],
|
|
50
|
+
backendModel: 'gemini-3.1-pro',
|
|
51
|
+
defaultEffort: 'high',
|
|
52
|
+
brief:
|
|
53
|
+
'Google Gemini 3.1 Pro via agy — own Antigravity login; quota shared across all Gemini tiers',
|
|
44
54
|
},
|
|
45
55
|
'google-antigravity/claude-sonnet-4-6': {
|
|
46
56
|
slug: 'google-antigravity/claude-sonnet-4-6',
|
|
@@ -68,37 +78,56 @@ export const MODELS: Record<string, ModelSpec> = {
|
|
|
68
78
|
backend: 'codex',
|
|
69
79
|
backendModel: 'gpt-5.6-sol',
|
|
70
80
|
efforts: ['low', 'medium', 'high', 'xhigh'],
|
|
71
|
-
brief: 'OpenAI
|
|
81
|
+
brief: 'OpenAI gpt-5.6-sol via codex CLI — frontier agentic coding; own ChatGPT login',
|
|
72
82
|
},
|
|
73
|
-
'
|
|
74
|
-
slug: '
|
|
83
|
+
'openai-codex/gpt-5.6-terra': {
|
|
84
|
+
slug: 'openai-codex/gpt-5.6-terra',
|
|
85
|
+
backend: 'codex',
|
|
86
|
+
backendModel: 'gpt-5.6-terra',
|
|
87
|
+
efforts: ['low', 'medium', 'high', 'xhigh'],
|
|
88
|
+
brief: 'OpenAI gpt-5.6-terra via codex CLI — balanced, everyday coding; own ChatGPT login',
|
|
89
|
+
},
|
|
90
|
+
'openai-codex/gpt-5.6-luna': {
|
|
91
|
+
slug: 'openai-codex/gpt-5.6-luna',
|
|
92
|
+
backend: 'codex',
|
|
93
|
+
backendModel: 'gpt-5.6-luna',
|
|
94
|
+
efforts: ['low', 'medium', 'high', 'xhigh'],
|
|
95
|
+
brief: 'OpenAI gpt-5.6-luna via codex CLI — fast and affordable coding; own ChatGPT login',
|
|
96
|
+
},
|
|
97
|
+
'anthropic-claude/fable-5': {
|
|
98
|
+
slug: 'anthropic-claude/fable-5',
|
|
75
99
|
backend: 'claude',
|
|
76
|
-
backendModel: 'claude-
|
|
100
|
+
backendModel: 'claude-fable-5',
|
|
77
101
|
efforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
78
|
-
|
|
102
|
+
defaultEffort: 'high',
|
|
103
|
+
brief:
|
|
104
|
+
'Claude Fable 5 via claude CLI — hardest, longest-running work; bills the claude CLI subscription',
|
|
79
105
|
},
|
|
80
106
|
'anthropic-claude/opus-5': {
|
|
81
107
|
slug: 'anthropic-claude/opus-5',
|
|
82
108
|
backend: 'claude',
|
|
83
|
-
backendModel: 'claude-opus-5',
|
|
109
|
+
backendModel: 'claude-opus-5[1m]',
|
|
84
110
|
efforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
85
111
|
defaultEffort: 'high',
|
|
86
|
-
brief:
|
|
112
|
+
brief:
|
|
113
|
+
'Claude Opus 5, 1M context via claude CLI — everyday complex work; bills the claude CLI subscription',
|
|
87
114
|
},
|
|
88
|
-
'anthropic-claude/
|
|
89
|
-
slug: 'anthropic-claude/
|
|
115
|
+
'anthropic-claude/sonnet-5': {
|
|
116
|
+
slug: 'anthropic-claude/sonnet-5',
|
|
90
117
|
backend: 'claude',
|
|
91
|
-
backendModel: 'claude-
|
|
118
|
+
backendModel: 'claude-sonnet-5',
|
|
92
119
|
efforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
brief: 'Claude Sonnet 5 via claude CLI — routine work; bills the claude CLI subscription',
|
|
121
|
+
},
|
|
122
|
+
'anthropic-claude/haiku-4-5': {
|
|
123
|
+
slug: 'anthropic-claude/haiku-4-5',
|
|
124
|
+
backend: 'claude',
|
|
125
|
+
backendModel: 'claude-haiku-4-5-20251001',
|
|
126
|
+
efforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
127
|
+
brief: 'Claude Haiku 4.5 via claude CLI — quick answers; bills the claude CLI subscription',
|
|
95
128
|
},
|
|
96
129
|
} as const satisfies Record<string, ModelSpec>;
|
|
97
130
|
|
|
98
|
-
export const DEFAULT_MODEL = 'xai-grok/grok-4.5';
|
|
99
|
-
export const DEFAULT_IMPLEMENTER = 'google-antigravity/gemini-3.6-flash';
|
|
100
|
-
export const DEFAULT_IMAGE_GEN = 'openai-codex/gpt-5.6-sol';
|
|
101
|
-
|
|
102
131
|
export type ImageFormat = 'jpg' | 'png';
|
|
103
132
|
|
|
104
133
|
const IMAGE_GEN_FORMATS: ReadonlyMap<Backend, ImageFormat> = new Map([
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import type { AgyQuotaSnapshot } from '@aibridge/driver-agy';
|
|
3
3
|
import type { CodexQuotaSnapshot } from '@aibridge/driver-codex';
|
|
4
|
+
import type { GrokQuotaSnapshot } from '@aibridge/driver-grok';
|
|
4
5
|
import { test } from 'vitest';
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
evaluateAgyPreflight,
|
|
8
|
+
evaluateCodexPreflight,
|
|
9
|
+
evaluateGrokPreflight,
|
|
10
|
+
} from './quotaPreflight.ts';
|
|
6
11
|
|
|
7
12
|
test('evaluateAgyPreflight: exhausted model returns ok:false with resetTime', () => {
|
|
8
13
|
const snapshot: AgyQuotaSnapshot = {
|
|
@@ -176,3 +181,43 @@ test('evaluateCodexPreflight: healthy (77% used) returns ok:true', () => {
|
|
|
176
181
|
|
|
177
182
|
assert.deepEqual(result, { ok: true });
|
|
178
183
|
});
|
|
184
|
+
|
|
185
|
+
test('evaluateGrokPreflight: usedPercent at 100 refuses with resetAt', () => {
|
|
186
|
+
const snapshot: GrokQuotaSnapshot = {
|
|
187
|
+
fetchedAt: '2024-01-01T12:00:00Z',
|
|
188
|
+
periodType: 'weekly',
|
|
189
|
+
periodStart: '2024-01-01T00:00:00Z',
|
|
190
|
+
periodEnd: '2024-01-08T00:00:00Z',
|
|
191
|
+
usedPercent: 100,
|
|
192
|
+
products: [],
|
|
193
|
+
onDemandUsedCents: undefined,
|
|
194
|
+
onDemandCapCents: undefined,
|
|
195
|
+
prepaidBalanceCents: undefined,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const result = evaluateGrokPreflight(snapshot);
|
|
199
|
+
|
|
200
|
+
assert.deepEqual(result, {
|
|
201
|
+
ok: false,
|
|
202
|
+
message: 'grok credit quota exhausted',
|
|
203
|
+
resetAt: '2024-01-08T00:00:00Z',
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('evaluateGrokPreflight: healthy (17% used) returns ok:true', () => {
|
|
208
|
+
const snapshot: GrokQuotaSnapshot = {
|
|
209
|
+
fetchedAt: '2024-01-01T12:00:00Z',
|
|
210
|
+
periodType: 'weekly',
|
|
211
|
+
periodStart: '2024-01-01T00:00:00Z',
|
|
212
|
+
periodEnd: '2024-01-08T00:00:00Z',
|
|
213
|
+
usedPercent: 17,
|
|
214
|
+
products: [],
|
|
215
|
+
onDemandUsedCents: undefined,
|
|
216
|
+
onDemandCapCents: undefined,
|
|
217
|
+
prepaidBalanceCents: undefined,
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const result = evaluateGrokPreflight(snapshot);
|
|
221
|
+
|
|
222
|
+
assert.deepEqual(result, { ok: true });
|
|
223
|
+
});
|
package/src/quotaPreflight.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AgyQuotaSnapshot, fetchAgyQuota, findModelQuota } from '@aibridge/driver-agy';
|
|
2
2
|
import { type CodexQuotaSnapshot, fetchCodexQuota } from '@aibridge/driver-codex';
|
|
3
|
+
import { fetchGrokQuota, type GrokQuotaSnapshot } from '@aibridge/driver-grok';
|
|
3
4
|
import { backendModelId, type ResolvedModel } from './models.ts';
|
|
4
5
|
|
|
5
6
|
export type PreflightVerdict =
|
|
@@ -51,11 +52,27 @@ export function evaluateCodexPreflight(snapshot: CodexQuotaSnapshot): PreflightV
|
|
|
51
52
|
return { ok: true };
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
export function evaluateGrokPreflight(snapshot: GrokQuotaSnapshot): PreflightVerdict {
|
|
56
|
+
if (snapshot.usedPercent !== undefined && snapshot.usedPercent >= 100) {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
message: 'grok credit quota exhausted',
|
|
60
|
+
resetAt: snapshot.periodEnd,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { ok: true };
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
export async function preflightModel(resolved: ResolvedModel): Promise<PreflightVerdict> {
|
|
55
68
|
if (resolved.spec.backend === 'codex') {
|
|
56
69
|
return preflightCodex();
|
|
57
70
|
}
|
|
58
71
|
|
|
72
|
+
if (resolved.spec.backend === 'grok') {
|
|
73
|
+
return preflightGrok();
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
if (resolved.spec.backend !== 'agy') {
|
|
60
77
|
return { ok: true };
|
|
61
78
|
}
|
|
@@ -83,6 +100,18 @@ export async function preflightCodex(): Promise<PreflightVerdict> {
|
|
|
83
100
|
}
|
|
84
101
|
}
|
|
85
102
|
|
|
103
|
+
export async function preflightGrok(): Promise<PreflightVerdict> {
|
|
104
|
+
try {
|
|
105
|
+
const snapshot = await fetchGrokQuota();
|
|
106
|
+
return evaluateGrokPreflight(snapshot);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
return {
|
|
109
|
+
ok: true,
|
|
110
|
+
warning: `quota preflight failed (${(err as Error).message}); proceeding`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
86
115
|
function formatReset(resetTime: string | undefined): string {
|
|
87
116
|
if (!resetTime) return '-';
|
|
88
117
|
const ms = new Date(resetTime).getTime() - Date.now();
|