@_davideast/jules-env 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/dist/cli.mjs +163 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,10 @@ Wrote .jules/shellenv
|
|
|
23
23
|
2. **Plan** — The recipe produces an execution plan: shell commands to run, environment variables to set, and paths to prepend.
|
|
24
24
|
3. **Execute** — The plan runs. Install steps that are already satisfied (checked via an optional `checkCmd`) are skipped. State is persisted to `.jules/shellenv`.
|
|
25
25
|
|
|
26
|
+
### Data recipes
|
|
27
|
+
|
|
28
|
+
Recipes can also be defined as JSON files. A data recipe is a static execution plan — no system probing, no dynamic resolution. This makes them easy to generate programmatically (e.g., by an LLM). Data recipes are validated against a Zod schema at load time. See `src/recipes/ollama.json` for an example.
|
|
29
|
+
|
|
26
30
|
## Shell environment
|
|
27
31
|
|
|
28
32
|
After execution, `.jules/shellenv` contains the environment your runtime needs:
|
|
@@ -69,9 +73,10 @@ jules-env use dart --dry-run
|
|
|
69
73
|
|
|
70
74
|
## Available recipes
|
|
71
75
|
|
|
72
|
-
| Runtime | Recipe |
|
|
73
|
-
|
|
74
|
-
| Dart | `dart` |
|
|
76
|
+
| Runtime | Recipe | Type | Description |
|
|
77
|
+
|---------|--------|------|-------------|
|
|
78
|
+
| Dart | `dart` | Code | Installs the Dart SDK (Homebrew on macOS, apt on Linux) |
|
|
79
|
+
| Ollama | `ollama` | Data | Installs Ollama with EmbeddingGemma model |
|
|
75
80
|
|
|
76
81
|
## Installation
|
|
77
82
|
|
|
@@ -95,7 +100,7 @@ bun run build
|
|
|
95
100
|
### Prerequisites
|
|
96
101
|
|
|
97
102
|
- [Bun](https://bun.sh/)
|
|
98
|
-
- [Homebrew](https://brew.sh/) (
|
|
103
|
+
- [Homebrew](https://brew.sh/) (macOS only, used by Dart recipe)
|
|
99
104
|
|
|
100
105
|
### Run tests
|
|
101
106
|
|
package/dist/cli.mjs
CHANGED
|
@@ -6011,6 +6011,18 @@ var ExecutionPlanSchema = z.object({
|
|
|
6011
6011
|
content: z.string()
|
|
6012
6012
|
})).default([])
|
|
6013
6013
|
});
|
|
6014
|
+
var DataRecipeSchema = z.object({
|
|
6015
|
+
name: z.string(),
|
|
6016
|
+
description: z.string(),
|
|
6017
|
+
defaultPreset: z.string().optional(),
|
|
6018
|
+
installSteps: z.array(ShellStepSchema),
|
|
6019
|
+
env: z.record(z.string()).default({}),
|
|
6020
|
+
paths: z.array(z.string()).default([]),
|
|
6021
|
+
files: z.array(z.object({
|
|
6022
|
+
path: z.string(),
|
|
6023
|
+
content: z.string()
|
|
6024
|
+
})).default([])
|
|
6025
|
+
});
|
|
6014
6026
|
|
|
6015
6027
|
// src/core/executor.ts
|
|
6016
6028
|
import { spawn } from "node:child_process";
|
|
@@ -6031,8 +6043,7 @@ async function executePlan(plan, dryRun) {
|
|
|
6031
6043
|
console.log(`[${step.id}] ${step.label}...`);
|
|
6032
6044
|
let skip = false;
|
|
6033
6045
|
if (step.checkCmd) {
|
|
6034
|
-
const
|
|
6035
|
-
const check = spawn(checkParts[0], checkParts.slice(1), {
|
|
6046
|
+
const check = spawn("sh", ["-c", step.checkCmd], {
|
|
6036
6047
|
stdio: "ignore"
|
|
6037
6048
|
});
|
|
6038
6049
|
const exitCode = await new Promise((res) => check.on("close", (code) => res(code ?? 1)));
|
|
@@ -6042,8 +6053,7 @@ async function executePlan(plan, dryRun) {
|
|
|
6042
6053
|
}
|
|
6043
6054
|
}
|
|
6044
6055
|
if (!skip) {
|
|
6045
|
-
const
|
|
6046
|
-
const proc = spawn(cmdParts[0], cmdParts.slice(1), {
|
|
6056
|
+
const proc = spawn("sh", ["-c", step.cmd], {
|
|
6047
6057
|
stdio: "inherit"
|
|
6048
6058
|
});
|
|
6049
6059
|
const exitCode = await new Promise((res) => proc.on("close", (code) => res(code ?? 1)));
|
|
@@ -6088,43 +6098,161 @@ async function executePlan(plan, dryRun) {
|
|
|
6088
6098
|
|
|
6089
6099
|
// src/recipes/dart.ts
|
|
6090
6100
|
import { spawnSync } from "node:child_process";
|
|
6101
|
+
async function resolveDarwin() {
|
|
6102
|
+
const installSteps = [{
|
|
6103
|
+
id: "install-dart",
|
|
6104
|
+
label: "Install Dart SDK",
|
|
6105
|
+
cmd: "brew install dart-sdk",
|
|
6106
|
+
checkCmd: "brew list --versions dart-sdk"
|
|
6107
|
+
}];
|
|
6108
|
+
let dartPrefix = "";
|
|
6109
|
+
try {
|
|
6110
|
+
const result = spawnSync("brew", ["--prefix", "dart-sdk"], { encoding: "utf-8" });
|
|
6111
|
+
if (result.status === 0) {
|
|
6112
|
+
dartPrefix = result.stdout.trim();
|
|
6113
|
+
}
|
|
6114
|
+
} catch (e) {}
|
|
6115
|
+
if (!dartPrefix) {
|
|
6116
|
+
dartPrefix = "/usr/local/opt/dart-sdk";
|
|
6117
|
+
}
|
|
6118
|
+
const env = {
|
|
6119
|
+
DART_SDK: `${dartPrefix}/libexec`
|
|
6120
|
+
};
|
|
6121
|
+
const paths = [
|
|
6122
|
+
`${dartPrefix}/bin`
|
|
6123
|
+
];
|
|
6124
|
+
return ExecutionPlanSchema.parse({ installSteps, env, paths });
|
|
6125
|
+
}
|
|
6126
|
+
async function resolveLinux() {
|
|
6127
|
+
const installSteps = [
|
|
6128
|
+
{
|
|
6129
|
+
id: "install-dart-prereqs",
|
|
6130
|
+
label: "Install prerequisites",
|
|
6131
|
+
cmd: "sudo apt-get update && sudo apt-get install -y apt-transport-https wget",
|
|
6132
|
+
checkCmd: "dpkg -s apt-transport-https && dpkg -s wget"
|
|
6133
|
+
},
|
|
6134
|
+
{
|
|
6135
|
+
id: "add-dart-signing-key",
|
|
6136
|
+
label: "Add Dart signing key",
|
|
6137
|
+
cmd: "wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/dart.gpg",
|
|
6138
|
+
checkCmd: "test -f /usr/share/keyrings/dart.gpg"
|
|
6139
|
+
},
|
|
6140
|
+
{
|
|
6141
|
+
id: "add-dart-repo",
|
|
6142
|
+
label: "Add Dart repository",
|
|
6143
|
+
cmd: "echo 'deb [signed-by=/usr/share/keyrings/dart.gpg arch=amd64] https://storage.googleapis.com/dart-archive/channels/stable/release/latest/linux/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list > /dev/null",
|
|
6144
|
+
checkCmd: "test -f /etc/apt/sources.list.d/dart_stable.list"
|
|
6145
|
+
},
|
|
6146
|
+
{
|
|
6147
|
+
id: "install-dart",
|
|
6148
|
+
label: "Install Dart SDK",
|
|
6149
|
+
cmd: "sudo apt-get update && sudo apt-get install -y dart",
|
|
6150
|
+
checkCmd: "dpkg -s dart"
|
|
6151
|
+
}
|
|
6152
|
+
];
|
|
6153
|
+
const env = {
|
|
6154
|
+
DART_SDK: "/usr/lib/dart"
|
|
6155
|
+
};
|
|
6156
|
+
const paths = [
|
|
6157
|
+
"/usr/lib/dart/bin"
|
|
6158
|
+
];
|
|
6159
|
+
return ExecutionPlanSchema.parse({ installSteps, env, paths });
|
|
6160
|
+
}
|
|
6091
6161
|
var DartRecipe = {
|
|
6092
6162
|
name: "dart",
|
|
6093
|
-
description: "Dart SDK
|
|
6163
|
+
description: "Dart SDK",
|
|
6094
6164
|
resolve: async (ctx) => {
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
const result = spawnSync("brew", ["--prefix", "dart-sdk"], { encoding: "utf-8" });
|
|
6104
|
-
if (result.status === 0) {
|
|
6105
|
-
dartPrefix = result.stdout.trim();
|
|
6106
|
-
}
|
|
6107
|
-
} catch (e) {}
|
|
6108
|
-
if (!dartPrefix) {
|
|
6109
|
-
dartPrefix = "/usr/local/opt/dart-sdk";
|
|
6110
|
-
}
|
|
6111
|
-
const env = {
|
|
6112
|
-
DART_SDK: `${dartPrefix}/libexec`
|
|
6113
|
-
};
|
|
6114
|
-
const paths = [
|
|
6115
|
-
`${dartPrefix}/bin`
|
|
6116
|
-
];
|
|
6117
|
-
return ExecutionPlanSchema.parse({
|
|
6118
|
-
installSteps,
|
|
6119
|
-
env,
|
|
6120
|
-
paths
|
|
6121
|
-
});
|
|
6165
|
+
switch (process.platform) {
|
|
6166
|
+
case "darwin":
|
|
6167
|
+
return resolveDarwin();
|
|
6168
|
+
case "linux":
|
|
6169
|
+
return resolveLinux();
|
|
6170
|
+
default:
|
|
6171
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
6172
|
+
}
|
|
6122
6173
|
}
|
|
6123
6174
|
};
|
|
6175
|
+
|
|
6176
|
+
// src/core/loader.ts
|
|
6177
|
+
function substituteVars(str, vars) {
|
|
6178
|
+
return str.replace(/\{\{(\w+)\}\}/g, (_, key) => {
|
|
6179
|
+
const val = vars[key];
|
|
6180
|
+
if (val === undefined) {
|
|
6181
|
+
throw new Error(`Missing required variable: {{${key}}}. Pass --preset to provide a value.`);
|
|
6182
|
+
}
|
|
6183
|
+
return val;
|
|
6184
|
+
});
|
|
6185
|
+
}
|
|
6186
|
+
function loadDataRecipe(data) {
|
|
6187
|
+
const parsed = DataRecipeSchema.parse(data);
|
|
6188
|
+
return {
|
|
6189
|
+
name: parsed.name,
|
|
6190
|
+
description: parsed.description,
|
|
6191
|
+
resolve: async (ctx) => {
|
|
6192
|
+
const vars = {};
|
|
6193
|
+
const preset = ctx.preset ?? parsed.defaultPreset;
|
|
6194
|
+
if (preset) {
|
|
6195
|
+
vars["preset"] = preset;
|
|
6196
|
+
}
|
|
6197
|
+
const installSteps = parsed.installSteps.map((step) => ({
|
|
6198
|
+
id: substituteVars(step.id, vars),
|
|
6199
|
+
label: substituteVars(step.label, vars),
|
|
6200
|
+
cmd: substituteVars(step.cmd, vars),
|
|
6201
|
+
...step.checkCmd ? { checkCmd: substituteVars(step.checkCmd, vars) } : {}
|
|
6202
|
+
}));
|
|
6203
|
+
return {
|
|
6204
|
+
installSteps,
|
|
6205
|
+
env: parsed.env,
|
|
6206
|
+
paths: parsed.paths,
|
|
6207
|
+
files: parsed.files
|
|
6208
|
+
};
|
|
6209
|
+
}
|
|
6210
|
+
};
|
|
6211
|
+
}
|
|
6212
|
+
// src/recipes/ollama.json
|
|
6213
|
+
var ollama_default = {
|
|
6214
|
+
name: "ollama",
|
|
6215
|
+
description: "Ollama with configurable model",
|
|
6216
|
+
installSteps: [
|
|
6217
|
+
{
|
|
6218
|
+
id: "install-zstd",
|
|
6219
|
+
label: "Install zstd",
|
|
6220
|
+
cmd: "sudo apt-get update && sudo apt-get install -y zstd",
|
|
6221
|
+
checkCmd: "dpkg -s zstd"
|
|
6222
|
+
},
|
|
6223
|
+
{
|
|
6224
|
+
id: "install-ollama",
|
|
6225
|
+
label: "Install Ollama",
|
|
6226
|
+
cmd: "curl -fsSL https://ollama.com/install.sh | sh",
|
|
6227
|
+
checkCmd: "which ollama"
|
|
6228
|
+
},
|
|
6229
|
+
{
|
|
6230
|
+
id: "enable-ollama",
|
|
6231
|
+
label: "Enable Ollama service",
|
|
6232
|
+
cmd: "sudo systemctl daemon-reload && sudo systemctl enable --now ollama",
|
|
6233
|
+
checkCmd: "systemctl is-active ollama"
|
|
6234
|
+
},
|
|
6235
|
+
{
|
|
6236
|
+
id: "wait-for-ollama",
|
|
6237
|
+
label: "Wait for Ollama to initialize",
|
|
6238
|
+
cmd: "sleep 5"
|
|
6239
|
+
},
|
|
6240
|
+
{
|
|
6241
|
+
id: "pull-model",
|
|
6242
|
+
label: "Pull {{preset}} model",
|
|
6243
|
+
cmd: "ollama pull {{preset}}",
|
|
6244
|
+
checkCmd: "ollama list | grep {{preset}}"
|
|
6245
|
+
}
|
|
6246
|
+
],
|
|
6247
|
+
env: {
|
|
6248
|
+
OLLAMA_HOST: "http://localhost:11434"
|
|
6249
|
+
},
|
|
6250
|
+
paths: []
|
|
6251
|
+
};
|
|
6124
6252
|
// package.json
|
|
6125
6253
|
var package_default = {
|
|
6126
6254
|
name: "@_davideast/jules-env",
|
|
6127
|
-
version: "0.0
|
|
6255
|
+
version: "0.1.0",
|
|
6128
6256
|
description: "Configure ephemeral development environments",
|
|
6129
6257
|
license: "Apache-2.0",
|
|
6130
6258
|
type: "module",
|
|
@@ -6164,7 +6292,8 @@ var package_default = {
|
|
|
6164
6292
|
// src/cli.ts
|
|
6165
6293
|
var program2 = new Command;
|
|
6166
6294
|
var recipes = {
|
|
6167
|
-
dart: DartRecipe
|
|
6295
|
+
dart: DartRecipe,
|
|
6296
|
+
ollama: loadDataRecipe(ollama_default)
|
|
6168
6297
|
};
|
|
6169
6298
|
program2.name("jules-env").description("Configure ephemeral development environments").version(package_default.version);
|
|
6170
6299
|
program2.command("use <runtime>").description("Setup a runtime environment").option("--version <v>", "Version to install", "latest").option("--dry-run", "Simulate execution", false).option("--preset <p>", "Configuration preset").action(async (runtime, options) => {
|