subconscious-cli 6.0.0 → 6.1.0
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 +135 -2
- package/bin/agent-data.js +242 -0
- package/bin/agent-help.js +106 -0
- package/bin/agent-install.js +354 -0
- package/bin/agent-launch.js +199 -0
- package/bin/agent-spawn.js +137 -0
- package/bin/agents.js +284 -840
- package/bin/auth.js +89 -33
- package/bin/branding.js +3 -1
- package/bin/claude-picker.js +143 -0
- package/bin/cli.js +87 -20
- package/bin/clipboard-image.js +24 -5
- package/bin/colors.js +1 -1
- package/bin/feedback.js +71 -21
- package/bin/harness-manifest.js +286 -0
- package/bin/headless-args.js +35 -0
- package/bin/launch-values.js +25 -0
- package/bin/model-capabilities.js +12 -8
- package/bin/models.js +35 -19
- 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/opencode-provider.js +48 -5
- package/bin/profile-settings.js +129 -0
- package/bin/profiles.js +136 -449
- package/bin/runbook/README.md +66 -23
- package/bin/runbook/claude-code/agent.json +300 -0
- package/bin/runbook/claude-code/install.sh +9 -252
- package/bin/runbook/codex/agent.json +461 -0
- package/bin/runbook/codex/hooks-lib.sh +17 -4
- package/bin/runbook/codex/install.sh +0 -4
- package/bin/runbook/codex/run.sh +16 -235
- package/bin/runbook/copilot/agent.json +185 -0
- package/bin/runbook/copilot/install.sh +45 -63
- package/bin/runbook/cursor/agent.json +137 -0
- package/bin/runbook/cursor/install.sh +8 -35
- package/bin/runbook/deepseek-harness/agent.json +163 -0
- package/bin/runbook/deepseek-harness/run.sh +20 -59
- package/bin/runbook/image-window/index.ts +14 -0
- package/bin/runbook/image-window/window.d.ts +19 -0
- package/bin/runbook/image-window/window.js +140 -0
- package/bin/runbook/lib.sh +41 -0
- package/bin/runbook/opencode/agent.json +158 -0
- package/bin/runbook/opencode/install.sh +9 -183
- package/bin/runbook/opencode/opencode-plugin.d.ts +16 -0
- package/bin/runbook/opencode/subconscious-image-window.ts +24 -0
- package/bin/runbook/pi/agent.json +216 -0
- package/bin/runbook/pi/install.sh +18 -44
- package/bin/runbook/pi/run.sh +6 -18
- package/bin/runbook/pi/subconscious-compaction.ts +71 -49
- package/bin/runbook/shared.json +58 -0
- package/bin/runbook/subconscious-code/agent.json +156 -0
- package/bin/sessions.js +173 -58
- package/bin/tui.js +104 -32
- package/bin/update-check.js +54 -21
- package/bin/upgrade.js +67 -43
- package/bin/usage.js +47 -24
- package/bin/windows/agents.js +141 -37
- package/bin/windows/common.js +36 -25
- package/bin/windows/install.js +116 -34
- package/bin/windows/launch.js +260 -77
- package/bin/windows/pi-compaction.ts +68 -13
- package/bin/windows/process.js +210 -57
- package/bin/windows/setup.js +251 -56
- package/package.json +9 -4
- package/bin/registry.generated.json +0 -406
- package/bin/runbook/claude-code/run.sh +0 -254
- package/bin/runbook/model-capabilities.generated.sh +0 -9
- package/bin/runbook/opencode/provider.sh +0 -93
- package/bin/runbook/opencode/run.sh +0 -99
- package/bin/runbook/opencode/subconscious-compaction.ts +0 -100
- package/bin/runbook/subconscious-code/run.sh +0 -21
- package/bin/windows/claude.js +0 -71
package/bin/update-check.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
1
|
import { spawn } from 'node:child_process';
|
|
3
|
-
import
|
|
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)
|
|
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]
|
|
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(
|
|
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),
|
|
@@ -75,6 +86,10 @@ function parseVersion(version) {
|
|
|
75
86
|
};
|
|
76
87
|
}
|
|
77
88
|
|
|
89
|
+
export function isVersion(version) {
|
|
90
|
+
return parseVersion(version) !== null;
|
|
91
|
+
}
|
|
92
|
+
|
|
78
93
|
function comparePrerelease(a, b) {
|
|
79
94
|
if (!a.length && !b.length) return 0;
|
|
80
95
|
if (!a.length) return 1;
|
|
@@ -87,10 +102,11 @@ function comparePrerelease(a, b) {
|
|
|
87
102
|
|
|
88
103
|
const aNumber = /^\d+$/.test(a[index]) ? Number(a[index]) : null;
|
|
89
104
|
const bNumber = /^\d+$/.test(b[index]) ? Number(b[index]) : null;
|
|
90
|
-
if (aNumber !== null && bNumber !== null)
|
|
105
|
+
if (aNumber !== null && bNumber !== null)
|
|
106
|
+
return Math.sign(aNumber - bNumber);
|
|
91
107
|
if (aNumber !== null) return -1;
|
|
92
108
|
if (bNumber !== null) return 1;
|
|
93
|
-
return a[index]
|
|
109
|
+
return a[index] < b[index] ? -1 : 1;
|
|
94
110
|
}
|
|
95
111
|
return 0;
|
|
96
112
|
}
|
|
@@ -109,7 +125,9 @@ export function compareVersions(a, b) {
|
|
|
109
125
|
}
|
|
110
126
|
|
|
111
127
|
export async function currentVersion() {
|
|
112
|
-
const pkg = JSON.parse(
|
|
128
|
+
const pkg = JSON.parse(
|
|
129
|
+
await fs.readFile(new URL('../package.json', import.meta.url), 'utf-8'),
|
|
130
|
+
);
|
|
113
131
|
return pkg.version;
|
|
114
132
|
}
|
|
115
133
|
|
|
@@ -134,7 +152,11 @@ export async function fetchLatestVersion(options = {}) {
|
|
|
134
152
|
try {
|
|
135
153
|
const aborted = new Promise((_, reject) => {
|
|
136
154
|
const fail = () => {
|
|
137
|
-
const error = new Error(
|
|
155
|
+
const error = new Error(
|
|
156
|
+
signal?.aborted
|
|
157
|
+
? 'Update check was cancelled'
|
|
158
|
+
: 'Update check timed out',
|
|
159
|
+
);
|
|
138
160
|
error.name = 'AbortError';
|
|
139
161
|
reject(error);
|
|
140
162
|
};
|
|
@@ -154,7 +176,8 @@ export async function fetchLatestVersion(options = {}) {
|
|
|
154
176
|
]);
|
|
155
177
|
if (!response.ok) throw new Error(`npm returned HTTP ${response.status}`);
|
|
156
178
|
const payload = await response.json();
|
|
157
|
-
if (!parseVersion(payload?.version))
|
|
179
|
+
if (!parseVersion(payload?.version))
|
|
180
|
+
throw new Error('npm returned an invalid version');
|
|
158
181
|
return payload.version;
|
|
159
182
|
} finally {
|
|
160
183
|
signal?.removeEventListener('abort', onAbort);
|
|
@@ -175,7 +198,10 @@ export function renderUpdateNotice(installedVersion, latestVersion) {
|
|
|
175
198
|
{ raw: '', value: '' },
|
|
176
199
|
{ raw: '', value: '' },
|
|
177
200
|
{ raw: '', value: '' },
|
|
178
|
-
{
|
|
201
|
+
{
|
|
202
|
+
raw: 'Subconscious CLI',
|
|
203
|
+
value: `${c.orange}${c.bold}Subconscious CLI${c.reset}`,
|
|
204
|
+
},
|
|
179
205
|
{ raw: 'Update available', value: `${c.bold}Update available${c.reset}` },
|
|
180
206
|
{ raw: '', value: '' },
|
|
181
207
|
{
|
|
@@ -198,7 +224,8 @@ export function renderUpdateNotice(installedVersion, latestVersion) {
|
|
|
198
224
|
}
|
|
199
225
|
|
|
200
226
|
export function renderUpdateOptions(selectedIndex = 0, versions = {}) {
|
|
201
|
-
const installCommand =
|
|
227
|
+
const installCommand =
|
|
228
|
+
versions.installCommand || detectInstallTarget().display;
|
|
202
229
|
const descriptions = [
|
|
203
230
|
`Runs \`${installCommand}\``,
|
|
204
231
|
versions.installedVersion
|
|
@@ -209,9 +236,7 @@ export function renderUpdateOptions(selectedIndex = 0, versions = {}) {
|
|
|
209
236
|
return UPDATE_ACTIONS.map((label, index) => {
|
|
210
237
|
const active = index === selectedIndex;
|
|
211
238
|
const pointer = active ? `${c.orange}›${c.reset}` : ' ';
|
|
212
|
-
const activeStyle = colorEnabled
|
|
213
|
-
? `${c.bgOrange}${c.black}`
|
|
214
|
-
: c.inverse;
|
|
239
|
+
const activeStyle = colorEnabled ? `${c.bgOrange}${c.black}` : c.inverse;
|
|
215
240
|
const option = active
|
|
216
241
|
? `${activeStyle}${c.bold} ${label} ${c.reset}`
|
|
217
242
|
: ` ${label} `;
|
|
@@ -278,7 +303,8 @@ export async function selectUpdateAction(options = {}) {
|
|
|
278
303
|
}
|
|
279
304
|
|
|
280
305
|
export async function installLatest(options = {}) {
|
|
281
|
-
const spawnImpl =
|
|
306
|
+
const spawnImpl =
|
|
307
|
+
options.spawnImpl || (process.platform === 'win32' ? spawnWindows : spawn);
|
|
282
308
|
const target = options.target || detectInstallTarget();
|
|
283
309
|
return new Promise((resolve) => {
|
|
284
310
|
const child = spawnImpl(target.command, target.args, {
|
|
@@ -325,8 +351,10 @@ export async function showUpdateNotice(options = {}) {
|
|
|
325
351
|
write(`\n${notice}\n\n`);
|
|
326
352
|
|
|
327
353
|
const interactive =
|
|
328
|
-
options.interactive ??
|
|
329
|
-
|
|
354
|
+
options.interactive ??
|
|
355
|
+
(process.stdin.isTTY === true && process.stderr.isTTY === true);
|
|
356
|
+
if (!interactive)
|
|
357
|
+
return { installedVersion, latestVersion, action: 'skip' };
|
|
330
358
|
|
|
331
359
|
const select = options.select || selectUpdateAction;
|
|
332
360
|
const installTarget = options.installTarget || detectInstallTarget();
|
|
@@ -341,10 +369,13 @@ export async function showUpdateNotice(options = {}) {
|
|
|
341
369
|
return { installedVersion, latestVersion, action };
|
|
342
370
|
}
|
|
343
371
|
|
|
344
|
-
write(
|
|
372
|
+
write(
|
|
373
|
+
`\n ${c.cyan}Updating ${PACKAGE_NAME} to ${latestVersion}...${c.reset}\n\n`,
|
|
374
|
+
);
|
|
345
375
|
const install =
|
|
346
376
|
options.install ||
|
|
347
|
-
((expectedVersion) =>
|
|
377
|
+
((expectedVersion) =>
|
|
378
|
+
installLatest({ expectedVersion, target: installTarget }));
|
|
348
379
|
const installed = await install(latestVersion);
|
|
349
380
|
if (installed) {
|
|
350
381
|
write(
|
|
@@ -353,7 +384,9 @@ export async function showUpdateNotice(options = {}) {
|
|
|
353
384
|
return { installedVersion, latestVersion, action: 'updated' };
|
|
354
385
|
}
|
|
355
386
|
|
|
356
|
-
write(
|
|
387
|
+
write(
|
|
388
|
+
`\n ${c.red}Update failed or the running installation stayed unchanged.${c.reset}\n`,
|
|
389
|
+
);
|
|
357
390
|
write(` Try the exact detected install target manually:\n\n`);
|
|
358
391
|
write(` ${c.cyan}${installTarget.display}${c.reset}\n\n`);
|
|
359
392
|
return { installedVersion, latestVersion, action: 'failed' };
|
package/bin/upgrade.js
CHANGED
|
@@ -6,12 +6,16 @@
|
|
|
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
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
compareVersions,
|
|
15
|
+
detectInstallTarget,
|
|
16
|
+
isVersion,
|
|
17
|
+
} from './update-check.js';
|
|
18
|
+
import { runWindows } from './windows/process.js';
|
|
15
19
|
|
|
16
20
|
export const PACKAGE_NAME = 'subconscious-cli';
|
|
17
21
|
export const MIN_LOGIN_VERSION = '4.0';
|
|
@@ -20,7 +24,9 @@ export function parseUpgradeArgs(argv = []) {
|
|
|
20
24
|
const latest = argv.includes('--latest');
|
|
21
25
|
const unknown = argv.filter((arg) => arg !== '--latest');
|
|
22
26
|
if (unknown.length) {
|
|
23
|
-
throw new Error(
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Unknown argument: ${unknown[0]}\nUsage: subc upgrade [--latest]`,
|
|
29
|
+
);
|
|
24
30
|
}
|
|
25
31
|
return { latest };
|
|
26
32
|
}
|
|
@@ -30,32 +36,27 @@ export function detectInstallCommand(here = fileURLToPath(import.meta.url)) {
|
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
export async function currentCliVersion() {
|
|
33
|
-
const pkg = JSON.parse(
|
|
39
|
+
const pkg = JSON.parse(
|
|
40
|
+
await fs.readFile(new URL('../package.json', import.meta.url), 'utf-8'),
|
|
41
|
+
);
|
|
34
42
|
return pkg.version;
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
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);
|
|
40
|
-
const n = Math.max(pa.length, pb.length);
|
|
41
|
-
for (let i = 0; i < n; i++) {
|
|
42
|
-
const da = pa[i] || 0;
|
|
43
|
-
const db = pb[i] || 0;
|
|
44
|
-
if (da > db) return 1;
|
|
45
|
-
if (da < db) return -1;
|
|
46
|
-
}
|
|
47
|
-
return 0;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
45
|
export async function fetchLatestVersion(fetchImpl = fetch) {
|
|
51
|
-
const res = await fetchImpl(
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
const res = await fetchImpl(
|
|
47
|
+
`https://registry.npmjs.org/${PACKAGE_NAME}/latest`,
|
|
48
|
+
{
|
|
49
|
+
signal: AbortSignal.timeout(10000),
|
|
50
|
+
},
|
|
51
|
+
);
|
|
54
52
|
if (!res.ok) {
|
|
55
|
-
throw new Error(
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Could not look up ${PACKAGE_NAME} on npm (${res.status}).`,
|
|
55
|
+
);
|
|
56
56
|
}
|
|
57
57
|
const data = await res.json();
|
|
58
|
-
if (!data?.version)
|
|
58
|
+
if (!data?.version)
|
|
59
|
+
throw new Error(`npm did not return a version for ${PACKAGE_NAME}.`);
|
|
59
60
|
return data.version;
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -72,7 +73,10 @@ export function printLoginUpgradeWarning() {
|
|
|
72
73
|
|
|
73
74
|
function askYesNo(question) {
|
|
74
75
|
return new Promise((resolve) => {
|
|
75
|
-
const rl = readline.createInterface({
|
|
76
|
+
const rl = readline.createInterface({
|
|
77
|
+
input: process.stdin,
|
|
78
|
+
output: process.stdout,
|
|
79
|
+
});
|
|
76
80
|
rl.question(question, (answer) => {
|
|
77
81
|
rl.close();
|
|
78
82
|
const a = answer.trim().toLowerCase();
|
|
@@ -84,7 +88,10 @@ function askYesNo(question) {
|
|
|
84
88
|
function runInstall(command) {
|
|
85
89
|
if (process.platform === 'win32') {
|
|
86
90
|
const target = detectInstallTarget();
|
|
87
|
-
return runWindows(target.command, target.args).then(
|
|
91
|
+
return runWindows(target.command, target.args).then(
|
|
92
|
+
(code) => code === 0,
|
|
93
|
+
() => false,
|
|
94
|
+
);
|
|
88
95
|
}
|
|
89
96
|
return new Promise((resolve) => {
|
|
90
97
|
const child = spawn(command, { shell: true, stdio: 'inherit' });
|
|
@@ -101,14 +108,20 @@ export async function installLatest(options = {}) {
|
|
|
101
108
|
|
|
102
109
|
let latest;
|
|
103
110
|
try {
|
|
104
|
-
|
|
111
|
+
const version = await fetchLatest();
|
|
112
|
+
if (!isVersion(version)) {
|
|
113
|
+
throw new Error(`npm returned an invalid version (${version}).`);
|
|
114
|
+
}
|
|
115
|
+
latest = version;
|
|
105
116
|
} catch (error) {
|
|
106
117
|
console.error(` ${c.yellow}${error.message}${c.reset}`);
|
|
107
118
|
console.error(` ${c.dim}Installing @latest anyway.${c.reset}\n`);
|
|
108
119
|
}
|
|
109
120
|
|
|
110
|
-
if (latest && compareVersions(current, latest) >= 0) {
|
|
111
|
-
console.log(
|
|
121
|
+
if (latest && isVersion(current) && compareVersions(current, latest) >= 0) {
|
|
122
|
+
console.log(
|
|
123
|
+
`\n ${c.green}Already up to date${c.reset} ${c.dim}(${current}).${c.reset}\n`,
|
|
124
|
+
);
|
|
112
125
|
return true;
|
|
113
126
|
}
|
|
114
127
|
|
|
@@ -117,7 +130,9 @@ export async function installLatest(options = {}) {
|
|
|
117
130
|
`\n Upgrading ${c.cyan}${PACKAGE_NAME}${c.reset} ${c.dim}${current}${c.reset} → ${c.cyan}${latest}${c.reset}`,
|
|
118
131
|
);
|
|
119
132
|
} else {
|
|
120
|
-
console.log(
|
|
133
|
+
console.log(
|
|
134
|
+
`\n Upgrading ${c.cyan}${PACKAGE_NAME}${c.reset} ${c.dim}${current}${c.reset} → latest`,
|
|
135
|
+
);
|
|
121
136
|
}
|
|
122
137
|
console.log(` ${c.dim}Running${c.reset} ${c.cyan}${command}${c.reset}\n`);
|
|
123
138
|
|
|
@@ -129,32 +144,39 @@ export async function installLatest(options = {}) {
|
|
|
129
144
|
}
|
|
130
145
|
|
|
131
146
|
if (latest) {
|
|
147
|
+
const readVersion = options.readVersion || currentCliVersion;
|
|
148
|
+
let installedVersion;
|
|
132
149
|
try {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
console.error(
|
|
150
|
+
installedVersion = await readVersion();
|
|
151
|
+
} catch {}
|
|
152
|
+
if (!isVersion(installedVersion)) {
|
|
153
|
+
console.error(
|
|
154
|
+
`\n ${c.red}Could not verify the updated CLI installation.${c.reset}`,
|
|
155
|
+
);
|
|
156
|
+
console.error(` Run the exact detected command manually:\n`);
|
|
157
|
+
console.error(` ${c.cyan}${command}${c.reset}\n`);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
if (compareVersions(installedVersion, latest) < 0) {
|
|
161
|
+
console.error(
|
|
162
|
+
`\n ${c.red}Upgrade command completed, but this installation is still ${installedVersion}.${c.reset}`,
|
|
163
|
+
);
|
|
145
164
|
console.error(` Run the exact detected command manually:\n`);
|
|
146
165
|
console.error(` ${c.cyan}${command}${c.reset}\n`);
|
|
147
166
|
return false;
|
|
148
167
|
}
|
|
149
168
|
}
|
|
150
169
|
|
|
151
|
-
console.log(
|
|
170
|
+
console.log(
|
|
171
|
+
`\n ${c.green}${c.bold}✓ Upgraded.${c.reset} Re-run ${c.cyan}subc login${c.reset} if you were signing in.\n`,
|
|
172
|
+
);
|
|
152
173
|
return true;
|
|
153
174
|
}
|
|
154
175
|
|
|
155
176
|
export async function upgradeCommand(argv = [], options = {}) {
|
|
156
177
|
const { latest } = parseUpgradeArgs(argv);
|
|
157
|
-
const interactive =
|
|
178
|
+
const interactive =
|
|
179
|
+
options.interactive ?? (process.stdin.isTTY && process.stdout.isTTY);
|
|
158
180
|
const ask = options.ask || askYesNo;
|
|
159
181
|
const installLatestFn = options.installLatest || installLatest;
|
|
160
182
|
|
|
@@ -163,7 +185,9 @@ export async function upgradeCommand(argv = [], options = {}) {
|
|
|
163
185
|
console.error(
|
|
164
186
|
`\n ${c.yellow}Do you want to upgrade?${c.reset} requires a TTY.`,
|
|
165
187
|
);
|
|
166
|
-
console.error(
|
|
188
|
+
console.error(
|
|
189
|
+
` Run ${c.cyan}subc upgrade --latest${c.reset} instead.\n`,
|
|
190
|
+
);
|
|
167
191
|
process.exitCode = 1;
|
|
168
192
|
return;
|
|
169
193
|
}
|
package/bin/usage.js
CHANGED
|
@@ -2,26 +2,37 @@
|
|
|
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
|
|
|
10
10
|
export const USAGE_API_PATH = '/api/v1/usage';
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// Largest first: [size, suffix].
|
|
13
|
+
const TOKEN_UNITS = [
|
|
14
|
+
[1_000_000_000, 'B'],
|
|
15
|
+
[1_000_000, 'M'],
|
|
16
|
+
[1_000, 'K'],
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export function formatTokens(value, { sigFigs = 3 } = {}) {
|
|
13
20
|
const n = Number(value);
|
|
21
|
+
|
|
14
22
|
if (!Number.isFinite(n) || n < 0) return '0';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
|
|
24
|
+
let index = TOKEN_UNITS.findIndex(([size]) => n >= size);
|
|
25
|
+
if (index === -1) return n.toLocaleString('en-US');
|
|
26
|
+
|
|
27
|
+
// Number() also turns toPrecision's exponent form ("1.00e+3") back into 1000.
|
|
28
|
+
|
|
29
|
+
let scaled = Number((n / TOKEN_UNITS[index][0]).toPrecision(sigFigs));
|
|
30
|
+
// Rounding can reach the next unit: 999_500 is 1M, not 1000K.
|
|
31
|
+
if (scaled >= 1_000 && index > 0) {
|
|
32
|
+
index -= 1;
|
|
33
|
+
scaled = Number((n / TOKEN_UNITS[index][0]).toPrecision(sigFigs));
|
|
22
34
|
}
|
|
23
|
-
|
|
24
|
-
return n.toLocaleString('en-US');
|
|
35
|
+
return `${scaled}${TOKEN_UNITS[index][1]}`;
|
|
25
36
|
}
|
|
26
37
|
|
|
27
38
|
export function formatCurrency(value) {
|
|
@@ -33,13 +44,18 @@ export function formatCurrency(value) {
|
|
|
33
44
|
|
|
34
45
|
export function shortModelSlug(slug) {
|
|
35
46
|
const text = String(slug ?? '').trim();
|
|
36
|
-
return text.startsWith('subconscious/')
|
|
47
|
+
return text.startsWith('subconscious/')
|
|
48
|
+
? text.slice('subconscious/'.length)
|
|
49
|
+
: text;
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
export function isLegacyUsagePayload(data) {
|
|
40
53
|
if (!data || typeof data !== 'object') return true;
|
|
41
|
-
if ('credits' in data && data.plan && typeof data.plan === 'object')
|
|
42
|
-
|
|
54
|
+
if ('credits' in data && data.plan && typeof data.plan === 'object')
|
|
55
|
+
return false;
|
|
56
|
+
return (
|
|
57
|
+
'dailyTokenCeiling' in data || 'consumedToday' in data || !('plan' in data)
|
|
58
|
+
);
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
export function meterColor(percentage) {
|
|
@@ -120,7 +136,9 @@ export function formatUsageDisplay(data) {
|
|
|
120
136
|
);
|
|
121
137
|
} else {
|
|
122
138
|
const label =
|
|
123
|
-
described.kind === 'credit'
|
|
139
|
+
described.kind === 'credit'
|
|
140
|
+
? 'of daily credit used'
|
|
141
|
+
: 'of daily tokens used';
|
|
124
142
|
lines.push(
|
|
125
143
|
` ${renderProgressBar(described.percent)} ${formatAllowancePercent(described.percent)} ${label}`,
|
|
126
144
|
);
|
|
@@ -155,9 +173,7 @@ export function formatUsageDisplay(data) {
|
|
|
155
173
|
);
|
|
156
174
|
for (const model of models) {
|
|
157
175
|
const label = shortModelSlug(model.slug).padEnd(slugWidth);
|
|
158
|
-
lines.push(
|
|
159
|
-
` ${label} ${formatTokens(model.consumedToday ?? 0)}`,
|
|
160
|
-
);
|
|
176
|
+
lines.push(` ${label} ${formatTokens(model.consumedToday ?? 0)}`);
|
|
161
177
|
}
|
|
162
178
|
}
|
|
163
179
|
|
|
@@ -166,10 +182,13 @@ export function formatUsageDisplay(data) {
|
|
|
166
182
|
}
|
|
167
183
|
|
|
168
184
|
export async function fetchUsage(apiKey, platformUrl, fetchImpl = fetch) {
|
|
169
|
-
const res = await fetchImpl(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
185
|
+
const res = await fetchImpl(
|
|
186
|
+
`${platformUrl.replace(/\/$/, '')}${USAGE_API_PATH}`,
|
|
187
|
+
{
|
|
188
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
189
|
+
signal: AbortSignal.timeout(5000),
|
|
190
|
+
},
|
|
191
|
+
);
|
|
173
192
|
return res;
|
|
174
193
|
}
|
|
175
194
|
|
|
@@ -183,7 +202,9 @@ export async function usageCommand(argv = [], options = {}) {
|
|
|
183
202
|
const auth = await getApiKey(options.profile);
|
|
184
203
|
if (!auth) {
|
|
185
204
|
console.log(`\n ${c.dim}Not logged in.${c.reset}`);
|
|
186
|
-
console.log(
|
|
205
|
+
console.log(
|
|
206
|
+
` Run ${c.cyan}subc ${profileFlag}login${c.reset} to get started.\n`,
|
|
207
|
+
);
|
|
187
208
|
return;
|
|
188
209
|
}
|
|
189
210
|
|
|
@@ -236,7 +257,9 @@ export async function usageCommand(argv = [], options = {}) {
|
|
|
236
257
|
console.log(formatUsageDisplay(data));
|
|
237
258
|
} catch (error) {
|
|
238
259
|
if (error.name === 'TimeoutError' || error.name === 'AbortError') {
|
|
239
|
-
console.log(
|
|
260
|
+
console.log(
|
|
261
|
+
`\n ${c.yellow}Could not reach the platform (timed out).${c.reset}`,
|
|
262
|
+
);
|
|
240
263
|
console.log(` ${c.dim}Host: ${platformUrl}${c.reset}\n`);
|
|
241
264
|
return;
|
|
242
265
|
}
|