@intentius/chant-lexicon-e2b 0.0.0-stage → 0.124.32
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/README.md +147 -2
- package/dist/catalog.d.ts +19 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +58 -0
- package/dist/codegen/docs-cli.d.ts +3 -0
- package/dist/codegen/docs-cli.d.ts.map +1 -0
- package/dist/codegen/docs-cli.js +6 -0
- package/dist/codegen/docs.d.ts +15 -0
- package/dist/codegen/docs.d.ts.map +1 -0
- package/dist/codegen/docs.js +123 -0
- package/dist/codegen/generate-cli.d.ts +3 -0
- package/dist/codegen/generate-cli.d.ts.map +1 -0
- package/dist/codegen/generate-cli.js +4 -0
- package/dist/codegen/generate.d.ts +11 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/generate.js +20 -0
- package/dist/codegen/package.d.ts +10 -0
- package/dist/codegen/package.d.ts.map +1 -0
- package/dist/codegen/package.js +34 -0
- package/dist/config.d.ts +113 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +82 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/integrity.json +15 -0
- package/dist/lint/audit-catalog.d.ts +11 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -0
- package/dist/lint/audit-catalog.js +15 -0
- package/dist/lint/post-synth/e2b011-template-name.d.ts +12 -0
- package/dist/lint/post-synth/e2b011-template-name.d.ts.map +1 -0
- package/dist/lint/post-synth/e2b011-template-name.js +48 -0
- package/dist/lint/post-synth/e2b012-steward-tools.d.ts +20 -0
- package/dist/lint/post-synth/e2b012-steward-tools.d.ts.map +1 -0
- package/dist/lint/post-synth/e2b012-steward-tools.js +53 -0
- package/dist/lint/post-synth/index.d.ts +3 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -0
- package/dist/lint/post-synth/index.js +6 -0
- package/dist/lint/rules/index.d.ts +6 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lint/rules/index.js +6 -0
- package/dist/lint/rules/pinned-image.d.ts +14 -0
- package/dist/lint/rules/pinned-image.d.ts.map +1 -0
- package/dist/lint/rules/pinned-image.js +36 -0
- package/dist/lint/rules/template-literal.d.ts +11 -0
- package/dist/lint/rules/template-literal.d.ts.map +1 -0
- package/dist/lint/rules/template-literal.js +39 -0
- package/dist/lint/rules/template-size.d.ts +10 -0
- package/dist/lint/rules/template-size.d.ts.map +1 -0
- package/dist/lint/rules/template-size.js +31 -0
- package/dist/lsp/completions.d.ts +7 -0
- package/dist/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/completions.js +10 -0
- package/dist/lsp/hover.d.ts +7 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/lsp/hover.js +30 -0
- package/dist/manifest.json +6 -0
- package/dist/meta.json +45 -0
- package/dist/okf/index.md +14 -0
- package/dist/okf/rules/E2B001.md +15 -0
- package/dist/okf/rules/E2B002.md +15 -0
- package/dist/okf/rules/E2B011.md +15 -0
- package/dist/okf/rules/E2B012.md +15 -0
- package/dist/okf/types/Template.md +16 -0
- package/dist/op/runtime.d.ts +119 -0
- package/dist/op/runtime.d.ts.map +1 -0
- package/dist/op/runtime.js +522 -0
- package/dist/op/sandbox.d.ts +67 -0
- package/dist/op/sandbox.d.ts.map +1 -0
- package/dist/op/sandbox.js +87 -0
- package/dist/package-cli.d.ts +3 -0
- package/dist/package-cli.d.ts.map +1 -0
- package/dist/package-cli.js +14 -0
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +66 -0
- package/dist/resources.d.ts +38 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +21 -0
- package/dist/rules/e2b011-template-name.ts +51 -0
- package/dist/rules/e2b012-steward-tools.ts +58 -0
- package/dist/rules/pinned-image.ts +41 -0
- package/dist/rules/template-literal.ts +42 -0
- package/dist/rules/template-size.ts +36 -0
- package/dist/serializer.d.ts +18 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/serializer.js +70 -0
- package/dist/skills/chant-e2b-ops.md +69 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +67 -4
- package/src/catalog.ts +72 -0
- package/src/codegen/docs-cli.ts +7 -0
- package/src/codegen/docs.ts +130 -0
- package/src/codegen/generate-cli.ts +5 -0
- package/src/codegen/generate.ts +22 -0
- package/src/codegen/package.ts +39 -0
- package/src/config.ts +102 -0
- package/src/index.ts +6 -0
- package/src/lint/audit-catalog.ts +29 -0
- package/src/lint/post-synth/e2b011-template-name.ts +51 -0
- package/src/lint/post-synth/e2b012-steward-tools.ts +58 -0
- package/src/lint/post-synth/index.ts +9 -0
- package/src/lint/post-synth/post-synth.test.ts +59 -0
- package/src/lint/rules/index.ts +9 -0
- package/src/lint/rules/pinned-image.ts +41 -0
- package/src/lint/rules/rules.test.ts +46 -0
- package/src/lint/rules/template-literal.ts +42 -0
- package/src/lint/rules/template-size.ts +36 -0
- package/src/lsp/completions.test.ts +23 -0
- package/src/lsp/completions.ts +13 -0
- package/src/lsp/hover.test.ts +29 -0
- package/src/lsp/hover.ts +35 -0
- package/src/op/ledger-sync.test.ts +136 -0
- package/src/op/runtime.test.ts +394 -0
- package/src/op/runtime.ts +573 -0
- package/src/op/sandbox.ts +138 -0
- package/src/package-cli.ts +16 -0
- package/src/plugin.test.ts +39 -0
- package/src/plugin.ts +79 -0
- package/src/resources.ts +50 -0
- package/src/serializer.test.ts +88 -0
- package/src/serializer.ts +73 -0
- package/src/skills/chant-e2b-ops.md +69 -0
package/README.md
CHANGED
|
@@ -1,3 +1,148 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @intentius/chant-lexicon-e2b
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run chant Ops on a steward: one [E2B](https://e2b.dev) sandbox that outlives
|
|
4
|
+
every run. `chant run <op> --on e2b` hands the run to it.
|
|
5
|
+
|
|
6
|
+
The steward keeps a clone of the project and its installed tools on its disk,
|
|
7
|
+
runs one Op at a time, and sleeps paused between runs, which costs nothing and
|
|
8
|
+
survives anything short of deleting it. Any API that speaks E2B's can host it:
|
|
9
|
+
hosted E2B, or a platform of your own that serves the same API.
|
|
10
|
+
|
|
11
|
+
It gives an Op what [Ops as teammates](https://intentius.io/chant/concepts/durable-workflows/)
|
|
12
|
+
calls *the machine*: something that runs when nobody's laptop is open, without
|
|
13
|
+
holding one open between runs.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install --save-dev @intentius/chant-lexicon-e2b
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// chant.config.ts
|
|
23
|
+
import type { ChantConfig } from "@intentius/chant/config";
|
|
24
|
+
import "@intentius/chant-lexicon-e2b";
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
lexicons: ["e2b"],
|
|
28
|
+
e2b: {
|
|
29
|
+
profiles: {
|
|
30
|
+
prod: {
|
|
31
|
+
apiKey: { env: "E2B_API_KEY" },
|
|
32
|
+
domain: "e2b.example.com",
|
|
33
|
+
steward: "prod-steward",
|
|
34
|
+
gitToken: { env: "GITHUB_TOKEN" },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
defaultProfile: "prod",
|
|
38
|
+
},
|
|
39
|
+
} satisfies ChantConfig;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
| Field | Default | |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `apiKey` | `{ env: "E2B_API_KEY" }` | The E2B API key, by environment variable. |
|
|
45
|
+
| `domain` | `E2B_DOMAIN`, else hosted E2B | The domain the SDK talks to. |
|
|
46
|
+
| `template` | `base` | The template the steward is made from. It needs git and Node. |
|
|
47
|
+
| `steward` | the repository's name | The steward's sandbox is found again by its `chant/steward` metadata. |
|
|
48
|
+
| `repo` | `origin`, as https | The repository the steward clones. An ssh `origin` is rewritten to https. |
|
|
49
|
+
| `gitToken` | `CHANT_GIT_TOKEN`, when set | A token to fetch a private repository and push chant's ledger branch. It is handed to each command in the sandbox and never written to its disk. |
|
|
50
|
+
|
|
51
|
+
With no profile, the runtime reads `E2B_API_KEY`, `E2B_DOMAIN` and
|
|
52
|
+
`CHANT_GIT_TOKEN`. `--profile <name>` picks another profile for one run.
|
|
53
|
+
|
|
54
|
+
The project must depend on `@intentius/chant`: the steward runs the chant the
|
|
55
|
+
project installs.
|
|
56
|
+
|
|
57
|
+
## The steward's template
|
|
58
|
+
|
|
59
|
+
A steward's sandbox is made from the profile's `template`, which is E2B's
|
|
60
|
+
`base` when the profile names none. Declare a `Template` and name it in the
|
|
61
|
+
profile when the project's Ops need tools that `base` lacks:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// src/steward.ts
|
|
65
|
+
import { Template } from "@intentius/chant-lexicon-e2b";
|
|
66
|
+
|
|
67
|
+
const steward = new Template({
|
|
68
|
+
name: "chant-steward",
|
|
69
|
+
fromImage: "node:22-bookworm",
|
|
70
|
+
run: ["apt-get update && apt-get install -y --no-install-recommends jq && rm -rf /var/lib/apt/lists/*"],
|
|
71
|
+
cpuCount: 2,
|
|
72
|
+
memoryMB: 2048,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export { steward };
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`chant build` writes it as `e2b.chant-steward.Dockerfile`, and the JSON index
|
|
79
|
+
in its main output gives the command that builds it, to run once in the output
|
|
80
|
+
directory:
|
|
81
|
+
`e2b template create chant-steward --dockerfile e2b.chant-steward.Dockerfile --cpu-count 2 --memory-mb 2048`.
|
|
82
|
+
A steward that already exists keeps its sandbox until it is deleted.
|
|
83
|
+
|
|
84
|
+
Two lint rules check a template's sizes and that its base image is pinned
|
|
85
|
+
(E2B001, E2B002). Two post-synth checks catch a name E2B refuses or two
|
|
86
|
+
templates sharing one (E2B011), and a template with no visible git or Node
|
|
87
|
+
(E2B012). The [docs](https://intentius.io/chant/lexicons/e2b/)
|
|
88
|
+
cover each property and rule.
|
|
89
|
+
|
|
90
|
+
## What a run does
|
|
91
|
+
|
|
92
|
+
1. Finds the steward's sandbox, or makes it. A steward already running an Op is
|
|
93
|
+
busy, and the run is refused rather than queued.
|
|
94
|
+
2. Fetches the repository in the steward's clone, checks out the commit this
|
|
95
|
+
checkout is at (so push it first; uncommitted changes don't travel, and the
|
|
96
|
+
run says so), and runs `npm ci` when the lockfile changed. Ignored files,
|
|
97
|
+
`node_modules` among them, are kept between runs.
|
|
98
|
+
3. Runs `chant run <op> --on local --json --progress-json` there, streaming each
|
|
99
|
+
settled step back.
|
|
100
|
+
4. Pushes the steward's ledger and pauses the sandbox.
|
|
101
|
+
|
|
102
|
+
Gates, leases and the run ledger are the local runtime's, unchanged. A gate the
|
|
103
|
+
steward reaches pushes its pending fact to the repository's `chant/lifecycle`
|
|
104
|
+
branch; `chant approve` on any checkout records the answer; the next run walks
|
|
105
|
+
through.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
chant run prod-apply --on e2b # exit 3: pending on rollout-gate
|
|
109
|
+
chant approve prod-apply rollout-gate # from any checkout
|
|
110
|
+
chant run prod-apply --on e2b # walks through, applies
|
|
111
|
+
chant run status prod-apply --on e2b # a run in progress, or the last one recorded
|
|
112
|
+
chant run log prod-apply --on e2b
|
|
113
|
+
chant run cancel prod-apply --on e2b --force
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
A client that dies mid-run leaves the run going in the sandbox. `status`
|
|
117
|
+
reports it, and the steward pauses itself when its awake window (30 minutes,
|
|
118
|
+
renewed while a client is watching) runs out. Ctrl-C, or `chant run cancel`
|
|
119
|
+
from any checkout, stops it the way Ctrl-C stops a local run: SIGTERM, then
|
|
120
|
+
SIGKILL after ten seconds.
|
|
121
|
+
|
|
122
|
+
## The steward's ledger
|
|
123
|
+
|
|
124
|
+
chant pushes the facts other checkouts need as it writes them (a gate's pending
|
|
125
|
+
fact, an approval, a lease), but keeps run records on the local branch. A
|
|
126
|
+
checkout that lives on, as a steward's does, would then diverge from the
|
|
127
|
+
repository the first time someone approved a gate elsewhere, and chant refuses
|
|
128
|
+
to read a diverged ledger. So before and after each run the steward reconciles
|
|
129
|
+
its `chant/lifecycle` with the repository's: it pushes when it is only ahead,
|
|
130
|
+
and when the two have diverged it merges them (each `.jsonl` file is
|
|
131
|
+
append-only, so the merge adds the lines one side lacks) and pushes the merge.
|
|
132
|
+
Its run records therefore reach the repository, and `chant run log` on any
|
|
133
|
+
checkout sees them.
|
|
134
|
+
|
|
135
|
+
## Schedules
|
|
136
|
+
|
|
137
|
+
Nothing in E2B fires on a clock. Run `chant run <op> --on e2b` from whatever
|
|
138
|
+
does: a CI `schedule:` trigger (the github lexicon renders an Op's `schedule`
|
|
139
|
+
as one), a CronJob, or `chant operator` on a machine that stays up. The job is
|
|
140
|
+
short, because the steward does the work.
|
|
141
|
+
|
|
142
|
+
## Limits
|
|
143
|
+
|
|
144
|
+
- Workspace members: `status`, `log` and `list` read the root project's run
|
|
145
|
+
ledger only.
|
|
146
|
+
- `declareSteward`'s forms are `local` and `fountain`; there is no `e2b` form
|
|
147
|
+
yet, so a steward's schedules don't render to anything here. Drive them from
|
|
148
|
+
a clock as above.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one kind the e2b lexicon declares, described once.
|
|
3
|
+
*
|
|
4
|
+
* The LSP's completions and hover, the bundle's `dist/meta.json` and the docs'
|
|
5
|
+
* resource reference all read this table, so a property added to `Template`
|
|
6
|
+
* is added here and reaches all three.
|
|
7
|
+
*/
|
|
8
|
+
import type { LexiconEntry } from "@intentius/chant/lsp/lexicon-providers";
|
|
9
|
+
export declare const TEMPLATE_TYPE = "E2B::Template";
|
|
10
|
+
export interface PropertyDoc {
|
|
11
|
+
type: string;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
description: string;
|
|
14
|
+
}
|
|
15
|
+
/** `Template`'s properties, in the order the hover lists them. */
|
|
16
|
+
export declare const TEMPLATE_PROPERTIES: Record<string, PropertyDoc>;
|
|
17
|
+
/** The registry the LSP and `dist/meta.json` read: one resource entry. */
|
|
18
|
+
export declare const LEXICON_ENTRIES: Record<string, LexiconEntry>;
|
|
19
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAE3E,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,kEAAkE;AAClE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAwC3D,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CASxD,CAAC"}
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one kind the e2b lexicon declares, described once.
|
|
3
|
+
*
|
|
4
|
+
* The LSP's completions and hover, the bundle's `dist/meta.json` and the docs'
|
|
5
|
+
* resource reference all read this table, so a property added to `Template`
|
|
6
|
+
* is added here and reaches all three.
|
|
7
|
+
*/
|
|
8
|
+
export const TEMPLATE_TYPE = "E2B::Template";
|
|
9
|
+
/** `Template`'s properties, in the order the hover lists them. */
|
|
10
|
+
export const TEMPLATE_PROPERTIES = {
|
|
11
|
+
name: {
|
|
12
|
+
type: "string",
|
|
13
|
+
required: true,
|
|
14
|
+
description: "The template's name on E2B: lowercase letters, digits, dashes and underscores. A profile's `template` names it.",
|
|
15
|
+
},
|
|
16
|
+
fromImage: {
|
|
17
|
+
type: "string",
|
|
18
|
+
required: true,
|
|
19
|
+
description: "The image the template starts from, such as `node:22-bookworm`. Pin a tag.",
|
|
20
|
+
},
|
|
21
|
+
run: {
|
|
22
|
+
type: "string[]",
|
|
23
|
+
description: "Shell commands run in order while the template builds, one `RUN` line each.",
|
|
24
|
+
},
|
|
25
|
+
env: {
|
|
26
|
+
type: "Record<string, string>",
|
|
27
|
+
description: "Environment variables set in every sandbox made from the template.",
|
|
28
|
+
},
|
|
29
|
+
workdir: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "The working directory for the `run` steps and the start command.",
|
|
32
|
+
},
|
|
33
|
+
startCmd: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "The start command (`--cmd`): E2B runs it when the template is snapshotted, so a sandbox starts with it running.",
|
|
36
|
+
},
|
|
37
|
+
readyCmd: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "The ready command (`--ready-cmd`): E2B polls it until it exits 0 before it snapshots the template.",
|
|
40
|
+
},
|
|
41
|
+
cpuCount: {
|
|
42
|
+
type: "number",
|
|
43
|
+
description: "vCPUs per sandbox. A positive integer; E2B's default is 2.",
|
|
44
|
+
},
|
|
45
|
+
memoryMB: {
|
|
46
|
+
type: "number",
|
|
47
|
+
description: "Memory per sandbox in MiB. A positive even integer; E2B's default is 1024.",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
/** The registry the LSP and `dist/meta.json` read: one resource entry. */
|
|
51
|
+
export const LEXICON_ENTRIES = {
|
|
52
|
+
Template: {
|
|
53
|
+
resourceType: TEMPLATE_TYPE,
|
|
54
|
+
kind: "resource",
|
|
55
|
+
lexicon: "e2b",
|
|
56
|
+
propertyConstraints: Object.fromEntries(Object.entries(TEMPLATE_PROPERTIES).map(([name, p]) => [name, { type: p.type, required: p.required ?? false }])),
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-cli.d.ts","sourceRoot":"","sources":["../../src/codegen/docs-cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* e2b documentation generator.
|
|
3
|
+
*
|
|
4
|
+
* Calls the core docsPipeline. The how-to and reference pages are authored
|
|
5
|
+
* MDX under `docs/pages/`, each tagged with its Diátaxis quadrant; this file
|
|
6
|
+
* holds the overview and the serialization page. writeDocsSite puts the pages
|
|
7
|
+
* in the main docs site under docs/src/content/docs/lexicons/e2b/ (chant #3993).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Generate the e2b lexicon's docs.
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateDocs(options?: {
|
|
13
|
+
verbose?: boolean;
|
|
14
|
+
}): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=docs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/codegen/docs.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAmGH;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBjF"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* e2b documentation generator.
|
|
3
|
+
*
|
|
4
|
+
* Calls the core docsPipeline. The how-to and reference pages are authored
|
|
5
|
+
* MDX under `docs/pages/`, each tagged with its Diátaxis quadrant; this file
|
|
6
|
+
* holds the overview and the serialization page. writeDocsSite puts the pages
|
|
7
|
+
* in the main docs site under docs/src/content/docs/lexicons/e2b/ (chant #3993).
|
|
8
|
+
*/
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import { docsPipeline, writeDocsSite } from "@intentius/chant/codegen/docs";
|
|
12
|
+
const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
13
|
+
const overview = `The **e2b** lexicon runs chant Ops on a steward: one [E2B](https://e2b.dev) sandbox that outlives every run. \`chant run <op> --on e2b\` hands the run to it. The steward keeps a clone of the project and its installed tools on its disk, runs one Op at a time, and sleeps paused between runs. Any API that speaks E2B's can host it, hosted E2B or a platform of your own.
|
|
14
|
+
|
|
15
|
+
The lexicon also declares one kind, \`Template\`, for the image a steward is made from. \`chant build\` writes each template as \`e2b.<name>.Dockerfile\`, and the \`e2b template create\` command line that builds it.
|
|
16
|
+
|
|
17
|
+
Install it with:
|
|
18
|
+
|
|
19
|
+
\`\`\`bash
|
|
20
|
+
npm install --save-dev @intentius/chant-lexicon-e2b
|
|
21
|
+
\`\`\`
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
Point a profile at your E2B account in \`chant.config.ts\`:
|
|
26
|
+
|
|
27
|
+
\`\`\`typescript
|
|
28
|
+
import type { ChantConfig } from "@intentius/chant/config";
|
|
29
|
+
import "@intentius/chant-lexicon-e2b";
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
lexicons: ["e2b"],
|
|
33
|
+
e2b: {
|
|
34
|
+
profiles: {
|
|
35
|
+
prod: {
|
|
36
|
+
apiKey: { env: "E2B_API_KEY" },
|
|
37
|
+
template: "chant-steward",
|
|
38
|
+
gitToken: { env: "GITHUB_TOKEN" },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
defaultProfile: "prod",
|
|
42
|
+
},
|
|
43
|
+
} satisfies ChantConfig;
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
Declare the template it names, with the tools the project's Ops call:
|
|
47
|
+
|
|
48
|
+
\`\`\`typescript
|
|
49
|
+
import { Template } from "@intentius/chant-lexicon-e2b";
|
|
50
|
+
|
|
51
|
+
const steward = new Template({
|
|
52
|
+
name: "chant-steward",
|
|
53
|
+
fromImage: "node:22-bookworm",
|
|
54
|
+
run: ["apt-get update && apt-get install -y --no-install-recommends jq && rm -rf /var/lib/apt/lists/*"],
|
|
55
|
+
cpuCount: 2,
|
|
56
|
+
memoryMB: 2048,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export { steward };
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
Then build the template once and run an Op on the steward:
|
|
63
|
+
|
|
64
|
+
\`\`\`bash
|
|
65
|
+
chant build src --lexicon e2b -o dist/e2b.json
|
|
66
|
+
(cd dist && e2b template create chant-steward --dockerfile e2b.chant-steward.Dockerfile --cpu-count 2 --memory-mb 2048)
|
|
67
|
+
chant run prod-apply --on e2b
|
|
68
|
+
\`\`\``;
|
|
69
|
+
const outputFormat = `The e2b lexicon writes each \`Template\` as a Dockerfile named after the template, so several can share a directory, and indexes them with the command that builds each one.
|
|
70
|
+
|
|
71
|
+
## Building
|
|
72
|
+
|
|
73
|
+
\`e2b.<name>.Dockerfile\` starts \`FROM\` the template's \`fromImage\`. Each \`env\` entry becomes an \`ENV\` line and each \`run\` step a \`RUN\` line, in order, with \`WORKDIR\` between them when \`workdir\` is set:
|
|
74
|
+
|
|
75
|
+
\`\`\`dockerfile
|
|
76
|
+
FROM node:22-bookworm
|
|
77
|
+
ENV CI="1"
|
|
78
|
+
RUN apt-get update && apt-get install -y --no-install-recommends jq && rm -rf /var/lib/apt/lists/*
|
|
79
|
+
\`\`\`
|
|
80
|
+
|
|
81
|
+
The primary output is a JSON index of the templates, keyed by the name each was exported as. \`build\` is the \`e2b template create\` command line for the template. It names the Dockerfile and passes \`--cmd\`, \`--ready-cmd\`, \`--cpu-count\` and \`--memory-mb\` only when the declaration sets them:
|
|
82
|
+
|
|
83
|
+
\`\`\`json
|
|
84
|
+
{
|
|
85
|
+
"templates": {
|
|
86
|
+
"steward": {
|
|
87
|
+
"name": "chant-steward",
|
|
88
|
+
"dockerfile": "e2b.chant-steward.Dockerfile",
|
|
89
|
+
"build": "e2b template create chant-steward --dockerfile e2b.chant-steward.Dockerfile --cpu-count 2 --memory-mb 2048",
|
|
90
|
+
"cpuCount": 2,
|
|
91
|
+
"memoryMB": 2048
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
A project that declares no template gets \`{ "templates": {} }\`.
|
|
98
|
+
|
|
99
|
+
## Applying
|
|
100
|
+
|
|
101
|
+
chant does not build templates. Run each template's \`build\` command in the output directory, once, and again when the declaration changes. A profile's \`template\` then names the template, and the next steward made from that profile starts from it. A steward that already exists keeps the sandbox it has; delete it to have the next run make a new one.`;
|
|
102
|
+
/**
|
|
103
|
+
* Generate the e2b lexicon's docs.
|
|
104
|
+
*/
|
|
105
|
+
export async function generateDocs(options) {
|
|
106
|
+
const config = {
|
|
107
|
+
name: "e2b",
|
|
108
|
+
displayName: "E2B",
|
|
109
|
+
description: "Run chant Ops on a steward sandbox on E2B, and declare the template it is made from",
|
|
110
|
+
distDir: join(pkgDir, "dist"),
|
|
111
|
+
outDir: join(pkgDir, "docs"),
|
|
112
|
+
srcDir: join(pkgDir, "src"),
|
|
113
|
+
basePath: process.env.DOCS_BASE_PATH ?? "/chant/lexicons/e2b/",
|
|
114
|
+
overview,
|
|
115
|
+
outputFormat,
|
|
116
|
+
serviceFromType: (type) => type.split("::")[1] ?? type,
|
|
117
|
+
};
|
|
118
|
+
const result = docsPipeline(config);
|
|
119
|
+
writeDocsSite(config, result);
|
|
120
|
+
if (options?.verbose) {
|
|
121
|
+
console.error(`Generated docs: ${result.stats.resources} resources, ${result.stats.rules} rules`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-cli.d.ts","sourceRoot":"","sources":["../../src/codegen/generate-cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation for the e2b lexicon.
|
|
3
|
+
*
|
|
4
|
+
* The lexicon's one kind is written by hand (../resources.ts), so there is no
|
|
5
|
+
* upstream spec to generate types from. What is generated is the static
|
|
6
|
+
* barrel of post-synth checks, `src/lint/post-synth/index.ts`, the same file
|
|
7
|
+
* every lexicon's generate step writes (#409).
|
|
8
|
+
*/
|
|
9
|
+
/** Write the post-synth barrel and return how many checks it lists. */
|
|
10
|
+
export declare function generate(): number;
|
|
11
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/codegen/generate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,uEAAuE;AACvE,wBAAgB,QAAQ,IAAI,MAAM,CAMjC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation for the e2b lexicon.
|
|
3
|
+
*
|
|
4
|
+
* The lexicon's one kind is written by hand (../resources.ts), so there is no
|
|
5
|
+
* upstream spec to generate types from. What is generated is the static
|
|
6
|
+
* barrel of post-synth checks, `src/lint/post-synth/index.ts`, the same file
|
|
7
|
+
* every lexicon's generate step writes (#409).
|
|
8
|
+
*/
|
|
9
|
+
import { writeFileSync } from "fs";
|
|
10
|
+
import { dirname, join } from "path";
|
|
11
|
+
import { moduleSourceDir } from "@intentius/chant/module-source";
|
|
12
|
+
import { renderPostSynthBarrelForDir } from "@intentius/chant/codegen/generate-post-synth-barrel";
|
|
13
|
+
/** Write the post-synth barrel and return how many checks it lists. */
|
|
14
|
+
export function generate() {
|
|
15
|
+
const srcDir = dirname(moduleSourceDir(import.meta.url));
|
|
16
|
+
const postSynthDir = join(srcDir, "lint", "post-synth");
|
|
17
|
+
const content = renderPostSynthBarrelForDir(postSynthDir, import.meta.url);
|
|
18
|
+
writeFileSync(join(postSynthDir, "index.ts"), content);
|
|
19
|
+
return (content.match(/^ \w/gm) ?? []).length;
|
|
20
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package the e2b lexicon: `dist/manifest.json`, `dist/meta.json`, the rules
|
|
3
|
+
* and the skills. There is no generation step to run first; the registry is
|
|
4
|
+
* the hand-written catalog in ../catalog.ts.
|
|
5
|
+
*/
|
|
6
|
+
export declare function packageLexicon(options?: {
|
|
7
|
+
verbose?: boolean;
|
|
8
|
+
force?: boolean;
|
|
9
|
+
}): Promise<import("@intentius/chant").PackageResult>;
|
|
10
|
+
//# sourceMappingURL=package.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../src/codegen/package.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,qDA0BpF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { packagePipeline, collectSkills } from "@intentius/chant/codegen/package";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
import { moduleSourceDir } from "@intentius/chant/module-source";
|
|
5
|
+
import { LEXICON_ENTRIES } from "../catalog.js";
|
|
6
|
+
import { e2bPlugin } from "../plugin.js";
|
|
7
|
+
/**
|
|
8
|
+
* Package the e2b lexicon: `dist/manifest.json`, `dist/meta.json`, the rules
|
|
9
|
+
* and the skills. There is no generation step to run first; the registry is
|
|
10
|
+
* the hand-written catalog in ../catalog.ts.
|
|
11
|
+
*/
|
|
12
|
+
export async function packageLexicon(options) {
|
|
13
|
+
const srcDir = dirname(moduleSourceDir(import.meta.url));
|
|
14
|
+
const pkgJson = JSON.parse(readFileSync(join(srcDir, "..", "package.json"), "utf-8"));
|
|
15
|
+
return packagePipeline({
|
|
16
|
+
generate: async () => ({
|
|
17
|
+
lexiconJSON: JSON.stringify(LEXICON_ENTRIES, null, 2),
|
|
18
|
+
typesDTS: 'export * from "../index.js";\n',
|
|
19
|
+
indexTS: "",
|
|
20
|
+
resources: Object.values(LEXICON_ENTRIES).filter((e) => e.kind === "resource").length,
|
|
21
|
+
properties: 0,
|
|
22
|
+
enums: 0,
|
|
23
|
+
warnings: [],
|
|
24
|
+
}),
|
|
25
|
+
buildManifest: () => ({
|
|
26
|
+
name: "e2b",
|
|
27
|
+
version: pkgJson.version ?? "0.0.0",
|
|
28
|
+
chantVersion: ">=0.1.0",
|
|
29
|
+
namespace: "E2B",
|
|
30
|
+
}),
|
|
31
|
+
srcDir,
|
|
32
|
+
collectSkills: () => collectSkills(e2bPlugin.skills?.() ?? []),
|
|
33
|
+
}, options);
|
|
34
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `e2b` namespace in `chant.config.ts`.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import type { ChantConfig } from "@intentius/chant/config";
|
|
6
|
+
* import "@intentius/chant-lexicon-e2b"; // brings the `e2b` key into ChantConfig
|
|
7
|
+
*
|
|
8
|
+
* export default {
|
|
9
|
+
* lexicons: ["e2b"],
|
|
10
|
+
* e2b: {
|
|
11
|
+
* profiles: {
|
|
12
|
+
* prod: {
|
|
13
|
+
* apiKey: { env: "E2B_API_KEY" },
|
|
14
|
+
* domain: "e2b.loam.example.com", // omit for hosted E2B
|
|
15
|
+
* steward: "prod-steward",
|
|
16
|
+
* gitToken: { env: "GITHUB_TOKEN" },
|
|
17
|
+
* },
|
|
18
|
+
* },
|
|
19
|
+
* defaultProfile: "prod",
|
|
20
|
+
* },
|
|
21
|
+
* } satisfies ChantConfig;
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* One schema, three consumers, as for fountain's namespace: core validates the
|
|
25
|
+
* namespace against it at load, the type below is derived from it, and that
|
|
26
|
+
* type augments `ChantConfig`. A project with no `e2b.profiles` keeps working:
|
|
27
|
+
* the runtime falls back to `E2B_API_KEY`, `E2B_DOMAIN` and `CHANT_GIT_TOKEN`.
|
|
28
|
+
*/
|
|
29
|
+
import { z } from "zod";
|
|
30
|
+
import type { ChantConfig } from "@intentius/chant/config";
|
|
31
|
+
export declare const e2bProfileSchema: z.ZodObject<{
|
|
32
|
+
/** The E2B API key, by environment variable. Default `E2B_API_KEY`. */
|
|
33
|
+
apiKey: z.ZodOptional<z.ZodObject<{
|
|
34
|
+
env: z.ZodString;
|
|
35
|
+
}, z.core.$strict>>;
|
|
36
|
+
/**
|
|
37
|
+
* The E2B domain the SDK talks to: `api.<domain>` and `<port>-<id>.<domain>`.
|
|
38
|
+
* Omitted, the SDK's own default (hosted E2B, or `E2B_DOMAIN`).
|
|
39
|
+
*/
|
|
40
|
+
domain: z.ZodOptional<z.ZodString>;
|
|
41
|
+
/** The template a steward's sandbox is made from. Default `base`. It needs git and Node. */
|
|
42
|
+
template: z.ZodOptional<z.ZodString>;
|
|
43
|
+
/**
|
|
44
|
+
* The steward's name: the one sandbox every run of this profile lands on,
|
|
45
|
+
* found again by its `chant/steward` metadata. Default: the repository's name.
|
|
46
|
+
*/
|
|
47
|
+
steward: z.ZodOptional<z.ZodString>;
|
|
48
|
+
/**
|
|
49
|
+
* The repository the sandbox clones, as an https URL. Default: `origin`'s
|
|
50
|
+
* URL, with an ssh URL rewritten to https.
|
|
51
|
+
*/
|
|
52
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
53
|
+
/**
|
|
54
|
+
* A token for that repository, by environment variable, so the sandbox can
|
|
55
|
+
* fetch a private repo and push chant's ledger branch. It is handed to each
|
|
56
|
+
* command in the sandbox and never written to its disk.
|
|
57
|
+
*/
|
|
58
|
+
gitToken: z.ZodOptional<z.ZodObject<{
|
|
59
|
+
env: z.ZodString;
|
|
60
|
+
}, z.core.$strict>>;
|
|
61
|
+
}, z.core.$strict>;
|
|
62
|
+
export declare const e2bConfigSchema: z.ZodObject<{
|
|
63
|
+
profiles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
64
|
+
/** The E2B API key, by environment variable. Default `E2B_API_KEY`. */
|
|
65
|
+
apiKey: z.ZodOptional<z.ZodObject<{
|
|
66
|
+
env: z.ZodString;
|
|
67
|
+
}, z.core.$strict>>;
|
|
68
|
+
/**
|
|
69
|
+
* The E2B domain the SDK talks to: `api.<domain>` and `<port>-<id>.<domain>`.
|
|
70
|
+
* Omitted, the SDK's own default (hosted E2B, or `E2B_DOMAIN`).
|
|
71
|
+
*/
|
|
72
|
+
domain: z.ZodOptional<z.ZodString>;
|
|
73
|
+
/** The template a steward's sandbox is made from. Default `base`. It needs git and Node. */
|
|
74
|
+
template: z.ZodOptional<z.ZodString>;
|
|
75
|
+
/**
|
|
76
|
+
* The steward's name: the one sandbox every run of this profile lands on,
|
|
77
|
+
* found again by its `chant/steward` metadata. Default: the repository's name.
|
|
78
|
+
*/
|
|
79
|
+
steward: z.ZodOptional<z.ZodString>;
|
|
80
|
+
/**
|
|
81
|
+
* The repository the sandbox clones, as an https URL. Default: `origin`'s
|
|
82
|
+
* URL, with an ssh URL rewritten to https.
|
|
83
|
+
*/
|
|
84
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
85
|
+
/**
|
|
86
|
+
* A token for that repository, by environment variable, so the sandbox can
|
|
87
|
+
* fetch a private repo and push chant's ledger branch. It is handed to each
|
|
88
|
+
* command in the sandbox and never written to its disk.
|
|
89
|
+
*/
|
|
90
|
+
gitToken: z.ZodOptional<z.ZodObject<{
|
|
91
|
+
env: z.ZodString;
|
|
92
|
+
}, z.core.$strict>>;
|
|
93
|
+
}, z.core.$strict>>>;
|
|
94
|
+
/** Profile used when a run names none. */
|
|
95
|
+
defaultProfile: z.ZodOptional<z.ZodString>;
|
|
96
|
+
}, z.core.$strict>;
|
|
97
|
+
export type E2bProfile = z.infer<typeof e2bProfileSchema>;
|
|
98
|
+
export type E2bConfig = z.infer<typeof e2bConfigSchema>;
|
|
99
|
+
declare module "@intentius/chant/config" {
|
|
100
|
+
interface ChantConfig {
|
|
101
|
+
e2b?: E2bConfig;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Compile-time proof that the augmentation reaches `ChantConfig`, as in fountain's config. */
|
|
105
|
+
export type E2bConfigNamespace = NonNullable<ChantConfig["e2b"]>;
|
|
106
|
+
/**
|
|
107
|
+
* The named profile, else `defaultProfile`, else `undefined`. A name that is
|
|
108
|
+
* not declared is an error rather than a fall back to the default: running
|
|
109
|
+
* against the wrong steward because a profile name was misspelt is the one
|
|
110
|
+
* mistake a runtime must not make.
|
|
111
|
+
*/
|
|
112
|
+
export declare function resolveProfile(config: Pick<ChantConfig, "e2b"> | undefined, name?: string): E2bProfile | undefined;
|
|
113
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAK3D,eAAO,MAAM,gBAAgB;IAC3B,uEAAuE;;;;IAEvE;;;OAGG;;IAEH,4FAA4F;;IAE5F;;;OAGG;;IAEH;;;OAGG;;IAEH;;;;OAIG;;;;kBAEH,CAAC;AAEH,eAAO,MAAM,eAAe;;QA3B1B,uEAAuE;;;;QAEvE;;;WAGG;;QAEH,4FAA4F;;QAE5F;;;WAGG;;QAEH;;;WAGG;;QAEH;;;;WAIG;;;;;IAMH,0CAA0C;;kBAE1C,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,OAAO,QAAQ,yBAAyB,CAAC;IACvC,UAAU,WAAW;QACnB,GAAG,CAAC,EAAE,SAAS,CAAC;KACjB;CACF;AAED,+FAA+F;AAC/F,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;AAEjE;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAclH"}
|