mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-07-06 18:52:03 +08:00
13a37def89
Make the agent provider a first-class, operator-chosen property instead of a Claude-only assumption. Trunk gains the seams; the actual non-default payloads (Codex first) install from the `providers` branch. Setup - A provider registry feeds a hard-wired setup picker (Claude | Codex). Picking a non-default provider installs its payload (setup/add-codex.sh, channel-style), runs a vault-only auth walkthrough (--step provider-auth), and records the pick on the first agent before its first spawn. - Picking Claude changes nothing — default installs are byte-for-byte unaffected. Provider as a DB property - Provider lives on container_configs.provider (materialized to container.json, read by resolveProviderName). Creation stays provider-agnostic; the picked provider is applied via the picked-provider seam. The deprecated agent_groups.agent_provider path is not used. Switching + memory - Switch a live group with `ncl groups config update --provider` + restart. - Memory never migrates at runtime — each provider keeps its own store. The /migrate-memory skill carries a group's memory across a switch in either direction (flat CLAUDE.local.md <-> memory/ scaffold). group-init seeds an imported-agent-memory note for non-default providers; the runner's memory definition reads it first turn. See docs/provider-migration.md. No install-wide default, no runtime provider guard — switching is operator-by- convention, consistent with the no-install-gating posture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
81 lines
3.1 KiB
TypeScript
81 lines
3.1 KiB
TypeScript
/**
|
|
* Standalone provider auth — the late-adopter entry point.
|
|
*
|
|
* Fresh installs reach a provider's auth walk-through via the setup picker;
|
|
* an existing install adding a provider later runs THIS instead:
|
|
*
|
|
* pnpm exec tsx setup/index.ts --step provider-auth codex
|
|
*
|
|
* Same walk-through, same vault-only invariant, idempotent (each provider's
|
|
* runAuth short-circuits when its secret already exists) — and unlike
|
|
* re-running full setup, it touches nothing else: no install-wide default
|
|
* provider rewrite, no service changes. Provider install skills call this as
|
|
* their auth step so there is exactly one auth implementation per provider.
|
|
*/
|
|
import { execSync } from 'child_process';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
import { getSetupProvider, listSetupProviders } from './providers/registry.js';
|
|
// Provider payloads self-register on import.
|
|
import './providers/index.js';
|
|
|
|
// Hard-wired install scripts — the audited control surface (no branch
|
|
// enumeration). Each setup/add-<name>.sh is idempotent and self-skips when the
|
|
// payload is already wired. Codex is the only manifest-style provider today.
|
|
const INSTALL_SCRIPTS: Record<string, string> = {
|
|
codex: 'setup/add-codex.sh',
|
|
};
|
|
|
|
export async function run(args: string[]): Promise<void> {
|
|
const name = args[0]?.trim().toLowerCase();
|
|
const withAuth = listSetupProviders().filter((entry) => entry.runAuth);
|
|
|
|
if (!name) {
|
|
console.error(
|
|
`Usage: pnpm exec tsx setup/index.ts --step provider-auth <provider>\n` +
|
|
`Providers with an auth step: ${withAuth.map((entry) => entry.value).join(', ') || '(none installed)'}`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
let entry = getSetupProvider(name);
|
|
const script = INSTALL_SCRIPTS[name];
|
|
if (script) {
|
|
// Install OR refresh: the script is idempotent and is also the upgrade
|
|
// path — payload files resync and a bumped Dockerfile pin replaces the
|
|
// local one. Rebuild the image only when the Dockerfile actually changed
|
|
// (payload code is mounted, not baked).
|
|
const dfPath = path.join(process.cwd(), 'container', 'Dockerfile');
|
|
const dfBefore = fs.readFileSync(dfPath, 'utf-8');
|
|
console.log(`${entry ? 'Refreshing' : 'Installing'} ${name}…`);
|
|
execSync(`bash ${script}`, { stdio: 'inherit' });
|
|
if (fs.readFileSync(dfPath, 'utf-8') !== dfBefore) {
|
|
console.log('Dockerfile pin changed — rebuilding the container image…');
|
|
execSync('./container/build.sh', { stdio: 'inherit' });
|
|
}
|
|
if (!entry) {
|
|
await import(`./providers/${name}.js`);
|
|
entry = getSetupProvider(name);
|
|
}
|
|
if (!entry) {
|
|
console.error(`Install completed but ${name} did not register — check setup/providers/${name}.ts`);
|
|
process.exit(1);
|
|
}
|
|
} else if (!entry) {
|
|
console.error(
|
|
`Unknown provider: ${name}. Installed: ${listSetupProviders()
|
|
.map((e) => e.value)
|
|
.join(', ')}.`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
if (!entry.runAuth) {
|
|
console.error(`Provider "${name}" uses the standard auth flow — run the full setup, or /add-${name}'s steps.`);
|
|
process.exit(1);
|
|
}
|
|
|
|
await entry.runAuth();
|
|
await entry.runInstallCheck?.();
|
|
}
|