@notis_ai/cli 0.2.2 → 0.2.3
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 +19 -18
- package/config/notis_app_boundary_rules.json +50 -0
- package/dist/scaffolds/notis-database/app/globals.css +44 -0
- package/dist/scaffolds/notis-database/app/page.tsx +1091 -0
- package/dist/scaffolds/notis-database/components/ui/table.tsx +120 -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/metadata/cover.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-database/notis.config.ts +27 -0
- package/dist/scaffolds/notis-database/package.json +31 -0
- package/dist/scaffolds/notis-database/src/dev-main.tsx +23 -0
- package/dist/scaffolds/notis-database/src/mock-runtime.ts +557 -0
- package/dist/scaffolds/notis-database/tailwind.config.ts +59 -0
- package/dist/scaffolds/{notes → notis-database}/tsconfig.json +2 -1
- package/dist/scaffolds/notis-database/vite.config.ts +22 -0
- package/dist/scaffolds/notis-notes/app/layout.tsx +6 -0
- package/dist/scaffolds/{notes → notis-notes}/app/page.tsx +783 -80
- 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/cover.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/{notes → notis-notes}/notis.config.ts +12 -7
- package/dist/scaffolds/{notes → notis-notes}/package.json +2 -2
- package/dist/scaffolds/notis-notes/postcss.config.mjs +8 -0
- package/dist/scaffolds/notis-notes/tsconfig.json +23 -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/cover.png +0 -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/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-random/notis.config.ts +43 -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 +128 -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 +27 -4
- package/package.json +2 -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/cli.js +30 -2
- package/src/command-specs/apps.js +42 -15
- package/src/command-specs/helpers.js +73 -45
- package/src/command-specs/meta.js +7 -7
- package/src/command-specs/tools.js +196 -83
- package/src/runtime/app-boundary-validator.js +65 -14
- package/src/runtime/profiles.js +10 -4
- package/src/runtime/transport.js +122 -37
- 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/dist/scaffolds/{notes → notis-database}/app/layout.tsx +0 -0
- /package/dist/scaffolds/{notes → notis-database}/components/ui/badge.tsx +0 -0
- /package/dist/scaffolds/{notes → notis-database}/components/ui/button.tsx +0 -0
- /package/dist/scaffolds/{notes → notis-database}/components/ui/card.tsx +0 -0
- /package/dist/scaffolds/{notes → notis-database}/components.json +0 -0
- /package/dist/scaffolds/{notes → notis-database}/lib/utils.ts +0 -0
- /package/dist/scaffolds/{notes → notis-database}/postcss.config.mjs +0 -0
- /package/dist/scaffolds/{notes → notis-notes}/app/globals.css +0 -0
- /package/dist/scaffolds/{notes → notis-notes}/tailwind.config.ts +0 -0
- /package/dist/scaffolds/{notes → notis-notes}/vite.config.ts +0 -0
package/dist/scaffolds.json
CHANGED
|
@@ -2,12 +2,35 @@
|
|
|
2
2
|
"source": "apps/*/notis.config.ts",
|
|
3
3
|
"scaffolds": [
|
|
4
4
|
{
|
|
5
|
-
"slug": "
|
|
5
|
+
"slug": "notis-database",
|
|
6
|
+
"name": "Databases",
|
|
7
|
+
"description": "Read-only catalog and schema explorer for every database in your Notis workspace.",
|
|
8
|
+
"icon": "phosphor:database",
|
|
9
|
+
"categories": [
|
|
10
|
+
"Product & Engineering",
|
|
11
|
+
"Operations"
|
|
12
|
+
],
|
|
13
|
+
"tagline": "Explore every database and schema in your Notis workspace."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"slug": "notis-notes",
|
|
6
17
|
"name": "Notis Notes",
|
|
7
18
|
"description": "Browse notes by documents, table, or calendar with folders in the sidebar.",
|
|
8
|
-
"icon": "
|
|
9
|
-
"categories": [
|
|
10
|
-
|
|
19
|
+
"icon": "phosphor:note-pencil",
|
|
20
|
+
"categories": [
|
|
21
|
+
"Productivity"
|
|
22
|
+
],
|
|
23
|
+
"tagline": "Folder-based notes with gallery, table, and calendar views."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"slug": "notis-random",
|
|
27
|
+
"name": "Random Number Generator",
|
|
28
|
+
"description": "Generate random numbers with configurable bounds, and keep a history of everything you rolled.",
|
|
29
|
+
"icon": "phosphor:dice-five",
|
|
30
|
+
"categories": [
|
|
31
|
+
"Personal"
|
|
32
|
+
],
|
|
33
|
+
"tagline": "Roll dice, keep history."
|
|
11
34
|
}
|
|
12
35
|
]
|
|
13
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notis_ai/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Agent-first Notis CLI for apps and generic tool execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
|
+
"config/",
|
|
11
12
|
"dist/",
|
|
12
13
|
"src/",
|
|
13
14
|
"skills/",
|
|
@@ -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
|
-
|
package/src/cli.js
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
1
4
|
import { Command } from 'commander';
|
|
2
5
|
import { COMMAND_SPECS, GROUP_SUMMARIES } from './command-specs/index.js';
|
|
3
6
|
import { OutputManager } from './runtime/output.js';
|
|
4
7
|
import { asCliError } from './runtime/errors.js';
|
|
5
8
|
import { resolveRuntimeProfile, workspacePath } from './runtime/profiles.js';
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
// Read the version from package.json: the publish pipeline bumps the manifest
|
|
11
|
+
// (scripts/release-utils.js applyVersion), so a hardcoded string here goes
|
|
12
|
+
// stale on every release.
|
|
13
|
+
function readCliVersion() {
|
|
14
|
+
try {
|
|
15
|
+
const manifestPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
16
|
+
const version = JSON.parse(readFileSync(manifestPath, 'utf-8')).version;
|
|
17
|
+
if (typeof version === 'string' && version.trim()) {
|
|
18
|
+
return version.trim();
|
|
19
|
+
}
|
|
20
|
+
} catch {
|
|
21
|
+
// Fall through to the placeholder below.
|
|
22
|
+
}
|
|
23
|
+
return '0.0.0';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const CLI_VERSION = readCliVersion();
|
|
8
27
|
|
|
9
28
|
function buildHelpFooter(spec) {
|
|
10
29
|
const lines = [
|
|
@@ -64,7 +83,16 @@ function attachSpec(program, parentMap, spec, specs) {
|
|
|
64
83
|
}
|
|
65
84
|
|
|
66
85
|
for (const option of spec.args_schema?.options || []) {
|
|
67
|
-
|
|
86
|
+
if (option.collect) {
|
|
87
|
+
command.option(
|
|
88
|
+
option.flags,
|
|
89
|
+
option.description,
|
|
90
|
+
(value, previous) => [...(previous || []), value],
|
|
91
|
+
[],
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
command.option(option.flags, option.description);
|
|
95
|
+
}
|
|
68
96
|
}
|
|
69
97
|
|
|
70
98
|
command.action(async (...raw) => {
|
|
@@ -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
|
}
|
|
@@ -44,39 +55,32 @@ export async function runToolCommand({
|
|
|
44
55
|
arguments_ = {},
|
|
45
56
|
mutating = false,
|
|
46
57
|
idempotencyKey,
|
|
58
|
+
fileBindings = [],
|
|
47
59
|
}) {
|
|
48
60
|
const result = await callTool({
|
|
49
61
|
runtime: { ...runtime, mutating },
|
|
50
62
|
toolName,
|
|
51
63
|
arguments_,
|
|
52
64
|
idempotencyKey: mutating ? idempotencyKey : null,
|
|
65
|
+
fileBindings,
|
|
53
66
|
});
|
|
54
67
|
return result;
|
|
55
68
|
}
|
|
56
69
|
|
|
57
70
|
export async function fetchToolkits(runtime) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (toolkits.length) {
|
|
68
|
-
return toolkits;
|
|
69
|
-
}
|
|
70
|
-
const availableToolkits = await fetchToolkits(runtime);
|
|
71
|
-
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
|
+
}));
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
export async function probeAuth(runtime) {
|
|
75
|
-
|
|
76
|
-
runtime,
|
|
77
|
-
toolName: 'notis_find_toolkits',
|
|
78
|
-
});
|
|
79
|
-
return result.payload;
|
|
83
|
+
return fetchToolDiscovery(runtime, 'List available toolkit namespaces and connection statuses');
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
export async function healthCheck(runtime) {
|
|
@@ -88,22 +92,46 @@ export async function healthCheck(runtime) {
|
|
|
88
92
|
});
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
export async function fetchToolSchema(runtime, toolName
|
|
92
|
-
const toolkits = normalizeToolkits(rawToolkits);
|
|
95
|
+
export async function fetchToolSchema(runtime, toolName) {
|
|
93
96
|
const result = await runToolCommand({
|
|
94
97
|
runtime,
|
|
95
|
-
toolName:
|
|
98
|
+
toolName: COMPOSIO_GET_TOOL_SCHEMAS,
|
|
96
99
|
arguments_: {
|
|
97
|
-
|
|
98
|
-
...(toolkits.length ? { toolkits } : {}),
|
|
100
|
+
tool_slugs: [toolName],
|
|
99
101
|
},
|
|
100
102
|
});
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
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) {
|
|
104
110
|
throw usageError(`Tool "${toolName}" not found.`);
|
|
105
111
|
}
|
|
106
|
-
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 || {};
|
|
107
135
|
}
|
|
108
136
|
|
|
109
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
|
{
|