@fairmint/captable-ai 0.1.39 → 0.1.41
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 +27 -4
- package/dist/index.js +352 -134
- package/package.json +2 -5
- package/dist/index.d.ts +0 -1
package/README.md
CHANGED
|
@@ -94,6 +94,7 @@ automated, but a few habits make extraction faster and more accurate:
|
|
|
94
94
|
| `documents upload\|list\|download\|delete` | Manage source documents |
|
|
95
95
|
| `documents trigger-extraction\|cancel-extraction\|restart-extraction\|reprocess` | Control the OCF extraction pipeline |
|
|
96
96
|
| `documents processing-status\|classification-types\|classification-update` | Inspect document status / classification |
|
|
97
|
+
| `agreements list\|download` | Signed investment and grant agreements, one-hour download links |
|
|
97
98
|
| `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`) |
|
|
98
99
|
| `schemas list\|get` | Browse the OCF object-type registry |
|
|
99
100
|
| `commit` | Commit drafts to the cap table — `--all` or `--objects id1,id2` (`--dry-run` to preview) |
|
|
@@ -111,6 +112,26 @@ automated, but a few habits make extraction faster and more accurate:
|
|
|
111
112
|
Run `captable-ai <command> --help` for full usage, or `captable-ai commands --json` for the
|
|
112
113
|
authoritative machine-readable list.
|
|
113
114
|
|
|
115
|
+
## Signed agreements
|
|
116
|
+
|
|
117
|
+
`documents list` holds what the extraction pipeline ingested. Signed agreements are
|
|
118
|
+
listed separately, whether or not they were ingested:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
captable-ai agreements list # investments and grants, newest signed first
|
|
122
|
+
captable-ai agreements list --type grant --json
|
|
123
|
+
captable-ai agreements download grant:<awardId> # one-hour download link
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- **Investments** — the signed file of a SAFE (Send One SAFE, Community SAFE), custom
|
|
127
|
+
document, or legacy investment, once signed, whether or not payment is marked.
|
|
128
|
+
- **Grants** — Send One Grants both parties signed. The download is the agreement,
|
|
129
|
+
supporting documents, and both signature certificates. The grantee's tax form and
|
|
130
|
+
83(b) election are never included; download those from Studio.
|
|
131
|
+
|
|
132
|
+
Legacy grant agreements imported through the extraction pipeline stay under
|
|
133
|
+
`documents list` / `documents download`.
|
|
134
|
+
|
|
114
135
|
## Instrument registers
|
|
115
136
|
|
|
116
137
|
`captable-ai export` with no register is the full legal cap table: a multi-sheet Excel
|
|
@@ -125,16 +146,18 @@ captable-ai export convertibles --type SAFE --csv # SAFEs only, CSV on
|
|
|
125
146
|
captable-ai export convertibles --type NOTE --json --output notes.json
|
|
126
147
|
```
|
|
127
148
|
|
|
128
|
-
Every register
|
|
129
|
-
|
|
149
|
+
Every register carries the holder fields (name, email, address, country, state) and the
|
|
150
|
+
economics of that family. Grants lead with the grant date; convertibles put the signature
|
|
151
|
+
date, execution date, and status right after the holder. Without a format flag the result is one Excel sheet
|
|
130
152
|
returned as a download link. `--csv` prints RFC-4180 CSV whose header row is the JSON
|
|
131
153
|
keys; `--json` prints the rows as a JSON array. Pass one or neither. `--output <path>`
|
|
132
154
|
writes the CSV or JSON body to a file and, for these commands only, does not imply
|
|
133
155
|
`--json`.
|
|
134
156
|
|
|
135
157
|
By default a register lists the committed cap table only. `--include-pending` adds what
|
|
136
|
-
is not committed yet: draft OCF objects, plus `investment
|
|
137
|
-
have no OCF transaction. A row that already has
|
|
158
|
+
is not committed yet: draft OCF objects, plus `investment`, `equity_grant`, and
|
|
159
|
+
`equity_award` (Send One Grant) rows that have no OCF transaction. A row that already has
|
|
160
|
+
an OCF object appears once. Registers
|
|
138
161
|
are read-only and never commit drafts. You must be an owner or admin of the active
|
|
139
162
|
company.
|
|
140
163
|
|
package/dist/index.js
CHANGED
|
@@ -16151,21 +16151,27 @@ var exportResultSchema = external_exports.object({
|
|
|
16151
16151
|
// ../captable-ai-core/dist/export-registers.js
|
|
16152
16152
|
var EXPORT_REGISTERS = ["stock", "grants", "warrants", "convertibles"];
|
|
16153
16153
|
var CONVERTIBLE_TYPE_FILTERS = ["SAFE", "NOTE"];
|
|
16154
|
-
var REGISTER_ROW_SOURCES = [
|
|
16154
|
+
var REGISTER_ROW_SOURCES = [
|
|
16155
|
+
"committed",
|
|
16156
|
+
"draft",
|
|
16157
|
+
"investment",
|
|
16158
|
+
"equity_grant",
|
|
16159
|
+
"equity_award"
|
|
16160
|
+
];
|
|
16155
16161
|
var isoDateSchema = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Must be YYYY-MM-DD").refine((val) => {
|
|
16156
16162
|
const [y, m, d] = val.split("-").map(Number);
|
|
16157
16163
|
const parsed = new Date(Date.UTC(y, m - 1, d));
|
|
16158
16164
|
return parsed.toISOString().slice(0, 10) === val;
|
|
16159
16165
|
}, { message: "Invalid calendar date (e.g. Feb 30 does not exist)" });
|
|
16160
16166
|
var exportRegisterInputSchema = external_exports.object({
|
|
16161
|
-
includePending: external_exports.boolean().optional().default(false).describe("Add draft OCF objects and investment / equity_grant rows that have no OCF transaction yet."),
|
|
16167
|
+
includePending: external_exports.boolean().optional().default(false).describe("Add draft OCF objects and investment / equity_grant / equity_award rows that have no OCF transaction yet."),
|
|
16162
16168
|
asOf: isoDateSchema.optional().describe("Only securities issued on or before this date (YYYY-MM-DD)."),
|
|
16163
16169
|
convertibleType: external_exports.enum(CONVERTIBLE_TYPE_FILTERS).optional().describe("convertibles only: keep SAFE or NOTE rows."),
|
|
16164
16170
|
format: external_exports.enum(["rows", "xlsx"]).optional().default("rows").describe("rows returns the rows as JSON; xlsx also uploads a one-sheet workbook.")
|
|
16165
16171
|
});
|
|
16166
16172
|
var cell = external_exports.union([external_exports.string(), external_exports.number()]).nullable();
|
|
16167
16173
|
var holderShape = {
|
|
16168
|
-
source: external_exports.enum(REGISTER_ROW_SOURCES).describe("committed, draft, investment, or
|
|
16174
|
+
source: external_exports.enum(REGISTER_ROW_SOURCES).describe("committed, draft, investment, equity_grant, or equity_award."),
|
|
16169
16175
|
security_id: external_exports.string().nullable().describe("OCF security id, null for operational-only rows."),
|
|
16170
16176
|
ocf_object_id: external_exports.string().nullable().describe("OCF transaction object id."),
|
|
16171
16177
|
holder_name: cell.describe("Holder legal name."),
|
|
@@ -16187,11 +16193,13 @@ var grantRegisterRowSchema = external_exports.object({
|
|
|
16187
16193
|
award_type: cell.describe("ISO, NSO, RSU, or another OCF compensation type."),
|
|
16188
16194
|
quantity: cell.describe("Shares or units granted."),
|
|
16189
16195
|
dollar_amount: cell.describe("equity_grant.dollar_amount when a linked grant row exists."),
|
|
16190
|
-
status: cell.describe("equity_grant workflow status, or COMMITTED / DRAFT for OCF-only grants."),
|
|
16196
|
+
status: cell.describe("equity_grant / equity_award workflow status, or COMMITTED / DRAFT for OCF-only grants."),
|
|
16191
16197
|
accepted_on: cell.describe("Date the grantee signed (YYYY-MM-DD)."),
|
|
16198
|
+
vesting_schedule: cell.describe("Vesting schedule name, e.g. 4 years, 1-year cliff."),
|
|
16192
16199
|
cliff_months: cell.describe("Cliff length in months."),
|
|
16193
16200
|
vesting_months: cell.describe("Total vesting length in months."),
|
|
16194
16201
|
effective_date: cell.describe("Grant date (YYYY-MM-DD)."),
|
|
16202
|
+
vesting_start_date: cell.describe("Vesting commencement date (YYYY-MM-DD)."),
|
|
16195
16203
|
exercise_price: cell.describe("Exercise price per share, blank for RSUs."),
|
|
16196
16204
|
series: cell.describe("Series name (Hospitable's team / retention / community categories)."),
|
|
16197
16205
|
plan: cell.describe("Stock plan name.")
|
|
@@ -16207,6 +16215,9 @@ var warrantRegisterRowSchema = external_exports.object({
|
|
|
16207
16215
|
});
|
|
16208
16216
|
var convertibleRegisterRowSchema = external_exports.object({
|
|
16209
16217
|
...holderShape,
|
|
16218
|
+
signature_date: cell.describe("Date the investor signed (YYYY-MM-DD)."),
|
|
16219
|
+
execution_date: cell.describe("Date the agreement was fully executed (YYYY-MM-DD)."),
|
|
16220
|
+
status: cell.describe("investment workflow status, or COMMITTED / DRAFT for OCF-only rows."),
|
|
16210
16221
|
convertible_type: cell.describe("SAFE, NOTE, or SECURITY."),
|
|
16211
16222
|
date: cell.describe("Investment date (YYYY-MM-DD)."),
|
|
16212
16223
|
round: cell.describe("Round / series / offering name."),
|
|
@@ -16237,17 +16248,24 @@ var REGISTER_COLUMNS = {
|
|
|
16237
16248
|
{ key: "amount", header: "Amount", format: "money" },
|
|
16238
16249
|
...TRAILING_COLUMNS
|
|
16239
16250
|
],
|
|
16251
|
+
// Leading columns follow the grant file Hospitable asked for (ENG-2200).
|
|
16240
16252
|
grants: [
|
|
16241
|
-
|
|
16242
|
-
{ key: "
|
|
16253
|
+
{ key: "effective_date", header: "Grant Date", format: "date" },
|
|
16254
|
+
{ key: "holder_name", header: "Name", format: "text" },
|
|
16255
|
+
{ key: "holder_email", header: "Email", format: "text" },
|
|
16256
|
+
{ key: "award_type", header: "Type", format: "text" },
|
|
16243
16257
|
{ key: "quantity", header: "Quantity", format: "number" },
|
|
16244
|
-
{ key: "dollar_amount", header: "Dollar Amount", format: "money" },
|
|
16245
16258
|
{ key: "status", header: "Status", format: "text" },
|
|
16246
|
-
{ key: "
|
|
16247
|
-
{ key: "
|
|
16248
|
-
{ key: "
|
|
16249
|
-
{ key: "effective_date", header: "Effective Date", format: "date" },
|
|
16259
|
+
{ key: "vesting_schedule", header: "Vesting", format: "text" },
|
|
16260
|
+
{ key: "vesting_months", header: "Vesting Period (Months)", format: "number" },
|
|
16261
|
+
{ key: "cliff_months", header: "Cliff Months", format: "number" },
|
|
16250
16262
|
{ key: "exercise_price", header: "Exercise Price", format: "money" },
|
|
16263
|
+
{ key: "accepted_on", header: "Signed Date", format: "date" },
|
|
16264
|
+
{ key: "vesting_start_date", header: "Vesting Start Date", format: "date" },
|
|
16265
|
+
{ key: "holder_address", header: "Address", format: "text" },
|
|
16266
|
+
{ key: "holder_country", header: "Country", format: "text" },
|
|
16267
|
+
{ key: "holder_state", header: "State", format: "text" },
|
|
16268
|
+
{ key: "dollar_amount", header: "Dollar Amount", format: "money" },
|
|
16251
16269
|
{ key: "series", header: "Series", format: "text" },
|
|
16252
16270
|
{ key: "plan", header: "Plan", format: "text" },
|
|
16253
16271
|
...TRAILING_COLUMNS
|
|
@@ -16262,13 +16280,18 @@ var REGISTER_COLUMNS = {
|
|
|
16262
16280
|
{ key: "issue_date", header: "Issue Date", format: "date" },
|
|
16263
16281
|
...TRAILING_COLUMNS
|
|
16264
16282
|
],
|
|
16283
|
+
// Signature / execution / status / round / amount / funding follow the SAFE
|
|
16284
|
+
// file Hospitable asked for (ENG-2200).
|
|
16265
16285
|
convertibles: [
|
|
16266
16286
|
...HOLDER_COLUMNS,
|
|
16267
|
-
{ key: "
|
|
16268
|
-
{ key: "
|
|
16287
|
+
{ key: "signature_date", header: "Signature Date", format: "date" },
|
|
16288
|
+
{ key: "execution_date", header: "Execution Date", format: "date" },
|
|
16289
|
+
{ key: "status", header: "Status", format: "text" },
|
|
16269
16290
|
{ key: "round", header: "Round", format: "text" },
|
|
16270
16291
|
{ key: "amount", header: "Amount", format: "money" },
|
|
16271
16292
|
{ key: "funding_method", header: "Funding Method", format: "text" },
|
|
16293
|
+
{ key: "convertible_type", header: "Type", format: "text" },
|
|
16294
|
+
{ key: "date", header: "Investment Date", format: "date" },
|
|
16272
16295
|
{ key: "valuation_cap", header: "Valuation Cap", format: "money" },
|
|
16273
16296
|
{ key: "discount", header: "Discount (%)", format: "percent" },
|
|
16274
16297
|
...TRAILING_COLUMNS
|
|
@@ -16990,6 +17013,37 @@ var securityholderFileResultSchema = external_exports.object({
|
|
|
16990
17013
|
s3Key: external_exports.string().describe("S3 object key.")
|
|
16991
17014
|
});
|
|
16992
17015
|
|
|
17016
|
+
// ../captable-ai-core/dist/signed-agreements.js
|
|
17017
|
+
var SIGNED_AGREEMENT_TYPES = ["investment", "grant"];
|
|
17018
|
+
var UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
17019
|
+
function isSignedAgreementType(value) {
|
|
17020
|
+
return value === "investment" || value === "grant";
|
|
17021
|
+
}
|
|
17022
|
+
var SIGNED_AGREEMENT_REF_PATTERN = new RegExp(`^(investment|grant):(${UUID})$`, "i");
|
|
17023
|
+
function parseSignedAgreementRef(ref) {
|
|
17024
|
+
const match = SIGNED_AGREEMENT_REF_PATTERN.exec(ref.trim());
|
|
17025
|
+
const type = match?.[1]?.toLowerCase();
|
|
17026
|
+
const id = match?.[2]?.toLowerCase();
|
|
17027
|
+
if (!type || !id || !isSignedAgreementType(type))
|
|
17028
|
+
return null;
|
|
17029
|
+
return { type, id };
|
|
17030
|
+
}
|
|
17031
|
+
var signedAgreementSchema = external_exports.object({
|
|
17032
|
+
ref: external_exports.string().describe("Pass to `agreements download`: investment:<s3AssetId> or grant:<awardId>."),
|
|
17033
|
+
type: external_exports.enum(SIGNED_AGREEMENT_TYPES).describe("investment or grant."),
|
|
17034
|
+
name: external_exports.string().describe("File name."),
|
|
17035
|
+
holder_name: external_exports.string().nullable().describe("Investor or grantee."),
|
|
17036
|
+
holder_email: external_exports.string().nullable().describe("Investor or grantee email."),
|
|
17037
|
+
offering: external_exports.string().nullable().describe("Series or fundraising title (investments); instrument (grants)."),
|
|
17038
|
+
status: external_exports.string().describe("Investment or award workflow status."),
|
|
17039
|
+
signed_on: external_exports.string().nullable().describe("Date the agreement was fully signed (YYYY-MM-DD)."),
|
|
17040
|
+
investment_id: external_exports.string().nullable().describe("investment.id, for investment agreements."),
|
|
17041
|
+
equity_award_id: external_exports.string().nullable().describe("equity_award.id, for grants.")
|
|
17042
|
+
});
|
|
17043
|
+
var signedAgreementListResultSchema = external_exports.object({
|
|
17044
|
+
items: external_exports.array(signedAgreementSchema).describe("Newest signed first.")
|
|
17045
|
+
});
|
|
17046
|
+
|
|
16993
17047
|
// ../captable-ai-core/dist/snapshot.js
|
|
16994
17048
|
var statusErrorSchema = external_exports.object({ message: external_exports.string() });
|
|
16995
17049
|
var statusWarningSchema = external_exports.object({
|
|
@@ -17715,6 +17769,223 @@ async function handleResponse(res, softFail = false) {
|
|
|
17715
17769
|
return parsed;
|
|
17716
17770
|
}
|
|
17717
17771
|
|
|
17772
|
+
// src/flags.ts
|
|
17773
|
+
function looksLikeFlag(value) {
|
|
17774
|
+
if (!value.startsWith("-")) return false;
|
|
17775
|
+
if (value === "-" || /^-\d+(?:\.\d+)?$/.test(value)) return false;
|
|
17776
|
+
return true;
|
|
17777
|
+
}
|
|
17778
|
+
function parseArgs(argv) {
|
|
17779
|
+
const positional = [];
|
|
17780
|
+
const positionalIndexes = [];
|
|
17781
|
+
const flags = {};
|
|
17782
|
+
const flagValueSources = {};
|
|
17783
|
+
const flagValueIndexes = {};
|
|
17784
|
+
const boolean4 = /* @__PURE__ */ new Set();
|
|
17785
|
+
let i = 0;
|
|
17786
|
+
while (i < argv.length) {
|
|
17787
|
+
const arg = argv[i];
|
|
17788
|
+
if (arg.startsWith("--")) {
|
|
17789
|
+
const eqIdx = arg.indexOf("=");
|
|
17790
|
+
if (eqIdx !== -1) {
|
|
17791
|
+
const key = arg.slice(2, eqIdx);
|
|
17792
|
+
flags[key] = arg.slice(eqIdx + 1);
|
|
17793
|
+
flagValueSources[key] = "equals";
|
|
17794
|
+
flagValueIndexes[key] = i;
|
|
17795
|
+
} else {
|
|
17796
|
+
const key = arg.slice(2);
|
|
17797
|
+
const next = argv[i + 1];
|
|
17798
|
+
if (next !== void 0 && !looksLikeFlag(next)) {
|
|
17799
|
+
flags[key] = next;
|
|
17800
|
+
flagValueSources[key] = "next";
|
|
17801
|
+
flagValueIndexes[key] = i + 1;
|
|
17802
|
+
i++;
|
|
17803
|
+
} else {
|
|
17804
|
+
boolean4.add(key);
|
|
17805
|
+
}
|
|
17806
|
+
}
|
|
17807
|
+
} else {
|
|
17808
|
+
positional.push(arg);
|
|
17809
|
+
positionalIndexes.push(i);
|
|
17810
|
+
}
|
|
17811
|
+
i++;
|
|
17812
|
+
}
|
|
17813
|
+
return { positional, positionalIndexes, flags, flagValueSources, flagValueIndexes, boolean: boolean4 };
|
|
17814
|
+
}
|
|
17815
|
+
function hasFlag(args, name) {
|
|
17816
|
+
return args.boolean.has(name) || name in args.flags;
|
|
17817
|
+
}
|
|
17818
|
+
function getFlagValue(args, name) {
|
|
17819
|
+
return args.flags[name];
|
|
17820
|
+
}
|
|
17821
|
+
function argvRequestsHelp(argv) {
|
|
17822
|
+
if (argv.includes("--help") || argv.includes("-h")) return true;
|
|
17823
|
+
return argv[0] === "help";
|
|
17824
|
+
}
|
|
17825
|
+
|
|
17826
|
+
// src/validate-flags.ts
|
|
17827
|
+
function restorePositional(args, value, originalIndex) {
|
|
17828
|
+
const insertAt = args.positionalIndexes.findIndex((index) => index > originalIndex);
|
|
17829
|
+
if (insertAt === -1) {
|
|
17830
|
+
args.positional.push(value);
|
|
17831
|
+
args.positionalIndexes.push(originalIndex);
|
|
17832
|
+
return;
|
|
17833
|
+
}
|
|
17834
|
+
args.positional.splice(insertAt, 0, value);
|
|
17835
|
+
args.positionalIndexes.splice(insertAt, 0, originalIndex);
|
|
17836
|
+
}
|
|
17837
|
+
function validateFlags(args, allowed) {
|
|
17838
|
+
const valueFlags = new Set(allowed.flags ?? []);
|
|
17839
|
+
const booleanFlags = new Set(allowed.booleans ?? []);
|
|
17840
|
+
const allAllowed = /* @__PURE__ */ new Set([...valueFlags, ...booleanFlags]);
|
|
17841
|
+
const allList = [...allAllowed].map((f) => `--${f}`).join(", ");
|
|
17842
|
+
for (const key of Object.keys(args.flags)) {
|
|
17843
|
+
if (!allAllowed.has(key)) {
|
|
17844
|
+
exitWithError(
|
|
17845
|
+
`Unknown flag: --${key}. Allowed flags: ${allList || "(none)"}`,
|
|
17846
|
+
"UNKNOWN_FLAG",
|
|
17847
|
+
void 0,
|
|
17848
|
+
EXIT.USAGE
|
|
17849
|
+
);
|
|
17850
|
+
}
|
|
17851
|
+
if (booleanFlags.has(key) && !valueFlags.has(key) && args.flagValueSources[key] === "next") {
|
|
17852
|
+
args.boolean.add(key);
|
|
17853
|
+
restorePositional(args, args.flags[key], args.flagValueIndexes[key]);
|
|
17854
|
+
delete args.flags[key];
|
|
17855
|
+
delete args.flagValueSources[key];
|
|
17856
|
+
delete args.flagValueIndexes[key];
|
|
17857
|
+
continue;
|
|
17858
|
+
}
|
|
17859
|
+
if (booleanFlags.has(key) && !valueFlags.has(key)) {
|
|
17860
|
+
exitWithError(
|
|
17861
|
+
`Flag --${key} does not take a value.`,
|
|
17862
|
+
"UNEXPECTED_FLAG_VALUE",
|
|
17863
|
+
void 0,
|
|
17864
|
+
EXIT.USAGE
|
|
17865
|
+
);
|
|
17866
|
+
}
|
|
17867
|
+
}
|
|
17868
|
+
for (const key of args.boolean) {
|
|
17869
|
+
if (valueFlags.has(key)) {
|
|
17870
|
+
exitWithError(`Flag --${key} requires a value.`, "MISSING_FLAG_VALUE", void 0, EXIT.USAGE);
|
|
17871
|
+
}
|
|
17872
|
+
if (!booleanFlags.has(key)) {
|
|
17873
|
+
exitWithError(
|
|
17874
|
+
`Unknown flag: --${key}. Allowed flags: ${allList || "(none)"}`,
|
|
17875
|
+
"UNKNOWN_FLAG",
|
|
17876
|
+
void 0,
|
|
17877
|
+
EXIT.USAGE
|
|
17878
|
+
);
|
|
17879
|
+
}
|
|
17880
|
+
}
|
|
17881
|
+
}
|
|
17882
|
+
|
|
17883
|
+
// src/commands/agreements.ts
|
|
17884
|
+
var LIST_USAGE = "Usage: captable-ai agreements list [--type investment|grant] [--json]";
|
|
17885
|
+
var DOWNLOAD_USAGE = "Usage: captable-ai agreements download <ref> [--json]";
|
|
17886
|
+
async function runAgreementsList(args, rawArgv) {
|
|
17887
|
+
if (argvRequestsHelp(rawArgv)) {
|
|
17888
|
+
process.stdout.write(
|
|
17889
|
+
`agreements list \u2014 List signed investment and grant agreements
|
|
17890
|
+
|
|
17891
|
+
${LIST_USAGE}
|
|
17892
|
+
|
|
17893
|
+
Investment agreements (SAFE, custom document, legacy) are listed once signed, paid or not.
|
|
17894
|
+
Grants are Send One Grants both parties signed. Pass a row's ref to \`agreements download\`.
|
|
17895
|
+
`
|
|
17896
|
+
);
|
|
17897
|
+
process.exit(0);
|
|
17898
|
+
}
|
|
17899
|
+
validateFlags(args, { flags: ["type"], booleans: [] });
|
|
17900
|
+
const type = getFlagValue(args, "type");
|
|
17901
|
+
if (type && !isSignedAgreementType(type)) {
|
|
17902
|
+
exitWithError(
|
|
17903
|
+
`--type must be one of: ${SIGNED_AGREEMENT_TYPES.join(", ")} (got: "${type}")`,
|
|
17904
|
+
"VALIDATION_FAILED",
|
|
17905
|
+
void 0,
|
|
17906
|
+
EXIT.VALIDATION
|
|
17907
|
+
);
|
|
17908
|
+
}
|
|
17909
|
+
const portalId = await getActivePortalId();
|
|
17910
|
+
const res = await apiRequest(`/${portalId}/agreements`);
|
|
17911
|
+
const items = (res.data?.items ?? []).filter((item) => !type || item.type === type);
|
|
17912
|
+
if (isJsonMode()) {
|
|
17913
|
+
printJson({ data: { items } });
|
|
17914
|
+
return;
|
|
17915
|
+
}
|
|
17916
|
+
if (items.length === 0) {
|
|
17917
|
+
printLine("No signed agreements found.");
|
|
17918
|
+
return;
|
|
17919
|
+
}
|
|
17920
|
+
printLine(`
|
|
17921
|
+
${items.length} signed agreement(s):
|
|
17922
|
+
`);
|
|
17923
|
+
printTable(
|
|
17924
|
+
items.map((item) => ({
|
|
17925
|
+
holder: item.holder_name ?? "\u2014",
|
|
17926
|
+
type: item.type,
|
|
17927
|
+
offering: item.offering ?? "\u2014",
|
|
17928
|
+
status: item.status,
|
|
17929
|
+
signed: item.signed_on ?? "\u2014",
|
|
17930
|
+
ref: item.ref
|
|
17931
|
+
})),
|
|
17932
|
+
[
|
|
17933
|
+
{ key: "holder", label: "Holder", width: 26 },
|
|
17934
|
+
{ key: "type", label: "Type", width: 10 },
|
|
17935
|
+
{ key: "offering", label: "Offering", width: 22 },
|
|
17936
|
+
{ key: "status", label: "Status", width: 14 },
|
|
17937
|
+
{ key: "signed", label: "Signed", width: 12 },
|
|
17938
|
+
{ key: "ref", label: "Ref" }
|
|
17939
|
+
]
|
|
17940
|
+
);
|
|
17941
|
+
printLine("\n Download one: captable-ai agreements download <ref>\n");
|
|
17942
|
+
}
|
|
17943
|
+
async function runAgreementsDownload(args, rawArgv) {
|
|
17944
|
+
if (argvRequestsHelp(rawArgv)) {
|
|
17945
|
+
process.stdout.write(
|
|
17946
|
+
`agreements download \u2014 Get a one-hour download URL for a signed agreement
|
|
17947
|
+
|
|
17948
|
+
${DOWNLOAD_USAGE}
|
|
17949
|
+
|
|
17950
|
+
ref is from \`agreements list\`: investment:<s3AssetId> or grant:<awardId>.
|
|
17951
|
+
A grant downloads as the agreement plus signature certificates; tax forms and
|
|
17952
|
+
83(b) elections are not included (download those from Studio).
|
|
17953
|
+
`
|
|
17954
|
+
);
|
|
17955
|
+
process.exit(0);
|
|
17956
|
+
}
|
|
17957
|
+
validateFlags(args, { flags: [], booleans: [] });
|
|
17958
|
+
if (args.positional.length !== 1) {
|
|
17959
|
+
exitWithUsage("Expected exactly one agreement ref.", DOWNLOAD_USAGE);
|
|
17960
|
+
}
|
|
17961
|
+
const ref = args.positional[0].trim();
|
|
17962
|
+
if (!parseSignedAgreementRef(ref)) {
|
|
17963
|
+
exitWithUsage(
|
|
17964
|
+
`Invalid ref "${ref}". Expected investment:<s3AssetId> or grant:<awardId> from \`agreements list\`.`,
|
|
17965
|
+
DOWNLOAD_USAGE
|
|
17966
|
+
);
|
|
17967
|
+
}
|
|
17968
|
+
const portalId = await getActivePortalId();
|
|
17969
|
+
const res = await apiRequest(
|
|
17970
|
+
`/${portalId}/agreements/${encodeURIComponent(ref)}/download`
|
|
17971
|
+
);
|
|
17972
|
+
if (isJsonMode()) {
|
|
17973
|
+
printJson({ data: res.data });
|
|
17974
|
+
return;
|
|
17975
|
+
}
|
|
17976
|
+
const d = res.data;
|
|
17977
|
+
if (!d?.downloadUrl || !d.filename || !d.expiresAt) {
|
|
17978
|
+
exitWithError("Download URL generation failed.", "ERROR", void 0, EXIT.SERVER_ERROR);
|
|
17979
|
+
}
|
|
17980
|
+
printLine("\n Agreement download URL generated.\n");
|
|
17981
|
+
printKeyValue([
|
|
17982
|
+
["Download URL", d.downloadUrl],
|
|
17983
|
+
["Filename", d.filename],
|
|
17984
|
+
["Expires at", d.expiresAt]
|
|
17985
|
+
]);
|
|
17986
|
+
printLine();
|
|
17987
|
+
}
|
|
17988
|
+
|
|
17718
17989
|
// src/auth/refresh-portals.ts
|
|
17719
17990
|
var PORTALS_TTL_MS = 3e5;
|
|
17720
17991
|
var REQUEST_TIMEOUT_MS2 = 1e4;
|
|
@@ -17786,63 +18057,6 @@ async function refreshCachedPortalNames() {
|
|
|
17786
18057
|
}
|
|
17787
18058
|
}
|
|
17788
18059
|
|
|
17789
|
-
// src/validate-flags.ts
|
|
17790
|
-
function restorePositional(args, value, originalIndex) {
|
|
17791
|
-
const insertAt = args.positionalIndexes.findIndex((index) => index > originalIndex);
|
|
17792
|
-
if (insertAt === -1) {
|
|
17793
|
-
args.positional.push(value);
|
|
17794
|
-
args.positionalIndexes.push(originalIndex);
|
|
17795
|
-
return;
|
|
17796
|
-
}
|
|
17797
|
-
args.positional.splice(insertAt, 0, value);
|
|
17798
|
-
args.positionalIndexes.splice(insertAt, 0, originalIndex);
|
|
17799
|
-
}
|
|
17800
|
-
function validateFlags(args, allowed) {
|
|
17801
|
-
const valueFlags = new Set(allowed.flags ?? []);
|
|
17802
|
-
const booleanFlags = new Set(allowed.booleans ?? []);
|
|
17803
|
-
const allAllowed = /* @__PURE__ */ new Set([...valueFlags, ...booleanFlags]);
|
|
17804
|
-
const allList = [...allAllowed].map((f) => `--${f}`).join(", ");
|
|
17805
|
-
for (const key of Object.keys(args.flags)) {
|
|
17806
|
-
if (!allAllowed.has(key)) {
|
|
17807
|
-
exitWithError(
|
|
17808
|
-
`Unknown flag: --${key}. Allowed flags: ${allList || "(none)"}`,
|
|
17809
|
-
"UNKNOWN_FLAG",
|
|
17810
|
-
void 0,
|
|
17811
|
-
EXIT.USAGE
|
|
17812
|
-
);
|
|
17813
|
-
}
|
|
17814
|
-
if (booleanFlags.has(key) && !valueFlags.has(key) && args.flagValueSources[key] === "next") {
|
|
17815
|
-
args.boolean.add(key);
|
|
17816
|
-
restorePositional(args, args.flags[key], args.flagValueIndexes[key]);
|
|
17817
|
-
delete args.flags[key];
|
|
17818
|
-
delete args.flagValueSources[key];
|
|
17819
|
-
delete args.flagValueIndexes[key];
|
|
17820
|
-
continue;
|
|
17821
|
-
}
|
|
17822
|
-
if (booleanFlags.has(key) && !valueFlags.has(key)) {
|
|
17823
|
-
exitWithError(
|
|
17824
|
-
`Flag --${key} does not take a value.`,
|
|
17825
|
-
"UNEXPECTED_FLAG_VALUE",
|
|
17826
|
-
void 0,
|
|
17827
|
-
EXIT.USAGE
|
|
17828
|
-
);
|
|
17829
|
-
}
|
|
17830
|
-
}
|
|
17831
|
-
for (const key of args.boolean) {
|
|
17832
|
-
if (valueFlags.has(key)) {
|
|
17833
|
-
exitWithError(`Flag --${key} requires a value.`, "MISSING_FLAG_VALUE", void 0, EXIT.USAGE);
|
|
17834
|
-
}
|
|
17835
|
-
if (!booleanFlags.has(key)) {
|
|
17836
|
-
exitWithError(
|
|
17837
|
-
`Unknown flag: --${key}. Allowed flags: ${allList || "(none)"}`,
|
|
17838
|
-
"UNKNOWN_FLAG",
|
|
17839
|
-
void 0,
|
|
17840
|
-
EXIT.USAGE
|
|
17841
|
-
);
|
|
17842
|
-
}
|
|
17843
|
-
}
|
|
17844
|
-
}
|
|
17845
|
-
|
|
17846
18060
|
// src/commands/auth-status.ts
|
|
17847
18061
|
async function runAuthStatus(args) {
|
|
17848
18062
|
validateFlags(args, { flags: [], booleans: [] });
|
|
@@ -17890,62 +18104,6 @@ async function runAuthStatus(args) {
|
|
|
17890
18104
|
|
|
17891
18105
|
// src/commands/export.ts
|
|
17892
18106
|
import { renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
17893
|
-
|
|
17894
|
-
// src/flags.ts
|
|
17895
|
-
function looksLikeFlag(value) {
|
|
17896
|
-
if (!value.startsWith("-")) return false;
|
|
17897
|
-
if (value === "-" || /^-\d+(?:\.\d+)?$/.test(value)) return false;
|
|
17898
|
-
return true;
|
|
17899
|
-
}
|
|
17900
|
-
function parseArgs(argv) {
|
|
17901
|
-
const positional = [];
|
|
17902
|
-
const positionalIndexes = [];
|
|
17903
|
-
const flags = {};
|
|
17904
|
-
const flagValueSources = {};
|
|
17905
|
-
const flagValueIndexes = {};
|
|
17906
|
-
const boolean4 = /* @__PURE__ */ new Set();
|
|
17907
|
-
let i = 0;
|
|
17908
|
-
while (i < argv.length) {
|
|
17909
|
-
const arg = argv[i];
|
|
17910
|
-
if (arg.startsWith("--")) {
|
|
17911
|
-
const eqIdx = arg.indexOf("=");
|
|
17912
|
-
if (eqIdx !== -1) {
|
|
17913
|
-
const key = arg.slice(2, eqIdx);
|
|
17914
|
-
flags[key] = arg.slice(eqIdx + 1);
|
|
17915
|
-
flagValueSources[key] = "equals";
|
|
17916
|
-
flagValueIndexes[key] = i;
|
|
17917
|
-
} else {
|
|
17918
|
-
const key = arg.slice(2);
|
|
17919
|
-
const next = argv[i + 1];
|
|
17920
|
-
if (next !== void 0 && !looksLikeFlag(next)) {
|
|
17921
|
-
flags[key] = next;
|
|
17922
|
-
flagValueSources[key] = "next";
|
|
17923
|
-
flagValueIndexes[key] = i + 1;
|
|
17924
|
-
i++;
|
|
17925
|
-
} else {
|
|
17926
|
-
boolean4.add(key);
|
|
17927
|
-
}
|
|
17928
|
-
}
|
|
17929
|
-
} else {
|
|
17930
|
-
positional.push(arg);
|
|
17931
|
-
positionalIndexes.push(i);
|
|
17932
|
-
}
|
|
17933
|
-
i++;
|
|
17934
|
-
}
|
|
17935
|
-
return { positional, positionalIndexes, flags, flagValueSources, flagValueIndexes, boolean: boolean4 };
|
|
17936
|
-
}
|
|
17937
|
-
function hasFlag(args, name) {
|
|
17938
|
-
return args.boolean.has(name) || name in args.flags;
|
|
17939
|
-
}
|
|
17940
|
-
function getFlagValue(args, name) {
|
|
17941
|
-
return args.flags[name];
|
|
17942
|
-
}
|
|
17943
|
-
function argvRequestsHelp(argv) {
|
|
17944
|
-
if (argv.includes("--help") || argv.includes("-h")) return true;
|
|
17945
|
-
return argv[0] === "help";
|
|
17946
|
-
}
|
|
17947
|
-
|
|
17948
|
-
// src/commands/export.ts
|
|
17949
18107
|
var EXPORT_USAGE = "captable-ai export [--as-of YYYY-MM-DD] [--show-empty-fields] [--json]";
|
|
17950
18108
|
var EXPORT_REGISTER_USAGE = "captable-ai export <stock|grants|warrants|convertibles> [--include-pending] [--as-of YYYY-MM-DD] [--csv | --json] [--output <path>]";
|
|
17951
18109
|
var EXPORT_CONVERTIBLES_USAGE = "captable-ai export convertibles [--type SAFE|NOTE] [--include-pending] [--as-of YYYY-MM-DD] [--csv | --json] [--output <path>]";
|
|
@@ -18172,7 +18330,7 @@ var REGISTER_DESCRIPTIONS = {
|
|
|
18172
18330
|
var REGISTER_FLAGS = [
|
|
18173
18331
|
{
|
|
18174
18332
|
name: "include-pending",
|
|
18175
|
-
description: "Add draft OCF objects and investment / equity_grant rows with no OCF transaction.",
|
|
18333
|
+
description: "Add draft OCF objects and investment / equity_grant / equity_award rows with no OCF transaction.",
|
|
18176
18334
|
type: "boolean"
|
|
18177
18335
|
},
|
|
18178
18336
|
{
|
|
@@ -18784,6 +18942,30 @@ var COMMAND_REGISTRY = [
|
|
|
18784
18942
|
requiresAuth: true,
|
|
18785
18943
|
requiresPortal: true
|
|
18786
18944
|
},
|
|
18945
|
+
{
|
|
18946
|
+
name: "agreements list",
|
|
18947
|
+
group: "captable",
|
|
18948
|
+
description: "List signed agreements: investment agreements (SAFE, custom document, legacy) once signed, paid or not, and Send One Grants both parties signed. Each row has a ref for `agreements download`.",
|
|
18949
|
+
usage: "captable-ai agreements list [--type investment|grant] [--json]",
|
|
18950
|
+
requiresAuth: true,
|
|
18951
|
+
requiresPortal: true,
|
|
18952
|
+
flags: [
|
|
18953
|
+
{
|
|
18954
|
+
name: "type",
|
|
18955
|
+
description: "Only investment or grant agreements.",
|
|
18956
|
+
type: "string",
|
|
18957
|
+
enum: ["investment", "grant"]
|
|
18958
|
+
}
|
|
18959
|
+
]
|
|
18960
|
+
},
|
|
18961
|
+
{
|
|
18962
|
+
name: "agreements download",
|
|
18963
|
+
group: "captable",
|
|
18964
|
+
description: "Get a one-hour download URL for a signed agreement by its ref (investment:<s3AssetId> or grant:<awardId>). A grant downloads as the agreement plus signature certificates, without tax forms or 83(b) elections.",
|
|
18965
|
+
usage: "captable-ai agreements download <ref> [--json]",
|
|
18966
|
+
requiresAuth: true,
|
|
18967
|
+
requiresPortal: true
|
|
18968
|
+
},
|
|
18787
18969
|
{
|
|
18788
18970
|
name: "documents trigger-extraction",
|
|
18789
18971
|
group: "captable",
|
|
@@ -19561,18 +19743,21 @@ Workbook options:
|
|
|
19561
19743
|
Registers (one row per security; holder name, email, address, country, state,
|
|
19562
19744
|
plus the economics of that instrument):
|
|
19563
19745
|
stock Stock issuances: class, quantity, issue date, price, amount
|
|
19564
|
-
grants Options, RSUs, other awards: award type
|
|
19565
|
-
|
|
19566
|
-
|
|
19746
|
+
grants Options, RSUs, other awards: grant date, award type
|
|
19747
|
+
(ISO/NSO/RSU), quantity, status, vesting schedule, vesting
|
|
19748
|
+
length, cliff, exercise price, signed date, vesting start
|
|
19749
|
+
date, dollar amount, series, plan
|
|
19567
19750
|
warrants Warrants: quantity, exercise price, purchase price, expiration,
|
|
19568
19751
|
underlying class, issue date
|
|
19569
|
-
convertibles SAFEs, notes:
|
|
19570
|
-
valuation cap,
|
|
19752
|
+
convertibles SAFEs, notes: signature date, execution date, status, round,
|
|
19753
|
+
amount, funding method, type, investment date, valuation cap,
|
|
19754
|
+
discount
|
|
19571
19755
|
|
|
19572
19756
|
Register options:
|
|
19573
19757
|
--include-pending Add what is not committed yet: draft OCF objects, and
|
|
19574
|
-
investment / equity_grant rows with no
|
|
19575
|
-
A row that already has an OCF object
|
|
19758
|
+
investment / equity_grant / equity_award rows with no
|
|
19759
|
+
OCF transaction. A row that already has an OCF object
|
|
19760
|
+
is listed once.
|
|
19576
19761
|
--as-of YYYY-MM-DD Only securities issued on or before this date
|
|
19577
19762
|
--type SAFE|NOTE convertibles only: keep SAFEs or notes
|
|
19578
19763
|
--csv Print CSV to stdout: RFC-4180 quoting, UTF-8, LF line
|
|
@@ -24255,6 +24440,7 @@ var VERSION = (() => {
|
|
|
24255
24440
|
return "0.0.0";
|
|
24256
24441
|
})();
|
|
24257
24442
|
var PROFILE_AWARE_COMMANDS = /* @__PURE__ */ new Set([
|
|
24443
|
+
"agreements",
|
|
24258
24444
|
"commit",
|
|
24259
24445
|
"company",
|
|
24260
24446
|
"documents",
|
|
@@ -24346,6 +24532,12 @@ Documents:
|
|
|
24346
24532
|
documents delete <id> Delete an uploaded document and all associated artifacts
|
|
24347
24533
|
documents download <id> Get a presigned download URL for a document
|
|
24348
24534
|
|
|
24535
|
+
Signed Agreements:
|
|
24536
|
+
agreements list List signed investment and grant agreements
|
|
24537
|
+
Filter: --type investment|grant
|
|
24538
|
+
agreements download <ref> Get a one-hour download URL for one agreement
|
|
24539
|
+
Grants: agreement + signature certificates, no tax forms
|
|
24540
|
+
|
|
24349
24541
|
Agent Logs (Fairmint super-admin only):
|
|
24350
24542
|
logs list List OCF agent log entries (expert/validator runs)
|
|
24351
24543
|
Options: --agent-type expert|validator, --limit N, --cursor C
|
|
@@ -24824,6 +25016,32 @@ async function main() {
|
|
|
24824
25016
|
);
|
|
24825
25017
|
}
|
|
24826
25018
|
break;
|
|
25019
|
+
case "agreements":
|
|
25020
|
+
if (isHelpToken(sub)) {
|
|
25021
|
+
process.stdout.write(
|
|
25022
|
+
"agreements \u2014 List and download signed investment and grant agreements\n\nSubcommands: list, download\nRun: captable-ai agreements <subcommand> --help\n"
|
|
25023
|
+
);
|
|
25024
|
+
process.exit(0);
|
|
25025
|
+
}
|
|
25026
|
+
switch (sub) {
|
|
25027
|
+
case "list":
|
|
25028
|
+
await runAgreementsList(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
|
|
25029
|
+
break;
|
|
25030
|
+
case "download":
|
|
25031
|
+
await runAgreementsDownload(
|
|
25032
|
+
parseArgs(positionalArgs.slice(2)),
|
|
25033
|
+
positionalArgs.slice(2)
|
|
25034
|
+
);
|
|
25035
|
+
break;
|
|
25036
|
+
default:
|
|
25037
|
+
exitWithError(
|
|
25038
|
+
`Unknown agreements command: ${sub ?? "(none)"}. Try: agreements list | download`,
|
|
25039
|
+
"UNKNOWN_COMMAND",
|
|
25040
|
+
void 0,
|
|
25041
|
+
EXIT.USAGE
|
|
25042
|
+
);
|
|
25043
|
+
}
|
|
25044
|
+
break;
|
|
24827
25045
|
case "logs":
|
|
24828
25046
|
if (isHelpToken(sub)) {
|
|
24829
25047
|
process.stdout.write(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fairmint/captable-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
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",
|
|
@@ -27,12 +27,10 @@
|
|
|
27
27
|
},
|
|
28
28
|
"exports": {
|
|
29
29
|
".": {
|
|
30
|
-
"types": "./dist/index.d.ts",
|
|
31
30
|
"import": "./dist/index.js",
|
|
32
31
|
"default": "./dist/index.js"
|
|
33
32
|
}
|
|
34
33
|
},
|
|
35
|
-
"types": "./dist/index.d.ts",
|
|
36
34
|
"files": [
|
|
37
35
|
"dist"
|
|
38
36
|
],
|
|
@@ -53,7 +51,6 @@
|
|
|
53
51
|
"devDependencies": {
|
|
54
52
|
"@captable-ai/core": "workspace:*",
|
|
55
53
|
"@types/node": "25.5.2",
|
|
56
|
-
"tsup": "8.5.1"
|
|
57
|
-
"typescript": "6.0.2"
|
|
54
|
+
"tsup": "8.5.1"
|
|
58
55
|
}
|
|
59
56
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|