subconscious-cli 6.3.1 → 7.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 +5 -2
- package/bin/agent-launch.js +1 -25
- package/bin/agents.js +2 -19
- package/bin/auth.js +31 -7
- package/bin/claude-config.js +13 -629
- package/bin/cli.js +42 -0
- package/bin/feedback.js +2 -1
- package/bin/models.js +4 -2
- 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/network.js +710 -0
- package/bin/profiles.js +7 -1
- package/bin/runbook/claude-code/agent.json +6 -27
- package/bin/runbook/claude-code/install.sh +3 -2
- package/bin/runbook/shared.json +10 -0
- package/bin/update-check.js +2 -1
- package/bin/upgrade.js +2 -1
- package/bin/usage.js +19 -3
- package/bin/windows/launch.js +5 -17
- package/package.json +1 -1
- package/bin/claude-config-drift.js +0 -58
- package/bin/claude-config-plugins.js +0 -103
package/README.md
CHANGED
|
@@ -351,8 +351,11 @@ subc opencode uninstall
|
|
|
351
351
|
```
|
|
352
352
|
|
|
353
353
|
`subc claude uninstall` and `subc opencode uninstall` only clean leftover files
|
|
354
|
-
from older overwrite-style setup.
|
|
355
|
-
|
|
354
|
+
from older overwrite-style setup. `subc claude` writes nothing persistent: it
|
|
355
|
+
passes routing, credentials and the model picker through the launch env and
|
|
356
|
+
`--settings`, and sessions use your own Claude config (~/.claude, or your
|
|
357
|
+
CLAUDE_CONFIG_DIR). Launch those agents with `subc claude` / `subc opencode`;
|
|
358
|
+
they do not need install.
|
|
356
359
|
|
|
357
360
|
Inspect a persistent integration with `subc <agent> status`. A one-off
|
|
358
361
|
`--api-key` passed to install takes precedence for that command but is not
|
package/bin/agent-launch.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
1
|
import { agentInputs, agentKeyName } from './agent-data.js';
|
|
3
2
|
import { openCodeConfigFromEnv } from './opencode-provider.js';
|
|
4
|
-
import {
|
|
3
|
+
import { isUnsetSetting } from './profiles.js';
|
|
5
4
|
|
|
6
5
|
// One pass, so a substituted value (a prompt, a settings document) is never
|
|
7
6
|
// scanned again for placeholders.
|
|
@@ -9,9 +8,6 @@ const PLACEHOLDER = /\$\{([A-Z_][A-Z0-9_]*)\}(\/)?|\{([A-Za-z][A-Za-z0-9]*)\}/g;
|
|
|
9
8
|
const present = (value) =>
|
|
10
9
|
value !== undefined && value !== null && value !== '';
|
|
11
10
|
|
|
12
|
-
// CLAUDE_CODE_ISOLATE values that stop Claude Code config isolation.
|
|
13
|
-
const ISOLATE_OFF = new Set(['off', '0', 'false', 'no']);
|
|
14
|
-
|
|
15
11
|
function tokensFor(agent, env, extra) {
|
|
16
12
|
const baseUrl = String(env.GATEWAY_URL ?? '').replace(/\/+$/, '');
|
|
17
13
|
const keyName = agentKeyName(agent);
|
|
@@ -23,26 +19,6 @@ function tokensFor(agent, env, extra) {
|
|
|
23
19
|
// Left for the runbook, which writes the file and substitutes its path.
|
|
24
20
|
tempFile: '{tempFile}',
|
|
25
21
|
opencodeConfig: () => JSON.stringify(openCodeConfigFromEnv(env)),
|
|
26
|
-
// Claude Code config isolation: subc sessions keep their Claude config
|
|
27
|
-
// (settings, sessions, plugins, credentials) under the subc config dir
|
|
28
|
-
// instead of ~/.claude, so session state cannot bleed into a plain
|
|
29
|
-
// `claude` installation. Empty when CLAUDE_CODE_ISOLATE opts out.
|
|
30
|
-
claudeConfigDir: () => {
|
|
31
|
-
const setting = String(env.CLAUDE_CODE_ISOLATE ?? '')
|
|
32
|
-
.trim()
|
|
33
|
-
.toLowerCase();
|
|
34
|
-
// Opted out: the user's own folder, as plain `claude` would use it.
|
|
35
|
-
if (ISOLATE_OFF.has(setting)) {
|
|
36
|
-
return String(
|
|
37
|
-
env.SUBC_CLAUDE_CONFIG_SOURCE || env.CLAUDE_CONFIG_DIR || '',
|
|
38
|
-
).trim();
|
|
39
|
-
}
|
|
40
|
-
const base = String(env.SUBC_CONFIG_DIR ?? '').trim() || configDir();
|
|
41
|
-
// A relative base would put the config, MCP tokens included, in the
|
|
42
|
-
// current directory; use ~/.claude unchanged instead.
|
|
43
|
-
if (!path.isAbsolute(base)) return '';
|
|
44
|
-
return path.join(base, 'claude-code');
|
|
45
|
-
},
|
|
46
22
|
...extra,
|
|
47
23
|
};
|
|
48
24
|
}
|
package/bin/agents.js
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
import { launchPlan, resolveInputs } from './agent-launch.js';
|
|
20
20
|
import { planCommand, runCommand, scriptCommand } from './agent-spawn.js';
|
|
21
21
|
import { getApiKey } from './auth.js';
|
|
22
|
-
import { prepareClaudeLaunch } from './claude-config.js';
|
|
23
22
|
import {
|
|
24
23
|
applyClaudePickerCatalog,
|
|
25
24
|
CLAUDE_PICKER_ENV_DESCRIPTION,
|
|
@@ -37,6 +36,7 @@ import {
|
|
|
37
36
|
PUBLIC_CATALOG_FALLBACK_MESSAGE,
|
|
38
37
|
resolveModelCatalog,
|
|
39
38
|
} from './models.js';
|
|
39
|
+
import { describeFailure } from './network.js';
|
|
40
40
|
import { isUnsetSetting, resolvedModelSetting } from './profiles.js';
|
|
41
41
|
import { runWindowsAgent } from './windows/agents.js';
|
|
42
42
|
|
|
@@ -372,7 +372,7 @@ async function resolvedModelsForLaunch(profile, apiKey, selectedModel) {
|
|
|
372
372
|
console.error(
|
|
373
373
|
` ${c.yellow}Could not fetch the live model catalog; using packaged defaults.${c.reset}`,
|
|
374
374
|
);
|
|
375
|
-
console.error(` ${c.dim}${catalog.error
|
|
375
|
+
console.error(` ${c.dim}${describeFailure(catalog.error)}${c.reset}\n`);
|
|
376
376
|
}
|
|
377
377
|
return catalog;
|
|
378
378
|
}
|
|
@@ -546,22 +546,5 @@ async function launchAgent(agent, argv, profile, headless) {
|
|
|
546
546
|
// The headless endpoint is the one endpoint for every harness, Claude too.
|
|
547
547
|
const launchEnv = headless ? { ...env, CLAUDE_GATEWAY_URL: '' } : env;
|
|
548
548
|
const plan = launchPlan(agent, { env: launchEnv, ...headlessParts(rest) });
|
|
549
|
-
if (agent.id === CLAUDE_CODE) {
|
|
550
|
-
const {
|
|
551
|
-
warnings,
|
|
552
|
-
refused,
|
|
553
|
-
env: sourceEnv,
|
|
554
|
-
} = prepareClaudeLaunch(agent, plan.env, launchEnv);
|
|
555
|
-
const color = refused ? c.red : c.yellow;
|
|
556
|
-
for (const warning of warnings) {
|
|
557
|
-
console.error(` ${color}${warning}${c.reset}`);
|
|
558
|
-
}
|
|
559
|
-
if (refused) {
|
|
560
|
-
process.exitCode = 1;
|
|
561
|
-
return 1;
|
|
562
|
-
}
|
|
563
|
-
const prepared = { ...plan, env: { ...plan.env, ...sourceEnv } };
|
|
564
|
-
return runCommand(planCommand(agent, prepared, binDir));
|
|
565
|
-
}
|
|
566
549
|
return runCommand(planCommand(agent, plan, binDir));
|
|
567
550
|
}
|
package/bin/auth.js
CHANGED
|
@@ -16,6 +16,12 @@ import fs from 'node:fs/promises';
|
|
|
16
16
|
import os from 'node:os';
|
|
17
17
|
import path from 'node:path';
|
|
18
18
|
import { c } from './colors.js';
|
|
19
|
+
import {
|
|
20
|
+
describeFetchError,
|
|
21
|
+
proxyAwareFetch,
|
|
22
|
+
proxyHint,
|
|
23
|
+
tlsTrustHint,
|
|
24
|
+
} from './network.js';
|
|
19
25
|
import {
|
|
20
26
|
clearProfileApiKey,
|
|
21
27
|
DEFAULT_PROFILE,
|
|
@@ -128,7 +134,10 @@ function terminalLink(url) {
|
|
|
128
134
|
return `\u001b]8;;${url}\u0007${url}\u001b]8;;\u0007`;
|
|
129
135
|
}
|
|
130
136
|
|
|
131
|
-
export async function registerDeviceLogin(
|
|
137
|
+
export async function registerDeviceLogin(
|
|
138
|
+
platformUrl,
|
|
139
|
+
fetchImpl = proxyAwareFetch,
|
|
140
|
+
) {
|
|
132
141
|
const res = await fetchImpl(`${platformUrl}/api/cli/device/register`, {
|
|
133
142
|
method: 'POST',
|
|
134
143
|
signal: AbortSignal.timeout(8000),
|
|
@@ -146,7 +155,7 @@ export async function registerDeviceLogin(platformUrl, fetchImpl = fetch) {
|
|
|
146
155
|
export async function pollDeviceLogin(
|
|
147
156
|
platformUrl,
|
|
148
157
|
deviceCode,
|
|
149
|
-
fetchImpl =
|
|
158
|
+
fetchImpl = proxyAwareFetch,
|
|
150
159
|
) {
|
|
151
160
|
const res = await fetchImpl(`${platformUrl}/api/cli/device/token`, {
|
|
152
161
|
method: 'POST',
|
|
@@ -180,10 +189,25 @@ function printDeviceInstructions(platformUrl, userCode) {
|
|
|
180
189
|
|
|
181
190
|
function printLoginFallback() {
|
|
182
191
|
console.error(
|
|
183
|
-
` ${c.dim}You can also copy an API key from the dashboard and run${c.reset} ${c.cyan}subc update-key <your-api-key>${c.reset}${c.dim}.${c.reset}`,
|
|
192
|
+
` ${c.dim}You can also copy an API key from the dashboard and run${c.reset} ${c.cyan}subc update-key <your-api-key>${c.reset}${c.dim}, which needs no network.${c.reset}`,
|
|
184
193
|
);
|
|
185
194
|
}
|
|
186
195
|
|
|
196
|
+
// Render a login failure with the real underlying network cause. Node's fetch
|
|
197
|
+
// reports every connection problem as a bare "fetch failed"; the diagnostic
|
|
198
|
+
// (`ENOTFOUND`, `ECONNREFUSED`, `ETIMEDOUT`, TLS, proxy) lives on `cause`.
|
|
199
|
+
function printLoginFailure(platformUrl, error) {
|
|
200
|
+
const message = describeFetchError(error);
|
|
201
|
+
console.error(` ${c.red}✗ ${message}${c.reset}`);
|
|
202
|
+
const hint = tlsTrustHint(error);
|
|
203
|
+
if (hint) {
|
|
204
|
+
console.error(` ${c.yellow}${hint}${c.reset}`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const proxy = proxyHint(error, platformUrl);
|
|
208
|
+
if (proxy) console.error(` ${c.dim}${proxy}${c.reset}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
187
211
|
async function saveLoginKey(profileName, token) {
|
|
188
212
|
if (profileName === DEFAULT_PROFILE) {
|
|
189
213
|
const config = await loadConfig();
|
|
@@ -196,7 +220,7 @@ async function saveLoginKey(profileName, token) {
|
|
|
196
220
|
export async function loginCommand(_argv = [], options = {}) {
|
|
197
221
|
const profileName = options.profileName || DEFAULT_PROFILE;
|
|
198
222
|
const existing = await getApiKey(options.profile);
|
|
199
|
-
const fetchImpl = options.fetchImpl ||
|
|
223
|
+
const fetchImpl = options.fetchImpl || proxyAwareFetch;
|
|
200
224
|
|
|
201
225
|
if (existing) {
|
|
202
226
|
const profile = await ensureProfile(profileName, existing.key);
|
|
@@ -225,7 +249,7 @@ export async function loginCommand(_argv = [], options = {}) {
|
|
|
225
249
|
try {
|
|
226
250
|
registered = await registerDeviceLogin(platformUrl, fetchImpl);
|
|
227
251
|
} catch (error) {
|
|
228
|
-
|
|
252
|
+
printLoginFailure(platformUrl, error);
|
|
229
253
|
printLoginFallback();
|
|
230
254
|
console.log();
|
|
231
255
|
process.exitCode = 1;
|
|
@@ -299,7 +323,7 @@ export async function loginCommand(_argv = [], options = {}) {
|
|
|
299
323
|
} catch (error) {
|
|
300
324
|
clearInterval(spinner);
|
|
301
325
|
process.stdout.write(`\r${' '.repeat(50)}\r`);
|
|
302
|
-
|
|
326
|
+
printLoginFailure(platformUrl, error);
|
|
303
327
|
printLoginFallback();
|
|
304
328
|
console.log();
|
|
305
329
|
process.exitCode = 1;
|
|
@@ -380,7 +404,7 @@ export async function whoamiCommand(_argv = [], options = {}) {
|
|
|
380
404
|
|
|
381
405
|
// Validate the key against the server; falls back to offline display if unreachable
|
|
382
406
|
try {
|
|
383
|
-
const res = await
|
|
407
|
+
const res = await proxyAwareFetch(
|
|
384
408
|
`${getPlatformUrl(options.profile)}/api/cli/whoami`,
|
|
385
409
|
{
|
|
386
410
|
headers: { Authorization: `Bearer ${key}` },
|