@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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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: "
|
|
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("--
|
|
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
|
|
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(
|
|
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("
|
|
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
|
|
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("
|
|
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
|
-
|
|
105
|
+
commerceCmd
|
|
104
106
|
.command("entitlements")
|
|
105
|
-
.description("
|
|
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.
|
|
114
|
+
const result = await client.workCommerce.getEntitlements(workId);
|
|
118
115
|
if (jsonRequested(opts))
|
|
119
116
|
return outJson(result);
|
|
120
|
-
printEntitlements(result
|
|
121
|
-
|
|
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("
|
|
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.
|
|
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.
|
|
19
|
+
"@neta-art/cohub": "2.2.0"
|
|
20
20
|
},
|
|
21
21
|
"publishConfig": {
|
|
22
22
|
"access": "public"
|