@desplega.ai/agent-swarm 1.113.0 → 1.115.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/README.md +2 -2
- package/dist/{actions-q4n7cz6e.js → actions-17ya5wde.js} +8 -9
- package/dist/{app-es4nzc71.js → app-4f718yk0.js} +6 -6
- package/dist/{assistant-0x8ey0vs.js → assistant-ah7csnjz.js} +19 -18
- package/dist/{boot-reembed-69agkhv6.js → boot-reembed-4jpxhb25.js} +6 -6
- package/dist/{boot-reembed-p0ny05t8.js → boot-reembed-qata34xa.js} +5 -5
- package/dist/{boot-scrub-logs-yybrkmvr.js → boot-scrub-logs-tv48nn3z.js} +4 -4
- package/dist/{claude-adapter-mma9a1yr.js → claude-adapter-3csn5bks.js} +3 -3
- package/dist/{claude-managed-adapter-pv2nbqy4.js → claude-managed-adapter-kysmkz89.js} +2 -2
- package/dist/cli-0pygq9r1.js +429 -0
- package/dist/{cli-hsetkkd3.js → cli-12vb0xg6.js} +33 -9
- package/dist/{cli-ncfgsqbd.js → cli-15y7qhzp.js} +262 -33
- package/dist/{cli-7ga43spr.js → cli-1yjrbg1f.js} +1 -1
- package/dist/{cli-36ymmpmp.js → cli-3samkyap.js} +2 -2
- package/dist/cli-52fg7s7x.js +269 -0
- package/dist/{cli-hjhvekf4.js → cli-65ekje01.js} +11 -8
- package/dist/{cli-h3k622d0.js → cli-6aq8xhvm.js} +90 -3
- package/dist/{cli-f3qa069v.js → cli-7xje5amz.js} +1 -1
- package/dist/cli-86fdnerz.js +405 -0
- package/dist/{cli-9cy6nc5m.js → cli-8fjwym53.js} +1 -1
- package/dist/{cli-c3frqf65.js → cli-b23axgz4.js} +3 -3
- package/dist/{cli-a0t7vapf.js → cli-bc89my0z.js} +1 -1
- package/dist/{cli-bnzbn8j8.js → cli-beea4a3y.js} +73 -24
- package/dist/{cli-q21d49ac.js → cli-bw80ck94.js} +1 -1
- package/dist/{cli-pwc5e83c.js → cli-cchksf5q.js} +12 -7
- package/dist/{cli-30bbaveh.js → cli-cmw9rzy5.js} +2 -2
- package/dist/{cli-y6mrptcn.js → cli-cnwssmsr.js} +1 -1
- package/dist/{cli-r5g6ngtn.js → cli-dcca6j8m.js} +46 -5
- package/dist/{cli-tkmm5nf9.js → cli-e61fs2hp.js} +59 -2
- package/dist/{cli-6wscj9z0.js → cli-e7306swv.js} +14 -14
- package/dist/{cli-dcfyyh3t.js → cli-ftyf58xj.js} +2 -2
- package/dist/{cli-4xyj9jtq.js → cli-hrhye2rm.js} +1062 -478
- package/dist/cli-jkhwmmgg.js +136 -0
- package/dist/{cli-y4ycrnjc.js → cli-k3y4z3h5.js} +1 -1
- package/dist/{cli-6xd0bvf8.js → cli-ma04faw5.js} +1 -1
- package/dist/{cli-2fc97xm5.js → cli-mnbgv3w7.js} +4 -4
- package/dist/{cli-trmapn9k.js → cli-pet3aev6.js} +95 -10
- package/dist/{cli-z46pnksz.js → cli-q1rx7g0d.js} +175 -28
- package/dist/{cli-qwgtacd6.js → cli-r9f6ngrf.js} +1244 -336
- package/dist/{cli-gtkqc144.js → cli-shvaxw27.js} +13 -7
- package/dist/{cli-ywez6yps.js → cli-sq7f72d6.js} +1 -1
- package/dist/cli-t0vqevnm.js +393 -0
- package/dist/{cli-tbw13sx8.js → cli-tece6zgm.js} +1 -1
- package/dist/{cli-bgvskydy.js → cli-tffvrh6j.js} +1 -1
- package/dist/cli-wh91zby6.js +187222 -0
- package/dist/{cli-anrj584m.js → cli-xz9aq0rf.js} +1 -1
- package/dist/{cli-mq580e06.js → cli-zsrdty74.js} +18 -18
- package/dist/cli.js +32 -15
- package/dist/{codex-adapter-5cmy858w.js → codex-adapter-de3w0nch.js} +10 -10
- package/dist/{codex-session-runner-46q8mrba.js → codex-session-runner-bnw8cysk.js} +10 -10
- package/dist/{commands-z5dbwta3.js → commands-hch4e2k1.js} +4 -4
- package/dist/{db-bbgahh4z.js → db-p3h2j06x.js} +14 -4
- package/dist/{handlers-6b44317z.js → handlers-9er4zx9q.js} +17 -31
- package/dist/{hook-sbp5fmps.js → hook-4axs756z.js} +7 -7
- package/dist/{http-2xz43gz3.js → http-809fvtz2.js} +1934 -886
- package/dist/{index-wz1ea15p.js → index-2dgfj9av.js} +3 -3
- package/dist/{index-nhat35em.js → index-47em3d2n.js} +14 -11
- package/dist/{index-5ghxn8s6.js → index-9fqaktjr.js} +12 -9
- package/dist/{index-0gzmqj4k.js → index-9ht8gr56.js} +13 -10
- package/dist/{index-vfedgdw6.js → index-rm85cfdx.js} +368 -40
- package/dist/{index-ykvs43z4.js → index-t2snazez.js} +4 -4
- package/dist/{keepalive-62gfj91d.js → keepalive-3cps6e3e.js} +7 -6
- package/dist/{lead-ag0akn0v.js → lead-fkk95wgq.js} +36 -32
- package/dist/{maintenance-xxrrzk5x.js → maintenance-fweqh0yj.js} +6 -6
- package/dist/{mistral-conversations-26yrnbf1.js → mistral-conversations-7gqnz2wt.js} +7 -7
- package/dist/oauth-refresh-sweep-sycqtbph.js +114 -0
- package/dist/{onboard-64zyfcbs.js → onboard-cy0rkgvx.js} +2 -2
- package/dist/{opencode-adapter-mqsxr170.js → opencode-adapter-p6bxnbm4.js} +2 -2
- package/dist/{otel-zjd9zaxs.js → otel-g299k41b.js} +2 -2
- package/dist/{otel-impl-b86qseaa.js → otel-impl-wbns8hf7.js} +1 -1
- package/dist/{pi-mono-adapter-kxjxpfgy.js → pi-mono-adapter-05sck1ds.js} +17 -109
- package/dist/{pricing-refresh-cgtkffpa.js → pricing-refresh-0hvazmkf.js} +6 -6
- package/dist/rbac-roles-kfesjy9y.js +28 -0
- package/dist/rbac-roles-mwcwhbxc.js +283 -0
- package/dist/{seed-pricing-1r1y1xkq.js → seed-pricing-h2kdq4cq.js} +5 -5
- package/dist/{setup-mkcgkjf7.js → setup-8506q97w.js} +2 -2
- package/dist/{worker-351sze63.js → worker-4r5c6zyb.js} +36 -32
- package/openapi.json +1327 -20
- package/package.json +3 -3
- package/src/agentmail/handlers.ts +16 -6
- package/src/agentmail/templates.ts +25 -5
- package/src/be/audit-user.ts +28 -4
- package/src/be/db-queries/oauth.ts +42 -0
- package/src/be/db.ts +314 -28
- package/src/be/identity.ts +79 -0
- package/src/be/mcp-proxy.ts +132 -0
- package/src/be/migrations/109_rbac_roles.sql +79 -0
- package/src/be/migrations/111_oauth_credential_bindings.sql +6 -0
- package/src/be/migrations/112_script_connections_graphql.sql +96 -0
- package/src/be/migrations/113_task_routing_affinity.sql +9 -0
- package/src/be/migrations/114_backfill_gpt_5_6_pricing.sql +15 -0
- package/src/be/modelsdev-cache.json +135 -0
- package/src/be/oauth-credential-bindings.ts +35 -0
- package/src/be/oauth-refresh-sweep.ts +159 -0
- package/src/be/rbac-audit.ts +52 -13
- package/src/be/rbac-roles.ts +424 -0
- package/src/be/script-connections.ts +459 -40
- package/src/be/script-credential-broker.ts +29 -3
- package/src/be/scripts/typecheck.ts +51 -7
- package/src/be/seed-scripts/catalog/catalog-report.inline.ts +325 -24
- package/src/be/seed-scripts/catalog/catalog-report.ts +325 -24
- package/src/be/users.ts +24 -0
- package/src/cli.tsx +17 -0
- package/src/github/handlers.ts +10 -9
- package/src/github/templates.ts +45 -9
- package/src/gitlab/handlers.ts +20 -8
- package/src/gitlab/templates.ts +18 -6
- package/src/heartbeat/heartbeat.ts +143 -21
- package/src/http/all-routes.ts +3 -0
- package/src/http/approval-requests.ts +12 -0
- package/src/http/config.ts +44 -10
- package/src/http/core.ts +30 -3
- package/src/http/favorites.ts +1 -5
- package/src/http/index.ts +25 -1
- package/src/http/mcp-oauth.ts +2 -0
- package/src/http/mcp-servers.ts +44 -3
- package/src/http/oauth-generic.ts +105 -0
- package/src/http/poll.ts +20 -3
- package/src/http/script-connection-proxy.ts +116 -0
- package/src/http/script-connections.ts +1657 -0
- package/src/http/scripts.ts +27 -7
- package/src/http/skills.ts +11 -0
- package/src/http/tasks.ts +27 -13
- package/src/http/x.ts +6 -2
- package/src/integrations/kapso/inbound.ts +23 -6
- package/src/jira/sync.ts +111 -17
- package/src/jira/templates.ts +10 -2
- package/src/mcp-client/http-client.ts +194 -0
- package/src/oauth/app-validation.ts +16 -0
- package/src/oauth/ensure-token.ts +69 -14
- package/src/oauth/mcp-wrapper.ts +15 -0
- package/src/oauth/wrapper.ts +48 -20
- package/src/prompts/session-templates.ts +3 -3
- package/src/providers/codex-models.ts +100 -30
- package/src/providers/modelsdev-reasoning.json +51 -0
- package/src/providers/pi-mono-mcp-client.ts +2 -133
- package/src/providers/reasoning-effort.ts +11 -5
- package/src/rbac/admission.ts +68 -0
- package/src/rbac/index.ts +2 -0
- package/src/rbac/legacy-policy.ts +12 -0
- package/src/rbac/permissions.ts +48 -0
- package/src/scheduler/scheduler.ts +9 -1
- package/src/scripts-runtime/api-client.ts +151 -19
- package/src/scripts-runtime/api-types.ts +38 -6
- package/src/scripts-runtime/credential-broker/default-bindings.ts +1 -0
- package/src/scripts-runtime/credential-broker/types.ts +8 -0
- package/src/scripts-runtime/ctx.ts +11 -1
- package/src/scripts-runtime/eval-harness.ts +5 -1
- package/src/scripts-runtime/executors/types.ts +2 -1
- package/src/scripts-runtime/loader.ts +3 -1
- package/src/scripts-runtime/mcp-client.ts +87 -0
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/types/stdlib.d.ts +6 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +19 -0
- package/src/server-user.ts +114 -59
- package/src/server.ts +12 -0
- package/src/slack/assistant.ts +14 -6
- package/src/slack/enrich.ts +17 -0
- package/src/slack/handlers.ts +8 -22
- package/src/slack/responses.ts +91 -1
- package/src/slack/thread-buffer.ts +6 -3
- package/src/slack/watcher.ts +9 -6
- package/src/tasks/worker-follow-up.ts +100 -3
- package/src/tests/agentmail-handlers.test.ts +32 -3
- package/src/tests/approval-requests.test.ts +61 -0
- package/src/tests/audit-user.test.ts +47 -1
- package/src/tests/codex-adapter.test.ts +46 -23
- package/src/tests/credential-broker.test.ts +5 -2
- package/src/tests/delete-page-tool.test.ts +197 -0
- package/src/tests/github-handlers.test.ts +28 -2
- package/src/tests/gitlab-handlers.test.ts +125 -0
- package/src/tests/heartbeat.test.ts +48 -3
- package/src/tests/identity.test.ts +117 -0
- package/src/tests/jira-sync.test.ts +99 -1
- package/src/tests/kapso-inbound.test.ts +7 -0
- package/src/tests/mcp-tools-user.test.ts +60 -6
- package/src/tests/model-control.test.ts +8 -2
- package/src/tests/oauth-credential-bindings.test.ts +490 -0
- package/src/tests/oauth-refresh-sweep.test.ts +160 -0
- package/src/tests/pool-affinity.test.ts +401 -0
- package/src/tests/prompt-template-remaining.test.ts +2 -2
- package/src/tests/rbac-admission-e2e.test.ts +386 -0
- package/src/tests/rbac-admission.test.ts +492 -0
- package/src/tests/rbac-e2e-helpers.ts +3 -0
- package/src/tests/rbac-engine.test.ts +50 -0
- package/src/tests/rbac-mcp-admission-e2e.test.ts +362 -0
- package/src/tests/rbac-roles.test.ts +328 -0
- package/src/tests/reasoning-effort.test.ts +15 -2
- package/src/tests/schedule-target-type.test.ts +50 -0
- package/src/tests/scheduled-tasks.test.ts +40 -0
- package/src/tests/script-apis-mcp.test.ts +37 -13
- package/src/tests/script-connections-http.test.ts +1158 -0
- package/src/tests/script-connections-mcp.test.ts +894 -0
- package/src/tests/script-connections.test.ts +754 -6
- package/src/tests/scripts-external-api.test.ts +49 -1
- package/src/tests/scripts-runtime-secret-egress.test.ts +1 -0
- package/src/tests/scripts-runtime.test.ts +291 -0
- package/src/tests/seed-scripts.test.ts +36 -0
- package/src/tests/slack-identity-resolution.test.ts +37 -3
- package/src/tests/slack-inline-output.test.ts +65 -0
- package/src/tests/slack-thread-buffer.test.ts +28 -0
- package/src/tests/tool-annotations.test.ts +1 -0
- package/src/tests/user-token-rest-auth.test.ts +140 -1
- package/src/tests/workflow-swarm-script.test.ts +44 -0
- package/src/tests/workflow-triggers-v2.test.ts +300 -1
- package/src/tools/credential-bindings/tool.ts +277 -18
- package/src/tools/delete-page.ts +145 -0
- package/src/tools/mcp-servers/mcp-server-delete.ts +7 -4
- package/src/tools/request-human-input.ts +2 -0
- package/src/tools/resolve-user.ts +77 -27
- package/src/tools/script-apis.ts +62 -3
- package/src/tools/script-connections/tool.ts +275 -31
- package/src/tools/send-task.ts +15 -1
- package/src/tools/task-action.ts +34 -2
- package/src/tools/templates.ts +53 -1
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/utils.ts +2 -0
- package/src/tools/workflows/create-workflow.ts +8 -1
- package/src/tools/workflows/update-workflow.ts +11 -2
- package/src/types.ts +90 -11
- package/src/utils/scoped-resource.ts +25 -0
- package/src/workflows/executors/human-in-the-loop.ts +1 -0
- package/src/workflows/executors/swarm-script.ts +7 -1
- package/src/workflows/triggers.ts +146 -18
- package/templates/skills/swarm-scripts/content.md +33 -8
- package/dist/cli-bgef578c.js +0 -771
- package/dist/cli-c9mtm09b.js +0 -43
- package/dist/cli-ceewqajw.js +0 -1698
- package/dist/{anthropic-messages-8nc6r2dw.js → anthropic-messages-kq3pjhbp.js} +6 -6
- package/dist/{azure-openai-responses-6yfzw3qw.js → azure-openai-responses-gm1ejtmn.js} +7 -7
- package/dist/{cli-vbraamxr.js → cli-6b4wwmm8.js} +9 -9
- package/dist/{cli-2r7s8jhp.js → cli-sznberje.js} +3 -3
- package/dist/{google-generative-ai-vfq6ymz9.js → google-generative-ai-hyrra2jm.js} +3 -3
- package/dist/{google-vertex-mj5y5rfj.js → google-vertex-8bs2pzgn.js} +3 -3
- package/dist/{openai-codex-responses-8h6kvfv8.js → openai-codex-responses-3nkvje79.js} +4 -4
- package/dist/{openai-completions-x6kjnqjd.js → openai-completions-0asftkwg.js} +11 -11
- package/dist/{openai-responses-7jwyz3ds.js → openai-responses-pmatek45.js} +10 -10
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
SwarmConfigCredentialBindingStore,
|
|
5
5
|
} from "@/scripts-runtime/credential-broker";
|
|
6
6
|
import type { EgressSecretEntry } from "@/scripts-runtime/executors/types";
|
|
7
|
-
import { registerVolatileSecret } from "@/utils/secret-scrubber";
|
|
7
|
+
import { registerVolatileSecret, scrubSecrets } from "@/utils/secret-scrubber";
|
|
8
8
|
import { getResolvedConfig, getSwarmConfigs } from "./db";
|
|
9
|
+
import { resolveOAuthBindingToken } from "./oauth-credential-bindings";
|
|
9
10
|
import { listRelationalCredentialBindings } from "./script-connections";
|
|
10
11
|
|
|
11
12
|
class RelationalCredentialBindingStore extends SwarmConfigCredentialBindingStore {
|
|
@@ -18,12 +19,37 @@ class RelationalCredentialBindingStore extends SwarmConfigCredentialBindingStore
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export function buildScriptCredentialBindings(input: {
|
|
22
|
+
export async function buildScriptCredentialBindings(input: {
|
|
22
23
|
agentId?: string;
|
|
23
24
|
repoId?: string;
|
|
24
|
-
}): EgressSecretEntry[] {
|
|
25
|
+
}): Promise<EgressSecretEntry[]> {
|
|
25
26
|
const resolvedConfigs = getResolvedConfig(input.agentId, input.repoId);
|
|
26
27
|
const configMap = new Map(resolvedConfigs.map((config) => [config.key, config.value]));
|
|
28
|
+
const relationalBindings = listRelationalCredentialBindings(input);
|
|
29
|
+
for (const binding of relationalBindings) {
|
|
30
|
+
if (binding.authKind !== "oauth") continue;
|
|
31
|
+
configMap.set(binding.configKey, "");
|
|
32
|
+
if (!binding.oauthProvider) continue;
|
|
33
|
+
|
|
34
|
+
let accessToken: string | undefined;
|
|
35
|
+
try {
|
|
36
|
+
accessToken = await resolveOAuthBindingToken(binding.oauthProvider);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
// A stale/broken provider must not take down unrelated script runs —
|
|
39
|
+
// skip just this binding (the "" shadow above keeps the placeholder
|
|
40
|
+
// unresolved instead of falling through to a same-named config value).
|
|
41
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
42
|
+
console.warn(
|
|
43
|
+
`[script-credential-broker] skipping OAuth binding ${binding.configKey}: token refresh for provider ${binding.oauthProvider} failed: ${scrubSecrets(message)}`,
|
|
44
|
+
);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (!accessToken) continue;
|
|
48
|
+
|
|
49
|
+
configMap.set(binding.configKey, accessToken);
|
|
50
|
+
registerVolatileSecret(accessToken, binding.configKey);
|
|
51
|
+
}
|
|
52
|
+
|
|
27
53
|
const broker = new CredentialBroker(
|
|
28
54
|
new RelationalCredentialBindingStore((filters) => getSwarmConfigs(filters)),
|
|
29
55
|
(configKey) => configMap.get(configKey) ?? process.env[configKey],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
-
import { getScriptApiTypes } from "@/be/script-connections";
|
|
2
|
+
import { getScriptApiTypes, getScriptMcpTypes } from "@/be/script-connections";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Structured diagnostic record returned to API callers when typecheck fails.
|
|
@@ -30,6 +30,17 @@ export const SCRIPT_SDK_TYPES = `
|
|
|
30
30
|
export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
|
|
31
31
|
export type ScriptScope = "agent" | "global";
|
|
32
32
|
export type ScriptFsMode = "none" | "workspace-rw";
|
|
33
|
+
export type ScriptApiRawOptions = { raw: true };
|
|
34
|
+
export type ScriptApiDefaultOptions = { raw?: false };
|
|
35
|
+
|
|
36
|
+
export interface ScriptApiRawResult {
|
|
37
|
+
ok: boolean;
|
|
38
|
+
status: number;
|
|
39
|
+
statusText: string;
|
|
40
|
+
headers: Record<string, string>;
|
|
41
|
+
url: string;
|
|
42
|
+
response: Response;
|
|
43
|
+
}
|
|
33
44
|
|
|
34
45
|
export interface Redacted<T> {
|
|
35
46
|
readonly __redactedBrand?: T;
|
|
@@ -77,7 +88,7 @@ export interface SwarmSdk {
|
|
|
77
88
|
swarm_get(args?: { includeFull?: boolean }): Promise<unknown>;
|
|
78
89
|
agent_info(args?: Record<string, unknown>): Promise<unknown>;
|
|
79
90
|
metrics_get(args?: Record<string, unknown>): Promise<unknown>;
|
|
80
|
-
user_resolve(args?: { kind?: string; externalId?: string; email?: string; userId?: string }): Promise<unknown>;
|
|
91
|
+
user_resolve(args?: { kind?: string; externalId?: string; email?: string; userId?: string; name?: string }): Promise<unknown>;
|
|
81
92
|
db_query(args: { sql: string; params?: unknown[] }): Promise<unknown>;
|
|
82
93
|
// --- config ---
|
|
83
94
|
config_get(args?: { agentId?: string; repoId?: string; key?: string; includeSecrets?: boolean }): Promise<unknown>;
|
|
@@ -210,6 +221,7 @@ export interface SwarmSdk {
|
|
|
210
221
|
|
|
211
222
|
// --- pages & metrics ---
|
|
212
223
|
page_create(args: Record<string, unknown>): Promise<unknown>;
|
|
224
|
+
page_delete(args: { pageId?: string; slug?: string }): Promise<unknown>;
|
|
213
225
|
metric_create(args: Record<string, unknown>): Promise<unknown>;
|
|
214
226
|
|
|
215
227
|
// --- human input ---
|
|
@@ -276,6 +288,7 @@ export interface ScriptContext {
|
|
|
276
288
|
step?: ScriptWorkflowSteps;
|
|
277
289
|
swarm: SwarmSdk & { config: SwarmConfig };
|
|
278
290
|
api: ScriptApiRegistry;
|
|
291
|
+
mcp: ScriptMcpRegistry;
|
|
279
292
|
stdlib: ScriptStdlib;
|
|
280
293
|
logger: ScriptLogger;
|
|
281
294
|
}
|
|
@@ -284,11 +297,14 @@ export interface ScriptContext {
|
|
|
284
297
|
export type ScriptMain = (args: any, ctx: ScriptContext) => unknown | Promise<unknown>;
|
|
285
298
|
`;
|
|
286
299
|
|
|
287
|
-
export function scriptSdkTypesWithGeneratedApis(
|
|
288
|
-
|
|
300
|
+
export function scriptSdkTypesWithGeneratedApis(
|
|
301
|
+
apiTypes = getScriptApiTypes(),
|
|
302
|
+
mcpTypes = getScriptMcpTypes(),
|
|
303
|
+
): string {
|
|
304
|
+
return `${SCRIPT_SDK_TYPES}\n${apiTypes}\n${mcpTypes}\n`;
|
|
289
305
|
}
|
|
290
306
|
|
|
291
|
-
|
|
307
|
+
const STDLIB_MODULE_TYPES = `
|
|
292
308
|
declare module "stdlib" {
|
|
293
309
|
export interface Redacted<T> {
|
|
294
310
|
readonly __redactedBrand?: T;
|
|
@@ -306,11 +322,36 @@ declare module "stdlib" {
|
|
|
306
322
|
export function glob(pattern: string): Promise<string[]>;
|
|
307
323
|
export function table(rows: Array<Record<string, unknown>>): string;
|
|
308
324
|
}
|
|
325
|
+
`;
|
|
309
326
|
|
|
327
|
+
function stdlibTypesFor(sdkModuleBody: string): string {
|
|
328
|
+
return `${STDLIB_MODULE_TYPES}
|
|
310
329
|
declare module "swarm-sdk" {
|
|
311
|
-
${
|
|
330
|
+
${sdkModuleBody.replace(/^/gm, " ")}
|
|
312
331
|
}
|
|
313
332
|
`;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export const SCRIPT_STDLIB_TYPES = stdlibTypesFor(SCRIPT_SDK_TYPES);
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Stdlib blob whose ambient `declare module "swarm-sdk"` also carries the
|
|
339
|
+
* generated per-connection registries (`ScriptApiRegistry` / `ScriptMcpRegistry`
|
|
340
|
+
* and their `<Slug>Api` / `<Slug>Mcp` interfaces).
|
|
341
|
+
*
|
|
342
|
+
* Monaco resolves the bare `import ... from "swarm-sdk"` through this ambient
|
|
343
|
+
* module — unlike the server typechecker, whose custom resolver maps the
|
|
344
|
+
* specifier onto the flat SDK virtual file. Without the generated registries
|
|
345
|
+
* inlined here, the ambient copy of `ScriptContext` references names that do
|
|
346
|
+
* not exist in that module scope ("Cannot find name 'ScriptApiRegistry'") and
|
|
347
|
+
* `ctx.api.<slug>` completions break in the editor.
|
|
348
|
+
*/
|
|
349
|
+
export function scriptStdlibTypesWithGeneratedApis(
|
|
350
|
+
apiTypes = getScriptApiTypes(),
|
|
351
|
+
mcpTypes = getScriptMcpTypes(),
|
|
352
|
+
): string {
|
|
353
|
+
return stdlibTypesFor(`${SCRIPT_SDK_TYPES}\n${apiTypes}\n${mcpTypes}`);
|
|
354
|
+
}
|
|
314
355
|
|
|
315
356
|
/**
|
|
316
357
|
* Minimal ambient declarations for runtime globals the executor (Bun) actually
|
|
@@ -799,7 +840,10 @@ export function typecheckScript(
|
|
|
799
840
|
types: [],
|
|
800
841
|
};
|
|
801
842
|
|
|
802
|
-
const sdkTypes = scriptSdkTypesWithGeneratedApis(
|
|
843
|
+
const sdkTypes = scriptSdkTypesWithGeneratedApis(
|
|
844
|
+
getScriptApiTypes(context),
|
|
845
|
+
getScriptMcpTypes(context),
|
|
846
|
+
);
|
|
803
847
|
const files = new Map<string, string>([
|
|
804
848
|
[USER_FILE, source],
|
|
805
849
|
[SDK_FILE, sdkTypes],
|
|
@@ -47,6 +47,7 @@ function catalogReportHumanLabel(value: string): string {
|
|
|
47
47
|
|
|
48
48
|
function catalogReportFormatMetric(value: unknown): string {
|
|
49
49
|
if (typeof value === "number") return new Intl.NumberFormat("en-US").format(value);
|
|
50
|
+
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
50
51
|
return catalogReportAsText(value);
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -57,6 +58,25 @@ function catalogReportSeverityTone(value?: string): string {
|
|
|
57
58
|
return "low";
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
function catalogReportIsScalar(value: unknown): boolean {
|
|
62
|
+
return value == null || ["string", "number", "boolean"].includes(typeof value);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function catalogReportIsRecord(value: unknown): value is Record<string, unknown> {
|
|
66
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function catalogReportValueClass(value: unknown): string {
|
|
70
|
+
if (typeof value === "number") return "numeric";
|
|
71
|
+
if (typeof value === "boolean") return value ? "positive" : "muted-value";
|
|
72
|
+
return "";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function catalogReportClassAttr(value: unknown): string {
|
|
76
|
+
const className = catalogReportValueClass(value);
|
|
77
|
+
return className ? ` class="${catalogReportHtmlEscape(className)}"` : "";
|
|
78
|
+
}
|
|
79
|
+
|
|
60
80
|
function catalogReportRenderSampleValue(value: unknown): string {
|
|
61
81
|
if (Array.isArray(value)) {
|
|
62
82
|
return value
|
|
@@ -94,9 +114,116 @@ function catalogReportRenderSamples(samples?: unknown[]): string {
|
|
|
94
114
|
</div>`;
|
|
95
115
|
}
|
|
96
116
|
|
|
117
|
+
function catalogReportTableRows(rows: Record<string, unknown>[], limit = 12): string {
|
|
118
|
+
if (rows.length === 0) return '<p class="empty">No rows.</p>';
|
|
119
|
+
const columns = Array.from(new Set(rows.flatMap((row) => Object.keys(row).slice(0, 8)))).slice(0, 8);
|
|
120
|
+
const visible = rows.slice(0, limit);
|
|
121
|
+
const hiddenCount = Math.max(0, rows.length - visible.length);
|
|
122
|
+
return `<div class="data-table" role="region" aria-label="Data table">
|
|
123
|
+
<table>
|
|
124
|
+
<thead><tr>${columns.map((column) => `<th>${catalogReportHtmlEscape(catalogReportHumanLabel(column))}</th>`).join("")}</tr></thead>
|
|
125
|
+
<tbody>
|
|
126
|
+
${visible
|
|
127
|
+
.map(
|
|
128
|
+
(row) =>
|
|
129
|
+
`<tr>${columns
|
|
130
|
+
.map((column) => {
|
|
131
|
+
const value = row[column];
|
|
132
|
+
return `<td${catalogReportClassAttr(value)}>${catalogReportHtmlEscape(
|
|
133
|
+
catalogReportRenderSampleValue(value),
|
|
134
|
+
)}</td>`;
|
|
135
|
+
})
|
|
136
|
+
.join("")}</tr>`,
|
|
137
|
+
)
|
|
138
|
+
.join("")}
|
|
139
|
+
</tbody>
|
|
140
|
+
</table>
|
|
141
|
+
${hiddenCount ? `<p class="table-note">Showing ${visible.length} of ${rows.length} rows. Full payload remains in the appendix.</p>` : ""}
|
|
142
|
+
</div>`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function catalogReportRenderDataValue(value: unknown): string {
|
|
146
|
+
if (catalogReportIsScalar(value)) {
|
|
147
|
+
return `<strong${catalogReportClassAttr(value)}>${catalogReportHtmlEscape(
|
|
148
|
+
catalogReportFormatMetric(value),
|
|
149
|
+
)}</strong>`;
|
|
150
|
+
}
|
|
151
|
+
if (Array.isArray(value)) {
|
|
152
|
+
if (value.every((item) => catalogReportIsRecord(item))) {
|
|
153
|
+
return catalogReportTableRows(value as Record<string, unknown>[]);
|
|
154
|
+
}
|
|
155
|
+
return `<div class="value-list">${value
|
|
156
|
+
.slice(0, 12)
|
|
157
|
+
.map((item) => `<span>${catalogReportHtmlEscape(catalogReportRenderSampleValue(item))}</span>`)
|
|
158
|
+
.join("")}</div>`;
|
|
159
|
+
}
|
|
160
|
+
if (catalogReportIsRecord(value)) {
|
|
161
|
+
const entries = Object.entries(value);
|
|
162
|
+
const scalarEntries = entries.filter(([, entryValue]) => catalogReportIsScalar(entryValue));
|
|
163
|
+
const complexEntries = entries.filter(([, entryValue]) => !catalogReportIsScalar(entryValue));
|
|
164
|
+
return `<div class="data-object">
|
|
165
|
+
${
|
|
166
|
+
scalarEntries.length
|
|
167
|
+
? `<div class="object-stats">${scalarEntries
|
|
168
|
+
.map(
|
|
169
|
+
([label, entryValue]) =>
|
|
170
|
+
`<div><span>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</span><strong${catalogReportClassAttr(
|
|
171
|
+
entryValue,
|
|
172
|
+
)}>${catalogReportHtmlEscape(catalogReportFormatMetric(entryValue))}</strong></div>`,
|
|
173
|
+
)
|
|
174
|
+
.join("")}</div>`
|
|
175
|
+
: ""
|
|
176
|
+
}
|
|
177
|
+
${complexEntries
|
|
178
|
+
.map(
|
|
179
|
+
([label, entryValue]) => `<section class="data-nested">
|
|
180
|
+
<h4>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</h4>
|
|
181
|
+
${catalogReportRenderDataValue(entryValue)}
|
|
182
|
+
</section>`,
|
|
183
|
+
)
|
|
184
|
+
.join("")}
|
|
185
|
+
</div>`;
|
|
186
|
+
}
|
|
187
|
+
return `<pre>${catalogReportHtmlEscape(JSON.stringify(value, null, 2))}</pre>`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function catalogReportRenderDataPanels(entries: Array<[string, unknown]>): string {
|
|
191
|
+
if (entries.length === 0) return "";
|
|
192
|
+
return `<div class="data-panels">
|
|
193
|
+
${entries
|
|
194
|
+
.map(
|
|
195
|
+
([label, value]) => `<section class="data-panel">
|
|
196
|
+
<div class="data-panel-head">
|
|
197
|
+
<h3>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</h3>
|
|
198
|
+
<span>${catalogReportHtmlEscape(
|
|
199
|
+
Array.isArray(value)
|
|
200
|
+
? `${value.length} rows`
|
|
201
|
+
: catalogReportIsRecord(value)
|
|
202
|
+
? `${Object.keys(value).length} fields`
|
|
203
|
+
: "value",
|
|
204
|
+
)}</span>
|
|
205
|
+
</div>
|
|
206
|
+
${catalogReportRenderDataValue(value)}
|
|
207
|
+
</section>`,
|
|
208
|
+
)
|
|
209
|
+
.join("")}
|
|
210
|
+
</div>`;
|
|
211
|
+
}
|
|
212
|
+
|
|
97
213
|
export function renderCatalogReportPage(report: CatalogReport): string {
|
|
214
|
+
const nav = report.sections
|
|
215
|
+
.map(
|
|
216
|
+
(section) =>
|
|
217
|
+
`<a href="#${catalogReportHtmlEscape(section.key)}"><span>${catalogReportHtmlEscape(
|
|
218
|
+
section.label || catalogReportHumanLabel(section.key),
|
|
219
|
+
)}</span><strong>${catalogReportHtmlEscape(catalogReportFormatMetric(section.findingCount ?? section.findings?.length ?? 0))}</strong></a>`,
|
|
220
|
+
)
|
|
221
|
+
.join("");
|
|
98
222
|
const sections = report.sections
|
|
99
223
|
.map((section) => {
|
|
224
|
+
const checkEntries = Object.entries(section.checks || {});
|
|
225
|
+
const scalarChecks = checkEntries.filter(([, value]) => catalogReportIsScalar(value));
|
|
226
|
+
const dataChecks = checkEntries.filter(([, value]) => !catalogReportIsScalar(value));
|
|
100
227
|
const findings = (section.findings || [])
|
|
101
228
|
.map(
|
|
102
229
|
(finding) => `<article class="finding ${catalogReportHtmlEscape(catalogReportSeverityTone(finding.severity))}">
|
|
@@ -114,25 +241,26 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
114
241
|
</article>`,
|
|
115
242
|
)
|
|
116
243
|
.join("");
|
|
117
|
-
const checks =
|
|
244
|
+
const checks = scalarChecks
|
|
118
245
|
.map(
|
|
119
246
|
([label, value]) =>
|
|
120
|
-
`<div class="check"><span>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</span><strong
|
|
121
|
-
|
|
122
|
-
)}</strong></div>`,
|
|
247
|
+
`<div class="check"><span>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</span><strong${catalogReportClassAttr(
|
|
248
|
+
value,
|
|
249
|
+
)}>${catalogReportHtmlEscape(catalogReportFormatMetric(value))}</strong></div>`,
|
|
123
250
|
)
|
|
124
251
|
.join("");
|
|
125
|
-
return `<section class="section">
|
|
252
|
+
return `<section class="section" id="${catalogReportHtmlEscape(section.key)}">
|
|
126
253
|
<div class="section-grid">
|
|
127
254
|
<aside class="checks">
|
|
128
255
|
<p class="section-kicker">${catalogReportHtmlEscape(section.label || catalogReportHumanLabel(section.key))}</p>
|
|
129
|
-
<div class="check-list">${checks}</div>
|
|
256
|
+
<div class="check-list">${checks || '<p class="empty">No scalar checks.</p>'}</div>
|
|
130
257
|
</aside>
|
|
131
258
|
<div>
|
|
132
259
|
<div class="section-head">
|
|
133
260
|
<h2>${catalogReportHtmlEscape(section.goal)}</h2>
|
|
134
261
|
<span>${catalogReportHtmlEscape(catalogReportFormatMetric(section.findingCount ?? section.findings?.length ?? 0))} finding(s)</span>
|
|
135
262
|
</div>
|
|
263
|
+
${catalogReportRenderDataPanels(dataChecks)}
|
|
136
264
|
<div class="findings">
|
|
137
265
|
${findings || '<p class="empty">No actionable findings in this cluster.</p>'}
|
|
138
266
|
</div>
|
|
@@ -153,10 +281,14 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
153
281
|
color-scheme: light;
|
|
154
282
|
--bg: #f5f2ea;
|
|
155
283
|
--panel: #ffffff;
|
|
284
|
+
--panel-soft: #fbfaf6;
|
|
156
285
|
--ink: #18181b;
|
|
157
286
|
--muted: #5f6368;
|
|
287
|
+
--muted-strong: #3f3f46;
|
|
158
288
|
--line: #ded8cb;
|
|
289
|
+
--line-soft: #ebe5d7;
|
|
159
290
|
--accent: #255c99;
|
|
291
|
+
--accent-soft: #edf5ff;
|
|
160
292
|
--danger: #b42318;
|
|
161
293
|
--danger-bg: #fff1f0;
|
|
162
294
|
--warn: #b54708;
|
|
@@ -178,11 +310,19 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
178
310
|
line-height: 1.55;
|
|
179
311
|
}
|
|
180
312
|
main {
|
|
181
|
-
width: min(
|
|
313
|
+
width: min(1240px, calc(100% - 32px));
|
|
182
314
|
margin: 0 auto;
|
|
183
315
|
padding: 48px 0 72px;
|
|
184
316
|
}
|
|
185
|
-
header {
|
|
317
|
+
header {
|
|
318
|
+
display: grid;
|
|
319
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
320
|
+
gap: 24px;
|
|
321
|
+
align-items: end;
|
|
322
|
+
margin-bottom: 24px;
|
|
323
|
+
padding-bottom: 22px;
|
|
324
|
+
border-bottom: 1px solid var(--line);
|
|
325
|
+
}
|
|
186
326
|
.eyebrow {
|
|
187
327
|
margin: 0 0 8px;
|
|
188
328
|
color: var(--muted);
|
|
@@ -194,34 +334,48 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
194
334
|
h1 {
|
|
195
335
|
margin: 0;
|
|
196
336
|
max-width: 860px;
|
|
197
|
-
font-size: clamp(2rem,
|
|
337
|
+
font-size: clamp(2rem, 3.8vw, 3.25rem);
|
|
198
338
|
line-height: 1.05;
|
|
199
339
|
letter-spacing: 0;
|
|
200
340
|
}
|
|
201
341
|
.lede {
|
|
202
|
-
max-width:
|
|
342
|
+
max-width: 840px;
|
|
203
343
|
margin: 16px 0 0;
|
|
204
|
-
color: var(--muted);
|
|
344
|
+
color: var(--muted-strong);
|
|
205
345
|
font-size: 18px;
|
|
206
346
|
}
|
|
347
|
+
.description {
|
|
348
|
+
max-width: 340px;
|
|
349
|
+
margin: 0;
|
|
350
|
+
color: var(--muted);
|
|
351
|
+
font-size: 14px;
|
|
352
|
+
text-align: right;
|
|
353
|
+
}
|
|
207
354
|
.metrics {
|
|
208
355
|
display: grid;
|
|
209
|
-
grid-template-columns: repeat(
|
|
356
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
210
357
|
gap: 12px;
|
|
211
|
-
margin:
|
|
358
|
+
margin: 24px 0 16px;
|
|
212
359
|
}
|
|
213
|
-
.metric, .section, details {
|
|
360
|
+
.metric, .section, details, .report-nav {
|
|
214
361
|
background: var(--panel);
|
|
215
362
|
border: 1px solid var(--line);
|
|
216
363
|
border-radius: var(--radius);
|
|
217
364
|
box-shadow: var(--shadow);
|
|
218
365
|
}
|
|
219
|
-
.metric {
|
|
366
|
+
.metric {
|
|
367
|
+
min-height: 112px;
|
|
368
|
+
padding: 16px;
|
|
369
|
+
display: flex;
|
|
370
|
+
flex-direction: column;
|
|
371
|
+
justify-content: space-between;
|
|
372
|
+
}
|
|
220
373
|
.metric strong {
|
|
221
374
|
display: block;
|
|
222
|
-
font-size:
|
|
375
|
+
font-size: 30px;
|
|
223
376
|
line-height: 1;
|
|
224
377
|
font-variant-numeric: tabular-nums;
|
|
378
|
+
overflow-wrap: anywhere;
|
|
225
379
|
}
|
|
226
380
|
.metric span {
|
|
227
381
|
display: block;
|
|
@@ -230,14 +384,45 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
230
384
|
font-size: 13px;
|
|
231
385
|
font-weight: 650;
|
|
232
386
|
}
|
|
387
|
+
.report-nav {
|
|
388
|
+
display: grid;
|
|
389
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
390
|
+
gap: 1px;
|
|
391
|
+
overflow: hidden;
|
|
392
|
+
margin: 0 0 20px;
|
|
393
|
+
background: var(--line);
|
|
394
|
+
}
|
|
395
|
+
.report-nav a {
|
|
396
|
+
display: flex;
|
|
397
|
+
align-items: center;
|
|
398
|
+
justify-content: space-between;
|
|
399
|
+
gap: 10px;
|
|
400
|
+
min-height: 48px;
|
|
401
|
+
padding: 11px 13px;
|
|
402
|
+
background: var(--panel);
|
|
403
|
+
color: var(--muted-strong);
|
|
404
|
+
text-decoration: none;
|
|
405
|
+
font-size: 13px;
|
|
406
|
+
font-weight: 700;
|
|
407
|
+
}
|
|
408
|
+
.report-nav a:hover { background: var(--accent-soft); }
|
|
409
|
+
.report-nav strong {
|
|
410
|
+
color: var(--accent);
|
|
411
|
+
font-variant-numeric: tabular-nums;
|
|
412
|
+
}
|
|
233
413
|
.section {
|
|
234
|
-
margin-top:
|
|
235
|
-
padding:
|
|
414
|
+
margin-top: 16px;
|
|
415
|
+
padding: 22px;
|
|
236
416
|
}
|
|
237
417
|
.section-grid {
|
|
238
418
|
display: grid;
|
|
239
|
-
grid-template-columns:
|
|
240
|
-
gap:
|
|
419
|
+
grid-template-columns: 240px minmax(0, 1fr);
|
|
420
|
+
gap: 24px;
|
|
421
|
+
}
|
|
422
|
+
.checks {
|
|
423
|
+
position: sticky;
|
|
424
|
+
top: 18px;
|
|
425
|
+
align-self: start;
|
|
241
426
|
}
|
|
242
427
|
.section-kicker {
|
|
243
428
|
margin: 0 0 12px;
|
|
@@ -259,6 +444,7 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
259
444
|
padding: 10px 0;
|
|
260
445
|
border-bottom: 1px solid var(--line);
|
|
261
446
|
}
|
|
447
|
+
.check:last-child { border-bottom: 0; }
|
|
262
448
|
.check span {
|
|
263
449
|
color: var(--muted);
|
|
264
450
|
font-size: 13px;
|
|
@@ -266,6 +452,8 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
266
452
|
.check strong {
|
|
267
453
|
font-size: 18px;
|
|
268
454
|
font-variant-numeric: tabular-nums;
|
|
455
|
+
text-align: right;
|
|
456
|
+
overflow-wrap: anywhere;
|
|
269
457
|
}
|
|
270
458
|
.section-head {
|
|
271
459
|
display: flex;
|
|
@@ -283,11 +471,113 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
283
471
|
}
|
|
284
472
|
.section-head > span {
|
|
285
473
|
flex: 0 0 auto;
|
|
286
|
-
color: var(--
|
|
474
|
+
color: var(--accent);
|
|
475
|
+
background: var(--accent-soft);
|
|
476
|
+
border: 1px solid #cfe5ff;
|
|
477
|
+
border-radius: 999px;
|
|
478
|
+
padding: 5px 10px;
|
|
287
479
|
font-size: 13px;
|
|
288
480
|
font-weight: 700;
|
|
289
481
|
white-space: nowrap;
|
|
290
482
|
}
|
|
483
|
+
.data-panels {
|
|
484
|
+
display: grid;
|
|
485
|
+
gap: 12px;
|
|
486
|
+
margin: 0 0 16px;
|
|
487
|
+
}
|
|
488
|
+
.data-panel {
|
|
489
|
+
border: 1px solid var(--line-soft);
|
|
490
|
+
border-radius: var(--radius);
|
|
491
|
+
background: var(--panel-soft);
|
|
492
|
+
overflow: hidden;
|
|
493
|
+
}
|
|
494
|
+
.data-panel-head {
|
|
495
|
+
display: flex;
|
|
496
|
+
align-items: baseline;
|
|
497
|
+
justify-content: space-between;
|
|
498
|
+
gap: 16px;
|
|
499
|
+
padding: 12px 14px;
|
|
500
|
+
border-bottom: 1px solid var(--line-soft);
|
|
501
|
+
background: #fffdf8;
|
|
502
|
+
}
|
|
503
|
+
.data-panel-head h3,
|
|
504
|
+
.data-nested h4 {
|
|
505
|
+
margin: 0;
|
|
506
|
+
color: var(--muted-strong);
|
|
507
|
+
font-size: 14px;
|
|
508
|
+
line-height: 1.25;
|
|
509
|
+
letter-spacing: 0;
|
|
510
|
+
}
|
|
511
|
+
.data-panel-head span,
|
|
512
|
+
.table-note {
|
|
513
|
+
color: var(--muted);
|
|
514
|
+
font-size: 12px;
|
|
515
|
+
font-weight: 700;
|
|
516
|
+
}
|
|
517
|
+
.data-object {
|
|
518
|
+
display: grid;
|
|
519
|
+
gap: 12px;
|
|
520
|
+
padding: 14px;
|
|
521
|
+
}
|
|
522
|
+
.object-stats {
|
|
523
|
+
display: grid;
|
|
524
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
525
|
+
gap: 10px;
|
|
526
|
+
}
|
|
527
|
+
.object-stats > div {
|
|
528
|
+
min-height: 76px;
|
|
529
|
+
padding: 12px;
|
|
530
|
+
border: 1px solid var(--line-soft);
|
|
531
|
+
border-radius: var(--radius);
|
|
532
|
+
background: var(--panel);
|
|
533
|
+
}
|
|
534
|
+
.object-stats span,
|
|
535
|
+
.value-list span {
|
|
536
|
+
display: block;
|
|
537
|
+
color: var(--muted);
|
|
538
|
+
font-size: 12px;
|
|
539
|
+
font-weight: 700;
|
|
540
|
+
}
|
|
541
|
+
.object-stats strong {
|
|
542
|
+
display: block;
|
|
543
|
+
margin-top: 6px;
|
|
544
|
+
color: var(--ink);
|
|
545
|
+
font-size: 20px;
|
|
546
|
+
line-height: 1.1;
|
|
547
|
+
font-variant-numeric: tabular-nums;
|
|
548
|
+
overflow-wrap: anywhere;
|
|
549
|
+
}
|
|
550
|
+
.data-nested {
|
|
551
|
+
display: grid;
|
|
552
|
+
gap: 8px;
|
|
553
|
+
}
|
|
554
|
+
.data-table {
|
|
555
|
+
overflow-x: auto;
|
|
556
|
+
background: var(--panel);
|
|
557
|
+
}
|
|
558
|
+
.data-table .table-note {
|
|
559
|
+
margin: 0;
|
|
560
|
+
padding: 10px 12px;
|
|
561
|
+
border-top: 1px solid var(--line-soft);
|
|
562
|
+
background: var(--panel-soft);
|
|
563
|
+
}
|
|
564
|
+
.value-list {
|
|
565
|
+
display: flex;
|
|
566
|
+
flex-wrap: wrap;
|
|
567
|
+
gap: 8px;
|
|
568
|
+
padding: 14px;
|
|
569
|
+
}
|
|
570
|
+
.value-list span {
|
|
571
|
+
padding: 6px 9px;
|
|
572
|
+
border: 1px solid var(--line-soft);
|
|
573
|
+
border-radius: 999px;
|
|
574
|
+
background: var(--panel);
|
|
575
|
+
color: var(--muted-strong);
|
|
576
|
+
overflow-wrap: anywhere;
|
|
577
|
+
}
|
|
578
|
+
.numeric { font-variant-numeric: tabular-nums; }
|
|
579
|
+
.positive { color: var(--low); }
|
|
580
|
+
.muted-value { color: var(--muted); }
|
|
291
581
|
.findings {
|
|
292
582
|
display: grid;
|
|
293
583
|
gap: 12px;
|
|
@@ -396,26 +686,36 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
396
686
|
}
|
|
397
687
|
@media (max-width: 860px) {
|
|
398
688
|
main { width: min(100% - 24px, 1120px); padding-top: 32px; }
|
|
689
|
+
header { grid-template-columns: 1fr; }
|
|
690
|
+
.description { max-width: none; text-align: left; }
|
|
399
691
|
.metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
692
|
+
.report-nav { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
400
693
|
.section-grid { grid-template-columns: 1fr; gap: 18px; }
|
|
694
|
+
.checks { position: static; }
|
|
401
695
|
.section { padding: 18px; }
|
|
402
696
|
.section-head { display: block; }
|
|
403
697
|
.section-head > span { display: block; margin-top: 8px; }
|
|
698
|
+
.object-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
404
699
|
}
|
|
405
700
|
@media (max-width: 520px) {
|
|
406
701
|
.metrics { grid-template-columns: 1fr; }
|
|
702
|
+
.report-nav { grid-template-columns: 1fr; }
|
|
407
703
|
.lede { font-size: 16px; }
|
|
408
704
|
.finding-head { display: block; }
|
|
409
705
|
.pill { margin-top: 10px; }
|
|
706
|
+
.object-stats { grid-template-columns: 1fr; }
|
|
410
707
|
}
|
|
411
708
|
</style>
|
|
412
709
|
</head>
|
|
413
710
|
<body>
|
|
414
711
|
<main>
|
|
415
712
|
<header>
|
|
416
|
-
<
|
|
417
|
-
|
|
418
|
-
|
|
713
|
+
<div>
|
|
714
|
+
<p class="eyebrow">Generated ${catalogReportHtmlEscape(report.generatedAt)}</p>
|
|
715
|
+
<h1>${catalogReportHtmlEscape(report.title)}</h1>
|
|
716
|
+
<p class="lede">${catalogReportHtmlEscape(report.lede)}</p>
|
|
717
|
+
</div>
|
|
718
|
+
<p class="description">${catalogReportHtmlEscape(report.description)}</p>
|
|
419
719
|
</header>
|
|
420
720
|
<section class="metrics" aria-label="Audit summary">
|
|
421
721
|
${report.metrics
|
|
@@ -427,6 +727,7 @@ export function renderCatalogReportPage(report: CatalogReport): string {
|
|
|
427
727
|
)
|
|
428
728
|
.join("")}
|
|
429
729
|
</section>
|
|
730
|
+
<nav class="report-nav" aria-label="Report sections">${nav}</nav>
|
|
430
731
|
${sections}
|
|
431
732
|
<details>
|
|
432
733
|
<summary>Compressed JSON appendix</summary>
|