openmeld 0.3.42
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 +315 -0
- package/dist/add-me-membership-B9sBeKPr.js +11 -0
- package/dist/add-me-membership-B9sBeKPr.js.map +1 -0
- package/dist/api-client-foundation-BnsgZrnh.js +384 -0
- package/dist/api-client-foundation-BnsgZrnh.js.map +1 -0
- package/dist/auth-session-_ideKYyk.js +746 -0
- package/dist/auth-session-_ideKYyk.js.map +1 -0
- package/dist/base-url-Bdqmyw0D.js +2327 -0
- package/dist/base-url-Bdqmyw0D.js.map +1 -0
- package/dist/command-DAwIiSbe.js +102198 -0
- package/dist/command-DAwIiSbe.js.map +1 -0
- package/dist/daemon-runtime-lease-B9LdD-he.js +702 -0
- package/dist/daemon-runtime-lease-B9LdD-he.js.map +1 -0
- package/dist/dist-BvAAI13G.js +15925 -0
- package/dist/dist-BvAAI13G.js.map +1 -0
- package/dist/openmeld-dev.js +24 -0
- package/dist/openmeld-dev.js.map +1 -0
- package/dist/openmeld.js +32013 -0
- package/dist/openmeld.js.map +1 -0
- package/dist/runtime-transport-rv43yBPB.js +6391 -0
- package/dist/runtime-transport-rv43yBPB.js.map +1 -0
- package/dist/service-contract-DZWQdPz5.js +39 -0
- package/dist/service-contract-DZWQdPz5.js.map +1 -0
- package/package.json +86 -0
- package/skills/README.md +27 -0
- package/skills/openmeld-cli/SKILL.md +1012 -0
- package/skills/openmeld-cli/playbooks/agent-onboarding.md +185 -0
- package/skills/openmeld-cli/playbooks/space-ops.md +343 -0
- package/skills/openmeld-cli/references/commands.md +666 -0
- package/skills/openmeld-cli/references/runtime-resolution.md +80 -0
|
@@ -0,0 +1,2327 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import "node:module";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { z } from "zod/v4";
|
|
6
|
+
import { basename, dirname, join, normalize, parse, posix, resolve, win32 } from "node:path";
|
|
7
|
+
import process$1 from "node:process";
|
|
8
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
9
|
+
import { lstat, mkdir, open, readFile, readdir, rename, rm, rmdir, unlink, writeFile } from "node:fs/promises";
|
|
10
|
+
import { rmdirSync, unlinkSync } from "node:fs";
|
|
11
|
+
//#region \0rolldown/runtime.js
|
|
12
|
+
var __defProp = Object.defineProperty;
|
|
13
|
+
var __exportAll = (all, no_symbols) => {
|
|
14
|
+
let target = {};
|
|
15
|
+
for (var name in all) __defProp(target, name, {
|
|
16
|
+
get: all[name],
|
|
17
|
+
enumerable: true
|
|
18
|
+
});
|
|
19
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
20
|
+
return target;
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region ../../packages/schemas/dist/schemas/daemon/agent-controller-conversation-capability.js
|
|
24
|
+
const agentControllerConversationExternalImportCapabilitySchema = z.enum(["unsupported", "by_ref_id"]);
|
|
25
|
+
const agentControllerConversationResumeCapabilitySchema = z.enum(["unsupported", "by_ref_id"]);
|
|
26
|
+
const agentControllerConversationDiscoverCapabilitySchema = z.literal("unsupported");
|
|
27
|
+
const agentControllerConversationTurnOverrideCapabilitySchema = z.enum([
|
|
28
|
+
"unsupported",
|
|
29
|
+
"stateless",
|
|
30
|
+
"persistent"
|
|
31
|
+
]);
|
|
32
|
+
z.strictObject({
|
|
33
|
+
discover: agentControllerConversationDiscoverCapabilitySchema,
|
|
34
|
+
externalImport: agentControllerConversationExternalImportCapabilitySchema,
|
|
35
|
+
resume: agentControllerConversationResumeCapabilitySchema,
|
|
36
|
+
turnOverride: agentControllerConversationTurnOverrideCapabilitySchema
|
|
37
|
+
});
|
|
38
|
+
const BUILTIN_AGENT_CONTROLLER_CONVERSATION_CAPABILITY_BY_CONTROLLER_ID = {
|
|
39
|
+
codex: {
|
|
40
|
+
discover: "unsupported",
|
|
41
|
+
externalImport: "by_ref_id",
|
|
42
|
+
resume: "by_ref_id",
|
|
43
|
+
turnOverride: "unsupported"
|
|
44
|
+
},
|
|
45
|
+
"claude-code": {
|
|
46
|
+
discover: "unsupported",
|
|
47
|
+
externalImport: "by_ref_id",
|
|
48
|
+
resume: "by_ref_id",
|
|
49
|
+
turnOverride: "unsupported"
|
|
50
|
+
},
|
|
51
|
+
cursor: {
|
|
52
|
+
discover: "unsupported",
|
|
53
|
+
externalImport: "by_ref_id",
|
|
54
|
+
resume: "by_ref_id",
|
|
55
|
+
turnOverride: "unsupported"
|
|
56
|
+
},
|
|
57
|
+
opencode: {
|
|
58
|
+
discover: "unsupported",
|
|
59
|
+
externalImport: "unsupported",
|
|
60
|
+
resume: "by_ref_id",
|
|
61
|
+
turnOverride: "unsupported"
|
|
62
|
+
},
|
|
63
|
+
openclaw: {
|
|
64
|
+
discover: "unsupported",
|
|
65
|
+
externalImport: "unsupported",
|
|
66
|
+
resume: "by_ref_id",
|
|
67
|
+
turnOverride: "unsupported"
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const resolveBuiltinAgentControllerConversationCapability = (builtinControllerId) => {
|
|
71
|
+
const capability = BUILTIN_AGENT_CONTROLLER_CONVERSATION_CAPABILITY_BY_CONTROLLER_ID[builtinControllerId];
|
|
72
|
+
return capability ? { ...capability } : null;
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region ../../packages/schemas/dist/schemas/daemon/agent-controller-ref.js
|
|
76
|
+
const BUILTIN_AGENT_CONTROLLER_REF_PREFIX = "builtin:";
|
|
77
|
+
const AGENT_CONTROLLER_REF_SEPARATOR = ":";
|
|
78
|
+
const HOSTED_CENTER_AGENT_CONTROLLER_REF = "hosted:center-agent";
|
|
79
|
+
const HOSTED_CENTER_AGENT_CONTROLLER_REF_ID = "center-agent";
|
|
80
|
+
const agentControllerRefSchema = z.string().transform((value, context) => {
|
|
81
|
+
const parsed = parseAgentControllerRef(value);
|
|
82
|
+
if (!parsed) {
|
|
83
|
+
context.addIssue({
|
|
84
|
+
code: "custom",
|
|
85
|
+
message: "Agent Controller ref is invalid"
|
|
86
|
+
});
|
|
87
|
+
return z.NEVER;
|
|
88
|
+
}
|
|
89
|
+
return parsed.ref;
|
|
90
|
+
});
|
|
91
|
+
function normalizeOptionalText(value) {
|
|
92
|
+
if (typeof value !== "string") return null;
|
|
93
|
+
const normalized = value.trim();
|
|
94
|
+
return normalized.length > 0 ? normalized : null;
|
|
95
|
+
}
|
|
96
|
+
function normalizeBuiltinControllerId(agentControllerRef) {
|
|
97
|
+
const normalized = normalizeOptionalText(agentControllerRef);
|
|
98
|
+
if (!normalized) return null;
|
|
99
|
+
if (normalized.includes(":") && !normalized.startsWith(BUILTIN_AGENT_CONTROLLER_REF_PREFIX)) return null;
|
|
100
|
+
const builtinId = normalized.startsWith(BUILTIN_AGENT_CONTROLLER_REF_PREFIX) ? normalized.slice(8) : normalized;
|
|
101
|
+
if (builtinId.length === 0 || builtinId.includes(":")) return null;
|
|
102
|
+
return builtinId;
|
|
103
|
+
}
|
|
104
|
+
function normalizeKnownBuiltinAgentControllerId(agentControllerRef) {
|
|
105
|
+
const builtinId = normalizeBuiltinControllerId(agentControllerRef);
|
|
106
|
+
return builtinId && resolveBuiltinAgentControllerConversationCapability(builtinId) ? builtinId : null;
|
|
107
|
+
}
|
|
108
|
+
function normalizeBuiltinAgentControllerId(agentControllerRef) {
|
|
109
|
+
return normalizeBuiltinControllerId(agentControllerRef);
|
|
110
|
+
}
|
|
111
|
+
function parseAgentControllerRef(agentControllerRef) {
|
|
112
|
+
const normalized = normalizeOptionalText(agentControllerRef);
|
|
113
|
+
if (!normalized) return null;
|
|
114
|
+
if (normalized === "hosted:center-agent") return {
|
|
115
|
+
kind: HOSTED_CENTER_AGENT_CONTROLLER_REF,
|
|
116
|
+
id: HOSTED_CENTER_AGENT_CONTROLLER_REF_ID,
|
|
117
|
+
ref: HOSTED_CENTER_AGENT_CONTROLLER_REF
|
|
118
|
+
};
|
|
119
|
+
for (const kind of ["builtin", "custom"]) {
|
|
120
|
+
const prefix = `${kind}${AGENT_CONTROLLER_REF_SEPARATOR}`;
|
|
121
|
+
if (!normalized.startsWith(prefix)) continue;
|
|
122
|
+
const id = normalized.slice(prefix.length).trim();
|
|
123
|
+
if (id.length === 0 || id.includes(AGENT_CONTROLLER_REF_SEPARATOR)) return null;
|
|
124
|
+
return {
|
|
125
|
+
kind,
|
|
126
|
+
id,
|
|
127
|
+
ref: `${kind}${AGENT_CONTROLLER_REF_SEPARATOR}${id}`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
function canonicalizeAgentControllerRef(agentControllerRef) {
|
|
133
|
+
return parseAgentControllerRef(agentControllerRef)?.ref ?? null;
|
|
134
|
+
}
|
|
135
|
+
function buildAgentControllerRoutingKey(agentControllerRef) {
|
|
136
|
+
return canonicalizeAgentControllerRef(agentControllerRef);
|
|
137
|
+
}
|
|
138
|
+
function canonicalizeBuiltinAgentControllerRef(agentControllerRef) {
|
|
139
|
+
const builtinId = normalizeBuiltinControllerId(agentControllerRef);
|
|
140
|
+
return builtinId ? `${BUILTIN_AGENT_CONTROLLER_REF_PREFIX}${builtinId}` : null;
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region ../../packages/schemas/dist/schemas/daemon/conversation-contract.js
|
|
144
|
+
const agentControllerConversationKindSchema = z.enum([
|
|
145
|
+
"session",
|
|
146
|
+
"thread",
|
|
147
|
+
"context"
|
|
148
|
+
]);
|
|
149
|
+
const trimmedNonEmptyStringSchema = z.string().trim().min(1);
|
|
150
|
+
const agentControllerConversationRefSchema = z.strictObject({
|
|
151
|
+
agentControllerRef: trimmedNonEmptyStringSchema,
|
|
152
|
+
agentControllerConversationKind: agentControllerConversationKindSchema,
|
|
153
|
+
agentControllerConversationId: trimmedNonEmptyStringSchema
|
|
154
|
+
});
|
|
155
|
+
const conversationPolicySchema = z.strictObject({
|
|
156
|
+
collaborationMode: z.enum(["default", "plan"]).optional(),
|
|
157
|
+
fallbackModel: z.string().min(1).optional(),
|
|
158
|
+
model: z.string().min(1).optional(),
|
|
159
|
+
reasoningLevel: z.string().min(1).optional()
|
|
160
|
+
});
|
|
161
|
+
const BUILTIN_AGENT_CONTROLLER_CONVERSATION_KIND_BY_CONTROLLER_ID = {
|
|
162
|
+
codex: "thread",
|
|
163
|
+
"claude-code": "session",
|
|
164
|
+
cursor: "session",
|
|
165
|
+
openclaw: "session",
|
|
166
|
+
opencode: "session"
|
|
167
|
+
};
|
|
168
|
+
const resolveBuiltinAgentControllerConversationKind = (agentControllerRef) => {
|
|
169
|
+
const canonical = canonicalizeBuiltinAgentControllerRef(agentControllerRef);
|
|
170
|
+
if (!canonical) return null;
|
|
171
|
+
const builtinControllerId = canonical.slice(8);
|
|
172
|
+
return BUILTIN_AGENT_CONTROLLER_CONVERSATION_KIND_BY_CONTROLLER_ID[builtinControllerId] ?? null;
|
|
173
|
+
};
|
|
174
|
+
const buildAgentControllerConversationRef = (input) => ({
|
|
175
|
+
agentControllerRef: input.agentControllerRef,
|
|
176
|
+
agentControllerConversationKind: input.agentControllerConversationKind,
|
|
177
|
+
agentControllerConversationId: input.agentControllerConversationId
|
|
178
|
+
});
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region ../../packages/schemas/dist/schemas/daemon/controller-contract.js
|
|
181
|
+
const LEGACY_RUNTIME_DISPATCH_POLICY = "mention_only";
|
|
182
|
+
const runtimeDispatchPolicySchema = z.enum([LEGACY_RUNTIME_DISPATCH_POLICY, "listen_all"]);
|
|
183
|
+
const runtimeTransportModeSchema = z.enum([
|
|
184
|
+
"local-cli",
|
|
185
|
+
"codex-app-server",
|
|
186
|
+
"acp"
|
|
187
|
+
]);
|
|
188
|
+
const runtimeStreamingModeSchema = z.enum(["final", "stream"]);
|
|
189
|
+
const runtimeAgentControllerConversationIdMappingSpecSchema = z.strictObject({
|
|
190
|
+
sourceField: z.string().min(1),
|
|
191
|
+
mappingVersion: z.number().int().min(1)
|
|
192
|
+
});
|
|
193
|
+
const runtimeAgentExecutableLaunchContractSchema = z.strictObject({
|
|
194
|
+
kind: z.literal("executable"),
|
|
195
|
+
commandPath: z.string().min(1),
|
|
196
|
+
argsPrefix: z.array(z.string().min(1)).optional()
|
|
197
|
+
});
|
|
198
|
+
const runtimeAgentNodeScriptLaunchContractSchema = z.strictObject({
|
|
199
|
+
kind: z.literal("node_script"),
|
|
200
|
+
nodePath: z.string().min(1),
|
|
201
|
+
scriptPath: z.string().min(1),
|
|
202
|
+
argsPrefix: z.array(z.string().min(1)).optional()
|
|
203
|
+
});
|
|
204
|
+
const runtimeAgentControllerLaunchContractSchema = z.discriminatedUnion("kind", [runtimeAgentExecutableLaunchContractSchema, runtimeAgentNodeScriptLaunchContractSchema]);
|
|
205
|
+
z.strictObject({
|
|
206
|
+
installId: z.string().min(1),
|
|
207
|
+
label: z.string().min(1),
|
|
208
|
+
detail: z.string().min(1),
|
|
209
|
+
commandPath: z.string().min(1),
|
|
210
|
+
launchContract: runtimeAgentControllerLaunchContractSchema
|
|
211
|
+
});
|
|
212
|
+
const runtimeAgentControllerSettingsSchema = z.strictObject({
|
|
213
|
+
dispatch_policy: runtimeDispatchPolicySchema.default(LEGACY_RUNTIME_DISPATCH_POLICY),
|
|
214
|
+
transport_mode: runtimeTransportModeSchema,
|
|
215
|
+
streaming_mode: runtimeStreamingModeSchema.default("final"),
|
|
216
|
+
timeout_ms: z.number().int().positive().nullable().default(null),
|
|
217
|
+
retry: z.number().int().min(0).default(0),
|
|
218
|
+
context_id_mapping_spec: runtimeAgentControllerConversationIdMappingSpecSchema
|
|
219
|
+
});
|
|
220
|
+
z.strictObject({
|
|
221
|
+
agentControllerConversationId: z.string().min(1).nullable(),
|
|
222
|
+
agentControllerConversation: agentControllerConversationRefSchema.nullable().optional(),
|
|
223
|
+
sourceField: z.string().min(1),
|
|
224
|
+
mappingVersion: z.number().int().min(1)
|
|
225
|
+
});
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region ../../packages/schemas/dist/schemas/local-machine/recovery.js
|
|
228
|
+
const localMachineIssueCodeSchema = z.enum([
|
|
229
|
+
"local_machine.storage.exhausted",
|
|
230
|
+
"local_machine.storage.not_writable",
|
|
231
|
+
"local_machine.permission.denied",
|
|
232
|
+
"local_machine.resources.exhausted",
|
|
233
|
+
"local_machine.background_execution.blocked",
|
|
234
|
+
"local_machine.concurrency.active",
|
|
235
|
+
"local_machine.concurrency.orphaned",
|
|
236
|
+
"local_machine.failure.unknown"
|
|
237
|
+
]);
|
|
238
|
+
const localMachineOperationSchema = z.enum([
|
|
239
|
+
"create_lock",
|
|
240
|
+
"read_local_state",
|
|
241
|
+
"write_local_state",
|
|
242
|
+
"start_background_service",
|
|
243
|
+
"start_process",
|
|
244
|
+
"unknown"
|
|
245
|
+
]);
|
|
246
|
+
const localMachineRecoveryPolicySchema = z.enum([
|
|
247
|
+
"automatic",
|
|
248
|
+
"retry_with_backoff",
|
|
249
|
+
"user_action",
|
|
250
|
+
"diagnostics"
|
|
251
|
+
]);
|
|
252
|
+
const LOCAL_MACHINE_RECOVERY_POLICY_BY_CODE = {
|
|
253
|
+
"local_machine.storage.exhausted": "user_action",
|
|
254
|
+
"local_machine.storage.not_writable": "user_action",
|
|
255
|
+
"local_machine.permission.denied": "user_action",
|
|
256
|
+
"local_machine.resources.exhausted": "user_action",
|
|
257
|
+
"local_machine.background_execution.blocked": "user_action",
|
|
258
|
+
"local_machine.concurrency.active": "user_action",
|
|
259
|
+
"local_machine.concurrency.orphaned": "user_action",
|
|
260
|
+
"local_machine.failure.unknown": "diagnostics"
|
|
261
|
+
};
|
|
262
|
+
function resolveLocalMachineRecoveryPolicy(code) {
|
|
263
|
+
if (!Object.hasOwn(LOCAL_MACHINE_RECOVERY_POLICY_BY_CODE, code)) return null;
|
|
264
|
+
return LOCAL_MACHINE_RECOVERY_POLICY_BY_CODE[code];
|
|
265
|
+
}
|
|
266
|
+
const localMachineUserMessageSchema = z.string().trim().min(1).max(512);
|
|
267
|
+
const localMachineDiagnosticTextSchema = z.string().trim().min(1).max(2048);
|
|
268
|
+
const localMachineAgentMessageSchema = z.string().trim().min(1).max(1025);
|
|
269
|
+
function formatLocalMachineIssueMessage(issue) {
|
|
270
|
+
return issue.primaryAction ? `${issue.summary} ${issue.primaryAction}` : issue.summary;
|
|
271
|
+
}
|
|
272
|
+
const localMachineIssueSchema = z.strictObject({
|
|
273
|
+
schemaVersion: z.literal(1),
|
|
274
|
+
code: localMachineIssueCodeSchema,
|
|
275
|
+
operation: localMachineOperationSchema,
|
|
276
|
+
recoveryPolicy: localMachineRecoveryPolicySchema,
|
|
277
|
+
summary: localMachineUserMessageSchema,
|
|
278
|
+
primaryAction: z.union([localMachineUserMessageSchema, z.null()]),
|
|
279
|
+
diagnostics: z.strictObject({
|
|
280
|
+
systemCode: z.union([z.string().trim().min(1).max(64), z.null()]),
|
|
281
|
+
detail: z.union([localMachineDiagnosticTextSchema, z.null()])
|
|
282
|
+
})
|
|
283
|
+
}).superRefine((value, context) => {
|
|
284
|
+
const expectedPolicy = resolveLocalMachineRecoveryPolicy(value.code);
|
|
285
|
+
if (value.recoveryPolicy !== expectedPolicy) context.addIssue({
|
|
286
|
+
code: "custom",
|
|
287
|
+
message: `recovery policy must be ${expectedPolicy} for ${value.code}`,
|
|
288
|
+
path: ["recoveryPolicy"]
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
z.strictObject({
|
|
292
|
+
type: z.literal("error"),
|
|
293
|
+
code: localMachineIssueCodeSchema,
|
|
294
|
+
message: localMachineAgentMessageSchema,
|
|
295
|
+
requestId: z.string().trim().min(1).max(256).optional(),
|
|
296
|
+
payload: z.strictObject({ localMachineIssue: localMachineIssueSchema })
|
|
297
|
+
}).superRefine((value, context) => {
|
|
298
|
+
if (value.code !== value.payload.localMachineIssue.code) context.addIssue({
|
|
299
|
+
code: "custom",
|
|
300
|
+
message: "agent event code must match local machine issue code",
|
|
301
|
+
path: ["code"]
|
|
302
|
+
});
|
|
303
|
+
const expectedMessage = formatLocalMachineIssueMessage(value.payload.localMachineIssue);
|
|
304
|
+
if (value.message !== expectedMessage) context.addIssue({
|
|
305
|
+
code: "custom",
|
|
306
|
+
message: "agent event message must match local machine issue copy",
|
|
307
|
+
path: ["message"]
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/config/paths.ts
|
|
312
|
+
const MAX_ACCOUNT_PATH_SEGMENT_LENGTH = 128;
|
|
313
|
+
const OPAQUE_CACHE_PATH_SEGMENT_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/u;
|
|
314
|
+
const FALLBACK_ACCOUNT_HOST_KEY = "unauthenticated";
|
|
315
|
+
const FALLBACK_ACCOUNT_USER_KEY = "local";
|
|
316
|
+
const OPENMELD_HOME_ENV_KEY = "OPENMELD_HOME";
|
|
317
|
+
const OPENMELD_ENV_PRESET_ENV_KEY = "OPENMELD_ENV_PRESET";
|
|
318
|
+
const DEFAULT_OPENMELD_ENV_PRESET = "prod";
|
|
319
|
+
const OPENMELD_DEFAULT_HOME_DIR_BY_PRESET = {
|
|
320
|
+
prod: ".openmeld",
|
|
321
|
+
"dev-local": ".openmeld-dev",
|
|
322
|
+
canary: ".openmeld-canary"
|
|
323
|
+
};
|
|
324
|
+
const OPENMELD_ENV_PRESET_BY_ENTRY_NAME = {
|
|
325
|
+
openmeld: "prod",
|
|
326
|
+
"openmeld-dev": "dev-local",
|
|
327
|
+
"openmeld-canary": "canary"
|
|
328
|
+
};
|
|
329
|
+
function normalizeOpenMeldProfileId(value) {
|
|
330
|
+
const raw = String(value ?? "").trim();
|
|
331
|
+
if (raw.length === 0) throw new Error("openMeldProfileId is required");
|
|
332
|
+
const safe = raw.replace(/[^a-zA-Z0-9._-]+/g, "_").toLowerCase();
|
|
333
|
+
if (!safe || safe === "." || safe === "..") throw new Error("openMeldProfileId is invalid");
|
|
334
|
+
if (safe.length > MAX_ACCOUNT_PATH_SEGMENT_LENGTH) throw new Error("openMeldProfileId is too long");
|
|
335
|
+
return safe;
|
|
336
|
+
}
|
|
337
|
+
function resolveOpenMeldEnvPreset(input = {}) {
|
|
338
|
+
const fromEnv = normalizeOpenMeldEnvPreset((input.env ?? process.env)[OPENMELD_ENV_PRESET_ENV_KEY]);
|
|
339
|
+
if (fromEnv) return fromEnv;
|
|
340
|
+
return resolveOpenMeldEnvPresetFromArgv(input.argv ?? process.argv);
|
|
341
|
+
}
|
|
342
|
+
function resolveExplicitOpenMeldEnvPreset(input = {}) {
|
|
343
|
+
return normalizeOpenMeldEnvPreset((input.env ?? process.env)[OPENMELD_ENV_PRESET_ENV_KEY]);
|
|
344
|
+
}
|
|
345
|
+
function resolveDefaultOpenMeldHomeDirname(preset) {
|
|
346
|
+
return OPENMELD_DEFAULT_HOME_DIR_BY_PRESET[preset];
|
|
347
|
+
}
|
|
348
|
+
function resolveOpenMeldRootDir(input = {}) {
|
|
349
|
+
const env = input.env ?? process.env;
|
|
350
|
+
const fromEnv = String(env["OPENMELD_HOME"] ?? "").trim();
|
|
351
|
+
if (fromEnv) return fromEnv;
|
|
352
|
+
const preset = resolveOpenMeldEnvPreset(input);
|
|
353
|
+
return join(resolveHomeDirectory(input), resolveDefaultOpenMeldHomeDirname(preset));
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* OS-level resources are shared by every process using the same environment
|
|
357
|
+
* preset, even when a command uses an isolated OPENMELD_HOME. Their local
|
|
358
|
+
* coordination files therefore live under the preset's canonical home.
|
|
359
|
+
*/
|
|
360
|
+
function resolveOpenMeldPresetOwnedPathInput(input = {}) {
|
|
361
|
+
const env = { ...input.env ?? process.env };
|
|
362
|
+
delete env[OPENMELD_HOME_ENV_KEY];
|
|
363
|
+
return {
|
|
364
|
+
...input,
|
|
365
|
+
env
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
function openMeldRootDir() {
|
|
369
|
+
return resolveOpenMeldRootDir();
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Whether the effective OpenMeld home is the published `~/.openmeld` user lane.
|
|
373
|
+
* Lane-specific homes (`openmeld-dev`, `openmeld-canary`, isolated dev lanes,
|
|
374
|
+
* and other `OPENMELD_HOME` overrides) resolve elsewhere and must not manage
|
|
375
|
+
* shared real-user state that the published lane owns.
|
|
376
|
+
*/
|
|
377
|
+
function isPublishedOpenMeldHome(input = {}) {
|
|
378
|
+
const publishedRootDir = join(resolveHomeDirectory(input), OPENMELD_DEFAULT_HOME_DIR_BY_PRESET.prod);
|
|
379
|
+
return resolve(resolveOpenMeldRootDir(input)) === resolve(publishedRootDir);
|
|
380
|
+
}
|
|
381
|
+
function openMeldBackupsDir() {
|
|
382
|
+
const root = openMeldRootDir();
|
|
383
|
+
return join(dirname(root), `${basename(root)}-backups`);
|
|
384
|
+
}
|
|
385
|
+
function openMeldVersionsDir(input = {}) {
|
|
386
|
+
return join(resolveOpenMeldRootDir(input), "versions");
|
|
387
|
+
}
|
|
388
|
+
function openMeldManagedVersionDir(version, input = {}) {
|
|
389
|
+
return join(openMeldVersionsDir(input), normalizePathSegment(version, "unknown"));
|
|
390
|
+
}
|
|
391
|
+
function openMeldManagedVersionBinaryPath(version, input = {}) {
|
|
392
|
+
return join(openMeldManagedVersionDir(version, input), "openmeld");
|
|
393
|
+
}
|
|
394
|
+
function openMeldBinDir(input = {}) {
|
|
395
|
+
return join(resolveOpenMeldRootDir(input), "bin");
|
|
396
|
+
}
|
|
397
|
+
function openMeldManagedBinaryPath(input = {}) {
|
|
398
|
+
return join(openMeldBinDir(input), "openmeld");
|
|
399
|
+
}
|
|
400
|
+
function accountsDir() {
|
|
401
|
+
return join(openMeldRootDir(), "accounts");
|
|
402
|
+
}
|
|
403
|
+
function deviceDir(input = {}) {
|
|
404
|
+
return join(resolveOpenMeldRootDir(input), "device");
|
|
405
|
+
}
|
|
406
|
+
function deviceSkillsDir(input = {}) {
|
|
407
|
+
return join(deviceDir(input), "skills");
|
|
408
|
+
}
|
|
409
|
+
function devicePreferencesPath(input = {}) {
|
|
410
|
+
return join(deviceDir(input), "preferences.json");
|
|
411
|
+
}
|
|
412
|
+
function deviceAgentRuntimePath(input = {}) {
|
|
413
|
+
return join(deviceDir(input), "agent-runtime.json");
|
|
414
|
+
}
|
|
415
|
+
function deviceProvidersDir(input = {}) {
|
|
416
|
+
return join(deviceDir(input), "providers");
|
|
417
|
+
}
|
|
418
|
+
function deviceProviderDir(providerId, input = {}) {
|
|
419
|
+
return join(deviceProvidersDir(input), normalizePathSegment(providerId, "unknown"));
|
|
420
|
+
}
|
|
421
|
+
function deviceProviderProfilesDir(providerId, input = {}) {
|
|
422
|
+
return join(deviceProviderDir(providerId, input), "profiles");
|
|
423
|
+
}
|
|
424
|
+
function deviceProviderProfileDir(providerId, profileId, input = {}) {
|
|
425
|
+
return join(deviceProviderProfilesDir(providerId, input), normalizePathSegment(profileId, "unknown"));
|
|
426
|
+
}
|
|
427
|
+
function deviceOpenClawProfileDir(profileId, input = {}) {
|
|
428
|
+
return deviceProviderProfileDir("openclaw", profileId, input);
|
|
429
|
+
}
|
|
430
|
+
function deviceOpenClawProfileWorkspaceDir(profileId, input = {}) {
|
|
431
|
+
return join(deviceOpenClawProfileDir(profileId, input), "workspace");
|
|
432
|
+
}
|
|
433
|
+
function deviceOpenClawProfileBootstrapPath(profileId, input = {}) {
|
|
434
|
+
return join(deviceOpenClawProfileDir(profileId, input), "bootstrap.json");
|
|
435
|
+
}
|
|
436
|
+
function deviceSkillsStorePath(input = {}) {
|
|
437
|
+
return join(deviceSkillsDir(input), "store.json");
|
|
438
|
+
}
|
|
439
|
+
function deviceSkillsCustomTargetsPath(input = {}) {
|
|
440
|
+
return join(deviceSkillsDir(input), "custom-targets.json");
|
|
441
|
+
}
|
|
442
|
+
function deviceSkillsInstallsPath(input = {}) {
|
|
443
|
+
return join(deviceSkillsDir(input), "installs.json");
|
|
444
|
+
}
|
|
445
|
+
function legacyRuntimeConfigPath(input = {}) {
|
|
446
|
+
return join(resolveOpenMeldRootDir(input), "config.json");
|
|
447
|
+
}
|
|
448
|
+
function normalizeAccountHostKey(value) {
|
|
449
|
+
return normalizePathSegment(value, FALLBACK_ACCOUNT_HOST_KEY);
|
|
450
|
+
}
|
|
451
|
+
function normalizeAccountUserKey(value) {
|
|
452
|
+
return normalizePathSegment(value, FALLBACK_ACCOUNT_USER_KEY);
|
|
453
|
+
}
|
|
454
|
+
function normalizeAccountKey(input) {
|
|
455
|
+
return {
|
|
456
|
+
hostKey: normalizeAccountHostKey(input.hostKey),
|
|
457
|
+
userKey: normalizeAccountUserKey(input.userKey)
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function accountDir(account) {
|
|
461
|
+
const normalized = normalizeAccountKey(account);
|
|
462
|
+
return join(accountsDir(), normalized.hostKey, normalized.userKey);
|
|
463
|
+
}
|
|
464
|
+
function accountProfilesDir(account) {
|
|
465
|
+
return join(accountDir(account), "profiles");
|
|
466
|
+
}
|
|
467
|
+
function accountProfileDir(account, openMeldProfileId) {
|
|
468
|
+
return join(accountProfilesDir(account), normalizeOpenMeldProfileId(openMeldProfileId));
|
|
469
|
+
}
|
|
470
|
+
function accountProfileViewPath(account, openMeldProfileId) {
|
|
471
|
+
return join(accountProfileDir(account, openMeldProfileId), "view.json");
|
|
472
|
+
}
|
|
473
|
+
function accountProfileLocalMetaPath(account, openMeldProfileId) {
|
|
474
|
+
return join(accountProfileDir(account, openMeldProfileId), "local-meta.json");
|
|
475
|
+
}
|
|
476
|
+
function accountProfileWorkspacePath(account, openMeldProfileId) {
|
|
477
|
+
return join(accountProfileDir(account, openMeldProfileId), "workspace");
|
|
478
|
+
}
|
|
479
|
+
function accountProfileSpaceCacheDir(account, openMeldProfileId) {
|
|
480
|
+
return join(accountProfileDir(account, openMeldProfileId), "space-cache");
|
|
481
|
+
}
|
|
482
|
+
function legacyAccountProfileSpacesDir(account, openMeldProfileId) {
|
|
483
|
+
return join(accountProfileDir(account, openMeldProfileId), "spaces");
|
|
484
|
+
}
|
|
485
|
+
function profileDir(openMeldProfileId) {
|
|
486
|
+
return join(openMeldRootDir(), "profiles", openMeldProfileId);
|
|
487
|
+
}
|
|
488
|
+
function runtimeDir(input = {}) {
|
|
489
|
+
return join(resolveOpenMeldRootDir(input), "runtime");
|
|
490
|
+
}
|
|
491
|
+
function runtimeLogsDir() {
|
|
492
|
+
return join(runtimeDir(), "logs");
|
|
493
|
+
}
|
|
494
|
+
function sessionsDir() {
|
|
495
|
+
return join(runtimeDir(), "sessions");
|
|
496
|
+
}
|
|
497
|
+
function systemDir(input = {}) {
|
|
498
|
+
return join(resolveOpenMeldRootDir(input), "system");
|
|
499
|
+
}
|
|
500
|
+
function skillsDir() {
|
|
501
|
+
return join(openMeldRootDir(), "skills");
|
|
502
|
+
}
|
|
503
|
+
function organizationSkillsDir(input = {}) {
|
|
504
|
+
return join(resolveOpenMeldRootDir(input), "organization-skills");
|
|
505
|
+
}
|
|
506
|
+
function organizationSkillReleaseDir(organizationId, skillId, releaseId, input = {}) {
|
|
507
|
+
return join(organizationSkillsDir(input), requireOpaqueCachePathSegment(organizationId, "organizationId"), requireOpaqueCachePathSegment(skillId, "skillId"), requireOpaqueCachePathSegment(releaseId, "releaseId"));
|
|
508
|
+
}
|
|
509
|
+
function spacesDir(openMeldProfileId) {
|
|
510
|
+
return join(profileDir(openMeldProfileId), "spaces");
|
|
511
|
+
}
|
|
512
|
+
function locksDir(openMeldProfileId, input = {}) {
|
|
513
|
+
return join(runtimeDir(input), "locks", openMeldProfileId);
|
|
514
|
+
}
|
|
515
|
+
function profileViewPath(openMeldProfileId) {
|
|
516
|
+
return join(profileDir(openMeldProfileId), "view.json");
|
|
517
|
+
}
|
|
518
|
+
function normalizePathSegment(value, fallback) {
|
|
519
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
520
|
+
if (!normalized) return fallback;
|
|
521
|
+
const safe = normalized.replace(/[^a-z0-9._-]+/g, "_");
|
|
522
|
+
if (!safe) return fallback;
|
|
523
|
+
if (safe === "." || safe === "..") return fallback;
|
|
524
|
+
return safe.slice(0, MAX_ACCOUNT_PATH_SEGMENT_LENGTH);
|
|
525
|
+
}
|
|
526
|
+
function requireOpaqueCachePathSegment(value, label) {
|
|
527
|
+
const normalized = String(value ?? "").trim();
|
|
528
|
+
if (normalized.length === 0 || normalized.length > 256 || normalized === "." || normalized === ".." || !OPAQUE_CACHE_PATH_SEGMENT_RE.test(normalized)) throw new Error(`${label} is not safe for the Organization Skills cache`);
|
|
529
|
+
return normalized;
|
|
530
|
+
}
|
|
531
|
+
function resolveOpenMeldEnvPresetFromArgv(argv) {
|
|
532
|
+
return resolveKnownOpenMeldEnvPresetFromArgv(argv) ?? DEFAULT_OPENMELD_ENV_PRESET;
|
|
533
|
+
}
|
|
534
|
+
function resolveKnownOpenMeldEnvPresetFromArgv(argv) {
|
|
535
|
+
const entryName = resolveOpenMeldCliEntryNameFromArgv(argv);
|
|
536
|
+
if (!entryName) return null;
|
|
537
|
+
return OPENMELD_ENV_PRESET_BY_ENTRY_NAME[entryName] ?? null;
|
|
538
|
+
}
|
|
539
|
+
function resolveOpenMeldCliEntryNameFromArgv(argv) {
|
|
540
|
+
return resolveCliEntryName(argv[1]);
|
|
541
|
+
}
|
|
542
|
+
function resolveCliEntryName(value) {
|
|
543
|
+
const raw = String(value ?? "").trim();
|
|
544
|
+
if (!raw) return null;
|
|
545
|
+
const normalizedName = parse(raw).name.trim().toLowerCase();
|
|
546
|
+
if (!normalizedName) return null;
|
|
547
|
+
if (!isOpenMeldCliEntryName(normalizedName)) return null;
|
|
548
|
+
return normalizedName;
|
|
549
|
+
}
|
|
550
|
+
function isOpenMeldCliEntryName(value) {
|
|
551
|
+
return value in OPENMELD_ENV_PRESET_BY_ENTRY_NAME;
|
|
552
|
+
}
|
|
553
|
+
function normalizeOpenMeldEnvPreset(value) {
|
|
554
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
555
|
+
if (normalized === "prod" || normalized === "dev-local" || normalized === "canary") return normalized;
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
function resolveHomeDirectory(input) {
|
|
559
|
+
if (input.homeDir) return input.homeDir;
|
|
560
|
+
const env = input.env ?? process.env;
|
|
561
|
+
const fromEnv = String(env.HOME ?? "").trim();
|
|
562
|
+
if (fromEnv) return fromEnv;
|
|
563
|
+
return homedir();
|
|
564
|
+
}
|
|
565
|
+
//#endregion
|
|
566
|
+
//#region src/system/local-machine-recovery/index.ts
|
|
567
|
+
var LocalMachineFailureError = class extends Error {
|
|
568
|
+
code;
|
|
569
|
+
issue;
|
|
570
|
+
constructor(input) {
|
|
571
|
+
super(formatLocalMachineIssueMessage(input.issue), { cause: input.cause });
|
|
572
|
+
this.name = "LocalMachineFailureError";
|
|
573
|
+
this.code = input.issue.code;
|
|
574
|
+
this.issue = input.issue;
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
function isLocalMachineFailureError(error) {
|
|
578
|
+
return error instanceof LocalMachineFailureError;
|
|
579
|
+
}
|
|
580
|
+
function toRecognizedLocalMachineFailureError(input) {
|
|
581
|
+
if (isLocalMachineFailureError(input.error)) return input.error;
|
|
582
|
+
const issue = assessLocalMachineFailure(input);
|
|
583
|
+
return issue.code === "local_machine.failure.unknown" ? null : new LocalMachineFailureError({
|
|
584
|
+
issue,
|
|
585
|
+
cause: input.error
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
function assessLocalMachineFailure(input) {
|
|
589
|
+
const systemCode = readSystemErrorCode(input.error);
|
|
590
|
+
const detail = readErrorMessage(input.error);
|
|
591
|
+
if (systemCode === "ENOSPC" || systemCode === "EDQUOT") return {
|
|
592
|
+
schemaVersion: 1,
|
|
593
|
+
code: "local_machine.storage.exhausted",
|
|
594
|
+
operation: input.operation,
|
|
595
|
+
recoveryPolicy: "user_action",
|
|
596
|
+
summary: "Your computer is out of storage space. OpenMeld can't save the local files it needs.",
|
|
597
|
+
primaryAction: "Free up storage, then try again.",
|
|
598
|
+
diagnostics: {
|
|
599
|
+
systemCode,
|
|
600
|
+
detail
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
if (systemCode === "EROFS" || (input.operation === "create_lock" || input.operation === "write_local_state") && (systemCode === "EACCES" || systemCode === "EPERM")) return {
|
|
604
|
+
schemaVersion: 1,
|
|
605
|
+
code: "local_machine.storage.not_writable",
|
|
606
|
+
operation: input.operation,
|
|
607
|
+
recoveryPolicy: "user_action",
|
|
608
|
+
summary: "OpenMeld can't write to its local data folder.",
|
|
609
|
+
primaryAction: "Make sure the folder is writable, then try again.",
|
|
610
|
+
diagnostics: {
|
|
611
|
+
systemCode,
|
|
612
|
+
detail
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
if (systemCode === "EACCES" || systemCode === "EPERM" || systemCode === "EROFS") return {
|
|
616
|
+
schemaVersion: 1,
|
|
617
|
+
code: "local_machine.permission.denied",
|
|
618
|
+
operation: input.operation,
|
|
619
|
+
recoveryPolicy: "user_action",
|
|
620
|
+
summary: "OpenMeld doesn't have permission to complete local setup on this computer.",
|
|
621
|
+
primaryAction: "Allow OpenMeld to access its local files and background services, then try again.",
|
|
622
|
+
diagnostics: {
|
|
623
|
+
systemCode,
|
|
624
|
+
detail
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
if (systemCode === "EMFILE" || systemCode === "ENFILE" || systemCode === "ENOMEM") return {
|
|
628
|
+
schemaVersion: 1,
|
|
629
|
+
code: "local_machine.resources.exhausted",
|
|
630
|
+
operation: input.operation,
|
|
631
|
+
recoveryPolicy: "user_action",
|
|
632
|
+
summary: "Your computer doesn't have enough available system resources for OpenMeld to continue.",
|
|
633
|
+
primaryAction: "Restart your computer, then try again.",
|
|
634
|
+
diagnostics: {
|
|
635
|
+
systemCode,
|
|
636
|
+
detail
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
return {
|
|
640
|
+
schemaVersion: 1,
|
|
641
|
+
code: "local_machine.failure.unknown",
|
|
642
|
+
operation: input.operation,
|
|
643
|
+
recoveryPolicy: "diagnostics",
|
|
644
|
+
summary: "OpenMeld couldn't complete a required action on this computer.",
|
|
645
|
+
primaryAction: "Try again. If it still fails, open diagnostics.",
|
|
646
|
+
diagnostics: {
|
|
647
|
+
systemCode,
|
|
648
|
+
detail
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
function createBackgroundExecutionBlockedIssue(input) {
|
|
653
|
+
return {
|
|
654
|
+
schemaVersion: 1,
|
|
655
|
+
code: "local_machine.background_execution.blocked",
|
|
656
|
+
operation: "start_background_service",
|
|
657
|
+
recoveryPolicy: "user_action",
|
|
658
|
+
summary: "macOS blocked OpenMeld Service from running in the background.",
|
|
659
|
+
primaryAction: "Open System Settings > General > Login Items & Extensions, enable OpenMeld, then try again.",
|
|
660
|
+
diagnostics: {
|
|
661
|
+
systemCode: "BACKGROUND_EXECUTION_BLOCKED",
|
|
662
|
+
detail: input.detail?.trim().slice(0, 2048) || null
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function createActiveSetupConcurrencyIssue(input) {
|
|
667
|
+
return {
|
|
668
|
+
schemaVersion: 1,
|
|
669
|
+
code: "local_machine.concurrency.active",
|
|
670
|
+
operation: "create_lock",
|
|
671
|
+
recoveryPolicy: "user_action",
|
|
672
|
+
summary: "Another OpenMeld setup is already running on this computer.",
|
|
673
|
+
primaryAction: input.primaryAction,
|
|
674
|
+
diagnostics: {
|
|
675
|
+
systemCode: "OPENMELD_LOCK_ACTIVE",
|
|
676
|
+
detail: input.detail?.trim().slice(0, 2048) || null
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function createOrphanedSetupConcurrencyIssue(input) {
|
|
681
|
+
return createOrphanedLocalConcurrencyIssue({
|
|
682
|
+
...input,
|
|
683
|
+
summary: "OpenMeld found a setup lock left by an older version."
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
function createOrphanedLocalConcurrencyIssue(input) {
|
|
687
|
+
return {
|
|
688
|
+
schemaVersion: 1,
|
|
689
|
+
code: "local_machine.concurrency.orphaned",
|
|
690
|
+
operation: "create_lock",
|
|
691
|
+
recoveryPolicy: "user_action",
|
|
692
|
+
summary: input.summary,
|
|
693
|
+
primaryAction: input.primaryAction,
|
|
694
|
+
diagnostics: {
|
|
695
|
+
systemCode: "OPENMELD_LOCK_ORPHANED",
|
|
696
|
+
detail: input.detail?.trim().slice(0, 2048) || null
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function readSystemErrorCode(error) {
|
|
701
|
+
if (!error || typeof error !== "object") return null;
|
|
702
|
+
const code = error.code;
|
|
703
|
+
return typeof code === "string" && code.trim().length > 0 ? code : null;
|
|
704
|
+
}
|
|
705
|
+
function readErrorMessage(error) {
|
|
706
|
+
if (error instanceof Error && error.message.trim().length > 0) return error.message.trim().slice(0, 2048);
|
|
707
|
+
if (typeof error === "string" && error.trim().length > 0) return error.trim().slice(0, 2048);
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
//#endregion
|
|
711
|
+
//#region src/config/openmeld-lane-descriptor.ts
|
|
712
|
+
function resolveOpenMeldLaneDescriptor(input) {
|
|
713
|
+
const env = input?.env ?? process.env;
|
|
714
|
+
const preset = resolveOpenMeldEnvPreset({
|
|
715
|
+
argv: input?.argv,
|
|
716
|
+
env,
|
|
717
|
+
homeDir: input?.homeDir
|
|
718
|
+
});
|
|
719
|
+
const command = resolveLaneCommand(preset);
|
|
720
|
+
return {
|
|
721
|
+
command,
|
|
722
|
+
preset,
|
|
723
|
+
openMeldHome: resolveOpenMeldRootDir({
|
|
724
|
+
argv: input?.argv,
|
|
725
|
+
env,
|
|
726
|
+
homeDir: input?.homeDir
|
|
727
|
+
}),
|
|
728
|
+
destructiveResetAllowed: true,
|
|
729
|
+
repairCommand: `${command} repair local-state`,
|
|
730
|
+
removeCommand: preset === "canary" ? "pnpm openmeld:canary:remove" : null,
|
|
731
|
+
resetCommand: `${command} reset --yes`
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function formatLaneScopeSentence(lane) {
|
|
735
|
+
return `This only affects the ${lane.command} lane at ${lane.openMeldHome}.`;
|
|
736
|
+
}
|
|
737
|
+
function resolveLaneCommand(preset) {
|
|
738
|
+
if (preset === "dev-local") return "openmeld-dev";
|
|
739
|
+
if (preset === "canary") return "openmeld-canary";
|
|
740
|
+
return "openmeld";
|
|
741
|
+
}
|
|
742
|
+
//#endregion
|
|
743
|
+
//#region src/config/session-profile.ts
|
|
744
|
+
const OPENMELD_CLI_CONTEXT_ENV_KEY = "OPENMELD_CLI_CONTEXT_ID";
|
|
745
|
+
const LEGACY_OPENMELD_AGENT_SESSION_ENV_KEY = "OPENMELD_AGENT_SESSION_ID";
|
|
746
|
+
const CLI_CONTEXT_ENV_KEYS = [OPENMELD_CLI_CONTEXT_ENV_KEY, LEGACY_OPENMELD_AGENT_SESSION_ENV_KEY];
|
|
747
|
+
const SESSION_FILE_MAX_LENGTH = 128;
|
|
748
|
+
const SESSION_FILE_HASH_LENGTH = 8;
|
|
749
|
+
function resolveCliContext(profileIdInput) {
|
|
750
|
+
const explicit = normalizeSessionId(profileIdInput);
|
|
751
|
+
if (explicit) return {
|
|
752
|
+
cliContextId: explicit,
|
|
753
|
+
source: "manual"
|
|
754
|
+
};
|
|
755
|
+
for (const key of CLI_CONTEXT_ENV_KEYS) {
|
|
756
|
+
const sessionId = normalizeSessionId(process.env[key]);
|
|
757
|
+
if (sessionId) return {
|
|
758
|
+
cliContextId: sessionId,
|
|
759
|
+
source: `environment:${key}`
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
return resolveTerminalSessionId();
|
|
763
|
+
}
|
|
764
|
+
function resolveCliContextId(profileIdInput) {
|
|
765
|
+
return resolveCliContext(profileIdInput)?.cliContextId ?? null;
|
|
766
|
+
}
|
|
767
|
+
function normalizeSessionSource(raw) {
|
|
768
|
+
const value = String(raw ?? "").trim();
|
|
769
|
+
if (!value) return null;
|
|
770
|
+
return value;
|
|
771
|
+
}
|
|
772
|
+
async function readCliContextState(input) {
|
|
773
|
+
const cliContextId = resolveSessionStateId(input?.sessionId);
|
|
774
|
+
if (!cliContextId) return null;
|
|
775
|
+
const raw = await readFile(cliContextStatePath(cliContextId), "utf8").catch(() => null);
|
|
776
|
+
if (!raw) return null;
|
|
777
|
+
return parseCliContextState(raw);
|
|
778
|
+
}
|
|
779
|
+
async function listCliContextStates() {
|
|
780
|
+
const entryNames = await readdir(sessionsDir()).catch(() => []);
|
|
781
|
+
const states = [];
|
|
782
|
+
for (const entryName of entryNames) {
|
|
783
|
+
if (!entryName.endsWith(".json")) continue;
|
|
784
|
+
const raw = await readFile(join(sessionsDir(), entryName), "utf8").catch(() => null);
|
|
785
|
+
if (!raw) continue;
|
|
786
|
+
const parsed = parseCliContextState(raw);
|
|
787
|
+
if (!parsed) continue;
|
|
788
|
+
states.push(parsed);
|
|
789
|
+
}
|
|
790
|
+
return states.sort((left, right) => left.sessionId.localeCompare(right.sessionId));
|
|
791
|
+
}
|
|
792
|
+
async function writeCliContextState(input) {
|
|
793
|
+
const cliContext = resolveCliContextStateInput(input) ?? resolveCliContext();
|
|
794
|
+
if (!cliContext) return false;
|
|
795
|
+
await mkdir(sessionsDir(), { recursive: true });
|
|
796
|
+
const existing = await readCliContextState({ sessionId: cliContext.cliContextId });
|
|
797
|
+
const nextOpenMeldProfileId = resolveNextSessionOpenMeldProfileId(existing, input);
|
|
798
|
+
const nextSelectedOpenMeldProfileId = resolveNextSelectedOpenMeldProfileId(existing, input);
|
|
799
|
+
const outputPath = cliContextStatePath(cliContext.cliContextId);
|
|
800
|
+
if (!(nextOpenMeldProfileId || nextSelectedOpenMeldProfileId)) {
|
|
801
|
+
await rm(outputPath, { force: true });
|
|
802
|
+
return true;
|
|
803
|
+
}
|
|
804
|
+
await writeCliContextStateFile(outputPath, {
|
|
805
|
+
v: 1,
|
|
806
|
+
sessionId: cliContext.cliContextId,
|
|
807
|
+
cliContextSource: cliContext.source ?? existing?.cliContextSource ?? "manual",
|
|
808
|
+
...nextOpenMeldProfileId ? { openMeldProfileId: nextOpenMeldProfileId } : {},
|
|
809
|
+
...nextSelectedOpenMeldProfileId ? { selectedOpenMeldProfileId: nextSelectedOpenMeldProfileId } : {},
|
|
810
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
811
|
+
});
|
|
812
|
+
return true;
|
|
813
|
+
}
|
|
814
|
+
function cliContextStatePath(sessionId) {
|
|
815
|
+
return join(sessionsDir(), `${toSafeSessionFile(sessionId)}.json`);
|
|
816
|
+
}
|
|
817
|
+
function toSafeSessionFile(value) {
|
|
818
|
+
const hash = createHash("sha1").update(value, "utf8").digest("hex").slice(0, SESSION_FILE_HASH_LENGTH);
|
|
819
|
+
const safeBaseRaw = value.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
820
|
+
const safeBase = safeBaseRaw.length > 0 ? safeBaseRaw : "session";
|
|
821
|
+
const maxBaseLength = Math.max(1, SESSION_FILE_MAX_LENGTH - SESSION_FILE_HASH_LENGTH - 1);
|
|
822
|
+
return `${safeBase.slice(0, maxBaseLength)}-${hash}`;
|
|
823
|
+
}
|
|
824
|
+
function resolveCliContextStateInput(input) {
|
|
825
|
+
const sessionId = normalizeSessionId(input.sessionId);
|
|
826
|
+
if (!sessionId) return null;
|
|
827
|
+
return {
|
|
828
|
+
cliContextId: sessionId,
|
|
829
|
+
source: normalizeSessionSource(input.sessionSource) ?? "manual"
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
function resolveSessionStateId(sessionId) {
|
|
833
|
+
const explicit = normalizeSessionId(sessionId);
|
|
834
|
+
if (explicit) return explicit;
|
|
835
|
+
return resolveCliContext()?.cliContextId ?? null;
|
|
836
|
+
}
|
|
837
|
+
function parseCliContextState(raw) {
|
|
838
|
+
let parsed;
|
|
839
|
+
try {
|
|
840
|
+
parsed = JSON.parse(raw);
|
|
841
|
+
} catch {
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
if (parsed.v !== 1) return null;
|
|
845
|
+
const sessionId = normalizeSessionId(parsed.sessionId);
|
|
846
|
+
if (!sessionId) return null;
|
|
847
|
+
const openMeldProfileId = resolveStoredSessionOpenMeldProfileId(parsed);
|
|
848
|
+
const selectedOpenMeldProfileId = resolveStoredSelectedOpenMeldProfileId(parsed);
|
|
849
|
+
if (!(openMeldProfileId || selectedOpenMeldProfileId)) return null;
|
|
850
|
+
const cliContextSource = normalizeSessionSource(parsed.cliContextSource);
|
|
851
|
+
return {
|
|
852
|
+
v: 1,
|
|
853
|
+
sessionId,
|
|
854
|
+
...cliContextSource ? { cliContextSource } : {},
|
|
855
|
+
...openMeldProfileId ? { openMeldProfileId } : {},
|
|
856
|
+
...selectedOpenMeldProfileId ? { selectedOpenMeldProfileId } : {},
|
|
857
|
+
updatedAt: typeof parsed.updatedAt === "string" && parsed.updatedAt.trim().length > 0 ? parsed.updatedAt : (/* @__PURE__ */ new Date(0)).toISOString()
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
function resolveNextSessionOpenMeldProfileId(existing, input) {
|
|
861
|
+
if (!("openMeldProfileId" in input)) return existing?.openMeldProfileId;
|
|
862
|
+
return normalizeOptionalOpenMeldProfileId(input.openMeldProfileId) ?? void 0;
|
|
863
|
+
}
|
|
864
|
+
function resolveNextSelectedOpenMeldProfileId(existing, input) {
|
|
865
|
+
if (!("selectedOpenMeldProfileId" in input)) return existing?.selectedOpenMeldProfileId;
|
|
866
|
+
return normalizeOptionalOpenMeldProfileId(input.selectedOpenMeldProfileId) ?? void 0;
|
|
867
|
+
}
|
|
868
|
+
async function writeCliContextStateFile(outputPath, payload) {
|
|
869
|
+
const tempPath = `${outputPath}.tmp-${process.pid}-${Date.now()}`;
|
|
870
|
+
const file = await open(tempPath, "w");
|
|
871
|
+
let writeFailure = null;
|
|
872
|
+
try {
|
|
873
|
+
await file.writeFile(`${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
874
|
+
await file.sync();
|
|
875
|
+
} catch (error) {
|
|
876
|
+
writeFailure = error;
|
|
877
|
+
}
|
|
878
|
+
try {
|
|
879
|
+
await file.close();
|
|
880
|
+
} catch (error) {
|
|
881
|
+
if (!writeFailure) writeFailure = error;
|
|
882
|
+
}
|
|
883
|
+
if (writeFailure) {
|
|
884
|
+
await rm(tempPath, { force: true }).catch(() => void 0);
|
|
885
|
+
throw writeFailure;
|
|
886
|
+
}
|
|
887
|
+
try {
|
|
888
|
+
await rename(tempPath, outputPath);
|
|
889
|
+
} catch (error) {
|
|
890
|
+
await rm(tempPath, { force: true }).catch(() => void 0);
|
|
891
|
+
throw error;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
function normalizeSessionId(value) {
|
|
895
|
+
const raw = String(value ?? "").trim();
|
|
896
|
+
if (!raw) return null;
|
|
897
|
+
return raw.slice(0, 256);
|
|
898
|
+
}
|
|
899
|
+
function resolveTerminalSessionId() {
|
|
900
|
+
if (!(process.stdin.isTTY || process.stdout.isTTY)) return null;
|
|
901
|
+
const parentPid = Number(process.ppid);
|
|
902
|
+
if (!Number.isInteger(parentPid) || parentPid <= 1) return null;
|
|
903
|
+
return {
|
|
904
|
+
cliContextId: `terminal-ppid-${parentPid}`,
|
|
905
|
+
source: `terminal:ppid-${parentPid}`
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
function resolveStoredSessionOpenMeldProfileId(input) {
|
|
909
|
+
const openMeldProfileId = normalizeOptionalOpenMeldProfileId(input.openMeldProfileId);
|
|
910
|
+
const legacyProfile = normalizeOptionalOpenMeldProfileId(input.profile);
|
|
911
|
+
if (openMeldProfileId && legacyProfile && openMeldProfileId !== legacyProfile) throw new Error(`conflicting stored OpenMeld profile identity: openMeldProfileId=${openMeldProfileId}, profile=${legacyProfile}. Remove or reconcile the legacy profile field so it matches openMeldProfileId.`);
|
|
912
|
+
return openMeldProfileId ?? legacyProfile;
|
|
913
|
+
}
|
|
914
|
+
function resolveStoredSelectedOpenMeldProfileId(input) {
|
|
915
|
+
return normalizeOptionalOpenMeldProfileId(input.selectedOpenMeldProfileId);
|
|
916
|
+
}
|
|
917
|
+
function normalizeOptionalOpenMeldProfileId(value) {
|
|
918
|
+
if (typeof value !== "string") return null;
|
|
919
|
+
try {
|
|
920
|
+
return normalizeOpenMeldProfileId(value);
|
|
921
|
+
} catch {
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
//#endregion
|
|
926
|
+
//#region src/config/lock.ts
|
|
927
|
+
const MALFORMED_LOCK_RECOVERY_GRACE_MS = 3e4;
|
|
928
|
+
const OPENMELD_ORPHANED_LOCK_SUMMARY = "OpenMeld found a local command lock left by an older version.";
|
|
929
|
+
var OpenMeldLockError = class extends Error {
|
|
930
|
+
code = "openmeld.locked";
|
|
931
|
+
lockPath;
|
|
932
|
+
lockKey;
|
|
933
|
+
metaRaw;
|
|
934
|
+
meta;
|
|
935
|
+
contentionState;
|
|
936
|
+
constructor(input) {
|
|
937
|
+
const normalizedMetaRaw = normalizeMetaRaw(input.metaRaw);
|
|
938
|
+
const contentionState = input.contentionState ?? "unknown";
|
|
939
|
+
super(buildOpenMeldLockErrorMessage(input.path, normalizedMetaRaw, contentionState));
|
|
940
|
+
this.name = "OpenMeldLockError";
|
|
941
|
+
this.lockPath = input.path;
|
|
942
|
+
this.lockKey = input.key;
|
|
943
|
+
this.metaRaw = normalizedMetaRaw;
|
|
944
|
+
this.meta = parseOpenMeldLockMeta(normalizedMetaRaw);
|
|
945
|
+
this.contentionState = contentionState;
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
function isOpenMeldLockError(error) {
|
|
949
|
+
return error instanceof OpenMeldLockError;
|
|
950
|
+
}
|
|
951
|
+
function isOpenMeldOrphanedLockError(error) {
|
|
952
|
+
return isOpenMeldLockError(error) && error.contentionState === "orphaned";
|
|
953
|
+
}
|
|
954
|
+
function resolveOpenMeldOrphanedLockIssue(error) {
|
|
955
|
+
if (!isOpenMeldOrphanedLockError(error)) return null;
|
|
956
|
+
return createOrphanedLocalConcurrencyIssue({
|
|
957
|
+
summary: OPENMELD_ORPHANED_LOCK_SUMMARY,
|
|
958
|
+
primaryAction: buildOpenMeldOrphanedLockAction(),
|
|
959
|
+
detail: error.message
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
function buildOpenMeldOrphanedLockAction() {
|
|
963
|
+
return `Close any other OpenMeld commands, then run \`${resolveOpenMeldLaneDescriptor().repairCommand}\` and try again.`;
|
|
964
|
+
}
|
|
965
|
+
async function acquireLock(input) {
|
|
966
|
+
const openMeldProfileId = normalizeOpenMeldProfileId(input.openMeldProfileId);
|
|
967
|
+
const sessionId = resolveCliContextId();
|
|
968
|
+
const dir = locksDir(openMeldProfileId, input.pathResolution);
|
|
969
|
+
const path = resolveLockPath({
|
|
970
|
+
openMeldProfileId,
|
|
971
|
+
key: input.key,
|
|
972
|
+
pathResolution: input.pathResolution
|
|
973
|
+
});
|
|
974
|
+
const ownerId = randomUUID();
|
|
975
|
+
try {
|
|
976
|
+
await mkdir(dir, { recursive: true });
|
|
977
|
+
} catch (error) {
|
|
978
|
+
throwRecognizedLocalMachineFailure({
|
|
979
|
+
operation: "create_lock",
|
|
980
|
+
error
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
984
|
+
const contents = `${JSON.stringify({
|
|
985
|
+
...input.meta ? { ...input.meta } : {},
|
|
986
|
+
pid: process$1.pid,
|
|
987
|
+
openMeldProfileId,
|
|
988
|
+
...sessionId ? { sessionId } : {},
|
|
989
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
990
|
+
ownerId
|
|
991
|
+
})}\n`;
|
|
992
|
+
let published;
|
|
993
|
+
try {
|
|
994
|
+
published = await tryPublishLock(path, ownerId, contents);
|
|
995
|
+
} catch (error) {
|
|
996
|
+
throwRecognizedLocalMachineFailure({
|
|
997
|
+
operation: "create_lock",
|
|
998
|
+
error
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
if (published) return {
|
|
1002
|
+
ownerId,
|
|
1003
|
+
path
|
|
1004
|
+
};
|
|
1005
|
+
try {
|
|
1006
|
+
const existingLock = await readLockSnapshot(path);
|
|
1007
|
+
if (!existingLock.exists || await tryCleanupStaleLockPath(path, existingLock)) continue;
|
|
1008
|
+
throw new OpenMeldLockError({
|
|
1009
|
+
path,
|
|
1010
|
+
key: input.key,
|
|
1011
|
+
metaRaw: existingLock.metaRaw ?? void 0,
|
|
1012
|
+
contentionState: resolveLockContentionState(existingLock)
|
|
1013
|
+
});
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
if (isOpenMeldLockError(error)) throw error;
|
|
1016
|
+
throwRecognizedLocalMachineFailure({
|
|
1017
|
+
operation: "create_lock",
|
|
1018
|
+
error
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
throw new OpenMeldLockError({
|
|
1023
|
+
path,
|
|
1024
|
+
key: input.key
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
async function releaseLock(lock) {
|
|
1028
|
+
if (!await unlinkOwnedDirectoryEntry(lock)) return;
|
|
1029
|
+
await rmdir(lock.path);
|
|
1030
|
+
}
|
|
1031
|
+
function releaseLockSync(lock) {
|
|
1032
|
+
try {
|
|
1033
|
+
unlinkSync(resolveOwnerEntryPath(lock.path, lock.ownerId));
|
|
1034
|
+
} catch (error) {
|
|
1035
|
+
if (getErrorCode(error) === "ENOENT" || getErrorCode(error) === "ENOTDIR") return;
|
|
1036
|
+
throw error;
|
|
1037
|
+
}
|
|
1038
|
+
try {
|
|
1039
|
+
rmdirSync(lock.path);
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
if (getErrorCode(error) === "ENOENT") return;
|
|
1042
|
+
throw error;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
async function runWithCleanupPreservingRunError(input) {
|
|
1046
|
+
let runResult;
|
|
1047
|
+
let runSucceeded = false;
|
|
1048
|
+
let runError;
|
|
1049
|
+
try {
|
|
1050
|
+
runResult = await input.run();
|
|
1051
|
+
runSucceeded = true;
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
runError = error;
|
|
1054
|
+
}
|
|
1055
|
+
try {
|
|
1056
|
+
await input.cleanup();
|
|
1057
|
+
} catch (cleanupError) {
|
|
1058
|
+
if (!runSucceeded) {
|
|
1059
|
+
input.onCleanupErrorAfterRunFailure?.(cleanupError);
|
|
1060
|
+
throw runError;
|
|
1061
|
+
}
|
|
1062
|
+
throw cleanupError;
|
|
1063
|
+
}
|
|
1064
|
+
if (!runSucceeded) throw runError;
|
|
1065
|
+
return runResult;
|
|
1066
|
+
}
|
|
1067
|
+
async function runWithHeldLock(input) {
|
|
1068
|
+
return await runWithCleanupPreservingRunError({
|
|
1069
|
+
run: input.run,
|
|
1070
|
+
cleanup: () => releaseLock(input.lock),
|
|
1071
|
+
onCleanupErrorAfterRunFailure: (releaseError) => {
|
|
1072
|
+
warnLockReleaseFailure({
|
|
1073
|
+
error: releaseError,
|
|
1074
|
+
lock: input.lock,
|
|
1075
|
+
releaseContext: input.releaseContext
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
async function readLockMeta(input) {
|
|
1081
|
+
return (await readLockSnapshot(resolveLockPath({
|
|
1082
|
+
openMeldProfileId: input.openMeldProfileId,
|
|
1083
|
+
key: input.key,
|
|
1084
|
+
pathResolution: input.pathResolution
|
|
1085
|
+
}))).meta;
|
|
1086
|
+
}
|
|
1087
|
+
async function tryCleanupStaleLock(input) {
|
|
1088
|
+
return await tryCleanupStaleLockPath(resolveLockPath({
|
|
1089
|
+
openMeldProfileId: input.openMeldProfileId,
|
|
1090
|
+
key: input.key,
|
|
1091
|
+
pathResolution: input.pathResolution
|
|
1092
|
+
}));
|
|
1093
|
+
}
|
|
1094
|
+
async function repairOrphanedLegacyLocks() {
|
|
1095
|
+
const locksRootPath = join(runtimeDir(), "locks");
|
|
1096
|
+
const profileEntries = await readDirectoryEntries(locksRootPath);
|
|
1097
|
+
let removedOrphanedLegacyLocks = 0;
|
|
1098
|
+
for (const profileEntry of profileEntries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
1099
|
+
if (!profileEntry.isDirectory()) continue;
|
|
1100
|
+
const profileLocksPath = join(locksRootPath, profileEntry.name);
|
|
1101
|
+
const lockEntries = await readDirectoryEntries(profileLocksPath);
|
|
1102
|
+
for (const lockEntry of lockEntries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
1103
|
+
if (!(lockEntry.isFile() && lockEntry.name.endsWith(".lock"))) continue;
|
|
1104
|
+
if (await repairOrphanedLegacyLockPath(join(profileLocksPath, lockEntry.name))) removedOrphanedLegacyLocks += 1;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
return { removedOrphanedLegacyLocks };
|
|
1108
|
+
}
|
|
1109
|
+
async function repairOrphanedLegacyLockPath(path) {
|
|
1110
|
+
const firstSnapshot = await readLockSnapshot(path);
|
|
1111
|
+
if (firstSnapshot.storageKind !== "legacy_file" || !isRecoverableStaleLock(firstSnapshot)) return false;
|
|
1112
|
+
if (!isSameRecoverableLockSnapshot(firstSnapshot, await readLockSnapshot(path))) return false;
|
|
1113
|
+
try {
|
|
1114
|
+
await unlink(path);
|
|
1115
|
+
return true;
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
if (getErrorCode(error) === "ENOENT" || getErrorCode(error) === "EISDIR") return false;
|
|
1118
|
+
throw error;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
async function readDirectoryEntries(path) {
|
|
1122
|
+
return await readdir(path, { withFileTypes: true }).catch((error) => {
|
|
1123
|
+
if (getErrorCode(error) === "ENOENT") return [];
|
|
1124
|
+
throw error;
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
function resolveLockPath(input) {
|
|
1128
|
+
return join(locksDir(normalizeOpenMeldProfileId(input.openMeldProfileId), input.pathResolution), `${sanitizeFilename(input.key)}.lock`);
|
|
1129
|
+
}
|
|
1130
|
+
function sanitizeFilename(value) {
|
|
1131
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
1132
|
+
}
|
|
1133
|
+
function safeParse(value) {
|
|
1134
|
+
try {
|
|
1135
|
+
return JSON.parse(value);
|
|
1136
|
+
} catch {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
function normalizeMetaRaw(metaRaw) {
|
|
1141
|
+
if (typeof metaRaw !== "string") return null;
|
|
1142
|
+
const normalized = metaRaw.trim();
|
|
1143
|
+
return normalized.length > 0 ? normalized : null;
|
|
1144
|
+
}
|
|
1145
|
+
function parseOpenMeldLockMeta(metaRaw) {
|
|
1146
|
+
if (!metaRaw) return null;
|
|
1147
|
+
const parsed = safeParse(metaRaw);
|
|
1148
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1149
|
+
return parsed;
|
|
1150
|
+
}
|
|
1151
|
+
function buildOpenMeldLockErrorMessage(path, metaRaw, contentionState) {
|
|
1152
|
+
const lockDetail = metaRaw ? `space is locked (${path}). Meta: ${metaRaw}` : `space is locked (${path})`;
|
|
1153
|
+
if (contentionState !== "orphaned") return lockDetail;
|
|
1154
|
+
return `${lockDetail}. ${OPENMELD_ORPHANED_LOCK_SUMMARY} ${buildOpenMeldOrphanedLockAction()}`;
|
|
1155
|
+
}
|
|
1156
|
+
function getPidFromMeta(meta) {
|
|
1157
|
+
if (!meta || typeof meta !== "object") return null;
|
|
1158
|
+
const pid = meta.pid;
|
|
1159
|
+
return typeof pid === "number" ? pid : null;
|
|
1160
|
+
}
|
|
1161
|
+
function missingLockSnapshot() {
|
|
1162
|
+
return {
|
|
1163
|
+
exists: false,
|
|
1164
|
+
fileIdentity: null,
|
|
1165
|
+
ownerEntryName: null,
|
|
1166
|
+
modifiedAtMs: null,
|
|
1167
|
+
metaRaw: null,
|
|
1168
|
+
meta: null,
|
|
1169
|
+
pid: null,
|
|
1170
|
+
hasStalePid: false,
|
|
1171
|
+
storageKind: null
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
async function readLockSnapshot(path, remainingRaceRetries = 2) {
|
|
1175
|
+
const pathStats = await lstat(path).catch((error) => {
|
|
1176
|
+
if (getErrorCode(error) === "ENOENT") return null;
|
|
1177
|
+
throw error;
|
|
1178
|
+
});
|
|
1179
|
+
if (!pathStats) return missingLockSnapshot();
|
|
1180
|
+
if (pathStats.isDirectory()) try {
|
|
1181
|
+
return await readDirectoryLockSnapshot(path, pathStats);
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
if (remainingRaceRetries > 0 && (getErrorCode(error) === "ENOENT" || getErrorCode(error) === "ENOTDIR")) return await readLockSnapshot(path, remainingRaceRetries - 1);
|
|
1184
|
+
throw error;
|
|
1185
|
+
}
|
|
1186
|
+
const handle = await open(path, "r").catch((error) => {
|
|
1187
|
+
if (getErrorCode(error) === "ENOENT") return null;
|
|
1188
|
+
if (remainingRaceRetries > 0 && getErrorCode(error) === "EISDIR") return "retry";
|
|
1189
|
+
throw error;
|
|
1190
|
+
});
|
|
1191
|
+
if (handle === "retry") return await readLockSnapshot(path, remainingRaceRetries - 1);
|
|
1192
|
+
if (!handle) return missingLockSnapshot();
|
|
1193
|
+
try {
|
|
1194
|
+
const [metaRaw, stats] = await Promise.all([handle.readFile("utf8"), handle.stat()]);
|
|
1195
|
+
const normalizedMetaRaw = normalizeMetaRaw(metaRaw);
|
|
1196
|
+
const meta = parseOpenMeldLockMeta(normalizedMetaRaw);
|
|
1197
|
+
const pid = getPidFromMeta(meta);
|
|
1198
|
+
return {
|
|
1199
|
+
exists: true,
|
|
1200
|
+
fileIdentity: `${stats.dev}:${stats.ino}:${stats.mtimeMs}:${stats.size}`,
|
|
1201
|
+
ownerEntryName: null,
|
|
1202
|
+
modifiedAtMs: stats.mtimeMs,
|
|
1203
|
+
metaRaw: normalizedMetaRaw,
|
|
1204
|
+
meta,
|
|
1205
|
+
pid,
|
|
1206
|
+
hasStalePid: pid !== null && !isPidAlive(pid),
|
|
1207
|
+
storageKind: "legacy_file"
|
|
1208
|
+
};
|
|
1209
|
+
} finally {
|
|
1210
|
+
await handle.close();
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
async function readDirectoryLockSnapshot(path, pathStats) {
|
|
1214
|
+
const ownerEntries = (await readdir(path, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".owner.json")).map((entry) => entry.name).sort();
|
|
1215
|
+
const ownerEntryName = ownerEntries.length === 1 ? ownerEntries[0] : null;
|
|
1216
|
+
const directoryIdentity = `${pathStats.dev}:${pathStats.ino}:${pathStats.mtimeMs}`;
|
|
1217
|
+
if (!ownerEntryName) return {
|
|
1218
|
+
exists: true,
|
|
1219
|
+
fileIdentity: `${directoryIdentity}:empty-or-malformed`,
|
|
1220
|
+
ownerEntryName: null,
|
|
1221
|
+
modifiedAtMs: Number(pathStats.mtimeMs),
|
|
1222
|
+
metaRaw: null,
|
|
1223
|
+
meta: null,
|
|
1224
|
+
pid: null,
|
|
1225
|
+
hasStalePid: false,
|
|
1226
|
+
storageKind: "directory"
|
|
1227
|
+
};
|
|
1228
|
+
const handle = await open(join(path, ownerEntryName), "r");
|
|
1229
|
+
try {
|
|
1230
|
+
const [metaRaw, stats] = await Promise.all([handle.readFile("utf8"), handle.stat()]);
|
|
1231
|
+
const normalizedMetaRaw = normalizeMetaRaw(metaRaw);
|
|
1232
|
+
const meta = parseOpenMeldLockMeta(normalizedMetaRaw);
|
|
1233
|
+
const pid = getPidFromMeta(meta);
|
|
1234
|
+
return {
|
|
1235
|
+
exists: true,
|
|
1236
|
+
fileIdentity: `${directoryIdentity}:${ownerEntryName}:${stats.dev}:${stats.ino}:${stats.mtimeMs}:${stats.size}`,
|
|
1237
|
+
ownerEntryName,
|
|
1238
|
+
modifiedAtMs: stats.mtimeMs,
|
|
1239
|
+
metaRaw: normalizedMetaRaw,
|
|
1240
|
+
meta,
|
|
1241
|
+
pid,
|
|
1242
|
+
hasStalePid: pid !== null && !isPidAlive(pid),
|
|
1243
|
+
storageKind: "directory"
|
|
1244
|
+
};
|
|
1245
|
+
} finally {
|
|
1246
|
+
await handle.close();
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
async function tryCleanupStaleLockPath(path, initialSnapshot) {
|
|
1250
|
+
const firstSnapshot = initialSnapshot ?? await readLockSnapshot(path);
|
|
1251
|
+
if (!isRecoverableStaleLock(firstSnapshot)) return false;
|
|
1252
|
+
if (firstSnapshot.storageKind === "legacy_file") return false;
|
|
1253
|
+
const confirmedSnapshot = await readLockSnapshot(path);
|
|
1254
|
+
if (!isSameRecoverableLockSnapshot(firstSnapshot, confirmedSnapshot)) return false;
|
|
1255
|
+
if (confirmedSnapshot.storageKind === "directory") return await cleanupStaleDirectoryLock(path, confirmedSnapshot);
|
|
1256
|
+
return false;
|
|
1257
|
+
}
|
|
1258
|
+
function isSameRecoverableLockSnapshot(firstSnapshot, confirmedSnapshot) {
|
|
1259
|
+
return confirmedSnapshot.exists && confirmedSnapshot.fileIdentity === firstSnapshot.fileIdentity && confirmedSnapshot.ownerEntryName === firstSnapshot.ownerEntryName && confirmedSnapshot.metaRaw === firstSnapshot.metaRaw && confirmedSnapshot.pid === firstSnapshot.pid && confirmedSnapshot.storageKind === firstSnapshot.storageKind && isRecoverableStaleLock(confirmedSnapshot);
|
|
1260
|
+
}
|
|
1261
|
+
function resolveLockContentionState(snapshot) {
|
|
1262
|
+
if (snapshot.storageKind === "legacy_file" && isRecoverableStaleLock(snapshot)) return "orphaned";
|
|
1263
|
+
if (snapshot.storageKind !== null && (snapshot.pid === null || !snapshot.hasStalePid)) return "active";
|
|
1264
|
+
return "unknown";
|
|
1265
|
+
}
|
|
1266
|
+
async function cleanupStaleDirectoryLock(path, snapshot) {
|
|
1267
|
+
if (snapshot.ownerEntryName) try {
|
|
1268
|
+
await unlink(join(path, snapshot.ownerEntryName));
|
|
1269
|
+
} catch (error) {
|
|
1270
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
1271
|
+
throw error;
|
|
1272
|
+
}
|
|
1273
|
+
try {
|
|
1274
|
+
await rmdir(path);
|
|
1275
|
+
return true;
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
if (getErrorCode(error) === "ENOENT") return true;
|
|
1278
|
+
if (getErrorCode(error) === "ENOTEMPTY") return false;
|
|
1279
|
+
throw error;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
async function tryPublishLock(path, ownerId, contents) {
|
|
1283
|
+
try {
|
|
1284
|
+
await mkdir(path);
|
|
1285
|
+
} catch (error) {
|
|
1286
|
+
if (getErrorCode(error) === "EEXIST") return false;
|
|
1287
|
+
throw error;
|
|
1288
|
+
}
|
|
1289
|
+
const ownerEntryPath = resolveOwnerEntryPath(path, ownerId);
|
|
1290
|
+
try {
|
|
1291
|
+
await writeFile(ownerEntryPath, contents, {
|
|
1292
|
+
encoding: "utf8",
|
|
1293
|
+
flag: "wx"
|
|
1294
|
+
});
|
|
1295
|
+
return true;
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
await unlink(ownerEntryPath).catch((cleanupError) => {
|
|
1298
|
+
if (getErrorCode(cleanupError) !== "ENOENT") console.warn("[openmeld.lock.owner_entry_cleanup_warning]", {
|
|
1299
|
+
ownerEntryPath,
|
|
1300
|
+
cleanupError: toErrorMessage$1(cleanupError)
|
|
1301
|
+
});
|
|
1302
|
+
});
|
|
1303
|
+
await rmdir(path).catch((cleanupError) => {
|
|
1304
|
+
if (getErrorCode(cleanupError) !== "ENOENT") console.warn("[openmeld.lock.directory_cleanup_warning]", {
|
|
1305
|
+
lockPath: path,
|
|
1306
|
+
cleanupError: toErrorMessage$1(cleanupError)
|
|
1307
|
+
});
|
|
1308
|
+
});
|
|
1309
|
+
throw error;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
async function unlinkOwnedDirectoryEntry(lock) {
|
|
1313
|
+
try {
|
|
1314
|
+
await unlink(resolveOwnerEntryPath(lock.path, lock.ownerId));
|
|
1315
|
+
return true;
|
|
1316
|
+
} catch (error) {
|
|
1317
|
+
if (getErrorCode(error) === "ENOENT" || getErrorCode(error) === "ENOTDIR") return false;
|
|
1318
|
+
throw error;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
function resolveOwnerEntryPath(path, ownerId) {
|
|
1322
|
+
return join(path, `${encodeURIComponent(ownerId)}.owner.json`);
|
|
1323
|
+
}
|
|
1324
|
+
function isRecoverableStaleLock(snapshot) {
|
|
1325
|
+
if (!snapshot.exists) return false;
|
|
1326
|
+
if (snapshot.pid !== null) return snapshot.hasStalePid;
|
|
1327
|
+
return snapshot.modifiedAtMs !== null && Date.now() - snapshot.modifiedAtMs >= MALFORMED_LOCK_RECOVERY_GRACE_MS;
|
|
1328
|
+
}
|
|
1329
|
+
function isPidAlive(pid) {
|
|
1330
|
+
try {
|
|
1331
|
+
process$1.kill(pid, 0);
|
|
1332
|
+
return true;
|
|
1333
|
+
} catch (err) {
|
|
1334
|
+
if (getErrorCode(err) === "ESRCH") return false;
|
|
1335
|
+
return true;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
function getErrorCode(err) {
|
|
1339
|
+
if (!err || typeof err !== "object") return;
|
|
1340
|
+
const code = err.code;
|
|
1341
|
+
return typeof code === "string" ? code : void 0;
|
|
1342
|
+
}
|
|
1343
|
+
function throwRecognizedLocalMachineFailure(input) {
|
|
1344
|
+
throw toRecognizedLocalMachineFailureError(input) ?? input.error;
|
|
1345
|
+
}
|
|
1346
|
+
function warnLockReleaseFailure(input) {
|
|
1347
|
+
console.warn("[openmeld.lock.release.warning]", {
|
|
1348
|
+
lockPath: input.lock.path,
|
|
1349
|
+
releaseError: toErrorMessage$1(input.error),
|
|
1350
|
+
releaseContext: input.releaseContext ?? {}
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
function toErrorMessage$1(error) {
|
|
1354
|
+
return error instanceof Error ? error.message : String(error);
|
|
1355
|
+
}
|
|
1356
|
+
//#endregion
|
|
1357
|
+
//#region src/protocol/urls.ts
|
|
1358
|
+
const TRAILING_SLASHES_REGEX = /\/+$/;
|
|
1359
|
+
const ROOT_PATH_REGEX = /^\/+$/;
|
|
1360
|
+
function normalizeBaseUrl(baseUrl) {
|
|
1361
|
+
const url = new URL(baseUrl);
|
|
1362
|
+
if (!ROOT_PATH_REGEX.test(url.pathname)) throw new Error("baseUrl must be origin-only (no path prefix)");
|
|
1363
|
+
if (url.search || url.hash) throw new Error("baseUrl must be origin-only (no query or hash)");
|
|
1364
|
+
url.pathname = "";
|
|
1365
|
+
url.search = "";
|
|
1366
|
+
url.hash = "";
|
|
1367
|
+
return url.toString().replace(TRAILING_SLASHES_REGEX, "");
|
|
1368
|
+
}
|
|
1369
|
+
function toSpaceCreateUrl(baseUrl) {
|
|
1370
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1371
|
+
url.pathname = "/v1/spaces";
|
|
1372
|
+
return url.toString();
|
|
1373
|
+
}
|
|
1374
|
+
function toSpaceDeleteUrl(baseUrl, space) {
|
|
1375
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1376
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}`;
|
|
1377
|
+
return url.toString();
|
|
1378
|
+
}
|
|
1379
|
+
function toSpaceMetaUrl(baseUrl, space) {
|
|
1380
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1381
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/meta`;
|
|
1382
|
+
return url.toString();
|
|
1383
|
+
}
|
|
1384
|
+
function toSpaceContractUrl(baseUrl, space) {
|
|
1385
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1386
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/contract`;
|
|
1387
|
+
return url.toString();
|
|
1388
|
+
}
|
|
1389
|
+
function toSpaceBoardUrl(baseUrl, space) {
|
|
1390
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1391
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/board`;
|
|
1392
|
+
return url.toString();
|
|
1393
|
+
}
|
|
1394
|
+
function toSpaceUpdatesUrl(baseUrl, space) {
|
|
1395
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1396
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/updates`;
|
|
1397
|
+
return url.toString();
|
|
1398
|
+
}
|
|
1399
|
+
function toSpaceResultUrl(baseUrl, space) {
|
|
1400
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1401
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/result`;
|
|
1402
|
+
return url.toString();
|
|
1403
|
+
}
|
|
1404
|
+
function toSpaceMembersUrl(baseUrl, space) {
|
|
1405
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1406
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/members`;
|
|
1407
|
+
return url.toString();
|
|
1408
|
+
}
|
|
1409
|
+
function toSpaceRemoveMembersUrl(baseUrl, space) {
|
|
1410
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1411
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/members/remove`;
|
|
1412
|
+
return url.toString();
|
|
1413
|
+
}
|
|
1414
|
+
function toSpaceLeaveUrl(baseUrl, space) {
|
|
1415
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1416
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/leave`;
|
|
1417
|
+
return url.toString();
|
|
1418
|
+
}
|
|
1419
|
+
function toSpaceDispatchTraceUrl(baseUrl, space, dispatchId) {
|
|
1420
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1421
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/dispatches/${encodeURIComponent(dispatchId)}/trace`;
|
|
1422
|
+
return url.toString();
|
|
1423
|
+
}
|
|
1424
|
+
function toSpaceDispatchLookupUrl(baseUrl, space) {
|
|
1425
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1426
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/dispatches/lookup`;
|
|
1427
|
+
return url.toString();
|
|
1428
|
+
}
|
|
1429
|
+
function toDaemonRouteObservationUrl(baseUrl, profileId) {
|
|
1430
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1431
|
+
url.pathname = `/v1/daemon/routes/${encodeURIComponent(profileId)}`;
|
|
1432
|
+
return url.toString();
|
|
1433
|
+
}
|
|
1434
|
+
function toSpacePasswordUrl(baseUrl, space) {
|
|
1435
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1436
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/password`;
|
|
1437
|
+
return url.toString();
|
|
1438
|
+
}
|
|
1439
|
+
function toSpaceCursorUrl(baseUrl, space, profileId) {
|
|
1440
|
+
const url = new URL(normalizeBaseUrl(baseUrl));
|
|
1441
|
+
url.pathname = `/v1/spaces/${encodeURIComponent(space)}/cursors/${encodeURIComponent(profileId)}`;
|
|
1442
|
+
return url.toString();
|
|
1443
|
+
}
|
|
1444
|
+
function toSpaceWsUrl(baseUrl, space) {
|
|
1445
|
+
const http = new URL(normalizeBaseUrl(baseUrl));
|
|
1446
|
+
let wsScheme;
|
|
1447
|
+
if (http.protocol === "http:") wsScheme = "ws:";
|
|
1448
|
+
else if (http.protocol === "https:") wsScheme = "wss:";
|
|
1449
|
+
else if (http.protocol === "ws:" || http.protocol === "wss:") wsScheme = http.protocol;
|
|
1450
|
+
else throw new Error(`unsupported baseUrl protocol for websocket URL: ${http.protocol}`);
|
|
1451
|
+
return `${wsScheme}//${http.host}/v1/spaces/${encodeURIComponent(space)}/stream`;
|
|
1452
|
+
}
|
|
1453
|
+
//#endregion
|
|
1454
|
+
//#region src/config/runtime-config.ts
|
|
1455
|
+
const DEFAULT_RUNTIME_AGENT_TRANSPORT_MODE = "local-cli";
|
|
1456
|
+
const DEFAULT_RUNTIME_AGENT_STREAMING_MODE = "final";
|
|
1457
|
+
const DEFAULT_RUNTIME_AGENT_CONTEXT_SOURCE_FIELD = "OPENMELD_CLI_CONTEXT_ID";
|
|
1458
|
+
const CUSTOM_TARGET_ID_PREFIX = "custom-";
|
|
1459
|
+
const CUSTOM_TARGET_ID_LENGTH = 12;
|
|
1460
|
+
const RUNTIME_CONFIG_LOCK_PROFILE = "__system__";
|
|
1461
|
+
const RUNTIME_CONFIG_LOCK_KEY = "runtime-config";
|
|
1462
|
+
const RUNTIME_CONFIG_LOCK_MAX_ATTEMPTS = 40;
|
|
1463
|
+
const RUNTIME_CONFIG_LOCK_RETRY_DELAY_MS = 25;
|
|
1464
|
+
const WINDOWS_DRIVE_ONLY_RE = /^[a-zA-Z]:$/;
|
|
1465
|
+
const WINDOWS_DRIVE_PREFIX_RE = /^[a-zA-Z]:[\\/]/;
|
|
1466
|
+
const WINDOWS_UNC_RE = /^[/\\]{2}[^/\\]+[/\\]+[^/\\]+/;
|
|
1467
|
+
const WINDOWS_DRIVE_ANY_RE = /^[a-zA-Z]:/;
|
|
1468
|
+
const TRAILING_PATH_SEPARATORS_RE = /[\\/]+$/;
|
|
1469
|
+
function resolveRuntimeSkillsCustomTargetId(input) {
|
|
1470
|
+
const existingId = typeof input.id === "string" && input.id.trim().length > 0 ? input.id.trim() : "";
|
|
1471
|
+
if (existingId) return existingId;
|
|
1472
|
+
const normalizedDir = normalizeCustomTargetDir(input.dir);
|
|
1473
|
+
const digest = createHash("sha256").update(normalizedDir).digest("hex");
|
|
1474
|
+
return `${CUSTOM_TARGET_ID_PREFIX}${digest.slice(0, CUSTOM_TARGET_ID_LENGTH)}`;
|
|
1475
|
+
}
|
|
1476
|
+
function resolveDefaultAgentContextSourceField(_agentId) {
|
|
1477
|
+
return DEFAULT_RUNTIME_AGENT_CONTEXT_SOURCE_FIELD;
|
|
1478
|
+
}
|
|
1479
|
+
function createDefaultRuntimeAgentControllerSettings(agentId) {
|
|
1480
|
+
return {
|
|
1481
|
+
transportMode: DEFAULT_RUNTIME_AGENT_TRANSPORT_MODE,
|
|
1482
|
+
streamingMode: resolveDefaultRuntimeAgentStreamingMode(),
|
|
1483
|
+
timeoutMs: null,
|
|
1484
|
+
retry: 0,
|
|
1485
|
+
contextIdMappingSpec: {
|
|
1486
|
+
sourceField: resolveDefaultAgentContextSourceField(agentId),
|
|
1487
|
+
mappingVersion: 1
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
const resolveDefaultRuntimeAgentStreamingMode = () => {
|
|
1492
|
+
const preset = resolveOpenMeldEnvPreset();
|
|
1493
|
+
if (preset === "dev-local" || preset === "canary") return "stream";
|
|
1494
|
+
return DEFAULT_RUNTIME_AGENT_STREAMING_MODE;
|
|
1495
|
+
};
|
|
1496
|
+
async function getConfiguredBaseUrl() {
|
|
1497
|
+
const config = await readRuntimeConfig();
|
|
1498
|
+
if (!config?.baseUrl) return null;
|
|
1499
|
+
return normalizeGatewayBaseUrl(config.baseUrl);
|
|
1500
|
+
}
|
|
1501
|
+
async function getConfiguredViewCustomization() {
|
|
1502
|
+
return (await readRuntimeConfig())?.viewCustomization ?? null;
|
|
1503
|
+
}
|
|
1504
|
+
async function getConfiguredSkillsConfig() {
|
|
1505
|
+
return (await readRuntimeConfig())?.skills ?? null;
|
|
1506
|
+
}
|
|
1507
|
+
async function getConfiguredDeviceRuntimeState() {
|
|
1508
|
+
return (await readRuntimeConfig())?.deviceRuntimeState ?? null;
|
|
1509
|
+
}
|
|
1510
|
+
async function getConfiguredDaemonDispatchLimits() {
|
|
1511
|
+
return resolveRuntimeDaemonDispatchLimits((await readRuntimeConfig())?.daemon?.dispatch);
|
|
1512
|
+
}
|
|
1513
|
+
async function getConfiguredOnboardingConfig() {
|
|
1514
|
+
return (await readRuntimeConfig())?.onboarding ?? null;
|
|
1515
|
+
}
|
|
1516
|
+
async function getConfiguredOnboardingDisclaimerAcceptedAt() {
|
|
1517
|
+
return (await getConfiguredOnboardingConfig())?.disclaimerAcceptedAt ?? null;
|
|
1518
|
+
}
|
|
1519
|
+
async function getRuntimeConfigReadHealth() {
|
|
1520
|
+
return (await readRuntimeConfigDetailed()).health;
|
|
1521
|
+
}
|
|
1522
|
+
async function alignRuntimeConfigStorage() {
|
|
1523
|
+
return await withRuntimeConfigLock(async () => {
|
|
1524
|
+
const split = await readSplitRuntimeConfigDetailed();
|
|
1525
|
+
const legacy = await readLegacyRuntimeConfigDetailed();
|
|
1526
|
+
const hasLegacyConfig = await pathExists(legacyRuntimeConfigPath());
|
|
1527
|
+
if (legacy.config && (!split.hasFiles || split.health.parseFallback)) {
|
|
1528
|
+
await writeRuntimeConfig(legacy.config);
|
|
1529
|
+
return {
|
|
1530
|
+
migratedLegacyConfig: true,
|
|
1531
|
+
removedLegacyConfig: hasLegacyConfig
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
if (hasLegacyConfig && split.hasFiles) {
|
|
1535
|
+
await rm(legacyRuntimeConfigPath(), { force: true });
|
|
1536
|
+
return {
|
|
1537
|
+
migratedLegacyConfig: false,
|
|
1538
|
+
removedLegacyConfig: true
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
return {
|
|
1542
|
+
migratedLegacyConfig: false,
|
|
1543
|
+
removedLegacyConfig: false
|
|
1544
|
+
};
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
async function setConfiguredBaseUrl(baseUrl) {
|
|
1548
|
+
await withRuntimeConfigLock(async () => {
|
|
1549
|
+
const normalized = normalizeGatewayBaseUrl(baseUrl);
|
|
1550
|
+
await writeRuntimeConfig(buildNextConfig(await readRuntimeConfig(), { baseUrl: normalized }));
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
async function setConfiguredSkillsConfig(skills) {
|
|
1554
|
+
await withRuntimeConfigLock(async () => {
|
|
1555
|
+
await writeRuntimeConfig(buildNextConfig(await readRuntimeConfig(), { skills }));
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
async function updateConfiguredSkillsConfig(updater) {
|
|
1559
|
+
await withRuntimeConfigLock(async () => {
|
|
1560
|
+
const previous = await readRuntimeConfig();
|
|
1561
|
+
const previousSkills = previous?.skills ?? null;
|
|
1562
|
+
const nextSkills = updater(previousSkills);
|
|
1563
|
+
if (JSON.stringify(previousSkills) === JSON.stringify(nextSkills)) return;
|
|
1564
|
+
await writeRuntimeConfig(buildNextConfig(previous, { skills: nextSkills }));
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
async function updateConfiguredDeviceRuntimeState(updater) {
|
|
1568
|
+
await withRuntimeConfigLock(async () => {
|
|
1569
|
+
const previous = await readRuntimeConfig();
|
|
1570
|
+
const previousDeviceRuntimeState = previous?.deviceRuntimeState ?? null;
|
|
1571
|
+
const nextDeviceRuntimeState = updater(previousDeviceRuntimeState);
|
|
1572
|
+
if (JSON.stringify(previousDeviceRuntimeState) === JSON.stringify(nextDeviceRuntimeState)) return;
|
|
1573
|
+
await writeRuntimeConfig(buildNextConfig(previous, { deviceRuntimeState: nextDeviceRuntimeState }));
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
async function updateConfiguredStartV1OnboardingState(updater) {
|
|
1577
|
+
await withRuntimeConfigLock(async () => {
|
|
1578
|
+
const previous = await readRuntimeConfig();
|
|
1579
|
+
const previousOnboarding = previous?.onboarding ?? null;
|
|
1580
|
+
const previousStartV1 = previousOnboarding?.startV1 ?? null;
|
|
1581
|
+
const nextStartV1 = updater(previousStartV1);
|
|
1582
|
+
if (JSON.stringify(previousStartV1) === JSON.stringify(nextStartV1)) return;
|
|
1583
|
+
await writeRuntimeConfig(buildNextConfig(previous, { onboarding: nextStartV1 || previousOnboarding?.disclaimerAcceptedAt ? {
|
|
1584
|
+
...previousOnboarding?.disclaimerAcceptedAt ? { disclaimerAcceptedAt: previousOnboarding.disclaimerAcceptedAt } : {},
|
|
1585
|
+
...nextStartV1 ? { startV1: nextStartV1 } : {}
|
|
1586
|
+
} : null }));
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
async function updateConfiguredOnboardingConfig(updater) {
|
|
1590
|
+
await withRuntimeConfigLock(async () => {
|
|
1591
|
+
const previous = await readRuntimeConfig();
|
|
1592
|
+
const previousOnboarding = previous?.onboarding ?? null;
|
|
1593
|
+
const nextOnboarding = updater(previousOnboarding);
|
|
1594
|
+
if (JSON.stringify(previousOnboarding) === JSON.stringify(nextOnboarding)) return;
|
|
1595
|
+
await writeRuntimeConfig(buildNextConfig(previous, { onboarding: nextOnboarding }));
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
function buildNextConfig(previous, overrides) {
|
|
1599
|
+
const baseUrl = resolveBaseUrlOverride(previous?.baseUrl, overrides);
|
|
1600
|
+
const viewCustomization = resolveConfigOverride("viewCustomization", previous?.viewCustomization, overrides);
|
|
1601
|
+
const skills = resolveConfigOverride("skills", previous?.skills, overrides);
|
|
1602
|
+
const daemon = resolveConfigOverride("daemon", previous?.daemon, overrides);
|
|
1603
|
+
const deviceRuntimeState = resolveConfigOverride("deviceRuntimeState", previous?.deviceRuntimeState, overrides);
|
|
1604
|
+
const onboarding = resolveConfigOverride("onboarding", previous?.onboarding, overrides);
|
|
1605
|
+
return {
|
|
1606
|
+
v: 1,
|
|
1607
|
+
...baseUrl ? { baseUrl } : {},
|
|
1608
|
+
...viewCustomization ? { viewCustomization } : {},
|
|
1609
|
+
...skills ? { skills } : {},
|
|
1610
|
+
...daemon ? { daemon } : {},
|
|
1611
|
+
...deviceRuntimeState ? { deviceRuntimeState } : {},
|
|
1612
|
+
...onboarding ? { onboarding } : {},
|
|
1613
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
function resolveBaseUrlOverride(previous, overrides) {
|
|
1617
|
+
if (!("baseUrl" in overrides)) return previous;
|
|
1618
|
+
if (typeof overrides.baseUrl !== "string" || overrides.baseUrl.length === 0) return;
|
|
1619
|
+
return overrides.baseUrl;
|
|
1620
|
+
}
|
|
1621
|
+
function resolveConfigOverride(key, previous, overrides) {
|
|
1622
|
+
if (!(key in overrides)) return previous;
|
|
1623
|
+
return overrides[key] ?? void 0;
|
|
1624
|
+
}
|
|
1625
|
+
async function acquireRuntimeConfigLock() {
|
|
1626
|
+
for (let attempt = 0; attempt < RUNTIME_CONFIG_LOCK_MAX_ATTEMPTS; attempt += 1) try {
|
|
1627
|
+
return await acquireLock({
|
|
1628
|
+
openMeldProfileId: RUNTIME_CONFIG_LOCK_PROFILE,
|
|
1629
|
+
key: RUNTIME_CONFIG_LOCK_KEY
|
|
1630
|
+
});
|
|
1631
|
+
} catch (error) {
|
|
1632
|
+
if (!isOpenMeldLockError(error) || isOpenMeldOrphanedLockError(error) || attempt === RUNTIME_CONFIG_LOCK_MAX_ATTEMPTS - 1) throw error;
|
|
1633
|
+
await delay(RUNTIME_CONFIG_LOCK_RETRY_DELAY_MS);
|
|
1634
|
+
}
|
|
1635
|
+
throw new Error("runtime config lock retries exhausted");
|
|
1636
|
+
}
|
|
1637
|
+
async function withRuntimeConfigLock(run) {
|
|
1638
|
+
return await runWithHeldLock({
|
|
1639
|
+
lock: await acquireRuntimeConfigLock(),
|
|
1640
|
+
run,
|
|
1641
|
+
releaseContext: {
|
|
1642
|
+
component: "runtime_config",
|
|
1643
|
+
lockKey: RUNTIME_CONFIG_LOCK_KEY,
|
|
1644
|
+
openMeldProfileId: RUNTIME_CONFIG_LOCK_PROFILE
|
|
1645
|
+
}
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
async function readRuntimeConfig() {
|
|
1649
|
+
return (await readRuntimeConfigDetailed()).config;
|
|
1650
|
+
}
|
|
1651
|
+
async function readRuntimeConfigDetailed() {
|
|
1652
|
+
const split = await readSplitRuntimeConfigDetailed();
|
|
1653
|
+
if (split.hasFiles) return {
|
|
1654
|
+
config: split.config,
|
|
1655
|
+
health: split.health
|
|
1656
|
+
};
|
|
1657
|
+
return await readLegacyRuntimeConfigDetailed();
|
|
1658
|
+
}
|
|
1659
|
+
function parseConfiguredBaseUrl(value) {
|
|
1660
|
+
if (typeof value !== "string") return;
|
|
1661
|
+
try {
|
|
1662
|
+
return normalizeGatewayBaseUrl(value);
|
|
1663
|
+
} catch {
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
function parseRuntimeViewCustomization(value) {
|
|
1668
|
+
if (!value || typeof value !== "object") return;
|
|
1669
|
+
const obj = value;
|
|
1670
|
+
const messages = parseMessageCustomization(obj.messages);
|
|
1671
|
+
const render = parseRenderCustomization(obj.render);
|
|
1672
|
+
if (!(messages || render)) return;
|
|
1673
|
+
return {
|
|
1674
|
+
...messages ? { messages } : {},
|
|
1675
|
+
...render ? { render } : {}
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
function parseRuntimeSkillsConfig(value) {
|
|
1679
|
+
if (!value || typeof value !== "object") return;
|
|
1680
|
+
const obj = value;
|
|
1681
|
+
const storeDir = typeof obj.storeDir === "string" && obj.storeDir.trim().length > 0 ? obj.storeDir.trim() : void 0;
|
|
1682
|
+
const agents = parseRuntimeSkillsAgents(obj.agents);
|
|
1683
|
+
const installs = parseRuntimeSkillsInstallRecords(obj.installs);
|
|
1684
|
+
const customTargets = parseRuntimeSkillsCustomTargets(obj.customTargets, buildLegacyCustomTargetIdByDir(installs));
|
|
1685
|
+
if (!(storeDir || agents || customTargets || installs)) return;
|
|
1686
|
+
return {
|
|
1687
|
+
...storeDir ? { storeDir } : {},
|
|
1688
|
+
...agents ? { agents } : {},
|
|
1689
|
+
...customTargets ? { customTargets } : {},
|
|
1690
|
+
...installs ? { installs } : {}
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
function parseRuntimeDaemonConfig(value) {
|
|
1694
|
+
if (!value || typeof value !== "object") return;
|
|
1695
|
+
const dispatch = parseRuntimeDaemonDispatchConfig(value.dispatch);
|
|
1696
|
+
if (!dispatch) return;
|
|
1697
|
+
return { dispatch };
|
|
1698
|
+
}
|
|
1699
|
+
function parseRuntimeDaemonDispatchConfig(value) {
|
|
1700
|
+
if (!value || typeof value !== "object") return;
|
|
1701
|
+
const obj = value;
|
|
1702
|
+
const globalConcurrency = parsePositiveInteger(obj.globalConcurrency);
|
|
1703
|
+
const queueMax = parsePositiveInteger(obj.queueMax);
|
|
1704
|
+
if (globalConcurrency === void 0 && queueMax === void 0) return;
|
|
1705
|
+
return {
|
|
1706
|
+
...globalConcurrency === void 0 ? {} : { globalConcurrency },
|
|
1707
|
+
...queueMax === void 0 ? {} : { queueMax }
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
function parseRuntimeDeviceRuntimeState(value) {
|
|
1711
|
+
return parseRuntimeDeviceRuntimeStateObject(value);
|
|
1712
|
+
}
|
|
1713
|
+
function parseRuntimeDeviceRuntimeStateObject(value) {
|
|
1714
|
+
if (!value || typeof value !== "object") return;
|
|
1715
|
+
const controllers = parseRuntimeAgentControllerStates(value.controllers);
|
|
1716
|
+
if (!controllers) return;
|
|
1717
|
+
return { controllers };
|
|
1718
|
+
}
|
|
1719
|
+
function parseRuntimeOnboardingConfig(value) {
|
|
1720
|
+
if (!value || typeof value !== "object") return;
|
|
1721
|
+
const obj = value;
|
|
1722
|
+
const disclaimerAcceptedAt = toTrimmedStringOrUndefined(obj.disclaimerAcceptedAt);
|
|
1723
|
+
const startV1 = parseRuntimeStartV1OnboardingState(obj.startV1);
|
|
1724
|
+
if (!(disclaimerAcceptedAt || startV1)) return;
|
|
1725
|
+
return {
|
|
1726
|
+
...disclaimerAcceptedAt ? { disclaimerAcceptedAt } : {},
|
|
1727
|
+
...startV1 ? { startV1 } : {}
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
function parseRuntimeStartV1OnboardingState(value) {
|
|
1731
|
+
if (!value || typeof value !== "object") return;
|
|
1732
|
+
const obj = value;
|
|
1733
|
+
const disclaimerAcceptedVersion = toTrimmedStringOrUndefined(obj.disclaimerAcceptedVersion);
|
|
1734
|
+
const lastCompletedAt = toTrimmedStringOrUndefined(obj.lastCompletedAt);
|
|
1735
|
+
const localAgentsSetupCompletedAt = toTrimmedStringOrUndefined(obj.localAgentsSetupCompletedAt);
|
|
1736
|
+
if (!(disclaimerAcceptedVersion || lastCompletedAt || localAgentsSetupCompletedAt)) return;
|
|
1737
|
+
return {
|
|
1738
|
+
...disclaimerAcceptedVersion ? { disclaimerAcceptedVersion } : {},
|
|
1739
|
+
...lastCompletedAt ? { lastCompletedAt } : {},
|
|
1740
|
+
...localAgentsSetupCompletedAt ? { localAgentsSetupCompletedAt } : {}
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
async function writeRuntimeConfig(next) {
|
|
1744
|
+
await mkdir(deviceDir(), { recursive: true });
|
|
1745
|
+
await mkdir(deviceSkillsDir(), { recursive: true });
|
|
1746
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1747
|
+
await writeJsonFileOrRemove(devicePreferencesPath(), buildDevicePreferencesState(next, updatedAt));
|
|
1748
|
+
await writeJsonFileOrRemove(deviceAgentRuntimePath(), buildDeviceAgentRuntimeState(next, updatedAt));
|
|
1749
|
+
await writeJsonFileOrRemove(deviceSkillsStorePath(), buildDeviceSkillsStoreState(next, updatedAt));
|
|
1750
|
+
await writeJsonFileOrRemove(deviceSkillsCustomTargetsPath(), buildDeviceSkillsCustomTargetsState(next, updatedAt));
|
|
1751
|
+
await writeJsonFileOrRemove(deviceSkillsInstallsPath(), buildDeviceSkillsInstallsState(next, updatedAt));
|
|
1752
|
+
await rm(legacyRuntimeConfigPath(), { force: true }).catch(() => void 0);
|
|
1753
|
+
}
|
|
1754
|
+
function delay(ms) {
|
|
1755
|
+
return new Promise((resolve) => {
|
|
1756
|
+
setTimeout(resolve, ms);
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
function parseMessageCustomization(value) {
|
|
1760
|
+
if (!value || typeof value !== "object") return;
|
|
1761
|
+
const obj = value;
|
|
1762
|
+
const human = toStringMap(obj.human);
|
|
1763
|
+
const agent = toStringMap(obj.agent);
|
|
1764
|
+
if (!(human || agent)) return;
|
|
1765
|
+
return {
|
|
1766
|
+
...human ? { human } : {},
|
|
1767
|
+
...agent ? { agent } : {}
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
function parseRenderCustomization(value) {
|
|
1771
|
+
if (!value || typeof value !== "object") return;
|
|
1772
|
+
const obj = value;
|
|
1773
|
+
const humanRaw = obj.human;
|
|
1774
|
+
const agentRaw = obj.agent;
|
|
1775
|
+
const human = humanRaw?.dataMode === "payload" || humanRaw?.dataMode === "envelope" ? { dataMode: humanRaw.dataMode } : void 0;
|
|
1776
|
+
const agent = agentRaw?.envelopeMode === "verbose" || agentRaw?.envelopeMode === "compact" ? { envelopeMode: agentRaw.envelopeMode } : void 0;
|
|
1777
|
+
if (!(human || agent)) return;
|
|
1778
|
+
return {
|
|
1779
|
+
...human ? { human } : {},
|
|
1780
|
+
...agent ? { agent } : {}
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
function parseRuntimeSkillsAgents(value) {
|
|
1784
|
+
if (!value || typeof value !== "object") return;
|
|
1785
|
+
const entries = Object.entries(value).flatMap(([agentName, agentValue]) => {
|
|
1786
|
+
if (!agentValue || typeof agentValue !== "object") return [];
|
|
1787
|
+
const agentObj = agentValue;
|
|
1788
|
+
const skillsDir = typeof agentObj.skillsDir === "string" && agentObj.skillsDir.trim().length > 0 ? agentObj.skillsDir.trim() : void 0;
|
|
1789
|
+
const globalSkillsDir = typeof agentObj.globalSkillsDir === "string" && agentObj.globalSkillsDir.trim().length > 0 ? agentObj.globalSkillsDir.trim() : void 0;
|
|
1790
|
+
if (!(skillsDir || globalSkillsDir)) return [];
|
|
1791
|
+
return [[agentName, {
|
|
1792
|
+
...skillsDir ? { skillsDir } : {},
|
|
1793
|
+
...globalSkillsDir ? { globalSkillsDir } : {}
|
|
1794
|
+
}]];
|
|
1795
|
+
});
|
|
1796
|
+
if (entries.length === 0) return;
|
|
1797
|
+
return Object.fromEntries(entries);
|
|
1798
|
+
}
|
|
1799
|
+
function parseRuntimeSkillsCustomTargets(value, legacyCustomTargetIdByDir) {
|
|
1800
|
+
if (!Array.isArray(value)) return;
|
|
1801
|
+
const targets = value.flatMap((item) => {
|
|
1802
|
+
if (!item || typeof item !== "object") return [];
|
|
1803
|
+
const obj = item;
|
|
1804
|
+
const name = typeof obj.name === "string" && obj.name.trim().length > 0 ? obj.name.trim() : "";
|
|
1805
|
+
const dir = typeof obj.dir === "string" && obj.dir.trim().length > 0 ? normalizeCustomTargetDir(obj.dir) : "";
|
|
1806
|
+
if (!(name && dir)) return [];
|
|
1807
|
+
const explicitId = toTrimmedStringOrUndefined(obj.id);
|
|
1808
|
+
const fallbackLegacyId = legacyCustomTargetIdByDir?.get(dir);
|
|
1809
|
+
return [{
|
|
1810
|
+
id: resolveRuntimeSkillsCustomTargetId({
|
|
1811
|
+
dir,
|
|
1812
|
+
...explicitId ? { id: explicitId } : {},
|
|
1813
|
+
...!explicitId && fallbackLegacyId ? { id: fallbackLegacyId } : {}
|
|
1814
|
+
}),
|
|
1815
|
+
name,
|
|
1816
|
+
dir
|
|
1817
|
+
}];
|
|
1818
|
+
});
|
|
1819
|
+
if (targets.length === 0) return;
|
|
1820
|
+
return targets;
|
|
1821
|
+
}
|
|
1822
|
+
function parseRuntimeSkillsInstallRecords(value) {
|
|
1823
|
+
if (!Array.isArray(value)) return;
|
|
1824
|
+
const records = value.map((item) => parseRuntimeSkillsInstallRecord(item)).filter((record) => record !== null);
|
|
1825
|
+
if (records.length === 0) return;
|
|
1826
|
+
return records;
|
|
1827
|
+
}
|
|
1828
|
+
function buildLegacyCustomTargetIdByDir(installs) {
|
|
1829
|
+
const byDir = /* @__PURE__ */ new Map();
|
|
1830
|
+
for (const install of installs ?? []) {
|
|
1831
|
+
if (!install.targetId.startsWith(CUSTOM_TARGET_ID_PREFIX)) continue;
|
|
1832
|
+
const normalizedTargetDir = normalizeCustomTargetDir(install.targetDir);
|
|
1833
|
+
if (!normalizedTargetDir) continue;
|
|
1834
|
+
byDir.set(normalizedTargetDir, install.targetId);
|
|
1835
|
+
}
|
|
1836
|
+
return byDir;
|
|
1837
|
+
}
|
|
1838
|
+
function parseRuntimeSkillsInstallRecord(value) {
|
|
1839
|
+
if (!value || typeof value !== "object") return null;
|
|
1840
|
+
const obj = value;
|
|
1841
|
+
const targetId = toTrimmedStringOrEmpty(obj.targetId);
|
|
1842
|
+
const targetDir = toTrimmedStringOrEmpty(obj.targetDir);
|
|
1843
|
+
const mode = parseSkillsInstallMode(obj.mode);
|
|
1844
|
+
const status = parseSkillsInstallStatus(obj.status);
|
|
1845
|
+
const skillIds = Array.isArray(obj.skillIds) ? obj.skillIds.map((skill) => typeof skill === "string" ? skill.trim() : "").filter((skill) => skill.length > 0) : [];
|
|
1846
|
+
const updatedAt = toTrimmedStringOrEmpty(obj.updatedAt);
|
|
1847
|
+
const reason = toTrimmedStringOrUndefined(obj.reason);
|
|
1848
|
+
if (!(targetId && targetDir && mode && status && updatedAt)) return null;
|
|
1849
|
+
return {
|
|
1850
|
+
targetId,
|
|
1851
|
+
targetDir,
|
|
1852
|
+
mode,
|
|
1853
|
+
status,
|
|
1854
|
+
skillIds,
|
|
1855
|
+
...reason ? { reason } : {},
|
|
1856
|
+
updatedAt
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
function parseRuntimeAgentControllerStates(value) {
|
|
1860
|
+
if (!Array.isArray(value)) return;
|
|
1861
|
+
const parsed = value.map((entry) => parseRuntimeAgentControllerState(entry)).filter((entry) => entry !== null);
|
|
1862
|
+
if (parsed.length === 0) return;
|
|
1863
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
1864
|
+
for (const entry of parsed) deduped.set(entry.agentId, entry);
|
|
1865
|
+
return [...deduped.values()].sort((a, b) => a.agentId.localeCompare(b.agentId));
|
|
1866
|
+
}
|
|
1867
|
+
function parseRuntimeAgentControllerState(value) {
|
|
1868
|
+
if (!value || typeof value !== "object") return null;
|
|
1869
|
+
const obj = value;
|
|
1870
|
+
const agentId = toTrimmedStringOrEmpty(obj.agentId);
|
|
1871
|
+
const displayName = toTrimmedStringOrEmpty(obj.displayName);
|
|
1872
|
+
const enabled = typeof obj.enabled === "boolean" ? obj.enabled : null;
|
|
1873
|
+
const launchContract = parseRuntimeAgentControllerLaunchContract(obj.launchContract);
|
|
1874
|
+
const defaults = createDefaultRuntimeAgentControllerSettings(agentId);
|
|
1875
|
+
const controllerSettings = parseRuntimeAgentControllerSettings({
|
|
1876
|
+
transportMode: obj.transportMode,
|
|
1877
|
+
streamingMode: obj.streamingMode,
|
|
1878
|
+
timeoutMs: obj.timeoutMs,
|
|
1879
|
+
retry: obj.retry,
|
|
1880
|
+
contextIdMappingSpec: obj.contextIdMappingSpec
|
|
1881
|
+
}, defaults);
|
|
1882
|
+
const registeredAt = toTrimmedStringOrEmpty(obj.registeredAt);
|
|
1883
|
+
const updatedAt = toTrimmedStringOrEmpty(obj.updatedAt);
|
|
1884
|
+
if (!(agentId && displayName && enabled !== null && controllerSettings && registeredAt && updatedAt)) return null;
|
|
1885
|
+
return {
|
|
1886
|
+
agentId,
|
|
1887
|
+
displayName,
|
|
1888
|
+
enabled,
|
|
1889
|
+
...launchContract ? { launchContract } : {},
|
|
1890
|
+
transportMode: controllerSettings.transportMode,
|
|
1891
|
+
streamingMode: controllerSettings.streamingMode,
|
|
1892
|
+
timeoutMs: controllerSettings.timeoutMs,
|
|
1893
|
+
retry: controllerSettings.retry,
|
|
1894
|
+
contextIdMappingSpec: controllerSettings.contextIdMappingSpec,
|
|
1895
|
+
registeredAt,
|
|
1896
|
+
updatedAt
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
function parseRuntimeAgentControllerLaunchContract(value) {
|
|
1900
|
+
if (!(value && typeof value === "object")) return;
|
|
1901
|
+
const parsed = runtimeAgentControllerLaunchContractSchema.safeParse(value);
|
|
1902
|
+
return parsed.success ? parsed.data : void 0;
|
|
1903
|
+
}
|
|
1904
|
+
function parseRuntimeAgentControllerSettings(raw, defaults) {
|
|
1905
|
+
const normalizedStreamingMode = normalizeRuntimeStreamingMode(raw.streamingMode);
|
|
1906
|
+
const parsed = runtimeAgentControllerSettingsSchema.safeParse({
|
|
1907
|
+
transport_mode: raw.transportMode ?? defaults.transportMode,
|
|
1908
|
+
streaming_mode: normalizedStreamingMode ?? defaults.streamingMode,
|
|
1909
|
+
timeout_ms: raw.timeoutMs === void 0 ? defaults.timeoutMs : raw.timeoutMs,
|
|
1910
|
+
retry: raw.retry ?? defaults.retry,
|
|
1911
|
+
context_id_mapping_spec: raw.contextIdMappingSpec ?? defaults.contextIdMappingSpec
|
|
1912
|
+
});
|
|
1913
|
+
if (!parsed.success) return null;
|
|
1914
|
+
return {
|
|
1915
|
+
transportMode: parsed.data.transport_mode,
|
|
1916
|
+
streamingMode: parsed.data.streaming_mode,
|
|
1917
|
+
timeoutMs: normalizeRuntimeAgentTimeoutMs({
|
|
1918
|
+
defaults,
|
|
1919
|
+
timeoutMs: parsed.data.timeout_ms
|
|
1920
|
+
}),
|
|
1921
|
+
retry: parsed.data.retry,
|
|
1922
|
+
contextIdMappingSpec: normalizeRuntimeAgentControllerConversationIdMappingSpec({
|
|
1923
|
+
contextIdMappingSpec: parsed.data.context_id_mapping_spec,
|
|
1924
|
+
defaults
|
|
1925
|
+
})
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
function normalizeRuntimeAgentTimeoutMs(input) {
|
|
1929
|
+
if (input.timeoutMs === 12e4) return input.defaults.timeoutMs;
|
|
1930
|
+
return input.timeoutMs;
|
|
1931
|
+
}
|
|
1932
|
+
function normalizeRuntimeAgentControllerConversationIdMappingSpec(input) {
|
|
1933
|
+
if (input.contextIdMappingSpec.sourceField !== "OPENMELD_CLI_CONTEXT_ID") return input.defaults.contextIdMappingSpec;
|
|
1934
|
+
return input.contextIdMappingSpec;
|
|
1935
|
+
}
|
|
1936
|
+
function resolveRuntimeDaemonDispatchLimits(config) {
|
|
1937
|
+
return {
|
|
1938
|
+
globalConcurrency: parsePositiveInteger(config?.globalConcurrency) ?? 10,
|
|
1939
|
+
queueMax: parsePositiveInteger(config?.queueMax) ?? 50
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
function normalizeRuntimeStreamingMode(value) {
|
|
1943
|
+
if (value === "final" || value === "stream") return value;
|
|
1944
|
+
return null;
|
|
1945
|
+
}
|
|
1946
|
+
function parseSkillsInstallMode(value) {
|
|
1947
|
+
if (value === "symlink" || value === "copy") return value;
|
|
1948
|
+
return null;
|
|
1949
|
+
}
|
|
1950
|
+
function parseSkillsInstallStatus(value) {
|
|
1951
|
+
if (value === "installed" || value === "skipped" || value === "failed") return value;
|
|
1952
|
+
return null;
|
|
1953
|
+
}
|
|
1954
|
+
function parsePositiveInteger(value) {
|
|
1955
|
+
if (!Number.isInteger(value)) return;
|
|
1956
|
+
const numericValue = Number(value);
|
|
1957
|
+
if (numericValue <= 0) return;
|
|
1958
|
+
return numericValue;
|
|
1959
|
+
}
|
|
1960
|
+
function toTrimmedStringOrEmpty(value) {
|
|
1961
|
+
if (typeof value !== "string") return "";
|
|
1962
|
+
const trimmed = value.trim();
|
|
1963
|
+
return trimmed.length > 0 ? trimmed : "";
|
|
1964
|
+
}
|
|
1965
|
+
function toTrimmedStringOrUndefined(value) {
|
|
1966
|
+
if (typeof value !== "string") return;
|
|
1967
|
+
const trimmed = value.trim();
|
|
1968
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1969
|
+
}
|
|
1970
|
+
function normalizeCustomTargetDir(value) {
|
|
1971
|
+
const trimmed = value.trim();
|
|
1972
|
+
if (!trimmed) return "";
|
|
1973
|
+
if (WINDOWS_DRIVE_ONLY_RE.test(trimmed)) return `${trimmed[0]?.toUpperCase() ?? ""}:`;
|
|
1974
|
+
const normalizedPath = WINDOWS_DRIVE_PREFIX_RE.test(trimmed) || WINDOWS_UNC_RE.test(trimmed) ? (() => {
|
|
1975
|
+
const normalizedWindowsPath = win32.normalize(trimmed);
|
|
1976
|
+
if (WINDOWS_DRIVE_ANY_RE.test(normalizedWindowsPath)) return `${normalizedWindowsPath[0]?.toUpperCase() ?? ""}${normalizedWindowsPath.slice(1)}`;
|
|
1977
|
+
return normalizedWindowsPath;
|
|
1978
|
+
})() : normalize(trimmed);
|
|
1979
|
+
const isPosixRoot = normalizedPath === posix.parse(normalizedPath).root;
|
|
1980
|
+
const isWindowsRoot = normalizedPath === win32.parse(normalizedPath).root;
|
|
1981
|
+
if (isPosixRoot || isWindowsRoot) return normalizedPath;
|
|
1982
|
+
return normalizedPath.replace(TRAILING_PATH_SEPARATORS_RE, "");
|
|
1983
|
+
}
|
|
1984
|
+
function toStringMap(value) {
|
|
1985
|
+
if (!value || typeof value !== "object") return;
|
|
1986
|
+
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
1987
|
+
if (entries.length === 0) return;
|
|
1988
|
+
return Object.fromEntries(entries);
|
|
1989
|
+
}
|
|
1990
|
+
function isFileNotFoundError(error) {
|
|
1991
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1992
|
+
}
|
|
1993
|
+
async function readSplitRuntimeConfigDetailed() {
|
|
1994
|
+
const [preferencesFile, agentRuntimeFile, skillsStoreFile, customTargetsFile, installsFile] = await Promise.all([
|
|
1995
|
+
readOptionalJsonFile(devicePreferencesPath()),
|
|
1996
|
+
readOptionalJsonFile(deviceAgentRuntimePath()),
|
|
1997
|
+
readOptionalJsonFile(deviceSkillsStorePath()),
|
|
1998
|
+
readOptionalJsonFile(deviceSkillsCustomTargetsPath()),
|
|
1999
|
+
readOptionalJsonFile(deviceSkillsInstallsPath())
|
|
2000
|
+
]);
|
|
2001
|
+
const files = [
|
|
2002
|
+
preferencesFile,
|
|
2003
|
+
agentRuntimeFile,
|
|
2004
|
+
skillsStoreFile,
|
|
2005
|
+
customTargetsFile,
|
|
2006
|
+
installsFile
|
|
2007
|
+
];
|
|
2008
|
+
if (!files.some((file) => file.found)) return {
|
|
2009
|
+
config: null,
|
|
2010
|
+
hasFiles: false,
|
|
2011
|
+
health: { parseFallback: false }
|
|
2012
|
+
};
|
|
2013
|
+
const invalidFile = files.find((file) => file.error);
|
|
2014
|
+
const preferences = parseDevicePreferencesConfig(preferencesFile.value);
|
|
2015
|
+
const agentRuntime = parseDeviceAgentRuntimeConfig(agentRuntimeFile.value);
|
|
2016
|
+
const skills = mergeRuntimeSkillsConfig({
|
|
2017
|
+
store: parseDeviceSkillsStoreConfig(skillsStoreFile.value),
|
|
2018
|
+
customTargets: parseDeviceSkillsCustomTargetsConfig(customTargetsFile.value),
|
|
2019
|
+
installs: parseDeviceSkillsInstallsConfig(installsFile.value)
|
|
2020
|
+
});
|
|
2021
|
+
return {
|
|
2022
|
+
config: buildRuntimeConfigFromParts({
|
|
2023
|
+
baseUrl: preferences.baseUrl,
|
|
2024
|
+
viewCustomization: preferences.viewCustomization,
|
|
2025
|
+
daemon: preferences.daemon,
|
|
2026
|
+
onboarding: preferences.onboarding,
|
|
2027
|
+
deviceRuntimeState: agentRuntime,
|
|
2028
|
+
skills
|
|
2029
|
+
}),
|
|
2030
|
+
hasFiles: true,
|
|
2031
|
+
health: invalidFile ? {
|
|
2032
|
+
parseFallback: true,
|
|
2033
|
+
reason: invalidFile.error ?? "invalid_json"
|
|
2034
|
+
} : { parseFallback: false }
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
async function readLegacyRuntimeConfigDetailed() {
|
|
2038
|
+
let raw;
|
|
2039
|
+
try {
|
|
2040
|
+
raw = await readFile(legacyRuntimeConfigPath(), "utf8");
|
|
2041
|
+
} catch (error) {
|
|
2042
|
+
if (isFileNotFoundError(error)) return {
|
|
2043
|
+
config: null,
|
|
2044
|
+
health: { parseFallback: false }
|
|
2045
|
+
};
|
|
2046
|
+
throw error;
|
|
2047
|
+
}
|
|
2048
|
+
if (raw.trim().length === 0) return {
|
|
2049
|
+
config: null,
|
|
2050
|
+
health: { parseFallback: false }
|
|
2051
|
+
};
|
|
2052
|
+
let parsed;
|
|
2053
|
+
try {
|
|
2054
|
+
parsed = JSON.parse(raw);
|
|
2055
|
+
} catch {
|
|
2056
|
+
return {
|
|
2057
|
+
config: null,
|
|
2058
|
+
health: {
|
|
2059
|
+
parseFallback: true,
|
|
2060
|
+
reason: "invalid_json"
|
|
2061
|
+
}
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
if (parsed.v !== 1) return {
|
|
2065
|
+
config: null,
|
|
2066
|
+
health: {
|
|
2067
|
+
parseFallback: true,
|
|
2068
|
+
reason: "invalid_version"
|
|
2069
|
+
}
|
|
2070
|
+
};
|
|
2071
|
+
if (parsed.agents !== void 0) throw new Error("runtime.config.legacy_agents_unsupported: key 'agents.registrations' is no longer supported. Use 'deviceRuntimeState.controllers' or run 'openmeld reset --yes'.");
|
|
2072
|
+
return {
|
|
2073
|
+
config: buildRuntimeConfigFromParts({
|
|
2074
|
+
baseUrl: parseConfiguredBaseUrl(parsed.baseUrl),
|
|
2075
|
+
viewCustomization: parseRuntimeViewCustomization(parsed.viewCustomization),
|
|
2076
|
+
skills: parseRuntimeSkillsConfig(parsed.skills),
|
|
2077
|
+
daemon: parseRuntimeDaemonConfig(parsed.daemon),
|
|
2078
|
+
deviceRuntimeState: parseRuntimeDeviceRuntimeState(parsed.deviceRuntimeState),
|
|
2079
|
+
onboarding: parseRuntimeOnboardingConfig(parsed.onboarding),
|
|
2080
|
+
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
2081
|
+
}),
|
|
2082
|
+
health: { parseFallback: false }
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
function buildRuntimeConfigFromParts(input) {
|
|
2086
|
+
const updatedAt = input.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2087
|
+
if (!(input.baseUrl || input.viewCustomization || input.skills || input.daemon || input.deviceRuntimeState || input.onboarding)) return null;
|
|
2088
|
+
return {
|
|
2089
|
+
v: 1,
|
|
2090
|
+
...input.baseUrl ? { baseUrl: input.baseUrl } : {},
|
|
2091
|
+
...input.viewCustomization ? { viewCustomization: input.viewCustomization } : {},
|
|
2092
|
+
...input.skills ? { skills: input.skills } : {},
|
|
2093
|
+
...input.daemon ? { daemon: input.daemon } : {},
|
|
2094
|
+
...input.deviceRuntimeState ? { deviceRuntimeState: input.deviceRuntimeState } : {},
|
|
2095
|
+
...input.onboarding ? { onboarding: input.onboarding } : {},
|
|
2096
|
+
updatedAt
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
function parseDevicePreferencesConfig(value) {
|
|
2100
|
+
if (!value || typeof value !== "object") return {};
|
|
2101
|
+
const parsed = value;
|
|
2102
|
+
if (parsed.v !== 1) return {};
|
|
2103
|
+
return {
|
|
2104
|
+
...parseConfiguredBaseUrl(parsed.baseUrl) ? { baseUrl: parseConfiguredBaseUrl(parsed.baseUrl) } : {},
|
|
2105
|
+
...parseRuntimeViewCustomization(parsed.viewCustomization) ? { viewCustomization: parseRuntimeViewCustomization(parsed.viewCustomization) } : {},
|
|
2106
|
+
...parseRuntimeDaemonConfig(parsed.daemon) ? { daemon: parseRuntimeDaemonConfig(parsed.daemon) } : {},
|
|
2107
|
+
...parseRuntimeOnboardingConfig(parsed.onboarding) ? { onboarding: parseRuntimeOnboardingConfig(parsed.onboarding) } : {}
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
function parseDeviceAgentRuntimeConfig(value) {
|
|
2111
|
+
if (!value || typeof value !== "object") return;
|
|
2112
|
+
const parsed = value;
|
|
2113
|
+
if (parsed.v !== 1) return;
|
|
2114
|
+
return parseRuntimeDeviceRuntimeState(parsed.deviceRuntimeState);
|
|
2115
|
+
}
|
|
2116
|
+
function parseDeviceSkillsStoreConfig(value) {
|
|
2117
|
+
if (!value || typeof value !== "object") return;
|
|
2118
|
+
const parsed = value;
|
|
2119
|
+
if (parsed.v !== 1) return;
|
|
2120
|
+
const storeDir = typeof parsed.storeDir === "string" && parsed.storeDir.trim().length > 0 ? parsed.storeDir.trim() : void 0;
|
|
2121
|
+
const agents = parseRuntimeSkillsAgents(parsed.agents);
|
|
2122
|
+
if (!(storeDir || agents)) return;
|
|
2123
|
+
return {
|
|
2124
|
+
...storeDir ? { storeDir } : {},
|
|
2125
|
+
...agents ? { agents } : {}
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
function parseDeviceSkillsCustomTargetsConfig(value) {
|
|
2129
|
+
if (!value || typeof value !== "object") return;
|
|
2130
|
+
const parsed = value;
|
|
2131
|
+
if (parsed.v !== 1) return;
|
|
2132
|
+
return parseRuntimeSkillsCustomTargets(parsed.customTargets, void 0);
|
|
2133
|
+
}
|
|
2134
|
+
function parseDeviceSkillsInstallsConfig(value) {
|
|
2135
|
+
if (!value || typeof value !== "object") return;
|
|
2136
|
+
const parsed = value;
|
|
2137
|
+
if (parsed.v !== 1) return;
|
|
2138
|
+
return parseRuntimeSkillsInstallRecords(parsed.installs);
|
|
2139
|
+
}
|
|
2140
|
+
function mergeRuntimeSkillsConfig(input) {
|
|
2141
|
+
if (!(input.store || input.customTargets || input.installs)) return;
|
|
2142
|
+
return {
|
|
2143
|
+
...input.store?.storeDir ? { storeDir: input.store.storeDir } : {},
|
|
2144
|
+
...input.store?.agents ? { agents: input.store.agents } : {},
|
|
2145
|
+
...input.customTargets ? { customTargets: input.customTargets } : {},
|
|
2146
|
+
...input.installs ? { installs: input.installs } : {}
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
function buildDevicePreferencesState(next, updatedAt) {
|
|
2150
|
+
if (!(next.baseUrl || next.viewCustomization || next.daemon || next.onboarding)) return null;
|
|
2151
|
+
return {
|
|
2152
|
+
v: 1,
|
|
2153
|
+
...next.baseUrl ? { baseUrl: next.baseUrl } : {},
|
|
2154
|
+
...next.viewCustomization ? { viewCustomization: next.viewCustomization } : {},
|
|
2155
|
+
...next.daemon ? { daemon: next.daemon } : {},
|
|
2156
|
+
...next.onboarding ? { onboarding: next.onboarding } : {},
|
|
2157
|
+
updatedAt
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
function buildDeviceAgentRuntimeState(next, updatedAt) {
|
|
2161
|
+
if (!next.deviceRuntimeState) return null;
|
|
2162
|
+
return {
|
|
2163
|
+
v: 1,
|
|
2164
|
+
deviceRuntimeState: next.deviceRuntimeState,
|
|
2165
|
+
updatedAt
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
function buildDeviceSkillsStoreState(next, updatedAt) {
|
|
2169
|
+
if (!(next.skills?.storeDir || next.skills?.agents)) return null;
|
|
2170
|
+
return {
|
|
2171
|
+
v: 1,
|
|
2172
|
+
...next.skills?.storeDir ? { storeDir: next.skills.storeDir } : {},
|
|
2173
|
+
...next.skills?.agents ? { agents: next.skills.agents } : {},
|
|
2174
|
+
updatedAt
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
function buildDeviceSkillsCustomTargetsState(next, updatedAt) {
|
|
2178
|
+
if (!(next.skills?.customTargets && next.skills.customTargets.length > 0)) return null;
|
|
2179
|
+
return {
|
|
2180
|
+
v: 1,
|
|
2181
|
+
customTargets: next.skills.customTargets,
|
|
2182
|
+
updatedAt
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
function buildDeviceSkillsInstallsState(next, updatedAt) {
|
|
2186
|
+
if (!(next.skills?.installs && next.skills.installs.length > 0)) return null;
|
|
2187
|
+
return {
|
|
2188
|
+
v: 1,
|
|
2189
|
+
installs: next.skills.installs,
|
|
2190
|
+
updatedAt
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
async function writeJsonFileOrRemove(path, payload) {
|
|
2194
|
+
if (!payload) {
|
|
2195
|
+
await rm(path, { force: true }).catch(() => void 0);
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
await mkdir(dirname(path), { recursive: true });
|
|
2199
|
+
const tempPath = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
2200
|
+
await writeFile(tempPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
2201
|
+
try {
|
|
2202
|
+
await rename(tempPath, path);
|
|
2203
|
+
} catch (error) {
|
|
2204
|
+
await rm(tempPath, { force: true }).catch(() => void 0);
|
|
2205
|
+
throw error;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
async function readOptionalJsonFile(path) {
|
|
2209
|
+
const raw = await readFile(path, "utf8").catch((error) => {
|
|
2210
|
+
if (isFileNotFoundError(error)) return null;
|
|
2211
|
+
throw error;
|
|
2212
|
+
});
|
|
2213
|
+
if (raw === null) return {
|
|
2214
|
+
found: false,
|
|
2215
|
+
value: null
|
|
2216
|
+
};
|
|
2217
|
+
if (raw.trim().length === 0) return {
|
|
2218
|
+
found: true,
|
|
2219
|
+
value: null
|
|
2220
|
+
};
|
|
2221
|
+
try {
|
|
2222
|
+
return {
|
|
2223
|
+
found: true,
|
|
2224
|
+
value: JSON.parse(raw)
|
|
2225
|
+
};
|
|
2226
|
+
} catch {
|
|
2227
|
+
return {
|
|
2228
|
+
found: true,
|
|
2229
|
+
value: null,
|
|
2230
|
+
error: "invalid_json"
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
async function pathExists(path) {
|
|
2235
|
+
return await readFile(path, "utf8").then(() => true).catch((error) => {
|
|
2236
|
+
if (isFileNotFoundError(error)) return false;
|
|
2237
|
+
throw error;
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
//#endregion
|
|
2241
|
+
//#region src/config/base-url.ts
|
|
2242
|
+
var base_url_exports = /* @__PURE__ */ __exportAll({
|
|
2243
|
+
DEFAULT_GATEWAY_BASE_URL: () => DEFAULT_GATEWAY_BASE_URL,
|
|
2244
|
+
DEV_LOCAL_GATEWAY_BASE_URL: () => DEV_LOCAL_GATEWAY_BASE_URL,
|
|
2245
|
+
normalizeGatewayBaseUrl: () => normalizeGatewayBaseUrl,
|
|
2246
|
+
normalizeGatewayBaseUrlOrNull: () => normalizeGatewayBaseUrlOrNull,
|
|
2247
|
+
resolveBaseUrl: () => resolveBaseUrl,
|
|
2248
|
+
resolveBaseUrlOrNull: () => resolveBaseUrlOrNull,
|
|
2249
|
+
resolveDefaultGatewayBaseUrl: () => resolveDefaultGatewayBaseUrl,
|
|
2250
|
+
resolveExplicitGatewayUrlOrThrow: () => resolveExplicitGatewayUrlOrThrow
|
|
2251
|
+
});
|
|
2252
|
+
const DEFAULT_GATEWAY_BASE_URL = "https://gateway.openmeld.ai";
|
|
2253
|
+
const DEV_LOCAL_GATEWAY_BASE_URL = "http://localhost:8080";
|
|
2254
|
+
const AUTH_HOST_LABEL = "auth";
|
|
2255
|
+
const AUTH_HOST_PREFIX = "auth-";
|
|
2256
|
+
const LOCAL_AUTH_PORT = "8788";
|
|
2257
|
+
async function resolveBaseUrl(cliValue, input = {}) {
|
|
2258
|
+
const env = input.env ?? process.env;
|
|
2259
|
+
const fromCli = String(cliValue ?? "").trim();
|
|
2260
|
+
if (fromCli) return normalizeGatewayBaseUrl(fromCli);
|
|
2261
|
+
const fromEnv = String(env.OPENMELD_GATEWAY_URL ?? "").trim();
|
|
2262
|
+
if (fromEnv) return normalizeGatewayBaseUrl(fromEnv);
|
|
2263
|
+
const fromConfig = await getConfiguredBaseUrl();
|
|
2264
|
+
if (fromConfig) return normalizeGatewayBaseUrl(fromConfig);
|
|
2265
|
+
return normalizeGatewayBaseUrl(resolveDefaultGatewayBaseUrl({ env }));
|
|
2266
|
+
}
|
|
2267
|
+
async function resolveBaseUrlOrNull(cliValue) {
|
|
2268
|
+
try {
|
|
2269
|
+
return await resolveBaseUrl(cliValue);
|
|
2270
|
+
} catch {
|
|
2271
|
+
return null;
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
async function resolveExplicitGatewayUrlOrThrow(cliValue) {
|
|
2275
|
+
const explicitGatewayUrl = normalizeOptionalString(cliValue);
|
|
2276
|
+
if (!explicitGatewayUrl) return;
|
|
2277
|
+
try {
|
|
2278
|
+
return await Promise.resolve().then(() => normalizeGatewayBaseUrl(explicitGatewayUrl));
|
|
2279
|
+
} catch (error) {
|
|
2280
|
+
throw new Error(`Invalid explicit gateway URL "${explicitGatewayUrl}": ${toErrorMessage(error)}`);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
function normalizeGatewayBaseUrl(gatewayUrl) {
|
|
2284
|
+
const normalizedGatewayUrl = normalizeBaseUrl(gatewayUrl);
|
|
2285
|
+
if (isAuthWorkerUrl(new URL(normalizedGatewayUrl))) throw new Error("gateway URL must point to the OpenMeld gateway, not the OpenMeld auth worker");
|
|
2286
|
+
return normalizedGatewayUrl;
|
|
2287
|
+
}
|
|
2288
|
+
function normalizeGatewayBaseUrlOrNull(gatewayUrl) {
|
|
2289
|
+
const normalizedGatewayUrl = normalizeOptionalString(gatewayUrl);
|
|
2290
|
+
if (!normalizedGatewayUrl) return null;
|
|
2291
|
+
try {
|
|
2292
|
+
return normalizeGatewayBaseUrl(normalizedGatewayUrl);
|
|
2293
|
+
} catch {
|
|
2294
|
+
return null;
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
function normalizeOptionalString(value) {
|
|
2298
|
+
const normalized = String(value ?? "").trim();
|
|
2299
|
+
return normalized.length > 0 ? normalized : null;
|
|
2300
|
+
}
|
|
2301
|
+
function resolveDefaultGatewayBaseUrl(input = {}) {
|
|
2302
|
+
const preset = resolveOpenMeldEnvPreset(input);
|
|
2303
|
+
if (preset === "dev-local" || preset === "canary") return DEV_LOCAL_GATEWAY_BASE_URL;
|
|
2304
|
+
return DEFAULT_GATEWAY_BASE_URL;
|
|
2305
|
+
}
|
|
2306
|
+
function isAuthWorkerUrl(url) {
|
|
2307
|
+
if (isLoopbackHostname(url.hostname)) return resolveUrlPort(url) === LOCAL_AUTH_PORT;
|
|
2308
|
+
const [firstLabel = ""] = url.hostname.toLowerCase().split(".");
|
|
2309
|
+
return firstLabel === AUTH_HOST_LABEL || firstLabel.startsWith(AUTH_HOST_PREFIX);
|
|
2310
|
+
}
|
|
2311
|
+
function isLoopbackHostname(hostname) {
|
|
2312
|
+
const normalizedHostname = hostname.toLowerCase();
|
|
2313
|
+
return normalizedHostname === "localhost" || normalizedHostname === "127.0.0.1" || normalizedHostname === "::1";
|
|
2314
|
+
}
|
|
2315
|
+
function resolveUrlPort(url) {
|
|
2316
|
+
if (url.port) return url.port;
|
|
2317
|
+
if (url.protocol === "http:" || url.protocol === "ws:") return "80";
|
|
2318
|
+
if (url.protocol === "https:" || url.protocol === "wss:") return "443";
|
|
2319
|
+
return "";
|
|
2320
|
+
}
|
|
2321
|
+
function toErrorMessage(error) {
|
|
2322
|
+
return error instanceof Error ? error.message : String(error);
|
|
2323
|
+
}
|
|
2324
|
+
//#endregion
|
|
2325
|
+
export { LEGACY_OPENMELD_AGENT_SESSION_ENV_KEY as $, runtimeDispatchPolicySchema as $t, toSpaceCursorUrl as A, openMeldManagedBinaryPath as At, toSpaceUpdatesUrl as B, resolveOpenMeldCliEntryNameFromArgv as Bt, updateConfiguredSkillsConfig as C, deviceOpenClawProfileWorkspaceDir as Ct, toSpaceBoardUrl as D, normalizeOpenMeldProfileId as Dt, toDaemonRouteObservationUrl as E, normalizeAccountKey as Et, toSpaceMembersUrl as F, organizationSkillReleaseDir as Ft, isOpenMeldOrphanedLockError as G, runtimeLogsDir as Gt, acquireLock as H, resolveOpenMeldPresetOwnedPathInput as Ht, toSpaceMetaUrl as I, profileDir as It, releaseLockSync as J, systemDir as Jt, readLockMeta as K, skillsDir as Kt, toSpacePasswordUrl as L, profileViewPath as Lt, toSpaceDispatchLookupUrl as M, openMeldManagedVersionDir as Mt, toSpaceDispatchTraceUrl as N, openMeldRootDir as Nt, toSpaceContractUrl as O, openMeldBackupsDir as Ot, toSpaceLeaveUrl as P, openMeldVersionsDir as Pt, tryCleanupStaleLock as Q, runtimeAgentControllerLaunchContractSchema as Qt, toSpaceRemoveMembersUrl as R, resolveDefaultOpenMeldHomeDirname as Rt, updateConfiguredOnboardingConfig as S, deviceOpenClawProfileDir as St, normalizeBaseUrl as T, legacyAccountProfileSpacesDir as Tt, buildOpenMeldOrphanedLockAction as U, resolveOpenMeldRootDir as Ut, toSpaceWsUrl as V, resolveOpenMeldEnvPreset as Vt, isOpenMeldLockError as W, runtimeDir as Wt, resolveOpenMeldOrphanedLockIssue as X, LEGACY_RUNTIME_DISPATCH_POLICY as Xt, repairOrphanedLegacyLocks as Y, formatLocalMachineIssueMessage as Yt, runWithHeldLock as Z, runtimeAgentControllerConversationIdMappingSpecSchema as Zt, getRuntimeConfigReadHealth as _, accountProfileSpaceCacheDir as _t, resolveBaseUrl as a, resolveBuiltinAgentControllerConversationKind as an, writeCliContextState as at, setConfiguredSkillsConfig as b, accountsDir as bt, resolveExplicitGatewayUrlOrThrow as c, agentControllerRefSchema as cn, LocalMachineFailureError as ct, createDefaultRuntimeAgentControllerSettings as d, canonicalizeBuiltinAgentControllerRef as dn, createBackgroundExecutionBlockedIssue as dt, runtimeStreamingModeSchema as en, OPENMELD_CLI_CONTEXT_ENV_KEY as et, getConfiguredDaemonDispatchLimits as f, normalizeBuiltinAgentControllerId as fn, createOrphanedSetupConcurrencyIssue as ft, getConfiguredViewCustomization as g, accountProfileLocalMetaPath as gt, getConfiguredSkillsConfig as h, __exportAll as hn, OPENMELD_HOME_ENV_KEY as ht, normalizeGatewayBaseUrlOrNull as i, conversationPolicySchema as in, resolveCliContextId as it, toSpaceDeleteUrl as j, openMeldManagedVersionBinaryPath as jt, toSpaceCreateUrl as k, openMeldBinDir as kt, DEFAULT_RUNTIME_AGENT_TRANSPORT_MODE as l, buildAgentControllerRoutingKey as ln, assessLocalMachineFailure as lt, getConfiguredOnboardingDisclaimerAcceptedAt as m, parseAgentControllerRef as mn, toRecognizedLocalMachineFailureError as mt, base_url_exports as n, agentControllerConversationRefSchema as nn, readCliContextState as nt, resolveBaseUrlOrNull as o, HOSTED_CENTER_AGENT_CONTROLLER_REF as on, formatLaneScopeSentence as ot, getConfiguredDeviceRuntimeState as p, normalizeKnownBuiltinAgentControllerId as pn, isLocalMachineFailureError as pt, releaseLock as q, spacesDir as qt, normalizeGatewayBaseUrl as r, buildAgentControllerConversationRef as rn, resolveCliContext as rt, resolveDefaultGatewayBaseUrl as s, HOSTED_CENTER_AGENT_CONTROLLER_REF_ID as sn, resolveOpenMeldLaneDescriptor as st, DEV_LOCAL_GATEWAY_BASE_URL as t, runtimeTransportModeSchema as tn, listCliContextStates as tt, alignRuntimeConfigStorage as u, canonicalizeAgentControllerRef as un, createActiveSetupConcurrencyIssue as ut, resolveRuntimeSkillsCustomTargetId as v, accountProfileViewPath as vt, updateConfiguredStartV1OnboardingState as w, isPublishedOpenMeldHome as wt, updateConfiguredDeviceRuntimeState as x, deviceOpenClawProfileBootstrapPath as xt, setConfiguredBaseUrl as y, accountProfileWorkspacePath as yt, toSpaceResultUrl as z, resolveExplicitOpenMeldEnvPreset as zt };
|
|
2326
|
+
|
|
2327
|
+
//# sourceMappingURL=base-url-Bdqmyw0D.js.map
|