@knpkv/herdr-fleet 0.4.0 → 0.5.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 +5 -0
- package/dist/response.d.ts.map +1 -1
- package/dist/response.js +9 -19
- package/dist/response.js.map +1 -1
- package/dist/sqlite.d.ts +86 -0
- package/dist/sqlite.d.ts.map +1 -0
- package/dist/sqlite.js +148 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/store.d.ts +101 -101
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +24 -42
- package/dist/store.js.map +1 -1
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -15,6 +15,11 @@ The package exports:
|
|
|
15
15
|
- `JobStore`, the local SQLite persistence boundary
|
|
16
16
|
- `makeFleetService`, the approval and execution state machine
|
|
17
17
|
- typed validation, storage, approval, authorization, and operation errors
|
|
18
|
+
- `@knpkv/herdr-fleet/sqlite`, the private on-disk database opener for the `node:sqlite` Herdr stores, and the path checks the orchestrator database reuses
|
|
19
|
+
|
|
20
|
+
The `node:sqlite` stores (Fleet jobs, approvals, coordinator chat, Connect activity and relationships, Work) open their database through `openPrivateSqlite` from `@knpkv/herdr-fleet/sqlite`. The state directory and the database, `-wal` and `-shm` files are refused, unchanged, when they are symbolic links, the wrong type, or group/other writable; otherwise the directory is restricted to `0700` (created if missing) and the files to `0600`, and WAL is enabled after the store's schema step. Symlinked ancestors such as macOS `/var` are allowed. `preparePrivateDatabasePath` does the same file-system work for other clients.
|
|
21
|
+
|
|
22
|
+
The coordinator's orchestrator database (`@effect/sql-sqlite-node`) keeps its own, different policy: it requires an existing state directory to be exactly `0700` and refuses it otherwise, refuses symbolic links through `verifyPrivatePathIdentity`, and then sets the files to `0600` with `securePrivateDatabaseFiles`. It does not refuse a group-writable database file; it restricts it.
|
|
18
23
|
|
|
19
24
|
`HostConfiguration.pushAllowedOrigins` is the explicit exact-origin allowlist used by the approval runtime for browser push delivery. It has no implicit defaults; deployment configuration owns the accepted push services.
|
|
20
25
|
|
package/dist/response.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAa,MAAM,EAAE,MAAM,QAAQ,CAAA;AAClD,OAAO,KAAK,KAAK,kBAAkB,MAAM,gCAAgC,CAAA;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAGpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAEvD,eAAO,MAAM,mBAAmB,+HAqB9B,CAAA;AAEF,eAAO,MAAM,yBAAyB,GAAmD,CAAC,wLAgBxF,CAAA"}
|
package/dist/response.js
CHANGED
|
@@ -1,31 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collectBounded } from "@knpkv/bounded-io";
|
|
2
|
+
import { Effect, Predicate, Schema } from "effect";
|
|
2
3
|
import { FleetResponseBodyError } from "./errors.js";
|
|
3
4
|
import { fleetResponseBodyMaxBytes } from "./limits.js";
|
|
4
5
|
export { fleetResponseBodyMaxBytes } from "./limits.js";
|
|
5
|
-
const emptyResponseBody = () => ({ bytes: 0, chunks: [] });
|
|
6
6
|
export const boundedResponseText = Effect.fn("FleetResponse.boundedText")(function* (response, maximumBytes = fleetResponseBodyMaxBytes) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}))
|
|
15
|
-
: Effect.succeed({ bytes, chunks: [...state.chunks, chunk] });
|
|
16
|
-
}), Effect.mapError((cause) => Predicate.isTagged(cause, "FleetResponseBodyError")
|
|
17
|
-
? cause
|
|
7
|
+
// A whole-body decode, as before: a truncated trailing sequence becomes U+FFFD.
|
|
8
|
+
const body = yield* collectBounded(response.stream, maximumBytes).pipe(Effect.mapError((cause) => Predicate.isTagged(cause, "ByteLimitExceeded")
|
|
9
|
+
? new FleetResponseBodyError({
|
|
10
|
+
cause: cause.observedBytes,
|
|
11
|
+
detail: `response body exceeded ${maximumBytes} bytes`,
|
|
12
|
+
reason: "too_large"
|
|
13
|
+
})
|
|
18
14
|
: new FleetResponseBodyError({
|
|
19
15
|
cause,
|
|
20
16
|
detail: String(cause),
|
|
21
17
|
reason: "transport"
|
|
22
18
|
})));
|
|
23
|
-
const body = new Uint8Array(collected.bytes);
|
|
24
|
-
let offset = 0;
|
|
25
|
-
for (const chunk of collected.chunks) {
|
|
26
|
-
body.set(chunk, offset);
|
|
27
|
-
offset += chunk.byteLength;
|
|
28
|
-
}
|
|
29
19
|
return new TextDecoder().decode(body);
|
|
30
20
|
});
|
|
31
21
|
export const decodeBoundedResponseJson = Effect.fn("FleetResponse.decodeJson")(function* (response, schema, maximumBytes = fleetResponseBodyMaxBytes) {
|
package/dist/response.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAElD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAEvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAEvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,EAAE,CAAC,2BAA2B,CAAC,CAAC,QAAQ,CAAC,EACjF,QAA+C,EAC/C,YAAY,GAAG,yBAAyB;IAExC,gFAAgF;IAChF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,IAAI,CACpE,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACxB,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;QAC5C,CAAC,CAAC,IAAI,sBAAsB,CAAC;YAC3B,KAAK,EAAE,KAAK,CAAC,aAAa;YAC1B,MAAM,EAAE,0BAA0B,YAAY,QAAQ;YACtD,MAAM,EAAE,WAAW;SACpB,CAAC;QACF,CAAC,CAAC,IAAI,sBAAsB,CAAC;YAC3B,KAAK;YACL,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC;YACrB,MAAM,EAAE,WAAW;SACpB,CAAC,CACL,CACF,CAAA;IACD,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AACvC,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC,EAAE,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC,EACtF,QAA+C,EAC/C,MAA8C,EAC9C,YAAY,GAAG,yBAAyB;IAExC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;IAC/D,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAChF,MAAM,CAAC,QAAQ,CACb,CAAC,KAAK,EAAE,EAAE,CACR,IAAI,sBAAsB,CAAC;QACzB,KAAK;QACL,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC;QACrB,MAAM,EAAE,QAAQ;KACjB,CAAC,CACL,CACF,CAAA;AACH,CAAC,CAAC,CAAA"}
|
package/dist/sqlite.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Private on-disk SQLite databases for Herdr stores.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Refuse, never repair.** A state directory or database file that other users
|
|
7
|
+
* could write is refused, and left exactly as found. Repairing it would hide that
|
|
8
|
+
* someone could already have changed it.
|
|
9
|
+
* - **Owner-only once accepted.** The state directory ends `0700` and the database
|
|
10
|
+
* files `0600`; an existing directory is checked before it is restricted, so a
|
|
11
|
+
* writable one is refused rather than quietly fixed.
|
|
12
|
+
* - **Only the last path component is checked for a symlink.** Symlinked ancestors
|
|
13
|
+
* (macOS `/var`, a Nix-managed home) are normal and allowed; a database or state
|
|
14
|
+
* directory that is itself a link is refused.
|
|
15
|
+
* - **Two layers.** {@link preparePrivateDatabasePath} and
|
|
16
|
+
* {@link securePrivateDatabaseFiles} only touch the file system, so any SQLite
|
|
17
|
+
* client can use them; {@link openPrivateSqlite} adds the `node:sqlite` connection.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const opened = yield* openPrivateSqlite(path, {
|
|
22
|
+
* initialize: (database) => database.exec("CREATE TABLE IF NOT EXISTS jobs (id TEXT PRIMARY KEY)")
|
|
23
|
+
* })
|
|
24
|
+
* // after each write, WAL may have created -wal/-shm:
|
|
25
|
+
* yield* opened.secureFiles
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
31
|
+
import { DatabaseSync } from "node:sqlite";
|
|
32
|
+
declare const PrivateDatabaseError_base: Schema.Class<PrivateDatabaseError, Schema.TaggedStruct<"PrivateDatabaseError", {
|
|
33
|
+
readonly operation: Schema.String;
|
|
34
|
+
readonly cause: Schema.Defect;
|
|
35
|
+
}>, import("effect/Cause").YieldableError>;
|
|
36
|
+
/** One step of opening or securing a private database failed; `operation` names the step. */
|
|
37
|
+
export declare class PrivateDatabaseError extends PrivateDatabaseError_base {
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Refuse `path` if it is itself a symlink or does not resolve to its real parent
|
|
41
|
+
* joined with its name; a missing component is checked through its parent.
|
|
42
|
+
* Failures carry `operation` (sub-steps append `.readlink`, `.exists`, …).
|
|
43
|
+
*/
|
|
44
|
+
export declare const verifyPrivatePathIdentity: (path: string, operation: string) => Effect.Effect<void, PrivateDatabaseError, FileSystem.FileSystem | Path.Path>;
|
|
45
|
+
/**
|
|
46
|
+
* Make `path`'s directory and any existing database files safe to open, or refuse.
|
|
47
|
+
*
|
|
48
|
+
* Refuses a directory or file that is a symlink, the wrong type, or group/other
|
|
49
|
+
* writable, without changing it. Otherwise restricts the directory (created if
|
|
50
|
+
* missing) to `0700` and existing db/-wal/-shm files to `0600`.
|
|
51
|
+
*/
|
|
52
|
+
export declare const preparePrivateDatabasePath: (path: string) => Effect.Effect<undefined, PrivateDatabaseError, FileSystem.FileSystem | Path.Path>;
|
|
53
|
+
/**
|
|
54
|
+
* Set the database file and its `-wal`/`-shm` siblings, whichever exist, to `0600`.
|
|
55
|
+
*
|
|
56
|
+
* SQLite creates the siblings lazily, so call this after writes as well as after
|
|
57
|
+
* opening. A sibling that has become a symlink is refused rather than followed.
|
|
58
|
+
*/
|
|
59
|
+
export declare const securePrivateDatabaseFiles: (path: string) => Effect.Effect<void, PrivateDatabaseError, FileSystem.FileSystem | Path.Path>;
|
|
60
|
+
/** An open `node:sqlite` connection to a private database. */
|
|
61
|
+
export interface PrivateSqlite {
|
|
62
|
+
readonly database: DatabaseSync;
|
|
63
|
+
/** {@link securePrivateDatabaseFiles} for this database; run after writes. */
|
|
64
|
+
readonly secureFiles: Effect.Effect<void, PrivateDatabaseError>;
|
|
65
|
+
}
|
|
66
|
+
export interface OpenPrivateSqliteOptions {
|
|
67
|
+
/**
|
|
68
|
+
* Creates or migrates the schema; a throw closes the connection. Runs before WAL
|
|
69
|
+
* is enabled, so a legacy rollback-journal database migrates in its own mode first
|
|
70
|
+
* (Work's legacy authority-table migration fails with "database is locked" otherwise).
|
|
71
|
+
*/
|
|
72
|
+
readonly initialize: (database: DatabaseSync) => void;
|
|
73
|
+
/** Wait up to this long for another connection's lock instead of failing at once. */
|
|
74
|
+
readonly busyTimeoutMillis?: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Prepare `path` (see {@link preparePrivateDatabasePath}), open it in WAL mode,
|
|
78
|
+
* initialize the schema and secure the files. The caller owns the returned
|
|
79
|
+
* connection and must close it.
|
|
80
|
+
*/
|
|
81
|
+
export declare const openPrivateSqlite: (path: string, options: OpenPrivateSqliteOptions) => Effect.Effect<{
|
|
82
|
+
database: DatabaseSync;
|
|
83
|
+
secureFiles: Effect.Effect<void, PrivateDatabaseError, never>;
|
|
84
|
+
}, PrivateDatabaseError, FileSystem.FileSystem | Path.Path>;
|
|
85
|
+
export {};
|
|
86
|
+
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;;;;;AAE1C,6FAA6F;AAC7F,qBAAa,oBAAqB,SAAQ,yBAMzC;CAAG;AA2CJ;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,mHAOpC,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,qGAwCrC,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,gGAcrC,CAAA;AAEF,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAA;CAChE;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAA;IACrD,qFAAqF;IACrF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CACpC;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;2DA8B5B,CAAA"}
|
package/dist/sqlite.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Private on-disk SQLite databases for Herdr stores.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Refuse, never repair.** A state directory or database file that other users
|
|
7
|
+
* could write is refused, and left exactly as found. Repairing it would hide that
|
|
8
|
+
* someone could already have changed it.
|
|
9
|
+
* - **Owner-only once accepted.** The state directory ends `0700` and the database
|
|
10
|
+
* files `0600`; an existing directory is checked before it is restricted, so a
|
|
11
|
+
* writable one is refused rather than quietly fixed.
|
|
12
|
+
* - **Only the last path component is checked for a symlink.** Symlinked ancestors
|
|
13
|
+
* (macOS `/var`, a Nix-managed home) are normal and allowed; a database or state
|
|
14
|
+
* directory that is itself a link is refused.
|
|
15
|
+
* - **Two layers.** {@link preparePrivateDatabasePath} and
|
|
16
|
+
* {@link securePrivateDatabaseFiles} only touch the file system, so any SQLite
|
|
17
|
+
* client can use them; {@link openPrivateSqlite} adds the `node:sqlite` connection.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const opened = yield* openPrivateSqlite(path, {
|
|
22
|
+
* initialize: (database) => database.exec("CREATE TABLE IF NOT EXISTS jobs (id TEXT PRIMARY KEY)")
|
|
23
|
+
* })
|
|
24
|
+
* // after each write, WAL may have created -wal/-shm:
|
|
25
|
+
* yield* opened.secureFiles
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
31
|
+
import { DatabaseSync } from "node:sqlite";
|
|
32
|
+
/** One step of opening or securing a private database failed; `operation` names the step. */
|
|
33
|
+
export class PrivateDatabaseError extends Schema.TaggedError()("PrivateDatabaseError", {
|
|
34
|
+
operation: Schema.String,
|
|
35
|
+
cause: Schema.Defect()
|
|
36
|
+
}) {
|
|
37
|
+
}
|
|
38
|
+
const failWith = (operation) => (cause) => new PrivateDatabaseError({ operation, cause });
|
|
39
|
+
const groupOrOtherWritable = (paths, mode) => paths.sep === "/" && (mode & 0o022) !== 0;
|
|
40
|
+
const databaseFiles = (path) => [path, `${path}-wal`, `${path}-shm`];
|
|
41
|
+
// Refuses `path` when it is itself a symlink — including a dangling one — or when
|
|
42
|
+
// its real path is not its real parent joined with its name. A component that does
|
|
43
|
+
// not exist yet is checked through its parent.
|
|
44
|
+
const verifyPathIdentity = (path, fileSystem, paths, operation) => fileSystem.readLink(path).pipe(Effect.matchEffect({
|
|
45
|
+
onFailure: (cause) => cause.reason._tag === "NotFound" || cause.reason._tag === "Unknown"
|
|
46
|
+
? Effect.void
|
|
47
|
+
: Effect.fail(new PrivateDatabaseError({ cause, operation: `${operation}.readlink` })),
|
|
48
|
+
onSuccess: (target) => Effect.fail(new PrivateDatabaseError({ cause: { path, target }, operation }))
|
|
49
|
+
}), Effect.andThen(fileSystem.exists(path).pipe(Effect.mapError(failWith(`${operation}.exists`)))), Effect.flatMap((exists) => {
|
|
50
|
+
const parent = paths.dirname(path);
|
|
51
|
+
if (!exists) {
|
|
52
|
+
return parent === path ? Effect.void : verifyPathIdentity(parent, fileSystem, paths, operation);
|
|
53
|
+
}
|
|
54
|
+
return Effect.all({
|
|
55
|
+
realPath: fileSystem.realPath(path).pipe(Effect.mapError(failWith(`${operation}.realpath`))),
|
|
56
|
+
realParentPath: fileSystem.realPath(parent).pipe(Effect.mapError(failWith(`${operation}.parent-realpath`)))
|
|
57
|
+
}).pipe(Effect.flatMap(({ realParentPath, realPath }) => {
|
|
58
|
+
const expectedPath = paths.join(realParentPath, paths.basename(path));
|
|
59
|
+
return realPath === expectedPath
|
|
60
|
+
? Effect.void
|
|
61
|
+
: Effect.fail(new PrivateDatabaseError({ cause: { expectedPath, path, realPath }, operation }));
|
|
62
|
+
}));
|
|
63
|
+
}));
|
|
64
|
+
/**
|
|
65
|
+
* Refuse `path` if it is itself a symlink or does not resolve to its real parent
|
|
66
|
+
* joined with its name; a missing component is checked through its parent.
|
|
67
|
+
* Failures carry `operation` (sub-steps append `.readlink`, `.exists`, …).
|
|
68
|
+
*/
|
|
69
|
+
export const verifyPrivatePathIdentity = Effect.fn("PrivateDatabase.verifyPathIdentity")(function* (path, operation) {
|
|
70
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
71
|
+
const paths = yield* Path.Path;
|
|
72
|
+
yield* verifyPathIdentity(path, fileSystem, paths, operation);
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* Make `path`'s directory and any existing database files safe to open, or refuse.
|
|
76
|
+
*
|
|
77
|
+
* Refuses a directory or file that is a symlink, the wrong type, or group/other
|
|
78
|
+
* writable, without changing it. Otherwise restricts the directory (created if
|
|
79
|
+
* missing) to `0700` and existing db/-wal/-shm files to `0600`.
|
|
80
|
+
*/
|
|
81
|
+
export const preparePrivateDatabasePath = Effect.fn("PrivateDatabase.prepare")(function* (path) {
|
|
82
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
83
|
+
const paths = yield* Path.Path;
|
|
84
|
+
const directory = paths.dirname(path);
|
|
85
|
+
yield* fileSystem.makeDirectory(directory, { recursive: true, mode: 0o700 }).pipe(Effect.mapError(failWith("open.directory")));
|
|
86
|
+
const directoryInfo = yield* fileSystem.stat(directory).pipe(Effect.mapError(failWith("open.directory.stat")));
|
|
87
|
+
if (directoryInfo.type !== "Directory" || groupOrOtherWritable(paths, directoryInfo.mode)) {
|
|
88
|
+
return yield* new PrivateDatabaseError({
|
|
89
|
+
cause: { directory, mode: directoryInfo.mode, type: directoryInfo.type },
|
|
90
|
+
operation: "open.directory.unsafe"
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
yield* verifyPathIdentity(directory, fileSystem, paths, "open.directory.path-identity");
|
|
94
|
+
yield* fileSystem.chmod(directory, 0o700).pipe(Effect.mapError(failWith("open.secureDirectory")));
|
|
95
|
+
yield* Effect.forEach(databaseFiles(path), (file) => verifyPathIdentity(file, fileSystem, paths, "open.path-identity").pipe(Effect.andThen(fileSystem.exists(file).pipe(Effect.mapError(failWith("open.file.exists")))), Effect.flatMap((exists) => {
|
|
96
|
+
if (!exists)
|
|
97
|
+
return Effect.void;
|
|
98
|
+
return fileSystem.stat(file).pipe(Effect.mapError(failWith("open.file.stat")), Effect.flatMap((info) => info.type !== "File" || groupOrOtherWritable(paths, info.mode)
|
|
99
|
+
? Effect.fail(new PrivateDatabaseError({
|
|
100
|
+
cause: { file, mode: info.mode, type: info.type },
|
|
101
|
+
operation: "open.file.unsafe"
|
|
102
|
+
}))
|
|
103
|
+
: fileSystem.chmod(file, 0o600).pipe(Effect.mapError(failWith("open.file.secure")))));
|
|
104
|
+
})), { discard: true });
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* Set the database file and its `-wal`/`-shm` siblings, whichever exist, to `0600`.
|
|
108
|
+
*
|
|
109
|
+
* SQLite creates the siblings lazily, so call this after writes as well as after
|
|
110
|
+
* opening. A sibling that has become a symlink is refused rather than followed.
|
|
111
|
+
*/
|
|
112
|
+
export const securePrivateDatabaseFiles = Effect.fn("PrivateDatabase.secureFiles")(function* (path) {
|
|
113
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
114
|
+
const paths = yield* Path.Path;
|
|
115
|
+
yield* Effect.forEach(databaseFiles(path), (file) => verifyPathIdentity(file, fileSystem, paths, "secure.path-identity").pipe(Effect.andThen(fileSystem.exists(file).pipe(Effect.mapError(failWith("secure.exists")))), Effect.flatMap((exists) => exists ? fileSystem.chmod(file, 0o600).pipe(Effect.mapError(failWith("secure.chmod"))) : Effect.void)), { discard: true });
|
|
116
|
+
});
|
|
117
|
+
/**
|
|
118
|
+
* Prepare `path` (see {@link preparePrivateDatabasePath}), open it in WAL mode,
|
|
119
|
+
* initialize the schema and secure the files. The caller owns the returned
|
|
120
|
+
* connection and must close it.
|
|
121
|
+
*/
|
|
122
|
+
export const openPrivateSqlite = Effect.fn("PrivateDatabase.openSqlite")(function* (path, options) {
|
|
123
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
124
|
+
const paths = yield* Path.Path;
|
|
125
|
+
yield* preparePrivateDatabasePath(path);
|
|
126
|
+
const database = yield* Effect.try({
|
|
127
|
+
try: () => {
|
|
128
|
+
const database = new DatabaseSync(path);
|
|
129
|
+
try {
|
|
130
|
+
if (options.busyTimeoutMillis !== undefined) {
|
|
131
|
+
database.exec(`PRAGMA busy_timeout = ${options.busyTimeoutMillis}`);
|
|
132
|
+
}
|
|
133
|
+
options.initialize(database);
|
|
134
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
135
|
+
return database;
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
database.close();
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
catch: failWith("open.database")
|
|
143
|
+
});
|
|
144
|
+
const secureFiles = securePrivateDatabaseFiles(path).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, paths));
|
|
145
|
+
yield* secureFiles.pipe(Effect.onError(() => Effect.sync(() => database.close())));
|
|
146
|
+
return { database, secureFiles };
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Effect
|
|
1
|
+
import { Effect } from "effect";
|
|
2
2
|
import { FleetJobConflictError, FleetStoreError, FleetTransitionConflictError } from "./errors.js";
|
|
3
3
|
export declare class JobStore {
|
|
4
4
|
#private;
|
|
5
5
|
readonly path: string;
|
|
6
6
|
private constructor();
|
|
7
|
-
static readonly open: (path: string) => Effect.Effect<JobStore, FleetStoreError, FileSystem.FileSystem | Path.Path>;
|
|
7
|
+
static readonly open: (path: string) => Effect.Effect<JobStore, FleetStoreError, import("effect/FileSystem").FileSystem | import("effect/Path").Path>;
|
|
8
8
|
readonly put: (this: JobStore, record: {
|
|
9
9
|
readonly id: string;
|
|
10
10
|
readonly createdAt: number;
|
|
@@ -20,20 +20,11 @@ export declare class JobStore {
|
|
|
20
20
|
readonly expiredAt?: number | null;
|
|
21
21
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
22
22
|
readonly payload: {
|
|
23
|
-
readonly kind: "nix.check";
|
|
24
|
-
} | {
|
|
25
|
-
readonly kind: "nix.apply";
|
|
26
|
-
readonly ref: string;
|
|
27
|
-
} | {
|
|
28
23
|
readonly kind: "agent.delegate";
|
|
29
24
|
readonly repository: string;
|
|
30
25
|
readonly prompt: string;
|
|
31
26
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
32
27
|
readonly channel?: "coordinator_chat";
|
|
33
|
-
} | {
|
|
34
|
-
readonly kind: "agent.message";
|
|
35
|
-
readonly session: string;
|
|
36
|
-
readonly message: string;
|
|
37
28
|
} | {
|
|
38
29
|
readonly kind: "work.reconcile";
|
|
39
30
|
readonly repository: string;
|
|
@@ -160,6 +151,15 @@ export declare class JobStore {
|
|
|
160
151
|
readonly expectedGoalUpdatedAt: number;
|
|
161
152
|
} | {
|
|
162
153
|
readonly kind: "browser.mcp.recover";
|
|
154
|
+
} | {
|
|
155
|
+
readonly kind: "nix.check";
|
|
156
|
+
} | {
|
|
157
|
+
readonly kind: "nix.apply";
|
|
158
|
+
readonly ref: string;
|
|
159
|
+
} | {
|
|
160
|
+
readonly kind: "agent.message";
|
|
161
|
+
readonly session: string;
|
|
162
|
+
readonly message: string;
|
|
163
163
|
};
|
|
164
164
|
readonly result: string | null;
|
|
165
165
|
readonly acceptedReceipt?: string | null;
|
|
@@ -196,20 +196,11 @@ export declare class JobStore {
|
|
|
196
196
|
readonly expiredAt?: number | null;
|
|
197
197
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
198
198
|
readonly payload: {
|
|
199
|
-
readonly kind: "nix.check";
|
|
200
|
-
} | {
|
|
201
|
-
readonly kind: "nix.apply";
|
|
202
|
-
readonly ref: string;
|
|
203
|
-
} | {
|
|
204
199
|
readonly kind: "agent.delegate";
|
|
205
200
|
readonly repository: string;
|
|
206
201
|
readonly prompt: string;
|
|
207
202
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
208
203
|
readonly channel?: "coordinator_chat";
|
|
209
|
-
} | {
|
|
210
|
-
readonly kind: "agent.message";
|
|
211
|
-
readonly session: string;
|
|
212
|
-
readonly message: string;
|
|
213
204
|
} | {
|
|
214
205
|
readonly kind: "work.reconcile";
|
|
215
206
|
readonly repository: string;
|
|
@@ -336,6 +327,15 @@ export declare class JobStore {
|
|
|
336
327
|
readonly expectedGoalUpdatedAt: number;
|
|
337
328
|
} | {
|
|
338
329
|
readonly kind: "browser.mcp.recover";
|
|
330
|
+
} | {
|
|
331
|
+
readonly kind: "nix.check";
|
|
332
|
+
} | {
|
|
333
|
+
readonly kind: "nix.apply";
|
|
334
|
+
readonly ref: string;
|
|
335
|
+
} | {
|
|
336
|
+
readonly kind: "agent.message";
|
|
337
|
+
readonly session: string;
|
|
338
|
+
readonly message: string;
|
|
339
339
|
};
|
|
340
340
|
readonly result: string | null;
|
|
341
341
|
readonly acceptedReceipt?: string | null;
|
|
@@ -373,20 +373,11 @@ export declare class JobStore {
|
|
|
373
373
|
readonly expiredAt?: number | null;
|
|
374
374
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
375
375
|
readonly payload: {
|
|
376
|
-
readonly kind: "nix.check";
|
|
377
|
-
} | {
|
|
378
|
-
readonly kind: "nix.apply";
|
|
379
|
-
readonly ref: string;
|
|
380
|
-
} | {
|
|
381
376
|
readonly kind: "agent.delegate";
|
|
382
377
|
readonly repository: string;
|
|
383
378
|
readonly prompt: string;
|
|
384
379
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
385
380
|
readonly channel?: "coordinator_chat";
|
|
386
|
-
} | {
|
|
387
|
-
readonly kind: "agent.message";
|
|
388
|
-
readonly session: string;
|
|
389
|
-
readonly message: string;
|
|
390
381
|
} | {
|
|
391
382
|
readonly kind: "work.reconcile";
|
|
392
383
|
readonly repository: string;
|
|
@@ -513,6 +504,15 @@ export declare class JobStore {
|
|
|
513
504
|
readonly expectedGoalUpdatedAt: number;
|
|
514
505
|
} | {
|
|
515
506
|
readonly kind: "browser.mcp.recover";
|
|
507
|
+
} | {
|
|
508
|
+
readonly kind: "nix.check";
|
|
509
|
+
} | {
|
|
510
|
+
readonly kind: "nix.apply";
|
|
511
|
+
readonly ref: string;
|
|
512
|
+
} | {
|
|
513
|
+
readonly kind: "agent.message";
|
|
514
|
+
readonly session: string;
|
|
515
|
+
readonly message: string;
|
|
516
516
|
};
|
|
517
517
|
readonly result: string | null;
|
|
518
518
|
readonly acceptedReceipt?: string | null;
|
|
@@ -549,20 +549,11 @@ export declare class JobStore {
|
|
|
549
549
|
readonly expiredAt?: number | null;
|
|
550
550
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
551
551
|
readonly payload: {
|
|
552
|
-
readonly kind: "nix.check";
|
|
553
|
-
} | {
|
|
554
|
-
readonly kind: "nix.apply";
|
|
555
|
-
readonly ref: string;
|
|
556
|
-
} | {
|
|
557
552
|
readonly kind: "agent.delegate";
|
|
558
553
|
readonly repository: string;
|
|
559
554
|
readonly prompt: string;
|
|
560
555
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
561
556
|
readonly channel?: "coordinator_chat";
|
|
562
|
-
} | {
|
|
563
|
-
readonly kind: "agent.message";
|
|
564
|
-
readonly session: string;
|
|
565
|
-
readonly message: string;
|
|
566
557
|
} | {
|
|
567
558
|
readonly kind: "work.reconcile";
|
|
568
559
|
readonly repository: string;
|
|
@@ -689,6 +680,15 @@ export declare class JobStore {
|
|
|
689
680
|
readonly expectedGoalUpdatedAt: number;
|
|
690
681
|
} | {
|
|
691
682
|
readonly kind: "browser.mcp.recover";
|
|
683
|
+
} | {
|
|
684
|
+
readonly kind: "nix.check";
|
|
685
|
+
} | {
|
|
686
|
+
readonly kind: "nix.apply";
|
|
687
|
+
readonly ref: string;
|
|
688
|
+
} | {
|
|
689
|
+
readonly kind: "agent.message";
|
|
690
|
+
readonly session: string;
|
|
691
|
+
readonly message: string;
|
|
692
692
|
};
|
|
693
693
|
readonly result: string | null;
|
|
694
694
|
readonly acceptedReceipt?: string | null;
|
|
@@ -725,20 +725,11 @@ export declare class JobStore {
|
|
|
725
725
|
readonly expiredAt?: number | null;
|
|
726
726
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
727
727
|
readonly payload: {
|
|
728
|
-
readonly kind: "nix.check";
|
|
729
|
-
} | {
|
|
730
|
-
readonly kind: "nix.apply";
|
|
731
|
-
readonly ref: string;
|
|
732
|
-
} | {
|
|
733
728
|
readonly kind: "agent.delegate";
|
|
734
729
|
readonly repository: string;
|
|
735
730
|
readonly prompt: string;
|
|
736
731
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
737
732
|
readonly channel?: "coordinator_chat";
|
|
738
|
-
} | {
|
|
739
|
-
readonly kind: "agent.message";
|
|
740
|
-
readonly session: string;
|
|
741
|
-
readonly message: string;
|
|
742
733
|
} | {
|
|
743
734
|
readonly kind: "work.reconcile";
|
|
744
735
|
readonly repository: string;
|
|
@@ -865,6 +856,15 @@ export declare class JobStore {
|
|
|
865
856
|
readonly expectedGoalUpdatedAt: number;
|
|
866
857
|
} | {
|
|
867
858
|
readonly kind: "browser.mcp.recover";
|
|
859
|
+
} | {
|
|
860
|
+
readonly kind: "nix.check";
|
|
861
|
+
} | {
|
|
862
|
+
readonly kind: "nix.apply";
|
|
863
|
+
readonly ref: string;
|
|
864
|
+
} | {
|
|
865
|
+
readonly kind: "agent.message";
|
|
866
|
+
readonly session: string;
|
|
867
|
+
readonly message: string;
|
|
868
868
|
};
|
|
869
869
|
readonly result: string | null;
|
|
870
870
|
readonly acceptedReceipt?: string | null;
|
|
@@ -902,20 +902,11 @@ export declare class JobStore {
|
|
|
902
902
|
readonly expiredAt?: number | null;
|
|
903
903
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
904
904
|
readonly payload: {
|
|
905
|
-
readonly kind: "nix.check";
|
|
906
|
-
} | {
|
|
907
|
-
readonly kind: "nix.apply";
|
|
908
|
-
readonly ref: string;
|
|
909
|
-
} | {
|
|
910
905
|
readonly kind: "agent.delegate";
|
|
911
906
|
readonly repository: string;
|
|
912
907
|
readonly prompt: string;
|
|
913
908
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
914
909
|
readonly channel?: "coordinator_chat";
|
|
915
|
-
} | {
|
|
916
|
-
readonly kind: "agent.message";
|
|
917
|
-
readonly session: string;
|
|
918
|
-
readonly message: string;
|
|
919
910
|
} | {
|
|
920
911
|
readonly kind: "work.reconcile";
|
|
921
912
|
readonly repository: string;
|
|
@@ -1042,6 +1033,15 @@ export declare class JobStore {
|
|
|
1042
1033
|
readonly expectedGoalUpdatedAt: number;
|
|
1043
1034
|
} | {
|
|
1044
1035
|
readonly kind: "browser.mcp.recover";
|
|
1036
|
+
} | {
|
|
1037
|
+
readonly kind: "nix.check";
|
|
1038
|
+
} | {
|
|
1039
|
+
readonly kind: "nix.apply";
|
|
1040
|
+
readonly ref: string;
|
|
1041
|
+
} | {
|
|
1042
|
+
readonly kind: "agent.message";
|
|
1043
|
+
readonly session: string;
|
|
1044
|
+
readonly message: string;
|
|
1045
1045
|
};
|
|
1046
1046
|
readonly result: string | null;
|
|
1047
1047
|
readonly acceptedReceipt?: string | null;
|
|
@@ -1079,20 +1079,11 @@ export declare class JobStore {
|
|
|
1079
1079
|
readonly expiredAt?: number | null;
|
|
1080
1080
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
1081
1081
|
readonly payload: {
|
|
1082
|
-
readonly kind: "nix.check";
|
|
1083
|
-
} | {
|
|
1084
|
-
readonly kind: "nix.apply";
|
|
1085
|
-
readonly ref: string;
|
|
1086
|
-
} | {
|
|
1087
1082
|
readonly kind: "agent.delegate";
|
|
1088
1083
|
readonly repository: string;
|
|
1089
1084
|
readonly prompt: string;
|
|
1090
1085
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
1091
1086
|
readonly channel?: "coordinator_chat";
|
|
1092
|
-
} | {
|
|
1093
|
-
readonly kind: "agent.message";
|
|
1094
|
-
readonly session: string;
|
|
1095
|
-
readonly message: string;
|
|
1096
1087
|
} | {
|
|
1097
1088
|
readonly kind: "work.reconcile";
|
|
1098
1089
|
readonly repository: string;
|
|
@@ -1219,6 +1210,15 @@ export declare class JobStore {
|
|
|
1219
1210
|
readonly expectedGoalUpdatedAt: number;
|
|
1220
1211
|
} | {
|
|
1221
1212
|
readonly kind: "browser.mcp.recover";
|
|
1213
|
+
} | {
|
|
1214
|
+
readonly kind: "nix.check";
|
|
1215
|
+
} | {
|
|
1216
|
+
readonly kind: "nix.apply";
|
|
1217
|
+
readonly ref: string;
|
|
1218
|
+
} | {
|
|
1219
|
+
readonly kind: "agent.message";
|
|
1220
|
+
readonly session: string;
|
|
1221
|
+
readonly message: string;
|
|
1222
1222
|
};
|
|
1223
1223
|
readonly result: string | null;
|
|
1224
1224
|
readonly acceptedReceipt?: string | null;
|
|
@@ -1259,20 +1259,11 @@ export declare class JobStore {
|
|
|
1259
1259
|
readonly expiredAt?: number | null;
|
|
1260
1260
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
1261
1261
|
readonly payload: {
|
|
1262
|
-
readonly kind: "nix.check";
|
|
1263
|
-
} | {
|
|
1264
|
-
readonly kind: "nix.apply";
|
|
1265
|
-
readonly ref: string;
|
|
1266
|
-
} | {
|
|
1267
1262
|
readonly kind: "agent.delegate";
|
|
1268
1263
|
readonly repository: string;
|
|
1269
1264
|
readonly prompt: string;
|
|
1270
1265
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
1271
1266
|
readonly channel?: "coordinator_chat";
|
|
1272
|
-
} | {
|
|
1273
|
-
readonly kind: "agent.message";
|
|
1274
|
-
readonly session: string;
|
|
1275
|
-
readonly message: string;
|
|
1276
1267
|
} | {
|
|
1277
1268
|
readonly kind: "work.reconcile";
|
|
1278
1269
|
readonly repository: string;
|
|
@@ -1399,6 +1390,15 @@ export declare class JobStore {
|
|
|
1399
1390
|
readonly expectedGoalUpdatedAt: number;
|
|
1400
1391
|
} | {
|
|
1401
1392
|
readonly kind: "browser.mcp.recover";
|
|
1393
|
+
} | {
|
|
1394
|
+
readonly kind: "nix.check";
|
|
1395
|
+
} | {
|
|
1396
|
+
readonly kind: "nix.apply";
|
|
1397
|
+
readonly ref: string;
|
|
1398
|
+
} | {
|
|
1399
|
+
readonly kind: "agent.message";
|
|
1400
|
+
readonly session: string;
|
|
1401
|
+
readonly message: string;
|
|
1402
1402
|
};
|
|
1403
1403
|
readonly result: string | null;
|
|
1404
1404
|
readonly acceptedReceipt?: string | null;
|
|
@@ -1439,20 +1439,11 @@ export declare class JobStore {
|
|
|
1439
1439
|
readonly expiredAt?: number | null;
|
|
1440
1440
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
1441
1441
|
readonly payload: {
|
|
1442
|
-
readonly kind: "nix.check";
|
|
1443
|
-
} | {
|
|
1444
|
-
readonly kind: "nix.apply";
|
|
1445
|
-
readonly ref: string;
|
|
1446
|
-
} | {
|
|
1447
1442
|
readonly kind: "agent.delegate";
|
|
1448
1443
|
readonly repository: string;
|
|
1449
1444
|
readonly prompt: string;
|
|
1450
1445
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
1451
1446
|
readonly channel?: "coordinator_chat";
|
|
1452
|
-
} | {
|
|
1453
|
-
readonly kind: "agent.message";
|
|
1454
|
-
readonly session: string;
|
|
1455
|
-
readonly message: string;
|
|
1456
1447
|
} | {
|
|
1457
1448
|
readonly kind: "work.reconcile";
|
|
1458
1449
|
readonly repository: string;
|
|
@@ -1579,6 +1570,15 @@ export declare class JobStore {
|
|
|
1579
1570
|
readonly expectedGoalUpdatedAt: number;
|
|
1580
1571
|
} | {
|
|
1581
1572
|
readonly kind: "browser.mcp.recover";
|
|
1573
|
+
} | {
|
|
1574
|
+
readonly kind: "nix.check";
|
|
1575
|
+
} | {
|
|
1576
|
+
readonly kind: "nix.apply";
|
|
1577
|
+
readonly ref: string;
|
|
1578
|
+
} | {
|
|
1579
|
+
readonly kind: "agent.message";
|
|
1580
|
+
readonly session: string;
|
|
1581
|
+
readonly message: string;
|
|
1582
1582
|
};
|
|
1583
1583
|
readonly result: string | null;
|
|
1584
1584
|
readonly acceptedReceipt?: string | null;
|
|
@@ -1616,20 +1616,11 @@ export declare class JobStore {
|
|
|
1616
1616
|
readonly expiredAt?: number | null;
|
|
1617
1617
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
1618
1618
|
readonly payload: {
|
|
1619
|
-
readonly kind: "nix.check";
|
|
1620
|
-
} | {
|
|
1621
|
-
readonly kind: "nix.apply";
|
|
1622
|
-
readonly ref: string;
|
|
1623
|
-
} | {
|
|
1624
1619
|
readonly kind: "agent.delegate";
|
|
1625
1620
|
readonly repository: string;
|
|
1626
1621
|
readonly prompt: string;
|
|
1627
1622
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
1628
1623
|
readonly channel?: "coordinator_chat";
|
|
1629
|
-
} | {
|
|
1630
|
-
readonly kind: "agent.message";
|
|
1631
|
-
readonly session: string;
|
|
1632
|
-
readonly message: string;
|
|
1633
1624
|
} | {
|
|
1634
1625
|
readonly kind: "work.reconcile";
|
|
1635
1626
|
readonly repository: string;
|
|
@@ -1756,6 +1747,15 @@ export declare class JobStore {
|
|
|
1756
1747
|
readonly expectedGoalUpdatedAt: number;
|
|
1757
1748
|
} | {
|
|
1758
1749
|
readonly kind: "browser.mcp.recover";
|
|
1750
|
+
} | {
|
|
1751
|
+
readonly kind: "nix.check";
|
|
1752
|
+
} | {
|
|
1753
|
+
readonly kind: "nix.apply";
|
|
1754
|
+
readonly ref: string;
|
|
1755
|
+
} | {
|
|
1756
|
+
readonly kind: "agent.message";
|
|
1757
|
+
readonly session: string;
|
|
1758
|
+
readonly message: string;
|
|
1759
1759
|
};
|
|
1760
1760
|
readonly result: string | null;
|
|
1761
1761
|
readonly acceptedReceipt?: string | null;
|
|
@@ -1793,20 +1793,11 @@ export declare class JobStore {
|
|
|
1793
1793
|
readonly expiredAt?: number | null;
|
|
1794
1794
|
readonly status: "expired" | "failed" | "interrupted" | "pending_approval" | "queued" | "rejected" | "running" | "succeeded";
|
|
1795
1795
|
readonly payload: {
|
|
1796
|
-
readonly kind: "nix.check";
|
|
1797
|
-
} | {
|
|
1798
|
-
readonly kind: "nix.apply";
|
|
1799
|
-
readonly ref: string;
|
|
1800
|
-
} | {
|
|
1801
1796
|
readonly kind: "agent.delegate";
|
|
1802
1797
|
readonly repository: string;
|
|
1803
1798
|
readonly prompt: string;
|
|
1804
1799
|
readonly mode: "consult" | "review" | "transition_summary" | "work";
|
|
1805
1800
|
readonly channel?: "coordinator_chat";
|
|
1806
|
-
} | {
|
|
1807
|
-
readonly kind: "agent.message";
|
|
1808
|
-
readonly session: string;
|
|
1809
|
-
readonly message: string;
|
|
1810
1801
|
} | {
|
|
1811
1802
|
readonly kind: "work.reconcile";
|
|
1812
1803
|
readonly repository: string;
|
|
@@ -1933,6 +1924,15 @@ export declare class JobStore {
|
|
|
1933
1924
|
readonly expectedGoalUpdatedAt: number;
|
|
1934
1925
|
} | {
|
|
1935
1926
|
readonly kind: "browser.mcp.recover";
|
|
1927
|
+
} | {
|
|
1928
|
+
readonly kind: "nix.check";
|
|
1929
|
+
} | {
|
|
1930
|
+
readonly kind: "nix.apply";
|
|
1931
|
+
readonly ref: string;
|
|
1932
|
+
} | {
|
|
1933
|
+
readonly kind: "agent.message";
|
|
1934
|
+
readonly session: string;
|
|
1935
|
+
readonly message: string;
|
|
1936
1936
|
};
|
|
1937
1937
|
readonly result: string | null;
|
|
1938
1938
|
readonly acceptedReceipt?: string | null;
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/store.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Effect,
|
|
2
|
-
import { DatabaseSync } from "node:sqlite";
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
3
2
|
import { FleetJobConflictError, FleetStoreError, FleetTransitionConflictError } from "./errors.js";
|
|
4
3
|
import { JobRecord } from "./model.js";
|
|
4
|
+
import { openPrivateSqlite } from "./sqlite.js";
|
|
5
5
|
const storeError = (operation) => (cause) => new FleetStoreError({ operation, detail: String(cause), cause });
|
|
6
|
+
const fromPrivateDatabaseError = (error) => new FleetStoreError({ operation: error.operation, detail: String(error.cause), cause: error.cause });
|
|
6
7
|
const decodeRecord = (text) => Effect.try({
|
|
7
8
|
try: () => Schema.decodeUnknownSync(JobRecord)(JSON.parse(text)),
|
|
8
9
|
catch: storeError("decode")
|
|
@@ -11,45 +12,30 @@ const encodeRecord = (record) => JSON.stringify(Schema.encodeSync(JobRecord)(rec
|
|
|
11
12
|
const decodeRows = (operation, rows) => Effect.forEach(rows, (row) => Schema.decodeUnknownEffect(Schema.Struct({ record: Schema.String }))(row).pipe(Effect.mapError(storeError(operation)), Effect.flatMap(({ record }) => decodeRecord(record))));
|
|
12
13
|
export class JobStore {
|
|
13
14
|
#database;
|
|
14
|
-
#
|
|
15
|
+
#secureFiles;
|
|
15
16
|
path;
|
|
16
|
-
constructor(path,
|
|
17
|
+
constructor(path, opened) {
|
|
17
18
|
this.path = path;
|
|
18
|
-
this.#
|
|
19
|
-
this.#
|
|
20
|
-
try {
|
|
21
|
-
this.#database.exec(`
|
|
22
|
-
PRAGMA journal_mode = WAL;
|
|
23
|
-
CREATE TABLE IF NOT EXISTS jobs (
|
|
24
|
-
id TEXT PRIMARY KEY,
|
|
25
|
-
created_at INTEGER NOT NULL,
|
|
26
|
-
record TEXT NOT NULL
|
|
27
|
-
);
|
|
28
|
-
CREATE INDEX IF NOT EXISTS jobs_record_status_idx ON jobs (
|
|
29
|
-
CASE
|
|
30
|
-
WHEN json_valid(record)
|
|
31
|
-
THEN json_extract(record, '$.status')
|
|
32
|
-
END
|
|
33
|
-
);
|
|
34
|
-
`);
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
this.#database.close();
|
|
38
|
-
throw error;
|
|
39
|
-
}
|
|
19
|
+
this.#database = opened.database;
|
|
20
|
+
this.#secureFiles = opened.secureFiles.pipe(Effect.mapError(fromPrivateDatabaseError));
|
|
40
21
|
}
|
|
41
22
|
static open = Effect.fn("JobStore.open")(function* (path) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
23
|
+
const opened = yield* openPrivateSqlite(path, {
|
|
24
|
+
initialize: (database) => database.exec(`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS jobs (
|
|
26
|
+
id TEXT PRIMARY KEY,
|
|
27
|
+
created_at INTEGER NOT NULL,
|
|
28
|
+
record TEXT NOT NULL
|
|
29
|
+
);
|
|
30
|
+
CREATE INDEX IF NOT EXISTS jobs_record_status_idx ON jobs (
|
|
31
|
+
CASE
|
|
32
|
+
WHEN json_valid(record)
|
|
33
|
+
THEN json_extract(record, '$.status')
|
|
34
|
+
END
|
|
35
|
+
);
|
|
36
|
+
`)
|
|
37
|
+
}).pipe(Effect.mapError(fromPrivateDatabaseError));
|
|
38
|
+
return new JobStore(path, opened);
|
|
53
39
|
});
|
|
54
40
|
put = Effect.fn("JobStore.put")(function* (record) {
|
|
55
41
|
const database = this.#database;
|
|
@@ -196,11 +182,7 @@ export class JobStore {
|
|
|
196
182
|
return yield* decodeRows("listWorkers", rows);
|
|
197
183
|
});
|
|
198
184
|
secureFiles() {
|
|
199
|
-
|
|
200
|
-
const files = [this.path, `${this.path}-wal`, `${this.path}-shm`];
|
|
201
|
-
return Effect.forEach(files, (path) => fileSystem.exists(path).pipe(Effect.mapError(storeError("secure.exists")), Effect.flatMap((exists) => exists
|
|
202
|
-
? fileSystem.chmod(path, 0o600).pipe(Effect.mapError(storeError("secure.chmod")))
|
|
203
|
-
: Effect.void)), { discard: true });
|
|
185
|
+
return this.#secureFiles;
|
|
204
186
|
}
|
|
205
187
|
close() {
|
|
206
188
|
this.#database.close();
|
package/dist/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knpkv/herdr-fleet",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Typed job protocol and local authority runtime for Herdr fleets",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "knpkv",
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"./response": {
|
|
26
26
|
"types": "./dist/response.d.ts",
|
|
27
27
|
"import": "./dist/response.js"
|
|
28
|
+
},
|
|
29
|
+
"./sqlite": {
|
|
30
|
+
"types": "./dist/sqlite.d.ts",
|
|
31
|
+
"import": "./dist/sqlite.js"
|
|
28
32
|
}
|
|
29
33
|
},
|
|
30
34
|
"files": [
|
|
@@ -38,7 +42,8 @@
|
|
|
38
42
|
"node": ">=26"
|
|
39
43
|
},
|
|
40
44
|
"dependencies": {
|
|
41
|
-
"effect": "4.0.0"
|
|
45
|
+
"effect": "4.0.0",
|
|
46
|
+
"@knpkv/bounded-io": "^0.2.0"
|
|
42
47
|
},
|
|
43
48
|
"devDependencies": {
|
|
44
49
|
"@effect/platform-node": "4.0.0",
|