@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 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 starts with the holder fields (name, email, address, country, state),
129
- then the economics of that family. Without a format flag the result is one Excel sheet
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` and `equity_grant` rows that
137
- have no OCF transaction. A row that already has an OCF object appears once. Registers
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 = ["committed", "draft", "investment", "equity_grant"];
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 equity_grant."),
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
- ...HOLDER_COLUMNS,
16242
- { key: "award_type", header: "Award Type", format: "text" },
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: "accepted_on", header: "Accepted On", format: "date" },
16247
- { key: "cliff_months", header: "Cliff (Months)", format: "number" },
16248
- { key: "vesting_months", header: "Vesting Length (Months)", format: "number" },
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: "convertible_type", header: "Type", format: "text" },
16268
- { key: "date", header: "Date", format: "date" },
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 (ISO/NSO/RSU), quantity,
19565
- dollar amount, status, accepted on, cliff, vesting length,
19566
- effective date, exercise price, series, plan
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: type, date, round, amount, funding method,
19570
- valuation cap, discount
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 OCF transaction.
19575
- A row that already has an OCF object is listed once.
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.39",
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