@fairmint/captable-ai 0.1.5 → 0.1.7

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 +662 -101
  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
@@ -602,11 +602,32 @@ var COMMAND_REGISTRY = [
602
602
  {
603
603
  name: "objects get",
604
604
  group: "captable",
605
- description: "Get a single OCF object with document and extraction details.",
605
+ description: "Get a single OCF object with document, extraction details, and a vesting summary when present.",
606
606
  usage: "captable-ai objects get <object-id> [--json]",
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: "9", 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,405 @@ 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/format-vesting-summary.js
16068
+ function asRecord(value) {
16069
+ if (typeof value !== "object" || value === null || Array.isArray(value))
16070
+ return null;
16071
+ const out = {};
16072
+ for (const [key, val] of Object.entries(value)) {
16073
+ out[key] = val;
16074
+ }
16075
+ return out;
16076
+ }
16077
+ function gcd(a, b) {
16078
+ let x = Math.abs(Math.trunc(a));
16079
+ let y = Math.abs(Math.trunc(b));
16080
+ while (y !== 0) {
16081
+ const t = y;
16082
+ y = x % y;
16083
+ x = t;
16084
+ }
16085
+ return x || 1;
16086
+ }
16087
+ function reduceFraction(numerator, denominator) {
16088
+ const divisor = gcd(numerator, denominator);
16089
+ return { numerator: numerator / divisor, denominator: denominator / divisor };
16090
+ }
16091
+ function asConditions(value) {
16092
+ if (!Array.isArray(value))
16093
+ return [];
16094
+ return value.filter((item) => asRecord(item) !== null);
16095
+ }
16096
+ function portionFraction(condition) {
16097
+ const portion = asRecord(condition.portion);
16098
+ if (!portion)
16099
+ return null;
16100
+ const numerator = Number(portion.numerator);
16101
+ const denominator = Number(portion.denominator);
16102
+ if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0)
16103
+ return null;
16104
+ if (numerator <= 0)
16105
+ return null;
16106
+ return { numerator, denominator };
16107
+ }
16108
+ function formatPercent(numerator, denominator) {
16109
+ const reduced = reduceFraction(numerator, denominator);
16110
+ const pct = reduced.numerator / reduced.denominator * 100;
16111
+ if (Number.isInteger(pct) && pct >= 1 && pct <= 100)
16112
+ return `${pct}%`;
16113
+ return `${reduced.numerator}/${reduced.denominator}`;
16114
+ }
16115
+ function periodLabel(period) {
16116
+ if (!period)
16117
+ return null;
16118
+ let occurrences = 1;
16119
+ if (period.occurrences != null && period.occurrences !== "") {
16120
+ occurrences = Number(period.occurrences);
16121
+ if (!Number.isInteger(occurrences) || occurrences <= 0)
16122
+ return null;
16123
+ }
16124
+ let length = 1;
16125
+ if (period.length != null && period.length !== "") {
16126
+ const suppliedLength = Number(period.length);
16127
+ if (!Number.isFinite(suppliedLength) || suppliedLength < 0)
16128
+ return null;
16129
+ length = suppliedLength;
16130
+ }
16131
+ if (length === 0)
16132
+ return { cadence: "immediately", occurrences };
16133
+ const unit = String(period.type ?? "").toUpperCase();
16134
+ if (unit === "MONTHS") {
16135
+ return { cadence: length === 1 ? "monthly" : `every ${length} months`, occurrences };
16136
+ }
16137
+ if (unit === "DAYS") {
16138
+ return { cadence: length === 1 ? "daily" : `every ${length} days`, occurrences };
16139
+ }
16140
+ if (unit === "WEEKS") {
16141
+ return { cadence: length === 1 ? "weekly" : `every ${length} weeks`, occurrences };
16142
+ }
16143
+ if (unit === "YEARS") {
16144
+ return { cadence: length === 1 ? "yearly" : `every ${length} years`, occurrences };
16145
+ }
16146
+ return null;
16147
+ }
16148
+ function quantityLabel(condition) {
16149
+ const raw = condition.quantity;
16150
+ if (raw == null || raw === "")
16151
+ return null;
16152
+ const quantity = Number(raw);
16153
+ if (!Number.isFinite(quantity) || quantity <= 0)
16154
+ return null;
16155
+ return quantity === 1 ? "1 share" : `${quantity} shares`;
16156
+ }
16157
+ function trancheAmount(condition) {
16158
+ const fraction = portionFraction(condition);
16159
+ if (fraction)
16160
+ return formatPercent(fraction.numerator, fraction.denominator);
16161
+ return quantityLabel(condition);
16162
+ }
16163
+ function formatVestingSummary(ocf) {
16164
+ const record2 = asRecord(ocf);
16165
+ const conditions = asConditions(record2?.vesting_conditions);
16166
+ if (conditions.length === 0)
16167
+ return null;
16168
+ const parts = [];
16169
+ for (const condition of conditions) {
16170
+ const trigger = asRecord(condition.trigger);
16171
+ const triggerType = String(trigger?.type ?? "").toUpperCase();
16172
+ if (triggerType === "VESTING_EVENT") {
16173
+ const portion = asRecord(condition.portion);
16174
+ const remainder = portion?.remainder === true || portion?.remainder === "true";
16175
+ const hasAllocation = Boolean(trancheAmount(condition) || remainder);
16176
+ if (portion && !hasAllocation)
16177
+ continue;
16178
+ parts.push("event-based (see conditions)");
16179
+ continue;
16180
+ }
16181
+ const amount = trancheAmount(condition);
16182
+ if (!amount)
16183
+ continue;
16184
+ if (triggerType === "VESTING_START_DATE") {
16185
+ parts.push(`${amount} at grant`);
16186
+ continue;
16187
+ }
16188
+ if (triggerType === "VESTING_SCHEDULE_ABSOLUTE") {
16189
+ const date5 = typeof trigger?.date === "string" && trigger.date.length > 0 ? trigger.date : null;
16190
+ parts.push(date5 ? `${amount} on ${date5}` : `${amount} on a fixed date`);
16191
+ continue;
16192
+ }
16193
+ if (triggerType === "VESTING_SCHEDULE_RELATIVE") {
16194
+ const period = periodLabel(asRecord(trigger?.period));
16195
+ if (!period)
16196
+ continue;
16197
+ if (period.cadence === "immediately" && period.occurrences === 1) {
16198
+ parts.push(`${amount} immediately`);
16199
+ continue;
16200
+ }
16201
+ const fraction = portionFraction(condition);
16202
+ if (fraction) {
16203
+ const reduced = reduceFraction(fraction.numerator, fraction.denominator);
16204
+ parts.push(`${reduced.numerator}/${reduced.denominator} ${period.cadence} \xD7 ${period.occurrences}`);
16205
+ continue;
16206
+ }
16207
+ parts.push(`${amount} ${period.cadence} \xD7 ${period.occurrences}`);
16208
+ }
16209
+ }
16210
+ if (parts.length === 0)
16211
+ return null;
16212
+ return `Vesting: ${parts.join(", then ")}`;
16213
+ }
16214
+ function unresolvedVestingTermsId(ocf) {
16215
+ const record2 = asRecord(ocf);
16216
+ if (!record2)
16217
+ return null;
16218
+ if (Array.isArray(record2.vesting_conditions) && record2.vesting_conditions.length > 0) {
16219
+ return null;
16220
+ }
16221
+ return typeof record2.vesting_terms_id === "string" && record2.vesting_terms_id.length > 0 ? record2.vesting_terms_id : null;
16222
+ }
16223
+ async function resolveVestingSummary(ocf, fetchTermsOcf) {
16224
+ const direct = formatVestingSummary(ocf);
16225
+ if (direct)
16226
+ return direct;
16227
+ const termsId = unresolvedVestingTermsId(ocf);
16228
+ if (!termsId)
16229
+ return null;
16230
+ try {
16231
+ return formatVestingSummary(await fetchTermsOcf(termsId));
16232
+ } catch {
16233
+ return null;
16234
+ }
16235
+ }
16236
+
16237
+ // ../captable-ai-core/dist/objects-dump.js
16238
+ var DumpAssemblyError = class extends Error {
16239
+ constructor(message) {
16240
+ super(message);
16241
+ this.name = "DumpAssemblyError";
16242
+ }
16243
+ };
16244
+ var ISSUANCE_TYPES = /* @__PURE__ */ new Set([
16245
+ "TX_STOCK_ISSUANCE",
16246
+ "TX_CONVERTIBLE_ISSUANCE",
16247
+ "TX_WARRANT_ISSUANCE",
16248
+ "TX_EQUITY_COMPENSATION_ISSUANCE",
16249
+ "TX_PLAN_SECURITY_ISSUANCE"
16250
+ ]);
16251
+ var CONVERSION_PARENT_TYPES = /* @__PURE__ */ new Set([
16252
+ "TX_CONVERTIBLE_CONVERSION",
16253
+ "TX_WARRANT_EXERCISE",
16254
+ "TX_EQUITY_COMPENSATION_EXERCISE",
16255
+ "TX_EQUITY_COMPENSATION_RELEASE",
16256
+ "TX_PLAN_SECURITY_RELEASE",
16257
+ "TX_STOCK_TRANSFER",
16258
+ "TX_STOCK_CONVERSION",
16259
+ "TX_CONVERTIBLE_TRANSFER",
16260
+ "TX_WARRANT_TRANSFER",
16261
+ "TX_EQUITY_COMPENSATION_TRANSFER",
16262
+ "TX_PLAN_SECURITY_TRANSFER",
16263
+ "TX_STOCK_REISSUANCE"
16264
+ ]);
16265
+ function isRecord(value) {
16266
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16267
+ }
16268
+ function canonicalType(obj) {
16269
+ const type = String(obj.type ?? "").toUpperCase();
16270
+ if (type === "OBJECT")
16271
+ return String(obj.subtype ?? "").toUpperCase();
16272
+ return type;
16273
+ }
16274
+ function issuerForEngine(payload) {
16275
+ if (payload.initial_shares_authorized == null && payload.authorized_shares != null) {
16276
+ return { ...payload, initial_shares_authorized: payload.authorized_shares };
16277
+ }
16278
+ return payload;
16279
+ }
16280
+ function payloadId(payload, fallback) {
16281
+ return typeof payload.id === "string" && payload.id.length > 0 ? payload.id : fallback;
16282
+ }
16283
+ function pushByOcfId(bucket, payload, fallback) {
16284
+ bucket.set(payloadId(payload, fallback), payload);
16285
+ }
16286
+ function timestampMs(input, defaultValue) {
16287
+ if (input == null)
16288
+ return defaultValue;
16289
+ const ms = typeof input === "number" ? input : new Date(String(input)).getTime();
16290
+ return Number.isNaN(ms) ? defaultValue : ms;
16291
+ }
16292
+ function dumpTxWeight(tx, conversionResultSecurityIds) {
16293
+ const objectType = String(tx.object_type ?? "");
16294
+ if (conversionResultSecurityIds.size > 0 && ISSUANCE_TYPES.has(objectType) && typeof tx.security_id === "string" && conversionResultSecurityIds.has(tx.security_id)) {
16295
+ return 36;
16296
+ }
16297
+ switch (objectType) {
16298
+ case "TX_ISSUER_AUTHORIZED_SHARES_ADJUSTMENT":
16299
+ case "TX_STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT":
16300
+ case "TX_STOCK_PLAN_POOL_ADJUSTMENT":
16301
+ return 5;
16302
+ case "TX_STOCK_REISSUANCE":
16303
+ return 9;
16304
+ case "TX_STOCK_ISSUANCE":
16305
+ case "TX_EQUITY_COMPENSATION_ISSUANCE":
16306
+ case "TX_WARRANT_ISSUANCE":
16307
+ case "TX_CONVERTIBLE_ISSUANCE":
16308
+ case "TX_PLAN_SECURITY_ISSUANCE":
16309
+ return 10;
16310
+ case "TX_STOCK_ACCEPTANCE":
16311
+ case "TX_WARRANT_ACCEPTANCE":
16312
+ case "TX_EQUITY_COMPENSATION_ACCEPTANCE":
16313
+ case "TX_PLAN_SECURITY_ACCEPTANCE":
16314
+ return 11;
16315
+ case "TX_VESTING_START":
16316
+ return 12;
16317
+ case "TX_VESTING_EVENT":
16318
+ return 13;
16319
+ case "TX_VESTING_ACCELERATION":
16320
+ return 14;
16321
+ case "TX_STOCK_CLASS_SPLIT":
16322
+ return 15;
16323
+ case "TX_STOCK_RETRACTION":
16324
+ case "TX_CONVERTIBLE_RETRACTION":
16325
+ case "TX_WARRANT_RETRACTION":
16326
+ case "TX_EQUITY_COMPENSATION_RETRACTION":
16327
+ return 16;
16328
+ case "TX_STOCK_CONSOLIDATION":
16329
+ return 17;
16330
+ case "TX_STOCK_CLASS_CONVERSION_RATIO_ADJUSTMENT":
16331
+ return 18;
16332
+ case "TX_EQUITY_COMPENSATION_REPRICING":
16333
+ return 19;
16334
+ case "TX_STOCK_TRANSFER":
16335
+ case "TX_WARRANT_TRANSFER":
16336
+ case "TX_CONVERTIBLE_TRANSFER":
16337
+ case "TX_EQUITY_COMPENSATION_TRANSFER":
16338
+ case "TX_PLAN_SECURITY_TRANSFER":
16339
+ return 20;
16340
+ case "TX_CONVERTIBLE_ACCEPTANCE":
16341
+ return 22;
16342
+ case "TX_EQUITY_COMPENSATION_EXERCISE":
16343
+ case "TX_WARRANT_EXERCISE":
16344
+ return 30;
16345
+ case "TX_CONVERTIBLE_CONVERSION":
16346
+ return 35;
16347
+ case "TX_STOCK_REPURCHASE":
16348
+ case "TX_STOCK_CANCELLATION":
16349
+ case "TX_EQUITY_COMPENSATION_CANCELLATION":
16350
+ case "TX_WARRANT_CANCELLATION":
16351
+ case "TX_CONVERTIBLE_CANCELLATION":
16352
+ case "TX_EQUITY_COMPENSATION_RETURN_TO_POOL":
16353
+ return 40;
16354
+ default:
16355
+ return 50;
16356
+ }
16357
+ }
16358
+ function dumpTransactionSortKey(tx, issuanceDates, conversionResultSecurityIds) {
16359
+ let day = new Date(timestampMs(tx.date, 0) ?? 0).toISOString().slice(0, 10);
16360
+ const objectType = String(tx.object_type ?? "");
16361
+ if (objectType === "TX_VESTING_START" && typeof tx.security_id === "string") {
16362
+ const issuanceDate = issuanceDates.get(tx.security_id);
16363
+ if (issuanceDate && String(tx.date) < issuanceDate) {
16364
+ day = new Date(timestampMs(issuanceDate, 0) ?? 0).toISOString().slice(0, 10);
16365
+ }
16366
+ }
16367
+ const weight = String(dumpTxWeight(tx, conversionResultSecurityIds)).padStart(3, "0");
16368
+ const group = typeof tx.security_id === "string" && tx.security_id ? tx.security_id : "_no_security_";
16369
+ const createdMs = timestampMs(tx.createdAt ?? tx.created_at, null);
16370
+ const created = createdMs !== null ? new Date(createdMs).toISOString() : "9999-12-31T23:59:59.999Z";
16371
+ const id = String(tx._id ?? tx.id ?? "");
16372
+ return `${day}|${weight}|${group}|${created}|${id}`;
16373
+ }
16374
+ function sortDumpTransactions(transactions) {
16375
+ const issuanceDates = /* @__PURE__ */ new Map();
16376
+ const conversionResultSecurityIds = /* @__PURE__ */ new Set();
16377
+ for (const tx of transactions) {
16378
+ const objectType = String(tx.object_type ?? "");
16379
+ if (ISSUANCE_TYPES.has(objectType) && typeof tx.security_id === "string" && typeof tx.date === "string") {
16380
+ issuanceDates.set(tx.security_id, tx.date);
16381
+ }
16382
+ if (!CONVERSION_PARENT_TYPES.has(objectType))
16383
+ continue;
16384
+ if (Array.isArray(tx.resulting_security_ids)) {
16385
+ for (const id of tx.resulting_security_ids) {
16386
+ if (typeof id === "string" && id)
16387
+ conversionResultSecurityIds.add(id);
16388
+ }
16389
+ }
16390
+ if (typeof tx.balance_security_id === "string" && tx.balance_security_id) {
16391
+ conversionResultSecurityIds.add(tx.balance_security_id);
16392
+ }
16393
+ }
16394
+ return [...transactions].sort((a, b) => {
16395
+ const ka = dumpTransactionSortKey(a, issuanceDates, conversionResultSecurityIds);
16396
+ const kb = dumpTransactionSortKey(b, issuanceDates, conversionResultSecurityIds);
16397
+ if (ka < kb)
16398
+ return -1;
16399
+ if (ka > kb)
16400
+ return 1;
16401
+ return 0;
16402
+ });
16403
+ }
16404
+ function assembleEngineOcfDump(objects) {
16405
+ const issuers = [];
16406
+ const stockClasses = /* @__PURE__ */ new Map();
16407
+ const stockPlans = /* @__PURE__ */ new Map();
16408
+ const stakeholders = /* @__PURE__ */ new Map();
16409
+ const transactions = /* @__PURE__ */ new Map();
16410
+ const vestingTerms = /* @__PURE__ */ new Map();
16411
+ let index = 0;
16412
+ for (const obj of objects) {
16413
+ if (!isRecord(obj))
16414
+ continue;
16415
+ const type = canonicalType(obj);
16416
+ const payload = isRecord(obj.ocf) ? obj.ocf : null;
16417
+ index += 1;
16418
+ switch (type) {
16419
+ case "ISSUER":
16420
+ if (!payload) {
16421
+ throw new DumpAssemblyError("ISSUER object is missing an ocf payload.");
16422
+ }
16423
+ issuers.push(issuerForEngine(payload));
16424
+ break;
16425
+ case "STOCK_CLASS":
16426
+ if (payload)
16427
+ pushByOcfId(stockClasses, payload, `anon-stock-class-${index}`);
16428
+ break;
16429
+ case "STOCK_PLAN":
16430
+ if (payload)
16431
+ pushByOcfId(stockPlans, payload, `anon-stock-plan-${index}`);
16432
+ break;
16433
+ case "STAKEHOLDER":
16434
+ if (payload)
16435
+ pushByOcfId(stakeholders, payload, `anon-stakeholder-${index}`);
16436
+ break;
16437
+ case "TRANSACTION":
16438
+ if (payload)
16439
+ pushByOcfId(transactions, payload, `anon-transaction-${index}`);
16440
+ break;
16441
+ case "VESTING_TERMS":
16442
+ if (payload)
16443
+ pushByOcfId(vestingTerms, payload, `anon-vesting-terms-${index}`);
16444
+ break;
16445
+ default:
16446
+ break;
16447
+ }
16448
+ }
16449
+ if (issuers.length === 0) {
16450
+ throw new DumpAssemblyError("Dump requires exactly one ISSUER object; none found.");
16451
+ }
16452
+ if (issuers.length > 1) {
16453
+ throw new DumpAssemblyError(`Dump requires exactly one ISSUER object; found ${issuers.length}.`);
16454
+ }
16455
+ return {
16456
+ issuer: issuers[0],
16457
+ stockClasses: [...stockClasses.values()],
16458
+ stockPlans: [...stockPlans.values()],
16459
+ stakeholders: [...stakeholders.values()],
16460
+ transactions: sortDumpTransactions([...transactions.values()]),
16461
+ vestingTerms: [...vestingTerms.values()],
16462
+ sharePrices: []
16463
+ };
16464
+ }
16465
+
16046
16466
  // ../captable-ai-core/dist/scopes.js
16047
16467
  var CAPTABLE_AI_SCOPES = {
16048
16468
  READ: "read",
@@ -16203,14 +16623,14 @@ var summarySchema = external_exports.object({
16203
16623
  });
16204
16624
 
16205
16625
  // src/api.ts
16206
- function isRecord(value) {
16626
+ function isRecord2(value) {
16207
16627
  return typeof value === "object" && value !== null && !Array.isArray(value);
16208
16628
  }
16209
16629
  function coerceDetails(raw) {
16210
16630
  if (!Array.isArray(raw)) return void 0;
16211
16631
  const details = [];
16212
16632
  for (const entry of raw) {
16213
- if (!isRecord(entry)) continue;
16633
+ if (!isRecord2(entry)) continue;
16214
16634
  const message = typeof entry.message === "string" ? entry.message : void 0;
16215
16635
  if (!message) continue;
16216
16636
  const path = typeof entry.path === "string" ? entry.path : void 0;
@@ -16219,12 +16639,19 @@ function coerceDetails(raw) {
16219
16639
  return details.length > 0 ? details : void 0;
16220
16640
  }
16221
16641
  var REQUEST_TIMEOUT_MS2 = 3e4;
16222
- function exitForRefreshFailure(err) {
16642
+ function exitForRefreshFailure(err, softFail = false) {
16223
16643
  if (err instanceof Error && err.name === "AuthError") {
16224
- exitWithError("Session expired. Run: captable-ai login", "AUTH_EXPIRED", void 0, EXIT.AUTH);
16644
+ failRequest(
16645
+ softFail,
16646
+ "Session expired. Run: captable-ai login",
16647
+ "AUTH_EXPIRED",
16648
+ void 0,
16649
+ EXIT.AUTH
16650
+ );
16225
16651
  }
16226
16652
  if (err instanceof Error && (err.name === "NetworkError" || isTransportError(err))) {
16227
- exitWithError(
16653
+ failRequest(
16654
+ softFail,
16228
16655
  "Network error while refreshing session. Check your connection and try again.",
16229
16656
  "REFRESH_TRANSPORT",
16230
16657
  void 0,
@@ -16232,7 +16659,8 @@ function exitForRefreshFailure(err) {
16232
16659
  );
16233
16660
  }
16234
16661
  const detail = err instanceof Error ? err.message : String(err);
16235
- exitWithError(
16662
+ failRequest(
16663
+ softFail,
16236
16664
  `Token refresh failed: ${detail}. Run: captable-ai login`,
16237
16665
  "REFRESH_FAILED",
16238
16666
  void 0,
@@ -16246,6 +16674,22 @@ function isTransportError(err) {
16246
16674
  if (!(err instanceof Error)) return false;
16247
16675
  return err.name === "TimeoutError" || err.name === "AbortError" || err.name === "TypeError" || /network|timeout|fetch|ECONNRESET|ENOTFOUND|ETIMEDOUT/i.test(err.message);
16248
16676
  }
16677
+ var ApiRequestError = class extends Error {
16678
+ code;
16679
+ details;
16680
+ constructor(message, code, details) {
16681
+ super(message);
16682
+ this.name = "ApiRequestError";
16683
+ if (code !== void 0) this.code = code;
16684
+ if (details !== void 0) this.details = details;
16685
+ }
16686
+ };
16687
+ function failRequest(softFail, message, code, details, exitCode) {
16688
+ if (softFail) {
16689
+ throw new ApiRequestError(message, code, details);
16690
+ }
16691
+ exitWithError(message, code, details, exitCode);
16692
+ }
16249
16693
  async function refreshTokens(creds) {
16250
16694
  const baseUrl = getApiUrl();
16251
16695
  let res;
@@ -16289,20 +16733,20 @@ async function refreshTokens(creds) {
16289
16733
  await writeCredentials(updated);
16290
16734
  return updated;
16291
16735
  }
16292
- async function getAuthHeaders() {
16736
+ async function getAuthHeaders(softFail = false) {
16293
16737
  let creds = await readCredentials();
16294
16738
  if (!creds) return {};
16295
16739
  if (isTokenExpired(creds.accessToken)) {
16296
16740
  try {
16297
16741
  creds = await refreshTokens(creds);
16298
16742
  } catch (err) {
16299
- exitForRefreshFailure(err);
16743
+ exitForRefreshFailure(err, softFail);
16300
16744
  }
16301
16745
  }
16302
16746
  return { Authorization: `Bearer ${creds.accessToken}` };
16303
16747
  }
16304
16748
  async function apiRequest(path, opts = {}) {
16305
- const { method = "GET", body, query, skipAuth = false } = opts;
16749
+ const { method = "GET", body, query, skipAuth = false, softFail = false } = opts;
16306
16750
  const baseUrl = getApiUrl();
16307
16751
  const url2 = new URL(`${baseUrl}${path}`);
16308
16752
  if (query) {
@@ -16312,7 +16756,7 @@ async function apiRequest(path, opts = {}) {
16312
16756
  }
16313
16757
  const headers = { "Content-Type": "application/json" };
16314
16758
  if (!skipAuth) {
16315
- Object.assign(headers, await getAuthHeaders());
16759
+ Object.assign(headers, await getAuthHeaders(softFail));
16316
16760
  }
16317
16761
  const buildFetchOpts = () => ({
16318
16762
  method,
@@ -16325,14 +16769,16 @@ async function apiRequest(path, opts = {}) {
16325
16769
  res = await fetch(url2.toString(), buildFetchOpts());
16326
16770
  } catch (err) {
16327
16771
  if (err instanceof DOMException && err.name === "TimeoutError") {
16328
- exitWithError(
16772
+ failRequest(
16773
+ softFail,
16329
16774
  "Request timed out. Check your network connection and try again.",
16330
16775
  "TIMEOUT",
16331
16776
  void 0,
16332
16777
  EXIT.NETWORK
16333
16778
  );
16334
16779
  }
16335
- exitWithError(
16780
+ failRequest(
16781
+ softFail,
16336
16782
  `Network error: ${err instanceof Error ? err.message : "connection failed"}`,
16337
16783
  "NETWORK",
16338
16784
  void 0,
@@ -16346,23 +16792,25 @@ async function apiRequest(path, opts = {}) {
16346
16792
  try {
16347
16793
  refreshed = await refreshTokens(creds);
16348
16794
  } catch (refreshErr) {
16349
- exitForRefreshFailure(refreshErr);
16795
+ exitForRefreshFailure(refreshErr, softFail);
16350
16796
  }
16351
16797
  if (refreshed) {
16352
16798
  headers.Authorization = `Bearer ${refreshed.accessToken}`;
16353
16799
  try {
16354
16800
  const retryRes = await fetch(url2.toString(), buildFetchOpts());
16355
- return handleResponse(retryRes);
16801
+ return handleResponse(retryRes, softFail);
16356
16802
  } catch (retryErr) {
16357
16803
  if (retryErr instanceof DOMException && retryErr.name === "TimeoutError") {
16358
- exitWithError(
16804
+ failRequest(
16805
+ softFail,
16359
16806
  "Request timed out. Check your network connection and try again.",
16360
16807
  "TIMEOUT",
16361
16808
  void 0,
16362
16809
  EXIT.NETWORK
16363
16810
  );
16364
16811
  }
16365
- exitWithError(
16812
+ failRequest(
16813
+ softFail,
16366
16814
  `Network error: ${retryErr instanceof Error ? retryErr.message : "connection failed"}`,
16367
16815
  "NETWORK",
16368
16816
  void 0,
@@ -16372,7 +16820,7 @@ async function apiRequest(path, opts = {}) {
16372
16820
  }
16373
16821
  }
16374
16822
  }
16375
- return handleResponse(res);
16823
+ return handleResponse(res, softFail);
16376
16824
  }
16377
16825
  function exitCodeForStatus(status) {
16378
16826
  if (status === 400) return EXIT.VALIDATION;
@@ -16383,14 +16831,26 @@ function exitCodeForStatus(status) {
16383
16831
  if (status >= 500) return EXIT.SERVER_ERROR;
16384
16832
  return EXIT.SERVER_ERROR;
16385
16833
  }
16386
- async function handleResponse(res) {
16387
- const text = await res.text();
16834
+ async function handleResponse(res, softFail = false) {
16835
+ let text;
16836
+ try {
16837
+ text = await res.text();
16838
+ } catch (err) {
16839
+ failRequest(
16840
+ softFail,
16841
+ `Network error: ${err instanceof Error ? err.message : "connection failed"}`,
16842
+ "NETWORK",
16843
+ void 0,
16844
+ EXIT.NETWORK
16845
+ );
16846
+ }
16388
16847
  let parsed;
16389
16848
  try {
16390
16849
  parsed = JSON.parse(text);
16391
16850
  } catch {
16392
16851
  if (!res.ok) {
16393
- exitWithError(
16852
+ failRequest(
16853
+ softFail,
16394
16854
  `HTTP ${res.status}: ${text.slice(0, 200)}`,
16395
16855
  `HTTP_${res.status}`,
16396
16856
  void 0,
@@ -16401,19 +16861,31 @@ async function handleResponse(res) {
16401
16861
  }
16402
16862
  if (!res.ok) {
16403
16863
  const exitCode = exitCodeForStatus(res.status);
16404
- const obj = isRecord(parsed) ? parsed : {};
16864
+ const obj = isRecord2(parsed) ? parsed : {};
16405
16865
  const nested = obj.error;
16406
- if (isRecord(nested)) {
16866
+ if (isRecord2(nested)) {
16407
16867
  const code = typeof nested.code === "string" && nested.code.trim() ? nested.code : void 0;
16408
16868
  const details = coerceDetails(nested.details);
16409
- exitWithError(String(nested.message ?? `HTTP ${res.status}`), code, details, exitCode);
16869
+ failRequest(
16870
+ softFail,
16871
+ String(nested.message ?? `HTTP ${res.status}`),
16872
+ code,
16873
+ details,
16874
+ exitCode
16875
+ );
16410
16876
  }
16411
16877
  if (typeof nested === "string") {
16412
16878
  const code = typeof obj.code === "string" && obj.code.trim() ? obj.code : `HTTP_${res.status}`;
16413
16879
  const details = coerceDetails(obj.details);
16414
- exitWithError(nested, code, details, exitCode);
16880
+ failRequest(softFail, nested, code, details, exitCode);
16415
16881
  }
16416
- exitWithError(`HTTP ${res.status}: ${text.slice(0, 200)}`, void 0, void 0, exitCode);
16882
+ failRequest(
16883
+ softFail,
16884
+ `HTTP ${res.status}: ${text.slice(0, 200)}`,
16885
+ void 0,
16886
+ void 0,
16887
+ exitCode
16888
+ );
16417
16889
  }
16418
16890
  return parsed;
16419
16891
  }
@@ -16529,8 +17001,35 @@ Global:
16529
17001
  --json Machine-readable JSON output (API order, not confidence-sorted)`;
16530
17002
  var HELP_OBJECTS_GET = `objects get \u2014 Show one OCF object
16531
17003
 
17004
+ After the OCF payload, prints a one-line vesting summary when the object (or
17005
+ its vesting_terms_id target) has a condition graph. --json adds vestingSummary
17006
+ as a sibling of data \u2014 not inside ocf.
17007
+
16532
17008
  Usage:
16533
17009
  captable-ai objects get <object-id> [--json]`;
17010
+ var HELP_OBJECTS_DUMP = `objects dump \u2014 Engine-shaped ocf.json
17011
+
17012
+ Assembles each row's ocf payload into the shape processCapTable consumes:
17013
+ { issuer, stockClasses, stockPlans, stakeholders, transactions, vestingTerms, sharePrices }
17014
+
17015
+ Fails if ISSUER is missing or duplicated. Not an OCF zip and not expected.json.
17016
+ Drains up to 400 pages (100,000 objects). Large tables: write a file with --output.
17017
+
17018
+ The list API picks the latest row per object id, then optionally filters status.
17019
+ --status committed therefore omits an object when a newer draft exists.
17020
+ --status draft dumps that latest-row working set (drafts win) so the engine
17021
+ still sees the committed ISSUER and other unchanged baseline objects.
17022
+
17023
+ Usage:
17024
+ captable-ai objects dump [--status committed|draft] [--output path]
17025
+
17026
+ Flags:
17027
+ --status STATUS committed = latest committed-only rows (default).
17028
+ draft = pending working set (latest row per object).
17029
+ --output PATH Write JSON to file (global flag; implies --json)
17030
+
17031
+ Global:
17032
+ --json Machine-readable JSON (dump is always JSON)`;
16534
17033
  var HELP_OBJECTS_UPDATE = `objects update \u2014 Update an OCF object
16535
17034
 
16536
17035
  Usage:
@@ -16619,6 +17118,8 @@ ${HELP_OBJECTS_LIST}
16619
17118
 
16620
17119
  ${HELP_OBJECTS_GET}
16621
17120
 
17121
+ ${HELP_OBJECTS_DUMP}
17122
+
16622
17123
  ${HELP_OBJECTS_UPDATE}
16623
17124
 
16624
17125
  ${HELP_OBJECTS_PATCH}
@@ -17713,11 +18214,11 @@ var JOBS_FILE = join2(CONFIG_DIR2, "jobs.jsonl");
17713
18214
  var LOCK_DIR = join2(CONFIG_DIR2, "jobs.lock");
17714
18215
  var VALID_JOB_STATUSES = /* @__PURE__ */ new Set(["submitted", "running", "complete", "failed"]);
17715
18216
  var VALID_JOB_KINDS = /* @__PURE__ */ new Set(["extraction"]);
17716
- function isRecord2(v) {
18217
+ function isRecord3(v) {
17717
18218
  return typeof v === "object" && v !== null;
17718
18219
  }
17719
18220
  function isJobEntry(value) {
17720
- if (!isRecord2(value)) return false;
18221
+ if (!isRecord3(value)) return false;
17721
18222
  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
18223
  }
17723
18224
  async function ensureDir() {
@@ -18268,11 +18769,11 @@ async function readEntries() {
18268
18769
  }
18269
18770
  return entries;
18270
18771
  }
18271
- function isRecord3(v) {
18772
+ function isRecord4(v) {
18272
18773
  return typeof v === "object" && v !== null;
18273
18774
  }
18274
18775
  function isFeedbackEntry(value) {
18275
- if (!isRecord3(value)) return false;
18776
+ if (!isRecord4(value)) return false;
18276
18777
  return typeof value.ts === "string" && typeof value.text === "string" && typeof value.sent === "boolean";
18277
18778
  }
18278
18779
 
@@ -18489,7 +18990,7 @@ function successHtml() {
18489
18990
  h1{font-size:1.5rem;color:#111}p{color:#666}</style></head>
18490
18991
  <body><div class="card"><h1>Login successful</h1><p>You can close this window and return to the terminal.</p></div></body></html>`;
18491
18992
  }
18492
- function isRecord4(value) {
18993
+ function isRecord5(value) {
18493
18994
  return typeof value === "object" && value !== null && !Array.isArray(value);
18494
18995
  }
18495
18996
  function readStr(obj, ...keys) {
@@ -18510,7 +19011,7 @@ function parsePortalsFromExchange(raw) {
18510
19011
  }
18511
19012
  if (!Array.isArray(parsed)) return [];
18512
19013
  return parsed.flatMap((p) => {
18513
- if (!isRecord4(p)) return [];
19014
+ if (!isRecord5(p)) return [];
18514
19015
  if (typeof p.id !== "string" || p.id.length === 0) return [];
18515
19016
  return [
18516
19017
  {
@@ -18554,7 +19055,7 @@ function tryParseLoginPaste(line, expectedState) {
18554
19055
  } catch {
18555
19056
  return null;
18556
19057
  }
18557
- if (!isRecord4(obj)) return null;
19058
+ if (!isRecord5(obj)) return null;
18558
19059
  if (typeof obj.state !== "string" || !pastedStateMatches(expectedState, obj.state)) return null;
18559
19060
  const accessToken = readStr(obj, "access_token", "accessToken");
18560
19061
  const refreshToken = readStr(obj, "refresh_token", "refreshToken");
@@ -18919,6 +19420,105 @@ async function runObjectsDelete(args, rawArgv) {
18919
19420
  );
18920
19421
  }
18921
19422
 
19423
+ // src/objects-pages.ts
19424
+ var OBJECTS_LIST_MAX_PAGES = 100;
19425
+ var OBJECTS_DUMP_MAX_PAGES = 400;
19426
+ async function fetchAllObjectPages(portalId, query, options) {
19427
+ const maxPages = options?.maxPages ?? OBJECTS_LIST_MAX_PAGES;
19428
+ const objects = [];
19429
+ const documentsById = /* @__PURE__ */ new Map();
19430
+ let cursor = typeof query.cursor === "string" ? query.cursor : void 0;
19431
+ let total = 0;
19432
+ let pages = 0;
19433
+ while (true) {
19434
+ pages += 1;
19435
+ if (pages > maxPages) {
19436
+ exitWithError(
19437
+ `Exceeded max object-list pages (${maxPages}). Narrow the query or raise the page size.`,
19438
+ "ERROR",
19439
+ void 0,
19440
+ EXIT.SERVER_ERROR
19441
+ );
19442
+ }
19443
+ const pageQuery = { ...query };
19444
+ if (cursor !== void 0) pageQuery.cursor = cursor;
19445
+ else delete pageQuery.cursor;
19446
+ const res = await apiRequest(`/${portalId}/objects`, { query: pageQuery });
19447
+ const pageObjects = res.data?.objects ?? [];
19448
+ const pageDocs = res.data?.documents ?? [];
19449
+ objects.push(...pageObjects);
19450
+ for (const doc of pageDocs) {
19451
+ documentsById.set(doc.id, doc);
19452
+ }
19453
+ if (res.meta?.total != null) total = res.meta.total;
19454
+ if (!res.meta?.hasMore) break;
19455
+ if (!res.meta.cursor) {
19456
+ exitWithError(
19457
+ "Pagination response has more results but no continuation cursor.",
19458
+ "ERROR",
19459
+ void 0,
19460
+ EXIT.SERVER_ERROR
19461
+ );
19462
+ }
19463
+ if (cursor !== void 0 && res.meta.cursor === cursor) {
19464
+ exitWithError(
19465
+ "Pagination cursor did not advance; stopped draining object pages.",
19466
+ "ERROR",
19467
+ void 0,
19468
+ EXIT.SERVER_ERROR
19469
+ );
19470
+ }
19471
+ cursor = res.meta.cursor;
19472
+ }
19473
+ return {
19474
+ data: { objects, documents: Array.from(documentsById.values()) },
19475
+ meta: { cursor: null, total: total || objects.length, hasMore: false }
19476
+ };
19477
+ }
19478
+
19479
+ // src/commands/objects-dump.ts
19480
+ var DUMP_PAGE_LIMIT = 250;
19481
+ function dumpStatus(raw) {
19482
+ const status = raw ?? "committed";
19483
+ if (status === "committed" || status === "draft") return status;
19484
+ exitWithError(
19485
+ `--status must be one of: draft, committed (got: "${status}")`,
19486
+ "VALIDATION_FAILED",
19487
+ void 0,
19488
+ EXIT.VALIDATION
19489
+ );
19490
+ }
19491
+ async function runObjectsDump(args, rawArgv) {
19492
+ if (argvRequestsHelp(rawArgv)) {
19493
+ process.stdout.write(`${HELP_OBJECTS_DUMP}
19494
+ `);
19495
+ process.exit(0);
19496
+ }
19497
+ validateFlags(args, { flags: ["status"], booleans: [] });
19498
+ if (args.positional.length > 0) {
19499
+ exitWithError(
19500
+ `Unexpected argument: ${args.positional[0]}. objects dump takes flags only.`,
19501
+ "USAGE",
19502
+ void 0,
19503
+ EXIT.USAGE
19504
+ );
19505
+ }
19506
+ const status = dumpStatus(getFlagValue(args, "status"));
19507
+ const portalId = await getActivePortalId();
19508
+ const query = { limit: DUMP_PAGE_LIMIT };
19509
+ if (status === "committed") query.status = "committed";
19510
+ const page = await fetchAllObjectPages(portalId, query, { maxPages: OBJECTS_DUMP_MAX_PAGES });
19511
+ try {
19512
+ const objects = Array.isArray(page.data?.objects) ? page.data.objects : [];
19513
+ printJson(assembleEngineOcfDump(objects));
19514
+ } catch (err) {
19515
+ if (err instanceof DumpAssemblyError) {
19516
+ exitWithError(err.message, "VALIDATION_FAILED", void 0, EXIT.VALIDATION);
19517
+ }
19518
+ throw err;
19519
+ }
19520
+ }
19521
+
18922
19522
  // src/commands/objects-get.ts
18923
19523
  function shellQuote2(value) {
18924
19524
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -18940,9 +19540,17 @@ async function runObjectsGet(args, rawArgv) {
18940
19540
  if (!obj) {
18941
19541
  exitWithError("Object not found", "OBJECT_NOT_FOUND", void 0, EXIT.NOT_FOUND);
18942
19542
  }
19543
+ const vestingSummary = await resolveVestingSummary(obj.ocf, async (termsId) => {
19544
+ const terms = await apiRequest(
19545
+ `/${portalId}/objects/${encodeURIComponent(termsId)}`,
19546
+ { softFail: true }
19547
+ );
19548
+ return terms.data?.ocf ?? null;
19549
+ });
18943
19550
  if (isJsonMode()) {
18944
19551
  const out = { data: obj };
18945
19552
  if (res.meta !== void 0) out.meta = res.meta;
19553
+ if (vestingSummary) out.vestingSummary = vestingSummary;
18946
19554
  printJson(out);
18947
19555
  return;
18948
19556
  }
@@ -18977,61 +19585,10 @@ Object: ${obj.id}
18977
19585
  }
18978
19586
  printLine("\nOCF data:\n");
18979
19587
  printJson(obj.ocf);
18980
- printLine();
18981
- }
18982
-
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
- };
19588
+ if (vestingSummary) printLine(`
19589
+ ${vestingSummary}
19590
+ `);
19591
+ else printLine();
19035
19592
  }
19036
19593
 
19037
19594
  // src/validate.ts
@@ -19103,7 +19660,7 @@ function validateIsoDateRange(fromRaw, toRaw) {
19103
19660
  // src/commands/objects-list.ts
19104
19661
  var CONFIDENCE_RED_MAX = 64;
19105
19662
  var CONFIDENCE_AMBER_MAX = 84;
19106
- function isRecord5(value) {
19663
+ function isRecord6(value) {
19107
19664
  return typeof value === "object" && value !== null && !Array.isArray(value);
19108
19665
  }
19109
19666
  function getValidatedMaxConfidence(raw) {
@@ -19142,7 +19699,7 @@ function stakeholderDisplayName(ocf) {
19142
19699
  if (!ocf) return null;
19143
19700
  if (typeof ocf.name === "string" && ocf.name.trim()) return ocf.name.trim();
19144
19701
  if (typeof ocf.legal_name === "string" && ocf.legal_name.trim()) return ocf.legal_name.trim();
19145
- if (isRecord5(ocf.name)) {
19702
+ if (isRecord6(ocf.name)) {
19146
19703
  const legal = ocf.name.legal_name;
19147
19704
  if (typeof legal === "string" && legal.trim()) return legal.trim();
19148
19705
  const parts = [ocf.name.first_name, ocf.name.last_name].filter(
@@ -19354,7 +19911,7 @@ async function readStdin() {
19354
19911
  }
19355
19912
  return Buffer.concat(chunks).toString("utf-8");
19356
19913
  }
19357
- function isRecord6(value) {
19914
+ function isRecord7(value) {
19358
19915
  return typeof value === "object" && value !== null && !Array.isArray(value);
19359
19916
  }
19360
19917
  function parsePatchPayload(raw) {
@@ -19369,7 +19926,7 @@ function parsePatchPayload(raw) {
19369
19926
  EXIT.VALIDATION
19370
19927
  );
19371
19928
  }
19372
- if (!isRecord6(parsed)) {
19929
+ if (!isRecord7(parsed)) {
19373
19930
  exitWithError(
19374
19931
  "OCF patch payload must be a JSON object.",
19375
19932
  "VALIDATION_FAILED",
@@ -19589,7 +20146,7 @@ async function readStdin2() {
19589
20146
  }
19590
20147
  return Buffer.concat(chunks).toString("utf-8");
19591
20148
  }
19592
- function isRecord7(value) {
20149
+ function isRecord8(value) {
19593
20150
  return typeof value === "object" && value !== null && !Array.isArray(value);
19594
20151
  }
19595
20152
  function parseOcfPayload(raw) {
@@ -19604,7 +20161,7 @@ function parseOcfPayload(raw) {
19604
20161
  EXIT.VALIDATION
19605
20162
  );
19606
20163
  }
19607
- if (!isRecord7(parsed)) {
20164
+ if (!isRecord8(parsed)) {
19608
20165
  exitWithError(
19609
20166
  "OCF payload must be a JSON object.",
19610
20167
  "VALIDATION_FAILED",
@@ -19973,20 +20530,20 @@ import { homedir as homedir4 } from "os";
19973
20530
  import { join as join4 } from "path";
19974
20531
  var CONFIG_DIR4 = join4(homedir4(), ".captable-ai");
19975
20532
  var PROFILES_FILE = join4(CONFIG_DIR4, "profiles.json");
19976
- function isRecord8(value) {
20533
+ function isRecord9(value) {
19977
20534
  return typeof value === "object" && value !== null && !Array.isArray(value);
19978
20535
  }
19979
20536
  function isErrno3(err, code) {
19980
- return isRecord8(err) && err.code === code;
20537
+ return isRecord9(err) && err.code === code;
19981
20538
  }
19982
20539
  function parseProfiles(raw) {
19983
20540
  const parsed = JSON.parse(raw);
19984
- if (!isRecord8(parsed) || !isRecord8(parsed.profiles)) {
20541
+ if (!isRecord9(parsed) || !isRecord9(parsed.profiles)) {
19985
20542
  throw new Error("profiles.json must contain a profiles object.");
19986
20543
  }
19987
20544
  const profiles = {};
19988
20545
  for (const [name, value] of Object.entries(parsed.profiles)) {
19989
- if (!isRecord8(value) || typeof value.company !== "string" || !value.company.trim()) {
20546
+ if (!isRecord9(value) || typeof value.company !== "string" || !value.company.trim()) {
19990
20547
  throw new Error(`Profile "${name}" must include a non-empty company string.`);
19991
20548
  }
19992
20549
  const stage = typeof value.stage === "string" && value.stage.trim() ? value.stage : void 0;
@@ -20670,7 +21227,8 @@ Cap Table (read):
20670
21227
  Review: --stakeholder, --document, --max-confidence N (requires --all)
20671
21228
  Pagination: --limit (1-250), --cursor, --all
20672
21229
  Dates: --from / --to as YYYY-MM-DD
20673
- objects get <id> Show a single OCF object
21230
+ objects get <id> Show a single OCF object (vesting summary when present)
21231
+ objects dump Engine-shaped ocf.json (default --status committed; --output path)
20674
21232
  schemas list List OCF object types
20675
21233
  schemas get <type> Show schema for an OCF type
20676
21234
 
@@ -20957,6 +21515,9 @@ async function main() {
20957
21515
  case "get":
20958
21516
  await runObjectsGet(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
20959
21517
  break;
21518
+ case "dump":
21519
+ await runObjectsDump(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
21520
+ break;
20960
21521
  case "update":
20961
21522
  await runObjectsUpdate(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
20962
21523
  break;
@@ -20974,7 +21535,7 @@ async function main() {
20974
21535
  break;
20975
21536
  default:
20976
21537
  exitWithError(
20977
- `Unknown objects command: ${sub ?? "(none)"}. Try: objects list | get | update | patch | revert | versions | delete`,
21538
+ `Unknown objects command: ${sub ?? "(none)"}. Try: objects list | get | dump | update | patch | revert | versions | delete`,
20978
21539
  "UNKNOWN_COMMAND",
20979
21540
  void 0,
20980
21541
  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.7",
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",