@intent-systems/nexus 2026.1.5-3 → 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 +245 -0
- package/dist/capabilities/registry.js +99 -0
- package/dist/channels/location.js +44 -0
- package/dist/channels/web/index.js +2 -0
- 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 +1022 -0
- package/dist/control-plane/compaction.js +282 -0
- package/dist/control-plane/factory.js +31 -0
- package/dist/control-plane/index.js +10 -0
- package/dist/control-plane/odu/agents.js +192 -0
- package/dist/control-plane/odu/interaction-tools.js +208 -0
- package/dist/control-plane/odu/prompt-loader.js +95 -0
- package/dist/control-plane/odu/runtime.js +479 -0
- package/dist/control-plane/odu/types.js +6 -0
- package/dist/control-plane/odu-control-plane.js +316 -0
- package/dist/control-plane/single-agent.js +249 -0
- package/dist/control-plane/types.js +11 -0
- package/dist/credentials/store.js +449 -0
- 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/logging/redact.js +109 -0
- package/dist/markdown/fences.js +58 -0
- package/dist/media/image-ops.js +3 -1
- package/dist/memory/embeddings.js +146 -0
- package/dist/memory/index.js +3 -0
- package/dist/memory/internal.js +163 -0
- package/dist/pairing/pairing-store.js +218 -0
- package/dist/plugins/cli.js +42 -0
- package/dist/plugins/discovery.js +253 -0
- package/dist/plugins/install.js +181 -0
- package/dist/plugins/loader.js +290 -0
- package/dist/plugins/registry.js +105 -0
- package/dist/plugins/status.js +29 -0
- package/dist/plugins/tools.js +39 -0
- package/dist/plugins/types.js +1 -0
- package/dist/providers/github-copilot-auth.js +1 -1
- package/dist/routing/resolve-route.js +144 -0
- package/dist/routing/session-key.js +65 -0
- 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/provider-utils.js +28 -0
- package/dist/utils.js +4 -3
- package/dist/wizard/onboarding.js +29 -7
- package/package.json +4 -29
- package/patches/@mariozechner__pi-ai.patch +215 -0
- package/patches/playwright-core@1.57.0.patch +13 -0
- package/patches/qrcode-terminal.patch +12 -0
- package/scripts/postinstall.js +202 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import fsp from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { flattenManifest, generateSkillManifest, readSkillManifest, } from "./skills-manifest.js";
|
|
5
4
|
import { resolveStateDir } from "../config/paths.js";
|
|
6
|
-
import { MANAGED_SKILLS_DIR, NEXUS_ROOT, resolveUserPath } from "../utils.js";
|
|
7
5
|
import { defaultRuntime } from "../runtime.js";
|
|
6
|
+
import { MANAGED_SKILLS_DIR, NEXUS_ROOT, resolveUserPath } from "../utils.js";
|
|
7
|
+
import { flattenManifest, generateSkillManifest, readSkillManifest, } from "./skills-manifest.js";
|
|
8
8
|
/**
|
|
9
9
|
* Generate and write `.cursor/rules` for a given workspace.
|
|
10
10
|
*
|
|
@@ -35,28 +35,46 @@ function categorizeSkills(skills) {
|
|
|
35
35
|
"Media & Content": [],
|
|
36
36
|
"Smart Home & Devices": [],
|
|
37
37
|
"Development & Tools": [],
|
|
38
|
-
|
|
38
|
+
Other: [],
|
|
39
39
|
};
|
|
40
40
|
const categoryPatterns = [
|
|
41
|
-
[
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
[
|
|
46
|
-
|
|
41
|
+
[
|
|
42
|
+
"Communication & Messaging",
|
|
43
|
+
[/imsg|imessage|whatsapp|discord|slack|telegram|sms|message|chat/i],
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
"Productivity & Notes",
|
|
47
|
+
[/note|reminder|bear|obsidian|notion|things|trello|task|todo/i],
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
"Google & Cloud Services",
|
|
51
|
+
[/google|gmail|calendar|drive|sheets|docs|gemini|gog|places/i],
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"Media & Content",
|
|
55
|
+
[/image|video|audio|camera|gif|pdf|whisper|speech|photo|screenshot/i],
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
"Smart Home & Devices",
|
|
59
|
+
[/hue|sonos|spotify|eight|sleep|smart|home|light|speaker/i],
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
"Development & Tools",
|
|
63
|
+
[/github|git|code|coding|terminal|bash|cli|api|mcp|1password|search/i],
|
|
64
|
+
],
|
|
47
65
|
];
|
|
48
66
|
for (const skill of skills) {
|
|
49
67
|
const text = `${skill.name} ${skill.description || ""}`;
|
|
50
68
|
let matched = false;
|
|
51
69
|
for (const [category, patterns] of categoryPatterns) {
|
|
52
|
-
if (patterns.some(p => p.test(text))) {
|
|
70
|
+
if (patterns.some((p) => p.test(text))) {
|
|
53
71
|
categories[category].push(skill);
|
|
54
72
|
matched = true;
|
|
55
73
|
break;
|
|
56
74
|
}
|
|
57
75
|
}
|
|
58
76
|
if (!matched) {
|
|
59
|
-
categories
|
|
77
|
+
categories.Other.push(skill);
|
|
60
78
|
}
|
|
61
79
|
}
|
|
62
80
|
return categories;
|
|
@@ -73,7 +91,8 @@ function formatSkillsSection(skills) {
|
|
|
73
91
|
lines.push(`### ${category}`);
|
|
74
92
|
for (const skill of categorySkills.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
75
93
|
const desc = skill.description
|
|
76
|
-
? skill.description.slice(0, 80) +
|
|
94
|
+
? skill.description.slice(0, 80) +
|
|
95
|
+
(skill.description.length > 80 ? "..." : "")
|
|
77
96
|
: "(no description)";
|
|
78
97
|
lines.push(`- **${skill.name}**: ${desc}`);
|
|
79
98
|
}
|
|
@@ -105,7 +124,7 @@ async function loadAgentsMd(workspaceDir) {
|
|
|
105
124
|
/**
|
|
106
125
|
* Load bootstrap files (IDENTITY.md, PROFILE.md, SOUL.md) if they exist
|
|
107
126
|
*/
|
|
108
|
-
async function loadBootstrapFiles(
|
|
127
|
+
async function loadBootstrapFiles(_workspaceDir) {
|
|
109
128
|
const files = {};
|
|
110
129
|
const agentId = process.env.NEXUS_AGENT_ID?.trim() || "default";
|
|
111
130
|
const stateDir = resolveStateDir();
|
|
@@ -137,6 +156,7 @@ export async function generateCursorRules(options = {}) {
|
|
|
137
156
|
const workspaceDir = options.workspaceDir || process.cwd();
|
|
138
157
|
const skillsDir = options.skillsDir || MANAGED_SKILLS_DIR;
|
|
139
158
|
const userSkillsDir = options.userSkillsDir || path.join(NEXUS_ROOT, "home", "skills");
|
|
159
|
+
const managedSkillsDir = path.join(skillsDir, "managed");
|
|
140
160
|
// Try to read existing manifest, or generate one
|
|
141
161
|
let manifest = await readSkillManifest();
|
|
142
162
|
if (!manifest) {
|
|
@@ -181,14 +201,14 @@ nexus cursor-rules --output .cursor/rules
|
|
|
181
201
|
|
|
182
202
|
## Available Skills (${skills.length} total)
|
|
183
203
|
|
|
184
|
-
The following skills are available in
|
|
204
|
+
The following skills are available in \`${skillsDir}\`. Read the full \`SKILL.md\` when you need detailed usage instructions.
|
|
185
205
|
|
|
186
206
|
${skillsSection}
|
|
187
207
|
## How to Use Skills
|
|
188
208
|
|
|
189
209
|
1. **When a user's request relates to a skill**, read the corresponding SKILL.md:
|
|
190
210
|
\`\`\`bash
|
|
191
|
-
cat
|
|
211
|
+
cat ${path.join(skillsDir, "<skill>", "SKILL.md")}
|
|
192
212
|
\`\`\`
|
|
193
213
|
|
|
194
214
|
2. **Skills provide CLI tools** - use bash to run them:
|
|
@@ -207,13 +227,13 @@ ${skillsSection}
|
|
|
207
227
|
${agentsMdSection}${bootstrapSection}
|
|
208
228
|
## Skill Directory Structure
|
|
209
229
|
|
|
210
|
-
- **Bundled**:
|
|
211
|
-
- **Managed**:
|
|
212
|
-
- **User**:
|
|
230
|
+
- **Bundled**: \`${skillsDir}\` (${manifest.bundled.count} skills)
|
|
231
|
+
- **Managed**: \`${managedSkillsDir}\` (${manifest.managed.count} skills)
|
|
232
|
+
- **User**: \`${userSkillsDir}\` (${manifest.user.count} skills)
|
|
213
233
|
|
|
214
234
|
Precedence: User > Managed > Bundled
|
|
215
235
|
`;
|
|
216
|
-
return rules.trim()
|
|
236
|
+
return `${rules.trim()}\n`;
|
|
217
237
|
}
|
|
218
238
|
/**
|
|
219
239
|
* Write cursor rules to the specified output path
|
package/dist/commands/doctor.js
CHANGED
|
@@ -24,7 +24,7 @@ import { ensureSystemdUserLingerInteractive } from "./systemd-linger.js";
|
|
|
24
24
|
function resolveMode(cfg) {
|
|
25
25
|
return cfg.gateway?.mode === "remote" ? "remote" : "local";
|
|
26
26
|
}
|
|
27
|
-
function resolveLegacyConfigPath(
|
|
27
|
+
function resolveLegacyConfigPath(_env) {
|
|
28
28
|
return path.join(os.homedir(), ".nexus", "nexus.json");
|
|
29
29
|
}
|
|
30
30
|
async function noteSecurityWarnings(cfg) {
|
|
@@ -438,8 +438,7 @@ async function maybeMigrateLegacyConfigFile(runtime) {
|
|
|
438
438
|
const gatewayBind = typeof legacySnapshot.parsed?.gateway?.bind === "string"
|
|
439
439
|
? legacySnapshot.parsed.gateway?.bind
|
|
440
440
|
: undefined;
|
|
441
|
-
const agentWorkspace = typeof legacySnapshot.parsed?.agent?.workspace ===
|
|
442
|
-
"string"
|
|
441
|
+
const agentWorkspace = typeof legacySnapshot.parsed?.agent?.workspace === "string"
|
|
443
442
|
? legacySnapshot.parsed.agent?.workspace
|
|
444
443
|
: undefined;
|
|
445
444
|
note([
|
|
@@ -471,7 +470,9 @@ async function maybeMigrateLegacyGatewayService(cfg, runtime) {
|
|
|
471
470
|
const legacyServices = await findLegacyGatewayServices(process.env);
|
|
472
471
|
if (legacyServices.length === 0)
|
|
473
472
|
return;
|
|
474
|
-
note(legacyServices
|
|
473
|
+
note(legacyServices
|
|
474
|
+
.map((svc) => `- ${svc.label} (${svc.platform}, ${svc.detail})`)
|
|
475
|
+
.join("\n"), "Legacy Nexus services detected");
|
|
475
476
|
const migrate = guardCancel(await confirm({
|
|
476
477
|
message: "Migrate legacy Nexus services to Nexus now?",
|
|
477
478
|
initialValue: true,
|
|
@@ -1,40 +1,37 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { resolveStateDir } from "../config/paths.js";
|
|
1
|
+
import { resolveIdentitySnapshot } from "../agents/identity-state.js";
|
|
4
2
|
import { defaultRuntime } from "../runtime.js";
|
|
5
|
-
function readField(pathname, label) {
|
|
6
|
-
try {
|
|
7
|
-
const raw = fs.readFileSync(pathname, "utf-8");
|
|
8
|
-
const regex = new RegExp(`^[-*]?\\s*${label}\\s*:\\s*(.+)$`, "im");
|
|
9
|
-
const match = raw.match(regex);
|
|
10
|
-
return match?.[1]?.trim();
|
|
11
|
-
}
|
|
12
|
-
catch {
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
3
|
export async function identityCommand(opts, runtime = defaultRuntime) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
4
|
+
const resolution = resolveIdentitySnapshot();
|
|
5
|
+
if (!resolution.ok) {
|
|
6
|
+
const payload = {
|
|
7
|
+
ok: false,
|
|
8
|
+
error: "multiple_agents",
|
|
9
|
+
agents: resolution.agentOptions,
|
|
10
|
+
};
|
|
11
|
+
if (opts.json) {
|
|
12
|
+
runtime.log(JSON.stringify(payload, null, 2));
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
runtime.error("Multiple agents detected in state/agents.");
|
|
16
|
+
runtime.error(`Set NEXUS_AGENT_ID to one of: ${payload.agents.join(", ")}`);
|
|
17
|
+
}
|
|
18
|
+
runtime.exit(2);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const identity = resolution.snapshot;
|
|
25
22
|
const payload = {
|
|
26
23
|
agent: {
|
|
27
|
-
id: agentId,
|
|
28
|
-
name:
|
|
29
|
-
identityPath: agentIdentityPath,
|
|
30
|
-
soulPath: agentSoulPath,
|
|
31
|
-
memoryPath: agentMemoryPath,
|
|
32
|
-
exists:
|
|
24
|
+
id: identity.agentId,
|
|
25
|
+
name: identity.agentName,
|
|
26
|
+
identityPath: identity.agentIdentityPath,
|
|
27
|
+
soulPath: identity.agentSoulPath,
|
|
28
|
+
memoryPath: identity.agentMemoryPath,
|
|
29
|
+
exists: identity.agentIdentityExists,
|
|
33
30
|
},
|
|
34
31
|
user: {
|
|
35
|
-
name:
|
|
36
|
-
profilePath: userProfilePath,
|
|
37
|
-
exists:
|
|
32
|
+
name: identity.userName,
|
|
33
|
+
profilePath: identity.userProfilePath,
|
|
34
|
+
exists: identity.userProfileExists,
|
|
38
35
|
},
|
|
39
36
|
};
|
|
40
37
|
if (opts.json) {
|
package/dist/commands/init.js
CHANGED
|
@@ -6,9 +6,10 @@ import { ensureAgentWorkspace } from "../agents/workspace.js";
|
|
|
6
6
|
import { CONFIG_PATH_NEXUS, writeConfigFile } from "../config/config.js";
|
|
7
7
|
import { resolveStateDir } from "../config/paths.js";
|
|
8
8
|
import { defaultRuntime } from "../runtime.js";
|
|
9
|
-
import { NEXUS_ROOT } from "../utils.js";
|
|
10
|
-
import {
|
|
9
|
+
import { MANAGED_SKILLS_DIR, NEXUS_ROOT } from "../utils.js";
|
|
10
|
+
import { scanCredentials } from "./credential.js";
|
|
11
11
|
import { writeCursorRules } from "./cursor-rules.js";
|
|
12
|
+
import { copyBundledSkills, generateSkillManifest, writeSkillManifest, } from "./skills-manifest.js";
|
|
12
13
|
const execFileAsync = promisify(execFile);
|
|
13
14
|
/**
|
|
14
15
|
* Checks if a directory is already a git repository.
|
|
@@ -84,30 +85,25 @@ build/
|
|
|
84
85
|
* - ~/nexus/state/ (state directory)
|
|
85
86
|
*/
|
|
86
87
|
export async function initCommand(opts, runtime = defaultRuntime) {
|
|
87
|
-
const workspaceDir = opts?.workspace?.trim() ||
|
|
88
|
+
const workspaceDir = opts?.workspace?.trim() ||
|
|
89
|
+
path.join(process.env.HOME || "~", "nexus", "home");
|
|
90
|
+
const createHomeLayout = false;
|
|
88
91
|
// 1. Create workspace directory and Nexus state scaffolding
|
|
89
92
|
const workspace = await ensureAgentWorkspace({
|
|
90
93
|
dir: workspaceDir,
|
|
91
94
|
ensureBootstrapFiles: true,
|
|
95
|
+
bootstrapMode: "minimal",
|
|
96
|
+
createHomeLayout,
|
|
92
97
|
});
|
|
93
98
|
runtime.log(`✓ Created workspace: ${workspace.dir}`);
|
|
94
99
|
if (workspace.agentsPath)
|
|
95
100
|
runtime.log(` - ${path.basename(workspace.agentsPath)}`);
|
|
96
|
-
if (workspace.toolsPath)
|
|
101
|
+
if (workspace.toolsPath && createHomeLayout)
|
|
97
102
|
runtime.log(` - ${path.basename(workspace.toolsPath)}`);
|
|
98
103
|
if (workspace.bootstrapPath)
|
|
99
104
|
runtime.log(` - ${path.basename(workspace.bootstrapPath)}`);
|
|
100
|
-
// 2.
|
|
105
|
+
// 2. Resolve user skills directory (may be created later by user)
|
|
101
106
|
const userSkillsDir = path.join(workspace.dir, "skills");
|
|
102
|
-
await fs.mkdir(userSkillsDir, { recursive: true });
|
|
103
|
-
runtime.log(`✓ Created user skills directory: ${userSkillsDir}`);
|
|
104
|
-
// 3. Create memory/ and projects/ directories
|
|
105
|
-
const memoryDir = path.join(workspace.dir, "memory");
|
|
106
|
-
await fs.mkdir(memoryDir, { recursive: true });
|
|
107
|
-
const projectsDir = path.join(workspace.dir, "projects");
|
|
108
|
-
await fs.mkdir(projectsDir, { recursive: true });
|
|
109
|
-
runtime.log(`✓ Created memory directory: ${memoryDir}`);
|
|
110
|
-
runtime.log(`✓ Created projects directory: ${projectsDir}`);
|
|
111
107
|
// 4. Create state directory (~/nexus/state)
|
|
112
108
|
const stateDir = resolveStateDir();
|
|
113
109
|
await fs.mkdir(stateDir, { recursive: true });
|
|
@@ -131,8 +127,11 @@ export async function initCommand(opts, runtime = defaultRuntime) {
|
|
|
131
127
|
};
|
|
132
128
|
await fs.writeFile(workspaceMetaPath, JSON.stringify(workspaceMeta, null, 2), "utf-8");
|
|
133
129
|
runtime.log(`✓ Created workspace metadata: ${workspaceMetaPath}`);
|
|
134
|
-
//
|
|
135
|
-
|
|
130
|
+
// 5b. Ensure credential index
|
|
131
|
+
await scanCredentials();
|
|
132
|
+
runtime.log(`✓ Created credential index: ${path.join(stateDir, "credentials", "index.json")}`);
|
|
133
|
+
// 6. Create main skills directory (state/skills) and copy bundled skills
|
|
134
|
+
const skillsDir = MANAGED_SKILLS_DIR;
|
|
136
135
|
await fs.mkdir(skillsDir, { recursive: true });
|
|
137
136
|
runtime.log(`✓ Created skills directory: ${skillsDir}`);
|
|
138
137
|
// Copy bundled skills from installed package to ~/nexus/skills/
|
|
@@ -160,8 +159,6 @@ export async function initCommand(opts, runtime = defaultRuntime) {
|
|
|
160
159
|
includeBootstrap: false,
|
|
161
160
|
});
|
|
162
161
|
runtime.log(`✓ Created Cursor rules: ${cursorRulesPath}`);
|
|
163
|
-
// 9. Initialize git repository for workspace
|
|
164
|
-
await initGitRepo(workspace.dir, runtime);
|
|
165
162
|
runtime.log("\nNexus initialization complete!");
|
|
166
163
|
runtime.log(`\nNext steps:\n - Run 'nexus status' to orient\n - Open ${NEXUS_ROOT} in your editor`);
|
|
167
164
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseDurationMs } from "../cli/parse-duration.js";
|
|
4
|
+
import { readSkillUsageEntries } from "../agents/skill-usage.js";
|
|
5
|
+
import { resolveEventLogDir } from "../infra/event-log.js";
|
|
6
|
+
import { defaultRuntime } from "../runtime.js";
|
|
7
|
+
function parseSince(raw) {
|
|
8
|
+
if (!raw)
|
|
9
|
+
return undefined;
|
|
10
|
+
const trimmed = raw.trim();
|
|
11
|
+
if (!trimmed)
|
|
12
|
+
return undefined;
|
|
13
|
+
const parsedDate = Date.parse(trimmed);
|
|
14
|
+
if (!Number.isNaN(parsedDate)) {
|
|
15
|
+
return parsedDate;
|
|
16
|
+
}
|
|
17
|
+
const ms = parseDurationMs(trimmed, { defaultUnit: "h" });
|
|
18
|
+
return Date.now() - ms;
|
|
19
|
+
}
|
|
20
|
+
function readEventFiles(dir) {
|
|
21
|
+
try {
|
|
22
|
+
return fs
|
|
23
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
24
|
+
.filter((entry) => entry.isFile() &&
|
|
25
|
+
entry.name.startsWith("events-") &&
|
|
26
|
+
entry.name.endsWith(".jsonl"))
|
|
27
|
+
.map((entry) => entry.name)
|
|
28
|
+
.sort();
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function parseEventLine(line) {
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(line);
|
|
37
|
+
if (!parsed || typeof parsed !== "object")
|
|
38
|
+
return null;
|
|
39
|
+
if (typeof parsed.ts !== "number")
|
|
40
|
+
return null;
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function loadEventLogEntries(opts) {
|
|
48
|
+
const dir = resolveEventLogDir();
|
|
49
|
+
const files = readEventFiles(dir);
|
|
50
|
+
const events = [];
|
|
51
|
+
for (const file of files) {
|
|
52
|
+
const filePath = path.join(dir, file);
|
|
53
|
+
let raw = "";
|
|
54
|
+
try {
|
|
55
|
+
raw = fs.readFileSync(filePath, "utf-8");
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
61
|
+
if (!line.trim())
|
|
62
|
+
continue;
|
|
63
|
+
const event = parseEventLine(line);
|
|
64
|
+
if (!event)
|
|
65
|
+
continue;
|
|
66
|
+
if (opts.sinceMs && event.ts < opts.sinceMs)
|
|
67
|
+
continue;
|
|
68
|
+
if (opts.source && event.source !== opts.source)
|
|
69
|
+
continue;
|
|
70
|
+
if (opts.command && event.command_path !== opts.command)
|
|
71
|
+
continue;
|
|
72
|
+
if (opts.errors) {
|
|
73
|
+
const isError = event.status === "error" ||
|
|
74
|
+
event.event_type === "command_failed" ||
|
|
75
|
+
event.event_type === "cli_session_end";
|
|
76
|
+
if (!isError)
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
events.push(event);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (opts.limit && opts.limit > 0 && events.length > opts.limit) {
|
|
83
|
+
return events.slice(-opts.limit);
|
|
84
|
+
}
|
|
85
|
+
return events;
|
|
86
|
+
}
|
|
87
|
+
export async function logCommand(opts, runtime = defaultRuntime) {
|
|
88
|
+
const sinceMs = parseSince(opts.since);
|
|
89
|
+
const limit = opts.limit && opts.limit > 0 ? opts.limit : undefined;
|
|
90
|
+
if (opts.skill) {
|
|
91
|
+
const entries = readSkillUsageEntries(opts.skill, {
|
|
92
|
+
sinceMs,
|
|
93
|
+
limit,
|
|
94
|
+
});
|
|
95
|
+
if (opts.json) {
|
|
96
|
+
runtime.log(JSON.stringify({ skill: opts.skill, entries }, null, 2));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (entries.length === 0) {
|
|
100
|
+
runtime.log("No skill usage found.");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
const time = new Date(entry.ts).toISOString();
|
|
105
|
+
const status = entry.ok ? "ok" : "error";
|
|
106
|
+
const duration = typeof entry.durationMs === "number"
|
|
107
|
+
? ` ${entry.durationMs}ms`
|
|
108
|
+
: "";
|
|
109
|
+
runtime.log(`${time} ${entry.event} ${status}${duration}`);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const events = loadEventLogEntries({
|
|
114
|
+
sinceMs,
|
|
115
|
+
errors: opts.errors,
|
|
116
|
+
source: opts.source,
|
|
117
|
+
command: opts.command,
|
|
118
|
+
limit,
|
|
119
|
+
});
|
|
120
|
+
if (opts.json) {
|
|
121
|
+
runtime.log(JSON.stringify({ events }, null, 2));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (events.length === 0) {
|
|
125
|
+
runtime.log("No events found.");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
for (const event of events) {
|
|
129
|
+
const time = new Date(event.ts).toISOString();
|
|
130
|
+
const status = event.status ?? "-";
|
|
131
|
+
const command = event.command_path ?? event.event_type ?? "-";
|
|
132
|
+
runtime.log(`${time} ${event.source}:${command} ${status}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildModelAliasIndex, resolveModelRefFromString } from "../../agents/model-selection.js";
|
|
1
|
+
import { buildModelAliasIndex, resolveModelRefFromString, } from "../../agents/model-selection.js";
|
|
2
2
|
import { CONFIG_PATH_NEXUS, loadConfig } from "../../config/config.js";
|
|
3
3
|
import { DEFAULT_PROVIDER, ensureFlagCompatibility, modelKey, resolveModelTarget, updateConfig, } from "./shared.js";
|
|
4
4
|
export async function modelsFallbacksListCommand(opts, runtime) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildModelAliasIndex, resolveModelRefFromString } from "../../agents/model-selection.js";
|
|
1
|
+
import { buildModelAliasIndex, resolveModelRefFromString, } from "../../agents/model-selection.js";
|
|
2
2
|
import { CONFIG_PATH_NEXUS, loadConfig } from "../../config/config.js";
|
|
3
3
|
import { DEFAULT_PROVIDER, ensureFlagCompatibility, modelKey, resolveModelTarget, updateConfig, } from "./shared.js";
|
|
4
4
|
export async function modelsImageFallbacksListCommand(opts, runtime) {
|
|
@@ -5,7 +5,7 @@ import { ensureAuthProfileStore, listProfilesForProvider, } from "../../agents/a
|
|
|
5
5
|
import { getCustomProviderApiKey, resolveEnvApiKey, } from "../../agents/model-auth.js";
|
|
6
6
|
import { buildModelAliasIndex, parseModelRef, resolveConfiguredModelRef, resolveModelRefFromString, } from "../../agents/model-selection.js";
|
|
7
7
|
import { ensureNexusModelsJson } from "../../agents/models-config.js";
|
|
8
|
-
import { CONFIG_PATH_NEXUS, loadConfig } from "../../config/config.js";
|
|
8
|
+
import { CONFIG_PATH_NEXUS, loadConfig, } from "../../config/config.js";
|
|
9
9
|
import { info } from "../../globals.js";
|
|
10
10
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER, ensureFlagCompatibility, formatTokenK, modelKey, } from "./shared.js";
|
|
11
11
|
const MODEL_PAD = 42;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cancel, isCancel, multiselect } from "@clack/prompts";
|
|
2
2
|
import { resolveApiKeyForProvider } from "../../agents/model-auth.js";
|
|
3
|
-
import { scanOpenRouterModels } from "../../agents/model-scan.js";
|
|
3
|
+
import { scanOpenRouterModels, } from "../../agents/model-scan.js";
|
|
4
4
|
import { CONFIG_PATH_NEXUS, loadConfig } from "../../config/config.js";
|
|
5
5
|
import { formatMs, formatTokenK, updateConfig } from "./shared.js";
|
|
6
6
|
const MODEL_PAD = 42;
|
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
import { upsertAuthProfile } from "../agents/auth-profiles.js";
|
|
2
2
|
export async function writeOAuthCredentials(provider, creds) {
|
|
3
|
+
const access = "accessToken" in creds && typeof creds.accessToken === "string"
|
|
4
|
+
? creds.accessToken
|
|
5
|
+
: typeof creds.access === "string"
|
|
6
|
+
? creds.access
|
|
7
|
+
: undefined;
|
|
8
|
+
const refresh = "refreshToken" in creds && typeof creds.refreshToken === "string"
|
|
9
|
+
? creds.refreshToken
|
|
10
|
+
: typeof creds.refresh === "string"
|
|
11
|
+
? creds.refresh
|
|
12
|
+
: undefined;
|
|
13
|
+
const rawExpires = "expiresAt" in creds
|
|
14
|
+
? creds.expiresAt
|
|
15
|
+
: creds.expires;
|
|
16
|
+
const parsedExpires = typeof rawExpires === "number"
|
|
17
|
+
? rawExpires
|
|
18
|
+
: typeof rawExpires === "string"
|
|
19
|
+
? Number.parseInt(rawExpires, 10)
|
|
20
|
+
: undefined;
|
|
21
|
+
const expires = typeof parsedExpires === "number" && Number.isFinite(parsedExpires)
|
|
22
|
+
? parsedExpires
|
|
23
|
+
: undefined;
|
|
24
|
+
const email = typeof creds.email === "string" ? creds.email : undefined;
|
|
3
25
|
upsertAuthProfile({
|
|
4
|
-
profileId: `${provider}:${
|
|
26
|
+
profileId: `${provider}:${email ?? "default"}`,
|
|
5
27
|
credential: {
|
|
6
28
|
type: "oauth",
|
|
7
29
|
provider,
|
|
8
|
-
|
|
30
|
+
access,
|
|
31
|
+
refresh,
|
|
32
|
+
expires,
|
|
33
|
+
email,
|
|
9
34
|
},
|
|
10
35
|
});
|
|
11
36
|
}
|
|
@@ -5,7 +5,10 @@ const execFileAsync = promisify(execFile);
|
|
|
5
5
|
function coerceStringArray(input) {
|
|
6
6
|
if (!Array.isArray(input))
|
|
7
7
|
return [];
|
|
8
|
-
return input
|
|
8
|
+
return input
|
|
9
|
+
.map((v) => String(v))
|
|
10
|
+
.map((s) => s.trim())
|
|
11
|
+
.filter(Boolean);
|
|
9
12
|
}
|
|
10
13
|
function parseEveWhoamiJson(stdout) {
|
|
11
14
|
const raw = stdout.trim();
|
|
@@ -78,18 +81,14 @@ export async function maybeSetupIdentityFromEve(params) {
|
|
|
78
81
|
await prompter.note(`Eve whoami failed: ${err instanceof Error ? err.message : String(err)}`, "Eve whoami");
|
|
79
82
|
return;
|
|
80
83
|
}
|
|
81
|
-
if (!whoami?.name &&
|
|
84
|
+
if (!whoami?.name && !whoami?.phones?.length && !whoami?.emails?.length) {
|
|
82
85
|
await prompter.note("Eve ran, but didn't return identity info in a readable format.", "Eve whoami");
|
|
83
86
|
return;
|
|
84
87
|
}
|
|
85
88
|
const summaryLines = [
|
|
86
89
|
whoami.name ? `Name: ${whoami.name}` : undefined,
|
|
87
|
-
whoami.phones
|
|
88
|
-
|
|
89
|
-
: undefined,
|
|
90
|
-
whoami.emails && whoami.emails.length
|
|
91
|
-
? `Emails: ${whoami.emails.join(", ")}`
|
|
92
|
-
: undefined,
|
|
90
|
+
whoami.phones?.length ? `Phones: ${whoami.phones.join(", ")}` : undefined,
|
|
91
|
+
whoami.emails?.length ? `Emails: ${whoami.emails.join(", ")}` : undefined,
|
|
93
92
|
].filter(Boolean);
|
|
94
93
|
await prompter.note(summaryLines.join("\n"), "Detected identity (Eve)");
|
|
95
94
|
// NOTE: We don't directly edit IDENTITY.md / PROFILE.md here yet; the bootstrap ritual
|
|
@@ -5,9 +5,9 @@ import { resolveGatewayProgramArguments } from "../daemon/program-args.js";
|
|
|
5
5
|
import { resolveGatewayService } from "../daemon/service.js";
|
|
6
6
|
import { defaultRuntime } from "../runtime.js";
|
|
7
7
|
import { resolveUserPath, sleep } from "../utils.js";
|
|
8
|
+
import { applyBootstrapPreset } from "./bootstrap-preset.js";
|
|
8
9
|
import { healthCommand } from "./health.js";
|
|
9
10
|
import { applyAuthProfileConfig, applyMinimaxConfig, setAnthropicApiKey, } from "./onboard-auth.js";
|
|
10
|
-
import { applyBootstrapPreset } from "./bootstrap-preset.js";
|
|
11
11
|
import { applyWizardMetadata, DEFAULT_WORKSPACE, ensureWorkspaceAndSessions, randomToken, } from "./onboard-helpers.js";
|
|
12
12
|
import { ensureSystemdUserLingerNonInteractive } from "./systemd-linger.js";
|
|
13
13
|
export async function runNonInteractiveOnboarding(opts, runtime = defaultRuntime) {
|
|
@@ -180,7 +180,9 @@ export async function runNonInteractiveOnboarding(opts, runtime = defaultRuntime
|
|
|
180
180
|
skipBootstrap: Boolean(nextConfig.agent?.skipBootstrap),
|
|
181
181
|
});
|
|
182
182
|
// Scripted bootstrap (test harness / automation): fill identity files + delete BOOTSTRAP.md.
|
|
183
|
-
if (opts.bootstrapPreset ||
|
|
183
|
+
if (opts.bootstrapPreset ||
|
|
184
|
+
opts.bootstrapAgentName ||
|
|
185
|
+
opts.bootstrapUserName) {
|
|
184
186
|
await applyBootstrapPreset({
|
|
185
187
|
workspaceDir,
|
|
186
188
|
runtime,
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import net from "node:net";
|
|
2
|
+
import { CLAUDE_CLI_PROFILE_ID, CODEX_CLI_PROFILE_ID, ensureAuthProfileStore, } from "../agents/auth-profiles.js";
|
|
2
3
|
import { CONFIG_PATH_NEXUS, readConfigFileSnapshot, resolveGatewayPort, writeConfigFile, } from "../config/config.js";
|
|
3
|
-
import { resolveGatewayService } from "../daemon/service.js";
|
|
4
|
-
import { resolveGatewayProgramArguments } from "../daemon/program-args.js";
|
|
5
4
|
import { GATEWAY_LAUNCH_AGENT_LABEL } from "../daemon/constants.js";
|
|
5
|
+
import { resolveGatewayProgramArguments } from "../daemon/program-args.js";
|
|
6
|
+
import { resolveGatewayService } from "../daemon/service.js";
|
|
6
7
|
import { ensureControlUiAssetsBuilt } from "../infra/control-ui-assets.js";
|
|
7
8
|
import { defaultRuntime } from "../runtime.js";
|
|
8
9
|
import { resolveUserPath, sleep } from "../utils.js";
|
|
10
|
+
import { applyBootstrapPreset } from "./bootstrap-preset.js";
|
|
9
11
|
import { healthCommand } from "./health.js";
|
|
10
12
|
import { applyAuthProfileConfig, setAnthropicApiKey } from "./onboard-auth.js";
|
|
11
|
-
import { applyBootstrapPreset } from "./bootstrap-preset.js";
|
|
12
13
|
import { applyWizardMetadata, DEFAULT_WORKSPACE, detectBrowserOpenSupport, ensureWorkspaceAndSessions, openUrl, resolveControlUiLinks, } from "./onboard-helpers.js";
|
|
13
|
-
import { CLAUDE_CLI_PROFILE_ID, CODEX_CLI_PROFILE_ID, ensureAuthProfileStore, } from "../agents/auth-profiles.js";
|
|
14
|
-
import { ensureSystemdUserLingerNonInteractive } from "./systemd-linger.js";
|
|
15
14
|
import { applyOpenAICodexModelDefault } from "./openai-codex-model-default.js";
|
|
15
|
+
import { ensureSystemdUserLingerNonInteractive } from "./systemd-linger.js";
|
|
16
16
|
async function isPortFree(port) {
|
|
17
17
|
try {
|
|
18
18
|
await new Promise((resolve, reject) => {
|
|
@@ -51,7 +51,7 @@ async function pickGatewayPort(preferred) {
|
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
function applyQuickstartAuth(cfg, runtime) {
|
|
54
|
-
//
|
|
54
|
+
// Quickstart only considers credentials already imported into the store.
|
|
55
55
|
const store = ensureAuthProfileStore();
|
|
56
56
|
// 1) Prefer OpenAI Codex credentials if present (works without API keys).
|
|
57
57
|
if (store.profiles[CODEX_CLI_PROFILE_ID]) {
|
|
@@ -79,9 +79,9 @@ function applyQuickstartAuth(cfg, runtime) {
|
|
|
79
79
|
// valid for general Anthropic API requests. We avoid auto-selecting them here.
|
|
80
80
|
// Users can still opt in via `nexus onboard` interactive.
|
|
81
81
|
if (store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
|
82
|
-
runtime.log(
|
|
82
|
+
runtime.log("Detected Claude Code credentials, but Nexus may require an Anthropic API key (or another provider) for API access. Run `nexus onboard` to choose billing/auth.");
|
|
83
83
|
}
|
|
84
|
-
runtime.log("No Claude/Codex credentials detected yet.
|
|
84
|
+
runtime.log("No Claude/Codex credentials detected yet. If you use the CLIs, run `nexus credential import claude-cli` or `nexus credential import codex-cli`.");
|
|
85
85
|
return cfg;
|
|
86
86
|
}
|
|
87
87
|
export async function runQuickstartOnboarding(opts, runtime = defaultRuntime) {
|
|
@@ -113,14 +113,19 @@ export async function runQuickstartOnboarding(opts, runtime = defaultRuntime) {
|
|
|
113
113
|
},
|
|
114
114
|
};
|
|
115
115
|
nextConfig = applyQuickstartAuth(nextConfig, runtime);
|
|
116
|
-
nextConfig = applyWizardMetadata(nextConfig, {
|
|
116
|
+
nextConfig = applyWizardMetadata(nextConfig, {
|
|
117
|
+
command: "onboard",
|
|
118
|
+
mode: "local",
|
|
119
|
+
});
|
|
117
120
|
await writeConfigFile(nextConfig);
|
|
118
121
|
runtime.log(`Updated ${CONFIG_PATH_NEXUS}`);
|
|
119
122
|
await ensureWorkspaceAndSessions(workspaceDir, runtime, {
|
|
120
123
|
skipBootstrap: Boolean(nextConfig.agent?.skipBootstrap),
|
|
121
124
|
});
|
|
122
125
|
// Scripted bootstrap (test harness / automation): fill identity files + delete BOOTSTRAP.md.
|
|
123
|
-
if (opts.bootstrapPreset ||
|
|
126
|
+
if (opts.bootstrapPreset ||
|
|
127
|
+
opts.bootstrapAgentName ||
|
|
128
|
+
opts.bootstrapUserName) {
|
|
124
129
|
await applyBootstrapPreset({
|
|
125
130
|
workspaceDir,
|
|
126
131
|
runtime,
|
|
@@ -142,7 +147,9 @@ export async function runQuickstartOnboarding(opts, runtime = defaultRuntime) {
|
|
|
142
147
|
const { programArguments, workingDirectory } = await resolveGatewayProgramArguments({ port, dev: devMode });
|
|
143
148
|
const environment = {
|
|
144
149
|
PATH: process.env.PATH,
|
|
145
|
-
NEXUS_LAUNCHD_LABEL: process.platform === "darwin"
|
|
150
|
+
NEXUS_LAUNCHD_LABEL: process.platform === "darwin"
|
|
151
|
+
? GATEWAY_LAUNCH_AGENT_LABEL
|
|
152
|
+
: undefined,
|
|
146
153
|
};
|
|
147
154
|
await service.install({
|
|
148
155
|
env: process.env,
|