@zeph-to/cli 1.12.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.
Files changed (51) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +499 -0
  3. package/dist/agents.d.ts +8 -0
  4. package/dist/agents.d.ts.map +1 -0
  5. package/dist/agents.js +29 -0
  6. package/dist/check-update.d.ts +4 -0
  7. package/dist/check-update.d.ts.map +1 -0
  8. package/dist/check-update.js +80 -0
  9. package/dist/cli.d.ts +3 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +374 -0
  12. package/dist/config.d.ts +14 -0
  13. package/dist/config.d.ts.map +1 -0
  14. package/dist/config.js +36 -0
  15. package/dist/crypto.d.ts +82 -0
  16. package/dist/crypto.d.ts.map +1 -0
  17. package/dist/crypto.js +291 -0
  18. package/dist/errors.d.ts +12 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +28 -0
  21. package/dist/index.d.ts +4 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +9 -0
  24. package/dist/installer.d.ts +14 -0
  25. package/dist/installer.d.ts.map +1 -0
  26. package/dist/installer.js +464 -0
  27. package/dist/listener.d.ts +126 -0
  28. package/dist/listener.d.ts.map +1 -0
  29. package/dist/listener.js +1008 -0
  30. package/dist/login.d.ts +38 -0
  31. package/dist/login.d.ts.map +1 -0
  32. package/dist/login.js +182 -0
  33. package/dist/templates.d.ts +44 -0
  34. package/dist/templates.d.ts.map +1 -0
  35. package/dist/templates.js +257 -0
  36. package/dist/types.d.ts +54 -0
  37. package/dist/types.d.ts.map +1 -0
  38. package/dist/types.js +2 -0
  39. package/dist/uninstall.d.ts +2 -0
  40. package/dist/uninstall.d.ts.map +1 -0
  41. package/dist/uninstall.js +217 -0
  42. package/dist/verify.d.ts +2 -0
  43. package/dist/verify.d.ts.map +1 -0
  44. package/dist/verify.js +109 -0
  45. package/dist/wrapper.d.ts +26 -0
  46. package/dist/wrapper.d.ts.map +1 -0
  47. package/dist/wrapper.js +238 -0
  48. package/dist/zeph-hook.d.ts +23 -0
  49. package/dist/zeph-hook.d.ts.map +1 -0
  50. package/dist/zeph-hook.js +196 -0
  51. package/package.json +75 -0
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ZephHook = void 0;
4
+ const errors_js_1 = require("./errors.js");
5
+ const crypto_js_1 = require("./crypto.js");
6
+ const DEFAULT_BASE_URL = 'https://api.zeph.to/v1';
7
+ const DEFAULT_TIMEOUT_MS = 30_000;
8
+ const BODY_FILE_THRESHOLD = 512;
9
+ const PREVIEW_LENGTH = 200;
10
+ const inferMimeType = (fileName) => {
11
+ const ext = fileName.split('.').pop()?.toLowerCase();
12
+ const map = { md: 'text/markdown', txt: 'text/plain', json: 'application/json' };
13
+ return map[ext ?? ''] ?? 'text/plain';
14
+ };
15
+ class ZephHook {
16
+ apiKey;
17
+ baseUrl;
18
+ timeoutMs;
19
+ cryptoInitialized = false;
20
+ constructor(options) {
21
+ if (!options.apiKey) {
22
+ throw new errors_js_1.ZephError('apiKey is required', 'INVALID_OPTIONS', 400);
23
+ }
24
+ this.apiKey = options.apiKey;
25
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, '');
26
+ this.timeoutMs = options.timeout ?? DEFAULT_TIMEOUT_MS;
27
+ }
28
+ async ensureCrypto() {
29
+ if (this.cryptoInitialized)
30
+ return !!(0, crypto_js_1.getKeyPair)();
31
+ try {
32
+ await (0, crypto_js_1.initCrypto)(this.apiKey, this.baseUrl);
33
+ this.cryptoInitialized = true;
34
+ return !!(0, crypto_js_1.getKeyPair)();
35
+ }
36
+ catch {
37
+ this.cryptoInitialized = true;
38
+ return false;
39
+ }
40
+ }
41
+ async notify(payload) {
42
+ const canEncrypt = await this.ensureCrypto();
43
+ const body = payload.body;
44
+ const bodyBytes = body ? new TextEncoder().encode(body).byteLength : 0;
45
+ const isLongBody = bodyBytes > BODY_FILE_THRESHOLD;
46
+ if (isLongBody && body) {
47
+ return this.notifyWithFile(payload, body, bodyBytes, canEncrypt);
48
+ }
49
+ // Encrypt push body if possible
50
+ let sendPayload = { ...payload };
51
+ if (canEncrypt) {
52
+ try {
53
+ const enc = await (0, crypto_js_1.encryptPushBodyForSelf)({ title: payload.title, body: payload.body, url: payload.url });
54
+ sendPayload = { ...sendPayload, title: undefined, body: enc.body, isEncrypted: true, encryptedKey: enc.encryptedKey, senderPublicKey: enc.senderPublicKey };
55
+ }
56
+ catch (err) {
57
+ console.error('[Crypto] Push encryption failed, sending plaintext:', err);
58
+ }
59
+ }
60
+ const json = await this.request('POST', '/pushes/send', sendPayload);
61
+ const pushId = json.data?.pushId;
62
+ if (!pushId) {
63
+ throw new errors_js_1.ZephError('Server returned no pushId', 'INVALID_RESPONSE', 500);
64
+ }
65
+ return { pushId };
66
+ }
67
+ async notifyWithFile(payload, body, fileSize, canEncrypt) {
68
+ const fileName = 'response.md';
69
+ let fileType = inferMimeType(fileName);
70
+ // Encrypt file content if possible
71
+ let uploadContent = body;
72
+ let uploadSize = fileSize;
73
+ let fileIv;
74
+ let fileEncryptedKey;
75
+ if (canEncrypt) {
76
+ try {
77
+ const encrypted = await (0, crypto_js_1.encryptFileForSelf)(body);
78
+ uploadContent = encrypted.ciphertext;
79
+ uploadSize = encrypted.ciphertext.length;
80
+ fileType = 'application/octet-stream';
81
+ fileIv = encrypted.iv;
82
+ fileEncryptedKey = encrypted.encryptedKey;
83
+ }
84
+ catch (err) {
85
+ console.error('[Crypto] File encryption failed, sending plaintext:', err);
86
+ }
87
+ }
88
+ const upload = await this.requestUpload({ fileName, fileType, fileSize: uploadSize });
89
+ await this.uploadToS3(upload.uploadUrl, uploadContent, fileType);
90
+ const preview = body.length > PREVIEW_LENGTH ? body.slice(0, PREVIEW_LENGTH) + '...' : body;
91
+ // Encrypt push body
92
+ let sendPayload = {
93
+ ...payload,
94
+ body: preview,
95
+ type: payload.type ?? 'file',
96
+ files: [{ fileKey: upload.fileKey, fileName, fileSize, fileType: inferMimeType(fileName), iv: fileIv, encryptedKey: fileEncryptedKey }],
97
+ };
98
+ if (canEncrypt) {
99
+ try {
100
+ const enc = await (0, crypto_js_1.encryptPushBodyForSelf)({ title: payload.title, body: preview, url: payload.url });
101
+ sendPayload = { ...sendPayload, title: undefined, body: enc.body, isEncrypted: true, encryptedKey: enc.encryptedKey, senderPublicKey: enc.senderPublicKey };
102
+ }
103
+ catch (err) {
104
+ console.error('[Crypto] Push encryption failed, sending plaintext:', err);
105
+ }
106
+ }
107
+ const json = await this.request('POST', '/pushes/send', sendPayload);
108
+ const pushId = json.data?.pushId;
109
+ if (!pushId) {
110
+ throw new errors_js_1.ZephError('Server returned no pushId', 'INVALID_RESPONSE', 500);
111
+ }
112
+ return { pushId, fileKey: upload.fileKey, autoFile: true };
113
+ }
114
+ async requestUpload(params) {
115
+ const json = await this.request('POST', '/files/upload-request', params);
116
+ return json.data;
117
+ }
118
+ async uploadToS3(url, content, contentType) {
119
+ const isText = typeof content === 'string';
120
+ const body = isText ? content : new Uint8Array(content);
121
+ const response = await fetch(url, {
122
+ method: 'PUT',
123
+ headers: { 'Content-Type': isText ? `${contentType}; charset=utf-8` : contentType },
124
+ body,
125
+ signal: AbortSignal.timeout(this.timeoutMs),
126
+ });
127
+ if (!response.ok) {
128
+ throw new errors_js_1.ZephError(`S3 upload failed with status ${response.status}`, 'UPLOAD_FAILED', response.status);
129
+ }
130
+ }
131
+ async list(params) {
132
+ const query = new URLSearchParams();
133
+ if (params?.limit)
134
+ query.set('limit', String(params.limit));
135
+ if (params?.type)
136
+ query.set('type', params.type);
137
+ const qs = query.toString();
138
+ const json = await this.request('GET', `/pushes${qs ? `?${qs}` : ''}`);
139
+ const pushes = json.data.map((p) => ({
140
+ pushId: p.pushId,
141
+ type: p.type,
142
+ title: p.title,
143
+ body: p.body?.slice(0, 100),
144
+ createdAt: p.createdAt,
145
+ }));
146
+ return { pushes, count: pushes.length, hasMore: json.pagination?.hasMore ?? false };
147
+ }
148
+ async dismiss(pushId) {
149
+ await this.request('POST', `/pushes/${encodeURIComponent(pushId)}/dismiss`);
150
+ return { dismissed: true };
151
+ }
152
+ async dismissAll() {
153
+ const json = await this.request('POST', '/pushes/dismiss-all');
154
+ return { dismissed: json.data?.dismissed ?? 0 };
155
+ }
156
+ async request(method, path, body) {
157
+ const controller = new AbortController();
158
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
159
+ const headers = { 'X-API-Key': this.apiKey };
160
+ if (body)
161
+ headers['Content-Type'] = 'application/json';
162
+ let response;
163
+ try {
164
+ response = await fetch(`${this.baseUrl}${path}`, {
165
+ method,
166
+ headers,
167
+ body: body ? JSON.stringify(body) : undefined,
168
+ signal: controller.signal,
169
+ });
170
+ }
171
+ catch (err) {
172
+ if (err instanceof DOMException && err.name === 'AbortError') {
173
+ throw new errors_js_1.ZephError(`Request timed out after ${this.timeoutMs}ms`, 'TIMEOUT', 408);
174
+ }
175
+ throw err;
176
+ }
177
+ finally {
178
+ clearTimeout(timer);
179
+ }
180
+ const json = await response.json();
181
+ if (!response.ok) {
182
+ throw this.parseError(response.status, json);
183
+ }
184
+ return json;
185
+ }
186
+ parseError(status, body) {
187
+ const message = body.error?.message ?? `Request failed with status ${status}`;
188
+ const code = body.error?.code ?? 'UNKNOWN_ERROR';
189
+ if (status === 401)
190
+ return new errors_js_1.AuthenticationError(message);
191
+ if (status === 403 && code === 'QUOTA_EXCEEDED')
192
+ return new errors_js_1.QuotaExceededError(message);
193
+ return new errors_js_1.ZephError(message, code, status);
194
+ }
195
+ }
196
+ exports.ZephHook = ZephHook;
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@zeph-to/cli",
3
+ "version": "1.12.0",
4
+ "description": "Zeph CLI + push notification SDK for AI agents",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "bin": {
14
+ "zeph": "./dist/cli.js"
15
+ },
16
+ "engines": {
17
+ "node": ">=18.0.0"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "!dist/**/*.tsbuildinfo"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.0.0",
31
+ "@types/ws": "^8.18.1",
32
+ "typescript": "^5.8.0",
33
+ "vitest": "^2.1.9"
34
+ },
35
+ "release": {
36
+ "branches": [
37
+ "main"
38
+ ],
39
+ "plugins": [
40
+ "@semantic-release/commit-analyzer",
41
+ "@semantic-release/release-notes-generator",
42
+ "@semantic-release/npm",
43
+ "@semantic-release/github"
44
+ ]
45
+ },
46
+ "author": "Zeph <dev@zeph.to>",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/zeph-to/cli"
50
+ },
51
+ "homepage": "https://github.com/zeph-to/cli",
52
+ "bugs": {
53
+ "url": "https://github.com/zeph-to/cli/issues"
54
+ },
55
+ "keywords": [
56
+ "zeph",
57
+ "push",
58
+ "notification",
59
+ "cli",
60
+ "webhook",
61
+ "ai-agent",
62
+ "mcp",
63
+ "claude",
64
+ "devtools"
65
+ ],
66
+ "license": "Apache-2.0",
67
+ "publishConfig": {
68
+ "access": "public",
69
+ "registry": "https://registry.npmjs.org/"
70
+ },
71
+ "dependencies": {
72
+ "@inquirer/prompts": "^8.4.3",
73
+ "ws": "^8.21.0"
74
+ }
75
+ }