@sift-wiki/cli 0.1.4 → 0.1.6
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 +889 -305
- 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];
|
|
@@ -2271,6 +2348,17 @@ function validateCommandCapability(input) {
|
|
|
2271
2348
|
}
|
|
2272
2349
|
}
|
|
2273
2350
|
|
|
2351
|
+
// src/commandErrors.ts
|
|
2352
|
+
var ROAM_IMPORT_SCOPE_FIX = "Roam import needs additional Sift access. Run 'npx -y @sift-wiki/cli@latest login --capability record:read,source:manage', then retry 'sift roam import'.";
|
|
2353
|
+
function toCommandError(error, commandKey) {
|
|
2354
|
+
if (commandKey === "roam:import" && error instanceof Error && error.message === "missing capability source:manage") {
|
|
2355
|
+
const permissionError = new Error(ROAM_IMPORT_SCOPE_FIX);
|
|
2356
|
+
permissionError.name = "RuntimePermissionDeniedError";
|
|
2357
|
+
return permissionError;
|
|
2358
|
+
}
|
|
2359
|
+
return error;
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2274
2362
|
// src/contractOption.ts
|
|
2275
2363
|
function extractContractVersion(argv2) {
|
|
2276
2364
|
const index = argv2.indexOf("--contract");
|
|
@@ -2300,6 +2388,190 @@ function withContractVersion(executor, contractVersion) {
|
|
|
2300
2388
|
};
|
|
2301
2389
|
}
|
|
2302
2390
|
|
|
2391
|
+
// src/roamImport.ts
|
|
2392
|
+
async function runRoamImportCommand(input) {
|
|
2393
|
+
try {
|
|
2394
|
+
const parsed = parseOptions(input.rest);
|
|
2395
|
+
const scope = parseCliRoamScope(optionalOption(parsed, "scope") ?? "sift-tag");
|
|
2396
|
+
const mode = parseCliRoamMode(optionalOption(parsed, "mode") ?? "personal");
|
|
2397
|
+
const limit = parseIntegerOption(parsed, "limit", 100);
|
|
2398
|
+
if (limit < 1 || limit > 100) {
|
|
2399
|
+
throw new Error("Option --limit must be between 1 and 100.");
|
|
2400
|
+
}
|
|
2401
|
+
const wholeGraphConfirmed = input.rest.includes("--confirm-whole-graph") || input.rest.includes("--yes");
|
|
2402
|
+
if (scope === "whole_graph" && !wholeGraphConfirmed) {
|
|
2403
|
+
throw new Error("Option --confirm-whole-graph is required.");
|
|
2404
|
+
}
|
|
2405
|
+
const workspaceAttestation = input.rest.includes("--workspace-attestation") || input.rest.includes("--confirm-workspace");
|
|
2406
|
+
if (mode === "workspace" && !workspaceAttestation) {
|
|
2407
|
+
throw new Error("Option --workspace-attestation is required.");
|
|
2408
|
+
}
|
|
2409
|
+
if (input.reader === void 0) {
|
|
2410
|
+
throw new Error(
|
|
2411
|
+
"Roam import needs the local Roam helper. Run this command through the Sift CLI package."
|
|
2412
|
+
);
|
|
2413
|
+
}
|
|
2414
|
+
if (input.importer === void 0) {
|
|
2415
|
+
throw new Error("Not signed in. Run 'sift login', then retry 'sift roam import'.");
|
|
2416
|
+
}
|
|
2417
|
+
const records = await input.reader.exportPages({
|
|
2418
|
+
scope,
|
|
2419
|
+
graph: optionalOption(parsed, "graph"),
|
|
2420
|
+
limit,
|
|
2421
|
+
now: input.now
|
|
2422
|
+
});
|
|
2423
|
+
if (records.length === 0) {
|
|
2424
|
+
throw new Error(
|
|
2425
|
+
scope === "sift_tag" ? "No Roam pages marked [[Sift]] were found." : "No Roam pages were found for import."
|
|
2426
|
+
);
|
|
2427
|
+
}
|
|
2428
|
+
const result = await input.importer.importRecords({
|
|
2429
|
+
mode,
|
|
2430
|
+
scope,
|
|
2431
|
+
records,
|
|
2432
|
+
defaultVisibility: visibilityOption(parsed),
|
|
2433
|
+
workspaceAttestation,
|
|
2434
|
+
wholeGraphConfirmed
|
|
2435
|
+
});
|
|
2436
|
+
return ok(input.json ? `${JSON.stringify(result)}
|
|
2437
|
+
` : renderRoamImportResult(result));
|
|
2438
|
+
} catch (error) {
|
|
2439
|
+
return errorResult(error, input.json);
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
function createSiftRoamImportClient(input) {
|
|
2443
|
+
const fetchImpl = input.fetch ?? globalThis.fetch;
|
|
2444
|
+
return {
|
|
2445
|
+
async importRecords(request) {
|
|
2446
|
+
const response = await fetchImpl(roamImportUrl(input.apiBaseUrl, input.workspaceId), {
|
|
2447
|
+
method: "POST",
|
|
2448
|
+
headers: {
|
|
2449
|
+
Authorization: `Bearer ${input.token}`,
|
|
2450
|
+
"Content-Type": "application/json"
|
|
2451
|
+
},
|
|
2452
|
+
body: JSON.stringify(request)
|
|
2453
|
+
});
|
|
2454
|
+
const body = await response.text();
|
|
2455
|
+
const parsed = body.length > 0 ? parseJson(body) : {};
|
|
2456
|
+
if (!response.ok) {
|
|
2457
|
+
throw new Error(responseError(parsed, response.status));
|
|
2458
|
+
}
|
|
2459
|
+
return parseRoamImportResult(parsed);
|
|
2460
|
+
}
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
function parseCliRoamScope(value) {
|
|
2464
|
+
if (value === "sift-tag" || value === "sift_tag" || value === "marked" || value === "sift") {
|
|
2465
|
+
return "sift_tag";
|
|
2466
|
+
}
|
|
2467
|
+
if (value === "whole-graph" || value === "whole_graph" || value === "everything") {
|
|
2468
|
+
return "whole_graph";
|
|
2469
|
+
}
|
|
2470
|
+
throw new Error("Roam scope must be sift-tag or whole-graph.");
|
|
2471
|
+
}
|
|
2472
|
+
function parseCliRoamMode(value) {
|
|
2473
|
+
if (value === "personal" || value === "workspace") return value;
|
|
2474
|
+
throw new Error("Roam import mode must be personal or workspace.");
|
|
2475
|
+
}
|
|
2476
|
+
function visibilityOption(parsed) {
|
|
2477
|
+
const visibility = optionalOption(parsed, "visibility");
|
|
2478
|
+
if (visibility === void 0) return void 0;
|
|
2479
|
+
const values = visibility.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
2480
|
+
if (values.length === 0) {
|
|
2481
|
+
throw new Error("Option --visibility must include at least one visibility segment.");
|
|
2482
|
+
}
|
|
2483
|
+
return values;
|
|
2484
|
+
}
|
|
2485
|
+
function renderRoamImportResult(result) {
|
|
2486
|
+
return [
|
|
2487
|
+
`Imported ${result.importedCount} Roam pages.`,
|
|
2488
|
+
`Stored: ${result.storedCount}`,
|
|
2489
|
+
`Deduped: ${result.dedupedCount}`,
|
|
2490
|
+
`Rejected: ${result.rejectedCount}`,
|
|
2491
|
+
""
|
|
2492
|
+
].join("\n");
|
|
2493
|
+
}
|
|
2494
|
+
function roamImportUrl(apiBaseUrl, workspaceId) {
|
|
2495
|
+
const base = `${apiBaseUrl.replace(/\/+$/u, "")}/integrations/roam/import`;
|
|
2496
|
+
return workspaceId === void 0 ? base : `${base}?workspaceId=${encodeURIComponent(workspaceId)}`;
|
|
2497
|
+
}
|
|
2498
|
+
function parseJson(body) {
|
|
2499
|
+
try {
|
|
2500
|
+
return JSON.parse(body);
|
|
2501
|
+
} catch {
|
|
2502
|
+
throw new Error("Sift Roam import API returned invalid JSON.");
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
function responseError(parsed, status2) {
|
|
2506
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
2507
|
+
const record = parsed;
|
|
2508
|
+
const message = record.message;
|
|
2509
|
+
if (typeof message === "string" && message.trim().length > 0) return message;
|
|
2510
|
+
const error = record.error;
|
|
2511
|
+
if (typeof error === "object" && error !== null && "message" in error) {
|
|
2512
|
+
const nested = error.message;
|
|
2513
|
+
if (typeof nested === "string" && nested.trim().length > 0) return nested;
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
return `Sift Roam import API failed with status ${status2}.`;
|
|
2517
|
+
}
|
|
2518
|
+
function parseRoamImportResult(value) {
|
|
2519
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2520
|
+
throw new Error("Sift Roam import API returned an invalid result.");
|
|
2521
|
+
}
|
|
2522
|
+
const record = value;
|
|
2523
|
+
if (record.providerKind !== "roam") {
|
|
2524
|
+
throw new Error("Sift Roam import API returned an unexpected provider kind.");
|
|
2525
|
+
}
|
|
2526
|
+
return {
|
|
2527
|
+
providerKind: "roam",
|
|
2528
|
+
importedCount: integerField(record, "importedCount"),
|
|
2529
|
+
storedCount: integerField(record, "storedCount"),
|
|
2530
|
+
dedupedCount: integerField(record, "dedupedCount"),
|
|
2531
|
+
rejectedCount: integerField(record, "rejectedCount")
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
function integerField(record, key) {
|
|
2535
|
+
const value = record[key];
|
|
2536
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
2537
|
+
throw new Error(`Sift Roam import API result field ${key} must be an integer.`);
|
|
2538
|
+
}
|
|
2539
|
+
return value;
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
// src/mcpServeCommand.ts
|
|
2543
|
+
async function mcpServe(input) {
|
|
2544
|
+
if (input.mcpServer === void 0) {
|
|
2545
|
+
return fail("No local MCP server is configured for mcp.serve.");
|
|
2546
|
+
}
|
|
2547
|
+
if (input.executor === void 0) {
|
|
2548
|
+
return fail("Not signed in. Run 'sift login', then 'sift mcp serve'.");
|
|
2549
|
+
}
|
|
2550
|
+
const result = await input.mcpServer.serve({
|
|
2551
|
+
config: input.config,
|
|
2552
|
+
executor: input.executor,
|
|
2553
|
+
transport: "local_mcp"
|
|
2554
|
+
});
|
|
2555
|
+
if (result === void 0) return ok("");
|
|
2556
|
+
return ok(`${JSON.stringify(result)}
|
|
2557
|
+
`);
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
// src/scopeCurrentCommand.ts
|
|
2561
|
+
function scopeCurrent(config, json) {
|
|
2562
|
+
const scope = {
|
|
2563
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
2564
|
+
tokenLabel: config.tokenLabel,
|
|
2565
|
+
tokenExpiresAt: config.tokenExpiresAt,
|
|
2566
|
+
principalId: config.principalId,
|
|
2567
|
+
workspaceId: config.workspaceId,
|
|
2568
|
+
brainId: config.brainId,
|
|
2569
|
+
capabilities: config.capabilities
|
|
2570
|
+
};
|
|
2571
|
+
return ok(json ? `${JSON.stringify(scope)}
|
|
2572
|
+
` : renderScope(scope));
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2303
2575
|
// src/specialCommands.ts
|
|
2304
2576
|
import { readFile } from "fs/promises";
|
|
2305
2577
|
|
|
@@ -2317,24 +2589,24 @@ async function doctor(input) {
|
|
|
2317
2589
|
checks.push(readToolsCheck(availableTools3));
|
|
2318
2590
|
checks.push(writeToolsCheck(input.config, availableTools3));
|
|
2319
2591
|
checks.push(recordGetCheck(availableTools3));
|
|
2320
|
-
const
|
|
2592
|
+
const result = {
|
|
2321
2593
|
ok: !checks.some((check) => check.status === "failed"),
|
|
2322
2594
|
apiBaseUrl: input.config.apiBaseUrl.trim().length > 0 ? input.config.apiBaseUrl : void 0,
|
|
2323
2595
|
scope: scopeResult(input.config),
|
|
2324
2596
|
checks
|
|
2325
2597
|
};
|
|
2326
2598
|
return {
|
|
2327
|
-
exitCode:
|
|
2328
|
-
stdout: input.json ? `${JSON.stringify(
|
|
2329
|
-
` : renderDoctorResult(
|
|
2599
|
+
exitCode: result.ok ? 0 : 1,
|
|
2600
|
+
stdout: input.json ? `${JSON.stringify(result)}
|
|
2601
|
+
` : renderDoctorResult(result),
|
|
2330
2602
|
stderr: ""
|
|
2331
2603
|
};
|
|
2332
2604
|
}
|
|
2333
2605
|
async function discoverToolNames(executor) {
|
|
2334
2606
|
if (executor === void 0) return void 0;
|
|
2335
2607
|
if (executor.listAvailableToolNames !== void 0) return executor.listAvailableToolNames();
|
|
2336
|
-
const
|
|
2337
|
-
return toolNamesFromResult(
|
|
2608
|
+
const result = await executor.execute("tools.list", {});
|
|
2609
|
+
return toolNamesFromResult(result);
|
|
2338
2610
|
}
|
|
2339
2611
|
async function apiReachability(executor) {
|
|
2340
2612
|
if (executor === void 0) return { reachable: false, detail: "No API executor is configured." };
|
|
@@ -2348,15 +2620,15 @@ async function apiReachability(executor) {
|
|
|
2348
2620
|
};
|
|
2349
2621
|
}
|
|
2350
2622
|
}
|
|
2351
|
-
function scopeResult(
|
|
2352
|
-
if (
|
|
2623
|
+
function scopeResult(config) {
|
|
2624
|
+
if (config.workspaceId.trim().length === 0 || config.brainId.trim().length === 0 || config.principalId.trim().length === 0) {
|
|
2353
2625
|
return void 0;
|
|
2354
2626
|
}
|
|
2355
2627
|
return {
|
|
2356
|
-
workspaceId:
|
|
2357
|
-
brainId:
|
|
2358
|
-
principalId:
|
|
2359
|
-
capabilities: [...
|
|
2628
|
+
workspaceId: config.workspaceId,
|
|
2629
|
+
brainId: config.brainId,
|
|
2630
|
+
principalId: config.principalId,
|
|
2631
|
+
capabilities: [...config.capabilities]
|
|
2360
2632
|
};
|
|
2361
2633
|
}
|
|
2362
2634
|
async function checkApi(executor) {
|
|
@@ -2372,8 +2644,8 @@ async function checkApi(executor) {
|
|
|
2372
2644
|
const api = await apiReachability(executor);
|
|
2373
2645
|
return api.reachable ? { id: "api", status: "ok", label: "API", detail: "whoami succeeded." } : { id: "api", status: "failed", label: "API", detail: api.detail };
|
|
2374
2646
|
}
|
|
2375
|
-
function authCheck(
|
|
2376
|
-
if (
|
|
2647
|
+
function authCheck(config, now) {
|
|
2648
|
+
if (config.apiBaseUrl.trim().length === 0 || config.workspaceId.trim().length === 0 || config.brainId.trim().length === 0 || config.principalId.trim().length === 0) {
|
|
2377
2649
|
return {
|
|
2378
2650
|
id: "auth",
|
|
2379
2651
|
status: "failed",
|
|
@@ -2382,7 +2654,7 @@ function authCheck(config2, now) {
|
|
|
2382
2654
|
fix: "Run sift login."
|
|
2383
2655
|
};
|
|
2384
2656
|
}
|
|
2385
|
-
if (
|
|
2657
|
+
if (config.tokenExpiresAt !== void 0 && Date.parse(config.tokenExpiresAt) <= now.getTime()) {
|
|
2386
2658
|
return {
|
|
2387
2659
|
id: "auth",
|
|
2388
2660
|
status: "failed",
|
|
@@ -2393,8 +2665,8 @@ function authCheck(config2, now) {
|
|
|
2393
2665
|
}
|
|
2394
2666
|
return { id: "auth", status: "ok", label: "Auth", detail: "authenticated profile loaded." };
|
|
2395
2667
|
}
|
|
2396
|
-
function scopeCheck(
|
|
2397
|
-
if (
|
|
2668
|
+
function scopeCheck(config) {
|
|
2669
|
+
if (config.workspaceId.trim().length === 0 || config.brainId.trim().length === 0) {
|
|
2398
2670
|
return {
|
|
2399
2671
|
id: "scope",
|
|
2400
2672
|
status: "failed",
|
|
@@ -2407,14 +2679,14 @@ function scopeCheck(config2) {
|
|
|
2407
2679
|
id: "scope",
|
|
2408
2680
|
status: "ok",
|
|
2409
2681
|
label: "Scope",
|
|
2410
|
-
detail: `${
|
|
2682
|
+
detail: `${config.workspaceId}/${config.brainId}`
|
|
2411
2683
|
};
|
|
2412
2684
|
}
|
|
2413
2685
|
function readToolsCheck(names) {
|
|
2414
2686
|
return toolSetCheck("read-tools", "Read tools", ["context.assemble", "search.query"], names);
|
|
2415
2687
|
}
|
|
2416
|
-
function writeToolsCheck(
|
|
2417
|
-
const hasWrite =
|
|
2688
|
+
function writeToolsCheck(config, names) {
|
|
2689
|
+
const hasWrite = config.capabilities.includes("record:write") || config.capabilities.includes("source:write");
|
|
2418
2690
|
if (!hasWrite) {
|
|
2419
2691
|
return {
|
|
2420
2692
|
id: "write-tools",
|
|
@@ -2453,9 +2725,9 @@ function toolSetCheck(id, label, required, names) {
|
|
|
2453
2725
|
const missing = required.filter((name) => !names.includes(name));
|
|
2454
2726
|
return missing.length === 0 ? { id, status: "ok", label, detail: "required tools are available." } : { id, status: "failed", label, detail: `missing ${missing.join(", ")}.` };
|
|
2455
2727
|
}
|
|
2456
|
-
function toolNamesFromResult(
|
|
2457
|
-
if (!Array.isArray(
|
|
2458
|
-
return
|
|
2728
|
+
function toolNamesFromResult(result) {
|
|
2729
|
+
if (!Array.isArray(result)) return [];
|
|
2730
|
+
return result.flatMap((item) => {
|
|
2459
2731
|
if (typeof item === "object" && item !== null && "name" in item) {
|
|
2460
2732
|
const name = item.name;
|
|
2461
2733
|
return typeof name === "string" ? [name] : [];
|
|
@@ -2488,6 +2760,7 @@ var knownTopLevelCommands = /* @__PURE__ */ new Set([
|
|
|
2488
2760
|
"mcp",
|
|
2489
2761
|
"record",
|
|
2490
2762
|
"remember",
|
|
2763
|
+
"roam",
|
|
2491
2764
|
"scope",
|
|
2492
2765
|
"search",
|
|
2493
2766
|
"show",
|
|
@@ -2580,11 +2853,11 @@ async function ask(executor, rest, json) {
|
|
|
2580
2853
|
}
|
|
2581
2854
|
const parsed = parseOptions(rest);
|
|
2582
2855
|
const query = argsWithoutOptions(rest).join(" ").trim();
|
|
2583
|
-
const
|
|
2856
|
+
const result = await executor.execute("context.assemble", {
|
|
2584
2857
|
query,
|
|
2585
2858
|
maxChars: parseIntegerOption(parsed, "max-chars", 8e3)
|
|
2586
2859
|
});
|
|
2587
|
-
return ok(json ? aliasJson("ask", "context.assemble",
|
|
2860
|
+
return ok(json ? aliasJson("ask", "context.assemble", result) : renderSiftFound(result));
|
|
2588
2861
|
}
|
|
2589
2862
|
async function simpleSearch(executor, rest, json) {
|
|
2590
2863
|
if (executor === void 0) {
|
|
@@ -2592,11 +2865,11 @@ async function simpleSearch(executor, rest, json) {
|
|
|
2592
2865
|
}
|
|
2593
2866
|
const parsed = parseOptions(rest);
|
|
2594
2867
|
const query = argsWithoutOptions(rest).join(" ").trim();
|
|
2595
|
-
const
|
|
2868
|
+
const result = await executor.execute("search.query", {
|
|
2596
2869
|
query,
|
|
2597
2870
|
limit: parseIntegerOption(parsed, "limit", 10)
|
|
2598
2871
|
});
|
|
2599
|
-
return ok(json ? aliasJson("search", "search.query",
|
|
2872
|
+
return ok(json ? aliasJson("search", "search.query", result) : renderSearchResult(result));
|
|
2600
2873
|
}
|
|
2601
2874
|
async function remember(executor, rest, json, readStdin2, now) {
|
|
2602
2875
|
if (executor === void 0) {
|
|
@@ -2615,8 +2888,8 @@ async function remember(executor, rest, json, readStdin2, now) {
|
|
|
2615
2888
|
visibility: [optionalOption(parsed, "visibility") ?? DEFAULT_CLI_VISIBILITY],
|
|
2616
2889
|
markdown
|
|
2617
2890
|
};
|
|
2618
|
-
const
|
|
2619
|
-
return ok(json ? aliasJson("remember", "capture.text",
|
|
2891
|
+
const result = await executor.execute("capture.text", input);
|
|
2892
|
+
return ok(json ? aliasJson("remember", "capture.text", result) : renderWriteReceipt("Remember", result));
|
|
2620
2893
|
}
|
|
2621
2894
|
async function addFile(executor, fileReader, rest, json) {
|
|
2622
2895
|
if (executor === void 0) {
|
|
@@ -2638,8 +2911,8 @@ async function addFile(executor, fileReader, rest, json) {
|
|
|
2638
2911
|
bytes,
|
|
2639
2912
|
visibility: [optionalOption(parsed, "visibility") ?? DEFAULT_CLI_VISIBILITY]
|
|
2640
2913
|
};
|
|
2641
|
-
const
|
|
2642
|
-
return ok(json ? aliasJson("add", "capture.file",
|
|
2914
|
+
const result = await executor.execute("capture.file", input);
|
|
2915
|
+
return ok(json ? aliasJson("add", "capture.file", result) : renderWriteReceipt("Add", result));
|
|
2643
2916
|
}
|
|
2644
2917
|
async function edit(executor, rest, json) {
|
|
2645
2918
|
if (executor === void 0) {
|
|
@@ -2664,8 +2937,8 @@ async function edit(executor, rest, json) {
|
|
|
2664
2937
|
if (expectedMarkdown !== void 0) {
|
|
2665
2938
|
input.expectedMarkdown = expectedMarkdown;
|
|
2666
2939
|
}
|
|
2667
|
-
const
|
|
2668
|
-
return ok(json ? aliasJson("edit", "record.patch_section",
|
|
2940
|
+
const result = await executor.execute("record.patch_section", input);
|
|
2941
|
+
return ok(json ? aliasJson("edit", "record.patch_section", result) : renderWriteReceipt("Edit", result));
|
|
2669
2942
|
}
|
|
2670
2943
|
async function decide(executor, rest, json) {
|
|
2671
2944
|
if (executor === void 0) {
|
|
@@ -2678,8 +2951,8 @@ async function decide(executor, rest, json) {
|
|
|
2678
2951
|
visibility: [optionalOption(parsed, "visibility") ?? DEFAULT_CLI_VISIBILITY]
|
|
2679
2952
|
};
|
|
2680
2953
|
addOptionalWorkAliasMetadata(input, parsed);
|
|
2681
|
-
const
|
|
2682
|
-
return ok(json ? aliasJson("decide", "decision.create",
|
|
2954
|
+
const result = await executor.execute("decision.create", input);
|
|
2955
|
+
return ok(json ? aliasJson("decide", "decision.create", result) : renderWriteReceipt("Decision", result));
|
|
2683
2956
|
}
|
|
2684
2957
|
async function todo(executor, rest, json) {
|
|
2685
2958
|
if (executor === void 0) {
|
|
@@ -2696,8 +2969,8 @@ async function todo(executor, rest, json) {
|
|
|
2696
2969
|
const dueDate = optionalOption(parsed, "due-date");
|
|
2697
2970
|
if (dueDate !== void 0) input.dueDate = dueDate;
|
|
2698
2971
|
addOptionalWorkAliasMetadata(input, parsed);
|
|
2699
|
-
const
|
|
2700
|
-
return ok(json ? aliasJson("todo", "task.create",
|
|
2972
|
+
const result = await executor.execute("task.create", input);
|
|
2973
|
+
return ok(json ? aliasJson("todo", "task.create", result) : renderWriteReceipt("Task", result));
|
|
2701
2974
|
}
|
|
2702
2975
|
async function show(executor, rest, json) {
|
|
2703
2976
|
if (executor === void 0) {
|
|
@@ -2720,21 +2993,21 @@ async function show(executor, rest, json) {
|
|
|
2720
2993
|
if (sectionAnchor !== void 0) {
|
|
2721
2994
|
input.sectionAnchor = sectionAnchor;
|
|
2722
2995
|
}
|
|
2723
|
-
const
|
|
2724
|
-
return ok(json ? aliasJson("show", "record.get",
|
|
2996
|
+
const result = await executor.execute("record.get", input);
|
|
2997
|
+
return ok(json ? aliasJson("show", "record.get", result) : renderRecordResult(result));
|
|
2725
2998
|
}
|
|
2726
|
-
async function status(
|
|
2999
|
+
async function status(config, executor, json) {
|
|
2727
3000
|
const scope = {
|
|
2728
|
-
apiBaseUrl:
|
|
2729
|
-
principalId:
|
|
2730
|
-
workspaceId:
|
|
2731
|
-
brainId:
|
|
2732
|
-
capabilities: [...
|
|
3001
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
3002
|
+
principalId: config.principalId,
|
|
3003
|
+
workspaceId: config.workspaceId,
|
|
3004
|
+
brainId: config.brainId,
|
|
3005
|
+
capabilities: [...config.capabilities]
|
|
2733
3006
|
};
|
|
2734
3007
|
const api = await apiReachability(executor);
|
|
2735
|
-
const
|
|
3008
|
+
const result = { scope, api };
|
|
2736
3009
|
if (json) {
|
|
2737
|
-
return ok(`${JSON.stringify({ command: "status", result
|
|
3010
|
+
return ok(`${JSON.stringify({ command: "status", result })}
|
|
2738
3011
|
`);
|
|
2739
3012
|
}
|
|
2740
3013
|
return ok(`${renderScope({ ...scope, tokenLabel: "configured" })}API reachable: ${api.reachable}
|
|
@@ -3005,9 +3278,9 @@ function toolsHelp(input) {
|
|
|
3005
3278
|
return executeSimple(input.executor, "tools.help", { name }, input.json);
|
|
3006
3279
|
}
|
|
3007
3280
|
async function executeSimple(executor, name, toolInput, json) {
|
|
3008
|
-
const
|
|
3009
|
-
return ok(json ? `${JSON.stringify(
|
|
3010
|
-
` : `${JSON.stringify(
|
|
3281
|
+
const result = await executor.execute(name, toolInput);
|
|
3282
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3283
|
+
` : `${JSON.stringify(result)}
|
|
3011
3284
|
`);
|
|
3012
3285
|
}
|
|
3013
3286
|
|
|
@@ -3028,7 +3301,7 @@ function createHostedApiExecutor(input) {
|
|
|
3028
3301
|
body: JSON.stringify({ input: toolInput }, serializeJsonValue)
|
|
3029
3302
|
});
|
|
3030
3303
|
const body = await response.text();
|
|
3031
|
-
const parsed = body.length > 0 ?
|
|
3304
|
+
const parsed = body.length > 0 ? parseJson2(body) : {};
|
|
3032
3305
|
if (!response.ok) {
|
|
3033
3306
|
throw new Error(errorMessage(parsed, response.status));
|
|
3034
3307
|
}
|
|
@@ -3045,7 +3318,7 @@ function serializeJsonValue(_key, value) {
|
|
|
3045
3318
|
function toolUrl(apiBaseUrl, name) {
|
|
3046
3319
|
return `${apiBaseUrl.replace(/\/+$/u, "")}/agent-tools/${encodeURIComponent(name)}`;
|
|
3047
3320
|
}
|
|
3048
|
-
function
|
|
3321
|
+
function parseJson2(body) {
|
|
3049
3322
|
try {
|
|
3050
3323
|
return JSON.parse(body);
|
|
3051
3324
|
} catch {
|
|
@@ -3148,7 +3421,19 @@ async function runSiftCli(rawInput) {
|
|
|
3148
3421
|
"task:create": () => createTask(input.executor, rest, json),
|
|
3149
3422
|
"agent:register": () => agentRegister(input.executor, input.agentName, rest, json),
|
|
3150
3423
|
"agent:status": () => executeSimple2(input.executor, "agent.status", {}, json),
|
|
3151
|
-
"mcp:serve": () => mcpServe(
|
|
3424
|
+
"mcp:serve": () => mcpServe({
|
|
3425
|
+
mcpServer: input.mcpServer,
|
|
3426
|
+
config: input.config,
|
|
3427
|
+
executor: rawInput.executor
|
|
3428
|
+
}),
|
|
3429
|
+
"roam:import": () => runRoamImportCommand({
|
|
3430
|
+
rest,
|
|
3431
|
+
json,
|
|
3432
|
+
config: input.config,
|
|
3433
|
+
reader: input.roamReader,
|
|
3434
|
+
importer: input.roamImporter,
|
|
3435
|
+
now: input.now ?? /* @__PURE__ */ new Date()
|
|
3436
|
+
}),
|
|
3152
3437
|
"login:": () => authCommand(input.authCommands, "login", { rest: commandRest, json }),
|
|
3153
3438
|
"auth:status": () => authCommand(input.authCommands, "status", { json }),
|
|
3154
3439
|
"logout:": () => authCommand(input.authCommands, "logout", { json })
|
|
@@ -3169,42 +3454,17 @@ async function runSiftCli(rawInput) {
|
|
|
3169
3454
|
validateCommandCapability({ commandKey, config: input.config });
|
|
3170
3455
|
return await handler();
|
|
3171
3456
|
} catch (error) {
|
|
3172
|
-
return errorResult(error, json);
|
|
3457
|
+
return errorResult(toCommandError(error, commandKey), json);
|
|
3173
3458
|
}
|
|
3174
3459
|
}
|
|
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
3460
|
async function searchQuery(executor, rest, json) {
|
|
3201
3461
|
if (executor === void 0) {
|
|
3202
3462
|
return fail("No Sift API executor is configured for search.query.");
|
|
3203
3463
|
}
|
|
3204
3464
|
const query = rest.join(" ").trim();
|
|
3205
|
-
const
|
|
3206
|
-
return ok(json ? `${JSON.stringify(
|
|
3207
|
-
` : renderSearchResult(
|
|
3465
|
+
const result = await executor.execute("search.query", { query, limit: 10 });
|
|
3466
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3467
|
+
` : renderSearchResult(result));
|
|
3208
3468
|
}
|
|
3209
3469
|
async function contextAssemble(executor, rest, json) {
|
|
3210
3470
|
if (executor === void 0) {
|
|
@@ -3212,12 +3472,12 @@ async function contextAssemble(executor, rest, json) {
|
|
|
3212
3472
|
}
|
|
3213
3473
|
const parsed = parseOptions(rest);
|
|
3214
3474
|
const query = positionalArgs(rest).join(" ").trim();
|
|
3215
|
-
const
|
|
3475
|
+
const result = await executor.execute("context.assemble", {
|
|
3216
3476
|
query,
|
|
3217
3477
|
maxChars: parseIntegerOption(parsed, "max-chars", 4e3)
|
|
3218
3478
|
});
|
|
3219
|
-
return ok(json ? `${JSON.stringify(
|
|
3220
|
-
` : renderSearchResult(
|
|
3479
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3480
|
+
` : renderSearchResult(result));
|
|
3221
3481
|
}
|
|
3222
3482
|
async function contextProfile(executor, rest, json) {
|
|
3223
3483
|
if (executor === void 0) {
|
|
@@ -3231,17 +3491,17 @@ async function contextProfile(executor, rest, json) {
|
|
|
3231
3491
|
if (query.length > 0) {
|
|
3232
3492
|
input.query = query;
|
|
3233
3493
|
}
|
|
3234
|
-
const
|
|
3235
|
-
return ok(json ? `${JSON.stringify(
|
|
3236
|
-
` : renderProfileResult(
|
|
3494
|
+
const result = await executor.execute("context.profile", input);
|
|
3495
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3496
|
+
` : renderProfileResult(result));
|
|
3237
3497
|
}
|
|
3238
3498
|
async function executeSimple2(executor, name, toolInput, json) {
|
|
3239
3499
|
if (executor === void 0) {
|
|
3240
3500
|
return fail(`No Sift API executor is configured for ${name}.`);
|
|
3241
3501
|
}
|
|
3242
|
-
const
|
|
3243
|
-
return ok(json ? `${JSON.stringify(
|
|
3244
|
-
` : `${JSON.stringify(
|
|
3502
|
+
const result = await executor.execute(name, toolInput);
|
|
3503
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3504
|
+
` : `${JSON.stringify(result)}
|
|
3245
3505
|
`);
|
|
3246
3506
|
}
|
|
3247
3507
|
async function captureText(executor, rest, json) {
|
|
@@ -3249,15 +3509,15 @@ async function captureText(executor, rest, json) {
|
|
|
3249
3509
|
return fail("No Sift API executor is configured for capture.text.");
|
|
3250
3510
|
}
|
|
3251
3511
|
const parsed = parseOptions(rest);
|
|
3252
|
-
const
|
|
3512
|
+
const result = await executor.execute("capture.text", {
|
|
3253
3513
|
sourceName: requireOption(parsed, "source"),
|
|
3254
3514
|
externalId: requireOption(parsed, "external-id"),
|
|
3255
3515
|
title: requireOption(parsed, "title"),
|
|
3256
3516
|
visibility: [requireOption(parsed, "visibility")],
|
|
3257
3517
|
markdown: requireOption(parsed, "markdown")
|
|
3258
3518
|
});
|
|
3259
|
-
return ok(json ? `${JSON.stringify(
|
|
3260
|
-
` : `${JSON.stringify(
|
|
3519
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3520
|
+
` : `${JSON.stringify(result)}
|
|
3261
3521
|
`);
|
|
3262
3522
|
}
|
|
3263
3523
|
async function captureFile(executor, fileReader, rest, json) {
|
|
@@ -3270,7 +3530,7 @@ async function captureFile(executor, fileReader, rest, json) {
|
|
|
3270
3530
|
}
|
|
3271
3531
|
const parsed = parseOptions(optionArgs);
|
|
3272
3532
|
const bytes = await fileReader(path);
|
|
3273
|
-
const
|
|
3533
|
+
const result = await executor.execute("capture.file", {
|
|
3274
3534
|
sourceName: requireOption(parsed, "source"),
|
|
3275
3535
|
externalId: requireOption(parsed, "external-id"),
|
|
3276
3536
|
title: requireOption(parsed, "title"),
|
|
@@ -3279,8 +3539,8 @@ async function captureFile(executor, fileReader, rest, json) {
|
|
|
3279
3539
|
bytes,
|
|
3280
3540
|
visibility: [requireOption(parsed, "visibility")]
|
|
3281
3541
|
});
|
|
3282
|
-
return ok(json ? `${JSON.stringify(
|
|
3283
|
-
` : `${JSON.stringify(
|
|
3542
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3543
|
+
` : `${JSON.stringify(result)}
|
|
3284
3544
|
`);
|
|
3285
3545
|
}
|
|
3286
3546
|
async function captureBatch(executor, fileReader, rest, json) {
|
|
@@ -3292,9 +3552,9 @@ async function captureBatch(executor, fileReader, rest, json) {
|
|
|
3292
3552
|
return fail("Missing required manifest path for capture.batch.");
|
|
3293
3553
|
}
|
|
3294
3554
|
const manifest = parseBatchManifest(await fileReader(manifestPath));
|
|
3295
|
-
const
|
|
3296
|
-
return ok(json ? `${JSON.stringify(
|
|
3297
|
-
` : `${JSON.stringify(
|
|
3555
|
+
const result = await executor.execute("capture.batch", { items: manifest });
|
|
3556
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3557
|
+
` : `${JSON.stringify(result)}
|
|
3298
3558
|
`);
|
|
3299
3559
|
}
|
|
3300
3560
|
function parseBatchManifest(bytes) {
|
|
@@ -3319,18 +3579,18 @@ async function createDecision(executor, rest, json) {
|
|
|
3319
3579
|
input.rationale = rationale;
|
|
3320
3580
|
}
|
|
3321
3581
|
addOptionalWorkMetadata(input, parsed);
|
|
3322
|
-
const
|
|
3323
|
-
return ok(json ? `${JSON.stringify(
|
|
3324
|
-
` : `${JSON.stringify(
|
|
3582
|
+
const result = await executor.execute("decision.create", input);
|
|
3583
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3584
|
+
` : `${JSON.stringify(result)}
|
|
3325
3585
|
`);
|
|
3326
3586
|
}
|
|
3327
3587
|
async function sourceList(executor, json) {
|
|
3328
3588
|
if (executor === void 0) {
|
|
3329
3589
|
return fail("No Sift API executor is configured for source.list.");
|
|
3330
3590
|
}
|
|
3331
|
-
const
|
|
3332
|
-
return ok(json ? `${JSON.stringify(
|
|
3333
|
-
` : `${JSON.stringify(
|
|
3591
|
+
const result = await executor.execute("source.list", {});
|
|
3592
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3593
|
+
` : `${JSON.stringify(result)}
|
|
3334
3594
|
`);
|
|
3335
3595
|
}
|
|
3336
3596
|
async function sourceCreate(executor, rest, json) {
|
|
@@ -3338,12 +3598,12 @@ async function sourceCreate(executor, rest, json) {
|
|
|
3338
3598
|
return fail("No Sift API executor is configured for source.create.");
|
|
3339
3599
|
}
|
|
3340
3600
|
const parsed = parseOptions(rest);
|
|
3341
|
-
const
|
|
3601
|
+
const result = await executor.execute("source.create", {
|
|
3342
3602
|
name: requireOption(parsed, "name"),
|
|
3343
3603
|
visibility: [requireOption(parsed, "visibility")]
|
|
3344
3604
|
});
|
|
3345
|
-
return ok(json ? `${JSON.stringify(
|
|
3346
|
-
` : `${JSON.stringify(
|
|
3605
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3606
|
+
` : `${JSON.stringify(result)}
|
|
3347
3607
|
`);
|
|
3348
3608
|
}
|
|
3349
3609
|
async function sourceRead(executor, command, rest, json) {
|
|
@@ -3355,9 +3615,9 @@ async function sourceRead(executor, command, rest, json) {
|
|
|
3355
3615
|
if (sourceId === void 0 || sourceId.trim().length === 0) {
|
|
3356
3616
|
return fail(`Missing required source ID for ${toolName}.`);
|
|
3357
3617
|
}
|
|
3358
|
-
const
|
|
3359
|
-
return ok(json ? `${JSON.stringify(
|
|
3360
|
-
` : `${JSON.stringify(
|
|
3618
|
+
const result = await executor.execute(toolName, { sourceId });
|
|
3619
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3620
|
+
` : `${JSON.stringify(result)}
|
|
3361
3621
|
`);
|
|
3362
3622
|
}
|
|
3363
3623
|
async function ingestionStatus(executor, rest, json) {
|
|
@@ -3368,18 +3628,18 @@ async function ingestionStatus(executor, rest, json) {
|
|
|
3368
3628
|
if (jobId === void 0 || jobId.trim().length === 0) {
|
|
3369
3629
|
return fail("Missing required job ID for ingestion.status.");
|
|
3370
3630
|
}
|
|
3371
|
-
const
|
|
3372
|
-
return ok(json ? `${JSON.stringify(
|
|
3373
|
-
` : `${JSON.stringify(
|
|
3631
|
+
const result = await executor.execute("ingestion.status", { jobId });
|
|
3632
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3633
|
+
` : `${JSON.stringify(result)}
|
|
3374
3634
|
`);
|
|
3375
3635
|
}
|
|
3376
3636
|
async function recordList(executor, json) {
|
|
3377
3637
|
if (executor === void 0) {
|
|
3378
3638
|
return fail("No Sift API executor is configured for record.list.");
|
|
3379
3639
|
}
|
|
3380
|
-
const
|
|
3381
|
-
return ok(json ? `${JSON.stringify(
|
|
3382
|
-
` : `${JSON.stringify(
|
|
3640
|
+
const result = await executor.execute("record.list", {});
|
|
3641
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3642
|
+
` : `${JSON.stringify(result)}
|
|
3383
3643
|
`);
|
|
3384
3644
|
}
|
|
3385
3645
|
async function recordRead(executor, toolName, rest, json) {
|
|
@@ -3397,23 +3657,23 @@ async function recordRead(executor, toolName, rest, json) {
|
|
|
3397
3657
|
input.sectionAnchor = sectionAnchor;
|
|
3398
3658
|
}
|
|
3399
3659
|
}
|
|
3400
|
-
const
|
|
3401
|
-
return ok(json ? `${JSON.stringify(
|
|
3402
|
-
` : renderRecordResult(
|
|
3660
|
+
const result = await executor.execute(toolName, input);
|
|
3661
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3662
|
+
` : renderRecordResult(result));
|
|
3403
3663
|
}
|
|
3404
3664
|
async function createMarkdownRecord(executor, rest, json) {
|
|
3405
3665
|
if (executor === void 0) {
|
|
3406
3666
|
return fail("No Sift API executor is configured for record.create_markdown.");
|
|
3407
3667
|
}
|
|
3408
3668
|
const parsed = parseOptions(rest);
|
|
3409
|
-
const
|
|
3669
|
+
const result = await executor.execute("record.create_markdown", {
|
|
3410
3670
|
recordType: requireOption(parsed, "type"),
|
|
3411
3671
|
title: requireOption(parsed, "title"),
|
|
3412
3672
|
markdown: requireOption(parsed, "markdown"),
|
|
3413
3673
|
visibility: [requireOption(parsed, "visibility")]
|
|
3414
3674
|
});
|
|
3415
|
-
return ok(json ? `${JSON.stringify(
|
|
3416
|
-
` : `${JSON.stringify(
|
|
3675
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3676
|
+
` : `${JSON.stringify(result)}
|
|
3417
3677
|
`);
|
|
3418
3678
|
}
|
|
3419
3679
|
async function patchRecordSection(executor, rest, json) {
|
|
@@ -3437,9 +3697,9 @@ async function patchRecordSection(executor, rest, json) {
|
|
|
3437
3697
|
if (expectedMarkdown !== void 0) {
|
|
3438
3698
|
input.expectedMarkdown = expectedMarkdown;
|
|
3439
3699
|
}
|
|
3440
|
-
const
|
|
3441
|
-
return ok(json ? `${JSON.stringify(
|
|
3442
|
-
` : `${JSON.stringify(
|
|
3700
|
+
const result = await executor.execute("record.patch_section", input);
|
|
3701
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3702
|
+
` : `${JSON.stringify(result)}
|
|
3443
3703
|
`);
|
|
3444
3704
|
}
|
|
3445
3705
|
async function createTask(executor, rest, json) {
|
|
@@ -3468,9 +3728,9 @@ async function createTask(executor, rest, json) {
|
|
|
3468
3728
|
input.rationale = rationale;
|
|
3469
3729
|
}
|
|
3470
3730
|
addOptionalWorkMetadata(input, parsed);
|
|
3471
|
-
const
|
|
3472
|
-
return ok(json ? `${JSON.stringify(
|
|
3473
|
-
` : `${JSON.stringify(
|
|
3731
|
+
const result = await executor.execute("task.create", input);
|
|
3732
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3733
|
+
` : `${JSON.stringify(result)}
|
|
3474
3734
|
`);
|
|
3475
3735
|
}
|
|
3476
3736
|
async function auditEvents(executor, rest, json) {
|
|
@@ -3482,9 +3742,9 @@ async function auditEvents(executor, rest, json) {
|
|
|
3482
3742
|
if (targetId !== void 0 && targetId.trim().length > 0) {
|
|
3483
3743
|
input.targetId = targetId;
|
|
3484
3744
|
}
|
|
3485
|
-
const
|
|
3486
|
-
return ok(json ? `${JSON.stringify(
|
|
3487
|
-
` : `${JSON.stringify(
|
|
3745
|
+
const result = await executor.execute("audit.events", input);
|
|
3746
|
+
return ok(json ? `${JSON.stringify(result)}
|
|
3747
|
+
` : `${JSON.stringify(result)}
|
|
3488
3748
|
`);
|
|
3489
3749
|
}
|
|
3490
3750
|
async function idTool(input) {
|
|
@@ -3495,9 +3755,9 @@ async function idTool(input) {
|
|
|
3495
3755
|
if (id === void 0 || id.trim().length === 0) {
|
|
3496
3756
|
return fail(`Missing required ${input.idLabel} for ${input.toolName}.`);
|
|
3497
3757
|
}
|
|
3498
|
-
const
|
|
3499
|
-
return ok(input.json ? `${JSON.stringify(
|
|
3500
|
-
` : `${JSON.stringify(
|
|
3758
|
+
const result = await input.executor.execute(input.toolName, { [input.inputKey]: id });
|
|
3759
|
+
return ok(input.json ? `${JSON.stringify(result)}
|
|
3760
|
+
` : `${JSON.stringify(result)}
|
|
3501
3761
|
`);
|
|
3502
3762
|
}
|
|
3503
3763
|
|
|
@@ -3523,10 +3783,10 @@ async function readStoredSiftConfig(input) {
|
|
|
3523
3783
|
return parseStoredSiftConfig(JSON.parse(raw));
|
|
3524
3784
|
}
|
|
3525
3785
|
async function writeStoredSiftConfig(input) {
|
|
3526
|
-
const
|
|
3786
|
+
const config = parseStoredSiftConfig(input.config);
|
|
3527
3787
|
const path = resolveSiftConfigPath(input);
|
|
3528
3788
|
await mkdir(dirname(path), { recursive: true, mode: 448 });
|
|
3529
|
-
await writeFile(path, `${JSON.stringify(
|
|
3789
|
+
await writeFile(path, `${JSON.stringify(config, null, 2)}
|
|
3530
3790
|
`, { mode: 384 });
|
|
3531
3791
|
await chmod(path, 384);
|
|
3532
3792
|
}
|
|
@@ -3747,14 +4007,14 @@ function createMacOSKeychainStore(input = {}) {
|
|
|
3747
4007
|
return {
|
|
3748
4008
|
async assertAvailable() {
|
|
3749
4009
|
await requireSupported();
|
|
3750
|
-
const
|
|
3751
|
-
if (
|
|
4010
|
+
const result = await runCommand(securityPath, ["list-keychains"]);
|
|
4011
|
+
if (result.exitCode !== 0) {
|
|
3752
4012
|
throw new UnsupportedCredentialStoreError();
|
|
3753
4013
|
}
|
|
3754
4014
|
},
|
|
3755
4015
|
async read(readInput) {
|
|
3756
4016
|
await requireSupported();
|
|
3757
|
-
const
|
|
4017
|
+
const result = await runCommand(securityPath, [
|
|
3758
4018
|
"find-generic-password",
|
|
3759
4019
|
"-s",
|
|
3760
4020
|
serviceName,
|
|
@@ -3762,15 +4022,15 @@ function createMacOSKeychainStore(input = {}) {
|
|
|
3762
4022
|
account(readInput),
|
|
3763
4023
|
"-w"
|
|
3764
4024
|
]);
|
|
3765
|
-
if (
|
|
4025
|
+
if (result.exitCode !== 0) {
|
|
3766
4026
|
return void 0;
|
|
3767
4027
|
}
|
|
3768
|
-
const secret =
|
|
4028
|
+
const secret = result.stdout.trim();
|
|
3769
4029
|
return secret.length === 0 ? void 0 : secret;
|
|
3770
4030
|
},
|
|
3771
4031
|
async write(writeInput) {
|
|
3772
4032
|
await requireSupported();
|
|
3773
|
-
const
|
|
4033
|
+
const result = await runCommand(securityPath, [
|
|
3774
4034
|
"add-generic-password",
|
|
3775
4035
|
"-U",
|
|
3776
4036
|
"-s",
|
|
@@ -3780,7 +4040,7 @@ function createMacOSKeychainStore(input = {}) {
|
|
|
3780
4040
|
"-w",
|
|
3781
4041
|
writeInput.secret
|
|
3782
4042
|
]);
|
|
3783
|
-
if (
|
|
4043
|
+
if (result.exitCode !== 0) {
|
|
3784
4044
|
throw new Error("Failed to write Sift CLI token secret to macOS Keychain.");
|
|
3785
4045
|
}
|
|
3786
4046
|
},
|
|
@@ -3798,8 +4058,8 @@ function createMacOSKeychainStore(input = {}) {
|
|
|
3798
4058
|
}
|
|
3799
4059
|
async function runSecurityCommand(file, args) {
|
|
3800
4060
|
try {
|
|
3801
|
-
const
|
|
3802
|
-
return { stdout:
|
|
4061
|
+
const result = await execFileAsync(file, args);
|
|
4062
|
+
return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
|
|
3803
4063
|
} catch (error) {
|
|
3804
4064
|
if (isExecError(error)) {
|
|
3805
4065
|
return {
|
|
@@ -3824,6 +4084,7 @@ import { hostname as hostname3 } from "os";
|
|
|
3824
4084
|
import { promisify as promisify2 } from "util";
|
|
3825
4085
|
|
|
3826
4086
|
// src/auth/loginHelpers.ts
|
|
4087
|
+
var DEFAULT_SIFT_API_BASE_URL = "https://sift-wiki-api.fly.dev";
|
|
3827
4088
|
async function resolveLoginApiBaseUrl(input) {
|
|
3828
4089
|
const options = parseOptions(input.argv);
|
|
3829
4090
|
const fromFlag = clean2(options.get("api-base-url"));
|
|
@@ -3833,7 +4094,7 @@ async function resolveLoginApiBaseUrl(input) {
|
|
|
3833
4094
|
const stored = await readStoredSiftConfig({ homeDir: input.homeDir });
|
|
3834
4095
|
const profile = stored?.profiles[stored.currentProfile];
|
|
3835
4096
|
if (profile !== void 0) return normalizeUrl(profile.apiBaseUrl);
|
|
3836
|
-
return
|
|
4097
|
+
return DEFAULT_SIFT_API_BASE_URL;
|
|
3837
4098
|
}
|
|
3838
4099
|
function requestedCapabilities(rest) {
|
|
3839
4100
|
const option = parseOptions(rest).get("capability");
|
|
@@ -3874,15 +4135,15 @@ function resolveCliOAuthConfig(input) {
|
|
|
3874
4135
|
return void 0;
|
|
3875
4136
|
}
|
|
3876
4137
|
const registrationUrl = clean3(options.get("oauth-registration-url")) ?? clean3(input.env.SIFT_OAUTH_REGISTRATION_URL);
|
|
3877
|
-
const
|
|
4138
|
+
const config = { authorizeUrl, tokenUrl, clientId };
|
|
3878
4139
|
if (registrationUrl !== void 0) {
|
|
3879
|
-
|
|
4140
|
+
config.registrationUrl = registrationUrl;
|
|
3880
4141
|
}
|
|
3881
4142
|
const scopes = parseScopeList(clean3(options.get("oauth-scopes")) ?? clean3(input.env.SIFT_OAUTH_SCOPES));
|
|
3882
4143
|
if (scopes.length > 0) {
|
|
3883
|
-
|
|
4144
|
+
config.defaultScopes = scopes;
|
|
3884
4145
|
}
|
|
3885
|
-
return
|
|
4146
|
+
return config;
|
|
3886
4147
|
}
|
|
3887
4148
|
function scopesForCapabilities(capabilities) {
|
|
3888
4149
|
const scopes = /* @__PURE__ */ new Set(["read"]);
|
|
@@ -4057,11 +4318,11 @@ async function finalizeOAuthLogin(input, tokens) {
|
|
|
4057
4318
|
tokenKind: "oauth",
|
|
4058
4319
|
refreshable: tokens.refreshToken !== void 0
|
|
4059
4320
|
};
|
|
4060
|
-
const
|
|
4321
|
+
const result = { profile, accessToken: tokens.accessToken };
|
|
4061
4322
|
if (tokens.refreshToken !== void 0) {
|
|
4062
|
-
|
|
4323
|
+
result.refreshToken = tokens.refreshToken;
|
|
4063
4324
|
}
|
|
4064
|
-
return
|
|
4325
|
+
return result;
|
|
4065
4326
|
}
|
|
4066
4327
|
function buildAuthorizeUrl(input) {
|
|
4067
4328
|
const url = new URL(input.oauth.authorizeUrl);
|
|
@@ -4294,7 +4555,7 @@ function oauthRefresherFor(input, rest) {
|
|
|
4294
4555
|
async function oauthBrowserLoginFlow(input, rest, json) {
|
|
4295
4556
|
const apiBaseUrl = await resolveLoginApiBaseUrl({ argv: rest, env: input.env, homeDir: input.homeDir });
|
|
4296
4557
|
const oauth = resolveOAuthConfigOrThrow(input, rest);
|
|
4297
|
-
const
|
|
4558
|
+
const result = await oauthBrowserLogin({
|
|
4298
4559
|
apiBaseUrl,
|
|
4299
4560
|
appBaseUrl: resolveAppBaseUrl(input.env, apiBaseUrl),
|
|
4300
4561
|
oauth,
|
|
@@ -4309,16 +4570,16 @@ async function oauthBrowserLoginFlow(input, rest, json) {
|
|
|
4309
4570
|
return persistConvergedLogin(
|
|
4310
4571
|
input,
|
|
4311
4572
|
{
|
|
4312
|
-
profile:
|
|
4313
|
-
accessToken:
|
|
4314
|
-
...
|
|
4573
|
+
profile: result.profile,
|
|
4574
|
+
accessToken: result.accessToken,
|
|
4575
|
+
...result.refreshToken === void 0 ? {} : { refreshToken: result.refreshToken }
|
|
4315
4576
|
},
|
|
4316
4577
|
json
|
|
4317
4578
|
);
|
|
4318
4579
|
}
|
|
4319
4580
|
async function serviceTokenLoginFlow(input, rest, json) {
|
|
4320
4581
|
const apiBaseUrl = await resolveLoginApiBaseUrl({ argv: rest, env: input.env, homeDir: input.homeDir });
|
|
4321
|
-
const
|
|
4582
|
+
const result = await serviceTokenLogin({
|
|
4322
4583
|
apiBaseUrl,
|
|
4323
4584
|
appBaseUrl: resolveAppBaseUrl(input.env, apiBaseUrl),
|
|
4324
4585
|
rest,
|
|
@@ -4327,7 +4588,7 @@ async function serviceTokenLoginFlow(input, rest, json) {
|
|
|
4327
4588
|
credentialStore: input.credentialStore,
|
|
4328
4589
|
resolveCallerBearer: defaultCallerBearerResolver(input)
|
|
4329
4590
|
});
|
|
4330
|
-
return persistConvergedLogin(input, { profile:
|
|
4591
|
+
return persistConvergedLogin(input, { profile: result.profile, accessToken: result.token }, json);
|
|
4331
4592
|
}
|
|
4332
4593
|
function resolveOAuthConfigOrThrow(input, rest) {
|
|
4333
4594
|
const oauth = input.oauthConfig ?? resolveCliOAuthConfig({ argv: rest, env: input.env });
|
|
@@ -4388,19 +4649,19 @@ function resolveAppBaseUrl(env, apiBaseUrl) {
|
|
|
4388
4649
|
if (fromEnv !== void 0) return normalizeUrl(fromEnv);
|
|
4389
4650
|
return apiBaseUrl.replace(/\/\/api\./u, "//");
|
|
4390
4651
|
}
|
|
4391
|
-
async function persistConvergedLogin(input,
|
|
4392
|
-
const { profile } =
|
|
4652
|
+
async function persistConvergedLogin(input, result, json) {
|
|
4653
|
+
const { profile } = result;
|
|
4393
4654
|
try {
|
|
4394
4655
|
await input.credentialStore.write({
|
|
4395
4656
|
apiBaseUrl: profile.apiBaseUrl,
|
|
4396
4657
|
tokenId: profile.tokenId,
|
|
4397
|
-
secret:
|
|
4658
|
+
secret: result.accessToken
|
|
4398
4659
|
});
|
|
4399
|
-
if (
|
|
4660
|
+
if (result.refreshToken !== void 0) {
|
|
4400
4661
|
await input.credentialStore.write({
|
|
4401
4662
|
apiBaseUrl: profile.apiBaseUrl,
|
|
4402
4663
|
tokenId: refreshSlotTokenId(profile.tokenId),
|
|
4403
|
-
secret:
|
|
4664
|
+
secret: result.refreshToken
|
|
4404
4665
|
});
|
|
4405
4666
|
}
|
|
4406
4667
|
} catch (error) {
|
|
@@ -4510,9 +4771,9 @@ async function deviceLogin(input, rest, sleep, json) {
|
|
|
4510
4771
|
{ requestId: request.requestId, userCode: request.userCode }
|
|
4511
4772
|
);
|
|
4512
4773
|
if ("token" in token) {
|
|
4513
|
-
const
|
|
4514
|
-
return
|
|
4515
|
-
${
|
|
4774
|
+
const result = await persistLogin(input, token, json);
|
|
4775
|
+
return result.exitCode === 0 ? { ...result, stdout: `Code: ${request.userCode}
|
|
4776
|
+
${result.stdout}` } : result;
|
|
4516
4777
|
}
|
|
4517
4778
|
if (token.status === "authorization_pending" || token.status === "slow_down") {
|
|
4518
4779
|
intervalSeconds = token.intervalSeconds;
|
|
@@ -4536,8 +4797,8 @@ async function persistLogin(input, token, json) {
|
|
|
4536
4797
|
`Sift CLI login storage failure: ${error instanceof Error ? error.message : "credential store write failed"}`
|
|
4537
4798
|
);
|
|
4538
4799
|
}
|
|
4539
|
-
const
|
|
4540
|
-
await writeStoredSiftConfig({ homeDir: input.homeDir, config
|
|
4800
|
+
const config = configFromToken(token);
|
|
4801
|
+
await writeStoredSiftConfig({ homeDir: input.homeDir, config });
|
|
4541
4802
|
if (oldProfile !== void 0) {
|
|
4542
4803
|
const oldSecret = await input.credentialStore.read({
|
|
4543
4804
|
apiBaseUrl: oldProfile.apiBaseUrl,
|
|
@@ -4576,6 +4837,33 @@ async function authStatus(input, now, json) {
|
|
|
4576
4837
|
if (profile === void 0) {
|
|
4577
4838
|
return ok(json ? '{"auth":"none"}\n' : "Auth: none\n");
|
|
4578
4839
|
}
|
|
4840
|
+
const expired = Date.parse(profile.tokenExpiresAt) <= now.getTime();
|
|
4841
|
+
if (expired) {
|
|
4842
|
+
return staleStoredStatus(
|
|
4843
|
+
profile,
|
|
4844
|
+
"expired",
|
|
4845
|
+
"Stored Sift CLI auth has expired; run `sift login` again.",
|
|
4846
|
+
json
|
|
4847
|
+
);
|
|
4848
|
+
}
|
|
4849
|
+
let secret;
|
|
4850
|
+
try {
|
|
4851
|
+
secret = await input.credentialStore.read({
|
|
4852
|
+
apiBaseUrl: profile.apiBaseUrl,
|
|
4853
|
+
tokenId: profile.tokenId
|
|
4854
|
+
});
|
|
4855
|
+
} catch (error) {
|
|
4856
|
+
const message = error instanceof Error ? error.message : "Stored Sift credential store could not be read; run `sift login` again.";
|
|
4857
|
+
return staleStoredStatus(profile, "credential_store_unavailable", message, json);
|
|
4858
|
+
}
|
|
4859
|
+
if (secret === void 0) {
|
|
4860
|
+
return staleStoredStatus(
|
|
4861
|
+
profile,
|
|
4862
|
+
"credential_missing",
|
|
4863
|
+
"Stored Sift credential store secret is missing; run `sift login` again.",
|
|
4864
|
+
json
|
|
4865
|
+
);
|
|
4866
|
+
}
|
|
4579
4867
|
return ok(
|
|
4580
4868
|
json ? `${JSON.stringify({ auth: "stored", ...profile })}
|
|
4581
4869
|
` : [
|
|
@@ -4590,6 +4878,24 @@ async function authStatus(input, now, json) {
|
|
|
4590
4878
|
].join("\n")
|
|
4591
4879
|
);
|
|
4592
4880
|
}
|
|
4881
|
+
function staleStoredStatus(profile, reason, message, json) {
|
|
4882
|
+
if (json) {
|
|
4883
|
+
return ok(
|
|
4884
|
+
`${JSON.stringify({ auth: "stored", status: "stale", reason, message, ...profile })}
|
|
4885
|
+
`
|
|
4886
|
+
);
|
|
4887
|
+
}
|
|
4888
|
+
return ok(
|
|
4889
|
+
[
|
|
4890
|
+
"Auth: stale",
|
|
4891
|
+
`Reason: ${reason}`,
|
|
4892
|
+
message,
|
|
4893
|
+
`API: ${profile.apiBaseUrl}`,
|
|
4894
|
+
`Token: ${profile.tokenLabel}`,
|
|
4895
|
+
""
|
|
4896
|
+
].join("\n")
|
|
4897
|
+
);
|
|
4898
|
+
}
|
|
4593
4899
|
async function logout(input, now, json) {
|
|
4594
4900
|
if (clean2(input.env.SIFT_API_TOKEN) !== void 0) {
|
|
4595
4901
|
return ok(json ? '{"status":"env_auth_active"}\n' : "Auth: env\nUnset SIFT_API_TOKEN to log out.\n");
|
|
@@ -4681,6 +4987,249 @@ function failJson(message) {
|
|
|
4681
4987
|
};
|
|
4682
4988
|
}
|
|
4683
4989
|
|
|
4990
|
+
// src/roamMcpReader.ts
|
|
4991
|
+
import { spawn } from "child_process";
|
|
4992
|
+
function createRoamMcpReader(input = {}) {
|
|
4993
|
+
return {
|
|
4994
|
+
async exportPages(request) {
|
|
4995
|
+
const client = createRoamMcpJsonLineClient({
|
|
4996
|
+
command: input.command ?? "npx",
|
|
4997
|
+
args: input.args ?? ["-y", "@roam-research/roam-mcp"],
|
|
4998
|
+
spawnProcess: input.spawnProcess ?? spawn
|
|
4999
|
+
});
|
|
5000
|
+
try {
|
|
5001
|
+
return await exportRoamPagesFromMcp(client, request);
|
|
5002
|
+
} finally {
|
|
5003
|
+
await client.close();
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
};
|
|
5007
|
+
}
|
|
5008
|
+
async function exportRoamPagesFromMcp(client, input) {
|
|
5009
|
+
const graphId = await resolveGraphId(client, input.graph);
|
|
5010
|
+
await client.callTool("get_graph_guidelines", graphArgs(graphId)).catch(() => void 0);
|
|
5011
|
+
const tuples = input.scope === "sift_tag" ? await queryMarkedPageTuples(client, graphId, input.limit) : await queryWholeGraphPageTuples(client, graphId, input.limit);
|
|
5012
|
+
const records = [];
|
|
5013
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5014
|
+
for (const tuple of tuples) {
|
|
5015
|
+
const key = tuple.uid ?? tuple.title;
|
|
5016
|
+
if (seen.has(key)) continue;
|
|
5017
|
+
seen.add(key);
|
|
5018
|
+
const markdown = await fetchPageMarkdown(client, graphId, tuple);
|
|
5019
|
+
records.push({
|
|
5020
|
+
graphId,
|
|
5021
|
+
pageUid: tuple.uid ?? stableFallbackUid(tuple.title),
|
|
5022
|
+
pageTitle: tuple.title,
|
|
5023
|
+
markdown,
|
|
5024
|
+
scope: input.scope,
|
|
5025
|
+
blockCount: estimateBlockCount(markdown),
|
|
5026
|
+
importedAt: input.now.toISOString()
|
|
5027
|
+
});
|
|
5028
|
+
}
|
|
5029
|
+
return records;
|
|
5030
|
+
}
|
|
5031
|
+
async function resolveGraphId(client, graph) {
|
|
5032
|
+
if (graph !== void 0) return graph;
|
|
5033
|
+
const result = await client.callTool("list_graphs", {});
|
|
5034
|
+
const parsed = parseToolJson(result);
|
|
5035
|
+
const graphId = firstGraphId(parsed);
|
|
5036
|
+
if (graphId !== void 0) return graphId;
|
|
5037
|
+
const message = errorMessageFromParsedTool(parsed);
|
|
5038
|
+
if (message !== void 0) throw new Error(message);
|
|
5039
|
+
return "local-graph";
|
|
5040
|
+
}
|
|
5041
|
+
async function queryMarkedPageTuples(client, graphId, limit) {
|
|
5042
|
+
const result = await client.callTool("datalog_query", {
|
|
5043
|
+
...graphArgs(graphId),
|
|
5044
|
+
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]]'
|
|
5045
|
+
});
|
|
5046
|
+
return tuplesFromToolResult(result).slice(0, limit);
|
|
5047
|
+
}
|
|
5048
|
+
async function queryWholeGraphPageTuples(client, graphId, limit) {
|
|
5049
|
+
const result = await client.callTool("datalog_query", {
|
|
5050
|
+
...graphArgs(graphId),
|
|
5051
|
+
query: "[:find ?title ?uid :where [?page :node/title ?title] [?page :block/uid ?uid]]"
|
|
5052
|
+
});
|
|
5053
|
+
return tuplesFromToolResult(result).slice(0, limit);
|
|
5054
|
+
}
|
|
5055
|
+
async function fetchPageMarkdown(client, graphId, tuple) {
|
|
5056
|
+
const result = await client.callTool("get_page", {
|
|
5057
|
+
...graphArgs(graphId),
|
|
5058
|
+
...tuple.uid === void 0 ? { title: tuple.title } : { uid: tuple.uid }
|
|
5059
|
+
});
|
|
5060
|
+
const text = toolText(result);
|
|
5061
|
+
const parsed = parseJsonIfPossible(text);
|
|
5062
|
+
const markdown = markdownFromParsed(parsed) ?? text;
|
|
5063
|
+
return stripRoamMetadataTags(markdown).trim();
|
|
5064
|
+
}
|
|
5065
|
+
function graphArgs(graphId) {
|
|
5066
|
+
return { graph: graphId };
|
|
5067
|
+
}
|
|
5068
|
+
function tuplesFromToolResult(result) {
|
|
5069
|
+
const parsed = parseToolJson(result);
|
|
5070
|
+
const values = candidateArrays(parsed);
|
|
5071
|
+
const tuples = [];
|
|
5072
|
+
for (const value of values) {
|
|
5073
|
+
if (Array.isArray(value) && typeof value[0] === "string") {
|
|
5074
|
+
tuples.push({ title: value[0], ...typeof value[1] === "string" ? { uid: value[1] } : {} });
|
|
5075
|
+
} else if (isRecord(value)) {
|
|
5076
|
+
const title = stringProperty(value, ["title", "pageTitle", "name"]);
|
|
5077
|
+
const uid = stringProperty(value, ["uid", "pageUid"]);
|
|
5078
|
+
if (title !== void 0) tuples.push({ title, ...uid === void 0 ? {} : { uid } });
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
return tuples;
|
|
5082
|
+
}
|
|
5083
|
+
function parseToolJson(result) {
|
|
5084
|
+
const text = toolText(result);
|
|
5085
|
+
return parseJsonIfPossible(text);
|
|
5086
|
+
}
|
|
5087
|
+
function toolText(result) {
|
|
5088
|
+
if (isRecord(result) && Array.isArray(result.content)) {
|
|
5089
|
+
return result.content.flatMap((item) => isRecord(item) && typeof item.text === "string" ? [item.text] : []).join("\n").trim();
|
|
5090
|
+
}
|
|
5091
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
5092
|
+
}
|
|
5093
|
+
function parseJsonIfPossible(text) {
|
|
5094
|
+
try {
|
|
5095
|
+
return JSON.parse(text);
|
|
5096
|
+
} catch {
|
|
5097
|
+
return text;
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
function candidateArrays(value) {
|
|
5101
|
+
if (Array.isArray(value)) return value;
|
|
5102
|
+
if (!isRecord(value)) return [];
|
|
5103
|
+
for (const key of ["results", "result", "data", "rows", "pages"]) {
|
|
5104
|
+
const candidate = value[key];
|
|
5105
|
+
if (Array.isArray(candidate)) return candidate;
|
|
5106
|
+
}
|
|
5107
|
+
return [];
|
|
5108
|
+
}
|
|
5109
|
+
function markdownFromParsed(value) {
|
|
5110
|
+
if (typeof value === "string") return value;
|
|
5111
|
+
if (!isRecord(value)) return void 0;
|
|
5112
|
+
return stringProperty(value, ["markdown", "content", "text"]);
|
|
5113
|
+
}
|
|
5114
|
+
function firstGraphId(value) {
|
|
5115
|
+
const graphs = isRecord(value) && Array.isArray(value.graphs) ? value.graphs : candidateArrays(value);
|
|
5116
|
+
for (const graph of graphs) {
|
|
5117
|
+
if (!isRecord(graph)) continue;
|
|
5118
|
+
const id = stringProperty(graph, ["nickname", "graph", "name", "id"]);
|
|
5119
|
+
if (id !== void 0) return id;
|
|
5120
|
+
}
|
|
5121
|
+
return void 0;
|
|
5122
|
+
}
|
|
5123
|
+
function errorMessageFromParsedTool(value) {
|
|
5124
|
+
if (!isRecord(value) || !isRecord(value.error)) return void 0;
|
|
5125
|
+
const message = value.error.message;
|
|
5126
|
+
return typeof message === "string" ? message : void 0;
|
|
5127
|
+
}
|
|
5128
|
+
function stringProperty(record, keys) {
|
|
5129
|
+
for (const key of keys) {
|
|
5130
|
+
const value = record[key];
|
|
5131
|
+
if (typeof value === "string" && value.trim().length > 0) return value;
|
|
5132
|
+
}
|
|
5133
|
+
return void 0;
|
|
5134
|
+
}
|
|
5135
|
+
function stripRoamMetadataTags(markdown) {
|
|
5136
|
+
return markdown.replace(/<roam\b[^>]*>/giu, "").replace(/<\/roam>/giu, "");
|
|
5137
|
+
}
|
|
5138
|
+
function estimateBlockCount(markdown) {
|
|
5139
|
+
const lines = markdown.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
5140
|
+
return Math.max(1, lines.length);
|
|
5141
|
+
}
|
|
5142
|
+
function stableFallbackUid(title) {
|
|
5143
|
+
return title.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 80);
|
|
5144
|
+
}
|
|
5145
|
+
function isRecord(value) {
|
|
5146
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5147
|
+
}
|
|
5148
|
+
function createRoamMcpJsonLineClient(input) {
|
|
5149
|
+
const child = input.spawnProcess(input.command, input.args, {
|
|
5150
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5151
|
+
});
|
|
5152
|
+
child.stderr.resume();
|
|
5153
|
+
child.stdout.setEncoding("utf8");
|
|
5154
|
+
let nextId = 1;
|
|
5155
|
+
let buffer = "";
|
|
5156
|
+
const pending = /* @__PURE__ */ new Map();
|
|
5157
|
+
child.stdout.on("data", (chunk) => {
|
|
5158
|
+
buffer += chunk;
|
|
5159
|
+
let newline = buffer.indexOf("\n");
|
|
5160
|
+
while (newline >= 0) {
|
|
5161
|
+
const line = buffer.slice(0, newline).trim();
|
|
5162
|
+
buffer = buffer.slice(newline + 1);
|
|
5163
|
+
if (line.length > 0) handleJsonRpcLine(line, pending);
|
|
5164
|
+
newline = buffer.indexOf("\n");
|
|
5165
|
+
}
|
|
5166
|
+
});
|
|
5167
|
+
child.on("error", (error) => {
|
|
5168
|
+
for (const entry of pending.values()) entry.reject(error);
|
|
5169
|
+
pending.clear();
|
|
5170
|
+
});
|
|
5171
|
+
child.on("exit", () => {
|
|
5172
|
+
for (const entry of pending.values()) entry.reject(new Error("Roam MCP server exited."));
|
|
5173
|
+
pending.clear();
|
|
5174
|
+
});
|
|
5175
|
+
const request = (method, params) => {
|
|
5176
|
+
const id = nextId;
|
|
5177
|
+
nextId += 1;
|
|
5178
|
+
const promise = new Promise((resolve2, reject) => {
|
|
5179
|
+
pending.set(id, { resolve: resolve2, reject });
|
|
5180
|
+
});
|
|
5181
|
+
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
|
|
5182
|
+
`);
|
|
5183
|
+
return promise;
|
|
5184
|
+
};
|
|
5185
|
+
const initialized = request("initialize", {
|
|
5186
|
+
protocolVersion: "2025-11-25",
|
|
5187
|
+
capabilities: {},
|
|
5188
|
+
clientInfo: { name: "sift-roam-import", version: "0.1.0" }
|
|
5189
|
+
}).then(() => {
|
|
5190
|
+
child.stdin.write(
|
|
5191
|
+
`${JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" })}
|
|
5192
|
+
`
|
|
5193
|
+
);
|
|
5194
|
+
});
|
|
5195
|
+
return {
|
|
5196
|
+
async callTool(name, args) {
|
|
5197
|
+
await initialized;
|
|
5198
|
+
return request("tools/call", { name, arguments: args });
|
|
5199
|
+
},
|
|
5200
|
+
async close() {
|
|
5201
|
+
child.stdin.end();
|
|
5202
|
+
child.kill("SIGTERM");
|
|
5203
|
+
}
|
|
5204
|
+
};
|
|
5205
|
+
}
|
|
5206
|
+
function handleJsonRpcLine(line, pending) {
|
|
5207
|
+
let parsed;
|
|
5208
|
+
try {
|
|
5209
|
+
parsed = JSON.parse(line);
|
|
5210
|
+
} catch {
|
|
5211
|
+
return;
|
|
5212
|
+
}
|
|
5213
|
+
if (typeof parsed.id !== "number") return;
|
|
5214
|
+
const entry = pending.get(parsed.id);
|
|
5215
|
+
if (entry === void 0) return;
|
|
5216
|
+
pending.delete(parsed.id);
|
|
5217
|
+
if (parsed.error !== void 0) {
|
|
5218
|
+
entry.reject(
|
|
5219
|
+
new Error(
|
|
5220
|
+
typeof parsed.error.message === "string" ? parsed.error.message : "Roam MCP error."
|
|
5221
|
+
)
|
|
5222
|
+
);
|
|
5223
|
+
return;
|
|
5224
|
+
}
|
|
5225
|
+
entry.resolve(parsed.result);
|
|
5226
|
+
}
|
|
5227
|
+
|
|
5228
|
+
// src/startupAuthMessage.ts
|
|
5229
|
+
function formatMissingStartupAuthMessage(_args) {
|
|
5230
|
+
return "Not signed in. Run 'npx -y @sift-wiki/cli@latest login', then retry this command.";
|
|
5231
|
+
}
|
|
5232
|
+
|
|
4684
5233
|
// src/bin/sift.ts
|
|
4685
5234
|
var credentialStore = createMacOSKeychainStore();
|
|
4686
5235
|
var authCommands = createSiftCliAuthCommands({
|
|
@@ -4689,51 +5238,86 @@ var authCommands = createSiftCliAuthCommands({
|
|
|
4689
5238
|
credentialStore,
|
|
4690
5239
|
fetch
|
|
4691
5240
|
});
|
|
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
5241
|
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
|
-
|
|
5242
|
+
var startupAuth = await loadAuthForStartup(authCommands, argv);
|
|
5243
|
+
if (startupAuth.ok === false) {
|
|
5244
|
+
process.stderr.write(`${startupAuth.message}
|
|
5245
|
+
`);
|
|
5246
|
+
process.exitCode = 1;
|
|
5247
|
+
} else {
|
|
5248
|
+
const loadedAuth = startupAuth.loadedAuth;
|
|
5249
|
+
const config = loadedAuth?.config ?? {
|
|
5250
|
+
apiBaseUrl: "",
|
|
5251
|
+
tokenLabel: "unset",
|
|
5252
|
+
workspaceId: "",
|
|
5253
|
+
brainId: "",
|
|
5254
|
+
principalId: "",
|
|
5255
|
+
capabilities: []
|
|
5256
|
+
};
|
|
5257
|
+
const result = await runSiftCli({
|
|
5258
|
+
argv,
|
|
5259
|
+
config,
|
|
5260
|
+
readStdin,
|
|
5261
|
+
agentName,
|
|
5262
|
+
executor: loadedAuth === void 0 ? void 0 : createHostedApiExecutor({
|
|
5263
|
+
apiBaseUrl: loadedAuth.config.apiBaseUrl,
|
|
5264
|
+
token: loadedAuth.token,
|
|
5265
|
+
workspaceId: loadedAuth.config.workspaceId,
|
|
5266
|
+
brainId: loadedAuth.config.brainId,
|
|
5267
|
+
agentName
|
|
5268
|
+
}),
|
|
5269
|
+
roamReader: createRoamMcpReader(),
|
|
5270
|
+
roamImporter: loadedAuth === void 0 ? void 0 : createSiftRoamImportClient({
|
|
5271
|
+
apiBaseUrl: loadedAuth.config.apiBaseUrl,
|
|
5272
|
+
token: loadedAuth.token,
|
|
5273
|
+
workspaceId: loadedAuth.config.workspaceId
|
|
5274
|
+
}),
|
|
5275
|
+
authCommands,
|
|
5276
|
+
mcpServer: {
|
|
5277
|
+
serve: async ({ config: config2, executor }) => {
|
|
5278
|
+
if (executor === void 0) {
|
|
5279
|
+
throw new Error(
|
|
5280
|
+
"Not signed in. Run 'sift login' to authenticate, then 'sift mcp serve' to start the local MCP server."
|
|
5281
|
+
);
|
|
5282
|
+
}
|
|
5283
|
+
const { createLocalMcpStdioServer: createLocalMcpStdioServer2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
5284
|
+
return createLocalMcpStdioServer2({
|
|
5285
|
+
input: process.stdin,
|
|
5286
|
+
output: process.stdout,
|
|
5287
|
+
error: process.stderr
|
|
5288
|
+
}).serve({
|
|
5289
|
+
capabilities: config2.capabilities,
|
|
5290
|
+
executor
|
|
5291
|
+
});
|
|
4721
5292
|
}
|
|
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
5293
|
}
|
|
5294
|
+
});
|
|
5295
|
+
process.stdout.write(result.stdout);
|
|
5296
|
+
process.stderr.write(result.stderr);
|
|
5297
|
+
process.exitCode = result.exitCode;
|
|
5298
|
+
}
|
|
5299
|
+
async function loadAuthForStartup(commands, args) {
|
|
5300
|
+
try {
|
|
5301
|
+
const loadedAuth = await commands.loadAuth();
|
|
5302
|
+
if (loadedAuth === void 0 && !canRunWithoutLoadedAuth(args)) {
|
|
5303
|
+
return { ok: false, message: formatMissingStartupAuthMessage(args) };
|
|
5304
|
+
}
|
|
5305
|
+
return { ok: true, loadedAuth };
|
|
5306
|
+
} catch (error) {
|
|
5307
|
+
if (canRunWithoutLoadedAuth(args)) {
|
|
5308
|
+
return { ok: true, loadedAuth: void 0 };
|
|
5309
|
+
}
|
|
5310
|
+
return {
|
|
5311
|
+
ok: false,
|
|
5312
|
+
message: error instanceof Error ? error.message : "Failed to load Sift CLI auth."
|
|
5313
|
+
};
|
|
4732
5314
|
}
|
|
4733
|
-
}
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
5315
|
+
}
|
|
5316
|
+
function canRunWithoutLoadedAuth(args) {
|
|
5317
|
+
const commandArgs = args.filter((arg) => arg !== "--json");
|
|
5318
|
+
const [group, command] = commandArgs;
|
|
5319
|
+
return group === void 0 || group === "help" || group === "--help" || group === "doctor" || group === "skill" || group === "login" || group === "logout" || group === "auth" && command === "status";
|
|
5320
|
+
}
|
|
4737
5321
|
function extractAgentName(args, envAgentName) {
|
|
4738
5322
|
const flagIndex = args.indexOf("--as-agent");
|
|
4739
5323
|
if (flagIndex !== -1 && args[flagIndex + 1] !== void 0) {
|