@neta-art/cohub-cli 5.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.
@@ -0,0 +1,11 @@
1
+ import type { AppGetResponse } from "@neta-art/cohub";
2
+ type DownloadResult = {
3
+ appId: string;
4
+ version: number;
5
+ kind: "file" | "directory";
6
+ output: string;
7
+ files: number;
8
+ bytes: number;
9
+ };
10
+ export declare function downloadApp(detail: AppGetResponse, outputOption?: string, fetcher?: typeof fetch): Promise<DownloadResult>;
11
+ export {};
@@ -9,11 +9,11 @@ const MAX_CONTENT_FILE_BYTES = 1024 * 1024 * 1024;
9
9
  const DOWNLOAD_CONCURRENCY = 4;
10
10
  function safeRelativePath(value, label) {
11
11
  if (!value || value.includes("\\") || value.includes("\0") || posix.isAbsolute(value)) {
12
- throw new Error(`Invalid ${label} in Work manifest`);
12
+ throw new Error(`Invalid ${label} in app manifest`);
13
13
  }
14
14
  const segments = value.split("/");
15
15
  if (segments.some((segment) => !segment || segment === "." || segment === "..")) {
16
- throw new Error(`Invalid ${label} in Work manifest`);
16
+ throw new Error(`Invalid ${label} in app manifest`);
17
17
  }
18
18
  return posix.normalize(value);
19
19
  }
@@ -31,7 +31,7 @@ function isManifestFile(value) {
31
31
  }
32
32
  function parseManifest(value) {
33
33
  if (!value || typeof value !== "object")
34
- throw new Error("Work download manifest is invalid");
34
+ throw new Error("App download manifest is invalid");
35
35
  const manifest = value;
36
36
  if (manifest.kind !== "cohub.work.artifact-manifest"
37
37
  || manifest.version !== 1
@@ -45,7 +45,7 @@ function parseManifest(value) {
45
45
  || !Array.isArray(manifest.files)
46
46
  || !manifest.files.every(isManifestFile)
47
47
  || manifest.files.length !== manifest.fileCount) {
48
- throw new Error("Work download manifest is invalid");
48
+ throw new Error("App download manifest is invalid");
49
49
  }
50
50
  const seenArtifactPaths = new Set();
51
51
  const seenOutputPaths = new Set();
@@ -54,26 +54,26 @@ function parseManifest(value) {
54
54
  file.artifactPath = safeRelativePath(file.artifactPath, "artifact path");
55
55
  file.outputPath = safeRelativePath(file.outputPath, "output path");
56
56
  if (seenArtifactPaths.has(file.artifactPath) || seenOutputPaths.has(file.outputPath)) {
57
- throw new Error("Work download manifest contains duplicate paths");
57
+ throw new Error("App download manifest contains duplicate paths");
58
58
  }
59
59
  seenArtifactPaths.add(file.artifactPath);
60
60
  seenOutputPaths.add(file.outputPath);
61
61
  sizeBytes += file.sizeBytes;
62
62
  }
63
63
  if (sizeBytes !== manifest.sizeBytes)
64
- throw new Error("Work download manifest size is invalid");
64
+ throw new Error("App download manifest size is invalid");
65
65
  safeRelativePath(manifest.entrypoint, "entrypoint");
66
66
  return manifest;
67
67
  }
68
68
  async function readManifest(url, expectedSha256, fetcher) {
69
69
  const response = await fetcher(url);
70
70
  if (!response.ok)
71
- throw new Error(`Failed to download Work manifest (${response.status})`);
71
+ throw new Error(`Failed to download app manifest (${response.status})`);
72
72
  if (!response.body)
73
- throw new Error("Work download manifest is invalid");
73
+ throw new Error("App download manifest is invalid");
74
74
  const contentLength = Number(response.headers.get("content-length") ?? 0);
75
75
  if (Number.isFinite(contentLength) && contentLength > MANIFEST_MAX_BYTES) {
76
- throw new Error("Work download manifest is too large");
76
+ throw new Error("App download manifest is too large");
77
77
  }
78
78
  const chunks = [];
79
79
  let sizeBytes = 0;
@@ -81,19 +81,19 @@ async function readManifest(url, expectedSha256, fetcher) {
81
81
  const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
82
82
  sizeBytes += bytes.byteLength;
83
83
  if (sizeBytes > MANIFEST_MAX_BYTES)
84
- throw new Error("Work download manifest is too large");
84
+ throw new Error("App download manifest is too large");
85
85
  chunks.push(bytes);
86
86
  }
87
87
  const bytes = Buffer.concat(chunks, sizeBytes);
88
88
  const sha256 = createHash("sha256").update(bytes).digest("hex");
89
89
  if (sha256 !== expectedSha256)
90
- throw new Error("Work download manifest checksum mismatch");
90
+ throw new Error("App download manifest checksum mismatch");
91
91
  let parsed;
92
92
  try {
93
93
  parsed = JSON.parse(bytes.toString("utf8"));
94
94
  }
95
95
  catch {
96
- throw new Error("Work download manifest is invalid");
96
+ throw new Error("App download manifest is invalid");
97
97
  }
98
98
  return parseManifest(parsed);
99
99
  }
@@ -203,25 +203,25 @@ async function downloadFiles(input) {
203
203
  throw failure;
204
204
  return downloadedBytes.reduce((sum, size) => sum + size, 0);
205
205
  }
206
- export async function downloadWork(detail, outputOption, fetcher = fetch) {
207
- const { work, content } = detail;
206
+ export async function downloadApp(detail, outputOption, fetcher = fetch) {
207
+ const { app, content } = detail;
208
208
  if (!content)
209
- throw new Error("This Work has no published downloadable artifact");
209
+ throw new Error("This app has no published downloadable artifact");
210
210
  if (content.kind === "port")
211
- throw new Error("Port Works do not have a downloadable artifact");
211
+ throw new Error("Port apps do not have a downloadable artifact");
212
212
  if (content.kind === "board")
213
- throw new Error("Board Works do not have a restorable file or directory artifact");
213
+ throw new Error("Board apps do not have a restorable file or directory artifact");
214
214
  if (!content.download)
215
- throw new Error("This Work version does not support download");
215
+ throw new Error("This app version does not support download");
216
216
  const manifest = await readManifest(content.download.manifestUrl, content.download.manifestSha256, fetcher);
217
217
  if (manifest.targetType !== content.targetType || manifest.targetRef !== content.path) {
218
- throw new Error("Work download manifest does not match the published artifact");
218
+ throw new Error("App download manifest does not match the published artifact");
219
219
  }
220
220
  const entry = manifest.files.find((file) => file.artifactPath === manifest.entrypoint);
221
221
  if (!entry)
222
- throw new Error("Work download manifest entrypoint is missing");
222
+ throw new Error("App download manifest entrypoint is missing");
223
223
  const hasDirectoryOutput = manifest.targetType === "directory" || manifest.files.length > 1;
224
- const output = resolve(outputOption ?? (hasDirectoryOutput ? work.slug : basename(manifest.targetRef)));
224
+ const output = resolve(outputOption ?? (hasDirectoryOutput ? app.slug : basename(manifest.targetRef)));
225
225
  await assertOutputMissing(output);
226
226
  await mkdir(dirname(output), { recursive: true });
227
227
  const stage = await mkdtemp(join(dirname(output), `.${basename(output)}.cohub-download-`));
@@ -237,8 +237,8 @@ export async function downloadWork(detail, outputOption, fetcher = fetch) {
237
237
  await rm(stage, { recursive: true, force: true });
238
238
  }
239
239
  return {
240
- workId: work.id,
241
- version: work.latestVersion,
240
+ appId: app.id,
241
+ version: app.latestVersion,
242
242
  kind: hasDirectoryOutput ? "directory" : "file",
243
243
  output,
244
244
  files: files.length,
@@ -0,0 +1,5 @@
1
+ import type { CohubHttpClient, ParsedAppRef, AppGetResponse } from "@neta-art/cohub";
2
+ import { formatAppRef, parseAppRef } from "@neta-art/cohub";
3
+ export type { ParsedAppRef };
4
+ export { formatAppRef, parseAppRef };
5
+ export declare function getAppByRef(client: CohubHttpClient, input: string): Promise<AppGetResponse>;
@@ -0,0 +1,8 @@
1
+ import { formatAppRef, parseAppRef } from "@neta-art/cohub";
2
+ export { formatAppRef, parseAppRef };
3
+ export function getAppByRef(client, input) {
4
+ const ref = parseAppRef(input);
5
+ return "id" in ref
6
+ ? client.apps.get(ref.id)
7
+ : client.apps.getBySlug(ref.username, ref.spaceSlug, ref.appSlug);
8
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerAppCommerce(appsCmd: Command): void;
@@ -69,31 +69,31 @@ function printOrder(order) {
69
69
  { key: "paidAt", label: "Paid At" },
70
70
  ]);
71
71
  }
72
- export function registerWorkCommerce(worksCmd) {
73
- const commerceCmd = worksCmd
72
+ export function registerAppCommerce(appsCmd) {
73
+ const commerceCmd = appsCmd
74
74
  .command("commerce")
75
- .description("Work commerce operations")
75
+ .description("App commerce operations")
76
76
  .addHelpText("after", `
77
77
  Examples:
78
- cohub works commerce products resolve --work-id <work-id> --product-key pro_pack
79
- cohub works commerce entitlements --work-id <work-id>
80
- cohub works commerce credits consume --work-id <work-id> --amount 100
78
+ cohub apps commerce products resolve --app-id <app-id> --product-key pro_pack
79
+ cohub apps commerce entitlements --app-id <app-id>
80
+ cohub apps commerce credits consume --app-id <app-id> --amount 100
81
81
  `);
82
82
  const productsCmd = commerceCmd
83
83
  .command("products")
84
84
  .description("Resolve commerce products");
85
85
  productsCmd
86
86
  .command("resolve")
87
- .description("Resolve public products for a work")
88
- .option("--work-id <id>", "Work ID")
87
+ .description("Resolve public products for an app")
88
+ .option("--app-id <id>", "App ID")
89
89
  .option("--product-key <key>", "Product key", collectOption)
90
90
  .option("--json", "Output as JSON")
91
91
  .action(async (opts) => {
92
- const workId = requireText(opts.workId, "work ID", "--work-id <id>");
92
+ const appId = requireText(opts.appId, "app ID", "--app-id <id>");
93
93
  const productKeys = requireList(opts.productKey, "product key", "--product-key <key>");
94
94
  const client = createClient();
95
95
  try {
96
- const result = await client.workCommerce.resolveProducts(workId, { productKeys });
96
+ const result = await client.appCommerce.resolveProducts(appId, { productKeys });
97
97
  if (jsonRequested(opts))
98
98
  return outJson(result);
99
99
  printProducts(result.products);
@@ -104,14 +104,14 @@ Examples:
104
104
  });
105
105
  commerceCmd
106
106
  .command("entitlements")
107
- .description("Show viewer entitlements and credit balance for a work")
108
- .option("--work-id <id>", "Work ID")
107
+ .description("Show viewer entitlements and credit balance for an app")
108
+ .option("--app-id <id>", "App ID")
109
109
  .option("--json", "Output as JSON")
110
110
  .action(async (opts) => {
111
- const workId = requireText(opts.workId, "work ID", "--work-id <id>");
111
+ const appId = requireText(opts.appId, "app ID", "--app-id <id>");
112
112
  const client = createClient();
113
113
  try {
114
- const result = await client.workCommerce.getEntitlements(workId);
114
+ const result = await client.appCommerce.getEntitlements(appId);
115
115
  if (jsonRequested(opts))
116
116
  return outJson(result);
117
117
  printEntitlements(result);
@@ -122,17 +122,17 @@ Examples:
122
122
  });
123
123
  const creditsCmd = commerceCmd
124
124
  .command("credits")
125
- .description("Consume credits for a work");
125
+ .description("Consume credits for an app");
126
126
  creditsCmd
127
127
  .command("consume")
128
- .description("Consume credits for a work (self by default)")
129
- .option("--work-id <id>", "Work ID")
128
+ .description("Consume credits for an app (self by default)")
129
+ .option("--app-id <id>", "App ID")
130
130
  .option("--amount <n>", "Positive integer credit amount")
131
131
  .option("--operation-id <id>", "Idempotency key (generated when omitted)")
132
132
  .option("--reason <text>", "Reason for the consumption")
133
133
  .option("--json", "Output as JSON")
134
134
  .action(async (opts) => {
135
- const workId = requireText(opts.workId, "work ID", "--work-id <id>");
135
+ const appId = requireText(opts.appId, "app ID", "--app-id <id>");
136
136
  const amountText = requireText(opts.amount, "amount", "--amount <n>");
137
137
  const amount = Number.parseInt(amountText, 10);
138
138
  if (!Number.isSafeInteger(amount) || amount <= 0) {
@@ -141,7 +141,7 @@ Examples:
141
141
  const operationId = opts.operationId?.trim() || crypto.randomUUID();
142
142
  const client = createClient();
143
143
  try {
144
- const result = await client.workCommerce.consumeCredits(workId, {
144
+ const result = await client.appCommerce.consumeCredits(appId, {
145
145
  amount,
146
146
  operationId,
147
147
  reason: opts.reason,
@@ -167,16 +167,16 @@ Examples:
167
167
  });
168
168
  commerceCmd
169
169
  .command("purchase")
170
- .description("Create a work purchase checkout")
171
- .option("--work-id <id>", "Work ID")
170
+ .description("Create an app purchase checkout")
171
+ .option("--app-id <id>", "App ID")
172
172
  .option("--product-key <key>", "Product key")
173
173
  .option("--json", "Output as JSON")
174
174
  .action(async (opts) => {
175
- const workId = requireText(opts.workId, "work ID", "--work-id <id>");
175
+ const appId = requireText(opts.appId, "app ID", "--app-id <id>");
176
176
  const productKey = requireText(opts.productKey, "product key", "--product-key <key>");
177
177
  const client = createClient();
178
178
  try {
179
- const result = await client.workCommerce.purchase(workId, { productKey });
179
+ const result = await client.appCommerce.purchase(appId, { productKey });
180
180
  if (jsonRequested(opts))
181
181
  return outJson(result);
182
182
  ok(`Checkout created: ${result.checkout.orderId}`);
@@ -205,16 +205,16 @@ Examples:
205
205
  .description("Look up commerce orders");
206
206
  ordersCmd
207
207
  .command("get")
208
- .description("Show a work commerce order")
209
- .option("--work-id <id>", "Work ID")
208
+ .description("Show an app commerce order")
209
+ .option("--app-id <id>", "App ID")
210
210
  .option("--order-id <id>", "Order ID")
211
211
  .option("--json", "Output as JSON")
212
212
  .action(async (opts) => {
213
- const workId = requireText(opts.workId, "work ID", "--work-id <id>");
213
+ const appId = requireText(opts.appId, "app ID", "--app-id <id>");
214
214
  const orderId = requireText(opts.orderId, "order ID", "--order-id <id>");
215
215
  const client = createClient();
216
216
  try {
217
- const result = await client.workCommerce.getOrder(workId, orderId);
217
+ const result = await client.appCommerce.getOrder(appId, orderId);
218
218
  if (jsonRequested(opts))
219
219
  return outJson(result);
220
220
  printOrder(result.order);
@@ -0,0 +1,4 @@
1
+ import { type CohubHttpClient, type AppViewStatsResponse } from "@neta-art/cohub";
2
+ import type { Command } from "commander";
3
+ export declare function getAppStatsByRef(client: CohubHttpClient, app: string): Promise<AppViewStatsResponse>;
4
+ export declare function registerApps(program: Command): void;