neuralos 3.3.8 → 3.4.0

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
@@ -350712,12 +350712,12 @@ var BUILTIN_TOOL_INFO = [
350712
350712
  {
350713
350713
  name: "write_file",
350714
350714
  description: WRITE_FILE_TOOL_DESCRIPTION,
350715
- hiddenFromSettings: true
350715
+ shortDescription: "Write a full file (replace contents)"
350716
350716
  },
350717
350717
  {
350718
350718
  name: "edit_file",
350719
350719
  description: EDIT_FILE_TOOL_DESCRIPTION,
350720
- hiddenFromSettings: true
350720
+ shortDescription: "Edit a file by replacing an exact string"
350721
350721
  },
350722
350722
  {
350723
350723
  name: "skill",
@@ -354867,6 +354867,7 @@ var CORE_METHODS = [
354867
354867
  m("tools:getMcp", "tools", "List MCP tools.", "1.0.0"),
354868
354868
  m("tools:setMcpEnabled", "tools", "Enable/disable an MCP tool.", "1.0.0"),
354869
354869
  m("tools:getBuiltIn", "tools", "List built-in agent tools with enabled state.", "1.0.0"),
354870
+ m("tools:getPlugins", "tools", "List plugin agent tools (name, plugin, description).", "3.4.0"),
354870
354871
  m("tools:setBuiltInEnabled", "tools", "Enable/disable a built-in tool.", "1.0.0", { name: { type: "string" }, enabled: { type: "boolean" } })
354871
354872
  ];
354872
354873
  var DESCRIBE_METHOD = m(
@@ -369458,6 +369459,7 @@ var AgentService_v2 = class {
369458
369459
  pluginTools = /* @__PURE__ */ new Map();
369459
369460
  /** Plugin tool schemas (for toolsForModel injection). */
369460
369461
  pluginToolSchemas = [];
369462
+ pluginToolMeta = [];
369461
369463
  passChatTempExportService = new PassChatTempExportService();
369462
369464
  fallbackCompactionHistoryExportService = null;
369463
369465
  activeAgentRunIdsBySession = /* @__PURE__ */ new Map();
@@ -369516,11 +369518,35 @@ var AgentService_v2 = class {
369516
369518
  * pluginTools (so the dispatch switch's default case can call them). */
369517
369519
  setPluginTools(tools2) {
369518
369520
  this.pluginTools = new Map(tools2.map((t) => [t.name, t.handler]));
369519
- this.pluginToolSchemas = tools2.map((t) => ({
369521
+ this.pluginToolMeta = tools2.map((t) => ({
369520
369522
  name: t.name,
369521
- description: t.description,
369522
- schema: t.params || {}
369523
+ description: t.description || t.name,
369524
+ plugin: t.plugin || "plugin"
369523
369525
  }));
369526
+ this.pluginToolSchemas = tools2.map((t) => {
369527
+ const params = t.params && typeof t.params === "object" ? t.params : {};
369528
+ const looksJsonSchema = typeof params.type === "string";
369529
+ const parameters = looksJsonSchema ? params : {
369530
+ type: "object",
369531
+ properties: params,
369532
+ additionalProperties: true
369533
+ };
369534
+ return {
369535
+ type: "function",
369536
+ function: {
369537
+ name: t.name,
369538
+ description: t.description || t.name,
369539
+ parameters
369540
+ }
369541
+ };
369542
+ });
369543
+ }
369544
+ /** Plugin tools in OpenAI bindTools shape (empty until setPluginTools). */
369545
+ getPluginToolSchemas() {
369546
+ return this.pluginToolSchemas;
369547
+ }
369548
+ listPluginTools() {
369549
+ return this.pluginToolMeta.map((t) => ({ ...t, enabled: true }));
369524
369550
  }
369525
369551
  /** Wire a session-log handle so list_session_logs / read_session_log work. */
369526
369552
  setSessionLogger(logger) {
@@ -370142,9 +370168,11 @@ var AgentService_v2 = class {
370142
370168
  }
370143
370169
  );
370144
370170
  const baseModel = shouldUseThinkingModelOnThisPass ? sessionBinding.thinkingModel || sessionBinding.model : sessionBinding.model;
370171
+ const pluginOpenAiTools = this.pluginToolSchemas;
370145
370172
  const modelWithTools = baseModel.bindTools([
370146
370173
  ...builtInTools,
370147
- ...mcpTools
370174
+ ...mcpTools,
370175
+ ...pluginOpenAiTools
370148
370176
  ]);
370149
370177
  const messageId = v4_default();
370150
370178
  let partialText = "";
@@ -370180,7 +370208,11 @@ var AgentService_v2 = class {
370180
370208
  shouldUseThinkingModelOnThisPass ? 0.2 : 0.7,
370181
370209
  null
370182
370210
  );
370183
- modelToUse = fallbackChat.bindTools([...builtInTools, ...mcpTools]);
370211
+ modelToUse = fallbackChat.bindTools([
370212
+ ...builtInTools,
370213
+ ...mcpTools,
370214
+ ...pluginOpenAiTools
370215
+ ]);
370184
370216
  }
370185
370217
  return await invokeWithRetryAndSanitizedInput({
370186
370218
  helpers: this.helpers,
@@ -376649,6 +376681,15 @@ var WebSocketGatewayAdapter = class {
376649
376681
  }
376650
376682
  return await this.options.toolsBridge.getBuiltIn();
376651
376683
  }
376684
+ case "tools:getPlugins": {
376685
+ if (!this.options.toolsBridge?.getPlugins) {
376686
+ throw new WebSocketRpcError(
376687
+ "METHOD_NOT_FOUND",
376688
+ "tools:getPlugins is not available on this websocket gateway."
376689
+ );
376690
+ }
376691
+ return await this.options.toolsBridge.getPlugins();
376692
+ }
376652
376693
  case "tools:setBuiltInEnabled": {
376653
376694
  if (!this.options.toolsBridge?.setBuiltInEnabled) {
376654
376695
  throw new WebSocketRpcError(
@@ -392577,6 +392618,23 @@ var PluginRegistry = class {
392577
392618
  allTools() {
392578
392619
  return this.list().filter((p) => p.enabled && !p.error).flatMap((p) => p.tools);
392579
392620
  }
392621
+ /** Flatten enabled plugin tools for AgentService.setPluginTools. */
392622
+ collectAgentTools() {
392623
+ const out = [];
392624
+ for (const record2 of this.list()) {
392625
+ if (record2.error || !record2.enabled) continue;
392626
+ for (const tool2 of record2.tools) {
392627
+ out.push({
392628
+ name: tool2.name,
392629
+ description: tool2.description ?? "",
392630
+ params: tool2.params ?? {},
392631
+ handler: tool2.handler,
392632
+ plugin: record2.manifest.name
392633
+ });
392634
+ }
392635
+ }
392636
+ return out;
392637
+ }
392580
392638
  /** All triggers from enabled plugins. */
392581
392639
  allTriggers() {
392582
392640
  return this.list().filter((p) => p.enabled && !p.error).flatMap((p) => p.triggers);
@@ -398016,25 +398074,15 @@ async function startGyBackend() {
398016
398074
  Promise.race([
398017
398075
  observability.pluginRegistry.reload(),
398018
398076
  new Promise((_, reject) => setTimeout(() => reject(new Error("plugin reload timeout (10s)")), 1e4))
398019
- ]).then((pluginRecords) => {
398020
- const pluginTools = [];
398021
- for (const record2 of pluginRecords) {
398022
- if (record2.error || !record2.enabled) continue;
398023
- for (const tool2 of record2.tools) {
398024
- pluginTools.push({
398025
- name: tool2.name,
398026
- description: tool2.description ?? "",
398027
- params: tool2.params ?? {},
398028
- handler: tool2.handler
398029
- });
398030
- }
398031
- }
398032
- if (pluginTools.length > 0) {
398033
- agentService.setPluginTools(pluginTools);
398034
- console.log(`[gybackend] Wired ${pluginTools.length} plugin tools from ${pluginRecords.filter((r) => !r.error && r.enabled).length} plugins into the agent.`);
398035
- } else {
398036
- console.log("[gybackend] No plugin tools found to wire.");
398077
+ ]).then(() => {
398078
+ const pluginTools = observability.pluginRegistry.collectAgentTools();
398079
+ agentService.setPluginTools(pluginTools);
398080
+ const enabled = observability.pluginRegistry.list().filter((r) => !r.error && r.enabled).length;
398081
+ try {
398082
+ gatewayService.broadcastRaw("tools:pluginsUpdated", agentService.listPluginTools());
398083
+ } catch {
398037
398084
  }
398085
+ console.log(`[gybackend] Wired ${pluginTools.length} plugin tools from ${enabled} plugins into the agent.`);
398038
398086
  }).catch((e) => {
398039
398087
  console.warn("[gybackend] Plugin tool wiring skipped:", e instanceof Error ? e.message : String(e));
398040
398088
  });
@@ -398698,6 +398746,7 @@ async function startGyBackend() {
398698
398746
  const settings = settingsService.getSettings();
398699
398747
  return buildBuiltInToolStatusSummary(settings.tools?.builtIn);
398700
398748
  },
398749
+ getPlugins: () => agentService.listPluginTools(),
398701
398750
  setBuiltInEnabled: async (name, enabled) => {
398702
398751
  const settings = settingsService.getSettings();
398703
398752
  const nextBuiltIn = { ...settings.tools?.builtIn ?? {} };
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuralos",
3
- "version": "3.3.8",
3
+ "version": "3.4.0",
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",
@@ -1,12 +1,16 @@
1
1
  /**
2
- * monid-bridge — thin wrapper around the official Monid CLI.
2
+ * monid-bridge — thin wrapper around the official Monid CLI (v0.1.6).
3
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).
4
+ * Settings → Plugins → Monid (settings.monid): enabled, binaryPath, keyLabel
5
+ * API key is NEVER stored in settings.json (`monid keys add -k … -l …`).
8
6
  *
9
- * Agent tools: monid_health, monid_discover, monid_run.
7
+ * Agent tools: monid_health, monid_discover, monid_inspect, monid_run.
8
+ *
9
+ * Argv MUST match `monid <cmd> --help`:
10
+ * discover --query <q> [--limit n] [--min-score s] --json
11
+ * inspect --provider <p> --endpoint <e> --json
12
+ * run --provider <p> --endpoint <e> [--input json] [--query json] [--path json] --json
13
+ * Bare `discover <query>` / `run <tool>` are false positives (CLI rejects them).
10
14
  */
11
15
 
12
16
  import { createRequire } from 'node:module'
@@ -23,25 +27,80 @@ export function resolveConfig(ctx = {}, env = process.env) {
23
27
  }
24
28
  }
25
29
 
26
- /** Build argv for a monid subcommand. Pure. */
30
+ function reqStr(v, name) {
31
+ const s = String(v ?? '').trim()
32
+ if (!s) throw new Error(`${name} is required`)
33
+ return s
34
+ }
35
+
36
+ function optJsonFlag(argv, flag, value) {
37
+ if (value == null) return
38
+ const s = typeof value === 'string' ? value.trim() : JSON.stringify(value)
39
+ if (!s) return
40
+ argv.push(flag, s)
41
+ }
42
+
43
+ function optNumFlag(argv, flag, value) {
44
+ if (value == null || value === '') return
45
+ const n = Number(value)
46
+ if (!Number.isFinite(n)) throw new Error(`${flag} must be a number`)
47
+ argv.push(flag, String(n))
48
+ }
49
+
50
+ /**
51
+ * Build argv AFTER the binary name.
52
+ * Always pass --json so agents get structured output (no ANSI).
53
+ */
27
54
  export function buildMonidArgv(sub, params = {}) {
28
55
  switch (sub) {
29
56
  case 'version':
30
57
  return ['--version']
31
58
  case 'keys-list':
32
59
  return ['keys', 'list']
60
+ case 'whoami':
61
+ return ['whoami']
33
62
  case 'discover': {
34
- const q = String(params.query || '').trim()
35
- if (!q) throw new Error('monid_discover needs a query')
36
- return ['discover', q]
63
+ const q = reqStr(params.query, 'query')
64
+ const argv = ['discover', '--query', q]
65
+ optNumFlag(argv, '--limit', params.limit)
66
+ optNumFlag(argv, '--min-score', params.minScore ?? params['min-score'])
67
+ argv.push('--json')
68
+ return argv
69
+ }
70
+ case 'inspect': {
71
+ const provider = reqStr(params.provider, 'provider')
72
+ const endpoint = reqStr(params.endpoint, 'endpoint')
73
+ return ['inspect', '--provider', provider, '--endpoint', endpoint, '--json']
37
74
  }
38
75
  case 'run': {
76
+ // FN: agents may pass tool="provider/endpoint" from older docs.
77
+ let provider = String(params.provider || '').trim()
78
+ let endpoint = String(params.endpoint || '').trim()
39
79
  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))
80
+ if ((!provider || !endpoint) && tool) {
81
+ const slash = tool.indexOf('/')
82
+ if (slash > 0) {
83
+ provider = provider || tool.slice(0, slash)
84
+ endpoint = endpoint || tool.slice(slash)
85
+ }
86
+ }
87
+ if (!provider) throw new Error('monid_run needs provider (slug from discover)')
88
+ if (!endpoint) throw new Error('monid_run needs endpoint (path from discover, e.g. /news/search)')
89
+ if (!endpoint.startsWith('/')) {
90
+ throw new Error('endpoint must start with / (Monid endpoint path, not a free-form tool name)')
91
+ }
92
+ const argv = ['run', '--provider', provider, '--endpoint', endpoint]
93
+ const input = params.input ?? params.body
94
+ if (input != null && String(input).trim()) {
95
+ argv.push('--input', typeof input === 'string' ? input : JSON.stringify(input))
44
96
  }
97
+ optJsonFlag(argv, '--query', params.queryParams ?? params.query)
98
+ optJsonFlag(argv, '--path', params.pathParams ?? params.path)
99
+ if (params.wait != null && params.wait !== false && params.wait !== '') {
100
+ argv.push('--wait')
101
+ if (params.wait !== true) argv.push(String(params.wait))
102
+ }
103
+ argv.push('--json')
45
104
  return argv
46
105
  }
47
106
  default:
@@ -49,21 +108,30 @@ export function buildMonidArgv(sub, params = {}) {
49
108
  }
50
109
  }
51
110
 
111
+ /** True if argv looks like the broken 3.3.8 shapes (for tests / guards). */
112
+ export function isLegacyBrokenArgv(argv) {
113
+ if (!Array.isArray(argv) || argv.length < 2) return false
114
+ if (argv[0] === 'discover' && argv[1] !== '--query' && !argv[1].startsWith('-')) return true
115
+ if (argv[0] === 'run' && argv[1] !== '--provider' && !String(argv[1]).startsWith('-')) return true
116
+ return false
117
+ }
118
+
52
119
  async function runMonid(ctx, cfg, argv) {
120
+ const env = { ...process.env, NO_COLOR: '1' }
53
121
  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 })
122
+ const cmdline = [cfg.binaryPath, ...argv].map((a) => (/\s/.test(a) ? JSON.stringify(a) : a)).join(' ')
123
+ return ctx.runCommand({ command: cmdline, env })
56
124
  }
57
125
  if (typeof ctx.exec === 'function') {
58
- const cmdline = [cfg.binaryPath, ...argv].map((a) => (/\s/.test(a) ? `"${a}"` : a)).join(' ')
59
- return ctx.exec(cmdline)
126
+ const cmdline = [cfg.binaryPath, ...argv].map((a) => (/\s/.test(a) ? JSON.stringify(a) : a)).join(' ')
127
+ return ctx.exec(cmdline, { env })
60
128
  }
61
129
  const { execFile } = require('node:child_process')
62
130
  return new Promise((resolve) => {
63
- execFile(cfg.binaryPath, argv, { timeout: 120000, env: { ...process.env, NO_COLOR: '1' } }, (err, stdout, stderr) => {
131
+ execFile(cfg.binaryPath, argv, { timeout: 120000, env }, (err, stdout, stderr) => {
64
132
  resolve({
65
133
  ok: !err,
66
- exitCode: err?.code ?? 0,
134
+ exitCode: typeof err?.code === 'number' ? err.code : err ? 1 : 0,
67
135
  stdout: String(stdout ?? ''),
68
136
  stderr: String(stderr ?? ''),
69
137
  error: err ? String(err.message) : undefined,
@@ -80,7 +148,7 @@ async function guarded(fn, log) {
80
148
  log?.(`[monid] ${msg}`)
81
149
  return {
82
150
  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).',
151
+ hint: 'Install @monid-ai/cli (`npm i -g @monid-ai/cli`) and paste an API key in Settings → Plugins → Monid. discover needs --query; run needs --provider and --endpoint.',
84
152
  }
85
153
  }
86
154
  }
@@ -101,15 +169,19 @@ export function register(ctx) {
101
169
  guarded(async () => {
102
170
  const ver = await runMonid(ctx, cfg, buildMonidArgv('version'))
103
171
  const keys = await runMonid(ctx, cfg, buildMonidArgv('keys-list'))
104
- return { binaryPath: cfg.binaryPath, keyLabel: cfg.keyLabel, version: ver, keys }
172
+ const who = await runMonid(ctx, cfg, buildMonidArgv('whoami'))
173
+ return { binaryPath: cfg.binaryPath, keyLabel: cfg.keyLabel, version: ver, keys, whoami: who }
105
174
  }, log),
106
175
  })
107
176
 
108
177
  registerTool({
109
178
  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.',
179
+ description:
180
+ 'Search Monid data endpoints with natural language (company news, people, enrichment). Uses `monid discover --query`. Do not pass a bare positional query.',
111
181
  params: {
112
- query: { type: 'string', description: 'What you need (e.g. "linkedin company employees")' },
182
+ query: { type: 'string', description: 'Natural-language search (required)' },
183
+ limit: { type: 'number', description: 'Max results (max 50)', optional: true },
184
+ minScore: { type: 'number', description: 'Minimum relevance score', optional: true },
113
185
  },
114
186
  handler: async (p) =>
115
187
  guarded(async () => {
@@ -119,12 +191,32 @@ export function register(ctx) {
119
191
  }, log),
120
192
  })
121
193
 
194
+ registerTool({
195
+ name: 'monid_inspect',
196
+ description: 'Get full details for one Monid endpoint (`monid inspect --provider --endpoint`).',
197
+ params: {
198
+ provider: { type: 'string', description: 'Provider slug (e.g. context.dev)' },
199
+ endpoint: { type: 'string', description: 'Endpoint path (e.g. /news/search)' },
200
+ },
201
+ handler: async (p) =>
202
+ guarded(async () => {
203
+ const argv = buildMonidArgv('inspect', p)
204
+ const r = await runMonid(ctx, cfg, argv)
205
+ return { argv: [cfg.binaryPath, ...argv], ...r }
206
+ }, log),
207
+ })
208
+
122
209
  registerTool({
123
210
  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.',
211
+ description:
212
+ 'Execute a Monid endpoint (`monid run --provider --endpoint`). provider+endpoint from discover; optional JSON --input / --query / --path. Not `monid run <toolName>`.',
125
213
  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 },
214
+ provider: { type: 'string', description: 'Provider slug', optional: true },
215
+ endpoint: { type: 'string', description: 'Endpoint path starting with /', optional: true },
216
+ tool: { type: 'string', description: 'Optional shorthand provider/endpoint', optional: true },
217
+ input: { type: 'string', description: 'Body JSON string', optional: true },
218
+ queryParams: { type: 'string', description: 'Query-parameters JSON string', optional: true },
219
+ pathParams: { type: 'string', description: 'Path-parameters JSON string', optional: true },
128
220
  },
129
221
  handler: async (p) =>
130
222
  guarded(async () => {
@@ -1,27 +1,75 @@
1
- import { buildMonidArgv, resolveConfig } from './index.mjs'
1
+ import { buildMonidArgv, isLegacyBrokenArgv, resolveConfig } from './index.mjs'
2
2
 
3
3
  const assert = (c, m) => {
4
4
  if (!c) throw new Error(`assert failed: ${m}`)
5
5
  }
6
6
 
7
+ // --- config ---
7
8
  assert(resolveConfig({ settings: {} }).enabled === true, 'enabled default')
8
9
  assert(resolveConfig({ settings: { monid: { enabled: false } } }).enabled === false, 'disabled')
9
10
  assert(resolveConfig({ settings: { monid: { binaryPath: '/opt/monid' } } }).binaryPath === '/opt/monid', 'bin')
10
11
  assert(resolveConfig({}, { MONID_BIN: 'custom-monid' }).binaryPath === 'custom-monid', 'env bin')
11
12
 
13
+ // --- FP: 3.3.8 positional argv must be detected as broken ---
14
+ assert(isLegacyBrokenArgv(['discover', 'company news']) === true, 'FP discover positional')
15
+ assert(isLegacyBrokenArgv(['run', 'foo']) === true, 'FP run positional')
16
+ assert(isLegacyBrokenArgv(['discover', '--query', 'company news']) === false, 'good discover')
17
+ assert(isLegacyBrokenArgv(['run', '--provider', 'x', '--endpoint', '/y']) === false, 'good run')
18
+
19
+ const disc = buildMonidArgv('discover', { query: 'company news', limit: 5 })
20
+ assert(!isLegacyBrokenArgv(disc), 'new discover not legacy')
21
+ assert(disc[0] === 'discover' && disc[1] === '--query' && disc[2] === 'company news', `disc=${disc}`)
22
+ assert(disc.includes('--limit') && disc.includes('5') && disc.includes('--json'), 'limit+json')
23
+
12
24
  assert(buildMonidArgv('version').join(' ') === '--version', 'version')
13
25
  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')
26
+ assert(buildMonidArgv('whoami').join(' ') === 'whoami', 'whoami')
27
+
28
+ const insp = buildMonidArgv('inspect', { provider: 'context.dev', endpoint: '/news/search' })
29
+ assert(insp.join(' ') === 'inspect --provider context.dev --endpoint /news/search --json', insp.join(' '))
30
+
31
+ const run = buildMonidArgv('run', {
32
+ provider: 'context.dev',
33
+ endpoint: '/news/search',
34
+ input: '{"q":"acme"}',
35
+ })
36
+ assert(run[0] === 'run' && run.includes('--provider') && run.includes('context.dev'), 'run provider')
37
+ assert(run.includes('--endpoint') && run.includes('/news/search'), 'run endpoint')
38
+ assert(run.includes('--input') && run.includes('--json'), 'run input+json')
39
+ assert(!isLegacyBrokenArgv(run), 'new run not legacy')
40
+
41
+ // FN: tool="provider/endpoint" shorthand
42
+ const fromTool = buildMonidArgv('run', { tool: 'apollo/mixed_companies/search' })
43
+ assert(fromTool.includes('apollo') && fromTool.includes('/mixed_companies/search'), `fromTool=${fromTool}`)
16
44
 
45
+ // FN: queryParams / pathParams
46
+ const withQP = buildMonidArgv('run', {
47
+ provider: 'akta',
48
+ endpoint: '/v1/news',
49
+ queryParams: '{"q":"x"}',
50
+ pathParams: '{"id":"1"}',
51
+ })
52
+ assert(withQP.includes('--query') && withQP.includes('{"q":"x"}'), 'query flag')
53
+ assert(withQP.includes('--path') && withQP.includes('{"id":"1"}'), 'path flag')
54
+
55
+ // rejects
17
56
  let threw = false
18
- try { buildMonidArgv('discover', { query: '' }) } catch { threw = true }
19
- assert(threw, 'discover requires query')
57
+ try { buildMonidArgv('discover', { query: ' ' }) } catch { threw = true }
58
+ assert(threw, 'empty query')
59
+ threw = false
60
+ try { buildMonidArgv('run', { tool: 'not-a-path' }) } catch { threw = true }
61
+ assert(threw, 'run without provider/endpoint')
20
62
  threw = false
21
- try { buildMonidArgv('run', {}) } catch { threw = true }
22
- assert(threw, 'run requires tool')
63
+ try { buildMonidArgv('run', { provider: 'x', endpoint: 'news' }) } catch { threw = true }
64
+ assert(threw, 'endpoint must start with /')
65
+ threw = false
66
+ try { buildMonidArgv('inspect', { provider: 'x' }) } catch { threw = true }
67
+ assert(threw, 'inspect needs endpoint')
23
68
  threw = false
24
69
  try { buildMonidArgv('nope') } catch { threw = true }
25
70
  assert(threw, 'unknown sub')
71
+ threw = false
72
+ try { buildMonidArgv('discover', { query: 'q', limit: 'nope' }) } catch { threw = true }
73
+ assert(threw, 'bad limit')
26
74
 
27
75
  console.log('monid-bridge: all cases passed')
@@ -6,6 +6,7 @@
6
6
  "tools": [
7
7
  "monid_health",
8
8
  "monid_discover",
9
+ "monid_inspect",
9
10
  "monid_run"
10
11
  ],
11
12
  "permissions": [
Binary file