@aibridge/cli 0.0.1
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/LICENSE +21 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +6 -0
- package/dist/context-BLjTHa41.mjs +1529 -0
- package/dist/index.d.mts +184 -0
- package/dist/index.mjs +2 -0
- package/package.json +53 -0
- package/src/app.exit-code.test.ts +91 -0
- package/src/app.ts +49 -0
- package/src/cli.ts +5 -0
- package/src/commands/image-gen/command.ts +77 -0
- package/src/commands/image-gen/impl.ts +268 -0
- package/src/commands/implement/command.ts +50 -0
- package/src/commands/implement/impl.ts +99 -0
- package/src/commands/plan/command.ts +56 -0
- package/src/commands/plan/impl.ts +172 -0
- package/src/commands/plan/plan.test.ts +19 -0
- package/src/commands/quota/command.ts +30 -0
- package/src/commands/quota/impl.ts +109 -0
- package/src/commands/review/command.ts +58 -0
- package/src/commands/review/impl.ts +211 -0
- package/src/commands/review/review.test.ts +54 -0
- package/src/commands/runs/command.ts +53 -0
- package/src/commands/runs/impl.ts +171 -0
- package/src/commands/subagent/command.ts +62 -0
- package/src/commands/subagent/impl.ts +87 -0
- package/src/context.ts +10 -0
- package/src/delegate.test.ts +180 -0
- package/src/delegate.ts +46 -0
- package/src/driver.ts +56 -0
- package/src/drivers.ts +44 -0
- package/src/exitCode.test.ts +44 -0
- package/src/exitCode.ts +24 -0
- package/src/flagMapping.test.ts +99 -0
- package/src/index.ts +37 -0
- package/src/models.test.ts +107 -0
- package/src/models.ts +159 -0
- package/src/parsers.ts +24 -0
- package/src/quotaPreflight.test.ts +178 -0
- package/src/quotaPreflight.ts +103 -0
- package/src/runlog.ts +195 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { CommandContext } from "@stricli/core";
|
|
2
|
+
import { AgyQuotaSnapshot } from "@aibridge/agy";
|
|
3
|
+
import { ClaudeQuotaSnapshot } from "@aibridge/claude";
|
|
4
|
+
import { CodexQuotaSnapshot } from "@aibridge/codex";
|
|
5
|
+
//#region src/context.d.ts
|
|
6
|
+
interface LocalContext extends CommandContext {
|
|
7
|
+
/** Full Node process — satisfies stricli WritableStreams + exitCode/env/cwd used by impls. */
|
|
8
|
+
readonly process: NodeJS.Process;
|
|
9
|
+
}
|
|
10
|
+
declare function buildContext(process: NodeJS.Process): LocalContext;
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/app.d.ts
|
|
13
|
+
declare const app: import("@stricli/core").Application<LocalContext>;
|
|
14
|
+
/** Public entry used by cli.ts and index.ts — preserves runCli(ctx, argv) surface. */
|
|
15
|
+
declare function runCli(ctx: LocalContext, argv: readonly string[]): Promise<void>;
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/models.d.ts
|
|
18
|
+
/**
|
|
19
|
+
* Canonical model registry and resolution for aibridge.
|
|
20
|
+
*
|
|
21
|
+
* Models are registered by canonical, provider-qualified slug —
|
|
22
|
+
* `<vendor>-<cli>/<model>[-<effort>]`, e.g. `openai-codex/gpt-5.6-sol-high`.
|
|
23
|
+
* Canonical slugs only — no short aliases, by design.
|
|
24
|
+
*/
|
|
25
|
+
type Backend = 'agy' | 'claude' | 'codex' | 'grok';
|
|
26
|
+
type Effort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
27
|
+
interface ModelSpec {
|
|
28
|
+
readonly slug: string;
|
|
29
|
+
readonly backend: Backend;
|
|
30
|
+
readonly backendModel: string | undefined;
|
|
31
|
+
readonly efforts: readonly Effort[] | null;
|
|
32
|
+
readonly defaultEffort?: Effort;
|
|
33
|
+
readonly brief: string;
|
|
34
|
+
}
|
|
35
|
+
interface ResolvedModel {
|
|
36
|
+
readonly spec: ModelSpec;
|
|
37
|
+
readonly effort: Effort | undefined;
|
|
38
|
+
}
|
|
39
|
+
declare const MODELS: Record<string, ModelSpec>;
|
|
40
|
+
declare const DEFAULT_MODEL = "xai-grok/grok-4.5";
|
|
41
|
+
declare const DEFAULT_IMPLEMENTER = "google-antigravity/gemini-3.6-flash";
|
|
42
|
+
declare const DEFAULT_IMAGE_GEN = "openai-codex/gpt-5.6-sol";
|
|
43
|
+
declare function supportsImageGen(resolved: ResolvedModel): boolean;
|
|
44
|
+
declare function resolveModel(input: string): ResolvedModel | undefined;
|
|
45
|
+
declare function backendModelId(resolved: ResolvedModel): string | undefined;
|
|
46
|
+
declare function listModelHelpLines(opts?: {
|
|
47
|
+
readonly imageOnly?: boolean;
|
|
48
|
+
}): string[];
|
|
49
|
+
declare function formatUnknownModelError(input: string): string;
|
|
50
|
+
declare function formatImageGenModelError(input: string, resolved: ResolvedModel): string;
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/driver.d.ts
|
|
53
|
+
type Availability = {
|
|
54
|
+
readonly ok: true;
|
|
55
|
+
readonly version: string;
|
|
56
|
+
} | {
|
|
57
|
+
readonly ok: false;
|
|
58
|
+
readonly error: string;
|
|
59
|
+
};
|
|
60
|
+
interface DelegationTask {
|
|
61
|
+
readonly prompt: string;
|
|
62
|
+
readonly tools: boolean;
|
|
63
|
+
readonly timeoutSec: number;
|
|
64
|
+
readonly cwd: string;
|
|
65
|
+
readonly backendModel: string | undefined;
|
|
66
|
+
readonly effort: Effort | undefined;
|
|
67
|
+
readonly onStdout?: (chunk: string) => void;
|
|
68
|
+
readonly onStderr?: (chunk: string) => void;
|
|
69
|
+
readonly onSpawn?: (pid: number) => void;
|
|
70
|
+
}
|
|
71
|
+
type DelegationResult = {
|
|
72
|
+
readonly ok: true;
|
|
73
|
+
readonly response: string;
|
|
74
|
+
readonly exitCode: number;
|
|
75
|
+
} | {
|
|
76
|
+
readonly ok: false;
|
|
77
|
+
readonly kind: 'not-found' | 'spawn' | 'timeout' | 'no-answer';
|
|
78
|
+
readonly message: string;
|
|
79
|
+
readonly exitCode: number | null;
|
|
80
|
+
};
|
|
81
|
+
type QuotaSnapshot = AgyQuotaSnapshot | CodexQuotaSnapshot | ClaudeQuotaSnapshot;
|
|
82
|
+
interface ImageGenRequest {
|
|
83
|
+
readonly prompt: string;
|
|
84
|
+
readonly workDir: string;
|
|
85
|
+
readonly backendModel: string | undefined;
|
|
86
|
+
readonly effort: Effort | undefined;
|
|
87
|
+
readonly quality: string;
|
|
88
|
+
readonly size: {
|
|
89
|
+
readonly w: number;
|
|
90
|
+
readonly h: number;
|
|
91
|
+
} | undefined;
|
|
92
|
+
readonly imagePaths: readonly string[];
|
|
93
|
+
readonly timeoutSec: number;
|
|
94
|
+
readonly forceful: boolean;
|
|
95
|
+
readonly minBytes: number;
|
|
96
|
+
}
|
|
97
|
+
type ImageResult = {
|
|
98
|
+
readonly kind: 'ok';
|
|
99
|
+
readonly path: string;
|
|
100
|
+
readonly bytes: number;
|
|
101
|
+
} | {
|
|
102
|
+
readonly kind: 'suspect';
|
|
103
|
+
} | {
|
|
104
|
+
readonly kind: 'error';
|
|
105
|
+
readonly reason: string;
|
|
106
|
+
};
|
|
107
|
+
interface AgentCliDriver {
|
|
108
|
+
probe(): Promise<Availability>;
|
|
109
|
+
run(task: DelegationTask): Promise<DelegationResult>;
|
|
110
|
+
quota?(): Promise<QuotaSnapshot>;
|
|
111
|
+
generateImage?(req: ImageGenRequest): Promise<ImageResult>;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/runlog.d.ts
|
|
115
|
+
interface RunMeta {
|
|
116
|
+
readonly id: string;
|
|
117
|
+
readonly command: string;
|
|
118
|
+
readonly detail: string;
|
|
119
|
+
pid: number | null;
|
|
120
|
+
readonly startedAt: string;
|
|
121
|
+
endedAt: string | null;
|
|
122
|
+
status: 'running' | 'done' | 'error' | 'timeout' | 'stale';
|
|
123
|
+
exitCode: number | null;
|
|
124
|
+
}
|
|
125
|
+
interface RunLog {
|
|
126
|
+
readonly id: string;
|
|
127
|
+
readonly dir: string;
|
|
128
|
+
setPid(pid: number): void;
|
|
129
|
+
stdout(chunk: string): void;
|
|
130
|
+
stderr(chunk: string): void;
|
|
131
|
+
finish(status: 'done' | 'error' | 'timeout', exitCode: number | null): void;
|
|
132
|
+
}
|
|
133
|
+
declare function startRun(command: string, detail: string): RunLog;
|
|
134
|
+
declare function listRuns(): RunMeta[];
|
|
135
|
+
declare function readRunLogs(id: string): {
|
|
136
|
+
meta: RunMeta;
|
|
137
|
+
stdout: string;
|
|
138
|
+
stderr: string;
|
|
139
|
+
} | null;
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/delegate.d.ts
|
|
142
|
+
interface DelegateOptions {
|
|
143
|
+
readonly model: ResolvedModel;
|
|
144
|
+
readonly prompt: string;
|
|
145
|
+
readonly tools: boolean;
|
|
146
|
+
readonly timeoutSec: number;
|
|
147
|
+
readonly cwd: string;
|
|
148
|
+
readonly run: RunLog;
|
|
149
|
+
}
|
|
150
|
+
type DelegateOutcome = DelegationResult;
|
|
151
|
+
declare function delegate(opts: DelegateOptions, driver?: AgentCliDriver): Promise<DelegateOutcome>;
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/drivers.d.ts
|
|
154
|
+
declare function getDriver(backend: Backend): AgentCliDriver;
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/parsers.d.ts
|
|
157
|
+
/**
|
|
158
|
+
* stricli `parse` functions (string -> T). Throwing inside one makes stricli
|
|
159
|
+
* reject the argument up-front with a clean, flag-named error — instead of
|
|
160
|
+
* silently letting a bad value (NaN, Infinity, "") flow into an impl where it
|
|
161
|
+
* gets masked or, worse, breaks `setTimeout`.
|
|
162
|
+
*/
|
|
163
|
+
declare function positiveIntSeconds(input: string): number;
|
|
164
|
+
declare function nonEmptyPrompt(input: string): string;
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/quotaPreflight.d.ts
|
|
167
|
+
type PreflightVerdict = {
|
|
168
|
+
readonly ok: true;
|
|
169
|
+
readonly warning?: string;
|
|
170
|
+
} | {
|
|
171
|
+
readonly ok: false;
|
|
172
|
+
readonly message: string;
|
|
173
|
+
readonly resetAt: string | undefined;
|
|
174
|
+
};
|
|
175
|
+
declare function evaluateAgyPreflight(snapshot: AgyQuotaSnapshot, backendModel: string): PreflightVerdict;
|
|
176
|
+
declare function evaluateCodexPreflight(snapshot: CodexQuotaSnapshot): PreflightVerdict;
|
|
177
|
+
declare function preflightModel(resolved: ResolvedModel): Promise<PreflightVerdict>;
|
|
178
|
+
declare function preflightCodex(): Promise<PreflightVerdict>;
|
|
179
|
+
declare function renderPreflightRefusal(cmd: string, verdict: {
|
|
180
|
+
message: string;
|
|
181
|
+
resetAt: string | undefined;
|
|
182
|
+
}): string;
|
|
183
|
+
//#endregion
|
|
184
|
+
export { type AgentCliDriver, type Backend, DEFAULT_IMAGE_GEN, DEFAULT_IMPLEMENTER, DEFAULT_MODEL, type DelegateOptions, type DelegateOutcome, type DelegationResult, type DelegationTask, type Effort, type LocalContext, MODELS, type ModelSpec, type PreflightVerdict, type ResolvedModel, type RunLog, type RunMeta, app, backendModelId, buildContext, delegate, evaluateAgyPreflight, evaluateCodexPreflight, formatImageGenModelError, formatUnknownModelError, getDriver, listModelHelpLines, listRuns, nonEmptyPrompt, positiveIntSeconds, preflightCodex, preflightModel, readRunLogs, renderPreflightRefusal, resolveModel, runCli, startRun, supportsImageGen };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as listModelHelpLines, S as formatUnknownModelError, T as supportsImageGen, _ as DEFAULT_IMPLEMENTER, a as readRunLogs, b as backendModelId, c as evaluateCodexPreflight, d as renderPreflightRefusal, f as delegate, g as DEFAULT_IMAGE_GEN, h as positiveIntSeconds, i as listRuns, l as preflightCodex, m as nonEmptyPrompt, n as app, o as startRun, p as getDriver, r as runCli, s as evaluateAgyPreflight, t as buildContext, u as preflightModel, v as DEFAULT_MODEL, w as resolveModel, x as formatImageGenModelError, y as MODELS } from "./context-BLjTHa41.mjs";
|
|
2
|
+
export { DEFAULT_IMAGE_GEN, DEFAULT_IMPLEMENTER, DEFAULT_MODEL, MODELS, app, backendModelId, buildContext, delegate, evaluateAgyPreflight, evaluateCodexPreflight, formatImageGenModelError, formatUnknownModelError, getDriver, listModelHelpLines, listRuns, nonEmptyPrompt, positiveIntSeconds, preflightCodex, preflightModel, readRunLogs, renderPreflightRefusal, resolveModel, runCli, startRun, supportsImageGen };
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aibridge/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI that bridges tasks to non-Claude AI CLIs (plan / implement / review / subagent / image-gen)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=24.11"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"aibridge",
|
|
12
|
+
"cli"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/fishballapp/aibridge.git",
|
|
17
|
+
"directory": "packages/cli"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/fishballapp/aibridge#readme",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/fishballapp/aibridge/issues"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.mts",
|
|
26
|
+
"import": "./dist/index.mjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"aibridge": "./dist/cli.mjs"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"src"
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@stricli/core": "1.3.0",
|
|
38
|
+
"@aibridge/proc": "0.0.1",
|
|
39
|
+
"@aibridge/codex": "0.0.1",
|
|
40
|
+
"@aibridge/claude": "0.0.1",
|
|
41
|
+
"@aibridge/grok": "0.0.1",
|
|
42
|
+
"@aibridge/agy": "0.0.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"tsdown": "0.22.14"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsdown"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { runCli } from './app.ts';
|
|
3
|
+
import type { LocalContext } from './context.ts';
|
|
4
|
+
|
|
5
|
+
function fakeCtx(): LocalContext & { _stdout: string[]; _stderr: string[] } {
|
|
6
|
+
const _stdout: string[] = [];
|
|
7
|
+
const _stderr: string[] = [];
|
|
8
|
+
const processLike = {
|
|
9
|
+
stdout: {
|
|
10
|
+
write: (s: string) => {
|
|
11
|
+
_stdout.push(s);
|
|
12
|
+
return true;
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
stderr: {
|
|
16
|
+
write: (s: string) => {
|
|
17
|
+
_stderr.push(s);
|
|
18
|
+
return true;
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
exitCode: undefined as number | undefined,
|
|
22
|
+
env: { ...process.env, NO_COLOR: '1', STRICLI_NO_COLOR: '1' },
|
|
23
|
+
cwd: () => process.cwd(),
|
|
24
|
+
};
|
|
25
|
+
return { process: processLike as unknown as NodeJS.Process, _stdout, _stderr };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('stricli exit-code lock & routing', () => {
|
|
29
|
+
it('unknown command → 2', async () => {
|
|
30
|
+
const ctx = fakeCtx();
|
|
31
|
+
await runCli(ctx, ['nonsense']);
|
|
32
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('unknown flag → 2', async () => {
|
|
36
|
+
const ctx = fakeCtx();
|
|
37
|
+
await runCli(ctx, ['plan', '--not-a-flag', 'x']);
|
|
38
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('missing required arg → 2', async () => {
|
|
42
|
+
const ctx = fakeCtx();
|
|
43
|
+
await runCli(ctx, ['plan']);
|
|
44
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('empty prompt → 2', async () => {
|
|
48
|
+
const ctx = fakeCtx();
|
|
49
|
+
await runCli(ctx, ['plan', '']);
|
|
50
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('review with stray positional → 2', async () => {
|
|
54
|
+
const ctx = fakeCtx();
|
|
55
|
+
await runCli(ctx, ['review', 'stray']);
|
|
56
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('runs --watch with idPrefix → 2', async () => {
|
|
60
|
+
const ctx = fakeCtx();
|
|
61
|
+
await runCli(ctx, ['runs', '--watch', 'someid']);
|
|
62
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('runs --watch with --json → 2', async () => {
|
|
66
|
+
const ctx = fakeCtx();
|
|
67
|
+
await runCli(ctx, ['runs', '--watch', '--json']);
|
|
68
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('runs --no-json → 2', async () => {
|
|
72
|
+
const ctx = fakeCtx();
|
|
73
|
+
await runCli(ctx, ['runs', '--no-json']);
|
|
74
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('runs --no-watch → 2', async () => {
|
|
78
|
+
const ctx = fakeCtx();
|
|
79
|
+
await runCli(ctx, ['runs', '--no-watch']);
|
|
80
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('root --help lists all commands', async () => {
|
|
84
|
+
const ctx = fakeCtx();
|
|
85
|
+
await runCli(ctx, ['--help']);
|
|
86
|
+
const output = ctx._stdout.join('');
|
|
87
|
+
for (const cmd of ['plan', 'implement', 'review', 'subagent', 'image-gen', 'runs', 'quota']) {
|
|
88
|
+
expect(output).toContain(cmd);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
package/src/app.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { buildApplication, buildRouteMap, run } from '@stricli/core';
|
|
3
|
+
import { imageGen } from './commands/image-gen/command.ts';
|
|
4
|
+
import { implement } from './commands/implement/command.ts';
|
|
5
|
+
import { plan } from './commands/plan/command.ts';
|
|
6
|
+
import { quota } from './commands/quota/command.ts';
|
|
7
|
+
import { review } from './commands/review/command.ts';
|
|
8
|
+
import { runs } from './commands/runs/command.ts';
|
|
9
|
+
import { subagent } from './commands/subagent/command.ts';
|
|
10
|
+
import type { LocalContext } from './context.ts';
|
|
11
|
+
import { normalizeExitCode } from './exitCode.ts';
|
|
12
|
+
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
const { version } = require('../package.json') as { version: string };
|
|
15
|
+
|
|
16
|
+
const BRIEF =
|
|
17
|
+
'Bridge tasks to non-Claude AI CLIs — a plan → implement → review workflow, task delegation, and image generation (codex gpt-image-2 / grok Imagine).';
|
|
18
|
+
|
|
19
|
+
const routes = buildRouteMap({
|
|
20
|
+
routes: {
|
|
21
|
+
plan,
|
|
22
|
+
implement,
|
|
23
|
+
review,
|
|
24
|
+
subagent,
|
|
25
|
+
'image-gen': imageGen,
|
|
26
|
+
runs,
|
|
27
|
+
quota,
|
|
28
|
+
},
|
|
29
|
+
docs: {
|
|
30
|
+
brief: BRIEF,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const app = buildApplication(routes, {
|
|
35
|
+
name: 'aibridge',
|
|
36
|
+
versionInfo: {
|
|
37
|
+
currentVersion: version,
|
|
38
|
+
},
|
|
39
|
+
scanner: {
|
|
40
|
+
// Accept --no-preflight / --no-tools while flag keys stay camelCase in TS
|
|
41
|
+
caseStyle: 'allow-kebab-for-camel',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/** Public entry used by cli.ts and index.ts — preserves runCli(ctx, argv) surface. */
|
|
46
|
+
export async function runCli(ctx: LocalContext, argv: readonly string[]): Promise<void> {
|
|
47
|
+
await run(app, argv, ctx);
|
|
48
|
+
normalizeExitCode(ctx);
|
|
49
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { buildCommand } from '@stricli/core';
|
|
2
|
+
import { DEFAULT_IMAGE_GEN, listModelHelpLines } from '../../models.ts';
|
|
3
|
+
import { nonEmptyPrompt, positiveIntSeconds } from '../../parsers.ts';
|
|
4
|
+
import imageGenImpl from './impl.ts';
|
|
5
|
+
|
|
6
|
+
const fullDescription = [
|
|
7
|
+
"Renders an image by driving the seat's CLI (codex → gpt-image-2, grok →",
|
|
8
|
+
'Imagine), then verifies the result is a real render before returning it.',
|
|
9
|
+
'',
|
|
10
|
+
'Image-gen seats (canonical slug):',
|
|
11
|
+
...listModelHelpLines({ imageOnly: true }),
|
|
12
|
+
`Default: ${DEFAULT_IMAGE_GEN} (gpt-image-2 via codex; historical default).`,
|
|
13
|
+
].join('\n');
|
|
14
|
+
|
|
15
|
+
export const imageGen = buildCommand({
|
|
16
|
+
func: imageGenImpl,
|
|
17
|
+
parameters: {
|
|
18
|
+
flags: {
|
|
19
|
+
model: {
|
|
20
|
+
kind: 'parsed',
|
|
21
|
+
parse: String,
|
|
22
|
+
optional: true,
|
|
23
|
+
brief: `Model slug (default: ${DEFAULT_IMAGE_GEN})`,
|
|
24
|
+
},
|
|
25
|
+
out: {
|
|
26
|
+
kind: 'parsed',
|
|
27
|
+
parse: String,
|
|
28
|
+
optional: true,
|
|
29
|
+
brief: 'Path to write the image (default: ./aibridge-image.png)',
|
|
30
|
+
},
|
|
31
|
+
size: {
|
|
32
|
+
kind: 'parsed',
|
|
33
|
+
parse: String,
|
|
34
|
+
optional: true,
|
|
35
|
+
brief:
|
|
36
|
+
'WIDTHxHEIGHT (codex: each edge ÷16; grok: mapped to aspect_ratio, then optionally resized)',
|
|
37
|
+
},
|
|
38
|
+
image: {
|
|
39
|
+
kind: 'parsed',
|
|
40
|
+
parse: String,
|
|
41
|
+
optional: true,
|
|
42
|
+
brief: 'Reference image path(s), comma-separated — visual reference',
|
|
43
|
+
},
|
|
44
|
+
quality: {
|
|
45
|
+
kind: 'parsed',
|
|
46
|
+
parse: String,
|
|
47
|
+
optional: true,
|
|
48
|
+
brief: 'low | medium | high (codex/gpt-image-2; default high)',
|
|
49
|
+
},
|
|
50
|
+
timeout: {
|
|
51
|
+
kind: 'parsed',
|
|
52
|
+
parse: positiveIntSeconds,
|
|
53
|
+
optional: true,
|
|
54
|
+
brief: 'Max seconds to wait for the render (default: 600)',
|
|
55
|
+
},
|
|
56
|
+
json: {
|
|
57
|
+
kind: 'boolean',
|
|
58
|
+
withNegated: false,
|
|
59
|
+
brief: 'Emit a machine-readable JSON result instead of prose',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
positional: {
|
|
63
|
+
kind: 'tuple',
|
|
64
|
+
parameters: [
|
|
65
|
+
{
|
|
66
|
+
brief: 'Description of the image to generate',
|
|
67
|
+
parse: nonEmptyPrompt,
|
|
68
|
+
placeholder: 'prompt',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
docs: {
|
|
74
|
+
brief: 'Generate a raster image via a model seat',
|
|
75
|
+
fullDescription,
|
|
76
|
+
},
|
|
77
|
+
});
|