mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
feat: wire gbrain auth subcommand with OAuth register-client
Previously auth.ts was a standalone script invoked via
`bun run src/commands/auth.ts`. CHANGELOG and README documented
`gbrain auth ...` commands that didn't actually work.
- Export `runAuth(args)` from auth.ts (keeps standalone entry intact
via `import.meta.url === file://${process.argv[1]}` check)
- Add `auth` to CLI_ONLY + dispatch in handleCliOnly
- New subcommand `gbrain auth register-client <name> [--grant-types]
[--scopes]` wraps GBrainOAuthProvider.registerClientManual
- Lazy DB check: only subcommands that need DATABASE_URL error out
Now the documented CLI flow works end to end:
gbrain auth register-client perplexity --grant-types client_credentials --scopes "read write"
gbrain serve --http --port 3131
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
5b0e61196a
commit
c4a86cec89
+6
-1
@@ -19,7 +19,7 @@ for (const op of operations) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CLI-only commands that bypass the operation layer
|
// CLI-only commands that bypass the operation layer
|
||||||
const CLI_ONLY = new Set(['init', 'upgrade', 'post-upgrade', 'check-update', 'integrations', 'publish', 'check-backlinks', 'lint', 'report', 'import', 'export', 'files', 'embed', 'serve', 'call', 'config', 'doctor', 'migrate', 'eval', 'sync', 'extract', 'features', 'autopilot', 'graph-query', 'jobs', 'agent', 'apply-migrations', 'skillpack-check', 'resolvers', 'integrity', 'repair-jsonb', 'orphans', 'sources', 'dream', 'check-resolvable']);
|
const CLI_ONLY = new Set(['init', 'upgrade', 'post-upgrade', 'check-update', 'integrations', 'publish', 'check-backlinks', 'lint', 'report', 'import', 'export', 'files', 'embed', 'serve', 'call', 'config', 'doctor', 'migrate', 'eval', 'sync', 'extract', 'features', 'autopilot', 'graph-query', 'jobs', 'agent', 'apply-migrations', 'skillpack-check', 'resolvers', 'integrity', 'repair-jsonb', 'orphans', 'sources', 'dream', 'check-resolvable', 'auth']);
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
// Parse global flags (--quiet / --progress-json / --progress-interval)
|
// Parse global flags (--quiet / --progress-json / --progress-interval)
|
||||||
@@ -265,6 +265,11 @@ async function handleCliOnly(command: string, args: string[]) {
|
|||||||
await runInit(args);
|
await runInit(args);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (command === 'auth') {
|
||||||
|
const { runAuth } = await import('./commands/auth.ts');
|
||||||
|
await runAuth(args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (command === 'upgrade') {
|
if (command === 'upgrade') {
|
||||||
const { runUpgrade } = await import('./commands/upgrade.ts');
|
const { runUpgrade } = await import('./commands/upgrade.ts');
|
||||||
await runUpgrade(args);
|
await runUpgrade(args);
|
||||||
|
|||||||
+76
-21
@@ -12,9 +12,15 @@ import postgres from 'postgres';
|
|||||||
import { createHash, randomBytes } from 'crypto';
|
import { createHash, randomBytes } from 'crypto';
|
||||||
|
|
||||||
const DATABASE_URL = process.env.DATABASE_URL || process.env.GBRAIN_DATABASE_URL;
|
const DATABASE_URL = process.env.DATABASE_URL || process.env.GBRAIN_DATABASE_URL;
|
||||||
if (!DATABASE_URL && process.argv[2] !== 'test') {
|
|
||||||
console.error('Set DATABASE_URL or GBRAIN_DATABASE_URL environment variable.');
|
/** Subcommands that need a DB connection. `test` (smoke test) does not. */
|
||||||
process.exit(1);
|
const NEEDS_DB = new Set(['create', 'list', 'revoke', 'register-client']);
|
||||||
|
|
||||||
|
function ensureDatabaseUrl(cmd: string | undefined) {
|
||||||
|
if (cmd && NEEDS_DB.has(cmd) && !DATABASE_URL) {
|
||||||
|
console.error('Set DATABASE_URL or GBRAIN_DATABASE_URL environment variable.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashToken(token: string): string {
|
function hashToken(token: string): string {
|
||||||
@@ -216,26 +222,75 @@ async function test(url: string, token: string) {
|
|||||||
console.log(`\n🧠 Your brain is live! (${elapsed}s)`);
|
console.log(`\n🧠 Your brain is live! (${elapsed}s)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLI dispatch
|
async function registerClient(name: string, args: string[]) {
|
||||||
const [cmd, ...args] = process.argv.slice(2);
|
if (!name) { console.error('Usage: auth register-client <name> [--grant-types G] [--scopes S]'); process.exit(1); }
|
||||||
switch (cmd) {
|
const grantsIdx = args.indexOf('--grant-types');
|
||||||
case 'create': await create(args[0]); break;
|
const scopesIdx = args.indexOf('--scopes');
|
||||||
case 'list': await list(); break;
|
const grantTypes = grantsIdx >= 0 && args[grantsIdx + 1]
|
||||||
case 'revoke': await revoke(args[0]); break;
|
? args[grantsIdx + 1].split(',').map(s => s.trim()).filter(Boolean)
|
||||||
case 'test': {
|
: ['client_credentials'];
|
||||||
const tokenIdx = args.indexOf('--token');
|
const scopes = scopesIdx >= 0 && args[scopesIdx + 1] ? args[scopesIdx + 1] : 'read';
|
||||||
const url = args.find(a => !a.startsWith('--') && a !== args[tokenIdx + 1]);
|
|
||||||
const token = tokenIdx >= 0 ? args[tokenIdx + 1] : '';
|
const sql = postgres(DATABASE_URL!);
|
||||||
await test(url || '', token || '');
|
try {
|
||||||
break;
|
const { GBrainOAuthProvider } = await import('../core/oauth-provider.ts');
|
||||||
|
const provider = new GBrainOAuthProvider({ sql: sql as any });
|
||||||
|
const { clientId, clientSecret } = await provider.registerClientManual(
|
||||||
|
name, grantTypes, scopes, [],
|
||||||
|
);
|
||||||
|
console.log(`OAuth client registered: "${name}"\n`);
|
||||||
|
console.log(` Client ID: ${clientId}`);
|
||||||
|
console.log(` Client Secret: ${clientSecret}\n`);
|
||||||
|
console.log(` Grant types: ${grantTypes.join(', ')}`);
|
||||||
|
console.log(` Scopes: ${scopes}\n`);
|
||||||
|
console.log('Save the client secret — it will not be shown again.');
|
||||||
|
console.log(`Revoke with: bun run src/commands/auth.ts revoke-client "${clientId}"`);
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('Error:', e.message);
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
await sql.end();
|
||||||
}
|
}
|
||||||
default:
|
}
|
||||||
console.log(`GBrain Token Management
|
|
||||||
|
function printHelp() {
|
||||||
|
console.log(`GBrain Token Management
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
bun run src/commands/auth.ts create <name> Create a new access token
|
gbrain auth create <name> Create a legacy bearer token
|
||||||
bun run src/commands/auth.ts list List all tokens
|
gbrain auth list List all tokens
|
||||||
bun run src/commands/auth.ts revoke <name> Revoke a token
|
gbrain auth revoke <name> Revoke a legacy token
|
||||||
bun run src/commands/auth.ts test <url> --token <token> Smoke test a remote MCP server
|
gbrain auth register-client <name> [options] Register an OAuth 2.1 client
|
||||||
|
--grant-types <client_credentials,authorization_code> (default: client_credentials)
|
||||||
|
--scopes "<read write admin>" (default: read)
|
||||||
|
gbrain auth test <url> --token <token> Smoke test a remote MCP server
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked by src/cli.ts for `gbrain auth <subcommand>` and by direct
|
||||||
|
* `bun run src/commands/auth.ts <subcommand>` (legacy path).
|
||||||
|
*/
|
||||||
|
export async function runAuth(args: string[]) {
|
||||||
|
const [cmd, ...rest] = args;
|
||||||
|
ensureDatabaseUrl(cmd);
|
||||||
|
switch (cmd) {
|
||||||
|
case 'create': await create(rest[0]); break;
|
||||||
|
case 'list': await list(); break;
|
||||||
|
case 'revoke': await revoke(rest[0]); break;
|
||||||
|
case 'register-client': await registerClient(rest[0], rest.slice(1)); break;
|
||||||
|
case 'test': {
|
||||||
|
const tokenIdx = rest.indexOf('--token');
|
||||||
|
const url = rest.find(a => !a.startsWith('--') && a !== rest[tokenIdx + 1]);
|
||||||
|
const token = tokenIdx >= 0 ? rest[tokenIdx + 1] : '';
|
||||||
|
await test(url || '', token || '');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: printHelp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct invocation (`bun run src/commands/auth.ts ...`). Skip when imported.
|
||||||
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
|
await runAuth(process.argv.slice(2));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user