@hasna/machines 0.0.20 → 0.0.22
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 +17 -0
- package/dist/cli/index.js +417 -15
- package/dist/commands/workspace.d.ts +37 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/consumer.d.ts +1 -1
- package/dist/consumer.d.ts.map +1 -1
- package/dist/consumer.js +201 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +354 -14
- package/dist/mcp/index.js +205 -14
- package/dist/topology.d.ts +21 -0
- package/dist/topology.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,6 +111,23 @@ workspace inference second; consumers can still pass manual overrides.
|
|
|
111
111
|
|
|
112
112
|
```bash
|
|
113
113
|
machines workspace resolve --machine spark01 --project open-knowledge --repo open-knowledge --json
|
|
114
|
+
machines workspace doctor --machine spark01 --project open-knowledge --repo open-knowledge --json
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`workspace resolve` and `workspace doctor` include JSON-friendly
|
|
118
|
+
`diagnostics` and `repair_hints`. Diagnostics classify missing manifests,
|
|
119
|
+
unresolved roots, inferred roots, local stale paths, untrusted machines, and
|
|
120
|
+
unknown auth. Repair hints include the dry-run command plus the matching
|
|
121
|
+
`--apply` command so downstream apps can surface the next step without
|
|
122
|
+
depending on open-machines internals.
|
|
123
|
+
|
|
124
|
+
If a resolver result reports inferred or unresolved project/open-files roots,
|
|
125
|
+
repair the manifest metadata explicitly. The command previews changes by
|
|
126
|
+
default and only writes when `--apply` is passed:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
machines workspace repair --machine spark01 --project open-knowledge --repo open-knowledge --json
|
|
130
|
+
machines workspace repair --machine spark01 --project open-knowledge --repo open-knowledge --apply --json
|
|
114
131
|
```
|
|
115
132
|
|
|
116
133
|
## Compatibility SDK
|
package/dist/cli/index.js
CHANGED
|
@@ -7633,7 +7633,8 @@ var MACHINES_CONSUMER_CAPABILITIES = {
|
|
|
7633
7633
|
compatibility: true,
|
|
7634
7634
|
route_resolution: true,
|
|
7635
7635
|
cli_json_fallback: true,
|
|
7636
|
-
workspace_path_mapping: true
|
|
7636
|
+
workspace_path_mapping: true,
|
|
7637
|
+
workspace_diagnostics: true
|
|
7637
7638
|
};
|
|
7638
7639
|
function getMachinesConsumerCapabilities() {
|
|
7639
7640
|
return { ...MACHINES_CONSUMER_CAPABILITIES };
|
|
@@ -8024,6 +8025,170 @@ function inferRepoRoot(workspaceRoot, repoName) {
|
|
|
8024
8025
|
}
|
|
8025
8026
|
return joinPath(root, repoName);
|
|
8026
8027
|
}
|
|
8028
|
+
function shellQuote(value) {
|
|
8029
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
8030
|
+
}
|
|
8031
|
+
function shellCommand(command) {
|
|
8032
|
+
return command.map(shellQuote).join(" ");
|
|
8033
|
+
}
|
|
8034
|
+
function canCheckPathForMachine(machine, localMachineId) {
|
|
8035
|
+
if (!machine)
|
|
8036
|
+
return false;
|
|
8037
|
+
if (machine.machine_id === localMachineId)
|
|
8038
|
+
return true;
|
|
8039
|
+
return machine.route_hints.some((hint) => hint.kind === "local");
|
|
8040
|
+
}
|
|
8041
|
+
function checkedPathExists(path, check) {
|
|
8042
|
+
if (!path || !check)
|
|
8043
|
+
return null;
|
|
8044
|
+
return existsSync5(path);
|
|
8045
|
+
}
|
|
8046
|
+
function repairHint(input) {
|
|
8047
|
+
const command = [
|
|
8048
|
+
"machines",
|
|
8049
|
+
"workspace",
|
|
8050
|
+
"repair",
|
|
8051
|
+
"--machine",
|
|
8052
|
+
input.machineId,
|
|
8053
|
+
"--project",
|
|
8054
|
+
input.projectId
|
|
8055
|
+
];
|
|
8056
|
+
if (input.repoName)
|
|
8057
|
+
command.push("--repo", input.repoName);
|
|
8058
|
+
if (input.openFilesRepoName)
|
|
8059
|
+
command.push("--open-files-repo", input.openFilesRepoName);
|
|
8060
|
+
command.push("--json");
|
|
8061
|
+
const applyCommand = [...command.slice(0, -1), "--apply", "--json"];
|
|
8062
|
+
return {
|
|
8063
|
+
id: `repair:${input.machineId}:${input.projectId}`,
|
|
8064
|
+
reason: input.reason,
|
|
8065
|
+
command,
|
|
8066
|
+
shell_command: shellCommand(command),
|
|
8067
|
+
apply_command: applyCommand,
|
|
8068
|
+
apply_shell_command: shellCommand(applyCommand)
|
|
8069
|
+
};
|
|
8070
|
+
}
|
|
8071
|
+
function pathDiagnostic(input) {
|
|
8072
|
+
if (!input.path.path) {
|
|
8073
|
+
return {
|
|
8074
|
+
id: input.id,
|
|
8075
|
+
status: "missing",
|
|
8076
|
+
severity: input.required ? "fail" : "warn",
|
|
8077
|
+
message: `${input.label} is unresolved.`,
|
|
8078
|
+
path: null,
|
|
8079
|
+
source: input.path.source,
|
|
8080
|
+
path_exists: null
|
|
8081
|
+
};
|
|
8082
|
+
}
|
|
8083
|
+
if (input.pathExists === false) {
|
|
8084
|
+
return {
|
|
8085
|
+
id: input.id,
|
|
8086
|
+
status: "stale",
|
|
8087
|
+
severity: "fail",
|
|
8088
|
+
message: `${input.label} points to a path that does not exist on this machine.`,
|
|
8089
|
+
path: input.path.path,
|
|
8090
|
+
source: input.path.source,
|
|
8091
|
+
path_exists: false
|
|
8092
|
+
};
|
|
8093
|
+
}
|
|
8094
|
+
if (input.path.source === "inferred") {
|
|
8095
|
+
return {
|
|
8096
|
+
id: input.id,
|
|
8097
|
+
status: "inferred",
|
|
8098
|
+
severity: "warn",
|
|
8099
|
+
message: `${input.label} was inferred from the workspace root; write an explicit manifest mapping for repeatable downstream sync.`,
|
|
8100
|
+
path: input.path.path,
|
|
8101
|
+
source: input.path.source,
|
|
8102
|
+
path_exists: input.pathExists
|
|
8103
|
+
};
|
|
8104
|
+
}
|
|
8105
|
+
return {
|
|
8106
|
+
id: input.id,
|
|
8107
|
+
status: "ok",
|
|
8108
|
+
severity: "ok",
|
|
8109
|
+
message: `${input.label} is explicit enough for downstream sync.`,
|
|
8110
|
+
path: input.path.path,
|
|
8111
|
+
source: input.path.source,
|
|
8112
|
+
path_exists: input.pathExists
|
|
8113
|
+
};
|
|
8114
|
+
}
|
|
8115
|
+
function workspaceDiagnostics(input) {
|
|
8116
|
+
const checkPaths = canCheckPathForMachine(input.machine, input.localMachineId);
|
|
8117
|
+
const diagnostics = [];
|
|
8118
|
+
if (!input.machine) {
|
|
8119
|
+
diagnostics.push({
|
|
8120
|
+
id: "manifest",
|
|
8121
|
+
status: "missing_manifest",
|
|
8122
|
+
severity: "fail",
|
|
8123
|
+
message: "Machine is not present in topology or manifest.",
|
|
8124
|
+
path: null,
|
|
8125
|
+
source: "manifest",
|
|
8126
|
+
path_exists: null
|
|
8127
|
+
});
|
|
8128
|
+
} else if (!input.resolution.evidence.manifest_declared) {
|
|
8129
|
+
diagnostics.push({
|
|
8130
|
+
id: "manifest",
|
|
8131
|
+
status: "missing_manifest",
|
|
8132
|
+
severity: "warn",
|
|
8133
|
+
message: "Machine came from live topology but is not declared in the manifest.",
|
|
8134
|
+
path: null,
|
|
8135
|
+
source: "manifest",
|
|
8136
|
+
path_exists: null
|
|
8137
|
+
});
|
|
8138
|
+
}
|
|
8139
|
+
diagnostics.push(pathDiagnostic({
|
|
8140
|
+
id: "workspace_root",
|
|
8141
|
+
label: "Workspace root",
|
|
8142
|
+
path: input.resolution.paths.workspace_root,
|
|
8143
|
+
pathExists: checkedPathExists(input.resolution.paths.workspace_root.path, checkPaths),
|
|
8144
|
+
required: false
|
|
8145
|
+
}));
|
|
8146
|
+
diagnostics.push(pathDiagnostic({
|
|
8147
|
+
id: "project_root",
|
|
8148
|
+
label: "Project root",
|
|
8149
|
+
path: input.resolution.paths.project_root,
|
|
8150
|
+
pathExists: checkedPathExists(input.resolution.paths.project_root.path, checkPaths),
|
|
8151
|
+
required: true
|
|
8152
|
+
}));
|
|
8153
|
+
diagnostics.push(pathDiagnostic({
|
|
8154
|
+
id: "open_files_root",
|
|
8155
|
+
label: "Open-files root",
|
|
8156
|
+
path: input.resolution.paths.open_files_root,
|
|
8157
|
+
pathExists: checkedPathExists(input.resolution.paths.open_files_root.path, checkPaths),
|
|
8158
|
+
required: false
|
|
8159
|
+
}));
|
|
8160
|
+
if (input.resolution.machine.trust_status !== "trusted") {
|
|
8161
|
+
diagnostics.push({
|
|
8162
|
+
id: "trust",
|
|
8163
|
+
status: "untrusted",
|
|
8164
|
+
severity: "warn",
|
|
8165
|
+
message: `Machine trust status is ${input.resolution.machine.trust_status}; manifest repair apply requires trust or --allow-untrusted.`,
|
|
8166
|
+
path: null,
|
|
8167
|
+
source: "trust",
|
|
8168
|
+
path_exists: null
|
|
8169
|
+
});
|
|
8170
|
+
}
|
|
8171
|
+
if (input.resolution.machine.auth_status !== "authenticated") {
|
|
8172
|
+
diagnostics.push({
|
|
8173
|
+
id: "auth",
|
|
8174
|
+
status: "unknown_auth",
|
|
8175
|
+
severity: "warn",
|
|
8176
|
+
message: `Machine auth status is ${input.resolution.machine.auth_status}; remote sync may still fail if SSH is unavailable.`,
|
|
8177
|
+
path: null,
|
|
8178
|
+
source: "auth",
|
|
8179
|
+
path_exists: null
|
|
8180
|
+
});
|
|
8181
|
+
}
|
|
8182
|
+
const needsRepair = diagnostics.some((entry) => (entry.id === "project_root" || entry.id === "open_files_root") && (entry.status === "missing" || entry.status === "inferred" || entry.status === "stale"));
|
|
8183
|
+
const repairHints = needsRepair ? [repairHint({
|
|
8184
|
+
machineId: input.resolution.machine_id ?? input.resolution.requested_machine_id,
|
|
8185
|
+
projectId: input.resolution.project.project_id,
|
|
8186
|
+
repoName: input.resolution.project.repo_name,
|
|
8187
|
+
openFilesRepoName: input.openFilesRepoName,
|
|
8188
|
+
reason: "Write explicit workspace_paths and open_files_roots manifest metadata."
|
|
8189
|
+
})] : [];
|
|
8190
|
+
return { diagnostics, repairHints };
|
|
8191
|
+
}
|
|
8027
8192
|
function projectPathFromMetadata(metadata, projectId, repoName) {
|
|
8028
8193
|
const keys = [projectId, repoName].filter((value) => Boolean(value));
|
|
8029
8194
|
return readMappedPath({
|
|
@@ -8099,7 +8264,7 @@ function resolveMachineWorkspace(options) {
|
|
|
8099
8264
|
const openFilesRepoName = options.openFilesRepoName ?? "open-files";
|
|
8100
8265
|
if (!machine) {
|
|
8101
8266
|
warnings.push(`machine_not_found:${options.machineId}`);
|
|
8102
|
-
|
|
8267
|
+
const resolution2 = {
|
|
8103
8268
|
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
8104
8269
|
package: topology.package,
|
|
8105
8270
|
ok: false,
|
|
@@ -8113,6 +8278,8 @@ function resolveMachineWorkspace(options) {
|
|
|
8113
8278
|
project_root: { path: null, source: "unresolved" },
|
|
8114
8279
|
open_files_root: { path: null, source: "unresolved" }
|
|
8115
8280
|
},
|
|
8281
|
+
diagnostics: [],
|
|
8282
|
+
repair_hints: [],
|
|
8116
8283
|
evidence: {
|
|
8117
8284
|
topology: true,
|
|
8118
8285
|
matched_by: matchedBy,
|
|
@@ -8121,6 +8288,17 @@ function resolveMachineWorkspace(options) {
|
|
|
8121
8288
|
},
|
|
8122
8289
|
warnings
|
|
8123
8290
|
};
|
|
8291
|
+
const diagnostics2 = workspaceDiagnostics({
|
|
8292
|
+
machine,
|
|
8293
|
+
localMachineId: topology.local_machine_id,
|
|
8294
|
+
resolution: resolution2,
|
|
8295
|
+
openFilesRepoName
|
|
8296
|
+
});
|
|
8297
|
+
return {
|
|
8298
|
+
...resolution2,
|
|
8299
|
+
diagnostics: diagnostics2.diagnostics,
|
|
8300
|
+
repair_hints: diagnostics2.repairHints
|
|
8301
|
+
};
|
|
8124
8302
|
}
|
|
8125
8303
|
const metadata = machine.metadata;
|
|
8126
8304
|
const workspaceRootPath = options.workspaceRoot ?? machine.workspace_path;
|
|
@@ -8139,7 +8317,7 @@ function resolveMachineWorkspace(options) {
|
|
|
8139
8317
|
warnings.push(`open_files_root_inferred:${options.projectId}`);
|
|
8140
8318
|
if (!projectRootPath)
|
|
8141
8319
|
warnings.push(`project_root_unresolved:${options.projectId}`);
|
|
8142
|
-
|
|
8320
|
+
const resolution = {
|
|
8143
8321
|
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
8144
8322
|
package: topology.package,
|
|
8145
8323
|
ok: Boolean(projectRootPath),
|
|
@@ -8162,6 +8340,8 @@ function resolveMachineWorkspace(options) {
|
|
|
8162
8340
|
project_root: { path: projectRootPath, source: projectRootSource },
|
|
8163
8341
|
open_files_root: { path: openFilesRootPath, source: openFilesRootSource }
|
|
8164
8342
|
},
|
|
8343
|
+
diagnostics: [],
|
|
8344
|
+
repair_hints: [],
|
|
8165
8345
|
evidence: {
|
|
8166
8346
|
topology: true,
|
|
8167
8347
|
matched_by: matchedBy,
|
|
@@ -8170,10 +8350,21 @@ function resolveMachineWorkspace(options) {
|
|
|
8170
8350
|
},
|
|
8171
8351
|
warnings
|
|
8172
8352
|
};
|
|
8353
|
+
const diagnostics = workspaceDiagnostics({
|
|
8354
|
+
machine,
|
|
8355
|
+
localMachineId: topology.local_machine_id,
|
|
8356
|
+
resolution,
|
|
8357
|
+
openFilesRepoName
|
|
8358
|
+
});
|
|
8359
|
+
return {
|
|
8360
|
+
...resolution,
|
|
8361
|
+
diagnostics: diagnostics.diagnostics,
|
|
8362
|
+
repair_hints: diagnostics.repairHints
|
|
8363
|
+
};
|
|
8173
8364
|
}
|
|
8174
8365
|
|
|
8175
8366
|
// src/commands/ssh.ts
|
|
8176
|
-
function
|
|
8367
|
+
function shellQuote2(value) {
|
|
8177
8368
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
8178
8369
|
}
|
|
8179
8370
|
function resolveSshTarget(machineId, options = {}) {
|
|
@@ -8194,11 +8385,11 @@ function resolveSshTarget(machineId, options = {}) {
|
|
|
8194
8385
|
}
|
|
8195
8386
|
function buildSshCommand(machineId, remoteCommand, options = {}) {
|
|
8196
8387
|
const resolved = resolveSshTarget(machineId, options);
|
|
8197
|
-
return remoteCommand ? `ssh ${resolved.target} ${
|
|
8388
|
+
return remoteCommand ? `ssh ${resolved.target} ${shellQuote2(remoteCommand)}` : `ssh ${resolved.target}`;
|
|
8198
8389
|
}
|
|
8199
8390
|
|
|
8200
8391
|
// src/remote.ts
|
|
8201
|
-
function
|
|
8392
|
+
function shellQuote3(value) {
|
|
8202
8393
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
8203
8394
|
}
|
|
8204
8395
|
function machineIsLocal(machineId, localMachineId) {
|
|
@@ -8216,7 +8407,7 @@ function resolveMachineCommand(machineId, command, localMachineId = getLocalMach
|
|
|
8216
8407
|
} catch (error) {
|
|
8217
8408
|
const message = String(error.message ?? error);
|
|
8218
8409
|
if (message.includes("Machine route not found") || message.includes("Machine not found in manifest")) {
|
|
8219
|
-
return { source: "ssh", shellCommand: `ssh ${
|
|
8410
|
+
return { source: "ssh", shellCommand: `ssh ${shellQuote3(machineId)} ${shellQuote3(command)}` };
|
|
8220
8411
|
}
|
|
8221
8412
|
throw error;
|
|
8222
8413
|
}
|
|
@@ -8249,7 +8440,7 @@ function getAppManager(machine, app) {
|
|
|
8249
8440
|
return "winget";
|
|
8250
8441
|
return "apt";
|
|
8251
8442
|
}
|
|
8252
|
-
function
|
|
8443
|
+
function shellQuote4(value) {
|
|
8253
8444
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
8254
8445
|
}
|
|
8255
8446
|
function buildAppCommand(machine, app) {
|
|
@@ -8270,7 +8461,7 @@ function buildAppCommand(machine, app) {
|
|
|
8270
8461
|
return `sudo apt-get install -y ${packageName}`;
|
|
8271
8462
|
}
|
|
8272
8463
|
function buildAppProbeCommand(machine, app) {
|
|
8273
|
-
const packageName =
|
|
8464
|
+
const packageName = shellQuote4(getPackageName(app));
|
|
8274
8465
|
const manager = getAppManager(machine, app);
|
|
8275
8466
|
if (manager === "custom") {
|
|
8276
8467
|
return `if command -v ${packageName} >/dev/null 2>&1; then printf 'installed=1\\nversion=custom\\n'; else printf 'installed=0\\n'; fi`;
|
|
@@ -8565,7 +8756,7 @@ var notificationConfigSchema = exports_external.object({
|
|
|
8565
8756
|
function sortChannels(channels) {
|
|
8566
8757
|
return [...channels].sort((left, right) => left.id.localeCompare(right.id));
|
|
8567
8758
|
}
|
|
8568
|
-
function
|
|
8759
|
+
function shellQuote5(value) {
|
|
8569
8760
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
8570
8761
|
}
|
|
8571
8762
|
function hasCommand2(binary) {
|
|
@@ -8612,7 +8803,7 @@ ${message}
|
|
|
8612
8803
|
};
|
|
8613
8804
|
}
|
|
8614
8805
|
if (hasCommand2("mail")) {
|
|
8615
|
-
const command = `printf %s ${
|
|
8806
|
+
const command = `printf %s ${shellQuote5(message)} | mail -s ${shellQuote5(subject)} ${shellQuote5(channel.target)}`;
|
|
8616
8807
|
const result = Bun.spawnSync(["bash", "-lc", command], {
|
|
8617
8808
|
stdout: "pipe",
|
|
8618
8809
|
stderr: "pipe",
|
|
@@ -9038,6 +9229,156 @@ function getStatus() {
|
|
|
9038
9229
|
};
|
|
9039
9230
|
}
|
|
9040
9231
|
|
|
9232
|
+
// src/commands/workspace.ts
|
|
9233
|
+
init_paths();
|
|
9234
|
+
function isRecord2(value) {
|
|
9235
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9236
|
+
}
|
|
9237
|
+
function cloneMetadata(metadata) {
|
|
9238
|
+
return isRecord2(metadata) ? { ...metadata } : {};
|
|
9239
|
+
}
|
|
9240
|
+
function mappedPath(metadata, field, key) {
|
|
9241
|
+
const container = metadata[field];
|
|
9242
|
+
if (!isRecord2(container))
|
|
9243
|
+
return null;
|
|
9244
|
+
const value = container[key];
|
|
9245
|
+
if (typeof value === "string" && value.trim())
|
|
9246
|
+
return value.trim();
|
|
9247
|
+
if (isRecord2(value)) {
|
|
9248
|
+
const nested = value["path"] ?? value["root"] ?? value["workspacePath"] ?? value["workspace_path"];
|
|
9249
|
+
if (typeof nested === "string" && nested.trim())
|
|
9250
|
+
return nested.trim();
|
|
9251
|
+
}
|
|
9252
|
+
return null;
|
|
9253
|
+
}
|
|
9254
|
+
function writeMappedPath(metadata, field, key, path) {
|
|
9255
|
+
const existing = metadata[field];
|
|
9256
|
+
const container = isRecord2(existing) ? { ...existing } : {};
|
|
9257
|
+
container[key] = path;
|
|
9258
|
+
metadata[field] = container;
|
|
9259
|
+
}
|
|
9260
|
+
function buildPatch(input) {
|
|
9261
|
+
const previous = mappedPath(input.metadata, input.field, input.key);
|
|
9262
|
+
if (!input.path) {
|
|
9263
|
+
return {
|
|
9264
|
+
field: input.field,
|
|
9265
|
+
key: input.key,
|
|
9266
|
+
path: null,
|
|
9267
|
+
previous_path: previous,
|
|
9268
|
+
status: "unresolved"
|
|
9269
|
+
};
|
|
9270
|
+
}
|
|
9271
|
+
const status = previous === input.path ? "unchanged" : input.apply ? "written" : "would_write";
|
|
9272
|
+
return {
|
|
9273
|
+
field: input.field,
|
|
9274
|
+
key: input.key,
|
|
9275
|
+
path: input.path,
|
|
9276
|
+
previous_path: previous,
|
|
9277
|
+
status
|
|
9278
|
+
};
|
|
9279
|
+
}
|
|
9280
|
+
function upsertMachineMetadata(manifest, machineId, metadata) {
|
|
9281
|
+
return {
|
|
9282
|
+
...manifest,
|
|
9283
|
+
machines: manifest.machines.map((machine) => machine.id === machineId ? { ...machine, metadata } : machine)
|
|
9284
|
+
};
|
|
9285
|
+
}
|
|
9286
|
+
function repairWorkspaceManifestMappings(options) {
|
|
9287
|
+
const projectId = options.projectId;
|
|
9288
|
+
const repoName = options.repoName ?? projectId;
|
|
9289
|
+
const openFilesRepoName = options.openFilesRepoName ?? "open-files";
|
|
9290
|
+
const apply = options.apply === true;
|
|
9291
|
+
const resolution = resolveMachineWorkspace({
|
|
9292
|
+
machineId: options.machineId,
|
|
9293
|
+
projectId,
|
|
9294
|
+
repoName,
|
|
9295
|
+
openFilesRepoName,
|
|
9296
|
+
projectRoot: options.projectRoot,
|
|
9297
|
+
openFilesRoot: options.openFilesRoot,
|
|
9298
|
+
workspaceRoot: options.workspaceRoot,
|
|
9299
|
+
includeTailscale: options.includeTailscale,
|
|
9300
|
+
now: options.now
|
|
9301
|
+
});
|
|
9302
|
+
const warnings = [...resolution.warnings];
|
|
9303
|
+
const manifest = readManifest();
|
|
9304
|
+
const manifestMachineId = resolution.machine_id ?? options.machineId;
|
|
9305
|
+
const machine = manifest.machines.find((entry) => entry.id === manifestMachineId) ?? null;
|
|
9306
|
+
const trusted = resolution.machine.trust_status === "trusted" || options.allowUntrusted === true;
|
|
9307
|
+
if (!machine) {
|
|
9308
|
+
warnings.push(`manifest_machine_missing:${manifestMachineId}`);
|
|
9309
|
+
return {
|
|
9310
|
+
ok: false,
|
|
9311
|
+
applied: false,
|
|
9312
|
+
manifest_path: getManifestPath(),
|
|
9313
|
+
machine_id: resolution.machine_id,
|
|
9314
|
+
project_id: projectId,
|
|
9315
|
+
repo_name: repoName,
|
|
9316
|
+
open_files_repo_name: openFilesRepoName,
|
|
9317
|
+
trusted,
|
|
9318
|
+
resolution,
|
|
9319
|
+
patches: [],
|
|
9320
|
+
warnings
|
|
9321
|
+
};
|
|
9322
|
+
}
|
|
9323
|
+
const metadata = cloneMetadata(machine.metadata);
|
|
9324
|
+
const patches = [
|
|
9325
|
+
buildPatch({
|
|
9326
|
+
metadata,
|
|
9327
|
+
field: "workspace_paths",
|
|
9328
|
+
key: projectId,
|
|
9329
|
+
path: options.projectRoot ?? resolution.paths.project_root.path,
|
|
9330
|
+
apply
|
|
9331
|
+
}),
|
|
9332
|
+
buildPatch({
|
|
9333
|
+
metadata,
|
|
9334
|
+
field: "open_files_roots",
|
|
9335
|
+
key: projectId,
|
|
9336
|
+
path: options.openFilesRoot ?? resolution.paths.open_files_root.path,
|
|
9337
|
+
apply
|
|
9338
|
+
})
|
|
9339
|
+
];
|
|
9340
|
+
const hasUnresolved = patches.some((patch) => patch.status === "unresolved");
|
|
9341
|
+
const hasWrites = patches.some((patch) => patch.status === "would_write" || patch.status === "written");
|
|
9342
|
+
if (apply && hasWrites && !trusted) {
|
|
9343
|
+
warnings.push(`manifest_repair_requires_trusted_machine:${manifestMachineId}`);
|
|
9344
|
+
return {
|
|
9345
|
+
ok: false,
|
|
9346
|
+
applied: false,
|
|
9347
|
+
manifest_path: getManifestPath(),
|
|
9348
|
+
machine_id: manifestMachineId,
|
|
9349
|
+
project_id: projectId,
|
|
9350
|
+
repo_name: repoName,
|
|
9351
|
+
open_files_repo_name: openFilesRepoName,
|
|
9352
|
+
trusted,
|
|
9353
|
+
resolution,
|
|
9354
|
+
patches: patches.map((patch) => patch.status === "written" ? { ...patch, status: "would_write" } : patch),
|
|
9355
|
+
warnings
|
|
9356
|
+
};
|
|
9357
|
+
}
|
|
9358
|
+
let applied = false;
|
|
9359
|
+
if (apply && !hasUnresolved && hasWrites) {
|
|
9360
|
+
for (const patch of patches) {
|
|
9361
|
+
if (patch.path && patch.status === "written")
|
|
9362
|
+
writeMappedPath(metadata, patch.field, patch.key, patch.path);
|
|
9363
|
+
}
|
|
9364
|
+
writeManifest(upsertMachineMetadata(manifest, manifestMachineId, metadata));
|
|
9365
|
+
applied = true;
|
|
9366
|
+
}
|
|
9367
|
+
return {
|
|
9368
|
+
ok: resolution.ok && !hasUnresolved && (!apply || applied || !hasWrites),
|
|
9369
|
+
applied,
|
|
9370
|
+
manifest_path: getManifestPath(),
|
|
9371
|
+
machine_id: manifestMachineId,
|
|
9372
|
+
project_id: projectId,
|
|
9373
|
+
repo_name: repoName,
|
|
9374
|
+
open_files_repo_name: openFilesRepoName,
|
|
9375
|
+
trusted,
|
|
9376
|
+
resolution,
|
|
9377
|
+
patches,
|
|
9378
|
+
warnings
|
|
9379
|
+
};
|
|
9380
|
+
}
|
|
9381
|
+
|
|
9041
9382
|
// src/compatibility.ts
|
|
9042
9383
|
init_db();
|
|
9043
9384
|
var DEFAULT_COMMANDS = [
|
|
@@ -9047,7 +9388,7 @@ var DEFAULT_COMMANDS = [
|
|
|
9047
9388
|
function defaultPackages() {
|
|
9048
9389
|
return [{ name: "@hasna/machines", command: "machines", expectedVersion: getPackageVersion(), required: true }];
|
|
9049
9390
|
}
|
|
9050
|
-
function
|
|
9391
|
+
function shellQuote6(value) {
|
|
9051
9392
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
9052
9393
|
}
|
|
9053
9394
|
function commandId(value) {
|
|
@@ -9098,7 +9439,7 @@ function defaultRunner2(machineId, command) {
|
|
|
9098
9439
|
return runMachineCommand(machineId, command);
|
|
9099
9440
|
}
|
|
9100
9441
|
function inspectCommand(machineId, spec, runner) {
|
|
9101
|
-
const command =
|
|
9442
|
+
const command = shellQuote6(spec.command);
|
|
9102
9443
|
const versionArgs = spec.versionArgs ?? "--version";
|
|
9103
9444
|
const script = [
|
|
9104
9445
|
`cmd=${command}`,
|
|
@@ -9127,7 +9468,7 @@ function fieldCommand(field) {
|
|
|
9127
9468
|
}
|
|
9128
9469
|
function inspectWorkspace(machineId, spec, runner) {
|
|
9129
9470
|
const script = [
|
|
9130
|
-
`path=${
|
|
9471
|
+
`path=${shellQuote6(spec.path)}`,
|
|
9131
9472
|
'printf "exists=%s\\n" "$(test -d "$path" && printf yes || printf no)"',
|
|
9132
9473
|
'pkg="$path/package.json"',
|
|
9133
9474
|
'printf "package_json=%s\\n" "$(test -f "$pkg" && printf yes || printf no)"',
|
|
@@ -10769,6 +11110,12 @@ function renderCompatibilityResult(result) {
|
|
|
10769
11110
|
`);
|
|
10770
11111
|
}
|
|
10771
11112
|
function renderWorkspaceResolution(result) {
|
|
11113
|
+
const diagnosticSummary = result.diagnostics.reduce((summary, entry) => {
|
|
11114
|
+
summary[entry.severity] += 1;
|
|
11115
|
+
return summary;
|
|
11116
|
+
}, { ok: 0, warn: 0, fail: 0 });
|
|
11117
|
+
const diagnosticLines = result.diagnostics.filter((entry) => entry.severity !== "ok").map((entry) => `${entry.id}: ${entry.status} ${entry.message}`);
|
|
11118
|
+
const repairLines = result.repair_hints.map((hint) => `${hint.reason}: ${hint.shell_command}`);
|
|
10772
11119
|
return renderKeyValueTable([
|
|
10773
11120
|
["machine", result.machine_id ?? result.requested_machine_id],
|
|
10774
11121
|
["ok", String(result.ok)],
|
|
@@ -10781,8 +11128,30 @@ function renderWorkspaceResolution(result) {
|
|
|
10781
11128
|
["workspace root", `${result.paths.workspace_root.path ?? "unresolved"} (${result.paths.workspace_root.source})`],
|
|
10782
11129
|
["project root", `${result.paths.project_root.path ?? "unresolved"} (${result.paths.project_root.source})`],
|
|
10783
11130
|
["open-files root", `${result.paths.open_files_root.path ?? "unresolved"} (${result.paths.open_files_root.source})`],
|
|
11131
|
+
["diagnostics", `${diagnosticSummary.ok} ok, ${diagnosticSummary.warn} warn, ${diagnosticSummary.fail} fail`],
|
|
10784
11132
|
["warnings", result.warnings.join(", ") || "none"]
|
|
10785
|
-
])
|
|
11133
|
+
]) + `
|
|
11134
|
+
` + renderList("issues", diagnosticLines) + `
|
|
11135
|
+
` + renderList("repair hints", repairLines);
|
|
11136
|
+
}
|
|
11137
|
+
function renderWorkspaceRepairResult(result) {
|
|
11138
|
+
const patchLines = result.patches.map((patch) => {
|
|
11139
|
+
const path = patch.path ?? "unresolved";
|
|
11140
|
+
const previous = patch.previous_path ? ` previous=${patch.previous_path}` : "";
|
|
11141
|
+
return `${patch.field}.${patch.key}: ${patch.status} ${path}${previous}`;
|
|
11142
|
+
});
|
|
11143
|
+
return [
|
|
11144
|
+
renderKeyValueTable([
|
|
11145
|
+
["machine", result.machine_id ?? "unresolved"],
|
|
11146
|
+
["project", result.project_id],
|
|
11147
|
+
["trusted", String(result.trusted)],
|
|
11148
|
+
["applied", String(result.applied)],
|
|
11149
|
+
["manifest", result.manifest_path],
|
|
11150
|
+
["warnings", result.warnings.join(", ") || "none"]
|
|
11151
|
+
]),
|
|
11152
|
+
renderList("patches", patchLines)
|
|
11153
|
+
].join(`
|
|
11154
|
+
`);
|
|
10786
11155
|
}
|
|
10787
11156
|
function renderFleetStatus(status) {
|
|
10788
11157
|
return [
|
|
@@ -10951,6 +11320,39 @@ workspaceCommand.command("resolve").description("Resolve repo and open-files roo
|
|
|
10951
11320
|
if (!result.ok && !options.json)
|
|
10952
11321
|
process.exitCode = 1;
|
|
10953
11322
|
});
|
|
11323
|
+
workspaceCommand.command("doctor").description("Diagnose repo and open-files workspace mappings and print repair hints").requiredOption("--machine <id>", "Machine identifier").requiredOption("--project <id>", "Canonical project id").option("--repo <name>", "Repository name; defaults to project id").option("--open-files-repo <name>", "Open-files repository name", "open-files").option("--primary-machine <id>", "Primary machine id for the project").option("--workspace-root <path>", "Override the machine workspace root").option("--project-root <path>", "Override the resolved project root").option("--open-files-root <path>", "Override the resolved open-files root").option("--no-tailscale", "Skip tailscale status probing").option("-j, --json", "Print JSON output", false).action((options) => {
|
|
11324
|
+
const result = resolveMachineWorkspace({
|
|
11325
|
+
machineId: options.machine,
|
|
11326
|
+
projectId: options.project,
|
|
11327
|
+
repoName: options.repo,
|
|
11328
|
+
openFilesRepoName: options.openFilesRepo,
|
|
11329
|
+
primaryMachineId: options.primaryMachine,
|
|
11330
|
+
workspaceRoot: options.workspaceRoot,
|
|
11331
|
+
projectRoot: options.projectRoot,
|
|
11332
|
+
openFilesRoot: options.openFilesRoot,
|
|
11333
|
+
includeTailscale: options.tailscale !== false
|
|
11334
|
+
});
|
|
11335
|
+
printJsonOrText(result, renderWorkspaceResolution(result), options.json);
|
|
11336
|
+
if (result.diagnostics.some((entry) => entry.severity === "fail") && !options.json)
|
|
11337
|
+
process.exitCode = 1;
|
|
11338
|
+
});
|
|
11339
|
+
workspaceCommand.command("repair").description("Preview or write explicit manifest path mappings for inferred workspace roots").requiredOption("--machine <id>", "Machine identifier").requiredOption("--project <id>", "Canonical project id").option("--repo <name>", "Repository name; defaults to project id").option("--open-files-repo <name>", "Open-files repository name", "open-files").option("--workspace-root <path>", "Override the machine workspace root for resolution").option("--project-root <path>", "Explicit project root to write").option("--open-files-root <path>", "Explicit open-files root to write").option("--apply", "Write the mappings into the manifest", false).option("--allow-untrusted", "Allow writing mappings for machines not marked trusted", false).option("--no-tailscale", "Skip tailscale status probing").option("-j, --json", "Print JSON output", false).action((options) => {
|
|
11340
|
+
const result = repairWorkspaceManifestMappings({
|
|
11341
|
+
machineId: options.machine,
|
|
11342
|
+
projectId: options.project,
|
|
11343
|
+
repoName: options.repo,
|
|
11344
|
+
openFilesRepoName: options.openFilesRepo,
|
|
11345
|
+
workspaceRoot: options.workspaceRoot,
|
|
11346
|
+
projectRoot: options.projectRoot,
|
|
11347
|
+
openFilesRoot: options.openFilesRoot,
|
|
11348
|
+
apply: options.apply,
|
|
11349
|
+
allowUntrusted: options.allowUntrusted,
|
|
11350
|
+
includeTailscale: options.tailscale !== false
|
|
11351
|
+
});
|
|
11352
|
+
printJsonOrText(result, renderWorkspaceRepairResult(result), options.json);
|
|
11353
|
+
if (!result.ok && !options.json)
|
|
11354
|
+
process.exitCode = 1;
|
|
11355
|
+
});
|
|
10954
11356
|
program2.command("diff").description("Show manifest differences between two machines").requiredOption("--left <id>", "Left machine identifier").option("--right <id>", "Right machine identifier (defaults to current machine)").option("-j, --json", "Print JSON output", false).action((options) => {
|
|
10955
11357
|
const result = diffMachines(options.left, options.right);
|
|
10956
11358
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type MachineWorkspaceResolution } from "../topology.js";
|
|
2
|
+
export interface WorkspaceManifestRepairOptions {
|
|
3
|
+
machineId: string;
|
|
4
|
+
projectId: string;
|
|
5
|
+
repoName?: string;
|
|
6
|
+
openFilesRepoName?: string;
|
|
7
|
+
projectRoot?: string;
|
|
8
|
+
openFilesRoot?: string;
|
|
9
|
+
workspaceRoot?: string;
|
|
10
|
+
includeTailscale?: boolean;
|
|
11
|
+
apply?: boolean;
|
|
12
|
+
allowUntrusted?: boolean;
|
|
13
|
+
now?: Date;
|
|
14
|
+
}
|
|
15
|
+
export type WorkspaceManifestRepairStatus = "unchanged" | "would_write" | "written" | "unresolved";
|
|
16
|
+
export interface WorkspaceManifestRepairPatch {
|
|
17
|
+
field: "workspace_paths" | "open_files_roots";
|
|
18
|
+
key: string;
|
|
19
|
+
path: string | null;
|
|
20
|
+
previous_path: string | null;
|
|
21
|
+
status: WorkspaceManifestRepairStatus;
|
|
22
|
+
}
|
|
23
|
+
export interface WorkspaceManifestRepairResult {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
applied: boolean;
|
|
26
|
+
manifest_path: string;
|
|
27
|
+
machine_id: string | null;
|
|
28
|
+
project_id: string;
|
|
29
|
+
repo_name: string;
|
|
30
|
+
open_files_repo_name: string;
|
|
31
|
+
trusted: boolean;
|
|
32
|
+
resolution: MachineWorkspaceResolution;
|
|
33
|
+
patches: WorkspaceManifestRepairPatch[];
|
|
34
|
+
warnings: string[];
|
|
35
|
+
}
|
|
36
|
+
export declare function repairWorkspaceManifestMappings(options: WorkspaceManifestRepairOptions): WorkspaceManifestRepairResult;
|
|
37
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/commands/workspace.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAG1F,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,6BAA6B,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,GAAG,YAAY,CAAC;AAEnG,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CAAC;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,6BAA6B,CAAC;CACvC;AAED,MAAM,WAAW,6BAA6B;IAC5C,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,0BAA0B,CAAC;IACvC,OAAO,EAAE,4BAA4B,EAAE,CAAC;IACxC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAqED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,8BAA8B,GAAG,6BAA6B,CAkGtH"}
|
package/dist/consumer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { MACHINES_CONSUMER_CAPABILITIES, MACHINES_CONSUMER_CONTRACT, MACHINES_CONSUMER_ENTRYPOINT, MACHINES_CONSUMER_CONTRACT_VERSION, MACHINES_PACKAGE_NAME, discoverMachineTopology, getMachinesConsumerCapabilities, getLocalMachineTopology, resolveMachineRoute, resolveMachineWorkspace, } from "./topology.js";
|
|
2
|
-
export type { MachineRouteConfidence, MachineRouteHint, MachineRouteKind, MachineRouteOptions, MachineRouteResolution, MachineTopology, MachineTopologyEntry, MachineTopologyOptions, MachineWorkspaceAuthStatus, MachineWorkspaceOptions, MachineWorkspacePath, MachineWorkspacePathSource, MachineWorkspaceProject, MachineWorkspaceResolution, MachineWorkspaceTrustStatus, MachinesConsumerContract, MachinesConsumerEnvelope, MachinesConsumerCapabilities, MachinesContractPackage, TopologyCommandResult, TopologyCommandRunner, } from "./topology.js";
|
|
2
|
+
export type { MachineRouteConfidence, MachineRouteHint, MachineRouteKind, MachineRouteOptions, MachineRouteResolution, MachineTopology, MachineTopologyEntry, MachineTopologyOptions, MachineWorkspaceAuthStatus, MachineWorkspaceDiagnostic, MachineWorkspaceDiagnosticStatus, MachineWorkspaceOptions, MachineWorkspacePath, MachineWorkspacePathSource, MachineWorkspaceProject, MachineWorkspaceRepairHint, MachineWorkspaceResolution, MachineWorkspaceTrustStatus, MachinesConsumerContract, MachinesConsumerEnvelope, MachinesConsumerCapabilities, MachinesContractPackage, TopologyCommandResult, TopologyCommandRunner, } from "./topology.js";
|
|
3
3
|
export { checkMachineCompatibility, } from "./compatibility.js";
|
|
4
4
|
export type { CompatibilityCheck, CompatibilityCommandRunner, CompatibilityCommandSpec, CompatibilityPackageSpec, CompatibilitySource, CompatibilityStatus, CompatibilityWorkspaceSpec, MachineCompatibilityOptions, MachineCompatibilityReport, } from "./compatibility.js";
|
|
5
5
|
export { resolveMachineCommand, runMachineCommand, } from "./remote.js";
|
package/dist/consumer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../src/consumer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,4BAA4B,EAC5B,kCAAkC,EAClC,qBAAqB,EACrB,uBAAuB,EACvB,+BAA+B,EAC/B,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,wBAAwB,EACxB,wBAAwB,EACxB,4BAA4B,EAC5B,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACnB,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,GAClB,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../src/consumer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,4BAA4B,EAC5B,kCAAkC,EAClC,qBAAqB,EACrB,uBAAuB,EACvB,+BAA+B,EAC/B,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,0BAA0B,EAC1B,gCAAgC,EAChC,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,0BAA0B,EAC1B,2BAA2B,EAC3B,wBAAwB,EACxB,wBAAwB,EACxB,4BAA4B,EAC5B,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACnB,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,GAClB,MAAM,cAAc,CAAC"}
|