@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.
@@ -1248,6 +1248,56 @@ var init_operations = __esm({
1248
1248
  });
1249
1249
 
1250
1250
  // src/client.ts
1251
+ function validateManifestPersistenceContract(payload) {
1252
+ const vertical = String(payload.store_vertical ?? "").trim().toLowerCase();
1253
+ const persistence = payload.persistence;
1254
+ if (persistence === void 0 || persistence === null) {
1255
+ return;
1256
+ }
1257
+ if (!isRecord(persistence)) {
1258
+ throw new SiglumeClientError("AppManifest.persistence must be an object.");
1259
+ }
1260
+ const mode = String(persistence.mode ?? (vertical === "game" ? "platform" : "none")).trim().toLowerCase();
1261
+ if (!["none", "local", "platform", "developer_server"].includes(mode)) {
1262
+ throw new SiglumeClientError(
1263
+ "AppManifest.persistence.mode must be one of: none, local, platform, developer_server."
1264
+ );
1265
+ }
1266
+ const schema = persistence.save_data_schema;
1267
+ if (vertical === "game" && mode !== "none" && schema === void 0) {
1268
+ throw new SiglumeClientError(
1269
+ "AppManifest.persistence.save_data_schema is required when store_vertical='game' and persistence.mode is not 'none'."
1270
+ );
1271
+ }
1272
+ if (schema !== void 0) {
1273
+ validateSaveDataSchema(schema, "AppManifest.persistence.save_data_schema");
1274
+ }
1275
+ }
1276
+ function validateSaveDataSchema(schema, fieldName) {
1277
+ if (!isRecord(schema)) {
1278
+ throw new SiglumeClientError(`${fieldName} must be a JSON Schema object.`);
1279
+ }
1280
+ const schemaSize = new TextEncoder().encode(JSON.stringify(schema)).length;
1281
+ if (schemaSize > 8192) {
1282
+ throw new SiglumeClientError(`${fieldName} must be at most 8192 bytes.`);
1283
+ }
1284
+ if (schema.type !== "object") {
1285
+ throw new SiglumeClientError(`${fieldName}.type must be 'object'.`);
1286
+ }
1287
+ const properties = schema.properties;
1288
+ if (!isRecord(properties) || Object.keys(properties).length === 0) {
1289
+ throw new SiglumeClientError(`${fieldName}.properties must be a non-empty object.`);
1290
+ }
1291
+ if (schema.required !== void 0) {
1292
+ if (!Array.isArray(schema.required) || !schema.required.every((item) => typeof item === "string")) {
1293
+ throw new SiglumeClientError(`${fieldName}.required must be an array of strings when provided.`);
1294
+ }
1295
+ const missing = schema.required.filter((item) => !(item in properties));
1296
+ if (missing.length > 0) {
1297
+ throw new SiglumeClientError(`${fieldName}.required references undefined properties: ${missing.join(", ")}.`);
1298
+ }
1299
+ }
1300
+ }
1251
1301
  function buildToolManualQualityReport(payload) {
1252
1302
  const qualityBlock = isRecord(payload.quality) ? payload.quality : payload;
1253
1303
  const issues = [];
@@ -1322,6 +1372,8 @@ function buildUrl(baseUrl, path, params) {
1322
1372
  return url.toString();
1323
1373
  }
1324
1374
  function parseListing(data) {
1375
+ const metadata = isRecord(data.metadata) ? data.metadata : {};
1376
+ const persistence = isRecord(data.persistence) ? data.persistence : isRecord(metadata.persistence) ? metadata.persistence : {};
1325
1377
  return {
1326
1378
  listing_id: String(data.listing_id ?? data.id ?? ""),
1327
1379
  capability_key: String(data.capability_key ?? ""),
@@ -1344,14 +1396,59 @@ function parseListing(data) {
1344
1396
  seller_display_name: stringOrNull(data.seller_display_name),
1345
1397
  seller_homepage_url: stringOrNull(data.seller_homepage_url),
1346
1398
  seller_social_url: stringOrNull(data.seller_social_url),
1399
+ publisher_type: stringOrNull(data.publisher_type),
1400
+ publisher_company_id: stringOrNull(data.publisher_company_id),
1401
+ company_id: stringOrNull(data.company_id),
1402
+ company_name: stringOrNull(data.company_name),
1403
+ company_publish_status: stringOrNull(data.company_publish_status),
1404
+ company_terms_version: stringOrNull(data.company_terms_version),
1347
1405
  review_status: stringOrNull(data.review_status),
1348
1406
  review_note: stringOrNull(data.review_note),
1349
1407
  submission_blockers: Array.isArray(data.submission_blockers) ? data.submission_blockers.filter((item) => typeof item === "string") : [],
1408
+ persistence: { ...persistence },
1350
1409
  created_at: stringOrNull(data.created_at),
1351
1410
  updated_at: stringOrNull(data.updated_at),
1352
1411
  raw: { ...data }
1353
1412
  };
1354
1413
  }
1414
+ function parseCompanyPublisher(data) {
1415
+ const wallets = Array.isArray(data.settlement_wallets) ? data.settlement_wallets.filter((item) => isRecord(item)) : [];
1416
+ return {
1417
+ company_id: String(data.company_id ?? data.id ?? ""),
1418
+ name: String(data.name ?? ""),
1419
+ status: String(data.status ?? ""),
1420
+ description: stringOrNull(data.description),
1421
+ is_founder: Boolean(data.is_founder ?? false),
1422
+ membership_role: stringOrNull(data.membership_role),
1423
+ membership_status: stringOrNull(data.membership_status),
1424
+ can_publish: Boolean(data.can_publish ?? true),
1425
+ can_approve: Boolean(data.can_approve ?? false),
1426
+ approval_required: Boolean(data.approval_required ?? false),
1427
+ paid_listing_allowed: Boolean(data.paid_listing_allowed ?? false),
1428
+ disabled_reasons: Array.isArray(data.disabled_reasons) ? data.disabled_reasons.filter((item) => typeof item === "string") : [],
1429
+ company_terms_version: stringOrNull(data.company_terms_version),
1430
+ active_listing_count: Number(data.active_listing_count ?? 0),
1431
+ pending_approval_count: Number(data.pending_approval_count ?? 0),
1432
+ settlement_wallet_ready: Boolean(data.settlement_wallet_ready ?? false),
1433
+ settlement_wallets: wallets.map((item) => ({ ...item })),
1434
+ raw: { ...data }
1435
+ };
1436
+ }
1437
+ function parseCapabilitySaveState(data) {
1438
+ return {
1439
+ capability_key: String(data.capability_key ?? ""),
1440
+ save_key: String(data.save_key ?? ""),
1441
+ schema_version: String(data.schema_version ?? "1"),
1442
+ revision: Number(data.revision ?? 0),
1443
+ payload: toRecord(data.payload),
1444
+ metadata: toRecord(data.metadata),
1445
+ checksum: stringOrNull(data.checksum),
1446
+ updated_at: stringOrNull(data.updated_at),
1447
+ created_at: stringOrNull(data.created_at),
1448
+ exists: Boolean(data.exists ?? false),
1449
+ raw: { ...data }
1450
+ };
1451
+ }
1355
1452
  function parseBundleMember(data) {
1356
1453
  return {
1357
1454
  capability_listing_id: String(data.capability_listing_id ?? ""),
@@ -1363,18 +1460,6 @@ function parseBundleMember(data) {
1363
1460
  link_id: stringOrNull(data.link_id)
1364
1461
  };
1365
1462
  }
1366
- function parseConnectedAccountLifecycle(data) {
1367
- return {
1368
- connected_account_id: String(data.connected_account_id ?? ""),
1369
- provider_key: String(data.provider_key ?? ""),
1370
- expires_at: stringOrNull(data.expires_at),
1371
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((s) => typeof s === "string") : [],
1372
- refreshed_at: stringOrNull(data.refreshed_at),
1373
- connection_status: stringOrNull(data.connection_status),
1374
- provider_revoked: typeof data.provider_revoked === "boolean" ? data.provider_revoked : null,
1375
- revoked_at: stringOrNull(data.revoked_at)
1376
- };
1377
- }
1378
1463
  function parseBundle(data) {
1379
1464
  const membersRaw = Array.isArray(data.members) ? data.members : [];
1380
1465
  return {
@@ -1453,21 +1538,6 @@ function parseBinding(data) {
1453
1538
  raw: { ...data }
1454
1539
  };
1455
1540
  }
1456
- function parseConnectedAccount(data) {
1457
- return {
1458
- connected_account_id: String(data.connected_account_id ?? data.id ?? ""),
1459
- provider_key: String(data.provider_key ?? ""),
1460
- account_role: String(data.account_role ?? ""),
1461
- display_name: stringOrNull(data.display_name),
1462
- environment: stringOrNull(data.environment),
1463
- connection_status: stringOrNull(data.connection_status),
1464
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((item) => typeof item === "string") : [],
1465
- metadata: toRecord(data.metadata),
1466
- created_at: stringOrNull(data.created_at),
1467
- updated_at: stringOrNull(data.updated_at),
1468
- raw: { ...data }
1469
- };
1470
- }
1471
1541
  function parseSupportCase(data) {
1472
1542
  return {
1473
1543
  support_case_id: String(data.support_case_id ?? data.id ?? ""),
@@ -2506,13 +2576,6 @@ var init_client = __esm({
2506
2576
  if (options.runtime_validation) {
2507
2577
  payload.runtime_validation = coerceMapping(options.runtime_validation, "runtime_validation");
2508
2578
  }
2509
- if (options.oauth_credentials) {
2510
- payload.oauth_credentials = Array.isArray(options.oauth_credentials) ? {
2511
- items: options.oauth_credentials.map(
2512
- (item, index) => coerceMapping(item, `oauth_credentials[${index}]`)
2513
- )
2514
- } : coerceMapping(options.oauth_credentials, "oauth_credentials");
2515
- }
2516
2579
  if (options.source_context) {
2517
2580
  payload.source_context = coerceMapping(options.source_context, "source_context");
2518
2581
  }
@@ -2531,6 +2594,9 @@ var init_client = __esm({
2531
2594
  "support_contact",
2532
2595
  "seller_homepage_url",
2533
2596
  "seller_social_url",
2597
+ "publisher_type",
2598
+ "company_id",
2599
+ "publisher_company_id",
2534
2600
  "store_vertical",
2535
2601
  "jurisdiction",
2536
2602
  "price_model",
@@ -2543,7 +2609,8 @@ var init_client = __esm({
2543
2609
  "dry_run_supported",
2544
2610
  "required_connected_accounts",
2545
2611
  "permission_scopes",
2546
- "compatibility_tags"
2612
+ "compatibility_tags",
2613
+ "persistence"
2547
2614
  ]) {
2548
2615
  const value = manifestPayload[fieldName];
2549
2616
  if (value !== void 0 && value !== null) {
@@ -2583,6 +2650,26 @@ var init_client = __esm({
2583
2650
  );
2584
2651
  }
2585
2652
  }
2653
+ const explicitPublisherType = payload.publisher_type !== void 0 && payload.publisher_type !== null;
2654
+ const companyId = String(payload.company_id ?? "").trim() || String(payload.publisher_company_id ?? "").trim();
2655
+ const publisherType = String(payload.publisher_type ?? "user").trim().toLowerCase();
2656
+ if (publisherType !== "user" && publisherType !== "company") {
2657
+ throw new SiglumeClientError("AppManifest.publisher_type must be 'user' or 'company'.");
2658
+ }
2659
+ if (publisherType === "company" && !companyId) {
2660
+ throw new SiglumeClientError("AppManifest.company_id is required when publisher_type='company'.");
2661
+ }
2662
+ if (publisherType === "user" && companyId) {
2663
+ throw new SiglumeClientError("AppManifest.company_id cannot be combined with publisher_type='user'.");
2664
+ }
2665
+ if (explicitPublisherType || companyId) {
2666
+ payload.publisher_type = publisherType;
2667
+ }
2668
+ if (companyId) {
2669
+ payload.company_id = companyId;
2670
+ payload.publisher_company_id = companyId;
2671
+ }
2672
+ validateManifestPersistenceContract(payload);
2586
2673
  if (payload.manifest && typeof payload.manifest === "object") {
2587
2674
  delete payload.manifest.version;
2588
2675
  }
@@ -2618,7 +2705,6 @@ var init_client = __esm({
2618
2705
  auto_manifest: toRecord(data.auto_manifest),
2619
2706
  confidence: toRecord(data.confidence),
2620
2707
  validation_report: toRecord(data.validation_report),
2621
- oauth_status: toRecord(data.oauth_status),
2622
2708
  review_url: stringOrNull(data.review_url),
2623
2709
  trace_id: meta.trace_id,
2624
2710
  request_id: meta.request_id
@@ -2690,6 +2776,45 @@ var init_client = __esm({
2690
2776
  const [data] = await this.request("GET", `/market/capabilities/${listing_id}`);
2691
2777
  return parseListing(data);
2692
2778
  }
2779
+ async list_company_publishers() {
2780
+ const [data] = await this.request("GET", "/market/company-publishers");
2781
+ return Array.isArray(data.items) ? data.items.filter((item) => isRecord(item)).map(parseCompanyPublisher) : [];
2782
+ }
2783
+ async request_company_publish_approval(listing_id, note) {
2784
+ const [data] = await this.request("POST", `/market/capabilities/${listing_id}/company-publish-approval`, {
2785
+ json_body: note ? { note } : {}
2786
+ });
2787
+ return parseListing(data);
2788
+ }
2789
+ async decide_company_publish_approval(listing_id, options) {
2790
+ const [data] = await this.request("POST", `/market/capabilities/${listing_id}/company-publish-approval/decision`, {
2791
+ json_body: {
2792
+ decision: options.decision,
2793
+ ...options.reason ? { reason: options.reason } : {}
2794
+ }
2795
+ });
2796
+ return parseListing(data);
2797
+ }
2798
+ async get_capability_state(capability_key, save_key = "default") {
2799
+ const [data] = await this.request("GET", `/market/capability-state/${capability_key}/${save_key}`);
2800
+ return parseCapabilitySaveState(data);
2801
+ }
2802
+ async put_capability_state(capability_key, save_key = "default", payload = {}, options = {}) {
2803
+ const body = {
2804
+ payload: toRecord(payload),
2805
+ schema_version: options.schema_version ?? "1",
2806
+ metadata: toRecord(options.metadata)
2807
+ };
2808
+ if (options.expected_revision !== void 0 && options.expected_revision !== null) {
2809
+ body.expected_revision = Math.trunc(options.expected_revision);
2810
+ }
2811
+ const [data] = await this.request("PUT", `/market/capability-state/${capability_key}/${save_key}`, { json_body: body });
2812
+ return parseCapabilitySaveState(data);
2813
+ }
2814
+ async delete_capability_state(capability_key, save_key = "default") {
2815
+ const [data] = await this.request("DELETE", `/market/capability-state/${capability_key}/${save_key}`);
2816
+ return parseCapabilitySaveState(data);
2817
+ }
2693
2818
  // ----- Capability bundles (v0.7 track 2) ---------------------------------
2694
2819
  async list_bundles(options = {}) {
2695
2820
  const params = {
@@ -2755,66 +2880,9 @@ var init_client = __esm({
2755
2880
  return parseBundle(data);
2756
2881
  }
2757
2882
  // ----- end bundles -------------------------------------------------------
2758
- // ----- Connected accounts (v0.7 track 3) ---------------------------------
2759
- // `resolve()` is intentionally NOT wrapped: runtime-only, never over the wire.
2760
- async start_connected_account_oauth(input) {
2761
- const body = {
2762
- listing_id: input.listing_id,
2763
- redirect_uri: input.redirect_uri
2764
- };
2765
- if (input.scopes !== void 0) body.scopes = input.scopes;
2766
- if (input.account_role !== void 0) body.account_role = input.account_role;
2767
- const [data] = await this.request("POST", "/me/connected-accounts/oauth/authorize", {
2768
- json_body: body
2769
- });
2770
- return {
2771
- authorize_url: String(data.authorize_url ?? ""),
2772
- state: String(data.state ?? ""),
2773
- provider_key: String(data.provider_key ?? ""),
2774
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((s) => typeof s === "string") : [],
2775
- pkce_method: stringOrNull(data.pkce_method)
2776
- };
2777
- }
2778
- async complete_connected_account_oauth(input) {
2779
- const [data] = await this.request("POST", "/me/connected-accounts/oauth/callback", {
2780
- json_body: { state: input.state, code: input.code }
2781
- });
2782
- return { ...data };
2783
- }
2784
- async refresh_connected_account(account_id) {
2785
- const [data] = await this.request("POST", `/me/connected-accounts/${account_id}/refresh`);
2786
- return parseConnectedAccountLifecycle(data);
2787
- }
2788
- async revoke_connected_account(account_id) {
2789
- const [data] = await this.request("POST", `/me/connected-accounts/${account_id}/revoke`);
2790
- return parseConnectedAccountLifecycle(data);
2791
- }
2792
- async set_listing_oauth_credentials(listing_id, input) {
2793
- const body = {
2794
- provider_key: input.provider_key,
2795
- client_id: input.client_id,
2796
- client_secret: input.client_secret,
2797
- authorize_url: input.authorize_url,
2798
- token_url: input.token_url
2799
- };
2800
- if (input.revoke_url !== void 0) body.revoke_url = input.revoke_url;
2801
- if (input.display_name !== void 0) body.display_name = input.display_name;
2802
- if (input.scope_separator !== void 0) body.scope_separator = input.scope_separator;
2803
- if (input.token_endpoint_auth !== void 0) body.token_endpoint_auth = input.token_endpoint_auth;
2804
- if (input.pkce_required !== void 0) body.pkce_required = input.pkce_required;
2805
- if (input.refresh_supported !== void 0) body.refresh_supported = input.refresh_supported;
2806
- if (input.available_scopes !== void 0) body.available_scopes = input.available_scopes;
2807
- if (input.required_scopes !== void 0) body.required_scopes = input.required_scopes;
2808
- const [data] = await this.request("PUT", `/market/capabilities/${listing_id}/oauth-credentials`, {
2809
- json_body: body
2810
- });
2811
- return { ...data };
2812
- }
2813
- async get_listing_oauth_credentials_status(listing_id) {
2814
- const [data] = await this.request("GET", `/market/capabilities/${listing_id}/oauth-credentials`);
2815
- return { ...data };
2816
- }
2817
- // ----- end connected accounts --------------------------------------------
2883
+ // ----- Connected accounts ------------------------------------------------
2884
+ // Architecture B: publisher APIs own external OAuth and token storage.
2885
+ // The SDK no longer exposes platform OAuth or listing credential APIs.
2818
2886
  async get_developer_portal() {
2819
2887
  const [data, meta] = await this.request("GET", "/market/developer/portal");
2820
2888
  return {
@@ -2847,7 +2915,6 @@ var init_client = __esm({
2847
2915
  dry_run_supported: Boolean(data.dry_run_supported ?? false),
2848
2916
  approval_mode: stringOrNull(data.approval_mode),
2849
2917
  required_connected_accounts: Array.isArray(data.required_connected_accounts) ? data.required_connected_accounts : [],
2850
- connected_accounts: Array.isArray(data.connected_accounts) ? data.connected_accounts.filter((item) => isRecord(item)).map((item) => ({ ...item })) : [],
2851
2918
  stub_providers_enabled: Boolean(data.stub_providers_enabled ?? false),
2852
2919
  simulated_receipts: Boolean(data.simulated_receipts ?? false),
2853
2920
  approval_simulator: Boolean(data.approval_simulator ?? false),
@@ -4145,25 +4212,6 @@ var init_client = __esm({
4145
4212
  raw: { ...data }
4146
4213
  };
4147
4214
  }
4148
- async list_connected_accounts(options = {}) {
4149
- const params = {
4150
- provider_key: options.provider_key,
4151
- environment: options.environment,
4152
- limit: Math.max(1, Math.min(Math.trunc(options.limit ?? 50), 100)),
4153
- cursor: options.cursor
4154
- };
4155
- const [data, meta] = await this.request("GET", "/market/connected-accounts", { params });
4156
- const items = Array.isArray(data.items) ? data.items.filter((item) => isRecord(item)).map(parseConnectedAccount) : [];
4157
- const next_cursor = stringOrNull(data.next_cursor);
4158
- return new CursorPageResult({
4159
- items,
4160
- next_cursor,
4161
- limit: typeof data.limit === "number" ? data.limit : params.limit,
4162
- offset: typeof data.offset === "number" ? data.offset : null,
4163
- meta,
4164
- fetchNext: next_cursor ? (cursor) => this.list_connected_accounts({ ...options, cursor }) : void 0
4165
- });
4166
- }
4167
4215
  async create_support_case(subject, body, options = {}) {
4168
4216
  const summary = subject.trim();
4169
4217
  const details = body.trim();
@@ -6012,24 +6060,12 @@ var AppTestHarness = class {
6012
6060
  this.stubs = stubs;
6013
6061
  }
6014
6062
  async executeWithKind(execution_kind, task_type = "default", options = {}) {
6015
- const connected_accounts = options.connected_accounts ?? Object.fromEntries(
6016
- Object.keys(this.stubs).map((key) => [
6017
- key,
6018
- {
6019
- provider_key: key,
6020
- session_token: `stub-token-${key}`,
6021
- environment: Environment.SANDBOX,
6022
- scopes: []
6023
- }
6024
- ])
6025
- );
6026
6063
  const ctx = {
6027
6064
  agent_id: "test-agent-001",
6028
6065
  owner_user_id: "test-owner-001",
6029
6066
  task_type,
6030
6067
  environment: Environment.SANDBOX,
6031
6068
  execution_kind,
6032
- connected_accounts,
6033
6069
  input_params: options.input_params ?? {},
6034
6070
  trace_id: options.trace_id,
6035
6071
  idempotency_key: options.idempotency_key,
@@ -6119,12 +6155,6 @@ var AppTestHarness = class {
6119
6155
  }
6120
6156
  return issues;
6121
6157
  }
6122
- async simulate_connected_account_missing(task_type = "default", options = {}) {
6123
- return this.executeWithKind("dry_run", task_type, {
6124
- ...options,
6125
- connected_accounts: {}
6126
- });
6127
- }
6128
6158
  async simulate_metering(record, options = {}) {
6129
6159
  const { normalizeUsageRecord: normalizeUsageRecord2 } = await Promise.resolve().then(() => (init_metering(), metering_exports));
6130
6160
  const manifest = await this.app.manifest();
@@ -7087,15 +7117,6 @@ async function loadProject(path = ".") {
7087
7117
  const tool_manual = tool_manual_path ? JSON.parse(await (0, import_promises.readFile)(tool_manual_path, "utf8")) : buildToolManualTemplate(manifest);
7088
7118
  const runtime_validation_path = await findRuntimeValidationPath(root_dir);
7089
7119
  const runtime_validation = runtime_validation_path ? await loadJsonObject(runtime_validation_path, "runtime_validation") : void 0;
7090
- const oauth_credentials_path = await findOauthCredentialsPath(root_dir);
7091
- let oauth_credentials;
7092
- if (oauth_credentials_path) {
7093
- const parsed = JSON.parse(await (0, import_promises.readFile)(oauth_credentials_path, "utf8"));
7094
- if (!isRecord(parsed) && !Array.isArray(parsed)) {
7095
- throw new SiglumeProjectError("oauth_credentials must be a JSON object or array");
7096
- }
7097
- oauth_credentials = parsed;
7098
- }
7099
7120
  return {
7100
7121
  root_dir,
7101
7122
  adapter_path,
@@ -7104,9 +7125,7 @@ async function loadProject(path = ".") {
7104
7125
  tool_manual_path: tool_manual_path ?? void 0,
7105
7126
  tool_manual,
7106
7127
  runtime_validation_path: runtime_validation_path ?? void 0,
7107
- runtime_validation,
7108
- oauth_credentials_path: oauth_credentials_path ?? void 0,
7109
- oauth_credentials
7128
+ runtime_validation
7110
7129
  };
7111
7130
  }
7112
7131
  function isPlatformManagedRequirement(value) {
@@ -7144,6 +7163,21 @@ function requiredOauthProviders(requirements) {
7144
7163
  }
7145
7164
  return providers;
7146
7165
  }
7166
+ function apiManagedRequirementsMissingConnectUrl(requirements) {
7167
+ const missing = [];
7168
+ for (const item of requirements ?? []) {
7169
+ if (!isRecord(item)) continue;
7170
+ const managedBy = String(item.managed_by ?? "").trim().toLowerCase().replaceAll("_", "-");
7171
+ if (managedBy !== "api") continue;
7172
+ const connectUrl = String(item.connect_url ?? "").trim();
7173
+ if (connectUrl) continue;
7174
+ const label = oauthProviderKeyFromRequirement(item) ?? "(missing provider_key)";
7175
+ if (!missing.includes(label)) {
7176
+ missing.push(label);
7177
+ }
7178
+ }
7179
+ return missing;
7180
+ }
7147
7181
  function connectedAccountRequirementLabel(value) {
7148
7182
  if (isRecord(value)) {
7149
7183
  for (const key of ["provider_key", "provider", "account_type", "name"]) {
@@ -7154,102 +7188,6 @@ function connectedAccountRequirementLabel(value) {
7154
7188
  }
7155
7189
  return String(value ?? "").trim();
7156
7190
  }
7157
- function oauthProviderRecordsMap(payload) {
7158
- if (!payload) {
7159
- return {};
7160
- }
7161
- const items = Array.isArray(payload) ? payload : Array.isArray(payload.items) ? payload.items : [payload];
7162
- const resolved = {};
7163
- for (const [index, item] of items.entries()) {
7164
- if (!isRecord(item)) {
7165
- throw new SiglumeProjectError(`oauth_credentials[${index}] must be a JSON object.`);
7166
- }
7167
- const providerKey = oauthProviderKeyFromRequirement(item.provider_key ?? item.provider);
7168
- if (!providerKey) {
7169
- throw new SiglumeProjectError(`oauth_credentials[${index}].provider_key is required.`);
7170
- }
7171
- const authorizeUrl = String(item.authorize_url ?? item.authorization_url ?? item.auth_url ?? "").trim();
7172
- const tokenUrl = String(item.token_url ?? "").trim();
7173
- if (!authorizeUrl || !tokenUrl) {
7174
- throw new SiglumeProjectError(
7175
- `oauth_credentials[${index}] must include authorize_url and token_url.`
7176
- );
7177
- }
7178
- for (const [urlKey, urlValue] of Object.entries({
7179
- authorize_url: authorizeUrl,
7180
- token_url: tokenUrl,
7181
- revoke_url: String(item.revoke_url ?? "").trim()
7182
- })) {
7183
- if (urlValue && !urlValue.startsWith("https://")) {
7184
- throw new SiglumeProjectError(`oauth_credentials[${index}].${urlKey} must be an https URL.`);
7185
- }
7186
- }
7187
- const clientId = String(item.client_id ?? "").trim();
7188
- const clientSecret = String(item.client_secret ?? "").trim();
7189
- if (!clientId || !clientSecret) {
7190
- throw new SiglumeProjectError(`oauth_credentials[${index}] must include client_id and client_secret.`);
7191
- }
7192
- const rawScopes = item.required_scopes ?? item.scopes;
7193
- let scopes = [];
7194
- if (rawScopes == null) {
7195
- scopes = [];
7196
- } else if (!Array.isArray(rawScopes)) {
7197
- throw new SiglumeProjectError(`oauth_credentials[${index}].required_scopes must be a JSON array.`);
7198
- } else {
7199
- scopes = rawScopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7200
- }
7201
- const record = {
7202
- provider_key: providerKey,
7203
- client_id: clientId,
7204
- client_secret: clientSecret,
7205
- required_scopes: scopes
7206
- };
7207
- for (const [key, value] of Object.entries({
7208
- authorize_url: authorizeUrl,
7209
- token_url: tokenUrl,
7210
- revoke_url: String(item.revoke_url ?? "").trim(),
7211
- display_name: String(item.display_name ?? "").trim(),
7212
- scope_separator: String(item.scope_separator ?? "").trim(),
7213
- token_endpoint_auth: String(item.token_endpoint_auth ?? "").trim()
7214
- })) {
7215
- if (value) record[key] = value;
7216
- }
7217
- for (const key of ["pkce_required", "refresh_supported"]) {
7218
- if (typeof item[key] === "boolean") record[key] = item[key];
7219
- }
7220
- if (Array.isArray(item.available_scopes)) {
7221
- const availableScopes = item.available_scopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7222
- if (availableScopes.length > 0) record.available_scopes = availableScopes;
7223
- }
7224
- resolved[providerKey] = record;
7225
- }
7226
- return resolved;
7227
- }
7228
- function canonicalOauthCredentialsPayload(payload) {
7229
- const records = oauthProviderRecordsMap(payload);
7230
- const providerKeys = Object.keys(records).sort();
7231
- if (providerKeys.length === 0) {
7232
- return void 0;
7233
- }
7234
- return {
7235
- items: providerKeys.map((providerKey) => records[providerKey])
7236
- };
7237
- }
7238
- function ensureRequiredOauthCredentials(project) {
7239
- const requiredProviders = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7240
- if (requiredProviders.length === 0) {
7241
- return;
7242
- }
7243
- const provided = new Set(Object.keys(oauthProviderRecordsMap(project.oauth_credentials)));
7244
- const missing = requiredProviders.filter((provider) => !provided.has(provider));
7245
- if (missing.length === 0) {
7246
- return;
7247
- }
7248
- const path = project.oauth_credentials_path ?? (0, import_node_path.join)(project.root_dir, "oauth_credentials.json");
7249
- throw new SiglumeProjectError(
7250
- `${path} is required for platform-managed OAuth APIs. Missing provider seeds: ${missing.join(", ")}`
7251
- );
7252
- }
7253
7191
  async function validateProject(path = ".", deps = {}) {
7254
7192
  const project = await loadProject(path);
7255
7193
  const manifest_issues = await projectValidationIssues(project);
@@ -7289,7 +7227,12 @@ function ensureManifestPublisherIdentity(project) {
7289
7227
  const sellerHomepageUrl = String(manifestPayload.seller_homepage_url ?? "").trim();
7290
7228
  const sellerSocialUrl = String(manifestPayload.seller_social_url ?? "").trim();
7291
7229
  const jurisdiction = String(manifestPayload.jurisdiction ?? "").trim();
7230
+ const companyId = String(manifestPayload.company_id ?? "").trim() || String(manifestPayload.publisher_company_id ?? "").trim();
7231
+ const publisherType = String(manifestPayload.publisher_type ?? "user").trim().toLowerCase();
7292
7232
  const issues = [];
7233
+ if (companyId && publisherType !== "company") {
7234
+ issues.push('manifest.company_id requires manifest.publisher_type to be "company"');
7235
+ }
7293
7236
  if (!docsUrl) {
7294
7237
  issues.push("manifest.docs_url is required");
7295
7238
  } else if (looksLikePlaceholder(docsUrl)) {
@@ -7399,10 +7342,21 @@ function ensureExplicitToolManual(project) {
7399
7342
  async function registrationPreflight(project, client) {
7400
7343
  const manifestIssues = await projectValidationIssues(project);
7401
7344
  const [toolManualValid, toolManualIssues] = validate_tool_manual(project.tool_manual);
7345
+ const retiredPlatformOauthProviders = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7346
+ if (retiredPlatformOauthProviders.length > 0) {
7347
+ throw new SiglumeProjectError(
7348
+ `Registration preflight failed. Fix these before calling auto-register:
7349
+ - platform-managed OAuth is retired. Use managed_by="api" with connect_url: ${retiredPlatformOauthProviders.join(", ")}`
7350
+ );
7351
+ }
7352
+ const apiManagedMissingConnectUrl = apiManagedRequirementsMissingConnectUrl(project.manifest.required_connected_accounts ?? []);
7353
+ if (apiManagedMissingConnectUrl.length > 0) {
7354
+ throw new SiglumeProjectError(
7355
+ `Registration preflight failed. Fix these before calling auto-register:
7356
+ - API-managed OAuth requirements must include connect_url: ${apiManagedMissingConnectUrl.join(", ")}`
7357
+ );
7358
+ }
7402
7359
  const remoteQuality = await client.preview_quality_score(project.tool_manual);
7403
- const requiredOauthProvidersList = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7404
- const oauthProviderRecords = oauthProviderRecordsMap(project.oauth_credentials);
7405
- const missingOauthProviders = requiredOauthProvidersList.filter((provider) => !oauthProviderRecords[provider]);
7406
7360
  const blockingToolManualIssues = toolManualIssues.filter((issue2) => issue2.severity === "error");
7407
7361
  const errors = [
7408
7362
  ...manifestIssues.map((issue2) => String(issue2)),
@@ -7414,17 +7368,12 @@ async function registrationPreflight(project, client) {
7414
7368
  if (!remoteQualityOk(remoteQuality)) {
7415
7369
  errors.push(`remote Tool Manual quality is not publishable: ${remoteQuality.grade} (${remoteQuality.overall_score}/100)`);
7416
7370
  }
7417
- if (missingOauthProviders.length > 0) {
7418
- errors.push(`oauth_credentials.json is required for platform-managed OAuth APIs: ${missingOauthProviders.join(", ")}`);
7419
- }
7420
7371
  const preflight = {
7421
7372
  manifest_issues: manifestIssues,
7422
7373
  tool_manual_valid: toolManualValid,
7423
7374
  tool_manual_issues: toolManualIssues.map((issue2) => toJsonable(issue2)),
7424
7375
  remote_quality: toJsonable(remoteQuality),
7425
- required_oauth_providers: requiredOauthProvidersList,
7426
- oauth_credentials_path: project.oauth_credentials_path ?? null,
7427
- oauth_missing_providers: missingOauthProviders,
7376
+ retired_platform_oauth_providers: retiredPlatformOauthProviders,
7428
7377
  ok: errors.length === 0
7429
7378
  };
7430
7379
  if (errors.length > 0) {
@@ -7435,35 +7384,116 @@ ${errors.map((error) => `- ${error}`).join("\n")}`
7435
7384
  }
7436
7385
  return preflight;
7437
7386
  }
7387
+ function companyNameSlug(value) {
7388
+ return value.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7389
+ }
7438
7390
  async function runRegistration(path = ".", options = {}, deps = {}) {
7439
7391
  const project = await loadProject(path);
7392
+ let requestedCompanyId = String(options.company_id ?? "").trim();
7393
+ const requestedCompanySlug = String(options.company_slug ?? "").trim();
7394
+ let companyPublisherCandidates = null;
7395
+ if (requestedCompanySlug) {
7396
+ if (requestedCompanyId) {
7397
+ throw new SiglumeProjectError("--company and --company-slug cannot be combined.");
7398
+ }
7399
+ const slug = companyNameSlug(requestedCompanySlug);
7400
+ if (!slug && requestedCompanySlug !== requestedCompanyId) {
7401
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not slug-compatible; use --company <company_id> instead.`);
7402
+ }
7403
+ }
7404
+ if (requestedCompanyId) {
7405
+ project.manifest = {
7406
+ ...project.manifest,
7407
+ publisher_type: "company",
7408
+ company_id: requestedCompanyId,
7409
+ publisher_company_id: requestedCompanyId
7410
+ };
7411
+ }
7440
7412
  ensureExplicitToolManual(project);
7441
7413
  ensureManifestPublisherIdentity(project);
7442
7414
  ensureRuntimeValidationReady(project);
7443
- ensureRequiredOauthCredentials(project);
7444
- const canonicalOauthCredentials = canonicalOauthCredentialsPayload(project.oauth_credentials);
7445
7415
  const client = await createClient(deps);
7416
+ if (requestedCompanySlug) {
7417
+ const slug = companyNameSlug(requestedCompanySlug);
7418
+ companyPublisherCandidates = await client.list_company_publishers();
7419
+ const matches = companyPublisherCandidates.filter(
7420
+ (item) => companyNameSlug(item.name || item.company_id) === slug || item.company_id === requestedCompanySlug
7421
+ );
7422
+ if (matches.length === 0) {
7423
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not available to this API key.`);
7424
+ }
7425
+ if (matches.length > 1) {
7426
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is ambiguous; use --company <company_id> instead.`);
7427
+ }
7428
+ const match = matches[0];
7429
+ if (!match) {
7430
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not available to this API key.`);
7431
+ }
7432
+ if (match.can_publish === false) {
7433
+ const disabledReasons = match.disabled_reasons ?? [];
7434
+ const reasons = disabledReasons.length > 0 ? disabledReasons.join(", ") : "company publisher is disabled";
7435
+ throw new SiglumeProjectError(`Company ${match.company_id} cannot publish: ${reasons}.`);
7436
+ }
7437
+ requestedCompanyId = match.company_id;
7438
+ project.manifest = {
7439
+ ...project.manifest,
7440
+ publisher_type: "company",
7441
+ company_id: requestedCompanyId,
7442
+ publisher_company_id: requestedCompanyId
7443
+ };
7444
+ }
7446
7445
  const preflight = await registrationPreflight(project, client);
7446
+ let companyPublisherPreflight = null;
7447
+ const companyId = String(project.manifest.company_id ?? "").trim() || String(project.manifest.publisher_company_id ?? "").trim();
7448
+ const publisherType = String(project.manifest.publisher_type ?? "user").toLowerCase();
7449
+ if (publisherType === "company") {
7450
+ if (!companyId) {
7451
+ throw new SiglumeProjectError("Company registration requires --company <company_id> or manifest.company_id.");
7452
+ }
7453
+ const companies = companyPublisherCandidates ?? await client.list_company_publishers();
7454
+ companyPublisherCandidates = companies;
7455
+ const company = companies.find((item) => item.company_id === companyId);
7456
+ if (!company) {
7457
+ throw new SiglumeProjectError(`Company ${companyId} is not available to this API key.`);
7458
+ }
7459
+ if (company.can_publish === false) {
7460
+ const disabledReasons = company.disabled_reasons ?? [];
7461
+ const reasons = disabledReasons.length > 0 ? disabledReasons.join(", ") : "company publisher is disabled";
7462
+ throw new SiglumeProjectError(`Company ${companyId} cannot publish: ${reasons}.`);
7463
+ }
7464
+ companyPublisherPreflight = company;
7465
+ }
7447
7466
  let developerPortalPreflight = null;
7448
7467
  if (String(project.manifest.price_model ?? "free").toLowerCase() !== "free") {
7449
- const portal = await client.get_developer_portal();
7450
- const verifiedDestination = portal.payout_readiness?.verified_destination;
7451
- if (verifiedDestination !== true) {
7452
- throw new SiglumeProjectError(
7453
- "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."
7454
- );
7468
+ if (publisherType === "company") {
7469
+ const company = companyPublisherPreflight;
7470
+ if (!company) {
7471
+ throw new SiglumeProjectError(`Company ${companyId} is not available to this API key.`);
7472
+ }
7473
+ if (company.settlement_wallet_ready !== true) {
7474
+ throw new SiglumeProjectError(
7475
+ `Paid company registration requires a verified company settlement wallet for ${company.name}. Open the company settings and complete settlement readiness before registering.`
7476
+ );
7477
+ }
7478
+ developerPortalPreflight = { company_publisher: toJsonable(company) };
7479
+ } else {
7480
+ const portal = await client.get_developer_portal();
7481
+ const verifiedDestination = portal.payout_readiness?.verified_destination;
7482
+ if (verifiedDestination !== true) {
7483
+ throw new SiglumeProjectError(
7484
+ "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."
7485
+ );
7486
+ }
7487
+ developerPortalPreflight = toJsonable(portal);
7455
7488
  }
7456
- developerPortalPreflight = toJsonable(portal);
7457
7489
  }
7458
7490
  const receipt = await client.auto_register(project.manifest, project.tool_manual, {
7459
- runtime_validation: project.runtime_validation,
7460
- oauth_credentials: canonicalOauthCredentials
7491
+ runtime_validation: project.runtime_validation
7461
7492
  });
7462
7493
  const result = {
7463
7494
  receipt: toJsonable(receipt),
7464
7495
  registration_preflight: preflight,
7465
- runtime_validation_path: project.runtime_validation_path ?? null,
7466
- oauth_credentials_path: project.oauth_credentials_path ?? null
7496
+ runtime_validation_path: project.runtime_validation_path ?? null
7467
7497
  };
7468
7498
  if (developerPortalPreflight) {
7469
7499
  result.developer_portal_preflight = developerPortalPreflight;
@@ -7484,7 +7514,6 @@ async function runPreflight(path = ".", deps = {}) {
7484
7514
  ensureExplicitToolManual(project);
7485
7515
  ensureManifestPublisherIdentity(project);
7486
7516
  ensureRuntimeValidationReady(project);
7487
- ensureRequiredOauthCredentials(project);
7488
7517
  const client = await createClient(deps);
7489
7518
  const preflight = await registrationPreflight(project, client);
7490
7519
  let developerPortalPreflight = null;
@@ -7502,8 +7531,7 @@ async function runPreflight(path = ".", deps = {}) {
7502
7531
  ok: true,
7503
7532
  adapter_path: project.adapter_path,
7504
7533
  registration_preflight: preflight,
7505
- runtime_validation_path: project.runtime_validation_path ?? null,
7506
- oauth_credentials_path: project.oauth_credentials_path ?? null
7534
+ runtime_validation_path: project.runtime_validation_path ?? null
7507
7535
  };
7508
7536
  if (developerPortalPreflight) {
7509
7537
  result.developer_portal_preflight = developerPortalPreflight;
@@ -7526,6 +7554,14 @@ async function getUsageReport(options, deps = {}) {
7526
7554
  count: items.length
7527
7555
  };
7528
7556
  }
7557
+ async function listCompanyPublishersReport(deps = {}) {
7558
+ const client = await createClient(deps);
7559
+ const companies = await client.list_company_publishers();
7560
+ return {
7561
+ companies: companies.map((item) => toJsonable(item)),
7562
+ count: companies.length
7563
+ };
7564
+ }
7529
7565
  async function diffJsonFiles(oldPath, newPath) {
7530
7566
  const oldPayload = await loadJsonDocument(oldPath);
7531
7567
  const newPayload = await loadJsonDocument(newPath);
@@ -7977,7 +8013,7 @@ function operationReadmeTemplate(operation, manifest, warning) {
7977
8013
  "- `tool_manual.json`: machine-generated ToolManual scaffold",
7978
8014
  "- `runtime_validation.json`: local public endpoint and review-key checks used by auto-register",
7979
8015
  "- `docs/api-usage.md`: publishable API usage guide template for `docs_url`",
7980
- "- `.gitignore`: keeps runtime review keys and OAuth client secrets out of Git",
8016
+ "- `.gitignore`: keeps runtime review keys out of Git",
7981
8017
  "- `tests/test_adapter.ts`: smoke test for `AppTestHarness`",
7982
8018
  "",
7983
8019
  "Before registering, replace all generated placeholders:",
@@ -7985,8 +8021,8 @@ function operationReadmeTemplate(operation, manifest, warning) {
7985
8021
  "- Replace `support_contact` with a real support email address or public support URL.",
7986
8022
  "- Optional `seller_homepage_url` is the seller's official site and can stay blank.",
7987
8023
  "- In the local `runtime_validation.json`, replace the public URL and review-key placeholders.",
7988
- "- If the API uses seller-side OAuth, create a local `oauth_credentials.json` next to the adapter.",
7989
- "- Do not commit real review keys or OAuth client secrets; the generated `.gitignore` excludes those files.",
8024
+ "- If the API uses external OAuth, implement that flow in your API runtime and keep user tokens outside Siglume.",
8025
+ "- Do not commit real review keys or external-provider secrets; the generated `.gitignore` excludes local secret files.",
7990
8026
  "- Because `runtime_validation.json` is ignored, GitHub samples do not commit review-key values.",
7991
8027
  "",
7992
8028
  "## Commands",
@@ -8062,8 +8098,6 @@ function generatedGitignore() {
8062
8098
  "!.env.example",
8063
8099
  "runtime_validation.json",
8064
8100
  "runtime-validation.json",
8065
- "oauth_credentials.json",
8066
- "oauth-credentials.json",
8067
8101
  "",
8068
8102
  "# Python / test artifacts.",
8069
8103
  "__pycache__/",
@@ -8209,13 +8243,6 @@ async function runHarnessForProject(project) {
8209
8243
  checks.push(executionCheck("quote", await harness.execute_quote(task_type, { input_params: sample_input }), harness));
8210
8244
  checks.push(executionCheck("payment", await harness.execute_payment(task_type, { input_params: sample_input }), harness));
8211
8245
  }
8212
- checks.push(
8213
- executionCheck(
8214
- "missing_account_simulation",
8215
- await harness.simulate_connected_account_missing(task_type, { input_params: sample_input }),
8216
- harness
8217
- )
8218
- );
8219
8246
  return {
8220
8247
  adapter_path: project.adapter_path,
8221
8248
  task_type,
@@ -8318,15 +8345,6 @@ async function findRuntimeValidationPath(root_dir) {
8318
8345
  }
8319
8346
  return null;
8320
8347
  }
8321
- async function findOauthCredentialsPath(root_dir) {
8322
- for (const name of ["oauth_credentials.json", "oauth-credentials.json"]) {
8323
- const candidate = (0, import_node_path.join)(root_dir, name);
8324
- if ((0, import_node_fs.existsSync)(candidate)) {
8325
- return candidate;
8326
- }
8327
- }
8328
- return null;
8329
- }
8330
8348
  async function loadJsonObject(path, label) {
8331
8349
  let payload;
8332
8350
  try {
@@ -8558,15 +8576,15 @@ function readmeTemplate(template) {
8558
8576
  "- `tool_manual.json`: editable ToolManual draft for validation and registration",
8559
8577
  "- `runtime_validation.json`: local live API smoke-test contract used during registration",
8560
8578
  "- `docs/api-usage.md`: publish this page and use its public URL as `docs_url`",
8561
- "- `.gitignore`: keeps runtime review keys and OAuth client secrets out of Git",
8579
+ "- `.gitignore`: keeps runtime review keys out of Git",
8562
8580
  "",
8563
8581
  "Before registering, replace all generated placeholders:",
8564
8582
  "- In `adapter.ts` and `manifest.json`, replace `docs_url` with a dedicated public API usage guide, not a homepage.",
8565
8583
  "- Replace `support_contact` with a real support email address or public support URL.",
8566
8584
  "- Optional `seller_homepage_url` is the seller's official site and can stay blank.",
8567
8585
  "- In the local `runtime_validation.json`, replace the public URL and review-key placeholders.",
8568
- "- If the API uses seller-side OAuth, create a local `oauth_credentials.json` next to the adapter.",
8569
- "- Do not commit real review keys or OAuth client secrets; the generated `.gitignore` excludes those files.",
8586
+ "- If the API uses external OAuth, implement that flow in your API runtime and keep user tokens outside Siglume.",
8587
+ "- Do not commit real review keys or external-provider secrets; the generated `.gitignore` excludes local secret files.",
8570
8588
  "- Because `runtime_validation.json` is ignored, GitHub samples do not commit review-key values.",
8571
8589
  "",
8572
8590
  "Suggested workflow:",
@@ -8612,6 +8630,24 @@ function renderOperationTable(operations) {
8612
8630
  ...rows.map((row) => row.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join(" "))
8613
8631
  ];
8614
8632
  }
8633
+ function renderCompanyTable(companies) {
8634
+ const rows = companies.map((item) => [
8635
+ String(item.company_id ?? item.id ?? ""),
8636
+ String(item.name ?? ""),
8637
+ String(item.membership_role ?? (item.is_founder ? "founder" : "")),
8638
+ String(item.settlement_wallet_ready === true ? "ready" : "not_ready"),
8639
+ String(item.pending_approval_count ?? 0)
8640
+ ]);
8641
+ const headers = ["company_id", "name", "role", "settlement", "pending"];
8642
+ const widths = headers.map(
8643
+ (header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0))
8644
+ );
8645
+ return [
8646
+ headers.map((header, index) => header.padEnd(widths[index] ?? header.length)).join(" "),
8647
+ widths.map((width) => "-".repeat(width)).join(" "),
8648
+ ...rows.map((row) => row.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join(" "))
8649
+ ];
8650
+ }
8615
8651
  async function runCli(argv, deps = {}) {
8616
8652
  const stdout = deps.stdout;
8617
8653
  const stderr = deps.stderr ?? console.error;
@@ -8766,9 +8802,22 @@ async function runCli(argv, deps = {}) {
8766
8802
  emit(stdout, `preflight_quality: ${preflight.remote_quality.grade} (${preflight.remote_quality.overall_score}/100)`);
8767
8803
  }
8768
8804
  if (report.runtime_validation_path) emit(stdout, `runtime_validation_path: ${String(report.runtime_validation_path)}`);
8769
- if (report.oauth_credentials_path) emit(stdout, `oauth_credentials_path: ${String(report.oauth_credentials_path)}`);
8770
8805
  });
8771
- 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) => {
8806
+ program.command("companies").description("List Siglume companies available for company-name publishing.").option("--json", "emit machine-readable JSON", false).action(async (options) => {
8807
+ const report = await listCompanyPublishersReport(deps);
8808
+ if (options.json) {
8809
+ emit(stdout, renderJson(report));
8810
+ return;
8811
+ }
8812
+ const companies = Array.isArray(report.companies) ? report.companies.filter((item) => Boolean(item && typeof item === "object")) : [];
8813
+ if (companies.length === 0) {
8814
+ emit(stdout, "No company publishers available for this API key.");
8815
+ return;
8816
+ }
8817
+ emit(stdout, "Company publishers");
8818
+ renderCompanyTable(companies).forEach((line) => emit(stdout, line));
8819
+ });
8820
+ 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) => {
8772
8821
  const draftOnly = Boolean(options.draftOnly);
8773
8822
  if (draftOnly && options.confirm) {
8774
8823
  throw new SiglumeProjectError("--draft-only cannot be combined with --confirm.");
@@ -8777,7 +8826,13 @@ async function runCli(argv, deps = {}) {
8777
8826
  throw new SiglumeProjectError("--draft-only cannot be combined with --submit-review.");
8778
8827
  }
8779
8828
  const shouldConfirm = Boolean(options.confirm) || !draftOnly && !options.submitReview;
8780
- const report = await runRegistration(path, { confirm: shouldConfirm, draft_only: draftOnly, submit_review: options.submitReview }, deps);
8829
+ const report = await runRegistration(path, {
8830
+ confirm: shouldConfirm,
8831
+ draft_only: draftOnly,
8832
+ submit_review: options.submitReview,
8833
+ company_id: options.company,
8834
+ company_slug: options.companySlug
8835
+ }, deps);
8781
8836
  if (options.json) {
8782
8837
  emit(stdout, renderJson(report));
8783
8838
  } else {
@@ -8797,7 +8852,6 @@ async function runCli(argv, deps = {}) {
8797
8852
  emit(stdout, `listing_id: ${receipt.listing_id}`);
8798
8853
  emit(stdout, `receipt_status: ${receipt.status}`);
8799
8854
  if (receipt.listing_status) emit(stdout, `listing_status: ${receipt.listing_status}`);
8800
- if (receipt.oauth_status) emit(stdout, `oauth_configured: ${Boolean(receipt.oauth_status.configured)}`);
8801
8855
  if (receipt.review_url) emit(stdout, `review_url: ${receipt.review_url}`);
8802
8856
  if (receipt.trace_id) emit(stdout, `trace_id: ${receipt.trace_id}`);
8803
8857
  if (receipt.request_id) emit(stdout, `request_id: ${receipt.request_id}`);