@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.
package/dist/cli/index.js CHANGED
@@ -1226,6 +1226,56 @@ var init_operations = __esm({
1226
1226
  });
1227
1227
 
1228
1228
  // src/client.ts
1229
+ function validateManifestPersistenceContract(payload) {
1230
+ const vertical = String(payload.store_vertical ?? "").trim().toLowerCase();
1231
+ const persistence = payload.persistence;
1232
+ if (persistence === void 0 || persistence === null) {
1233
+ return;
1234
+ }
1235
+ if (!isRecord(persistence)) {
1236
+ throw new SiglumeClientError("AppManifest.persistence must be an object.");
1237
+ }
1238
+ const mode = String(persistence.mode ?? (vertical === "game" ? "platform" : "none")).trim().toLowerCase();
1239
+ if (!["none", "local", "platform", "developer_server"].includes(mode)) {
1240
+ throw new SiglumeClientError(
1241
+ "AppManifest.persistence.mode must be one of: none, local, platform, developer_server."
1242
+ );
1243
+ }
1244
+ const schema = persistence.save_data_schema;
1245
+ if (vertical === "game" && mode !== "none" && schema === void 0) {
1246
+ throw new SiglumeClientError(
1247
+ "AppManifest.persistence.save_data_schema is required when store_vertical='game' and persistence.mode is not 'none'."
1248
+ );
1249
+ }
1250
+ if (schema !== void 0) {
1251
+ validateSaveDataSchema(schema, "AppManifest.persistence.save_data_schema");
1252
+ }
1253
+ }
1254
+ function validateSaveDataSchema(schema, fieldName) {
1255
+ if (!isRecord(schema)) {
1256
+ throw new SiglumeClientError(`${fieldName} must be a JSON Schema object.`);
1257
+ }
1258
+ const schemaSize = new TextEncoder().encode(JSON.stringify(schema)).length;
1259
+ if (schemaSize > 8192) {
1260
+ throw new SiglumeClientError(`${fieldName} must be at most 8192 bytes.`);
1261
+ }
1262
+ if (schema.type !== "object") {
1263
+ throw new SiglumeClientError(`${fieldName}.type must be 'object'.`);
1264
+ }
1265
+ const properties = schema.properties;
1266
+ if (!isRecord(properties) || Object.keys(properties).length === 0) {
1267
+ throw new SiglumeClientError(`${fieldName}.properties must be a non-empty object.`);
1268
+ }
1269
+ if (schema.required !== void 0) {
1270
+ if (!Array.isArray(schema.required) || !schema.required.every((item) => typeof item === "string")) {
1271
+ throw new SiglumeClientError(`${fieldName}.required must be an array of strings when provided.`);
1272
+ }
1273
+ const missing = schema.required.filter((item) => !(item in properties));
1274
+ if (missing.length > 0) {
1275
+ throw new SiglumeClientError(`${fieldName}.required references undefined properties: ${missing.join(", ")}.`);
1276
+ }
1277
+ }
1278
+ }
1229
1279
  function buildToolManualQualityReport(payload) {
1230
1280
  const qualityBlock = isRecord(payload.quality) ? payload.quality : payload;
1231
1281
  const issues = [];
@@ -1300,6 +1350,8 @@ function buildUrl(baseUrl, path, params) {
1300
1350
  return url.toString();
1301
1351
  }
1302
1352
  function parseListing(data) {
1353
+ const metadata = isRecord(data.metadata) ? data.metadata : {};
1354
+ const persistence = isRecord(data.persistence) ? data.persistence : isRecord(metadata.persistence) ? metadata.persistence : {};
1303
1355
  return {
1304
1356
  listing_id: String(data.listing_id ?? data.id ?? ""),
1305
1357
  capability_key: String(data.capability_key ?? ""),
@@ -1322,14 +1374,59 @@ function parseListing(data) {
1322
1374
  seller_display_name: stringOrNull(data.seller_display_name),
1323
1375
  seller_homepage_url: stringOrNull(data.seller_homepage_url),
1324
1376
  seller_social_url: stringOrNull(data.seller_social_url),
1377
+ publisher_type: stringOrNull(data.publisher_type),
1378
+ publisher_company_id: stringOrNull(data.publisher_company_id),
1379
+ company_id: stringOrNull(data.company_id),
1380
+ company_name: stringOrNull(data.company_name),
1381
+ company_publish_status: stringOrNull(data.company_publish_status),
1382
+ company_terms_version: stringOrNull(data.company_terms_version),
1325
1383
  review_status: stringOrNull(data.review_status),
1326
1384
  review_note: stringOrNull(data.review_note),
1327
1385
  submission_blockers: Array.isArray(data.submission_blockers) ? data.submission_blockers.filter((item) => typeof item === "string") : [],
1386
+ persistence: { ...persistence },
1328
1387
  created_at: stringOrNull(data.created_at),
1329
1388
  updated_at: stringOrNull(data.updated_at),
1330
1389
  raw: { ...data }
1331
1390
  };
1332
1391
  }
1392
+ function parseCompanyPublisher(data) {
1393
+ const wallets = Array.isArray(data.settlement_wallets) ? data.settlement_wallets.filter((item) => isRecord(item)) : [];
1394
+ return {
1395
+ company_id: String(data.company_id ?? data.id ?? ""),
1396
+ name: String(data.name ?? ""),
1397
+ status: String(data.status ?? ""),
1398
+ description: stringOrNull(data.description),
1399
+ is_founder: Boolean(data.is_founder ?? false),
1400
+ membership_role: stringOrNull(data.membership_role),
1401
+ membership_status: stringOrNull(data.membership_status),
1402
+ can_publish: Boolean(data.can_publish ?? true),
1403
+ can_approve: Boolean(data.can_approve ?? false),
1404
+ approval_required: Boolean(data.approval_required ?? false),
1405
+ paid_listing_allowed: Boolean(data.paid_listing_allowed ?? false),
1406
+ disabled_reasons: Array.isArray(data.disabled_reasons) ? data.disabled_reasons.filter((item) => typeof item === "string") : [],
1407
+ company_terms_version: stringOrNull(data.company_terms_version),
1408
+ active_listing_count: Number(data.active_listing_count ?? 0),
1409
+ pending_approval_count: Number(data.pending_approval_count ?? 0),
1410
+ settlement_wallet_ready: Boolean(data.settlement_wallet_ready ?? false),
1411
+ settlement_wallets: wallets.map((item) => ({ ...item })),
1412
+ raw: { ...data }
1413
+ };
1414
+ }
1415
+ function parseCapabilitySaveState(data) {
1416
+ return {
1417
+ capability_key: String(data.capability_key ?? ""),
1418
+ save_key: String(data.save_key ?? ""),
1419
+ schema_version: String(data.schema_version ?? "1"),
1420
+ revision: Number(data.revision ?? 0),
1421
+ payload: toRecord(data.payload),
1422
+ metadata: toRecord(data.metadata),
1423
+ checksum: stringOrNull(data.checksum),
1424
+ updated_at: stringOrNull(data.updated_at),
1425
+ created_at: stringOrNull(data.created_at),
1426
+ exists: Boolean(data.exists ?? false),
1427
+ raw: { ...data }
1428
+ };
1429
+ }
1333
1430
  function parseBundleMember(data) {
1334
1431
  return {
1335
1432
  capability_listing_id: String(data.capability_listing_id ?? ""),
@@ -1341,18 +1438,6 @@ function parseBundleMember(data) {
1341
1438
  link_id: stringOrNull(data.link_id)
1342
1439
  };
1343
1440
  }
1344
- function parseConnectedAccountLifecycle(data) {
1345
- return {
1346
- connected_account_id: String(data.connected_account_id ?? ""),
1347
- provider_key: String(data.provider_key ?? ""),
1348
- expires_at: stringOrNull(data.expires_at),
1349
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((s) => typeof s === "string") : [],
1350
- refreshed_at: stringOrNull(data.refreshed_at),
1351
- connection_status: stringOrNull(data.connection_status),
1352
- provider_revoked: typeof data.provider_revoked === "boolean" ? data.provider_revoked : null,
1353
- revoked_at: stringOrNull(data.revoked_at)
1354
- };
1355
- }
1356
1441
  function parseBundle(data) {
1357
1442
  const membersRaw = Array.isArray(data.members) ? data.members : [];
1358
1443
  return {
@@ -1431,21 +1516,6 @@ function parseBinding(data) {
1431
1516
  raw: { ...data }
1432
1517
  };
1433
1518
  }
1434
- function parseConnectedAccount(data) {
1435
- return {
1436
- connected_account_id: String(data.connected_account_id ?? data.id ?? ""),
1437
- provider_key: String(data.provider_key ?? ""),
1438
- account_role: String(data.account_role ?? ""),
1439
- display_name: stringOrNull(data.display_name),
1440
- environment: stringOrNull(data.environment),
1441
- connection_status: stringOrNull(data.connection_status),
1442
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((item) => typeof item === "string") : [],
1443
- metadata: toRecord(data.metadata),
1444
- created_at: stringOrNull(data.created_at),
1445
- updated_at: stringOrNull(data.updated_at),
1446
- raw: { ...data }
1447
- };
1448
- }
1449
1519
  function parseSupportCase(data) {
1450
1520
  return {
1451
1521
  support_case_id: String(data.support_case_id ?? data.id ?? ""),
@@ -2484,13 +2554,6 @@ var init_client = __esm({
2484
2554
  if (options.runtime_validation) {
2485
2555
  payload.runtime_validation = coerceMapping(options.runtime_validation, "runtime_validation");
2486
2556
  }
2487
- if (options.oauth_credentials) {
2488
- payload.oauth_credentials = Array.isArray(options.oauth_credentials) ? {
2489
- items: options.oauth_credentials.map(
2490
- (item, index) => coerceMapping(item, `oauth_credentials[${index}]`)
2491
- )
2492
- } : coerceMapping(options.oauth_credentials, "oauth_credentials");
2493
- }
2494
2557
  if (options.source_context) {
2495
2558
  payload.source_context = coerceMapping(options.source_context, "source_context");
2496
2559
  }
@@ -2509,6 +2572,9 @@ var init_client = __esm({
2509
2572
  "support_contact",
2510
2573
  "seller_homepage_url",
2511
2574
  "seller_social_url",
2575
+ "publisher_type",
2576
+ "company_id",
2577
+ "publisher_company_id",
2512
2578
  "store_vertical",
2513
2579
  "jurisdiction",
2514
2580
  "price_model",
@@ -2521,7 +2587,8 @@ var init_client = __esm({
2521
2587
  "dry_run_supported",
2522
2588
  "required_connected_accounts",
2523
2589
  "permission_scopes",
2524
- "compatibility_tags"
2590
+ "compatibility_tags",
2591
+ "persistence"
2525
2592
  ]) {
2526
2593
  const value = manifestPayload[fieldName];
2527
2594
  if (value !== void 0 && value !== null) {
@@ -2561,6 +2628,26 @@ var init_client = __esm({
2561
2628
  );
2562
2629
  }
2563
2630
  }
2631
+ const explicitPublisherType = payload.publisher_type !== void 0 && payload.publisher_type !== null;
2632
+ const companyId = String(payload.company_id ?? "").trim() || String(payload.publisher_company_id ?? "").trim();
2633
+ const publisherType = String(payload.publisher_type ?? "user").trim().toLowerCase();
2634
+ if (publisherType !== "user" && publisherType !== "company") {
2635
+ throw new SiglumeClientError("AppManifest.publisher_type must be 'user' or 'company'.");
2636
+ }
2637
+ if (publisherType === "company" && !companyId) {
2638
+ throw new SiglumeClientError("AppManifest.company_id is required when publisher_type='company'.");
2639
+ }
2640
+ if (publisherType === "user" && companyId) {
2641
+ throw new SiglumeClientError("AppManifest.company_id cannot be combined with publisher_type='user'.");
2642
+ }
2643
+ if (explicitPublisherType || companyId) {
2644
+ payload.publisher_type = publisherType;
2645
+ }
2646
+ if (companyId) {
2647
+ payload.company_id = companyId;
2648
+ payload.publisher_company_id = companyId;
2649
+ }
2650
+ validateManifestPersistenceContract(payload);
2564
2651
  if (payload.manifest && typeof payload.manifest === "object") {
2565
2652
  delete payload.manifest.version;
2566
2653
  }
@@ -2596,7 +2683,6 @@ var init_client = __esm({
2596
2683
  auto_manifest: toRecord(data.auto_manifest),
2597
2684
  confidence: toRecord(data.confidence),
2598
2685
  validation_report: toRecord(data.validation_report),
2599
- oauth_status: toRecord(data.oauth_status),
2600
2686
  review_url: stringOrNull(data.review_url),
2601
2687
  trace_id: meta.trace_id,
2602
2688
  request_id: meta.request_id
@@ -2668,6 +2754,45 @@ var init_client = __esm({
2668
2754
  const [data] = await this.request("GET", `/market/capabilities/${listing_id}`);
2669
2755
  return parseListing(data);
2670
2756
  }
2757
+ async list_company_publishers() {
2758
+ const [data] = await this.request("GET", "/market/company-publishers");
2759
+ return Array.isArray(data.items) ? data.items.filter((item) => isRecord(item)).map(parseCompanyPublisher) : [];
2760
+ }
2761
+ async request_company_publish_approval(listing_id, note) {
2762
+ const [data] = await this.request("POST", `/market/capabilities/${listing_id}/company-publish-approval`, {
2763
+ json_body: note ? { note } : {}
2764
+ });
2765
+ return parseListing(data);
2766
+ }
2767
+ async decide_company_publish_approval(listing_id, options) {
2768
+ const [data] = await this.request("POST", `/market/capabilities/${listing_id}/company-publish-approval/decision`, {
2769
+ json_body: {
2770
+ decision: options.decision,
2771
+ ...options.reason ? { reason: options.reason } : {}
2772
+ }
2773
+ });
2774
+ return parseListing(data);
2775
+ }
2776
+ async get_capability_state(capability_key, save_key = "default") {
2777
+ const [data] = await this.request("GET", `/market/capability-state/${capability_key}/${save_key}`);
2778
+ return parseCapabilitySaveState(data);
2779
+ }
2780
+ async put_capability_state(capability_key, save_key = "default", payload = {}, options = {}) {
2781
+ const body = {
2782
+ payload: toRecord(payload),
2783
+ schema_version: options.schema_version ?? "1",
2784
+ metadata: toRecord(options.metadata)
2785
+ };
2786
+ if (options.expected_revision !== void 0 && options.expected_revision !== null) {
2787
+ body.expected_revision = Math.trunc(options.expected_revision);
2788
+ }
2789
+ const [data] = await this.request("PUT", `/market/capability-state/${capability_key}/${save_key}`, { json_body: body });
2790
+ return parseCapabilitySaveState(data);
2791
+ }
2792
+ async delete_capability_state(capability_key, save_key = "default") {
2793
+ const [data] = await this.request("DELETE", `/market/capability-state/${capability_key}/${save_key}`);
2794
+ return parseCapabilitySaveState(data);
2795
+ }
2671
2796
  // ----- Capability bundles (v0.7 track 2) ---------------------------------
2672
2797
  async list_bundles(options = {}) {
2673
2798
  const params = {
@@ -2733,66 +2858,9 @@ var init_client = __esm({
2733
2858
  return parseBundle(data);
2734
2859
  }
2735
2860
  // ----- end bundles -------------------------------------------------------
2736
- // ----- Connected accounts (v0.7 track 3) ---------------------------------
2737
- // `resolve()` is intentionally NOT wrapped: runtime-only, never over the wire.
2738
- async start_connected_account_oauth(input) {
2739
- const body = {
2740
- listing_id: input.listing_id,
2741
- redirect_uri: input.redirect_uri
2742
- };
2743
- if (input.scopes !== void 0) body.scopes = input.scopes;
2744
- if (input.account_role !== void 0) body.account_role = input.account_role;
2745
- const [data] = await this.request("POST", "/me/connected-accounts/oauth/authorize", {
2746
- json_body: body
2747
- });
2748
- return {
2749
- authorize_url: String(data.authorize_url ?? ""),
2750
- state: String(data.state ?? ""),
2751
- provider_key: String(data.provider_key ?? ""),
2752
- scopes: Array.isArray(data.scopes) ? data.scopes.filter((s) => typeof s === "string") : [],
2753
- pkce_method: stringOrNull(data.pkce_method)
2754
- };
2755
- }
2756
- async complete_connected_account_oauth(input) {
2757
- const [data] = await this.request("POST", "/me/connected-accounts/oauth/callback", {
2758
- json_body: { state: input.state, code: input.code }
2759
- });
2760
- return { ...data };
2761
- }
2762
- async refresh_connected_account(account_id) {
2763
- const [data] = await this.request("POST", `/me/connected-accounts/${account_id}/refresh`);
2764
- return parseConnectedAccountLifecycle(data);
2765
- }
2766
- async revoke_connected_account(account_id) {
2767
- const [data] = await this.request("POST", `/me/connected-accounts/${account_id}/revoke`);
2768
- return parseConnectedAccountLifecycle(data);
2769
- }
2770
- async set_listing_oauth_credentials(listing_id, input) {
2771
- const body = {
2772
- provider_key: input.provider_key,
2773
- client_id: input.client_id,
2774
- client_secret: input.client_secret,
2775
- authorize_url: input.authorize_url,
2776
- token_url: input.token_url
2777
- };
2778
- if (input.revoke_url !== void 0) body.revoke_url = input.revoke_url;
2779
- if (input.display_name !== void 0) body.display_name = input.display_name;
2780
- if (input.scope_separator !== void 0) body.scope_separator = input.scope_separator;
2781
- if (input.token_endpoint_auth !== void 0) body.token_endpoint_auth = input.token_endpoint_auth;
2782
- if (input.pkce_required !== void 0) body.pkce_required = input.pkce_required;
2783
- if (input.refresh_supported !== void 0) body.refresh_supported = input.refresh_supported;
2784
- if (input.available_scopes !== void 0) body.available_scopes = input.available_scopes;
2785
- if (input.required_scopes !== void 0) body.required_scopes = input.required_scopes;
2786
- const [data] = await this.request("PUT", `/market/capabilities/${listing_id}/oauth-credentials`, {
2787
- json_body: body
2788
- });
2789
- return { ...data };
2790
- }
2791
- async get_listing_oauth_credentials_status(listing_id) {
2792
- const [data] = await this.request("GET", `/market/capabilities/${listing_id}/oauth-credentials`);
2793
- return { ...data };
2794
- }
2795
- // ----- end connected accounts --------------------------------------------
2861
+ // ----- Connected accounts ------------------------------------------------
2862
+ // Architecture B: publisher APIs own external OAuth and token storage.
2863
+ // The SDK no longer exposes platform OAuth or listing credential APIs.
2796
2864
  async get_developer_portal() {
2797
2865
  const [data, meta] = await this.request("GET", "/market/developer/portal");
2798
2866
  return {
@@ -2825,7 +2893,6 @@ var init_client = __esm({
2825
2893
  dry_run_supported: Boolean(data.dry_run_supported ?? false),
2826
2894
  approval_mode: stringOrNull(data.approval_mode),
2827
2895
  required_connected_accounts: Array.isArray(data.required_connected_accounts) ? data.required_connected_accounts : [],
2828
- connected_accounts: Array.isArray(data.connected_accounts) ? data.connected_accounts.filter((item) => isRecord(item)).map((item) => ({ ...item })) : [],
2829
2896
  stub_providers_enabled: Boolean(data.stub_providers_enabled ?? false),
2830
2897
  simulated_receipts: Boolean(data.simulated_receipts ?? false),
2831
2898
  approval_simulator: Boolean(data.approval_simulator ?? false),
@@ -4123,25 +4190,6 @@ var init_client = __esm({
4123
4190
  raw: { ...data }
4124
4191
  };
4125
4192
  }
4126
- async list_connected_accounts(options = {}) {
4127
- const params = {
4128
- provider_key: options.provider_key,
4129
- environment: options.environment,
4130
- limit: Math.max(1, Math.min(Math.trunc(options.limit ?? 50), 100)),
4131
- cursor: options.cursor
4132
- };
4133
- const [data, meta] = await this.request("GET", "/market/connected-accounts", { params });
4134
- const items = Array.isArray(data.items) ? data.items.filter((item) => isRecord(item)).map(parseConnectedAccount) : [];
4135
- const next_cursor = stringOrNull(data.next_cursor);
4136
- return new CursorPageResult({
4137
- items,
4138
- next_cursor,
4139
- limit: typeof data.limit === "number" ? data.limit : params.limit,
4140
- offset: typeof data.offset === "number" ? data.offset : null,
4141
- meta,
4142
- fetchNext: next_cursor ? (cursor) => this.list_connected_accounts({ ...options, cursor }) : void 0
4143
- });
4144
- }
4145
4193
  async create_support_case(subject, body, options = {}) {
4146
4194
  const summary = subject.trim();
4147
4195
  const details = body.trim();
@@ -5985,24 +6033,12 @@ var AppTestHarness = class {
5985
6033
  this.stubs = stubs;
5986
6034
  }
5987
6035
  async executeWithKind(execution_kind, task_type = "default", options = {}) {
5988
- const connected_accounts = options.connected_accounts ?? Object.fromEntries(
5989
- Object.keys(this.stubs).map((key) => [
5990
- key,
5991
- {
5992
- provider_key: key,
5993
- session_token: `stub-token-${key}`,
5994
- environment: Environment.SANDBOX,
5995
- scopes: []
5996
- }
5997
- ])
5998
- );
5999
6036
  const ctx = {
6000
6037
  agent_id: "test-agent-001",
6001
6038
  owner_user_id: "test-owner-001",
6002
6039
  task_type,
6003
6040
  environment: Environment.SANDBOX,
6004
6041
  execution_kind,
6005
- connected_accounts,
6006
6042
  input_params: options.input_params ?? {},
6007
6043
  trace_id: options.trace_id,
6008
6044
  idempotency_key: options.idempotency_key,
@@ -6092,12 +6128,6 @@ var AppTestHarness = class {
6092
6128
  }
6093
6129
  return issues;
6094
6130
  }
6095
- async simulate_connected_account_missing(task_type = "default", options = {}) {
6096
- return this.executeWithKind("dry_run", task_type, {
6097
- ...options,
6098
- connected_accounts: {}
6099
- });
6100
- }
6101
6131
  async simulate_metering(record, options = {}) {
6102
6132
  const { normalizeUsageRecord: normalizeUsageRecord2 } = await Promise.resolve().then(() => (init_metering(), metering_exports));
6103
6133
  const manifest = await this.app.manifest();
@@ -7060,15 +7090,6 @@ async function loadProject(path = ".") {
7060
7090
  const tool_manual = tool_manual_path ? JSON.parse(await readFile(tool_manual_path, "utf8")) : buildToolManualTemplate(manifest);
7061
7091
  const runtime_validation_path = await findRuntimeValidationPath(root_dir);
7062
7092
  const runtime_validation = runtime_validation_path ? await loadJsonObject(runtime_validation_path, "runtime_validation") : void 0;
7063
- const oauth_credentials_path = await findOauthCredentialsPath(root_dir);
7064
- let oauth_credentials;
7065
- if (oauth_credentials_path) {
7066
- const parsed = JSON.parse(await readFile(oauth_credentials_path, "utf8"));
7067
- if (!isRecord(parsed) && !Array.isArray(parsed)) {
7068
- throw new SiglumeProjectError("oauth_credentials must be a JSON object or array");
7069
- }
7070
- oauth_credentials = parsed;
7071
- }
7072
7093
  return {
7073
7094
  root_dir,
7074
7095
  adapter_path,
@@ -7077,9 +7098,7 @@ async function loadProject(path = ".") {
7077
7098
  tool_manual_path: tool_manual_path ?? void 0,
7078
7099
  tool_manual,
7079
7100
  runtime_validation_path: runtime_validation_path ?? void 0,
7080
- runtime_validation,
7081
- oauth_credentials_path: oauth_credentials_path ?? void 0,
7082
- oauth_credentials
7101
+ runtime_validation
7083
7102
  };
7084
7103
  }
7085
7104
  function isPlatformManagedRequirement(value) {
@@ -7117,6 +7136,21 @@ function requiredOauthProviders(requirements) {
7117
7136
  }
7118
7137
  return providers;
7119
7138
  }
7139
+ function apiManagedRequirementsMissingConnectUrl(requirements) {
7140
+ const missing = [];
7141
+ for (const item of requirements ?? []) {
7142
+ if (!isRecord(item)) continue;
7143
+ const managedBy = String(item.managed_by ?? "").trim().toLowerCase().replaceAll("_", "-");
7144
+ if (managedBy !== "api") continue;
7145
+ const connectUrl = String(item.connect_url ?? "").trim();
7146
+ if (connectUrl) continue;
7147
+ const label = oauthProviderKeyFromRequirement(item) ?? "(missing provider_key)";
7148
+ if (!missing.includes(label)) {
7149
+ missing.push(label);
7150
+ }
7151
+ }
7152
+ return missing;
7153
+ }
7120
7154
  function connectedAccountRequirementLabel(value) {
7121
7155
  if (isRecord(value)) {
7122
7156
  for (const key of ["provider_key", "provider", "account_type", "name"]) {
@@ -7127,102 +7161,6 @@ function connectedAccountRequirementLabel(value) {
7127
7161
  }
7128
7162
  return String(value ?? "").trim();
7129
7163
  }
7130
- function oauthProviderRecordsMap(payload) {
7131
- if (!payload) {
7132
- return {};
7133
- }
7134
- const items = Array.isArray(payload) ? payload : Array.isArray(payload.items) ? payload.items : [payload];
7135
- const resolved = {};
7136
- for (const [index, item] of items.entries()) {
7137
- if (!isRecord(item)) {
7138
- throw new SiglumeProjectError(`oauth_credentials[${index}] must be a JSON object.`);
7139
- }
7140
- const providerKey = oauthProviderKeyFromRequirement(item.provider_key ?? item.provider);
7141
- if (!providerKey) {
7142
- throw new SiglumeProjectError(`oauth_credentials[${index}].provider_key is required.`);
7143
- }
7144
- const authorizeUrl = String(item.authorize_url ?? item.authorization_url ?? item.auth_url ?? "").trim();
7145
- const tokenUrl = String(item.token_url ?? "").trim();
7146
- if (!authorizeUrl || !tokenUrl) {
7147
- throw new SiglumeProjectError(
7148
- `oauth_credentials[${index}] must include authorize_url and token_url.`
7149
- );
7150
- }
7151
- for (const [urlKey, urlValue] of Object.entries({
7152
- authorize_url: authorizeUrl,
7153
- token_url: tokenUrl,
7154
- revoke_url: String(item.revoke_url ?? "").trim()
7155
- })) {
7156
- if (urlValue && !urlValue.startsWith("https://")) {
7157
- throw new SiglumeProjectError(`oauth_credentials[${index}].${urlKey} must be an https URL.`);
7158
- }
7159
- }
7160
- const clientId = String(item.client_id ?? "").trim();
7161
- const clientSecret = String(item.client_secret ?? "").trim();
7162
- if (!clientId || !clientSecret) {
7163
- throw new SiglumeProjectError(`oauth_credentials[${index}] must include client_id and client_secret.`);
7164
- }
7165
- const rawScopes = item.required_scopes ?? item.scopes;
7166
- let scopes = [];
7167
- if (rawScopes == null) {
7168
- scopes = [];
7169
- } else if (!Array.isArray(rawScopes)) {
7170
- throw new SiglumeProjectError(`oauth_credentials[${index}].required_scopes must be a JSON array.`);
7171
- } else {
7172
- scopes = rawScopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7173
- }
7174
- const record = {
7175
- provider_key: providerKey,
7176
- client_id: clientId,
7177
- client_secret: clientSecret,
7178
- required_scopes: scopes
7179
- };
7180
- for (const [key, value] of Object.entries({
7181
- authorize_url: authorizeUrl,
7182
- token_url: tokenUrl,
7183
- revoke_url: String(item.revoke_url ?? "").trim(),
7184
- display_name: String(item.display_name ?? "").trim(),
7185
- scope_separator: String(item.scope_separator ?? "").trim(),
7186
- token_endpoint_auth: String(item.token_endpoint_auth ?? "").trim()
7187
- })) {
7188
- if (value) record[key] = value;
7189
- }
7190
- for (const key of ["pkce_required", "refresh_supported"]) {
7191
- if (typeof item[key] === "boolean") record[key] = item[key];
7192
- }
7193
- if (Array.isArray(item.available_scopes)) {
7194
- const availableScopes = item.available_scopes.map((scope) => String(scope ?? "").trim()).filter(Boolean);
7195
- if (availableScopes.length > 0) record.available_scopes = availableScopes;
7196
- }
7197
- resolved[providerKey] = record;
7198
- }
7199
- return resolved;
7200
- }
7201
- function canonicalOauthCredentialsPayload(payload) {
7202
- const records = oauthProviderRecordsMap(payload);
7203
- const providerKeys = Object.keys(records).sort();
7204
- if (providerKeys.length === 0) {
7205
- return void 0;
7206
- }
7207
- return {
7208
- items: providerKeys.map((providerKey) => records[providerKey])
7209
- };
7210
- }
7211
- function ensureRequiredOauthCredentials(project) {
7212
- const requiredProviders = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7213
- if (requiredProviders.length === 0) {
7214
- return;
7215
- }
7216
- const provided = new Set(Object.keys(oauthProviderRecordsMap(project.oauth_credentials)));
7217
- const missing = requiredProviders.filter((provider) => !provided.has(provider));
7218
- if (missing.length === 0) {
7219
- return;
7220
- }
7221
- const path = project.oauth_credentials_path ?? join(project.root_dir, "oauth_credentials.json");
7222
- throw new SiglumeProjectError(
7223
- `${path} is required for platform-managed OAuth APIs. Missing provider seeds: ${missing.join(", ")}`
7224
- );
7225
- }
7226
7164
  async function validateProject(path = ".", deps = {}) {
7227
7165
  const project = await loadProject(path);
7228
7166
  const manifest_issues = await projectValidationIssues(project);
@@ -7262,7 +7200,12 @@ function ensureManifestPublisherIdentity(project) {
7262
7200
  const sellerHomepageUrl = String(manifestPayload.seller_homepage_url ?? "").trim();
7263
7201
  const sellerSocialUrl = String(manifestPayload.seller_social_url ?? "").trim();
7264
7202
  const jurisdiction = String(manifestPayload.jurisdiction ?? "").trim();
7203
+ const companyId = String(manifestPayload.company_id ?? "").trim() || String(manifestPayload.publisher_company_id ?? "").trim();
7204
+ const publisherType = String(manifestPayload.publisher_type ?? "user").trim().toLowerCase();
7265
7205
  const issues = [];
7206
+ if (companyId && publisherType !== "company") {
7207
+ issues.push('manifest.company_id requires manifest.publisher_type to be "company"');
7208
+ }
7266
7209
  if (!docsUrl) {
7267
7210
  issues.push("manifest.docs_url is required");
7268
7211
  } else if (looksLikePlaceholder(docsUrl)) {
@@ -7372,10 +7315,21 @@ function ensureExplicitToolManual(project) {
7372
7315
  async function registrationPreflight(project, client) {
7373
7316
  const manifestIssues = await projectValidationIssues(project);
7374
7317
  const [toolManualValid, toolManualIssues] = validate_tool_manual(project.tool_manual);
7318
+ const retiredPlatformOauthProviders = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7319
+ if (retiredPlatformOauthProviders.length > 0) {
7320
+ throw new SiglumeProjectError(
7321
+ `Registration preflight failed. Fix these before calling auto-register:
7322
+ - platform-managed OAuth is retired. Use managed_by="api" with connect_url: ${retiredPlatformOauthProviders.join(", ")}`
7323
+ );
7324
+ }
7325
+ const apiManagedMissingConnectUrl = apiManagedRequirementsMissingConnectUrl(project.manifest.required_connected_accounts ?? []);
7326
+ if (apiManagedMissingConnectUrl.length > 0) {
7327
+ throw new SiglumeProjectError(
7328
+ `Registration preflight failed. Fix these before calling auto-register:
7329
+ - API-managed OAuth requirements must include connect_url: ${apiManagedMissingConnectUrl.join(", ")}`
7330
+ );
7331
+ }
7375
7332
  const remoteQuality = await client.preview_quality_score(project.tool_manual);
7376
- const requiredOauthProvidersList = requiredOauthProviders(project.manifest.required_connected_accounts ?? []);
7377
- const oauthProviderRecords = oauthProviderRecordsMap(project.oauth_credentials);
7378
- const missingOauthProviders = requiredOauthProvidersList.filter((provider) => !oauthProviderRecords[provider]);
7379
7333
  const blockingToolManualIssues = toolManualIssues.filter((issue2) => issue2.severity === "error");
7380
7334
  const errors = [
7381
7335
  ...manifestIssues.map((issue2) => String(issue2)),
@@ -7387,17 +7341,12 @@ async function registrationPreflight(project, client) {
7387
7341
  if (!remoteQualityOk(remoteQuality)) {
7388
7342
  errors.push(`remote Tool Manual quality is not publishable: ${remoteQuality.grade} (${remoteQuality.overall_score}/100)`);
7389
7343
  }
7390
- if (missingOauthProviders.length > 0) {
7391
- errors.push(`oauth_credentials.json is required for platform-managed OAuth APIs: ${missingOauthProviders.join(", ")}`);
7392
- }
7393
7344
  const preflight = {
7394
7345
  manifest_issues: manifestIssues,
7395
7346
  tool_manual_valid: toolManualValid,
7396
7347
  tool_manual_issues: toolManualIssues.map((issue2) => toJsonable(issue2)),
7397
7348
  remote_quality: toJsonable(remoteQuality),
7398
- required_oauth_providers: requiredOauthProvidersList,
7399
- oauth_credentials_path: project.oauth_credentials_path ?? null,
7400
- oauth_missing_providers: missingOauthProviders,
7349
+ retired_platform_oauth_providers: retiredPlatformOauthProviders,
7401
7350
  ok: errors.length === 0
7402
7351
  };
7403
7352
  if (errors.length > 0) {
@@ -7408,35 +7357,116 @@ ${errors.map((error) => `- ${error}`).join("\n")}`
7408
7357
  }
7409
7358
  return preflight;
7410
7359
  }
7360
+ function companyNameSlug(value) {
7361
+ return value.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7362
+ }
7411
7363
  async function runRegistration(path = ".", options = {}, deps = {}) {
7412
7364
  const project = await loadProject(path);
7365
+ let requestedCompanyId = String(options.company_id ?? "").trim();
7366
+ const requestedCompanySlug = String(options.company_slug ?? "").trim();
7367
+ let companyPublisherCandidates = null;
7368
+ if (requestedCompanySlug) {
7369
+ if (requestedCompanyId) {
7370
+ throw new SiglumeProjectError("--company and --company-slug cannot be combined.");
7371
+ }
7372
+ const slug = companyNameSlug(requestedCompanySlug);
7373
+ if (!slug && requestedCompanySlug !== requestedCompanyId) {
7374
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not slug-compatible; use --company <company_id> instead.`);
7375
+ }
7376
+ }
7377
+ if (requestedCompanyId) {
7378
+ project.manifest = {
7379
+ ...project.manifest,
7380
+ publisher_type: "company",
7381
+ company_id: requestedCompanyId,
7382
+ publisher_company_id: requestedCompanyId
7383
+ };
7384
+ }
7413
7385
  ensureExplicitToolManual(project);
7414
7386
  ensureManifestPublisherIdentity(project);
7415
7387
  ensureRuntimeValidationReady(project);
7416
- ensureRequiredOauthCredentials(project);
7417
- const canonicalOauthCredentials = canonicalOauthCredentialsPayload(project.oauth_credentials);
7418
7388
  const client = await createClient(deps);
7389
+ if (requestedCompanySlug) {
7390
+ const slug = companyNameSlug(requestedCompanySlug);
7391
+ companyPublisherCandidates = await client.list_company_publishers();
7392
+ const matches = companyPublisherCandidates.filter(
7393
+ (item) => companyNameSlug(item.name || item.company_id) === slug || item.company_id === requestedCompanySlug
7394
+ );
7395
+ if (matches.length === 0) {
7396
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not available to this API key.`);
7397
+ }
7398
+ if (matches.length > 1) {
7399
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is ambiguous; use --company <company_id> instead.`);
7400
+ }
7401
+ const match = matches[0];
7402
+ if (!match) {
7403
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not available to this API key.`);
7404
+ }
7405
+ if (match.can_publish === false) {
7406
+ const disabledReasons = match.disabled_reasons ?? [];
7407
+ const reasons = disabledReasons.length > 0 ? disabledReasons.join(", ") : "company publisher is disabled";
7408
+ throw new SiglumeProjectError(`Company ${match.company_id} cannot publish: ${reasons}.`);
7409
+ }
7410
+ requestedCompanyId = match.company_id;
7411
+ project.manifest = {
7412
+ ...project.manifest,
7413
+ publisher_type: "company",
7414
+ company_id: requestedCompanyId,
7415
+ publisher_company_id: requestedCompanyId
7416
+ };
7417
+ }
7419
7418
  const preflight = await registrationPreflight(project, client);
7419
+ let companyPublisherPreflight = null;
7420
+ const companyId = String(project.manifest.company_id ?? "").trim() || String(project.manifest.publisher_company_id ?? "").trim();
7421
+ const publisherType = String(project.manifest.publisher_type ?? "user").toLowerCase();
7422
+ if (publisherType === "company") {
7423
+ if (!companyId) {
7424
+ throw new SiglumeProjectError("Company registration requires --company <company_id> or manifest.company_id.");
7425
+ }
7426
+ const companies = companyPublisherCandidates ?? await client.list_company_publishers();
7427
+ companyPublisherCandidates = companies;
7428
+ const company = companies.find((item) => item.company_id === companyId);
7429
+ if (!company) {
7430
+ throw new SiglumeProjectError(`Company ${companyId} is not available to this API key.`);
7431
+ }
7432
+ if (company.can_publish === false) {
7433
+ const disabledReasons = company.disabled_reasons ?? [];
7434
+ const reasons = disabledReasons.length > 0 ? disabledReasons.join(", ") : "company publisher is disabled";
7435
+ throw new SiglumeProjectError(`Company ${companyId} cannot publish: ${reasons}.`);
7436
+ }
7437
+ companyPublisherPreflight = company;
7438
+ }
7420
7439
  let developerPortalPreflight = null;
7421
7440
  if (String(project.manifest.price_model ?? "free").toLowerCase() !== "free") {
7422
- const portal = await client.get_developer_portal();
7423
- const verifiedDestination = portal.payout_readiness?.verified_destination;
7424
- if (verifiedDestination !== true) {
7425
- throw new SiglumeProjectError(
7426
- "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."
7427
- );
7441
+ if (publisherType === "company") {
7442
+ const company = companyPublisherPreflight;
7443
+ if (!company) {
7444
+ throw new SiglumeProjectError(`Company ${companyId} is not available to this API key.`);
7445
+ }
7446
+ if (company.settlement_wallet_ready !== true) {
7447
+ throw new SiglumeProjectError(
7448
+ `Paid company registration requires a verified company settlement wallet for ${company.name}. Open the company settings and complete settlement readiness before registering.`
7449
+ );
7450
+ }
7451
+ developerPortalPreflight = { company_publisher: toJsonable(company) };
7452
+ } else {
7453
+ const portal = await client.get_developer_portal();
7454
+ const verifiedDestination = portal.payout_readiness?.verified_destination;
7455
+ if (verifiedDestination !== true) {
7456
+ throw new SiglumeProjectError(
7457
+ "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."
7458
+ );
7459
+ }
7460
+ developerPortalPreflight = toJsonable(portal);
7428
7461
  }
7429
- developerPortalPreflight = toJsonable(portal);
7430
7462
  }
7431
7463
  const receipt = await client.auto_register(project.manifest, project.tool_manual, {
7432
- runtime_validation: project.runtime_validation,
7433
- oauth_credentials: canonicalOauthCredentials
7464
+ runtime_validation: project.runtime_validation
7434
7465
  });
7435
7466
  const result = {
7436
7467
  receipt: toJsonable(receipt),
7437
7468
  registration_preflight: preflight,
7438
- runtime_validation_path: project.runtime_validation_path ?? null,
7439
- oauth_credentials_path: project.oauth_credentials_path ?? null
7469
+ runtime_validation_path: project.runtime_validation_path ?? null
7440
7470
  };
7441
7471
  if (developerPortalPreflight) {
7442
7472
  result.developer_portal_preflight = developerPortalPreflight;
@@ -7457,7 +7487,6 @@ async function runPreflight(path = ".", deps = {}) {
7457
7487
  ensureExplicitToolManual(project);
7458
7488
  ensureManifestPublisherIdentity(project);
7459
7489
  ensureRuntimeValidationReady(project);
7460
- ensureRequiredOauthCredentials(project);
7461
7490
  const client = await createClient(deps);
7462
7491
  const preflight = await registrationPreflight(project, client);
7463
7492
  let developerPortalPreflight = null;
@@ -7475,8 +7504,7 @@ async function runPreflight(path = ".", deps = {}) {
7475
7504
  ok: true,
7476
7505
  adapter_path: project.adapter_path,
7477
7506
  registration_preflight: preflight,
7478
- runtime_validation_path: project.runtime_validation_path ?? null,
7479
- oauth_credentials_path: project.oauth_credentials_path ?? null
7507
+ runtime_validation_path: project.runtime_validation_path ?? null
7480
7508
  };
7481
7509
  if (developerPortalPreflight) {
7482
7510
  result.developer_portal_preflight = developerPortalPreflight;
@@ -7499,6 +7527,14 @@ async function getUsageReport(options, deps = {}) {
7499
7527
  count: items.length
7500
7528
  };
7501
7529
  }
7530
+ async function listCompanyPublishersReport(deps = {}) {
7531
+ const client = await createClient(deps);
7532
+ const companies = await client.list_company_publishers();
7533
+ return {
7534
+ companies: companies.map((item) => toJsonable(item)),
7535
+ count: companies.length
7536
+ };
7537
+ }
7502
7538
  async function diffJsonFiles(oldPath, newPath) {
7503
7539
  const oldPayload = await loadJsonDocument(oldPath);
7504
7540
  const newPayload = await loadJsonDocument(newPath);
@@ -7950,7 +7986,7 @@ function operationReadmeTemplate(operation, manifest, warning) {
7950
7986
  "- `tool_manual.json`: machine-generated ToolManual scaffold",
7951
7987
  "- `runtime_validation.json`: local public endpoint and review-key checks used by auto-register",
7952
7988
  "- `docs/api-usage.md`: publishable API usage guide template for `docs_url`",
7953
- "- `.gitignore`: keeps runtime review keys and OAuth client secrets out of Git",
7989
+ "- `.gitignore`: keeps runtime review keys out of Git",
7954
7990
  "- `tests/test_adapter.ts`: smoke test for `AppTestHarness`",
7955
7991
  "",
7956
7992
  "Before registering, replace all generated placeholders:",
@@ -7958,8 +7994,8 @@ function operationReadmeTemplate(operation, manifest, warning) {
7958
7994
  "- Replace `support_contact` with a real support email address or public support URL.",
7959
7995
  "- Optional `seller_homepage_url` is the seller's official site and can stay blank.",
7960
7996
  "- In the local `runtime_validation.json`, replace the public URL and review-key placeholders.",
7961
- "- If the API uses seller-side OAuth, create a local `oauth_credentials.json` next to the adapter.",
7962
- "- Do not commit real review keys or OAuth client secrets; the generated `.gitignore` excludes those files.",
7997
+ "- If the API uses external OAuth, implement that flow in your API runtime and keep user tokens outside Siglume.",
7998
+ "- Do not commit real review keys or external-provider secrets; the generated `.gitignore` excludes local secret files.",
7963
7999
  "- Because `runtime_validation.json` is ignored, GitHub samples do not commit review-key values.",
7964
8000
  "",
7965
8001
  "## Commands",
@@ -8035,8 +8071,6 @@ function generatedGitignore() {
8035
8071
  "!.env.example",
8036
8072
  "runtime_validation.json",
8037
8073
  "runtime-validation.json",
8038
- "oauth_credentials.json",
8039
- "oauth-credentials.json",
8040
8074
  "",
8041
8075
  "# Python / test artifacts.",
8042
8076
  "__pycache__/",
@@ -8182,13 +8216,6 @@ async function runHarnessForProject(project) {
8182
8216
  checks.push(executionCheck("quote", await harness.execute_quote(task_type, { input_params: sample_input }), harness));
8183
8217
  checks.push(executionCheck("payment", await harness.execute_payment(task_type, { input_params: sample_input }), harness));
8184
8218
  }
8185
- checks.push(
8186
- executionCheck(
8187
- "missing_account_simulation",
8188
- await harness.simulate_connected_account_missing(task_type, { input_params: sample_input }),
8189
- harness
8190
- )
8191
- );
8192
8219
  return {
8193
8220
  adapter_path: project.adapter_path,
8194
8221
  task_type,
@@ -8291,15 +8318,6 @@ async function findRuntimeValidationPath(root_dir) {
8291
8318
  }
8292
8319
  return null;
8293
8320
  }
8294
- async function findOauthCredentialsPath(root_dir) {
8295
- for (const name of ["oauth_credentials.json", "oauth-credentials.json"]) {
8296
- const candidate = join(root_dir, name);
8297
- if (existsSync(candidate)) {
8298
- return candidate;
8299
- }
8300
- }
8301
- return null;
8302
- }
8303
8321
  async function loadJsonObject(path, label) {
8304
8322
  let payload;
8305
8323
  try {
@@ -8531,15 +8549,15 @@ function readmeTemplate(template) {
8531
8549
  "- `tool_manual.json`: editable ToolManual draft for validation and registration",
8532
8550
  "- `runtime_validation.json`: local live API smoke-test contract used during registration",
8533
8551
  "- `docs/api-usage.md`: publish this page and use its public URL as `docs_url`",
8534
- "- `.gitignore`: keeps runtime review keys and OAuth client secrets out of Git",
8552
+ "- `.gitignore`: keeps runtime review keys out of Git",
8535
8553
  "",
8536
8554
  "Before registering, replace all generated placeholders:",
8537
8555
  "- In `adapter.ts` and `manifest.json`, replace `docs_url` with a dedicated public API usage guide, not a homepage.",
8538
8556
  "- Replace `support_contact` with a real support email address or public support URL.",
8539
8557
  "- Optional `seller_homepage_url` is the seller's official site and can stay blank.",
8540
8558
  "- In the local `runtime_validation.json`, replace the public URL and review-key placeholders.",
8541
- "- If the API uses seller-side OAuth, create a local `oauth_credentials.json` next to the adapter.",
8542
- "- Do not commit real review keys or OAuth client secrets; the generated `.gitignore` excludes those files.",
8559
+ "- If the API uses external OAuth, implement that flow in your API runtime and keep user tokens outside Siglume.",
8560
+ "- Do not commit real review keys or external-provider secrets; the generated `.gitignore` excludes local secret files.",
8543
8561
  "- Because `runtime_validation.json` is ignored, GitHub samples do not commit review-key values.",
8544
8562
  "",
8545
8563
  "Suggested workflow:",
@@ -8585,6 +8603,24 @@ function renderOperationTable(operations) {
8585
8603
  ...rows.map((row) => row.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join(" "))
8586
8604
  ];
8587
8605
  }
8606
+ function renderCompanyTable(companies) {
8607
+ const rows = companies.map((item) => [
8608
+ String(item.company_id ?? item.id ?? ""),
8609
+ String(item.name ?? ""),
8610
+ String(item.membership_role ?? (item.is_founder ? "founder" : "")),
8611
+ String(item.settlement_wallet_ready === true ? "ready" : "not_ready"),
8612
+ String(item.pending_approval_count ?? 0)
8613
+ ]);
8614
+ const headers = ["company_id", "name", "role", "settlement", "pending"];
8615
+ const widths = headers.map(
8616
+ (header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0))
8617
+ );
8618
+ return [
8619
+ headers.map((header, index) => header.padEnd(widths[index] ?? header.length)).join(" "),
8620
+ widths.map((width) => "-".repeat(width)).join(" "),
8621
+ ...rows.map((row) => row.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join(" "))
8622
+ ];
8623
+ }
8588
8624
  async function runCli(argv, deps = {}) {
8589
8625
  const stdout = deps.stdout;
8590
8626
  const stderr = deps.stderr ?? console.error;
@@ -8739,9 +8775,22 @@ async function runCli(argv, deps = {}) {
8739
8775
  emit(stdout, `preflight_quality: ${preflight.remote_quality.grade} (${preflight.remote_quality.overall_score}/100)`);
8740
8776
  }
8741
8777
  if (report.runtime_validation_path) emit(stdout, `runtime_validation_path: ${String(report.runtime_validation_path)}`);
8742
- if (report.oauth_credentials_path) emit(stdout, `oauth_credentials_path: ${String(report.oauth_credentials_path)}`);
8743
8778
  });
8744
- 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) => {
8779
+ program.command("companies").description("List Siglume companies available for company-name publishing.").option("--json", "emit machine-readable JSON", false).action(async (options) => {
8780
+ const report = await listCompanyPublishersReport(deps);
8781
+ if (options.json) {
8782
+ emit(stdout, renderJson(report));
8783
+ return;
8784
+ }
8785
+ const companies = Array.isArray(report.companies) ? report.companies.filter((item) => Boolean(item && typeof item === "object")) : [];
8786
+ if (companies.length === 0) {
8787
+ emit(stdout, "No company publishers available for this API key.");
8788
+ return;
8789
+ }
8790
+ emit(stdout, "Company publishers");
8791
+ renderCompanyTable(companies).forEach((line) => emit(stdout, line));
8792
+ });
8793
+ 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) => {
8745
8794
  const draftOnly = Boolean(options.draftOnly);
8746
8795
  if (draftOnly && options.confirm) {
8747
8796
  throw new SiglumeProjectError("--draft-only cannot be combined with --confirm.");
@@ -8750,7 +8799,13 @@ async function runCli(argv, deps = {}) {
8750
8799
  throw new SiglumeProjectError("--draft-only cannot be combined with --submit-review.");
8751
8800
  }
8752
8801
  const shouldConfirm = Boolean(options.confirm) || !draftOnly && !options.submitReview;
8753
- const report = await runRegistration(path, { confirm: shouldConfirm, draft_only: draftOnly, submit_review: options.submitReview }, deps);
8802
+ const report = await runRegistration(path, {
8803
+ confirm: shouldConfirm,
8804
+ draft_only: draftOnly,
8805
+ submit_review: options.submitReview,
8806
+ company_id: options.company,
8807
+ company_slug: options.companySlug
8808
+ }, deps);
8754
8809
  if (options.json) {
8755
8810
  emit(stdout, renderJson(report));
8756
8811
  } else {
@@ -8770,7 +8825,6 @@ async function runCli(argv, deps = {}) {
8770
8825
  emit(stdout, `listing_id: ${receipt.listing_id}`);
8771
8826
  emit(stdout, `receipt_status: ${receipt.status}`);
8772
8827
  if (receipt.listing_status) emit(stdout, `listing_status: ${receipt.listing_status}`);
8773
- if (receipt.oauth_status) emit(stdout, `oauth_configured: ${Boolean(receipt.oauth_status.configured)}`);
8774
8828
  if (receipt.review_url) emit(stdout, `review_url: ${receipt.review_url}`);
8775
8829
  if (receipt.trace_id) emit(stdout, `trace_id: ${receipt.trace_id}`);
8776
8830
  if (receipt.request_id) emit(stdout, `request_id: ${receipt.request_id}`);