@openclaw/crabline 0.1.9 → 0.1.10
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 +47 -22
- package/assets/crabline-banner.svg +20 -0
- package/dist/src/bin/crabline.js.map +1 -1
- package/dist/src/cli/program.d.ts +25 -1
- package/dist/src/cli/program.js +579 -82
- package/dist/src/cli/program.js.map +1 -1
- package/dist/src/config/load.js +59 -6
- package/dist/src/config/load.js.map +1 -1
- package/dist/src/config/schema.d.ts +116 -92
- package/dist/src/config/schema.js +179 -73
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/core/errors.js +9 -2
- package/dist/src/core/errors.js.map +1 -1
- package/dist/src/core/exit-codes.js.map +1 -1
- package/dist/src/core/matcher.js +7 -6
- package/dist/src/core/matcher.js.map +1 -1
- package/dist/src/core/message-template.js.map +1 -1
- package/dist/src/core/nonces.d.ts +1 -0
- package/dist/src/core/nonces.js +4 -2
- package/dist/src/core/nonces.js.map +1 -1
- package/dist/src/core/reporters.d.ts +1 -0
- package/dist/src/core/reporters.js +42 -4
- package/dist/src/core/reporters.js.map +1 -1
- package/dist/src/core/run.d.ts +3 -0
- package/dist/src/core/run.js +415 -107
- package/dist/src/core/run.js.map +1 -1
- package/dist/src/core/safe-regex.d.ts +3 -0
- package/dist/src/core/safe-regex.js +21 -0
- package/dist/src/core/safe-regex.js.map +1 -0
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/openclaw/artifact-generation.d.ts +38 -0
- package/dist/src/openclaw/artifact-generation.js +314 -0
- package/dist/src/openclaw/artifact-generation.js.map +1 -0
- package/dist/src/openclaw/bridges/matrix.js +97 -12
- package/dist/src/openclaw/bridges/matrix.js.map +1 -1
- package/dist/src/openclaw/bridges/mattermost.js +32 -10
- package/dist/src/openclaw/bridges/mattermost.js.map +1 -1
- package/dist/src/openclaw/bridges/signal.js +55 -18
- package/dist/src/openclaw/bridges/signal.js.map +1 -1
- package/dist/src/openclaw/bridges/slack.js +85 -7
- package/dist/src/openclaw/bridges/slack.js.map +1 -1
- package/dist/src/openclaw/bridges/telegram.js +124 -30
- package/dist/src/openclaw/bridges/telegram.js.map +1 -1
- package/dist/src/openclaw/bridges/whatsapp.js +78 -17
- package/dist/src/openclaw/bridges/whatsapp.js.map +1 -1
- package/dist/src/openclaw/bridges/zalo.js +14 -5
- package/dist/src/openclaw/bridges/zalo.js.map +1 -1
- package/dist/src/openclaw/outbound-contract.d.ts +5 -0
- package/dist/src/openclaw/outbound-contract.js +40 -0
- package/dist/src/openclaw/outbound-contract.js.map +1 -0
- package/dist/src/openclaw/private-file.d.ts +26 -0
- package/dist/src/openclaw/private-file.js +411 -0
- package/dist/src/openclaw/private-file.js.map +1 -0
- package/dist/src/openclaw/shared.d.ts +21 -2
- package/dist/src/openclaw/shared.js +115 -18
- package/dist/src/openclaw/shared.js.map +1 -1
- package/dist/src/openclaw/smoke-lock.d.ts +63 -0
- package/dist/src/openclaw/smoke-lock.js +949 -0
- package/dist/src/openclaw/smoke-lock.js.map +1 -0
- package/dist/src/openclaw.d.ts +16 -8
- package/dist/src/openclaw.js +194 -52
- package/dist/src/openclaw.js.map +1 -1
- package/dist/src/providers/builtin/discord.d.ts +10 -1
- package/dist/src/providers/builtin/discord.js +144 -18
- package/dist/src/providers/builtin/discord.js.map +1 -1
- package/dist/src/providers/builtin/external-webhook-auth.d.ts +11 -0
- package/dist/src/providers/builtin/external-webhook-auth.js +10 -0
- package/dist/src/providers/builtin/external-webhook-auth.js.map +1 -0
- package/dist/src/providers/builtin/feishu.d.ts +19 -2
- package/dist/src/providers/builtin/feishu.js +307 -25
- package/dist/src/providers/builtin/feishu.js.map +1 -1
- package/dist/src/providers/builtin/googlechat.d.ts +20 -1
- package/dist/src/providers/builtin/googlechat.js +178 -19
- package/dist/src/providers/builtin/googlechat.js.map +1 -1
- package/dist/src/providers/builtin/imessage.d.ts +4 -0
- package/dist/src/providers/builtin/imessage.js +26 -11
- package/dist/src/providers/builtin/imessage.js.map +1 -1
- package/dist/src/providers/builtin/loopback.js +92 -30
- package/dist/src/providers/builtin/loopback.js.map +1 -1
- package/dist/src/providers/builtin/matrix.d.ts +11 -1
- package/dist/src/providers/builtin/matrix.js +53 -29
- package/dist/src/providers/builtin/matrix.js.map +1 -1
- package/dist/src/providers/builtin/mattermost.d.ts +10 -0
- package/dist/src/providers/builtin/mattermost.js +38 -15
- package/dist/src/providers/builtin/mattermost.js.map +1 -1
- package/dist/src/providers/builtin/msteams.d.ts +17 -1
- package/dist/src/providers/builtin/msteams.js +113 -14
- package/dist/src/providers/builtin/msteams.js.map +1 -1
- package/dist/src/providers/builtin/native-local-mock.d.ts +2 -9
- package/dist/src/providers/builtin/native-local-mock.js +12 -35
- package/dist/src/providers/builtin/native-local-mock.js.map +1 -1
- package/dist/src/providers/builtin/script.d.ts +11 -15
- package/dist/src/providers/builtin/script.js +566 -104
- package/dist/src/providers/builtin/script.js.map +1 -1
- package/dist/src/providers/builtin/slack.d.ts +6 -0
- package/dist/src/providers/builtin/slack.js +99 -40
- package/dist/src/providers/builtin/slack.js.map +1 -1
- package/dist/src/providers/builtin/telegram.d.ts +6 -0
- package/dist/src/providers/builtin/telegram.js +64 -43
- package/dist/src/providers/builtin/telegram.js.map +1 -1
- package/dist/src/providers/builtin/whatsapp.d.ts +28 -8
- package/dist/src/providers/builtin/whatsapp.js +567 -30
- package/dist/src/providers/builtin/whatsapp.js.map +1 -1
- package/dist/src/providers/builtin/zalo.js +30 -19
- package/dist/src/providers/builtin/zalo.js.map +1 -1
- package/dist/src/providers/catalog.d.ts +3 -3
- package/dist/src/providers/catalog.js +3 -3
- package/dist/src/providers/catalog.js.map +1 -1
- package/dist/src/providers/local-mock.d.ts +11 -2
- package/dist/src/providers/local-mock.js +322 -96
- package/dist/src/providers/local-mock.js.map +1 -1
- package/dist/src/providers/native-ids.d.ts +1 -0
- package/dist/src/providers/native-ids.js +6 -1
- package/dist/src/providers/native-ids.js.map +1 -1
- package/dist/src/providers/recorder.d.ts +30 -2
- package/dist/src/providers/recorder.js +539 -70
- package/dist/src/providers/recorder.js.map +1 -1
- package/dist/src/providers/registry.d.ts +25 -1
- package/dist/src/providers/registry.js +280 -40
- package/dist/src/providers/registry.js.map +1 -1
- package/dist/src/providers/signed-jwt.d.ts +29 -0
- package/dist/src/providers/signed-jwt.js +202 -0
- package/dist/src/providers/signed-jwt.js.map +1 -0
- package/dist/src/providers/slack-ids.js.map +1 -1
- package/dist/src/providers/target-normalizers.d.ts +26 -0
- package/dist/src/providers/target-normalizers.js +276 -0
- package/dist/src/providers/target-normalizers.js.map +1 -0
- package/dist/src/providers/types.d.ts +5 -0
- package/dist/src/providers/types.js.map +1 -1
- package/dist/src/providers/webhook-server.d.ts +2 -0
- package/dist/src/providers/webhook-server.js +72 -25
- package/dist/src/providers/webhook-server.js.map +1 -1
- package/dist/src/servers/http.d.ts +23 -3
- package/dist/src/servers/http.js +213 -20
- package/dist/src/servers/http.js.map +1 -1
- package/dist/src/servers/index.js +20 -21
- package/dist/src/servers/index.js.map +1 -1
- package/dist/src/servers/matrix.js +465 -51
- package/dist/src/servers/matrix.js.map +1 -1
- package/dist/src/servers/mattermost.d.ts +5 -0
- package/dist/src/servers/mattermost.js +306 -58
- package/dist/src/servers/mattermost.js.map +1 -1
- package/dist/src/servers/pending-events.d.ts +2 -0
- package/dist/src/servers/pending-events.js +11 -0
- package/dist/src/servers/pending-events.js.map +1 -0
- package/dist/src/servers/recorder.d.ts +5 -0
- package/dist/src/servers/recorder.js +46 -3
- package/dist/src/servers/recorder.js.map +1 -1
- package/dist/src/servers/signal.d.ts +2 -0
- package/dist/src/servers/signal.js +444 -56
- package/dist/src/servers/signal.js.map +1 -1
- package/dist/src/servers/slack.d.ts +5 -0
- package/dist/src/servers/slack.js +515 -49
- package/dist/src/servers/slack.js.map +1 -1
- package/dist/src/servers/telegram.d.ts +111 -0
- package/dist/src/servers/telegram.js +811 -152
- package/dist/src/servers/telegram.js.map +1 -1
- package/dist/src/servers/webhook-target.d.ts +40 -0
- package/dist/src/servers/webhook-target.js +349 -0
- package/dist/src/servers/webhook-target.js.map +1 -0
- package/dist/src/servers/websocket.d.ts +2 -0
- package/dist/src/servers/websocket.js +29 -0
- package/dist/src/servers/websocket.js.map +1 -0
- package/dist/src/servers/whatsapp-baileys-websocket.d.ts +65 -1
- package/dist/src/servers/whatsapp-baileys-websocket.js +723 -99
- package/dist/src/servers/whatsapp-baileys-websocket.js.map +1 -1
- package/dist/src/servers/whatsapp-jid.d.ts +5 -0
- package/dist/src/servers/whatsapp-jid.js +32 -0
- package/dist/src/servers/whatsapp-jid.js.map +1 -0
- package/dist/src/servers/whatsapp-wire/binary-node.d.ts +5 -0
- package/dist/src/servers/whatsapp-wire/binary-node.js +87 -20
- package/dist/src/servers/whatsapp-wire/binary-node.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/crypto.d.ts +6 -1
- package/dist/src/servers/whatsapp-wire/crypto.js +99 -45
- package/dist/src/servers/whatsapp-wire/crypto.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/handshake.js +39 -4
- package/dist/src/servers/whatsapp-wire/handshake.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/signal.js.map +1 -1
- package/dist/src/servers/whatsapp.d.ts +12 -7
- package/dist/src/servers/whatsapp.js +269 -107
- package/dist/src/servers/whatsapp.js.map +1 -1
- package/dist/src/servers/zalo.d.ts +14 -0
- package/dist/src/servers/zalo.js +521 -88
- package/dist/src/servers/zalo.js.map +1 -1
- package/docs/channel-setup.md +197 -19
- package/fixtures/examples/crabline.example.yaml +10 -0
- package/package.json +20 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whatsapp.js","sourceRoot":"","sources":["../../../../src/openclaw/bridges/whatsapp.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"whatsapp.js","sourceRoot":"","sources":["../../../../src/openclaw/bridges/whatsapp.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createAdminInboundRequest,\n createOpenClawCrablineProviderBridge,\n DEFAULT_ACCOUNT_ID,\n isRecord,\n qaTargetForInbound,\n readNonBlankString,\n readString,\n} from \"../shared.js\";\nimport {\n canonicalizeWhatsAppChatJid,\n canonicalizeWhatsAppUserCorrelationJid,\n canonicalizeWhatsAppUserJid,\n} from \"../../servers/whatsapp-jid.js\";\n\nfunction requireWhatsAppJid(value: string, label: string, userOnly = false): string {\n const canonical = userOnly\n ? canonicalizeWhatsAppUserJid(value)\n : canonicalizeWhatsAppChatJid(value);\n if (!canonical) {\n throw new Error(`${label} must be a native WhatsApp JID.`);\n }\n return canonical;\n}\n\nfunction requireWhatsAppTargetKind(\n parsed: { kind: \"direct\" | \"group\"; native: boolean },\n targetId: string,\n): void {\n if (parsed.native) {\n return;\n }\n const nativeKind = targetId.endsWith(\"@g.us\") ? \"group\" : \"direct\";\n if (parsed.kind !== nativeKind) {\n throw new Error(\"WhatsApp target kind does not match the native JID.\");\n }\n}\n\nfunction requireWhatsAppConversationKind(kind: \"direct\" | \"group\", targetId: string): void {\n const nativeKind = targetId.endsWith(\"@g.us\") ? \"group\" : \"direct\";\n if (kind !== nativeKind) {\n throw new Error(\"WhatsApp inbound conversation kind does not match the native JID.\");\n }\n}\n\nexport const WHATSAPP_OPENCLAW_CRABLINE_PROVIDER_BRIDGE = createOpenClawCrablineProviderBridge({\n provider: \"whatsapp\",\n createAdapter(whatsapp) {\n const messagesPath = new URL(whatsapp.endpoints.messagesUrl).pathname;\n return {\n async probe(signal) {\n const response = await fetch(whatsapp.endpoints.phoneNumberUrl, {\n headers: {\n authorization: `Bearer ${whatsapp.accessToken}`,\n },\n ...(signal ? { signal } : {}),\n });\n if (!response.ok) {\n throw new Error(`Crabline WhatsApp probe failed with HTTP ${response.status}.`);\n }\n const payload: unknown = await response.json();\n if (!isRecord(payload) || readString(payload.id) !== whatsapp.phoneNumberId) {\n throw new Error(\"Crabline WhatsApp probe returned an unexpected phone number.\");\n }\n return payload;\n },\n createBinding() {\n return {\n accountId: DEFAULT_ACCOUNT_ID,\n channel: \"whatsapp\",\n createChannelDriverSmokeEnv: (env) => ({\n ...env,\n CRABLINE_WHATSAPP_ADMIN_TOKEN: whatsapp.adminToken,\n CRABLINE_WHATSAPP_RECORDER_PATH: whatsapp.recorderPath,\n CRABLINE_WHATSAPP_SELF_JID: whatsapp.selfJid,\n OPENCLAW_WHATSAPP_WEB_SOCKET_URL: whatsapp.endpoints.baileysWebSocketUrl,\n }),\n createGatewayConfig: (openclawConfig = {}) => {\n const channels = isRecord(openclawConfig.channels) ? openclawConfig.channels : {};\n const whatsappConfig = isRecord(channels.whatsapp) ? channels.whatsapp : {};\n const groups = isRecord(whatsappConfig.groups) ? whatsappConfig.groups : {};\n const defaultGroup = isRecord(groups[\"*\"]) ? groups[\"*\"] : {};\n\n return {\n ...openclawConfig,\n channels: {\n ...channels,\n whatsapp: {\n ...whatsappConfig,\n enabled: true,\n dmPolicy: \"open\",\n groupPolicy: \"open\",\n allowFrom: [\"*\"],\n groupAllowFrom: [\"*\"],\n groups: {\n ...groups,\n \"*\": {\n ...defaultGroup,\n requireMention: false,\n },\n },\n },\n },\n };\n },\n requiredPluginIds: [\"whatsapp\"],\n };\n },\n createAgentDelivery(parsed) {\n if (parsed.threadId !== undefined) {\n throw new Error(\"WhatsApp does not support thread targets.\");\n }\n const to = requireWhatsAppJid(parsed.id, \"WhatsApp target\");\n requireWhatsAppTargetKind(parsed, to);\n if (to.endsWith(\"@g.us\")) {\n throw new Error(\"WhatsApp Crabline WebSocket outbound supports direct targets only.\");\n }\n return {\n channel: \"whatsapp\",\n to,\n replyChannel: \"whatsapp\",\n replyTo: to,\n };\n },\n createInbound(input) {\n if (input.threadId !== undefined) {\n throw new Error(\"WhatsApp does not support thread targets.\");\n }\n const chatJid = requireWhatsAppJid(input.conversation.id, \"WhatsApp conversation\");\n requireWhatsAppConversationKind(input.conversation.kind, chatJid);\n const senderJid = requireWhatsAppJid(input.senderId, \"WhatsApp sender\", true);\n return {\n ...createAdminInboundRequest(whatsapp),\n providerBody: {\n chatJid,\n senderJid,\n ...(input.senderName ? { pushName: input.senderName } : {}),\n text: input.text,\n },\n providerTargetKey: chatJid,\n qaTarget: qaTargetForInbound({\n ...input,\n conversation: { ...input.conversation, id: chatJid },\n }),\n stateConversation: {\n id: chatJid,\n kind: chatJid.endsWith(\"@g.us\") ? \"group\" : \"direct\",\n },\n };\n },\n createOutboundFromRecorderEvent({ event, targetByProviderTarget }) {\n if (\n !isRecord(event) ||\n event.type !== \"api\" ||\n event.accepted !== true ||\n typeof event.path !== \"string\"\n ) {\n return null;\n }\n if (!isRecord(event.body)) {\n return null;\n }\n const baileysKey = isRecord(event.body.key) ? event.body.key : undefined;\n const baileysMessage = isRecord(event.body.message) ? event.body.message : undefined;\n const isBaileysSend = event.method === \"WEBSOCKET\" && event.path === \"/ws/chat\";\n if (!isBaileysSend && event.path !== messagesPath) {\n return null;\n }\n const to = isBaileysSend ? readString(baileysKey?.remoteJid) : readString(event.body.to);\n const textPayload = event.body.text;\n const text = isBaileysSend\n ? readNonBlankString(baileysMessage?.conversation)\n : isRecord(textPayload)\n ? readNonBlankString(textPayload.body)\n : undefined;\n if (!to || !text) {\n return null;\n }\n const providerTarget = isBaileysSend\n ? canonicalizeWhatsAppUserCorrelationJid(to)\n : (canonicalizeWhatsAppChatJid(to) ??\n (/^\\d{7,15}$/u.test(to) ? `${to}@s.whatsapp.net` : to));\n if (!providerTarget) {\n return null;\n }\n return {\n accountId: DEFAULT_ACCOUNT_ID,\n senderId: \"openclaw\",\n senderName: \"OpenClaw QA\",\n text,\n to: targetByProviderTarget.get(providerTarget) ?? providerTarget,\n };\n },\n };\n },\n});\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createAdminInboundRequest, createOpenClawCrablineProviderBridge, DEFAULT_ACCOUNT_ID, isRecord, qaTargetForInbound, readString, } from "../shared.js";
|
|
1
|
+
import { createAdminInboundRequest, createOpenClawCrablineProviderBridge, DEFAULT_ACCOUNT_ID, isRecord, qaTargetForInbound, readNonBlankString, readString, } from "../shared.js";
|
|
2
2
|
function nativeId(value) {
|
|
3
3
|
const id = value.trim();
|
|
4
4
|
if (!id) {
|
|
@@ -10,14 +10,23 @@ export const ZALO_OPENCLAW_CRABLINE_PROVIDER_BRIDGE = createOpenClawCrablineProv
|
|
|
10
10
|
provider: "zalo",
|
|
11
11
|
createAdapter(zalo) {
|
|
12
12
|
return {
|
|
13
|
-
async probe() {
|
|
13
|
+
async probe(signal) {
|
|
14
14
|
const response = await fetch(`${zalo.endpoints.apiRoot}/bot${zalo.botToken}/getMe`, {
|
|
15
15
|
method: "POST",
|
|
16
|
+
...(signal ? { signal } : {}),
|
|
16
17
|
});
|
|
17
18
|
if (!response.ok) {
|
|
18
19
|
throw new Error(`Crabline Zalo getMe probe failed with HTTP ${response.status}.`);
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
+
const result = await response.json();
|
|
22
|
+
const bot = isRecord(result) && isRecord(result.result) ? result.result : undefined;
|
|
23
|
+
if (!isRecord(result) || result.ok !== true || readNonBlankString(bot?.id) !== zalo.botId) {
|
|
24
|
+
const detail = (isRecord(result) ? readNonBlankString(result.description) : undefined) ??
|
|
25
|
+
(isRecord(result) ? readNonBlankString(result.error) : undefined) ??
|
|
26
|
+
"invalid response";
|
|
27
|
+
throw new Error(`Crabline Zalo getMe probe failed: ${detail}.`);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
21
30
|
},
|
|
22
31
|
createBinding() {
|
|
23
32
|
return {
|
|
@@ -83,8 +92,8 @@ export const ZALO_OPENCLAW_CRABLINE_PROVIDER_BRIDGE = createOpenClawCrablineProv
|
|
|
83
92
|
}
|
|
84
93
|
const chatId = readString(event.body.chat_id);
|
|
85
94
|
const text = event.path === "/bot<redacted>/sendMessage"
|
|
86
|
-
?
|
|
87
|
-
:
|
|
95
|
+
? readNonBlankString(event.body.text)
|
|
96
|
+
: readNonBlankString(event.body.caption);
|
|
88
97
|
if (!chatId || !text) {
|
|
89
98
|
return null;
|
|
90
99
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zalo.js","sourceRoot":"","sources":["../../../../src/openclaw/bridges/zalo.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,oCAAoC,EACpC,kBAAkB,EAClB,QAAQ,EACR,kBAAkB,EAClB,UAAU,GACX,MAAM,cAAc,CAAC;AAEtB,SAAS,QAAQ,CAAC,KAAa;IAC7B,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,CAAC,MAAM,sCAAsC,GAAG,oCAAoC,CAAC;IACzF,QAAQ,EAAE,MAAM;IAChB,aAAa,CAAC,IAAI;QAChB,OAAO;YACL,KAAK,CAAC,KAAK;
|
|
1
|
+
{"version":3,"file":"zalo.js","sourceRoot":"","sources":["../../../../src/openclaw/bridges/zalo.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createAdminInboundRequest,\n createOpenClawCrablineProviderBridge,\n DEFAULT_ACCOUNT_ID,\n isRecord,\n qaTargetForInbound,\n readNonBlankString,\n readString,\n} from \"../shared.js\";\n\nfunction nativeId(value: string): string {\n const id = value.trim();\n if (!id) {\n throw new Error(\"Zalo target is required.\");\n }\n return id;\n}\n\nexport const ZALO_OPENCLAW_CRABLINE_PROVIDER_BRIDGE = createOpenClawCrablineProviderBridge({\n provider: \"zalo\",\n createAdapter(zalo) {\n return {\n async probe(signal) {\n const response = await fetch(`${zalo.endpoints.apiRoot}/bot${zalo.botToken}/getMe`, {\n method: \"POST\",\n ...(signal ? { signal } : {}),\n });\n if (!response.ok) {\n throw new Error(`Crabline Zalo getMe probe failed with HTTP ${response.status}.`);\n }\n const result = await response.json();\n const bot = isRecord(result) && isRecord(result.result) ? result.result : undefined;\n if (!isRecord(result) || result.ok !== true || readNonBlankString(bot?.id) !== zalo.botId) {\n const detail =\n (isRecord(result) ? readNonBlankString(result.description) : undefined) ??\n (isRecord(result) ? readNonBlankString(result.error) : undefined) ??\n \"invalid response\";\n throw new Error(`Crabline Zalo getMe probe failed: ${detail}.`);\n }\n return result;\n },\n createBinding() {\n return {\n accountId: DEFAULT_ACCOUNT_ID,\n channel: \"zalo\",\n createChannelDriverSmokeEnv: (env) => ({ ...env, ...zalo.env }),\n createGatewayConfig: (openclawConfig = {}) => {\n const channels = isRecord(openclawConfig.channels) ? openclawConfig.channels : {};\n const zaloConfig = isRecord(channels.zalo) ? channels.zalo : {};\n return {\n ...openclawConfig,\n channels: {\n ...channels,\n zalo: {\n ...zaloConfig,\n allowFrom: [\"*\"],\n botToken: zalo.botToken,\n dmPolicy: \"open\",\n enabled: true,\n groupAllowFrom: [\"*\"],\n groupPolicy: \"open\",\n },\n },\n };\n },\n requiredPluginIds: [\"zalo\"],\n };\n },\n createAgentDelivery(parsed) {\n if (parsed.threadId) {\n throw new Error(\"Zalo does not support thread targets.\");\n }\n const to = nativeId(parsed.id);\n return { channel: \"zalo\", replyChannel: \"zalo\", replyTo: to, to };\n },\n createInbound(input) {\n if (input.threadId) {\n throw new Error(\"Zalo does not support thread targets.\");\n }\n const kind = input.conversation.kind === \"direct\" ? \"direct\" : \"group\";\n const chatId = nativeId(input.conversation.id);\n return {\n ...createAdminInboundRequest(zalo),\n providerBody: {\n chatId,\n chatType: kind === \"direct\" ? \"PRIVATE\" : \"GROUP\",\n senderId: nativeId(input.senderId),\n ...(input.senderName ? { senderName: input.senderName } : {}),\n text: input.text,\n },\n providerTargetKey: chatId,\n qaTarget: qaTargetForInbound(input),\n stateConversation: { id: chatId, kind },\n };\n },\n createOutboundFromRecorderEvent({ event, targetByProviderTarget }) {\n if (\n !isRecord(event) ||\n event.type !== \"api\" ||\n (event.method !== \"GET\" && event.method !== \"POST\") ||\n !isRecord(event.body) ||\n (event.path !== \"/bot<redacted>/sendMessage\" && event.path !== \"/bot<redacted>/sendPhoto\")\n ) {\n return null;\n }\n const chatId = readString(event.body.chat_id);\n const text =\n event.path === \"/bot<redacted>/sendMessage\"\n ? readNonBlankString(event.body.text)\n : readNonBlankString(event.body.caption);\n if (!chatId || !text) {\n return null;\n }\n return {\n accountId: DEFAULT_ACCOUNT_ID,\n senderId: zalo.botId,\n senderName: \"OpenClaw QA\",\n text,\n to: targetByProviderTarget.get(chatId) ?? chatId,\n };\n },\n };\n },\n});\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { canonicalizeWhatsAppUserCorrelationJid } from "../servers/whatsapp-jid.js";
|
|
2
|
+
import { isRecord, readNonBlankString } from "./shared.js";
|
|
3
|
+
const MATRIX_SEND_PATH_RE = /^\/_matrix\/client\/(?:v3|r0)\/rooms\/[^/]+\/send\/m\.room\.message\/[^/]+$/u;
|
|
4
|
+
const TELEGRAM_SEND_PATH_RE = /^\/bot<redacted>\/(?:sendAnimation|sendAudio|sendDocument|sendMessage|sendPhoto|sendVideo)$/iu;
|
|
5
|
+
const ZALO_SEND_PATH_RE = /^\/bot<redacted>\/(?:sendMessage|sendPhoto)$/u;
|
|
6
|
+
export function isAcceptedOpenClawCrablineOutbound(params) {
|
|
7
|
+
if (!isRecord(params.event) || params.event.type !== "api" || params.event.accepted !== true) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
const method = readNonBlankString(params.event.method);
|
|
11
|
+
const requestPath = readNonBlankString(params.event.path);
|
|
12
|
+
if (!method || !requestPath) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
switch (params.manifest.provider) {
|
|
16
|
+
case "mattermost":
|
|
17
|
+
return method === "POST" && requestPath === "/api/v4/posts";
|
|
18
|
+
case "matrix":
|
|
19
|
+
return method === "PUT" && MATRIX_SEND_PATH_RE.test(requestPath);
|
|
20
|
+
case "signal":
|
|
21
|
+
return method === "POST" && requestPath === "/api/v1/rpc";
|
|
22
|
+
case "slack":
|
|
23
|
+
return method === "POST" && requestPath === "/api/chat.postMessage";
|
|
24
|
+
case "telegram":
|
|
25
|
+
return (method === "GET" || method === "POST") && TELEGRAM_SEND_PATH_RE.test(requestPath);
|
|
26
|
+
case "whatsapp": {
|
|
27
|
+
const messagesPath = new URL(params.manifest.endpoints.messagesUrl).pathname;
|
|
28
|
+
const body = isRecord(params.event.body) ? params.event.body : undefined;
|
|
29
|
+
const key = isRecord(body?.key) ? body.key : undefined;
|
|
30
|
+
const directWebSocketTarget = canonicalizeWhatsAppUserCorrelationJid(readNonBlankString(key?.remoteJid) ?? "");
|
|
31
|
+
return ((method === "POST" && requestPath === messagesPath) ||
|
|
32
|
+
(method === "WEBSOCKET" &&
|
|
33
|
+
requestPath === "/ws/chat" &&
|
|
34
|
+
directWebSocketTarget !== undefined));
|
|
35
|
+
}
|
|
36
|
+
case "zalo":
|
|
37
|
+
return (method === "GET" || method === "POST") && ZALO_SEND_PATH_RE.test(requestPath);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=outbound-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbound-contract.js","sourceRoot":"","sources":["../../../src/openclaw/outbound-contract.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sCAAsC,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,mBAAmB,GACvB,8EAA8E,CAAC;AACjF,MAAM,qBAAqB,GACzB,+FAA+F,CAAC;AAClG,MAAM,iBAAiB,GAAG,+CAA+C,CAAC;AAE1E,MAAM,UAAU,kCAAkC,CAAC,MAGlD;IACC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC7F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,QAAQ,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACjC,KAAK,YAAY;YACf,OAAO,MAAM,KAAK,MAAM,IAAI,WAAW,KAAK,eAAe,CAAC;QAC9D,KAAK,QAAQ;YACX,OAAO,MAAM,KAAK,KAAK,IAAI,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACnE,KAAK,QAAQ;YACX,OAAO,MAAM,KAAK,MAAM,IAAI,WAAW,KAAK,aAAa,CAAC;QAC5D,KAAK,OAAO;YACV,OAAO,MAAM,KAAK,MAAM,IAAI,WAAW,KAAK,uBAAuB,CAAC;QACtE,KAAK,UAAU;YACb,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,CAAC,IAAI,qBAAqB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5F,KAAK,UAAU,EAAE,CAAC;YAChB,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC;YAC7E,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YACzE,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;YACvD,MAAM,qBAAqB,GAAG,sCAAsC,CAClE,kBAAkB,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,EAAE,CACzC,CAAC;YACF,OAAO,CACL,CAAC,MAAM,KAAK,MAAM,IAAI,WAAW,KAAK,YAAY,CAAC;gBACnD,CAAC,MAAM,KAAK,WAAW;oBACrB,WAAW,KAAK,UAAU;oBAC1B,qBAAqB,KAAK,SAAS,CAAC,CACvC,CAAC;QACJ,CAAC;QACD,KAAK,MAAM;YACT,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC1F,CAAC;AACH,CAAC","sourcesContent":["import type { CrablineServerManifest } from \"../servers/index.js\";\nimport { canonicalizeWhatsAppUserCorrelationJid } from \"../servers/whatsapp-jid.js\";\nimport { isRecord, readNonBlankString } from \"./shared.js\";\n\nconst MATRIX_SEND_PATH_RE =\n /^\\/_matrix\\/client\\/(?:v3|r0)\\/rooms\\/[^/]+\\/send\\/m\\.room\\.message\\/[^/]+$/u;\nconst TELEGRAM_SEND_PATH_RE =\n /^\\/bot<redacted>\\/(?:sendAnimation|sendAudio|sendDocument|sendMessage|sendPhoto|sendVideo)$/iu;\nconst ZALO_SEND_PATH_RE = /^\\/bot<redacted>\\/(?:sendMessage|sendPhoto)$/u;\n\nexport function isAcceptedOpenClawCrablineOutbound(params: {\n event: unknown;\n manifest: CrablineServerManifest;\n}): boolean {\n if (!isRecord(params.event) || params.event.type !== \"api\" || params.event.accepted !== true) {\n return false;\n }\n const method = readNonBlankString(params.event.method);\n const requestPath = readNonBlankString(params.event.path);\n if (!method || !requestPath) {\n return false;\n }\n\n switch (params.manifest.provider) {\n case \"mattermost\":\n return method === \"POST\" && requestPath === \"/api/v4/posts\";\n case \"matrix\":\n return method === \"PUT\" && MATRIX_SEND_PATH_RE.test(requestPath);\n case \"signal\":\n return method === \"POST\" && requestPath === \"/api/v1/rpc\";\n case \"slack\":\n return method === \"POST\" && requestPath === \"/api/chat.postMessage\";\n case \"telegram\":\n return (method === \"GET\" || method === \"POST\") && TELEGRAM_SEND_PATH_RE.test(requestPath);\n case \"whatsapp\": {\n const messagesPath = new URL(params.manifest.endpoints.messagesUrl).pathname;\n const body = isRecord(params.event.body) ? params.event.body : undefined;\n const key = isRecord(body?.key) ? body.key : undefined;\n const directWebSocketTarget = canonicalizeWhatsAppUserCorrelationJid(\n readNonBlankString(key?.remoteJid) ?? \"\",\n );\n return (\n (method === \"POST\" && requestPath === messagesPath) ||\n (method === \"WEBSOCKET\" &&\n requestPath === \"/ws/chat\" &&\n directWebSocketTarget !== undefined)\n );\n }\n case \"zalo\":\n return (method === \"GET\" || method === \"POST\") && ZALO_SEND_PATH_RE.test(requestPath);\n }\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type WindowsAclRunner = (command: string, args: string[], options: {
|
|
2
|
+
env: NodeJS.ProcessEnv;
|
|
3
|
+
windowsHide: boolean;
|
|
4
|
+
}) => Promise<void>;
|
|
5
|
+
export declare function resolveWindowsPowerShellPath(systemRoot: string | null | undefined): string;
|
|
6
|
+
export declare function applyOwnerOnlyWindowsAcl(filePath: string, run?: WindowsAclRunner, systemRoot?: string | null | undefined): Promise<void>;
|
|
7
|
+
export declare function applyOwnerOnlyWindowsDirectoryAcl(directoryPath: string, run?: WindowsAclRunner, systemRoot?: string | null | undefined): Promise<void>;
|
|
8
|
+
export type SecuredPrivateDirectory = {
|
|
9
|
+
assertIdentityAt(directoryPath?: string): Promise<void>;
|
|
10
|
+
directoryPath: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function captureDirectoryIdentity(directoryPath: string): Promise<SecuredPrivateDirectory>;
|
|
13
|
+
export declare function syncParentDirectory(filePath: string, platform?: NodeJS.Platform): Promise<void>;
|
|
14
|
+
export declare function removeSecuredPrivateDirectory(secured: SecuredPrivateDirectory, currentPath?: string): Promise<void>;
|
|
15
|
+
export declare function securePrivateDirectory(directoryPath: string, options?: {
|
|
16
|
+
platform?: NodeJS.Platform;
|
|
17
|
+
secureWindowsDirectory?: (directoryPath: string) => Promise<void>;
|
|
18
|
+
}): Promise<SecuredPrivateDirectory>;
|
|
19
|
+
export declare function publishPrivateFileAtomically(filePath: string, contents: string, options?: {
|
|
20
|
+
afterRename?: (filePath: string) => Promise<void>;
|
|
21
|
+
beforeRename?: (temporaryPath: string) => Promise<void>;
|
|
22
|
+
platform?: NodeJS.Platform;
|
|
23
|
+
removeTemporaryFile?: (temporaryPath: string) => Promise<void>;
|
|
24
|
+
secureWindowsFile?: (temporaryPath: string) => Promise<void>;
|
|
25
|
+
syncParent?: (filePath: string, platform?: NodeJS.Platform) => Promise<void>;
|
|
26
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import fs, {} from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const WINDOWS_OWNER_ONLY_ACL_SCRIPT = String.raw `
|
|
8
|
+
$ErrorActionPreference = "Stop"
|
|
9
|
+
$filePath = $env:CRABLINE_PRIVATE_FILE_PATH
|
|
10
|
+
if ([string]::IsNullOrWhiteSpace($filePath)) {
|
|
11
|
+
throw "CRABLINE_PRIVATE_FILE_PATH is required."
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
|
|
15
|
+
$sid = $identity.User
|
|
16
|
+
if ($null -eq $sid) {
|
|
17
|
+
throw "Could not resolve the current Windows user SID."
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
$acl = Get-Acl -LiteralPath $filePath
|
|
21
|
+
$acl.SetOwner($sid)
|
|
22
|
+
$acl.SetAccessRuleProtection($true, $false)
|
|
23
|
+
$existingRules = @($acl.GetAccessRules(
|
|
24
|
+
$true,
|
|
25
|
+
$false,
|
|
26
|
+
[System.Security.Principal.SecurityIdentifier]
|
|
27
|
+
))
|
|
28
|
+
foreach ($existingRule in $existingRules) {
|
|
29
|
+
[void]$acl.RemoveAccessRuleSpecific($existingRule)
|
|
30
|
+
}
|
|
31
|
+
$rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
|
|
32
|
+
$sid,
|
|
33
|
+
[System.Security.AccessControl.FileSystemRights]::FullControl,
|
|
34
|
+
[System.Security.AccessControl.AccessControlType]::Allow
|
|
35
|
+
)
|
|
36
|
+
$acl.SetAccessRule($rule)
|
|
37
|
+
Set-Acl -LiteralPath $filePath -AclObject $acl
|
|
38
|
+
|
|
39
|
+
$actual = Get-Acl -LiteralPath $filePath
|
|
40
|
+
$ownerSid = $actual.GetOwner([System.Security.Principal.SecurityIdentifier])
|
|
41
|
+
$rules = @($actual.GetAccessRules(
|
|
42
|
+
$true,
|
|
43
|
+
$true,
|
|
44
|
+
[System.Security.Principal.SecurityIdentifier]
|
|
45
|
+
))
|
|
46
|
+
if (-not $actual.AreAccessRulesProtected) {
|
|
47
|
+
throw "Private file DACL still inherits permissions."
|
|
48
|
+
}
|
|
49
|
+
if ($ownerSid.Value -ne $sid.Value) {
|
|
50
|
+
throw "Private file owner SID does not match the current user."
|
|
51
|
+
}
|
|
52
|
+
if ($rules.Count -ne 1) {
|
|
53
|
+
throw "Private file DACL must contain exactly one access rule."
|
|
54
|
+
}
|
|
55
|
+
$actualRule = $rules[0]
|
|
56
|
+
if (
|
|
57
|
+
$actualRule.IsInherited -or
|
|
58
|
+
$actualRule.IdentityReference.Value -ne $sid.Value -or
|
|
59
|
+
$actualRule.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow -or
|
|
60
|
+
(($actualRule.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -ne [System.Security.AccessControl.FileSystemRights]::FullControl)
|
|
61
|
+
) {
|
|
62
|
+
throw "Private file DACL is not owner-only full control."
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
const WINDOWS_OWNER_ONLY_DIRECTORY_ACL_SCRIPT = String.raw `
|
|
66
|
+
$ErrorActionPreference = "Stop"
|
|
67
|
+
$directoryPath = $env:CRABLINE_PRIVATE_DIRECTORY_PATH
|
|
68
|
+
if ([string]::IsNullOrWhiteSpace($directoryPath)) {
|
|
69
|
+
throw "CRABLINE_PRIVATE_DIRECTORY_PATH is required."
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
|
|
73
|
+
$sid = $identity.User
|
|
74
|
+
if ($null -eq $sid) {
|
|
75
|
+
throw "Could not resolve the current Windows user SID."
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
$acl = Get-Acl -LiteralPath $directoryPath
|
|
79
|
+
$acl.SetOwner($sid)
|
|
80
|
+
$acl.SetAccessRuleProtection($true, $false)
|
|
81
|
+
$existingRules = @($acl.GetAccessRules(
|
|
82
|
+
$true,
|
|
83
|
+
$false,
|
|
84
|
+
[System.Security.Principal.SecurityIdentifier]
|
|
85
|
+
))
|
|
86
|
+
foreach ($existingRule in $existingRules) {
|
|
87
|
+
[void]$acl.RemoveAccessRuleSpecific($existingRule)
|
|
88
|
+
}
|
|
89
|
+
$inheritance = (
|
|
90
|
+
[System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor
|
|
91
|
+
[System.Security.AccessControl.InheritanceFlags]::ObjectInherit
|
|
92
|
+
)
|
|
93
|
+
$rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
|
|
94
|
+
$sid,
|
|
95
|
+
[System.Security.AccessControl.FileSystemRights]::FullControl,
|
|
96
|
+
$inheritance,
|
|
97
|
+
[System.Security.AccessControl.PropagationFlags]::None,
|
|
98
|
+
[System.Security.AccessControl.AccessControlType]::Allow
|
|
99
|
+
)
|
|
100
|
+
$acl.SetAccessRule($rule)
|
|
101
|
+
Set-Acl -LiteralPath $directoryPath -AclObject $acl
|
|
102
|
+
|
|
103
|
+
$actual = Get-Acl -LiteralPath $directoryPath
|
|
104
|
+
$ownerSid = $actual.GetOwner([System.Security.Principal.SecurityIdentifier])
|
|
105
|
+
$rules = @($actual.GetAccessRules(
|
|
106
|
+
$true,
|
|
107
|
+
$true,
|
|
108
|
+
[System.Security.Principal.SecurityIdentifier]
|
|
109
|
+
))
|
|
110
|
+
if (-not $actual.AreAccessRulesProtected) {
|
|
111
|
+
throw "Private directory DACL still inherits permissions."
|
|
112
|
+
}
|
|
113
|
+
if ($ownerSid.Value -ne $sid.Value) {
|
|
114
|
+
throw "Private directory owner SID does not match the current user."
|
|
115
|
+
}
|
|
116
|
+
if ($rules.Count -ne 1) {
|
|
117
|
+
throw "Private directory DACL must contain exactly one access rule."
|
|
118
|
+
}
|
|
119
|
+
$actualRule = $rules[0]
|
|
120
|
+
$requiredInheritance = (
|
|
121
|
+
[System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor
|
|
122
|
+
[System.Security.AccessControl.InheritanceFlags]::ObjectInherit
|
|
123
|
+
)
|
|
124
|
+
if (
|
|
125
|
+
$actualRule.IsInherited -or
|
|
126
|
+
$actualRule.IdentityReference.Value -ne $sid.Value -or
|
|
127
|
+
$actualRule.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow -or
|
|
128
|
+
(($actualRule.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -ne [System.Security.AccessControl.FileSystemRights]::FullControl) -or
|
|
129
|
+
(($actualRule.InheritanceFlags -band $requiredInheritance) -ne $requiredInheritance)
|
|
130
|
+
) {
|
|
131
|
+
throw "Private directory DACL is not owner-only inheritable full control."
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
const runWindowsAclCommand = async (command, args, options) => {
|
|
135
|
+
await execFileAsync(command, args, options);
|
|
136
|
+
};
|
|
137
|
+
export function resolveWindowsPowerShellPath(systemRoot) {
|
|
138
|
+
const normalizedRoot = systemRoot?.trim() ? path.win32.normalize(systemRoot.trim()) : undefined;
|
|
139
|
+
if (!normalizedRoot || !/^[A-Za-z]:\\/.test(normalizedRoot)) {
|
|
140
|
+
throw new Error("SystemRoot must be an absolute local Windows path.");
|
|
141
|
+
}
|
|
142
|
+
return path.win32.join(normalizedRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
143
|
+
}
|
|
144
|
+
export async function applyOwnerOnlyWindowsAcl(filePath, run = runWindowsAclCommand, systemRoot = process.env.SystemRoot) {
|
|
145
|
+
try {
|
|
146
|
+
const powershellPath = resolveWindowsPowerShellPath(systemRoot);
|
|
147
|
+
await run(powershellPath, ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", WINDOWS_OWNER_ONLY_ACL_SCRIPT], {
|
|
148
|
+
env: {
|
|
149
|
+
...process.env,
|
|
150
|
+
CRABLINE_PRIVATE_FILE_PATH: path.resolve(filePath),
|
|
151
|
+
},
|
|
152
|
+
windowsHide: true,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
throw new Error("Could not apply and verify an owner-only Windows ACL; powershell.exe with Set-Acl is required.", { cause: error });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export async function applyOwnerOnlyWindowsDirectoryAcl(directoryPath, run = runWindowsAclCommand, systemRoot = process.env.SystemRoot) {
|
|
160
|
+
try {
|
|
161
|
+
const powershellPath = resolveWindowsPowerShellPath(systemRoot);
|
|
162
|
+
await run(powershellPath, [
|
|
163
|
+
"-NoLogo",
|
|
164
|
+
"-NoProfile",
|
|
165
|
+
"-NonInteractive",
|
|
166
|
+
"-Command",
|
|
167
|
+
WINDOWS_OWNER_ONLY_DIRECTORY_ACL_SCRIPT,
|
|
168
|
+
], {
|
|
169
|
+
env: {
|
|
170
|
+
...process.env,
|
|
171
|
+
CRABLINE_PRIVATE_DIRECTORY_PATH: path.resolve(directoryPath),
|
|
172
|
+
},
|
|
173
|
+
windowsHide: true,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
throw new Error("Could not apply and verify an owner-only Windows directory ACL; powershell.exe with Set-Acl is required.", { cause: error });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function readHandleIdentity(handle) {
|
|
181
|
+
const stats = await handle.stat({ bigint: true });
|
|
182
|
+
if (stats.ino <= 0n) {
|
|
183
|
+
throw new Error("The filesystem did not provide a stable private file identity.");
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
device: stats.dev,
|
|
187
|
+
inode: stats.ino,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
async function assertPathIdentity(filePath, expected) {
|
|
191
|
+
try {
|
|
192
|
+
const stats = await fs.lstat(filePath, { bigint: true });
|
|
193
|
+
if (stats.isFile() &&
|
|
194
|
+
stats.nlink === 1n &&
|
|
195
|
+
stats.dev === expected.device &&
|
|
196
|
+
stats.ino === expected.inode) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
throw new Error("Private file path identity changed during publication.", { cause: error });
|
|
202
|
+
}
|
|
203
|
+
throw new Error("Private file path identity changed during publication.");
|
|
204
|
+
}
|
|
205
|
+
async function readDirectoryHandleIdentity(handle) {
|
|
206
|
+
const stats = await handle.stat({ bigint: true });
|
|
207
|
+
if (!stats.isDirectory() || stats.ino <= 0n) {
|
|
208
|
+
throw new Error("The filesystem did not provide a stable private directory identity.");
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
device: stats.dev,
|
|
212
|
+
inode: stats.ino,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
async function assertDirectoryPathIdentity(directoryPath, expected) {
|
|
216
|
+
try {
|
|
217
|
+
const stats = await fs.lstat(directoryPath, { bigint: true });
|
|
218
|
+
if (stats.isDirectory() && stats.dev === expected.device && stats.ino === expected.inode) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
throw new Error("Private directory path identity changed during publication.", {
|
|
224
|
+
cause: error,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
throw new Error("Private directory path identity changed during publication.");
|
|
228
|
+
}
|
|
229
|
+
export async function captureDirectoryIdentity(directoryPath) {
|
|
230
|
+
const handle = await fs.open(directoryPath, "r");
|
|
231
|
+
try {
|
|
232
|
+
const identity = await readDirectoryHandleIdentity(handle);
|
|
233
|
+
const secured = {
|
|
234
|
+
async assertIdentityAt(currentPath = directoryPath) {
|
|
235
|
+
await assertDirectoryPathIdentity(currentPath, identity);
|
|
236
|
+
},
|
|
237
|
+
directoryPath,
|
|
238
|
+
};
|
|
239
|
+
await secured.assertIdentityAt();
|
|
240
|
+
return secured;
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
await handle.close();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
export async function syncParentDirectory(filePath, platform = process.platform) {
|
|
247
|
+
if (platform === "win32") {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const handle = await fs.open(path.dirname(filePath), "r");
|
|
251
|
+
try {
|
|
252
|
+
await handle.sync();
|
|
253
|
+
}
|
|
254
|
+
finally {
|
|
255
|
+
await handle.close();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
export async function removeSecuredPrivateDirectory(secured, currentPath = secured.directoryPath) {
|
|
259
|
+
const quarantinePath = path.join(path.dirname(currentPath), `.${path.basename(currentPath)}.${process.pid}.${randomUUID()}.remove`);
|
|
260
|
+
await secured.assertIdentityAt(currentPath);
|
|
261
|
+
await fs.rename(currentPath, quarantinePath);
|
|
262
|
+
await secured.assertIdentityAt(quarantinePath);
|
|
263
|
+
await syncParentDirectory(quarantinePath);
|
|
264
|
+
await fs.rm(quarantinePath, { force: true, recursive: true });
|
|
265
|
+
await syncParentDirectory(quarantinePath);
|
|
266
|
+
}
|
|
267
|
+
export async function securePrivateDirectory(directoryPath, options = {}) {
|
|
268
|
+
let created = false;
|
|
269
|
+
try {
|
|
270
|
+
await fs.mkdir(directoryPath, { mode: 0o700 });
|
|
271
|
+
created = true;
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
if (error.code !== "EEXIST") {
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const handle = await fs.open(directoryPath, "r");
|
|
279
|
+
let handleOpen = true;
|
|
280
|
+
const closeHandle = async () => {
|
|
281
|
+
if (!handleOpen) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
handleOpen = false;
|
|
285
|
+
await handle.close();
|
|
286
|
+
};
|
|
287
|
+
let identity;
|
|
288
|
+
try {
|
|
289
|
+
identity = await readDirectoryHandleIdentity(handle);
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
try {
|
|
293
|
+
await closeHandle();
|
|
294
|
+
}
|
|
295
|
+
catch (closeError) {
|
|
296
|
+
const aggregateError = new AggregateError([error, closeError], "Private directory identity verification failed and its handle could not be closed.");
|
|
297
|
+
aggregateError.cause = error;
|
|
298
|
+
throw aggregateError;
|
|
299
|
+
}
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
const secured = {
|
|
303
|
+
async assertIdentityAt(currentPath = directoryPath) {
|
|
304
|
+
await assertDirectoryPathIdentity(currentPath, identity);
|
|
305
|
+
},
|
|
306
|
+
directoryPath,
|
|
307
|
+
};
|
|
308
|
+
try {
|
|
309
|
+
await secured.assertIdentityAt();
|
|
310
|
+
if ((options.platform ?? process.platform) === "win32") {
|
|
311
|
+
await (options.secureWindowsDirectory ?? applyOwnerOnlyWindowsDirectoryAcl)(directoryPath);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
await handle.chmod(0o700);
|
|
315
|
+
}
|
|
316
|
+
await secured.assertIdentityAt();
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
let primaryError = error;
|
|
320
|
+
try {
|
|
321
|
+
await closeHandle();
|
|
322
|
+
}
|
|
323
|
+
catch (closeError) {
|
|
324
|
+
const aggregateError = new AggregateError([error, closeError], "Private directory securing failed and its verification handle could not be closed.");
|
|
325
|
+
aggregateError.cause = error;
|
|
326
|
+
primaryError = aggregateError;
|
|
327
|
+
}
|
|
328
|
+
if (created) {
|
|
329
|
+
try {
|
|
330
|
+
await removeSecuredPrivateDirectory(secured);
|
|
331
|
+
}
|
|
332
|
+
catch (cleanupError) {
|
|
333
|
+
const aggregateError = new AggregateError([primaryError, cleanupError], `Private directory securing failed and rollback cleanup also failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
334
|
+
aggregateError.cause = primaryError;
|
|
335
|
+
throw aggregateError;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
throw primaryError;
|
|
339
|
+
}
|
|
340
|
+
finally {
|
|
341
|
+
await closeHandle();
|
|
342
|
+
}
|
|
343
|
+
return secured;
|
|
344
|
+
}
|
|
345
|
+
export async function publishPrivateFileAtomically(filePath, contents, options = {}) {
|
|
346
|
+
const temporaryPath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`);
|
|
347
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
348
|
+
let handle;
|
|
349
|
+
let publicationFailed = false;
|
|
350
|
+
let primaryError;
|
|
351
|
+
try {
|
|
352
|
+
handle = await fs.open(temporaryPath, "wx+", 0o600);
|
|
353
|
+
const identity = await readHandleIdentity(handle);
|
|
354
|
+
if ((options.platform ?? process.platform) === "win32") {
|
|
355
|
+
await (options.secureWindowsFile ?? applyOwnerOnlyWindowsAcl)(temporaryPath);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
await handle.chmod(0o600);
|
|
359
|
+
}
|
|
360
|
+
await assertPathIdentity(temporaryPath, identity);
|
|
361
|
+
await handle.writeFile(contents, "utf8");
|
|
362
|
+
await handle.sync();
|
|
363
|
+
await assertPathIdentity(temporaryPath, identity);
|
|
364
|
+
await options.beforeRename?.(temporaryPath);
|
|
365
|
+
await assertPathIdentity(temporaryPath, identity);
|
|
366
|
+
await fs.rename(temporaryPath, filePath);
|
|
367
|
+
await (options.syncParent ?? syncParentDirectory)(filePath, options.platform);
|
|
368
|
+
await options.afterRename?.(filePath);
|
|
369
|
+
await assertPathIdentity(filePath, identity);
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
publicationFailed = true;
|
|
373
|
+
primaryError = error;
|
|
374
|
+
}
|
|
375
|
+
const cleanupErrors = [];
|
|
376
|
+
try {
|
|
377
|
+
await handle?.close();
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
cleanupErrors.push(error);
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
await (options.removeTemporaryFile ??
|
|
384
|
+
((candidatePath) => fs.rm(candidatePath, { force: true })))(temporaryPath);
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
cleanupErrors.push(error);
|
|
388
|
+
}
|
|
389
|
+
if (publicationFailed) {
|
|
390
|
+
if (cleanupErrors.length > 0) {
|
|
391
|
+
const primaryMessage = primaryError instanceof Error ? primaryError.message : String(primaryError);
|
|
392
|
+
const aggregateError = new AggregateError([primaryError, ...cleanupErrors], `${primaryMessage} Private temporary file cleanup also failed.`);
|
|
393
|
+
aggregateError.cause = primaryError;
|
|
394
|
+
const primaryCode = typeof primaryError === "object" && primaryError !== null
|
|
395
|
+
? primaryError.code
|
|
396
|
+
: undefined;
|
|
397
|
+
if (primaryCode) {
|
|
398
|
+
Object.assign(aggregateError, { code: primaryCode });
|
|
399
|
+
}
|
|
400
|
+
throw aggregateError;
|
|
401
|
+
}
|
|
402
|
+
throw primaryError;
|
|
403
|
+
}
|
|
404
|
+
if (cleanupErrors.length === 1) {
|
|
405
|
+
throw cleanupErrors[0];
|
|
406
|
+
}
|
|
407
|
+
if (cleanupErrors.length > 1) {
|
|
408
|
+
throw new AggregateError(cleanupErrors, "Private temporary file publication cleanup failed.");
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
//# sourceMappingURL=private-file.js.map
|