neuralos 3.3.6 → 3.3.8

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/bin/gybackend.cjs CHANGED
@@ -377393,6 +377393,7 @@ function pickBackendSnapshot(raw) {
377393
377393
  nats: raw.nats,
377394
377394
  synapse: raw.synapse,
377395
377395
  numbat: raw.numbat,
377396
+ monid: raw.monid,
377396
377397
  gateway: raw.gateway,
377397
377398
  layout: raw.layout,
377398
377399
  recursionLimit: raw.recursionLimit,
@@ -377476,6 +377477,7 @@ function normalizeBackendSettings(settings) {
377476
377477
  next.nats = normalizeNatsSettings(next.nats);
377477
377478
  next.synapse = normalizeSynapseSettings(next.synapse);
377478
377479
  next.numbat = normalizeNumbatSettings(next.numbat);
377480
+ next.monid = normalizeMonidSettings(next.monid);
377479
377481
  next.schemaVersion = BACKEND_SETTINGS_SCHEMA_VERSION;
377480
377482
  return next;
377481
377483
  }
@@ -377730,6 +377732,16 @@ function normalizeNumbatSettings(raw) {
377730
377732
  ...minSeverity ? { minSeverity } : {}
377731
377733
  };
377732
377734
  }
377735
+ function normalizeMonidSettings(raw) {
377736
+ const src = isObject5(raw) ? raw : {};
377737
+ const binaryPath = typeof src.binaryPath === "string" && src.binaryPath.trim() ? src.binaryPath.trim() : void 0;
377738
+ const keyLabel = typeof src.keyLabel === "string" && src.keyLabel.trim() ? src.keyLabel.trim() : void 0;
377739
+ return {
377740
+ enabled: src.enabled !== false,
377741
+ ...binaryPath ? { binaryPath } : {},
377742
+ ...keyLabel ? { keyLabel } : {}
377743
+ };
377744
+ }
377733
377745
  function migrateBackendToV3(settings) {
377734
377746
  const next = { ...settings };
377735
377747
  delete next.language;
@@ -395968,8 +395980,29 @@ var ResourceMonitorService = class {
395968
395980
  sessions = /* @__PURE__ */ new Map();
395969
395981
  sourceKeyByTerminalId = /* @__PURE__ */ new Map();
395970
395982
  publisher = null;
395983
+ /** Extra listeners (UI window registry, triggers, observability). Fan-out so
395984
+ * later setPublisher callers cannot steal snapshots from earlier ones. */
395985
+ extraPublishers = [];
395971
395986
  setPublisher(publisher) {
395972
- this.publisher = publisher;
395987
+ if (!this.publisher) {
395988
+ this.publisher = publisher;
395989
+ return;
395990
+ }
395991
+ if (this.publisher === publisher) return;
395992
+ if (!this.extraPublishers.includes(publisher)) {
395993
+ this.extraPublishers.push(publisher);
395994
+ }
395995
+ }
395996
+ emitSnapshot(channel, data) {
395997
+ const pubs = [this.publisher, ...this.extraPublishers].filter(
395998
+ (p) => typeof p === "function"
395999
+ );
396000
+ for (const pub of pubs) {
396001
+ try {
396002
+ pub(channel, data);
396003
+ } catch {
396004
+ }
396005
+ }
395973
396006
  }
395974
396007
  start(terminalId, ownerId = "default", intervalMs) {
395975
396008
  const normalizedTerminalId = String(terminalId || "").trim();
@@ -396191,14 +396224,12 @@ var ResourceMonitorService = class {
396191
396224
  const sourceTerminalId = session.sourceTerminalId || terminalIds[0];
396192
396225
  const snapshot = await this.collectSnapshot(sourceTerminalId);
396193
396226
  session.lastCollectAt = Date.now();
396194
- if (this.publisher) {
396195
- terminalIds.forEach((terminalId) => {
396196
- this.publisher?.(
396197
- "monitor:snapshot",
396198
- terminalId === snapshot.terminalId ? snapshot : { ...snapshot, terminalId }
396199
- );
396200
- });
396201
- }
396227
+ terminalIds.forEach((terminalId) => {
396228
+ this.emitSnapshot(
396229
+ "monitor:snapshot",
396230
+ terminalId === snapshot.terminalId ? snapshot : { ...snapshot, terminalId }
396231
+ );
396232
+ });
396202
396233
  } finally {
396203
396234
  session.inFlight = false;
396204
396235
  }
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuralos",
3
- "version": "3.3.6",
3
+ "version": "3.3.8",
4
4
  "description": "AI-native terminal & agentic-AI operations platform for Forward Deployed Engineers & SREs: AIOps closed-loop remediation, AI SRE, self-healing infrastructure, runbook automation, ChatOps; executes over SSH/WinRM/serial under policy with tamper-evident audit.",
5
5
  "keywords": [
6
6
  "forward-deployed-engineer",
@@ -0,0 +1,136 @@
1
+ /**
2
+ * monid-bridge — thin wrapper around the official Monid CLI.
3
+ *
4
+ * Settings → Plugins → Monid (settings.monid):
5
+ * enabled, binaryPath, keyLabel
6
+ * The API key is NEVER stored in settings.json. Saving a key in the UI runs
7
+ * `monid keys add -k <key> -l <label>` (see applyMonidApiKey).
8
+ *
9
+ * Agent tools: monid_health, monid_discover, monid_run.
10
+ */
11
+
12
+ import { createRequire } from 'node:module'
13
+
14
+ const require = createRequire(import.meta.url)
15
+
16
+ export function resolveConfig(ctx = {}, env = process.env) {
17
+ const s = (typeof ctx.getSettings === 'function' ? ctx.getSettings() : ctx.settings) || {}
18
+ const b = s.monid || {}
19
+ return {
20
+ enabled: b.enabled !== false,
21
+ binaryPath: b.binaryPath || env.MONID_BIN || 'monid',
22
+ keyLabel: b.keyLabel || 'rterm',
23
+ }
24
+ }
25
+
26
+ /** Build argv for a monid subcommand. Pure. */
27
+ export function buildMonidArgv(sub, params = {}) {
28
+ switch (sub) {
29
+ case 'version':
30
+ return ['--version']
31
+ case 'keys-list':
32
+ return ['keys', 'list']
33
+ case 'discover': {
34
+ const q = String(params.query || '').trim()
35
+ if (!q) throw new Error('monid_discover needs a query')
36
+ return ['discover', q]
37
+ }
38
+ case 'run': {
39
+ const tool = String(params.tool || '').trim()
40
+ if (!tool) throw new Error('monid_run needs a tool name')
41
+ const argv = ['run', tool]
42
+ if (params.input != null && String(params.input).trim()) {
43
+ argv.push('--input', String(params.input))
44
+ }
45
+ return argv
46
+ }
47
+ default:
48
+ throw new Error(`unknown monid subcommand: ${sub}`)
49
+ }
50
+ }
51
+
52
+ async function runMonid(ctx, cfg, argv) {
53
+ if (typeof ctx.runCommand === 'function') {
54
+ const cmdline = [cfg.binaryPath, ...argv].map((a) => (/\s/.test(a) ? `"${a}"` : a)).join(' ')
55
+ return ctx.runCommand({ command: cmdline })
56
+ }
57
+ if (typeof ctx.exec === 'function') {
58
+ const cmdline = [cfg.binaryPath, ...argv].map((a) => (/\s/.test(a) ? `"${a}"` : a)).join(' ')
59
+ return ctx.exec(cmdline)
60
+ }
61
+ const { execFile } = require('node:child_process')
62
+ return new Promise((resolve) => {
63
+ execFile(cfg.binaryPath, argv, { timeout: 120000, env: { ...process.env, NO_COLOR: '1' } }, (err, stdout, stderr) => {
64
+ resolve({
65
+ ok: !err,
66
+ exitCode: err?.code ?? 0,
67
+ stdout: String(stdout ?? ''),
68
+ stderr: String(stderr ?? ''),
69
+ error: err ? String(err.message) : undefined,
70
+ })
71
+ })
72
+ })
73
+ }
74
+
75
+ async function guarded(fn, log) {
76
+ try {
77
+ return await fn()
78
+ } catch (e) {
79
+ const msg = e?.message ?? String(e)
80
+ log?.(`[monid] ${msg}`)
81
+ return {
82
+ error: msg,
83
+ hint: 'Install @monid-ai/cli (`npm i -g @monid-ai/cli`) and paste an API key in Settings → Plugins → Monid (https://app.monid.ai/access/api-keys).',
84
+ }
85
+ }
86
+ }
87
+
88
+ export function register(ctx) {
89
+ const { registerTool, log } = ctx
90
+ const cfg = resolveConfig(ctx)
91
+ if (!cfg.enabled) {
92
+ log?.('[monid] disabled in settings.monid.enabled')
93
+ return
94
+ }
95
+
96
+ registerTool({
97
+ name: 'monid_health',
98
+ description: 'Check the Monid CLI is installed and list configured key labels (never the secret).',
99
+ params: {},
100
+ handler: async () =>
101
+ guarded(async () => {
102
+ const ver = await runMonid(ctx, cfg, buildMonidArgv('version'))
103
+ const keys = await runMonid(ctx, cfg, buildMonidArgv('keys-list'))
104
+ return { binaryPath: cfg.binaryPath, keyLabel: cfg.keyLabel, version: ver, keys }
105
+ }, log),
106
+ })
107
+
108
+ registerTool({
109
+ name: 'monid_discover',
110
+ description: 'Discover Monid tools/endpoints for a task (web data, enrichment, social, company/people). Use before writing a scraper or when a generic fetch is not enough.',
111
+ params: {
112
+ query: { type: 'string', description: 'What you need (e.g. "linkedin company employees")' },
113
+ },
114
+ handler: async (p) =>
115
+ guarded(async () => {
116
+ const argv = buildMonidArgv('discover', p)
117
+ const r = await runMonid(ctx, cfg, argv)
118
+ return { argv: [cfg.binaryPath, ...argv], ...r }
119
+ }, log),
120
+ })
121
+
122
+ registerTool({
123
+ name: 'monid_run',
124
+ description: 'Run a named Monid tool with optional JSON/text input. Prefer this over hand-rolled scrapers when discover returned a tool.',
125
+ params: {
126
+ tool: { type: 'string', description: 'Tool name from monid_discover' },
127
+ input: { type: 'string', description: 'JSON or text input for the tool', optional: true },
128
+ },
129
+ handler: async (p) =>
130
+ guarded(async () => {
131
+ const argv = buildMonidArgv('run', p)
132
+ const r = await runMonid(ctx, cfg, argv)
133
+ return { argv: [cfg.binaryPath, ...argv], ...r }
134
+ }, log),
135
+ })
136
+ }
@@ -0,0 +1,27 @@
1
+ import { buildMonidArgv, resolveConfig } from './index.mjs'
2
+
3
+ const assert = (c, m) => {
4
+ if (!c) throw new Error(`assert failed: ${m}`)
5
+ }
6
+
7
+ assert(resolveConfig({ settings: {} }).enabled === true, 'enabled default')
8
+ assert(resolveConfig({ settings: { monid: { enabled: false } } }).enabled === false, 'disabled')
9
+ assert(resolveConfig({ settings: { monid: { binaryPath: '/opt/monid' } } }).binaryPath === '/opt/monid', 'bin')
10
+ assert(resolveConfig({}, { MONID_BIN: 'custom-monid' }).binaryPath === 'custom-monid', 'env bin')
11
+
12
+ assert(buildMonidArgv('version').join(' ') === '--version', 'version')
13
+ assert(buildMonidArgv('keys-list').join(' ') === 'keys list', 'keys list')
14
+ assert(buildMonidArgv('discover', { query: 'company news' }).join(' ') === 'discover company news', 'discover')
15
+ assert(buildMonidArgv('run', { tool: 'foo', input: '{"a":1}' }).join(' ') === 'run foo --input {"a":1}', 'run')
16
+
17
+ let threw = false
18
+ try { buildMonidArgv('discover', { query: '' }) } catch { threw = true }
19
+ assert(threw, 'discover requires query')
20
+ threw = false
21
+ try { buildMonidArgv('run', {}) } catch { threw = true }
22
+ assert(threw, 'run requires tool')
23
+ threw = false
24
+ try { buildMonidArgv('nope') } catch { threw = true }
25
+ assert(threw, 'unknown sub')
26
+
27
+ console.log('monid-bridge: all cases passed')
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "monid-bridge",
3
+ "version": "1.0.0",
4
+ "description": "Thin Monid CLI bridge: paste an API key in Settings → Plugins; agents get monid_discover / monid_run / monid_health wrapping the official CLI. Does not reimplement TinyFish.",
5
+ "entry": "index.mjs",
6
+ "tools": [
7
+ "monid_health",
8
+ "monid_discover",
9
+ "monid_run"
10
+ ],
11
+ "permissions": [
12
+ "exec"
13
+ ]
14
+ }
Binary file