@siglume/api-sdk 0.10.7 → 0.10.8

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.
@@ -144,6 +144,27 @@ declare const ListingCurrency: {
144
144
  readonly JPY: "JPY";
145
145
  };
146
146
  type ListingCurrency = (typeof ListingCurrency)[keyof typeof ListingCurrency];
147
+ declare const PersistenceMode: {
148
+ readonly NONE: "none";
149
+ readonly LOCAL: "local";
150
+ readonly PLATFORM: "platform";
151
+ readonly DEVELOPER_SERVER: "developer_server";
152
+ };
153
+ type PersistenceMode = (typeof PersistenceMode)[keyof typeof PersistenceMode];
154
+ interface CapabilityPersistencePolicy {
155
+ mode: PersistenceMode;
156
+ schema_version?: string;
157
+ scope?: string;
158
+ restore_required?: boolean;
159
+ max_bytes?: number;
160
+ endpoint?: string | null;
161
+ description?: string;
162
+ /**
163
+ * JSON Schema for persisted game save data.
164
+ * Required when store_vertical is "game" and mode is not "none".
165
+ */
166
+ save_data_schema?: Record<string, unknown>;
167
+ }
147
168
  interface AppManifest {
148
169
  capability_key: string;
149
170
  version?: string;
@@ -169,10 +190,14 @@ interface AppManifest {
169
190
  support_contact?: string;
170
191
  seller_homepage_url?: string;
171
192
  seller_social_url?: string;
193
+ publisher_type?: "user" | "company";
194
+ company_id?: string;
195
+ publisher_company_id?: string;
172
196
  store_vertical: StoreVertical;
173
197
  compatibility_tags?: string[];
174
198
  example_prompts?: string[];
175
199
  latency_tier?: string;
200
+ persistence?: CapabilityPersistencePolicy;
176
201
  }
177
202
  declare const ToolManualPermissionClass: {
178
203
  readonly READ_ONLY: "read_only";
@@ -267,13 +292,53 @@ interface AppListingRecord {
267
292
  seller_display_name?: string | null;
268
293
  seller_homepage_url?: string | null;
269
294
  seller_social_url?: string | null;
295
+ publisher_type?: string | null;
296
+ publisher_company_id?: string | null;
297
+ company_id?: string | null;
298
+ company_name?: string | null;
299
+ company_publish_status?: string | null;
300
+ company_terms_version?: string | null;
270
301
  review_status?: string | null;
271
302
  review_note?: string | null;
272
303
  submission_blockers: string[];
304
+ persistence: Record<string, unknown>;
273
305
  created_at?: string | null;
274
306
  updated_at?: string | null;
275
307
  raw: Record<string, unknown>;
276
308
  }
309
+ interface CompanyPublisherRecord {
310
+ company_id: string;
311
+ name: string;
312
+ status: string;
313
+ description?: string | null;
314
+ is_founder: boolean;
315
+ membership_role?: string | null;
316
+ membership_status?: string | null;
317
+ can_publish: boolean;
318
+ can_approve: boolean;
319
+ approval_required: boolean;
320
+ paid_listing_allowed: boolean;
321
+ disabled_reasons: string[];
322
+ company_terms_version?: string | null;
323
+ active_listing_count: number;
324
+ pending_approval_count: number;
325
+ settlement_wallet_ready: boolean;
326
+ settlement_wallets: Array<Record<string, unknown>>;
327
+ raw: Record<string, unknown>;
328
+ }
329
+ interface CapabilitySaveStateRecord {
330
+ capability_key: string;
331
+ save_key: string;
332
+ schema_version: string;
333
+ revision: number;
334
+ payload: Record<string, unknown>;
335
+ metadata: Record<string, unknown>;
336
+ checksum?: string | null;
337
+ updated_at?: string | null;
338
+ created_at?: string | null;
339
+ exists: boolean;
340
+ raw: Record<string, unknown>;
341
+ }
277
342
  interface ConnectedAccountOAuthStart {
278
343
  authorize_url: string;
279
344
  state: string;
@@ -1363,6 +1428,19 @@ interface SiglumeClientShape {
1363
1428
  cursor?: string;
1364
1429
  }): Promise<CursorPage<AppListingRecord>>;
1365
1430
  get_listing(listing_id: string): Promise<AppListingRecord>;
1431
+ list_company_publishers(): Promise<CompanyPublisherRecord[]>;
1432
+ request_company_publish_approval(listing_id: string, note?: string): Promise<AppListingRecord>;
1433
+ decide_company_publish_approval(listing_id: string, options: {
1434
+ decision: "approve" | "reject";
1435
+ reason?: string;
1436
+ }): Promise<AppListingRecord>;
1437
+ get_capability_state(capability_key: string, save_key?: string): Promise<CapabilitySaveStateRecord>;
1438
+ put_capability_state(capability_key: string, save_key?: string, payload?: Record<string, unknown>, options?: {
1439
+ schema_version?: string;
1440
+ expected_revision?: number | null;
1441
+ metadata?: Record<string, unknown>;
1442
+ }): Promise<CapabilitySaveStateRecord>;
1443
+ delete_capability_state(capability_key: string, save_key?: string): Promise<CapabilitySaveStateRecord>;
1366
1444
  list_capabilities(options?: {
1367
1445
  mine?: boolean;
1368
1446
  status?: string;
@@ -144,6 +144,27 @@ declare const ListingCurrency: {
144
144
  readonly JPY: "JPY";
145
145
  };
146
146
  type ListingCurrency = (typeof ListingCurrency)[keyof typeof ListingCurrency];
147
+ declare const PersistenceMode: {
148
+ readonly NONE: "none";
149
+ readonly LOCAL: "local";
150
+ readonly PLATFORM: "platform";
151
+ readonly DEVELOPER_SERVER: "developer_server";
152
+ };
153
+ type PersistenceMode = (typeof PersistenceMode)[keyof typeof PersistenceMode];
154
+ interface CapabilityPersistencePolicy {
155
+ mode: PersistenceMode;
156
+ schema_version?: string;
157
+ scope?: string;
158
+ restore_required?: boolean;
159
+ max_bytes?: number;
160
+ endpoint?: string | null;
161
+ description?: string;
162
+ /**
163
+ * JSON Schema for persisted game save data.
164
+ * Required when store_vertical is "game" and mode is not "none".
165
+ */
166
+ save_data_schema?: Record<string, unknown>;
167
+ }
147
168
  interface AppManifest {
148
169
  capability_key: string;
149
170
  version?: string;
@@ -169,10 +190,14 @@ interface AppManifest {
169
190
  support_contact?: string;
170
191
  seller_homepage_url?: string;
171
192
  seller_social_url?: string;
193
+ publisher_type?: "user" | "company";
194
+ company_id?: string;
195
+ publisher_company_id?: string;
172
196
  store_vertical: StoreVertical;
173
197
  compatibility_tags?: string[];
174
198
  example_prompts?: string[];
175
199
  latency_tier?: string;
200
+ persistence?: CapabilityPersistencePolicy;
176
201
  }
177
202
  declare const ToolManualPermissionClass: {
178
203
  readonly READ_ONLY: "read_only";
@@ -267,13 +292,53 @@ interface AppListingRecord {
267
292
  seller_display_name?: string | null;
268
293
  seller_homepage_url?: string | null;
269
294
  seller_social_url?: string | null;
295
+ publisher_type?: string | null;
296
+ publisher_company_id?: string | null;
297
+ company_id?: string | null;
298
+ company_name?: string | null;
299
+ company_publish_status?: string | null;
300
+ company_terms_version?: string | null;
270
301
  review_status?: string | null;
271
302
  review_note?: string | null;
272
303
  submission_blockers: string[];
304
+ persistence: Record<string, unknown>;
273
305
  created_at?: string | null;
274
306
  updated_at?: string | null;
275
307
  raw: Record<string, unknown>;
276
308
  }
309
+ interface CompanyPublisherRecord {
310
+ company_id: string;
311
+ name: string;
312
+ status: string;
313
+ description?: string | null;
314
+ is_founder: boolean;
315
+ membership_role?: string | null;
316
+ membership_status?: string | null;
317
+ can_publish: boolean;
318
+ can_approve: boolean;
319
+ approval_required: boolean;
320
+ paid_listing_allowed: boolean;
321
+ disabled_reasons: string[];
322
+ company_terms_version?: string | null;
323
+ active_listing_count: number;
324
+ pending_approval_count: number;
325
+ settlement_wallet_ready: boolean;
326
+ settlement_wallets: Array<Record<string, unknown>>;
327
+ raw: Record<string, unknown>;
328
+ }
329
+ interface CapabilitySaveStateRecord {
330
+ capability_key: string;
331
+ save_key: string;
332
+ schema_version: string;
333
+ revision: number;
334
+ payload: Record<string, unknown>;
335
+ metadata: Record<string, unknown>;
336
+ checksum?: string | null;
337
+ updated_at?: string | null;
338
+ created_at?: string | null;
339
+ exists: boolean;
340
+ raw: Record<string, unknown>;
341
+ }
277
342
  interface ConnectedAccountOAuthStart {
278
343
  authorize_url: string;
279
344
  state: string;
@@ -1363,6 +1428,19 @@ interface SiglumeClientShape {
1363
1428
  cursor?: string;
1364
1429
  }): Promise<CursorPage<AppListingRecord>>;
1365
1430
  get_listing(listing_id: string): Promise<AppListingRecord>;
1431
+ list_company_publishers(): Promise<CompanyPublisherRecord[]>;
1432
+ request_company_publish_approval(listing_id: string, note?: string): Promise<AppListingRecord>;
1433
+ decide_company_publish_approval(listing_id: string, options: {
1434
+ decision: "approve" | "reject";
1435
+ reason?: string;
1436
+ }): Promise<AppListingRecord>;
1437
+ get_capability_state(capability_key: string, save_key?: string): Promise<CapabilitySaveStateRecord>;
1438
+ put_capability_state(capability_key: string, save_key?: string, payload?: Record<string, unknown>, options?: {
1439
+ schema_version?: string;
1440
+ expected_revision?: number | null;
1441
+ metadata?: Record<string, unknown>;
1442
+ }): Promise<CapabilitySaveStateRecord>;
1443
+ delete_capability_state(capability_key: string, save_key?: string): Promise<CapabilitySaveStateRecord>;
1366
1444
  list_capabilities(options?: {
1367
1445
  mine?: boolean;
1368
1446
  status?: string;
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 ?? ""),
@@ -2509,6 +2606,9 @@ var init_client = __esm({
2509
2606
  "support_contact",
2510
2607
  "seller_homepage_url",
2511
2608
  "seller_social_url",
2609
+ "publisher_type",
2610
+ "company_id",
2611
+ "publisher_company_id",
2512
2612
  "store_vertical",
2513
2613
  "jurisdiction",
2514
2614
  "price_model",
@@ -2521,7 +2621,8 @@ var init_client = __esm({
2521
2621
  "dry_run_supported",
2522
2622
  "required_connected_accounts",
2523
2623
  "permission_scopes",
2524
- "compatibility_tags"
2624
+ "compatibility_tags",
2625
+ "persistence"
2525
2626
  ]) {
2526
2627
  const value = manifestPayload[fieldName];
2527
2628
  if (value !== void 0 && value !== null) {
@@ -2561,6 +2662,26 @@ var init_client = __esm({
2561
2662
  );
2562
2663
  }
2563
2664
  }
2665
+ const explicitPublisherType = payload.publisher_type !== void 0 && payload.publisher_type !== null;
2666
+ const companyId = String(payload.company_id ?? "").trim() || String(payload.publisher_company_id ?? "").trim();
2667
+ const publisherType = String(payload.publisher_type ?? "user").trim().toLowerCase();
2668
+ if (publisherType !== "user" && publisherType !== "company") {
2669
+ throw new SiglumeClientError("AppManifest.publisher_type must be 'user' or 'company'.");
2670
+ }
2671
+ if (publisherType === "company" && !companyId) {
2672
+ throw new SiglumeClientError("AppManifest.company_id is required when publisher_type='company'.");
2673
+ }
2674
+ if (publisherType === "user" && companyId) {
2675
+ throw new SiglumeClientError("AppManifest.company_id cannot be combined with publisher_type='user'.");
2676
+ }
2677
+ if (explicitPublisherType || companyId) {
2678
+ payload.publisher_type = publisherType;
2679
+ }
2680
+ if (companyId) {
2681
+ payload.company_id = companyId;
2682
+ payload.publisher_company_id = companyId;
2683
+ }
2684
+ validateManifestPersistenceContract(payload);
2564
2685
  if (payload.manifest && typeof payload.manifest === "object") {
2565
2686
  delete payload.manifest.version;
2566
2687
  }
@@ -2668,6 +2789,45 @@ var init_client = __esm({
2668
2789
  const [data] = await this.request("GET", `/market/capabilities/${listing_id}`);
2669
2790
  return parseListing(data);
2670
2791
  }
2792
+ async list_company_publishers() {
2793
+ const [data] = await this.request("GET", "/market/company-publishers");
2794
+ return Array.isArray(data.items) ? data.items.filter((item) => isRecord(item)).map(parseCompanyPublisher) : [];
2795
+ }
2796
+ async request_company_publish_approval(listing_id, note) {
2797
+ const [data] = await this.request("POST", `/market/capabilities/${listing_id}/company-publish-approval`, {
2798
+ json_body: note ? { note } : {}
2799
+ });
2800
+ return parseListing(data);
2801
+ }
2802
+ async decide_company_publish_approval(listing_id, options) {
2803
+ const [data] = await this.request("POST", `/market/capabilities/${listing_id}/company-publish-approval/decision`, {
2804
+ json_body: {
2805
+ decision: options.decision,
2806
+ ...options.reason ? { reason: options.reason } : {}
2807
+ }
2808
+ });
2809
+ return parseListing(data);
2810
+ }
2811
+ async get_capability_state(capability_key, save_key = "default") {
2812
+ const [data] = await this.request("GET", `/market/capability-state/${capability_key}/${save_key}`);
2813
+ return parseCapabilitySaveState(data);
2814
+ }
2815
+ async put_capability_state(capability_key, save_key = "default", payload = {}, options = {}) {
2816
+ const body = {
2817
+ payload: toRecord(payload),
2818
+ schema_version: options.schema_version ?? "1",
2819
+ metadata: toRecord(options.metadata)
2820
+ };
2821
+ if (options.expected_revision !== void 0 && options.expected_revision !== null) {
2822
+ body.expected_revision = Math.trunc(options.expected_revision);
2823
+ }
2824
+ const [data] = await this.request("PUT", `/market/capability-state/${capability_key}/${save_key}`, { json_body: body });
2825
+ return parseCapabilitySaveState(data);
2826
+ }
2827
+ async delete_capability_state(capability_key, save_key = "default") {
2828
+ const [data] = await this.request("DELETE", `/market/capability-state/${capability_key}/${save_key}`);
2829
+ return parseCapabilitySaveState(data);
2830
+ }
2671
2831
  // ----- Capability bundles (v0.7 track 2) ---------------------------------
2672
2832
  async list_bundles(options = {}) {
2673
2833
  const params = {
@@ -7262,7 +7422,12 @@ function ensureManifestPublisherIdentity(project) {
7262
7422
  const sellerHomepageUrl = String(manifestPayload.seller_homepage_url ?? "").trim();
7263
7423
  const sellerSocialUrl = String(manifestPayload.seller_social_url ?? "").trim();
7264
7424
  const jurisdiction = String(manifestPayload.jurisdiction ?? "").trim();
7425
+ const companyId = String(manifestPayload.company_id ?? "").trim() || String(manifestPayload.publisher_company_id ?? "").trim();
7426
+ const publisherType = String(manifestPayload.publisher_type ?? "user").trim().toLowerCase();
7265
7427
  const issues = [];
7428
+ if (companyId && publisherType !== "company") {
7429
+ issues.push('manifest.company_id requires manifest.publisher_type to be "company"');
7430
+ }
7266
7431
  if (!docsUrl) {
7267
7432
  issues.push("manifest.docs_url is required");
7268
7433
  } else if (looksLikePlaceholder(docsUrl)) {
@@ -7408,25 +7573,110 @@ ${errors.map((error) => `- ${error}`).join("\n")}`
7408
7573
  }
7409
7574
  return preflight;
7410
7575
  }
7576
+ function companyNameSlug(value) {
7577
+ return value.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7578
+ }
7411
7579
  async function runRegistration(path = ".", options = {}, deps = {}) {
7412
7580
  const project = await loadProject(path);
7581
+ let requestedCompanyId = String(options.company_id ?? "").trim();
7582
+ const requestedCompanySlug = String(options.company_slug ?? "").trim();
7583
+ let companyPublisherCandidates = null;
7584
+ if (requestedCompanySlug) {
7585
+ if (requestedCompanyId) {
7586
+ throw new SiglumeProjectError("--company and --company-slug cannot be combined.");
7587
+ }
7588
+ const slug = companyNameSlug(requestedCompanySlug);
7589
+ if (!slug && requestedCompanySlug !== requestedCompanyId) {
7590
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not slug-compatible; use --company <company_id> instead.`);
7591
+ }
7592
+ }
7593
+ if (requestedCompanyId) {
7594
+ project.manifest = {
7595
+ ...project.manifest,
7596
+ publisher_type: "company",
7597
+ company_id: requestedCompanyId,
7598
+ publisher_company_id: requestedCompanyId
7599
+ };
7600
+ }
7413
7601
  ensureExplicitToolManual(project);
7414
7602
  ensureManifestPublisherIdentity(project);
7415
7603
  ensureRuntimeValidationReady(project);
7416
7604
  ensureRequiredOauthCredentials(project);
7417
7605
  const canonicalOauthCredentials = canonicalOauthCredentialsPayload(project.oauth_credentials);
7418
7606
  const client = await createClient(deps);
7607
+ if (requestedCompanySlug) {
7608
+ const slug = companyNameSlug(requestedCompanySlug);
7609
+ companyPublisherCandidates = await client.list_company_publishers();
7610
+ const matches = companyPublisherCandidates.filter(
7611
+ (item) => companyNameSlug(item.name || item.company_id) === slug || item.company_id === requestedCompanySlug
7612
+ );
7613
+ if (matches.length === 0) {
7614
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not available to this API key.`);
7615
+ }
7616
+ if (matches.length > 1) {
7617
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is ambiguous; use --company <company_id> instead.`);
7618
+ }
7619
+ const match = matches[0];
7620
+ if (!match) {
7621
+ throw new SiglumeProjectError(`Company slug ${requestedCompanySlug} is not available to this API key.`);
7622
+ }
7623
+ if (match.can_publish === false) {
7624
+ const disabledReasons = match.disabled_reasons ?? [];
7625
+ const reasons = disabledReasons.length > 0 ? disabledReasons.join(", ") : "company publisher is disabled";
7626
+ throw new SiglumeProjectError(`Company ${match.company_id} cannot publish: ${reasons}.`);
7627
+ }
7628
+ requestedCompanyId = match.company_id;
7629
+ project.manifest = {
7630
+ ...project.manifest,
7631
+ publisher_type: "company",
7632
+ company_id: requestedCompanyId,
7633
+ publisher_company_id: requestedCompanyId
7634
+ };
7635
+ }
7419
7636
  const preflight = await registrationPreflight(project, client);
7637
+ let companyPublisherPreflight = null;
7638
+ const companyId = String(project.manifest.company_id ?? "").trim() || String(project.manifest.publisher_company_id ?? "").trim();
7639
+ const publisherType = String(project.manifest.publisher_type ?? "user").toLowerCase();
7640
+ if (publisherType === "company") {
7641
+ if (!companyId) {
7642
+ throw new SiglumeProjectError("Company registration requires --company <company_id> or manifest.company_id.");
7643
+ }
7644
+ const companies = companyPublisherCandidates ?? await client.list_company_publishers();
7645
+ companyPublisherCandidates = companies;
7646
+ const company = companies.find((item) => item.company_id === companyId);
7647
+ if (!company) {
7648
+ throw new SiglumeProjectError(`Company ${companyId} is not available to this API key.`);
7649
+ }
7650
+ if (company.can_publish === false) {
7651
+ const disabledReasons = company.disabled_reasons ?? [];
7652
+ const reasons = disabledReasons.length > 0 ? disabledReasons.join(", ") : "company publisher is disabled";
7653
+ throw new SiglumeProjectError(`Company ${companyId} cannot publish: ${reasons}.`);
7654
+ }
7655
+ companyPublisherPreflight = company;
7656
+ }
7420
7657
  let developerPortalPreflight = null;
7421
7658
  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
- );
7659
+ if (publisherType === "company") {
7660
+ const company = companyPublisherPreflight;
7661
+ if (!company) {
7662
+ throw new SiglumeProjectError(`Company ${companyId} is not available to this API key.`);
7663
+ }
7664
+ if (company.settlement_wallet_ready !== true) {
7665
+ throw new SiglumeProjectError(
7666
+ `Paid company registration requires a verified company settlement wallet for ${company.name}. Open the company settings and complete settlement readiness before registering.`
7667
+ );
7668
+ }
7669
+ developerPortalPreflight = { company_publisher: toJsonable(company) };
7670
+ } else {
7671
+ const portal = await client.get_developer_portal();
7672
+ const verifiedDestination = portal.payout_readiness?.verified_destination;
7673
+ if (verifiedDestination !== true) {
7674
+ throw new SiglumeProjectError(
7675
+ "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."
7676
+ );
7677
+ }
7678
+ developerPortalPreflight = toJsonable(portal);
7428
7679
  }
7429
- developerPortalPreflight = toJsonable(portal);
7430
7680
  }
7431
7681
  const receipt = await client.auto_register(project.manifest, project.tool_manual, {
7432
7682
  runtime_validation: project.runtime_validation,
@@ -7499,6 +7749,14 @@ async function getUsageReport(options, deps = {}) {
7499
7749
  count: items.length
7500
7750
  };
7501
7751
  }
7752
+ async function listCompanyPublishersReport(deps = {}) {
7753
+ const client = await createClient(deps);
7754
+ const companies = await client.list_company_publishers();
7755
+ return {
7756
+ companies: companies.map((item) => toJsonable(item)),
7757
+ count: companies.length
7758
+ };
7759
+ }
7502
7760
  async function diffJsonFiles(oldPath, newPath) {
7503
7761
  const oldPayload = await loadJsonDocument(oldPath);
7504
7762
  const newPayload = await loadJsonDocument(newPath);
@@ -8585,6 +8843,24 @@ function renderOperationTable(operations) {
8585
8843
  ...rows.map((row) => row.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join(" "))
8586
8844
  ];
8587
8845
  }
8846
+ function renderCompanyTable(companies) {
8847
+ const rows = companies.map((item) => [
8848
+ String(item.company_id ?? item.id ?? ""),
8849
+ String(item.name ?? ""),
8850
+ String(item.membership_role ?? (item.is_founder ? "founder" : "")),
8851
+ String(item.settlement_wallet_ready === true ? "ready" : "not_ready"),
8852
+ String(item.pending_approval_count ?? 0)
8853
+ ]);
8854
+ const headers = ["company_id", "name", "role", "settlement", "pending"];
8855
+ const widths = headers.map(
8856
+ (header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0))
8857
+ );
8858
+ return [
8859
+ headers.map((header, index) => header.padEnd(widths[index] ?? header.length)).join(" "),
8860
+ widths.map((width) => "-".repeat(width)).join(" "),
8861
+ ...rows.map((row) => row.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join(" "))
8862
+ ];
8863
+ }
8588
8864
  async function runCli(argv, deps = {}) {
8589
8865
  const stdout = deps.stdout;
8590
8866
  const stderr = deps.stderr ?? console.error;
@@ -8741,7 +9017,21 @@ async function runCli(argv, deps = {}) {
8741
9017
  if (report.runtime_validation_path) emit(stdout, `runtime_validation_path: ${String(report.runtime_validation_path)}`);
8742
9018
  if (report.oauth_credentials_path) emit(stdout, `oauth_credentials_path: ${String(report.oauth_credentials_path)}`);
8743
9019
  });
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) => {
9020
+ program.command("companies").description("List Siglume companies available for company-name publishing.").option("--json", "emit machine-readable JSON", false).action(async (options) => {
9021
+ const report = await listCompanyPublishersReport(deps);
9022
+ if (options.json) {
9023
+ emit(stdout, renderJson(report));
9024
+ return;
9025
+ }
9026
+ const companies = Array.isArray(report.companies) ? report.companies.filter((item) => Boolean(item && typeof item === "object")) : [];
9027
+ if (companies.length === 0) {
9028
+ emit(stdout, "No company publishers available for this API key.");
9029
+ return;
9030
+ }
9031
+ emit(stdout, "Company publishers");
9032
+ renderCompanyTable(companies).forEach((line) => emit(stdout, line));
9033
+ });
9034
+ 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
9035
  const draftOnly = Boolean(options.draftOnly);
8746
9036
  if (draftOnly && options.confirm) {
8747
9037
  throw new SiglumeProjectError("--draft-only cannot be combined with --confirm.");
@@ -8750,7 +9040,13 @@ async function runCli(argv, deps = {}) {
8750
9040
  throw new SiglumeProjectError("--draft-only cannot be combined with --submit-review.");
8751
9041
  }
8752
9042
  const shouldConfirm = Boolean(options.confirm) || !draftOnly && !options.submitReview;
8753
- const report = await runRegistration(path, { confirm: shouldConfirm, draft_only: draftOnly, submit_review: options.submitReview }, deps);
9043
+ const report = await runRegistration(path, {
9044
+ confirm: shouldConfirm,
9045
+ draft_only: draftOnly,
9046
+ submit_review: options.submitReview,
9047
+ company_id: options.company,
9048
+ company_slug: options.companySlug
9049
+ }, deps);
8754
9050
  if (options.json) {
8755
9051
  emit(stdout, renderJson(report));
8756
9052
  } else {