@deslop/workbench 0.0.286 → 0.0.291

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/client/assets/agent-CGU2xxEX.js +2 -0
  2. package/dist/client/assets/agent-CXAodtk6.js +1 -0
  3. package/dist/client/assets/browser-Big0jxfR.js +2 -0
  4. package/dist/client/assets/browser-DCBM0Y0g.js +1 -0
  5. package/dist/client/assets/button-DfUCUTxI.js +16 -0
  6. package/dist/client/assets/check-SxkDrjH-.js +1 -0
  7. package/dist/client/assets/composite-B4-E2IfK.js +1 -0
  8. package/dist/client/assets/dialog-DgrnySLb.js +153 -0
  9. package/dist/client/assets/diff-CQ3kp_ZY.js +109 -0
  10. package/dist/client/assets/diff-Ct5O0lZ0.js +2 -0
  11. package/dist/client/assets/fallbacks-3StR6PZ5.js +1 -0
  12. package/dist/client/assets/index-B0KpV0Yl.js +10 -0
  13. package/dist/client/assets/index-DzI5lMF1.css +2 -0
  14. package/dist/client/assets/info-DbWKjMtL.js +1 -0
  15. package/dist/client/assets/route-BlxnDll3.js +2 -0
  16. package/dist/client/assets/route-DVPqWkmA.js +46 -0
  17. package/dist/client/assets/run-BKul2xTf.js +1 -0
  18. package/dist/client/assets/run-DzoATG3-.js +2 -0
  19. package/dist/client/assets/state-BHj3zfmM.js +2 -0
  20. package/dist/client/assets/terminal-2Tn71jzR.js +2 -0
  21. package/dist/client/assets/terminal-B4yuHpYU.js +119 -0
  22. package/dist/client/assets/terminal-BB7jdQhY.js +1 -0
  23. package/dist/client/assets/triangle-alert-DBIRu_Yl.js +1 -0
  24. package/dist/client/index.html +12 -9
  25. package/dist/server.js +1057 -123
  26. package/package.json +2 -2
  27. package/dist/client/assets/browser-WO1SlWNU.js +0 -1
  28. package/dist/client/assets/button-D5PTso91.js +0 -1
  29. package/dist/client/assets/check-C1Q0x6Ev.js +0 -1
  30. package/dist/client/assets/composite-CV4TlLb7.js +0 -1
  31. package/dist/client/assets/dialog-CTqPdO7i.js +0 -153
  32. package/dist/client/assets/diff-A0Z4G6HR.js +0 -2
  33. package/dist/client/assets/diff-CIAXHFVs.js +0 -109
  34. package/dist/client/assets/fallbacks-Cvg98U3d.js +0 -2
  35. package/dist/client/assets/index-C_XQfQXO.js +0 -10
  36. package/dist/client/assets/index-DSI5FM62.css +0 -2
  37. package/dist/client/assets/info-C9lZ-VFp.js +0 -1
  38. package/dist/client/assets/route-CPP99S5h.js +0 -2
  39. package/dist/client/assets/route-Xh3KoGqC.js +0 -45
  40. package/dist/client/assets/state-CQDoh6vk.js +0 -2
  41. package/dist/client/assets/terminal-B-q7SW0v.js +0 -119
  42. package/dist/client/assets/terminal-RfiAqmoT.js +0 -2
  43. package/dist/client/assets/utils-BcciP3Sx.js +0 -16
package/dist/server.js CHANGED
@@ -12,12 +12,17 @@ import * as fs from "fs";
12
12
  import * as path from "path";
13
13
  import * as zlib from "zlib";
14
14
  import * as Crypto$1 from "node:crypto";
15
+ import { randomUUID } from "node:crypto";
15
16
  import * as NFS from "node:fs";
16
17
  import * as OS from "node:os";
17
18
  import * as Path from "node:path";
19
+ import { join } from "node:path";
18
20
  import * as NodeStreamP from "node:stream/promises";
19
21
  import { pipeline } from "node:stream/promises";
22
+ import { readFile } from "node:fs/promises";
20
23
  import * as readline from "node:readline";
24
+ import nodeProcess from "node:process";
25
+ import * as nodePty from "@lydell/node-pty";
21
26
  //#region \0rolldown/runtime.js
22
27
  var __create = Object.create;
23
28
  var __defProp = Object.defineProperty;
@@ -5437,7 +5442,43 @@ const flatMap$5 = /*#__PURE__*/ dual(2, (self, f) => isNone(self) ? none() : f(s
5437
5442
  * @category filtering
5438
5443
  * @since 2.0.0
5439
5444
  */
5440
- const filter$2 = /*#__PURE__*/ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
5445
+ const filter$3 = /*#__PURE__*/ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
5446
+ /**
5447
+ * Lifts a `Predicate` or `Refinement` into the `Option` context: returns
5448
+ * `Some(value)` when the predicate holds, `None` otherwise.
5449
+ *
5450
+ * **When to use**
5451
+ *
5452
+ * Use to convert a boolean check into an `Option`-returning function
5453
+ * - Validating input and wrapping it in `Option`
5454
+ *
5455
+ * **Details**
5456
+ *
5457
+ * - `predicate(value)` is `true` → `Some(value)`
5458
+ * - `predicate(value)` is `false` → `None`
5459
+ * - Supports refinements for type narrowing
5460
+ *
5461
+ * **Example** (Validating positive numbers)
5462
+ *
5463
+ * ```ts
5464
+ * import { Option } from "effect"
5465
+ *
5466
+ * const parsePositive = Option.liftPredicate((n: number) => n > 0)
5467
+ *
5468
+ * console.log(parsePositive(1))
5469
+ * // Output: { _id: 'Option', _tag: 'Some', value: 1 }
5470
+ *
5471
+ * console.log(parsePositive(-1))
5472
+ * // Output: { _id: 'Option', _tag: 'None' }
5473
+ * ```
5474
+ *
5475
+ * @see {@link filter} to apply a predicate to an existing `Option`
5476
+ * @see {@link toRefinement} for the inverse direction
5477
+ *
5478
+ * @category lifting
5479
+ * @since 2.0.0
5480
+ */
5481
+ const liftPredicate = /*#__PURE__*/ dual(2, (b, predicate) => predicate(b) ? some(b) : none());
5441
5482
  //#endregion
5442
5483
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.74/node_modules/effect/dist/Result.js
5443
5484
  /**
@@ -6713,6 +6754,43 @@ const getSuccesses = (self) => {
6713
6754
  return out;
6714
6755
  };
6715
6756
  /**
6757
+ * Keeps transformed values for elements where a `Filter` succeeds.
6758
+ *
6759
+ * **When to use**
6760
+ *
6761
+ * Use to transform elements with a `Result`-returning filter while discarding
6762
+ * failures.
6763
+ *
6764
+ * **Details**
6765
+ *
6766
+ * - The filter receives `(element, index)`.
6767
+ * - Failures are discarded.
6768
+ *
6769
+ * **Example** (Filter and transform)
6770
+ *
6771
+ * ```ts
6772
+ * import { Array, Result } from "effect"
6773
+ *
6774
+ * console.log(Array.filterMap([1, 2, 3, 4], (n) => n % 2 === 0 ? Result.succeed(n * 10) : Result.failVoid))
6775
+ * // [20, 40]
6776
+ * ```
6777
+ *
6778
+ * @see {@link filter} — keep original elements matching a predicate
6779
+ * @see {@link partition} for keeping both failures and successes
6780
+ *
6781
+ * @category filtering
6782
+ * @since 2.0.0
6783
+ */
6784
+ const filterMap = /*#__PURE__*/ dual(2, (self, f) => {
6785
+ const as = fromIterable$2(self);
6786
+ const out = [];
6787
+ for (let i = 0; i < as.length; i++) {
6788
+ const result = f(as[i], i);
6789
+ if (isSuccess$1(result)) out.push(result.success);
6790
+ }
6791
+ return out;
6792
+ });
6793
+ /**
6716
6794
  * Keeps only elements satisfying a predicate (or refinement).
6717
6795
  *
6718
6796
  * **When to use**
@@ -6738,7 +6816,7 @@ const getSuccesses = (self) => {
6738
6816
  * @category filtering
6739
6817
  * @since 2.0.0
6740
6818
  */
6741
- const filter$1 = /*#__PURE__*/ dual(2, (self, predicate) => {
6819
+ const filter$2 = /*#__PURE__*/ dual(2, (self, predicate) => {
6742
6820
  const as = fromIterable$2(self);
6743
6821
  const out = [];
6744
6822
  for (let i = 0; i < as.length; i++) if (predicate(as[i], i)) out.push(as[i]);
@@ -6887,7 +6965,7 @@ const dedupe = (self) => dedupeWith(self, asEquivalence());
6887
6965
  * @category folding
6888
6966
  * @since 2.0.0
6889
6967
  */
6890
- const join$2 = /*#__PURE__*/ dual(2, (self, sep) => fromIterable$2(self).join(sep));
6968
+ const join$3 = /*#__PURE__*/ dual(2, (self, sep) => fromIterable$2(self).join(sep));
6891
6969
  //#endregion
6892
6970
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.74/node_modules/effect/dist/Effectable.js
6893
6971
  /**
@@ -13794,6 +13872,76 @@ const endsWith = (searchString, position) => (self) => self.endsWith(searchStrin
13794
13872
  * @since 2.0.0
13795
13873
  */
13796
13874
  const replaceAll = (searchValue, replaceValue) => (self) => self.replaceAll(searchValue, replaceValue);
13875
+ const CR = 13;
13876
+ const LF = 10;
13877
+ /**
13878
+ * Returns an `IterableIterator` which yields each line contained within the
13879
+ * string, trimming off the trailing newline character.
13880
+ *
13881
+ * **Example** (Iterating lines without separators)
13882
+ *
13883
+ * ```ts
13884
+ * import { String } from "effect"
13885
+ *
13886
+ * const lines = String.linesIterator("hello\nworld\n")
13887
+ * console.log(Array.from(lines)) // ["hello", "world"]
13888
+ * ```
13889
+ *
13890
+ * @category splitting
13891
+ * @since 2.0.0
13892
+ */
13893
+ const linesIterator = (self) => linesSeparated(self, true);
13894
+ var LinesIterator = class LinesIterator {
13895
+ index;
13896
+ length;
13897
+ s;
13898
+ stripped;
13899
+ constructor(s, stripped = false) {
13900
+ this.s = s;
13901
+ this.stripped = stripped;
13902
+ this.index = 0;
13903
+ this.length = s.length;
13904
+ }
13905
+ next() {
13906
+ if (this.done) return {
13907
+ done: true,
13908
+ value: void 0
13909
+ };
13910
+ const start = this.index;
13911
+ while (!this.done && !isLineBreak(this.s[this.index])) this.index = this.index + 1;
13912
+ let end = this.index;
13913
+ if (!this.done) {
13914
+ const char = this.s[this.index];
13915
+ this.index = this.index + 1;
13916
+ if (!this.done && isLineBreak2(char, this.s[this.index])) this.index = this.index + 1;
13917
+ if (!this.stripped) end = this.index;
13918
+ }
13919
+ return {
13920
+ done: false,
13921
+ value: this.s.substring(start, end)
13922
+ };
13923
+ }
13924
+ [Symbol.iterator]() {
13925
+ return new LinesIterator(this.s, this.stripped);
13926
+ }
13927
+ get done() {
13928
+ return this.index >= this.length;
13929
+ }
13930
+ };
13931
+ /**
13932
+ * Checks whether the provided character is a line break character (i.e. either `"\r"`
13933
+ * or `"\n"`).
13934
+ */
13935
+ const isLineBreak = (char) => {
13936
+ const code = char.charCodeAt(0);
13937
+ return code === CR || code === LF;
13938
+ };
13939
+ /**
13940
+ * Checks whether the provided characters combine to form a carriage return/line-feed
13941
+ * (i.e. `"\r\n"`).
13942
+ */
13943
+ const isLineBreak2 = (char0, char1) => char0.charCodeAt(0) === CR && char1.charCodeAt(0) === LF;
13944
+ const linesSeparated = (self, stripped) => new LinesIterator(self, stripped);
13797
13945
  //#endregion
13798
13946
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.74/node_modules/effect/dist/internal/random.js
13799
13947
  /** @internal */
@@ -19650,7 +19798,7 @@ function transformOptional(f) {
19650
19798
  */
19651
19799
  function withDefault$1(defaultValue) {
19652
19800
  return new Getter((o) => {
19653
- const filtered = filter$2(o, isNotUndefined);
19801
+ const filtered = filter$3(o, isNotUndefined);
19654
19802
  return isSome(filtered) ? succeed$3(filtered) : mapEager(defaultValue, some);
19655
19803
  });
19656
19804
  }
@@ -20724,7 +20872,7 @@ var Declaration = class Declaration extends Base {
20724
20872
  * @category models
20725
20873
  * @since 4.0.0
20726
20874
  */
20727
- var Null = class extends Base {
20875
+ var Null$1 = class extends Base {
20728
20876
  _tag = "Null";
20729
20877
  /** @internal */
20730
20878
  getParser() {
@@ -20735,7 +20883,7 @@ var Null = class extends Base {
20735
20883
  return "null";
20736
20884
  }
20737
20885
  };
20738
- const null_ = /*#__PURE__*/ new Null();
20886
+ const null_ = /*#__PURE__*/ new Null$1();
20739
20887
  /**
20740
20888
  * AST node matching the `undefined` value.
20741
20889
  *
@@ -22552,7 +22700,7 @@ const badArgument = (options) => new PlatformError(new BadArgument(options));
22552
22700
  * @category combinators
22553
22701
  * @since 2.0.0
22554
22702
  */
22555
- const join$1 = fiberJoin;
22703
+ const join$2 = fiberJoin;
22556
22704
  /**
22557
22705
  * Interrupts a fiber, causing it to stop executing and clean up any
22558
22706
  * acquired resources.
@@ -23154,7 +23302,7 @@ const takeAll$2 = (self) => takeN(self, self.length);
23154
23302
  * @category elements
23155
23303
  * @since 4.0.0
23156
23304
  */
23157
- const take$2 = (self) => {
23305
+ const take$3 = (self) => {
23158
23306
  if (!self.head) return Empty$2;
23159
23307
  const message = self.head.array[self.head.offset];
23160
23308
  if (self.head.mutable) self.head.array[self.head.offset] = void 0;
@@ -23208,7 +23356,7 @@ const take$2 = (self) => {
23208
23356
  * @category mutations
23209
23357
  * @since 4.0.0
23210
23358
  */
23211
- const filter = (self, f) => {
23359
+ const filter$1 = (self, f) => {
23212
23360
  const array = [];
23213
23361
  let chunk = self.head;
23214
23362
  while (chunk) {
@@ -23274,7 +23422,7 @@ const filter = (self, f) => {
23274
23422
  * @category mutations
23275
23423
  * @since 4.0.0
23276
23424
  */
23277
- const remove$3 = (self, value) => filter(self, (v) => v !== value);
23425
+ const remove$3 = (self, value) => filter$1(self, (v) => v !== value);
23278
23426
  //#endregion
23279
23427
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.74/node_modules/effect/dist/MutableRef.js
23280
23428
  const TypeId$36 = "~effect/MutableRef";
@@ -23856,7 +24004,7 @@ const unsubscribe = (self) => uninterruptible(withFiber((state) => {
23856
24004
  * @category subscriptions
23857
24005
  * @since 4.0.0
23858
24006
  */
23859
- const take$1 = (self) => suspend$2(() => {
24007
+ const take$2 = (self) => suspend$2(() => {
23860
24008
  if (self.shutdownFlag.current) return interrupt$1;
23861
24009
  if (self.replayWindow.remaining > 0) return succeed$3(self.replayWindow.take());
23862
24010
  const message = self.pollers.length === 0 ? self.subscription.poll() : Empty$2;
@@ -24602,7 +24750,7 @@ var BackPressureStrategy = class {
24602
24750
  onPubSubEmptySpaceUnsafe(pubsub, subscribers) {
24603
24751
  let keepPolling = true;
24604
24752
  while (keepPolling && !pubsub.isFull()) {
24605
- const publisher = take$2(this.publishers);
24753
+ const publisher = take$3(this.publishers);
24606
24754
  if (publisher === Empty$2) keepPolling = false;
24607
24755
  else {
24608
24756
  const [value, deferred] = publisher;
@@ -24641,7 +24789,7 @@ var BackPressureStrategy = class {
24641
24789
  }
24642
24790
  }
24643
24791
  removeUnsafe(deferred) {
24644
- filter(this.publishers, ([_, d]) => d !== deferred);
24792
+ filter$1(this.publishers, ([_, d]) => d !== deferred);
24645
24793
  }
24646
24794
  };
24647
24795
  /**
@@ -24714,7 +24862,7 @@ var DroppingStrategy = class {
24714
24862
  const strategyCompletePollersUnsafe = (strategy, pubsub, subscribers, subscription, pollers) => {
24715
24863
  let keepPolling = true;
24716
24864
  while (keepPolling && !subscription.isEmpty()) {
24717
- const poller = take$2(pollers);
24865
+ const poller = take$3(pollers);
24718
24866
  if (poller === Empty$2) {
24719
24867
  removeSubscribers(subscribers, subscription, pollers);
24720
24868
  if (pollers.length === 0) keepPolling = false;
@@ -25063,7 +25211,7 @@ const offer = (self, message) => suspend$3(() => {
25063
25211
  }
25064
25212
  return offerRemainingSingle(self, message);
25065
25213
  case "sliding":
25066
- take$2(self.messages);
25214
+ take$3(self.messages);
25067
25215
  append(self.messages, message);
25068
25216
  return exitTrue;
25069
25217
  }
@@ -25106,7 +25254,7 @@ const offerUnsafe = (self, message) => {
25106
25254
  if (self.state._tag !== "Open") return false;
25107
25255
  else if (self.messages.length >= self.capacity) {
25108
25256
  if (self.strategy === "sliding") {
25109
- take$2(self.messages);
25257
+ take$3(self.messages);
25110
25258
  append(self.messages, message);
25111
25259
  return true;
25112
25260
  } else if (self.capacity <= 0 && self.state.takers.size > 0) {
@@ -25469,7 +25617,7 @@ const takeBetween = (self, min, max) => suspend$3(() => takeBetweenUnsafe(self,
25469
25617
  * @category taking
25470
25618
  * @since 2.0.0
25471
25619
  */
25472
- const take = (self) => suspend$3(() => takeUnsafe(self) ?? andThen$1(awaitTake(self), take(self)));
25620
+ const take$1 = (self) => suspend$3(() => takeUnsafe(self) ?? andThen$1(awaitTake(self), take$1(self)));
25473
25621
  /**
25474
25622
  * Attempts to take one message from the queue synchronously.
25475
25623
  *
@@ -25511,14 +25659,14 @@ const take = (self) => suspend$3(() => takeUnsafe(self) ?? andThen$1(awaitTake(s
25511
25659
  const takeUnsafe = (self) => {
25512
25660
  if (self.state._tag === "Done") return self.state.exit;
25513
25661
  if (self.messages.length > 0) {
25514
- const message = take$2(self.messages);
25662
+ const message = take$3(self.messages);
25515
25663
  releaseCapacity(self);
25516
25664
  return exitSucceed(message);
25517
25665
  } else if (self.capacity <= 0 && self.state.offers.size > 0) {
25518
25666
  self.capacity = 1;
25519
25667
  releaseCapacity(self);
25520
25668
  self.capacity = 0;
25521
- const message = take$2(self.messages);
25669
+ const message = take$3(self.messages);
25522
25670
  releaseCapacity(self);
25523
25671
  return exitSucceed(message);
25524
25672
  }
@@ -25590,7 +25738,7 @@ const takeBetweenUnsafe = (self, min, max) => {
25590
25738
  self.capacity = 1;
25591
25739
  releaseCapacity(self);
25592
25740
  self.capacity = 0;
25593
- const messages = [take$2(self.messages)];
25741
+ const messages = [take$3(self.messages)];
25594
25742
  releaseCapacity(self);
25595
25743
  return exitSucceed(messages);
25596
25744
  }
@@ -26749,7 +26897,7 @@ const mapEffectConcurrent = (self, f, options) => fromTransformBracket(fnUntrace
26749
26897
  } else {
26750
26898
  const effects = yield* bounded(concurrencyN - 2);
26751
26899
  yield* addFinalizer$1(forkedScope, shutdown(queue));
26752
- yield* take(effects).pipe(flatten$1, flatMap$2((value) => offer(queue, value)), forever({ disableYield: true }), catchCause$1((cause) => failCause$1(queue, cause)), forkIn(forkedScope));
26900
+ yield* take$1(effects).pipe(flatten$1, flatMap$2((value) => offer(queue, value)), forever({ disableYield: true }), catchCause$1((cause) => failCause$1(queue, cause)), forkIn(forkedScope));
26753
26901
  let errorCause;
26754
26902
  const onExit = (exit) => {
26755
26903
  if (exit._tag === "Success") return;
@@ -26761,10 +26909,10 @@ const mapEffectConcurrent = (self, f, options) => fromTransformBracket(fnUntrace
26761
26909
  const fiber = runFork(f(value, i++));
26762
26910
  trackFiber(fiber);
26763
26911
  fiber.addObserver(onExit);
26764
- return offer(effects, join$1(fiber));
26912
+ return offer(effects, join$2(fiber));
26765
26913
  }), forever({ disableYield: true }), catchCause$1((cause) => offer(effects, failCause$3(cause)).pipe(andThen(failCause$1(effects, cause)))), forkIn(forkedScope));
26766
26914
  }
26767
- return take(queue);
26915
+ return take$1(queue);
26768
26916
  }));
26769
26917
  /**
26770
26918
  * Maps each output element to a channel and flattens the child channel
@@ -26874,6 +27022,47 @@ const flattenArray = (self) => transformPull$1(self, (pull) => {
26874
27022
  }));
26875
27023
  });
26876
27024
  /**
27025
+ * Filters arrays of elements emitted by a channel, applying the filter
27026
+ * to each element within the arrays and only emitting non-empty filtered arrays.
27027
+ *
27028
+ * **Example** (Filtering array output)
27029
+ *
27030
+ * ```ts
27031
+ * import { Array, Channel } from "effect"
27032
+ *
27033
+ * const nonEmptyArrayPredicate = Array.isReadonlyArrayNonEmpty
27034
+ *
27035
+ * // Create a channel that outputs arrays of mixed data
27036
+ * const arrayChannel = Channel.fromIterable([
27037
+ * Array.make(1, 2, 3, 4, 5),
27038
+ * Array.make(6, 7, 8, 9, 10),
27039
+ * Array.make(11, 12, 13, 14, 15)
27040
+ * ]).pipe(Channel.filter(nonEmptyArrayPredicate))
27041
+ *
27042
+ * // Filter arrays to keep only even numbers
27043
+ * const evenArraysChannel = Channel.filterArray(arrayChannel, (n) => n % 2 === 0)
27044
+ * // Outputs: [2, 4], [6, 8, 10], [12, 14]
27045
+ * // Note: Only non-empty filtered arrays are emitted
27046
+ *
27047
+ * // Arrays that would become empty after filtering are discarded entirely
27048
+ * const oddChannel = Channel.fromIterable([
27049
+ * Array.make(1, 3, 5),
27050
+ * Array.make(2, 4),
27051
+ * Array.make(7, 9)
27052
+ * ]).pipe(Channel.filter(nonEmptyArrayPredicate))
27053
+ * const filteredOddChannel = Channel.filterArray(oddChannel, (n) => n % 2 === 0)
27054
+ * // Outputs: [2, 4] (the arrays [1,3,5] and [7,9] are discarded)
27055
+ * ```
27056
+ *
27057
+ * @category filtering
27058
+ * @since 4.0.0
27059
+ */
27060
+ const filterArray = /*#__PURE__*/ dual(2, (self, predicate) => transformPull$1(self, (pull) => succeed$3(flatMap$2(pull, function loop(arr) {
27061
+ const passes = [];
27062
+ for (let i = 0; i < arr.length; i++) if (predicate(arr[i])) passes.push(arr[i]);
27063
+ return isReadonlyArrayNonEmpty(passes) ? succeed$3(passes) : flatMap$2(pull, loop);
27064
+ }))));
27065
+ /**
26877
27066
  * Catches any cause of failure from the channel and allows recovery by
26878
27067
  * creating a new channel based on the caught cause.
26879
27068
  *
@@ -27042,7 +27231,7 @@ const mergeAll = /*#__PURE__*/ dual(2, (channels, { bufferSize = 16, concurrency
27042
27231
  fibers.add(fiber);
27043
27232
  }
27044
27233
  }).pipe(catchCause$1((cause) => doneLatch.whenOpen(failCause$1(queue, cause))), forkIn(forkedScope));
27045
- return take(queue);
27234
+ return take$1(queue);
27046
27235
  })));
27047
27236
  /**
27048
27237
  * Returns a new channel, which is the merge of this channel and the specified
@@ -27091,7 +27280,7 @@ const merge$2 = /*#__PURE__*/ dual((args) => isChannel(args[0]) && isChannel(arg
27091
27280
  const runSide = (side, channel, scope) => toTransform(channel)(upstream, scope).pipe(flatMap$2((pull) => pull.pipe(flatMap$2((value) => offer(queue, value)), forever)), onError((cause) => andThen(close(scope, doneExitFromCause(cause)), onExit(side, cause))), forkIn(forkedScope));
27092
27281
  yield* runSide("left", left, forkUnsafe(forkedScope));
27093
27282
  yield* runSide("right", right, forkUnsafe(forkedScope));
27094
- return take(queue);
27283
+ return take$1(queue);
27095
27284
  })));
27096
27285
  /**
27097
27286
  * Splits upstream string chunks into lines, recognizing `\n`, `\r\n`, and
@@ -27331,6 +27520,35 @@ const runWith = (self, f, onHalt) => suspend$2(() => {
27331
27520
  */
27332
27521
  const provideContext$1 = /*#__PURE__*/ dual(2, (self, context) => fromTransform$1((upstream, scope) => map$4(provideContext$2(toTransform(self)(upstream, scope), context), provideContext$2(context))));
27333
27522
  /**
27523
+ * Runs a channel and discards all output elements, returning only the final result.
27524
+ *
27525
+ * **Example** (Draining channel output at runtime)
27526
+ *
27527
+ * ```ts
27528
+ * import { Channel, Data } from "effect"
27529
+ *
27530
+ * class DrainError extends Data.TaggedError("DrainError")<{
27531
+ * readonly stage: string
27532
+ * }> {}
27533
+ *
27534
+ * // Create a channel that outputs elements and completes with a result
27535
+ * const resultChannel = Channel.fromIterable([1, 2, 3])
27536
+ * const completedChannel = Channel.concatWith(
27537
+ * resultChannel,
27538
+ * () => Channel.succeed("completed")
27539
+ * )
27540
+ *
27541
+ * // Drain all elements and get only the final result
27542
+ * const drainEffect = Channel.runDrain(completedChannel)
27543
+ *
27544
+ * // Effect.runSync(drainEffect) // Returns: "completed"
27545
+ * ```
27546
+ *
27547
+ * @category execution
27548
+ * @since 2.0.0
27549
+ */
27550
+ const runDrain$1 = (self) => runWith(self, (pull) => forever(pull, { disableYield: true }));
27551
+ /**
27334
27552
  * Runs a channel and applies an effect to each output element.
27335
27553
  *
27336
27554
  * **Example** (Running effects for each output)
@@ -29155,6 +29373,30 @@ const concat = /*#__PURE__*/ dual(2, (self, that) => flatten(fromArray([self, th
29155
29373
  * @since 2.0.0
29156
29374
  */
29157
29375
  const merge$1 = /*#__PURE__*/ dual((args) => isStream(args[0]) && isStream(args[1]), (self, that, options) => fromChannel(merge$2(toChannel(self), toChannel(that), options)));
29376
+ /**
29377
+ * Filters a stream to the elements that satisfy a predicate.
29378
+ *
29379
+ * **Example** (Filtering stream values)
29380
+ *
29381
+ * ```ts
29382
+ * import { Console, Effect, Stream } from "effect"
29383
+ *
29384
+ * const program = Effect.gen(function*() {
29385
+ * const stream = Stream.make(1, 2, 3, 4).pipe(
29386
+ * Stream.filter((n) => n % 2 === 0)
29387
+ * )
29388
+ * const values = yield* Stream.runCollect(stream)
29389
+ * yield* Console.log(values)
29390
+ * })
29391
+ *
29392
+ * Effect.runPromise(program)
29393
+ * // Output: [ 2, 4 ]
29394
+ * ```
29395
+ *
29396
+ * @category filtering
29397
+ * @since 2.0.0
29398
+ */
29399
+ const filter = /*#__PURE__*/ dual(2, (self, predicate) => fromChannel(filterArray(toChannel(self), predicate)));
29158
29400
  const catch_ = /*#__PURE__*/ dual(2, (self, f) => fromChannel(catch_$1(self.channel, (error) => f(error).channel)));
29159
29401
  /**
29160
29402
  * Turns typed failures into defects, making the stream infallible.
@@ -29182,6 +29424,77 @@ const catch_ = /*#__PURE__*/ dual(2, (self, f) => fromChannel(catch_$1(self.chan
29182
29424
  */
29183
29425
  const orDie = (self) => fromChannel(orDie$1(self.channel));
29184
29426
  /**
29427
+ * Takes the first `n` elements from this stream, returning `Stream.empty` when `n < 1`.
29428
+ *
29429
+ * **Example** (Taking values from the left)
29430
+ *
29431
+ * ```ts
29432
+ * import { Console, Effect, Stream } from "effect"
29433
+ *
29434
+ * const program = Effect.gen(function*() {
29435
+ * const values = yield* Stream.make(1, 2, 3, 4, 5).pipe(
29436
+ * Stream.take(3),
29437
+ * Stream.runCollect
29438
+ * )
29439
+ * yield* Console.log(values)
29440
+ * })
29441
+ *
29442
+ * Effect.runPromise(program)
29443
+ * // Output: [ 1, 2, 3 ]
29444
+ * ```
29445
+ *
29446
+ * @category filtering
29447
+ * @since 2.0.0
29448
+ */
29449
+ const take = /*#__PURE__*/ dual(2, (self, n) => n < 1 ? empty$6 : takeUntil(self, (_, i) => i === n - 1));
29450
+ /**
29451
+ * Takes elements until the predicate matches.
29452
+ *
29453
+ * **Details**
29454
+ *
29455
+ * When `excludeLast` is `true`, the matching element is dropped.
29456
+ *
29457
+ * **Example** (Taking until a predicate matches)
29458
+ *
29459
+ * ```ts
29460
+ * import { Console, Effect, Stream } from "effect"
29461
+ *
29462
+ * const stream = Stream.range(1, 5)
29463
+ *
29464
+ * const program = Effect.gen(function*() {
29465
+ * const inclusive = yield* stream.pipe(
29466
+ * Stream.takeUntil((n) => n % 3 === 0),
29467
+ * Stream.runCollect
29468
+ * )
29469
+ * yield* Console.log(inclusive)
29470
+ * // Output: [ 1, 2, 3 ]
29471
+ *
29472
+ * const exclusive = yield* stream.pipe(
29473
+ * Stream.takeUntil((n) => n % 3 === 0, { excludeLast: true }),
29474
+ * Stream.runCollect
29475
+ * )
29476
+ * yield* Console.log(exclusive)
29477
+ * // Output: [ 1, 2 ]
29478
+ * })
29479
+ * ```
29480
+ *
29481
+ * @category filtering
29482
+ * @since 2.0.0
29483
+ */
29484
+ const takeUntil = /*#__PURE__*/ dual((args) => isStream(args[0]), (self, predicate, options) => transformPull(self, (pull, _scope) => sync(() => {
29485
+ let i = 0;
29486
+ let done$9 = false;
29487
+ return flatMap$2(suspend$2(() => done$9 ? done() : pull), (chunk) => {
29488
+ const index = chunk.findIndex((a) => predicate(a, i++));
29489
+ if (index >= 0) {
29490
+ done$9 = true;
29491
+ const arr = chunk.slice(0, options?.excludeLast ? index : index + 1);
29492
+ return isReadonlyArrayNonEmpty(arr) ? succeed$3(arr) : done();
29493
+ }
29494
+ return succeed$3(chunk);
29495
+ });
29496
+ })));
29497
+ /**
29185
29498
  * Drops the first `n` elements from this stream.
29186
29499
  *
29187
29500
  * **Example** (Dropping values from the left)
@@ -29589,6 +29902,32 @@ const runForEach = /*#__PURE__*/ dual(2, (self, f) => runForEach$1(self.channel,
29589
29902
  */
29590
29903
  const runForEachArray = /*#__PURE__*/ dual(2, (self, f) => runForEach$1(self.channel, f));
29591
29904
  /**
29905
+ * Runs the stream for its effects, discarding emitted elements.
29906
+ *
29907
+ * **Example** (Draining a stream run)
29908
+ *
29909
+ * ```ts
29910
+ * import { Console, Effect, Stream } from "effect"
29911
+ *
29912
+ * const program = Effect.gen(function*() {
29913
+ * const stream = Stream.make(1, 2, 3).pipe(
29914
+ * Stream.mapEffect((n) => Console.log(`Processing: ${n}`))
29915
+ * )
29916
+ *
29917
+ * yield* Stream.runDrain(stream)
29918
+ * })
29919
+ *
29920
+ * Effect.runPromise(program)
29921
+ * // Processing: 1
29922
+ * // Processing: 2
29923
+ * // Processing: 3
29924
+ * ```
29925
+ *
29926
+ * @category destructors
29927
+ * @since 2.0.0
29928
+ */
29929
+ const runDrain = (self) => runDrain$1(self.channel);
29930
+ /**
29592
29931
  * Concatenates all emitted strings into a single string.
29593
29932
  *
29594
29933
  * **Example** (Joining strings from a stream)
@@ -31295,7 +31634,7 @@ const set$2 = /*#__PURE__*/ dual(2, (self, value) => sync(() => set$4(self.ref,
31295
31634
  * @category setters
31296
31635
  * @since 2.0.0
31297
31636
  */
31298
- const update = /*#__PURE__*/ dual(2, (self, f) => sync(() => {
31637
+ const update$1 = /*#__PURE__*/ dual(2, (self, f) => sync(() => {
31299
31638
  self.ref.current = f(self.ref.current);
31300
31639
  }));
31301
31640
  /**
@@ -31551,6 +31890,29 @@ function decodeUnknownEffect$1(schema, options) {
31551
31890
  return options === void 0 ? parser : (input, overrideOptions) => parser(input, mergeParseOptions(options, overrideOptions));
31552
31891
  }
31553
31892
  /**
31893
+ * Creates a synchronous decoder for `unknown` input.
31894
+ *
31895
+ * **When to use**
31896
+ *
31897
+ * Use to decode untrusted or dynamically typed input at a synchronous boundary
31898
+ * where invalid data should be reported by throwing.
31899
+ *
31900
+ * **Details**
31901
+ *
31902
+ * The returned function returns the decoded `Type` on success and throws an
31903
+ * `Error` with the `SchemaIssue.Issue` in its `cause` on decoding failure.
31904
+ *
31905
+ * @see {@link decodeSync} for input already typed as the schema's `Encoded` type
31906
+ * @see {@link decodeUnknownEffect} for preserving decoding failures in `Effect`
31907
+ * @see {@link decodeUnknownResult} for returning schema issues as data
31908
+ *
31909
+ * @category decoding
31910
+ * @since 3.10.0
31911
+ */
31912
+ function decodeUnknownSync$1(schema, options) {
31913
+ return asSync(decodeUnknownEffect$1(schema, options));
31914
+ }
31915
+ /**
31554
31916
  * Creates an effectful encoder for `unknown` input.
31555
31917
  *
31556
31918
  * **When to use**
@@ -31906,6 +32268,44 @@ function decodeUnknownEffect(schema, options) {
31906
32268
  };
31907
32269
  }
31908
32270
  /**
32271
+ * Decodes an `unknown` input against a schema synchronously, returning the
32272
+ * decoded value or throwing an `Error` whose cause contains the schema issue.
32273
+ *
32274
+ * **When to use**
32275
+ *
32276
+ * Use when validating unknown data at a boundary and treating schema mismatches
32277
+ * as exceptions.
32278
+ *
32279
+ * **Details**
32280
+ *
32281
+ * For typed input use `decodeSync`.
32282
+ * Only service-free schemas can be decoded synchronously. For non-throwing
32283
+ * alternatives see `decodeUnknownOption`, `decodeUnknownExit`, or
32284
+ * `decodeUnknownEffect`. Options may be provided either when creating the
32285
+ * decoder or when applying it; application options override creation options.
32286
+ *
32287
+ * **Example** (Decoding with a transformation schema)
32288
+ *
32289
+ * ```ts
32290
+ * import { Schema } from "effect"
32291
+ *
32292
+ * const NumberFromString = Schema.NumberFromString
32293
+ *
32294
+ * console.log(Schema.decodeUnknownSync(NumberFromString)("42"))
32295
+ * // Output: 42
32296
+ *
32297
+ * Schema.decodeUnknownSync(NumberFromString)("not a number")
32298
+ * // throws SchemaError: NumberFromString
32299
+ * // └─ Encoded side transformation failure
32300
+ * // └─ NumberFromString
32301
+ * // └─ Expected a numeric string, actual "not a number"
32302
+ * ```
32303
+ *
32304
+ * @category decoding
32305
+ * @since 4.0.0
32306
+ */
32307
+ const decodeUnknownSync = decodeUnknownSync$1;
32308
+ /**
31909
32309
  * Encodes an `unknown` input against a schema, returning an `Effect` that
31910
32310
  * succeeds with the encoded value or fails with a {@link SchemaError}.
31911
32311
  *
@@ -32093,6 +32493,14 @@ const Any = /*#__PURE__*/ make$34(any);
32093
32493
  */
32094
32494
  const Unknown = /*#__PURE__*/ make$34(unknown);
32095
32495
  /**
32496
+ * Schema for the `null` literal. Validates that the input is strictly `null`.
32497
+ *
32498
+ * @see {@link NullOr} for a union with another schema.
32499
+ * @category schemas
32500
+ * @since 3.10.0
32501
+ */
32502
+ const Null = /*#__PURE__*/ make$34(null_);
32503
+ /**
32096
32504
  * Schema for the `undefined` literal. Validates that the input is strictly `undefined`.
32097
32505
  *
32098
32506
  * @see {@link UndefinedOr} for a union with another schema.
@@ -32270,6 +32678,13 @@ function Literals(literals) {
32270
32678
  });
32271
32679
  }
32272
32680
  /**
32681
+ * Creates a union schema of `S | null`.
32682
+ *
32683
+ * @category constructors
32684
+ * @since 3.10.0
32685
+ */
32686
+ const NullOr = /*#__PURE__*/ lambda((self) => Union([self, Null]));
32687
+ /**
32273
32688
  * Creates a union schema of `S | undefined`.
32274
32689
  *
32275
32690
  * @category constructors
@@ -36698,7 +37113,7 @@ const runtime = (self) => () => map$4(context$1(), (services) => {
36698
37113
  * @category combinators
36699
37114
  * @since 2.0.0
36700
37115
  */
36701
- const join = (self) => _await(self.deferred);
37116
+ const join$1 = (self) => _await(self.deferred);
36702
37117
  //#endregion
36703
37118
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.74/node_modules/effect/dist/unstable/socket/Socket.js
36704
37119
  /**
@@ -36984,13 +37399,13 @@ const fromWebSocket = (acquire, options) => withFiber((fiber) => {
36984
37399
  kind: "Timeout",
36985
37400
  cause: /* @__PURE__ */ new Error("timeout waiting for \"open\"")
36986
37401
  }) }))
36987
- }), raceFirst(join(fiberSet)));
37402
+ }), raceFirst(join$1(fiberSet)));
36988
37403
  }
36989
37404
  open = true;
36990
37405
  currentWS = ws;
36991
37406
  latch.openUnsafe();
36992
37407
  if (opts?.onOpen) yield* opts.onOpen;
36993
- return yield* catchFilter(join(fiberSet), SocketCloseError.filterClean((_) => !closeCodeIsError(_)), () => void_$1);
37408
+ return yield* catchFilter(join$1(fiberSet), SocketCloseError.filterClean((_) => !closeCodeIsError(_)), () => void_$1);
36994
37409
  })).pipe(updateContext((input) => merge$3(acquireContext, input)), ensuring$2(sync(() => {
36995
37410
  latch.closeUnsafe();
36996
37411
  currentWS = void 0;
@@ -42406,7 +42821,7 @@ const make$15 = /*#__PURE__*/ fnUntraced(function* (group, options) {
42406
42821
  }).pipe(provide$3(scope));
42407
42822
  yield* forkChild(whileLoop({
42408
42823
  while: constTrue,
42409
- body: constant(flatMap$2(take(disconnects), (clientId) => {
42824
+ body: constant(flatMap$2(take$1(disconnects), (clientId) => {
42410
42825
  clients.delete(clientId);
42411
42826
  return server.disconnect(clientId);
42412
42827
  })),
@@ -47659,7 +48074,7 @@ const readDirectory = (path, options) => tryPromise({
47659
48074
  try: () => NFS.promises.readdir(path, options),
47660
48075
  catch: (err) => handleErrnoException("FileSystem", "readDirectory")(err, [path])
47661
48076
  });
47662
- const readFile = (path) => callback$1((resume, signal) => {
48077
+ const readFile$1 = (path) => callback$1((resume, signal) => {
47663
48078
  try {
47664
48079
  NFS.readFile(path, { signal }, (err, data) => {
47665
48080
  if (err) resume(fail$3(handleErrnoException("FileSystem", "readFile")(err, [path])));
@@ -47780,7 +48195,7 @@ const makeFileSystem = /*#__PURE__*/ map$4(/*#__PURE__*/ serviceOption(WatchBack
47780
48195
  makeTempFileScoped,
47781
48196
  open,
47782
48197
  readDirectory,
47783
- readFile,
48198
+ readFile: readFile$1,
47784
48199
  readLink,
47785
48200
  realPath,
47786
48201
  remove,
@@ -50026,6 +50441,29 @@ const setUnsafe = (self, value) => {
50026
50441
  */
50027
50442
  const set = /*#__PURE__*/ dual(2, (self, value) => self.semaphore.withPermit(sync(() => setUnsafe(self, value))));
50028
50443
  /**
50444
+ * Updates the value of the `SubscriptionRef` with the result of applying a
50445
+ * function, notifying subscribers of the change.
50446
+ *
50447
+ * **Example** (Updating a value)
50448
+ *
50449
+ * ```ts
50450
+ * import { Effect, SubscriptionRef } from "effect"
50451
+ *
50452
+ * const program = Effect.gen(function*() {
50453
+ * const ref = yield* SubscriptionRef.make(10)
50454
+ *
50455
+ * yield* SubscriptionRef.update(ref, (n) => n * 2)
50456
+ *
50457
+ * const value = yield* SubscriptionRef.get(ref)
50458
+ * console.log(value)
50459
+ * })
50460
+ * ```
50461
+ *
50462
+ * @category updating
50463
+ * @since 2.0.0
50464
+ */
50465
+ const update = /*#__PURE__*/ dual(2, (self, update) => self.semaphore.withPermit(sync(() => setUnsafe(self, update(self.value)))));
50466
+ /**
50029
50467
  * Applies an update function to the current value. If it returns
50030
50468
  * `Option.some`, sets and publishes that value; if it returns `Option.none`,
50031
50469
  * leaves the reference unchanged and does not publish.
@@ -50442,9 +50880,100 @@ const TerminalEvent = Union([
50442
50880
  }),
50443
50881
  Struct({ type: Literal("reset") })
50444
50882
  ]);
50883
+ const TerminalStatus = Union([
50884
+ Struct({ state: Literal("starting") }),
50885
+ Struct({
50886
+ pid: Number$1,
50887
+ state: Literal("running")
50888
+ }),
50889
+ Struct({ state: Literal("stopped") }),
50890
+ Struct({
50891
+ exitCode: Number$1,
50892
+ signal: optional(Number$1),
50893
+ state: Literal("exited")
50894
+ }),
50895
+ Struct({
50896
+ exitCode: optional(Number$1),
50897
+ signal: optional(Number$1),
50898
+ state: Literal("failed")
50899
+ })
50900
+ ]);
50901
+ const TerminalSignals = Struct({
50902
+ activity: Union([
50903
+ Literal("idle"),
50904
+ Literal("starting"),
50905
+ Literal("working"),
50906
+ Literal("thinking"),
50907
+ Literal("waiting"),
50908
+ Literal("needs_input"),
50909
+ Literal("unknown")
50910
+ ]),
50911
+ displayTitle: NullOr(String$1),
50912
+ notification: NullOr(Struct({
50913
+ message: NullOr(String$1),
50914
+ sequence: Number$1
50915
+ })),
50916
+ title: NullOr(String$1)
50917
+ });
50918
+ const TerminalState = Struct({
50919
+ args: ArraySchema(String$1),
50920
+ command: String$1,
50921
+ cwd: String$1,
50922
+ ports: ArraySchema(Number$1),
50923
+ runId: Number$1,
50924
+ signals: TerminalSignals,
50925
+ size: Struct({
50926
+ cols: Number$1,
50927
+ rows: Number$1
50928
+ }),
50929
+ status: TerminalStatus
50930
+ });
50445
50931
  //#endregion
50446
50932
  //#region src/rpcs/contracts.ts
50447
- var RpcContracts = class extends make$14(make$17("projects.watch", {
50933
+ const TerminalPayload = Struct({
50934
+ args: optional(ArraySchema(String$1)),
50935
+ command: optional(String$1),
50936
+ cwd: String$1,
50937
+ sessionId: optional(String$1)
50938
+ });
50939
+ const AgentSession = Struct({
50940
+ args: ArraySchema(String$1),
50941
+ command: String$1,
50942
+ cwd: String$1,
50943
+ icon: Literals([
50944
+ "opencode",
50945
+ "codex",
50946
+ "pi"
50947
+ ]),
50948
+ label: String$1,
50949
+ uuid: String$1
50950
+ });
50951
+ var RpcContracts = class extends make$14(make$17("agents.create", {
50952
+ error: TerminalError,
50953
+ payload: Struct({
50954
+ args: ArraySchema(String$1),
50955
+ command: String$1,
50956
+ cwd: String$1,
50957
+ icon: Literals([
50958
+ "opencode",
50959
+ "codex",
50960
+ "pi"
50961
+ ]),
50962
+ label: String$1
50963
+ }),
50964
+ success: AgentSession
50965
+ }), make$17("agents.remove", {
50966
+ error: TerminalError,
50967
+ payload: Struct({
50968
+ cwd: String$1,
50969
+ uuid: String$1
50970
+ })
50971
+ }), make$17("agents.watch", {
50972
+ error: TerminalError,
50973
+ payload: Struct({ cwd: String$1 }),
50974
+ stream: true,
50975
+ success: ArraySchema(AgentSession)
50976
+ }), make$17("projects.watch", {
50448
50977
  stream: true,
50449
50978
  success: ArraySchema(GitProject)
50450
50979
  }), make$17("projects.branches", {
@@ -50525,16 +51054,27 @@ var RpcContracts = class extends make$14(make$17("projects.watch", {
50525
51054
  cwd: String$1,
50526
51055
  force: Boolean$1
50527
51056
  })
50528
- }), make$17("terminal.events", {
51057
+ }), make$17("runs.scripts", {
50529
51058
  error: TerminalError,
50530
51059
  payload: Struct({ cwd: String$1 }),
51060
+ success: ArraySchema(Struct({
51061
+ command: String$1,
51062
+ name: String$1,
51063
+ tasks: ArraySchema(String$1)
51064
+ }))
51065
+ }), make$17("terminal.events", {
51066
+ error: TerminalError,
51067
+ payload: TerminalPayload,
50531
51068
  stream: true,
50532
51069
  success: TerminalEvent
50533
51070
  }), make$17("terminal.input", {
50534
51071
  error: TerminalError,
50535
51072
  payload: Struct({
51073
+ args: optional(ArraySchema(String$1)),
51074
+ command: optional(String$1),
50536
51075
  cwd: String$1,
50537
- data: String$1
51076
+ data: String$1,
51077
+ sessionId: optional(String$1)
50538
51078
  })
50539
51079
  }), make$17("terminal.killPort", {
50540
51080
  error: TerminalError,
@@ -50550,10 +51090,30 @@ var RpcContracts = class extends make$14(make$17("projects.watch", {
50550
51090
  }), make$17("terminal.resize", {
50551
51091
  error: TerminalError,
50552
51092
  payload: Struct({
51093
+ args: optional(ArraySchema(String$1)),
50553
51094
  cols: Number$1,
51095
+ command: optional(String$1),
50554
51096
  cwd: String$1,
50555
- rows: Number$1
51097
+ rows: Number$1,
51098
+ sessionId: optional(String$1)
50556
51099
  })
51100
+ }), make$17("terminal.restart", {
51101
+ error: TerminalError,
51102
+ payload: TerminalPayload,
51103
+ success: TerminalState
51104
+ }), make$17("terminal.state", {
51105
+ error: TerminalError,
51106
+ payload: TerminalPayload,
51107
+ stream: true,
51108
+ success: TerminalState
51109
+ }), make$17("terminal.status", {
51110
+ error: TerminalError,
51111
+ payload: TerminalPayload,
51112
+ stream: true,
51113
+ success: TerminalStatus
51114
+ }), make$17("terminal.stop", {
51115
+ error: TerminalError,
51116
+ payload: TerminalPayload
50557
51117
  })) {};
50558
51118
  //#endregion
50559
51119
  //#region ../../packages/git/src/service.ts
@@ -50622,7 +51182,7 @@ var GitWorkspace = class extends Service()("@deslop/git/service/GitWorkspace", {
50622
51182
  gitDirectory,
50623
51183
  root: roots[0]
50624
51184
  }))), orElseSucceed(() => failVoid), flatMap$2((repository) => collectRepositoriesFromRoots(drop$1(roots, 1), append$1(repositories, repository))));
50625
- return pipe(entries, filter$1((entry) => !(new Set([
51185
+ return pipe(entries, filter$2((entry) => !(new Set([
50626
51186
  ".git",
50627
51187
  ".next",
50628
51188
  ".turbo",
@@ -50630,7 +51190,7 @@ var GitWorkspace = class extends Service()("@deslop/git/service/GitWorkspace", {
50630
51190
  "coverage",
50631
51191
  "dist",
50632
51192
  "node_modules"
50633
- ]).has(entry) || startsWith(".")(entry) && entry !== ".git")), forEach$1((entry) => pipe(fs.stat(path.join(roots[0], entry)), map$4((info) => info.type === "Directory" ? path.join(roots[0], entry) : ""), orElseSucceed(() => ""))), flatMap$2((nextRoots) => collectRepositoriesFromRoots(pipe(nextRoots, filter$1(isNonEmpty$1), appendAll(drop$1(roots, 1))), repositories)));
51193
+ ]).has(entry) || startsWith(".")(entry) && entry !== ".git")), forEach$1((entry) => pipe(fs.stat(path.join(roots[0], entry)), map$4((info) => info.type === "Directory" ? path.join(roots[0], entry) : ""), orElseSucceed(() => ""))), flatMap$2((nextRoots) => collectRepositoriesFromRoots(pipe(nextRoots, filter$2(isNonEmpty$1), appendAll(drop$1(roots, 1))), repositories)));
50634
51194
  }))
50635
51195
  });
50636
51196
  });
@@ -50707,11 +51267,11 @@ var GitWorkspace = class extends Service()("@deslop/git/service/GitWorkspace", {
50707
51267
  "for-each-ref",
50708
51268
  "--format=%(refname:short)",
50709
51269
  "refs/remotes"
50710
- ]), map$4((lines) => pipe(lines, filter$1((name) => !endsWith("/HEAD")(name)), map$7((name) => new GitBranch({
50711
- name: pipe(split$1("/")(name), drop$1(1), join$2("/")),
51270
+ ]), map$4((lines) => pipe(lines, filter$2((name) => !endsWith("/HEAD")(name)), map$7((name) => new GitBranch({
51271
+ name: pipe(split$1("/")(name), drop$1(1), join$3("/")),
50712
51272
  remote: split$1("/")(name)[0],
50713
51273
  type: "remote"
50714
- })), filter$1((branch) => isNonEmpty$1(branch.name)), appendAll(localBranches)))))),
51274
+ })), filter$2((branch) => isNonEmpty$1(branch.name)), appendAll(localBranches)))))),
50715
51275
  defaultBranch: yield* getDefaultBranch(cwd)
50716
51276
  });
50717
51277
  }),
@@ -50863,7 +51423,7 @@ var GitWorktree = class extends Service()("@deslop/git/service/GitWorktree", { m
50863
51423
  "--exclude-standard"
50864
51424
  ]), (filePath) => pipe(fs.readFileString(path.join(config.cwd, filePath)), orElseSucceed(() => ""), map$4((content) => new GitDiff({
50865
51425
  filePath,
50866
- patch: `diff --git a/${filePath} b/${filePath}\nnew file mode 100644\n--- /dev/null\n+++ b/${filePath}\n@@ -0,0 +1,${length(split$1("\n")(content))} @@\n${pipe(split$1("\n")(content), map$7((line) => `+${line}`), join$2("\n"))}`,
51426
+ patch: `diff --git a/${filePath} b/${filePath}\nnew file mode 100644\n--- /dev/null\n+++ b/${filePath}\n@@ -0,0 +1,${length(split$1("\n")(content))} @@\n${pipe(split$1("\n")(content), map$7((line) => `+${line}`), join$3("\n"))}`,
50867
51427
  status: "added"
50868
51428
  }))), { concurrency: "unbounded" });
50869
51429
  });
@@ -50962,7 +51522,7 @@ var GitWorktree = class extends Service()("@deslop/git/service/GitWorktree", { m
50962
51522
  const subject = parts[2] ?? "";
50963
51523
  return new GitCommit({
50964
51524
  hash: parts[0] ?? "",
50965
- parents: pipe(parts[3] ?? "", split$1(" "), filter$1(isNonEmpty$1)),
51525
+ parents: pipe(parts[3] ?? "", split$1(" "), filter$2(isNonEmpty$1)),
50966
51526
  shortHash: parts[1] ?? "",
50967
51527
  subject,
50968
51528
  wip: startsWith("wip: ")(subject)
@@ -50972,7 +51532,7 @@ var GitWorktree = class extends Service()("@deslop/git/service/GitWorktree", { m
50972
51532
  return {
50973
51533
  commitAndPush: fnUntraced(function* (input) {
50974
51534
  if (yield* hasWorktreeChanges) return yield* fail$3(new GitError({ message: "Create a WIP commit before squashing." }));
50975
- const oldestWip = pipe(yield* commits(input.base), filter$1((commit) => commit.wip), last, getOrUndefined$1);
51535
+ const oldestWip = pipe(yield* commits(input.base), filter$2((commit) => commit.wip), last, getOrUndefined$1);
50976
51536
  if (!oldestWip) return yield* fail$3(new GitError({ message: "No WIP commits to squash." }));
50977
51537
  yield* pipe(git.string(config.cwd, [
50978
51538
  "reset",
@@ -57376,27 +57936,23 @@ var require_xterm_headless = /* @__PURE__ */ __commonJSMin(((exports) => {
57376
57936
  var import_addon_serialize = require_addon_serialize();
57377
57937
  var import_xterm_headless = /* @__PURE__ */ __toESM(require_xterm_headless(), 1);
57378
57938
  function parseProcessParents(output) {
57379
- const parents = /* @__PURE__ */ new Map();
57380
- for (const line of output.split("\n")) {
57381
- const columns = line.trim().split(/\s+/);
57382
- const pid = Number(columns[0]);
57383
- const ppid = Number(columns[1]);
57384
- if (Number.isFinite(pid) && Number.isFinite(ppid)) parents.set(pid, ppid);
57385
- }
57386
- return parents;
57939
+ return new Map(pipe(linesIterator(output), fromIterable$2, filterMap((line) => {
57940
+ const columns = split$1(/\s+/)(trim(line));
57941
+ const pid = Number(columns[0] ?? NaN);
57942
+ const ppid = Number(columns[1] ?? NaN);
57943
+ return Number.isFinite(pid) && Number.isFinite(ppid) ? succeed$7([pid, ppid]) : failVoid;
57944
+ })));
57387
57945
  }
57388
57946
  function parseListeningPorts(output) {
57389
- const ports = [];
57390
- for (const line of output.split("\n")) {
57391
- const columns = line.trim().split(/\s+/);
57947
+ return pipe(linesIterator(output), fromIterable$2, filterMap((line) => {
57948
+ const columns = split$1(/\s+/)(trim(line));
57392
57949
  const port = Number(columns[3]?.match(/:(\d+)$/)?.[1]);
57393
57950
  const pid = Number(line.match(/pid=(\d+)/)?.[1]);
57394
- if (Number.isFinite(port) && Number.isFinite(pid)) ports.push({
57951
+ return Number.isFinite(port) && Number.isFinite(pid) ? succeed$7({
57395
57952
  pid,
57396
57953
  port
57397
- });
57398
- }
57399
- return ports;
57954
+ }) : failVoid;
57955
+ }));
57400
57956
  }
57401
57957
  function isDescendant(pid, ancestorPid, parents) {
57402
57958
  let current = pid;
@@ -57410,6 +57966,11 @@ function isDescendant(pid, ancestorPid, parents) {
57410
57966
  }
57411
57967
  return false;
57412
57968
  }
57969
+ function descendantsOf(ancestorPid, parents) {
57970
+ const descendants = [];
57971
+ for (const pid of parents.keys()) if (pid !== ancestorPid && isDescendant(pid, ancestorPid, parents)) descendants.push(pid);
57972
+ return descendants;
57973
+ }
57413
57974
  const commandString = fnUntraced(function* (command, args, message) {
57414
57975
  return yield* pipe((yield* ChildProcessSpawner.useSync((spawner) => spawner.string))(make$39(command, args)), mapError$2((cause) => new TerminalError({
57415
57976
  cause,
@@ -57418,46 +57979,196 @@ const commandString = fnUntraced(function* (command, args, message) {
57418
57979
  });
57419
57980
  const readProcessParents = pipe(commandString("ps", ["-eo", "pid=,ppid="], "failed to list terminal processes"), map$4(parseProcessParents));
57420
57981
  const readListeningPorts = pipe(commandString("ss", ["-H", "-ltnp"], "failed to list terminal ports"), map$4(parseListeningPorts));
57982
+ const initialSize = {
57983
+ cols: 120,
57984
+ rows: 32
57985
+ };
57986
+ const initialSignals = {
57987
+ activity: "idle",
57988
+ displayTitle: null,
57989
+ notification: null,
57990
+ title: null
57991
+ };
57992
+ const scrollback = 1e4;
57993
+ const snapshotChunkSize = 64 * 1024;
57994
+ function parseTitleSignal(title) {
57995
+ const trimmed = trim(title);
57996
+ if (/^\[\s*[!.]\s*\]\s*Action Required\b/i.test(trimmed)) return {
57997
+ activity: "needs_input",
57998
+ displayTitle: trimmed.replace(/^\[\s*[!.]\s*\]\s*/i, "") || trimmed,
57999
+ title
58000
+ };
58001
+ const displayTitle = trimmed.replace(/^OC\s*\|\s*/i, "").replace(/^π\s*-\s*/i, "").replace(/^\[\s*[^\]]+\s*\]\s*/, "").replace(/^(?:[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏|/\\-]\s*)+/, "").trim() || (trimmed === "" ? null : trimmed);
58002
+ const segment = displayTitle?.match(/^(Starting|Working|Thinking|Waiting|Ready)\b/i)?.[1]?.toLowerCase();
58003
+ if (segment === "starting") return {
58004
+ activity: "starting",
58005
+ displayTitle,
58006
+ title
58007
+ };
58008
+ if (segment === "working") return {
58009
+ activity: "working",
58010
+ displayTitle,
58011
+ title
58012
+ };
58013
+ if (segment === "thinking") return {
58014
+ activity: "thinking",
58015
+ displayTitle,
58016
+ title
58017
+ };
58018
+ if (segment === "waiting") return {
58019
+ activity: "waiting",
58020
+ displayTitle,
58021
+ title
58022
+ };
58023
+ if (segment === "ready") return {
58024
+ activity: "idle",
58025
+ displayTitle,
58026
+ title
58027
+ };
58028
+ if (/^\[\s*[^\]]+\s*\]/.test(trimmed) || /^(?:[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏|/\\-]\s*)+/.test(trimmed)) return {
58029
+ activity: "working",
58030
+ displayTitle,
58031
+ title
58032
+ };
58033
+ return {
58034
+ activity: isNonEmpty$1(trimmed) ? "unknown" : "idle",
58035
+ displayTitle,
58036
+ title
58037
+ };
58038
+ }
58039
+ function snapshotEvents(data) {
58040
+ if (data === "") return [{
58041
+ data,
58042
+ type: "snapshot"
58043
+ }];
58044
+ const events = [];
58045
+ for (let index = 0; index < data.length; index += snapshotChunkSize) events.push({
58046
+ data: data.slice(index, index + snapshotChunkSize),
58047
+ type: "snapshot"
58048
+ });
58049
+ return events;
58050
+ }
57421
58051
  var Terminal = class extends Service()("@deslop/terminal/service/Terminal", { make: fnUntraced(function* (config) {
57422
58052
  const events = yield* bounded$1({ capacity: 256 });
58053
+ const controls = yield* unbounded();
57423
58054
  const dataQueue = yield* unbounded();
57424
- const portsRef = yield* make$3([]);
58055
+ const signalQueue = yield* unbounded();
57425
58056
  const portOwners = yield* make$37(/* @__PURE__ */ new Map());
57426
58057
  const processRef = yield* make$37(void 0);
57427
58058
  const screenLock = yield* make$46(1);
57428
58059
  const sequenceRef = yield* make$37(0);
57429
- const sizeRef = yield* make$37({
57430
- cols: 120,
57431
- rows: 32
57432
- });
57433
58060
  const shell = yield* string("SHELL").pipe(orElseSucceed(() => "bash"));
58061
+ const processCommand = config.command ?? shell;
58062
+ const processArgs = config.args ?? [];
58063
+ const autostart = config.command === void 0;
58064
+ const restartPolicy = config.restart ?? (config.command ? "never" : "always");
58065
+ const stateRef = yield* make$3({
58066
+ args: [...processArgs],
58067
+ command: processCommand,
58068
+ cwd: config.cwd,
58069
+ ports: [],
58070
+ runId: 0,
58071
+ signals: initialSignals,
58072
+ size: initialSize,
58073
+ status: autostart ? { state: "starting" } : { state: "stopped" }
58074
+ });
57434
58075
  const screen = new import_xterm_headless.default.Terminal({
57435
58076
  allowProposedApi: true,
57436
- cols: 120,
57437
- rows: 32,
57438
- scrollback: 1e4
58077
+ cols: initialSize.cols,
58078
+ rows: initialSize.rows,
58079
+ scrollback
57439
58080
  });
57440
58081
  const serialize = new import_addon_serialize.SerializeAddon();
57441
58082
  screen.loadAddon(serialize);
57442
- function publish$1(event) {
57443
- return pipe(updateAndGet(sequenceRef, (sequence) => sequence + 1), flatMap$2((sequence) => publish(events, {
58083
+ screen.onTitleChange((title) => {
58084
+ offerUnsafe(signalQueue, {
58085
+ title,
58086
+ type: "title"
58087
+ });
58088
+ });
58089
+ screen.onBell(() => {
58090
+ offerUnsafe(signalQueue, {
58091
+ message: null,
58092
+ type: "notification"
58093
+ });
58094
+ });
58095
+ screen.parser.registerOscHandler(9, (data) => {
58096
+ const [kind, value] = split$1(";")(data);
58097
+ if (kind === "4") {
58098
+ offerUnsafe(signalQueue, {
58099
+ activity: value === "0" ? "idle" : "working",
58100
+ type: "activity"
58101
+ });
58102
+ return true;
58103
+ }
58104
+ offerUnsafe(signalQueue, {
58105
+ message: data,
58106
+ type: "notification"
58107
+ });
58108
+ return true;
58109
+ });
58110
+ const publish$1 = fnUntraced(function* (event) {
58111
+ return yield* pipe(updateAndGet(sequenceRef, (sequence) => sequence + 1), flatMap$2((sequence) => publish(events, {
57444
58112
  event,
57445
58113
  sequence
57446
58114
  })), asVoid);
57447
- }
58115
+ });
57448
58116
  const requestSnapshot = withPermit(screenLock, gen(function* () {
57449
58117
  return {
57450
- data: serialize.serialize({ scrollback: 1e4 }),
58118
+ data: serialize.serialize({ scrollback }),
57451
58119
  sequence: yield* get$1(sequenceRef)
57452
58120
  };
57453
58121
  }));
57454
- function resetScreen() {
57455
- return withPermit(screenLock, pipe(sync(() => {
57456
- screen.reset();
57457
- }), andThen(publish$1({ type: "reset" }))));
58122
+ function setStatus(status) {
58123
+ return update(stateRef, (state) => ({
58124
+ ...state,
58125
+ status
58126
+ }));
58127
+ }
58128
+ const resetSignals = update(stateRef, (state) => ({
58129
+ ...state,
58130
+ signals: initialSignals
58131
+ }));
58132
+ const startRun = gen(function* () {
58133
+ yield* update(stateRef, (state) => ({
58134
+ ...state,
58135
+ ports: [],
58136
+ runId: state.runId + 1,
58137
+ signals: initialSignals,
58138
+ status: { state: "starting" }
58139
+ }));
58140
+ return yield* get(stateRef);
58141
+ });
58142
+ function setPorts(ports) {
58143
+ return updateSome(stateRef, (state) => state.ports.length === ports.length && state.ports.every((port, index) => port === ports[index]) ? none() : some({
58144
+ ...state,
58145
+ ports: [...ports]
58146
+ }));
57458
58147
  }
57459
- function writeScreen(data) {
57460
- return withPermit(screenLock, pipe(callback$1((resume) => {
58148
+ const interruptProcessTree = fnUntraced(function* (subprocess, signal) {
58149
+ const descendants = yield* pipe(readProcessParents, map$4((parents) => descendantsOf(subprocess.pid, parents)), timeoutOption("250 millis"), map$4((option) => getOrElse$1(option, () => [])), catch_$2(() => succeed$3([])));
58150
+ yield* sync(() => {
58151
+ try {
58152
+ if (nodeProcess.platform !== "win32") nodeProcess.kill(-subprocess.pid, signal);
58153
+ } catch {}
58154
+ for (const pid of descendants.toReversed()) try {
58155
+ nodeProcess.kill(pid, signal);
58156
+ } catch {}
58157
+ try {
58158
+ subprocess.kill(signal);
58159
+ } catch {}
58160
+ });
58161
+ });
58162
+ const terminateProcess = fnUntraced(function* (subprocess) {
58163
+ yield* interruptProcessTree(subprocess, "SIGTERM");
58164
+ yield* sleep("250 millis");
58165
+ yield* interruptProcessTree(subprocess, "SIGKILL");
58166
+ });
58167
+ const resetScreen = withPermit(screenLock, pipe(sync(() => {
58168
+ screen.reset();
58169
+ }), andThen(publish$1({ type: "reset" }))));
58170
+ const writeScreen = fnUntraced(function* (data) {
58171
+ return yield* withPermit(screenLock, pipe(callback$1((resume) => {
57461
58172
  screen.write(data, () => {
57462
58173
  resume(void_$1);
57463
58174
  });
@@ -57465,26 +58176,54 @@ var Terminal = class extends Service()("@deslop/terminal/service/Terminal", { ma
57465
58176
  data,
57466
58177
  type: "data"
57467
58178
  }))));
57468
- }
57469
- yield* addFinalizer(() => all([shutdown(dataQueue), shutdown$1(events)], { discard: true }));
58179
+ });
58180
+ yield* addFinalizer(() => all([
58181
+ shutdown(controls),
58182
+ shutdown(dataQueue),
58183
+ shutdown(signalQueue),
58184
+ shutdown$1(events)
58185
+ ], { discard: true }));
57470
58186
  yield* pipe(fromQueue(dataQueue), runForEach(writeScreen), forkScoped);
58187
+ yield* pipe(fromQueue(signalQueue), runForEach((signal) => update(stateRef, (state) => {
58188
+ if (signal.type === "activity") {
58189
+ const titleActivity = state.signals.title === null ? "idle" : parseTitleSignal(state.signals.title).activity;
58190
+ if (titleActivity !== "idle" && titleActivity !== "unknown") return state;
58191
+ return {
58192
+ ...state,
58193
+ signals: {
58194
+ ...state.signals,
58195
+ activity: signal.activity
58196
+ }
58197
+ };
58198
+ }
58199
+ if (signal.type === "title") return {
58200
+ ...state,
58201
+ signals: {
58202
+ ...state.signals,
58203
+ ...parseTitleSignal(signal.title)
58204
+ }
58205
+ };
58206
+ return {
58207
+ ...state,
58208
+ signals: {
58209
+ ...state.signals,
58210
+ notification: {
58211
+ message: signal.message,
58212
+ sequence: (state.signals.notification?.sequence ?? 0) + 1
58213
+ }
58214
+ }
58215
+ };
58216
+ })), forkScoped);
57471
58217
  const spawnProcess = acquireRelease(gen(function* () {
57472
- const size = yield* get$1(sizeRef);
58218
+ const size = (yield* get(stateRef)).size;
57473
58219
  const exited = yield* make$59();
57474
58220
  let didExit = false;
57475
- const nodePty = yield* tryPromise({
57476
- catch: (cause) => new TerminalError({
57477
- cause,
57478
- message: "failed to load terminal process runtime"
57479
- }),
57480
- try: () => import("@lydell/node-pty")
57481
- });
57482
58221
  const subprocess = yield* try_({
57483
58222
  catch: (cause) => new TerminalError({
57484
58223
  cause,
57485
58224
  message: `failed to spawn terminal in ${config.cwd}`
57486
58225
  }),
57487
- try: () => nodePty.spawn(shell, [], {
58226
+ try: () => nodePty.spawn(processCommand, [...processArgs], {
57488
58227
  cols: size.cols,
57489
58228
  cwd: config.cwd,
57490
58229
  env: {
@@ -57498,58 +58237,95 @@ var Terminal = class extends Service()("@deslop/terminal/service/Terminal", { ma
57498
58237
  const data = subprocess.onData((data) => {
57499
58238
  offerUnsafe(dataQueue, data);
57500
58239
  });
57501
- const exit = subprocess.onExit(() => {
58240
+ const exit = subprocess.onExit((event) => {
57502
58241
  didExit = true;
57503
- doneUnsafe(exited, void_$1);
58242
+ doneUnsafe(exited, succeed$3(event));
57504
58243
  });
57505
58244
  yield* set$2(processRef, subprocess);
58245
+ yield* setStatus({
58246
+ pid: subprocess.pid,
58247
+ state: "running"
58248
+ });
57506
58249
  return {
57507
58250
  data,
58251
+ didExit: () => didExit,
57508
58252
  exit,
57509
58253
  exited,
57510
- kill: () => {
57511
- if (!didExit) subprocess.kill();
57512
- },
57513
- process: subprocess
58254
+ process: subprocess,
58255
+ terminate: terminateProcess(subprocess)
57514
58256
  };
57515
58257
  }), (subprocess) => gen(function* () {
57516
58258
  yield* sync(() => {
57517
58259
  subprocess.data.dispose();
57518
58260
  subprocess.exit.dispose();
57519
- subprocess.kill();
57520
58261
  });
57521
- yield* update(processRef, (current) => current === subprocess.process ? void 0 : current);
58262
+ if (!subprocess.didExit()) yield* pipe(subprocess.terminate, ignore$1);
58263
+ yield* update$1(processRef, (current) => current === subprocess.process ? void 0 : current);
57522
58264
  }));
57523
- yield* pipe(scoped$3(gen(function* () {
57524
- yield* _await((yield* spawnProcess).exited);
57525
- })), ignore$1, andThen(set$2(portOwners, /* @__PURE__ */ new Map())), andThen(updateSome(portsRef, (ports) => ports.length === 0 ? none() : some([]))), andThen(resetScreen()), repeat(spaced("250 millis")), forkScoped);
58265
+ yield* pipe(gen(function* () {
58266
+ let restart = autostart;
58267
+ while (true) {
58268
+ if (!restart) {
58269
+ restart = (yield* take$1(controls)).type === "restart";
58270
+ if (!restart) continue;
58271
+ }
58272
+ yield* resetScreen;
58273
+ yield* resetSignals;
58274
+ yield* setStatus({ state: "starting" });
58275
+ const action = yield* pipe(scoped$3(flatMap$2(spawnProcess, (subprocess) => raceFirst(pipe(_await(subprocess.exited), map$4((event) => ({
58276
+ event,
58277
+ type: "exit"
58278
+ }))), take$1(controls)))), catch_$2((error) => succeed$3({
58279
+ error,
58280
+ type: "error"
58281
+ })));
58282
+ yield* set$2(portOwners, /* @__PURE__ */ new Map());
58283
+ yield* setPorts([]);
58284
+ if (action.type === "stop") {
58285
+ yield* setStatus({ state: "stopped" });
58286
+ restart = (yield* take$1(controls)).type === "restart";
58287
+ if (restart) yield* sleep("250 millis");
58288
+ continue;
58289
+ } else if (action.type === "restart") restart = true;
58290
+ else if (action.type === "error") {
58291
+ yield* setStatus({ state: "failed" });
58292
+ restart = restartPolicy === "always" || restartPolicy === "failed";
58293
+ } else {
58294
+ const failed = action.event.exitCode !== 0;
58295
+ yield* setStatus({
58296
+ exitCode: action.event.exitCode,
58297
+ ...action.event.signal === void 0 ? {} : { signal: action.event.signal },
58298
+ state: failed ? "failed" : "exited"
58299
+ });
58300
+ restart = restartPolicy === "always" || restartPolicy === "failed" && failed;
58301
+ }
58302
+ if (restart) yield* sleep("250 millis");
58303
+ }
58304
+ }), forkScoped);
57526
58305
  yield* pipe(gen(function* () {
57527
58306
  const process = yield* get$1(processRef);
57528
58307
  if (!process?.pid) {
57529
58308
  yield* set$2(portOwners, /* @__PURE__ */ new Map());
57530
- yield* updateSome(portsRef, (ports) => ports.length === 0 ? none() : some([]));
58309
+ yield* setPorts([]);
57531
58310
  return;
57532
58311
  }
57533
58312
  const [parents, listeningPorts] = yield* all([readProcessParents, readListeningPorts]);
57534
58313
  const nextPortOwners = /* @__PURE__ */ new Map();
57535
- const nextPorts = pipe(listeningPorts, filter$1((port) => isDescendant(port.pid, process.pid, parents)), map$7((port) => {
58314
+ const nextPorts = pipe(listeningPorts, filter$2((port) => isDescendant(port.pid, process.pid, parents)), map$7((port) => {
57536
58315
  nextPortOwners.set(port.port, port.pid);
57537
58316
  return port.port;
57538
58317
  }), dedupe, sort(Number$5));
57539
58318
  yield* set$2(portOwners, nextPortOwners);
57540
- yield* updateSome(portsRef, (currentPorts) => currentPorts.length === nextPorts.length && currentPorts.every((port, index) => port === nextPorts[index]) ? none() : some(nextPorts));
58319
+ yield* setPorts(nextPorts);
57541
58320
  }), ignore$1, repeat(spaced("1 second")), forkScoped);
57542
58321
  return {
57543
58322
  events: scoped$1(unwrap$1(gen(function* () {
57544
58323
  const subscription = yield* subscribe(events);
57545
58324
  const snapshot = yield* requestSnapshot;
57546
58325
  const initialEvents = (yield* takeUpTo(subscription, Number.POSITIVE_INFINITY)).filter((event) => event.sequence > snapshot.sequence).map((event) => event.event);
57547
- return pipe(fromIterable$1([{
57548
- data: snapshot.data,
57549
- type: "snapshot"
57550
- }, ...initialEvents]), concat(fromEffectRepeat(take$1(subscription)).pipe(map$2((event) => event.event))));
58326
+ return pipe(fromIterable$1([...snapshotEvents(snapshot.data), ...initialEvents]), concat(fromEffectRepeat(take$2(subscription)).pipe(map$2((event) => event.event))));
57551
58327
  }))),
57552
- killPort: (port) => gen(function* () {
58328
+ killPort: fnUntraced(function* (port) {
57553
58329
  const pid = (yield* get$1(portOwners)).get(port);
57554
58330
  if (!pid) return;
57555
58331
  yield* try_({
@@ -57558,15 +58334,17 @@ var Terminal = class extends Service()("@deslop/terminal/service/Terminal", { ma
57558
58334
  message: `failed to kill process on port ${port}`
57559
58335
  }),
57560
58336
  try: () => {
57561
- process.kill(pid);
58337
+ nodeProcess.kill(pid);
57562
58338
  }
57563
58339
  });
57564
58340
  }),
57565
- ports: changes(portsRef),
57566
- resize: (nextSize) => gen(function* () {
57567
- const size = yield* get$1(sizeRef);
58341
+ resize: fnUntraced(function* (nextSize) {
58342
+ const size = (yield* get(stateRef)).size;
57568
58343
  if (size.cols === nextSize.cols && size.rows === nextSize.rows) return;
57569
- yield* set$2(sizeRef, nextSize);
58344
+ yield* update(stateRef, (state) => ({
58345
+ ...state,
58346
+ size: nextSize
58347
+ }));
57570
58348
  yield* withPermit(screenLock, sync(() => {
57571
58349
  screen.resize(nextSize.cols, nextSize.rows);
57572
58350
  }));
@@ -57582,7 +58360,10 @@ var Terminal = class extends Service()("@deslop/terminal/service/Terminal", { ma
57582
58360
  }
57583
58361
  });
57584
58362
  }),
57585
- write: (data) => gen(function* () {
58363
+ restart: pipe(startRun, tap(() => offer(controls, { type: "restart" }))),
58364
+ state: stateRef,
58365
+ stop: offer(controls, { type: "stop" }),
58366
+ write: fnUntraced(function* (data) {
57586
58367
  const process = yield* get$1(processRef);
57587
58368
  if (!process) return;
57588
58369
  yield* try_({
@@ -57600,11 +58381,101 @@ var Terminal = class extends Service()("@deslop/terminal/service/Terminal", { ma
57600
58381
  static layer = flow(this.make, effect(this));
57601
58382
  };
57602
58383
  //#endregion
58384
+ //#region ../../packages/terminal/src/utils.ts
58385
+ function splitParallelCommands(script) {
58386
+ const state = pipe(split$1("")(script), reduce({
58387
+ commands: [],
58388
+ current: "",
58389
+ escaped: false,
58390
+ skipNext: false
58391
+ }, (state, char, index) => {
58392
+ if (state.skipNext) return {
58393
+ ...state,
58394
+ skipNext: false
58395
+ };
58396
+ if (state.escaped) return {
58397
+ ...state,
58398
+ current: state.current + char,
58399
+ escaped: false
58400
+ };
58401
+ if (char === "\\") return {
58402
+ ...state,
58403
+ current: state.current + char,
58404
+ escaped: true
58405
+ };
58406
+ if (state.quote) return {
58407
+ ...state,
58408
+ current: state.current + char,
58409
+ quote: char === state.quote ? void 0 : state.quote
58410
+ };
58411
+ if (char === "\"" || char === "'") return {
58412
+ ...state,
58413
+ current: state.current + char,
58414
+ quote: char
58415
+ };
58416
+ if (char === "&" && script[index + 1] === "&") return {
58417
+ ...state,
58418
+ current: `${state.current}&&`,
58419
+ skipNext: true
58420
+ };
58421
+ if (char !== "&") return {
58422
+ ...state,
58423
+ current: state.current + char
58424
+ };
58425
+ return pipe(liftPredicate(trim(state.current), isNonEmpty$1), match$4({
58426
+ onNone: () => ({
58427
+ ...state,
58428
+ current: ""
58429
+ }),
58430
+ onSome: (command) => ({
58431
+ ...state,
58432
+ commands: [...state.commands, command],
58433
+ current: ""
58434
+ })
58435
+ }));
58436
+ }));
58437
+ return pipe(liftPredicate(trim(state.current), isNonEmpty$1), match$4({
58438
+ onNone: () => state.commands,
58439
+ onSome: (command) => [...state.commands, command]
58440
+ }));
58441
+ }
58442
+ //#endregion
57603
58443
  //#region src/rpcs/handlers.ts
58444
+ const TerminalSessionKey = Struct({
58445
+ args: optional(ArraySchema(String$1)),
58446
+ command: optional(String$1),
58447
+ cwd: String$1,
58448
+ sessionId: optional(String$1)
58449
+ });
58450
+ function terminalSessionKey(input) {
58451
+ return JSON.stringify({
58452
+ args: input.args,
58453
+ command: input.command,
58454
+ cwd: input.cwd,
58455
+ sessionId: input.sessionId
58456
+ });
58457
+ }
58458
+ function agentSessionId(uuid) {
58459
+ return `agent:${uuid}`;
58460
+ }
58461
+ function agentSessionKey(input) {
58462
+ return JSON.stringify(input);
58463
+ }
57604
58464
  const TerminalSessions = make$45({
57605
58465
  idleTimeToLive: infinity,
57606
- lookup: fnUntraced(function* (cwd) {
57607
- return get$7(yield* buildWithScope(Terminal.layer({ cwd }), yield* scope), Terminal);
58466
+ lookup: fnUntraced(function* (key) {
58467
+ const config = yield* try_({
58468
+ catch: (cause) => new TerminalError({
58469
+ cause,
58470
+ message: "invalid terminal session key"
58471
+ }),
58472
+ try: () => decodeUnknownSync(TerminalSessionKey)(JSON.parse(key))
58473
+ });
58474
+ return get$7(yield* buildWithScope(Terminal.layer({
58475
+ args: config.args,
58476
+ command: config.command,
58477
+ cwd: config.cwd
58478
+ }), yield* scope), Terminal);
57608
58479
  })
57609
58480
  });
57610
58481
  const GitWorktreeSessions = make$45({
@@ -57617,7 +58488,52 @@ const RpcHandlers = RpcContracts.toLayer(gen(function* () {
57617
58488
  const git = yield* GitWorkspace;
57618
58489
  const terminals = yield* TerminalSessions;
57619
58490
  const gitWorktrees = yield* GitWorktreeSessions;
58491
+ const agents = yield* make$3(/* @__PURE__ */ new Map());
57620
58492
  return RpcContracts.of({
58493
+ "agents.create": (payload) => gen(function* () {
58494
+ const labelCount = pipe(fromIterable$2((yield* get(agents)).values()), filter$2((session) => session.cwd === payload.cwd), filter$2((session) => session.command === payload.command), length);
58495
+ const session = {
58496
+ args: [...payload.args],
58497
+ command: payload.command,
58498
+ cwd: payload.cwd,
58499
+ icon: payload.icon,
58500
+ label: `${payload.label} ${labelCount + 1}`,
58501
+ uuid: randomUUID()
58502
+ };
58503
+ yield* update(agents, (sessions) => {
58504
+ const next = new Map(sessions);
58505
+ next.set(agentSessionKey({
58506
+ cwd: session.cwd,
58507
+ uuid: session.uuid
58508
+ }), session);
58509
+ return next;
58510
+ });
58511
+ const terminal = yield* get$4(terminals, terminalSessionKey({
58512
+ args: session.args,
58513
+ command: session.command,
58514
+ cwd: session.cwd,
58515
+ sessionId: agentSessionId(session.uuid)
58516
+ }));
58517
+ yield* terminal.restart;
58518
+ yield* pipe(pipe(changes(terminal.state), map$2((state) => state.status.state), filter((state) => state === "exited" || state === "failed" || state === "stopped"), take(1), runDrain), andThen(update(agents, (current) => {
58519
+ const next = new Map(current);
58520
+ next.delete(agentSessionKey({
58521
+ cwd: session.cwd,
58522
+ uuid: session.uuid
58523
+ }));
58524
+ return next;
58525
+ })), forkScoped);
58526
+ return session;
58527
+ }),
58528
+ "agents.remove": (payload) => update(agents, (current) => {
58529
+ const next = new Map(current);
58530
+ next.delete(agentSessionKey({
58531
+ cwd: payload.cwd,
58532
+ uuid: payload.uuid
58533
+ }));
58534
+ return next;
58535
+ }),
58536
+ "agents.watch": (payload) => unwrap$1(pipe(get(agents), map$4((current) => pipe(concat(drop(1)(changes(agents)))(make$42(current)), map$2((sessions) => pipe(fromIterable$2(sessions.values()), filter$2((session) => session.cwd === payload.cwd))))))),
57621
58537
  "projects.branches": (payload) => git.branches(payload.cwd),
57622
58538
  "projects.createWorktree": (payload) => git.createWorktree(payload),
57623
58539
  "projects.deleteWorktree": (payload) => git.deleteWorktree(payload),
@@ -57636,14 +58552,29 @@ const RpcHandlers = RpcContracts.toLayer(gen(function* () {
57636
58552
  from: payload.from,
57637
58553
  to: payload.to
57638
58554
  })))),
57639
- "terminal.events": (payload) => unwrap$1(pipe(get$4(terminals, payload.cwd), map$4((terminal) => terminal.events))),
57640
- "terminal.input": (payload) => pipe(get$4(terminals, payload.cwd), flatMap$2((terminal) => terminal.write(payload.data)), asVoid),
57641
- "terminal.killPort": (payload) => pipe(get$4(terminals, payload.cwd), flatMap$2((terminal) => terminal.killPort(payload.port))),
57642
- "terminal.ports": (payload) => unwrap$1(pipe(get$4(terminals, payload.cwd), map$4((terminal) => terminal.ports))),
57643
- "terminal.resize": (payload) => pipe(get$4(terminals, payload.cwd), flatMap$2((terminal) => terminal.resize({
58555
+ "runs.scripts": (payload) => pipe(tryPromise({
58556
+ catch: (cause) => new TerminalError({
58557
+ cause,
58558
+ message: `failed to read package.json in ${payload.cwd}`
58559
+ }),
58560
+ try: () => readFile(join(payload.cwd, "package.json"), "utf8")
58561
+ }), map$4((content) => JSON.parse(content)), map$4((packageJson) => Object.entries(packageJson.scripts ?? {}).map(([name, command]) => ({
58562
+ command,
58563
+ name,
58564
+ tasks: splitParallelCommands(command)
58565
+ })))),
58566
+ "terminal.events": (payload) => unwrap$1(pipe(get$4(terminals, terminalSessionKey(payload)), map$4((terminal) => terminal.events))),
58567
+ "terminal.input": (payload) => pipe(get$4(terminals, terminalSessionKey(payload)), flatMap$2((terminal) => terminal.write(payload.data)), asVoid),
58568
+ "terminal.killPort": (payload) => pipe(get$4(terminals, terminalSessionKey({ cwd: payload.cwd })), flatMap$2((terminal) => terminal.killPort(payload.port))),
58569
+ "terminal.ports": (payload) => unwrap$1(pipe(get$4(terminals, terminalSessionKey(payload)), map$4((terminal) => pipe(changes(terminal.state), map$2((state) => state.ports))))),
58570
+ "terminal.resize": (payload) => pipe(get$4(terminals, terminalSessionKey(payload)), flatMap$2((terminal) => terminal.resize({
57644
58571
  cols: payload.cols,
57645
58572
  rows: payload.rows
57646
- })))
58573
+ }))),
58574
+ "terminal.restart": (payload) => pipe(get$4(terminals, terminalSessionKey(payload)), flatMap$2((terminal) => terminal.restart)),
58575
+ "terminal.state": (payload) => unwrap$1(pipe(get$4(terminals, terminalSessionKey(payload)), map$4((terminal) => changes(terminal.state)))),
58576
+ "terminal.status": (payload) => unwrap$1(pipe(get$4(terminals, terminalSessionKey(payload)), map$4((terminal) => pipe(changes(terminal.state), map$2((state) => state.status))))),
58577
+ "terminal.stop": (payload) => pipe(get$4(terminals, terminalSessionKey(payload)), flatMap$2((terminal) => terminal.stop), asVoid)
57647
58578
  });
57648
58579
  }));
57649
58580
  //#endregion
@@ -74527,13 +75458,16 @@ function BrowserProxyMiddleware(app) {
74527
75458
  //#endregion
74528
75459
  //#region src/main.server.ts
74529
75460
  runMain(pipe(serve(mergeAll$1(layerHttp({
74530
- group: make$14().merge(RpcContracts),
75461
+ group: RpcContracts,
74531
75462
  path: "/api/rpc",
74532
75463
  protocol: "websocket"
74533
75464
  }), layer$4({
74534
75465
  index: "index.html",
74535
75466
  root: fileURLToPath(new URL("./client", import.meta.url)),
74536
75467
  spa: true
74537
- }), middleware(BrowserProxyMiddleware, { global: true }), middleware(xForwardedHeaders, { global: true })), { disableLogger: true }), provide$2(LiveLayers), provide$2(layerConfig(createServer, { port: port("PORT").pipe(withDefault(4010)) })), provide$2(layer$6), launch));
75468
+ }), middleware(BrowserProxyMiddleware, { global: true }), middleware(xForwardedHeaders, { global: true })), { disableLogger: true }), provide$2(LiveLayers), provide$2(layerConfig(createServer, {
75469
+ gracefulShutdownTimeout: succeed("1500 millis"),
75470
+ port: port("PORT").pipe(withDefault(4010))
75471
+ })), provide$2(layer$6), launch));
74538
75472
  //#endregion
74539
75473
  export { __toCommonJS as a, __require as i, init_esm$2 as n, __commonJSMin as r, esm_exports$2 as t };