@siglume/api-sdk 0.10.0 → 0.10.2

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  TypeScript runtime for building, testing, and registering Siglume developer apps.
4
4
 
5
- This package is prepared in the public SDK repo and ships with the current v0.7.6 release line.
5
+ This package is prepared in the public SDK repo and ships with the current v0.10.x release line.
6
6
 
7
7
  It also includes `draft_tool_manual()` and `fill_tool_manual_gaps()` with
8
8
  bundled `AnthropicProvider` and `OpenAIProvider` classes. Provide
@@ -65,8 +65,8 @@ siglume score . --offline
65
65
  siglume validate .
66
66
  siglume score . --remote
67
67
  siglume preflight . # checks blockers without creating a draft
68
- siglume register . # preflight + draft only
69
- siglume register . --confirm # confirm + publish
68
+ siglume register . # preflight + auto-register + confirm/publish
69
+ siglume register . --draft-only # review-only draft staging
70
70
  ```
71
71
 
72
72
  `siglume register` reads `tool_manual.json`, the local Git-ignored
@@ -76,8 +76,9 @@ credential files Git-ignored because they can contain review keys and client
76
76
  secrets. SDK / HTTP automation can pass
77
77
  `source_url`, `source_context`, and `input_form_spec` directly to
78
78
  `auto-register`. The CLI runs preflight by default, then calls the same
79
- `auto-register` route used by SDK / automation clients. Re-run the
80
- same `capability_key` to stage an upgrade. The server-side publish gate
79
+ `auto-register` route used by SDK / automation clients and confirms publication
80
+ unless `--draft-only` is set. Re-run the same `capability_key` to publish a
81
+ non-material upgrade when checks pass. The server-side publish gate
81
82
  includes runtime checks, contract checks, seller OAuth checks, pricing / payout
82
83
  rules, and a mandatory fail-closed LLM legal review for law compliance plus
83
84
  public-order / morals compliance.
@@ -140,18 +140,6 @@ function camelCaseFromCapabilityKey(capabilityKey) {
140
140
  }
141
141
  return `${words.map((word) => word[0].toUpperCase() + word.slice(1)).join("")}App`;
142
142
  }
143
- function buildDefaultI18n(manifestPayload) {
144
- const job = String(manifestPayload.job_to_be_done ?? "").trim();
145
- const shortDescription = String(
146
- manifestPayload.short_description ?? manifestPayload.job_to_be_done ?? manifestPayload.name ?? ""
147
- ).trim();
148
- return {
149
- job_to_be_done_en: job,
150
- job_to_be_done_ja: job,
151
- short_description_en: shortDescription,
152
- short_description_ja: shortDescription
153
- };
154
- }
155
143
  function buildRegistrationStubSource(manifestPayload, toolManualPayload) {
156
144
  const capabilityKey = String(manifestPayload.capability_key ?? "generated-registration");
157
145
  const jobToBeDone = String(
@@ -306,10 +294,8 @@ var init_webhooks = __esm({
306
294
  "subscription.cancelled",
307
295
  "subscription.paused",
308
296
  "subscription.reinstated",
309
- "refund.issued",
310
297
  "payment.succeeded",
311
298
  "payment.failed",
312
- "payment.disputed",
313
299
  "capability.published",
314
300
  "capability.delisted",
315
301
  "execution.completed",
@@ -1349,6 +1335,7 @@ function parseListing(data) {
1349
1335
  price_value_minor: Number(data.price_value_minor ?? 0),
1350
1336
  currency: String(data.currency ?? "USD"),
1351
1337
  short_description: stringOrNull(data.short_description),
1338
+ description: stringOrNull(data.description),
1352
1339
  docs_url: stringOrNull(data.docs_url),
1353
1340
  support_contact: stringOrNull(data.support_contact),
1354
1341
  seller_display_name: stringOrNull(data.seller_display_name),
@@ -1373,19 +1360,6 @@ function parseBundleMember(data) {
1373
1360
  link_id: stringOrNull(data.link_id)
1374
1361
  };
1375
1362
  }
1376
- function parseConnectedAccountProvider(data) {
1377
- return {
1378
- provider_key: String(data.provider_key ?? ""),
1379
- display_name: String(data.display_name ?? ""),
1380
- auth_type: String(data.auth_type ?? "oauth2"),
1381
- refresh_supported: Boolean(data.refresh_supported ?? false),
1382
- pkce_required: Boolean(data.pkce_required ?? false),
1383
- default_scopes: Array.isArray(data.default_scopes) ? data.default_scopes.filter((s) => typeof s === "string") : [],
1384
- available_scopes: Array.isArray(data.available_scopes) ? data.available_scopes.filter((s) => typeof s === "string") : [],
1385
- scope_separator: String(data.scope_separator ?? " "),
1386
- notes: stringOrNull(data.notes)
1387
- };
1388
- }
1389
1363
  function parseConnectedAccountLifecycle(data) {
1390
1364
  return {
1391
1365
  connected_account_id: String(data.connected_account_id ?? ""),
@@ -2427,50 +2401,6 @@ function parseMarketProposalActionResult(execution) {
2427
2401
  raw: { ...execution.raw }
2428
2402
  };
2429
2403
  }
2430
- function parseRefund(data) {
2431
- return {
2432
- refund_id: String(data.refund_id ?? data.id ?? ""),
2433
- receipt_id: String(data.receipt_id ?? ""),
2434
- owner_user_id: stringOrNull(data.owner_user_id) ?? void 0,
2435
- payment_mandate_id: stringOrNull(data.payment_mandate_id) ?? void 0,
2436
- usage_event_id: stringOrNull(data.usage_event_id) ?? void 0,
2437
- chain_receipt_id: stringOrNull(data.chain_receipt_id) ?? void 0,
2438
- amount_minor: Number(data.amount_minor ?? 0),
2439
- currency: String(data.currency ?? "USD"),
2440
- status: String(data.status ?? "issued"),
2441
- reason_code: String(data.reason_code ?? "customer-request"),
2442
- note: stringOrNull(data.note) ?? void 0,
2443
- idempotency_key: stringOrNull(data.idempotency_key) ?? void 0,
2444
- on_chain_tx_hash: stringOrNull(data.on_chain_tx_hash) ?? void 0,
2445
- metadata: toRecord(data.metadata),
2446
- idempotent_replay: Boolean(data.idempotent_replay ?? false),
2447
- created_at: stringOrNull(data.created_at) ?? void 0,
2448
- updated_at: stringOrNull(data.updated_at) ?? void 0,
2449
- raw: { ...data }
2450
- };
2451
- }
2452
- function parseDispute(data) {
2453
- return {
2454
- dispute_id: String(data.dispute_id ?? data.id ?? ""),
2455
- receipt_id: String(data.receipt_id ?? ""),
2456
- owner_user_id: stringOrNull(data.owner_user_id) ?? void 0,
2457
- payment_mandate_id: stringOrNull(data.payment_mandate_id) ?? void 0,
2458
- usage_event_id: stringOrNull(data.usage_event_id) ?? void 0,
2459
- external_dispute_id: stringOrNull(data.external_dispute_id) ?? void 0,
2460
- status: String(data.status ?? "open"),
2461
- reason_code: String(data.reason_code ?? "manual-review"),
2462
- description: stringOrNull(data.description) ?? void 0,
2463
- evidence: toRecord(data.evidence),
2464
- response_decision: stringOrNull(data.response_decision) ?? void 0,
2465
- response_note: stringOrNull(data.response_note) ?? void 0,
2466
- responded_at: stringOrNull(data.responded_at) ?? void 0,
2467
- metadata: toRecord(data.metadata),
2468
- idempotent_replay: Boolean(data.idempotent_replay ?? false),
2469
- created_at: stringOrNull(data.created_at) ?? void 0,
2470
- updated_at: stringOrNull(data.updated_at) ?? void 0,
2471
- raw: { ...data }
2472
- };
2473
- }
2474
2404
  function cloneJsonLike(value) {
2475
2405
  if (Array.isArray(value)) {
2476
2406
  return value.map((item) => cloneJsonLike(item));
@@ -2555,10 +2485,13 @@ var init_client = __esm({
2555
2485
  async auto_register(manifest, tool_manual, options = {}) {
2556
2486
  const manifestPayload = coerceMapping(manifest, "manifest");
2557
2487
  const toolManualPayload = coerceMapping(tool_manual, "tool_manual");
2488
+ const toolManualForRequest = { ...toolManualPayload };
2489
+ const embeddedInputFormSpec = toolManualForRequest.input_form_spec;
2490
+ delete toolManualForRequest.input_form_spec;
2491
+ const inputFormSpec = options.input_form_spec ?? embeddedInputFormSpec;
2558
2492
  const payload = {
2559
- i18n: buildDefaultI18n(manifestPayload),
2560
2493
  manifest: { ...manifestPayload },
2561
- tool_manual: { ...toolManualPayload }
2494
+ tool_manual: toolManualForRequest
2562
2495
  };
2563
2496
  if (options.source_url) {
2564
2497
  payload.source_url = options.source_url;
@@ -2577,14 +2510,11 @@ var init_client = __esm({
2577
2510
  )
2578
2511
  } : coerceMapping(options.oauth_credentials, "oauth_credentials");
2579
2512
  }
2580
- if (options.metadata) {
2581
- payload.metadata = coerceMapping(options.metadata, "metadata");
2582
- }
2583
2513
  if (options.source_context) {
2584
2514
  payload.source_context = coerceMapping(options.source_context, "source_context");
2585
2515
  }
2586
- if (options.input_form_spec) {
2587
- payload.input_form_spec = coerceMapping(options.input_form_spec, "input_form_spec");
2516
+ if (inputFormSpec !== void 0 && inputFormSpec !== null) {
2517
+ payload.input_form_spec = coerceMapping(inputFormSpec, "input_form_spec");
2588
2518
  }
2589
2519
  for (const fieldName of [
2590
2520
  "capability_key",
@@ -2635,7 +2565,11 @@ var init_client = __esm({
2635
2565
  if (!listing_id) {
2636
2566
  throw new SiglumeClientError("Siglume auto-register response did not include listing_id.");
2637
2567
  }
2638
- this.pendingConfirmations.set(listing_id, { manifest: manifestPayload, tool_manual: toolManualPayload });
2568
+ this.pendingConfirmations.set(listing_id, {
2569
+ manifest: manifestPayload,
2570
+ tool_manual: toRecord(payload.tool_manual),
2571
+ input_form_spec: toRecord(payload.input_form_spec)
2572
+ });
2639
2573
  return {
2640
2574
  listing_id,
2641
2575
  status: String(data.status ?? "draft"),
@@ -2783,11 +2717,6 @@ var init_client = __esm({
2783
2717
  // ----- end bundles -------------------------------------------------------
2784
2718
  // ----- Connected accounts (v0.7 track 3) ---------------------------------
2785
2719
  // `resolve()` is intentionally NOT wrapped: runtime-only, never over the wire.
2786
- async list_connected_account_providers() {
2787
- const [data] = await this.request("GET", "/me/connected-accounts/providers");
2788
- const items = Array.isArray(data.items) ? data.items : [];
2789
- return items.filter((item) => isRecord(item)).map(parseConnectedAccountProvider);
2790
- }
2791
2720
  async start_connected_account_oauth(input) {
2792
2721
  const body = {
2793
2722
  listing_id: input.listing_id,
@@ -2824,8 +2753,17 @@ var init_client = __esm({
2824
2753
  const body = {
2825
2754
  provider_key: input.provider_key,
2826
2755
  client_id: input.client_id,
2827
- client_secret: input.client_secret
2756
+ client_secret: input.client_secret,
2757
+ authorize_url: input.authorize_url,
2758
+ token_url: input.token_url
2828
2759
  };
2760
+ if (input.revoke_url !== void 0) body.revoke_url = input.revoke_url;
2761
+ if (input.display_name !== void 0) body.display_name = input.display_name;
2762
+ if (input.scope_separator !== void 0) body.scope_separator = input.scope_separator;
2763
+ if (input.token_endpoint_auth !== void 0) body.token_endpoint_auth = input.token_endpoint_auth;
2764
+ if (input.pkce_required !== void 0) body.pkce_required = input.pkce_required;
2765
+ if (input.refresh_supported !== void 0) body.refresh_supported = input.refresh_supported;
2766
+ if (input.available_scopes !== void 0) body.available_scopes = input.available_scopes;
2829
2767
  if (input.required_scopes !== void 0) body.required_scopes = input.required_scopes;
2830
2768
  const [data] = await this.request("PUT", `/market/capabilities/${listing_id}/oauth-credentials`, {
2831
2769
  json_body: body
@@ -4230,105 +4168,6 @@ ${details}` : summary;
4230
4168
  fetchNext: next_cursor ? (cursor) => this.list_support_cases({ ...options, cursor }) : void 0
4231
4169
  });
4232
4170
  }
4233
- async issue_partial_refund(options) {
4234
- const receipt_id = String(options.receipt_id ?? "").trim();
4235
- const idempotency_key = String(options.idempotency_key ?? "").trim();
4236
- if (!receipt_id) {
4237
- throw new SiglumeClientError("receipt_id is required.");
4238
- }
4239
- if (!idempotency_key) {
4240
- throw new SiglumeClientError("idempotency_key is required.");
4241
- }
4242
- if (!Number.isFinite(options.amount_minor)) {
4243
- throw new SiglumeClientError("amount_minor must be a finite number.");
4244
- }
4245
- const amount_minor = Math.trunc(options.amount_minor);
4246
- if (amount_minor <= 0) {
4247
- throw new SiglumeClientError("amount_minor must be positive.");
4248
- }
4249
- if (typeof options.original_amount_minor === "number" && amount_minor > Math.trunc(options.original_amount_minor)) {
4250
- throw new SiglumeClientError("amount_minor cannot exceed the original receipt amount.");
4251
- }
4252
- const [data] = await this.request("POST", "/market/refunds", {
4253
- json_body: {
4254
- receipt_id,
4255
- amount_minor,
4256
- reason_code: options.reason ?? "customer-request",
4257
- note: options.note,
4258
- idempotency_key
4259
- }
4260
- });
4261
- return parseRefund(data);
4262
- }
4263
- async issue_full_refund(options) {
4264
- const receipt_id = String(options.receipt_id ?? "").trim();
4265
- if (!receipt_id) {
4266
- throw new SiglumeClientError("receipt_id is required.");
4267
- }
4268
- const provided_key = String(options.idempotency_key ?? "").trim();
4269
- const idempotency_key = provided_key || `full-refund:${receipt_id}`;
4270
- const [data] = await this.request("POST", "/market/refunds", {
4271
- json_body: {
4272
- receipt_id,
4273
- reason_code: options.reason ?? "customer-request",
4274
- note: options.note,
4275
- idempotency_key
4276
- }
4277
- });
4278
- return parseRefund(data);
4279
- }
4280
- async list_refunds(options = {}) {
4281
- const [data] = await this.requestAny("GET", "/market/refunds", {
4282
- params: {
4283
- receipt_id: options.receipt_id,
4284
- limit: Math.max(1, Math.min(Math.trunc(options.limit ?? 50), 100))
4285
- }
4286
- });
4287
- if (!Array.isArray(data)) {
4288
- throw new SiglumeClientError("Expected refunds to be returned as an array.");
4289
- }
4290
- return data.filter((item) => isRecord(item)).map(parseRefund);
4291
- }
4292
- async get_refund(refund_id) {
4293
- const [data] = await this.request("GET", `/market/refunds/${refund_id}`);
4294
- return parseRefund(data);
4295
- }
4296
- async get_refunds_for_receipt(receipt_id, options = {}) {
4297
- return this.list_refunds({ receipt_id, limit: options.limit });
4298
- }
4299
- async list_disputes(options = {}) {
4300
- const [data] = await this.requestAny("GET", "/market/disputes", {
4301
- params: {
4302
- receipt_id: options.receipt_id,
4303
- limit: Math.max(1, Math.min(Math.trunc(options.limit ?? 50), 100))
4304
- }
4305
- });
4306
- if (!Array.isArray(data)) {
4307
- throw new SiglumeClientError("Expected disputes to be returned as an array.");
4308
- }
4309
- return data.filter((item) => isRecord(item)).map(parseDispute);
4310
- }
4311
- async get_dispute(dispute_id) {
4312
- const [data] = await this.request("GET", `/market/disputes/${dispute_id}`);
4313
- return parseDispute(data);
4314
- }
4315
- async respond_to_dispute(options) {
4316
- const dispute_id = String(options.dispute_id ?? "").trim();
4317
- if (!dispute_id) {
4318
- throw new SiglumeClientError("dispute_id is required.");
4319
- }
4320
- if (!isRecord(options.evidence)) {
4321
- throw new SiglumeClientError("evidence must be an object.");
4322
- }
4323
- const [data] = await this.request("POST", `/market/disputes/${dispute_id}/respond`, {
4324
- json_body: {
4325
- response: options.response,
4326
- evidence: toRecord(options.evidence),
4327
- note: options.note
4328
- }
4329
- });
4330
- return parseDispute(data);
4331
- }
4332
4171
  async create_webhook_subscription(options) {
4333
4172
  const normalizedEventTypes = options.event_types.map((item) => String(item).trim()).filter((item) => item.length > 0);
4334
4173
  if (normalizedEventTypes.length === 0) {
@@ -5837,16 +5676,24 @@ function coerceToolManual(manual) {
5837
5676
  }
5838
5677
  function checkSchemaForbiddenRecursive(schema, rootField, pushIssue, path = "") {
5839
5678
  for (const keyword of COMPOSITION_KEYWORDS) {
5840
- if (keyword in schema) {
5841
- const location = path ? `${rootField}.${path}.${keyword}` : `${rootField}.${keyword}`;
5842
- pushIssue(
5843
- issue(
5844
- "INPUT_SCHEMA",
5845
- `Composition keyword '${keyword}' is not allowed in beta${path ? ` at ${path}` : ""}`,
5846
- location
5847
- )
5848
- );
5679
+ if (!(keyword in schema)) {
5680
+ continue;
5681
+ }
5682
+ const branches = schema[keyword];
5683
+ const location = path ? `${rootField}.${path}.${keyword}` : `${rootField}.${keyword}`;
5684
+ if (!Array.isArray(branches) || branches.length === 0) {
5685
+ pushIssue(issue("INPUT_SCHEMA", `${keyword} must be a non-empty array`, location));
5686
+ continue;
5849
5687
  }
5688
+ branches.forEach((branch, index) => {
5689
+ const branchPath = path ? `${path}.${keyword}[${index}]` : `${keyword}[${index}]`;
5690
+ const branchLocation = `${rootField}.${branchPath}`;
5691
+ if (!isRecord(branch)) {
5692
+ pushIssue(issue("INPUT_SCHEMA", `${keyword}[${index}] must be an object`, branchLocation));
5693
+ return;
5694
+ }
5695
+ checkSchemaForbiddenRecursive(branch, rootField, pushIssue, branchPath);
5696
+ });
5850
5697
  }
5851
5698
  for (const forbidden of INPUT_SCHEMA_FORBIDDEN_KEYS) {
5852
5699
  if (forbidden in schema) {
@@ -7213,43 +7060,51 @@ async function loadProject(path = ".") {
7213
7060
  oauth_credentials
7214
7061
  };
7215
7062
  }
7216
- var OAUTH_PROVIDER_ALIASES = {
7217
- x: "twitter",
7218
- "x-twitter": "twitter",
7219
- twitter: "twitter",
7220
- slack: "slack",
7221
- google: "google",
7222
- gmail: "google",
7223
- "google-drive": "google",
7224
- "google-calendar": "google",
7225
- github: "github",
7226
- linear: "linear",
7227
- notion: "notion"
7228
- };
7063
+ function isPlatformManagedRequirement(value) {
7064
+ if (!isRecord(value)) return false;
7065
+ if (value.platform_managed === true) return true;
7066
+ const owner = String(
7067
+ value.managed_by ?? value.auth_managed_by ?? value.connection_owner ?? ""
7068
+ ).trim().toLowerCase().replaceAll("_", "-");
7069
+ return owner === "platform" || owner === "siglume" || owner === "siglume-platform";
7070
+ }
7229
7071
  function oauthProviderKeyFromRequirement(value) {
7230
- const raw = String(value ?? "").trim().toLowerCase().replaceAll("_", "-");
7231
- if (!raw) return null;
7232
- if (OAUTH_PROVIDER_ALIASES[raw]) {
7233
- return OAUTH_PROVIDER_ALIASES[raw];
7234
- }
7235
- for (const token of raw.replaceAll("/", "-").replaceAll(":", "-").split("-")) {
7236
- const next = token.trim();
7237
- if (OAUTH_PROVIDER_ALIASES[next]) {
7238
- return OAUTH_PROVIDER_ALIASES[next];
7072
+ if (isRecord(value)) {
7073
+ for (const key of ["provider_key", "provider", "account_type", "name"]) {
7074
+ const providerKey = oauthProviderKeyFromRequirement(value[key]);
7075
+ if (providerKey) return providerKey;
7239
7076
  }
7077
+ return null;
7240
7078
  }
7241
- return null;
7079
+ const raw = String(value ?? "").trim();
7080
+ return raw || null;
7242
7081
  }
7243
7082
  function requiredOauthProviders(requirements) {
7244
7083
  const providers = [];
7245
7084
  for (const item of requirements ?? []) {
7085
+ if (!isPlatformManagedRequirement(item)) continue;
7246
7086
  const providerKey = oauthProviderKeyFromRequirement(item);
7087
+ if (!providerKey) {
7088
+ throw new SiglumeProjectError(
7089
+ "required_connected_accounts platform-managed entries must include a provider_key"
7090
+ );
7091
+ }
7247
7092
  if (providerKey && !providers.includes(providerKey)) {
7248
7093
  providers.push(providerKey);
7249
7094
  }
7250
7095
  }
7251
7096
  return providers;
7252
7097
  }
7098
+ function connectedAccountRequirementLabel(value) {
7099
+ if (isRecord(value)) {
7100
+ for (const key of ["provider_key", "provider", "account_type", "name"]) {
7101
+ const label = String(value[key] ?? "").trim();
7102
+ if (label) return label;
7103
+ }
7104
+ return "";
7105
+ }
7106
+ return String(value ?? "").trim();
7107
+ }
7253
7108
  function oauthProviderRecordsMap(payload) {
7254
7109
  if (!payload) {
7255
7110
  return {};
@@ -7262,7 +7117,23 @@ function oauthProviderRecordsMap(payload) {
7262
7117
  }
7263
7118
  const providerKey = oauthProviderKeyFromRequirement(item.provider_key ?? item.provider);
7264
7119
  if (!providerKey) {
7265
- throw new SiglumeProjectError(`oauth_credentials[${index}].provider_key is unsupported.`);
7120
+ throw new SiglumeProjectError(`oauth_credentials[${index}].provider_key is required.`);
7121
+ }
7122
+ const authorizeUrl = String(item.authorize_url ?? item.authorization_url ?? item.auth_url ?? "").trim();
7123
+ const tokenUrl = String(item.token_url ?? "").trim();
7124
+ if (!authorizeUrl || !tokenUrl) {
7125
+ throw new SiglumeProjectError(
7126
+ `oauth_credentials[${index}] must include authorize_url and token_url.`
7127
+ );
7128
+ }
7129
+ for (const [urlKey, urlValue] of Object.entries({
7130
+ authorize_url: authorizeUrl,
7131
+ token_url: tokenUrl,
7132
+ revoke_url: String(item.revoke_url ?? "").trim()
7133
+ })) {
7134
+ if (urlValue && !urlValue.startsWith("https://")) {
7135
+ throw new SiglumeProjectError(`oauth_credentials[${index}].${urlKey} must be an https URL.`);
7136
+ }
7266
7137
  }
7267
7138
  const clientId = String(item.client_id ?? "").trim();
7268
7139
  const clientSecret = String(item.client_secret ?? "").trim();
@@ -7278,12 +7149,30 @@ function oauthProviderRecordsMap(payload) {
7278
7149
  } else {
7279
7150
  scopes = rawScopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7280
7151
  }
7281
- resolved[providerKey] = {
7152
+ const record = {
7282
7153
  provider_key: providerKey,
7283
7154
  client_id: clientId,
7284
7155
  client_secret: clientSecret,
7285
7156
  required_scopes: scopes
7286
7157
  };
7158
+ for (const [key, value] of Object.entries({
7159
+ authorize_url: authorizeUrl,
7160
+ token_url: tokenUrl,
7161
+ revoke_url: String(item.revoke_url ?? "").trim(),
7162
+ display_name: String(item.display_name ?? "").trim(),
7163
+ scope_separator: String(item.scope_separator ?? "").trim(),
7164
+ token_endpoint_auth: String(item.token_endpoint_auth ?? "").trim()
7165
+ })) {
7166
+ if (value) record[key] = value;
7167
+ }
7168
+ for (const key of ["pkce_required", "refresh_supported"]) {
7169
+ if (typeof item[key] === "boolean") record[key] = item[key];
7170
+ }
7171
+ if (Array.isArray(item.available_scopes)) {
7172
+ const availableScopes = item.available_scopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7173
+ if (availableScopes.length > 0) record.available_scopes = availableScopes;
7174
+ }
7175
+ resolved[providerKey] = record;
7287
7176
  }
7288
7177
  return resolved;
7289
7178
  }
@@ -7309,7 +7198,7 @@ function ensureRequiredOauthCredentials(project) {
7309
7198
  }
7310
7199
  const path = project.oauth_credentials_path ?? (0, import_node_path.join)(project.root_dir, "oauth_credentials.json");
7311
7200
  throw new SiglumeProjectError(
7312
- `${path} is required for OAuth-backed APIs. Missing provider seeds: ${missing.join(", ")}`
7201
+ `${path} is required for platform-managed OAuth APIs. Missing provider seeds: ${missing.join(", ")}`
7313
7202
  );
7314
7203
  }
7315
7204
  async function validateProject(path = ".", deps = {}) {
@@ -7477,7 +7366,7 @@ async function registrationPreflight(project, client) {
7477
7366
  errors.push(`remote Tool Manual quality is not publishable: ${remoteQuality.grade} (${remoteQuality.overall_score}/100)`);
7478
7367
  }
7479
7368
  if (missingOauthProviders.length > 0) {
7480
- errors.push(`oauth_credentials.json is required for OAuth-backed APIs: ${missingOauthProviders.join(", ")}`);
7369
+ errors.push(`oauth_credentials.json is required for platform-managed OAuth APIs: ${missingOauthProviders.join(", ")}`);
7481
7370
  }
7482
7371
  const preflight = {
7483
7372
  manifest_issues: manifestIssues,
@@ -7503,6 +7392,7 @@ async function runRegistration(path = ".", options = {}, deps = {}) {
7503
7392
  ensureManifestPublisherIdentity(project);
7504
7393
  ensureRuntimeValidationReady(project);
7505
7394
  ensureRequiredOauthCredentials(project);
7395
+ const canonicalOauthCredentials = canonicalOauthCredentialsPayload(project.oauth_credentials);
7506
7396
  const client = await createClient(deps);
7507
7397
  const preflight = await registrationPreflight(project, client);
7508
7398
  let developerPortalPreflight = null;
@@ -7518,7 +7408,7 @@ async function runRegistration(path = ".", options = {}, deps = {}) {
7518
7408
  }
7519
7409
  const receipt = await client.auto_register(project.manifest, project.tool_manual, {
7520
7410
  runtime_validation: project.runtime_validation,
7521
- oauth_credentials: canonicalOauthCredentialsPayload(project.oauth_credentials)
7411
+ oauth_credentials: canonicalOauthCredentials
7522
7412
  });
7523
7413
  const result = {
7524
7414
  receipt: toJsonable(receipt),
@@ -7529,7 +7419,8 @@ async function runRegistration(path = ".", options = {}, deps = {}) {
7529
7419
  if (developerPortalPreflight) {
7530
7420
  result.developer_portal_preflight = developerPortalPreflight;
7531
7421
  }
7532
- if (options.confirm) {
7422
+ const shouldConfirm = Boolean(options.confirm) || options.confirm === void 0 && !options.draft_only && !options.submit_review;
7423
+ if (shouldConfirm) {
7533
7424
  result.confirmation = toJsonable(await client.confirm_registration(receipt.listing_id));
7534
7425
  if (options.submit_review) {
7535
7426
  result.submit_review_skipped = true;
@@ -8060,8 +7951,8 @@ function operationReadmeTemplate(operation, manifest, warning) {
8060
7951
  "siglume score . --remote",
8061
7952
  "siglume preflight .",
8062
7953
  "siglume register .",
8063
- "# inspect the draft, then explicitly approve publish:",
8064
- "siglume register . --confirm",
7954
+ "# review-only staging path:",
7955
+ "siglume register . --draft-only",
8065
7956
  "```",
8066
7957
  ""
8067
7958
  ].join("\n");
@@ -8072,7 +7963,7 @@ function apiUsageDocsTemplate(manifest) {
8072
7963
  const jobToBeDone = String(manifest.job_to_be_done ?? "Describe what this API lets an agent do.");
8073
7964
  const permissionClass = String(manifest.permission_class ?? "read-only");
8074
7965
  const priceModel = String(manifest.price_model ?? "free");
8075
- const requiredAccounts = (manifest.required_connected_accounts ?? []).join(", ") || "none";
7966
+ const requiredAccounts = (manifest.required_connected_accounts ?? []).map((item) => connectedAccountRequirementLabel(item)).filter(Boolean).join(", ") || "none";
8076
7967
  const supportContact = String(manifest.support_contact ?? "replace-with-support-contact");
8077
7968
  return [
8078
7969
  `# ${name} API Usage Guide`,
@@ -8633,8 +8524,8 @@ function readmeTemplate(template) {
8633
8524
  "siglume score . --remote",
8634
8525
  "siglume preflight .",
8635
8526
  "siglume register .",
8636
- "# inspect the draft, then explicitly approve publish:",
8637
- "siglume register . --confirm",
8527
+ "# review-only staging path:",
8528
+ "siglume register . --draft-only",
8638
8529
  "```",
8639
8530
  ""
8640
8531
  ].join("\n");
@@ -8816,16 +8707,25 @@ async function runCli(argv, deps = {}) {
8816
8707
  if (report.runtime_validation_path) emit(stdout, `runtime_validation_path: ${String(report.runtime_validation_path)}`);
8817
8708
  if (report.oauth_credentials_path) emit(stdout, `oauth_credentials_path: ${String(report.oauth_credentials_path)}`);
8818
8709
  });
8819
- program.command("register").option("--confirm", "confirm the draft registration immediately and publish it when the self-serve checks pass", false).option("--submit-review", "legacy alias: publish immediately if your environment still routes through submit-review", false).option("--json", "emit machine-readable JSON", false).argument("[path]", ".", "project path").action(async (path, options) => {
8820
- const report = await runRegistration(path, { confirm: options.confirm, submit_review: options.submitReview }, deps);
8710
+ program.command("register").option("--confirm", "explicitly confirm the registration; this is the default unless --draft-only is set", false).option("--draft-only", "create or refresh the draft without confirming publication", false).option("--submit-review", "legacy alias: publish immediately if your environment still routes through submit-review", false).option("--json", "emit machine-readable JSON", false).argument("[path]", ".", "project path").action(async (path, options) => {
8711
+ const draftOnly = Boolean(options.draftOnly);
8712
+ if (draftOnly && options.confirm) {
8713
+ throw new SiglumeProjectError("--draft-only cannot be combined with --confirm.");
8714
+ }
8715
+ if (draftOnly && options.submitReview) {
8716
+ throw new SiglumeProjectError("--draft-only cannot be combined with --submit-review.");
8717
+ }
8718
+ const shouldConfirm = Boolean(options.confirm) || !draftOnly && !options.submitReview;
8719
+ const report = await runRegistration(path, { confirm: shouldConfirm, draft_only: draftOnly, submit_review: options.submitReview }, deps);
8821
8720
  if (options.json) {
8822
8721
  emit(stdout, renderJson(report));
8823
8722
  } else {
8824
8723
  const receipt = report.receipt;
8825
- if (report.confirmation) {
8826
- emit(stdout, "Listing published.");
8827
- } else if (report.review) {
8828
- emit(stdout, "Listing published via legacy submit-review alias.");
8724
+ const published = Boolean(report.confirmation || report.review);
8725
+ if (published && receipt.registration_mode === "upgrade") {
8726
+ emit(stdout, "Upgrade registered.");
8727
+ } else if (published) {
8728
+ emit(stdout, "Registration accepted.");
8829
8729
  } else if (receipt.registration_mode === "upgrade") {
8830
8730
  emit(stdout, "Upgrade staged.");
8831
8731
  } else if (receipt.registration_mode === "refresh") {
@@ -8842,10 +8742,12 @@ async function runCli(argv, deps = {}) {
8842
8742
  if (receipt.request_id) emit(stdout, `request_id: ${receipt.request_id}`);
8843
8743
  if (report.confirmation) {
8844
8744
  const confirmation = report.confirmation;
8745
+ emit(stdout, "Listing published.");
8845
8746
  if (confirmation.status) emit(stdout, `confirmation_status: ${confirmation.status}`);
8846
8747
  if (confirmation.release?.release_status) emit(stdout, `release_status: ${confirmation.release.release_status}`);
8847
8748
  } else if (report.review) {
8848
8749
  const review = report.review;
8750
+ emit(stdout, "Listing published via legacy submit-review alias.");
8849
8751
  if (review.status) emit(stdout, `publish_status: ${review.status}`);
8850
8752
  }
8851
8753
  const preflight = report.registration_preflight;