@intent-systems/nexus 2026.1.5-4 → 2026.1.5-5
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/dist/agents/agent-id.js +41 -0
- package/dist/agents/auth-profiles.js +114 -25
- package/dist/agents/identity-state.js +79 -0
- package/dist/agents/model-auth.js +1 -0
- package/dist/agents/model-fallback.js +15 -9
- package/dist/agents/model-selection.js +1 -1
- package/dist/agents/models-config.js +17 -11
- package/dist/agents/pi-embedded-runner.js +101 -9
- package/dist/agents/sandbox.js +12 -3
- package/dist/agents/skill-runner.js +29 -4
- package/dist/agents/skill-usage.js +114 -11
- package/dist/agents/skills-status.js +4 -4
- package/dist/agents/skills.js +18 -7
- package/dist/agents/subagent-registry.js +25 -11
- package/dist/agents/system-prompt.js +16 -0
- package/dist/agents/tool-policy.js +19 -3
- package/dist/agents/tools/browser-tool.js +5 -2
- package/dist/agents/tools/image-tool.js +93 -8
- package/dist/agents/tools/sessions-announce-target.js +5 -1
- package/dist/agents/workspace.js +55 -46
- package/dist/auto-reply/command-detection.js +2 -1
- package/dist/auto-reply/reply/directive-handling.js +153 -28
- package/dist/auto-reply/reply/directives.js +17 -2
- package/dist/auto-reply/reply/model-selection.js +8 -3
- package/dist/auto-reply/reply/queue.js +2 -2
- package/dist/auto-reply/reply.js +1 -1
- package/dist/auto-reply/thinking.js +15 -0
- package/dist/browser/chrome.js +1 -1
- package/dist/browser/client.js +2 -0
- package/dist/browser/config.js +6 -2
- package/dist/browser/pw-tools-core.js +3 -0
- package/dist/browser/routes/agent.js +14 -0
- package/dist/canvas-host/server.js +1 -1
- package/dist/capabilities/detector.js +46 -15
- package/dist/capabilities/registry.js +2 -1
- package/dist/cli/cloud-cli.js +12 -7
- package/dist/cli/credential-cli.js +139 -17
- package/dist/cli/gateway-cli.js +1 -1
- package/dist/cli/log-cli.js +25 -0
- package/dist/cli/pairing-cli.js +1 -1
- package/dist/cli/program.js +58 -6
- package/dist/cli/run-main.js +1 -1
- package/dist/cli/skills-cli.js +144 -21
- package/dist/cli/skills-hub-cli.js +59 -29
- package/dist/cli/tool-connector-cli.js +99 -24
- package/dist/cli/upstream-sync-cli.js +253 -96
- package/dist/cli/usage-cli.js +14 -0
- package/dist/commands/auth-choice-options.js +6 -1
- package/dist/commands/auth-choice.js +157 -5
- package/dist/commands/bootstrap-preset.js +10 -6
- package/dist/commands/capabilities.js +33 -6
- package/dist/commands/claude-md.js +3 -2
- package/dist/commands/config-view.js +1 -1
- package/dist/commands/configure.js +4 -4
- package/dist/commands/credential.js +497 -36
- package/dist/commands/cursor-rules.js +39 -19
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/identity.js +28 -31
- package/dist/commands/init.js +15 -18
- package/dist/commands/log.js +134 -0
- package/dist/commands/models/fallbacks.js +1 -1
- package/dist/commands/models/image-fallbacks.js +1 -1
- package/dist/commands/models/list.js +1 -1
- package/dist/commands/models/scan.js +1 -1
- package/dist/commands/onboard-auth.js +27 -2
- package/dist/commands/onboard-eve-identity.js +7 -8
- package/dist/commands/onboard-non-interactive.js +4 -2
- package/dist/commands/onboard-quickstart.js +18 -11
- package/dist/commands/quest-state.js +271 -0
- package/dist/commands/quest.js +53 -13
- package/dist/commands/reset.js +1 -1
- package/dist/commands/sessions-ingest.js +5 -4
- package/dist/commands/setup.js +4 -2
- package/dist/commands/skills-manifest.js +2 -2
- package/dist/commands/status.js +179 -61
- package/dist/commands/suggestions.js +1 -1
- package/dist/commands/usage-tracking.js +32 -0
- package/dist/commands/usage-upload.js +6 -1
- package/dist/config/defaults.js +1 -3
- package/dist/config/includes.js +5 -7
- package/dist/config/io.js +88 -16
- package/dist/config/legacy.js +4 -2
- package/dist/config/paths.js +16 -0
- package/dist/config/sessions.js +9 -5
- package/dist/config/zod-schema.js +4 -3
- package/dist/control-plane/broker/broker.js +131 -78
- package/dist/control-plane/compaction.js +3 -5
- package/dist/control-plane/factory.js +2 -2
- package/dist/control-plane/index.js +2 -2
- package/dist/control-plane/odu/agents.js +28 -23
- package/dist/control-plane/odu/interaction-tools.js +62 -50
- package/dist/control-plane/odu/prompt-loader.js +8 -8
- package/dist/control-plane/odu/runtime.js +87 -75
- package/dist/control-plane/odu-control-plane.js +14 -12
- package/dist/control-plane/single-agent.js +13 -13
- package/dist/credentials/store.js +133 -7
- package/dist/gateway/server-browser.js +5 -4
- package/dist/gateway/server-methods/cron.js +11 -1
- package/dist/gateway/server.js +14 -7
- package/dist/infra/bonjour.js +1 -1
- package/dist/infra/event-log.js +8 -2
- package/dist/infra/path-env.js +1 -2
- package/dist/infra/provider-usage.auth.js +5 -3
- package/dist/infra/provider-usage.fetch.claude.js +16 -6
- package/dist/infra/provider-usage.fetch.minimax.js +8 -3
- package/dist/infra/provider-usage.js +9 -5
- package/dist/infra/restart.js +2 -2
- package/dist/infra/usage-settings.js +78 -0
- package/dist/infra/usage-suggestions.js +17 -5
- package/dist/infra/usage-upload.js +38 -1
- package/dist/infra/voicewake.js +2 -2
- package/dist/media/image-ops.js +3 -1
- package/dist/memory/index.js +2 -381
- package/dist/pairing/pairing-store.js +24 -0
- package/dist/providers/github-copilot-auth.js +1 -1
- package/dist/routing/resolve-route.js +6 -6
- package/dist/routing/session-key.js +3 -1
- package/dist/sessions/send-policy.js +5 -5
- package/dist/slack/monitor.js +22 -1
- package/dist/telegram/reaction-level.js +2 -1
- package/dist/utils.js +4 -3
- package/dist/wizard/onboarding.js +29 -7
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { normalizeElevatedLevel, normalizeThinkLevel, normalizeVerboseLevel, } from "../thinking.js";
|
|
1
|
+
import { normalizeElevatedLevel, normalizeReasoningLevel, normalizeThinkLevel, normalizeVerboseLevel, } from "../thinking.js";
|
|
2
2
|
export function extractThinkDirective(body) {
|
|
3
3
|
if (!body)
|
|
4
4
|
return { cleaned: "", hasDirective: false };
|
|
5
5
|
// Match the longest keyword first to avoid partial captures (e.g. "/think:high")
|
|
6
|
-
const match = body.match(/(?:^|\s)\/(?:thinking|think|t)\s*:?\s*([a-zA-Z-]+)
|
|
6
|
+
const match = body.match(/(?:^|\s)\/(?:thinking|think|t)(?=$|\s|:)\s*:?\s*([a-zA-Z-]+)?\b/i);
|
|
7
7
|
const thinkLevel = normalizeThinkLevel(match?.[1]);
|
|
8
8
|
const cleaned = match
|
|
9
9
|
? body.replace(match[0], "").replace(/\s+/g, " ").trim()
|
|
@@ -45,6 +45,21 @@ export function extractElevatedDirective(body) {
|
|
|
45
45
|
hasDirective: !!match,
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
+
export function extractReasoningDirective(body) {
|
|
49
|
+
if (!body)
|
|
50
|
+
return { cleaned: "", hasDirective: false };
|
|
51
|
+
const match = body.match(/(?:^|\s)\/(?:reasoning|reason)(?=$|\s|:)\s*:?\s*([a-zA-Z-]+)?\b/i);
|
|
52
|
+
const reasoningLevel = normalizeReasoningLevel(match?.[1]);
|
|
53
|
+
const cleaned = match
|
|
54
|
+
? body.replace(match[0], "").replace(/\s+/g, " ").trim()
|
|
55
|
+
: body.trim();
|
|
56
|
+
return {
|
|
57
|
+
cleaned,
|
|
58
|
+
reasoningLevel,
|
|
59
|
+
rawLevel: match?.[1],
|
|
60
|
+
hasDirective: !!match,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
48
63
|
export function extractStatusDirective(body) {
|
|
49
64
|
if (!body)
|
|
50
65
|
return { cleaned: "", hasDirective: false };
|
|
@@ -122,7 +122,8 @@ export function resolveModelDirectiveSelection(params) {
|
|
|
122
122
|
if (params.provider && provider !== normalizeProviderId(params.provider))
|
|
123
123
|
continue;
|
|
124
124
|
const haystack = `${provider}/${model}`.toLowerCase();
|
|
125
|
-
if (haystack.includes(fragment) ||
|
|
125
|
+
if (haystack.includes(fragment) ||
|
|
126
|
+
model.toLowerCase().includes(fragment)) {
|
|
126
127
|
candidates.push({ provider, model });
|
|
127
128
|
}
|
|
128
129
|
}
|
|
@@ -132,7 +133,10 @@ export function resolveModelDirectiveSelection(params) {
|
|
|
132
133
|
for (const [aliasKey, entry] of aliasIndex.byAlias.entries()) {
|
|
133
134
|
if (!aliasKey.includes(fragment))
|
|
134
135
|
continue;
|
|
135
|
-
aliasMatches.push({
|
|
136
|
+
aliasMatches.push({
|
|
137
|
+
provider: entry.ref.provider,
|
|
138
|
+
model: entry.ref.model,
|
|
139
|
+
});
|
|
136
140
|
}
|
|
137
141
|
for (const match of aliasMatches) {
|
|
138
142
|
const key = modelKey(match.provider, match.model);
|
|
@@ -176,7 +180,8 @@ export function resolveModelDirectiveSelection(params) {
|
|
|
176
180
|
}
|
|
177
181
|
const resolvedKey = modelKey(resolved.ref.provider, resolved.ref.model);
|
|
178
182
|
if (allowedModelKeys.size === 0 || allowedModelKeys.has(resolvedKey)) {
|
|
179
|
-
const alias = resolved.alias ??
|
|
183
|
+
const alias = resolved.alias ??
|
|
184
|
+
pickAliasForKey(resolved.ref.provider, resolved.ref.model);
|
|
180
185
|
return {
|
|
181
186
|
selection: {
|
|
182
187
|
provider: resolved.ref.provider,
|
|
@@ -370,8 +370,8 @@ function defaultQueueModeForProvider(provider) {
|
|
|
370
370
|
export function resolveQueueSettings(params) {
|
|
371
371
|
const providerKey = params.provider?.trim().toLowerCase();
|
|
372
372
|
const queueCfg = params.cfg.routing?.queue;
|
|
373
|
-
const providerModeRaw = providerKey && queueCfg?.
|
|
374
|
-
? queueCfg.
|
|
373
|
+
const providerModeRaw = providerKey && queueCfg?.byChannel
|
|
374
|
+
? queueCfg.byChannel[providerKey]
|
|
375
375
|
: undefined;
|
|
376
376
|
const resolvedMode = params.inlineMode ??
|
|
377
377
|
normalizeQueueMode(params.sessionEntry?.queueMode) ??
|
package/dist/auto-reply/reply.js
CHANGED
|
@@ -35,7 +35,7 @@ export { extractElevatedDirective, extractThinkDirective, extractVerboseDirectiv
|
|
|
35
35
|
export { extractQueueDirective } from "./reply/queue.js";
|
|
36
36
|
export { extractReplyToTag } from "./reply/reply-tags.js";
|
|
37
37
|
const BARE_SESSION_RESET_PROMPT = "A new session was started via /new or /reset. Say hi briefly (1-2 sentences) and ask what the user wants to do next. Do not mention internal steps, files, tools, or reasoning.";
|
|
38
|
-
const CONTROL_COMMAND_PREFIX_RE = /^\/(?:status|help|thinking|think|t|verbose|v|elevated|elev|model|queue|activation|send|restart|reset|new|compact)\b/i;
|
|
38
|
+
const CONTROL_COMMAND_PREFIX_RE = /^\/(?:status|help|thinking|think|t|reasoning|reason|verbose|v|elevated|elev|model|queue|activation|send|restart|reset|new|compact)\b/i;
|
|
39
39
|
function normalizeAllowToken(value) {
|
|
40
40
|
if (!value)
|
|
41
41
|
return "";
|
|
@@ -21,6 +21,8 @@ export function normalizeThinkLevel(raw) {
|
|
|
21
21
|
"max",
|
|
22
22
|
].includes(key))
|
|
23
23
|
return "high";
|
|
24
|
+
if (["xhigh", "x-high", "extra-high"].includes(key))
|
|
25
|
+
return "xhigh";
|
|
24
26
|
if (["think"].includes(key))
|
|
25
27
|
return "minimal";
|
|
26
28
|
return undefined;
|
|
@@ -47,3 +49,16 @@ export function normalizeElevatedLevel(raw) {
|
|
|
47
49
|
return "on";
|
|
48
50
|
return undefined;
|
|
49
51
|
}
|
|
52
|
+
// Normalize reasoning visibility flags used to control <think> output.
|
|
53
|
+
export function normalizeReasoningLevel(raw) {
|
|
54
|
+
if (!raw)
|
|
55
|
+
return undefined;
|
|
56
|
+
const key = raw.toLowerCase();
|
|
57
|
+
if (["off", "false", "no", "0"].includes(key))
|
|
58
|
+
return "off";
|
|
59
|
+
if (["on", "true", "yes", "1"].includes(key))
|
|
60
|
+
return "on";
|
|
61
|
+
if (["stream", "live"].includes(key))
|
|
62
|
+
return "stream";
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
package/dist/browser/chrome.js
CHANGED
|
@@ -7,7 +7,7 @@ import { ensurePortAvailable } from "../infra/ports.js";
|
|
|
7
7
|
import { createSubsystemLogger } from "../logging.js";
|
|
8
8
|
import { CONFIG_DIR } from "../utils.js";
|
|
9
9
|
import { normalizeCdpWsUrl } from "./cdp.js";
|
|
10
|
-
import { DEFAULT_NEXUS_BROWSER_COLOR, DEFAULT_NEXUS_BROWSER_PROFILE_NAME } from "./constants.js";
|
|
10
|
+
import { DEFAULT_NEXUS_BROWSER_COLOR, DEFAULT_NEXUS_BROWSER_PROFILE_NAME, } from "./constants.js";
|
|
11
11
|
const log = createSubsystemLogger("browser").child("chrome");
|
|
12
12
|
function exists(filePath) {
|
|
13
13
|
try {
|
package/dist/browser/client.js
CHANGED
|
@@ -96,6 +96,8 @@ export async function browserSnapshot(baseUrl, opts) {
|
|
|
96
96
|
q.set("targetId", opts.targetId);
|
|
97
97
|
if (typeof opts.limit === "number")
|
|
98
98
|
q.set("limit", String(opts.limit));
|
|
99
|
+
if (typeof opts.maxChars === "number")
|
|
100
|
+
q.set("maxChars", String(opts.maxChars));
|
|
99
101
|
if (opts.profile)
|
|
100
102
|
q.set("profile", opts.profile);
|
|
101
103
|
return await fetchBrowserJson(`${baseUrl}/snapshot?${q.toString()}`, {
|
package/dist/browser/config.js
CHANGED
|
@@ -56,7 +56,8 @@ function ensureDefaultProfile(profiles, defaultColor, legacyCdpPort, derivedDefa
|
|
|
56
56
|
}
|
|
57
57
|
export function resolveBrowserConfig(cfg) {
|
|
58
58
|
const enabled = cfg?.enabled ?? DEFAULT_NEXUS_BROWSER_ENABLED;
|
|
59
|
-
const envControlUrl = (process.env.NEXUS_BROWSER_CONTROL_URL ??
|
|
59
|
+
const envControlUrl = (process.env.NEXUS_BROWSER_CONTROL_URL ??
|
|
60
|
+
process.env.NEXUS_BROWSER_CONTROL_URL)?.trim();
|
|
60
61
|
const derivedControlPort = (() => {
|
|
61
62
|
const raw = (process.env.NEXUS_GATEWAY_PORT ?? process.env.NEXUS_GATEWAY_PORT)?.trim();
|
|
62
63
|
if (!raw)
|
|
@@ -69,7 +70,10 @@ export function resolveBrowserConfig(cfg) {
|
|
|
69
70
|
const derivedControlUrl = derivedControlPort
|
|
70
71
|
? `http://127.0.0.1:${derivedControlPort}`
|
|
71
72
|
: null;
|
|
72
|
-
const controlInfo = parseHttpUrl(cfg?.controlUrl ??
|
|
73
|
+
const controlInfo = parseHttpUrl(cfg?.controlUrl ??
|
|
74
|
+
envControlUrl ??
|
|
75
|
+
derivedControlUrl ??
|
|
76
|
+
DEFAULT_NEXUS_BROWSER_CONTROL_URL, "browser.controlUrl");
|
|
73
77
|
const controlPort = controlInfo.port;
|
|
74
78
|
const defaultColor = normalizeHexColor(cfg?.color);
|
|
75
79
|
const derivedCdpRange = deriveDefaultBrowserCdpPortRange(controlPort);
|
|
@@ -20,6 +20,9 @@ export async function snapshotAiViaPlaywright(opts) {
|
|
|
20
20
|
const result = await maybe._snapshotForAI({
|
|
21
21
|
timeout: Math.max(500, Math.min(60_000, Math.floor(opts.timeoutMs ?? 5000))),
|
|
22
22
|
track: "response",
|
|
23
|
+
...(typeof opts.maxChars === "number" && Number.isFinite(opts.maxChars)
|
|
24
|
+
? { maxChars: Math.max(1, Math.floor(opts.maxChars)) }
|
|
25
|
+
: {}),
|
|
23
26
|
});
|
|
24
27
|
return { snapshot: String(result?.full ?? "") };
|
|
25
28
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { ensureMediaDir, saveMediaBuffer } from "../../media/store.js";
|
|
3
3
|
import { captureScreenshot, snapshotAria } from "../cdp.js";
|
|
4
|
+
import { DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "../constants.js";
|
|
4
5
|
import { DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES, DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE, normalizeBrowserScreenshot, } from "../screenshot.js";
|
|
5
6
|
import { getProfileContext, jsonError, toBoolean, toNumber, toStringArray, toStringOrEmpty, } from "./utils.js";
|
|
6
7
|
const SELECTOR_UNSUPPORTED_MESSAGE = [
|
|
@@ -498,6 +499,18 @@ export function registerBrowserAgentRoutes(app, ctx) {
|
|
|
498
499
|
? "ai"
|
|
499
500
|
: "aria";
|
|
500
501
|
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : undefined;
|
|
502
|
+
const maxCharsRaw = typeof req.query.maxChars === "string" ||
|
|
503
|
+
typeof req.query.maxChars === "number"
|
|
504
|
+
? Number(req.query.maxChars)
|
|
505
|
+
: undefined;
|
|
506
|
+
const maxChars = format === "ai" &&
|
|
507
|
+
typeof maxCharsRaw === "number" &&
|
|
508
|
+
Number.isFinite(maxCharsRaw) &&
|
|
509
|
+
maxCharsRaw > 0
|
|
510
|
+
? Math.floor(maxCharsRaw)
|
|
511
|
+
: format === "ai"
|
|
512
|
+
? DEFAULT_AI_SNAPSHOT_MAX_CHARS
|
|
513
|
+
: undefined;
|
|
501
514
|
try {
|
|
502
515
|
const tab = await profileCtx.ensureTabAvailable(targetId || undefined);
|
|
503
516
|
if (format === "ai") {
|
|
@@ -507,6 +520,7 @@ export function registerBrowserAgentRoutes(app, ctx) {
|
|
|
507
520
|
const snap = await pw.snapshotAiViaPlaywright({
|
|
508
521
|
cdpUrl: profileCtx.profile.cdpUrl,
|
|
509
522
|
targetId: tab.targetId,
|
|
523
|
+
maxChars,
|
|
510
524
|
});
|
|
511
525
|
return res.json({
|
|
512
526
|
ok: true,
|
|
@@ -126,7 +126,7 @@ async function resolveFilePath(rootReal, urlPath) {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
function isDisabledByEnv() {
|
|
129
|
-
if (process.env.NEXUS_SKIP_CANVAS_HOST === "1"
|
|
129
|
+
if (process.env.NEXUS_SKIP_CANVAS_HOST === "1")
|
|
130
130
|
return true;
|
|
131
131
|
if (process.env.NODE_ENV === "test")
|
|
132
132
|
return true;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { getSkillMetadata, isConfigPathTruthy, loadWorkspaceSkillEntries, resolveRuntimePlatform, resolveSkillConfig, } from "../agents/skills.js";
|
|
3
4
|
import { loadConfig } from "../config/config.js";
|
|
4
|
-
import { resolveStateDir } from "../config/paths.js";
|
|
5
|
-
import { getSkillMetadata, isConfigPathTruthy, loadWorkspaceSkillEntries, resolveSkillConfig, resolveRuntimePlatform, } from "../agents/skills.js";
|
|
6
5
|
import { ensureCredentialIndexSync } from "../credentials/store.js";
|
|
7
|
-
import { NEXUS_ROOT } from "../utils.js";
|
|
6
|
+
import { MANAGED_SKILLS_DIR, NEXUS_ROOT } from "../utils.js";
|
|
8
7
|
import { loadCapabilityRegistry } from "./registry.js";
|
|
9
8
|
const STATUS_PRIORITY = [
|
|
10
9
|
"active",
|
|
@@ -58,7 +57,7 @@ function setupLooksCredentialed(raw) {
|
|
|
58
57
|
lowered.includes("key"));
|
|
59
58
|
}
|
|
60
59
|
function detectSkillUsageActive(skillName) {
|
|
61
|
-
const usageLog = path.join(
|
|
60
|
+
const usageLog = path.join(MANAGED_SKILLS_DIR, skillName, "usage.log");
|
|
62
61
|
try {
|
|
63
62
|
const stat = fs.statSync(usageLog);
|
|
64
63
|
return stat.isFile() && stat.size > 0;
|
|
@@ -67,7 +66,7 @@ function detectSkillUsageActive(skillName) {
|
|
|
67
66
|
return false;
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
|
-
function resolveSkillType(
|
|
69
|
+
function resolveSkillType(_skillName, metadata) {
|
|
71
70
|
if (metadata?.type)
|
|
72
71
|
return metadata.type;
|
|
73
72
|
const requires = metadata?.requires;
|
|
@@ -78,11 +77,16 @@ function resolveSkillType(skillName, metadata) {
|
|
|
78
77
|
return "guide";
|
|
79
78
|
}
|
|
80
79
|
function resolveSkillProviderStatus(params) {
|
|
81
|
-
const { skillName, providerId, config, credentialServices, metadata, setupHint } = params;
|
|
80
|
+
const { skillName, providerId, config, credentialServices, metadata, setupHint, } = params;
|
|
82
81
|
const platform = resolveRuntimePlatform();
|
|
83
82
|
const skillType = resolveSkillType(skillName, metadata);
|
|
84
83
|
if (metadata?.os?.length && !metadata.os.includes(platform)) {
|
|
85
|
-
return {
|
|
84
|
+
return {
|
|
85
|
+
id: providerId,
|
|
86
|
+
kind: "skill",
|
|
87
|
+
status: "unavailable",
|
|
88
|
+
reason: "os",
|
|
89
|
+
};
|
|
86
90
|
}
|
|
87
91
|
const skillKey = metadata?.skillKey ?? skillName;
|
|
88
92
|
const skillConfig = resolveSkillConfig(config, skillKey);
|
|
@@ -108,15 +112,27 @@ function resolveSkillProviderStatus(params) {
|
|
|
108
112
|
return true;
|
|
109
113
|
});
|
|
110
114
|
if (missingEnv.length > 0) {
|
|
111
|
-
return {
|
|
115
|
+
return {
|
|
116
|
+
id: providerId,
|
|
117
|
+
kind: "skill",
|
|
118
|
+
status: "needs_setup",
|
|
119
|
+
reason: "missing_env",
|
|
120
|
+
};
|
|
112
121
|
}
|
|
113
122
|
const missingConfig = (requires?.config ?? []).filter((configPath) => !isConfigPathTruthy(config, configPath));
|
|
114
123
|
if (missingConfig.length > 0) {
|
|
115
|
-
return {
|
|
124
|
+
return {
|
|
125
|
+
id: providerId,
|
|
126
|
+
kind: "skill",
|
|
127
|
+
status: "needs_setup",
|
|
128
|
+
reason: "missing_config",
|
|
129
|
+
};
|
|
116
130
|
}
|
|
117
131
|
const credentialHints = requires?.credentials ?? [];
|
|
118
132
|
const requiresCredential = setupLooksCredentialed(setupHint ?? "");
|
|
119
|
-
if (skillType === "connector" ||
|
|
133
|
+
if (skillType === "connector" ||
|
|
134
|
+
credentialHints.length > 0 ||
|
|
135
|
+
requiresCredential) {
|
|
120
136
|
const aliases = new Set();
|
|
121
137
|
for (const entry of [providerId, ...credentialHints]) {
|
|
122
138
|
for (const alias of resolveCredentialAliases(entry))
|
|
@@ -124,7 +140,12 @@ function resolveSkillProviderStatus(params) {
|
|
|
124
140
|
}
|
|
125
141
|
const hasCred = Array.from(aliases).some((alias) => credentialServices.has(alias));
|
|
126
142
|
if (!hasCred) {
|
|
127
|
-
return {
|
|
143
|
+
return {
|
|
144
|
+
id: providerId,
|
|
145
|
+
kind: "skill",
|
|
146
|
+
status: "needs_setup",
|
|
147
|
+
reason: "missing_credentials",
|
|
148
|
+
};
|
|
128
149
|
}
|
|
129
150
|
}
|
|
130
151
|
if (detectSkillUsageActive(skillName)) {
|
|
@@ -162,7 +183,9 @@ export function detectCapabilities(params) {
|
|
|
162
183
|
const metadata = getSkillMetadata(entry);
|
|
163
184
|
const key = normalizeProviderId(metadata?.skillKey ?? entry.skill.name);
|
|
164
185
|
skillMap.set(key, { name: entry.skill.name, metadata });
|
|
165
|
-
const provides = (metadata?.provides ?? [])
|
|
186
|
+
const provides = (metadata?.provides ?? [])
|
|
187
|
+
.map((cap) => cap.trim())
|
|
188
|
+
.filter(Boolean);
|
|
166
189
|
for (const capId of provides) {
|
|
167
190
|
const normalizedCap = capId.trim();
|
|
168
191
|
if (!normalizedCap)
|
|
@@ -176,7 +199,10 @@ export function detectCapabilities(params) {
|
|
|
176
199
|
const capabilities = [];
|
|
177
200
|
for (const capability of registry.all) {
|
|
178
201
|
const extraProviders = providesMap.get(capability.id) ?? [];
|
|
179
|
-
const providerList = Array.from(new Set([
|
|
202
|
+
const providerList = Array.from(new Set([
|
|
203
|
+
...capability.providers.map(normalizeProviderId),
|
|
204
|
+
...extraProviders,
|
|
205
|
+
]));
|
|
180
206
|
const providerStatuses = [];
|
|
181
207
|
for (const provider of providerList) {
|
|
182
208
|
const normalized = normalizeProviderId(provider);
|
|
@@ -195,7 +221,11 @@ export function detectCapabilities(params) {
|
|
|
195
221
|
providerStatuses.push(resolveCredentialProviderStatus(normalized, credentialServices));
|
|
196
222
|
}
|
|
197
223
|
else {
|
|
198
|
-
providerStatuses.push({
|
|
224
|
+
providerStatuses.push({
|
|
225
|
+
id: normalized,
|
|
226
|
+
kind: "unknown",
|
|
227
|
+
status: "unavailable",
|
|
228
|
+
});
|
|
199
229
|
}
|
|
200
230
|
}
|
|
201
231
|
const status = pickBestStatus(providerStatuses.map((p) => p.status));
|
|
@@ -206,7 +236,8 @@ export function detectCapabilities(params) {
|
|
|
206
236
|
active: capabilities.filter((c) => c.status === "active").length,
|
|
207
237
|
ready: capabilities.filter((c) => c.status === "ready").length,
|
|
208
238
|
needs_setup: capabilities.filter((c) => c.status === "needs_setup").length,
|
|
209
|
-
needs_install: capabilities.filter((c) => c.status === "needs_install")
|
|
239
|
+
needs_install: capabilities.filter((c) => c.status === "needs_install")
|
|
240
|
+
.length,
|
|
210
241
|
unavailable: capabilities.filter((c) => c.status === "unavailable").length,
|
|
211
242
|
broken: capabilities.filter((c) => c.status === "broken").length,
|
|
212
243
|
};
|
|
@@ -70,7 +70,8 @@ export function loadCapabilityRegistry() {
|
|
|
70
70
|
for (const line of lines) {
|
|
71
71
|
const headingMatch = line.match(/^###\s+(.+)$/);
|
|
72
72
|
if (headingMatch) {
|
|
73
|
-
currentCategory =
|
|
73
|
+
currentCategory =
|
|
74
|
+
headingMatch[1]?.replace(/^[-–]\s*/, "").trim() || currentCategory;
|
|
74
75
|
continue;
|
|
75
76
|
}
|
|
76
77
|
const tableMatch = line.match(/^\|\s*`([^`]+)`\s*\|\s*([^|]+)\|\s*([^|]+)\|\s*([^|]+)\|/);
|
package/dist/cli/cloud-cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import crypto from "node:crypto";
|
|
1
3
|
import fs from "node:fs";
|
|
2
4
|
import path from "node:path";
|
|
3
|
-
import crypto from "node:crypto";
|
|
4
|
-
import { spawn } from "node:child_process";
|
|
5
5
|
import { scanCredentials } from "../commands/credential.js";
|
|
6
6
|
import { openUrl } from "../commands/onboard-helpers.js";
|
|
7
|
-
import { storeKeychainSecret, writeCredentialRecord, } from "../credentials/store.js";
|
|
7
|
+
import { resolveDefaultEnvVar, storeKeychainSecret, writeCredentialRecord, } from "../credentials/store.js";
|
|
8
8
|
import { NEXUS_ROOT, sleep } from "../utils.js";
|
|
9
9
|
const DEFAULT_CLOUD_TIMEOUT_MS = 120_000;
|
|
10
10
|
const DEFAULT_CLOUD_POLL_MS = 2_000;
|
|
@@ -77,6 +77,7 @@ async function storeHubToken(params) {
|
|
|
77
77
|
value: params.token,
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
|
+
let envVar;
|
|
80
81
|
if (storedInKeychain) {
|
|
81
82
|
record = {
|
|
82
83
|
owner: "user",
|
|
@@ -86,17 +87,18 @@ async function storeHubToken(params) {
|
|
|
86
87
|
};
|
|
87
88
|
}
|
|
88
89
|
else {
|
|
90
|
+
envVar = resolveDefaultEnvVar({ service, type: "token" });
|
|
91
|
+
process.env[envVar] = params.token;
|
|
89
92
|
record = {
|
|
90
93
|
owner: "user",
|
|
91
94
|
type: "token",
|
|
92
95
|
configuredAt: now,
|
|
93
|
-
storage: { provider: "
|
|
94
|
-
token: params.token,
|
|
96
|
+
storage: { provider: "env", var: envVar },
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
99
|
await writeCredentialRecord(service, account, authId, record);
|
|
98
100
|
await scanCredentials();
|
|
99
|
-
return { storedInKeychain, account };
|
|
101
|
+
return { storedInKeychain, account, envVar };
|
|
100
102
|
}
|
|
101
103
|
function parseCloudLoginArgs(args) {
|
|
102
104
|
let baseUrl = getHubBaseUrl();
|
|
@@ -296,7 +298,10 @@ async function handleCloudLogin(rawArgs) {
|
|
|
296
298
|
}
|
|
297
299
|
console.log(stored.storedInKeychain
|
|
298
300
|
? " Token stored in keychain"
|
|
299
|
-
:
|
|
301
|
+
: ` Token stored in env var ${stored.envVar ?? ""}`.trim());
|
|
302
|
+
if (!stored.storedInKeychain && stored.envVar) {
|
|
303
|
+
console.log(` Persist it in your shell: export ${stored.envVar}=...`);
|
|
304
|
+
}
|
|
300
305
|
if (cloudUrl) {
|
|
301
306
|
console.log(` Cloud URL: ${cloudUrl}`);
|
|
302
307
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cancel, confirm, isCancel } from "@clack/prompts";
|
|
2
|
-
import { addCredential, flagCredential, getCredential, getCredentialPaths, listCredentials, removeCredential, scanCredentialEnv, } from "../commands/credential.js";
|
|
2
|
+
import { addCredential, flagCredential, getCredential, getCredentialPaths, getCredentialValue, importCliCredential, listCredentials, removeCredential, scanCredentialEnv, verifyCredentials, } from "../commands/credential.js";
|
|
3
3
|
function parseFields(raw, fieldsArgs) {
|
|
4
4
|
const out = {};
|
|
5
5
|
if (raw) {
|
|
@@ -25,7 +25,9 @@ function parseFields(raw, fieldsArgs) {
|
|
|
25
25
|
return out;
|
|
26
26
|
}
|
|
27
27
|
export function registerCredentialCli(program) {
|
|
28
|
-
const credential = program
|
|
28
|
+
const credential = program
|
|
29
|
+
.command("credential")
|
|
30
|
+
.description("Manage credentials");
|
|
29
31
|
credential
|
|
30
32
|
.command("list")
|
|
31
33
|
.description("List credentials from the index")
|
|
@@ -54,29 +56,72 @@ export function registerCredentialCli(program) {
|
|
|
54
56
|
const paths = getCredentialPaths();
|
|
55
57
|
console.log(`\nIndex: ${paths.indexPath}`);
|
|
56
58
|
});
|
|
59
|
+
credential
|
|
60
|
+
.command("verify <service>")
|
|
61
|
+
.description("Verify credential status for a service")
|
|
62
|
+
.option("--json", "Output as JSON")
|
|
63
|
+
.action(async (service, opts) => {
|
|
64
|
+
const result = await verifyCredentials({ service });
|
|
65
|
+
if (!result) {
|
|
66
|
+
console.error("Credential service not found.");
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
if (opts.json) {
|
|
70
|
+
console.log(JSON.stringify(result, null, 2));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const iconFor = (status) => status === "ok" ? "✅" : status === "skipped" ? "⚠️" : "❌";
|
|
74
|
+
console.log(`${result.ok ? "✅" : "❌"} ${result.service}: ${result.accounts} account(s) checked`);
|
|
75
|
+
for (const entry of result.checked) {
|
|
76
|
+
const err = entry.error ? ` - ${entry.error}` : "";
|
|
77
|
+
console.log(` ${iconFor(entry.status)} ${entry.account} (${entry.authId})${err}`);
|
|
78
|
+
}
|
|
79
|
+
if (!result.ok) {
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
57
83
|
credential
|
|
58
84
|
.command("get")
|
|
59
|
-
.description("Get a credential
|
|
85
|
+
.description("Get a credential value")
|
|
60
86
|
.requiredOption("--service <id>", "Service id")
|
|
61
87
|
.requiredOption("--account <id>", "Account id")
|
|
62
88
|
.requiredOption("--auth <id>", "Auth id")
|
|
63
89
|
.option("--json", "Output as JSON")
|
|
90
|
+
.option("--record", "Show credential record instead of value")
|
|
64
91
|
.action(async (opts) => {
|
|
65
|
-
const
|
|
92
|
+
const params = {
|
|
66
93
|
service: opts.service,
|
|
67
94
|
account: opts.account,
|
|
68
95
|
authId: opts.auth,
|
|
69
|
-
}
|
|
70
|
-
if (
|
|
71
|
-
|
|
96
|
+
};
|
|
97
|
+
if (opts.record) {
|
|
98
|
+
const result = await getCredential(params);
|
|
99
|
+
if (!result) {
|
|
100
|
+
console.error("Credential not found.");
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
if (opts.json) {
|
|
104
|
+
console.log(JSON.stringify(result, null, 2));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
console.log(`\n${result.filePath}`);
|
|
108
|
+
console.log(JSON.stringify(result.record, null, 2));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const valueResult = await getCredentialValue(params);
|
|
112
|
+
if (!valueResult) {
|
|
113
|
+
console.error("Credential value not found.");
|
|
72
114
|
process.exit(1);
|
|
73
115
|
}
|
|
74
116
|
if (opts.json) {
|
|
75
|
-
console.log(JSON.stringify(
|
|
117
|
+
console.log(JSON.stringify({
|
|
118
|
+
filePath: valueResult.filePath,
|
|
119
|
+
field: valueResult.field,
|
|
120
|
+
value: valueResult.value,
|
|
121
|
+
}, null, 2));
|
|
76
122
|
return;
|
|
77
123
|
}
|
|
78
|
-
console.log(
|
|
79
|
-
console.log(JSON.stringify(result.record, null, 2));
|
|
124
|
+
console.log(valueResult.value);
|
|
80
125
|
});
|
|
81
126
|
credential
|
|
82
127
|
.command("add")
|
|
@@ -86,23 +131,55 @@ export function registerCredentialCli(program) {
|
|
|
86
131
|
.requiredOption("--type <type>", "api_key | token | oauth | config")
|
|
87
132
|
.option("--auth <id>", "Auth id (defaults to type)")
|
|
88
133
|
.option("--owner <owner>", "shared | user | agent:<id>", "user")
|
|
89
|
-
.option("--storage <provider>", "
|
|
90
|
-
.option("--value <value>", "Secret value for
|
|
134
|
+
.option("--storage <provider>", "keychain | 1password | env | external")
|
|
135
|
+
.option("--value <value>", "Secret value for keychain")
|
|
91
136
|
.option("--refresh-token <token>", "OAuth refresh token")
|
|
92
137
|
.option("--expires-at <ts>", "Expiration (unix ms or ISO string)")
|
|
93
138
|
.option("--vault <name>", "1Password vault")
|
|
94
139
|
.option("--item <name>", "1Password item")
|
|
95
140
|
.option("--fields <json>", "1Password field map as JSON")
|
|
96
141
|
.option("--field <pair...>", "1Password field map: key=value")
|
|
97
|
-
.option("--
|
|
142
|
+
.option("--env-var <name>", "Env var name for env storage")
|
|
143
|
+
.option("--command <cmd>", "External command to fetch secret")
|
|
144
|
+
.option("--sync-command <cmd>", "External sync command (deprecated)")
|
|
145
|
+
.option("--format <format>", "External command output format: raw|json")
|
|
146
|
+
.option("--json-path <path>", "JSON path for external output")
|
|
98
147
|
.action(async (opts) => {
|
|
99
148
|
const type = String(opts.type).trim();
|
|
100
149
|
if (!["api_key", "token", "oauth", "config"].includes(type)) {
|
|
101
150
|
console.error("Invalid --type");
|
|
102
151
|
process.exit(1);
|
|
103
152
|
}
|
|
104
|
-
const
|
|
153
|
+
const storageRaw = opts.storage ? String(opts.storage).trim() : "";
|
|
154
|
+
const storage = storageRaw || (process.platform === "darwin" ? "keychain" : "");
|
|
155
|
+
if (!storage) {
|
|
156
|
+
console.error("Missing --storage. Use keychain (macOS), 1password, env, or external.");
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
if (storage === "keychain" && !opts.value) {
|
|
160
|
+
console.error("Missing --value for keychain storage.");
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
if (storage === "env" && !opts.envVar) {
|
|
164
|
+
console.error("Missing --env-var for env storage.");
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
if (storage === "external" && !opts.command && !opts.syncCommand) {
|
|
168
|
+
console.error("Missing --command for external storage.");
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
if (opts.format && !["raw", "json"].includes(String(opts.format))) {
|
|
172
|
+
console.error("Invalid --format. Use raw or json.");
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
if (!["keychain", "1password", "env", "external"].includes(storage)) {
|
|
176
|
+
console.error("Invalid --storage. Use keychain, 1password, env, external.");
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
105
179
|
const fields = parseFields(opts.fields, opts.field);
|
|
180
|
+
const format = opts.format === "raw" || opts.format === "json"
|
|
181
|
+
? opts.format
|
|
182
|
+
: undefined;
|
|
106
183
|
const expiresAt = opts.expiresAt && /^\d+$/.test(String(opts.expiresAt).trim())
|
|
107
184
|
? Number.parseInt(String(opts.expiresAt).trim(), 10)
|
|
108
185
|
: opts.expiresAt;
|
|
@@ -121,14 +198,59 @@ export function registerCredentialCli(program) {
|
|
|
121
198
|
item: String(opts.item ?? ""),
|
|
122
199
|
fields,
|
|
123
200
|
}
|
|
124
|
-
: storage === "
|
|
125
|
-
? { provider: "
|
|
126
|
-
:
|
|
201
|
+
: storage === "env"
|
|
202
|
+
? { provider: "env", var: String(opts.envVar ?? "") }
|
|
203
|
+
: storage === "external"
|
|
204
|
+
? {
|
|
205
|
+
provider: "external",
|
|
206
|
+
command: opts.command ? String(opts.command) : undefined,
|
|
207
|
+
syncCommand: opts.syncCommand
|
|
208
|
+
? String(opts.syncCommand)
|
|
209
|
+
: undefined,
|
|
210
|
+
...(format ? { format } : {}),
|
|
211
|
+
jsonPath: opts.jsonPath
|
|
212
|
+
? String(opts.jsonPath)
|
|
213
|
+
: undefined,
|
|
214
|
+
}
|
|
215
|
+
: { provider: "env", var: String(opts.envVar ?? "") },
|
|
127
216
|
refreshToken: opts.refreshToken,
|
|
128
217
|
expiresAt,
|
|
129
218
|
});
|
|
130
219
|
console.log(`Added credential at ${record.filePath}`);
|
|
131
220
|
});
|
|
221
|
+
credential
|
|
222
|
+
.command("import <source>")
|
|
223
|
+
.description("Import external CLI credentials")
|
|
224
|
+
.option("--account <id>", "Account id override")
|
|
225
|
+
.option("--owner <owner>", "shared | user | agent:<id>", "user")
|
|
226
|
+
.option("--force", "Overwrite existing record")
|
|
227
|
+
.option("--json", "Output as JSON")
|
|
228
|
+
.option("--no-keychain-prompt", "Skip keychain prompts")
|
|
229
|
+
.action(async (source, opts) => {
|
|
230
|
+
const normalized = String(source ?? "").trim();
|
|
231
|
+
if (!["claude-cli", "codex-cli"].includes(normalized)) {
|
|
232
|
+
console.error("Invalid source. Use claude-cli or codex-cli.");
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
const result = await importCliCredential({
|
|
237
|
+
source: normalized,
|
|
238
|
+
account: opts.account,
|
|
239
|
+
owner: opts.owner,
|
|
240
|
+
force: Boolean(opts.force),
|
|
241
|
+
allowKeychainPrompt: opts.keychainPrompt !== false,
|
|
242
|
+
});
|
|
243
|
+
if (opts.json) {
|
|
244
|
+
console.log(JSON.stringify(result, null, 2));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
console.log(`Imported ${result.source} credentials into ${result.profileId} (${result.filePath})`);
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
132
254
|
credential
|
|
133
255
|
.command("remove")
|
|
134
256
|
.description("Remove a credential record")
|
package/dist/cli/gateway-cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import { CONFIG_PATH_NEXUS, loadConfig, resolveGatewayPort } from "../config/config.js";
|
|
2
|
+
import { CONFIG_PATH_NEXUS, loadConfig, resolveGatewayPort, } from "../config/config.js";
|
|
3
3
|
import { GATEWAY_LAUNCH_AGENT_LABEL, GATEWAY_SYSTEMD_SERVICE_NAME, GATEWAY_WINDOWS_TASK_NAME, } from "../daemon/constants.js";
|
|
4
4
|
import { resolveGatewayService } from "../daemon/service.js";
|
|
5
5
|
import { callGateway, randomIdempotencyKey } from "../gateway/call.js";
|