@archipelagolab/lobi 1.0.0
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/CHANGELOG.md +164 -0
- package/ENDOFFILE +0 -0
- package/EOF +0 -0
- package/LICENSE +21 -0
- package/SPEC-SUPPORT.md +116 -0
- package/YAMLEND +0 -0
- package/api.ts +18 -0
- package/archipelagolab-lobi-1.0.0.tgz +0 -0
- package/auth-presence.ts +56 -0
- package/channel-plugin-api.ts +3 -0
- package/cli-metadata.ts +11 -0
- package/contract-api.ts +17 -0
- package/docs/CHECKLIST.md +83 -0
- package/docs/FORK_SDK_GUIDE.md +279 -0
- package/helper-api.ts +3 -0
- package/index.test.ts +61 -0
- package/index.ts +65 -0
- package/openclaw.plugin.json +23 -0
- package/package.json +52 -0
- package/plugin-entry.handlers.runtime.ts +1 -0
- package/runtime-api.ts +54 -0
- package/runtime-heavy-api.ts +1 -0
- package/scripts/migrate-to-lobi.sh +72 -0
- package/secret-contract-api.ts +5 -0
- package/setup-entry.ts +13 -0
- package/src/account-selection.test.ts +124 -0
- package/src/account-selection.ts +226 -0
- package/src/actions.account-propagation.test.ts +251 -0
- package/src/actions.test.ts +251 -0
- package/src/actions.ts +336 -0
- package/src/approval-auth.test.ts +23 -0
- package/src/approval-auth.ts +25 -0
- package/src/approval-handler.runtime.test.ts +46 -0
- package/src/approval-handler.runtime.ts +400 -0
- package/src/approval-ids.ts +6 -0
- package/src/approval-native.test.ts +329 -0
- package/src/approval-native.ts +336 -0
- package/src/approval-reactions.test.ts +107 -0
- package/src/approval-reactions.ts +158 -0
- package/src/auth-precedence.ts +61 -0
- package/src/channel-account-paths.ts +92 -0
- package/src/channel.account-paths.test.ts +102 -0
- package/src/channel.directory.test.ts +601 -0
- package/src/channel.resolve.test.ts +38 -0
- package/src/channel.runtime.ts +16 -0
- package/src/channel.setup.test.ts +269 -0
- package/src/channel.ts +570 -0
- package/src/cli-metadata.ts +19 -0
- package/src/cli.test.ts +1015 -0
- package/src/cli.ts +1198 -0
- package/src/config-adapter.ts +41 -0
- package/src/config-schema.test.ts +90 -0
- package/src/config-schema.ts +114 -0
- package/src/directory-live.test.ts +200 -0
- package/src/directory-live.ts +238 -0
- package/src/doctor-contract.ts +287 -0
- package/src/doctor.test.ts +440 -0
- package/src/doctor.ts +262 -0
- package/src/env-vars.ts +92 -0
- package/src/exec-approval-resolver.test.ts +68 -0
- package/src/exec-approval-resolver.ts +23 -0
- package/src/exec-approvals.test.ts +483 -0
- package/src/exec-approvals.ts +290 -0
- package/src/group-mentions.ts +41 -0
- package/src/legacy-crypto-inspector-availability.test.ts +81 -0
- package/src/legacy-crypto-inspector-availability.ts +60 -0
- package/src/legacy-crypto.test.ts +234 -0
- package/src/legacy-crypto.ts +549 -0
- package/src/legacy-state.test.ts +86 -0
- package/src/legacy-state.ts +156 -0
- package/src/matrix/account-config.ts +150 -0
- package/src/matrix/accounts.readiness.test.ts +27 -0
- package/src/matrix/accounts.test.ts +757 -0
- package/src/matrix/accounts.ts +194 -0
- package/src/matrix/actions/client.test.ts +215 -0
- package/src/matrix/actions/client.ts +31 -0
- package/src/matrix/actions/devices.test.ts +114 -0
- package/src/matrix/actions/devices.ts +34 -0
- package/src/matrix/actions/limits.test.ts +15 -0
- package/src/matrix/actions/limits.ts +6 -0
- package/src/matrix/actions/messages.test.ts +289 -0
- package/src/matrix/actions/messages.ts +123 -0
- package/src/matrix/actions/pins.test.ts +74 -0
- package/src/matrix/actions/pins.ts +64 -0
- package/src/matrix/actions/polls.test.ts +71 -0
- package/src/matrix/actions/polls.ts +109 -0
- package/src/matrix/actions/profile.test.ts +109 -0
- package/src/matrix/actions/profile.ts +37 -0
- package/src/matrix/actions/reactions.test.ts +135 -0
- package/src/matrix/actions/reactions.ts +59 -0
- package/src/matrix/actions/room.test.ts +79 -0
- package/src/matrix/actions/room.ts +71 -0
- package/src/matrix/actions/summary.test.ts +87 -0
- package/src/matrix/actions/summary.ts +88 -0
- package/src/matrix/actions/types.ts +82 -0
- package/src/matrix/actions/verification.test.ts +105 -0
- package/src/matrix/actions/verification.ts +237 -0
- package/src/matrix/actions.ts +37 -0
- package/src/matrix/active-client.ts +26 -0
- package/src/matrix/async-lock.ts +18 -0
- package/src/matrix/backup-health.ts +115 -0
- package/src/matrix/client/config-runtime-api.ts +14 -0
- package/src/matrix/client/config-secret-input.runtime.ts +1 -0
- package/src/matrix/client/config.ts +982 -0
- package/src/matrix/client/create-client.test.ts +115 -0
- package/src/matrix/client/create-client.ts +101 -0
- package/src/matrix/client/env-auth.ts +6 -0
- package/src/matrix/client/file-sync-store.test.ts +265 -0
- package/src/matrix/client/file-sync-store.ts +289 -0
- package/src/matrix/client/logging.ts +123 -0
- package/src/matrix/client/migration-snapshot.runtime.ts +1 -0
- package/src/matrix/client/private-network-host.ts +56 -0
- package/src/matrix/client/runtime.ts +4 -0
- package/src/matrix/client/shared.test.ts +344 -0
- package/src/matrix/client/shared.ts +306 -0
- package/src/matrix/client/storage.test.ts +634 -0
- package/src/matrix/client/storage.ts +544 -0
- package/src/matrix/client/types.ts +50 -0
- package/src/matrix/client-bootstrap.test.ts +84 -0
- package/src/matrix/client-bootstrap.ts +164 -0
- package/src/matrix/client-resolver.test-helpers.ts +147 -0
- package/src/matrix/client.test.ts +1521 -0
- package/src/matrix/client.ts +23 -0
- package/src/matrix/config-paths.ts +31 -0
- package/src/matrix/config-update.test.ts +237 -0
- package/src/matrix/config-update.ts +291 -0
- package/src/matrix/credentials-read.ts +206 -0
- package/src/matrix/credentials-write.runtime.ts +26 -0
- package/src/matrix/credentials.test.ts +501 -0
- package/src/matrix/credentials.ts +95 -0
- package/src/matrix/deps.test.ts +74 -0
- package/src/matrix/deps.ts +225 -0
- package/src/matrix/device-health.test.ts +45 -0
- package/src/matrix/device-health.ts +31 -0
- package/src/matrix/direct-management.test.ts +350 -0
- package/src/matrix/direct-management.ts +347 -0
- package/src/matrix/direct-room.test.ts +61 -0
- package/src/matrix/direct-room.ts +128 -0
- package/src/matrix/draft-stream.test.ts +406 -0
- package/src/matrix/draft-stream.ts +216 -0
- package/src/matrix/encryption-guidance.ts +27 -0
- package/src/matrix/errors.ts +21 -0
- package/src/matrix/format.test.ts +340 -0
- package/src/matrix/format.ts +428 -0
- package/src/matrix/legacy-crypto-inspector.ts +95 -0
- package/src/matrix/media-errors.ts +20 -0
- package/src/matrix/media-text.ts +169 -0
- package/src/matrix/monitor/access-state.test.ts +45 -0
- package/src/matrix/monitor/access-state.ts +77 -0
- package/src/matrix/monitor/ack-config.test.ts +57 -0
- package/src/matrix/monitor/ack-config.ts +26 -0
- package/src/matrix/monitor/allowlist.test.ts +45 -0
- package/src/matrix/monitor/allowlist.ts +94 -0
- package/src/matrix/monitor/auto-join.test.ts +203 -0
- package/src/matrix/monitor/auto-join.ts +86 -0
- package/src/matrix/monitor/config.test.ts +197 -0
- package/src/matrix/monitor/config.ts +303 -0
- package/src/matrix/monitor/context-summary.ts +43 -0
- package/src/matrix/monitor/direct.test.ts +529 -0
- package/src/matrix/monitor/direct.ts +270 -0
- package/src/matrix/monitor/events.test.ts +1524 -0
- package/src/matrix/monitor/events.ts +213 -0
- package/src/matrix/monitor/handler.body-for-agent.test.ts +396 -0
- package/src/matrix/monitor/handler.group-history.test.ts +648 -0
- package/src/matrix/monitor/handler.media-failure.test.ts +267 -0
- package/src/matrix/monitor/handler.test-helpers.ts +308 -0
- package/src/matrix/monitor/handler.test.ts +2952 -0
- package/src/matrix/monitor/handler.thread-root-media.test.ts +82 -0
- package/src/matrix/monitor/handler.ts +1679 -0
- package/src/matrix/monitor/inbound-dedupe.test.ts +146 -0
- package/src/matrix/monitor/inbound-dedupe.ts +267 -0
- package/src/matrix/monitor/index.test.ts +920 -0
- package/src/matrix/monitor/index.ts +434 -0
- package/src/matrix/monitor/legacy-crypto-restore.test.ts +206 -0
- package/src/matrix/monitor/legacy-crypto-restore.ts +139 -0
- package/src/matrix/monitor/location.ts +100 -0
- package/src/matrix/monitor/media.test.ts +159 -0
- package/src/matrix/monitor/media.ts +119 -0
- package/src/matrix/monitor/mentions.test.ts +289 -0
- package/src/matrix/monitor/mentions.ts +177 -0
- package/src/matrix/monitor/reaction-events.test.ts +326 -0
- package/src/matrix/monitor/reaction-events.ts +187 -0
- package/src/matrix/monitor/recent-invite.test.ts +92 -0
- package/src/matrix/monitor/recent-invite.ts +30 -0
- package/src/matrix/monitor/replies.test.ts +265 -0
- package/src/matrix/monitor/replies.ts +136 -0
- package/src/matrix/monitor/reply-context.test.ts +276 -0
- package/src/matrix/monitor/reply-context.ts +92 -0
- package/src/matrix/monitor/room-history.test.ts +258 -0
- package/src/matrix/monitor/room-history.ts +301 -0
- package/src/matrix/monitor/room-info.test.ts +201 -0
- package/src/matrix/monitor/room-info.ts +126 -0
- package/src/matrix/monitor/rooms.test.ts +121 -0
- package/src/matrix/monitor/rooms.ts +52 -0
- package/src/matrix/monitor/route.test.ts +255 -0
- package/src/matrix/monitor/route.ts +178 -0
- package/src/matrix/monitor/runtime-api.ts +31 -0
- package/src/matrix/monitor/startup-verification.test.ts +294 -0
- package/src/matrix/monitor/startup-verification.ts +237 -0
- package/src/matrix/monitor/startup.test.ts +257 -0
- package/src/matrix/monitor/startup.ts +218 -0
- package/src/matrix/monitor/status.ts +111 -0
- package/src/matrix/monitor/sync-lifecycle.test.ts +224 -0
- package/src/matrix/monitor/sync-lifecycle.ts +91 -0
- package/src/matrix/monitor/task-runner.ts +38 -0
- package/src/matrix/monitor/thread-context.test.ts +149 -0
- package/src/matrix/monitor/thread-context.ts +108 -0
- package/src/matrix/monitor/threads.test.ts +68 -0
- package/src/matrix/monitor/threads.ts +85 -0
- package/src/matrix/monitor/types.ts +30 -0
- package/src/matrix/monitor/verification-events.ts +627 -0
- package/src/matrix/monitor/verification-utils.test.ts +47 -0
- package/src/matrix/monitor/verification-utils.ts +46 -0
- package/src/matrix/outbound-media-runtime.ts +1 -0
- package/src/matrix/poll-summary.ts +110 -0
- package/src/matrix/poll-types.test.ts +205 -0
- package/src/matrix/poll-types.ts +433 -0
- package/src/matrix/probe.runtime.ts +4 -0
- package/src/matrix/probe.test.ts +154 -0
- package/src/matrix/probe.ts +96 -0
- package/src/matrix/profile.test.ts +154 -0
- package/src/matrix/profile.ts +184 -0
- package/src/matrix/reaction-common.test.ts +96 -0
- package/src/matrix/reaction-common.ts +147 -0
- package/src/matrix/sdk/crypto-bootstrap.test.ts +505 -0
- package/src/matrix/sdk/crypto-bootstrap.ts +341 -0
- package/src/matrix/sdk/crypto-facade.test.ts +197 -0
- package/src/matrix/sdk/crypto-facade.ts +207 -0
- package/src/matrix/sdk/crypto-node.runtime.test.ts +27 -0
- package/src/matrix/sdk/crypto-node.runtime.ts +9 -0
- package/src/matrix/sdk/crypto-runtime.ts +11 -0
- package/src/matrix/sdk/decrypt-bridge.ts +356 -0
- package/src/matrix/sdk/event-helpers.test.ts +60 -0
- package/src/matrix/sdk/event-helpers.ts +71 -0
- package/src/matrix/sdk/http-client.test.ts +134 -0
- package/src/matrix/sdk/http-client.ts +87 -0
- package/src/matrix/sdk/idb-persistence-lock.ts +51 -0
- package/src/matrix/sdk/idb-persistence.lock-order.test.ts +108 -0
- package/src/matrix/sdk/idb-persistence.test-helpers.ts +88 -0
- package/src/matrix/sdk/idb-persistence.test.ts +149 -0
- package/src/matrix/sdk/idb-persistence.ts +283 -0
- package/src/matrix/sdk/logger.test.ts +25 -0
- package/src/matrix/sdk/logger.ts +108 -0
- package/src/matrix/sdk/read-response-with-limit.ts +19 -0
- package/src/matrix/sdk/recovery-key-store.test.ts +385 -0
- package/src/matrix/sdk/recovery-key-store.ts +430 -0
- package/src/matrix/sdk/transport.test.ts +161 -0
- package/src/matrix/sdk/transport.ts +344 -0
- package/src/matrix/sdk/types.ts +236 -0
- package/src/matrix/sdk/verification-manager.test.ts +509 -0
- package/src/matrix/sdk/verification-manager.ts +694 -0
- package/src/matrix/sdk/verification-status.ts +23 -0
- package/src/matrix/sdk.test.ts +2568 -0
- package/src/matrix/sdk.ts +1789 -0
- package/src/matrix/send/client.test.ts +174 -0
- package/src/matrix/send/client.ts +90 -0
- package/src/matrix/send/formatting.ts +189 -0
- package/src/matrix/send/media.ts +244 -0
- package/src/matrix/send/targets.test.ts +254 -0
- package/src/matrix/send/targets.ts +104 -0
- package/src/matrix/send/types.ts +134 -0
- package/src/matrix/send.test.ts +958 -0
- package/src/matrix/send.ts +609 -0
- package/src/matrix/session-store-metadata.ts +108 -0
- package/src/matrix/startup-abort.ts +44 -0
- package/src/matrix/sync-state.ts +27 -0
- package/src/matrix/target-ids.ts +102 -0
- package/src/matrix/thread-bindings-shared.ts +201 -0
- package/src/matrix/thread-bindings.test.ts +673 -0
- package/src/matrix/thread-bindings.ts +577 -0
- package/src/matrix-migration.runtime.ts +9 -0
- package/src/migration-config.test.ts +228 -0
- package/src/migration-config.ts +243 -0
- package/src/migration-snapshot-backup.ts +117 -0
- package/src/migration-snapshot.test.ts +184 -0
- package/src/migration-snapshot.ts +55 -0
- package/src/onboarding.resolve.test.ts +55 -0
- package/src/onboarding.test-harness.ts +158 -0
- package/src/onboarding.test.ts +665 -0
- package/src/onboarding.ts +773 -0
- package/src/outbound.test.ts +173 -0
- package/src/outbound.ts +78 -0
- package/src/plugin-entry.runtime.js +159 -0
- package/src/plugin-entry.runtime.test.ts +108 -0
- package/src/plugin-entry.runtime.ts +68 -0
- package/src/profile-update.ts +68 -0
- package/src/record-shared.ts +3 -0
- package/src/resolve-targets.test.ts +178 -0
- package/src/resolve-targets.ts +175 -0
- package/src/resolver.ts +21 -0
- package/src/runtime-api.ts +144 -0
- package/src/runtime.ts +7 -0
- package/src/secret-contract.ts +174 -0
- package/src/session-route.test.ts +315 -0
- package/src/session-route.ts +113 -0
- package/src/setup-bootstrap.ts +94 -0
- package/src/setup-config.ts +222 -0
- package/src/setup-contract.ts +89 -0
- package/src/setup-core.test.ts +326 -0
- package/src/setup-core.ts +50 -0
- package/src/setup-surface.ts +4 -0
- package/src/startup-maintenance.test.ts +227 -0
- package/src/startup-maintenance.ts +114 -0
- package/src/storage-paths.ts +92 -0
- package/src/test-helpers.ts +42 -0
- package/src/test-mocks.ts +55 -0
- package/src/test-runtime.ts +72 -0
- package/src/test-support/monitor-route-test-support.ts +8 -0
- package/src/tool-actions.runtime.ts +1 -0
- package/src/tool-actions.test.ts +422 -0
- package/src/tool-actions.ts +498 -0
- package/src/types.ts +230 -0
- package/test-api.ts +2 -0
- package/thread-bindings-runtime.ts +4 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { withTempHome } from "../../../test/helpers/temp-home.js";
|
|
5
|
+
import { resolveMatrixMigrationAccountTarget } from "./migration-config.js";
|
|
6
|
+
import {
|
|
7
|
+
LOBI_OPS_ACCESS_TOKEN,
|
|
8
|
+
MATRIX_OPS_ACCOUNT_ID,
|
|
9
|
+
MATRIX_OPS_USER_ID,
|
|
10
|
+
MATRIX_TEST_HOMESERVER,
|
|
11
|
+
writeMatrixCredentials,
|
|
12
|
+
} from "./test-helpers.js";
|
|
13
|
+
|
|
14
|
+
function resolveOpsTarget(cfg: OpenClawConfig, env = process.env) {
|
|
15
|
+
return resolveMatrixMigrationAccountTarget({
|
|
16
|
+
cfg,
|
|
17
|
+
env,
|
|
18
|
+
accountId: MATRIX_OPS_ACCOUNT_ID,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("resolveMatrixMigrationAccountTarget", () => {
|
|
23
|
+
it("reuses stored user identity for token-only configs when the access token matches", async () => {
|
|
24
|
+
await withTempHome(async (home) => {
|
|
25
|
+
const stateDir = path.join(home, ".openclaw");
|
|
26
|
+
writeMatrixCredentials(stateDir, {
|
|
27
|
+
accountId: MATRIX_OPS_ACCOUNT_ID,
|
|
28
|
+
deviceId: "DEVICE-OPS",
|
|
29
|
+
accessToken: LOBI_OPS_ACCESS_TOKEN,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const cfg: OpenClawConfig = {
|
|
33
|
+
channels: {
|
|
34
|
+
matrix: {
|
|
35
|
+
accounts: {
|
|
36
|
+
ops: {
|
|
37
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
38
|
+
accessToken: LOBI_OPS_ACCESS_TOKEN,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const target = resolveOpsTarget(cfg);
|
|
46
|
+
|
|
47
|
+
expect(target).not.toBeNull();
|
|
48
|
+
expect(target?.userId).toBe(MATRIX_OPS_USER_ID);
|
|
49
|
+
expect(target?.storedDeviceId).toBe("DEVICE-OPS");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("ignores stored device IDs from stale cached Matrix credentials", async () => {
|
|
54
|
+
await withTempHome(async (home) => {
|
|
55
|
+
const stateDir = path.join(home, ".openclaw");
|
|
56
|
+
writeMatrixCredentials(stateDir, {
|
|
57
|
+
accountId: MATRIX_OPS_ACCOUNT_ID,
|
|
58
|
+
userId: "@old-bot:example.org",
|
|
59
|
+
accessToken: "tok-old",
|
|
60
|
+
deviceId: "DEVICE-OLD",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const cfg: OpenClawConfig = {
|
|
64
|
+
channels: {
|
|
65
|
+
matrix: {
|
|
66
|
+
accounts: {
|
|
67
|
+
ops: {
|
|
68
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
69
|
+
userId: "@new-bot:example.org",
|
|
70
|
+
accessToken: "tok-new",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const target = resolveOpsTarget(cfg);
|
|
78
|
+
|
|
79
|
+
expect(target).not.toBeNull();
|
|
80
|
+
expect(target?.userId).toBe("@new-bot:example.org");
|
|
81
|
+
expect(target?.accessToken).toBe("tok-new");
|
|
82
|
+
expect(target?.storedDeviceId).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("does not trust stale stored creds on the same homeserver when the token changes", async () => {
|
|
87
|
+
await withTempHome(async (home) => {
|
|
88
|
+
const stateDir = path.join(home, ".openclaw");
|
|
89
|
+
writeMatrixCredentials(stateDir, {
|
|
90
|
+
accountId: MATRIX_OPS_ACCOUNT_ID,
|
|
91
|
+
userId: "@old-bot:example.org",
|
|
92
|
+
accessToken: "tok-old",
|
|
93
|
+
deviceId: "DEVICE-OLD",
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const cfg: OpenClawConfig = {
|
|
97
|
+
channels: {
|
|
98
|
+
matrix: {
|
|
99
|
+
accounts: {
|
|
100
|
+
ops: {
|
|
101
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
102
|
+
accessToken: "tok-new",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const target = resolveOpsTarget(cfg);
|
|
110
|
+
|
|
111
|
+
expect(target).toBeNull();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("does not inherit the base userId for non-default token-only accounts", async () => {
|
|
116
|
+
await withTempHome(async (home) => {
|
|
117
|
+
const stateDir = path.join(home, ".openclaw");
|
|
118
|
+
writeMatrixCredentials(stateDir, {
|
|
119
|
+
accountId: MATRIX_OPS_ACCOUNT_ID,
|
|
120
|
+
deviceId: "DEVICE-OPS",
|
|
121
|
+
accessToken: LOBI_OPS_ACCESS_TOKEN,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const cfg: OpenClawConfig = {
|
|
125
|
+
channels: {
|
|
126
|
+
matrix: {
|
|
127
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
128
|
+
userId: "@base-bot:example.org",
|
|
129
|
+
accounts: {
|
|
130
|
+
ops: {
|
|
131
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
132
|
+
accessToken: LOBI_OPS_ACCESS_TOKEN,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const target = resolveOpsTarget(cfg);
|
|
140
|
+
|
|
141
|
+
expect(target).not.toBeNull();
|
|
142
|
+
expect(target?.userId).toBe(MATRIX_OPS_USER_ID);
|
|
143
|
+
expect(target?.storedDeviceId).toBe("DEVICE-OPS");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("does not inherit the base access token for non-default accounts", async () => {
|
|
148
|
+
await withTempHome(async () => {
|
|
149
|
+
const cfg: OpenClawConfig = {
|
|
150
|
+
channels: {
|
|
151
|
+
matrix: {
|
|
152
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
153
|
+
userId: "@base-bot:example.org",
|
|
154
|
+
accessToken: "tok-base",
|
|
155
|
+
accounts: {
|
|
156
|
+
ops: {
|
|
157
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
158
|
+
userId: MATRIX_OPS_USER_ID,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const target = resolveOpsTarget(cfg);
|
|
166
|
+
|
|
167
|
+
expect(target).toBeNull();
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("does not inherit the global Matrix access token for non-default accounts", async () => {
|
|
172
|
+
await withTempHome(
|
|
173
|
+
async () => {
|
|
174
|
+
const cfg: OpenClawConfig = {
|
|
175
|
+
channels: {
|
|
176
|
+
matrix: {
|
|
177
|
+
accounts: {
|
|
178
|
+
ops: {
|
|
179
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
180
|
+
userId: MATRIX_OPS_USER_ID,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const target = resolveOpsTarget(cfg);
|
|
188
|
+
|
|
189
|
+
expect(target).toBeNull();
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
env: {
|
|
193
|
+
LOBI_ACCESS_TOKEN: "tok-global",
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("uses the same scoped env token encoding as runtime account auth", async () => {
|
|
200
|
+
await withTempHome(async () => {
|
|
201
|
+
const cfg: OpenClawConfig = {
|
|
202
|
+
channels: {
|
|
203
|
+
matrix: {
|
|
204
|
+
accounts: {
|
|
205
|
+
"ops-prod": {},
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
const env = {
|
|
211
|
+
MATRIX_OPS_X2D_PROD_HOMESERVER: "https://matrix.example.org",
|
|
212
|
+
MATRIX_OPS_X2D_PROD_USER_ID: "@ops-prod:example.org",
|
|
213
|
+
MATRIX_OPS_X2D_PROD_ACCESS_TOKEN: "tok-ops-prod",
|
|
214
|
+
} as NodeJS.ProcessEnv;
|
|
215
|
+
|
|
216
|
+
const target = resolveMatrixMigrationAccountTarget({
|
|
217
|
+
cfg,
|
|
218
|
+
env,
|
|
219
|
+
accountId: "ops-prod",
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(target).not.toBeNull();
|
|
223
|
+
expect(target?.homeserver).toBe("https://matrix.example.org");
|
|
224
|
+
expect(target?.userId).toBe("@ops-prod:example.org");
|
|
225
|
+
expect(target?.accessToken).toBe("tok-ops-prod");
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
});
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
4
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
|
5
|
+
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
6
|
+
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
|
7
|
+
import {
|
|
8
|
+
findMatrixAccountEntry,
|
|
9
|
+
requiresExplicitMatrixDefaultAccount,
|
|
10
|
+
resolveConfiguredMatrixAccountIds,
|
|
11
|
+
resolveMatrixChannelConfig,
|
|
12
|
+
resolveMatrixDefaultOrOnlyAccountId,
|
|
13
|
+
} from "./account-selection.js";
|
|
14
|
+
import { resolveMatrixAccountStringValues } from "./auth-precedence.js";
|
|
15
|
+
import {
|
|
16
|
+
resolveGlobalMatrixEnvConfig,
|
|
17
|
+
resolveScopedMatrixEnvConfig,
|
|
18
|
+
} from "./matrix/client/env-auth.js";
|
|
19
|
+
import { resolveMatrixAccountStorageRoot, resolveMatrixCredentialsPath } from "./storage-paths.js";
|
|
20
|
+
|
|
21
|
+
export type MatrixStoredCredentials = {
|
|
22
|
+
homeserver: string;
|
|
23
|
+
userId: string;
|
|
24
|
+
accessToken: string;
|
|
25
|
+
deviceId?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type MatrixMigrationAccountTarget = {
|
|
29
|
+
accountId: string;
|
|
30
|
+
homeserver: string;
|
|
31
|
+
userId: string;
|
|
32
|
+
accessToken: string;
|
|
33
|
+
rootDir: string;
|
|
34
|
+
storedDeviceId: string | null;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type MatrixLegacyFlatStoreTarget = MatrixMigrationAccountTarget & {
|
|
38
|
+
selectionNote?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type MatrixLegacyFlatStoreKind = "state" | "encrypted state";
|
|
42
|
+
|
|
43
|
+
function clean(value: unknown): string {
|
|
44
|
+
return normalizeOptionalString(value) ?? "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function resolveMatrixAccountConfigEntry(
|
|
48
|
+
cfg: OpenClawConfig,
|
|
49
|
+
accountId: string,
|
|
50
|
+
): Record<string, unknown> | null {
|
|
51
|
+
return findMatrixAccountEntry(cfg, accountId);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveMatrixFlatStoreSelectionNote(
|
|
55
|
+
cfg: OpenClawConfig,
|
|
56
|
+
accountId: string,
|
|
57
|
+
): string | undefined {
|
|
58
|
+
if (resolveConfiguredMatrixAccountIds(cfg).length <= 1) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
return (
|
|
62
|
+
`Legacy Matrix flat store uses one shared on-disk state, so it will be migrated into ` +
|
|
63
|
+
`account "${accountId}".`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function resolveMatrixMigrationConfigFields(params: {
|
|
68
|
+
cfg: OpenClawConfig;
|
|
69
|
+
env: NodeJS.ProcessEnv;
|
|
70
|
+
accountId: string;
|
|
71
|
+
}): {
|
|
72
|
+
homeserver: string;
|
|
73
|
+
userId: string;
|
|
74
|
+
accessToken: string;
|
|
75
|
+
} {
|
|
76
|
+
const channel = resolveMatrixChannelConfig(params.cfg);
|
|
77
|
+
const account = resolveMatrixAccountConfigEntry(params.cfg, params.accountId);
|
|
78
|
+
const scopedEnv = resolveScopedMatrixEnvConfig(params.accountId, params.env);
|
|
79
|
+
const globalEnv = resolveGlobalMatrixEnvConfig(params.env);
|
|
80
|
+
const normalizedAccountId = normalizeAccountId(params.accountId);
|
|
81
|
+
const resolvedStrings = resolveMatrixAccountStringValues({
|
|
82
|
+
accountId: normalizedAccountId,
|
|
83
|
+
account: {
|
|
84
|
+
homeserver: clean(account?.homeserver),
|
|
85
|
+
userId: clean(account?.userId),
|
|
86
|
+
accessToken: clean(account?.accessToken),
|
|
87
|
+
},
|
|
88
|
+
scopedEnv,
|
|
89
|
+
channel: {
|
|
90
|
+
homeserver: clean(channel?.homeserver),
|
|
91
|
+
userId: clean(channel?.userId),
|
|
92
|
+
accessToken: clean(channel?.accessToken),
|
|
93
|
+
},
|
|
94
|
+
globalEnv,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
homeserver: resolvedStrings.homeserver,
|
|
99
|
+
userId: resolvedStrings.userId,
|
|
100
|
+
accessToken: resolvedStrings.accessToken,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function loadStoredMatrixCredentials(
|
|
105
|
+
env: NodeJS.ProcessEnv,
|
|
106
|
+
accountId: string,
|
|
107
|
+
): MatrixStoredCredentials | null {
|
|
108
|
+
const stateDir = resolveStateDir(env, os.homedir);
|
|
109
|
+
const credentialsPath = resolveMatrixCredentialsPath({
|
|
110
|
+
stateDir,
|
|
111
|
+
accountId: normalizeAccountId(accountId),
|
|
112
|
+
});
|
|
113
|
+
try {
|
|
114
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const parsed = JSON.parse(
|
|
118
|
+
fs.readFileSync(credentialsPath, "utf8"),
|
|
119
|
+
) as Partial<MatrixStoredCredentials>;
|
|
120
|
+
if (
|
|
121
|
+
typeof parsed.homeserver !== "string" ||
|
|
122
|
+
typeof parsed.userId !== "string" ||
|
|
123
|
+
typeof parsed.accessToken !== "string"
|
|
124
|
+
) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
homeserver: parsed.homeserver,
|
|
129
|
+
userId: parsed.userId,
|
|
130
|
+
accessToken: parsed.accessToken,
|
|
131
|
+
deviceId: typeof parsed.deviceId === "string" ? parsed.deviceId : undefined,
|
|
132
|
+
};
|
|
133
|
+
} catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function credentialsMatchResolvedIdentity(
|
|
139
|
+
stored: MatrixStoredCredentials | null,
|
|
140
|
+
identity: {
|
|
141
|
+
homeserver: string;
|
|
142
|
+
userId: string;
|
|
143
|
+
accessToken: string;
|
|
144
|
+
},
|
|
145
|
+
): stored is MatrixStoredCredentials {
|
|
146
|
+
if (!stored || !identity.homeserver) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
if (!identity.userId) {
|
|
150
|
+
if (!identity.accessToken) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
return stored.homeserver === identity.homeserver && stored.accessToken === identity.accessToken;
|
|
154
|
+
}
|
|
155
|
+
return stored.homeserver === identity.homeserver && stored.userId === identity.userId;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function resolveMatrixMigrationAccountTarget(params: {
|
|
159
|
+
cfg: OpenClawConfig;
|
|
160
|
+
env: NodeJS.ProcessEnv;
|
|
161
|
+
accountId: string;
|
|
162
|
+
}): MatrixMigrationAccountTarget | null {
|
|
163
|
+
const stored = loadStoredMatrixCredentials(params.env, params.accountId);
|
|
164
|
+
const resolved = resolveMatrixMigrationConfigFields(params);
|
|
165
|
+
const matchingStored = credentialsMatchResolvedIdentity(stored, {
|
|
166
|
+
homeserver: resolved.homeserver,
|
|
167
|
+
userId: resolved.userId,
|
|
168
|
+
accessToken: resolved.accessToken,
|
|
169
|
+
})
|
|
170
|
+
? stored
|
|
171
|
+
: null;
|
|
172
|
+
const homeserver = resolved.homeserver;
|
|
173
|
+
const userId = resolved.userId || matchingStored?.userId || "";
|
|
174
|
+
const accessToken = resolved.accessToken || matchingStored?.accessToken || "";
|
|
175
|
+
if (!homeserver || !userId || !accessToken) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const stateDir = resolveStateDir(params.env, os.homedir);
|
|
180
|
+
const { rootDir } = resolveMatrixAccountStorageRoot({
|
|
181
|
+
stateDir,
|
|
182
|
+
homeserver,
|
|
183
|
+
userId,
|
|
184
|
+
accessToken,
|
|
185
|
+
accountId: params.accountId,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
accountId: params.accountId,
|
|
190
|
+
homeserver,
|
|
191
|
+
userId,
|
|
192
|
+
accessToken,
|
|
193
|
+
rootDir,
|
|
194
|
+
storedDeviceId: matchingStored?.deviceId ?? null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function resolveLegacyMatrixFlatStoreTarget(params: {
|
|
199
|
+
cfg: OpenClawConfig;
|
|
200
|
+
env: NodeJS.ProcessEnv;
|
|
201
|
+
detectedPath: string;
|
|
202
|
+
detectedKind: MatrixLegacyFlatStoreKind;
|
|
203
|
+
}): MatrixLegacyFlatStoreTarget | { warning: string } {
|
|
204
|
+
const channel = resolveMatrixChannelConfig(params.cfg);
|
|
205
|
+
if (!channel) {
|
|
206
|
+
return {
|
|
207
|
+
warning:
|
|
208
|
+
`Legacy Matrix ${params.detectedKind} detected at ${params.detectedPath}, but channels.lobi is not configured yet. ` +
|
|
209
|
+
'Configure Matrix, then rerun "openclaw doctor --fix" or restart the gateway.',
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (requiresExplicitMatrixDefaultAccount(params.cfg)) {
|
|
213
|
+
return {
|
|
214
|
+
warning:
|
|
215
|
+
`Legacy Matrix ${params.detectedKind} detected at ${params.detectedPath}, but multiple Matrix accounts are configured and channels.lobi.defaultAccount is not set. ` +
|
|
216
|
+
'Set "channels.lobi.defaultAccount" to the intended target account before rerunning "openclaw doctor --fix" or restarting the gateway.',
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const accountId = resolveMatrixDefaultOrOnlyAccountId(params.cfg);
|
|
221
|
+
const target = resolveMatrixMigrationAccountTarget({
|
|
222
|
+
cfg: params.cfg,
|
|
223
|
+
env: params.env,
|
|
224
|
+
accountId,
|
|
225
|
+
});
|
|
226
|
+
if (!target) {
|
|
227
|
+
const targetDescription =
|
|
228
|
+
params.detectedKind === "state"
|
|
229
|
+
? "the new account-scoped target"
|
|
230
|
+
: "the account-scoped target";
|
|
231
|
+
return {
|
|
232
|
+
warning:
|
|
233
|
+
`Legacy Matrix ${params.detectedKind} detected at ${params.detectedPath}, but ${targetDescription} could not be resolved yet ` +
|
|
234
|
+
`(need homeserver, userId, and access token for channels.lobi${accountId === DEFAULT_ACCOUNT_ID ? "" : `.accounts.${accountId}`}). ` +
|
|
235
|
+
'Start the gateway once with a working Matrix login, or rerun "openclaw doctor --fix" after cached credentials are available.',
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
...target,
|
|
241
|
+
selectionNote: resolveMatrixFlatStoreSelectionNote(params.cfg, accountId),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
|
|
5
|
+
import { resolveRequiredHomeDir } from "openclaw/plugin-sdk/provider-auth";
|
|
6
|
+
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
7
|
+
|
|
8
|
+
const MATRIX_MIGRATION_SNAPSHOT_DIRNAME = "openclaw-migrations";
|
|
9
|
+
|
|
10
|
+
type MatrixMigrationSnapshotMarker = {
|
|
11
|
+
version: 1;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
archivePath: string;
|
|
14
|
+
trigger: string;
|
|
15
|
+
includeWorkspace: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type MatrixMigrationSnapshotResult = {
|
|
19
|
+
created: boolean;
|
|
20
|
+
archivePath: string;
|
|
21
|
+
markerPath: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function loadSnapshotMarker(filePath: string): MatrixMigrationSnapshotMarker | null {
|
|
25
|
+
try {
|
|
26
|
+
if (!fs.existsSync(filePath)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const parsed = JSON.parse(
|
|
30
|
+
fs.readFileSync(filePath, "utf8"),
|
|
31
|
+
) as Partial<MatrixMigrationSnapshotMarker>;
|
|
32
|
+
if (
|
|
33
|
+
parsed.version !== 1 ||
|
|
34
|
+
typeof parsed.createdAt !== "string" ||
|
|
35
|
+
typeof parsed.archivePath !== "string" ||
|
|
36
|
+
typeof parsed.trigger !== "string"
|
|
37
|
+
) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
version: 1,
|
|
42
|
+
createdAt: parsed.createdAt,
|
|
43
|
+
archivePath: parsed.archivePath,
|
|
44
|
+
trigger: parsed.trigger,
|
|
45
|
+
includeWorkspace: parsed.includeWorkspace === true,
|
|
46
|
+
};
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resolveMatrixMigrationSnapshotMarkerPath(
|
|
53
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
54
|
+
): string {
|
|
55
|
+
const stateDir = resolveStateDir(env, os.homedir);
|
|
56
|
+
return path.join(stateDir, "matrix", "migration-snapshot.json");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function resolveMatrixMigrationSnapshotOutputDir(
|
|
60
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
61
|
+
): string {
|
|
62
|
+
const homeDir = resolveRequiredHomeDir(env, os.homedir);
|
|
63
|
+
return path.join(homeDir, "Backups", MATRIX_MIGRATION_SNAPSHOT_DIRNAME);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function maybeCreateMatrixMigrationSnapshot(params: {
|
|
67
|
+
trigger: string;
|
|
68
|
+
env?: NodeJS.ProcessEnv;
|
|
69
|
+
outputDir?: string;
|
|
70
|
+
createBackupArchive?: typeof import("openclaw/plugin-sdk/runtime").createBackupArchive;
|
|
71
|
+
log?: { info?: (message: string) => void; warn?: (message: string) => void };
|
|
72
|
+
}): Promise<MatrixMigrationSnapshotResult> {
|
|
73
|
+
const env = params.env ?? process.env;
|
|
74
|
+
const createBackupArchive =
|
|
75
|
+
params.createBackupArchive ?? (await import("openclaw/plugin-sdk/runtime")).createBackupArchive;
|
|
76
|
+
const markerPath = resolveMatrixMigrationSnapshotMarkerPath(env);
|
|
77
|
+
const existingMarker = loadSnapshotMarker(markerPath);
|
|
78
|
+
if (existingMarker?.archivePath && fs.existsSync(existingMarker.archivePath)) {
|
|
79
|
+
params.log?.info?.(
|
|
80
|
+
`matrix: reusing existing pre-migration backup snapshot: ${existingMarker.archivePath}`,
|
|
81
|
+
);
|
|
82
|
+
return {
|
|
83
|
+
created: false,
|
|
84
|
+
archivePath: existingMarker.archivePath,
|
|
85
|
+
markerPath,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (existingMarker?.archivePath && !fs.existsSync(existingMarker.archivePath)) {
|
|
89
|
+
params.log?.warn?.(
|
|
90
|
+
`matrix: previous migration snapshot is missing (${existingMarker.archivePath}); creating a replacement backup before continuing`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const snapshot = await createBackupArchive({
|
|
95
|
+
output: (() => {
|
|
96
|
+
const outputDir = params.outputDir ?? resolveMatrixMigrationSnapshotOutputDir(env);
|
|
97
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
98
|
+
return outputDir;
|
|
99
|
+
})(),
|
|
100
|
+
includeWorkspace: false,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const marker: MatrixMigrationSnapshotMarker = {
|
|
104
|
+
version: 1,
|
|
105
|
+
createdAt: snapshot.createdAt,
|
|
106
|
+
archivePath: snapshot.archivePath,
|
|
107
|
+
trigger: params.trigger,
|
|
108
|
+
includeWorkspace: snapshot.includeWorkspace,
|
|
109
|
+
};
|
|
110
|
+
await writeJsonFileAtomically(markerPath, marker);
|
|
111
|
+
params.log?.info?.(`matrix: created pre-migration backup snapshot: ${snapshot.archivePath}`);
|
|
112
|
+
return {
|
|
113
|
+
created: true,
|
|
114
|
+
archivePath: snapshot.archivePath,
|
|
115
|
+
markerPath,
|
|
116
|
+
};
|
|
117
|
+
}
|