@sift-wiki/cli 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/bin/sift.js +878 -304
- package/package.json +1 -1
package/dist/bin/sift.js
CHANGED
|
@@ -149,20 +149,20 @@ function parseProfileQuery(input) {
|
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
151
|
function parseToolsSearch(input) {
|
|
152
|
-
const
|
|
152
|
+
const result = {
|
|
153
153
|
intent: requireString(input, "intent")
|
|
154
154
|
};
|
|
155
155
|
if (input.toolsetNames !== void 0) {
|
|
156
|
-
|
|
156
|
+
result.toolsetNames = requireStringArray(input, "toolsetNames");
|
|
157
157
|
}
|
|
158
158
|
if (input.limit !== void 0) {
|
|
159
159
|
const limit = optionalInteger(input, "limit");
|
|
160
160
|
if (limit === void 0 || limit < 1 || limit > 20) {
|
|
161
161
|
throw new Error("limit must be an integer between 1 and 20.");
|
|
162
162
|
}
|
|
163
|
-
|
|
163
|
+
result.limit = limit;
|
|
164
164
|
}
|
|
165
|
-
return
|
|
165
|
+
return result;
|
|
166
166
|
}
|
|
167
167
|
function parseDecision(input) {
|
|
168
168
|
return {
|
|
@@ -373,6 +373,7 @@ function writeTool(name, summary, properties, cliExample, options) {
|
|
|
373
373
|
capability: "record:write",
|
|
374
374
|
mutability: "write",
|
|
375
375
|
transports: writeTransports,
|
|
376
|
+
idempotency: options?.idempotency,
|
|
376
377
|
cliExample,
|
|
377
378
|
hostedAgent: options?.hostedAgent
|
|
378
379
|
});
|
|
@@ -428,7 +429,7 @@ function defineTool(input) {
|
|
|
428
429
|
mutability: input.mutability,
|
|
429
430
|
auditCategory: input.mutability === "admin" ? "admin" : input.mutability,
|
|
430
431
|
transports: input.transports,
|
|
431
|
-
idempotency: input.mutability === "write" ? "recommended" : "none",
|
|
432
|
+
idempotency: input.idempotency ?? (input.mutability === "write" ? "recommended" : "none"),
|
|
432
433
|
resultSize: "compact",
|
|
433
434
|
cliExample: input.cliExample,
|
|
434
435
|
hostedAgent: hostedAgentMetadata(input, required)
|
|
@@ -610,6 +611,32 @@ var init_registry = __esm({
|
|
|
610
611
|
severity: { type: "string" },
|
|
611
612
|
visibility: { type: "array", items: { type: "string" } }
|
|
612
613
|
}, "sift skill teach <skill-id> --lesson 'when X, do Y'", { required: ["skillId", "lesson", "visibility"] }),
|
|
614
|
+
writeTool("skill.feedback", "Report structured feedback for a pinned skill version; external-agent reports remain reported until trusted Sift evidence verifies them.", {
|
|
615
|
+
skillId: { type: "string" },
|
|
616
|
+
skillVersionId: { type: "string" },
|
|
617
|
+
exerciseRef: {
|
|
618
|
+
type: "object",
|
|
619
|
+
properties: { exerciseId: { type: "string" } },
|
|
620
|
+
required: ["exerciseId"]
|
|
621
|
+
},
|
|
622
|
+
subjectRef: { type: "object", properties: {} },
|
|
623
|
+
signalKind: { type: "string" },
|
|
624
|
+
polarity: { type: "string", enum: ["positive", "negative", "mixed", "unknown"] },
|
|
625
|
+
strength: { type: "string", enum: ["weak", "medium", "strong"] },
|
|
626
|
+
payload: { type: "object", properties: {} },
|
|
627
|
+
idempotencyKey: { type: "string" }
|
|
628
|
+
}, "sift skill feedback <skill-id> <skill-version-id>", {
|
|
629
|
+
required: [
|
|
630
|
+
"skillId",
|
|
631
|
+
"skillVersionId",
|
|
632
|
+
"signalKind",
|
|
633
|
+
"polarity",
|
|
634
|
+
"strength",
|
|
635
|
+
"payload",
|
|
636
|
+
"idempotencyKey"
|
|
637
|
+
],
|
|
638
|
+
idempotency: "required"
|
|
639
|
+
}),
|
|
613
640
|
readTool("search.query", "Search authorized brain context and return raw cited candidate results for exploration.", {
|
|
614
641
|
query: { type: "string" },
|
|
615
642
|
limit: { type: "integer", minimum: 1, maximum: 20 }
|
|
@@ -813,24 +840,24 @@ var init_discovery = __esm({
|
|
|
813
840
|
});
|
|
814
841
|
|
|
815
842
|
// ../tools/dist/results.js
|
|
816
|
-
function captureResult(
|
|
843
|
+
function captureResult(result) {
|
|
817
844
|
return {
|
|
818
|
-
status:
|
|
819
|
-
jobId:
|
|
820
|
-
sourceId:
|
|
821
|
-
sourceItemId:
|
|
822
|
-
recordId:
|
|
823
|
-
versionId:
|
|
824
|
-
versionNumber:
|
|
845
|
+
status: result.job?.status ?? result.status ?? "captured",
|
|
846
|
+
jobId: result.job?.id,
|
|
847
|
+
sourceId: result.sourceId,
|
|
848
|
+
sourceItemId: result.sourceItemId,
|
|
849
|
+
recordId: result.recordId,
|
|
850
|
+
versionId: result.versionId,
|
|
851
|
+
versionNumber: result.versionNumber
|
|
825
852
|
};
|
|
826
853
|
}
|
|
827
|
-
function workRecordResult(
|
|
854
|
+
function workRecordResult(result) {
|
|
828
855
|
return {
|
|
829
|
-
status:
|
|
830
|
-
jobId:
|
|
831
|
-
recordId:
|
|
832
|
-
versionId:
|
|
833
|
-
versionNumber:
|
|
856
|
+
status: result.job.status,
|
|
857
|
+
jobId: result.job.id,
|
|
858
|
+
recordId: result.recordId,
|
|
859
|
+
versionId: result.versionId,
|
|
860
|
+
versionNumber: result.versionNumber
|
|
834
861
|
};
|
|
835
862
|
}
|
|
836
863
|
var init_results = __esm({
|
|
@@ -842,16 +869,16 @@ var init_results = __esm({
|
|
|
842
869
|
// ../tools/dist/captureTools.js
|
|
843
870
|
async function executeCaptureText(input, toolInput) {
|
|
844
871
|
const capture = parseCaptureText(toolInput);
|
|
845
|
-
const
|
|
846
|
-
return captureResult(
|
|
872
|
+
const result = await input.service.ingestText({ auth: input.auth, ...capture });
|
|
873
|
+
return captureResult(result);
|
|
847
874
|
}
|
|
848
875
|
async function executeCaptureFile(input, toolInput) {
|
|
849
876
|
if (input.service.ingestFile === void 0) {
|
|
850
877
|
throw new Error("Tool 'capture.file' requires a file ingestion service contract.");
|
|
851
878
|
}
|
|
852
879
|
const file = parseCaptureFile(toolInput);
|
|
853
|
-
const
|
|
854
|
-
return captureResult(
|
|
880
|
+
const result = await input.service.ingestFile({ auth: input.auth, ...file });
|
|
881
|
+
return captureResult(result);
|
|
855
882
|
}
|
|
856
883
|
async function executeCaptureBatch(input, toolInput) {
|
|
857
884
|
const batch = parseCaptureBatch(toolInput);
|
|
@@ -859,16 +886,16 @@ async function executeCaptureBatch(input, toolInput) {
|
|
|
859
886
|
for (const item of batch.items) {
|
|
860
887
|
if (item.kind === "text") {
|
|
861
888
|
const { kind: _kind2, ...capture } = item;
|
|
862
|
-
const
|
|
863
|
-
results.push(captureResult(
|
|
889
|
+
const result2 = await input.service.ingestText({ auth: input.auth, ...capture });
|
|
890
|
+
results.push(captureResult(result2));
|
|
864
891
|
continue;
|
|
865
892
|
}
|
|
866
893
|
if (input.service.ingestFile === void 0) {
|
|
867
894
|
throw new Error("Tool 'capture.batch' requires a file ingestion service contract.");
|
|
868
895
|
}
|
|
869
896
|
const { kind: _kind, ...file } = item;
|
|
870
|
-
const
|
|
871
|
-
results.push(captureResult(
|
|
897
|
+
const result = await input.service.ingestFile({ auth: input.auth, ...file });
|
|
898
|
+
results.push(captureResult(result));
|
|
872
899
|
}
|
|
873
900
|
return results;
|
|
874
901
|
}
|
|
@@ -941,6 +968,15 @@ function skillToolHandlers(input, toolInput) {
|
|
|
941
968
|
if (input.service.teachSkill === void 0)
|
|
942
969
|
throw missingSkillService("skill.teach");
|
|
943
970
|
return input.service.teachSkill({ auth: input.auth, ...parseSkillTeach(toolInput) });
|
|
971
|
+
},
|
|
972
|
+
"skill.feedback": () => {
|
|
973
|
+
if (input.service.reportFeedbackSignal === void 0) {
|
|
974
|
+
throw missingSkillService("skill.feedback");
|
|
975
|
+
}
|
|
976
|
+
return input.service.reportFeedbackSignal({
|
|
977
|
+
auth: input.auth,
|
|
978
|
+
...parseSkillFeedback(toolInput)
|
|
979
|
+
});
|
|
944
980
|
}
|
|
945
981
|
};
|
|
946
982
|
}
|
|
@@ -950,7 +986,8 @@ function skillToolAvailability(service) {
|
|
|
950
986
|
[service.getSkill !== void 0, ["skill.get"]],
|
|
951
987
|
[service.getSkillFile !== void 0, ["skill.file"]],
|
|
952
988
|
[service.recordSkillExercise !== void 0, ["skill.exercise"]],
|
|
953
|
-
[service.teachSkill !== void 0, ["skill.teach"]]
|
|
989
|
+
[service.teachSkill !== void 0, ["skill.teach"]],
|
|
990
|
+
[service.reportFeedbackSignal !== void 0, ["skill.feedback"]]
|
|
954
991
|
];
|
|
955
992
|
}
|
|
956
993
|
function parseSkillExercise(input) {
|
|
@@ -983,6 +1020,29 @@ function parseSkillTeach(input) {
|
|
|
983
1020
|
visibility
|
|
984
1021
|
};
|
|
985
1022
|
}
|
|
1023
|
+
function parseSkillFeedback(input) {
|
|
1024
|
+
const exerciseRef = optionalObject(input, "exerciseRef");
|
|
1025
|
+
const subjectRef = optionalObject(input, "subjectRef");
|
|
1026
|
+
const polarity = requireString(input, "polarity");
|
|
1027
|
+
const strength = requireString(input, "strength");
|
|
1028
|
+
if (!isSkillFeedbackPolarity(polarity)) {
|
|
1029
|
+
throw new Error("polarity must be positive, negative, mixed, or unknown.");
|
|
1030
|
+
}
|
|
1031
|
+
if (!isSkillFeedbackStrength(strength)) {
|
|
1032
|
+
throw new Error("strength must be weak, medium, or strong.");
|
|
1033
|
+
}
|
|
1034
|
+
return {
|
|
1035
|
+
skillId: requireString(input, "skillId"),
|
|
1036
|
+
skillVersionId: requireString(input, "skillVersionId"),
|
|
1037
|
+
exerciseRef: exerciseRef === void 0 ? void 0 : { exerciseId: requireString(exerciseRef, "exerciseId") },
|
|
1038
|
+
subjectRef,
|
|
1039
|
+
signalKind: requireString(input, "signalKind"),
|
|
1040
|
+
polarity,
|
|
1041
|
+
strength,
|
|
1042
|
+
payload: optionalObject(input, "payload") ?? {},
|
|
1043
|
+
idempotencyKey: requireString(input, "idempotencyKey")
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
986
1046
|
function requireSkillOutputRef(input, key) {
|
|
987
1047
|
const value = input[key];
|
|
988
1048
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
@@ -1001,6 +1061,22 @@ function requireSkillOutputRef(input, key) {
|
|
|
1001
1061
|
}
|
|
1002
1062
|
throw new Error(`${key}.kind must be record or external.`);
|
|
1003
1063
|
}
|
|
1064
|
+
function optionalObject(input, key) {
|
|
1065
|
+
const value = input[key];
|
|
1066
|
+
if (value === void 0) {
|
|
1067
|
+
return void 0;
|
|
1068
|
+
}
|
|
1069
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1070
|
+
throw new Error(`${key} must be an object.`);
|
|
1071
|
+
}
|
|
1072
|
+
return value;
|
|
1073
|
+
}
|
|
1074
|
+
function isSkillFeedbackPolarity(value) {
|
|
1075
|
+
return value === "positive" || value === "negative" || value === "mixed" || value === "unknown";
|
|
1076
|
+
}
|
|
1077
|
+
function isSkillFeedbackStrength(value) {
|
|
1078
|
+
return value === "weak" || value === "medium" || value === "strong";
|
|
1079
|
+
}
|
|
1004
1080
|
function missingSkillService(toolName) {
|
|
1005
1081
|
return new Error(`Tool '${toolName}' requires a runtime service contract.`);
|
|
1006
1082
|
}
|
|
@@ -1267,7 +1343,7 @@ function createRuntimeToolExecutor(input) {
|
|
|
1267
1343
|
throw error;
|
|
1268
1344
|
}
|
|
1269
1345
|
try {
|
|
1270
|
-
const
|
|
1346
|
+
const result = await handler();
|
|
1271
1347
|
logToolCall({
|
|
1272
1348
|
auth: input.auth,
|
|
1273
1349
|
onToolLog: input.onToolLog,
|
|
@@ -1275,9 +1351,9 @@ function createRuntimeToolExecutor(input) {
|
|
|
1275
1351
|
toolInput,
|
|
1276
1352
|
status: "success",
|
|
1277
1353
|
startedAt,
|
|
1278
|
-
result
|
|
1354
|
+
result
|
|
1279
1355
|
});
|
|
1280
|
-
return
|
|
1356
|
+
return result;
|
|
1281
1357
|
} catch (error) {
|
|
1282
1358
|
logToolCall({
|
|
1283
1359
|
auth: input.auth,
|
|
@@ -1411,16 +1487,16 @@ async function executeDecisionCreate(input, toolInput) {
|
|
|
1411
1487
|
throw new Error("Tool 'decision.create' requires a work-record service contract.");
|
|
1412
1488
|
}
|
|
1413
1489
|
const decision = parseDecision(toolInput);
|
|
1414
|
-
const
|
|
1415
|
-
return workRecordResult(
|
|
1490
|
+
const result = await input.service.createDecision({ auth: input.auth, ...decision });
|
|
1491
|
+
return workRecordResult(result);
|
|
1416
1492
|
}
|
|
1417
1493
|
async function executeTaskCreate(input, toolInput) {
|
|
1418
1494
|
if (input.service.createTask === void 0) {
|
|
1419
1495
|
throw new Error("Tool 'task.create' requires a work-record service contract.");
|
|
1420
1496
|
}
|
|
1421
1497
|
const task = parseTask(toolInput);
|
|
1422
|
-
const
|
|
1423
|
-
return workRecordResult(
|
|
1498
|
+
const result = await input.service.createTask({ auth: input.auth, ...task });
|
|
1499
|
+
return workRecordResult(result);
|
|
1424
1500
|
}
|
|
1425
1501
|
function executeSourceList(input) {
|
|
1426
1502
|
if (input.service.listSources === void 0) {
|
|
@@ -1471,16 +1547,16 @@ async function executeRecordCreateMarkdown(input, toolInput) {
|
|
|
1471
1547
|
throw new Error("Tool 'record.create_markdown' requires a record write service contract.");
|
|
1472
1548
|
}
|
|
1473
1549
|
const record = parseMarkdownRecord(toolInput);
|
|
1474
|
-
const
|
|
1475
|
-
return workRecordResult(
|
|
1550
|
+
const result = await input.service.createMarkdownRecord({ auth: input.auth, ...record });
|
|
1551
|
+
return workRecordResult(result);
|
|
1476
1552
|
}
|
|
1477
1553
|
async function executeRecordPatchSection(input, toolInput) {
|
|
1478
1554
|
if (input.service.patchRecordSection === void 0) {
|
|
1479
1555
|
throw new Error("Tool 'record.patch_section' requires a record write service contract.");
|
|
1480
1556
|
}
|
|
1481
1557
|
const patch = parseRecordSectionPatch(toolInput);
|
|
1482
|
-
const
|
|
1483
|
-
return workRecordResult(
|
|
1558
|
+
const result = await input.service.patchRecordSection({ auth: input.auth, ...patch });
|
|
1559
|
+
return workRecordResult(result);
|
|
1484
1560
|
}
|
|
1485
1561
|
function executeRecordVersions(input, toolInput) {
|
|
1486
1562
|
if (input.service.listRecordVersions === void 0) {
|
|
@@ -1563,11 +1639,11 @@ function createMcpAdapter(input) {
|
|
|
1563
1639
|
return errorResult2("tool_unavailable", `Tool '${call.name}' is unavailable for this MCP transport or scope.`);
|
|
1564
1640
|
}
|
|
1565
1641
|
try {
|
|
1566
|
-
const
|
|
1642
|
+
const result = await input.executor.execute(call.name, call.arguments);
|
|
1567
1643
|
return {
|
|
1568
1644
|
isError: false,
|
|
1569
|
-
structuredContent:
|
|
1570
|
-
content: [{ type: "text", text: renderToolResult(
|
|
1645
|
+
structuredContent: result,
|
|
1646
|
+
content: [{ type: "text", text: renderToolResult(result) }]
|
|
1571
1647
|
};
|
|
1572
1648
|
} catch (error) {
|
|
1573
1649
|
return errorResult2(classifyToolError(error), messageForError(error));
|
|
@@ -1575,14 +1651,14 @@ function createMcpAdapter(input) {
|
|
|
1575
1651
|
}
|
|
1576
1652
|
};
|
|
1577
1653
|
}
|
|
1578
|
-
function renderToolResult(
|
|
1579
|
-
if (typeof
|
|
1580
|
-
const context =
|
|
1654
|
+
function renderToolResult(result) {
|
|
1655
|
+
if (typeof result === "object" && result !== null && "contextMarkdown" in result) {
|
|
1656
|
+
const context = result;
|
|
1581
1657
|
if (typeof context.contextMarkdown === "string") {
|
|
1582
1658
|
return context.contextMarkdown;
|
|
1583
1659
|
}
|
|
1584
1660
|
}
|
|
1585
|
-
return JSON.stringify(
|
|
1661
|
+
return JSON.stringify(result);
|
|
1586
1662
|
}
|
|
1587
1663
|
function classifyToolError(error) {
|
|
1588
1664
|
if (error instanceof Error) {
|
|
@@ -1703,7 +1779,7 @@ var init_hostedMcpEntrypoint = __esm({
|
|
|
1703
1779
|
|
|
1704
1780
|
// ../tools/dist/mcpJsonRpcCore.js
|
|
1705
1781
|
function createMcpJsonRpcCore(input) {
|
|
1706
|
-
const { adapter, config
|
|
1782
|
+
const { adapter, config } = input;
|
|
1707
1783
|
return {
|
|
1708
1784
|
async handleMessage(message) {
|
|
1709
1785
|
if (message.id === void 0) {
|
|
@@ -1717,7 +1793,7 @@ function createMcpJsonRpcCore(input) {
|
|
|
1717
1793
|
return {
|
|
1718
1794
|
jsonrpc: "2.0",
|
|
1719
1795
|
id,
|
|
1720
|
-
result: await dispatchRequest(message.method, message.params, adapter,
|
|
1796
|
+
result: await dispatchRequest(message.method, message.params, adapter, config)
|
|
1721
1797
|
};
|
|
1722
1798
|
} catch (err) {
|
|
1723
1799
|
return errorResponse(id, -32601, err instanceof Error ? err.message : "Method not found");
|
|
@@ -1728,14 +1804,14 @@ function createMcpJsonRpcCore(input) {
|
|
|
1728
1804
|
function parseErrorResponse() {
|
|
1729
1805
|
return errorResponse(null, -32700, "Parse error");
|
|
1730
1806
|
}
|
|
1731
|
-
async function dispatchRequest(method, params, adapter,
|
|
1807
|
+
async function dispatchRequest(method, params, adapter, config) {
|
|
1732
1808
|
if (method === "initialize") {
|
|
1733
1809
|
const requested = readProtocolVersion(params);
|
|
1734
1810
|
return {
|
|
1735
1811
|
protocolVersion: requested ?? MCP_PROTOCOL_VERSION,
|
|
1736
1812
|
capabilities: { tools: { listChanged: false } },
|
|
1737
|
-
serverInfo: { name:
|
|
1738
|
-
instructions:
|
|
1813
|
+
serverInfo: { name: config.serverName, version: config.version },
|
|
1814
|
+
instructions: config.instructions
|
|
1739
1815
|
};
|
|
1740
1816
|
}
|
|
1741
1817
|
if (method === "ping")
|
|
@@ -1749,23 +1825,23 @@ async function dispatchRequest(method, params, adapter, config2) {
|
|
|
1749
1825
|
throw new Error(`Method '${method}' is not supported by Sift MCP.`);
|
|
1750
1826
|
}
|
|
1751
1827
|
function readProtocolVersion(params) {
|
|
1752
|
-
if (!
|
|
1828
|
+
if (!isRecord2(params))
|
|
1753
1829
|
return void 0;
|
|
1754
1830
|
return typeof params.protocolVersion === "string" ? params.protocolVersion : void 0;
|
|
1755
1831
|
}
|
|
1756
1832
|
function parseToolCall(params) {
|
|
1757
|
-
if (!
|
|
1833
|
+
if (!isRecord2(params) || typeof params.name !== "string") {
|
|
1758
1834
|
throw new Error("tools/call requires a tool name.");
|
|
1759
1835
|
}
|
|
1760
1836
|
return {
|
|
1761
1837
|
name: params.name,
|
|
1762
|
-
arguments:
|
|
1838
|
+
arguments: isRecord2(params.arguments) ? params.arguments : {}
|
|
1763
1839
|
};
|
|
1764
1840
|
}
|
|
1765
1841
|
function errorResponse(id, code, message) {
|
|
1766
1842
|
return { jsonrpc: "2.0", id, error: { code, message } };
|
|
1767
1843
|
}
|
|
1768
|
-
function
|
|
1844
|
+
function isRecord2(value) {
|
|
1769
1845
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1770
1846
|
}
|
|
1771
1847
|
function normalizeId(value) {
|
|
@@ -1900,19 +1976,19 @@ function renderScope(scope) {
|
|
|
1900
1976
|
""
|
|
1901
1977
|
].join("\n");
|
|
1902
1978
|
}
|
|
1903
|
-
function validateAuthenticatedScope(
|
|
1979
|
+
function validateAuthenticatedScope(config, now) {
|
|
1904
1980
|
const requiredScope = [
|
|
1905
|
-
["apiBaseUrl",
|
|
1906
|
-
["workspaceId",
|
|
1907
|
-
["brainId",
|
|
1908
|
-
["principalId",
|
|
1981
|
+
["apiBaseUrl", config.apiBaseUrl],
|
|
1982
|
+
["workspaceId", config.workspaceId],
|
|
1983
|
+
["brainId", config.brainId],
|
|
1984
|
+
["principalId", config.principalId]
|
|
1909
1985
|
];
|
|
1910
1986
|
const missing = requiredScope.find(([, value]) => value.trim().length === 0);
|
|
1911
1987
|
if (missing !== void 0) {
|
|
1912
1988
|
throw new Error(`Missing authenticated CLI scope: ${missing[0]}.`);
|
|
1913
1989
|
}
|
|
1914
|
-
if (
|
|
1915
|
-
const expiresAt = Date.parse(
|
|
1990
|
+
if (config.tokenExpiresAt !== void 0) {
|
|
1991
|
+
const expiresAt = Date.parse(config.tokenExpiresAt);
|
|
1916
1992
|
if (!Number.isFinite(expiresAt)) {
|
|
1917
1993
|
throw new Error("Invalid CLI auth expiry timestamp.");
|
|
1918
1994
|
}
|
|
@@ -1921,49 +1997,49 @@ function validateAuthenticatedScope(config2, now) {
|
|
|
1921
1997
|
}
|
|
1922
1998
|
}
|
|
1923
1999
|
}
|
|
1924
|
-
function renderSearchResult(
|
|
1925
|
-
if (typeof
|
|
1926
|
-
const context =
|
|
2000
|
+
function renderSearchResult(result) {
|
|
2001
|
+
if (typeof result === "object" && result !== null && "contextMarkdown" in result) {
|
|
2002
|
+
const context = result;
|
|
1927
2003
|
return `${context.contextMarkdown}
|
|
1928
2004
|
`;
|
|
1929
2005
|
}
|
|
1930
|
-
return `${JSON.stringify(
|
|
2006
|
+
return `${JSON.stringify(result)}
|
|
1931
2007
|
`;
|
|
1932
2008
|
}
|
|
1933
|
-
function renderRecordResult(
|
|
1934
|
-
if (typeof
|
|
1935
|
-
const record =
|
|
2009
|
+
function renderRecordResult(result) {
|
|
2010
|
+
if (typeof result === "object" && result !== null && "markdown" in result) {
|
|
2011
|
+
const record = result;
|
|
1936
2012
|
return `${record.markdown}
|
|
1937
2013
|
`;
|
|
1938
2014
|
}
|
|
1939
|
-
return `${JSON.stringify(
|
|
2015
|
+
return `${JSON.stringify(result)}
|
|
1940
2016
|
`;
|
|
1941
2017
|
}
|
|
1942
|
-
function renderProfileResult(
|
|
1943
|
-
if (typeof
|
|
1944
|
-
const profile =
|
|
2018
|
+
function renderProfileResult(result) {
|
|
2019
|
+
if (typeof result === "object" && result !== null && "profileMarkdown" in result) {
|
|
2020
|
+
const profile = result;
|
|
1945
2021
|
return `${profile.profileMarkdown}
|
|
1946
2022
|
`;
|
|
1947
2023
|
}
|
|
1948
|
-
return `${JSON.stringify(
|
|
2024
|
+
return `${JSON.stringify(result)}
|
|
1949
2025
|
`;
|
|
1950
2026
|
}
|
|
1951
2027
|
function renderTools(tools) {
|
|
1952
2028
|
return `${tools.map((tool) => `${tool.name}: ${tool.summary}`).join("\n")}
|
|
1953
2029
|
`;
|
|
1954
2030
|
}
|
|
1955
|
-
function renderSiftFound(
|
|
2031
|
+
function renderSiftFound(result) {
|
|
1956
2032
|
return `Sift found:
|
|
1957
2033
|
|
|
1958
|
-
${renderSearchResult(
|
|
2034
|
+
${renderSearchResult(result)}`;
|
|
1959
2035
|
}
|
|
1960
|
-
function renderWriteReceipt(action,
|
|
1961
|
-
if (typeof
|
|
2036
|
+
function renderWriteReceipt(action, result) {
|
|
2037
|
+
if (typeof result !== "object" || result === null) {
|
|
1962
2038
|
return `${action} complete.
|
|
1963
|
-
${JSON.stringify(
|
|
2039
|
+
${JSON.stringify(result)}
|
|
1964
2040
|
`;
|
|
1965
2041
|
}
|
|
1966
|
-
const record =
|
|
2042
|
+
const record = result;
|
|
1967
2043
|
const lines = [`${action} complete.`];
|
|
1968
2044
|
addReceiptLine(lines, "Record", record.recordId);
|
|
1969
2045
|
addReceiptLine(lines, "Version", record.versionId);
|
|
@@ -1973,20 +2049,20 @@ ${JSON.stringify(result2)}
|
|
|
1973
2049
|
addReceiptLine(lines, "Job", record.job.id);
|
|
1974
2050
|
}
|
|
1975
2051
|
if (lines.length === 1) {
|
|
1976
|
-
lines.push(JSON.stringify(
|
|
2052
|
+
lines.push(JSON.stringify(result));
|
|
1977
2053
|
}
|
|
1978
2054
|
lines.push("");
|
|
1979
2055
|
return lines.join("\n");
|
|
1980
2056
|
}
|
|
1981
|
-
function renderDoctorResult(
|
|
1982
|
-
return `${
|
|
2057
|
+
function renderDoctorResult(result) {
|
|
2058
|
+
return `${result.checks.map((check) => {
|
|
1983
2059
|
const fix = check.fix === void 0 ? "" : ` Fix: ${check.fix}`;
|
|
1984
2060
|
return `[${check.status}] ${check.label}: ${check.detail}${fix}`;
|
|
1985
2061
|
}).join("\n")}
|
|
1986
2062
|
`;
|
|
1987
2063
|
}
|
|
1988
|
-
function aliasJson(command, tool,
|
|
1989
|
-
return `${JSON.stringify({ command, tool, result
|
|
2064
|
+
function aliasJson(command, tool, result) {
|
|
2065
|
+
return `${JSON.stringify({ command, tool, result })}
|
|
1990
2066
|
`;
|
|
1991
2067
|
}
|
|
1992
2068
|
function positionalArgs(args) {
|
|
@@ -2184,16 +2260,16 @@ async function agentRegister(executor, assertedAgentName, rest, json) {
|
|
|
2184
2260
|
if (kind !== void 0) {
|
|
2185
2261
|
input.kind = kind;
|
|
2186
2262
|
}
|
|
2187
|
-
const
|
|
2188
|
-
return ok(json ? `${JSON.stringify(
|
|
2189
|
-
` : renderAgentRegisterResult(
|
|
2263
|
+
const result = await executor.execute("agent.register", input);
|
|
2264
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
2265
|
+
` : renderAgentRegisterResult(result));
|
|
2190
2266
|
}
|
|
2191
|
-
function renderAgentRegisterResult(
|
|
2192
|
-
if (typeof
|
|
2193
|
-
return `${JSON.stringify(
|
|
2267
|
+
function renderAgentRegisterResult(result) {
|
|
2268
|
+
if (typeof result !== "object" || result === null || !("agent" in result)) {
|
|
2269
|
+
return `${JSON.stringify(result)}
|
|
2194
2270
|
`;
|
|
2195
2271
|
}
|
|
2196
|
-
const { agent, created, reactivated } =
|
|
2272
|
+
const { agent, created, reactivated } = result;
|
|
2197
2273
|
const verb = created === true ? "Registered" : reactivated === true ? "Reactivated" : "Already registered";
|
|
2198
2274
|
const actsFor = agent.actsForDisplayName === void 0 ? "" : ` (acting for ${agent.actsForDisplayName})`;
|
|
2199
2275
|
return `${verb} agent worker '${agent.name ?? "unknown"}'${actsFor}.
|
|
@@ -2262,7 +2338,8 @@ var commandCapabilities = {
|
|
|
2262
2338
|
"evidence:get": "record:read",
|
|
2263
2339
|
"graph:neighbors": "record:read",
|
|
2264
2340
|
"event:list": "record:read",
|
|
2265
|
-
"audit:events": "event:audit:read"
|
|
2341
|
+
"audit:events": "event:audit:read",
|
|
2342
|
+
"roam:import": "source:manage"
|
|
2266
2343
|
};
|
|
2267
2344
|
function validateCommandCapability(input) {
|
|
2268
2345
|
const capability = commandCapabilities[input.commandKey];
|
|
@@ -2300,6 +2377,190 @@ function withContractVersion(executor, contractVersion) {
|
|
|
2300
2377
|
};
|
|
2301
2378
|
}
|
|
2302
2379
|
|
|
2380
|
+
// src/roamImport.ts
|
|
2381
|
+
async function runRoamImportCommand(input) {
|
|
2382
|
+
try {
|
|
2383
|
+
const parsed = parseOptions(input.rest);
|
|
2384
|
+
const scope = parseCliRoamScope(optionalOption(parsed, "scope") ?? "sift-tag");
|
|
2385
|
+
const mode = parseCliRoamMode(optionalOption(parsed, "mode") ?? "personal");
|
|
2386
|
+
const limit = parseIntegerOption(parsed, "limit", 100);
|
|
2387
|
+
if (limit < 1 || limit > 100) {
|
|
2388
|
+
throw new Error("Option --limit must be between 1 and 100.");
|
|
2389
|
+
}
|
|
2390
|
+
const wholeGraphConfirmed = input.rest.includes("--confirm-whole-graph") || input.rest.includes("--yes");
|
|
2391
|
+
if (scope === "whole_graph" && !wholeGraphConfirmed) {
|
|
2392
|
+
throw new Error("Option --confirm-whole-graph is required.");
|
|
2393
|
+
}
|
|
2394
|
+
const workspaceAttestation = input.rest.includes("--workspace-attestation") || input.rest.includes("--confirm-workspace");
|
|
2395
|
+
if (mode === "workspace" && !workspaceAttestation) {
|
|
2396
|
+
throw new Error("Option --workspace-attestation is required.");
|
|
2397
|
+
}
|
|
2398
|
+
if (input.reader === void 0) {
|
|
2399
|
+
throw new Error(
|
|
2400
|
+
"Roam import needs the local Roam helper. Run this command through the Sift CLI package."
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
if (input.importer === void 0) {
|
|
2404
|
+
throw new Error("Not signed in. Run 'sift login', then retry 'sift roam import'.");
|
|
2405
|
+
}
|
|
2406
|
+
const records = await input.reader.exportPages({
|
|
2407
|
+
scope,
|
|
2408
|
+
graph: optionalOption(parsed, "graph"),
|
|
2409
|
+
limit,
|
|
2410
|
+
now: input.now
|
|
2411
|
+
});
|
|
2412
|
+
if (records.length === 0) {
|
|
2413
|
+
throw new Error(
|
|
2414
|
+
scope === "sift_tag" ? "No Roam pages marked [[Sift]] were found." : "No Roam pages were found for import."
|
|
2415
|
+
);
|
|
2416
|
+
}
|
|
2417
|
+
const result = await input.importer.importRecords({
|
|
2418
|
+
mode,
|
|
2419
|
+
scope,
|
|
2420
|
+
records,
|
|
2421
|
+
defaultVisibility: visibilityOption(parsed),
|
|
2422
|
+
workspaceAttestation,
|
|
2423
|
+
wholeGraphConfirmed
|
|
2424
|
+
});
|
|
2425
|
+
return ok(input.json ? `${JSON.stringify(result)}
|
|
2426
|
+
` : renderRoamImportResult(result));
|
|
2427
|
+
} catch (error) {
|
|
2428
|
+
return errorResult(error, input.json);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
function createSiftRoamImportClient(input) {
|
|
2432
|
+
const fetchImpl = input.fetch ?? globalThis.fetch;
|
|
2433
|
+
return {
|
|
2434
|
+
async importRecords(request) {
|
|
2435
|
+
const response = await fetchImpl(roamImportUrl(input.apiBaseUrl, input.workspaceId), {
|
|
2436
|
+
method: "POST",
|
|
2437
|
+
headers: {
|
|
2438
|
+
Authorization: `Bearer ${input.token}`,
|
|
2439
|
+
"Content-Type": "application/json"
|
|
2440
|
+
},
|
|
2441
|
+
body: JSON.stringify(request)
|
|
2442
|
+
});
|
|
2443
|
+
const body = await response.text();
|
|
2444
|
+
const parsed = body.length > 0 ? parseJson(body) : {};
|
|
2445
|
+
if (!response.ok) {
|
|
2446
|
+
throw new Error(responseError(parsed, response.status));
|
|
2447
|
+
}
|
|
2448
|
+
return parseRoamImportResult(parsed);
|
|
2449
|
+
}
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
function parseCliRoamScope(value) {
|
|
2453
|
+
if (value === "sift-tag" || value === "sift_tag" || value === "marked" || value === "sift") {
|
|
2454
|
+
return "sift_tag";
|
|
2455
|
+
}
|
|
2456
|
+
if (value === "whole-graph" || value === "whole_graph" || value === "everything") {
|
|
2457
|
+
return "whole_graph";
|
|
2458
|
+
}
|
|
2459
|
+
throw new Error("Roam scope must be sift-tag or whole-graph.");
|
|
2460
|
+
}
|
|
2461
|
+
function parseCliRoamMode(value) {
|
|
2462
|
+
if (value === "personal" || value === "workspace") return value;
|
|
2463
|
+
throw new Error("Roam import mode must be personal or workspace.");
|
|
2464
|
+
}
|
|
2465
|
+
function visibilityOption(parsed) {
|
|
2466
|
+
const visibility = optionalOption(parsed, "visibility");
|
|
2467
|
+
if (visibility === void 0) return void 0;
|
|
2468
|
+
const values = visibility.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
2469
|
+
if (values.length === 0) {
|
|
2470
|
+
throw new Error("Option --visibility must include at least one visibility segment.");
|
|
2471
|
+
}
|
|
2472
|
+
return values;
|
|
2473
|
+
}
|
|
2474
|
+
function renderRoamImportResult(result) {
|
|
2475
|
+
return [
|
|
2476
|
+
`Imported ${result.importedCount} Roam pages.`,
|
|
2477
|
+
`Stored: ${result.storedCount}`,
|
|
2478
|
+
`Deduped: ${result.dedupedCount}`,
|
|
2479
|
+
`Rejected: ${result.rejectedCount}`,
|
|
2480
|
+
""
|
|
2481
|
+
].join("\n");
|
|
2482
|
+
}
|
|
2483
|
+
function roamImportUrl(apiBaseUrl, workspaceId) {
|
|
2484
|
+
const base = `${apiBaseUrl.replace(/\/+$/u, "")}/integrations/roam/import`;
|
|
2485
|
+
return workspaceId === void 0 ? base : `${base}?workspaceId=${encodeURIComponent(workspaceId)}`;
|
|
2486
|
+
}
|
|
2487
|
+
function parseJson(body) {
|
|
2488
|
+
try {
|
|
2489
|
+
return JSON.parse(body);
|
|
2490
|
+
} catch {
|
|
2491
|
+
throw new Error("Sift Roam import API returned invalid JSON.");
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
function responseError(parsed, status2) {
|
|
2495
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
2496
|
+
const record = parsed;
|
|
2497
|
+
const message = record.message;
|
|
2498
|
+
if (typeof message === "string" && message.trim().length > 0) return message;
|
|
2499
|
+
const error = record.error;
|
|
2500
|
+
if (typeof error === "object" && error !== null && "message" in error) {
|
|
2501
|
+
const nested = error.message;
|
|
2502
|
+
if (typeof nested === "string" && nested.trim().length > 0) return nested;
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
return `Sift Roam import API failed with status ${status2}.`;
|
|
2506
|
+
}
|
|
2507
|
+
function parseRoamImportResult(value) {
|
|
2508
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2509
|
+
throw new Error("Sift Roam import API returned an invalid result.");
|
|
2510
|
+
}
|
|
2511
|
+
const record = value;
|
|
2512
|
+
if (record.providerKind !== "roam") {
|
|
2513
|
+
throw new Error("Sift Roam import API returned an unexpected provider kind.");
|
|
2514
|
+
}
|
|
2515
|
+
return {
|
|
2516
|
+
providerKind: "roam",
|
|
2517
|
+
importedCount: integerField(record, "importedCount"),
|
|
2518
|
+
storedCount: integerField(record, "storedCount"),
|
|
2519
|
+
dedupedCount: integerField(record, "dedupedCount"),
|
|
2520
|
+
rejectedCount: integerField(record, "rejectedCount")
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
function integerField(record, key) {
|
|
2524
|
+
const value = record[key];
|
|
2525
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
2526
|
+
throw new Error(`Sift Roam import API result field ${key} must be an integer.`);
|
|
2527
|
+
}
|
|
2528
|
+
return value;
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
// src/mcpServeCommand.ts
|
|
2532
|
+
async function mcpServe(input) {
|
|
2533
|
+
if (input.mcpServer === void 0) {
|
|
2534
|
+
return fail("No local MCP server is configured for mcp.serve.");
|
|
2535
|
+
}
|
|
2536
|
+
if (input.executor === void 0) {
|
|
2537
|
+
return fail("Not signed in. Run 'sift login', then 'sift mcp serve'.");
|
|
2538
|
+
}
|
|
2539
|
+
const result = await input.mcpServer.serve({
|
|
2540
|
+
config: input.config,
|
|
2541
|
+
executor: input.executor,
|
|
2542
|
+
transport: "local_mcp"
|
|
2543
|
+
});
|
|
2544
|
+
if (result === void 0) return ok("");
|
|
2545
|
+
return ok(`${JSON.stringify(result)}
|
|
2546
|
+
`);
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
// src/scopeCurrentCommand.ts
|
|
2550
|
+
function scopeCurrent(config, json) {
|
|
2551
|
+
const scope = {
|
|
2552
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
2553
|
+
tokenLabel: config.tokenLabel,
|
|
2554
|
+
tokenExpiresAt: config.tokenExpiresAt,
|
|
2555
|
+
principalId: config.principalId,
|
|
2556
|
+
workspaceId: config.workspaceId,
|
|
2557
|
+
brainId: config.brainId,
|
|
2558
|
+
capabilities: config.capabilities
|
|
2559
|
+
};
|
|
2560
|
+
return ok(json ? `${JSON.stringify(scope)}
|
|
2561
|
+
` : renderScope(scope));
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2303
2564
|
// src/specialCommands.ts
|
|
2304
2565
|
import { readFile } from "fs/promises";
|
|
2305
2566
|
|
|
@@ -2317,24 +2578,24 @@ async function doctor(input) {
|
|
|
2317
2578
|
checks.push(readToolsCheck(availableTools3));
|
|
2318
2579
|
checks.push(writeToolsCheck(input.config, availableTools3));
|
|
2319
2580
|
checks.push(recordGetCheck(availableTools3));
|
|
2320
|
-
const
|
|
2581
|
+
const result = {
|
|
2321
2582
|
ok: !checks.some((check) => check.status === "failed"),
|
|
2322
2583
|
apiBaseUrl: input.config.apiBaseUrl.trim().length > 0 ? input.config.apiBaseUrl : void 0,
|
|
2323
2584
|
scope: scopeResult(input.config),
|
|
2324
2585
|
checks
|
|
2325
2586
|
};
|
|
2326
2587
|
return {
|
|
2327
|
-
exitCode:
|
|
2328
|
-
stdout: input.json ? `${JSON.stringify(
|
|
2329
|
-
` : renderDoctorResult(
|
|
2588
|
+
exitCode: result.ok ? 0 : 1,
|
|
2589
|
+
stdout: input.json ? `${JSON.stringify(result)}
|
|
2590
|
+
` : renderDoctorResult(result),
|
|
2330
2591
|
stderr: ""
|
|
2331
2592
|
};
|
|
2332
2593
|
}
|
|
2333
2594
|
async function discoverToolNames(executor) {
|
|
2334
2595
|
if (executor === void 0) return void 0;
|
|
2335
2596
|
if (executor.listAvailableToolNames !== void 0) return executor.listAvailableToolNames();
|
|
2336
|
-
const
|
|
2337
|
-
return toolNamesFromResult(
|
|
2597
|
+
const result = await executor.execute("tools.list", {});
|
|
2598
|
+
return toolNamesFromResult(result);
|
|
2338
2599
|
}
|
|
2339
2600
|
async function apiReachability(executor) {
|
|
2340
2601
|
if (executor === void 0) return { reachable: false, detail: "No API executor is configured." };
|
|
@@ -2348,15 +2609,15 @@ async function apiReachability(executor) {
|
|
|
2348
2609
|
};
|
|
2349
2610
|
}
|
|
2350
2611
|
}
|
|
2351
|
-
function scopeResult(
|
|
2352
|
-
if (
|
|
2612
|
+
function scopeResult(config) {
|
|
2613
|
+
if (config.workspaceId.trim().length === 0 || config.brainId.trim().length === 0 || config.principalId.trim().length === 0) {
|
|
2353
2614
|
return void 0;
|
|
2354
2615
|
}
|
|
2355
2616
|
return {
|
|
2356
|
-
workspaceId:
|
|
2357
|
-
brainId:
|
|
2358
|
-
principalId:
|
|
2359
|
-
capabilities: [...
|
|
2617
|
+
workspaceId: config.workspaceId,
|
|
2618
|
+
brainId: config.brainId,
|
|
2619
|
+
principalId: config.principalId,
|
|
2620
|
+
capabilities: [...config.capabilities]
|
|
2360
2621
|
};
|
|
2361
2622
|
}
|
|
2362
2623
|
async function checkApi(executor) {
|
|
@@ -2372,8 +2633,8 @@ async function checkApi(executor) {
|
|
|
2372
2633
|
const api = await apiReachability(executor);
|
|
2373
2634
|
return api.reachable ? { id: "api", status: "ok", label: "API", detail: "whoami succeeded." } : { id: "api", status: "failed", label: "API", detail: api.detail };
|
|
2374
2635
|
}
|
|
2375
|
-
function authCheck(
|
|
2376
|
-
if (
|
|
2636
|
+
function authCheck(config, now) {
|
|
2637
|
+
if (config.apiBaseUrl.trim().length === 0 || config.workspaceId.trim().length === 0 || config.brainId.trim().length === 0 || config.principalId.trim().length === 0) {
|
|
2377
2638
|
return {
|
|
2378
2639
|
id: "auth",
|
|
2379
2640
|
status: "failed",
|
|
@@ -2382,7 +2643,7 @@ function authCheck(config2, now) {
|
|
|
2382
2643
|
fix: "Run sift login."
|
|
2383
2644
|
};
|
|
2384
2645
|
}
|
|
2385
|
-
if (
|
|
2646
|
+
if (config.tokenExpiresAt !== void 0 && Date.parse(config.tokenExpiresAt) <= now.getTime()) {
|
|
2386
2647
|
return {
|
|
2387
2648
|
id: "auth",
|
|
2388
2649
|
status: "failed",
|
|
@@ -2393,8 +2654,8 @@ function authCheck(config2, now) {
|
|
|
2393
2654
|
}
|
|
2394
2655
|
return { id: "auth", status: "ok", label: "Auth", detail: "authenticated profile loaded." };
|
|
2395
2656
|
}
|
|
2396
|
-
function scopeCheck(
|
|
2397
|
-
if (
|
|
2657
|
+
function scopeCheck(config) {
|
|
2658
|
+
if (config.workspaceId.trim().length === 0 || config.brainId.trim().length === 0) {
|
|
2398
2659
|
return {
|
|
2399
2660
|
id: "scope",
|
|
2400
2661
|
status: "failed",
|
|
@@ -2407,14 +2668,14 @@ function scopeCheck(config2) {
|
|
|
2407
2668
|
id: "scope",
|
|
2408
2669
|
status: "ok",
|
|
2409
2670
|
label: "Scope",
|
|
2410
|
-
detail: `${
|
|
2671
|
+
detail: `${config.workspaceId}/${config.brainId}`
|
|
2411
2672
|
};
|
|
2412
2673
|
}
|
|
2413
2674
|
function readToolsCheck(names) {
|
|
2414
2675
|
return toolSetCheck("read-tools", "Read tools", ["context.assemble", "search.query"], names);
|
|
2415
2676
|
}
|
|
2416
|
-
function writeToolsCheck(
|
|
2417
|
-
const hasWrite =
|
|
2677
|
+
function writeToolsCheck(config, names) {
|
|
2678
|
+
const hasWrite = config.capabilities.includes("record:write") || config.capabilities.includes("source:write");
|
|
2418
2679
|
if (!hasWrite) {
|
|
2419
2680
|
return {
|
|
2420
2681
|
id: "write-tools",
|
|
@@ -2453,9 +2714,9 @@ function toolSetCheck(id, label, required, names) {
|
|
|
2453
2714
|
const missing = required.filter((name) => !names.includes(name));
|
|
2454
2715
|
return missing.length === 0 ? { id, status: "ok", label, detail: "required tools are available." } : { id, status: "failed", label, detail: `missing ${missing.join(", ")}.` };
|
|
2455
2716
|
}
|
|
2456
|
-
function toolNamesFromResult(
|
|
2457
|
-
if (!Array.isArray(
|
|
2458
|
-
return
|
|
2717
|
+
function toolNamesFromResult(result) {
|
|
2718
|
+
if (!Array.isArray(result)) return [];
|
|
2719
|
+
return result.flatMap((item) => {
|
|
2459
2720
|
if (typeof item === "object" && item !== null && "name" in item) {
|
|
2460
2721
|
const name = item.name;
|
|
2461
2722
|
return typeof name === "string" ? [name] : [];
|
|
@@ -2488,6 +2749,7 @@ var knownTopLevelCommands = /* @__PURE__ */ new Set([
|
|
|
2488
2749
|
"mcp",
|
|
2489
2750
|
"record",
|
|
2490
2751
|
"remember",
|
|
2752
|
+
"roam",
|
|
2491
2753
|
"scope",
|
|
2492
2754
|
"search",
|
|
2493
2755
|
"show",
|
|
@@ -2580,11 +2842,11 @@ async function ask(executor, rest, json) {
|
|
|
2580
2842
|
}
|
|
2581
2843
|
const parsed = parseOptions(rest);
|
|
2582
2844
|
const query = argsWithoutOptions(rest).join(" ").trim();
|
|
2583
|
-
const
|
|
2845
|
+
const result = await executor.execute("context.assemble", {
|
|
2584
2846
|
query,
|
|
2585
2847
|
maxChars: parseIntegerOption(parsed, "max-chars", 8e3)
|
|
2586
2848
|
});
|
|
2587
|
-
return ok(json ? aliasJson("ask", "context.assemble",
|
|
2849
|
+
return ok(json ? aliasJson("ask", "context.assemble", result) : renderSiftFound(result));
|
|
2588
2850
|
}
|
|
2589
2851
|
async function simpleSearch(executor, rest, json) {
|
|
2590
2852
|
if (executor === void 0) {
|
|
@@ -2592,11 +2854,11 @@ async function simpleSearch(executor, rest, json) {
|
|
|
2592
2854
|
}
|
|
2593
2855
|
const parsed = parseOptions(rest);
|
|
2594
2856
|
const query = argsWithoutOptions(rest).join(" ").trim();
|
|
2595
|
-
const
|
|
2857
|
+
const result = await executor.execute("search.query", {
|
|
2596
2858
|
query,
|
|
2597
2859
|
limit: parseIntegerOption(parsed, "limit", 10)
|
|
2598
2860
|
});
|
|
2599
|
-
return ok(json ? aliasJson("search", "search.query",
|
|
2861
|
+
return ok(json ? aliasJson("search", "search.query", result) : renderSearchResult(result));
|
|
2600
2862
|
}
|
|
2601
2863
|
async function remember(executor, rest, json, readStdin2, now) {
|
|
2602
2864
|
if (executor === void 0) {
|
|
@@ -2615,8 +2877,8 @@ async function remember(executor, rest, json, readStdin2, now) {
|
|
|
2615
2877
|
visibility: [optionalOption(parsed, "visibility") ?? DEFAULT_CLI_VISIBILITY],
|
|
2616
2878
|
markdown
|
|
2617
2879
|
};
|
|
2618
|
-
const
|
|
2619
|
-
return ok(json ? aliasJson("remember", "capture.text",
|
|
2880
|
+
const result = await executor.execute("capture.text", input);
|
|
2881
|
+
return ok(json ? aliasJson("remember", "capture.text", result) : renderWriteReceipt("Remember", result));
|
|
2620
2882
|
}
|
|
2621
2883
|
async function addFile(executor, fileReader, rest, json) {
|
|
2622
2884
|
if (executor === void 0) {
|
|
@@ -2638,8 +2900,8 @@ async function addFile(executor, fileReader, rest, json) {
|
|
|
2638
2900
|
bytes,
|
|
2639
2901
|
visibility: [optionalOption(parsed, "visibility") ?? DEFAULT_CLI_VISIBILITY]
|
|
2640
2902
|
};
|
|
2641
|
-
const
|
|
2642
|
-
return ok(json ? aliasJson("add", "capture.file",
|
|
2903
|
+
const result = await executor.execute("capture.file", input);
|
|
2904
|
+
return ok(json ? aliasJson("add", "capture.file", result) : renderWriteReceipt("Add", result));
|
|
2643
2905
|
}
|
|
2644
2906
|
async function edit(executor, rest, json) {
|
|
2645
2907
|
if (executor === void 0) {
|
|
@@ -2664,8 +2926,8 @@ async function edit(executor, rest, json) {
|
|
|
2664
2926
|
if (expectedMarkdown !== void 0) {
|
|
2665
2927
|
input.expectedMarkdown = expectedMarkdown;
|
|
2666
2928
|
}
|
|
2667
|
-
const
|
|
2668
|
-
return ok(json ? aliasJson("edit", "record.patch_section",
|
|
2929
|
+
const result = await executor.execute("record.patch_section", input);
|
|
2930
|
+
return ok(json ? aliasJson("edit", "record.patch_section", result) : renderWriteReceipt("Edit", result));
|
|
2669
2931
|
}
|
|
2670
2932
|
async function decide(executor, rest, json) {
|
|
2671
2933
|
if (executor === void 0) {
|
|
@@ -2678,8 +2940,8 @@ async function decide(executor, rest, json) {
|
|
|
2678
2940
|
visibility: [optionalOption(parsed, "visibility") ?? DEFAULT_CLI_VISIBILITY]
|
|
2679
2941
|
};
|
|
2680
2942
|
addOptionalWorkAliasMetadata(input, parsed);
|
|
2681
|
-
const
|
|
2682
|
-
return ok(json ? aliasJson("decide", "decision.create",
|
|
2943
|
+
const result = await executor.execute("decision.create", input);
|
|
2944
|
+
return ok(json ? aliasJson("decide", "decision.create", result) : renderWriteReceipt("Decision", result));
|
|
2683
2945
|
}
|
|
2684
2946
|
async function todo(executor, rest, json) {
|
|
2685
2947
|
if (executor === void 0) {
|
|
@@ -2696,8 +2958,8 @@ async function todo(executor, rest, json) {
|
|
|
2696
2958
|
const dueDate = optionalOption(parsed, "due-date");
|
|
2697
2959
|
if (dueDate !== void 0) input.dueDate = dueDate;
|
|
2698
2960
|
addOptionalWorkAliasMetadata(input, parsed);
|
|
2699
|
-
const
|
|
2700
|
-
return ok(json ? aliasJson("todo", "task.create",
|
|
2961
|
+
const result = await executor.execute("task.create", input);
|
|
2962
|
+
return ok(json ? aliasJson("todo", "task.create", result) : renderWriteReceipt("Task", result));
|
|
2701
2963
|
}
|
|
2702
2964
|
async function show(executor, rest, json) {
|
|
2703
2965
|
if (executor === void 0) {
|
|
@@ -2720,21 +2982,21 @@ async function show(executor, rest, json) {
|
|
|
2720
2982
|
if (sectionAnchor !== void 0) {
|
|
2721
2983
|
input.sectionAnchor = sectionAnchor;
|
|
2722
2984
|
}
|
|
2723
|
-
const
|
|
2724
|
-
return ok(json ? aliasJson("show", "record.get",
|
|
2985
|
+
const result = await executor.execute("record.get", input);
|
|
2986
|
+
return ok(json ? aliasJson("show", "record.get", result) : renderRecordResult(result));
|
|
2725
2987
|
}
|
|
2726
|
-
async function status(
|
|
2988
|
+
async function status(config, executor, json) {
|
|
2727
2989
|
const scope = {
|
|
2728
|
-
apiBaseUrl:
|
|
2729
|
-
principalId:
|
|
2730
|
-
workspaceId:
|
|
2731
|
-
brainId:
|
|
2732
|
-
capabilities: [...
|
|
2990
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
2991
|
+
principalId: config.principalId,
|
|
2992
|
+
workspaceId: config.workspaceId,
|
|
2993
|
+
brainId: config.brainId,
|
|
2994
|
+
capabilities: [...config.capabilities]
|
|
2733
2995
|
};
|
|
2734
2996
|
const api = await apiReachability(executor);
|
|
2735
|
-
const
|
|
2997
|
+
const result = { scope, api };
|
|
2736
2998
|
if (json) {
|
|
2737
|
-
return ok(`${JSON.stringify({ command: "status", result
|
|
2999
|
+
return ok(`${JSON.stringify({ command: "status", result })}
|
|
2738
3000
|
`);
|
|
2739
3001
|
}
|
|
2740
3002
|
return ok(`${renderScope({ ...scope, tokenLabel: "configured" })}API reachable: ${api.reachable}
|
|
@@ -3005,9 +3267,9 @@ function toolsHelp(input) {
|
|
|
3005
3267
|
return executeSimple(input.executor, "tools.help", { name }, input.json);
|
|
3006
3268
|
}
|
|
3007
3269
|
async function executeSimple(executor, name, toolInput, json) {
|
|
3008
|
-
const
|
|
3009
|
-
return ok(json ? `${JSON.stringify(
|
|
3010
|
-
` : `${JSON.stringify(
|
|
3270
|
+
const result = await executor.execute(name, toolInput);
|
|
3271
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3272
|
+
` : `${JSON.stringify(result)}
|
|
3011
3273
|
`);
|
|
3012
3274
|
}
|
|
3013
3275
|
|
|
@@ -3028,7 +3290,7 @@ function createHostedApiExecutor(input) {
|
|
|
3028
3290
|
body: JSON.stringify({ input: toolInput }, serializeJsonValue)
|
|
3029
3291
|
});
|
|
3030
3292
|
const body = await response.text();
|
|
3031
|
-
const parsed = body.length > 0 ?
|
|
3293
|
+
const parsed = body.length > 0 ? parseJson2(body) : {};
|
|
3032
3294
|
if (!response.ok) {
|
|
3033
3295
|
throw new Error(errorMessage(parsed, response.status));
|
|
3034
3296
|
}
|
|
@@ -3045,7 +3307,7 @@ function serializeJsonValue(_key, value) {
|
|
|
3045
3307
|
function toolUrl(apiBaseUrl, name) {
|
|
3046
3308
|
return `${apiBaseUrl.replace(/\/+$/u, "")}/agent-tools/${encodeURIComponent(name)}`;
|
|
3047
3309
|
}
|
|
3048
|
-
function
|
|
3310
|
+
function parseJson2(body) {
|
|
3049
3311
|
try {
|
|
3050
3312
|
return JSON.parse(body);
|
|
3051
3313
|
} catch {
|
|
@@ -3148,7 +3410,19 @@ async function runSiftCli(rawInput) {
|
|
|
3148
3410
|
"task:create": () => createTask(input.executor, rest, json),
|
|
3149
3411
|
"agent:register": () => agentRegister(input.executor, input.agentName, rest, json),
|
|
3150
3412
|
"agent:status": () => executeSimple2(input.executor, "agent.status", {}, json),
|
|
3151
|
-
"mcp:serve": () => mcpServe(
|
|
3413
|
+
"mcp:serve": () => mcpServe({
|
|
3414
|
+
mcpServer: input.mcpServer,
|
|
3415
|
+
config: input.config,
|
|
3416
|
+
executor: rawInput.executor
|
|
3417
|
+
}),
|
|
3418
|
+
"roam:import": () => runRoamImportCommand({
|
|
3419
|
+
rest,
|
|
3420
|
+
json,
|
|
3421
|
+
config: input.config,
|
|
3422
|
+
reader: input.roamReader,
|
|
3423
|
+
importer: input.roamImporter,
|
|
3424
|
+
now: input.now ?? /* @__PURE__ */ new Date()
|
|
3425
|
+
}),
|
|
3152
3426
|
"login:": () => authCommand(input.authCommands, "login", { rest: commandRest, json }),
|
|
3153
3427
|
"auth:status": () => authCommand(input.authCommands, "status", { json }),
|
|
3154
3428
|
"logout:": () => authCommand(input.authCommands, "logout", { json })
|
|
@@ -3172,39 +3446,14 @@ async function runSiftCli(rawInput) {
|
|
|
3172
3446
|
return errorResult(error, json);
|
|
3173
3447
|
}
|
|
3174
3448
|
}
|
|
3175
|
-
async function mcpServe(mcpServer, config2, executor, _json) {
|
|
3176
|
-
if (mcpServer === void 0) {
|
|
3177
|
-
return fail("No local MCP server is configured for mcp.serve.");
|
|
3178
|
-
}
|
|
3179
|
-
if (executor === void 0) {
|
|
3180
|
-
return fail("Not signed in. Run 'sift login', then 'sift mcp serve'.");
|
|
3181
|
-
}
|
|
3182
|
-
const result2 = await mcpServer.serve({ config: config2, executor, transport: "local_mcp" });
|
|
3183
|
-
if (result2 === void 0) return ok("");
|
|
3184
|
-
return ok(`${JSON.stringify(result2)}
|
|
3185
|
-
`);
|
|
3186
|
-
}
|
|
3187
|
-
function scopeCurrent(config2, json) {
|
|
3188
|
-
const scope = {
|
|
3189
|
-
apiBaseUrl: config2.apiBaseUrl,
|
|
3190
|
-
tokenLabel: config2.tokenLabel,
|
|
3191
|
-
tokenExpiresAt: config2.tokenExpiresAt,
|
|
3192
|
-
principalId: config2.principalId,
|
|
3193
|
-
workspaceId: config2.workspaceId,
|
|
3194
|
-
brainId: config2.brainId,
|
|
3195
|
-
capabilities: config2.capabilities
|
|
3196
|
-
};
|
|
3197
|
-
return ok(json ? `${JSON.stringify(scope)}
|
|
3198
|
-
` : renderScope(scope));
|
|
3199
|
-
}
|
|
3200
3449
|
async function searchQuery(executor, rest, json) {
|
|
3201
3450
|
if (executor === void 0) {
|
|
3202
3451
|
return fail("No Sift API executor is configured for search.query.");
|
|
3203
3452
|
}
|
|
3204
3453
|
const query = rest.join(" ").trim();
|
|
3205
|
-
const
|
|
3206
|
-
return ok(json ? `${JSON.stringify(
|
|
3207
|
-
` : renderSearchResult(
|
|
3454
|
+
const result = await executor.execute("search.query", { query, limit: 10 });
|
|
3455
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3456
|
+
` : renderSearchResult(result));
|
|
3208
3457
|
}
|
|
3209
3458
|
async function contextAssemble(executor, rest, json) {
|
|
3210
3459
|
if (executor === void 0) {
|
|
@@ -3212,12 +3461,12 @@ async function contextAssemble(executor, rest, json) {
|
|
|
3212
3461
|
}
|
|
3213
3462
|
const parsed = parseOptions(rest);
|
|
3214
3463
|
const query = positionalArgs(rest).join(" ").trim();
|
|
3215
|
-
const
|
|
3464
|
+
const result = await executor.execute("context.assemble", {
|
|
3216
3465
|
query,
|
|
3217
3466
|
maxChars: parseIntegerOption(parsed, "max-chars", 4e3)
|
|
3218
3467
|
});
|
|
3219
|
-
return ok(json ? `${JSON.stringify(
|
|
3220
|
-
` : renderSearchResult(
|
|
3468
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3469
|
+
` : renderSearchResult(result));
|
|
3221
3470
|
}
|
|
3222
3471
|
async function contextProfile(executor, rest, json) {
|
|
3223
3472
|
if (executor === void 0) {
|
|
@@ -3231,17 +3480,17 @@ async function contextProfile(executor, rest, json) {
|
|
|
3231
3480
|
if (query.length > 0) {
|
|
3232
3481
|
input.query = query;
|
|
3233
3482
|
}
|
|
3234
|
-
const
|
|
3235
|
-
return ok(json ? `${JSON.stringify(
|
|
3236
|
-
` : renderProfileResult(
|
|
3483
|
+
const result = await executor.execute("context.profile", input);
|
|
3484
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3485
|
+
` : renderProfileResult(result));
|
|
3237
3486
|
}
|
|
3238
3487
|
async function executeSimple2(executor, name, toolInput, json) {
|
|
3239
3488
|
if (executor === void 0) {
|
|
3240
3489
|
return fail(`No Sift API executor is configured for ${name}.`);
|
|
3241
3490
|
}
|
|
3242
|
-
const
|
|
3243
|
-
return ok(json ? `${JSON.stringify(
|
|
3244
|
-
` : `${JSON.stringify(
|
|
3491
|
+
const result = await executor.execute(name, toolInput);
|
|
3492
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3493
|
+
` : `${JSON.stringify(result)}
|
|
3245
3494
|
`);
|
|
3246
3495
|
}
|
|
3247
3496
|
async function captureText(executor, rest, json) {
|
|
@@ -3249,15 +3498,15 @@ async function captureText(executor, rest, json) {
|
|
|
3249
3498
|
return fail("No Sift API executor is configured for capture.text.");
|
|
3250
3499
|
}
|
|
3251
3500
|
const parsed = parseOptions(rest);
|
|
3252
|
-
const
|
|
3501
|
+
const result = await executor.execute("capture.text", {
|
|
3253
3502
|
sourceName: requireOption(parsed, "source"),
|
|
3254
3503
|
externalId: requireOption(parsed, "external-id"),
|
|
3255
3504
|
title: requireOption(parsed, "title"),
|
|
3256
3505
|
visibility: [requireOption(parsed, "visibility")],
|
|
3257
3506
|
markdown: requireOption(parsed, "markdown")
|
|
3258
3507
|
});
|
|
3259
|
-
return ok(json ? `${JSON.stringify(
|
|
3260
|
-
` : `${JSON.stringify(
|
|
3508
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3509
|
+
` : `${JSON.stringify(result)}
|
|
3261
3510
|
`);
|
|
3262
3511
|
}
|
|
3263
3512
|
async function captureFile(executor, fileReader, rest, json) {
|
|
@@ -3270,7 +3519,7 @@ async function captureFile(executor, fileReader, rest, json) {
|
|
|
3270
3519
|
}
|
|
3271
3520
|
const parsed = parseOptions(optionArgs);
|
|
3272
3521
|
const bytes = await fileReader(path);
|
|
3273
|
-
const
|
|
3522
|
+
const result = await executor.execute("capture.file", {
|
|
3274
3523
|
sourceName: requireOption(parsed, "source"),
|
|
3275
3524
|
externalId: requireOption(parsed, "external-id"),
|
|
3276
3525
|
title: requireOption(parsed, "title"),
|
|
@@ -3279,8 +3528,8 @@ async function captureFile(executor, fileReader, rest, json) {
|
|
|
3279
3528
|
bytes,
|
|
3280
3529
|
visibility: [requireOption(parsed, "visibility")]
|
|
3281
3530
|
});
|
|
3282
|
-
return ok(json ? `${JSON.stringify(
|
|
3283
|
-
` : `${JSON.stringify(
|
|
3531
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3532
|
+
` : `${JSON.stringify(result)}
|
|
3284
3533
|
`);
|
|
3285
3534
|
}
|
|
3286
3535
|
async function captureBatch(executor, fileReader, rest, json) {
|
|
@@ -3292,9 +3541,9 @@ async function captureBatch(executor, fileReader, rest, json) {
|
|
|
3292
3541
|
return fail("Missing required manifest path for capture.batch.");
|
|
3293
3542
|
}
|
|
3294
3543
|
const manifest = parseBatchManifest(await fileReader(manifestPath));
|
|
3295
|
-
const
|
|
3296
|
-
return ok(json ? `${JSON.stringify(
|
|
3297
|
-
` : `${JSON.stringify(
|
|
3544
|
+
const result = await executor.execute("capture.batch", { items: manifest });
|
|
3545
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3546
|
+
` : `${JSON.stringify(result)}
|
|
3298
3547
|
`);
|
|
3299
3548
|
}
|
|
3300
3549
|
function parseBatchManifest(bytes) {
|
|
@@ -3319,18 +3568,18 @@ async function createDecision(executor, rest, json) {
|
|
|
3319
3568
|
input.rationale = rationale;
|
|
3320
3569
|
}
|
|
3321
3570
|
addOptionalWorkMetadata(input, parsed);
|
|
3322
|
-
const
|
|
3323
|
-
return ok(json ? `${JSON.stringify(
|
|
3324
|
-
` : `${JSON.stringify(
|
|
3571
|
+
const result = await executor.execute("decision.create", input);
|
|
3572
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3573
|
+
` : `${JSON.stringify(result)}
|
|
3325
3574
|
`);
|
|
3326
3575
|
}
|
|
3327
3576
|
async function sourceList(executor, json) {
|
|
3328
3577
|
if (executor === void 0) {
|
|
3329
3578
|
return fail("No Sift API executor is configured for source.list.");
|
|
3330
3579
|
}
|
|
3331
|
-
const
|
|
3332
|
-
return ok(json ? `${JSON.stringify(
|
|
3333
|
-
` : `${JSON.stringify(
|
|
3580
|
+
const result = await executor.execute("source.list", {});
|
|
3581
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3582
|
+
` : `${JSON.stringify(result)}
|
|
3334
3583
|
`);
|
|
3335
3584
|
}
|
|
3336
3585
|
async function sourceCreate(executor, rest, json) {
|
|
@@ -3338,12 +3587,12 @@ async function sourceCreate(executor, rest, json) {
|
|
|
3338
3587
|
return fail("No Sift API executor is configured for source.create.");
|
|
3339
3588
|
}
|
|
3340
3589
|
const parsed = parseOptions(rest);
|
|
3341
|
-
const
|
|
3590
|
+
const result = await executor.execute("source.create", {
|
|
3342
3591
|
name: requireOption(parsed, "name"),
|
|
3343
3592
|
visibility: [requireOption(parsed, "visibility")]
|
|
3344
3593
|
});
|
|
3345
|
-
return ok(json ? `${JSON.stringify(
|
|
3346
|
-
` : `${JSON.stringify(
|
|
3594
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3595
|
+
` : `${JSON.stringify(result)}
|
|
3347
3596
|
`);
|
|
3348
3597
|
}
|
|
3349
3598
|
async function sourceRead(executor, command, rest, json) {
|
|
@@ -3355,9 +3604,9 @@ async function sourceRead(executor, command, rest, json) {
|
|
|
3355
3604
|
if (sourceId === void 0 || sourceId.trim().length === 0) {
|
|
3356
3605
|
return fail(`Missing required source ID for ${toolName}.`);
|
|
3357
3606
|
}
|
|
3358
|
-
const
|
|
3359
|
-
return ok(json ? `${JSON.stringify(
|
|
3360
|
-
` : `${JSON.stringify(
|
|
3607
|
+
const result = await executor.execute(toolName, { sourceId });
|
|
3608
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3609
|
+
` : `${JSON.stringify(result)}
|
|
3361
3610
|
`);
|
|
3362
3611
|
}
|
|
3363
3612
|
async function ingestionStatus(executor, rest, json) {
|
|
@@ -3368,18 +3617,18 @@ async function ingestionStatus(executor, rest, json) {
|
|
|
3368
3617
|
if (jobId === void 0 || jobId.trim().length === 0) {
|
|
3369
3618
|
return fail("Missing required job ID for ingestion.status.");
|
|
3370
3619
|
}
|
|
3371
|
-
const
|
|
3372
|
-
return ok(json ? `${JSON.stringify(
|
|
3373
|
-
` : `${JSON.stringify(
|
|
3620
|
+
const result = await executor.execute("ingestion.status", { jobId });
|
|
3621
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3622
|
+
` : `${JSON.stringify(result)}
|
|
3374
3623
|
`);
|
|
3375
3624
|
}
|
|
3376
3625
|
async function recordList(executor, json) {
|
|
3377
3626
|
if (executor === void 0) {
|
|
3378
3627
|
return fail("No Sift API executor is configured for record.list.");
|
|
3379
3628
|
}
|
|
3380
|
-
const
|
|
3381
|
-
return ok(json ? `${JSON.stringify(
|
|
3382
|
-
` : `${JSON.stringify(
|
|
3629
|
+
const result = await executor.execute("record.list", {});
|
|
3630
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3631
|
+
` : `${JSON.stringify(result)}
|
|
3383
3632
|
`);
|
|
3384
3633
|
}
|
|
3385
3634
|
async function recordRead(executor, toolName, rest, json) {
|
|
@@ -3397,23 +3646,23 @@ async function recordRead(executor, toolName, rest, json) {
|
|
|
3397
3646
|
input.sectionAnchor = sectionAnchor;
|
|
3398
3647
|
}
|
|
3399
3648
|
}
|
|
3400
|
-
const
|
|
3401
|
-
return ok(json ? `${JSON.stringify(
|
|
3402
|
-
` : renderRecordResult(
|
|
3649
|
+
const result = await executor.execute(toolName, input);
|
|
3650
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3651
|
+
` : renderRecordResult(result));
|
|
3403
3652
|
}
|
|
3404
3653
|
async function createMarkdownRecord(executor, rest, json) {
|
|
3405
3654
|
if (executor === void 0) {
|
|
3406
3655
|
return fail("No Sift API executor is configured for record.create_markdown.");
|
|
3407
3656
|
}
|
|
3408
3657
|
const parsed = parseOptions(rest);
|
|
3409
|
-
const
|
|
3658
|
+
const result = await executor.execute("record.create_markdown", {
|
|
3410
3659
|
recordType: requireOption(parsed, "type"),
|
|
3411
3660
|
title: requireOption(parsed, "title"),
|
|
3412
3661
|
markdown: requireOption(parsed, "markdown"),
|
|
3413
3662
|
visibility: [requireOption(parsed, "visibility")]
|
|
3414
3663
|
});
|
|
3415
|
-
return ok(json ? `${JSON.stringify(
|
|
3416
|
-
` : `${JSON.stringify(
|
|
3664
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3665
|
+
` : `${JSON.stringify(result)}
|
|
3417
3666
|
`);
|
|
3418
3667
|
}
|
|
3419
3668
|
async function patchRecordSection(executor, rest, json) {
|
|
@@ -3437,9 +3686,9 @@ async function patchRecordSection(executor, rest, json) {
|
|
|
3437
3686
|
if (expectedMarkdown !== void 0) {
|
|
3438
3687
|
input.expectedMarkdown = expectedMarkdown;
|
|
3439
3688
|
}
|
|
3440
|
-
const
|
|
3441
|
-
return ok(json ? `${JSON.stringify(
|
|
3442
|
-
` : `${JSON.stringify(
|
|
3689
|
+
const result = await executor.execute("record.patch_section", input);
|
|
3690
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3691
|
+
` : `${JSON.stringify(result)}
|
|
3443
3692
|
`);
|
|
3444
3693
|
}
|
|
3445
3694
|
async function createTask(executor, rest, json) {
|
|
@@ -3468,9 +3717,9 @@ async function createTask(executor, rest, json) {
|
|
|
3468
3717
|
input.rationale = rationale;
|
|
3469
3718
|
}
|
|
3470
3719
|
addOptionalWorkMetadata(input, parsed);
|
|
3471
|
-
const
|
|
3472
|
-
return ok(json ? `${JSON.stringify(
|
|
3473
|
-
` : `${JSON.stringify(
|
|
3720
|
+
const result = await executor.execute("task.create", input);
|
|
3721
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3722
|
+
` : `${JSON.stringify(result)}
|
|
3474
3723
|
`);
|
|
3475
3724
|
}
|
|
3476
3725
|
async function auditEvents(executor, rest, json) {
|
|
@@ -3482,9 +3731,9 @@ async function auditEvents(executor, rest, json) {
|
|
|
3482
3731
|
if (targetId !== void 0 && targetId.trim().length > 0) {
|
|
3483
3732
|
input.targetId = targetId;
|
|
3484
3733
|
}
|
|
3485
|
-
const
|
|
3486
|
-
return ok(json ? `${JSON.stringify(
|
|
3487
|
-
` : `${JSON.stringify(
|
|
3734
|
+
const result = await executor.execute("audit.events", input);
|
|
3735
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3736
|
+
` : `${JSON.stringify(result)}
|
|
3488
3737
|
`);
|
|
3489
3738
|
}
|
|
3490
3739
|
async function idTool(input) {
|
|
@@ -3495,9 +3744,9 @@ async function idTool(input) {
|
|
|
3495
3744
|
if (id === void 0 || id.trim().length === 0) {
|
|
3496
3745
|
return fail(`Missing required ${input.idLabel} for ${input.toolName}.`);
|
|
3497
3746
|
}
|
|
3498
|
-
const
|
|
3499
|
-
return ok(input.json ? `${JSON.stringify(
|
|
3500
|
-
` : `${JSON.stringify(
|
|
3747
|
+
const result = await input.executor.execute(input.toolName, { [input.inputKey]: id });
|
|
3748
|
+
return ok(input.json ? `${JSON.stringify(result)}
|
|
3749
|
+
` : `${JSON.stringify(result)}
|
|
3501
3750
|
`);
|
|
3502
3751
|
}
|
|
3503
3752
|
|
|
@@ -3523,10 +3772,10 @@ async function readStoredSiftConfig(input) {
|
|
|
3523
3772
|
return parseStoredSiftConfig(JSON.parse(raw));
|
|
3524
3773
|
}
|
|
3525
3774
|
async function writeStoredSiftConfig(input) {
|
|
3526
|
-
const
|
|
3775
|
+
const config = parseStoredSiftConfig(input.config);
|
|
3527
3776
|
const path = resolveSiftConfigPath(input);
|
|
3528
3777
|
await mkdir(dirname(path), { recursive: true, mode: 448 });
|
|
3529
|
-
await writeFile(path, `${JSON.stringify(
|
|
3778
|
+
await writeFile(path, `${JSON.stringify(config, null, 2)}
|
|
3530
3779
|
`, { mode: 384 });
|
|
3531
3780
|
await chmod(path, 384);
|
|
3532
3781
|
}
|
|
@@ -3747,14 +3996,14 @@ function createMacOSKeychainStore(input = {}) {
|
|
|
3747
3996
|
return {
|
|
3748
3997
|
async assertAvailable() {
|
|
3749
3998
|
await requireSupported();
|
|
3750
|
-
const
|
|
3751
|
-
if (
|
|
3999
|
+
const result = await runCommand(securityPath, ["list-keychains"]);
|
|
4000
|
+
if (result.exitCode !== 0) {
|
|
3752
4001
|
throw new UnsupportedCredentialStoreError();
|
|
3753
4002
|
}
|
|
3754
4003
|
},
|
|
3755
4004
|
async read(readInput) {
|
|
3756
4005
|
await requireSupported();
|
|
3757
|
-
const
|
|
4006
|
+
const result = await runCommand(securityPath, [
|
|
3758
4007
|
"find-generic-password",
|
|
3759
4008
|
"-s",
|
|
3760
4009
|
serviceName,
|
|
@@ -3762,15 +4011,15 @@ function createMacOSKeychainStore(input = {}) {
|
|
|
3762
4011
|
account(readInput),
|
|
3763
4012
|
"-w"
|
|
3764
4013
|
]);
|
|
3765
|
-
if (
|
|
4014
|
+
if (result.exitCode !== 0) {
|
|
3766
4015
|
return void 0;
|
|
3767
4016
|
}
|
|
3768
|
-
const secret =
|
|
4017
|
+
const secret = result.stdout.trim();
|
|
3769
4018
|
return secret.length === 0 ? void 0 : secret;
|
|
3770
4019
|
},
|
|
3771
4020
|
async write(writeInput) {
|
|
3772
4021
|
await requireSupported();
|
|
3773
|
-
const
|
|
4022
|
+
const result = await runCommand(securityPath, [
|
|
3774
4023
|
"add-generic-password",
|
|
3775
4024
|
"-U",
|
|
3776
4025
|
"-s",
|
|
@@ -3780,7 +4029,7 @@ function createMacOSKeychainStore(input = {}) {
|
|
|
3780
4029
|
"-w",
|
|
3781
4030
|
writeInput.secret
|
|
3782
4031
|
]);
|
|
3783
|
-
if (
|
|
4032
|
+
if (result.exitCode !== 0) {
|
|
3784
4033
|
throw new Error("Failed to write Sift CLI token secret to macOS Keychain.");
|
|
3785
4034
|
}
|
|
3786
4035
|
},
|
|
@@ -3798,8 +4047,8 @@ function createMacOSKeychainStore(input = {}) {
|
|
|
3798
4047
|
}
|
|
3799
4048
|
async function runSecurityCommand(file, args) {
|
|
3800
4049
|
try {
|
|
3801
|
-
const
|
|
3802
|
-
return { stdout:
|
|
4050
|
+
const result = await execFileAsync(file, args);
|
|
4051
|
+
return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
|
|
3803
4052
|
} catch (error) {
|
|
3804
4053
|
if (isExecError(error)) {
|
|
3805
4054
|
return {
|
|
@@ -3824,6 +4073,7 @@ import { hostname as hostname3 } from "os";
|
|
|
3824
4073
|
import { promisify as promisify2 } from "util";
|
|
3825
4074
|
|
|
3826
4075
|
// src/auth/loginHelpers.ts
|
|
4076
|
+
var DEFAULT_SIFT_API_BASE_URL = "https://sift-wiki-api.fly.dev";
|
|
3827
4077
|
async function resolveLoginApiBaseUrl(input) {
|
|
3828
4078
|
const options = parseOptions(input.argv);
|
|
3829
4079
|
const fromFlag = clean2(options.get("api-base-url"));
|
|
@@ -3833,7 +4083,7 @@ async function resolveLoginApiBaseUrl(input) {
|
|
|
3833
4083
|
const stored = await readStoredSiftConfig({ homeDir: input.homeDir });
|
|
3834
4084
|
const profile = stored?.profiles[stored.currentProfile];
|
|
3835
4085
|
if (profile !== void 0) return normalizeUrl(profile.apiBaseUrl);
|
|
3836
|
-
return
|
|
4086
|
+
return DEFAULT_SIFT_API_BASE_URL;
|
|
3837
4087
|
}
|
|
3838
4088
|
function requestedCapabilities(rest) {
|
|
3839
4089
|
const option = parseOptions(rest).get("capability");
|
|
@@ -3874,15 +4124,15 @@ function resolveCliOAuthConfig(input) {
|
|
|
3874
4124
|
return void 0;
|
|
3875
4125
|
}
|
|
3876
4126
|
const registrationUrl = clean3(options.get("oauth-registration-url")) ?? clean3(input.env.SIFT_OAUTH_REGISTRATION_URL);
|
|
3877
|
-
const
|
|
4127
|
+
const config = { authorizeUrl, tokenUrl, clientId };
|
|
3878
4128
|
if (registrationUrl !== void 0) {
|
|
3879
|
-
|
|
4129
|
+
config.registrationUrl = registrationUrl;
|
|
3880
4130
|
}
|
|
3881
4131
|
const scopes = parseScopeList(clean3(options.get("oauth-scopes")) ?? clean3(input.env.SIFT_OAUTH_SCOPES));
|
|
3882
4132
|
if (scopes.length > 0) {
|
|
3883
|
-
|
|
4133
|
+
config.defaultScopes = scopes;
|
|
3884
4134
|
}
|
|
3885
|
-
return
|
|
4135
|
+
return config;
|
|
3886
4136
|
}
|
|
3887
4137
|
function scopesForCapabilities(capabilities) {
|
|
3888
4138
|
const scopes = /* @__PURE__ */ new Set(["read"]);
|
|
@@ -4057,11 +4307,11 @@ async function finalizeOAuthLogin(input, tokens) {
|
|
|
4057
4307
|
tokenKind: "oauth",
|
|
4058
4308
|
refreshable: tokens.refreshToken !== void 0
|
|
4059
4309
|
};
|
|
4060
|
-
const
|
|
4310
|
+
const result = { profile, accessToken: tokens.accessToken };
|
|
4061
4311
|
if (tokens.refreshToken !== void 0) {
|
|
4062
|
-
|
|
4312
|
+
result.refreshToken = tokens.refreshToken;
|
|
4063
4313
|
}
|
|
4064
|
-
return
|
|
4314
|
+
return result;
|
|
4065
4315
|
}
|
|
4066
4316
|
function buildAuthorizeUrl(input) {
|
|
4067
4317
|
const url = new URL(input.oauth.authorizeUrl);
|
|
@@ -4294,7 +4544,7 @@ function oauthRefresherFor(input, rest) {
|
|
|
4294
4544
|
async function oauthBrowserLoginFlow(input, rest, json) {
|
|
4295
4545
|
const apiBaseUrl = await resolveLoginApiBaseUrl({ argv: rest, env: input.env, homeDir: input.homeDir });
|
|
4296
4546
|
const oauth = resolveOAuthConfigOrThrow(input, rest);
|
|
4297
|
-
const
|
|
4547
|
+
const result = await oauthBrowserLogin({
|
|
4298
4548
|
apiBaseUrl,
|
|
4299
4549
|
appBaseUrl: resolveAppBaseUrl(input.env, apiBaseUrl),
|
|
4300
4550
|
oauth,
|
|
@@ -4309,16 +4559,16 @@ async function oauthBrowserLoginFlow(input, rest, json) {
|
|
|
4309
4559
|
return persistConvergedLogin(
|
|
4310
4560
|
input,
|
|
4311
4561
|
{
|
|
4312
|
-
profile:
|
|
4313
|
-
accessToken:
|
|
4314
|
-
...
|
|
4562
|
+
profile: result.profile,
|
|
4563
|
+
accessToken: result.accessToken,
|
|
4564
|
+
...result.refreshToken === void 0 ? {} : { refreshToken: result.refreshToken }
|
|
4315
4565
|
},
|
|
4316
4566
|
json
|
|
4317
4567
|
);
|
|
4318
4568
|
}
|
|
4319
4569
|
async function serviceTokenLoginFlow(input, rest, json) {
|
|
4320
4570
|
const apiBaseUrl = await resolveLoginApiBaseUrl({ argv: rest, env: input.env, homeDir: input.homeDir });
|
|
4321
|
-
const
|
|
4571
|
+
const result = await serviceTokenLogin({
|
|
4322
4572
|
apiBaseUrl,
|
|
4323
4573
|
appBaseUrl: resolveAppBaseUrl(input.env, apiBaseUrl),
|
|
4324
4574
|
rest,
|
|
@@ -4327,7 +4577,7 @@ async function serviceTokenLoginFlow(input, rest, json) {
|
|
|
4327
4577
|
credentialStore: input.credentialStore,
|
|
4328
4578
|
resolveCallerBearer: defaultCallerBearerResolver(input)
|
|
4329
4579
|
});
|
|
4330
|
-
return persistConvergedLogin(input, { profile:
|
|
4580
|
+
return persistConvergedLogin(input, { profile: result.profile, accessToken: result.token }, json);
|
|
4331
4581
|
}
|
|
4332
4582
|
function resolveOAuthConfigOrThrow(input, rest) {
|
|
4333
4583
|
const oauth = input.oauthConfig ?? resolveCliOAuthConfig({ argv: rest, env: input.env });
|
|
@@ -4388,19 +4638,19 @@ function resolveAppBaseUrl(env, apiBaseUrl) {
|
|
|
4388
4638
|
if (fromEnv !== void 0) return normalizeUrl(fromEnv);
|
|
4389
4639
|
return apiBaseUrl.replace(/\/\/api\./u, "//");
|
|
4390
4640
|
}
|
|
4391
|
-
async function persistConvergedLogin(input,
|
|
4392
|
-
const { profile } =
|
|
4641
|
+
async function persistConvergedLogin(input, result, json) {
|
|
4642
|
+
const { profile } = result;
|
|
4393
4643
|
try {
|
|
4394
4644
|
await input.credentialStore.write({
|
|
4395
4645
|
apiBaseUrl: profile.apiBaseUrl,
|
|
4396
4646
|
tokenId: profile.tokenId,
|
|
4397
|
-
secret:
|
|
4647
|
+
secret: result.accessToken
|
|
4398
4648
|
});
|
|
4399
|
-
if (
|
|
4649
|
+
if (result.refreshToken !== void 0) {
|
|
4400
4650
|
await input.credentialStore.write({
|
|
4401
4651
|
apiBaseUrl: profile.apiBaseUrl,
|
|
4402
4652
|
tokenId: refreshSlotTokenId(profile.tokenId),
|
|
4403
|
-
secret:
|
|
4653
|
+
secret: result.refreshToken
|
|
4404
4654
|
});
|
|
4405
4655
|
}
|
|
4406
4656
|
} catch (error) {
|
|
@@ -4510,9 +4760,9 @@ async function deviceLogin(input, rest, sleep, json) {
|
|
|
4510
4760
|
{ requestId: request.requestId, userCode: request.userCode }
|
|
4511
4761
|
);
|
|
4512
4762
|
if ("token" in token) {
|
|
4513
|
-
const
|
|
4514
|
-
return
|
|
4515
|
-
${
|
|
4763
|
+
const result = await persistLogin(input, token, json);
|
|
4764
|
+
return result.exitCode === 0 ? { ...result, stdout: `Code: ${request.userCode}
|
|
4765
|
+
${result.stdout}` } : result;
|
|
4516
4766
|
}
|
|
4517
4767
|
if (token.status === "authorization_pending" || token.status === "slow_down") {
|
|
4518
4768
|
intervalSeconds = token.intervalSeconds;
|
|
@@ -4536,8 +4786,8 @@ async function persistLogin(input, token, json) {
|
|
|
4536
4786
|
`Sift CLI login storage failure: ${error instanceof Error ? error.message : "credential store write failed"}`
|
|
4537
4787
|
);
|
|
4538
4788
|
}
|
|
4539
|
-
const
|
|
4540
|
-
await writeStoredSiftConfig({ homeDir: input.homeDir, config
|
|
4789
|
+
const config = configFromToken(token);
|
|
4790
|
+
await writeStoredSiftConfig({ homeDir: input.homeDir, config });
|
|
4541
4791
|
if (oldProfile !== void 0) {
|
|
4542
4792
|
const oldSecret = await input.credentialStore.read({
|
|
4543
4793
|
apiBaseUrl: oldProfile.apiBaseUrl,
|
|
@@ -4576,6 +4826,33 @@ async function authStatus(input, now, json) {
|
|
|
4576
4826
|
if (profile === void 0) {
|
|
4577
4827
|
return ok(json ? '{"auth":"none"}\n' : "Auth: none\n");
|
|
4578
4828
|
}
|
|
4829
|
+
const expired = Date.parse(profile.tokenExpiresAt) <= now.getTime();
|
|
4830
|
+
if (expired) {
|
|
4831
|
+
return staleStoredStatus(
|
|
4832
|
+
profile,
|
|
4833
|
+
"expired",
|
|
4834
|
+
"Stored Sift CLI auth has expired; run `sift login` again.",
|
|
4835
|
+
json
|
|
4836
|
+
);
|
|
4837
|
+
}
|
|
4838
|
+
let secret;
|
|
4839
|
+
try {
|
|
4840
|
+
secret = await input.credentialStore.read({
|
|
4841
|
+
apiBaseUrl: profile.apiBaseUrl,
|
|
4842
|
+
tokenId: profile.tokenId
|
|
4843
|
+
});
|
|
4844
|
+
} catch (error) {
|
|
4845
|
+
const message = error instanceof Error ? error.message : "Stored Sift credential store could not be read; run `sift login` again.";
|
|
4846
|
+
return staleStoredStatus(profile, "credential_store_unavailable", message, json);
|
|
4847
|
+
}
|
|
4848
|
+
if (secret === void 0) {
|
|
4849
|
+
return staleStoredStatus(
|
|
4850
|
+
profile,
|
|
4851
|
+
"credential_missing",
|
|
4852
|
+
"Stored Sift credential store secret is missing; run `sift login` again.",
|
|
4853
|
+
json
|
|
4854
|
+
);
|
|
4855
|
+
}
|
|
4579
4856
|
return ok(
|
|
4580
4857
|
json ? `${JSON.stringify({ auth: "stored", ...profile })}
|
|
4581
4858
|
` : [
|
|
@@ -4590,6 +4867,24 @@ async function authStatus(input, now, json) {
|
|
|
4590
4867
|
].join("\n")
|
|
4591
4868
|
);
|
|
4592
4869
|
}
|
|
4870
|
+
function staleStoredStatus(profile, reason, message, json) {
|
|
4871
|
+
if (json) {
|
|
4872
|
+
return ok(
|
|
4873
|
+
`${JSON.stringify({ auth: "stored", status: "stale", reason, message, ...profile })}
|
|
4874
|
+
`
|
|
4875
|
+
);
|
|
4876
|
+
}
|
|
4877
|
+
return ok(
|
|
4878
|
+
[
|
|
4879
|
+
"Auth: stale",
|
|
4880
|
+
`Reason: ${reason}`,
|
|
4881
|
+
message,
|
|
4882
|
+
`API: ${profile.apiBaseUrl}`,
|
|
4883
|
+
`Token: ${profile.tokenLabel}`,
|
|
4884
|
+
""
|
|
4885
|
+
].join("\n")
|
|
4886
|
+
);
|
|
4887
|
+
}
|
|
4593
4888
|
async function logout(input, now, json) {
|
|
4594
4889
|
if (clean2(input.env.SIFT_API_TOKEN) !== void 0) {
|
|
4595
4890
|
return ok(json ? '{"status":"env_auth_active"}\n' : "Auth: env\nUnset SIFT_API_TOKEN to log out.\n");
|
|
@@ -4681,6 +4976,244 @@ function failJson(message) {
|
|
|
4681
4976
|
};
|
|
4682
4977
|
}
|
|
4683
4978
|
|
|
4979
|
+
// src/roamMcpReader.ts
|
|
4980
|
+
import { spawn } from "child_process";
|
|
4981
|
+
function createRoamMcpReader(input = {}) {
|
|
4982
|
+
return {
|
|
4983
|
+
async exportPages(request) {
|
|
4984
|
+
const client = createRoamMcpJsonLineClient({
|
|
4985
|
+
command: input.command ?? "npx",
|
|
4986
|
+
args: input.args ?? ["-y", "@roam-research/roam-mcp"],
|
|
4987
|
+
spawnProcess: input.spawnProcess ?? spawn
|
|
4988
|
+
});
|
|
4989
|
+
try {
|
|
4990
|
+
return await exportRoamPagesFromMcp(client, request);
|
|
4991
|
+
} finally {
|
|
4992
|
+
await client.close();
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
};
|
|
4996
|
+
}
|
|
4997
|
+
async function exportRoamPagesFromMcp(client, input) {
|
|
4998
|
+
const graphId = await resolveGraphId(client, input.graph);
|
|
4999
|
+
await client.callTool("get_graph_guidelines", graphArgs(graphId)).catch(() => void 0);
|
|
5000
|
+
const tuples = input.scope === "sift_tag" ? await queryMarkedPageTuples(client, graphId, input.limit) : await queryWholeGraphPageTuples(client, graphId, input.limit);
|
|
5001
|
+
const records = [];
|
|
5002
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5003
|
+
for (const tuple of tuples) {
|
|
5004
|
+
const key = tuple.uid ?? tuple.title;
|
|
5005
|
+
if (seen.has(key)) continue;
|
|
5006
|
+
seen.add(key);
|
|
5007
|
+
const markdown = await fetchPageMarkdown(client, graphId, tuple);
|
|
5008
|
+
records.push({
|
|
5009
|
+
graphId,
|
|
5010
|
+
pageUid: tuple.uid ?? stableFallbackUid(tuple.title),
|
|
5011
|
+
pageTitle: tuple.title,
|
|
5012
|
+
markdown,
|
|
5013
|
+
scope: input.scope,
|
|
5014
|
+
blockCount: estimateBlockCount(markdown),
|
|
5015
|
+
importedAt: input.now.toISOString()
|
|
5016
|
+
});
|
|
5017
|
+
}
|
|
5018
|
+
return records;
|
|
5019
|
+
}
|
|
5020
|
+
async function resolveGraphId(client, graph) {
|
|
5021
|
+
if (graph !== void 0) return graph;
|
|
5022
|
+
const result = await client.callTool("list_graphs", {});
|
|
5023
|
+
const parsed = parseToolJson(result);
|
|
5024
|
+
const graphId = firstGraphId(parsed);
|
|
5025
|
+
if (graphId !== void 0) return graphId;
|
|
5026
|
+
const message = errorMessageFromParsedTool(parsed);
|
|
5027
|
+
if (message !== void 0) throw new Error(message);
|
|
5028
|
+
return "local-graph";
|
|
5029
|
+
}
|
|
5030
|
+
async function queryMarkedPageTuples(client, graphId, limit) {
|
|
5031
|
+
const result = await client.callTool("datalog_query", {
|
|
5032
|
+
...graphArgs(graphId),
|
|
5033
|
+
query: '[:find ?title ?uid :where [?tag :node/title "Sift"] [?block :block/refs ?tag] [?block :block/page ?page] [?page :node/title ?title] [?page :block/uid ?uid]]'
|
|
5034
|
+
});
|
|
5035
|
+
return tuplesFromToolResult(result).slice(0, limit);
|
|
5036
|
+
}
|
|
5037
|
+
async function queryWholeGraphPageTuples(client, graphId, limit) {
|
|
5038
|
+
const result = await client.callTool("datalog_query", {
|
|
5039
|
+
...graphArgs(graphId),
|
|
5040
|
+
query: "[:find ?title ?uid :where [?page :node/title ?title] [?page :block/uid ?uid]]"
|
|
5041
|
+
});
|
|
5042
|
+
return tuplesFromToolResult(result).slice(0, limit);
|
|
5043
|
+
}
|
|
5044
|
+
async function fetchPageMarkdown(client, graphId, tuple) {
|
|
5045
|
+
const result = await client.callTool("get_page", {
|
|
5046
|
+
...graphArgs(graphId),
|
|
5047
|
+
...tuple.uid === void 0 ? { title: tuple.title } : { uid: tuple.uid }
|
|
5048
|
+
});
|
|
5049
|
+
const text = toolText(result);
|
|
5050
|
+
const parsed = parseJsonIfPossible(text);
|
|
5051
|
+
const markdown = markdownFromParsed(parsed) ?? text;
|
|
5052
|
+
return stripRoamMetadataTags(markdown).trim();
|
|
5053
|
+
}
|
|
5054
|
+
function graphArgs(graphId) {
|
|
5055
|
+
return { graph: graphId };
|
|
5056
|
+
}
|
|
5057
|
+
function tuplesFromToolResult(result) {
|
|
5058
|
+
const parsed = parseToolJson(result);
|
|
5059
|
+
const values = candidateArrays(parsed);
|
|
5060
|
+
const tuples = [];
|
|
5061
|
+
for (const value of values) {
|
|
5062
|
+
if (Array.isArray(value) && typeof value[0] === "string") {
|
|
5063
|
+
tuples.push({ title: value[0], ...typeof value[1] === "string" ? { uid: value[1] } : {} });
|
|
5064
|
+
} else if (isRecord(value)) {
|
|
5065
|
+
const title = stringProperty(value, ["title", "pageTitle", "name"]);
|
|
5066
|
+
const uid = stringProperty(value, ["uid", "pageUid"]);
|
|
5067
|
+
if (title !== void 0) tuples.push({ title, ...uid === void 0 ? {} : { uid } });
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
return tuples;
|
|
5071
|
+
}
|
|
5072
|
+
function parseToolJson(result) {
|
|
5073
|
+
const text = toolText(result);
|
|
5074
|
+
return parseJsonIfPossible(text);
|
|
5075
|
+
}
|
|
5076
|
+
function toolText(result) {
|
|
5077
|
+
if (isRecord(result) && Array.isArray(result.content)) {
|
|
5078
|
+
return result.content.flatMap((item) => isRecord(item) && typeof item.text === "string" ? [item.text] : []).join("\n").trim();
|
|
5079
|
+
}
|
|
5080
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
5081
|
+
}
|
|
5082
|
+
function parseJsonIfPossible(text) {
|
|
5083
|
+
try {
|
|
5084
|
+
return JSON.parse(text);
|
|
5085
|
+
} catch {
|
|
5086
|
+
return text;
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
function candidateArrays(value) {
|
|
5090
|
+
if (Array.isArray(value)) return value;
|
|
5091
|
+
if (!isRecord(value)) return [];
|
|
5092
|
+
for (const key of ["results", "result", "data", "rows", "pages"]) {
|
|
5093
|
+
const candidate = value[key];
|
|
5094
|
+
if (Array.isArray(candidate)) return candidate;
|
|
5095
|
+
}
|
|
5096
|
+
return [];
|
|
5097
|
+
}
|
|
5098
|
+
function markdownFromParsed(value) {
|
|
5099
|
+
if (typeof value === "string") return value;
|
|
5100
|
+
if (!isRecord(value)) return void 0;
|
|
5101
|
+
return stringProperty(value, ["markdown", "content", "text"]);
|
|
5102
|
+
}
|
|
5103
|
+
function firstGraphId(value) {
|
|
5104
|
+
const graphs = isRecord(value) && Array.isArray(value.graphs) ? value.graphs : candidateArrays(value);
|
|
5105
|
+
for (const graph of graphs) {
|
|
5106
|
+
if (!isRecord(graph)) continue;
|
|
5107
|
+
const id = stringProperty(graph, ["nickname", "graph", "name", "id"]);
|
|
5108
|
+
if (id !== void 0) return id;
|
|
5109
|
+
}
|
|
5110
|
+
return void 0;
|
|
5111
|
+
}
|
|
5112
|
+
function errorMessageFromParsedTool(value) {
|
|
5113
|
+
if (!isRecord(value) || !isRecord(value.error)) return void 0;
|
|
5114
|
+
const message = value.error.message;
|
|
5115
|
+
return typeof message === "string" ? message : void 0;
|
|
5116
|
+
}
|
|
5117
|
+
function stringProperty(record, keys) {
|
|
5118
|
+
for (const key of keys) {
|
|
5119
|
+
const value = record[key];
|
|
5120
|
+
if (typeof value === "string" && value.trim().length > 0) return value;
|
|
5121
|
+
}
|
|
5122
|
+
return void 0;
|
|
5123
|
+
}
|
|
5124
|
+
function stripRoamMetadataTags(markdown) {
|
|
5125
|
+
return markdown.replace(/<roam\b[^>]*>/giu, "").replace(/<\/roam>/giu, "");
|
|
5126
|
+
}
|
|
5127
|
+
function estimateBlockCount(markdown) {
|
|
5128
|
+
const lines = markdown.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
5129
|
+
return Math.max(1, lines.length);
|
|
5130
|
+
}
|
|
5131
|
+
function stableFallbackUid(title) {
|
|
5132
|
+
return title.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 80);
|
|
5133
|
+
}
|
|
5134
|
+
function isRecord(value) {
|
|
5135
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5136
|
+
}
|
|
5137
|
+
function createRoamMcpJsonLineClient(input) {
|
|
5138
|
+
const child = input.spawnProcess(input.command, input.args, {
|
|
5139
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5140
|
+
});
|
|
5141
|
+
child.stderr.resume();
|
|
5142
|
+
child.stdout.setEncoding("utf8");
|
|
5143
|
+
let nextId = 1;
|
|
5144
|
+
let buffer = "";
|
|
5145
|
+
const pending = /* @__PURE__ */ new Map();
|
|
5146
|
+
child.stdout.on("data", (chunk) => {
|
|
5147
|
+
buffer += chunk;
|
|
5148
|
+
let newline = buffer.indexOf("\n");
|
|
5149
|
+
while (newline >= 0) {
|
|
5150
|
+
const line = buffer.slice(0, newline).trim();
|
|
5151
|
+
buffer = buffer.slice(newline + 1);
|
|
5152
|
+
if (line.length > 0) handleJsonRpcLine(line, pending);
|
|
5153
|
+
newline = buffer.indexOf("\n");
|
|
5154
|
+
}
|
|
5155
|
+
});
|
|
5156
|
+
child.on("error", (error) => {
|
|
5157
|
+
for (const entry of pending.values()) entry.reject(error);
|
|
5158
|
+
pending.clear();
|
|
5159
|
+
});
|
|
5160
|
+
child.on("exit", () => {
|
|
5161
|
+
for (const entry of pending.values()) entry.reject(new Error("Roam MCP server exited."));
|
|
5162
|
+
pending.clear();
|
|
5163
|
+
});
|
|
5164
|
+
const request = (method, params) => {
|
|
5165
|
+
const id = nextId;
|
|
5166
|
+
nextId += 1;
|
|
5167
|
+
const promise = new Promise((resolve2, reject) => {
|
|
5168
|
+
pending.set(id, { resolve: resolve2, reject });
|
|
5169
|
+
});
|
|
5170
|
+
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
|
|
5171
|
+
`);
|
|
5172
|
+
return promise;
|
|
5173
|
+
};
|
|
5174
|
+
const initialized = request("initialize", {
|
|
5175
|
+
protocolVersion: "2025-11-25",
|
|
5176
|
+
capabilities: {},
|
|
5177
|
+
clientInfo: { name: "sift-roam-import", version: "0.1.0" }
|
|
5178
|
+
}).then(() => {
|
|
5179
|
+
child.stdin.write(
|
|
5180
|
+
`${JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" })}
|
|
5181
|
+
`
|
|
5182
|
+
);
|
|
5183
|
+
});
|
|
5184
|
+
return {
|
|
5185
|
+
async callTool(name, args) {
|
|
5186
|
+
await initialized;
|
|
5187
|
+
return request("tools/call", { name, arguments: args });
|
|
5188
|
+
},
|
|
5189
|
+
async close() {
|
|
5190
|
+
child.stdin.end();
|
|
5191
|
+
child.kill("SIGTERM");
|
|
5192
|
+
}
|
|
5193
|
+
};
|
|
5194
|
+
}
|
|
5195
|
+
function handleJsonRpcLine(line, pending) {
|
|
5196
|
+
let parsed;
|
|
5197
|
+
try {
|
|
5198
|
+
parsed = JSON.parse(line);
|
|
5199
|
+
} catch {
|
|
5200
|
+
return;
|
|
5201
|
+
}
|
|
5202
|
+
if (typeof parsed.id !== "number") return;
|
|
5203
|
+
const entry = pending.get(parsed.id);
|
|
5204
|
+
if (entry === void 0) return;
|
|
5205
|
+
pending.delete(parsed.id);
|
|
5206
|
+
if (parsed.error !== void 0) {
|
|
5207
|
+
entry.reject(
|
|
5208
|
+
new Error(
|
|
5209
|
+
typeof parsed.error.message === "string" ? parsed.error.message : "Roam MCP error."
|
|
5210
|
+
)
|
|
5211
|
+
);
|
|
5212
|
+
return;
|
|
5213
|
+
}
|
|
5214
|
+
entry.resolve(parsed.result);
|
|
5215
|
+
}
|
|
5216
|
+
|
|
4684
5217
|
// src/bin/sift.ts
|
|
4685
5218
|
var credentialStore = createMacOSKeychainStore();
|
|
4686
5219
|
var authCommands = createSiftCliAuthCommands({
|
|
@@ -4689,51 +5222,92 @@ var authCommands = createSiftCliAuthCommands({
|
|
|
4689
5222
|
credentialStore,
|
|
4690
5223
|
fetch
|
|
4691
5224
|
});
|
|
4692
|
-
var loadedAuth = await authCommands.loadAuth();
|
|
4693
|
-
var config = loadedAuth?.config ?? {
|
|
4694
|
-
apiBaseUrl: "",
|
|
4695
|
-
tokenLabel: "unset",
|
|
4696
|
-
workspaceId: "",
|
|
4697
|
-
brainId: "",
|
|
4698
|
-
principalId: "",
|
|
4699
|
-
capabilities: []
|
|
4700
|
-
};
|
|
4701
5225
|
var { argv, agentName } = extractAgentName(process.argv.slice(2), process.env.SIFT_AGENT);
|
|
4702
|
-
var
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
5226
|
+
var startupAuth = await loadAuthForStartup(authCommands, argv);
|
|
5227
|
+
if (startupAuth.ok === false) {
|
|
5228
|
+
process.stderr.write(`${startupAuth.message}
|
|
5229
|
+
`);
|
|
5230
|
+
process.exitCode = 1;
|
|
5231
|
+
} else {
|
|
5232
|
+
const loadedAuth = startupAuth.loadedAuth;
|
|
5233
|
+
const config = loadedAuth?.config ?? {
|
|
5234
|
+
apiBaseUrl: "",
|
|
5235
|
+
tokenLabel: "unset",
|
|
5236
|
+
workspaceId: "",
|
|
5237
|
+
brainId: "",
|
|
5238
|
+
principalId: "",
|
|
5239
|
+
capabilities: []
|
|
5240
|
+
};
|
|
5241
|
+
const result = await runSiftCli({
|
|
5242
|
+
argv,
|
|
5243
|
+
config,
|
|
5244
|
+
readStdin,
|
|
5245
|
+
agentName,
|
|
5246
|
+
executor: loadedAuth === void 0 ? void 0 : createHostedApiExecutor({
|
|
5247
|
+
apiBaseUrl: loadedAuth.config.apiBaseUrl,
|
|
5248
|
+
token: loadedAuth.token,
|
|
5249
|
+
workspaceId: loadedAuth.config.workspaceId,
|
|
5250
|
+
brainId: loadedAuth.config.brainId,
|
|
5251
|
+
agentName
|
|
5252
|
+
}),
|
|
5253
|
+
roamReader: createRoamMcpReader(),
|
|
5254
|
+
roamImporter: loadedAuth === void 0 ? void 0 : createSiftRoamImportClient({
|
|
5255
|
+
apiBaseUrl: loadedAuth.config.apiBaseUrl,
|
|
5256
|
+
token: loadedAuth.token,
|
|
5257
|
+
workspaceId: loadedAuth.config.workspaceId
|
|
5258
|
+
}),
|
|
5259
|
+
authCommands,
|
|
5260
|
+
mcpServer: {
|
|
5261
|
+
serve: async ({ config: config2, executor }) => {
|
|
5262
|
+
if (executor === void 0) {
|
|
5263
|
+
throw new Error(
|
|
5264
|
+
"Not signed in. Run 'sift login' to authenticate, then 'sift mcp serve' to start the local MCP server."
|
|
5265
|
+
);
|
|
5266
|
+
}
|
|
5267
|
+
const { createLocalMcpStdioServer: createLocalMcpStdioServer2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
5268
|
+
return createLocalMcpStdioServer2({
|
|
5269
|
+
input: process.stdin,
|
|
5270
|
+
output: process.stdout,
|
|
5271
|
+
error: process.stderr
|
|
5272
|
+
}).serve({
|
|
5273
|
+
capabilities: config2.capabilities,
|
|
5274
|
+
executor
|
|
5275
|
+
});
|
|
4721
5276
|
}
|
|
4722
|
-
const { createLocalMcpStdioServer: createLocalMcpStdioServer2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
4723
|
-
return createLocalMcpStdioServer2({
|
|
4724
|
-
input: process.stdin,
|
|
4725
|
-
output: process.stdout,
|
|
4726
|
-
error: process.stderr
|
|
4727
|
-
}).serve({
|
|
4728
|
-
capabilities: config2.capabilities,
|
|
4729
|
-
executor
|
|
4730
|
-
});
|
|
4731
5277
|
}
|
|
5278
|
+
});
|
|
5279
|
+
process.stdout.write(result.stdout);
|
|
5280
|
+
process.stderr.write(result.stderr);
|
|
5281
|
+
process.exitCode = result.exitCode;
|
|
5282
|
+
}
|
|
5283
|
+
async function loadAuthForStartup(commands, args) {
|
|
5284
|
+
try {
|
|
5285
|
+
const loadedAuth = await commands.loadAuth();
|
|
5286
|
+
if (loadedAuth === void 0 && !canRunWithoutLoadedAuth(args)) {
|
|
5287
|
+
return { ok: false, message: missingAuthMessage(args) };
|
|
5288
|
+
}
|
|
5289
|
+
return { ok: true, loadedAuth };
|
|
5290
|
+
} catch (error) {
|
|
5291
|
+
if (canRunWithoutLoadedAuth(args)) {
|
|
5292
|
+
return { ok: true, loadedAuth: void 0 };
|
|
5293
|
+
}
|
|
5294
|
+
return {
|
|
5295
|
+
ok: false,
|
|
5296
|
+
message: error instanceof Error ? error.message : "Failed to load Sift CLI auth."
|
|
5297
|
+
};
|
|
4732
5298
|
}
|
|
4733
|
-
}
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
5299
|
+
}
|
|
5300
|
+
function missingAuthMessage(args) {
|
|
5301
|
+
const commandArgs = args.filter((arg) => arg !== "--json");
|
|
5302
|
+
const [group, command] = commandArgs;
|
|
5303
|
+
const capability = group === "roam" && command === "import" ? " --capability record:read,source:manage" : "";
|
|
5304
|
+
return `Not signed in. Run 'npx -y @sift-wiki/cli@latest login --api-base-url ${DEFAULT_SIFT_API_BASE_URL}${capability}', then retry this command.`;
|
|
5305
|
+
}
|
|
5306
|
+
function canRunWithoutLoadedAuth(args) {
|
|
5307
|
+
const commandArgs = args.filter((arg) => arg !== "--json");
|
|
5308
|
+
const [group, command] = commandArgs;
|
|
5309
|
+
return group === void 0 || group === "help" || group === "--help" || group === "doctor" || group === "skill" || group === "login" || group === "logout" || group === "auth" && command === "status";
|
|
5310
|
+
}
|
|
4737
5311
|
function extractAgentName(args, envAgentName) {
|
|
4738
5312
|
const flagIndex = args.indexOf("--as-agent");
|
|
4739
5313
|
if (flagIndex !== -1 && args[flagIndex + 1] !== void 0) {
|