@notis_ai/cli 0.2.0-beta.41.1 → 0.2.0-beta.48.1
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/README.md +6 -10
- package/package.json +1 -1
- package/skills/notis-cli/SKILL.md +1 -1
- package/src/command-specs/apps.js +7 -7
- package/src/command-specs/helpers.js +67 -46
- package/src/command-specs/meta.js +6 -6
- package/src/command-specs/tools.js +59 -48
- package/src/runtime/transport.js +1 -28
package/README.md
CHANGED
|
@@ -179,9 +179,9 @@ Examples:
|
|
|
179
179
|
|
|
180
180
|
### `npx --package @notis_ai/cli@latest -- notis tools toolkits`
|
|
181
181
|
|
|
182
|
-
List toolkit namespaces available to the active user.
|
|
182
|
+
List toolkit namespaces and connection statuses available to the active user.
|
|
183
183
|
|
|
184
|
-
When to use: Use this before searching or executing generic tools.
|
|
184
|
+
When to use: Use this to inspect connection state before searching or executing generic tools.
|
|
185
185
|
|
|
186
186
|
Examples:
|
|
187
187
|
- `npx --package @notis_ai/cli@latest -- notis tools toolkits`
|
|
@@ -194,11 +194,11 @@ Search across toolkit namespaces using natural language.
|
|
|
194
194
|
When to use: Use this when you need a generic capability that does not have a first-class CLI command.
|
|
195
195
|
|
|
196
196
|
Options:
|
|
197
|
-
- `--
|
|
197
|
+
- `--known-fields <text>` — Optional known field hints, such as channel_name:general or user_email:a@example.com.
|
|
198
198
|
|
|
199
199
|
Examples:
|
|
200
200
|
- `npx --package @notis_ai/cli@latest -- notis tools search "send an email"`
|
|
201
|
-
- `npx --package @notis_ai/cli@latest -- notis tools search "
|
|
201
|
+
- `npx --package @notis_ai/cli@latest -- notis tools search "post on LinkedIn" --known-fields "platform:linkedin"`
|
|
202
202
|
|
|
203
203
|
### `npx --package @notis_ai/cli@latest -- notis tools describe <tool-name>`
|
|
204
204
|
|
|
@@ -206,12 +206,9 @@ Describe a generic tool by name.
|
|
|
206
206
|
|
|
207
207
|
When to use: Use this when you know the tool name and want its parameter schema before execution.
|
|
208
208
|
|
|
209
|
-
Options:
|
|
210
|
-
- `--toolkits <csv-or-json>` — Optional subset of toolkit ids to search.
|
|
211
|
-
|
|
212
209
|
Examples:
|
|
213
210
|
- `npx --package @notis_ai/cli@latest -- notis tools describe composio-gmail-default-send_email`
|
|
214
|
-
- `npx --package @notis_ai/cli@latest -- notis tools describe notis-default-query
|
|
211
|
+
- `npx --package @notis_ai/cli@latest -- notis tools describe notis-default-query`
|
|
215
212
|
|
|
216
213
|
### `npx --package @notis_ai/cli@latest -- notis tools exec <tool-name>`
|
|
217
214
|
|
|
@@ -224,7 +221,6 @@ Options:
|
|
|
224
221
|
- `--file <argument-path=local-path>` — Upload a local file into a file-uploadable tool argument. Repeatable.
|
|
225
222
|
- `--get-schema` — Display the tool parameter schema without executing.
|
|
226
223
|
- `--dry-run` — Validate arguments against the tool schema without executing.
|
|
227
|
-
- `--toolkits <csv-or-json>` — Optional toolkit namespace(s) to use when resolving the tool.
|
|
228
224
|
|
|
229
225
|
Examples:
|
|
230
226
|
- `npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --arguments '{"database_slug":"tasks","query":{}}'`
|
|
@@ -272,7 +268,7 @@ Examples:
|
|
|
272
268
|
|
|
273
269
|
### `npx --package @notis_ai/cli@latest -- notis whoami`
|
|
274
270
|
|
|
275
|
-
Display the active profile, user, and available
|
|
271
|
+
Display the active profile, user, and available toolkit connection statuses.
|
|
276
272
|
|
|
277
273
|
When to use: Use this to quickly confirm which account and environment a command will target.
|
|
278
274
|
|
package/package.json
CHANGED
|
@@ -130,7 +130,7 @@ This is the main escape hatch for:
|
|
|
130
130
|
- `npx --package @notis_ai/cli@latest -- notis tools toolkits`
|
|
131
131
|
2. Search for the capability you need using natural language:
|
|
132
132
|
- `npx --package @notis_ai/cli@latest -- notis tools search "<query>"`
|
|
133
|
-
- optionally
|
|
133
|
+
- optionally add known field hints with `--known-fields "<key:value>"`
|
|
134
134
|
3. If needed, inspect the exact tool and parameter schema:
|
|
135
135
|
- `npx --package @notis_ai/cli@latest -- notis tools describe <tool-name>`
|
|
136
136
|
- `npx --package @notis_ai/cli@latest -- notis tools exec <tool-name> --get-schema`
|
|
@@ -447,7 +447,7 @@ function databaseMaterializationWarnings(apps) {
|
|
|
447
447
|
async function assertDirectDeployAccess(runtime, appId) {
|
|
448
448
|
const result = await runToolCommand({
|
|
449
449
|
runtime,
|
|
450
|
-
toolName: '
|
|
450
|
+
toolName: 'notis-default-list_apps',
|
|
451
451
|
});
|
|
452
452
|
const apps = result.payload.apps || [];
|
|
453
453
|
const hasAccess = apps.some((app) => (app.app_id || app.id) === appId);
|
|
@@ -463,7 +463,7 @@ async function assertDirectDeployAccess(runtime, appId) {
|
|
|
463
463
|
async function appsListHandler(ctx) {
|
|
464
464
|
const result = await runToolCommand({
|
|
465
465
|
runtime: ctx.runtime,
|
|
466
|
-
toolName: '
|
|
466
|
+
toolName: 'notis-default-list_apps',
|
|
467
467
|
});
|
|
468
468
|
const apps = result.payload.apps || [];
|
|
469
469
|
return ctx.output.emitSuccess({
|
|
@@ -511,7 +511,7 @@ async function appsCreateHandler(ctx) {
|
|
|
511
511
|
const idempotencyKey = nextIdempotencyKey(ctx.globalOptions);
|
|
512
512
|
const result = await runToolCommand({
|
|
513
513
|
runtime: ctx.runtime,
|
|
514
|
-
toolName: '
|
|
514
|
+
toolName: 'notis-default-create_app',
|
|
515
515
|
arguments_: {
|
|
516
516
|
name: ctx.args.name,
|
|
517
517
|
description: appConfig?.description || undefined,
|
|
@@ -1073,7 +1073,7 @@ async function appsDeployHandler(ctx) {
|
|
|
1073
1073
|
try {
|
|
1074
1074
|
result = await runToolCommand({
|
|
1075
1075
|
runtime: ctx.runtime,
|
|
1076
|
-
toolName: '
|
|
1076
|
+
toolName: 'notis-default-save_app_files',
|
|
1077
1077
|
arguments_: {
|
|
1078
1078
|
app_id: appId,
|
|
1079
1079
|
files,
|
|
@@ -1185,7 +1185,7 @@ export const appsCommandSpecs = [
|
|
|
1185
1185
|
examples: ['notis apps list', 'notis apps list --json'],
|
|
1186
1186
|
mutates: false,
|
|
1187
1187
|
idempotent: true,
|
|
1188
|
-
backend_call: { type: 'tool', name: '
|
|
1188
|
+
backend_call: { type: 'tool', name: 'notis-default-list_apps' },
|
|
1189
1189
|
handler: appsListHandler,
|
|
1190
1190
|
},
|
|
1191
1191
|
{
|
|
@@ -1242,7 +1242,7 @@ export const appsCommandSpecs = [
|
|
|
1242
1242
|
],
|
|
1243
1243
|
mutates: true,
|
|
1244
1244
|
idempotent: false,
|
|
1245
|
-
backend_call: { type: 'tool', name: '
|
|
1245
|
+
backend_call: { type: 'tool', name: 'notis-default-create_app' },
|
|
1246
1246
|
handler: appsCreateHandler,
|
|
1247
1247
|
},
|
|
1248
1248
|
{
|
|
@@ -1376,7 +1376,7 @@ export const appsCommandSpecs = [
|
|
|
1376
1376
|
examples: ['notis apps deploy', 'notis apps deploy --skip-build', 'notis apps deploy --app-id abc123', 'notis apps deploy --direct'],
|
|
1377
1377
|
mutates: true,
|
|
1378
1378
|
idempotent: true,
|
|
1379
|
-
backend_call: { type: 'tool', name: '
|
|
1379
|
+
backend_call: { type: 'tool', name: 'notis-default-save_app_files' },
|
|
1380
1380
|
handler: appsDeployHandler,
|
|
1381
1381
|
},
|
|
1382
1382
|
{
|
|
@@ -17,23 +17,6 @@ export function parseMaybeJson(value, label) {
|
|
|
17
17
|
return parseJson(value, label);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export function normalizeToolkits(value) {
|
|
21
|
-
if (!value) {
|
|
22
|
-
return [];
|
|
23
|
-
}
|
|
24
|
-
if (value.startsWith('[')) {
|
|
25
|
-
const parsed = parseJson(value, 'toolkits');
|
|
26
|
-
if (!Array.isArray(parsed)) {
|
|
27
|
-
throw usageError('toolkits JSON must be an array of toolkit strings');
|
|
28
|
-
}
|
|
29
|
-
return parsed;
|
|
30
|
-
}
|
|
31
|
-
return value
|
|
32
|
-
.split(',')
|
|
33
|
-
.map((entry) => entry.trim())
|
|
34
|
-
.filter(Boolean);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
20
|
export function nextIdempotencyKey(globalOptions) {
|
|
38
21
|
return globalOptions.idempotencyKey || randomUUID();
|
|
39
22
|
}
|
|
@@ -57,28 +40,19 @@ export async function runToolCommand({
|
|
|
57
40
|
}
|
|
58
41
|
|
|
59
42
|
export async function fetchToolkits(runtime) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (toolkits.length) {
|
|
70
|
-
return toolkits;
|
|
71
|
-
}
|
|
72
|
-
const availableToolkits = await fetchToolkits(runtime);
|
|
73
|
-
return availableToolkits.map((entry) => entry.id);
|
|
43
|
+
const payload = await fetchToolDiscovery(runtime, 'List available toolkit namespaces and connection statuses');
|
|
44
|
+
return (payload.toolkit_connection_statuses || []).map((entry) => ({
|
|
45
|
+
id: entry.toolkit,
|
|
46
|
+
provider: typeof entry.toolkit === 'string' ? entry.toolkit.split('-', 1)[0] : undefined,
|
|
47
|
+
description: entry.description || entry.status_message || entry.toolkit,
|
|
48
|
+
has_active_connection: Boolean(entry.has_active_connection),
|
|
49
|
+
status_message: entry.status_message || '',
|
|
50
|
+
connection_details: entry.connection_details || {},
|
|
51
|
+
}));
|
|
74
52
|
}
|
|
75
53
|
|
|
76
54
|
export async function probeAuth(runtime) {
|
|
77
|
-
|
|
78
|
-
runtime,
|
|
79
|
-
toolName: 'notis_find_toolkits',
|
|
80
|
-
});
|
|
81
|
-
return result.payload;
|
|
55
|
+
return fetchToolDiscovery(runtime, 'List available toolkit namespaces and connection statuses');
|
|
82
56
|
}
|
|
83
57
|
|
|
84
58
|
export async function healthCheck(runtime) {
|
|
@@ -90,22 +64,69 @@ export async function healthCheck(runtime) {
|
|
|
90
64
|
});
|
|
91
65
|
}
|
|
92
66
|
|
|
93
|
-
export async function fetchToolSchema(runtime, toolName
|
|
94
|
-
const
|
|
67
|
+
export async function fetchToolSchema(runtime, toolName) {
|
|
68
|
+
const canonicalHints = canonicalToolHints(toolName);
|
|
69
|
+
const payload = await fetchToolDiscovery(
|
|
70
|
+
runtime,
|
|
71
|
+
canonicalHints.useCase,
|
|
72
|
+
canonicalHints.knownFields,
|
|
73
|
+
);
|
|
74
|
+
const schema = payload.tool_schemas?.[toolName];
|
|
75
|
+
if (!schema) {
|
|
76
|
+
throw usageError(`Tool "${toolName}" not found.`);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
name: toolName,
|
|
80
|
+
toolkit_id: schema.toolkit,
|
|
81
|
+
description: schema.description || '',
|
|
82
|
+
parameters: schema.input_schema || { type: 'object', properties: {} },
|
|
83
|
+
output_schema: schema.output_schema || {},
|
|
84
|
+
schema_available: Boolean(schema.hasFullSchema),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function canonicalToolHints(toolName) {
|
|
89
|
+
const rawName = String(toolName || '').trim();
|
|
90
|
+
const parts = rawName.split('-').filter(Boolean);
|
|
91
|
+
if (parts.length < 3) {
|
|
92
|
+
return {
|
|
93
|
+
useCase: `Describe the ${rawName} tool schema`,
|
|
94
|
+
knownFields: `tool_name:${rawName}`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const provider = parts[0];
|
|
99
|
+
const action = parts[parts.length - 1];
|
|
100
|
+
const toolkitParts = parts.slice(1, -1);
|
|
101
|
+
const toolkit = [provider, ...toolkitParts].join('-');
|
|
102
|
+
const providerToolkit = toolkitParts[0] || '';
|
|
103
|
+
const knownFields = [
|
|
104
|
+
`tool_name:${rawName}`,
|
|
105
|
+
`provider:${provider}`,
|
|
106
|
+
`toolkit:${toolkit}`,
|
|
107
|
+
providerToolkit ? `toolkit_slug:${providerToolkit}` : null,
|
|
108
|
+
`action:${action}`,
|
|
109
|
+
].filter(Boolean).join(' ');
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
useCase: `Describe the ${action} schema for ${toolkit}`,
|
|
113
|
+
knownFields,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function fetchToolDiscovery(runtime, useCase, knownFields = '') {
|
|
118
|
+
const query = { use_case: useCase };
|
|
119
|
+
if (knownFields) {
|
|
120
|
+
query.known_fields = knownFields;
|
|
121
|
+
}
|
|
95
122
|
const result = await runToolCommand({
|
|
96
123
|
runtime,
|
|
97
124
|
toolName: 'notis_find_tools',
|
|
98
125
|
arguments_: {
|
|
99
|
-
|
|
100
|
-
...(toolkits.length ? { toolkits } : {}),
|
|
126
|
+
queries: [query],
|
|
101
127
|
},
|
|
102
128
|
});
|
|
103
|
-
|
|
104
|
-
const match = tools.find((t) => t.name === toolName);
|
|
105
|
-
if (!match) {
|
|
106
|
-
throw usageError(`Tool "${toolName}" not found.`);
|
|
107
|
-
}
|
|
108
|
-
return match;
|
|
129
|
+
return result.payload || {};
|
|
109
130
|
}
|
|
110
131
|
|
|
111
132
|
export function validateArguments(schema, args) {
|
|
@@ -21,7 +21,7 @@ async function doctorHandler(ctx) {
|
|
|
21
21
|
if (ctx.runtime.jwt) {
|
|
22
22
|
try {
|
|
23
23
|
const payload = await probeAuth(ctx.runtime);
|
|
24
|
-
checks.tool_roundtrip = Array.isArray(payload.
|
|
24
|
+
checks.tool_roundtrip = Array.isArray(payload.toolkit_connection_statuses) ? 'ok' : 'error';
|
|
25
25
|
} catch {
|
|
26
26
|
checks.tool_roundtrip = 'error';
|
|
27
27
|
}
|
|
@@ -68,7 +68,7 @@ function decodeJwtUserId(jwt) {
|
|
|
68
68
|
|
|
69
69
|
async function whoamiHandler(ctx) {
|
|
70
70
|
const payload = await probeAuth(ctx.runtime);
|
|
71
|
-
const toolkits = payload.
|
|
71
|
+
const toolkits = payload.toolkit_connection_statuses || [];
|
|
72
72
|
const userId = decodeJwtUserId(ctx.runtime.jwt);
|
|
73
73
|
|
|
74
74
|
return ctx.output.emitSuccess({
|
|
@@ -78,7 +78,7 @@ async function whoamiHandler(ctx) {
|
|
|
78
78
|
api_base: ctx.runtime.apiBase,
|
|
79
79
|
user_id: userId,
|
|
80
80
|
toolkit_count: toolkits.length,
|
|
81
|
-
toolkits: toolkits.map((t) => t.
|
|
81
|
+
toolkits: toolkits.map((t) => t.toolkit),
|
|
82
82
|
cli_version: ctx.runtime.cliVersion,
|
|
83
83
|
},
|
|
84
84
|
humanSummary: `Logged in as ${userId || 'unknown'} via profile "${ctx.runtime.profileName}"`,
|
|
@@ -91,7 +91,7 @@ async function whoamiHandler(ctx) {
|
|
|
91
91
|
`Version: ${ctx.runtime.cliVersion}`,
|
|
92
92
|
].join('\n'),
|
|
93
93
|
hints: [
|
|
94
|
-
{ command: 'notis tools toolkits', reason: 'List available toolkit namespaces' },
|
|
94
|
+
{ command: 'notis tools toolkits', reason: 'List available toolkit namespaces and connection statuses' },
|
|
95
95
|
{ command: 'notis doctor', reason: 'Run a full health check' },
|
|
96
96
|
],
|
|
97
97
|
});
|
|
@@ -110,7 +110,7 @@ async function describeHandler(ctx) {
|
|
|
110
110
|
export const metaCommandSpecs = [
|
|
111
111
|
{
|
|
112
112
|
command_path: ['whoami'],
|
|
113
|
-
summary: 'Display the active profile, user, and available
|
|
113
|
+
summary: 'Display the active profile, user, and available toolkit connection statuses.',
|
|
114
114
|
when_to_use: 'Use this to quickly confirm which account and environment a command will target.',
|
|
115
115
|
args_schema: { arguments: [], options: [] },
|
|
116
116
|
examples: ['notis whoami', 'notis whoami --json'],
|
|
@@ -118,7 +118,7 @@ export const metaCommandSpecs = [
|
|
|
118
118
|
mutates: false,
|
|
119
119
|
idempotent: true,
|
|
120
120
|
related_commands: ['notis doctor'],
|
|
121
|
-
backend_call: { type: 'tool', name: '
|
|
121
|
+
backend_call: { type: 'tool', name: 'notis_find_tools' },
|
|
122
122
|
handler: whoamiHandler,
|
|
123
123
|
},
|
|
124
124
|
{
|
|
@@ -3,13 +3,12 @@ import { createHash } from 'node:crypto';
|
|
|
3
3
|
import { basename } from 'node:path';
|
|
4
4
|
import { usageError } from '../runtime/errors.js';
|
|
5
5
|
import {
|
|
6
|
+
fetchToolDiscovery,
|
|
6
7
|
fetchToolkits,
|
|
7
8
|
fetchToolSchema,
|
|
8
9
|
nextIdempotencyKey,
|
|
9
10
|
parseJson,
|
|
10
11
|
parseMaybeJson,
|
|
11
|
-
normalizeToolkits,
|
|
12
|
-
resolveSearchToolkits,
|
|
13
12
|
runToolCommand,
|
|
14
13
|
validateArguments,
|
|
15
14
|
} from './helpers.js';
|
|
@@ -142,27 +141,55 @@ async function toolsToolkitsHandler(ctx) {
|
|
|
142
141
|
data: { toolkits },
|
|
143
142
|
humanSummary: `Found ${toolkits.length} toolkits`,
|
|
144
143
|
hints: [
|
|
145
|
-
{ command: 'notis tools search <query>', reason: 'Search
|
|
144
|
+
{ command: 'notis tools search <query>', reason: 'Search canonical tools and schemas' },
|
|
146
145
|
],
|
|
147
|
-
renderHuman: () =>
|
|
146
|
+
renderHuman: () =>
|
|
147
|
+
toolkits
|
|
148
|
+
.map((entry) => {
|
|
149
|
+
const status = entry.has_active_connection ? 'connected' : 'needs auth';
|
|
150
|
+
return `${entry.id} ${status} ${entry.description}`;
|
|
151
|
+
})
|
|
152
|
+
.join('\n'),
|
|
148
153
|
});
|
|
149
154
|
}
|
|
150
155
|
|
|
156
|
+
function discoveryToolRows(payload) {
|
|
157
|
+
const schemas = payload.tool_schemas || {};
|
|
158
|
+
const statuses = new Map(
|
|
159
|
+
(payload.toolkit_connection_statuses || [])
|
|
160
|
+
.filter((entry) => entry && typeof entry.toolkit === 'string')
|
|
161
|
+
.map((entry) => [entry.toolkit, entry])
|
|
162
|
+
);
|
|
163
|
+
const seen = new Set();
|
|
164
|
+
const rows = [];
|
|
165
|
+
for (const result of payload.results || []) {
|
|
166
|
+
for (const name of [...(result.primary_tool_slugs || []), ...(result.related_tool_slugs || [])]) {
|
|
167
|
+
if (!name || seen.has(name)) continue;
|
|
168
|
+
seen.add(name);
|
|
169
|
+
const schema = schemas[name] || {};
|
|
170
|
+
const toolkit = schema.toolkit || name.split('-').slice(0, -1).join('-');
|
|
171
|
+
const status = statuses.get(toolkit);
|
|
172
|
+
rows.push({
|
|
173
|
+
name,
|
|
174
|
+
toolkit,
|
|
175
|
+
description: schema.description || '',
|
|
176
|
+
schema_available: Boolean(schemas[name]),
|
|
177
|
+
connection_status: status
|
|
178
|
+
? (status.has_active_connection ? 'connected' : 'needs auth')
|
|
179
|
+
: 'unknown',
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return rows;
|
|
184
|
+
}
|
|
185
|
+
|
|
151
186
|
async function toolsSearchHandler(ctx) {
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
runtime: ctx.runtime,
|
|
155
|
-
toolName: 'notis_find_tools',
|
|
156
|
-
arguments_: {
|
|
157
|
-
query: ctx.args.query || undefined,
|
|
158
|
-
toolkits,
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
const tools = result.payload.tools || [];
|
|
187
|
+
const payload = await fetchToolDiscovery(ctx.runtime, ctx.args.query || 'Find tools', ctx.options.knownFields || '');
|
|
188
|
+
const tools = discoveryToolRows(payload);
|
|
162
189
|
const firstTool = tools[0];
|
|
163
190
|
return ctx.output.emitSuccess({
|
|
164
191
|
command: ctx.spec.command_path.join(' '),
|
|
165
|
-
data:
|
|
192
|
+
data: payload,
|
|
166
193
|
humanSummary: `Found ${tools.length} tools`,
|
|
167
194
|
hints: firstTool
|
|
168
195
|
? [
|
|
@@ -172,31 +199,18 @@ async function toolsSearchHandler(ctx) {
|
|
|
172
199
|
: [],
|
|
173
200
|
renderHuman: () =>
|
|
174
201
|
tools
|
|
175
|
-
.map((tool) => `${tool.name}\n ${tool.
|
|
202
|
+
.map((tool) => `${tool.name}\n toolkit: ${tool.toolkit || 'unknown'} (${tool.connection_status})\n schema: ${tool.schema_available ? 'available' : 'missing'}\n ${tool.description || 'No description'}\n`)
|
|
176
203
|
.join('\n')
|
|
177
204
|
.trim(),
|
|
178
205
|
});
|
|
179
206
|
}
|
|
180
207
|
|
|
181
208
|
async function toolsDescribeHandler(ctx) {
|
|
182
|
-
const
|
|
183
|
-
const result = await runToolCommand({
|
|
184
|
-
runtime: ctx.runtime,
|
|
185
|
-
toolName: 'notis_find_tools',
|
|
186
|
-
arguments_: {
|
|
187
|
-
query: ctx.args.toolName,
|
|
188
|
-
toolkits,
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
const tools = result.payload.tools || [];
|
|
192
|
-
const match = tools.find((tool) => tool.name === ctx.args.toolName);
|
|
193
|
-
if (!match) {
|
|
194
|
-
throw usageError(`Tool ${ctx.args.toolName} was not found in the selected toolkits.`);
|
|
195
|
-
}
|
|
209
|
+
const match = await fetchToolSchema(ctx.runtime, ctx.args.toolName);
|
|
196
210
|
|
|
197
211
|
return ctx.output.emitSuccess({
|
|
198
212
|
command: ctx.spec.command_path.join(' '),
|
|
199
|
-
data: { tool: match
|
|
213
|
+
data: { tool: match },
|
|
200
214
|
humanSummary: `Described tool ${ctx.args.toolName}`,
|
|
201
215
|
hints: [
|
|
202
216
|
{ command: `notis tools exec ${ctx.args.toolName} --dry-run --arguments '{}'`, reason: 'Validate arguments first' },
|
|
@@ -208,14 +222,13 @@ async function toolsDescribeHandler(ctx) {
|
|
|
208
222
|
|
|
209
223
|
async function toolsExecHandler(ctx) {
|
|
210
224
|
ensureLongRunningToolTimeout(ctx, ctx.args.toolName);
|
|
211
|
-
const toolkits = normalizeToolkits(ctx.options.toolkits);
|
|
212
225
|
const fileBindings = await parseFileBindings(ctx.options.file);
|
|
213
226
|
|
|
214
227
|
if (ctx.options.getSchema) {
|
|
215
|
-
const tool = await fetchToolSchema(ctx.runtime, ctx.args.toolName
|
|
228
|
+
const tool = await fetchToolSchema(ctx.runtime, ctx.args.toolName);
|
|
216
229
|
return ctx.output.emitSuccess({
|
|
217
230
|
command: ctx.spec.command_path.join(' '),
|
|
218
|
-
data: { tool
|
|
231
|
+
data: { tool },
|
|
219
232
|
humanSummary: `Schema for ${ctx.args.toolName}`,
|
|
220
233
|
hints: [
|
|
221
234
|
{ command: `notis tools exec ${ctx.args.toolName} --dry-run --arguments '{}'`, reason: 'Validate arguments before executing' },
|
|
@@ -233,13 +246,13 @@ async function toolsExecHandler(ctx) {
|
|
|
233
246
|
if (fileBindings.length) {
|
|
234
247
|
throw usageError('--file is only supported when executing a tool, not with --dry-run');
|
|
235
248
|
}
|
|
236
|
-
const tool = await fetchToolSchema(ctx.runtime, ctx.args.toolName
|
|
249
|
+
const tool = await fetchToolSchema(ctx.runtime, ctx.args.toolName);
|
|
237
250
|
const errors = validateArguments(tool.parameters, args);
|
|
238
251
|
|
|
239
252
|
if (errors.length) {
|
|
240
253
|
return ctx.output.emitSuccess({
|
|
241
254
|
command: ctx.spec.command_path.join(' '),
|
|
242
|
-
data: { valid: false, errors, tool: ctx.args.toolName
|
|
255
|
+
data: { valid: false, errors, tool: ctx.args.toolName },
|
|
243
256
|
humanSummary: `Dry run failed: ${errors.length} validation error(s)`,
|
|
244
257
|
hints: [
|
|
245
258
|
{ command: `notis tools exec ${ctx.args.toolName} --get-schema`, reason: 'Review the full parameter schema' },
|
|
@@ -250,7 +263,7 @@ async function toolsExecHandler(ctx) {
|
|
|
250
263
|
|
|
251
264
|
return ctx.output.emitSuccess({
|
|
252
265
|
command: ctx.spec.command_path.join(' '),
|
|
253
|
-
data: { valid: true, tool: ctx.args.toolName, arguments: args
|
|
266
|
+
data: { valid: true, tool: ctx.args.toolName, arguments: args },
|
|
254
267
|
humanSummary: `Dry run passed for ${ctx.args.toolName}`,
|
|
255
268
|
hints: [
|
|
256
269
|
{ command: `notis tools exec ${ctx.args.toolName} --arguments '${JSON.stringify(args)}'`, reason: 'Execute with these arguments' },
|
|
@@ -266,7 +279,6 @@ async function toolsExecHandler(ctx) {
|
|
|
266
279
|
arguments_: {
|
|
267
280
|
tool_name: ctx.args.toolName,
|
|
268
281
|
arguments: args,
|
|
269
|
-
...(toolkits.length ? { toolkits } : {}),
|
|
270
282
|
},
|
|
271
283
|
mutating: true,
|
|
272
284
|
idempotencyKey,
|
|
@@ -369,15 +381,15 @@ async function toolsLinkHandler(ctx) {
|
|
|
369
381
|
export const toolsCommandSpecs = [
|
|
370
382
|
{
|
|
371
383
|
command_path: ['tools', 'toolkits'],
|
|
372
|
-
summary: 'List toolkit namespaces available to the active user.',
|
|
373
|
-
when_to_use: 'Use this before searching or executing generic tools.',
|
|
384
|
+
summary: 'List toolkit namespaces and connection statuses available to the active user.',
|
|
385
|
+
when_to_use: 'Use this to inspect connection state before searching or executing generic tools.',
|
|
374
386
|
args_schema: { arguments: [], options: [] },
|
|
375
387
|
examples: ['notis tools toolkits', 'notis tools toolkits --json'],
|
|
376
|
-
output_schema: 'Returns
|
|
388
|
+
output_schema: 'Returns toolkit_connection_statuses from notis_find_tools, rendered as toolkit rows.',
|
|
377
389
|
mutates: false,
|
|
378
390
|
idempotent: true,
|
|
379
391
|
related_commands: ['notis tools search <query>', 'notis tools describe <tool-name>'],
|
|
380
|
-
backend_call: { type: 'tool', name: '
|
|
392
|
+
backend_call: { type: 'tool', name: 'notis_find_tools' },
|
|
381
393
|
handler: toolsToolkitsHandler,
|
|
382
394
|
},
|
|
383
395
|
{
|
|
@@ -386,10 +398,10 @@ export const toolsCommandSpecs = [
|
|
|
386
398
|
when_to_use: 'Use this when you need a generic capability that does not have a first-class CLI command.',
|
|
387
399
|
args_schema: {
|
|
388
400
|
arguments: [{ token: '<query>', description: 'Natural language description of the tool you need.' }],
|
|
389
|
-
options: [{ flags: '--
|
|
401
|
+
options: [{ flags: '--known-fields <text>', key: 'knownFields', description: 'Optional known field hints, such as channel_name:general or user_email:a@example.com.' }],
|
|
390
402
|
},
|
|
391
|
-
examples: ['notis tools search "send an email"', 'notis tools search "
|
|
392
|
-
output_schema: 'Returns
|
|
403
|
+
examples: ['notis tools search "send an email"', 'notis tools search "post on LinkedIn" --known-fields "platform:linkedin"'],
|
|
404
|
+
output_schema: 'Returns the full notis_find_tools response. Human output renders canonical names, connection status, and schema availability.',
|
|
393
405
|
mutates: false,
|
|
394
406
|
idempotent: true,
|
|
395
407
|
related_commands: ['notis tools toolkits', 'notis tools exec <tool-name>'],
|
|
@@ -402,9 +414,9 @@ export const toolsCommandSpecs = [
|
|
|
402
414
|
when_to_use: 'Use this when you know the tool name and want its parameter schema before execution.',
|
|
403
415
|
args_schema: {
|
|
404
416
|
arguments: [{ token: '<tool-name>', description: 'Exact tool name to locate and describe.' }],
|
|
405
|
-
options: [
|
|
417
|
+
options: [],
|
|
406
418
|
},
|
|
407
|
-
examples: ['notis tools describe composio-gmail-default-send_email', 'notis tools describe notis-default-query
|
|
419
|
+
examples: ['notis tools describe composio-gmail-default-send_email', 'notis tools describe notis-default-query'],
|
|
408
420
|
output_schema: 'Returns one tool descriptor with name, description, and parameters.',
|
|
409
421
|
mutates: false,
|
|
410
422
|
idempotent: true,
|
|
@@ -423,7 +435,6 @@ export const toolsCommandSpecs = [
|
|
|
423
435
|
{ flags: '--file <argument-path=local-path>', description: 'Upload a local file into a file-uploadable tool argument. Repeatable.', collect: true },
|
|
424
436
|
{ flags: '--get-schema', description: 'Display the tool parameter schema without executing.' },
|
|
425
437
|
{ flags: '--dry-run', description: 'Validate arguments against the tool schema without executing.' },
|
|
426
|
-
{ flags: '--toolkits <csv-or-json>', description: 'Optional toolkit namespace(s) to use when resolving the tool.' },
|
|
427
438
|
],
|
|
428
439
|
},
|
|
429
440
|
examples: [
|
package/src/runtime/transport.js
CHANGED
|
@@ -9,33 +9,6 @@ import {
|
|
|
9
9
|
saveConfig,
|
|
10
10
|
} from './profiles.js';
|
|
11
11
|
|
|
12
|
-
const META_TOOL_NAMES = new Set([
|
|
13
|
-
'notis_find_toolkits',
|
|
14
|
-
'notis_find_tools',
|
|
15
|
-
'notis_execute_tool',
|
|
16
|
-
'notis_find_additional_tools',
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
function canonicalizeToolName(toolName) {
|
|
20
|
-
if (typeof toolName !== 'string' || !toolName.length) {
|
|
21
|
-
return toolName;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (META_TOOL_NAMES.has(toolName) || toolName.startsWith('notis-default-')) {
|
|
25
|
-
return toolName;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (toolName.startsWith('notis_')) {
|
|
29
|
-
return `notis-default-${toolName.slice('notis_'.length)}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (toolName.includes('_notis_')) {
|
|
33
|
-
return `notis-default-${toolName.replace(/_notis_/g, '_')}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return toolName;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
12
|
function escapeMultipartHeaderValue(value) {
|
|
40
13
|
return String(value ?? '')
|
|
41
14
|
.replace(/"/g, '%22')
|
|
@@ -397,7 +370,7 @@ export async function callTool({
|
|
|
397
370
|
}) {
|
|
398
371
|
const requestId = idempotencyKey || (runtime.mutating ? randomUUID() : null);
|
|
399
372
|
const payload = {
|
|
400
|
-
tool_name:
|
|
373
|
+
tool_name: toolName,
|
|
401
374
|
arguments: argumentsPayload,
|
|
402
375
|
idempotency_key: requestId,
|
|
403
376
|
cli_context: {
|