@hraness/ghostget 0.17.6 → 0.18.2

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.
Files changed (114) hide show
  1. package/CHANGELOG.md +301 -0
  2. package/README.md +16 -9
  3. package/dist/apple-photos-client.js +2 -1
  4. package/dist/beeper-client.js +2 -1
  5. package/dist/client.js +1 -0
  6. package/dist/imessage-direct-install-2gm3kge7.js +324 -0
  7. package/dist/index-01eeae9e.js +5 -0
  8. package/dist/index-2ymnp8xv.js +145 -0
  9. package/dist/index-en5hycxp.js +175 -0
  10. package/dist/{index-cytf8d9p.js → index-hfbygww8.js} +1 -1
  11. package/dist/index-n4szk3nw.js +50 -0
  12. package/dist/index-yq6maz71.js +1105 -0
  13. package/dist/index-z1w83f81.js +4 -0
  14. package/dist/index.js +4 -47
  15. package/dist/messaging-automation-api.js +21 -0
  16. package/dist/messaging-automation-j4274hvc.js +609 -0
  17. package/dist/messaging-native-install-8w1j36ah.js +16 -0
  18. package/dist/messaging.js +1 -0
  19. package/dist/omni-client.js +1 -0
  20. package/dist/whatsapp-automation-runtime-hgh1e9rm.js +845 -0
  21. package/dist/whatsapp-client.js +1 -0
  22. package/docs/control-panel.md +147 -0
  23. package/docs/imessage-direct-provider.md +31 -7
  24. package/docs/messaging-automation.md +103 -0
  25. package/package.json +66 -7
  26. package/skills/ghostget/SKILL.md +3 -1
  27. package/skills/ghostget/references/control-panel.md +43 -0
  28. package/skills/ghostget/references/install.md +5 -5
  29. package/skills/ghostget/references/linkedin-adapter.md +222 -12
  30. package/skills/ghostget/references/platform-patterns.md +1 -1
  31. package/src/args.ts +18 -5
  32. package/src/assets/adapters/imessage/wrench-web-adapter.json +126 -0
  33. package/src/assets/adapters/linkedin/wrench-web-adapter.json +1 -1
  34. package/src/assets/adapters/whatsapp/wrench-web-adapter.json +144 -0
  35. package/src/assets/messaging-runtime/NOTICE.txt +2580 -0
  36. package/src/assets/messaging-runtime/imsg-darwin-arm64.gz +0 -0
  37. package/src/assets/messaging-runtime/phone-number-info.plist.gz +0 -0
  38. package/src/assets/messaging-runtime/phone-number-metadata.json.gz +0 -0
  39. package/src/assets/messaging-runtime/phone-number-privacy.plist.gz +0 -0
  40. package/src/assets/messaging-runtime/wacli-darwin-arm64.gz +0 -0
  41. package/src/auth.ts +35 -1
  42. package/src/beeper-client-types.ts +1 -1
  43. package/src/cli.ts +18 -0
  44. package/src/confirmed-write-platform.ts +16 -1
  45. package/src/control/account-revision.ts +16 -0
  46. package/src/control/activity.ts +104 -0
  47. package/src/control/approval-broker.ts +59 -0
  48. package/src/control/approval-client.ts +49 -0
  49. package/src/control/bundled-interfaces.ts +20 -0
  50. package/src/control/cli.ts +20 -0
  51. package/src/control/connections.ts +87 -0
  52. package/src/control/credential-helper.ts +152 -0
  53. package/src/control/helper.ts +79 -0
  54. package/src/control/interface-cli.ts +22 -0
  55. package/src/control/interface-json.ts +94 -0
  56. package/src/control/interface-schema.ts +120 -0
  57. package/src/control/interfaces.ts +438 -0
  58. package/src/control/protocol.ts +182 -0
  59. package/src/control/service.ts +104 -0
  60. package/src/control/validation.ts +103 -0
  61. package/src/control/vault.ts +105 -0
  62. package/src/control/web-gateway.ts +62 -0
  63. package/src/control/web-policy.ts +56 -0
  64. package/src/ghostget.ts +18 -4
  65. package/src/messaging-automation-api.ts +12 -0
  66. package/src/messaging-automation-descriptors.ts +29 -0
  67. package/src/messaging-automation-factory.ts +148 -0
  68. package/src/messaging-automation-server.ts +183 -0
  69. package/src/messaging-automation-types.ts +155 -0
  70. package/src/messaging-automation-validation.ts +110 -0
  71. package/src/messaging-automation.ts +356 -0
  72. package/src/messaging-runtime.ts +3 -0
  73. package/src/oauth-google.ts +11 -5
  74. package/src/omni-runtime.ts +18 -3
  75. package/src/operation-permission-store.ts +92 -0
  76. package/src/operation-permission.ts +308 -0
  77. package/src/pinned-https.ts +5 -0
  78. package/src/plugins/imessage-direct/plugin.ts +12 -1
  79. package/src/plugins/imessage-direct/vendor/0003-feat-rpc-add-no-fetch-rich-cards.patch +276 -0
  80. package/src/plugins/imessage-direct/vendor/provenance.json +84 -10
  81. package/src/plugins/linkedin-web/plugin.ts +1 -1
  82. package/src/plugins/whatsapp-linked-device/plugin.ts +7 -2
  83. package/src/plugins/whatsapp-linked-device/vendor/0001-ghostget-private-messaging.patch +1093 -0
  84. package/src/plugins/whatsapp-linked-device/vendor/README.md +39 -0
  85. package/src/plugins/whatsapp-linked-device/vendor/provenance.json +45 -0
  86. package/src/provider-http.ts +11 -3
  87. package/src/provider-plugin-contract-identity.ts +2 -2
  88. package/src/provider-plugin-import-analysis.ts +52 -0
  89. package/src/provider-plugin-module-analysis.ts +21 -1
  90. package/src/provider-plugin-registry.ts +4 -8
  91. package/src/provider-plugin.ts +18 -8
  92. package/src/providers/imessage-automation.ts +250 -0
  93. package/src/providers/imessage-direct-install.ts +7 -0
  94. package/src/providers/imessage-direct-runtime.ts +32 -2
  95. package/src/providers/imessage-direct.ts +13 -3
  96. package/src/providers/linkedin-contact-failure.ts +21 -0
  97. package/src/providers/linkedin-contact-platform.ts +8 -1
  98. package/src/providers/linkedin-contact-program.ts +34 -5
  99. package/src/providers/linkedin-web-contact.ts +1223 -35
  100. package/src/providers/linkedin-web-profile-browser.ts +659 -45
  101. package/src/providers/linkedin-web-runtime.ts +1 -0
  102. package/src/providers/linkedin-web.ts +46 -2
  103. package/src/providers/messaging-native-artifacts.ts +38 -0
  104. package/src/providers/messaging-native-install.ts +75 -0
  105. package/src/providers/whatsapp-automation-runtime.ts +240 -0
  106. package/src/providers/whatsapp-automation.ts +153 -0
  107. package/src/read-client.ts +12 -2
  108. package/src/runtime.ts +81 -9
  109. package/src/state-helper.ts +2 -0
  110. package/src/storage.ts +71 -1
  111. package/src/usage.ts +9 -1
  112. package/src/version.ts +1 -1
  113. package/src/web-session-contract-definitions.ts +1 -1
  114. package/tsconfig.json +3 -0
@@ -0,0 +1,324 @@
1
+ // @bun
2
+ import {
3
+ ensureImsgNativeResources,
4
+ verifyImsgNativeResources
5
+ } from "./index-en5hycxp.js";
6
+ import {
7
+ ensurePrivateStateDirectory,
8
+ ghostgetStateHome
9
+ } from "./index-yq6maz71.js";
10
+ import"./index-gwk7rbyj.js";
11
+ import"./index-z1w83f81.js";
12
+
13
+ // src/providers/imessage-direct-install.ts
14
+ import { createHash, randomBytes } from "crypto";
15
+ import { constants, createReadStream } from "fs";
16
+ import {
17
+ chmod,
18
+ link,
19
+ lstat,
20
+ open,
21
+ realpath,
22
+ unlink
23
+ } from "fs/promises";
24
+ import { isAbsolute, join } from "path";
25
+
26
+ // src/providers/imessage-direct.ts
27
+ var IMSG_UPSTREAM_VERSION = "0.14.1";
28
+ var IMSG_UPSTREAM_COMMIT = "25beb76c902b0acf2dd7ae392f1b0792f6813240";
29
+ var IMSG_PRIVATE_TRANSPORT_PATCH_COMMIT = "292db82d89293867ef847a2875667fea0fdd5dc1";
30
+ var IMSG_PRIVATE_TRANSPORT_PATCH_SHA256 = "99cf18953470e85a62a226f207e6a5c0452d3997675c99eabf4d5cb73c6411fd";
31
+ var IMSG_EXACT_CHAT_PATCH_COMMIT = "c5994f00d17969fd7772fd2772e7b3591089513a";
32
+ var IMSG_EXACT_CHAT_PATCH_SHA256 = "b05aa92a078930f96fda611c674436638843f7b148d7bfa3b65ed1ccb0885c13";
33
+ var IMSG_NO_FETCH_RICH_CARDS_PATCH_COMMIT = "520b82ab025c1d4d57333b552aa65c9ae3fdb5fd";
34
+ var IMSG_NO_FETCH_RICH_CARDS_PATCH_SHA256 = "c2346afca4dd0f9721c235db4f692d6a2d6dece36481e074d94b5ce78f2e019b";
35
+ var IMSG_REVIEWED_PATCH_COMMIT = IMSG_NO_FETCH_RICH_CARDS_PATCH_COMMIT;
36
+ var IMSG_REVIEWED_PATCHES = Object.freeze([
37
+ Object.freeze({
38
+ commit: IMSG_PRIVATE_TRANSPORT_PATCH_COMMIT,
39
+ sha256: IMSG_PRIVATE_TRANSPORT_PATCH_SHA256
40
+ }),
41
+ Object.freeze({
42
+ commit: IMSG_EXACT_CHAT_PATCH_COMMIT,
43
+ sha256: IMSG_EXACT_CHAT_PATCH_SHA256
44
+ }),
45
+ Object.freeze({
46
+ commit: IMSG_NO_FETCH_RICH_CARDS_PATCH_COMMIT,
47
+ sha256: IMSG_NO_FETCH_RICH_CARDS_PATCH_SHA256
48
+ })
49
+ ]);
50
+ var IMSG_REVIEWED_VERSION = "0.14.1+private-transport.3";
51
+ var IMSG_DARWIN_ARM64_EXECUTABLE_SHA256 = "46c4c73c81c7db2d516c2d467c66aff03c73de196996d646bc8afce0ea85cff6";
52
+ var IMSG_TOOL_PIN = Object.freeze({
53
+ id: "imsg-private-transport",
54
+ implementation: "github.com/openclaw/imsg+reviewed-patch",
55
+ version: IMSG_REVIEWED_VERSION,
56
+ upstreamVersion: IMSG_UPSTREAM_VERSION,
57
+ upstreamCommit: IMSG_UPSTREAM_COMMIT,
58
+ reviewedPatchCommit: IMSG_REVIEWED_PATCH_COMMIT,
59
+ reviewedPatches: IMSG_REVIEWED_PATCHES,
60
+ sourceUrl: `https://github.com/openclaw/imsg/tree/${IMSG_UPSTREAM_COMMIT}`,
61
+ artifacts: Object.freeze([Object.freeze({
62
+ platform: "darwin",
63
+ arch: "arm64",
64
+ executableSha256: IMSG_DARWIN_ARM64_EXECUTABLE_SHA256
65
+ })])
66
+ });
67
+ var IMSG_DIRECT_OPERATION_NAMES = Object.freeze([
68
+ "messaging.list",
69
+ "conversations.read",
70
+ "messaging.read",
71
+ "messaging.send",
72
+ "messaging.delivery.read"
73
+ ]);
74
+ var IMSG_DIRECT_OPERATIONS = Object.freeze({
75
+ "messaging.list": Object.freeze({
76
+ effect: "read",
77
+ risk: "R1",
78
+ reason: "list a bounded current local Messages conversation window"
79
+ }),
80
+ "conversations.read": Object.freeze({
81
+ effect: "read",
82
+ risk: "R1",
83
+ reason: "resolve one exact live iMessage chat GUID, service, and database row"
84
+ }),
85
+ "messaging.read": Object.freeze({
86
+ effect: "read",
87
+ risk: "R1",
88
+ reason: "read bounded current context from one exact live iMessage chat"
89
+ }),
90
+ "messaging.send": Object.freeze({
91
+ effect: "write",
92
+ risk: "R3",
93
+ reason: "submit one confirmed text bubble through explicit AppleScript iMessage transport with SMS fallback disabled"
94
+ }),
95
+ "messaging.delivery.read": Object.freeze({
96
+ effect: "read",
97
+ risk: "R1",
98
+ reason: "read the local Messages status row for one exact observed outgoing GUID"
99
+ })
100
+ });
101
+
102
+ // src/providers/imessage-direct-install.ts
103
+ var MAX_IMSG_BINARY_BYTES = 256 * 1024 * 1024;
104
+
105
+ class ImsgInstallFailure extends Error {
106
+ }
107
+ function installFailure(message) {
108
+ return new ImsgInstallFailure(message);
109
+ }
110
+ function filesystemErrorCode(error) {
111
+ if (typeof error !== "object" || error === null || !("code" in error) || typeof error.code !== "string")
112
+ return null;
113
+ return error.code;
114
+ }
115
+ function sourceOpenFailure(error) {
116
+ const code = filesystemErrorCode(error);
117
+ if (code === "ENOENT") {
118
+ return installFailure("imsg install source file does not exist");
119
+ }
120
+ if (code === "EACCES" || code === "EPERM") {
121
+ return installFailure("imsg install source file is unreadable");
122
+ }
123
+ if (code === "ELOOP") {
124
+ return installFailure("imsg install source is not a trusted executable file");
125
+ }
126
+ return installFailure("imsg install source file could not be opened safely");
127
+ }
128
+ function imsgArtifactForCurrentRuntime() {
129
+ const artifact = IMSG_TOOL_PIN.artifacts.find((candidate) => candidate.platform === process.platform && candidate.arch === process.arch);
130
+ if (artifact === undefined) {
131
+ throw new Error(`reviewed imsg transport has no artifact for ${process.platform}/${process.arch}`);
132
+ }
133
+ return artifact;
134
+ }
135
+ async function sha256File(path) {
136
+ return await new Promise((resolve, reject) => {
137
+ const hash = createHash("sha256");
138
+ const stream = createReadStream(path);
139
+ stream.on("data", (chunk) => hash.update(chunk));
140
+ stream.on("error", reject);
141
+ stream.on("end", () => resolve(hash.digest("hex")));
142
+ });
143
+ }
144
+ async function pinnedBinaryCandidate(path, executableSha256) {
145
+ try {
146
+ const canonical = await realpath(path);
147
+ if (canonical !== path)
148
+ return null;
149
+ const stats = await lstat(canonical);
150
+ if (!stats.isFile() || stats.isSymbolicLink() || stats.size < 1 || stats.size > MAX_IMSG_BINARY_BYTES || (stats.mode & 18) !== 0 || (stats.mode & 73) === 0 || stats.uid !== process.getuid?.() && stats.uid !== 0)
151
+ return null;
152
+ return await sha256File(canonical) === executableSha256 ? canonical : null;
153
+ } catch {
154
+ return null;
155
+ }
156
+ }
157
+ function imsgInstalledBinaryPath(environment = process.env) {
158
+ return join(ghostgetStateHome(environment), "tools", "imsg", IMSG_REVIEWED_VERSION, "imsg");
159
+ }
160
+ async function resolvePinnedImsgBinary(environment = process.env) {
161
+ const artifact = imsgArtifactForCurrentRuntime();
162
+ let candidate;
163
+ try {
164
+ candidate = imsgInstalledBinaryPath(environment);
165
+ } catch {
166
+ throw installFailure(`reviewed imsg transport ${IMSG_REVIEWED_VERSION} is unavailable or failed integrity verification`);
167
+ }
168
+ const resolved = await pinnedBinaryCandidate(candidate, artifact.executableSha256);
169
+ if (resolved === null) {
170
+ throw new Error(`reviewed imsg transport ${IMSG_REVIEWED_VERSION} is unavailable or failed integrity verification`);
171
+ }
172
+ try {
173
+ await verifyImsgNativeResources(join(ghostgetStateHome(environment), "tools", "imsg", IMSG_REVIEWED_VERSION));
174
+ } catch {
175
+ throw installFailure("reviewed imsg resources are unavailable or failed integrity verification");
176
+ }
177
+ return resolved;
178
+ }
179
+ async function validateInstallDirectory(path) {
180
+ try {
181
+ const canonical = await realpath(path);
182
+ const stats = await lstat(path);
183
+ if (canonical !== path || !stats.isDirectory() || stats.isSymbolicLink() || stats.uid !== process.getuid?.() || (stats.mode & 63) !== 0) {
184
+ throw installFailure("imsg install directory must be an owned physical private directory");
185
+ }
186
+ } catch (error) {
187
+ if (error instanceof ImsgInstallFailure)
188
+ throw error;
189
+ throw installFailure("imsg install state directory is unavailable or unsafe");
190
+ }
191
+ }
192
+ async function copyPinnedBinary(sourcePath, destinationPath, expectedSha256) {
193
+ let source;
194
+ try {
195
+ source = await open(sourcePath, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
196
+ } catch (error) {
197
+ throw sourceOpenFailure(error);
198
+ }
199
+ let destination;
200
+ try {
201
+ const before = await source.stat();
202
+ if (!before.isFile() || before.size < 1 || before.size > MAX_IMSG_BINARY_BYTES || (before.mode & 18) !== 0 || (before.mode & 73) === 0 || before.uid !== process.getuid?.() && before.uid !== 0)
203
+ throw installFailure("imsg install source is not a trusted executable file");
204
+ destination = await open(destinationPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 320);
205
+ const hash = createHash("sha256");
206
+ const buffer = Buffer.allocUnsafe(1024 * 1024);
207
+ let offset = 0;
208
+ while (offset < before.size) {
209
+ const read = await source.read(buffer, 0, Math.min(buffer.byteLength, before.size - offset), offset);
210
+ if (read.bytesRead < 1) {
211
+ throw installFailure("imsg install source changed while copied");
212
+ }
213
+ const chunk = buffer.subarray(0, read.bytesRead);
214
+ hash.update(chunk);
215
+ let written = 0;
216
+ while (written < chunk.byteLength) {
217
+ const result = await destination.write(chunk, written, chunk.byteLength - written, offset + written);
218
+ if (result.bytesWritten < 1) {
219
+ throw installFailure("imsg install copy failed");
220
+ }
221
+ written += result.bytesWritten;
222
+ }
223
+ offset += read.bytesRead;
224
+ }
225
+ const extra = await source.read(Buffer.allocUnsafe(1), 0, 1, offset);
226
+ const after = await source.stat();
227
+ if (extra.bytesRead !== 0 || before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || hash.digest("hex") !== expectedSha256) {
228
+ throw installFailure("imsg install source changed or did not match the reviewed digest");
229
+ }
230
+ await destination.sync();
231
+ } finally {
232
+ await destination?.close();
233
+ await source.close();
234
+ }
235
+ await chmod(destinationPath, 320);
236
+ if (await sha256File(destinationPath) !== expectedSha256) {
237
+ throw installFailure("installed imsg transport failed its reviewed digest");
238
+ }
239
+ }
240
+ async function installReviewedImsgBinary(sourcePath, environment = process.env) {
241
+ if (!isAbsolute(sourcePath)) {
242
+ throw installFailure("imsg install source must be an absolute path");
243
+ }
244
+ const artifact = imsgArtifactForCurrentRuntime();
245
+ let stateHome;
246
+ try {
247
+ stateHome = ghostgetStateHome(environment);
248
+ } catch {
249
+ throw installFailure("imsg install state directory is unavailable or unsafe");
250
+ }
251
+ const installDirectory = join(stateHome, "tools", "imsg", IMSG_REVIEWED_VERSION);
252
+ const destinationPath = join(installDirectory, "imsg");
253
+ try {
254
+ ensurePrivateStateDirectory(installDirectory, environment);
255
+ await validateInstallDirectory(installDirectory);
256
+ await ensureImsgNativeResources(installDirectory, environment);
257
+ } catch (error) {
258
+ if (error instanceof ImsgInstallFailure)
259
+ throw error;
260
+ throw installFailure("imsg install state directory is unavailable or unsafe");
261
+ }
262
+ const existing = await pinnedBinaryCandidate(destinationPath, artifact.executableSha256);
263
+ if (existing !== null) {
264
+ return Object.freeze({
265
+ path: existing,
266
+ executableSha256: artifact.executableSha256,
267
+ version: IMSG_REVIEWED_VERSION,
268
+ alreadyPresent: true
269
+ });
270
+ }
271
+ try {
272
+ await lstat(destinationPath);
273
+ throw installFailure("existing imsg install does not match the reviewed artifact");
274
+ } catch (error) {
275
+ if (error instanceof ImsgInstallFailure)
276
+ throw error;
277
+ if (filesystemErrorCode(error) !== "ENOENT") {
278
+ throw installFailure("existing imsg install could not be inspected safely");
279
+ }
280
+ }
281
+ const temporaryPath = join(installDirectory, `.imsg-install-${randomBytes(16).toString("hex")}`);
282
+ try {
283
+ try {
284
+ await copyPinnedBinary(sourcePath, temporaryPath, artifact.executableSha256);
285
+ } catch (error) {
286
+ if (error instanceof ImsgInstallFailure)
287
+ throw error;
288
+ throw installFailure("imsg install I/O failed safely");
289
+ }
290
+ try {
291
+ await link(temporaryPath, destinationPath);
292
+ } catch (error) {
293
+ if (filesystemErrorCode(error) !== "EEXIST") {
294
+ throw installFailure("imsg install I/O failed safely");
295
+ }
296
+ if (await pinnedBinaryCandidate(destinationPath, artifact.executableSha256) === null)
297
+ throw installFailure("concurrent imsg install produced unreviewed bytes");
298
+ }
299
+ } finally {
300
+ try {
301
+ await unlink(temporaryPath);
302
+ } catch (error) {
303
+ if (filesystemErrorCode(error) !== "ENOENT") {
304
+ throw installFailure("imsg install I/O cleanup failed safely");
305
+ }
306
+ }
307
+ }
308
+ const installed = await pinnedBinaryCandidate(destinationPath, artifact.executableSha256);
309
+ if (installed === null) {
310
+ throw installFailure("imsg install could not be verified");
311
+ }
312
+ return Object.freeze({
313
+ path: installed,
314
+ executableSha256: artifact.executableSha256,
315
+ version: IMSG_REVIEWED_VERSION,
316
+ alreadyPresent: false
317
+ });
318
+ }
319
+ export {
320
+ resolvePinnedImsgBinary,
321
+ installReviewedImsgBinary,
322
+ imsgInstalledBinaryPath,
323
+ imsgArtifactForCurrentRuntime
324
+ };
@@ -0,0 +1,5 @@
1
+ // @bun
2
+ // src/messaging-automation-types.ts
3
+ var MESSAGING_AUTOMATION_PROTOCOL = "ghostget.messaging-automation/1";
4
+
5
+ export { MESSAGING_AUTOMATION_PROTOCOL };
@@ -0,0 +1,145 @@
1
+ // @bun
2
+ // src/messaging-automation-validation.ts
3
+ import { types } from "util";
4
+ var AUTOMATION_ACTION_KINDS = Object.freeze(["text", "attachment", "reaction", "sticker", "link", "poll", "app-clip", "experience"]);
5
+ function automationRecord(value, keys) {
6
+ if (types.isProxy(value) || typeof value !== "object" || value === null || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
7
+ throw new Error("Messaging automation value must be a plain object.");
8
+ const fields = Object.getOwnPropertyDescriptors(value);
9
+ if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key) => !Object.hasOwn(fields, key)) || Reflect.ownKeys(fields).some((key) => typeof key !== "string" || !keys.includes(key) || !fields[key]?.enumerable || !("value" in fields[key]))) {
10
+ throw new Error("Messaging automation value has unsupported fields.");
11
+ }
12
+ return value;
13
+ }
14
+ function automationText(value, maximum = 256, empty = false) {
15
+ if (typeof value !== "string" || !empty && value.length === 0 || Buffer.byteLength(value) > maximum || /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(value) || Buffer.from(value, "utf8").toString("utf8") !== value)
16
+ throw new Error("Messaging automation text is invalid or exceeds its bound.");
17
+ return value;
18
+ }
19
+ function automationId(value) {
20
+ const id = automationText(value, 256);
21
+ if (!/^[A-Za-z0-9._:-]+$/u.test(id))
22
+ throw new Error("Messaging automation identifier is invalid.");
23
+ return id;
24
+ }
25
+ function automationDigest(value) {
26
+ if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value))
27
+ throw new Error("Messaging automation digest is invalid.");
28
+ return value;
29
+ }
30
+ function automationInteger(value, minimum, maximum) {
31
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum || value > maximum)
32
+ throw new Error("Messaging automation integer is outside its bound.");
33
+ return value;
34
+ }
35
+ function automationArray(value, maximum) {
36
+ if (types.isProxy(value) || !Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > maximum || Reflect.ownKeys(value).length !== value.length + 1)
37
+ throw new Error("Messaging automation list is invalid or exceeds its bound.");
38
+ for (let i = 0;i < value.length; i++) {
39
+ const field = Object.getOwnPropertyDescriptor(value, String(i));
40
+ if (field === undefined || !field.enumerable || !("value" in field))
41
+ throw new Error("Messaging automation list must be dense data.");
42
+ }
43
+ return value;
44
+ }
45
+ function automationDate(value) {
46
+ const text = automationText(value, 32);
47
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(text) || !Number.isFinite(Date.parse(text)) || new Date(text).toISOString() !== text)
48
+ throw new Error("Messaging automation time must be canonical UTC.");
49
+ return text;
50
+ }
51
+ function parseAutomationCoordinate(value) {
52
+ if (typeof value !== "object" || value === null || types.isProxy(value))
53
+ throw new Error("Messaging coordinate is invalid.");
54
+ const provider = Object.getOwnPropertyDescriptor(value, "provider")?.value;
55
+ if (provider === "imessage") {
56
+ const r2 = automationRecord(value, ["provider", "chatGuid", "service", "observedChatRowId"]);
57
+ const chatGuid = automationText(r2.chatGuid, 1024);
58
+ if (!chatGuid.startsWith("iMessage;") || r2.service !== "iMessage")
59
+ throw new Error("Only exact iMessage conversations are supported.");
60
+ return Object.freeze({ provider, chatGuid, service: "iMessage", observedChatRowId: automationInteger(r2.observedChatRowId, 1, Number.MAX_SAFE_INTEGER) });
61
+ }
62
+ const r = automationRecord(value, ["provider", "conversationJid"]);
63
+ if (provider !== "whatsapp" || typeof r.conversationJid !== "string" || !/^(?:[1-9][0-9]{4,14}@s\.whatsapp\.net|[1-9][0-9]{4,19}@lid)$/u.test(r.conversationJid))
64
+ throw new Error("Only exact individual WhatsApp conversations are supported.");
65
+ return Object.freeze({ provider, conversationJid: r.conversationJid });
66
+ }
67
+ function parseAutomationIdentity(value) {
68
+ const r = automationRecord(value, ["provider", "authId", "accountIdentity", "accountSubject", "implementationIdentity", "sourceGeneration"]);
69
+ if (r.provider !== "imessage" && r.provider !== "whatsapp")
70
+ throw new Error("Messaging provider is invalid.");
71
+ return Object.freeze({ provider: r.provider, authId: automationId(r.authId), accountIdentity: automationDigest(r.accountIdentity), accountSubject: automationText(r.accountSubject, 512), implementationIdentity: automationDigest(r.implementationIdentity), sourceGeneration: automationText(r.sourceGeneration, 256) });
72
+ }
73
+ function parseAutomationActionKind(value) {
74
+ if (!AUTOMATION_ACTION_KINDS.includes(value))
75
+ throw new Error("Messaging action is unsupported.");
76
+ return value;
77
+ }
78
+ function https(value) {
79
+ const url = automationText(value, 4096);
80
+ const parsed = new URL(url);
81
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password)
82
+ throw new Error("Messaging links require credential-free HTTPS.");
83
+ return url;
84
+ }
85
+ function parseAutomationAction(value) {
86
+ if (typeof value !== "object" || value === null || types.isProxy(value))
87
+ throw new Error("Messaging action is invalid.");
88
+ const kind = parseAutomationActionKind(Object.getOwnPropertyDescriptor(value, "kind")?.value);
89
+ if (kind === "text") {
90
+ const r2 = automationRecord(value, ["kind", "text"]);
91
+ return Object.freeze({ kind, text: automationText(r2.text, 65536) });
92
+ }
93
+ if (kind === "link" || kind === "app-clip") {
94
+ const r2 = automationRecord(value, ["kind", "url"]);
95
+ return Object.freeze({ kind, url: https(r2.url) });
96
+ }
97
+ if (kind === "attachment") {
98
+ const r2 = automationRecord(value, ["kind", "assetId", "name", "mimeType"]);
99
+ const name = automationText(r2.name, 255);
100
+ const mimeType = automationText(r2.mimeType, 128);
101
+ if (/[\\/\r\n]/u.test(name) || !/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/u.test(mimeType))
102
+ throw new Error("Messaging attachment metadata is invalid.");
103
+ return Object.freeze({ kind, assetId: automationId(r2.assetId), name, mimeType });
104
+ }
105
+ if (kind === "reaction") {
106
+ const r2 = automationRecord(value, ["kind", "messageId", "emoji", "remove"]);
107
+ const emoji = automationText(r2.emoji, 64, true);
108
+ if (typeof r2.remove !== "boolean" || !r2.remove && (emoji.length === 0 || [...new Intl.Segmenter(undefined, { granularity: "grapheme" }).segment(emoji)].length !== 1))
109
+ throw new Error("Messaging reaction is invalid.");
110
+ return Object.freeze({ kind, messageId: automationId(r2.messageId), emoji, remove: r2.remove });
111
+ }
112
+ if (kind === "sticker") {
113
+ const r2 = automationRecord(value, ["kind", "assetId", "messageId"]);
114
+ return Object.freeze({ kind, assetId: automationId(r2.assetId), messageId: r2.messageId === null ? null : automationId(r2.messageId) });
115
+ }
116
+ if (kind === "poll") {
117
+ const r2 = automationRecord(value, ["kind", "question", "options", "maximumSelections"]);
118
+ const options = automationArray(r2.options, 12).map((option) => automationText(option, 256));
119
+ if (options.length < 2 || new Set(options).size !== options.length)
120
+ throw new Error("Messaging poll needs distinct options.");
121
+ return Object.freeze({ kind, question: automationText(r2.question, 1024), options: Object.freeze(options), maximumSelections: r2.maximumSelections === null ? null : automationInteger(r2.maximumSelections, 1, options.length) });
122
+ }
123
+ const r = automationRecord(value, ["kind", "experienceId", "parameters"]);
124
+ if (typeof r.parameters !== "object" || r.parameters === null || types.isProxy(r.parameters))
125
+ throw new Error("Messaging experience parameters are invalid.");
126
+ const keys = Object.keys(r.parameters);
127
+ if (keys.length > 16)
128
+ throw new Error("Messaging experience parameters exceed their bound.");
129
+ const parameters = automationRecord(r.parameters, keys);
130
+ return Object.freeze({ kind, experienceId: automationId(r.experienceId), parameters: Object.freeze(Object.fromEntries(keys.map((key) => [automationId(key), automationText(parameters[key], 4096, true)]))) });
131
+ }
132
+ function parseAutomationMessage(value) {
133
+ const r = automationRecord(value, ["id", "coordinate", "direction", "occurredAt", "text", "kind", "relatedMessageId", "attachments"]);
134
+ if (r.direction !== "incoming" && r.direction !== "outgoing" && r.direction !== "unknown")
135
+ throw new Error("Messaging authorship is invalid.");
136
+ if (r.kind !== "message" && r.kind !== "reaction" && r.kind !== "edit" && r.kind !== "delete")
137
+ throw new Error("Messaging event kind is invalid.");
138
+ const attachments = automationArray(r.attachments, 20).map((value2) => {
139
+ const a = automationRecord(value2, ["name", "mimeType", "sizeBytes"]);
140
+ return Object.freeze({ name: a.name === null ? null : automationText(a.name, 512), mimeType: a.mimeType === null ? null : automationText(a.mimeType, 256), sizeBytes: a.sizeBytes === null ? null : automationInteger(a.sizeBytes, 0, 1024 * 1024 * 1024) });
141
+ });
142
+ return Object.freeze({ id: automationId(r.id), coordinate: parseAutomationCoordinate(r.coordinate), direction: r.direction, occurredAt: automationDate(r.occurredAt), text: r.text === null ? null : automationText(r.text, 65536, true), kind: r.kind, relatedMessageId: r.relatedMessageId === null ? null : automationId(r.relatedMessageId), attachments: Object.freeze(attachments) });
143
+ }
144
+
145
+ export { AUTOMATION_ACTION_KINDS, automationRecord, automationText, automationId, automationDigest, automationInteger, automationArray, automationDate, parseAutomationCoordinate, parseAutomationIdentity, parseAutomationActionKind, parseAutomationAction, parseAutomationMessage };
@@ -0,0 +1,175 @@
1
+ // @bun
2
+ import {
3
+ ensurePrivateStateDirectory
4
+ } from "./index-yq6maz71.js";
5
+ import {
6
+ __require
7
+ } from "./index-z1w83f81.js";
8
+
9
+ // src/providers/messaging-native-install.ts
10
+ import { createHash, randomBytes } from "crypto";
11
+ import { constants } from "fs";
12
+ import { chmod, link, lstat, mkdtemp, open, realpath, rmdir, unlink } from "fs/promises";
13
+ import { tmpdir } from "os";
14
+ import { dirname, join } from "path";
15
+ import { fileURLToPath } from "url";
16
+ import { gunzipSync } from "zlib";
17
+
18
+ // src/providers/messaging-native-artifacts.ts
19
+ var MESSAGING_NATIVE_ARTIFACTS = Object.freeze({
20
+ imessage: {
21
+ file: "imsg-darwin-arm64.gz",
22
+ sha256: "46c4c73c81c7db2d516c2d467c66aff03c73de196996d646bc8afce0ea85cff6",
23
+ bytes: 4525960,
24
+ compressedSha256: "12861bdb93779405f7cc4cb4a47fc1d6ea3bf2fc8cec5ed69f2197ce96804532",
25
+ compressedBytes: 1490956
26
+ },
27
+ whatsapp: {
28
+ file: "wacli-darwin-arm64.gz",
29
+ sha256: "9b77ffb810d028fde725ca02b1451f1725b5ff5312a46a54468a9a38533d4cea",
30
+ bytes: 21963810,
31
+ compressedSha256: "1c1650d6c79b74db7f8f335b4746398c802031260a90468312dcaac0374a5166",
32
+ compressedBytes: 7682949
33
+ },
34
+ phoneMetadata: {
35
+ file: "phone-number-metadata.json.gz",
36
+ sha256: "cbfe80ee5ee9901f46893a4d2c353d5eb513a21e62529ee5dfaa4e94b4460d83",
37
+ bytes: 365770,
38
+ compressedSha256: "27c5818bae151e945f0047fdc5a73f87a44ba678a4eb7343d1f56ef8a2b77e6f",
39
+ compressedBytes: 51131
40
+ },
41
+ phonePrivacy: {
42
+ file: "phone-number-privacy.plist.gz",
43
+ sha256: "561040f7a52952f75d02d4b6758382ff48c563d6f25c84a09f1a655f6dc60ff8",
44
+ bytes: 372,
45
+ compressedSha256: "83585010417bc61a2262f3c051eba0ab275987b02c824184c556f98412db4405",
46
+ compressedBytes: 240
47
+ },
48
+ phoneInfo: {
49
+ file: "phone-number-info.plist.gz",
50
+ sha256: "161f6c4a3ceaee9ab2ce30e33c80e605a11e196c53e9791f87341e01bf80d7e9",
51
+ bytes: 532,
52
+ compressedSha256: "6b41b44fe783ea873ef4a4a90bad1f554f474c0dc46735a95f41ff1f5bfb0729",
53
+ compressedBytes: 287
54
+ }
55
+ });
56
+
57
+ // src/providers/messaging-native-install.ts
58
+ var sha = (bytes) => createHash("sha256").update(bytes).digest("hex");
59
+ var resources = [
60
+ ["phoneMetadata", "Contents/Resources/PhoneNumberMetadata.json"],
61
+ ["phonePrivacy", "Contents/Resources/PrivacyInfo.xcprivacy"],
62
+ ["phoneInfo", "Contents/Info.plist"]
63
+ ];
64
+ async function readBundledMessagingAsset(asset) {
65
+ const expected = MESSAGING_NATIVE_ARTIFACTS[asset];
66
+ const candidates = [new URL(`../assets/messaging-runtime/${expected.file}`, import.meta.url), new URL(`../src/assets/messaging-runtime/${expected.file}`, import.meta.url)];
67
+ for (const candidate of candidates) {
68
+ const path = fileURLToPath(candidate);
69
+ let handle;
70
+ try {
71
+ handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
72
+ } catch (error) {
73
+ if (error.code === "ENOENT")
74
+ continue;
75
+ throw new Error("Bundled messaging asset is unreadable");
76
+ }
77
+ try {
78
+ const before = await handle.stat();
79
+ if (!before.isFile() || before.size !== expected.compressedBytes || before.size > 32 * 1024 * 1024 || (before.mode & 18) !== 0 || ![0, process.getuid?.()].includes(before.uid) || await realpath(path) !== path)
80
+ throw new Error("Bundled messaging asset has unsafe metadata");
81
+ const compressed = await handle.readFile(), after = await handle.stat();
82
+ if (before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || compressed.length !== expected.compressedBytes || sha(compressed) !== expected.compressedSha256)
83
+ throw new Error("Bundled messaging asset changed or failed its digest");
84
+ const bytes = gunzipSync(compressed, { maxOutputLength: expected.bytes });
85
+ if (bytes.length !== expected.bytes || sha(bytes) !== expected.sha256)
86
+ throw new Error("Bundled messaging executable or resource failed its exact pin");
87
+ return bytes;
88
+ } finally {
89
+ await handle.close();
90
+ }
91
+ }
92
+ throw new Error("The installed package omitted its pinned messaging runtime");
93
+ }
94
+ async function verifyResource(path, asset) {
95
+ const expected = MESSAGING_NATIVE_ARTIFACTS[asset], handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
96
+ try {
97
+ const info = await handle.stat();
98
+ if (!info.isFile() || info.nlink !== 1 || info.uid !== process.getuid?.() || (info.mode & 511) !== 384 || info.size !== expected.bytes || await realpath(path) !== path || sha(await handle.readFile()) !== expected.sha256)
99
+ throw new Error("Installed iMessage resource differs from its pin");
100
+ } finally {
101
+ await handle.close();
102
+ }
103
+ }
104
+ async function verifyImsgNativeResources(installDirectory) {
105
+ for (const [asset, relative] of resources)
106
+ await verifyResource(join(installDirectory, "PhoneNumberKit_PhoneNumberKit.bundle", relative), asset);
107
+ }
108
+ async function ensureImsgNativeResources(installDirectory, environment) {
109
+ for (const [asset, relative] of resources) {
110
+ const path = join(installDirectory, "PhoneNumberKit_PhoneNumberKit.bundle", relative), parent = dirname(path);
111
+ ensurePrivateStateDirectory(parent, environment);
112
+ try {
113
+ await lstat(path);
114
+ await verifyResource(path, asset);
115
+ continue;
116
+ } catch (error) {
117
+ if (error.code !== "ENOENT")
118
+ throw error;
119
+ }
120
+ const bytes = await readBundledMessagingAsset(asset), temporary = join(parent, `.install-${randomBytes(16).toString("hex")}`);
121
+ const handle = await open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
122
+ try {
123
+ try {
124
+ await handle.writeFile(bytes);
125
+ await handle.sync();
126
+ } finally {
127
+ await handle.close();
128
+ }
129
+ try {
130
+ await link(temporary, path);
131
+ } catch (error) {
132
+ if (error.code !== "EEXIST")
133
+ throw error;
134
+ }
135
+ } finally {
136
+ await unlink(temporary);
137
+ }
138
+ await verifyResource(path, asset);
139
+ const directory = await open(parent, constants.O_RDONLY);
140
+ try {
141
+ await directory.sync();
142
+ } finally {
143
+ await directory.close();
144
+ }
145
+ }
146
+ }
147
+ async function installBundledMessagingRuntime(provider, environment = process.env) {
148
+ if (process.platform !== "darwin" || process.arch !== "arm64")
149
+ throw new Error("Bundled messaging runtimes require Apple silicon macOS");
150
+ if (provider !== "imessage" && provider !== "whatsapp")
151
+ throw new Error("Unknown messaging runtime");
152
+ const bytes = await readBundledMessagingAsset(provider), directory = await mkdtemp(join(await realpath(tmpdir()), "wrench-messaging-install-"));
153
+ await chmod(directory, 448);
154
+ const path = join(directory, "runtime"), file = await open(path, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 320);
155
+ try {
156
+ try {
157
+ await file.writeFile(bytes);
158
+ await file.sync();
159
+ } finally {
160
+ await file.close();
161
+ }
162
+ if (provider === "imessage") {
163
+ const { installReviewedImsgBinary } = await import("./imessage-direct-install-2gm3kge7.js");
164
+ const result = await installReviewedImsgBinary(path, environment);
165
+ return { version: result.version, sha256: result.executableSha256, alreadyPresent: result.alreadyPresent };
166
+ }
167
+ const { installReviewedWhatsAppAutomationBinary } = await import("./whatsapp-automation-runtime-hgh1e9rm.js");
168
+ return await installReviewedWhatsAppAutomationBinary(path, environment);
169
+ } finally {
170
+ await unlink(path);
171
+ await rmdir(directory);
172
+ }
173
+ }
174
+
175
+ export { readBundledMessagingAsset, verifyImsgNativeResources, ensureImsgNativeResources, installBundledMessagingRuntime };
@@ -1,5 +1,5 @@
1
1
  // @bun
2
2
  // src/version.ts
3
- var GHOSTGET_VERSION = "0.17.6";
3
+ var GHOSTGET_VERSION = "0.18.2";
4
4
 
5
5
  export { GHOSTGET_VERSION };