@fairmint/captable-ai 0.1.35 → 0.1.37
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 +243 -91
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15113,22 +15113,27 @@ function deriveExtractionReadinessPhase(summary) {
|
|
|
15113
15113
|
return "complete";
|
|
15114
15114
|
}
|
|
15115
15115
|
var IMPORT_SESSION_CLOCK_SKEW_MS = 5e3;
|
|
15116
|
-
function
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
const cutoffMs = sessionMs - IMPORT_SESSION_CLOCK_SKEW_MS;
|
|
15116
|
+
function sameUuid(a, b) {
|
|
15117
|
+
return typeof b === "string" && a.toLowerCase() === b.toLowerCase();
|
|
15118
|
+
}
|
|
15119
|
+
function documentsForImportSession(documents, sessionCreatedAt, sessionId) {
|
|
15120
|
+
const sessionMs = typeof sessionCreatedAt === "string" ? parseTimestampMs(sessionCreatedAt) : null;
|
|
15121
|
+
const cutoffMs = sessionMs === null ? null : sessionMs - IMPORT_SESSION_CLOCK_SKEW_MS;
|
|
15123
15122
|
return documents.filter((document) => {
|
|
15124
|
-
if (typeof document.
|
|
15123
|
+
if (typeof document.importSessionId === "string") {
|
|
15124
|
+
return sameUuid(document.importSessionId, sessionId);
|
|
15125
|
+
}
|
|
15126
|
+
if (cutoffMs === null || typeof document.createdAt !== "string")
|
|
15125
15127
|
return false;
|
|
15126
15128
|
const createdMs = parseTimestampMs(document.createdAt);
|
|
15127
15129
|
return createdMs !== null && createdMs >= cutoffMs;
|
|
15128
15130
|
});
|
|
15129
15131
|
}
|
|
15130
15132
|
function importSessionScopeUnknown(documents) {
|
|
15131
|
-
return documents.length > 0 && documents.every((document) =>
|
|
15133
|
+
return documents.length > 0 && documents.every((document) => (
|
|
15134
|
+
// A stamped document is attributable without a creation time.
|
|
15135
|
+
typeof document.importSessionId !== "string" && (typeof document.createdAt !== "string" || parseTimestampMs(document.createdAt) === null)
|
|
15136
|
+
));
|
|
15132
15137
|
}
|
|
15133
15138
|
var HAS_ZONE = /(?:Z|[+-]\d{2}(?::?\d{2})?)$/i;
|
|
15134
15139
|
function parseTimestampMs(value) {
|
|
@@ -15608,6 +15613,7 @@ var documentProcessingStatusItemSchema = external_exports.object({
|
|
|
15608
15613
|
name: external_exports.string().nullable().optional().describe("Original file name; absent on older servers."),
|
|
15609
15614
|
status: external_exports.string().describe("Document processing status."),
|
|
15610
15615
|
createdAt: external_exports.string().nullable().optional().describe("Immutable document creation timestamp; absent on older servers."),
|
|
15616
|
+
importSessionId: external_exports.string().nullable().optional().describe("Import session the upload named (set when the document is uploaded or re-uploaded into that session); null for older uploads and uploads that named none; absent on older servers."),
|
|
15611
15617
|
lastActivityAt: external_exports.string().nullable().describe("Last backend activity timestamp (updated_at / heartbeat); null if unknown."),
|
|
15612
15618
|
queuePosition: external_exports.number().int().min(0).nullable().describe("0 = at the head (processing); >0 = waiting behind others; null = not in pipeline."),
|
|
15613
15619
|
health: external_exports.enum(DOCUMENT_HEALTH_VALUES).describe("Derived health from backend heartbeat + queue position.")
|
|
@@ -18569,8 +18575,8 @@ var COMMAND_REGISTRY = [
|
|
|
18569
18575
|
{
|
|
18570
18576
|
name: "objects revert",
|
|
18571
18577
|
group: "captable",
|
|
18572
|
-
description: "Preview or revert draft objects using backend dependency closure. Execution requires explicit final IDs (<object-id
|
|
18573
|
-
usage: "captable-ai objects revert --dry-run [<object-id> | --objects id1,id2,... | --all] [--json]\ncaptable-ai objects revert (<object-id> | --objects id1,id2,... | --all) [--yes] [--json]",
|
|
18578
|
+
description: "Preview or revert draft objects using backend dependency closure. Execution requires explicit final IDs (<object-id>, --objects or --objects-file) or --all.",
|
|
18579
|
+
usage: "captable-ai objects revert --dry-run [<object-id> | --objects id1,id2,... | --objects-file <path|-> | --all] [--json]\ncaptable-ai objects revert (<object-id> | --objects id1,id2,... | --objects-file <path|-> | --all) [--yes] [--json]",
|
|
18574
18580
|
requiresAuth: true,
|
|
18575
18581
|
requiresPortal: true,
|
|
18576
18582
|
flags: [
|
|
@@ -18584,6 +18590,11 @@ var COMMAND_REGISTRY = [
|
|
|
18584
18590
|
description: "Comma-separated final object IDs to revert selectively.",
|
|
18585
18591
|
type: "string"
|
|
18586
18592
|
},
|
|
18593
|
+
{
|
|
18594
|
+
name: "objects-file",
|
|
18595
|
+
description: "File of final object IDs to revert, one per line (- for stdin).",
|
|
18596
|
+
type: "string"
|
|
18597
|
+
},
|
|
18587
18598
|
{
|
|
18588
18599
|
name: "all",
|
|
18589
18600
|
description: "Revert all pending drafts for the active company.",
|
|
@@ -18693,8 +18704,8 @@ var COMMAND_REGISTRY = [
|
|
|
18693
18704
|
{
|
|
18694
18705
|
name: "commit",
|
|
18695
18706
|
group: "captable",
|
|
18696
|
-
description: "Commit pending draft objects to the cap table. Use --dry-run to preview selected/final IDs without writing. Execution requires --objects with final IDs from preview, or --all. Exits with NOTHING_TO_COMMIT if no drafts exist.",
|
|
18697
|
-
usage: "captable-ai commit --dry-run [--objects id1,id2,...] [--json]\ncaptable-ai commit (--objects id1,id2,... | --all) [--json]",
|
|
18707
|
+
description: "Commit pending draft objects to the cap table. Use --dry-run to preview selected/final IDs without writing. Execution requires --objects or --objects-file with final IDs from preview, or --all. Exits with NOTHING_TO_COMMIT if no drafts exist.",
|
|
18708
|
+
usage: "captable-ai commit --dry-run [--objects id1,id2,... | --objects-file <path|->] [--json]\ncaptable-ai commit (--objects id1,id2,... | --objects-file <path|-> | --all) [--json]",
|
|
18698
18709
|
requiresAuth: true,
|
|
18699
18710
|
requiresPortal: true,
|
|
18700
18711
|
flags: [
|
|
@@ -18712,6 +18723,11 @@ var COMMAND_REGISTRY = [
|
|
|
18712
18723
|
name: "objects",
|
|
18713
18724
|
description: "Comma-separated final object IDs to commit selectively. Use preview.applyObjectIds.",
|
|
18714
18725
|
type: "string"
|
|
18726
|
+
},
|
|
18727
|
+
{
|
|
18728
|
+
name: "objects-file",
|
|
18729
|
+
description: "File of final object IDs to commit, one per line (- for stdin).",
|
|
18730
|
+
type: "string"
|
|
18715
18731
|
}
|
|
18716
18732
|
]
|
|
18717
18733
|
},
|
|
@@ -19291,22 +19307,24 @@ Examples:
|
|
|
19291
19307
|
var HELP_OBJECTS_REVERT = `objects revert \u2014 Preview or revert draft objects
|
|
19292
19308
|
|
|
19293
19309
|
Usage:
|
|
19294
|
-
captable-ai objects revert --dry-run [<object-id> | --objects id1,id2,... | --all] [--json]
|
|
19310
|
+
captable-ai objects revert --dry-run [<object-id> | --objects id1,id2,... | --objects-file <path|-> | --all] [--json]
|
|
19295
19311
|
captable-ai objects revert <object-id> [--yes] [--json]
|
|
19296
19312
|
captable-ai objects revert --objects id1,id2,... [--yes] [--json]
|
|
19313
|
+
captable-ai objects revert --objects-file <path|-> [--yes] [--json]
|
|
19297
19314
|
captable-ai objects revert --all [--yes] [--json]
|
|
19298
19315
|
|
|
19299
19316
|
Previews or reverts draft objects using the backend dependency closure. Preview
|
|
19300
19317
|
returns selected IDs, final IDs, auto-included IDs, invalid IDs, dependency
|
|
19301
19318
|
reasons, blockers, and the applyObjectIds value to use for execution.
|
|
19302
19319
|
|
|
19303
|
-
Execution requires explicit final IDs (<object-id
|
|
19320
|
+
Execution requires explicit final IDs (<object-id>, --objects or --objects-file) or --all.
|
|
19304
19321
|
Use IDs from preview.applyObjectIds when applyObjectIds is not null. Revert-all
|
|
19305
19322
|
prompts for confirmation unless --yes or --json is set.
|
|
19306
19323
|
|
|
19307
19324
|
Options:
|
|
19308
19325
|
--dry-run Preview mode \u2014 no write
|
|
19309
19326
|
--objects IDS Revert specific final object IDs (comma-separated)
|
|
19327
|
+
--objects-file F Read the IDs from a file, one per line (- for stdin); for long lists
|
|
19310
19328
|
--all Revert all pending drafts for the active company
|
|
19311
19329
|
--yes Skip the confirmation prompt (for scripts)
|
|
19312
19330
|
|
|
@@ -19368,14 +19386,14 @@ ${HELP_SCHEMAS_GET}`;
|
|
|
19368
19386
|
var HELP_COMMIT = `commit \u2014 Commit pending drafts to the cap table
|
|
19369
19387
|
|
|
19370
19388
|
Usage:
|
|
19371
|
-
captable-ai commit --dry-run [--objects id1,id2,...] [--json]
|
|
19372
|
-
captable-ai commit (--objects id1,id2,... | --all) [--json]
|
|
19389
|
+
captable-ai commit --dry-run [--objects id1,id2,... | --objects-file <path|->] [--json]
|
|
19390
|
+
captable-ai commit (--objects id1,id2,... | --objects-file <path|-> | --all) [--json]
|
|
19373
19391
|
|
|
19374
19392
|
Previews or commits pending draft objects using the backend dependency closure.
|
|
19375
19393
|
Preview returns selected IDs, final IDs, auto-included IDs, invalid IDs,
|
|
19376
19394
|
dependency reasons, blockers, and the applyObjectIds value to use for execution.
|
|
19377
19395
|
|
|
19378
|
-
Execution requires explicit final IDs (--objects) or --all. Use IDs from
|
|
19396
|
+
Execution requires explicit final IDs (--objects or --objects-file) or --all. Use IDs from
|
|
19379
19397
|
preview.applyObjectIds when applyObjectIds is not null. Exits with
|
|
19380
19398
|
NOTHING_TO_COMMIT if no drafts exist.
|
|
19381
19399
|
|
|
@@ -19385,6 +19403,8 @@ Options:
|
|
|
19385
19403
|
--objects id1,id2,... Commit only specific object IDs (comma-separated). The server
|
|
19386
19404
|
validates that all transitive dependencies are satisfied.
|
|
19387
19405
|
Note: IDs containing commas are not supported.
|
|
19406
|
+
--objects-file F Read the IDs from a file, one per line (- for stdin), for lists too
|
|
19407
|
+
long for one command line. The dry run writes one when it must.
|
|
19388
19408
|
|
|
19389
19409
|
Global:
|
|
19390
19410
|
--json Machine-readable JSON output
|
|
@@ -19609,6 +19629,89 @@ Environment:
|
|
|
19609
19629
|
Global:
|
|
19610
19630
|
--json Machine-readable JSON output`;
|
|
19611
19631
|
|
|
19632
|
+
// src/selection-input.ts
|
|
19633
|
+
import { createHash, randomBytes } from "crypto";
|
|
19634
|
+
import { chmod, mkdir as mkdir2, readFile as readFile2, rename, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
19635
|
+
import { homedir as homedir2 } from "os";
|
|
19636
|
+
import { join as join2 } from "path";
|
|
19637
|
+
var CLI_SELECTION_MAX_BYTES = 1e5;
|
|
19638
|
+
function isPlainId(id) {
|
|
19639
|
+
return id.length > 0 && id === id.trim();
|
|
19640
|
+
}
|
|
19641
|
+
function canSelectInline(objectIds) {
|
|
19642
|
+
if (!objectIds.every((id) => isPlainId(id) && !id.includes(","))) return false;
|
|
19643
|
+
const joined = objectIds.join(",");
|
|
19644
|
+
const quotes = joined.split("'").length - 1;
|
|
19645
|
+
return Buffer.byteLength(`--objects=${joined}`, "utf8") + 2 + quotes * 3 <= CLI_SELECTION_MAX_BYTES;
|
|
19646
|
+
}
|
|
19647
|
+
function canSelectFromFile(objectIds) {
|
|
19648
|
+
return objectIds.every((id) => isPlainId(id) && !/[\r\n]/.test(id));
|
|
19649
|
+
}
|
|
19650
|
+
function parseObjectIdLines(text) {
|
|
19651
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
19652
|
+
}
|
|
19653
|
+
var processSelectionReader = {
|
|
19654
|
+
readFile: (path) => readFile2(path, "utf8"),
|
|
19655
|
+
stdinIsTerminal: () => process.stdin.isTTY === true,
|
|
19656
|
+
// Reads to end of input, like any `-` filter: a pipe that never closes waits, as `cat -` does.
|
|
19657
|
+
readStdin: async () => {
|
|
19658
|
+
const chunks = [];
|
|
19659
|
+
for await (const chunk of process.stdin) {
|
|
19660
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
19661
|
+
}
|
|
19662
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
19663
|
+
}
|
|
19664
|
+
};
|
|
19665
|
+
async function readSelectionIds(source, reader = processSelectionReader) {
|
|
19666
|
+
let text;
|
|
19667
|
+
try {
|
|
19668
|
+
if (source === "-") {
|
|
19669
|
+
if (reader.stdinIsTerminal()) {
|
|
19670
|
+
return {
|
|
19671
|
+
ok: false,
|
|
19672
|
+
message: "--objects-file - reads ids from stdin, but nothing is piped in."
|
|
19673
|
+
};
|
|
19674
|
+
}
|
|
19675
|
+
text = await reader.readStdin();
|
|
19676
|
+
} else {
|
|
19677
|
+
text = await reader.readFile(source);
|
|
19678
|
+
}
|
|
19679
|
+
} catch (error51) {
|
|
19680
|
+
const reason = error51 instanceof Error ? error51.message : String(error51);
|
|
19681
|
+
return { ok: false, message: `Could not read --objects-file ${source}: ${reason}` };
|
|
19682
|
+
}
|
|
19683
|
+
const objectIds = parseObjectIdLines(text);
|
|
19684
|
+
if (objectIds.length === 0) {
|
|
19685
|
+
return {
|
|
19686
|
+
ok: false,
|
|
19687
|
+
message: `The --objects-file ${source === "-" ? "input" : source} must include at least one object ID.`
|
|
19688
|
+
};
|
|
19689
|
+
}
|
|
19690
|
+
return { ok: true, objectIds };
|
|
19691
|
+
}
|
|
19692
|
+
function selectionsDir() {
|
|
19693
|
+
return join2(homedir2(), ".captable-ai", "selections");
|
|
19694
|
+
}
|
|
19695
|
+
async function writeSelectionFile(name, objectIds) {
|
|
19696
|
+
if (!canSelectFromFile(objectIds)) return null;
|
|
19697
|
+
const dir = selectionsDir();
|
|
19698
|
+
const digest = createHash("sha256").update(JSON.stringify(objectIds)).digest("hex").slice(0, 32);
|
|
19699
|
+
const path = join2(dir, `${name.replace(/[^A-Za-z0-9._-]/g, "_")}-${digest}.txt`);
|
|
19700
|
+
const temp = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
19701
|
+
try {
|
|
19702
|
+
await mkdir2(dir, { recursive: true, mode: 448 });
|
|
19703
|
+
await chmod(dir, 448);
|
|
19704
|
+
await writeFile2(temp, `${objectIds.join("\n")}
|
|
19705
|
+
`, { mode: 384, flag: "wx" });
|
|
19706
|
+
await chmod(temp, 384);
|
|
19707
|
+
await rename(temp, path);
|
|
19708
|
+
return path;
|
|
19709
|
+
} catch {
|
|
19710
|
+
await rm2(temp, { force: true }).catch(() => void 0);
|
|
19711
|
+
return null;
|
|
19712
|
+
}
|
|
19713
|
+
}
|
|
19714
|
+
|
|
19612
19715
|
// src/commands/draft-operations.ts
|
|
19613
19716
|
function parseObjectIdsList(raw) {
|
|
19614
19717
|
if (raw === void 0) return void 0;
|
|
@@ -19617,6 +19720,23 @@ function parseObjectIdsList(raw) {
|
|
|
19617
19720
|
function hasObjectIds(raw, objectIds) {
|
|
19618
19721
|
return raw !== void 0 && (objectIds?.length ?? 0) > 0;
|
|
19619
19722
|
}
|
|
19723
|
+
async function resolveObjectsInput(args, otherSelectorMessage = null, reader = processSelectionReader) {
|
|
19724
|
+
const objectsRaw = getFlagValue(args, "objects");
|
|
19725
|
+
const fileSource = getFlagValue(args, "objects-file");
|
|
19726
|
+
if (objectsRaw !== void 0 && fileSource !== void 0) {
|
|
19727
|
+
return { kind: "invalid", message: "Specify either --objects or --objects-file, not both." };
|
|
19728
|
+
}
|
|
19729
|
+
if (fileSource !== void 0) {
|
|
19730
|
+
if (otherSelectorMessage !== null) return { kind: "invalid", message: otherSelectorMessage };
|
|
19731
|
+
const read = await readSelectionIds(fileSource, reader);
|
|
19732
|
+
return read.ok ? { kind: "ids", objectIds: read.objectIds } : { kind: "invalid", message: read.message };
|
|
19733
|
+
}
|
|
19734
|
+
const objectIds = parseObjectIdsList(objectsRaw);
|
|
19735
|
+
if (objectsRaw !== void 0 && !hasObjectIds(objectsRaw, objectIds)) {
|
|
19736
|
+
return { kind: "invalid", message: "The --objects flag must include at least one object ID." };
|
|
19737
|
+
}
|
|
19738
|
+
return objectIds === void 0 ? { kind: "none" } : { kind: "ids", objectIds };
|
|
19739
|
+
}
|
|
19620
19740
|
function operationPastTense(operation) {
|
|
19621
19741
|
return operation === "commit" ? "committed" : "reverted";
|
|
19622
19742
|
}
|
|
@@ -19667,7 +19787,7 @@ function canApplyRevertAllFallback(data, operation) {
|
|
|
19667
19787
|
function shellQuote(value) {
|
|
19668
19788
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
19669
19789
|
}
|
|
19670
|
-
function printDraftOperationPreview(data, options) {
|
|
19790
|
+
async function printDraftOperationPreview(data, options) {
|
|
19671
19791
|
const applied = operationPastTense(options.operation);
|
|
19672
19792
|
const objectCount = data.finalIds.length;
|
|
19673
19793
|
if (objectCount === 0) {
|
|
@@ -19702,10 +19822,29 @@ Run \`${options.command} --all\` to revert all pending drafts.
|
|
|
19702
19822
|
printLine("\nResolve the reported issues before applying this preview.\n");
|
|
19703
19823
|
return;
|
|
19704
19824
|
}
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
Run \`${
|
|
19825
|
+
if (data.applyObjectIds === null) {
|
|
19826
|
+
printLine(`
|
|
19827
|
+
Run \`${options.command} --all\` to apply this preview.
|
|
19708
19828
|
`);
|
|
19829
|
+
return;
|
|
19830
|
+
}
|
|
19831
|
+
if (canSelectInline(data.applyObjectIds)) {
|
|
19832
|
+
printLine(
|
|
19833
|
+
`
|
|
19834
|
+
Run \`${options.command} --objects=${shellQuote(data.applyObjectIds.join(","))}\` to apply this preview.
|
|
19835
|
+
`
|
|
19836
|
+
);
|
|
19837
|
+
return;
|
|
19838
|
+
}
|
|
19839
|
+
const file2 = await writeSelectionFile(`${options.operation}-apply`, data.applyObjectIds);
|
|
19840
|
+
printLine(
|
|
19841
|
+
file2 ? `
|
|
19842
|
+
These ${data.applyObjectIds.length} IDs cannot be given inline (too many for one command line, or an ID with a comma). They are in ${file2}.
|
|
19843
|
+
Run \`${options.command} --objects-file ${shellQuote(file2)}\` to apply this preview.
|
|
19844
|
+
` : `
|
|
19845
|
+
These ${data.applyObjectIds.length} IDs cannot be put on one command line or in a file as they are (too many, or an ID with surrounding spaces or a line break). Apply this preview from the Studio timeline or the MCP.
|
|
19846
|
+
`
|
|
19847
|
+
);
|
|
19709
19848
|
}
|
|
19710
19849
|
|
|
19711
19850
|
// src/commands/commit.ts
|
|
@@ -19715,8 +19854,8 @@ async function runCommit(args, rawArgv) {
|
|
|
19715
19854
|
`);
|
|
19716
19855
|
process.exit(0);
|
|
19717
19856
|
}
|
|
19718
|
-
validateFlags(args, { flags: ["objects"], booleans: ["all", "dry-run"] });
|
|
19719
|
-
const USAGE2 = "captable-ai commit --dry-run [--objects id1,id2,...] [--json]\ncaptable-ai commit (--objects id1,id2,... | --all) [--json]";
|
|
19857
|
+
validateFlags(args, { flags: ["objects", "objects-file"], booleans: ["all", "dry-run"] });
|
|
19858
|
+
const USAGE2 = "captable-ai commit --dry-run [--objects id1,id2,... | --objects-file <path|->] [--json]\ncaptable-ai commit (--objects id1,id2,... | --objects-file <path|-> | --all) [--json]";
|
|
19720
19859
|
if (args.positional.length > 0) {
|
|
19721
19860
|
exitWithUsage(
|
|
19722
19861
|
`Unexpected argument: ${args.positional[0]}. This command takes no positional arguments.`,
|
|
@@ -19725,14 +19864,14 @@ async function runCommit(args, rawArgv) {
|
|
|
19725
19864
|
}
|
|
19726
19865
|
const dryRun = hasFlag(args, "dry-run");
|
|
19727
19866
|
const all = hasFlag(args, "all");
|
|
19728
|
-
const
|
|
19729
|
-
|
|
19730
|
-
|
|
19731
|
-
|
|
19732
|
-
|
|
19733
|
-
|
|
19867
|
+
const objectsInput = await resolveObjectsInput(
|
|
19868
|
+
args,
|
|
19869
|
+
all ? "Specify either --objects (or --objects-file) or --all, not both." : null
|
|
19870
|
+
);
|
|
19871
|
+
if (objectsInput.kind === "invalid") exitWithUsage(objectsInput.message, USAGE2);
|
|
19872
|
+
const objectIds = objectsInput.kind === "ids" ? objectsInput.objectIds : void 0;
|
|
19734
19873
|
if (all && objectIds) {
|
|
19735
|
-
exitWithUsage("Specify either --objects or --all, not both.", USAGE2);
|
|
19874
|
+
exitWithUsage("Specify either --objects (or --objects-file) or --all, not both.", USAGE2);
|
|
19736
19875
|
}
|
|
19737
19876
|
if (dryRun) {
|
|
19738
19877
|
const portalId2 = await getActivePortalId();
|
|
@@ -19751,7 +19890,7 @@ async function runCommit(args, rawArgv) {
|
|
|
19751
19890
|
printJson({ data: data2 });
|
|
19752
19891
|
return;
|
|
19753
19892
|
}
|
|
19754
|
-
printDraftOperationPreview(data2, { command: "captable-ai commit", operation: "commit" });
|
|
19893
|
+
await printDraftOperationPreview(data2, { command: "captable-ai commit", operation: "commit" });
|
|
19755
19894
|
return;
|
|
19756
19895
|
}
|
|
19757
19896
|
const selection = resolveDraftOperationSelection(
|
|
@@ -20476,12 +20615,12 @@ async function runDocumentsRestartExtraction(args, rawArgv) {
|
|
|
20476
20615
|
|
|
20477
20616
|
// src/jobs/tracker.ts
|
|
20478
20617
|
import { randomUUID } from "crypto";
|
|
20479
|
-
import { appendFile, mkdir as
|
|
20480
|
-
import { homedir as
|
|
20481
|
-
import { join as
|
|
20482
|
-
var CONFIG_DIR2 =
|
|
20483
|
-
var JOBS_FILE =
|
|
20484
|
-
var LOCK_DIR =
|
|
20618
|
+
import { appendFile, mkdir as mkdir3, readFile as readFile3, rename as rename2, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
20619
|
+
import { homedir as homedir3 } from "os";
|
|
20620
|
+
import { join as join3 } from "path";
|
|
20621
|
+
var CONFIG_DIR2 = join3(homedir3(), ".captable-ai");
|
|
20622
|
+
var JOBS_FILE = join3(CONFIG_DIR2, "jobs.jsonl");
|
|
20623
|
+
var LOCK_DIR = join3(CONFIG_DIR2, "jobs.lock");
|
|
20485
20624
|
var VALID_JOB_STATUSES = /* @__PURE__ */ new Set(["submitted", "running", "complete", "failed"]);
|
|
20486
20625
|
var VALID_JOB_KINDS = /* @__PURE__ */ new Set(["extraction"]);
|
|
20487
20626
|
function isRecord3(v) {
|
|
@@ -20492,7 +20631,7 @@ function isJobEntry(value) {
|
|
|
20492
20631
|
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);
|
|
20493
20632
|
}
|
|
20494
20633
|
async function ensureDir() {
|
|
20495
|
-
await
|
|
20634
|
+
await mkdir3(CONFIG_DIR2, { recursive: true, mode: 448 });
|
|
20496
20635
|
}
|
|
20497
20636
|
function sleep(ms) {
|
|
20498
20637
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -20509,7 +20648,7 @@ async function withJobsLock(fn) {
|
|
|
20509
20648
|
const startedAt = Date.now();
|
|
20510
20649
|
for (; ; ) {
|
|
20511
20650
|
try {
|
|
20512
|
-
await
|
|
20651
|
+
await mkdir3(LOCK_DIR, { mode: 448 });
|
|
20513
20652
|
break;
|
|
20514
20653
|
} catch (err) {
|
|
20515
20654
|
if (!isErrno(err, "EEXIST")) throw err;
|
|
@@ -20522,7 +20661,7 @@ async function withJobsLock(fn) {
|
|
|
20522
20661
|
try {
|
|
20523
20662
|
return await fn();
|
|
20524
20663
|
} finally {
|
|
20525
|
-
await
|
|
20664
|
+
await rm3(LOCK_DIR, { recursive: true, force: true });
|
|
20526
20665
|
}
|
|
20527
20666
|
}
|
|
20528
20667
|
function generateJobId() {
|
|
@@ -20537,7 +20676,7 @@ async function appendJob(entry) {
|
|
|
20537
20676
|
async function listJobs(filter) {
|
|
20538
20677
|
let raw;
|
|
20539
20678
|
try {
|
|
20540
|
-
raw = await
|
|
20679
|
+
raw = await readFile3(JOBS_FILE, "utf8");
|
|
20541
20680
|
} catch (err) {
|
|
20542
20681
|
if (!isErrno(err, "ENOENT")) throw err;
|
|
20543
20682
|
return [];
|
|
@@ -20598,10 +20737,10 @@ async function pruneJobs(before) {
|
|
|
20598
20737
|
async function atomicWriteJobs(content) {
|
|
20599
20738
|
const tmp = `${JOBS_FILE}.${process.pid}.${Date.now()}.tmp`;
|
|
20600
20739
|
try {
|
|
20601
|
-
await
|
|
20602
|
-
await
|
|
20740
|
+
await writeFile3(tmp, content, { mode: 384 });
|
|
20741
|
+
await rename2(tmp, JOBS_FILE);
|
|
20603
20742
|
} catch (err) {
|
|
20604
|
-
await
|
|
20743
|
+
await rm3(tmp, { force: true });
|
|
20605
20744
|
throw err;
|
|
20606
20745
|
}
|
|
20607
20746
|
}
|
|
@@ -20778,12 +20917,12 @@ function flagMissingIds(missingIds) {
|
|
|
20778
20917
|
}
|
|
20779
20918
|
|
|
20780
20919
|
// src/commands/documents-upload.ts
|
|
20781
|
-
import { createHash } from "crypto";
|
|
20782
|
-
import { readFile as
|
|
20920
|
+
import { createHash as createHash2 } from "crypto";
|
|
20921
|
+
import { readFile as readFile4, stat } from "fs/promises";
|
|
20783
20922
|
import { basename } from "path";
|
|
20784
20923
|
async function hashFile(path) {
|
|
20785
|
-
const data = await
|
|
20786
|
-
return
|
|
20924
|
+
const data = await readFile4(path);
|
|
20925
|
+
return createHash2("sha256").update(data).digest("hex");
|
|
20787
20926
|
}
|
|
20788
20927
|
async function runDocumentsUpload(args, rawArgv) {
|
|
20789
20928
|
if (rawArgv.includes("--help") || rawArgv.includes("-h")) {
|
|
@@ -20839,7 +20978,7 @@ async function runDocumentsUpload(args, rawArgv) {
|
|
|
20839
20978
|
}
|
|
20840
20979
|
for (const { doc, filePath } of toUpload) {
|
|
20841
20980
|
if (!isJsonMode()) printLine(` \u2191 Uploading ${doc.name}...`);
|
|
20842
|
-
const fileData = await
|
|
20981
|
+
const fileData = await readFile4(filePath);
|
|
20843
20982
|
const putRes = await fetch(doc.presignedUrl, {
|
|
20844
20983
|
method: "PUT",
|
|
20845
20984
|
headers: { "Content-Type": doc.contentType },
|
|
@@ -20890,11 +21029,11 @@ ${successCount}/${results.length} document(s) confirmed.`);
|
|
|
20890
21029
|
}
|
|
20891
21030
|
|
|
20892
21031
|
// src/commands/feedback.ts
|
|
20893
|
-
import { appendFile as appendFile2, mkdir as
|
|
20894
|
-
import { homedir as
|
|
20895
|
-
import { join as
|
|
20896
|
-
var CONFIG_DIR3 =
|
|
20897
|
-
var FEEDBACK_FILE =
|
|
21032
|
+
import { appendFile as appendFile2, mkdir as mkdir4, readFile as readFile5, rename as rename3, rm as rm4, writeFile as writeFile4 } from "fs/promises";
|
|
21033
|
+
import { homedir as homedir4 } from "os";
|
|
21034
|
+
import { join as join4 } from "path";
|
|
21035
|
+
var CONFIG_DIR3 = join4(homedir4(), ".captable-ai");
|
|
21036
|
+
var FEEDBACK_FILE = join4(CONFIG_DIR3, "feedback.jsonl");
|
|
20898
21037
|
function isErrno2(err, code) {
|
|
20899
21038
|
return typeof err === "object" && err !== null && "code" in err && err.code === code;
|
|
20900
21039
|
}
|
|
@@ -20963,7 +21102,7 @@ async function runFeedback(args, rawArgv) {
|
|
|
20963
21102
|
if (!text.trim()) {
|
|
20964
21103
|
exitWithUsage("Feedback text cannot be empty.", 'Usage: captable-ai feedback "<message>"');
|
|
20965
21104
|
}
|
|
20966
|
-
await
|
|
21105
|
+
await mkdir4(CONFIG_DIR3, { recursive: true, mode: 448 });
|
|
20967
21106
|
const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), text: text.trim(), sent: false };
|
|
20968
21107
|
await appendFile2(FEEDBACK_FILE, `${JSON.stringify(entry)}
|
|
20969
21108
|
`, { mode: 384 });
|
|
@@ -21046,14 +21185,14 @@ async function runFeedbackSend() {
|
|
|
21046
21185
|
);
|
|
21047
21186
|
}
|
|
21048
21187
|
const updated = entries.map((e) => e.sent ? e : { ...e, sent: true });
|
|
21049
|
-
await
|
|
21188
|
+
await mkdir4(CONFIG_DIR3, { recursive: true, mode: 448 });
|
|
21050
21189
|
const tmp = `${FEEDBACK_FILE}.${process.pid}.${Date.now()}.tmp`;
|
|
21051
21190
|
try {
|
|
21052
|
-
await
|
|
21191
|
+
await writeFile4(tmp, updated.map((e) => `${JSON.stringify(e)}
|
|
21053
21192
|
`).join(""), { mode: 384 });
|
|
21054
|
-
await
|
|
21193
|
+
await rename3(tmp, FEEDBACK_FILE);
|
|
21055
21194
|
} catch (err) {
|
|
21056
|
-
await
|
|
21195
|
+
await rm4(tmp, { force: true });
|
|
21057
21196
|
throw err;
|
|
21058
21197
|
}
|
|
21059
21198
|
if (isJsonMode()) {
|
|
@@ -21065,7 +21204,7 @@ async function runFeedbackSend() {
|
|
|
21065
21204
|
async function readEntries() {
|
|
21066
21205
|
let raw;
|
|
21067
21206
|
try {
|
|
21068
|
-
raw = await
|
|
21207
|
+
raw = await readFile5(FEEDBACK_FILE, "utf8");
|
|
21069
21208
|
} catch (err) {
|
|
21070
21209
|
if (!isErrno2(err, "ENOENT")) {
|
|
21071
21210
|
exitWithError(
|
|
@@ -21283,7 +21422,7 @@ async function fetchTrackedDocuments(portalId, assetIds) {
|
|
|
21283
21422
|
|
|
21284
21423
|
// src/commands/login.ts
|
|
21285
21424
|
import { execFile } from "child_process";
|
|
21286
|
-
import { randomBytes, timingSafeEqual } from "crypto";
|
|
21425
|
+
import { randomBytes as randomBytes2, timingSafeEqual } from "crypto";
|
|
21287
21426
|
import { createServer } from "http";
|
|
21288
21427
|
import { platform } from "os";
|
|
21289
21428
|
import { createInterface } from "readline";
|
|
@@ -21396,7 +21535,7 @@ function tryParseLoginPaste(line, expectedState) {
|
|
|
21396
21535
|
async function runLogin(args) {
|
|
21397
21536
|
validateFlags(args, { flags: [], booleans: [] });
|
|
21398
21537
|
const jsonMode2 = isJsonMode();
|
|
21399
|
-
const state =
|
|
21538
|
+
const state = randomBytes2(16).toString("hex");
|
|
21400
21539
|
let settled = false;
|
|
21401
21540
|
let timeoutId;
|
|
21402
21541
|
let rl;
|
|
@@ -22407,16 +22546,19 @@ async function runObjectsRevert(args, rawArgv) {
|
|
|
22407
22546
|
`);
|
|
22408
22547
|
process.exit(0);
|
|
22409
22548
|
}
|
|
22410
|
-
validateFlags(args, { flags: ["objects"], booleans: ["all", "dry-run", "yes"] });
|
|
22549
|
+
validateFlags(args, { flags: ["objects", "objects-file"], booleans: ["all", "dry-run", "yes"] });
|
|
22411
22550
|
const objectIdRaw = args.positional[0];
|
|
22412
22551
|
const objectId = objectIdRaw?.trim();
|
|
22413
22552
|
const all = hasFlag(args, "all");
|
|
22414
22553
|
const dryRun = hasFlag(args, "dry-run");
|
|
22415
22554
|
const yes = hasFlag(args, "yes");
|
|
22416
|
-
const
|
|
22417
|
-
const
|
|
22418
|
-
|
|
22419
|
-
|
|
22555
|
+
const USAGE2 = "captable-ai objects revert --dry-run [<object-id> | --objects id1,id2,... | --objects-file <path|-> | --all] [--json]\ncaptable-ai objects revert (<object-id> | --objects id1,id2,... | --objects-file <path|-> | --all) [--yes] [--json]";
|
|
22556
|
+
const objectsInput = await resolveObjectsInput(
|
|
22557
|
+
args,
|
|
22558
|
+
objectIdRaw !== void 0 || all ? "Specify only one of an object ID, --objects (or --objects-file), or --all." : null
|
|
22559
|
+
);
|
|
22560
|
+
if (objectsInput.kind === "invalid") exitWithUsage(objectsInput.message, USAGE2);
|
|
22561
|
+
const objectIdsFromFlag = objectsInput.kind === "ids" ? objectsInput.objectIds : void 0;
|
|
22420
22562
|
if (args.positional.length > 1) {
|
|
22421
22563
|
exitWithUsage(
|
|
22422
22564
|
`Too many arguments: expected one object ID, got ${args.positional.length}.`,
|
|
@@ -22426,12 +22568,12 @@ async function runObjectsRevert(args, rawArgv) {
|
|
|
22426
22568
|
if (objectIdRaw !== void 0 && !objectId) {
|
|
22427
22569
|
exitWithUsage("Object ID must be a non-empty string.", USAGE2);
|
|
22428
22570
|
}
|
|
22429
|
-
if (objectIdsRaw !== void 0 && !hasExplicitObjectIds) {
|
|
22430
|
-
exitWithUsage("The --objects flag must include at least one object ID.", USAGE2);
|
|
22431
|
-
}
|
|
22432
22571
|
const selectionSources = [Boolean(objectId), Boolean(objectIdsFromFlag), all].filter(Boolean);
|
|
22433
22572
|
if (selectionSources.length > 1) {
|
|
22434
|
-
exitWithUsage(
|
|
22573
|
+
exitWithUsage(
|
|
22574
|
+
"Specify only one of an object ID, --objects (or --objects-file), or --all.",
|
|
22575
|
+
USAGE2
|
|
22576
|
+
);
|
|
22435
22577
|
}
|
|
22436
22578
|
const objectIds = objectId ? [objectId] : objectIdsFromFlag;
|
|
22437
22579
|
if (dryRun) {
|
|
@@ -22450,7 +22592,7 @@ async function runObjectsRevert(args, rawArgv) {
|
|
|
22450
22592
|
printJson({ data: res2.data });
|
|
22451
22593
|
return;
|
|
22452
22594
|
}
|
|
22453
|
-
printDraftOperationPreview(res2.data, {
|
|
22595
|
+
await printDraftOperationPreview(res2.data, {
|
|
22454
22596
|
command: "captable-ai objects revert",
|
|
22455
22597
|
operation: "revert"
|
|
22456
22598
|
});
|
|
@@ -23103,13 +23245,14 @@ async function runImportStart(args) {
|
|
|
23103
23245
|
function scopeFlag(ids) {
|
|
23104
23246
|
return ids && ids.length > 0 ? ` --s3-asset-ids ${ids.join(",")}` : "";
|
|
23105
23247
|
}
|
|
23106
|
-
|
|
23107
|
-
|
|
23108
|
-
|
|
23109
|
-
|
|
23248
|
+
function selectionTooLong(action) {
|
|
23249
|
+
return (action.id === "preview_commit" || action.id === "confirm_commit") && action.objectIds !== void 0 && !canSelectInline(action.objectIds);
|
|
23250
|
+
}
|
|
23251
|
+
function importStatusCommand(action, sessionId, selectionFile) {
|
|
23252
|
+
if (selectionTooLong(action) && selectionFile === null) {
|
|
23110
23253
|
return {
|
|
23111
23254
|
id: action.id,
|
|
23112
|
-
label: `This import has ${action.objectIds?.length ?? 0} drafts, too many to select on one command line. Commit them from the Studio timeline, or with the MCP. Not --all: the portal also has pending drafts from other sources.`,
|
|
23255
|
+
label: `This import has ${action.objectIds?.length ?? 0} drafts, too many to select on one command line, and they could not be written to a file. Commit them from the Studio timeline, or with the MCP. Not --all: the portal also has pending drafts from other sources.`,
|
|
23113
23256
|
required: action.required
|
|
23114
23257
|
};
|
|
23115
23258
|
}
|
|
@@ -23139,7 +23282,8 @@ function importStatusCommand(action, sessionId) {
|
|
|
23139
23282
|
return `captable-ai objects list --status draft --ids=${shellQuote(reviewable.join(","))}`;
|
|
23140
23283
|
}
|
|
23141
23284
|
case "preview_commit":
|
|
23142
|
-
|
|
23285
|
+
if (!action.objectIds) return "captable-ai commit --dry-run";
|
|
23286
|
+
return selectionTooLong(action) && selectionFile !== null ? `captable-ai commit --dry-run --objects-file ${shellQuote(selectionFile)}` : `captable-ai commit --dry-run --objects=${shellQuote(action.objectIds.join(","))}`;
|
|
23143
23287
|
case "confirm_commit":
|
|
23144
23288
|
return action.objectIds ? "captable-ai commit --objects <final IDs from the dry run>" : "captable-ai commit --all";
|
|
23145
23289
|
default: {
|
|
@@ -23206,7 +23350,11 @@ async function runImportStatus(args) {
|
|
|
23206
23350
|
`/import-sessions/${encodeURIComponent(sessionId)}/status`
|
|
23207
23351
|
);
|
|
23208
23352
|
const portalDocuments = response.data?.processing?.documents ?? [];
|
|
23209
|
-
const documents = documentsForImportSession(
|
|
23353
|
+
const documents = documentsForImportSession(
|
|
23354
|
+
portalDocuments,
|
|
23355
|
+
response.data?.session?.createdAt,
|
|
23356
|
+
sessionId
|
|
23357
|
+
);
|
|
23210
23358
|
const documentIds = documents.flatMap(
|
|
23211
23359
|
(document) => typeof document.s3AssetId === "string" ? [document.s3AssetId] : []
|
|
23212
23360
|
);
|
|
@@ -23233,7 +23381,11 @@ async function runImportStatus(args) {
|
|
|
23233
23381
|
response.data.session = { ...response.data.session, ...completed.data.session };
|
|
23234
23382
|
}
|
|
23235
23383
|
}
|
|
23236
|
-
const
|
|
23384
|
+
const longSelection = plan.actions.find(selectionTooLong)?.objectIds;
|
|
23385
|
+
const selectionFile = longSelection ? await writeSelectionFile(`import-${sessionId}`, longSelection) : null;
|
|
23386
|
+
const nextActions = plan.actions.map(
|
|
23387
|
+
(action) => importStatusCommand(action, sessionId, selectionFile)
|
|
23388
|
+
);
|
|
23237
23389
|
const suggestMonitoring = shouldSuggestBackgroundMonitoring(plan.phase);
|
|
23238
23390
|
const data = {
|
|
23239
23391
|
...response.data,
|
|
@@ -23457,11 +23609,11 @@ function formatNumber(n) {
|
|
|
23457
23609
|
}
|
|
23458
23610
|
|
|
23459
23611
|
// src/profiles.ts
|
|
23460
|
-
import { mkdir as
|
|
23461
|
-
import { homedir as
|
|
23462
|
-
import { join as
|
|
23463
|
-
var CONFIG_DIR4 =
|
|
23464
|
-
var PROFILES_FILE =
|
|
23612
|
+
import { mkdir as mkdir5, readFile as readFile6, rename as rename4, rm as rm5, writeFile as writeFile5 } from "fs/promises";
|
|
23613
|
+
import { homedir as homedir5 } from "os";
|
|
23614
|
+
import { join as join5 } from "path";
|
|
23615
|
+
var CONFIG_DIR4 = join5(homedir5(), ".captable-ai");
|
|
23616
|
+
var PROFILES_FILE = join5(CONFIG_DIR4, "profiles.json");
|
|
23465
23617
|
function isRecord9(value) {
|
|
23466
23618
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23467
23619
|
}
|
|
@@ -23489,7 +23641,7 @@ function parseProfiles(raw) {
|
|
|
23489
23641
|
}
|
|
23490
23642
|
async function readProfiles() {
|
|
23491
23643
|
try {
|
|
23492
|
-
const raw = await
|
|
23644
|
+
const raw = await readFile6(PROFILES_FILE, "utf8");
|
|
23493
23645
|
return parseProfiles(raw);
|
|
23494
23646
|
} catch (err) {
|
|
23495
23647
|
if (isErrno3(err, "ENOENT")) return { profiles: {}, default: null };
|
|
@@ -23503,13 +23655,13 @@ async function readProfiles() {
|
|
|
23503
23655
|
}
|
|
23504
23656
|
async function writeProfiles(data) {
|
|
23505
23657
|
try {
|
|
23506
|
-
await
|
|
23658
|
+
await mkdir5(CONFIG_DIR4, { recursive: true, mode: 448 });
|
|
23507
23659
|
const tmp = `${PROFILES_FILE}.${process.pid}.${Date.now()}.tmp`;
|
|
23508
23660
|
try {
|
|
23509
|
-
await
|
|
23510
|
-
await
|
|
23661
|
+
await writeFile5(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
23662
|
+
await rename4(tmp, PROFILES_FILE);
|
|
23511
23663
|
} catch (err) {
|
|
23512
|
-
await
|
|
23664
|
+
await rm5(tmp, { force: true });
|
|
23513
23665
|
throw err;
|
|
23514
23666
|
}
|
|
23515
23667
|
} catch (err) {
|
package/package.json
CHANGED