@notis_ai/cli 0.2.0-beta.102.1 → 0.2.0-beta.104.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 CHANGED
@@ -281,13 +281,19 @@ Examples:
281
281
 
282
282
  ### `npx --package @notis_ai/cli@latest -- notis tools link <toolkit>`
283
283
 
284
- Get the URL to connect an integration toolkit.
284
+ Connect or reconnect an integration toolkit.
285
285
 
286
- When to use: Use this when a tool requires authentication with an external service.
286
+ When to use: Use this when a tool requires authentication or an active connection must be replaced.
287
+
288
+ Options:
289
+ - `--reconnect` — Replace the existing account instead of adding another connection.
290
+ - `--connection-id <id>` — Exact connection id to replace when multiple accounts exist.
291
+ - `--label <label>` — Account label for a new or replacement connection.
292
+ - `--credentials <json>` — Credential JSON object, @file path, or - for stdin. Prefer stdin so secrets do not enter shell history.
287
293
 
288
294
  Examples:
289
295
  - `npx --package @notis_ai/cli@latest -- notis tools link github`
290
- - `npx --package @notis_ai/cli@latest -- notis tools link gmail --json`
296
+ - `npx --package @notis_ai/cli@latest -- notis tools link dataforseo --reconnect --credentials - < credentials.json`
291
297
 
292
298
 
293
299
  ## Meta Commands
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notis_ai/cli",
3
- "version": "0.2.0-beta.102.1",
3
+ "version": "0.2.0-beta.104.1",
4
4
  "description": "Agent-first Notis CLI for apps and generic tool execution",
5
5
  "type": "module",
6
6
  "bin": {
@@ -140,8 +140,9 @@ This is the main escape hatch for:
140
140
  - `npx --package @notis_ai/cli@latest -- notis tools exec <tool-name> --arguments '<json>'`
141
141
  6. If multiple independent calls are needed, use:
142
142
  - `npx --package @notis_ai/cli@latest -- notis tools exec-parallel '<json-array>'`
143
- 7. If the toolkit is not connected yet, generate the connection URL:
143
+ 7. If the toolkit is not connected yet, start its connection flow:
144
144
  - `npx --package @notis_ai/cli@latest -- notis tools link <toolkit>`
145
+ - For a revoked or invalid credential-based connection, reconnect with credential JSON on stdin: `npx --package @notis_ai/cli@latest -- notis tools link <toolkit> --reconnect --credentials -`
145
146
 
146
147
  ### Tool access rules
147
148
 
@@ -150,6 +151,8 @@ This is the main escape hatch for:
150
151
  - When you know the tool name but not the argument shape, use `npx --package @notis_ai/cli@latest -- notis tools describe` or `--get-schema` before execution.
151
152
  - Use `--dry-run` before mutating calls when you want schema validation without execution.
152
153
  - If a toolkit is missing, use `npx --package @notis_ai/cli@latest -- notis tools link <toolkit>` to start the connection flow.
154
+ - Use `--reconnect` to replace an existing connection. If multiple accounts exist, select one with `--connection-id <id>`.
155
+ - For API keys, basic auth, or other credential JSON, prefer `--credentials -` and pipe or redirect stdin. Avoid inline secrets because they can enter shell history and process listings.
153
156
 
154
157
  ### Toolkit mental model
155
158
 
@@ -202,6 +205,12 @@ Connect a missing toolkit:
202
205
  npx --package @notis_ai/cli@latest -- notis tools link github
203
206
  ```
204
207
 
208
+ Reconnect a credential-based toolkit without putting the secret in shell history:
209
+
210
+ ```bash
211
+ npx --package @notis_ai/cli@latest -- notis tools link dataforseo --reconnect --credentials - < credentials.json
212
+ ```
213
+
205
214
  ## Native database access
206
215
 
207
216
  Native Notis databases are accessed through the generic tool workflow, not a first-class database command group. Use these canonical tool names:
@@ -342,43 +342,48 @@ async function toolsExecParallelHandler(ctx) {
342
342
  }
343
343
 
344
344
  async function toolsLinkHandler(ctx) {
345
- const apiBase = ctx.runtime.apiBase.replace(/\/+$/, '');
346
- let portalBase;
347
- try {
348
- const parsed = new URL(apiBase);
349
- if (parsed.hostname.startsWith('api.')) {
350
- parsed.hostname = parsed.hostname.replace(/^api\./, 'app.');
351
- portalBase = parsed.origin;
352
- } else if (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1') {
353
- const apiPort = parseInt(parsed.port, 10) || 80;
354
- parsed.port = String(apiPort === 3001 ? 3000 : apiPort);
355
- portalBase = parsed.origin;
356
- } else {
357
- portalBase = apiBase;
358
- }
359
- } catch {
360
- portalBase = apiBase.replace('api.', 'app.');
345
+ const credentials = ctx.options.credentials
346
+ ? await resolveJsonInput(ctx.options.credentials, 'credentials')
347
+ : undefined;
348
+ if (credentials !== undefined && (typeof credentials !== 'object' || credentials === null || Array.isArray(credentials))) {
349
+ throw usageError('credentials must be a JSON object');
350
+ }
351
+
352
+ const arguments_ = {
353
+ toolkit: ctx.args.toolkit,
354
+ ...(ctx.options.reconnect ? { reconnect: true } : {}),
355
+ ...(ctx.options.connectionId ? { reconnect_connection_id: ctx.options.connectionId } : {}),
356
+ ...(ctx.options.label ? { label: ctx.options.label } : {}),
357
+ ...(credentials ? { credentials } : {}),
358
+ };
359
+ const idempotencyKey = nextIdempotencyKey(ctx.globalOptions);
360
+ const result = await runToolCommand({
361
+ runtime: ctx.runtime,
362
+ toolName: 'LOCAL_NOTIS_AUTHENTIFY',
363
+ arguments_,
364
+ mutating: true,
365
+ idempotencyKey,
366
+ });
367
+ const payload = result.payload || {};
368
+ if (payload.status === 'error') {
369
+ throw usageError(payload.message || `Failed to connect ${ctx.args.toolkit}`, payload);
361
370
  }
362
- const portalUrl = `${portalBase}/integrations?connect=${encodeURIComponent(ctx.args.toolkit)}`;
371
+ const authUrl = payload.redirect_url || payload.integrations_url || payload.url || null;
363
372
 
364
373
  return ctx.output.emitSuccess({
365
374
  command: ctx.spec.command_path.join(' '),
366
375
  data: {
367
- toolkit: ctx.args.toolkit,
368
- auth_url: portalUrl,
376
+ ...payload,
377
+ auth_url: authUrl,
369
378
  },
370
- humanSummary: `Open this URL to connect ${ctx.args.toolkit}`,
379
+ humanSummary: ctx.options.reconnect
380
+ ? `Reconnected ${ctx.args.toolkit}`
381
+ : `Started connection for ${ctx.args.toolkit}`,
371
382
  hints: [
372
383
  { command: 'notis tools toolkits', reason: 'Verify the toolkit is connected after setup' },
373
384
  ],
374
- renderHuman: () =>
375
- [
376
- `Connect ${ctx.args.toolkit}:`,
377
- ` ${portalUrl}`,
378
- '',
379
- 'After connecting, verify with:',
380
- ' notis tools toolkits',
381
- ].join('\n'),
385
+ meta: { mutating: true, idempotency_key: idempotencyKey },
386
+ renderHuman: () => JSON.stringify({ ...payload, auth_url: authUrl }, null, 2),
382
387
  });
383
388
  }
384
389
 
@@ -481,19 +486,27 @@ export const toolsCommandSpecs = [
481
486
  },
482
487
  {
483
488
  command_path: ['tools', 'link'],
484
- summary: 'Get the URL to connect an integration toolkit.',
485
- when_to_use: 'Use this when a tool requires authentication with an external service.',
489
+ summary: 'Connect or reconnect an integration toolkit.',
490
+ when_to_use: 'Use this when a tool requires authentication or an active connection must be replaced.',
486
491
  args_schema: {
487
492
  arguments: [{ token: '<toolkit>', description: 'Toolkit name to connect (e.g. github, gmail, slack).' }],
488
- options: [],
493
+ options: [
494
+ { flags: '--reconnect', description: 'Replace the existing account instead of adding another connection.' },
495
+ { flags: '--connection-id <id>', key: 'connectionId', description: 'Exact connection id to replace when multiple accounts exist.' },
496
+ { flags: '--label <label>', description: 'Account label for a new or replacement connection.' },
497
+ { flags: '--credentials <json>', description: 'Credential JSON object, @file path, or - for stdin. Prefer stdin so secrets do not enter shell history.' },
498
+ ],
489
499
  },
490
- examples: ['notis tools link github', 'notis tools link gmail --json'],
491
- output_schema: 'Returns the authentication URL for the specified toolkit.',
492
- mutates: false,
493
- idempotent: true,
494
- require_auth: false,
500
+ examples: [
501
+ 'notis tools link github',
502
+ 'notis tools link dataforseo --reconnect --credentials - < credentials.json',
503
+ ],
504
+ output_schema: 'Returns the connection result and an authentication URL when provider authorization is still required.',
505
+ mutates: true,
506
+ idempotent: false,
507
+ require_auth: true,
495
508
  related_commands: ['notis tools toolkits', 'notis tools search <query>'],
496
- backend_call: { type: 'local' },
509
+ backend_call: { type: 'tool', name: 'LOCAL_NOTIS_AUTHENTIFY' },
497
510
  handler: toolsLinkHandler,
498
511
  },
499
512
  ];