@rendotdev/rig 0.0.17 → 0.0.18
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 +4 -0
- package/dist/cli-0p4wnh3p.js +226 -0
- package/dist/{cli-mswdavqx.js → cli-96n231pn.js} +3 -3
- package/dist/{cli-7zqnqrah.js → cli-c6y9p8v9.js} +106 -42
- package/dist/{cli-dshh1cp9.js → cli-f4k31gtc.js} +1 -1
- package/dist/{cli-vx11bmzr.js → cli-ggk7t6dg.js} +2 -2
- package/dist/{cli-xv4m20sx.js → cli-jbscjyqq.js} +93 -65
- package/dist/{cli-ne7ed594.js → cli-vkw9rcke.js} +8 -2
- package/dist/{run-y66hvyxg.js → cli-yahk2r84.js} +158 -16
- package/dist/{config-24gwe08t.js → config-8mr59m0q.js} +2 -3
- package/dist/{create-59myk91d.js → create-9frp1c7g.js} +7 -8
- package/dist/cron-e6whgdf7.js +18 -0
- package/dist/{dev-link-jpte1gq9.js → dev-link-x3hjyy3j.js} +7 -4
- package/dist/{discover-fd9e6j9j.js → discover-9n7b9ra2.js} +3 -4
- package/dist/{help-6k8m07yv.js → help-17a9ywd9.js} +5 -6
- package/dist/{inspect-ks3x7s4n.js → inspect-k2g3dt1g.js} +5 -6
- package/dist/list-pahq60sp.js +13 -0
- package/dist/{paths-hq1vy0wh.js → paths-cxq6fvhp.js} +1 -1
- package/dist/{registry-gng9br0x.js → registry-e68a7b4v.js} +2 -3
- package/dist/rig.js +74 -28
- package/dist/run-apb4mx5d.js +13 -0
- package/dist/{runtime-comment-gvmkkcyh.js → runtime-comment-3yq1pmaf.js} +15 -9
- package/dist/{sync-zsc35m3f.js → sync-3362wne1.js} +33 -16
- package/dist/{typecheck-c2k87ppw.js → typecheck-5325aspe.js} +5 -7
- package/dist/{update-check-87p71vrc.js → update-check-a02ydxdb.js} +8 -5
- package/package.json +2 -2
- package/dist/cli-aj56a1ja.js +0 -48
- package/dist/list-f7r354tq.js +0 -14
package/README.md
CHANGED
|
@@ -68,11 +68,15 @@ The `rig` CLI is installed on this machine. It allows you to write, run and own
|
|
|
68
68
|
- To discover available tools, run `rig list`.
|
|
69
69
|
- To learn about a tool's usage, run `rig help <tool>`.
|
|
70
70
|
- To run a tool, use `rig run <tool>.<command> [args]`.
|
|
71
|
+
- Tools run under Bun with fallback auto-install enabled, so tool files can import npm packages; add explicit package versions when reproducibility matters.
|
|
72
|
+
- To schedule a tool command, use `rig cron add <name> <tool>.<command> <schedule> --input '<json>'`; use `rig cron list`, `rig cron run <name>`, and `rig cron remove <name>` to manage scheduled runs.
|
|
73
|
+
- If a tool needs local secrets or settings, put them in the tool folder's `.env`, add an `env` Zod schema to the tool definition, and read validated values from `context.env`.
|
|
71
74
|
- To create a new tool, run `rig create <tool>`.
|
|
72
75
|
- To edit an existing tool, run `rig edit <tool>` and open the printed file path.
|
|
73
76
|
- To remove an existing tool, run `rig remove <tool>`.
|
|
74
77
|
- To list tool registries, run `rig registry list`.
|
|
75
78
|
- To add a registry, run `rig registry create [path]` (defaults to current directory).
|
|
79
|
+
- If a tool needs persistent state, define `setupDb` and use `context.db`; Rig stores that SQLite database beside the tool entry file as `index.sqlite`.
|
|
76
80
|
|
|
77
81
|
When rig runs, it keeps detected `AGENTS.md` and `CLAUDE.md` files updated with these instructions and the current `rig list` output.
|
|
78
82
|
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ToolRunner
|
|
3
|
+
} from "./cli-yahk2r84.js";
|
|
4
|
+
import {
|
|
5
|
+
RigConfigStore
|
|
6
|
+
} from "./cli-jbscjyqq.js";
|
|
7
|
+
import {
|
|
8
|
+
RigError
|
|
9
|
+
} from "./cli-1c7te5cg.js";
|
|
10
|
+
import {
|
|
11
|
+
RigPaths
|
|
12
|
+
} from "./cli-vkw9rcke.js";
|
|
13
|
+
|
|
14
|
+
// src/tools/cron.ts
|
|
15
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
16
|
+
import { dirname } from "node:path";
|
|
17
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
18
|
+
class BunCronRegistrar {
|
|
19
|
+
register(path, schedule, title) {
|
|
20
|
+
return this.cron()(path, schedule, title);
|
|
21
|
+
}
|
|
22
|
+
remove(title) {
|
|
23
|
+
return this.cron().remove(title);
|
|
24
|
+
}
|
|
25
|
+
validate(schedule) {
|
|
26
|
+
const next = this.cron().parse(schedule);
|
|
27
|
+
if (!next)
|
|
28
|
+
throw new RigError("CRON_ERROR", `Cron schedule has no future runs: ${schedule}`);
|
|
29
|
+
}
|
|
30
|
+
cron() {
|
|
31
|
+
const cron = globalThis.Bun?.cron;
|
|
32
|
+
if (typeof cron !== "function") {
|
|
33
|
+
throw new RigError("CRON_ERROR", "Bun cron is unavailable. Run rig with Bun.");
|
|
34
|
+
}
|
|
35
|
+
return cron;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class CronJobName {
|
|
40
|
+
value;
|
|
41
|
+
constructor(value) {
|
|
42
|
+
this.value = value;
|
|
43
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value)) {
|
|
44
|
+
throw new RigError("INPUT_ERROR", "Cron job names may only contain letters, numbers, hyphens, and underscores.", { name: value });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class CronInputReader {
|
|
50
|
+
async read(options) {
|
|
51
|
+
if (options.input && options.inputFile) {
|
|
52
|
+
throw new RigError("INPUT_ERROR", "Use --input or --input-file, not both.");
|
|
53
|
+
}
|
|
54
|
+
if (options.inputFile) {
|
|
55
|
+
return typeof Bun !== "undefined" ? Bun.file(options.inputFile).json() : JSON.parse(await readFile(options.inputFile, "utf8"));
|
|
56
|
+
}
|
|
57
|
+
return options.input === undefined ? undefined : JSON.parse(options.input);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class CronCommandTarget {
|
|
62
|
+
id;
|
|
63
|
+
tool;
|
|
64
|
+
command;
|
|
65
|
+
constructor(id) {
|
|
66
|
+
this.id = id;
|
|
67
|
+
const parts = id.split(".");
|
|
68
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
69
|
+
throw new RigError("INPUT_ERROR", `Command id must use <tool>.<command>: ${id}`);
|
|
70
|
+
}
|
|
71
|
+
this.tool = parts[0];
|
|
72
|
+
this.command = parts[1];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class CronWorkerScript {
|
|
77
|
+
render(params) {
|
|
78
|
+
const entrypoint = fileURLToPath(params.moduleUrl);
|
|
79
|
+
const env = params.homeDir ? { RIG_HOME: params.homeDir } : {};
|
|
80
|
+
return `export default {
|
|
81
|
+
async scheduled() {
|
|
82
|
+
const proc = Bun.spawn([
|
|
83
|
+
process.execPath,
|
|
84
|
+
"--install=fallback",
|
|
85
|
+
${JSON.stringify(entrypoint)},
|
|
86
|
+
"cron",
|
|
87
|
+
"run",
|
|
88
|
+
${JSON.stringify(params.name)},
|
|
89
|
+
], {
|
|
90
|
+
stdout: "pipe",
|
|
91
|
+
stderr: "pipe",
|
|
92
|
+
env: { ...process.env, ...${JSON.stringify(env)} },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
96
|
+
new Response(proc.stdout).text(),
|
|
97
|
+
new Response(proc.stderr).text(),
|
|
98
|
+
proc.exited,
|
|
99
|
+
]);
|
|
100
|
+
if (stdout) console.log(stdout.trimEnd());
|
|
101
|
+
if (stderr) console.error(stderr.trimEnd());
|
|
102
|
+
if (exitCode !== 0) throw new Error("Rig cron job failed: " + ${JSON.stringify(params.name)});
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class RigCronService {
|
|
110
|
+
options;
|
|
111
|
+
registrar;
|
|
112
|
+
paths;
|
|
113
|
+
configStore;
|
|
114
|
+
inputReader = new CronInputReader;
|
|
115
|
+
workerScript = new CronWorkerScript;
|
|
116
|
+
constructor(options = {}, registrar = new BunCronRegistrar) {
|
|
117
|
+
this.options = options;
|
|
118
|
+
this.registrar = registrar;
|
|
119
|
+
this.paths = new RigPaths(options);
|
|
120
|
+
this.configStore = new RigConfigStore(options);
|
|
121
|
+
}
|
|
122
|
+
async list() {
|
|
123
|
+
const config = await this.configStore.ensure();
|
|
124
|
+
return {
|
|
125
|
+
cronJobs: [...config.cronJobs].toSorted((left, right) => left.name.localeCompare(right.name))
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async add(options) {
|
|
129
|
+
const name = new CronJobName(options.name);
|
|
130
|
+
const target = new CronCommandTarget(options.command);
|
|
131
|
+
const input = await this.inputReader.read(options);
|
|
132
|
+
this.registrar.validate(options.schedule);
|
|
133
|
+
await this.validateCommand(target, input);
|
|
134
|
+
const config = await this.configStore.ensure();
|
|
135
|
+
const job = {
|
|
136
|
+
name: name.value,
|
|
137
|
+
command: target.id,
|
|
138
|
+
schedule: options.schedule,
|
|
139
|
+
...input === undefined ? {} : { input }
|
|
140
|
+
};
|
|
141
|
+
const workerPath = this.paths.cronWorkerPath(name.value);
|
|
142
|
+
await this.writeWorker(workerPath, {
|
|
143
|
+
name: name.value,
|
|
144
|
+
homeDir: this.options.homeDir,
|
|
145
|
+
moduleUrl: options.moduleUrl
|
|
146
|
+
});
|
|
147
|
+
await this.configStore.write({
|
|
148
|
+
...config,
|
|
149
|
+
cronJobs: [...config.cronJobs.filter((existing) => existing.name !== name.value), job]
|
|
150
|
+
});
|
|
151
|
+
try {
|
|
152
|
+
await this.registrar.register(workerPath, options.schedule, name.value);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
await this.configStore.write(config);
|
|
155
|
+
await rm(workerPath, { force: true });
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
return { job, workerPath };
|
|
159
|
+
}
|
|
160
|
+
async remove(nameValue) {
|
|
161
|
+
const name = new CronJobName(nameValue);
|
|
162
|
+
const config = await this.configStore.ensure();
|
|
163
|
+
const workerPath = this.paths.cronWorkerPath(name.value);
|
|
164
|
+
const nextJobs = config.cronJobs.filter((job) => job.name !== name.value);
|
|
165
|
+
await this.configStore.write({ ...config, cronJobs: nextJobs });
|
|
166
|
+
await this.registrar.remove(name.value);
|
|
167
|
+
await rm(workerPath, { force: true });
|
|
168
|
+
return { name: name.value, removed: nextJobs.length !== config.cronJobs.length, workerPath };
|
|
169
|
+
}
|
|
170
|
+
async run(nameValue) {
|
|
171
|
+
const name = new CronJobName(nameValue);
|
|
172
|
+
const config = await this.configStore.ensure();
|
|
173
|
+
const job = config.cronJobs.find((candidate) => candidate.name === name.value);
|
|
174
|
+
if (!job)
|
|
175
|
+
throw new RigError("CRON_ERROR", `Cron job not found: ${name.value}`, { name });
|
|
176
|
+
const target = new CronCommandTarget(job.command);
|
|
177
|
+
const result = await new ToolRunner(this.options).run(target.tool, target.command, {
|
|
178
|
+
...this.options,
|
|
179
|
+
input: job.input === undefined ? undefined : JSON.stringify(job.input)
|
|
180
|
+
});
|
|
181
|
+
return { job, envelope: result.envelope, exitCode: result.exitCode };
|
|
182
|
+
}
|
|
183
|
+
async validateCommand(target, input) {
|
|
184
|
+
const result = await new ToolRunner(this.options).run(target.tool, target.command, {
|
|
185
|
+
...this.options,
|
|
186
|
+
input: input === undefined ? undefined : JSON.stringify(input),
|
|
187
|
+
dryRun: true
|
|
188
|
+
});
|
|
189
|
+
if (result.exitCode !== 0) {
|
|
190
|
+
throw new RigError("CRON_ERROR", `Cron command validation failed: ${target.id}`, {
|
|
191
|
+
envelope: result.envelope
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async writeWorker(workerPath, params) {
|
|
196
|
+
await mkdir(dirname(workerPath), { recursive: true });
|
|
197
|
+
await writeFile(workerPath, this.workerScript.render(params), "utf8");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
class RigCronWorker {
|
|
202
|
+
options;
|
|
203
|
+
constructor(options = {}) {
|
|
204
|
+
this.options = options;
|
|
205
|
+
}
|
|
206
|
+
async scheduled(name, controller) {
|
|
207
|
+
const result = await new RigCronService(this.options).run(name);
|
|
208
|
+
const envelope = result.envelope;
|
|
209
|
+
console.log(JSON.stringify({
|
|
210
|
+
cron: {
|
|
211
|
+
name: result.job.name,
|
|
212
|
+
command: result.job.command,
|
|
213
|
+
schedule: controller?.cron ?? result.job.schedule,
|
|
214
|
+
scheduledTime: controller?.scheduledTime,
|
|
215
|
+
type: controller?.type
|
|
216
|
+
},
|
|
217
|
+
result: envelope
|
|
218
|
+
}, null, 2));
|
|
219
|
+
process.exitCode = result.exitCode;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function cronModuleUrl(metaUrl) {
|
|
223
|
+
return pathToFileURL(fileURLToPath(metaUrl)).href;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export { RigCronService, RigCronWorker, cronModuleUrl };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SchemaRenderer
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-f4k31gtc.js";
|
|
4
4
|
import {
|
|
5
5
|
CommandIds,
|
|
6
6
|
ToolLoader
|
|
7
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-c6y9p8v9.js";
|
|
8
8
|
import {
|
|
9
9
|
ToolDiscoveryService
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-ggk7t6dg.js";
|
|
11
11
|
|
|
12
12
|
// src/tools/list.ts
|
|
13
13
|
class CommandRunExampleRenderer {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ToolDiscoveryService
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-ggk7t6dg.js";
|
|
4
4
|
import {
|
|
5
5
|
exports_external
|
|
6
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-jbscjyqq.js";
|
|
7
7
|
import {
|
|
8
8
|
RigError
|
|
9
9
|
} from "./cli-1c7te5cg.js";
|
|
@@ -12,8 +12,6 @@ import {
|
|
|
12
12
|
} from "./cli-b7jgjgy7.js";
|
|
13
13
|
|
|
14
14
|
// src/tools/types.ts
|
|
15
|
-
var RigSchemaRoleSymbol = Symbol.for("rig.schemaRole");
|
|
16
|
-
|
|
17
15
|
class CommandIds {
|
|
18
16
|
static from(tool, command) {
|
|
19
17
|
return `${tool}.${command}`;
|
|
@@ -238,7 +236,7 @@ class RigCommandRunnerRuntime {
|
|
|
238
236
|
}
|
|
239
237
|
async run(options) {
|
|
240
238
|
const target = this.commandTarget(options);
|
|
241
|
-
const { ToolRunner } = await import("./run-
|
|
239
|
+
const { ToolRunner } = await import("./run-apb4mx5d.js");
|
|
242
240
|
const result = await new ToolRunner(this.options).run(target.tool, target.command, {
|
|
243
241
|
...this.options,
|
|
244
242
|
args: options.args,
|
|
@@ -272,9 +270,7 @@ class RigToolKitFactory {
|
|
|
272
270
|
return {
|
|
273
271
|
z: exports_external,
|
|
274
272
|
defineTool: (definition) => definition,
|
|
275
|
-
|
|
276
|
-
input: (value) => this.schema(value, "input"),
|
|
277
|
-
output: (value) => this.schema(value, "output"),
|
|
273
|
+
defineCommand: (definition) => definition,
|
|
278
274
|
run: (options) => new RigCommandRunnerRuntime(this.options).run(options),
|
|
279
275
|
$: (strings, ...values) => shell.$(strings, ...values),
|
|
280
276
|
args: () => new RigArgsRuntime,
|
|
@@ -282,27 +278,9 @@ class RigToolKitFactory {
|
|
|
282
278
|
shell
|
|
283
279
|
};
|
|
284
280
|
}
|
|
285
|
-
schema(value, role) {
|
|
286
|
-
const schema = this.isZodSchema(value) ? value : exports_external.object(value);
|
|
287
|
-
const existingRole = schema[RigSchemaRoleSymbol];
|
|
288
|
-
if (existingRole === role)
|
|
289
|
-
return schema;
|
|
290
|
-
Object.defineProperty(schema, RigSchemaRoleSymbol, {
|
|
291
|
-
value: role,
|
|
292
|
-
configurable: false,
|
|
293
|
-
enumerable: false,
|
|
294
|
-
writable: false
|
|
295
|
-
});
|
|
296
|
-
return schema;
|
|
297
|
-
}
|
|
298
|
-
isZodSchema(value) {
|
|
299
|
-
return typeof value === "object" && value !== null && typeof value.safeParse === "function";
|
|
300
|
-
}
|
|
301
281
|
}
|
|
302
282
|
var rig = new RigToolKitFactory().create();
|
|
303
|
-
var
|
|
304
|
-
var input = rig.input;
|
|
305
|
-
var output = rig.output;
|
|
283
|
+
var defineCommand = rig.defineCommand;
|
|
306
284
|
var run = rig.run;
|
|
307
285
|
var args = rig.args;
|
|
308
286
|
var paths = rig.paths;
|
|
@@ -311,6 +289,9 @@ function createRigToolKit(options = {}) {
|
|
|
311
289
|
}
|
|
312
290
|
|
|
313
291
|
// src/tools/loader.ts
|
|
292
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
293
|
+
import { readFile } from "node:fs/promises";
|
|
294
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
314
295
|
import { pathToFileURL } from "node:url";
|
|
315
296
|
class ToolDefinitionValidator {
|
|
316
297
|
validateToolName(name) {
|
|
@@ -346,6 +327,14 @@ class ToolDefinitionValidator {
|
|
|
346
327
|
expected: "non-empty string"
|
|
347
328
|
});
|
|
348
329
|
}
|
|
330
|
+
if (value.setupDb !== undefined && typeof value.setupDb !== "function") {
|
|
331
|
+
throw new RigError("TOOL_INVALID", `Tool ${value.name} setupDb must be a function.`, {
|
|
332
|
+
expected: "function"
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
if (value.env !== undefined) {
|
|
336
|
+
this.validateSchema(value.env, "env", value.name);
|
|
337
|
+
}
|
|
349
338
|
if (!this.isRecord(value.commands)) {
|
|
350
339
|
throw new RigError("TOOL_INVALID", `Tool ${value.name} needs a commands object.`, {
|
|
351
340
|
expected: "object"
|
|
@@ -355,8 +344,8 @@ class ToolDefinitionValidator {
|
|
|
355
344
|
if (entries.length === 0) {
|
|
356
345
|
throw new RigError("TOOL_INVALID", `Tool ${value.name} must define at least one command.`);
|
|
357
346
|
}
|
|
358
|
-
for (const [commandName,
|
|
359
|
-
this.validateCommand(commandName,
|
|
347
|
+
for (const [commandName, command] of entries) {
|
|
348
|
+
this.validateCommand(commandName, command, value.name);
|
|
360
349
|
}
|
|
361
350
|
return value;
|
|
362
351
|
}
|
|
@@ -406,15 +395,8 @@ class ToolDefinitionValidator {
|
|
|
406
395
|
}
|
|
407
396
|
validateSchema(value, role, id) {
|
|
408
397
|
if (!this.hasSafeParse(value)) {
|
|
409
|
-
throw new RigError("TOOL_INVALID", `Command ${id} needs a
|
|
410
|
-
expected: `
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
const actualRole = value[RigSchemaRoleSymbol];
|
|
414
|
-
if (actualRole !== role) {
|
|
415
|
-
throw new RigError("TOOL_INVALID", `Command ${id} needs a Rig ${role} schema.`, {
|
|
416
|
-
expected: `rig.${role}(...)`,
|
|
417
|
-
actual: actualRole ?? "unbranded Zod schema"
|
|
398
|
+
throw new RigError("TOOL_INVALID", `Command ${id} needs a Zod schema for ${role}.`, {
|
|
399
|
+
expected: `rig.z.object({ ... })`
|
|
418
400
|
});
|
|
419
401
|
}
|
|
420
402
|
}
|
|
@@ -426,9 +408,90 @@ class ToolDefinitionValidator {
|
|
|
426
408
|
}
|
|
427
409
|
}
|
|
428
410
|
|
|
411
|
+
class ToolEnvFileParser {
|
|
412
|
+
parse(source, path) {
|
|
413
|
+
const env = {};
|
|
414
|
+
for (const [index, line] of source.split(/\r?\n/).entries()) {
|
|
415
|
+
const parsed = this.parseLine(line, path, index + 1);
|
|
416
|
+
if (parsed)
|
|
417
|
+
env[parsed.key] = parsed.value;
|
|
418
|
+
}
|
|
419
|
+
return env;
|
|
420
|
+
}
|
|
421
|
+
parseLine(line, path, lineNumber) {
|
|
422
|
+
const trimmed = line.trim();
|
|
423
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
424
|
+
return;
|
|
425
|
+
const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(trimmed);
|
|
426
|
+
if (!match) {
|
|
427
|
+
throw new RigError("TOOL_INVALID", "Invalid .env line.", {
|
|
428
|
+
path,
|
|
429
|
+
line: lineNumber
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return { key: match[1], value: this.parseValue(match[2]) };
|
|
433
|
+
}
|
|
434
|
+
parseValue(value) {
|
|
435
|
+
const trimmed = value.trim();
|
|
436
|
+
if (trimmed.length < 2)
|
|
437
|
+
return trimmed;
|
|
438
|
+
const quote = trimmed[0];
|
|
439
|
+
if (quote !== '"' && quote !== "'" || trimmed.at(-1) !== quote)
|
|
440
|
+
return trimmed;
|
|
441
|
+
const inner = trimmed.slice(1, -1);
|
|
442
|
+
if (quote === "'")
|
|
443
|
+
return inner;
|
|
444
|
+
return inner.replaceAll("\\n", `
|
|
445
|
+
`).replaceAll("\\r", "\r").replaceAll("\\t", "\t").replaceAll("\\\"", '"').replaceAll("\\\\", "\\");
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
class ToolEnvLoader {
|
|
450
|
+
parser = new ToolEnvFileParser;
|
|
451
|
+
async load(tool, definition) {
|
|
452
|
+
const envPath = join2(dirname2(tool.toolPath), ".env");
|
|
453
|
+
const fileExists = await this.exists(envPath);
|
|
454
|
+
if (!definition.env) {
|
|
455
|
+
if (fileExists) {
|
|
456
|
+
throw new RigError("TOOL_INVALID", `Tool ${definition.name} has .env but no env schema.`, {
|
|
457
|
+
path: envPath
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
return {};
|
|
461
|
+
}
|
|
462
|
+
const rawEnv = fileExists ? this.parser.parse(await this.readText(envPath), envPath) : {};
|
|
463
|
+
const result = definition.env.safeParse(rawEnv);
|
|
464
|
+
if (!result.success) {
|
|
465
|
+
throw new RigError("TOOL_INVALID", `Tool ${definition.name} env is invalid.`, {
|
|
466
|
+
path: envPath,
|
|
467
|
+
errors: result.error.flatten()
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
return result.data;
|
|
471
|
+
}
|
|
472
|
+
async exists(path) {
|
|
473
|
+
const bunFile = this.bunFile();
|
|
474
|
+
if (bunFile) {
|
|
475
|
+
return bunFile(path).exists();
|
|
476
|
+
}
|
|
477
|
+
return existsSync2(path);
|
|
478
|
+
}
|
|
479
|
+
async readText(path) {
|
|
480
|
+
const bunFile = this.bunFile();
|
|
481
|
+
if (bunFile)
|
|
482
|
+
return bunFile(path).text();
|
|
483
|
+
return readFile(path, "utf8");
|
|
484
|
+
}
|
|
485
|
+
bunFile() {
|
|
486
|
+
const candidate = globalThis.Bun?.file;
|
|
487
|
+
return typeof candidate === "function" ? candidate : undefined;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
429
491
|
class ToolLoader {
|
|
430
492
|
discovery;
|
|
431
493
|
validator;
|
|
494
|
+
envLoader = new ToolEnvLoader;
|
|
432
495
|
constructor(options = {}) {
|
|
433
496
|
this.discovery = new ToolDiscoveryService(options);
|
|
434
497
|
this.validator = new ToolDefinitionValidator;
|
|
@@ -453,7 +516,8 @@ class ToolLoader {
|
|
|
453
516
|
const moduleRecord = moduleValue;
|
|
454
517
|
const definitionValue = await this.evaluateModuleDefault(moduleRecord.default, tool.name);
|
|
455
518
|
const definition = this.validator.validateToolDefinition(definitionValue, tool.name);
|
|
456
|
-
|
|
519
|
+
const env = await this.envLoader.load(tool, definition);
|
|
520
|
+
return { name: definition.name, path: tool.toolPath, env, definition };
|
|
457
521
|
}
|
|
458
522
|
async evaluateModuleDefault(value, toolName) {
|
|
459
523
|
const awaitedValue = await Promise.resolve(value);
|
|
@@ -475,15 +539,15 @@ class ToolLoader {
|
|
|
475
539
|
async loadCommand(toolName, commandName) {
|
|
476
540
|
const tool = await this.load(toolName);
|
|
477
541
|
this.validator.validateCommandName(commandName);
|
|
478
|
-
const
|
|
479
|
-
if (!
|
|
542
|
+
const command = tool.definition.commands[commandName];
|
|
543
|
+
if (!command) {
|
|
480
544
|
throw new RigError("COMMAND_NOT_FOUND", `Command not found: ${CommandIds.from(toolName, commandName)}`, {
|
|
481
545
|
tool: toolName,
|
|
482
546
|
command: commandName,
|
|
483
547
|
available: Object.keys(tool.definition.commands)
|
|
484
548
|
});
|
|
485
549
|
}
|
|
486
|
-
return { tool, commandName, command
|
|
550
|
+
return { tool, commandName, command };
|
|
487
551
|
}
|
|
488
552
|
}
|
|
489
553
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RigConfigStore
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-jbscjyqq.js";
|
|
4
4
|
import {
|
|
5
5
|
RigError
|
|
6
6
|
} from "./cli-1c7te5cg.js";
|
|
7
7
|
import {
|
|
8
8
|
RigPaths
|
|
9
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-vkw9rcke.js";
|
|
10
10
|
|
|
11
11
|
// src/registry/discover.ts
|
|
12
12
|
import { existsSync, statSync } from "node:fs";
|