@notis_ai/cli 0.2.0-beta.16.1 → 0.2.0-beta.160.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 +433 -133
- package/config/notis_app_boundary_rules.json +50 -0
- package/config/notis_app_design_rules.json +135 -0
- package/dist/agent-hooks/notis-agent-hook.mjs +18672 -0
- package/dist/base-skills/notis-apps/SKILL.md +70 -0
- package/dist/base-skills/notis-apps/references/architecture.md +164 -0
- package/dist/base-skills/notis-apps/references/context.md +81 -0
- package/dist/base-skills/notis-apps/references/design.md +165 -0
- package/dist/base-skills/notis-apps/references/reading.md +89 -0
- package/dist/base-skills/notis-apps/references/release.md +99 -0
- package/dist/base-skills/notis-apps/references/sdk.md +62 -0
- package/dist/base-skills/notis-apps/references/troubleshooting.md +23 -0
- package/dist/base-skills/notis-cli/SKILL.md +140 -0
- package/dist/base-skills/notis-cli/references/app-delivery.md +18 -0
- package/dist/base-skills/notis-cli/references/native-databases.md +20 -0
- package/dist/base-skills/notis-cli/references/tool-examples.md +56 -0
- package/dist/base-skills/notis-cli/references/troubleshooting.md +39 -0
- package/dist/base-skills/notis-query/SKILL.md +67 -0
- package/dist/base-skills/notis-query/references/database-discovery.md +59 -0
- package/dist/base-skills/notis-query/references/documents.md +50 -0
- package/dist/base-skills/notis-query/references/query.md +543 -0
- package/dist/skill-sync/index.js +1626 -0
- package/dist/skill-sync/index.js.map +7 -0
- package/dist/skill-sync-worker.mjs +2990 -0
- package/package.json +16 -6
- package/skills/notis-apps/cli.md +313 -0
- package/skills/notis-cli/AGENT_INSTRUCTIONS.md +39 -0
- package/skills/notis-onboarding/BRIEF.md +129 -0
- package/skills/notis-query/cli.md +39 -0
- package/src/agent-hook-entry.js +5 -0
- package/src/cli.js +294 -25
- package/src/command-specs/agents.js +392 -0
- package/src/command-specs/apps.js +1470 -202
- package/src/command-specs/auth.js +114 -137
- package/src/command-specs/diagnostics.js +716 -0
- package/src/command-specs/handover.js +374 -0
- package/src/command-specs/helpers.js +84 -82
- package/src/command-specs/index.js +25 -6
- package/src/command-specs/meta.js +150 -18
- package/src/command-specs/onboarding.js +290 -0
- package/src/command-specs/profile.js +358 -0
- package/src/command-specs/reports.js +86 -0
- package/src/command-specs/skills.js +75 -0
- package/src/command-specs/smoke.js +386 -0
- package/src/command-specs/tools.js +455 -139
- package/src/runtime/agent-browser.js +632 -0
- package/src/runtime/agent-memory-state.js +126 -0
- package/src/runtime/agent-setup.js +383 -0
- package/src/runtime/app-boundary-validator.js +404 -0
- package/src/runtime/app-changelog.js +79 -0
- package/src/runtime/app-platform.js +2633 -210
- package/src/runtime/app-registry-scaffolds.js +367 -0
- package/src/runtime/app-test-server.js +292 -0
- package/src/runtime/assets/store-screenshot-dark.png +0 -0
- package/src/runtime/auth-recovery.js +110 -0
- package/src/runtime/base-skills.d.ts +20 -0
- package/src/runtime/base-skills.js +167 -0
- package/src/runtime/channel.js +133 -0
- package/src/runtime/delegated-context.js +68 -0
- package/src/runtime/errors.js +1 -0
- package/src/runtime/git.js +233 -0
- package/src/runtime/login-listener.js +15 -0
- package/src/runtime/oauth.js +2622 -0
- package/src/runtime/output.js +37 -5
- package/src/runtime/ports.js +31 -0
- package/src/runtime/profiles.js +906 -55
- package/src/runtime/skill-sync/cloud-client.ts +99 -0
- package/src/runtime/skill-sync/index.ts +697 -0
- package/src/runtime/skill-sync/local-scanner.ts +1046 -0
- package/src/runtime/skill-sync/symlink-manager.ts +433 -0
- package/src/runtime/skill-sync/sync-plan.ts +22 -0
- package/src/runtime/skill-sync/types.ts +110 -0
- package/src/runtime/skill-sync/write-cloud-skill.ts +50 -0
- package/src/runtime/skill-sync-service.js +109 -0
- package/src/runtime/store-screenshot.js +143 -0
- package/src/runtime/sync-skills.d.ts +37 -0
- package/src/runtime/sync-skills.js +231 -0
- package/src/runtime/telemetry.js +92 -0
- package/src/runtime/transport.js +324 -45
- package/src/skill-sync-worker-entry.js +2 -0
- package/src/skill-sync-worker.js +50 -0
- package/template/.harness/index.html.tmpl +430 -0
- package/template/CHANGELOG.md +5 -0
- package/template/app/layout.tsx +5 -2
- package/template/app/page.tsx +49 -42
- package/template/components/page-heading.tsx +23 -0
- package/template/components/ui/badge.tsx +7 -4
- package/template/components/ui/card.tsx +24 -11
- package/template/components/ui/native-select.tsx +24 -0
- package/template/notis.config.ts +24 -6
- package/template/package-lock.json +4137 -0
- package/template/package.json +5 -5
- package/template/packages/{notis-sdk → sdk}/package.json +13 -3
- package/template/packages/sdk/src/agentContext.ts +36 -0
- package/template/packages/sdk/src/components/DocumentEditor.tsx +103 -0
- package/template/packages/sdk/src/components/Markdown.tsx +60 -0
- package/template/packages/sdk/src/components/MarkdownEditor.tsx +121 -0
- package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +285 -0
- package/template/packages/sdk/src/components/MultiSelectCheckbox.tsx +97 -0
- package/template/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/template/packages/sdk/src/components/NotisCommentBoundary.tsx +172 -0
- package/template/packages/sdk/src/components/NotisSelectionBoundary.tsx +59 -0
- package/template/packages/sdk/src/components/ShortcutHints.tsx +56 -0
- package/template/packages/sdk/src/components/Skeleton.tsx +24 -0
- package/template/packages/sdk/src/config.ts +257 -0
- package/template/packages/sdk/src/documents.ts +256 -0
- package/template/packages/sdk/src/hooks/useActiveResource.ts +19 -0
- package/template/packages/sdk/src/hooks/useAgentContext.ts +23 -0
- package/template/packages/sdk/src/hooks/useCloudComputer.ts +64 -0
- package/template/packages/sdk/src/hooks/useCollectionInteractions.ts +836 -0
- package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +49 -0
- package/template/packages/sdk/src/hooks/useDatabaseSubscription.ts +76 -0
- package/template/packages/sdk/src/hooks/useDocument.ts +43 -0
- package/template/packages/sdk/src/hooks/useDocuments.ts +84 -0
- package/template/packages/sdk/src/hooks/useHandover.ts +78 -0
- package/template/packages/sdk/src/hooks/useLongPressSelection.ts +79 -0
- package/template/packages/sdk/src/hooks/useMultiSelect.ts +95 -0
- package/template/packages/{notis-sdk → sdk}/src/hooks/useNotis.ts +10 -4
- package/template/packages/{notis-sdk → sdk}/src/hooks/useNotisNavigation.ts +11 -8
- package/template/packages/sdk/src/hooks/useQuery.ts +71 -0
- package/template/packages/sdk/src/hooks/useTool.ts +65 -0
- package/template/packages/sdk/src/hooks/useToolQuery.ts +12 -0
- package/template/packages/sdk/src/hooks/useTopBarSearch.ts +81 -0
- package/template/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/template/packages/sdk/src/index.ts +161 -0
- package/template/packages/sdk/src/interactions/actions.ts +59 -0
- package/template/packages/sdk/src/interactions/shortcuts.tsx +694 -0
- package/template/packages/sdk/src/interactions/visibility.ts +13 -0
- package/template/packages/sdk/src/interactions.ts +45 -0
- package/template/packages/sdk/src/provider.tsx +44 -0
- package/template/packages/sdk/src/queryCache.ts +170 -0
- package/template/packages/sdk/src/runtime.ts +451 -0
- package/template/packages/sdk/src/styles.css +213 -0
- package/template/packages/sdk/src/tailwind.ts +56 -0
- package/template/packages/{notis-sdk → sdk}/src/vite.ts +5 -1
- package/template/tailwind.config.ts +1 -0
- package/src/command-specs/db.js +0 -163
- package/src/runtime/app-preview-server.js +0 -312
- package/template/packages/notis-sdk/src/config.ts +0 -48
- package/template/packages/notis-sdk/src/helpers.ts +0 -131
- package/template/packages/notis-sdk/src/hooks/useAppState.ts +0 -50
- package/template/packages/notis-sdk/src/hooks/useCollectionItem.ts +0 -58
- package/template/packages/notis-sdk/src/hooks/useDatabase.ts +0 -87
- package/template/packages/notis-sdk/src/hooks/useDocument.ts +0 -61
- package/template/packages/notis-sdk/src/hooks/useTool.ts +0 -49
- package/template/packages/notis-sdk/src/hooks/useUpsertDocument.ts +0 -57
- package/template/packages/notis-sdk/src/index.ts +0 -47
- package/template/packages/notis-sdk/src/provider.tsx +0 -44
- package/template/packages/notis-sdk/src/runtime.ts +0 -159
- package/template/packages/notis-sdk/src/styles.css +0 -123
- /package/template/packages/{notis-sdk → sdk}/src/hooks/useBackend.ts +0 -0
- /package/template/packages/{notis-sdk → sdk}/src/hooks/useTools.ts +0 -0
- /package/template/packages/{notis-sdk → sdk}/src/ui.ts +0 -0
- /package/template/packages/{notis-sdk → sdk}/tsconfig.json +0 -0
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
1
|
+
import { accessSync, constants as fsConstants, createReadStream, existsSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { basename } from 'node:path';
|
|
2
4
|
import { usageError } from '../runtime/errors.js';
|
|
5
|
+
import { assertToolNotDelegated } from '../runtime/delegated-context.js';
|
|
3
6
|
import {
|
|
7
|
+
COMPOSIO_GET_TOOL_SCHEMAS,
|
|
8
|
+
COMPOSIO_MULTI_EXECUTE_TOOL,
|
|
9
|
+
COMPOSIO_SEARCH_TOOLS,
|
|
10
|
+
fetchToolDiscovery,
|
|
4
11
|
fetchToolkits,
|
|
5
12
|
fetchToolSchema,
|
|
13
|
+
localNotisToolSlug,
|
|
6
14
|
nextIdempotencyKey,
|
|
7
15
|
parseJson,
|
|
8
16
|
parseMaybeJson,
|
|
9
|
-
resolveSearchToolkits,
|
|
10
17
|
runToolCommand,
|
|
11
18
|
validateArguments,
|
|
12
19
|
} from './helpers.js';
|
|
13
20
|
|
|
14
|
-
async function resolveJsonInput(value, label) {
|
|
21
|
+
export async function resolveJsonInput(value, label) {
|
|
15
22
|
if (!value) return undefined;
|
|
16
23
|
|
|
17
24
|
if (value === '-') {
|
|
@@ -33,6 +40,263 @@ async function resolveJsonInput(value, label) {
|
|
|
33
40
|
return parseJson(value, label);
|
|
34
41
|
}
|
|
35
42
|
|
|
43
|
+
const READ_ACTIONS = new Set([
|
|
44
|
+
'CHECK',
|
|
45
|
+
'DESCRIBE',
|
|
46
|
+
'DOWNLOAD',
|
|
47
|
+
'FETCH',
|
|
48
|
+
'FIND',
|
|
49
|
+
'GET',
|
|
50
|
+
'INSPECT',
|
|
51
|
+
'LIST',
|
|
52
|
+
'LOOKUP',
|
|
53
|
+
'PREVIEW',
|
|
54
|
+
'QUERY',
|
|
55
|
+
'READ',
|
|
56
|
+
'SEARCH',
|
|
57
|
+
'STATUS',
|
|
58
|
+
'VERIFY',
|
|
59
|
+
]);
|
|
60
|
+
const WRITE_ACTIONS = new Set([
|
|
61
|
+
'AUTHENTIFY',
|
|
62
|
+
'CANCEL',
|
|
63
|
+
'CONNECT',
|
|
64
|
+
'COPY',
|
|
65
|
+
'CREATE',
|
|
66
|
+
'DELETE',
|
|
67
|
+
'DEPLOY',
|
|
68
|
+
'DISCARD',
|
|
69
|
+
'EDIT',
|
|
70
|
+
'GRANT',
|
|
71
|
+
'INCREMENT',
|
|
72
|
+
'INSERT',
|
|
73
|
+
'LINK',
|
|
74
|
+
'MOVE',
|
|
75
|
+
'POST',
|
|
76
|
+
'PUBLISH',
|
|
77
|
+
'REFUND',
|
|
78
|
+
'REMOVE',
|
|
79
|
+
'RENAME',
|
|
80
|
+
'SAVE',
|
|
81
|
+
'SEND',
|
|
82
|
+
'SUBMIT',
|
|
83
|
+
'UPDATE',
|
|
84
|
+
'UPLOAD',
|
|
85
|
+
'UPSERT',
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
function splitSqlStatements(query) {
|
|
89
|
+
const statements = [];
|
|
90
|
+
let current = '';
|
|
91
|
+
let quote = null;
|
|
92
|
+
let dollarTag = null;
|
|
93
|
+
for (let index = 0; index < query.length; index += 1) {
|
|
94
|
+
const char = query[index];
|
|
95
|
+
if (dollarTag) {
|
|
96
|
+
if (query.startsWith(dollarTag, index)) {
|
|
97
|
+
current += dollarTag;
|
|
98
|
+
index += dollarTag.length - 1;
|
|
99
|
+
dollarTag = null;
|
|
100
|
+
} else {
|
|
101
|
+
current += char;
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (quote) {
|
|
106
|
+
current += char;
|
|
107
|
+
if (char === quote) {
|
|
108
|
+
if (query[index + 1] === quote) {
|
|
109
|
+
current += query[index + 1];
|
|
110
|
+
index += 1;
|
|
111
|
+
} else {
|
|
112
|
+
quote = null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (char === "'" || char === '"') {
|
|
118
|
+
quote = char;
|
|
119
|
+
current += char;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (char === '$') {
|
|
123
|
+
const match = query.slice(index).match(/^\$(?:[A-Za-z_][A-Za-z0-9_]*)?\$/);
|
|
124
|
+
if (match) {
|
|
125
|
+
dollarTag = match[0];
|
|
126
|
+
current += dollarTag;
|
|
127
|
+
index += dollarTag.length - 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (char === ';') {
|
|
132
|
+
if (current.trim()) statements.push(current.trim());
|
|
133
|
+
current = '';
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
current += char;
|
|
137
|
+
}
|
|
138
|
+
if (current.trim()) statements.push(current.trim());
|
|
139
|
+
return statements;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function classifySqlMutation(query) {
|
|
143
|
+
const statements = splitSqlStatements(query);
|
|
144
|
+
if (statements.length > 1) {
|
|
145
|
+
const classifications = statements.map(classifySqlMutation);
|
|
146
|
+
if (classifications.includes(true)) return true;
|
|
147
|
+
if (classifications.every((classification) => classification === false)) return false;
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const statement = statements[0] || '';
|
|
151
|
+
if (statement) {
|
|
152
|
+
const normalizedStatement = statement.toUpperCase();
|
|
153
|
+
if (/^EXPLAIN\b/.test(normalizedStatement)) {
|
|
154
|
+
const executes = /^EXPLAIN\s+(?:\([^)]*\bANALYZE\b[^)]*\)\s*|ANALYZE\s+)/.test(normalizedStatement);
|
|
155
|
+
if (!executes) return false;
|
|
156
|
+
return /\b(ALTER|CREATE|DELETE|DROP|GRANT|INSERT|MERGE|REVOKE|TRUNCATE|UPDATE)\b/.test(normalizedStatement);
|
|
157
|
+
}
|
|
158
|
+
if (/^SHOW\b/.test(normalizedStatement)) return false;
|
|
159
|
+
// PostgreSQL SELECT can call side-effecting functions. Without catalog
|
|
160
|
+
// knowledge it is not provably read-only, so preserve idempotency and report
|
|
161
|
+
// an unknown classification instead of claiming mutating=false.
|
|
162
|
+
if (/^SELECT\b/.test(normalizedStatement)) return null;
|
|
163
|
+
if (/^WITH\b/.test(normalizedStatement)) {
|
|
164
|
+
return /\b(ALTER|CREATE|DELETE|DROP|GRANT|INSERT|MERGE|REVOKE|TRUNCATE|UPDATE)\b/.test(normalizedStatement)
|
|
165
|
+
? true
|
|
166
|
+
: null;
|
|
167
|
+
}
|
|
168
|
+
if (/^(ALTER|CREATE|DELETE|DROP|GRANT|INSERT|MERGE|REVOKE|TRUNCATE|UPDATE)\b/.test(normalizedStatement)) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function classifyToolMutation(toolName, args = {}) {
|
|
176
|
+
const normalized = localNotisToolSlug(toolName).toUpperCase();
|
|
177
|
+
if (normalized === 'LOCAL_NOTIS_SANDBOX_PREVIEW') {
|
|
178
|
+
if (args.action === 'status') return false;
|
|
179
|
+
if (['register', 'open', 'heartbeat', 'stop'].includes(args.action)) return true;
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
if (normalized.includes('EXECUTE_SQL') && typeof args.query === 'string') {
|
|
183
|
+
const query = args.query
|
|
184
|
+
.replace(/\/\*[\s\S]*?\*\//g, ' ')
|
|
185
|
+
.replace(/--[^\n]*/g, ' ')
|
|
186
|
+
.trim();
|
|
187
|
+
const sqlClassification = classifySqlMutation(query);
|
|
188
|
+
if (sqlClassification !== null) return sqlClassification;
|
|
189
|
+
}
|
|
190
|
+
if (/(?:^|_)(?:FIND|GET|LOOKUP|SEARCH)_OR_(?:CREATE|INSERT|SAVE|UPDATE|UPSERT)(?:_|$)/.test(normalized)) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
const tokens = normalized.split(/[^A-Z0-9]+/).filter(Boolean);
|
|
194
|
+
for (const token of tokens) {
|
|
195
|
+
if (WRITE_ACTIONS.has(token)) return true;
|
|
196
|
+
if (READ_ACTIONS.has(token)) return false;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const LONG_RUNNING_TOOL_TIMEOUT_MS = 600000;
|
|
202
|
+
const LONG_RUNNING_TOOL_NAMES = new Set([
|
|
203
|
+
'LOCAL_NOTIS_GENERATE_IMAGE_OPENAI',
|
|
204
|
+
'LOCAL_NOTIS_EDIT_IMAGE_OPENAI',
|
|
205
|
+
'LOCAL_NOTIS_GENERATE_IMAGE_GEMINI',
|
|
206
|
+
'LOCAL_NOTIS_EDIT_IMAGE_GEMINI',
|
|
207
|
+
]);
|
|
208
|
+
|
|
209
|
+
const EXTENSION_CONTENT_TYPES = new Map([
|
|
210
|
+
['.pdf', 'application/pdf'],
|
|
211
|
+
['.png', 'image/png'],
|
|
212
|
+
['.jpg', 'image/jpeg'],
|
|
213
|
+
['.jpeg', 'image/jpeg'],
|
|
214
|
+
['.gif', 'image/gif'],
|
|
215
|
+
['.webp', 'image/webp'],
|
|
216
|
+
['.txt', 'text/plain'],
|
|
217
|
+
['.csv', 'text/csv'],
|
|
218
|
+
['.json', 'application/json'],
|
|
219
|
+
['.md', 'text/markdown'],
|
|
220
|
+
]);
|
|
221
|
+
|
|
222
|
+
function guessContentType(filePath) {
|
|
223
|
+
const lower = filePath.toLowerCase();
|
|
224
|
+
const dotIndex = lower.lastIndexOf('.');
|
|
225
|
+
if (dotIndex === -1) return 'application/octet-stream';
|
|
226
|
+
return EXTENSION_CONTENT_TYPES.get(lower.slice(dotIndex)) || 'application/octet-stream';
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export async function hashFileSha256(localPath) {
|
|
230
|
+
const hash = createHash('sha256');
|
|
231
|
+
for await (const chunk of createReadStream(localPath)) {
|
|
232
|
+
hash.update(chunk);
|
|
233
|
+
}
|
|
234
|
+
return hash.digest('hex');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function parseFileBindingSpec(spec, index) {
|
|
238
|
+
if (typeof spec !== 'string' || !spec.trim()) {
|
|
239
|
+
throw usageError('--file must be formatted as <argument-path>=<local-path>');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const equalsIndex = spec.indexOf('=');
|
|
243
|
+
if (equalsIndex <= 0 || equalsIndex === spec.length - 1) {
|
|
244
|
+
throw usageError('--file must be formatted as <argument-path>=<local-path>');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const argumentPath = spec.slice(0, equalsIndex).trim();
|
|
248
|
+
const localPath = spec.slice(equalsIndex + 1).trim();
|
|
249
|
+
if (!argumentPath) {
|
|
250
|
+
throw usageError('--file argument path is required');
|
|
251
|
+
}
|
|
252
|
+
if (!localPath) {
|
|
253
|
+
throw usageError('--file local path is required');
|
|
254
|
+
}
|
|
255
|
+
if (!existsSync(localPath)) {
|
|
256
|
+
throw usageError(`File not found: ${localPath}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let stats;
|
|
260
|
+
try {
|
|
261
|
+
stats = statSync(localPath);
|
|
262
|
+
accessSync(localPath, fsConstants.R_OK);
|
|
263
|
+
} catch {
|
|
264
|
+
throw usageError(`File is not readable: ${localPath}`);
|
|
265
|
+
}
|
|
266
|
+
if (!stats.isFile()) {
|
|
267
|
+
throw usageError(`File is not a regular file: ${localPath}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const sha256 = await hashFileSha256(localPath);
|
|
271
|
+
const fileBasename = basename(localPath);
|
|
272
|
+
const fieldName = `file_${index}`;
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
argument_path: argumentPath,
|
|
276
|
+
field_name: fieldName,
|
|
277
|
+
basename: fileBasename,
|
|
278
|
+
size: stats.size,
|
|
279
|
+
sha256,
|
|
280
|
+
contentType: guessContentType(fileBasename),
|
|
281
|
+
localPath,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export async function parseFileBindings(rawFileOptions) {
|
|
286
|
+
const values = Array.isArray(rawFileOptions)
|
|
287
|
+
? rawFileOptions
|
|
288
|
+
: (rawFileOptions ? [rawFileOptions] : []);
|
|
289
|
+
return Promise.all(values.map((value, index) => parseFileBindingSpec(value, index)));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function ensureLongRunningToolTimeout(ctx, toolNames) {
|
|
293
|
+
const names = Array.isArray(toolNames) ? toolNames : [toolNames];
|
|
294
|
+
if (!names.some((name) => LONG_RUNNING_TOOL_NAMES.has(localNotisToolSlug(name)))) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
ctx.runtime.timeoutMs = Math.max(ctx.runtime.timeoutMs || 0, LONG_RUNNING_TOOL_TIMEOUT_MS);
|
|
298
|
+
}
|
|
299
|
+
|
|
36
300
|
async function toolsToolkitsHandler(ctx) {
|
|
37
301
|
const toolkits = await fetchToolkits(ctx.runtime);
|
|
38
302
|
return ctx.output.emitSuccess({
|
|
@@ -40,27 +304,55 @@ async function toolsToolkitsHandler(ctx) {
|
|
|
40
304
|
data: { toolkits },
|
|
41
305
|
humanSummary: `Found ${toolkits.length} toolkits`,
|
|
42
306
|
hints: [
|
|
43
|
-
{ command: 'notis tools search <query>', reason: 'Search
|
|
307
|
+
{ command: 'notis tools search <query>', reason: 'Search canonical tools and schemas' },
|
|
44
308
|
],
|
|
45
|
-
renderHuman: () =>
|
|
309
|
+
renderHuman: () =>
|
|
310
|
+
toolkits
|
|
311
|
+
.map((entry) => {
|
|
312
|
+
const status = entry.has_active_connection ? 'connected' : 'needs auth';
|
|
313
|
+
return `${entry.id} ${status} ${entry.description}`;
|
|
314
|
+
})
|
|
315
|
+
.join('\n'),
|
|
46
316
|
});
|
|
47
317
|
}
|
|
48
318
|
|
|
319
|
+
function discoveryToolRows(payload) {
|
|
320
|
+
const schemas = payload.tool_schemas || {};
|
|
321
|
+
const statuses = new Map(
|
|
322
|
+
(payload.toolkit_connection_statuses || [])
|
|
323
|
+
.filter((entry) => entry && typeof entry.toolkit === 'string')
|
|
324
|
+
.map((entry) => [entry.toolkit, entry])
|
|
325
|
+
);
|
|
326
|
+
const seen = new Set();
|
|
327
|
+
const rows = [];
|
|
328
|
+
for (const result of payload.results || []) {
|
|
329
|
+
for (const name of [...(result.primary_tool_slugs || []), ...(result.related_tool_slugs || [])]) {
|
|
330
|
+
if (!name || seen.has(name)) continue;
|
|
331
|
+
seen.add(name);
|
|
332
|
+
const schema = schemas[name] || {};
|
|
333
|
+
const toolkit = schema.toolkit || name.split('-').slice(0, -1).join('-');
|
|
334
|
+
const status = statuses.get(toolkit);
|
|
335
|
+
rows.push({
|
|
336
|
+
name,
|
|
337
|
+
toolkit,
|
|
338
|
+
description: schema.description || '',
|
|
339
|
+
schema_available: Boolean(schemas[name]),
|
|
340
|
+
connection_status: status
|
|
341
|
+
? (status.has_active_connection ? 'connected' : 'needs auth')
|
|
342
|
+
: 'unknown',
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return rows;
|
|
347
|
+
}
|
|
348
|
+
|
|
49
349
|
async function toolsSearchHandler(ctx) {
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
runtime: ctx.runtime,
|
|
53
|
-
toolName: 'notis_find_tools',
|
|
54
|
-
arguments_: {
|
|
55
|
-
query: ctx.args.query || undefined,
|
|
56
|
-
toolkits,
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
const tools = result.payload.tools || [];
|
|
350
|
+
const payload = await fetchToolDiscovery(ctx.runtime, ctx.args.query || 'Find tools', ctx.options.knownFields || '');
|
|
351
|
+
const tools = discoveryToolRows(payload);
|
|
60
352
|
const firstTool = tools[0];
|
|
61
353
|
return ctx.output.emitSuccess({
|
|
62
354
|
command: ctx.spec.command_path.join(' '),
|
|
63
|
-
data:
|
|
355
|
+
data: payload,
|
|
64
356
|
humanSummary: `Found ${tools.length} tools`,
|
|
65
357
|
hints: firstTool
|
|
66
358
|
? [
|
|
@@ -70,70 +362,79 @@ async function toolsSearchHandler(ctx) {
|
|
|
70
362
|
: [],
|
|
71
363
|
renderHuman: () =>
|
|
72
364
|
tools
|
|
73
|
-
.map((tool) => `${tool.name}\n ${tool.
|
|
365
|
+
.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`)
|
|
74
366
|
.join('\n')
|
|
75
367
|
.trim(),
|
|
76
368
|
});
|
|
77
369
|
}
|
|
78
370
|
|
|
79
371
|
async function toolsDescribeHandler(ctx) {
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
runtime: ctx.runtime,
|
|
83
|
-
toolName: 'notis_find_tools',
|
|
84
|
-
arguments_: {
|
|
85
|
-
query: ctx.args.toolName,
|
|
86
|
-
toolkits,
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
const tools = result.payload.tools || [];
|
|
90
|
-
const match = tools.find((tool) => tool.name === ctx.args.toolName);
|
|
91
|
-
if (!match) {
|
|
92
|
-
throw usageError(`Tool ${ctx.args.toolName} was not found in the selected toolkits.`);
|
|
93
|
-
}
|
|
372
|
+
const requestedToolName = localNotisToolSlug(ctx.args.toolName);
|
|
373
|
+
const match = await fetchToolSchema(ctx.runtime, requestedToolName);
|
|
94
374
|
|
|
95
375
|
return ctx.output.emitSuccess({
|
|
96
376
|
command: ctx.spec.command_path.join(' '),
|
|
97
|
-
data: { tool: match
|
|
98
|
-
humanSummary: `Described tool ${
|
|
377
|
+
data: { tool: match },
|
|
378
|
+
humanSummary: `Described tool ${requestedToolName}`,
|
|
99
379
|
hints: [
|
|
100
|
-
{ command: `notis tools exec ${
|
|
101
|
-
{ command: `notis tools exec ${
|
|
380
|
+
{ command: `notis tools exec ${requestedToolName} --dry-run --arguments '{}'`, reason: 'Validate arguments first' },
|
|
381
|
+
{ command: `notis tools exec ${requestedToolName} --arguments '{}'`, reason: 'Execute this tool' },
|
|
102
382
|
],
|
|
103
383
|
renderHuman: () => JSON.stringify(match, null, 2),
|
|
104
384
|
});
|
|
105
385
|
}
|
|
106
386
|
|
|
107
387
|
async function toolsExecHandler(ctx) {
|
|
388
|
+
const requestedToolName = localNotisToolSlug(ctx.args.toolName);
|
|
389
|
+
// `tools exec` is the escape hatch around every first-class command, so the
|
|
390
|
+
// hand-over guard has to live here too or it guards nothing.
|
|
391
|
+
assertToolNotDelegated(requestedToolName);
|
|
392
|
+
let targetMutating = classifyToolMutation(requestedToolName);
|
|
393
|
+
ensureLongRunningToolTimeout(ctx, requestedToolName);
|
|
394
|
+
ctx.output.emitProgress({
|
|
395
|
+
phase: 'prepare',
|
|
396
|
+
message: `Resolving ${requestedToolName}`,
|
|
397
|
+
});
|
|
398
|
+
const fileBindings = await parseFileBindings(ctx.options.file);
|
|
399
|
+
|
|
108
400
|
if (ctx.options.getSchema) {
|
|
109
|
-
const tool = await fetchToolSchema(ctx.runtime,
|
|
401
|
+
const tool = await fetchToolSchema(ctx.runtime, requestedToolName);
|
|
110
402
|
return ctx.output.emitSuccess({
|
|
111
403
|
command: ctx.spec.command_path.join(' '),
|
|
112
404
|
data: { tool },
|
|
113
|
-
humanSummary: `Schema for ${
|
|
405
|
+
humanSummary: `Schema for ${requestedToolName}`,
|
|
114
406
|
hints: [
|
|
115
|
-
{ command: `notis tools exec ${
|
|
407
|
+
{ command: `notis tools exec ${requestedToolName} --dry-run --arguments '{}'`, reason: 'Validate arguments before executing' },
|
|
116
408
|
],
|
|
117
409
|
renderHuman: () => JSON.stringify(tool, null, 2),
|
|
118
410
|
});
|
|
119
411
|
}
|
|
120
412
|
|
|
121
|
-
|
|
413
|
+
if (ctx.options.argumentsFile && ctx.options.arguments) {
|
|
414
|
+
throw usageError('Use either --arguments or --arguments-file, not both.');
|
|
415
|
+
}
|
|
416
|
+
const rawArguments = ctx.options.argumentsFile
|
|
417
|
+
? `@${ctx.options.argumentsFile}`
|
|
418
|
+
: (ctx.options.arguments || '{}');
|
|
122
419
|
const args = rawArguments === '-' || rawArguments.startsWith('@')
|
|
123
420
|
? await resolveJsonInput(rawArguments, 'arguments') || {}
|
|
124
421
|
: parseMaybeJson(rawArguments, 'arguments') || {};
|
|
422
|
+
targetMutating = classifyToolMutation(requestedToolName, args);
|
|
125
423
|
|
|
126
424
|
if (ctx.options.dryRun) {
|
|
127
|
-
|
|
425
|
+
if (fileBindings.length) {
|
|
426
|
+
throw usageError('--file is only supported when executing a tool, not with --dry-run');
|
|
427
|
+
}
|
|
428
|
+
const tool = await fetchToolSchema(ctx.runtime, requestedToolName);
|
|
128
429
|
const errors = validateArguments(tool.parameters, args);
|
|
129
430
|
|
|
130
431
|
if (errors.length) {
|
|
131
432
|
return ctx.output.emitSuccess({
|
|
132
433
|
command: ctx.spec.command_path.join(' '),
|
|
133
|
-
data: { valid: false, errors, tool:
|
|
434
|
+
data: { valid: false, errors, tool: requestedToolName },
|
|
134
435
|
humanSummary: `Dry run failed: ${errors.length} validation error(s)`,
|
|
135
436
|
hints: [
|
|
136
|
-
{ command: `notis tools exec ${
|
|
437
|
+
{ command: `notis tools exec ${requestedToolName} --get-schema`, reason: 'Review the full parameter schema' },
|
|
137
438
|
],
|
|
138
439
|
renderHuman: () => errors.map((e) => ` - ${e}`).join('\n'),
|
|
139
440
|
});
|
|
@@ -141,64 +442,59 @@ async function toolsExecHandler(ctx) {
|
|
|
141
442
|
|
|
142
443
|
return ctx.output.emitSuccess({
|
|
143
444
|
command: ctx.spec.command_path.join(' '),
|
|
144
|
-
data: { valid: true, tool:
|
|
145
|
-
humanSummary: `Dry run passed for ${
|
|
445
|
+
data: { valid: true, tool: requestedToolName, arguments: args },
|
|
446
|
+
humanSummary: `Dry run passed for ${requestedToolName}`,
|
|
146
447
|
hints: [
|
|
147
|
-
{ command: `notis tools exec ${
|
|
448
|
+
{ command: `notis tools exec ${requestedToolName} --arguments '${JSON.stringify(args)}'`, reason: 'Execute with these arguments' },
|
|
148
449
|
],
|
|
149
450
|
});
|
|
150
451
|
}
|
|
151
452
|
|
|
152
453
|
const idempotencyKey = nextIdempotencyKey(ctx.globalOptions);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
process.on('SIGINT', () => process.exit(0));
|
|
158
|
-
|
|
159
|
-
while (true) {
|
|
160
|
-
try {
|
|
161
|
-
const result = await runToolCommand({
|
|
162
|
-
runtime: ctx.runtime,
|
|
163
|
-
toolName: 'notis_execute_tool',
|
|
164
|
-
arguments_: { tool_name: ctx.args.toolName, arguments: args },
|
|
165
|
-
mutating: true,
|
|
166
|
-
idempotencyKey: nextIdempotencyKey(ctx.globalOptions),
|
|
167
|
-
});
|
|
168
|
-
ctx.output.emitSuccess({
|
|
169
|
-
command: ctx.spec.command_path.join(' '),
|
|
170
|
-
data: result.payload,
|
|
171
|
-
humanSummary: `[${new Date().toISOString()}] ${ctx.args.toolName}`,
|
|
172
|
-
renderHuman: () => JSON.stringify(result.payload, null, 2),
|
|
173
|
-
});
|
|
174
|
-
} catch (error) {
|
|
175
|
-
process.stderr.write(`[${new Date().toISOString()}] Error: ${error.message}\n`);
|
|
176
|
-
}
|
|
177
|
-
await new Promise((r) => setTimeout(r, intervalMs));
|
|
178
|
-
}
|
|
179
|
-
}
|
|
454
|
+
ctx.output.emitProgress({
|
|
455
|
+
phase: 'execute',
|
|
456
|
+
message: `Calling ${requestedToolName}`,
|
|
457
|
+
});
|
|
180
458
|
|
|
181
459
|
const result = await runToolCommand({
|
|
182
460
|
runtime: ctx.runtime,
|
|
183
|
-
toolName:
|
|
461
|
+
toolName: COMPOSIO_MULTI_EXECUTE_TOOL,
|
|
184
462
|
arguments_: {
|
|
185
|
-
|
|
186
|
-
arguments: args,
|
|
463
|
+
tools: [{ tool_slug: requestedToolName, arguments: args }],
|
|
187
464
|
},
|
|
465
|
+
// The server is authoritative about provider/MCP effects. A tool name that
|
|
466
|
+
// looks like a read is not proof of read-only behavior, so generic
|
|
467
|
+
// executions always carry a key and use mutation-safe transport retries.
|
|
468
|
+
// Proven reads are still executed in the read lane by hosted MCP.
|
|
188
469
|
mutating: true,
|
|
189
470
|
idempotencyKey,
|
|
471
|
+
fileBindings,
|
|
472
|
+
});
|
|
473
|
+
ctx.output.emitProgress({
|
|
474
|
+
phase: 'complete',
|
|
475
|
+
message: `Finished ${requestedToolName}`,
|
|
476
|
+
requestId: result.requestId,
|
|
190
477
|
});
|
|
191
478
|
|
|
192
479
|
return ctx.output.emitSuccess({
|
|
193
480
|
command: ctx.spec.command_path.join(' '),
|
|
194
481
|
data: result.payload,
|
|
195
|
-
humanSummary: `Executed tool ${
|
|
196
|
-
|
|
482
|
+
humanSummary: `Executed tool ${requestedToolName}`,
|
|
483
|
+
requestId: result.requestId,
|
|
484
|
+
meta: {
|
|
485
|
+
mutating: targetMutating,
|
|
486
|
+
mutation_classification: targetMutating === null ? 'unknown' : 'name_hint_only',
|
|
487
|
+
idempotency_key: idempotencyKey,
|
|
488
|
+
},
|
|
197
489
|
renderHuman: () => JSON.stringify(result.payload, null, 2),
|
|
198
490
|
});
|
|
199
491
|
}
|
|
200
492
|
|
|
201
493
|
async function toolsExecParallelHandler(ctx) {
|
|
494
|
+
const parallelFileBindings = await parseFileBindings(ctx.options.file);
|
|
495
|
+
if (parallelFileBindings.length) {
|
|
496
|
+
throw usageError('--file is supported by `notis tools exec` only; exec-parallel accepts JSON calls without file uploads.');
|
|
497
|
+
}
|
|
202
498
|
const calls = parseMaybeJson(ctx.args.calls, 'calls');
|
|
203
499
|
if (!Array.isArray(calls) || !calls.length) {
|
|
204
500
|
throw usageError('calls must be a non-empty JSON array of {tool_name, arguments} objects');
|
|
@@ -208,16 +504,18 @@ async function toolsExecParallelHandler(ctx) {
|
|
|
208
504
|
if (!call.tool_name || typeof call.tool_name !== 'string') {
|
|
209
505
|
throw usageError(`calls[${i}] missing required "tool_name" string`);
|
|
210
506
|
}
|
|
507
|
+
// A batch is just as good an escape hatch as a single exec.
|
|
508
|
+
assertToolNotDelegated(localNotisToolSlug(call.tool_name));
|
|
211
509
|
}
|
|
510
|
+
ensureLongRunningToolTimeout(ctx, calls.map((call) => localNotisToolSlug(call.tool_name)));
|
|
212
511
|
|
|
213
512
|
const data = await Promise.all(
|
|
214
513
|
calls.map((call) =>
|
|
215
514
|
runToolCommand({
|
|
216
515
|
runtime: ctx.runtime,
|
|
217
|
-
toolName:
|
|
516
|
+
toolName: COMPOSIO_MULTI_EXECUTE_TOOL,
|
|
218
517
|
arguments_: {
|
|
219
|
-
|
|
220
|
-
arguments: call.arguments || {},
|
|
518
|
+
tools: [{ tool_slug: localNotisToolSlug(call.tool_name), arguments: call.arguments || {} }],
|
|
221
519
|
},
|
|
222
520
|
mutating: true,
|
|
223
521
|
idempotencyKey: nextIdempotencyKey(ctx.globalOptions),
|
|
@@ -237,58 +535,63 @@ async function toolsExecParallelHandler(ctx) {
|
|
|
237
535
|
}
|
|
238
536
|
|
|
239
537
|
async function toolsLinkHandler(ctx) {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
538
|
+
const credentials = ctx.options.credentials
|
|
539
|
+
? await resolveJsonInput(ctx.options.credentials, 'credentials')
|
|
540
|
+
: undefined;
|
|
541
|
+
if (credentials !== undefined && (typeof credentials !== 'object' || credentials === null || Array.isArray(credentials))) {
|
|
542
|
+
throw usageError('credentials must be a JSON object');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const arguments_ = {
|
|
546
|
+
toolkit: ctx.args.toolkit,
|
|
547
|
+
...(ctx.options.reconnect ? { reconnect: true } : {}),
|
|
548
|
+
...(ctx.options.connectionId ? { reconnect_connection_id: ctx.options.connectionId } : {}),
|
|
549
|
+
...(ctx.options.label ? { label: ctx.options.label } : {}),
|
|
550
|
+
...(credentials ? { credentials } : {}),
|
|
551
|
+
};
|
|
552
|
+
const idempotencyKey = nextIdempotencyKey(ctx.globalOptions);
|
|
553
|
+
const result = await runToolCommand({
|
|
554
|
+
runtime: ctx.runtime,
|
|
555
|
+
toolName: 'LOCAL_NOTIS_AUTHENTIFY',
|
|
556
|
+
arguments_,
|
|
557
|
+
mutating: true,
|
|
558
|
+
idempotencyKey,
|
|
559
|
+
});
|
|
560
|
+
const payload = result.payload || {};
|
|
561
|
+
if (payload.status === 'error') {
|
|
562
|
+
throw usageError(payload.message || `Failed to connect ${ctx.args.toolkit}`, payload);
|
|
256
563
|
}
|
|
257
|
-
const
|
|
564
|
+
const authUrl = payload.redirect_url || payload.integrations_url || payload.url || null;
|
|
258
565
|
|
|
259
566
|
return ctx.output.emitSuccess({
|
|
260
567
|
command: ctx.spec.command_path.join(' '),
|
|
261
568
|
data: {
|
|
262
|
-
|
|
263
|
-
auth_url:
|
|
569
|
+
...payload,
|
|
570
|
+
auth_url: authUrl,
|
|
264
571
|
},
|
|
265
|
-
humanSummary:
|
|
572
|
+
humanSummary: ctx.options.reconnect
|
|
573
|
+
? `Reconnected ${ctx.args.toolkit}`
|
|
574
|
+
: `Started connection for ${ctx.args.toolkit}`,
|
|
266
575
|
hints: [
|
|
267
576
|
{ command: 'notis tools toolkits', reason: 'Verify the toolkit is connected after setup' },
|
|
268
577
|
],
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
`Connect ${ctx.args.toolkit}:`,
|
|
272
|
-
` ${portalUrl}`,
|
|
273
|
-
'',
|
|
274
|
-
'After connecting, verify with:',
|
|
275
|
-
' notis tools toolkits',
|
|
276
|
-
].join('\n'),
|
|
578
|
+
meta: { mutating: true, idempotency_key: idempotencyKey },
|
|
579
|
+
renderHuman: () => JSON.stringify({ ...payload, auth_url: authUrl }, null, 2),
|
|
277
580
|
});
|
|
278
581
|
}
|
|
279
582
|
|
|
280
583
|
export const toolsCommandSpecs = [
|
|
281
584
|
{
|
|
282
585
|
command_path: ['tools', 'toolkits'],
|
|
283
|
-
summary: 'List toolkit namespaces available to the active user.',
|
|
284
|
-
when_to_use: 'Use this before searching or executing generic tools.',
|
|
586
|
+
summary: 'List toolkit namespaces and connection statuses available to the active user.',
|
|
587
|
+
when_to_use: 'Use this to inspect connection state before searching or executing generic tools.',
|
|
285
588
|
args_schema: { arguments: [], options: [] },
|
|
286
589
|
examples: ['notis tools toolkits', 'notis tools toolkits --json'],
|
|
287
|
-
output_schema: 'Returns
|
|
590
|
+
output_schema: 'Returns toolkit_connection_statuses from COMPOSIO_SEARCH_TOOLS, rendered as toolkit rows.',
|
|
288
591
|
mutates: false,
|
|
289
592
|
idempotent: true,
|
|
290
593
|
related_commands: ['notis tools search <query>', 'notis tools describe <tool-name>'],
|
|
291
|
-
backend_call: { type: 'tool', name:
|
|
594
|
+
backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
|
|
292
595
|
handler: toolsToolkitsHandler,
|
|
293
596
|
},
|
|
294
597
|
{
|
|
@@ -297,14 +600,14 @@ export const toolsCommandSpecs = [
|
|
|
297
600
|
when_to_use: 'Use this when you need a generic capability that does not have a first-class CLI command.',
|
|
298
601
|
args_schema: {
|
|
299
602
|
arguments: [{ token: '<query>', description: 'Natural language description of the tool you need.' }],
|
|
300
|
-
options: [{ flags: '--
|
|
603
|
+
options: [{ flags: '--known-fields <text>', key: 'knownFields', description: 'Optional known field hints, such as channel_name:general or user_email:a@example.com.' }],
|
|
301
604
|
},
|
|
302
|
-
examples: ['notis tools search "send an email"', 'notis tools search "
|
|
303
|
-
output_schema: 'Returns
|
|
605
|
+
examples: ['notis tools search "send an email"', 'notis tools search "post on LinkedIn" --known-fields "platform:linkedin"'],
|
|
606
|
+
output_schema: 'Returns the full COMPOSIO_SEARCH_TOOLS response. Human output renders canonical names, connection status, and schema availability.',
|
|
304
607
|
mutates: false,
|
|
305
608
|
idempotent: true,
|
|
306
609
|
related_commands: ['notis tools toolkits', 'notis tools exec <tool-name>'],
|
|
307
|
-
backend_call: { type: 'tool', name:
|
|
610
|
+
backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
|
|
308
611
|
handler: toolsSearchHandler,
|
|
309
612
|
},
|
|
310
613
|
{
|
|
@@ -313,14 +616,14 @@ export const toolsCommandSpecs = [
|
|
|
313
616
|
when_to_use: 'Use this when you know the tool name and want its parameter schema before execution.',
|
|
314
617
|
args_schema: {
|
|
315
618
|
arguments: [{ token: '<tool-name>', description: 'Exact tool name to locate and describe.' }],
|
|
316
|
-
options: [
|
|
619
|
+
options: [],
|
|
317
620
|
},
|
|
318
|
-
examples: ['notis tools describe composio-gmail-
|
|
621
|
+
examples: ['notis tools describe composio-gmail-send_email', 'notis tools describe LOCAL_NOTIS_DATABASE_QUERY'],
|
|
319
622
|
output_schema: 'Returns one tool descriptor with name, description, and parameters.',
|
|
320
623
|
mutates: false,
|
|
321
624
|
idempotent: true,
|
|
322
625
|
related_commands: ['notis tools search <query>', 'notis tools exec <tool-name>'],
|
|
323
|
-
backend_call: { type: 'tool', name:
|
|
626
|
+
backend_call: { type: 'tool', name: COMPOSIO_GET_TOOL_SCHEMAS },
|
|
324
627
|
handler: toolsDescribeHandler,
|
|
325
628
|
},
|
|
326
629
|
{
|
|
@@ -331,24 +634,27 @@ export const toolsCommandSpecs = [
|
|
|
331
634
|
arguments: [{ token: '<tool-name>', description: 'Tool name returned by `notis tools search`.' }],
|
|
332
635
|
options: [
|
|
333
636
|
{ flags: '--arguments <json>', description: 'JSON object, @file path, or - for stdin.' },
|
|
637
|
+
{ flags: '--arguments-file <path>', description: 'Read the JSON arguments object from a file.' },
|
|
638
|
+
{ flags: '--file <argument-path=local-path>', description: 'Upload a local file into a file-uploadable tool argument. Repeatable.', collect: true },
|
|
334
639
|
{ flags: '--get-schema', description: 'Display the tool parameter schema without executing.' },
|
|
335
640
|
{ flags: '--dry-run', description: 'Validate arguments against the tool schema without executing.' },
|
|
336
|
-
{ flags: '--watch <seconds>', description: 'Re-execute on an interval and stream results.' },
|
|
337
641
|
],
|
|
338
642
|
},
|
|
339
643
|
examples: [
|
|
340
|
-
'notis tools exec
|
|
341
|
-
'notis tools exec
|
|
342
|
-
'notis tools exec
|
|
343
|
-
'notis tools exec
|
|
344
|
-
'notis tools exec
|
|
345
|
-
'notis tools exec
|
|
644
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments \'{"database_slug":"tasks","query":{}}\'',
|
|
645
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments \'{"database_slug":"tasks"}\'',
|
|
646
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --get-schema',
|
|
647
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --dry-run --arguments \'{"database_slug":"tasks","query":{}}\'',
|
|
648
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments @query.json',
|
|
649
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments-file query.json',
|
|
650
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments - < query.json',
|
|
651
|
+
'notis tools exec composio-dropbox-upload_file --arguments \'{"path":"/target/in/dropbox.pdf"}\' --file content=./Invoice.pdf',
|
|
346
652
|
],
|
|
347
653
|
output_schema: 'Returns the raw tool execution payload.',
|
|
348
654
|
mutates: true,
|
|
349
655
|
idempotent: true,
|
|
350
656
|
related_commands: ['notis tools search <query>', 'notis tools describe <tool-name>', 'notis tools exec-parallel <calls>'],
|
|
351
|
-
backend_call: { type: 'tool', name:
|
|
657
|
+
backend_call: { type: 'tool', name: COMPOSIO_MULTI_EXECUTE_TOOL },
|
|
352
658
|
handler: toolsExecHandler,
|
|
353
659
|
},
|
|
354
660
|
{
|
|
@@ -359,33 +665,43 @@ export const toolsCommandSpecs = [
|
|
|
359
665
|
arguments: [
|
|
360
666
|
{ token: '<calls>', description: 'JSON array of {tool_name, arguments} objects.' },
|
|
361
667
|
],
|
|
362
|
-
options: [
|
|
668
|
+
options: [
|
|
669
|
+
{ flags: '--file <argument-path=local-path>', description: 'Unsupported for exec-parallel; use tools exec for file uploads.', collect: true },
|
|
670
|
+
],
|
|
363
671
|
},
|
|
364
672
|
examples: [
|
|
365
|
-
'notis tools exec-parallel \'[{"tool_name":"
|
|
673
|
+
'notis tools exec-parallel \'[{"tool_name":"LOCAL_NOTIS_DATABASE_QUERY","arguments":{"database_slug":"tasks","query":{}}},{"tool_name":"LOCAL_NOTIS_DATABASE_LIST_DATABASES","arguments":{}}]\'',
|
|
366
674
|
],
|
|
367
675
|
output_schema: 'Returns an array of results, one per tool call.',
|
|
368
676
|
mutates: true,
|
|
369
677
|
idempotent: true,
|
|
370
678
|
related_commands: ['notis tools exec <tool-name>', 'notis tools search <query>'],
|
|
371
|
-
backend_call: { type: 'tool', name:
|
|
679
|
+
backend_call: { type: 'tool', name: COMPOSIO_MULTI_EXECUTE_TOOL },
|
|
372
680
|
handler: toolsExecParallelHandler,
|
|
373
681
|
},
|
|
374
682
|
{
|
|
375
683
|
command_path: ['tools', 'link'],
|
|
376
|
-
summary: '
|
|
377
|
-
when_to_use: 'Use this when a tool requires authentication
|
|
684
|
+
summary: 'Connect or reconnect an integration toolkit.',
|
|
685
|
+
when_to_use: 'Use this when a tool requires authentication or an active connection must be replaced.',
|
|
378
686
|
args_schema: {
|
|
379
687
|
arguments: [{ token: '<toolkit>', description: 'Toolkit name to connect (e.g. github, gmail, slack).' }],
|
|
380
|
-
options: [
|
|
688
|
+
options: [
|
|
689
|
+
{ flags: '--reconnect', description: 'Replace the existing account instead of adding another connection.' },
|
|
690
|
+
{ flags: '--connection-id <id>', key: 'connectionId', description: 'Exact connection id to replace when multiple accounts exist.' },
|
|
691
|
+
{ flags: '--label <label>', description: 'Account label for a new or replacement connection.' },
|
|
692
|
+
{ flags: '--credentials <json>', description: 'Credential JSON object, @file path, or - for stdin. Prefer stdin so secrets do not enter shell history.' },
|
|
693
|
+
],
|
|
381
694
|
},
|
|
382
|
-
examples: [
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
695
|
+
examples: [
|
|
696
|
+
'notis tools link github',
|
|
697
|
+
'notis tools link dataforseo --reconnect --credentials - < credentials.json',
|
|
698
|
+
],
|
|
699
|
+
output_schema: 'Returns the connection result and an authentication URL when provider authorization is still required.',
|
|
700
|
+
mutates: true,
|
|
701
|
+
idempotent: false,
|
|
702
|
+
require_auth: true,
|
|
387
703
|
related_commands: ['notis tools toolkits', 'notis tools search <query>'],
|
|
388
|
-
backend_call: { type: '
|
|
704
|
+
backend_call: { type: 'tool', name: 'LOCAL_NOTIS_AUTHENTIFY' },
|
|
389
705
|
handler: toolsLinkHandler,
|
|
390
706
|
},
|
|
391
707
|
];
|