@fabric-harness/sdk 0.6.1 → 0.8.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.
@@ -0,0 +1,156 @@
1
+ import { FabricError } from './error.js';
2
+ /**
3
+ * Decorator that adds idle-based auto-suspend to any `SandboxEnv` whose
4
+ * underlying implementation supports `suspend()` / `resume()`. If the inner
5
+ * env doesn't implement them, the decorator is a no-op pass-through (the
6
+ * idle timer never fires anything).
7
+ *
8
+ * The decorator tracks a `lastAccessAt` timestamp on every operation. After
9
+ * `idleSuspendMs` elapses without activity, it calls `inner.suspend()`. The
10
+ * next operation transparently calls `inner.resume()` first (unless
11
+ * `autoResumeOnAccess: false`).
12
+ *
13
+ * `cleanup()` cancels the idle timer.
14
+ */
15
+ export class SuspendingSandboxEnv {
16
+ inner;
17
+ cwd;
18
+ capabilities;
19
+ suspended = false;
20
+ idleTimer;
21
+ inFlight = 0;
22
+ idleMs;
23
+ autoResume;
24
+ constructor(inner, options) {
25
+ this.inner = inner;
26
+ this.idleMs = options.idleSuspendMs;
27
+ this.autoResume = options.autoResumeOnAccess !== false;
28
+ this.cwd = inner.cwd;
29
+ if (inner.capabilities) {
30
+ this.capabilities = {
31
+ ...inner.capabilities,
32
+ suspend: typeof inner.suspend === 'function',
33
+ resume: typeof inner.resume === 'function',
34
+ };
35
+ }
36
+ }
37
+ resolvePath(path) { return this.inner.resolvePath(path); }
38
+ async exec(command, options) {
39
+ return this.access(() => this.inner.exec(command, options));
40
+ }
41
+ async readFile(path) { return this.access(() => this.inner.readFile(path)); }
42
+ async readFileBuffer(path) { return this.access(() => this.inner.readFileBuffer(path)); }
43
+ async writeFile(path, content) { await this.access(() => this.inner.writeFile(path, content)); }
44
+ async stat(path) { return this.access(() => this.inner.stat(path)); }
45
+ async readdir(path) { return this.access(() => this.inner.readdir(path)); }
46
+ async exists(path) { return this.access(() => this.inner.exists(path)); }
47
+ async mkdir(path, options) { await this.access(() => this.inner.mkdir(path, options)); }
48
+ async rm(path, options) { await this.access(() => this.inner.rm(path, options)); }
49
+ async snapshot() {
50
+ const snapshot = this.inner.snapshot;
51
+ if (!snapshot)
52
+ throw new FabricError({ code: 'SANDBOX_UNAVAILABLE', message: 'Sandbox does not support snapshots.' });
53
+ return this.access(() => snapshot.call(this.inner));
54
+ }
55
+ async restore(snapshot) {
56
+ const restore = this.inner.restore;
57
+ if (!restore)
58
+ throw new FabricError({ code: 'SANDBOX_UNAVAILABLE', message: 'Sandbox does not support restore.' });
59
+ await this.access(() => restore.call(this.inner, snapshot));
60
+ }
61
+ async fork(snapshot) {
62
+ const fork = this.inner.fork;
63
+ if (!fork)
64
+ throw new FabricError({ code: 'SANDBOX_UNAVAILABLE', message: 'Sandbox does not support fork.' });
65
+ return this.access(() => fork.call(this.inner, snapshot));
66
+ }
67
+ async suspend() {
68
+ if (typeof this.inner.suspend !== 'function')
69
+ return;
70
+ this.cancelIdleTimer();
71
+ if (!this.suspended) {
72
+ await this.inner.suspend();
73
+ this.suspended = true;
74
+ }
75
+ }
76
+ async resume() {
77
+ if (typeof this.inner.resume !== 'function')
78
+ return;
79
+ if (this.suspended) {
80
+ await this.inner.resume();
81
+ this.suspended = false;
82
+ }
83
+ this.scheduleIdleTimer();
84
+ }
85
+ async cleanup() {
86
+ this.cancelIdleTimer();
87
+ await this.inner.cleanup();
88
+ }
89
+ /** True when the sandbox is currently suspended (for tests / introspection). */
90
+ get isSuspended() { return this.suspended; }
91
+ async access(operation) {
92
+ this.cancelIdleTimer();
93
+ if (this.suspended) {
94
+ if (!this.autoResume) {
95
+ throw new FabricError({
96
+ code: 'SANDBOX_UNAVAILABLE',
97
+ message: 'Sandbox is suspended; pass autoResumeOnAccess: true (default) or call resume() before use.',
98
+ retryable: true,
99
+ });
100
+ }
101
+ if (typeof this.inner.resume !== 'function') {
102
+ throw new FabricError({
103
+ code: 'SANDBOX_UNAVAILABLE',
104
+ message: 'Sandbox is suspended but the underlying backend does not support resume().',
105
+ });
106
+ }
107
+ await this.inner.resume();
108
+ this.suspended = false;
109
+ }
110
+ this.inFlight += 1;
111
+ try {
112
+ return await operation();
113
+ }
114
+ finally {
115
+ this.inFlight -= 1;
116
+ // Only restart the idle timer once nothing is in flight, so multiple
117
+ // concurrent operations don't suspend mid-flight.
118
+ if (this.inFlight === 0)
119
+ this.scheduleIdleTimer();
120
+ }
121
+ }
122
+ scheduleIdleTimer() {
123
+ const suspend = this.inner.suspend;
124
+ if (!suspend)
125
+ return;
126
+ this.cancelIdleTimer();
127
+ const timer = setTimeout(() => {
128
+ // Best-effort suspend; swallow errors so a failing provider doesn't crash the process.
129
+ void suspend.call(this.inner).then(() => { this.suspended = true; }).catch(() => undefined);
130
+ }, this.idleMs);
131
+ // Don't keep the event loop alive just for the suspend timer.
132
+ if (typeof timer === 'object' && timer !== null && 'unref' in timer && typeof timer.unref === 'function') {
133
+ timer.unref();
134
+ }
135
+ this.idleTimer = timer;
136
+ }
137
+ cancelIdleTimer() {
138
+ if (this.idleTimer) {
139
+ clearTimeout(this.idleTimer);
140
+ this.idleTimer = undefined;
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * Wrap any `SandboxEnv` with idle-based auto-suspend. Returns the inner env
146
+ * unchanged when `idleSuspendMs` is undefined or the inner env doesn't
147
+ * implement `suspend()`.
148
+ */
149
+ export function withIdleSuspend(inner, options) {
150
+ if (!options?.idleSuspendMs || options.idleSuspendMs <= 0)
151
+ return inner;
152
+ if (typeof inner.suspend !== 'function')
153
+ return inner;
154
+ return new SuspendingSandboxEnv(inner, options);
155
+ }
156
+ //# sourceMappingURL=suspending-sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suspending-sandbox.js","sourceRoot":"","sources":["../src/suspending-sandbox.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fabric-harness/sdk",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "Headless TypeScript framework for building durable, deployable autonomous agents — core SDK.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Fabric",