@codeworksh/plugin 0.0.1-dev.20260922103030
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/LICENSE +21 -0
- package/README.md +111 -0
- package/error-ClDISWlT.d.mts +11 -0
- package/event-rIZpnOTo.d.mts +895 -0
- package/event.d.mts +2 -0
- package/event.mjs +125 -0
- package/event.mjs.map +1 -0
- package/filesystem-Cp-KapCE.d.mts +142 -0
- package/ids-CHjmAdTc.d.mts +43 -0
- package/ids.d.mts +2 -0
- package/ids.mjs +48 -0
- package/ids.mjs.map +1 -0
- package/index.d.mts +3 -0
- package/index.mjs +3 -0
- package/io-BYs7hbiY.d.mts +497 -0
- package/location-D4hAfuXF.d.mts +61 -0
- package/location.d.mts +2 -0
- package/location.mjs +48 -0
- package/location.mjs.map +1 -0
- package/package.json +200 -0
- package/plugin/prompt.d.mts +2 -0
- package/plugin/prompt.mjs +7 -0
- package/plugin/prompt.mjs.map +1 -0
- package/plugin/tool.d.mts +2 -0
- package/plugin/tool.mjs +29 -0
- package/plugin/tool.mjs.map +1 -0
- package/plugin-DbJEpY3M.d.mts +140 -0
- package/plugin-lorpq7Oi.mjs +41 -0
- package/plugin-lorpq7Oi.mjs.map +1 -0
- package/plugin.d.mts +4 -0
- package/plugin.mjs +4 -0
- package/posix.d.mts +6 -0
- package/posix.mjs +8 -0
- package/posix.mjs.map +1 -0
- package/project-CLGsIFD-.d.mts +21 -0
- package/project.d.mts +2 -0
- package/project.mjs +22 -0
- package/project.mjs.map +1 -0
- package/prompt-BcpagjGh.d.mts +10 -0
- package/repo.d.mts +42 -0
- package/repo.mjs +46 -0
- package/repo.mjs.map +1 -0
- package/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/sandbox/driver.d.mts +2 -0
- package/sandbox/driver.mjs +63 -0
- package/sandbox/driver.mjs.map +1 -0
- package/sandbox/error.d.mts +7 -0
- package/sandbox/error.mjs +15 -0
- package/sandbox/error.mjs.map +1 -0
- package/sandbox/errors.d.mts +2 -0
- package/sandbox/errors.mjs +160 -0
- package/sandbox/errors.mjs.map +1 -0
- package/sandbox/filesystem.d.mts +2 -0
- package/sandbox/filesystem.mjs +142 -0
- package/sandbox/filesystem.mjs.map +1 -0
- package/sandbox/instance.d.mts +2 -0
- package/sandbox/instance.mjs +144 -0
- package/sandbox/instance.mjs.map +1 -0
- package/sandbox/io.d.mts +2 -0
- package/sandbox/io.mjs +127 -0
- package/sandbox/io.mjs.map +1 -0
- package/sandbox/resource.d.mts +28 -0
- package/sandbox/resource.mjs +29 -0
- package/sandbox/resource.mjs.map +1 -0
- package/sandbox/shell.d.mts +2 -0
- package/sandbox/shell.mjs +93 -0
- package/sandbox/shell.mjs.map +1 -0
- package/sandbox.d.mts +17 -0
- package/sandbox.mjs +39 -0
- package/sandbox.mjs.map +1 -0
- package/schema.d.mts +91 -0
- package/schema.mjs +105 -0
- package/schema.mjs.map +1 -0
- package/settings-CYL6yyDD.d.mts +193 -0
- package/settings.d.mts +2 -0
- package/settings.mjs +102 -0
- package/settings.mjs.map +1 -0
- package/shell-CZ6Y4lNU.d.mts +109 -0
- package/space-CR-9UxdT.d.mts +25 -0
- package/space.d.mts +2 -0
- package/space.mjs +33 -0
- package/space.mjs.map +1 -0
- package/tool/error.d.mts +2 -0
- package/tool/error.mjs +11 -0
- package/tool/error.mjs.map +1 -0
- package/tool/progress.d.mts +2 -0
- package/tool/progress.mjs +11 -0
- package/tool/progress.mjs.map +1 -0
- package/tool-BoVqkYvL.d.mts +287 -0
- package/tool-DuYBgiJf.d.mts +142 -0
- package/tool.d.mts +2 -0
- package/tool.mjs +71 -0
- package/tool.mjs.map +1 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { t as __exportAll } from "../rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { withStatics } from "../schema.mjs";
|
|
3
|
+
import { Option, Schema } from "effect";
|
|
4
|
+
import { uuidv7 } from "uuidv7";
|
|
5
|
+
//#region src/sandbox/instance.ts
|
|
6
|
+
var instance_exports = /* @__PURE__ */ __exportAll({
|
|
7
|
+
ID: () => ID,
|
|
8
|
+
Kind: () => Kind,
|
|
9
|
+
Ownership: () => Ownership,
|
|
10
|
+
PersistedError: () => PersistedError,
|
|
11
|
+
SandboxInstance: () => instance_exports,
|
|
12
|
+
Status: () => Status,
|
|
13
|
+
Usage: () => Usage,
|
|
14
|
+
fromColumn: () => fromColumn,
|
|
15
|
+
fromField: () => fromField,
|
|
16
|
+
isMountable: () => isMountable,
|
|
17
|
+
mountable: () => mountable,
|
|
18
|
+
toColumn: () => toColumn,
|
|
19
|
+
toField: () => toField
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* A Sandbox instance is a **durable filesystem namespace** plus whatever compute
|
|
23
|
+
* acts on it — a device in Unix terms, which exists whether or not anything has
|
|
24
|
+
* it mounted. `Sandbox.Controller` is the only thing that creates,
|
|
25
|
+
* stops, or destroys one; this module is just its identity and state model.
|
|
26
|
+
*
|
|
27
|
+
* The application ID is deliberately separate from the driver's own resource
|
|
28
|
+
* locator: callers never parse a Vercel name or a Daytona ID, driver formats may
|
|
29
|
+
* change, and a destroyed resource must stay identifiable in Project/Session
|
|
30
|
+
* history. A missing resource is never recreated under an existing ID — a new
|
|
31
|
+
* resource is a new namespace and therefore a new ID.
|
|
32
|
+
*/
|
|
33
|
+
const ID = Schema.String.pipe(Schema.brand("SandboxInstance.ID"), withStatics((schema) => ({
|
|
34
|
+
/**
|
|
35
|
+
* The host. Reserved, and never written to a column — see {@link toColumn}.
|
|
36
|
+
* It exists at runtime so nothing has to branch on "is this the host": it is
|
|
37
|
+
* what identity reads, what logs show, and what the transport cache keys on.
|
|
38
|
+
*/
|
|
39
|
+
local: schema.make("local"),
|
|
40
|
+
/** A fresh identity for a namespace nothing has named yet. */
|
|
41
|
+
create: () => schema.make(`sbx_${uuidv7()}`)
|
|
42
|
+
})));
|
|
43
|
+
/**
|
|
44
|
+
* The storage boundary for namespace references, in one place.
|
|
45
|
+
*
|
|
46
|
+
* The host filesystem exists whether or not a row describes it, so it gets no
|
|
47
|
+
* row and `NULL` is the only spelling of it — the Unix analogue is exact, since
|
|
48
|
+
* `/` has no entry in the mount table you consult to find other mounts. That
|
|
49
|
+
* makes a session or directory writable before any namespace is registered (the
|
|
50
|
+
* foreign key is skipped on NULL), makes `SET sandbox_instance_id = NULL` a
|
|
51
|
+
* meaningful "revert to the host", and leaves the host impossible to tombstone,
|
|
52
|
+
* collect, or destroy, because there is nothing to point at.
|
|
53
|
+
*
|
|
54
|
+
* Two SQLite consequences ride on this and break correctness silently if missed:
|
|
55
|
+
* unique indexes treat NULLs as distinct, so every uniqueness constraint
|
|
56
|
+
* spanning a namespace column coalesces to `'local'`; and `= NULL` never
|
|
57
|
+
* matches, so namespace-scoped reads use `IS`.
|
|
58
|
+
*/
|
|
59
|
+
const toColumn = (id) => id === ID.local ? null : id;
|
|
60
|
+
const fromColumn = (value) => value === null ? ID.local : ID.make(value);
|
|
61
|
+
/**
|
|
62
|
+
* The same mapping for row models, whose optional columns are `Option` rather
|
|
63
|
+
* than `null`. Kept beside {@link toColumn} so the boundary stays one place:
|
|
64
|
+
* `toColumn`/`fromColumn` for SQL parameters, these for `Model.FieldOption`.
|
|
65
|
+
*/
|
|
66
|
+
const toField = (id) => id === ID.local ? Option.none() : Option.some(id);
|
|
67
|
+
const fromField = (value) => Option.getOrElse(value, () => ID.local);
|
|
68
|
+
/**
|
|
69
|
+
* Filesystem-class taxonomy, mirroring Unix: disk, tmpfs/procfs, NFS/CIFS.
|
|
70
|
+
* Stored on the row rather than derived from the registered driver, so reading
|
|
71
|
+
* an instance never depends on the registry — which matters most when a driver
|
|
72
|
+
* is *not* configured and you need to list or clean up its rows.
|
|
73
|
+
*/
|
|
74
|
+
const Kind = Schema.Literals([
|
|
75
|
+
"local",
|
|
76
|
+
"virtual",
|
|
77
|
+
"remote"
|
|
78
|
+
]);
|
|
79
|
+
const Ownership = Schema.Literals(["managed", "external"]);
|
|
80
|
+
/**
|
|
81
|
+
* Lifecycle state, in ZFS pool vocabulary. This is the **last observed** value,
|
|
82
|
+
* not live driver truth: Daytona auto-stops and auto-archives, Vercel sandboxes
|
|
83
|
+
* expire on their own timeout, so drift is normal. `stateObservedAt` carries the
|
|
84
|
+
* freshness and `Controller.refresh` updates it without waking anything.
|
|
85
|
+
*
|
|
86
|
+
* `removed` and `unavail` are deliberately distinct. `removed` means we deleted
|
|
87
|
+
* it; `unavail` means the driver claims it is gone. A "not found" is frequently a
|
|
88
|
+
* misclassification — wrong region, wrong API url, a revoked key answering 404,
|
|
89
|
+
* eventual consistency right after create — so it must never be recorded as if we
|
|
90
|
+
* had destroyed the resource ourselves.
|
|
91
|
+
*/
|
|
92
|
+
const Status = Schema.Literals([
|
|
93
|
+
"provisioning",
|
|
94
|
+
"online",
|
|
95
|
+
"offline",
|
|
96
|
+
"suspending",
|
|
97
|
+
"removing",
|
|
98
|
+
"removed",
|
|
99
|
+
"unavail",
|
|
100
|
+
"faulted"
|
|
101
|
+
]);
|
|
102
|
+
/**
|
|
103
|
+
* The statuses a `mount` may proceed from. This is the predicate every
|
|
104
|
+
* conditional write depends on, so it is enumerated once here rather than
|
|
105
|
+
* restated as prose at each call site.
|
|
106
|
+
*
|
|
107
|
+
* `offline` qualifies because mounting wakes.
|
|
108
|
+
* `faulted` qualifies because a fault is a *usability* condition, not an identity one — see {@link Status}.
|
|
109
|
+
*
|
|
110
|
+
* There is no `resuming`: it would exist to be observed by nothing, since
|
|
111
|
+
* mounting wakes, `offline` is already mountable, and waking is not destructive
|
|
112
|
+
* so it needs no claim. `suspending` and `removing` stay because they *are*
|
|
113
|
+
* compare-and-set claims, blocking a concurrent mount mid-destruction.
|
|
114
|
+
*/
|
|
115
|
+
const mountable = /* @__PURE__ */ new Set([
|
|
116
|
+
"online",
|
|
117
|
+
"offline",
|
|
118
|
+
"faulted"
|
|
119
|
+
]);
|
|
120
|
+
const isMountable = (status) => mountable.has(status);
|
|
121
|
+
/**
|
|
122
|
+
* Reference state, derived — never stored. `busy` carries its `umount` meaning:
|
|
123
|
+
* something holds this and destruction must not proceed unforced.
|
|
124
|
+
*
|
|
125
|
+
* `pinned` is the kernel sense of the word: never reclaimable. It short-circuits
|
|
126
|
+
* counting entirely for instances that cannot be stopped or destroyed (the local
|
|
127
|
+
* host), which is what keeps them out of any future collector by construction
|
|
128
|
+
* rather than by an ownership check happening to catch them.
|
|
129
|
+
*/
|
|
130
|
+
const Usage = Schema.Literals([
|
|
131
|
+
"idle",
|
|
132
|
+
"busy",
|
|
133
|
+
"pinned"
|
|
134
|
+
]);
|
|
135
|
+
/** Sanitized driver failure. The only error shape allowed to be persisted or logged. */
|
|
136
|
+
const PersistedError = Schema.Struct({
|
|
137
|
+
name: Schema.String,
|
|
138
|
+
message: Schema.String,
|
|
139
|
+
code: Schema.optional(Schema.String)
|
|
140
|
+
});
|
|
141
|
+
//#endregion
|
|
142
|
+
export { ID, Kind, Ownership, PersistedError, instance_exports as SandboxInstance, Status, Usage, fromColumn, fromField, isMountable, mountable, toColumn, toField };
|
|
143
|
+
|
|
144
|
+
//# sourceMappingURL=instance.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance.mjs","names":[],"sources":["../../../src/sandbox/instance.ts"],"sourcesContent":["import { Option, Schema } from \"effect\";\nimport { uuidv7 } from \"uuidv7\";\nimport { withStatics } from \"../schema.ts\";\nimport type { SandboxDriver } from \"./driver.ts\";\n\n/**\n * A Sandbox instance is a **durable filesystem namespace** plus whatever compute\n * acts on it — a device in Unix terms, which exists whether or not anything has\n * it mounted. `Sandbox.Controller` is the only thing that creates,\n * stops, or destroys one; this module is just its identity and state model.\n *\n * The application ID is deliberately separate from the driver's own resource\n * locator: callers never parse a Vercel name or a Daytona ID, driver formats may\n * change, and a destroyed resource must stay identifiable in Project/Session\n * history. A missing resource is never recreated under an existing ID — a new\n * resource is a new namespace and therefore a new ID.\n */\n\nexport const ID = Schema.String.pipe(\n\tSchema.brand(\"SandboxInstance.ID\"),\n\twithStatics((schema) => ({\n\t\t/**\n\t\t * The host. Reserved, and never written to a column — see {@link toColumn}.\n\t\t * It exists at runtime so nothing has to branch on \"is this the host\": it is\n\t\t * what identity reads, what logs show, and what the transport cache keys on.\n\t\t */\n\t\tlocal: schema.make(\"local\"),\n\t\t/** A fresh identity for a namespace nothing has named yet. */\n\t\tcreate: () => schema.make(`sbx_${uuidv7()}`),\n\t})),\n);\nexport type ID = typeof ID.Type;\n\n/**\n * The storage boundary for namespace references, in one place.\n *\n * The host filesystem exists whether or not a row describes it, so it gets no\n * row and `NULL` is the only spelling of it — the Unix analogue is exact, since\n * `/` has no entry in the mount table you consult to find other mounts. That\n * makes a session or directory writable before any namespace is registered (the\n * foreign key is skipped on NULL), makes `SET sandbox_instance_id = NULL` a\n * meaningful \"revert to the host\", and leaves the host impossible to tombstone,\n * collect, or destroy, because there is nothing to point at.\n *\n * Two SQLite consequences ride on this and break correctness silently if missed:\n * unique indexes treat NULLs as distinct, so every uniqueness constraint\n * spanning a namespace column coalesces to `'local'`; and `= NULL` never\n * matches, so namespace-scoped reads use `IS`.\n */\nexport const toColumn = (id: ID): string | null => (id === ID.local ? null : id);\nexport const fromColumn = (value: string | null): ID => (value === null ? ID.local : ID.make(value));\n\n/**\n * The same mapping for row models, whose optional columns are `Option` rather\n * than `null`. Kept beside {@link toColumn} so the boundary stays one place:\n * `toColumn`/`fromColumn` for SQL parameters, these for `Model.FieldOption`.\n */\nexport const toField = (id: ID): Option.Option<ID> => (id === ID.local ? Option.none() : Option.some(id));\nexport const fromField = (value: Option.Option<ID>): ID => Option.getOrElse(value, () => ID.local);\n\n/**\n * Filesystem-class taxonomy, mirroring Unix: disk, tmpfs/procfs, NFS/CIFS.\n * Stored on the row rather than derived from the registered driver, so reading\n * an instance never depends on the registry — which matters most when a driver\n * is *not* configured and you need to list or clean up its rows.\n */\nexport const Kind = Schema.Literals([\"local\", \"virtual\", \"remote\"]);\nexport type Kind = typeof Kind.Type;\n\nexport const Ownership = Schema.Literals([\"managed\", \"external\"]);\nexport type Ownership = typeof Ownership.Type;\n\n/**\n * Lifecycle state, in ZFS pool vocabulary. This is the **last observed** value,\n * not live driver truth: Daytona auto-stops and auto-archives, Vercel sandboxes\n * expire on their own timeout, so drift is normal. `stateObservedAt` carries the\n * freshness and `Controller.refresh` updates it without waking anything.\n *\n * `removed` and `unavail` are deliberately distinct. `removed` means we deleted\n * it; `unavail` means the driver claims it is gone. A \"not found\" is frequently a\n * misclassification — wrong region, wrong API url, a revoked key answering 404,\n * eventual consistency right after create — so it must never be recorded as if we\n * had destroyed the resource ourselves.\n */\nexport const Status = Schema.Literals([\n\t\"provisioning\",\n\t\"online\",\n\t\"offline\",\n\t\"suspending\",\n\t\"removing\",\n\t\"removed\",\n\t\"unavail\",\n\t\"faulted\",\n]);\nexport type Status = typeof Status.Type;\n\n/**\n * The statuses a `mount` may proceed from. This is the predicate every\n * conditional write depends on, so it is enumerated once here rather than\n * restated as prose at each call site.\n *\n * `offline` qualifies because mounting wakes.\n * `faulted` qualifies because a fault is a *usability* condition, not an identity one — see {@link Status}.\n *\n * There is no `resuming`: it would exist to be observed by nothing, since\n * mounting wakes, `offline` is already mountable, and waking is not destructive\n * so it needs no claim. `suspending` and `removing` stay because they *are*\n * compare-and-set claims, blocking a concurrent mount mid-destruction.\n */\nexport const mountable: ReadonlySet<Status> = new Set<Status>([\"online\", \"offline\", \"faulted\"]);\n\nexport const isMountable = (status: Status): boolean => mountable.has(status);\n\n/**\n * Reference state, derived — never stored. `busy` carries its `umount` meaning:\n * something holds this and destruction must not proceed unforced.\n *\n * `pinned` is the kernel sense of the word: never reclaimable. It short-circuits\n * counting entirely for instances that cannot be stopped or destroyed (the local\n * host), which is what keeps them out of any future collector by construction\n * rather than by an ownership check happening to catch them.\n */\nexport const Usage = Schema.Literals([\"idle\", \"busy\", \"pinned\"]);\nexport type Usage = typeof Usage.Type;\n\n/** Sanitized driver failure. The only error shape allowed to be persisted or logged. */\nexport const PersistedError = Schema.Struct({\n\tname: Schema.String,\n\tmessage: Schema.String,\n\tcode: Schema.optional(Schema.String),\n});\nexport type PersistedError = typeof PersistedError.Type;\n\n/** Durable metadata, safe to return and persist. Assembled by the control plane. */\nexport interface Info {\n\treadonly id: ID;\n\treadonly driver: SandboxDriver.Name;\n\treadonly kind: Kind;\n\treadonly providerResourceId: Option.Option<string>;\n\treadonly ownership: Ownership;\n\treadonly status: Status;\n\treadonly usage: Usage;\n\t/** References held by *this* control plane. Process-local; see the transport cache. */\n\treadonly refCount: number;\n\treadonly providerStatus: Option.Option<string>;\n\treadonly metadata: Readonly<Record<string, string>>;\n\treadonly lastError: Option.Option<PersistedError>;\n\treadonly createdAt: Date;\n\treadonly updatedAt: Date;\n\treadonly stateObservedAt: Date;\n\treadonly lastMountedAt: Option.Option<Date>;\n\treadonly lastUnmountedAt: Option.Option<Date>;\n\treadonly lastUsedAt: Option.Option<Date>;\n\treadonly removedAt: Option.Option<Date>;\n}\n\nexport * as SandboxInstance from \"./instance.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBA,MAAa,KAAK,OAAO,OAAO,KAC/B,OAAO,MAAM,oBAAoB,GACjC,aAAa,YAAY;;;;;;CAMxB,OAAO,OAAO,KAAK,OAAO;;CAE1B,cAAc,OAAO,KAAK,OAAO,OAAO,GAAG;AAC5C,EAAE,CACH;;;;;;;;;;;;;;;;;AAmBA,MAAa,YAAY,OAA2B,OAAO,GAAG,QAAQ,OAAO;AAC7E,MAAa,cAAc,UAA8B,UAAU,OAAO,GAAG,QAAQ,GAAG,KAAK,KAAK;;;;;;AAOlG,MAAa,WAAW,OAA+B,OAAO,GAAG,QAAQ,OAAO,KAAK,IAAI,OAAO,KAAK,EAAE;AACvG,MAAa,aAAa,UAAiC,OAAO,UAAU,aAAa,GAAG,KAAK;;;;;;;AAQjG,MAAa,OAAO,OAAO,SAAS;CAAC;CAAS;CAAW;AAAQ,CAAC;AAGlE,MAAa,YAAY,OAAO,SAAS,CAAC,WAAW,UAAU,CAAC;;;;;;;;;;;;;AAehE,MAAa,SAAS,OAAO,SAAS;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;;;;;;;;;;;;;;AAgBD,MAAa,4BAAiC,IAAI,IAAY;CAAC;CAAU;CAAW;AAAS,CAAC;AAE9F,MAAa,eAAe,WAA4B,UAAU,IAAI,MAAM;;;;;;;;;;AAW5E,MAAa,QAAQ,OAAO,SAAS;CAAC;CAAQ;CAAQ;AAAQ,CAAC;;AAI/D,MAAa,iBAAiB,OAAO,OAAO;CAC3C,MAAM,OAAO;CACb,SAAS,OAAO;CAChB,MAAM,OAAO,SAAS,OAAO,MAAM;AACpC,CAAC"}
|
package/sandbox/io.d.mts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as Provides, c as hostLayer, d as mount, f as remote, i as Layer, l as identityLayer, m as virtual, n as FileSystem, o as Shell, p as resolveMountCwd, r as Identity, s as host, t as Current, u as io_d_exports } from "../io-BYs7hbiY.mjs";
|
|
2
|
+
export { Current, FileSystem, Identity, Layer, Provides, io_d_exports as SandboxIO, Shell, host, hostLayer, identityLayer, mount, remote, resolveMountCwd, virtual };
|
package/sandbox/io.mjs
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { t as __exportAll } from "../rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { ID } from "./instance.mjs";
|
|
3
|
+
import { posix } from "../posix.mjs";
|
|
4
|
+
import { Service, withCwd } from "./filesystem.mjs";
|
|
5
|
+
import { Shell as Shell$1, withCwd as withCwd$1 } from "./shell.mjs";
|
|
6
|
+
import { Context, Effect, Layer } from "effect";
|
|
7
|
+
//#region src/sandbox/io.ts
|
|
8
|
+
var io_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
Current: () => Current,
|
|
10
|
+
FileSystem: () => FileSystem,
|
|
11
|
+
SandboxIO: () => io_exports,
|
|
12
|
+
Shell: () => Shell,
|
|
13
|
+
host: () => host,
|
|
14
|
+
hostLayer: () => hostLayer,
|
|
15
|
+
identityLayer: () => identityLayer,
|
|
16
|
+
mount: () => mount,
|
|
17
|
+
remote: () => remote,
|
|
18
|
+
resolveMountCwd: () => resolveMountCwd,
|
|
19
|
+
virtual: () => virtual
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* `SandboxIO` is a **mount**: a filesystem, a shell, and the identity and
|
|
23
|
+
* working directory they act on.
|
|
24
|
+
*
|
|
25
|
+
* It is the whole vocabulary a consumer needs. Project, Git, Copy, Location, and
|
|
26
|
+
* every tool ask for `SandboxIO.FileSystem`, `SandboxIO.Shell`, and
|
|
27
|
+
* `SandboxIO.Current` — never for a driver, an address, or a provider SDK — so
|
|
28
|
+
* a host directory, an in-memory VFS, and a remote microVM are interchangeable
|
|
29
|
+
* behind one contract.
|
|
30
|
+
*
|
|
31
|
+
* The mount neither creates nor destroys infrastructure. `SandboxInstance` is
|
|
32
|
+
* the durable namespace it acts on — a device, which exists whether or not
|
|
33
|
+
* anything has it mounted — and `Sandbox.Controller` is the only path that
|
|
34
|
+
* creates, stops, or destroys one.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* The filesystem tag. Re-exported here so consumers import one namespace —
|
|
38
|
+
* Project, Git, Copy, and the runner ask for `SandboxIO.FileSystem`, never for
|
|
39
|
+
* the module that happens to define it. Code *inside* `sandbox/` keeps importing
|
|
40
|
+
* the tag directly, since `io.ts` is built on top of it.
|
|
41
|
+
*/
|
|
42
|
+
const FileSystem = Service;
|
|
43
|
+
/** The shell tag. Re-exported here so consumers import one namespace. */
|
|
44
|
+
const Shell = Shell$1;
|
|
45
|
+
/** Identity and working directory of the current mount. */
|
|
46
|
+
var Current = class extends Context.Service()("@codeworksh/plugin/sandbox/io/Current") {};
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a mount's cwd without consulting ambient process state.
|
|
49
|
+
*
|
|
50
|
+
* The default is supplied by the namespace adapter:
|
|
51
|
+
* provider metadata remotely, `/` for a virtual filesystem, and `process.cwd()` for the host adapter.
|
|
52
|
+
* An explicit absolute cwd replaces it; a relative cwd is resolved inside it. The
|
|
53
|
+
* result is therefore always the one concrete absolute path `Current` requires.
|
|
54
|
+
*
|
|
55
|
+
* A non-absolute default throws rather than failing typed. It is the one
|
|
56
|
+
* defect-level guard in this module, and deliberately so:
|
|
57
|
+
* Adapters reading a value from a provider call this inside their own error channel,
|
|
58
|
+
* where the throw becomes their typed failure (see `EnvDaytona.mountCwd`).
|
|
59
|
+
*/
|
|
60
|
+
const resolveMountCwd = (defaultCwd, cwd) => {
|
|
61
|
+
if (!posix.isAbsolute(defaultCwd)) throw new TypeError(`Sandbox default cwd must be absolute: ${defaultCwd}`);
|
|
62
|
+
if (cwd === void 0) return posix.resolve(defaultCwd);
|
|
63
|
+
return posix.isAbsolute(cwd) ? posix.resolve(cwd) : posix.resolve(defaultCwd, cwd);
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Bind a cwd-neutral transport to one mount.
|
|
67
|
+
*
|
|
68
|
+
* The transport underneath is shared between
|
|
69
|
+
* mounts and stays rooted at the namespace root, and everything directory-shaped
|
|
70
|
+
* happens here, once per mount. Two mounts of one namespace at different working
|
|
71
|
+
* directories therefore coexist without seeing or moving each other's.
|
|
72
|
+
*
|
|
73
|
+
* It consumes the same two tags it provides — the transport is supplied by the
|
|
74
|
+
* layer this is composed over, not by itself.
|
|
75
|
+
*/
|
|
76
|
+
const mount = (identity) => Layer.mergeAll(Layer.succeed(Current, identity), Layer.effect(Service, Effect.map(Service, (fs) => withCwd(fs, identity.cwd))), Layer.effect(Shell$1, Effect.map(Shell$1, (shell) => withCwd$1(shell, identity.cwd))));
|
|
77
|
+
/**
|
|
78
|
+
* Just the identity tag, with no filesystem or shell beneath it. For consumers
|
|
79
|
+
* assembled from stubs — a test that runs `Project` over a fake filesystem still
|
|
80
|
+
* needs to know which namespace it is in.
|
|
81
|
+
*/
|
|
82
|
+
const identityLayer = (identity) => Layer.succeed(Current, identity);
|
|
83
|
+
/** The host identity alone. See {@link identityLayer}. */
|
|
84
|
+
const hostLayer = (cwd) => identityLayer(host(cwd));
|
|
85
|
+
/**
|
|
86
|
+
* The host, rooted at `cwd`. Always `SandboxInstance.ID.local`: the working
|
|
87
|
+
* directory selects where relative paths resolve, it does not make a second host
|
|
88
|
+
* namespace — two mounts rooted at different directories still agree on absolute
|
|
89
|
+
* paths.
|
|
90
|
+
*
|
|
91
|
+
* The host coordinate is explicit so a caller cannot accidentally substitute ambient process
|
|
92
|
+
* state for the application or session directory it owns.
|
|
93
|
+
*/
|
|
94
|
+
const host = (cwd) => ({
|
|
95
|
+
id: ID.local,
|
|
96
|
+
driver: "local",
|
|
97
|
+
kind: "local",
|
|
98
|
+
cwd: resolveMountCwd(cwd)
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* A VFS-backed namespace. The id is minted per call unless one is named, because
|
|
102
|
+
* building one of these builds a fresh VFS: N unnamed calls are N distinct
|
|
103
|
+
* namespaces, none of which outlives the process. A file-backed store is the
|
|
104
|
+
* exception — its file is the state, so whoever knows the file supplies the id
|
|
105
|
+
* it was registered under.
|
|
106
|
+
*/
|
|
107
|
+
const virtual = (input) => ({
|
|
108
|
+
id: input.id ?? ID.create(),
|
|
109
|
+
driver: input.driver,
|
|
110
|
+
kind: "virtual",
|
|
111
|
+
cwd: resolveMountCwd(input.defaultCwd ?? "/", input.cwd)
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* A provider-hosted namespace. The id is always supplied: a remote resource's
|
|
115
|
+
* durable identity is minted and recorded by whoever provisioned it, never
|
|
116
|
+
* derived here from the provider's own locator.
|
|
117
|
+
*/
|
|
118
|
+
const remote = (input) => ({
|
|
119
|
+
id: input.id,
|
|
120
|
+
driver: input.driver,
|
|
121
|
+
kind: "remote",
|
|
122
|
+
cwd: resolveMountCwd(input.defaultCwd, input.cwd)
|
|
123
|
+
});
|
|
124
|
+
//#endregion
|
|
125
|
+
export { Current, FileSystem, io_exports as SandboxIO, Shell, host, hostLayer, identityLayer, mount, remote, resolveMountCwd, virtual };
|
|
126
|
+
|
|
127
|
+
//# sourceMappingURL=io.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.mjs","names":["SandboxFileSystem.Service","ShellTag","EffectLayer","SandboxFileSystem.withCwd","shellWithCwd"],"sources":["../../../src/sandbox/io.ts"],"sourcesContent":["import { Context, Effect, Layer as EffectLayer } from \"effect\";\nimport { posix } from \"../posix.ts\";\nimport type { SandboxDriver } from \"./driver.ts\";\nimport { SandboxFileSystem } from \"./filesystem.ts\";\nimport { SandboxInstance } from \"./instance.ts\";\nimport { type ISandboxExe, Shell as ShellTag, withCwd as shellWithCwd } from \"./shell.ts\";\n\n/**\n * `SandboxIO` is a **mount**: a filesystem, a shell, and the identity and\n * working directory they act on.\n *\n * It is the whole vocabulary a consumer needs. Project, Git, Copy, Location, and\n * every tool ask for `SandboxIO.FileSystem`, `SandboxIO.Shell`, and\n * `SandboxIO.Current` — never for a driver, an address, or a provider SDK — so\n * a host directory, an in-memory VFS, and a remote microVM are interchangeable\n * behind one contract.\n *\n * The mount neither creates nor destroys infrastructure. `SandboxInstance` is\n * the durable namespace it acts on — a device, which exists whether or not\n * anything has it mounted — and `Sandbox.Controller` is the only path that\n * creates, stops, or destroys one.\n */\n\n/**\n * The filesystem tag. Re-exported here so consumers import one namespace —\n * Project, Git, Copy, and the runner ask for `SandboxIO.FileSystem`, never for\n * the module that happens to define it. Code *inside* `sandbox/` keeps importing\n * the tag directly, since `io.ts` is built on top of it.\n */\nexport const FileSystem = SandboxFileSystem.Service;\nexport type FileSystem = SandboxFileSystem.Service;\n\n/** The shell tag. Re-exported here so consumers import one namespace. */\nexport const Shell = ShellTag;\nexport type Shell = ShellTag;\n\n/**\n * What a mount sees: immutable identity, plus the working directory resolved for\n * *this* mount.\n *\n * Nothing mutable belongs here. `status`, `usage`, and reference counts all\n * change while a mount is open, so a mount-time snapshot of them would be wrong\n * by construction — runtime code that needs live management state asks the\n * control plane. The driver's own resource locator is likewise absent: consumers\n * never parse a Vercel name or a Daytona id.\n */\nexport interface Identity {\n\treadonly id: SandboxInstance.ID;\n\treadonly driver: SandboxDriver.Name;\n\treadonly kind: SandboxInstance.Kind;\n\t/** Absolute, and always a path in *this* namespace. */\n\treadonly cwd: string;\n}\n\n/** Identity and working directory of the current mount. */\nexport class Current extends Context.Service<Current, Identity>()(\"@codeworksh/plugin/sandbox/io/Current\") {}\n\n/** Everything a mount provides. */\nexport type Provides = Current | SandboxFileSystem.Service | ShellTag;\n\n/** A built mount. */\nexport type Layer<E = never, RIn = never> = EffectLayer.Layer<Provides, E, RIn>;\n\n/**\n * Resolve a mount's cwd without consulting ambient process state.\n *\n * The default is supplied by the namespace adapter:\n * provider metadata remotely, `/` for a virtual filesystem, and `process.cwd()` for the host adapter.\n * An explicit absolute cwd replaces it; a relative cwd is resolved inside it. The\n * result is therefore always the one concrete absolute path `Current` requires.\n *\n * A non-absolute default throws rather than failing typed. It is the one\n * defect-level guard in this module, and deliberately so:\n * Adapters reading a value from a provider call this inside their own error channel,\n * where the throw becomes their typed failure (see `EnvDaytona.mountCwd`).\n */\nexport const resolveMountCwd = (defaultCwd: string, cwd?: string): string => {\n\tif (!posix.isAbsolute(defaultCwd)) {\n\t\tthrow new TypeError(`Sandbox default cwd must be absolute: ${defaultCwd}`);\n\t}\n\t// `resolve`, not `normalize`: normalize keeps a trailing slash, and `cwd` is\n\t// persisted — `space.location` and its id hash would read\n\t// `/workspace/` and `/workspace` as two directories and keep a row for each.\n\t//\n\t// A provider-reported default (`getWorkDir()`) or a config value is free to\n\t// carry one, so it is stripped here rather than at every reader.\n\tif (cwd === undefined) return posix.resolve(defaultCwd);\n\treturn posix.isAbsolute(cwd) ? posix.resolve(cwd) : posix.resolve(defaultCwd, cwd);\n};\n\n/**\n * Bind a cwd-neutral transport to one mount.\n *\n * The transport underneath is shared between\n * mounts and stays rooted at the namespace root, and everything directory-shaped\n * happens here, once per mount. Two mounts of one namespace at different working\n * directories therefore coexist without seeing or moving each other's.\n *\n * It consumes the same two tags it provides — the transport is supplied by the\n * layer this is composed over, not by itself.\n */\nexport const mount = (identity: Identity): EffectLayer.Layer<Provides, never, SandboxFileSystem.Service | ShellTag> =>\n\tEffectLayer.mergeAll(\n\t\tEffectLayer.succeed(Current, identity),\n\t\tEffectLayer.effect(\n\t\t\tSandboxFileSystem.Service,\n\t\t\tEffect.map(SandboxFileSystem.Service, (fs) => SandboxFileSystem.withCwd(fs, identity.cwd)),\n\t\t),\n\t\tEffectLayer.effect(\n\t\t\tShellTag,\n\t\t\tEffect.map(ShellTag, (shell: ISandboxExe) => shellWithCwd(shell, identity.cwd)),\n\t\t),\n\t);\n\n/**\n * Just the identity tag, with no filesystem or shell beneath it. For consumers\n * assembled from stubs — a test that runs `Project` over a fake filesystem still\n * needs to know which namespace it is in.\n */\nexport const identityLayer = (identity: Identity) => EffectLayer.succeed(Current, identity);\n\n/** The host identity alone. See {@link identityLayer}. */\nexport const hostLayer = (cwd: string) => identityLayer(host(cwd));\n\n/**\n * The host, rooted at `cwd`. Always `SandboxInstance.ID.local`: the working\n * directory selects where relative paths resolve, it does not make a second host\n * namespace — two mounts rooted at different directories still agree on absolute\n * paths.\n *\n * The host coordinate is explicit so a caller cannot accidentally substitute ambient process\n * state for the application or session directory it owns.\n */\nexport const host = (cwd: string): Identity => ({\n\tid: SandboxInstance.ID.local,\n\tdriver: \"local\" as SandboxDriver.Name,\n\tkind: \"local\",\n\tcwd: resolveMountCwd(cwd),\n});\n\n/**\n * A VFS-backed namespace. The id is minted per call unless one is named, because\n * building one of these builds a fresh VFS: N unnamed calls are N distinct\n * namespaces, none of which outlives the process. A file-backed store is the\n * exception — its file is the state, so whoever knows the file supplies the id\n * it was registered under.\n */\nexport const virtual = (input: {\n\treadonly driver: string;\n\treadonly id?: SandboxInstance.ID;\n\treadonly defaultCwd?: string;\n\treadonly cwd?: string;\n}): Identity => ({\n\tid: input.id ?? SandboxInstance.ID.create(),\n\tdriver: input.driver as SandboxDriver.Name,\n\tkind: \"virtual\",\n\tcwd: resolveMountCwd(input.defaultCwd ?? \"/\", input.cwd),\n});\n\n/**\n * A provider-hosted namespace. The id is always supplied: a remote resource's\n * durable identity is minted and recorded by whoever provisioned it, never\n * derived here from the provider's own locator.\n */\nexport const remote = (input: {\n\treadonly driver: string;\n\treadonly id: SandboxInstance.ID;\n\t/** Namespace-intrinsic default supplied or discovered by the driver. */\n\treadonly defaultCwd: string;\n\treadonly cwd?: string;\n}): Identity => ({\n\tid: input.id,\n\tdriver: input.driver as SandboxDriver.Name,\n\tkind: \"remote\",\n\tcwd: resolveMountCwd(input.defaultCwd, input.cwd),\n});\n\nexport * as SandboxIO from \"./io.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,aAAaA;;AAI1B,MAAa,QAAQC;;AAsBrB,IAAa,UAAb,cAA6B,QAAQ,QAA2B,CAAC,CAAC,uCAAuC,CAAC,CAAC,CAAC;;;;;;;;;;;;;;AAqB5G,MAAa,mBAAmB,YAAoB,QAAyB;CAC5E,IAAI,CAAC,MAAM,WAAW,UAAU,GAC/B,MAAM,IAAI,UAAU,yCAAyC,YAAY;CAQ1E,IAAI,QAAQ,KAAA,GAAW,OAAO,MAAM,QAAQ,UAAU;CACtD,OAAO,MAAM,WAAW,GAAG,IAAI,MAAM,QAAQ,GAAG,IAAI,MAAM,QAAQ,YAAY,GAAG;AAClF;;;;;;;;;;;;AAaA,MAAa,SAAS,aACrBC,MAAY,SACXA,MAAY,QAAQ,SAAS,QAAQ,GACrCA,MAAY,OACXF,SACA,OAAO,IAAIA,UAA4B,OAAOG,QAA0B,IAAI,SAAS,GAAG,CAAC,CAC1F,GACAD,MAAY,OACXD,SACA,OAAO,IAAIA,UAAW,UAAuBG,UAAa,OAAO,SAAS,GAAG,CAAC,CAC/E,CACD;;;;;;AAOD,MAAa,iBAAiB,aAAuBF,MAAY,QAAQ,SAAS,QAAQ;;AAG1F,MAAa,aAAa,QAAgB,cAAc,KAAK,GAAG,CAAC;;;;;;;;;;AAWjE,MAAa,QAAQ,SAA2B;CAC/C,IAAA,GAAuB;CACvB,QAAQ;CACR,MAAM;CACN,KAAK,gBAAgB,GAAG;AACzB;;;;;;;;AASA,MAAa,WAAW,WAKP;CAChB,IAAI,MAAM,MAAA,GAAyB,OAAO;CAC1C,QAAQ,MAAM;CACd,MAAM;CACN,KAAK,gBAAgB,MAAM,cAAc,KAAK,MAAM,GAAG;AACxD;;;;;;AAOA,MAAa,UAAU,WAMN;CAChB,IAAI,MAAM;CACV,QAAQ,MAAM;CACd,MAAM;CACN,KAAK,gBAAgB,MAAM,YAAY,MAAM,GAAG;AACjD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
declare namespace resource_d_exports {
|
|
3
|
+
export { resource_d_exports as SandboxResource, Service };
|
|
4
|
+
}
|
|
5
|
+
declare const Service_base: Context.ServiceClass<Service, "@codeworksh/plugin/sandbox/resource/Service", {
|
|
6
|
+
readonly providerResourceId: string;
|
|
7
|
+
}>;
|
|
8
|
+
/**
|
|
9
|
+
* The driver's own locator for the attached resource — a Vercel sandbox name, a
|
|
10
|
+
* Daytona sandbox id.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately *not* on `SandboxIO.Current`: consumers never parse one,
|
|
13
|
+
* and keeps it separate from the application id precisely so a driver's format can
|
|
14
|
+
* change without touching identity.
|
|
15
|
+
*
|
|
16
|
+
* It exists for the control plane, which records it as `provider_resource_id`,
|
|
17
|
+
* and for tests that reattach to the same resource.
|
|
18
|
+
*
|
|
19
|
+
* One tag for every driver rather than one per driver. A mount has exactly one
|
|
20
|
+
* driver, so there is nothing to disambiguate, and the control plane has to read
|
|
21
|
+
* the locator without knowing which driver produced it — keeps the
|
|
22
|
+
* driver name open, so anything keyed on a closed `"vercel" | "daytona"` set is
|
|
23
|
+
* a bug waiting for the third driver.
|
|
24
|
+
*/
|
|
25
|
+
export declare class Service extends Service_base {}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { resource_d_exports as SandboxResource };
|
|
28
|
+
//# sourceMappingURL=resource.d.mts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { t as __exportAll } from "../rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { Context } from "effect";
|
|
3
|
+
//#region src/sandbox/resource.ts
|
|
4
|
+
var resource_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
SandboxResource: () => resource_exports,
|
|
6
|
+
Service: () => Service
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* The driver's own locator for the attached resource — a Vercel sandbox name, a
|
|
10
|
+
* Daytona sandbox id.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately *not* on `SandboxIO.Current`: consumers never parse one,
|
|
13
|
+
* and keeps it separate from the application id precisely so a driver's format can
|
|
14
|
+
* change without touching identity.
|
|
15
|
+
*
|
|
16
|
+
* It exists for the control plane, which records it as `provider_resource_id`,
|
|
17
|
+
* and for tests that reattach to the same resource.
|
|
18
|
+
*
|
|
19
|
+
* One tag for every driver rather than one per driver. A mount has exactly one
|
|
20
|
+
* driver, so there is nothing to disambiguate, and the control plane has to read
|
|
21
|
+
* the locator without knowing which driver produced it — keeps the
|
|
22
|
+
* driver name open, so anything keyed on a closed `"vercel" | "daytona"` set is
|
|
23
|
+
* a bug waiting for the third driver.
|
|
24
|
+
*/
|
|
25
|
+
var Service = class extends Context.Service()("@codeworksh/plugin/sandbox/resource/Service") {};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { resource_exports as SandboxResource, Service };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=resource.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.mjs","names":[],"sources":["../../../src/sandbox/resource.ts"],"sourcesContent":["import { Context } from \"effect\";\n\n/**\n * The driver's own locator for the attached resource — a Vercel sandbox name, a\n * Daytona sandbox id.\n *\n * Deliberately *not* on `SandboxIO.Current`: consumers never parse one,\n * and keeps it separate from the application id precisely so a driver's format can\n * change without touching identity.\n *\n * It exists for the control plane, which records it as `provider_resource_id`,\n * and for tests that reattach to the same resource.\n *\n * One tag for every driver rather than one per driver. A mount has exactly one\n * driver, so there is nothing to disambiguate, and the control plane has to read\n * the locator without knowing which driver produced it — keeps the\n * driver name open, so anything keyed on a closed `\"vercel\" | \"daytona\"` set is\n * a bug waiting for the third driver.\n */\nexport class Service extends Context.Service<Service, { readonly providerResourceId: string }>()(\n\t\"@codeworksh/plugin/sandbox/resource/Service\",\n) {}\n\nexport * as SandboxResource from \"./resource.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAmBA,IAAa,UAAb,cAA6B,QAAQ,QAA0D,CAAC,CAC/F,6CACD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as ShellError, c as perMount, d as resolveCwd, f as withCwd, i as Shell, l as quote, n as ExecResult, o as ShellOptions, r as ISandboxExe, s as fromExec, t as ExecChunk, u as quoteArgv } from "../shell-CZ6Y4lNU.mjs";
|
|
2
|
+
export { ExecChunk, ExecResult, ISandboxExe, Shell, ShellError, ShellOptions, fromExec, perMount, quote, quoteArgv, resolveCwd, withCwd };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { posix } from "../posix.mjs";
|
|
2
|
+
import { Context, Schema } from "effect";
|
|
3
|
+
//#region src/sandbox/shell.ts
|
|
4
|
+
/**
|
|
5
|
+
* The pluggable execution contract. Local sandboxes usually get a Shell through
|
|
6
|
+
* just-bash over the local `Local.Vfs`; remote sandboxes provide their own
|
|
7
|
+
* native Shell. Either way the rest of the harness depends only on this service
|
|
8
|
+
* tag.
|
|
9
|
+
*/
|
|
10
|
+
var ShellError = class extends Schema.TaggedError()("ShellError", {
|
|
11
|
+
command: Schema.String,
|
|
12
|
+
cause: Schema.optional(Schema.Defect())
|
|
13
|
+
}) {};
|
|
14
|
+
/**
|
|
15
|
+
* Mount-local shell factories are attached out-of-band so the public Shell
|
|
16
|
+
* contract stays about command execution only.
|
|
17
|
+
*
|
|
18
|
+
* Most transports are safely shared and need only the cwd wrapper below.
|
|
19
|
+
* Stateful in-process interpreters use this hook to construct one interpreter
|
|
20
|
+
* per mount while retaining the underlying filesystem transport. This tag must
|
|
21
|
+
* remain on the outermost wrapper consumed by {@link withCwd}; wrapper helpers
|
|
22
|
+
* such as {@link fromExec} must propagate it when they return a new object.
|
|
23
|
+
*/
|
|
24
|
+
const mountFactories = /* @__PURE__ */ new WeakMap();
|
|
25
|
+
const perMount = (transport, make) => {
|
|
26
|
+
mountFactories.set(transport, make);
|
|
27
|
+
return transport;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* POSIX single-quote escaping: wrap in `'…'` and rewrite each embedded quote as
|
|
31
|
+
* `'\''`. Everything inside single quotes is literal to the shell, so this is
|
|
32
|
+
* safe for arbitrary bytes.
|
|
33
|
+
*/
|
|
34
|
+
const quote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
35
|
+
/** Render an argument vector as one shell-safe command string. */
|
|
36
|
+
const quoteArgv = (argv) => argv.map(quote).join(" ");
|
|
37
|
+
/**
|
|
38
|
+
* Resolve a per-command cwd against the sandbox cwd. Passing a relative cwd
|
|
39
|
+
* straight to a host or remote process API would otherwise resolve it against
|
|
40
|
+
* that API's own default, which need not be the filesystem's configured cwd.
|
|
41
|
+
*/
|
|
42
|
+
const resolveCwd = (base, cwd) => {
|
|
43
|
+
if (cwd === void 0 || posix.isAbsolute(cwd) || base === void 0) return cwd ?? base;
|
|
44
|
+
return posix.resolve(base, cwd);
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Complete a string-only backend: `execArgv` quotes the vector and runs it
|
|
48
|
+
* through `exec`. Backends that spawn a real argument vector (Vercel) implement
|
|
49
|
+
* `execArgv` themselves instead, so the args never meet a shell parser at all.
|
|
50
|
+
*
|
|
51
|
+
* `cwd` rides the options rather than a `cd <dir> && …` prefix. Every backend
|
|
52
|
+
* we wrap takes a working directory natively, and the prefix form cannot tell a
|
|
53
|
+
* failed `cd` from a failed command — both arrive as one exit code.
|
|
54
|
+
*/
|
|
55
|
+
const fromExec = (backend) => {
|
|
56
|
+
const wrapped = {
|
|
57
|
+
...backend,
|
|
58
|
+
execArgv: (argv, options) => backend.exec(quoteArgv(argv), options)
|
|
59
|
+
};
|
|
60
|
+
const mountFactory = mountFactories.get(backend);
|
|
61
|
+
return mountFactory === void 0 ? wrapped : perMount(wrapped, (cwd) => fromExec(mountFactory(cwd)));
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Bind a cwd-neutral backend to one mount's working directory.
|
|
65
|
+
*
|
|
66
|
+
* The transport is shared between mounts and must stay rooted at the namespace
|
|
67
|
+
* root, so the directory cannot live inside it: two mounts at different
|
|
68
|
+
* directories would otherwise see each other's. This wrapper is per mount, and
|
|
69
|
+
* an operation-level `cwd` still wins — resolved against the mount's, so a
|
|
70
|
+
* relative one means what it reads like.
|
|
71
|
+
*/
|
|
72
|
+
const withCwd = (backend, cwd) => {
|
|
73
|
+
const mounted = mountFactories.get(backend)?.(cwd) ?? backend;
|
|
74
|
+
const at = (options) => {
|
|
75
|
+
const resolvedCwd = resolveCwd(cwd, options?.cwd);
|
|
76
|
+
return {
|
|
77
|
+
...options,
|
|
78
|
+
...resolvedCwd === void 0 ? {} : { cwd: resolvedCwd }
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
const stream = mounted.stream;
|
|
82
|
+
return {
|
|
83
|
+
exec: (command, options) => mounted.exec(command, at(options)),
|
|
84
|
+
execArgv: (argv, options) => mounted.execArgv(argv, at(options)),
|
|
85
|
+
...stream === void 0 ? {} : { stream: (command, options) => stream(command, at(options)) }
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
/** Execution service — the live {@link ISandboxExe} for the active sandbox. */
|
|
89
|
+
var Shell = class extends Context.Service()("@codeworksh/plugin/sandbox/shell") {};
|
|
90
|
+
//#endregion
|
|
91
|
+
export { Shell, ShellError, fromExec, perMount, quote, quoteArgv, resolveCwd, withCwd };
|
|
92
|
+
|
|
93
|
+
//# sourceMappingURL=shell.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.mjs","names":["path"],"sources":["../../../src/sandbox/shell.ts"],"sourcesContent":["import { Context, type Effect, Schema, type Stream } from \"effect\";\nimport { posix as path } from \"../posix.ts\";\n\n/**\n * The pluggable execution contract. Local sandboxes usually get a Shell through\n * just-bash over the local `Local.Vfs`; remote sandboxes provide their own\n * native Shell. Either way the rest of the harness depends only on this service\n * tag.\n */\n\nexport class ShellError extends Schema.TaggedError<ShellError>()(\"ShellError\", {\n\tcommand: Schema.String,\n\tcause: Schema.optional(Schema.Defect()),\n}) {}\n\nexport interface ExecResult {\n\treadonly stdout: string;\n\treadonly stderr: string;\n\treadonly exitCode: number;\n}\n\n/**\n * One options shape for every entry point. `cwd` is the operation-level\n * override: it wins over the mount's working directory, and a relative value\n * resolves against it. Neither mutates shared state, so concurrent commands in\n * one mount can run in different directories.\n */\nexport interface ShellOptions {\n\treadonly env?: Record<string, string>;\n\treadonly cwd?: string;\n}\n\n/**\n * A streamed chunk of command output, terminated by a single `exit` carrying the\n * exit code. (A backend-level mirror of the tool layer's event; kept here so\n * `sandbox/` does not depend on `tool/`.)\n */\nexport type ExecChunk =\n\t| { readonly _tag: \"stdout\"; readonly bytes: Uint8Array }\n\t| { readonly _tag: \"stderr\"; readonly bytes: Uint8Array }\n\t| { readonly _tag: \"exit\"; readonly exitCode: number };\n\n/**\n * The command-execution capability a sandbox exposes. For an in-process\n * backend this is just-bash; for a remote backend it is the sandbox's own\n * shell. The {@link Shell} service tag carries this interface.\n */\nexport interface ISandboxExe {\n\treadonly exec: (command: string, options?: ShellOptions) => Effect.Effect<ExecResult, ShellError>;\n\t/**\n\t * Run a program with an explicit argument vector, bypassing shell word\n\t * splitting. Callers that build commands from untrusted values — branch\n\t * names, file paths — must use this instead of interpolating into\n\t * {@link exec}, where a space or `$(…)` would change what runs.\n\t *\n\t * Backends whose transport only accepts a string quote the vector with\n\t * {@link quote}; backends that spawn directly pass it through untouched.\n\t */\n\treadonly execArgv: (argv: ReadonlyArray<string>, options?: ShellOptions) => Effect.Effect<ExecResult, ShellError>;\n\t/**\n\t * Optional streaming output: stdout/stderr chunks then a terminal `exit`.\n\t * Backends that can stream (e.g. Vercel) implement it; `ToolShell.fromSandboxShell`\n\t * bridges it to `ToolShell.stream` so the bash tool streams over them too.\n\t */\n\treadonly stream?: (command: string, options?: ShellOptions) => Stream.Stream<ExecChunk, ShellError>;\n}\n\ntype MountFactory = (cwd: string) => ISandboxExe;\n\n/**\n * Mount-local shell factories are attached out-of-band so the public Shell\n * contract stays about command execution only.\n *\n * Most transports are safely shared and need only the cwd wrapper below.\n * Stateful in-process interpreters use this hook to construct one interpreter\n * per mount while retaining the underlying filesystem transport. This tag must\n * remain on the outermost wrapper consumed by {@link withCwd}; wrapper helpers\n * such as {@link fromExec} must propagate it when they return a new object.\n */\nconst mountFactories = new WeakMap<object, MountFactory>();\n\nexport const perMount = (transport: ISandboxExe, make: MountFactory): ISandboxExe => {\n\tmountFactories.set(transport, make);\n\treturn transport;\n};\n\n/**\n * POSIX single-quote escaping: wrap in `'…'` and rewrite each embedded quote as\n * `'\\''`. Everything inside single quotes is literal to the shell, so this is\n * safe for arbitrary bytes.\n */\nexport const quote = (value: string) => `'${value.replace(/'/g, `'\\\\''`)}'`;\n\n/** Render an argument vector as one shell-safe command string. */\nexport const quoteArgv = (argv: ReadonlyArray<string>) => argv.map(quote).join(\" \");\n\n/**\n * Resolve a per-command cwd against the sandbox cwd. Passing a relative cwd\n * straight to a host or remote process API would otherwise resolve it against\n * that API's own default, which need not be the filesystem's configured cwd.\n */\nexport const resolveCwd = (base: string | undefined, cwd: string | undefined) => {\n\tif (cwd === undefined || path.isAbsolute(cwd) || base === undefined) return cwd ?? base;\n\treturn path.resolve(base, cwd);\n};\n\n/**\n * Complete a string-only backend: `execArgv` quotes the vector and runs it\n * through `exec`. Backends that spawn a real argument vector (Vercel) implement\n * `execArgv` themselves instead, so the args never meet a shell parser at all.\n *\n * `cwd` rides the options rather than a `cd <dir> && …` prefix. Every backend\n * we wrap takes a working directory natively, and the prefix form cannot tell a\n * failed `cd` from a failed command — both arrive as one exit code.\n */\nexport const fromExec = (backend: Omit<ISandboxExe, \"execArgv\">): ISandboxExe => {\n\tconst wrapped: ISandboxExe = {\n\t\t...backend,\n\t\texecArgv: (argv, options) => backend.exec(quoteArgv(argv), options),\n\t};\n\tconst mountFactory = mountFactories.get(backend);\n\treturn mountFactory === undefined ? wrapped : perMount(wrapped, (cwd) => fromExec(mountFactory(cwd)));\n};\n\n/**\n * Bind a cwd-neutral backend to one mount's working directory.\n *\n * The transport is shared between mounts and must stay rooted at the namespace\n * root, so the directory cannot live inside it: two mounts at different\n * directories would otherwise see each other's. This wrapper is per mount, and\n * an operation-level `cwd` still wins — resolved against the mount's, so a\n * relative one means what it reads like.\n */\nexport const withCwd = (backend: ISandboxExe, cwd: string): ISandboxExe => {\n\tconst mounted = mountFactories.get(backend)?.(cwd) ?? backend;\n\tconst at = (options?: ShellOptions): ShellOptions => {\n\t\tconst resolvedCwd = resolveCwd(cwd, options?.cwd);\n\t\treturn { ...options, ...(resolvedCwd === undefined ? {} : { cwd: resolvedCwd }) };\n\t};\n\tconst stream = mounted.stream;\n\treturn {\n\t\texec: (command, options) => mounted.exec(command, at(options)),\n\t\texecArgv: (argv, options) => mounted.execArgv(argv, at(options)),\n\t\t...(stream === undefined ? {} : { stream: (command, options) => stream(command, at(options)) }),\n\t};\n};\n\n/** Execution service — the live {@link ISandboxExe} for the active sandbox. */\nexport class Shell extends Context.Service<Shell, ISandboxExe>()(\"@codeworksh/plugin/sandbox/shell\") {}\n"],"mappings":";;;;;;;;;AAUA,IAAa,aAAb,cAAgC,OAAO,YAAwB,CAAC,CAAC,cAAc;CAC9E,SAAS,OAAO;CAChB,OAAO,OAAO,SAAS,OAAO,OAAO,CAAC;AACvC,CAAC,CAAC,CAAC,CAAC;;;;;;;;;;;AAkEJ,MAAM,iCAAiB,IAAI,QAA8B;AAEzD,MAAa,YAAY,WAAwB,SAAoC;CACpF,eAAe,IAAI,WAAW,IAAI;CAClC,OAAO;AACR;;;;;;AAOA,MAAa,SAAS,UAAkB,IAAI,MAAM,QAAQ,MAAM,OAAO,EAAE;;AAGzE,MAAa,aAAa,SAAgC,KAAK,IAAI,KAAK,CAAC,CAAC,KAAK,GAAG;;;;;;AAOlF,MAAa,cAAc,MAA0B,QAA4B;CAChF,IAAI,QAAQ,KAAA,KAAaA,MAAK,WAAW,GAAG,KAAK,SAAS,KAAA,GAAW,OAAO,OAAO;CACnF,OAAOA,MAAK,QAAQ,MAAM,GAAG;AAC9B;;;;;;;;;;AAWA,MAAa,YAAY,YAAwD;CAChF,MAAM,UAAuB;EAC5B,GAAG;EACH,WAAW,MAAM,YAAY,QAAQ,KAAK,UAAU,IAAI,GAAG,OAAO;CACnE;CACA,MAAM,eAAe,eAAe,IAAI,OAAO;CAC/C,OAAO,iBAAiB,KAAA,IAAY,UAAU,SAAS,UAAU,QAAQ,SAAS,aAAa,GAAG,CAAC,CAAC;AACrG;;;;;;;;;;AAWA,MAAa,WAAW,SAAsB,QAA6B;CAC1E,MAAM,UAAU,eAAe,IAAI,OAAO,CAAC,GAAG,GAAG,KAAK;CACtD,MAAM,MAAM,YAAyC;EACpD,MAAM,cAAc,WAAW,KAAK,SAAS,GAAG;EAChD,OAAO;GAAE,GAAG;GAAS,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK,YAAY;EAAG;CACjF;CACA,MAAM,SAAS,QAAQ;CACvB,OAAO;EACN,OAAO,SAAS,YAAY,QAAQ,KAAK,SAAS,GAAG,OAAO,CAAC;EAC7D,WAAW,MAAM,YAAY,QAAQ,SAAS,MAAM,GAAG,OAAO,CAAC;EAC/D,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,SAAS,YAAY,OAAO,SAAS,GAAG,OAAO,CAAC,EAAE;CAC9F;AACD;;AAGA,IAAa,QAAb,cAA2B,QAAQ,QAA4B,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC"}
|
package/sandbox.d.mts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { A as apiVersion, C as Observed, D as RuntimeConfigBase, E as Registration, M as driver, O as RuntimeInput, S as Name, _ as Capabilities, a as Provides, b as Module, g as ApiVersion, h as AbsolutePath, i as Layer, j as defineModule, n as FileSystem, o as Shell, r as Identity, t as Current, v as Definition, vt as instance_d_exports, w as Provisioned, x as ModuleDefinition, y as Driver } from "./io-BYs7hbiY.mjs";
|
|
2
|
+
import { c as filesystem_d_exports } from "./filesystem-Cp-KapCE.mjs";
|
|
3
|
+
import { a as ShellError, i as Shell$1, l as quote, n as ExecResult, o as ShellOptions, r as ISandboxExe, s as fromExec, t as ExecChunk, u as quoteArgv } from "./shell-CZ6Y4lNU.mjs";
|
|
4
|
+
import { t as error_d_exports } from "./sandbox/error.mjs";
|
|
5
|
+
import { SandboxResource as resource_d_exports } from "./sandbox/resource.mjs";
|
|
6
|
+
declare namespace driver_d_exports {
|
|
7
|
+
export { AbsolutePath, ApiVersion, Capabilities, Definition, Driver, Module, ModuleDefinition, Name, Observed, Provisioned, Registration, RuntimeConfigBase as RuntimeConfig, RuntimeConfigBase, RuntimeInput, driver_d_exports as SandboxDriver, apiVersion, driver, defineModule as module };
|
|
8
|
+
}
|
|
9
|
+
declare namespace io_d_exports {
|
|
10
|
+
export { Current, FileSystem, Identity, Layer, Provides, io_d_exports as SandboxIO, Shell };
|
|
11
|
+
}
|
|
12
|
+
declare namespace shell_d_exports {
|
|
13
|
+
export { ExecChunk, ExecResult, ISandboxExe, Shell$1 as Shell, ShellError, ShellOptions, fromExec, quote, quoteArgv };
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { driver_d_exports as SandboxDriver, filesystem_d_exports as SandboxFileSystem, io_d_exports as SandboxIO, instance_d_exports as SandboxInstance, error_d_exports as SandboxProvider, resource_d_exports as SandboxResource, shell_d_exports as SandboxShell };
|
|
17
|
+
//# sourceMappingURL=sandbox.d.mts.map
|
package/sandbox.mjs
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { SandboxInstance as instance_exports } from "./sandbox/instance.mjs";
|
|
3
|
+
import { SandboxFileSystem as filesystem_exports } from "./sandbox/filesystem.mjs";
|
|
4
|
+
import { Shell, ShellError, fromExec, quote, quoteArgv } from "./sandbox/shell.mjs";
|
|
5
|
+
import { Current, FileSystem, Shell as Shell$1 } from "./sandbox/io.mjs";
|
|
6
|
+
import { AbsolutePath, Name, RuntimeConfigBase, driver, module as defineModule } from "./sandbox/driver.mjs";
|
|
7
|
+
import { t as error_exports } from "./sandbox/error.mjs";
|
|
8
|
+
import { SandboxResource as resource_exports } from "./sandbox/resource.mjs";
|
|
9
|
+
//#region src/sandbox/public/driver.ts
|
|
10
|
+
var driver_exports = /* @__PURE__ */ __exportAll({
|
|
11
|
+
AbsolutePath: () => AbsolutePath,
|
|
12
|
+
Name: () => Name,
|
|
13
|
+
RuntimeConfigBase: () => RuntimeConfigBase,
|
|
14
|
+
SandboxDriver: () => driver_exports,
|
|
15
|
+
apiVersion: () => 1,
|
|
16
|
+
driver: () => driver,
|
|
17
|
+
module: () => defineModule
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/sandbox/public/io.ts
|
|
21
|
+
var io_exports = /* @__PURE__ */ __exportAll({
|
|
22
|
+
Current: () => Current,
|
|
23
|
+
FileSystem: () => FileSystem,
|
|
24
|
+
SandboxIO: () => io_exports,
|
|
25
|
+
Shell: () => Shell$1
|
|
26
|
+
});
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/sandbox/public/shell.ts
|
|
29
|
+
var shell_exports = /* @__PURE__ */ __exportAll({
|
|
30
|
+
Shell: () => Shell,
|
|
31
|
+
ShellError: () => ShellError,
|
|
32
|
+
fromExec: () => fromExec,
|
|
33
|
+
quote: () => quote,
|
|
34
|
+
quoteArgv: () => quoteArgv
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export { driver_exports as SandboxDriver, filesystem_exports as SandboxFileSystem, io_exports as SandboxIO, instance_exports as SandboxInstance, error_exports as SandboxProvider, resource_exports as SandboxResource, shell_exports as SandboxShell };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=sandbox.mjs.map
|
package/sandbox.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.mjs","names":[],"sources":["../../src/sandbox/public/driver.ts","../../src/sandbox/public/io.ts","../../src/sandbox/public/shell.ts"],"sourcesContent":["export { AbsolutePath, apiVersion, driver, module, Name, RuntimeConfigBase } from \"../driver.ts\";\nexport type {\n\tApiVersion,\n\tCapabilities,\n\tDefinition,\n\tDriver,\n\tModule,\n\tModuleDefinition,\n\tObserved,\n\tProvisioned,\n\tRegistration,\n\tRuntimeConfigBase as RuntimeConfig,\n\tRuntimeInput,\n} from \"../driver.ts\";\n\nexport * as SandboxDriver from \"./driver.ts\";\n","export { Current, FileSystem, Shell } from \"../io.ts\";\nexport type { Identity, Layer, Provides } from \"../io.ts\";\n\nexport * as SandboxIO from \"./io.ts\";\n","export { fromExec, quote, quoteArgv, Shell, ShellError } from \"../shell.ts\";\nexport type { ExecChunk, ExecResult, ISandboxExe, ShellOptions } from \"../shell.ts\";\n"],"mappings":""}
|