@openclaw/imessage 2026.10.10 → 2026.11.5-beta.1
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/dist/.setup/{accounts-BH0coL46.mjs → accounts-C1Ypv3Wn.mjs} +6 -7
- package/dist/.setup/{actions-CW7XYYF4.mjs → actions-qOuKy8To.mjs} +360 -108
- package/dist/.setup/{actions.runtime-B2ZHQ3TJ.mjs → actions.runtime-Ccg9CARL.mjs} +10 -56
- package/dist/.setup/{api-6xi-NBrt.mjs → api-BXXuQQCS.mjs} +7 -7
- package/dist/.setup/{api-B0ih8Iof.mjs → api-DTbxSOPc.mjs} +8 -8
- package/dist/.setup/{approval-handler.runtime-BKmqba3L.mjs → approval-handler.runtime-B6vrFfZx.mjs} +112 -131
- package/dist/.setup/{approval-reactions-B9CJS0NT.mjs → approval-reactions-DBBsqbRC.mjs} +152 -72
- package/dist/.setup/{channel-6kcKpSRw.mjs → channel-CLaN4mQn.mjs} +142 -119
- package/dist/.setup/{channel.runtime-BW4A0m4h.mjs → channel.runtime-XJGx1MIH.mjs} +205 -363
- package/dist/.setup/chat-context-fTfJVqAi.mjs +77 -0
- package/dist/.setup/chat-lookup-xtPsOBkl.mjs +50 -0
- package/dist/.setup/client-D_BaG8sC.mjs +367 -0
- package/dist/.setup/{config-schema-DRkdRC21.mjs → config-schema-BjkQV5P5.mjs} +1 -2
- package/dist/.setup/{conversation-bindings-vZFn6sN2.mjs → conversation-bindings-8DjxoIYU.mjs} +9 -6
- package/dist/.setup/{media-contract-CoixpXRX.mjs → media-contract-BErFBKV9.mjs} +1 -1
- package/dist/.setup/{message-tool-api-CxSY_rh7.mjs → message-tool-api-DtZ1wKbT.mjs} +3 -6
- package/dist/.setup/{remote-file-CCgMYZ-u.mjs → remote-file-DpK3GQFD.mjs} +8 -22
- package/dist/.setup/{sanitize-outbound-C02fJrn6.mjs → sanitize-outbound-Dhjhz0a-.mjs} +23 -27
- package/dist/.setup/{send-D98hQFdv.mjs → send-DhEW0gfe.mjs} +150 -226
- package/dist/.setup/{setup-core-BWnW0czE.mjs → setup-core-B1_P_t4T.mjs} +2 -2
- package/dist/.setup/{setup-surface-aRZhAzzQ.mjs → setup-surface-CD28mXWK.mjs} +11 -25
- package/dist/.setup/{targets-CIx4BKqL.mjs → targets-C7CIWJ0k.mjs} +12 -3
- package/dist/.source-module-urls/929c518fa2986b0f85f66bceefead22868db0ab452dee914dc6a2bb86b2e60ad.json +1 -1
- package/dist/api.js +8 -8
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/contract-api.js +1 -1
- package/dist/media-contract-api.js +1 -1
- package/dist/message-tool-api.js +1 -1
- package/dist/runtime-api.js +1 -3
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-entry.js +1 -1
- package/dist/src/chat-db.worker.js +1 -1
- package/node_modules/typebox/build/format/duration.d.mts +2 -2
- package/node_modules/typebox/build/format/duration.mjs +2 -2
- package/node_modules/typebox/build/format/idn_email.d.mts +1 -0
- package/node_modules/typebox/build/format/idn_email.mjs +1 -0
- package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -1
- package/node_modules/typebox/build/format/idn_hostname.mjs +1 -1
- package/node_modules/typebox/build/format/ipv4.d.mts +2 -2
- package/node_modules/typebox/build/format/ipv4.mjs +2 -2
- package/node_modules/typebox/build/format/ipv6.d.mts +1 -1
- package/node_modules/typebox/build/format/ipv6.mjs +1 -1
- package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
- package/node_modules/typebox/build/schema/build.mjs +1 -0
- package/node_modules/typebox/build/schema/engine/_unique.d.mts +4 -2
- package/node_modules/typebox/build/schema/engine/_unique.mjs +7 -3
- package/node_modules/typebox/build/schema/engine/additionalItems.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +0 -3
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +15 -17
- package/node_modules/typebox/build/schema/engine/contains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/index.d.mts +1 -0
- package/node_modules/typebox/build/schema/engine/index.mjs +1 -0
- package/node_modules/typebox/build/schema/engine/items.mjs +7 -4
- package/node_modules/typebox/build/schema/engine/maxContains.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/minContains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/oneOf.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +2 -2
- package/node_modules/typebox/build/system/hashing/hash.mjs +63 -34
- package/node_modules/typebox/build/system/locale/en_US.mjs +2 -1
- package/node_modules/typebox/build/system/memory/assign.d.mts +2 -8
- package/node_modules/typebox/build/system/memory/assign.mjs +13 -6
- package/node_modules/typebox/build/system/memory/clone.d.mts +4 -5
- package/node_modules/typebox/build/system/memory/clone.mjs +8 -96
- package/node_modules/typebox/build/system/memory/create.d.mts +1 -5
- package/node_modules/typebox/build/system/memory/create.mjs +18 -13
- package/node_modules/typebox/build/system/memory/deep.d.mts +2 -0
- package/node_modules/typebox/build/system/memory/deep.mjs +108 -0
- package/node_modules/typebox/build/system/memory/discard.d.mts +1 -1
- package/node_modules/typebox/build/system/memory/discard.mjs +9 -8
- package/node_modules/typebox/build/system/memory/finalize.d.mts +4 -0
- package/node_modules/typebox/build/system/memory/finalize.mjs +30 -0
- package/node_modules/typebox/build/system/memory/keys.d.mts +2 -0
- package/node_modules/typebox/build/system/memory/keys.mjs +8 -0
- package/node_modules/typebox/build/system/memory/update.d.mts +1 -4
- package/node_modules/typebox/build/system/memory/update.mjs +43 -16
- package/node_modules/typebox/build/type/engine/call/index.d.mts +1 -0
- package/node_modules/typebox/build/type/engine/call/index.mjs +1 -0
- package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +2 -24
- package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +2 -29
- package/node_modules/typebox/build/type/engine/evaluate/index.d.mts +1 -0
- package/node_modules/typebox/build/type/engine/evaluate/index.mjs +1 -0
- package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +5 -1
- package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +6 -1
- package/node_modules/typebox/build/type/engine/evaluate/property.d.mts +30 -0
- package/node_modules/typebox/build/type/engine/evaluate/property.mjs +29 -0
- package/node_modules/typebox/build/type/engine/immutable/instantiate_add.d.mts +1 -3
- package/node_modules/typebox/build/type/engine/immutable/instantiate_add.mjs +1 -4
- package/node_modules/typebox/build/type/engine/object/from_intersect.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/object/from_intersect.mjs +2 -2
- package/node_modules/typebox/build/type/engine/object/from_union.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/object/from_union.mjs +2 -2
- package/node_modules/typebox/build/type/engine/optional/instantiate_add.d.mts +1 -3
- package/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs +1 -4
- package/node_modules/typebox/build/type/engine/readonly/instantiate_add.d.mts +1 -3
- package/node_modules/typebox/build/type/engine/readonly/instantiate_add.mjs +1 -4
- package/node_modules/typebox/build/type/engine/this/index.d.mts +1 -0
- package/node_modules/typebox/build/type/engine/this/index.mjs +1 -0
- package/node_modules/typebox/build/value/clone/clone.d.mts +4 -3
- package/node_modules/typebox/build/value/clone/clone.mjs +5 -7
- package/node_modules/typebox/build/value/repair/from_cyclic.d.mts +2 -0
- package/node_modules/typebox/build/value/repair/from_cyclic.mjs +6 -0
- package/node_modules/typebox/build/value/shared/union_priority_sort.mjs +25 -14
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +1 -1
- package/node_modules/zod/package.json +1 -1
- package/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
- package/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
- package/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
- package/node_modules/zod/src/v4/core/compile.ts +9 -0
- package/node_modules/zod/src/v4/core/schemas.ts +35 -1
- package/node_modules/zod/src/v4/core/util.ts +13 -0
- package/node_modules/zod/src/v4/core/versions.ts +1 -1
- package/node_modules/zod/v4/core/compile.cjs +8 -0
- package/node_modules/zod/v4/core/compile.js +8 -0
- package/node_modules/zod/v4/core/schemas.cjs +30 -0
- package/node_modules/zod/v4/core/schemas.d.cts +3 -1
- package/node_modules/zod/v4/core/schemas.d.ts +3 -1
- package/node_modules/zod/v4/core/schemas.js +30 -0
- package/node_modules/zod/v4/core/util.cjs +15 -0
- package/node_modules/zod/v4/core/util.d.cts +2 -0
- package/node_modules/zod/v4/core/util.d.ts +2 -0
- package/node_modules/zod/v4/core/util.js +14 -0
- package/node_modules/zod/v4/core/versions.cjs +1 -1
- package/node_modules/zod/v4/core/versions.js +1 -1
- package/openclaw.plugin.json +0 -30
- package/package.json +6 -6
- package/dist/.setup/client-C3e7ZLqA.mjs +0 -739
- package/dist/.setup/question-reactions-DTj-VH3N.mjs +0 -67
- package/dist/.setup/reaction-context-Bw00FkmU.mjs +0 -105
- package/dist/.setup/runtime-BRQCPuAD.mjs +0 -7
- package/node_modules/typebox/build/system/memory/freeze.d.mts +0 -4
- package/node_modules/typebox/build/system/memory/freeze.mjs +0 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
2
|
import { normalizeAccountId, resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
|
|
3
3
|
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
|
4
|
-
import { accessSync, constants, readFileSync,
|
|
4
|
+
import { accessSync, constants, readFileSync, statSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { createAccountListHelpers } from "openclaw/plugin-sdk/account-helpers";
|
|
7
7
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
@@ -49,7 +49,7 @@ function resolveIMessageExecutable(cliPath) {
|
|
|
49
49
|
}
|
|
50
50
|
function isMachOExecutable(filePath) {
|
|
51
51
|
try {
|
|
52
|
-
return MACH_O_MAGICS.has(readFileSync(
|
|
52
|
+
return MACH_O_MAGICS.has(readFileSync(filePath).subarray(0, 4).toString("hex"));
|
|
53
53
|
} catch {
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
@@ -63,7 +63,7 @@ function isProvenLocalIMessageCliPath(params) {
|
|
|
63
63
|
const executable = resolveIMessageExecutable(cliPath);
|
|
64
64
|
let local = executable ? isMachOExecutable(executable) : false;
|
|
65
65
|
if (executable && !local) try {
|
|
66
|
-
const match = readFileSync(
|
|
66
|
+
const match = readFileSync(executable, "utf8").match(/^#![^\r\n]+\r?\n\s*exec\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s+"\$@"\s*$/u);
|
|
67
67
|
const target = match?.[1] ?? match?.[2] ?? match?.[3];
|
|
68
68
|
local = Boolean(target && path.isAbsolute(target) && isMachOExecutable(target));
|
|
69
69
|
} catch {
|
|
@@ -148,8 +148,8 @@ async function resolveIMessageRemoteHost(params) {
|
|
|
148
148
|
const { logVerbose } = await import("openclaw/plugin-sdk/runtime-env");
|
|
149
149
|
if (params.remoteHost?.trim()) logVerbose("imessage: ignoring unsafe configured remoteHost value");
|
|
150
150
|
const key = cacheKey(params.cliPath);
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
const cached = detectedRemoteHosts.get(key);
|
|
152
|
+
if (cached !== void 0) {
|
|
153
153
|
if (cached === ambiguousSshWrapper) throwAmbiguousSshWrapper();
|
|
154
154
|
return cached ?? void 0;
|
|
155
155
|
}
|
|
@@ -171,7 +171,6 @@ async function resolveIMessageRemoteHost(params) {
|
|
|
171
171
|
detectedRemoteHosts.set(key, ambiguousSshWrapper);
|
|
172
172
|
throwAmbiguousSshWrapper();
|
|
173
173
|
}
|
|
174
|
-
if (detected) logVerbose(`imessage: detected remoteHost=${detected} from cliPath`);
|
|
175
174
|
detectedRemoteHosts.set(key, detected ?? null);
|
|
176
175
|
return detected;
|
|
177
176
|
})().finally(() => remoteHostLookups.delete(key));
|
|
@@ -211,7 +210,7 @@ function resolveIMessageAccount(params) {
|
|
|
211
210
|
const baseEnabled = params.cfg.channels?.imessage?.enabled !== false;
|
|
212
211
|
const merged = mergeIMessageAccountConfig(params.cfg, accountId);
|
|
213
212
|
const accountEnabled = merged.enabled !== false;
|
|
214
|
-
const configured = Boolean(merged.enabled === true || merged.cliPath?.trim() || merged.dbPath?.trim() || merged.service || merged.sendTransport || merged.region?.trim() || merged.allowFrom && merged.allowFrom.length > 0 || merged.groupAllowFrom && merged.groupAllowFrom.length > 0 || merged.dmPolicy || merged.groupPolicy ||
|
|
213
|
+
const configured = Boolean(merged.enabled === true || merged.cliPath?.trim() || merged.dbPath?.trim() || merged.service || merged.sendTransport || merged.region?.trim() || merged.allowFrom && merged.allowFrom.length > 0 || merged.groupAllowFrom && merged.groupAllowFrom.length > 0 || merged.dmPolicy || merged.groupPolicy || merged.includeAttachments !== void 0 || merged.attachmentRoots && merged.attachmentRoots.length > 0 || merged.remoteAttachmentRoots && merged.remoteAttachmentRoots.length > 0 || merged.mediaMaxMb !== void 0 || merged.textChunkLimit !== void 0 || merged.groups && Object.keys(merged.groups).length > 0);
|
|
215
214
|
return {
|
|
216
215
|
accountId,
|
|
217
216
|
enabled: baseEnabled && accountEnabled,
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { l as resolveIMessageRemoteHost, n as hasExclusiveIMessageLocalDatabase, o as resolveIMessageAccount, u as expandIMessageUserPath } from "./accounts-
|
|
3
|
-
import { a as setCachedIMessagePrivateApiStatus, n as getCachedIMessagePrivateApiStatus, o as IMESSAGE_ACTIONS, r as imessageRpcSupportsMethod, s as IMESSAGE_ACTION_NAMES, t as describeIMessageMessageTool } from "./message-tool-api-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { l as parseIMessageTarget } from "./targets-C7CIWJ0k.mjs";
|
|
2
|
+
import { l as resolveIMessageRemoteHost, n as hasExclusiveIMessageLocalDatabase, o as resolveIMessageAccount, u as expandIMessageUserPath } from "./accounts-C1Ypv3Wn.mjs";
|
|
3
|
+
import { a as setCachedIMessagePrivateApiStatus, n as getCachedIMessagePrivateApiStatus, o as IMESSAGE_ACTIONS, r as imessageRpcSupportsMethod, s as IMESSAGE_ACTION_NAMES, t as describeIMessageMessageTool } from "./message-tool-api-DtZ1wKbT.mjs";
|
|
4
|
+
import { i as resolveIMessageChatMatch, t as chatContextFromIMessageTarget } from "./chat-context-fTfJVqAi.mjs";
|
|
5
|
+
import { n as createIMessageRpcClient } from "./client-D_BaG8sC.mjs";
|
|
6
|
+
import { l as isAutoManagedIMessageCliPath, n as IMESSAGE_UPDATE_COMMAND, t as IMESSAGE_INSTALL_COMMAND } from "./setup-core-B1_P_t4T.mjs";
|
|
7
|
+
import { filterStringEntries, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
8
|
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
|
8
9
|
import path from "node:path";
|
|
9
10
|
import { readBooleanParam } from "openclaw/plugin-sdk/boolean-param";
|
|
10
11
|
import { createActionGate, jsonResult, readNonNegativeIntegerParam, readPositiveIntegerParam, readReactionParams, readStringArrayParam, readStringParam } from "openclaw/plugin-sdk/channel-actions";
|
|
11
12
|
import { canonicalizeBase64 } from "openclaw/plugin-sdk/media-runtime";
|
|
12
13
|
import { normalizePollInput } from "openclaw/plugin-sdk/poll-runtime";
|
|
13
|
-
import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
|
|
14
|
+
import { createSubsystemLogger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
14
15
|
import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
|
|
15
|
-
import {
|
|
16
|
+
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
17
|
+
import { createHash } from "node:crypto";
|
|
16
18
|
import { runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
|
|
17
19
|
import { captureRuntimeConfigAsyncReader } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
18
20
|
import { detectBinary } from "openclaw/plugin-sdk/setup";
|
|
@@ -20,7 +22,6 @@ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
|
20
22
|
// extensions/imessage/src/message-action-reference.ts
|
|
21
23
|
async function resolveAuthorizedIMessageActionReference(params) {
|
|
22
24
|
const options = {
|
|
23
|
-
requireKnownShortId: true,
|
|
24
25
|
chatContext: params.inputChatContext,
|
|
25
26
|
...params.requireFromMe ? { requireFromMe: true } : {}
|
|
26
27
|
};
|
|
@@ -34,10 +35,7 @@ async function resolveAuthorizedIMessageActionReference(params) {
|
|
|
34
35
|
await authorize(messageId, params.inputChatContext);
|
|
35
36
|
const chatGuid = await params.resolveChatGuid();
|
|
36
37
|
const chatContext = { chatGuid };
|
|
37
|
-
const resolvedMessageId = await params.resolveMessageId(messageId, {
|
|
38
|
-
requireKnownShortId: true,
|
|
39
|
-
chatContext
|
|
40
|
-
});
|
|
38
|
+
const resolvedMessageId = await params.resolveMessageId(messageId, { chatContext });
|
|
41
39
|
await authorize(resolvedMessageId, chatContext);
|
|
42
40
|
return {
|
|
43
41
|
messageId: resolvedMessageId,
|
|
@@ -54,31 +52,312 @@ function normalizeIMessageMessageId(value) {
|
|
|
54
52
|
const id = value?.trim();
|
|
55
53
|
return id && id !== "ok" && id !== "unknown" ? id : void 0;
|
|
56
54
|
}
|
|
55
|
+
// extensions/imessage/src/persistence-failure.ts
|
|
56
|
+
function createPersistenceFailureReporter(cache) {
|
|
57
|
+
let reported = false;
|
|
58
|
+
return (scope, err) => {
|
|
59
|
+
if (reported) return;
|
|
60
|
+
reported = true;
|
|
61
|
+
logVerbose(`imessage ${cache}: ${scope} disabled after first failure: ${String(err)}`);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// extensions/imessage/src/runtime.ts
|
|
65
|
+
const { getRuntime: getIMessageRuntime, setRuntime: setIMessageRuntime, tryGetRuntime: getOptionalIMessageRuntime } = createPluginRuntimeStore({
|
|
66
|
+
pluginId: "imessage",
|
|
67
|
+
errorMessage: "iMessage runtime not initialized"
|
|
68
|
+
});
|
|
69
|
+
// extensions/imessage/src/state-contract.ts
|
|
70
|
+
const IMESSAGE_REPLY_CACHE_NAMESPACE = "imessage.reply-cache";
|
|
71
|
+
const IMESSAGE_REPLY_CACHE_MAX_ENTRIES = 2e3;
|
|
72
|
+
const IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE = "imessage.reply-cache-counter";
|
|
73
|
+
const IMESSAGE_REPLY_CACHE_COUNTER_KEY = "short-id-counter";
|
|
74
|
+
const IMESSAGE_REPLY_CACHE_TTL_MS = 216e5;
|
|
75
|
+
function resolveIMessageReplyCacheEntryKey(messageId) {
|
|
76
|
+
return createHash("sha256").update(messageId, "utf8").digest("hex").slice(0, 32);
|
|
77
|
+
}
|
|
78
|
+
const MAX_FAILURE_RETRY_MAP_SIZE = 512;
|
|
79
|
+
const textEncoder = new TextEncoder();
|
|
80
|
+
const IMESSAGE_CATCHUP_CURSOR_NAMESPACE = "imessage.catchup-cursors";
|
|
81
|
+
function resolveIMessageCatchupCursorKey(accountId) {
|
|
82
|
+
return createHash("sha256").update(accountId, "utf8").digest("hex").slice(0, 32);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Bound the retry map so a pathological storm of unique failing GUIDs
|
|
86
|
+
* cannot grow the cursor value without limit. Keeps the `maxSize` entries
|
|
87
|
+
* with the highest counts (closest to give-up) when over the bound.
|
|
88
|
+
*/
|
|
89
|
+
function capFailureRetriesMap(map, maxSize = MAX_FAILURE_RETRY_MAP_SIZE) {
|
|
90
|
+
const maxBytes = 48e3;
|
|
91
|
+
const entries = Object.entries(map);
|
|
92
|
+
if (entries.length <= maxSize && textEncoder.encode(JSON.stringify(map)).byteLength <= maxBytes) return map;
|
|
93
|
+
entries.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
94
|
+
const capped = {};
|
|
95
|
+
for (const [guid, count] of entries.slice(0, maxSize)) {
|
|
96
|
+
capped[guid] = count;
|
|
97
|
+
if (textEncoder.encode(JSON.stringify(capped)).byteLength > maxBytes) {
|
|
98
|
+
delete capped[guid];
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return capped;
|
|
103
|
+
}
|
|
104
|
+
const IMESSAGE_SENT_ECHOES_TTL_MS = 432e5;
|
|
105
|
+
const IMESSAGE_SENT_ECHOES_NAMESPACE = "imessage.sent-echoes";
|
|
106
|
+
function resolveIMessageSentEchoEntryKey(entry) {
|
|
107
|
+
return createHash("sha256").update(JSON.stringify([
|
|
108
|
+
entry.scope,
|
|
109
|
+
entry.text ?? "",
|
|
110
|
+
resolveIMessageEchoMediaKey(entry.media) ?? "",
|
|
111
|
+
entry.messageId ?? "",
|
|
112
|
+
entry.timestamp
|
|
113
|
+
])).digest("hex").slice(0, 32);
|
|
114
|
+
}
|
|
115
|
+
function resolveIMessageEchoMediaKey(media) {
|
|
116
|
+
const contentType = media?.contentType?.trim().toLowerCase() || void 0;
|
|
117
|
+
const kind = media?.kind && media.kind !== "unknown" ? media.kind : void 0;
|
|
118
|
+
if (kind) return `kind:${kind}`;
|
|
119
|
+
const normalizedContentType = contentType?.split(";", 1)[0]?.trim();
|
|
120
|
+
return normalizedContentType ? `mime:${normalizedContentType}` : void 0;
|
|
121
|
+
}
|
|
122
|
+
// extensions/imessage/src/monitor-reply-cache.ts
|
|
123
|
+
/** Recency window for the "react to the latest message" fallback. */
|
|
124
|
+
const LATEST_FALLBACK_MS = 6e5;
|
|
125
|
+
const reportPersistenceFailure = createPersistenceFailureReporter("reply-cache");
|
|
126
|
+
const imessageReplyCacheByMessageId = /* @__PURE__ */ new Map();
|
|
127
|
+
const imessageShortIdToUuid = /* @__PURE__ */ new Map();
|
|
128
|
+
let imessageShortIdCounter = 0;
|
|
129
|
+
function openReplyCacheStore() {
|
|
130
|
+
return getIMessageRuntime().state.openKeyedStoreV2({
|
|
131
|
+
namespace: IMESSAGE_REPLY_CACHE_NAMESPACE,
|
|
132
|
+
maxEntries: IMESSAGE_REPLY_CACHE_MAX_ENTRIES
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function openReplyCacheCounterStore() {
|
|
136
|
+
return getIMessageRuntime().state.openKeyedStoreV2({
|
|
137
|
+
namespace: IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE,
|
|
138
|
+
maxEntries: 1
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function remainingTtlMs(timestamp) {
|
|
142
|
+
const remaining = IMESSAGE_REPLY_CACHE_TTL_MS - Math.max(0, Date.now() - timestamp);
|
|
143
|
+
return remaining > 0 ? remaining : void 0;
|
|
144
|
+
}
|
|
145
|
+
let hydrated = false;
|
|
146
|
+
let hydration;
|
|
147
|
+
let persistence = Promise.resolve();
|
|
148
|
+
function hydrateCounter(counter) {
|
|
149
|
+
if (counter && Number.isSafeInteger(counter.counter) && counter.counter > 0) imessageShortIdCounter = Math.max(imessageShortIdCounter, counter.counter);
|
|
150
|
+
}
|
|
151
|
+
function hydrateRows(entries) {
|
|
152
|
+
const cutoff = Date.now() - IMESSAGE_REPLY_CACHE_TTL_MS;
|
|
153
|
+
for (const entry of entries.filter((cached) => cached.timestamp >= cutoff).toSorted((a, b) => a.timestamp - b.timestamp).slice(-2e3)) {
|
|
154
|
+
const numeric = Number.parseInt(entry.shortId, 10);
|
|
155
|
+
if (Number.isFinite(numeric) && numeric > imessageShortIdCounter) imessageShortIdCounter = numeric;
|
|
156
|
+
imessageReplyCacheByMessageId.set(entry.messageId, entry);
|
|
157
|
+
imessageShortIdToUuid.set(entry.shortId, entry.messageId);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function hydrateFromStoreOnce() {
|
|
161
|
+
if (hydrated) return;
|
|
162
|
+
hydration ??= (async () => {
|
|
163
|
+
try {
|
|
164
|
+
hydrateCounter(await openReplyCacheCounterStore().lookup(IMESSAGE_REPLY_CACHE_COUNTER_KEY));
|
|
165
|
+
hydrateRows((await openReplyCacheStore().entries()).map(({ value }) => value));
|
|
166
|
+
} catch (err) {
|
|
167
|
+
reportPersistenceFailure("read", err);
|
|
168
|
+
} finally {
|
|
169
|
+
hydrated = true;
|
|
170
|
+
}
|
|
171
|
+
})();
|
|
172
|
+
await hydration;
|
|
173
|
+
}
|
|
174
|
+
async function persistReplyCacheEntry(entry) {
|
|
175
|
+
const ttlMs = remainingTtlMs(entry.timestamp);
|
|
176
|
+
if (!ttlMs) return;
|
|
177
|
+
try {
|
|
178
|
+
await openReplyCacheStore().register(resolveIMessageReplyCacheEntryKey(entry.messageId), entry, { ttlMs });
|
|
179
|
+
} catch (err) {
|
|
180
|
+
reportPersistenceFailure("write", err);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function deleteReplyCacheEntry(messageId) {
|
|
184
|
+
try {
|
|
185
|
+
await openReplyCacheStore().delete(resolveIMessageReplyCacheEntryKey(messageId));
|
|
186
|
+
} catch (err) {
|
|
187
|
+
reportPersistenceFailure("delete", err);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function persistReplyCacheCounter(counter) {
|
|
191
|
+
try {
|
|
192
|
+
await openReplyCacheCounterStore().register(IMESSAGE_REPLY_CACHE_COUNTER_KEY, { counter });
|
|
193
|
+
} catch (err) {
|
|
194
|
+
reportPersistenceFailure("counter", err);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function buildReplyCacheEntry(entry, messageId, shortId) {
|
|
198
|
+
return {
|
|
199
|
+
accountId: entry.accountId,
|
|
200
|
+
messageId,
|
|
201
|
+
shortId,
|
|
202
|
+
timestamp: entry.timestamp,
|
|
203
|
+
...entry.chatGuid !== void 0 ? { chatGuid: entry.chatGuid } : {},
|
|
204
|
+
...entry.chatIdentifier !== void 0 ? { chatIdentifier: entry.chatIdentifier } : {},
|
|
205
|
+
...entry.chatId !== void 0 ? { chatId: entry.chatId } : {},
|
|
206
|
+
...entry.isFromMe !== void 0 ? { isFromMe: entry.isFromMe } : {}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
async function rememberIMessageReplyCache(entry) {
|
|
210
|
+
await hydrateFromStoreOnce();
|
|
211
|
+
const messageId = entry.messageId.trim();
|
|
212
|
+
if (!messageId) return {
|
|
213
|
+
...entry,
|
|
214
|
+
shortId: ""
|
|
215
|
+
};
|
|
216
|
+
let shortId = imessageReplyCacheByMessageId.get(messageId)?.shortId;
|
|
217
|
+
const isNewMessage = !shortId;
|
|
218
|
+
if (!shortId) {
|
|
219
|
+
shortId = String(++imessageShortIdCounter);
|
|
220
|
+
imessageShortIdToUuid.set(shortId, messageId);
|
|
221
|
+
}
|
|
222
|
+
const fullEntry = buildReplyCacheEntry(entry, messageId, shortId);
|
|
223
|
+
imessageReplyCacheByMessageId.delete(messageId);
|
|
224
|
+
imessageReplyCacheByMessageId.set(messageId, fullEntry);
|
|
225
|
+
const cutoff = Date.now() - IMESSAGE_REPLY_CACHE_TTL_MS;
|
|
226
|
+
const deletedMessageIds = [];
|
|
227
|
+
const pruneUntil = (retain) => {
|
|
228
|
+
for (const [key, cached] of imessageReplyCacheByMessageId) {
|
|
229
|
+
if (retain(key, cached)) break;
|
|
230
|
+
imessageReplyCacheByMessageId.delete(key);
|
|
231
|
+
deletedMessageIds.push(key);
|
|
232
|
+
if (cached.shortId) imessageShortIdToUuid.delete(cached.shortId);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
pruneUntil((_key, cached) => cached.timestamp >= cutoff);
|
|
236
|
+
pruneUntil((key) => !key || imessageReplyCacheByMessageId.size <= 2e3);
|
|
237
|
+
const counter = imessageShortIdCounter;
|
|
238
|
+
persistence = persistence.then(async () => {
|
|
239
|
+
if (isNewMessage) await persistReplyCacheCounter(counter);
|
|
240
|
+
for (const messageIdToDelete of deletedMessageIds) await deleteReplyCacheEntry(messageIdToDelete);
|
|
241
|
+
await persistReplyCacheEntry(fullEntry);
|
|
242
|
+
});
|
|
243
|
+
await persistence;
|
|
244
|
+
return fullEntry;
|
|
245
|
+
}
|
|
246
|
+
function hasChatScope(ctx) {
|
|
247
|
+
if (!ctx) return false;
|
|
248
|
+
return Boolean(normalizeOptionalString(ctx.chatGuid) || normalizeOptionalString(ctx.chatIdentifier) || typeof ctx.chatId === "number");
|
|
249
|
+
}
|
|
250
|
+
function describeChatForError(values) {
|
|
251
|
+
const parts = [];
|
|
252
|
+
if (normalizeOptionalString(values.chatGuid)) parts.push("chatGuid=<redacted>");
|
|
253
|
+
if (normalizeOptionalString(values.chatIdentifier)) parts.push("chatIdentifier=<redacted>");
|
|
254
|
+
if (typeof values.chatId === "number") parts.push("chatId=<redacted>");
|
|
255
|
+
return parts.length === 0 ? "<unknown chat>" : parts.join(", ");
|
|
256
|
+
}
|
|
257
|
+
function describeMessageIdForError(inputId, inputKind) {
|
|
258
|
+
if (inputKind === "short") return `<short:${inputId.length}-digit>`;
|
|
259
|
+
return `<uuid:${inputId.slice(0, 8)}...>`;
|
|
260
|
+
}
|
|
261
|
+
function buildCrossChatError(inputId, inputKind, cached, ctx) {
|
|
262
|
+
const remediation = inputKind === "short" ? "Use a message ID from the current chat target; MessageSidFull from another chat is rejected." : "Retry with the correct chat target.";
|
|
263
|
+
return /* @__PURE__ */ new Error(`iMessage message id ${describeMessageIdForError(inputId, inputKind)} belongs to a different chat (${describeChatForError(cached)}) than the current call target (${describeChatForError(ctx)}). ${remediation}`);
|
|
264
|
+
}
|
|
265
|
+
async function resolveIMessageMessageId(shortOrUuid, opts) {
|
|
266
|
+
const trimmed = shortOrUuid.trim();
|
|
267
|
+
if (!trimmed) return trimmed;
|
|
268
|
+
await hydrateFromStoreOnce();
|
|
269
|
+
const inputKind = /^\d+$/.test(trimmed) ? "short" : "uuid";
|
|
270
|
+
const messageId = inputKind === "short" ? imessageShortIdToUuid.get(trimmed) : trimmed;
|
|
271
|
+
if (!messageId) {
|
|
272
|
+
if (!hasChatScope(opts?.chatContext)) throw new Error(`iMessage short message id ${describeMessageIdForError(trimmed, "short")} requires a chat scope (chatGuid / chatIdentifier / chatId or a target).`);
|
|
273
|
+
throw new Error(`iMessage short message id ${describeMessageIdForError(trimmed, "short")} is no longer available. Use MessageSidFull.`);
|
|
274
|
+
}
|
|
275
|
+
const cached = imessageReplyCacheByMessageId.get(messageId);
|
|
276
|
+
if (cached && opts?.chatContext && (inputKind === "uuid" || hasChatScope(opts.chatContext)) && resolveIMessageChatMatch(cached, opts.chatContext) === "mismatch") throw buildCrossChatError(trimmed, inputKind, cached, opts.chatContext);
|
|
277
|
+
if (opts?.requireFromMe && cached?.isFromMe !== true) throw buildFromMeError(trimmed, inputKind);
|
|
278
|
+
return messageId;
|
|
279
|
+
}
|
|
280
|
+
async function isKnownFromMeIMessageMessageId(messageId, ctx) {
|
|
281
|
+
const trimmed = normalizeOptionalString(messageId);
|
|
282
|
+
if (!trimmed || !ctx.accountId || !hasChatScope(ctx)) return false;
|
|
283
|
+
await hydrateFromStoreOnce();
|
|
284
|
+
const cached = imessageReplyCacheByMessageId.get(trimmed);
|
|
285
|
+
if (!cached || cached.isFromMe !== true) return false;
|
|
286
|
+
return resolveCachedResourceBinding(trimmed, {
|
|
287
|
+
...ctx,
|
|
288
|
+
accountId: ctx.accountId
|
|
289
|
+
}) === "match";
|
|
290
|
+
}
|
|
291
|
+
async function isKnownFromMeIMessageTarget(params) {
|
|
292
|
+
const { accountId, chatId, chatGuid, chatIdentifier } = params;
|
|
293
|
+
const ctx = {
|
|
294
|
+
accountId,
|
|
295
|
+
chatId,
|
|
296
|
+
chatGuid,
|
|
297
|
+
chatIdentifier
|
|
298
|
+
};
|
|
299
|
+
for (const messageId of params.messageIds) if (await isKnownFromMeIMessageMessageId(messageId, ctx)) return true;
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
function buildFromMeError(inputId, inputKind) {
|
|
303
|
+
return /* @__PURE__ */ new Error(`iMessage message id ${describeMessageIdForError(inputId, inputKind)} is not one this agent sent. edit and unsend can only target messages the gateway delivered itself; messages received from other participants cannot be modified.`);
|
|
304
|
+
}
|
|
305
|
+
/** Latest recent entry with a positive same-account conversation match; never guess a chat. */
|
|
306
|
+
function findLatestIMessageEntryForChat(ctx) {
|
|
307
|
+
if (!hasChatScope(ctx) || !ctx.accountId) return;
|
|
308
|
+
const cutoff = Date.now() - LATEST_FALLBACK_MS;
|
|
309
|
+
let best;
|
|
310
|
+
for (const entry of imessageReplyCacheByMessageId.values()) {
|
|
311
|
+
if (entry.accountId !== ctx.accountId) continue;
|
|
312
|
+
if (entry.timestamp < cutoff) continue;
|
|
313
|
+
if (resolveIMessageChatMatch(entry, ctx) !== "match") continue;
|
|
314
|
+
if (!best || entry.timestamp > best.timestamp) best = entry;
|
|
315
|
+
}
|
|
316
|
+
return best;
|
|
317
|
+
}
|
|
318
|
+
async function resolveIMessageCachedResourceBinding(messageId, ctx) {
|
|
319
|
+
await hydrateFromStoreOnce();
|
|
320
|
+
return resolveCachedResourceBinding(messageId, ctx);
|
|
321
|
+
}
|
|
322
|
+
function resolveCachedResourceBinding(messageId, ctx) {
|
|
323
|
+
const entry = imessageReplyCacheByMessageId.get(messageId.trim());
|
|
324
|
+
if (!entry) return "unknown";
|
|
325
|
+
if (Date.now() - entry.timestamp > 216e5) return "unknown";
|
|
326
|
+
if (entry.accountId !== ctx.accountId) return "mismatch";
|
|
327
|
+
return resolveIMessageChatMatch(entry, ctx);
|
|
328
|
+
}
|
|
329
|
+
async function isIMessageCurrentMessageInChatAsync(params) {
|
|
330
|
+
await hydrateFromStoreOnce();
|
|
331
|
+
return isCurrentMessageInChat(params);
|
|
332
|
+
}
|
|
333
|
+
function isCurrentMessageInChat(params) {
|
|
334
|
+
if (!params.accountId || !hasChatScope(params.chatContext)) return false;
|
|
335
|
+
const currentMessageId = normalizeOptionalString(String(params.currentMessageId));
|
|
336
|
+
if (!currentMessageId) return false;
|
|
337
|
+
const fullMessageId = /^\d+$/.test(currentMessageId) ? imessageShortIdToUuid.get(currentMessageId) : currentMessageId;
|
|
338
|
+
if (!fullMessageId) return false;
|
|
339
|
+
return resolveCachedResourceBinding(fullMessageId, {
|
|
340
|
+
...params.chatContext,
|
|
341
|
+
accountId: params.accountId
|
|
342
|
+
}) === "match";
|
|
343
|
+
}
|
|
57
344
|
// extensions/imessage/src/probe.ts
|
|
58
345
|
const RPC_SUPPORT_CACHE_TTL_MS = 3e5;
|
|
59
346
|
const PRIVATE_API_NEGATIVE_TTL_MS = 1e4;
|
|
60
347
|
const rpcSupportCache = /* @__PURE__ */ new Map();
|
|
61
348
|
function cacheIMessagePrivateApiStatus(cliPath, status) {
|
|
62
|
-
|
|
63
|
-
setCachedIMessagePrivateApiStatus(cliPath, status, 0);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const expiresAt = resolveExpiresAtMsFromDurationMs(PRIVATE_API_NEGATIVE_TTL_MS);
|
|
67
|
-
if (expiresAt !== void 0) setCachedIMessagePrivateApiStatus(cliPath, status, expiresAt);
|
|
349
|
+
setCachedIMessagePrivateApiStatus(cliPath, status, status.available ? 0 : Date.now() + PRIVATE_API_NEGATIVE_TTL_MS);
|
|
68
350
|
}
|
|
69
351
|
function setCachedRpcSupport(cliPath, result) {
|
|
70
|
-
const expiresAt = resolveExpiresAtMsFromDurationMs(RPC_SUPPORT_CACHE_TTL_MS);
|
|
71
|
-
if (expiresAt === void 0) return;
|
|
72
352
|
rpcSupportCache.set(cliPath, {
|
|
73
353
|
result,
|
|
74
|
-
expiresAt
|
|
354
|
+
expiresAt: Date.now() + RPC_SUPPORT_CACHE_TTL_MS
|
|
75
355
|
});
|
|
76
356
|
}
|
|
77
357
|
async function probeRpcSupport(cliPath, timeoutMs) {
|
|
78
358
|
const cached = rpcSupportCache.get(cliPath);
|
|
79
359
|
if (cached) {
|
|
80
|
-
|
|
81
|
-
if (now !== void 0 && cached.expiresAt > now) return cached.result;
|
|
360
|
+
if (cached.expiresAt > Date.now()) return cached.result;
|
|
82
361
|
rpcSupportCache.delete(cliPath);
|
|
83
362
|
}
|
|
84
363
|
try {
|
|
@@ -134,6 +413,21 @@ async function probeIMessagePrivateApi(cliPath, timeoutMs, options = {}) {
|
|
|
134
413
|
const cached = getCachedIMessagePrivateApiStatus(key);
|
|
135
414
|
if (cached) return cached;
|
|
136
415
|
}
|
|
416
|
+
const unavailable = (error) => {
|
|
417
|
+
const status = {
|
|
418
|
+
available: false,
|
|
419
|
+
v2Ready: false,
|
|
420
|
+
selectors: {},
|
|
421
|
+
rpcMethods: [],
|
|
422
|
+
cliCapabilities: {
|
|
423
|
+
sendRichSupportsAttachment: false,
|
|
424
|
+
pollSendSupportsNoComment: false
|
|
425
|
+
},
|
|
426
|
+
error
|
|
427
|
+
};
|
|
428
|
+
cacheIMessagePrivateApiStatus(key, status);
|
|
429
|
+
return status;
|
|
430
|
+
};
|
|
137
431
|
try {
|
|
138
432
|
const result = await runCommandWithTimeout([
|
|
139
433
|
expandIMessageUserPath(key),
|
|
@@ -142,21 +436,7 @@ async function probeIMessagePrivateApi(cliPath, timeoutMs, options = {}) {
|
|
|
142
436
|
], { timeoutMs });
|
|
143
437
|
const combined = `${result.stdout}\n${result.stderr}`.trim();
|
|
144
438
|
const normalized = normalizeLowercaseStringOrEmpty(combined);
|
|
145
|
-
if (result.code !== 0 && normalized.includes("unknown subcommand") && normalized.includes("status")) {
|
|
146
|
-
const status = {
|
|
147
|
-
available: false,
|
|
148
|
-
v2Ready: false,
|
|
149
|
-
selectors: {},
|
|
150
|
-
rpcMethods: [],
|
|
151
|
-
cliCapabilities: {
|
|
152
|
-
sendRichSupportsAttachment: false,
|
|
153
|
-
pollSendSupportsNoComment: false
|
|
154
|
-
},
|
|
155
|
-
error: `imsg CLI does not support the "status" subcommand. Update imsg on the Messages Mac: ${IMESSAGE_UPDATE_COMMAND}`
|
|
156
|
-
};
|
|
157
|
-
cacheIMessagePrivateApiStatus(key, status);
|
|
158
|
-
return status;
|
|
159
|
-
}
|
|
439
|
+
if (result.code !== 0 && normalized.includes("unknown subcommand") && normalized.includes("status")) return unavailable(`imsg CLI does not support the "status" subcommand. Update imsg on the Messages Mac: ${IMESSAGE_UPDATE_COMMAND}`);
|
|
160
440
|
const lines = normalizeStringEntries(result.stdout.split(/\r?\n/));
|
|
161
441
|
let payload = null;
|
|
162
442
|
for (const line of lines.toReversed()) try {
|
|
@@ -193,19 +473,7 @@ async function probeIMessagePrivateApi(cliPath, timeoutMs, options = {}) {
|
|
|
193
473
|
cacheIMessagePrivateApiStatus(key, status);
|
|
194
474
|
return status;
|
|
195
475
|
} catch (err) {
|
|
196
|
-
|
|
197
|
-
available: false,
|
|
198
|
-
v2Ready: false,
|
|
199
|
-
selectors: {},
|
|
200
|
-
rpcMethods: [],
|
|
201
|
-
cliCapabilities: {
|
|
202
|
-
sendRichSupportsAttachment: false,
|
|
203
|
-
pollSendSupportsNoComment: false
|
|
204
|
-
},
|
|
205
|
-
error: String(err)
|
|
206
|
-
};
|
|
207
|
-
cacheIMessagePrivateApiStatus(key, status);
|
|
208
|
-
return status;
|
|
476
|
+
return unavailable(String(err));
|
|
209
477
|
}
|
|
210
478
|
}
|
|
211
479
|
async function probeIMessage(timeoutMs, opts = {}) {
|
|
@@ -257,7 +525,7 @@ async function probeIMessage(timeoutMs, opts = {}) {
|
|
|
257
525
|
}
|
|
258
526
|
}
|
|
259
527
|
// extensions/imessage/src/actions.ts
|
|
260
|
-
const loadIMessageActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-
|
|
528
|
+
const loadIMessageActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-Ccg9CARL.mjs"), "imessageActionsRuntime");
|
|
261
529
|
const log = createSubsystemLogger("channels/imessage");
|
|
262
530
|
const providerId = "imessage";
|
|
263
531
|
const SUPPORTED_ACTIONS = /* @__PURE__ */ new Set([...IMESSAGE_ACTION_NAMES, "upload-file"]);
|
|
@@ -329,32 +597,9 @@ function readMessageIdWithChatFallback(params, chatContext) {
|
|
|
329
597
|
if (latest?.messageId) return latest.messageId;
|
|
330
598
|
return readStringParam(params, "messageId", { required: true });
|
|
331
599
|
}
|
|
332
|
-
async function resolveChatGuid(params) {
|
|
333
|
-
const target = resolveIMessageActionTarget(params);
|
|
334
|
-
if (!target) throw new Error(`iMessage ${params.action} requires chatGuid, chatId, chatIdentifier, or a chat target.`);
|
|
335
|
-
if (target.kind === "chat_guid") return target.chatGuid;
|
|
336
|
-
const synthesizedIdentifier = target.kind === "handle" ? `${target.service === "sms" ? "SMS" : "iMessage"};-;${target.to}` : "";
|
|
337
|
-
const lookupTarget = target.kind === "handle" ? {
|
|
338
|
-
kind: "chat_identifier",
|
|
339
|
-
chatIdentifier: synthesizedIdentifier
|
|
340
|
-
} : target;
|
|
341
|
-
const resolved = await params.runtime.resolveChatGuidForTarget({
|
|
342
|
-
target: lookupTarget,
|
|
343
|
-
options: params.options,
|
|
344
|
-
conversationReadOrigin: params.conversationReadOrigin
|
|
345
|
-
});
|
|
346
|
-
if (resolved) return resolved;
|
|
347
|
-
if (target.kind !== "handle") throw new Error(`iMessage ${params.action} failed: chatGuid not found for ${formatUnresolvedTarget(target)}.`);
|
|
348
|
-
if (params.action === "react" || params.action === "edit" || params.action === "unsend") throw new Error(`iMessage ${params.action} requires a known chat. No registered chat for the supplied target; send a message first or pass an explicit chatGuid.`);
|
|
349
|
-
return synthesizedIdentifier;
|
|
350
|
-
}
|
|
351
600
|
function formatUnresolvedTarget(target) {
|
|
352
601
|
return target.kind === "chat_id" ? "chat_id:<redacted>" : "chat_identifier:<redacted>";
|
|
353
602
|
}
|
|
354
|
-
function buildChatContextFromActionParams(params) {
|
|
355
|
-
const target = resolveIMessageActionTarget(params);
|
|
356
|
-
return target ? chatContextFromIMessageTarget(target, params.service) : {};
|
|
357
|
-
}
|
|
358
603
|
function mapTapbackReaction(emoji) {
|
|
359
604
|
const value = normalizeOptionalLowercaseString(emoji)?.replace(/\ufe0f/g, "");
|
|
360
605
|
return value ? [
|
|
@@ -399,7 +644,7 @@ function decodeBase64Buffer(params, action) {
|
|
|
399
644
|
if (!base64Buffer) throw new Error(`iMessage ${action} requires buffer (base64) parameter.`);
|
|
400
645
|
const canonical = canonicalizeBase64(base64Buffer.replaceAll("-", "+").replaceAll("_", "/"));
|
|
401
646
|
if (!canonical) throw new Error(`iMessage ${action} buffer must be valid base64.`);
|
|
402
|
-
return
|
|
647
|
+
return Buffer.from(canonical, "base64");
|
|
403
648
|
}
|
|
404
649
|
const REPLY_ATTACHMENT_PATH_PARAM_NAMES = [
|
|
405
650
|
"filePath",
|
|
@@ -408,21 +653,6 @@ const REPLY_ATTACHMENT_PATH_PARAM_NAMES = [
|
|
|
408
653
|
"mediaUrl",
|
|
409
654
|
"fileUrl"
|
|
410
655
|
];
|
|
411
|
-
function extractReplyAttachment(params) {
|
|
412
|
-
if (readStringParam(params, "buffer")) {
|
|
413
|
-
const filename = readStringParam(params, "filename") ?? "attachment.bin";
|
|
414
|
-
return { spec: {
|
|
415
|
-
kind: "buffer",
|
|
416
|
-
buffer: decodeBase64Buffer(params, "reply attachment"),
|
|
417
|
-
filename
|
|
418
|
-
} };
|
|
419
|
-
}
|
|
420
|
-
for (const name of REPLY_ATTACHMENT_PATH_PARAM_NAMES) if (readStringParam(params, name)) return {
|
|
421
|
-
spec: null,
|
|
422
|
-
bypassParam: name
|
|
423
|
-
};
|
|
424
|
-
return null;
|
|
425
|
-
}
|
|
426
656
|
const EFFECT_ALIASES = {
|
|
427
657
|
slam: "com.apple.MobileSMS.expressivesend.impact",
|
|
428
658
|
impact: "com.apple.MobileSMS.expressivesend.impact",
|
|
@@ -509,20 +739,35 @@ const imessageMessageActions = {
|
|
|
509
739
|
timeoutMs: account.config.probeTimeoutMs
|
|
510
740
|
};
|
|
511
741
|
const attestedConversationReadOrigin = conversationReadOrigin ?? "delegated";
|
|
512
|
-
const chatGuid = async () =>
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
742
|
+
const chatGuid = async () => {
|
|
743
|
+
const target = resolveIMessageActionTarget({
|
|
744
|
+
actionParams: params,
|
|
745
|
+
currentChannelId: toolContext?.currentChannelId
|
|
746
|
+
});
|
|
747
|
+
if (!target) throw new Error(`iMessage ${action} requires chatGuid, chatId, chatIdentifier, or a chat target.`);
|
|
748
|
+
if (target.kind === "chat_guid") return target.chatGuid;
|
|
749
|
+
const synthesizedIdentifier = target.kind === "handle" ? `${target.service === "sms" ? "SMS" : "iMessage"};-;${target.to}` : "";
|
|
750
|
+
const lookupTarget = target.kind === "handle" ? {
|
|
751
|
+
kind: "chat_identifier",
|
|
752
|
+
chatIdentifier: synthesizedIdentifier
|
|
753
|
+
} : target;
|
|
754
|
+
const resolved = await runtime.resolveChatGuidForTarget({
|
|
755
|
+
target: lookupTarget,
|
|
756
|
+
options: opts,
|
|
757
|
+
conversationReadOrigin: attestedConversationReadOrigin
|
|
758
|
+
});
|
|
759
|
+
if (resolved) return resolved;
|
|
760
|
+
if (target.kind !== "handle") throw new Error(`iMessage ${action} failed: chatGuid not found for ${formatUnresolvedTarget(target)}.`);
|
|
761
|
+
if (action === "react" || action === "edit" || action === "unsend") throw new Error(`iMessage ${action} requires a known chat. No registered chat for the supplied target; send a message first or pass an explicit chatGuid.`);
|
|
762
|
+
return synthesizedIdentifier;
|
|
763
|
+
};
|
|
520
764
|
const messageReference = async (input) => {
|
|
521
|
-
const
|
|
765
|
+
const service = account.config.service;
|
|
766
|
+
const target = resolveIMessageActionTarget({
|
|
522
767
|
actionParams: params,
|
|
523
|
-
currentChannelId: toolContext?.currentChannelId
|
|
524
|
-
service: account.config.service
|
|
768
|
+
currentChannelId: toolContext?.currentChannelId
|
|
525
769
|
});
|
|
770
|
+
const inputChatContext = target ? chatContextFromIMessageTarget(target, service) : {};
|
|
526
771
|
return await resolveAuthorizedIMessageActionReference({
|
|
527
772
|
messageId: input?.messageId,
|
|
528
773
|
inputChatContext,
|
|
@@ -580,7 +825,7 @@ const imessageMessageActions = {
|
|
|
580
825
|
if (!remove && (isEmpty || !reaction)) throw new Error("iMessage react supports love, like, dislike, laugh, emphasize, and question tapbacks.");
|
|
581
826
|
const partIndex = readNonNegativeIntegerParam(params, "partIndex");
|
|
582
827
|
const reference = await messageReference();
|
|
583
|
-
const reactionsToSend = remove && !reaction ?
|
|
828
|
+
const reactionsToSend = remove && !reaction ? TAPBACK_KINDS : reaction ? [reaction] : [];
|
|
584
829
|
for (const kind of reactionsToSend) await runtime.sendReaction({
|
|
585
830
|
chatGuid: reference.chatGuid,
|
|
586
831
|
messageId: reference.messageId,
|
|
@@ -631,9 +876,16 @@ const imessageMessageActions = {
|
|
|
631
876
|
const text = readMessageText(params);
|
|
632
877
|
if (!text) throw new Error("iMessage reply requires text or message.");
|
|
633
878
|
const reference = await messageReference();
|
|
634
|
-
|
|
879
|
+
let attachment;
|
|
880
|
+
if (readStringParam(params, "buffer")) {
|
|
881
|
+
const filename = readStringParam(params, "filename") ?? "attachment.bin";
|
|
882
|
+
attachment = {
|
|
883
|
+
kind: "buffer",
|
|
884
|
+
buffer: decodeBase64Buffer(params, "reply attachment"),
|
|
885
|
+
filename
|
|
886
|
+
};
|
|
887
|
+
} else for (const name of REPLY_ATTACHMENT_PATH_PARAM_NAMES) if (readStringParam(params, name)) throw new Error(`iMessage reply rejected \`${name}\` because it did not pass through the outbound media resolver. Pass a base64 \`buffer\` + \`filename\` directly, or invoke message(action: "reply") through the runner so the resolver can validate the path against mediaLocalRoots/sandbox/size before sending.`);
|
|
635
888
|
if (attachment) {
|
|
636
|
-
if (attachment.spec === null) throw new Error(`iMessage reply rejected \`${attachment.bypassParam}\` because it did not pass through the outbound media resolver. Pass a base64 \`buffer\` + \`filename\` directly, or invoke message(action: "reply") through the runner so the resolver can validate the path against mediaLocalRoots/sandbox/size before sending.`);
|
|
637
889
|
if (!opts.remoteHost && privateApiStatus?.cliCapabilities?.sendRichSupportsAttachment !== true) throw new Error("iMessage reply with an attachment needs an imsg build that exposes `send-rich --file` (openclaw/imsg#114). Upgrade imsg, or use action 'upload-file' (with filePath/filename) or action 'send' (with media) to deliver the file plus a separate 'reply' for any text.");
|
|
638
890
|
}
|
|
639
891
|
const partIndex = readNonNegativeIntegerParam(params, "partIndex");
|
|
@@ -642,7 +894,7 @@ const imessageMessageActions = {
|
|
|
642
894
|
text,
|
|
643
895
|
replyToMessageId: reference.messageId,
|
|
644
896
|
partIndex,
|
|
645
|
-
attachment
|
|
897
|
+
attachment,
|
|
646
898
|
options: opts
|
|
647
899
|
}), reference.chatGuid, { repliedTo: reference.messageId });
|
|
648
900
|
}
|
|
@@ -773,4 +1025,4 @@ const imessageMessageActions = {
|
|
|
773
1025
|
throw new Error(`Action ${action} is not supported for provider ${providerId}.`);
|
|
774
1026
|
}
|
|
775
1027
|
};
|
|
776
|
-
export {
|
|
1028
|
+
export { setIMessageRuntime as _, rememberIMessageReplyCache as a, normalizeIMessageMessageId as b, IMESSAGE_CATCHUP_CURSOR_NAMESPACE as c, capFailureRetriesMap as d, resolveIMessageCatchupCursorKey as f, getOptionalIMessageRuntime as g, getIMessageRuntime as h, isKnownFromMeIMessageTarget as i, IMESSAGE_SENT_ECHOES_NAMESPACE as l, resolveIMessageSentEchoEntryKey as m, probeIMessage as n, resolveIMessageCachedResourceBinding as o, resolveIMessageEchoMediaKey as p, probeIMessagePrivateApi as r, resolveIMessageMessageId as s, imessageMessageActions as t, IMESSAGE_SENT_ECHOES_TTL_MS as u, createPersistenceFailureReporter as v, normalizeIMessageGuid as y };
|