@fairmint/captable-ai 0.1.5 → 0.1.6

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +401 -77
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -88,7 +88,7 @@ automated, but a few habits make extraction faster and more accurate:
88
88
  | `documents upload\|list\|download\|delete` | Manage source documents |
89
89
  | `documents trigger-extraction\|cancel-extraction\|restart-extraction\|reprocess` | Control the OCF extraction pipeline |
90
90
  | `documents processing-status\|classification-types\|classification-update` | Inspect document status / classification |
91
- | `objects list\|get\|update\|patch\|revert\|versions\|delete` | Work with extracted OCF objects (`list` supports `--document`, `--stakeholder`, `--all`, `--max-confidence`) |
91
+ | `objects list\|get\|dump\|update\|patch\|revert\|versions\|delete` | Work with extracted OCF objects (`list` supports `--document`, `--stakeholder`, `--all`, `--max-confidence`; `dump` writes engine-shaped `ocf.json`) |
92
92
  | `schemas list\|get` | Browse the OCF object-type registry |
93
93
  | `commit` | Commit drafts to the cap table — `--all` or `--objects id1,id2` (`--dry-run` to preview) |
94
94
  | `summary` | Full cap table snapshot |
package/dist/index.js CHANGED
@@ -607,6 +607,27 @@ var COMMAND_REGISTRY = [
607
607
  requiresAuth: true,
608
608
  requiresPortal: true
609
609
  },
610
+ {
611
+ name: "objects dump",
612
+ group: "captable",
613
+ description: "Write engine-shaped ocf.json from object ocf payloads.",
614
+ usage: "captable-ai objects dump [--status committed|draft] [--output path]",
615
+ requiresAuth: true,
616
+ requiresPortal: true,
617
+ flags: [
618
+ {
619
+ name: "status",
620
+ description: "Filter by status: draft or committed. Default committed.",
621
+ type: "string",
622
+ enum: ["draft", "committed"]
623
+ },
624
+ {
625
+ name: "output",
626
+ description: "Write JSON to this file (global --output; implies --json).",
627
+ type: "string"
628
+ }
629
+ ]
630
+ },
610
631
  {
611
632
  name: "schemas list",
612
633
  group: "captable",
@@ -1147,7 +1168,7 @@ var COMMAND_REGISTRY = [
1147
1168
  }
1148
1169
  ];
1149
1170
  async function runCommands(_args) {
1150
- printJson({ schemaVersion: "7", version: "0.0.1", commands: COMMAND_REGISTRY });
1171
+ printJson({ schemaVersion: "8", version: "0.0.1", commands: COMMAND_REGISTRY });
1151
1172
  }
1152
1173
 
1153
1174
  // ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
@@ -16043,6 +16064,235 @@ var exportResultSchema = external_exports.object({
16043
16064
  }).optional().describe("Present when pro-forma sheets were requested but engine refused.")
16044
16065
  });
16045
16066
 
16067
+ // ../captable-ai-core/dist/objects-dump.js
16068
+ var DumpAssemblyError = class extends Error {
16069
+ constructor(message) {
16070
+ super(message);
16071
+ this.name = "DumpAssemblyError";
16072
+ }
16073
+ };
16074
+ var ISSUANCE_TYPES = /* @__PURE__ */ new Set([
16075
+ "TX_STOCK_ISSUANCE",
16076
+ "TX_CONVERTIBLE_ISSUANCE",
16077
+ "TX_WARRANT_ISSUANCE",
16078
+ "TX_EQUITY_COMPENSATION_ISSUANCE",
16079
+ "TX_PLAN_SECURITY_ISSUANCE"
16080
+ ]);
16081
+ var CONVERSION_PARENT_TYPES = /* @__PURE__ */ new Set([
16082
+ "TX_CONVERTIBLE_CONVERSION",
16083
+ "TX_WARRANT_EXERCISE",
16084
+ "TX_EQUITY_COMPENSATION_EXERCISE",
16085
+ "TX_EQUITY_COMPENSATION_RELEASE",
16086
+ "TX_PLAN_SECURITY_RELEASE",
16087
+ "TX_STOCK_TRANSFER",
16088
+ "TX_STOCK_CONVERSION",
16089
+ "TX_CONVERTIBLE_TRANSFER",
16090
+ "TX_WARRANT_TRANSFER",
16091
+ "TX_EQUITY_COMPENSATION_TRANSFER",
16092
+ "TX_PLAN_SECURITY_TRANSFER",
16093
+ "TX_STOCK_REISSUANCE"
16094
+ ]);
16095
+ function isRecord(value) {
16096
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16097
+ }
16098
+ function canonicalType(obj) {
16099
+ const type = String(obj.type ?? "").toUpperCase();
16100
+ if (type === "OBJECT")
16101
+ return String(obj.subtype ?? "").toUpperCase();
16102
+ return type;
16103
+ }
16104
+ function issuerForEngine(payload) {
16105
+ if (payload.initial_shares_authorized == null && payload.authorized_shares != null) {
16106
+ return { ...payload, initial_shares_authorized: payload.authorized_shares };
16107
+ }
16108
+ return payload;
16109
+ }
16110
+ function payloadId(payload, fallback) {
16111
+ return typeof payload.id === "string" && payload.id.length > 0 ? payload.id : fallback;
16112
+ }
16113
+ function pushByOcfId(bucket, payload, fallback) {
16114
+ bucket.set(payloadId(payload, fallback), payload);
16115
+ }
16116
+ function timestampMs(input, defaultValue) {
16117
+ if (input == null)
16118
+ return defaultValue;
16119
+ const ms = typeof input === "number" ? input : new Date(String(input)).getTime();
16120
+ return Number.isNaN(ms) ? defaultValue : ms;
16121
+ }
16122
+ function dumpTxWeight(tx, conversionResultSecurityIds) {
16123
+ const objectType = String(tx.object_type ?? "");
16124
+ if (conversionResultSecurityIds.size > 0 && ISSUANCE_TYPES.has(objectType) && typeof tx.security_id === "string" && conversionResultSecurityIds.has(tx.security_id)) {
16125
+ return 36;
16126
+ }
16127
+ switch (objectType) {
16128
+ case "TX_ISSUER_AUTHORIZED_SHARES_ADJUSTMENT":
16129
+ case "TX_STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT":
16130
+ case "TX_STOCK_PLAN_POOL_ADJUSTMENT":
16131
+ return 5;
16132
+ case "TX_STOCK_REISSUANCE":
16133
+ return 9;
16134
+ case "TX_STOCK_ISSUANCE":
16135
+ case "TX_EQUITY_COMPENSATION_ISSUANCE":
16136
+ case "TX_WARRANT_ISSUANCE":
16137
+ case "TX_CONVERTIBLE_ISSUANCE":
16138
+ case "TX_PLAN_SECURITY_ISSUANCE":
16139
+ return 10;
16140
+ case "TX_STOCK_ACCEPTANCE":
16141
+ case "TX_WARRANT_ACCEPTANCE":
16142
+ case "TX_EQUITY_COMPENSATION_ACCEPTANCE":
16143
+ case "TX_PLAN_SECURITY_ACCEPTANCE":
16144
+ return 11;
16145
+ case "TX_VESTING_START":
16146
+ return 12;
16147
+ case "TX_VESTING_EVENT":
16148
+ return 13;
16149
+ case "TX_VESTING_ACCELERATION":
16150
+ return 14;
16151
+ case "TX_STOCK_CLASS_SPLIT":
16152
+ return 15;
16153
+ case "TX_STOCK_RETRACTION":
16154
+ case "TX_CONVERTIBLE_RETRACTION":
16155
+ case "TX_WARRANT_RETRACTION":
16156
+ case "TX_EQUITY_COMPENSATION_RETRACTION":
16157
+ return 16;
16158
+ case "TX_STOCK_CONSOLIDATION":
16159
+ return 17;
16160
+ case "TX_STOCK_CLASS_CONVERSION_RATIO_ADJUSTMENT":
16161
+ return 18;
16162
+ case "TX_EQUITY_COMPENSATION_REPRICING":
16163
+ return 19;
16164
+ case "TX_STOCK_TRANSFER":
16165
+ case "TX_WARRANT_TRANSFER":
16166
+ case "TX_CONVERTIBLE_TRANSFER":
16167
+ case "TX_EQUITY_COMPENSATION_TRANSFER":
16168
+ case "TX_PLAN_SECURITY_TRANSFER":
16169
+ return 20;
16170
+ case "TX_CONVERTIBLE_ACCEPTANCE":
16171
+ return 22;
16172
+ case "TX_EQUITY_COMPENSATION_EXERCISE":
16173
+ case "TX_WARRANT_EXERCISE":
16174
+ return 30;
16175
+ case "TX_CONVERTIBLE_CONVERSION":
16176
+ return 35;
16177
+ case "TX_STOCK_REPURCHASE":
16178
+ case "TX_STOCK_CANCELLATION":
16179
+ case "TX_EQUITY_COMPENSATION_CANCELLATION":
16180
+ case "TX_WARRANT_CANCELLATION":
16181
+ case "TX_CONVERTIBLE_CANCELLATION":
16182
+ case "TX_EQUITY_COMPENSATION_RETURN_TO_POOL":
16183
+ return 40;
16184
+ default:
16185
+ return 50;
16186
+ }
16187
+ }
16188
+ function dumpTransactionSortKey(tx, issuanceDates, conversionResultSecurityIds) {
16189
+ let day = new Date(timestampMs(tx.date, 0) ?? 0).toISOString().slice(0, 10);
16190
+ const objectType = String(tx.object_type ?? "");
16191
+ if (objectType === "TX_VESTING_START" && typeof tx.security_id === "string") {
16192
+ const issuanceDate = issuanceDates.get(tx.security_id);
16193
+ if (issuanceDate && String(tx.date) < issuanceDate) {
16194
+ day = new Date(timestampMs(issuanceDate, 0) ?? 0).toISOString().slice(0, 10);
16195
+ }
16196
+ }
16197
+ const weight = String(dumpTxWeight(tx, conversionResultSecurityIds)).padStart(3, "0");
16198
+ const group = typeof tx.security_id === "string" && tx.security_id ? tx.security_id : "_no_security_";
16199
+ const createdMs = timestampMs(tx.createdAt ?? tx.created_at, null);
16200
+ const created = createdMs !== null ? new Date(createdMs).toISOString() : "9999-12-31T23:59:59.999Z";
16201
+ const id = String(tx._id ?? tx.id ?? "");
16202
+ return `${day}|${weight}|${group}|${created}|${id}`;
16203
+ }
16204
+ function sortDumpTransactions(transactions) {
16205
+ const issuanceDates = /* @__PURE__ */ new Map();
16206
+ const conversionResultSecurityIds = /* @__PURE__ */ new Set();
16207
+ for (const tx of transactions) {
16208
+ const objectType = String(tx.object_type ?? "");
16209
+ if (ISSUANCE_TYPES.has(objectType) && typeof tx.security_id === "string" && typeof tx.date === "string") {
16210
+ issuanceDates.set(tx.security_id, tx.date);
16211
+ }
16212
+ if (!CONVERSION_PARENT_TYPES.has(objectType))
16213
+ continue;
16214
+ if (Array.isArray(tx.resulting_security_ids)) {
16215
+ for (const id of tx.resulting_security_ids) {
16216
+ if (typeof id === "string" && id)
16217
+ conversionResultSecurityIds.add(id);
16218
+ }
16219
+ }
16220
+ if (typeof tx.balance_security_id === "string" && tx.balance_security_id) {
16221
+ conversionResultSecurityIds.add(tx.balance_security_id);
16222
+ }
16223
+ }
16224
+ return [...transactions].sort((a, b) => {
16225
+ const ka = dumpTransactionSortKey(a, issuanceDates, conversionResultSecurityIds);
16226
+ const kb = dumpTransactionSortKey(b, issuanceDates, conversionResultSecurityIds);
16227
+ if (ka < kb)
16228
+ return -1;
16229
+ if (ka > kb)
16230
+ return 1;
16231
+ return 0;
16232
+ });
16233
+ }
16234
+ function assembleEngineOcfDump(objects) {
16235
+ const issuers = [];
16236
+ const stockClasses = /* @__PURE__ */ new Map();
16237
+ const stockPlans = /* @__PURE__ */ new Map();
16238
+ const stakeholders = /* @__PURE__ */ new Map();
16239
+ const transactions = /* @__PURE__ */ new Map();
16240
+ const vestingTerms = /* @__PURE__ */ new Map();
16241
+ let index = 0;
16242
+ for (const obj of objects) {
16243
+ if (!isRecord(obj))
16244
+ continue;
16245
+ const type = canonicalType(obj);
16246
+ const payload = isRecord(obj.ocf) ? obj.ocf : null;
16247
+ index += 1;
16248
+ switch (type) {
16249
+ case "ISSUER":
16250
+ if (!payload) {
16251
+ throw new DumpAssemblyError("ISSUER object is missing an ocf payload.");
16252
+ }
16253
+ issuers.push(issuerForEngine(payload));
16254
+ break;
16255
+ case "STOCK_CLASS":
16256
+ if (payload)
16257
+ pushByOcfId(stockClasses, payload, `anon-stock-class-${index}`);
16258
+ break;
16259
+ case "STOCK_PLAN":
16260
+ if (payload)
16261
+ pushByOcfId(stockPlans, payload, `anon-stock-plan-${index}`);
16262
+ break;
16263
+ case "STAKEHOLDER":
16264
+ if (payload)
16265
+ pushByOcfId(stakeholders, payload, `anon-stakeholder-${index}`);
16266
+ break;
16267
+ case "TRANSACTION":
16268
+ if (payload)
16269
+ pushByOcfId(transactions, payload, `anon-transaction-${index}`);
16270
+ break;
16271
+ case "VESTING_TERMS":
16272
+ if (payload)
16273
+ pushByOcfId(vestingTerms, payload, `anon-vesting-terms-${index}`);
16274
+ break;
16275
+ default:
16276
+ break;
16277
+ }
16278
+ }
16279
+ if (issuers.length === 0) {
16280
+ throw new DumpAssemblyError("Dump requires exactly one ISSUER object; none found.");
16281
+ }
16282
+ if (issuers.length > 1) {
16283
+ throw new DumpAssemblyError(`Dump requires exactly one ISSUER object; found ${issuers.length}.`);
16284
+ }
16285
+ return {
16286
+ issuer: issuers[0],
16287
+ stockClasses: [...stockClasses.values()],
16288
+ stockPlans: [...stockPlans.values()],
16289
+ stakeholders: [...stakeholders.values()],
16290
+ transactions: sortDumpTransactions([...transactions.values()]),
16291
+ vestingTerms: [...vestingTerms.values()],
16292
+ sharePrices: []
16293
+ };
16294
+ }
16295
+
16046
16296
  // ../captable-ai-core/dist/scopes.js
16047
16297
  var CAPTABLE_AI_SCOPES = {
16048
16298
  READ: "read",
@@ -16203,14 +16453,14 @@ var summarySchema = external_exports.object({
16203
16453
  });
16204
16454
 
16205
16455
  // src/api.ts
16206
- function isRecord(value) {
16456
+ function isRecord2(value) {
16207
16457
  return typeof value === "object" && value !== null && !Array.isArray(value);
16208
16458
  }
16209
16459
  function coerceDetails(raw) {
16210
16460
  if (!Array.isArray(raw)) return void 0;
16211
16461
  const details = [];
16212
16462
  for (const entry of raw) {
16213
- if (!isRecord(entry)) continue;
16463
+ if (!isRecord2(entry)) continue;
16214
16464
  const message = typeof entry.message === "string" ? entry.message : void 0;
16215
16465
  if (!message) continue;
16216
16466
  const path = typeof entry.path === "string" ? entry.path : void 0;
@@ -16401,9 +16651,9 @@ async function handleResponse(res) {
16401
16651
  }
16402
16652
  if (!res.ok) {
16403
16653
  const exitCode = exitCodeForStatus(res.status);
16404
- const obj = isRecord(parsed) ? parsed : {};
16654
+ const obj = isRecord2(parsed) ? parsed : {};
16405
16655
  const nested = obj.error;
16406
- if (isRecord(nested)) {
16656
+ if (isRecord2(nested)) {
16407
16657
  const code = typeof nested.code === "string" && nested.code.trim() ? nested.code : void 0;
16408
16658
  const details = coerceDetails(nested.details);
16409
16659
  exitWithError(String(nested.message ?? `HTTP ${res.status}`), code, details, exitCode);
@@ -16531,6 +16781,29 @@ var HELP_OBJECTS_GET = `objects get \u2014 Show one OCF object
16531
16781
 
16532
16782
  Usage:
16533
16783
  captable-ai objects get <object-id> [--json]`;
16784
+ var HELP_OBJECTS_DUMP = `objects dump \u2014 Engine-shaped ocf.json
16785
+
16786
+ Assembles each row's ocf payload into the shape processCapTable consumes:
16787
+ { issuer, stockClasses, stockPlans, stakeholders, transactions, vestingTerms, sharePrices }
16788
+
16789
+ Fails if ISSUER is missing or duplicated. Not an OCF zip and not expected.json.
16790
+ Drains up to 400 pages (100,000 objects). Large tables: write a file with --output.
16791
+
16792
+ The list API picks the latest row per object id, then optionally filters status.
16793
+ --status committed therefore omits an object when a newer draft exists.
16794
+ --status draft dumps that latest-row working set (drafts win) so the engine
16795
+ still sees the committed ISSUER and other unchanged baseline objects.
16796
+
16797
+ Usage:
16798
+ captable-ai objects dump [--status committed|draft] [--output path]
16799
+
16800
+ Flags:
16801
+ --status STATUS committed = latest committed-only rows (default).
16802
+ draft = pending working set (latest row per object).
16803
+ --output PATH Write JSON to file (global flag; implies --json)
16804
+
16805
+ Global:
16806
+ --json Machine-readable JSON (dump is always JSON)`;
16534
16807
  var HELP_OBJECTS_UPDATE = `objects update \u2014 Update an OCF object
16535
16808
 
16536
16809
  Usage:
@@ -16619,6 +16892,8 @@ ${HELP_OBJECTS_LIST}
16619
16892
 
16620
16893
  ${HELP_OBJECTS_GET}
16621
16894
 
16895
+ ${HELP_OBJECTS_DUMP}
16896
+
16622
16897
  ${HELP_OBJECTS_UPDATE}
16623
16898
 
16624
16899
  ${HELP_OBJECTS_PATCH}
@@ -17713,11 +17988,11 @@ var JOBS_FILE = join2(CONFIG_DIR2, "jobs.jsonl");
17713
17988
  var LOCK_DIR = join2(CONFIG_DIR2, "jobs.lock");
17714
17989
  var VALID_JOB_STATUSES = /* @__PURE__ */ new Set(["submitted", "running", "complete", "failed"]);
17715
17990
  var VALID_JOB_KINDS = /* @__PURE__ */ new Set(["extraction"]);
17716
- function isRecord2(v) {
17991
+ function isRecord3(v) {
17717
17992
  return typeof v === "object" && v !== null;
17718
17993
  }
17719
17994
  function isJobEntry(value) {
17720
- if (!isRecord2(value)) return false;
17995
+ if (!isRecord3(value)) return false;
17721
17996
  return typeof value.id === "string" && typeof value.kind === "string" && VALID_JOB_KINDS.has(value.kind) && typeof value.portalId === "string" && typeof value.submittedAt === "string" && typeof value.status === "string" && VALID_JOB_STATUSES.has(value.status);
17722
17997
  }
17723
17998
  async function ensureDir() {
@@ -18268,11 +18543,11 @@ async function readEntries() {
18268
18543
  }
18269
18544
  return entries;
18270
18545
  }
18271
- function isRecord3(v) {
18546
+ function isRecord4(v) {
18272
18547
  return typeof v === "object" && v !== null;
18273
18548
  }
18274
18549
  function isFeedbackEntry(value) {
18275
- if (!isRecord3(value)) return false;
18550
+ if (!isRecord4(value)) return false;
18276
18551
  return typeof value.ts === "string" && typeof value.text === "string" && typeof value.sent === "boolean";
18277
18552
  }
18278
18553
 
@@ -18489,7 +18764,7 @@ function successHtml() {
18489
18764
  h1{font-size:1.5rem;color:#111}p{color:#666}</style></head>
18490
18765
  <body><div class="card"><h1>Login successful</h1><p>You can close this window and return to the terminal.</p></div></body></html>`;
18491
18766
  }
18492
- function isRecord4(value) {
18767
+ function isRecord5(value) {
18493
18768
  return typeof value === "object" && value !== null && !Array.isArray(value);
18494
18769
  }
18495
18770
  function readStr(obj, ...keys) {
@@ -18510,7 +18785,7 @@ function parsePortalsFromExchange(raw) {
18510
18785
  }
18511
18786
  if (!Array.isArray(parsed)) return [];
18512
18787
  return parsed.flatMap((p) => {
18513
- if (!isRecord4(p)) return [];
18788
+ if (!isRecord5(p)) return [];
18514
18789
  if (typeof p.id !== "string" || p.id.length === 0) return [];
18515
18790
  return [
18516
18791
  {
@@ -18554,7 +18829,7 @@ function tryParseLoginPaste(line, expectedState) {
18554
18829
  } catch {
18555
18830
  return null;
18556
18831
  }
18557
- if (!isRecord4(obj)) return null;
18832
+ if (!isRecord5(obj)) return null;
18558
18833
  if (typeof obj.state !== "string" || !pastedStateMatches(expectedState, obj.state)) return null;
18559
18834
  const accessToken = readStr(obj, "access_token", "accessToken");
18560
18835
  const refreshToken = readStr(obj, "refresh_token", "refreshToken");
@@ -18919,6 +19194,105 @@ async function runObjectsDelete(args, rawArgv) {
18919
19194
  );
18920
19195
  }
18921
19196
 
19197
+ // src/objects-pages.ts
19198
+ var OBJECTS_LIST_MAX_PAGES = 100;
19199
+ var OBJECTS_DUMP_MAX_PAGES = 400;
19200
+ async function fetchAllObjectPages(portalId, query, options) {
19201
+ const maxPages = options?.maxPages ?? OBJECTS_LIST_MAX_PAGES;
19202
+ const objects = [];
19203
+ const documentsById = /* @__PURE__ */ new Map();
19204
+ let cursor = typeof query.cursor === "string" ? query.cursor : void 0;
19205
+ let total = 0;
19206
+ let pages = 0;
19207
+ while (true) {
19208
+ pages += 1;
19209
+ if (pages > maxPages) {
19210
+ exitWithError(
19211
+ `Exceeded max object-list pages (${maxPages}). Narrow the query or raise the page size.`,
19212
+ "ERROR",
19213
+ void 0,
19214
+ EXIT.SERVER_ERROR
19215
+ );
19216
+ }
19217
+ const pageQuery = { ...query };
19218
+ if (cursor !== void 0) pageQuery.cursor = cursor;
19219
+ else delete pageQuery.cursor;
19220
+ const res = await apiRequest(`/${portalId}/objects`, { query: pageQuery });
19221
+ const pageObjects = res.data?.objects ?? [];
19222
+ const pageDocs = res.data?.documents ?? [];
19223
+ objects.push(...pageObjects);
19224
+ for (const doc of pageDocs) {
19225
+ documentsById.set(doc.id, doc);
19226
+ }
19227
+ if (res.meta?.total != null) total = res.meta.total;
19228
+ if (!res.meta?.hasMore) break;
19229
+ if (!res.meta.cursor) {
19230
+ exitWithError(
19231
+ "Pagination response has more results but no continuation cursor.",
19232
+ "ERROR",
19233
+ void 0,
19234
+ EXIT.SERVER_ERROR
19235
+ );
19236
+ }
19237
+ if (cursor !== void 0 && res.meta.cursor === cursor) {
19238
+ exitWithError(
19239
+ "Pagination cursor did not advance; stopped draining object pages.",
19240
+ "ERROR",
19241
+ void 0,
19242
+ EXIT.SERVER_ERROR
19243
+ );
19244
+ }
19245
+ cursor = res.meta.cursor;
19246
+ }
19247
+ return {
19248
+ data: { objects, documents: Array.from(documentsById.values()) },
19249
+ meta: { cursor: null, total: total || objects.length, hasMore: false }
19250
+ };
19251
+ }
19252
+
19253
+ // src/commands/objects-dump.ts
19254
+ var DUMP_PAGE_LIMIT = 250;
19255
+ function dumpStatus(raw) {
19256
+ const status = raw ?? "committed";
19257
+ if (status === "committed" || status === "draft") return status;
19258
+ exitWithError(
19259
+ `--status must be one of: draft, committed (got: "${status}")`,
19260
+ "VALIDATION_FAILED",
19261
+ void 0,
19262
+ EXIT.VALIDATION
19263
+ );
19264
+ }
19265
+ async function runObjectsDump(args, rawArgv) {
19266
+ if (argvRequestsHelp(rawArgv)) {
19267
+ process.stdout.write(`${HELP_OBJECTS_DUMP}
19268
+ `);
19269
+ process.exit(0);
19270
+ }
19271
+ validateFlags(args, { flags: ["status"], booleans: [] });
19272
+ if (args.positional.length > 0) {
19273
+ exitWithError(
19274
+ `Unexpected argument: ${args.positional[0]}. objects dump takes flags only.`,
19275
+ "USAGE",
19276
+ void 0,
19277
+ EXIT.USAGE
19278
+ );
19279
+ }
19280
+ const status = dumpStatus(getFlagValue(args, "status"));
19281
+ const portalId = await getActivePortalId();
19282
+ const query = { limit: DUMP_PAGE_LIMIT };
19283
+ if (status === "committed") query.status = "committed";
19284
+ const page = await fetchAllObjectPages(portalId, query, { maxPages: OBJECTS_DUMP_MAX_PAGES });
19285
+ try {
19286
+ const objects = Array.isArray(page.data?.objects) ? page.data.objects : [];
19287
+ printJson(assembleEngineOcfDump(objects));
19288
+ } catch (err) {
19289
+ if (err instanceof DumpAssemblyError) {
19290
+ exitWithError(err.message, "VALIDATION_FAILED", void 0, EXIT.VALIDATION);
19291
+ }
19292
+ throw err;
19293
+ }
19294
+ }
19295
+
18922
19296
  // src/commands/objects-get.ts
18923
19297
  function shellQuote2(value) {
18924
19298
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -18980,60 +19354,6 @@ Object: ${obj.id}
18980
19354
  printLine();
18981
19355
  }
18982
19356
 
18983
- // src/objects-pages.ts
18984
- var OBJECTS_LIST_MAX_PAGES = 100;
18985
- async function fetchAllObjectPages(portalId, query) {
18986
- const objects = [];
18987
- const documentsById = /* @__PURE__ */ new Map();
18988
- let cursor = typeof query.cursor === "string" ? query.cursor : void 0;
18989
- let total = 0;
18990
- let pages = 0;
18991
- while (true) {
18992
- pages += 1;
18993
- if (pages > OBJECTS_LIST_MAX_PAGES) {
18994
- exitWithError(
18995
- `Exceeded max object-list pages (${OBJECTS_LIST_MAX_PAGES}). Narrow the query or raise the page size.`,
18996
- "ERROR",
18997
- void 0,
18998
- EXIT.SERVER_ERROR
18999
- );
19000
- }
19001
- const pageQuery = { ...query };
19002
- if (cursor !== void 0) pageQuery.cursor = cursor;
19003
- else delete pageQuery.cursor;
19004
- const res = await apiRequest(`/${portalId}/objects`, { query: pageQuery });
19005
- const pageObjects = res.data?.objects ?? [];
19006
- const pageDocs = res.data?.documents ?? [];
19007
- objects.push(...pageObjects);
19008
- for (const doc of pageDocs) {
19009
- documentsById.set(doc.id, doc);
19010
- }
19011
- if (res.meta?.total != null) total = res.meta.total;
19012
- if (!res.meta?.hasMore) break;
19013
- if (!res.meta.cursor) {
19014
- exitWithError(
19015
- "Pagination response has more results but no continuation cursor.",
19016
- "ERROR",
19017
- void 0,
19018
- EXIT.SERVER_ERROR
19019
- );
19020
- }
19021
- if (cursor !== void 0 && res.meta.cursor === cursor) {
19022
- exitWithError(
19023
- "Pagination cursor did not advance; stopped draining object pages.",
19024
- "ERROR",
19025
- void 0,
19026
- EXIT.SERVER_ERROR
19027
- );
19028
- }
19029
- cursor = res.meta.cursor;
19030
- }
19031
- return {
19032
- data: { objects, documents: Array.from(documentsById.values()) },
19033
- meta: { cursor: null, total: total || objects.length, hasMore: false }
19034
- };
19035
- }
19036
-
19037
19357
  // src/validate.ts
19038
19358
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
19039
19359
  function isValidCalendarIsoDate(s) {
@@ -19103,7 +19423,7 @@ function validateIsoDateRange(fromRaw, toRaw) {
19103
19423
  // src/commands/objects-list.ts
19104
19424
  var CONFIDENCE_RED_MAX = 64;
19105
19425
  var CONFIDENCE_AMBER_MAX = 84;
19106
- function isRecord5(value) {
19426
+ function isRecord6(value) {
19107
19427
  return typeof value === "object" && value !== null && !Array.isArray(value);
19108
19428
  }
19109
19429
  function getValidatedMaxConfidence(raw) {
@@ -19142,7 +19462,7 @@ function stakeholderDisplayName(ocf) {
19142
19462
  if (!ocf) return null;
19143
19463
  if (typeof ocf.name === "string" && ocf.name.trim()) return ocf.name.trim();
19144
19464
  if (typeof ocf.legal_name === "string" && ocf.legal_name.trim()) return ocf.legal_name.trim();
19145
- if (isRecord5(ocf.name)) {
19465
+ if (isRecord6(ocf.name)) {
19146
19466
  const legal = ocf.name.legal_name;
19147
19467
  if (typeof legal === "string" && legal.trim()) return legal.trim();
19148
19468
  const parts = [ocf.name.first_name, ocf.name.last_name].filter(
@@ -19354,7 +19674,7 @@ async function readStdin() {
19354
19674
  }
19355
19675
  return Buffer.concat(chunks).toString("utf-8");
19356
19676
  }
19357
- function isRecord6(value) {
19677
+ function isRecord7(value) {
19358
19678
  return typeof value === "object" && value !== null && !Array.isArray(value);
19359
19679
  }
19360
19680
  function parsePatchPayload(raw) {
@@ -19369,7 +19689,7 @@ function parsePatchPayload(raw) {
19369
19689
  EXIT.VALIDATION
19370
19690
  );
19371
19691
  }
19372
- if (!isRecord6(parsed)) {
19692
+ if (!isRecord7(parsed)) {
19373
19693
  exitWithError(
19374
19694
  "OCF patch payload must be a JSON object.",
19375
19695
  "VALIDATION_FAILED",
@@ -19589,7 +19909,7 @@ async function readStdin2() {
19589
19909
  }
19590
19910
  return Buffer.concat(chunks).toString("utf-8");
19591
19911
  }
19592
- function isRecord7(value) {
19912
+ function isRecord8(value) {
19593
19913
  return typeof value === "object" && value !== null && !Array.isArray(value);
19594
19914
  }
19595
19915
  function parseOcfPayload(raw) {
@@ -19604,7 +19924,7 @@ function parseOcfPayload(raw) {
19604
19924
  EXIT.VALIDATION
19605
19925
  );
19606
19926
  }
19607
- if (!isRecord7(parsed)) {
19927
+ if (!isRecord8(parsed)) {
19608
19928
  exitWithError(
19609
19929
  "OCF payload must be a JSON object.",
19610
19930
  "VALIDATION_FAILED",
@@ -19973,20 +20293,20 @@ import { homedir as homedir4 } from "os";
19973
20293
  import { join as join4 } from "path";
19974
20294
  var CONFIG_DIR4 = join4(homedir4(), ".captable-ai");
19975
20295
  var PROFILES_FILE = join4(CONFIG_DIR4, "profiles.json");
19976
- function isRecord8(value) {
20296
+ function isRecord9(value) {
19977
20297
  return typeof value === "object" && value !== null && !Array.isArray(value);
19978
20298
  }
19979
20299
  function isErrno3(err, code) {
19980
- return isRecord8(err) && err.code === code;
20300
+ return isRecord9(err) && err.code === code;
19981
20301
  }
19982
20302
  function parseProfiles(raw) {
19983
20303
  const parsed = JSON.parse(raw);
19984
- if (!isRecord8(parsed) || !isRecord8(parsed.profiles)) {
20304
+ if (!isRecord9(parsed) || !isRecord9(parsed.profiles)) {
19985
20305
  throw new Error("profiles.json must contain a profiles object.");
19986
20306
  }
19987
20307
  const profiles = {};
19988
20308
  for (const [name, value] of Object.entries(parsed.profiles)) {
19989
- if (!isRecord8(value) || typeof value.company !== "string" || !value.company.trim()) {
20309
+ if (!isRecord9(value) || typeof value.company !== "string" || !value.company.trim()) {
19990
20310
  throw new Error(`Profile "${name}" must include a non-empty company string.`);
19991
20311
  }
19992
20312
  const stage = typeof value.stage === "string" && value.stage.trim() ? value.stage : void 0;
@@ -20671,6 +20991,7 @@ Cap Table (read):
20671
20991
  Pagination: --limit (1-250), --cursor, --all
20672
20992
  Dates: --from / --to as YYYY-MM-DD
20673
20993
  objects get <id> Show a single OCF object
20994
+ objects dump Engine-shaped ocf.json (default --status committed; --output path)
20674
20995
  schemas list List OCF object types
20675
20996
  schemas get <type> Show schema for an OCF type
20676
20997
 
@@ -20957,6 +21278,9 @@ async function main() {
20957
21278
  case "get":
20958
21279
  await runObjectsGet(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
20959
21280
  break;
21281
+ case "dump":
21282
+ await runObjectsDump(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
21283
+ break;
20960
21284
  case "update":
20961
21285
  await runObjectsUpdate(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
20962
21286
  break;
@@ -20974,7 +21298,7 @@ async function main() {
20974
21298
  break;
20975
21299
  default:
20976
21300
  exitWithError(
20977
- `Unknown objects command: ${sub ?? "(none)"}. Try: objects list | get | update | patch | revert | versions | delete`,
21301
+ `Unknown objects command: ${sub ?? "(none)"}. Try: objects list | get | dump | update | patch | revert | versions | delete`,
20978
21302
  "UNKNOWN_COMMAND",
20979
21303
  void 0,
20980
21304
  EXIT.USAGE
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fairmint/captable-ai",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Agent-native CLI for Captable AI — manage companies, upload documents, run OCF extraction, and commit cap tables from the terminal.",
5
5
  "keywords": [
6
6
  "captable",