feat(offload): managed tier (CodeGraph AI) — metered reasoning via org token [WIP]

Adds the managed offload mode: point codegraph_explore at the CodeGraph AI metered
gateway (https://ai.getcodegraph.com) with an org token instead of a BYO provider key.
Same synthesis client, pointed at codegraph-ai-proxy (a metered OpenAI-compatible gateway).

- credentials.ts — org token in ~/.codegraph/credentials.json (0600); unlike a BYO
  provider key it's a revocable org-scoped auth token (gh/npm-login style), kept out
  of config.json
- config.ts — managed branch in resolveOffload: default gateway URL + public model id
  (openai/gpt-oss-120b) + login token as bearer; managed requires a token to be enabled
- reasoner.ts — fetchUsage() reads the credit balance from /v1/usage
- bin/codegraph.ts — `codegraph offload login --token <t>` / `logout`; status shows the
  managed tier + live balance

Proven GREEN end-to-end against a local wrangler-dev of the proxy: org token validated,
credits prechecked, real Cerebras synthesis returned, and credits metered + charged
(250,000 → 248,473). Graceful degrade on upstream failure; balance via /v1/usage.
Phase 3 (codegraph login device flow) replaces the manual --token.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby McHenry
2026-06-17 15:06:42 -05:00
co-authored by Claude Opus 4.8
parent db4c9f3641
commit da5c6c2f79
5 changed files with 229 additions and 27 deletions
+60
View File
@@ -18,7 +18,10 @@ import {
readOffloadConfig,
writeOffloadConfig,
resolveOffload,
MANAGED_DEFAULT_URL,
MANAGED_DEFAULT_MODEL,
} from '../src/reasoning/config';
import { readOffloadToken, writeOffloadToken } from '../src/reasoning/credentials';
import { isOffloadEnabled, synthesizeOffload, stripAgentDirectives } from '../src/reasoning/reasoner';
describe('reasoning offload', () => {
@@ -183,4 +186,61 @@ describe('reasoning offload', () => {
expect(stripped).toContain('code body');
});
});
describe('managed tier (CodeGraph AI)', () => {
it('stores the org token at 0600 in credentials.json, not in config.json', () => {
writeOffloadConfig({ managed: true });
writeOffloadToken('cgai_secrettoken');
expect(readOffloadToken()).toBe('cgai_secrettoken');
// config.json carries the managed flag but NOT the token.
const cfg = fs.readFileSync(path.join(home, '.codegraph', 'config.json'), 'utf8');
expect(cfg).toContain('managed');
expect(cfg).not.toContain('cgai_secrettoken');
const credPath = path.join(home, '.codegraph', 'credentials.json');
expect(fs.readFileSync(credPath, 'utf8')).toContain('cgai_secrettoken');
// POSIX perms must be owner-only (0600). (Windows has no POSIX mode bits.)
if (process.platform !== 'win32') {
expect(fs.statSync(credPath).mode & 0o777).toBe(0o600);
}
});
it('resolves managed mode to the gateway URL + public model id + login token', () => {
writeOffloadConfig({ managed: true });
writeOffloadToken('cgai_live');
const c = resolveOffload();
expect(c.enabled).toBe(true);
expect(c.managed).toBe(true);
expect(c.url).toBe(MANAGED_DEFAULT_URL);
expect(c.model).toBe(MANAGED_DEFAULT_MODEL);
expect(c.apiKey).toBe('cgai_live');
expect(c.keySource).toBe('codegraph login');
});
it('is NOT enabled when managed but signed out (no token)', () => {
writeOffloadConfig({ managed: true });
const c = resolveOffload();
expect(c.managed).toBe(true);
expect(c.enabled).toBe(false); // url defaults, but no token → effectively logged out
expect(isOffloadEnabled()).toBe(false);
});
it('clears the token on logout', () => {
writeOffloadToken('cgai_live');
writeOffloadToken(null);
expect(readOffloadToken()).toBeUndefined();
});
it('lets env override the managed endpoint and token (for testing)', () => {
writeOffloadConfig({ managed: true });
writeOffloadToken('cgai_stored');
process.env.CODEGRAPH_OFFLOAD_URL = 'http://localhost:8787/v1';
process.env.CODEGRAPH_OFFLOAD_KEY = 'cgai_env';
const c = resolveOffload();
expect(c.url).toBe('http://localhost:8787/v1');
expect(c.apiKey).toBe('cgai_env');
expect(c.keySource).toBe('CODEGRAPH_OFFLOAD_KEY');
});
});
});