@openclaw/crabline 0.1.11 → 0.1.13
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 +137 -23
- package/dist/src/bin/crabline.js +3 -1
- package/dist/src/bin/crabline.js.map +1 -1
- package/dist/src/cli/program.d.ts +5 -1
- package/dist/src/cli/program.js +281 -52
- package/dist/src/cli/program.js.map +1 -1
- package/dist/src/config/load.js +39 -3
- package/dist/src/config/load.js.map +1 -1
- package/dist/src/config/schema.d.ts +20 -0
- package/dist/src/config/schema.js +163 -35
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/core/errors.d.ts +1 -0
- package/dist/src/core/errors.js +6 -3
- package/dist/src/core/errors.js.map +1 -1
- package/dist/src/core/http-path.d.ts +1 -0
- package/dist/src/core/http-path.js +14 -0
- package/dist/src/core/http-path.js.map +1 -0
- package/dist/src/core/matcher.d.ts +3 -1
- package/dist/src/core/matcher.js +58 -2
- package/dist/src/core/matcher.js.map +1 -1
- package/dist/src/core/nonces.d.ts +2 -0
- package/dist/src/core/nonces.js +11 -1
- package/dist/src/core/nonces.js.map +1 -1
- package/dist/src/core/reporters.d.ts +5 -0
- package/dist/src/core/reporters.js +26 -2
- package/dist/src/core/reporters.js.map +1 -1
- package/dist/src/core/run.d.ts +3 -1
- package/dist/src/core/run.js +178 -66
- package/dist/src/core/run.js.map +1 -1
- package/dist/src/index.d.ts +12 -10
- package/dist/src/index.js +6 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/matrix-ids.d.ts +5 -0
- package/dist/src/matrix-ids.js +83 -0
- package/dist/src/matrix-ids.js.map +1 -0
- package/dist/src/openclaw/artifact-generation.d.ts +7 -6
- package/dist/src/openclaw/artifact-generation.js +385 -41
- package/dist/src/openclaw/artifact-generation.js.map +1 -1
- package/dist/src/openclaw/bridges/discord.d.ts +1 -0
- package/dist/src/openclaw/bridges/discord.js +149 -0
- package/dist/src/openclaw/bridges/discord.js.map +1 -0
- package/dist/src/openclaw/bridges/matrix.js +22 -5
- package/dist/src/openclaw/bridges/matrix.js.map +1 -1
- package/dist/src/openclaw/bridges/mattermost.js +14 -9
- package/dist/src/openclaw/bridges/mattermost.js.map +1 -1
- package/dist/src/openclaw/bridges/probe-response.d.ts +1 -0
- package/dist/src/openclaw/bridges/probe-response.js +5 -0
- package/dist/src/openclaw/bridges/probe-response.js.map +1 -0
- package/dist/src/openclaw/bridges/signal.js +16 -7
- package/dist/src/openclaw/bridges/signal.js.map +1 -1
- package/dist/src/openclaw/bridges/slack.js +117 -24
- package/dist/src/openclaw/bridges/slack.js.map +1 -1
- package/dist/src/openclaw/bridges/telegram.js +46 -20
- package/dist/src/openclaw/bridges/telegram.js.map +1 -1
- package/dist/src/openclaw/bridges/whatsapp.js +33 -17
- package/dist/src/openclaw/bridges/whatsapp.js.map +1 -1
- package/dist/src/openclaw/bridges/zalo.js +9 -4
- package/dist/src/openclaw/bridges/zalo.js.map +1 -1
- package/dist/src/openclaw/outbound-contract.js +3 -0
- package/dist/src/openclaw/outbound-contract.js.map +1 -1
- package/dist/src/openclaw/private-file.d.ts +38 -3
- package/dist/src/openclaw/private-file.js +2008 -87
- package/dist/src/openclaw/private-file.js.map +1 -1
- package/dist/src/openclaw/{smoke-lock.d.ts → provider-readiness-lock.d.ts} +14 -5
- package/dist/src/openclaw/provider-readiness-lock.js +1320 -0
- package/dist/src/openclaw/provider-readiness-lock.js.map +1 -0
- package/dist/src/openclaw/shared.d.ts +10 -17
- package/dist/src/openclaw/shared.js +53 -19
- package/dist/src/openclaw/shared.js.map +1 -1
- package/dist/src/openclaw.d.ts +3 -5
- package/dist/src/openclaw.js +245 -60
- package/dist/src/openclaw.js.map +1 -1
- package/dist/src/platform/process-owned-lock.d.ts +11 -0
- package/dist/src/platform/process-owned-lock.js +1698 -0
- package/dist/src/platform/process-owned-lock.js.map +1 -0
- package/dist/src/platform/recorder-directory.d.ts +1 -0
- package/dist/src/platform/recorder-directory.js +14 -0
- package/dist/src/platform/recorder-directory.js.map +1 -0
- package/dist/src/platform/windows-acl.d.ts +18 -0
- package/dist/src/platform/windows-acl.js +1367 -0
- package/dist/src/platform/windows-acl.js.map +1 -0
- package/dist/src/platform/windows-lock-root.d.ts +13 -0
- package/dist/src/platform/windows-lock-root.js +98 -0
- package/dist/src/platform/windows-lock-root.js.map +1 -0
- package/dist/src/providers/builtin/discord.d.ts +4 -0
- package/dist/src/providers/builtin/discord.js +52 -9
- package/dist/src/providers/builtin/discord.js.map +1 -1
- package/dist/src/providers/builtin/external-webhook-auth.d.ts +1 -0
- package/dist/src/providers/builtin/external-webhook-auth.js +38 -1
- package/dist/src/providers/builtin/external-webhook-auth.js.map +1 -1
- package/dist/src/providers/builtin/feishu.d.ts +1 -0
- package/dist/src/providers/builtin/feishu.js +96 -26
- package/dist/src/providers/builtin/feishu.js.map +1 -1
- package/dist/src/providers/builtin/googlechat.d.ts +1 -0
- package/dist/src/providers/builtin/googlechat.js +95 -26
- package/dist/src/providers/builtin/googlechat.js.map +1 -1
- package/dist/src/providers/builtin/imessage.js +60 -17
- package/dist/src/providers/builtin/imessage.js.map +1 -1
- package/dist/src/providers/builtin/loopback.js +88 -35
- package/dist/src/providers/builtin/loopback.js.map +1 -1
- package/dist/src/providers/builtin/matrix.d.ts +2 -2
- package/dist/src/providers/builtin/matrix.js +78 -21
- package/dist/src/providers/builtin/matrix.js.map +1 -1
- package/dist/src/providers/builtin/mattermost.d.ts +11 -7
- package/dist/src/providers/builtin/mattermost.js +138 -24
- package/dist/src/providers/builtin/mattermost.js.map +1 -1
- package/dist/src/providers/builtin/msteams.d.ts +1 -0
- package/dist/src/providers/builtin/msteams.js +118 -38
- package/dist/src/providers/builtin/msteams.js.map +1 -1
- package/dist/src/providers/builtin/native-local-mock.js +4 -4
- package/dist/src/providers/builtin/native-local-mock.js.map +1 -1
- package/dist/src/providers/builtin/script.js +1057 -146
- package/dist/src/providers/builtin/script.js.map +1 -1
- package/dist/src/providers/builtin/slack.d.ts +8 -1
- package/dist/src/providers/builtin/slack.js +327 -24
- package/dist/src/providers/builtin/slack.js.map +1 -1
- package/dist/src/providers/builtin/telegram.d.ts +1 -1
- package/dist/src/providers/builtin/telegram.js +55 -14
- package/dist/src/providers/builtin/telegram.js.map +1 -1
- package/dist/src/providers/builtin/whatsapp.d.ts +4 -3
- package/dist/src/providers/builtin/whatsapp.js +157 -29
- package/dist/src/providers/builtin/whatsapp.js.map +1 -1
- package/dist/src/providers/builtin/zalo.d.ts +5 -2
- package/dist/src/providers/builtin/zalo.js +31 -7
- package/dist/src/providers/builtin/zalo.js.map +1 -1
- package/dist/src/providers/catalog.d.ts +1 -1
- package/dist/src/providers/catalog.js +1 -1
- package/dist/src/providers/catalog.js.map +1 -1
- package/dist/src/providers/local-mock.d.ts +4 -0
- package/dist/src/providers/local-mock.js +162 -35
- package/dist/src/providers/local-mock.js.map +1 -1
- package/dist/src/providers/native-ids.d.ts +2 -0
- package/dist/src/providers/native-ids.js +3 -0
- package/dist/src/providers/native-ids.js.map +1 -1
- package/dist/src/providers/recorder.d.ts +14 -3
- package/dist/src/providers/recorder.js +521 -41
- package/dist/src/providers/recorder.js.map +1 -1
- package/dist/src/providers/registry.d.ts +1 -0
- package/dist/src/providers/registry.js +65 -20
- package/dist/src/providers/registry.js.map +1 -1
- package/dist/src/providers/signed-jwt.d.ts +6 -1
- package/dist/src/providers/signed-jwt.js +241 -50
- package/dist/src/providers/signed-jwt.js.map +1 -1
- package/dist/src/providers/slack-ids.d.ts +1 -0
- package/dist/src/providers/slack-ids.js +5 -0
- package/dist/src/providers/slack-ids.js.map +1 -1
- package/dist/src/providers/target-normalizers.d.ts +9 -1
- package/dist/src/providers/target-normalizers.js +177 -58
- package/dist/src/providers/target-normalizers.js.map +1 -1
- package/dist/src/providers/webhook-server.d.ts +3 -0
- package/dist/src/providers/webhook-server.js +268 -47
- package/dist/src/providers/webhook-server.js.map +1 -1
- package/dist/src/servers/discord.d.ts +40 -0
- package/dist/src/servers/discord.js +842 -0
- package/dist/src/servers/discord.js.map +1 -0
- package/dist/src/servers/http.d.ts +14 -2
- package/dist/src/servers/http.js +229 -58
- package/dist/src/servers/http.js.map +1 -1
- package/dist/src/servers/index.d.ts +10 -4
- package/dist/src/servers/index.js +4 -0
- package/dist/src/servers/index.js.map +1 -1
- package/dist/src/servers/matrix.d.ts +3 -0
- package/dist/src/servers/matrix.js +268 -110
- package/dist/src/servers/matrix.js.map +1 -1
- package/dist/src/servers/mattermost.d.ts +5 -0
- package/dist/src/servers/mattermost.js +389 -91
- package/dist/src/servers/mattermost.js.map +1 -1
- package/dist/src/servers/recorder.d.ts +11 -0
- package/dist/src/servers/recorder.js +1072 -69
- package/dist/src/servers/recorder.js.map +1 -1
- package/dist/src/servers/signal.d.ts +1 -0
- package/dist/src/servers/signal.js +279 -44
- package/dist/src/servers/signal.js.map +1 -1
- package/dist/src/servers/slack.d.ts +15 -0
- package/dist/src/servers/slack.js +270 -54
- package/dist/src/servers/slack.js.map +1 -1
- package/dist/src/servers/telegram-identity.d.ts +6 -0
- package/dist/src/servers/telegram-identity.js +27 -0
- package/dist/src/servers/telegram-identity.js.map +1 -0
- package/dist/src/servers/telegram.d.ts +17 -6
- package/dist/src/servers/telegram.js +1218 -107
- package/dist/src/servers/telegram.js.map +1 -1
- package/dist/src/servers/webhook-target.d.ts +12 -4
- package/dist/src/servers/webhook-target.js +226 -28
- package/dist/src/servers/webhook-target.js.map +1 -1
- package/dist/src/servers/whatsapp-baileys-websocket.d.ts +60 -1
- package/dist/src/servers/whatsapp-baileys-websocket.js +782 -112
- package/dist/src/servers/whatsapp-baileys-websocket.js.map +1 -1
- package/dist/src/servers/whatsapp-jid.d.ts +1 -0
- package/dist/src/servers/whatsapp-jid.js +9 -5
- package/dist/src/servers/whatsapp-jid.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/binary-node.d.ts +1 -0
- package/dist/src/servers/whatsapp-wire/binary-node.js +37 -13
- package/dist/src/servers/whatsapp-wire/binary-node.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/crypto.d.ts +3 -0
- package/dist/src/servers/whatsapp-wire/crypto.js +23 -8
- package/dist/src/servers/whatsapp-wire/crypto.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/handshake.d.ts +9 -3
- package/dist/src/servers/whatsapp-wire/handshake.js +173 -12
- package/dist/src/servers/whatsapp-wire/handshake.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/signal.js +3 -3
- package/dist/src/servers/whatsapp-wire/signal.js.map +1 -1
- package/dist/src/servers/whatsapp.d.ts +7 -1
- package/dist/src/servers/whatsapp.js +149 -25
- package/dist/src/servers/whatsapp.js.map +1 -1
- package/dist/src/servers/zalo.d.ts +1 -0
- package/dist/src/servers/zalo.js +193 -82
- package/dist/src/servers/zalo.js.map +1 -1
- package/docs/channel-setup.md +251 -64
- package/fixtures/examples/crabline.example.yaml +3 -2
- package/package.json +18 -10
- package/dist/src/openclaw/smoke-lock.js +0 -1004
- package/dist/src/openclaw/smoke-lock.js.map +0 -1
- package/fixtures/examples/openclaw-bridge.yaml +0 -125
|
@@ -1,22 +1,213 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
5
|
import { captureDirectoryIdentity, publishPrivateFileAtomically, removeSecuredPrivateDirectory, securePrivateDirectory, syncParentDirectory, } from "./private-file.js";
|
|
5
|
-
import { OPENCLAW_CRABLINE_ARTIFACT_POINTER_PATH, OPENCLAW_CRABLINE_ARTIFACT_STORE_DIRECTORY, OPENCLAW_CRABLINE_CHANNEL_CAPABILITY_MATRIX_PATH,
|
|
6
|
+
import { OPENCLAW_CRABLINE_ARTIFACT_POINTER_PATH, OPENCLAW_CRABLINE_ARTIFACT_STORE_DIRECTORY, OPENCLAW_CRABLINE_CHANNEL_CAPABILITY_MATRIX_PATH, OPENCLAW_CRABLINE_PROVIDER_READINESS_PATH, OPENCLAW_CRABLINE_MANIFEST_PATH, } from "./shared.js";
|
|
6
7
|
const GENERATION_NAME_PATTERN = /^generation-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
7
8
|
const STAGING_NAME_PATTERN = /^\.staging-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
8
9
|
const REMOVAL_TOMBSTONE_PATTERN = /^\.(.+)\.\d+\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.remove$/iu;
|
|
10
|
+
const CURRENT_GENERATION_READ_ATTEMPTS = 8;
|
|
9
11
|
function resolveProviderReadinessArtifactPath(selection) {
|
|
10
|
-
const readinessPath = selection.providerReadinessArtifactPath ?? selection.smokeArtifactPath;
|
|
11
12
|
if (selection.capabilityMatrixPath !== OPENCLAW_CRABLINE_CHANNEL_CAPABILITY_MATRIX_PATH ||
|
|
12
|
-
|
|
13
|
+
selection.providerReadinessArtifactPath !== OPENCLAW_CRABLINE_PROVIDER_READINESS_PATH) {
|
|
13
14
|
throw new Error("OpenClaw Crabline artifact selection paths are malformed.");
|
|
14
15
|
}
|
|
15
|
-
return
|
|
16
|
+
return selection.providerReadinessArtifactPath;
|
|
16
17
|
}
|
|
17
18
|
function isMissingPathError(error) {
|
|
18
19
|
return error.code === "ENOENT";
|
|
19
20
|
}
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function hasNonEmptyStringFields(value, fields) {
|
|
25
|
+
return fields.every((field) => typeof value[field] === "string" && value[field].length > 0);
|
|
26
|
+
}
|
|
27
|
+
function isGeneratedManifest(value) {
|
|
28
|
+
if (value.version !== 1 ||
|
|
29
|
+
!hasNonEmptyStringFields(value, ["adminToken", "baseUrl", "provider"]) ||
|
|
30
|
+
!isRecord(value.endpoints) ||
|
|
31
|
+
!isRecord(value.env)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const endpoints = value.endpoints;
|
|
35
|
+
const env = value.env;
|
|
36
|
+
const baseUrl = value.baseUrl;
|
|
37
|
+
switch (value.provider) {
|
|
38
|
+
case "discord":
|
|
39
|
+
return (hasNonEmptyStringFields(value, ["applicationId", "botToken", "botUserId"]) &&
|
|
40
|
+
hasNonEmptyStringFields(endpoints, [
|
|
41
|
+
"adminInboundUrl",
|
|
42
|
+
"apiRoot",
|
|
43
|
+
"gatewayBotUrl",
|
|
44
|
+
"gatewayUrl",
|
|
45
|
+
]) &&
|
|
46
|
+
hasNonEmptyStringFields(env, ["DISCORD_BOT_TOKEN"]) &&
|
|
47
|
+
endpoints.adminInboundUrl === `${baseUrl}/crabline/discord/inbound` &&
|
|
48
|
+
endpoints.apiRoot === `${baseUrl}/api` &&
|
|
49
|
+
endpoints.gatewayBotUrl === `${baseUrl}/api/v10/gateway/bot` &&
|
|
50
|
+
endpoints.gatewayUrl === `${baseUrl.replace(/^http/u, "ws")}/gateway` &&
|
|
51
|
+
env.DISCORD_BOT_TOKEN === value.botToken);
|
|
52
|
+
case "mattermost":
|
|
53
|
+
return (hasNonEmptyStringFields(value, ["botToken", "botUserId"]) &&
|
|
54
|
+
hasNonEmptyStringFields(endpoints, ["adminInboundUrl", "apiRoot", "websocketUrl"]) &&
|
|
55
|
+
hasNonEmptyStringFields(env, ["MATTERMOST_BOT_TOKEN", "MATTERMOST_URL"]) &&
|
|
56
|
+
endpoints.adminInboundUrl === `${baseUrl}/crabline/mattermost/inbound` &&
|
|
57
|
+
endpoints.apiRoot === `${baseUrl}/api/v4` &&
|
|
58
|
+
endpoints.websocketUrl === `${baseUrl.replace(/^http/u, "ws")}/api/v4/websocket` &&
|
|
59
|
+
env.MATTERMOST_BOT_TOKEN === value.botToken &&
|
|
60
|
+
env.MATTERMOST_URL === baseUrl);
|
|
61
|
+
case "matrix":
|
|
62
|
+
return (hasNonEmptyStringFields(value, ["accessToken", "botUserId", "deviceId"]) &&
|
|
63
|
+
hasNonEmptyStringFields(endpoints, ["adminInboundUrl", "clientApiRoot", "syncUrl"]) &&
|
|
64
|
+
hasNonEmptyStringFields(env, [
|
|
65
|
+
"MATRIX_ACCESS_TOKEN",
|
|
66
|
+
"MATRIX_BASE_URL",
|
|
67
|
+
"MATRIX_USER_ID",
|
|
68
|
+
]) &&
|
|
69
|
+
endpoints.adminInboundUrl === `${baseUrl}/crabline/matrix/inbound` &&
|
|
70
|
+
endpoints.clientApiRoot === `${baseUrl}/_matrix/client/v3` &&
|
|
71
|
+
endpoints.syncUrl === `${endpoints.clientApiRoot}/sync` &&
|
|
72
|
+
env.MATRIX_ACCESS_TOKEN === value.accessToken &&
|
|
73
|
+
env.MATRIX_BASE_URL === baseUrl &&
|
|
74
|
+
env.MATRIX_USER_ID === value.botUserId);
|
|
75
|
+
case "signal":
|
|
76
|
+
return (hasNonEmptyStringFields(value, ["account"]) &&
|
|
77
|
+
hasNonEmptyStringFields(endpoints, ["adminInboundUrl", "apiRoot", "eventsUrl", "rpcUrl"]) &&
|
|
78
|
+
endpoints.adminInboundUrl === `${baseUrl}/crabline/signal/inbound` &&
|
|
79
|
+
endpoints.apiRoot === baseUrl &&
|
|
80
|
+
endpoints.eventsUrl === `${baseUrl}/api/v1/events` &&
|
|
81
|
+
endpoints.rpcUrl === `${baseUrl}/api/v1/rpc` &&
|
|
82
|
+
Object.keys(env).length === 0);
|
|
83
|
+
case "slack":
|
|
84
|
+
return (hasNonEmptyStringFields(value, ["botToken", "signingSecret"]) &&
|
|
85
|
+
hasNonEmptyStringFields(endpoints, ["adminInboundUrl", "apiRoot", "eventsUrl"]) &&
|
|
86
|
+
hasNonEmptyStringFields(env, [
|
|
87
|
+
"SLACK_API_URL",
|
|
88
|
+
"SLACK_BOT_TOKEN",
|
|
89
|
+
"SLACK_SIGNING_SECRET",
|
|
90
|
+
]) &&
|
|
91
|
+
endpoints.adminInboundUrl === `${baseUrl}/crabline/slack/inbound` &&
|
|
92
|
+
endpoints.apiRoot === `${baseUrl}/api/` &&
|
|
93
|
+
endpoints.eventsUrl === `${baseUrl}/slack/events` &&
|
|
94
|
+
env.SLACK_API_URL === endpoints.apiRoot &&
|
|
95
|
+
env.SLACK_BOT_TOKEN === value.botToken &&
|
|
96
|
+
env.SLACK_SIGNING_SECRET === value.signingSecret);
|
|
97
|
+
case "telegram":
|
|
98
|
+
return (hasNonEmptyStringFields(value, ["botToken"]) &&
|
|
99
|
+
hasNonEmptyStringFields(endpoints, ["adminInboundUrl", "apiRoot"]) &&
|
|
100
|
+
hasNonEmptyStringFields(env, ["TELEGRAM_BOT_TOKEN"]) &&
|
|
101
|
+
endpoints.adminInboundUrl === `${baseUrl}/crabline/telegram/inbound` &&
|
|
102
|
+
endpoints.apiRoot === baseUrl &&
|
|
103
|
+
env.TELEGRAM_BOT_TOKEN === value.botToken);
|
|
104
|
+
case "whatsapp":
|
|
105
|
+
return (hasNonEmptyStringFields(value, [
|
|
106
|
+
"accessToken",
|
|
107
|
+
"graphVersion",
|
|
108
|
+
"phoneNumberId",
|
|
109
|
+
"selfJid",
|
|
110
|
+
]) &&
|
|
111
|
+
hasNonEmptyStringFields(endpoints, [
|
|
112
|
+
"adminInboundUrl",
|
|
113
|
+
"apiRoot",
|
|
114
|
+
"baileysWebSocketUrl",
|
|
115
|
+
"messagesUrl",
|
|
116
|
+
"phoneNumberUrl",
|
|
117
|
+
]) &&
|
|
118
|
+
hasNonEmptyStringFields(env, [
|
|
119
|
+
"CLOUD_API_ACCESS_TOKEN",
|
|
120
|
+
"CLOUD_API_VERSION",
|
|
121
|
+
"WA_BASE_URL",
|
|
122
|
+
"WA_PHONE_NUMBER_ID",
|
|
123
|
+
]) &&
|
|
124
|
+
endpoints.adminInboundUrl === `${baseUrl}/_crabline/admin/whatsapp/inbound` &&
|
|
125
|
+
endpoints.apiRoot === `${baseUrl}/${value.graphVersion}` &&
|
|
126
|
+
endpoints.phoneNumberUrl ===
|
|
127
|
+
`${endpoints.apiRoot}/${value.phoneNumberId}` &&
|
|
128
|
+
endpoints.messagesUrl === `${endpoints.phoneNumberUrl}/messages` &&
|
|
129
|
+
endpoints.baileysWebSocketUrl ===
|
|
130
|
+
`${baseUrl.replace(/^http/u, "ws")}/ws/chat?access_token=${encodeURIComponent(value.accessToken)}` &&
|
|
131
|
+
env.CLOUD_API_ACCESS_TOKEN === value.accessToken &&
|
|
132
|
+
env.CLOUD_API_VERSION === value.graphVersion &&
|
|
133
|
+
env.WA_BASE_URL === baseUrl &&
|
|
134
|
+
env.WA_PHONE_NUMBER_ID === value.phoneNumberId);
|
|
135
|
+
case "zalo":
|
|
136
|
+
return (hasNonEmptyStringFields(value, ["botId", "botToken"]) &&
|
|
137
|
+
hasNonEmptyStringFields(endpoints, ["adminInboundUrl", "apiRoot"]) &&
|
|
138
|
+
hasNonEmptyStringFields(env, ["ZALO_API_URL", "ZALO_BOT_TOKEN"]) &&
|
|
139
|
+
endpoints.adminInboundUrl === `${baseUrl}/crabline/zalo/inbound` &&
|
|
140
|
+
endpoints.apiRoot === baseUrl &&
|
|
141
|
+
env.ZALO_API_URL === baseUrl &&
|
|
142
|
+
env.ZALO_BOT_TOKEN === value.botToken);
|
|
143
|
+
default:
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function isSuccessfulProbe(value, manifest) {
|
|
148
|
+
if (!isRecord(value)) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
switch (manifest.provider) {
|
|
152
|
+
case "discord":
|
|
153
|
+
return (value.id === manifest.botUserId &&
|
|
154
|
+
value.bot === true &&
|
|
155
|
+
typeof value.username === "string" &&
|
|
156
|
+
value.username.trim().length > 0);
|
|
157
|
+
case "mattermost":
|
|
158
|
+
return (value.id === manifest.botUserId &&
|
|
159
|
+
typeof value.username === "string" &&
|
|
160
|
+
value.username.trim().length > 0 &&
|
|
161
|
+
typeof value.update_at === "number" &&
|
|
162
|
+
Number.isSafeInteger(value.update_at) &&
|
|
163
|
+
value.update_at >= 0);
|
|
164
|
+
case "matrix":
|
|
165
|
+
return value.user_id === manifest.botUserId;
|
|
166
|
+
case "signal":
|
|
167
|
+
return (value.ok === true &&
|
|
168
|
+
typeof value.status === "number" &&
|
|
169
|
+
Number.isInteger(value.status) &&
|
|
170
|
+
value.status >= 200 &&
|
|
171
|
+
value.status < 300);
|
|
172
|
+
case "slack":
|
|
173
|
+
return value.ok === true;
|
|
174
|
+
case "telegram": {
|
|
175
|
+
const result = value.result;
|
|
176
|
+
const tokenBotId = typeof manifest.botToken === "string"
|
|
177
|
+
? /^([1-9]\d*):/u.exec(manifest.botToken)?.[1]
|
|
178
|
+
: undefined;
|
|
179
|
+
const expectedBotId = tokenBotId === undefined ? undefined : Number(tokenBotId);
|
|
180
|
+
return (value.ok === true &&
|
|
181
|
+
isRecord(result) &&
|
|
182
|
+
typeof result.id === "number" &&
|
|
183
|
+
Number.isSafeInteger(result.id) &&
|
|
184
|
+
result.id > 0 &&
|
|
185
|
+
Number.isSafeInteger(expectedBotId) &&
|
|
186
|
+
result.id === expectedBotId &&
|
|
187
|
+
result.is_bot === true &&
|
|
188
|
+
typeof result.first_name === "string" &&
|
|
189
|
+
result.first_name.length > 0);
|
|
190
|
+
}
|
|
191
|
+
case "whatsapp":
|
|
192
|
+
return value.id === manifest.phoneNumberId;
|
|
193
|
+
case "zalo":
|
|
194
|
+
return value.ok === true && isRecord(value.result) && value.result.id === manifest.botId;
|
|
195
|
+
default:
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function isReadinessSection(value, manifest, manifestPath, requireCurrentFields) {
|
|
200
|
+
if (!isRecord(value) || value.manifestPath !== manifestPath || !isRecord(value.result)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
const result = value.result;
|
|
204
|
+
return (result.ok === true &&
|
|
205
|
+
result.provider === manifest.provider &&
|
|
206
|
+
isRecord(result.endpoints) &&
|
|
207
|
+
isDeepStrictEqual(result.endpoints, manifest.endpoints) &&
|
|
208
|
+
isSuccessfulProbe(result.probe, manifest) &&
|
|
209
|
+
(!requireCurrentFields || (result.proof === "provider-api-probe" && result.ready === true)));
|
|
210
|
+
}
|
|
20
211
|
function assertGenerationName(value, field) {
|
|
21
212
|
if (typeof value !== "string" || !GENERATION_NAME_PATTERN.test(value)) {
|
|
22
213
|
throw new Error(`OpenClaw Crabline artifact pointer ${field} is malformed.`);
|
|
@@ -32,28 +223,38 @@ function artifactRemovalTombstoneBaseName(name) {
|
|
|
32
223
|
? originalName
|
|
33
224
|
: null;
|
|
34
225
|
}
|
|
35
|
-
function
|
|
226
|
+
function withPublishedRecorderPath(providerReadiness, recorderPath) {
|
|
36
227
|
const result = providerReadiness.result;
|
|
37
228
|
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
38
229
|
throw new Error("OpenClaw Crabline provider readiness result is malformed.");
|
|
39
230
|
}
|
|
231
|
+
const publishedResult = {
|
|
232
|
+
...result,
|
|
233
|
+
};
|
|
234
|
+
if (recorderPath === undefined) {
|
|
235
|
+
delete publishedResult.recorderPath;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
publishedResult.recorderPath = recorderPath;
|
|
239
|
+
}
|
|
40
240
|
return {
|
|
41
241
|
...providerReadiness,
|
|
42
|
-
result:
|
|
43
|
-
...result,
|
|
44
|
-
recorderPath,
|
|
45
|
-
},
|
|
242
|
+
result: publishedResult,
|
|
46
243
|
};
|
|
47
244
|
}
|
|
48
245
|
function parseArtifactPointer(contents) {
|
|
49
|
-
let
|
|
246
|
+
let parsed;
|
|
50
247
|
try {
|
|
51
|
-
|
|
248
|
+
parsed = JSON.parse(contents);
|
|
52
249
|
}
|
|
53
250
|
catch (error) {
|
|
54
251
|
throw new Error("OpenClaw Crabline artifact pointer is malformed.", { cause: error });
|
|
55
252
|
}
|
|
56
|
-
if (
|
|
253
|
+
if (!isRecord(parsed)) {
|
|
254
|
+
throw new Error("OpenClaw Crabline artifact pointer is malformed.");
|
|
255
|
+
}
|
|
256
|
+
const value = parsed;
|
|
257
|
+
if (value.version !== 3) {
|
|
57
258
|
throw new Error("OpenClaw Crabline artifact pointer is malformed.");
|
|
58
259
|
}
|
|
59
260
|
assertGenerationName(value.generation, "generation");
|
|
@@ -67,26 +268,31 @@ function parseArtifactPointer(contents) {
|
|
|
67
268
|
capabilityMatrixPath: generationArtifactPath(value.generation, OPENCLAW_CRABLINE_CHANNEL_CAPABILITY_MATRIX_PATH),
|
|
68
269
|
manifestPath: generationArtifactPath(value.generation, OPENCLAW_CRABLINE_MANIFEST_PATH),
|
|
69
270
|
providerReadinessArtifactPath: generationArtifactPath(value.generation, OPENCLAW_CRABLINE_PROVIDER_READINESS_PATH),
|
|
70
|
-
smokeArtifactPath: generationArtifactPath(value.generation, OPENCLAW_CRABLINE_CHANNEL_SMOKE_PATH),
|
|
71
271
|
};
|
|
72
272
|
if (typeof value.capabilityMatrixPath !== "string" ||
|
|
73
273
|
value.capabilityMatrixPath !== expected.capabilityMatrixPath ||
|
|
74
274
|
value.manifestPath !== expected.manifestPath ||
|
|
75
|
-
|
|
76
|
-
value.providerReadinessArtifactPath !== expected.providerReadinessArtifactPath) ||
|
|
77
|
-
(value.smokeArtifactPath !== undefined &&
|
|
78
|
-
value.smokeArtifactPath !== expected.smokeArtifactPath) ||
|
|
79
|
-
(value.providerReadinessArtifactPath === undefined && value.smokeArtifactPath === undefined)) {
|
|
275
|
+
value.providerReadinessArtifactPath !== expected.providerReadinessArtifactPath) {
|
|
80
276
|
throw new Error("OpenClaw Crabline artifact pointer is malformed.");
|
|
81
277
|
}
|
|
278
|
+
if (value.recorderSnapshotPath !== null && typeof value.recorderSnapshotPath !== "string") {
|
|
279
|
+
throw new Error("OpenClaw Crabline artifact pointer is malformed.");
|
|
280
|
+
}
|
|
281
|
+
if (typeof value.recorderSnapshotPath === "string") {
|
|
282
|
+
const recorderFileName = path.basename(value.recorderSnapshotPath);
|
|
283
|
+
if (!recorderFileName.endsWith(".jsonl") ||
|
|
284
|
+
value.recorderSnapshotPath !== generationArtifactPath(value.generation, recorderFileName)) {
|
|
285
|
+
throw new Error("OpenClaw Crabline artifact pointer is malformed.");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
82
288
|
return {
|
|
83
289
|
capabilityMatrixPath: value.capabilityMatrixPath,
|
|
84
290
|
generation: value.generation,
|
|
85
291
|
...(value.previousGeneration ? { previousGeneration: value.previousGeneration } : {}),
|
|
86
292
|
manifestPath: value.manifestPath,
|
|
87
|
-
providerReadinessArtifactPath: value.providerReadinessArtifactPath
|
|
88
|
-
|
|
89
|
-
version:
|
|
293
|
+
providerReadinessArtifactPath: value.providerReadinessArtifactPath,
|
|
294
|
+
recorderSnapshotPath: value.recorderSnapshotPath,
|
|
295
|
+
version: 3,
|
|
90
296
|
};
|
|
91
297
|
}
|
|
92
298
|
export async function readOpenClawCrablineArtifactPointer(outputDir) {
|
|
@@ -100,17 +306,155 @@ export async function readOpenClawCrablineArtifactPointer(outputDir) {
|
|
|
100
306
|
throw error;
|
|
101
307
|
}
|
|
102
308
|
}
|
|
103
|
-
async function
|
|
309
|
+
async function assertArtifactGenerationExists(outputDir, pointer) {
|
|
310
|
+
const generationDirectory = path.resolve(outputDir, OPENCLAW_CRABLINE_ARTIFACT_STORE_DIRECTORY, pointer.generation);
|
|
311
|
+
let currentGeneration;
|
|
312
|
+
try {
|
|
313
|
+
currentGeneration = await captureDirectoryIdentity(generationDirectory);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.", {
|
|
317
|
+
cause: error,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
const assertGenerationIdentity = async () => {
|
|
321
|
+
try {
|
|
322
|
+
await currentGeneration.assertIdentityAt();
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.", {
|
|
326
|
+
cause: error,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
};
|
|
104
330
|
for (const artifactPath of [
|
|
105
331
|
pointer.manifestPath,
|
|
106
332
|
pointer.capabilityMatrixPath,
|
|
107
333
|
pointer.providerReadinessArtifactPath,
|
|
108
334
|
]) {
|
|
109
|
-
|
|
335
|
+
await assertGenerationIdentity();
|
|
336
|
+
let stats;
|
|
337
|
+
try {
|
|
338
|
+
stats = await fs.lstat(path.join(outputDir, artifactPath));
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.", {
|
|
342
|
+
cause: error,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
110
345
|
if (!stats.isFile()) {
|
|
111
346
|
throw new Error("OpenClaw Crabline current artifact generation is incomplete.");
|
|
112
347
|
}
|
|
348
|
+
await assertGenerationIdentity();
|
|
113
349
|
}
|
|
350
|
+
const readArtifactObject = async (artifactPath) => {
|
|
351
|
+
try {
|
|
352
|
+
await assertGenerationIdentity();
|
|
353
|
+
const value = JSON.parse(await fs.readFile(path.join(outputDir, artifactPath), "utf8"));
|
|
354
|
+
await assertGenerationIdentity();
|
|
355
|
+
if (!isRecord(value)) {
|
|
356
|
+
throw new Error("artifact is not an object");
|
|
357
|
+
}
|
|
358
|
+
return value;
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.", {
|
|
362
|
+
cause: error,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
const readNestedRecorderPath = (value) => {
|
|
367
|
+
const result = value.result;
|
|
368
|
+
const recorderPath = result.recorderPath;
|
|
369
|
+
if (recorderPath !== undefined && typeof recorderPath !== "string") {
|
|
370
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.");
|
|
371
|
+
}
|
|
372
|
+
return recorderPath;
|
|
373
|
+
};
|
|
374
|
+
const manifest = await readArtifactObject(pointer.manifestPath);
|
|
375
|
+
const capabilityMatrix = await readArtifactObject(pointer.capabilityMatrixPath);
|
|
376
|
+
const readiness = await readArtifactObject(pointer.providerReadinessArtifactPath);
|
|
377
|
+
const providerReadiness = readiness.providerReadiness;
|
|
378
|
+
if (!isGeneratedManifest(manifest) ||
|
|
379
|
+
capabilityMatrix.version !== 1 ||
|
|
380
|
+
capabilityMatrix.source !== "openclaw/crabline" ||
|
|
381
|
+
capabilityMatrix.manifestPath !== pointer.manifestPath ||
|
|
382
|
+
capabilityMatrix.channelDriver !== "crabline" ||
|
|
383
|
+
typeof capabilityMatrix.selectedChannel !== "string" ||
|
|
384
|
+
capabilityMatrix.selectedChannel.length === 0 ||
|
|
385
|
+
capabilityMatrix.report === null ||
|
|
386
|
+
typeof capabilityMatrix.report !== "object" ||
|
|
387
|
+
Array.isArray(capabilityMatrix.report) ||
|
|
388
|
+
readiness.version !== 2 ||
|
|
389
|
+
readiness.source !== "openclaw/crabline" ||
|
|
390
|
+
readiness.manifestPath !== pointer.manifestPath ||
|
|
391
|
+
readiness.channelDriver !== "crabline" ||
|
|
392
|
+
readiness.selectedChannel !== capabilityMatrix.selectedChannel ||
|
|
393
|
+
manifest.provider !== readiness.selectedChannel ||
|
|
394
|
+
!isReadinessSection(providerReadiness, manifest, pointer.manifestPath, true)) {
|
|
395
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.");
|
|
396
|
+
}
|
|
397
|
+
const manifestRecorderPath = typeof manifest.recorderPath === "string" ? manifest.recorderPath : undefined;
|
|
398
|
+
const providerReadinessRecorderPath = readNestedRecorderPath(providerReadiness);
|
|
399
|
+
const recorderPaths = [manifestRecorderPath, providerReadinessRecorderPath];
|
|
400
|
+
if (manifest.recorderPath !== undefined && typeof manifest.recorderPath !== "string") {
|
|
401
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.");
|
|
402
|
+
}
|
|
403
|
+
if (pointer.recorderSnapshotPath === null) {
|
|
404
|
+
if (recorderPaths.some((recorderPath) => recorderPath !== undefined)) {
|
|
405
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.");
|
|
406
|
+
}
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const resolvedRecorderPaths = recorderPaths.map((recorderPath) => recorderPath === undefined ? undefined : path.resolve(outputDir, recorderPath));
|
|
410
|
+
if (resolvedRecorderPaths.some((recorderPath) => recorderPath === undefined || path.dirname(recorderPath) !== generationDirectory) ||
|
|
411
|
+
new Set(resolvedRecorderPaths).size !== 1) {
|
|
412
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.");
|
|
413
|
+
}
|
|
414
|
+
const recorderPath = resolvedRecorderPaths[0];
|
|
415
|
+
if (pointer.recorderSnapshotPath !== null &&
|
|
416
|
+
recorderPath !== path.resolve(outputDir, pointer.recorderSnapshotPath)) {
|
|
417
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.");
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
await assertGenerationIdentity();
|
|
421
|
+
const recorderStats = await fs.lstat(recorderPath, { bigint: true });
|
|
422
|
+
if (recorderStats.isFile() && recorderStats.nlink === 1n) {
|
|
423
|
+
await assertGenerationIdentity();
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.", {
|
|
429
|
+
cause: error,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.");
|
|
433
|
+
}
|
|
434
|
+
async function readValidCurrentArtifactGeneration(outputDir, initialPointer) {
|
|
435
|
+
let pointer = initialPointer;
|
|
436
|
+
let lastValidationError;
|
|
437
|
+
for (let attempt = 0; attempt < CURRENT_GENERATION_READ_ATTEMPTS; attempt += 1) {
|
|
438
|
+
try {
|
|
439
|
+
await assertArtifactGenerationExists(outputDir, pointer);
|
|
440
|
+
lastValidationError = undefined;
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
lastValidationError = error;
|
|
444
|
+
}
|
|
445
|
+
const currentPointer = await readOpenClawCrablineArtifactPointer(outputDir);
|
|
446
|
+
if (currentPointer === null || isDeepStrictEqual(currentPointer, pointer)) {
|
|
447
|
+
if (lastValidationError !== undefined) {
|
|
448
|
+
throw lastValidationError;
|
|
449
|
+
}
|
|
450
|
+
if (currentPointer === null) {
|
|
451
|
+
throw new Error("OpenClaw Crabline current artifact generation is incomplete.");
|
|
452
|
+
}
|
|
453
|
+
return pointer;
|
|
454
|
+
}
|
|
455
|
+
pointer = currentPointer;
|
|
456
|
+
}
|
|
457
|
+
throw new Error("OpenClaw Crabline current artifact generation changed too frequently to validate.", lastValidationError === undefined ? undefined : { cause: lastValidationError });
|
|
114
458
|
}
|
|
115
459
|
async function pruneArtifactStore(params) {
|
|
116
460
|
const retainedGenerations = new Set(params.pointer
|
|
@@ -153,11 +497,11 @@ export async function publishOpenClawCrablineArtifactGeneration(params, dependen
|
|
|
153
497
|
await output.assertIdentityAt();
|
|
154
498
|
await store.assertIdentityAt();
|
|
155
499
|
await params.lock.assertOwned();
|
|
156
|
-
|
|
500
|
+
let currentPointer = await readOpenClawCrablineArtifactPointer(outputDir);
|
|
157
501
|
await output.assertIdentityAt();
|
|
158
502
|
await store.assertIdentityAt();
|
|
159
503
|
if (currentPointer) {
|
|
160
|
-
await
|
|
504
|
+
currentPointer = await readValidCurrentArtifactGeneration(outputDir, currentPointer);
|
|
161
505
|
await output.assertIdentityAt();
|
|
162
506
|
await store.assertIdentityAt();
|
|
163
507
|
}
|
|
@@ -193,15 +537,6 @@ export async function publishOpenClawCrablineArtifactGeneration(params, dependen
|
|
|
193
537
|
let primaryError;
|
|
194
538
|
let published;
|
|
195
539
|
try {
|
|
196
|
-
const pointer = {
|
|
197
|
-
capabilityMatrixPath: generationArtifactPath(generation, params.selection.capabilityMatrixPath),
|
|
198
|
-
generation,
|
|
199
|
-
manifestPath: generationArtifactPath(generation, OPENCLAW_CRABLINE_MANIFEST_PATH),
|
|
200
|
-
...(currentPointer ? { previousGeneration: currentPointer.generation } : {}),
|
|
201
|
-
providerReadinessArtifactPath: generationArtifactPath(generation, providerReadinessArtifactPath),
|
|
202
|
-
smokeArtifactPath: generationArtifactPath(generation, providerReadinessArtifactPath),
|
|
203
|
-
version: 1,
|
|
204
|
-
};
|
|
205
540
|
if (params.recorderSnapshot &&
|
|
206
541
|
(path.basename(params.recorderSnapshot.fileName) !== params.recorderSnapshot.fileName ||
|
|
207
542
|
!params.recorderSnapshot.fileName.endsWith(".jsonl"))) {
|
|
@@ -210,12 +545,19 @@ export async function publishOpenClawCrablineArtifactGeneration(params, dependen
|
|
|
210
545
|
const recorderSnapshotPath = params.recorderSnapshot
|
|
211
546
|
? generationArtifactPath(generation, params.recorderSnapshot.fileName)
|
|
212
547
|
: undefined;
|
|
548
|
+
const pointer = {
|
|
549
|
+
capabilityMatrixPath: generationArtifactPath(generation, params.selection.capabilityMatrixPath),
|
|
550
|
+
generation,
|
|
551
|
+
manifestPath: generationArtifactPath(generation, OPENCLAW_CRABLINE_MANIFEST_PATH),
|
|
552
|
+
...(currentPointer ? { previousGeneration: currentPointer.generation } : {}),
|
|
553
|
+
providerReadinessArtifactPath: generationArtifactPath(generation, providerReadinessArtifactPath),
|
|
554
|
+
recorderSnapshotPath: recorderSnapshotPath ?? null,
|
|
555
|
+
version: 3,
|
|
556
|
+
};
|
|
213
557
|
const publishedManifest = recorderSnapshotPath
|
|
214
558
|
? { ...params.manifest, recorderPath: recorderSnapshotPath }
|
|
215
|
-
: params.manifest;
|
|
216
|
-
const providerReadinessBase = recorderSnapshotPath
|
|
217
|
-
? withRecorderSnapshotPath(params.providerReadiness, recorderSnapshotPath)
|
|
218
|
-
: params.providerReadiness;
|
|
559
|
+
: Object.fromEntries(Object.entries(params.manifest).filter(([key]) => key !== "recorderPath"));
|
|
560
|
+
const providerReadinessBase = withPublishedRecorderPath(params.providerReadiness, recorderSnapshotPath);
|
|
219
561
|
const providerReadiness = {
|
|
220
562
|
...providerReadinessBase,
|
|
221
563
|
manifestPath: pointer.manifestPath,
|
|
@@ -238,13 +580,12 @@ export async function publishOpenClawCrablineArtifactGeneration(params, dependen
|
|
|
238
580
|
},
|
|
239
581
|
{
|
|
240
582
|
contents: `${JSON.stringify({
|
|
241
|
-
version:
|
|
583
|
+
version: 2,
|
|
242
584
|
source: "openclaw/crabline",
|
|
243
585
|
channelDriver: params.selection.channelDriver,
|
|
244
586
|
selectedChannel: params.selection.channel,
|
|
245
587
|
manifestPath: pointer.manifestPath,
|
|
246
588
|
providerReadiness,
|
|
247
|
-
smoke: providerReadiness,
|
|
248
589
|
}, null, 2)}\n`,
|
|
249
590
|
fileName: providerReadinessArtifactPath,
|
|
250
591
|
},
|
|
@@ -280,6 +621,10 @@ export async function publishOpenClawCrablineArtifactGeneration(params, dependen
|
|
|
280
621
|
await output.assertIdentityAt();
|
|
281
622
|
await store.assertIdentityAt();
|
|
282
623
|
await staging.assertIdentityAt(generationPath);
|
|
624
|
+
await assertArtifactGenerationExists(outputDir, pointer);
|
|
625
|
+
await output.assertIdentityAt();
|
|
626
|
+
await store.assertIdentityAt();
|
|
627
|
+
await staging.assertIdentityAt(generationPath);
|
|
283
628
|
await params.lock.commitFileAtomically({
|
|
284
629
|
contents: `${JSON.stringify(pointer, null, 2)}\n`,
|
|
285
630
|
destinationPath: path.join(outputDir, OPENCLAW_CRABLINE_ARTIFACT_POINTER_PATH),
|
|
@@ -316,7 +661,6 @@ export async function publishOpenClawCrablineArtifactGeneration(params, dependen
|
|
|
316
661
|
...pointer,
|
|
317
662
|
pointerPath: OPENCLAW_CRABLINE_ARTIFACT_POINTER_PATH,
|
|
318
663
|
providerReadiness,
|
|
319
|
-
smoke: providerReadiness,
|
|
320
664
|
...(warnings ? { warnings } : {}),
|
|
321
665
|
};
|
|
322
666
|
}
|