@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.
- package/README.md +1 -1
- package/dist/index.js +662 -101
- 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
|
|
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: "
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
16864
|
+
const obj = isRecord2(parsed) ? parsed : {};
|
|
16405
16865
|
const nested = obj.error;
|
|
16406
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
16880
|
+
failRequest(softFail, nested, code, details, exitCode);
|
|
16415
16881
|
}
|
|
16416
|
-
|
|
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
|
|
18217
|
+
function isRecord3(v) {
|
|
17717
18218
|
return typeof v === "object" && v !== null;
|
|
17718
18219
|
}
|
|
17719
18220
|
function isJobEntry(value) {
|
|
17720
|
-
if (!
|
|
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
|
|
18772
|
+
function isRecord4(v) {
|
|
18272
18773
|
return typeof v === "object" && v !== null;
|
|
18273
18774
|
}
|
|
18274
18775
|
function isFeedbackEntry(value) {
|
|
18275
|
-
if (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
20537
|
+
return isRecord9(err) && err.code === code;
|
|
19981
20538
|
}
|
|
19982
20539
|
function parseProfiles(raw) {
|
|
19983
20540
|
const parsed = JSON.parse(raw);
|
|
19984
|
-
if (!
|
|
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 (!
|
|
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