crewly 1.20.303 → 1.20.304

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 (135) hide show
  1. package/config/skills/orchestrator/move-team/SKILL.md +53 -0
  2. package/config/skills/orchestrator/move-team/execute.sh +42 -0
  3. package/dist/backend/backend/src/constants.d.ts +69 -0
  4. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  5. package/dist/backend/backend/src/constants.js +69 -0
  6. package/dist/backend/backend/src/constants.js.map +1 -1
  7. package/dist/backend/backend/src/controllers/team-move/team-move.controller.d.ts +45 -0
  8. package/dist/backend/backend/src/controllers/team-move/team-move.controller.d.ts.map +1 -0
  9. package/dist/backend/backend/src/controllers/team-move/team-move.controller.js +214 -0
  10. package/dist/backend/backend/src/controllers/team-move/team-move.controller.js.map +1 -0
  11. package/dist/backend/backend/src/index.d.ts.map +1 -1
  12. package/dist/backend/backend/src/index.js +22 -3
  13. package/dist/backend/backend/src/index.js.map +1 -1
  14. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  15. package/dist/backend/backend/src/routes/api.routes.js +3 -0
  16. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  17. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  18. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  19. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
  20. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  21. package/dist/backend/backend/src/services/core/storage.service.d.ts +18 -0
  22. package/dist/backend/backend/src/services/core/storage.service.d.ts.map +1 -1
  23. package/dist/backend/backend/src/services/core/storage.service.js +16 -0
  24. package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
  25. package/dist/backend/backend/src/services/decisions/decision.service.d.ts +19 -0
  26. package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  27. package/dist/backend/backend/src/services/decisions/decision.service.js +46 -0
  28. package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
  29. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  30. package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
  31. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  32. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +9 -0
  33. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  34. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +25 -0
  35. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  36. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +19 -0
  37. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  38. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +41 -0
  39. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  40. package/dist/backend/backend/src/services/team-move/team-move-archive.d.ts +122 -0
  41. package/dist/backend/backend/src/services/team-move/team-move-archive.d.ts.map +1 -0
  42. package/dist/backend/backend/src/services/team-move/team-move-archive.js +202 -0
  43. package/dist/backend/backend/src/services/team-move/team-move-archive.js.map +1 -0
  44. package/dist/backend/backend/src/services/team-move/team-move-chat.d.ts +59 -0
  45. package/dist/backend/backend/src/services/team-move/team-move-chat.d.ts.map +1 -0
  46. package/dist/backend/backend/src/services/team-move/team-move-chat.js +133 -0
  47. package/dist/backend/backend/src/services/team-move/team-move-chat.js.map +1 -0
  48. package/dist/backend/backend/src/services/team-move/team-move-cloud.client.d.ts +102 -0
  49. package/dist/backend/backend/src/services/team-move/team-move-cloud.client.d.ts.map +1 -0
  50. package/dist/backend/backend/src/services/team-move/team-move-cloud.client.js +217 -0
  51. package/dist/backend/backend/src/services/team-move/team-move-cloud.client.js.map +1 -0
  52. package/dist/backend/backend/src/services/team-move/team-move-crypto.d.ts +102 -0
  53. package/dist/backend/backend/src/services/team-move/team-move-crypto.d.ts.map +1 -0
  54. package/dist/backend/backend/src/services/team-move/team-move-crypto.js +348 -0
  55. package/dist/backend/backend/src/services/team-move/team-move-crypto.js.map +1 -0
  56. package/dist/backend/backend/src/services/team-move/team-move-driver.service.d.ts +209 -0
  57. package/dist/backend/backend/src/services/team-move/team-move-driver.service.d.ts.map +1 -0
  58. package/dist/backend/backend/src/services/team-move/team-move-driver.service.js +504 -0
  59. package/dist/backend/backend/src/services/team-move/team-move-driver.service.js.map +1 -0
  60. package/dist/backend/backend/src/services/team-move/team-move-exporter.d.ts +156 -0
  61. package/dist/backend/backend/src/services/team-move/team-move-exporter.d.ts.map +1 -0
  62. package/dist/backend/backend/src/services/team-move/team-move-exporter.js +462 -0
  63. package/dist/backend/backend/src/services/team-move/team-move-exporter.js.map +1 -0
  64. package/dist/backend/backend/src/services/team-move/team-move-fs.d.ts +157 -0
  65. package/dist/backend/backend/src/services/team-move/team-move-fs.d.ts.map +1 -0
  66. package/dist/backend/backend/src/services/team-move/team-move-fs.js +298 -0
  67. package/dist/backend/backend/src/services/team-move/team-move-fs.js.map +1 -0
  68. package/dist/backend/backend/src/services/team-move/team-move-git.d.ts +69 -0
  69. package/dist/backend/backend/src/services/team-move/team-move-git.d.ts.map +1 -0
  70. package/dist/backend/backend/src/services/team-move/team-move-git.js +198 -0
  71. package/dist/backend/backend/src/services/team-move/team-move-git.js.map +1 -0
  72. package/dist/backend/backend/src/services/team-move/team-move-importer.d.ts +121 -0
  73. package/dist/backend/backend/src/services/team-move/team-move-importer.d.ts.map +1 -0
  74. package/dist/backend/backend/src/services/team-move/team-move-importer.js +462 -0
  75. package/dist/backend/backend/src/services/team-move/team-move-importer.js.map +1 -0
  76. package/dist/backend/backend/src/services/team-move/team-move-journal.d.ts +110 -0
  77. package/dist/backend/backend/src/services/team-move/team-move-journal.d.ts.map +1 -0
  78. package/dist/backend/backend/src/services/team-move/team-move-journal.js +198 -0
  79. package/dist/backend/backend/src/services/team-move/team-move-journal.js.map +1 -0
  80. package/dist/backend/backend/src/services/team-move/team-move-preflight.d.ts +71 -0
  81. package/dist/backend/backend/src/services/team-move/team-move-preflight.d.ts.map +1 -0
  82. package/dist/backend/backend/src/services/team-move/team-move-preflight.js +102 -0
  83. package/dist/backend/backend/src/services/team-move/team-move-preflight.js.map +1 -0
  84. package/dist/backend/backend/src/services/team-move/team-move-runtime.d.ts +29 -0
  85. package/dist/backend/backend/src/services/team-move/team-move-runtime.d.ts.map +1 -0
  86. package/dist/backend/backend/src/services/team-move/team-move-runtime.js +173 -0
  87. package/dist/backend/backend/src/services/team-move/team-move-runtime.js.map +1 -0
  88. package/dist/backend/backend/src/services/team-move/team-move-secret-scan.d.ts +50 -0
  89. package/dist/backend/backend/src/services/team-move/team-move-secret-scan.d.ts.map +1 -0
  90. package/dist/backend/backend/src/services/team-move/team-move-secret-scan.js +96 -0
  91. package/dist/backend/backend/src/services/team-move/team-move-secret-scan.js.map +1 -0
  92. package/dist/backend/backend/src/services/team-move/team-move-select.d.ts +94 -0
  93. package/dist/backend/backend/src/services/team-move/team-move-select.d.ts.map +1 -0
  94. package/dist/backend/backend/src/services/team-move/team-move-select.js +124 -0
  95. package/dist/backend/backend/src/services/team-move/team-move-select.js.map +1 -0
  96. package/dist/backend/backend/src/services/team-move/team-move.fixture.d.ts +46 -0
  97. package/dist/backend/backend/src/services/team-move/team-move.fixture.d.ts.map +1 -0
  98. package/dist/backend/backend/src/services/team-move/team-move.fixture.js +202 -0
  99. package/dist/backend/backend/src/services/team-move/team-move.fixture.js.map +1 -0
  100. package/dist/backend/backend/src/services/team-move/team-move.types.d.ts +269 -0
  101. package/dist/backend/backend/src/services/team-move/team-move.types.d.ts.map +1 -0
  102. package/dist/backend/backend/src/services/team-move/team-move.types.js +44 -0
  103. package/dist/backend/backend/src/services/team-move/team-move.types.js.map +1 -0
  104. package/dist/backend/build-info.json +2 -2
  105. package/dist/cli/backend/src/constants.d.ts +69 -0
  106. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  107. package/dist/cli/backend/src/constants.js +69 -0
  108. package/dist/cli/backend/src/constants.js.map +1 -1
  109. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  110. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  111. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
  112. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  113. package/dist/cli/backend/src/services/core/storage.service.d.ts +18 -0
  114. package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -1
  115. package/dist/cli/backend/src/services/core/storage.service.js +16 -0
  116. package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
  117. package/dist/cli/backend/src/services/decisions/decision.service.d.ts +19 -0
  118. package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  119. package/dist/cli/backend/src/services/decisions/decision.service.js +46 -0
  120. package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
  121. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +9 -0
  122. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  123. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +25 -0
  124. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  125. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +19 -0
  126. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  127. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +41 -0
  128. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  129. package/dist/cli/cli/src/commands/team-move.d.ts +68 -0
  130. package/dist/cli/cli/src/commands/team-move.d.ts.map +1 -0
  131. package/dist/cli/cli/src/commands/team-move.js +155 -0
  132. package/dist/cli/cli/src/commands/team-move.js.map +1 -0
  133. package/dist/cli/cli/src/index.js +33 -0
  134. package/dist/cli/cli/src/index.js.map +1 -1
  135. package/package.json +1 -1
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Team move crypto — end-to-end encryption of the package stream.
3
+ *
4
+ * Only the two machines can read a package; Crewly Cloud stores ciphertext.
5
+ *
6
+ * - The TARGET makes an ephemeral X25519 key pair per move. The public key
7
+ * goes to Cloud, the private key never leaves the target's
8
+ * `~/.crewly/team-moves/<moveId>/`.
9
+ * - The SOURCE makes an ephemeral X25519 key pair per package, derives a
10
+ * shared secret with the target's public key and runs it through
11
+ * HKDF-SHA256 (random 32-byte salt, `info` binds the move id) to get a
12
+ * one-time AES-256 key.
13
+ * - The stream is cut into frames of `frameBytes` plaintext. Each frame is
14
+ * sealed with AES-256-GCM under its own nonce (frame counter). The
15
+ * authenticated data of every frame is the header (which carries the move
16
+ * id and the ephemeral public key), the frame counter and a "final" flag,
17
+ * so a frame cannot be moved, dropped, repeated or cut off at the end, and
18
+ * a package cannot be replayed into another move, without failing.
19
+ *
20
+ * Wire format:
21
+ *
22
+ * header : "CTMV" | version u8 | frameBytes u32 | ephemeralPublicKey[32]
23
+ * | salt[32] | moveIdLength u8 | moveId
24
+ * frame* : length u32 (ciphertext incl. 16-byte tag) | ciphertext
25
+ *
26
+ * The one-time key makes counter nonces safe: a (key, nonce) pair is never
27
+ * used twice. Both transforms are streaming — memory stays at one frame.
28
+ *
29
+ * @module services/team-move/team-move-crypto
30
+ */
31
+ import { createCipheriv, createDecipheriv, createHash, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes, } from 'node:crypto';
32
+ import { Transform } from 'node:stream';
33
+ import { TEAM_MOVE_CONSTANTS } from '../../constants.js';
34
+ /** Magic bytes at the start of an encrypted package. */
35
+ const MAGIC = Buffer.from('CTMV', 'ascii');
36
+ /** Format version in the header. */
37
+ const FORMAT_VERSION = 1;
38
+ /** Length of the GCM tag. */
39
+ const TAG_BYTES = 16;
40
+ /** Length of an X25519 raw key. */
41
+ const KEY_BYTES = 32;
42
+ /** DER prefix of an X25519 SubjectPublicKeyInfo (the 32 key bytes follow). */
43
+ const X25519_SPKI_PREFIX = Buffer.from('302a300506032b656e032100', 'hex');
44
+ /** HKDF info prefix; the move id is appended. */
45
+ const HKDF_INFO_PREFIX = 'crewly-team-move/v1|';
46
+ /** Largest frame size a header may announce (guards memory). */
47
+ const MAX_FRAME_BYTES = 16 * 1024 * 1024;
48
+ /** Fixed part of the header, before the move id. */
49
+ const FIXED_HEADER_BYTES = MAGIC.length + 1 + 4 + KEY_BYTES + KEY_BYTES + 1;
50
+ /** Thrown when a package cannot be decrypted or has been altered. */
51
+ export class TeamMoveCryptoError extends Error {
52
+ constructor(message) {
53
+ super(message);
54
+ this.name = 'TeamMoveCryptoError';
55
+ }
56
+ }
57
+ /**
58
+ * Make the target's ephemeral key pair for one move.
59
+ *
60
+ * @returns Base64 public (raw) and private (PKCS8) keys
61
+ */
62
+ export function generateTargetKeyPair() {
63
+ const { publicKey, privateKey } = generateKeyPairSync('x25519');
64
+ return {
65
+ publicKey: rawPublicKey(publicKey).toString('base64'),
66
+ privateKey: privateKey.export({ format: 'der', type: 'pkcs8' }).toString('base64'),
67
+ };
68
+ }
69
+ /**
70
+ * Raw 32 public-key bytes of a key object.
71
+ *
72
+ * @param key - Public or private X25519 key
73
+ * @returns The 32 raw bytes
74
+ */
75
+ function rawPublicKey(key) {
76
+ const spki = (key.type === 'private' ? createPublicKey(key) : key).export({ format: 'der', type: 'spki' });
77
+ return Buffer.from(spki.subarray(spki.length - KEY_BYTES));
78
+ }
79
+ /**
80
+ * Import a raw X25519 public key.
81
+ *
82
+ * @param raw - 32 raw bytes
83
+ * @returns Key object
84
+ */
85
+ function importPublicKey(raw) {
86
+ if (raw.length !== KEY_BYTES)
87
+ throw new TeamMoveCryptoError('public key must be 32 bytes');
88
+ return createPublicKey({ key: Buffer.concat([X25519_SPKI_PREFIX, raw]), format: 'der', type: 'spki' });
89
+ }
90
+ /**
91
+ * Whether a string is a base64 raw X25519 public key (as Cloud validates it).
92
+ *
93
+ * @param value - Candidate
94
+ * @returns True for 32 bytes of base64
95
+ */
96
+ export function isPublicKeyString(value) {
97
+ return typeof value === 'string' && /^[A-Za-z0-9+/]{43}=$/.test(value) && Buffer.from(value, 'base64').length === KEY_BYTES;
98
+ }
99
+ /**
100
+ * One-time AES key for a package.
101
+ *
102
+ * @param privateKey - One side's X25519 private key
103
+ * @param peerPublic - The other side's X25519 public key
104
+ * @param salt - 32 random bytes from the header
105
+ * @param moveId - Move id (bound into the derivation)
106
+ * @returns 32-byte key
107
+ */
108
+ function deriveKey(privateKey, peerPublic, salt, moveId) {
109
+ const shared = diffieHellman({ privateKey, publicKey: peerPublic });
110
+ return Buffer.from(hkdfSync('sha256', shared, salt, Buffer.from(HKDF_INFO_PREFIX + moveId, 'utf8'), 32));
111
+ }
112
+ /**
113
+ * Nonce of frame `counter`: 4 zero bytes + 8-byte big-endian counter.
114
+ *
115
+ * @param counter - Frame index
116
+ * @returns 12-byte nonce
117
+ */
118
+ function nonceFor(counter) {
119
+ const nonce = Buffer.alloc(12);
120
+ nonce.writeBigUInt64BE(BigInt(counter), 4);
121
+ return nonce;
122
+ }
123
+ /**
124
+ * Authenticated data of a frame: header, frame index, final flag.
125
+ *
126
+ * @param header - Package header bytes
127
+ * @param counter - Frame index
128
+ * @param final - Whether this is the last frame
129
+ * @returns AAD bytes
130
+ */
131
+ function aadFor(header, counter, final) {
132
+ const tail = Buffer.alloc(9);
133
+ tail.writeBigUInt64BE(BigInt(counter), 0);
134
+ tail.writeUInt8(final ? 1 : 0, 8);
135
+ return Buffer.concat([header, tail]);
136
+ }
137
+ /**
138
+ * Transform that encrypts a byte stream for the target of a move.
139
+ *
140
+ * @param options - Recipient key, move id, frame size
141
+ * @returns A Transform (plaintext in, package out)
142
+ * @throws TeamMoveCryptoError for an invalid recipient key or move id
143
+ */
144
+ export function createEncryptStream(options) {
145
+ const frameBytes = options.frameBytes ?? TEAM_MOVE_CONSTANTS.CRYPTO_FRAME_BYTES;
146
+ if (!Number.isInteger(frameBytes) || frameBytes <= 0 || frameBytes > MAX_FRAME_BYTES) {
147
+ throw new TeamMoveCryptoError('invalid frame size');
148
+ }
149
+ const moveId = Buffer.from(options.moveId, 'utf8');
150
+ if (moveId.length === 0 || moveId.length > 255)
151
+ throw new TeamMoveCryptoError('invalid move id');
152
+ if (!isPublicKeyString(options.recipientPublicKey))
153
+ throw new TeamMoveCryptoError('invalid recipient public key');
154
+ const eph = generateKeyPairSync('x25519');
155
+ const salt = randomBytes(KEY_BYTES);
156
+ const key = deriveKey(eph.privateKey, importPublicKey(Buffer.from(options.recipientPublicKey, 'base64')), salt, options.moveId);
157
+ const fixed = Buffer.alloc(MAGIC.length + 1 + 4);
158
+ MAGIC.copy(fixed, 0);
159
+ fixed.writeUInt8(FORMAT_VERSION, MAGIC.length);
160
+ fixed.writeUInt32BE(frameBytes, MAGIC.length + 1);
161
+ const header = Buffer.concat([fixed, rawPublicKey(eph.publicKey), salt, Buffer.from([moveId.length]), moveId]);
162
+ let pending = [];
163
+ let pendingBytes = 0;
164
+ let counter = 0;
165
+ let headerSent = false;
166
+ const seal = (plain, final) => {
167
+ const cipher = createCipheriv('aes-256-gcm', key, nonceFor(counter), { authTagLength: TAG_BYTES });
168
+ cipher.setAAD(aadFor(header, counter, final));
169
+ const body = Buffer.concat([cipher.update(plain), cipher.final(), cipher.getAuthTag()]);
170
+ counter += 1;
171
+ const len = Buffer.alloc(4);
172
+ len.writeUInt32BE(body.length, 0);
173
+ return Buffer.concat([len, body]);
174
+ };
175
+ return new Transform({
176
+ transform(chunk, _encoding, callback) {
177
+ try {
178
+ if (!headerSent) {
179
+ this.push(header);
180
+ headerSent = true;
181
+ }
182
+ pending.push(chunk);
183
+ pendingBytes += chunk.length;
184
+ // Keep at least one byte back so the last frame can be sealed as final.
185
+ while (pendingBytes > frameBytes) {
186
+ const all = Buffer.concat(pending);
187
+ this.push(seal(all.subarray(0, frameBytes), false));
188
+ const rest = all.subarray(frameBytes);
189
+ pending = rest.length > 0 ? [rest] : [];
190
+ pendingBytes = rest.length;
191
+ }
192
+ callback();
193
+ }
194
+ catch (error) {
195
+ callback(error);
196
+ }
197
+ },
198
+ flush(callback) {
199
+ try {
200
+ if (!headerSent) {
201
+ this.push(header);
202
+ headerSent = true;
203
+ }
204
+ this.push(seal(Buffer.concat(pending), true));
205
+ callback();
206
+ }
207
+ catch (error) {
208
+ callback(error);
209
+ }
210
+ },
211
+ });
212
+ }
213
+ /**
214
+ * Transform that decrypts a package on the target. Fails with
215
+ * {@link TeamMoveCryptoError} on any altered, truncated, reordered or
216
+ * foreign-move package.
217
+ *
218
+ * @param options - Target private key and expected move id
219
+ * @returns A Transform (package in, plaintext out)
220
+ */
221
+ export function createDecryptStream(options) {
222
+ let privateKey;
223
+ try {
224
+ privateKey = createPrivateKey({ key: Buffer.from(options.privateKey, 'base64'), format: 'der', type: 'pkcs8' });
225
+ }
226
+ catch {
227
+ throw new TeamMoveCryptoError('invalid private key');
228
+ }
229
+ let buffer = Buffer.alloc(0);
230
+ let header = null;
231
+ let key = null;
232
+ let frameBytes = 0;
233
+ let counter = 0;
234
+ const open = (body, final) => {
235
+ if (!header || !key)
236
+ throw new TeamMoveCryptoError('package header missing');
237
+ if (body.length < TAG_BYTES)
238
+ throw new TeamMoveCryptoError('truncated frame');
239
+ const decipher = createDecipheriv('aes-256-gcm', key, nonceFor(counter), { authTagLength: TAG_BYTES });
240
+ decipher.setAAD(aadFor(header, counter, final));
241
+ decipher.setAuthTag(body.subarray(body.length - TAG_BYTES));
242
+ try {
243
+ const plain = Buffer.concat([decipher.update(body.subarray(0, body.length - TAG_BYTES)), decipher.final()]);
244
+ counter += 1;
245
+ return plain;
246
+ }
247
+ catch {
248
+ throw new TeamMoveCryptoError('package failed authentication (altered, truncated or not for this move)');
249
+ }
250
+ };
251
+ /** Parse the header once enough bytes are here; returns false when more are needed. */
252
+ const readHeader = () => {
253
+ if (buffer.length < FIXED_HEADER_BYTES)
254
+ return false;
255
+ if (!buffer.subarray(0, MAGIC.length).equals(MAGIC))
256
+ throw new TeamMoveCryptoError('not a team move package');
257
+ if (buffer.readUInt8(MAGIC.length) !== FORMAT_VERSION)
258
+ throw new TeamMoveCryptoError('unsupported package version');
259
+ const announced = buffer.readUInt32BE(MAGIC.length + 1);
260
+ if (announced <= 0 || announced > MAX_FRAME_BYTES)
261
+ throw new TeamMoveCryptoError('invalid frame size in package');
262
+ const idLength = buffer.readUInt8(FIXED_HEADER_BYTES - 1);
263
+ const total = FIXED_HEADER_BYTES + idLength;
264
+ if (buffer.length < total)
265
+ return false;
266
+ const moveId = buffer.subarray(FIXED_HEADER_BYTES, total).toString('utf8');
267
+ if (moveId !== options.moveId)
268
+ throw new TeamMoveCryptoError('package belongs to another move');
269
+ const ephPublic = buffer.subarray(MAGIC.length + 5, MAGIC.length + 5 + KEY_BYTES);
270
+ const salt = buffer.subarray(MAGIC.length + 5 + KEY_BYTES, MAGIC.length + 5 + 2 * KEY_BYTES);
271
+ header = Buffer.from(buffer.subarray(0, total));
272
+ key = deriveKey(privateKey, importPublicKey(Buffer.from(ephPublic)), Buffer.from(salt), moveId);
273
+ frameBytes = announced;
274
+ buffer = buffer.subarray(total);
275
+ return true;
276
+ };
277
+ /**
278
+ * Open every frame that is certainly not the last one: a frame is open-able
279
+ * once at least one more byte follows it.
280
+ */
281
+ const drain = (push) => {
282
+ for (;;) {
283
+ if (!header && !readHeader())
284
+ return;
285
+ if (buffer.length < 4)
286
+ return;
287
+ const length = buffer.readUInt32BE(0);
288
+ if (length < TAG_BYTES || length > frameBytes + TAG_BYTES)
289
+ throw new TeamMoveCryptoError('invalid frame length');
290
+ if (buffer.length < 4 + length + 1)
291
+ return; // need a byte beyond it to know it is not final
292
+ const body = buffer.subarray(4, 4 + length);
293
+ push(open(body, false));
294
+ buffer = buffer.subarray(4 + length);
295
+ }
296
+ };
297
+ return new Transform({
298
+ transform(chunk, _encoding, callback) {
299
+ try {
300
+ buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk]);
301
+ drain((b) => this.push(b));
302
+ callback();
303
+ }
304
+ catch (error) {
305
+ callback(error);
306
+ }
307
+ },
308
+ flush(callback) {
309
+ try {
310
+ if (!header && !readHeader())
311
+ throw new TeamMoveCryptoError('package is empty or truncated');
312
+ if (buffer.length < 4)
313
+ throw new TeamMoveCryptoError('package is truncated');
314
+ const length = buffer.readUInt32BE(0);
315
+ if (length < TAG_BYTES || length > frameBytes + TAG_BYTES)
316
+ throw new TeamMoveCryptoError('invalid frame length');
317
+ if (buffer.length !== 4 + length)
318
+ throw new TeamMoveCryptoError('package is truncated or has trailing data');
319
+ this.push(open(buffer.subarray(4), true));
320
+ buffer = Buffer.alloc(0);
321
+ callback();
322
+ }
323
+ catch (error) {
324
+ callback(error);
325
+ }
326
+ },
327
+ });
328
+ }
329
+ /**
330
+ * Pass-through stream computing SHA-256 and size of its data.
331
+ *
332
+ * @returns The tap
333
+ */
334
+ export function createHashTap() {
335
+ const hash = createHash('sha256');
336
+ let bytes = 0;
337
+ const tap = new Transform({
338
+ transform(chunk, _encoding, callback) {
339
+ hash.update(chunk);
340
+ bytes += chunk.length;
341
+ callback(null, chunk);
342
+ },
343
+ });
344
+ Object.defineProperty(tap, 'bytes', { get: () => bytes });
345
+ tap.digest = () => hash.digest('hex');
346
+ return tap;
347
+ }
348
+ //# sourceMappingURL=team-move-crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-move-crypto.js","sourceRoot":"","sources":["../../../../../../backend/src/services/team-move/team-move-crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,QAAQ,EACR,WAAW,GAEZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,wDAAwD;AACxD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC3C,oCAAoC;AACpC,MAAM,cAAc,GAAG,CAAC,CAAC;AACzB,6BAA6B;AAC7B,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,mCAAmC;AACnC,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,8EAA8E;AAC9E,MAAM,kBAAkB,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;AAC1E,iDAAiD;AACjD,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAChD,gEAAgE;AAChE,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,oDAAoD;AACpD,MAAM,kBAAkB,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,GAAG,SAAS,GAAG,CAAC,CAAC;AAU5E,qEAAqE;AACrE,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACnC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAChE,OAAO;QACL,SAAS,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACrD,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;KACnF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,GAAc;IAClC,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3G,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,GAAW;IAClC,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;QAAE,MAAM,IAAI,mBAAmB,CAAC,6BAA6B,CAAC,CAAC;IAC3F,OAAO,eAAe,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AACzG,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC;AAC9H,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,SAAS,CAAC,UAAqB,EAAE,UAAqB,EAAE,IAAY,EAAE,MAAc;IAC3F,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;IACpE,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3G,CAAC;AAED;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,OAAe;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/B,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3C,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,MAAM,CAAC,MAAc,EAAE,OAAe,EAAE,KAAc;IAC7D,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AACvC,CAAC;AAYD;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC,kBAAkB,CAAC;IAChF,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,CAAC,IAAI,UAAU,GAAG,eAAe,EAAE,CAAC;QACrF,MAAM,IAAI,mBAAmB,CAAC,oBAAoB,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG;QAAE,MAAM,IAAI,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;IACjG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,kBAAkB,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,8BAA8B,CAAC,CAAC;IAElH,MAAM,GAAG,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAEhI,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACrB,KAAK,CAAC,UAAU,CAAC,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAE/G,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,KAAc,EAAU,EAAE;QACrD,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC;QACnG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QACxF,OAAO,IAAI,CAAC,CAAC;QACb,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAClC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC;IAEF,OAAO,IAAI,SAAS,CAAC;QACnB,SAAS,CAAC,KAAa,EAAE,SAAS,EAAE,QAA2B;YAC7D,IAAI,CAAC;gBACH,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAClB,UAAU,GAAG,IAAI,CAAC;gBACpB,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACpB,YAAY,IAAI,KAAK,CAAC,MAAM,CAAC;gBAC7B,wEAAwE;gBACxE,OAAO,YAAY,GAAG,UAAU,EAAE,CAAC;oBACjC,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;oBACnC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;oBACpD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;oBACtC,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBACxC,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;gBAC7B,CAAC;gBACD,QAAQ,EAAE,CAAC;YACb,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,QAAQ,CAAC,KAAc,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QACD,KAAK,CAAC,QAA2B;YAC/B,IAAI,CAAC;gBACH,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAClB,UAAU,GAAG,IAAI,CAAC;gBACpB,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC9C,QAAQ,EAAE,CAAC;YACb,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,QAAQ,CAAC,KAAc,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAUD;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,IAAI,UAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,UAAU,GAAG,gBAAgB,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAClH,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,mBAAmB,CAAC,qBAAqB,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,MAAM,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,GAAG,GAAkB,IAAI,CAAC;IAC9B,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,KAAc,EAAU,EAAE;QACpD,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,mBAAmB,CAAC,wBAAwB,CAAC,CAAC;QAC7E,IAAI,IAAI,CAAC,MAAM,GAAG,SAAS;YAAE,MAAM,IAAI,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;QAC9E,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC;QACvG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAChD,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAC5G,OAAO,IAAI,CAAC,CAAC;YACb,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,mBAAmB,CAAC,yEAAyE,CAAC,CAAC;QAC3G,CAAC;IACH,CAAC,CAAC;IAEF,uFAAuF;IACvF,MAAM,UAAU,GAAG,GAAY,EAAE;QAC/B,IAAI,MAAM,CAAC,MAAM,GAAG,kBAAkB;YAAE,OAAO,KAAK,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,mBAAmB,CAAC,yBAAyB,CAAC,CAAC;QAC9G,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,cAAc;YAAE,MAAM,IAAI,mBAAmB,CAAC,6BAA6B,CAAC,CAAC;QACpH,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACxD,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,GAAG,eAAe;YAAE,MAAM,IAAI,mBAAmB,CAAC,+BAA+B,CAAC,CAAC;QAClH,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAG,kBAAkB,GAAG,QAAQ,CAAC;QAC5C,IAAI,MAAM,CAAC,MAAM,GAAG,KAAK;YAAE,OAAO,KAAK,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3E,IAAI,MAAM,KAAK,OAAO,CAAC,MAAM;YAAE,MAAM,IAAI,mBAAmB,CAAC,iCAAiC,CAAC,CAAC;QAChG,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;QAClF,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;QAC7F,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QAChD,GAAG,GAAG,SAAS,CAAC,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAChG,UAAU,GAAG,SAAS,CAAC;QACvB,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF;;;OAGG;IACH,MAAM,KAAK,GAAG,CAAC,IAAyB,EAAQ,EAAE;QAChD,SAAS,CAAC;YACR,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE;gBAAE,OAAO;YACrC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO;YAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,MAAM,GAAG,SAAS,IAAI,MAAM,GAAG,UAAU,GAAG,SAAS;gBAAE,MAAM,IAAI,mBAAmB,CAAC,sBAAsB,CAAC,CAAC;YACjH,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC;gBAAE,OAAO,CAAC,gDAAgD;YAC5F,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YACxB,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,IAAI,SAAS,CAAC;QACnB,SAAS,CAAC,KAAa,EAAE,SAAS,EAAE,QAA2B;YAC7D,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;gBACtE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3B,QAAQ,EAAE,CAAC;YACb,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,QAAQ,CAAC,KAAc,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QACD,KAAK,CAAC,QAA2B;YAC/B,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE;oBAAE,MAAM,IAAI,mBAAmB,CAAC,+BAA+B,CAAC,CAAC;gBAC7F,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;oBAAE,MAAM,IAAI,mBAAmB,CAAC,sBAAsB,CAAC,CAAC;gBAC7E,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;gBACtC,IAAI,MAAM,GAAG,SAAS,IAAI,MAAM,GAAG,UAAU,GAAG,SAAS;oBAAE,MAAM,IAAI,mBAAmB,CAAC,sBAAsB,CAAC,CAAC;gBACjH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,MAAM;oBAAE,MAAM,IAAI,mBAAmB,CAAC,2CAA2C,CAAC,CAAC;gBAC7G,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC1C,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBACzB,QAAQ,EAAE,CAAC;YACb,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,QAAQ,CAAC,KAAc,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC;QACxB,SAAS,CAAC,KAAa,EAAE,SAAS,EAAE,QAA2B;YAC7D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnB,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;YACtB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;KACF,CAAY,CAAC;IACd,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1D,GAAG,CAAC,MAAM,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Team move driver — the machine side of the Cloud protocol.
3
+ *
4
+ * Cloud tells a machine what to do with a `team_move` relay push
5
+ * (`{moveId, role, action}`); the push is only a hint, the driver fetches the
6
+ * move from Cloud and acts on what Cloud says. The same list is read at boot
7
+ * and every few minutes, so a machine that was offline when the push went out
8
+ * picks the move up when it returns. Every action is idempotent (journaled
9
+ * steps, resumable upload/download), so a repeated push or a restart in the
10
+ * middle is harmless.
11
+ *
12
+ * target prepare -> make the per-move key, preflight, publish the key
13
+ * source package -> collect, scan, encrypt, upload
14
+ * target import -> download, decrypt, import, start, report `imported`
15
+ * source stop_archive -> stop the team, archive it, report `source_stopped`
16
+ * either rollback -> source: restore + start (`source_restored`);
17
+ * target: stop + archive its copy (`target_stopped`).
18
+ * Also "abort and clean up" after a failure.
19
+ *
20
+ * Starting the driver advertises the `teamMove:1` capability to Cloud.
21
+ *
22
+ * @module services/team-move/team-move-driver.service
23
+ */
24
+ import { type ComponentLogger } from '../core/logger.service.js';
25
+ import type { IncomingMessage } from './../cloud/cloud-sync.types.js';
26
+ import { TeamMoveArchiver } from './team-move-archive.js';
27
+ import { TeamMoveCloudClient } from './team-move-cloud.client.js';
28
+ import { TeamMoveExporter } from './team-move-exporter.js';
29
+ import { TeamMoveImporter } from './team-move-importer.js';
30
+ import type { PackageRepo, TeamMovePreflight, TeamMoveRelayData, TeamMoveView } from './team-move.types.js';
31
+ /**
32
+ * Install the process-wide driver (composition root).
33
+ *
34
+ * @param driver - The driver or null
35
+ */
36
+ export declare function setTeamMoveDriver(driver: TeamMoveDriver | null): void;
37
+ /**
38
+ * The process-wide driver, or null before boot wired it.
39
+ *
40
+ * @returns The driver or null
41
+ */
42
+ export declare function getTeamMoveDriver(): TeamMoveDriver | null;
43
+ /**
44
+ * Whether the driver runs on this machine.
45
+ *
46
+ * @returns True once started
47
+ */
48
+ export declare function isTeamMoveDriverActive(): boolean;
49
+ /**
50
+ * Capabilities to advertise to Cloud (heartbeat).
51
+ *
52
+ * @returns `['teamMove:1']` while the driver runs, else `[]`
53
+ */
54
+ export declare function teamMoveCapabilities(): string[];
55
+ /** Emitter of relay messages (CloudSyncService). */
56
+ export interface TeamMoveRelaySource {
57
+ on(event: 'message', listener: (msg: IncomingMessage) => void): unknown;
58
+ off(event: 'message', listener: (msg: IncomingMessage) => void): unknown;
59
+ }
60
+ /** Collaborators of the driver. */
61
+ export interface TeamMoveDriverDeps {
62
+ home?: string;
63
+ cloud: TeamMoveCloudClient;
64
+ source: TeamMoveRelaySource;
65
+ identity: () => Promise<{
66
+ instanceId: string;
67
+ deviceName: string;
68
+ }>;
69
+ exporter: TeamMoveExporter;
70
+ /** Built per import so each uses the current services. */
71
+ importer: () => TeamMoveImporter;
72
+ archiver: TeamMoveArchiver;
73
+ preflight: (requiredTools?: readonly string[]) => Promise<TeamMovePreflight>;
74
+ /** True when no member of the team is working. */
75
+ isTeamIdle: (teamId: string) => Promise<boolean>;
76
+ sleep?: (ms: number) => Promise<void>;
77
+ now?: () => string;
78
+ logger?: ComponentLogger;
79
+ }
80
+ /** The driver. */
81
+ export declare class TeamMoveDriver {
82
+ private readonly deps;
83
+ private readonly logger;
84
+ private readonly home;
85
+ private readonly sleep;
86
+ private readonly now;
87
+ private readonly inFlight;
88
+ private readonly listener;
89
+ private pollTimer;
90
+ private bootTimer;
91
+ private started;
92
+ constructor(deps: TeamMoveDriverDeps);
93
+ /** Listen for pushes, resume pending moves, advertise the capability. Idempotent. */
94
+ start(): void;
95
+ /** Stop listening; the capability is no longer advertised. */
96
+ stop(): void;
97
+ /**
98
+ * Handle one relay message. Anything but a well-formed `team_move` is ignored.
99
+ *
100
+ * @param msg - Relay message
101
+ * @returns True when it was a team move push that started work
102
+ */
103
+ handleRelay(msg: IncomingMessage): Promise<boolean>;
104
+ /** Read this machine's pending moves from Cloud and act on each. */
105
+ poll(): Promise<number>;
106
+ /** Run one action (one at a time per move and action). */
107
+ private dispatch;
108
+ private execute;
109
+ private prepare;
110
+ private packageAndUpload;
111
+ private waitForIdle;
112
+ private importPackage;
113
+ private stopArchive;
114
+ private rollback;
115
+ /**
116
+ * Ask Cloud to move a team from this machine to another.
117
+ *
118
+ * @param input - Team, target machine, flags
119
+ * @returns The new move id
120
+ * @throws TeamMoveBlockedError when a repository holds unpushed work (unless allowed)
121
+ */
122
+ startMove(input: {
123
+ teamId: string;
124
+ targetInstanceId: string;
125
+ allowDirty?: boolean;
126
+ skipFlagged?: boolean;
127
+ }): Promise<{
128
+ moveId: string;
129
+ }>;
130
+ /**
131
+ * What would stop a move of a team: repositories with unpushed or uncommitted work.
132
+ *
133
+ * @param teamId - Team id
134
+ * @returns Repo states and the blocking lines
135
+ */
136
+ checkRepos(teamId: string): Promise<{
137
+ repos: PackageRepo[];
138
+ blocking: string[];
139
+ }>;
140
+ /**
141
+ * Dry run: build the package here exactly as a move would, encrypt it with a
142
+ * throwaway key to measure it, and report. Nothing is sent anywhere.
143
+ *
144
+ * @param input - Team and a scratch directory
145
+ * @returns Manifest highlights and sizes
146
+ */
147
+ preview(input: {
148
+ teamId: string;
149
+ stagingDir: string;
150
+ allowDirty?: boolean;
151
+ skipFlagged?: boolean;
152
+ }): Promise<Record<string, unknown>>;
153
+ /**
154
+ * Moves known to Cloud plus what this machine remembers of each.
155
+ *
156
+ * @returns Moves, newest first
157
+ */
158
+ listMoves(): Promise<Array<TeamMoveView & {
159
+ local?: {
160
+ phase: string;
161
+ action: string | null;
162
+ error?: string;
163
+ };
164
+ }>>;
165
+ /**
166
+ * One move with its local progress.
167
+ *
168
+ * @param moveId - Move id
169
+ * @returns Cloud's view plus the local journal
170
+ */
171
+ getMove(moveId: string): Promise<{
172
+ move: TeamMoveView;
173
+ journal: Array<{
174
+ step: string;
175
+ state: string;
176
+ at: string;
177
+ }>;
178
+ }>;
179
+ /**
180
+ * Owner rollback.
181
+ *
182
+ * @param moveId - Move id
183
+ */
184
+ rollbackMove(moveId: string): Promise<void>;
185
+ /**
186
+ * Housekeeping at boot and daily: purge expired archives.
187
+ *
188
+ * @returns Purged move ids
189
+ */
190
+ housekeeping(): Promise<string[]>;
191
+ private readIntent;
192
+ /** Tell Cloud an action failed, with text the owner can act on. */
193
+ private reportFailure;
194
+ }
195
+ /**
196
+ * Turn an error into the sentence the owner sees.
197
+ *
198
+ * @param error - Thrown value
199
+ * @returns Message
200
+ */
201
+ export declare function friendlyError(error: unknown): string;
202
+ /**
203
+ * Parse the data of a `team_move` push.
204
+ *
205
+ * @param payload - Relay payload
206
+ * @returns Parsed data, or null when malformed
207
+ */
208
+ export declare function parseRelayData(payload: unknown): TeamMoveRelayData | null;
209
+ //# sourceMappingURL=team-move-driver.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-move-driver.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/team-move/team-move-driver.service.ts"],"names":[],"mappings":"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"}