@intent-systems/nexus 2026.1.5-3 → 2026.1.5-4
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/capabilities/detector.js +214 -0
- package/dist/capabilities/registry.js +98 -0
- package/dist/channels/location.js +44 -0
- package/dist/channels/web/index.js +2 -0
- package/dist/control-plane/broker/broker.js +969 -0
- package/dist/control-plane/compaction.js +284 -0
- package/dist/control-plane/factory.js +31 -0
- package/dist/control-plane/index.js +10 -0
- package/dist/control-plane/odu/agents.js +187 -0
- package/dist/control-plane/odu/interaction-tools.js +196 -0
- package/dist/control-plane/odu/prompt-loader.js +95 -0
- package/dist/control-plane/odu/runtime.js +467 -0
- package/dist/control-plane/odu/types.js +6 -0
- package/dist/control-plane/odu-control-plane.js +314 -0
- package/dist/control-plane/single-agent.js +249 -0
- package/dist/control-plane/types.js +11 -0
- package/dist/credentials/store.js +323 -0
- package/dist/logging/redact.js +109 -0
- package/dist/markdown/fences.js +58 -0
- package/dist/memory/embeddings.js +146 -0
- package/dist/memory/index.js +382 -0
- package/dist/memory/internal.js +163 -0
- package/dist/pairing/pairing-store.js +194 -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/routing/resolve-route.js +144 -0
- package/dist/routing/session-key.js +63 -0
- package/dist/utils/provider-utils.js +28 -0
- 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,214 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
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
|
+
import { ensureCredentialIndexSync } from "../credentials/store.js";
|
|
7
|
+
import { NEXUS_ROOT } from "../utils.js";
|
|
8
|
+
import { loadCapabilityRegistry } from "./registry.js";
|
|
9
|
+
const STATUS_PRIORITY = [
|
|
10
|
+
"active",
|
|
11
|
+
"ready",
|
|
12
|
+
"needs_setup",
|
|
13
|
+
"needs_install",
|
|
14
|
+
"broken",
|
|
15
|
+
"unavailable",
|
|
16
|
+
];
|
|
17
|
+
function hasBinary(bin) {
|
|
18
|
+
const pathEnv = process.env.PATH ?? "";
|
|
19
|
+
const parts = pathEnv.split(path.delimiter).filter(Boolean);
|
|
20
|
+
for (const part of parts) {
|
|
21
|
+
const candidate = path.join(part, bin);
|
|
22
|
+
try {
|
|
23
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// keep scanning
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
function normalizeProviderId(raw) {
|
|
33
|
+
return raw.trim().toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
function resolveCredentialAliases(providerId) {
|
|
36
|
+
const base = providerId.replace(/-oauth$/, "");
|
|
37
|
+
const aliases = new Set([providerId, base]);
|
|
38
|
+
if (providerId === "gog") {
|
|
39
|
+
aliases.add("google");
|
|
40
|
+
aliases.add("google-oauth");
|
|
41
|
+
}
|
|
42
|
+
if (providerId === "google-oauth") {
|
|
43
|
+
aliases.add("google");
|
|
44
|
+
aliases.add("gog");
|
|
45
|
+
}
|
|
46
|
+
return Array.from(aliases).filter(Boolean);
|
|
47
|
+
}
|
|
48
|
+
function setupLooksCredentialed(raw) {
|
|
49
|
+
const lowered = raw.toLowerCase();
|
|
50
|
+
if (!lowered || lowered === "-" || lowered === "none")
|
|
51
|
+
return false;
|
|
52
|
+
return (lowered.includes("oauth") ||
|
|
53
|
+
lowered.includes("api key") ||
|
|
54
|
+
lowered.includes("apikey") ||
|
|
55
|
+
lowered.includes("token") ||
|
|
56
|
+
lowered.includes("account") ||
|
|
57
|
+
lowered.includes("bot") ||
|
|
58
|
+
lowered.includes("key"));
|
|
59
|
+
}
|
|
60
|
+
function detectSkillUsageActive(skillName) {
|
|
61
|
+
const usageLog = path.join(resolveStateDir(), "nexus", "skills", skillName, "usage.log");
|
|
62
|
+
try {
|
|
63
|
+
const stat = fs.statSync(usageLog);
|
|
64
|
+
return stat.isFile() && stat.size > 0;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function resolveSkillType(skillName, metadata) {
|
|
71
|
+
if (metadata?.type)
|
|
72
|
+
return metadata.type;
|
|
73
|
+
const requires = metadata?.requires;
|
|
74
|
+
if (requires?.env?.length || requires?.config?.length)
|
|
75
|
+
return "connector";
|
|
76
|
+
if (requires?.bins?.length)
|
|
77
|
+
return "tool";
|
|
78
|
+
return "guide";
|
|
79
|
+
}
|
|
80
|
+
function resolveSkillProviderStatus(params) {
|
|
81
|
+
const { skillName, providerId, config, credentialServices, metadata, setupHint } = params;
|
|
82
|
+
const platform = resolveRuntimePlatform();
|
|
83
|
+
const skillType = resolveSkillType(skillName, metadata);
|
|
84
|
+
if (metadata?.os?.length && !metadata.os.includes(platform)) {
|
|
85
|
+
return { id: providerId, kind: "skill", status: "unavailable", reason: "os" };
|
|
86
|
+
}
|
|
87
|
+
const skillKey = metadata?.skillKey ?? skillName;
|
|
88
|
+
const skillConfig = resolveSkillConfig(config, skillKey);
|
|
89
|
+
const requires = metadata?.requires;
|
|
90
|
+
const missingBins = (requires?.bins ?? []).filter((bin) => !hasBinary(bin));
|
|
91
|
+
const anyBins = requires?.anyBins ?? [];
|
|
92
|
+
const anyBinsOk = anyBins.length === 0 || anyBins.some((bin) => hasBinary(bin));
|
|
93
|
+
if (missingBins.length > 0 || !anyBinsOk) {
|
|
94
|
+
return {
|
|
95
|
+
id: providerId,
|
|
96
|
+
kind: "skill",
|
|
97
|
+
status: "needs_install",
|
|
98
|
+
reason: missingBins.length > 0 ? "missing_bins" : "missing_any_bins",
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const missingEnv = (requires?.env ?? []).filter((envName) => {
|
|
102
|
+
if (process.env[envName])
|
|
103
|
+
return false;
|
|
104
|
+
if (skillConfig?.env?.[envName])
|
|
105
|
+
return false;
|
|
106
|
+
if (skillConfig?.apiKey && metadata?.primaryEnv === envName)
|
|
107
|
+
return false;
|
|
108
|
+
return true;
|
|
109
|
+
});
|
|
110
|
+
if (missingEnv.length > 0) {
|
|
111
|
+
return { id: providerId, kind: "skill", status: "needs_setup", reason: "missing_env" };
|
|
112
|
+
}
|
|
113
|
+
const missingConfig = (requires?.config ?? []).filter((configPath) => !isConfigPathTruthy(config, configPath));
|
|
114
|
+
if (missingConfig.length > 0) {
|
|
115
|
+
return { id: providerId, kind: "skill", status: "needs_setup", reason: "missing_config" };
|
|
116
|
+
}
|
|
117
|
+
const credentialHints = requires?.credentials ?? [];
|
|
118
|
+
const requiresCredential = setupLooksCredentialed(setupHint ?? "");
|
|
119
|
+
if (skillType === "connector" || credentialHints.length > 0 || requiresCredential) {
|
|
120
|
+
const aliases = new Set();
|
|
121
|
+
for (const entry of [providerId, ...credentialHints]) {
|
|
122
|
+
for (const alias of resolveCredentialAliases(entry))
|
|
123
|
+
aliases.add(alias);
|
|
124
|
+
}
|
|
125
|
+
const hasCred = Array.from(aliases).some((alias) => credentialServices.has(alias));
|
|
126
|
+
if (!hasCred) {
|
|
127
|
+
return { id: providerId, kind: "skill", status: "needs_setup", reason: "missing_credentials" };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (detectSkillUsageActive(skillName)) {
|
|
131
|
+
return { id: providerId, kind: "skill", status: "active" };
|
|
132
|
+
}
|
|
133
|
+
return { id: providerId, kind: "skill", status: "ready" };
|
|
134
|
+
}
|
|
135
|
+
function resolveCredentialProviderStatus(providerId, credentialServices) {
|
|
136
|
+
const aliases = resolveCredentialAliases(providerId);
|
|
137
|
+
const hasCredential = aliases.some((alias) => credentialServices.has(alias));
|
|
138
|
+
return {
|
|
139
|
+
id: providerId,
|
|
140
|
+
kind: "credential",
|
|
141
|
+
status: hasCredential ? "ready" : "needs_setup",
|
|
142
|
+
reason: hasCredential ? undefined : "missing_credentials",
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function pickBestStatus(statuses) {
|
|
146
|
+
for (const status of STATUS_PRIORITY) {
|
|
147
|
+
if (statuses.includes(status))
|
|
148
|
+
return status;
|
|
149
|
+
}
|
|
150
|
+
return "unavailable";
|
|
151
|
+
}
|
|
152
|
+
export function detectCapabilities(params) {
|
|
153
|
+
const config = params?.config ?? loadConfig();
|
|
154
|
+
const workspaceDir = params?.workspaceDir ?? path.join(NEXUS_ROOT, "home");
|
|
155
|
+
const registry = params?.registry ?? loadCapabilityRegistry();
|
|
156
|
+
const credentialIndex = ensureCredentialIndexSync();
|
|
157
|
+
const credentialServices = new Set(Object.keys(credentialIndex.services ?? {}).map((name) => normalizeProviderId(name)));
|
|
158
|
+
const skillEntries = loadWorkspaceSkillEntries(workspaceDir, { config });
|
|
159
|
+
const skillMap = new Map();
|
|
160
|
+
const providesMap = new Map();
|
|
161
|
+
for (const entry of skillEntries) {
|
|
162
|
+
const metadata = getSkillMetadata(entry);
|
|
163
|
+
const key = normalizeProviderId(metadata?.skillKey ?? entry.skill.name);
|
|
164
|
+
skillMap.set(key, { name: entry.skill.name, metadata });
|
|
165
|
+
const provides = (metadata?.provides ?? []).map((cap) => cap.trim()).filter(Boolean);
|
|
166
|
+
for (const capId of provides) {
|
|
167
|
+
const normalizedCap = capId.trim();
|
|
168
|
+
if (!normalizedCap)
|
|
169
|
+
continue;
|
|
170
|
+
const existing = providesMap.get(normalizedCap) ?? [];
|
|
171
|
+
if (!existing.includes(key))
|
|
172
|
+
existing.push(key);
|
|
173
|
+
providesMap.set(normalizedCap, existing);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const capabilities = [];
|
|
177
|
+
for (const capability of registry.all) {
|
|
178
|
+
const extraProviders = providesMap.get(capability.id) ?? [];
|
|
179
|
+
const providerList = Array.from(new Set([...capability.providers.map(normalizeProviderId), ...extraProviders]));
|
|
180
|
+
const providerStatuses = [];
|
|
181
|
+
for (const provider of providerList) {
|
|
182
|
+
const normalized = normalizeProviderId(provider);
|
|
183
|
+
const skillEntry = skillMap.get(normalized);
|
|
184
|
+
if (skillEntry) {
|
|
185
|
+
providerStatuses.push(resolveSkillProviderStatus({
|
|
186
|
+
skillName: skillEntry.name,
|
|
187
|
+
providerId: normalized,
|
|
188
|
+
config,
|
|
189
|
+
credentialServices,
|
|
190
|
+
metadata: skillEntry.metadata,
|
|
191
|
+
setupHint: capability.setup,
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
else if (resolveCredentialAliases(normalized).some((alias) => credentialServices.has(alias))) {
|
|
195
|
+
providerStatuses.push(resolveCredentialProviderStatus(normalized, credentialServices));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
providerStatuses.push({ id: normalized, kind: "unknown", status: "unavailable" });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const status = pickBestStatus(providerStatuses.map((p) => p.status));
|
|
202
|
+
capabilities.push({ ...capability, status, providers: providerStatuses });
|
|
203
|
+
}
|
|
204
|
+
const summary = {
|
|
205
|
+
total: capabilities.length,
|
|
206
|
+
active: capabilities.filter((c) => c.status === "active").length,
|
|
207
|
+
ready: capabilities.filter((c) => c.status === "ready").length,
|
|
208
|
+
needs_setup: capabilities.filter((c) => c.status === "needs_setup").length,
|
|
209
|
+
needs_install: capabilities.filter((c) => c.status === "needs_install").length,
|
|
210
|
+
unavailable: capabilities.filter((c) => c.status === "unavailable").length,
|
|
211
|
+
broken: capabilities.filter((c) => c.status === "broken").length,
|
|
212
|
+
};
|
|
213
|
+
return { registry, capabilities, summary };
|
|
214
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { resolveUserPath } from "../utils.js";
|
|
5
|
+
const CAPABILITIES_PATH = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../docs/CAPABILITIES.md");
|
|
6
|
+
function resolveCapabilitiesPath() {
|
|
7
|
+
const candidates = [];
|
|
8
|
+
const override = process.env.NEXUS_CAPABILITIES_PATH?.trim();
|
|
9
|
+
if (override) {
|
|
10
|
+
candidates.push(resolveUserPath(override));
|
|
11
|
+
}
|
|
12
|
+
candidates.push(CAPABILITIES_PATH);
|
|
13
|
+
for (const candidate of candidates) {
|
|
14
|
+
if (!candidate)
|
|
15
|
+
continue;
|
|
16
|
+
try {
|
|
17
|
+
if (fs.existsSync(candidate))
|
|
18
|
+
return candidate;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// ignore and keep searching
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function normalizeProviderToken(raw) {
|
|
27
|
+
const normalized = raw
|
|
28
|
+
.replace(/\(.*?\)/g, "")
|
|
29
|
+
.replace(/\s+/g, " ")
|
|
30
|
+
.trim()
|
|
31
|
+
.toLowerCase();
|
|
32
|
+
if (normalized.endsWith(" guide")) {
|
|
33
|
+
return normalized.slice(0, -" guide".length).trim();
|
|
34
|
+
}
|
|
35
|
+
return normalized;
|
|
36
|
+
}
|
|
37
|
+
function parseProviders(raw) {
|
|
38
|
+
const cleaned = raw.replace(/\(.*?\)/g, "").trim();
|
|
39
|
+
if (!cleaned)
|
|
40
|
+
return [];
|
|
41
|
+
const parts = cleaned.split(/[,;+]/g).map((p) => p.trim());
|
|
42
|
+
const out = [];
|
|
43
|
+
for (const part of parts) {
|
|
44
|
+
if (!part)
|
|
45
|
+
continue;
|
|
46
|
+
const normalized = normalizeProviderToken(part);
|
|
47
|
+
if (!normalized)
|
|
48
|
+
continue;
|
|
49
|
+
if (!out.includes(normalized))
|
|
50
|
+
out.push(normalized);
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
export function loadCapabilityRegistry() {
|
|
55
|
+
const capabilitiesPath = resolveCapabilitiesPath();
|
|
56
|
+
if (!capabilitiesPath) {
|
|
57
|
+
return { categories: {}, all: [] };
|
|
58
|
+
}
|
|
59
|
+
let raw;
|
|
60
|
+
try {
|
|
61
|
+
raw = fs.readFileSync(capabilitiesPath, "utf-8");
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return { categories: {}, all: [] };
|
|
65
|
+
}
|
|
66
|
+
const lines = raw.replace(/\r\n/g, "\n").split("\n");
|
|
67
|
+
let currentCategory = "Uncategorized";
|
|
68
|
+
const categories = {};
|
|
69
|
+
const all = [];
|
|
70
|
+
for (const line of lines) {
|
|
71
|
+
const headingMatch = line.match(/^###\s+(.+)$/);
|
|
72
|
+
if (headingMatch) {
|
|
73
|
+
currentCategory = headingMatch[1]?.replace(/^[-–]\s*/, "").trim() || currentCategory;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const tableMatch = line.match(/^\|\s*`([^`]+)`\s*\|\s*([^|]+)\|\s*([^|]+)\|\s*([^|]+)\|/);
|
|
77
|
+
if (!tableMatch)
|
|
78
|
+
continue;
|
|
79
|
+
const [, idRaw, descriptionRaw, providersRaw, setupRaw] = tableMatch;
|
|
80
|
+
const id = (idRaw ?? "").trim();
|
|
81
|
+
if (!id)
|
|
82
|
+
continue;
|
|
83
|
+
const providersRawTrimmed = (providersRaw ?? "").trim();
|
|
84
|
+
const providers = parseProviders(providersRawTrimmed);
|
|
85
|
+
const capability = {
|
|
86
|
+
id,
|
|
87
|
+
description: (descriptionRaw ?? "").trim(),
|
|
88
|
+
providersRaw: providersRawTrimmed,
|
|
89
|
+
providers,
|
|
90
|
+
setup: (setupRaw ?? "").trim(),
|
|
91
|
+
category: currentCategory,
|
|
92
|
+
};
|
|
93
|
+
categories[currentCategory] = categories[currentCategory] ?? [];
|
|
94
|
+
categories[currentCategory].push(capability);
|
|
95
|
+
all.push(capability);
|
|
96
|
+
}
|
|
97
|
+
return { categories, all };
|
|
98
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
function resolveLocation(location) {
|
|
2
|
+
const source = location.source ??
|
|
3
|
+
(location.isLive ? "live" : location.name || location.address ? "place" : "pin");
|
|
4
|
+
const isLive = Boolean(location.isLive ?? source === "live");
|
|
5
|
+
return { ...location, source, isLive };
|
|
6
|
+
}
|
|
7
|
+
function formatAccuracy(accuracy) {
|
|
8
|
+
if (!Number.isFinite(accuracy))
|
|
9
|
+
return "";
|
|
10
|
+
return ` ±${Math.round(accuracy ?? 0)}m`;
|
|
11
|
+
}
|
|
12
|
+
function formatCoords(latitude, longitude) {
|
|
13
|
+
return `${latitude.toFixed(6)}, ${longitude.toFixed(6)}`;
|
|
14
|
+
}
|
|
15
|
+
export function formatLocationText(location) {
|
|
16
|
+
const resolved = resolveLocation(location);
|
|
17
|
+
const coords = formatCoords(resolved.latitude, resolved.longitude);
|
|
18
|
+
const accuracy = formatAccuracy(resolved.accuracy);
|
|
19
|
+
const caption = resolved.caption?.trim();
|
|
20
|
+
let header = "";
|
|
21
|
+
if (resolved.source === "live" || resolved.isLive) {
|
|
22
|
+
header = `🛰 Live location: ${coords}${accuracy}`;
|
|
23
|
+
}
|
|
24
|
+
else if (resolved.name || resolved.address) {
|
|
25
|
+
const label = [resolved.name, resolved.address].filter(Boolean).join(" — ");
|
|
26
|
+
header = `📍 ${label} (${coords}${accuracy})`;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
header = `📍 ${coords}${accuracy}`;
|
|
30
|
+
}
|
|
31
|
+
return caption ? `${header}\n${caption}` : header;
|
|
32
|
+
}
|
|
33
|
+
export function toLocationContext(location) {
|
|
34
|
+
const resolved = resolveLocation(location);
|
|
35
|
+
return {
|
|
36
|
+
LocationLat: resolved.latitude,
|
|
37
|
+
LocationLon: resolved.longitude,
|
|
38
|
+
LocationAccuracy: resolved.accuracy,
|
|
39
|
+
LocationName: resolved.name,
|
|
40
|
+
LocationAddress: resolved.address,
|
|
41
|
+
LocationSource: resolved.source,
|
|
42
|
+
LocationIsLive: resolved.isLive,
|
|
43
|
+
};
|
|
44
|
+
}
|