@knpkv/herdr-approvals 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 +21 -0
- package/README.md +22 -0
- package/dist/activity-history.d.ts +36 -0
- package/dist/activity-history.d.ts.map +1 -0
- package/dist/activity-history.js +233 -0
- package/dist/activity-history.js.map +1 -0
- package/dist/approval-app-view.d.ts +26 -0
- package/dist/approval-app-view.d.ts.map +1 -0
- package/dist/approval-app-view.js +79 -0
- package/dist/approval-app-view.js.map +1 -0
- package/dist/approval-client.d.ts +2 -0
- package/dist/approval-client.d.ts.map +1 -0
- package/dist/approval-client.js +520 -0
- package/dist/approval-client.js.map +1 -0
- package/dist/approval-sw.js +28 -0
- package/dist/approval-sw.js.map +7 -0
- package/dist/approval-url.d.ts +37 -0
- package/dist/approval-url.d.ts.map +1 -0
- package/dist/approval-url.js +37 -0
- package/dist/approval-url.js.map +1 -0
- package/dist/approval.js +188 -0
- package/dist/approval.js.map +7 -0
- package/dist/auth.d.ts +10 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +14 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +47 -0
- package/dist/bin.js.map +1 -0
- package/dist/connect-entry.d.ts +2 -0
- package/dist/connect-entry.d.ts.map +1 -0
- package/dist/connect-entry.js +2 -0
- package/dist/connect-entry.js.map +1 -0
- package/dist/connect.js +188 -0
- package/dist/connect.js.map +7 -0
- package/dist/dashboard-model.d.ts +719 -0
- package/dist/dashboard-model.d.ts.map +1 -0
- package/dist/dashboard-model.js +83 -0
- package/dist/dashboard-model.js.map +1 -0
- package/dist/dashboard-view.d.ts +42 -0
- package/dist/dashboard-view.d.ts.map +1 -0
- package/dist/dashboard-view.js +194 -0
- package/dist/dashboard-view.js.map +1 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +12 -0
- package/dist/errors.js.map +1 -0
- package/dist/fleetctl-submission.d.ts +68 -0
- package/dist/fleetctl-submission.d.ts.map +1 -0
- package/dist/fleetctl-submission.js +31 -0
- package/dist/fleetctl-submission.js.map +1 -0
- package/dist/fleetctl.d.ts +22 -0
- package/dist/fleetctl.d.ts.map +1 -0
- package/dist/fleetctl.js +264 -0
- package/dist/fleetctl.js.map +1 -0
- package/dist/geist-latin-wght-normal.woff2 +0 -0
- package/dist/geist-mono-latin-wght-normal.woff2 +0 -0
- package/dist/http.d.ts +1512 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +1653 -0
- package/dist/http.js.map +1 -0
- package/dist/index.css +2 -0
- package/dist/index.css.map +7 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/config-path.d.ts +3 -0
- package/dist/internal/config-path.d.ts.map +1 -0
- package/dist/internal/config-path.js +3 -0
- package/dist/internal/config-path.js.map +1 -0
- package/dist/internal/dashboard-pending-state.d.ts +49 -0
- package/dist/internal/dashboard-pending-state.d.ts.map +1 -0
- package/dist/internal/dashboard-pending-state.js +98 -0
- package/dist/internal/dashboard-pending-state.js.map +1 -0
- package/dist/internal/fleet-follow.d.ts +54 -0
- package/dist/internal/fleet-follow.d.ts.map +1 -0
- package/dist/internal/fleet-follow.js +10 -0
- package/dist/internal/fleet-follow.js.map +1 -0
- package/dist/internal/fleet-request.d.ts +4 -0
- package/dist/internal/fleet-request.d.ts.map +1 -0
- package/dist/internal/fleet-request.js +11 -0
- package/dist/internal/fleet-request.js.map +1 -0
- package/dist/internal/html.d.ts +3 -0
- package/dist/internal/html.d.ts.map +1 -0
- package/dist/internal/html.js +8 -0
- package/dist/internal/html.js.map +1 -0
- package/dist/internal/websocket.d.ts +4 -0
- package/dist/internal/websocket.d.ts.map +1 -0
- package/dist/internal/websocket.js +12 -0
- package/dist/internal/websocket.js.map +1 -0
- package/dist/model.d.ts +40 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +53 -0
- package/dist/model.js.map +1 -0
- package/dist/operations.d.ts +72 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +284 -0
- package/dist/operations.js.map +1 -0
- package/dist/push-policy.d.ts +3 -0
- package/dist/push-policy.d.ts.map +1 -0
- package/dist/push-policy.js +3 -0
- package/dist/push-policy.js.map +1 -0
- package/dist/push-sender.d.ts +18 -0
- package/dist/push-sender.d.ts.map +1 -0
- package/dist/push-sender.js +33 -0
- package/dist/push-sender.js.map +1 -0
- package/dist/push-subscription.d.ts +17 -0
- package/dist/push-subscription.d.ts.map +1 -0
- package/dist/push-subscription.js +62 -0
- package/dist/push-subscription.js.map +1 -0
- package/dist/push-worker.d.ts +20 -0
- package/dist/push-worker.d.ts.map +1 -0
- package/dist/push-worker.js +32 -0
- package/dist/push-worker.js.map +1 -0
- package/dist/pwa.d.ts +39 -0
- package/dist/pwa.d.ts.map +1 -0
- package/dist/pwa.js +51 -0
- package/dist/pwa.js.map +1 -0
- package/dist/shell-view.d.ts +20 -0
- package/dist/shell-view.d.ts.map +1 -0
- package/dist/shell-view.js +86 -0
- package/dist/shell-view.js.map +1 -0
- package/dist/store.d.ts +46 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +291 -0
- package/dist/store.js.map +1 -0
- package/dist/work-checkpoint.d.ts +107 -0
- package/dist/work-checkpoint.d.ts.map +1 -0
- package/dist/work-checkpoint.js +37 -0
- package/dist/work-checkpoint.js.map +1 -0
- package/package.json +90 -0
package/dist/pwa.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Effect, Result, Schema } from "effect";
|
|
2
|
+
import { ApprovalNotificationCandidate } from "./model.js";
|
|
3
|
+
export class ApprovalDeepLinkError extends Schema.TaggedError()("ApprovalDeepLinkError", { cause: Schema.Defect() }) {
|
|
4
|
+
}
|
|
5
|
+
export const showApprovalNotification = async (registration, setBadge, payload) => {
|
|
6
|
+
const pendingCount = payload.pendingCount;
|
|
7
|
+
const badge = pendingCount === null
|
|
8
|
+
? Result.succeed(undefined)
|
|
9
|
+
: await Effect.runPromise(Effect.result(Effect.tryPromise(() => setBadge(pendingCount))));
|
|
10
|
+
await registration.showNotification(`Approval needed on ${payload.host}`, {
|
|
11
|
+
badge: "/assets/approval-icon.svg",
|
|
12
|
+
body: `Job ${payload.jobId} is waiting for approval.`,
|
|
13
|
+
data: payload,
|
|
14
|
+
icon: "/assets/approval-icon.svg",
|
|
15
|
+
tag: `approval:${payload.host}:${payload.jobId}`
|
|
16
|
+
});
|
|
17
|
+
if (Result.isFailure(badge))
|
|
18
|
+
throw badge.failure;
|
|
19
|
+
};
|
|
20
|
+
export const approvalDeepLink = (canonicalUrl, payload) => {
|
|
21
|
+
const url = new URL(canonicalUrl);
|
|
22
|
+
url.searchParams.set("tab", "approvals");
|
|
23
|
+
url.searchParams.set("approvalHost", payload.host);
|
|
24
|
+
url.searchParams.set("approvalJob", payload.jobId);
|
|
25
|
+
return url.href;
|
|
26
|
+
};
|
|
27
|
+
export const readApprovalDeepLink = (search) => {
|
|
28
|
+
const parameters = new URLSearchParams(search);
|
|
29
|
+
const host = parameters.get("approvalHost");
|
|
30
|
+
const jobId = parameters.get("approvalJob");
|
|
31
|
+
if (host === null && jobId === null)
|
|
32
|
+
return Result.succeed(null);
|
|
33
|
+
const decoded = Schema.decodeUnknownResult(ApprovalNotificationCandidate)({ host, jobId });
|
|
34
|
+
return Result.isFailure(decoded)
|
|
35
|
+
? Result.fail(new ApprovalDeepLinkError({ cause: decoded.failure }))
|
|
36
|
+
: Result.succeed(decoded.success);
|
|
37
|
+
};
|
|
38
|
+
export const matchesApprovalDeepLink = (dataset, target) => dataset.approvalHost?.toLowerCase() === target.host.toLowerCase() &&
|
|
39
|
+
dataset.approvalJob === target.jobId;
|
|
40
|
+
export const handleNotificationClick = async (clients, deepLinkUrl) => {
|
|
41
|
+
const windows = await clients.matchAll();
|
|
42
|
+
const target = new URL(deepLinkUrl);
|
|
43
|
+
const canonical = windows.find((client) => new URL(client.url).origin === target.origin);
|
|
44
|
+
if (canonical !== undefined) {
|
|
45
|
+
await canonical.navigate(deepLinkUrl);
|
|
46
|
+
await canonical.focus();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
await clients.openWindow(deepLinkUrl);
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=pwa.js.map
|
package/dist/pwa.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pwa.js","sourceRoot":"","sources":["../src/pwa.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from "react";
|
|
2
|
+
export type FleetShellTab = "approvals" | "connect" | "work";
|
|
3
|
+
export type FleetShortcut = {
|
|
4
|
+
readonly _tag: "focus_agent_search";
|
|
5
|
+
} | {
|
|
6
|
+
readonly _tag: "select_tab";
|
|
7
|
+
readonly tab: FleetShellTab;
|
|
8
|
+
};
|
|
9
|
+
export declare const fleetShortcutFor: ({ editable, key, modified }: {
|
|
10
|
+
readonly editable: boolean;
|
|
11
|
+
readonly key: string;
|
|
12
|
+
readonly modified: boolean;
|
|
13
|
+
}) => FleetShortcut | null;
|
|
14
|
+
export declare const FleetShell: ({ approvals, connect, hostCount, work }: {
|
|
15
|
+
readonly approvals: ReactNode;
|
|
16
|
+
readonly connect: ReactNode;
|
|
17
|
+
readonly hostCount: number;
|
|
18
|
+
readonly work: ReactNode;
|
|
19
|
+
}) => ReactElement;
|
|
20
|
+
//# sourceMappingURL=shell-view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-view.d.ts","sourceRoot":"","sources":["../src/shell-view.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAE9E,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAA;AAE5D,MAAM,MAAM,aAAa,GACvB;IAAE,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAA;CAAE,CAAA;AAExG,eAAO,MAAM,gBAAgB,gCAI1B;IACD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC3B,KAAG,aAAa,GAAG,IAcnB,CAAA;AA6BD,eAAO,MAAM,UAAU,4CAKpB;IACD,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CACzB,KAAG,YAuFH,CAAA"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { StateLabel, Tabs, Text } from "@knpkv/rly/primitives";
|
|
3
|
+
import { Predicate } from "effect";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
export const fleetShortcutFor = ({ editable, key, modified }) => {
|
|
6
|
+
if (editable || modified)
|
|
7
|
+
return null;
|
|
8
|
+
switch (key) {
|
|
9
|
+
case "1":
|
|
10
|
+
return { _tag: "select_tab", tab: "approvals" };
|
|
11
|
+
case "2":
|
|
12
|
+
return { _tag: "select_tab", tab: "connect" };
|
|
13
|
+
case "3":
|
|
14
|
+
return { _tag: "select_tab", tab: "work" };
|
|
15
|
+
case "/":
|
|
16
|
+
return { _tag: "focus_agent_search" };
|
|
17
|
+
default:
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const isEditableTarget = (target) => {
|
|
22
|
+
if (!Predicate.hasProperty(target, "nodeName") || !Predicate.isString(target.nodeName))
|
|
23
|
+
return false;
|
|
24
|
+
if (target.nodeName === "INPUT" || target.nodeName === "TEXTAREA" || target.nodeName === "SELECT")
|
|
25
|
+
return true;
|
|
26
|
+
if (Predicate.hasProperty(target, "isContentEditable") && target.isContentEditable === true)
|
|
27
|
+
return true;
|
|
28
|
+
return Predicate.hasProperty(target, "role") && target.role === "textbox";
|
|
29
|
+
};
|
|
30
|
+
const isShellTab = (value) => value === "approvals" || value === "connect" || value === "work";
|
|
31
|
+
const FleetKeyRail = () => (_jsxs("nav", { className: "fleet-key-rail", "aria-label": "Keyboard shortcuts", children: [_jsxs("span", { children: [_jsx("kbd", { children: "1" }), " Approvals"] }), _jsxs("span", { children: [_jsx("kbd", { children: "2" }), " Connect"] }), _jsxs("span", { children: [_jsx("kbd", { children: "3" }), " Work"] }), _jsxs("span", { children: [_jsx("kbd", { children: "/" }), " Agent search"] })] }));
|
|
32
|
+
export const FleetShell = ({ approvals, connect, hostCount, work }) => {
|
|
33
|
+
const [tab, setTab] = useState("approvals");
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const candidate = new URLSearchParams(window.location.search).get("tab");
|
|
36
|
+
if (candidate !== null && isShellTab(candidate))
|
|
37
|
+
setTab(candidate);
|
|
38
|
+
}, []);
|
|
39
|
+
const selectTab = (value) => {
|
|
40
|
+
if (!isShellTab(value))
|
|
41
|
+
return;
|
|
42
|
+
setTab(value);
|
|
43
|
+
const url = new URL(window.location.href);
|
|
44
|
+
url.searchParams.set("tab", value);
|
|
45
|
+
window.history.replaceState(null, "", url);
|
|
46
|
+
};
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const handleShortcut = (event) => {
|
|
49
|
+
const shortcut = fleetShortcutFor({
|
|
50
|
+
editable: isEditableTarget(event.target),
|
|
51
|
+
key: event.key,
|
|
52
|
+
modified: event.altKey || event.ctrlKey || event.metaKey
|
|
53
|
+
});
|
|
54
|
+
if (shortcut === null)
|
|
55
|
+
return;
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
if (shortcut._tag === "select_tab") {
|
|
58
|
+
selectTab(shortcut.tab);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
selectTab("connect");
|
|
62
|
+
window.requestAnimationFrame(() => document.getElementById("connect-agent-search")?.focus());
|
|
63
|
+
};
|
|
64
|
+
window.addEventListener("keydown", handleShortcut);
|
|
65
|
+
return () => window.removeEventListener("keydown", handleShortcut);
|
|
66
|
+
});
|
|
67
|
+
const items = [
|
|
68
|
+
{
|
|
69
|
+
content: (_jsxs(_Fragment, { children: [_jsx(FleetKeyRail, {}), approvals] })),
|
|
70
|
+
label: "Approvals",
|
|
71
|
+
value: "approvals"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
content: (_jsxs(_Fragment, { children: [_jsx(FleetKeyRail, {}), connect] })),
|
|
75
|
+
label: "Connect",
|
|
76
|
+
value: "connect"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
content: (_jsxs(_Fragment, { children: [_jsx(FleetKeyRail, {}), work] })),
|
|
80
|
+
label: "Work",
|
|
81
|
+
value: "work"
|
|
82
|
+
}
|
|
83
|
+
];
|
|
84
|
+
return (_jsxs("div", { className: "fleet-shell", children: [_jsxs("header", { className: "fleet-shell-masthead", children: [_jsxs("div", { className: "fleet-shell-brand", children: [_jsx("span", { "aria-hidden": "true", className: "fleet-shell-mark", children: "H" }), _jsxs("span", { children: [_jsx(Text, { as: "strong", variant: "label", children: "Herdr" }), _jsx(Text, { tone: "secondary", variant: "meta", children: "Fleet control" })] })] }), _jsx(StateLabel, { label: `${String(hostCount)} configured hosts`, size: "compact", tone: "positive" })] }), _jsx("main", { className: "fleet-shell-main", children: _jsx(Tabs, { "aria-label": "Fleet applications", items: items, onValueChange: selectTab, size: "large", value: tab }) })] }));
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=shell-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-view.js","sourceRoot":"","sources":["../src/shell-view.tsx"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
2
|
+
import { ApprovalAppStoreError } from "./errors.js";
|
|
3
|
+
import { type VapidKeyPair as VapidKeyPairType } from "./model.js";
|
|
4
|
+
export declare class ApprovalAppStore {
|
|
5
|
+
#private;
|
|
6
|
+
readonly path: string;
|
|
7
|
+
private constructor();
|
|
8
|
+
static readonly open: (path: string) => Effect.Effect<ApprovalAppStore, ApprovalAppStoreError, FileSystem.FileSystem | Path.Path>;
|
|
9
|
+
readonly getOrCreateVapidKeys: (this: ApprovalAppStore, generate: () => VapidKeyPairType) => Effect.Effect<{
|
|
10
|
+
readonly privateKey: string;
|
|
11
|
+
readonly publicKey: string;
|
|
12
|
+
}, ApprovalAppStoreError, never>;
|
|
13
|
+
readonly putSubscription: (this: ApprovalAppStore, subscription: {
|
|
14
|
+
readonly endpoint: string;
|
|
15
|
+
readonly expirationTime: number | null;
|
|
16
|
+
readonly keys: {
|
|
17
|
+
readonly auth: string;
|
|
18
|
+
readonly p256dh: string;
|
|
19
|
+
};
|
|
20
|
+
}, owner: string) => Effect.Effect<void, ApprovalAppStoreError, never>;
|
|
21
|
+
readonly deleteSubscriptionPrivileged: (this: ApprovalAppStore, endpoint: string) => Effect.Effect<void, ApprovalAppStoreError, never>;
|
|
22
|
+
readonly deleteOwnedSubscription: (this: ApprovalAppStore, endpoint: string, owner: string) => Effect.Effect<boolean, ApprovalAppStoreError, never>;
|
|
23
|
+
readonly hasSubscription: (this: ApprovalAppStore, endpoint: string, owner: string) => Effect.Effect<boolean, ApprovalAppStoreError, never>;
|
|
24
|
+
readonly recordDeliveryIfSubscriptionMatches: (this: ApprovalAppStore, host: string, jobId: string, subscription: {
|
|
25
|
+
readonly endpoint: string;
|
|
26
|
+
readonly expirationTime: number | null;
|
|
27
|
+
readonly keys: {
|
|
28
|
+
readonly auth: string;
|
|
29
|
+
readonly p256dh: string;
|
|
30
|
+
};
|
|
31
|
+
}, owner: string, deliveredAt: number, countComplete?: any) => Effect.Effect<boolean, ApprovalAppStoreError, never>;
|
|
32
|
+
readonly listSubscriptions: (this: ApprovalAppStore) => Effect.Effect<{
|
|
33
|
+
readonly endpoint: string;
|
|
34
|
+
readonly expirationTime: number | null;
|
|
35
|
+
readonly keys: {
|
|
36
|
+
readonly auth: string;
|
|
37
|
+
readonly p256dh: string;
|
|
38
|
+
};
|
|
39
|
+
readonly owner?: string;
|
|
40
|
+
}[], ApprovalAppStoreError, never>;
|
|
41
|
+
readonly hasDelivered: (this: ApprovalAppStore, host: string, jobId: string, endpoint: string, deliveredAfter?: any, countComplete?: any) => Effect.Effect<boolean, ApprovalAppStoreError, never>;
|
|
42
|
+
readonly recordDelivery: (this: ApprovalAppStore, host: string, jobId: string, endpoint: string, deliveredAt: number, countComplete?: any) => Effect.Effect<void, ApprovalAppStoreError, never>;
|
|
43
|
+
private secureFiles;
|
|
44
|
+
close(): void;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAU,MAAM,QAAQ,CAAA;AAEzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAIL,KAAK,YAAY,IAAI,gBAAgB,EACtC,MAAM,YAAY,CAAA;AA4GnB,qBAAa,gBAAgB;;IAG3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB,OAAO,eAwCN;IAED,MAAM,CAAC,QAAQ,CAAC,IAAI,8GAgBlB;IAEF,QAAQ,CAAC,oBAAoB,2CAEX,gBAAgB;;;qCA8BhC;IAEF,QAAQ,CAAC,eAAe;;;;;;;2EAkCtB;IAEF,QAAQ,CAAC,4BAA4B,kGAmBnC;IAEF,QAAQ,CAAC,uBAAuB,oHA+B9B;IAEF,QAAQ,CAAC,eAAe,oHAoBtB;IAEF,QAAQ,CAAC,mCAAmC;;;;;;;wHAuC1C;IAEF,QAAQ,CAAC,iBAAiB;;;;;;;;uCAcxB;IAEF,QAAQ,CAAC,YAAY,6KA8BnB;IAEF,QAAQ,CAAC,cAAc,yKAuBrB;IAEF,OAAO,CAAC,WAAW;IAoBnB,KAAK,IAAI,IAAI,CAEZ;CACF"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
2
|
+
import { DatabaseSync } from "node:sqlite";
|
|
3
|
+
import { ApprovalAppStoreError } from "./errors.js";
|
|
4
|
+
import { PushSubscriptionRecord, VapidKeyPair } from "./model.js";
|
|
5
|
+
const storeError = (operation) => (cause) => new ApprovalAppStoreError({ operation, detail: String(cause), cause });
|
|
6
|
+
const immediateTransaction = (database, operation) => {
|
|
7
|
+
database.exec("BEGIN IMMEDIATE");
|
|
8
|
+
try {
|
|
9
|
+
const value = operation();
|
|
10
|
+
database.exec("COMMIT");
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
database.exec("ROLLBACK");
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const decodeJson = (operation, schema, encoded) => Effect.try({
|
|
19
|
+
try: () => Schema.decodeUnknownSync(schema)(JSON.parse(encoded)),
|
|
20
|
+
catch: storeError(operation)
|
|
21
|
+
});
|
|
22
|
+
const StoredJsonRow = Schema.Struct({ record: Schema.String });
|
|
23
|
+
const StoredCountRow = Schema.Struct({ count: Schema.Number });
|
|
24
|
+
const StoredPushSubscription = Schema.Struct({
|
|
25
|
+
...PushSubscriptionRecord.fields,
|
|
26
|
+
owner: Schema.optionalKey(Schema.String.check(Schema.isNonEmpty(), Schema.isMaxLength(256)))
|
|
27
|
+
});
|
|
28
|
+
const subscriptionMatches = (stored, subscription, owner) => stored.owner === owner &&
|
|
29
|
+
stored.expirationTime === subscription.expirationTime &&
|
|
30
|
+
stored.keys.auth === subscription.keys.auth &&
|
|
31
|
+
stored.keys.p256dh === subscription.keys.p256dh;
|
|
32
|
+
const recordDelivery = (database, host, jobId, endpoint, deliveredAt, countComplete) => {
|
|
33
|
+
const encodedCountComplete = countComplete ? 1 : 0;
|
|
34
|
+
const updated = database
|
|
35
|
+
.prepare(`UPDATE push_deliveries
|
|
36
|
+
SET count_complete = CASE
|
|
37
|
+
WHEN delivered_at < ? THEN ?
|
|
38
|
+
WHEN delivered_at = ? THEN max(count_complete, ?)
|
|
39
|
+
ELSE count_complete
|
|
40
|
+
END,
|
|
41
|
+
delivered_at = max(delivered_at, ?)
|
|
42
|
+
WHERE host = ? COLLATE NOCASE AND job_id = ? AND endpoint = ?`)
|
|
43
|
+
.run(deliveredAt, encodedCountComplete, deliveredAt, encodedCountComplete, deliveredAt, host, jobId, endpoint);
|
|
44
|
+
if (updated.changes !== 0 && updated.changes !== 0n)
|
|
45
|
+
return;
|
|
46
|
+
database
|
|
47
|
+
.prepare(`INSERT INTO push_deliveries (host, job_id, endpoint, delivered_at, count_complete)
|
|
48
|
+
VALUES (?, ?, ?, ?, ?)
|
|
49
|
+
ON CONFLICT(host, job_id, endpoint) DO UPDATE SET
|
|
50
|
+
count_complete = CASE
|
|
51
|
+
WHEN excluded.delivered_at > push_deliveries.delivered_at
|
|
52
|
+
THEN excluded.count_complete
|
|
53
|
+
WHEN excluded.delivered_at = push_deliveries.delivered_at
|
|
54
|
+
THEN max(push_deliveries.count_complete, excluded.count_complete)
|
|
55
|
+
ELSE push_deliveries.count_complete
|
|
56
|
+
END,
|
|
57
|
+
delivered_at = max(push_deliveries.delivered_at, excluded.delivered_at)`)
|
|
58
|
+
.run(host, jobId, endpoint, deliveredAt, encodedCountComplete);
|
|
59
|
+
};
|
|
60
|
+
const decodeRow = (operation, schema, row) => Schema.decodeUnknownEffect(StoredJsonRow)(row).pipe(Effect.mapError(storeError(operation)), Effect.flatMap(({ record }) => decodeJson(operation, schema, record)));
|
|
61
|
+
export class ApprovalAppStore {
|
|
62
|
+
#database;
|
|
63
|
+
#fileSystem;
|
|
64
|
+
path;
|
|
65
|
+
constructor(path, fileSystem) {
|
|
66
|
+
this.path = path;
|
|
67
|
+
this.#fileSystem = fileSystem;
|
|
68
|
+
this.#database = new DatabaseSync(path);
|
|
69
|
+
try {
|
|
70
|
+
this.#database.exec(`
|
|
71
|
+
PRAGMA journal_mode = WAL;
|
|
72
|
+
CREATE TABLE IF NOT EXISTS vapid_keys (
|
|
73
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
74
|
+
record TEXT NOT NULL
|
|
75
|
+
);
|
|
76
|
+
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
|
77
|
+
endpoint TEXT PRIMARY KEY,
|
|
78
|
+
record TEXT NOT NULL
|
|
79
|
+
);
|
|
80
|
+
CREATE TABLE IF NOT EXISTS push_deliveries (
|
|
81
|
+
host TEXT NOT NULL,
|
|
82
|
+
job_id TEXT NOT NULL,
|
|
83
|
+
endpoint TEXT NOT NULL,
|
|
84
|
+
delivered_at INTEGER NOT NULL,
|
|
85
|
+
count_complete INTEGER NOT NULL DEFAULT 1,
|
|
86
|
+
PRIMARY KEY (host, job_id, endpoint)
|
|
87
|
+
);
|
|
88
|
+
`);
|
|
89
|
+
const { count } = Schema.decodeUnknownSync(StoredCountRow)(this.#database
|
|
90
|
+
.prepare("SELECT count(*) AS count FROM pragma_table_info('push_deliveries') WHERE name = 'count_complete'")
|
|
91
|
+
.get());
|
|
92
|
+
if (count === 0) {
|
|
93
|
+
this.#database.exec("ALTER TABLE push_deliveries ADD COLUMN count_complete INTEGER NOT NULL DEFAULT 1");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
this.#database.close();
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
static open = Effect.fn("ApprovalAppStore.open")(function* (path) {
|
|
102
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
103
|
+
const paths = yield* Path.Path;
|
|
104
|
+
const directory = paths.dirname(path);
|
|
105
|
+
yield* fileSystem.makeDirectory(directory, { recursive: true, mode: 0o700 }).pipe(Effect.mapError(storeError("open.makeDirectory")));
|
|
106
|
+
yield* fileSystem.chmod(directory, 0o700).pipe(Effect.mapError(storeError("open.secureDirectory")));
|
|
107
|
+
const store = yield* Effect.try({
|
|
108
|
+
try: () => new ApprovalAppStore(path, fileSystem),
|
|
109
|
+
catch: storeError("open.database")
|
|
110
|
+
});
|
|
111
|
+
yield* store.secureFiles();
|
|
112
|
+
return store;
|
|
113
|
+
});
|
|
114
|
+
getOrCreateVapidKeys = Effect.fn("ApprovalAppStore.getOrCreateVapidKeys")(function* (generate) {
|
|
115
|
+
const database = this.#database;
|
|
116
|
+
const existing = yield* Effect.try({
|
|
117
|
+
try: () => database.prepare("SELECT record FROM vapid_keys WHERE id = 1").get(),
|
|
118
|
+
catch: storeError("vapid.get")
|
|
119
|
+
});
|
|
120
|
+
if (existing !== undefined) {
|
|
121
|
+
return yield* decodeRow("vapid.decode", VapidKeyPair, existing);
|
|
122
|
+
}
|
|
123
|
+
const generated = yield* Effect.try({
|
|
124
|
+
try: generate,
|
|
125
|
+
catch: storeError("vapid.generate")
|
|
126
|
+
});
|
|
127
|
+
const keys = yield* Schema.decodeUnknownEffect(VapidKeyPair)(generated).pipe(Effect.mapError(storeError("vapid.validate")));
|
|
128
|
+
yield* Effect.try({
|
|
129
|
+
try: () => database
|
|
130
|
+
.prepare("INSERT OR IGNORE INTO vapid_keys (id, record) VALUES (1, ?)")
|
|
131
|
+
.run(JSON.stringify(keys)),
|
|
132
|
+
catch: storeError("vapid.put")
|
|
133
|
+
});
|
|
134
|
+
yield* this.secureFiles();
|
|
135
|
+
const persisted = yield* Effect.try({
|
|
136
|
+
try: () => database.prepare("SELECT record FROM vapid_keys WHERE id = 1").get(),
|
|
137
|
+
catch: storeError("vapid.get.persisted")
|
|
138
|
+
});
|
|
139
|
+
return yield* decodeRow("vapid.decode.persisted", VapidKeyPair, persisted);
|
|
140
|
+
});
|
|
141
|
+
putSubscription = Effect.fn("ApprovalAppStore.putSubscription")(function* (subscription, owner) {
|
|
142
|
+
const database = this.#database;
|
|
143
|
+
yield* Effect.try({
|
|
144
|
+
try: () => {
|
|
145
|
+
immediateTransaction(database, () => {
|
|
146
|
+
const current = database
|
|
147
|
+
.prepare("SELECT record FROM push_subscriptions WHERE endpoint = ?")
|
|
148
|
+
.get(subscription.endpoint);
|
|
149
|
+
const unchanged = current !== undefined && subscriptionMatches(Schema.decodeUnknownSync(StoredPushSubscription)(JSON.parse(Schema.decodeUnknownSync(StoredJsonRow)(current).record)), subscription, owner);
|
|
150
|
+
if (!unchanged) {
|
|
151
|
+
database
|
|
152
|
+
.prepare("DELETE FROM push_deliveries WHERE endpoint = ?")
|
|
153
|
+
.run(subscription.endpoint);
|
|
154
|
+
}
|
|
155
|
+
database
|
|
156
|
+
.prepare("INSERT INTO push_subscriptions (endpoint, record) VALUES (?, ?) ON CONFLICT(endpoint) DO UPDATE SET record = excluded.record")
|
|
157
|
+
.run(subscription.endpoint, JSON.stringify({ ...subscription, owner }));
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
catch: storeError("subscription.put")
|
|
161
|
+
});
|
|
162
|
+
yield* this.secureFiles();
|
|
163
|
+
});
|
|
164
|
+
deleteSubscriptionPrivileged = Effect.fn("ApprovalAppStore.deleteSubscriptionPrivileged")(function* (endpoint) {
|
|
165
|
+
const database = this.#database;
|
|
166
|
+
yield* Effect.try({
|
|
167
|
+
try: () => {
|
|
168
|
+
immediateTransaction(database, () => {
|
|
169
|
+
database
|
|
170
|
+
.prepare("DELETE FROM push_deliveries WHERE endpoint = ?")
|
|
171
|
+
.run(endpoint);
|
|
172
|
+
database
|
|
173
|
+
.prepare("DELETE FROM push_subscriptions WHERE endpoint = ?")
|
|
174
|
+
.run(endpoint);
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
catch: storeError("subscription.delete")
|
|
178
|
+
});
|
|
179
|
+
yield* this.secureFiles();
|
|
180
|
+
});
|
|
181
|
+
deleteOwnedSubscription = Effect.fn("ApprovalAppStore.deleteOwnedSubscription")(function* (endpoint, owner) {
|
|
182
|
+
const database = this.#database;
|
|
183
|
+
const deleted = yield* Effect.try({
|
|
184
|
+
try: () => {
|
|
185
|
+
return immediateTransaction(database, () => {
|
|
186
|
+
const row = database
|
|
187
|
+
.prepare("SELECT record FROM push_subscriptions WHERE endpoint = ?")
|
|
188
|
+
.get(endpoint);
|
|
189
|
+
if (row === undefined)
|
|
190
|
+
return false;
|
|
191
|
+
const encoded = Schema.decodeUnknownSync(StoredJsonRow)(row);
|
|
192
|
+
const stored = Schema.decodeUnknownSync(StoredPushSubscription)(JSON.parse(encoded.record));
|
|
193
|
+
if (stored.owner !== owner)
|
|
194
|
+
return false;
|
|
195
|
+
database
|
|
196
|
+
.prepare("DELETE FROM push_deliveries WHERE endpoint = ?")
|
|
197
|
+
.run(endpoint);
|
|
198
|
+
database
|
|
199
|
+
.prepare("DELETE FROM push_subscriptions WHERE endpoint = ?")
|
|
200
|
+
.run(endpoint);
|
|
201
|
+
return true;
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
catch: storeError("subscription.deleteOwned")
|
|
205
|
+
});
|
|
206
|
+
if (deleted)
|
|
207
|
+
yield* this.secureFiles();
|
|
208
|
+
return deleted;
|
|
209
|
+
});
|
|
210
|
+
hasSubscription = Effect.fn("ApprovalAppStore.hasSubscription")(function* (endpoint, owner) {
|
|
211
|
+
const database = this.#database;
|
|
212
|
+
const row = yield* Effect.try({
|
|
213
|
+
try: () => database
|
|
214
|
+
.prepare("SELECT record FROM push_subscriptions WHERE endpoint = ?")
|
|
215
|
+
.get(endpoint),
|
|
216
|
+
catch: storeError("subscription.has")
|
|
217
|
+
});
|
|
218
|
+
if (row === undefined)
|
|
219
|
+
return false;
|
|
220
|
+
const stored = yield* decodeRow("subscription.has.decode", StoredPushSubscription, row);
|
|
221
|
+
return stored.owner === owner;
|
|
222
|
+
});
|
|
223
|
+
recordDeliveryIfSubscriptionMatches = Effect.fn("ApprovalAppStore.recordDeliveryIfSubscriptionMatches")(function* (host, jobId, subscription, owner, deliveredAt, countComplete = true) {
|
|
224
|
+
const database = this.#database;
|
|
225
|
+
const normalizedHost = host.toLowerCase();
|
|
226
|
+
const recorded = yield* Effect.try({
|
|
227
|
+
try: () => immediateTransaction(database, () => {
|
|
228
|
+
const row = database
|
|
229
|
+
.prepare("SELECT record FROM push_subscriptions WHERE endpoint = ?")
|
|
230
|
+
.get(subscription.endpoint);
|
|
231
|
+
if (row === undefined)
|
|
232
|
+
return false;
|
|
233
|
+
const encoded = Schema.decodeUnknownSync(StoredJsonRow)(row);
|
|
234
|
+
const stored = Schema.decodeUnknownSync(StoredPushSubscription)(JSON.parse(encoded.record));
|
|
235
|
+
if (!subscriptionMatches(stored, subscription, owner))
|
|
236
|
+
return false;
|
|
237
|
+
recordDelivery(database, normalizedHost, jobId, subscription.endpoint, deliveredAt, countComplete);
|
|
238
|
+
return true;
|
|
239
|
+
}),
|
|
240
|
+
catch: storeError("delivery.recordIfSubscriptionMatches")
|
|
241
|
+
});
|
|
242
|
+
if (recorded)
|
|
243
|
+
yield* this.secureFiles();
|
|
244
|
+
return recorded;
|
|
245
|
+
});
|
|
246
|
+
listSubscriptions = Effect.fn("ApprovalAppStore.listSubscriptions")(function* () {
|
|
247
|
+
const database = this.#database;
|
|
248
|
+
const rows = yield* Effect.try({
|
|
249
|
+
try: () => database
|
|
250
|
+
.prepare("SELECT record FROM push_subscriptions ORDER BY endpoint ASC")
|
|
251
|
+
.all(),
|
|
252
|
+
catch: storeError("subscription.list")
|
|
253
|
+
});
|
|
254
|
+
return yield* Effect.forEach(rows, (row) => decodeRow("subscription.decode", StoredPushSubscription, row));
|
|
255
|
+
});
|
|
256
|
+
hasDelivered = Effect.fn("ApprovalAppStore.hasDelivered")(function* (host, jobId, endpoint, deliveredAfter = Number.MIN_SAFE_INTEGER, countComplete = true) {
|
|
257
|
+
const database = this.#database;
|
|
258
|
+
const normalizedHost = host.toLowerCase();
|
|
259
|
+
return yield* Effect.try({
|
|
260
|
+
try: () => database
|
|
261
|
+
.prepare(`SELECT 1 FROM push_deliveries
|
|
262
|
+
WHERE host = ? COLLATE NOCASE
|
|
263
|
+
AND job_id = ?
|
|
264
|
+
AND endpoint = ?
|
|
265
|
+
AND delivered_at >= ?
|
|
266
|
+
AND (count_complete = 1 OR ? = 0)`)
|
|
267
|
+
.get(normalizedHost, jobId, endpoint, deliveredAfter, countComplete === true ? 1 : 0) !== undefined,
|
|
268
|
+
catch: storeError("delivery.has")
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
recordDelivery = Effect.fn("ApprovalAppStore.recordDelivery")(function* (host, jobId, endpoint, deliveredAt, countComplete = true) {
|
|
272
|
+
const database = this.#database;
|
|
273
|
+
const normalizedHost = host.toLowerCase();
|
|
274
|
+
yield* Effect.try({
|
|
275
|
+
try: () => recordDelivery(database, normalizedHost, jobId, endpoint, deliveredAt, countComplete),
|
|
276
|
+
catch: storeError("delivery.record")
|
|
277
|
+
});
|
|
278
|
+
yield* this.secureFiles();
|
|
279
|
+
});
|
|
280
|
+
secureFiles() {
|
|
281
|
+
const fileSystem = this.#fileSystem;
|
|
282
|
+
const files = [this.path, `${this.path}-wal`, `${this.path}-shm`];
|
|
283
|
+
return Effect.forEach(files, (path) => fileSystem.exists(path).pipe(Effect.mapError(storeError("secure.exists")), Effect.flatMap((exists) => exists
|
|
284
|
+
? fileSystem.chmod(path, 0o600).pipe(Effect.mapError(storeError("secure.chmod")))
|
|
285
|
+
: Effect.void)), { discard: true });
|
|
286
|
+
}
|
|
287
|
+
close() {
|
|
288
|
+
this.#database.close();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { FleetValidationError } from "@knpkv/herdr-fleet";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
export declare const workSnapshotPath = "/v1/work";
|
|
4
|
+
export declare const workCheckpointPath = "/v1/work/checkpoints";
|
|
5
|
+
export declare const workCheckpointFromJson: (text: string) => Effect.Effect<{
|
|
6
|
+
readonly version: "herdr.work.event.v1";
|
|
7
|
+
readonly eventId: string;
|
|
8
|
+
readonly occurredAt: number;
|
|
9
|
+
readonly goal: {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly title: string;
|
|
12
|
+
readonly summary: string;
|
|
13
|
+
readonly detail: string;
|
|
14
|
+
readonly state: "blocked" | "completed" | "deployed" | "planned" | "review" | "working";
|
|
15
|
+
readonly owner: {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
};
|
|
19
|
+
readonly repository: {
|
|
20
|
+
readonly repository: string;
|
|
21
|
+
readonly branch: string;
|
|
22
|
+
};
|
|
23
|
+
readonly spend: {
|
|
24
|
+
readonly currency: string;
|
|
25
|
+
readonly minorUnits: number;
|
|
26
|
+
} | null;
|
|
27
|
+
readonly delivery: "deployed" | "local" | "merged" | "pull_request" | "review";
|
|
28
|
+
readonly blocker: {
|
|
29
|
+
readonly summary: string;
|
|
30
|
+
readonly since: number;
|
|
31
|
+
} | null;
|
|
32
|
+
readonly connectTarget: {
|
|
33
|
+
readonly host: string;
|
|
34
|
+
readonly agentId: string;
|
|
35
|
+
readonly url: string;
|
|
36
|
+
} | null;
|
|
37
|
+
readonly createdAt: number;
|
|
38
|
+
readonly updatedAt: number;
|
|
39
|
+
};
|
|
40
|
+
}, FleetValidationError, never>;
|
|
41
|
+
export declare const workCheckpointUrl: (config: {
|
|
42
|
+
readonly host: string;
|
|
43
|
+
readonly repository: string;
|
|
44
|
+
readonly stateDirectory: string;
|
|
45
|
+
readonly crossHost: boolean;
|
|
46
|
+
readonly port: number;
|
|
47
|
+
readonly localPort: number;
|
|
48
|
+
readonly approvalPort: number;
|
|
49
|
+
readonly allowedUsers: readonly string[];
|
|
50
|
+
readonly approvalNodes: readonly string[];
|
|
51
|
+
readonly machines: readonly {
|
|
52
|
+
readonly host: string;
|
|
53
|
+
readonly nodeId: string;
|
|
54
|
+
}[];
|
|
55
|
+
readonly applyMachines: readonly string[];
|
|
56
|
+
readonly checkCommand: readonly string[];
|
|
57
|
+
readonly applyCommand: readonly string[] | null;
|
|
58
|
+
readonly browserMcpRecoverCommand: readonly string[] | null;
|
|
59
|
+
readonly coordinatorCommand: readonly string[];
|
|
60
|
+
readonly herdrCommand: string;
|
|
61
|
+
readonly tailscaleCommand: string;
|
|
62
|
+
readonly approvalTls: {
|
|
63
|
+
readonly certificatePath: string;
|
|
64
|
+
readonly privateKeyPath: string;
|
|
65
|
+
} | null;
|
|
66
|
+
readonly approvalHub: {
|
|
67
|
+
readonly host: string;
|
|
68
|
+
readonly nodeId: string;
|
|
69
|
+
readonly url: string;
|
|
70
|
+
};
|
|
71
|
+
readonly pushAllowedOrigins: readonly string[];
|
|
72
|
+
readonly pushSubject: string;
|
|
73
|
+
}, target: string) => Effect.Effect<string, FleetValidationError, never>;
|
|
74
|
+
export declare const workSnapshotUrl: (config: {
|
|
75
|
+
readonly host: string;
|
|
76
|
+
readonly repository: string;
|
|
77
|
+
readonly stateDirectory: string;
|
|
78
|
+
readonly crossHost: boolean;
|
|
79
|
+
readonly port: number;
|
|
80
|
+
readonly localPort: number;
|
|
81
|
+
readonly approvalPort: number;
|
|
82
|
+
readonly allowedUsers: readonly string[];
|
|
83
|
+
readonly approvalNodes: readonly string[];
|
|
84
|
+
readonly machines: readonly {
|
|
85
|
+
readonly host: string;
|
|
86
|
+
readonly nodeId: string;
|
|
87
|
+
}[];
|
|
88
|
+
readonly applyMachines: readonly string[];
|
|
89
|
+
readonly checkCommand: readonly string[];
|
|
90
|
+
readonly applyCommand: readonly string[] | null;
|
|
91
|
+
readonly browserMcpRecoverCommand: readonly string[] | null;
|
|
92
|
+
readonly coordinatorCommand: readonly string[];
|
|
93
|
+
readonly herdrCommand: string;
|
|
94
|
+
readonly tailscaleCommand: string;
|
|
95
|
+
readonly approvalTls: {
|
|
96
|
+
readonly certificatePath: string;
|
|
97
|
+
readonly privateKeyPath: string;
|
|
98
|
+
} | null;
|
|
99
|
+
readonly approvalHub: {
|
|
100
|
+
readonly host: string;
|
|
101
|
+
readonly nodeId: string;
|
|
102
|
+
readonly url: string;
|
|
103
|
+
};
|
|
104
|
+
readonly pushAllowedOrigins: readonly string[];
|
|
105
|
+
readonly pushSubject: string;
|
|
106
|
+
}, target: string) => Effect.Effect<string, FleetValidationError, never>;
|
|
107
|
+
//# sourceMappingURL=work-checkpoint.d.ts.map
|