@fairmint/captable-ai 0.1.33 → 0.1.35
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 +143 -20
- 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",
|
|
@@ -15730,7 +15802,13 @@ var draftOperationDependencyIssueSchema = external_exports.object({
|
|
|
15730
15802
|
expectedType: external_exports.string().describe("Expected OCF object category for the reference.")
|
|
15731
15803
|
});
|
|
15732
15804
|
var draftOperationPlanIssueSchema = external_exports.object({
|
|
15733
|
-
code: external_exports.enum([
|
|
15805
|
+
code: external_exports.enum([
|
|
15806
|
+
"NO_DRAFTS",
|
|
15807
|
+
"REVERT_LIMIT_EXCEEDED",
|
|
15808
|
+
"ISSUER_REQUIRED",
|
|
15809
|
+
"ISSUER_INCOMPLETE",
|
|
15810
|
+
"DOCUMENT_INTEGRITY_FAILED"
|
|
15811
|
+
]).describe("Plan-level validation code."),
|
|
15734
15812
|
message: external_exports.string().describe("Human-readable plan validation message.")
|
|
15735
15813
|
});
|
|
15736
15814
|
var draftOperationPreviewInputSchema = external_exports.object({
|
|
@@ -23025,7 +23103,16 @@ async function runImportStart(args) {
|
|
|
23025
23103
|
function scopeFlag(ids) {
|
|
23026
23104
|
return ids && ids.length > 0 ? ` --s3-asset-ids ${ids.join(",")}` : "";
|
|
23027
23105
|
}
|
|
23106
|
+
var CLI_SELECTION_MAX_BYTES = 1e5;
|
|
23028
23107
|
function importStatusCommand(action, sessionId) {
|
|
23108
|
+
const tooManyToSelect = (action.id === "preview_commit" || action.id === "confirm_commit") && action.objectIds !== void 0 && Buffer.byteLength(`--objects=${action.objectIds.join(",")}`, "utf8") > CLI_SELECTION_MAX_BYTES;
|
|
23109
|
+
if (tooManyToSelect) {
|
|
23110
|
+
return {
|
|
23111
|
+
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.`,
|
|
23113
|
+
required: action.required
|
|
23114
|
+
};
|
|
23115
|
+
}
|
|
23029
23116
|
const command = (() => {
|
|
23030
23117
|
switch (action.id) {
|
|
23031
23118
|
case "upload_documents":
|
|
@@ -23042,12 +23129,19 @@ function importStatusCommand(action, sessionId) {
|
|
|
23042
23129
|
return "captable-ai documents list";
|
|
23043
23130
|
case "review_failures":
|
|
23044
23131
|
return action.statusFilter ? `captable-ai documents list --status ${action.statusFilter}` : "captable-ai documents list";
|
|
23045
|
-
case "review_drafts":
|
|
23046
|
-
return "captable-ai objects list --status draft --all";
|
|
23132
|
+
case "review_drafts": {
|
|
23133
|
+
if (!action.objectIds) return "captable-ai objects list --status draft --all";
|
|
23134
|
+
const [onlyDocument, ...moreDocuments] = action.s3AssetIds ?? [];
|
|
23135
|
+
if (onlyDocument !== void 0 && moreDocuments.length === 0) {
|
|
23136
|
+
return `captable-ai objects list --status draft --document ${shellQuote(onlyDocument)} --all`;
|
|
23137
|
+
}
|
|
23138
|
+
const reviewable = (action.reviewObjectIds ?? []).slice(0, IMPORT_REVIEW_MAX_IDS);
|
|
23139
|
+
return `captable-ai objects list --status draft --ids=${shellQuote(reviewable.join(","))}`;
|
|
23140
|
+
}
|
|
23047
23141
|
case "preview_commit":
|
|
23048
|
-
return "captable-ai commit --dry-run";
|
|
23142
|
+
return action.objectIds ? `captable-ai commit --dry-run --objects=${shellQuote(action.objectIds.join(","))}` : "captable-ai commit --dry-run";
|
|
23049
23143
|
case "confirm_commit":
|
|
23050
|
-
return "captable-ai commit --all";
|
|
23144
|
+
return action.objectIds ? "captable-ai commit --objects <final IDs from the dry run>" : "captable-ai commit --all";
|
|
23051
23145
|
default: {
|
|
23052
23146
|
const unhandled = action.id;
|
|
23053
23147
|
throw new Error(`Unhandled import status action: ${String(unhandled)}`);
|
|
@@ -23057,35 +23151,40 @@ function importStatusCommand(action, sessionId) {
|
|
|
23057
23151
|
const id = action.id === "upload_documents" ? "upload_cli" : action.id;
|
|
23058
23152
|
return {
|
|
23059
23153
|
id,
|
|
23060
|
-
label: action.label,
|
|
23154
|
+
label: action.id === "review_drafts" && reviewIsCut(action) ? `${action.label} (first ${IMPORT_REVIEW_MAX_IDS} of ${action.reviewObjectIds?.length ?? 0})` : action.label,
|
|
23061
23155
|
required: action.required,
|
|
23062
23156
|
command,
|
|
23063
23157
|
...action.id === "review_failures" && action.s3AssetIds && { s3AssetIds: action.s3AssetIds }
|
|
23064
23158
|
};
|
|
23065
23159
|
}
|
|
23066
|
-
var
|
|
23067
|
-
async function
|
|
23160
|
+
var DRAFT_SCOPE_MAX_PAGES = 100;
|
|
23161
|
+
async function readImportDraftScope(portalId, documentIds) {
|
|
23068
23162
|
if (!portalId) return null;
|
|
23069
23163
|
try {
|
|
23070
|
-
|
|
23164
|
+
const drafts = [];
|
|
23071
23165
|
let cursor;
|
|
23072
|
-
for (let page = 0; page <
|
|
23166
|
+
for (let page = 0; page < DRAFT_SCOPE_MAX_PAGES; page += 1) {
|
|
23073
23167
|
const res = await apiRequest(
|
|
23074
23168
|
`/${encodeURIComponent(portalId)}/objects`,
|
|
23075
23169
|
{
|
|
23076
|
-
query: { status: "draft", ...cursor ? { cursor } : {} },
|
|
23170
|
+
query: { status: "draft", limit: 250, ...cursor ? { cursor } : {} },
|
|
23077
23171
|
// Optional read: throw (caught below) instead of exiting the process on a failure.
|
|
23078
23172
|
softFail: true
|
|
23079
23173
|
}
|
|
23080
23174
|
);
|
|
23081
23175
|
const objects = res.data?.objects;
|
|
23082
23176
|
if (!Array.isArray(objects)) return null;
|
|
23083
|
-
|
|
23084
|
-
(
|
|
23085
|
-
|
|
23086
|
-
|
|
23177
|
+
for (const obj of objects) {
|
|
23178
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return null;
|
|
23179
|
+
drafts.push({
|
|
23180
|
+
id: Reflect.get(obj, "id"),
|
|
23181
|
+
type: Reflect.get(obj, "type"),
|
|
23182
|
+
subtype: Reflect.get(obj, "subtype"),
|
|
23183
|
+
..."documentId" in obj ? { documentId: Reflect.get(obj, "documentId") } : {}
|
|
23184
|
+
});
|
|
23185
|
+
}
|
|
23186
|
+
if (!res.meta?.hasMore) return partitionImportDrafts(drafts, documentIds);
|
|
23087
23187
|
const next = res.meta?.cursor;
|
|
23088
|
-
if (!res.meta?.hasMore) return count;
|
|
23089
23188
|
if (!next || next === cursor) return null;
|
|
23090
23189
|
cursor = next;
|
|
23091
23190
|
}
|
|
@@ -23108,12 +23207,19 @@ async function runImportStatus(args) {
|
|
|
23108
23207
|
);
|
|
23109
23208
|
const portalDocuments = response.data?.processing?.documents ?? [];
|
|
23110
23209
|
const documents = documentsForImportSession(portalDocuments, response.data?.session?.createdAt);
|
|
23210
|
+
const documentIds = documents.flatMap(
|
|
23211
|
+
(document) => typeof document.s3AssetId === "string" ? [document.s3AssetId] : []
|
|
23212
|
+
);
|
|
23111
23213
|
const documentPlan = planImportStatus({
|
|
23112
23214
|
documents,
|
|
23113
23215
|
sessionState: response.data?.session?.state,
|
|
23114
23216
|
scopeUnknown: importSessionScopeUnknown(portalDocuments)
|
|
23115
23217
|
});
|
|
23116
|
-
const plan = documentPlan.phase === "complete" ?
|
|
23218
|
+
const plan = documentPlan.phase === "complete" ? applyImportDrafts(
|
|
23219
|
+
documentPlan,
|
|
23220
|
+
await readImportDraftScope(response.data?.session?.portalId, documentIds),
|
|
23221
|
+
documentIds
|
|
23222
|
+
) : documentPlan;
|
|
23117
23223
|
const { summary } = plan;
|
|
23118
23224
|
if (plan.shouldComplete) {
|
|
23119
23225
|
const completed = await apiRequest(
|
|
@@ -23135,6 +23241,7 @@ async function runImportStatus(args) {
|
|
|
23135
23241
|
phase: plan.phase,
|
|
23136
23242
|
summary,
|
|
23137
23243
|
nextActions,
|
|
23244
|
+
...plan.unrelatedDrafts && { unrelatedDrafts: true },
|
|
23138
23245
|
...suggestMonitoring && { monitoringHint: BACKGROUND_MONITORING_SUGGESTION }
|
|
23139
23246
|
};
|
|
23140
23247
|
if (isJsonMode()) {
|
|
@@ -23159,12 +23266,28 @@ async function runImportStatus(args) {
|
|
|
23159
23266
|
" Some uploads never finished. Re-upload them; checking status again will not complete them."
|
|
23160
23267
|
);
|
|
23161
23268
|
}
|
|
23269
|
+
if (plan.unrelatedDrafts) {
|
|
23270
|
+
printLine(
|
|
23271
|
+
" The portal has other pending drafts that this import did not produce; leave them alone unless asked."
|
|
23272
|
+
);
|
|
23273
|
+
}
|
|
23274
|
+
const scopedCommit = plan.actions.some(
|
|
23275
|
+
(planned) => planned.id === "confirm_commit" && planned.objectIds !== void 0
|
|
23276
|
+
);
|
|
23162
23277
|
for (const action of nextActions) {
|
|
23278
|
+
if (action.command === void 0) {
|
|
23279
|
+
printLine(` Note: ${action.label}`);
|
|
23280
|
+
continue;
|
|
23281
|
+
}
|
|
23163
23282
|
printLine(` Next: ${action.command}`);
|
|
23164
23283
|
if (action.id === "resolve_extraction_scope") printLine(` Note: ${action.label}`);
|
|
23165
23284
|
if (action.id === "check_status" && plan.phase === "empty") {
|
|
23166
23285
|
printLine(` Note: ${action.label}`);
|
|
23167
23286
|
}
|
|
23287
|
+
if (action.id === "confirm_commit" && scopedCommit) printLine(` Note: ${action.label}`);
|
|
23288
|
+
if (action.id === "review_drafts" && action.label.includes("(first ")) {
|
|
23289
|
+
printLine(` Note: ${action.label}`);
|
|
23290
|
+
}
|
|
23168
23291
|
if (action.id === "review_failures" && action.s3AssetIds) {
|
|
23169
23292
|
printLine(` Documents: ${action.s3AssetIds.join(", ")}`);
|
|
23170
23293
|
}
|
package/package.json
CHANGED