@hraness/ghostget 0.18.13 → 0.18.14
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 +17 -0
- package/README.md +12 -7
- package/dist/apple-photos-client.js +1 -1
- package/dist/beeper-client.js +42 -6
- package/dist/{imessage-direct-install-2gm3kge7.js → imessage-direct-install-5nftwc6p.js} +1 -1
- package/dist/{index-maa9hb5z.js → index-g6na3p6k.js} +1 -1
- package/dist/{index-en5hycxp.js → index-kaav16fd.js} +4 -2
- package/dist/{index-2ymnp8xv.js → index-qcf2f6wm.js} +5 -1
- package/dist/{messaging-automation-j4274hvc.js → messaging-automation-58s1366z.js} +1 -1
- package/dist/messaging-automation-api.js +2 -2
- package/dist/{messaging-native-install-8w1j36ah.js → messaging-native-install-c90nv501.js} +1 -1
- package/dist/{whatsapp-automation-runtime-hgh1e9rm.js → whatsapp-automation-runtime-ke1dx91p.js} +1 -1
- package/docs/messaging-automation.md +14 -7
- package/package.json +8 -4
- package/skills/ghostget/references/install.md +5 -5
- package/src/assets/adapters/beeper/wrench-web-adapter.json +37 -1
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.4.0.json +1500 -0
- package/src/beeper-client-types.ts +1 -1
- package/src/control/menubar-cli.ts +3 -1
- package/src/control/menubar-icon.ts +4 -0
- package/src/messaging-automation-descriptors.ts +9 -1
- package/src/messaging-automation-factory.ts +7 -5
- package/src/messaging-automation-server.ts +2 -2
- package/src/messaging-automation-types.ts +3 -2
- package/src/messaging-automation-validation.ts +5 -1
- package/src/plugins/beeper-linked-device/plugin.ts +3 -1
- package/src/provider-plugin-contract-identity.ts +11 -2
- package/src/providers/beeper-automation.ts +447 -0
- package/src/providers/beeper-local-runtime.ts +61 -21
- package/src/providers/beeper-local.ts +4 -4
- package/src/providers/beeper-omni.ts +9 -2
- package/src/providers/messaging-native-install.ts +2 -1
- package/src/version.ts +1 -1
|
@@ -96,7 +96,7 @@ export type BeeperContactInteractionExportReceipt = Readonly<{
|
|
|
96
96
|
implementation: Readonly<{
|
|
97
97
|
producer: Readonly<{
|
|
98
98
|
package: "@hraness/ghostget";
|
|
99
|
-
version: "0.18.
|
|
99
|
+
version: "0.18.14";
|
|
100
100
|
}>;
|
|
101
101
|
officialCli: Readonly<{
|
|
102
102
|
implementation: "github.com/beeper/cli";
|
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
import { handleCompanionCommand, openBrowser, type CompanionOptions, type MenuItem } from "@hraness/desktop-foundation";
|
|
6
6
|
import { createSupportOffer } from "@hraness/support-foundation";
|
|
7
7
|
import { ghostgetSupportProfile } from "../support-profile";
|
|
8
|
+
import { TRAY_ICON } from "./menubar-icon";
|
|
8
9
|
import { ghostgetStateHome } from "../storage";
|
|
9
10
|
import { CONTROL_PROTOCOL, type ActivityRow, type ApprovalView, type CapabilityView, type ControlRequest, type ControlResponse, type ControlSnapshot } from "./protocol";
|
|
10
11
|
import type { ControlEnvironment } from "./web-policy";
|
|
@@ -426,7 +427,8 @@ export function companionOptions(
|
|
|
426
427
|
return {
|
|
427
428
|
appId: "ghostget",
|
|
428
429
|
name: "Ghostget",
|
|
429
|
-
title: "
|
|
430
|
+
title: "\u{1f47b}",
|
|
431
|
+
icon: TRAY_ICON,
|
|
430
432
|
tooltip: "Ghostget · control, outputs and CLI help",
|
|
431
433
|
stateDir: join(ghostgetStateHome(environment), "menubar"),
|
|
432
434
|
refreshMs: 5_000,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Ghost status mark (U+1F47B), pre-rendered at 32px from Twemoji
|
|
2
|
+
* (CC-BY 4.0 — https://twemoji.twitter.com). macOS renders the emoji title
|
|
3
|
+
* natively; icon-only tray surfaces use this art. */
|
|
4
|
+
export const TRAY_ICON = { width: 32, height: 32, rgba: "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" } as const;
|
|
@@ -10,8 +10,15 @@ export function automationPermissionOperation(operation: string): string {
|
|
|
10
10
|
throw new Error("This messaging action has no admitted host permission.");
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
const AUTOMATION_SEND_KINDS: Readonly<Record<AutomationProviderId, readonly string[]>> = Object.freeze({
|
|
14
|
+
imessage: Object.freeze(["text", "attachment", "reaction", "sticker", "link", "poll"]),
|
|
15
|
+
whatsapp: Object.freeze(["text", "attachment", "reaction", "sticker", "link", "poll"]),
|
|
16
|
+
// The pinned Beeper local transport admits exact text sends only.
|
|
17
|
+
beeper: Object.freeze(["text"]),
|
|
18
|
+
});
|
|
19
|
+
|
|
13
20
|
export function automationOperationDefinitions(provider: AutomationProviderId): readonly LocalCliPluginOperationDefinitionV1[] {
|
|
14
|
-
const names = ["messaging.automation.read", ...(provider === "whatsapp" ? ["messaging.automation.sync"] : []), ...[
|
|
21
|
+
const names = ["messaging.automation.read", ...(provider === "whatsapp" ? ["messaging.automation.sync"] : []), ...AUTOMATION_SEND_KINDS[provider].map(kind => `messaging.automation.send.${kind}`)];
|
|
15
22
|
return names.map(name => ({
|
|
16
23
|
name, contractVersion: 1, risk: name.endsWith(".read") ? "R1" : name.endsWith(".sync") ? "R2" : "R3",
|
|
17
24
|
input: { properties: {}, required: [] },
|
|
@@ -27,3 +34,4 @@ export function automationOperationDefinitions(provider: AutomationProviderId):
|
|
|
27
34
|
// pinned runtime. The owner permission kernel stays outside provider plugins.
|
|
28
35
|
export function loadImsgAutomationRuntime() { return import("./providers/imessage-automation"); }
|
|
29
36
|
export function loadWhatsAppAutomationRuntime() { return import("./providers/whatsapp-automation"); }
|
|
37
|
+
export function loadBeeperAutomationRuntime() { return import("./providers/beeper-automation"); }
|
|
@@ -4,7 +4,7 @@ import type { GhostgetAuth } from "./auth";
|
|
|
4
4
|
import { MessagingAutomationHost } from "./messaging-automation";
|
|
5
5
|
import type { AutomationActionKind, AutomationProviderId, AutomationProviderStatus, MessagingAutomationProvider } from "./messaging-automation-types";
|
|
6
6
|
import { AUTOMATION_ACTION_KINDS } from "./messaging-automation-validation";
|
|
7
|
-
import { automationPermissionOperation, loadImsgAutomationRuntime, loadWhatsAppAutomationRuntime } from "./messaging-automation-descriptors";
|
|
7
|
+
import { automationPermissionOperation, loadBeeperAutomationRuntime, loadImsgAutomationRuntime, loadWhatsAppAutomationRuntime } from "./messaging-automation-descriptors";
|
|
8
8
|
import { describeOperationPermission, type OperationPermissionDescription } from "./operation-permission";
|
|
9
9
|
import { loadProviderPluginExtensionRuntime } from "./provider-plugin";
|
|
10
10
|
import type { ProviderPluginRegistry } from "./provider-plugin-registry";
|
|
@@ -29,7 +29,7 @@ export async function createMessagingAutomationSession(options: MessagingAutomat
|
|
|
29
29
|
const starts = new Map<AutomationProviderId, (signal?: AbortSignal) => Promise<AutomationProviderStatus>>();
|
|
30
30
|
try {
|
|
31
31
|
for (const selected of options.providers) {
|
|
32
|
-
const adapterId = selected.provider === "imessage" ? "imessage-direct" : "whatsapp-web";
|
|
32
|
+
const adapterId = selected.provider === "imessage" ? "imessage-direct" : selected.provider === "beeper" ? "beeper-local" : "whatsapp-web";
|
|
33
33
|
let custody: WebSessionCleanupAdmissionController | undefined;
|
|
34
34
|
let custodyIdentity: string | undefined;
|
|
35
35
|
let poisoned = false, persistent = false, closed = false, started = false;
|
|
@@ -59,7 +59,7 @@ export async function createMessagingAutomationSession(options: MessagingAutomat
|
|
|
59
59
|
pluginImplementationHash: options.registry.implementationHash(description.resolution.binding).toString("hex"),
|
|
60
60
|
adapterId, adapterHash: description.coordinate.manifestHash, surfaceId: selected.provider,
|
|
61
61
|
authId: selected.authId, authHash: sha256(canonicalJson(auth)),
|
|
62
|
-
transport: selected.provider === "
|
|
62
|
+
transport: selected.provider === "whatsapp" ? "web-session-api" : "local-cli",
|
|
63
63
|
executionIdentityHash: implementationIdentity,
|
|
64
64
|
}, options.environment);
|
|
65
65
|
custodyIdentity = coordinate;
|
|
@@ -85,10 +85,12 @@ export async function createMessagingAutomationSession(options: MessagingAutomat
|
|
|
85
85
|
return finishing;
|
|
86
86
|
};
|
|
87
87
|
const execution = { environment: options.environment, registerCleanupBarrier };
|
|
88
|
-
const binding = options.registry.requireOperationDefinition(selected.provider === "
|
|
88
|
+
const binding = options.registry.requireOperationDefinition(selected.provider === "whatsapp" ? "linked-device" : "local-cli", selected.provider, "messaging.automation.read", 1).binding;
|
|
89
89
|
const concrete: Concrete = await loadProviderPluginExtensionRuntime(binding.loadRuntime, async () => selected.provider === "imessage"
|
|
90
90
|
? (await loadImsgAutomationRuntime()).createImsgAutomationProvider({ authorize, execution, resolveAsset: options.resolveAsset })
|
|
91
|
-
:
|
|
91
|
+
: selected.provider === "beeper"
|
|
92
|
+
? (await loadBeeperAutomationRuntime()).createBeeperAutomationProvider({ authorize, execution })
|
|
93
|
+
: (await loadWhatsAppAutomationRuntime()).createWhatsAppAutomationProvider({ authorize, execution, resolveAsset: options.resolveAsset }));
|
|
92
94
|
const call = async <T>(work: () => Promise<T>): Promise<T> => {
|
|
93
95
|
if (poisoned) throw new AutomationHostRecoveryRequired("Host is fenced");
|
|
94
96
|
if (closed) throw new Error("Host closed");
|
|
@@ -15,7 +15,7 @@ type Asset = { bytes: Uint8Array; sha256: string; expires: number; plan: string
|
|
|
15
15
|
const MAX_ASSET = 16 * 1024 * 1024;
|
|
16
16
|
const MAX_FRAME = 24 * 1024 * 1024;
|
|
17
17
|
const MAX_RESPONSE = 32 * 1024 * 1024;
|
|
18
|
-
const provider = (value: unknown): AutomationProviderId => { if (value !== "imessage" && value !== "whatsapp") throw new Error("Invalid provider"); return value; };
|
|
18
|
+
const provider = (value: unknown): AutomationProviderId => { if (value !== "imessage" && value !== "whatsapp" && value !== "beeper") throw new Error("Invalid provider"); return value; };
|
|
19
19
|
export class AutomationHostRecoveryRequired extends Error {}
|
|
20
20
|
|
|
21
21
|
/** One trusted owner connection. No agent receives this port. */
|
|
@@ -44,7 +44,7 @@ export class MessagingAutomationRpcServer {
|
|
|
44
44
|
if (method === "initialize") {
|
|
45
45
|
if (this.initialized || this.closed) throw new Error("Already initialized");
|
|
46
46
|
const r = automationRecord(raw, ["providers"]);
|
|
47
|
-
const accounts = automationArray(r.providers,
|
|
47
|
+
const accounts = automationArray(r.providers, 3).map(value => {
|
|
48
48
|
const item = automationRecord(value, ["provider", "authId"]); const authId = automationText(item.authId, 48);
|
|
49
49
|
if (!/^[a-z][a-z0-9-]{0,47}$/u.test(authId)) throw new Error("Invalid account identifier");
|
|
50
50
|
return { provider: provider(item.provider), authId };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** Closed host-side messaging contract. Provider credentials and local paths never cross it. */
|
|
2
2
|
export const MESSAGING_AUTOMATION_PROTOCOL = "ghostget.messaging-automation/1" as const;
|
|
3
3
|
|
|
4
|
-
export type AutomationProviderId = "imessage" | "whatsapp";
|
|
4
|
+
export type AutomationProviderId = "imessage" | "whatsapp" | "beeper";
|
|
5
5
|
export type AutomationActionKind = AutomationAction["kind"];
|
|
6
6
|
export type AutomationCoordinate =
|
|
7
7
|
| Readonly<{ provider: "imessage"; chatGuid: string; service: "iMessage"; observedChatRowId: number }>
|
|
8
|
-
| Readonly<{ provider: "whatsapp"; conversationJid: string }
|
|
8
|
+
| Readonly<{ provider: "whatsapp"; conversationJid: string }>
|
|
9
|
+
| Readonly<{ provider: "beeper"; accountId: string; conversationId: string }>;
|
|
9
10
|
export type AutomationIdentity = Readonly<{
|
|
10
11
|
provider: AutomationProviderId;
|
|
11
12
|
authId: string;
|
|
@@ -53,13 +53,17 @@ export function parseAutomationCoordinate(value: unknown): AutomationCoordinate
|
|
|
53
53
|
if (!chatGuid.startsWith("iMessage;") || r.service !== "iMessage") throw new Error("Only exact iMessage conversations are supported.");
|
|
54
54
|
return Object.freeze({ provider, chatGuid, service: "iMessage", observedChatRowId: automationInteger(r.observedChatRowId, 1, Number.MAX_SAFE_INTEGER) });
|
|
55
55
|
}
|
|
56
|
+
if (provider === "beeper") {
|
|
57
|
+
const r = automationRecord(value, ["provider", "accountId", "conversationId"]);
|
|
58
|
+
return Object.freeze({ provider, accountId: automationText(r.accountId, 512), conversationId: automationText(r.conversationId, 2048) });
|
|
59
|
+
}
|
|
56
60
|
const r = automationRecord(value, ["provider", "conversationJid"]);
|
|
57
61
|
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)) throw new Error("Only exact individual WhatsApp conversations are supported.");
|
|
58
62
|
return Object.freeze({ provider, conversationJid: r.conversationJid });
|
|
59
63
|
}
|
|
60
64
|
export function parseAutomationIdentity(value: unknown): AutomationIdentity {
|
|
61
65
|
const r = automationRecord(value, ["provider", "authId", "accountIdentity", "accountSubject", "implementationIdentity", "sourceGeneration"]);
|
|
62
|
-
if (r.provider !== "imessage" && r.provider !== "whatsapp") throw new Error("Messaging provider is invalid.");
|
|
66
|
+
if (r.provider !== "imessage" && r.provider !== "whatsapp" && r.provider !== "beeper") throw new Error("Messaging provider is invalid.");
|
|
63
67
|
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) });
|
|
64
68
|
}
|
|
65
69
|
export function parseAutomationActionKind(value: unknown): AutomationActionKind {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { automationOperationDefinitions } from "../../messaging-automation-descriptors";
|
|
1
2
|
import {
|
|
2
3
|
defineProviderPlugin,
|
|
3
4
|
lazyLocalCliRuntime,
|
|
@@ -250,12 +251,13 @@ const operations = Object.freeze([
|
|
|
250
251
|
operationDefinition(action, 2)),
|
|
251
252
|
operationDefinition("messaging.read", 3),
|
|
252
253
|
operationDefinition("contacts.list", 3),
|
|
254
|
+
...automationOperationDefinitions("beeper"),
|
|
253
255
|
]);
|
|
254
256
|
|
|
255
257
|
export const beeperLinkedDevicePlugin = defineProviderPlugin({
|
|
256
258
|
apiVersion: 1,
|
|
257
259
|
id: "beeper-linked-device",
|
|
258
|
-
version: "2.
|
|
260
|
+
version: "2.5.0",
|
|
259
261
|
displayName: "Beeper Pinned Local CLI",
|
|
260
262
|
sourceKind: "built-in",
|
|
261
263
|
implementationSources: providerImplementationEntry(import.meta.url),
|
|
@@ -69,6 +69,10 @@ const BEEPER_2_3_BINDING_ROUTE_COORDINATES = Object.freeze([
|
|
|
69
69
|
"local-cli:beeper/contacts.list@2",
|
|
70
70
|
"local-cli:beeper/messaging.read@3",
|
|
71
71
|
]);
|
|
72
|
+
const BEEPER_2_4_BINDING_ROUTE_COORDINATES = Object.freeze([
|
|
73
|
+
...BEEPER_2_3_BINDING_ROUTE_COORDINATES,
|
|
74
|
+
"local-cli:beeper/contacts.list@3",
|
|
75
|
+
]);
|
|
72
76
|
|
|
73
77
|
const REDDIT_1_3_BINDING_ROUTE_COORDINATES = Object.freeze([
|
|
74
78
|
"comments.create@1", "comments.read@1", "communities.membership.set@1",
|
|
@@ -84,10 +88,15 @@ const REDDIT_1_3_BINDING_ROUTE_COORDINATES = Object.freeze([
|
|
|
84
88
|
const identities = Object.freeze({
|
|
85
89
|
"beeper-linked-device": {
|
|
86
90
|
schemaVersion: 1,
|
|
87
|
-
pluginVersion: "2.
|
|
88
|
-
implementationSha256: "
|
|
91
|
+
pluginVersion: "2.5.0",
|
|
92
|
+
implementationSha256: "112abd8d9a819c03d56ac0d8d7e4b5178cde5b83b1cf41144e38c4ce42bc3945",
|
|
89
93
|
legacyCurrentReadImplementationSha256: [],
|
|
90
94
|
legacyDistributionReadImplementationSha256: [
|
|
95
|
+
{
|
|
96
|
+
implementationSha256:
|
|
97
|
+
"a989e65e372aa63af41cb36f32d6a1d769c734ba0adc207dd2f25d916f877ff5",
|
|
98
|
+
routes: [...BEEPER_2_4_BINDING_ROUTE_COORDINATES],
|
|
99
|
+
},
|
|
91
100
|
{
|
|
92
101
|
implementationSha256:
|
|
93
102
|
"2d2cef38ce2d0c193f4e6890c51d59f8a3547d9011d5c3aa8df18f5f077ebcdd",
|