This commit was merged in pull request #806.
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
import { readRegularFileSecure } from '../fleet/secure-file.js';
|
||||
import { readPersonaContractBlock } from '../fleet/persona-contract.js';
|
||||
import { canonicalizeRoleClass } from './fleet-personas.js';
|
||||
import { launchClaudex, type ClaudexHarnessAdapter } from './claudex.js';
|
||||
|
||||
const MOSAIC_HOME = process.env['MOSAIC_HOME'] ?? join(homedir(), '.config', 'mosaic');
|
||||
const MAX_INSTALLED_TOOLS_BYTES = 256 * 1024;
|
||||
@@ -806,12 +807,12 @@ function launchRuntime(runtime: RuntimeName, args: string[], yolo: boolean): nev
|
||||
}
|
||||
|
||||
/** exec into the runtime, replacing the current process. */
|
||||
function execRuntime(cmd: string, args: string[]): void {
|
||||
function execRuntime(cmd: string, args: string[], env: NodeJS.ProcessEnv = process.env): void {
|
||||
try {
|
||||
// Use execFileSync with inherited stdio to replace the process
|
||||
const result = spawnSync(cmd, args, {
|
||||
stdio: 'inherit',
|
||||
env: process.env,
|
||||
env,
|
||||
});
|
||||
process.exit(result.status ?? 0);
|
||||
} catch (err) {
|
||||
@@ -820,6 +821,29 @@ function execRuntime(cmd: string, args: string[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Production glue for `mosaic [yolo] claudex` (EXPERIMENTAL — GPT models inside
|
||||
* the Claude Code harness via claude-code-proxy). Assembles the real harness
|
||||
* adapter and delegates the security-critical composition + fail-closed
|
||||
* orchestration to `launchClaudex` in `claudex.ts`. Kept thin so the tested
|
||||
* logic lives in the DI module, not here.
|
||||
*/
|
||||
function launchClaudexProduction(args: string[], yolo: boolean): void {
|
||||
writeSessionLock('claude');
|
||||
const adapter: ClaudexHarnessAdapter = {
|
||||
harnessPreflight: () => {
|
||||
checkMosaicHome();
|
||||
checkFile(join(MOSAIC_HOME, 'AGENTS.md'), 'AGENTS.md');
|
||||
checkSoul();
|
||||
checkRuntime('claude');
|
||||
checkSequentialThinking('claude');
|
||||
},
|
||||
composePrompt: () => buildRuntimePrompt('claude'),
|
||||
exec: (cmd, cmdArgs, env) => execRuntime(cmd, cmdArgs, env),
|
||||
};
|
||||
void launchClaudex(args, yolo, adapter);
|
||||
}
|
||||
|
||||
// ─── Framework script/tool delegation ───────────────────────────────────────
|
||||
|
||||
function delegateToScript(scriptPath: string, args: string[], env?: Record<string, string>): never {
|
||||
@@ -1034,12 +1058,25 @@ export type RuntimeLaunchHandler = (
|
||||
yolo: boolean,
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Handler invoked for `claudex` / `yolo claudex`. Kept separate from
|
||||
* `RuntimeLaunchHandler` because claudex is an EXPERIMENTAL harness overlay
|
||||
* (GPT-via-proxy), not one of the first-class runtimes. Exposed + injectable so
|
||||
* the commander wiring can be exercised without composing a real launch.
|
||||
*/
|
||||
export type ClaudexLaunchHandler = (extraArgs: string[], yolo: boolean) => void;
|
||||
|
||||
/**
|
||||
* Wire `<runtime>` and `yolo <runtime>` subcommands onto `program` using a
|
||||
* pluggable launch handler. Separated from `registerLaunchCommands` so tests
|
||||
* can inject a spy and verify argument forwarding.
|
||||
*/
|
||||
export function registerRuntimeLaunchers(program: Command, handler: RuntimeLaunchHandler): void {
|
||||
export function registerRuntimeLaunchers(
|
||||
program: Command,
|
||||
handler: RuntimeLaunchHandler,
|
||||
claudexHandler: ClaudexLaunchHandler = (extraArgs, yolo) =>
|
||||
launchClaudexProduction(extraArgs, yolo),
|
||||
): void {
|
||||
for (const runtime of ['claude', 'codex', 'opencode', 'pi'] as const) {
|
||||
program
|
||||
.command(runtime)
|
||||
@@ -1051,16 +1088,37 @@ export function registerRuntimeLaunchers(program: Command, handler: RuntimeLaunc
|
||||
});
|
||||
}
|
||||
|
||||
// claudex — EXPERIMENTAL: GPT models inside the Claude Code harness via
|
||||
// claude-code-proxy (ChatGPT-subscription OAuth). Isolated CLAUDE_CONFIG_DIR
|
||||
// + zero-token-leak env injection live in claudex.ts.
|
||||
program
|
||||
.command('claudex')
|
||||
.description('EXPERIMENTAL: launch Claude Code harness against GPT via claude-code-proxy')
|
||||
.allowUnknownOption(true)
|
||||
.allowExcessArguments(true)
|
||||
.action((_opts: unknown, cmd: Command) => {
|
||||
claudexHandler(cmd.args, false);
|
||||
});
|
||||
|
||||
program
|
||||
.command('yolo <runtime>')
|
||||
.description('Launch a runtime in dangerous-permissions mode (claude|codex|opencode|pi)')
|
||||
.description(
|
||||
'Launch a runtime in dangerous-permissions mode (claude|codex|opencode|pi|claudex)',
|
||||
)
|
||||
.allowUnknownOption(true)
|
||||
.allowExcessArguments(true)
|
||||
.action((runtime: string, _opts: unknown, cmd: Command) => {
|
||||
// claudex is an EXPERIMENTAL overlay, not a RuntimeName — dispatch it
|
||||
// before the runtime allowlist check. Slice off the positional runtime
|
||||
// name for the same reason as below (#454).
|
||||
if (runtime === 'claudex') {
|
||||
claudexHandler(cmd.args.slice(1), true);
|
||||
return;
|
||||
}
|
||||
const valid: RuntimeName[] = ['claude', 'codex', 'opencode', 'pi'];
|
||||
if (!valid.includes(runtime as RuntimeName)) {
|
||||
console.error(
|
||||
`[mosaic] ERROR: Unsupported yolo runtime '${runtime}'. Use: ${valid.join('|')}`,
|
||||
`[mosaic] ERROR: Unsupported yolo runtime '${runtime}'. Use: ${valid.join('|')}|claudex`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user