@koed-labs/server 0.0.0-bootstrap.0 → 0.10.3

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 (56) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +65 -0
  3. package/bin/koed.js +7 -0
  4. package/package.json +22 -4
  5. package/third-party-notices.json +92 -0
  6. package/vendor/chunk-2BWTLF7A.js +13883 -0
  7. package/vendor/chunk-2DA7EFZV.js +924 -0
  8. package/vendor/chunk-2S3OGPUC.js +58 -0
  9. package/vendor/chunk-473OA62O.js +26827 -0
  10. package/vendor/chunk-6JRWGOQG.js +236 -0
  11. package/vendor/chunk-7NOQSX4T.js +14 -0
  12. package/vendor/chunk-7NXKPDF3.js +1586 -0
  13. package/vendor/chunk-A4NKL2PK.js +153 -0
  14. package/vendor/chunk-A6WMU2GG.js +48 -0
  15. package/vendor/chunk-AFLKCQHF.js +73 -0
  16. package/vendor/chunk-BTS4ZTF3.js +43 -0
  17. package/vendor/chunk-C3N4N5HR.js +108 -0
  18. package/vendor/chunk-DXTAWHY3.js +25 -0
  19. package/vendor/chunk-G4IIVVJO.js +56 -0
  20. package/vendor/chunk-GELDEJJ2.js +4337 -0
  21. package/vendor/chunk-HFM6ROJL.js +188 -0
  22. package/vendor/chunk-HK2H3MVE.js +328 -0
  23. package/vendor/chunk-HZJK5RWC.js +5088 -0
  24. package/vendor/chunk-J2L726BC.js +2497 -0
  25. package/vendor/chunk-JBGD4JLR.js +3779 -0
  26. package/vendor/chunk-JJNLH5GA.js +2470 -0
  27. package/vendor/chunk-JKOG52PE.js +128 -0
  28. package/vendor/chunk-K2EIFEQF.js +89 -0
  29. package/vendor/chunk-KUIWH33N.js +16 -0
  30. package/vendor/chunk-NEWIOOM6.js +34 -0
  31. package/vendor/chunk-NM5NXCVM.js +2404 -0
  32. package/vendor/chunk-PT3AUPGE.js +459 -0
  33. package/vendor/chunk-PY5LYLV7.js +143 -0
  34. package/vendor/chunk-QIIKMG3M.js +103 -0
  35. package/vendor/chunk-QT24SAW6.js +110 -0
  36. package/vendor/chunk-RVYJKKWA.js +22 -0
  37. package/vendor/chunk-SKVYP7NY.js +1647 -0
  38. package/vendor/chunk-TSRPHKAC.js +780 -0
  39. package/vendor/chunk-UI5RLBKQ.js +30 -0
  40. package/vendor/chunk-VPC5NLZG.js +259 -0
  41. package/vendor/chunk-W2HNI6AA.js +75 -0
  42. package/vendor/chunk-X6M525ZJ.js +20 -0
  43. package/vendor/chunk-XIZVLPHZ.js +18 -0
  44. package/vendor/chunk-XNFVSVBS.js +242 -0
  45. package/vendor/chunk-YCDEMYY6.js +2076 -0
  46. package/vendor/chunk-YJ6XUZT6.js +292 -0
  47. package/vendor/chunk-YJWREXN6.js +26020 -0
  48. package/vendor/chunk-YK4F4VY2.js +173 -0
  49. package/vendor/chunk-YOFI4PCH.js +36 -0
  50. package/vendor/chunk-ZUGNCQZE.js +247 -0
  51. package/vendor/chunk-ZXX4ZNJY.js +21 -0
  52. package/vendor/cli.js +7 -0
  53. package/vendor/component-manifest-catalog.js +8 -0
  54. package/vendor/control-plane.js +23 -0
  55. package/vendor/package.json +1 -0
  56. package/vendor/privacy-service-bootstrap.js +6 -0
@@ -0,0 +1,30 @@
1
+ import { createRequire as createKoedRequire } from "node:module"; const require = createKoedRequire(import.meta.url);
2
+
3
+ // packages/koed-server/dist/runtime-artifact-source.js
4
+ import { resolve } from "node:path";
5
+ var runtimeProcess = process;
6
+ var trimRuntimeValue = (value) => {
7
+ const trimmed = value?.trim();
8
+ return trimmed ? trimmed : void 0;
9
+ };
10
+ var isPackagedRuntimeMode = (environment = process.env) => environment.KOED_PACKAGED_DESKTOP === "1";
11
+ var allowsPackagedSourceFallback = (environment = process.env) => environment.KOED_ALLOW_PACKAGED_SOURCE_FALLBACK === "1";
12
+ var canUseSourceCheckoutFallback = (environment = process.env) => !isPackagedRuntimeMode(environment) || allowsPackagedSourceFallback(environment);
13
+ var resolvePackagedResourcesPath = (environment = process.env) => {
14
+ const explicit = trimRuntimeValue(environment.KOED_PACKAGED_RESOURCES_PATH);
15
+ if (explicit) {
16
+ return resolve(explicit);
17
+ }
18
+ return runtimeProcess.resourcesPath ? resolve(runtimeProcess.resourcesPath) : void 0;
19
+ };
20
+ var resolvePackagedKoedRuntimeRoot = (environment = process.env) => {
21
+ const resourcesPath = resolvePackagedResourcesPath(environment);
22
+ return resourcesPath ? resolve(resourcesPath, "koed-runtime") : void 0;
23
+ };
24
+
25
+ export {
26
+ isPackagedRuntimeMode,
27
+ canUseSourceCheckoutFallback,
28
+ resolvePackagedResourcesPath,
29
+ resolvePackagedKoedRuntimeRoot
30
+ };
@@ -0,0 +1,259 @@
1
+ import { createRequire as createKoedRequire } from "node:module"; const require = createKoedRequire(import.meta.url);
2
+ import {
3
+ PINNED_PRIVACY_MODEL_FILES,
4
+ PINNED_PRIVACY_MODEL_ID,
5
+ PINNED_PRIVACY_MODEL_REVISION
6
+ } from "./chunk-473OA62O.js";
7
+
8
+ // packages/koed-server/dist/privacy-model-runtime.js
9
+ import { createHash, randomUUID } from "node:crypto";
10
+ import { createReadStream, createWriteStream, existsSync, linkSync, lstatSync, mkdirSync, renameSync, rmSync, statSync } from "node:fs";
11
+ import { dirname, resolve } from "node:path";
12
+ import { Readable, Transform } from "node:stream";
13
+ import { pipeline } from "node:stream/promises";
14
+ import { setTimeout as delay } from "node:timers/promises";
15
+ var PRIVACY_MODEL_ID = PINNED_PRIVACY_MODEL_ID;
16
+ var PRIVACY_MODEL_REVISION = PINNED_PRIVACY_MODEL_REVISION;
17
+ var PRIVACY_MODEL_FILES = PINNED_PRIVACY_MODEL_FILES;
18
+ var resolvePrivacyModelPaths = (paths) => ({
19
+ blobsDir: resolve(paths.modelsDir, "privacy", "blobs", "sha256"),
20
+ cacheDir: resolve(paths.modelsDir, "privacy", "transformers-cache")
21
+ });
22
+ var cachePath = (paths, file) => resolve(paths.cacheDir, PRIVACY_MODEL_ID, PRIVACY_MODEL_REVISION, file.path);
23
+ var blobPath = (paths, file) => resolve(paths.blobsDir, file.sha256);
24
+ var isSameFile = (left, right) => {
25
+ try {
26
+ const leftStat = statSync(left);
27
+ const rightStat = lstatSync(right);
28
+ return rightStat.isFile() && leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
29
+ } catch (error) {
30
+ if (error.code === "ENOENT")
31
+ return false;
32
+ throw error;
33
+ }
34
+ };
35
+ var materializePrivacyModelCacheEntry = (blob, target) => {
36
+ if (isSameFile(blob, target))
37
+ return;
38
+ rmSync(target, { force: true });
39
+ try {
40
+ linkSync(blob, target);
41
+ } catch (error) {
42
+ if (error.code === "EEXIST" && isSameFile(blob, target)) {
43
+ return;
44
+ }
45
+ throw error;
46
+ }
47
+ };
48
+ var sha256File = async (path, signal) => {
49
+ const hash = createHash("sha256");
50
+ await pipeline(createReadStream(path), hash, signal ? { signal } : {});
51
+ return hash.digest("hex");
52
+ };
53
+ var materializeCacheEntry = (paths, file) => {
54
+ const target = cachePath(paths, file);
55
+ mkdirSync(dirname(target), { recursive: true, mode: 448 });
56
+ materializePrivacyModelCacheEntry(blobPath(paths, file), target);
57
+ };
58
+ var retryableTransferError = (error) => {
59
+ if (error instanceof TypeError)
60
+ return true;
61
+ const code = error?.code;
62
+ return [
63
+ "ECONNRESET",
64
+ "ECONNREFUSED",
65
+ "ETIMEDOUT",
66
+ "EAI_AGAIN",
67
+ "UND_ERR_SOCKET",
68
+ "UND_ERR_CONNECT_TIMEOUT",
69
+ "UND_ERR_HEADERS_TIMEOUT",
70
+ "UND_ERR_BODY_TIMEOUT"
71
+ ].includes(code ?? "");
72
+ };
73
+ var downloadPrivacyAsset = async (file, temporary, fetcher, options) => {
74
+ const url = `https://huggingface.co/${PRIVACY_MODEL_ID}/resolve/${PRIVACY_MODEL_REVISION}/${file.path}`;
75
+ for (let attempt = 1; attempt <= 3; attempt++) {
76
+ options.signal?.throwIfAborted();
77
+ rmSync(temporary, { force: true });
78
+ const idle = new AbortController();
79
+ let idleTimer;
80
+ const activity = () => {
81
+ clearTimeout(idleTimer);
82
+ idleTimer = setTimeout(() => idle.abort(new Error("Privacy model transfer stalled.")), 3e4);
83
+ };
84
+ activity();
85
+ const signal = options.signal ? AbortSignal.any([options.signal, idle.signal]) : idle.signal;
86
+ try {
87
+ const response = await fetcher(url, { signal });
88
+ if (!response.ok || !response.body) {
89
+ await response.body?.cancel();
90
+ const retryAfter = response.headers.get("retry-after");
91
+ const seconds = retryAfter === null ? NaN : Number(retryAfter);
92
+ const requestedDelay = retryAfter === null ? NaN : Number.isFinite(seconds) ? seconds * 1e3 : Date.parse(retryAfter) - Date.now();
93
+ const retryDelay = Number.isFinite(requestedDelay) ? Math.max(0, requestedDelay) : 250 * attempt;
94
+ if (attempt < 3 && retryDelay <= 3e4 && (response.status === 408 || response.status === 429 || response.status >= 500)) {
95
+ clearTimeout(idleTimer);
96
+ await delay(retryDelay, void 0, options.signal ? { signal: options.signal } : {});
97
+ continue;
98
+ }
99
+ return response.status;
100
+ }
101
+ let transferredBytes = 0;
102
+ let lastProgressAt = 0;
103
+ const headerSize = Number(response.headers.get("content-length"));
104
+ const totalBytes = file.size ?? (Number.isSafeInteger(headerSize) && headerSize > 0 ? headerSize : void 0);
105
+ options.progress?.({
106
+ phase: "model-download",
107
+ transferredBytes,
108
+ ...totalBytes === void 0 ? {} : { totalBytes },
109
+ file: file.path
110
+ });
111
+ const monitor = new Transform({
112
+ transform(chunk, _encoding, callback) {
113
+ activity();
114
+ transferredBytes += chunk.length;
115
+ if (Date.now() - lastProgressAt >= 100) {
116
+ options.progress?.({
117
+ phase: "model-download",
118
+ transferredBytes,
119
+ ...totalBytes === void 0 ? {} : { totalBytes },
120
+ file: file.path
121
+ });
122
+ lastProgressAt = Date.now();
123
+ }
124
+ callback(null, chunk);
125
+ }
126
+ });
127
+ await pipeline(Readable.fromWeb(response.body), monitor, createWriteStream(temporary, { mode: 384 }), { signal });
128
+ options.progress?.({
129
+ phase: "model-download",
130
+ transferredBytes,
131
+ ...totalBytes === void 0 ? {} : { totalBytes },
132
+ file: file.path
133
+ });
134
+ return void 0;
135
+ } catch (error) {
136
+ rmSync(temporary, { force: true });
137
+ options.signal?.throwIfAborted();
138
+ if (!idle.signal.aborted && !retryableTransferError(error))
139
+ throw error;
140
+ if (attempt === 3) {
141
+ throw new Error(`Could not download pinned Privacy Filter model asset ${file.path}: transfer failed after ${attempt} attempts.`, { cause: error });
142
+ }
143
+ clearTimeout(idleTimer);
144
+ await delay(250 * attempt, void 0, options.signal ? { signal: options.signal } : {});
145
+ } finally {
146
+ clearTimeout(idleTimer);
147
+ }
148
+ }
149
+ throw new Error("Privacy Filter model transfer did not complete.");
150
+ };
151
+ var collectPrivacyModelStatus = async (serverPaths, signal) => {
152
+ const paths = resolvePrivacyModelPaths(serverPaths);
153
+ const files = [];
154
+ for (const file of PRIVACY_MODEL_FILES) {
155
+ signal?.throwIfAborted();
156
+ const blob = blobPath(paths, file);
157
+ if (!existsSync(blob)) {
158
+ return {
159
+ ok: false,
160
+ state: "missing",
161
+ message: `Pinned Privacy Filter model asset is missing: ${file.path}.`,
162
+ action: "Run koed models install --kind privacy.",
163
+ modelPath: paths.cacheDir,
164
+ files
165
+ };
166
+ }
167
+ const sizeBytes = statSync(blob).size;
168
+ const actual = await sha256File(blob, signal);
169
+ files.push({ path: file.path, sha256: actual, sizeBytes });
170
+ if (actual !== file.sha256 || file.size !== void 0 && sizeBytes !== file.size) {
171
+ return {
172
+ ok: false,
173
+ state: "checksum_mismatch",
174
+ message: `Pinned Privacy Filter model asset failed verification: ${file.path}.`,
175
+ action: "Run koed models install --kind privacy to replace the invalid content-addressed asset.",
176
+ modelPath: paths.cacheDir,
177
+ files
178
+ };
179
+ }
180
+ materializeCacheEntry(paths, file);
181
+ }
182
+ return {
183
+ ok: true,
184
+ state: "installed",
185
+ message: "Pinned Privacy Filter model assets are installed and verified.",
186
+ modelPath: paths.cacheDir,
187
+ files
188
+ };
189
+ };
190
+ var installPrivacyModel = async (serverPaths, environment = process.env, fetcher = globalThis.fetch.bind(globalThis), options = {}) => {
191
+ if (environment.KOED_DEPENDENCY_MODE?.trim() === "external") {
192
+ return {
193
+ ok: false,
194
+ state: "not_configured",
195
+ message: "Privacy Filter model downloads are disabled in external dependency mode.",
196
+ action: "Run the Operator-managed Privacy Filter Service with its pinned model assets.",
197
+ modelPath: resolvePrivacyModelPaths(serverPaths).cacheDir,
198
+ files: []
199
+ };
200
+ }
201
+ const paths = resolvePrivacyModelPaths(serverPaths);
202
+ mkdirSync(paths.blobsDir, { recursive: true, mode: 448 });
203
+ for (const file of PRIVACY_MODEL_FILES) {
204
+ options.signal?.throwIfAborted();
205
+ const blob = blobPath(paths, file);
206
+ if (existsSync(blob) && await sha256File(blob, options.signal) === file.sha256) {
207
+ materializeCacheEntry(paths, file);
208
+ continue;
209
+ }
210
+ const temporary = `${blob}.${process.pid}.${randomUUID()}.download`;
211
+ try {
212
+ const failedStatus = await downloadPrivacyAsset(file, temporary, fetcher, options);
213
+ if (failedStatus !== void 0) {
214
+ return {
215
+ ok: false,
216
+ state: "missing",
217
+ message: `Could not download pinned Privacy Filter model asset ${file.path} (HTTP ${failedStatus}).`,
218
+ action: "Check network access to huggingface.co and retry.",
219
+ modelPath: paths.cacheDir,
220
+ files: []
221
+ };
222
+ }
223
+ const sizeBytes = statSync(temporary).size;
224
+ options.progress?.({
225
+ phase: "model-verify",
226
+ transferredBytes: sizeBytes,
227
+ totalBytes: sizeBytes,
228
+ file: file.path
229
+ });
230
+ const actual = await sha256File(temporary, options.signal);
231
+ if (actual !== file.sha256 || file.size !== void 0 && sizeBytes !== file.size) {
232
+ return {
233
+ ok: false,
234
+ state: "checksum_mismatch",
235
+ message: `Downloaded Privacy Filter model asset failed verification: ${file.path}.`,
236
+ action: "Do not use the asset; verify the pinned upstream revision and retry.",
237
+ modelPath: paths.cacheDir,
238
+ files: [{ path: file.path, sha256: actual, sizeBytes }]
239
+ };
240
+ }
241
+ rmSync(blob, { force: true });
242
+ renameSync(temporary, blob);
243
+ materializeCacheEntry(paths, file);
244
+ } finally {
245
+ rmSync(temporary, { force: true });
246
+ }
247
+ }
248
+ return collectPrivacyModelStatus(serverPaths, options.signal);
249
+ };
250
+
251
+ export {
252
+ PRIVACY_MODEL_ID,
253
+ PRIVACY_MODEL_REVISION,
254
+ PRIVACY_MODEL_FILES,
255
+ resolvePrivacyModelPaths,
256
+ materializePrivacyModelCacheEntry,
257
+ collectPrivacyModelStatus,
258
+ installPrivacyModel
259
+ };
@@ -0,0 +1,75 @@
1
+ import { createRequire as createKoedRequire } from "node:module"; const require = createKoedRequire(import.meta.url);
2
+
3
+ // packages/koed-server/dist/env-file.js
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { resolve } from "node:path";
6
+ var parseEnvFile = (content, options = {}) => {
7
+ const values = {};
8
+ const fail = (lineNumber, reason) => {
9
+ throw new Error(`${options.source ?? "Environment file"}:${lineNumber}: ${reason}`);
10
+ };
11
+ for (const [index, line] of content.split(/\r?\n/).entries()) {
12
+ const trimmed = line.trim();
13
+ if (!trimmed || trimmed.startsWith("#")) {
14
+ continue;
15
+ }
16
+ const equals = trimmed.indexOf("=");
17
+ if (equals <= 0) {
18
+ if (options.strict)
19
+ fail(index + 1, "expected KEY=VALUE");
20
+ continue;
21
+ }
22
+ const key = trimmed.slice(0, equals).trim();
23
+ let value = trimmed.slice(equals + 1).trim();
24
+ const quoted = value.startsWith('"') || value.startsWith("'");
25
+ if (quoted && options.strict) {
26
+ const quote = value[0];
27
+ let closingQuote = -1;
28
+ for (let position = 1; position < value.length; position += 1) {
29
+ if (quote === '"' && value[position] === "\\") {
30
+ position += 1;
31
+ } else if (value[position] === quote) {
32
+ closingQuote = position;
33
+ break;
34
+ }
35
+ }
36
+ if (closingQuote < 0) {
37
+ fail(index + 1, `unterminated quoted value for ${key}`);
38
+ }
39
+ const trailing = value.slice(closingQuote + 1).trim();
40
+ if (trailing && !trailing.startsWith("#")) {
41
+ fail(index + 1, `unexpected text after quoted value for ${key}`);
42
+ }
43
+ value = value.slice(1, closingQuote);
44
+ } else if (quoted) {
45
+ if (value.length < 2 || value.at(-1) !== value[0]) {
46
+ } else {
47
+ value = value.slice(1, -1);
48
+ }
49
+ }
50
+ if (options.strict && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
51
+ fail(index + 1, `invalid environment variable name ${key}`);
52
+ }
53
+ values[key] = value;
54
+ }
55
+ return values;
56
+ };
57
+ var loadRepoEnv = (repoRoot, environment = process.env) => {
58
+ const envPath = environment.KOED_ENV_PATH?.trim() ? resolve(environment.KOED_ENV_PATH) : resolve(repoRoot, ".env");
59
+ if (!existsSync(envPath)) {
60
+ return {};
61
+ }
62
+ return parseEnvFile(readFileSync(envPath, "utf8"));
63
+ };
64
+ var environmentWithRepoEnv = (repoRoot, environment = process.env) => ({
65
+ ...loadRepoEnv(repoRoot, environment),
66
+ ...environment
67
+ });
68
+ var resolveApiUrl = (environment, repoEnv) => ((environment.KOED_AUTO_PORTS === "1" && environment.API_HOST_PORT ? `http://localhost:${environment.API_HOST_PORT}` : null) ?? environment.MEMORY_API_URL ?? (environment.API_HOST_PORT ? `http://localhost:${environment.API_HOST_PORT}` : null) ?? repoEnv.MEMORY_API_URL ?? (repoEnv.API_HOST_PORT ? `http://localhost:${repoEnv.API_HOST_PORT}` : null) ?? environment.CODEX_MEMORY_BASE_URL ?? repoEnv.CODEX_MEMORY_BASE_URL ?? "http://localhost:3300").trim();
69
+
70
+ export {
71
+ parseEnvFile,
72
+ loadRepoEnv,
73
+ environmentWithRepoEnv,
74
+ resolveApiUrl
75
+ };
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as createKoedRequire } from "node:module"; const require = createKoedRequire(import.meta.url);
3
+ import {
4
+ isKoedServerCliEntrypoint,
5
+ runKoedServerCli,
6
+ shouldExitPackagedSupervisor
7
+ } from "./chunk-GELDEJJ2.js";
8
+ import {
9
+ startKoedServerDaemon
10
+ } from "./chunk-JKOG52PE.js";
11
+ import "./chunk-A4NKL2PK.js";
12
+ import "./chunk-K2EIFEQF.js";
13
+ import "./chunk-UI5RLBKQ.js";
14
+ import "./chunk-BTS4ZTF3.js";
15
+ export {
16
+ isKoedServerCliEntrypoint,
17
+ runKoedServerCli,
18
+ shouldExitPackagedSupervisor,
19
+ startKoedServerDaemon
20
+ };
@@ -0,0 +1,18 @@
1
+ import { createRequire as createKoedRequire } from "node:module"; const require = createKoedRequire(import.meta.url);
2
+ import {
3
+ getProjectTeamWorkspaceLink,
4
+ linkProjectTeamWorkspace,
5
+ listProjectTeamWorkspaceLinks,
6
+ removeProjectTeamWorkspaceLink,
7
+ removeProjectTeamWorkspaceLinksForBackend,
8
+ removeProjectTeamWorkspaceLinksForMismatchedBinding
9
+ } from "./chunk-6JRWGOQG.js";
10
+ import "./chunk-BTS4ZTF3.js";
11
+ export {
12
+ getProjectTeamWorkspaceLink,
13
+ linkProjectTeamWorkspace,
14
+ listProjectTeamWorkspaceLinks,
15
+ removeProjectTeamWorkspaceLink,
16
+ removeProjectTeamWorkspaceLinksForBackend,
17
+ removeProjectTeamWorkspaceLinksForMismatchedBinding
18
+ };
@@ -0,0 +1,242 @@
1
+ import { createRequire as createKoedRequire } from "node:module"; const require = createKoedRequire(import.meta.url);
2
+ import {
3
+ require_proper_lockfile
4
+ } from "./chunk-7NXKPDF3.js";
5
+ import {
6
+ createPlatformHostProofStore,
7
+ deviceIdentityStatePathFor,
8
+ deviceProofFingerprint,
9
+ hostProofReferenceFor,
10
+ inspectDeviceIdentity,
11
+ serializeHostProof
12
+ } from "./chunk-473OA62O.js";
13
+ import {
14
+ __toESM
15
+ } from "./chunk-BTS4ZTF3.js";
16
+
17
+ // packages/koed-server/dist/device-identity.js
18
+ var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
19
+ import { randomBytes, randomUUID } from "node:crypto";
20
+ import { existsSync, lstatSync, mkdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
21
+ import { resolve } from "node:path";
22
+ var lockStaleMs = 3e4;
23
+ var defaultProof = () => randomBytes(32).toString("base64url");
24
+ var testProofDirectory = (paths) => {
25
+ const proofRoot = resolve(`${paths.koedHome}-proof-root`);
26
+ mkdirSync(proofRoot, { recursive: true, mode: 448 });
27
+ return resolve(proofRoot, "proof");
28
+ };
29
+ var dependencies = (paths, deps) => {
30
+ const environment = deps.environment ?? process.env;
31
+ return {
32
+ environment,
33
+ proofStore: deps.proofStore ?? createPlatformHostProofStore({
34
+ koedHome: paths.koedHome,
35
+ environment: environment.NODE_ENV === "test" ? {
36
+ ...environment,
37
+ KOED_DEVICE_PROOF_DIR: testProofDirectory(paths)
38
+ } : environment
39
+ }),
40
+ now: deps.now ?? (() => /* @__PURE__ */ new Date()),
41
+ randomId: deps.randomId ?? randomUUID,
42
+ randomProof: deps.randomProof ?? defaultProof,
43
+ onPhase: deps.onPhase ?? (() => void 0)
44
+ };
45
+ };
46
+ var lockTarget = (paths) => resolve(paths.runDir, "device-identity");
47
+ var bootstrapTombstonePath = (paths) => resolve(paths.configDir, "device-identity-bootstrap.json");
48
+ var withDeviceIdentityLock = async (paths, mutation) => {
49
+ mkdirSync(paths.runDir, { recursive: true, mode: 448 });
50
+ const release = await import_proper_lockfile.default.lock(lockTarget(paths), {
51
+ realpath: false,
52
+ stale: lockStaleMs,
53
+ update: lockStaleMs / 3,
54
+ retries: {
55
+ retries: 100,
56
+ factor: 1,
57
+ minTimeout: 25,
58
+ maxTimeout: 100,
59
+ randomize: true
60
+ }
61
+ });
62
+ try {
63
+ return await mutation();
64
+ } finally {
65
+ await release();
66
+ }
67
+ };
68
+ var writeState = (statePath, state) => {
69
+ mkdirSync(resolve(statePath, ".."), { recursive: true, mode: 448 });
70
+ const temporary = `${statePath}.${randomUUID()}.tmp`;
71
+ try {
72
+ writeFileSync(temporary, `${JSON.stringify(state, null, 2)}
73
+ `, {
74
+ mode: 384,
75
+ flag: "wx"
76
+ });
77
+ renameSync(temporary, statePath);
78
+ } finally {
79
+ rmSync(temporary, { force: true });
80
+ }
81
+ };
82
+ var prepareIdentityStorage = (paths) => {
83
+ mkdirSync(paths.koedHome, { recursive: true, mode: 448 });
84
+ if (lstatSync(paths.koedHome).isSymbolicLink()) {
85
+ throw new Error("KOED_HOME must not be a symbolic link.");
86
+ }
87
+ mkdirSync(paths.configDir, { recursive: true, mode: 448 });
88
+ if (lstatSync(paths.configDir).isSymbolicLink()) {
89
+ throw new Error("Device identity config directory must not be a symbolic link.");
90
+ }
91
+ return realpathSync(paths.koedHome);
92
+ };
93
+ var writeBootstrapTombstone = (paths, now) => {
94
+ const tombstonePath = bootstrapTombstonePath(paths);
95
+ if (existsSync(tombstonePath))
96
+ return;
97
+ const temporary = `${tombstonePath}.${randomUUID()}.tmp`;
98
+ try {
99
+ writeFileSync(temporary, `${JSON.stringify({ schemaVersion: 1, initializedAt: now.toISOString() })}
100
+ `, { mode: 384, flag: "wx" });
101
+ renameSync(temporary, tombstonePath);
102
+ } finally {
103
+ rmSync(temporary, { force: true });
104
+ }
105
+ };
106
+ var newIdentityState = (now, randomId, randomProof, remoteOperations, deploymentId = randomId(), pendingRemoteRevocation = false) => {
107
+ const deviceInstanceId = randomId();
108
+ const proof = randomProof();
109
+ const timestamp = now.toISOString();
110
+ return {
111
+ state: {
112
+ schemaVersion: 1,
113
+ deploymentId,
114
+ deviceInstanceId,
115
+ proof: {
116
+ reference: hostProofReferenceFor(deviceInstanceId),
117
+ fingerprint: deviceProofFingerprint(proof)
118
+ },
119
+ remoteOperations,
120
+ deploymentIdentityAdoptionPending: true,
121
+ ...pendingRemoteRevocation ? { pendingRemoteRevocation: true } : {},
122
+ createdAt: timestamp,
123
+ updatedAt: timestamp
124
+ },
125
+ proof
126
+ };
127
+ };
128
+ var result = (identity, options = {}) => ({
129
+ ...identity,
130
+ initialized: options.initialized ?? false,
131
+ rotated: options.rotated ?? false,
132
+ ...options.referencesInvalidated === void 0 ? {} : { referencesInvalidated: options.referencesInvalidated }
133
+ });
134
+ var inspect = (paths, proofStore) => inspectDeviceIdentity({
135
+ statePath: deviceIdentityStatePathFor(paths.koedHome),
136
+ proofStore
137
+ });
138
+ var inspectDeviceIdentityStatus = (paths, deps = {}) => {
139
+ const resolved = dependencies(paths, deps);
140
+ return Promise.resolve(result(inspect(paths, resolved.proofStore)));
141
+ };
142
+ var ensureDeviceIdentity = async (paths, deps = {}) => {
143
+ const resolved = dependencies(paths, deps);
144
+ return withDeviceIdentityLock(paths, () => {
145
+ try {
146
+ const canonicalKoedHome = prepareIdentityStorage(paths);
147
+ const current = inspect(paths, resolved.proofStore);
148
+ if (current.health !== "missing_state")
149
+ return result(current);
150
+ if (existsSync(bootstrapTombstonePath(paths)))
151
+ return result(current);
152
+ writeBootstrapTombstone(paths, resolved.now());
153
+ resolved.onPhase("tombstone");
154
+ const next = newIdentityState(resolved.now(), resolved.randomId, resolved.randomProof, "repair_required");
155
+ resolved.proofStore.write(next.state.proof.reference, serializeHostProof({
156
+ deploymentId: next.state.deploymentId,
157
+ deviceInstanceId: next.state.deviceInstanceId,
158
+ proof: next.proof,
159
+ canonicalKoedHome
160
+ }));
161
+ resolved.onPhase("proof");
162
+ writeState(deviceIdentityStatePathFor(paths.koedHome), next.state);
163
+ resolved.onPhase("state");
164
+ writeState(deviceIdentityStatePathFor(paths.koedHome), {
165
+ ...next.state,
166
+ remoteOperations: "enabled",
167
+ updatedAt: resolved.now().toISOString()
168
+ });
169
+ return result(inspect(paths, resolved.proofStore), { initialized: true });
170
+ } catch {
171
+ return result(inspect(paths, resolved.proofStore));
172
+ }
173
+ });
174
+ };
175
+ var rotateDeviceIdentity = async (paths, options) => {
176
+ const resolved = dependencies(paths, options.dependencies ?? {});
177
+ return withDeviceIdentityLock(paths, async () => {
178
+ let canonicalKoedHome;
179
+ try {
180
+ canonicalKoedHome = prepareIdentityStorage(paths);
181
+ } catch {
182
+ return result(inspect(paths, resolved.proofStore), { rotated: false });
183
+ }
184
+ const previous = inspect(paths, resolved.proofStore);
185
+ const previousReference = previous.deviceInstanceId ? hostProofReferenceFor(previous.deviceInstanceId) : null;
186
+ const next = newIdentityState(resolved.now(), resolved.randomId, resolved.randomProof, "repair_required", previous.deploymentId ?? void 0, true);
187
+ delete next.state.deploymentIdentityAdoptionPending;
188
+ try {
189
+ writeBootstrapTombstone(paths, resolved.now());
190
+ resolved.proofStore.write(next.state.proof.reference, serializeHostProof({
191
+ deploymentId: next.state.deploymentId,
192
+ deviceInstanceId: next.state.deviceInstanceId,
193
+ proof: next.proof,
194
+ canonicalKoedHome
195
+ }));
196
+ writeState(deviceIdentityStatePathFor(paths.koedHome), next.state);
197
+ } catch {
198
+ return result(inspect(paths, resolved.proofStore), { rotated: false });
199
+ }
200
+ try {
201
+ const invalidation = await options.invalidateRemoteReferences();
202
+ if (invalidation?.pendingRemoteRevocation) {
203
+ return result(inspect(paths, resolved.proofStore), {
204
+ rotated: true,
205
+ referencesInvalidated: false
206
+ });
207
+ }
208
+ } catch {
209
+ return result(inspect(paths, resolved.proofStore), {
210
+ rotated: true,
211
+ referencesInvalidated: false
212
+ });
213
+ }
214
+ const completed = {
215
+ ...next.state,
216
+ remoteOperations: "enabled",
217
+ updatedAt: resolved.now().toISOString()
218
+ };
219
+ delete completed.pendingRemoteRevocation;
220
+ try {
221
+ writeState(deviceIdentityStatePathFor(paths.koedHome), completed);
222
+ if (previousReference && previousReference !== completed.proof.reference) {
223
+ resolved.proofStore.remove(previousReference);
224
+ }
225
+ return result(inspect(paths, resolved.proofStore), {
226
+ rotated: true,
227
+ referencesInvalidated: true
228
+ });
229
+ } catch {
230
+ return result(inspect(paths, resolved.proofStore), {
231
+ rotated: true,
232
+ referencesInvalidated: false
233
+ });
234
+ }
235
+ });
236
+ };
237
+
238
+ export {
239
+ inspectDeviceIdentityStatus,
240
+ ensureDeviceIdentity,
241
+ rotateDeviceIdentity
242
+ };