@siglume/api-sdk 1.0.0 → 1.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.
- package/README.md +59 -0
- package/dist/bin/siglume.cjs +201 -49
- package/dist/bin/siglume.cjs.map +1 -1
- package/dist/bin/siglume.js +201 -49
- package/dist/bin/siglume.js.map +1 -1
- package/dist/cli/index.cjs +201 -49
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +27 -0
- package/dist/cli/index.d.ts +27 -0
- package/dist/cli/index.js +201 -49
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +223 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +223 -69
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,6 +83,65 @@ includes runtime checks, contract checks, external OAuth declaration checks, pri
|
|
|
83
83
|
rules, and a mandatory fail-closed LLM legal review for law compliance plus
|
|
84
84
|
public-order / morals compliance.
|
|
85
85
|
|
|
86
|
+
## Usage-Based And Per-Action Billing
|
|
87
|
+
|
|
88
|
+
Use `price_model: PriceModel.USAGE_BASED` or `PriceModel.PER_ACTION` when the
|
|
89
|
+
API must execute before the final operation is known. These listings are free to
|
|
90
|
+
invoke up front. Your adapter returns the executed operation in
|
|
91
|
+
`ExecutionResult.receipt_summary`; the matching `pricing_plan` item sets the
|
|
92
|
+
charge:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
output: { posted: true, post_url: "https://x.com/..." },
|
|
98
|
+
units_consumed: 1,
|
|
99
|
+
amount_minor: 20,
|
|
100
|
+
currency: "JPY",
|
|
101
|
+
receipt_summary: {
|
|
102
|
+
operation: "url_post",
|
|
103
|
+
amount_minor: 20,
|
|
104
|
+
currency: "JPY",
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Set `price_value_minor: 0` when prices vary by operation, and publish a
|
|
110
|
+
buyer-facing `pricing_plan` so API Store and Game API Store can show the plan.
|
|
111
|
+
`pricing_plan.items` is required for `usage_based` and `per_action` listings:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
pricing_plan: {
|
|
115
|
+
display_name: "Operation prices",
|
|
116
|
+
currency: "JPY",
|
|
117
|
+
free_upfront_invocation: true,
|
|
118
|
+
items: [
|
|
119
|
+
{ key: "connection_check", label: "Connection check", price_minor: 0 },
|
|
120
|
+
{ key: "dry_run", label: "Dry-run preview", price_minor: 0 },
|
|
121
|
+
{ key: "text_post", label: "Text post", price_minor: 15 },
|
|
122
|
+
{ key: "url_post", label: "URL post", price_minor: 20 },
|
|
123
|
+
{ key: "reply", label: "Reply", price_minor: 30 },
|
|
124
|
+
],
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The `pricing_plan` is authoritative. If the adapter returns a conflicting
|
|
129
|
+
positive amount, the platform rejects the call instead of charging an arbitrary
|
|
130
|
+
API-declared amount. `0` is valid for free operations. For JPY/JPYC billing,
|
|
131
|
+
positive operation prices must be at least `15` minor units; `1` through `14`
|
|
132
|
+
are rejected by the SDK and platform because platform-sponsored gas can exceed
|
|
133
|
+
the fee.
|
|
134
|
+
`units_consumed` is kept for receipts and analytics; it does not multiply a
|
|
135
|
+
request-type plan price.
|
|
136
|
+
|
|
137
|
+
For irreversible side effects such as posting to X, set
|
|
138
|
+
`billing_timing: "prepay"`. The platform first calls your API as a quote
|
|
139
|
+
(`execution_kind="quote"` / `dry_run=true`), reads `billingPreview.operation`
|
|
140
|
+
and `draftToken`, collects the direct payment for that pricing-plan operation,
|
|
141
|
+
then calls the ACTION endpoint with the same token as `commit_token`. If payment
|
|
142
|
+
fails, the ACTION call is never made. Use the default `"post"` timing only for
|
|
143
|
+
read-only or reversible usage.
|
|
144
|
+
|
|
86
145
|
Company-name publishing is founder-only in the Phase 2 MVP. Use
|
|
87
146
|
`publisher_type: "company"` with `company_id` in `app_manifest.yaml`, or pass
|
|
88
147
|
`--company <company_id>` to the CLI. Paid company listings require the
|
package/dist/bin/siglume.cjs
CHANGED
|
@@ -176,6 +176,61 @@ var init_utils = __esm({
|
|
|
176
176
|
}
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
+
// src/types.ts
|
|
180
|
+
var PermissionClass, ApprovalMode, Environment, PriceModel, AppCategory, MINIMUM_JPY_OPERATION_PRICE_MINOR, ToolManualPermissionClass, SettlementMode;
|
|
181
|
+
var init_types = __esm({
|
|
182
|
+
"src/types.ts"() {
|
|
183
|
+
"use strict";
|
|
184
|
+
PermissionClass = {
|
|
185
|
+
READ_ONLY: "read-only",
|
|
186
|
+
ACTION: "action",
|
|
187
|
+
PAYMENT: "payment",
|
|
188
|
+
/** @deprecated Use READ_ONLY. Behaves identically. */
|
|
189
|
+
RECOMMENDATION: "recommendation"
|
|
190
|
+
};
|
|
191
|
+
ApprovalMode = {
|
|
192
|
+
AUTO: "auto",
|
|
193
|
+
BUDGET_BOUNDED: "budget-bounded",
|
|
194
|
+
ALWAYS_ASK: "always-ask",
|
|
195
|
+
DENY: "deny"
|
|
196
|
+
};
|
|
197
|
+
Environment = {
|
|
198
|
+
SANDBOX: "sandbox",
|
|
199
|
+
LIVE: "live"
|
|
200
|
+
};
|
|
201
|
+
PriceModel = {
|
|
202
|
+
FREE: "free",
|
|
203
|
+
SUBSCRIPTION: "subscription",
|
|
204
|
+
ONE_TIME: "one_time",
|
|
205
|
+
BUNDLE: "bundle",
|
|
206
|
+
USAGE_BASED: "usage_based",
|
|
207
|
+
PER_ACTION: "per_action"
|
|
208
|
+
};
|
|
209
|
+
AppCategory = {
|
|
210
|
+
COMMERCE: "commerce",
|
|
211
|
+
BOOKING: "booking",
|
|
212
|
+
CRM: "crm",
|
|
213
|
+
FINANCE: "finance",
|
|
214
|
+
DOCUMENT: "document",
|
|
215
|
+
COMMUNICATION: "communication",
|
|
216
|
+
MONITORING: "monitoring",
|
|
217
|
+
OTHER: "other"
|
|
218
|
+
};
|
|
219
|
+
MINIMUM_JPY_OPERATION_PRICE_MINOR = 15;
|
|
220
|
+
ToolManualPermissionClass = {
|
|
221
|
+
READ_ONLY: "read_only",
|
|
222
|
+
ACTION: "action",
|
|
223
|
+
PAYMENT: "payment"
|
|
224
|
+
};
|
|
225
|
+
SettlementMode = {
|
|
226
|
+
STRIPE_CHECKOUT: "stripe_checkout",
|
|
227
|
+
STRIPE_PAYMENT_INTENT: "stripe_payment_intent",
|
|
228
|
+
POLYGON_MANDATE: "polygon_mandate",
|
|
229
|
+
EMBEDDED_WALLET_CHARGE: "embedded_wallet_charge"
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
179
234
|
// src/webhooks.ts
|
|
180
235
|
function isRecord2(value) {
|
|
181
236
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1297,6 +1352,58 @@ function validateSaveDataSchema(schema, fieldName) {
|
|
|
1297
1352
|
}
|
|
1298
1353
|
}
|
|
1299
1354
|
}
|
|
1355
|
+
function validatePricingPlanFloor(plan, defaultCurrency) {
|
|
1356
|
+
if (plan === void 0 || plan === null) {
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
if (!isRecord(plan)) {
|
|
1360
|
+
throw new SiglumeClientError("AppManifest.pricing_plan must be an object when provided.");
|
|
1361
|
+
}
|
|
1362
|
+
const items = plan.items;
|
|
1363
|
+
if (items === void 0 || items === null) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
if (!Array.isArray(items)) {
|
|
1367
|
+
throw new SiglumeClientError("AppManifest.pricing_plan.items must be an array when provided.");
|
|
1368
|
+
}
|
|
1369
|
+
const planCurrency = String(plan.currency ?? defaultCurrency ?? "").trim().toUpperCase();
|
|
1370
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
1371
|
+
items.forEach((item, index) => {
|
|
1372
|
+
if (!isRecord(item)) {
|
|
1373
|
+
throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}] must be an object.`);
|
|
1374
|
+
}
|
|
1375
|
+
const itemKey = String(
|
|
1376
|
+
item.key ?? item.operation ?? item.operation_key ?? item.request_type ?? item.receipt_code ?? item.action ?? ""
|
|
1377
|
+
).trim();
|
|
1378
|
+
if (!itemKey) {
|
|
1379
|
+
throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].key is required.`);
|
|
1380
|
+
}
|
|
1381
|
+
if (seenKeys.has(itemKey)) {
|
|
1382
|
+
throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].key duplicates ${itemKey}.`);
|
|
1383
|
+
}
|
|
1384
|
+
seenKeys.add(itemKey);
|
|
1385
|
+
const amountRaw = item.price_minor ?? item.amount_minor ?? item.cost_minor ?? item.value_minor;
|
|
1386
|
+
if (amountRaw === void 0 || amountRaw === null) {
|
|
1387
|
+
throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].price_minor is required.`);
|
|
1388
|
+
}
|
|
1389
|
+
const amountMinor = typeof amountRaw === "number" ? amountRaw : typeof amountRaw === "string" && amountRaw.trim() ? Number(amountRaw) : NaN;
|
|
1390
|
+
if (!Number.isInteger(amountMinor)) {
|
|
1391
|
+
throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].price_minor must be an integer.`);
|
|
1392
|
+
}
|
|
1393
|
+
if (amountMinor < 0) {
|
|
1394
|
+
throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].price_minor must be zero or positive.`);
|
|
1395
|
+
}
|
|
1396
|
+
const currency = String(item.currency ?? planCurrency ?? defaultCurrency ?? "").trim().toUpperCase();
|
|
1397
|
+
if (MINIMUM_JPY_OPERATION_PRICE_CURRENCIES.has(currency) && amountMinor > 0 && amountMinor < MINIMUM_JPY_OPERATION_PRICE_MINOR) {
|
|
1398
|
+
throw new SiglumeClientError(
|
|
1399
|
+
`AppManifest.pricing_plan.items[${index}].price_minor must be 0 or at least ${MINIMUM_JPY_OPERATION_PRICE_MINOR} for JPY/JPYC operation billing.`
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
function pricingPlanHasItems(plan) {
|
|
1405
|
+
return isRecord(plan) && Array.isArray(plan.items) && plan.items.length > 0;
|
|
1406
|
+
}
|
|
1300
1407
|
function buildToolManualQualityReport(payload) {
|
|
1301
1408
|
const qualityBlock = isRecord(payload.quality) ? payload.quality : payload;
|
|
1302
1409
|
const issues = [];
|
|
@@ -1372,6 +1479,7 @@ function buildUrl(baseUrl, path, params) {
|
|
|
1372
1479
|
}
|
|
1373
1480
|
function parseListing(data) {
|
|
1374
1481
|
const metadata = isRecord(data.metadata) ? data.metadata : {};
|
|
1482
|
+
const pricing_plan = isRecord(data.pricing_plan) ? data.pricing_plan : isRecord(metadata.pricing_plan) ? metadata.pricing_plan : null;
|
|
1375
1483
|
const persistence = isRecord(data.persistence) ? data.persistence : isRecord(metadata.persistence) ? metadata.persistence : {};
|
|
1376
1484
|
return {
|
|
1377
1485
|
listing_id: String(data.listing_id ?? data.id ?? ""),
|
|
@@ -1385,6 +1493,8 @@ function parseListing(data) {
|
|
|
1385
1493
|
dry_run_supported: Boolean(data.dry_run_supported ?? false),
|
|
1386
1494
|
price_model: stringOrNull(data.price_model),
|
|
1387
1495
|
price_value_minor: Number(data.price_value_minor ?? 0),
|
|
1496
|
+
pricing_plan,
|
|
1497
|
+
billing_timing: String(data.billing_timing ?? metadata.billing_timing ?? "post"),
|
|
1388
1498
|
currency: String(data.currency ?? "USD"),
|
|
1389
1499
|
allow_free_trial: Boolean(data.allow_free_trial ?? false),
|
|
1390
1500
|
free_trial_duration_days: Number(data.free_trial_duration_days ?? 30),
|
|
@@ -2482,10 +2592,11 @@ function cloneJsonLike(value) {
|
|
|
2482
2592
|
}
|
|
2483
2593
|
return value;
|
|
2484
2594
|
}
|
|
2485
|
-
var DEFAULT_SIGLUME_API_BASE, RETRYABLE_STATUS_CODES, CursorPageResult, SiglumeClient;
|
|
2595
|
+
var DEFAULT_SIGLUME_API_BASE, RETRYABLE_STATUS_CODES, MINIMUM_JPY_OPERATION_PRICE_CURRENCIES, CursorPageResult, SiglumeClient;
|
|
2486
2596
|
var init_client = __esm({
|
|
2487
2597
|
"src/client.ts"() {
|
|
2488
2598
|
"use strict";
|
|
2599
|
+
init_types();
|
|
2489
2600
|
init_errors();
|
|
2490
2601
|
init_webhooks();
|
|
2491
2602
|
init_web3();
|
|
@@ -2493,6 +2604,7 @@ var init_client = __esm({
|
|
|
2493
2604
|
init_utils();
|
|
2494
2605
|
DEFAULT_SIGLUME_API_BASE = "https://siglume.com/v1";
|
|
2495
2606
|
RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
2607
|
+
MINIMUM_JPY_OPERATION_PRICE_CURRENCIES = /* @__PURE__ */ new Set(["JPY", "JPYC"]);
|
|
2496
2608
|
CursorPageResult = class {
|
|
2497
2609
|
items;
|
|
2498
2610
|
next_cursor;
|
|
@@ -2600,6 +2712,8 @@ var init_client = __esm({
|
|
|
2600
2712
|
"jurisdiction",
|
|
2601
2713
|
"price_model",
|
|
2602
2714
|
"price_value_minor",
|
|
2715
|
+
"pricing_plan",
|
|
2716
|
+
"billing_timing",
|
|
2603
2717
|
"currency",
|
|
2604
2718
|
"allow_free_trial",
|
|
2605
2719
|
"free_trial_duration_days",
|
|
@@ -2616,6 +2730,16 @@ var init_client = __esm({
|
|
|
2616
2730
|
payload[fieldName] = value;
|
|
2617
2731
|
}
|
|
2618
2732
|
}
|
|
2733
|
+
if (payload.pricing_plan !== void 0 && (typeof payload.pricing_plan !== "object" || Array.isArray(payload.pricing_plan))) {
|
|
2734
|
+
throw new SiglumeClientError("AppManifest.pricing_plan must be an object when provided.");
|
|
2735
|
+
}
|
|
2736
|
+
if (payload.billing_timing !== void 0 && payload.billing_timing !== null) {
|
|
2737
|
+
const billingTiming = String(payload.billing_timing || "post").trim().toLowerCase();
|
|
2738
|
+
if (billingTiming !== "post" && billingTiming !== "prepay") {
|
|
2739
|
+
throw new SiglumeClientError("AppManifest.billing_timing must be 'post' or 'prepay'.");
|
|
2740
|
+
}
|
|
2741
|
+
payload.billing_timing = billingTiming;
|
|
2742
|
+
}
|
|
2619
2743
|
if (payload.store_vertical === void 0 || payload.store_vertical === null) {
|
|
2620
2744
|
throw new SiglumeClientError(
|
|
2621
2745
|
"AppManifest.store_vertical is required. Choose 'api' for normal API Store listings or 'game' for API games."
|
|
@@ -2631,6 +2755,13 @@ var init_client = __esm({
|
|
|
2631
2755
|
throw new SiglumeClientError(`AppManifest.currency must be 'USD' or 'JPY'. Got ${String(payload.currency)}.`);
|
|
2632
2756
|
}
|
|
2633
2757
|
payload.currency = currency;
|
|
2758
|
+
if (payload.pricing_plan !== void 0) {
|
|
2759
|
+
validatePricingPlanFloor(payload.pricing_plan, currency);
|
|
2760
|
+
}
|
|
2761
|
+
const priceModel = String(payload.price_model ?? "free").trim().toLowerCase();
|
|
2762
|
+
if ((priceModel === "usage_based" || priceModel === "per_action") && !pricingPlanHasItems(payload.pricing_plan)) {
|
|
2763
|
+
throw new SiglumeClientError("AppManifest.pricing_plan.items is required for usage_based/per_action pricing.");
|
|
2764
|
+
}
|
|
2634
2765
|
if (payload.allow_free_trial === void 0 || payload.allow_free_trial === null) {
|
|
2635
2766
|
throw new SiglumeClientError(
|
|
2636
2767
|
"AppManifest.allow_free_trial is required. Pass true to offer a Plus/Pro buyer free trial or false to disable trials."
|
|
@@ -5305,53 +5436,8 @@ function stableValue(value) {
|
|
|
5305
5436
|
return value;
|
|
5306
5437
|
}
|
|
5307
5438
|
|
|
5308
|
-
// src/
|
|
5309
|
-
|
|
5310
|
-
READ_ONLY: "read-only",
|
|
5311
|
-
ACTION: "action",
|
|
5312
|
-
PAYMENT: "payment",
|
|
5313
|
-
/** @deprecated Use READ_ONLY. Behaves identically. */
|
|
5314
|
-
RECOMMENDATION: "recommendation"
|
|
5315
|
-
};
|
|
5316
|
-
var ApprovalMode = {
|
|
5317
|
-
AUTO: "auto",
|
|
5318
|
-
BUDGET_BOUNDED: "budget-bounded",
|
|
5319
|
-
ALWAYS_ASK: "always-ask",
|
|
5320
|
-
DENY: "deny"
|
|
5321
|
-
};
|
|
5322
|
-
var Environment = {
|
|
5323
|
-
SANDBOX: "sandbox",
|
|
5324
|
-
LIVE: "live"
|
|
5325
|
-
};
|
|
5326
|
-
var PriceModel = {
|
|
5327
|
-
FREE: "free",
|
|
5328
|
-
SUBSCRIPTION: "subscription",
|
|
5329
|
-
ONE_TIME: "one_time",
|
|
5330
|
-
BUNDLE: "bundle",
|
|
5331
|
-
USAGE_BASED: "usage_based",
|
|
5332
|
-
PER_ACTION: "per_action"
|
|
5333
|
-
};
|
|
5334
|
-
var AppCategory = {
|
|
5335
|
-
COMMERCE: "commerce",
|
|
5336
|
-
BOOKING: "booking",
|
|
5337
|
-
CRM: "crm",
|
|
5338
|
-
FINANCE: "finance",
|
|
5339
|
-
DOCUMENT: "document",
|
|
5340
|
-
COMMUNICATION: "communication",
|
|
5341
|
-
MONITORING: "monitoring",
|
|
5342
|
-
OTHER: "other"
|
|
5343
|
-
};
|
|
5344
|
-
var ToolManualPermissionClass = {
|
|
5345
|
-
READ_ONLY: "read_only",
|
|
5346
|
-
ACTION: "action",
|
|
5347
|
-
PAYMENT: "payment"
|
|
5348
|
-
};
|
|
5349
|
-
var SettlementMode = {
|
|
5350
|
-
STRIPE_CHECKOUT: "stripe_checkout",
|
|
5351
|
-
STRIPE_PAYMENT_INTENT: "stripe_payment_intent",
|
|
5352
|
-
POLYGON_MANDATE: "polygon_mandate",
|
|
5353
|
-
EMBEDDED_WALLET_CHARGE: "embedded_wallet_charge"
|
|
5354
|
-
};
|
|
5439
|
+
// src/runtime.ts
|
|
5440
|
+
init_types();
|
|
5355
5441
|
|
|
5356
5442
|
// src/testing/recorder.ts
|
|
5357
5443
|
var CASSETTE_VERSION = 1;
|
|
@@ -5743,6 +5829,7 @@ Actual: ${requestSignature(requestRecord, ignoreBodyFields)}`
|
|
|
5743
5829
|
};
|
|
5744
5830
|
|
|
5745
5831
|
// src/tool-manual-validator.ts
|
|
5832
|
+
init_types();
|
|
5746
5833
|
init_utils();
|
|
5747
5834
|
var JURISDICTION_PATTERN = /^[A-Z]{2}(-[A-Z0-9]{1,3})?$/;
|
|
5748
5835
|
var TOOL_NAME_RE = /^[A-Za-z0-9_]{3,64}$/;
|
|
@@ -6014,6 +6101,64 @@ function validate_tool_manual(manualInput) {
|
|
|
6014
6101
|
// src/runtime.ts
|
|
6015
6102
|
init_web3();
|
|
6016
6103
|
var CAPABILITY_KEY_RE = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
6104
|
+
var MINIMUM_JPY_OPERATION_PRICE_CURRENCIES2 = /* @__PURE__ */ new Set(["JPY", "JPYC"]);
|
|
6105
|
+
function pricingPlanFloorIssues(plan, defaultCurrency) {
|
|
6106
|
+
const issues = [];
|
|
6107
|
+
if (plan === void 0 || plan === null) {
|
|
6108
|
+
return issues;
|
|
6109
|
+
}
|
|
6110
|
+
if (typeof plan !== "object" || Array.isArray(plan)) {
|
|
6111
|
+
return ["pricing_plan must be an object when provided"];
|
|
6112
|
+
}
|
|
6113
|
+
const record = plan;
|
|
6114
|
+
const items = record.items;
|
|
6115
|
+
if (items === void 0 || items === null) {
|
|
6116
|
+
return issues;
|
|
6117
|
+
}
|
|
6118
|
+
if (!Array.isArray(items)) {
|
|
6119
|
+
return ["pricing_plan.items must be an array when provided"];
|
|
6120
|
+
}
|
|
6121
|
+
const planCurrency = String(record.currency ?? defaultCurrency ?? "").trim().toUpperCase();
|
|
6122
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
6123
|
+
items.forEach((item, index) => {
|
|
6124
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
6125
|
+
issues.push(`pricing_plan.items[${index}] must be an object`);
|
|
6126
|
+
return;
|
|
6127
|
+
}
|
|
6128
|
+
const itemRecord = item;
|
|
6129
|
+
const itemKey = String(
|
|
6130
|
+
itemRecord.key ?? itemRecord.operation ?? itemRecord.operation_key ?? itemRecord.request_type ?? itemRecord.receipt_code ?? itemRecord.action ?? ""
|
|
6131
|
+
).trim();
|
|
6132
|
+
if (!itemKey) {
|
|
6133
|
+
issues.push(`pricing_plan.items[${index}].key is required`);
|
|
6134
|
+
} else if (seenKeys.has(itemKey)) {
|
|
6135
|
+
issues.push(`pricing_plan.items[${index}].key duplicates ${itemKey}`);
|
|
6136
|
+
} else {
|
|
6137
|
+
seenKeys.add(itemKey);
|
|
6138
|
+
}
|
|
6139
|
+
const amountRaw = itemRecord.price_minor ?? itemRecord.amount_minor ?? itemRecord.cost_minor ?? itemRecord.value_minor;
|
|
6140
|
+
if (amountRaw === void 0 || amountRaw === null) {
|
|
6141
|
+
issues.push(`pricing_plan.items[${index}].price_minor is required`);
|
|
6142
|
+
return;
|
|
6143
|
+
}
|
|
6144
|
+
const amountMinor = typeof amountRaw === "number" ? amountRaw : typeof amountRaw === "string" && amountRaw.trim() ? Number(amountRaw) : NaN;
|
|
6145
|
+
if (!Number.isInteger(amountMinor)) {
|
|
6146
|
+
issues.push(`pricing_plan.items[${index}].price_minor must be an integer`);
|
|
6147
|
+
return;
|
|
6148
|
+
}
|
|
6149
|
+
if (amountMinor < 0) {
|
|
6150
|
+
issues.push(`pricing_plan.items[${index}].price_minor must be zero or positive`);
|
|
6151
|
+
return;
|
|
6152
|
+
}
|
|
6153
|
+
const currency = String(itemRecord.currency ?? planCurrency ?? defaultCurrency ?? "").trim().toUpperCase();
|
|
6154
|
+
if (MINIMUM_JPY_OPERATION_PRICE_CURRENCIES2.has(currency) && amountMinor > 0 && amountMinor < MINIMUM_JPY_OPERATION_PRICE_MINOR) {
|
|
6155
|
+
issues.push(
|
|
6156
|
+
`pricing_plan.items[${index}].price_minor must be 0 or at least ${MINIMUM_JPY_OPERATION_PRICE_MINOR} for JPY/JPYC operation billing`
|
|
6157
|
+
);
|
|
6158
|
+
}
|
|
6159
|
+
});
|
|
6160
|
+
return issues;
|
|
6161
|
+
}
|
|
6017
6162
|
function normalizeExecutionResult(result, executionKind) {
|
|
6018
6163
|
return {
|
|
6019
6164
|
success: Boolean(result.success),
|
|
@@ -6101,6 +6246,13 @@ var AppTestHarness = class {
|
|
|
6101
6246
|
if (!manifest.example_prompts || manifest.example_prompts.length === 0) {
|
|
6102
6247
|
issues.push("at least one example_prompt is recommended");
|
|
6103
6248
|
}
|
|
6249
|
+
issues.push(...pricingPlanFloorIssues(manifest.pricing_plan, String(manifest.currency ?? "USD")));
|
|
6250
|
+
if (manifest.billing_timing !== void 0 && manifest.billing_timing !== "post" && manifest.billing_timing !== "prepay") {
|
|
6251
|
+
issues.push("billing_timing must be 'post' or 'prepay'");
|
|
6252
|
+
}
|
|
6253
|
+
if ((manifest.price_model === PriceModel.USAGE_BASED || manifest.price_model === PriceModel.PER_ACTION) && (!manifest.pricing_plan || !Array.isArray(manifest.pricing_plan.items) || manifest.pricing_plan.items.length === 0)) {
|
|
6254
|
+
issues.push("pricing_plan.items is required for usage_based/per_action pricing");
|
|
6255
|
+
}
|
|
6104
6256
|
if (manifest.permission_class === PermissionClass.ACTION || manifest.permission_class === PermissionClass.PAYMENT) {
|
|
6105
6257
|
if (!manifest.dry_run_supported) {
|
|
6106
6258
|
issues.push("action/payment apps should support dry_run");
|
|
@@ -6176,7 +6328,7 @@ var AppTestHarness = class {
|
|
|
6176
6328
|
};
|
|
6177
6329
|
}
|
|
6178
6330
|
return {
|
|
6179
|
-
experimental:
|
|
6331
|
+
experimental: false,
|
|
6180
6332
|
usage_record,
|
|
6181
6333
|
invoice_line_preview
|
|
6182
6334
|
};
|