@siglume/api-sdk 0.10.7 → 1.0.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.
@@ -1247,6 +1247,56 @@ var init_operations = __esm({
1247
1247
  });
1248
1248
 
1249
1249
  // src/client.ts
1250
+ function validateManifestPersistenceContract(payload) {
1251
+ const vertical = String(payload.store_vertical ?? "").trim().toLowerCase();
1252
+ const persistence = payload.persistence;
1253
+ if (persistence === void 0 || persistence === null) {
1254
+ return;
1255
+ }
1256
+ if (!isRecord(persistence)) {
1257
+ throw new SiglumeClientError("AppManifest.persistence must be an object.");
1258
+ }
1259
+ const mode = String(persistence.mode ?? (vertical === "game" ? "platform" : "none")).trim().toLowerCase();
1260
+ if (!["none", "local", "platform", "developer_server"].includes(mode)) {
1261
+ throw new SiglumeClientError(
1262
+ "AppManifest.persistence.mode must be one of: none, local, platform, developer_server."
1263
+ );
1264
+ }
1265
+ const schema = persistence.save_data_schema;
1266
+ if (vertical === "game" && mode !== "none" && schema === void 0) {
1267
+ throw new SiglumeClientError(
1268
+ "AppManifest.persistence.save_data_schema is required when store_vertical='game' and persistence.mode is not 'none'."
1269
+ );
1270
+ }
1271
+ if (schema !== void 0) {
1272
+ validateSaveDataSchema(schema, "AppManifest.persistence.save_data_schema");
1273
+ }
1274
+ }
1275
+ function validateSaveDataSchema(schema, fieldName) {
1276
+ if (!isRecord(schema)) {
1277
+ throw new SiglumeClientError(`${fieldName} must be a JSON Schema object.`);
1278
+ }
1279
+ const schemaSize = new TextEncoder().encode(JSON.stringify(schema)).length;
1280
+ if (schemaSize > 8192) {
1281
+ throw new SiglumeClientError(`${fieldName} must be at most 8192 bytes.`);
1282
+ }
1283
+ if (schema.type !== "object") {
1284
+ throw new SiglumeClientError(`${fieldName}.type must be 'object'.`);
1285
+ }
1286
+ const properties = schema.properties;
1287
+ if (!isRecord(properties) || Object.keys(properties).length === 0) {
1288
+ throw new SiglumeClientError(`${fieldName}.properties must be a non-empty object.`);
1289
+ }
1290
+ if (schema.required !== void 0) {
1291
+ if (!Array.isArray(schema.required) || !schema.required.every((item) => typeof item === "string")) {
1292
+ throw new SiglumeClientError(`${fieldName}.required must be an array of strings when provided.`);
1293
+ }
1294
+ const missing = schema.required.filter((item) => !(item in properties));
1295
+ if (missing.length > 0) {
1296
+ throw new SiglumeClientError(`${fieldName}.required references undefined properties: ${missing.join(", ")}.`);
1297
+ }
1298
+ }
1299
+ }
1250
1300
  function buildToolManualQualityReport(payload) {
1251
1301
  const qualityBlock = isRecord(payload.quality) ? payload.quality : payload;
1252
1302
  const issues = [];
@@ -1321,6 +1371,8 @@ function buildUrl(baseUrl, path, params) {
1321
1371
  return url.toString();
1322
1372
  }
1323
1373
  function parseListing(data) {
1374
+ const metadata = isRecord(data.metadata) ? data.metadata : {};
1375
+ const persistence = isRecord(data.persistence) ? data.persistence : isRecord(metadata.persistence) ? metadata.persistence : {};
1324
1376
  return {
1325
1377
  listing_id: String(data.listing_id ?? data.id ?? ""),
1326
1378
  capability_key: String(data.capability_key ?? ""),
@@ -1343,14 +1395,59 @@ function parseListing(data) {
1343
1395
  seller_display_name: stringOrNull(data.seller_display_name),
1344
1396
  seller_homepage_url: stringOrNull(data.seller_homepage_url),
1345
1397
  seller_social_url: stringOrNull(data.seller_social_url),
1398
+ publisher_type: stringOrNull(data.publisher_type),
1399
+ publisher_company_id: stringOrNull(data.publisher_company_id),
1400
+ company_id: stringOrNull(data.company_id),
1401
+ company_name: stringOrNull(data.company_name),
1402
+ company_publish_status: stringOrNull(data.company_publish_status),
1403
+ company_terms_version: stringOrNull(data.company_terms_version),
1346
1404
  review_status: stringOrNull(data.review_status),
1347
1405
  review_note: stringOrNull(data.review_note),
1348
1406
  submission_blockers: Array.isArray(data.submission_blockers) ? data.submission_blockers.filter((item) => typeof item === "string") : [],
1407
+ persistence: { ...persistence },
1349
1408
  created_at: stringOrNull(data.created_at),
1350
1409
  updated_at: stringOrNull(data.updated_at),
1351
1410
  raw: { ...data }
1352
1411
  };
1353
1412
  }
1413
+ function parseCompanyPublisher(data) {
1414
+ const wallets = Array.isArray(data.settlement_wallets) ? data.settlement_wallets.filter((item) => isRecord(item)) : [];
1415
+ return {
1416
+ company_id: String(data.company_id ?? data.id ?? ""),
1417
+ name: String(data.name ?? ""),
1418
+ status: String(data.status ?? ""),
1419
+ description: stringOrNull(data.description),
1420
+ is_founder: Boolean(data.is_founder ?? false),
1421
+ membership_role: stringOrNull(data.membership_role),
1422
+ membership_status: stringOrNull(data.membership_status),
1423
+ can_publish: Boolean(data.can_publish ?? true),
1424
+ can_approve: Boolean(data.can_approve ?? false),
1425
+ approval_required: Boolean(data.approval_required ?? false),
1426
+ paid_listing_allowed: Boolean(data.paid_listing_allowed ?? false),
1427
+ disabled_reasons: Array.isArray(data.disabled_reasons) ? data.disabled_reasons.filter((item) => typeof item === "string") : [],
1428
+ company_terms_version: stringOrNull(data.company_terms_version),
1429
+ active_listing_count: Number(data.active_listing_count ?? 0),
1430
+ pending_approval_count: Number(data.pending_approval_count ?? 0),
1431
+ settlement_wallet_ready: Boolean(data.settlement_wallet_ready ?? false),
1432
+ settlement_wallets: wallets.map((item) => ({ ...item })),
1433
+ raw: { ...data }
1434
+ };
1435
+ }
1436
+ function parseCapabilitySaveState(data) {
1437
+ return {
1438
+ capability_key: String(data.capability_key ?? ""),
1439
+ save_key: String(data.save_key ?? ""),
1440
+ schema_version: String(data.schema_version ?? "1"),
1441
+ revision: Number(data.revision ?? 0),
1442
+ payload: toRecord(data.payload),
1443
+ metadata: toRecord(data.metadata),
1444
+ checksum: stringOrNull(data.checksum),
1445
+ updated_at: stringOrNull(data.updated_at),
1446
+ created_at: stringOrNull(data.created_at),
1447
+ exists: Boolean(data.exists ?? false),
1448
+ raw: { ...data }
1449
+ };
1450
+ }
1354
1451
  function parseBundleMember(data) {
1355
1452
  return {
1356
1453
  capability_listing_id: String(data.capability_listing_id ?? ""),
@@ -1362,18 +1459,6 @@ function parseBundleMember(data) {
1362
1459
  link_id: stringOrNull(data.link_id)
1363
1460
  };
1364
1461
  }
1365
- function parseConnectedAccountLifecycle(data) {
1366
- return {
1367
- connected_account_id: String(data.connected_account_id ?? ""),
1368
- provider_key: String(data.provider_key ?? ""),
1369
- expires_at: stringOrNull(data.expires_at),
1370
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((s) => typeof s === "string") : [],
1371
- refreshed_at: stringOrNull(data.refreshed_at),
1372
- connection_status: stringOrNull(data.connection_status),
1373
- provider_revoked: typeof data.provider_revoked === "boolean" ? data.provider_revoked : null,
1374
- revoked_at: stringOrNull(data.revoked_at)
1375
- };
1376
- }
1377
1462
  function parseBundle(data) {
1378
1463
  const membersRaw = Array.isArray(data.members) ? data.members : [];
1379
1464
  return {
@@ -1452,21 +1537,6 @@ function parseBinding(data) {
1452
1537
  raw: { ...data }
1453
1538
  };
1454
1539
  }
1455
- function parseConnectedAccount(data) {
1456
- return {
1457
- connected_account_id: String(data.connected_account_id ?? data.id ?? ""),
1458
- provider_key: String(data.provider_key ?? ""),
1459
- account_role: String(data.account_role ?? ""),
1460
- display_name: stringOrNull(data.display_name),
1461
- environment: stringOrNull(data.environment),
1462
- connection_status: stringOrNull(data.connection_status),
1463
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((item) => typeof item === "string") : [],
1464
- metadata: toRecord(data.metadata),
1465
- created_at: stringOrNull(data.created_at),
1466
- updated_at: stringOrNull(data.updated_at),
1467
- raw: { ...data }
1468
- };
1469
- }
1470
1540
  function parseSupportCase(data) {
1471
1541
  return {
1472
1542
  support_case_id: String(data.support_case_id ?? data.id ?? ""),
@@ -2505,13 +2575,6 @@ var init_client = __esm({
2505
2575
  if (options.runtime_validation) {
2506
2576
  payload.runtime_validation = coerceMapping(options.runtime_validation, "runtime_validation");
2507
2577
  }
2508
- if (options.oauth_credentials) {
2509
- payload.oauth_credentials = Array.isArray(options.oauth_credentials) ? {
2510
- items: options.oauth_credentials.map(
2511
- (item, index) => coerceMapping(item, `oauth_credentials[${index}]`)
2512
- )
2513
- } : coerceMapping(options.oauth_credentials, "oauth_credentials");
2514
- }
2515
2578
  if (options.source_context) {
2516
2579
  payload.source_context = coerceMapping(options.source_context, "source_context");
2517
2580
  }
@@ -2530,6 +2593,9 @@ var init_client = __esm({
2530
2593
  "support_contact",
2531
2594
  "seller_homepage_url",
2532
2595
  "seller_social_url",
2596
+ "publisher_type",
2597
+ "company_id",
2598
+ "publisher_company_id",
2533
2599
  "store_vertical",
2534
2600
  "jurisdiction",
2535
2601
  "price_model",
@@ -2542,7 +2608,8 @@ var init_client = __esm({
2542
2608
  "dry_run_supported",
2543
2609
  "required_connected_accounts",
2544
2610
  "permission_scopes",
2545
- "compatibility_tags"
2611
+ "compatibility_tags",
2612
+ "persistence"
2546
2613
  ]) {
2547
2614
  const value = manifestPayload[fieldName];
2548
2615
  if (value !== void 0 && value !== null) {
@@ -2582,6 +2649,26 @@ var init_client = __esm({
2582
2649
  );
2583
2650
  }
2584
2651
  }
2652
+ const explicitPublisherType = payload.publisher_type !== void 0 && payload.publisher_type !== null;
2653
+ const companyId = String(payload.company_id ?? "").trim() || String(payload.publisher_company_id ?? "").trim();
2654
+ const publisherType = String(payload.publisher_type ?? "user").trim().toLowerCase();
2655
+ if (publisherType !== "user" && publisherType !== "company") {
2656
+ throw new SiglumeClientError("AppManifest.publisher_type must be 'user' or 'company'.");
2657
+ }
2658
+ if (publisherType === "company" && !companyId) {
2659
+ throw new SiglumeClientError("AppManifest.company_id is required when publisher_type='company'.");
2660
+ }
2661
+ if (publisherType === "user" && companyId) {
2662
+ throw new SiglumeClientError("AppManifest.company_id cannot be combined with publisher_type='user'.");
2663
+ }
2664
+ if (explicitPublisherType || companyId) {
2665
+ payload.publisher_type = publisherType;
2666
+ }
2667
+ if (companyId) {
2668
+ payload.company_id = companyId;
2669
+ payload.publisher_company_id = companyId;
2670
+ }
2671
+ validateManifestPersistenceContract(payload);
2585
2672
  if (payload.manifest && typeof payload.manifest === "object") {
2586
2673
  delete payload.manifest.version;
2587
2674
  }
@@ -2617,7 +2704,6 @@ var init_client = __esm({
2617
2704
  auto_manifest: toRecord(data.auto_manifest),
2618
2705
  confidence: toRecord(data.confidence),
2619
2706
  validation_report: toRecord(data.validation_report),
2620
- oauth_status: toRecord(data.oauth_status),
2621
2707
  review_url: stringOrNull(data.review_url),
2622
2708
  trace_id: meta.trace_id,
2623
2709
  request_id: meta.request_id
@@ -2689,6 +2775,45 @@ var init_client = __esm({
2689
2775
  const [data] = await this.request("GET", `/market/capabilities/${listing_id}`);
2690
2776
  return parseListing(data);
2691
2777
  }
2778
+ async list_company_publishers() {
2779
+ const [data] = await this.request("GET", "/market/company-publishers");
2780
+ return Array.isArray(data.items) ? data.items.filter((item) => isRecord(item)).map(parseCompanyPublisher) : [];
2781
+ }
2782
+ async request_company_publish_approval(listing_id, note) {
2783
+ const [data] = await this.request("POST", `/market/capabilities/${listing_id}/company-publish-approval`, {
2784
+ json_body: note ? { note } : {}
2785
+ });
2786
+ return parseListing(data);
2787
+ }
2788
+ async decide_company_publish_approval(listing_id, options) {
2789
+ const [data] = await this.request("POST", `/market/capabilities/${listing_id}/company-publish-approval/decision`, {
2790
+ json_body: {
2791
+ decision: options.decision,
2792
+ ...options.reason ? { reason: options.reason } : {}
2793
+ }
2794
+ });
2795
+ return parseListing(data);
2796
+ }
2797
+ async get_capability_state(capability_key, save_key = "default") {
2798
+ const [data] = await this.request("GET", `/market/capability-state/${capability_key}/${save_key}`);
2799
+ return parseCapabilitySaveState(data);
2800
+ }
2801
+ async put_capability_state(capability_key, save_key = "default", payload = {}, options = {}) {
2802
+ const body = {
2803
+ payload: toRecord(payload),
2804
+ schema_version: options.schema_version ?? "1",
2805
+ metadata: toRecord(options.metadata)
2806
+ };
2807
+ if (options.expected_revision !== void 0 && options.expected_revision !== null) {
2808
+ body.expected_revision = Math.trunc(options.expected_revision);
2809
+ }
2810
+ const [data] = await this.request("PUT", `/market/capability-state/${capability_key}/${save_key}`, { json_body: body });
2811
+ return parseCapabilitySaveState(data);
2812
+ }
2813
+ async delete_capability_state(capability_key, save_key = "default") {
2814
+ const [data] = await this.request("DELETE", `/market/capability-state/${capability_key}/${save_key}`);
2815
+ return parseCapabilitySaveState(data);
2816
+ }
2692
2817
  // ----- Capability bundles (v0.7 track 2) ---------------------------------
2693
2818
  async list_bundles(options = {}) {
2694
2819
  const params = {
@@ -2754,66 +2879,9 @@ var init_client = __esm({
2754
2879
  return parseBundle(data);
2755
2880
  }
2756
2881
  // ----- end bundles -------------------------------------------------------
2757
- // ----- Connected accounts (v0.7 track 3) ---------------------------------
2758
- // `resolve()` is intentionally NOT wrapped: runtime-only, never over the wire.
2759
- async start_connected_account_oauth(input) {
2760
- const body = {
2761
- listing_id: input.listing_id,
2762
- redirect_uri: input.redirect_uri
2763
- };
2764
- if (input.scopes !== void 0) body.scopes = input.scopes;
2765
- if (input.account_role !== void 0) body.account_role = input.account_role;
2766
- const [data] = await this.request("POST", "/me/connected-accounts/oauth/authorize", {
2767
- json_body: body
2768
- });
2769
- return {
2770
- authorize_url: String(data.authorize_url ?? ""),
2771
- state: String(data.state ?? ""),
2772
- provider_key: String(data.provider_key ?? ""),
2773
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((s) => typeof s === "string") : [],
2774
- pkce_method: stringOrNull(data.pkce_method)
2775
- };
2776
- }
2777
- async complete_connected_account_oauth(input) {
2778
- const [data] = await this.request("POST", "/me/connected-accounts/oauth/callback", {
2779
- json_body: { state: input.state, code: input.code }
2780
- });
2781
- return { ...data };
2782
- }
2783
- async refresh_connected_account(account_id) {
2784
- const [data] = await this.request("POST", `/me/connected-accounts/${account_id}/refresh`);
2785
- return parseConnectedAccountLifecycle(data);
2786
- }
2787
- async revoke_connected_account(account_id) {
2788
- const [data] = await this.request("POST", `/me/connected-accounts/${account_id}/revoke`);
2789
- return parseConnectedAccountLifecycle(data);
2790
- }
2791
- async set_listing_oauth_credentials(listing_id, input) {
2792
- const body = {
2793
- provider_key: input.provider_key,
2794
- client_id: input.client_id,
2795
- client_secret: input.client_secret,
2796
- authorize_url: input.authorize_url,
2797
- token_url: input.token_url
2798
- };
2799
- if (input.revoke_url !== void 0) body.revoke_url = input.revoke_url;
2800
- if (input.display_name !== void 0) body.display_name = input.display_name;
2801
- if (input.scope_separator !== void 0) body.scope_separator = input.scope_separator;
2802
- if (input.token_endpoint_auth !== void 0) body.token_endpoint_auth = input.token_endpoint_auth;
2803
- if (input.pkce_required !== void 0) body.pkce_required = input.pkce_required;
2804
- if (input.refresh_supported !== void 0) body.refresh_supported = input.refresh_supported;
2805
- if (input.available_scopes !== void 0) body.available_scopes = input.available_scopes;
2806
- if (input.required_scopes !== void 0) body.required_scopes = input.required_scopes;
2807
- const [data] = await this.request("PUT", `/market/capabilities/${listing_id}/oauth-credentials`, {
2808
- json_body: body
2809
- });
2810
- return { ...data };
2811
- }
2812
- async get_listing_oauth_credentials_status(listing_id) {
2813
- const [data] = await this.request("GET", `/market/capabilities/${listing_id}/oauth-credentials`);
2814
- return { ...data };
2815
- }
2816
- // ----- end connected accounts --------------------------------------------
2882
+ // ----- Connected accounts ------------------------------------------------
2883
+ // Architecture B: publisher APIs own external OAuth and token storage.
2884
+ // The SDK no longer exposes platform OAuth or listing credential APIs.
2817
2885
  async get_developer_portal() {
2818
2886
  const [data, meta] = await this.request("GET", "/market/developer/portal");
2819
2887
  return {
@@ -2846,7 +2914,6 @@ var init_client = __esm({
2846
2914
  dry_run_supported: Boolean(data.dry_run_supported ?? false),
2847
2915
  approval_mode: stringOrNull(data.approval_mode),
2848
2916
  required_connected_accounts: Array.isArray(data.required_connected_accounts) ? data.required_connected_accounts : [],
2849
- connected_accounts: Array.isArray(data.connected_accounts) ? data.connected_accounts.filter((item) => isRecord(item)).map((item) => ({ ...item })) : [],
2850
2917
  stub_providers_enabled: Boolean(data.stub_providers_enabled ?? false),
2851
2918
  simulated_receipts: Boolean(data.simulated_receipts ?? false),
2852
2919
  approval_simulator: Boolean(data.approval_simulator ?? false),
@@ -4144,25 +4211,6 @@ var init_client = __esm({
4144
4211
  raw: { ...data }
4145
4212
  };
4146
4213
  }
4147
- async list_connected_accounts(options = {}) {
4148
- const params = {
4149
- provider_key: options.provider_key,
4150
- environment: options.environment,
4151
- limit: Math.max(1, Math.min(Math.trunc(options.limit ?? 50), 100)),
4152
- cursor: options.cursor
4153
- };
4154
- const [data, meta] = await this.request("GET", "/market/connected-accounts", { params });
4155
- const items = Array.isArray(data.items) ? data.items.filter((item) => isRecord(item)).map(parseConnectedAccount) : [];
4156
- const next_cursor = stringOrNull(data.next_cursor);
4157
- return new CursorPageResult({
4158
- items,
4159
- next_cursor,
4160
- limit: typeof data.limit === "number" ? data.limit : params.limit,
4161
- offset: typeof data.offset === "number" ? data.offset : null,
4162
- meta,
4163
- fetchNext: next_cursor ? (cursor) => this.list_connected_accounts({ ...options, cursor }) : void 0
4164
- });
4165
- }
4166
4214
  async create_support_case(subject, body, options = {}) {
4167
4215
  const summary = subject.trim();
4168
4216
  const details = body.trim();
@@ -6006,24 +6054,12 @@ var AppTestHarness = class {
6006
6054
  this.stubs = stubs;
6007
6055
  }
6008
6056
  async executeWithKind(execution_kind, task_type = "default", options = {}) {
6009
- const connected_accounts = options.connected_accounts ?? Object.fromEntries(
6010
- Object.keys(this.stubs).map((key) => [
6011
- key,
6012
- {
6013
- provider_key: key,
6014
- session_token: `stub-token-${key}`,
6015
- environment: Environment.SANDBOX,
6016
- scopes: []
6017
- }
6018
- ])
6019
- );
6020
6057
  const ctx = {
6021
6058
  agent_id: "test-agent-001",
6022
6059
  owner_user_id: "test-owner-001",
6023
6060
  task_type,
6024
6061
  environment: Environment.SANDBOX,
6025
6062
  execution_kind,
6026
- connected_accounts,
6027
6063
  input_params: options.input_params ?? {},
6028
6064
  trace_id: options.trace_id,
6029
6065
  idempotency_key: options.idempotency_key,
@@ -6113,12 +6149,6 @@ var AppTestHarness = class {
6113
6149
  }
6114
6150
  return issues;
6115
6151
  }
6116
- async simulate_connected_account_missing(task_type = "default", options = {}) {
6117
- return this.executeWithKind("dry_run", task_type, {
6118
- ...options,
6119
- connected_accounts: {}
6120
- });
6121
- }
6122
6152
  async simulate_metering(record, options = {}) {
6123
6153
  const { normalizeUsageRecord: normalizeUsageRecord2 } = await Promise.resolve().then(() => (init_metering(), metering_exports));
6124
6154
  const manifest = await this.app.manifest();
@@ -7081,15 +7111,6 @@ async function loadProject(path = ".") {
7081
7111
  const tool_manual = tool_manual_path ? JSON.parse(await (0, import_promises.readFile)(tool_manual_path, "utf8")) : buildToolManualTemplate(manifest);
7082
7112
  const runtime_validation_path = await findRuntimeValidationPath(root_dir);
7083
7113
  const runtime_validation = runtime_validation_path ? await loadJsonObject(runtime_validation_path, "runtime_validation") : void 0;
7084
- const oauth_credentials_path = await findOauthCredentialsPath(root_dir);
7085
- let oauth_credentials;
7086
- if (oauth_credentials_path) {
7087
- const parsed = JSON.parse(await (0, import_promises.readFile)(oauth_credentials_path, "utf8"));
7088
- if (!isRecord(parsed) && !Array.isArray(parsed)) {
7089
- throw new SiglumeProjectError("oauth_credentials must be a JSON object or array");
7090
- }
7091
- oauth_credentials = parsed;
7092
- }
7093
7114
  return {
7094
7115
  root_dir,
7095
7116
  adapter_path,
@@ -7098,9 +7119,7 @@ async function loadProject(path = ".") {
7098
7119
  tool_manual_path: tool_manual_path ?? void 0,
7099
7120
  tool_manual,
7100
7121
  runtime_validation_path: runtime_validation_path ?? void 0,
7101
- runtime_validation,
7102
- oauth_credentials_path: oauth_credentials_path ?? void 0,
7103
- oauth_credentials
7122
+ runtime_validation
7104
7123
  };
7105
7124
  }
7106
7125
  function isPlatformManagedRequirement(value) {
@@ -7138,6 +7157,21 @@ function requiredOauthProviders(requirements) {
7138
7157
  }
7139
7158
  return providers;
7140
7159
  }
7160
+ function apiManagedRequirementsMissingConnectUrl(requirements) {
7161
+ const missing = [];
7162
+ for (const item of requirements ?? []) {
7163
+ if (!isRecord(item)) continue;
7164
+ const managedBy = String(item.managed_by ?? "").trim().toLowerCase().replaceAll("_", "-");
7165
+ if (managedBy !== "api") continue;
7166
+ const connectUrl = String(item.connect_url ?? "").trim();
7167
+ if (connectUrl) continue;
7168
+ const label = oauthProviderKeyFromRequirement(item) ?? "(missing provider_key)";
7169
+ if (!missing.includes(label)) {
7170
+ missing.push(label);
7171
+ }
7172
+ }
7173
+ return missing;
7174
+ }
7141
7175
  function connectedAccountRequirementLabel(value) {
7142
7176
  if (isRecord(value)) {
7143
7177
  for (const key of ["provider_key", "provider", "account_type", "name"]) {
@@ -7148,102 +7182,6 @@ function connectedAccountRequirementLabel(value) {
7148
7182
  }
7149
7183
  return String(value ?? "").trim();
7150
7184
  }
7151
- function oauthProviderRecordsMap(payload) {
7152
- if (!payload) {
7153
- return {};
7154
- }
7155
- const items = Array.isArray(payload) ? payload : Array.isArray(payload.items) ? payload.items : [payload];
7156
- const resolved = {};
7157
- for (const [index, item] of items.entries()) {
7158
- if (!isRecord(item)) {
7159
- throw new SiglumeProjectError(`oauth_credentials[${index}] must be a JSON object.`);
7160
- }
7161
- const providerKey = oauthProviderKeyFromRequirement(item.provider_key ?? item.provider);
7162
- if (!providerKey) {
7163
- throw new SiglumeProjectError(`oauth_credentials[${index}].provider_key is required.`);
7164
- }
7165
- const authorizeUrl = String(item.authorize_url ?? item.authorization_url ?? item.auth_url ?? "").trim();
7166
- const tokenUrl = String(item.token_url ?? "").trim();
7167
- if (!authorizeUrl || !tokenUrl) {
7168
- throw new SiglumeProjectError(
7169
- `oauth_credentials[${index}] must include authorize_url and token_url.`
7170
- );
7171
- }
7172
- for (const [urlKey, urlValue] of Object.entries({
7173
- authorize_url: authorizeUrl,
7174
- token_url: tokenUrl,
7175
- revoke_url: String(item.revoke_url ?? "").trim()
7176
- })) {
7177
- if (urlValue && !urlValue.startsWith("https://")) {
7178
- throw new SiglumeProjectError(`oauth_credentials[${index}].${urlKey} must be an https URL.`);
7179
- }
7180
- }
7181
- const clientId = String(item.client_id ?? "").trim();
7182
- const clientSecret = String(item.client_secret ?? "").trim();
7183
- if (!clientId || !clientSecret) {
7184
- throw new SiglumeProjectError(`oauth_credentials[${index}] must include client_id and client_secret.`);
7185
- }
7186
- const rawScopes = item.required_scopes ?? item.scopes;
7187
- let scopes = [];
7188
- if (rawScopes == null) {
7189
- scopes = [];
7190
- } else if (!Array.isArray(rawScopes)) {
7191
- throw new SiglumeProjectError(`oauth_credentials[${index}].required_scopes must be a JSON array.`);
7192
- } else {
7193
- scopes = rawScopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7194
- }
7195
- const record = {
7196
- provider_key: providerKey,
7197
- client_id: clientId,
7198
- client_secret: clientSecret,
7199
- required_scopes: scopes
7200
- };
7201
- for (const [key, value] of Object.entries({
7202
- authorize_url: authorizeUrl,
7203
- token_url: tokenUrl,
7204
- revoke_url: String(item.revoke_url ?? "").trim(),
7205
- display_name: String(item.display_name ?? "").trim(),
7206
- scope_separator: String(item.scope_separator ?? "").trim(),
7207
- token_endpoint_auth: String(item.token_endpoint_auth ?? "").trim()
7208
- })) {
7209
- if (value) record[key] = value;
7210
- }
7211
- for (const key of ["pkce_required", "refresh_supported"]) {
7212
- if (typeof item[key] === "boolean") record[key] = item[key];
7213
- }
7214
- if (Array.isArray(item.available_scopes)) {
7215
- const availableScopes = item.available_scopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7216
- if (availableScopes.length > 0) record.available_scopes = availableScopes;
7217
- }
7218
- resolved[providerKey] = record;
7219
- }
7220
- return resolved;
7221
- }
7222
- function canonicalOauthCredentialsPayload(payload) {
7223
- const records = oauthProviderRecordsMap(payload);
7224
- const providerKeys = Object.keys(records).sort();
7225
- if (providerKeys.length === 0) {
7226
- return void 0;
7227
- }
7228
- return {
7229
- items: providerKeys.map((providerKey) => records[providerKey])
7230
- };
7231
- }
7232
- function ensureRequiredOauthCredentials(project) {
7233
- const requiredProviders = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7234
- if (requiredProviders.length === 0) {
7235
- return;
7236
- }
7237
- const provided = new Set(Object.keys(oauthProviderRecordsMap(project.oauth_credentials)));
7238
- const missing = requiredProviders.filter((provider) => !provided.has(provider));
7239
- if (missing.length === 0) {
7240
- return;
7241
- }
7242
- const path = project.oauth_credentials_path ?? (0, import_node_path.join)(project.root_dir, "oauth_credentials.json");
7243
- throw new SiglumeProjectError(
7244
- `${path} is required for platform-managed OAuth APIs. Missing provider seeds: ${missing.join(", ")}`
7245
- );
7246
- }
7247
7185
  async function validateProject(path = ".", deps = {}) {
7248
7186
  const project = await loadProject(path);
7249
7187
  const manifest_issues = await projectValidationIssues(project);
@@ -7283,7 +7221,12 @@ function ensureManifestPublisherIdentity(project) {
7283
7221
  const sellerHomepageUrl = String(manifestPayload.seller_homepage_url ?? "").trim();
7284
7222
  const sellerSocialUrl = String(manifestPayload.seller_social_url ?? "").trim();
7285
7223
  const jurisdiction = String(manifestPayload.jurisdiction ?? "").trim();
7224
+ const companyId = String(manifestPayload.company_id ?? "").trim() || String(manifestPayload.publisher_company_id ?? "").trim();
7225
+ const publisherType = String(manifestPayload.publisher_type ?? "user").trim().toLowerCase();
7286
7226
  const issues = [];
7227
+ if (companyId && publisherType !== "company") {
7228
+ issues.push('manifest.company_id requires manifest.publisher_type to be "company"');
7229
+ }
7287
7230
  if (!docsUrl) {
7288
7231
  issues.push("manifest.docs_url is required");
7289
7232
  } else if (looksLikePlaceholder(docsUrl)) {
@@ -7393,10 +7336,21 @@ function ensureExplicitToolManual(project) {
7393
7336
  async function registrationPreflight(project, client) {
7394
7337
  const manifestIssues = await projectValidationIssues(project);
7395
7338
  const [toolManualValid, toolManualIssues] = validate_tool_manual(project.tool_manual);
7339
+ const retiredPlatformOauthProviders = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7340
+ if (retiredPlatformOauthProviders.length > 0) {
7341
+ throw new SiglumeProjectError(
7342
+ `Registration preflight failed. Fix these before calling auto-register:
7343
+ - platform-managed OAuth is retired. Use managed_by="api" with connect_url: ${retiredPlatformOauthProviders.join(", ")}`
7344
+ );
7345
+ }
7346
+ const apiManagedMissingConnectUrl = apiManagedRequirementsMissingConnectUrl(project.manifest.required_connected_accounts ?? []);
7347
+ if (apiManagedMissingConnectUrl.length > 0) {
7348
+ throw new SiglumeProjectError(
7349
+ `Registration preflight failed. Fix these before calling auto-register:
7350
+ - API-managed OAuth requirements must include connect_url: ${apiManagedMissingConnectUrl.join(", ")}`
7351
+ );
7352
+ }
7396
7353
  const remoteQuality = await client.preview_quality_score(project.tool_manual);
7397
- const requiredOauthProvidersList = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7398
- const oauthProviderRecords = oauthProviderRecordsMap(project.oauth_credentials);
7399
- const missingOauthProviders = requiredOauthProvidersList.filter((provider) => !oauthProviderRecords[provider]);
7400
7354
  const blockingToolManualIssues = toolManualIssues.filter((issue2) => issue2.severity === "error");
7401
7355
  const errors = [
7402
7356
  ...manifestIssues.map((issue2) => String(issue2)),
@@ -7408,17 +7362,12 @@ async function registrationPreflight(project, client) {
7408
7362
  if (!remoteQualityOk(remoteQuality)) {
7409
7363
  errors.push(`remote Tool Manual quality is not publishable: ${remoteQuality.grade} (${remoteQuality.overall_score}/100)`);
7410
7364
  }
7411
- if (missingOauthProviders.length > 0) {
7412
- errors.push(`oauth_credentials.json is required for platform-managed OAuth APIs: ${missingOauthProviders.join(", ")}`);
7413
- }
7414
7365
  const preflight = {
7415
7366
  manifest_issues: manifestIssues,
7416
7367
  tool_manual_valid: toolManualValid,
7417
7368
  tool_manual_issues: toolManualIssues.map((issue2) => toJsonable(issue2)),
7418
7369
  remote_quality: toJsonable(remoteQuality),
7419
- required_oauth_providers: requiredOauthProvidersList,
7420
- oauth_credentials_path: project.oauth_credentials_path ?? null,
7421
- oauth_missing_providers: missingOauthProviders,
7370
+ retired_platform_oauth_providers: retiredPlatformOauthProviders,
7422
7371
  ok: errors.length === 0
7423
7372
  };
7424
7373
  if (errors.length > 0) {
@@ -7429,35 +7378,116 @@ ${errors.map((error) => `- ${error}`).join("\n")}`
7429
7378
  }
7430
7379
  return preflight;
7431
7380
  }
7381
+ function companyNameSlug(value) {
7382
+ return value.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7383
+ }
7432
7384
  async function runRegistration(path = ".", options = {}, deps = {}) {
7433
7385
  const project = await loadProject(path);
7386
+ let requestedCompanyId = String(options.company_id ?? "").trim();
7387
+ const requestedCompanySlug = String(options.company_slug ?? "").trim();
7388
+ let companyPublisherCandidates = null;
7389
+ if (requestedCompanySlug) {
7390
+ if (requestedCompanyId) {
7391
+ throw new SiglumeProjectError("--company and --company-slug cannot be combined.");
7392
+ }
7393
+ const slug = companyNameSlug(requestedCompanySlug);
7394
+ if (!slug && requestedCompanySlug !== requestedCompanyId) {
7395
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not slug-compatible; use --company <company_id> instead.`);
7396
+ }
7397
+ }
7398
+ if (requestedCompanyId) {
7399
+ project.manifest = {
7400
+ ...project.manifest,
7401
+ publisher_type: "company",
7402
+ company_id: requestedCompanyId,
7403
+ publisher_company_id: requestedCompanyId
7404
+ };
7405
+ }
7434
7406
  ensureExplicitToolManual(project);
7435
7407
  ensureManifestPublisherIdentity(project);
7436
7408
  ensureRuntimeValidationReady(project);
7437
- ensureRequiredOauthCredentials(project);
7438
- const canonicalOauthCredentials = canonicalOauthCredentialsPayload(project.oauth_credentials);
7439
7409
  const client = await createClient(deps);
7410
+ if (requestedCompanySlug) {
7411
+ const slug = companyNameSlug(requestedCompanySlug);
7412
+ companyPublisherCandidates = await client.list_company_publishers();
7413
+ const matches = companyPublisherCandidates.filter(
7414
+ (item) => companyNameSlug(item.name || item.company_id) === slug || item.company_id === requestedCompanySlug
7415
+ );
7416
+ if (matches.length === 0) {
7417
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not available to this API key.`);
7418
+ }
7419
+ if (matches.length > 1) {
7420
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is ambiguous; use --company <company_id> instead.`);
7421
+ }
7422
+ const match = matches[0];
7423
+ if (!match) {
7424
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not available to this API key.`);
7425
+ }
7426
+ if (match.can_publish === false) {
7427
+ const disabledReasons = match.disabled_reasons ?? [];
7428
+ const reasons = disabledReasons.length > 0 ? disabledReasons.join(", ") : "company publisher is disabled";
7429
+ throw new SiglumeProjectError(`Company ${match.company_id} cannot publish: ${reasons}.`);
7430
+ }
7431
+ requestedCompanyId = match.company_id;
7432
+ project.manifest = {
7433
+ ...project.manifest,
7434
+ publisher_type: "company",
7435
+ company_id: requestedCompanyId,
7436
+ publisher_company_id: requestedCompanyId
7437
+ };
7438
+ }
7440
7439
  const preflight = await registrationPreflight(project, client);
7440
+ let companyPublisherPreflight = null;
7441
+ const companyId = String(project.manifest.company_id ?? "").trim() || String(project.manifest.publisher_company_id ?? "").trim();
7442
+ const publisherType = String(project.manifest.publisher_type ?? "user").toLowerCase();
7443
+ if (publisherType === "company") {
7444
+ if (!companyId) {
7445
+ throw new SiglumeProjectError("Company registration requires --company <company_id> or manifest.company_id.");
7446
+ }
7447
+ const companies = companyPublisherCandidates ?? await client.list_company_publishers();
7448
+ companyPublisherCandidates = companies;
7449
+ const company = companies.find((item) => item.company_id === companyId);
7450
+ if (!company) {
7451
+ throw new SiglumeProjectError(`Company ${companyId} is not available to this API key.`);
7452
+ }
7453
+ if (company.can_publish === false) {
7454
+ const disabledReasons = company.disabled_reasons ?? [];
7455
+ const reasons = disabledReasons.length > 0 ? disabledReasons.join(", ") : "company publisher is disabled";
7456
+ throw new SiglumeProjectError(`Company ${companyId} cannot publish: ${reasons}.`);
7457
+ }
7458
+ companyPublisherPreflight = company;
7459
+ }
7441
7460
  let developerPortalPreflight = null;
7442
7461
  if (String(project.manifest.price_model ?? "free").toLowerCase() !== "free") {
7443
- const portal = await client.get_developer_portal();
7444
- const verifiedDestination = portal.payout_readiness?.verified_destination;
7445
- if (verifiedDestination !== true) {
7446
- throw new SiglumeProjectError(
7447
- "Paid API registration requires a verified Polygon payout destination. Open https://siglume.com/owner/credits/payout and confirm the embedded-wallet payout token, or call GET /v1/market/developer/portal until payout_readiness.verified_destination is true."
7448
- );
7462
+ if (publisherType === "company") {
7463
+ const company = companyPublisherPreflight;
7464
+ if (!company) {
7465
+ throw new SiglumeProjectError(`Company ${companyId} is not available to this API key.`);
7466
+ }
7467
+ if (company.settlement_wallet_ready !== true) {
7468
+ throw new SiglumeProjectError(
7469
+ `Paid company registration requires a verified company settlement wallet for ${company.name}. Open the company settings and complete settlement readiness before registering.`
7470
+ );
7471
+ }
7472
+ developerPortalPreflight = { company_publisher: toJsonable(company) };
7473
+ } else {
7474
+ const portal = await client.get_developer_portal();
7475
+ const verifiedDestination = portal.payout_readiness?.verified_destination;
7476
+ if (verifiedDestination !== true) {
7477
+ throw new SiglumeProjectError(
7478
+ "Paid API registration requires a verified Polygon payout destination. Open https://siglume.com/owner/credits/payout and confirm the embedded-wallet payout token, or call GET /v1/market/developer/portal until payout_readiness.verified_destination is true."
7479
+ );
7480
+ }
7481
+ developerPortalPreflight = toJsonable(portal);
7449
7482
  }
7450
- developerPortalPreflight = toJsonable(portal);
7451
7483
  }
7452
7484
  const receipt = await client.auto_register(project.manifest, project.tool_manual, {
7453
- runtime_validation: project.runtime_validation,
7454
- oauth_credentials: canonicalOauthCredentials
7485
+ runtime_validation: project.runtime_validation
7455
7486
  });
7456
7487
  const result = {
7457
7488
  receipt: toJsonable(receipt),
7458
7489
  registration_preflight: preflight,
7459
- runtime_validation_path: project.runtime_validation_path ?? null,
7460
- oauth_credentials_path: project.oauth_credentials_path ?? null
7490
+ runtime_validation_path: project.runtime_validation_path ?? null
7461
7491
  };
7462
7492
  if (developerPortalPreflight) {
7463
7493
  result.developer_portal_preflight = developerPortalPreflight;
@@ -7478,7 +7508,6 @@ async function runPreflight(path = ".", deps = {}) {
7478
7508
  ensureExplicitToolManual(project);
7479
7509
  ensureManifestPublisherIdentity(project);
7480
7510
  ensureRuntimeValidationReady(project);
7481
- ensureRequiredOauthCredentials(project);
7482
7511
  const client = await createClient(deps);
7483
7512
  const preflight = await registrationPreflight(project, client);
7484
7513
  let developerPortalPreflight = null;
@@ -7496,8 +7525,7 @@ async function runPreflight(path = ".", deps = {}) {
7496
7525
  ok: true,
7497
7526
  adapter_path: project.adapter_path,
7498
7527
  registration_preflight: preflight,
7499
- runtime_validation_path: project.runtime_validation_path ?? null,
7500
- oauth_credentials_path: project.oauth_credentials_path ?? null
7528
+ runtime_validation_path: project.runtime_validation_path ?? null
7501
7529
  };
7502
7530
  if (developerPortalPreflight) {
7503
7531
  result.developer_portal_preflight = developerPortalPreflight;
@@ -7520,6 +7548,14 @@ async function getUsageReport(options, deps = {}) {
7520
7548
  count: items.length
7521
7549
  };
7522
7550
  }
7551
+ async function listCompanyPublishersReport(deps = {}) {
7552
+ const client = await createClient(deps);
7553
+ const companies = await client.list_company_publishers();
7554
+ return {
7555
+ companies: companies.map((item) => toJsonable(item)),
7556
+ count: companies.length
7557
+ };
7558
+ }
7523
7559
  async function diffJsonFiles(oldPath, newPath) {
7524
7560
  const oldPayload = await loadJsonDocument(oldPath);
7525
7561
  const newPayload = await loadJsonDocument(newPath);
@@ -7971,7 +8007,7 @@ function operationReadmeTemplate(operation, manifest, warning) {
7971
8007
  "- `tool_manual.json`: machine-generated ToolManual scaffold",
7972
8008
  "- `runtime_validation.json`: local public endpoint and review-key checks used by auto-register",
7973
8009
  "- `docs/api-usage.md`: publishable API usage guide template for `docs_url`",
7974
- "- `.gitignore`: keeps runtime review keys and OAuth client secrets out of Git",
8010
+ "- `.gitignore`: keeps runtime review keys out of Git",
7975
8011
  "- `tests/test_adapter.ts`: smoke test for `AppTestHarness`",
7976
8012
  "",
7977
8013
  "Before registering, replace all generated placeholders:",
@@ -7979,8 +8015,8 @@ function operationReadmeTemplate(operation, manifest, warning) {
7979
8015
  "- Replace `support_contact` with a real support email address or public support URL.",
7980
8016
  "- Optional `seller_homepage_url` is the seller's official site and can stay blank.",
7981
8017
  "- In the local `runtime_validation.json`, replace the public URL and review-key placeholders.",
7982
- "- If the API uses seller-side OAuth, create a local `oauth_credentials.json` next to the adapter.",
7983
- "- Do not commit real review keys or OAuth client secrets; the generated `.gitignore` excludes those files.",
8018
+ "- If the API uses external OAuth, implement that flow in your API runtime and keep user tokens outside Siglume.",
8019
+ "- Do not commit real review keys or external-provider secrets; the generated `.gitignore` excludes local secret files.",
7984
8020
  "- Because `runtime_validation.json` is ignored, GitHub samples do not commit review-key values.",
7985
8021
  "",
7986
8022
  "## Commands",
@@ -8056,8 +8092,6 @@ function generatedGitignore() {
8056
8092
  "!.env.example",
8057
8093
  "runtime_validation.json",
8058
8094
  "runtime-validation.json",
8059
- "oauth_credentials.json",
8060
- "oauth-credentials.json",
8061
8095
  "",
8062
8096
  "# Python / test artifacts.",
8063
8097
  "__pycache__/",
@@ -8203,13 +8237,6 @@ async function runHarnessForProject(project) {
8203
8237
  checks.push(executionCheck("quote", await harness.execute_quote(task_type, { input_params: sample_input }), harness));
8204
8238
  checks.push(executionCheck("payment", await harness.execute_payment(task_type, { input_params: sample_input }), harness));
8205
8239
  }
8206
- checks.push(
8207
- executionCheck(
8208
- "missing_account_simulation",
8209
- await harness.simulate_connected_account_missing(task_type, { input_params: sample_input }),
8210
- harness
8211
- )
8212
- );
8213
8240
  return {
8214
8241
  adapter_path: project.adapter_path,
8215
8242
  task_type,
@@ -8312,15 +8339,6 @@ async function findRuntimeValidationPath(root_dir) {
8312
8339
  }
8313
8340
  return null;
8314
8341
  }
8315
- async function findOauthCredentialsPath(root_dir) {
8316
- for (const name of ["oauth_credentials.json", "oauth-credentials.json"]) {
8317
- const candidate = (0, import_node_path.join)(root_dir, name);
8318
- if ((0, import_node_fs.existsSync)(candidate)) {
8319
- return candidate;
8320
- }
8321
- }
8322
- return null;
8323
- }
8324
8342
  async function loadJsonObject(path, label) {
8325
8343
  let payload;
8326
8344
  try {
@@ -8552,15 +8570,15 @@ function readmeTemplate(template) {
8552
8570
  "- `tool_manual.json`: editable ToolManual draft for validation and registration",
8553
8571
  "- `runtime_validation.json`: local live API smoke-test contract used during registration",
8554
8572
  "- `docs/api-usage.md`: publish this page and use its public URL as `docs_url`",
8555
- "- `.gitignore`: keeps runtime review keys and OAuth client secrets out of Git",
8573
+ "- `.gitignore`: keeps runtime review keys out of Git",
8556
8574
  "",
8557
8575
  "Before registering, replace all generated placeholders:",
8558
8576
  "- In `adapter.ts` and `manifest.json`, replace `docs_url` with a dedicated public API usage guide, not a homepage.",
8559
8577
  "- Replace `support_contact` with a real support email address or public support URL.",
8560
8578
  "- Optional `seller_homepage_url` is the seller's official site and can stay blank.",
8561
8579
  "- In the local `runtime_validation.json`, replace the public URL and review-key placeholders.",
8562
- "- If the API uses seller-side OAuth, create a local `oauth_credentials.json` next to the adapter.",
8563
- "- Do not commit real review keys or OAuth client secrets; the generated `.gitignore` excludes those files.",
8580
+ "- If the API uses external OAuth, implement that flow in your API runtime and keep user tokens outside Siglume.",
8581
+ "- Do not commit real review keys or external-provider secrets; the generated `.gitignore` excludes local secret files.",
8564
8582
  "- Because `runtime_validation.json` is ignored, GitHub samples do not commit review-key values.",
8565
8583
  "",
8566
8584
  "Suggested workflow:",
@@ -8606,6 +8624,24 @@ function renderOperationTable(operations) {
8606
8624
  ...rows.map((row) => row.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join(" "))
8607
8625
  ];
8608
8626
  }
8627
+ function renderCompanyTable(companies) {
8628
+ const rows = companies.map((item) => [
8629
+ String(item.company_id ?? item.id ?? ""),
8630
+ String(item.name ?? ""),
8631
+ String(item.membership_role ?? (item.is_founder ? "founder" : "")),
8632
+ String(item.settlement_wallet_ready === true ? "ready" : "not_ready"),
8633
+ String(item.pending_approval_count ?? 0)
8634
+ ]);
8635
+ const headers = ["company_id", "name", "role", "settlement", "pending"];
8636
+ const widths = headers.map(
8637
+ (header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0))
8638
+ );
8639
+ return [
8640
+ headers.map((header, index) => header.padEnd(widths[index] ?? header.length)).join(" "),
8641
+ widths.map((width) => "-".repeat(width)).join(" "),
8642
+ ...rows.map((row) => row.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join(" "))
8643
+ ];
8644
+ }
8609
8645
  async function runCli(argv, deps = {}) {
8610
8646
  const stdout = deps.stdout;
8611
8647
  const stderr = deps.stderr ?? console.error;
@@ -8760,9 +8796,22 @@ async function runCli(argv, deps = {}) {
8760
8796
  emit(stdout, `preflight_quality: ${preflight.remote_quality.grade} (${preflight.remote_quality.overall_score}/100)`);
8761
8797
  }
8762
8798
  if (report.runtime_validation_path) emit(stdout, `runtime_validation_path: ${String(report.runtime_validation_path)}`);
8763
- if (report.oauth_credentials_path) emit(stdout, `oauth_credentials_path: ${String(report.oauth_credentials_path)}`);
8764
8799
  });
8765
- 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) => {
8800
+ program.command("companies").description("List Siglume companies available for company-name publishing.").option("--json", "emit machine-readable JSON", false).action(async (options) => {
8801
+ const report = await listCompanyPublishersReport(deps);
8802
+ if (options.json) {
8803
+ emit(stdout, renderJson(report));
8804
+ return;
8805
+ }
8806
+ const companies = Array.isArray(report.companies) ? report.companies.filter((item) => Boolean(item && typeof item === "object")) : [];
8807
+ if (companies.length === 0) {
8808
+ emit(stdout, "No company publishers available for this API key.");
8809
+ return;
8810
+ }
8811
+ emit(stdout, "Company publishers");
8812
+ renderCompanyTable(companies).forEach((line) => emit(stdout, line));
8813
+ });
8814
+ 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("--company <companyId>", "publish under a Siglume company name; revenue is split equally among active members", "").option("--company-slug <slug>", "publish under a Siglume company by matching the slugified company name", "").option("--json", "emit machine-readable JSON", false).argument("[path]", ".", "project path").action(async (path, options) => {
8766
8815
  const draftOnly = Boolean(options.draftOnly);
8767
8816
  if (draftOnly && options.confirm) {
8768
8817
  throw new SiglumeProjectError("--draft-only cannot be combined with --confirm.");
@@ -8771,7 +8820,13 @@ async function runCli(argv, deps = {}) {
8771
8820
  throw new SiglumeProjectError("--draft-only cannot be combined with --submit-review.");
8772
8821
  }
8773
8822
  const shouldConfirm = Boolean(options.confirm) || !draftOnly && !options.submitReview;
8774
- const report = await runRegistration(path, { confirm: shouldConfirm, draft_only: draftOnly, submit_review: options.submitReview }, deps);
8823
+ const report = await runRegistration(path, {
8824
+ confirm: shouldConfirm,
8825
+ draft_only: draftOnly,
8826
+ submit_review: options.submitReview,
8827
+ company_id: options.company,
8828
+ company_slug: options.companySlug
8829
+ }, deps);
8775
8830
  if (options.json) {
8776
8831
  emit(stdout, renderJson(report));
8777
8832
  } else {
@@ -8791,7 +8846,6 @@ async function runCli(argv, deps = {}) {
8791
8846
  emit(stdout, `listing_id: ${receipt.listing_id}`);
8792
8847
  emit(stdout, `receipt_status: ${receipt.status}`);
8793
8848
  if (receipt.listing_status) emit(stdout, `listing_status: ${receipt.listing_status}`);
8794
- if (receipt.oauth_status) emit(stdout, `oauth_configured: ${Boolean(receipt.oauth_status.configured)}`);
8795
8849
  if (receipt.review_url) emit(stdout, `review_url: ${receipt.review_url}`);
8796
8850
  if (receipt.trace_id) emit(stdout, `trace_id: ${receipt.trace_id}`);
8797
8851
  if (receipt.request_id) emit(stdout, `request_id: ${receipt.request_id}`);