@fairmint/captable-ai 0.1.14 → 0.1.16
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/dist/index.js +205 -79
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -170,8 +170,11 @@ function parsePortals(body) {
|
|
|
170
170
|
if (!portals.every(isStoredPortal)) return null;
|
|
171
171
|
return portals;
|
|
172
172
|
}
|
|
173
|
+
function cachedPortalsNeedNameRefresh(portals) {
|
|
174
|
+
return portals.some((portal) => typeof portal.name !== "string" || portal.name.trim() === "");
|
|
175
|
+
}
|
|
173
176
|
async function refreshPortalsIfStale(creds, force = false) {
|
|
174
|
-
if (!force && creds.portalsLastFetchedAt && Date.now() - Date.parse(creds.portalsLastFetchedAt) < PORTALS_TTL_MS) {
|
|
177
|
+
if (!force && !cachedPortalsNeedNameRefresh(creds.portals) && creds.portalsLastFetchedAt && Date.now() - Date.parse(creds.portalsLastFetchedAt) < PORTALS_TTL_MS) {
|
|
175
178
|
return creds;
|
|
176
179
|
}
|
|
177
180
|
if (isTokenExpired(creds.accessToken)) {
|
|
@@ -205,6 +208,22 @@ async function markPortalsFetched(creds) {
|
|
|
205
208
|
creds.portalsLastFetchedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
206
209
|
await writeCredentials(creds);
|
|
207
210
|
}
|
|
211
|
+
async function refreshCachedPortalNames() {
|
|
212
|
+
try {
|
|
213
|
+
const creds = await readCredentials();
|
|
214
|
+
if (!creds) return;
|
|
215
|
+
const updated = await refreshPortalsIfStale(creds, true);
|
|
216
|
+
const config2 = await readConfig();
|
|
217
|
+
const activeId = config2.activePortal?.id;
|
|
218
|
+
if (!activeId) return;
|
|
219
|
+
const portal = updated.portals.find((candidate) => candidate.id === activeId);
|
|
220
|
+
if (portal) await setActivePortal(portal);
|
|
221
|
+
} catch {
|
|
222
|
+
process.stderr.write(
|
|
223
|
+
"Warning: Could not refresh company name after updating the company list.\n"
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
208
227
|
|
|
209
228
|
// src/output.ts
|
|
210
229
|
import { renameSync, rmSync, writeFileSync } from "fs";
|
|
@@ -526,10 +545,73 @@ var COMMAND_REGISTRY = [
|
|
|
526
545
|
{
|
|
527
546
|
name: "onboarding setup",
|
|
528
547
|
group: "onboarding",
|
|
529
|
-
description: "Idempotently create the authenticated user's first production workspace.",
|
|
530
|
-
usage: "captable-ai onboarding setup --legal-name <name> --company-type <type> --
|
|
548
|
+
description: "Idempotently create the authenticated user's first production workspace. Only --accept-terms is required; the company profile is read from uploaded documents.",
|
|
549
|
+
usage: "captable-ai onboarding setup --accept-terms [--legal-name <name>] [--company-type <type>] [--country <code>] [--role <role>] [--other-role <role>] [--cap-table-source <source>] [--other-cap-table-source <source>] [--stakeholder-count <bucket>] [--json]",
|
|
531
550
|
requiresAuth: true,
|
|
532
|
-
requiresPortal: false
|
|
551
|
+
requiresPortal: false,
|
|
552
|
+
flags: [
|
|
553
|
+
{
|
|
554
|
+
name: "accept-terms",
|
|
555
|
+
description: "Confirm acceptance of Fairmint's Terms of Service (required).",
|
|
556
|
+
type: "boolean"
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
name: "legal-name",
|
|
560
|
+
description: "Company name. Left unset until you or document extraction supplies one.",
|
|
561
|
+
type: "string"
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
name: "company-type",
|
|
565
|
+
description: "Legal entity type. Left unrecorded when omitted.",
|
|
566
|
+
type: "string",
|
|
567
|
+
enum: ["us_c_corporation", "us_llc", "foreign_entity", "not_incorporated"]
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
name: "country",
|
|
571
|
+
description: "ISO 3166-1 alpha-2 code. Only with --company-type foreign_entity.",
|
|
572
|
+
type: "string"
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: "role",
|
|
576
|
+
description: "Your role at the company.",
|
|
577
|
+
type: "string",
|
|
578
|
+
enum: [
|
|
579
|
+
"CEO",
|
|
580
|
+
"COO",
|
|
581
|
+
"CMO",
|
|
582
|
+
"CFO",
|
|
583
|
+
"CTO",
|
|
584
|
+
"CRO",
|
|
585
|
+
"Director",
|
|
586
|
+
"Founder",
|
|
587
|
+
"co-founder",
|
|
588
|
+
"co-CEO",
|
|
589
|
+
"other"
|
|
590
|
+
]
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
name: "other-role",
|
|
594
|
+
description: "Free-text role. Required when --role other.",
|
|
595
|
+
type: "string"
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
name: "cap-table-source",
|
|
599
|
+
description: "Where the cap table is maintained today.",
|
|
600
|
+
type: "string",
|
|
601
|
+
enum: ["none", "spreadsheet", "carta", "pulley", "angellist", "other"]
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
name: "other-cap-table-source",
|
|
605
|
+
description: "Free-text source. Required when --cap-table-source other.",
|
|
606
|
+
type: "string"
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: "stakeholder-count",
|
|
610
|
+
description: "Approximate number of stakeholders on the cap table.",
|
|
611
|
+
type: "string",
|
|
612
|
+
enum: ["1_10", "11_25", "26_50", "50_100", "100_plus"]
|
|
613
|
+
}
|
|
614
|
+
]
|
|
533
615
|
},
|
|
534
616
|
{
|
|
535
617
|
name: "import start",
|
|
@@ -16637,60 +16719,109 @@ var hasControlCharacter = (value) => Array.from(value).some((character) => {
|
|
|
16637
16719
|
});
|
|
16638
16720
|
var legalNameSchema = shortTextSchema(255).refine((value) => !hasControlCharacter(value), "Legal name cannot contain control characters").describe("Confirmed legal name of the company.");
|
|
16639
16721
|
var countryCodeSchema = external_exports.string().trim().transform((value) => value.toUpperCase()).pipe(external_exports.string().regex(/^[A-Z]{2}$/, "Must be an ISO 3166-1 alpha-2 country code")).describe("ISO 3166-1 alpha-2 country of incorporation. Required for foreign entities.");
|
|
16640
|
-
var
|
|
16641
|
-
external_exports.
|
|
16642
|
-
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16648
|
-
|
|
16649
|
-
|
|
16650
|
-
|
|
16651
|
-
|
|
16652
|
-
|
|
16653
|
-
|
|
16654
|
-
|
|
16655
|
-
|
|
16656
|
-
|
|
16657
|
-
|
|
16658
|
-
|
|
16659
|
-
|
|
16660
|
-
|
|
16661
|
-
external_exports.
|
|
16662
|
-
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16666
|
-
|
|
16667
|
-
|
|
16668
|
-
|
|
16669
|
-
|
|
16670
|
-
}
|
|
16671
|
-
|
|
16672
|
-
|
|
16673
|
-
|
|
16674
|
-
|
|
16675
|
-
|
|
16676
|
-
|
|
16677
|
-
|
|
16678
|
-
|
|
16679
|
-
|
|
16680
|
-
|
|
16681
|
-
|
|
16682
|
-
|
|
16683
|
-
|
|
16684
|
-
|
|
16685
|
-
|
|
16686
|
-
|
|
16687
|
-
|
|
16722
|
+
var onboardingWorkspaceSetupInputSchema = external_exports.object({
|
|
16723
|
+
termsAccepted: external_exports.literal(true).describe("Whether the user accepted Fairmint's Terms of Service; must be true."),
|
|
16724
|
+
legalName: legalNameSchema.optional(),
|
|
16725
|
+
companyType: external_exports.enum([
|
|
16726
|
+
ONBOARDING_COMPANY_TYPES.US_C_CORPORATION,
|
|
16727
|
+
ONBOARDING_COMPANY_TYPES.US_LLC,
|
|
16728
|
+
ONBOARDING_COMPANY_TYPES.FOREIGN_ENTITY,
|
|
16729
|
+
ONBOARDING_COMPANY_TYPES.NOT_INCORPORATED
|
|
16730
|
+
]).optional().describe("Confirmed company type. Left unrecorded when omitted."),
|
|
16731
|
+
country: countryCodeSchema.optional(),
|
|
16732
|
+
role: external_exports.enum([...ONBOARDING_ROLES, ONBOARDING_OTHER_ROLE]).optional().describe("Founder's role at the company."),
|
|
16733
|
+
otherRole: shortTextSchema(100).optional().describe("Free-text role when role is other."),
|
|
16734
|
+
capTableSource: external_exports.enum([
|
|
16735
|
+
ONBOARDING_CAP_TABLE_SOURCES.NONE,
|
|
16736
|
+
ONBOARDING_CAP_TABLE_SOURCES.SPREADSHEET,
|
|
16737
|
+
ONBOARDING_CAP_TABLE_SOURCES.CARTA,
|
|
16738
|
+
ONBOARDING_CAP_TABLE_SOURCES.PULLEY,
|
|
16739
|
+
ONBOARDING_CAP_TABLE_SOURCES.ANGELLIST,
|
|
16740
|
+
ONBOARDING_CAP_TABLE_SOURCES.OTHER
|
|
16741
|
+
]).optional().describe("Where the current cap table is maintained."),
|
|
16742
|
+
otherCapTableSource: shortTextSchema(100).optional().describe("Free-text cap table tool when capTableSource is other."),
|
|
16743
|
+
stakeholderCount: external_exports.enum(ONBOARDING_STAKEHOLDER_COUNT_BUCKETS).optional().describe("Approximate number of stakeholders on the cap table.")
|
|
16744
|
+
}).superRefine((value, ctx) => {
|
|
16745
|
+
if (value.companyType === ONBOARDING_COMPANY_TYPES.FOREIGN_ENTITY) {
|
|
16746
|
+
if (value.country === void 0) {
|
|
16747
|
+
ctx.addIssue({
|
|
16748
|
+
code: "custom",
|
|
16749
|
+
path: ["country"],
|
|
16750
|
+
message: "Foreign entities must provide a country of incorporation"
|
|
16751
|
+
});
|
|
16752
|
+
} else if (value.country === "US") {
|
|
16753
|
+
ctx.addIssue({
|
|
16754
|
+
code: "custom",
|
|
16755
|
+
path: ["country"],
|
|
16756
|
+
message: "Foreign entities must use a non-US country"
|
|
16757
|
+
});
|
|
16758
|
+
}
|
|
16759
|
+
} else if (value.country !== void 0) {
|
|
16760
|
+
ctx.addIssue({
|
|
16761
|
+
code: "custom",
|
|
16762
|
+
path: ["country"],
|
|
16763
|
+
message: "Omit country unless companyType is foreign_entity"
|
|
16764
|
+
});
|
|
16765
|
+
}
|
|
16766
|
+
if (value.role === ONBOARDING_OTHER_ROLE) {
|
|
16767
|
+
if (value.otherRole === void 0) {
|
|
16768
|
+
ctx.addIssue({
|
|
16769
|
+
code: "custom",
|
|
16770
|
+
path: ["otherRole"],
|
|
16771
|
+
message: "Describe the role when role is other"
|
|
16772
|
+
});
|
|
16773
|
+
}
|
|
16774
|
+
} else if (value.otherRole !== void 0) {
|
|
16775
|
+
ctx.addIssue({
|
|
16776
|
+
code: "custom",
|
|
16777
|
+
path: ["otherRole"],
|
|
16778
|
+
message: "Omit otherRole unless role is other"
|
|
16779
|
+
});
|
|
16780
|
+
}
|
|
16781
|
+
if (value.capTableSource === ONBOARDING_CAP_TABLE_SOURCES.OTHER) {
|
|
16782
|
+
if (value.otherCapTableSource === void 0) {
|
|
16783
|
+
ctx.addIssue({
|
|
16784
|
+
code: "custom",
|
|
16785
|
+
path: ["otherCapTableSource"],
|
|
16786
|
+
message: "Describe the cap table source when capTableSource is other"
|
|
16787
|
+
});
|
|
16788
|
+
}
|
|
16789
|
+
} else if (value.otherCapTableSource !== void 0) {
|
|
16790
|
+
ctx.addIssue({
|
|
16791
|
+
code: "custom",
|
|
16792
|
+
path: ["otherCapTableSource"],
|
|
16793
|
+
message: "Omit otherCapTableSource unless capTableSource is other"
|
|
16794
|
+
});
|
|
16795
|
+
}
|
|
16796
|
+
});
|
|
16797
|
+
function dropOrphanOnboardingSetupDetails(input) {
|
|
16798
|
+
const next = { ...input };
|
|
16799
|
+
if (next.companyType !== void 0 && next.companyType !== ONBOARDING_COMPANY_TYPES.FOREIGN_ENTITY) {
|
|
16800
|
+
delete next.country;
|
|
16801
|
+
}
|
|
16802
|
+
if (next.role !== void 0 && next.role !== ONBOARDING_OTHER_ROLE) {
|
|
16803
|
+
delete next.otherRole;
|
|
16804
|
+
}
|
|
16805
|
+
if (next.capTableSource !== void 0 && next.capTableSource !== ONBOARDING_CAP_TABLE_SOURCES.OTHER) {
|
|
16806
|
+
delete next.otherCapTableSource;
|
|
16807
|
+
}
|
|
16808
|
+
return next;
|
|
16809
|
+
}
|
|
16810
|
+
function resolveOnboardingCompanyProfile(input) {
|
|
16811
|
+
const companyType = input.companyType ?? null;
|
|
16812
|
+
const country = companyType === null ? null : companyType === ONBOARDING_COMPANY_TYPES.FOREIGN_ENTITY ? input.country ?? null : ONBOARDING_COMPANY_TYPE_TO_ISSUER[companyType].countryOfFormation;
|
|
16813
|
+
return {
|
|
16814
|
+
legalName: input.legalName ?? null,
|
|
16815
|
+
companyType,
|
|
16816
|
+
country
|
|
16817
|
+
};
|
|
16818
|
+
}
|
|
16688
16819
|
var onboardingWorkspaceSetupResultSchema = external_exports.object({
|
|
16689
16820
|
portalId: external_exports.uuid().describe("ID of the user's first production workspace."),
|
|
16690
16821
|
state: external_exports.literal(ONBOARDING_WORKSPACE_STATES.WORKSPACE_READY).describe("Workspace is created or reused and ready for import."),
|
|
16691
16822
|
created: external_exports.boolean().describe("True when this request inserted a new portal."),
|
|
16692
|
-
commitReady: external_exports.boolean().describe("True when the draft issuer has formation date and country of formation."),
|
|
16693
|
-
commitBlockers: external_exports.array(external_exports.enum(["formation_date", "country_of_formation"])).describe("Issuer fields that still block a live commit."),
|
|
16823
|
+
commitReady: external_exports.boolean().describe("True when the draft issuer has legal name, formation date, and country of formation."),
|
|
16824
|
+
commitBlockers: external_exports.array(external_exports.enum(["legal_name", "formation_date", "country_of_formation"])).describe("Issuer fields that still block a live commit."),
|
|
16694
16825
|
session: external_exports.object({
|
|
16695
16826
|
action: external_exports.enum([
|
|
16696
16827
|
ONBOARDING_SESSION_ACTIONS.NONE,
|
|
@@ -17244,20 +17375,24 @@ var HELP_ONBOARDING = `onboarding \u2014 Create a first workspace and inspect ne
|
|
|
17244
17375
|
|
|
17245
17376
|
Usage:
|
|
17246
17377
|
captable-ai onboarding status [--session <import-session-id>] [--json]
|
|
17247
|
-
captable-ai onboarding setup --legal-name <name> --
|
|
17248
|
-
|
|
17249
|
-
|
|
17378
|
+
captable-ai onboarding setup --accept-terms [--legal-name <name>] [options] [--json]
|
|
17379
|
+
|
|
17380
|
+
Accepting the Terms is all that is required. The legal name, entity type, and
|
|
17381
|
+
country are read from the documents you upload, so you can skip them here and
|
|
17382
|
+
start importing right away.
|
|
17250
17383
|
|
|
17251
17384
|
Setup values:
|
|
17385
|
+
--accept-terms Confirm acceptance of Fairmint's Terms of Service (required)
|
|
17386
|
+
--legal-name Company name. Left unset until you or document
|
|
17387
|
+
extraction supplies one; commit stays blocked until then.
|
|
17252
17388
|
--company-type us_c_corporation | us_llc | foreign_entity | not_incorporated
|
|
17253
|
-
--country ISO country code (
|
|
17389
|
+
--country ISO country code (only with --company-type foreign_entity)
|
|
17254
17390
|
--role CEO | COO | CMO | CFO | CTO | CRO | Director | Founder |
|
|
17255
17391
|
co-founder | co-CEO | other
|
|
17256
17392
|
--other-role Required when --role other
|
|
17257
17393
|
--cap-table-source none | spreadsheet | carta | pulley | angellist | other
|
|
17258
17394
|
--other-cap-table-source Required when --cap-table-source other
|
|
17259
|
-
--stakeholder-count 1_10 | 11_25 | 26_50 | 50_100 | 100_plus
|
|
17260
|
-
--accept-terms Confirm acceptance of Fairmint's Terms of Service`;
|
|
17395
|
+
--stakeholder-count 1_10 | 11_25 | 26_50 | 50_100 | 100_plus`;
|
|
17261
17396
|
var HELP_IMPORT = `import \u2014 Start and inspect a secure document-import session
|
|
17262
17397
|
|
|
17263
17398
|
Usage:
|
|
@@ -17855,6 +17990,7 @@ Committing ${countCopy}...
|
|
|
17855
17990
|
exitWithError("Unexpected empty response from server.");
|
|
17856
17991
|
}
|
|
17857
17992
|
const data = res.data;
|
|
17993
|
+
await refreshCachedPortalNames();
|
|
17858
17994
|
if (isJsonMode()) {
|
|
17859
17995
|
printJson({ data });
|
|
17860
17996
|
return;
|
|
@@ -20476,6 +20612,7 @@ Reverting ${selection.objectIds.length} selected draft object(s)...
|
|
|
20476
20612
|
if (!res.data) {
|
|
20477
20613
|
exitWithError("Unexpected empty response from server.");
|
|
20478
20614
|
}
|
|
20615
|
+
await refreshCachedPortalNames();
|
|
20479
20616
|
if (isJsonMode()) {
|
|
20480
20617
|
printJson({ data: res.data });
|
|
20481
20618
|
return;
|
|
@@ -20865,10 +21002,10 @@ async function runOnboardingStatus(args) {
|
|
|
20865
21002
|
nextAction(
|
|
20866
21003
|
"setup_workspace",
|
|
20867
21004
|
"Create your first workspace",
|
|
20868
|
-
"captable-ai onboarding setup"
|
|
21005
|
+
"captable-ai onboarding setup --accept-terms"
|
|
20869
21006
|
)
|
|
20870
21007
|
],
|
|
20871
|
-
question: "
|
|
21008
|
+
question: "Do you accept Fairmint's Terms of Service? That is all we need to open your workspace."
|
|
20872
21009
|
} : !active ? {
|
|
20873
21010
|
state: ONBOARDING_STATES.NEEDS_COMPANY_SELECTION,
|
|
20874
21011
|
companies: credentials.portals,
|
|
@@ -20929,19 +21066,6 @@ function setupInput(args) {
|
|
|
20929
21066
|
}
|
|
20930
21067
|
return input;
|
|
20931
21068
|
}
|
|
20932
|
-
function omitInapplicableOnboardingSetupFields(input) {
|
|
20933
|
-
const next = { ...input };
|
|
20934
|
-
if (next.companyType !== ONBOARDING_COMPANY_TYPES.FOREIGN_ENTITY) {
|
|
20935
|
-
delete next.country;
|
|
20936
|
-
}
|
|
20937
|
-
if (next.role !== ONBOARDING_OTHER_ROLE) {
|
|
20938
|
-
delete next.otherRole;
|
|
20939
|
-
}
|
|
20940
|
-
if (next.capTableSource !== ONBOARDING_CAP_TABLE_SOURCES.OTHER) {
|
|
20941
|
-
delete next.otherCapTableSource;
|
|
20942
|
-
}
|
|
20943
|
-
return next;
|
|
20944
|
-
}
|
|
20945
21069
|
async function runOnboardingSetup(args) {
|
|
20946
21070
|
validateFlags(args, {
|
|
20947
21071
|
flags: [
|
|
@@ -20957,7 +21081,7 @@ async function runOnboardingSetup(args) {
|
|
|
20957
21081
|
booleans: ["accept-terms"]
|
|
20958
21082
|
});
|
|
20959
21083
|
const parsed = onboardingWorkspaceSetupInputSchema.safeParse(
|
|
20960
|
-
|
|
21084
|
+
dropOrphanOnboardingSetupDetails(setupInput(args))
|
|
20961
21085
|
);
|
|
20962
21086
|
if (!parsed.success) {
|
|
20963
21087
|
const details = parsed.error.issues.map((issue2) => ({
|
|
@@ -20965,13 +21089,15 @@ async function runOnboardingSetup(args) {
|
|
|
20965
21089
|
message: issue2.message
|
|
20966
21090
|
}));
|
|
20967
21091
|
exitWithError(
|
|
20968
|
-
"Company setup answers are
|
|
21092
|
+
"Company setup answers are invalid. Only `--accept-terms` is required on its own, but some answers require a partner flag: `--company-type foreign_entity` needs `--country`, `--role other` needs `--other-role`, and `--cap-table-source other` needs `--other-cap-table-source`. Run `captable-ai onboarding setup --help` for accepted values.",
|
|
20969
21093
|
"VALIDATION_FAILED",
|
|
20970
21094
|
details,
|
|
20971
21095
|
EXIT.VALIDATION
|
|
20972
21096
|
);
|
|
20973
21097
|
}
|
|
20974
21098
|
const input = parsed.data;
|
|
21099
|
+
const { legalName } = resolveOnboardingCompanyProfile(input);
|
|
21100
|
+
const workspaceLabel = legalName ?? "your new workspace";
|
|
20975
21101
|
const response = await apiRequest("/onboarding/workspace", { method: "POST", body: input });
|
|
20976
21102
|
const result = onboardingWorkspaceSetupResultSchema.safeParse(response.data ?? response);
|
|
20977
21103
|
if (!result.success) {
|
|
@@ -21007,7 +21133,7 @@ async function runOnboardingSetup(args) {
|
|
|
21007
21133
|
printJson({ data: data2 });
|
|
21008
21134
|
return;
|
|
21009
21135
|
}
|
|
21010
|
-
printSuccess(`Workspace ready: ${
|
|
21136
|
+
printSuccess(`Workspace ready: ${workspaceLabel} (${result.data.portalId})`);
|
|
21011
21137
|
printLine("Start the import: captable-ai import start");
|
|
21012
21138
|
return;
|
|
21013
21139
|
}
|
|
@@ -21023,7 +21149,7 @@ async function runOnboardingSetup(args) {
|
|
|
21023
21149
|
printJson({ data });
|
|
21024
21150
|
return;
|
|
21025
21151
|
}
|
|
21026
|
-
printSuccess(`Workspace ready: ${
|
|
21152
|
+
printSuccess(`Workspace ready: ${workspaceLabel} (${result.data.portalId})`);
|
|
21027
21153
|
printLine("Reconnect so your session can access it: captable-ai login");
|
|
21028
21154
|
}
|
|
21029
21155
|
async function runImportStart(args) {
|
package/package.json
CHANGED