@ductape/cli 0.3.17 → 0.3.18

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.
@@ -0,0 +1,8 @@
1
+ import { type CommandInteractiveFlags } from '../lib/interactive-opts.js';
2
+ export declare function runAppActions(verb: string, opts: {
3
+ profile?: string;
4
+ app?: string;
5
+ action?: string;
6
+ file?: string;
7
+ json?: boolean;
8
+ } & CommandInteractiveFlags): Promise<void>;
@@ -0,0 +1,56 @@
1
+ import { toInteractiveOpts } from '../lib/interactive-opts.js';
2
+ import { bodyRequiredHint, resolveBody } from '../lib/read-body.js';
3
+ import { createAppAction, deleteAppAction, getAppAction, listAppActions, updateAppAction, } from '../lib/platform-api.js';
4
+ import { printJson } from '../lib/output.js';
5
+ import { requireWorkspaceContext } from '../lib/workspace-context.js';
6
+ const VERBS = ['list', 'get', 'create', 'update', 'delete'];
7
+ export async function runAppActions(verb, opts) {
8
+ const v = verb.toLowerCase();
9
+ if (!VERBS.includes(v)) {
10
+ throw new Error(`Unknown apps actions verb "${verb}". Use: ${VERBS.join(', ')}`);
11
+ }
12
+ if (!opts.app)
13
+ throw new Error('Provide --app <app_tag>');
14
+ const ctx = requireWorkspaceContext({ profile: opts.profile });
15
+ const interactive = toInteractiveOpts(opts);
16
+ const body = v === 'create' || v === 'update'
17
+ ? await resolveBody({
18
+ ...interactive,
19
+ file: opts.file,
20
+ patch: v === 'update',
21
+ requireBody: v === 'create',
22
+ })
23
+ : undefined;
24
+ let result;
25
+ switch (v) {
26
+ case 'list':
27
+ result = await listAppActions(ctx, opts.app);
28
+ break;
29
+ case 'get':
30
+ if (!opts.action)
31
+ throw new Error('Provide --action <action_tag>');
32
+ result = await getAppAction(ctx, opts.app, opts.action);
33
+ break;
34
+ case 'create':
35
+ if (!body || Object.keys(body).length === 0) {
36
+ throw new Error(`create requires a body. ${bodyRequiredHint('apps:actions:create')}`);
37
+ }
38
+ result = await createAppAction(ctx, opts.app, body);
39
+ break;
40
+ case 'update': {
41
+ const actionTag = opts.action ?? body?.tag;
42
+ if (!actionTag)
43
+ throw new Error('update requires --action <action_tag>');
44
+ result = await updateAppAction(ctx, opts.app, actionTag, body ?? {});
45
+ break;
46
+ }
47
+ case 'delete':
48
+ if (!opts.action)
49
+ throw new Error('Provide --action <action_tag>');
50
+ result = await deleteAppAction(ctx, opts.app, opts.action);
51
+ break;
52
+ default:
53
+ throw new Error(`Unsupported: apps actions ${v}`);
54
+ }
55
+ printJson(result, Boolean(opts.json));
56
+ }
package/dist/index.js CHANGED
@@ -27,6 +27,7 @@ import { runCompletion } from './commands/completion.js';
27
27
  import { runProducts, runProductApps, runProductAppActions, runProductComponents, runProductEnvironments } from './commands/products.js';
28
28
  import { runApps } from './commands/apps.js';
29
29
  import { runAppsImport } from './commands/apps-import.js';
30
+ import { runAppActions } from './commands/app-actions.js';
30
31
  import { runMarketplace } from './commands/marketplace.js';
31
32
  import { runApply } from './commands/apply.js';
32
33
  import { runMigrateCodebase } from './commands/migrate-codebase.js';
@@ -663,6 +664,49 @@ apps
663
664
  version: opts.version,
664
665
  json: opts.json,
665
666
  })));
667
+ const appActions = apps.command('actions').description("An app's own actions (endpoints). resource is a path relative to each of the app's " +
668
+ 'environment base_urls (e.g. "/v1/users/{id}"), not a full URL.');
669
+ appActions
670
+ .command('list')
671
+ .requiredOption('--app <tag>', 'App tag')
672
+ .option('--profile <name>')
673
+ .option('--json', 'JSON output')
674
+ .action(wrap((opts) => runAppActions('list', opts)));
675
+ appActions
676
+ .command('get')
677
+ .requiredOption('--app <tag>', 'App tag')
678
+ .requiredOption('--action <tag>', 'Action tag')
679
+ .option('--profile <name>')
680
+ .option('--json', 'JSON output')
681
+ .action(wrap((opts) => runAppActions('get', opts)));
682
+ appActions
683
+ .command('create')
684
+ .requiredOption('--app <tag>', 'App tag')
685
+ // NOTE: cannot use -f/--file here — it collides with the parent `apps` command's own -f/--file
686
+ // option (used by `apps create`/`apps update`). Commander resolves a flag shared by an ancestor
687
+ // and a descendant command against the ancestor, so the value never reaches this subcommand's
688
+ // own opts, and requiredOption fails even when -f is supplied. Same fix as productEnvironments
689
+ // create/update (--env-file) above.
690
+ .requiredOption('--action-file <path>', 'JSON body: { tag, name, resource, method, description?, params?, query?, headers?, body?, response? }')
691
+ .option('--profile <name>')
692
+ .option('--json', 'JSON output')
693
+ .action(wrap((opts) => runAppActions('create', { ...opts, file: opts.actionFile })));
694
+ appActions
695
+ .command('update')
696
+ .requiredOption('--app <tag>', 'App tag')
697
+ .requiredOption('--action <tag>', 'Action tag')
698
+ // See NOTE above on `create` — same -f/--file collision with the parent `apps` command.
699
+ .requiredOption('--action-file <path>', 'JSON body (partial update)')
700
+ .option('--profile <name>')
701
+ .option('--json', 'JSON output')
702
+ .action(wrap((opts) => runAppActions('update', { ...opts, file: opts.actionFile })));
703
+ appActions
704
+ .command('delete')
705
+ .requiredOption('--app <tag>', 'App tag')
706
+ .requiredOption('--action <tag>', 'Action tag')
707
+ .option('--profile <name>')
708
+ .option('--json', 'JSON output')
709
+ .action(wrap((opts) => runAppActions('delete', opts)));
666
710
  program
667
711
  .command('marketplace')
668
712
  .description('Discover public apps by capability and inspect their actions')
@@ -24,6 +24,11 @@ export declare function createApp(ctx: WorkspaceContext, body: Record<string, un
24
24
  export declare function updateApp(ctx: WorkspaceContext, appId: string, body: Record<string, unknown>): Promise<unknown>;
25
25
  export declare function updateAppViaProxy(ctx: WorkspaceContext, tag: string, body: Record<string, unknown>): Promise<unknown>;
26
26
  export declare function deleteApp(ctx: WorkspaceContext, appId: string): Promise<unknown>;
27
+ export declare function listAppActions(ctx: WorkspaceContext, appTag: string): Promise<unknown>;
28
+ export declare function getAppAction(ctx: WorkspaceContext, appTag: string, actionTag: string): Promise<unknown>;
29
+ export declare function createAppAction(ctx: WorkspaceContext, appTag: string, body: Record<string, unknown>): Promise<unknown>;
30
+ export declare function updateAppAction(ctx: WorkspaceContext, appTag: string, actionTag: string, body: Record<string, unknown>): Promise<unknown>;
31
+ export declare function deleteAppAction(ctx: WorkspaceContext, appTag: string, actionTag: string): Promise<unknown>;
27
32
  export interface MarketplaceSearchOptions {
28
33
  query?: string;
29
34
  category?: string;
@@ -333,6 +333,26 @@ export async function deleteApp(ctx, appId) {
333
333
  };
334
334
  return client(ctx).delete(`/apps/v1/${appId}?${new URLSearchParams(q).toString()}`);
335
335
  }
336
+ // —— App actions (sdk-proxy — admin op, requires access key) ——
337
+ //
338
+ // resource is a path RELATIVE to each of the app's environment base_urls
339
+ // (e.g. "/v1/users/{id}", not a full URL) — Ductape joins base_url + resource
340
+ // per environment at call time.
341
+ export async function listAppActions(ctx, appTag) {
342
+ return unwrapList(await proxy(ctx).execute('actions', 'list', [appTag]));
343
+ }
344
+ export async function getAppAction(ctx, appTag, actionTag) {
345
+ return unwrapOne(await proxy(ctx).execute('actions', 'fetch', [appTag, actionTag]));
346
+ }
347
+ export async function createAppAction(ctx, appTag, body) {
348
+ return unwrapOne(await proxy(ctx).execute('actions', 'create', [appTag, body]));
349
+ }
350
+ export async function updateAppAction(ctx, appTag, actionTag, body) {
351
+ return unwrapOne(await proxy(ctx).execute('actions', 'update', [appTag, actionTag, body]));
352
+ }
353
+ export async function deleteAppAction(ctx, appTag, actionTag) {
354
+ return proxy(ctx).execute('actions', 'delete', [appTag, actionTag]);
355
+ }
336
356
  function searchableMarketplaceText(app) {
337
357
  if (!app || typeof app !== 'object')
338
358
  return '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/cli",
3
- "version": "0.3.17",
3
+ "version": "0.3.18",
4
4
  "description": "Ductape CLI — local platform, login, link projects, and manage resources via the proxy (Workbench-compatible)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",