@neta-art/cohub-cli 5.0.0 → 6.0.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.
@@ -2,11 +2,11 @@ import { HttpError } from "@neta-art/cohub";
2
2
  import { createClient } from "../client.js";
3
3
  import { error, handleHttp, json as outJson, jsonRequested, ok, table } from "../output.js";
4
4
  import { resolveSpace } from "../space.js";
5
- import { downloadWork } from "../work-download.js";
6
- import { getWorkByRef, parseWorkRef } from "../work-ref.js";
7
- import { registerWorkCommerce } from "./work-commerce.js";
8
- const WORK_STATUSES = ["published", "disabled"];
9
- const WORK_VISIBILITIES = ["public", "space"];
5
+ import { downloadApp } from "../app-download.js";
6
+ import { getAppByRef, parseAppRef } from "../app-ref.js";
7
+ import { registerAppCommerce } from "./app-commerce.js";
8
+ const APP_STATUSES = ["published", "disabled"];
9
+ const APP_VISIBILITIES = ["public", "space"];
10
10
  const collectOption = (value, previous = []) => [...previous, value];
11
11
  function parseChoice(value, name, choices) {
12
12
  if (choices.includes(value))
@@ -62,13 +62,13 @@ function resolveStatus(opts) {
62
62
  const values = [opts.status, opts.disabled ? "disabled" : undefined].filter(Boolean);
63
63
  if (values.length > 1)
64
64
  return error("Conflicting status", "Use only one of --status or --disabled");
65
- return values[0] ? parseChoice(values[0], "status", WORK_STATUSES) : "published";
65
+ return values[0] ? parseChoice(values[0], "status", APP_STATUSES) : "published";
66
66
  }
67
67
  function resolveVisibility(value) {
68
- return value ? parseChoice(value, "visibility", WORK_VISIBILITIES) : undefined;
68
+ return value ? parseChoice(value, "visibility", APP_VISIBILITIES) : undefined;
69
69
  }
70
- function printWork(work) {
71
- table([work], [
70
+ function printApp(app) {
71
+ table([app], [
72
72
  { key: "id", label: "ID" },
73
73
  { key: "slug", label: "Slug" },
74
74
  { key: "status", label: "Status" },
@@ -79,7 +79,7 @@ function printWork(work) {
79
79
  { key: "publishedAt", label: "Published" },
80
80
  ]);
81
81
  }
82
- function printWorkUrls(result) {
82
+ function printAppUrls(result) {
83
83
  const lines = [
84
84
  result.publicUrl ? `Public URL: ${result.publicUrl}` : null,
85
85
  result.content?.url ? `Content URL: ${result.content.url}` : null,
@@ -96,7 +96,7 @@ function promotionUrl(publicUrl, promotion) {
96
96
  url.searchParams.set(key, value);
97
97
  return url.toString();
98
98
  }
99
- function printWorkStats(stats) {
99
+ function printAppStats(stats) {
100
100
  table([stats.summary], [
101
101
  { key: "totalViews", label: "Total" },
102
102
  { key: "views24h", label: "24h" },
@@ -111,19 +111,19 @@ function printWorkStats(stats) {
111
111
  ]);
112
112
  }
113
113
  }
114
- export async function getWorkStatsByRef(client, work) {
115
- const ref = parseWorkRef(work);
114
+ export async function getAppStatsByRef(client, app) {
115
+ const ref = parseAppRef(app);
116
116
  if ("id" in ref)
117
- return client.works.getStats(ref.id);
118
- const detail = await getWorkByRef(client, work);
119
- return client.works.getStats(detail.work.id);
117
+ return client.apps.getStats(ref.id);
118
+ const detail = await getAppByRef(client, app);
119
+ return client.apps.getStats(detail.app.id);
120
120
  }
121
121
  async function confirmDelete(opts) {
122
122
  if (opts.yes)
123
123
  return;
124
124
  if (!process.stdin.isTTY || !process.stdout.isTTY)
125
- return error("Confirmation required", "Pass --yes to delete the work.");
126
- process.stdout.write("Deleting a work also removes its versions and viewer grants. Continue? [y/N] ");
125
+ return error("Confirmation required", "Pass --yes to delete the app.");
126
+ process.stdout.write("Deleting an app also removes its versions and viewer grants. Continue? [y/N] ");
127
127
  const chunks = [];
128
128
  for await (const chunk of process.stdin) {
129
129
  chunks.push(chunk);
@@ -133,34 +133,43 @@ async function confirmDelete(opts) {
133
133
  if (answer !== "y" && answer !== "yes")
134
134
  return error("Cancelled");
135
135
  }
136
- async function publishWorkVersion(id, opts) {
136
+ async function publishAppVersion(id, opts) {
137
137
  const client = createClient();
138
138
  try {
139
- const result = await client.works.publishVersion(id);
139
+ const result = await client.apps.publishVersion(id);
140
140
  if (jsonRequested(opts))
141
141
  return outJson(result);
142
- ok(`Work version updated: v${result.version.version}`);
143
- printWork(result.work);
142
+ ok(`App version updated: v${result.version.version}`);
143
+ printApp(result.app);
144
144
  }
145
145
  catch (e) {
146
146
  handleHttp(e);
147
147
  }
148
148
  }
149
- export function registerWorks(program) {
150
- const worksCmd = program.command("works").description("Work management");
151
- worksCmd
149
+ export function registerApps(program) {
150
+ const appsCmd = program
151
+ .command("apps")
152
+ .description("App management")
153
+ // Legacy spelling; hidden from help but still typed by existing scripts.
154
+ .alias("works")
155
+ .hook("preAction", () => {
156
+ if (process.argv.slice(2).includes("works")) {
157
+ ok("Deprecated: `cohub works` is now `cohub apps`.");
158
+ }
159
+ });
160
+ appsCmd
152
161
  .command("ls")
153
162
  .alias("list")
154
- .description("List works in the target space")
163
+ .description("List apps in the target space")
155
164
  .option("--json", "Output as JSON")
156
165
  .action(async (opts) => {
157
- const spaceId = resolveSpace(worksCmd);
166
+ const spaceId = resolveSpace(appsCmd);
158
167
  const client = createClient();
159
168
  try {
160
- const result = await client.works.listBySpace(spaceId);
169
+ const result = await client.apps.listBySpace(spaceId);
161
170
  if (jsonRequested(opts))
162
171
  return outJson(result);
163
- table(result.works, [
172
+ table(result.apps, [
164
173
  { key: "id", label: "ID" },
165
174
  { key: "slug", label: "Slug" },
166
175
  { key: "status", label: "Status" },
@@ -175,49 +184,49 @@ export function registerWorks(program) {
175
184
  handleHttp(e);
176
185
  }
177
186
  });
178
- worksCmd
179
- .command("get <work>")
180
- .description("Show work details by id, URL, mention URI, or username/space/work")
187
+ appsCmd
188
+ .command("get <app>")
189
+ .description("Show app details by id, URL, mention URI, or username/space/app")
181
190
  .option("--json", "Output as JSON")
182
- .action(async (work, opts) => {
191
+ .action(async (app, opts) => {
183
192
  const client = createClient();
184
193
  try {
185
- const result = await getWorkByRef(client, work);
194
+ const result = await getAppByRef(client, app);
186
195
  if (jsonRequested(opts))
187
196
  return outJson(result);
188
- printWork(result.work);
189
- printWorkUrls(result);
197
+ printApp(result.app);
198
+ printAppUrls(result);
190
199
  }
191
200
  catch (e) {
192
201
  handleHttp(e);
193
202
  }
194
203
  });
195
- worksCmd
196
- .command("stats <work>")
197
- .description("Show view statistics by id, URL, mention URI, or username/space/work")
204
+ appsCmd
205
+ .command("stats <app>")
206
+ .description("Show view statistics by id, URL, mention URI, or username/space/app")
198
207
  .option("--json", "Output as JSON")
199
- .action(async (work, opts) => {
208
+ .action(async (app, opts) => {
200
209
  const client = createClient();
201
210
  try {
202
- const result = await getWorkStatsByRef(client, work);
211
+ const result = await getAppStatsByRef(client, app);
203
212
  if (jsonRequested(opts))
204
213
  return outJson(result);
205
- printWorkStats(result);
214
+ printAppStats(result);
206
215
  }
207
216
  catch (e) {
208
217
  handleHttp(e);
209
218
  }
210
219
  });
211
- worksCmd
212
- .command("download <work>")
213
- .description("Download a published file or directory Work")
220
+ appsCmd
221
+ .command("download <app>")
222
+ .description("Download a published file or directory app")
214
223
  .option("-o, --output <path>", "Output file or directory")
215
224
  .option("--json", "Output as JSON")
216
- .action(async (work, opts) => {
225
+ .action(async (app, opts) => {
217
226
  const client = createClient();
218
227
  try {
219
- const detail = await getWorkByRef(client, work);
220
- const result = await downloadWork(detail, opts.output);
228
+ const detail = await getAppByRef(client, app);
229
+ const result = await downloadApp(detail, opts.output);
221
230
  if (jsonRequested(opts))
222
231
  return outJson(result);
223
232
  ok(`Downloaded ${result.files} file${result.files === 1 ? "" : "s"} to ${result.output}`);
@@ -226,43 +235,43 @@ export function registerWorks(program) {
226
235
  handleHttp(e);
227
236
  }
228
237
  });
229
- worksCmd
230
- .command("resolve <workSlug>")
231
- .description("Resolve a published work by owner and space slug")
238
+ appsCmd
239
+ .command("resolve <appSlug>")
240
+ .description("Resolve a published app by owner and space slug")
232
241
  .option("--owner <username>", "Owner username")
233
242
  .option("--space-slug <slug>", "Space slug")
234
243
  .option("--json", "Output as JSON")
235
- .action(async (workSlug, opts) => {
244
+ .action(async (appSlug, opts) => {
236
245
  if (!opts.owner?.trim())
237
246
  return error("Missing owner username", "Pass --owner <username>.");
238
247
  if (!opts.spaceSlug?.trim())
239
248
  return error("Missing space slug", "Pass --space-slug <slug>.");
240
249
  const client = createClient();
241
250
  try {
242
- const result = await client.works.getBySlug(opts.owner.trim(), opts.spaceSlug.trim(), workSlug);
251
+ const result = await client.apps.getBySlug(opts.owner.trim(), opts.spaceSlug.trim(), appSlug);
243
252
  if (jsonRequested(opts))
244
253
  return outJson(result);
245
- printWork(result.work);
246
- printWorkUrls(result);
254
+ printApp(result.app);
255
+ printAppUrls(result);
247
256
  }
248
257
  catch (e) {
249
258
  handleHttp(e);
250
259
  }
251
260
  });
252
- worksCmd
261
+ appsCmd
253
262
  .command("publish <slug>")
254
- .description("Create or publish a work in the target space")
263
+ .description("Create or publish an app in the target space")
255
264
  .option("--file <path>", "Publish a file (HTML page, board, or any other file)")
256
265
  .option("--dir <path>", "Publish a directory site")
257
266
  .option("--port <port>", "Publish a public sandbox port")
258
267
  .option("--disabled", "Create as disabled")
259
- .option("--status <status>", "Work status: published, disabled")
260
- .option("--visibility <visibility>", "Work visibility: public, space")
261
- .option("--work-scope <scope>", "Scope granted to the work runtime (space.view, session.view, file.view, taskrun.view)", collectOption, [])
268
+ .option("--status <status>", "App status: published, disabled")
269
+ .option("--visibility <visibility>", "App visibility: public, space")
270
+ .option("--app-scope <scope>", "Scope granted to the app runtime (space.view, session.view, file.view, taskrun.view)", collectOption, [])
262
271
  .option("--viewer-scope <scope>", "Scope viewers may request (taskrun.view, session.prompt.readonly, session.prompt.fullaccess, generation.create, user.space.list, user.session.list, user.usage.read)", collectOption, [])
263
- .option("--meta <json>", "Work metadata as a JSON object")
264
- .option("--hide-cohub-bar", "Hide the Cohub footer bar on the public work page")
265
- .option("--show-cohub-bar", "Show the Cohub footer bar on the public work page")
272
+ .option("--meta <json>", "App metadata as a JSON object")
273
+ .option("--hide-cohub-bar", "Hide the Cohub footer bar on the public app page")
274
+ .option("--show-cohub-bar", "Show the Cohub footer bar on the public app page")
266
275
  .option("--json", "Output as JSON")
267
276
  .action(async (slug, opts) => {
268
277
  if (opts.hideCohubBar && opts.showCohubBar)
@@ -270,7 +279,7 @@ export function registerWorks(program) {
270
279
  const target = resolveTarget(opts);
271
280
  if (!target)
272
281
  return error("Missing target", "Use one of --file, --dir, or --port.");
273
- const spaceId = resolveSpace(worksCmd);
282
+ const spaceId = resolveSpace(appsCmd);
274
283
  const client = createClient();
275
284
  const status = resolveStatus(opts);
276
285
  const meta = withCohubBarMeta({
@@ -285,72 +294,72 @@ export function registerWorks(program) {
285
294
  visibility: resolveVisibility(opts.visibility),
286
295
  targetType: target.targetType,
287
296
  targetRef: target.targetRef,
288
- workScopes: opts.workScope,
297
+ appScopes: opts.appScope,
289
298
  allowedViewerScopes: opts.viewerScope,
290
299
  meta,
291
300
  };
292
301
  try {
293
- const result = await client.works.create(input);
302
+ const result = await client.apps.create(input);
294
303
  if (jsonRequested(opts))
295
304
  return outJson(result);
296
- ok(`Work published: ${result.work.id}`);
297
- printWork(result.work);
305
+ ok(`App published: ${result.app.id}`);
306
+ printApp(result.app);
298
307
  }
299
308
  catch (e) {
300
309
  if (!(e instanceof HttpError) || e.status !== 409)
301
310
  handleHttp(e);
302
311
  try {
303
- const { works } = await client.works.listBySpace(spaceId);
304
- const existingWork = works.find((work) => work.slug === slug);
305
- if (!existingWork)
312
+ const { apps } = await client.apps.listBySpace(spaceId);
313
+ const existingApp = apps.find((app) => app.slug === slug);
314
+ if (!existingApp)
306
315
  return handleHttp(e);
307
- const { work } = await client.works.update(existingWork.id, {
308
- status: status === "published" && existingWork.status !== "published" ? existingWork.status : status,
316
+ const { app } = await client.apps.update(existingApp.id, {
317
+ status: status === "published" && existingApp.status !== "published" ? existingApp.status : status,
309
318
  visibility: resolveVisibility(opts.visibility),
310
319
  targetType: target.targetType,
311
320
  targetRef: target.targetRef,
312
- workScopes: opts.workScope,
321
+ appScopes: opts.appScope,
313
322
  allowedViewerScopes: opts.viewerScope,
314
323
  meta,
315
324
  });
316
325
  const publishedVersion = status === "published"
317
- ? await client.works.publishVersion(work.id)
326
+ ? await client.apps.publishVersion(app.id)
318
327
  : null;
319
- const result = publishedVersion ?? { work };
328
+ const result = publishedVersion ?? { app };
320
329
  if (jsonRequested(opts))
321
330
  return outJson(result);
322
- ok(status === "published" && publishedVersion ? `Work version updated: v${publishedVersion.version.version}` : `Work updated: ${work.id}`);
323
- printWork(result.work);
331
+ ok(status === "published" && publishedVersion ? `App version updated: v${publishedVersion.version.version}` : `App updated: ${app.id}`);
332
+ printApp(result.app);
324
333
  }
325
334
  catch (fallbackError) {
326
335
  handleHttp(fallbackError);
327
336
  }
328
337
  }
329
338
  });
330
- worksCmd
339
+ appsCmd
331
340
  .command("update <id>")
332
- .description("Update work settings")
333
- .option("--slug <slug>", "New work slug")
341
+ .description("Update app settings")
342
+ .option("--slug <slug>", "New app slug")
334
343
  .option("--file <path>", "Use a file target (HTML page, board, or any other file)")
335
344
  .option("--dir <path>", "Use a directory site target")
336
345
  .option("--port <port>", "Use a public sandbox port target")
337
346
  .option("--disabled", "Set status to disabled")
338
- .option("--status <status>", "Work status: published, disabled")
339
- .option("--visibility <visibility>", "Work visibility: public, space")
340
- .option("--work-scope <scope>", "Scope granted to the work runtime (space.view, session.view, file.view, taskrun.view)", collectOption, [])
347
+ .option("--status <status>", "App status: published, disabled")
348
+ .option("--visibility <visibility>", "App visibility: public, space")
349
+ .option("--app-scope <scope>", "Scope granted to the app runtime (space.view, session.view, file.view, taskrun.view)", collectOption, [])
341
350
  .option("--viewer-scope <scope>", "Scope viewers may request (taskrun.view, session.prompt.readonly, session.prompt.fullaccess, generation.create, user.space.list, user.session.list, user.usage.read)", collectOption, [])
342
- .option("--clear-work-scopes", "Clear work runtime scopes")
351
+ .option("--clear-app-scopes", "Clear app runtime scopes")
343
352
  .option("--clear-viewer-scopes", "Clear viewer-requestable scopes")
344
- .option("--meta <json>", "Work metadata as a JSON object")
345
- .option("--hide-cohub-bar", "Hide the Cohub footer bar on the public work page")
346
- .option("--show-cohub-bar", "Show the Cohub footer bar on the public work page")
353
+ .option("--meta <json>", "App metadata as a JSON object")
354
+ .option("--hide-cohub-bar", "Hide the Cohub footer bar on the public app page")
355
+ .option("--show-cohub-bar", "Show the Cohub footer bar on the public app page")
347
356
  .option("--json", "Output as JSON")
348
357
  .action(async (id, opts) => {
349
358
  if (opts.hideCohubBar && opts.showCohubBar)
350
359
  return error("Conflicting Cohub bar options", "Use either --hide-cohub-bar or --show-cohub-bar.");
351
360
  const target = resolveTarget(opts);
352
- if (opts.clearWorkScopes && opts.workScope?.length)
353
- return error("Conflicting work scopes", "Use either --work-scope or --clear-work-scopes.");
361
+ if (opts.clearAppScopes && opts.appScope?.length)
362
+ return error("Conflicting app scopes", "Use either --app-scope or --clear-app-scopes.");
354
363
  if (opts.clearViewerScopes && opts.viewerScope?.length)
355
364
  return error("Conflicting viewer scopes", "Use either --viewer-scope or --clear-viewer-scopes.");
356
365
  const hasMetaUpdate = opts.meta !== undefined || opts.hideCohubBar || opts.showCohubBar;
@@ -360,7 +369,7 @@ export function registerWorks(program) {
360
369
  let baseMeta = opts.meta !== undefined ? parseJsonObject(opts.meta, "meta") ?? null : undefined;
361
370
  if (baseMeta === undefined && (opts.hideCohubBar || opts.showCohubBar)) {
362
371
  try {
363
- baseMeta = (await client.works.get(id)).work.meta;
372
+ baseMeta = (await client.apps.get(id)).app.meta;
364
373
  }
365
374
  catch (e) {
366
375
  handleHttp(e);
@@ -373,52 +382,52 @@ export function registerWorks(program) {
373
382
  });
374
383
  }
375
384
  const nextStatus = opts.status || opts.disabled ? resolveStatus(opts) : undefined;
376
- const currentWork = nextStatus === "published" ? (await client.works.get(id)).work : null;
385
+ const currentApp = nextStatus === "published" ? (await client.apps.get(id)).app : null;
377
386
  const input = compactObject({
378
387
  slug: opts.slug,
379
- status: nextStatus === "published" && currentWork?.status !== "published" ? currentWork?.status : nextStatus,
388
+ status: nextStatus === "published" && currentApp?.status !== "published" ? currentApp?.status : nextStatus,
380
389
  visibility: resolveVisibility(opts.visibility),
381
390
  targetType: target?.targetType,
382
391
  targetRef: target?.targetRef,
383
- workScopes: opts.clearWorkScopes ? [] : opts.workScope?.length ? opts.workScope : undefined,
392
+ appScopes: opts.clearAppScopes ? [] : opts.appScope?.length ? opts.appScope : undefined,
384
393
  allowedViewerScopes: opts.clearViewerScopes ? [] : opts.viewerScope?.length ? opts.viewerScope : undefined,
385
394
  meta,
386
395
  });
387
396
  if (Object.keys(input).length === 0)
388
- return error("Nothing to update", "Pass --slug, --file, --dir, --port, --status, --visibility, --work-scope, --viewer-scope, --clear-work-scopes, --clear-viewer-scopes, --meta, --hide-cohub-bar, or --show-cohub-bar.");
397
+ return error("Nothing to update", "Pass --slug, --file, --dir, --port, --status, --visibility, --app-scope, --viewer-scope, --clear-app-scopes, --clear-viewer-scopes, --meta, --hide-cohub-bar, or --show-cohub-bar.");
389
398
  try {
390
- const updated = await client.works.update(id, input);
391
- const publishedVersion = nextStatus === "published" && currentWork?.status !== "published"
392
- ? await client.works.publishVersion(updated.work.id)
399
+ const updated = await client.apps.update(id, input);
400
+ const publishedVersion = nextStatus === "published" && currentApp?.status !== "published"
401
+ ? await client.apps.publishVersion(updated.app.id)
393
402
  : null;
394
- const result = publishedVersion ?? updated;
403
+ const result = publishedVersion ?? { app: updated.app };
395
404
  if (jsonRequested(opts))
396
405
  return outJson(result);
397
- ok(publishedVersion ? `Work published: v${publishedVersion.version.version}` : "Work updated");
398
- printWork(result.work);
406
+ ok(publishedVersion ? `App published: v${publishedVersion.version.version}` : "App updated");
407
+ printApp(result.app);
399
408
  }
400
409
  catch (e) {
401
410
  handleHttp(e);
402
411
  }
403
412
  });
404
- worksCmd
413
+ appsCmd
405
414
  .command("publish-version <id>")
406
- .description("Publish or update the current work version")
415
+ .description("Publish or update the current app version")
407
416
  .option("--json", "Output as JSON")
408
- .action(publishWorkVersion);
409
- worksCmd
417
+ .action(publishAppVersion);
418
+ appsCmd
410
419
  .command("release <id>", { hidden: true })
411
420
  .description("Deprecated alias for publish-version")
412
421
  .option("--json", "Output as JSON")
413
- .action(publishWorkVersion);
414
- worksCmd
422
+ .action(publishAppVersion);
423
+ appsCmd
415
424
  .command("versions <id>")
416
- .description("List work versions")
425
+ .description("List app versions")
417
426
  .option("--json", "Output as JSON")
418
427
  .action(async (id, opts) => {
419
428
  const client = createClient();
420
429
  try {
421
- const result = await client.works.listVersions(id);
430
+ const result = await client.apps.listVersions(id);
422
431
  if (jsonRequested(opts))
423
432
  return outJson(result);
424
433
  table(result.versions, [
@@ -433,17 +442,17 @@ export function registerWorks(program) {
433
442
  handleHttp(e);
434
443
  }
435
444
  });
436
- const promotionsCmd = worksCmd.command("promotions").description("Work promotion links and analytics");
445
+ const promotionsCmd = appsCmd.command("promotions").description("App promotion links and analytics");
437
446
  promotionsCmd
438
- .command("list <work>")
447
+ .command("list <app>")
439
448
  .alias("ls")
440
449
  .description("List promotion links")
441
450
  .option("--json", "Output as JSON")
442
- .action(async (workRef, opts) => {
451
+ .action(async (appRef, opts) => {
443
452
  const client = createClient();
444
453
  try {
445
- const detail = await getWorkByRef(client, workRef);
446
- const result = await client.works.listPromotions(detail.work.id);
454
+ const detail = await getAppByRef(client, appRef);
455
+ const result = await client.apps.listPromotions(detail.app.id);
447
456
  const promotions = result.promotions.map((promotion) => ({
448
457
  ...promotion,
449
458
  url: promotionUrl(detail.publicUrl, promotion),
@@ -462,7 +471,7 @@ export function registerWorks(program) {
462
471
  }
463
472
  });
464
473
  promotionsCmd
465
- .command("create <work>")
474
+ .command("create <app>")
466
475
  .description("Create an immutable promotion link")
467
476
  .requiredOption("--name <name>", "Promotion name")
468
477
  .option("--provider <provider>", "Promotion provider", "generic")
@@ -473,10 +482,10 @@ export function registerWorks(program) {
473
482
  .option("--utm-term <value>", "UTM term")
474
483
  .option("--utm-content <value>", "UTM content")
475
484
  .option("--json", "Output as JSON")
476
- .action(async (workRef, opts) => {
485
+ .action(async (appRef, opts) => {
477
486
  const client = createClient();
478
487
  try {
479
- const detail = await getWorkByRef(client, workRef);
488
+ const detail = await getAppByRef(client, appRef);
480
489
  const parameters = Object.fromEntries(Object.entries({
481
490
  utm_id: opts.utmId,
482
491
  utm_source: opts.utmSource,
@@ -485,7 +494,7 @@ export function registerWorks(program) {
485
494
  utm_term: opts.utmTerm,
486
495
  utm_content: opts.utmContent,
487
496
  }).filter((entry) => typeof entry[1] === "string"));
488
- const result = await client.works.createPromotion(detail.work.id, {
497
+ const result = await client.apps.createPromotion(detail.app.id, {
489
498
  name: opts.name,
490
499
  provider: opts.provider,
491
500
  parameters,
@@ -506,14 +515,14 @@ export function registerWorks(program) {
506
515
  }
507
516
  });
508
517
  promotionsCmd
509
- .command("stats <work> <promotionId>")
518
+ .command("stats <app> <promotionId>")
510
519
  .description("Show promotion analytics")
511
520
  .option("--json", "Output as JSON")
512
- .action(async (workRef, promotionId, opts) => {
521
+ .action(async (appRef, promotionId, opts) => {
513
522
  const client = createClient();
514
523
  try {
515
- const detail = await getWorkByRef(client, workRef);
516
- const result = await client.works.getPromotionStats(detail.work.id, promotionId);
524
+ const detail = await getAppByRef(client, appRef);
525
+ const result = await client.apps.getPromotionStats(detail.app.id, promotionId);
517
526
  if (jsonRequested(opts))
518
527
  return outJson(result);
519
528
  table([{
@@ -538,18 +547,18 @@ export function registerWorks(program) {
538
547
  handleHttp(e);
539
548
  }
540
549
  });
541
- registerWorkCommerce(worksCmd);
542
- worksCmd
550
+ registerAppCommerce(appsCmd);
551
+ appsCmd
543
552
  .command("rm <id>")
544
553
  .alias("delete")
545
- .description("Delete a work")
554
+ .description("Delete an app")
546
555
  .option("-y, --yes", "Confirm deletion")
547
556
  .action(async (id, opts) => {
548
557
  await confirmDelete(opts);
549
558
  const client = createClient();
550
559
  try {
551
- await client.works.delete(id);
552
- ok("Work deleted");
560
+ await client.apps.delete(id);
561
+ ok("App deleted");
553
562
  }
554
563
  catch (e) {
555
564
  handleHttp(e);
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerDesktop(program: Command): void;
3
+ export declare function registerLegacyUi(program: Command): void;