@wenathlan/saddle 2.0.154 → 2.0.159
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +167 -1334
- package/README.md +59 -60
- package/biome.jsonc +3 -3
- package/browser.ts +482 -0
- package/cli.ts +274 -0
- package/distribution.ts +731 -0
- package/docker.config +29 -8
- package/docs/{architecture.1.8.19.mmd → architecture1819.md} +6 -0
- package/docs/artifactavailability.md +2 -2
- package/docs/browser.md +18 -1
- package/docs/{containerplatforms.2.1.0.md → containerplatforms210.md} +1 -1
- package/docs/{documentmap.1.8.12.md → documentmap1812.md} +1 -1
- package/docs/extension.md +13 -2
- package/docs/features.md +5 -0
- package/docs/libraryapi.md +1 -1
- package/docs/{ninety.modalities.md → ninetymodalities.md} +87 -4
- package/docs/{planning.ninety.md → planningninety.md} +6 -6
- package/docs/{releasenotes.1.8.13.md → releasenotes1813.md} +2 -2
- package/docs/{releasenotes.1.8.14.md → releasenotes1814.md} +1 -1
- package/docs/{repo.synthesis.1.8.16.md → reposynthesis1816.md} +1 -1
- package/docs/todo.md +2 -2
- package/docs/{todo.1.8.15.md → todo1815.md} +1 -1
- package/docs/{todo.1.8.16.md → todo1816.md} +5 -5
- package/execution.ts +677 -0
- package/foundation.ts +314 -0
- package/index.ts +36 -34
- package/isolation.ts +84 -0
- package/modalityregistry.ts +189 -145
- package/modes.ts +170 -0
- package/mttg.config +1 -1
- package/orchestrator.ts +49 -15
- package/package.json +105 -89
- package/qemu.config +2 -2
- package/sandbox.ts +3279 -0
- package/scheduler.ts +1 -1
- package/server.ts +52 -0
- package/tsconfig.json +12 -13
- package/virtual.ts +7528 -0
- package/workloadlaw.ts +293 -0
- package/dist/App.d.ts +0 -5
- package/dist/App.js +0 -52
- package/dist/Sol/Sol.d.ts +0 -3
- package/dist/Sol/Sol.js +0 -46
- package/dist/Sol/agentbrowser/agentbrowser.d.ts +0 -9
- package/dist/Sol/agentbrowser/agentbrowser.js +0 -121
- package/dist/Sol/architecture/architecture.d.ts +0 -9
- package/dist/Sol/architecture/architecture.js +0 -93
- package/dist/Sol/compute/compute.d.ts +0 -9
- package/dist/Sol/compute/compute.js +0 -22
- package/dist/Sol/console/console.d.ts +0 -15
- package/dist/Sol/console/console.js +0 -292
- package/dist/Sol/dashboard/dashboard.d.ts +0 -10
- package/dist/Sol/dashboard/dashboard.js +0 -181
- package/dist/Sol/docs/docs.d.ts +0 -9
- package/dist/Sol/docs/docs.js +0 -28
- package/dist/Sol/home/home.d.ts +0 -35
- package/dist/Sol/home/home.js +0 -239
- package/dist/Sol/integrations/integrations.d.ts +0 -9
- package/dist/Sol/integrations/integrations.js +0 -83
- package/dist/Sol/intro/intro.d.ts +0 -7
- package/dist/Sol/intro/intro.js +0 -102
- package/dist/Sol/login/login.d.ts +0 -16
- package/dist/Sol/login/login.js +0 -49
- package/dist/Sol/notfound/notfound.d.ts +0 -9
- package/dist/Sol/notfound/notfound.js +0 -15
- package/dist/Sol/onboarding/onboarding.d.ts +0 -25
- package/dist/Sol/onboarding/onboarding.js +0 -60
- package/dist/Sol/playground/playground.d.ts +0 -9
- package/dist/Sol/playground/playground.js +0 -127
- package/dist/Sol/register/register.d.ts +0 -16
- package/dist/Sol/register/register.js +0 -193
- package/dist/Sol/shell/Shell.d.ts +0 -124
- package/dist/Sol/shell/Shell.js +0 -335
- package/dist/Sol/wingets/ComputeBootladder.d.ts +0 -6
- package/dist/Sol/wingets/ComputeBootladder.js +0 -71
- package/dist/Sol/wingets/ComputeModalitycatalog.d.ts +0 -7
- package/dist/Sol/wingets/ComputeModalitycatalog.js +0 -71
- package/dist/Sol/wingets/ConsoleApibadge.d.ts +0 -7
- package/dist/Sol/wingets/ConsoleApibadge.js +0 -5
- package/dist/Sol/wingets/ConsoleEdgelaw.d.ts +0 -6
- package/dist/Sol/wingets/ConsoleEdgelaw.js +0 -36
- package/dist/Sol/wingets/ConsoleEventstimeline.d.ts +0 -19
- package/dist/Sol/wingets/ConsoleEventstimeline.js +0 -12
- package/dist/Sol/wingets/ConsoleSnapshotcontrols.d.ts +0 -8
- package/dist/Sol/wingets/ConsoleSnapshotcontrols.js +0 -8
- package/dist/Sol/wingets/ConsoleSpecpanel.d.ts +0 -21
- package/dist/Sol/wingets/ConsoleSpecpanel.js +0 -75
- package/dist/Sol/wingets/ConsoleTerminal.d.ts +0 -22
- package/dist/Sol/wingets/ConsoleTerminal.js +0 -77
- package/dist/Sol/wingets/DashboardAccountpanel.d.ts +0 -18
- package/dist/Sol/wingets/DashboardAccountpanel.js +0 -65
- package/dist/Sol/wingets/DashboardAdminpanel.d.ts +0 -6
- package/dist/Sol/wingets/DashboardAdminpanel.js +0 -90
- package/dist/Sol/wingets/DashboardAuditlog.d.ts +0 -8
- package/dist/Sol/wingets/DashboardAuditlog.js +0 -50
- package/dist/Sol/wingets/DashboardCreatesandboxform.d.ts +0 -10
- package/dist/Sol/wingets/DashboardCreatesandboxform.js +0 -71
- package/dist/Sol/wingets/DashboardEventstimeline.d.ts +0 -8
- package/dist/Sol/wingets/DashboardEventstimeline.js +0 -83
- package/dist/Sol/wingets/DashboardGlobalsandboxestable.d.ts +0 -8
- package/dist/Sol/wingets/DashboardGlobalsandboxestable.js +0 -57
- package/dist/Sol/wingets/DashboardMeshnodestable.d.ts +0 -8
- package/dist/Sol/wingets/DashboardMeshnodestable.js +0 -103
- package/dist/Sol/wingets/DashboardSandboxlist.d.ts +0 -10
- package/dist/Sol/wingets/DashboardSandboxlist.js +0 -163
- package/dist/Sol/wingets/DashboardUserstable.d.ts +0 -8
- package/dist/Sol/wingets/DashboardUserstable.js +0 -78
- package/dist/Sol/wingets/HomeEntrygate.d.ts +0 -23
- package/dist/Sol/wingets/HomeEntrygate.js +0 -73
- package/dist/Sol/wingets/HomeMetricstrip.d.ts +0 -9
- package/dist/Sol/wingets/HomeMetricstrip.js +0 -4
- package/dist/Sol/wingets/HomeRuntimediagram.d.ts +0 -1
- package/dist/Sol/wingets/HomeRuntimediagram.js +0 -22
- package/dist/Sol/wingets/LoginForm.d.ts +0 -19
- package/dist/Sol/wingets/LoginForm.js +0 -141
- package/dist/Sol/wingets/RegisterPasswordconfirm.d.ts +0 -13
- package/dist/Sol/wingets/RegisterPasswordconfirm.js +0 -16
- package/dist/Sol/wingets/RegisterPasswordmeter.d.ts +0 -19
- package/dist/Sol/wingets/RegisterPasswordmeter.js +0 -65
- package/dist/Sol/wingets/RegisterUsernamerules.d.ts +0 -11
- package/dist/Sol/wingets/RegisterUsernamerules.js +0 -16
- package/dist/Sol/wingets/index.d.ts +0 -39
- package/dist/Sol/wingets/index.js +0 -39
- package/dist/alternatives.d.ts +0 -508
- package/dist/alternatives.js +0 -950
- package/dist/api.d.ts +0 -154
- package/dist/api.js +0 -174
- package/dist/auth.d.ts +0 -268
- package/dist/auth.js +0 -476
- package/dist/automation.d.ts +0 -425
- package/dist/automation.js +0 -566
- package/dist/browser.d.ts +0 -561
- package/dist/browser.js +0 -1058
- package/dist/capacitor.config.d.ts +0 -45
- package/dist/capacitor.config.js +0 -48
- package/dist/catalog.d.ts +0 -43
- package/dist/catalog.js +0 -44
- package/dist/cli.d.ts +0 -16
- package/dist/cli.js +0 -168
- package/dist/communication.d.ts +0 -181
- package/dist/communication.js +0 -376
- package/dist/compute.d.ts +0 -1160
- package/dist/compute.js +0 -2272
- package/dist/dashboardlib.d.ts +0 -58
- package/dist/dashboardlib.js +0 -161
- package/dist/db.d.ts +0 -569
- package/dist/db.js +0 -1080
- package/dist/distribution.d.ts +0 -528
- package/dist/distribution.js +0 -810
- package/dist/drizzle.config.d.ts +0 -20
- package/dist/drizzle.config.js +0 -18
- package/dist/edgelandes.d.ts +0 -70
- package/dist/edgelandes.host.d.ts +0 -26
- package/dist/edgelandes.host.js +0 -1089
- package/dist/edgelandes.inproc.d.ts +0 -27
- package/dist/edgelandes.inproc.js +0 -952
- package/dist/edgelandes.js +0 -1133
- package/dist/edgelandes.ninety.d.ts +0 -31
- package/dist/edgelandes.ninety.js +0 -174
- package/dist/edgelandes.virtual.d.ts +0 -25
- package/dist/edgelandes.virtual.js +0 -629
- package/dist/edgeplane.d.ts +0 -121
- package/dist/edgeplane.js +0 -341
- package/dist/examplelocaljob.d.ts +0 -1
- package/dist/examplelocaljob.js +0 -14
- package/dist/execution.d.ts +0 -529
- package/dist/execution.js +0 -760
- package/dist/family.d.ts +0 -16
- package/dist/family.js +0 -42
- package/dist/familyurl.d.ts +0 -21
- package/dist/familyurl.js +0 -24
- package/dist/format.d.ts +0 -18
- package/dist/format.js +0 -59
- package/dist/foundation.d.ts +0 -172
- package/dist/foundation.js +0 -312
- package/dist/index.d.ts +0 -735
- package/dist/index.js +0 -1020
- package/dist/integration.d.ts +0 -235
- package/dist/integration.js +0 -228
- package/dist/intelligence.d.ts +0 -93
- package/dist/intelligence.js +0 -144
- package/dist/isolation.d.ts +0 -128
- package/dist/isolation.js +0 -95
- package/dist/labels.d.ts +0 -665
- package/dist/labels.js +0 -707
- package/dist/localauth.d.ts +0 -127
- package/dist/localauth.js +0 -413
- package/dist/media.d.ts +0 -343
- package/dist/media.js +0 -985
- package/dist/mesh.d.ts +0 -177
- package/dist/mesh.js +0 -468
- package/dist/modalityregistry.d.ts +0 -109
- package/dist/modalityregistry.js +0 -1386
- package/dist/modes.d.ts +0 -358
- package/dist/modes.js +0 -133
- package/dist/onboardingstate.d.ts +0 -79
- package/dist/onboardingstate.js +0 -282
- package/dist/orchestrator.d.ts +0 -1338
- package/dist/orchestrator.js +0 -4310
- package/dist/packager/targetcli.js +0 -4
- package/dist/paths.d.ts +0 -2
- package/dist/paths.js +0 -14
- package/dist/performance.d.ts +0 -415
- package/dist/performance.js +0 -826
- package/dist/quantum.d.ts +0 -809
- package/dist/quantum.js +0 -2072
- package/dist/render.d.ts +0 -380
- package/dist/render.js +0 -1072
- package/dist/runall.d.ts +0 -2
- package/dist/runall.js +0 -39
- package/dist/runtimeprobe.d.ts +0 -4
- package/dist/runtimeprobe.js +0 -8
- package/dist/sandbox.d.ts +0 -355
- package/dist/sandbox.js +0 -1721
- package/dist/sandboxenvprofile.d.ts +0 -29
- package/dist/sandboxenvprofile.js +0 -89
- package/dist/sandboxprofile.d.ts +0 -137
- package/dist/sandboxprofile.js +0 -241
- package/dist/sandboxqueue.d.ts +0 -108
- package/dist/sandboxqueue.js +0 -201
- package/dist/sandboxquota.d.ts +0 -121
- package/dist/sandboxquota.js +0 -236
- package/dist/scheduler.d.ts +0 -595
- package/dist/scheduler.js +0 -1258
- package/dist/scriptlanes.d.ts +0 -182
- package/dist/scriptlanes.js +0 -660
- package/dist/scripts.buildengine.d.ts +0 -1
- package/dist/scripts.buildengine.js +0 -39
- package/dist/scripts.securitygate.d.ts +0 -1
- package/dist/scripts.securitygate.js +0 -68
- package/dist/scripts.validateflatnative.d.ts +0 -1
- package/dist/scripts.validateflatnative.js +0 -72
- package/dist/security.d.ts +0 -500
- package/dist/security.js +0 -1169
- package/dist/seedcatalog.d.ts +0 -13
- package/dist/seedcatalog.js +0 -46
- package/dist/server.d.ts +0 -20
- package/dist/server.js +0 -53
- package/dist/speccatalog.d.ts +0 -31
- package/dist/speccatalog.js +0 -163
- package/dist/tiers.d.ts +0 -1001
- package/dist/tiers.js +0 -2169
- package/dist/utils.d.ts +0 -2
- package/dist/utils.js +0 -5
- package/dist/vaulthandoff.d.ts +0 -141
- package/dist/vaulthandoff.js +0 -444
- package/dist/virtual.d.ts +0 -640
- package/dist/virtual.js +0 -1284
- package/dist/virtualcpu.d.ts +0 -315
- package/dist/virtualcpu.js +0 -1180
- package/dist/virtualgpu.d.ts +0 -344
- package/dist/virtualgpu.js +0 -1000
- package/dist/virtualization.d.ts +0 -606
- package/dist/virtualization.js +0 -1186
- package/dist/virtualmemory.d.ts +0 -817
- package/dist/virtualmemory.js +0 -1795
- package/dist/vite.config.d.ts +0 -3
- package/dist/vite.config.js +0 -48
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -25
- package/dist/webserver.d.ts +0 -166
- package/dist/webserver.js +0 -2226
- package/dist/workloadlaw.d.ts +0 -133
- package/dist/workloadlaw.js +0 -193
- package/docs/.gitkeep +0 -0
- package/docs/architecture.1.8.19.md +0 -76
- package/docs/logs/examplesession.json +0 -54
- package/docs/repo.analysis.1.8.21.json +0 -2617
- package/docs/repo.candidates.1.8.21.jsonl +0 -666
- package/docs/repo.selected.1.8.21.json +0 -786
- package/manifest.json +0 -20
- package/media.ts +0 -1337
- package/virtualcpu.ts +0 -1274
- package/virtualgpu.ts +0 -1182
- package/virtualization.ts +0 -1628
- package/virtualmemory.ts +0 -2303
- /package/docs/{artifactresearch.1.8.9.md → artifactresearch189.md} +0 -0
- /package/docs/{batches.1816.to.1820.md → batches1816to1820.md} +0 -0
- /package/docs/{brancharchive.2026.08.18.md → brancharchive20260818.md} +0 -0
- /package/docs/{containerplatforms.1.8.17.md → containerplatforms1817.md} +0 -0
- /package/docs/{e2ugh.engine.md → e2ughengine.md} +0 -0
- /package/docs/{flatbuildresearch.1.8.11.md → flatbuildresearch1811.md} +0 -0
- /package/docs/{licenseaudit.1.8.12.md → licenseaudit1812.md} +0 -0
- /package/docs/{mobileconversionresearch.1.8.10.md → mobileconversionresearch1810.md} +0 -0
- /package/docs/{nativeidentityresearch.1.8.11.md → nativeidentityresearch1811.md} +0 -0
- /package/docs/{native.wrappers.md → nativewrappers.md} +0 -0
- /package/docs/{planning.1.8.18.md → planning1818.md} +0 -0
- /package/docs/{planning.1.8.19.md → planning1819.md} +0 -0
- /package/docs/{planning.engine.md → planningengine.md} +0 -0
- /package/docs/{releasenotes.1.8.12.md → releasenotes1812.md} +0 -0
- /package/docs/{releasenotes.1.8.15.md → releasenotes1815.md} +0 -0
- /package/docs/{releasenotes.1.8.16.md → releasenotes1816.md} +0 -0
- /package/docs/{releasenotes.1.8.17.md → releasenotes1817.md} +0 -0
- /package/docs/{releasenotes.1.8.18.md → releasenotes1818.md} +0 -0
- /package/docs/{releasenotes.1.8.19.md → releasenotes1819.md} +0 -0
- /package/docs/{reorganization.1.8.8.md → reorganization188.md} +0 -0
- /package/docs/{reorganization.1.8.9.md → reorganization189.md} +0 -0
- /package/docs/{repo.research.1.8.16.md → reporesearch1816.md} +0 -0
- /package/docs/{repo.research.1.8.21.md → reporesearch1821.md} +0 -0
- /package/docs/{repo.synthesis.1.8.21.md → reposynthesis1821.md} +0 -0
- /package/docs/{research.1.8.18.isolation.md → research1818isolation.md} +0 -0
- /package/docs/{research.1.8.19.virtual.browser.md → research1819virtualbrowser.md} +0 -0
- /package/docs/{scanningresearch.1.8.11.md → scanningresearch1811.md} +0 -0
- /package/docs/{securityaudit.1.8.7.md → securityaudit187.md} +0 -0
- /package/docs/{securityreferences.1.8.11.md → securityreferences1811.md} +0 -0
- /package/docs/{securityresearch.1.8.11.md → securityresearch1811.md} +0 -0
- /package/docs/{signingresearch.1.8.12.md → signingresearch1812.md} +0 -0
- /package/docs/{tauri.glib.risk.1.8.11.md → tauriglibrisk1811.md} +0 -0
- /package/docs/{todo.1.8.18.md → todo1818.md} +0 -0
- /package/docs/{todo.1.8.19.md → todo1819.md} +0 -0
- /package/docs/{toolchainresearch.1.8.9.md → toolchainresearch189.md} +0 -0
- /package/docs/{web.duplicatecleanup.md → webduplicatecleanup.md} +0 -0
- /package/docs/{web.ideas.md → webideas.md} +0 -0
- /package/docs/{workflowimprovements.2026.08.19.md → workflowimprovements20260819.md} +0 -0
package/dist/sandboxqueue.js
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* sandboxqueue.ts — the sandbox lifecycle queue of saddle.
|
|
3
|
-
*
|
|
4
|
-
* one sandbox walks a single linear machine: creating -> starting ->
|
|
5
|
-
* running -> stopping -> stopped, with the supervision states cancelled,
|
|
6
|
-
* failed and timedout closing the paths out. every intermediate state
|
|
7
|
-
* carries a per-state time budget; the health-check tick reaps entries
|
|
8
|
-
* whose budget expired (the ttl reaper pattern of webserver.ts and the
|
|
9
|
-
* pool reaper of opensandbox). transitions are validated by a pure
|
|
10
|
-
* transition table so both callers and tests exercise the same guard.
|
|
11
|
-
*
|
|
12
|
-
* the module is deliberately pure: no timers of its own, no dom, no node
|
|
13
|
-
* builtins, an injectable clock. hosts drive it — webserver.ts ramps
|
|
14
|
-
* the api lifecycle with a timeout, the ttl reaper sweeps every minute,
|
|
15
|
-
* and orchestrator.ts owns the fifteen state vm plane machine; this
|
|
16
|
-
* module is the queue-level complement those surfaces can delegate to
|
|
17
|
-
* (wire point documented in the webserver statemachine context: the
|
|
18
|
-
* api states map as created->creating, expired->timedout,
|
|
19
|
-
* destroyed->stopped).
|
|
20
|
-
*
|
|
21
|
-
* studied competitors shaping the contract: daytonaio/daytona (sandbox
|
|
22
|
-
* states creating/starting/started/stopping/stopped/error with the
|
|
23
|
-
* -ing intermediates) and opensandbox-group/opensandbox (Creating ->
|
|
24
|
-
* Running -> Deleting -> Deleted with 409 on state conflicting
|
|
25
|
-
* operations and a pool acquisition that fails fast with Retry-After).
|
|
26
|
-
*/
|
|
27
|
-
/** phases that end the queue entry (no further transition is legal). */
|
|
28
|
-
export const SANDBOXTERMINALPHASES = [
|
|
29
|
-
'stopped',
|
|
30
|
-
'cancelled',
|
|
31
|
-
'failed',
|
|
32
|
-
'timedout',
|
|
33
|
-
];
|
|
34
|
-
/** the legal transitions of the single lifecycle machine. */
|
|
35
|
-
const sandboxtransitions = {
|
|
36
|
-
creating: ['starting', 'cancelled', 'failed', 'timedout'],
|
|
37
|
-
starting: ['running', 'cancelled', 'failed', 'timedout'],
|
|
38
|
-
running: ['stopping', 'cancelled', 'failed', 'timedout'],
|
|
39
|
-
stopping: ['stopped', 'failed', 'timedout'],
|
|
40
|
-
stopped: [],
|
|
41
|
-
cancelled: [],
|
|
42
|
-
failed: [],
|
|
43
|
-
timedout: [],
|
|
44
|
-
};
|
|
45
|
-
/** the typed queue failure, traceable to the entry and both phases. */
|
|
46
|
-
export class sandboxqueueerror extends Error {
|
|
47
|
-
/** machine readable failure code. */
|
|
48
|
-
code;
|
|
49
|
-
/** the entry the failure belongs to. */
|
|
50
|
-
id;
|
|
51
|
-
/** the phase the entry was in (or from), when known. */
|
|
52
|
-
from;
|
|
53
|
-
/** the requested target phase, when known. */
|
|
54
|
-
to;
|
|
55
|
-
constructor(code, id, from, to, message) {
|
|
56
|
-
super(message ??
|
|
57
|
-
`sandbox queue ${code} for ${id}${from === null ? '' : ` (from ${from})`}${to === null ? '' : ` to ${to}`}`);
|
|
58
|
-
this.name = 'sandboxqueueerror';
|
|
59
|
-
this.code = code;
|
|
60
|
-
this.id = id;
|
|
61
|
-
this.from = from;
|
|
62
|
-
this.to = to;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
/* ------------------------------------------------------------------ */
|
|
66
|
-
/* pure guards */
|
|
67
|
-
/* ------------------------------------------------------------------ */
|
|
68
|
-
/** checks whether a phase change is legal on the single machine. */
|
|
69
|
-
export function cansandboxstep(from, to) {
|
|
70
|
-
return sandboxtransitions[from].includes(to);
|
|
71
|
-
}
|
|
72
|
-
/** reports whether a phase is terminal. */
|
|
73
|
-
export function issandboxterminal(phase) {
|
|
74
|
-
return SANDBOXTERMINALPHASES.includes(phase);
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* creates the lifecycle queue. the queue validates every transition,
|
|
78
|
-
* stamps deadlines from the budgets and reaps expired entries on the
|
|
79
|
-
* health-check tick; the host decides when to tick.
|
|
80
|
-
*
|
|
81
|
-
* @param options the budgets and event ring capacity.
|
|
82
|
-
* @returns the queue facade (add, step, cancel, healthcheck, status,
|
|
83
|
-
* list, events, size).
|
|
84
|
-
*/
|
|
85
|
-
export function createsandboxqueue(options) {
|
|
86
|
-
const budgets = options?.budgets ?? {
|
|
87
|
-
creating: 125,
|
|
88
|
-
starting: 125,
|
|
89
|
-
stopping: 30000,
|
|
90
|
-
};
|
|
91
|
-
const eventcap = options?.eventcap ?? 100;
|
|
92
|
-
const entries = new Map();
|
|
93
|
-
const ring = [];
|
|
94
|
-
/** stamps one event into the bounded ring. */
|
|
95
|
-
function record(id, from, to, at, reason) {
|
|
96
|
-
ring.push({ id, from, to, at, reason });
|
|
97
|
-
if (ring.length > eventcap) {
|
|
98
|
-
ring.splice(0, ring.length - eventcap);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/** resolves the deadline of a phase from the budget table. */
|
|
102
|
-
function deadlinefor(state, now) {
|
|
103
|
-
const budget = budgets[state];
|
|
104
|
-
return typeof budget === 'number' && budget > 0 ? now + budget : null;
|
|
105
|
-
}
|
|
106
|
-
function statusOf(entry, now) {
|
|
107
|
-
return {
|
|
108
|
-
id: entry.id,
|
|
109
|
-
state: entry.state,
|
|
110
|
-
enteredat: entry.enteredat,
|
|
111
|
-
deadline: entry.deadline,
|
|
112
|
-
age: Math.max(0, now - entry.enteredat),
|
|
113
|
-
terminal: issandboxterminal(entry.state),
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
return {
|
|
117
|
-
add(id, now = Date.now()) {
|
|
118
|
-
if (entries.has(id)) {
|
|
119
|
-
throw new sandboxqueueerror('duplicate-sandbox', id, null, null);
|
|
120
|
-
}
|
|
121
|
-
const entry = {
|
|
122
|
-
id,
|
|
123
|
-
state: 'creating',
|
|
124
|
-
enteredat: now,
|
|
125
|
-
deadline: deadlinefor('creating', now),
|
|
126
|
-
};
|
|
127
|
-
entries.set(id, entry);
|
|
128
|
-
record(id, 'creating', 'creating', now, 'requested');
|
|
129
|
-
return entry;
|
|
130
|
-
},
|
|
131
|
-
step(id, to, now = Date.now()) {
|
|
132
|
-
const entry = entries.get(id);
|
|
133
|
-
if (entry === undefined) {
|
|
134
|
-
throw new sandboxqueueerror('unknown-sandbox', id, null, to);
|
|
135
|
-
}
|
|
136
|
-
if (!cansandboxstep(entry.state, to)) {
|
|
137
|
-
if (issandboxterminal(entry.state)) {
|
|
138
|
-
throw new sandboxqueueerror('terminal-phase', id, entry.state, to, `sandbox ${id} is terminal (${entry.state}); ${to} is unreachable`);
|
|
139
|
-
}
|
|
140
|
-
throw new sandboxqueueerror('invalid-transition', id, entry.state, to, `sandbox ${id} cannot transition ${entry.state} -> ${to}`);
|
|
141
|
-
}
|
|
142
|
-
const from = entry.state;
|
|
143
|
-
entry.state = to;
|
|
144
|
-
entry.enteredat = now;
|
|
145
|
-
entry.deadline = deadlinefor(to, now);
|
|
146
|
-
record(id, from, to, now, 'requested');
|
|
147
|
-
return entry;
|
|
148
|
-
},
|
|
149
|
-
cancel(id, now = Date.now()) {
|
|
150
|
-
const entry = entries.get(id);
|
|
151
|
-
if (entry === undefined) {
|
|
152
|
-
throw new sandboxqueueerror('unknown-sandbox', id, null, 'cancelled');
|
|
153
|
-
}
|
|
154
|
-
const from = entry.state;
|
|
155
|
-
if (!cansandboxstep(from, 'cancelled')) {
|
|
156
|
-
if (issandboxterminal(from)) {
|
|
157
|
-
throw new sandboxqueueerror('terminal-phase', id, from, 'cancelled', `sandbox ${id} is terminal (${from}); cancellation is unreachable`);
|
|
158
|
-
}
|
|
159
|
-
throw new sandboxqueueerror('invalid-transition', id, from, 'cancelled', `sandbox ${id} cannot be cancelled from ${from}`);
|
|
160
|
-
}
|
|
161
|
-
entry.state = 'cancelled';
|
|
162
|
-
entry.enteredat = now;
|
|
163
|
-
entry.deadline = null;
|
|
164
|
-
record(id, from, 'cancelled', now, 'cancel');
|
|
165
|
-
return entry;
|
|
166
|
-
},
|
|
167
|
-
healthcheck(now = Date.now()) {
|
|
168
|
-
const reaped = [];
|
|
169
|
-
for (const entry of entries.values()) {
|
|
170
|
-
if (entry.deadline === null || entry.deadline > now || issandboxterminal(entry.state)) {
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
const from = entry.state;
|
|
174
|
-
entry.state = 'timedout';
|
|
175
|
-
entry.enteredat = now;
|
|
176
|
-
entry.deadline = null;
|
|
177
|
-
record(entry.id, from, 'timedout', now, 'timeout');
|
|
178
|
-
reaped.push(entry.id);
|
|
179
|
-
}
|
|
180
|
-
return reaped;
|
|
181
|
-
},
|
|
182
|
-
status(id, now = Date.now()) {
|
|
183
|
-
const entry = entries.get(id);
|
|
184
|
-
if (entry === undefined) {
|
|
185
|
-
throw new sandboxqueueerror('unknown-sandbox', id, null, null);
|
|
186
|
-
}
|
|
187
|
-
return statusOf(entry, now);
|
|
188
|
-
},
|
|
189
|
-
list(now = Date.now()) {
|
|
190
|
-
return [...entries.values()]
|
|
191
|
-
.map((entry) => statusOf(entry, now))
|
|
192
|
-
.sort((a, b) => (a.id < b.id ? -1 : 1));
|
|
193
|
-
},
|
|
194
|
-
events() {
|
|
195
|
-
return [...ring];
|
|
196
|
-
},
|
|
197
|
-
size() {
|
|
198
|
-
return entries.size;
|
|
199
|
-
},
|
|
200
|
-
};
|
|
201
|
-
}
|
package/dist/sandboxquota.d.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* sandboxquota.ts — the per-account resource quota ledger of saddle.
|
|
3
|
-
*
|
|
4
|
-
* every account holds counters of the resources its live sandboxes
|
|
5
|
-
* allocate (sandbox count, vcpus, ramgb, vgpus, diskgb) against a
|
|
6
|
-
* limits table. the limits are a plain parameter — the operator's plan
|
|
7
|
-
* table comes from config or the database and never lives in code; an
|
|
8
|
-
* absent limit means unlimited, mirroring the SADDLE_MAX_SANDBOXES
|
|
9
|
-
* convention of webserver.ts (0 or unset means unlimited).
|
|
10
|
-
*
|
|
11
|
-
* the ledger keeps reservations and releases in balance with a simple
|
|
12
|
-
* reconciliation: the host projects the authoritative usage (for
|
|
13
|
-
* example from the sqlite sandboxes rows) and reconcile() overwrites
|
|
14
|
-
* the in-memory counters with it, so drift from admin deletes, restarts
|
|
15
|
-
* or crashes self-heals on the next create.
|
|
16
|
-
*
|
|
17
|
-
* the module is pure: zero imports, zero dom, zero node builtins, a
|
|
18
|
-
* Map per account, typed traceable errors (quotaexceedederror carries
|
|
19
|
-
* the resource, the request, the limit and the remaining headroom).
|
|
20
|
-
*
|
|
21
|
-
* competitor sources shaping the contract: daytonaio/daytona (tenant
|
|
22
|
-
* resource quota admission with per-sandbox cpu/memory/gpu quota
|
|
23
|
-
* fields and the gpu allocator acquire/release pair around create) and
|
|
24
|
-
* opensandbox-group/opensandbox (namespace ResourceQuota admission —
|
|
25
|
-
* the sandbox is not admitted when the quota is exhausted — and the
|
|
26
|
-
* pool acquisition that fails fast when capacity stays unavailable).
|
|
27
|
-
*
|
|
28
|
-
* wire point: webserver.ts createsandbox reconciles the owning
|
|
29
|
-
* account from store.listsandboxesbyuser rows and reserves the new
|
|
30
|
-
* sandbox; destroysandbox releases it. limits come from
|
|
31
|
-
* accountlimitsfromenv(process.env) and stay unlimited when unset.
|
|
32
|
-
*/
|
|
33
|
-
/** the resource counters one account allocates through live sandboxes. */
|
|
34
|
-
export type accountusage = {
|
|
35
|
-
sandboxes: number;
|
|
36
|
-
vcpus: number;
|
|
37
|
-
ramgb: number;
|
|
38
|
-
vgpus: number;
|
|
39
|
-
diskgb: number;
|
|
40
|
-
};
|
|
41
|
-
/** the per-account limits; an absent or non-positive field is unlimited. */
|
|
42
|
-
export type accountlimits = {
|
|
43
|
-
readonly sandboxes?: number;
|
|
44
|
-
readonly vcpus?: number;
|
|
45
|
-
readonly ramgb?: number;
|
|
46
|
-
readonly vgpus?: number;
|
|
47
|
-
readonly diskgb?: number;
|
|
48
|
-
};
|
|
49
|
-
/** one resource wish (a partial usage delta). */
|
|
50
|
-
export type resourcewish = Partial<accountusage>;
|
|
51
|
-
/** failure codes carried by quotaerror. */
|
|
52
|
-
export type quotaerrorcode = 'invalid-wish' | 'quota-exceeded';
|
|
53
|
-
/** the typed quota failure, traceable to the account and the resource. */
|
|
54
|
-
export declare class quotaerror extends Error {
|
|
55
|
-
/** machine readable failure code. */
|
|
56
|
-
readonly code: quotaerrorcode;
|
|
57
|
-
/** the account the failure belongs to. */
|
|
58
|
-
readonly accountid: string;
|
|
59
|
-
/** the resource that failed (or 'wish' for malformed deltas). */
|
|
60
|
-
readonly resource: string;
|
|
61
|
-
/** the requested amount (null when the wish itself was malformed). */
|
|
62
|
-
readonly requested: number | null;
|
|
63
|
-
/** the active limit (null when unlimited). */
|
|
64
|
-
readonly limit: number | null;
|
|
65
|
-
/** the headroom the account had before the request. */
|
|
66
|
-
readonly available: number | null;
|
|
67
|
-
constructor(code: quotaerrorcode, accountid: string, resource: string, requested: number | null, limit: number | null, available: number | null, message?: string);
|
|
68
|
-
}
|
|
69
|
-
/** a read-only snapshot answered by usage(). */
|
|
70
|
-
export type accountquotasnapshot = {
|
|
71
|
-
readonly accountid: string;
|
|
72
|
-
readonly used: Readonly<accountusage>;
|
|
73
|
-
readonly remaining: Readonly<Record<keyof accountusage, number | null>>;
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* creates the per-account ledger with one limits table for every
|
|
77
|
-
* account (per-plan tables ride on the caller passing the plan row of
|
|
78
|
-
* the account).
|
|
79
|
-
*
|
|
80
|
-
* @param limits the limits table (absent fields are unlimited).
|
|
81
|
-
* @returns the ledger facade (admits, reserve, release, usage,
|
|
82
|
-
* reconcile, reset).
|
|
83
|
-
*/
|
|
84
|
-
export declare function createaccountledger(limits: accountlimits): {
|
|
85
|
-
admits: (accountid: string, wish: resourcewish) => boolean;
|
|
86
|
-
reserve: (accountid: string, wish: resourcewish) => accountquotasnapshot;
|
|
87
|
-
release: (accountid: string, wish: resourcewish) => accountquotasnapshot;
|
|
88
|
-
usage: (accountid: string) => accountquotasnapshot;
|
|
89
|
-
reconcile: (accountid: string, observed: accountusage) => {
|
|
90
|
-
before: accountquotasnapshot;
|
|
91
|
-
after: accountquotasnapshot;
|
|
92
|
-
};
|
|
93
|
-
reset: (accountid?: string) => void;
|
|
94
|
-
};
|
|
95
|
-
/** the structural row shape the projection reads (SandboxRow of db.ts
|
|
96
|
-
* satisfies it; kept structural so the module stays import-free). */
|
|
97
|
-
export type sandboxrowlike = {
|
|
98
|
-
readonly vcpus: number | null;
|
|
99
|
-
readonly ramgb: number | null;
|
|
100
|
-
readonly state: string | null;
|
|
101
|
-
};
|
|
102
|
-
/**
|
|
103
|
-
* projects sandbox rows onto one account usage: every row that is not
|
|
104
|
-
* destroyed counts as a live allocation (the same semantics the
|
|
105
|
-
* SADDLE_MAX_SANDBOXES cap check of webserver.ts uses).
|
|
106
|
-
*
|
|
107
|
-
* @param rows the authoritative rows of one account.
|
|
108
|
-
* @returns the usage counters the rows allocate.
|
|
109
|
-
*/
|
|
110
|
-
export declare function usagefromrows(rows: readonly sandboxrowlike[]): accountusage;
|
|
111
|
-
/**
|
|
112
|
-
* reads the per-account limits from the environment: SADDLE_MAX_SANDBOXES
|
|
113
|
-
* (the existing count cap of webserver.ts), SADDLE_ACCOUNT_MAX_VCPUS,
|
|
114
|
-
* SADDLE_ACCOUNT_MAX_RAM_GB, SADDLE_ACCOUNT_MAX_VGPUS and
|
|
115
|
-
* SADDLE_ACCOUNT_MAX_DISK_GB. unset, zero and negative values mean
|
|
116
|
-
* unlimited, so the default deployment enforces nothing.
|
|
117
|
-
*
|
|
118
|
-
* @param env the environment record (process.env shaped).
|
|
119
|
-
* @returns the limits table.
|
|
120
|
-
*/
|
|
121
|
-
export declare function accountlimitsfromenv(env: Record<string, string | undefined>): accountlimits;
|
package/dist/sandboxquota.js
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* sandboxquota.ts — the per-account resource quota ledger of saddle.
|
|
3
|
-
*
|
|
4
|
-
* every account holds counters of the resources its live sandboxes
|
|
5
|
-
* allocate (sandbox count, vcpus, ramgb, vgpus, diskgb) against a
|
|
6
|
-
* limits table. the limits are a plain parameter — the operator's plan
|
|
7
|
-
* table comes from config or the database and never lives in code; an
|
|
8
|
-
* absent limit means unlimited, mirroring the SADDLE_MAX_SANDBOXES
|
|
9
|
-
* convention of webserver.ts (0 or unset means unlimited).
|
|
10
|
-
*
|
|
11
|
-
* the ledger keeps reservations and releases in balance with a simple
|
|
12
|
-
* reconciliation: the host projects the authoritative usage (for
|
|
13
|
-
* example from the sqlite sandboxes rows) and reconcile() overwrites
|
|
14
|
-
* the in-memory counters with it, so drift from admin deletes, restarts
|
|
15
|
-
* or crashes self-heals on the next create.
|
|
16
|
-
*
|
|
17
|
-
* the module is pure: zero imports, zero dom, zero node builtins, a
|
|
18
|
-
* Map per account, typed traceable errors (quotaexceedederror carries
|
|
19
|
-
* the resource, the request, the limit and the remaining headroom).
|
|
20
|
-
*
|
|
21
|
-
* competitor sources shaping the contract: daytonaio/daytona (tenant
|
|
22
|
-
* resource quota admission with per-sandbox cpu/memory/gpu quota
|
|
23
|
-
* fields and the gpu allocator acquire/release pair around create) and
|
|
24
|
-
* opensandbox-group/opensandbox (namespace ResourceQuota admission —
|
|
25
|
-
* the sandbox is not admitted when the quota is exhausted — and the
|
|
26
|
-
* pool acquisition that fails fast when capacity stays unavailable).
|
|
27
|
-
*
|
|
28
|
-
* wire point: webserver.ts createsandbox reconciles the owning
|
|
29
|
-
* account from store.listsandboxesbyuser rows and reserves the new
|
|
30
|
-
* sandbox; destroysandbox releases it. limits come from
|
|
31
|
-
* accountlimitsfromenv(process.env) and stay unlimited when unset.
|
|
32
|
-
*/
|
|
33
|
-
/** the resource names tracked by the ledger. */
|
|
34
|
-
const RESOURCES = [
|
|
35
|
-
'sandboxes',
|
|
36
|
-
'vcpus',
|
|
37
|
-
'ramgb',
|
|
38
|
-
'vgpus',
|
|
39
|
-
'diskgb',
|
|
40
|
-
];
|
|
41
|
-
/** the typed quota failure, traceable to the account and the resource. */
|
|
42
|
-
export class quotaerror extends Error {
|
|
43
|
-
/** machine readable failure code. */
|
|
44
|
-
code;
|
|
45
|
-
/** the account the failure belongs to. */
|
|
46
|
-
accountid;
|
|
47
|
-
/** the resource that failed (or 'wish' for malformed deltas). */
|
|
48
|
-
resource;
|
|
49
|
-
/** the requested amount (null when the wish itself was malformed). */
|
|
50
|
-
requested;
|
|
51
|
-
/** the active limit (null when unlimited). */
|
|
52
|
-
limit;
|
|
53
|
-
/** the headroom the account had before the request. */
|
|
54
|
-
available;
|
|
55
|
-
constructor(code, accountid, resource, requested, limit, available, message) {
|
|
56
|
-
super(message ??
|
|
57
|
-
`account ${accountid} ${code} on ${resource}: requested ${requested ?? '?'} of ${limit ?? 'unlimited'} (${available ?? '?'} available)`);
|
|
58
|
-
this.name = 'quotaerror';
|
|
59
|
-
this.code = code;
|
|
60
|
-
this.accountid = accountid;
|
|
61
|
-
this.resource = resource;
|
|
62
|
-
this.requested = requested;
|
|
63
|
-
this.limit = limit;
|
|
64
|
-
this.available = available;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/* ------------------------------------------------------------------ */
|
|
68
|
-
/* helpers */
|
|
69
|
-
/* ------------------------------------------------------------------ */
|
|
70
|
-
/** validates one resource wish: every present field is a positive number. */
|
|
71
|
-
function sanitize(wish) {
|
|
72
|
-
const clean = {};
|
|
73
|
-
for (const resource of RESOURCES) {
|
|
74
|
-
const value = wish[resource];
|
|
75
|
-
if (value === undefined) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
|
|
79
|
-
throw new quotaerror('invalid-wish', '', resource, typeof value === 'number' ? value : null, null, null, `resource ${resource} must be a positive finite number (received ${String(value)})`);
|
|
80
|
-
}
|
|
81
|
-
clean[resource] = value;
|
|
82
|
-
}
|
|
83
|
-
return clean;
|
|
84
|
-
}
|
|
85
|
-
/** headroom of one resource under its limit (null limit means Infinity). */
|
|
86
|
-
function headroom(limit, used) {
|
|
87
|
-
return typeof limit === 'number' && limit > 0 ? Math.max(0, limit - used) : null;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* creates the per-account ledger with one limits table for every
|
|
91
|
-
* account (per-plan tables ride on the caller passing the plan row of
|
|
92
|
-
* the account).
|
|
93
|
-
*
|
|
94
|
-
* @param limits the limits table (absent fields are unlimited).
|
|
95
|
-
* @returns the ledger facade (admits, reserve, release, usage,
|
|
96
|
-
* reconcile, reset).
|
|
97
|
-
*/
|
|
98
|
-
export function createaccountledger(limits) {
|
|
99
|
-
const books = new Map();
|
|
100
|
-
/** zeroes the counters of an unseen account. */
|
|
101
|
-
function blank(accountid) {
|
|
102
|
-
const existing = books.get(accountid);
|
|
103
|
-
if (existing !== undefined) {
|
|
104
|
-
return existing;
|
|
105
|
-
}
|
|
106
|
-
const fresh = { sandboxes: 0, vcpus: 0, ramgb: 0, vgpus: 0, diskgb: 0 };
|
|
107
|
-
books.set(accountid, fresh);
|
|
108
|
-
return fresh;
|
|
109
|
-
}
|
|
110
|
-
function snapshotOf(accountid, used) {
|
|
111
|
-
const remaining = {};
|
|
112
|
-
for (const resource of RESOURCES) {
|
|
113
|
-
remaining[resource] = headroom(limits[resource], used[resource]);
|
|
114
|
-
}
|
|
115
|
-
return { accountid, used: { ...used }, remaining };
|
|
116
|
-
}
|
|
117
|
-
/** the shared admit-or-throw walk used by admits and reserve. */
|
|
118
|
-
function check(accountid, wish) {
|
|
119
|
-
const clean = sanitize(wish);
|
|
120
|
-
const used = blank(accountid);
|
|
121
|
-
for (const resource of RESOURCES) {
|
|
122
|
-
const request = clean[resource];
|
|
123
|
-
if (request === undefined) {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
const room = headroom(limits[resource], used[resource]);
|
|
127
|
-
if (room !== null && request > room) {
|
|
128
|
-
throw new quotaerror('quota-exceeded', accountid, resource, request, limits[resource] ?? null, room);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return clean;
|
|
132
|
-
}
|
|
133
|
-
return {
|
|
134
|
-
admits(accountid, wish) {
|
|
135
|
-
try {
|
|
136
|
-
check(accountid, wish);
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
if (error instanceof quotaerror) {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
throw error;
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
reserve(accountid, wish) {
|
|
147
|
-
const clean = check(accountid, wish);
|
|
148
|
-
const used = blank(accountid);
|
|
149
|
-
for (const resource of RESOURCES) {
|
|
150
|
-
const request = clean[resource];
|
|
151
|
-
if (request !== undefined) {
|
|
152
|
-
used[resource] += request;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return snapshotOf(accountid, used);
|
|
156
|
-
},
|
|
157
|
-
release(accountid, wish) {
|
|
158
|
-
const clean = sanitize(wish);
|
|
159
|
-
const used = blank(accountid);
|
|
160
|
-
for (const resource of RESOURCES) {
|
|
161
|
-
const request = clean[resource];
|
|
162
|
-
if (request !== undefined) {
|
|
163
|
-
used[resource] = Math.max(0, used[resource] - request);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return snapshotOf(accountid, used);
|
|
167
|
-
},
|
|
168
|
-
usage(accountid) {
|
|
169
|
-
return snapshotOf(accountid, blank(accountid));
|
|
170
|
-
},
|
|
171
|
-
reconcile(accountid, observed) {
|
|
172
|
-
const used = blank(accountid);
|
|
173
|
-
const before = snapshotOf(accountid, used);
|
|
174
|
-
for (const resource of RESOURCES) {
|
|
175
|
-
const value = observed[resource];
|
|
176
|
-
used[resource] =
|
|
177
|
-
typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
178
|
-
}
|
|
179
|
-
const after = snapshotOf(accountid, used);
|
|
180
|
-
return { before, after };
|
|
181
|
-
},
|
|
182
|
-
reset(accountid) {
|
|
183
|
-
if (accountid === undefined) {
|
|
184
|
-
books.clear();
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
books.delete(accountid);
|
|
188
|
-
},
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* projects sandbox rows onto one account usage: every row that is not
|
|
193
|
-
* destroyed counts as a live allocation (the same semantics the
|
|
194
|
-
* SADDLE_MAX_SANDBOXES cap check of webserver.ts uses).
|
|
195
|
-
*
|
|
196
|
-
* @param rows the authoritative rows of one account.
|
|
197
|
-
* @returns the usage counters the rows allocate.
|
|
198
|
-
*/
|
|
199
|
-
export function usagefromrows(rows) {
|
|
200
|
-
const usage = { sandboxes: 0, vcpus: 0, ramgb: 0, vgpus: 0, diskgb: 0 };
|
|
201
|
-
for (const row of rows) {
|
|
202
|
-
if (row.state === 'destroyed') {
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
usage.sandboxes += 1;
|
|
206
|
-
usage.vcpus += typeof row.vcpus === 'number' ? row.vcpus : 0;
|
|
207
|
-
usage.ramgb += typeof row.ramgb === 'number' ? row.ramgb : 0;
|
|
208
|
-
}
|
|
209
|
-
return usage;
|
|
210
|
-
}
|
|
211
|
-
/* ------------------------------------------------------------------ */
|
|
212
|
-
/* adapter: limits from the environment */
|
|
213
|
-
/* ------------------------------------------------------------------ */
|
|
214
|
-
/**
|
|
215
|
-
* reads the per-account limits from the environment: SADDLE_MAX_SANDBOXES
|
|
216
|
-
* (the existing count cap of webserver.ts), SADDLE_ACCOUNT_MAX_VCPUS,
|
|
217
|
-
* SADDLE_ACCOUNT_MAX_RAM_GB, SADDLE_ACCOUNT_MAX_VGPUS and
|
|
218
|
-
* SADDLE_ACCOUNT_MAX_DISK_GB. unset, zero and negative values mean
|
|
219
|
-
* unlimited, so the default deployment enforces nothing.
|
|
220
|
-
*
|
|
221
|
-
* @param env the environment record (process.env shaped).
|
|
222
|
-
* @returns the limits table.
|
|
223
|
-
*/
|
|
224
|
-
export function accountlimitsfromenv(env) {
|
|
225
|
-
const positive = (name) => {
|
|
226
|
-
const parsed = Number(env[name]);
|
|
227
|
-
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : undefined;
|
|
228
|
-
};
|
|
229
|
-
return {
|
|
230
|
-
sandboxes: positive('SADDLE_MAX_SANDBOXES'),
|
|
231
|
-
vcpus: positive('SADDLE_ACCOUNT_MAX_VCPUS'),
|
|
232
|
-
ramgb: positive('SADDLE_ACCOUNT_MAX_RAM_GB'),
|
|
233
|
-
vgpus: positive('SADDLE_ACCOUNT_MAX_VGPUS'),
|
|
234
|
-
diskgb: positive('SADDLE_ACCOUNT_MAX_DISK_GB'),
|
|
235
|
-
};
|
|
236
|
-
}
|