Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ Historical entries retain their original delivery coordinates.

## Unreleased

## 0.18.71

Automate selected group conversations in Beeper, iMessage, and WhatsApp.

- Let clients discover and enroll a group with its exact account and participant
list. Direct conversations remain the default.
- Begin each group enrollment with new messages, excluding earlier history.
- Stop an enrollment and revoke its sends when the account, conversation, or
participant list changes, including changes observed before a restart.
- Recheck the group before sending and require a new enrollment after a change.
- Keep analytics preferences compact and their panel inside the viewport.
- Use one product navigation row across marketing pages and documentation.

## 0.18.70

Preserve provider text when projecting profile and rental listing data.
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Install [Bun 1.3.14](https://bun.sh/docs/installation) if needed, then install
GhostGet and read a public page:

```sh
bun add --global https://github.com/hraness/ghostget/releases/download/v0.18.70/hraness-ghostget-0.18.70.tgz
bun add --global https://github.com/hraness/ghostget/releases/download/v0.18.71/hraness-ghostget-0.18.71.tgz
ghostget read https://example.com
```

Expand All @@ -49,9 +49,9 @@ which always names the latest published release. Upgrading from Wrench? Read the
The optional Agent Skill teaches your agent when and how to use GhostGet:

```sh
npx skills add hraness/ghostget#v0.18.70
npx skills add hraness/ghostget#v0.18.71
# With Bun instead:
bunx skills add hraness/ghostget#v0.18.70
bunx skills add hraness/ghostget#v0.18.71
```

Start a new agent session, then ask: “Use GhostGet to read https://example.com
Expand Down Expand Up @@ -183,7 +183,7 @@ firewall.

## Built-in provider catalog

This v0.18.70 source tree supports executable actions for 21 services: Beeper,
This v0.18.71 source tree supports executable actions for 21 services: Beeper,
Bluesky, ClasificadosOnline, Facebook, Facebook Groups, Facebook Marketplace,
GitHub, Gmail, Hacker News, Instagram, iMessage, LinkedIn, Reddit, Substack,
Threads, TikTok, Twitch, WebMCP Registry, WhatsApp, X, and YouTube. LinkedIn
Expand Down Expand Up @@ -267,7 +267,7 @@ For that same released coordinate, install GhostGet in an agent or application
that owns its own model, planning, tool loop, approvals, and interface:

```sh
bun add https://github.com/hraness/ghostget/releases/download/v0.18.70/hraness-ghostget-0.18.70.tgz
bun add https://github.com/hraness/ghostget/releases/download/v0.18.71/hraness-ghostget-0.18.71.tgz
```

```ts
Expand Down
9 changes: 9 additions & 0 deletions costs.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
{
"version": 1,
"surfaces": {
"local:messaging-group-history-exclusions": {
"kind": "authoritative",
"retention": "tombstone",
"owner": "src/messaging-automation.ts",
"source": "Enrollment-scoped IDs of baseline, historical, or future-dated group messages; no message bodies",
"deletion": "src/messaging-automation.ts",
"expiry": "Retained with the enrollment; never evicted independently or copied to another enrollment",
"budget": { "maxEntries": 50000, "maxBytesPerMessageId": 256, "maxJournalBytes": 201326592 }
},
"local:contract-repair-inbox": {
"kind": "derived",
"retention": "ttl:P30D",
Expand Down
2 changes: 1 addition & 1 deletion dist/apple-photos-client.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @bun
import {
GHOSTGET_VERSION
} from "./index-4bq0p2rh.js";
} from "./index-k6mpf68q.js";
import {
canonicalJson,
sha256
Expand Down
2 changes: 1 addition & 1 deletion dist/beeper-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from "./index-26yq8q16.js";
import {
GHOSTGET_VERSION
} from "./index-4bq0p2rh.js";
} from "./index-k6mpf68q.js";
import {
canonicalJson,
canonicalJsonSha256Matches,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import {
ensureImsgNativeResources,
verifyImsgNativeResources
} from "./index-ptenxvf8.js";
} from "./index-pngvnj0q.js";
import {
ensurePrivateStateDirectory,
ghostgetStateHome
Expand Down
5 changes: 0 additions & 5 deletions dist/index-01eeae9e.js

This file was deleted.

28 changes: 25 additions & 3 deletions dist/index-42adge2e.js → dist/index-cs9v6bqx.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// src/messaging-automation-validation.ts
import { types } from "util";
var AUTOMATION_ACTION_KINDS = Object.freeze(["text", "attachment", "reaction", "sticker", "link", "poll", "app-clip", "experience"]);
var AUTOMATION_WHATSAPP_GROUP_JID = /^[1-9][0-9]{4,19}(?:-[1-9][0-9]{0,19})?@g\.us$/u;
function automationRecord(value, keys) {
if (types.isProxy(value) || typeof value !== "object" || value === null || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
throw new Error("Messaging automation value must be a plain object.");
Expand Down Expand Up @@ -72,10 +73,31 @@ function parseAutomationCoordinate(value) {
return Object.freeze({ provider, accountId: automationText(r2.accountId, 512), conversationId: automationText(r2.conversationId, 2048) });
}
const r = automationRecord(value, ["provider", "conversationJid"]);
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.");
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) && !AUTOMATION_WHATSAPP_GROUP_JID.test(r.conversationJid))
throw new Error("Only exact WhatsApp conversation JIDs are supported.");
return Object.freeze({ provider, conversationJid: r.conversationJid });
}
function parseAutomationConversation(value) {
const r = automationRecord(value, ["coordinate", "title", "kind", "participants"]);
const coordinate = parseAutomationCoordinate(r.coordinate);
if (r.kind !== "single" && r.kind !== "group")
throw new Error("Messaging automation requires a verified conversation kind.");
const participants = automationArray(r.participants, r.kind === "group" ? 500 : 2).map((value2) => automationText(value2, 512));
if (participants.length < 1 || new Set(participants).size !== participants.length)
throw new Error("Messaging automation requires a complete distinct participant roster.");
if (coordinate.provider === "whatsapp" && AUTOMATION_WHATSAPP_GROUP_JID.test(coordinate.conversationJid) !== (r.kind === "group"))
throw new Error("WhatsApp conversation kind and exact JID disagree.");
return Object.freeze({ coordinate, title: r.title === null ? null : automationText(r.title, 512), kind: r.kind, participants: Object.freeze([...participants].sort()) });
}
function automationGroupOptions(value, keys) {
if (types.isProxy(value))
throw new Error("Messaging automation value must be a plain object.");
const present = value !== null && typeof value === "object" && Object.hasOwn(value, "includeGroups");
const r = automationRecord(value, present ? [...keys, "includeGroups"] : keys);
if (present && typeof r.includeGroups !== "boolean")
throw new Error("Messaging group selection must be boolean.");
return { ...r, includeGroups: present ? r.includeGroups : false };
}
function parseAutomationIdentity(value) {
const r = automationRecord(value, ["provider", "authId", "accountIdentity", "accountSubject", "implementationIdentity", "sourceGeneration"]);
if (r.provider !== "imessage" && r.provider !== "whatsapp" && r.provider !== "beeper")
Expand Down Expand Up @@ -308,4 +330,4 @@ class OperationDeadline {
}
}

export { OperationDeadlineError, AUTOMATION_ACTION_KINDS, automationRecord, automationText, automationId, automationDigest, automationInstant, automationInteger, automationArray, automationDate, parseAutomationCoordinate, parseAutomationIdentity, parseAutomationActionKind, parseAutomationAction, parseAutomationMessage };
export { OperationDeadlineError, AUTOMATION_ACTION_KINDS, AUTOMATION_WHATSAPP_GROUP_JID, automationRecord, automationText, automationId, automationDigest, automationInstant, automationInteger, automationArray, automationDate, parseAutomationCoordinate, parseAutomationConversation, automationGroupOptions, parseAutomationIdentity, parseAutomationActionKind, parseAutomationAction, parseAutomationMessage };
2 changes: 1 addition & 1 deletion dist/index-4bq0p2rh.js → dist/index-k6mpf68q.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @bun
// src/version.ts
var GHOSTGET_VERSION = "0.18.70";
var GHOSTGET_VERSION = "0.18.71";

export { GHOSTGET_VERSION };
17 changes: 17 additions & 0 deletions dist/index-m84wqtz6.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// @bun
// src/messaging-automation-types.ts
var MESSAGING_AUTOMATION_PROTOCOL = "ghostget.messaging-automation/1";
var AUTOMATION_BINDING_CHANGED_REASON = "ghostget.binding-changed.v1";

class AutomationGroupBindingChangedError extends Error {
identity;
coordinate;
constructor(identity, coordinate) {
super(AUTOMATION_BINDING_CHANGED_REASON);
this.identity = identity;
this.coordinate = coordinate;
this.name = "AutomationGroupBindingChangedError";
}
}

export { MESSAGING_AUTOMATION_PROTOCOL, AUTOMATION_BINDING_CHANGED_REASON, AutomationGroupBindingChangedError };
12 changes: 6 additions & 6 deletions dist/index-ptenxvf8.js → dist/index-pngvnj0q.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ var MESSAGING_NATIVE_ARTIFACTS = Object.freeze({
},
whatsapp: {
file: "wacli-darwin-arm64.gz",
sha256: "9b77ffb810d028fde725ca02b1451f1725b5ff5312a46a54468a9a38533d4cea",
bytes: 21963810,
compressedSha256: "1c1650d6c79b74db7f8f335b4746398c802031260a90468312dcaac0374a5166",
compressedBytes: 7682949
sha256: "85a4c2b6f538103df08425f75984657559169a95161a256c6d096daf9de38f47",
bytes: 21980338,
compressedSha256: "61c9aef8d1a2c38f4831e8546fea0ae9907c365a301b8cde7388e47adbb2d697",
compressedBytes: 7694763
},
phoneMetadata: {
file: "phone-number-metadata.json.gz",
Expand Down Expand Up @@ -217,11 +217,11 @@ async function installBundledMessagingRuntime(provider, environment = process.en
await file.close();
}
if (provider === "imessage") {
const { installReviewedImsgBinary } = await import("./imessage-direct-install-5fbzef0t.js");
const { installReviewedImsgBinary } = await import("./imessage-direct-install-rc7txgp5.js");
const result = await installReviewedImsgBinary(path, environment);
return { version: result.version, sha256: result.executableSha256, alreadyPresent: result.alreadyPresent };
}
const { installReviewedWhatsAppAutomationBinary } = await import("./whatsapp-automation-runtime-xc7yvjsj.js");
const { installReviewedWhatsAppAutomationBinary } = await import("./whatsapp-automation-runtime-2v9x3j80.js");
return await installReviewedWhatsAppAutomationBinary(path, environment);
} finally {
await unlink(path);
Expand Down
12 changes: 8 additions & 4 deletions dist/messaging-automation-api.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
// @bun
import {
AUTOMATION_BINDING_CHANGED_REASON,
AutomationGroupBindingChangedError,
MESSAGING_AUTOMATION_PROTOCOL
} from "./index-01eeae9e.js";
} from "./index-m84wqtz6.js";
import {
__require
} from "./index-z1w83f81.js";

// src/messaging-automation-api.ts
async function createMessagingAutomationHost(providers, environment) {
const { MessagingAutomationHost } = await import("./messaging-automation-2ybn110m.js");
const { MessagingAutomationHost } = await import("./messaging-automation-tn4gf00y.js");
return new MessagingAutomationHost(providers, environment);
}
async function installBundledMessagingRuntime(provider, environment) {
return (await import("./messaging-native-install-gmczhw4b.js")).installBundledMessagingRuntime(provider, environment);
return (await import("./messaging-native-install-a1jy4mse.js")).installBundledMessagingRuntime(provider, environment);
}
export {
installBundledMessagingRuntime,
createMessagingAutomationHost,
MESSAGING_AUTOMATION_PROTOCOL
MESSAGING_AUTOMATION_PROTOCOL,
AutomationGroupBindingChangedError,
AUTOMATION_BINDING_CHANGED_REASON
};
Loading
Loading