subconscious-cli 0.3.0 → 4.0.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 +65 -93
- package/bin/agents.js +119 -87
- package/bin/auth.js +70 -14
- package/bin/cli.js +158 -84
- package/bin/profiles.js +129 -199
- package/bin/registry.generated.json +40 -12
- package/bin/runbook/README.md +12 -15
- package/bin/runbook/claude-code/install.sh +18 -44
- package/bin/runbook/claude-code/run.sh +21 -21
- package/bin/runbook/codex/hook.sh +1 -1
- package/bin/runbook/codex/hooks-lib.sh +139 -0
- package/bin/runbook/codex/install.sh +21 -463
- package/bin/runbook/codex/run.sh +7 -23
- package/bin/runbook/copilot/install.sh +9 -8
- package/bin/runbook/cursor/install.sh +36 -11
- package/bin/runbook/opencode/install.sh +46 -46
- package/bin/runbook/opencode/run.sh +4 -5
- package/bin/runbook/pi/install.sh +37 -41
- package/bin/runbook/pi/run.sh +3 -3
- package/bin/upgrade.js +164 -0
- package/package.json +2 -2
package/bin/auth.js
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
* Login flow (localhost callback pattern, similar to Vercel/Supabase CLIs):
|
|
5
5
|
* 1. CLI generates a random `state` token (CSRF protection) and starts
|
|
6
6
|
* an ephemeral HTTP server on a random port bound to 127.0.0.1.
|
|
7
|
-
* 2. Opens the browser to {
|
|
7
|
+
* 2. Opens the browser to {platformUrl}/cli/auth?port=...&state=...
|
|
8
8
|
* 3. The web app authenticates the user, generates an API key, and
|
|
9
9
|
* delivers it back to the CLI via a cross-origin fetch to
|
|
10
10
|
* localhost:{port}/callback?token=...&state=...
|
|
11
11
|
* 4. CLI verifies the `state`, saves the key to ~/.subconscious/config.json,
|
|
12
12
|
* and creates a coding-agent profile under ~/.subconscious/profiles/.
|
|
13
13
|
*
|
|
14
|
-
* Override SUBCONSCIOUS_URL env var for local development
|
|
14
|
+
* Override SUBCONSCIOUS_URL env var for local development
|
|
15
|
+
* (e.g. http://localhost:3000). Production defaults to platform.subconscious.dev.
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
import http from 'node:http';
|
|
@@ -22,6 +23,7 @@ import os from 'node:os';
|
|
|
22
23
|
import path from 'node:path';
|
|
23
24
|
import { c } from './colors.js';
|
|
24
25
|
import { clearProfileApiKey, DEFAULT_PROFILE, ensureProfile } from './profiles.js';
|
|
26
|
+
import { printLoginUpgradeWarning } from './upgrade.js';
|
|
25
27
|
|
|
26
28
|
const CONFIG_OVERRIDE = process.env.SUBC_CONFIG_DIR?.trim();
|
|
27
29
|
const CONFIG_DIR = CONFIG_OVERRIDE || path.join(os.homedir(), '.subconscious');
|
|
@@ -29,8 +31,34 @@ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
|
29
31
|
const LEGACY_CONFIG_FILE = CONFIG_OVERRIDE
|
|
30
32
|
? null
|
|
31
33
|
: path.join(os.homedir(), '.subcon', 'config.json');
|
|
32
|
-
// Defaults to
|
|
33
|
-
const
|
|
34
|
+
// Defaults to the platform host. Developers set SUBCONSCIOUS_URL for local dev.
|
|
35
|
+
export const DEFAULT_PLATFORM_URL = 'https://platform.subconscious.dev';
|
|
36
|
+
|
|
37
|
+
export function getPlatformUrl() {
|
|
38
|
+
const raw = process.env.SUBCONSCIOUS_URL?.trim() || DEFAULT_PLATFORM_URL;
|
|
39
|
+
return raw.replace(/\/$/, '');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Login callback CORS. After the marketing/platform split, /cli/auth lives on
|
|
43
|
+
// platform. www may still 307 there for older CLIs; keep www so a redirected
|
|
44
|
+
// or leftover tab can complete the callback.
|
|
45
|
+
const CALLBACK_ORIGINS = new Set([
|
|
46
|
+
'https://www.subconscious.dev',
|
|
47
|
+
'https://platform.subconscious.dev',
|
|
48
|
+
'https://dev.subconscious.dev',
|
|
49
|
+
'https://platform-dev.subconscious.dev',
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
export function isAllowedCallbackOrigin(origin, platformUrl = getPlatformUrl()) {
|
|
53
|
+
if (!origin) return false;
|
|
54
|
+
if (origin === platformUrl || CALLBACK_ORIGINS.has(origin)) return true;
|
|
55
|
+
try {
|
|
56
|
+
const { protocol, hostname } = new URL(origin);
|
|
57
|
+
return protocol === 'http:' && (hostname === 'localhost' || hostname === '127.0.0.1');
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
34
62
|
|
|
35
63
|
// ── Config helpers ──────────────────────────────────────────────────────
|
|
36
64
|
|
|
@@ -83,6 +111,24 @@ export async function getApiKey(profile) {
|
|
|
83
111
|
return null;
|
|
84
112
|
}
|
|
85
113
|
|
|
114
|
+
export async function probeLoginPage(platformUrl = getPlatformUrl(), fetchImpl = fetch) {
|
|
115
|
+
const url = `${platformUrl.replace(/\/$/, '')}/cli/auth`;
|
|
116
|
+
try {
|
|
117
|
+
const res = await fetchImpl(url, {
|
|
118
|
+
method: 'GET',
|
|
119
|
+
redirect: 'manual',
|
|
120
|
+
signal: AbortSignal.timeout(8000),
|
|
121
|
+
});
|
|
122
|
+
return res.status;
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function isLoginMissing(status) {
|
|
129
|
+
return status === 404;
|
|
130
|
+
}
|
|
131
|
+
|
|
86
132
|
// ── Browser opener ──────────────────────────────────────────────────────
|
|
87
133
|
|
|
88
134
|
function openBrowser(url) {
|
|
@@ -114,15 +160,13 @@ function startCallbackServer(expectedState) {
|
|
|
114
160
|
});
|
|
115
161
|
|
|
116
162
|
const server = http.createServer((req, res) => {
|
|
117
|
-
// CORS: only allow
|
|
163
|
+
// CORS: only allow known web-app origins (or localhost dev).
|
|
118
164
|
// This prevents arbitrary websites from hitting this callback.
|
|
119
165
|
const origin = req.headers.origin || '';
|
|
120
|
-
const allowed =
|
|
121
|
-
origin === PLATFORM_URL ||
|
|
122
|
-
origin.startsWith('http://localhost:');
|
|
166
|
+
const allowed = isAllowedCallbackOrigin(origin);
|
|
123
167
|
res.setHeader(
|
|
124
168
|
'Access-Control-Allow-Origin',
|
|
125
|
-
allowed ? origin :
|
|
169
|
+
allowed ? origin : getPlatformUrl(),
|
|
126
170
|
);
|
|
127
171
|
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
128
172
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
@@ -279,10 +323,18 @@ export async function loginCommand(_argv = [], options = {}) {
|
|
|
279
323
|
);
|
|
280
324
|
console.log();
|
|
281
325
|
|
|
326
|
+
const platformUrl = getPlatformUrl();
|
|
327
|
+
const loginStatus = await probeLoginPage(platformUrl);
|
|
328
|
+
if (isLoginMissing(loginStatus)) {
|
|
329
|
+
printLoginUpgradeWarning();
|
|
330
|
+
process.exitCode = 1;
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
282
334
|
const state = crypto.randomBytes(16).toString('hex');
|
|
283
335
|
const { port, promise } = await startCallbackServer(state);
|
|
284
336
|
|
|
285
|
-
const authUrl = `${
|
|
337
|
+
const authUrl = `${platformUrl}/cli/auth?port=${port}&state=${state}`;
|
|
286
338
|
|
|
287
339
|
console.log(` ${c.dim}Opening browser to sign in...${c.reset}`);
|
|
288
340
|
console.log();
|
|
@@ -326,9 +378,8 @@ export async function loginCommand(_argv = [], options = {}) {
|
|
|
326
378
|
console.log(` ${c.dim}Saved to ~/.subconscious/config.json${c.reset}`);
|
|
327
379
|
}
|
|
328
380
|
console.log(` ${c.dim}Runbook profile: ${profile.path}${c.reset}`);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
console.log(` ${c.dim}Run ${setup} once to configure all coding agents.${c.reset}`);
|
|
381
|
+
console.log(` ${c.dim}Launch a terminal agent with ${c.reset}${c.cyan}subc claude${c.reset}${c.dim}, or install editor hooks with ${c.reset}${c.cyan}subc cursor install${c.reset}${c.dim}.${c.reset}`);
|
|
382
|
+
console.log(` ${c.dim}Pi needs ${c.reset}${c.cyan}subc pi install${c.reset}${c.dim} first. List profiles with ${c.reset}${c.cyan}subc config${c.reset}${c.dim}.${c.reset}`);
|
|
332
383
|
console.log();
|
|
333
384
|
} catch (error) {
|
|
334
385
|
clearInterval(spinner);
|
|
@@ -408,11 +459,16 @@ export async function whoamiCommand(_argv = [], options = {}) {
|
|
|
408
459
|
|
|
409
460
|
// Validate the key against the server; falls back to offline display if unreachable
|
|
410
461
|
try {
|
|
411
|
-
const res = await fetch(`${
|
|
462
|
+
const res = await fetch(`${getPlatformUrl()}/api/cli/whoami`, {
|
|
412
463
|
headers: { Authorization: `Bearer ${key}` },
|
|
413
464
|
signal: AbortSignal.timeout(5000),
|
|
414
465
|
});
|
|
415
466
|
|
|
467
|
+
if (res.status === 404) {
|
|
468
|
+
printLoginUpgradeWarning();
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
|
|
416
472
|
if (res.ok) {
|
|
417
473
|
const data = await res.json();
|
|
418
474
|
console.log(` ${c.green}✓ Authenticated${c.reset}`);
|
package/bin/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Subconscious CLI — log in, then launch coding agents on your hosted models.
|
|
5
5
|
*
|
|
6
|
-
* subc login | update-key | logout | whoami — manage your API key
|
|
6
|
+
* subc login | update-key | logout | whoami | upgrade — manage your API key
|
|
7
7
|
* subc <agent> [...args] — launch or configure a coding agent
|
|
8
8
|
*
|
|
9
9
|
* Auth lives in ./auth.js, the agent launcher + registry in ./agents.js.
|
|
@@ -18,22 +18,29 @@ import {
|
|
|
18
18
|
updateApiKeyCommand,
|
|
19
19
|
whoamiCommand,
|
|
20
20
|
} from './auth.js';
|
|
21
|
+
import { upgradeCommand } from './upgrade.js';
|
|
21
22
|
import {
|
|
22
23
|
resolveAgent,
|
|
23
24
|
runAgent,
|
|
24
25
|
agentList,
|
|
25
|
-
|
|
26
|
+
agentCommandName,
|
|
26
27
|
isAgentHelpRequest,
|
|
28
|
+
parseAgentAction,
|
|
27
29
|
} from './agents.js';
|
|
28
30
|
import {
|
|
29
31
|
configCommand,
|
|
30
32
|
DEFAULT_PROFILE,
|
|
31
33
|
loadProfile,
|
|
32
34
|
modelsCommand,
|
|
35
|
+
printConfigHelp,
|
|
33
36
|
updateUrlCommand,
|
|
34
37
|
validateProfileName,
|
|
35
38
|
} from './profiles.js';
|
|
36
39
|
|
|
40
|
+
function isHelpArg(arg) {
|
|
41
|
+
return arg === 'help' || arg === '-h' || arg === '--help';
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
function printHelp() {
|
|
38
45
|
const agents = agentList()
|
|
39
46
|
.map(({ name, alias, action }) => ` ${c.cyan}${alias.padEnd(13)}${c.reset}${c.dim}${action} ${name}${c.reset}`)
|
|
@@ -43,6 +50,7 @@ function printHelp() {
|
|
|
43
50
|
|
|
44
51
|
${c.bold}Usage${c.reset}
|
|
45
52
|
${c.cyan}subc${c.reset} <command> [...args]
|
|
53
|
+
${c.cyan}subc${c.reset} <command> help
|
|
46
54
|
|
|
47
55
|
${c.bold}Auth${c.reset}
|
|
48
56
|
${c.cyan}login${c.reset} Authenticate and save your API key
|
|
@@ -50,12 +58,10 @@ function printHelp() {
|
|
|
50
58
|
${c.cyan}update-url${c.reset} Update the active profile's gateway URL automatically
|
|
51
59
|
${c.cyan}logout${c.reset} Remove saved credentials
|
|
52
60
|
${c.cyan}whoami${c.reset} Show current authentication status
|
|
61
|
+
${c.cyan}upgrade${c.reset} Upgrade this CLI to the latest version
|
|
53
62
|
|
|
54
|
-
${c.bold}
|
|
55
|
-
${c.cyan}
|
|
56
|
-
${c.cyan}setup${c.reset} Configure all or one coding-agent integration
|
|
57
|
-
${c.cyan}config${c.reset} Show or update the selected runbook profile
|
|
58
|
-
${c.cyan}settings${c.reset} Edit profile and per-agent settings interactively
|
|
63
|
+
${c.bold}Profiles${c.reset}
|
|
64
|
+
${c.cyan}config${c.reset} List profiles, or show/edit one with ${c.dim}-p${c.reset}
|
|
59
65
|
${c.cyan}models${c.reset} List available Subconscious models
|
|
60
66
|
|
|
61
67
|
${c.bold}Coding agents${c.reset}
|
|
@@ -69,17 +75,103 @@ ${agents}
|
|
|
69
75
|
|
|
70
76
|
${c.bold}Examples${c.reset}
|
|
71
77
|
${c.dim}$${c.reset} subc login
|
|
72
|
-
${c.dim}$${c.reset} subc
|
|
73
|
-
${c.dim}$${c.reset} subc
|
|
74
|
-
${c.dim}$${c.reset} subc
|
|
75
|
-
${c.dim}$${c.reset} subc
|
|
76
|
-
${c.dim}$${c.reset} subc
|
|
77
|
-
${c.dim}$${c.reset} subc
|
|
78
|
+
${c.dim}$${c.reset} subc upgrade
|
|
79
|
+
${c.dim}$${c.reset} subc upgrade --latest
|
|
80
|
+
${c.dim}$${c.reset} subc config
|
|
81
|
+
${c.dim}$${c.reset} subc config help
|
|
82
|
+
${c.dim}$${c.reset} subc -p staging config
|
|
83
|
+
${c.dim}$${c.reset} subc config edit vim
|
|
78
84
|
${c.dim}$${c.reset} subc claude
|
|
79
|
-
${c.dim}$${c.reset} subc
|
|
80
|
-
${c.dim}$${c.reset} subc
|
|
85
|
+
${c.dim}$${c.reset} subc claude help
|
|
86
|
+
${c.dim}$${c.reset} subc cursor install
|
|
87
|
+
${c.dim}$${c.reset} subc cursor uninstall
|
|
88
|
+
${c.dim}$${c.reset} subc pi install
|
|
89
|
+
${c.dim}$${c.reset} subc -p staging codex
|
|
90
|
+
|
|
91
|
+
${c.dim}Use subc <command> help for command-specific usage.${c.reset}
|
|
92
|
+
`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const COMMAND_HELP = {
|
|
96
|
+
login: `
|
|
97
|
+
Usage:
|
|
98
|
+
subc login
|
|
99
|
+
subc -p NAME login
|
|
100
|
+
subc login help
|
|
101
|
+
|
|
102
|
+
Authenticate and save an API key to the selected profile.
|
|
103
|
+
`,
|
|
104
|
+
logout: `
|
|
105
|
+
Usage:
|
|
106
|
+
subc logout
|
|
107
|
+
subc -p NAME logout
|
|
108
|
+
subc logout help
|
|
109
|
+
|
|
110
|
+
Remove the selected profile's saved API key. Non-secret settings are kept.
|
|
111
|
+
`,
|
|
112
|
+
whoami: `
|
|
113
|
+
Usage:
|
|
114
|
+
subc whoami
|
|
115
|
+
subc -p NAME whoami
|
|
116
|
+
subc whoami help
|
|
117
|
+
|
|
118
|
+
Show the current authentication status for the selected profile.
|
|
119
|
+
`,
|
|
120
|
+
upgrade: `
|
|
121
|
+
Usage:
|
|
122
|
+
subc upgrade
|
|
123
|
+
subc upgrade --latest
|
|
124
|
+
subc upgrade help
|
|
125
|
+
|
|
126
|
+
Upgrade subconscious-cli to the latest published version.
|
|
127
|
+
|
|
128
|
+
subc upgrade Prompt "Do you want to upgrade?" then install @latest
|
|
129
|
+
subc upgrade --latest Skip the prompt and install @latest
|
|
130
|
+
`,
|
|
131
|
+
'update-key': `
|
|
132
|
+
Usage:
|
|
133
|
+
subc update-key <api-key>
|
|
134
|
+
subc -p NAME update-key <api-key>
|
|
135
|
+
subc update-key help
|
|
136
|
+
|
|
137
|
+
Replace the selected profile's shared API key.
|
|
138
|
+
`,
|
|
139
|
+
'update-url': `
|
|
140
|
+
Usage:
|
|
141
|
+
subc update-url <gateway-url>
|
|
142
|
+
subc update-url help
|
|
143
|
+
|
|
144
|
+
Update the active profile's gateway URL.
|
|
145
|
+
`,
|
|
146
|
+
models: `
|
|
147
|
+
Usage:
|
|
148
|
+
subc models
|
|
149
|
+
subc models help
|
|
150
|
+
|
|
151
|
+
List available Subconscious models.
|
|
152
|
+
`,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
function printRetiredSetup(args = []) {
|
|
156
|
+
const maybeAgent = resolveAgent(args[0]);
|
|
157
|
+
const command = maybeAgent ? agentCommandName(maybeAgent) : 'cursor';
|
|
158
|
+
console.error(`
|
|
159
|
+
${c.red}subc setup is no longer used.${c.reset} Use the agent command instead:
|
|
81
160
|
|
|
82
|
-
|
|
161
|
+
${c.cyan}subc ${command} install${c.reset}
|
|
162
|
+
${c.cyan}subc ${command} uninstall${c.reset}
|
|
163
|
+
${c.cyan}subc ${command} help${c.reset}
|
|
164
|
+
`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function printRetiredSettings() {
|
|
168
|
+
console.error(`
|
|
169
|
+
${c.red}subc settings is no longer used.${c.reset} Use:
|
|
170
|
+
|
|
171
|
+
${c.cyan}subc config${c.reset} List profiles
|
|
172
|
+
${c.cyan}subc -p NAME config${c.reset} Show a profile
|
|
173
|
+
${c.cyan}subc -p NAME config edit${c.reset} Open the profile env file
|
|
174
|
+
${c.cyan}subc config help${c.reset}
|
|
83
175
|
`);
|
|
84
176
|
}
|
|
85
177
|
|
|
@@ -91,10 +183,8 @@ const authCommands = {
|
|
|
91
183
|
};
|
|
92
184
|
|
|
93
185
|
function extractProfile(argv) {
|
|
94
|
-
let profileName =
|
|
95
|
-
|
|
96
|
-
process.env.MBTA_PROFILE?.trim() ||
|
|
97
|
-
DEFAULT_PROFILE;
|
|
186
|
+
let profileName = process.env.SUBC_PROFILE?.trim() || DEFAULT_PROFILE;
|
|
187
|
+
let profileExplicit = false;
|
|
98
188
|
const args = [];
|
|
99
189
|
for (let i = 0; i < argv.length; i++) {
|
|
100
190
|
const arg = argv[i];
|
|
@@ -106,39 +196,41 @@ function extractProfile(argv) {
|
|
|
106
196
|
const value = argv[++i];
|
|
107
197
|
if (!value) throw new Error(`${arg} requires a profile name`);
|
|
108
198
|
profileName = value;
|
|
199
|
+
profileExplicit = true;
|
|
109
200
|
continue;
|
|
110
201
|
}
|
|
111
202
|
if (arg.startsWith('--profile=')) {
|
|
112
203
|
profileName = arg.slice('--profile='.length);
|
|
204
|
+
profileExplicit = true;
|
|
113
205
|
continue;
|
|
114
206
|
}
|
|
115
207
|
args.push(arg);
|
|
116
208
|
}
|
|
117
209
|
validateProfileName(profileName);
|
|
118
|
-
return { args, profileName };
|
|
210
|
+
return { args, profileName, profileExplicit };
|
|
119
211
|
}
|
|
120
212
|
|
|
121
213
|
function requireNamedProfile(profile) {
|
|
122
214
|
if (profile.name !== DEFAULT_PROFILE && !profile.exists) {
|
|
123
215
|
throw new Error(
|
|
124
216
|
`Profile '${profile.name}' does not exist. Create it with ` +
|
|
125
|
-
`subc
|
|
217
|
+
`subc -p ${profile.name} config --api-key KEY`,
|
|
126
218
|
);
|
|
127
219
|
}
|
|
128
220
|
}
|
|
129
221
|
|
|
130
222
|
async function main() {
|
|
131
223
|
const parsed = extractProfile(process.argv.slice(2));
|
|
132
|
-
const { args, profileName } = parsed;
|
|
224
|
+
const { args, profileName, profileExplicit } = parsed;
|
|
133
225
|
const command = args[0];
|
|
134
226
|
|
|
135
|
-
if (!command || command === '--help' || command === '-h') {
|
|
227
|
+
if (!command || command === '--help' || command === '-h' || (command === 'help' && !args[1])) {
|
|
136
228
|
printHelp();
|
|
137
229
|
return;
|
|
138
230
|
}
|
|
139
231
|
|
|
140
232
|
if (command === 'help') {
|
|
141
|
-
if (!args[1]) {
|
|
233
|
+
if (!args[1] || isHelpArg(args[1])) {
|
|
142
234
|
printHelp();
|
|
143
235
|
return;
|
|
144
236
|
}
|
|
@@ -156,94 +248,76 @@ async function main() {
|
|
|
156
248
|
return;
|
|
157
249
|
}
|
|
158
250
|
|
|
251
|
+
if (command === 'setup') {
|
|
252
|
+
printRetiredSetup(args.slice(1));
|
|
253
|
+
process.exitCode = 1;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (command === 'settings') {
|
|
258
|
+
printRetiredSettings();
|
|
259
|
+
process.exitCode = 1;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
159
263
|
const authHandler = authCommands[command];
|
|
160
264
|
if (authHandler) {
|
|
265
|
+
if (isHelpArg(args[1])) {
|
|
266
|
+
console.log(COMMAND_HELP[command]);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
161
269
|
const profile = await loadProfile(profileName);
|
|
162
270
|
await authHandler(args.slice(1), { profile, profileName });
|
|
163
271
|
return;
|
|
164
272
|
}
|
|
165
273
|
|
|
166
274
|
if (command === 'config') {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (command === 'settings') {
|
|
172
|
-
if (['help', '-h', '--help'].includes(args[1])) {
|
|
173
|
-
console.log(`
|
|
174
|
-
Usage:
|
|
175
|
-
subc settings
|
|
176
|
-
subc --profile NAME settings
|
|
177
|
-
subc --profile NAME config interactive
|
|
178
|
-
|
|
179
|
-
Interactively choose or create a profile, then edit shared or per-agent settings.
|
|
180
|
-
`);
|
|
275
|
+
if (isHelpArg(args[1])) {
|
|
276
|
+
printConfigHelp();
|
|
181
277
|
return;
|
|
182
278
|
}
|
|
183
|
-
|
|
184
|
-
await configCommand(['interactive'], profileName);
|
|
279
|
+
await configCommand(args.slice(1), profileName, { profileExplicit });
|
|
185
280
|
return;
|
|
186
281
|
}
|
|
187
282
|
|
|
188
283
|
if (command === 'models') {
|
|
284
|
+
if (isHelpArg(args[1])) {
|
|
285
|
+
console.log(COMMAND_HELP.models);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
189
288
|
modelsCommand();
|
|
190
289
|
return;
|
|
191
290
|
}
|
|
192
291
|
|
|
193
|
-
if (command === '
|
|
194
|
-
|
|
292
|
+
if (command === 'upgrade') {
|
|
293
|
+
if (isHelpArg(args[1])) {
|
|
294
|
+
console.log(COMMAND_HELP.upgrade);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
await upgradeCommand(args.slice(1));
|
|
195
298
|
return;
|
|
196
299
|
}
|
|
197
300
|
|
|
198
|
-
if (command === '
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
console.log(`
|
|
202
|
-
Usage:
|
|
203
|
-
subc setup [install|status|uninstall]
|
|
204
|
-
subc setup AGENT [install|status|uninstall] [agent options]
|
|
205
|
-
|
|
206
|
-
Examples:
|
|
207
|
-
subc setup Configure every coding-agent integration
|
|
208
|
-
subc setup status Show every integration's setup status
|
|
209
|
-
subc setup codex Configure only Codex
|
|
210
|
-
subc setup codex status Show only Codex's setup status
|
|
211
|
-
subc setup codex --subagents Configure Codex's legacy subagent mode
|
|
212
|
-
subc setup codex env Print persistent Codex exports for sourcing
|
|
213
|
-
`);
|
|
301
|
+
if (command === 'update-url') {
|
|
302
|
+
if (isHelpArg(args[1])) {
|
|
303
|
+
console.log(COMMAND_HELP['update-url']);
|
|
214
304
|
return;
|
|
215
305
|
}
|
|
216
|
-
|
|
217
|
-
const profile = await loadProfile(profileName);
|
|
218
|
-
const targetHelp = request.targeted && isAgentHelpRequest(request.args);
|
|
219
|
-
const persistentHelper = ['use', 'env', 'unset'].includes(request.action);
|
|
220
|
-
const oneOffApiKey = request.targeted && request.args.includes('--api-key');
|
|
221
|
-
if (!targetHelp && !persistentHelper && !oneOffApiKey) requireNamedProfile(profile);
|
|
222
|
-
const failures = [];
|
|
223
|
-
for (const agent of request.agents) {
|
|
224
|
-
const code = await runAgent(agent, request.args, { profile, setup: true });
|
|
225
|
-
if (code) failures.push(agent.name);
|
|
226
|
-
}
|
|
227
|
-
if (failures.length) {
|
|
228
|
-
throw new Error(`Setup failed for: ${failures.join(', ')}`);
|
|
229
|
-
}
|
|
230
|
-
if (targetHelp || persistentHelper) return;
|
|
231
|
-
const subject = request.targeted ? request.agents[0].name : 'Coding-agent';
|
|
232
|
-
const message =
|
|
233
|
-
request.action === 'status'
|
|
234
|
-
? `${subject} status check complete.`
|
|
235
|
-
: request.action === 'uninstall'
|
|
236
|
-
? `${subject} integration${request.targeted ? '' : 's'} removed.`
|
|
237
|
-
: `${subject} setup complete.`;
|
|
238
|
-
console.log(`\n ${c.green}${c.bold}✓ ${message}${c.reset}\n`);
|
|
306
|
+
await updateUrlCommand(args.slice(1), { profileName });
|
|
239
307
|
return;
|
|
240
308
|
}
|
|
241
309
|
|
|
242
310
|
const agent = resolveAgent(command);
|
|
243
311
|
if (agent) {
|
|
312
|
+
const agentArgs = args.slice(1);
|
|
244
313
|
const profile = await loadProfile(profileName);
|
|
245
|
-
if (!isAgentHelpRequest(
|
|
246
|
-
|
|
314
|
+
if (!isAgentHelpRequest(agentArgs)) {
|
|
315
|
+
const action = parseAgentAction(agent, agentArgs);
|
|
316
|
+
if (action.action !== 'status' && action.action !== 'uninstall') {
|
|
317
|
+
requireNamedProfile(profile);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
await runAgent(agent, agentArgs, { profile });
|
|
247
321
|
return;
|
|
248
322
|
}
|
|
249
323
|
|