@floomhq/floom 1.0.63 → 2.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.
package/dist/index.js ADDED
@@ -0,0 +1,3663 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
+ }) : x)(function(x) {
11
+ if (typeof require !== "undefined") return require.apply(this, arguments);
12
+ throw Error('Dynamic require of "' + x + '" is not supported');
13
+ });
14
+ var __commonJS = (cb, mod) => function __require2() {
15
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+
34
+ // ../../node_modules/.pnpm/bcryptjs@2.4.3/node_modules/bcryptjs/dist/bcrypt.js
35
+ var require_bcrypt = __commonJS({
36
+ "../../node_modules/.pnpm/bcryptjs@2.4.3/node_modules/bcryptjs/dist/bcrypt.js"(exports, module) {
37
+ (function(global, factory) {
38
+ if (typeof define === "function" && define["amd"])
39
+ define([], factory);
40
+ else if (typeof __require === "function" && typeof module === "object" && module && module["exports"])
41
+ module["exports"] = factory();
42
+ else
43
+ (global["dcodeIO"] = global["dcodeIO"] || {})["bcrypt"] = factory();
44
+ })(exports, function() {
45
+ "use strict";
46
+ var bcrypt2 = {};
47
+ var randomFallback = null;
48
+ function random(len) {
49
+ if (typeof module !== "undefined" && module && module["exports"])
50
+ try {
51
+ return __require("crypto")["randomBytes"](len);
52
+ } catch (e) {
53
+ }
54
+ try {
55
+ var a;
56
+ (self["crypto"] || self["msCrypto"])["getRandomValues"](a = new Uint32Array(len));
57
+ return Array.prototype.slice.call(a);
58
+ } catch (e) {
59
+ }
60
+ if (!randomFallback)
61
+ throw Error("Neither WebCryptoAPI nor a crypto module is available. Use bcrypt.setRandomFallback to set an alternative");
62
+ return randomFallback(len);
63
+ }
64
+ var randomAvailable = false;
65
+ try {
66
+ random(1);
67
+ randomAvailable = true;
68
+ } catch (e) {
69
+ }
70
+ randomFallback = null;
71
+ bcrypt2.setRandomFallback = function(random2) {
72
+ randomFallback = random2;
73
+ };
74
+ bcrypt2.genSaltSync = function(rounds, seed_length) {
75
+ rounds = rounds || GENSALT_DEFAULT_LOG2_ROUNDS;
76
+ if (typeof rounds !== "number")
77
+ throw Error("Illegal arguments: " + typeof rounds + ", " + typeof seed_length);
78
+ if (rounds < 4)
79
+ rounds = 4;
80
+ else if (rounds > 31)
81
+ rounds = 31;
82
+ var salt = [];
83
+ salt.push("$2a$");
84
+ if (rounds < 10)
85
+ salt.push("0");
86
+ salt.push(rounds.toString());
87
+ salt.push("$");
88
+ salt.push(base64_encode(random(BCRYPT_SALT_LEN), BCRYPT_SALT_LEN));
89
+ return salt.join("");
90
+ };
91
+ bcrypt2.genSalt = function(rounds, seed_length, callback) {
92
+ if (typeof seed_length === "function")
93
+ callback = seed_length, seed_length = void 0;
94
+ if (typeof rounds === "function")
95
+ callback = rounds, rounds = void 0;
96
+ if (typeof rounds === "undefined")
97
+ rounds = GENSALT_DEFAULT_LOG2_ROUNDS;
98
+ else if (typeof rounds !== "number")
99
+ throw Error("illegal arguments: " + typeof rounds);
100
+ function _async(callback2) {
101
+ nextTick(function() {
102
+ try {
103
+ callback2(null, bcrypt2.genSaltSync(rounds));
104
+ } catch (err) {
105
+ callback2(err);
106
+ }
107
+ });
108
+ }
109
+ if (callback) {
110
+ if (typeof callback !== "function")
111
+ throw Error("Illegal callback: " + typeof callback);
112
+ _async(callback);
113
+ } else
114
+ return new Promise(function(resolve3, reject) {
115
+ _async(function(err, res) {
116
+ if (err) {
117
+ reject(err);
118
+ return;
119
+ }
120
+ resolve3(res);
121
+ });
122
+ });
123
+ };
124
+ bcrypt2.hashSync = function(s, salt) {
125
+ if (typeof salt === "undefined")
126
+ salt = GENSALT_DEFAULT_LOG2_ROUNDS;
127
+ if (typeof salt === "number")
128
+ salt = bcrypt2.genSaltSync(salt);
129
+ if (typeof s !== "string" || typeof salt !== "string")
130
+ throw Error("Illegal arguments: " + typeof s + ", " + typeof salt);
131
+ return _hash(s, salt);
132
+ };
133
+ bcrypt2.hash = function(s, salt, callback, progressCallback) {
134
+ function _async(callback2) {
135
+ if (typeof s === "string" && typeof salt === "number")
136
+ bcrypt2.genSalt(salt, function(err, salt2) {
137
+ _hash(s, salt2, callback2, progressCallback);
138
+ });
139
+ else if (typeof s === "string" && typeof salt === "string")
140
+ _hash(s, salt, callback2, progressCallback);
141
+ else
142
+ nextTick(callback2.bind(this, Error("Illegal arguments: " + typeof s + ", " + typeof salt)));
143
+ }
144
+ if (callback) {
145
+ if (typeof callback !== "function")
146
+ throw Error("Illegal callback: " + typeof callback);
147
+ _async(callback);
148
+ } else
149
+ return new Promise(function(resolve3, reject) {
150
+ _async(function(err, res) {
151
+ if (err) {
152
+ reject(err);
153
+ return;
154
+ }
155
+ resolve3(res);
156
+ });
157
+ });
158
+ };
159
+ function safeStringCompare(known, unknown) {
160
+ var right = 0, wrong = 0;
161
+ for (var i = 0, k = known.length; i < k; ++i) {
162
+ if (known.charCodeAt(i) === unknown.charCodeAt(i))
163
+ ++right;
164
+ else
165
+ ++wrong;
166
+ }
167
+ if (right < 0)
168
+ return false;
169
+ return wrong === 0;
170
+ }
171
+ bcrypt2.compareSync = function(s, hash) {
172
+ if (typeof s !== "string" || typeof hash !== "string")
173
+ throw Error("Illegal arguments: " + typeof s + ", " + typeof hash);
174
+ if (hash.length !== 60)
175
+ return false;
176
+ return safeStringCompare(bcrypt2.hashSync(s, hash.substr(0, hash.length - 31)), hash);
177
+ };
178
+ bcrypt2.compare = function(s, hash, callback, progressCallback) {
179
+ function _async(callback2) {
180
+ if (typeof s !== "string" || typeof hash !== "string") {
181
+ nextTick(callback2.bind(this, Error("Illegal arguments: " + typeof s + ", " + typeof hash)));
182
+ return;
183
+ }
184
+ if (hash.length !== 60) {
185
+ nextTick(callback2.bind(this, null, false));
186
+ return;
187
+ }
188
+ bcrypt2.hash(s, hash.substr(0, 29), function(err, comp) {
189
+ if (err)
190
+ callback2(err);
191
+ else
192
+ callback2(null, safeStringCompare(comp, hash));
193
+ }, progressCallback);
194
+ }
195
+ if (callback) {
196
+ if (typeof callback !== "function")
197
+ throw Error("Illegal callback: " + typeof callback);
198
+ _async(callback);
199
+ } else
200
+ return new Promise(function(resolve3, reject) {
201
+ _async(function(err, res) {
202
+ if (err) {
203
+ reject(err);
204
+ return;
205
+ }
206
+ resolve3(res);
207
+ });
208
+ });
209
+ };
210
+ bcrypt2.getRounds = function(hash) {
211
+ if (typeof hash !== "string")
212
+ throw Error("Illegal arguments: " + typeof hash);
213
+ return parseInt(hash.split("$")[2], 10);
214
+ };
215
+ bcrypt2.getSalt = function(hash) {
216
+ if (typeof hash !== "string")
217
+ throw Error("Illegal arguments: " + typeof hash);
218
+ if (hash.length !== 60)
219
+ throw Error("Illegal hash length: " + hash.length + " != 60");
220
+ return hash.substring(0, 29);
221
+ };
222
+ var nextTick = typeof process !== "undefined" && process && typeof process.nextTick === "function" ? typeof setImmediate === "function" ? setImmediate : process.nextTick : setTimeout;
223
+ function stringToBytes(str) {
224
+ var out = [], i = 0;
225
+ utfx.encodeUTF16toUTF8(function() {
226
+ if (i >= str.length) return null;
227
+ return str.charCodeAt(i++);
228
+ }, function(b) {
229
+ out.push(b);
230
+ });
231
+ return out;
232
+ }
233
+ var BASE64_CODE = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
234
+ var BASE64_INDEX = [
235
+ -1,
236
+ -1,
237
+ -1,
238
+ -1,
239
+ -1,
240
+ -1,
241
+ -1,
242
+ -1,
243
+ -1,
244
+ -1,
245
+ -1,
246
+ -1,
247
+ -1,
248
+ -1,
249
+ -1,
250
+ -1,
251
+ -1,
252
+ -1,
253
+ -1,
254
+ -1,
255
+ -1,
256
+ -1,
257
+ -1,
258
+ -1,
259
+ -1,
260
+ -1,
261
+ -1,
262
+ -1,
263
+ -1,
264
+ -1,
265
+ -1,
266
+ -1,
267
+ -1,
268
+ -1,
269
+ -1,
270
+ -1,
271
+ -1,
272
+ -1,
273
+ -1,
274
+ -1,
275
+ -1,
276
+ -1,
277
+ -1,
278
+ -1,
279
+ -1,
280
+ -1,
281
+ 0,
282
+ 1,
283
+ 54,
284
+ 55,
285
+ 56,
286
+ 57,
287
+ 58,
288
+ 59,
289
+ 60,
290
+ 61,
291
+ 62,
292
+ 63,
293
+ -1,
294
+ -1,
295
+ -1,
296
+ -1,
297
+ -1,
298
+ -1,
299
+ -1,
300
+ 2,
301
+ 3,
302
+ 4,
303
+ 5,
304
+ 6,
305
+ 7,
306
+ 8,
307
+ 9,
308
+ 10,
309
+ 11,
310
+ 12,
311
+ 13,
312
+ 14,
313
+ 15,
314
+ 16,
315
+ 17,
316
+ 18,
317
+ 19,
318
+ 20,
319
+ 21,
320
+ 22,
321
+ 23,
322
+ 24,
323
+ 25,
324
+ 26,
325
+ 27,
326
+ -1,
327
+ -1,
328
+ -1,
329
+ -1,
330
+ -1,
331
+ -1,
332
+ 28,
333
+ 29,
334
+ 30,
335
+ 31,
336
+ 32,
337
+ 33,
338
+ 34,
339
+ 35,
340
+ 36,
341
+ 37,
342
+ 38,
343
+ 39,
344
+ 40,
345
+ 41,
346
+ 42,
347
+ 43,
348
+ 44,
349
+ 45,
350
+ 46,
351
+ 47,
352
+ 48,
353
+ 49,
354
+ 50,
355
+ 51,
356
+ 52,
357
+ 53,
358
+ -1,
359
+ -1,
360
+ -1,
361
+ -1,
362
+ -1
363
+ ];
364
+ var stringFromCharCode = String.fromCharCode;
365
+ function base64_encode(b, len) {
366
+ var off = 0, rs = [], c1, c2;
367
+ if (len <= 0 || len > b.length)
368
+ throw Error("Illegal len: " + len);
369
+ while (off < len) {
370
+ c1 = b[off++] & 255;
371
+ rs.push(BASE64_CODE[c1 >> 2 & 63]);
372
+ c1 = (c1 & 3) << 4;
373
+ if (off >= len) {
374
+ rs.push(BASE64_CODE[c1 & 63]);
375
+ break;
376
+ }
377
+ c2 = b[off++] & 255;
378
+ c1 |= c2 >> 4 & 15;
379
+ rs.push(BASE64_CODE[c1 & 63]);
380
+ c1 = (c2 & 15) << 2;
381
+ if (off >= len) {
382
+ rs.push(BASE64_CODE[c1 & 63]);
383
+ break;
384
+ }
385
+ c2 = b[off++] & 255;
386
+ c1 |= c2 >> 6 & 3;
387
+ rs.push(BASE64_CODE[c1 & 63]);
388
+ rs.push(BASE64_CODE[c2 & 63]);
389
+ }
390
+ return rs.join("");
391
+ }
392
+ function base64_decode(s, len) {
393
+ var off = 0, slen = s.length, olen = 0, rs = [], c1, c2, c3, c4, o, code;
394
+ if (len <= 0)
395
+ throw Error("Illegal len: " + len);
396
+ while (off < slen - 1 && olen < len) {
397
+ code = s.charCodeAt(off++);
398
+ c1 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
399
+ code = s.charCodeAt(off++);
400
+ c2 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
401
+ if (c1 == -1 || c2 == -1)
402
+ break;
403
+ o = c1 << 2 >>> 0;
404
+ o |= (c2 & 48) >> 4;
405
+ rs.push(stringFromCharCode(o));
406
+ if (++olen >= len || off >= slen)
407
+ break;
408
+ code = s.charCodeAt(off++);
409
+ c3 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
410
+ if (c3 == -1)
411
+ break;
412
+ o = (c2 & 15) << 4 >>> 0;
413
+ o |= (c3 & 60) >> 2;
414
+ rs.push(stringFromCharCode(o));
415
+ if (++olen >= len || off >= slen)
416
+ break;
417
+ code = s.charCodeAt(off++);
418
+ c4 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
419
+ o = (c3 & 3) << 6 >>> 0;
420
+ o |= c4;
421
+ rs.push(stringFromCharCode(o));
422
+ ++olen;
423
+ }
424
+ var res = [];
425
+ for (off = 0; off < olen; off++)
426
+ res.push(rs[off].charCodeAt(0));
427
+ return res;
428
+ }
429
+ var utfx = (function() {
430
+ "use strict";
431
+ var utfx2 = {};
432
+ utfx2.MAX_CODEPOINT = 1114111;
433
+ utfx2.encodeUTF8 = function(src, dst) {
434
+ var cp2 = null;
435
+ if (typeof src === "number")
436
+ cp2 = src, src = function() {
437
+ return null;
438
+ };
439
+ while (cp2 !== null || (cp2 = src()) !== null) {
440
+ if (cp2 < 128)
441
+ dst(cp2 & 127);
442
+ else if (cp2 < 2048)
443
+ dst(cp2 >> 6 & 31 | 192), dst(cp2 & 63 | 128);
444
+ else if (cp2 < 65536)
445
+ dst(cp2 >> 12 & 15 | 224), dst(cp2 >> 6 & 63 | 128), dst(cp2 & 63 | 128);
446
+ else
447
+ dst(cp2 >> 18 & 7 | 240), dst(cp2 >> 12 & 63 | 128), dst(cp2 >> 6 & 63 | 128), dst(cp2 & 63 | 128);
448
+ cp2 = null;
449
+ }
450
+ };
451
+ utfx2.decodeUTF8 = function(src, dst) {
452
+ var a, b, c, d, fail = function(b2) {
453
+ b2 = b2.slice(0, b2.indexOf(null));
454
+ var err = Error(b2.toString());
455
+ err.name = "TruncatedError";
456
+ err["bytes"] = b2;
457
+ throw err;
458
+ };
459
+ while ((a = src()) !== null) {
460
+ if ((a & 128) === 0)
461
+ dst(a);
462
+ else if ((a & 224) === 192)
463
+ (b = src()) === null && fail([a, b]), dst((a & 31) << 6 | b & 63);
464
+ else if ((a & 240) === 224)
465
+ ((b = src()) === null || (c = src()) === null) && fail([a, b, c]), dst((a & 15) << 12 | (b & 63) << 6 | c & 63);
466
+ else if ((a & 248) === 240)
467
+ ((b = src()) === null || (c = src()) === null || (d = src()) === null) && fail([a, b, c, d]), dst((a & 7) << 18 | (b & 63) << 12 | (c & 63) << 6 | d & 63);
468
+ else throw RangeError("Illegal starting byte: " + a);
469
+ }
470
+ };
471
+ utfx2.UTF16toUTF8 = function(src, dst) {
472
+ var c1, c2 = null;
473
+ while (true) {
474
+ if ((c1 = c2 !== null ? c2 : src()) === null)
475
+ break;
476
+ if (c1 >= 55296 && c1 <= 57343) {
477
+ if ((c2 = src()) !== null) {
478
+ if (c2 >= 56320 && c2 <= 57343) {
479
+ dst((c1 - 55296) * 1024 + c2 - 56320 + 65536);
480
+ c2 = null;
481
+ continue;
482
+ }
483
+ }
484
+ }
485
+ dst(c1);
486
+ }
487
+ if (c2 !== null) dst(c2);
488
+ };
489
+ utfx2.UTF8toUTF16 = function(src, dst) {
490
+ var cp2 = null;
491
+ if (typeof src === "number")
492
+ cp2 = src, src = function() {
493
+ return null;
494
+ };
495
+ while (cp2 !== null || (cp2 = src()) !== null) {
496
+ if (cp2 <= 65535)
497
+ dst(cp2);
498
+ else
499
+ cp2 -= 65536, dst((cp2 >> 10) + 55296), dst(cp2 % 1024 + 56320);
500
+ cp2 = null;
501
+ }
502
+ };
503
+ utfx2.encodeUTF16toUTF8 = function(src, dst) {
504
+ utfx2.UTF16toUTF8(src, function(cp2) {
505
+ utfx2.encodeUTF8(cp2, dst);
506
+ });
507
+ };
508
+ utfx2.decodeUTF8toUTF16 = function(src, dst) {
509
+ utfx2.decodeUTF8(src, function(cp2) {
510
+ utfx2.UTF8toUTF16(cp2, dst);
511
+ });
512
+ };
513
+ utfx2.calculateCodePoint = function(cp2) {
514
+ return cp2 < 128 ? 1 : cp2 < 2048 ? 2 : cp2 < 65536 ? 3 : 4;
515
+ };
516
+ utfx2.calculateUTF8 = function(src) {
517
+ var cp2, l = 0;
518
+ while ((cp2 = src()) !== null)
519
+ l += utfx2.calculateCodePoint(cp2);
520
+ return l;
521
+ };
522
+ utfx2.calculateUTF16asUTF8 = function(src) {
523
+ var n = 0, l = 0;
524
+ utfx2.UTF16toUTF8(src, function(cp2) {
525
+ ++n;
526
+ l += utfx2.calculateCodePoint(cp2);
527
+ });
528
+ return [n, l];
529
+ };
530
+ return utfx2;
531
+ })();
532
+ Date.now = Date.now || function() {
533
+ return +/* @__PURE__ */ new Date();
534
+ };
535
+ var BCRYPT_SALT_LEN = 16;
536
+ var GENSALT_DEFAULT_LOG2_ROUNDS = 10;
537
+ var BLOWFISH_NUM_ROUNDS = 16;
538
+ var MAX_EXECUTION_TIME = 100;
539
+ var P_ORIG = [
540
+ 608135816,
541
+ 2242054355,
542
+ 320440878,
543
+ 57701188,
544
+ 2752067618,
545
+ 698298832,
546
+ 137296536,
547
+ 3964562569,
548
+ 1160258022,
549
+ 953160567,
550
+ 3193202383,
551
+ 887688300,
552
+ 3232508343,
553
+ 3380367581,
554
+ 1065670069,
555
+ 3041331479,
556
+ 2450970073,
557
+ 2306472731
558
+ ];
559
+ var S_ORIG = [
560
+ 3509652390,
561
+ 2564797868,
562
+ 805139163,
563
+ 3491422135,
564
+ 3101798381,
565
+ 1780907670,
566
+ 3128725573,
567
+ 4046225305,
568
+ 614570311,
569
+ 3012652279,
570
+ 134345442,
571
+ 2240740374,
572
+ 1667834072,
573
+ 1901547113,
574
+ 2757295779,
575
+ 4103290238,
576
+ 227898511,
577
+ 1921955416,
578
+ 1904987480,
579
+ 2182433518,
580
+ 2069144605,
581
+ 3260701109,
582
+ 2620446009,
583
+ 720527379,
584
+ 3318853667,
585
+ 677414384,
586
+ 3393288472,
587
+ 3101374703,
588
+ 2390351024,
589
+ 1614419982,
590
+ 1822297739,
591
+ 2954791486,
592
+ 3608508353,
593
+ 3174124327,
594
+ 2024746970,
595
+ 1432378464,
596
+ 3864339955,
597
+ 2857741204,
598
+ 1464375394,
599
+ 1676153920,
600
+ 1439316330,
601
+ 715854006,
602
+ 3033291828,
603
+ 289532110,
604
+ 2706671279,
605
+ 2087905683,
606
+ 3018724369,
607
+ 1668267050,
608
+ 732546397,
609
+ 1947742710,
610
+ 3462151702,
611
+ 2609353502,
612
+ 2950085171,
613
+ 1814351708,
614
+ 2050118529,
615
+ 680887927,
616
+ 999245976,
617
+ 1800124847,
618
+ 3300911131,
619
+ 1713906067,
620
+ 1641548236,
621
+ 4213287313,
622
+ 1216130144,
623
+ 1575780402,
624
+ 4018429277,
625
+ 3917837745,
626
+ 3693486850,
627
+ 3949271944,
628
+ 596196993,
629
+ 3549867205,
630
+ 258830323,
631
+ 2213823033,
632
+ 772490370,
633
+ 2760122372,
634
+ 1774776394,
635
+ 2652871518,
636
+ 566650946,
637
+ 4142492826,
638
+ 1728879713,
639
+ 2882767088,
640
+ 1783734482,
641
+ 3629395816,
642
+ 2517608232,
643
+ 2874225571,
644
+ 1861159788,
645
+ 326777828,
646
+ 3124490320,
647
+ 2130389656,
648
+ 2716951837,
649
+ 967770486,
650
+ 1724537150,
651
+ 2185432712,
652
+ 2364442137,
653
+ 1164943284,
654
+ 2105845187,
655
+ 998989502,
656
+ 3765401048,
657
+ 2244026483,
658
+ 1075463327,
659
+ 1455516326,
660
+ 1322494562,
661
+ 910128902,
662
+ 469688178,
663
+ 1117454909,
664
+ 936433444,
665
+ 3490320968,
666
+ 3675253459,
667
+ 1240580251,
668
+ 122909385,
669
+ 2157517691,
670
+ 634681816,
671
+ 4142456567,
672
+ 3825094682,
673
+ 3061402683,
674
+ 2540495037,
675
+ 79693498,
676
+ 3249098678,
677
+ 1084186820,
678
+ 1583128258,
679
+ 426386531,
680
+ 1761308591,
681
+ 1047286709,
682
+ 322548459,
683
+ 995290223,
684
+ 1845252383,
685
+ 2603652396,
686
+ 3431023940,
687
+ 2942221577,
688
+ 3202600964,
689
+ 3727903485,
690
+ 1712269319,
691
+ 422464435,
692
+ 3234572375,
693
+ 1170764815,
694
+ 3523960633,
695
+ 3117677531,
696
+ 1434042557,
697
+ 442511882,
698
+ 3600875718,
699
+ 1076654713,
700
+ 1738483198,
701
+ 4213154764,
702
+ 2393238008,
703
+ 3677496056,
704
+ 1014306527,
705
+ 4251020053,
706
+ 793779912,
707
+ 2902807211,
708
+ 842905082,
709
+ 4246964064,
710
+ 1395751752,
711
+ 1040244610,
712
+ 2656851899,
713
+ 3396308128,
714
+ 445077038,
715
+ 3742853595,
716
+ 3577915638,
717
+ 679411651,
718
+ 2892444358,
719
+ 2354009459,
720
+ 1767581616,
721
+ 3150600392,
722
+ 3791627101,
723
+ 3102740896,
724
+ 284835224,
725
+ 4246832056,
726
+ 1258075500,
727
+ 768725851,
728
+ 2589189241,
729
+ 3069724005,
730
+ 3532540348,
731
+ 1274779536,
732
+ 3789419226,
733
+ 2764799539,
734
+ 1660621633,
735
+ 3471099624,
736
+ 4011903706,
737
+ 913787905,
738
+ 3497959166,
739
+ 737222580,
740
+ 2514213453,
741
+ 2928710040,
742
+ 3937242737,
743
+ 1804850592,
744
+ 3499020752,
745
+ 2949064160,
746
+ 2386320175,
747
+ 2390070455,
748
+ 2415321851,
749
+ 4061277028,
750
+ 2290661394,
751
+ 2416832540,
752
+ 1336762016,
753
+ 1754252060,
754
+ 3520065937,
755
+ 3014181293,
756
+ 791618072,
757
+ 3188594551,
758
+ 3933548030,
759
+ 2332172193,
760
+ 3852520463,
761
+ 3043980520,
762
+ 413987798,
763
+ 3465142937,
764
+ 3030929376,
765
+ 4245938359,
766
+ 2093235073,
767
+ 3534596313,
768
+ 375366246,
769
+ 2157278981,
770
+ 2479649556,
771
+ 555357303,
772
+ 3870105701,
773
+ 2008414854,
774
+ 3344188149,
775
+ 4221384143,
776
+ 3956125452,
777
+ 2067696032,
778
+ 3594591187,
779
+ 2921233993,
780
+ 2428461,
781
+ 544322398,
782
+ 577241275,
783
+ 1471733935,
784
+ 610547355,
785
+ 4027169054,
786
+ 1432588573,
787
+ 1507829418,
788
+ 2025931657,
789
+ 3646575487,
790
+ 545086370,
791
+ 48609733,
792
+ 2200306550,
793
+ 1653985193,
794
+ 298326376,
795
+ 1316178497,
796
+ 3007786442,
797
+ 2064951626,
798
+ 458293330,
799
+ 2589141269,
800
+ 3591329599,
801
+ 3164325604,
802
+ 727753846,
803
+ 2179363840,
804
+ 146436021,
805
+ 1461446943,
806
+ 4069977195,
807
+ 705550613,
808
+ 3059967265,
809
+ 3887724982,
810
+ 4281599278,
811
+ 3313849956,
812
+ 1404054877,
813
+ 2845806497,
814
+ 146425753,
815
+ 1854211946,
816
+ 1266315497,
817
+ 3048417604,
818
+ 3681880366,
819
+ 3289982499,
820
+ 290971e4,
821
+ 1235738493,
822
+ 2632868024,
823
+ 2414719590,
824
+ 3970600049,
825
+ 1771706367,
826
+ 1449415276,
827
+ 3266420449,
828
+ 422970021,
829
+ 1963543593,
830
+ 2690192192,
831
+ 3826793022,
832
+ 1062508698,
833
+ 1531092325,
834
+ 1804592342,
835
+ 2583117782,
836
+ 2714934279,
837
+ 4024971509,
838
+ 1294809318,
839
+ 4028980673,
840
+ 1289560198,
841
+ 2221992742,
842
+ 1669523910,
843
+ 35572830,
844
+ 157838143,
845
+ 1052438473,
846
+ 1016535060,
847
+ 1802137761,
848
+ 1753167236,
849
+ 1386275462,
850
+ 3080475397,
851
+ 2857371447,
852
+ 1040679964,
853
+ 2145300060,
854
+ 2390574316,
855
+ 1461121720,
856
+ 2956646967,
857
+ 4031777805,
858
+ 4028374788,
859
+ 33600511,
860
+ 2920084762,
861
+ 1018524850,
862
+ 629373528,
863
+ 3691585981,
864
+ 3515945977,
865
+ 2091462646,
866
+ 2486323059,
867
+ 586499841,
868
+ 988145025,
869
+ 935516892,
870
+ 3367335476,
871
+ 2599673255,
872
+ 2839830854,
873
+ 265290510,
874
+ 3972581182,
875
+ 2759138881,
876
+ 3795373465,
877
+ 1005194799,
878
+ 847297441,
879
+ 406762289,
880
+ 1314163512,
881
+ 1332590856,
882
+ 1866599683,
883
+ 4127851711,
884
+ 750260880,
885
+ 613907577,
886
+ 1450815602,
887
+ 3165620655,
888
+ 3734664991,
889
+ 3650291728,
890
+ 3012275730,
891
+ 3704569646,
892
+ 1427272223,
893
+ 778793252,
894
+ 1343938022,
895
+ 2676280711,
896
+ 2052605720,
897
+ 1946737175,
898
+ 3164576444,
899
+ 3914038668,
900
+ 3967478842,
901
+ 3682934266,
902
+ 1661551462,
903
+ 3294938066,
904
+ 4011595847,
905
+ 840292616,
906
+ 3712170807,
907
+ 616741398,
908
+ 312560963,
909
+ 711312465,
910
+ 1351876610,
911
+ 322626781,
912
+ 1910503582,
913
+ 271666773,
914
+ 2175563734,
915
+ 1594956187,
916
+ 70604529,
917
+ 3617834859,
918
+ 1007753275,
919
+ 1495573769,
920
+ 4069517037,
921
+ 2549218298,
922
+ 2663038764,
923
+ 504708206,
924
+ 2263041392,
925
+ 3941167025,
926
+ 2249088522,
927
+ 1514023603,
928
+ 1998579484,
929
+ 1312622330,
930
+ 694541497,
931
+ 2582060303,
932
+ 2151582166,
933
+ 1382467621,
934
+ 776784248,
935
+ 2618340202,
936
+ 3323268794,
937
+ 2497899128,
938
+ 2784771155,
939
+ 503983604,
940
+ 4076293799,
941
+ 907881277,
942
+ 423175695,
943
+ 432175456,
944
+ 1378068232,
945
+ 4145222326,
946
+ 3954048622,
947
+ 3938656102,
948
+ 3820766613,
949
+ 2793130115,
950
+ 2977904593,
951
+ 26017576,
952
+ 3274890735,
953
+ 3194772133,
954
+ 1700274565,
955
+ 1756076034,
956
+ 4006520079,
957
+ 3677328699,
958
+ 720338349,
959
+ 1533947780,
960
+ 354530856,
961
+ 688349552,
962
+ 3973924725,
963
+ 1637815568,
964
+ 332179504,
965
+ 3949051286,
966
+ 53804574,
967
+ 2852348879,
968
+ 3044236432,
969
+ 1282449977,
970
+ 3583942155,
971
+ 3416972820,
972
+ 4006381244,
973
+ 1617046695,
974
+ 2628476075,
975
+ 3002303598,
976
+ 1686838959,
977
+ 431878346,
978
+ 2686675385,
979
+ 1700445008,
980
+ 1080580658,
981
+ 1009431731,
982
+ 832498133,
983
+ 3223435511,
984
+ 2605976345,
985
+ 2271191193,
986
+ 2516031870,
987
+ 1648197032,
988
+ 4164389018,
989
+ 2548247927,
990
+ 300782431,
991
+ 375919233,
992
+ 238389289,
993
+ 3353747414,
994
+ 2531188641,
995
+ 2019080857,
996
+ 1475708069,
997
+ 455242339,
998
+ 2609103871,
999
+ 448939670,
1000
+ 3451063019,
1001
+ 1395535956,
1002
+ 2413381860,
1003
+ 1841049896,
1004
+ 1491858159,
1005
+ 885456874,
1006
+ 4264095073,
1007
+ 4001119347,
1008
+ 1565136089,
1009
+ 3898914787,
1010
+ 1108368660,
1011
+ 540939232,
1012
+ 1173283510,
1013
+ 2745871338,
1014
+ 3681308437,
1015
+ 4207628240,
1016
+ 3343053890,
1017
+ 4016749493,
1018
+ 1699691293,
1019
+ 1103962373,
1020
+ 3625875870,
1021
+ 2256883143,
1022
+ 3830138730,
1023
+ 1031889488,
1024
+ 3479347698,
1025
+ 1535977030,
1026
+ 4236805024,
1027
+ 3251091107,
1028
+ 2132092099,
1029
+ 1774941330,
1030
+ 1199868427,
1031
+ 1452454533,
1032
+ 157007616,
1033
+ 2904115357,
1034
+ 342012276,
1035
+ 595725824,
1036
+ 1480756522,
1037
+ 206960106,
1038
+ 497939518,
1039
+ 591360097,
1040
+ 863170706,
1041
+ 2375253569,
1042
+ 3596610801,
1043
+ 1814182875,
1044
+ 2094937945,
1045
+ 3421402208,
1046
+ 1082520231,
1047
+ 3463918190,
1048
+ 2785509508,
1049
+ 435703966,
1050
+ 3908032597,
1051
+ 1641649973,
1052
+ 2842273706,
1053
+ 3305899714,
1054
+ 1510255612,
1055
+ 2148256476,
1056
+ 2655287854,
1057
+ 3276092548,
1058
+ 4258621189,
1059
+ 236887753,
1060
+ 3681803219,
1061
+ 274041037,
1062
+ 1734335097,
1063
+ 3815195456,
1064
+ 3317970021,
1065
+ 1899903192,
1066
+ 1026095262,
1067
+ 4050517792,
1068
+ 356393447,
1069
+ 2410691914,
1070
+ 3873677099,
1071
+ 3682840055,
1072
+ 3913112168,
1073
+ 2491498743,
1074
+ 4132185628,
1075
+ 2489919796,
1076
+ 1091903735,
1077
+ 1979897079,
1078
+ 3170134830,
1079
+ 3567386728,
1080
+ 3557303409,
1081
+ 857797738,
1082
+ 1136121015,
1083
+ 1342202287,
1084
+ 507115054,
1085
+ 2535736646,
1086
+ 337727348,
1087
+ 3213592640,
1088
+ 1301675037,
1089
+ 2528481711,
1090
+ 1895095763,
1091
+ 1721773893,
1092
+ 3216771564,
1093
+ 62756741,
1094
+ 2142006736,
1095
+ 835421444,
1096
+ 2531993523,
1097
+ 1442658625,
1098
+ 3659876326,
1099
+ 2882144922,
1100
+ 676362277,
1101
+ 1392781812,
1102
+ 170690266,
1103
+ 3921047035,
1104
+ 1759253602,
1105
+ 3611846912,
1106
+ 1745797284,
1107
+ 664899054,
1108
+ 1329594018,
1109
+ 3901205900,
1110
+ 3045908486,
1111
+ 2062866102,
1112
+ 2865634940,
1113
+ 3543621612,
1114
+ 3464012697,
1115
+ 1080764994,
1116
+ 553557557,
1117
+ 3656615353,
1118
+ 3996768171,
1119
+ 991055499,
1120
+ 499776247,
1121
+ 1265440854,
1122
+ 648242737,
1123
+ 3940784050,
1124
+ 980351604,
1125
+ 3713745714,
1126
+ 1749149687,
1127
+ 3396870395,
1128
+ 4211799374,
1129
+ 3640570775,
1130
+ 1161844396,
1131
+ 3125318951,
1132
+ 1431517754,
1133
+ 545492359,
1134
+ 4268468663,
1135
+ 3499529547,
1136
+ 1437099964,
1137
+ 2702547544,
1138
+ 3433638243,
1139
+ 2581715763,
1140
+ 2787789398,
1141
+ 1060185593,
1142
+ 1593081372,
1143
+ 2418618748,
1144
+ 4260947970,
1145
+ 69676912,
1146
+ 2159744348,
1147
+ 86519011,
1148
+ 2512459080,
1149
+ 3838209314,
1150
+ 1220612927,
1151
+ 3339683548,
1152
+ 133810670,
1153
+ 1090789135,
1154
+ 1078426020,
1155
+ 1569222167,
1156
+ 845107691,
1157
+ 3583754449,
1158
+ 4072456591,
1159
+ 1091646820,
1160
+ 628848692,
1161
+ 1613405280,
1162
+ 3757631651,
1163
+ 526609435,
1164
+ 236106946,
1165
+ 48312990,
1166
+ 2942717905,
1167
+ 3402727701,
1168
+ 1797494240,
1169
+ 859738849,
1170
+ 992217954,
1171
+ 4005476642,
1172
+ 2243076622,
1173
+ 3870952857,
1174
+ 3732016268,
1175
+ 765654824,
1176
+ 3490871365,
1177
+ 2511836413,
1178
+ 1685915746,
1179
+ 3888969200,
1180
+ 1414112111,
1181
+ 2273134842,
1182
+ 3281911079,
1183
+ 4080962846,
1184
+ 172450625,
1185
+ 2569994100,
1186
+ 980381355,
1187
+ 4109958455,
1188
+ 2819808352,
1189
+ 2716589560,
1190
+ 2568741196,
1191
+ 3681446669,
1192
+ 3329971472,
1193
+ 1835478071,
1194
+ 660984891,
1195
+ 3704678404,
1196
+ 4045999559,
1197
+ 3422617507,
1198
+ 3040415634,
1199
+ 1762651403,
1200
+ 1719377915,
1201
+ 3470491036,
1202
+ 2693910283,
1203
+ 3642056355,
1204
+ 3138596744,
1205
+ 1364962596,
1206
+ 2073328063,
1207
+ 1983633131,
1208
+ 926494387,
1209
+ 3423689081,
1210
+ 2150032023,
1211
+ 4096667949,
1212
+ 1749200295,
1213
+ 3328846651,
1214
+ 309677260,
1215
+ 2016342300,
1216
+ 1779581495,
1217
+ 3079819751,
1218
+ 111262694,
1219
+ 1274766160,
1220
+ 443224088,
1221
+ 298511866,
1222
+ 1025883608,
1223
+ 3806446537,
1224
+ 1145181785,
1225
+ 168956806,
1226
+ 3641502830,
1227
+ 3584813610,
1228
+ 1689216846,
1229
+ 3666258015,
1230
+ 3200248200,
1231
+ 1692713982,
1232
+ 2646376535,
1233
+ 4042768518,
1234
+ 1618508792,
1235
+ 1610833997,
1236
+ 3523052358,
1237
+ 4130873264,
1238
+ 2001055236,
1239
+ 3610705100,
1240
+ 2202168115,
1241
+ 4028541809,
1242
+ 2961195399,
1243
+ 1006657119,
1244
+ 2006996926,
1245
+ 3186142756,
1246
+ 1430667929,
1247
+ 3210227297,
1248
+ 1314452623,
1249
+ 4074634658,
1250
+ 4101304120,
1251
+ 2273951170,
1252
+ 1399257539,
1253
+ 3367210612,
1254
+ 3027628629,
1255
+ 1190975929,
1256
+ 2062231137,
1257
+ 2333990788,
1258
+ 2221543033,
1259
+ 2438960610,
1260
+ 1181637006,
1261
+ 548689776,
1262
+ 2362791313,
1263
+ 3372408396,
1264
+ 3104550113,
1265
+ 3145860560,
1266
+ 296247880,
1267
+ 1970579870,
1268
+ 3078560182,
1269
+ 3769228297,
1270
+ 1714227617,
1271
+ 3291629107,
1272
+ 3898220290,
1273
+ 166772364,
1274
+ 1251581989,
1275
+ 493813264,
1276
+ 448347421,
1277
+ 195405023,
1278
+ 2709975567,
1279
+ 677966185,
1280
+ 3703036547,
1281
+ 1463355134,
1282
+ 2715995803,
1283
+ 1338867538,
1284
+ 1343315457,
1285
+ 2802222074,
1286
+ 2684532164,
1287
+ 233230375,
1288
+ 2599980071,
1289
+ 2000651841,
1290
+ 3277868038,
1291
+ 1638401717,
1292
+ 4028070440,
1293
+ 3237316320,
1294
+ 6314154,
1295
+ 819756386,
1296
+ 300326615,
1297
+ 590932579,
1298
+ 1405279636,
1299
+ 3267499572,
1300
+ 3150704214,
1301
+ 2428286686,
1302
+ 3959192993,
1303
+ 3461946742,
1304
+ 1862657033,
1305
+ 1266418056,
1306
+ 963775037,
1307
+ 2089974820,
1308
+ 2263052895,
1309
+ 1917689273,
1310
+ 448879540,
1311
+ 3550394620,
1312
+ 3981727096,
1313
+ 150775221,
1314
+ 3627908307,
1315
+ 1303187396,
1316
+ 508620638,
1317
+ 2975983352,
1318
+ 2726630617,
1319
+ 1817252668,
1320
+ 1876281319,
1321
+ 1457606340,
1322
+ 908771278,
1323
+ 3720792119,
1324
+ 3617206836,
1325
+ 2455994898,
1326
+ 1729034894,
1327
+ 1080033504,
1328
+ 976866871,
1329
+ 3556439503,
1330
+ 2881648439,
1331
+ 1522871579,
1332
+ 1555064734,
1333
+ 1336096578,
1334
+ 3548522304,
1335
+ 2579274686,
1336
+ 3574697629,
1337
+ 3205460757,
1338
+ 3593280638,
1339
+ 3338716283,
1340
+ 3079412587,
1341
+ 564236357,
1342
+ 2993598910,
1343
+ 1781952180,
1344
+ 1464380207,
1345
+ 3163844217,
1346
+ 3332601554,
1347
+ 1699332808,
1348
+ 1393555694,
1349
+ 1183702653,
1350
+ 3581086237,
1351
+ 1288719814,
1352
+ 691649499,
1353
+ 2847557200,
1354
+ 2895455976,
1355
+ 3193889540,
1356
+ 2717570544,
1357
+ 1781354906,
1358
+ 1676643554,
1359
+ 2592534050,
1360
+ 3230253752,
1361
+ 1126444790,
1362
+ 2770207658,
1363
+ 2633158820,
1364
+ 2210423226,
1365
+ 2615765581,
1366
+ 2414155088,
1367
+ 3127139286,
1368
+ 673620729,
1369
+ 2805611233,
1370
+ 1269405062,
1371
+ 4015350505,
1372
+ 3341807571,
1373
+ 4149409754,
1374
+ 1057255273,
1375
+ 2012875353,
1376
+ 2162469141,
1377
+ 2276492801,
1378
+ 2601117357,
1379
+ 993977747,
1380
+ 3918593370,
1381
+ 2654263191,
1382
+ 753973209,
1383
+ 36408145,
1384
+ 2530585658,
1385
+ 25011837,
1386
+ 3520020182,
1387
+ 2088578344,
1388
+ 530523599,
1389
+ 2918365339,
1390
+ 1524020338,
1391
+ 1518925132,
1392
+ 3760827505,
1393
+ 3759777254,
1394
+ 1202760957,
1395
+ 3985898139,
1396
+ 3906192525,
1397
+ 674977740,
1398
+ 4174734889,
1399
+ 2031300136,
1400
+ 2019492241,
1401
+ 3983892565,
1402
+ 4153806404,
1403
+ 3822280332,
1404
+ 352677332,
1405
+ 2297720250,
1406
+ 60907813,
1407
+ 90501309,
1408
+ 3286998549,
1409
+ 1016092578,
1410
+ 2535922412,
1411
+ 2839152426,
1412
+ 457141659,
1413
+ 509813237,
1414
+ 4120667899,
1415
+ 652014361,
1416
+ 1966332200,
1417
+ 2975202805,
1418
+ 55981186,
1419
+ 2327461051,
1420
+ 676427537,
1421
+ 3255491064,
1422
+ 2882294119,
1423
+ 3433927263,
1424
+ 1307055953,
1425
+ 942726286,
1426
+ 933058658,
1427
+ 2468411793,
1428
+ 3933900994,
1429
+ 4215176142,
1430
+ 1361170020,
1431
+ 2001714738,
1432
+ 2830558078,
1433
+ 3274259782,
1434
+ 1222529897,
1435
+ 1679025792,
1436
+ 2729314320,
1437
+ 3714953764,
1438
+ 1770335741,
1439
+ 151462246,
1440
+ 3013232138,
1441
+ 1682292957,
1442
+ 1483529935,
1443
+ 471910574,
1444
+ 1539241949,
1445
+ 458788160,
1446
+ 3436315007,
1447
+ 1807016891,
1448
+ 3718408830,
1449
+ 978976581,
1450
+ 1043663428,
1451
+ 3165965781,
1452
+ 1927990952,
1453
+ 4200891579,
1454
+ 2372276910,
1455
+ 3208408903,
1456
+ 3533431907,
1457
+ 1412390302,
1458
+ 2931980059,
1459
+ 4132332400,
1460
+ 1947078029,
1461
+ 3881505623,
1462
+ 4168226417,
1463
+ 2941484381,
1464
+ 1077988104,
1465
+ 1320477388,
1466
+ 886195818,
1467
+ 18198404,
1468
+ 3786409e3,
1469
+ 2509781533,
1470
+ 112762804,
1471
+ 3463356488,
1472
+ 1866414978,
1473
+ 891333506,
1474
+ 18488651,
1475
+ 661792760,
1476
+ 1628790961,
1477
+ 3885187036,
1478
+ 3141171499,
1479
+ 876946877,
1480
+ 2693282273,
1481
+ 1372485963,
1482
+ 791857591,
1483
+ 2686433993,
1484
+ 3759982718,
1485
+ 3167212022,
1486
+ 3472953795,
1487
+ 2716379847,
1488
+ 445679433,
1489
+ 3561995674,
1490
+ 3504004811,
1491
+ 3574258232,
1492
+ 54117162,
1493
+ 3331405415,
1494
+ 2381918588,
1495
+ 3769707343,
1496
+ 4154350007,
1497
+ 1140177722,
1498
+ 4074052095,
1499
+ 668550556,
1500
+ 3214352940,
1501
+ 367459370,
1502
+ 261225585,
1503
+ 2610173221,
1504
+ 4209349473,
1505
+ 3468074219,
1506
+ 3265815641,
1507
+ 314222801,
1508
+ 3066103646,
1509
+ 3808782860,
1510
+ 282218597,
1511
+ 3406013506,
1512
+ 3773591054,
1513
+ 379116347,
1514
+ 1285071038,
1515
+ 846784868,
1516
+ 2669647154,
1517
+ 3771962079,
1518
+ 3550491691,
1519
+ 2305946142,
1520
+ 453669953,
1521
+ 1268987020,
1522
+ 3317592352,
1523
+ 3279303384,
1524
+ 3744833421,
1525
+ 2610507566,
1526
+ 3859509063,
1527
+ 266596637,
1528
+ 3847019092,
1529
+ 517658769,
1530
+ 3462560207,
1531
+ 3443424879,
1532
+ 370717030,
1533
+ 4247526661,
1534
+ 2224018117,
1535
+ 4143653529,
1536
+ 4112773975,
1537
+ 2788324899,
1538
+ 2477274417,
1539
+ 1456262402,
1540
+ 2901442914,
1541
+ 1517677493,
1542
+ 1846949527,
1543
+ 2295493580,
1544
+ 3734397586,
1545
+ 2176403920,
1546
+ 1280348187,
1547
+ 1908823572,
1548
+ 3871786941,
1549
+ 846861322,
1550
+ 1172426758,
1551
+ 3287448474,
1552
+ 3383383037,
1553
+ 1655181056,
1554
+ 3139813346,
1555
+ 901632758,
1556
+ 1897031941,
1557
+ 2986607138,
1558
+ 3066810236,
1559
+ 3447102507,
1560
+ 1393639104,
1561
+ 373351379,
1562
+ 950779232,
1563
+ 625454576,
1564
+ 3124240540,
1565
+ 4148612726,
1566
+ 2007998917,
1567
+ 544563296,
1568
+ 2244738638,
1569
+ 2330496472,
1570
+ 2058025392,
1571
+ 1291430526,
1572
+ 424198748,
1573
+ 50039436,
1574
+ 29584100,
1575
+ 3605783033,
1576
+ 2429876329,
1577
+ 2791104160,
1578
+ 1057563949,
1579
+ 3255363231,
1580
+ 3075367218,
1581
+ 3463963227,
1582
+ 1469046755,
1583
+ 985887462
1584
+ ];
1585
+ var C_ORIG = [
1586
+ 1332899944,
1587
+ 1700884034,
1588
+ 1701343084,
1589
+ 1684370003,
1590
+ 1668446532,
1591
+ 1869963892
1592
+ ];
1593
+ function _encipher(lr, off, P, S) {
1594
+ var n, l = lr[off], r = lr[off + 1];
1595
+ l ^= P[0];
1596
+ n = S[l >>> 24];
1597
+ n += S[256 | l >> 16 & 255];
1598
+ n ^= S[512 | l >> 8 & 255];
1599
+ n += S[768 | l & 255];
1600
+ r ^= n ^ P[1];
1601
+ n = S[r >>> 24];
1602
+ n += S[256 | r >> 16 & 255];
1603
+ n ^= S[512 | r >> 8 & 255];
1604
+ n += S[768 | r & 255];
1605
+ l ^= n ^ P[2];
1606
+ n = S[l >>> 24];
1607
+ n += S[256 | l >> 16 & 255];
1608
+ n ^= S[512 | l >> 8 & 255];
1609
+ n += S[768 | l & 255];
1610
+ r ^= n ^ P[3];
1611
+ n = S[r >>> 24];
1612
+ n += S[256 | r >> 16 & 255];
1613
+ n ^= S[512 | r >> 8 & 255];
1614
+ n += S[768 | r & 255];
1615
+ l ^= n ^ P[4];
1616
+ n = S[l >>> 24];
1617
+ n += S[256 | l >> 16 & 255];
1618
+ n ^= S[512 | l >> 8 & 255];
1619
+ n += S[768 | l & 255];
1620
+ r ^= n ^ P[5];
1621
+ n = S[r >>> 24];
1622
+ n += S[256 | r >> 16 & 255];
1623
+ n ^= S[512 | r >> 8 & 255];
1624
+ n += S[768 | r & 255];
1625
+ l ^= n ^ P[6];
1626
+ n = S[l >>> 24];
1627
+ n += S[256 | l >> 16 & 255];
1628
+ n ^= S[512 | l >> 8 & 255];
1629
+ n += S[768 | l & 255];
1630
+ r ^= n ^ P[7];
1631
+ n = S[r >>> 24];
1632
+ n += S[256 | r >> 16 & 255];
1633
+ n ^= S[512 | r >> 8 & 255];
1634
+ n += S[768 | r & 255];
1635
+ l ^= n ^ P[8];
1636
+ n = S[l >>> 24];
1637
+ n += S[256 | l >> 16 & 255];
1638
+ n ^= S[512 | l >> 8 & 255];
1639
+ n += S[768 | l & 255];
1640
+ r ^= n ^ P[9];
1641
+ n = S[r >>> 24];
1642
+ n += S[256 | r >> 16 & 255];
1643
+ n ^= S[512 | r >> 8 & 255];
1644
+ n += S[768 | r & 255];
1645
+ l ^= n ^ P[10];
1646
+ n = S[l >>> 24];
1647
+ n += S[256 | l >> 16 & 255];
1648
+ n ^= S[512 | l >> 8 & 255];
1649
+ n += S[768 | l & 255];
1650
+ r ^= n ^ P[11];
1651
+ n = S[r >>> 24];
1652
+ n += S[256 | r >> 16 & 255];
1653
+ n ^= S[512 | r >> 8 & 255];
1654
+ n += S[768 | r & 255];
1655
+ l ^= n ^ P[12];
1656
+ n = S[l >>> 24];
1657
+ n += S[256 | l >> 16 & 255];
1658
+ n ^= S[512 | l >> 8 & 255];
1659
+ n += S[768 | l & 255];
1660
+ r ^= n ^ P[13];
1661
+ n = S[r >>> 24];
1662
+ n += S[256 | r >> 16 & 255];
1663
+ n ^= S[512 | r >> 8 & 255];
1664
+ n += S[768 | r & 255];
1665
+ l ^= n ^ P[14];
1666
+ n = S[l >>> 24];
1667
+ n += S[256 | l >> 16 & 255];
1668
+ n ^= S[512 | l >> 8 & 255];
1669
+ n += S[768 | l & 255];
1670
+ r ^= n ^ P[15];
1671
+ n = S[r >>> 24];
1672
+ n += S[256 | r >> 16 & 255];
1673
+ n ^= S[512 | r >> 8 & 255];
1674
+ n += S[768 | r & 255];
1675
+ l ^= n ^ P[16];
1676
+ lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
1677
+ lr[off + 1] = l;
1678
+ return lr;
1679
+ }
1680
+ function _streamtoword(data, offp) {
1681
+ for (var i = 0, word = 0; i < 4; ++i)
1682
+ word = word << 8 | data[offp] & 255, offp = (offp + 1) % data.length;
1683
+ return { key: word, offp };
1684
+ }
1685
+ function _key(key, P, S) {
1686
+ var offset = 0, lr = [0, 0], plen = P.length, slen = S.length, sw;
1687
+ for (var i = 0; i < plen; i++)
1688
+ sw = _streamtoword(key, offset), offset = sw.offp, P[i] = P[i] ^ sw.key;
1689
+ for (i = 0; i < plen; i += 2)
1690
+ lr = _encipher(lr, 0, P, S), P[i] = lr[0], P[i + 1] = lr[1];
1691
+ for (i = 0; i < slen; i += 2)
1692
+ lr = _encipher(lr, 0, P, S), S[i] = lr[0], S[i + 1] = lr[1];
1693
+ }
1694
+ function _ekskey(data, key, P, S) {
1695
+ var offp = 0, lr = [0, 0], plen = P.length, slen = S.length, sw;
1696
+ for (var i = 0; i < plen; i++)
1697
+ sw = _streamtoword(key, offp), offp = sw.offp, P[i] = P[i] ^ sw.key;
1698
+ offp = 0;
1699
+ for (i = 0; i < plen; i += 2)
1700
+ sw = _streamtoword(data, offp), offp = sw.offp, lr[0] ^= sw.key, sw = _streamtoword(data, offp), offp = sw.offp, lr[1] ^= sw.key, lr = _encipher(lr, 0, P, S), P[i] = lr[0], P[i + 1] = lr[1];
1701
+ for (i = 0; i < slen; i += 2)
1702
+ sw = _streamtoword(data, offp), offp = sw.offp, lr[0] ^= sw.key, sw = _streamtoword(data, offp), offp = sw.offp, lr[1] ^= sw.key, lr = _encipher(lr, 0, P, S), S[i] = lr[0], S[i + 1] = lr[1];
1703
+ }
1704
+ function _crypt(b, salt, rounds, callback, progressCallback) {
1705
+ var cdata = C_ORIG.slice(), clen = cdata.length, err;
1706
+ if (rounds < 4 || rounds > 31) {
1707
+ err = Error("Illegal number of rounds (4-31): " + rounds);
1708
+ if (callback) {
1709
+ nextTick(callback.bind(this, err));
1710
+ return;
1711
+ } else
1712
+ throw err;
1713
+ }
1714
+ if (salt.length !== BCRYPT_SALT_LEN) {
1715
+ err = Error("Illegal salt length: " + salt.length + " != " + BCRYPT_SALT_LEN);
1716
+ if (callback) {
1717
+ nextTick(callback.bind(this, err));
1718
+ return;
1719
+ } else
1720
+ throw err;
1721
+ }
1722
+ rounds = 1 << rounds >>> 0;
1723
+ var P, S, i = 0, j;
1724
+ if (Int32Array) {
1725
+ P = new Int32Array(P_ORIG);
1726
+ S = new Int32Array(S_ORIG);
1727
+ } else {
1728
+ P = P_ORIG.slice();
1729
+ S = S_ORIG.slice();
1730
+ }
1731
+ _ekskey(salt, b, P, S);
1732
+ function next() {
1733
+ if (progressCallback)
1734
+ progressCallback(i / rounds);
1735
+ if (i < rounds) {
1736
+ var start = Date.now();
1737
+ for (; i < rounds; ) {
1738
+ i = i + 1;
1739
+ _key(b, P, S);
1740
+ _key(salt, P, S);
1741
+ if (Date.now() - start > MAX_EXECUTION_TIME)
1742
+ break;
1743
+ }
1744
+ } else {
1745
+ for (i = 0; i < 64; i++)
1746
+ for (j = 0; j < clen >> 1; j++)
1747
+ _encipher(cdata, j << 1, P, S);
1748
+ var ret = [];
1749
+ for (i = 0; i < clen; i++)
1750
+ ret.push((cdata[i] >> 24 & 255) >>> 0), ret.push((cdata[i] >> 16 & 255) >>> 0), ret.push((cdata[i] >> 8 & 255) >>> 0), ret.push((cdata[i] & 255) >>> 0);
1751
+ if (callback) {
1752
+ callback(null, ret);
1753
+ return;
1754
+ } else
1755
+ return ret;
1756
+ }
1757
+ if (callback)
1758
+ nextTick(next);
1759
+ }
1760
+ if (typeof callback !== "undefined") {
1761
+ next();
1762
+ } else {
1763
+ var res;
1764
+ while (true)
1765
+ if (typeof (res = next()) !== "undefined")
1766
+ return res || [];
1767
+ }
1768
+ }
1769
+ function _hash(s, salt, callback, progressCallback) {
1770
+ var err;
1771
+ if (typeof s !== "string" || typeof salt !== "string") {
1772
+ err = Error("Invalid string / salt: Not a string");
1773
+ if (callback) {
1774
+ nextTick(callback.bind(this, err));
1775
+ return;
1776
+ } else
1777
+ throw err;
1778
+ }
1779
+ var minor, offset;
1780
+ if (salt.charAt(0) !== "$" || salt.charAt(1) !== "2") {
1781
+ err = Error("Invalid salt version: " + salt.substring(0, 2));
1782
+ if (callback) {
1783
+ nextTick(callback.bind(this, err));
1784
+ return;
1785
+ } else
1786
+ throw err;
1787
+ }
1788
+ if (salt.charAt(2) === "$")
1789
+ minor = String.fromCharCode(0), offset = 3;
1790
+ else {
1791
+ minor = salt.charAt(2);
1792
+ if (minor !== "a" && minor !== "b" && minor !== "y" || salt.charAt(3) !== "$") {
1793
+ err = Error("Invalid salt revision: " + salt.substring(2, 4));
1794
+ if (callback) {
1795
+ nextTick(callback.bind(this, err));
1796
+ return;
1797
+ } else
1798
+ throw err;
1799
+ }
1800
+ offset = 4;
1801
+ }
1802
+ if (salt.charAt(offset + 2) > "$") {
1803
+ err = Error("Missing salt rounds");
1804
+ if (callback) {
1805
+ nextTick(callback.bind(this, err));
1806
+ return;
1807
+ } else
1808
+ throw err;
1809
+ }
1810
+ var r1 = parseInt(salt.substring(offset, offset + 1), 10) * 10, r2 = parseInt(salt.substring(offset + 1, offset + 2), 10), rounds = r1 + r2, real_salt = salt.substring(offset + 3, offset + 25);
1811
+ s += minor >= "a" ? "\0" : "";
1812
+ var passwordb = stringToBytes(s), saltb = base64_decode(real_salt, BCRYPT_SALT_LEN);
1813
+ function finish(bytes) {
1814
+ var res = [];
1815
+ res.push("$2");
1816
+ if (minor >= "a")
1817
+ res.push(minor);
1818
+ res.push("$");
1819
+ if (rounds < 10)
1820
+ res.push("0");
1821
+ res.push(rounds.toString());
1822
+ res.push("$");
1823
+ res.push(base64_encode(saltb, saltb.length));
1824
+ res.push(base64_encode(bytes, C_ORIG.length * 4 - 1));
1825
+ return res.join("");
1826
+ }
1827
+ if (typeof callback == "undefined")
1828
+ return finish(_crypt(passwordb, saltb, rounds));
1829
+ else {
1830
+ _crypt(passwordb, saltb, rounds, function(err2, bytes) {
1831
+ if (err2)
1832
+ callback(err2, null);
1833
+ else
1834
+ callback(null, finish(bytes));
1835
+ }, progressCallback);
1836
+ }
1837
+ }
1838
+ bcrypt2.encodeBase64 = base64_encode;
1839
+ bcrypt2.decodeBase64 = base64_decode;
1840
+ return bcrypt2;
1841
+ });
1842
+ }
1843
+ });
1844
+
1845
+ // ../../node_modules/.pnpm/bcryptjs@2.4.3/node_modules/bcryptjs/index.js
1846
+ var require_bcryptjs = __commonJS({
1847
+ "../../node_modules/.pnpm/bcryptjs@2.4.3/node_modules/bcryptjs/index.js"(exports, module) {
1848
+ module.exports = require_bcrypt();
1849
+ }
1850
+ });
1851
+
1852
+ // src/index.ts
1853
+ import { homedir as homedir5 } from "node:os";
1854
+ import { Command } from "commander";
1855
+
1856
+ // ../shared/src/manifest.ts
1857
+ import { z } from "zod";
1858
+ var slugSchema = z.string().regex(
1859
+ /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/,
1860
+ "name must be lowercase letters/digits/hyphens, 3-64 chars, start+end alphanumeric"
1861
+ );
1862
+ var semverSchema = z.string().regex(
1863
+ /^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$/,
1864
+ "version must be semver (e.g. 0.1.0 or 1.0.0-beta.1)"
1865
+ );
1866
+ var skillTargetSchema = z.enum([
1867
+ "claude",
1868
+ "codex",
1869
+ "cursor",
1870
+ "gemini",
1871
+ "opencode",
1872
+ "custom"
1873
+ ]);
1874
+ var authorSchema = z.object({
1875
+ name: z.string().min(1),
1876
+ email: z.string().email().optional()
1877
+ });
1878
+ var declaredScriptSchema = z.object({
1879
+ path: z.string(),
1880
+ description: z.string().max(280)
1881
+ });
1882
+ var manifestSchema = z.object({
1883
+ schema_version: z.literal("0.1"),
1884
+ name: slugSchema,
1885
+ title: z.string().min(1).max(120),
1886
+ description: z.string().min(1).max(500),
1887
+ version: semverSchema,
1888
+ entrypoint: z.string().default("SKILL.md"),
1889
+ targets: z.array(skillTargetSchema).default(["codex"]),
1890
+ tags: z.array(z.string().min(1).max(32)).max(10).optional(),
1891
+ authors: z.array(authorSchema).optional(),
1892
+ license: z.string().optional(),
1893
+ homepage: z.string().url().optional(),
1894
+ repository: z.string().url().optional(),
1895
+ source_url: z.string().url().optional(),
1896
+ declared_scripts: z.array(declaredScriptSchema).optional(),
1897
+ min_floom_version: semverSchema.optional()
1898
+ }).strict();
1899
+
1900
+ // ../shared/src/errors.ts
1901
+ var FloomError = class extends Error {
1902
+ code;
1903
+ details;
1904
+ constructor(code, message, details) {
1905
+ super(message);
1906
+ this.name = "FloomError";
1907
+ this.code = code;
1908
+ this.details = details;
1909
+ }
1910
+ };
1911
+
1912
+ // ../shared/src/api-keys.ts
1913
+ var import_bcryptjs = __toESM(require_bcryptjs(), 1);
1914
+ import { randomBytes, scrypt as scryptCb, timingSafeEqual, createHash } from "node:crypto";
1915
+ import { promisify } from "node:util";
1916
+ var scrypt = promisify(scryptCb);
1917
+
1918
+ // ../shared/src/install-targets.ts
1919
+ import { homedir } from "node:os";
1920
+ import { stat } from "node:fs/promises";
1921
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
1922
+ var INSTALL_TARGETS = [
1923
+ "claude",
1924
+ "codex",
1925
+ "cursor",
1926
+ "gemini",
1927
+ "opencode"
1928
+ ];
1929
+ function assertInstallTarget(value) {
1930
+ if (INSTALL_TARGETS.includes(value)) {
1931
+ return value;
1932
+ }
1933
+ throw new Error(
1934
+ `Unknown target "${value}". Use one of: ${INSTALL_TARGETS.join(", ")}.`
1935
+ );
1936
+ }
1937
+ var COMPATIBLE_AGENTS = {
1938
+ claude: ["Claude Code"],
1939
+ codex: ["Codex CLI"],
1940
+ cursor: ["Cursor"],
1941
+ gemini: ["Gemini CLI"],
1942
+ opencode: ["OpenCode"]
1943
+ };
1944
+ var TARGET_ENV_DIRS = {
1945
+ claude: ["FLOOM_SKILLS_DIR", "CLAUDE_SKILLS_DIR"],
1946
+ codex: ["FLOOM_SKILLS_DIR", "CODEX_SKILLS_DIR"],
1947
+ cursor: ["FLOOM_SKILLS_DIR", "CURSOR_SKILLS_DIR"],
1948
+ gemini: ["FLOOM_SKILLS_DIR", "GEMINI_SKILLS_DIR"],
1949
+ opencode: ["FLOOM_SKILLS_DIR", "OPENCODE_SKILLS_DIR"]
1950
+ };
1951
+ var TARGET_PARENT_DIRS = {
1952
+ claude: ".claude",
1953
+ codex: ".codex",
1954
+ cursor: ".cursor",
1955
+ gemini: ".gemini",
1956
+ opencode: ".opencode"
1957
+ };
1958
+ var SYSTEM_ROOTS = /* @__PURE__ */ new Set(["/", "/etc", "/usr", "/bin", "/sbin", "/var", "/sys", "/proc"]);
1959
+ function isInsideDir(path, parent) {
1960
+ const rel = relative(parent, path);
1961
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
1962
+ }
1963
+ function validateEnvDirPath(envVar, rawValue, homeDir) {
1964
+ const resolved = resolve(rawValue);
1965
+ const resolvedHome = resolve(homeDir);
1966
+ if (SYSTEM_ROOTS.has(resolved) || !isInsideDir(resolved, resolvedHome)) {
1967
+ throw new Error(
1968
+ `${envVar} points outside your home directory (${resolved}). Set it to a path under ${resolvedHome} or use --target without an env override.`
1969
+ );
1970
+ }
1971
+ return resolved;
1972
+ }
1973
+ function envDirForTarget(target, homeDir) {
1974
+ for (const key of TARGET_ENV_DIRS[target]) {
1975
+ const value = process.env[key]?.trim();
1976
+ if (value) return validateEnvDirPath(key, value, homeDir);
1977
+ }
1978
+ return null;
1979
+ }
1980
+ function presetDir(target, opts) {
1981
+ const cwd = opts.cwd ?? process.cwd();
1982
+ const root = opts.global ? opts.homeDir ?? homedir() : cwd;
1983
+ switch (target) {
1984
+ case "claude":
1985
+ return join(root, ".claude", "skills");
1986
+ case "gemini":
1987
+ return join(root, ".gemini", "skills");
1988
+ case "codex":
1989
+ return join(root, ".codex", "skills");
1990
+ case "cursor":
1991
+ return join(root, ".cursor", "skills");
1992
+ case "opencode":
1993
+ return join(root, ".opencode", "skills");
1994
+ }
1995
+ }
1996
+ function resolveInstallDir(args) {
1997
+ const target = args.target ?? "codex";
1998
+ const homeDir = args.homeDir ?? homedir();
1999
+ if (args.to) {
2000
+ return {
2001
+ target,
2002
+ dir: args.to,
2003
+ origin: "explicit",
2004
+ compatibleAgents: COMPATIBLE_AGENTS[target]
2005
+ };
2006
+ }
2007
+ const targetEnvDir = envDirForTarget(target, homeDir);
2008
+ if (targetEnvDir) {
2009
+ return {
2010
+ target,
2011
+ dir: targetEnvDir,
2012
+ origin: "env",
2013
+ compatibleAgents: COMPATIBLE_AGENTS[target]
2014
+ };
2015
+ }
2016
+ return {
2017
+ target,
2018
+ dir: presetDir(target, { global: args.global, cwd: args.cwd, homeDir }),
2019
+ origin: "preset",
2020
+ compatibleAgents: COMPATIBLE_AGENTS[target]
2021
+ };
2022
+ }
2023
+ async function detectInstalledTargets(opts = {}) {
2024
+ const homeDir = opts.homeDir ?? homedir();
2025
+ const detected = [];
2026
+ for (const target of INSTALL_TARGETS) {
2027
+ const parentDir = join(homeDir, TARGET_PARENT_DIRS[target]);
2028
+ try {
2029
+ const parentStat = await stat(parentDir);
2030
+ if (parentStat.isDirectory()) detected.push(target);
2031
+ } catch {
2032
+ }
2033
+ }
2034
+ return detected;
2035
+ }
2036
+
2037
+ // ../shared/src/security-scan.ts
2038
+ import { readFile } from "node:fs/promises";
2039
+
2040
+ // ../shared/src/skill-package.ts
2041
+ import { readFile as readFile2, readdir, stat as stat2, lstat, mkdir } from "node:fs/promises";
2042
+ import { join as join2, relative as relative2, sep as sep2, posix } from "node:path";
2043
+ import { createHash as createHash2 } from "node:crypto";
2044
+ var LIMITS = {
2045
+ maxBundleBytes: 10 * 1024 * 1024,
2046
+ // 10 MB compressed
2047
+ maxUncompressedBytes: 50 * 1024 * 1024,
2048
+ maxFiles: 100,
2049
+ maxFileBytes: 2 * 1024 * 1024,
2050
+ // 2 MB per file
2051
+ maxPathLength: 200
2052
+ };
2053
+ var DEFAULT_IGNORE = [
2054
+ ".git",
2055
+ "node_modules",
2056
+ "dist",
2057
+ "build",
2058
+ ".next",
2059
+ ".cache",
2060
+ ".DS_Store",
2061
+ ".env",
2062
+ ".env.local",
2063
+ ".env.development",
2064
+ ".env.production",
2065
+ "*.pem",
2066
+ "*.key",
2067
+ "*.p12",
2068
+ "*.sqlite",
2069
+ "*.db"
2070
+ ];
2071
+ function matchesGlob(name, pat) {
2072
+ if (pat.includes("*")) {
2073
+ const re = new RegExp("^" + pat.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$");
2074
+ return re.test(name);
2075
+ }
2076
+ return name === pat;
2077
+ }
2078
+ function isIgnored(relPath, patterns) {
2079
+ const parts = relPath.split(posix.sep);
2080
+ for (const pat of patterns) {
2081
+ const cleanPat = pat.replace(/^\/|\/$/g, "");
2082
+ if (parts.some((p) => matchesGlob(p, cleanPat))) return true;
2083
+ }
2084
+ return false;
2085
+ }
2086
+ async function loadFloomIgnore(dir) {
2087
+ try {
2088
+ const raw = await readFile2(join2(dir, ".floomignore"), "utf8");
2089
+ return raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
2090
+ } catch {
2091
+ return [];
2092
+ }
2093
+ }
2094
+ async function walk(rootDir, ignore, acc, current) {
2095
+ const entries = await readdir(current, { withFileTypes: true });
2096
+ for (const entry of entries) {
2097
+ const abs = join2(current, entry.name);
2098
+ const rel = relative2(rootDir, abs).split(sep2).join(posix.sep);
2099
+ if (entry.isSymbolicLink()) {
2100
+ throw new Error(`Symlinks are not allowed: ${rel}`);
2101
+ }
2102
+ if (isIgnored(rel, ignore)) continue;
2103
+ if (entry.isDirectory()) {
2104
+ await walk(rootDir, ignore, acc, abs);
2105
+ continue;
2106
+ }
2107
+ if (!entry.isFile()) continue;
2108
+ const ls = await lstat(abs);
2109
+ if (ls.isSymbolicLink()) throw new Error(`Symlinks are not allowed: ${rel}`);
2110
+ if (rel.length > LIMITS.maxPathLength) {
2111
+ throw new Error(`Path too long (>${LIMITS.maxPathLength} chars): ${rel}`);
2112
+ }
2113
+ if (rel.includes("..")) {
2114
+ throw new Error(`Path traversal detected: ${rel}`);
2115
+ }
2116
+ const buf = await readFile2(abs);
2117
+ if (buf.length > LIMITS.maxFileBytes) {
2118
+ throw new Error(`File too large (${buf.length} > ${LIMITS.maxFileBytes}): ${rel}`);
2119
+ }
2120
+ const sha256 = createHash2("sha256").update(buf).digest("hex");
2121
+ acc.push({ relPath: rel, absPath: abs, size: buf.length, sha256 });
2122
+ }
2123
+ }
2124
+ async function collectBundle(rootDir) {
2125
+ const st = await stat2(rootDir);
2126
+ if (!st.isDirectory()) throw new Error(`Not a directory: ${rootDir}`);
2127
+ const ignore = [...DEFAULT_IGNORE, ...await loadFloomIgnore(rootDir)];
2128
+ const files = [];
2129
+ await walk(rootDir, ignore, files, rootDir);
2130
+ files.sort((a, b) => a.relPath.localeCompare(b.relPath));
2131
+ if (files.length === 0) throw new Error("No files to bundle (after applying ignores)");
2132
+ if (files.length > LIMITS.maxFiles) {
2133
+ throw new Error(`Too many files (${files.length} > ${LIMITS.maxFiles})`);
2134
+ }
2135
+ const total = files.reduce((n, f) => n + f.size, 0);
2136
+ if (total > LIMITS.maxUncompressedBytes) {
2137
+ throw new Error(`Bundle too large uncompressed (${total} > ${LIMITS.maxUncompressedBytes})`);
2138
+ }
2139
+ const hasScripts = files.some((f) => f.relPath.startsWith("scripts/"));
2140
+ return { rootDir, files, totalUncompressedBytes: total, hasScripts };
2141
+ }
2142
+
2143
+ // ../shared/src/contract/index.ts
2144
+ import { z as z2 } from "zod";
2145
+ var CONTRACT_ERROR_CODES = [
2146
+ "UNAUTHENTICATED",
2147
+ "FORBIDDEN",
2148
+ "NOT_FOUND",
2149
+ "VALIDATION_FAILED",
2150
+ "CONFLICT",
2151
+ "RATE_LIMITED",
2152
+ "INTERNAL",
2153
+ "WORKSPACE_REQUIRED",
2154
+ "WORKSPACE_ALREADY_EXISTS",
2155
+ "INVITE_EMAIL_MISMATCH",
2156
+ "INVITE_EXPIRED",
2157
+ "INVITE_REVOKED",
2158
+ "INVITE_ALREADY_ACCEPTED",
2159
+ "DEVICE_DENIED",
2160
+ "DEVICE_EXPIRED",
2161
+ "DEVICE_PENDING",
2162
+ "UNSUPPORTED_TARGET",
2163
+ "DIRTY_LOCAL_STATE",
2164
+ "FILE_TOO_LARGE",
2165
+ "BUNDLE_TOO_LARGE",
2166
+ "UNSAFE_FILE_PATH",
2167
+ "SKILL_MARKDOWN_REQUIRED"
2168
+ ];
2169
+ var ContractErrorCodeSchema = z2.enum(CONTRACT_ERROR_CODES);
2170
+ var RequestIdSchema = z2.string().min(1);
2171
+ var IsoDateTimeSchema = z2.string().datetime({ offset: true });
2172
+ var IdSchema = z2.string().uuid();
2173
+ var SlugSchema = z2.string().min(1).max(80).regex(/^(?=.{1,80}$)[a-z0-9]+(?:-[a-z0-9]+)*$/);
2174
+ var EmailSchema = z2.string().email().max(320);
2175
+ var Sha256Schema = z2.string().regex(/^[a-f0-9]{64}$/);
2176
+ var VersionSeqSchema = z2.number().int().positive();
2177
+ var UrlSchema = z2.string().url();
2178
+ var ErrorDetailSchema = z2.object({
2179
+ code: ContractErrorCodeSchema,
2180
+ message: z2.string().min(1),
2181
+ request_id: RequestIdSchema
2182
+ }).catchall(z2.unknown());
2183
+ var ErrorEnvelopeSchema = z2.object({
2184
+ error: ErrorDetailSchema
2185
+ }).strict();
2186
+ var UserSchema = z2.object({
2187
+ id: IdSchema,
2188
+ email: EmailSchema,
2189
+ name: z2.string().min(1),
2190
+ avatar_url: UrlSchema.nullable()
2191
+ }).strict();
2192
+ var WorkspaceSchema = z2.object({
2193
+ id: IdSchema,
2194
+ slug: SlugSchema,
2195
+ name: z2.string().min(1).max(120)
2196
+ }).strict();
2197
+ var MeResponseSchema = z2.object({
2198
+ user: UserSchema,
2199
+ workspace: WorkspaceSchema.nullable()
2200
+ }).strict();
2201
+ var WorkspaceCreateRequestSchema = z2.object({
2202
+ name: z2.string().trim().min(1).max(120)
2203
+ }).strict();
2204
+ var WorkspaceCreateResponseSchema = z2.object({
2205
+ workspace: WorkspaceSchema
2206
+ }).strict();
2207
+ var MemberSchema = z2.object({
2208
+ name: z2.string().min(1),
2209
+ email: EmailSchema,
2210
+ joined_at: IsoDateTimeSchema
2211
+ }).strict();
2212
+ var MembersListResponseSchema = z2.object({
2213
+ workspace: WorkspaceSchema,
2214
+ members: z2.array(MemberSchema),
2215
+ total: z2.number().int().nonnegative()
2216
+ }).strict();
2217
+ var InviteStatusSchema = z2.enum([
2218
+ "pending",
2219
+ "accepted",
2220
+ "revoked",
2221
+ "expired"
2222
+ ]);
2223
+ var InviteCreateRequestSchema = z2.object({
2224
+ email: EmailSchema
2225
+ }).strict();
2226
+ var InviteSchema = z2.object({
2227
+ email: EmailSchema,
2228
+ status: InviteStatusSchema,
2229
+ expires_at: IsoDateTimeSchema,
2230
+ created_at: IsoDateTimeSchema
2231
+ }).strict();
2232
+ var InviteCreateResponseSchema = z2.object({
2233
+ invite: InviteSchema,
2234
+ accept_url: UrlSchema
2235
+ }).strict();
2236
+ var InvitesListResponseSchema = z2.object({
2237
+ invites: z2.array(InviteSchema),
2238
+ total: z2.number().int().nonnegative()
2239
+ }).strict();
2240
+ var InviteAcceptResponseSchema = z2.object({
2241
+ invite: InviteSchema.extend({
2242
+ status: z2.literal("accepted"),
2243
+ accepted_at: IsoDateTimeSchema
2244
+ }),
2245
+ workspace: WorkspaceSchema
2246
+ }).strict();
2247
+ var TargetSchema = z2.enum([
2248
+ "claude",
2249
+ "codex",
2250
+ "cursor",
2251
+ "gemini",
2252
+ "opencode"
2253
+ ]);
2254
+ var VersionRefSchema = z2.object({
2255
+ version_seq: VersionSeqSchema,
2256
+ display: z2.string().regex(/^v[1-9][0-9]*$/)
2257
+ }).strict().superRefine((value, context) => {
2258
+ if (value.display !== `v${value.version_seq}`) {
2259
+ context.addIssue({
2260
+ code: z2.ZodIssueCode.custom,
2261
+ message: "display must equal v${version_seq}",
2262
+ path: ["display"]
2263
+ });
2264
+ }
2265
+ });
2266
+ var PersonNameSchema = z2.object({
2267
+ name: z2.string().min(1)
2268
+ }).strict();
2269
+ var PersonNameEmailSchema = PersonNameSchema.extend({
2270
+ email: EmailSchema
2271
+ }).strict();
2272
+ var FileMetadataSchema = z2.object({
2273
+ path: z2.string().min(1).max(512),
2274
+ size_bytes: z2.number().int().nonnegative(),
2275
+ size: z2.number().int().nonnegative(),
2276
+ sha256: Sha256Schema,
2277
+ mime: z2.string().min(1)
2278
+ }).strict();
2279
+ var FileTreeEntrySchema = z2.object({
2280
+ path: z2.string().min(1).max(512),
2281
+ kind: z2.enum(["directory", "file"]),
2282
+ parent_path: z2.string().max(512).nullable(),
2283
+ depth: z2.number().int().nonnegative(),
2284
+ size_bytes: z2.number().int().nonnegative().nullable()
2285
+ }).strict();
2286
+ var FileDownloadSchema = z2.object({
2287
+ available: z2.boolean(),
2288
+ url: UrlSchema.nullable(),
2289
+ filename: z2.string().min(1).nullable(),
2290
+ expires_at: IsoDateTimeSchema.nullable()
2291
+ }).strict();
2292
+ var FileCopySchema = z2.object({
2293
+ available: z2.boolean(),
2294
+ source: z2.enum(["content_text", "safe_extraction", "none"])
2295
+ }).strict();
2296
+ var FileActionSchema = z2.object({
2297
+ copy: FileCopySchema,
2298
+ download: FileDownloadSchema
2299
+ }).strict();
2300
+ var TextFileContentSchema = FileMetadataSchema.extend({
2301
+ mode: z2.literal("text"),
2302
+ content_text: z2.string(),
2303
+ safe_extraction: z2.null(),
2304
+ binary: z2.null(),
2305
+ actions: FileActionSchema
2306
+ }).strict();
2307
+ var SafeExtractionSchema = z2.object({
2308
+ status: z2.enum(["available", "truncated", "unsupported"]),
2309
+ text_preview: z2.string().nullable(),
2310
+ reason: z2.string().min(1).nullable()
2311
+ }).strict();
2312
+ var BinaryMetadataSchema = z2.object({
2313
+ filename: z2.string().min(1),
2314
+ size_bytes: z2.number().int().nonnegative(),
2315
+ sha256: Sha256Schema,
2316
+ mime: z2.string().min(1),
2317
+ download_url: UrlSchema.nullable()
2318
+ }).strict();
2319
+ var NonTextFileContentSchema = FileMetadataSchema.extend({
2320
+ mode: z2.literal("non_text"),
2321
+ content_text: z2.null(),
2322
+ safe_extraction: SafeExtractionSchema,
2323
+ binary: BinaryMetadataSchema,
2324
+ actions: FileActionSchema
2325
+ }).strict();
2326
+ var FileContentSchema = z2.discriminatedUnion("mode", [
2327
+ TextFileContentSchema,
2328
+ NonTextFileContentSchema
2329
+ ]);
2330
+ var SkillsListItemSchema = z2.object({
2331
+ slug: SlugSchema,
2332
+ title: z2.string().min(1),
2333
+ description: z2.string(),
2334
+ icon: z2.string().min(1).nullable().optional(),
2335
+ latest_version: VersionRefSchema,
2336
+ updated_at: IsoDateTimeSchema,
2337
+ updated_by: PersonNameSchema
2338
+ }).strict();
2339
+ var SkillsListResponseSchema = z2.object({
2340
+ skills: z2.array(SkillsListItemSchema),
2341
+ total: z2.number().int().nonnegative(),
2342
+ order: z2.literal("updated_desc"),
2343
+ search: z2.object({
2344
+ mode: z2.literal("client_side_only"),
2345
+ server_search: z2.literal(false),
2346
+ query_param: z2.null()
2347
+ }).strict(),
2348
+ pagination: z2.null()
2349
+ }).strict();
2350
+ var VersionHistoryItemSchema = z2.object({
2351
+ version_seq: VersionSeqSchema,
2352
+ display: z2.string().regex(/^v[1-9][0-9]*$/),
2353
+ created_at: IsoDateTimeSchema,
2354
+ created_by: PersonNameSchema,
2355
+ changelog: z2.string().nullable().optional()
2356
+ }).strict().superRefine((value, context) => {
2357
+ if (value.display !== `v${value.version_seq}`) {
2358
+ context.addIssue({
2359
+ code: z2.ZodIssueCode.custom,
2360
+ message: "display must equal v${version_seq}",
2361
+ path: ["display"]
2362
+ });
2363
+ }
2364
+ });
2365
+ var SkillDetailResponseSchema = z2.object({
2366
+ skill: z2.object({
2367
+ slug: SlugSchema,
2368
+ title: z2.string().min(1),
2369
+ description: z2.string(),
2370
+ owner: PersonNameEmailSchema,
2371
+ created_at: IsoDateTimeSchema,
2372
+ license: z2.string().nullable().optional(),
2373
+ min_floom: z2.string().nullable().optional(),
2374
+ latest: VersionRefSchema,
2375
+ works_with: z2.array(TargetSchema),
2376
+ files: z2.array(FileMetadataSchema),
2377
+ versions: z2.array(VersionHistoryItemSchema)
2378
+ }).strict()
2379
+ }).strict();
2380
+ var SkillVersionReadResponseSchema = z2.object({
2381
+ skill: z2.object({
2382
+ slug: SlugSchema,
2383
+ title: z2.string().min(1),
2384
+ description: z2.string()
2385
+ }).strict(),
2386
+ version: VersionHistoryItemSchema,
2387
+ files: z2.array(FileMetadataSchema),
2388
+ tree: z2.array(FileTreeEntrySchema),
2389
+ selected_file: FileContentSchema.nullable(),
2390
+ file_contents: z2.array(FileContentSchema)
2391
+ }).strict();
2392
+ var ShareCreateRequestSchema = z2.object({}).strict();
2393
+ var ShareCreateResponseSchema = z2.object({
2394
+ url: UrlSchema,
2395
+ token: z2.string().min(32)
2396
+ }).strict();
2397
+ var PublicShareGetResponseSchema = z2.object({
2398
+ skill: z2.object({
2399
+ title: z2.string().min(1),
2400
+ description: z2.string(),
2401
+ owner: PersonNameSchema,
2402
+ version: VersionRefSchema
2403
+ }).strict(),
2404
+ files: z2.array(FileMetadataSchema),
2405
+ tree: z2.array(FileTreeEntrySchema),
2406
+ selected_file: FileContentSchema.nullable(),
2407
+ file_contents: z2.array(FileContentSchema),
2408
+ license: z2.string().nullable().optional()
2409
+ }).strict();
2410
+ var DeviceSessionStateSchema = z2.enum([
2411
+ "pending",
2412
+ "approved",
2413
+ "denied",
2414
+ "expired"
2415
+ ]);
2416
+ var DeviceSessionResponseSchema = z2.object({
2417
+ state: DeviceSessionStateSchema,
2418
+ device: z2.object({
2419
+ label: z2.string().min(1),
2420
+ location: z2.string().min(1).nullable().optional(),
2421
+ client: z2.string().min(1),
2422
+ first_seen: IsoDateTimeSchema
2423
+ }).strict()
2424
+ }).strict();
2425
+ var DeviceSessionActionResponseSchema = z2.object({
2426
+ state: DeviceSessionStateSchema
2427
+ }).strict();
2428
+ var CliLibraryStateSchema = z2.enum([
2429
+ "active",
2430
+ "stale",
2431
+ "missing",
2432
+ "dirty",
2433
+ "conflict",
2434
+ "unsupported_target"
2435
+ ]);
2436
+ var CliLibraryTargetQuerySchema = z2.object({
2437
+ target: TargetSchema
2438
+ }).strict();
2439
+ var CliLibrarySkillSchema = z2.object({
2440
+ slug: SlugSchema,
2441
+ title: z2.string().min(1),
2442
+ description: z2.string(),
2443
+ version: VersionRefSchema,
2444
+ hash: Sha256Schema,
2445
+ state: CliLibraryStateSchema,
2446
+ files: z2.array(FileContentSchema)
2447
+ }).strict();
2448
+ var CliLibraryResponseSchema = z2.object({
2449
+ workspace: WorkspaceSchema,
2450
+ target: TargetSchema,
2451
+ generated_at: IsoDateTimeSchema,
2452
+ manifest_schema_version: z2.literal(1),
2453
+ hash_basis: z2.literal("workspace_slug:skill_slug:version_seq:file_sha256"),
2454
+ full_hash: Sha256Schema,
2455
+ states: z2.array(CliLibraryStateSchema),
2456
+ skills: z2.array(CliLibrarySkillSchema)
2457
+ }).strict();
2458
+ var LocalManifestSkillSchema = z2.object({
2459
+ slug: SlugSchema,
2460
+ version_seq: VersionSeqSchema,
2461
+ hash: Sha256Schema,
2462
+ state: CliLibraryStateSchema,
2463
+ installed_paths: z2.array(z2.string().min(1))
2464
+ }).strict();
2465
+ var LocalManifestSchema = z2.object({
2466
+ manifest_schema_version: z2.literal(1),
2467
+ target: TargetSchema,
2468
+ workspace_slug: SlugSchema,
2469
+ full_hash: Sha256Schema,
2470
+ hash_basis: z2.literal("workspace_slug:skill_slug:version_seq:file_sha256"),
2471
+ installed_at: IsoDateTimeSchema,
2472
+ backup_path: z2.string().min(1).nullable(),
2473
+ skills: z2.array(LocalManifestSkillSchema)
2474
+ }).strict();
2475
+ var ContractDecisionChecklistItemSchema = z2.object({
2476
+ id: z2.string().min(1),
2477
+ title: z2.string().min(1),
2478
+ locked: z2.literal(true)
2479
+ }).strict();
2480
+
2481
+ // src/lib/output.ts
2482
+ import chalk from "chalk";
2483
+ var log = {
2484
+ info: (msg) => console.log(msg),
2485
+ ok: (msg) => console.log(chalk.green("\u2713 ") + msg),
2486
+ warn: (msg) => console.log(chalk.yellow("! ") + msg),
2487
+ err: (msg) => console.error(chalk.red("\u2717 ") + msg),
2488
+ step: (msg) => console.log(chalk.dim("\xB7 ") + msg),
2489
+ heading: (msg) => console.log("\n" + chalk.bold(msg)),
2490
+ kv: (key, value) => console.log(` ${chalk.dim(key.padEnd(18))}${value}`),
2491
+ blank: () => console.log("")
2492
+ };
2493
+
2494
+ // src/config.ts
2495
+ import { homedir as homedir2 } from "node:os";
2496
+ import { join as join3 } from "node:path";
2497
+ import { mkdir as mkdir2, readFile as readFile3, writeFile, chmod } from "node:fs/promises";
2498
+ var CONFIG_DIR = join3(homedir2(), ".floom");
2499
+ var AUTH_FILE = join3(CONFIG_DIR, "auth.json");
2500
+ var DEFAULT_APP_URL = "https://floom.dev";
2501
+ var DEFAULT_API_URL = "https://floom.dev/api/v1";
2502
+ var TRUSTED_API_HOSTS = /* @__PURE__ */ new Set(["floom.dev", "try.floom.dev"]);
2503
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
2504
+ function devModeEnabled() {
2505
+ const v = process.env.FLOOM_DEV;
2506
+ return v === "1" || v === "true";
2507
+ }
2508
+ function isTrustedApiUrl(apiUrl) {
2509
+ if (!apiUrl) return false;
2510
+ try {
2511
+ const url = new URL(apiUrl);
2512
+ if (TRUSTED_API_HOSTS.has(url.hostname)) {
2513
+ return url.protocol === "https:";
2514
+ }
2515
+ if (LOOPBACK_HOSTS.has(url.hostname) && devModeEnabled()) {
2516
+ return true;
2517
+ }
2518
+ return false;
2519
+ } catch {
2520
+ return false;
2521
+ }
2522
+ }
2523
+ function trustedApiUrlOrDefault(apiUrl) {
2524
+ if (!apiUrl) return DEFAULT_API_URL;
2525
+ const trimmed = apiUrl.replace(/\/$/, "");
2526
+ if (!isTrustedApiUrl(trimmed)) return DEFAULT_API_URL;
2527
+ try {
2528
+ if (TRUSTED_API_HOSTS.has(new URL(trimmed).hostname)) return DEFAULT_API_URL;
2529
+ } catch {
2530
+ return DEFAULT_API_URL;
2531
+ }
2532
+ return trimmed;
2533
+ }
2534
+ async function ensureDir() {
2535
+ await mkdir2(CONFIG_DIR, { recursive: true, mode: 448 });
2536
+ }
2537
+ async function readAuth() {
2538
+ const parsed = await readRawAuth();
2539
+ return parsed ? { ...parsed, apiUrl: normalizeApiUrl(parsed.apiUrl) } : null;
2540
+ }
2541
+ async function readRawAuth() {
2542
+ let raw;
2543
+ try {
2544
+ raw = await readFile3(AUTH_FILE, "utf8");
2545
+ } catch (e) {
2546
+ const code = e.code;
2547
+ if (code === "ENOENT") return null;
2548
+ if (code === "EACCES" || code === "EPERM") {
2549
+ throw new Error("Cannot read your Floom auth file (permission denied). Fix its permissions or run: floom login");
2550
+ }
2551
+ throw new Error("Cannot read your Floom auth file. Run: floom login");
2552
+ }
2553
+ try {
2554
+ return JSON.parse(raw);
2555
+ } catch {
2556
+ throw new Error("Your Floom auth file is unreadable. Run: floom login");
2557
+ }
2558
+ }
2559
+ async function writeAuth(state) {
2560
+ await ensureDir();
2561
+ await writeFile(AUTH_FILE, JSON.stringify({ ...state, apiUrl: normalizeApiUrl(state.apiUrl) }, null, 2), { mode: 384 });
2562
+ try {
2563
+ await chmod(AUTH_FILE, 384);
2564
+ } catch {
2565
+ }
2566
+ }
2567
+ async function clearAuth() {
2568
+ const { unlink } = await import("node:fs/promises");
2569
+ try {
2570
+ await unlink(AUTH_FILE);
2571
+ } catch {
2572
+ }
2573
+ }
2574
+ function getApiUrl() {
2575
+ if (process.env.FLOOM_API_URL) {
2576
+ return trustedApiUrlOrDefault(process.env.FLOOM_API_URL);
2577
+ }
2578
+ if (process.env.FLOOM_APP_URL) {
2579
+ return trustedApiUrlOrDefault(`${getAppUrl().replace(/\/$/, "")}/api/v1`);
2580
+ }
2581
+ return DEFAULT_API_URL;
2582
+ }
2583
+ function getAppUrl() {
2584
+ return process.env.FLOOM_APP_URL ?? DEFAULT_APP_URL;
2585
+ }
2586
+ function getApiBaseUrls(preferred) {
2587
+ const explicitApiUrl = process.env.FLOOM_API_URL?.trim();
2588
+ if (explicitApiUrl) return [trustedApiUrlOrDefault(explicitApiUrl)];
2589
+ return [normalizeApiUrl(preferred ?? getApiUrl())];
2590
+ }
2591
+ function normalizeApiUrl(apiUrl) {
2592
+ return trustedApiUrlOrDefault(apiUrl);
2593
+ }
2594
+
2595
+ // src/lib/machine.ts
2596
+ import { hostname, platform, type, release } from "node:os";
2597
+ import { join as join4 } from "node:path";
2598
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
2599
+ import { homedir as homedir3 } from "node:os";
2600
+ import { randomUUID } from "node:crypto";
2601
+ import { spawn } from "node:child_process";
2602
+ var CONFIG_DIR2 = join4(homedir3(), ".floom");
2603
+ var MACHINE_FILE = join4(CONFIG_DIR2, "machine.json");
2604
+ async function tryCommand(cmd, args) {
2605
+ return new Promise((resolve3) => {
2606
+ let child;
2607
+ try {
2608
+ child = spawn(cmd, args, { stdio: ["ignore", "pipe", "ignore"] });
2609
+ } catch {
2610
+ resolve3(null);
2611
+ return;
2612
+ }
2613
+ let out = "";
2614
+ child.stdout?.on("data", (d) => {
2615
+ out += d.toString();
2616
+ });
2617
+ child.on("close", (code) => resolve3(code === 0 ? out.trim() : null));
2618
+ child.on("error", () => resolve3(null));
2619
+ const timer = setTimeout(() => {
2620
+ try {
2621
+ child.kill();
2622
+ } catch {
2623
+ }
2624
+ resolve3(null);
2625
+ }, 800);
2626
+ child.on("close", () => clearTimeout(timer));
2627
+ });
2628
+ }
2629
+ async function nativeDeviceName() {
2630
+ const p = platform();
2631
+ if (p === "darwin") {
2632
+ return tryCommand("scutil", ["--get", "ComputerName"]);
2633
+ }
2634
+ if (p === "win32") {
2635
+ return process.env.COMPUTERNAME ?? null;
2636
+ }
2637
+ return null;
2638
+ }
2639
+ async function defaultLabel() {
2640
+ const native = await nativeDeviceName();
2641
+ const host = (hostname() || "").trim();
2642
+ const base = native || host || `${type()}`.slice(0, 40);
2643
+ const p = platform();
2644
+ const os = p === "darwin" ? "macOS" : p === "linux" ? "Linux" : p === "win32" ? "Windows" : type();
2645
+ const label = base.toLowerCase().includes(os.toLowerCase()) ? base : `${base} \xB7 ${os}`;
2646
+ return label.slice(0, 80);
2647
+ }
2648
+ function defaultLabelSync() {
2649
+ const host = (hostname() || "").trim();
2650
+ if (host) return host.slice(0, 80);
2651
+ return `${type()} ${release()}`.slice(0, 80);
2652
+ }
2653
+ async function getMachineIdentity() {
2654
+ try {
2655
+ const raw = await readFile4(MACHINE_FILE, "utf8");
2656
+ const parsed = JSON.parse(raw);
2657
+ if (typeof parsed?.id === "string" && /^[0-9a-f-]{36}$/i.test(parsed.id)) {
2658
+ const label2 = parsed.label || await defaultLabel();
2659
+ return { id: parsed.id, label: label2.slice(0, 80) };
2660
+ }
2661
+ } catch {
2662
+ }
2663
+ const label = await defaultLabel().catch(() => defaultLabelSync());
2664
+ const identity = { id: randomUUID(), label };
2665
+ try {
2666
+ await mkdir3(CONFIG_DIR2, { recursive: true, mode: 448 });
2667
+ await writeFile2(
2668
+ MACHINE_FILE,
2669
+ JSON.stringify({ id: identity.id, label: identity.label, created_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
2670
+ { mode: 384 }
2671
+ );
2672
+ } catch {
2673
+ }
2674
+ return identity;
2675
+ }
2676
+
2677
+ // src/version.ts
2678
+ var VERSION = "0.2.21";
2679
+
2680
+ // src/api-client.ts
2681
+ var DEFAULT_TIMEOUT_MS = 2e4;
2682
+ var MAX_ERROR_BODY_BYTES = 64 * 1024;
2683
+ function timeoutMs() {
2684
+ const raw = Number(process.env.FLOOM_API_TIMEOUT_MS);
2685
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_TIMEOUT_MS;
2686
+ }
2687
+ async function fetchWithTimeout(url, init = {}) {
2688
+ const controller = new AbortController();
2689
+ const timer = setTimeout(() => controller.abort(), timeoutMs());
2690
+ try {
2691
+ return await fetch(url, { ...init, redirect: "manual", signal: controller.signal });
2692
+ } catch (e) {
2693
+ if (e.name === "AbortError") {
2694
+ throw new Error(`Request timed out after ${timeoutMs()}ms`);
2695
+ }
2696
+ throw e;
2697
+ } finally {
2698
+ clearTimeout(timer);
2699
+ }
2700
+ }
2701
+ async function readLimitedText(res, limitBytes = MAX_ERROR_BODY_BYTES) {
2702
+ if (!res.body) return res.text();
2703
+ const reader = res.body.getReader();
2704
+ const chunks = [];
2705
+ let total = 0;
2706
+ let truncated = false;
2707
+ while (true) {
2708
+ const { done, value } = await reader.read();
2709
+ if (done) break;
2710
+ const chunk = value ?? new Uint8Array();
2711
+ if (total + chunk.byteLength > limitBytes) {
2712
+ const remaining = Math.max(limitBytes - total, 0);
2713
+ if (remaining > 0) chunks.push(chunk.slice(0, remaining));
2714
+ truncated = true;
2715
+ await reader.cancel();
2716
+ break;
2717
+ }
2718
+ chunks.push(chunk);
2719
+ total += chunk.byteLength;
2720
+ }
2721
+ const text = new TextDecoder().decode(Buffer.concat(chunks));
2722
+ return truncated ? `${text}
2723
+ [truncated]` : text;
2724
+ }
2725
+ async function api(path, opts = {}) {
2726
+ const auth = await readAuth();
2727
+ const token = process.env.FLOOM_API_TOKEN?.trim() || auth?.token;
2728
+ if (opts.authRequired && !token) {
2729
+ throw new FloomError("AUTH_REQUIRED", "Not logged in. Run: floom login");
2730
+ }
2731
+ let lastError = null;
2732
+ const bases = getApiBaseUrls(auth?.apiUrl);
2733
+ for (const base of bases) {
2734
+ const url = new URL(base + path);
2735
+ if (opts.query) {
2736
+ for (const [k, v] of Object.entries(opts.query)) {
2737
+ if (v !== void 0) url.searchParams.set(k, String(v));
2738
+ }
2739
+ }
2740
+ const headers = {
2741
+ "Content-Type": "application/json",
2742
+ "User-Agent": `floom-cli/${VERSION}`,
2743
+ "x-floom-cli-version": VERSION
2744
+ };
2745
+ if (token && opts.authRequired) headers.Authorization = `Bearer ${token}`;
2746
+ try {
2747
+ const mach = await getMachineIdentity();
2748
+ headers["x-floom-machine-id"] = mach.id;
2749
+ headers["x-floom-machine-label"] = encodeURIComponent(mach.label);
2750
+ } catch {
2751
+ }
2752
+ let res;
2753
+ try {
2754
+ res = await fetchWithTimeout(url.toString(), {
2755
+ method: opts.method ?? "GET",
2756
+ headers,
2757
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
2758
+ });
2759
+ } catch (e) {
2760
+ lastError = new FloomError(
2761
+ "INTERNAL_ERROR",
2762
+ `Unable to reach Floom API at ${base}: ${e.message}`,
2763
+ { apiUrl: base }
2764
+ );
2765
+ continue;
2766
+ }
2767
+ if (res.type === "opaqueredirect" || res.status >= 300 && res.status < 400) {
2768
+ throw new FloomError(
2769
+ "INTERNAL_ERROR",
2770
+ "Refusing to follow an HTTP redirect from the Floom API.",
2771
+ { apiUrl: base }
2772
+ );
2773
+ }
2774
+ const text = res.ok ? await res.text() : await readLimitedText(res);
2775
+ let json = null;
2776
+ try {
2777
+ json = text ? JSON.parse(text) : null;
2778
+ } catch {
2779
+ }
2780
+ const envelopeError = json?.error;
2781
+ if (envelopeError && typeof envelopeError === "object" && envelopeError.code) {
2782
+ throw new FloomError(
2783
+ envelopeError.code,
2784
+ typeof envelopeError.message === "string" ? envelopeError.message : String(envelopeError.code),
2785
+ { status: res.status, requestId: envelopeError.request_id, apiUrl: base }
2786
+ );
2787
+ }
2788
+ if (res.ok) return json;
2789
+ const err = envelopeError ?? {};
2790
+ lastError = new FloomError(
2791
+ err.code ?? "INTERNAL_ERROR",
2792
+ err.message ?? `HTTP ${res.status} ${res.statusText}`,
2793
+ { status: res.status, requestId: err.request_id, apiUrl: base }
2794
+ );
2795
+ if (res.status !== 404 || json?.error) break;
2796
+ }
2797
+ throw lastError ?? new FloomError("INTERNAL_ERROR", "API request failed");
2798
+ }
2799
+
2800
+ // src/commands/login.ts
2801
+ function sleep(ms) {
2802
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
2803
+ }
2804
+ async function loginCommand() {
2805
+ const session = await api("/cli/device/start", {
2806
+ method: "POST",
2807
+ body: {
2808
+ label: process.env.FLOOM_DEVICE_LABEL ?? "CLI device",
2809
+ client: `floom-cli/${VERSION}`
2810
+ }
2811
+ });
2812
+ log.heading("CLI login");
2813
+ log.info(`Open: ${session.verification_uri}`);
2814
+ log.info(`Code: ${session.user_code}`);
2815
+ log.info("Waiting for browser approval. Press Ctrl+C to cancel.");
2816
+ const deadline = new Date(session.expires_at).getTime();
2817
+ const interval = Math.max(2, session.poll_interval_seconds) * 1e3;
2818
+ while (Date.now() < deadline) {
2819
+ try {
2820
+ const token = await api("/cli/device/token", {
2821
+ method: "POST",
2822
+ body: { device_code: session.device_code }
2823
+ });
2824
+ if (typeof token.token !== "string" || token.token.trim() === "") {
2825
+ process.stdout.write(".");
2826
+ } else {
2827
+ await writeAuth({
2828
+ token: token.token,
2829
+ handle: token.workspace?.slug ?? "workspace",
2830
+ email: token.user?.email ?? "unknown@example.com",
2831
+ apiUrl: getApiUrl()
2832
+ });
2833
+ log.ok(`Logged in to ${token.workspace?.name ?? "Floom"}.`);
2834
+ return;
2835
+ }
2836
+ } catch (error) {
2837
+ const message = error instanceof Error ? error.message : String(error);
2838
+ if (error instanceof FloomError && String(error.code) === "DEVICE_PENDING" || message.toLowerCase().includes("pending")) {
2839
+ process.stdout.write(".");
2840
+ } else {
2841
+ throw error;
2842
+ }
2843
+ }
2844
+ const remaining = deadline - Date.now();
2845
+ if (remaining <= 0) break;
2846
+ await sleep(Math.min(interval, remaining));
2847
+ }
2848
+ throw new Error("Login timed out.");
2849
+ }
2850
+
2851
+ // src/commands/mcp.ts
2852
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2853
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2854
+
2855
+ // src/commands/sync.ts
2856
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
2857
+ import { cp, lstat as lstat2, mkdir as mkdir4, readdir as readdir2, readFile as readFile5, rename, rm, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
2858
+ import { dirname, join as join5, sep as sep3 } from "node:path";
2859
+ import { createInterface } from "node:readline/promises";
2860
+ import { ZodError } from "zod";
2861
+
2862
+ // src/lib/signals.ts
2863
+ import { rmSync } from "node:fs";
2864
+ function installCancellationHandler() {
2865
+ const cleanupDirs = /* @__PURE__ */ new Set();
2866
+ const handlers = /* @__PURE__ */ new Map();
2867
+ const cleanupAndExit = (signal) => {
2868
+ log.warn("Cancelling\u2026");
2869
+ for (const dir of cleanupDirs) {
2870
+ try {
2871
+ rmSync(dir, { recursive: true, force: true });
2872
+ } catch (error) {
2873
+ log.warn(`Failed to remove temp dir ${dir}: ${error.message}`);
2874
+ }
2875
+ }
2876
+ process.exit(signal === "SIGINT" ? 130 : 143);
2877
+ };
2878
+ for (const signal of ["SIGINT", "SIGTERM"]) {
2879
+ const handler = () => cleanupAndExit(signal);
2880
+ handlers.set(signal, handler);
2881
+ process.once(signal, handler);
2882
+ }
2883
+ return {
2884
+ trackDir(path) {
2885
+ cleanupDirs.add(path);
2886
+ },
2887
+ dispose() {
2888
+ for (const [signal, handler] of handlers) {
2889
+ process.off(signal, handler);
2890
+ }
2891
+ cleanupDirs.clear();
2892
+ }
2893
+ };
2894
+ }
2895
+
2896
+ // src/commands/sync.ts
2897
+ var BACKUP_RETENTION_LIMIT = 10;
2898
+ var MAX_DECODED_FILE_BYTES = 2 * 1024 * 1024;
2899
+ var SAFE_SLUG_RE = /^(?=.{1,80}$)[a-z0-9]+(?:-[a-z0-9]+)*$/;
2900
+ function nowStamp() {
2901
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2902
+ }
2903
+ function backupStamp() {
2904
+ return `${nowStamp()}-${randomUUID2().slice(0, 8)}`;
2905
+ }
2906
+ function hashBytes(bytes) {
2907
+ return createHash3("sha256").update(bytes).digest("hex");
2908
+ }
2909
+ function manifestPath(root) {
2910
+ return join5(root, ".floom", "manifest.json");
2911
+ }
2912
+ async function readManifest(root) {
2913
+ try {
2914
+ const raw = await readFile5(manifestPath(root), "utf8");
2915
+ return LocalManifestSchema.parse(JSON.parse(raw));
2916
+ } catch (error) {
2917
+ const err = error;
2918
+ if (err.code === "ENOENT") return null;
2919
+ if (error instanceof SyntaxError || error instanceof ZodError) {
2920
+ throw new Error(
2921
+ `Corrupted manifest at ${manifestPath(root)}. Remove that file or run floom pull after backing up local edits.`
2922
+ );
2923
+ }
2924
+ throw error;
2925
+ }
2926
+ }
2927
+ function decodedBase64ByteLength(encoded) {
2928
+ const compact = encoded.replace(/\s/g, "");
2929
+ const padding = compact.endsWith("==") ? 2 : compact.endsWith("=") ? 1 : 0;
2930
+ return Math.floor(compact.length * 3 / 4) - padding;
2931
+ }
2932
+ function bytesForFile(file) {
2933
+ if (file.mode === "text") {
2934
+ if (Buffer.byteLength(file.content_text, "utf8") > MAX_DECODED_FILE_BYTES) {
2935
+ throw new Error(`Remote file ${file.path} exceeds the 2 MiB decoded file limit.`);
2936
+ }
2937
+ const bytes2 = Buffer.from(file.content_text, "utf8");
2938
+ const actualHash2 = hashBytes(bytes2);
2939
+ if (actualHash2 !== file.sha256) {
2940
+ throw new Error(`INTEGRITY_FAIL: sha256 mismatch on ${file.path}: expected ${file.sha256}, got ${actualHash2}`);
2941
+ }
2942
+ return bytes2;
2943
+ }
2944
+ const url = file.binary.download_url;
2945
+ if (!url?.startsWith("data:")) return Buffer.alloc(0);
2946
+ const [, encoded] = url.split(",", 2);
2947
+ const base64 = encoded ?? "";
2948
+ if (decodedBase64ByteLength(base64) > MAX_DECODED_FILE_BYTES) {
2949
+ throw new Error(`Remote file ${file.path} exceeds the 2 MiB decoded file limit.`);
2950
+ }
2951
+ const bytes = Buffer.from(base64, "base64");
2952
+ if (bytes.byteLength > MAX_DECODED_FILE_BYTES) {
2953
+ throw new Error(`Remote file ${file.path} exceeds the 2 MiB decoded file limit.`);
2954
+ }
2955
+ const actualHash = hashBytes(bytes);
2956
+ if (actualHash !== file.sha256) {
2957
+ throw new Error(`INTEGRITY_FAIL: sha256 mismatch on ${file.path}: expected ${file.sha256}, got ${actualHash}`);
2958
+ }
2959
+ return bytes;
2960
+ }
2961
+ function safeSkillSlug(slug) {
2962
+ if (!slug) throw new Error("Remote skill slug is empty.");
2963
+ if (slug.length > 80) throw new Error(`Remote skill slug "${slug.slice(0, 80)}\u2026" exceeds 80 characters.`);
2964
+ if (!SAFE_SLUG_RE.test(slug)) throw new Error(`Unsafe remote skill slug: ${slug}`);
2965
+ return slug;
2966
+ }
2967
+ function safeRemotePath(path) {
2968
+ const normalized = path.replace(/\\/g, "/");
2969
+ if (!normalized || /[\x00-\x1f\x7f]/.test(normalized) || normalized.startsWith("/") || normalized.split("/").some((part) => part === "" || part === "." || part === "..")) {
2970
+ throw new Error(`Unsafe remote file path: ${path}`);
2971
+ }
2972
+ return normalized;
2973
+ }
2974
+ async function fileExists(path) {
2975
+ try {
2976
+ await stat3(path);
2977
+ return true;
2978
+ } catch {
2979
+ return false;
2980
+ }
2981
+ }
2982
+ async function liveSkillHash(root, workspaceSlug, skill) {
2983
+ const slug = safeSkillSlug(skill.slug);
2984
+ const fileHashes = [];
2985
+ for (const file of skill.files) {
2986
+ const path = join5(root, slug, ...safeRemotePath(file.path).split("/"));
2987
+ if (!await fileExists(path)) return null;
2988
+ const bytes = await readFile5(path);
2989
+ fileHashes.push(hashBytes(bytes));
2990
+ }
2991
+ return hashBytes([
2992
+ workspaceSlug,
2993
+ slug,
2994
+ String(skill.version.version_seq),
2995
+ ...fileHashes.sort()
2996
+ ].join(":"));
2997
+ }
2998
+ async function liveManifestSkillHash(root, workspaceSlug, skill) {
2999
+ const fileHashes = [];
3000
+ for (const installedPath of skill.installed_paths) {
3001
+ const path = join5(root, installedPath);
3002
+ if (!await fileExists(path)) return null;
3003
+ const bytes = await readFile5(path);
3004
+ fileHashes.push(hashBytes(bytes));
3005
+ }
3006
+ return hashBytes([
3007
+ workspaceSlug,
3008
+ skill.slug,
3009
+ String(skill.version_seq),
3010
+ ...fileHashes.sort()
3011
+ ].join(":"));
3012
+ }
3013
+ function computeSyncState(input) {
3014
+ if (input.unsupportedTarget) return "unsupported_target";
3015
+ if (input.deletedRemote) return "stale";
3016
+ if (!input.previousHash || !input.remoteHash || input.liveHash === null) return "missing";
3017
+ const dirty = input.liveHash !== input.previousHash;
3018
+ const stale = input.previousHash !== input.remoteHash;
3019
+ if (dirty && stale) return "conflict";
3020
+ if (dirty) return "dirty";
3021
+ if (stale) return "stale";
3022
+ return "active";
3023
+ }
3024
+ async function writeSkillAtomically(root, skill) {
3025
+ const slug = safeSkillSlug(skill.slug);
3026
+ const tempDir = join5(root, ".floom", "tmp", `${slug}-${nowStamp()}`);
3027
+ const finalDir = join5(root, slug);
3028
+ const replacedDir = join5(root, ".floom", "tmp", `${slug}-previous-${backupStamp()}`);
3029
+ await rm(tempDir, { recursive: true, force: true });
3030
+ await rm(replacedDir, { recursive: true, force: true });
3031
+ await mkdir4(tempDir, { recursive: true });
3032
+ const installed = [];
3033
+ for (const file of skill.files) {
3034
+ const safePath = safeRemotePath(file.path);
3035
+ const dest = join5(tempDir, ...safePath.split("/"));
3036
+ await mkdir4(dirname(dest), { recursive: true });
3037
+ await writeFile3(dest, bytesForFile(file));
3038
+ installed.push([slug, ...safePath.split("/")].join(sep3));
3039
+ }
3040
+ let movedExisting = false;
3041
+ try {
3042
+ await mkdir4(dirname(finalDir), { recursive: true });
3043
+ if (await fileExists(finalDir)) {
3044
+ try {
3045
+ await rename(finalDir, replacedDir);
3046
+ } catch (error) {
3047
+ throw new Error(`Failed to prepare install for ${slug}; existing install remains at ${finalDir}: ${error.message}`);
3048
+ }
3049
+ movedExisting = true;
3050
+ }
3051
+ try {
3052
+ await rename(tempDir, finalDir);
3053
+ } catch (error) {
3054
+ if (movedExisting) {
3055
+ try {
3056
+ await rename(replacedDir, finalDir);
3057
+ } catch (restoreError) {
3058
+ throw new Error(
3059
+ `Failed to install ${slug} and could not restore the previous install from ${replacedDir} to ${finalDir}: ${restoreError.message}. Original error: ${error.message}`
3060
+ );
3061
+ }
3062
+ }
3063
+ throw new Error(`Failed to install ${slug}; previous install ${movedExisting ? "was restored" : "remains"} at ${finalDir}: ${error.message}`);
3064
+ }
3065
+ if (movedExisting) await rm(replacedDir, { recursive: true, force: true });
3066
+ } catch (error) {
3067
+ throw error;
3068
+ } finally {
3069
+ await rm(tempDir, { recursive: true, force: true });
3070
+ if (!movedExisting) await rm(replacedDir, { recursive: true, force: true });
3071
+ }
3072
+ return installed.sort();
3073
+ }
3074
+ async function backupSkill(root, backupRoot, slug) {
3075
+ slug = safeSkillSlug(slug);
3076
+ const from = join5(root, slug);
3077
+ if (!await fileExists(from)) return false;
3078
+ const to = join5(backupRoot, slug);
3079
+ await rm(to, { recursive: true, force: true });
3080
+ await mkdir4(dirname(to), { recursive: true });
3081
+ await cp(from, to, { recursive: true });
3082
+ return true;
3083
+ }
3084
+ async function pruneBackups(root, keep = BACKUP_RETENTION_LIMIT) {
3085
+ const backupsDir = join5(root, ".floom", "backups");
3086
+ let entries;
3087
+ try {
3088
+ entries = await readdir2(backupsDir);
3089
+ } catch {
3090
+ return;
3091
+ }
3092
+ const sorted = entries.sort().reverse();
3093
+ for (const stale of sorted.slice(keep)) {
3094
+ if (stale === "." || stale === ".." || stale.includes("/") || stale.includes("\\") || /[\x00-\x1f\x7f]/.test(stale)) {
3095
+ log.warn(`Skipping malformed backup entry: ${stale}`);
3096
+ continue;
3097
+ }
3098
+ const candidate = join5(backupsDir, stale);
3099
+ try {
3100
+ const info = await lstat2(candidate);
3101
+ if (info.isSymbolicLink()) {
3102
+ log.warn(`Skipping symlink in backups dir: ${stale}`);
3103
+ continue;
3104
+ }
3105
+ } catch {
3106
+ continue;
3107
+ }
3108
+ await rm(candidate, { recursive: true, force: true });
3109
+ }
3110
+ }
3111
+ async function fetchLibrary(target, operation) {
3112
+ const response = await api("/cli/library", {
3113
+ authRequired: true,
3114
+ query: { target, operation }
3115
+ });
3116
+ return CliLibraryResponseSchema.parse(response);
3117
+ }
3118
+ async function confirmPullAll(count) {
3119
+ if (!process.stdin.isTTY) return true;
3120
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
3121
+ try {
3122
+ const answer = (await rl.question(`Pull the whole library into all ${count}? [Y/n] `)).trim().toLowerCase();
3123
+ return answer === "" || answer === "y" || answer === "yes";
3124
+ } finally {
3125
+ rl.close();
3126
+ }
3127
+ }
3128
+ function printDetectedTargets(targets) {
3129
+ log.info("Detected agents on this machine:");
3130
+ for (const target of targets) log.info(`- ${target}`);
3131
+ }
3132
+ function printNoDetectedTargets() {
3133
+ log.warn(`No installed agents detected. Checked: ${INSTALL_TARGETS.join(", ")}.`);
3134
+ log.info(`Use --target <${INSTALL_TARGETS.join("|")}> to choose one target explicitly.`);
3135
+ }
3136
+ async function statusCommand(options) {
3137
+ if (!options.target) {
3138
+ const targets = await detectInstalledTargets();
3139
+ if (targets.length === 0) {
3140
+ printNoDetectedTargets();
3141
+ process.exitCode = 1;
3142
+ return;
3143
+ }
3144
+ printDetectedTargets(targets);
3145
+ const results = [];
3146
+ for (const target2 of targets) {
3147
+ try {
3148
+ results.push({ target: target2, ok: true, status: await statusLibrary(target2) });
3149
+ } catch (error) {
3150
+ results.push({ target: target2, ok: false, error });
3151
+ }
3152
+ }
3153
+ for (const result of results) {
3154
+ if (!result.ok) {
3155
+ log.err(`${result.target} ${result.error.message}`);
3156
+ continue;
3157
+ }
3158
+ log.heading(`${result.status.workspaceName} (${result.target})`);
3159
+ for (const line of result.status.skills) log.info(`${line.slug} ${line.state} ${line.version}`);
3160
+ }
3161
+ if (results.some((result) => !result.ok)) process.exitCode = 1;
3162
+ return;
3163
+ }
3164
+ const target = assertInstallTarget(options.target);
3165
+ const status = await statusLibrary(target);
3166
+ log.heading(`${status.workspaceName} (${target})`);
3167
+ for (const line of status.skills) log.info(`${line.slug} ${line.state} ${line.version}`);
3168
+ }
3169
+ async function statusLibrary(target, deps = {}) {
3170
+ const resolved = resolveInstallDir({ target, global: true });
3171
+ const remote = await (deps.fetchLibrary ?? fetchLibrary)(target, "status");
3172
+ const manifest = await readManifest(resolved.dir);
3173
+ if (!manifest) {
3174
+ return {
3175
+ workspaceName: remote.workspace.name,
3176
+ skills: remote.skills.map((skill) => ({ slug: skill.slug, state: "missing", version: skill.version.display }))
3177
+ };
3178
+ }
3179
+ const manifestMatchesTarget = manifest.target === target && manifest.workspace_slug === remote.workspace.slug;
3180
+ const remoteBySlug = new Map(remote.skills.map((skill) => [skill.slug, skill]));
3181
+ const skills = [];
3182
+ for (const skill of remote.skills) {
3183
+ const previous = manifestMatchesTarget ? manifest.skills.find((entry) => entry.slug === skill.slug) : null;
3184
+ const liveHash = await liveSkillHash(resolved.dir, remote.workspace.slug, skill);
3185
+ const state = computeSyncState({
3186
+ previousHash: previous?.hash ?? null,
3187
+ remoteHash: skill.hash,
3188
+ liveHash
3189
+ });
3190
+ skills.push({ slug: skill.slug, state, version: skill.version.display });
3191
+ }
3192
+ for (const previous of manifest.skills) {
3193
+ if (manifestMatchesTarget && remoteBySlug.has(previous.slug)) continue;
3194
+ skills.push({
3195
+ slug: previous.slug,
3196
+ state: computeSyncState({
3197
+ previousHash: previous.hash,
3198
+ remoteHash: null,
3199
+ liveHash: await liveManifestSkillHash(resolved.dir, manifest.workspace_slug, previous),
3200
+ unsupportedTarget: !manifestMatchesTarget,
3201
+ deletedRemote: manifestMatchesTarget
3202
+ }),
3203
+ version: `v${previous.version_seq}`
3204
+ });
3205
+ }
3206
+ return { workspaceName: remote.workspace.name, skills };
3207
+ }
3208
+ async function pullCommand(options) {
3209
+ const cleanup = installCancellationHandler();
3210
+ try {
3211
+ if (!options.target) {
3212
+ const targets = await detectInstalledTargets();
3213
+ if (targets.length === 0) {
3214
+ printNoDetectedTargets();
3215
+ process.exitCode = 1;
3216
+ return;
3217
+ }
3218
+ printDetectedTargets(targets);
3219
+ if (!await confirmPullAll(targets.length)) {
3220
+ log.info("Cancelled.");
3221
+ return;
3222
+ }
3223
+ for (const target2 of targets) {
3224
+ const resolved2 = resolveInstallDir({ target: target2, global: true });
3225
+ cleanup.trackDir(join5(resolved2.dir, ".floom", "tmp"));
3226
+ }
3227
+ const results = [];
3228
+ for (const target2 of targets) {
3229
+ try {
3230
+ const result2 = await pullLibrary(target2);
3231
+ results.push({ target: target2, ok: true, ...result2 });
3232
+ } catch (error) {
3233
+ results.push({ target: target2, ok: false, error });
3234
+ }
3235
+ }
3236
+ log.heading("Pull summary:");
3237
+ for (const result2 of results) {
3238
+ if (result2.ok) log.ok(`${result2.target} ${result2.skillCount} skills ${result2.dir}`);
3239
+ else log.err(`${result2.target} ${result2.error.message}`);
3240
+ }
3241
+ if (results.some((result2) => !result2.ok)) process.exitCode = 1;
3242
+ return;
3243
+ }
3244
+ const target = assertInstallTarget(options.target);
3245
+ const resolved = resolveInstallDir({ target, global: true });
3246
+ cleanup.trackDir(join5(resolved.dir, ".floom", "tmp"));
3247
+ const result = await pullLibrary(target);
3248
+ log.ok(`Pulled ${result.skillCount} skills into ${target} (${result.dir}).`);
3249
+ log.info(`This syncs ${target} only. For another agent: floom pull --target <claude|codex|cursor|gemini|opencode>`);
3250
+ } finally {
3251
+ cleanup.dispose();
3252
+ }
3253
+ }
3254
+ async function pullLibrary(target, deps = {}) {
3255
+ const resolved = resolveInstallDir({ target, global: true });
3256
+ const remote = await (deps.fetchLibrary ?? fetchLibrary)(target, "pull");
3257
+ for (const skill of remote.skills) safeSkillSlug(skill.slug);
3258
+ const manifest = await readManifest(resolved.dir);
3259
+ const backupRoot = join5(resolved.dir, ".floom", "backups", backupStamp());
3260
+ let wroteBackup = false;
3261
+ const installedSkills = [];
3262
+ await mkdir4(resolved.dir, { recursive: true });
3263
+ const manifestMatchesTarget = Boolean(manifest && manifest.target === target && manifest.workspace_slug === remote.workspace.slug);
3264
+ if (manifestMatchesTarget && manifest?.full_hash === remote.full_hash) {
3265
+ let dirty = false;
3266
+ for (const previous of manifest.skills) {
3267
+ const liveHash = await liveManifestSkillHash(resolved.dir, remote.workspace.slug, previous);
3268
+ if (liveHash !== previous.hash) {
3269
+ dirty = true;
3270
+ break;
3271
+ }
3272
+ }
3273
+ if (!dirty) {
3274
+ return { skillCount: remote.skills.length, dir: resolved.dir };
3275
+ }
3276
+ }
3277
+ const remoteBySlug = new Map(remote.skills.map((skill) => [skill.slug, skill]));
3278
+ if (manifest) {
3279
+ for (const previous of manifest.skills) {
3280
+ if (manifestMatchesTarget && remoteBySlug.has(previous.slug)) continue;
3281
+ wroteBackup = await backupSkill(resolved.dir, backupRoot, previous.slug) || wroteBackup;
3282
+ await rm(join5(resolved.dir, previous.slug), { recursive: true, force: true });
3283
+ }
3284
+ }
3285
+ for (const skill of remote.skills) {
3286
+ const previous = manifestMatchesTarget ? manifest?.skills.find((entry) => entry.slug === skill.slug) ?? null : null;
3287
+ const liveHash = await liveSkillHash(resolved.dir, remote.workspace.slug, skill);
3288
+ const state = computeSyncState({
3289
+ previousHash: previous?.hash ?? null,
3290
+ remoteHash: skill.hash,
3291
+ liveHash
3292
+ });
3293
+ if (state === "active" && previous) {
3294
+ installedSkills.push({
3295
+ slug: skill.slug,
3296
+ version_seq: skill.version.version_seq,
3297
+ hash: skill.hash,
3298
+ state: "active",
3299
+ installed_paths: previous.installed_paths
3300
+ });
3301
+ continue;
3302
+ }
3303
+ if (state === "dirty" || state === "conflict") {
3304
+ wroteBackup = await backupSkill(resolved.dir, backupRoot, skill.slug) || wroteBackup;
3305
+ }
3306
+ if (state === "missing" && await fileExists(join5(resolved.dir, skill.slug))) {
3307
+ wroteBackup = await backupSkill(resolved.dir, backupRoot, skill.slug) || wroteBackup;
3308
+ }
3309
+ const installedPaths = await writeSkillAtomically(resolved.dir, skill);
3310
+ installedSkills.push({
3311
+ slug: skill.slug,
3312
+ version_seq: skill.version.version_seq,
3313
+ hash: skill.hash,
3314
+ state: "active",
3315
+ installed_paths: installedPaths
3316
+ });
3317
+ }
3318
+ const nextManifest = LocalManifestSchema.parse({
3319
+ manifest_schema_version: 1,
3320
+ target,
3321
+ workspace_slug: remote.workspace.slug,
3322
+ full_hash: remote.full_hash,
3323
+ hash_basis: remote.hash_basis,
3324
+ installed_at: (/* @__PURE__ */ new Date()).toISOString(),
3325
+ backup_path: wroteBackup ? backupRoot : null,
3326
+ skills: installedSkills
3327
+ });
3328
+ await mkdir4(dirname(manifestPath(resolved.dir)), { recursive: true });
3329
+ await writeFile3(manifestPath(resolved.dir), JSON.stringify(nextManifest, null, 2));
3330
+ if (wroteBackup) await pruneBackups(resolved.dir);
3331
+ return { skillCount: remote.skills.length, dir: resolved.dir };
3332
+ }
3333
+
3334
+ // src/commands/mcp.ts
3335
+ var targetSchema = TargetSchema;
3336
+ var SESSION_START_ADVISORY = "On session start, pull the workspace library when skills are missing or stale.";
3337
+ function jsonResult(value) {
3338
+ return {
3339
+ content: [
3340
+ {
3341
+ type: "text",
3342
+ text: JSON.stringify(value)
3343
+ }
3344
+ ]
3345
+ };
3346
+ }
3347
+ function createMcpServer(deps = {}) {
3348
+ const pullLibrary2 = deps.pullLibrary ?? pullLibrary;
3349
+ const statusLibrary2 = deps.statusLibrary ?? statusLibrary;
3350
+ const server = new McpServer(
3351
+ { name: "floom", version: VERSION },
3352
+ { instructions: SESSION_START_ADVISORY }
3353
+ );
3354
+ server.registerTool(
3355
+ "workspace_status",
3356
+ {
3357
+ title: "Workspace Status",
3358
+ description: "Read the authenticated CLI session workspace sync status for one install target.",
3359
+ inputSchema: { target: targetSchema.default("codex") },
3360
+ annotations: {
3361
+ readOnlyHint: true,
3362
+ destructiveHint: false,
3363
+ idempotentHint: true,
3364
+ openWorldHint: true
3365
+ }
3366
+ },
3367
+ async ({ target }) => {
3368
+ const status = await statusLibrary2(target);
3369
+ return jsonResult({
3370
+ tool: "workspace_status",
3371
+ target,
3372
+ status: "ok",
3373
+ workspace: status.workspaceName,
3374
+ skills: status.skills,
3375
+ advisory: SESSION_START_ADVISORY
3376
+ });
3377
+ }
3378
+ );
3379
+ server.registerTool(
3380
+ "pull_workspace_library",
3381
+ {
3382
+ title: "Pull Workspace Library",
3383
+ description: "Use the authenticated CLI session to pull the whole workspace library into one target skill directory.",
3384
+ inputSchema: { target: targetSchema.default("codex") },
3385
+ annotations: {
3386
+ readOnlyHint: false,
3387
+ destructiveHint: false,
3388
+ idempotentHint: true,
3389
+ openWorldHint: true
3390
+ }
3391
+ },
3392
+ async ({ target }) => {
3393
+ const result = await pullLibrary2(target);
3394
+ return jsonResult({
3395
+ tool: "pull_workspace_library",
3396
+ target,
3397
+ status: "ok",
3398
+ skill_count: result.skillCount,
3399
+ install_dir: result.dir
3400
+ });
3401
+ }
3402
+ );
3403
+ return server;
3404
+ }
3405
+ async function mcpCommand() {
3406
+ const server = createMcpServer();
3407
+ const transport = new StdioServerTransport();
3408
+ await server.connect(transport);
3409
+ }
3410
+
3411
+ // src/commands/push.ts
3412
+ import { basename, join as join6, resolve as resolve2 } from "node:path";
3413
+ import { readdir as readdir3, readFile as readFile6, stat as stat4 } from "node:fs/promises";
3414
+ function parseConcurrency(value) {
3415
+ const raw = value ?? 6;
3416
+ const parsed = typeof raw === "number" ? raw : Number.parseInt(raw, 10);
3417
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 16) {
3418
+ throw new Error("--concurrency must be an integer from 1 to 16.");
3419
+ }
3420
+ return parsed;
3421
+ }
3422
+ async function hasSkillMarkdown(dir) {
3423
+ try {
3424
+ const skillMd = await stat4(join6(dir, "SKILL.md"));
3425
+ return skillMd.isFile();
3426
+ } catch {
3427
+ return false;
3428
+ }
3429
+ }
3430
+ async function findImmediateSkillDirs(root) {
3431
+ const entries = await readdir3(root, { withFileTypes: true });
3432
+ const dirs = entries.filter((entry) => entry.isDirectory()).map((entry) => join6(root, entry.name)).sort();
3433
+ const checks = await Promise.all(dirs.map(async (dir) => await hasSkillMarkdown(dir) ? dir : null));
3434
+ return checks.filter((dir) => Boolean(dir));
3435
+ }
3436
+ async function pushOneSkill(root, pushApi) {
3437
+ const dirStat = await stat4(root).catch(() => null);
3438
+ if (!dirStat || !dirStat.isDirectory()) {
3439
+ throw new Error(`Directory not found: ${root}`);
3440
+ }
3441
+ const bundle = await collectBundle(root);
3442
+ const skillMd = bundle.files.find((file) => file.relPath === "SKILL.md");
3443
+ if (!skillMd) throw new Error("SKILL.md is required.");
3444
+ const files = await Promise.all(bundle.files.map(async (file) => ({
3445
+ path: file.relPath,
3446
+ content_base64: (await readFile6(file.absPath)).toString("base64")
3447
+ })));
3448
+ return pushApi("/skills/push", {
3449
+ method: "POST",
3450
+ authRequired: true,
3451
+ body: {
3452
+ folder_name: basename(root),
3453
+ files
3454
+ }
3455
+ });
3456
+ }
3457
+ async function runBounded(items, concurrency, worker) {
3458
+ let next = 0;
3459
+ const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
3460
+ while (next < items.length) {
3461
+ const item = items[next];
3462
+ next += 1;
3463
+ await worker(item);
3464
+ }
3465
+ });
3466
+ await Promise.all(workers);
3467
+ }
3468
+ async function pushCommand(dir = ".", options = {}, deps = {}) {
3469
+ const cleanup = installCancellationHandler();
3470
+ try {
3471
+ const root = resolve2(dir);
3472
+ const dirStat = await stat4(root).catch(() => null);
3473
+ if (!dirStat || !dirStat.isDirectory()) {
3474
+ throw new Error(`Directory not found: ${dir}`);
3475
+ }
3476
+ const pushApi = deps.pushApi ?? api;
3477
+ if (await hasSkillMarkdown(root)) {
3478
+ const result = await pushOneSkill(root, pushApi);
3479
+ log.ok(`Pushed ${result.skill.slug} ${result.skill.latest.display}.`);
3480
+ return;
3481
+ }
3482
+ const skillDirs = await findImmediateSkillDirs(root);
3483
+ if (skillDirs.length === 0) throw new Error("SKILL.md is required.");
3484
+ const concurrency = parseConcurrency(options.concurrency);
3485
+ const startedAt = Date.now();
3486
+ const errors = [];
3487
+ let pushed = 0;
3488
+ await runBounded(skillDirs, concurrency, async (skillDir) => {
3489
+ const slug = basename(skillDir);
3490
+ try {
3491
+ const result = await pushOneSkill(skillDir, pushApi);
3492
+ pushed += 1;
3493
+ log.info(`\u2713 ${result.skill.slug} ${result.skill.latest.display}`);
3494
+ } catch (error) {
3495
+ errors.push({ slug, message: error.message });
3496
+ }
3497
+ });
3498
+ const elapsed = ((Date.now() - startedAt) / 1e3).toFixed(1).replace(/\.0$/, "");
3499
+ log.info(`Pushed ${pushed}/${skillDirs.length} skills in ${elapsed}s.`);
3500
+ if (errors.length > 0) {
3501
+ log.err("Push errors:");
3502
+ for (const error of errors) log.err(`- ${error.slug}: ${error.message}`);
3503
+ process.exitCode = 1;
3504
+ }
3505
+ } finally {
3506
+ cleanup.dispose();
3507
+ }
3508
+ }
3509
+
3510
+ // src/commands/delete.ts
3511
+ var defaultDeps = { api, log, readAuth };
3512
+ async function deleteCommand(slug, opts = {}, deps = defaultDeps) {
3513
+ if (!opts.yes) {
3514
+ const auth = await deps.readAuth();
3515
+ deps.log.info(`About to delete "${slug}" from ${auth?.handle ?? "your workspace"}. Re-run with --yes to confirm.`);
3516
+ process.exitCode = 1;
3517
+ return;
3518
+ }
3519
+ await deps.api(`/skills/${encodeURIComponent(slug)}`, {
3520
+ method: "DELETE",
3521
+ authRequired: true
3522
+ });
3523
+ deps.log.ok(`Deleted ${slug}.`);
3524
+ }
3525
+
3526
+ // src/commands/list.ts
3527
+ async function listCommand() {
3528
+ const result = await api("/skills", { authRequired: true });
3529
+ if (result.total === 0) {
3530
+ log.info("No skills in this workspace.");
3531
+ return;
3532
+ }
3533
+ for (const skill of result.skills) {
3534
+ log.info(`${skill.slug} ${skill.latest_version.display} ${skill.title}`);
3535
+ }
3536
+ }
3537
+
3538
+ // src/commands/rename-machine.ts
3539
+ import { readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
3540
+ import { join as join7 } from "node:path";
3541
+ import { homedir as homedir4 } from "node:os";
3542
+ var MACHINE_FILE2 = join7(homedir4(), ".floom", "machine.json");
3543
+ async function renameMachineCommand(newLabel, _opts) {
3544
+ const trimmed = newLabel.trim().slice(0, 80);
3545
+ if (!trimmed) {
3546
+ log.err('Label cannot be empty. Use: floom rename-machine "Office Server"');
3547
+ process.exitCode = 1;
3548
+ return;
3549
+ }
3550
+ const raw = await readFile7(MACHINE_FILE2, "utf8").catch(() => null);
3551
+ if (!raw) {
3552
+ log.err("No machine.json found. Run a `floom pull` first.");
3553
+ process.exitCode = 1;
3554
+ return;
3555
+ }
3556
+ let stored;
3557
+ try {
3558
+ stored = JSON.parse(raw);
3559
+ } catch {
3560
+ log.err("machine.json is malformed. Run a `floom pull` to reset it.");
3561
+ process.exitCode = 1;
3562
+ return;
3563
+ }
3564
+ stored["label"] = trimmed;
3565
+ await writeFile4(MACHINE_FILE2, JSON.stringify(stored, null, 2), { mode: 384 });
3566
+ log.ok(`Local machine label set to "${trimmed}".`);
3567
+ try {
3568
+ await api("/pull-state", {
3569
+ method: "PATCH",
3570
+ authRequired: true,
3571
+ body: { machine_id: stored["id"], machine_label: trimmed }
3572
+ });
3573
+ log.ok("Synced to your workspace.");
3574
+ } catch (e) {
3575
+ log.warn(`Couldn't push the rename to the server right now: ${e.message}`);
3576
+ log.warn("It will sync on your next pull.");
3577
+ }
3578
+ }
3579
+
3580
+ // src/index.ts
3581
+ async function logoutCommand() {
3582
+ const auth = await readAuth();
3583
+ if (auth) {
3584
+ try {
3585
+ await api("/cli/session/revoke", { method: "POST", authRequired: true });
3586
+ } catch (error) {
3587
+ log.warn(`Remote session revoke failed: ${error.message}`);
3588
+ }
3589
+ }
3590
+ await clearAuth();
3591
+ log.ok("Logged out.");
3592
+ }
3593
+ async function whoamiCommand() {
3594
+ const auth = await readAuth();
3595
+ if (!auth) {
3596
+ log.info("Not logged in. Run: npx -y @floomhq/skills-mvp login");
3597
+ process.exitCode = 1;
3598
+ return;
3599
+ }
3600
+ const rawAuth = await readRawAuth();
3601
+ if (rawAuth && !isTrustedApiUrl(rawAuth.apiUrl)) {
3602
+ log.warn(
3603
+ `Your CLI auth.json points at ${rawAuth.apiUrl}, which is not a trusted Floom API. Run 'npx -y @floomhq/skills-mvp login' to re-auth on try.floom.dev.`
3604
+ );
3605
+ }
3606
+ try {
3607
+ const me = await api("/me", { authRequired: true });
3608
+ log.heading("Logged in as:");
3609
+ log.kv("email", me.user.email ?? auth.email);
3610
+ if (me.workspace) {
3611
+ log.kv("workspace", `${me.workspace.name} (${me.workspace.slug})`);
3612
+ }
3613
+ log.kv("api url", auth.apiUrl);
3614
+ } catch (error) {
3615
+ if (error instanceof FloomError) {
3616
+ log.err(`${error.code}: ${error.message}`);
3617
+ } else {
3618
+ log.err(error.message);
3619
+ }
3620
+ log.warn("Local auth file exists but the server rejected this session. Run: npx -y @floomhq/skills-mvp login");
3621
+ process.exitCode = 1;
3622
+ }
3623
+ }
3624
+ var program = new Command();
3625
+ program.name("floom").description("Floom CLI \u2014 one shared skill library, pulled into the AI agent you choose.").version(VERSION);
3626
+ program.command("login").description("Log in via browser device flow.").action(loginCommand);
3627
+ program.command("logout").description("Clear local Floom auth.").action(logoutCommand);
3628
+ program.command("whoami").description("Show local auth state.").action(whoamiCommand);
3629
+ program.command("push [dir]").description("Push a skill folder.").option("--concurrency <n>", "Bulk push concurrency, 1-16", "6").action(pushCommand);
3630
+ program.command("delete <slug>").description("Delete a workspace skill.").option("--yes", "Skip confirmation").action((slug, opts) => deleteCommand(slug, opts));
3631
+ program.command("pull").description("Pull the whole workspace library.").option("--target <target>", "claude | codex | cursor | gemini | opencode").action(pullCommand);
3632
+ program.command("list").description("List workspace skills.").action(listCommand);
3633
+ program.command("status").description("Show local workspace sync status.").option("--target <target>", "claude | codex | cursor | gemini | opencode").action(statusCommand);
3634
+ program.command("mcp").description("Run the local MCP server over stdio.").action(mcpCommand);
3635
+ program.command("rename-machine <label>").description('Set the friendly name for THIS machine (e.g. "Office Server", "Travel Mac").').action(renameMachineCommand);
3636
+ async function main() {
3637
+ try {
3638
+ await program.parseAsync(process.argv);
3639
+ } catch (e) {
3640
+ if (e instanceof FloomError) {
3641
+ log.err(`${e.code}: ${e.message}`);
3642
+ process.exit(1);
3643
+ }
3644
+ log.err(e.message ?? "Unknown error");
3645
+ if (process.env.FLOOM_DEBUG) {
3646
+ const raw = e?.stack ?? String(e);
3647
+ const home = homedir5();
3648
+ const sanitized = raw.split(process.cwd()).join(".").split(home && home.length > 1 ? home : "\0").join("~");
3649
+ console.error(sanitized);
3650
+ }
3651
+ process.exit(1);
3652
+ }
3653
+ }
3654
+ main();
3655
+ /*! Bundled license information:
3656
+
3657
+ bcryptjs/dist/bcrypt.js:
3658
+ (**
3659
+ * @license bcrypt.js (c) 2013 Daniel Wirtz <dcode@dcode.io>
3660
+ * Released under the Apache License, Version 2.0
3661
+ * see: https://github.com/dcodeIO/bcrypt.js for details
3662
+ *)
3663
+ */