@agentproto/apps 0.13.1 → 0.16.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.
@@ -2,7 +2,7 @@
2
2
  * The `@agentproto/config` deep-link fragment grammar (plan §3.4):
3
3
  *
4
4
  * #<section>[/<id>[/<sub>]]
5
- * section := wallets | harnesses | models | defaults | remote | advanced
5
+ * section := wallets | harnesses | models | capabilities | defaults | remote | advanced
6
6
  * id := encodeURIComponent(profileId | adapterSlug | modelRef | configKey | "pairing")
7
7
  *
8
8
  * Written in plain ES5-safe syntax (`var`/`function`, no arrows, no
@@ -13,7 +13,7 @@
13
13
  * section list) is declared INSIDE the function body so the embedded
14
14
  * source has no dependency on this module's closure.
15
15
  */
16
- export declare const CONFIG_SECTIONS: readonly ["wallets", "harnesses", "models", "defaults", "remote", "advanced"];
16
+ export declare const CONFIG_SECTIONS: readonly ["wallets", "harnesses", "models", "capabilities", "defaults", "remote", "advanced"];
17
17
  export type ConfigSection = (typeof CONFIG_SECTIONS)[number];
18
18
  export interface ParsedConfigFragment {
19
19
  section: ConfigSection;
@@ -1 +1 @@
1
- {"version":3,"file":"fragment.d.ts","sourceRoot":"","sources":["../../src/config/fragment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,eAAe,+EAAgF,CAAA;AAE5G,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE5D,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAA;IACtB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED;;;gBAGgB;AAChB,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CA0BtE;AAED;2EAC2E;AAC3E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAKtF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAG,OAAO,CAEjG"}
1
+ {"version":3,"file":"fragment.d.ts","sourceRoot":"","sources":["../../src/config/fragment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,eAAe,+FAAgG,CAAA;AAE5H,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE5D,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAA;IACtB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED;;;gBAGgB;AAChB,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CA0BtE;AAED;2EAC2E;AAC3E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAKtF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAG,OAAO,CAEjG"}
@@ -38,6 +38,6 @@
38
38
  * `location.hash` wins when the panel first loads with one already set;
39
39
  * every tool-input notification after that re-routes unconditionally.
40
40
  */
41
- export declare const CONFIG_TOOLS: readonly ["auth_profile_list", "auth_profile_create", "auth_profile_delete", "auth_profile_set_enabled", "auth_profile_set_models", "auth_profile_refresh_models", "auth_profile_import", "auth_discover_credentials", "auth_profile_update", "adapter_list", "harness_capabilities", "harness_preset_list", "harness_preset_create", "harness_preset_delete", "harness_preset_set_default", "catalog_models", "catalog_provider_models", "role_list", "usage_rollup", "config_get", "config_set", "daemon_health", "remote_status", "remote_enable", "remote_disable", "pair_list", "pair_offer", "pair_revoke", "tunnel_list", "tunnel_status"];
41
+ export declare const CONFIG_TOOLS: readonly ["auth_profile_list", "auth_profile_create", "auth_profile_delete", "auth_profile_set_enabled", "auth_profile_set_models", "auth_profile_refresh_models", "auth_profile_import", "auth_discover_credentials", "auth_profile_update", "adapter_list", "harness_capabilities", "capabilities_inventory", "mcp_imported_tool_list", "mcp_imported_status", "mcp_import", "mcp_imported_remove", "harness_preset_list", "harness_preset_create", "harness_preset_delete", "harness_preset_set_default", "catalog_models", "catalog_provider_models", "role_list", "usage_rollup", "config_get", "config_set", "daemon_health", "remote_status", "remote_enable", "remote_disable", "pair_list", "pair_offer", "pair_revoke", "tunnel_list", "tunnel_status"];
42
42
  export declare const CONFIG_HTML: string;
43
43
  //# sourceMappingURL=ui.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/config/ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAIH,eAAO,MAAM,YAAY,mnBA+Bf,CAAA;AAaV,eAAO,MAAM,WAAW,QAytEhB,CAAA"}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/config/ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAIH,eAAO,MAAM,YAAY,muBAoCf,CAAA;AAcV,eAAO,MAAM,WAAW,QA4/EhB,CAAA"}
package/dist/config.mjs CHANGED
@@ -7,9 +7,9 @@ import { defineApp } from '@agentproto/app-kit';
7
7
 
8
8
 
9
9
  // src/config/fragment.ts
10
- var CONFIG_SECTIONS = ["wallets", "harnesses", "models", "defaults", "remote", "advanced"];
10
+ var CONFIG_SECTIONS = ["wallets", "harnesses", "models", "capabilities", "defaults", "remote", "advanced"];
11
11
  function parseConfigFragment(hash) {
12
- var SECTIONS2 = ["wallets", "harnesses", "models", "defaults", "remote", "advanced"];
12
+ var SECTIONS2 = ["wallets", "harnesses", "models", "capabilities", "defaults", "remote", "advanced"];
13
13
  var raw = String(hash || "").replace(/^#/, "");
14
14
  var parts = raw.split("/").filter(function(p) {
15
15
  return p.length > 0;
@@ -58,6 +58,11 @@ var CONFIG_TOOLS = [
58
58
  "auth_profile_update",
59
59
  "adapter_list",
60
60
  "harness_capabilities",
61
+ "capabilities_inventory",
62
+ "mcp_imported_tool_list",
63
+ "mcp_imported_status",
64
+ "mcp_import",
65
+ "mcp_imported_remove",
61
66
  "harness_preset_list",
62
67
  "harness_preset_create",
63
68
  "harness_preset_delete",
@@ -83,6 +88,7 @@ var SECTIONS = [
83
88
  { id: "wallets", label: "Wallets" },
84
89
  { id: "harnesses", label: "Harnesses" },
85
90
  { id: "models", label: "Models" },
91
+ { id: "capabilities", label: "Capabilities" },
86
92
  { id: "defaults", label: "Defaults" },
87
93
  { id: "remote", label: "Remote" },
88
94
  { id: "advanced", label: "Advanced" }
@@ -230,6 +236,7 @@ td.matrix-cell.disabled{opacity:.35;cursor:default}
230
236
  <div class="section" data-section="wallets" id="sec-wallets"></div>
231
237
  <div class="section" data-section="harnesses" id="sec-harnesses"></div>
232
238
  <div class="section" data-section="models" id="sec-models"></div>
239
+ <div class="section" data-section="capabilities" id="sec-capabilities"></div>
233
240
  <div class="section" data-section="defaults" id="sec-defaults"></div>
234
241
  <div class="section" data-section="remote" id="sec-remote"></div>
235
242
  <div class="section" data-section="advanced" id="sec-advanced"></div>
@@ -767,11 +774,31 @@ function showSection(sectionId) {
767
774
  function applyDeepLink(sectionId, id, sub) {
768
775
  var notfound = document.getElementById("notfound");
769
776
  notfound.style.display = "none";
770
- var prevHl = document.querySelectorAll(".card.hl, tr.hl");
777
+ var prevHl = document.querySelectorAll(".card.hl, tr.hl, th.hl");
771
778
  for (var p = 0; p < prevHl.length; p++) prevHl[p].classList.remove("hl");
772
779
  if (id === undefined) return;
773
780
  var container = document.getElementById("sec-" + sectionId);
774
781
  if (!container) return;
782
+ // #capabilities/<mcp|skills>/<sub>: 'id' selects the TAB, not a card \u2014
783
+ // switch tab (re-rendering the section) before looking for 'sub's card.
784
+ if (sectionId === "capabilities") {
785
+ var capTab = id === "skills" ? "skills" : "mcp";
786
+ if (capabilitiesState.tab !== capTab) {
787
+ capabilitiesState.tab = capTab;
788
+ renderCapabilities();
789
+ container = document.getElementById("sec-" + sectionId);
790
+ }
791
+ if (sub === undefined) return;
792
+ var capTarget = container.querySelector('[data-card-id="' + cssEscape(sub) + '"]');
793
+ if (!capTarget) {
794
+ notfound.textContent = "not found: " + id + "/" + sub;
795
+ notfound.style.display = "block";
796
+ return;
797
+ }
798
+ capTarget.classList.add("hl");
799
+ scrollIntoViewSafe(capTarget);
800
+ return;
801
+ }
775
802
  var targetId = sectionId === "remote" && id === "pairing" ? "pairing" : id;
776
803
  var target = container.querySelector('[data-card-id="' + cssEscape(targetId) + '"]');
777
804
  if (!target) {
@@ -872,6 +899,7 @@ var SECTION_LOADERS = {
872
899
  wallets: loadWallets,
873
900
  harnesses: loadHarnesses,
874
901
  models: loadModels,
902
+ capabilities: loadCapabilities,
875
903
  defaults: loadDefaults,
876
904
  remote: loadRemote,
877
905
  advanced: loadAdvanced,
@@ -1955,6 +1983,275 @@ function renderTitlerModelCard() {
1955
1983
  });
1956
1984
  }
1957
1985
 
1986
+ // ============================================================
1987
+ // Capabilities (MCP & Skills)
1988
+ // ============================================================
1989
+
1990
+ var capabilitiesState = {
1991
+ tab: "mcp", // "mcp" | "skills"
1992
+ invResult: null,
1993
+ expandedImports: {}, // importId -> bool
1994
+ };
1995
+
1996
+ function loadCapabilities() {
1997
+ return loadTool("capabilities_inventory", {}).then(function (r) {
1998
+ capabilitiesState.invResult = r;
1999
+ renderCapabilities();
2000
+ });
2001
+ }
2002
+
2003
+ function capTabsHtml() {
2004
+ return '<span class="pillbar">'
2005
+ + '<button type="button" class="' + (capabilitiesState.tab === "mcp" ? "active" : "") + '" data-cap-tab="mcp">MCP</button>'
2006
+ + '<button type="button" class="' + (capabilitiesState.tab === "skills" ? "active" : "") + '" data-cap-tab="skills">Skills</button>'
2007
+ + '</span>';
2008
+ }
2009
+
2010
+ function capStatusTagHtml(status) {
2011
+ var cls = status === "connected" ? "ok" : status === "error" ? "bad" : status === "idle" ? "" : "warn-tag";
2012
+ return '<span class="tag ' + cls + '">' + escHtml(status) + '</span>';
2013
+ }
2014
+
2015
+ function renderCapabilities() {
2016
+ var el = document.getElementById("sec-capabilities");
2017
+ var r = capabilitiesState.invResult;
2018
+ var html = '<div class="sect-h">Capabilities' + capTabsHtml() + '</div>';
2019
+ if (!r) { el.innerHTML = html; return; }
2020
+ if (!r.ok) {
2021
+ html += r.unknown
2022
+ ? '<div class="muted-note">Capabilities inventory needs a newer agentproto daemon (capabilities_inventory).</div>'
2023
+ : '<div class="muted-note">capabilities_inventory: ' + escHtml(r.message) + '</div>';
2024
+ el.innerHTML = html;
2025
+ return;
2026
+ }
2027
+ var inv = r.data;
2028
+ html += capabilitiesState.tab === "skills" ? renderSkillsTabHtml(inv.skills || {}) : renderMcpTabHtml(inv.mcp || {});
2029
+ el.innerHTML = html;
2030
+ wireCapabilitiesTabs(el);
2031
+ if (capabilitiesState.tab === "mcp") wireMcpTab(el);
2032
+ }
2033
+
2034
+ function wireCapabilitiesTabs(container) {
2035
+ var btns = container.querySelectorAll("[data-cap-tab]");
2036
+ for (var i = 0; i < btns.length; i++) {
2037
+ (function (btn) {
2038
+ btn.addEventListener("click", function () {
2039
+ capabilitiesState.tab = btn.getAttribute("data-cap-tab");
2040
+ renderCapabilities();
2041
+ });
2042
+ })(btns[i]);
2043
+ }
2044
+ }
2045
+
2046
+ function renderMcpTabHtml(mcp) {
2047
+ var imported = mcp.imported || [];
2048
+ var discovered = mcp.discovered || [];
2049
+ var reach = mcp.daemonMountByHarness || {};
2050
+ var html = "";
2051
+ if (mcp.error) html += '<div class="muted-note">mcp: ' + escHtml(mcp.error) + '</div>';
2052
+
2053
+ html += '<div class="sect-h">Imported MCP servers</div>';
2054
+ if (imported.length === 0) {
2055
+ html += '<div class="empty">No MCP servers imported yet.</div>';
2056
+ } else {
2057
+ html += '<div class="card"><div class="body"><table><thead><tr><th>Name</th><th>Source</th><th>Status</th><th>Tools</th><th>Used by</th><th></th><th></th></tr></thead><tbody>';
2058
+ for (var i = 0; i < imported.length; i++) {
2059
+ var m = imported[i];
2060
+ var expanded = !!capabilitiesState.expandedImports[m.id];
2061
+ var usedBy = (m.usedBySessions || []).length;
2062
+ html += '<tr data-card-id="' + escHtml(m.id) + '" data-row-id="' + escHtml(m.id) + '">'
2063
+ + '<td>' + escHtml(m.alias || m.name) + '</td>'
2064
+ + '<td>' + escHtml(m.source) + '</td>'
2065
+ + '<td>' + capStatusTagHtml(m.status) + (m.error ? ' <span class="tag bad" title="' + escHtml(m.error) + '">!</span>' : '') + '</td>'
2066
+ + '<td>' + (m.toolCount != null ? String(m.toolCount) : "&#8211;") + '</td>'
2067
+ + '<td>' + (usedBy > 0 ? "used by " + usedBy + " session" + (usedBy === 1 ? "" : "s") : "unused") + '</td>'
2068
+ + '<td><button type="button" class="btn-sm" data-cap-import-tools="' + escHtml(m.id) + '" data-cap-import-alias="' + escHtml(m.alias || m.name) + '">' + (expanded ? "hide tools" : "show tools") + '</button></td>'
2069
+ + '<td><button type="button" class="btn-sm danger" data-cap-import-remove="' + escHtml(m.id) + '">remove</button></td>'
2070
+ + '</tr>'
2071
+ + '<tr><td colspan="7"><div id="cap-import-tools-' + escHtml(pathId(m.id)) + '"></div></td></tr>';
2072
+ }
2073
+ html += '</tbody></table></div></div>';
2074
+ }
2075
+
2076
+ html += '<div class="sect-h">Reach</div>';
2077
+ var slugs = Object.keys(reach);
2078
+ if (slugs.length === 0) {
2079
+ html += '<div class="empty">No adapters installed.</div>';
2080
+ } else {
2081
+ html += '<div class="card"><div class="body">';
2082
+ for (var s = 0; s < slugs.length; s++) {
2083
+ var r2 = reach[slugs[s]];
2084
+ var cls = r2 === "default" ? "ok" : r2 === "on-request" ? "" : "bad";
2085
+ html += '<span class="tag ' + cls + '" style="margin:2px 4px 2px 0">' + escHtml(slugs[s]) + ": " + escHtml(r2) + '</span>';
2086
+ }
2087
+ html += '<div class="muted-note">"default" harnesses get the imported pool automatically; "on-request" harnesses can mount it explicitly via <code>mcpServers</code>; "none" harnesses cannot reach it at all. Either way, reach is via <code>mcp_imported_tool_list</code> / <code>mcp_imported_call</code>, not a direct per-server mount.</div>';
2088
+ html += '</div></div>';
2089
+ }
2090
+
2091
+ html += '<div class="sect-h">Discovered, not imported</div>';
2092
+ var notImported = discovered.filter(function (d) { return !d.imported; });
2093
+ if (notImported.length === 0) {
2094
+ html += '<div class="empty">Nothing new discovered.</div>';
2095
+ } else {
2096
+ html += '<div class="card"><div class="body"><table><thead><tr><th>Name</th><th>Source</th><th>Type</th><th></th></tr></thead><tbody>';
2097
+ for (var d = 0; d < notImported.length; d++) {
2098
+ var disc = notImported[d];
2099
+ html += '<tr data-card-id="' + escHtml(disc.id) + '" data-row-id="' + escHtml(disc.id) + '"><td>' + escHtml(disc.name) + '</td><td>' + escHtml(disc.source) + '</td><td>' + escHtml(disc.type) + '</td>'
2100
+ + '<td><button type="button" class="btn-sm primary" data-cap-import="' + escHtml(disc.id) + '">import</button></td></tr>';
2101
+ }
2102
+ html += '</tbody></table></div></div>';
2103
+ }
2104
+ return html;
2105
+ }
2106
+
2107
+ function wireMcpTab(container) {
2108
+ var toolBtns = container.querySelectorAll("[data-cap-import-tools]");
2109
+ for (var i = 0; i < toolBtns.length; i++) {
2110
+ (function (btn) {
2111
+ btn.addEventListener("click", function () {
2112
+ var id = btn.getAttribute("data-cap-import-tools");
2113
+ var alias = btn.getAttribute("data-cap-import-alias");
2114
+ var expanded = !capabilitiesState.expandedImports[id];
2115
+ capabilitiesState.expandedImports[id] = expanded;
2116
+ btn.textContent = expanded ? "hide tools" : "show tools";
2117
+ var host = document.getElementById("cap-import-tools-" + pathId(id));
2118
+ if (!host) return;
2119
+ if (!expanded) { host.innerHTML = ""; return; }
2120
+ host.innerHTML = '<div class="muted-note">Loading tools&hellip;</div>';
2121
+ loadTool("mcp_imported_tool_list", { alias: alias }).then(function (r) {
2122
+ if (!r.ok) { host.innerHTML = '<div class="muted-note">mcp_imported_tool_list: ' + escHtml(r.message) + '</div>'; return; }
2123
+ var tools = (r.data && r.data.tools) || [];
2124
+ if (tools.length === 0) { host.innerHTML = '<div class="empty">No tools (server may not be connected yet).</div>'; return; }
2125
+ var h = '<div class="muted-note">' + tools.length + ' tool(s):</div><ul>';
2126
+ for (var t = 0; t < tools.length; t++) {
2127
+ h += '<li><code>' + escHtml(tools[t].name) + '</code>' + (tools[t].description ? " &mdash; " + escHtml(tools[t].description) : "") + '</li>';
2128
+ }
2129
+ h += '</ul>';
2130
+ host.innerHTML = h;
2131
+ });
2132
+ });
2133
+ })(toolBtns[i]);
2134
+ }
2135
+
2136
+ var removeBtns = container.querySelectorAll("[data-cap-import-remove]");
2137
+ for (var r3 = 0; r3 < removeBtns.length; r3++) {
2138
+ (function (btn) {
2139
+ var id = btn.getAttribute("data-cap-import-remove");
2140
+ btn.addEventListener("click", function () {
2141
+ confirmClick("cap-import-remove-" + id, btn, function () {
2142
+ loadTool("mcp_imported_remove", { id: id }).then(function (res) {
2143
+ if (!res.ok) { toast(res.message, true); return; }
2144
+ toast("Removed.", false);
2145
+ loadCapabilities();
2146
+ });
2147
+ });
2148
+ });
2149
+ })(removeBtns[r3]);
2150
+ }
2151
+
2152
+ var importBtns = container.querySelectorAll("[data-cap-import]");
2153
+ for (var im = 0; im < importBtns.length; im++) {
2154
+ (function (btn) {
2155
+ var id = btn.getAttribute("data-cap-import");
2156
+ btn.addEventListener("click", function () {
2157
+ confirmClick("cap-import-" + id, btn, function () {
2158
+ loadTool("mcp_import", { sourceMcpId: id }).then(function (res) {
2159
+ if (!res.ok) { toast(res.message, true); return; }
2160
+ toast("Imported.", false);
2161
+ loadCapabilities();
2162
+ });
2163
+ });
2164
+ });
2165
+ })(importBtns[im]);
2166
+ }
2167
+ }
2168
+
2169
+ function capPackHasSkill(packs, name) {
2170
+ for (var i = 0; i < packs.length; i++) {
2171
+ if ((packs[i].skills || []).indexOf(name) !== -1) return true;
2172
+ }
2173
+ return false;
2174
+ }
2175
+
2176
+ function renderSkillsTabHtml(skills) {
2177
+ var harnesses = skills.byHarness || [];
2178
+ var packs = skills.packs || [];
2179
+ var defaults = skills.defaults || { global: [], byAdapter: {} };
2180
+ var html = "";
2181
+ if (skills.error) html += '<div class="muted-note">skills: ' + escHtml(skills.error) + '</div>';
2182
+
2183
+ html += '<div class="sect-h">Skills by harness</div>';
2184
+ if (harnesses.length === 0) {
2185
+ html += '<div class="empty">No adapters installed.</div>';
2186
+ return html;
2187
+ }
2188
+
2189
+ var skillNames = {};
2190
+ for (var p = 0; p < packs.length; p++) {
2191
+ for (var s = 0; s < (packs[p].skills || []).length; s++) skillNames[packs[p].skills[s]] = true;
2192
+ }
2193
+ for (var h = 0; h < harnesses.length; h++) {
2194
+ for (var i2 = 0; i2 < (harnesses[h].installed || []).length; i2++) skillNames[harnesses[h].installed[i2]] = true;
2195
+ for (var n = 0; n < (harnesses[h].native || []).length; n++) skillNames[harnesses[h].native[n]] = true;
2196
+ }
2197
+ var names = Object.keys(skillNames).sort();
2198
+
2199
+ html += '<div class="card"><div class="body" style="overflow-x:auto"><table><thead><tr><th>Skill</th>';
2200
+ for (var c = 0; c < harnesses.length; c++) {
2201
+ var target = harnesses[c].target;
2202
+ html += '<th data-card-id="' + escHtml(harnesses[c].adapter) + '">' + escHtml(harnesses[c].adapter)
2203
+ + '<div class="cfg-help">' + (target ? escHtml(target.dir || "") : "unsupported") + '</div>'
2204
+ + (harnesses[c].spawnOption ? '<div class="cfg-help">skills spawn option</div>' : '')
2205
+ + '</th>';
2206
+ }
2207
+ html += '</tr></thead><tbody>';
2208
+
2209
+ html += '<tr><td><em>defaults</em></td>';
2210
+ for (var d = 0; d < harnesses.length; d++) {
2211
+ var adapterDefaults = defaults.byAdapter[harnesses[d].adapter] || [];
2212
+ var all = defaults.global.concat(adapterDefaults);
2213
+ html += '<td>' + (all.length > 0 ? escHtml(all.join(", ")) : "&#8211;") + '</td>';
2214
+ }
2215
+ html += '</tr>';
2216
+
2217
+ if (names.length === 0) {
2218
+ html += '<tr><td colspan="' + (harnesses.length + 1) + '">No skills found in any installed pack.</td></tr>';
2219
+ }
2220
+ for (var nm = 0; nm < names.length; nm++) {
2221
+ html += '<tr><td>' + escHtml(names[nm]) + '</td>';
2222
+ for (var hc = 0; hc < harnesses.length; hc++) {
2223
+ var row = harnesses[hc];
2224
+ var cell;
2225
+ if (!row.target) {
2226
+ cell = '<span class="tag">unsupported</span>';
2227
+ } else if ((row.installed || []).indexOf(names[nm]) !== -1) {
2228
+ cell = '<span class="tag ok">installed</span>';
2229
+ } else if ((row.native || []).indexOf(names[nm]) !== -1) {
2230
+ cell = '<span class="tag ok">native</span>';
2231
+ } else if (capPackHasSkill(packs, names[nm])) {
2232
+ cell = '<span class="tag warn-tag">available</span><div class="cfg-help"><code>agentproto install skill/' + escHtml(names[nm]) + '</code></div>';
2233
+ } else {
2234
+ cell = '<span class="tag">&#8211;</span>';
2235
+ }
2236
+ html += '<td>' + cell + '</td>';
2237
+ }
2238
+ html += '</tr>';
2239
+ }
2240
+ html += '</tbody></table></div></div>';
2241
+
2242
+ html += '<div class="sect-h">Packs</div>';
2243
+ if (packs.length === 0) {
2244
+ html += '<div class="empty">No skill packs found under ~/.agentproto/packs.</div>';
2245
+ } else {
2246
+ html += '<div class="card"><div class="body"><table><thead><tr><th>Pack</th><th>Version</th><th>Skills</th></tr></thead><tbody>';
2247
+ for (var pk = 0; pk < packs.length; pk++) {
2248
+ html += '<tr data-card-id="' + escHtml(packs[pk].name) + '"><td>' + escHtml(packs[pk].name) + '</td><td>' + escHtml(packs[pk].version || "") + '</td><td>' + escHtml((packs[pk].skills || []).join(", ")) + '</td></tr>';
2249
+ }
2250
+ html += '</tbody></table></div></div>';
2251
+ }
2252
+ return html;
2253
+ }
2254
+
1958
2255
  // ============================================================
1959
2256
  // Defaults & messaging
1960
2257
  // ============================================================