@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
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import { exec, execFile } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import fsp from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { resolveCredentialsDir } from "../config/paths.js";
|
|
7
|
+
export { resolveCredentialsDir };
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
export function resolveCredentialIndexPath() {
|
|
11
|
+
return path.join(resolveCredentialsDir(), "index.json");
|
|
12
|
+
}
|
|
13
|
+
export function resolveCredentialPath(service, account, authId) {
|
|
14
|
+
return path.join(resolveCredentialsDir(), service, "accounts", account, "auth", `${authId}.json`);
|
|
15
|
+
}
|
|
16
|
+
export function readCredentialRecordSync(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
if (!parsed || typeof parsed !== "object")
|
|
21
|
+
return null;
|
|
22
|
+
if (!parsed.type || !parsed.storage)
|
|
23
|
+
return null;
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function readCredentialRecord(filePath) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = await fsp.readFile(filePath, "utf-8");
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
if (!parsed || typeof parsed !== "object")
|
|
35
|
+
return null;
|
|
36
|
+
if (!parsed.type || !parsed.storage)
|
|
37
|
+
return null;
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function writeCredentialRecordSync(service, account, authId, record) {
|
|
45
|
+
const filePath = resolveCredentialPath(service, account, authId);
|
|
46
|
+
const dir = path.dirname(filePath);
|
|
47
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
48
|
+
fs.writeFileSync(filePath, `${JSON.stringify(record, null, 2)}\n`, "utf-8");
|
|
49
|
+
fs.chmodSync(filePath, 0o600);
|
|
50
|
+
}
|
|
51
|
+
export async function writeCredentialRecord(service, account, authId, record) {
|
|
52
|
+
const filePath = resolveCredentialPath(service, account, authId);
|
|
53
|
+
const dir = path.dirname(filePath);
|
|
54
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
55
|
+
await fsp.writeFile(filePath, `${JSON.stringify(record, null, 2)}\n`, "utf-8");
|
|
56
|
+
}
|
|
57
|
+
function isDirentDirectory(entry) {
|
|
58
|
+
return typeof entry !== "string";
|
|
59
|
+
}
|
|
60
|
+
export function listCredentialEntriesSync() {
|
|
61
|
+
const root = resolveCredentialsDir();
|
|
62
|
+
if (!fs.existsSync(root))
|
|
63
|
+
return [];
|
|
64
|
+
const services = fs.readdirSync(root, { withFileTypes: true });
|
|
65
|
+
const entries = [];
|
|
66
|
+
for (const serviceEntry of services) {
|
|
67
|
+
if (!isDirentDirectory(serviceEntry) || !serviceEntry.isDirectory())
|
|
68
|
+
continue;
|
|
69
|
+
const service = serviceEntry.name;
|
|
70
|
+
const accountsDir = path.join(root, service, "accounts");
|
|
71
|
+
if (!fs.existsSync(accountsDir))
|
|
72
|
+
continue;
|
|
73
|
+
const accounts = fs.readdirSync(accountsDir, { withFileTypes: true });
|
|
74
|
+
for (const accountEntry of accounts) {
|
|
75
|
+
if (!isDirentDirectory(accountEntry) || !accountEntry.isDirectory())
|
|
76
|
+
continue;
|
|
77
|
+
const account = accountEntry.name;
|
|
78
|
+
const authDir = path.join(accountsDir, account, "auth");
|
|
79
|
+
if (!fs.existsSync(authDir))
|
|
80
|
+
continue;
|
|
81
|
+
const authFiles = fs.readdirSync(authDir, { withFileTypes: true });
|
|
82
|
+
for (const authEntry of authFiles) {
|
|
83
|
+
if (!isDirentDirectory(authEntry) || authEntry.isDirectory())
|
|
84
|
+
continue;
|
|
85
|
+
if (!authEntry.name.endsWith(".json"))
|
|
86
|
+
continue;
|
|
87
|
+
const authId = authEntry.name.replace(/\.json$/, "");
|
|
88
|
+
const filePath = path.join(authDir, authEntry.name);
|
|
89
|
+
const record = readCredentialRecordSync(filePath);
|
|
90
|
+
if (!record)
|
|
91
|
+
continue;
|
|
92
|
+
entries.push({ service, account, authId, filePath, record });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return entries;
|
|
97
|
+
}
|
|
98
|
+
export async function listCredentialEntries() {
|
|
99
|
+
const root = resolveCredentialsDir();
|
|
100
|
+
if (!fs.existsSync(root))
|
|
101
|
+
return [];
|
|
102
|
+
const services = await fsp.readdir(root, { withFileTypes: true });
|
|
103
|
+
const entries = [];
|
|
104
|
+
for (const serviceEntry of services) {
|
|
105
|
+
if (!isDirentDirectory(serviceEntry) || !serviceEntry.isDirectory())
|
|
106
|
+
continue;
|
|
107
|
+
const service = serviceEntry.name;
|
|
108
|
+
const accountsDir = path.join(root, service, "accounts");
|
|
109
|
+
if (!fs.existsSync(accountsDir))
|
|
110
|
+
continue;
|
|
111
|
+
const accounts = await fsp.readdir(accountsDir, { withFileTypes: true });
|
|
112
|
+
for (const accountEntry of accounts) {
|
|
113
|
+
if (!isDirentDirectory(accountEntry) || !accountEntry.isDirectory())
|
|
114
|
+
continue;
|
|
115
|
+
const account = accountEntry.name;
|
|
116
|
+
const authDir = path.join(accountsDir, account, "auth");
|
|
117
|
+
if (!fs.existsSync(authDir))
|
|
118
|
+
continue;
|
|
119
|
+
const authFiles = await fsp.readdir(authDir, { withFileTypes: true });
|
|
120
|
+
for (const authEntry of authFiles) {
|
|
121
|
+
if (!isDirentDirectory(authEntry) || authEntry.isDirectory())
|
|
122
|
+
continue;
|
|
123
|
+
if (!authEntry.name.endsWith(".json"))
|
|
124
|
+
continue;
|
|
125
|
+
const authId = authEntry.name.replace(/\.json$/, "");
|
|
126
|
+
const filePath = path.join(authDir, authEntry.name);
|
|
127
|
+
const record = await readCredentialRecord(filePath);
|
|
128
|
+
if (!record)
|
|
129
|
+
continue;
|
|
130
|
+
entries.push({ service, account, authId, filePath, record });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return entries;
|
|
135
|
+
}
|
|
136
|
+
export function buildCredentialIndex(entries) {
|
|
137
|
+
const services = {};
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
const service = services[entry.service] ?? { accounts: [] };
|
|
140
|
+
const account = service.accounts.find((acc) => acc.id === entry.account);
|
|
141
|
+
const status = entry.record.lastError
|
|
142
|
+
? "broken"
|
|
143
|
+
: entry.record.lastUsed
|
|
144
|
+
? "active"
|
|
145
|
+
: "ready";
|
|
146
|
+
if (account) {
|
|
147
|
+
if (!account.auths.includes(entry.authId))
|
|
148
|
+
account.auths.push(entry.authId);
|
|
149
|
+
if (entry.record.lastError) {
|
|
150
|
+
account.status = "broken";
|
|
151
|
+
account.lastError = entry.record.lastError;
|
|
152
|
+
}
|
|
153
|
+
else if (account.status !== "broken" && entry.record.lastUsed) {
|
|
154
|
+
account.status = "active";
|
|
155
|
+
account.lastUsed = entry.record.lastUsed;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
service.accounts.push({
|
|
160
|
+
id: entry.account,
|
|
161
|
+
owner: entry.record.owner,
|
|
162
|
+
auths: [entry.authId],
|
|
163
|
+
status,
|
|
164
|
+
lastUsed: entry.record.lastUsed,
|
|
165
|
+
lastError: entry.record.lastError ?? null,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
services[entry.service] = service;
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
version: 1,
|
|
172
|
+
lastUpdated: new Date().toISOString(),
|
|
173
|
+
services,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
export function readCredentialIndexSync() {
|
|
177
|
+
const indexPath = resolveCredentialIndexPath();
|
|
178
|
+
try {
|
|
179
|
+
const raw = fs.readFileSync(indexPath, "utf-8");
|
|
180
|
+
const parsed = JSON.parse(raw);
|
|
181
|
+
if (!parsed || typeof parsed !== "object")
|
|
182
|
+
return null;
|
|
183
|
+
if (!parsed.services || typeof parsed.services !== "object")
|
|
184
|
+
return null;
|
|
185
|
+
return parsed;
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
export async function readCredentialIndex() {
|
|
192
|
+
const indexPath = resolveCredentialIndexPath();
|
|
193
|
+
try {
|
|
194
|
+
const raw = await fsp.readFile(indexPath, "utf-8");
|
|
195
|
+
const parsed = JSON.parse(raw);
|
|
196
|
+
if (!parsed || typeof parsed !== "object")
|
|
197
|
+
return null;
|
|
198
|
+
if (!parsed.services || typeof parsed.services !== "object")
|
|
199
|
+
return null;
|
|
200
|
+
return parsed;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
export function writeCredentialIndexSync(index) {
|
|
207
|
+
const indexPath = resolveCredentialIndexPath();
|
|
208
|
+
fs.mkdirSync(path.dirname(indexPath), { recursive: true });
|
|
209
|
+
fs.writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}\n`, "utf-8");
|
|
210
|
+
}
|
|
211
|
+
export async function writeCredentialIndex(index) {
|
|
212
|
+
const indexPath = resolveCredentialIndexPath();
|
|
213
|
+
await fsp.mkdir(path.dirname(indexPath), { recursive: true });
|
|
214
|
+
await fsp.writeFile(indexPath, `${JSON.stringify(index, null, 2)}\n`, "utf-8");
|
|
215
|
+
}
|
|
216
|
+
export function ensureCredentialIndexSync() {
|
|
217
|
+
const existing = readCredentialIndexSync();
|
|
218
|
+
if (existing)
|
|
219
|
+
return existing;
|
|
220
|
+
const index = buildCredentialIndex(listCredentialEntriesSync());
|
|
221
|
+
writeCredentialIndexSync(index);
|
|
222
|
+
return index;
|
|
223
|
+
}
|
|
224
|
+
function resolveStorageField(storage, field) {
|
|
225
|
+
if (storage.provider !== "1password")
|
|
226
|
+
return null;
|
|
227
|
+
const exact = storage.fields?.[field];
|
|
228
|
+
if (exact)
|
|
229
|
+
return exact;
|
|
230
|
+
if (field === "key")
|
|
231
|
+
return storage.fields?.apiKey ?? storage.fields?.token ?? null;
|
|
232
|
+
if (field === "accessToken")
|
|
233
|
+
return storage.fields?.accessToken ?? storage.fields?.token ?? null;
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
async function readRawFromStorage(storage, field) {
|
|
237
|
+
if (storage.provider === "keychain") {
|
|
238
|
+
try {
|
|
239
|
+
const { stdout } = await execFileAsync("security", [
|
|
240
|
+
"find-generic-password",
|
|
241
|
+
"-s",
|
|
242
|
+
storage.service,
|
|
243
|
+
"-a",
|
|
244
|
+
storage.account,
|
|
245
|
+
"-w",
|
|
246
|
+
]);
|
|
247
|
+
const trimmed = stdout.trim();
|
|
248
|
+
return trimmed ? trimmed : null;
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (storage.provider === "env") {
|
|
255
|
+
const envValue = process.env[storage.var];
|
|
256
|
+
if (typeof envValue !== "string")
|
|
257
|
+
return null;
|
|
258
|
+
const trimmed = envValue.trim();
|
|
259
|
+
return trimmed ? trimmed : null;
|
|
260
|
+
}
|
|
261
|
+
if (storage.provider === "1password") {
|
|
262
|
+
const fieldName = resolveStorageField(storage, field);
|
|
263
|
+
if (!fieldName)
|
|
264
|
+
return null;
|
|
265
|
+
const itemRef = `op://${storage.vault}/${storage.item}/${fieldName}`;
|
|
266
|
+
try {
|
|
267
|
+
const { stdout } = await execFileAsync("op", ["read", itemRef]);
|
|
268
|
+
const trimmed = stdout.trim();
|
|
269
|
+
return trimmed ? trimmed : null;
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (storage.provider === "external") {
|
|
276
|
+
try {
|
|
277
|
+
const command = storage.command ?? storage.syncCommand;
|
|
278
|
+
if (!command)
|
|
279
|
+
return null;
|
|
280
|
+
const { stdout } = await execAsync(command);
|
|
281
|
+
const trimmed = stdout.trim();
|
|
282
|
+
return trimmed ? trimmed : null;
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
async function readSecretFromStorage(record, field) {
|
|
291
|
+
const storage = record.storage;
|
|
292
|
+
const raw = await readRawFromStorage(storage, field);
|
|
293
|
+
if (!raw)
|
|
294
|
+
return null;
|
|
295
|
+
const wantsJson = storage.format === "json" || Boolean(storage.jsonPath);
|
|
296
|
+
if (!wantsJson)
|
|
297
|
+
return raw;
|
|
298
|
+
try {
|
|
299
|
+
const parsed = JSON.parse(raw);
|
|
300
|
+
const pathSpec = storage.jsonPath ?? field;
|
|
301
|
+
const resolved = resolveJsonPath(parsed, pathSpec);
|
|
302
|
+
if (typeof resolved === "string")
|
|
303
|
+
return resolved.trim() || null;
|
|
304
|
+
if (typeof resolved === "number")
|
|
305
|
+
return String(resolved);
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function resolveJsonPath(value, pathSpec) {
|
|
313
|
+
const parts = pathSpec.split(".").filter(Boolean);
|
|
314
|
+
let current = value;
|
|
315
|
+
for (const part of parts) {
|
|
316
|
+
if (!current || typeof current !== "object")
|
|
317
|
+
return undefined;
|
|
318
|
+
const match = /^(.+?)\[(\d+)\]$/.exec(part);
|
|
319
|
+
if (match) {
|
|
320
|
+
const [, key, indexRaw] = match;
|
|
321
|
+
const index = Number.parseInt(indexRaw, 10);
|
|
322
|
+
if (Number.isNaN(index))
|
|
323
|
+
return undefined;
|
|
324
|
+
const next = current[key ?? ""];
|
|
325
|
+
if (!Array.isArray(next))
|
|
326
|
+
return undefined;
|
|
327
|
+
current = next[index];
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
current = current[part];
|
|
331
|
+
}
|
|
332
|
+
return current;
|
|
333
|
+
}
|
|
334
|
+
export function resolveDefaultEnvVar(params) {
|
|
335
|
+
const service = params.service.toLowerCase();
|
|
336
|
+
const type = params.type;
|
|
337
|
+
const known = {
|
|
338
|
+
anthropic: {
|
|
339
|
+
api_key: "ANTHROPIC_API_KEY",
|
|
340
|
+
token: "ANTHROPIC_OAUTH_TOKEN",
|
|
341
|
+
oauth: "ANTHROPIC_OAUTH_TOKEN",
|
|
342
|
+
},
|
|
343
|
+
openai: { api_key: "OPENAI_API_KEY" },
|
|
344
|
+
gemini: { api_key: "GEMINI_API_KEY" },
|
|
345
|
+
"brave-search": { api_key: "BRAVE_API_KEY" },
|
|
346
|
+
github: { token: "GITHUB_TOKEN" },
|
|
347
|
+
"github-copilot": { token: "COPILOT_GITHUB_TOKEN" },
|
|
348
|
+
discord: { token: "DISCORD_BOT_TOKEN" },
|
|
349
|
+
slack: { token: "SLACK_BOT_TOKEN" },
|
|
350
|
+
openrouter: { api_key: "OPENROUTER_API_KEY" },
|
|
351
|
+
zai: { api_key: "ZAI_API_KEY" },
|
|
352
|
+
minimax: { api_key: "MINIMAX_API_KEY", token: "MINIMAX_API_KEY" },
|
|
353
|
+
"openai-codex": { oauth: "NEXUS_OPENAI_CODEX_TOKEN" },
|
|
354
|
+
"google-antigravity": { oauth: "NEXUS_GOOGLE_ANTIGRAVITY_TOKEN" },
|
|
355
|
+
chutes: { oauth: "NEXUS_CHUTES_TOKEN" },
|
|
356
|
+
"nexus-cloud": { token: "NEXUS_CLOUD_TOKEN" },
|
|
357
|
+
"nexus-hub": { token: "NEXUS_HUB_TOKEN" },
|
|
358
|
+
};
|
|
359
|
+
const mapped = known[service]?.[type];
|
|
360
|
+
if (mapped)
|
|
361
|
+
return mapped;
|
|
362
|
+
const suffix = type === "api_key"
|
|
363
|
+
? "API_KEY"
|
|
364
|
+
: type === "oauth"
|
|
365
|
+
? "OAUTH_TOKEN"
|
|
366
|
+
: type === "token"
|
|
367
|
+
? "TOKEN"
|
|
368
|
+
: "CONFIG";
|
|
369
|
+
const normalized = service.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
|
|
370
|
+
return `NEXUS_${normalized}_${suffix}`;
|
|
371
|
+
}
|
|
372
|
+
export async function resolveOAuthBundle(record) {
|
|
373
|
+
if (record.type !== "oauth")
|
|
374
|
+
return {};
|
|
375
|
+
const raw = await readRawFromStorage(record.storage, "accessToken");
|
|
376
|
+
if (!raw)
|
|
377
|
+
return {};
|
|
378
|
+
const wantsJson = record.storage.format === "json" || Boolean(record.storage.jsonPath);
|
|
379
|
+
if (!wantsJson) {
|
|
380
|
+
return { accessToken: raw, expiresAt: record.expiresAt };
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
const parsed = JSON.parse(raw);
|
|
384
|
+
if (record.storage.jsonPath) {
|
|
385
|
+
const access = resolveJsonPath(parsed, record.storage.jsonPath);
|
|
386
|
+
if (typeof access === "string") {
|
|
387
|
+
return { accessToken: access };
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
const accessToken = typeof parsed.accessToken === "string"
|
|
391
|
+
? parsed.accessToken
|
|
392
|
+
: typeof parsed.access_token === "string"
|
|
393
|
+
? parsed.access_token
|
|
394
|
+
: undefined;
|
|
395
|
+
const refreshToken = typeof parsed.refreshToken === "string"
|
|
396
|
+
? parsed.refreshToken
|
|
397
|
+
: typeof parsed.refresh_token === "string"
|
|
398
|
+
? parsed.refresh_token
|
|
399
|
+
: undefined;
|
|
400
|
+
const expiresAt = typeof parsed.expiresAt === "number" || typeof parsed.expiresAt === "string"
|
|
401
|
+
? parsed.expiresAt
|
|
402
|
+
: typeof parsed.expires_at === "number" || typeof parsed.expires_at === "string"
|
|
403
|
+
? parsed.expires_at
|
|
404
|
+
: undefined;
|
|
405
|
+
return { accessToken, refreshToken, expiresAt };
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
return {};
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
export async function storeKeychainSecret(params) {
|
|
412
|
+
try {
|
|
413
|
+
await execFileAsync("security", [
|
|
414
|
+
"add-generic-password",
|
|
415
|
+
"-U",
|
|
416
|
+
"-s",
|
|
417
|
+
params.service,
|
|
418
|
+
"-a",
|
|
419
|
+
params.account,
|
|
420
|
+
"-w",
|
|
421
|
+
params.value,
|
|
422
|
+
]);
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
export async function resolveCredentialValue(record) {
|
|
430
|
+
if (record.type === "api_key") {
|
|
431
|
+
const value = await readSecretFromStorage(record, "key");
|
|
432
|
+
if (!value)
|
|
433
|
+
return null;
|
|
434
|
+
return { value, field: "key" };
|
|
435
|
+
}
|
|
436
|
+
if (record.type === "token") {
|
|
437
|
+
const value = await readSecretFromStorage(record, "token");
|
|
438
|
+
if (!value)
|
|
439
|
+
return null;
|
|
440
|
+
return { value, field: "token" };
|
|
441
|
+
}
|
|
442
|
+
if (record.type === "oauth") {
|
|
443
|
+
const value = await readSecretFromStorage(record, "accessToken");
|
|
444
|
+
if (!value)
|
|
445
|
+
return null;
|
|
446
|
+
return { value, field: "accessToken" };
|
|
447
|
+
}
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export async function startBrowserControlServerIfEnabled() {
|
|
2
|
-
if (process.env.NEXUS_SKIP_BROWSER_CONTROL_SERVER === "1"
|
|
3
|
-
process.env.NEXUS_SKIP_BROWSER_CONTROL_SERVER === "1")
|
|
2
|
+
if (process.env.NEXUS_SKIP_BROWSER_CONTROL_SERVER === "1")
|
|
4
3
|
return null;
|
|
5
4
|
// Lazy import: keeps startup fast, but still bundles for the embedded
|
|
6
5
|
// gateway (bun --compile) via the static specifier path.
|
|
7
|
-
const override =
|
|
8
|
-
const mod = override
|
|
6
|
+
const override = process.env.NEXUS_BROWSER_CONTROL_MODULE?.trim();
|
|
7
|
+
const mod = override
|
|
8
|
+
? await import(override)
|
|
9
|
+
: await import("../browser/server.js");
|
|
9
10
|
await mod.startBrowserControlServerFromConfig();
|
|
10
11
|
return { stop: mod.stopBrowserControlServer };
|
|
11
12
|
}
|
|
@@ -41,9 +41,19 @@ export const cronHandlers = {
|
|
|
41
41
|
},
|
|
42
42
|
"cron.update": async ({ params, respond, context }) => {
|
|
43
43
|
const normalizedPatch = normalizeCronJobPatch(params?.patch);
|
|
44
|
-
const
|
|
44
|
+
const withPatch = normalizedPatch && typeof params === "object" && params !== null
|
|
45
45
|
? { ...params, patch: normalizedPatch }
|
|
46
46
|
: params;
|
|
47
|
+
const candidate = withPatch && typeof withPatch === "object" && withPatch !== null
|
|
48
|
+
? (() => {
|
|
49
|
+
const entry = withPatch;
|
|
50
|
+
if (!entry.id && entry.jobId) {
|
|
51
|
+
const { jobId, ...rest } = entry;
|
|
52
|
+
return { ...rest, id: jobId };
|
|
53
|
+
}
|
|
54
|
+
return entry;
|
|
55
|
+
})()
|
|
56
|
+
: withPatch;
|
|
47
57
|
if (!validateCronUpdateParams(candidate)) {
|
|
48
58
|
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid cron.update params: ${formatValidationErrors(validateCronUpdateParams.errors)}`));
|
|
49
59
|
return;
|
package/dist/gateway/server.js
CHANGED
|
@@ -2,10 +2,10 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { WebSocketServer } from "ws";
|
|
5
|
-
import { createSubagentRegistry } from "../agents/subagent-registry.js";
|
|
6
5
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
|
|
7
6
|
import { loadModelCatalog, resetModelCatalogCacheForTest, } from "../agents/model-catalog.js";
|
|
8
7
|
import { resolveConfiguredModelRef } from "../agents/model-selection.js";
|
|
8
|
+
import { createSubagentRegistry } from "../agents/subagent-registry.js";
|
|
9
9
|
import { CANVAS_HOST_PATH } from "../canvas-host/a2ui.js";
|
|
10
10
|
import { createCanvasHostHandler, startCanvasHost, } from "../canvas-host/server.js";
|
|
11
11
|
import { createDefaultDeps } from "../cli/deps.js";
|
|
@@ -256,7 +256,8 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
256
256
|
allowTailscale,
|
|
257
257
|
};
|
|
258
258
|
let hooksConfig = resolveHooksConfig(cfgAtStart);
|
|
259
|
-
const canvasHostEnabled = process.env.NEXUS_SKIP_CANVAS_HOST !== "1" &&
|
|
259
|
+
const canvasHostEnabled = process.env.NEXUS_SKIP_CANVAS_HOST !== "1" &&
|
|
260
|
+
cfgAtStart.canvasHost?.enabled !== false;
|
|
260
261
|
assertGatewayAuthConfigured(resolvedAuth);
|
|
261
262
|
if (tailscaleMode === "funnel" && authMode !== "password") {
|
|
262
263
|
throw new Error("tailscale funnel requires gateway auth mode=password (set gateway.auth.password or NEXUS_GATEWAY_PASSWORD)");
|
|
@@ -464,7 +465,7 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
464
465
|
return resolveMainSessionKey(cfg);
|
|
465
466
|
return resolveAgentMainSessionKey({ cfg, agentId });
|
|
466
467
|
};
|
|
467
|
-
const
|
|
468
|
+
const _enqueueCronSystemEvent = (text, opts) => {
|
|
468
469
|
const sessionKey = resolveCronMainSessionKey(opts?.agentId);
|
|
469
470
|
if (sessionKey) {
|
|
470
471
|
enqueueSystemEvent(text, { sessionKey });
|
|
@@ -586,7 +587,9 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
586
587
|
}
|
|
587
588
|
if (process.env.NEXUS_BRIDGE_PORT !== undefined) {
|
|
588
589
|
const parsed = Number.parseInt(process.env.NEXUS_BRIDGE_PORT, 10);
|
|
589
|
-
return Number.isFinite(parsed) && parsed > 0
|
|
590
|
+
return Number.isFinite(parsed) && parsed > 0
|
|
591
|
+
? parsed
|
|
592
|
+
: deriveDefaultBridgePort(port);
|
|
590
593
|
}
|
|
591
594
|
return deriveDefaultBridgePort(port);
|
|
592
595
|
})();
|
|
@@ -1049,7 +1052,9 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
1049
1052
|
type: "hello-ok",
|
|
1050
1053
|
protocol: PROTOCOL_VERSION,
|
|
1051
1054
|
server: {
|
|
1052
|
-
version: process.env.NEXUS_VERSION ??
|
|
1055
|
+
version: process.env.NEXUS_VERSION ??
|
|
1056
|
+
process.env.npm_package_version ??
|
|
1057
|
+
"dev",
|
|
1053
1058
|
commit: process.env.GIT_COMMIT,
|
|
1054
1059
|
host: os.hostname(),
|
|
1055
1060
|
connId,
|
|
@@ -1240,7 +1245,8 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
1240
1245
|
}
|
|
1241
1246
|
// Launch configured channels (WhatsApp Web, Discord, Slack, Telegram) so gateway replies via the
|
|
1242
1247
|
// surface the message came from. Tests can opt out via NEXUS_SKIP_CHANNELS (or legacy _PROVIDERS).
|
|
1243
|
-
const skipChannels = process.env.NEXUS_SKIP_CHANNELS === "1" ||
|
|
1248
|
+
const skipChannels = process.env.NEXUS_SKIP_CHANNELS === "1" ||
|
|
1249
|
+
process.env.NEXUS_SKIP_PROVIDERS === "1";
|
|
1244
1250
|
if (!skipChannels) {
|
|
1245
1251
|
try {
|
|
1246
1252
|
await startProviders();
|
|
@@ -1315,7 +1321,8 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
1315
1321
|
}
|
|
1316
1322
|
}
|
|
1317
1323
|
if (plan.restartProviders.size > 0) {
|
|
1318
|
-
const skipChannelReload = process.env.NEXUS_SKIP_CHANNELS === "1" ||
|
|
1324
|
+
const skipChannelReload = process.env.NEXUS_SKIP_CHANNELS === "1" ||
|
|
1325
|
+
process.env.NEXUS_SKIP_PROVIDERS === "1";
|
|
1319
1326
|
if (skipChannelReload) {
|
|
1320
1327
|
logChannels.info("skipping channel reload (NEXUS_SKIP_CHANNELS=1)");
|
|
1321
1328
|
}
|
package/dist/infra/bonjour.js
CHANGED
|
@@ -2,7 +2,7 @@ import os from "node:os";
|
|
|
2
2
|
import { logDebug, logWarn } from "../logger.js";
|
|
3
3
|
import { getLogger } from "../logging.js";
|
|
4
4
|
function isDisabledByEnv() {
|
|
5
|
-
if (process.env.NEXUS_DISABLE_BONJOUR === "1"
|
|
5
|
+
if (process.env.NEXUS_DISABLE_BONJOUR === "1")
|
|
6
6
|
return true;
|
|
7
7
|
if (process.env.NODE_ENV === "test")
|
|
8
8
|
return true;
|
package/dist/infra/event-log.js
CHANGED
|
@@ -200,7 +200,11 @@ export function recordCliCommandFinish(status = "ok") {
|
|
|
200
200
|
export function recordCliCommandFailure(error) {
|
|
201
201
|
if (!cliSession?.activeCommand)
|
|
202
202
|
return;
|
|
203
|
-
const message = error instanceof Error
|
|
203
|
+
const message = error instanceof Error
|
|
204
|
+
? error.message
|
|
205
|
+
: typeof error === "string"
|
|
206
|
+
? error
|
|
207
|
+
: "unknown error";
|
|
204
208
|
writeEvent({
|
|
205
209
|
id: createEventId(),
|
|
206
210
|
ts: Date.now(),
|
|
@@ -227,7 +231,9 @@ export function recordCliSessionEnd(params) {
|
|
|
227
231
|
invocation_kind: cliSession.invocationKind,
|
|
228
232
|
status: params.status,
|
|
229
233
|
error: params.error,
|
|
230
|
-
data: params.exitCode !== undefined
|
|
234
|
+
data: params.exitCode !== undefined
|
|
235
|
+
? { exit_code: params.exitCode }
|
|
236
|
+
: undefined,
|
|
231
237
|
});
|
|
232
238
|
cliSession = null;
|
|
233
239
|
}
|
package/dist/infra/path-env.js
CHANGED
|
@@ -79,10 +79,9 @@ function candidateBinDirs(opts) {
|
|
|
79
79
|
* under launchd/minimal environments (and inside the macOS bun bundle).
|
|
80
80
|
*/
|
|
81
81
|
export function ensureNexusCliOnPath(opts = {}) {
|
|
82
|
-
if (process.env.NEXUS_PATH_BOOTSTRAPPED === "1"
|
|
82
|
+
if (process.env.NEXUS_PATH_BOOTSTRAPPED === "1")
|
|
83
83
|
return;
|
|
84
84
|
process.env.NEXUS_PATH_BOOTSTRAPPED = "1";
|
|
85
|
-
process.env.NEXUS_PATH_BOOTSTRAPPED = "1"; // keep for backward compat
|
|
86
85
|
const existing = opts.pathEnv ?? process.env.PATH ?? "";
|
|
87
86
|
const prepend = candidateBinDirs(opts);
|
|
88
87
|
if (prepend.length === 0)
|
|
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { CLAUDE_CLI_PROFILE_ID, ensureAuthProfileStore, listProfilesForProvider, resolveApiKeyForProfile, resolveAuthProfileOrder, } from "../agents/auth-profiles.js";
|
|
5
|
-
import { getCustomProviderApiKey, resolveEnvApiKey } from "../agents/model-auth.js";
|
|
5
|
+
import { getCustomProviderApiKey, resolveEnvApiKey, } from "../agents/model-auth.js";
|
|
6
6
|
import { normalizeProviderId } from "../agents/model-selection.js";
|
|
7
7
|
import { loadConfig } from "../config/config.js";
|
|
8
8
|
function parseGoogleToken(apiKey) {
|
|
@@ -53,7 +53,8 @@ function resolveZaiApiKey() {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
function resolveMinimaxApiKey() {
|
|
56
|
-
const envDirect = process.env.MINIMAX_CODE_PLAN_KEY?.trim() ||
|
|
56
|
+
const envDirect = process.env.MINIMAX_CODE_PLAN_KEY?.trim() ||
|
|
57
|
+
process.env.MINIMAX_API_KEY?.trim();
|
|
57
58
|
if (envDirect)
|
|
58
59
|
return envDirect;
|
|
59
60
|
const envResolved = resolveEnvApiKey("minimax");
|
|
@@ -112,7 +113,8 @@ async function resolveOAuthToken(params) {
|
|
|
112
113
|
if (!resolved?.apiKey)
|
|
113
114
|
continue;
|
|
114
115
|
let token = resolved.apiKey;
|
|
115
|
-
if (params.provider === "google-gemini-cli" ||
|
|
116
|
+
if (params.provider === "google-gemini-cli" ||
|
|
117
|
+
params.provider === "google-antigravity") {
|
|
116
118
|
const parsed = parseGoogleToken(resolved.apiKey);
|
|
117
119
|
token = parsed?.token ?? resolved.apiKey;
|
|
118
120
|
}
|