@phpsandbox/sdk 0.0.37 → 0.0.39

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.
@@ -596,6 +596,8 @@ var once = (fn) => {
596
596
  };
597
597
 
598
598
  // src/terminal.ts
599
+ var TERMINAL_INPUT_FLUSH_DELAY_MS = 8;
600
+ var terminalInputFlushPattern = /[\x00-\x1f\x7f]/;
599
601
  var Terminal = class {
600
602
  constructor(okra) {
601
603
  this.okra = okra;
@@ -619,13 +621,51 @@ var Terminal = class {
619
621
  this.okra.listen(`terminal.output.${id}`, handler);
620
622
  }
621
623
  input(id, input) {
622
- return this.okra.invoke("terminal.input", { id, input });
624
+ return Promise.resolve(this.okra.send("terminal.input", { id, input }));
623
625
  }
624
626
  listen(event, handler) {
625
627
  return this.okra.listen(event, handler);
626
628
  }
629
+ async attach(id, opts) {
630
+ const handle = this.processHandle(id, opts?.abortSignal);
631
+ const task = await this.okra.invoke(
632
+ "terminal.attach",
633
+ {
634
+ id,
635
+ replayHistory: opts?.replayHistory
636
+ },
637
+ { abortSignal: opts?.abortSignal }
638
+ );
639
+ if (!task) {
640
+ handle.dispose();
641
+ return false;
642
+ }
643
+ return {
644
+ ...handle.process,
645
+ ...task
646
+ };
647
+ }
627
648
  async spawn(command, args, opts) {
628
649
  const id = opts?.id || nanoid();
650
+ const handle = this.processHandle(id, opts?.abortSignal);
651
+ const { abortSignal, ...otherOpts } = opts ?? {};
652
+ const result = await this.okra.invoke(
653
+ "terminal.spawn",
654
+ {
655
+ command: [command, ...args],
656
+ opts: {
657
+ id,
658
+ ...otherOpts
659
+ }
660
+ },
661
+ { abortSignal }
662
+ );
663
+ return {
664
+ ...handle.process,
665
+ ...result
666
+ };
667
+ }
668
+ processHandle(id, abortSignal) {
629
669
  const disposables = /* @__PURE__ */ new Set();
630
670
  const dispose = () => {
631
671
  for (const disposable of disposables) {
@@ -633,11 +673,42 @@ var Terminal = class {
633
673
  }
634
674
  disposables.clear();
635
675
  };
676
+ let pendingInput = "";
677
+ let inputFlushTimer;
678
+ const flushInput = () => {
679
+ if (inputFlushTimer) {
680
+ clearTimeout(inputFlushTimer);
681
+ inputFlushTimer = void 0;
682
+ }
683
+ if (pendingInput === "") {
684
+ return;
685
+ }
686
+ const input2 = pendingInput;
687
+ pendingInput = "";
688
+ void this.input(id, input2);
689
+ };
690
+ const scheduleInputFlush = () => {
691
+ if (inputFlushTimer) {
692
+ return;
693
+ }
694
+ inputFlushTimer = setTimeout(flushInput, TERMINAL_INPUT_FLUSH_DELAY_MS);
695
+ };
696
+ const queueInput = (chunk) => {
697
+ pendingInput += chunk;
698
+ if (terminalInputFlushPattern.test(chunk)) {
699
+ flushInput();
700
+ return;
701
+ }
702
+ scheduleInputFlush();
703
+ };
704
+ const disposeInput = () => {
705
+ flushInput();
706
+ dispose();
707
+ };
636
708
  const input = new WritableStream({
637
- write: (chunk) => {
638
- this.input(id, chunk);
639
- },
640
- close: dispose
709
+ write: queueInput,
710
+ close: disposeInput,
711
+ abort: disposeInput
641
712
  });
642
713
  let controller = null;
643
714
  const output = new ReadableStream({
@@ -651,7 +722,7 @@ var Terminal = class {
651
722
  },
652
723
  cancel: () => {
653
724
  controller = null;
654
- dispose();
725
+ disposeInput();
655
726
  }
656
727
  });
657
728
  const exit = new Promise((resolve) => {
@@ -663,40 +734,30 @@ var Terminal = class {
663
734
  } catch {
664
735
  }
665
736
  }
666
- dispose();
737
+ disposeInput();
667
738
  resolve(data.exitCode);
668
739
  })
669
740
  );
670
741
  });
671
742
  const kill = once(() => {
672
- dispose();
743
+ disposeInput();
673
744
  return this.okra.invoke("terminal.close", { id });
674
745
  });
675
746
  const resize = (dimensions) => {
676
747
  this.okra.invoke("terminal.resize", { id, width: dimensions.cols, height: dimensions.rows });
677
748
  };
678
- const { abortSignal, ...otherOpts } = opts ?? {};
679
749
  if (abortSignal) {
680
750
  abortSignal.addEventListener("abort", kill);
681
751
  }
682
- const result = await this.okra.invoke(
683
- "terminal.spawn",
684
- {
685
- command: [command, ...args],
686
- opts: {
687
- id,
688
- ...otherOpts
689
- }
690
- },
691
- { abortSignal }
692
- );
693
752
  return {
694
- exit,
695
- input,
696
- output,
697
- kill,
698
- resize,
699
- ...result
753
+ process: {
754
+ exit,
755
+ input,
756
+ output,
757
+ kill,
758
+ resize
759
+ },
760
+ dispose: disposeInput
700
761
  };
701
762
  }
702
763
  };
@@ -3250,6 +3311,7 @@ var InvalidConfigurationError = class extends Error {
3250
3311
  };
3251
3312
  var _Transport_instances, connect_fn, startPeriodicPing_fn;
3252
3313
  var Transport = class {
3314
+ // 30 seconds
3253
3315
  constructor(url, eventEmitter, options = {}) {
3254
3316
  this.eventEmitter = eventEmitter;
3255
3317
  this.options = options;
@@ -3275,14 +3337,6 @@ var Transport = class {
3275
3337
  this.messageQueue = [];
3276
3338
  this.MAX_QUEUE_SIZE = 100;
3277
3339
  this.QUEUE_TIMEOUT = 3e4;
3278
- // 30 seconds
3279
- // Rate limiting
3280
- this.rateLimiter = {
3281
- requests: [],
3282
- maxRequests: 50,
3283
- windowMs: 1e3
3284
- // 1 second
3285
- };
3286
3340
  this.validateConfiguration(options);
3287
3341
  this.url = new URL(url);
3288
3342
  this.PING_INTERVAL = options.pingInterval ?? 3e4;
@@ -3534,9 +3588,6 @@ var Transport = class {
3534
3588
  if (this.terminalError) {
3535
3589
  throw this.terminalError;
3536
3590
  }
3537
- if (this.isRateLimited()) {
3538
- throw new RateLimitError("Rate limit exceeded - too many requests");
3539
- }
3540
3591
  this.clearOldQueuedMessages();
3541
3592
  const responseEvent = options.responseEvent || `${action}_${nanoid()}_response`;
3542
3593
  const errorEvent = `${responseEvent}_error`;
@@ -3633,6 +3684,27 @@ var Transport = class {
3633
3684
  };
3634
3685
  return this.sendWithRetry(async () => await send(), options.retries || 10);
3635
3686
  }
3687
+ send(action, data = {}) {
3688
+ if (this.terminalError) {
3689
+ throw this.terminalError;
3690
+ }
3691
+ if (!this.isConnected || this.isClosed) {
3692
+ this.log("debug", "Connection not available, dropping one-way message", {
3693
+ action
3694
+ });
3695
+ return false;
3696
+ }
3697
+ try {
3698
+ this.rws.send(this.pack({ action, data }));
3699
+ this.connectionStats.totalMessages++;
3700
+ this.log("debug", "One-way message sent", { action, data });
3701
+ return true;
3702
+ } catch (error) {
3703
+ this.connectionStats.totalErrors++;
3704
+ this.log("error", "Failed to send one-way message", { action, error });
3705
+ return false;
3706
+ }
3707
+ }
3636
3708
  pack(data) {
3637
3709
  return new Blob([encode(data)]);
3638
3710
  }
@@ -3736,7 +3808,6 @@ var Transport = class {
3736
3808
  if (queuedCount > 0) {
3737
3809
  this.log("debug", `Rejected ${queuedCount} queued messages due to connection close`);
3738
3810
  }
3739
- this.rateLimiter.requests = [];
3740
3811
  this.disposables.dispose();
3741
3812
  try {
3742
3813
  this.rws.close(code, reason);
@@ -3863,18 +3934,6 @@ var Transport = class {
3863
3934
  this.log("debug", `Cleared ${originalLength - this.messageQueue.length} expired messages`);
3864
3935
  }
3865
3936
  }
3866
- /**
3867
- * Rate limiting check
3868
- */
3869
- isRateLimited() {
3870
- const now = Date.now();
3871
- this.rateLimiter.requests = this.rateLimiter.requests.filter((timestamp) => now - timestamp < this.rateLimiter.windowMs);
3872
- if (this.rateLimiter.requests.length >= this.rateLimiter.maxRequests) {
3873
- return true;
3874
- }
3875
- this.rateLimiter.requests.push(now);
3876
- return false;
3877
- }
3878
3937
  /**
3879
3938
  * Calculate exponential backoff delay
3880
3939
  */
@@ -3909,12 +3968,6 @@ var Transport = class {
3909
3968
  maxSize: this.MAX_QUEUE_SIZE,
3910
3969
  oldestMessageAge: this.messageQueue.length > 0 ? now - Math.min(...this.messageQueue.map((m) => m.timestamp)) : 0
3911
3970
  },
3912
- rateLimiter: {
3913
- currentRequests: this.rateLimiter.requests.length,
3914
- maxRequests: this.rateLimiter.maxRequests,
3915
- windowMs: this.rateLimiter.windowMs,
3916
- isLimited: this.isRateLimited()
3917
- },
3918
3971
  config: {
3919
3972
  pingInterval: this.PING_INTERVAL,
3920
3973
  queueTimeout: this.QUEUE_TIMEOUT,
@@ -3980,10 +4033,6 @@ var Transport = class {
3980
4033
  issues.push("High average response time");
3981
4034
  recommendations.push("Check network latency and server performance");
3982
4035
  }
3983
- if (metrics.rateLimiter.isLimited) {
3984
- issues.push("Rate limiting is active");
3985
- recommendations.push("Reduce request frequency or increase rate limit");
3986
- }
3987
4036
  this.log("info", "Connection diagnostics completed", {
3988
4037
  status,
3989
4038
  issueCount: issues.length,
@@ -4004,10 +4053,6 @@ var Transport = class {
4004
4053
  const maintenanceInterval = setInterval(
4005
4054
  () => {
4006
4055
  this.clearOldQueuedMessages();
4007
- const now = Date.now();
4008
- this.rateLimiter.requests = this.rateLimiter.requests.filter(
4009
- (timestamp) => now - timestamp < this.rateLimiter.windowMs
4010
- );
4011
4056
  if (this.options.debug) {
4012
4057
  const health = this.getHealthStatus();
4013
4058
  const metrics = this.getConnectionMetrics();
@@ -5187,6 +5232,9 @@ var NotebookInstance = class {
5187
5232
  invoke(action, data = {}, options = {}) {
5188
5233
  return this.socket.invoke(action, data || {}, options);
5189
5234
  }
5235
+ send(action, data = {}) {
5236
+ return this.socket.send(action, data || {});
5237
+ }
5190
5238
  ping() {
5191
5239
  return this.invoke("ping");
5192
5240
  }