@notis_ai/cli 0.2.0-beta.102.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 +334 -0
- package/bin/notis.js +2 -0
- package/config/notis_app_boundary_rules.json +50 -0
- package/dist/scaffolds/notis-database/CHANGELOG.md +5 -0
- package/dist/scaffolds/notis-database/app/globals.css +44 -0
- package/dist/scaffolds/notis-database/app/layout.tsx +6 -0
- package/dist/scaffolds/notis-database/app/page.tsx +1091 -0
- package/dist/scaffolds/notis-database/components/ui/badge.tsx +28 -0
- package/dist/scaffolds/notis-database/components/ui/button.tsx +53 -0
- package/dist/scaffolds/notis-database/components/ui/card.tsx +56 -0
- package/dist/scaffolds/notis-database/components/ui/table.tsx +120 -0
- package/dist/scaffolds/notis-database/components.json +20 -0
- package/dist/scaffolds/notis-database/index.html +12 -0
- package/dist/scaffolds/notis-database/lib/types.ts +134 -0
- package/dist/scaffolds/notis-database/lib/utils.ts +6 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-database/notis.config.ts +26 -0
- package/dist/scaffolds/notis-database/package.json +31 -0
- package/dist/scaffolds/notis-database/postcss.config.mjs +8 -0
- package/dist/scaffolds/notis-database/src/dev-main.tsx +23 -0
- package/dist/scaffolds/notis-database/src/mock-runtime.ts +558 -0
- package/dist/scaffolds/notis-database/tailwind.config.ts +59 -0
- package/dist/scaffolds/notis-database/tsconfig.json +23 -0
- package/dist/scaffolds/notis-database/vite.config.ts +22 -0
- package/dist/scaffolds/notis-journal/CHANGELOG.md +25 -0
- package/dist/scaffolds/notis-journal/app/globals.css +37 -0
- package/dist/scaffolds/notis-journal/app/insights/page.tsx +513 -0
- package/dist/scaffolds/notis-journal/app/journal-core.tsx +362 -0
- package/dist/scaffolds/notis-journal/app/journal-ui.tsx +337 -0
- package/dist/scaffolds/notis-journal/app/layout.tsx +6 -0
- package/dist/scaffolds/notis-journal/app/page.tsx +485 -0
- package/dist/scaffolds/notis-journal/components/ui/badge.tsx +28 -0
- package/dist/scaffolds/notis-journal/components/ui/button.tsx +53 -0
- package/dist/scaffolds/notis-journal/components/ui/card.tsx +56 -0
- package/dist/scaffolds/notis-journal/components.json +20 -0
- package/dist/scaffolds/notis-journal/index.html +12 -0
- package/dist/scaffolds/notis-journal/lib/utils.ts +6 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-6.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-fixtures.json +132 -0
- package/dist/scaffolds/notis-journal/notis.config.ts +93 -0
- package/dist/scaffolds/notis-journal/package.json +34 -0
- package/dist/scaffolds/notis-journal/packages/sdk/package.json +36 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/DocumentEditor.tsx +93 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/Markdown.tsx +60 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +145 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/documents.ts +229 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useBackend.ts +41 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocument.ts +78 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocuments.ts +121 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotis.ts +34 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTool.ts +64 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTools.ts +56 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/index.ts +83 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/provider.tsx +43 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/runtime.ts +220 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/styles.css +186 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/ui.ts +15 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/vite.ts +56 -0
- package/dist/scaffolds/notis-journal/packages/sdk/tsconfig.json +15 -0
- package/dist/scaffolds/notis-journal/postcss.config.mjs +8 -0
- package/dist/scaffolds/notis-journal/skills/journal-onboarding/SKILL.md +120 -0
- package/dist/scaffolds/notis-journal/src/dev-main.tsx +58 -0
- package/dist/scaffolds/notis-journal/src/mock-runtime.ts +197 -0
- package/dist/scaffolds/notis-journal/tailwind.config.ts +58 -0
- package/dist/scaffolds/notis-journal/tsconfig.json +23 -0
- package/dist/scaffolds/notis-journal/vite.config.ts +10 -0
- package/dist/scaffolds/notis-notes/CHANGELOG.md +5 -0
- package/dist/scaffolds/notis-notes/app/globals.css +3 -0
- package/dist/scaffolds/notis-notes/app/layout.tsx +6 -0
- package/dist/scaffolds/notis-notes/app/page.tsx +1950 -0
- package/dist/scaffolds/notis-notes/components/ui/badge.tsx +28 -0
- package/dist/scaffolds/notis-notes/components/ui/button.tsx +53 -0
- package/dist/scaffolds/notis-notes/components/ui/card.tsx +56 -0
- package/dist/scaffolds/notis-notes/components.json +20 -0
- package/dist/scaffolds/notis-notes/lib/utils.ts +6 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-notes/notis.config.ts +35 -0
- package/dist/scaffolds/notis-notes/package.json +31 -0
- package/dist/scaffolds/notis-notes/postcss.config.mjs +8 -0
- package/dist/scaffolds/notis-notes/tailwind.config.ts +58 -0
- package/dist/scaffolds/notis-notes/tsconfig.json +23 -0
- package/dist/scaffolds/notis-notes/vite.config.ts +10 -0
- package/dist/scaffolds/notis-random/CHANGELOG.md +5 -0
- package/dist/scaffolds/notis-random/README.md +33 -0
- package/dist/scaffolds/notis-random/app/globals.css +11 -0
- package/dist/scaffolds/notis-random/app/history/page.tsx +66 -0
- package/dist/scaffolds/notis-random/app/layout.tsx +7 -0
- package/dist/scaffolds/notis-random/app/page.tsx +222 -0
- package/dist/scaffolds/notis-random/index.html +12 -0
- package/dist/scaffolds/notis-random/lib/notis-tools.ts +109 -0
- package/dist/scaffolds/notis-random/lib/rng.ts +42 -0
- package/dist/scaffolds/notis-random/lib/roll-record.ts +102 -0
- package/dist/scaffolds/notis-random/lib/utils.ts +25 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-random/notis.config.ts +42 -0
- package/dist/scaffolds/notis-random/package.json +32 -0
- package/dist/scaffolds/notis-random/postcss.config.mjs +6 -0
- package/dist/scaffolds/notis-random/src/dev-main.tsx +70 -0
- package/dist/scaffolds/notis-random/src/mock-runtime.ts +129 -0
- package/dist/scaffolds/notis-random/tailwind.config.ts +43 -0
- package/dist/scaffolds/notis-random/tsconfig.json +23 -0
- package/dist/scaffolds/notis-random/vite.config.ts +11 -0
- package/dist/scaffolds.json +47 -0
- package/package.json +45 -0
- package/skills/notis-apps/SKILL.md +219 -0
- package/skills/notis-apps/cli.md +244 -0
- package/skills/notis-cli/SKILL.md +269 -0
- package/skills/notis-query/cli.md +39 -0
- package/src/cli.js +175 -0
- package/src/command-specs/apps.js +1939 -0
- package/src/command-specs/helpers.js +186 -0
- package/src/command-specs/index.js +14 -0
- package/src/command-specs/meta.js +161 -0
- package/src/command-specs/tools.js +499 -0
- package/src/runtime/agent-browser.js +464 -0
- package/src/runtime/app-boundary-validator.js +183 -0
- package/src/runtime/app-changelog.js +79 -0
- package/src/runtime/app-dev-server.js +712 -0
- package/src/runtime/app-dev-sessions.js +251 -0
- package/src/runtime/app-platform.js +1500 -0
- package/src/runtime/assets/store-screenshot-dark.png +0 -0
- package/src/runtime/cli-mode.generated.js +4 -0
- package/src/runtime/cli-mode.js +29 -0
- package/src/runtime/desktop-auth.js +93 -0
- package/src/runtime/errors.js +55 -0
- package/src/runtime/help.js +60 -0
- package/src/runtime/output.js +180 -0
- package/src/runtime/ports.js +15 -0
- package/src/runtime/profiles.js +261 -0
- package/src/runtime/store-screenshot.js +138 -0
- package/src/runtime/transport.js +347 -0
- package/template/.harness/index.html.tmpl +333 -0
- package/template/CHANGELOG.md +5 -0
- package/template/app/globals.css +3 -0
- package/template/app/layout.tsx +6 -0
- package/template/app/page.tsx +60 -0
- package/template/components/ui/badge.tsx +28 -0
- package/template/components/ui/button.tsx +53 -0
- package/template/components/ui/card.tsx +56 -0
- package/template/components.json +20 -0
- package/template/lib/utils.ts +6 -0
- package/template/metadata/screenshot-1.png +0 -0
- package/template/metadata/screenshot-2.png +0 -0
- package/template/metadata/screenshot-3.png +0 -0
- package/template/notis.config.ts +36 -0
- package/template/package-lock.json +4137 -0
- package/template/package.json +32 -0
- package/template/packages/sdk/package.json +36 -0
- package/template/packages/sdk/src/components/DocumentEditor.tsx +93 -0
- package/template/packages/sdk/src/components/Markdown.tsx +60 -0
- package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
- package/template/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
- package/template/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/template/packages/sdk/src/config.ts +145 -0
- package/template/packages/sdk/src/documents.ts +229 -0
- package/template/packages/sdk/src/hooks/useBackend.ts +41 -0
- package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
- package/template/packages/sdk/src/hooks/useDocument.ts +78 -0
- package/template/packages/sdk/src/hooks/useDocuments.ts +121 -0
- package/template/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
- package/template/packages/sdk/src/hooks/useNotis.ts +34 -0
- package/template/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
- package/template/packages/sdk/src/hooks/useTool.ts +64 -0
- package/template/packages/sdk/src/hooks/useTools.ts +56 -0
- package/template/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
- package/template/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/template/packages/sdk/src/index.ts +83 -0
- package/template/packages/sdk/src/provider.tsx +43 -0
- package/template/packages/sdk/src/runtime.ts +220 -0
- package/template/packages/sdk/src/styles.css +186 -0
- package/template/packages/sdk/src/ui.ts +15 -0
- package/template/packages/sdk/src/vite.ts +56 -0
- package/template/packages/sdk/tsconfig.json +15 -0
- package/template/postcss.config.mjs +8 -0
- package/template/tailwind.config.ts +58 -0
- package/template/tsconfig.json +22 -0
- package/template/vite.config.ts +10 -0
|
@@ -0,0 +1,499 @@
|
|
|
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';
|
|
4
|
+
import { usageError } from '../runtime/errors.js';
|
|
5
|
+
import {
|
|
6
|
+
COMPOSIO_GET_TOOL_SCHEMAS,
|
|
7
|
+
COMPOSIO_MULTI_EXECUTE_TOOL,
|
|
8
|
+
COMPOSIO_SEARCH_TOOLS,
|
|
9
|
+
fetchToolDiscovery,
|
|
10
|
+
fetchToolkits,
|
|
11
|
+
fetchToolSchema,
|
|
12
|
+
localNotisToolSlug,
|
|
13
|
+
nextIdempotencyKey,
|
|
14
|
+
parseJson,
|
|
15
|
+
parseMaybeJson,
|
|
16
|
+
runToolCommand,
|
|
17
|
+
validateArguments,
|
|
18
|
+
} from './helpers.js';
|
|
19
|
+
|
|
20
|
+
async function resolveJsonInput(value, label) {
|
|
21
|
+
if (!value) return undefined;
|
|
22
|
+
|
|
23
|
+
if (value === '-') {
|
|
24
|
+
const chunks = [];
|
|
25
|
+
for await (const chunk of process.stdin) {
|
|
26
|
+
chunks.push(chunk);
|
|
27
|
+
}
|
|
28
|
+
return parseJson(Buffer.concat(chunks).toString('utf-8'), label);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (value.startsWith('@')) {
|
|
32
|
+
const filePath = value.slice(1);
|
|
33
|
+
if (!existsSync(filePath)) {
|
|
34
|
+
throw usageError(`File not found: ${filePath}`);
|
|
35
|
+
}
|
|
36
|
+
return parseJson(readFileSync(filePath, 'utf-8'), label);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return parseJson(value, label);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const LONG_RUNNING_TOOL_TIMEOUT_MS = 600000;
|
|
43
|
+
const LONG_RUNNING_TOOL_NAMES = new Set([
|
|
44
|
+
'LOCAL_NOTIS_GENERATE_IMAGE_OPENAI',
|
|
45
|
+
'LOCAL_NOTIS_EDIT_IMAGE_OPENAI',
|
|
46
|
+
'LOCAL_NOTIS_GENERATE_IMAGE_GEMINI',
|
|
47
|
+
'LOCAL_NOTIS_EDIT_IMAGE_GEMINI',
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
const EXTENSION_CONTENT_TYPES = new Map([
|
|
51
|
+
['.pdf', 'application/pdf'],
|
|
52
|
+
['.png', 'image/png'],
|
|
53
|
+
['.jpg', 'image/jpeg'],
|
|
54
|
+
['.jpeg', 'image/jpeg'],
|
|
55
|
+
['.gif', 'image/gif'],
|
|
56
|
+
['.webp', 'image/webp'],
|
|
57
|
+
['.txt', 'text/plain'],
|
|
58
|
+
['.csv', 'text/csv'],
|
|
59
|
+
['.json', 'application/json'],
|
|
60
|
+
['.md', 'text/markdown'],
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
function guessContentType(filePath) {
|
|
64
|
+
const lower = filePath.toLowerCase();
|
|
65
|
+
const dotIndex = lower.lastIndexOf('.');
|
|
66
|
+
if (dotIndex === -1) return 'application/octet-stream';
|
|
67
|
+
return EXTENSION_CONTENT_TYPES.get(lower.slice(dotIndex)) || 'application/octet-stream';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function hashFileSha256(localPath) {
|
|
71
|
+
const hash = createHash('sha256');
|
|
72
|
+
for await (const chunk of createReadStream(localPath)) {
|
|
73
|
+
hash.update(chunk);
|
|
74
|
+
}
|
|
75
|
+
return hash.digest('hex');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function parseFileBindingSpec(spec, index) {
|
|
79
|
+
if (typeof spec !== 'string' || !spec.trim()) {
|
|
80
|
+
throw usageError('--file must be formatted as <argument-path>=<local-path>');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const equalsIndex = spec.indexOf('=');
|
|
84
|
+
if (equalsIndex <= 0 || equalsIndex === spec.length - 1) {
|
|
85
|
+
throw usageError('--file must be formatted as <argument-path>=<local-path>');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const argumentPath = spec.slice(0, equalsIndex).trim();
|
|
89
|
+
const localPath = spec.slice(equalsIndex + 1).trim();
|
|
90
|
+
if (!argumentPath) {
|
|
91
|
+
throw usageError('--file argument path is required');
|
|
92
|
+
}
|
|
93
|
+
if (!localPath) {
|
|
94
|
+
throw usageError('--file local path is required');
|
|
95
|
+
}
|
|
96
|
+
if (!existsSync(localPath)) {
|
|
97
|
+
throw usageError(`File not found: ${localPath}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let stats;
|
|
101
|
+
try {
|
|
102
|
+
stats = statSync(localPath);
|
|
103
|
+
accessSync(localPath, fsConstants.R_OK);
|
|
104
|
+
} catch {
|
|
105
|
+
throw usageError(`File is not readable: ${localPath}`);
|
|
106
|
+
}
|
|
107
|
+
if (!stats.isFile()) {
|
|
108
|
+
throw usageError(`File is not a regular file: ${localPath}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const sha256 = await hashFileSha256(localPath);
|
|
112
|
+
const fileBasename = basename(localPath);
|
|
113
|
+
const fieldName = `file_${index}`;
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
argument_path: argumentPath,
|
|
117
|
+
field_name: fieldName,
|
|
118
|
+
basename: fileBasename,
|
|
119
|
+
size: stats.size,
|
|
120
|
+
sha256,
|
|
121
|
+
contentType: guessContentType(fileBasename),
|
|
122
|
+
localPath,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function parseFileBindings(rawFileOptions) {
|
|
127
|
+
const values = Array.isArray(rawFileOptions)
|
|
128
|
+
? rawFileOptions
|
|
129
|
+
: (rawFileOptions ? [rawFileOptions] : []);
|
|
130
|
+
return Promise.all(values.map((value, index) => parseFileBindingSpec(value, index)));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function ensureLongRunningToolTimeout(ctx, toolNames) {
|
|
134
|
+
const names = Array.isArray(toolNames) ? toolNames : [toolNames];
|
|
135
|
+
if (!names.some((name) => LONG_RUNNING_TOOL_NAMES.has(localNotisToolSlug(name)))) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
ctx.runtime.timeoutMs = Math.max(ctx.runtime.timeoutMs || 0, LONG_RUNNING_TOOL_TIMEOUT_MS);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function toolsToolkitsHandler(ctx) {
|
|
142
|
+
const toolkits = await fetchToolkits(ctx.runtime);
|
|
143
|
+
return ctx.output.emitSuccess({
|
|
144
|
+
command: ctx.spec.command_path.join(' '),
|
|
145
|
+
data: { toolkits },
|
|
146
|
+
humanSummary: `Found ${toolkits.length} toolkits`,
|
|
147
|
+
hints: [
|
|
148
|
+
{ command: 'notis tools search <query>', reason: 'Search canonical tools and schemas' },
|
|
149
|
+
],
|
|
150
|
+
renderHuman: () =>
|
|
151
|
+
toolkits
|
|
152
|
+
.map((entry) => {
|
|
153
|
+
const status = entry.has_active_connection ? 'connected' : 'needs auth';
|
|
154
|
+
return `${entry.id} ${status} ${entry.description}`;
|
|
155
|
+
})
|
|
156
|
+
.join('\n'),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function discoveryToolRows(payload) {
|
|
161
|
+
const schemas = payload.tool_schemas || {};
|
|
162
|
+
const statuses = new Map(
|
|
163
|
+
(payload.toolkit_connection_statuses || [])
|
|
164
|
+
.filter((entry) => entry && typeof entry.toolkit === 'string')
|
|
165
|
+
.map((entry) => [entry.toolkit, entry])
|
|
166
|
+
);
|
|
167
|
+
const seen = new Set();
|
|
168
|
+
const rows = [];
|
|
169
|
+
for (const result of payload.results || []) {
|
|
170
|
+
for (const name of [...(result.primary_tool_slugs || []), ...(result.related_tool_slugs || [])]) {
|
|
171
|
+
if (!name || seen.has(name)) continue;
|
|
172
|
+
seen.add(name);
|
|
173
|
+
const schema = schemas[name] || {};
|
|
174
|
+
const toolkit = schema.toolkit || name.split('-').slice(0, -1).join('-');
|
|
175
|
+
const status = statuses.get(toolkit);
|
|
176
|
+
rows.push({
|
|
177
|
+
name,
|
|
178
|
+
toolkit,
|
|
179
|
+
description: schema.description || '',
|
|
180
|
+
schema_available: Boolean(schemas[name]),
|
|
181
|
+
connection_status: status
|
|
182
|
+
? (status.has_active_connection ? 'connected' : 'needs auth')
|
|
183
|
+
: 'unknown',
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return rows;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function toolsSearchHandler(ctx) {
|
|
191
|
+
const payload = await fetchToolDiscovery(ctx.runtime, ctx.args.query || 'Find tools', ctx.options.knownFields || '');
|
|
192
|
+
const tools = discoveryToolRows(payload);
|
|
193
|
+
const firstTool = tools[0];
|
|
194
|
+
return ctx.output.emitSuccess({
|
|
195
|
+
command: ctx.spec.command_path.join(' '),
|
|
196
|
+
data: payload,
|
|
197
|
+
humanSummary: `Found ${tools.length} tools`,
|
|
198
|
+
hints: firstTool
|
|
199
|
+
? [
|
|
200
|
+
{ command: `notis tools exec ${firstTool.name} --get-schema`, reason: 'Inspect the parameter schema' },
|
|
201
|
+
{ command: `notis tools exec ${firstTool.name} --arguments '{}'`, reason: 'Execute the tool' },
|
|
202
|
+
]
|
|
203
|
+
: [],
|
|
204
|
+
renderHuman: () =>
|
|
205
|
+
tools
|
|
206
|
+
.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`)
|
|
207
|
+
.join('\n')
|
|
208
|
+
.trim(),
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function toolsDescribeHandler(ctx) {
|
|
213
|
+
const requestedToolName = localNotisToolSlug(ctx.args.toolName);
|
|
214
|
+
const match = await fetchToolSchema(ctx.runtime, requestedToolName);
|
|
215
|
+
|
|
216
|
+
return ctx.output.emitSuccess({
|
|
217
|
+
command: ctx.spec.command_path.join(' '),
|
|
218
|
+
data: { tool: match },
|
|
219
|
+
humanSummary: `Described tool ${requestedToolName}`,
|
|
220
|
+
hints: [
|
|
221
|
+
{ command: `notis tools exec ${requestedToolName} --dry-run --arguments '{}'`, reason: 'Validate arguments first' },
|
|
222
|
+
{ command: `notis tools exec ${requestedToolName} --arguments '{}'`, reason: 'Execute this tool' },
|
|
223
|
+
],
|
|
224
|
+
renderHuman: () => JSON.stringify(match, null, 2),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function toolsExecHandler(ctx) {
|
|
229
|
+
const requestedToolName = localNotisToolSlug(ctx.args.toolName);
|
|
230
|
+
ensureLongRunningToolTimeout(ctx, requestedToolName);
|
|
231
|
+
const fileBindings = await parseFileBindings(ctx.options.file);
|
|
232
|
+
|
|
233
|
+
if (ctx.options.getSchema) {
|
|
234
|
+
const tool = await fetchToolSchema(ctx.runtime, requestedToolName);
|
|
235
|
+
return ctx.output.emitSuccess({
|
|
236
|
+
command: ctx.spec.command_path.join(' '),
|
|
237
|
+
data: { tool },
|
|
238
|
+
humanSummary: `Schema for ${requestedToolName}`,
|
|
239
|
+
hints: [
|
|
240
|
+
{ command: `notis tools exec ${requestedToolName} --dry-run --arguments '{}'`, reason: 'Validate arguments before executing' },
|
|
241
|
+
],
|
|
242
|
+
renderHuman: () => JSON.stringify(tool, null, 2),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const rawArguments = ctx.options.arguments || '{}';
|
|
247
|
+
const args = rawArguments === '-' || rawArguments.startsWith('@')
|
|
248
|
+
? await resolveJsonInput(rawArguments, 'arguments') || {}
|
|
249
|
+
: parseMaybeJson(rawArguments, 'arguments') || {};
|
|
250
|
+
|
|
251
|
+
if (ctx.options.dryRun) {
|
|
252
|
+
if (fileBindings.length) {
|
|
253
|
+
throw usageError('--file is only supported when executing a tool, not with --dry-run');
|
|
254
|
+
}
|
|
255
|
+
const tool = await fetchToolSchema(ctx.runtime, requestedToolName);
|
|
256
|
+
const errors = validateArguments(tool.parameters, args);
|
|
257
|
+
|
|
258
|
+
if (errors.length) {
|
|
259
|
+
return ctx.output.emitSuccess({
|
|
260
|
+
command: ctx.spec.command_path.join(' '),
|
|
261
|
+
data: { valid: false, errors, tool: requestedToolName },
|
|
262
|
+
humanSummary: `Dry run failed: ${errors.length} validation error(s)`,
|
|
263
|
+
hints: [
|
|
264
|
+
{ command: `notis tools exec ${requestedToolName} --get-schema`, reason: 'Review the full parameter schema' },
|
|
265
|
+
],
|
|
266
|
+
renderHuman: () => errors.map((e) => ` - ${e}`).join('\n'),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return ctx.output.emitSuccess({
|
|
271
|
+
command: ctx.spec.command_path.join(' '),
|
|
272
|
+
data: { valid: true, tool: requestedToolName, arguments: args },
|
|
273
|
+
humanSummary: `Dry run passed for ${requestedToolName}`,
|
|
274
|
+
hints: [
|
|
275
|
+
{ command: `notis tools exec ${requestedToolName} --arguments '${JSON.stringify(args)}'`, reason: 'Execute with these arguments' },
|
|
276
|
+
],
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const idempotencyKey = nextIdempotencyKey(ctx.globalOptions);
|
|
281
|
+
|
|
282
|
+
const result = await runToolCommand({
|
|
283
|
+
runtime: ctx.runtime,
|
|
284
|
+
toolName: COMPOSIO_MULTI_EXECUTE_TOOL,
|
|
285
|
+
arguments_: {
|
|
286
|
+
tools: [{ tool_slug: requestedToolName, arguments: args }],
|
|
287
|
+
},
|
|
288
|
+
mutating: true,
|
|
289
|
+
idempotencyKey,
|
|
290
|
+
fileBindings,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return ctx.output.emitSuccess({
|
|
294
|
+
command: ctx.spec.command_path.join(' '),
|
|
295
|
+
data: result.payload,
|
|
296
|
+
humanSummary: `Executed tool ${requestedToolName}`,
|
|
297
|
+
meta: { mutating: true, idempotency_key: idempotencyKey },
|
|
298
|
+
renderHuman: () => JSON.stringify(result.payload, null, 2),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function toolsExecParallelHandler(ctx) {
|
|
303
|
+
const parallelFileBindings = await parseFileBindings(ctx.options.file);
|
|
304
|
+
if (parallelFileBindings.length) {
|
|
305
|
+
throw usageError('--file is supported by `notis tools exec` only; exec-parallel accepts JSON calls without file uploads.');
|
|
306
|
+
}
|
|
307
|
+
const calls = parseMaybeJson(ctx.args.calls, 'calls');
|
|
308
|
+
if (!Array.isArray(calls) || !calls.length) {
|
|
309
|
+
throw usageError('calls must be a non-empty JSON array of {tool_name, arguments} objects');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
for (const [i, call] of calls.entries()) {
|
|
313
|
+
if (!call.tool_name || typeof call.tool_name !== 'string') {
|
|
314
|
+
throw usageError(`calls[${i}] missing required "tool_name" string`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
ensureLongRunningToolTimeout(ctx, calls.map((call) => localNotisToolSlug(call.tool_name)));
|
|
318
|
+
|
|
319
|
+
const data = await Promise.all(
|
|
320
|
+
calls.map((call) =>
|
|
321
|
+
runToolCommand({
|
|
322
|
+
runtime: ctx.runtime,
|
|
323
|
+
toolName: COMPOSIO_MULTI_EXECUTE_TOOL,
|
|
324
|
+
arguments_: {
|
|
325
|
+
tools: [{ tool_slug: localNotisToolSlug(call.tool_name), arguments: call.arguments || {} }],
|
|
326
|
+
},
|
|
327
|
+
mutating: true,
|
|
328
|
+
idempotencyKey: nextIdempotencyKey(ctx.globalOptions),
|
|
329
|
+
})
|
|
330
|
+
.then((r) => ({ tool_name: call.tool_name, status: 'ok', payload: r.payload }))
|
|
331
|
+
.catch((e) => ({ tool_name: call.tool_name, status: 'error', error: e.message }))
|
|
332
|
+
)
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
return ctx.output.emitSuccess({
|
|
336
|
+
command: ctx.spec.command_path.join(' '),
|
|
337
|
+
data: { results: data },
|
|
338
|
+
humanSummary: `Executed ${calls.length} tools in parallel`,
|
|
339
|
+
meta: { mutating: true },
|
|
340
|
+
renderHuman: () => data.map((r) => `${r.tool_name}: ${r.status}`).join('\n'),
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async function toolsLinkHandler(ctx) {
|
|
345
|
+
const apiBase = ctx.runtime.apiBase.replace(/\/+$/, '');
|
|
346
|
+
let portalBase;
|
|
347
|
+
try {
|
|
348
|
+
const parsed = new URL(apiBase);
|
|
349
|
+
if (parsed.hostname.startsWith('api.')) {
|
|
350
|
+
parsed.hostname = parsed.hostname.replace(/^api\./, 'app.');
|
|
351
|
+
portalBase = parsed.origin;
|
|
352
|
+
} else if (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1') {
|
|
353
|
+
const apiPort = parseInt(parsed.port, 10) || 80;
|
|
354
|
+
parsed.port = String(apiPort === 3001 ? 3000 : apiPort);
|
|
355
|
+
portalBase = parsed.origin;
|
|
356
|
+
} else {
|
|
357
|
+
portalBase = apiBase;
|
|
358
|
+
}
|
|
359
|
+
} catch {
|
|
360
|
+
portalBase = apiBase.replace('api.', 'app.');
|
|
361
|
+
}
|
|
362
|
+
const portalUrl = `${portalBase}/integrations?connect=${encodeURIComponent(ctx.args.toolkit)}`;
|
|
363
|
+
|
|
364
|
+
return ctx.output.emitSuccess({
|
|
365
|
+
command: ctx.spec.command_path.join(' '),
|
|
366
|
+
data: {
|
|
367
|
+
toolkit: ctx.args.toolkit,
|
|
368
|
+
auth_url: portalUrl,
|
|
369
|
+
},
|
|
370
|
+
humanSummary: `Open this URL to connect ${ctx.args.toolkit}`,
|
|
371
|
+
hints: [
|
|
372
|
+
{ command: 'notis tools toolkits', reason: 'Verify the toolkit is connected after setup' },
|
|
373
|
+
],
|
|
374
|
+
renderHuman: () =>
|
|
375
|
+
[
|
|
376
|
+
`Connect ${ctx.args.toolkit}:`,
|
|
377
|
+
` ${portalUrl}`,
|
|
378
|
+
'',
|
|
379
|
+
'After connecting, verify with:',
|
|
380
|
+
' notis tools toolkits',
|
|
381
|
+
].join('\n'),
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export const toolsCommandSpecs = [
|
|
386
|
+
{
|
|
387
|
+
command_path: ['tools', 'toolkits'],
|
|
388
|
+
summary: 'List toolkit namespaces and connection statuses available to the active user.',
|
|
389
|
+
when_to_use: 'Use this to inspect connection state before searching or executing generic tools.',
|
|
390
|
+
args_schema: { arguments: [], options: [] },
|
|
391
|
+
examples: ['notis tools toolkits', 'notis tools toolkits --json'],
|
|
392
|
+
output_schema: 'Returns toolkit_connection_statuses from COMPOSIO_SEARCH_TOOLS, rendered as toolkit rows.',
|
|
393
|
+
mutates: false,
|
|
394
|
+
idempotent: true,
|
|
395
|
+
related_commands: ['notis tools search <query>', 'notis tools describe <tool-name>'],
|
|
396
|
+
backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
|
|
397
|
+
handler: toolsToolkitsHandler,
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
command_path: ['tools', 'search'],
|
|
401
|
+
summary: 'Search across toolkit namespaces using natural language.',
|
|
402
|
+
when_to_use: 'Use this when you need a generic capability that does not have a first-class CLI command.',
|
|
403
|
+
args_schema: {
|
|
404
|
+
arguments: [{ token: '<query>', description: 'Natural language description of the tool you need.' }],
|
|
405
|
+
options: [{ flags: '--known-fields <text>', key: 'knownFields', description: 'Optional known field hints, such as channel_name:general or user_email:a@example.com.' }],
|
|
406
|
+
},
|
|
407
|
+
examples: ['notis tools search "send an email"', 'notis tools search "post on LinkedIn" --known-fields "platform:linkedin"'],
|
|
408
|
+
output_schema: 'Returns the full COMPOSIO_SEARCH_TOOLS response. Human output renders canonical names, connection status, and schema availability.',
|
|
409
|
+
mutates: false,
|
|
410
|
+
idempotent: true,
|
|
411
|
+
related_commands: ['notis tools toolkits', 'notis tools exec <tool-name>'],
|
|
412
|
+
backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
|
|
413
|
+
handler: toolsSearchHandler,
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
command_path: ['tools', 'describe'],
|
|
417
|
+
summary: 'Describe a generic tool by name.',
|
|
418
|
+
when_to_use: 'Use this when you know the tool name and want its parameter schema before execution.',
|
|
419
|
+
args_schema: {
|
|
420
|
+
arguments: [{ token: '<tool-name>', description: 'Exact tool name to locate and describe.' }],
|
|
421
|
+
options: [],
|
|
422
|
+
},
|
|
423
|
+
examples: ['notis tools describe composio-gmail-send_email', 'notis tools describe LOCAL_NOTIS_DATABASE_QUERY'],
|
|
424
|
+
output_schema: 'Returns one tool descriptor with name, description, and parameters.',
|
|
425
|
+
mutates: false,
|
|
426
|
+
idempotent: true,
|
|
427
|
+
related_commands: ['notis tools search <query>', 'notis tools exec <tool-name>'],
|
|
428
|
+
backend_call: { type: 'tool', name: COMPOSIO_GET_TOOL_SCHEMAS },
|
|
429
|
+
handler: toolsDescribeHandler,
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
command_path: ['tools', 'exec'],
|
|
433
|
+
summary: 'Execute a generic tool by canonical tool name.',
|
|
434
|
+
when_to_use: 'Use this as the escape hatch for integrations or Notis tools without a first-class CLI wrapper.',
|
|
435
|
+
args_schema: {
|
|
436
|
+
arguments: [{ token: '<tool-name>', description: 'Tool name returned by `notis tools search`.' }],
|
|
437
|
+
options: [
|
|
438
|
+
{ flags: '--arguments <json>', description: 'JSON object, @file path, or - for stdin.' },
|
|
439
|
+
{ flags: '--file <argument-path=local-path>', description: 'Upload a local file into a file-uploadable tool argument. Repeatable.', collect: true },
|
|
440
|
+
{ flags: '--get-schema', description: 'Display the tool parameter schema without executing.' },
|
|
441
|
+
{ flags: '--dry-run', description: 'Validate arguments against the tool schema without executing.' },
|
|
442
|
+
],
|
|
443
|
+
},
|
|
444
|
+
examples: [
|
|
445
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments \'{"database_slug":"tasks","query":{}}\'',
|
|
446
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments \'{"database_slug":"tasks"}\'',
|
|
447
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --get-schema',
|
|
448
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --dry-run --arguments \'{"database_slug":"tasks","query":{}}\'',
|
|
449
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments @query.json',
|
|
450
|
+
'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments - < query.json',
|
|
451
|
+
'notis tools exec composio-dropbox-upload_file --arguments \'{"path":"/target/in/dropbox.pdf"}\' --file content=./Invoice.pdf',
|
|
452
|
+
],
|
|
453
|
+
output_schema: 'Returns the raw tool execution payload.',
|
|
454
|
+
mutates: true,
|
|
455
|
+
idempotent: true,
|
|
456
|
+
related_commands: ['notis tools search <query>', 'notis tools describe <tool-name>', 'notis tools exec-parallel <calls>'],
|
|
457
|
+
backend_call: { type: 'tool', name: COMPOSIO_MULTI_EXECUTE_TOOL },
|
|
458
|
+
handler: toolsExecHandler,
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
command_path: ['tools', 'exec-parallel'],
|
|
462
|
+
summary: 'Execute multiple tools concurrently.',
|
|
463
|
+
when_to_use: 'Use this when you need to run independent tool calls simultaneously for speed.',
|
|
464
|
+
args_schema: {
|
|
465
|
+
arguments: [
|
|
466
|
+
{ token: '<calls>', description: 'JSON array of {tool_name, arguments} objects.' },
|
|
467
|
+
],
|
|
468
|
+
options: [
|
|
469
|
+
{ flags: '--file <argument-path=local-path>', description: 'Unsupported for exec-parallel; use tools exec for file uploads.', collect: true },
|
|
470
|
+
],
|
|
471
|
+
},
|
|
472
|
+
examples: [
|
|
473
|
+
'notis tools exec-parallel \'[{"tool_name":"LOCAL_NOTIS_DATABASE_QUERY","arguments":{"database_slug":"tasks","query":{}}},{"tool_name":"LOCAL_NOTIS_DATABASE_LIST_DATABASES","arguments":{}}]\'',
|
|
474
|
+
],
|
|
475
|
+
output_schema: 'Returns an array of results, one per tool call.',
|
|
476
|
+
mutates: true,
|
|
477
|
+
idempotent: true,
|
|
478
|
+
related_commands: ['notis tools exec <tool-name>', 'notis tools search <query>'],
|
|
479
|
+
backend_call: { type: 'tool', name: COMPOSIO_MULTI_EXECUTE_TOOL },
|
|
480
|
+
handler: toolsExecParallelHandler,
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
command_path: ['tools', 'link'],
|
|
484
|
+
summary: 'Get the URL to connect an integration toolkit.',
|
|
485
|
+
when_to_use: 'Use this when a tool requires authentication with an external service.',
|
|
486
|
+
args_schema: {
|
|
487
|
+
arguments: [{ token: '<toolkit>', description: 'Toolkit name to connect (e.g. github, gmail, slack).' }],
|
|
488
|
+
options: [],
|
|
489
|
+
},
|
|
490
|
+
examples: ['notis tools link github', 'notis tools link gmail --json'],
|
|
491
|
+
output_schema: 'Returns the authentication URL for the specified toolkit.',
|
|
492
|
+
mutates: false,
|
|
493
|
+
idempotent: true,
|
|
494
|
+
require_auth: false,
|
|
495
|
+
related_commands: ['notis tools toolkits', 'notis tools search <query>'],
|
|
496
|
+
backend_call: { type: 'local' },
|
|
497
|
+
handler: toolsLinkHandler,
|
|
498
|
+
},
|
|
499
|
+
];
|