@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.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +22 -0
  3. package/dist/activity-history.d.ts +36 -0
  4. package/dist/activity-history.d.ts.map +1 -0
  5. package/dist/activity-history.js +233 -0
  6. package/dist/activity-history.js.map +1 -0
  7. package/dist/approval-app-view.d.ts +26 -0
  8. package/dist/approval-app-view.d.ts.map +1 -0
  9. package/dist/approval-app-view.js +79 -0
  10. package/dist/approval-app-view.js.map +1 -0
  11. package/dist/approval-client.d.ts +2 -0
  12. package/dist/approval-client.d.ts.map +1 -0
  13. package/dist/approval-client.js +520 -0
  14. package/dist/approval-client.js.map +1 -0
  15. package/dist/approval-sw.js +28 -0
  16. package/dist/approval-sw.js.map +7 -0
  17. package/dist/approval-url.d.ts +37 -0
  18. package/dist/approval-url.d.ts.map +1 -0
  19. package/dist/approval-url.js +37 -0
  20. package/dist/approval-url.js.map +1 -0
  21. package/dist/approval.js +188 -0
  22. package/dist/approval.js.map +7 -0
  23. package/dist/auth.d.ts +10 -0
  24. package/dist/auth.d.ts.map +1 -0
  25. package/dist/auth.js +14 -0
  26. package/dist/auth.js.map +1 -0
  27. package/dist/bin.d.ts +3 -0
  28. package/dist/bin.d.ts.map +1 -0
  29. package/dist/bin.js +47 -0
  30. package/dist/bin.js.map +1 -0
  31. package/dist/connect-entry.d.ts +2 -0
  32. package/dist/connect-entry.d.ts.map +1 -0
  33. package/dist/connect-entry.js +2 -0
  34. package/dist/connect-entry.js.map +1 -0
  35. package/dist/connect.js +188 -0
  36. package/dist/connect.js.map +7 -0
  37. package/dist/dashboard-model.d.ts +719 -0
  38. package/dist/dashboard-model.d.ts.map +1 -0
  39. package/dist/dashboard-model.js +83 -0
  40. package/dist/dashboard-model.js.map +1 -0
  41. package/dist/dashboard-view.d.ts +42 -0
  42. package/dist/dashboard-view.d.ts.map +1 -0
  43. package/dist/dashboard-view.js +194 -0
  44. package/dist/dashboard-view.js.map +1 -0
  45. package/dist/errors.d.ts +33 -0
  46. package/dist/errors.d.ts.map +1 -0
  47. package/dist/errors.js +12 -0
  48. package/dist/errors.js.map +1 -0
  49. package/dist/fleetctl-submission.d.ts +68 -0
  50. package/dist/fleetctl-submission.d.ts.map +1 -0
  51. package/dist/fleetctl-submission.js +31 -0
  52. package/dist/fleetctl-submission.js.map +1 -0
  53. package/dist/fleetctl.d.ts +22 -0
  54. package/dist/fleetctl.d.ts.map +1 -0
  55. package/dist/fleetctl.js +264 -0
  56. package/dist/fleetctl.js.map +1 -0
  57. package/dist/geist-latin-wght-normal.woff2 +0 -0
  58. package/dist/geist-mono-latin-wght-normal.woff2 +0 -0
  59. package/dist/http.d.ts +1512 -0
  60. package/dist/http.d.ts.map +1 -0
  61. package/dist/http.js +1653 -0
  62. package/dist/http.js.map +1 -0
  63. package/dist/index.css +2 -0
  64. package/dist/index.css.map +7 -0
  65. package/dist/index.d.ts +12 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +12 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/internal/config-path.d.ts +3 -0
  70. package/dist/internal/config-path.d.ts.map +1 -0
  71. package/dist/internal/config-path.js +3 -0
  72. package/dist/internal/config-path.js.map +1 -0
  73. package/dist/internal/dashboard-pending-state.d.ts +49 -0
  74. package/dist/internal/dashboard-pending-state.d.ts.map +1 -0
  75. package/dist/internal/dashboard-pending-state.js +98 -0
  76. package/dist/internal/dashboard-pending-state.js.map +1 -0
  77. package/dist/internal/fleet-follow.d.ts +54 -0
  78. package/dist/internal/fleet-follow.d.ts.map +1 -0
  79. package/dist/internal/fleet-follow.js +10 -0
  80. package/dist/internal/fleet-follow.js.map +1 -0
  81. package/dist/internal/fleet-request.d.ts +4 -0
  82. package/dist/internal/fleet-request.d.ts.map +1 -0
  83. package/dist/internal/fleet-request.js +11 -0
  84. package/dist/internal/fleet-request.js.map +1 -0
  85. package/dist/internal/html.d.ts +3 -0
  86. package/dist/internal/html.d.ts.map +1 -0
  87. package/dist/internal/html.js +8 -0
  88. package/dist/internal/html.js.map +1 -0
  89. package/dist/internal/websocket.d.ts +4 -0
  90. package/dist/internal/websocket.d.ts.map +1 -0
  91. package/dist/internal/websocket.js +12 -0
  92. package/dist/internal/websocket.js.map +1 -0
  93. package/dist/model.d.ts +40 -0
  94. package/dist/model.d.ts.map +1 -0
  95. package/dist/model.js +53 -0
  96. package/dist/model.js.map +1 -0
  97. package/dist/operations.d.ts +72 -0
  98. package/dist/operations.d.ts.map +1 -0
  99. package/dist/operations.js +284 -0
  100. package/dist/operations.js.map +1 -0
  101. package/dist/push-policy.d.ts +3 -0
  102. package/dist/push-policy.d.ts.map +1 -0
  103. package/dist/push-policy.js +3 -0
  104. package/dist/push-policy.js.map +1 -0
  105. package/dist/push-sender.d.ts +18 -0
  106. package/dist/push-sender.d.ts.map +1 -0
  107. package/dist/push-sender.js +33 -0
  108. package/dist/push-sender.js.map +1 -0
  109. package/dist/push-subscription.d.ts +17 -0
  110. package/dist/push-subscription.d.ts.map +1 -0
  111. package/dist/push-subscription.js +62 -0
  112. package/dist/push-subscription.js.map +1 -0
  113. package/dist/push-worker.d.ts +20 -0
  114. package/dist/push-worker.d.ts.map +1 -0
  115. package/dist/push-worker.js +32 -0
  116. package/dist/push-worker.js.map +1 -0
  117. package/dist/pwa.d.ts +39 -0
  118. package/dist/pwa.d.ts.map +1 -0
  119. package/dist/pwa.js +51 -0
  120. package/dist/pwa.js.map +1 -0
  121. package/dist/shell-view.d.ts +20 -0
  122. package/dist/shell-view.d.ts.map +1 -0
  123. package/dist/shell-view.js +86 -0
  124. package/dist/shell-view.js.map +1 -0
  125. package/dist/store.d.ts +46 -0
  126. package/dist/store.d.ts.map +1 -0
  127. package/dist/store.js +291 -0
  128. package/dist/store.js.map +1 -0
  129. package/dist/work-checkpoint.d.ts +107 -0
  130. package/dist/work-checkpoint.d.ts.map +1 -0
  131. package/dist/work-checkpoint.js +37 -0
  132. package/dist/work-checkpoint.js.map +1 -0
  133. 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
@@ -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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"}
@@ -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