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:
co-authored by
Claude Opus 4.8
parent
db4c9f3641
commit
da5c6c2f79
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user