@pitcher/cli-scripts 2.2.2 → 2.4.0

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.
Files changed (44) hide show
  1. package/README.md +203 -0
  2. package/dist/index.js +26 -2
  3. package/dist/src/commands/analytics.d.ts +12 -0
  4. package/dist/src/commands/analytics.js +45 -0
  5. package/dist/src/commands/announcements.d.ts +23 -0
  6. package/dist/src/commands/announcements.js +131 -0
  7. package/dist/src/commands/canvases.d.ts +25 -0
  8. package/dist/src/commands/canvases.js +73 -0
  9. package/dist/src/commands/dsrLanding.d.ts +28 -0
  10. package/dist/src/commands/dsrLanding.js +150 -0
  11. package/dist/src/commands/emailTemplate.d.ts +32 -0
  12. package/dist/src/commands/emailTemplate.js +124 -0
  13. package/dist/src/commands/files.d.ts +46 -0
  14. package/dist/src/commands/files.js +167 -0
  15. package/dist/src/commands/folders.d.ts +33 -0
  16. package/dist/src/commands/folders.js +120 -0
  17. package/dist/src/commands/instances.d.ts +21 -0
  18. package/dist/src/commands/instances.js +58 -0
  19. package/dist/src/commands/org.d.ts +9 -0
  20. package/dist/src/commands/org.js +26 -0
  21. package/dist/src/commands/scaffolds.d.ts +19 -0
  22. package/dist/src/commands/scaffolds.js +176 -0
  23. package/dist/src/commands/search.d.ts +13 -0
  24. package/dist/src/commands/search.js +35 -0
  25. package/dist/src/commands/sharedLinks.d.ts +16 -0
  26. package/dist/src/commands/sharedLinks.js +61 -0
  27. package/dist/src/commands/tags.d.ts +20 -0
  28. package/dist/src/commands/tags.js +52 -0
  29. package/dist/src/commands/withAuth.d.ts +3 -0
  30. package/dist/src/commands/withAuth.js +7 -0
  31. package/dist/src/commands/withInstance.d.ts +3 -0
  32. package/dist/src/commands/withInstance.js +4 -0
  33. package/dist/src/core/baseUrl.js +2 -2
  34. package/dist/src/core/http.d.ts +10 -0
  35. package/dist/src/core/http.js +6 -0
  36. package/dist/src/core/pagination.d.ts +30 -0
  37. package/dist/src/core/pagination.js +47 -0
  38. package/dist/src/core/readInput.d.ts +2 -0
  39. package/dist/src/core/readInput.js +23 -0
  40. package/dist/src/core/upload.d.ts +15 -0
  41. package/dist/src/core/upload.js +30 -0
  42. package/dist/src/schema/content.d.ts +12 -0
  43. package/dist/src/schema/content.js +63 -0
  44. package/package.json +3 -3
package/README.md CHANGED
@@ -86,3 +86,206 @@ p dev-sync watch --file-id <pitcher_file_id> --org pitcher
86
86
  - Run from your app's project root (with `dist/` folder) or directly from the `dist/` folder
87
87
  - The `watch` command automatically reverts changes on graceful shutdown (Ctrl+C)
88
88
  - Apps with dev override show a badge in the installed apps view
89
+
90
+ ## To run the `files` command:
91
+
92
+ Manage Pitcher content files — list within an instance, fetch by id, create (upload or link), or update.
93
+
94
+ ```bash
95
+ # List files in an instance (paginated)
96
+ p files list --instance-id 01... --api-key myapikey
97
+
98
+ # Filter by tags or specific IDs
99
+ p files list --instance-id 01... --tags brochure,demo --api-key myapikey
100
+
101
+ # Get a single file by id
102
+ p files get 01FILEID... --api-key myapikey
103
+
104
+ # Create a file from a local upload
105
+ p files create --instance-id 01... --name "Q3 Deck" --file ./deck.pdf --api-key myapikey
106
+
107
+ # Create a link/WEB file from a source URL
108
+ p files create --instance-id 01... --name "Product Page" --source-url https://example.com --api-key myapikey
109
+
110
+ # Update a file's tags or metadata (metadata/app-metadata are deep-merged, not replaced)
111
+ p files update 01FILEID... --tags updated,reviewed --api-key myapikey
112
+ ```
113
+
114
+ ## To run the `folders` command:
115
+
116
+ Manage Pitcher content folders — list, fetch (or the instance root), create, and update smart-folder rules.
117
+
118
+ ```bash
119
+ # List folders in an instance (paginated)
120
+ p folders list --instance-id 01... --api-key myapikey
121
+
122
+ # Get a folder by id
123
+ p folders get 01FOLDERID... --api-key myapikey
124
+
125
+ # Get the instance root folder
126
+ p folders get --root --instance-id 01... --api-key myapikey
127
+
128
+ # Create a folder under a parent
129
+ p folders create --name "Marketing" --parent-folder-id 01PARENTID... --api-key myapikey
130
+
131
+ # Update a folder's name or access type
132
+ p folders update 01FOLDERID... --name "Sales Enablement" --access-type restricted --api-key myapikey
133
+ ```
134
+
135
+ ## To run the `search` command:
136
+
137
+ ```bash
138
+ # Ranked cross-type search (files, canvases, users, apps) within an instance
139
+ p search --instance-id 01... --query "quarterly deck" --api-key myapikey
140
+ ```
141
+
142
+ ## To run the `instances` command:
143
+
144
+ Discover Pitcher instances — resolve names to instance IDs before running other commands.
145
+
146
+ ```bash
147
+ # List instances (optionally filter by region/storage-region/environment)
148
+ p instances list --api-key myapikey
149
+
150
+ p instances list --environment Demo --api-key myapikey
151
+
152
+ # Get a single instance by id
153
+ p instances info 01INSTANCEID... --api-key myapikey
154
+ ```
155
+
156
+ ## To run the `canvases` command:
157
+
158
+ Read Pitcher canvases — presentations, sections, and blocks.
159
+
160
+ ```bash
161
+ # List canvases in an instance (paginated)
162
+ p canvases list --instance-id 01... --api-key myapikey
163
+
164
+ # Only templates or only sections
165
+ p canvases list --instance-id 01... --is-template --api-key myapikey
166
+ p canvases list --instance-id 01... --is-section --api-key myapikey
167
+
168
+ # Only blocks (blocks are hidden unless --is-block is passed)
169
+ p canvases list --instance-id 01... --is-block --api-key myapikey
170
+
171
+ # Get a single canvas by id
172
+ p canvases get 01CANVASID... --api-key myapikey
173
+ ```
174
+
175
+ ## To run the `shared-links` command:
176
+
177
+ Create and read share links for canvases or files (returns the `short_url`).
178
+
179
+ ```bash
180
+ # Create a share link for a canvas
181
+ p shared-links create --canvas-id 01CANVASID... --api-key myapikey
182
+
183
+ # Create a share link for a file
184
+ p shared-links create --file-id 01FILEID... --api-key myapikey
185
+
186
+ # Fetch the existing share link for a canvas
187
+ p shared-links get --canvas-id 01CANVASID... --api-key myapikey
188
+ ```
189
+
190
+ ## To run the `tags` command:
191
+
192
+ List tags in an instance (the server requires a search term of at least 3 characters).
193
+
194
+ ```bash
195
+ # List tags matching a search term
196
+ p tags list --instance-id 01... --search "demo" --api-key myapikey
197
+
198
+ # Filter by type
199
+ p tags list --instance-id 01... --search "demo" --type canvases --api-key myapikey
200
+ ```
201
+
202
+ ## To run the `email-template` command:
203
+
204
+ Create/list/get HTML email templates for the emails app. `create`, `list`, and `get` require `--instance-id` (`scaffold` does not).
205
+
206
+ ```bash
207
+ # Create a template from an HTML file (inline CSS)
208
+ p email-template create --instance-id 01... --name "Welcome" --body-file ./welcome.html --api-key myapikey
209
+
210
+ # List templates (optionally filter by name/status/category/target)
211
+ p email-template list --instance-id 01... --api-key myapikey
212
+
213
+ # Get a template by id
214
+ p email-template get --instance-id 01... --id 01TEMPLATEID... --api-key myapikey
215
+
216
+ # Generate a starter HTML file with tokens in context
217
+ p email-template scaffold --out ./welcome.html --api-key myapikey
218
+ ```
219
+
220
+ Run `p email-template create --help` for the full token/marker reference.
221
+
222
+ `scaffold` returns the starter HTML in the envelope `data` (never printed raw to stdout); pass `--out <path>` to write it to a file instead.
223
+
224
+ ## To run the `dsr-landing` command:
225
+
226
+ Create/list DSR (Digital Sales Room) landing-page templates, and set one as the instance-scope default. `create`, `list`, and `set-default` require `--instance-id` (`scaffold` does not).
227
+
228
+ ```bash
229
+ # Create a landing template from an HTML file (must contain the email-gate marker)
230
+ p dsr-landing create --instance-id 01... --name "Default" --html-file ./landing.html --api-key myapikey
231
+
232
+ # Create and set as default in one call
233
+ p dsr-landing create --instance-id 01... --name "Default" --html-file ./landing.html --default --api-key myapikey
234
+
235
+ # Set an existing template as the default
236
+ p dsr-landing set-default --instance-id 01... --id 01TEMPLATEID... --api-key myapikey
237
+
238
+ # List templates in an instance
239
+ p dsr-landing list --instance-id 01... --api-key myapikey
240
+
241
+ # Generate a starter HTML file with the required marker and tokens in context
242
+ p dsr-landing scaffold --out ./landing.html --api-key myapikey
243
+ ```
244
+
245
+ `--html-file` must contain the `data-pitcher-email-gate` marker verbatim (the platform injects the email-gate form there) — `create` fails client-side before any request if it's missing.
246
+
247
+ Run `p dsr-landing create --help` for the full token/marker reference (editable-region attributes, recipient/customer/org/rep tokens).
248
+
249
+ `scaffold` returns the starter HTML in the envelope `data`; pass `--out <path>` to write it to a file instead.
250
+
251
+ ## To run the `announcements` command:
252
+
253
+ Create/list announcement records for the wa-announcements app. `create` and `list` require `--instance-id` (`scaffold` does not).
254
+
255
+ ```bash
256
+ # Create from a JSON data file
257
+ p announcements create --instance-id 01... --from-file ./announcement.json --api-key myapikey
258
+
259
+ # Create from convenience flags only
260
+ p announcements create --instance-id 01... --title "New feature" --body "<p>Check it out</p>" --api-key myapikey
261
+
262
+ # List announcements
263
+ p announcements list --instance-id 01... --api-key myapikey
264
+
265
+ # Generate a fully populated starter data.json
266
+ p announcements scaffold --out ./announcement.json --api-key myapikey
267
+ ```
268
+
269
+ `create` requires either `--from-file` or both `--title` and `--body`. `--title`/`--body`/`--priority` override matching keys from `--from-file` when both are given. Run `p announcements create --help` for the full `data` field catalog (media, links, scheduling, target groups, popup overrides) — note `start_at`/`expires_at` are naive local dates (`YYYY-MM-DD[THH:mm]`), not ISO/UTC.
270
+
271
+ `scaffold` returns the starter JSON in the envelope `data`; pass `--out <path>` to write it to a file instead.
272
+
273
+ ## To run the `org` command:
274
+
275
+ Read the organization the API key belongs to.
276
+
277
+ ```bash
278
+ p org get --api-key myapikey
279
+ ```
280
+
281
+ ## To run the `analytics` command:
282
+
283
+ Read rollup analytics totals for an organization or instance. Requires an org-admin API key — a non-admin key returns an auth error.
284
+
285
+ ```bash
286
+ # Organization-wide rollups (org-admin key required)
287
+ p analytics org --api-key myapikey
288
+
289
+ # Instance rollups (instance-admin or org-admin key required)
290
+ p analytics instance --instance-id 01... --api-key myapikey
291
+ ```
package/dist/index.js CHANGED
@@ -1,10 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from 'commander';
3
2
  import { readFileSync } from 'fs';
4
- import { fileURLToPath } from 'url';
5
3
  import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { Command } from 'commander';
6
6
  import { createPublishAppCommand } from './src/commands/publishApp.js';
7
7
  import { createDevSyncCommand } from './src/commands/devSync.js';
8
+ import { createFilesCommand } from './src/commands/files.js';
9
+ import { createFoldersCommand } from './src/commands/folders.js';
10
+ import { createSearchCommand } from './src/commands/search.js';
11
+ import { createInstancesCommand } from './src/commands/instances.js';
12
+ import { createCanvasesCommand } from './src/commands/canvases.js';
13
+ import { createSharedLinksCommand } from './src/commands/sharedLinks.js';
14
+ import { createTagsCommand } from './src/commands/tags.js';
15
+ import { createOrgCommand } from './src/commands/org.js';
16
+ import { createAnalyticsCommand } from './src/commands/analytics.js';
17
+ import { createAnnouncementsCommand } from './src/commands/announcements.js';
18
+ import { createDsrLandingCommand } from './src/commands/dsrLanding.js';
19
+ import { createEmailTemplateCommand } from './src/commands/emailTemplate.js';
8
20
  const __filename = fileURLToPath(import.meta.url);
9
21
  const __dirname = dirname(__filename);
10
22
  const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
@@ -17,4 +29,16 @@ program
17
29
  .option('--quiet', 'Suppress non-essential (progress) output');
18
30
  program.addCommand(createPublishAppCommand());
19
31
  program.addCommand(createDevSyncCommand());
32
+ program.addCommand(createFilesCommand());
33
+ program.addCommand(createFoldersCommand());
34
+ program.addCommand(createSearchCommand());
35
+ program.addCommand(createInstancesCommand());
36
+ program.addCommand(createCanvasesCommand());
37
+ program.addCommand(createSharedLinksCommand());
38
+ program.addCommand(createTagsCommand());
39
+ program.addCommand(createAnnouncementsCommand());
40
+ program.addCommand(createDsrLandingCommand());
41
+ program.addCommand(createEmailTemplateCommand());
42
+ program.addCommand(createOrgCommand());
43
+ program.addCommand(createAnalyticsCommand());
20
44
  program.parseAsync(process.argv);
@@ -0,0 +1,12 @@
1
+ import { Command } from 'commander';
2
+ import type { AxiosInstance } from 'axios';
3
+ import { type CommonOpts } from '../core/http.js';
4
+ import { type Envelope } from '../core/envelope.js';
5
+ export interface AnalyticsDeps {
6
+ client?: AxiosInstance;
7
+ }
8
+ export declare function runAnalyticsOrg(opts: CommonOpts, deps?: AnalyticsDeps): Promise<Envelope<unknown>>;
9
+ export declare function runAnalyticsInstance(opts: CommonOpts & {
10
+ instanceId?: string;
11
+ }, deps?: AnalyticsDeps): Promise<Envelope<unknown>>;
12
+ export declare function createAnalyticsCommand(): Command;
@@ -0,0 +1,45 @@
1
+ import { Command } from 'commander';
2
+ import { clientFor, toCliError } from '../core/http.js';
3
+ import { ok, fail } from '../core/envelope.js';
4
+ import { createCliError } from '../core/errors.js';
5
+ import { emitEnvelope } from '../core/output.js';
6
+ import { withAuthFlags } from './withAuth.js';
7
+ export async function runAnalyticsOrg(opts, deps = {}) {
8
+ try {
9
+ const client = clientFor(opts, deps);
10
+ const res = await client.get('/api/v1/organization/analytics/');
11
+ return ok(res.data);
12
+ }
13
+ catch (e) {
14
+ return fail(toCliError(e));
15
+ }
16
+ }
17
+ export async function runAnalyticsInstance(opts, deps = {}) {
18
+ try {
19
+ if (!opts.instanceId)
20
+ throw createCliError('VALIDATION', '--instance-id is required for analytics instance');
21
+ const client = clientFor(opts, deps);
22
+ const res = await client.get(`/api/v1/instances/${encodeURIComponent(opts.instanceId)}/analytics/`);
23
+ return ok(res.data);
24
+ }
25
+ catch (e) {
26
+ return fail(toCliError(e));
27
+ }
28
+ }
29
+ export function createAnalyticsCommand() {
30
+ const cmd = new Command('analytics').description('read rollup analytics totals (requires an org-admin API key)');
31
+ withAuthFlags(cmd.command('org'))
32
+ .description('organization rollups (org-admin key required)')
33
+ .action(async (options, command) => {
34
+ const globals = command.optsWithGlobals();
35
+ emitEnvelope(await runAnalyticsOrg({ ...options, quiet: globals.quiet }), globals);
36
+ });
37
+ withAuthFlags(cmd.command('instance'))
38
+ .requiredOption('--instance-id <id>', 'Instance ID')
39
+ .description('instance rollups (instance-admin or org-admin key required)')
40
+ .action(async (options, command) => {
41
+ const globals = command.optsWithGlobals();
42
+ emitEnvelope(await runAnalyticsInstance({ ...options, quiet: globals.quiet }), globals);
43
+ });
44
+ return cmd;
45
+ }
@@ -0,0 +1,23 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ import { Command } from 'commander';
3
+ import { type Envelope } from '../core/envelope.js';
4
+ import { type CommonOpts } from '../core/http.js';
5
+ export interface AnnouncementsDeps {
6
+ client?: AxiosInstance;
7
+ }
8
+ export declare function runAnnouncementsCreate(opts: CommonOpts & {
9
+ instanceId?: string;
10
+ fromFile?: string;
11
+ title?: string;
12
+ body?: string;
13
+ priority?: string;
14
+ }, deps?: AnnouncementsDeps): Promise<Envelope<unknown>>;
15
+ export declare function runAnnouncementsList(opts: CommonOpts & {
16
+ instanceId?: string;
17
+ limit?: number;
18
+ offset?: number;
19
+ }, deps?: AnnouncementsDeps): Promise<Envelope<unknown>>;
20
+ export declare function runAnnouncementsScaffold(opts: CommonOpts & {
21
+ out?: string;
22
+ }, _deps?: AnnouncementsDeps): Promise<Envelope<unknown>>;
23
+ export declare function createAnnouncementsCommand(): Command;
@@ -0,0 +1,131 @@
1
+ import { Command } from 'commander';
2
+ import { ok, fail } from '../core/envelope.js';
3
+ import { createCliError } from '../core/errors.js';
4
+ import { clientFor, toCliError } from '../core/http.js';
5
+ import { emitEnvelope } from '../core/output.js';
6
+ import { readJsonFile } from '../core/readInput.js';
7
+ import { listLimitOffsetParams } from '../core/pagination.js';
8
+ import { assertIntFlags, isPlainObject } from '../schema/content.js';
9
+ import { ANNOUNCEMENT_SCAFFOLD_JSON, emitScaffold } from './scaffolds.js';
10
+ import { withAuthFlags } from './withAuth.js';
11
+ import { withInstanceFlag } from './withInstance.js';
12
+ const APPSDB_ENDPOINT = '/core/api/protected/appsdb';
13
+ const ANNOUNCEMENT_TYPE = 'wa-announcement';
14
+ const FIELD_HELP = `
15
+ data fields (wa-announcements app; all optional except title/body):
16
+ title string, required announcement headline
17
+ body string (HTML), required announcement body
18
+ media_type 'none' | 'image' | 'video' media attachment kind
19
+ media_url string URL of the attached media
20
+ link_type 'url' | 'file' call-to-action link kind
21
+ link_url string target URL when link_type='url'
22
+ link_label string call-to-action button text
23
+ link_file_id string target file id when link_type='file'
24
+ link_target_name string display name for the linked target
25
+ priority 'normal' | 'important' visual emphasis
26
+ author_name string displayed author
27
+ published_at string display-only publish label
28
+ start_at string when the announcement becomes visible
29
+ expires_at string when the announcement stops being visible
30
+ target_groups string[] content-distribution group scoping
31
+ display_mode ('inline' | 'popup')[] where the announcement renders
32
+ popup_title string | null popup-only title override
33
+ popup_body string | null popup-only body override
34
+ popup_link_label string | null popup-only link label override
35
+
36
+ Naive local dates: start_at/expires_at are 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:mm'
37
+ compared as LOCAL wall-clock time by the app, not ISO/UTC. '' means "none".
38
+ Do not convert these to ISO/UTC — that would shift the displayed time.
39
+
40
+ Run "p announcements scaffold" for a fully populated example.`;
41
+ export async function runAnnouncementsCreate(opts, deps = {}) {
42
+ try {
43
+ if (!opts.instanceId)
44
+ throw createCliError('VALIDATION', '--instance-id is required for announcements create');
45
+ if (!opts.fromFile && !(opts.title && opts.body)) {
46
+ throw createCliError('VALIDATION', 'announcements create requires either --from-file or both --title and --body');
47
+ }
48
+ let data = {};
49
+ if (opts.fromFile) {
50
+ // readJsonFile throws VALIDATION on a missing/unreadable/malformed file, before any request is made.
51
+ const fromFileData = readJsonFile(opts.fromFile);
52
+ if (!isPlainObject(fromFileData)) {
53
+ throw createCliError('VALIDATION', '--from-file must contain a JSON object (not an array or primitive)');
54
+ }
55
+ data = { ...fromFileData };
56
+ }
57
+ // Convenience flags override matching keys from --from-file when set.
58
+ if (opts.title !== undefined)
59
+ data.title = opts.title;
60
+ if (opts.body !== undefined)
61
+ data.body = opts.body;
62
+ if (opts.priority !== undefined)
63
+ data.priority = opts.priority;
64
+ // Re-check after the merge: a --from-file missing (or mistyping) title/body
65
+ // — e.g. `{}`, `{ "body": "x" }`, or `{ "title": 123 }` — must not slip
66
+ // through just because --from-file was present. Require non-empty strings,
67
+ // not just truthy values. No request may be sent when this fails.
68
+ const isNonEmptyString = (v) => typeof v === 'string' && v.trim().length > 0;
69
+ if (!isNonEmptyString(data.title) || !isNonEmptyString(data.body)) {
70
+ throw createCliError('VALIDATION', 'announcement requires non-empty string title and body (via --from-file or --title/--body)');
71
+ }
72
+ const client = clientFor(opts, deps);
73
+ // Deliberately omit `user_id`/`no_user_id`: omitting user_id yields the
74
+ // desired instance-shared row (see design §4.3); there is no --owned flag.
75
+ const res = await client.post(APPSDB_ENDPOINT, { type: ANNOUNCEMENT_TYPE, instance_id: opts.instanceId, data }, { params: { instance_id: opts.instanceId }, headers: { 'x-instance-id': opts.instanceId } });
76
+ return ok(res.data);
77
+ }
78
+ catch (e) {
79
+ return fail(toCliError(e));
80
+ }
81
+ }
82
+ export async function runAnnouncementsList(opts, deps = {}) {
83
+ try {
84
+ if (!opts.instanceId)
85
+ throw createCliError('VALIDATION', '--instance-id is required for announcements list');
86
+ assertIntFlags(opts);
87
+ const client = clientFor(opts, deps);
88
+ const params = {
89
+ type: ANNOUNCEMENT_TYPE,
90
+ instance_id: opts.instanceId,
91
+ ...listLimitOffsetParams(opts),
92
+ };
93
+ const res = await client.get(APPSDB_ENDPOINT, { params, headers: { 'x-instance-id': opts.instanceId } });
94
+ return ok(res.data);
95
+ }
96
+ catch (e) {
97
+ return fail(toCliError(e));
98
+ }
99
+ }
100
+ export async function runAnnouncementsScaffold(opts, _deps = {}) {
101
+ return emitScaffold(ANNOUNCEMENT_SCAFFOLD_JSON, opts.out);
102
+ }
103
+ export function createAnnouncementsCommand() {
104
+ const cmd = new Command('announcements').description('create/list announcement records for the wa-announcements app');
105
+ withInstanceFlag(withAuthFlags(cmd.command('create')))
106
+ .option('--from-file <path>', 'Path to a JSON file with the announcement data object')
107
+ .option('--title <text>', 'Announcement title (overrides a matching --from-file key)')
108
+ .option('--body <html>', 'Announcement body HTML (overrides a matching --from-file key)')
109
+ .option('--priority <priority>', "Priority, e.g. 'normal' or 'important' (overrides a matching --from-file key)")
110
+ .addHelpText('after', FIELD_HELP)
111
+ .action(async (options, command) => {
112
+ const globals = command.optsWithGlobals();
113
+ emitEnvelope(await runAnnouncementsCreate({ ...options, quiet: globals.quiet }), globals);
114
+ });
115
+ withInstanceFlag(withAuthFlags(cmd.command('list')))
116
+ .option('--limit <n>', 'Limit', (v) => parseInt(v, 10))
117
+ .option('--offset <n>', 'Offset', (v) => parseInt(v, 10))
118
+ .action(async (options, command) => {
119
+ const globals = command.optsWithGlobals();
120
+ emitEnvelope(await runAnnouncementsList({ ...options, quiet: globals.quiet }), globals);
121
+ });
122
+ withAuthFlags(cmd.command('scaffold'))
123
+ .description('generate a fully populated starter announcement data.json')
124
+ .option('--out <path>', 'Write the scaffold JSON to a file instead of returning it inline')
125
+ .addHelpText('after', FIELD_HELP)
126
+ .action(async (options, command) => {
127
+ const globals = command.optsWithGlobals();
128
+ emitEnvelope(await runAnnouncementsScaffold({ ...options, quiet: globals.quiet }), globals);
129
+ });
130
+ return cmd;
131
+ }
@@ -0,0 +1,25 @@
1
+ import { Command } from 'commander';
2
+ import type { AxiosInstance } from 'axios';
3
+ import { type CommonOpts } from '../core/http.js';
4
+ import { type Envelope } from '../core/envelope.js';
5
+ export interface CanvasesDeps {
6
+ client?: AxiosInstance;
7
+ }
8
+ export declare function runCanvasesList(opts: CommonOpts & {
9
+ instanceId?: string;
10
+ isTemplate?: boolean;
11
+ isSection?: boolean;
12
+ isBlock?: boolean;
13
+ tags?: string;
14
+ idIn?: string;
15
+ accessType?: string;
16
+ page?: number;
17
+ pageSize?: number;
18
+ }, deps?: CanvasesDeps): Promise<Envelope<{
19
+ results: unknown[];
20
+ pagination: Record<string, unknown>;
21
+ }>>;
22
+ export declare function runCanvasesGet(opts: CommonOpts & {
23
+ id?: string;
24
+ }, deps?: CanvasesDeps): Promise<Envelope<unknown>>;
25
+ export declare function createCanvasesCommand(): Command;
@@ -0,0 +1,73 @@
1
+ import { Command } from 'commander';
2
+ import { clientFor, toCliError } from '../core/http.js';
3
+ import { ok, fail } from '../core/envelope.js';
4
+ import { createCliError } from '../core/errors.js';
5
+ import { emitEnvelope } from '../core/output.js';
6
+ import { listPageParams, listPaginationEcho } from '../core/pagination.js';
7
+ import { validateAccessType, assertIntFlags } from '../schema/content.js';
8
+ import { withAuthFlags } from './withAuth.js';
9
+ export async function runCanvasesList(opts, deps = {}) {
10
+ try {
11
+ if (!opts.instanceId)
12
+ throw createCliError('VALIDATION', '--instance-id is required for canvases list');
13
+ assertIntFlags(opts);
14
+ const client = clientFor(opts, deps);
15
+ const params = { instance_id: opts.instanceId, ...listPageParams(opts) };
16
+ // Send each type flag ONLY when set. The server FilterSet does
17
+ // setdefault("is_block", False), so blocks are hidden unless --is-block.
18
+ if (opts.isTemplate)
19
+ params.is_template = true;
20
+ if (opts.isSection)
21
+ params.is_section = true;
22
+ if (opts.isBlock)
23
+ params.is_block = true;
24
+ if (opts.tags)
25
+ params.tags__in = opts.tags;
26
+ if (opts.idIn)
27
+ params.id__in = opts.idIn;
28
+ const accessType = validateAccessType(opts.accessType);
29
+ if (accessType)
30
+ params.access_type = accessType;
31
+ const res = await client.get('/api/v1/canvases/', { params });
32
+ return ok({ results: res.data.results, pagination: listPaginationEcho(opts, res.data) });
33
+ }
34
+ catch (e) {
35
+ return fail(toCliError(e));
36
+ }
37
+ }
38
+ export async function runCanvasesGet(opts, deps = {}) {
39
+ try {
40
+ if (!opts.id)
41
+ throw createCliError('VALIDATION', 'canvas id is required');
42
+ const client = clientFor(opts, deps);
43
+ const res = await client.get(`/api/v1/canvases/${encodeURIComponent(opts.id)}`);
44
+ return ok(res.data);
45
+ }
46
+ catch (e) {
47
+ return fail(toCliError(e));
48
+ }
49
+ }
50
+ export function createCanvasesCommand() {
51
+ const cmd = new Command('canvases').description('read Pitcher canvases (presentations, sections, blocks)');
52
+ withAuthFlags(cmd.command('list'))
53
+ .requiredOption('--instance-id <id>', 'Instance ID')
54
+ .option('--is-template', 'Only templates')
55
+ .option('--is-section', 'Only sections')
56
+ .option('--is-block', 'Only blocks (blocks are hidden unless this flag is set)')
57
+ .option('--tags <tags>', 'Comma-separated tags (filters via tags__in)')
58
+ .option('--id-in <ids>', 'Comma-separated canvas IDs')
59
+ .option('--access-type <type>', 'public | restricted | personal')
60
+ .option('--page <n>', 'Page number', (v) => parseInt(v, 10))
61
+ .option('--page-size <n>', 'Page size (max 500)', (v) => parseInt(v, 10))
62
+ .action(async (options, command) => {
63
+ const globals = command.optsWithGlobals();
64
+ emitEnvelope(await runCanvasesList({ ...options, quiet: globals.quiet }), globals);
65
+ });
66
+ withAuthFlags(cmd.command('get'))
67
+ .argument('<id>', 'Canvas ID')
68
+ .action(async (id, options, command) => {
69
+ const globals = command.optsWithGlobals();
70
+ emitEnvelope(await runCanvasesGet({ ...options, id, quiet: globals.quiet }), globals);
71
+ });
72
+ return cmd;
73
+ }
@@ -0,0 +1,28 @@
1
+ import { Command } from 'commander';
2
+ import type { AxiosInstance } from 'axios';
3
+ import { type CommonOpts } from '../core/http.js';
4
+ import { type Envelope } from '../core/envelope.js';
5
+ export interface DsrLandingDeps {
6
+ client?: AxiosInstance;
7
+ }
8
+ export declare function runDsrLandingCreate(opts: CommonOpts & {
9
+ instanceId?: string;
10
+ name?: string;
11
+ htmlFile?: string;
12
+ cssFile?: string;
13
+ mergeSchema?: string;
14
+ thumbnailUrl?: string;
15
+ language?: string;
16
+ default?: boolean;
17
+ }, deps?: DsrLandingDeps): Promise<Envelope<unknown>>;
18
+ export declare function runDsrLandingSetDefault(opts: CommonOpts & {
19
+ instanceId?: string;
20
+ id?: string;
21
+ }, deps?: DsrLandingDeps): Promise<Envelope<unknown>>;
22
+ export declare function runDsrLandingList(opts: CommonOpts & {
23
+ instanceId?: string;
24
+ }, deps?: DsrLandingDeps): Promise<Envelope<unknown>>;
25
+ export declare function runDsrLandingScaffold(opts: CommonOpts & {
26
+ out?: string;
27
+ }, _deps?: DsrLandingDeps): Promise<Envelope<unknown>>;
28
+ export declare function createDsrLandingCommand(): Command;