@notis_ai/cli 0.2.0-beta.41.1 → 0.2.0-beta.57.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 +15 -19
- package/dist/scaffolds/notis-database/app/page.tsx +3 -3
- package/dist/scaffolds/notis-database/notis.config.ts +3 -3
- package/dist/scaffolds/notis-database/src/mock-runtime.ts +6 -6
- package/dist/scaffolds/notis-notes/app/page.tsx +3 -3
- package/dist/scaffolds/notis-notes/notis.config.ts +3 -3
- package/dist/scaffolds/notis-random/lib/notis-tools.ts +2 -2
- package/dist/scaffolds/notis-random/notis.config.ts +2 -2
- package/dist/scaffolds/notis-random/src/mock-runtime.ts +4 -4
- package/package.json +1 -1
- package/skills/notis-apps/SKILL.md +2 -2
- package/skills/notis-cli/SKILL.md +18 -16
- package/skills/notis-query/cli.md +9 -10
- package/src/command-specs/apps.js +42 -15
- package/src/command-specs/helpers.js +71 -45
- package/src/command-specs/meta.js +7 -7
- package/src/command-specs/tools.js +97 -82
- package/src/runtime/profiles.js +10 -4
- package/src/runtime/transport.js +1 -28
- package/template/app/page.tsx +1 -1
- package/template/notis.config.ts +1 -1
- package/template/packages/sdk/src/hooks/useTool.ts +1 -1
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
|
-
- `npx --package @notis_ai/cli@latest -- notis tools describe composio-gmail-
|
|
214
|
-
- `npx --package @notis_ai/cli@latest -- notis tools describe
|
|
210
|
+
- `npx --package @notis_ai/cli@latest -- notis tools describe composio-gmail-send_email`
|
|
211
|
+
- `npx --package @notis_ai/cli@latest -- notis tools describe LOCAL_NOTIS_DATABASE_QUERY`
|
|
215
212
|
|
|
216
213
|
### `npx --package @notis_ai/cli@latest -- notis tools exec <tool-name>`
|
|
217
214
|
|
|
@@ -224,16 +221,15 @@ 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
|
-
- `npx --package @notis_ai/cli@latest -- notis tools exec
|
|
231
|
-
- `npx --package @notis_ai/cli@latest -- notis tools exec
|
|
232
|
-
- `npx --package @notis_ai/cli@latest -- notis tools exec
|
|
233
|
-
- `npx --package @notis_ai/cli@latest -- notis tools exec
|
|
234
|
-
- `npx --package @notis_ai/cli@latest -- notis tools exec
|
|
235
|
-
- `npx --package @notis_ai/cli@latest -- notis tools exec
|
|
236
|
-
- `npx --package @notis_ai/cli@latest -- notis tools exec composio-dropbox-
|
|
226
|
+
- `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments '{"database_slug":"tasks","query":{}}'`
|
|
227
|
+
- `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments '{"database_slug":"tasks"}'`
|
|
228
|
+
- `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --get-schema`
|
|
229
|
+
- `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --dry-run --arguments '{"database_slug":"tasks","query":{}}'`
|
|
230
|
+
- `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments @query.json`
|
|
231
|
+
- `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments - < query.json`
|
|
232
|
+
- `npx --package @notis_ai/cli@latest -- notis tools exec composio-dropbox-upload_file --arguments '{"path":"/target/in/dropbox.pdf"}' --file content=./Invoice.pdf`
|
|
237
233
|
|
|
238
234
|
### `npx --package @notis_ai/cli@latest -- notis tools exec-parallel <calls>`
|
|
239
235
|
|
|
@@ -245,7 +241,7 @@ Options:
|
|
|
245
241
|
- `--file <argument-path=local-path>` — Unsupported for exec-parallel; use tools exec for file uploads.
|
|
246
242
|
|
|
247
243
|
Examples:
|
|
248
|
-
- `npx --package @notis_ai/cli@latest -- notis tools exec-parallel '[{"tool_name":"
|
|
244
|
+
- `npx --package @notis_ai/cli@latest -- notis tools exec-parallel '[{"tool_name":"LOCAL_NOTIS_DATABASE_QUERY","arguments":{"database_slug":"tasks","query":{}}},{"tool_name":"LOCAL_NOTIS_DATABASE_LIST_DATABASES","arguments":{}}]'`
|
|
249
245
|
|
|
250
246
|
### `npx --package @notis_ai/cli@latest -- notis tools link <toolkit>`
|
|
251
247
|
|
|
@@ -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
|
|
|
@@ -224,13 +224,13 @@ type ActiveTab = 'properties' | 'relations' | 'documents';
|
|
|
224
224
|
|
|
225
225
|
export default function CatalogPage() {
|
|
226
226
|
const listDatabases = useTool<ListDatabasesArgs, ListDatabasesResult>(
|
|
227
|
-
'
|
|
227
|
+
'LOCAL_NOTIS_DATABASE_LIST_DATABASES',
|
|
228
228
|
);
|
|
229
229
|
const getDatabase = useTool<GetDatabaseArgs, GetDatabaseResult>(
|
|
230
|
-
'
|
|
230
|
+
'LOCAL_NOTIS_DATABASE_GET_DATABASE',
|
|
231
231
|
);
|
|
232
232
|
const queryDatabase = useTool<QueryDatabaseArgs, QueryDatabaseResult>(
|
|
233
|
-
'
|
|
233
|
+
'LOCAL_NOTIS_DATABASE_QUERY',
|
|
234
234
|
);
|
|
235
235
|
|
|
236
236
|
const [rows, setRows] = useState<DatabaseCatalogRow[]>([]);
|
|
@@ -20,8 +20,8 @@ export default defineNotisApp({
|
|
|
20
20
|
},
|
|
21
21
|
],
|
|
22
22
|
tools: [
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
23
|
+
'LOCAL_NOTIS_DATABASE_LIST_DATABASES',
|
|
24
|
+
'LOCAL_NOTIS_DATABASE_GET_DATABASE',
|
|
25
|
+
'LOCAL_NOTIS_DATABASE_QUERY',
|
|
26
26
|
],
|
|
27
27
|
});
|
|
@@ -505,21 +505,21 @@ export function installMockRuntime(): NotisRuntime {
|
|
|
505
505
|
},
|
|
506
506
|
context: {},
|
|
507
507
|
listTools: async () => [
|
|
508
|
-
{ name: '
|
|
509
|
-
{ name: '
|
|
510
|
-
{ name: '
|
|
508
|
+
{ name: 'LOCAL_NOTIS_DATABASE_LIST_DATABASES', inputSchema: { type: 'object', properties: {} } },
|
|
509
|
+
{ name: 'LOCAL_NOTIS_DATABASE_GET_DATABASE', inputSchema: { type: 'object', properties: {} } },
|
|
510
|
+
{ name: 'LOCAL_NOTIS_DATABASE_QUERY', inputSchema: { type: 'object', properties: {} } },
|
|
511
511
|
],
|
|
512
512
|
callTool: async <TResult = unknown>(
|
|
513
513
|
name: string,
|
|
514
514
|
args?: Record<string, unknown>,
|
|
515
515
|
): Promise<TResult> => {
|
|
516
|
-
if (name === '
|
|
516
|
+
if (name === 'LOCAL_NOTIS_DATABASE_LIST_DATABASES') {
|
|
517
517
|
return {
|
|
518
518
|
databases: SEED.map((s) => s.catalog),
|
|
519
519
|
total_count: SEED.length,
|
|
520
520
|
} as TResult;
|
|
521
521
|
}
|
|
522
|
-
if (name === '
|
|
522
|
+
if (name === 'LOCAL_NOTIS_DATABASE_GET_DATABASE') {
|
|
523
523
|
const request = args ?? {};
|
|
524
524
|
const id = typeof request.database_id === 'string' ? request.database_id : null;
|
|
525
525
|
const slug = typeof request.database_slug === 'string' ? request.database_slug : null;
|
|
@@ -531,7 +531,7 @@ export function installMockRuntime(): NotisRuntime {
|
|
|
531
531
|
}
|
|
532
532
|
return { database: match.detail } as TResult;
|
|
533
533
|
}
|
|
534
|
-
if (name === '
|
|
534
|
+
if (name === 'LOCAL_NOTIS_DATABASE_QUERY') {
|
|
535
535
|
const request = args ?? {};
|
|
536
536
|
const slug = typeof request.database_slug === 'string' ? request.database_slug : null;
|
|
537
537
|
const match = SEED.find((s) => slug && s.detail.slug === slug);
|
|
@@ -358,7 +358,7 @@ function normalizeCollectionItem(value: unknown): CollectionItemContext | null {
|
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
function useNoteSchema(databaseSlug: string) {
|
|
361
|
-
const getDatabase = useTool<GetDatabaseArgs, GetDatabaseResult>('
|
|
361
|
+
const getDatabase = useTool<GetDatabaseArgs, GetDatabaseResult>('LOCAL_NOTIS_DATABASE_GET_DATABASE');
|
|
362
362
|
const [properties, setProperties] = useState<DatabaseProperty[]>([]);
|
|
363
363
|
const [loading, setLoading] = useState(true);
|
|
364
364
|
const [error, setError] = useState<Error | null>(null);
|
|
@@ -399,7 +399,7 @@ function useNoteSchema(databaseSlug: string) {
|
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
function useQueryDocuments(databaseSlug: string, options: { filter?: QueryFilter; pageSize?: number; offset?: number } = {}) {
|
|
402
|
-
const queryTool = useTool<QueryDatabaseArgs, QueryDatabaseResult>('
|
|
402
|
+
const queryTool = useTool<QueryDatabaseArgs, QueryDatabaseResult>('LOCAL_NOTIS_DATABASE_QUERY');
|
|
403
403
|
const [documents, setDocuments] = useState<DocumentRecord[]>([]);
|
|
404
404
|
const [loading, setLoading] = useState(true);
|
|
405
405
|
const [error, setError] = useState<Error | null>(null);
|
|
@@ -1061,7 +1061,7 @@ export default function NotesPage() {
|
|
|
1061
1061
|
const { app, route, context } = useNotis();
|
|
1062
1062
|
const navigation = useNotisNavigation();
|
|
1063
1063
|
const { request } = useBackend();
|
|
1064
|
-
const upsertNote = useTool<UpsertNoteArgs, UpsertNoteResult>('
|
|
1064
|
+
const upsertNote = useTool<UpsertNoteArgs, UpsertNoteResult>('LOCAL_NOTIS_DATABASE_UPSERT_NOTES');
|
|
1065
1065
|
|
|
1066
1066
|
const [activeTab, setActiveTab] = useState<TabKey>('gallery');
|
|
1067
1067
|
const [activeDateProperty, setActiveDateProperty] = useState('');
|
|
@@ -29,8 +29,8 @@ export default defineNotisApp({
|
|
|
29
29
|
},
|
|
30
30
|
],
|
|
31
31
|
tools: [
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
32
|
+
'LOCAL_NOTIS_DATABASE_QUERY',
|
|
33
|
+
'LOCAL_NOTIS_DATABASE_GET_DATABASE',
|
|
34
|
+
'LOCAL_NOTIS_DATABASE_UPSERT_NOTES',
|
|
35
35
|
],
|
|
36
36
|
});
|
|
@@ -37,7 +37,7 @@ type UpsertRollResult = {
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
export function useRollDocuments(pageSize?: number) {
|
|
40
|
-
const queryRolls = useTool<QueryRollsArgs, QueryRollsResult>('
|
|
40
|
+
const queryRolls = useTool<QueryRollsArgs, QueryRollsResult>('LOCAL_NOTIS_DATABASE_QUERY');
|
|
41
41
|
const [documents, setDocuments] = useState<RollDocument[]>([]);
|
|
42
42
|
const [error, setError] = useState<Error | null>(null);
|
|
43
43
|
const [fetchKey, setFetchKey] = useState(0);
|
|
@@ -80,7 +80,7 @@ export function useRollDocuments(pageSize?: number) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export function usePersistRoll() {
|
|
83
|
-
const upsertRoll = useTool<UpsertRollArgs, UpsertRollResult>('
|
|
83
|
+
const upsertRoll = useTool<UpsertRollArgs, UpsertRollResult>('LOCAL_NOTIS_DATABASE_UPSERT_ROLLS');
|
|
84
84
|
|
|
85
85
|
const persist = useCallback(
|
|
86
86
|
async (roll: Roll) => {
|
|
@@ -34,16 +34,16 @@ export function installMockRuntime(initialRoute: 'home' | 'history' = 'home'): N
|
|
|
34
34
|
|
|
35
35
|
listTools: async () => [
|
|
36
36
|
{
|
|
37
|
-
name: '
|
|
37
|
+
name: 'LOCAL_NOTIS_DATABASE_QUERY',
|
|
38
38
|
inputSchema: { type: 'object', properties: { database_slug: { type: 'string' } } },
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
name: '
|
|
41
|
+
name: 'LOCAL_NOTIS_DATABASE_UPSERT_ROLLS',
|
|
42
42
|
inputSchema: { type: 'object', properties: {} },
|
|
43
43
|
},
|
|
44
44
|
],
|
|
45
45
|
callTool: async <TResult = unknown>(name: string, args?: Record<string, unknown>): Promise<TResult> => {
|
|
46
|
-
if (name === '
|
|
46
|
+
if (name === 'LOCAL_NOTIS_DATABASE_QUERY') {
|
|
47
47
|
const request = args ?? {};
|
|
48
48
|
const databaseSlug = typeof request.database_slug === 'string' ? request.database_slug : 'rolls';
|
|
49
49
|
const query = request.query && typeof request.query === 'object'
|
|
@@ -56,7 +56,7 @@ export function installMockRuntime(initialRoute: 'home' | 'history' = 'home'): N
|
|
|
56
56
|
return { documents: typeof pageSize === 'number' ? sliced.slice(0, pageSize) : sliced } as TResult;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
if (name === '
|
|
59
|
+
if (name === 'LOCAL_NOTIS_DATABASE_UPSERT_ROLLS') {
|
|
60
60
|
const request = args ?? {};
|
|
61
61
|
const docs = STATE.rolls ?? [];
|
|
62
62
|
const now = new Date().toISOString();
|
package/package.json
CHANGED
|
@@ -138,7 +138,7 @@ export default defineNotisApp({
|
|
|
138
138
|
default: true,
|
|
139
139
|
},
|
|
140
140
|
],
|
|
141
|
-
tools: ['
|
|
141
|
+
tools: ['LOCAL_NOTIS_DATABASE_QUERY', 'LOCAL_NOTIS_DATABASE_UPSERT_TASKS'],
|
|
142
142
|
});
|
|
143
143
|
```
|
|
144
144
|
|
|
@@ -149,7 +149,7 @@ Use SDK hooks for data and tool access:
|
|
|
149
149
|
```tsx
|
|
150
150
|
import { useTool } from '@notis/sdk';
|
|
151
151
|
|
|
152
|
-
const query = useTool('
|
|
152
|
+
const query = useTool('LOCAL_NOTIS_DATABASE_QUERY');
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
Do not fetch Notis backend endpoints directly from app code unless the SDK explicitly requires it. The Portal provides authentication, runtime context, and route rendering.
|
|
@@ -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`
|
|
@@ -155,7 +155,7 @@ This is the main escape hatch for:
|
|
|
155
155
|
|
|
156
156
|
Typical toolkit namespaces include:
|
|
157
157
|
|
|
158
|
-
- `notis
|
|
158
|
+
- `notis` for native Notis tools
|
|
159
159
|
- `composio-*` for Composio-backed integrations
|
|
160
160
|
- `mcp-*` for MCP-backed tools
|
|
161
161
|
|
|
@@ -178,22 +178,22 @@ npx --package @notis_ai/cli@latest -- notis tools search "list today's calendar
|
|
|
178
178
|
Inspect a tool before execution:
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
|
-
npx --package @notis_ai/cli@latest -- notis tools describe composio-googlecalendar-
|
|
182
|
-
npx --package @notis_ai/cli@latest -- notis tools exec composio-googlecalendar-
|
|
181
|
+
npx --package @notis_ai/cli@latest -- notis tools describe composio-googlecalendar-list_events
|
|
182
|
+
npx --package @notis_ai/cli@latest -- notis tools exec composio-googlecalendar-list_events --get-schema
|
|
183
183
|
```
|
|
184
184
|
|
|
185
185
|
Dry-run a tool call:
|
|
186
186
|
|
|
187
187
|
```bash
|
|
188
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
189
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
188
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --dry-run --arguments '{"database_slug":"tasks"}'
|
|
189
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --dry-run --arguments '{"database_id":"tasks-db-id","query":{"page_size":10}}'
|
|
190
190
|
```
|
|
191
191
|
|
|
192
192
|
Execute a tool call:
|
|
193
193
|
|
|
194
194
|
```bash
|
|
195
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
196
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
195
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments '{"database_slug":"tasks"}'
|
|
196
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments '{"database_id":"tasks-db-id","query":{"page_size":10}}'
|
|
197
197
|
```
|
|
198
198
|
|
|
199
199
|
Connect a missing toolkit:
|
|
@@ -206,21 +206,23 @@ npx --package @notis_ai/cli@latest -- notis tools link github
|
|
|
206
206
|
|
|
207
207
|
Native Notis databases are accessed through the generic tool workflow, not a first-class database command group. Use these canonical tool names:
|
|
208
208
|
|
|
209
|
-
- `
|
|
210
|
-
- `
|
|
211
|
-
- `
|
|
212
|
-
- `
|
|
209
|
+
- `LOCAL_NOTIS_DATABASE_LIST_DATABASES` -- list databases accessible to the current profile
|
|
210
|
+
- `LOCAL_NOTIS_DATABASE_GET_DATABASE` -- inspect read-only metadata and schema detail
|
|
211
|
+
- `LOCAL_NOTIS_DATABASE_QUERY` -- query documents from a database
|
|
212
|
+
- `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` -- create or update a database schema
|
|
213
213
|
|
|
214
214
|
Example workflow before building an app:
|
|
215
215
|
|
|
216
216
|
```bash
|
|
217
217
|
npx --package @notis_ai/cli@latest -- notis tools search "list Notis databases"
|
|
218
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
219
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
220
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
221
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
218
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_LIST_DATABASES --arguments '{}'
|
|
219
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --get-schema
|
|
220
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments '{"database_slug":"social_media_calendar"}'
|
|
221
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments '{"database_id":"social-media-calendar-db-id","query":{"page_size":1}}'
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
+
When `LOCAL_NOTIS_DATABASE_LIST_DATABASES` or `LOCAL_NOTIS_DATABASE_GET_DATABASE` returns a database ID, prefer `database_id` for `LOCAL_NOTIS_DATABASE_QUERY`; `database_slug` remains supported as a fallback.
|
|
225
|
+
|
|
224
226
|
## Supporting commands
|
|
225
227
|
|
|
226
228
|
- `npx --package @notis_ai/cli@latest -- notis doctor` — verify CLI config, auth, and API reachability before relying on the CLI
|
|
@@ -10,20 +10,20 @@ For CI, hosted agents, or internal scripts, pass a non-persisted token with `NOT
|
|
|
10
10
|
|
|
11
11
|
## Canonical database tools
|
|
12
12
|
|
|
13
|
-
- `
|
|
14
|
-
- `
|
|
15
|
-
- `
|
|
16
|
-
- `
|
|
13
|
+
- `LOCAL_NOTIS_DATABASE_LIST_DATABASES` — list native databases.
|
|
14
|
+
- `LOCAL_NOTIS_DATABASE_GET_DATABASE` — inspect one database schema.
|
|
15
|
+
- `LOCAL_NOTIS_DATABASE_QUERY` — query native database documents.
|
|
16
|
+
- `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` — create or update database schema.
|
|
17
17
|
|
|
18
18
|
## Workflow
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
npx --package @notis_ai/cli@latest -- notis tools search "list Notis databases"
|
|
22
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
23
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
24
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
25
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
26
|
-
npx --package @notis_ai/cli@latest -- notis tools exec
|
|
22
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_LIST_DATABASES --arguments '{}'
|
|
23
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --get-schema
|
|
24
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments '{"database_slug":"tasks"}'
|
|
25
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --dry-run --arguments '{"database_slug":"tasks","query":{"page_size":10}}'
|
|
26
|
+
npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments '{"database_slug":"tasks","query":{"page_size":10}}'
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
## Supporting command
|
|
@@ -37,4 +37,3 @@ When to use: Use this before relying on the CLI in automation or after changing
|
|
|
37
37
|
Examples:
|
|
38
38
|
- `npx --package @notis_ai/cli@latest -- notis doctor`
|
|
39
39
|
- `npx --package @notis_ai/cli@latest -- notis doctor --json`
|
|
40
|
-
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
import { getAvailablePort } from '../runtime/ports.js';
|
|
47
47
|
import { getCliMode } from '../runtime/cli-mode.js';
|
|
48
48
|
import {
|
|
49
|
+
localNotisToolSlug,
|
|
49
50
|
nextIdempotencyKey,
|
|
50
51
|
runToolCommand,
|
|
51
52
|
toolConflictToError,
|
|
@@ -54,8 +55,11 @@ import {
|
|
|
54
55
|
const DEFAULT_DEV_PORT = 5173;
|
|
55
56
|
const DEV_HEARTBEAT_INTERVAL_MS = 10_000;
|
|
56
57
|
const CONFIG_FILENAMES = ['notis.config.ts', 'notis.config.js', 'notis.config.mjs'];
|
|
57
|
-
const ENSURE_DEV_APP_INSTALLATION_TOOL = '
|
|
58
|
-
const GET_APP_TOOL = '
|
|
58
|
+
const ENSURE_DEV_APP_INSTALLATION_TOOL = 'LOCAL_NOTIS_ENSURE_DEV_APP_INSTALLATION';
|
|
59
|
+
const GET_APP_TOOL = 'LOCAL_NOTIS_GET_APP';
|
|
60
|
+
const LIST_APPS_TOOL = 'LOCAL_NOTIS_LIST_APPS';
|
|
61
|
+
const CREATE_APP_TOOL = 'LOCAL_NOTIS_CREATE_APP';
|
|
62
|
+
const SAVE_APP_FILES_TOOL = 'LOCAL_NOTIS_SAVE_APP_FILES';
|
|
59
63
|
|
|
60
64
|
// ---------------------------------------------------------------------------
|
|
61
65
|
// Formatters
|
|
@@ -192,9 +196,29 @@ function pickDefaultRouteSlug(manifest) {
|
|
|
192
196
|
return firstWithSlug ? firstWithSlug.slug : null;
|
|
193
197
|
}
|
|
194
198
|
|
|
195
|
-
|
|
199
|
+
const DESKTOP_DEEP_LINK_SCHEME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
200
|
+
|
|
201
|
+
export function resolveDevelopmentDesktopScheme(env = process.env) {
|
|
202
|
+
// Mirror the desktop app's own scheme resolution (electron main + forge config):
|
|
203
|
+
// local dev launches register `notis-dev`, while installed prod/beta builds claim
|
|
204
|
+
// `notis`. Hardcoding `notis` here is what made `apps dev` open the installed
|
|
205
|
+
// prod/beta app instead of the local dev app.
|
|
206
|
+
const scheme = (env.NOTIS_DESKTOP_DEEP_LINK_SCHEME || '').trim();
|
|
207
|
+
return DESKTOP_DEEP_LINK_SCHEME_PATTERN.test(scheme) ? scheme : 'notis';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function buildDevelopmentDesktopUrl(appHref = null, scheme = 'notis') {
|
|
196
211
|
const route = String(appHref || '/store').replace(/^\/+/, '');
|
|
197
|
-
|
|
212
|
+
const normalizedScheme = DESKTOP_DEEP_LINK_SCHEME_PATTERN.test(scheme) ? scheme : 'notis';
|
|
213
|
+
return `${normalizedScheme}://${route || 'store'}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function shouldOpenDevelopmentTab(options = {}) {
|
|
217
|
+
// Commander stores the negatable `--no-open` flag as `options.open === false`;
|
|
218
|
+
// it never sets `options.noOpen`. Reading the non-existent `noOpen` key meant
|
|
219
|
+
// `--no-open` was silently ignored and `apps dev` always ran `open notis://…`,
|
|
220
|
+
// which macOS routes to the installed prod/beta desktop app.
|
|
221
|
+
return options.open !== false;
|
|
198
222
|
}
|
|
199
223
|
|
|
200
224
|
function buildAppHref({ appSlug, appId, manifest }) {
|
|
@@ -311,14 +335,14 @@ function assertHarnessResult(result, route, databaseSlugs) {
|
|
|
311
335
|
const seen = (result.runtimeCalls || []).some(
|
|
312
336
|
(call) =>
|
|
313
337
|
call?.op === 'callTool' &&
|
|
314
|
-
call?.args?.name === '
|
|
338
|
+
localNotisToolSlug(call?.args?.name) === 'LOCAL_NOTIS_DATABASE_QUERY' &&
|
|
315
339
|
call?.args?.arguments?.database_slug === databaseSlug,
|
|
316
340
|
);
|
|
317
341
|
if (!seen) {
|
|
318
342
|
assertions.push({
|
|
319
343
|
ok: false,
|
|
320
344
|
code: 'missing_database_query',
|
|
321
|
-
message: `Route "${route.slug}" did not call
|
|
345
|
+
message: `Route "${route.slug}" did not call LOCAL_NOTIS_DATABASE_QUERY for "${databaseSlug}".`,
|
|
322
346
|
details: { databaseSlug },
|
|
323
347
|
});
|
|
324
348
|
}
|
|
@@ -447,7 +471,7 @@ function databaseMaterializationWarnings(apps) {
|
|
|
447
471
|
async function assertDirectDeployAccess(runtime, appId) {
|
|
448
472
|
const result = await runToolCommand({
|
|
449
473
|
runtime,
|
|
450
|
-
toolName:
|
|
474
|
+
toolName: LIST_APPS_TOOL,
|
|
451
475
|
});
|
|
452
476
|
const apps = result.payload.apps || [];
|
|
453
477
|
const hasAccess = apps.some((app) => (app.app_id || app.id) === appId);
|
|
@@ -463,7 +487,7 @@ async function assertDirectDeployAccess(runtime, appId) {
|
|
|
463
487
|
async function appsListHandler(ctx) {
|
|
464
488
|
const result = await runToolCommand({
|
|
465
489
|
runtime: ctx.runtime,
|
|
466
|
-
toolName:
|
|
490
|
+
toolName: LIST_APPS_TOOL,
|
|
467
491
|
});
|
|
468
492
|
const apps = result.payload.apps || [];
|
|
469
493
|
return ctx.output.emitSuccess({
|
|
@@ -511,7 +535,7 @@ async function appsCreateHandler(ctx) {
|
|
|
511
535
|
const idempotencyKey = nextIdempotencyKey(ctx.globalOptions);
|
|
512
536
|
const result = await runToolCommand({
|
|
513
537
|
runtime: ctx.runtime,
|
|
514
|
-
toolName:
|
|
538
|
+
toolName: CREATE_APP_TOOL,
|
|
515
539
|
arguments_: {
|
|
516
540
|
name: ctx.args.name,
|
|
517
541
|
description: appConfig?.description || undefined,
|
|
@@ -634,7 +658,10 @@ async function appsDevHandler(ctx) {
|
|
|
634
658
|
}),
|
|
635
659
|
};
|
|
636
660
|
});
|
|
637
|
-
const developmentTabUrl = buildDevelopmentDesktopUrl(
|
|
661
|
+
const developmentTabUrl = buildDevelopmentDesktopUrl(
|
|
662
|
+
apps[0]?.appHref,
|
|
663
|
+
resolveDevelopmentDesktopScheme(),
|
|
664
|
+
);
|
|
638
665
|
const warnings = databaseMaterializationWarnings(apps);
|
|
639
666
|
|
|
640
667
|
const devServer = await startAppDevServer({
|
|
@@ -710,7 +737,7 @@ async function appsDevHandler(ctx) {
|
|
|
710
737
|
].join('\n'),
|
|
711
738
|
});
|
|
712
739
|
|
|
713
|
-
if (
|
|
740
|
+
if (shouldOpenDevelopmentTab(ctx.options)) {
|
|
714
741
|
openInBrowser(developmentTabUrl);
|
|
715
742
|
}
|
|
716
743
|
|
|
@@ -1073,7 +1100,7 @@ async function appsDeployHandler(ctx) {
|
|
|
1073
1100
|
try {
|
|
1074
1101
|
result = await runToolCommand({
|
|
1075
1102
|
runtime: ctx.runtime,
|
|
1076
|
-
toolName:
|
|
1103
|
+
toolName: SAVE_APP_FILES_TOOL,
|
|
1077
1104
|
arguments_: {
|
|
1078
1105
|
app_id: appId,
|
|
1079
1106
|
files,
|
|
@@ -1185,7 +1212,7 @@ export const appsCommandSpecs = [
|
|
|
1185
1212
|
examples: ['notis apps list', 'notis apps list --json'],
|
|
1186
1213
|
mutates: false,
|
|
1187
1214
|
idempotent: true,
|
|
1188
|
-
backend_call: { type: 'tool', name:
|
|
1215
|
+
backend_call: { type: 'tool', name: LIST_APPS_TOOL },
|
|
1189
1216
|
handler: appsListHandler,
|
|
1190
1217
|
},
|
|
1191
1218
|
{
|
|
@@ -1242,7 +1269,7 @@ export const appsCommandSpecs = [
|
|
|
1242
1269
|
],
|
|
1243
1270
|
mutates: true,
|
|
1244
1271
|
idempotent: false,
|
|
1245
|
-
backend_call: { type: 'tool', name:
|
|
1272
|
+
backend_call: { type: 'tool', name: CREATE_APP_TOOL },
|
|
1246
1273
|
handler: appsCreateHandler,
|
|
1247
1274
|
},
|
|
1248
1275
|
{
|
|
@@ -1376,7 +1403,7 @@ export const appsCommandSpecs = [
|
|
|
1376
1403
|
examples: ['notis apps deploy', 'notis apps deploy --skip-build', 'notis apps deploy --app-id abc123', 'notis apps deploy --direct'],
|
|
1377
1404
|
mutates: true,
|
|
1378
1405
|
idempotent: true,
|
|
1379
|
-
backend_call: { type: 'tool', name:
|
|
1406
|
+
backend_call: { type: 'tool', name: SAVE_APP_FILES_TOOL },
|
|
1380
1407
|
handler: appsDeployHandler,
|
|
1381
1408
|
},
|
|
1382
1409
|
{
|
|
@@ -2,6 +2,34 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { CliError, EXIT_CODES, usageError } from '../runtime/errors.js';
|
|
3
3
|
import { callTool, httpRequest } from '../runtime/transport.js';
|
|
4
4
|
|
|
5
|
+
export const COMPOSIO_SEARCH_TOOLS = 'COMPOSIO_SEARCH_TOOLS';
|
|
6
|
+
export const COMPOSIO_GET_TOOL_SCHEMAS = 'COMPOSIO_GET_TOOL_SCHEMAS';
|
|
7
|
+
export const COMPOSIO_MULTI_EXECUTE_TOOL = 'COMPOSIO_MULTI_EXECUTE_TOOL';
|
|
8
|
+
|
|
9
|
+
const NOTIS_DATABASE_CORE_NAMES = new Set([
|
|
10
|
+
'query',
|
|
11
|
+
'get_database',
|
|
12
|
+
'get_document',
|
|
13
|
+
'list_databases',
|
|
14
|
+
'upsert_database',
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
function isNotisDatabaseCoreName(coreName) {
|
|
18
|
+
// Mirrors server is_database_tool_name: canonical database ops plus generated
|
|
19
|
+
// upsert_<db> tools. Database tools live in the NOTIS_DATABASE toolkit so their
|
|
20
|
+
// public slug carries the DATABASE segment.
|
|
21
|
+
return NOTIS_DATABASE_CORE_NAMES.has(coreName) || coreName.startsWith('upsert_');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function localNotisToolSlug(toolName) {
|
|
25
|
+
if (typeof toolName !== 'string' || !toolName.startsWith('notis-')) {
|
|
26
|
+
return toolName;
|
|
27
|
+
}
|
|
28
|
+
const coreName = toolName.slice('notis-'.length);
|
|
29
|
+
const prefix = isNotisDatabaseCoreName(coreName) ? 'LOCAL_NOTIS_DATABASE_' : 'LOCAL_NOTIS_';
|
|
30
|
+
return `${prefix}${coreName.replace(/-/g, '_').toUpperCase()}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
5
33
|
export function parseJson(value, label) {
|
|
6
34
|
try {
|
|
7
35
|
return JSON.parse(value);
|
|
@@ -17,23 +45,6 @@ export function parseMaybeJson(value, label) {
|
|
|
17
45
|
return parseJson(value, label);
|
|
18
46
|
}
|
|
19
47
|
|
|
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
48
|
export function nextIdempotencyKey(globalOptions) {
|
|
38
49
|
return globalOptions.idempotencyKey || randomUUID();
|
|
39
50
|
}
|
|
@@ -57,28 +68,19 @@ export async function runToolCommand({
|
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
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);
|
|
71
|
+
const payload = await fetchToolDiscovery(runtime, 'List available toolkit namespaces and connection statuses');
|
|
72
|
+
return (payload.toolkit_connection_statuses || []).map((entry) => ({
|
|
73
|
+
id: entry.toolkit,
|
|
74
|
+
provider: typeof entry.toolkit === 'string' ? entry.toolkit.split('-', 1)[0] : undefined,
|
|
75
|
+
description: entry.description || entry.status_message || entry.toolkit,
|
|
76
|
+
has_active_connection: Boolean(entry.has_active_connection),
|
|
77
|
+
status_message: entry.status_message || '',
|
|
78
|
+
connection_details: entry.connection_details || {},
|
|
79
|
+
}));
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
export async function probeAuth(runtime) {
|
|
77
|
-
|
|
78
|
-
runtime,
|
|
79
|
-
toolName: 'notis_find_toolkits',
|
|
80
|
-
});
|
|
81
|
-
return result.payload;
|
|
83
|
+
return fetchToolDiscovery(runtime, 'List available toolkit namespaces and connection statuses');
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
export async function healthCheck(runtime) {
|
|
@@ -90,22 +92,46 @@ export async function healthCheck(runtime) {
|
|
|
90
92
|
});
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
export async function fetchToolSchema(runtime, toolName
|
|
94
|
-
const toolkits = normalizeToolkits(rawToolkits);
|
|
95
|
+
export async function fetchToolSchema(runtime, toolName) {
|
|
95
96
|
const result = await runToolCommand({
|
|
96
97
|
runtime,
|
|
97
|
-
toolName:
|
|
98
|
+
toolName: COMPOSIO_GET_TOOL_SCHEMAS,
|
|
98
99
|
arguments_: {
|
|
99
|
-
|
|
100
|
-
...(toolkits.length ? { toolkits } : {}),
|
|
100
|
+
tool_slugs: [toolName],
|
|
101
101
|
},
|
|
102
102
|
});
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
|
|
103
|
+
const payload = result.payload || {};
|
|
104
|
+
const schema = (
|
|
105
|
+
payload.tool_schemas?.[toolName] ||
|
|
106
|
+
payload.schemas?.[toolName] ||
|
|
107
|
+
payload.tools?.find?.((tool) => tool?.tool_slug === toolName || tool?.name === toolName)
|
|
108
|
+
);
|
|
109
|
+
if (!schema) {
|
|
106
110
|
throw usageError(`Tool "${toolName}" not found.`);
|
|
107
111
|
}
|
|
108
|
-
return
|
|
112
|
+
return {
|
|
113
|
+
name: toolName,
|
|
114
|
+
toolkit_id: schema.toolkit,
|
|
115
|
+
description: schema.description || '',
|
|
116
|
+
parameters: schema.input_schema || { type: 'object', properties: {} },
|
|
117
|
+
output_schema: schema.output_schema || {},
|
|
118
|
+
schema_available: Boolean(schema.hasFullSchema),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function fetchToolDiscovery(runtime, useCase, knownFields = '') {
|
|
123
|
+
const query = { use_case: useCase };
|
|
124
|
+
if (knownFields) {
|
|
125
|
+
query.known_fields = knownFields;
|
|
126
|
+
}
|
|
127
|
+
const result = await runToolCommand({
|
|
128
|
+
runtime,
|
|
129
|
+
toolName: COMPOSIO_SEARCH_TOOLS,
|
|
130
|
+
arguments_: {
|
|
131
|
+
queries: [query],
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return result.payload || {};
|
|
109
135
|
}
|
|
110
136
|
|
|
111
137
|
export function validateArguments(schema, args) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { healthCheck, probeAuth } from './helpers.js';
|
|
1
|
+
import { COMPOSIO_SEARCH_TOOLS, healthCheck, probeAuth } from './helpers.js';
|
|
2
2
|
import { findCommandSpec, formatDescribe } from '../runtime/help.js';
|
|
3
3
|
|
|
4
4
|
async function doctorHandler(ctx) {
|
|
@@ -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: COMPOSIO_SEARCH_TOOLS },
|
|
122
122
|
handler: whoamiHandler,
|
|
123
123
|
},
|
|
124
124
|
{
|
|
@@ -3,13 +3,16 @@ import { createHash } from 'node:crypto';
|
|
|
3
3
|
import { basename } from 'node:path';
|
|
4
4
|
import { usageError } from '../runtime/errors.js';
|
|
5
5
|
import {
|
|
6
|
+
COMPOSIO_GET_TOOL_SCHEMAS,
|
|
7
|
+
COMPOSIO_MULTI_EXECUTE_TOOL,
|
|
8
|
+
COMPOSIO_SEARCH_TOOLS,
|
|
9
|
+
fetchToolDiscovery,
|
|
6
10
|
fetchToolkits,
|
|
7
11
|
fetchToolSchema,
|
|
12
|
+
localNotisToolSlug,
|
|
8
13
|
nextIdempotencyKey,
|
|
9
14
|
parseJson,
|
|
10
15
|
parseMaybeJson,
|
|
11
|
-
normalizeToolkits,
|
|
12
|
-
resolveSearchToolkits,
|
|
13
16
|
runToolCommand,
|
|
14
17
|
validateArguments,
|
|
15
18
|
} from './helpers.js';
|
|
@@ -38,10 +41,10 @@ async function resolveJsonInput(value, label) {
|
|
|
38
41
|
|
|
39
42
|
const LONG_RUNNING_TOOL_TIMEOUT_MS = 600000;
|
|
40
43
|
const LONG_RUNNING_TOOL_NAMES = new Set([
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
44
|
+
'LOCAL_NOTIS_GENERATE_IMAGE_OPENAI',
|
|
45
|
+
'LOCAL_NOTIS_EDIT_IMAGE_OPENAI',
|
|
46
|
+
'LOCAL_NOTIS_GENERATE_IMAGE_GEMINI',
|
|
47
|
+
'LOCAL_NOTIS_EDIT_IMAGE_GEMINI',
|
|
45
48
|
]);
|
|
46
49
|
|
|
47
50
|
const EXTENSION_CONTENT_TYPES = new Map([
|
|
@@ -129,7 +132,7 @@ async function parseFileBindings(rawFileOptions) {
|
|
|
129
132
|
|
|
130
133
|
function ensureLongRunningToolTimeout(ctx, toolNames) {
|
|
131
134
|
const names = Array.isArray(toolNames) ? toolNames : [toolNames];
|
|
132
|
-
if (!names.some((name) => LONG_RUNNING_TOOL_NAMES.has(name))) {
|
|
135
|
+
if (!names.some((name) => LONG_RUNNING_TOOL_NAMES.has(localNotisToolSlug(name)))) {
|
|
133
136
|
return;
|
|
134
137
|
}
|
|
135
138
|
ctx.runtime.timeoutMs = Math.max(ctx.runtime.timeoutMs || 0, LONG_RUNNING_TOOL_TIMEOUT_MS);
|
|
@@ -142,27 +145,55 @@ async function toolsToolkitsHandler(ctx) {
|
|
|
142
145
|
data: { toolkits },
|
|
143
146
|
humanSummary: `Found ${toolkits.length} toolkits`,
|
|
144
147
|
hints: [
|
|
145
|
-
{ command: 'notis tools search <query>', reason: 'Search
|
|
148
|
+
{ command: 'notis tools search <query>', reason: 'Search canonical tools and schemas' },
|
|
146
149
|
],
|
|
147
|
-
renderHuman: () =>
|
|
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'),
|
|
148
157
|
});
|
|
149
158
|
}
|
|
150
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
|
+
|
|
151
190
|
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 || [];
|
|
191
|
+
const payload = await fetchToolDiscovery(ctx.runtime, ctx.args.query || 'Find tools', ctx.options.knownFields || '');
|
|
192
|
+
const tools = discoveryToolRows(payload);
|
|
162
193
|
const firstTool = tools[0];
|
|
163
194
|
return ctx.output.emitSuccess({
|
|
164
195
|
command: ctx.spec.command_path.join(' '),
|
|
165
|
-
data:
|
|
196
|
+
data: payload,
|
|
166
197
|
humanSummary: `Found ${tools.length} tools`,
|
|
167
198
|
hints: firstTool
|
|
168
199
|
? [
|
|
@@ -172,53 +203,41 @@ async function toolsSearchHandler(ctx) {
|
|
|
172
203
|
: [],
|
|
173
204
|
renderHuman: () =>
|
|
174
205
|
tools
|
|
175
|
-
.map((tool) => `${tool.name}\n ${tool.
|
|
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`)
|
|
176
207
|
.join('\n')
|
|
177
208
|
.trim(),
|
|
178
209
|
});
|
|
179
210
|
}
|
|
180
211
|
|
|
181
212
|
async function toolsDescribeHandler(ctx) {
|
|
182
|
-
const
|
|
183
|
-
const
|
|
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
|
-
}
|
|
213
|
+
const requestedToolName = localNotisToolSlug(ctx.args.toolName);
|
|
214
|
+
const match = await fetchToolSchema(ctx.runtime, requestedToolName);
|
|
196
215
|
|
|
197
216
|
return ctx.output.emitSuccess({
|
|
198
217
|
command: ctx.spec.command_path.join(' '),
|
|
199
|
-
data: { tool: match
|
|
200
|
-
humanSummary: `Described tool ${
|
|
218
|
+
data: { tool: match },
|
|
219
|
+
humanSummary: `Described tool ${requestedToolName}`,
|
|
201
220
|
hints: [
|
|
202
|
-
{ command: `notis tools exec ${
|
|
203
|
-
{ command: `notis tools exec ${
|
|
221
|
+
{ command: `notis tools exec ${requestedToolName} --dry-run --arguments '{}'`, reason: 'Validate arguments first' },
|
|
222
|
+
{ command: `notis tools exec ${requestedToolName} --arguments '{}'`, reason: 'Execute this tool' },
|
|
204
223
|
],
|
|
205
224
|
renderHuman: () => JSON.stringify(match, null, 2),
|
|
206
225
|
});
|
|
207
226
|
}
|
|
208
227
|
|
|
209
228
|
async function toolsExecHandler(ctx) {
|
|
210
|
-
|
|
211
|
-
|
|
229
|
+
const requestedToolName = localNotisToolSlug(ctx.args.toolName);
|
|
230
|
+
ensureLongRunningToolTimeout(ctx, requestedToolName);
|
|
212
231
|
const fileBindings = await parseFileBindings(ctx.options.file);
|
|
213
232
|
|
|
214
233
|
if (ctx.options.getSchema) {
|
|
215
|
-
const tool = await fetchToolSchema(ctx.runtime,
|
|
234
|
+
const tool = await fetchToolSchema(ctx.runtime, requestedToolName);
|
|
216
235
|
return ctx.output.emitSuccess({
|
|
217
236
|
command: ctx.spec.command_path.join(' '),
|
|
218
|
-
data: { tool
|
|
219
|
-
humanSummary: `Schema for ${
|
|
237
|
+
data: { tool },
|
|
238
|
+
humanSummary: `Schema for ${requestedToolName}`,
|
|
220
239
|
hints: [
|
|
221
|
-
{ command: `notis tools exec ${
|
|
240
|
+
{ command: `notis tools exec ${requestedToolName} --dry-run --arguments '{}'`, reason: 'Validate arguments before executing' },
|
|
222
241
|
],
|
|
223
242
|
renderHuman: () => JSON.stringify(tool, null, 2),
|
|
224
243
|
});
|
|
@@ -233,16 +252,16 @@ async function toolsExecHandler(ctx) {
|
|
|
233
252
|
if (fileBindings.length) {
|
|
234
253
|
throw usageError('--file is only supported when executing a tool, not with --dry-run');
|
|
235
254
|
}
|
|
236
|
-
const tool = await fetchToolSchema(ctx.runtime,
|
|
255
|
+
const tool = await fetchToolSchema(ctx.runtime, requestedToolName);
|
|
237
256
|
const errors = validateArguments(tool.parameters, args);
|
|
238
257
|
|
|
239
258
|
if (errors.length) {
|
|
240
259
|
return ctx.output.emitSuccess({
|
|
241
260
|
command: ctx.spec.command_path.join(' '),
|
|
242
|
-
data: { valid: false, errors, tool:
|
|
261
|
+
data: { valid: false, errors, tool: requestedToolName },
|
|
243
262
|
humanSummary: `Dry run failed: ${errors.length} validation error(s)`,
|
|
244
263
|
hints: [
|
|
245
|
-
{ command: `notis tools exec ${
|
|
264
|
+
{ command: `notis tools exec ${requestedToolName} --get-schema`, reason: 'Review the full parameter schema' },
|
|
246
265
|
],
|
|
247
266
|
renderHuman: () => errors.map((e) => ` - ${e}`).join('\n'),
|
|
248
267
|
});
|
|
@@ -250,10 +269,10 @@ async function toolsExecHandler(ctx) {
|
|
|
250
269
|
|
|
251
270
|
return ctx.output.emitSuccess({
|
|
252
271
|
command: ctx.spec.command_path.join(' '),
|
|
253
|
-
data: { valid: true, tool:
|
|
254
|
-
humanSummary: `Dry run passed for ${
|
|
272
|
+
data: { valid: true, tool: requestedToolName, arguments: args },
|
|
273
|
+
humanSummary: `Dry run passed for ${requestedToolName}`,
|
|
255
274
|
hints: [
|
|
256
|
-
{ command: `notis tools exec ${
|
|
275
|
+
{ command: `notis tools exec ${requestedToolName} --arguments '${JSON.stringify(args)}'`, reason: 'Execute with these arguments' },
|
|
257
276
|
],
|
|
258
277
|
});
|
|
259
278
|
}
|
|
@@ -262,11 +281,9 @@ async function toolsExecHandler(ctx) {
|
|
|
262
281
|
|
|
263
282
|
const result = await runToolCommand({
|
|
264
283
|
runtime: ctx.runtime,
|
|
265
|
-
toolName:
|
|
284
|
+
toolName: COMPOSIO_MULTI_EXECUTE_TOOL,
|
|
266
285
|
arguments_: {
|
|
267
|
-
|
|
268
|
-
arguments: args,
|
|
269
|
-
...(toolkits.length ? { toolkits } : {}),
|
|
286
|
+
tools: [{ tool_slug: requestedToolName, arguments: args }],
|
|
270
287
|
},
|
|
271
288
|
mutating: true,
|
|
272
289
|
idempotencyKey,
|
|
@@ -276,7 +293,7 @@ async function toolsExecHandler(ctx) {
|
|
|
276
293
|
return ctx.output.emitSuccess({
|
|
277
294
|
command: ctx.spec.command_path.join(' '),
|
|
278
295
|
data: result.payload,
|
|
279
|
-
humanSummary: `Executed tool ${
|
|
296
|
+
humanSummary: `Executed tool ${requestedToolName}`,
|
|
280
297
|
meta: { mutating: true, idempotency_key: idempotencyKey },
|
|
281
298
|
renderHuman: () => JSON.stringify(result.payload, null, 2),
|
|
282
299
|
});
|
|
@@ -297,16 +314,15 @@ async function toolsExecParallelHandler(ctx) {
|
|
|
297
314
|
throw usageError(`calls[${i}] missing required "tool_name" string`);
|
|
298
315
|
}
|
|
299
316
|
}
|
|
300
|
-
ensureLongRunningToolTimeout(ctx, calls.map((call) => call.tool_name));
|
|
317
|
+
ensureLongRunningToolTimeout(ctx, calls.map((call) => localNotisToolSlug(call.tool_name)));
|
|
301
318
|
|
|
302
319
|
const data = await Promise.all(
|
|
303
320
|
calls.map((call) =>
|
|
304
321
|
runToolCommand({
|
|
305
322
|
runtime: ctx.runtime,
|
|
306
|
-
toolName:
|
|
323
|
+
toolName: COMPOSIO_MULTI_EXECUTE_TOOL,
|
|
307
324
|
arguments_: {
|
|
308
|
-
|
|
309
|
-
arguments: call.arguments || {},
|
|
325
|
+
tools: [{ tool_slug: localNotisToolSlug(call.tool_name), arguments: call.arguments || {} }],
|
|
310
326
|
},
|
|
311
327
|
mutating: true,
|
|
312
328
|
idempotencyKey: nextIdempotencyKey(ctx.globalOptions),
|
|
@@ -369,15 +385,15 @@ async function toolsLinkHandler(ctx) {
|
|
|
369
385
|
export const toolsCommandSpecs = [
|
|
370
386
|
{
|
|
371
387
|
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.',
|
|
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.',
|
|
374
390
|
args_schema: { arguments: [], options: [] },
|
|
375
391
|
examples: ['notis tools toolkits', 'notis tools toolkits --json'],
|
|
376
|
-
output_schema: 'Returns
|
|
392
|
+
output_schema: 'Returns toolkit_connection_statuses from COMPOSIO_SEARCH_TOOLS, rendered as toolkit rows.',
|
|
377
393
|
mutates: false,
|
|
378
394
|
idempotent: true,
|
|
379
395
|
related_commands: ['notis tools search <query>', 'notis tools describe <tool-name>'],
|
|
380
|
-
backend_call: { type: 'tool', name:
|
|
396
|
+
backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
|
|
381
397
|
handler: toolsToolkitsHandler,
|
|
382
398
|
},
|
|
383
399
|
{
|
|
@@ -386,14 +402,14 @@ export const toolsCommandSpecs = [
|
|
|
386
402
|
when_to_use: 'Use this when you need a generic capability that does not have a first-class CLI command.',
|
|
387
403
|
args_schema: {
|
|
388
404
|
arguments: [{ token: '<query>', description: 'Natural language description of the tool you need.' }],
|
|
389
|
-
options: [{ flags: '--
|
|
405
|
+
options: [{ flags: '--known-fields <text>', key: 'knownFields', description: 'Optional known field hints, such as channel_name:general or user_email:a@example.com.' }],
|
|
390
406
|
},
|
|
391
|
-
examples: ['notis tools search "send an email"', 'notis tools search "
|
|
392
|
-
output_schema: 'Returns
|
|
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.',
|
|
393
409
|
mutates: false,
|
|
394
410
|
idempotent: true,
|
|
395
411
|
related_commands: ['notis tools toolkits', 'notis tools exec <tool-name>'],
|
|
396
|
-
backend_call: { type: 'tool', name:
|
|
412
|
+
backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
|
|
397
413
|
handler: toolsSearchHandler,
|
|
398
414
|
},
|
|
399
415
|
{
|
|
@@ -402,14 +418,14 @@ export const toolsCommandSpecs = [
|
|
|
402
418
|
when_to_use: 'Use this when you know the tool name and want its parameter schema before execution.',
|
|
403
419
|
args_schema: {
|
|
404
420
|
arguments: [{ token: '<tool-name>', description: 'Exact tool name to locate and describe.' }],
|
|
405
|
-
options: [
|
|
421
|
+
options: [],
|
|
406
422
|
},
|
|
407
|
-
examples: ['notis tools describe composio-gmail-
|
|
423
|
+
examples: ['notis tools describe composio-gmail-send_email', 'notis tools describe LOCAL_NOTIS_DATABASE_QUERY'],
|
|
408
424
|
output_schema: 'Returns one tool descriptor with name, description, and parameters.',
|
|
409
425
|
mutates: false,
|
|
410
426
|
idempotent: true,
|
|
411
427
|
related_commands: ['notis tools search <query>', 'notis tools exec <tool-name>'],
|
|
412
|
-
backend_call: { type: 'tool', name:
|
|
428
|
+
backend_call: { type: 'tool', name: COMPOSIO_GET_TOOL_SCHEMAS },
|
|
413
429
|
handler: toolsDescribeHandler,
|
|
414
430
|
},
|
|
415
431
|
{
|
|
@@ -423,23 +439,22 @@ export const toolsCommandSpecs = [
|
|
|
423
439
|
{ flags: '--file <argument-path=local-path>', description: 'Upload a local file into a file-uploadable tool argument. Repeatable.', collect: true },
|
|
424
440
|
{ flags: '--get-schema', description: 'Display the tool parameter schema without executing.' },
|
|
425
441
|
{ 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
442
|
],
|
|
428
443
|
},
|
|
429
444
|
examples: [
|
|
430
|
-
'notis tools exec
|
|
431
|
-
'notis tools exec
|
|
432
|
-
'notis tools exec
|
|
433
|
-
'notis tools exec
|
|
434
|
-
'notis tools exec
|
|
435
|
-
'notis tools exec
|
|
436
|
-
'notis tools exec composio-dropbox-
|
|
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',
|
|
437
452
|
],
|
|
438
453
|
output_schema: 'Returns the raw tool execution payload.',
|
|
439
454
|
mutates: true,
|
|
440
455
|
idempotent: true,
|
|
441
456
|
related_commands: ['notis tools search <query>', 'notis tools describe <tool-name>', 'notis tools exec-parallel <calls>'],
|
|
442
|
-
backend_call: { type: 'tool', name:
|
|
457
|
+
backend_call: { type: 'tool', name: COMPOSIO_MULTI_EXECUTE_TOOL },
|
|
443
458
|
handler: toolsExecHandler,
|
|
444
459
|
},
|
|
445
460
|
{
|
|
@@ -455,13 +470,13 @@ export const toolsCommandSpecs = [
|
|
|
455
470
|
],
|
|
456
471
|
},
|
|
457
472
|
examples: [
|
|
458
|
-
'notis tools exec-parallel \'[{"tool_name":"
|
|
473
|
+
'notis tools exec-parallel \'[{"tool_name":"LOCAL_NOTIS_DATABASE_QUERY","arguments":{"database_slug":"tasks","query":{}}},{"tool_name":"LOCAL_NOTIS_DATABASE_LIST_DATABASES","arguments":{}}]\'',
|
|
459
474
|
],
|
|
460
475
|
output_schema: 'Returns an array of results, one per tool call.',
|
|
461
476
|
mutates: true,
|
|
462
477
|
idempotent: true,
|
|
463
478
|
related_commands: ['notis tools exec <tool-name>', 'notis tools search <query>'],
|
|
464
|
-
backend_call: { type: 'tool', name:
|
|
479
|
+
backend_call: { type: 'tool', name: COMPOSIO_MULTI_EXECUTE_TOOL },
|
|
465
480
|
handler: toolsExecParallelHandler,
|
|
466
481
|
},
|
|
467
482
|
{
|
package/src/runtime/profiles.js
CHANGED
|
@@ -212,15 +212,21 @@ export function resolveRuntimeProfile(globalOptions = {}, { requireAuth = true }
|
|
|
212
212
|
});
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
// An explicit NOTIS_JWT is a complete credential override: use it verbatim and do
|
|
216
|
+
// NOT attempt a stored-profile dev_portal token refresh (whose refresh_token may be
|
|
217
|
+
// stale or belong to a different session). Without this, a long-lived shell with an
|
|
218
|
+
// expired profile refresh_token fails every command with invalid_grant even though
|
|
219
|
+
// the supplied NOTIS_JWT is valid.
|
|
220
|
+
const usingEnvJwt = Boolean(process.env.NOTIS_JWT);
|
|
215
221
|
return {
|
|
216
222
|
config,
|
|
217
223
|
profileName,
|
|
218
224
|
apiBase,
|
|
219
225
|
jwt,
|
|
220
|
-
authMode: profile.auth_mode,
|
|
221
|
-
refreshToken: profile.refresh_token,
|
|
222
|
-
accessExpiresAt: profile.access_expires_at,
|
|
223
|
-
refreshExpiresAt: profile.refresh_expires_at,
|
|
226
|
+
authMode: usingEnvJwt ? 'jwt' : profile.auth_mode,
|
|
227
|
+
refreshToken: usingEnvJwt ? undefined : profile.refresh_token,
|
|
228
|
+
accessExpiresAt: usingEnvJwt ? undefined : profile.access_expires_at,
|
|
229
|
+
refreshExpiresAt: usingEnvJwt ? undefined : profile.refresh_expires_at,
|
|
224
230
|
agentMode,
|
|
225
231
|
nonInteractive,
|
|
226
232
|
outputMode,
|
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: {
|
package/template/app/page.tsx
CHANGED
|
@@ -25,7 +25,7 @@ type QueryItemsResult = {
|
|
|
25
25
|
|
|
26
26
|
export default function HomePage() {
|
|
27
27
|
const { app, ready } = useNotis();
|
|
28
|
-
const queryItems = useTool<QueryItemsArgs, QueryItemsResult>('
|
|
28
|
+
const queryItems = useTool<QueryItemsArgs, QueryItemsResult>('LOCAL_NOTIS_DATABASE_QUERY');
|
|
29
29
|
const [documents, setDocuments] = useState<ItemDocument[]>([]);
|
|
30
30
|
|
|
31
31
|
useEffect(() => {
|
package/template/notis.config.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface UseToolResult<
|
|
|
25
25
|
* Call a specific Notis tool by name.
|
|
26
26
|
*
|
|
27
27
|
* ```tsx
|
|
28
|
-
* const { call, loading } = useTool('
|
|
28
|
+
* const { call, loading } = useTool('LOCAL_NOTIS_WEB_SEARCH');
|
|
29
29
|
* const result = await call({ query: 'latest news' });
|
|
30
30
|
* ```
|
|
31
31
|
*/
|