subconscious-cli 4.0.17 → 4.0.18
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/README.md +27 -7
- package/bin/auth.js +8 -5
- package/bin/cli.js +41 -0
- package/bin/native/subc-tui-darwin-amd64 +0 -0
- package/bin/native/subc-tui-darwin-arm64 +0 -0
- package/bin/native/subc-tui-linux-amd64 +0 -0
- package/bin/native/subc-tui-linux-arm64 +0 -0
- package/bin/native/subc-tui-windows-amd64.exe +0 -0
- package/bin/native/subc-tui-windows-arm64.exe +0 -0
- package/bin/profiles.js +49 -12
- package/bin/registry.generated.json +12 -3
- package/bin/runbook/claude-code/install.sh +4 -4
- package/bin/runbook/claude-code/run.sh +3 -3
- package/bin/runbook/codex/run.sh +3 -2
- package/bin/runbook/copilot/install.sh +6 -5
- package/bin/runbook/cursor/install.sh +3 -2
- package/bin/runbook/deepseek-harness/run.sh +3 -2
- package/bin/runbook/opencode/install.sh +6 -5
- package/bin/runbook/opencode/run.sh +3 -2
- package/bin/runbook/pi/install.sh +4 -3
- package/bin/runbook/pi/run.sh +1 -1
- package/bin/runbook/subconscious-code/run.sh +1 -1
- package/bin/tui.js +10 -1
- package/bin/usage.js +229 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,8 +34,9 @@ subc <agent> uninstall
|
|
|
34
34
|
|
|
35
35
|
Running `subc` with no arguments in a terminal opens the native Go TUI. Use
|
|
36
36
|
the arrow keys and Enter to launch agents or manage the active profile, `p` to
|
|
37
|
-
switch profiles, and `q` to quit. The menu includes dedicated **
|
|
38
|
-
**Set default model**, **Set subagent model**,
|
|
37
|
+
switch profiles, and `q` to quit. The menu includes dedicated **Usage**,
|
|
38
|
+
**Create profile**, **Set default model**, **Set subagent model**,
|
|
39
|
+
**Update base URL**, and **Update platform URL** actions.
|
|
39
40
|
Model and URL changes are validated and saved to the active profile without
|
|
40
41
|
leaving the TUI.
|
|
41
42
|
`subc help` and `subc --help` continue to print script-friendly command help.
|
|
@@ -161,7 +162,7 @@ DeepSeek Harness context and output settings used by the packaged runbook script
|
|
|
161
162
|
subc config # list every profile and its file path
|
|
162
163
|
subc -p staging config create # create a profile with default settings
|
|
163
164
|
subc -p staging config # print that path and env file
|
|
164
|
-
subc -p staging config --model subconscious/glm-5.
|
|
165
|
+
subc -p staging config --model subconscious/glm-5.3-marathon
|
|
165
166
|
subc -p staging config --model UNSET
|
|
166
167
|
subc -p staging config --subagent-model subconscious/deepseek-v4-flash-marathon
|
|
167
168
|
subc -p staging config --subagent-model UNSET
|
|
@@ -197,7 +198,7 @@ Named profiles work like AWS CLI profiles:
|
|
|
197
198
|
subc -p staging config \
|
|
198
199
|
--gateway-url https://staging.example \
|
|
199
200
|
--api-key sk-staging-... \
|
|
200
|
-
--model subconscious/glm-5.
|
|
201
|
+
--model subconscious/glm-5.3-marathon
|
|
201
202
|
|
|
202
203
|
subc -p staging claude
|
|
203
204
|
subc -p staging cursor install
|
|
@@ -216,7 +217,8 @@ that request fails, it falls back to the public `/v1/models` fleet list, then
|
|
|
216
217
|
to the models packaged with the CLI:
|
|
217
218
|
|
|
218
219
|
```text
|
|
219
|
-
subconscious/glm-5.
|
|
220
|
+
subconscious/glm-5.3-marathon (default)
|
|
221
|
+
subconscious/glm-5.2
|
|
220
222
|
subconscious/tim-qwen3.6-27b
|
|
221
223
|
subconscious/deepseek-v4-flash-marathon
|
|
222
224
|
```
|
|
@@ -225,10 +227,10 @@ Select a model per run, save it in the current profile, or override it through
|
|
|
225
227
|
the environment:
|
|
226
228
|
|
|
227
229
|
```bash
|
|
228
|
-
subc codex --model subconscious/glm-5.
|
|
230
|
+
subc codex --model subconscious/glm-5.3-marathon
|
|
229
231
|
subc config --model subconscious/deepseek-v4-flash-marathon
|
|
230
232
|
subc config --subagent-model subconscious/tim-qwen3.6-27b
|
|
231
|
-
export SUBCONSCIOUS_MODEL=subconscious/glm-5.
|
|
233
|
+
export SUBCONSCIOUS_MODEL=subconscious/glm-5.3-marathon
|
|
232
234
|
```
|
|
233
235
|
|
|
234
236
|
Every launch and install fetches the same live catalog without caching. Codex,
|
|
@@ -275,6 +277,9 @@ also maintains `~/.subconscious/config.json`.
|
|
|
275
277
|
subc login
|
|
276
278
|
subc update-key sk-new-key-...
|
|
277
279
|
subc update-url https://api.subconscious.dev
|
|
280
|
+
subc update-platform-url https://platform.subconscious.dev
|
|
281
|
+
subc usage
|
|
282
|
+
subc usage --json
|
|
278
283
|
subc whoami
|
|
279
284
|
subc logout
|
|
280
285
|
```
|
|
@@ -305,6 +310,21 @@ updated automatically as well.
|
|
|
305
310
|
`SUBCONSCIOUS_BASE_URL` continues to take precedence when set. A configured
|
|
306
311
|
`CLAUDE_GATEWAY_URL` remains a Claude-specific override.
|
|
307
312
|
|
|
313
|
+
`subc update-platform-url <platform-url>` validates the URL and saves
|
|
314
|
+
`PLATFORM_URL` in the active profile. Login, `whoami`, and `usage` call this
|
|
315
|
+
host. Production defaults to `https://platform.subconscious.dev`:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
subc update-platform-url https://platform.subconscious.dev
|
|
319
|
+
subc config --platform-url http://localhost:3000
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
`SUBCONSCIOUS_URL` overrides the saved profile value for local development.
|
|
323
|
+
|
|
324
|
+
`subc usage` fetches billing mode, daily token allowance, credit balance,
|
|
325
|
+
overage, and per-model consumption from `GET /api/v1/usage` on the resolved
|
|
326
|
+
platform host. Pass `--json` for machine-readable output.
|
|
327
|
+
|
|
308
328
|
`SUBCONSCIOUS_API_KEY` takes precedence over profile and saved config keys,
|
|
309
329
|
which is useful for CI and temporary sessions. `subc logout` clears the
|
|
310
330
|
selected profile's shared key while preserving its non-secret runbook settings;
|
package/bin/auth.js
CHANGED
|
@@ -34,9 +34,12 @@ const LEGACY_CONFIG_FILE = CONFIG_OVERRIDE
|
|
|
34
34
|
// Defaults to the platform host. Developers set SUBCONSCIOUS_URL for local dev.
|
|
35
35
|
export const DEFAULT_PLATFORM_URL = 'https://platform.subconscious.dev';
|
|
36
36
|
|
|
37
|
-
export function getPlatformUrl() {
|
|
38
|
-
const
|
|
39
|
-
return
|
|
37
|
+
export function getPlatformUrl(profile) {
|
|
38
|
+
const fromEnv = process.env.SUBCONSCIOUS_URL?.trim();
|
|
39
|
+
if (fromEnv) return fromEnv.replace(/\/$/, '');
|
|
40
|
+
const fromProfile = profile?.values?.PLATFORM_URL?.trim();
|
|
41
|
+
if (fromProfile) return fromProfile.replace(/\/$/, '');
|
|
42
|
+
return DEFAULT_PLATFORM_URL;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
// Login callback CORS. After the marketing/platform split, /cli/auth lives on
|
|
@@ -323,7 +326,7 @@ export async function loginCommand(_argv = [], options = {}) {
|
|
|
323
326
|
);
|
|
324
327
|
console.log();
|
|
325
328
|
|
|
326
|
-
const platformUrl = getPlatformUrl();
|
|
329
|
+
const platformUrl = getPlatformUrl(options.profile);
|
|
327
330
|
const loginStatus = await probeLoginPage(platformUrl);
|
|
328
331
|
if (isLoginMissing(loginStatus)) {
|
|
329
332
|
printLoginUpgradeWarning();
|
|
@@ -459,7 +462,7 @@ export async function whoamiCommand(_argv = [], options = {}) {
|
|
|
459
462
|
|
|
460
463
|
// Validate the key against the server; falls back to offline display if unreachable
|
|
461
464
|
try {
|
|
462
|
-
const res = await fetch(`${getPlatformUrl()}/api/cli/whoami`, {
|
|
465
|
+
const res = await fetch(`${getPlatformUrl(options.profile)}/api/cli/whoami`, {
|
|
463
466
|
headers: { Authorization: `Bearer ${key}` },
|
|
464
467
|
signal: AbortSignal.timeout(5000),
|
|
465
468
|
});
|
package/bin/cli.js
CHANGED
|
@@ -37,12 +37,14 @@ import {
|
|
|
37
37
|
resolvedModelSetting,
|
|
38
38
|
RUNBOOK_DEFAULTS,
|
|
39
39
|
SUPPORTED_MODELS as PACKAGED_MODELS,
|
|
40
|
+
updatePlatformUrlCommand,
|
|
40
41
|
updateUrlCommand,
|
|
41
42
|
validateProfileName,
|
|
42
43
|
} from './profiles.js';
|
|
43
44
|
import { resolveModelCatalog } from './models.js';
|
|
44
45
|
import { showUpdateNotice } from './update-check.js';
|
|
45
46
|
import { runTui } from './tui.js';
|
|
47
|
+
import { printUsageHelp, usageCommand } from './usage.js';
|
|
46
48
|
|
|
47
49
|
function isHelpArg(arg) {
|
|
48
50
|
return arg === 'help' || arg === '-h' || arg === '--help';
|
|
@@ -63,8 +65,10 @@ function printHelp() {
|
|
|
63
65
|
${c.cyan}login${c.reset} Authenticate and save your API key
|
|
64
66
|
${c.cyan}update-key${c.reset} Replace the selected profile's API key
|
|
65
67
|
${c.cyan}update-url${c.reset} Update the active profile's gateway URL automatically
|
|
68
|
+
${c.cyan}update-platform-url${c.reset} Update the active profile's platform URL
|
|
66
69
|
${c.cyan}logout${c.reset} Remove saved credentials
|
|
67
70
|
${c.cyan}whoami${c.reset} Show current authentication status
|
|
71
|
+
${c.cyan}usage${c.reset} Show billing, quota, and model usage
|
|
68
72
|
${c.cyan}upgrade${c.reset} Upgrade this CLI to the latest version
|
|
69
73
|
|
|
70
74
|
${c.bold}Profiles${c.reset}
|
|
@@ -88,6 +92,8 @@ ${agents}
|
|
|
88
92
|
${c.dim}$${c.reset} subc config help
|
|
89
93
|
${c.dim}$${c.reset} subc -p staging config
|
|
90
94
|
${c.dim}$${c.reset} subc config edit vim
|
|
95
|
+
${c.dim}$${c.reset} subc usage
|
|
96
|
+
${c.dim}$${c.reset} subc usage --json
|
|
91
97
|
${c.dim}$${c.reset} subc claude
|
|
92
98
|
${c.dim}$${c.reset} subc claude help
|
|
93
99
|
${c.dim}$${c.reset} subc cursor install
|
|
@@ -149,6 +155,22 @@ Usage:
|
|
|
149
155
|
subc update-url help
|
|
150
156
|
|
|
151
157
|
Update the active profile's gateway URL.
|
|
158
|
+
`,
|
|
159
|
+
'update-platform-url': `
|
|
160
|
+
Usage:
|
|
161
|
+
subc update-platform-url <platform-url>
|
|
162
|
+
subc update-platform-url help
|
|
163
|
+
|
|
164
|
+
Update the active profile's platform URL for login, whoami, and usage.
|
|
165
|
+
`,
|
|
166
|
+
usage: `
|
|
167
|
+
Usage:
|
|
168
|
+
subc usage
|
|
169
|
+
subc -p NAME usage
|
|
170
|
+
subc usage --json
|
|
171
|
+
subc usage help
|
|
172
|
+
|
|
173
|
+
Show billing mode, daily token allowance, credit balance, and per-model usage.
|
|
152
174
|
`,
|
|
153
175
|
models: `
|
|
154
176
|
Usage:
|
|
@@ -351,6 +373,25 @@ async function main() {
|
|
|
351
373
|
return;
|
|
352
374
|
}
|
|
353
375
|
|
|
376
|
+
if (command === 'update-platform-url') {
|
|
377
|
+
if (isHelpArg(args[1])) {
|
|
378
|
+
console.log(COMMAND_HELP['update-platform-url']);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
await updatePlatformUrlCommand(args.slice(1), { profileName });
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (command === 'usage') {
|
|
386
|
+
if (isHelpArg(args[1])) {
|
|
387
|
+
printUsageHelp();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const profile = await loadProfile(profileName);
|
|
391
|
+
await usageCommand(args.slice(1), { profile, profileName });
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
354
395
|
const agent = resolveAgent(command);
|
|
355
396
|
if (agent) {
|
|
356
397
|
const agentArgs = args.slice(1);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/profiles.js
CHANGED
|
@@ -76,6 +76,12 @@ const SETTINGS = {
|
|
|
76
76
|
type: 'url',
|
|
77
77
|
required: true,
|
|
78
78
|
},
|
|
79
|
+
PLATFORM_URL: {
|
|
80
|
+
key: 'PLATFORM_URL',
|
|
81
|
+
label: 'Platform URL',
|
|
82
|
+
description: 'Subconscious platform origin for login, whoami, and usage.',
|
|
83
|
+
type: 'url',
|
|
84
|
+
},
|
|
79
85
|
API_KEY: {
|
|
80
86
|
key: 'API_KEY',
|
|
81
87
|
label: 'Shared API key',
|
|
@@ -711,7 +717,7 @@ Usage:
|
|
|
711
717
|
subc config edit [vim|nano]
|
|
712
718
|
subc -p NAME config edit [vim|nano]
|
|
713
719
|
subc config [show|path|list|create|delete]
|
|
714
|
-
[--gateway-url URL] [--api-key KEY]
|
|
720
|
+
[--gateway-url URL] [--platform-url URL] [--api-key KEY]
|
|
715
721
|
[--model MODEL|UNSET]
|
|
716
722
|
[--subagent-model MODEL|UNSET]
|
|
717
723
|
|
|
@@ -757,6 +763,7 @@ export async function configCommand(argv, profileName = DEFAULT_PROFILE, options
|
|
|
757
763
|
action = arg === 'interactive' ? 'edit' : arg;
|
|
758
764
|
} else if (
|
|
759
765
|
arg === '--gateway-url' ||
|
|
766
|
+
arg === '--platform-url' ||
|
|
760
767
|
arg === '--api-key' ||
|
|
761
768
|
arg === '--model' ||
|
|
762
769
|
arg === '--subagent-model'
|
|
@@ -765,6 +772,7 @@ export async function configCommand(argv, profileName = DEFAULT_PROFILE, options
|
|
|
765
772
|
if (!value) throw new Error(`${arg} requires a value`);
|
|
766
773
|
const key = {
|
|
767
774
|
'--gateway-url': 'GATEWAY_URL',
|
|
775
|
+
'--platform-url': 'PLATFORM_URL',
|
|
768
776
|
'--api-key': 'API_KEY',
|
|
769
777
|
'--model': 'MODEL',
|
|
770
778
|
'--subagent-model': 'CLAUDE_CODE_SUBAGENT_MODEL',
|
|
@@ -784,7 +792,7 @@ export async function configCommand(argv, profileName = DEFAULT_PROFILE, options
|
|
|
784
792
|
if (action === 'edit') {
|
|
785
793
|
if (Object.keys(updates).length) {
|
|
786
794
|
throw new Error(
|
|
787
|
-
'config edit cannot be combined with --gateway-url, --api-key, --model, or --subagent-model',
|
|
795
|
+
'config edit cannot be combined with --gateway-url, --platform-url, --api-key, --model, or --subagent-model',
|
|
788
796
|
);
|
|
789
797
|
}
|
|
790
798
|
await editProfile(profileName, editor);
|
|
@@ -855,27 +863,35 @@ export function modelsCommand(models = SUPPORTED_MODELS, options = {}) {
|
|
|
855
863
|
console.log();
|
|
856
864
|
}
|
|
857
865
|
|
|
858
|
-
export
|
|
859
|
-
|
|
860
|
-
|
|
866
|
+
export function validateOriginUrl(rawUrl, label = 'URL') {
|
|
867
|
+
const normalized = String(rawUrl ?? '').trim().replace(/\/+$/, '');
|
|
868
|
+
if (!normalized) {
|
|
869
|
+
throw new Error(`${label} cannot be blank`);
|
|
861
870
|
}
|
|
862
|
-
|
|
863
|
-
const gatewayUrl = argv[0].trim().replace(/\/+$/, '');
|
|
864
871
|
let parsed;
|
|
865
872
|
try {
|
|
866
|
-
parsed = new URL(
|
|
873
|
+
parsed = new URL(normalized);
|
|
867
874
|
} catch {
|
|
868
|
-
throw new Error(
|
|
875
|
+
throw new Error(`${label} must be a valid http:// or https:// URL`);
|
|
869
876
|
}
|
|
870
877
|
if (!['http:', 'https:'].includes(parsed.protocol) || !parsed.hostname) {
|
|
871
|
-
throw new Error(
|
|
878
|
+
throw new Error(`${label} must be a valid http:// or https:// URL`);
|
|
872
879
|
}
|
|
873
880
|
if (parsed.username || parsed.password) {
|
|
874
|
-
throw new Error(
|
|
881
|
+
throw new Error(`${label} cannot contain embedded credentials`);
|
|
875
882
|
}
|
|
876
883
|
if (parsed.search || parsed.hash) {
|
|
877
|
-
throw new Error(
|
|
884
|
+
throw new Error(`${label} cannot contain a query string or fragment`);
|
|
878
885
|
}
|
|
886
|
+
return normalized;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
export async function updateUrlCommand(argv = [], options = {}) {
|
|
890
|
+
if (argv.length !== 1 || !argv[0]?.trim()) {
|
|
891
|
+
throw new Error('Usage: subc update-url <gateway-url>');
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const gatewayUrl = validateOriginUrl(argv[0], 'Gateway URL');
|
|
879
895
|
|
|
880
896
|
const profileName = options.profileName || DEFAULT_PROFILE;
|
|
881
897
|
const profile = await updateProfile(profileName, { GATEWAY_URL: gatewayUrl });
|
|
@@ -897,3 +913,24 @@ export async function updateUrlCommand(argv = [], options = {}) {
|
|
|
897
913
|
}
|
|
898
914
|
console.log();
|
|
899
915
|
}
|
|
916
|
+
|
|
917
|
+
export async function updatePlatformUrlCommand(argv = [], options = {}) {
|
|
918
|
+
if (argv.length !== 1 || !argv[0]?.trim()) {
|
|
919
|
+
throw new Error('Usage: subc update-platform-url <platform-url>');
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
const platformUrl = validateOriginUrl(argv[0], 'Platform URL');
|
|
923
|
+
|
|
924
|
+
const profileName = options.profileName || DEFAULT_PROFILE;
|
|
925
|
+
const profile = await updateProfile(profileName, { PLATFORM_URL: platformUrl });
|
|
926
|
+
|
|
927
|
+
console.log(`\n ${c.green}${c.bold}✓ Platform URL updated.${c.reset}`);
|
|
928
|
+
console.log(` ${c.dim}Updated profile automatically: ${profile.path}${c.reset}`);
|
|
929
|
+
console.log(` ${c.dim}URL: ${platformUrl}${c.reset}`);
|
|
930
|
+
if (process.env.SUBCONSCIOUS_URL?.trim()) {
|
|
931
|
+
console.log(
|
|
932
|
+
`\n ${c.yellow}SUBCONSCIOUS_URL is set and will override this saved URL.${c.reset}`,
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
console.log();
|
|
936
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_generated": "Source of truth: agents/registry.json. Do not edit by hand.",
|
|
3
3
|
"defaults": {
|
|
4
|
-
"model": "subconscious/glm-5.
|
|
4
|
+
"model": "subconscious/glm-5.3-marathon",
|
|
5
5
|
"models": [
|
|
6
|
+
"subconscious/glm-5.3-marathon",
|
|
6
7
|
"subconscious/glm-5.2",
|
|
7
8
|
"subconscious/tim-qwen3.6-27b",
|
|
8
9
|
"subconscious/deepseek-v4-flash-marathon"
|
|
@@ -100,8 +101,8 @@
|
|
|
100
101
|
"ANTHROPIC_AUTH_TOKEN": "{apiKey}",
|
|
101
102
|
"ANTHROPIC_MODEL": "{model}",
|
|
102
103
|
"ANTHROPIC_SMALL_FAST_MODEL": "{model}",
|
|
103
|
-
"ANTHROPIC_DEFAULT_OPUS_MODEL": "subconscious/glm-5.
|
|
104
|
-
"ANTHROPIC_DEFAULT_OPUS_MODEL_NAME": "subconscious/glm-5.
|
|
104
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "subconscious/glm-5.3-marathon",
|
|
105
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL_NAME": "subconscious/glm-5.3-marathon",
|
|
105
106
|
"ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION": "Subconscious default model",
|
|
106
107
|
"ANTHROPIC_DEFAULT_SONNET_MODEL": "subconscious/tim-qwen3.6-27b",
|
|
107
108
|
"ANTHROPIC_DEFAULT_SONNET_MODEL_NAME": "subconscious/tim-qwen3.6-27b",
|
|
@@ -219,6 +220,14 @@
|
|
|
219
220
|
}
|
|
220
221
|
},
|
|
221
222
|
"models": {
|
|
223
|
+
"subconscious/glm-5.3-marathon": {
|
|
224
|
+
"name": "subconscious/glm-5.3-marathon",
|
|
225
|
+
"tools": true,
|
|
226
|
+
"limit": {
|
|
227
|
+
"context": 5000000,
|
|
228
|
+
"output": 65536
|
|
229
|
+
}
|
|
230
|
+
},
|
|
222
231
|
"subconscious/glm-5.2": {
|
|
223
232
|
"name": "subconscious/glm-5.2",
|
|
224
233
|
"tools": true,
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
#
|
|
21
21
|
# export ANTHROPIC_BASE_URL=https://your-gateway.example
|
|
22
22
|
# export ANTHROPIC_AUTH_TOKEN=sk-gw-...
|
|
23
|
-
# export ANTHROPIC_MODEL=subconscious/glm-5.
|
|
24
|
-
# export ANTHROPIC_SMALL_FAST_MODEL=subconscious/glm-5.
|
|
25
|
-
# export CLAUDE_CODE_SUBAGENT_MODEL=subconscious/glm-5.
|
|
23
|
+
# export ANTHROPIC_MODEL=subconscious/glm-5.3-marathon
|
|
24
|
+
# export ANTHROPIC_SMALL_FAST_MODEL=subconscious/glm-5.3-marathon
|
|
25
|
+
# export CLAUDE_CODE_SUBAGENT_MODEL=subconscious/glm-5.3-marathon
|
|
26
26
|
# export CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS=4
|
|
27
27
|
# export CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=1
|
|
28
28
|
# export CLAUDE_CODE_AUTO_COMPACT_WINDOW=1000000
|
|
@@ -57,7 +57,7 @@ if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
|
57
57
|
|
|
58
58
|
CLAUDE_DIR="${HOME}/.claude"
|
|
59
59
|
ENV_FILE="${CLAUDE_DIR}/subconscious-gateway.env"
|
|
60
|
-
DEFAULT_MODEL="subconscious/glm-5.
|
|
60
|
+
DEFAULT_MODEL="subconscious/glm-5.3-marathon"
|
|
61
61
|
DEFAULT_COMPACT_WINDOW="1000000"
|
|
62
62
|
DEFAULT_MAX_CONTEXT_TOKENS="3000000"
|
|
63
63
|
DEFAULT_MAX_CONCURRENT_SUBAGENTS="4"
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# ./run.sh # uses GATEWAY_URL/API_KEY from ../.env
|
|
9
9
|
# ./run.sh --continue # pass args through to claude
|
|
10
10
|
# ./run.sh --compact-window 1000000 -- --continue
|
|
11
|
-
# ./run.sh --model subconscious/glm-5.
|
|
11
|
+
# ./run.sh --model subconscious/glm-5.3-marathon -p "hi"
|
|
12
12
|
#
|
|
13
13
|
# Config: copy ../env.example to ../.env and edit. .env is gitignored.
|
|
14
14
|
# Profile env is injected by subc. A sibling .env is only used when SUBC_ENV_FILE is unset.
|
|
@@ -25,7 +25,7 @@ SHARED_ENV="${SUBC_ENV_FILE:-${SCRIPT_DIR}/../.env}"
|
|
|
25
25
|
[[ -f "$SHARED_ENV" ]] || SHARED_ENV="${SCRIPT_DIR}/../env.example"
|
|
26
26
|
if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
27
27
|
|
|
28
|
-
DEFAULT_MODEL="subconscious/glm-5.
|
|
28
|
+
DEFAULT_MODEL="subconscious/glm-5.3-marathon"
|
|
29
29
|
DEFAULT_COMPACT_WINDOW="1000000"
|
|
30
30
|
DEFAULT_MAX_CONTEXT_TOKENS="3000000"
|
|
31
31
|
DEFAULT_MAX_CONCURRENT_SUBAGENTS="4"
|
|
@@ -136,7 +136,7 @@ Usage:
|
|
|
136
136
|
Options (same as install.sh; flags override env for this run):
|
|
137
137
|
--gateway-url URL Gateway origin (e.g. https://gateway.example)
|
|
138
138
|
--api-key KEY Gateway API key (sk-gw-...)
|
|
139
|
-
--model MODEL Model name (default: subconscious/glm-5.
|
|
139
|
+
--model MODEL Model name (default: subconscious/glm-5.3-marathon)
|
|
140
140
|
--compact-window N CLAUDE_CODE_AUTO_COMPACT_WINDOW (default: 1000000; Claude Code clamps to 100000–1000000)
|
|
141
141
|
See https://code.claude.com/docs/en/env-vars and
|
|
142
142
|
https://code.claude.com/docs/en/context-window#set-the-auto-compact-window
|
package/bin/runbook/codex/run.sh
CHANGED
|
@@ -39,7 +39,7 @@ if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
|
39
39
|
|
|
40
40
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
41
41
|
API_KEY="${CODEX_API_KEY:-${API_KEY:-}}"
|
|
42
|
-
MODEL="${MODEL:-subconscious/glm-5.
|
|
42
|
+
MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
|
|
43
43
|
MAX_CONCURRENT_SUBAGENTS="${MAX_CONCURRENT_SUBAGENTS:-4}"
|
|
44
44
|
SUBAGENTS=false
|
|
45
45
|
EXTERNAL_TOOLS="${CODEX_EXTERNAL_TOOLS:-false}"
|
|
@@ -102,7 +102,8 @@ case "$CODEX_REASONING_EFFORT" in
|
|
|
102
102
|
;;
|
|
103
103
|
esac
|
|
104
104
|
|
|
105
|
-
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.
|
|
105
|
+
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
|
|
106
|
+
subconscious/glm-5.2
|
|
106
107
|
subconscious/tim-qwen3.6-27b
|
|
107
108
|
subconscious/deepseek-v4-flash-marathon"
|
|
108
109
|
SUPPORTED_MODELS=()
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
# "apiType": "messages",
|
|
34
34
|
# "models": [
|
|
35
35
|
# {
|
|
36
|
-
# "id": "subconscious/glm-5.
|
|
37
|
-
# "name": "Subconscious GLM 5.
|
|
36
|
+
# "id": "subconscious/glm-5.3-marathon",
|
|
37
|
+
# "name": "Subconscious GLM 5.3 Marathon",
|
|
38
38
|
# "url": "https://your-gateway.example/v1/messages",
|
|
39
39
|
# "toolCalling": true,
|
|
40
40
|
# "vision": false,
|
|
@@ -77,7 +77,7 @@ if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
|
77
77
|
COMMAND="install"
|
|
78
78
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
79
79
|
API_KEY="${COPILOT_API_KEY:-${API_KEY:-}}"
|
|
80
|
-
MODEL="${MODEL:-subconscious/glm-5.
|
|
80
|
+
MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
|
|
81
81
|
MAX_INPUT_TOKENS="${COPILOT_MAX_INPUT_TOKENS:-5000000}"
|
|
82
82
|
MAX_OUTPUT_TOKENS="${COPILOT_MAX_OUTPUT_TOKENS:-65536}"
|
|
83
83
|
VSCODE_APP="${VSCODE_APP:-}" # auto-detected: Code | Code - Insiders | VSCodium
|
|
@@ -122,7 +122,7 @@ default. Restart VS Code after install.
|
|
|
122
122
|
Options:
|
|
123
123
|
--gateway-url URL Gateway origin (default: $GATEWAY_URL from .env)
|
|
124
124
|
--api-key KEY Gateway API key for hooks (default: $API_KEY from .env)
|
|
125
|
-
--model MODEL Model id (default: subconscious/glm-5.
|
|
125
|
+
--model MODEL Model id (default: subconscious/glm-5.3-marathon)
|
|
126
126
|
--max-input-tokens N Model context window input tokens (default: 5000000)
|
|
127
127
|
--max-output-tokens N Model max output tokens (default: 65536)
|
|
128
128
|
--vscode-app APP Code, Code - Insiders, or VSCodium (auto-detected)
|
|
@@ -174,7 +174,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
174
174
|
esac
|
|
175
175
|
done
|
|
176
176
|
|
|
177
|
-
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.
|
|
177
|
+
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
|
|
178
|
+
subconscious/glm-5.2
|
|
178
179
|
subconscious/tim-qwen3.6-27b
|
|
179
180
|
subconscious/deepseek-v4-flash-marathon"
|
|
180
181
|
SUPPORTED_MODELS=()
|
|
@@ -53,10 +53,11 @@ if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
|
53
53
|
|
|
54
54
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
55
55
|
API_KEY="${CURSOR_API_KEY:-${API_KEY:-}}"
|
|
56
|
-
MODEL="${MODEL:-subconscious/glm-5.
|
|
56
|
+
MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
|
|
57
57
|
COMMAND="install"
|
|
58
58
|
|
|
59
|
-
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.
|
|
59
|
+
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
|
|
60
|
+
subconscious/glm-5.2
|
|
60
61
|
subconscious/tim-qwen3.6-27b
|
|
61
62
|
subconscious/deepseek-v4-flash-marathon"
|
|
62
63
|
|
|
@@ -6,11 +6,12 @@ set -euo pipefail
|
|
|
6
6
|
|
|
7
7
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
8
8
|
API_KEY="${DEEPSEEK_HARNESS_API_KEY:-${API_KEY:-}}"
|
|
9
|
-
MODEL="${MODEL:-subconscious/glm-5.
|
|
9
|
+
MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
|
|
10
10
|
CONTEXT_WINDOW="${DEEPSEEK_HARNESS_CONTEXT_WINDOW:-5000000}"
|
|
11
11
|
MAX_TOKENS="${DEEPSEEK_HARNESS_MAX_TOKENS:-65536}"
|
|
12
12
|
|
|
13
|
-
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.
|
|
13
|
+
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
|
|
14
|
+
subconscious/glm-5.2
|
|
14
15
|
subconscious/tim-qwen3.6-27b
|
|
15
16
|
subconscious/deepseek-v4-flash-marathon"
|
|
16
17
|
|
|
@@ -28,15 +28,15 @@
|
|
|
28
28
|
# "headers": { "x-subconscious-client": "opencode" }
|
|
29
29
|
# },
|
|
30
30
|
# "models": {
|
|
31
|
-
# "subconscious/glm-5.
|
|
32
|
-
# "name": "subconscious/glm-5.
|
|
31
|
+
# "subconscious/glm-5.3-marathon": {
|
|
32
|
+
# "name": "subconscious/glm-5.3-marathon",
|
|
33
33
|
# "tools": true,
|
|
34
34
|
# "limit": { "context": 5000000, "output": 65536 }
|
|
35
35
|
# }
|
|
36
36
|
# }
|
|
37
37
|
# }
|
|
38
38
|
# },
|
|
39
|
-
# "model": "subconscious/subconscious/glm-5.
|
|
39
|
+
# "model": "subconscious/subconscious/glm-5.3-marathon"
|
|
40
40
|
# }
|
|
41
41
|
# EOF
|
|
42
42
|
# opencode
|
|
@@ -63,7 +63,7 @@ if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
|
63
63
|
COMMAND="install"
|
|
64
64
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
65
65
|
API_KEY="${OPENCODE_API_KEY:-${API_KEY:-}}"
|
|
66
|
-
MODEL="${MODEL:-subconscious/glm-5.
|
|
66
|
+
MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
|
|
67
67
|
# OpenCode auto-compaction uses model limit.context. Custom openai-compatible
|
|
68
68
|
# providers do not get that from baseURL/models.dev; set it explicitly.
|
|
69
69
|
CONTEXT_LIMIT="${OPENCODE_CONTEXT_LIMIT:-5000000}"
|
|
@@ -119,7 +119,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
119
119
|
esac
|
|
120
120
|
done
|
|
121
121
|
|
|
122
|
-
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.
|
|
122
|
+
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
|
|
123
|
+
subconscious/glm-5.2
|
|
123
124
|
subconscious/tim-qwen3.6-27b
|
|
124
125
|
subconscious/deepseek-v4-flash-marathon"
|
|
125
126
|
SUPPORTED_MODELS=()
|
|
@@ -26,11 +26,12 @@ if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
|
26
26
|
|
|
27
27
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
28
28
|
API_KEY="${OPENCODE_API_KEY:-${API_KEY:-}}"
|
|
29
|
-
MODEL="${MODEL:-subconscious/glm-5.
|
|
29
|
+
MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
|
|
30
30
|
CONTEXT_LIMIT="${OPENCODE_CONTEXT_LIMIT:-5000000}"
|
|
31
31
|
OUTPUT_LIMIT="${OPENCODE_OUTPUT_LIMIT:-65536}"
|
|
32
32
|
|
|
33
|
-
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.
|
|
33
|
+
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
|
|
34
|
+
subconscious/glm-5.2
|
|
34
35
|
subconscious/tim-qwen3.6-27b
|
|
35
36
|
subconscious/deepseek-v4-flash-marathon"
|
|
36
37
|
SUPPORTED_MODELS=()
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
# "apiKey": "sk-gw-...",
|
|
22
22
|
# "headers": { "x-subconscious-client": "pi" },
|
|
23
23
|
# "models": [{
|
|
24
|
-
# "id": "subconscious/glm-5.
|
|
24
|
+
# "id": "subconscious/glm-5.3-marathon",
|
|
25
25
|
# "contextWindow": 5000000,
|
|
26
26
|
# "maxTokens": 65536,
|
|
27
27
|
# "compat": {
|
|
@@ -57,7 +57,7 @@ if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
|
57
57
|
COMMAND="install"
|
|
58
58
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
59
59
|
API_KEY="${PI_API_KEY:-${API_KEY:-}}"
|
|
60
|
-
MODEL="${MODEL:-subconscious/glm-5.
|
|
60
|
+
MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
|
|
61
61
|
CONTEXT_WINDOW="${PI_CONTEXT_WINDOW:-5000000}"
|
|
62
62
|
MAX_TOKENS="${PI_MAX_TOKENS:-65536}"
|
|
63
63
|
|
|
@@ -114,7 +114,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
114
114
|
esac
|
|
115
115
|
done
|
|
116
116
|
|
|
117
|
-
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.
|
|
117
|
+
DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
|
|
118
|
+
subconscious/glm-5.2
|
|
118
119
|
subconscious/tim-qwen3.6-27b
|
|
119
120
|
subconscious/deepseek-v4-flash-marathon"
|
|
120
121
|
SUPPORTED_MODELS=()
|
package/bin/runbook/pi/run.sh
CHANGED
|
@@ -7,7 +7,7 @@ set -euo pipefail
|
|
|
7
7
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
8
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
9
9
|
API_KEY="${PI_API_KEY:-${API_KEY:-}}"
|
|
10
|
-
MODEL="${MODEL:-subconscious/glm-5.
|
|
10
|
+
MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
|
|
11
11
|
PI_DIR="${PI_CODING_AGENT_DIR:-${HOME}/.pi/agent}"
|
|
12
12
|
MODELS_JSON="${PI_DIR}/models.json"
|
|
13
13
|
|
|
@@ -5,7 +5,7 @@ set -euo pipefail
|
|
|
5
5
|
|
|
6
6
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
7
7
|
API_KEY="${SC_API_KEY:-${API_KEY:-}}"
|
|
8
|
-
MODEL="${MODEL:-subconscious/glm-5.
|
|
8
|
+
MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
|
|
9
9
|
|
|
10
10
|
if [[ -z "$GATEWAY_URL" || -z "$API_KEY" ]]; then
|
|
11
11
|
echo "error: GATEWAY_URL and API_KEY are required to launch Subconscious Code" >&2
|
package/bin/tui.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
|
|
7
7
|
import { agentList } from './agents.js';
|
|
8
|
-
import { getApiKey } from './auth.js';
|
|
8
|
+
import { DEFAULT_PLATFORM_URL, getApiKey, getPlatformUrl } from './auth.js';
|
|
9
9
|
import { resolveModelCatalog } from './models.js';
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_PROFILE,
|
|
@@ -86,6 +86,10 @@ function gatewayFor(profile) {
|
|
|
86
86
|
).replace(/\/+$/, '');
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
function platformFor(profile) {
|
|
90
|
+
return getPlatformUrl(profile);
|
|
91
|
+
}
|
|
92
|
+
|
|
89
93
|
async function packageVersion() {
|
|
90
94
|
const pkg = JSON.parse(
|
|
91
95
|
await fs.readFile(new URL('../package.json', import.meta.url), 'utf-8'),
|
|
@@ -116,6 +120,7 @@ export async function createTuiState(profileName = DEFAULT_PROFILE, options = {}
|
|
|
116
120
|
const auth = await getApiKey(activeProfile);
|
|
117
121
|
const requestedModel = selectedModelFor(activeProfile);
|
|
118
122
|
const gatewayUrl = gatewayFor(activeProfile);
|
|
123
|
+
const platformUrl = platformFor(activeProfile);
|
|
119
124
|
const catalog = await resolveModelCatalog({
|
|
120
125
|
baseUrl: gatewayUrl,
|
|
121
126
|
apiKey: auth?.key,
|
|
@@ -137,6 +142,10 @@ export async function createTuiState(profileName = DEFAULT_PROFILE, options = {}
|
|
|
137
142
|
activeProfile.values.GATEWAY_URL?.trim().replace(/\/+$/, '') ||
|
|
138
143
|
RUNBOOK_DEFAULTS.GATEWAY_URL,
|
|
139
144
|
gatewayOverridden: Boolean(process.env.SUBCONSCIOUS_BASE_URL?.trim()),
|
|
145
|
+
platformUrl,
|
|
146
|
+
savedPlatformUrl:
|
|
147
|
+
activeProfile.values.PLATFORM_URL?.trim().replace(/\/+$/, '') || DEFAULT_PLATFORM_URL,
|
|
148
|
+
platformOverridden: Boolean(process.env.SUBCONSCIOUS_URL?.trim()),
|
|
140
149
|
modelError: catalog.error?.message || '',
|
|
141
150
|
modelSource: catalog.source,
|
|
142
151
|
agents: agentList().map((agent) => ({
|
package/bin/usage.js
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage display for `subc usage` — fetches billing/quota from the platform API.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { c } from './colors.js';
|
|
6
|
+
import { getApiKey, getPlatformUrl } from './auth.js';
|
|
7
|
+
import { DEFAULT_PROFILE } from './profiles.js';
|
|
8
|
+
import { printLoginUpgradeWarning } from './upgrade.js';
|
|
9
|
+
|
|
10
|
+
export const USAGE_API_PATH = '/api/v1/usage';
|
|
11
|
+
|
|
12
|
+
export function formatTokens(value) {
|
|
13
|
+
const n = Number(value);
|
|
14
|
+
if (!Number.isFinite(n) || n < 0) return '0';
|
|
15
|
+
if (n >= 1_000_000_000) {
|
|
16
|
+
const scaled = n / 1_000_000_000;
|
|
17
|
+
return `${scaled.toFixed(n % 1_000_000_000 ? 1 : 0)}B`;
|
|
18
|
+
}
|
|
19
|
+
if (n >= 1_000_000) {
|
|
20
|
+
const scaled = n / 1_000_000;
|
|
21
|
+
return `${scaled.toFixed(n % 1_000_000 ? 1 : 0)}M`;
|
|
22
|
+
}
|
|
23
|
+
if (n >= 1_000) return `${Math.round(n / 1_000)}K`;
|
|
24
|
+
return n.toLocaleString('en-US');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function formatCurrency(value) {
|
|
28
|
+
const n = Number(value);
|
|
29
|
+
if (!Number.isFinite(n)) return '$0.00';
|
|
30
|
+
const sign = n < 0 ? '-' : '';
|
|
31
|
+
return `${sign}$${Math.abs(n).toFixed(2)}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function shortModelSlug(slug) {
|
|
35
|
+
const text = String(slug ?? '').trim();
|
|
36
|
+
return text.startsWith('subconscious/') ? text.slice('subconscious/'.length) : text;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function isLegacyUsagePayload(data) {
|
|
40
|
+
if (!data || typeof data !== 'object') return true;
|
|
41
|
+
if ('credits' in data && data.plan && typeof data.plan === 'object') return false;
|
|
42
|
+
return 'dailyTokenCeiling' in data || 'consumedToday' in data || !('plan' in data);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function meterColor(percentage) {
|
|
46
|
+
if (percentage >= 100) return c.red;
|
|
47
|
+
if (percentage >= 80) return c.yellow;
|
|
48
|
+
return c.green;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function renderProgressBar(percentage, width = 20) {
|
|
52
|
+
const pct = Math.max(0, Math.min(100, percentage));
|
|
53
|
+
const filled = Math.round((pct / 100) * width);
|
|
54
|
+
const empty = Math.max(0, width - filled);
|
|
55
|
+
const color = meterColor(pct);
|
|
56
|
+
return `${color}${'█'.repeat(filled)}${c.dim}${'░'.repeat(empty)}${c.reset}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function formatUsageDisplay(data) {
|
|
60
|
+
const lines = [];
|
|
61
|
+
lines.push(`\n ${c.bold}Subconscious Usage${c.reset}\n`);
|
|
62
|
+
|
|
63
|
+
const billingMode = data.billingMode ?? null;
|
|
64
|
+
const plan = data.plan ?? {};
|
|
65
|
+
const credits = data.credits ?? {};
|
|
66
|
+
const models = Array.isArray(data.models) ? data.models : [];
|
|
67
|
+
|
|
68
|
+
const planLabel = plan.label || 'No plan';
|
|
69
|
+
const planStatus = plan.status ? plan.status.replace(/_/g, ' ') : null;
|
|
70
|
+
const planLine = planStatus ? `${planLabel} · ${planStatus}` : planLabel;
|
|
71
|
+
|
|
72
|
+
lines.push(` ${c.dim}Plan${c.reset} ${planLine}`);
|
|
73
|
+
if (billingMode) {
|
|
74
|
+
lines.push(` ${c.dim}Billing${c.reset} ${billingMode}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const allowance = plan.dailyAllowance;
|
|
78
|
+
const unlimitedComp = Boolean(plan.isUnlimitedComp);
|
|
79
|
+
|
|
80
|
+
if (unlimitedComp) {
|
|
81
|
+
lines.push('');
|
|
82
|
+
lines.push(` ${c.dim}Daily allowance${c.reset}`);
|
|
83
|
+
lines.push(` ${c.green}Unlimited${c.reset} ${c.dim}(comp)${c.reset}`);
|
|
84
|
+
} else if (allowance && allowance.amount > 0) {
|
|
85
|
+
const consumed = Number(allowance.consumed) || 0;
|
|
86
|
+
const amount = Number(allowance.amount) || 0;
|
|
87
|
+
const remaining = Number(allowance.remaining) || 0;
|
|
88
|
+
const pct = amount > 0 ? Math.min((consumed / amount) * 100, 100) : 0;
|
|
89
|
+
const overage = Math.max(0, consumed - amount);
|
|
90
|
+
|
|
91
|
+
lines.push('');
|
|
92
|
+
lines.push(` ${c.dim}Daily allowance${c.reset}`);
|
|
93
|
+
lines.push(
|
|
94
|
+
` ${renderProgressBar(pct)} ${formatTokens(consumed)} / ${formatTokens(amount)} tokens`,
|
|
95
|
+
);
|
|
96
|
+
const resetNote = allowance.resetAt
|
|
97
|
+
? `${formatTokens(remaining)} remaining · resets midnight UTC`
|
|
98
|
+
: `${formatTokens(remaining)} remaining`;
|
|
99
|
+
lines.push(` ${c.dim}${resetNote}${c.reset}`);
|
|
100
|
+
if (overage > 0) {
|
|
101
|
+
lines.push(` ${c.yellow}+${formatTokens(overage)} over daily allowance${c.reset}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
lines.push('');
|
|
106
|
+
lines.push(
|
|
107
|
+
` ${c.dim}Credits${c.reset} ${formatCurrency(credits.balanceDollars ?? 0)}`,
|
|
108
|
+
);
|
|
109
|
+
if (billingMode === 'subscription' || billingMode === 'comp' || allowance) {
|
|
110
|
+
const overage = Number(credits.overageThisPeriodDollars) || 0;
|
|
111
|
+
const overageColor = overage > 0 ? c.yellow : c.reset;
|
|
112
|
+
lines.push(
|
|
113
|
+
` ${c.dim}Overage${c.reset} ${overageColor}${formatCurrency(overage)} this period${c.reset}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (models.length > 0) {
|
|
118
|
+
lines.push('');
|
|
119
|
+
lines.push(` ${c.dim}Models today${c.reset}`);
|
|
120
|
+
const slugWidth = Math.max(
|
|
121
|
+
12,
|
|
122
|
+
...models.map((model) => shortModelSlug(model.slug).length),
|
|
123
|
+
);
|
|
124
|
+
for (const model of models) {
|
|
125
|
+
const label = shortModelSlug(model.slug).padEnd(slugWidth);
|
|
126
|
+
lines.push(
|
|
127
|
+
` ${label} ${formatTokens(model.consumedToday ?? 0)}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
lines.push('');
|
|
133
|
+
return lines.join('\n');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function fetchUsage(apiKey, platformUrl, fetchImpl = fetch) {
|
|
137
|
+
const res = await fetchImpl(`${platformUrl.replace(/\/$/, '')}${USAGE_API_PATH}`, {
|
|
138
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
139
|
+
signal: AbortSignal.timeout(5000),
|
|
140
|
+
});
|
|
141
|
+
return res;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function usageCommand(argv = [], options = {}) {
|
|
145
|
+
const jsonOutput = argv.includes('--json');
|
|
146
|
+
const profileFlag =
|
|
147
|
+
options.profileName && options.profileName !== DEFAULT_PROFILE
|
|
148
|
+
? `--profile ${options.profileName} `
|
|
149
|
+
: '';
|
|
150
|
+
|
|
151
|
+
const auth = await getApiKey(options.profile);
|
|
152
|
+
if (!auth) {
|
|
153
|
+
console.log(`\n ${c.dim}Not logged in.${c.reset}`);
|
|
154
|
+
console.log(` Run ${c.cyan}subc ${profileFlag}login${c.reset} to get started.\n`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const platformUrl = getPlatformUrl(options.profile);
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const res = await fetchUsage(auth.key, platformUrl);
|
|
162
|
+
|
|
163
|
+
if (res.status === 404) {
|
|
164
|
+
printLoginUpgradeWarning();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let data;
|
|
169
|
+
try {
|
|
170
|
+
data = await res.json();
|
|
171
|
+
} catch {
|
|
172
|
+
throw new Error('Platform returned an invalid response');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!res.ok) {
|
|
176
|
+
const message = data?.error || `Request failed (${res.status})`;
|
|
177
|
+
if (res.status === 401) {
|
|
178
|
+
console.log(`\n ${c.red}✗ ${message}${c.reset}`);
|
|
179
|
+
console.log(
|
|
180
|
+
` Run ${c.cyan}subc ${profileFlag}logout${c.reset} then ${c.cyan}subc ${profileFlag}login${c.reset} to re-authenticate.\n`,
|
|
181
|
+
);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (res.status === 403) {
|
|
185
|
+
console.log(`\n ${c.red}✗ ${message}${c.reset}\n`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
throw new Error(message);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (isLegacyUsagePayload(data)) {
|
|
192
|
+
console.log(`\n ${c.yellow}Platform upgrade required.${c.reset}`);
|
|
193
|
+
console.log(
|
|
194
|
+
` ${c.dim}This platform host returns a legacy usage shape. Deploy the latest platform or point PLATFORM_URL at a current host.${c.reset}\n`,
|
|
195
|
+
);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (jsonOutput) {
|
|
200
|
+
console.log(JSON.stringify(data, null, 2));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log(formatUsageDisplay(data));
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (error.name === 'TimeoutError' || error.name === 'AbortError') {
|
|
207
|
+
console.log(`\n ${c.yellow}Could not reach the platform (timed out).${c.reset}`);
|
|
208
|
+
console.log(` ${c.dim}Host: ${platformUrl}${c.reset}\n`);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
console.error(`\n ${c.red}${error.message}${c.reset}\n`);
|
|
212
|
+
process.exitCode = 1;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function printUsageHelp() {
|
|
217
|
+
console.log(`
|
|
218
|
+
Usage:
|
|
219
|
+
subc usage
|
|
220
|
+
subc -p NAME usage
|
|
221
|
+
subc usage --json
|
|
222
|
+
subc usage help
|
|
223
|
+
|
|
224
|
+
Show billing mode, daily token allowance, credit balance, and per-model usage
|
|
225
|
+
for the authenticated organization.
|
|
226
|
+
|
|
227
|
+
--json Print the raw platform JSON response
|
|
228
|
+
`);
|
|
229
|
+
}
|
package/package.json
CHANGED