@knpkv/herdr-fleet 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 knpkv
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # `@knpkv/herdr-fleet`
2
+
3
+ Fleet job protocol and local decision authority for Herdr hosts.
4
+
5
+ The host that receives a job owns its durable SQLite record and every state transition. Risky work starts as `pending_approval`; approval is bound to the job's canonical hash and one-time nonce. Rejection and expiry clear that nonce. Safe work queues immediately. Recovery resumes queued jobs and expires stale approvals without moving authority to a coordinator.
6
+
7
+ `JobRecord.hash` persists a lowercase hex SHA-256 digest. Its canonical JSON input contains the host, actor, payload discriminant, and every identity field for that variant: `ref` for `nix.apply`; `repository`, `prompt`, `mode`, and the optional `channel` for `agent.delegate`; `session` and `message` for `agent.message`; no extra field for `nix.check` or `browser.mcp.recover`. Raw credentials and provider secrets never enter this hash or the persisted job payload.
8
+
9
+ The package exports:
10
+
11
+ - Schema contracts for configuration, jobs, status, and agent inventory
12
+ - `AgentConnectTarget`, the canonical sanitized Connect URL for one exact host and stable agent ID
13
+ - `JobStore`, the local SQLite persistence boundary
14
+ - `makeFleetService`, the approval and execution state machine
15
+ - typed validation, storage, approval, authorization, and operation errors
16
+
17
+ `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.
18
+
19
+ `HostConfiguration.machines` stores `{ host, nodeId }` for every fleet machine. Both fields must be unique. Tailscale adapters use the stable node ID as authority and treat the hostname only as a consistency check.
20
+
21
+ Callers provide `HostOperations`. This keeps Nix, Git, and Herdr command execution out of the protocol package.
22
+
23
+ When an `agent.delegate` worker starts, `JobRecord` persists its exact identity and matching `AgentConnectTarget` in the same transition. Records before start, including pending, rejected, expired, and failed-before-start jobs, contain neither field. Terminal observation time remains separate and appears only after that exact started job succeeds or fails.
24
+
25
+ `agent.delegate.prompt` and `agent.message.message` are capped at the exported `jobTextMaxLength` (16 KiB). Repository, session, and ref fields have smaller identifier/path bounds. These limits keep every schema-valid payload executable through the host command adapter without crossing the operating system's single-argument limit after JSON escaping and UTF-8 encoding.
@@ -0,0 +1,36 @@
1
+ import { Effect, FileSystem } from "effect";
2
+ import { FleetValidationError } from "./errors.js";
3
+ export declare const loadConfiguration: (path: string) => Effect.Effect<{
4
+ readonly host: string;
5
+ readonly repository: string;
6
+ readonly stateDirectory: string;
7
+ readonly crossHost: boolean;
8
+ readonly port: number;
9
+ readonly localPort: number;
10
+ readonly approvalPort: number;
11
+ readonly allowedUsers: readonly string[];
12
+ readonly approvalNodes: readonly string[];
13
+ readonly machines: readonly {
14
+ readonly host: string;
15
+ readonly nodeId: string;
16
+ }[];
17
+ readonly applyMachines: readonly string[];
18
+ readonly checkCommand: readonly string[];
19
+ readonly applyCommand: readonly string[] | null;
20
+ readonly browserMcpRecoverCommand: readonly string[] | null;
21
+ readonly coordinatorCommand: readonly string[];
22
+ readonly herdrCommand: string;
23
+ readonly tailscaleCommand: string;
24
+ readonly approvalTls: {
25
+ readonly certificatePath: string;
26
+ readonly privateKeyPath: string;
27
+ } | null;
28
+ readonly approvalHub: {
29
+ readonly host: string;
30
+ readonly nodeId: string;
31
+ readonly url: string;
32
+ };
33
+ readonly pushAllowedOrigins: readonly string[];
34
+ readonly pushSubject: string;
35
+ }, FleetValidationError, FileSystem.FileSystem>;
36
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAU,MAAM,QAAQ,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAGlD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CA4B7B,CAAA"}
package/dist/config.js ADDED
@@ -0,0 +1,21 @@
1
+ import { Effect, FileSystem, Schema } from "effect";
2
+ import { FleetValidationError } from "./errors.js";
3
+ import { HostConfiguration } from "./model.js";
4
+ export const loadConfiguration = Effect.fn("FleetConfiguration.load")(function* (path) {
5
+ const fileSystem = yield* FileSystem.FileSystem;
6
+ const text = yield* fileSystem.readFileString(path).pipe(Effect.mapError((cause) => new FleetValidationError({
7
+ detail: `cannot read ${path}: ${String(cause)}`
8
+ })));
9
+ const unknown = yield* Effect.try({
10
+ try: () => JSON.parse(text),
11
+ catch: (cause) => new FleetValidationError({
12
+ detail: `invalid JSON in ${path}: ${String(cause)}`
13
+ })
14
+ });
15
+ return yield* Schema.decodeUnknownEffect(HostConfiguration, {
16
+ onExcessProperty: "error"
17
+ })(unknown).pipe(Effect.mapError((error) => new FleetValidationError({
18
+ detail: `invalid fleet configuration: ${String(error)}`
19
+ })));
20
+ });
21
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE9C,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CACnE,QAAQ,CAAC,EAAC,IAAY;IACpB,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAA;IAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,CACtD,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACxB,IAAI,oBAAoB,CAAC;QACvB,MAAM,EAAE,eAAe,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE;KAChD,CAAC,CACH,CACF,CAAA;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;QAChC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAC3B,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,oBAAoB,CAAC;YACvB,MAAM,EAAE,mBAAmB,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE;SACpD,CAAC;KACL,CAAC,CAAA;IACF,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,EAAE;QAC1D,gBAAgB,EAAE,OAAO;KAC1B,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CACd,MAAM,CAAC,QAAQ,CACb,CAAC,KAAK,EAAE,EAAE,CACR,IAAI,oBAAoB,CAAC;QACvB,MAAM,EAAE,gCAAgC,MAAM,CAAC,KAAK,CAAC,EAAE;KACxD,CAAC,CACL,CACF,CAAA;AACH,CAAC,CACF,CAAA"}
@@ -0,0 +1,93 @@
1
+ import { Schema } from "effect";
2
+ declare const FleetValidationError_base: Schema.Class<FleetValidationError, Schema.TaggedStruct<"FleetValidationError", {
3
+ readonly detail: Schema.String;
4
+ }>, import("effect/Cause").YieldableError>;
5
+ export declare class FleetValidationError extends FleetValidationError_base {
6
+ }
7
+ declare const FleetAuthorizationError_base: Schema.Class<FleetAuthorizationError, Schema.TaggedStruct<"FleetAuthorizationError", {
8
+ readonly actor: Schema.String;
9
+ }>, import("effect/Cause").YieldableError>;
10
+ export declare class FleetAuthorizationError extends FleetAuthorizationError_base {
11
+ }
12
+ declare const FleetStoreError_base: Schema.Class<FleetStoreError, Schema.TaggedStruct<"FleetStoreError", {
13
+ readonly operation: Schema.String;
14
+ readonly detail: Schema.String;
15
+ readonly cause: Schema.Defect;
16
+ }>, import("effect/Cause").YieldableError>;
17
+ export declare class FleetStoreError extends FleetStoreError_base {
18
+ }
19
+ declare const FleetTransitionConflictError_base: Schema.Class<FleetTransitionConflictError, Schema.TaggedStruct<"FleetTransitionConflictError", {
20
+ readonly jobId: Schema.String;
21
+ }>, import("effect/Cause").YieldableError>;
22
+ export declare class FleetTransitionConflictError extends FleetTransitionConflictError_base {
23
+ }
24
+ declare const FleetJobConflictError_base: Schema.Class<FleetJobConflictError, Schema.TaggedStruct<"FleetJobConflictError", {
25
+ readonly jobId: Schema.String;
26
+ }>, import("effect/Cause").YieldableError>;
27
+ export declare class FleetJobConflictError extends FleetJobConflictError_base {
28
+ }
29
+ declare const FleetJobNotFoundError_base: Schema.Class<FleetJobNotFoundError, Schema.TaggedStruct<"FleetJobNotFoundError", {
30
+ readonly jobId: Schema.String;
31
+ }>, import("effect/Cause").YieldableError>;
32
+ export declare class FleetJobNotFoundError extends FleetJobNotFoundError_base {
33
+ }
34
+ declare const FleetApprovalError_base: Schema.Class<FleetApprovalError, Schema.TaggedStruct<"FleetApprovalError", {
35
+ readonly jobId: Schema.String;
36
+ readonly detail: Schema.String;
37
+ }>, import("effect/Cause").YieldableError>;
38
+ export declare class FleetApprovalError extends FleetApprovalError_base {
39
+ }
40
+ declare const FleetOperationError_base: Schema.Class<FleetOperationError, Schema.TaggedStruct<"FleetOperationError", {
41
+ readonly operation: Schema.String;
42
+ readonly detail: Schema.String;
43
+ readonly cause: Schema.Defect;
44
+ }>, import("effect/Cause").YieldableError>;
45
+ export declare class FleetOperationError extends FleetOperationError_base {
46
+ }
47
+ declare const FleetResponseBodyError_base: Schema.Class<FleetResponseBodyError, Schema.TaggedStruct<"FleetResponseBodyError", {
48
+ readonly cause: Schema.Defect;
49
+ readonly detail: Schema.String;
50
+ readonly reason: Schema.Literals<readonly ["decode", "too_large", "transport"]>;
51
+ }>, import("effect/Cause").YieldableError>;
52
+ export declare class FleetResponseBodyError extends FleetResponseBodyError_base {
53
+ }
54
+ declare const PeerPendingTransportError_base: Schema.Class<PeerPendingTransportError, Schema.TaggedStruct<"PeerPendingTransportError", {
55
+ readonly host: Schema.String;
56
+ readonly detail: Schema.String;
57
+ readonly cause: Schema.Defect;
58
+ }>, import("effect/Cause").YieldableError>;
59
+ export declare class PeerPendingTransportError extends PeerPendingTransportError_base {
60
+ }
61
+ declare const PeerPendingStatusError_base: Schema.Class<PeerPendingStatusError, Schema.TaggedStruct<"PeerPendingStatusError", {
62
+ readonly host: Schema.String;
63
+ readonly status: Schema.Number;
64
+ }>, import("effect/Cause").YieldableError>;
65
+ export declare class PeerPendingStatusError extends PeerPendingStatusError_base {
66
+ }
67
+ declare const PeerPendingDecodeError_base: Schema.Class<PeerPendingDecodeError, Schema.TaggedStruct<"PeerPendingDecodeError", {
68
+ readonly host: Schema.String;
69
+ readonly detail: Schema.String;
70
+ readonly cause: Schema.Defect;
71
+ }>, import("effect/Cause").YieldableError>;
72
+ export declare class PeerPendingDecodeError extends PeerPendingDecodeError_base {
73
+ }
74
+ declare const PeerPendingHostMismatchError_base: Schema.Class<PeerPendingHostMismatchError, Schema.TaggedStruct<"PeerPendingHostMismatchError", {
75
+ readonly expectedHost: Schema.String;
76
+ readonly receivedHost: Schema.String;
77
+ }>, import("effect/Cause").YieldableError>;
78
+ export declare class PeerPendingHostMismatchError extends PeerPendingHostMismatchError_base {
79
+ }
80
+ declare const PeerPendingTimeoutError_base: Schema.Class<PeerPendingTimeoutError, Schema.TaggedStruct<"PeerPendingTimeoutError", {
81
+ readonly host: Schema.String;
82
+ readonly timeoutMs: Schema.Number;
83
+ }>, import("effect/Cause").YieldableError>;
84
+ export declare class PeerPendingTimeoutError extends PeerPendingTimeoutError_base {
85
+ }
86
+ declare const PeerPendingUnavailableError_base: Schema.Class<PeerPendingUnavailableError, Schema.TaggedStruct<"PeerPendingUnavailableError", {
87
+ readonly host: Schema.String;
88
+ readonly reason: Schema.Literals<readonly ["offline", "unavailable"]>;
89
+ }>, import("effect/Cause").YieldableError>;
90
+ export declare class PeerPendingUnavailableError extends PeerPendingUnavailableError_base {
91
+ }
92
+ export {};
93
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;;;;AAE/B,qBAAa,oBAAqB,SAAQ,yBAGzC;CAAG;;;;AAEJ,qBAAa,uBAAwB,SAAQ,4BAG5C;CAAG;;;;;;AAEJ,qBAAa,eAAgB,SAAQ,oBAOpC;CAAG;;;;AAEJ,qBAAa,4BAA6B,SAAQ,iCAGjD;CAAG;;;;AAEJ,qBAAa,qBAAsB,SAAQ,0BAG1C;CAAG;;;;AAEJ,qBAAa,qBAAsB,SAAQ,0BAG1C;CAAG;;;;;AAEJ,qBAAa,kBAAmB,SAAQ,uBAGvC;CAAG;;;;;;AAEJ,qBAAa,mBAAoB,SAAQ,wBAOxC;CAAG;;;;;;AAEJ,qBAAa,sBAAuB,SAAQ,2BAO3C;CAAG;;;;;;AAEJ,qBAAa,yBAA0B,SAAQ,8BAO9C;CAAG;;;;;AAEJ,qBAAa,sBAAuB,SAAQ,2BAG3C;CAAG;;;;;;AAEJ,qBAAa,sBAAuB,SAAQ,2BAO3C;CAAG;;;;;AAEJ,qBAAa,4BAA6B,SAAQ,iCAGjD;CAAG;;;;;AAEJ,qBAAa,uBAAwB,SAAQ,4BAG5C;CAAG;;;;;AAEJ,qBAAa,2BAA4B,SAAQ,gCAMhD;CAAG"}
package/dist/errors.js ADDED
@@ -0,0 +1,55 @@
1
+ import { Schema } from "effect";
2
+ export class FleetValidationError extends Schema.TaggedError()("FleetValidationError", { detail: Schema.String }) {
3
+ }
4
+ export class FleetAuthorizationError extends Schema.TaggedError()("FleetAuthorizationError", { actor: Schema.String }) {
5
+ }
6
+ export class FleetStoreError extends Schema.TaggedError()("FleetStoreError", {
7
+ operation: Schema.String,
8
+ detail: Schema.String,
9
+ cause: Schema.Defect()
10
+ }) {
11
+ }
12
+ export class FleetTransitionConflictError extends Schema.TaggedError()("FleetTransitionConflictError", { jobId: Schema.String }) {
13
+ }
14
+ export class FleetJobConflictError extends Schema.TaggedError()("FleetJobConflictError", { jobId: Schema.String }) {
15
+ }
16
+ export class FleetJobNotFoundError extends Schema.TaggedError()("FleetJobNotFoundError", { jobId: Schema.String }) {
17
+ }
18
+ export class FleetApprovalError extends Schema.TaggedError()("FleetApprovalError", { jobId: Schema.String, detail: Schema.String }) {
19
+ }
20
+ export class FleetOperationError extends Schema.TaggedError()("FleetOperationError", {
21
+ operation: Schema.String,
22
+ detail: Schema.String,
23
+ cause: Schema.Defect()
24
+ }) {
25
+ }
26
+ export class FleetResponseBodyError extends Schema.TaggedError()("FleetResponseBodyError", {
27
+ cause: Schema.Defect(),
28
+ detail: Schema.String,
29
+ reason: Schema.Literals(["decode", "too_large", "transport"])
30
+ }) {
31
+ }
32
+ export class PeerPendingTransportError extends Schema.TaggedError()("PeerPendingTransportError", {
33
+ host: Schema.String,
34
+ detail: Schema.String,
35
+ cause: Schema.Defect()
36
+ }) {
37
+ }
38
+ export class PeerPendingStatusError extends Schema.TaggedError()("PeerPendingStatusError", { host: Schema.String, status: Schema.Number }) {
39
+ }
40
+ export class PeerPendingDecodeError extends Schema.TaggedError()("PeerPendingDecodeError", {
41
+ host: Schema.String,
42
+ detail: Schema.String,
43
+ cause: Schema.Defect()
44
+ }) {
45
+ }
46
+ export class PeerPendingHostMismatchError extends Schema.TaggedError()("PeerPendingHostMismatchError", { expectedHost: Schema.String, receivedHost: Schema.String }) {
47
+ }
48
+ export class PeerPendingTimeoutError extends Schema.TaggedError()("PeerPendingTimeoutError", { host: Schema.String, timeoutMs: Schema.Number }) {
49
+ }
50
+ export class PeerPendingUnavailableError extends Schema.TaggedError()("PeerPendingUnavailableError", {
51
+ host: Schema.String,
52
+ reason: Schema.Literals(["offline", "unavailable"])
53
+ }) {
54
+ }
55
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/hash.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { Crypto, Effect } from "effect";
2
+ import { FleetOperationError } from "./errors.js";
3
+ export declare const jobHash: (host: string, actor: string, payload: {
4
+ readonly kind: "nix.check";
5
+ } | {
6
+ readonly kind: "nix.apply";
7
+ readonly ref: string;
8
+ } | {
9
+ readonly kind: "agent.delegate";
10
+ readonly repository: string;
11
+ readonly prompt: string;
12
+ readonly mode: "consult" | "review" | "work";
13
+ readonly channel?: "coordinator_chat";
14
+ } | {
15
+ readonly kind: "agent.message";
16
+ readonly session: string;
17
+ readonly message: string;
18
+ } | {
19
+ readonly kind: "browser.mcp.recover";
20
+ }) => Effect.Effect<string, FleetOperationError, Crypto.Crypto>;
21
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAY,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAsBjD,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;+DAkBlB,CAAA"}
package/dist/hash.js ADDED
@@ -0,0 +1,28 @@
1
+ import { Crypto, Effect, Encoding } from "effect";
2
+ import { FleetOperationError } from "./errors.js";
3
+ const text = (value) => JSON.stringify(value);
4
+ const canonicalPayload = (payload) => {
5
+ switch (payload.kind) {
6
+ case "browser.mcp.recover":
7
+ return `{"kind":${text(payload.kind)}}`;
8
+ case "nix.check":
9
+ return `{"kind":${text(payload.kind)}}`;
10
+ case "nix.apply":
11
+ return `{"kind":${text(payload.kind)},"ref":${text(payload.ref)}}`;
12
+ case "agent.delegate":
13
+ return `{"channel":${payload.channel === undefined ? "null" : text(payload.channel)},"kind":${text(payload.kind)},"mode":${text(payload.mode)},"prompt":${text(payload.prompt)},"repository":${text(payload.repository)}}`;
14
+ case "agent.message":
15
+ return `{"kind":${text(payload.kind)},"message":${text(payload.message)},"session":${text(payload.session)}}`;
16
+ }
17
+ };
18
+ export const jobHash = Effect.fn("Fleet.jobHash")(function* (host, actor, payload) {
19
+ const cryptoService = yield* Crypto.Crypto;
20
+ const canonical = `{"actor":${text(actor)},"host":${text(host)},"payload":${canonicalPayload(payload)}}`;
21
+ const digest = yield* cryptoService.digest("SHA-256", new TextEncoder().encode(canonical)).pipe(Effect.mapError((cause) => new FleetOperationError({
22
+ cause,
23
+ detail: "could not hash fleet job payload",
24
+ operation: "fleet.job_hash"
25
+ })));
26
+ return Encoding.encodeHex(digest);
27
+ });
28
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAGjD,MAAM,IAAI,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AAE7D,MAAM,gBAAgB,GAAG,CAAC,OAAmB,EAAU,EAAE;IACvD,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,qBAAqB;YACxB,OAAO,WAAW,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAA;QACzC,KAAK,WAAW;YACd,OAAO,WAAW,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAA;QACzC,KAAK,WAAW;YACd,OAAO,WAAW,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAA;QACpE,KAAK,gBAAgB;YACnB,OAAO,cAAc,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,WACjF,IAAI,CAAC,OAAO,CAAC,IAAI,CACnB,WAAW,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAA;QAC5G,KAAK,eAAe;YAClB,OAAO,WAAW,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAA;IACjH,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,EACzD,IAAY,EACZ,KAAa,EACb,OAAmB;IAEnB,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;IAC1C,MAAM,SAAS,GAAG,YAAY,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,cAAc,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAA;IACxG,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAC7F,MAAM,CAAC,QAAQ,CACb,CAAC,KAAK,EAAE,EAAE,CACR,IAAI,mBAAmB,CAAC;QACtB,KAAK;QACL,MAAM,EAAE,kCAAkC;QAC1C,SAAS,EAAE,gBAAgB;KAC5B,CAAC,CACL,CACF,CAAA;IACD,OAAO,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;AACnC,CAAC,CAAC,CAAA"}
@@ -0,0 +1,8 @@
1
+ export * from "./config.js";
2
+ export * from "./errors.js";
3
+ export * from "./hash.js";
4
+ export * from "./model.js";
5
+ export * from "./response.js";
6
+ export * from "./service.js";
7
+ export * from "./store.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export * from "./config.js";
2
+ export * from "./errors.js";
3
+ export * from "./hash.js";
4
+ export * from "./model.js";
5
+ export * from "./response.js";
6
+ export * from "./service.js";
7
+ export * from "./store.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
@@ -0,0 +1,3 @@
1
+ export declare const fleetResponseBodyMaxBytes: number;
2
+ export declare const fleetJobRecordMaxBytes: number;
3
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,QAAc,CAAA;AACpD,eAAO,MAAM,sBAAsB,QAAoC,CAAA"}
package/dist/limits.js ADDED
@@ -0,0 +1,3 @@
1
+ export const fleetResponseBodyMaxBytes = 1024 * 1024;
2
+ export const fleetJobRecordMaxBytes = fleetResponseBodyMaxBytes - 1_024;
3
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,GAAG,IAAI,CAAA;AACpD,MAAM,CAAC,MAAM,sBAAsB,GAAG,yBAAyB,GAAG,KAAK,CAAA"}