@highstate/backend-api 0.19.1 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/highstate.manifest.json +1 -1
- package/dist/index.js +46 -40
- package/package.json +14 -14
- package/src/handlers/worker.ts +20 -3
- package/LICENSE +0 -21
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
import { InstanceServiceDefinition } from '@highstate/api/instance.v1';
|
|
3
|
-
import { SecretServiceDefinition } from '@highstate/api/secret.v1';
|
|
4
|
-
import { WorkerServiceDefinition } from '@highstate/api/worker.v1';
|
|
5
|
-
import { createServer } from 'nice-grpc';
|
|
6
|
-
import { AccessError, instanceCustomStatusInputSchema } from '@highstate/backend/shared';
|
|
7
|
-
import { z, commonObjectMetaSchema } from '@highstate/contract';
|
|
8
|
-
import { ServerError, Status } from 'nice-grpc-common';
|
|
9
|
-
import { isAbortError } from 'abort-controller-x';
|
|
10
|
-
|
|
1
|
+
// @bun
|
|
11
2
|
// src/index.ts
|
|
3
|
+
import { rm } from "fs/promises";
|
|
4
|
+
import { InstanceServiceDefinition } from "@highstate/api/instance.v1";
|
|
5
|
+
import { SecretServiceDefinition } from "@highstate/api/secret.v1";
|
|
6
|
+
import { WorkerServiceDefinition } from "@highstate/api/worker.v1";
|
|
7
|
+
import { createServer } from "nice-grpc";
|
|
8
|
+
|
|
9
|
+
// src/handlers/instance.ts
|
|
10
|
+
import { instanceCustomStatusInputSchema } from "@highstate/backend/shared";
|
|
11
|
+
import { z } from "@highstate/contract";
|
|
12
|
+
|
|
13
|
+
// src/shared/authentication.ts
|
|
14
|
+
import { ServerError, Status } from "nice-grpc-common";
|
|
12
15
|
async function authenticate(services, context) {
|
|
13
16
|
const token = context.metadata.get("api-key");
|
|
14
17
|
if (!token) {
|
|
@@ -21,34 +24,36 @@ async function authenticate(services, context) {
|
|
|
21
24
|
const apiKey = await services.apiKeyService.getApiKeyByToken(projectId, token);
|
|
22
25
|
return [projectId, apiKey];
|
|
23
26
|
}
|
|
27
|
+
// src/shared/error-handling.ts
|
|
28
|
+
import { AccessError } from "@highstate/backend/shared";
|
|
29
|
+
import { isAbortError } from "abort-controller-x";
|
|
30
|
+
import { ServerError as ServerError2, Status as Status2 } from "nice-grpc-common";
|
|
24
31
|
function createErrorHandlingMiddleware(services) {
|
|
25
32
|
return async function* errorHandlingMiddleware(call, context) {
|
|
26
33
|
try {
|
|
27
34
|
return yield* call.next(call.request, context);
|
|
28
35
|
} catch (error) {
|
|
29
|
-
if (error instanceof
|
|
36
|
+
if (error instanceof ServerError2 || isAbortError(error)) {
|
|
30
37
|
throw error;
|
|
31
38
|
}
|
|
32
39
|
if (error instanceof AccessError) {
|
|
33
40
|
services.logger.info({ error }, "access denied");
|
|
34
|
-
throw new
|
|
41
|
+
throw new ServerError2(Status2.UNAUTHENTICATED, "Access denied");
|
|
35
42
|
}
|
|
36
43
|
services.logger.error({ error }, "unexpected error");
|
|
37
|
-
throw new
|
|
44
|
+
throw new ServerError2(Status2.INTERNAL, "An unexpected error occurred");
|
|
38
45
|
}
|
|
39
46
|
};
|
|
40
47
|
}
|
|
48
|
+
// src/shared/validation.ts
|
|
49
|
+
import { ServerError as ServerError3, Status as Status3 } from "nice-grpc-common";
|
|
41
50
|
function parseArgument(request, argumentName, schema) {
|
|
42
51
|
const result = schema.safeParse(request[argumentName]);
|
|
43
52
|
if (!result.success) {
|
|
44
|
-
throw new
|
|
45
|
-
Status.INVALID_ARGUMENT,
|
|
46
|
-
`Invalid argument "${argumentName}": ${result.error.message}`
|
|
47
|
-
);
|
|
53
|
+
throw new ServerError3(Status3.INVALID_ARGUMENT, `Invalid argument "${argumentName}": ${result.error.message}`);
|
|
48
54
|
}
|
|
49
55
|
return result.data;
|
|
50
56
|
}
|
|
51
|
-
|
|
52
57
|
// src/handlers/instance.ts
|
|
53
58
|
function createInstanceService(services) {
|
|
54
59
|
return {
|
|
@@ -56,23 +61,13 @@ function createInstanceService(services) {
|
|
|
56
61
|
const [projectId, apiKey] = await authenticate(services, context);
|
|
57
62
|
const stateId = parseArgument(request, "stateId", z.cuid2());
|
|
58
63
|
const customStatus = parseArgument(request, "status", instanceCustomStatusInputSchema);
|
|
59
|
-
await services.instanceStateService.updateCustomStatus(
|
|
60
|
-
projectId,
|
|
61
|
-
stateId,
|
|
62
|
-
apiKey.serviceAccountId,
|
|
63
|
-
customStatus
|
|
64
|
-
);
|
|
64
|
+
await services.instanceStateService.updateCustomStatus(projectId, stateId, apiKey.serviceAccountId, customStatus);
|
|
65
65
|
return {};
|
|
66
66
|
},
|
|
67
67
|
async removeCustomStatus(request, context) {
|
|
68
68
|
const [projectId, apiKey] = await authenticate(services, context);
|
|
69
69
|
const stateId = parseArgument(request, "stateId", z.cuid2());
|
|
70
|
-
await services.instanceStateService.removeCustomStatus(
|
|
71
|
-
projectId,
|
|
72
|
-
stateId,
|
|
73
|
-
apiKey.serviceAccountId,
|
|
74
|
-
request.statusName
|
|
75
|
-
);
|
|
70
|
+
await services.instanceStateService.removeCustomStatus(projectId, stateId, apiKey.serviceAccountId, request.statusName);
|
|
76
71
|
return {};
|
|
77
72
|
}
|
|
78
73
|
};
|
|
@@ -87,11 +82,21 @@ function createSecretService(services) {
|
|
|
87
82
|
}
|
|
88
83
|
};
|
|
89
84
|
}
|
|
85
|
+
|
|
86
|
+
// src/handlers/worker.ts
|
|
87
|
+
import { commonObjectMetaSchema, serviceAccountMetaSchema, z as z2 } from "@highstate/contract";
|
|
88
|
+
var workerMetaUpdatePayloadSchema = z2.union([
|
|
89
|
+
commonObjectMetaSchema,
|
|
90
|
+
z2.object({
|
|
91
|
+
workerMeta: commonObjectMetaSchema,
|
|
92
|
+
serviceAccountMeta: serviceAccountMetaSchema
|
|
93
|
+
})
|
|
94
|
+
]);
|
|
90
95
|
function createWorkerService(services) {
|
|
91
96
|
return {
|
|
92
|
-
async
|
|
97
|
+
async* connect(request, context) {
|
|
93
98
|
const [projectId] = await authenticate(services, context);
|
|
94
|
-
const workerVersionId = parseArgument(request, "workerVersionId",
|
|
99
|
+
const workerVersionId = parseArgument(request, "workerVersionId", z2.cuid2());
|
|
95
100
|
services.workerManager.setWorkerRunning(projectId, workerVersionId);
|
|
96
101
|
const database = await services.database.forProject(projectId);
|
|
97
102
|
const existingRegistrations = await database.workerUnitRegistration.findMany({
|
|
@@ -139,9 +144,11 @@ function createWorkerService(services) {
|
|
|
139
144
|
},
|
|
140
145
|
async updateWorkerVersionMeta(request, context) {
|
|
141
146
|
const [projectId] = await authenticate(services, context);
|
|
142
|
-
const workerVersionId = parseArgument(request, "workerVersionId",
|
|
143
|
-
const
|
|
144
|
-
|
|
147
|
+
const workerVersionId = parseArgument(request, "workerVersionId", z2.string());
|
|
148
|
+
const payload = parseArgument(request, "meta", workerMetaUpdatePayloadSchema);
|
|
149
|
+
const workerMeta = "workerMeta" in payload ? payload.workerMeta : payload;
|
|
150
|
+
const serviceAccountMeta = "serviceAccountMeta" in payload ? payload.serviceAccountMeta : undefined;
|
|
151
|
+
await services.workerService.updateWorkerVersionMeta(projectId, workerVersionId, workerMeta, serviceAccountMeta);
|
|
145
152
|
return {};
|
|
146
153
|
}
|
|
147
154
|
};
|
|
@@ -159,7 +166,7 @@ async function startBackedApi(services) {
|
|
|
159
166
|
try {
|
|
160
167
|
await rm(sockPath, { force: true });
|
|
161
168
|
} catch (error) {
|
|
162
|
-
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
|
|
169
|
+
if (!(error instanceof Error && ("code" in error) && error.code === "ENOENT")) {
|
|
163
170
|
services.logger.error({ error }, "failed to remove existing socket file");
|
|
164
171
|
}
|
|
165
172
|
}
|
|
@@ -167,7 +174,6 @@ async function startBackedApi(services) {
|
|
|
167
174
|
services.workerManager.config.HIGHSTATE_WORKER_API_PATH = sockPath;
|
|
168
175
|
services.logger.info(`api listening at %s`, sockPath);
|
|
169
176
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
//# sourceMappingURL=index.js.map
|
|
177
|
+
export {
|
|
178
|
+
startBackedApi
|
|
179
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@highstate/backend-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -21,8 +21,14 @@
|
|
|
21
21
|
"platform"
|
|
22
22
|
]
|
|
23
23
|
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "highstate build",
|
|
26
|
+
"typecheck": "tsgo --noEmit --skipLibCheck",
|
|
27
|
+
"biome": "biome check --write --unsafe --error-on-warnings",
|
|
28
|
+
"biome:check": "biome check --error-on-warnings"
|
|
29
|
+
},
|
|
24
30
|
"peerDependencies": {
|
|
25
|
-
"@pulumi/pulumi": "3.
|
|
31
|
+
"@pulumi/pulumi": "3.232.0",
|
|
26
32
|
"classic-level": "^2.0.0"
|
|
27
33
|
},
|
|
28
34
|
"peerDependenciesMeta": {
|
|
@@ -35,14 +41,14 @@
|
|
|
35
41
|
},
|
|
36
42
|
"dependencies": {
|
|
37
43
|
"@bufbuild/protobuf": "^2.6.1",
|
|
44
|
+
"@highstate/api": "0.20.0",
|
|
45
|
+
"@highstate/backend": "0.20.0",
|
|
46
|
+
"@highstate/cli": "0.20.0",
|
|
47
|
+
"@highstate/contract": "0.20.0",
|
|
38
48
|
"abort-controller-x": "^0.4.3",
|
|
39
49
|
"nice-grpc": "^2.1.12",
|
|
40
50
|
"nice-grpc-common": "^2.0.2",
|
|
41
|
-
"zod": "^4.0.5"
|
|
42
|
-
"@highstate/api": "0.19.1",
|
|
43
|
-
"@highstate/backend": "0.19.1",
|
|
44
|
-
"@highstate/contract": "0.19.1",
|
|
45
|
-
"@highstate/cli": "0.19.1"
|
|
51
|
+
"zod": "^4.0.5"
|
|
46
52
|
},
|
|
47
53
|
"devDependencies": {
|
|
48
54
|
"@biomejs/biome": "2.2.0",
|
|
@@ -50,11 +56,5 @@
|
|
|
50
56
|
},
|
|
51
57
|
"repository": {
|
|
52
58
|
"url": "https://github.com/highstate-io/highstate"
|
|
53
|
-
},
|
|
54
|
-
"scripts": {
|
|
55
|
-
"build": "highstate build",
|
|
56
|
-
"typecheck": "tsgo --noEmit --skipLibCheck",
|
|
57
|
-
"biome": "biome check --write --unsafe --error-on-warnings",
|
|
58
|
-
"biome:check": "biome check --error-on-warnings"
|
|
59
59
|
}
|
|
60
|
-
}
|
|
60
|
+
}
|
package/src/handlers/worker.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { WorkerServiceImplementation } from "@highstate/api/worker.v1"
|
|
2
2
|
import type { Services } from "@highstate/backend"
|
|
3
|
-
import { commonObjectMetaSchema, z } from "@highstate/contract"
|
|
3
|
+
import { commonObjectMetaSchema, serviceAccountMetaSchema, z } from "@highstate/contract"
|
|
4
4
|
import { authenticate, parseArgument } from "../shared"
|
|
5
5
|
|
|
6
|
+
const workerMetaUpdatePayloadSchema = z.union([
|
|
7
|
+
commonObjectMetaSchema,
|
|
8
|
+
z.object({
|
|
9
|
+
workerMeta: commonObjectMetaSchema,
|
|
10
|
+
serviceAccountMeta: serviceAccountMetaSchema,
|
|
11
|
+
}),
|
|
12
|
+
])
|
|
13
|
+
|
|
6
14
|
export function createWorkerService(services: Services): WorkerServiceImplementation {
|
|
7
15
|
return {
|
|
8
16
|
async *connect(request, context) {
|
|
@@ -69,9 +77,18 @@ export function createWorkerService(services: Services): WorkerServiceImplementa
|
|
|
69
77
|
const [projectId] = await authenticate(services, context)
|
|
70
78
|
|
|
71
79
|
const workerVersionId = parseArgument(request, "workerVersionId", z.string())
|
|
72
|
-
const
|
|
80
|
+
const payload = parseArgument(request, "meta", workerMetaUpdatePayloadSchema)
|
|
81
|
+
|
|
82
|
+
const workerMeta = "workerMeta" in payload ? payload.workerMeta : payload
|
|
83
|
+
const serviceAccountMeta =
|
|
84
|
+
"serviceAccountMeta" in payload ? payload.serviceAccountMeta : undefined
|
|
73
85
|
|
|
74
|
-
await services.workerService.updateWorkerVersionMeta(
|
|
86
|
+
await services.workerService.updateWorkerVersionMeta(
|
|
87
|
+
projectId,
|
|
88
|
+
workerVersionId,
|
|
89
|
+
workerMeta,
|
|
90
|
+
serviceAccountMeta,
|
|
91
|
+
)
|
|
75
92
|
|
|
76
93
|
return {}
|
|
77
94
|
},
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Exeteres
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/authentication.ts","../src/shared/error-handling.ts","../src/shared/validation.ts","../src/handlers/instance.ts","../src/handlers/secret.ts","../src/handlers/worker.ts","../src/index.ts"],"names":["ServerError","Status","z"],"mappings":"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","file":"index.js","sourcesContent":["import type { ApiKey, Services } from \"@highstate/backend\"\nimport { type CallContext, ServerError, Status } from \"nice-grpc-common\"\n\nexport async function authenticate(\n services: Services,\n context: CallContext,\n): Promise<[projectId: string, apiKey: ApiKey]> {\n const token = context.metadata.get(\"api-key\")\n if (!token) {\n throw new ServerError(Status.UNAUTHENTICATED, \"No API key provided\")\n }\n\n const projectId = context.metadata.get(\"project-id\")\n if (!projectId) {\n throw new ServerError(Status.UNAUTHENTICATED, \"No project ID provided\")\n }\n\n const apiKey = await services.apiKeyService.getApiKeyByToken(projectId, token)\n\n return [projectId, apiKey]\n}\n","import type { Services } from \"@highstate/backend\"\nimport { AccessError } from \"@highstate/backend/shared\"\nimport { isAbortError } from \"abort-controller-x\"\nimport { type CallContext, ServerError, type ServerMiddlewareCall, Status } from \"nice-grpc-common\"\n\nexport function createErrorHandlingMiddleware(services: Services) {\n return async function* errorHandlingMiddleware<TRequest, TResponse>(\n call: ServerMiddlewareCall<TRequest, TResponse>,\n context: CallContext,\n ) {\n try {\n return yield* call.next(call.request, context)\n } catch (error) {\n if (error instanceof ServerError || isAbortError(error)) {\n throw error\n }\n\n if (error instanceof AccessError) {\n services.logger.info({ error }, \"access denied\")\n throw new ServerError(Status.UNAUTHENTICATED, \"Access denied\")\n }\n\n services.logger.error({ error }, \"unexpected error\")\n throw new ServerError(Status.INTERNAL, \"An unexpected error occurred\")\n }\n }\n}\n","import type { z } from \"zod\"\nimport { ServerError, Status } from \"nice-grpc-common\"\n\nexport function parseArgument<\n TRequest,\n TArgumentName extends string & keyof TRequest,\n TSchema extends z.ZodType,\n>(request: TRequest, argumentName: TArgumentName, schema: TSchema): z.infer<TSchema> {\n const result = schema.safeParse(request[argumentName])\n if (!result.success) {\n throw new ServerError(\n Status.INVALID_ARGUMENT,\n `Invalid argument \"${argumentName}\": ${result.error.message}`,\n )\n }\n\n return result.data\n}\n","import type { InstanceServiceImplementation } from \"@highstate/api/instance.v1\"\nimport type { Services } from \"@highstate/backend\"\nimport { instanceCustomStatusInputSchema } from \"@highstate/backend/shared\"\nimport { z } from \"@highstate/contract\"\nimport { authenticate, parseArgument } from \"../shared\"\n\nexport function createInstanceService(services: Services): InstanceServiceImplementation {\n return {\n async updateCustomStatus(request, context) {\n const [projectId, apiKey] = await authenticate(services, context)\n\n // TODO: validate instance access\n\n const stateId = parseArgument(request, \"stateId\", z.cuid2())\n const customStatus = parseArgument(request, \"status\", instanceCustomStatusInputSchema)\n\n await services.instanceStateService.updateCustomStatus(\n projectId,\n stateId,\n apiKey.serviceAccountId,\n customStatus,\n )\n\n return {}\n },\n\n async removeCustomStatus(request, context) {\n const [projectId, apiKey] = await authenticate(services, context)\n\n // TODO: validate instance access\n\n const stateId = parseArgument(request, \"stateId\", z.cuid2())\n\n await services.instanceStateService.removeCustomStatus(\n projectId,\n stateId,\n apiKey.serviceAccountId,\n request.statusName,\n )\n\n return {}\n },\n }\n}\n","import type { SecretServiceImplementation } from \"@highstate/api/secret.v1\"\nimport type { Services } from \"@highstate/backend\"\nimport { authenticate } from \"../shared\"\n\nexport function createSecretService(services: Services): SecretServiceImplementation {\n return {\n async getSecretContent(_request, context) {\n const [_projectId] = await authenticate(services, context)\n\n // TODO: validate secret access\n\n throw new Error(\"Not implemented\")\n\n // return {\n // content,\n // }\n },\n }\n}\n","import type { WorkerServiceImplementation } from \"@highstate/api/worker.v1\"\nimport type { Services } from \"@highstate/backend\"\nimport { commonObjectMetaSchema, z } from \"@highstate/contract\"\nimport { authenticate, parseArgument } from \"../shared\"\n\nexport function createWorkerService(services: Services): WorkerServiceImplementation {\n return {\n async *connect(request, context) {\n const [projectId] = await authenticate(services, context)\n\n const workerVersionId = parseArgument(request, \"workerVersionId\", z.cuid2())\n\n // set worker as running\n services.workerManager.setWorkerRunning(projectId, workerVersionId)\n\n // get existing registrations for this worker version\n const database = await services.database.forProject(projectId)\n const existingRegistrations = await database.workerUnitRegistration.findMany({\n where: { workerVersionId },\n select: { stateId: true, params: true },\n })\n\n // emit existing registrations\n for (const registration of existingRegistrations) {\n yield {\n event: {\n $case: \"unitRegistration\",\n value: {\n stateId: registration.stateId,\n params: registration.params,\n },\n },\n }\n }\n\n // subscribe to new registration events for this worker version\n const registrationStream = await services.pubsubManager.subscribe([\n \"worker-unit-registration\",\n projectId,\n workerVersionId,\n ])\n\n // emit new registration/deregistration events\n for await (const event of registrationStream) {\n if (event.type === \"registered\") {\n yield {\n event: {\n $case: \"unitRegistration\",\n value: {\n instanceId: event.instanceId,\n params: event.params,\n },\n },\n }\n } else if (event.type === \"deregistered\") {\n yield {\n event: {\n $case: \"unitDeregistration\",\n value: {\n instanceId: event.instanceId,\n },\n },\n }\n }\n }\n },\n\n async updateWorkerVersionMeta(request, context) {\n const [projectId] = await authenticate(services, context)\n\n const workerVersionId = parseArgument(request, \"workerVersionId\", z.string())\n const meta = parseArgument(request, \"meta\", commonObjectMetaSchema)\n\n await services.workerService.updateWorkerVersionMeta(projectId, workerVersionId, meta)\n\n return {}\n },\n }\n}\n","import type { Services } from \"@highstate/backend\"\nimport { rm } from \"node:fs/promises\"\nimport { InstanceServiceDefinition } from \"@highstate/api/instance.v1\"\nimport { SecretServiceDefinition } from \"@highstate/api/secret.v1\"\nimport { WorkerServiceDefinition } from \"@highstate/api/worker.v1\"\nimport { createServer } from \"nice-grpc\"\nimport { createInstanceService } from \"./handlers/instance\"\nimport { createSecretService } from \"./handlers/secret\"\nimport { createWorkerService } from \"./handlers/worker\"\nimport { createErrorHandlingMiddleware } from \"./shared\"\n\nexport async function startBackedApi(services: Services) {\n const server = createServer()\n server.use(createErrorHandlingMiddleware(services))\n\n server.add(InstanceServiceDefinition, createInstanceService(services))\n server.add(SecretServiceDefinition, createSecretService(services))\n server.add(WorkerServiceDefinition, createWorkerService(services))\n\n const uid = process.geteuid?.()\n const sockPath = `/run/user/${uid}/highstate.sock`\n\n try {\n await rm(sockPath, { force: true })\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) {\n services.logger.error({ error }, \"failed to remove existing socket file\")\n }\n }\n\n await server.listen(`unix:${sockPath}`)\n\n services.workerManager.config.HIGHSTATE_WORKER_API_PATH = sockPath\n services.logger.info(`api listening at %s`, sockPath)\n}\n"]}
|