@vibelet/cli 0.1.37 → 1.0.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 (323) hide show
  1. package/README.md +80 -0
  2. package/bin/cloudflared-quick-tunnel.mjs +11 -0
  3. package/bin/cloudflared-resolver.mjs +171 -0
  4. package/bin/vibelet-runtime-policy.mjs +36 -0
  5. package/bin/vibelet.cjs +12 -0
  6. package/bin/vibelet.mjs +1062 -0
  7. package/dist/index.cjs +126 -0
  8. package/package.json +25 -24
  9. package/app.json +0 -5
  10. package/dist/advertised-hosts.d.ts +0 -34
  11. package/dist/advertised-hosts.d.ts.map +0 -1
  12. package/dist/advertised-hosts.js +0 -176
  13. package/dist/advertised-hosts.js.map +0 -1
  14. package/dist/advertised-hosts.test.d.ts +0 -2
  15. package/dist/advertised-hosts.test.d.ts.map +0 -1
  16. package/dist/advertised-hosts.test.js +0 -96
  17. package/dist/advertised-hosts.test.js.map +0 -1
  18. package/dist/audit.d.ts +0 -30
  19. package/dist/audit.d.ts.map +0 -1
  20. package/dist/audit.js +0 -73
  21. package/dist/audit.js.map +0 -1
  22. package/dist/audit.test.d.ts +0 -2
  23. package/dist/audit.test.d.ts.map +0 -1
  24. package/dist/audit.test.js +0 -33
  25. package/dist/audit.test.js.map +0 -1
  26. package/dist/auth.d.ts +0 -6
  27. package/dist/auth.d.ts.map +0 -1
  28. package/dist/auth.js +0 -27
  29. package/dist/auth.js.map +0 -1
  30. package/dist/claude-hooks.d.ts +0 -58
  31. package/dist/claude-hooks.d.ts.map +0 -1
  32. package/dist/claude-hooks.js +0 -129
  33. package/dist/claude-hooks.js.map +0 -1
  34. package/dist/cli-version.d.ts +0 -3
  35. package/dist/cli-version.d.ts.map +0 -1
  36. package/dist/cli-version.js +0 -35
  37. package/dist/cli-version.js.map +0 -1
  38. package/dist/cli-version.test.d.ts +0 -2
  39. package/dist/cli-version.test.d.ts.map +0 -1
  40. package/dist/cli-version.test.js +0 -38
  41. package/dist/cli-version.test.js.map +0 -1
  42. package/dist/config.d.ts +0 -30
  43. package/dist/config.d.ts.map +0 -1
  44. package/dist/config.js +0 -327
  45. package/dist/config.js.map +0 -1
  46. package/dist/config.test.d.ts +0 -2
  47. package/dist/config.test.d.ts.map +0 -1
  48. package/dist/config.test.js +0 -184
  49. package/dist/config.test.js.map +0 -1
  50. package/dist/dev-auth.test.d.ts +0 -2
  51. package/dist/dev-auth.test.d.ts.map +0 -1
  52. package/dist/dev-auth.test.js +0 -154
  53. package/dist/dev-auth.test.js.map +0 -1
  54. package/dist/dev-script.test.d.ts +0 -2
  55. package/dist/dev-script.test.d.ts.map +0 -1
  56. package/dist/dev-script.test.js +0 -412
  57. package/dist/dev-script.test.js.map +0 -1
  58. package/dist/drivers/claude.d.ts +0 -34
  59. package/dist/drivers/claude.d.ts.map +0 -1
  60. package/dist/drivers/claude.js +0 -413
  61. package/dist/drivers/claude.js.map +0 -1
  62. package/dist/drivers/claude.test.d.ts +0 -2
  63. package/dist/drivers/claude.test.d.ts.map +0 -1
  64. package/dist/drivers/claude.test.js +0 -951
  65. package/dist/drivers/claude.test.js.map +0 -1
  66. package/dist/drivers/codex.d.ts +0 -38
  67. package/dist/drivers/codex.d.ts.map +0 -1
  68. package/dist/drivers/codex.js +0 -771
  69. package/dist/drivers/codex.js.map +0 -1
  70. package/dist/drivers/codex.test.d.ts +0 -2
  71. package/dist/drivers/codex.test.d.ts.map +0 -1
  72. package/dist/drivers/codex.test.js +0 -939
  73. package/dist/drivers/codex.test.js.map +0 -1
  74. package/dist/drivers/types.d.ts +0 -14
  75. package/dist/drivers/types.d.ts.map +0 -1
  76. package/dist/drivers/types.js +0 -2
  77. package/dist/drivers/types.js.map +0 -1
  78. package/dist/e2e.test.d.ts +0 -2
  79. package/dist/e2e.test.d.ts.map +0 -1
  80. package/dist/e2e.test.js +0 -111
  81. package/dist/e2e.test.js.map +0 -1
  82. package/dist/identity.d.ts +0 -10
  83. package/dist/identity.d.ts.map +0 -1
  84. package/dist/identity.js +0 -66
  85. package/dist/identity.js.map +0 -1
  86. package/dist/identity.test.d.ts +0 -2
  87. package/dist/identity.test.d.ts.map +0 -1
  88. package/dist/identity.test.js +0 -25
  89. package/dist/identity.test.js.map +0 -1
  90. package/dist/index-entry.test.d.ts +0 -2
  91. package/dist/index-entry.test.d.ts.map +0 -1
  92. package/dist/index-entry.test.js +0 -272
  93. package/dist/index-entry.test.js.map +0 -1
  94. package/dist/index.d.ts +0 -2
  95. package/dist/index.d.ts.map +0 -1
  96. package/dist/index.js +0 -707
  97. package/dist/index.js.map +0 -1
  98. package/dist/logger.d.ts +0 -31
  99. package/dist/logger.d.ts.map +0 -1
  100. package/dist/logger.js +0 -75
  101. package/dist/logger.js.map +0 -1
  102. package/dist/metrics.d.ts +0 -52
  103. package/dist/metrics.d.ts.map +0 -1
  104. package/dist/metrics.js +0 -89
  105. package/dist/metrics.js.map +0 -1
  106. package/dist/pairing-store.d.ts +0 -29
  107. package/dist/pairing-store.d.ts.map +0 -1
  108. package/dist/pairing-store.js +0 -131
  109. package/dist/pairing-store.js.map +0 -1
  110. package/dist/pairing-store.test.d.ts +0 -2
  111. package/dist/pairing-store.test.d.ts.map +0 -1
  112. package/dist/pairing-store.test.js +0 -47
  113. package/dist/pairing-store.test.js.map +0 -1
  114. package/dist/paths.d.ts +0 -16
  115. package/dist/paths.d.ts.map +0 -1
  116. package/dist/paths.js +0 -18
  117. package/dist/paths.js.map +0 -1
  118. package/dist/perf-compare.d.ts +0 -13
  119. package/dist/perf-compare.d.ts.map +0 -1
  120. package/dist/perf-compare.js +0 -125
  121. package/dist/perf-compare.js.map +0 -1
  122. package/dist/port-conflict.d.ts +0 -9
  123. package/dist/port-conflict.d.ts.map +0 -1
  124. package/dist/port-conflict.js +0 -33
  125. package/dist/port-conflict.js.map +0 -1
  126. package/dist/port-conflict.test.d.ts +0 -2
  127. package/dist/port-conflict.test.d.ts.map +0 -1
  128. package/dist/port-conflict.test.js +0 -38
  129. package/dist/port-conflict.test.js.map +0 -1
  130. package/dist/process-scanner.d.ts +0 -43
  131. package/dist/process-scanner.d.ts.map +0 -1
  132. package/dist/process-scanner.js +0 -453
  133. package/dist/process-scanner.js.map +0 -1
  134. package/dist/process-scanner.perf.test.d.ts +0 -2
  135. package/dist/process-scanner.perf.test.d.ts.map +0 -1
  136. package/dist/process-scanner.perf.test.js +0 -186
  137. package/dist/process-scanner.perf.test.js.map +0 -1
  138. package/dist/process-scanner.test.d.ts +0 -2
  139. package/dist/process-scanner.test.d.ts.map +0 -1
  140. package/dist/process-scanner.test.js +0 -399
  141. package/dist/process-scanner.test.js.map +0 -1
  142. package/dist/push-protocol.d.ts +0 -15
  143. package/dist/push-protocol.d.ts.map +0 -1
  144. package/dist/push-protocol.js +0 -23
  145. package/dist/push-protocol.js.map +0 -1
  146. package/dist/push-protocol.test.d.ts +0 -2
  147. package/dist/push-protocol.test.d.ts.map +0 -1
  148. package/dist/push-protocol.test.js +0 -57
  149. package/dist/push-protocol.test.js.map +0 -1
  150. package/dist/push-store.d.ts +0 -22
  151. package/dist/push-store.d.ts.map +0 -1
  152. package/dist/push-store.js +0 -103
  153. package/dist/push-store.js.map +0 -1
  154. package/dist/push-store.test.d.ts +0 -2
  155. package/dist/push-store.test.d.ts.map +0 -1
  156. package/dist/push-store.test.js +0 -79
  157. package/dist/push-store.test.js.map +0 -1
  158. package/dist/push.d.ts +0 -65
  159. package/dist/push.d.ts.map +0 -1
  160. package/dist/push.js +0 -202
  161. package/dist/push.js.map +0 -1
  162. package/dist/push.test.d.ts +0 -2
  163. package/dist/push.test.d.ts.map +0 -1
  164. package/dist/push.test.js +0 -199
  165. package/dist/push.test.js.map +0 -1
  166. package/dist/safe-stdio.d.ts +0 -3
  167. package/dist/safe-stdio.d.ts.map +0 -1
  168. package/dist/safe-stdio.js +0 -46
  169. package/dist/safe-stdio.js.map +0 -1
  170. package/dist/scanner.d.ts +0 -30
  171. package/dist/scanner.d.ts.map +0 -1
  172. package/dist/scanner.js +0 -859
  173. package/dist/scanner.js.map +0 -1
  174. package/dist/scanner.perf.test.d.ts +0 -2
  175. package/dist/scanner.perf.test.d.ts.map +0 -1
  176. package/dist/scanner.perf.test.js +0 -320
  177. package/dist/scanner.perf.test.js.map +0 -1
  178. package/dist/scanner.test.d.ts +0 -2
  179. package/dist/scanner.test.d.ts.map +0 -1
  180. package/dist/scanner.test.js +0 -948
  181. package/dist/scanner.test.js.map +0 -1
  182. package/dist/session-inventory.d.ts +0 -63
  183. package/dist/session-inventory.d.ts.map +0 -1
  184. package/dist/session-inventory.js +0 -525
  185. package/dist/session-inventory.js.map +0 -1
  186. package/dist/session-inventory.perf.test.d.ts +0 -2
  187. package/dist/session-inventory.perf.test.d.ts.map +0 -1
  188. package/dist/session-inventory.perf.test.js +0 -220
  189. package/dist/session-inventory.perf.test.js.map +0 -1
  190. package/dist/session-inventory.test.d.ts +0 -2
  191. package/dist/session-inventory.test.d.ts.map +0 -1
  192. package/dist/session-inventory.test.js +0 -712
  193. package/dist/session-inventory.test.js.map +0 -1
  194. package/dist/session-manager.d.ts +0 -75
  195. package/dist/session-manager.d.ts.map +0 -1
  196. package/dist/session-manager.js +0 -1515
  197. package/dist/session-manager.js.map +0 -1
  198. package/dist/session-manager.test.d.ts +0 -2
  199. package/dist/session-manager.test.d.ts.map +0 -1
  200. package/dist/session-manager.test.js +0 -2861
  201. package/dist/session-manager.test.js.map +0 -1
  202. package/dist/session-store.d.ts +0 -42
  203. package/dist/session-store.d.ts.map +0 -1
  204. package/dist/session-store.js +0 -163
  205. package/dist/session-store.js.map +0 -1
  206. package/dist/session-store.test.d.ts +0 -2
  207. package/dist/session-store.test.d.ts.map +0 -1
  208. package/dist/session-store.test.js +0 -236
  209. package/dist/session-store.test.js.map +0 -1
  210. package/dist/session-title.d.ts +0 -6
  211. package/dist/session-title.d.ts.map +0 -1
  212. package/dist/session-title.js +0 -105
  213. package/dist/session-title.js.map +0 -1
  214. package/dist/session-title.perf.test.d.ts +0 -2
  215. package/dist/session-title.perf.test.d.ts.map +0 -1
  216. package/dist/session-title.perf.test.js +0 -99
  217. package/dist/session-title.perf.test.js.map +0 -1
  218. package/dist/session-title.test.d.ts +0 -2
  219. package/dist/session-title.test.d.ts.map +0 -1
  220. package/dist/session-title.test.js +0 -199
  221. package/dist/session-title.test.js.map +0 -1
  222. package/dist/shutdown-endpoint.test.d.ts +0 -2
  223. package/dist/shutdown-endpoint.test.d.ts.map +0 -1
  224. package/dist/shutdown-endpoint.test.js +0 -93
  225. package/dist/shutdown-endpoint.test.js.map +0 -1
  226. package/dist/storage-housekeeping.d.ts +0 -28
  227. package/dist/storage-housekeeping.d.ts.map +0 -1
  228. package/dist/storage-housekeeping.js +0 -76
  229. package/dist/storage-housekeeping.js.map +0 -1
  230. package/dist/storage-housekeeping.test.d.ts +0 -2
  231. package/dist/storage-housekeeping.test.d.ts.map +0 -1
  232. package/dist/storage-housekeeping.test.js +0 -65
  233. package/dist/storage-housekeeping.test.js.map +0 -1
  234. package/dist/test-daemon-harness.d.ts +0 -31
  235. package/dist/test-daemon-harness.d.ts.map +0 -1
  236. package/dist/test-daemon-harness.js +0 -337
  237. package/dist/test-daemon-harness.js.map +0 -1
  238. package/dist/token-auth.test.d.ts +0 -2
  239. package/dist/token-auth.test.d.ts.map +0 -1
  240. package/dist/token-auth.test.js +0 -52
  241. package/dist/token-auth.test.js.map +0 -1
  242. package/dist/utils.d.ts +0 -4
  243. package/dist/utils.d.ts.map +0 -1
  244. package/dist/utils.js +0 -40
  245. package/dist/utils.js.map +0 -1
  246. package/dist/utils.test.d.ts +0 -2
  247. package/dist/utils.test.d.ts.map +0 -1
  248. package/dist/utils.test.js +0 -54
  249. package/dist/utils.test.js.map +0 -1
  250. package/dist/ws-data.d.ts +0 -4
  251. package/dist/ws-data.d.ts.map +0 -1
  252. package/dist/ws-data.js +0 -20
  253. package/dist/ws-data.js.map +0 -1
  254. package/dist/ws-data.test.d.ts +0 -2
  255. package/dist/ws-data.test.d.ts.map +0 -1
  256. package/dist/ws-data.test.js +0 -17
  257. package/dist/ws-data.test.js.map +0 -1
  258. package/perf-reporter.mjs +0 -138
  259. package/scripts/build-release.mjs +0 -41
  260. package/scripts/dev.mjs +0 -537
  261. package/src/advertised-hosts.test.ts +0 -125
  262. package/src/advertised-hosts.ts +0 -225
  263. package/src/audit.test.ts +0 -38
  264. package/src/audit.ts +0 -117
  265. package/src/auth.ts +0 -31
  266. package/src/claude-hooks.ts +0 -195
  267. package/src/cli-version.test.ts +0 -36
  268. package/src/cli-version.ts +0 -46
  269. package/src/config.test.ts +0 -254
  270. package/src/config.ts +0 -324
  271. package/src/dev-auth.test.ts +0 -183
  272. package/src/dev-script.test.ts +0 -511
  273. package/src/drivers/claude.test.ts +0 -1186
  274. package/src/drivers/claude.ts +0 -443
  275. package/src/drivers/codex.test.ts +0 -1096
  276. package/src/drivers/codex.ts +0 -879
  277. package/src/drivers/types.ts +0 -15
  278. package/src/e2e.test.ts +0 -139
  279. package/src/identity.test.ts +0 -26
  280. package/src/identity.ts +0 -82
  281. package/src/index-entry.test.ts +0 -336
  282. package/src/index.ts +0 -781
  283. package/src/logger.ts +0 -112
  284. package/src/metrics.ts +0 -117
  285. package/src/pairing-store.test.ts +0 -53
  286. package/src/pairing-store.ts +0 -154
  287. package/src/paths.ts +0 -19
  288. package/src/perf-compare.ts +0 -164
  289. package/src/port-conflict.test.ts +0 -45
  290. package/src/port-conflict.ts +0 -44
  291. package/src/process-scanner.perf.test.ts +0 -222
  292. package/src/process-scanner.test.ts +0 -575
  293. package/src/process-scanner.ts +0 -514
  294. package/src/push-protocol.test.ts +0 -74
  295. package/src/push-protocol.ts +0 -36
  296. package/src/push-store.test.ts +0 -89
  297. package/src/push-store.ts +0 -126
  298. package/src/push.test.ts +0 -234
  299. package/src/push.ts +0 -318
  300. package/src/safe-stdio.ts +0 -51
  301. package/src/scanner.perf.test.ts +0 -359
  302. package/src/scanner.test.ts +0 -1045
  303. package/src/scanner.ts +0 -924
  304. package/src/session-inventory.perf.test.ts +0 -250
  305. package/src/session-inventory.test.ts +0 -1002
  306. package/src/session-inventory.ts +0 -721
  307. package/src/session-manager.test.ts +0 -3430
  308. package/src/session-manager.ts +0 -1775
  309. package/src/session-store.test.ts +0 -276
  310. package/src/session-store.ts +0 -202
  311. package/src/session-title.perf.test.ts +0 -118
  312. package/src/session-title.test.ts +0 -286
  313. package/src/session-title.ts +0 -108
  314. package/src/shutdown-endpoint.test.ts +0 -95
  315. package/src/storage-housekeeping.test.ts +0 -78
  316. package/src/storage-housekeeping.ts +0 -111
  317. package/src/test-daemon-harness.ts +0 -410
  318. package/src/token-auth.test.ts +0 -67
  319. package/src/utils.test.ts +0 -65
  320. package/src/utils.ts +0 -47
  321. package/src/ws-data.test.ts +0 -20
  322. package/src/ws-data.ts +0 -26
  323. package/tsconfig.json +0 -12
@@ -1,89 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { mkdtemp, readFile, rm } from 'fs/promises';
4
- import { tmpdir } from 'os';
5
- import { join } from 'path';
6
- import { PushStore } from './push-store.js';
7
-
8
- test('PushStore overwrites the previous token for the same device', async () => {
9
- const dir = await mkdtemp(join(tmpdir(), 'vibelet-push-'));
10
- const pushSubscriptionsPath = join(dir, 'push-subscriptions.json');
11
-
12
- try {
13
- const store = new PushStore(pushSubscriptionsPath);
14
- store.register('device-1', 'ExponentPushToken[expo-go]');
15
- store.register('device-1', 'ExponentPushToken[vibelet]');
16
-
17
- assert.deepEqual(store.listTokens(), ['ExponentPushToken[vibelet]']);
18
-
19
- const persisted = JSON.parse(await readFile(pushSubscriptionsPath, 'utf8'));
20
- assert.equal(persisted.length, 1);
21
- assert.equal(persisted[0].deviceId, 'device-1');
22
- assert.equal(persisted[0].pushToken, 'ExponentPushToken[vibelet]');
23
- } finally {
24
- await rm(dir, { recursive: true, force: true });
25
- }
26
- });
27
-
28
- test('PushStore de-duplicates tokens reused by a different device', async () => {
29
- const dir = await mkdtemp(join(tmpdir(), 'vibelet-push-'));
30
- const pushSubscriptionsPath = join(dir, 'push-subscriptions.json');
31
-
32
- try {
33
- const store = new PushStore(pushSubscriptionsPath);
34
- store.register('device-expo-go', 'ExponentPushToken[shared]');
35
- store.register('device-vibelet', 'ExponentPushToken[shared]');
36
-
37
- assert.deepEqual(store.list(), [
38
- {
39
- deviceId: 'device-vibelet',
40
- pushToken: 'ExponentPushToken[shared]',
41
- platform: 'expo',
42
- createdAt: store.list()[0]?.createdAt,
43
- updatedAt: store.list()[0]?.updatedAt,
44
- },
45
- ]);
46
- } finally {
47
- await rm(dir, { recursive: true, force: true });
48
- }
49
- });
50
-
51
- test('PushStore unregister only removes the matching device token', async () => {
52
- const dir = await mkdtemp(join(tmpdir(), 'vibelet-push-'));
53
- const pushSubscriptionsPath = join(dir, 'push-subscriptions.json');
54
-
55
- try {
56
- const store = new PushStore(pushSubscriptionsPath);
57
- store.register('device-1', 'ExponentPushToken[one]');
58
- store.register('device-2', 'ExponentPushToken[two]');
59
-
60
- assert.equal(store.unregister('device-1', 'ExponentPushToken[two]'), false);
61
- assert.deepEqual(store.listTokens(), ['ExponentPushToken[two]', 'ExponentPushToken[one]']);
62
-
63
- assert.equal(store.unregister('device-1', 'ExponentPushToken[one]'), true);
64
- assert.deepEqual(store.listTokens(), ['ExponentPushToken[two]']);
65
- } finally {
66
- await rm(dir, { recursive: true, force: true });
67
- }
68
- });
69
-
70
- test('PushStore unregisterToken removes stale subscriptions by token', async () => {
71
- const dir = await mkdtemp(join(tmpdir(), 'vibelet-push-'));
72
- const pushSubscriptionsPath = join(dir, 'push-subscriptions.json');
73
-
74
- try {
75
- const store = new PushStore(pushSubscriptionsPath);
76
- store.register('device-1', 'ExponentPushToken[stale]');
77
- store.register('device-2', 'ExponentPushToken[live]');
78
-
79
- assert.equal(store.unregisterToken('ExponentPushToken[missing]'), false);
80
- assert.equal(store.unregisterToken('ExponentPushToken[stale]'), true);
81
- assert.deepEqual(store.listTokens(), ['ExponentPushToken[live]']);
82
-
83
- const persisted = JSON.parse(await readFile(pushSubscriptionsPath, 'utf8'));
84
- assert.equal(persisted.length, 1);
85
- assert.equal(persisted[0].pushToken, 'ExponentPushToken[live]');
86
- } finally {
87
- await rm(dir, { recursive: true, force: true });
88
- }
89
- });
package/src/push-store.ts DELETED
@@ -1,126 +0,0 @@
1
- import { mkdirSync, readFileSync, writeFileSync } from 'fs';
2
- import { dirname } from 'path';
3
- import { PUSH_SUBSCRIPTIONS_PATH } from './paths.js';
4
-
5
- export interface PushSubscriptionRecord {
6
- deviceId: string;
7
- pushToken: string;
8
- platform: 'expo';
9
- createdAt: string;
10
- updatedAt: string;
11
- lastSentAt?: string;
12
- }
13
-
14
- function isPushSubscriptionRecord(entry: unknown): entry is PushSubscriptionRecord {
15
- return Boolean(
16
- entry
17
- && typeof entry === 'object'
18
- && typeof (entry as PushSubscriptionRecord).deviceId === 'string'
19
- && typeof (entry as PushSubscriptionRecord).pushToken === 'string'
20
- && (entry as PushSubscriptionRecord).platform === 'expo'
21
- && typeof (entry as PushSubscriptionRecord).createdAt === 'string'
22
- && typeof (entry as PushSubscriptionRecord).updatedAt === 'string'
23
- && (
24
- typeof (entry as PushSubscriptionRecord).lastSentAt === 'string'
25
- || typeof (entry as PushSubscriptionRecord).lastSentAt === 'undefined'
26
- )
27
- );
28
- }
29
-
30
- function readRecords(pushSubscriptionsPath: string): PushSubscriptionRecord[] {
31
- try {
32
- const raw = readFileSync(pushSubscriptionsPath, 'utf8');
33
- const parsed = JSON.parse(raw);
34
- if (!Array.isArray(parsed)) return [];
35
- return parsed.filter(isPushSubscriptionRecord);
36
- } catch {
37
- return [];
38
- }
39
- }
40
-
41
- function writeRecords(pushSubscriptionsPath: string, records: PushSubscriptionRecord[]): void {
42
- mkdirSync(dirname(pushSubscriptionsPath), { recursive: true });
43
- writeFileSync(pushSubscriptionsPath, JSON.stringify(records, null, 2) + '\n', 'utf8');
44
- }
45
-
46
- export class PushStore {
47
- private records: PushSubscriptionRecord[];
48
-
49
- constructor(private readonly pushSubscriptionsPath = PUSH_SUBSCRIPTIONS_PATH) {
50
- this.records = readRecords(pushSubscriptionsPath);
51
- }
52
-
53
- list(): PushSubscriptionRecord[] {
54
- return this.records.map((record) => ({ ...record }));
55
- }
56
-
57
- listTokens(): string[] {
58
- return this.records.map((record) => record.pushToken);
59
- }
60
-
61
- count(): number {
62
- return this.records.length;
63
- }
64
-
65
- register(deviceId: string, pushToken: string): void {
66
- const now = new Date().toISOString();
67
- const existingRecord = this.records.find((record) => record.deviceId === deviceId);
68
- const nextRecord: PushSubscriptionRecord = {
69
- deviceId,
70
- pushToken,
71
- platform: 'expo',
72
- createdAt: existingRecord?.createdAt ?? now,
73
- updatedAt: now,
74
- ...(existingRecord?.lastSentAt ? { lastSentAt: existingRecord.lastSentAt } : {}),
75
- };
76
-
77
- this.records = [
78
- nextRecord,
79
- ...this.records.filter((record) => record.deviceId !== deviceId && record.pushToken !== pushToken),
80
- ];
81
-
82
- writeRecords(this.pushSubscriptionsPath, this.records);
83
- }
84
-
85
- unregister(deviceId: string, pushToken: string): boolean {
86
- const nextRecords = this.records.filter(
87
- (record) => !(record.deviceId === deviceId && record.pushToken === pushToken),
88
- );
89
- if (nextRecords.length === this.records.length) {
90
- return false;
91
- }
92
- this.records = nextRecords;
93
- writeRecords(this.pushSubscriptionsPath, this.records);
94
- return true;
95
- }
96
-
97
- unregisterToken(pushToken: string): boolean {
98
- const nextRecords = this.records.filter((record) => record.pushToken !== pushToken);
99
- if (nextRecords.length === this.records.length) {
100
- return false;
101
- }
102
- this.records = nextRecords;
103
- writeRecords(this.pushSubscriptionsPath, this.records);
104
- return true;
105
- }
106
-
107
- markSent(pushTokens: string[]): void {
108
- if (pushTokens.length === 0) return;
109
- const touched = new Set(pushTokens);
110
- const now = new Date().toISOString();
111
- let didChange = false;
112
- for (const record of this.records) {
113
- if (!touched.has(record.pushToken)) continue;
114
- record.lastSentAt = now;
115
- didChange = true;
116
- }
117
- if (didChange) {
118
- writeRecords(this.pushSubscriptionsPath, this.records);
119
- }
120
- }
121
-
122
- reset(): void {
123
- this.records = [];
124
- writeRecords(this.pushSubscriptionsPath, this.records);
125
- }
126
- }
package/src/push.test.ts DELETED
@@ -1,234 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { mkdtemp, rm } from 'node:fs/promises';
4
- import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
- import {
7
- collectExpoPushReceiptResults,
8
- collectSuccessfulExpoPushTokens,
9
- createPushSender,
10
- shouldRemoveTokenForExpoError,
11
- } from './push.js';
12
- import { PushStore } from './push-store.js';
13
-
14
- test('collectSuccessfulExpoPushTokens returns only ok tokens', () => {
15
- const result = collectSuccessfulExpoPushTokens(
16
- {
17
- data: [
18
- { status: 'ok', id: 'ticket-1' },
19
- { status: 'error', message: 'bad token', details: { error: 'DeviceNotRegistered' } },
20
- { status: 'ok', id: 'ticket-3' },
21
- ],
22
- },
23
- ['ExponentPushToken[a]', 'ExponentPushToken[b]', 'ExponentPushToken[c]'],
24
- );
25
-
26
- assert.deepEqual(result.successfulTokens, ['ExponentPushToken[a]', 'ExponentPushToken[c]']);
27
- assert.deepEqual(result.ticketErrors, [
28
- {
29
- token: 'ExponentPushToken[b]',
30
- ticket: { status: 'error', message: 'bad token', details: { error: 'DeviceNotRegistered' } },
31
- },
32
- ]);
33
- });
34
-
35
- test('collectSuccessfulExpoPushTokens handles invalid credentials response', () => {
36
- const result = collectSuccessfulExpoPushTokens(
37
- {
38
- data: [
39
- {
40
- status: 'error',
41
- message: 'Could not find APNs credentials.',
42
- details: { error: 'InvalidCredentials' },
43
- },
44
- ],
45
- },
46
- ['ExponentPushToken[a]'],
47
- );
48
-
49
- assert.deepEqual(result.successfulTokens, []);
50
- assert.deepEqual(result.ticketErrors, [
51
- {
52
- token: 'ExponentPushToken[a]',
53
- ticket: {
54
- status: 'error',
55
- message: 'Could not find APNs credentials.',
56
- details: { error: 'InvalidCredentials' },
57
- },
58
- },
59
- ]);
60
- });
61
-
62
- test('collectSuccessfulExpoPushTokens ignores unreadable payloads', () => {
63
- const result = collectSuccessfulExpoPushTokens({}, ['ExponentPushToken[a]']);
64
-
65
- assert.deepEqual(result.successfulTokens, []);
66
- assert.deepEqual(result.ticketErrors, []);
67
- });
68
-
69
- test('collectExpoPushReceiptResults keeps unresolved ids and returns receipt errors', () => {
70
- const result = collectExpoPushReceiptResults(
71
- {
72
- data: {
73
- 'ticket-live': { status: 'ok' },
74
- 'ticket-dead': { status: 'error', message: 'gone', details: { error: 'DeviceNotRegistered' } },
75
- },
76
- },
77
- new Map([
78
- ['ticket-live', 'ExponentPushToken[live]'],
79
- ['ticket-dead', 'ExponentPushToken[dead]'],
80
- ['ticket-pending', 'ExponentPushToken[pending]'],
81
- ]),
82
- );
83
-
84
- assert.deepEqual(result.pendingTicketIds, ['ticket-pending']);
85
- assert.deepEqual(result.receiptErrors, [
86
- {
87
- token: 'ExponentPushToken[dead]',
88
- receipt: { status: 'error', message: 'gone', details: { error: 'DeviceNotRegistered' } },
89
- },
90
- ]);
91
- });
92
-
93
- test('shouldRemoveTokenForExpoError only removes unregistered devices', () => {
94
- assert.equal(shouldRemoveTokenForExpoError('DeviceNotRegistered'), true);
95
- assert.equal(shouldRemoveTokenForExpoError('InvalidCredentials'), false);
96
- assert.equal(shouldRemoveTokenForExpoError(undefined), false);
97
- });
98
-
99
- test('sendPush removes DeviceNotRegistered tokens reported by receipts', async () => {
100
- const dir = await mkdtemp(join(tmpdir(), 'vibelet-push-'));
101
- const pushSubscriptionsPath = join(dir, 'push-subscriptions.json');
102
-
103
- try {
104
- const store = new PushStore(pushSubscriptionsPath);
105
- store.register('device-dead', 'ExponentPushToken[dead]');
106
- store.register('device-live', 'ExponentPushToken[live]');
107
-
108
- const requests: Array<{ url: string; body?: string }> = [];
109
- const sendPush = createPushSender({
110
- pushStore: store,
111
- sleepImpl: async () => {},
112
- fetchImpl: async (url, init) => {
113
- requests.push({ url, body: init?.body });
114
- if (url.endsWith('/push/send')) {
115
- return {
116
- ok: true,
117
- status: 200,
118
- statusText: 'OK',
119
- json: async () => ({
120
- data: [
121
- { status: 'ok', id: 'ticket-live' },
122
- { status: 'ok', id: 'ticket-dead' },
123
- ],
124
- }),
125
- };
126
- }
127
-
128
- return {
129
- ok: true,
130
- status: 200,
131
- statusText: 'OK',
132
- json: async () => ({
133
- data: {
134
- 'ticket-live': { status: 'ok' },
135
- 'ticket-dead': {
136
- status: 'error',
137
- message: 'unregistered',
138
- details: { error: 'DeviceNotRegistered' },
139
- },
140
- },
141
- }),
142
- };
143
- },
144
- });
145
-
146
- await sendPush('Hello', 'World');
147
-
148
- assert.equal(requests.length, 2);
149
- assert.deepEqual(store.listTokens(), ['ExponentPushToken[live]']);
150
- } finally {
151
- await rm(dir, { recursive: true, force: true });
152
- }
153
- });
154
-
155
- test('sendPush removes DeviceNotRegistered tokens reported by send tickets', async () => {
156
- const dir = await mkdtemp(join(tmpdir(), 'vibelet-push-'));
157
- const pushSubscriptionsPath = join(dir, 'push-subscriptions.json');
158
-
159
- try {
160
- const store = new PushStore(pushSubscriptionsPath);
161
- store.register('device-dead', 'ExponentPushToken[dead]');
162
- store.register('device-live', 'ExponentPushToken[live]');
163
-
164
- const sendPush = createPushSender({
165
- pushStore: store,
166
- sleepImpl: async () => {},
167
- fetchImpl: async () => ({
168
- ok: true,
169
- status: 200,
170
- statusText: 'OK',
171
- json: async () => ({
172
- data: [
173
- { status: 'ok', id: 'ticket-live' },
174
- {
175
- status: 'error',
176
- message: 'unregistered',
177
- details: { error: 'DeviceNotRegistered' },
178
- },
179
- ],
180
- }),
181
- }),
182
- });
183
-
184
- await sendPush('Hello', 'World');
185
-
186
- assert.deepEqual(store.listTokens(), ['ExponentPushToken[live]']);
187
- } finally {
188
- await rm(dir, { recursive: true, force: true });
189
- }
190
- });
191
-
192
- test('sendPush attaches approval categoryId for approval_request events', async () => {
193
- const dir = await mkdtemp(join(tmpdir(), 'vibelet-push-'));
194
- const pushSubscriptionsPath = join(dir, 'push-subscriptions.json');
195
-
196
- try {
197
- const store = new PushStore(pushSubscriptionsPath);
198
- store.register('device-1', 'ExponentPushToken[live]');
199
-
200
- const requests: Array<{ url: string; body?: string }> = [];
201
- const sendPush = createPushSender({
202
- pushStore: store,
203
- sleepImpl: async () => {},
204
- fetchImpl: async (url, init) => {
205
- requests.push({ url, body: init?.body });
206
- return {
207
- ok: true,
208
- status: 200,
209
- statusText: 'OK',
210
- json: async () => ({
211
- data: [
212
- { status: 'ok', id: 'ticket-live' },
213
- ],
214
- }),
215
- };
216
- },
217
- });
218
-
219
- await sendPush('Approval required', 'Inspect the build process', {
220
- daemonId: 'daemon-1',
221
- canonicalHost: 'daemon.local',
222
- sessionId: 'session-1',
223
- agent: 'codex',
224
- eventType: 'approval_request',
225
- requestId: 'req-1',
226
- });
227
-
228
- const payload = JSON.parse(requests[0]?.body ?? '[]');
229
- assert.equal(payload.length, 1);
230
- assert.equal(payload[0].categoryId, 'vibelet.approvalRequest.v1');
231
- } finally {
232
- await rm(dir, { recursive: true, force: true });
233
- }
234
- });