@fairmint/captable-ai 0.1.34 → 0.1.36
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 +354 -95
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15355,10 +15355,82 @@ function planImportStatus({ documents, sessionState, scopeUnknown = false }) {
|
|
|
15355
15355
|
}
|
|
15356
15356
|
}
|
|
15357
15357
|
}
|
|
15358
|
-
|
|
15359
|
-
|
|
15358
|
+
var IMPORT_REVIEW_MAX_IDS = 50;
|
|
15359
|
+
function reviewIsCut(action) {
|
|
15360
|
+
return action.reviewObjectIds !== void 0 && action.s3AssetIds?.length !== 1 && action.reviewObjectIds.length > IMPORT_REVIEW_MAX_IDS;
|
|
15361
|
+
}
|
|
15362
|
+
function isFileRecordRow(draft) {
|
|
15363
|
+
return draft.type === "DOCUMENT" || draft.subtype === "DOCUMENT";
|
|
15364
|
+
}
|
|
15365
|
+
function partitionImportDrafts(drafts, documentIds) {
|
|
15366
|
+
if (documentIds.length === 0)
|
|
15367
|
+
return null;
|
|
15368
|
+
const documents = new Set(documentIds.map((id) => id.toLowerCase()));
|
|
15369
|
+
const importDraftIds = [];
|
|
15370
|
+
const importReviewableIds = [];
|
|
15371
|
+
const portalIssuerDraftIds = [];
|
|
15372
|
+
let otherDrafts = false;
|
|
15373
|
+
for (const draft of drafts) {
|
|
15374
|
+
if (typeof draft.id !== "string" || draft.id.length === 0)
|
|
15375
|
+
return null;
|
|
15376
|
+
if (!("documentId" in draft))
|
|
15377
|
+
return null;
|
|
15378
|
+
const ours = typeof draft.documentId === "string" && documents.has(draft.documentId.toLowerCase());
|
|
15379
|
+
const selectable = !draft.id.includes(",") && draft.id === draft.id.trim();
|
|
15380
|
+
if (!ours) {
|
|
15381
|
+
if (draft.type === "ISSUER" && draft.documentId === null) {
|
|
15382
|
+
if (!selectable)
|
|
15383
|
+
return null;
|
|
15384
|
+
portalIssuerDraftIds.push(draft.id);
|
|
15385
|
+
} else {
|
|
15386
|
+
otherDrafts = true;
|
|
15387
|
+
}
|
|
15388
|
+
continue;
|
|
15389
|
+
}
|
|
15390
|
+
if (!selectable)
|
|
15391
|
+
return null;
|
|
15392
|
+
importDraftIds.push(draft.id);
|
|
15393
|
+
if (!isFileRecordRow(draft))
|
|
15394
|
+
importReviewableIds.push(draft.id);
|
|
15395
|
+
}
|
|
15396
|
+
return { importDraftIds, importReviewableIds, portalIssuerDraftIds, otherDrafts };
|
|
15397
|
+
}
|
|
15398
|
+
function applyImportDrafts(plan, scope, documentIds) {
|
|
15399
|
+
if (plan.phase !== "complete" || scope === null)
|
|
15400
|
+
return plan;
|
|
15401
|
+
if (scope.importReviewableIds.length === 0) {
|
|
15402
|
+
return {
|
|
15403
|
+
...plan,
|
|
15404
|
+
state: "complete",
|
|
15405
|
+
actions: [],
|
|
15406
|
+
...scope.otherDrafts ? { unrelatedDrafts: true } : {}
|
|
15407
|
+
};
|
|
15408
|
+
}
|
|
15409
|
+
if (!scope.otherDrafts)
|
|
15360
15410
|
return plan;
|
|
15361
|
-
|
|
15411
|
+
const selection = [...scope.importDraftIds, ...scope.portalIssuerDraftIds];
|
|
15412
|
+
const scoped = (action) => {
|
|
15413
|
+
const base = { ...action, objectIds: selection };
|
|
15414
|
+
switch (action.id) {
|
|
15415
|
+
case "review_drafts":
|
|
15416
|
+
return {
|
|
15417
|
+
...base,
|
|
15418
|
+
label: "Review the drafts from this import's documents",
|
|
15419
|
+
s3AssetIds: [...documentIds],
|
|
15420
|
+
reviewObjectIds: scope.importReviewableIds
|
|
15421
|
+
};
|
|
15422
|
+
case "preview_commit":
|
|
15423
|
+
return { ...base, label: "Preview committing this import's drafts" };
|
|
15424
|
+
case "confirm_commit":
|
|
15425
|
+
return {
|
|
15426
|
+
...base,
|
|
15427
|
+
label: "Commit this import's drafts with the preview's final IDs as objectIds. Not allDrafts: the portal also has pending drafts from other sources"
|
|
15428
|
+
};
|
|
15429
|
+
default:
|
|
15430
|
+
return action;
|
|
15431
|
+
}
|
|
15432
|
+
};
|
|
15433
|
+
return { ...plan, actions: plan.actions.map(scoped) };
|
|
15362
15434
|
}
|
|
15363
15435
|
var EXTRACTION_NOT_READY_REASONS = [
|
|
15364
15436
|
"pending_upload",
|
|
@@ -18497,8 +18569,8 @@ var COMMAND_REGISTRY = [
|
|
|
18497
18569
|
{
|
|
18498
18570
|
name: "objects revert",
|
|
18499
18571
|
group: "captable",
|
|
18500
|
-
description: "Preview or revert draft objects using backend dependency closure. Execution requires explicit final IDs (<object-id
|
|
18501
|
-
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]",
|
|
18572
|
+
description: "Preview or revert draft objects using backend dependency closure. Execution requires explicit final IDs (<object-id>, --objects or --objects-file) or --all.",
|
|
18573
|
+
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]",
|
|
18502
18574
|
requiresAuth: true,
|
|
18503
18575
|
requiresPortal: true,
|
|
18504
18576
|
flags: [
|
|
@@ -18512,6 +18584,11 @@ var COMMAND_REGISTRY = [
|
|
|
18512
18584
|
description: "Comma-separated final object IDs to revert selectively.",
|
|
18513
18585
|
type: "string"
|
|
18514
18586
|
},
|
|
18587
|
+
{
|
|
18588
|
+
name: "objects-file",
|
|
18589
|
+
description: "File of final object IDs to revert, one per line (- for stdin).",
|
|
18590
|
+
type: "string"
|
|
18591
|
+
},
|
|
18515
18592
|
{
|
|
18516
18593
|
name: "all",
|
|
18517
18594
|
description: "Revert all pending drafts for the active company.",
|
|
@@ -18621,8 +18698,8 @@ var COMMAND_REGISTRY = [
|
|
|
18621
18698
|
{
|
|
18622
18699
|
name: "commit",
|
|
18623
18700
|
group: "captable",
|
|
18624
|
-
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.",
|
|
18625
|
-
usage: "captable-ai commit --dry-run [--objects id1,id2,...] [--json]\ncaptable-ai commit (--objects id1,id2,... | --all) [--json]",
|
|
18701
|
+
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.",
|
|
18702
|
+
usage: "captable-ai commit --dry-run [--objects id1,id2,... | --objects-file <path|->] [--json]\ncaptable-ai commit (--objects id1,id2,... | --objects-file <path|-> | --all) [--json]",
|
|
18626
18703
|
requiresAuth: true,
|
|
18627
18704
|
requiresPortal: true,
|
|
18628
18705
|
flags: [
|
|
@@ -18640,6 +18717,11 @@ var COMMAND_REGISTRY = [
|
|
|
18640
18717
|
name: "objects",
|
|
18641
18718
|
description: "Comma-separated final object IDs to commit selectively. Use preview.applyObjectIds.",
|
|
18642
18719
|
type: "string"
|
|
18720
|
+
},
|
|
18721
|
+
{
|
|
18722
|
+
name: "objects-file",
|
|
18723
|
+
description: "File of final object IDs to commit, one per line (- for stdin).",
|
|
18724
|
+
type: "string"
|
|
18643
18725
|
}
|
|
18644
18726
|
]
|
|
18645
18727
|
},
|
|
@@ -19219,22 +19301,24 @@ Examples:
|
|
|
19219
19301
|
var HELP_OBJECTS_REVERT = `objects revert \u2014 Preview or revert draft objects
|
|
19220
19302
|
|
|
19221
19303
|
Usage:
|
|
19222
|
-
captable-ai objects revert --dry-run [<object-id> | --objects id1,id2,... | --all] [--json]
|
|
19304
|
+
captable-ai objects revert --dry-run [<object-id> | --objects id1,id2,... | --objects-file <path|-> | --all] [--json]
|
|
19223
19305
|
captable-ai objects revert <object-id> [--yes] [--json]
|
|
19224
19306
|
captable-ai objects revert --objects id1,id2,... [--yes] [--json]
|
|
19307
|
+
captable-ai objects revert --objects-file <path|-> [--yes] [--json]
|
|
19225
19308
|
captable-ai objects revert --all [--yes] [--json]
|
|
19226
19309
|
|
|
19227
19310
|
Previews or reverts draft objects using the backend dependency closure. Preview
|
|
19228
19311
|
returns selected IDs, final IDs, auto-included IDs, invalid IDs, dependency
|
|
19229
19312
|
reasons, blockers, and the applyObjectIds value to use for execution.
|
|
19230
19313
|
|
|
19231
|
-
Execution requires explicit final IDs (<object-id
|
|
19314
|
+
Execution requires explicit final IDs (<object-id>, --objects or --objects-file) or --all.
|
|
19232
19315
|
Use IDs from preview.applyObjectIds when applyObjectIds is not null. Revert-all
|
|
19233
19316
|
prompts for confirmation unless --yes or --json is set.
|
|
19234
19317
|
|
|
19235
19318
|
Options:
|
|
19236
19319
|
--dry-run Preview mode \u2014 no write
|
|
19237
19320
|
--objects IDS Revert specific final object IDs (comma-separated)
|
|
19321
|
+
--objects-file F Read the IDs from a file, one per line (- for stdin); for long lists
|
|
19238
19322
|
--all Revert all pending drafts for the active company
|
|
19239
19323
|
--yes Skip the confirmation prompt (for scripts)
|
|
19240
19324
|
|
|
@@ -19296,14 +19380,14 @@ ${HELP_SCHEMAS_GET}`;
|
|
|
19296
19380
|
var HELP_COMMIT = `commit \u2014 Commit pending drafts to the cap table
|
|
19297
19381
|
|
|
19298
19382
|
Usage:
|
|
19299
|
-
captable-ai commit --dry-run [--objects id1,id2,...] [--json]
|
|
19300
|
-
captable-ai commit (--objects id1,id2,... | --all) [--json]
|
|
19383
|
+
captable-ai commit --dry-run [--objects id1,id2,... | --objects-file <path|->] [--json]
|
|
19384
|
+
captable-ai commit (--objects id1,id2,... | --objects-file <path|-> | --all) [--json]
|
|
19301
19385
|
|
|
19302
19386
|
Previews or commits pending draft objects using the backend dependency closure.
|
|
19303
19387
|
Preview returns selected IDs, final IDs, auto-included IDs, invalid IDs,
|
|
19304
19388
|
dependency reasons, blockers, and the applyObjectIds value to use for execution.
|
|
19305
19389
|
|
|
19306
|
-
Execution requires explicit final IDs (--objects) or --all. Use IDs from
|
|
19390
|
+
Execution requires explicit final IDs (--objects or --objects-file) or --all. Use IDs from
|
|
19307
19391
|
preview.applyObjectIds when applyObjectIds is not null. Exits with
|
|
19308
19392
|
NOTHING_TO_COMMIT if no drafts exist.
|
|
19309
19393
|
|
|
@@ -19313,6 +19397,8 @@ Options:
|
|
|
19313
19397
|
--objects id1,id2,... Commit only specific object IDs (comma-separated). The server
|
|
19314
19398
|
validates that all transitive dependencies are satisfied.
|
|
19315
19399
|
Note: IDs containing commas are not supported.
|
|
19400
|
+
--objects-file F Read the IDs from a file, one per line (- for stdin), for lists too
|
|
19401
|
+
long for one command line. The dry run writes one when it must.
|
|
19316
19402
|
|
|
19317
19403
|
Global:
|
|
19318
19404
|
--json Machine-readable JSON output
|
|
@@ -19537,6 +19623,89 @@ Environment:
|
|
|
19537
19623
|
Global:
|
|
19538
19624
|
--json Machine-readable JSON output`;
|
|
19539
19625
|
|
|
19626
|
+
// src/selection-input.ts
|
|
19627
|
+
import { createHash, randomBytes } from "crypto";
|
|
19628
|
+
import { chmod, mkdir as mkdir2, readFile as readFile2, rename, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
19629
|
+
import { homedir as homedir2 } from "os";
|
|
19630
|
+
import { join as join2 } from "path";
|
|
19631
|
+
var CLI_SELECTION_MAX_BYTES = 1e5;
|
|
19632
|
+
function isPlainId(id) {
|
|
19633
|
+
return id.length > 0 && id === id.trim();
|
|
19634
|
+
}
|
|
19635
|
+
function canSelectInline(objectIds) {
|
|
19636
|
+
if (!objectIds.every((id) => isPlainId(id) && !id.includes(","))) return false;
|
|
19637
|
+
const joined = objectIds.join(",");
|
|
19638
|
+
const quotes = joined.split("'").length - 1;
|
|
19639
|
+
return Buffer.byteLength(`--objects=${joined}`, "utf8") + 2 + quotes * 3 <= CLI_SELECTION_MAX_BYTES;
|
|
19640
|
+
}
|
|
19641
|
+
function canSelectFromFile(objectIds) {
|
|
19642
|
+
return objectIds.every((id) => isPlainId(id) && !/[\r\n]/.test(id));
|
|
19643
|
+
}
|
|
19644
|
+
function parseObjectIdLines(text) {
|
|
19645
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
19646
|
+
}
|
|
19647
|
+
var processSelectionReader = {
|
|
19648
|
+
readFile: (path) => readFile2(path, "utf8"),
|
|
19649
|
+
stdinIsTerminal: () => process.stdin.isTTY === true,
|
|
19650
|
+
// Reads to end of input, like any `-` filter: a pipe that never closes waits, as `cat -` does.
|
|
19651
|
+
readStdin: async () => {
|
|
19652
|
+
const chunks = [];
|
|
19653
|
+
for await (const chunk of process.stdin) {
|
|
19654
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
19655
|
+
}
|
|
19656
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
19657
|
+
}
|
|
19658
|
+
};
|
|
19659
|
+
async function readSelectionIds(source, reader = processSelectionReader) {
|
|
19660
|
+
let text;
|
|
19661
|
+
try {
|
|
19662
|
+
if (source === "-") {
|
|
19663
|
+
if (reader.stdinIsTerminal()) {
|
|
19664
|
+
return {
|
|
19665
|
+
ok: false,
|
|
19666
|
+
message: "--objects-file - reads ids from stdin, but nothing is piped in."
|
|
19667
|
+
};
|
|
19668
|
+
}
|
|
19669
|
+
text = await reader.readStdin();
|
|
19670
|
+
} else {
|
|
19671
|
+
text = await reader.readFile(source);
|
|
19672
|
+
}
|
|
19673
|
+
} catch (error51) {
|
|
19674
|
+
const reason = error51 instanceof Error ? error51.message : String(error51);
|
|
19675
|
+
return { ok: false, message: `Could not read --objects-file ${source}: ${reason}` };
|
|
19676
|
+
}
|
|
19677
|
+
const objectIds = parseObjectIdLines(text);
|
|
19678
|
+
if (objectIds.length === 0) {
|
|
19679
|
+
return {
|
|
19680
|
+
ok: false,
|
|
19681
|
+
message: `The --objects-file ${source === "-" ? "input" : source} must include at least one object ID.`
|
|
19682
|
+
};
|
|
19683
|
+
}
|
|
19684
|
+
return { ok: true, objectIds };
|
|
19685
|
+
}
|
|
19686
|
+
function selectionsDir() {
|
|
19687
|
+
return join2(homedir2(), ".captable-ai", "selections");
|
|
19688
|
+
}
|
|
19689
|
+
async function writeSelectionFile(name, objectIds) {
|
|
19690
|
+
if (!canSelectFromFile(objectIds)) return null;
|
|
19691
|
+
const dir = selectionsDir();
|
|
19692
|
+
const digest = createHash("sha256").update(JSON.stringify(objectIds)).digest("hex").slice(0, 32);
|
|
19693
|
+
const path = join2(dir, `${name.replace(/[^A-Za-z0-9._-]/g, "_")}-${digest}.txt`);
|
|
19694
|
+
const temp = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
19695
|
+
try {
|
|
19696
|
+
await mkdir2(dir, { recursive: true, mode: 448 });
|
|
19697
|
+
await chmod(dir, 448);
|
|
19698
|
+
await writeFile2(temp, `${objectIds.join("\n")}
|
|
19699
|
+
`, { mode: 384, flag: "wx" });
|
|
19700
|
+
await chmod(temp, 384);
|
|
19701
|
+
await rename(temp, path);
|
|
19702
|
+
return path;
|
|
19703
|
+
} catch {
|
|
19704
|
+
await rm2(temp, { force: true }).catch(() => void 0);
|
|
19705
|
+
return null;
|
|
19706
|
+
}
|
|
19707
|
+
}
|
|
19708
|
+
|
|
19540
19709
|
// src/commands/draft-operations.ts
|
|
19541
19710
|
function parseObjectIdsList(raw) {
|
|
19542
19711
|
if (raw === void 0) return void 0;
|
|
@@ -19545,6 +19714,23 @@ function parseObjectIdsList(raw) {
|
|
|
19545
19714
|
function hasObjectIds(raw, objectIds) {
|
|
19546
19715
|
return raw !== void 0 && (objectIds?.length ?? 0) > 0;
|
|
19547
19716
|
}
|
|
19717
|
+
async function resolveObjectsInput(args, otherSelectorMessage = null, reader = processSelectionReader) {
|
|
19718
|
+
const objectsRaw = getFlagValue(args, "objects");
|
|
19719
|
+
const fileSource = getFlagValue(args, "objects-file");
|
|
19720
|
+
if (objectsRaw !== void 0 && fileSource !== void 0) {
|
|
19721
|
+
return { kind: "invalid", message: "Specify either --objects or --objects-file, not both." };
|
|
19722
|
+
}
|
|
19723
|
+
if (fileSource !== void 0) {
|
|
19724
|
+
if (otherSelectorMessage !== null) return { kind: "invalid", message: otherSelectorMessage };
|
|
19725
|
+
const read = await readSelectionIds(fileSource, reader);
|
|
19726
|
+
return read.ok ? { kind: "ids", objectIds: read.objectIds } : { kind: "invalid", message: read.message };
|
|
19727
|
+
}
|
|
19728
|
+
const objectIds = parseObjectIdsList(objectsRaw);
|
|
19729
|
+
if (objectsRaw !== void 0 && !hasObjectIds(objectsRaw, objectIds)) {
|
|
19730
|
+
return { kind: "invalid", message: "The --objects flag must include at least one object ID." };
|
|
19731
|
+
}
|
|
19732
|
+
return objectIds === void 0 ? { kind: "none" } : { kind: "ids", objectIds };
|
|
19733
|
+
}
|
|
19548
19734
|
function operationPastTense(operation) {
|
|
19549
19735
|
return operation === "commit" ? "committed" : "reverted";
|
|
19550
19736
|
}
|
|
@@ -19595,7 +19781,7 @@ function canApplyRevertAllFallback(data, operation) {
|
|
|
19595
19781
|
function shellQuote(value) {
|
|
19596
19782
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
19597
19783
|
}
|
|
19598
|
-
function printDraftOperationPreview(data, options) {
|
|
19784
|
+
async function printDraftOperationPreview(data, options) {
|
|
19599
19785
|
const applied = operationPastTense(options.operation);
|
|
19600
19786
|
const objectCount = data.finalIds.length;
|
|
19601
19787
|
if (objectCount === 0) {
|
|
@@ -19630,10 +19816,29 @@ Run \`${options.command} --all\` to revert all pending drafts.
|
|
|
19630
19816
|
printLine("\nResolve the reported issues before applying this preview.\n");
|
|
19631
19817
|
return;
|
|
19632
19818
|
}
|
|
19633
|
-
|
|
19634
|
-
|
|
19635
|
-
Run \`${
|
|
19819
|
+
if (data.applyObjectIds === null) {
|
|
19820
|
+
printLine(`
|
|
19821
|
+
Run \`${options.command} --all\` to apply this preview.
|
|
19636
19822
|
`);
|
|
19823
|
+
return;
|
|
19824
|
+
}
|
|
19825
|
+
if (canSelectInline(data.applyObjectIds)) {
|
|
19826
|
+
printLine(
|
|
19827
|
+
`
|
|
19828
|
+
Run \`${options.command} --objects=${shellQuote(data.applyObjectIds.join(","))}\` to apply this preview.
|
|
19829
|
+
`
|
|
19830
|
+
);
|
|
19831
|
+
return;
|
|
19832
|
+
}
|
|
19833
|
+
const file2 = await writeSelectionFile(`${options.operation}-apply`, data.applyObjectIds);
|
|
19834
|
+
printLine(
|
|
19835
|
+
file2 ? `
|
|
19836
|
+
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}.
|
|
19837
|
+
Run \`${options.command} --objects-file ${shellQuote(file2)}\` to apply this preview.
|
|
19838
|
+
` : `
|
|
19839
|
+
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.
|
|
19840
|
+
`
|
|
19841
|
+
);
|
|
19637
19842
|
}
|
|
19638
19843
|
|
|
19639
19844
|
// src/commands/commit.ts
|
|
@@ -19643,8 +19848,8 @@ async function runCommit(args, rawArgv) {
|
|
|
19643
19848
|
`);
|
|
19644
19849
|
process.exit(0);
|
|
19645
19850
|
}
|
|
19646
|
-
validateFlags(args, { flags: ["objects"], booleans: ["all", "dry-run"] });
|
|
19647
|
-
const USAGE2 = "captable-ai commit --dry-run [--objects id1,id2,...] [--json]\ncaptable-ai commit (--objects id1,id2,... | --all) [--json]";
|
|
19851
|
+
validateFlags(args, { flags: ["objects", "objects-file"], booleans: ["all", "dry-run"] });
|
|
19852
|
+
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]";
|
|
19648
19853
|
if (args.positional.length > 0) {
|
|
19649
19854
|
exitWithUsage(
|
|
19650
19855
|
`Unexpected argument: ${args.positional[0]}. This command takes no positional arguments.`,
|
|
@@ -19653,14 +19858,14 @@ async function runCommit(args, rawArgv) {
|
|
|
19653
19858
|
}
|
|
19654
19859
|
const dryRun = hasFlag(args, "dry-run");
|
|
19655
19860
|
const all = hasFlag(args, "all");
|
|
19656
|
-
const
|
|
19657
|
-
|
|
19658
|
-
|
|
19659
|
-
|
|
19660
|
-
|
|
19661
|
-
|
|
19861
|
+
const objectsInput = await resolveObjectsInput(
|
|
19862
|
+
args,
|
|
19863
|
+
all ? "Specify either --objects (or --objects-file) or --all, not both." : null
|
|
19864
|
+
);
|
|
19865
|
+
if (objectsInput.kind === "invalid") exitWithUsage(objectsInput.message, USAGE2);
|
|
19866
|
+
const objectIds = objectsInput.kind === "ids" ? objectsInput.objectIds : void 0;
|
|
19662
19867
|
if (all && objectIds) {
|
|
19663
|
-
exitWithUsage("Specify either --objects or --all, not both.", USAGE2);
|
|
19868
|
+
exitWithUsage("Specify either --objects (or --objects-file) or --all, not both.", USAGE2);
|
|
19664
19869
|
}
|
|
19665
19870
|
if (dryRun) {
|
|
19666
19871
|
const portalId2 = await getActivePortalId();
|
|
@@ -19679,7 +19884,7 @@ async function runCommit(args, rawArgv) {
|
|
|
19679
19884
|
printJson({ data: data2 });
|
|
19680
19885
|
return;
|
|
19681
19886
|
}
|
|
19682
|
-
printDraftOperationPreview(data2, { command: "captable-ai commit", operation: "commit" });
|
|
19887
|
+
await printDraftOperationPreview(data2, { command: "captable-ai commit", operation: "commit" });
|
|
19683
19888
|
return;
|
|
19684
19889
|
}
|
|
19685
19890
|
const selection = resolveDraftOperationSelection(
|
|
@@ -20404,12 +20609,12 @@ async function runDocumentsRestartExtraction(args, rawArgv) {
|
|
|
20404
20609
|
|
|
20405
20610
|
// src/jobs/tracker.ts
|
|
20406
20611
|
import { randomUUID } from "crypto";
|
|
20407
|
-
import { appendFile, mkdir as
|
|
20408
|
-
import { homedir as
|
|
20409
|
-
import { join as
|
|
20410
|
-
var CONFIG_DIR2 =
|
|
20411
|
-
var JOBS_FILE =
|
|
20412
|
-
var LOCK_DIR =
|
|
20612
|
+
import { appendFile, mkdir as mkdir3, readFile as readFile3, rename as rename2, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
20613
|
+
import { homedir as homedir3 } from "os";
|
|
20614
|
+
import { join as join3 } from "path";
|
|
20615
|
+
var CONFIG_DIR2 = join3(homedir3(), ".captable-ai");
|
|
20616
|
+
var JOBS_FILE = join3(CONFIG_DIR2, "jobs.jsonl");
|
|
20617
|
+
var LOCK_DIR = join3(CONFIG_DIR2, "jobs.lock");
|
|
20413
20618
|
var VALID_JOB_STATUSES = /* @__PURE__ */ new Set(["submitted", "running", "complete", "failed"]);
|
|
20414
20619
|
var VALID_JOB_KINDS = /* @__PURE__ */ new Set(["extraction"]);
|
|
20415
20620
|
function isRecord3(v) {
|
|
@@ -20420,7 +20625,7 @@ function isJobEntry(value) {
|
|
|
20420
20625
|
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);
|
|
20421
20626
|
}
|
|
20422
20627
|
async function ensureDir() {
|
|
20423
|
-
await
|
|
20628
|
+
await mkdir3(CONFIG_DIR2, { recursive: true, mode: 448 });
|
|
20424
20629
|
}
|
|
20425
20630
|
function sleep(ms) {
|
|
20426
20631
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -20437,7 +20642,7 @@ async function withJobsLock(fn) {
|
|
|
20437
20642
|
const startedAt = Date.now();
|
|
20438
20643
|
for (; ; ) {
|
|
20439
20644
|
try {
|
|
20440
|
-
await
|
|
20645
|
+
await mkdir3(LOCK_DIR, { mode: 448 });
|
|
20441
20646
|
break;
|
|
20442
20647
|
} catch (err) {
|
|
20443
20648
|
if (!isErrno(err, "EEXIST")) throw err;
|
|
@@ -20450,7 +20655,7 @@ async function withJobsLock(fn) {
|
|
|
20450
20655
|
try {
|
|
20451
20656
|
return await fn();
|
|
20452
20657
|
} finally {
|
|
20453
|
-
await
|
|
20658
|
+
await rm3(LOCK_DIR, { recursive: true, force: true });
|
|
20454
20659
|
}
|
|
20455
20660
|
}
|
|
20456
20661
|
function generateJobId() {
|
|
@@ -20465,7 +20670,7 @@ async function appendJob(entry) {
|
|
|
20465
20670
|
async function listJobs(filter) {
|
|
20466
20671
|
let raw;
|
|
20467
20672
|
try {
|
|
20468
|
-
raw = await
|
|
20673
|
+
raw = await readFile3(JOBS_FILE, "utf8");
|
|
20469
20674
|
} catch (err) {
|
|
20470
20675
|
if (!isErrno(err, "ENOENT")) throw err;
|
|
20471
20676
|
return [];
|
|
@@ -20526,10 +20731,10 @@ async function pruneJobs(before) {
|
|
|
20526
20731
|
async function atomicWriteJobs(content) {
|
|
20527
20732
|
const tmp = `${JOBS_FILE}.${process.pid}.${Date.now()}.tmp`;
|
|
20528
20733
|
try {
|
|
20529
|
-
await
|
|
20530
|
-
await
|
|
20734
|
+
await writeFile3(tmp, content, { mode: 384 });
|
|
20735
|
+
await rename2(tmp, JOBS_FILE);
|
|
20531
20736
|
} catch (err) {
|
|
20532
|
-
await
|
|
20737
|
+
await rm3(tmp, { force: true });
|
|
20533
20738
|
throw err;
|
|
20534
20739
|
}
|
|
20535
20740
|
}
|
|
@@ -20706,12 +20911,12 @@ function flagMissingIds(missingIds) {
|
|
|
20706
20911
|
}
|
|
20707
20912
|
|
|
20708
20913
|
// src/commands/documents-upload.ts
|
|
20709
|
-
import { createHash } from "crypto";
|
|
20710
|
-
import { readFile as
|
|
20914
|
+
import { createHash as createHash2 } from "crypto";
|
|
20915
|
+
import { readFile as readFile4, stat } from "fs/promises";
|
|
20711
20916
|
import { basename } from "path";
|
|
20712
20917
|
async function hashFile(path) {
|
|
20713
|
-
const data = await
|
|
20714
|
-
return
|
|
20918
|
+
const data = await readFile4(path);
|
|
20919
|
+
return createHash2("sha256").update(data).digest("hex");
|
|
20715
20920
|
}
|
|
20716
20921
|
async function runDocumentsUpload(args, rawArgv) {
|
|
20717
20922
|
if (rawArgv.includes("--help") || rawArgv.includes("-h")) {
|
|
@@ -20767,7 +20972,7 @@ async function runDocumentsUpload(args, rawArgv) {
|
|
|
20767
20972
|
}
|
|
20768
20973
|
for (const { doc, filePath } of toUpload) {
|
|
20769
20974
|
if (!isJsonMode()) printLine(` \u2191 Uploading ${doc.name}...`);
|
|
20770
|
-
const fileData = await
|
|
20975
|
+
const fileData = await readFile4(filePath);
|
|
20771
20976
|
const putRes = await fetch(doc.presignedUrl, {
|
|
20772
20977
|
method: "PUT",
|
|
20773
20978
|
headers: { "Content-Type": doc.contentType },
|
|
@@ -20818,11 +21023,11 @@ ${successCount}/${results.length} document(s) confirmed.`);
|
|
|
20818
21023
|
}
|
|
20819
21024
|
|
|
20820
21025
|
// src/commands/feedback.ts
|
|
20821
|
-
import { appendFile as appendFile2, mkdir as
|
|
20822
|
-
import { homedir as
|
|
20823
|
-
import { join as
|
|
20824
|
-
var CONFIG_DIR3 =
|
|
20825
|
-
var FEEDBACK_FILE =
|
|
21026
|
+
import { appendFile as appendFile2, mkdir as mkdir4, readFile as readFile5, rename as rename3, rm as rm4, writeFile as writeFile4 } from "fs/promises";
|
|
21027
|
+
import { homedir as homedir4 } from "os";
|
|
21028
|
+
import { join as join4 } from "path";
|
|
21029
|
+
var CONFIG_DIR3 = join4(homedir4(), ".captable-ai");
|
|
21030
|
+
var FEEDBACK_FILE = join4(CONFIG_DIR3, "feedback.jsonl");
|
|
20826
21031
|
function isErrno2(err, code) {
|
|
20827
21032
|
return typeof err === "object" && err !== null && "code" in err && err.code === code;
|
|
20828
21033
|
}
|
|
@@ -20891,7 +21096,7 @@ async function runFeedback(args, rawArgv) {
|
|
|
20891
21096
|
if (!text.trim()) {
|
|
20892
21097
|
exitWithUsage("Feedback text cannot be empty.", 'Usage: captable-ai feedback "<message>"');
|
|
20893
21098
|
}
|
|
20894
|
-
await
|
|
21099
|
+
await mkdir4(CONFIG_DIR3, { recursive: true, mode: 448 });
|
|
20895
21100
|
const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), text: text.trim(), sent: false };
|
|
20896
21101
|
await appendFile2(FEEDBACK_FILE, `${JSON.stringify(entry)}
|
|
20897
21102
|
`, { mode: 384 });
|
|
@@ -20974,14 +21179,14 @@ async function runFeedbackSend() {
|
|
|
20974
21179
|
);
|
|
20975
21180
|
}
|
|
20976
21181
|
const updated = entries.map((e) => e.sent ? e : { ...e, sent: true });
|
|
20977
|
-
await
|
|
21182
|
+
await mkdir4(CONFIG_DIR3, { recursive: true, mode: 448 });
|
|
20978
21183
|
const tmp = `${FEEDBACK_FILE}.${process.pid}.${Date.now()}.tmp`;
|
|
20979
21184
|
try {
|
|
20980
|
-
await
|
|
21185
|
+
await writeFile4(tmp, updated.map((e) => `${JSON.stringify(e)}
|
|
20981
21186
|
`).join(""), { mode: 384 });
|
|
20982
|
-
await
|
|
21187
|
+
await rename3(tmp, FEEDBACK_FILE);
|
|
20983
21188
|
} catch (err) {
|
|
20984
|
-
await
|
|
21189
|
+
await rm4(tmp, { force: true });
|
|
20985
21190
|
throw err;
|
|
20986
21191
|
}
|
|
20987
21192
|
if (isJsonMode()) {
|
|
@@ -20993,7 +21198,7 @@ async function runFeedbackSend() {
|
|
|
20993
21198
|
async function readEntries() {
|
|
20994
21199
|
let raw;
|
|
20995
21200
|
try {
|
|
20996
|
-
raw = await
|
|
21201
|
+
raw = await readFile5(FEEDBACK_FILE, "utf8");
|
|
20997
21202
|
} catch (err) {
|
|
20998
21203
|
if (!isErrno2(err, "ENOENT")) {
|
|
20999
21204
|
exitWithError(
|
|
@@ -21211,7 +21416,7 @@ async function fetchTrackedDocuments(portalId, assetIds) {
|
|
|
21211
21416
|
|
|
21212
21417
|
// src/commands/login.ts
|
|
21213
21418
|
import { execFile } from "child_process";
|
|
21214
|
-
import { randomBytes, timingSafeEqual } from "crypto";
|
|
21419
|
+
import { randomBytes as randomBytes2, timingSafeEqual } from "crypto";
|
|
21215
21420
|
import { createServer } from "http";
|
|
21216
21421
|
import { platform } from "os";
|
|
21217
21422
|
import { createInterface } from "readline";
|
|
@@ -21324,7 +21529,7 @@ function tryParseLoginPaste(line, expectedState) {
|
|
|
21324
21529
|
async function runLogin(args) {
|
|
21325
21530
|
validateFlags(args, { flags: [], booleans: [] });
|
|
21326
21531
|
const jsonMode2 = isJsonMode();
|
|
21327
|
-
const state =
|
|
21532
|
+
const state = randomBytes2(16).toString("hex");
|
|
21328
21533
|
let settled = false;
|
|
21329
21534
|
let timeoutId;
|
|
21330
21535
|
let rl;
|
|
@@ -22335,16 +22540,19 @@ async function runObjectsRevert(args, rawArgv) {
|
|
|
22335
22540
|
`);
|
|
22336
22541
|
process.exit(0);
|
|
22337
22542
|
}
|
|
22338
|
-
validateFlags(args, { flags: ["objects"], booleans: ["all", "dry-run", "yes"] });
|
|
22543
|
+
validateFlags(args, { flags: ["objects", "objects-file"], booleans: ["all", "dry-run", "yes"] });
|
|
22339
22544
|
const objectIdRaw = args.positional[0];
|
|
22340
22545
|
const objectId = objectIdRaw?.trim();
|
|
22341
22546
|
const all = hasFlag(args, "all");
|
|
22342
22547
|
const dryRun = hasFlag(args, "dry-run");
|
|
22343
22548
|
const yes = hasFlag(args, "yes");
|
|
22344
|
-
const
|
|
22345
|
-
const
|
|
22346
|
-
|
|
22347
|
-
|
|
22549
|
+
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]";
|
|
22550
|
+
const objectsInput = await resolveObjectsInput(
|
|
22551
|
+
args,
|
|
22552
|
+
objectIdRaw !== void 0 || all ? "Specify only one of an object ID, --objects (or --objects-file), or --all." : null
|
|
22553
|
+
);
|
|
22554
|
+
if (objectsInput.kind === "invalid") exitWithUsage(objectsInput.message, USAGE2);
|
|
22555
|
+
const objectIdsFromFlag = objectsInput.kind === "ids" ? objectsInput.objectIds : void 0;
|
|
22348
22556
|
if (args.positional.length > 1) {
|
|
22349
22557
|
exitWithUsage(
|
|
22350
22558
|
`Too many arguments: expected one object ID, got ${args.positional.length}.`,
|
|
@@ -22354,12 +22562,12 @@ async function runObjectsRevert(args, rawArgv) {
|
|
|
22354
22562
|
if (objectIdRaw !== void 0 && !objectId) {
|
|
22355
22563
|
exitWithUsage("Object ID must be a non-empty string.", USAGE2);
|
|
22356
22564
|
}
|
|
22357
|
-
if (objectIdsRaw !== void 0 && !hasExplicitObjectIds) {
|
|
22358
|
-
exitWithUsage("The --objects flag must include at least one object ID.", USAGE2);
|
|
22359
|
-
}
|
|
22360
22565
|
const selectionSources = [Boolean(objectId), Boolean(objectIdsFromFlag), all].filter(Boolean);
|
|
22361
22566
|
if (selectionSources.length > 1) {
|
|
22362
|
-
exitWithUsage(
|
|
22567
|
+
exitWithUsage(
|
|
22568
|
+
"Specify only one of an object ID, --objects (or --objects-file), or --all.",
|
|
22569
|
+
USAGE2
|
|
22570
|
+
);
|
|
22363
22571
|
}
|
|
22364
22572
|
const objectIds = objectId ? [objectId] : objectIdsFromFlag;
|
|
22365
22573
|
if (dryRun) {
|
|
@@ -22378,7 +22586,7 @@ async function runObjectsRevert(args, rawArgv) {
|
|
|
22378
22586
|
printJson({ data: res2.data });
|
|
22379
22587
|
return;
|
|
22380
22588
|
}
|
|
22381
|
-
printDraftOperationPreview(res2.data, {
|
|
22589
|
+
await printDraftOperationPreview(res2.data, {
|
|
22382
22590
|
command: "captable-ai objects revert",
|
|
22383
22591
|
operation: "revert"
|
|
22384
22592
|
});
|
|
@@ -23031,7 +23239,17 @@ async function runImportStart(args) {
|
|
|
23031
23239
|
function scopeFlag(ids) {
|
|
23032
23240
|
return ids && ids.length > 0 ? ` --s3-asset-ids ${ids.join(",")}` : "";
|
|
23033
23241
|
}
|
|
23034
|
-
function
|
|
23242
|
+
function selectionTooLong(action) {
|
|
23243
|
+
return (action.id === "preview_commit" || action.id === "confirm_commit") && action.objectIds !== void 0 && !canSelectInline(action.objectIds);
|
|
23244
|
+
}
|
|
23245
|
+
function importStatusCommand(action, sessionId, selectionFile) {
|
|
23246
|
+
if (selectionTooLong(action) && selectionFile === null) {
|
|
23247
|
+
return {
|
|
23248
|
+
id: action.id,
|
|
23249
|
+
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.`,
|
|
23250
|
+
required: action.required
|
|
23251
|
+
};
|
|
23252
|
+
}
|
|
23035
23253
|
const command = (() => {
|
|
23036
23254
|
switch (action.id) {
|
|
23037
23255
|
case "upload_documents":
|
|
@@ -23048,12 +23266,20 @@ function importStatusCommand(action, sessionId) {
|
|
|
23048
23266
|
return "captable-ai documents list";
|
|
23049
23267
|
case "review_failures":
|
|
23050
23268
|
return action.statusFilter ? `captable-ai documents list --status ${action.statusFilter}` : "captable-ai documents list";
|
|
23051
|
-
case "review_drafts":
|
|
23052
|
-
return "captable-ai objects list --status draft --all";
|
|
23269
|
+
case "review_drafts": {
|
|
23270
|
+
if (!action.objectIds) return "captable-ai objects list --status draft --all";
|
|
23271
|
+
const [onlyDocument, ...moreDocuments] = action.s3AssetIds ?? [];
|
|
23272
|
+
if (onlyDocument !== void 0 && moreDocuments.length === 0) {
|
|
23273
|
+
return `captable-ai objects list --status draft --document ${shellQuote(onlyDocument)} --all`;
|
|
23274
|
+
}
|
|
23275
|
+
const reviewable = (action.reviewObjectIds ?? []).slice(0, IMPORT_REVIEW_MAX_IDS);
|
|
23276
|
+
return `captable-ai objects list --status draft --ids=${shellQuote(reviewable.join(","))}`;
|
|
23277
|
+
}
|
|
23053
23278
|
case "preview_commit":
|
|
23054
|
-
return "captable-ai commit --dry-run";
|
|
23279
|
+
if (!action.objectIds) return "captable-ai commit --dry-run";
|
|
23280
|
+
return selectionTooLong(action) && selectionFile !== null ? `captable-ai commit --dry-run --objects-file ${shellQuote(selectionFile)}` : `captable-ai commit --dry-run --objects=${shellQuote(action.objectIds.join(","))}`;
|
|
23055
23281
|
case "confirm_commit":
|
|
23056
|
-
return "captable-ai commit --all";
|
|
23282
|
+
return action.objectIds ? "captable-ai commit --objects <final IDs from the dry run>" : "captable-ai commit --all";
|
|
23057
23283
|
default: {
|
|
23058
23284
|
const unhandled = action.id;
|
|
23059
23285
|
throw new Error(`Unhandled import status action: ${String(unhandled)}`);
|
|
@@ -23063,35 +23289,40 @@ function importStatusCommand(action, sessionId) {
|
|
|
23063
23289
|
const id = action.id === "upload_documents" ? "upload_cli" : action.id;
|
|
23064
23290
|
return {
|
|
23065
23291
|
id,
|
|
23066
|
-
label: action.label,
|
|
23292
|
+
label: action.id === "review_drafts" && reviewIsCut(action) ? `${action.label} (first ${IMPORT_REVIEW_MAX_IDS} of ${action.reviewObjectIds?.length ?? 0})` : action.label,
|
|
23067
23293
|
required: action.required,
|
|
23068
23294
|
command,
|
|
23069
23295
|
...action.id === "review_failures" && action.s3AssetIds && { s3AssetIds: action.s3AssetIds }
|
|
23070
23296
|
};
|
|
23071
23297
|
}
|
|
23072
|
-
var
|
|
23073
|
-
async function
|
|
23298
|
+
var DRAFT_SCOPE_MAX_PAGES = 100;
|
|
23299
|
+
async function readImportDraftScope(portalId, documentIds) {
|
|
23074
23300
|
if (!portalId) return null;
|
|
23075
23301
|
try {
|
|
23076
|
-
|
|
23302
|
+
const drafts = [];
|
|
23077
23303
|
let cursor;
|
|
23078
|
-
for (let page = 0; page <
|
|
23304
|
+
for (let page = 0; page < DRAFT_SCOPE_MAX_PAGES; page += 1) {
|
|
23079
23305
|
const res = await apiRequest(
|
|
23080
23306
|
`/${encodeURIComponent(portalId)}/objects`,
|
|
23081
23307
|
{
|
|
23082
|
-
query: { status: "draft", ...cursor ? { cursor } : {} },
|
|
23308
|
+
query: { status: "draft", limit: 250, ...cursor ? { cursor } : {} },
|
|
23083
23309
|
// Optional read: throw (caught below) instead of exiting the process on a failure.
|
|
23084
23310
|
softFail: true
|
|
23085
23311
|
}
|
|
23086
23312
|
);
|
|
23087
23313
|
const objects = res.data?.objects;
|
|
23088
23314
|
if (!Array.isArray(objects)) return null;
|
|
23089
|
-
|
|
23090
|
-
(
|
|
23091
|
-
|
|
23092
|
-
|
|
23315
|
+
for (const obj of objects) {
|
|
23316
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return null;
|
|
23317
|
+
drafts.push({
|
|
23318
|
+
id: Reflect.get(obj, "id"),
|
|
23319
|
+
type: Reflect.get(obj, "type"),
|
|
23320
|
+
subtype: Reflect.get(obj, "subtype"),
|
|
23321
|
+
..."documentId" in obj ? { documentId: Reflect.get(obj, "documentId") } : {}
|
|
23322
|
+
});
|
|
23323
|
+
}
|
|
23324
|
+
if (!res.meta?.hasMore) return partitionImportDrafts(drafts, documentIds);
|
|
23093
23325
|
const next = res.meta?.cursor;
|
|
23094
|
-
if (!res.meta?.hasMore) return count;
|
|
23095
23326
|
if (!next || next === cursor) return null;
|
|
23096
23327
|
cursor = next;
|
|
23097
23328
|
}
|
|
@@ -23114,12 +23345,19 @@ async function runImportStatus(args) {
|
|
|
23114
23345
|
);
|
|
23115
23346
|
const portalDocuments = response.data?.processing?.documents ?? [];
|
|
23116
23347
|
const documents = documentsForImportSession(portalDocuments, response.data?.session?.createdAt);
|
|
23348
|
+
const documentIds = documents.flatMap(
|
|
23349
|
+
(document) => typeof document.s3AssetId === "string" ? [document.s3AssetId] : []
|
|
23350
|
+
);
|
|
23117
23351
|
const documentPlan = planImportStatus({
|
|
23118
23352
|
documents,
|
|
23119
23353
|
sessionState: response.data?.session?.state,
|
|
23120
23354
|
scopeUnknown: importSessionScopeUnknown(portalDocuments)
|
|
23121
23355
|
});
|
|
23122
|
-
const plan = documentPlan.phase === "complete" ?
|
|
23356
|
+
const plan = documentPlan.phase === "complete" ? applyImportDrafts(
|
|
23357
|
+
documentPlan,
|
|
23358
|
+
await readImportDraftScope(response.data?.session?.portalId, documentIds),
|
|
23359
|
+
documentIds
|
|
23360
|
+
) : documentPlan;
|
|
23123
23361
|
const { summary } = plan;
|
|
23124
23362
|
if (plan.shouldComplete) {
|
|
23125
23363
|
const completed = await apiRequest(
|
|
@@ -23133,7 +23371,11 @@ async function runImportStatus(args) {
|
|
|
23133
23371
|
response.data.session = { ...response.data.session, ...completed.data.session };
|
|
23134
23372
|
}
|
|
23135
23373
|
}
|
|
23136
|
-
const
|
|
23374
|
+
const longSelection = plan.actions.find(selectionTooLong)?.objectIds;
|
|
23375
|
+
const selectionFile = longSelection ? await writeSelectionFile(`import-${sessionId}`, longSelection) : null;
|
|
23376
|
+
const nextActions = plan.actions.map(
|
|
23377
|
+
(action) => importStatusCommand(action, sessionId, selectionFile)
|
|
23378
|
+
);
|
|
23137
23379
|
const suggestMonitoring = shouldSuggestBackgroundMonitoring(plan.phase);
|
|
23138
23380
|
const data = {
|
|
23139
23381
|
...response.data,
|
|
@@ -23141,6 +23383,7 @@ async function runImportStatus(args) {
|
|
|
23141
23383
|
phase: plan.phase,
|
|
23142
23384
|
summary,
|
|
23143
23385
|
nextActions,
|
|
23386
|
+
...plan.unrelatedDrafts && { unrelatedDrafts: true },
|
|
23144
23387
|
...suggestMonitoring && { monitoringHint: BACKGROUND_MONITORING_SUGGESTION }
|
|
23145
23388
|
};
|
|
23146
23389
|
if (isJsonMode()) {
|
|
@@ -23165,12 +23408,28 @@ async function runImportStatus(args) {
|
|
|
23165
23408
|
" Some uploads never finished. Re-upload them; checking status again will not complete them."
|
|
23166
23409
|
);
|
|
23167
23410
|
}
|
|
23411
|
+
if (plan.unrelatedDrafts) {
|
|
23412
|
+
printLine(
|
|
23413
|
+
" The portal has other pending drafts that this import did not produce; leave them alone unless asked."
|
|
23414
|
+
);
|
|
23415
|
+
}
|
|
23416
|
+
const scopedCommit = plan.actions.some(
|
|
23417
|
+
(planned) => planned.id === "confirm_commit" && planned.objectIds !== void 0
|
|
23418
|
+
);
|
|
23168
23419
|
for (const action of nextActions) {
|
|
23420
|
+
if (action.command === void 0) {
|
|
23421
|
+
printLine(` Note: ${action.label}`);
|
|
23422
|
+
continue;
|
|
23423
|
+
}
|
|
23169
23424
|
printLine(` Next: ${action.command}`);
|
|
23170
23425
|
if (action.id === "resolve_extraction_scope") printLine(` Note: ${action.label}`);
|
|
23171
23426
|
if (action.id === "check_status" && plan.phase === "empty") {
|
|
23172
23427
|
printLine(` Note: ${action.label}`);
|
|
23173
23428
|
}
|
|
23429
|
+
if (action.id === "confirm_commit" && scopedCommit) printLine(` Note: ${action.label}`);
|
|
23430
|
+
if (action.id === "review_drafts" && action.label.includes("(first ")) {
|
|
23431
|
+
printLine(` Note: ${action.label}`);
|
|
23432
|
+
}
|
|
23174
23433
|
if (action.id === "review_failures" && action.s3AssetIds) {
|
|
23175
23434
|
printLine(` Documents: ${action.s3AssetIds.join(", ")}`);
|
|
23176
23435
|
}
|
|
@@ -23340,11 +23599,11 @@ function formatNumber(n) {
|
|
|
23340
23599
|
}
|
|
23341
23600
|
|
|
23342
23601
|
// src/profiles.ts
|
|
23343
|
-
import { mkdir as
|
|
23344
|
-
import { homedir as
|
|
23345
|
-
import { join as
|
|
23346
|
-
var CONFIG_DIR4 =
|
|
23347
|
-
var PROFILES_FILE =
|
|
23602
|
+
import { mkdir as mkdir5, readFile as readFile6, rename as rename4, rm as rm5, writeFile as writeFile5 } from "fs/promises";
|
|
23603
|
+
import { homedir as homedir5 } from "os";
|
|
23604
|
+
import { join as join5 } from "path";
|
|
23605
|
+
var CONFIG_DIR4 = join5(homedir5(), ".captable-ai");
|
|
23606
|
+
var PROFILES_FILE = join5(CONFIG_DIR4, "profiles.json");
|
|
23348
23607
|
function isRecord9(value) {
|
|
23349
23608
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23350
23609
|
}
|
|
@@ -23372,7 +23631,7 @@ function parseProfiles(raw) {
|
|
|
23372
23631
|
}
|
|
23373
23632
|
async function readProfiles() {
|
|
23374
23633
|
try {
|
|
23375
|
-
const raw = await
|
|
23634
|
+
const raw = await readFile6(PROFILES_FILE, "utf8");
|
|
23376
23635
|
return parseProfiles(raw);
|
|
23377
23636
|
} catch (err) {
|
|
23378
23637
|
if (isErrno3(err, "ENOENT")) return { profiles: {}, default: null };
|
|
@@ -23386,13 +23645,13 @@ async function readProfiles() {
|
|
|
23386
23645
|
}
|
|
23387
23646
|
async function writeProfiles(data) {
|
|
23388
23647
|
try {
|
|
23389
|
-
await
|
|
23648
|
+
await mkdir5(CONFIG_DIR4, { recursive: true, mode: 448 });
|
|
23390
23649
|
const tmp = `${PROFILES_FILE}.${process.pid}.${Date.now()}.tmp`;
|
|
23391
23650
|
try {
|
|
23392
|
-
await
|
|
23393
|
-
await
|
|
23651
|
+
await writeFile5(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
23652
|
+
await rename4(tmp, PROFILES_FILE);
|
|
23394
23653
|
} catch (err) {
|
|
23395
|
-
await
|
|
23654
|
+
await rm5(tmp, { force: true });
|
|
23396
23655
|
throw err;
|
|
23397
23656
|
}
|
|
23398
23657
|
} catch (err) {
|
package/package.json
CHANGED