@neta-art/cohub-cli 4.0.0 → 6.0.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.
- package/README.md +23 -51
- package/dist/app-download.d.ts +11 -0
- package/dist/{work-download.js → app-download.js} +23 -23
- package/dist/app-ref.d.ts +5 -0
- package/dist/app-ref.js +8 -0
- package/dist/board-export.js +5 -5
- package/dist/commands/app-commerce.d.ts +2 -0
- package/dist/commands/{work-commerce.js → app-commerce.js} +27 -27
- package/dist/commands/apps.d.ts +4 -0
- package/dist/commands/{works.js → apps.js} +136 -127
- package/dist/commands/board-domain.js +2 -0
- package/dist/commands/boards/animation.js +26 -21
- package/dist/commands/boards/appearance.js +28 -24
- package/dist/commands/boards/context.d.ts +17 -1
- package/dist/commands/boards/context.js +38 -1
- package/dist/commands/boards/examples.d.ts +4 -0
- package/dist/commands/boards/examples.js +224 -0
- package/dist/commands/boards/items.js +12 -16
- package/dist/commands/boards/nodes.js +5 -8
- package/dist/commands/boards.d.ts +6 -11
- package/dist/commands/boards.js +40 -140
- package/dist/commands/cron-jobs.js +1 -1
- package/dist/commands/desktop.d.ts +3 -0
- package/dist/commands/desktop.js +219 -0
- package/dist/commands/profile.js +5 -5
- package/dist/commands/tasks.js +1 -1
- package/dist/index.js +7 -6
- package/package.json +2 -2
- package/dist/commands/ui.d.ts +0 -2
- package/dist/commands/ui.js +0 -199
- package/dist/commands/work-commerce.d.ts +0 -2
- package/dist/commands/works.d.ts +0 -4
- package/dist/work-download.d.ts +0 -11
- package/dist/work-ref.d.ts +0 -5
- package/dist/work-ref.js +0 -8
|
@@ -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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
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",
|
|
65
|
+
return values[0] ? parseChoice(values[0], "status", APP_STATUSES) : "published";
|
|
66
66
|
}
|
|
67
67
|
function resolveVisibility(value) {
|
|
68
|
-
return value ? parseChoice(value, "visibility",
|
|
68
|
+
return value ? parseChoice(value, "visibility", APP_VISIBILITIES) : undefined;
|
|
69
69
|
}
|
|
70
|
-
function
|
|
71
|
-
table([
|
|
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
|
|
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
|
|
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
|
|
115
|
-
const ref =
|
|
114
|
+
export async function getAppStatsByRef(client, app) {
|
|
115
|
+
const ref = parseAppRef(app);
|
|
116
116
|
if ("id" in ref)
|
|
117
|
-
return client.
|
|
118
|
-
const detail = await
|
|
119
|
-
return client.
|
|
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
|
|
126
|
-
process.stdout.write("Deleting
|
|
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
|
|
136
|
+
async function publishAppVersion(id, opts) {
|
|
137
137
|
const client = createClient();
|
|
138
138
|
try {
|
|
139
|
-
const result = await client.
|
|
139
|
+
const result = await client.apps.publishVersion(id);
|
|
140
140
|
if (jsonRequested(opts))
|
|
141
141
|
return outJson(result);
|
|
142
|
-
ok(`
|
|
143
|
-
|
|
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
|
|
150
|
-
const
|
|
151
|
-
|
|
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
|
|
163
|
+
.description("List apps in the target space")
|
|
155
164
|
.option("--json", "Output as JSON")
|
|
156
165
|
.action(async (opts) => {
|
|
157
|
-
const spaceId = resolveSpace(
|
|
166
|
+
const spaceId = resolveSpace(appsCmd);
|
|
158
167
|
const client = createClient();
|
|
159
168
|
try {
|
|
160
|
-
const result = await client.
|
|
169
|
+
const result = await client.apps.listBySpace(spaceId);
|
|
161
170
|
if (jsonRequested(opts))
|
|
162
171
|
return outJson(result);
|
|
163
|
-
table(result.
|
|
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
|
-
|
|
179
|
-
.command("get <
|
|
180
|
-
.description("Show
|
|
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 (
|
|
191
|
+
.action(async (app, opts) => {
|
|
183
192
|
const client = createClient();
|
|
184
193
|
try {
|
|
185
|
-
const result = await
|
|
194
|
+
const result = await getAppByRef(client, app);
|
|
186
195
|
if (jsonRequested(opts))
|
|
187
196
|
return outJson(result);
|
|
188
|
-
|
|
189
|
-
|
|
197
|
+
printApp(result.app);
|
|
198
|
+
printAppUrls(result);
|
|
190
199
|
}
|
|
191
200
|
catch (e) {
|
|
192
201
|
handleHttp(e);
|
|
193
202
|
}
|
|
194
203
|
});
|
|
195
|
-
|
|
196
|
-
.command("stats <
|
|
197
|
-
.description("Show view statistics by id, URL, mention URI, or username/space/
|
|
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 (
|
|
208
|
+
.action(async (app, opts) => {
|
|
200
209
|
const client = createClient();
|
|
201
210
|
try {
|
|
202
|
-
const result = await
|
|
211
|
+
const result = await getAppStatsByRef(client, app);
|
|
203
212
|
if (jsonRequested(opts))
|
|
204
213
|
return outJson(result);
|
|
205
|
-
|
|
214
|
+
printAppStats(result);
|
|
206
215
|
}
|
|
207
216
|
catch (e) {
|
|
208
217
|
handleHttp(e);
|
|
209
218
|
}
|
|
210
219
|
});
|
|
211
|
-
|
|
212
|
-
.command("download <
|
|
213
|
-
.description("Download a published file or directory
|
|
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 (
|
|
225
|
+
.action(async (app, opts) => {
|
|
217
226
|
const client = createClient();
|
|
218
227
|
try {
|
|
219
|
-
const detail = await
|
|
220
|
-
const result = await
|
|
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
|
-
|
|
230
|
-
.command("resolve <
|
|
231
|
-
.description("Resolve a published
|
|
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 (
|
|
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.
|
|
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
|
-
|
|
246
|
-
|
|
254
|
+
printApp(result.app);
|
|
255
|
+
printAppUrls(result);
|
|
247
256
|
}
|
|
248
257
|
catch (e) {
|
|
249
258
|
handleHttp(e);
|
|
250
259
|
}
|
|
251
260
|
});
|
|
252
|
-
|
|
261
|
+
appsCmd
|
|
253
262
|
.command("publish <slug>")
|
|
254
|
-
.description("Create or publish
|
|
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>", "
|
|
260
|
-
.option("--visibility <visibility>", "
|
|
261
|
-
.option("--
|
|
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>", "
|
|
264
|
-
.option("--hide-cohub-bar", "Hide the Cohub footer bar on the public
|
|
265
|
-
.option("--show-cohub-bar", "Show the Cohub footer bar on the public
|
|
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(
|
|
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
|
-
|
|
297
|
+
appScopes: opts.appScope,
|
|
289
298
|
allowedViewerScopes: opts.viewerScope,
|
|
290
299
|
meta,
|
|
291
300
|
};
|
|
292
301
|
try {
|
|
293
|
-
const result = await client.
|
|
302
|
+
const result = await client.apps.create(input);
|
|
294
303
|
if (jsonRequested(opts))
|
|
295
304
|
return outJson(result);
|
|
296
|
-
ok(`
|
|
297
|
-
|
|
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 {
|
|
304
|
-
const
|
|
305
|
-
if (!
|
|
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 {
|
|
308
|
-
status: status === "published" &&
|
|
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
|
-
|
|
321
|
+
appScopes: opts.appScope,
|
|
313
322
|
allowedViewerScopes: opts.viewerScope,
|
|
314
323
|
meta,
|
|
315
324
|
});
|
|
316
325
|
const publishedVersion = status === "published"
|
|
317
|
-
? await client.
|
|
326
|
+
? await client.apps.publishVersion(app.id)
|
|
318
327
|
: null;
|
|
319
|
-
const result = publishedVersion ?? {
|
|
328
|
+
const result = publishedVersion ?? { app };
|
|
320
329
|
if (jsonRequested(opts))
|
|
321
330
|
return outJson(result);
|
|
322
|
-
ok(status === "published" && publishedVersion ? `
|
|
323
|
-
|
|
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
|
-
|
|
339
|
+
appsCmd
|
|
331
340
|
.command("update <id>")
|
|
332
|
-
.description("Update
|
|
333
|
-
.option("--slug <slug>", "New
|
|
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>", "
|
|
339
|
-
.option("--visibility <visibility>", "
|
|
340
|
-
.option("--
|
|
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-
|
|
351
|
+
.option("--clear-app-scopes", "Clear app runtime scopes")
|
|
343
352
|
.option("--clear-viewer-scopes", "Clear viewer-requestable scopes")
|
|
344
|
-
.option("--meta <json>", "
|
|
345
|
-
.option("--hide-cohub-bar", "Hide the Cohub footer bar on the public
|
|
346
|
-
.option("--show-cohub-bar", "Show the Cohub footer bar on the public
|
|
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.
|
|
353
|
-
return error("Conflicting
|
|
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.
|
|
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
|
|
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" &&
|
|
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
|
-
|
|
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, --
|
|
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.
|
|
391
|
-
const publishedVersion = nextStatus === "published" &&
|
|
392
|
-
? await client.
|
|
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 ? `
|
|
398
|
-
|
|
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
|
-
|
|
413
|
+
appsCmd
|
|
405
414
|
.command("publish-version <id>")
|
|
406
|
-
.description("Publish or update the current
|
|
415
|
+
.description("Publish or update the current app version")
|
|
407
416
|
.option("--json", "Output as JSON")
|
|
408
|
-
.action(
|
|
409
|
-
|
|
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(
|
|
414
|
-
|
|
422
|
+
.action(publishAppVersion);
|
|
423
|
+
appsCmd
|
|
415
424
|
.command("versions <id>")
|
|
416
|
-
.description("List
|
|
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.
|
|
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 =
|
|
445
|
+
const promotionsCmd = appsCmd.command("promotions").description("App promotion links and analytics");
|
|
437
446
|
promotionsCmd
|
|
438
|
-
.command("list <
|
|
447
|
+
.command("list <app>")
|
|
439
448
|
.alias("ls")
|
|
440
449
|
.description("List promotion links")
|
|
441
450
|
.option("--json", "Output as JSON")
|
|
442
|
-
.action(async (
|
|
451
|
+
.action(async (appRef, opts) => {
|
|
443
452
|
const client = createClient();
|
|
444
453
|
try {
|
|
445
|
-
const detail = await
|
|
446
|
-
const result = await client.
|
|
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 <
|
|
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 (
|
|
485
|
+
.action(async (appRef, opts) => {
|
|
477
486
|
const client = createClient();
|
|
478
487
|
try {
|
|
479
|
-
const detail = await
|
|
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.
|
|
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 <
|
|
518
|
+
.command("stats <app> <promotionId>")
|
|
510
519
|
.description("Show promotion analytics")
|
|
511
520
|
.option("--json", "Output as JSON")
|
|
512
|
-
.action(async (
|
|
521
|
+
.action(async (appRef, promotionId, opts) => {
|
|
513
522
|
const client = createClient();
|
|
514
523
|
try {
|
|
515
|
-
const detail = await
|
|
516
|
-
const result = await client.
|
|
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
|
-
|
|
542
|
-
|
|
550
|
+
registerAppCommerce(appsCmd);
|
|
551
|
+
appsCmd
|
|
543
552
|
.command("rm <id>")
|
|
544
553
|
.alias("delete")
|
|
545
|
-
.description("Delete
|
|
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.
|
|
552
|
-
ok("
|
|
560
|
+
await client.apps.delete(id);
|
|
561
|
+
ok("App deleted");
|
|
553
562
|
}
|
|
554
563
|
catch (e) {
|
|
555
564
|
handleHttp(e);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { registerBoardAnimationCommands } from "./boards/animation.js";
|
|
2
2
|
import { registerBoardAppearanceCommands } from "./boards/appearance.js";
|
|
3
|
+
import { registerBoardExampleCommands } from "./boards/examples.js";
|
|
3
4
|
import { registerBoardItemCommands } from "./boards/items.js";
|
|
4
5
|
import { registerBoardNodeCommands } from "./boards/nodes.js";
|
|
5
6
|
export function registerBoardDomainCommands(boards) {
|
|
7
|
+
registerBoardExampleCommands(boards);
|
|
6
8
|
registerBoardAppearanceCommands(boards);
|
|
7
9
|
registerBoardNodeCommands(boards);
|
|
8
10
|
registerBoardItemCommands(boards);
|