@gajae-code/coding-agent 0.4.5 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/dist/types/async/job-manager.d.ts +26 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/list-models.d.ts +6 -0
- package/dist/types/commands/gc.d.ts +26 -0
- package/dist/types/commands/harness.d.ts +3 -0
- package/dist/types/config/file-lock-gc.d.ts +5 -0
- package/dist/types/config/file-lock.d.ts +7 -0
- package/dist/types/config/model-profile-activation.d.ts +11 -2
- package/dist/types/config/model-profiles.d.ts +7 -0
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/model-resolver.d.ts +2 -0
- package/dist/types/config/models-config-schema.d.ts +30 -0
- package/dist/types/config/settings-schema.d.ts +4 -3
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/defaults/gjc/extensions/grok-build/index.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/index.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.d.ts +25 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.d.ts +27 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.d.ts +8 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.d.ts +5 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +10 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.d.ts +38 -0
- package/dist/types/defaults/gjc-grok-cli.d.ts +5 -0
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +35 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +103 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +112 -0
- package/dist/types/gjc-runtime/gc-render.d.ts +6 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +134 -0
- package/dist/types/gjc-runtime/ledger-event-renderer.d.ts +68 -0
- package/dist/types/gjc-runtime/team-gc.d.ts +7 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +5 -1
- package/dist/types/gjc-runtime/tmux-common.d.ts +14 -0
- package/dist/types/gjc-runtime/tmux-gc.d.ts +7 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +13 -0
- package/dist/types/harness-control-plane/gc-adapter.d.ts +3 -0
- package/dist/types/harness-control-plane/owner.d.ts +8 -1
- package/dist/types/harness-control-plane/receipt-spool.d.ts +19 -0
- package/dist/types/harness-control-plane/state-machine.d.ts +6 -1
- package/dist/types/harness-control-plane/storage.d.ts +20 -0
- package/dist/types/harness-control-plane/types.d.ts +4 -0
- package/dist/types/hindsight/mental-models.d.ts +5 -5
- package/dist/types/modes/components/hook-selector.d.ts +7 -1
- package/dist/types/modes/components/model-selector.d.ts +1 -12
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +2 -2
- package/dist/types/modes/rpc/rpc-mode.d.ts +16 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +4 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +13 -0
- package/dist/types/modes/shared/agent-wire/session-registry.d.ts +25 -0
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +2 -0
- package/dist/types/sdk.d.ts +5 -0
- package/dist/types/session/agent-session.d.ts +3 -1
- package/dist/types/session/blob-store.d.ts +59 -4
- package/dist/types/session/session-manager.d.ts +24 -6
- package/dist/types/session/streaming-output.d.ts +3 -2
- package/dist/types/session/tool-choice-queue.d.ts +6 -0
- package/dist/types/skill-state/workflow-hud.d.ts +14 -0
- package/dist/types/task/receipt.d.ts +1 -0
- package/dist/types/task/types.d.ts +7 -0
- package/dist/types/thinking-metadata.d.ts +16 -0
- package/dist/types/thinking.d.ts +3 -12
- package/dist/types/tools/ask.d.ts +15 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/resolve.d.ts +0 -10
- package/dist/types/tools/subagent.d.ts +6 -0
- package/dist/types/utils/tool-choice.d.ts +14 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +52 -0
- package/src/cli/args.ts +3 -0
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/list-models.ts +13 -1
- package/src/cli.ts +9 -4
- package/src/commands/gc.ts +22 -0
- package/src/commands/harness.ts +43 -5
- package/src/commands/launch.ts +2 -2
- package/src/commands/session.ts +3 -1
- package/src/config/file-lock-gc.ts +181 -0
- package/src/config/file-lock.ts +14 -0
- package/src/config/model-profile-activation.ts +15 -3
- package/src/config/model-profiles.ts +264 -56
- package/src/config/model-resolver.ts +9 -6
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +6 -3
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/server.ts +513 -26
- package/src/cursor.ts +16 -2
- package/src/defaults/gjc/agent.models.grok-cli.yml +36 -0
- package/src/defaults/gjc/extensions/grok-build/index.ts +1 -0
- package/src/defaults/gjc/extensions/grok-build/package.json +7 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +39 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/package.json +8 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/index.ts +1 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +155 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.ts +361 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.ts +57 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +99 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +50 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.ts +56 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +36 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.ts +44 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +131 -113
- package/src/defaults/gjc/skills/deep-interview/lateral-review-panel.md +49 -0
- package/src/defaults/gjc/skills/team/SKILL.md +3 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +8 -2
- package/src/defaults/gjc-defaults.ts +7 -0
- package/src/defaults/gjc-grok-cli.ts +22 -0
- package/src/export/html/index.ts +13 -9
- package/src/extensibility/extensions/index.ts +1 -0
- package/src/extensibility/extensions/prefix-command-bridge.ts +128 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +417 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +18 -26
- package/src/gjc-runtime/deep-interview-state.ts +324 -0
- package/src/gjc-runtime/gc-render.ts +70 -0
- package/src/gjc-runtime/gc-runtime.ts +403 -0
- package/src/gjc-runtime/ledger-event-renderer.ts +164 -0
- package/src/gjc-runtime/ralplan-runtime.ts +58 -7
- package/src/gjc-runtime/state-renderer.ts +12 -3
- package/src/gjc-runtime/state-runtime.ts +46 -29
- package/src/gjc-runtime/team-gc.ts +49 -0
- package/src/gjc-runtime/team-runtime.ts +211 -8
- package/src/gjc-runtime/tmux-common.ts +29 -0
- package/src/gjc-runtime/tmux-gc.ts +176 -0
- package/src/gjc-runtime/tmux-sessions.ts +68 -12
- package/src/gjc-runtime/ultragoal-runtime.ts +517 -41
- package/src/gjc-runtime/workflow-manifest.generated.json +27 -1
- package/src/gjc-runtime/workflow-manifest.ts +16 -1
- package/src/harness-control-plane/gc-adapter.ts +184 -0
- package/src/harness-control-plane/owner.ts +89 -27
- package/src/harness-control-plane/receipt-spool.ts +128 -0
- package/src/harness-control-plane/state-machine.ts +27 -6
- package/src/harness-control-plane/storage.ts +93 -0
- package/src/harness-control-plane/types.ts +4 -0
- package/src/hindsight/mental-models.ts +17 -16
- package/src/internal-urls/docs-index.generated.ts +14 -8
- package/src/main.ts +7 -2
- package/src/modes/components/assistant-message.ts +26 -14
- package/src/modes/components/diff.ts +97 -0
- package/src/modes/components/hook-selector.ts +19 -0
- package/src/modes/components/model-selector.ts +370 -181
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/tool-execution.ts +30 -13
- package/src/modes/controllers/command-controller.ts +25 -6
- package/src/modes/controllers/extension-ui-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +34 -42
- package/src/modes/rpc/rpc-client.ts +3 -2
- package/src/modes/rpc/rpc-mode.ts +187 -39
- package/src/modes/rpc/rpc-types.ts +5 -2
- package/src/modes/shared/agent-wire/command-dispatch.ts +279 -257
- package/src/modes/shared/agent-wire/command-validation.ts +11 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +30 -1
- package/src/modes/shared/agent-wire/session-registry.ts +109 -0
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +24 -0
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +23 -3
- package/src/modes/shared/agent-wire/unattended-session.ts +16 -1
- package/src/sdk.ts +46 -5
- package/src/secrets/obfuscator.ts +102 -27
- package/src/session/agent-session.ts +179 -25
- package/src/session/blob-store.ts +148 -6
- package/src/session/session-manager.ts +311 -60
- package/src/session/streaming-output.ts +185 -122
- package/src/session/tool-choice-queue.ts +23 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +7 -1
- package/src/skill-state/workflow-hud.ts +106 -10
- package/src/slash-commands/builtin-registry.ts +3 -2
- package/src/task/executor.ts +78 -6
- package/src/task/receipt.ts +5 -0
- package/src/task/render.ts +21 -1
- package/src/task/types.ts +8 -0
- package/src/thinking-metadata.ts +51 -0
- package/src/thinking.ts +26 -46
- package/src/tools/ask.ts +56 -1
- package/src/tools/bash.ts +1 -1
- package/src/tools/index.ts +2 -0
- package/src/tools/job.ts +3 -2
- package/src/tools/monitor.ts +36 -1
- package/src/tools/resolve.ts +93 -18
- package/src/tools/subagent-render.ts +9 -0
- package/src/tools/subagent.ts +26 -2
- package/src/utils/edit-mode.ts +1 -1
- package/src/utils/tool-choice.ts +45 -16
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getBaseUrl } from '../shared/base-url.js';
|
|
2
|
+
|
|
3
|
+
interface BillingUsage {
|
|
4
|
+
monthlyLimit: number;
|
|
5
|
+
used: number;
|
|
6
|
+
billingPeriodEnd: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function parseBillingUsage(payload: unknown): BillingUsage {
|
|
10
|
+
if (!payload || typeof payload !== 'object') throw new Error('invalid billing payload');
|
|
11
|
+
const config = (payload as Record<string, unknown>).config;
|
|
12
|
+
if (!config || typeof config !== 'object') throw new Error('invalid billing payload');
|
|
13
|
+
const monthlyLimit = ((config as Record<string, unknown>).monthlyLimit as Record<string, unknown>)
|
|
14
|
+
?.val;
|
|
15
|
+
const used = ((config as Record<string, unknown>).used as Record<string, unknown>)?.val;
|
|
16
|
+
const billingPeriodEnd = (config as Record<string, unknown>).billingPeriodEnd;
|
|
17
|
+
if (
|
|
18
|
+
typeof monthlyLimit !== 'number' ||
|
|
19
|
+
!Number.isFinite(monthlyLimit) ||
|
|
20
|
+
typeof used !== 'number' ||
|
|
21
|
+
!Number.isFinite(used) ||
|
|
22
|
+
typeof billingPeriodEnd !== 'string' ||
|
|
23
|
+
!Number.isFinite(new Date(billingPeriodEnd).getTime())
|
|
24
|
+
) {
|
|
25
|
+
throw new Error('invalid billing payload');
|
|
26
|
+
}
|
|
27
|
+
return { monthlyLimit, used, billingPeriodEnd };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function fetchBillingUsage(token: string): Promise<BillingUsage> {
|
|
31
|
+
const response = await fetch(`${getBaseUrl()}/billing`, {
|
|
32
|
+
headers: {
|
|
33
|
+
authorization: `Bearer ${token}`,
|
|
34
|
+
'x-xai-token-auth': 'xai-grok-cli',
|
|
35
|
+
accept: 'application/json',
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) throw new Error(`billing endpoint returned ${response.status}`);
|
|
39
|
+
return parseBillingUsage(await response.json());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function formatQuota(usage: BillingUsage | undefined) {
|
|
43
|
+
if (!usage) {
|
|
44
|
+
return [
|
|
45
|
+
' Usage:',
|
|
46
|
+
' no billing data available — run /login grok-build or set GROK_CLI_OAUTH_TOKEN',
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const resetDate = new Date(new Date(usage.billingPeriodEnd).getTime() - 8 * 60 * 60 * 1000);
|
|
51
|
+
return [
|
|
52
|
+
' Usage:',
|
|
53
|
+
` ${usage.used.toLocaleString()} / ${usage.monthlyLimit.toLocaleString()} credits used (${Math.round((usage.used / usage.monthlyLimit) * 100)}%)`,
|
|
54
|
+
` ${(usage.monthlyLimit - usage.used).toLocaleString()} credits remaining`,
|
|
55
|
+
` Resets at ${['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][resetDate.getUTCMonth()]} ${resetDate.getUTCDate()} ${resetDate.getUTCHours().toString().padStart(2, '0')}:${resetDate.getUTCMinutes().toString().padStart(2, '0')} PT`,
|
|
56
|
+
];
|
|
57
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GJC Grok Build provider — SuperGrok OAuth + cli-chat-proxy models.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Api, Model } from '@gajae-code/ai';
|
|
6
|
+
import { Effort } from '@gajae-code/ai/model-thinking';
|
|
7
|
+
import type { OAuthCredentials, OAuthLoginCallbacks } from '@gajae-code/ai/utils/oauth/types';
|
|
8
|
+
import { loginXai, refreshXaiToken, XAI_OAUTH_SCOPE } from '@gajae-code/ai/utils/oauth/xai';
|
|
9
|
+
import type { ExtensionAPI, ProviderConfig } from '@gajae-code/coding-agent';
|
|
10
|
+
import { type GrokCliModelConfig, resolveModels } from '../models/catalog.js';
|
|
11
|
+
import { sanitizePayload } from '../payload/sanitize.js';
|
|
12
|
+
import { getBaseUrl, isGrokBuildBaseUrlOverrideIgnored } from '../shared/base-url.js';
|
|
13
|
+
import { streamGrokCli } from './stream.js';
|
|
14
|
+
import { registerUsageCommand } from './usage.js';
|
|
15
|
+
|
|
16
|
+
const GROK_BUILD_XAI_AUTHORIZE_PARAMS = {
|
|
17
|
+
plan: 'generic',
|
|
18
|
+
referrer: 'gjc-grok-cli',
|
|
19
|
+
} satisfies Readonly<Record<string, string>>;
|
|
20
|
+
|
|
21
|
+
const GROK_BUILD_XAI_REFRESH_PARAMS = {
|
|
22
|
+
scope: XAI_OAUTH_SCOPE,
|
|
23
|
+
...GROK_BUILD_XAI_AUTHORIZE_PARAMS,
|
|
24
|
+
} satisfies Readonly<Record<string, string>>;
|
|
25
|
+
|
|
26
|
+
export default function registerGrokCli(api: ExtensionAPI) {
|
|
27
|
+
const baseUrl = getBaseUrl();
|
|
28
|
+
const models = resolveModels();
|
|
29
|
+
|
|
30
|
+
api.registerProvider('grok-build', {
|
|
31
|
+
baseUrl,
|
|
32
|
+
apiKey: process.env.GROK_CLI_OAUTH_TOKEN ? 'GROK_CLI_OAUTH_TOKEN' : undefined,
|
|
33
|
+
api: 'grok-cli-responses',
|
|
34
|
+
models: models.map((m: GrokCliModelConfig) => ({
|
|
35
|
+
id: m.id,
|
|
36
|
+
name: m.name,
|
|
37
|
+
reasoning: m.reasoning,
|
|
38
|
+
thinking: m.reasoning
|
|
39
|
+
? { minLevel: Effort.Low, maxLevel: Effort.XHigh, mode: 'effort' }
|
|
40
|
+
: undefined,
|
|
41
|
+
input: m.input,
|
|
42
|
+
cost: m.cost,
|
|
43
|
+
contextWindow: m.contextWindow,
|
|
44
|
+
maxTokens: m.maxTokens,
|
|
45
|
+
})),
|
|
46
|
+
oauth: {
|
|
47
|
+
name: 'Grok Build',
|
|
48
|
+
|
|
49
|
+
async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
|
|
50
|
+
return loginXai(callbacks, { extraAuthorizeParams: GROK_BUILD_XAI_AUTHORIZE_PARAMS });
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {
|
|
54
|
+
return refreshXaiToken(credentials.refresh, {
|
|
55
|
+
extraTokenParams: GROK_BUILD_XAI_REFRESH_PARAMS,
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
getApiKey(credentials: OAuthCredentials): string {
|
|
60
|
+
return credentials.access;
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
modifyModels(models: Model<Api>[], _credentials: OAuthCredentials) {
|
|
64
|
+
const effectiveBaseUrl = getBaseUrl().replace(/\/+$/, '');
|
|
65
|
+
|
|
66
|
+
return models.map((m) =>
|
|
67
|
+
m.provider === 'grok-build' ? { ...m, baseUrl: effectiveBaseUrl } : m,
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
} satisfies ProviderConfig['oauth'],
|
|
71
|
+
|
|
72
|
+
streamSimple: streamGrokCli,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
api.on('session_start', (_event, ctx) => {
|
|
76
|
+
if (process.env.GROK_CLI_OAUTH_TOKEN) {
|
|
77
|
+
ctx.ui.notify(
|
|
78
|
+
'[Grok Build] Using GROK_CLI_OAUTH_TOKEN env bypass — no auto-refresh, no model discovery. Login with /login grok-build for persisted refreshable auth.',
|
|
79
|
+
'warning',
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (isGrokBuildBaseUrlOverrideIgnored()) {
|
|
83
|
+
ctx.ui.notify(
|
|
84
|
+
'[Grok Build] Ignoring unsafe Grok base URL override for OAuth credential safety. Set GJC_GROK_CLI_ALLOW_UNSAFE_BASE_URL=1 only for trusted local testing.',
|
|
85
|
+
'warning',
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
api.on('before_provider_request', (event, ctx) => {
|
|
91
|
+
if (ctx.model?.provider !== 'grok-build') return;
|
|
92
|
+
|
|
93
|
+
const modelId = ctx.model?.id ?? '';
|
|
94
|
+
const sessionId = ctx.sessionManager?.getSessionId();
|
|
95
|
+
return sanitizePayload(event.payload as Record<string, unknown>, modelId, sessionId, ctx.cwd);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
registerUsageCommand(api);
|
|
99
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Api,
|
|
3
|
+
AssistantMessageEventStream,
|
|
4
|
+
Context,
|
|
5
|
+
Model,
|
|
6
|
+
SimpleStreamOptions,
|
|
7
|
+
} from '@gajae-code/ai';
|
|
8
|
+
import { streamOpenAIResponses } from '@gajae-code/ai/providers/openai-responses';
|
|
9
|
+
|
|
10
|
+
const GROK_CLI_VERSION = '0.2.33';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Stream function that adds Grok CLI-specific headers to requests.
|
|
14
|
+
*
|
|
15
|
+
* GJC Grok Build extension sends cli-chat-proxy headers (see agent.models.grok-cli.yml):
|
|
16
|
+
* - x-grok-conv-id: <session/conversation ID>
|
|
17
|
+
* - x-grok-model-override: <model ID>
|
|
18
|
+
* - x-xai-token-auth: xai-grok-cli
|
|
19
|
+
*/
|
|
20
|
+
export function streamGrokCli(
|
|
21
|
+
model: Model<Api>,
|
|
22
|
+
context: Context,
|
|
23
|
+
options?: SimpleStreamOptions,
|
|
24
|
+
): AssistantMessageEventStream {
|
|
25
|
+
const sessionId = options?.sessionId;
|
|
26
|
+
const headers: Record<string, string> = {
|
|
27
|
+
...options?.headers,
|
|
28
|
+
'x-grok-client-identifier': 'gjc-grok-cli',
|
|
29
|
+
'x-grok-client-version': GROK_CLI_VERSION,
|
|
30
|
+
'x-xai-token-auth': 'xai-grok-cli',
|
|
31
|
+
'x-grok-model-override': model.id,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (sessionId) {
|
|
35
|
+
headers['x-grok-conv-id'] = sessionId;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const responsesModel = {
|
|
39
|
+
...model,
|
|
40
|
+
api: 'openai-responses',
|
|
41
|
+
} as Model<'openai-responses'>;
|
|
42
|
+
|
|
43
|
+
return streamOpenAIResponses(responsesModel, context, {
|
|
44
|
+
...options,
|
|
45
|
+
headers,
|
|
46
|
+
onResponse(response) {
|
|
47
|
+
options?.onResponse?.(response, model);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Api, Model } from '@gajae-code/ai';
|
|
2
|
+
import type { ExtensionAPI } from '@gajae-code/coding-agent';
|
|
3
|
+
import { XaiOAuthError } from '../shared/errors.js';
|
|
4
|
+
import { fetchBillingUsage, formatQuota } from './billing.js';
|
|
5
|
+
|
|
6
|
+
export function registerUsageCommand(api: Pick<ExtensionAPI, 'registerCommand'>) {
|
|
7
|
+
api.registerCommand('grok-build-usage', {
|
|
8
|
+
description: 'Show Grok Build provider status, quota, and token health',
|
|
9
|
+
handler: async (_args, ctx) => {
|
|
10
|
+
const token = process.env.GROK_CLI_OAUTH_TOKEN;
|
|
11
|
+
if (token) {
|
|
12
|
+
ctx.ui.notify(
|
|
13
|
+
'⚠️ Grok Build: using GROK_CLI_OAUTH_TOKEN env bypass — no auto-refresh available',
|
|
14
|
+
'warning',
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const registry = ctx.modelRegistry;
|
|
20
|
+
const grokModels = registry.getAll().filter((m: Model<Api>) => m.provider === 'grok-build');
|
|
21
|
+
if (grokModels.length === 0) {
|
|
22
|
+
ctx.ui.notify(
|
|
23
|
+
'Grok Build: no models registered. Run /login grok-build first.',
|
|
24
|
+
'warning',
|
|
25
|
+
);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const apiKey = token ?? (await registry.getApiKeyForProvider?.('grok-build'));
|
|
30
|
+
if (!apiKey) {
|
|
31
|
+
ctx.ui.notify(formatQuota(undefined).join('\n'), 'info');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
ctx.ui.notify('Fetching grok build usage…', 'info');
|
|
37
|
+
ctx.ui.notify(formatQuota(await fetchBillingUsage(apiKey)).join('\n'), 'info');
|
|
38
|
+
} catch (err) {
|
|
39
|
+
ctx.ui.notify(
|
|
40
|
+
`Grok Build billing refresh failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
41
|
+
'warning',
|
|
42
|
+
);
|
|
43
|
+
ctx.ui.notify(formatQuota(undefined).join('\n'), 'info');
|
|
44
|
+
}
|
|
45
|
+
} catch (err) {
|
|
46
|
+
const msg =
|
|
47
|
+
err instanceof XaiOAuthError
|
|
48
|
+
? `${err.message} (code: ${err.code})`
|
|
49
|
+
: err instanceof Error
|
|
50
|
+
? err.message
|
|
51
|
+
: String(err);
|
|
52
|
+
ctx.ui.notify(`Grok Build: ${msg}`, 'warning');
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const DEFAULT_GROK_BUILD_BASE_URL = 'https://cli-chat-proxy.grok.com/v1';
|
|
2
|
+
const ALLOWED_GROK_BUILD_HOSTS = new Set(['cli-chat-proxy.grok.com']);
|
|
3
|
+
|
|
4
|
+
function normalizeBaseUrl(baseUrl: string): string {
|
|
5
|
+
return baseUrl.trim().replace(/\/+$/, '');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function isAllowedCredentialHost(baseUrl: string): boolean {
|
|
9
|
+
try {
|
|
10
|
+
const url = new URL(baseUrl);
|
|
11
|
+
return url.protocol === 'https:' && ALLOWED_GROK_BUILD_HOSTS.has(url.hostname.toLowerCase());
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getBaseUrl(): string {
|
|
18
|
+
const configured = process.env.GJC_GROK_CLI_BASE_URL || process.env.GROK_CLI_BASE_URL;
|
|
19
|
+
if (!configured) return DEFAULT_GROK_BUILD_BASE_URL;
|
|
20
|
+
|
|
21
|
+
const normalized = normalizeBaseUrl(configured);
|
|
22
|
+
if (isAllowedCredentialHost(normalized)) return normalized;
|
|
23
|
+
|
|
24
|
+
if (process.env.GJC_GROK_CLI_ALLOW_UNSAFE_BASE_URL === '1') return normalized;
|
|
25
|
+
|
|
26
|
+
return DEFAULT_GROK_BUILD_BASE_URL;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isGrokBuildBaseUrlOverrideIgnored(): boolean {
|
|
30
|
+
const configured = process.env.GJC_GROK_CLI_BASE_URL || process.env.GROK_CLI_BASE_URL;
|
|
31
|
+
if (!configured) return false;
|
|
32
|
+
const normalized = normalizeBaseUrl(configured);
|
|
33
|
+
return (
|
|
34
|
+
!isAllowedCredentialHost(normalized) && process.env.GJC_GROK_CLI_ALLOW_UNSAFE_BASE_URL !== '1'
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed error for xAI OAuth failures.
|
|
3
|
+
*
|
|
4
|
+
* Codes allow the login flow and stream handlers to distinguish
|
|
5
|
+
* retryable failures (network) from fatal ones (revoked refresh token).
|
|
6
|
+
*/
|
|
7
|
+
export class XaiOAuthError extends Error {
|
|
8
|
+
constructor(
|
|
9
|
+
message: string,
|
|
10
|
+
public readonly code: string,
|
|
11
|
+
public readonly reloginRequired = false,
|
|
12
|
+
) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'XaiOAuthError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Well-known error codes. */
|
|
19
|
+
export const XaiErrorCode = {
|
|
20
|
+
/** OIDC discovery failed (network, invalid response). */
|
|
21
|
+
DISCOVERY_FAILED: 'discovery_failed',
|
|
22
|
+
/** Discovery endpoint returned a non-xAI origin. */
|
|
23
|
+
DISCOVERY_INVALID_ORIGIN: 'discovery_invalid_origin',
|
|
24
|
+
/** Authorization was denied or errored in the browser. */
|
|
25
|
+
AUTHORIZATION_FAILED: 'authorization_failed',
|
|
26
|
+
/** CSRF state mismatch between request and callback. */
|
|
27
|
+
STATE_MISMATCH: 'state_mismatch',
|
|
28
|
+
/** Callback did not include an authorization code. */
|
|
29
|
+
CODE_MISSING: 'code_missing',
|
|
30
|
+
/** Token exchange failed (network, invalid response). */
|
|
31
|
+
TOKEN_EXCHANGE_FAILED: 'token_exchange_failed',
|
|
32
|
+
/** Token exchange returned an invalid payload. */
|
|
33
|
+
TOKEN_EXCHANGE_INVALID: 'token_exchange_invalid',
|
|
34
|
+
/** Refresh token is missing or empty. */
|
|
35
|
+
REFRESH_MISSING: 'refresh_missing',
|
|
36
|
+
/** Token refresh failed (expired, revoked). */
|
|
37
|
+
REFRESH_FAILED: 'refresh_failed',
|
|
38
|
+
/** No credentials stored. */
|
|
39
|
+
AUTH_MISSING: 'auth_missing',
|
|
40
|
+
/** Loopback callback server could not bind. */
|
|
41
|
+
CALLBACK_BIND_FAILED: 'callback_bind_failed',
|
|
42
|
+
/** Loopback callback timed out. */
|
|
43
|
+
CALLBACK_TIMEOUT: 'callback_timeout',
|
|
44
|
+
} as const;
|