cabletidy 0.1.0
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/LICENSE +21 -0
- package/README.md +230 -0
- package/bin/cabletidy.mjs +2 -0
- package/package.json +40 -0
- package/src/cli.mjs +141 -0
- package/src/codex-catalog.mjs +227 -0
- package/src/codex-config-file.mjs +67 -0
- package/src/codex-native-provider.mjs +311 -0
- package/src/config.mjs +361 -0
- package/src/model-resolver.mjs +187 -0
- package/src/server.mjs +1269 -0
- package/src/target-artifacts.mjs +177 -0
- package/src/validation.mjs +387 -0
- package/web/app.js +1642 -0
- package/web/config-identity.js +73 -0
- package/web/index.html +48 -0
- package/web/styles.css +1188 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export function configurationId(name, fallback = "codex") {
|
|
2
|
+
const normalize = (value) => String(value || "")
|
|
3
|
+
.trim()
|
|
4
|
+
.toLowerCase()
|
|
5
|
+
.replace(/[^a-z0-9_-]+/g, "-")
|
|
6
|
+
.replace(/^[^a-z0-9]+|-+$/g, "")
|
|
7
|
+
.slice(0, 54)
|
|
8
|
+
.replace(/-+$/, "");
|
|
9
|
+
return normalize(name) || normalize(fallback) || "codex";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function providerIdForConfiguration(id) {
|
|
13
|
+
return `cabletidy_${id}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function configurationBaseUrl(config, id) {
|
|
17
|
+
const host = String(config.web?.listenHost || "127.0.0.1");
|
|
18
|
+
const address = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
19
|
+
return `http://${address}:${config.web?.port || 43100}/${encodeURIComponent(id)}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isRecord(value) {
|
|
23
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function normalizeConfigurationIdentities(config) {
|
|
27
|
+
const bindings = Object.entries(config.bindings);
|
|
28
|
+
const bindingIds = new Map(bindings.map(([id, binding]) => [
|
|
29
|
+
id, isRecord(binding) ? configurationId(binding.name, id) : id,
|
|
30
|
+
]));
|
|
31
|
+
const providerIds = new Map(Object.entries(config.virtualProviders).map(([id, provider]) => {
|
|
32
|
+
if (!isRecord(provider)) return [id, id];
|
|
33
|
+
const owners = bindings.filter(([, binding]) => binding?.virtualProvider === id);
|
|
34
|
+
if (owners.length > 1) return [id, id];
|
|
35
|
+
const owner = owners[0];
|
|
36
|
+
const suffix = id.startsWith("cabletidy_") ? id.slice("cabletidy_".length) : id;
|
|
37
|
+
return [id, providerIdForConfiguration(owner ? bindingIds.get(owner[0]) : configurationId(suffix))];
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
// Leave colliding records intact so validation can report them without
|
|
41
|
+
// losing either configuration or retargeting a reference to another one.
|
|
42
|
+
const uniqueRenames = (ids) => {
|
|
43
|
+
const counts = new Map();
|
|
44
|
+
for (const id of ids.values()) counts.set(id, (counts.get(id) || 0) + 1);
|
|
45
|
+
const result = new Map([...ids].map(([oldId, id]) => [oldId, counts.get(id) === 1 ? id : oldId]));
|
|
46
|
+
let changed;
|
|
47
|
+
do {
|
|
48
|
+
changed = false;
|
|
49
|
+
for (const [oldId, id] of result) {
|
|
50
|
+
if (id !== oldId && result.has(id) && result.get(id) === id) {
|
|
51
|
+
result.set(oldId, oldId);
|
|
52
|
+
changed = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} while (changed);
|
|
56
|
+
return result;
|
|
57
|
+
};
|
|
58
|
+
const renamedBindings = uniqueRenames(bindingIds);
|
|
59
|
+
const renamedProviders = uniqueRenames(providerIds);
|
|
60
|
+
config.bindings = Object.fromEntries(bindings.map(([id, binding]) => [
|
|
61
|
+
renamedBindings.get(id),
|
|
62
|
+
isRecord(binding) ? {
|
|
63
|
+
...binding,
|
|
64
|
+
id: renamedBindings.get(id),
|
|
65
|
+
virtualProvider: renamedProviders.get(binding.virtualProvider) ?? binding.virtualProvider,
|
|
66
|
+
} : binding,
|
|
67
|
+
]));
|
|
68
|
+
config.virtualProviders = Object.fromEntries(Object.entries(config.virtualProviders).map(([id, provider]) => [
|
|
69
|
+
renamedProviders.get(id),
|
|
70
|
+
isRecord(provider) ? { ...provider, id: renamedProviders.get(id) } : provider,
|
|
71
|
+
]));
|
|
72
|
+
return { bindingIds: renamedBindings, providerIds: renamedProviders };
|
|
73
|
+
}
|
package/web/index.html
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="theme-color" content="#f5f6f7" />
|
|
7
|
+
<title>CableTidy 管理</title>
|
|
8
|
+
<link rel="stylesheet" href="/styles.css" />
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="app" class="app-shell">
|
|
12
|
+
<aside class="rail">
|
|
13
|
+
<div class="brand">
|
|
14
|
+
<strong>CableTidy</strong>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="rail-status">
|
|
17
|
+
<span class="status-light" aria-hidden="true"></span>
|
|
18
|
+
<span id="rail-status">连接中</span>
|
|
19
|
+
</div>
|
|
20
|
+
<nav class="nav" aria-label="主导航">
|
|
21
|
+
<button class="nav-item is-active" data-page="overview">
|
|
22
|
+
配置套装
|
|
23
|
+
</button>
|
|
24
|
+
<button class="nav-item" data-page="diagnostics">
|
|
25
|
+
诊断
|
|
26
|
+
</button>
|
|
27
|
+
</nav>
|
|
28
|
+
<div class="rail-footer">
|
|
29
|
+
<span id="revision-label">版本 0</span>
|
|
30
|
+
</div>
|
|
31
|
+
</aside>
|
|
32
|
+
|
|
33
|
+
<main class="main" id="main-content">
|
|
34
|
+
<header class="topbar">
|
|
35
|
+
<div>
|
|
36
|
+
<h1 id="page-title">配置套装</h1>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="topbar-actions">
|
|
39
|
+
<button class="button button-quiet" id="refresh-button">刷新</button>
|
|
40
|
+
</div>
|
|
41
|
+
</header>
|
|
42
|
+
<div id="toast-region" class="toast-region" aria-live="polite"></div>
|
|
43
|
+
<section id="page-content" class="page-content"></section>
|
|
44
|
+
</main>
|
|
45
|
+
</div>
|
|
46
|
+
<script type="module" src="/app.js"></script>
|
|
47
|
+
</body>
|
|
48
|
+
</html>
|