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 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 **Create profile**,
38
- **Set default model**, **Set subagent model**, and **Update base URL** actions.
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.2
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.2
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.2 (default)
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.2
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.2
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 raw = process.env.SUBCONSCIOUS_URL?.trim() || DEFAULT_PLATFORM_URL;
39
- return raw.replace(/\/$/, '');
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
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 async function updateUrlCommand(argv = [], options = {}) {
859
- if (argv.length !== 1 || !argv[0]?.trim()) {
860
- throw new Error('Usage: subc update-url <gateway-url>');
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(gatewayUrl);
873
+ parsed = new URL(normalized);
867
874
  } catch {
868
- throw new Error('Gateway URL must be a valid http:// or https:// URL');
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('Gateway URL must be a valid http:// or https:// URL');
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('Gateway URL cannot contain embedded credentials');
881
+ throw new Error(`${label} cannot contain embedded credentials`);
875
882
  }
876
883
  if (parsed.search || parsed.hash) {
877
- throw new Error('Gateway URL cannot contain a query string or fragment');
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.2",
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.2",
104
- "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME": "subconscious/glm-5.2",
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.2
24
- # export ANTHROPIC_SMALL_FAST_MODEL=subconscious/glm-5.2
25
- # export CLAUDE_CODE_SUBAGENT_MODEL=subconscious/glm-5.2
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.2"
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.2 -p "hi"
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.2"
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.2)
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
@@ -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.2}"
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.2
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.2",
37
- # "name": "Subconscious GLM 5.2",
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.2}"
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.2)
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.2
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.2}"
56
+ MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
57
57
  COMMAND="install"
58
58
 
59
- DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.2
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.2}"
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.2
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.2": {
32
- # "name": "subconscious/glm-5.2",
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.2"
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.2}"
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.2
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.2}"
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.2
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.2",
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.2}"
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.2
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=()
@@ -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.2}"
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.2}"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subconscious-cli",
3
- "version": "4.0.17",
3
+ "version": "4.0.18",
4
4
  "description": "CLI for Subconscious — run Subconscious Code, Claude Code, Codex, OpenCode, DeepSeek Harness, Cursor, Copilot, and Pi",
5
5
  "bin": {
6
6
  "subc": "bin/cli.js"