neuralos 3.9.4 → 3.9.6
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/LICENSE +202 -0
- package/README.md +48 -347
- package/cli.js +78 -0
- package/engine.js +124 -0
- package/index.js +160 -0
- package/package.json +37 -43
- package/test.js +20 -0
- package/tools.js +47 -0
- package/bin/gybackend.cjs +0 -398530
- package/plugins/agentspan-bridge/agentspan-bridge.extreme.spec.ts +0 -328
- package/plugins/agentspan-bridge/agentspan-bridge.phase2.extreme.spec.ts +0 -282
- package/plugins/agentspan-bridge/conductorClient.mjs +0 -182
- package/plugins/agentspan-bridge/index.mjs +0 -389
- package/plugins/agentspan-bridge/playbookToWorkflowDef.mjs +0 -209
- package/plugins/agentspan-bridge/plugin.json +0 -27
- package/plugins/fraudops/index.d.mts +0 -9
- package/plugins/fraudops/index.mjs +0 -185
- package/plugins/fraudops/plugin.json +0 -10
- package/plugins/iam-connector/index.d.mts +0 -10
- package/plugins/iam-connector/index.mjs +0 -229
- package/plugins/iam-connector/plugin.json +0 -10
- package/plugins/mitmproxy-bridge/index.d.mts +0 -20
- package/plugins/mitmproxy-bridge/index.mjs +0 -256
- package/plugins/mitmproxy-bridge/plugin.json +0 -17
- package/plugins/monid-bridge/index.mjs +0 -228
- package/plugins/monid-bridge/monid-bridge.extreme.spec.mjs +0 -75
- package/plugins/monid-bridge/plugin.json +0 -15
- package/plugins/netdata-rterm/index.mjs +0 -229
- package/plugins/netdata-rterm/netdata-rterm.extreme.spec.ts +0 -252
- package/plugins/netdata-rterm/plugin.json +0 -22
- package/plugins/netexec-bridge/index.d.mts +0 -28
- package/plugins/netexec-bridge/index.mjs +0 -325
- package/plugins/netexec-bridge/plugin.json +0 -20
- package/plugins/neuralos/index.mjs +0 -137
- package/plugins/neuralos/neuralos.extreme.spec.mts +0 -386
- package/plugins/neuralos/neuralosEngine.mjs +0 -369
- package/plugins/neuralos/plugin.json +0 -15
- package/plugins/numbat-bridge/index.mjs +0 -229
- package/plugins/numbat-bridge/numbat-bridge.extreme.spec.mjs +0 -148
- package/plugins/numbat-bridge/plugin.json +0 -21
- package/plugins/patch-manager/index.d.mts +0 -10
- package/plugins/patch-manager/index.mjs +0 -288
- package/plugins/patch-manager/plugin.json +0 -10
- package/plugins/promptfoo-redteam/index.d.mts +0 -15
- package/plugins/promptfoo-redteam/index.mjs +0 -277
- package/plugins/promptfoo-redteam/plugin.json +0 -16
- package/plugins/reactorpro-bridge/index.mjs +0 -499
- package/plugins/reactorpro-bridge/plugin.json +0 -26
- package/plugins/reactorpro-bridge/reactorpro-bridge.extreme.spec.mts +0 -409
- package/plugins/reactorpro-bridge/reactorproAgent.mjs +0 -399
- package/plugins/request-router/index.d.mts +0 -9
- package/plugins/request-router/index.mjs +0 -239
- package/plugins/request-router/plugin.json +0 -10
- package/plugins/sample-k8s-slo/index.ts +0 -58
- package/plugins/sample-k8s-slo/plugin.json +0 -11
- package/plugins/sop-assistant/index.d.mts +0 -9
- package/plugins/sop-assistant/index.mjs +0 -325
- package/plugins/sop-assistant/plugin.json +0 -10
- package/plugins/synapse-bridge/index.mjs +0 -484
- package/plugins/synapse-bridge/plugin.json +0 -29
- package/plugins/synapse-bridge/synapse-bridge.extreme.spec.mjs +0 -204
- package/plugins/synapse-bridge/synapseAgent.extreme.spec.mjs +0 -236
- package/plugins/synapse-bridge/synapseAgent.mjs +0 -254
- package/plugins/web-intel/index.mjs +0 -381
- package/plugins/web-intel/plugin.json +0 -28
- package/plugins/web-intel/sidecar.mjs +0 -134
- package/plugins/web-intel/web-intel.extreme.spec.ts +0 -263
- package/plugins/web-intel/wigoloClient.mjs +0 -145
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* neuralos — neuralOS instances as RTerm agent tools.
|
|
3
|
-
*
|
|
4
|
-
* neuralOS instances (~/neuralos-instances) are on-device data agents: a
|
|
5
|
-
* verified menu of probes over a real data source (databases, APIs, log
|
|
6
|
-
* sets), selected by the 121M neuralOS engine and executed by each
|
|
7
|
-
* instance's Python bridge. This plugin wires four agent tools:
|
|
8
|
-
*
|
|
9
|
-
* neuralos_list_instances — what can be queried
|
|
10
|
-
* neuralos_ask — plain-English question -> verified digest
|
|
11
|
-
* neuralos_graph — relationship maps (overview/neighbors/connect)
|
|
12
|
-
* neuralos_admin — write probes, confirm='yes' interlocked
|
|
13
|
-
*
|
|
14
|
-
* The engine ships with the RTerm desktop bundle ({resourcesPath}/neuralos);
|
|
15
|
-
* the standalone backend auto-provisions ~/.cache/neuralos on first use
|
|
16
|
-
* (one ~36 MB fetch from HuggingFace, offline forever after).
|
|
17
|
-
*
|
|
18
|
-
* Config (settings.neuralos, or env NEURALOS_*):
|
|
19
|
-
* instancesDir — instances root (default ~/neuralos-instances)
|
|
20
|
-
* pythonBin — interpreter with pydantic for the bridges (default python3)
|
|
21
|
-
* engineBin / engineWeights — explicit engine override
|
|
22
|
-
* autoDownload — provision the shared cache when nothing local (default true)
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import { resolveConfig, listInstances, instanceDirFor, resolveEngine, engineSelect, executeProbe, graphProbe, adminProbe, truncate, defaultExec } from './neuralosEngine.mjs'
|
|
26
|
-
|
|
27
|
-
export { resolveConfig } from './neuralosEngine.mjs'
|
|
28
|
-
|
|
29
|
-
export function register(ctx) {
|
|
30
|
-
const cfg = resolveConfig(ctx)
|
|
31
|
-
const exec = defaultExec()
|
|
32
|
-
const log = (line) => {
|
|
33
|
-
try {
|
|
34
|
-
if (typeof ctx.log === 'function') ctx.log(`[neuralos] ${line}`)
|
|
35
|
-
} catch { /* logging must never break a tool */ }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const settings = (typeof ctx.getSettings === 'function' ? ctx.getSettings() : ctx.settings) || {}
|
|
39
|
-
if (settings.neuralos?.enabled === false) {
|
|
40
|
-
log('disabled in settings — not registering tools')
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
registerList(ctx, cfg, log)
|
|
45
|
-
registerAsk(ctx, cfg, exec, log)
|
|
46
|
-
registerGraph(ctx, cfg, exec, log)
|
|
47
|
-
registerAdmin(ctx, cfg, exec, log)
|
|
48
|
-
|
|
49
|
-
log('registered neuralos tools (list_instances, ask, graph, admin)')
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function registerList(ctx, cfg, log) {
|
|
53
|
-
ctx.registerTool({
|
|
54
|
-
name: 'neuralos_list_instances',
|
|
55
|
-
description:
|
|
56
|
-
'List the available neuralOS instances (on-device data agents). Each instance is a verified menu of probes over a real data source (a database, API, or file set). Use this first to see what can be queried.',
|
|
57
|
-
params: {},
|
|
58
|
-
handler: async () => {
|
|
59
|
-
const result = await listInstances(cfg)
|
|
60
|
-
log('list_instances')
|
|
61
|
-
return result
|
|
62
|
-
},
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function registerAsk(ctx, cfg, exec, log) {
|
|
67
|
-
ctx.registerTool({
|
|
68
|
-
name: 'neuralos_ask',
|
|
69
|
-
description:
|
|
70
|
-
'Ask a neuralOS instance a question in plain English. The on-device engine selects the probe, the instance bridge executes it against the real data source, and a verified digest comes back. Route data questions through this instead of querying the source directly.',
|
|
71
|
-
params: {
|
|
72
|
-
instance: { type: 'string', description: 'instance name from neuralos_list_instances' },
|
|
73
|
-
question: { type: 'string', description: 'the question in plain English, verbatim' },
|
|
74
|
-
},
|
|
75
|
-
handler: async (p) => {
|
|
76
|
-
const instance = String(p?.instance ?? '')
|
|
77
|
-
const question = String(p?.question ?? '')
|
|
78
|
-
const dir = await instanceDirFor(cfg, instance)
|
|
79
|
-
if (typeof dir !== 'string') return dir
|
|
80
|
-
const engine = await resolveEngine(cfg)
|
|
81
|
-
if (engine.error) return engine
|
|
82
|
-
const selection = await engineSelect(exec, engine, `${dir}/needle_menu.json`, question, { timeoutMs: cfg.timeoutMs })
|
|
83
|
-
if (selection.error) return { instance, question, ...selection }
|
|
84
|
-
const result = await executeProbe(exec, cfg, dir, selection.pick, selection.args, { timeoutMs: cfg.timeoutMs })
|
|
85
|
-
log(`ask ${instance} -> ${selection.pick} (conf ${selection.confidence})`)
|
|
86
|
-
return truncate({ instance, question, pick: selection.pick, confidence: selection.confidence, result })
|
|
87
|
-
},
|
|
88
|
-
})
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function registerGraph(ctx, cfg, exec, log) {
|
|
92
|
-
ctx.registerTool({
|
|
93
|
-
name: 'neuralos_graph',
|
|
94
|
-
description:
|
|
95
|
-
'Relationship questions over an instance: overview (the verified entity/edge map), neighbors (one-hop adjacency for a node fragment), or connect (path between two entities). Executes the graph probe directly.',
|
|
96
|
-
params: {
|
|
97
|
-
instance: { type: 'string', description: 'instance name' },
|
|
98
|
-
op: { type: 'string', description: 'overview | neighbors | connect' },
|
|
99
|
-
node: { type: 'string', description: 'node fragment for neighbors', optional: true },
|
|
100
|
-
a: { type: 'string', description: 'first entity for connect', optional: true },
|
|
101
|
-
b: { type: 'string', description: 'second entity for connect', optional: true },
|
|
102
|
-
},
|
|
103
|
-
handler: async (p) => {
|
|
104
|
-
const dir = await instanceDirFor(cfg, String(p?.instance ?? ''))
|
|
105
|
-
if (typeof dir !== 'string') return dir
|
|
106
|
-
const result = await graphProbe(exec, cfg, dir, { op: String(p?.op ?? 'overview'), node: p?.node, a: p?.a, b: p?.b })
|
|
107
|
-
log(`graph ${p?.op}`)
|
|
108
|
-
return truncate(result)
|
|
109
|
-
},
|
|
110
|
-
})
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function registerAdmin(ctx, cfg, exec, log) {
|
|
114
|
-
ctx.registerTool({
|
|
115
|
-
name: 'neuralos_admin',
|
|
116
|
-
description:
|
|
117
|
-
'Execute a WRITE/admin probe on an instance (boot a server, tag a resource, purge DNS). Destructive and reversible only per the instance design; the confirm parameter must be the literal string "yes" — that is the operator interlock. Read-only questions must use neuralos_ask instead.',
|
|
118
|
-
params: {
|
|
119
|
-
instance: { type: 'string', description: 'instance name' },
|
|
120
|
-
probe: { type: 'string', description: 'admin probe name, e.g. aws_power' },
|
|
121
|
-
args: { type: 'object', description: 'probe arguments as JSON', optional: true },
|
|
122
|
-
confirm: { type: 'string', description: 'must be the literal "yes"' },
|
|
123
|
-
},
|
|
124
|
-
handler: async (p) => {
|
|
125
|
-
if (p?.confirm !== 'yes') return { error: 'admin probes require confirm="yes" — restate what you intend to run and pass confirm="yes"' }
|
|
126
|
-
const dir = await instanceDirFor(cfg, String(p?.instance ?? ''))
|
|
127
|
-
if (typeof dir !== 'string') return dir
|
|
128
|
-
const result = await adminProbe(exec, cfg, dir, String(p?.probe ?? ''), p?.args ?? {})
|
|
129
|
-
log(`admin ${p?.probe} on ${p?.instance}`)
|
|
130
|
-
return truncate(result)
|
|
131
|
-
},
|
|
132
|
-
})
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function unregister() {
|
|
136
|
-
/* tools deregister with the plugin record; nothing to stop here */
|
|
137
|
-
}
|
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* neuralos — plugin extreme spec.
|
|
3
|
-
*
|
|
4
|
-
* Pure invariants, always run, no network and no engine required:
|
|
5
|
-
* config precedence, engine-name mapping, JSON recovery, instance
|
|
6
|
-
* discovery, engine selection against a FAKE engine script (node is the
|
|
7
|
-
* only dependency), probe execution against a FAKE python (the runner is
|
|
8
|
-
* exercised via an injected exec), the graph direct-execution rule, and
|
|
9
|
-
* the admin confirm interlock. The auto-download path is tested with an
|
|
10
|
-
* injected fetch that writes local files — CI never touches HuggingFace.
|
|
11
|
-
*
|
|
12
|
-
* Run: npx tsx plugins/neuralos/neuralos.extreme.spec.mts
|
|
13
|
-
*/
|
|
14
|
-
import { spawnSync } from 'node:child_process'
|
|
15
|
-
import { mkdtempSync, writeFileSync, mkdirSync, existsSync, chmodSync } from 'node:fs'
|
|
16
|
-
import { homedir, tmpdir } from 'node:os'
|
|
17
|
-
import { join } from 'node:path'
|
|
18
|
-
|
|
19
|
-
import {
|
|
20
|
-
HF_BASE,
|
|
21
|
-
adminProbe,
|
|
22
|
-
bundledEngineName,
|
|
23
|
-
defaultExec,
|
|
24
|
-
engineSelect,
|
|
25
|
-
ensureEngine,
|
|
26
|
-
executeProbe,
|
|
27
|
-
graphProbe,
|
|
28
|
-
listInstances,
|
|
29
|
-
parseJsonObject,
|
|
30
|
-
resolveConfig,
|
|
31
|
-
resolveEngine,
|
|
32
|
-
truncate,
|
|
33
|
-
} from './neuralosEngine.mjs'
|
|
34
|
-
import { register } from './index.mjs'
|
|
35
|
-
|
|
36
|
-
let pass = 0
|
|
37
|
-
let fail = 0
|
|
38
|
-
const failures: string[] = []
|
|
39
|
-
|
|
40
|
-
function ok(cond: unknown, label: string, note = ''): void {
|
|
41
|
-
if (cond) {
|
|
42
|
-
pass++
|
|
43
|
-
console.log(`PASS ${label}`)
|
|
44
|
-
} else {
|
|
45
|
-
fail++
|
|
46
|
-
failures.push(`${label}${note ? ` — ${note}` : ''}`)
|
|
47
|
-
console.log(`FAIL ${label}${note ? ` — ${note}` : ''}`)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
// 1. Config precedence: settings block > env > default; blank = unset.
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
{
|
|
55
|
-
const cfg = resolveConfig({ getSettings: () => ({ neuralos: { pythonBin: '/framework/python3.12', instancesDir: ' ' } }) }, {})
|
|
56
|
-
ok(cfg.pythonBin === '/framework/python3.12', 'settings block wins for pythonBin')
|
|
57
|
-
ok(cfg.instancesDir === join(homedir(), 'neuralos-instances'), 'blank settings value falls through to default', cfg.instancesDir)
|
|
58
|
-
|
|
59
|
-
const cfgEnv = resolveConfig({ getSettings: () => ({}) }, { NEURALOS_INSTANCES_DIR: '/custom/instances' })
|
|
60
|
-
ok(cfgEnv.instancesDir === '/custom/instances', 'env wins when settings are silent')
|
|
61
|
-
|
|
62
|
-
const cfgOff = resolveConfig({ getSettings: () => ({ neuralos: { autoDownload: false } }) }, {})
|
|
63
|
-
ok(cfgOff.autoDownload === false, 'autoDownload can be disabled in settings')
|
|
64
|
-
const cfgOn = resolveConfig({ getSettings: () => ({}) }, {})
|
|
65
|
-
ok(cfgOn.autoDownload === true, 'autoDownload defaults on')
|
|
66
|
-
}
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
// 2. Engine name mapping (macos-x64 has no published engine).
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
{
|
|
71
|
-
ok(bundledEngineName('darwin', 'arm64') === 'engine-macos-arm64', 'darwin/arm64 maps to engine-macos-arm64')
|
|
72
|
-
ok(bundledEngineName('linux', 'x64') === 'engine-linux-x86_64', 'linux/x64 maps to engine-linux-x86_64')
|
|
73
|
-
ok(bundledEngineName('linux', 'arm64') === 'engine-linux-arm64', 'linux/arm64 maps to engine-linux-arm64')
|
|
74
|
-
ok(bundledEngineName('win32', 'x64') === 'engine-windows-x86_64.exe', 'win32/x64 maps to engine-windows-x86_64.exe')
|
|
75
|
-
ok(bundledEngineName('darwin', 'x64') === null, 'darwin/x64 has no published engine')
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ---------------------------------------------------------------------------
|
|
79
|
-
// 3. JSON recovery from noisy engine output.
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
{
|
|
82
|
-
const parsed = parseJsonObject('noise {"function_calls":[{"name":"p"}], "confidence": 0.9} trailing')
|
|
83
|
-
ok(parsed?.function_calls?.[0]?.name === 'p', 'parseJsonObject recovers JSON embedded in noise')
|
|
84
|
-
ok(parseJsonObject('not json') === null, 'parseJsonObject returns null for garbage')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// ---------------------------------------------------------------------------
|
|
88
|
-
// 4. Instance discovery (fake instances root in a temp dir).
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
|
-
{
|
|
91
|
-
const root = mkdtempSync(join(tmpdir(), 'neuralos-spec-'))
|
|
92
|
-
mkdirSync(join(root, 'chinook'))
|
|
93
|
-
writeFileSync(join(root, 'chinook', 'needle_menu.json'), JSON.stringify([{ name: 'p1' }, { name: 'p2' }]))
|
|
94
|
-
mkdirSync(join(root, 'not-an-instance'))
|
|
95
|
-
|
|
96
|
-
const cfg = resolveConfig({}, { NEURALOS_INSTANCES_DIR: root })
|
|
97
|
-
const listed = await listInstances(cfg)
|
|
98
|
-
ok(
|
|
99
|
-
Array.isArray(listed) && listed.length === 1 && listed[0].name === 'chinook' && listed[0].probeCount === 2,
|
|
100
|
-
'listInstances finds only dirs with a needle_menu.json',
|
|
101
|
-
JSON.stringify(listed),
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
const missing = await listInstances(cfg, { readdir: async () => { throw new Error('ENOENT') }, readFile: async () => '' })
|
|
105
|
-
ok(
|
|
106
|
-
(missing as { error?: string }).error?.includes('no instances directory'),
|
|
107
|
-
'listInstances reports a missing root as data',
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// ---------------------------------------------------------------------------
|
|
112
|
-
// 5. Engine selection against a FAKE engine (a node script).
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
{
|
|
115
|
-
const dir = mkdtempSync(join(tmpdir(), 'neuralos-engine-'))
|
|
116
|
-
const fakeEngine = join(dir, 'fake-engine.mjs')
|
|
117
|
-
writeFileSync(
|
|
118
|
-
fakeEngine,
|
|
119
|
-
`const args = process.argv.slice(2)
|
|
120
|
-
const qi = args.indexOf('--prompt')
|
|
121
|
-
const q = qi >= 0 ? args[qi + 1] : ''
|
|
122
|
-
if (q === 'refuse me') { console.log(JSON.stringify({ function_calls: [] })) }
|
|
123
|
-
else if (q === 'garbage') { console.log('not json at all') }
|
|
124
|
-
else if (q === 'crash') { process.stderr.write('boom'); process.exit(1) }
|
|
125
|
-
else { console.log(JSON.stringify({ function_calls: [{ name: 'transactions_count', arguments: { limit: 5 } }], confidence: 0.98 })) }
|
|
126
|
-
`,
|
|
127
|
-
)
|
|
128
|
-
const runEngine = (q: string) =>
|
|
129
|
-
engineSelect(
|
|
130
|
-
(cmd: string, args: string[]) => {
|
|
131
|
-
void cmd
|
|
132
|
-
const r = spawnSync(process.execPath, [fakeEngine, ...args.slice(2)], { encoding: 'utf8' })
|
|
133
|
-
return Promise.resolve({ ok: r.status === 0, code: r.status ?? 1, stdout: r.stdout ?? '', stderr: r.stderr ?? '' })
|
|
134
|
-
},
|
|
135
|
-
{ engineBin: fakeEngine, engineWeights: join(dir, 'weights.cact') },
|
|
136
|
-
'/i/needle_menu.json',
|
|
137
|
-
q,
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
const hit = await runEngine('count invoices')
|
|
141
|
-
ok(hit.pick === 'transactions_count' && hit.confidence === 0.98, 'engineSelect parses the selection with confidence', JSON.stringify(hit))
|
|
142
|
-
const refused = await runEngine('refuse me')
|
|
143
|
-
ok((refused as { error?: string }).error === 'engine refused the question (no call selected)', 'refusal is data, not an exception')
|
|
144
|
-
const garbage = await runEngine('garbage')
|
|
145
|
-
ok((garbage as { error?: string }).error?.includes('engine output not JSON'), 'non-JSON engine output is data')
|
|
146
|
-
const crash = await runEngine('crash')
|
|
147
|
-
ok((crash as { error?: string }).error === 'engine exited 1: boom', 'engine crash with empty stdout is data')
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// ---------------------------------------------------------------------------
|
|
151
|
-
// 6. Probe execution via an injected exec (the real path spawns python).
|
|
152
|
-
// ---------------------------------------------------------------------------
|
|
153
|
-
{
|
|
154
|
-
const cfg = resolveConfig({}, { NEURALOS_PYTHON: '/framework/python3.12' })
|
|
155
|
-
const seen: Array<{ cmd: string; args: string[]; env: Record<string, string> }> = []
|
|
156
|
-
const exec = (cmd: string, args: string[], opts: { env?: Record<string, string> }) => {
|
|
157
|
-
seen.push({ cmd, args, env: opts.env ?? {} })
|
|
158
|
-
return Promise.resolve({ ok: true, code: 0, stdout: '{"count": 42}', stderr: '' })
|
|
159
|
-
}
|
|
160
|
-
const result = await executeProbe(exec, cfg, '/i', 'transactions_count', { limit: 5 })
|
|
161
|
-
ok(result.count === 42, 'executeProbe returns the parsed digest')
|
|
162
|
-
ok(seen[0].cmd === '/framework/python3.12' && seen[0].args[0] === '-c', 'executeProbe spawns the configured python with the runner')
|
|
163
|
-
ok(
|
|
164
|
-
seen[0].env.NEURALOS_INSTANCE_DIR === '/i' && seen[0].env.NEURALOS_PROBE === 'transactions_count' && seen[0].env.NEURALOS_ARGS === '{"limit":5}',
|
|
165
|
-
'probe name and args travel through the environment, never the command line',
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
const failed = await executeProbe((() => Promise.resolve({ ok: false, code: 1, stdout: '', stderr: 'boom' })) as never, cfg, '/i', 'p', {})
|
|
169
|
-
ok((failed as { error?: string }).error === 'probe exited 1: boom', 'probe failure surfaces as data')
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// ---------------------------------------------------------------------------
|
|
173
|
-
// 7. Graph direct execution + admin interlock.
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
{
|
|
176
|
-
const dir = mkdtempSync(join(tmpdir(), 'neuralos-graph-'))
|
|
177
|
-
writeFileSync(
|
|
178
|
-
join(dir, 'needle_menu.json'),
|
|
179
|
-
JSON.stringify([{ name: 'cb_graph_overview' }, { name: 'cb_graph_neighbors' }, { name: 'transactions_count' }]),
|
|
180
|
-
)
|
|
181
|
-
const cfg = resolveConfig({}, {})
|
|
182
|
-
const exec = () => Promise.resolve({ ok: true, code: 0, stdout: '{"nodes":[]}', stderr: '' })
|
|
183
|
-
|
|
184
|
-
const overview = await graphProbe(exec, cfg, dir, { op: 'overview' })
|
|
185
|
-
ok((overview as { probe?: string }).probe === 'cb_graph_overview' && (overview as { nodes?: unknown[] }).nodes?.length === 0, 'graph overview executes the probe directly by name')
|
|
186
|
-
const neighbors = await graphProbe(exec, cfg, dir, { op: 'neighbors', node: '4001234567' })
|
|
187
|
-
ok((neighbors as { probe?: string }).probe === 'cb_graph_neighbors', 'graph neighbors executes by name (numeric fragments bypass engine grounding)')
|
|
188
|
-
const missingOp = await graphProbe(exec, cfg, dir, { op: 'connect', a: 'x', b: 'y' })
|
|
189
|
-
ok(
|
|
190
|
-
(missingOp as { error?: string }).error === 'this instance has no connect graph probe' &&
|
|
191
|
-
Array.isArray((missingOp as { available_graph_probes?: string[] }).available_graph_probes),
|
|
192
|
-
'a missing graph op lists what exists instead of guessing',
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
const admin = await adminProbe(exec, cfg, dir, 'aws_tag', { confirm: 'yes' })
|
|
196
|
-
ok(admin.admin === true && admin.probe === 'aws_tag', 'admin wraps the result with the probe name and admin flag')
|
|
197
|
-
const badName = await adminProbe(exec, cfg, dir, 'rm -rf', {})
|
|
198
|
-
ok((badName as { error?: string }).error === 'invalid probe name: rm -rf', 'admin rejects probe names that are not identifiers')
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// ---------------------------------------------------------------------------
|
|
202
|
-
// 8. Engine resolution: settings override wins; auto-download provisions the
|
|
203
|
-
// cache via an injected fetch (no network in CI); idempotent on re-run.
|
|
204
|
-
// ---------------------------------------------------------------------------
|
|
205
|
-
{
|
|
206
|
-
const explicitDir = mkdtempSync(join(tmpdir(), 'neuralos-explicit-'))
|
|
207
|
-
writeFileSync(join(explicitDir, 'needle'), 'fake-engine')
|
|
208
|
-
writeFileSync(join(explicitDir, 'needle3.cact'), 'fake-weights')
|
|
209
|
-
const cacheAlt = mkdtempSync(join(tmpdir(), 'neuralos-cache-alt-'))
|
|
210
|
-
writeFileSync(join(cacheAlt, 'engine-macos-arm64'), 'cache-engine')
|
|
211
|
-
writeFileSync(join(cacheAlt, 'needle3.cact'), 'cache-weights')
|
|
212
|
-
const explicit = await resolveEngine(
|
|
213
|
-
resolveConfig({ getSettings: () => ({ neuralos: { engineBin: join(explicitDir, 'needle'), engineWeights: join(explicitDir, 'needle3.cact'), cacheDir: cacheAlt } }) }, {}),
|
|
214
|
-
{ platform: 'darwin', arch: 'arm64' },
|
|
215
|
-
)
|
|
216
|
-
ok(explicit.engineBin === join(explicitDir, 'needle'), 'explicit settings engine wins over a populated cache')
|
|
217
|
-
|
|
218
|
-
const cache = mkdtempSync(join(tmpdir(), 'neuralos-cache-'))
|
|
219
|
-
const downloads: string[] = []
|
|
220
|
-
const fetchImpl = async (url: string, target: string) => {
|
|
221
|
-
downloads.push(url)
|
|
222
|
-
writeFileSync(target, `fake:${url}`)
|
|
223
|
-
}
|
|
224
|
-
const cfg = resolveConfig({ getSettings: () => ({}) }, { NEURALOS_INSTANCES_DIR: mkdtempSync(join(tmpdir(), 'neuralos-none-')), NEURALOS_CACHE_DIR: cache })
|
|
225
|
-
const first = await ensureEngine({ platform: 'darwin', arch: 'arm64', cacheDir: cache, fetchImpl })
|
|
226
|
-
ok(first.engineBin === join(cache, 'engine-macos-arm64') && first.engineWeights === join(cache, 'needle3.cact'), 'ensureEngine provisions weights + per-arch engine into the cache', JSON.stringify(first))
|
|
227
|
-
ok(downloads.length === 2 && downloads[0] === `${HF_BASE}/needle3.cact` && downloads[1].includes('macos-arm64/needle'), 'downloads come from the public Cactus-Compute/needle3 URLs')
|
|
228
|
-
ok(existsSync(join(cache, 'engine-macos-arm64')), 'the engine file exists on disk after provisioning')
|
|
229
|
-
|
|
230
|
-
const second = await ensureEngine({ platform: 'darwin', arch: 'arm64', cacheDir: cache, fetchImpl })
|
|
231
|
-
ok(second.engineBin === first.engineBin && downloads.length === 2, 'provisioning is idempotent — no re-download when cached')
|
|
232
|
-
|
|
233
|
-
// resolution now finds the cache without any download
|
|
234
|
-
const resolved = await resolveEngine(cfg, { platform: 'darwin', arch: 'arm64' })
|
|
235
|
-
ok(resolved.engineBin === join(cache, 'engine-macos-arm64'), 'resolveEngine picks the provisioned cache copy')
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// ---------------------------------------------------------------------------
|
|
239
|
-
// 9. Tool registration + the admin confirm interlock at the tool layer.
|
|
240
|
-
// ---------------------------------------------------------------------------
|
|
241
|
-
{
|
|
242
|
-
const registered: Array<{ name: string; params: Record<string, unknown>; handler: (args: unknown) => Promise<unknown> }> = []
|
|
243
|
-
const logged: string[] = []
|
|
244
|
-
register({
|
|
245
|
-
registerTool: (t) => registered.push(t as never),
|
|
246
|
-
log: (l: string) => logged.push(l),
|
|
247
|
-
getSettings: () => ({}),
|
|
248
|
-
})
|
|
249
|
-
ok(
|
|
250
|
-
registered.map((t) => t.name).sort().join(',') === 'neuralos_admin,neuralos_ask,neuralos_graph,neuralos_list_instances',
|
|
251
|
-
'register() wires exactly the four neuralos tools',
|
|
252
|
-
)
|
|
253
|
-
const admin = registered.find((t) => t.name === 'neuralos_admin')
|
|
254
|
-
const denied = (await admin.handler({ instance: 'aws', probe: 'aws_power', confirm: 'no' })) as { error?: string }
|
|
255
|
-
ok(denied.error?.includes('confirm="yes"'), 'the tool layer refuses admin without confirm="yes"')
|
|
256
|
-
const unknown = registered.find((t) => t.name === 'neuralos_ask')
|
|
257
|
-
const answered = (await unknown.handler({ instance: 'does-not-exist', question: 'x' })) as { error?: string }
|
|
258
|
-
ok(answered.error?.includes("no instance named 'does-not-exist'"), 'ask answers an unknown instance as data')
|
|
259
|
-
ok(logged.some((l) => l.includes('registered neuralos tools')), 'the plugin announces registration through ctx.log')
|
|
260
|
-
|
|
261
|
-
// the enabled=false settings gate: the Settings panel switch is honest
|
|
262
|
-
const off: Array<{ name: string }> = []
|
|
263
|
-
register({
|
|
264
|
-
registerTool: (t) => off.push(t as never),
|
|
265
|
-
log: () => {},
|
|
266
|
-
getSettings: () => ({ neuralos: { enabled: false } }),
|
|
267
|
-
})
|
|
268
|
-
ok(off.length === 0, 'enabled=false registers zero tools')
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// ---------------------------------------------------------------------------
|
|
272
|
-
// 10. Digest truncation keeps tool results bounded.
|
|
273
|
-
// ---------------------------------------------------------------------------
|
|
274
|
-
{
|
|
275
|
-
const big = { rows: 'x'.repeat(20_000) }
|
|
276
|
-
const text = truncate(big, 6000)
|
|
277
|
-
ok(text.length < 21_000 && text.includes('truncated at 6000 chars'), 'truncate caps large digests with an honest marker')
|
|
278
|
-
ok(truncate('small') === 'small', 'small digests pass through untouched')
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// ---------------------------------------------------------------------------
|
|
282
|
-
// 11. The REAL defaultExec forwards opts.env (probe transport) to the child
|
|
283
|
-
// while keeping the parent environment (PATH etc.) intact.
|
|
284
|
-
// ---------------------------------------------------------------------------
|
|
285
|
-
{
|
|
286
|
-
const exec = defaultExec()
|
|
287
|
-
const out = await exec(process.execPath, ['-e', 'console.log(process.env.NEURALOS_SPEC_PROBE ?? "missing")'], { env: { NEURALOS_SPEC_PROBE: 'forwarded' } })
|
|
288
|
-
ok(out.code === 0 && out.stdout.trim() === 'forwarded', 'defaultExec forwards opts.env to the child process')
|
|
289
|
-
const plain = await exec(process.execPath, ['-e', 'console.log(Boolean(process.env.PATH))'])
|
|
290
|
-
ok(plain.code === 0 && plain.stdout.trim() === 'true', 'defaultExec keeps the parent environment for plain calls')
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// ---------------------------------------------------------------------------
|
|
294
|
-
// 12. The lost-exec-bit family (v3.9.2). curl -o / electron-builder
|
|
295
|
-
// extraResources ship engines with mode 0644; spawning them dies with
|
|
296
|
-
// EACCES and an EMPTY stderr. Resolution must check executability, not
|
|
297
|
-
// existence — a broken bundle must never shadow a runnable engine.
|
|
298
|
-
// ---------------------------------------------------------------------------
|
|
299
|
-
{
|
|
300
|
-
// 12a. A 0644 bundled engine is skipped, and the next candidate wins.
|
|
301
|
-
const root = mkdtempSync(join(tmpdir(), 'neuralos-execbit-'))
|
|
302
|
-
const resources = join(root, 'resources')
|
|
303
|
-
const cache = join(root, 'cache')
|
|
304
|
-
const instances = join(root, 'instances')
|
|
305
|
-
mkdirSync(join(resources, 'neuralos'), { recursive: true })
|
|
306
|
-
mkdirSync(cache, { recursive: true })
|
|
307
|
-
mkdirSync(join(instances, 'engine'), { recursive: true })
|
|
308
|
-
// bundled copy: present but NOT executable (the v3.9.1 bug)
|
|
309
|
-
writeFileSync(join(resources, 'neuralos', 'engine-macos-arm64'), 'bundled-broken')
|
|
310
|
-
writeFileSync(join(resources, 'neuralos', 'needle3.cact'), 'bundled-weights')
|
|
311
|
-
chmodSync(join(resources, 'neuralos', 'engine-macos-arm64'), 0o644)
|
|
312
|
-
// fleet-convention copy: executable
|
|
313
|
-
writeFileSync(join(instances, 'engine', 'needle'), '#!/bin/sh\necho ok')
|
|
314
|
-
writeFileSync(join(instances, 'engine', 'needle3.cact'), 'fleet-weights')
|
|
315
|
-
chmodSync(join(instances, 'engine', 'needle'), 0o755)
|
|
316
|
-
|
|
317
|
-
const cfg = resolveConfig({ getSettings: () => ({}) }, { NEURALOS_INSTANCES_DIR: instances, NEURALOS_CACHE_DIR: cache, NEURALOS_AUTO_DOWNLOAD: '0' })
|
|
318
|
-
const resolved = await resolveEngine(cfg, { platform: 'darwin', arch: 'arm64', resourcesPath: resources })
|
|
319
|
-
ok(resolved.engineBin === join(instances, 'engine', 'needle'), 'a 0644 bundled engine is skipped for the runnable fleet engine', JSON.stringify(resolved))
|
|
320
|
-
|
|
321
|
-
// 12b. chmod recovery: a 0644 engine we OWN is fixed in place, not skipped.
|
|
322
|
-
const ownedDir = mkdtempSync(join(tmpdir(), 'neuralos-owned-'))
|
|
323
|
-
writeFileSync(join(ownedDir, 'engine-macos-arm64'), 'owned')
|
|
324
|
-
writeFileSync(join(ownedDir, 'needle3.cact'), 'owned-weights')
|
|
325
|
-
chmodSync(join(ownedDir, 'engine-macos-arm64'), 0o644)
|
|
326
|
-
const cfgOwned = resolveConfig({ getSettings: () => ({}) }, { NEURALOS_INSTANCES_DIR: join(root, 'nope'), NEURALOS_CACHE_DIR: ownedDir, NEURALOS_AUTO_DOWNLOAD: '0' })
|
|
327
|
-
const recovered = await resolveEngine(cfgOwned, { platform: 'darwin', arch: 'arm64' })
|
|
328
|
-
ok(recovered.engineBin === join(ownedDir, 'engine-macos-arm64'), 'a 0644 engine we own is chmod-recovered instead of skipped', JSON.stringify(recovered))
|
|
329
|
-
|
|
330
|
-
// 12c. EACCES at spawn time is named, with the fix in the message.
|
|
331
|
-
const denied = await engineSelect(
|
|
332
|
-
() => Promise.resolve({ ok: false, code: 'EACCES', stdout: '', stderr: '' }),
|
|
333
|
-
{ engineBin: '/broken/engine', engineWeights: '/w' },
|
|
334
|
-
'/m',
|
|
335
|
-
'q',
|
|
336
|
-
)
|
|
337
|
-
ok(
|
|
338
|
-
(denied as { error?: string }).error?.includes('engine not executable') && (denied as { error?: string }).error?.includes('chmod +x'),
|
|
339
|
-
'EACCES spawn failure names the fix (chmod +x / engineBin override)',
|
|
340
|
-
JSON.stringify(denied),
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
// 12d. A non-EACCES crash keeps the original shape.
|
|
344
|
-
const crash = await engineSelect(
|
|
345
|
-
() => Promise.resolve({ ok: false, code: 1, stdout: '', stderr: 'boom' }),
|
|
346
|
-
{ engineBin: '/e', engineWeights: '/w' },
|
|
347
|
-
'/m',
|
|
348
|
-
'q',
|
|
349
|
-
)
|
|
350
|
-
ok((crash as { error?: string }).error === 'engine exited 1: boom', 'non-EACCES crashes keep the engine exited N: stderr shape')
|
|
351
|
-
|
|
352
|
-
// 12e. defaultExec preserves a real EACCES code from the OS.
|
|
353
|
-
const realExec = defaultExec()
|
|
354
|
-
const noExecFile = join(root, 'not-executable')
|
|
355
|
-
writeFileSync(noExecFile, 'x')
|
|
356
|
-
chmodSync(noExecFile, 0o644)
|
|
357
|
-
const eacc = await realExec(noExecFile, [])
|
|
358
|
-
ok(eacc.code === 'EACCES', 'defaultExec surfaces EACCES as the code (not a generic 1)', JSON.stringify(eacc.code))
|
|
359
|
-
|
|
360
|
-
// 12f. defaultExec reports timeout kills honestly.
|
|
361
|
-
const slow = await realExec(process.execPath, ['-e', 'setTimeout(() => {}, 30000)'], { timeoutMs: 300 })
|
|
362
|
-
ok(String(slow.code).includes('killed') || String(slow.code).includes('ETIMEDOUT'), 'a timeout kill is reported as killed/ETIMEDOUT, not exit 1', JSON.stringify(slow.code))
|
|
363
|
-
|
|
364
|
-
// 12g. truncate(undefined) must not throw (JSON.stringify(undefined) is undefined).
|
|
365
|
-
const undef = truncate(undefined as never)
|
|
366
|
-
ok(undef === 'null', 'truncate(undefined) renders honestly instead of crashing', String(undef))
|
|
367
|
-
|
|
368
|
-
// 12h. A corrupt menu is data for graphProbe, never an exception.
|
|
369
|
-
const corruptDir = mkdtempSync(join(tmpdir(), 'neuralos-corrupt-'))
|
|
370
|
-
writeFileSync(join(corruptDir, 'needle_menu.json'), 'NOT JSON {{{')
|
|
371
|
-
const corrupt = await graphProbe(() => Promise.resolve({ ok: true, code: 0, stdout: '{}', stderr: '' }), resolveConfig({}, {}), corruptDir, { op: 'overview' })
|
|
372
|
-
ok((corrupt as { error?: string }).error?.includes('instance menu unreadable'), 'graphProbe reports a corrupt menu as data')
|
|
373
|
-
|
|
374
|
-
// 12i. A menu that is valid JSON but not an array is also rejected as data.
|
|
375
|
-
const nonArrayDir = mkdtempSync(join(tmpdir(), 'neuralos-nonarray-'))
|
|
376
|
-
writeFileSync(join(nonArrayDir, 'needle_menu.json'), '{"not":"an array"}')
|
|
377
|
-
const nonArray = await graphProbe(() => Promise.resolve({ ok: true, code: 0, stdout: '{}', stderr: '' }), resolveConfig({}, {}), nonArrayDir, { op: 'overview' })
|
|
378
|
-
ok((nonArray as { error?: string }).error?.includes('instance menu unreadable'), 'a non-array menu is rejected as data')
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
console.log(`\n${pass} passed, ${fail} failed`)
|
|
382
|
-
if (fail > 0) {
|
|
383
|
-
console.log(`FAILURES:\n${failures.map((f) => ` - ${f}`).join('\n')}`)
|
|
384
|
-
process.exit(1)
|
|
385
|
-
}
|
|
386
|
-
console.log('neuralos: ALL TESTS PASSED')
|