@zeph-to/cli 1.27.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -29
- package/dist/cli.js +6 -1
- package/dist/crypto.d.ts +80 -55
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +178 -184
- package/dist/gate.d.ts +47 -2
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +50 -4
- package/dist/input-sequencer.d.ts +66 -0
- package/dist/input-sequencer.d.ts.map +1 -0
- package/dist/input-sequencer.js +127 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +7 -3
- package/dist/listener.d.ts +119 -2
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +549 -40
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +15 -5
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/zeph-hook.d.ts +9 -0
- package/dist/zeph-hook.d.ts.map +1 -1
- package/dist/zeph-hook.js +81 -46
- package/package.json +1 -1
package/dist/templates.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAsGA,6EAA6E;AAC7E,eAAO,MAAM,WAAW,QAItB,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,aAAa,QAAyE,CAAC;AAEpG,sDAAsD;AACtD,eAAO,MAAM,WAAW,QAAyE,CAAC;AAElG,oDAAoD;AACpD,eAAO,MAAM,UAAU,QAAyE,CAAC;AAEjG,oFAAoF;AACpF,eAAO,MAAM,YAAY,QAAyE,CAAC;AAEnG,gEAAgE;AAChE,eAAO,MAAM,UAAU,QAAkE,CAAC;AAE1F,4FAA4F;AAC5F,eAAO,MAAM,UAAU,QAAkE,CAAC;AAI1F,eAAO,MAAM,YAAY,QAKd,CAAC;AAEZ,eAAO,MAAM,cAAc,QAOhB,CAAC;AAEZ,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;CAuBxB,CAAC;AAYF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;CASvB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO,KAAG,OAUhD,CAAC;AAEF,eAAO,MAAM,aAAa,QASf,CAAC;AASZ,eAAO,MAAM,eAAe,oFAA+E,CAAC;AAC5G,eAAO,MAAM,aAAa,sBAAsB,CAAC;AAQjD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,KAAG,MAWnE,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,KAAG,MAOrD,CAAC"}
|
package/dist/templates.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
// and the agents can't drift apart.
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.removeManagedBlock = exports.upsertManagedBlock = exports.ZEPH_MARK_END = exports.ZEPH_MARK_START = exports.COPILOT_HOOKS = exports.isZephHookGroup = exports.CODEX_HOOKS = exports.GEMINI_HOOKS = exports.WINDSURF_HOOKS = exports.CURSOR_HOOKS = exports.AIDER_RULE = exports.CLINE_RULE = exports.COPILOT_RULE = exports.CODEX_RULE = exports.GEMINI_RULE = exports.WINDSURF_RULE = exports.CURSOR_RULE = void 0;
|
|
22
|
+
const gate_js_1 = require("./gate.js");
|
|
22
23
|
const zeph_core_generated_js_1 = require("./zeph-core.generated.js");
|
|
23
24
|
// Graceful resolution: prefer the installed `zeph` CLI, but fall back to
|
|
24
25
|
// `npx -y @zeph-to/cli` so the hook still fires when the user
|
|
@@ -27,11 +28,20 @@ const zeph_core_generated_js_1 = require("./zeph-core.generated.js");
|
|
|
27
28
|
// pattern in plugin/hooks/zeph-{stop,ask}.sh.
|
|
28
29
|
//
|
|
29
30
|
// `--auto` applies the shared push-gate before sending (see src/gate.ts):
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
31
|
+
// the push still fires (gate defaults assume real work), and the
|
|
32
|
+
// /zeph-quiet | /zeph-loud dial works for every hook-driven agent.
|
|
33
|
+
//
|
|
34
|
+
// `--pushmode-default normal` is what keeps the first half of that true. The
|
|
35
|
+
// built-in default for a project with no dial is quiet, and quiet only lets a
|
|
36
|
+
// `high` Push Signal marker through — a marker these hooks have no way to
|
|
37
|
+
// emit. Without the flag they would install and then never push again. The
|
|
38
|
+
// user's own dial still outranks it, so /zeph-quiet keeps working here.
|
|
39
|
+
//
|
|
40
|
+
// Older installed `zeph` versions parse both flags as unknown booleans and
|
|
41
|
+
// ignore them — graceful backward compatibility, and for `--pushmode-default`
|
|
42
|
+
// specifically the old build's behavior was already "normal when no dial".
|
|
43
|
+
const NOTIFY_CMD = '$(command -v zeph || echo "npx -y @zeph-to/cli") notify --title "Task done" --auto '
|
|
44
|
+
+ `--${gate_js_1.PUSHMODE_DEFAULT_FLAG} normal 2>/dev/null || true`;
|
|
35
45
|
// Prompt-submit hook command — remote-origin detection (ADR-0002). Reads
|
|
36
46
|
// the hook JSON on stdin and prints additionalContext JSON on a marker
|
|
37
47
|
// match (see src/remote-hook.ts). stdout IS the hook response, so only
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACzC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;oEAEgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;CACxD;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACzC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;oEAEgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;CACxD;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH"}
|
package/dist/zeph-hook.d.ts
CHANGED
|
@@ -5,6 +5,15 @@ export declare class ZephHook {
|
|
|
5
5
|
private readonly timeoutMs;
|
|
6
6
|
private cryptoInitialized;
|
|
7
7
|
constructor(options: ZephOptions);
|
|
8
|
+
/**
|
|
9
|
+
* Resolve who this push can be encrypted for, or null when it cannot be.
|
|
10
|
+
*
|
|
11
|
+
* The device list is fetched per send rather than cached: a phone that
|
|
12
|
+
* registered its key a minute ago must be able to read the next push, and a
|
|
13
|
+
* long-lived listener would otherwise keep wrapping for a stale set.
|
|
14
|
+
* Failures here are not fatal — plaintext the user can read beats a
|
|
15
|
+
* notification that never arrives.
|
|
16
|
+
*/
|
|
8
17
|
private ensureCrypto;
|
|
9
18
|
/**
|
|
10
19
|
* A push must not be lost because E2E turned out to be unavailable.
|
package/dist/zeph-hook.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zeph-hook.d.ts","sourceRoot":"","sources":["../src/zeph-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAY,gBAAgB,EAAE,gBAAgB,EAAoB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAoCxL,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,OAAO,EAAE,WAAW;
|
|
1
|
+
{"version":3,"file":"zeph-hook.d.ts","sourceRoot":"","sources":["../src/zeph-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAY,gBAAgB,EAAE,gBAAgB,EAAoB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAoCxL,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,OAAO,EAAE,WAAW;IAShC;;;;;;;;OAQG;YACW,YAAY;IAyB1B;;;;;;;;;;;OAWG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;YAgB7C,UAAU;YAyCV,cAAc;IA6DtB,aAAa,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAK7G,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrF,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAuB9C,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAKlD,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAK7C;;;;OAIG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;YASxF,OAAO;IAiCrB,OAAO,CAAC,UAAU;CASnB"}
|
package/dist/zeph-hook.js
CHANGED
|
@@ -50,17 +50,39 @@ class ZephHook {
|
|
|
50
50
|
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
51
51
|
this.timeoutMs = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve who this push can be encrypted for, or null when it cannot be.
|
|
55
|
+
*
|
|
56
|
+
* The device list is fetched per send rather than cached: a phone that
|
|
57
|
+
* registered its key a minute ago must be able to read the next push, and a
|
|
58
|
+
* long-lived listener would otherwise keep wrapping for a stale set.
|
|
59
|
+
* Failures here are not fatal — plaintext the user can read beats a
|
|
60
|
+
* notification that never arrives.
|
|
61
|
+
*/
|
|
53
62
|
async ensureCrypto() {
|
|
54
|
-
if (this.cryptoInitialized)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
if (!this.cryptoInitialized) {
|
|
64
|
+
try {
|
|
65
|
+
await (0, crypto_js_1.initCrypto)(this.apiKey, this.baseUrl);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// fall through — getKeyPair() stays null and we send plaintext
|
|
69
|
+
}
|
|
58
70
|
this.cryptoInitialized = true;
|
|
59
|
-
return !!(0, crypto_js_1.getKeyPair)();
|
|
60
71
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
if (!(0, crypto_js_1.getKeyPair)())
|
|
73
|
+
return null;
|
|
74
|
+
try {
|
|
75
|
+
const json = await this.request('GET', '/devices');
|
|
76
|
+
const recipients = (0, crypto_js_1.selectRecipients)(json.data ?? []);
|
|
77
|
+
if (recipients.length === 0) {
|
|
78
|
+
console.error('[Crypto] No device has a per-device public key — sending plaintext.');
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
return recipients;
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
console.error('[Crypto] Could not list devices, sending plaintext:', err);
|
|
85
|
+
return null;
|
|
64
86
|
}
|
|
65
87
|
}
|
|
66
88
|
/**
|
|
@@ -99,19 +121,23 @@ class ZephHook {
|
|
|
99
121
|
// carry it. Explicit caller values win.
|
|
100
122
|
if (agentCtx)
|
|
101
123
|
payload = { ...agentCtx, ...payload };
|
|
102
|
-
const
|
|
124
|
+
const recipients = await this.ensureCrypto();
|
|
103
125
|
const body = payload.body;
|
|
104
126
|
const bodyBytes = body ? new TextEncoder().encode(body).byteLength : 0;
|
|
105
127
|
const isLongBody = bodyBytes > BODY_FILE_THRESHOLD;
|
|
106
128
|
if (isLongBody && body) {
|
|
107
|
-
return this.notifyWithFile(payload, body, bodyBytes,
|
|
129
|
+
return this.notifyWithFile(payload, body, bodyBytes, recipients);
|
|
108
130
|
}
|
|
109
131
|
// Encrypt push body if possible
|
|
110
132
|
let sendPayload = { ...payload };
|
|
111
|
-
if (
|
|
133
|
+
if (recipients) {
|
|
112
134
|
try {
|
|
113
|
-
const enc = await (0, crypto_js_1.
|
|
114
|
-
|
|
135
|
+
const enc = await (0, crypto_js_1.encryptPushBodyForDevices)({ title: payload.title, body: payload.body, url: payload.url }, recipients);
|
|
136
|
+
// `url: undefined` alongside the title: it is already sealed inside the
|
|
137
|
+
// ciphertext, and on a link push it IS the payload. Spreading `payload`
|
|
138
|
+
// above left the plaintext copy at the top level, handing the server the
|
|
139
|
+
// one thing `isEncrypted` promises it cannot see.
|
|
140
|
+
sendPayload = { ...sendPayload, title: undefined, url: undefined, body: enc.body, isEncrypted: true, deviceKeyMap: enc.deviceKeyMap, senderPublicKey: enc.senderPublicKey };
|
|
115
141
|
}
|
|
116
142
|
catch (err) {
|
|
117
143
|
console.error('[Crypto] Push encryption failed, sending plaintext:', err);
|
|
@@ -124,46 +150,51 @@ class ZephHook {
|
|
|
124
150
|
}
|
|
125
151
|
return { pushId };
|
|
126
152
|
}
|
|
127
|
-
async notifyWithFile(payload, body, fileSize,
|
|
153
|
+
async notifyWithFile(payload, body, fileSize, recipients) {
|
|
128
154
|
const fileName = 'response.md';
|
|
129
|
-
|
|
130
|
-
// Encrypt
|
|
131
|
-
let
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
let
|
|
135
|
-
if (
|
|
155
|
+
const preview = body.length > PREVIEW_LENGTH ? body.slice(0, PREVIEW_LENGTH) + '...' : body;
|
|
156
|
+
// Encrypt the attachment and the push body together, before anything is
|
|
157
|
+
// uploaded. Doing them one at a time around the upload let a failure land
|
|
158
|
+
// in between and ship ciphertext under a push with no `isEncrypted` — an
|
|
159
|
+
// attachment no client would even try to open.
|
|
160
|
+
let encrypted = null;
|
|
161
|
+
if (recipients) {
|
|
136
162
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
fileIv = encrypted.iv;
|
|
142
|
-
fileEncryptedKey = encrypted.encryptedKey;
|
|
163
|
+
encrypted = {
|
|
164
|
+
file: await (0, crypto_js_1.encryptFileForDevices)(body, recipients),
|
|
165
|
+
push: await (0, crypto_js_1.encryptPushBodyForDevices)({ title: payload.title, body: preview, url: payload.url }, recipients),
|
|
166
|
+
};
|
|
143
167
|
}
|
|
144
168
|
catch (err) {
|
|
145
|
-
console.error('[Crypto]
|
|
169
|
+
console.error('[Crypto] Encryption failed, sending plaintext:', err);
|
|
146
170
|
}
|
|
147
171
|
}
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
172
|
+
const uploadContent = encrypted?.file.ciphertext ?? body;
|
|
173
|
+
const uploadType = encrypted ? 'application/octet-stream' : inferMimeType(fileName);
|
|
174
|
+
const uploadSize = encrypted ? encrypted.file.ciphertext.length : fileSize;
|
|
175
|
+
const upload = await this.requestUpload({ fileName, fileType: uploadType, fileSize: uploadSize });
|
|
176
|
+
await this.uploadToS3(upload.uploadUrl, uploadContent, uploadType);
|
|
177
|
+
const sendPayload = {
|
|
153
178
|
...payload,
|
|
154
|
-
|
|
179
|
+
title: encrypted ? undefined : payload.title,
|
|
180
|
+
body: encrypted ? encrypted.push.body : preview,
|
|
155
181
|
type: payload.type ?? 'file',
|
|
156
|
-
files: [{
|
|
182
|
+
files: [{
|
|
183
|
+
fileKey: upload.fileKey,
|
|
184
|
+
fileName,
|
|
185
|
+
fileSize,
|
|
186
|
+
fileType: inferMimeType(fileName),
|
|
187
|
+
iv: encrypted?.file.iv,
|
|
188
|
+
deviceKeyMap: encrypted?.file.deviceKeyMap,
|
|
189
|
+
}],
|
|
190
|
+
...(encrypted && {
|
|
191
|
+
// See notifyOnce: the url must not survive in the clear either.
|
|
192
|
+
url: undefined,
|
|
193
|
+
isEncrypted: true,
|
|
194
|
+
deviceKeyMap: encrypted.push.deviceKeyMap,
|
|
195
|
+
senderPublicKey: encrypted.push.senderPublicKey,
|
|
196
|
+
}),
|
|
157
197
|
};
|
|
158
|
-
if (canEncrypt) {
|
|
159
|
-
try {
|
|
160
|
-
const enc = await (0, crypto_js_1.encryptPushBodyForSelf)({ title: payload.title, body: preview, url: payload.url });
|
|
161
|
-
sendPayload = { ...sendPayload, title: undefined, body: enc.body, isEncrypted: true, encryptedKey: enc.encryptedKey, senderPublicKey: enc.senderPublicKey };
|
|
162
|
-
}
|
|
163
|
-
catch (err) {
|
|
164
|
-
console.error('[Crypto] Push encryption failed, sending plaintext:', err);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
198
|
const json = await this.request('POST', '/pushes/send', sendPayload);
|
|
168
199
|
const pushId = json.data?.pushId;
|
|
169
200
|
if (!pushId) {
|
|
@@ -199,8 +230,12 @@ class ZephHook {
|
|
|
199
230
|
const pushes = json.data.map((p) => ({
|
|
200
231
|
pushId: p.pushId,
|
|
201
232
|
type: p.type,
|
|
202
|
-
title
|
|
203
|
-
body:
|
|
233
|
+
// An encrypted push carries no plaintext title and a JSON envelope in
|
|
234
|
+
// `body`; slicing that renders `{"ciphertext":"AAAB...` as if it were the
|
|
235
|
+
// message. This host cannot decrypt (send-only), so say so instead.
|
|
236
|
+
title: p.isEncrypted ? undefined : p.title,
|
|
237
|
+
body: p.isEncrypted ? '[encrypted]' : p.body?.slice(0, 100),
|
|
238
|
+
isEncrypted: p.isEncrypted,
|
|
204
239
|
createdAt: p.createdAt,
|
|
205
240
|
}));
|
|
206
241
|
return { pushes, count: pushes.length, hasMore: json.pagination?.hasMore ?? false };
|