@neta-art/cohub-cli 2.1.0 → 2.2.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.
@@ -83,25 +83,39 @@ function printProducts(products) {
83
83
  status: product.status,
84
84
  visibility: product.visibility,
85
85
  price: formatUsd(product.pricing.amountUsd),
86
+ credits: product.display.creditsAmount ?? "",
86
87
  })), [
87
88
  { key: "key", label: "Key" },
88
89
  { key: "name", label: "Name" },
89
90
  { key: "status", label: "Status" },
90
91
  { key: "visibility", label: "Visibility" },
91
92
  { key: "price", label: "Price" },
93
+ { key: "credits", label: "Credits" },
92
94
  ]);
93
95
  }
94
96
  function printBenefits(benefits) {
95
- table(benefits.map((benefit) => ({
96
- key: benefit.key,
97
- name: benefit.name,
98
- status: benefit.status,
99
- description: benefit.description ?? "",
100
- })), [
97
+ table(benefits.map((benefit) => {
98
+ let detail;
99
+ if (benefit.type === "credits") {
100
+ const config = benefit.config;
101
+ detail = `${config.amount} credits${config.expiresInDays != null ? ` · ${config.expiresInDays}d` : ""}`;
102
+ }
103
+ else {
104
+ detail = JSON.stringify(benefit.config.metadata);
105
+ }
106
+ return {
107
+ key: benefit.key,
108
+ name: benefit.name,
109
+ type: benefit.type,
110
+ status: benefit.status,
111
+ detail,
112
+ };
113
+ }), [
101
114
  { key: "key", label: "Key" },
102
115
  { key: "name", label: "Name" },
116
+ { key: "type", label: "Type" },
103
117
  { key: "status", label: "Status" },
104
- { key: "description", label: "Description" },
118
+ { key: "detail", label: "Detail" },
105
119
  ]);
106
120
  }
107
121
  function printOrders(orders) {
@@ -303,13 +317,46 @@ Examples:
303
317
  .option("--benefit-key <key>", "Benefit key override")
304
318
  .option("--name <name>", "Benefit name")
305
319
  .option("--description <text>", "Benefit description")
306
- .option("--metadata-json <json>", "Benefit metadata JSON object")
320
+ .option("--type <type>", "Benefit type: feature, credits (default: feature)")
321
+ .option("--amount <n>", "Credit amount (credits type only)")
322
+ .option("--expires-in-days <n>", "Credit expiry in days (credits type only)")
323
+ .option("--metadata-json <json>", "Benefit metadata JSON object (feature type only)")
307
324
  .option("--json", "Output as JSON")
308
325
  .action(async (opts) => {
326
+ const name = requireText(opts.name, "name", "--name <name>");
327
+ const type = parseChoice(opts.type, "type", ["feature", "credits"]) ?? "feature";
328
+ if (type === "credits") {
329
+ const amount = parseInteger(opts.amount, "amount", { fallback: 0, min: 1 });
330
+ if (amount < 1)
331
+ return error("Missing amount", "Pass --amount <n> with a positive integer for credits benefits.");
332
+ const input = {
333
+ key: opts.benefitKey?.trim() || undefined,
334
+ name,
335
+ description: opts.description,
336
+ type: "credits",
337
+ amount,
338
+ expiresInDays: opts.expiresInDays !== undefined
339
+ ? parseInteger(opts.expiresInDays, "expires-in-days", { fallback: 0, min: 1 })
340
+ : undefined,
341
+ };
342
+ const { commerce } = commerceClient(spacesCmd);
343
+ try {
344
+ const result = await commerce.createBenefit(input);
345
+ if (jsonRequested(opts))
346
+ return outJson(result);
347
+ ok(`Benefit created: ${result.benefit.key}`);
348
+ printBenefits([result.benefit]);
349
+ }
350
+ catch (e) {
351
+ handleHttp(e);
352
+ }
353
+ return;
354
+ }
309
355
  const input = {
310
356
  key: opts.benefitKey?.trim() || undefined,
311
- name: requireText(opts.name, "name", "--name <name>"),
357
+ name,
312
358
  description: opts.description,
359
+ type: "feature",
313
360
  metadata: parseMetadataJson(opts.metadataJson),
314
361
  };
315
362
  const { commerce } = commerceClient(spacesCmd);
@@ -28,26 +28,27 @@ function printProducts(products) {
28
28
  status: product.status,
29
29
  visibility: product.visibility,
30
30
  price: formatUsd(product.pricing.amountUsd),
31
+ credits: product.display.creditsAmount ?? "",
31
32
  })), [
32
33
  { key: "key", label: "Key" },
33
34
  { key: "name", label: "Name" },
34
35
  { key: "status", label: "Status" },
35
36
  { key: "visibility", label: "Visibility" },
36
37
  { key: "price", label: "Price" },
38
+ { key: "credits", label: "Credits" },
37
39
  ]);
38
40
  }
39
- function printEntitlements(entitlements) {
40
- table(entitlements.map((item) => ({
41
+ function printEntitlements(result) {
42
+ table(result.entitlements.map((item) => ({
41
43
  benefitKey: item.benefitKey,
42
44
  enabled: item.enabled ? "yes" : "no",
43
- reason: item.reason,
44
45
  metadata: item.metadata ? JSON.stringify(item.metadata) : "",
45
46
  })), [
46
47
  { key: "benefitKey", label: "Benefit" },
47
48
  { key: "enabled", label: "Enabled" },
48
- { key: "reason", label: "Reason" },
49
49
  { key: "metadata", label: "Metadata" },
50
50
  ]);
51
+ console.log(`\nCredits available: ${result.credits.available} (net: ${result.credits.net})`);
51
52
  }
52
53
  function printOrder(order) {
53
54
  table([{
@@ -71,15 +72,16 @@ function printOrder(order) {
71
72
  export function registerWorkCommerce(worksCmd) {
72
73
  const commerceCmd = worksCmd
73
74
  .command("commerce")
74
- .description("Debug work commerce")
75
+ .description("Work commerce operations")
75
76
  .addHelpText("after", `
76
77
  Examples:
77
78
  cohub works commerce products resolve --work-id <work-id> --product-key pro_pack
78
- cohub works commerce entitlements check --work-id <work-id> --benefit-key premium_export
79
+ cohub works commerce entitlements --work-id <work-id>
80
+ cohub works commerce credits consume --work-id <work-id> --amount 100
79
81
  `);
80
82
  const productsCmd = commerceCmd
81
83
  .command("products")
82
- .description("Debug commerce products");
84
+ .description("Resolve commerce products");
83
85
  productsCmd
84
86
  .command("resolve")
85
87
  .description("Resolve public products for a work")
@@ -100,25 +102,64 @@ Examples:
100
102
  handleHttp(e);
101
103
  }
102
104
  });
103
- const entitlementsCmd = commerceCmd
105
+ commerceCmd
104
106
  .command("entitlements")
105
- .description("Debug commerce entitlements");
106
- entitlementsCmd
107
- .command("check")
108
- .description("Check viewer entitlements for a work")
107
+ .description("Show viewer entitlements and credit balance for a work")
109
108
  .option("--work-id <id>", "Work ID")
110
- .option("--benefit-key <key>", "Benefit key", collectOption)
111
109
  .option("--json", "Output as JSON")
112
110
  .action(async (opts) => {
113
111
  const workId = requireText(opts.workId, "work ID", "--work-id <id>");
114
- const benefitKeys = requireList(opts.benefitKey, "benefit key", "--benefit-key <key>");
115
112
  const client = createClient();
116
113
  try {
117
- const result = await client.workCommerce.checkEntitlements(workId, { benefitKeys });
114
+ const result = await client.workCommerce.getEntitlements(workId);
118
115
  if (jsonRequested(opts))
119
116
  return outJson(result);
120
- printEntitlements(result.entitlements);
121
- console.log(`\nChecked at: ${result.checkedAt}`);
117
+ printEntitlements(result);
118
+ }
119
+ catch (e) {
120
+ handleHttp(e);
121
+ }
122
+ });
123
+ const creditsCmd = commerceCmd
124
+ .command("credits")
125
+ .description("Consume credits for a work");
126
+ creditsCmd
127
+ .command("consume")
128
+ .description("Consume credits for a work (self by default)")
129
+ .option("--work-id <id>", "Work ID")
130
+ .option("--amount <n>", "Positive integer credit amount")
131
+ .option("--operation-id <id>", "Idempotency key (generated when omitted)")
132
+ .option("--reason <text>", "Reason for the consumption")
133
+ .option("--json", "Output as JSON")
134
+ .action(async (opts) => {
135
+ const workId = requireText(opts.workId, "work ID", "--work-id <id>");
136
+ const amountText = requireText(opts.amount, "amount", "--amount <n>");
137
+ const amount = Number.parseInt(amountText, 10);
138
+ if (!Number.isSafeInteger(amount) || amount <= 0) {
139
+ return error("Invalid amount", "--amount must be a positive integer.");
140
+ }
141
+ const operationId = opts.operationId?.trim() || crypto.randomUUID();
142
+ const client = createClient();
143
+ try {
144
+ const result = await client.workCommerce.consumeCredits(workId, {
145
+ amount,
146
+ operationId,
147
+ reason: opts.reason,
148
+ });
149
+ if (jsonRequested(opts))
150
+ return outJson({ ...result, operationId });
151
+ ok(`Consumed ${result.amount} credits (operation: ${operationId})`);
152
+ table([{
153
+ status: result.status,
154
+ amount: result.amount,
155
+ remaining: result.remaining,
156
+ shortfall: result.shortfall ?? "",
157
+ }], [
158
+ { key: "status", label: "Status" },
159
+ { key: "amount", label: "Amount" },
160
+ { key: "remaining", label: "Remaining" },
161
+ { key: "shortfall", label: "Shortfall" },
162
+ ]);
122
163
  }
123
164
  catch (e) {
124
165
  handleHttp(e);
@@ -161,7 +202,7 @@ Examples:
161
202
  });
162
203
  const ordersCmd = commerceCmd
163
204
  .command("orders")
164
- .description("Debug commerce orders");
205
+ .description("Look up commerce orders");
165
206
  ordersCmd
166
207
  .command("get")
167
208
  .description("Show a work commerce order")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -16,7 +16,7 @@
16
16
  "@neta-art/generation": "^0.1.10",
17
17
  "commander": "^14.0.3",
18
18
  "sharp": "^0.34.5",
19
- "@neta-art/cohub": "2.1.0"
19
+ "@neta-art/cohub": "2.2.0"
20
20
  },
21
21
  "publishConfig": {
22
22
  "access": "public"