subconscious-cli 6.0.0 → 6.0.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/bin/tui.js CHANGED
@@ -6,22 +6,29 @@ import { fileURLToPath } from 'node:url';
6
6
 
7
7
  import { agentList } from './agents.js';
8
8
  import { DEFAULT_PLATFORM_URL, getApiKey, getPlatformUrl } from './auth.js';
9
- import { isAbortError, normalizeModelIds, resolveModelCatalog } from './models.js';
10
- import { discoverSessions, SESSION_HARNESSES } from './sessions.js';
11
- import { compareVersions, fetchLatestVersion } from './update-check.js';
9
+ import {
10
+ isAbortError,
11
+ normalizeModelIds,
12
+ resolveModelCatalog,
13
+ } from './models.js';
12
14
  import {
13
15
  DEFAULT_PROFILE,
14
16
  listProfiles,
15
17
  loadProfile,
16
- resolvedModelSetting,
17
- RUNBOOK_DEFAULTS,
18
18
  SUPPORTED_MODELS as PACKAGED_MODELS,
19
+ RUNBOOK_DEFAULTS,
20
+ resolvedModelSetting,
19
21
  } from './profiles.js';
22
+ import { discoverSessions, SESSION_HARNESSES } from './sessions.js';
23
+ import { compareVersions, fetchLatestVersion } from './update-check.js';
20
24
 
21
25
  const BIN_DIR = path.dirname(fileURLToPath(import.meta.url));
22
26
  const TUI_SOURCE_DIR = path.resolve(BIN_DIR, '../tui');
23
27
 
24
- export function nativeTargetName(platform = process.platform, arch = process.arch) {
28
+ export function nativeTargetName(
29
+ platform = process.platform,
30
+ arch = process.arch,
31
+ ) {
25
32
  const goArch = { x64: 'amd64', arm64: 'arm64' }[arch];
26
33
  if (!goArch || !['darwin', 'linux', 'win32'].includes(platform)) return null;
27
34
  const goOS = platform === 'win32' ? 'windows' : platform;
@@ -34,10 +41,13 @@ export function isTuiResult(result) {
34
41
  result !== null &&
35
42
  typeof result === 'object' &&
36
43
  (result.updatePrompt === true ||
37
- (Array.isArray(result.args) && result.args.every((arg) => typeof arg === 'string'))) &&
44
+ (Array.isArray(result.args) &&
45
+ result.args.every((arg) => typeof arg === 'string'))) &&
38
46
  (result.baseUrl === undefined || typeof result.baseUrl === 'string') &&
39
- (result.installedVersion === undefined || typeof result.installedVersion === 'string') &&
40
- (result.latestVersion === undefined || typeof result.latestVersion === 'string')
47
+ (result.installedVersion === undefined ||
48
+ typeof result.installedVersion === 'string') &&
49
+ (result.latestVersion === undefined ||
50
+ typeof result.latestVersion === 'string')
41
51
  );
42
52
  }
43
53
 
@@ -50,9 +60,15 @@ async function pathExists(file) {
50
60
  }
51
61
  }
52
62
 
53
- export async function tuiSourceIsNewerThan(binary, sourceFile = path.join(TUI_SOURCE_DIR, 'cmd/subc-tui/main.go')) {
63
+ export async function tuiSourceIsNewerThan(
64
+ binary,
65
+ sourceFile = path.join(TUI_SOURCE_DIR, 'cmd/subc-tui/main.go'),
66
+ ) {
54
67
  try {
55
- const [binaryStat, sourceStat] = await Promise.all([fs.stat(binary), fs.stat(sourceFile)]);
68
+ const [binaryStat, sourceStat] = await Promise.all([
69
+ fs.stat(binary),
70
+ fs.stat(sourceFile),
71
+ ]);
56
72
  return sourceStat.mtimeMs > binaryStat.mtimeMs;
57
73
  } catch {
58
74
  return false;
@@ -70,7 +86,11 @@ function sourceTuiCommand() {
70
86
  export async function resolveTuiExecutable(options = {}) {
71
87
  const override = options.binary || process.env.SUBC_TUI_BIN?.trim();
72
88
  if (override) {
73
- return { command: override, args: options.binaryArgs || [], cwd: undefined };
89
+ return {
90
+ command: override,
91
+ args: options.binaryArgs || [],
92
+ cwd: undefined,
93
+ };
74
94
  }
75
95
 
76
96
  const hasSource = await pathExists(path.join(TUI_SOURCE_DIR, 'go.mod'));
@@ -79,7 +99,10 @@ export async function resolveTuiExecutable(options = {}) {
79
99
  const packaged = path.join(BIN_DIR, 'native', target);
80
100
  // Source checkouts keep a cached host binary for speed. If TUI source is
81
101
  // newer, that cache is stale (for example it will reject --updates).
82
- if (await pathExists(packaged) && !(hasSource && (await tuiSourceIsNewerThan(packaged)))) {
102
+ if (
103
+ (await pathExists(packaged)) &&
104
+ !(hasSource && (await tuiSourceIsNewerThan(packaged)))
105
+ ) {
83
106
  return { command: packaged, args: [], cwd: undefined };
84
107
  }
85
108
  }
@@ -91,7 +114,9 @@ export async function resolveTuiExecutable(options = {}) {
91
114
  }
92
115
 
93
116
  function selectedModelFor(profile) {
94
- return resolvedModelSetting(process.env.SUBCONSCIOUS_MODEL || profile.values.MODEL);
117
+ return resolvedModelSetting(
118
+ process.env.SUBCONSCIOUS_MODEL || profile.values.MODEL,
119
+ );
95
120
  }
96
121
 
97
122
  function subagentModelFor(profile) {
@@ -181,16 +206,22 @@ export async function writeAtomicJson(file, value) {
181
206
  }
182
207
  }
183
208
 
184
- export async function createLocalTuiState(profileName = DEFAULT_PROFILE, options = {}) {
209
+ export async function createLocalTuiState(
210
+ profileName = DEFAULT_PROFILE,
211
+ options = {},
212
+ ) {
185
213
  const activeProfile = options.profile || (await loadProfile(profileName));
186
- const names = [...new Set([profileName, ...(await listProfiles())])].sort((a, b) => {
187
- if (a === profileName) return -1;
188
- if (b === profileName) return 1;
189
- return a.localeCompare(b);
190
- });
214
+ const names = [...new Set([profileName, ...(await listProfiles())])].sort(
215
+ (a, b) => {
216
+ if (a === profileName) return -1;
217
+ if (b === profileName) return 1;
218
+ return a.localeCompare(b);
219
+ },
220
+ );
191
221
  const profiles = await Promise.all(
192
222
  names.map(async (name) => {
193
- const profile = name === profileName ? activeProfile : await loadProfile(name);
223
+ const profile =
224
+ name === profileName ? activeProfile : await loadProfile(name);
194
225
  const auth = await getApiKey(profile);
195
226
  return {
196
227
  name,
@@ -210,7 +241,10 @@ export async function createLocalTuiState(profileName = DEFAULT_PROFILE, options
210
241
  activeProfile: profileName,
211
242
  profilePath: activeProfile.path,
212
243
  profiles,
213
- models: normalizeModelIds([requestedModel, ...PACKAGED_MODELS], requestedModel),
244
+ models: normalizeModelIds(
245
+ [requestedModel, ...PACKAGED_MODELS],
246
+ requestedModel,
247
+ ),
214
248
  selectedModel: requestedModel,
215
249
  subagentModel: subagentModelFor(activeProfile),
216
250
  gatewayUrl,
@@ -220,7 +254,8 @@ export async function createLocalTuiState(profileName = DEFAULT_PROFILE, options
220
254
  gatewayOverridden: Boolean(process.env.SUBCONSCIOUS_BASE_URL?.trim()),
221
255
  platformUrl,
222
256
  savedPlatformUrl:
223
- activeProfile.values.PLATFORM_URL?.trim().replace(/\/+$/, '') || DEFAULT_PLATFORM_URL,
257
+ activeProfile.values.PLATFORM_URL?.trim().replace(/\/+$/, '') ||
258
+ DEFAULT_PLATFORM_URL,
224
259
  platformOverridden: Boolean(process.env.SUBCONSCIOUS_URL?.trim()),
225
260
  modelError: '',
226
261
  modelSource: 'packaged',
@@ -274,11 +309,13 @@ export async function loadRemoteTuiUpdates(state, options = {}) {
274
309
  const fetchLatest = options.fetchLatestVersion || fetchLatestVersion;
275
310
  const readApiKey = options.getApiKey || getApiKey;
276
311
  const updateDisabled =
277
- options.disableUpdateCheck ?? process.env.SUBC_DISABLE_UPDATE_CHECK?.trim() === '1';
312
+ options.disableUpdateCheck ??
313
+ process.env.SUBC_DISABLE_UPDATE_CHECK?.trim() === '1';
278
314
 
279
315
  const tasks = [
280
316
  (async () => {
281
- const profile = options.profile || (await loadProfile(state.activeProfile));
317
+ const profile =
318
+ options.profile || (await loadProfile(state.activeProfile));
282
319
  if (signal?.aborted) return;
283
320
  const auth = await readApiKey(profile);
284
321
  if (signal?.aborted) return;
@@ -330,7 +367,10 @@ export async function loadRemoteTuiUpdates(state, options = {}) {
330
367
  });
331
368
  if (signal?.aborted || !latest) return;
332
369
  if (compareVersions(latest, state.version) > 0) {
333
- await enqueuePatch({ updateAvailable: true, latestVersion: latest });
370
+ await enqueuePatch({
371
+ updateAvailable: true,
372
+ latestVersion: latest,
373
+ });
334
374
  }
335
375
  } catch (error) {
336
376
  if (isAbortError(error) || signal?.aborted) return;
@@ -347,7 +387,10 @@ export async function loadRemoteTuiUpdates(state, options = {}) {
347
387
  }
348
388
  }
349
389
 
350
- export async function createTuiState(profileName = DEFAULT_PROFILE, options = {}) {
390
+ export async function createTuiState(
391
+ profileName = DEFAULT_PROFILE,
392
+ options = {},
393
+ ) {
351
394
  const state = await createLocalTuiState(profileName, options);
352
395
  let next = { ...state };
353
396
  await loadRemoteTuiUpdates(state, {
@@ -383,7 +426,8 @@ export async function runTui(options = {}) {
383
426
  if (!executable) return null;
384
427
 
385
428
  const providedState = options.state;
386
- const state = providedState || (await createLocalTuiState(options.profileName, options));
429
+ const state =
430
+ providedState || (await createLocalTuiState(options.profileName, options));
387
431
  const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'subc-tui-'));
388
432
  const statePath = path.join(tempDir, 'state.json');
389
433
  const resultPath = path.join(tempDir, 'result.json');
@@ -403,20 +447,35 @@ export async function runTui(options = {}) {
403
447
 
404
448
  const remote = providedState
405
449
  ? Promise.resolve()
406
- : loadRemoteTuiUpdates(state, { ...options, signal: controller.signal, writePatch });
450
+ : loadRemoteTuiUpdates(state, {
451
+ ...options,
452
+ signal: controller.signal,
453
+ writePatch,
454
+ });
407
455
 
408
456
  try {
409
457
  // This state intentionally contains only display data. API keys are used
410
458
  // by the Node command engine and are never passed into the TUI process.
411
- await fs.writeFile(statePath, `${JSON.stringify(state)}\n`, { mode: 0o600 });
459
+ await fs.writeFile(statePath, `${JSON.stringify(state)}\n`, {
460
+ mode: 0o600,
461
+ });
412
462
  const code = await spawnAndWait(
413
463
  executable.command,
414
- [...executable.args, '--state', statePath, '--result', resultPath, '--updates', updatesPath],
464
+ [
465
+ ...executable.args,
466
+ '--state',
467
+ statePath,
468
+ '--result',
469
+ resultPath,
470
+ '--updates',
471
+ updatesPath,
472
+ ],
415
473
  { cwd: executable.cwd, spawn: options.spawn, stdio: options.stdio },
416
474
  );
417
475
  controller.abort();
418
476
  await remote.catch(() => {});
419
- if (code !== 0) throw new Error(`Subconscious TUI exited with status ${code}`);
477
+ if (code !== 0)
478
+ throw new Error(`Subconscious TUI exited with status ${code}`);
420
479
 
421
480
  try {
422
481
  const result = JSON.parse(await fs.readFile(resultPath, 'utf-8'));
@@ -1,10 +1,10 @@
1
- import fs from 'node:fs/promises';
2
1
  import { spawn } from 'node:child_process';
3
- import { spawnWindows } from './windows/process.js';
2
+ import fs from 'node:fs/promises';
4
3
  import readline from 'node:readline';
5
4
  import { fileURLToPath } from 'node:url';
6
5
  import { LOGO_ART_SMALL_LINES } from './branding.js';
7
6
  import { c, colorEnabled } from './colors.js';
7
+ import { spawnWindows } from './windows/process.js';
8
8
 
9
9
  export const PACKAGE_NAME = 'subconscious-cli';
10
10
  export const UPDATE_CHECK_TIMEOUT_MS = 1500;
@@ -46,7 +46,9 @@ export function detectInstallTarget(
46
46
  }
47
47
 
48
48
  const marker = `/lib/node_modules/${PACKAGE_NAME}/`;
49
- let prefix = normalized.includes(marker) ? normalized.slice(0, normalized.indexOf(marker)) : '';
49
+ let prefix = normalized.includes(marker)
50
+ ? normalized.slice(0, normalized.indexOf(marker))
51
+ : '';
50
52
  if (!prefix && platform === 'win32') {
51
53
  const windowsMarker = `/node_modules/${PACKAGE_NAME}/`;
52
54
  if (normalized.includes(windowsMarker)) {
@@ -59,7 +61,14 @@ export function detectInstallTarget(
59
61
  return {
60
62
  command: 'npm',
61
63
  args,
62
- display: ['npm', ...args].map(platform === 'win32' ? value => /^[-/@A-Za-z0-9._:]+$/.test(value) ? value : `"${value}"` : shellQuote).join(' '),
64
+ display: ['npm', ...args]
65
+ .map(
66
+ platform === 'win32'
67
+ ? (value) =>
68
+ /^[-/@A-Za-z0-9._:]+$/.test(value) ? value : `"${value}"`
69
+ : shellQuote,
70
+ )
71
+ .join(' '),
63
72
  prefix,
64
73
  };
65
74
  }
@@ -67,7 +76,9 @@ export function detectInstallTarget(
67
76
  function parseVersion(version) {
68
77
  const match = String(version)
69
78
  .trim()
70
- .match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
79
+ .match(
80
+ /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/,
81
+ );
71
82
  if (!match) return null;
72
83
  return {
73
84
  core: match.slice(1, 4).map(Number),
@@ -87,7 +98,8 @@ function comparePrerelease(a, b) {
87
98
 
88
99
  const aNumber = /^\d+$/.test(a[index]) ? Number(a[index]) : null;
89
100
  const bNumber = /^\d+$/.test(b[index]) ? Number(b[index]) : null;
90
- if (aNumber !== null && bNumber !== null) return Math.sign(aNumber - bNumber);
101
+ if (aNumber !== null && bNumber !== null)
102
+ return Math.sign(aNumber - bNumber);
91
103
  if (aNumber !== null) return -1;
92
104
  if (bNumber !== null) return 1;
93
105
  return a[index].localeCompare(b[index]);
@@ -109,7 +121,9 @@ export function compareVersions(a, b) {
109
121
  }
110
122
 
111
123
  export async function currentVersion() {
112
- const pkg = JSON.parse(await fs.readFile(new URL('../package.json', import.meta.url), 'utf-8'));
124
+ const pkg = JSON.parse(
125
+ await fs.readFile(new URL('../package.json', import.meta.url), 'utf-8'),
126
+ );
113
127
  return pkg.version;
114
128
  }
115
129
 
@@ -134,7 +148,11 @@ export async function fetchLatestVersion(options = {}) {
134
148
  try {
135
149
  const aborted = new Promise((_, reject) => {
136
150
  const fail = () => {
137
- const error = new Error(signal?.aborted ? 'Update check was cancelled' : 'Update check timed out');
151
+ const error = new Error(
152
+ signal?.aborted
153
+ ? 'Update check was cancelled'
154
+ : 'Update check timed out',
155
+ );
138
156
  error.name = 'AbortError';
139
157
  reject(error);
140
158
  };
@@ -154,7 +172,8 @@ export async function fetchLatestVersion(options = {}) {
154
172
  ]);
155
173
  if (!response.ok) throw new Error(`npm returned HTTP ${response.status}`);
156
174
  const payload = await response.json();
157
- if (!parseVersion(payload?.version)) throw new Error('npm returned an invalid version');
175
+ if (!parseVersion(payload?.version))
176
+ throw new Error('npm returned an invalid version');
158
177
  return payload.version;
159
178
  } finally {
160
179
  signal?.removeEventListener('abort', onAbort);
@@ -175,7 +194,10 @@ export function renderUpdateNotice(installedVersion, latestVersion) {
175
194
  { raw: '', value: '' },
176
195
  { raw: '', value: '' },
177
196
  { raw: '', value: '' },
178
- { raw: 'Subconscious CLI', value: `${c.orange}${c.bold}Subconscious CLI${c.reset}` },
197
+ {
198
+ raw: 'Subconscious CLI',
199
+ value: `${c.orange}${c.bold}Subconscious CLI${c.reset}`,
200
+ },
179
201
  { raw: 'Update available', value: `${c.bold}Update available${c.reset}` },
180
202
  { raw: '', value: '' },
181
203
  {
@@ -198,7 +220,8 @@ export function renderUpdateNotice(installedVersion, latestVersion) {
198
220
  }
199
221
 
200
222
  export function renderUpdateOptions(selectedIndex = 0, versions = {}) {
201
- const installCommand = versions.installCommand || detectInstallTarget().display;
223
+ const installCommand =
224
+ versions.installCommand || detectInstallTarget().display;
202
225
  const descriptions = [
203
226
  `Runs \`${installCommand}\``,
204
227
  versions.installedVersion
@@ -209,9 +232,7 @@ export function renderUpdateOptions(selectedIndex = 0, versions = {}) {
209
232
  return UPDATE_ACTIONS.map((label, index) => {
210
233
  const active = index === selectedIndex;
211
234
  const pointer = active ? `${c.orange}›${c.reset}` : ' ';
212
- const activeStyle = colorEnabled
213
- ? `${c.bgOrange}${c.black}`
214
- : c.inverse;
235
+ const activeStyle = colorEnabled ? `${c.bgOrange}${c.black}` : c.inverse;
215
236
  const option = active
216
237
  ? `${activeStyle}${c.bold} ${label} ${c.reset}`
217
238
  : ` ${label} `;
@@ -278,7 +299,8 @@ export async function selectUpdateAction(options = {}) {
278
299
  }
279
300
 
280
301
  export async function installLatest(options = {}) {
281
- const spawnImpl = options.spawnImpl || (process.platform === 'win32' ? spawnWindows : spawn);
302
+ const spawnImpl =
303
+ options.spawnImpl || (process.platform === 'win32' ? spawnWindows : spawn);
282
304
  const target = options.target || detectInstallTarget();
283
305
  return new Promise((resolve) => {
284
306
  const child = spawnImpl(target.command, target.args, {
@@ -325,8 +347,10 @@ export async function showUpdateNotice(options = {}) {
325
347
  write(`\n${notice}\n\n`);
326
348
 
327
349
  const interactive =
328
- options.interactive ?? (process.stdin.isTTY === true && process.stderr.isTTY === true);
329
- if (!interactive) return { installedVersion, latestVersion, action: 'skip' };
350
+ options.interactive ??
351
+ (process.stdin.isTTY === true && process.stderr.isTTY === true);
352
+ if (!interactive)
353
+ return { installedVersion, latestVersion, action: 'skip' };
330
354
 
331
355
  const select = options.select || selectUpdateAction;
332
356
  const installTarget = options.installTarget || detectInstallTarget();
@@ -341,10 +365,13 @@ export async function showUpdateNotice(options = {}) {
341
365
  return { installedVersion, latestVersion, action };
342
366
  }
343
367
 
344
- write(`\n ${c.cyan}Updating ${PACKAGE_NAME} to ${latestVersion}...${c.reset}\n\n`);
368
+ write(
369
+ `\n ${c.cyan}Updating ${PACKAGE_NAME} to ${latestVersion}...${c.reset}\n\n`,
370
+ );
345
371
  const install =
346
372
  options.install ||
347
- ((expectedVersion) => installLatest({ expectedVersion, target: installTarget }));
373
+ ((expectedVersion) =>
374
+ installLatest({ expectedVersion, target: installTarget }));
348
375
  const installed = await install(latestVersion);
349
376
  if (installed) {
350
377
  write(
@@ -353,7 +380,9 @@ export async function showUpdateNotice(options = {}) {
353
380
  return { installedVersion, latestVersion, action: 'updated' };
354
381
  }
355
382
 
356
- write(`\n ${c.red}Update failed or the running installation stayed unchanged.${c.reset}\n`);
383
+ write(
384
+ `\n ${c.red}Update failed or the running installation stayed unchanged.${c.reset}\n`,
385
+ );
357
386
  write(` Try the exact detected install target manually:\n\n`);
358
387
  write(` ${c.cyan}${installTarget.display}${c.reset}\n\n`);
359
388
  return { installedVersion, latestVersion, action: 'failed' };
package/bin/upgrade.js CHANGED
@@ -6,12 +6,12 @@
6
6
  */
7
7
 
8
8
  import { spawn } from 'node:child_process';
9
- import { runWindows } from './windows/process.js';
10
9
  import fs from 'node:fs/promises';
11
10
  import readline from 'node:readline';
12
11
  import { fileURLToPath } from 'node:url';
13
12
  import { c } from './colors.js';
14
13
  import { detectInstallTarget } from './update-check.js';
14
+ import { runWindows } from './windows/process.js';
15
15
 
16
16
  export const PACKAGE_NAME = 'subconscious-cli';
17
17
  export const MIN_LOGIN_VERSION = '4.0';
@@ -20,7 +20,9 @@ export function parseUpgradeArgs(argv = []) {
20
20
  const latest = argv.includes('--latest');
21
21
  const unknown = argv.filter((arg) => arg !== '--latest');
22
22
  if (unknown.length) {
23
- throw new Error(`Unknown argument: ${unknown[0]}\nUsage: subc upgrade [--latest]`);
23
+ throw new Error(
24
+ `Unknown argument: ${unknown[0]}\nUsage: subc upgrade [--latest]`,
25
+ );
24
26
  }
25
27
  return { latest };
26
28
  }
@@ -30,13 +32,19 @@ export function detectInstallCommand(here = fileURLToPath(import.meta.url)) {
30
32
  }
31
33
 
32
34
  export async function currentCliVersion() {
33
- const pkg = JSON.parse(await fs.readFile(new URL('../package.json', import.meta.url), 'utf-8'));
35
+ const pkg = JSON.parse(
36
+ await fs.readFile(new URL('../package.json', import.meta.url), 'utf-8'),
37
+ );
34
38
  return pkg.version;
35
39
  }
36
40
 
37
41
  export function compareVersions(a, b) {
38
- const pa = String(a).split('.').map((part) => Number.parseInt(part, 10) || 0);
39
- const pb = String(b).split('.').map((part) => Number.parseInt(part, 10) || 0);
42
+ const pa = String(a)
43
+ .split('.')
44
+ .map((part) => Number.parseInt(part, 10) || 0);
45
+ const pb = String(b)
46
+ .split('.')
47
+ .map((part) => Number.parseInt(part, 10) || 0);
40
48
  const n = Math.max(pa.length, pb.length);
41
49
  for (let i = 0; i < n; i++) {
42
50
  const da = pa[i] || 0;
@@ -48,14 +56,20 @@ export function compareVersions(a, b) {
48
56
  }
49
57
 
50
58
  export async function fetchLatestVersion(fetchImpl = fetch) {
51
- const res = await fetchImpl(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
52
- signal: AbortSignal.timeout(10000),
53
- });
59
+ const res = await fetchImpl(
60
+ `https://registry.npmjs.org/${PACKAGE_NAME}/latest`,
61
+ {
62
+ signal: AbortSignal.timeout(10000),
63
+ },
64
+ );
54
65
  if (!res.ok) {
55
- throw new Error(`Could not look up ${PACKAGE_NAME} on npm (${res.status}).`);
66
+ throw new Error(
67
+ `Could not look up ${PACKAGE_NAME} on npm (${res.status}).`,
68
+ );
56
69
  }
57
70
  const data = await res.json();
58
- if (!data?.version) throw new Error(`npm did not return a version for ${PACKAGE_NAME}.`);
71
+ if (!data?.version)
72
+ throw new Error(`npm did not return a version for ${PACKAGE_NAME}.`);
59
73
  return data.version;
60
74
  }
61
75
 
@@ -72,7 +86,10 @@ export function printLoginUpgradeWarning() {
72
86
 
73
87
  function askYesNo(question) {
74
88
  return new Promise((resolve) => {
75
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
89
+ const rl = readline.createInterface({
90
+ input: process.stdin,
91
+ output: process.stdout,
92
+ });
76
93
  rl.question(question, (answer) => {
77
94
  rl.close();
78
95
  const a = answer.trim().toLowerCase();
@@ -84,7 +101,10 @@ function askYesNo(question) {
84
101
  function runInstall(command) {
85
102
  if (process.platform === 'win32') {
86
103
  const target = detectInstallTarget();
87
- return runWindows(target.command, target.args).then(code => code === 0, () => false);
104
+ return runWindows(target.command, target.args).then(
105
+ (code) => code === 0,
106
+ () => false,
107
+ );
88
108
  }
89
109
  return new Promise((resolve) => {
90
110
  const child = spawn(command, { shell: true, stdio: 'inherit' });
@@ -108,7 +128,9 @@ export async function installLatest(options = {}) {
108
128
  }
109
129
 
110
130
  if (latest && compareVersions(current, latest) >= 0) {
111
- console.log(`\n ${c.green}Already up to date${c.reset} ${c.dim}(${current}).${c.reset}\n`);
131
+ console.log(
132
+ `\n ${c.green}Already up to date${c.reset} ${c.dim}(${current}).${c.reset}\n`,
133
+ );
112
134
  return true;
113
135
  }
114
136
 
@@ -117,7 +139,9 @@ export async function installLatest(options = {}) {
117
139
  `\n Upgrading ${c.cyan}${PACKAGE_NAME}${c.reset} ${c.dim}${current}${c.reset} → ${c.cyan}${latest}${c.reset}`,
118
140
  );
119
141
  } else {
120
- console.log(`\n Upgrading ${c.cyan}${PACKAGE_NAME}${c.reset} ${c.dim}${current}${c.reset} → latest`);
142
+ console.log(
143
+ `\n Upgrading ${c.cyan}${PACKAGE_NAME}${c.reset} ${c.dim}${current}${c.reset} → latest`,
144
+ );
121
145
  }
122
146
  console.log(` ${c.dim}Running${c.reset} ${c.cyan}${command}${c.reset}\n`);
123
147
 
@@ -141,20 +165,25 @@ export async function installLatest(options = {}) {
141
165
  return false;
142
166
  }
143
167
  } catch {
144
- console.error(`\n ${c.red}Could not verify the updated CLI installation.${c.reset}`);
168
+ console.error(
169
+ `\n ${c.red}Could not verify the updated CLI installation.${c.reset}`,
170
+ );
145
171
  console.error(` Run the exact detected command manually:\n`);
146
172
  console.error(` ${c.cyan}${command}${c.reset}\n`);
147
173
  return false;
148
174
  }
149
175
  }
150
176
 
151
- console.log(`\n ${c.green}${c.bold}✓ Upgraded.${c.reset} Re-run ${c.cyan}subc login${c.reset} if you were signing in.\n`);
177
+ console.log(
178
+ `\n ${c.green}${c.bold}✓ Upgraded.${c.reset} Re-run ${c.cyan}subc login${c.reset} if you were signing in.\n`,
179
+ );
152
180
  return true;
153
181
  }
154
182
 
155
183
  export async function upgradeCommand(argv = [], options = {}) {
156
184
  const { latest } = parseUpgradeArgs(argv);
157
- const interactive = options.interactive ?? (process.stdin.isTTY && process.stdout.isTTY);
185
+ const interactive =
186
+ options.interactive ?? (process.stdin.isTTY && process.stdout.isTTY);
158
187
  const ask = options.ask || askYesNo;
159
188
  const installLatestFn = options.installLatest || installLatest;
160
189
 
@@ -163,7 +192,9 @@ export async function upgradeCommand(argv = [], options = {}) {
163
192
  console.error(
164
193
  `\n ${c.yellow}Do you want to upgrade?${c.reset} requires a TTY.`,
165
194
  );
166
- console.error(` Run ${c.cyan}subc upgrade --latest${c.reset} instead.\n`);
195
+ console.error(
196
+ ` Run ${c.cyan}subc upgrade --latest${c.reset} instead.\n`,
197
+ );
167
198
  process.exitCode = 1;
168
199
  return;
169
200
  }
package/bin/usage.js CHANGED
@@ -2,8 +2,8 @@
2
2
  * Usage display for `subc usage` — fetches billing/quota from the platform API.
3
3
  */
4
4
 
5
- import { c } from './colors.js';
6
5
  import { getApiKey, getPlatformUrl } from './auth.js';
6
+ import { c } from './colors.js';
7
7
  import { DEFAULT_PROFILE } from './profiles.js';
8
8
  import { printLoginUpgradeWarning } from './upgrade.js';
9
9
 
@@ -33,13 +33,18 @@ export function formatCurrency(value) {
33
33
 
34
34
  export function shortModelSlug(slug) {
35
35
  const text = String(slug ?? '').trim();
36
- return text.startsWith('subconscious/') ? text.slice('subconscious/'.length) : text;
36
+ return text.startsWith('subconscious/')
37
+ ? text.slice('subconscious/'.length)
38
+ : text;
37
39
  }
38
40
 
39
41
  export function isLegacyUsagePayload(data) {
40
42
  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
+ if ('credits' in data && data.plan && typeof data.plan === 'object')
44
+ return false;
45
+ return (
46
+ 'dailyTokenCeiling' in data || 'consumedToday' in data || !('plan' in data)
47
+ );
43
48
  }
44
49
 
45
50
  export function meterColor(percentage) {
@@ -120,7 +125,9 @@ export function formatUsageDisplay(data) {
120
125
  );
121
126
  } else {
122
127
  const label =
123
- described.kind === 'credit' ? 'of daily credit used' : 'of daily tokens used';
128
+ described.kind === 'credit'
129
+ ? 'of daily credit used'
130
+ : 'of daily tokens used';
124
131
  lines.push(
125
132
  ` ${renderProgressBar(described.percent)} ${formatAllowancePercent(described.percent)} ${label}`,
126
133
  );
@@ -155,9 +162,7 @@ export function formatUsageDisplay(data) {
155
162
  );
156
163
  for (const model of models) {
157
164
  const label = shortModelSlug(model.slug).padEnd(slugWidth);
158
- lines.push(
159
- ` ${label} ${formatTokens(model.consumedToday ?? 0)}`,
160
- );
165
+ lines.push(` ${label} ${formatTokens(model.consumedToday ?? 0)}`);
161
166
  }
162
167
  }
163
168
 
@@ -166,10 +171,13 @@ export function formatUsageDisplay(data) {
166
171
  }
167
172
 
168
173
  export async function fetchUsage(apiKey, platformUrl, fetchImpl = fetch) {
169
- const res = await fetchImpl(`${platformUrl.replace(/\/$/, '')}${USAGE_API_PATH}`, {
170
- headers: { Authorization: `Bearer ${apiKey}` },
171
- signal: AbortSignal.timeout(5000),
172
- });
174
+ const res = await fetchImpl(
175
+ `${platformUrl.replace(/\/$/, '')}${USAGE_API_PATH}`,
176
+ {
177
+ headers: { Authorization: `Bearer ${apiKey}` },
178
+ signal: AbortSignal.timeout(5000),
179
+ },
180
+ );
173
181
  return res;
174
182
  }
175
183
 
@@ -183,7 +191,9 @@ export async function usageCommand(argv = [], options = {}) {
183
191
  const auth = await getApiKey(options.profile);
184
192
  if (!auth) {
185
193
  console.log(`\n ${c.dim}Not logged in.${c.reset}`);
186
- console.log(` Run ${c.cyan}subc ${profileFlag}login${c.reset} to get started.\n`);
194
+ console.log(
195
+ ` Run ${c.cyan}subc ${profileFlag}login${c.reset} to get started.\n`,
196
+ );
187
197
  return;
188
198
  }
189
199
 
@@ -236,7 +246,9 @@ export async function usageCommand(argv = [], options = {}) {
236
246
  console.log(formatUsageDisplay(data));
237
247
  } catch (error) {
238
248
  if (error.name === 'TimeoutError' || error.name === 'AbortError') {
239
- console.log(`\n ${c.yellow}Could not reach the platform (timed out).${c.reset}`);
249
+ console.log(
250
+ `\n ${c.yellow}Could not reach the platform (timed out).${c.reset}`,
251
+ );
240
252
  console.log(` ${c.dim}Host: ${platformUrl}${c.reset}\n`);
241
253
  return;
242
254
  }