nolo-cli 0.1.34 → 0.1.35

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 (46) hide show
  1. package/README.md +11 -1
  2. package/agent-runtime/localToolPolicy.ts +1 -1
  3. package/agent-runtime/localWorkspaceTools.ts +5 -2
  4. package/agent-runtime/providerResolution.ts +1 -3
  5. package/agentCommandSupport.ts +4 -0
  6. package/agentListCommands.ts +73 -11
  7. package/agentListHelpers.ts +56 -1
  8. package/agentRecordHelpers.ts +45 -5
  9. package/cli/agentCommandSupport.ts +4 -0
  10. package/cli/agentListCommands.ts +73 -11
  11. package/cli/agentListHelpers.ts +56 -1
  12. package/cli/agentRecordHelpers.ts +45 -5
  13. package/cli/cliSpaceHelpers.ts +39 -0
  14. package/cli/connectorWebSocketTarget.ts +10 -3
  15. package/cli/dialogCommands.ts +446 -0
  16. package/cli/dialogInternalCommandEntries.ts +13 -0
  17. package/cli/globalRecordOperations.ts +258 -0
  18. package/cli/internalCommandEntries.ts +2 -0
  19. package/cli/localRuntimeAuthority.ts +182 -0
  20. package/cli/localRuntimeDb.ts +2 -4
  21. package/cli/machineWsRunDispatch.ts +0 -3
  22. package/cli/machineWsSession.ts +2 -0
  23. package/cli/runtimeDoctorCommands.ts +55 -5
  24. package/cliSpaceHelpers.ts +39 -0
  25. package/connectorWebSocketTarget.ts +10 -3
  26. package/database/server/cliAuthorityBrokerClient.ts +227 -0
  27. package/database/server/cliAuthorityBrokerServer.ts +283 -0
  28. package/database/server/cliAuthorityBrokerTypes.ts +43 -0
  29. package/database/server/cliAuthorityStoreDriver.ts +98 -0
  30. package/database/server/levelAuthorityStore.ts +46 -26
  31. package/database/tombstones.ts +52 -0
  32. package/database/userDataMerge.ts +95 -0
  33. package/dialogCommands.ts +446 -0
  34. package/dialogInternalCommandEntries.ts +13 -0
  35. package/globalRecordOperations.ts +258 -0
  36. package/internalCommandEntries.ts +2 -0
  37. package/localRuntimeAuthority.ts +182 -0
  38. package/localRuntimeDb.ts +2 -4
  39. package/machineWsRunDispatch.ts +0 -3
  40. package/machineWsSession.ts +2 -0
  41. package/package.json +18 -6
  42. package/runtimeDoctorCommands.ts +55 -5
  43. package/database/server/db.ts +0 -99
  44. package/database/server/ensureDbOpen.ts +0 -12
  45. package/database/server/memoryAuthorityStore.ts +0 -133
  46. package/database/server/serverStoreFactory.ts +0 -118
@@ -0,0 +1,227 @@
1
+ import type {
2
+ AuthorityBatchOperation,
3
+ AuthorityBatchWriter,
4
+ AuthorityIteratorOptions,
5
+ AuthorityStore,
6
+ } from "./authorityStoreTypes";
7
+ import type {
8
+ CliAuthorityBrokerRequest,
9
+ CliAuthorityBrokerResponse,
10
+ } from "./cliAuthorityBrokerTypes";
11
+ import { createConnection } from "node:net";
12
+
13
+ const DEFAULT_ITERATOR_PAGE_SIZE = 200;
14
+
15
+ export class CliAuthorityBrokerUnavailableError extends Error {
16
+ readonly reason = "cli_authority_broker_unavailable";
17
+ readonly status = 503;
18
+ readonly endpoint: string;
19
+
20
+ constructor(endpoint: string, cause?: unknown) {
21
+ super(`CLI authority broker unavailable at ${endpoint}. Start the local authority broker and retry.`);
22
+ this.name = "CliAuthorityBrokerUnavailableError";
23
+ this.endpoint = endpoint;
24
+ if (cause !== undefined) {
25
+ (this as Error & { cause?: unknown }).cause = cause;
26
+ }
27
+ }
28
+ }
29
+
30
+ export class CliAuthorityBrokerProtocolError extends Error {
31
+ readonly code: string;
32
+ readonly retryable: boolean;
33
+
34
+ constructor(error: { code: string; message: string; retryable?: boolean }) {
35
+ super(error.message);
36
+ this.name = "CliAuthorityBrokerProtocolError";
37
+ this.code = error.code;
38
+ this.retryable = error.retryable === true;
39
+ }
40
+ }
41
+
42
+ export function isCliAuthorityBrokerUnavailableError(
43
+ error: unknown
44
+ ): error is CliAuthorityBrokerUnavailableError {
45
+ return (
46
+ error instanceof CliAuthorityBrokerUnavailableError ||
47
+ (
48
+ typeof error === "object" &&
49
+ error !== null &&
50
+ (error as any).reason === "cli_authority_broker_unavailable" &&
51
+ (error as any).status === 503
52
+ )
53
+ );
54
+ }
55
+
56
+ function isCliAuthorityBrokerProtocolError(
57
+ error: unknown
58
+ ): error is CliAuthorityBrokerProtocolError {
59
+ return error instanceof CliAuthorityBrokerProtocolError;
60
+ }
61
+
62
+ export type CliAuthorityBrokerRequestInvoker = (
63
+ request: CliAuthorityBrokerRequest
64
+ ) => Promise<CliAuthorityBrokerResponse>;
65
+
66
+ export type CliAuthorityBrokerClientOptions = {
67
+ endpoint: string;
68
+ location?: string;
69
+ status?: string;
70
+ invoke: CliAuthorityBrokerRequestInvoker;
71
+ iteratorPageSize?: number;
72
+ };
73
+
74
+ type BrokerSocketTarget =
75
+ | { kind: "unix"; path: string }
76
+ | { kind: "tcp"; host: string; port: number };
77
+
78
+ function resolveBrokerSocketTarget(endpoint: string): BrokerSocketTarget {
79
+ if (endpoint.startsWith("unix://")) {
80
+ return { kind: "unix", path: endpoint.slice("unix://".length) };
81
+ }
82
+ if (endpoint.startsWith("tcp://")) {
83
+ const url = new URL(endpoint);
84
+ return {
85
+ kind: "tcp",
86
+ host: url.hostname,
87
+ port: Number(url.port),
88
+ };
89
+ }
90
+ throw new Error(`Unsupported CLI authority broker endpoint: ${endpoint}`);
91
+ }
92
+
93
+ export function createCliAuthorityBrokerSocketInvoker(options: {
94
+ endpoint: string;
95
+ }): CliAuthorityBrokerRequestInvoker {
96
+ const target = resolveBrokerSocketTarget(options.endpoint);
97
+ return async (request: CliAuthorityBrokerRequest) => {
98
+ return await new Promise<CliAuthorityBrokerResponse>((resolve, reject) => {
99
+ const socket = target.kind === "unix"
100
+ ? createConnection(target.path)
101
+ : createConnection({ host: target.host, port: target.port });
102
+ let responseBuffer = "";
103
+ let settled = false;
104
+
105
+ const finish = (fn: () => void) => {
106
+ if (settled) return;
107
+ settled = true;
108
+ fn();
109
+ };
110
+
111
+ socket.setEncoding("utf8");
112
+ socket.on("connect", () => {
113
+ socket.write(`${JSON.stringify(request)}\n`);
114
+ });
115
+ socket.on("data", (chunk) => {
116
+ responseBuffer += chunk;
117
+ });
118
+ socket.on("end", () => {
119
+ finish(() => {
120
+ const payload = responseBuffer.trim();
121
+ if (!payload) {
122
+ reject(new Error(`CLI authority broker at ${options.endpoint} returned no response`));
123
+ return;
124
+ }
125
+ try {
126
+ resolve(JSON.parse(payload) as CliAuthorityBrokerResponse);
127
+ } catch (error) {
128
+ reject(error);
129
+ }
130
+ });
131
+ });
132
+ socket.on("error", (error) => {
133
+ finish(() => reject(error));
134
+ });
135
+ });
136
+ };
137
+ }
138
+
139
+ async function invokeBrokerRequest(
140
+ options: CliAuthorityBrokerClientOptions,
141
+ request: CliAuthorityBrokerRequest
142
+ ) {
143
+ try {
144
+ const response = await options.invoke(request);
145
+ if (response.ok) return response.result;
146
+ if (response.error.code === "BROKER_UNAVAILABLE") {
147
+ throw new CliAuthorityBrokerUnavailableError(options.endpoint, response.error);
148
+ }
149
+ throw new CliAuthorityBrokerProtocolError(response.error);
150
+ } catch (error) {
151
+ if (isCliAuthorityBrokerUnavailableError(error)) throw error;
152
+ if (isCliAuthorityBrokerProtocolError(error)) throw error;
153
+ throw new CliAuthorityBrokerUnavailableError(options.endpoint, error);
154
+ }
155
+ }
156
+
157
+ export function createCliAuthorityBrokerClient(
158
+ options: CliAuthorityBrokerClientOptions
159
+ ): AuthorityStore {
160
+ const pageSize = options.iteratorPageSize ?? DEFAULT_ITERATOR_PAGE_SIZE;
161
+
162
+ return {
163
+ get location() {
164
+ return options.location ?? options.endpoint;
165
+ },
166
+ get status() {
167
+ return options.status ?? "broker";
168
+ },
169
+ async open() {
170
+ await invokeBrokerRequest(options, { type: "status" });
171
+ },
172
+ async close() {},
173
+ async get<T = any>(key: string) {
174
+ const result = await invokeBrokerRequest(options, { type: "get", key });
175
+ if (result.type !== "get") {
176
+ throw new Error(`CLI authority broker protocol mismatch for get at ${options.endpoint}`);
177
+ }
178
+ return result.value as T;
179
+ },
180
+ async put(key: string, value: unknown) {
181
+ await invokeBrokerRequest(options, { type: "put", key, value });
182
+ },
183
+ async del(key: string) {
184
+ await invokeBrokerRequest(options, { type: "del", key });
185
+ },
186
+ async batchWrite(ops: AuthorityBatchOperation[]) {
187
+ await invokeBrokerRequest(options, { type: "batchWrite", ops });
188
+ },
189
+ createBatch(): AuthorityBatchWriter {
190
+ const ops: AuthorityBatchOperation[] = [];
191
+ return {
192
+ put(key: string, value: unknown) {
193
+ ops.push({ type: "put", key, value });
194
+ },
195
+ del(key: string) {
196
+ ops.push({ type: "del", key });
197
+ },
198
+ async write() {
199
+ await invokeBrokerRequest(options, { type: "batchWrite", ops });
200
+ },
201
+ };
202
+ },
203
+ iterator(iteratorOptions: AuthorityIteratorOptions = {}) {
204
+ return (async function* iterate() {
205
+ let cursor: string | null | undefined = undefined;
206
+ while (true) {
207
+ const result = await invokeBrokerRequest(options, {
208
+ type: "iterator",
209
+ options: iteratorOptions,
210
+ cursor,
211
+ limit: pageSize,
212
+ });
213
+ if (result.type !== "iterator") {
214
+ throw new Error(
215
+ `CLI authority broker protocol mismatch for iterator at ${options.endpoint}`
216
+ );
217
+ }
218
+ for (const entry of result.page.entries) {
219
+ yield entry;
220
+ }
221
+ if (result.page.done || !result.page.nextCursor) return;
222
+ cursor = result.page.nextCursor;
223
+ }
224
+ })();
225
+ },
226
+ };
227
+ }
@@ -0,0 +1,283 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createServer, type AddressInfo, type Server, type Socket } from "node:net";
4
+
5
+ import type { AuthorityStore } from "./authorityStoreTypes";
6
+ import type {
7
+ CliAuthorityBrokerIteratorPage,
8
+ CliAuthorityBrokerRequest,
9
+ CliAuthorityBrokerResponse,
10
+ } from "./cliAuthorityBrokerTypes";
11
+
12
+ const brokerRegistry = new Map<string, Promise<CliAuthorityBrokerServerHandle>>();
13
+ const DEFAULT_ITERATOR_LIMIT = 200;
14
+
15
+ export type CliAuthorityBrokerServerOptions = {
16
+ endpoint: string;
17
+ createStore: () => AuthorityStore | Promise<AuthorityStore>;
18
+ metadataPath?: string;
19
+ healthPath?: string;
20
+ now?: () => number;
21
+ transportMode?: "socket" | "inprocess";
22
+ };
23
+
24
+ export type CliAuthorityBrokerServerHandle = {
25
+ endpoint: string;
26
+ listeningEndpoint: string;
27
+ authorityStore: AuthorityStore;
28
+ request(request: CliAuthorityBrokerRequest): Promise<CliAuthorityBrokerResponse>;
29
+ close(): Promise<void>;
30
+ };
31
+
32
+ type BrokerListenTarget =
33
+ | { kind: "unix"; path: string }
34
+ | { kind: "tcp"; host: string; port: number };
35
+
36
+ function resolveBrokerListenTarget(endpoint: string): BrokerListenTarget {
37
+ if (endpoint.startsWith("unix://")) {
38
+ return { kind: "unix", path: endpoint.slice("unix://".length) };
39
+ }
40
+ if (endpoint.startsWith("tcp://")) {
41
+ const url = new URL(endpoint);
42
+ return {
43
+ kind: "tcp",
44
+ host: url.hostname,
45
+ port: Number(url.port),
46
+ };
47
+ }
48
+ throw new Error(`Unsupported CLI authority broker endpoint: ${endpoint}`);
49
+ }
50
+
51
+ function buildListeningEndpoint(server: Server, target: BrokerListenTarget) {
52
+ if (target.kind === "unix") {
53
+ return `unix://${target.path}`;
54
+ }
55
+ const address = server.address();
56
+ if (!address || typeof address === "string") {
57
+ return `tcp://${target.host}:${target.port}`;
58
+ }
59
+ const info = address as AddressInfo;
60
+ return `tcp://${target.host}:${info.port}`;
61
+ }
62
+
63
+ function ensureParentDir(filePath: string) {
64
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
65
+ }
66
+
67
+ function writeJsonFile(filePath: string | undefined, data: Record<string, unknown>) {
68
+ if (!filePath) return;
69
+ ensureParentDir(filePath);
70
+ fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
71
+ }
72
+
73
+ function removeFileIfPresent(filePath: string | undefined) {
74
+ if (!filePath) return;
75
+ try {
76
+ fs.rmSync(filePath, { force: true });
77
+ } catch {}
78
+ }
79
+
80
+ async function buildIteratorPage(
81
+ store: AuthorityStore,
82
+ request: Extract<CliAuthorityBrokerRequest, { type: "iterator" }>
83
+ ): Promise<CliAuthorityBrokerIteratorPage> {
84
+ const rows: Array<[string, unknown]> = [];
85
+ for await (const entry of store.iterator(request.options ?? {})) {
86
+ rows.push(entry as [string, unknown]);
87
+ }
88
+
89
+ const startIndex = request.cursor
90
+ ? rows.findIndex(([key]) => key === request.cursor) + 1
91
+ : 0;
92
+ const limit = request.limit ?? DEFAULT_ITERATOR_LIMIT;
93
+ const entries = rows.slice(startIndex, startIndex + limit);
94
+ const lastEntry = entries.at(-1) ?? null;
95
+
96
+ return {
97
+ entries,
98
+ nextCursor: lastEntry ? lastEntry[0] : null,
99
+ done: startIndex + entries.length >= rows.length,
100
+ };
101
+ }
102
+
103
+ function createErrorResponse(error: unknown): CliAuthorityBrokerResponse {
104
+ if (
105
+ typeof error === "object" &&
106
+ error !== null &&
107
+ typeof (error as any).code === "string" &&
108
+ typeof (error as any).message === "string"
109
+ ) {
110
+ return {
111
+ ok: false,
112
+ error: {
113
+ code: (error as any).code,
114
+ message: (error as any).message,
115
+ retryable: (error as any).retryable === true,
116
+ },
117
+ };
118
+ }
119
+ return {
120
+ ok: false,
121
+ error: {
122
+ code: "BROKER_INTERNAL_ERROR",
123
+ message: error instanceof Error ? error.message : String(error),
124
+ },
125
+ };
126
+ }
127
+
128
+ export async function dispatchCliAuthorityBrokerRequest(
129
+ store: AuthorityStore,
130
+ request: CliAuthorityBrokerRequest
131
+ ): Promise<CliAuthorityBrokerResponse> {
132
+ try {
133
+ switch (request.type) {
134
+ case "status":
135
+ return { ok: true, result: { type: "status" } };
136
+ case "open":
137
+ await store.open();
138
+ return { ok: true, result: { type: "open" } };
139
+ case "close":
140
+ await store.close();
141
+ return { ok: true, result: { type: "close" } };
142
+ case "get":
143
+ return { ok: true, result: { type: "get", value: await store.get(request.key) } };
144
+ case "put":
145
+ await store.put(request.key, request.value);
146
+ return { ok: true, result: { type: "put" } };
147
+ case "del":
148
+ await store.del(request.key);
149
+ return { ok: true, result: { type: "del" } };
150
+ case "batchWrite":
151
+ await store.batchWrite(request.ops);
152
+ return { ok: true, result: { type: "batchWrite" } };
153
+ case "iterator":
154
+ return {
155
+ ok: true,
156
+ result: {
157
+ type: "iterator",
158
+ page: await buildIteratorPage(store, request),
159
+ },
160
+ };
161
+ }
162
+ } catch (error) {
163
+ return createErrorResponse(error);
164
+ }
165
+ }
166
+
167
+ async function createBrokerHandle(
168
+ options: CliAuthorityBrokerServerOptions
169
+ ): Promise<CliAuthorityBrokerServerHandle> {
170
+ const target = resolveBrokerListenTarget(options.endpoint);
171
+ if (target.kind === "unix") {
172
+ ensureParentDir(target.path);
173
+ removeFileIfPresent(target.path);
174
+ }
175
+
176
+ const store = await options.createStore();
177
+ await store.open();
178
+
179
+ const server = createServer((socket) => {
180
+ bindSocketRequestHandler({ socket, store });
181
+ });
182
+
183
+ if ((options.transportMode ?? "socket") === "socket") {
184
+ try {
185
+ await new Promise<void>((resolve, reject) => {
186
+ server.once("error", reject);
187
+ if (target.kind === "unix") {
188
+ server.listen(target.path, () => {
189
+ server.off("error", reject);
190
+ resolve();
191
+ });
192
+ return;
193
+ }
194
+ server.listen(target.port, target.host, () => {
195
+ server.off("error", reject);
196
+ resolve();
197
+ });
198
+ });
199
+ server.unref();
200
+ } catch (error) {
201
+ await store.close().catch(() => {});
202
+ throw error;
203
+ }
204
+ }
205
+
206
+ const now = options.now ?? Date.now;
207
+ const listeningEndpoint = (options.transportMode ?? "socket") === "socket"
208
+ ? buildListeningEndpoint(server, target)
209
+ : options.endpoint;
210
+ writeJsonFile(options.metadataPath, {
211
+ pid: process.pid,
212
+ endpoint: listeningEndpoint,
213
+ location: store.location ?? listeningEndpoint,
214
+ startedAt: new Date(now()).toISOString(),
215
+ });
216
+ writeJsonFile(options.healthPath, {
217
+ ok: true,
218
+ pid: process.pid,
219
+ endpoint: listeningEndpoint,
220
+ checkedAt: new Date(now()).toISOString(),
221
+ });
222
+
223
+ let closed = false;
224
+ return {
225
+ endpoint: options.endpoint,
226
+ listeningEndpoint,
227
+ authorityStore: store,
228
+ request(request: CliAuthorityBrokerRequest) {
229
+ return dispatchCliAuthorityBrokerRequest(store, request);
230
+ },
231
+ async close() {
232
+ if (closed) return;
233
+ closed = true;
234
+ brokerRegistry.delete(options.endpoint);
235
+ if ((options.transportMode ?? "socket") === "socket") {
236
+ await new Promise<void>((resolve, reject) => {
237
+ server.close((error) => error ? reject(error) : resolve());
238
+ });
239
+ }
240
+ await store.close();
241
+ if (target.kind === "unix") {
242
+ removeFileIfPresent(target.path);
243
+ }
244
+ removeFileIfPresent(options.metadataPath);
245
+ removeFileIfPresent(options.healthPath);
246
+ },
247
+ };
248
+ }
249
+
250
+ function bindSocketRequestHandler(args: { socket: Socket; store: AuthorityStore }) {
251
+ let buffer = "";
252
+ args.socket.setEncoding("utf8");
253
+ args.socket.on("data", async (chunk) => {
254
+ buffer += chunk;
255
+ if (!buffer.includes("\n")) return;
256
+ const [rawRequest] = buffer.split("\n");
257
+ buffer = "";
258
+ let response: CliAuthorityBrokerResponse;
259
+ try {
260
+ response = await dispatchCliAuthorityBrokerRequest(
261
+ args.store,
262
+ JSON.parse(rawRequest) as CliAuthorityBrokerRequest
263
+ );
264
+ } catch (error) {
265
+ response = createErrorResponse(error);
266
+ }
267
+ args.socket.end(`${JSON.stringify(response)}\n`);
268
+ });
269
+ }
270
+
271
+ export function getOrCreateCliAuthorityBrokerServer(
272
+ options: CliAuthorityBrokerServerOptions
273
+ ): Promise<CliAuthorityBrokerServerHandle> {
274
+ const existing = brokerRegistry.get(options.endpoint);
275
+ if (existing) return existing;
276
+
277
+ const pending = createBrokerHandle(options).catch((error) => {
278
+ brokerRegistry.delete(options.endpoint);
279
+ throw error;
280
+ });
281
+ brokerRegistry.set(options.endpoint, pending);
282
+ return pending;
283
+ }
@@ -0,0 +1,43 @@
1
+ import type {
2
+ AuthorityBatchOperation,
3
+ AuthorityIteratorOptions,
4
+ } from "./authorityStoreTypes";
5
+
6
+ export type CliAuthorityBrokerRequest =
7
+ | { type: "status" }
8
+ | { type: "open" }
9
+ | { type: "close" }
10
+ | { type: "get"; key: string }
11
+ | { type: "put"; key: string; value: unknown }
12
+ | { type: "del"; key: string }
13
+ | { type: "batchWrite"; ops: AuthorityBatchOperation[] }
14
+ | {
15
+ type: "iterator";
16
+ options?: AuthorityIteratorOptions;
17
+ cursor?: string | null;
18
+ limit?: number;
19
+ };
20
+
21
+ export type CliAuthorityBrokerIteratorPage = {
22
+ entries: Array<[string, unknown]>;
23
+ nextCursor: string | null;
24
+ done: boolean;
25
+ };
26
+
27
+ export type CliAuthorityBrokerSuccessResponse =
28
+ | { ok: true; result: { type: "status" | "open" | "close" | "put" | "del" | "batchWrite" } }
29
+ | { ok: true; result: { type: "get"; value: unknown } }
30
+ | { ok: true; result: { type: "iterator"; page: CliAuthorityBrokerIteratorPage } };
31
+
32
+ export type CliAuthorityBrokerErrorResponse = {
33
+ ok: false;
34
+ error: {
35
+ code: string;
36
+ message: string;
37
+ retryable?: boolean;
38
+ };
39
+ };
40
+
41
+ export type CliAuthorityBrokerResponse =
42
+ | CliAuthorityBrokerSuccessResponse
43
+ | CliAuthorityBrokerErrorResponse;
@@ -0,0 +1,98 @@
1
+ import path from "node:path";
2
+
3
+ import { resolveNoloHome, type ResolveServerDbPathOptions } from "./dbPath";
4
+
5
+ export type CliAuthorityStoreDriver = "broker";
6
+ export type CliAuthorityBrokerTransport = "tcp" | "unix";
7
+
8
+ type EnvLike = Record<string, string | undefined>;
9
+
10
+ type CliAuthorityStoreDriverOptions = ResolveServerDbPathOptions & {
11
+ env?: EnvLike;
12
+ };
13
+
14
+ type CliAuthorityBrokerOptions = CliAuthorityStoreDriverOptions & {
15
+ transport: CliAuthorityBrokerTransport;
16
+ };
17
+
18
+ function readCliAuthorityDriver(
19
+ env: EnvLike
20
+ ): { driver: CliAuthorityStoreDriver; invalidDriver: string | null } {
21
+ const value = env.NOLO_CLI_AUTHORITY_DRIVER?.trim();
22
+ if (value === "broker") {
23
+ return { driver: "broker", invalidDriver: null };
24
+ }
25
+ return {
26
+ driver: "broker",
27
+ invalidDriver: value ? value : null,
28
+ };
29
+ }
30
+
31
+ function resolveCliAuthorityRunDir(options: CliAuthorityStoreDriverOptions = {}) {
32
+ return path.join(resolveNoloHome({
33
+ ...options,
34
+ env: options.env ?? {},
35
+ }), "run");
36
+ }
37
+
38
+ function hashString(value: string) {
39
+ let hash = 0;
40
+ for (let index = 0; index < value.length; index += 1) {
41
+ hash = ((hash << 5) - hash + value.charCodeAt(index)) | 0;
42
+ }
43
+ return Math.abs(hash);
44
+ }
45
+
46
+ export function resolveCliAuthorityBrokerPort(
47
+ options: CliAuthorityStoreDriverOptions = {}
48
+ ) {
49
+ const envPort = options.env?.NOLO_CLI_AUTHORITY_BROKER_PORT?.trim();
50
+ if (envPort) {
51
+ const port = Number(envPort);
52
+ if (Number.isInteger(port) && port > 0 && port < 65536) return port;
53
+ }
54
+ const noloHome = resolveNoloHome({
55
+ ...options,
56
+ env: options.env ?? {},
57
+ });
58
+ return 47000 + (hashString(noloHome) % 2000);
59
+ }
60
+
61
+ export function resolveCliAuthorityStoreDriver(
62
+ options: CliAuthorityStoreDriverOptions = {}
63
+ ): CliAuthorityStoreDriver {
64
+ return readCliAuthorityDriver(options.env ?? {}).driver;
65
+ }
66
+
67
+ export function resolveCliAuthorityStoreDriverConfig(
68
+ options: CliAuthorityStoreDriverOptions = {}
69
+ ) {
70
+ return readCliAuthorityDriver(options.env ?? {});
71
+ }
72
+
73
+ export function resolveCliAuthorityBrokerSocketPath(
74
+ options: CliAuthorityBrokerOptions
75
+ ) {
76
+ return path.join(resolveCliAuthorityRunDir(options), "authority-store-broker.sock");
77
+ }
78
+
79
+ export function resolveCliAuthorityBrokerEndpoint(
80
+ options: CliAuthorityBrokerOptions
81
+ ) {
82
+ if (options.transport === "tcp") {
83
+ return `tcp://127.0.0.1:${resolveCliAuthorityBrokerPort(options)}`;
84
+ }
85
+ return `unix://${resolveCliAuthorityBrokerSocketPath(options)}`;
86
+ }
87
+
88
+ export function resolveCliAuthorityBrokerMetadataPath(
89
+ options: CliAuthorityBrokerOptions
90
+ ) {
91
+ return path.join(resolveCliAuthorityRunDir(options), "authority-store-broker.json");
92
+ }
93
+
94
+ export function resolveCliAuthorityBrokerHealthPath(
95
+ options: CliAuthorityBrokerOptions
96
+ ) {
97
+ return path.join(resolveCliAuthorityRunDir(options), "authority-store-broker.health.json");
98
+ }