@siglume/api-sdk 0.10.8 → 1.1.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.
@@ -155,6 +155,61 @@ var init_utils = __esm({
155
155
  }
156
156
  });
157
157
 
158
+ // src/types.ts
159
+ var PermissionClass, ApprovalMode, Environment, PriceModel, AppCategory, MINIMUM_JPY_OPERATION_PRICE_MINOR, ToolManualPermissionClass, SettlementMode;
160
+ var init_types = __esm({
161
+ "src/types.ts"() {
162
+ "use strict";
163
+ PermissionClass = {
164
+ READ_ONLY: "read-only",
165
+ ACTION: "action",
166
+ PAYMENT: "payment",
167
+ /** @deprecated Use READ_ONLY. Behaves identically. */
168
+ RECOMMENDATION: "recommendation"
169
+ };
170
+ ApprovalMode = {
171
+ AUTO: "auto",
172
+ BUDGET_BOUNDED: "budget-bounded",
173
+ ALWAYS_ASK: "always-ask",
174
+ DENY: "deny"
175
+ };
176
+ Environment = {
177
+ SANDBOX: "sandbox",
178
+ LIVE: "live"
179
+ };
180
+ PriceModel = {
181
+ FREE: "free",
182
+ SUBSCRIPTION: "subscription",
183
+ ONE_TIME: "one_time",
184
+ BUNDLE: "bundle",
185
+ USAGE_BASED: "usage_based",
186
+ PER_ACTION: "per_action"
187
+ };
188
+ AppCategory = {
189
+ COMMERCE: "commerce",
190
+ BOOKING: "booking",
191
+ CRM: "crm",
192
+ FINANCE: "finance",
193
+ DOCUMENT: "document",
194
+ COMMUNICATION: "communication",
195
+ MONITORING: "monitoring",
196
+ OTHER: "other"
197
+ };
198
+ MINIMUM_JPY_OPERATION_PRICE_MINOR = 15;
199
+ ToolManualPermissionClass = {
200
+ READ_ONLY: "read_only",
201
+ ACTION: "action",
202
+ PAYMENT: "payment"
203
+ };
204
+ SettlementMode = {
205
+ STRIPE_CHECKOUT: "stripe_checkout",
206
+ STRIPE_PAYMENT_INTENT: "stripe_payment_intent",
207
+ POLYGON_MANDATE: "polygon_mandate",
208
+ EMBEDDED_WALLET_CHARGE: "embedded_wallet_charge"
209
+ };
210
+ }
211
+ });
212
+
158
213
  // src/webhooks.ts
159
214
  function isRecord2(value) {
160
215
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1276,6 +1331,58 @@ function validateSaveDataSchema(schema, fieldName) {
1276
1331
  }
1277
1332
  }
1278
1333
  }
1334
+ function validatePricingPlanFloor(plan, defaultCurrency) {
1335
+ if (plan === void 0 || plan === null) {
1336
+ return;
1337
+ }
1338
+ if (!isRecord(plan)) {
1339
+ throw new SiglumeClientError("AppManifest.pricing_plan must be an object when provided.");
1340
+ }
1341
+ const items = plan.items;
1342
+ if (items === void 0 || items === null) {
1343
+ return;
1344
+ }
1345
+ if (!Array.isArray(items)) {
1346
+ throw new SiglumeClientError("AppManifest.pricing_plan.items must be an array when provided.");
1347
+ }
1348
+ const planCurrency = String(plan.currency ?? defaultCurrency ?? "").trim().toUpperCase();
1349
+ const seenKeys = /* @__PURE__ */ new Set();
1350
+ items.forEach((item, index) => {
1351
+ if (!isRecord(item)) {
1352
+ throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}] must be an object.`);
1353
+ }
1354
+ const itemKey = String(
1355
+ item.key ?? item.operation ?? item.operation_key ?? item.request_type ?? item.receipt_code ?? item.action ?? ""
1356
+ ).trim();
1357
+ if (!itemKey) {
1358
+ throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].key is required.`);
1359
+ }
1360
+ if (seenKeys.has(itemKey)) {
1361
+ throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].key duplicates ${itemKey}.`);
1362
+ }
1363
+ seenKeys.add(itemKey);
1364
+ const amountRaw = item.price_minor ?? item.amount_minor ?? item.cost_minor ?? item.value_minor;
1365
+ if (amountRaw === void 0 || amountRaw === null) {
1366
+ throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].price_minor is required.`);
1367
+ }
1368
+ const amountMinor = typeof amountRaw === "number" ? amountRaw : typeof amountRaw === "string" && amountRaw.trim() ? Number(amountRaw) : NaN;
1369
+ if (!Number.isInteger(amountMinor)) {
1370
+ throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].price_minor must be an integer.`);
1371
+ }
1372
+ if (amountMinor < 0) {
1373
+ throw new SiglumeClientError(`AppManifest.pricing_plan.items[${index}].price_minor must be zero or positive.`);
1374
+ }
1375
+ const currency = String(item.currency ?? planCurrency ?? defaultCurrency ?? "").trim().toUpperCase();
1376
+ if (MINIMUM_JPY_OPERATION_PRICE_CURRENCIES.has(currency) && amountMinor > 0 && amountMinor < MINIMUM_JPY_OPERATION_PRICE_MINOR) {
1377
+ throw new SiglumeClientError(
1378
+ `AppManifest.pricing_plan.items[${index}].price_minor must be 0 or at least ${MINIMUM_JPY_OPERATION_PRICE_MINOR} for JPY/JPYC operation billing.`
1379
+ );
1380
+ }
1381
+ });
1382
+ }
1383
+ function pricingPlanHasItems(plan) {
1384
+ return isRecord(plan) && Array.isArray(plan.items) && plan.items.length > 0;
1385
+ }
1279
1386
  function buildToolManualQualityReport(payload) {
1280
1387
  const qualityBlock = isRecord(payload.quality) ? payload.quality : payload;
1281
1388
  const issues = [];
@@ -1351,6 +1458,7 @@ function buildUrl(baseUrl, path, params) {
1351
1458
  }
1352
1459
  function parseListing(data) {
1353
1460
  const metadata = isRecord(data.metadata) ? data.metadata : {};
1461
+ const pricing_plan = isRecord(data.pricing_plan) ? data.pricing_plan : isRecord(metadata.pricing_plan) ? metadata.pricing_plan : null;
1354
1462
  const persistence = isRecord(data.persistence) ? data.persistence : isRecord(metadata.persistence) ? metadata.persistence : {};
1355
1463
  return {
1356
1464
  listing_id: String(data.listing_id ?? data.id ?? ""),
@@ -1364,6 +1472,7 @@ function parseListing(data) {
1364
1472
  dry_run_supported: Boolean(data.dry_run_supported ?? false),
1365
1473
  price_model: stringOrNull(data.price_model),
1366
1474
  price_value_minor: Number(data.price_value_minor ?? 0),
1475
+ pricing_plan,
1367
1476
  currency: String(data.currency ?? "USD"),
1368
1477
  allow_free_trial: Boolean(data.allow_free_trial ?? false),
1369
1478
  free_trial_duration_days: Number(data.free_trial_duration_days ?? 30),
@@ -1438,18 +1547,6 @@ function parseBundleMember(data) {
1438
1547
  link_id: stringOrNull(data.link_id)
1439
1548
  };
1440
1549
  }
1441
- function parseConnectedAccountLifecycle(data) {
1442
- return {
1443
- connected_account_id: String(data.connected_account_id ?? ""),
1444
- provider_key: String(data.provider_key ?? ""),
1445
- expires_at: stringOrNull(data.expires_at),
1446
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((s) => typeof s === "string") : [],
1447
- refreshed_at: stringOrNull(data.refreshed_at),
1448
- connection_status: stringOrNull(data.connection_status),
1449
- provider_revoked: typeof data.provider_revoked === "boolean" ? data.provider_revoked : null,
1450
- revoked_at: stringOrNull(data.revoked_at)
1451
- };
1452
- }
1453
1550
  function parseBundle(data) {
1454
1551
  const membersRaw = Array.isArray(data.members) ? data.members : [];
1455
1552
  return {
@@ -1528,21 +1625,6 @@ function parseBinding(data) {
1528
1625
  raw: { ...data }
1529
1626
  };
1530
1627
  }
1531
- function parseConnectedAccount(data) {
1532
- return {
1533
- connected_account_id: String(data.connected_account_id ?? data.id ?? ""),
1534
- provider_key: String(data.provider_key ?? ""),
1535
- account_role: String(data.account_role ?? ""),
1536
- display_name: stringOrNull(data.display_name),
1537
- environment: stringOrNull(data.environment),
1538
- connection_status: stringOrNull(data.connection_status),
1539
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((item) => typeof item === "string") : [],
1540
- metadata: toRecord(data.metadata),
1541
- created_at: stringOrNull(data.created_at),
1542
- updated_at: stringOrNull(data.updated_at),
1543
- raw: { ...data }
1544
- };
1545
- }
1546
1628
  function parseSupportCase(data) {
1547
1629
  return {
1548
1630
  support_case_id: String(data.support_case_id ?? data.id ?? ""),
@@ -2488,10 +2570,11 @@ function cloneJsonLike(value) {
2488
2570
  }
2489
2571
  return value;
2490
2572
  }
2491
- var DEFAULT_SIGLUME_API_BASE, RETRYABLE_STATUS_CODES, CursorPageResult, SiglumeClient;
2573
+ var DEFAULT_SIGLUME_API_BASE, RETRYABLE_STATUS_CODES, MINIMUM_JPY_OPERATION_PRICE_CURRENCIES, CursorPageResult, SiglumeClient;
2492
2574
  var init_client = __esm({
2493
2575
  "src/client.ts"() {
2494
2576
  "use strict";
2577
+ init_types();
2495
2578
  init_errors();
2496
2579
  init_webhooks();
2497
2580
  init_web3();
@@ -2499,6 +2582,7 @@ var init_client = __esm({
2499
2582
  init_utils();
2500
2583
  DEFAULT_SIGLUME_API_BASE = "https://siglume.com/v1";
2501
2584
  RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
2585
+ MINIMUM_JPY_OPERATION_PRICE_CURRENCIES = /* @__PURE__ */ new Set(["JPY", "JPYC"]);
2502
2586
  CursorPageResult = class {
2503
2587
  items;
2504
2588
  next_cursor;
@@ -2581,13 +2665,6 @@ var init_client = __esm({
2581
2665
  if (options.runtime_validation) {
2582
2666
  payload.runtime_validation = coerceMapping(options.runtime_validation, "runtime_validation");
2583
2667
  }
2584
- if (options.oauth_credentials) {
2585
- payload.oauth_credentials = Array.isArray(options.oauth_credentials) ? {
2586
- items: options.oauth_credentials.map(
2587
- (item, index) => coerceMapping(item, `oauth_credentials[${index}]`)
2588
- )
2589
- } : coerceMapping(options.oauth_credentials, "oauth_credentials");
2590
- }
2591
2668
  if (options.source_context) {
2592
2669
  payload.source_context = coerceMapping(options.source_context, "source_context");
2593
2670
  }
@@ -2613,6 +2690,7 @@ var init_client = __esm({
2613
2690
  "jurisdiction",
2614
2691
  "price_model",
2615
2692
  "price_value_minor",
2693
+ "pricing_plan",
2616
2694
  "currency",
2617
2695
  "allow_free_trial",
2618
2696
  "free_trial_duration_days",
@@ -2629,6 +2707,9 @@ var init_client = __esm({
2629
2707
  payload[fieldName] = value;
2630
2708
  }
2631
2709
  }
2710
+ if (payload.pricing_plan !== void 0 && (typeof payload.pricing_plan !== "object" || Array.isArray(payload.pricing_plan))) {
2711
+ throw new SiglumeClientError("AppManifest.pricing_plan must be an object when provided.");
2712
+ }
2632
2713
  if (payload.store_vertical === void 0 || payload.store_vertical === null) {
2633
2714
  throw new SiglumeClientError(
2634
2715
  "AppManifest.store_vertical is required. Choose 'api' for normal API Store listings or 'game' for API games."
@@ -2644,6 +2725,13 @@ var init_client = __esm({
2644
2725
  throw new SiglumeClientError(`AppManifest.currency must be 'USD' or 'JPY'. Got ${String(payload.currency)}.`);
2645
2726
  }
2646
2727
  payload.currency = currency;
2728
+ if (payload.pricing_plan !== void 0) {
2729
+ validatePricingPlanFloor(payload.pricing_plan, currency);
2730
+ }
2731
+ const priceModel = String(payload.price_model ?? "free").trim().toLowerCase();
2732
+ if ((priceModel === "usage_based" || priceModel === "per_action") && !pricingPlanHasItems(payload.pricing_plan)) {
2733
+ throw new SiglumeClientError("AppManifest.pricing_plan.items is required for usage_based/per_action pricing.");
2734
+ }
2647
2735
  if (payload.allow_free_trial === void 0 || payload.allow_free_trial === null) {
2648
2736
  throw new SiglumeClientError(
2649
2737
  "AppManifest.allow_free_trial is required. Pass true to offer a Plus/Pro buyer free trial or false to disable trials."
@@ -2717,7 +2805,6 @@ var init_client = __esm({
2717
2805
  auto_manifest: toRecord(data.auto_manifest),
2718
2806
  confidence: toRecord(data.confidence),
2719
2807
  validation_report: toRecord(data.validation_report),
2720
- oauth_status: toRecord(data.oauth_status),
2721
2808
  review_url: stringOrNull(data.review_url),
2722
2809
  trace_id: meta.trace_id,
2723
2810
  request_id: meta.request_id
@@ -2893,66 +2980,9 @@ var init_client = __esm({
2893
2980
  return parseBundle(data);
2894
2981
  }
2895
2982
  // ----- end bundles -------------------------------------------------------
2896
- // ----- Connected accounts (v0.7 track 3) ---------------------------------
2897
- // `resolve()` is intentionally NOT wrapped: runtime-only, never over the wire.
2898
- async start_connected_account_oauth(input) {
2899
- const body = {
2900
- listing_id: input.listing_id,
2901
- redirect_uri: input.redirect_uri
2902
- };
2903
- if (input.scopes !== void 0) body.scopes = input.scopes;
2904
- if (input.account_role !== void 0) body.account_role = input.account_role;
2905
- const [data] = await this.request("POST", "/me/connected-accounts/oauth/authorize", {
2906
- json_body: body
2907
- });
2908
- return {
2909
- authorize_url: String(data.authorize_url ?? ""),
2910
- state: String(data.state ?? ""),
2911
- provider_key: String(data.provider_key ?? ""),
2912
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((s) => typeof s === "string") : [],
2913
- pkce_method: stringOrNull(data.pkce_method)
2914
- };
2915
- }
2916
- async complete_connected_account_oauth(input) {
2917
- const [data] = await this.request("POST", "/me/connected-accounts/oauth/callback", {
2918
- json_body: { state: input.state, code: input.code }
2919
- });
2920
- return { ...data };
2921
- }
2922
- async refresh_connected_account(account_id) {
2923
- const [data] = await this.request("POST", `/me/connected-accounts/${account_id}/refresh`);
2924
- return parseConnectedAccountLifecycle(data);
2925
- }
2926
- async revoke_connected_account(account_id) {
2927
- const [data] = await this.request("POST", `/me/connected-accounts/${account_id}/revoke`);
2928
- return parseConnectedAccountLifecycle(data);
2929
- }
2930
- async set_listing_oauth_credentials(listing_id, input) {
2931
- const body = {
2932
- provider_key: input.provider_key,
2933
- client_id: input.client_id,
2934
- client_secret: input.client_secret,
2935
- authorize_url: input.authorize_url,
2936
- token_url: input.token_url
2937
- };
2938
- if (input.revoke_url !== void 0) body.revoke_url = input.revoke_url;
2939
- if (input.display_name !== void 0) body.display_name = input.display_name;
2940
- if (input.scope_separator !== void 0) body.scope_separator = input.scope_separator;
2941
- if (input.token_endpoint_auth !== void 0) body.token_endpoint_auth = input.token_endpoint_auth;
2942
- if (input.pkce_required !== void 0) body.pkce_required = input.pkce_required;
2943
- if (input.refresh_supported !== void 0) body.refresh_supported = input.refresh_supported;
2944
- if (input.available_scopes !== void 0) body.available_scopes = input.available_scopes;
2945
- if (input.required_scopes !== void 0) body.required_scopes = input.required_scopes;
2946
- const [data] = await this.request("PUT", `/market/capabilities/${listing_id}/oauth-credentials`, {
2947
- json_body: body
2948
- });
2949
- return { ...data };
2950
- }
2951
- async get_listing_oauth_credentials_status(listing_id) {
2952
- const [data] = await this.request("GET", `/market/capabilities/${listing_id}/oauth-credentials`);
2953
- return { ...data };
2954
- }
2955
- // ----- end connected accounts --------------------------------------------
2983
+ // ----- Connected accounts ------------------------------------------------
2984
+ // Architecture B: publisher APIs own external OAuth and token storage.
2985
+ // The SDK no longer exposes platform OAuth or listing credential APIs.
2956
2986
  async get_developer_portal() {
2957
2987
  const [data, meta] = await this.request("GET", "/market/developer/portal");
2958
2988
  return {
@@ -2985,7 +3015,6 @@ var init_client = __esm({
2985
3015
  dry_run_supported: Boolean(data.dry_run_supported ?? false),
2986
3016
  approval_mode: stringOrNull(data.approval_mode),
2987
3017
  required_connected_accounts: Array.isArray(data.required_connected_accounts) ? data.required_connected_accounts : [],
2988
- connected_accounts: Array.isArray(data.connected_accounts) ? data.connected_accounts.filter((item) => isRecord(item)).map((item) => ({ ...item })) : [],
2989
3018
  stub_providers_enabled: Boolean(data.stub_providers_enabled ?? false),
2990
3019
  simulated_receipts: Boolean(data.simulated_receipts ?? false),
2991
3020
  approval_simulator: Boolean(data.approval_simulator ?? false),
@@ -4283,25 +4312,6 @@ var init_client = __esm({
4283
4312
  raw: { ...data }
4284
4313
  };
4285
4314
  }
4286
- async list_connected_accounts(options = {}) {
4287
- const params = {
4288
- provider_key: options.provider_key,
4289
- environment: options.environment,
4290
- limit: Math.max(1, Math.min(Math.trunc(options.limit ?? 50), 100)),
4291
- cursor: options.cursor
4292
- };
4293
- const [data, meta] = await this.request("GET", "/market/connected-accounts", { params });
4294
- const items = Array.isArray(data.items) ? data.items.filter((item) => isRecord(item)).map(parseConnectedAccount) : [];
4295
- const next_cursor = stringOrNull(data.next_cursor);
4296
- return new CursorPageResult({
4297
- items,
4298
- next_cursor,
4299
- limit: typeof data.limit === "number" ? data.limit : params.limit,
4300
- offset: typeof data.offset === "number" ? data.offset : null,
4301
- meta,
4302
- fetchNext: next_cursor ? (cursor) => this.list_connected_accounts({ ...options, cursor }) : void 0
4303
- });
4304
- }
4305
4315
  async create_support_case(subject, body, options = {}) {
4306
4316
  const summary = subject.trim();
4307
4317
  const details = body.trim();
@@ -5396,53 +5406,8 @@ function stableValue(value) {
5396
5406
  return value;
5397
5407
  }
5398
5408
 
5399
- // src/types.ts
5400
- var PermissionClass = {
5401
- READ_ONLY: "read-only",
5402
- ACTION: "action",
5403
- PAYMENT: "payment",
5404
- /** @deprecated Use READ_ONLY. Behaves identically. */
5405
- RECOMMENDATION: "recommendation"
5406
- };
5407
- var ApprovalMode = {
5408
- AUTO: "auto",
5409
- BUDGET_BOUNDED: "budget-bounded",
5410
- ALWAYS_ASK: "always-ask",
5411
- DENY: "deny"
5412
- };
5413
- var Environment = {
5414
- SANDBOX: "sandbox",
5415
- LIVE: "live"
5416
- };
5417
- var PriceModel = {
5418
- FREE: "free",
5419
- SUBSCRIPTION: "subscription",
5420
- ONE_TIME: "one_time",
5421
- BUNDLE: "bundle",
5422
- USAGE_BASED: "usage_based",
5423
- PER_ACTION: "per_action"
5424
- };
5425
- var AppCategory = {
5426
- COMMERCE: "commerce",
5427
- BOOKING: "booking",
5428
- CRM: "crm",
5429
- FINANCE: "finance",
5430
- DOCUMENT: "document",
5431
- COMMUNICATION: "communication",
5432
- MONITORING: "monitoring",
5433
- OTHER: "other"
5434
- };
5435
- var ToolManualPermissionClass = {
5436
- READ_ONLY: "read_only",
5437
- ACTION: "action",
5438
- PAYMENT: "payment"
5439
- };
5440
- var SettlementMode = {
5441
- STRIPE_CHECKOUT: "stripe_checkout",
5442
- STRIPE_PAYMENT_INTENT: "stripe_payment_intent",
5443
- POLYGON_MANDATE: "polygon_mandate",
5444
- EMBEDDED_WALLET_CHARGE: "embedded_wallet_charge"
5445
- };
5409
+ // src/runtime.ts
5410
+ init_types();
5446
5411
 
5447
5412
  // src/testing/recorder.ts
5448
5413
  var CASSETTE_VERSION = 1;
@@ -5834,6 +5799,7 @@ Actual: ${requestSignature(requestRecord, ignoreBodyFields)}`
5834
5799
  };
5835
5800
 
5836
5801
  // src/tool-manual-validator.ts
5802
+ init_types();
5837
5803
  init_utils();
5838
5804
  var JURISDICTION_PATTERN = /^[A-Z]{2}(-[A-Z0-9]{1,3})?$/;
5839
5805
  var TOOL_NAME_RE = /^[A-Za-z0-9_]{3,64}$/;
@@ -6105,6 +6071,64 @@ function validate_tool_manual(manualInput) {
6105
6071
  // src/runtime.ts
6106
6072
  init_web3();
6107
6073
  var CAPABILITY_KEY_RE = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
6074
+ var MINIMUM_JPY_OPERATION_PRICE_CURRENCIES2 = /* @__PURE__ */ new Set(["JPY", "JPYC"]);
6075
+ function pricingPlanFloorIssues(plan, defaultCurrency) {
6076
+ const issues = [];
6077
+ if (plan === void 0 || plan === null) {
6078
+ return issues;
6079
+ }
6080
+ if (typeof plan !== "object" || Array.isArray(plan)) {
6081
+ return ["pricing_plan must be an object when provided"];
6082
+ }
6083
+ const record = plan;
6084
+ const items = record.items;
6085
+ if (items === void 0 || items === null) {
6086
+ return issues;
6087
+ }
6088
+ if (!Array.isArray(items)) {
6089
+ return ["pricing_plan.items must be an array when provided"];
6090
+ }
6091
+ const planCurrency = String(record.currency ?? defaultCurrency ?? "").trim().toUpperCase();
6092
+ const seenKeys = /* @__PURE__ */ new Set();
6093
+ items.forEach((item, index) => {
6094
+ if (typeof item !== "object" || item === null || Array.isArray(item)) {
6095
+ issues.push(`pricing_plan.items[${index}] must be an object`);
6096
+ return;
6097
+ }
6098
+ const itemRecord = item;
6099
+ const itemKey = String(
6100
+ itemRecord.key ?? itemRecord.operation ?? itemRecord.operation_key ?? itemRecord.request_type ?? itemRecord.receipt_code ?? itemRecord.action ?? ""
6101
+ ).trim();
6102
+ if (!itemKey) {
6103
+ issues.push(`pricing_plan.items[${index}].key is required`);
6104
+ } else if (seenKeys.has(itemKey)) {
6105
+ issues.push(`pricing_plan.items[${index}].key duplicates ${itemKey}`);
6106
+ } else {
6107
+ seenKeys.add(itemKey);
6108
+ }
6109
+ const amountRaw = itemRecord.price_minor ?? itemRecord.amount_minor ?? itemRecord.cost_minor ?? itemRecord.value_minor;
6110
+ if (amountRaw === void 0 || amountRaw === null) {
6111
+ issues.push(`pricing_plan.items[${index}].price_minor is required`);
6112
+ return;
6113
+ }
6114
+ const amountMinor = typeof amountRaw === "number" ? amountRaw : typeof amountRaw === "string" && amountRaw.trim() ? Number(amountRaw) : NaN;
6115
+ if (!Number.isInteger(amountMinor)) {
6116
+ issues.push(`pricing_plan.items[${index}].price_minor must be an integer`);
6117
+ return;
6118
+ }
6119
+ if (amountMinor < 0) {
6120
+ issues.push(`pricing_plan.items[${index}].price_minor must be zero or positive`);
6121
+ return;
6122
+ }
6123
+ const currency = String(itemRecord.currency ?? planCurrency ?? defaultCurrency ?? "").trim().toUpperCase();
6124
+ if (MINIMUM_JPY_OPERATION_PRICE_CURRENCIES2.has(currency) && amountMinor > 0 && amountMinor < MINIMUM_JPY_OPERATION_PRICE_MINOR) {
6125
+ issues.push(
6126
+ `pricing_plan.items[${index}].price_minor must be 0 or at least ${MINIMUM_JPY_OPERATION_PRICE_MINOR} for JPY/JPYC operation billing`
6127
+ );
6128
+ }
6129
+ });
6130
+ return issues;
6131
+ }
6108
6132
  function normalizeExecutionResult(result, executionKind) {
6109
6133
  return {
6110
6134
  success: Boolean(result.success),
@@ -6145,24 +6169,12 @@ var AppTestHarness = class {
6145
6169
  this.stubs = stubs;
6146
6170
  }
6147
6171
  async executeWithKind(execution_kind, task_type = "default", options = {}) {
6148
- const connected_accounts = options.connected_accounts ?? Object.fromEntries(
6149
- Object.keys(this.stubs).map((key) => [
6150
- key,
6151
- {
6152
- provider_key: key,
6153
- session_token: `stub-token-${key}`,
6154
- environment: Environment.SANDBOX,
6155
- scopes: []
6156
- }
6157
- ])
6158
- );
6159
6172
  const ctx = {
6160
6173
  agent_id: "test-agent-001",
6161
6174
  owner_user_id: "test-owner-001",
6162
6175
  task_type,
6163
6176
  environment: Environment.SANDBOX,
6164
6177
  execution_kind,
6165
- connected_accounts,
6166
6178
  input_params: options.input_params ?? {},
6167
6179
  trace_id: options.trace_id,
6168
6180
  idempotency_key: options.idempotency_key,
@@ -6204,6 +6216,10 @@ var AppTestHarness = class {
6204
6216
  if (!manifest.example_prompts || manifest.example_prompts.length === 0) {
6205
6217
  issues.push("at least one example_prompt is recommended");
6206
6218
  }
6219
+ issues.push(...pricingPlanFloorIssues(manifest.pricing_plan, String(manifest.currency ?? "USD")));
6220
+ 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)) {
6221
+ issues.push("pricing_plan.items is required for usage_based/per_action pricing");
6222
+ }
6207
6223
  if (manifest.permission_class === PermissionClass.ACTION || manifest.permission_class === PermissionClass.PAYMENT) {
6208
6224
  if (!manifest.dry_run_supported) {
6209
6225
  issues.push("action/payment apps should support dry_run");
@@ -6252,12 +6268,6 @@ var AppTestHarness = class {
6252
6268
  }
6253
6269
  return issues;
6254
6270
  }
6255
- async simulate_connected_account_missing(task_type = "default", options = {}) {
6256
- return this.executeWithKind("dry_run", task_type, {
6257
- ...options,
6258
- connected_accounts: {}
6259
- });
6260
- }
6261
6271
  async simulate_metering(record, options = {}) {
6262
6272
  const { normalizeUsageRecord: normalizeUsageRecord2 } = await Promise.resolve().then(() => (init_metering(), metering_exports));
6263
6273
  const manifest = await this.app.manifest();
@@ -6285,7 +6295,7 @@ var AppTestHarness = class {
6285
6295
  };
6286
6296
  }
6287
6297
  return {
6288
- experimental: manifest.price_model === PriceModel.USAGE_BASED || manifest.price_model === PriceModel.PER_ACTION,
6298
+ experimental: false,
6289
6299
  usage_record,
6290
6300
  invoice_line_preview
6291
6301
  };
@@ -7220,15 +7230,6 @@ async function loadProject(path = ".") {
7220
7230
  const tool_manual = tool_manual_path ? JSON.parse(await readFile(tool_manual_path, "utf8")) : buildToolManualTemplate(manifest);
7221
7231
  const runtime_validation_path = await findRuntimeValidationPath(root_dir);
7222
7232
  const runtime_validation = runtime_validation_path ? await loadJsonObject(runtime_validation_path, "runtime_validation") : void 0;
7223
- const oauth_credentials_path = await findOauthCredentialsPath(root_dir);
7224
- let oauth_credentials;
7225
- if (oauth_credentials_path) {
7226
- const parsed = JSON.parse(await readFile(oauth_credentials_path, "utf8"));
7227
- if (!isRecord(parsed) && !Array.isArray(parsed)) {
7228
- throw new SiglumeProjectError("oauth_credentials must be a JSON object or array");
7229
- }
7230
- oauth_credentials = parsed;
7231
- }
7232
7233
  return {
7233
7234
  root_dir,
7234
7235
  adapter_path,
@@ -7237,9 +7238,7 @@ async function loadProject(path = ".") {
7237
7238
  tool_manual_path: tool_manual_path ?? void 0,
7238
7239
  tool_manual,
7239
7240
  runtime_validation_path: runtime_validation_path ?? void 0,
7240
- runtime_validation,
7241
- oauth_credentials_path: oauth_credentials_path ?? void 0,
7242
- oauth_credentials
7241
+ runtime_validation
7243
7242
  };
7244
7243
  }
7245
7244
  function isPlatformManagedRequirement(value) {
@@ -7277,6 +7276,21 @@ function requiredOauthProviders(requirements) {
7277
7276
  }
7278
7277
  return providers;
7279
7278
  }
7279
+ function apiManagedRequirementsMissingConnectUrl(requirements) {
7280
+ const missing = [];
7281
+ for (const item of requirements ?? []) {
7282
+ if (!isRecord(item)) continue;
7283
+ const managedBy = String(item.managed_by ?? "").trim().toLowerCase().replaceAll("_", "-");
7284
+ if (managedBy !== "api") continue;
7285
+ const connectUrl = String(item.connect_url ?? "").trim();
7286
+ if (connectUrl) continue;
7287
+ const label = oauthProviderKeyFromRequirement(item) ?? "(missing provider_key)";
7288
+ if (!missing.includes(label)) {
7289
+ missing.push(label);
7290
+ }
7291
+ }
7292
+ return missing;
7293
+ }
7280
7294
  function connectedAccountRequirementLabel(value) {
7281
7295
  if (isRecord(value)) {
7282
7296
  for (const key of ["provider_key", "provider", "account_type", "name"]) {
@@ -7287,102 +7301,6 @@ function connectedAccountRequirementLabel(value) {
7287
7301
  }
7288
7302
  return String(value ?? "").trim();
7289
7303
  }
7290
- function oauthProviderRecordsMap(payload) {
7291
- if (!payload) {
7292
- return {};
7293
- }
7294
- const items = Array.isArray(payload) ? payload : Array.isArray(payload.items) ? payload.items : [payload];
7295
- const resolved = {};
7296
- for (const [index, item] of items.entries()) {
7297
- if (!isRecord(item)) {
7298
- throw new SiglumeProjectError(`oauth_credentials[${index}] must be a JSON object.`);
7299
- }
7300
- const providerKey = oauthProviderKeyFromRequirement(item.provider_key ?? item.provider);
7301
- if (!providerKey) {
7302
- throw new SiglumeProjectError(`oauth_credentials[${index}].provider_key is required.`);
7303
- }
7304
- const authorizeUrl = String(item.authorize_url ?? item.authorization_url ?? item.auth_url ?? "").trim();
7305
- const tokenUrl = String(item.token_url ?? "").trim();
7306
- if (!authorizeUrl || !tokenUrl) {
7307
- throw new SiglumeProjectError(
7308
- `oauth_credentials[${index}] must include authorize_url and token_url.`
7309
- );
7310
- }
7311
- for (const [urlKey, urlValue] of Object.entries({
7312
- authorize_url: authorizeUrl,
7313
- token_url: tokenUrl,
7314
- revoke_url: String(item.revoke_url ?? "").trim()
7315
- })) {
7316
- if (urlValue && !urlValue.startsWith("https://")) {
7317
- throw new SiglumeProjectError(`oauth_credentials[${index}].${urlKey} must be an https URL.`);
7318
- }
7319
- }
7320
- const clientId = String(item.client_id ?? "").trim();
7321
- const clientSecret = String(item.client_secret ?? "").trim();
7322
- if (!clientId || !clientSecret) {
7323
- throw new SiglumeProjectError(`oauth_credentials[${index}] must include client_id and client_secret.`);
7324
- }
7325
- const rawScopes = item.required_scopes ?? item.scopes;
7326
- let scopes = [];
7327
- if (rawScopes == null) {
7328
- scopes = [];
7329
- } else if (!Array.isArray(rawScopes)) {
7330
- throw new SiglumeProjectError(`oauth_credentials[${index}].required_scopes must be a JSON array.`);
7331
- } else {
7332
- scopes = rawScopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7333
- }
7334
- const record = {
7335
- provider_key: providerKey,
7336
- client_id: clientId,
7337
- client_secret: clientSecret,
7338
- required_scopes: scopes
7339
- };
7340
- for (const [key, value] of Object.entries({
7341
- authorize_url: authorizeUrl,
7342
- token_url: tokenUrl,
7343
- revoke_url: String(item.revoke_url ?? "").trim(),
7344
- display_name: String(item.display_name ?? "").trim(),
7345
- scope_separator: String(item.scope_separator ?? "").trim(),
7346
- token_endpoint_auth: String(item.token_endpoint_auth ?? "").trim()
7347
- })) {
7348
- if (value) record[key] = value;
7349
- }
7350
- for (const key of ["pkce_required", "refresh_supported"]) {
7351
- if (typeof item[key] === "boolean") record[key] = item[key];
7352
- }
7353
- if (Array.isArray(item.available_scopes)) {
7354
- const availableScopes = item.available_scopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7355
- if (availableScopes.length > 0) record.available_scopes = availableScopes;
7356
- }
7357
- resolved[providerKey] = record;
7358
- }
7359
- return resolved;
7360
- }
7361
- function canonicalOauthCredentialsPayload(payload) {
7362
- const records = oauthProviderRecordsMap(payload);
7363
- const providerKeys = Object.keys(records).sort();
7364
- if (providerKeys.length === 0) {
7365
- return void 0;
7366
- }
7367
- return {
7368
- items: providerKeys.map((providerKey) => records[providerKey])
7369
- };
7370
- }
7371
- function ensureRequiredOauthCredentials(project) {
7372
- const requiredProviders = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7373
- if (requiredProviders.length === 0) {
7374
- return;
7375
- }
7376
- const provided = new Set(Object.keys(oauthProviderRecordsMap(project.oauth_credentials)));
7377
- const missing = requiredProviders.filter((provider) => !provided.has(provider));
7378
- if (missing.length === 0) {
7379
- return;
7380
- }
7381
- const path = project.oauth_credentials_path ?? join(project.root_dir, "oauth_credentials.json");
7382
- throw new SiglumeProjectError(
7383
- `${path} is required for platform-managed OAuth APIs. Missing provider seeds: ${missing.join(", ")}`
7384
- );
7385
- }
7386
7304
  async function validateProject(path = ".", deps = {}) {
7387
7305
  const project = await loadProject(path);
7388
7306
  const manifest_issues = await projectValidationIssues(project);
@@ -7537,10 +7455,21 @@ function ensureExplicitToolManual(project) {
7537
7455
  async function registrationPreflight(project, client) {
7538
7456
  const manifestIssues = await projectValidationIssues(project);
7539
7457
  const [toolManualValid, toolManualIssues] = validate_tool_manual(project.tool_manual);
7458
+ const retiredPlatformOauthProviders = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7459
+ if (retiredPlatformOauthProviders.length > 0) {
7460
+ throw new SiglumeProjectError(
7461
+ `Registration preflight failed. Fix these before calling auto-register:
7462
+ - platform-managed OAuth is retired. Use managed_by="api" with connect_url: ${retiredPlatformOauthProviders.join(", ")}`
7463
+ );
7464
+ }
7465
+ const apiManagedMissingConnectUrl = apiManagedRequirementsMissingConnectUrl(project.manifest.required_connected_accounts ?? []);
7466
+ if (apiManagedMissingConnectUrl.length > 0) {
7467
+ throw new SiglumeProjectError(
7468
+ `Registration preflight failed. Fix these before calling auto-register:
7469
+ - API-managed OAuth requirements must include connect_url: ${apiManagedMissingConnectUrl.join(", ")}`
7470
+ );
7471
+ }
7540
7472
  const remoteQuality = await client.preview_quality_score(project.tool_manual);
7541
- const requiredOauthProvidersList = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7542
- const oauthProviderRecords = oauthProviderRecordsMap(project.oauth_credentials);
7543
- const missingOauthProviders = requiredOauthProvidersList.filter((provider) => !oauthProviderRecords[provider]);
7544
7473
  const blockingToolManualIssues = toolManualIssues.filter((issue2) => issue2.severity === "error");
7545
7474
  const errors = [
7546
7475
  ...manifestIssues.map((issue2) => String(issue2)),
@@ -7552,17 +7481,12 @@ async function registrationPreflight(project, client) {
7552
7481
  if (!remoteQualityOk(remoteQuality)) {
7553
7482
  errors.push(`remote Tool Manual quality is not publishable: ${remoteQuality.grade} (${remoteQuality.overall_score}/100)`);
7554
7483
  }
7555
- if (missingOauthProviders.length > 0) {
7556
- errors.push(`oauth_credentials.json is required for platform-managed OAuth APIs: ${missingOauthProviders.join(", ")}`);
7557
- }
7558
7484
  const preflight = {
7559
7485
  manifest_issues: manifestIssues,
7560
7486
  tool_manual_valid: toolManualValid,
7561
7487
  tool_manual_issues: toolManualIssues.map((issue2) => toJsonable(issue2)),
7562
7488
  remote_quality: toJsonable(remoteQuality),
7563
- required_oauth_providers: requiredOauthProvidersList,
7564
- oauth_credentials_path: project.oauth_credentials_path ?? null,
7565
- oauth_missing_providers: missingOauthProviders,
7489
+ retired_platform_oauth_providers: retiredPlatformOauthProviders,
7566
7490
  ok: errors.length === 0
7567
7491
  };
7568
7492
  if (errors.length > 0) {
@@ -7601,8 +7525,6 @@ async function runRegistration(path = ".", options = {}, deps = {}) {
7601
7525
  ensureExplicitToolManual(project);
7602
7526
  ensureManifestPublisherIdentity(project);
7603
7527
  ensureRuntimeValidationReady(project);
7604
- ensureRequiredOauthCredentials(project);
7605
- const canonicalOauthCredentials = canonicalOauthCredentialsPayload(project.oauth_credentials);
7606
7528
  const client = await createClient(deps);
7607
7529
  if (requestedCompanySlug) {
7608
7530
  const slug = companyNameSlug(requestedCompanySlug);
@@ -7679,14 +7601,12 @@ async function runRegistration(path = ".", options = {}, deps = {}) {
7679
7601
  }
7680
7602
  }
7681
7603
  const receipt = await client.auto_register(project.manifest, project.tool_manual, {
7682
- runtime_validation: project.runtime_validation,
7683
- oauth_credentials: canonicalOauthCredentials
7604
+ runtime_validation: project.runtime_validation
7684
7605
  });
7685
7606
  const result = {
7686
7607
  receipt: toJsonable(receipt),
7687
7608
  registration_preflight: preflight,
7688
- runtime_validation_path: project.runtime_validation_path ?? null,
7689
- oauth_credentials_path: project.oauth_credentials_path ?? null
7609
+ runtime_validation_path: project.runtime_validation_path ?? null
7690
7610
  };
7691
7611
  if (developerPortalPreflight) {
7692
7612
  result.developer_portal_preflight = developerPortalPreflight;
@@ -7707,7 +7627,6 @@ async function runPreflight(path = ".", deps = {}) {
7707
7627
  ensureExplicitToolManual(project);
7708
7628
  ensureManifestPublisherIdentity(project);
7709
7629
  ensureRuntimeValidationReady(project);
7710
- ensureRequiredOauthCredentials(project);
7711
7630
  const client = await createClient(deps);
7712
7631
  const preflight = await registrationPreflight(project, client);
7713
7632
  let developerPortalPreflight = null;
@@ -7725,8 +7644,7 @@ async function runPreflight(path = ".", deps = {}) {
7725
7644
  ok: true,
7726
7645
  adapter_path: project.adapter_path,
7727
7646
  registration_preflight: preflight,
7728
- runtime_validation_path: project.runtime_validation_path ?? null,
7729
- oauth_credentials_path: project.oauth_credentials_path ?? null
7647
+ runtime_validation_path: project.runtime_validation_path ?? null
7730
7648
  };
7731
7649
  if (developerPortalPreflight) {
7732
7650
  result.developer_portal_preflight = developerPortalPreflight;
@@ -8208,7 +8126,7 @@ function operationReadmeTemplate(operation, manifest, warning) {
8208
8126
  "- `tool_manual.json`: machine-generated ToolManual scaffold",
8209
8127
  "- `runtime_validation.json`: local public endpoint and review-key checks used by auto-register",
8210
8128
  "- `docs/api-usage.md`: publishable API usage guide template for `docs_url`",
8211
- "- `.gitignore`: keeps runtime review keys and OAuth client secrets out of Git",
8129
+ "- `.gitignore`: keeps runtime review keys out of Git",
8212
8130
  "- `tests/test_adapter.ts`: smoke test for `AppTestHarness`",
8213
8131
  "",
8214
8132
  "Before registering, replace all generated placeholders:",
@@ -8216,8 +8134,8 @@ function operationReadmeTemplate(operation, manifest, warning) {
8216
8134
  "- Replace `support_contact` with a real support email address or public support URL.",
8217
8135
  "- Optional `seller_homepage_url` is the seller's official site and can stay blank.",
8218
8136
  "- In the local `runtime_validation.json`, replace the public URL and review-key placeholders.",
8219
- "- If the API uses seller-side OAuth, create a local `oauth_credentials.json` next to the adapter.",
8220
- "- Do not commit real review keys or OAuth client secrets; the generated `.gitignore` excludes those files.",
8137
+ "- If the API uses external OAuth, implement that flow in your API runtime and keep user tokens outside Siglume.",
8138
+ "- Do not commit real review keys or external-provider secrets; the generated `.gitignore` excludes local secret files.",
8221
8139
  "- Because `runtime_validation.json` is ignored, GitHub samples do not commit review-key values.",
8222
8140
  "",
8223
8141
  "## Commands",
@@ -8293,8 +8211,6 @@ function generatedGitignore() {
8293
8211
  "!.env.example",
8294
8212
  "runtime_validation.json",
8295
8213
  "runtime-validation.json",
8296
- "oauth_credentials.json",
8297
- "oauth-credentials.json",
8298
8214
  "",
8299
8215
  "# Python / test artifacts.",
8300
8216
  "__pycache__/",
@@ -8440,13 +8356,6 @@ async function runHarnessForProject(project) {
8440
8356
  checks.push(executionCheck("quote", await harness.execute_quote(task_type, { input_params: sample_input }), harness));
8441
8357
  checks.push(executionCheck("payment", await harness.execute_payment(task_type, { input_params: sample_input }), harness));
8442
8358
  }
8443
- checks.push(
8444
- executionCheck(
8445
- "missing_account_simulation",
8446
- await harness.simulate_connected_account_missing(task_type, { input_params: sample_input }),
8447
- harness
8448
- )
8449
- );
8450
8359
  return {
8451
8360
  adapter_path: project.adapter_path,
8452
8361
  task_type,
@@ -8549,15 +8458,6 @@ async function findRuntimeValidationPath(root_dir) {
8549
8458
  }
8550
8459
  return null;
8551
8460
  }
8552
- async function findOauthCredentialsPath(root_dir) {
8553
- for (const name of ["oauth_credentials.json", "oauth-credentials.json"]) {
8554
- const candidate = join(root_dir, name);
8555
- if (existsSync(candidate)) {
8556
- return candidate;
8557
- }
8558
- }
8559
- return null;
8560
- }
8561
8461
  async function loadJsonObject(path, label) {
8562
8462
  let payload;
8563
8463
  try {
@@ -8789,15 +8689,15 @@ function readmeTemplate(template) {
8789
8689
  "- `tool_manual.json`: editable ToolManual draft for validation and registration",
8790
8690
  "- `runtime_validation.json`: local live API smoke-test contract used during registration",
8791
8691
  "- `docs/api-usage.md`: publish this page and use its public URL as `docs_url`",
8792
- "- `.gitignore`: keeps runtime review keys and OAuth client secrets out of Git",
8692
+ "- `.gitignore`: keeps runtime review keys out of Git",
8793
8693
  "",
8794
8694
  "Before registering, replace all generated placeholders:",
8795
8695
  "- In `adapter.ts` and `manifest.json`, replace `docs_url` with a dedicated public API usage guide, not a homepage.",
8796
8696
  "- Replace `support_contact` with a real support email address or public support URL.",
8797
8697
  "- Optional `seller_homepage_url` is the seller's official site and can stay blank.",
8798
8698
  "- In the local `runtime_validation.json`, replace the public URL and review-key placeholders.",
8799
- "- If the API uses seller-side OAuth, create a local `oauth_credentials.json` next to the adapter.",
8800
- "- Do not commit real review keys or OAuth client secrets; the generated `.gitignore` excludes those files.",
8699
+ "- If the API uses external OAuth, implement that flow in your API runtime and keep user tokens outside Siglume.",
8700
+ "- Do not commit real review keys or external-provider secrets; the generated `.gitignore` excludes local secret files.",
8801
8701
  "- Because `runtime_validation.json` is ignored, GitHub samples do not commit review-key values.",
8802
8702
  "",
8803
8703
  "Suggested workflow:",
@@ -9015,7 +8915,6 @@ async function runCli(argv, deps = {}) {
9015
8915
  emit(stdout, `preflight_quality: ${preflight.remote_quality.grade} (${preflight.remote_quality.overall_score}/100)`);
9016
8916
  }
9017
8917
  if (report.runtime_validation_path) emit(stdout, `runtime_validation_path: ${String(report.runtime_validation_path)}`);
9018
- if (report.oauth_credentials_path) emit(stdout, `oauth_credentials_path: ${String(report.oauth_credentials_path)}`);
9019
8918
  });
9020
8919
  program.command("companies").description("List Siglume companies available for company-name publishing.").option("--json", "emit machine-readable JSON", false).action(async (options) => {
9021
8920
  const report = await listCompanyPublishersReport(deps);
@@ -9066,7 +8965,6 @@ async function runCli(argv, deps = {}) {
9066
8965
  emit(stdout, `listing_id: ${receipt.listing_id}`);
9067
8966
  emit(stdout, `receipt_status: ${receipt.status}`);
9068
8967
  if (receipt.listing_status) emit(stdout, `listing_status: ${receipt.listing_status}`);
9069
- if (receipt.oauth_status) emit(stdout, `oauth_configured: ${Boolean(receipt.oauth_status.configured)}`);
9070
8968
  if (receipt.review_url) emit(stdout, `review_url: ${receipt.review_url}`);
9071
8969
  if (receipt.trace_id) emit(stdout, `trace_id: ${receipt.trace_id}`);
9072
8970
  if (receipt.request_id) emit(stdout, `request_id: ${receipt.request_id}`);