nolo-cli 0.1.34 → 0.1.36
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/README.md +11 -1
- package/agent-runtime/index.ts +2 -0
- package/agent-runtime/localToolPolicy.ts +1 -1
- package/agent-runtime/localWorkspaceTools.ts +5 -2
- package/agent-runtime/platformChatProvider.ts +17 -9
- package/agent-runtime/providerResolution.ts +71 -13
- package/agentCommandSupport.ts +4 -0
- package/agentListCommands.ts +73 -11
- package/agentListHelpers.ts +56 -1
- package/agentRecordHelpers.ts +45 -5
- package/cli/agentCommandSupport.ts +4 -0
- package/cli/agentListCommands.ts +73 -11
- package/cli/agentListHelpers.ts +56 -1
- package/cli/agentRecordHelpers.ts +45 -5
- package/cli/cliSpaceHelpers.ts +39 -0
- package/cli/client/localRuntimeAdapter.ts +86 -2
- package/cli/connectorWebSocketTarget.ts +10 -3
- package/cli/dialogCommands.ts +446 -0
- package/cli/dialogInternalCommandEntries.ts +13 -0
- package/cli/globalRecordOperations.ts +258 -0
- package/cli/internalCommandEntries.ts +2 -0
- package/cli/localRuntimeAuthority.ts +182 -0
- package/cli/localRuntimeDb.ts +2 -4
- package/cli/machineWsRunDispatch.ts +42 -3
- package/cli/machineWsSession.ts +2 -0
- package/cli/runtimeDoctorCommands.ts +55 -5
- package/cliSpaceHelpers.ts +39 -0
- package/client/localRuntimeAdapter.ts +86 -2
- package/connectorWebSocketTarget.ts +10 -3
- package/database/server/cliAuthorityBrokerClient.ts +227 -0
- package/database/server/cliAuthorityBrokerServer.ts +283 -0
- package/database/server/cliAuthorityBrokerTypes.ts +43 -0
- package/database/server/cliAuthorityStoreDriver.ts +98 -0
- package/database/server/levelAuthorityStore.ts +46 -26
- package/database/tombstones.ts +52 -0
- package/database/userDataMerge.ts +95 -0
- package/dialogCommands.ts +446 -0
- package/dialogInternalCommandEntries.ts +13 -0
- package/globalRecordOperations.ts +258 -0
- package/internalCommandEntries.ts +2 -0
- package/localRuntimeAuthority.ts +182 -0
- package/localRuntimeDb.ts +2 -4
- package/machineWsRunDispatch.ts +42 -3
- package/machineWsSession.ts +2 -0
- package/package.json +18 -6
- package/runtimeDoctorCommands.ts +55 -5
- package/database/server/db.ts +0 -99
- package/database/server/ensureDbOpen.ts +0 -12
- package/database/server/memoryAuthorityStore.ts +0 -133
- package/database/server/serverStoreFactory.ts +0 -118
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CommandEntry } from "./cliCommandTypes";
|
|
2
|
+
import { createEnvCommand } from "./cliCommandFactories";
|
|
3
|
+
import {
|
|
4
|
+
runDialogDeleteCommand,
|
|
5
|
+
runDialogListCommand,
|
|
6
|
+
} from "./dialogCommands";
|
|
7
|
+
|
|
8
|
+
export function getDialogInternalCommandEntries(): CommandEntry[] {
|
|
9
|
+
return [
|
|
10
|
+
createEnvCommand(["dialog", "list"], "List dialogs", runDialogListCommand),
|
|
11
|
+
createEnvCommand(["dialog", "delete"], "Delete a dialog", runDialogDeleteCommand),
|
|
12
|
+
];
|
|
13
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deleteDbRecord,
|
|
3
|
+
queryUserRecords,
|
|
4
|
+
readDbRecord,
|
|
5
|
+
} from "./agentRecordHelpers";
|
|
6
|
+
import { isTombstoneRecord } from "./database/tombstones";
|
|
7
|
+
import { mergeAndDedupUserData } from "./database/userDataMerge";
|
|
8
|
+
|
|
9
|
+
export type GlobalRecordFailure = {
|
|
10
|
+
serverUrl: string;
|
|
11
|
+
error: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type GlobalDeleteResult = {
|
|
15
|
+
serverUrl: string;
|
|
16
|
+
ok: boolean;
|
|
17
|
+
result?: any;
|
|
18
|
+
error?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type GlobalRecordTarget = {
|
|
22
|
+
serverUrl: string;
|
|
23
|
+
authToken?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type DbDeleteOptions = {
|
|
27
|
+
type?: "single" | "table" | "messages";
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export async function listUserRecordsFromServers(args: {
|
|
31
|
+
authToken: string;
|
|
32
|
+
fallbackFetchImpl?: typeof fetch;
|
|
33
|
+
fetchImpl: typeof fetch;
|
|
34
|
+
limit?: number;
|
|
35
|
+
serverUrls: string[];
|
|
36
|
+
summary?: boolean;
|
|
37
|
+
type: string | string[];
|
|
38
|
+
userId: string;
|
|
39
|
+
label?: string;
|
|
40
|
+
}) {
|
|
41
|
+
const remoteResults: Array<{ data: { data: any[] } }> = [];
|
|
42
|
+
const failures: GlobalRecordFailure[] = [];
|
|
43
|
+
|
|
44
|
+
for (const serverUrl of args.serverUrls) {
|
|
45
|
+
try {
|
|
46
|
+
const records = await queryUserRecords({
|
|
47
|
+
authToken: args.authToken,
|
|
48
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
49
|
+
fetchImpl: args.fetchImpl,
|
|
50
|
+
includeDeleted: true,
|
|
51
|
+
limit: args.limit,
|
|
52
|
+
serverUrl,
|
|
53
|
+
summary: args.summary,
|
|
54
|
+
type: args.type,
|
|
55
|
+
userId: args.userId,
|
|
56
|
+
});
|
|
57
|
+
remoteResults.push({
|
|
58
|
+
data: {
|
|
59
|
+
data: records.map((record: any) => ({
|
|
60
|
+
...record,
|
|
61
|
+
serverOrigin:
|
|
62
|
+
typeof record?.serverOrigin === "string" && record.serverOrigin.trim()
|
|
63
|
+
? record.serverOrigin
|
|
64
|
+
: serverUrl,
|
|
65
|
+
})),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
} catch (error) {
|
|
69
|
+
failures.push({
|
|
70
|
+
serverUrl,
|
|
71
|
+
error: error instanceof Error ? error.message : String(error),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (remoteResults.length === 0 && failures.length > 0) {
|
|
77
|
+
const label = args.label ?? "record query";
|
|
78
|
+
throw new Error(
|
|
79
|
+
`${label} failed on all candidate servers: ${failures
|
|
80
|
+
.map((failure) => `${failure.serverUrl}: ${failure.error}`)
|
|
81
|
+
.join("; ")}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const merged = mergeAndDedupUserData([], remoteResults, { includeDeleted: true });
|
|
86
|
+
return {
|
|
87
|
+
records: merged.filter((record) => !isTombstoneRecord(record)),
|
|
88
|
+
failures,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function readDbRecordFromServers(args: {
|
|
93
|
+
authToken: string;
|
|
94
|
+
dbKey: string;
|
|
95
|
+
fallbackFetchImpl?: typeof fetch;
|
|
96
|
+
fetchImpl: typeof fetch;
|
|
97
|
+
includeDeleted?: boolean;
|
|
98
|
+
serverUrls: string[];
|
|
99
|
+
label?: string;
|
|
100
|
+
}) {
|
|
101
|
+
const failures: GlobalRecordFailure[] = [];
|
|
102
|
+
for (const serverUrl of args.serverUrls) {
|
|
103
|
+
try {
|
|
104
|
+
const record = await readDbRecord({
|
|
105
|
+
authToken: args.authToken,
|
|
106
|
+
dbKey: args.dbKey,
|
|
107
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
108
|
+
fetchImpl: args.fetchImpl,
|
|
109
|
+
includeDeleted: args.includeDeleted,
|
|
110
|
+
serverUrl,
|
|
111
|
+
});
|
|
112
|
+
return { record, serverUrl, failures };
|
|
113
|
+
} catch (error) {
|
|
114
|
+
failures.push({
|
|
115
|
+
serverUrl,
|
|
116
|
+
error: error instanceof Error ? error.message : String(error),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const label = args.label ?? `record ${args.dbKey}`;
|
|
122
|
+
throw new Error(
|
|
123
|
+
`${label} is unreadable on all candidate servers: ${failures
|
|
124
|
+
.map((failure) => `${failure.serverUrl}: ${failure.error}`)
|
|
125
|
+
.join("; ")}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function readDbRecordVersionsFromServers(args: {
|
|
130
|
+
authToken: string;
|
|
131
|
+
dbKey: string;
|
|
132
|
+
fallbackFetchImpl?: typeof fetch;
|
|
133
|
+
fetchImpl: typeof fetch;
|
|
134
|
+
serverUrls: string[];
|
|
135
|
+
}) {
|
|
136
|
+
const records: any[] = [];
|
|
137
|
+
const failures: GlobalRecordFailure[] = [];
|
|
138
|
+
for (const serverUrl of args.serverUrls) {
|
|
139
|
+
try {
|
|
140
|
+
const record = await readDbRecord({
|
|
141
|
+
authToken: args.authToken,
|
|
142
|
+
dbKey: args.dbKey,
|
|
143
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
144
|
+
fetchImpl: args.fetchImpl,
|
|
145
|
+
includeDeleted: true,
|
|
146
|
+
serverUrl,
|
|
147
|
+
});
|
|
148
|
+
records.push({
|
|
149
|
+
...record,
|
|
150
|
+
dbKey: typeof record?.dbKey === "string" && record.dbKey.trim()
|
|
151
|
+
? record.dbKey
|
|
152
|
+
: args.dbKey,
|
|
153
|
+
serverOrigin:
|
|
154
|
+
typeof record?.serverOrigin === "string" && record.serverOrigin.trim()
|
|
155
|
+
? record.serverOrigin
|
|
156
|
+
: serverUrl,
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
failures.push({
|
|
160
|
+
serverUrl,
|
|
161
|
+
error: error instanceof Error ? error.message : String(error),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { records, failures };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function readLiveDbRecordAfterTombstoneMerge(args: {
|
|
169
|
+
authToken: string;
|
|
170
|
+
dbKey: string;
|
|
171
|
+
fallbackFetchImpl?: typeof fetch;
|
|
172
|
+
fetchImpl: typeof fetch;
|
|
173
|
+
serverUrls: string[];
|
|
174
|
+
label?: string;
|
|
175
|
+
}) {
|
|
176
|
+
const result = await readDbRecordVersionsFromServers(args);
|
|
177
|
+
const merged = mergeAndDedupUserData(
|
|
178
|
+
[],
|
|
179
|
+
[{ data: { data: result.records } }],
|
|
180
|
+
{ includeDeleted: true }
|
|
181
|
+
);
|
|
182
|
+
const record = merged.find((item) => item?.dbKey === args.dbKey);
|
|
183
|
+
if (record && !isTombstoneRecord(record)) {
|
|
184
|
+
return {
|
|
185
|
+
record,
|
|
186
|
+
serverUrl: typeof record.serverOrigin === "string" ? record.serverOrigin : "",
|
|
187
|
+
failures: result.failures,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const label = args.label ?? `record ${args.dbKey}`;
|
|
192
|
+
const failureText = result.failures.length
|
|
193
|
+
? ` Failures: ${result.failures
|
|
194
|
+
.map((failure) => `${failure.serverUrl}: ${failure.error}`)
|
|
195
|
+
.join("; ")}`
|
|
196
|
+
: "";
|
|
197
|
+
throw new Error(`${label} is deleted or unreadable after global tombstone merge.${failureText}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export async function recordExistsAfterTombstoneMerge(args: {
|
|
201
|
+
authToken: string;
|
|
202
|
+
dbKey: string;
|
|
203
|
+
fallbackFetchImpl?: typeof fetch;
|
|
204
|
+
fetchImpl: typeof fetch;
|
|
205
|
+
serverUrls: string[];
|
|
206
|
+
}) {
|
|
207
|
+
try {
|
|
208
|
+
await readLiveDbRecordAfterTombstoneMerge(args);
|
|
209
|
+
return true;
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export async function deleteDbRecordOnServers(args: {
|
|
216
|
+
authToken: string;
|
|
217
|
+
dbKey: string;
|
|
218
|
+
deleteOptions?: DbDeleteOptions;
|
|
219
|
+
fallbackFetchImpl?: typeof fetch;
|
|
220
|
+
fetchImpl: typeof fetch;
|
|
221
|
+
serverUrls: string[];
|
|
222
|
+
}): Promise<GlobalDeleteResult[]> {
|
|
223
|
+
return deleteDbRecordOnTargets({
|
|
224
|
+
...args,
|
|
225
|
+
targets: args.serverUrls.map((serverUrl) => ({ serverUrl })),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export async function deleteDbRecordOnTargets(args: {
|
|
230
|
+
authToken: string;
|
|
231
|
+
dbKey: string;
|
|
232
|
+
deleteOptions?: DbDeleteOptions;
|
|
233
|
+
fallbackFetchImpl?: typeof fetch;
|
|
234
|
+
fetchImpl: typeof fetch;
|
|
235
|
+
targets: GlobalRecordTarget[];
|
|
236
|
+
}): Promise<GlobalDeleteResult[]> {
|
|
237
|
+
const results: GlobalDeleteResult[] = [];
|
|
238
|
+
for (const target of args.targets) {
|
|
239
|
+
try {
|
|
240
|
+
const result = await deleteDbRecord({
|
|
241
|
+
authToken: target.authToken ?? args.authToken,
|
|
242
|
+
dbKey: args.dbKey,
|
|
243
|
+
deleteOptions: args.deleteOptions,
|
|
244
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
245
|
+
fetchImpl: args.fetchImpl,
|
|
246
|
+
serverUrl: target.serverUrl,
|
|
247
|
+
});
|
|
248
|
+
results.push({ serverUrl: target.serverUrl, ok: true, result });
|
|
249
|
+
} catch (error) {
|
|
250
|
+
results.push({
|
|
251
|
+
serverUrl: target.serverUrl,
|
|
252
|
+
ok: false,
|
|
253
|
+
error: error instanceof Error ? error.message : String(error),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return results;
|
|
258
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getAgentInternalCommandEntries } from "./agentInternalCommandEntries";
|
|
2
|
+
import { getDialogInternalCommandEntries } from "./dialogInternalCommandEntries";
|
|
2
3
|
import type { CommandEntry } from "./cliCommandTypes";
|
|
3
4
|
import { getSystemInternalCommandEntries } from "./systemInternalCommandEntries";
|
|
4
5
|
import { getWorkflowInternalCommandEntries } from "./workflowInternalCommandEntries";
|
|
@@ -6,6 +7,7 @@ import { getWorkflowInternalCommandEntries } from "./workflowInternalCommandEntr
|
|
|
6
7
|
export function getInternalCommandEntries(renderHelpText: () => string): CommandEntry[] {
|
|
7
8
|
return [
|
|
8
9
|
...getAgentInternalCommandEntries(),
|
|
10
|
+
...getDialogInternalCommandEntries(),
|
|
9
11
|
...getWorkflowInternalCommandEntries(),
|
|
10
12
|
...getSystemInternalCommandEntries(renderHelpText),
|
|
11
13
|
];
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
createCliAuthorityBrokerClient,
|
|
6
|
+
createCliAuthorityBrokerSocketInvoker,
|
|
7
|
+
isCliAuthorityBrokerUnavailableError,
|
|
8
|
+
} from "./database/server/cliAuthorityBrokerClient";
|
|
9
|
+
import type { CliAuthorityBrokerClientOptions } from "./database/server/cliAuthorityBrokerClient";
|
|
10
|
+
import {
|
|
11
|
+
getOrCreateCliAuthorityBrokerServer,
|
|
12
|
+
} from "./database/server/cliAuthorityBrokerServer";
|
|
13
|
+
import type { CliAuthorityBrokerServerOptions } from "./database/server/cliAuthorityBrokerServer";
|
|
14
|
+
import {
|
|
15
|
+
resolveCliAuthorityBrokerEndpoint,
|
|
16
|
+
resolveCliAuthorityBrokerHealthPath,
|
|
17
|
+
resolveCliAuthorityBrokerMetadataPath,
|
|
18
|
+
} from "./database/server/cliAuthorityStoreDriver";
|
|
19
|
+
import { resolveNoloHome } from "./database/server/dbPath";
|
|
20
|
+
import type { AuthorityStore } from "./database/server/authorityStoreTypes";
|
|
21
|
+
import { createLevelAuthorityStore } from "./database/server/levelAuthorityStore";
|
|
22
|
+
import { createLegacyServerDb } from "./database/server/legacyServerDb";
|
|
23
|
+
|
|
24
|
+
type EnvLike = Record<string, string | undefined>;
|
|
25
|
+
|
|
26
|
+
type ResolveCliLocalRuntimeAuthorityOptions = {
|
|
27
|
+
env?: EnvLike;
|
|
28
|
+
homeDir?: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function ensureDbParentDir(dbPath: string) {
|
|
32
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isCliAuthorityLockError(error: unknown) {
|
|
36
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
37
|
+
return /LEVEL_LOCKED|Resource temporarily unavailable|\/LOCK|LOCK:|Database failed to open|EADDRINUSE|Failed to listen/i.test(message);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function sleep(ms: number) {
|
|
41
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveCliLocalRuntimeDbPath(options: ResolveCliLocalRuntimeAuthorityOptions = {}) {
|
|
45
|
+
return path.join(
|
|
46
|
+
resolveNoloHome({
|
|
47
|
+
env: options.env,
|
|
48
|
+
homeDir: options.homeDir,
|
|
49
|
+
}),
|
|
50
|
+
"data",
|
|
51
|
+
"leveldb"
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type CliAuthorityBrokerConnectDeps = {
|
|
56
|
+
createClient: (options: CliAuthorityBrokerClientOptions) => AuthorityStore;
|
|
57
|
+
startBroker: (options: CliAuthorityBrokerServerOptions) => Promise<unknown>;
|
|
58
|
+
sleep: (ms: number) => Promise<void>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const defaultCliAuthorityBrokerConnectDeps: CliAuthorityBrokerConnectDeps = {
|
|
62
|
+
createClient: (options) => createCliAuthorityBrokerClient(options),
|
|
63
|
+
startBroker: (options) => getOrCreateCliAuthorityBrokerServer(options),
|
|
64
|
+
sleep,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export async function connectCliAuthorityBroker(args: {
|
|
68
|
+
endpoint: string;
|
|
69
|
+
metadataPath: string;
|
|
70
|
+
healthPath: string;
|
|
71
|
+
dbPath: string;
|
|
72
|
+
deps?: Partial<CliAuthorityBrokerConnectDeps>;
|
|
73
|
+
}) {
|
|
74
|
+
const deps: CliAuthorityBrokerConnectDeps = {
|
|
75
|
+
...defaultCliAuthorityBrokerConnectDeps,
|
|
76
|
+
...args.deps,
|
|
77
|
+
};
|
|
78
|
+
const client = deps.createClient({
|
|
79
|
+
endpoint: args.endpoint,
|
|
80
|
+
invoke: createCliAuthorityBrokerSocketInvoker({ endpoint: args.endpoint }),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
async function attachToExistingBroker(attempts: number) {
|
|
84
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
85
|
+
await deps.sleep(100);
|
|
86
|
+
try {
|
|
87
|
+
await client.open();
|
|
88
|
+
return true;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (!isCliAuthorityBrokerUnavailableError(error)) throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await deps.startBroker({
|
|
98
|
+
endpoint: args.endpoint,
|
|
99
|
+
metadataPath: args.metadataPath,
|
|
100
|
+
healthPath: args.healthPath,
|
|
101
|
+
createStore: () => createLevelAuthorityStore(args.dbPath),
|
|
102
|
+
});
|
|
103
|
+
await client.open();
|
|
104
|
+
return client;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (!isCliAuthorityLockError(error)) throw error;
|
|
107
|
+
|
|
108
|
+
if (await attachToExistingBroker(5)) {
|
|
109
|
+
return client;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let lastError: unknown = error;
|
|
113
|
+
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
114
|
+
await deps.sleep(100);
|
|
115
|
+
try {
|
|
116
|
+
await deps.startBroker({
|
|
117
|
+
endpoint: args.endpoint,
|
|
118
|
+
metadataPath: args.metadataPath,
|
|
119
|
+
healthPath: args.healthPath,
|
|
120
|
+
createStore: () => createLevelAuthorityStore(args.dbPath),
|
|
121
|
+
});
|
|
122
|
+
await client.open();
|
|
123
|
+
return client;
|
|
124
|
+
} catch (retryError) {
|
|
125
|
+
lastError = retryError;
|
|
126
|
+
if (isCliAuthorityLockError(retryError)) {
|
|
127
|
+
if (await attachToExistingBroker(2)) {
|
|
128
|
+
return client;
|
|
129
|
+
}
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (isCliAuthorityBrokerUnavailableError(retryError)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
throw retryError;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
throw new Error(`CLI authority broker could not attach or take ownership for ${args.endpoint}`, {
|
|
139
|
+
cause: lastError,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function getDefaultCliLocalRuntimeAuthority(
|
|
146
|
+
options: ResolveCliLocalRuntimeAuthorityOptions = {}
|
|
147
|
+
) {
|
|
148
|
+
const env = options.env ?? process.env;
|
|
149
|
+
const dbPath = resolveCliLocalRuntimeDbPath({
|
|
150
|
+
env,
|
|
151
|
+
homeDir: options.homeDir,
|
|
152
|
+
});
|
|
153
|
+
ensureDbParentDir(dbPath);
|
|
154
|
+
const endpoint = resolveCliAuthorityBrokerEndpoint({
|
|
155
|
+
transport: "tcp",
|
|
156
|
+
env,
|
|
157
|
+
homeDir: options.homeDir,
|
|
158
|
+
});
|
|
159
|
+
const metadataPath = resolveCliAuthorityBrokerMetadataPath({
|
|
160
|
+
transport: "tcp",
|
|
161
|
+
env,
|
|
162
|
+
homeDir: options.homeDir,
|
|
163
|
+
});
|
|
164
|
+
const healthPath = resolveCliAuthorityBrokerHealthPath({
|
|
165
|
+
transport: "tcp",
|
|
166
|
+
env,
|
|
167
|
+
homeDir: options.homeDir,
|
|
168
|
+
});
|
|
169
|
+
return connectCliAuthorityBroker({
|
|
170
|
+
endpoint,
|
|
171
|
+
metadataPath,
|
|
172
|
+
healthPath,
|
|
173
|
+
dbPath,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export async function getDefaultCliLocalRuntimeDb(
|
|
178
|
+
options: ResolveCliLocalRuntimeAuthorityOptions = {}
|
|
179
|
+
) {
|
|
180
|
+
const authorityStore = await getDefaultCliLocalRuntimeAuthority(options);
|
|
181
|
+
return createLegacyServerDb(authorityStore);
|
|
182
|
+
}
|
package/localRuntimeDb.ts
CHANGED
|
@@ -21,8 +21,6 @@ export function buildCliLocalRuntimeDbEnv(env: EnvLike = process.env) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export async function getDefaultCliLocalRuntimeDb(): Promise<CliLocalRuntimeDb> {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
await ensureServerDbOpen();
|
|
27
|
-
return serverDb as CliLocalRuntimeDb;
|
|
24
|
+
const { getDefaultCliLocalRuntimeDb: getAuthorityBackedDb } = await import("./localRuntimeAuthority");
|
|
25
|
+
return getAuthorityBackedDb();
|
|
28
26
|
}
|
package/machineWsRunDispatch.ts
CHANGED
|
@@ -96,6 +96,37 @@ function logConnectorRun(message: string, fields: Record<string, unknown> = {})
|
|
|
96
96
|
console.error(`[nolo-connector] ${message} ${JSON.stringify(fields)}`);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
function summarizeEndpoint(value: unknown) {
|
|
100
|
+
if (typeof value !== "string" || !value.trim()) return undefined;
|
|
101
|
+
try {
|
|
102
|
+
const url = new URL(value);
|
|
103
|
+
return `${url.protocol}//${url.host}${url.pathname}`;
|
|
104
|
+
} catch {
|
|
105
|
+
return "invalid-url";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function summarizeAgentConfigForLogs(agentConfig: any) {
|
|
110
|
+
const runtimeBinding = isRecord(agentConfig?.runtimeBinding) ? agentConfig.runtimeBinding : {};
|
|
111
|
+
return {
|
|
112
|
+
apiSource: agentConfig?.apiSource ?? null,
|
|
113
|
+
provider: agentConfig?.provider ?? null,
|
|
114
|
+
cliProvider: agentConfig?.cliProvider ?? null,
|
|
115
|
+
model: agentConfig?.model ?? null,
|
|
116
|
+
customProviderEndpoint: summarizeEndpoint(agentConfig?.customProviderUrl) ?? null,
|
|
117
|
+
useServerProxy: agentConfig?.useServerProxy ?? null,
|
|
118
|
+
skipLocalRuntimeEnsure: agentConfig?.skipLocalRuntimeEnsure ?? null,
|
|
119
|
+
hasApiKey: typeof agentConfig?.apiKey === "string" && agentConfig.apiKey.length > 0,
|
|
120
|
+
apiKeyHeader: agentConfig?.apiKeyHeader ?? null,
|
|
121
|
+
hasApiKeyFromAgentKey:
|
|
122
|
+
typeof agentConfig?.apiKeyFromAgentKey === "string" &&
|
|
123
|
+
agentConfig.apiKeyFromAgentKey.length > 0,
|
|
124
|
+
runtimeBindingKind: runtimeBinding.kind ?? null,
|
|
125
|
+
runtimeMachineId: runtimeBinding.machineId ?? null,
|
|
126
|
+
connectorSurface: runtimeBinding.connectorSurface ?? runtimeBinding.surface ?? null,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
99
130
|
function isRecord(value: unknown): value is Record<string, any> {
|
|
100
131
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
101
132
|
}
|
|
@@ -197,9 +228,6 @@ function mergeToolNames(...values: unknown[]) {
|
|
|
197
228
|
function localRuntimeEnvFromPolicy(runtimeEnv: EnvLike, policy?: ConnectorRuntimePolicy): EnvLike {
|
|
198
229
|
return {
|
|
199
230
|
...runtimeEnv,
|
|
200
|
-
...(policy?.shell?.enabled && policy.shell.mode === "worktree"
|
|
201
|
-
? { NOLO_LOCAL_SHELL_MODE: "worktree" }
|
|
202
|
-
: {}),
|
|
203
231
|
...(policy?.workspace?.cwd ? { NOLO_LOCAL_WORKTREE: policy.workspace.cwd } : {}),
|
|
204
232
|
};
|
|
205
233
|
}
|
|
@@ -572,6 +600,15 @@ export async function handleConnectorRunMessage(
|
|
|
572
600
|
if (!requestsLocalWorkspaceRuntime(runtimePolicy) && !isMachineBoundLocalCustomProvider(agentConfig)) {
|
|
573
601
|
throw new Error("Connector can only execute non-CLI agents when runtimeToolPolicySnapshot requests a local workspace runtime.");
|
|
574
602
|
}
|
|
603
|
+
logConnectorRun("agent.run.local-runtime.start", {
|
|
604
|
+
requestId: parsed.requestId,
|
|
605
|
+
agentKey: parsed.payload?.agentKey,
|
|
606
|
+
cwd,
|
|
607
|
+
timeout,
|
|
608
|
+
machineBoundLocalCustomProvider: isMachineBoundLocalCustomProvider(agentConfig),
|
|
609
|
+
runtimePolicy: Boolean(runtimePolicy),
|
|
610
|
+
...summarizeAgentConfigForLogs(agentConfig),
|
|
611
|
+
});
|
|
575
612
|
baseSha = await readConnectorGitHead(cwd);
|
|
576
613
|
const result = await runConnectorLocalRuntimeAgent({
|
|
577
614
|
parsed,
|
|
@@ -610,6 +647,8 @@ export async function handleConnectorRunMessage(
|
|
|
610
647
|
logConnectorRun("agent.run.error", {
|
|
611
648
|
requestId: parsed.requestId,
|
|
612
649
|
agentKey: parsed.payload?.agentKey,
|
|
650
|
+
...summarizeAgentConfigForLogs(agentConfig),
|
|
651
|
+
errorName: error instanceof Error ? error.name : null,
|
|
613
652
|
error: error instanceof Error ? error.message : String(error),
|
|
614
653
|
});
|
|
615
654
|
send(JSON.stringify({
|
package/machineWsSession.ts
CHANGED
|
@@ -119,6 +119,7 @@ export async function runMachineWsSession(options: {
|
|
|
119
119
|
resolveConnectorWebSocketTarget: (args: {
|
|
120
120
|
serverUrl: string;
|
|
121
121
|
machineId: string;
|
|
122
|
+
connectorSurface?: string;
|
|
122
123
|
headers: Record<string, string>;
|
|
123
124
|
}) => Promise<string>;
|
|
124
125
|
connectWebSocket: (url: string, options: ConnectorWebSocketOptions) => Promise<void>;
|
|
@@ -149,6 +150,7 @@ export async function runMachineWsSession(options: {
|
|
|
149
150
|
const wsTarget = await options.resolveConnectorWebSocketTarget({
|
|
150
151
|
serverUrl: options.serverUrl,
|
|
151
152
|
machineId: options.machine.machineId,
|
|
153
|
+
connectorSurface: "cli",
|
|
152
154
|
headers: { Authorization: `Bearer ${options.authToken}` },
|
|
153
155
|
});
|
|
154
156
|
websocketPromise = options.connectWebSocket(wsTarget, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nolo-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent-first terminal workspace for Nolo",
|
|
6
6
|
"bin": {
|
|
@@ -27,11 +27,15 @@
|
|
|
27
27
|
"cliCommandDispatch.ts",
|
|
28
28
|
"cliCommandTypes.ts",
|
|
29
29
|
"cliEnvHelpers.ts",
|
|
30
|
+
"cliSpaceHelpers.ts",
|
|
30
31
|
"commandRegistry.ts",
|
|
31
32
|
"connectorRunArtifact.ts",
|
|
32
33
|
"connectorWebSocketTarget.ts",
|
|
33
34
|
"defaultServer.ts",
|
|
34
35
|
"agentInternalCommandEntries.ts",
|
|
36
|
+
"dialogCommands.ts",
|
|
37
|
+
"dialogInternalCommandEntries.ts",
|
|
38
|
+
"globalRecordOperations.ts",
|
|
35
39
|
"internalCommandEntries.ts",
|
|
36
40
|
"machineCommands.ts",
|
|
37
41
|
"offlineMarxistsAgentCommand.ts",
|
|
@@ -86,6 +90,7 @@
|
|
|
86
90
|
"machineWsSession.ts",
|
|
87
91
|
"machineStatusCommands.ts",
|
|
88
92
|
"machineWsPromptMaterialization.ts",
|
|
93
|
+
"localRuntimeAuthority.ts",
|
|
89
94
|
"cli/commandRegistry.ts",
|
|
90
95
|
"cli/client/profileConfig.ts",
|
|
91
96
|
"cli/runtimeModeArgs.ts",
|
|
@@ -97,7 +102,10 @@
|
|
|
97
102
|
"cli/agentCommandSupport.ts",
|
|
98
103
|
"cli/agentListHelpers.ts",
|
|
99
104
|
"cli/agentRecordHelpers.ts",
|
|
105
|
+
"cli/cliSpaceHelpers.ts",
|
|
106
|
+
"cli/globalRecordOperations.ts",
|
|
100
107
|
"cli/client/localRuntimeAdapter.ts",
|
|
108
|
+
"cli/localRuntimeAuthority.ts",
|
|
101
109
|
"cli/agentListCommands.ts",
|
|
102
110
|
"cli/agentMachineCommands.ts",
|
|
103
111
|
"cli/runtimeDoctorCommands.ts",
|
|
@@ -120,7 +128,9 @@
|
|
|
120
128
|
"cli/agentRunCommand.ts",
|
|
121
129
|
"cli/cliCommandFactories.ts",
|
|
122
130
|
"cli/offlineMarxistsAgentCommand.ts",
|
|
131
|
+
"cli/dialogCommands.ts",
|
|
123
132
|
"cli/agentInternalCommandEntries.ts",
|
|
133
|
+
"cli/dialogInternalCommandEntries.ts",
|
|
124
134
|
"cli/systemInternalCommandEntries.ts",
|
|
125
135
|
"cli/workflowInternalCommandEntries.ts",
|
|
126
136
|
"cli/machineDaemonCommands.ts",
|
|
@@ -168,14 +178,16 @@
|
|
|
168
178
|
"connector-experimental/capabilities.ts",
|
|
169
179
|
"connector-experimental/codexBinary.ts",
|
|
170
180
|
"database/server/dbPath.ts",
|
|
171
|
-
"database/server/db.ts",
|
|
172
181
|
"database/config.ts",
|
|
173
|
-
"database/
|
|
174
|
-
"database/
|
|
182
|
+
"database/tombstones.ts",
|
|
183
|
+
"database/userDataMerge.ts",
|
|
184
|
+
"database/server/cliAuthorityStoreDriver.ts",
|
|
185
|
+
"database/server/cliAuthorityBrokerClient.ts",
|
|
186
|
+
"database/server/cliAuthorityBrokerServer.ts",
|
|
175
187
|
"database/server/authorityStoreTypes.ts",
|
|
176
|
-
"database/server/serverStoreFactory.ts",
|
|
177
188
|
"database/server/levelAuthorityStore.ts",
|
|
178
|
-
"database/server/
|
|
189
|
+
"database/server/legacyServerDb.ts",
|
|
190
|
+
"database/server/cliAuthorityBrokerTypes.ts",
|
|
179
191
|
"ai/agent/cliExecutor.ts",
|
|
180
192
|
"ai/skills/skillDocProtocol.ts",
|
|
181
193
|
"ai/tools/agent/taskRunTool.ts",
|