@basaltbytes/odoo-agentic-dev 0.1.0-beta.1
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 +464 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +73 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/compose.d.ts +19 -0
- package/dist/commands/compose.js +29 -0
- package/dist/commands/compose.js.map +1 -0
- package/dist/commands/doctor.d.ts +35 -0
- package/dist/commands/doctor.js +209 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/down.d.ts +23 -0
- package/dist/commands/down.js +46 -0
- package/dist/commands/down.js.map +1 -0
- package/dist/commands/info.d.ts +10 -0
- package/dist/commands/info.js +42 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/json-report.d.ts +45 -0
- package/dist/commands/json-report.js +55 -0
- package/dist/commands/json-report.js.map +1 -0
- package/dist/commands/link-source.d.ts +33 -0
- package/dist/commands/link-source.js +116 -0
- package/dist/commands/link-source.js.map +1 -0
- package/dist/commands/list.d.ts +29 -0
- package/dist/commands/list.js +117 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/logs.d.ts +17 -0
- package/dist/commands/logs.js +28 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/prune.d.ts +54 -0
- package/dist/commands/prune.js +118 -0
- package/dist/commands/prune.js.map +1 -0
- package/dist/commands/psql.d.ts +8 -0
- package/dist/commands/psql.js +24 -0
- package/dist/commands/psql.js.map +1 -0
- package/dist/commands/reset-db.d.ts +34 -0
- package/dist/commands/reset-db.js +86 -0
- package/dist/commands/reset-db.js.map +1 -0
- package/dist/commands/resolve-context.d.ts +17 -0
- package/dist/commands/resolve-context.js +30 -0
- package/dist/commands/resolve-context.js.map +1 -0
- package/dist/commands/run.d.ts +31 -0
- package/dist/commands/run.js +83 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/setup.d.ts +46 -0
- package/dist/commands/setup.js +93 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/shell.d.ts +20 -0
- package/dist/commands/shell.js +46 -0
- package/dist/commands/shell.js.map +1 -0
- package/dist/commands/state-hooks.d.ts +28 -0
- package/dist/commands/state-hooks.js +86 -0
- package/dist/commands/state-hooks.js.map +1 -0
- package/dist/commands/test.d.ts +24 -0
- package/dist/commands/test.js +70 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/trailing-args.d.ts +10 -0
- package/dist/commands/trailing-args.js +14 -0
- package/dist/commands/trailing-args.js.map +1 -0
- package/dist/commands/up.d.ts +23 -0
- package/dist/commands/up.js +62 -0
- package/dist/commands/up.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.js +31 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/worktree.d.ts +97 -0
- package/dist/commands/worktree.js +355 -0
- package/dist/commands/worktree.js.map +1 -0
- package/dist/config/load-recipe.d.ts +14 -0
- package/dist/config/load-recipe.js +75 -0
- package/dist/config/load-recipe.js.map +1 -0
- package/dist/config/schema.d.ts +9 -0
- package/dist/config/schema.js +190 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/command-plan.d.ts +34 -0
- package/dist/core/command-plan.js +112 -0
- package/dist/core/command-plan.js.map +1 -0
- package/dist/core/compose-model.d.ts +18 -0
- package/dist/core/compose-model.js +80 -0
- package/dist/core/compose-model.js.map +1 -0
- package/dist/core/compose-project.d.ts +3 -0
- package/dist/core/compose-project.js +15 -0
- package/dist/core/compose-project.js.map +1 -0
- package/dist/core/database-name.d.ts +15 -0
- package/dist/core/database-name.js +59 -0
- package/dist/core/database-name.js.map +1 -0
- package/dist/core/environment.d.ts +84 -0
- package/dist/core/environment.js +78 -0
- package/dist/core/environment.js.map +1 -0
- package/dist/core/port-allocator.d.ts +30 -0
- package/dist/core/port-allocator.js +58 -0
- package/dist/core/port-allocator.js.map +1 -0
- package/dist/core/project-recipe.d.ts +163 -0
- package/dist/core/project-recipe.js +13 -0
- package/dist/core/project-recipe.js.map +1 -0
- package/dist/core/safety.d.ts +11 -0
- package/dist/core/safety.js +16 -0
- package/dist/core/safety.js.map +1 -0
- package/dist/core/worktree-context.d.ts +32 -0
- package/dist/core/worktree-context.js +94 -0
- package/dist/core/worktree-context.js.map +1 -0
- package/dist/errors/errors.d.ts +124 -0
- package/dist/errors/errors.js +129 -0
- package/dist/errors/errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/platform/command-runner.d.ts +30 -0
- package/dist/platform/command-runner.js +65 -0
- package/dist/platform/command-runner.js.map +1 -0
- package/dist/platform/docker-compose.d.ts +69 -0
- package/dist/platform/docker-compose.js +239 -0
- package/dist/platform/docker-compose.js.map +1 -0
- package/dist/platform/git.d.ts +10 -0
- package/dist/platform/git.js +35 -0
- package/dist/platform/git.js.map +1 -0
- package/dist/platform/odoo-lifecycle.d.ts +30 -0
- package/dist/platform/odoo-lifecycle.js +109 -0
- package/dist/platform/odoo-lifecycle.js.map +1 -0
- package/dist/platform/port-probe.d.ts +7 -0
- package/dist/platform/port-probe.js +13 -0
- package/dist/platform/port-probe.js.map +1 -0
- package/dist/platform/process-supervisor.d.ts +16 -0
- package/dist/platform/process-supervisor.js +23 -0
- package/dist/platform/process-supervisor.js.map +1 -0
- package/dist/platform/state-store.d.ts +37 -0
- package/dist/platform/state-store.js +122 -0
- package/dist/platform/state-store.js.map +1 -0
- package/dist/suppress-sqlite-warning.d.ts +1 -0
- package/dist/suppress-sqlite-warning.js +18 -0
- package/dist/suppress-sqlite-warning.js.map +1 -0
- package/dist/testing/fake-adapters.d.ts +34 -0
- package/dist/testing/fake-adapters.js +90 -0
- package/dist/testing/fake-adapters.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
import { Context, Duration, Effect, Layer } from "effect";
|
|
4
|
+
import { ComposeCommandError, DockerUnavailableError, tail } from "../errors/errors.js";
|
|
5
|
+
import { buildComposeModel, GENERATED_COMPOSE_RELATIVE_PATH, renderComposeYaml, } from "../core/compose-model.js";
|
|
6
|
+
import { CommandRunner } from "./command-runner.js";
|
|
7
|
+
export const composeArgs = (ref, rest) => [
|
|
8
|
+
"compose",
|
|
9
|
+
"-p",
|
|
10
|
+
ref.projectName,
|
|
11
|
+
"-f",
|
|
12
|
+
ref.composeFile,
|
|
13
|
+
"--project-directory",
|
|
14
|
+
ref.projectDir,
|
|
15
|
+
...rest,
|
|
16
|
+
];
|
|
17
|
+
export const DockerCompose = Context.Service("odoo-agentic-dev/DockerCompose");
|
|
18
|
+
/**
|
|
19
|
+
* Lenient parse of `docker compose ls -a --format json`: an array of
|
|
20
|
+
* `{ Name, Status, ConfigFiles }` where Status reads like "running(2)" or
|
|
21
|
+
* "exited(2)". Anything unrecognized is skipped rather than fatal.
|
|
22
|
+
*/
|
|
23
|
+
export const parseComposeLs = (stdout) => {
|
|
24
|
+
const text = stdout.trim();
|
|
25
|
+
if (text.length === 0)
|
|
26
|
+
return [];
|
|
27
|
+
const parsed = JSON.parse(text);
|
|
28
|
+
if (!Array.isArray(parsed))
|
|
29
|
+
return [];
|
|
30
|
+
const projects = [];
|
|
31
|
+
for (const item of parsed) {
|
|
32
|
+
if (typeof item !== "object" || item === null)
|
|
33
|
+
continue;
|
|
34
|
+
const record = item;
|
|
35
|
+
if (typeof record["Name"] !== "string" || record["Name"].length === 0)
|
|
36
|
+
continue;
|
|
37
|
+
projects.push({
|
|
38
|
+
name: record["Name"],
|
|
39
|
+
running: String(record["Status"] ?? "").startsWith("running"),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return projects;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Lenient parse of `docker ps --format json` output (NDJSON, one container per
|
|
46
|
+
* line; a top-level array is tolerated). `Labels` is a comma-joined `k=v`
|
|
47
|
+
* string, so label values containing commas are truncated — acceptable for the
|
|
48
|
+
* best-effort adoption this feeds. Unparseable lines are skipped.
|
|
49
|
+
*/
|
|
50
|
+
export const parseLabeledPs = (stdout) => {
|
|
51
|
+
const containers = new Map();
|
|
52
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
53
|
+
const text = line.trim();
|
|
54
|
+
if (text.length === 0)
|
|
55
|
+
continue;
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = JSON.parse(text);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
for (const item of Array.isArray(parsed) ? parsed : [parsed]) {
|
|
64
|
+
if (typeof item !== "object" || item === null)
|
|
65
|
+
continue;
|
|
66
|
+
const labelsRaw = item["Labels"];
|
|
67
|
+
if (typeof labelsRaw !== "string")
|
|
68
|
+
continue;
|
|
69
|
+
const labels = new Map();
|
|
70
|
+
for (const part of labelsRaw.split(",")) {
|
|
71
|
+
const eq = part.indexOf("=");
|
|
72
|
+
if (eq > 0)
|
|
73
|
+
labels.set(part.slice(0, eq), part.slice(eq + 1));
|
|
74
|
+
}
|
|
75
|
+
const composeProject = labels.get("com.docker.compose.project");
|
|
76
|
+
if (composeProject === undefined || containers.has(composeProject))
|
|
77
|
+
continue;
|
|
78
|
+
containers.set(composeProject, {
|
|
79
|
+
composeProject,
|
|
80
|
+
projectId: labels.get("dev.basaltbytes.oad.project-id") ?? null,
|
|
81
|
+
database: labels.get("dev.basaltbytes.oad.database") ?? null,
|
|
82
|
+
rootDir: labels.get("dev.basaltbytes.oad.root-dir") ?? null,
|
|
83
|
+
branch: labels.get("dev.basaltbytes.oad.branch") ?? null,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return [...containers.values()];
|
|
88
|
+
};
|
|
89
|
+
const splitLines = (stdout) => stdout
|
|
90
|
+
.split(/\r?\n/)
|
|
91
|
+
.map((line) => line.trim())
|
|
92
|
+
.filter((line) => line.length > 0);
|
|
93
|
+
export const DockerComposeLive = Layer.effect(DockerCompose, Effect.gen(function* () {
|
|
94
|
+
const runner = yield* CommandRunner;
|
|
95
|
+
const toComposeError = (args) => (cause) => new ComposeCommandError({
|
|
96
|
+
args,
|
|
97
|
+
exitCode: -1,
|
|
98
|
+
stderrTail: cause.stderrTail ?? String(cause),
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* Single captured execution path: the caller hands over the fully expanded
|
|
102
|
+
* argv exactly once, and that same argv is shared by the runner spec and
|
|
103
|
+
* the error mapping (and, in `run`, by `failOnNonZero`). Never re-expand
|
|
104
|
+
* `composeArgs` for the same invocation.
|
|
105
|
+
*/
|
|
106
|
+
const exec = (ref, argv, stdin) => runner
|
|
107
|
+
.run({ command: "docker", args: argv, cwd: ref.projectDir, env: ref.env, stdin })
|
|
108
|
+
.pipe(Effect.mapError(toComposeError(argv)));
|
|
109
|
+
const tryRun = (ref, args) => exec(ref, composeArgs(ref, args));
|
|
110
|
+
/** Spec rule: never dump huge logs inline — full output goes to .odoo-agentic-dev/logs/, the error keeps a tail + the path. */
|
|
111
|
+
const writeFailureLog = (ref, argv, result) => {
|
|
112
|
+
try {
|
|
113
|
+
const dir = join(ref.projectDir, ".odoo-agentic-dev", "logs");
|
|
114
|
+
mkdirSync(dir, { recursive: true });
|
|
115
|
+
const file = join(dir, `compose-${Date.now()}.log`);
|
|
116
|
+
writeFileSync(file, `$ docker ${argv.join(" ")}\nexit ${result.exitCode}\n\n--- stdout ---\n${result.stdout}\n--- stderr ---\n${result.stderr}\n`);
|
|
117
|
+
return file;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const failOnNonZero = (ref, argv) => (result) => {
|
|
124
|
+
if (result.exitCode === 0)
|
|
125
|
+
return Effect.succeed(result);
|
|
126
|
+
const logFile = writeFailureLog(ref, argv, result);
|
|
127
|
+
return Effect.fail(new ComposeCommandError({
|
|
128
|
+
args: argv,
|
|
129
|
+
exitCode: result.exitCode,
|
|
130
|
+
stderrTail: tail(result.stderr || result.stdout) +
|
|
131
|
+
(logFile !== undefined ? `\nfull log: ${logFile}` : ""),
|
|
132
|
+
}));
|
|
133
|
+
};
|
|
134
|
+
const run = (ref, args, stdin) => {
|
|
135
|
+
const argv = composeArgs(ref, args);
|
|
136
|
+
return exec(ref, argv, stdin).pipe(Effect.flatMap(failOnNonZero(ref, argv)));
|
|
137
|
+
};
|
|
138
|
+
/** Top-level `docker <argv>` (no compose ref): captured, fails typed on non-zero. */
|
|
139
|
+
const dockerRun = (argv) => runner.run({ command: "docker", args: argv }).pipe(Effect.mapError(toComposeError(argv)), Effect.flatMap((result) => result.exitCode === 0
|
|
140
|
+
? Effect.succeed(result)
|
|
141
|
+
: Effect.fail(new ComposeCommandError({
|
|
142
|
+
args: argv,
|
|
143
|
+
exitCode: result.exitCode,
|
|
144
|
+
stderrTail: tail(result.stderr || result.stdout),
|
|
145
|
+
}))));
|
|
146
|
+
const parseWith = (argv, parse) => (result) => Effect.try({
|
|
147
|
+
try: () => parse(result.stdout),
|
|
148
|
+
catch: (cause) => new ComposeCommandError({
|
|
149
|
+
args: argv,
|
|
150
|
+
exitCode: 0,
|
|
151
|
+
stderrTail: `unparseable docker output: ${String(cause)}`,
|
|
152
|
+
}),
|
|
153
|
+
});
|
|
154
|
+
const listProjects = () => {
|
|
155
|
+
const argv = ["compose", "ls", "-a", "--format", "json"];
|
|
156
|
+
return dockerRun(argv).pipe(Effect.flatMap(parseWith(argv, parseComposeLs)));
|
|
157
|
+
};
|
|
158
|
+
const listLabeledContainers = () => {
|
|
159
|
+
const argv = ["ps", "-a", "--filter", "label=dev.basaltbytes.oad=1", "--format", "json"];
|
|
160
|
+
return dockerRun(argv).pipe(Effect.flatMap(parseWith(argv, parseLabeledPs)));
|
|
161
|
+
};
|
|
162
|
+
const removeByLabel = (composeProject) => Effect.gen(function* () {
|
|
163
|
+
const filter = `label=com.docker.compose.project=${composeProject}`;
|
|
164
|
+
const ids = splitLines((yield* dockerRun(["ps", "-aq", "--filter", filter])).stdout);
|
|
165
|
+
if (ids.length > 0)
|
|
166
|
+
yield* dockerRun(["rm", "-f", ...ids]);
|
|
167
|
+
const volumes = splitLines((yield* dockerRun(["volume", "ls", "-q", "--filter", filter])).stdout);
|
|
168
|
+
if (volumes.length > 0)
|
|
169
|
+
yield* dockerRun(["volume", "rm", ...volumes]);
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
ensureAvailable: () => runner.run({ command: "docker", args: ["version", "--format", "json"] }).pipe(Effect.mapError((e) => new DockerUnavailableError({ reason: e.stderrTail || "docker CLI not found" })), Effect.flatMap((result) => result.exitCode === 0
|
|
173
|
+
? Effect.void
|
|
174
|
+
: Effect.fail(new DockerUnavailableError({
|
|
175
|
+
reason: tail(result.stderr) || `docker exited ${result.exitCode}`,
|
|
176
|
+
})))),
|
|
177
|
+
prepareComposeFile: (recipe, ctx) => Effect.gen(function* () {
|
|
178
|
+
if (recipe.compose.file !== null) {
|
|
179
|
+
const file = isAbsolute(recipe.compose.file)
|
|
180
|
+
? recipe.compose.file
|
|
181
|
+
: resolve(ctx.rootDir, recipe.compose.file);
|
|
182
|
+
return {
|
|
183
|
+
projectName: ctx.composeProjectName,
|
|
184
|
+
composeFile: file,
|
|
185
|
+
projectDir: ctx.rootDir,
|
|
186
|
+
env: ctx.env,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const file = join(ctx.rootDir, GENERATED_COMPOSE_RELATIVE_PATH);
|
|
190
|
+
yield* Effect.try({
|
|
191
|
+
try: () => {
|
|
192
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
193
|
+
writeFileSync(file, renderComposeYaml(buildComposeModel(recipe, ctx)));
|
|
194
|
+
},
|
|
195
|
+
catch: (cause) => new ComposeCommandError({
|
|
196
|
+
args: ["<prepare>"],
|
|
197
|
+
exitCode: -1,
|
|
198
|
+
stderrTail: String(cause),
|
|
199
|
+
}),
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
projectName: ctx.composeProjectName,
|
|
203
|
+
composeFile: file,
|
|
204
|
+
projectDir: ctx.rootDir,
|
|
205
|
+
env: ctx.env,
|
|
206
|
+
};
|
|
207
|
+
}),
|
|
208
|
+
listProjects,
|
|
209
|
+
listLabeledContainers,
|
|
210
|
+
removeByLabel,
|
|
211
|
+
run,
|
|
212
|
+
tryRun,
|
|
213
|
+
stream: (ref, args) => {
|
|
214
|
+
const argv = composeArgs(ref, args);
|
|
215
|
+
return runner
|
|
216
|
+
.runInherited({ command: "docker", args: argv, cwd: ref.projectDir, env: ref.env })
|
|
217
|
+
.pipe(Effect.mapError(toComposeError(argv)), Effect.flatMap((code) => code === 0
|
|
218
|
+
? Effect.void
|
|
219
|
+
: Effect.fail(new ComposeCommandError({ args: argv, exitCode: code, stderrTail: "" }))));
|
|
220
|
+
},
|
|
221
|
+
waitForDb: (ref, dbService, options) => {
|
|
222
|
+
const interval = options?.intervalMillis ?? 1000;
|
|
223
|
+
const maxAttempts = options?.maxAttempts ?? 60;
|
|
224
|
+
const args = ["exec", "-T", dbService, "pg_isready", "-U", "odoo", "-d", "postgres"];
|
|
225
|
+
const argv = composeArgs(ref, args);
|
|
226
|
+
const attempt = (n) => exec(ref, argv).pipe(Effect.flatMap((result) => result.exitCode === 0
|
|
227
|
+
? Effect.void
|
|
228
|
+
: n >= maxAttempts
|
|
229
|
+
? Effect.fail(new ComposeCommandError({
|
|
230
|
+
args: argv,
|
|
231
|
+
exitCode: result.exitCode,
|
|
232
|
+
stderrTail: `database not ready after ${maxAttempts} attempts`,
|
|
233
|
+
}))
|
|
234
|
+
: Effect.sleep(Duration.millis(interval)).pipe(Effect.flatMap(() => attempt(n + 1)))));
|
|
235
|
+
return attempt(1);
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}));
|
|
239
|
+
//# sourceMappingURL=docker-compose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-compose.js","sourceRoot":"","sources":["../../src/platform/docker-compose.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { GitError } from "../errors/errors.js";
|
|
3
|
+
import type { GitState } from "../core/worktree-context.js";
|
|
4
|
+
export interface GitApi {
|
|
5
|
+
readonly state: (rootDir: string) => Effect.Effect<GitState, GitError>;
|
|
6
|
+
/** Does `refs/heads/<branch>` exist in the repo at rootDir? exit 0 → true, 1 → false. */
|
|
7
|
+
readonly branchExists: (rootDir: string, branch: string) => Effect.Effect<boolean, GitError>;
|
|
8
|
+
}
|
|
9
|
+
export declare const Git: Context.Service<GitApi, GitApi>;
|
|
10
|
+
export declare const GitLive: Layer.Layer<GitApi, never, import("./command-runner.js").CommandRunnerApi>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { GitError } from "../errors/errors.js";
|
|
3
|
+
import { CommandRunner } from "./command-runner.js";
|
|
4
|
+
export const Git = Context.Service("odoo-agentic-dev/Git");
|
|
5
|
+
export const GitLive = Layer.effect(Git, Effect.gen(function* () {
|
|
6
|
+
const runner = yield* CommandRunner;
|
|
7
|
+
return {
|
|
8
|
+
state: (rootDir) => runner
|
|
9
|
+
.run({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"], cwd: rootDir })
|
|
10
|
+
.pipe(Effect.mapError((e) => new GitError({ reason: e.stderrTail || String(e) })), Effect.flatMap((result) => {
|
|
11
|
+
if (result.exitCode === 0) {
|
|
12
|
+
const branch = result.stdout.trim();
|
|
13
|
+
return Effect.succeed(branch === "HEAD" ? { _tag: "Detached" } : { _tag: "Branch", branch });
|
|
14
|
+
}
|
|
15
|
+
if (result.stderr.includes("not a git repository")) {
|
|
16
|
+
return Effect.succeed({ _tag: "NotARepo" });
|
|
17
|
+
}
|
|
18
|
+
return Effect.fail(new GitError({ reason: result.stderr.trim() || `git exited ${result.exitCode}` }));
|
|
19
|
+
})),
|
|
20
|
+
branchExists: (rootDir, branch) => runner
|
|
21
|
+
.run({
|
|
22
|
+
command: "git",
|
|
23
|
+
args: ["-C", rootDir, "rev-parse", "--verify", "--quiet", `refs/heads/${branch}`],
|
|
24
|
+
})
|
|
25
|
+
.pipe(Effect.mapError((e) => new GitError({ reason: e.stderrTail || String(e) })), Effect.flatMap((result) => {
|
|
26
|
+
if (result.exitCode === 0)
|
|
27
|
+
return Effect.succeed(true);
|
|
28
|
+
// --quiet: a missing ref exits 1 with no output — that is the "false" case
|
|
29
|
+
if (result.exitCode === 1)
|
|
30
|
+
return Effect.succeed(false);
|
|
31
|
+
return Effect.fail(new GitError({ reason: result.stderr.trim() || `git exited ${result.exitCode}` }));
|
|
32
|
+
})),
|
|
33
|
+
};
|
|
34
|
+
}));
|
|
35
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/platform/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import type { RuntimeError } from "../errors/errors.js";
|
|
3
|
+
import type { OdooAgenticDevConfig } from "../core/project-recipe.js";
|
|
4
|
+
import type { WorktreeContext } from "../core/worktree-context.js";
|
|
5
|
+
import type { OdooTestOptions } from "../core/command-plan.js";
|
|
6
|
+
export interface OdooLifecycleApi {
|
|
7
|
+
readonly resetDatabase: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, options: {
|
|
8
|
+
readonly modules?: ReadonlyArray<string> | undefined;
|
|
9
|
+
readonly withoutDemo?: string | false | undefined;
|
|
10
|
+
}) => Effect.Effect<void, RuntimeError>;
|
|
11
|
+
readonly runPostInitHooks: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext) => Effect.Effect<void, RuntimeError>;
|
|
12
|
+
readonly updateModules: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, modules: ReadonlyArray<string>, options: {
|
|
13
|
+
readonly restart: boolean;
|
|
14
|
+
}) => Effect.Effect<void, RuntimeError>;
|
|
15
|
+
/**
|
|
16
|
+
* Snapshot `<db>` into `<db>__tpl` (database + filestore). Docker-only:
|
|
17
|
+
* recording the template in the state registry is the caller's concern.
|
|
18
|
+
*/
|
|
19
|
+
readonly snapshotTemplate: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext) => Effect.Effect<void, RuntimeError>;
|
|
20
|
+
/** Recreate `<db>` from `<db>__tpl` (database + filestore). Hooks are baked in. */
|
|
21
|
+
readonly restoreFromTemplate: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext) => Effect.Effect<void, RuntimeError>;
|
|
22
|
+
/** Runs the test suite; reporting the tails is the caller's concern. */
|
|
23
|
+
readonly runTests: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, options: OdooTestOptions) => Effect.Effect<{
|
|
24
|
+
readonly exitCode: number;
|
|
25
|
+
readonly stdoutTail: string;
|
|
26
|
+
readonly stderrTail: string;
|
|
27
|
+
}, RuntimeError>;
|
|
28
|
+
}
|
|
29
|
+
export declare const OdooLifecycle: Context.Service<OdooLifecycleApi, OdooLifecycleApi>;
|
|
30
|
+
export declare const OdooLifecycleLive: Layer.Layer<OdooLifecycleApi, never, import("./command-runner.js").CommandRunnerApi | import("./docker-compose.js").DockerComposeApi>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
|
+
import { Context, Effect, Layer } from "effect";
|
|
4
|
+
import { ConfigLoadError, OdooCommandError, tail } from "../errors/errors.js";
|
|
5
|
+
import { copyFilestoreArgs, createDatabaseSql, createFromTemplateSql, dropDatabaseSql, expandHook, odooInitArgs, odooShellArgs, odooTestArgs, odooUpdateArgs, psqlArgs, removeFilestoreArgs, terminateSessionsSql, } from "../core/command-plan.js";
|
|
6
|
+
import { templateDbName } from "../core/environment.js";
|
|
7
|
+
import { DockerCompose } from "./docker-compose.js";
|
|
8
|
+
import { CommandRunner, runInheritedOrFail } from "./command-runner.js";
|
|
9
|
+
const toOdooError = (e) => new OdooCommandError({ args: e.args, exitCode: e.exitCode, stderrTail: e.stderrTail });
|
|
10
|
+
export const OdooLifecycle = Context.Service("odoo-agentic-dev/OdooLifecycle");
|
|
11
|
+
export const OdooLifecycleLive = Layer.effect(OdooLifecycle, Effect.gen(function* () {
|
|
12
|
+
const compose = yield* DockerCompose;
|
|
13
|
+
const runner = yield* CommandRunner;
|
|
14
|
+
const ensureDbReady = (recipe, ref) => compose
|
|
15
|
+
.stream(ref, ["up", "-d", recipe.odoo.databaseServiceName])
|
|
16
|
+
.pipe(Effect.andThen(compose.waitForDb(ref, recipe.odoo.databaseServiceName)));
|
|
17
|
+
const runShellCode = (recipe, ctx, ref, code) => compose.run(ref, odooShellArgs(recipe.odoo.serviceName, ctx.databaseName), code);
|
|
18
|
+
return {
|
|
19
|
+
resetDatabase: (recipe, ctx, options) => Effect.gen(function* () {
|
|
20
|
+
const ref = yield* compose.prepareComposeFile(recipe, ctx);
|
|
21
|
+
yield* ensureDbReady(recipe, ref);
|
|
22
|
+
const db = recipe.odoo.databaseServiceName;
|
|
23
|
+
yield* compose.run(ref, psqlArgs(db, terminateSessionsSql(ctx.databaseName)));
|
|
24
|
+
yield* compose.run(ref, psqlArgs(db, dropDatabaseSql(ctx.databaseName)));
|
|
25
|
+
yield* compose.run(ref, psqlArgs(db, createDatabaseSql(ctx.databaseName)));
|
|
26
|
+
yield* compose.run(ref, removeFilestoreArgs(recipe.odoo.serviceName, ctx.databaseName));
|
|
27
|
+
const initArgs = odooInitArgs(recipe.odoo.serviceName, ctx.databaseName, options.modules ?? recipe.database.initialModules, options.withoutDemo ?? recipe.database.withoutDemo);
|
|
28
|
+
yield* compose.stream(ref, initArgs).pipe(Effect.mapError(toOdooError));
|
|
29
|
+
}),
|
|
30
|
+
runPostInitHooks: (recipe, ctx) => Effect.gen(function* () {
|
|
31
|
+
if (recipe.database.postInit.length === 0)
|
|
32
|
+
return;
|
|
33
|
+
const ref = yield* compose.prepareComposeFile(recipe, ctx);
|
|
34
|
+
for (const hook of recipe.database.postInit) {
|
|
35
|
+
const expanded = expandHook(hook, ctx.env);
|
|
36
|
+
switch (expanded.kind) {
|
|
37
|
+
case "odoo-shell": {
|
|
38
|
+
yield* runShellCode(recipe, ctx, ref, expanded.code);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "odoo-shell-file": {
|
|
42
|
+
const path = isAbsolute(expanded.file)
|
|
43
|
+
? expanded.file
|
|
44
|
+
: resolve(ctx.rootDir, expanded.file);
|
|
45
|
+
const code = yield* Effect.try({
|
|
46
|
+
try: () => readFileSync(path, "utf8"),
|
|
47
|
+
catch: (cause) => new ConfigLoadError({ path, reason: String(cause) }),
|
|
48
|
+
});
|
|
49
|
+
yield* runShellCode(recipe, ctx, ref, code);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case "host-command": {
|
|
53
|
+
const cwd = expanded.cwd === undefined ? ctx.rootDir : resolve(ctx.rootDir, expanded.cwd);
|
|
54
|
+
yield* runInheritedOrFail(runner, {
|
|
55
|
+
command: expanded.command,
|
|
56
|
+
args: expanded.args,
|
|
57
|
+
cwd,
|
|
58
|
+
env: ctx.env,
|
|
59
|
+
prefix: `[hook:${expanded.command}] `,
|
|
60
|
+
});
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}),
|
|
66
|
+
snapshotTemplate: (recipe, ctx) => Effect.gen(function* () {
|
|
67
|
+
const ref = yield* compose.prepareComposeFile(recipe, ctx);
|
|
68
|
+
yield* ensureDbReady(recipe, ref);
|
|
69
|
+
const db = recipe.odoo.databaseServiceName;
|
|
70
|
+
const tpl = templateDbName(ctx.databaseName);
|
|
71
|
+
yield* compose.run(ref, psqlArgs(db, terminateSessionsSql(ctx.databaseName)));
|
|
72
|
+
yield* compose.run(ref, psqlArgs(db, dropDatabaseSql(tpl)));
|
|
73
|
+
yield* compose.run(ref, psqlArgs(db, createFromTemplateSql(tpl, ctx.databaseName)));
|
|
74
|
+
yield* compose.run(ref, copyFilestoreArgs(recipe.odoo.serviceName, ctx.databaseName, tpl));
|
|
75
|
+
}),
|
|
76
|
+
restoreFromTemplate: (recipe, ctx) => Effect.gen(function* () {
|
|
77
|
+
const ref = yield* compose.prepareComposeFile(recipe, ctx);
|
|
78
|
+
yield* ensureDbReady(recipe, ref);
|
|
79
|
+
const db = recipe.odoo.databaseServiceName;
|
|
80
|
+
const tpl = templateDbName(ctx.databaseName);
|
|
81
|
+
yield* compose.run(ref, psqlArgs(db, terminateSessionsSql(ctx.databaseName)));
|
|
82
|
+
yield* compose.run(ref, psqlArgs(db, dropDatabaseSql(ctx.databaseName)));
|
|
83
|
+
yield* compose.run(ref, psqlArgs(db, createFromTemplateSql(ctx.databaseName, tpl)));
|
|
84
|
+
yield* compose.run(ref, copyFilestoreArgs(recipe.odoo.serviceName, tpl, ctx.databaseName));
|
|
85
|
+
}),
|
|
86
|
+
updateModules: (recipe, ctx, modules, options) => Effect.gen(function* () {
|
|
87
|
+
const ref = yield* compose.prepareComposeFile(recipe, ctx);
|
|
88
|
+
yield* ensureDbReady(recipe, ref);
|
|
89
|
+
yield* compose.stream(ref, ["stop", recipe.odoo.serviceName]);
|
|
90
|
+
yield* compose
|
|
91
|
+
.stream(ref, odooUpdateArgs(recipe.odoo.serviceName, ctx.databaseName, modules))
|
|
92
|
+
.pipe(Effect.mapError(toOdooError));
|
|
93
|
+
if (options.restart) {
|
|
94
|
+
yield* compose.stream(ref, ["up", "-d", recipe.odoo.serviceName]);
|
|
95
|
+
}
|
|
96
|
+
}),
|
|
97
|
+
runTests: (recipe, ctx, options) => Effect.gen(function* () {
|
|
98
|
+
const ref = yield* compose.prepareComposeFile(recipe, ctx);
|
|
99
|
+
yield* ensureDbReady(recipe, ref);
|
|
100
|
+
const result = yield* compose.tryRun(ref, odooTestArgs(recipe.odoo.serviceName, ctx.databaseName, options));
|
|
101
|
+
return {
|
|
102
|
+
exitCode: result.exitCode,
|
|
103
|
+
stdoutTail: tail(result.stdout, 200),
|
|
104
|
+
stderrTail: tail(result.stderr, 200),
|
|
105
|
+
};
|
|
106
|
+
}),
|
|
107
|
+
};
|
|
108
|
+
}));
|
|
109
|
+
//# sourceMappingURL=odoo-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"odoo-lifecycle.js","sourceRoot":"","sources":["../../src/platform/odoo-lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
export interface PortProbeApi {
|
|
3
|
+
/** Bind test on 127.0.0.1. Never fails: any bind error means "not free". */
|
|
4
|
+
readonly isFree: (port: number) => Effect.Effect<boolean>;
|
|
5
|
+
}
|
|
6
|
+
export declare const PortProbe: Context.Service<PortProbeApi, PortProbeApi>;
|
|
7
|
+
export declare const PortProbeLive: Layer.Layer<PortProbeApi, never, never>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createServer } from "node:net";
|
|
2
|
+
import { Context, Effect, Layer } from "effect";
|
|
3
|
+
export const PortProbe = Context.Service("odoo-agentic-dev/PortProbe");
|
|
4
|
+
export const PortProbeLive = Layer.succeed(PortProbe, {
|
|
5
|
+
isFree: (port) => Effect.callback((resume) => {
|
|
6
|
+
const server = createServer();
|
|
7
|
+
server.once("error", () => resume(Effect.succeed(false)));
|
|
8
|
+
server.listen({ port, host: "127.0.0.1", exclusive: true }, () => {
|
|
9
|
+
server.close(() => resume(Effect.succeed(true)));
|
|
10
|
+
});
|
|
11
|
+
}),
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=port-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-probe.js","sourceRoot":"","sources":["../../src/platform/port-probe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAOhD,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAe,4BAA4B,CAAC,CAAC;AAErF,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE;IACpD,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CACf,MAAM,CAAC,QAAQ,CAAU,CAAC,MAAM,EAAE,EAAE;QAClC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE;YAC/D,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;CACL,CAAC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { CompanionProcessError } from "../errors/errors.js";
|
|
3
|
+
import type { CommandFailedError } from "../errors/errors.js";
|
|
4
|
+
export type CompanionSpec = {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
readonly command: string;
|
|
8
|
+
readonly args: ReadonlyArray<string>;
|
|
9
|
+
readonly env: Record<string, string>;
|
|
10
|
+
};
|
|
11
|
+
export interface ProcessSupervisorApi {
|
|
12
|
+
/** Run all companions concurrently; first failure interrupts the rest. */
|
|
13
|
+
readonly runAll: (specs: ReadonlyArray<CompanionSpec>) => Effect.Effect<void, CompanionProcessError | CommandFailedError>;
|
|
14
|
+
}
|
|
15
|
+
export declare const ProcessSupervisor: Context.Service<ProcessSupervisorApi, ProcessSupervisorApi>;
|
|
16
|
+
export declare const ProcessSupervisorLive: Layer.Layer<ProcessSupervisorApi, never, import("./command-runner.js").CommandRunnerApi>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { CompanionProcessError } from "../errors/errors.js";
|
|
3
|
+
import { CommandRunner } from "./command-runner.js";
|
|
4
|
+
export const ProcessSupervisor = Context.Service("odoo-agentic-dev/ProcessSupervisor");
|
|
5
|
+
export const ProcessSupervisorLive = Layer.effect(ProcessSupervisor, Effect.gen(function* () {
|
|
6
|
+
const runner = yield* CommandRunner;
|
|
7
|
+
return {
|
|
8
|
+
runAll: (specs) => specs.length === 0
|
|
9
|
+
? Effect.void
|
|
10
|
+
: Effect.all(specs.map((spec) => runner
|
|
11
|
+
.runInherited({
|
|
12
|
+
command: spec.command,
|
|
13
|
+
args: spec.args,
|
|
14
|
+
cwd: spec.cwd,
|
|
15
|
+
env: spec.env,
|
|
16
|
+
prefix: `[${spec.name}] `,
|
|
17
|
+
})
|
|
18
|
+
.pipe(Effect.flatMap((code) => code === 0
|
|
19
|
+
? Effect.void
|
|
20
|
+
: Effect.fail(new CompanionProcessError({ name: spec.name, exitCode: code }))))), { concurrency: "unbounded" }).pipe(Effect.asVoid),
|
|
21
|
+
};
|
|
22
|
+
}));
|
|
23
|
+
//# sourceMappingURL=process-supervisor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-supervisor.js","sourceRoot":"","sources":["../../src/platform/process-supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAiBpD,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAC9C,oCAAoC,CACrC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC,MAAM,CAC/C,iBAAiB,EACjB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC;IACpC,OAAO;QACL,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAChB,KAAK,CAAC,MAAM,KAAK,CAAC;YAChB,CAAC,CAAC,MAAM,CAAC,IAAI;YACb,CAAC,CAAC,MAAM,CAAC,GAAG,CACR,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACjB,MAAM;iBACH,YAAY,CAAC;gBACZ,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,IAAI;aAC1B,CAAC;iBACD,IAAI,CACH,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CACtB,IAAI,KAAK,CAAC;gBACR,CAAC,CAAC,MAAM,CAAC,IAAI;gBACb,CAAC,CAAC,MAAM,CAAC,IAAI,CACT,IAAI,qBAAqB,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAC/D,CACN,CACF,CACJ,EACD,EAAE,WAAW,EAAE,WAAW,EAAE,CAC7B,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;KAC5B,CAAC;AACJ,CAAC,CAAC,CACH,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import type { EnvironmentRow } from "../core/environment.js";
|
|
3
|
+
import { StateError } from "../errors/errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* Upsert payload: the live identity of an environment. Timestamps and template
|
|
6
|
+
* metadata are owned by the store — `created_at`, `template_db` and
|
|
7
|
+
* `template_key` are preserved on existing rows, `last_used_at` is refreshed.
|
|
8
|
+
* `now` exists so tests can inject a fixed clock; callers normally omit it.
|
|
9
|
+
*/
|
|
10
|
+
export type EnvironmentUpsert = {
|
|
11
|
+
readonly composeProject: string;
|
|
12
|
+
readonly projectId: string;
|
|
13
|
+
readonly databaseName: string;
|
|
14
|
+
readonly rootDir: string;
|
|
15
|
+
readonly worktreeName: string;
|
|
16
|
+
readonly branch: string | null;
|
|
17
|
+
readonly odooHttpPort: number;
|
|
18
|
+
readonly shared: boolean;
|
|
19
|
+
readonly now?: string | undefined;
|
|
20
|
+
};
|
|
21
|
+
export interface StateStoreApi {
|
|
22
|
+
readonly upsert: (env: EnvironmentUpsert) => Effect.Effect<void, StateError>;
|
|
23
|
+
readonly touch: (composeProject: string) => Effect.Effect<void, StateError>;
|
|
24
|
+
readonly get: (composeProject: string) => Effect.Effect<EnvironmentRow | undefined, StateError>;
|
|
25
|
+
readonly list: (filter: {
|
|
26
|
+
readonly projectId?: string | undefined;
|
|
27
|
+
}) => Effect.Effect<ReadonlyArray<EnvironmentRow>, StateError>;
|
|
28
|
+
readonly remove: (composeProject: string) => Effect.Effect<void, StateError>;
|
|
29
|
+
readonly setTemplate: (composeProject: string, meta: {
|
|
30
|
+
readonly databaseName: string;
|
|
31
|
+
readonly key: string;
|
|
32
|
+
} | null) => Effect.Effect<void, StateError>;
|
|
33
|
+
}
|
|
34
|
+
export declare const StateStore: Context.Service<StateStoreApi, StateStoreApi>;
|
|
35
|
+
/** Global registry location; `ODOO_AGENTIC_DEV_STATE_DB` overrides (tests). */
|
|
36
|
+
export declare const resolveStateDbPath: (env?: Record<string, string | undefined>) => string;
|
|
37
|
+
export declare const StateStoreLive: Layer.Layer<StateStoreApi, never, never>;
|