@arnilo/prism-tools 0.0.0-stage → 0.13.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/CHANGELOG.md +410 -0
- package/LICENSE +21 -0
- package/README.md +44 -2
- package/browser/README.md +98 -0
- package/browser/skills/browser-automation/SKILL.md +14 -0
- package/browser/src/actions/cdp.ts +131 -0
- package/browser/src/actions/dialog.ts +25 -0
- package/browser/src/actions/download_release.ts +22 -0
- package/browser/src/actions/index.ts +37 -0
- package/browser/src/actions/locator.ts +70 -0
- package/browser/src/actions/navigate.ts +23 -0
- package/browser/src/actions/screenshot.ts +27 -0
- package/browser/src/actions/scroll.ts +24 -0
- package/browser/src/actions/select_page.ts +21 -0
- package/browser/src/actions/types.ts +114 -0
- package/browser/src/actions/upload.ts +44 -0
- package/browser/src/actions/wait.ts +45 -0
- package/browser/src/browser.ts +197 -0
- package/browser/src/cdp.ts +178 -0
- package/browser/src/checkpoint.ts +189 -0
- package/browser/src/config.ts +72 -0
- package/browser/src/downloads.ts +253 -0
- package/browser/src/errors.ts +31 -0
- package/browser/src/evaluate.ts +101 -0
- package/browser/src/index.ts +129 -0
- package/browser/src/limits.ts +271 -0
- package/browser/src/manager.ts +730 -0
- package/browser/src/network.ts +263 -0
- package/browser/src/observe.ts +178 -0
- package/browser/src/policy.ts +133 -0
- package/browser/src/screenshot.ts +107 -0
- package/browser/src/shared-sandbox.ts +53 -0
- package/browser/src/snapshot.ts +99 -0
- package/browser/src/targets.ts +165 -0
- package/browser/src/tools.ts +480 -0
- package/browser/src/types.ts +316 -0
- package/browser/src/uploads.ts +174 -0
- package/coding/README.md +337 -0
- package/coding/src/agent/acp-operations.ts +146 -0
- package/coding/src/agent/artifacts.ts +38 -0
- package/coding/src/agent/ask-user-decision.ts +623 -0
- package/coding/src/agent/atomic-write.ts +23 -0
- package/coding/src/agent/bounded-file.ts +21 -0
- package/coding/src/agent/checks.ts +301 -0
- package/coding/src/agent/coding-checkpoint.ts +828 -0
- package/coding/src/agent/delete.ts +237 -0
- package/coding/src/agent/diagnostics-tool.ts +163 -0
- package/coding/src/agent/diagnostics.ts +262 -0
- package/coding/src/agent/edit-diff.ts +382 -0
- package/coding/src/agent/edit.ts +322 -0
- package/coding/src/agent/effects.ts +102 -0
- package/coding/src/agent/env.ts +28 -0
- package/coding/src/agent/execution-policy.ts +89 -0
- package/coding/src/agent/file-mutation-queue.ts +72 -0
- package/coding/src/agent/forge/github.ts +603 -0
- package/coding/src/agent/forge/index.ts +16 -0
- package/coding/src/agent/forge/types.ts +185 -0
- package/coding/src/agent/git-aware-repository.ts +305 -0
- package/coding/src/agent/git-exec.ts +326 -0
- package/coding/src/agent/git-status.ts +177 -0
- package/coding/src/agent/git-tools.ts +360 -0
- package/coding/src/agent/git.ts +1074 -0
- package/coding/src/agent/glob-match.ts +146 -0
- package/coding/src/agent/glob.ts +182 -0
- package/coding/src/agent/goal-verify.ts +385 -0
- package/coding/src/agent/grep-worker.ts +74 -0
- package/coding/src/agent/grep.ts +459 -0
- package/coding/src/agent/identity.ts +48 -0
- package/coding/src/agent/index.ts +676 -0
- package/coding/src/agent/language/client.ts +429 -0
- package/coding/src/agent/language/framing.ts +190 -0
- package/coding/src/agent/language/index.ts +23 -0
- package/coding/src/agent/language/intelligence.ts +682 -0
- package/coding/src/agent/language/types.ts +187 -0
- package/coding/src/agent/lifecycle-events.ts +121 -0
- package/coding/src/agent/lifecycle.ts +166 -0
- package/coding/src/agent/limits.ts +214 -0
- package/coding/src/agent/move.ts +178 -0
- package/coding/src/agent/mutation-path.ts +52 -0
- package/coding/src/agent/output-accumulator.ts +304 -0
- package/coding/src/agent/path-utils.ts +120 -0
- package/coding/src/agent/process/index.ts +58 -0
- package/coding/src/agent/process/recovery.ts +508 -0
- package/coding/src/agent/process/sessions-host.ts +222 -0
- package/coding/src/agent/process/sessions-monitor.ts +141 -0
- package/coding/src/agent/process/sessions-recovery.ts +241 -0
- package/coding/src/agent/process/sessions-spawn.ts +613 -0
- package/coding/src/agent/process/sessions-teardown.ts +281 -0
- package/coding/src/agent/process/sessions.ts +30 -0
- package/coding/src/agent/process/types.ts +358 -0
- package/coding/src/agent/read-path-set.ts +126 -0
- package/coding/src/agent/read.ts +713 -0
- package/coding/src/agent/repository/glob.ts +147 -0
- package/coding/src/agent/repository/indexed-search.ts +480 -0
- package/coding/src/agent/repository/list.ts +131 -0
- package/coding/src/agent/repository/operations.ts +22 -0
- package/coding/src/agent/repository/path.ts +98 -0
- package/coding/src/agent/repository/search.ts +277 -0
- package/coding/src/agent/repository/types.ts +243 -0
- package/coding/src/agent/repository/walk.ts +126 -0
- package/coding/src/agent/repository.ts +12 -0
- package/coding/src/agent/shell-background.ts +264 -0
- package/coding/src/agent/shell-output.ts +75 -0
- package/coding/src/agent/shell.ts +526 -0
- package/coding/src/agent/todo.ts +399 -0
- package/coding/src/agent/truncate.ts +251 -0
- package/coding/src/agent/write.ts +170 -0
- package/coding/src/ask-user-host.ts +48 -0
- package/coding/src/config.ts +278 -0
- package/coding/src/hard-deny.ts +97 -0
- package/coding/src/index.ts +318 -0
- package/coding/src/principal-guard.ts +84 -0
- package/coding/src/sandbox.ts +92 -0
- package/coding/src/security/approval.ts +144 -0
- package/coding/src/security/command-rules.ts +57 -0
- package/coding/src/security/docker-cli.ts +222 -0
- package/coding/src/security/docker-sandbox.ts +1205 -0
- package/coding/src/security/e2b-sandbox.ts +853 -0
- package/coding/src/security/egress/dns-pin.ts +88 -0
- package/coding/src/security/egress/index.ts +32 -0
- package/coding/src/security/egress/limits.ts +57 -0
- package/coding/src/security/egress/policy.ts +137 -0
- package/coding/src/security/egress/proxy.ts +599 -0
- package/coding/src/security/egress/types.ts +30 -0
- package/coding/src/security/index.ts +199 -0
- package/coding/src/security/native-sandbox.ts +639 -0
- package/coding/src/security/path-containment.ts +55 -0
- package/coding/src/security/sandbox-coding-operations.ts +392 -0
- package/coding/src/security/sandbox-fs-operations.ts +644 -0
- package/coding/src/security/sandbox-limits.ts +147 -0
- package/coding/src/security/sandbox-tar.ts +308 -0
- package/coding/src/security/sandbox.ts +179 -0
- package/coding/src/security/semaphore.ts +55 -0
- package/coding/src/targets.ts +110 -0
- package/coding/src/workspace/diff.ts +78 -0
- package/coding/src/workspace/errors.ts +28 -0
- package/coding/src/workspace/index.ts +5 -0
- package/coding/src/workspace/merge.ts +151 -0
- package/coding/src/workspace/routing.ts +76 -0
- package/coding/src/workspace/service.ts +545 -0
- package/computer-use/README.md +105 -0
- package/computer-use/skills/computer-use-linux/SKILL.md +17 -0
- package/computer-use/src/classify.ts +53 -0
- package/computer-use/src/command.ts +29 -0
- package/computer-use/src/config.ts +69 -0
- package/computer-use/src/create.ts +170 -0
- package/computer-use/src/devices.ts +247 -0
- package/computer-use/src/env.ts +28 -0
- package/computer-use/src/index.ts +157 -0
- package/computer-use/src/skill.ts +21 -0
- package/openapi/README.md +97 -0
- package/openapi/src/compile.ts +386 -0
- package/openapi/src/config.ts +73 -0
- package/openapi/src/errors.ts +16 -0
- package/openapi/src/execute.ts +241 -0
- package/openapi/src/index.ts +124 -0
- package/openapi/src/limits.ts +63 -0
- package/openapi/src/specs.ts +86 -0
- package/openapi/src/tools.ts +60 -0
- package/openapi/src/types.ts +61 -0
- package/package.json +171 -4
- package/research/README.md +72 -0
- package/research/skills/research-report/SKILL.md +12 -0
- package/research/skills/research-scoping/SKILL.md +10 -0
- package/research/skills/research-synthesis/SKILL.md +10 -0
- package/research/skills/source-triage/SKILL.md +10 -0
- package/research/src/check.ts +71 -0
- package/research/src/config.ts +41 -0
- package/research/src/index.ts +147 -0
- package/research/src/ledger.ts +157 -0
- package/research/src/urls.ts +21 -0
- package/web/README.md +287 -0
- package/web/skills/web-research/SKILL.md +17 -0
- package/web/src/brave.ts +66 -0
- package/web/src/cache.ts +151 -0
- package/web/src/config.ts +210 -0
- package/web/src/controls.ts +128 -0
- package/web/src/domains.ts +114 -0
- package/web/src/exa.ts +60 -0
- package/web/src/firecrawl.ts +160 -0
- package/web/src/index.ts +387 -0
- package/web/src/kagi.ts +73 -0
- package/web/src/limits.ts +55 -0
- package/web/src/native/convert.ts +84 -0
- package/web/src/native/fetch.ts +226 -0
- package/web/src/normalize.ts +131 -0
- package/web/src/obscura/cdp.ts +201 -0
- package/web/src/obscura/classify.ts +35 -0
- package/web/src/obscura/cli.ts +254 -0
- package/web/src/obscura/errors.ts +8 -0
- package/web/src/obscura/index.ts +59 -0
- package/web/src/obscura/limits.ts +92 -0
- package/web/src/obscura/mcp.ts +104 -0
- package/web/src/obscura/playwright.ts +133 -0
- package/web/src/obscura/process.ts +235 -0
- package/web/src/obscura/search-profile.ts +73 -0
- package/web/src/obscura/types.ts +48 -0
- package/web/src/obscura/web.ts +419 -0
- package/web/src/prompt-fetch.ts +88 -0
- package/web/src/render.ts +75 -0
- package/web/src/searxng.ts +78 -0
- package/web/src/slice.ts +139 -0
- package/web/src/tavily.ts +77 -0
- package/web/src/tools.ts +182 -0
- package/web/src/transport.ts +310 -0
- package/web/src/types.ts +199 -0
- package/work/README.md +77 -0
- package/work/THIRD_PARTY_NOTICES +24 -0
- package/work/docling/ocr.py +150 -0
- package/work/sandbox/Dockerfile +36 -0
- package/work/sandbox/soffice.sh +12 -0
- package/work/src/command.ts +29 -0
- package/work/src/config.ts +138 -0
- package/work/src/connectors/cli.ts +343 -0
- package/work/src/connectors/drafts.ts +647 -0
- package/work/src/connectors/errors.ts +8 -0
- package/work/src/connectors/file-bytes.ts +149 -0
- package/work/src/connectors/google-workspace-http.ts +399 -0
- package/work/src/connectors/google-workspace.ts +465 -0
- package/work/src/connectors/gws-tools.ts +378 -0
- package/work/src/connectors/http.ts +123 -0
- package/work/src/connectors/idempotency.ts +235 -0
- package/work/src/connectors/identity.ts +34 -0
- package/work/src/connectors/index.ts +93 -0
- package/work/src/connectors/limits.ts +50 -0
- package/work/src/connectors/m365-tools.ts +308 -0
- package/work/src/connectors/microsoft365-http.ts +428 -0
- package/work/src/connectors/microsoft365.ts +402 -0
- package/work/src/connectors/normalize.ts +120 -0
- package/work/src/connectors/tool-helpers.ts +398 -0
- package/work/src/connectors/tools.ts +42 -0
- package/work/src/connectors/types.ts +399 -0
- package/work/src/diagrams/canonicalize.ts +166 -0
- package/work/src/diagrams/caps.ts +52 -0
- package/work/src/diagrams/embed.ts +340 -0
- package/work/src/diagrams/errors.ts +76 -0
- package/work/src/diagrams/index.ts +81 -0
- package/work/src/diagrams/messages.ts +150 -0
- package/work/src/diagrams/origin.ts +52 -0
- package/work/src/diagrams/telemetry.ts +34 -0
- package/work/src/diagrams/xml.ts +267 -0
- package/work/src/document-extraction/index.ts +563 -0
- package/work/src/document-reader/ambient.d.ts +2 -0
- package/work/src/document-reader/errors.ts +7 -0
- package/work/src/document-reader/index.ts +181 -0
- package/work/src/document-reader/mistral-ocr.ts +318 -0
- package/work/src/document-reader/parsers.ts +171 -0
- package/work/src/documents/caps.ts +135 -0
- package/work/src/documents/diff.ts +269 -0
- package/work/src/documents/errors.ts +44 -0
- package/work/src/documents/fidelity.ts +98 -0
- package/work/src/documents/generate.ts +96 -0
- package/work/src/documents/hash.ts +8 -0
- package/work/src/documents/index.ts +14 -0
- package/work/src/documents/model-schema.ts +639 -0
- package/work/src/documents/parse.ts +252 -0
- package/work/src/documents/patch-history.ts +81 -0
- package/work/src/documents/patch.ts +476 -0
- package/work/src/documents/preview-html.ts +245 -0
- package/work/src/documents/preview.ts +227 -0
- package/work/src/documents/telemetry.ts +36 -0
- package/work/src/documents/translate/docx.ts +292 -0
- package/work/src/documents/translate/pptx.ts +304 -0
- package/work/src/documents/translate/xlsx.ts +137 -0
- package/work/src/documents/types.ts +188 -0
- package/work/src/index.ts +311 -0
- package/work/src/runtime/worker-pool-entry.ts +102 -0
- package/work/src/runtime/worker-pool.ts +267 -0
- package/work/src/sandbox/composition.ts +212 -0
- package/work/src/sandbox/image.ts +6 -0
- package/work/src/sandbox/index.ts +14 -0
- package/work/src/sheets/caps.ts +68 -0
- package/work/src/sheets/csv.ts +351 -0
- package/work/src/sheets/decimal.ts +388 -0
- package/work/src/sheets/errors.ts +52 -0
- package/work/src/sheets/index.ts +62 -0
- package/work/src/sheets/inference.ts +378 -0
- package/work/src/sheets/telemetry.ts +34 -0
- package/work/src/sheets/types.ts +162 -0
- package/work/src/sheets/xlsx.ts +388 -0
- package/work/src/skills/index.ts +8 -0
- package/work/src/skills/load.ts +43 -0
- package/work/src/tools/filesystem.ts +21 -0
- package/work/src/tools/index.ts +1 -0
- package/work/src/tools/local-filesystem.ts +33 -0
- package/work/src/tools/office.ts +311 -0
- package/work/vendor/hermes-agent/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_comments.py +289 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_common.py +94 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_create.py +177 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_edit.py +250 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_read.py +149 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_revisions.py +147 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_template.py +70 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_validate.py +156 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/SKILL.md +125 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/_raster.py +76 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_marker.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_pymupdf.py +101 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_create.py +130 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_fill_form.py +97 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_form_layout.py +168 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_make_form.py +145 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_merge.py +50 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_meta.py +115 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_page_image.py +99 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_read.py +153 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_secure.py +71 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_split.py +84 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_stamp.py +143 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_watermark.py +51 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/SKILL.md +220 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_create.py +214 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_edit.py +436 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_from_template.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_read.py +131 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_render.py +93 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/csv_to_xlsx.py +104 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_create.py +259 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_edit.py +263 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_read.py +160 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_recalc.py +111 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_restructure.py +337 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_to_csv.py +69 -0
- package/work/vendor-lock.json +11 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { JsonObject, OwnershipScope, ToolDefinition, ToolExecutionContext } from "@arnilo/prism";
|
|
2
|
+
import { ArtifactBodyRef, ArtifactBodyStore } from "@arnilo/prism/artifacts";
|
|
3
|
+
import type { WorkSandboxFilesystem } from "../tools/filesystem.js";
|
|
4
|
+
import { AgentIdentity } from "./identity.js";
|
|
5
|
+
export type WorkProvider = "microsoft365" | "google-workspace";
|
|
6
|
+
|
|
7
|
+
export type Microsoft365Capability = "mail" | "calendar" | "files" | "tasks" | "todo" | "planner" | "teams";
|
|
8
|
+
|
|
9
|
+
export type Microsoft365Op =
|
|
10
|
+
| "version"
|
|
11
|
+
| "mail.list"
|
|
12
|
+
| "mail.get"
|
|
13
|
+
| "mail.send"
|
|
14
|
+
| "calendar.list"
|
|
15
|
+
| "calendar.add"
|
|
16
|
+
| "file.list"
|
|
17
|
+
| "file.get"
|
|
18
|
+
| "file.add"
|
|
19
|
+
| "file.copy"
|
|
20
|
+
| "file.share"
|
|
21
|
+
| "todo.list"
|
|
22
|
+
| "todo.add"
|
|
23
|
+
| "todo.complete"
|
|
24
|
+
| "planner.list"
|
|
25
|
+
| "planner.add"
|
|
26
|
+
| "planner.complete";
|
|
27
|
+
|
|
28
|
+
export type GoogleWorkspaceCapability = "mail" | "calendar" | "files" | "tasks" | "docs" | "sheets" | "slides";
|
|
29
|
+
|
|
30
|
+
export type GoogleWorkspaceOp =
|
|
31
|
+
| "version"
|
|
32
|
+
| "mail.list"
|
|
33
|
+
| "mail.get"
|
|
34
|
+
| "mail.send"
|
|
35
|
+
| "calendar.list"
|
|
36
|
+
| "calendar.add"
|
|
37
|
+
| "file.list"
|
|
38
|
+
| "file.get"
|
|
39
|
+
| "file.add"
|
|
40
|
+
| "file.share"
|
|
41
|
+
| "task.list"
|
|
42
|
+
| "task.add"
|
|
43
|
+
| "task.complete"
|
|
44
|
+
| "docs.create"
|
|
45
|
+
| "docs.update"
|
|
46
|
+
| "sheets.create"
|
|
47
|
+
| "sheets.update"
|
|
48
|
+
| "slides.create"
|
|
49
|
+
| "slides.update";
|
|
50
|
+
|
|
51
|
+
export interface WorkLimits {
|
|
52
|
+
readonly maxPaginationPages?: number;
|
|
53
|
+
readonly maxItemsPerPage?: number;
|
|
54
|
+
readonly maxAggregateItems?: number;
|
|
55
|
+
readonly maxRequestBytes?: number;
|
|
56
|
+
readonly maxResponseBytes?: number;
|
|
57
|
+
readonly maxAttachmentBytes?: number;
|
|
58
|
+
readonly maxFileBytes?: number;
|
|
59
|
+
readonly maxStdoutBytes?: number;
|
|
60
|
+
readonly maxStderrBytes?: number;
|
|
61
|
+
readonly timeoutMs?: number;
|
|
62
|
+
readonly maxConcurrency?: number;
|
|
63
|
+
readonly maxRetries?: number;
|
|
64
|
+
readonly maxIdempotencyKeyBytes?: number;
|
|
65
|
+
readonly maxJsonDepth?: number;
|
|
66
|
+
readonly maxJsonProperties?: number;
|
|
67
|
+
}
|
|
68
|
+
export type ResolvedWorkLimits = Required<WorkLimits>;
|
|
69
|
+
|
|
70
|
+
export interface WorkCitation {
|
|
71
|
+
readonly citationId: string;
|
|
72
|
+
readonly provider: WorkProvider;
|
|
73
|
+
readonly resourceId?: string;
|
|
74
|
+
readonly url?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface WorkMailMessage extends WorkCitation {
|
|
78
|
+
readonly subject?: string;
|
|
79
|
+
readonly preview?: string;
|
|
80
|
+
readonly from?: string;
|
|
81
|
+
readonly to?: readonly string[];
|
|
82
|
+
readonly receivedAt?: string;
|
|
83
|
+
readonly isDraft?: boolean;
|
|
84
|
+
readonly changeKey?: string;
|
|
85
|
+
readonly untrusted: true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface WorkCalendarEvent extends WorkCitation {
|
|
89
|
+
readonly subject?: string;
|
|
90
|
+
readonly start?: string;
|
|
91
|
+
readonly end?: string;
|
|
92
|
+
readonly changeKey?: string;
|
|
93
|
+
readonly untrusted: true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface WorkFileItem extends WorkCitation {
|
|
97
|
+
readonly name?: string;
|
|
98
|
+
readonly size?: number;
|
|
99
|
+
readonly eTag?: string;
|
|
100
|
+
readonly mimeType?: string;
|
|
101
|
+
readonly untrusted: true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface WorkTaskItem extends WorkCitation {
|
|
105
|
+
readonly title?: string;
|
|
106
|
+
readonly status?: string;
|
|
107
|
+
readonly changeKey?: string;
|
|
108
|
+
readonly untrusted: true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface WorkPage<T> {
|
|
112
|
+
readonly items: readonly T[];
|
|
113
|
+
readonly nextCursor?: string;
|
|
114
|
+
readonly truncated?: boolean;
|
|
115
|
+
readonly untrusted: true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface WorkDraftApproval {
|
|
119
|
+
readonly draftId: string;
|
|
120
|
+
readonly revision: number;
|
|
121
|
+
readonly payloadDigest: string;
|
|
122
|
+
readonly policyRevision?: string;
|
|
123
|
+
readonly identityKey?: string;
|
|
124
|
+
readonly approver?: string;
|
|
125
|
+
readonly approvedAt?: string;
|
|
126
|
+
readonly expiresAt?: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface WorkDraft {
|
|
130
|
+
readonly draftId: string;
|
|
131
|
+
readonly provider: WorkProvider;
|
|
132
|
+
readonly op: string;
|
|
133
|
+
readonly identityKey: string;
|
|
134
|
+
readonly payload: JsonObject;
|
|
135
|
+
readonly revision: number;
|
|
136
|
+
readonly payloadDigest: string;
|
|
137
|
+
readonly recipients?: readonly string[];
|
|
138
|
+
readonly createdAt: string;
|
|
139
|
+
readonly updatedAt: string;
|
|
140
|
+
readonly status: "pending" | "pending_approval" | "approved" | "executed" | "rejected" | "unknown";
|
|
141
|
+
readonly approval?: WorkDraftApproval;
|
|
142
|
+
readonly concurrencyToken?: string;
|
|
143
|
+
readonly executedAt?: string;
|
|
144
|
+
readonly bodyRef?: ArtifactBodyRef;
|
|
145
|
+
readonly policyRevision?: string;
|
|
146
|
+
readonly mode?: "ephemeral" | "durable";
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface WorkDraftCreateInput {
|
|
150
|
+
readonly draftId?: string;
|
|
151
|
+
readonly provider: WorkProvider;
|
|
152
|
+
readonly op: string;
|
|
153
|
+
readonly identity: AgentIdentity;
|
|
154
|
+
readonly payload: JsonObject;
|
|
155
|
+
readonly policyRevision?: string;
|
|
156
|
+
readonly signal?: AbortSignal;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface WorkDraftRefInput {
|
|
160
|
+
readonly draftId: string;
|
|
161
|
+
readonly identity: AgentIdentity;
|
|
162
|
+
readonly provider?: WorkProvider;
|
|
163
|
+
readonly signal?: AbortSignal;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface WorkDraftUpdateInput {
|
|
167
|
+
readonly draftId: string;
|
|
168
|
+
readonly identity: AgentIdentity;
|
|
169
|
+
readonly payload: JsonObject;
|
|
170
|
+
readonly expectedRevision?: number;
|
|
171
|
+
readonly expectedConcurrencyToken?: string;
|
|
172
|
+
readonly policyRevision?: string;
|
|
173
|
+
readonly signal?: AbortSignal;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface WorkDraftApproveInput {
|
|
177
|
+
readonly draftId: string;
|
|
178
|
+
readonly identity: AgentIdentity;
|
|
179
|
+
readonly approval: WorkDraftApproval;
|
|
180
|
+
readonly expectedConcurrencyToken?: string;
|
|
181
|
+
readonly signal?: AbortSignal;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface WorkDraftMarkInput {
|
|
185
|
+
readonly draftId: string;
|
|
186
|
+
readonly identity: AgentIdentity;
|
|
187
|
+
readonly status: WorkDraft["status"];
|
|
188
|
+
readonly concurrencyToken?: string;
|
|
189
|
+
readonly expectedConcurrencyToken?: string;
|
|
190
|
+
readonly signal?: AbortSignal;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface WorkDraftStore {
|
|
194
|
+
readonly mode: "ephemeral" | "durable";
|
|
195
|
+
createDraft(input: WorkDraftCreateInput): Promise<WorkDraft> | WorkDraft;
|
|
196
|
+
getDraft(input: WorkDraftRefInput): Promise<WorkDraft | undefined> | WorkDraft | undefined;
|
|
197
|
+
updateDraft(input: WorkDraftUpdateInput): Promise<WorkDraft> | WorkDraft;
|
|
198
|
+
approveDraft(input: WorkDraftApproveInput): Promise<WorkDraft> | WorkDraft;
|
|
199
|
+
markDraft(input: WorkDraftMarkInput): Promise<WorkDraft> | WorkDraft;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface SyncWorkDraftStore extends WorkDraftStore {
|
|
203
|
+
createDraft(input: WorkDraftCreateInput): WorkDraft;
|
|
204
|
+
getDraft(input: WorkDraftRefInput): WorkDraft | undefined;
|
|
205
|
+
updateDraft(input: WorkDraftUpdateInput): WorkDraft;
|
|
206
|
+
approveDraft(input: WorkDraftApproveInput): WorkDraft;
|
|
207
|
+
markDraft(input: WorkDraftMarkInput): WorkDraft;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Bounded summary retained after an approved external mutation. */
|
|
211
|
+
export interface WorkMutationResult {
|
|
212
|
+
readonly draftId: string;
|
|
213
|
+
readonly resourceId?: string;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface WorkMutationFailure {
|
|
217
|
+
readonly code: string;
|
|
218
|
+
readonly reference?: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export type WorkMutationStatus = "in_progress" | "completed" | "failed_retryable" | "failed_terminal" | "unknown";
|
|
222
|
+
|
|
223
|
+
export interface WorkMutationKey {
|
|
224
|
+
/** Active host-verified identity; its exact owner/principal scopes this mutation. */
|
|
225
|
+
readonly identity: AgentIdentity;
|
|
226
|
+
readonly key: string;
|
|
227
|
+
readonly op: string;
|
|
228
|
+
readonly signal?: AbortSignal;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface WorkMutationBeginInput extends WorkMutationKey {
|
|
232
|
+
readonly claimTtlMs?: number;
|
|
233
|
+
readonly maxAttempts?: number;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export interface WorkMutationTransitionInput extends WorkMutationKey {
|
|
237
|
+
readonly claimToken: string;
|
|
238
|
+
readonly expectedVersion: number;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface WorkMutationRecord extends OwnershipScope {
|
|
242
|
+
readonly principalId: string;
|
|
243
|
+
readonly key: string;
|
|
244
|
+
readonly op: string;
|
|
245
|
+
readonly status: WorkMutationStatus;
|
|
246
|
+
readonly attempt: number;
|
|
247
|
+
readonly version: number;
|
|
248
|
+
readonly claimToken?: string;
|
|
249
|
+
readonly result?: WorkMutationResult;
|
|
250
|
+
readonly failure?: WorkMutationFailure;
|
|
251
|
+
readonly createdAt: string;
|
|
252
|
+
readonly updatedAt: string;
|
|
253
|
+
readonly expiresAt?: string;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export interface IdempotencyStore {
|
|
257
|
+
/**
|
|
258
|
+
* Reconciliation read. Normal execution starts with {@link begin}.
|
|
259
|
+
*
|
|
260
|
+
* Adapters reject with the portable codes `ERR_PRISM_WORK_IDEMPOTENCY` (rejected
|
|
261
|
+
* claim/transition) and `ERR_PRISM_WORK_IDEMPOTENCY_CONFLICT` (lost race or stale
|
|
262
|
+
* claim token). The error class is adapter-specific — a memory store throws
|
|
263
|
+
* `WorkToolError`, the PostgreSQL adapter throws `EnterprisePostgresError` — so
|
|
264
|
+
* hosts match on {@link WorkToolError.code}, never on `instanceof`.
|
|
265
|
+
*/
|
|
266
|
+
get(input: WorkMutationKey): Promise<WorkMutationRecord | undefined>;
|
|
267
|
+
begin(input: WorkMutationBeginInput): Promise<{ readonly outcome: "acquired" | "existing"; readonly record: WorkMutationRecord }>;
|
|
268
|
+
complete(input: WorkMutationTransitionInput & { readonly result: WorkMutationResult }): Promise<WorkMutationRecord>;
|
|
269
|
+
fail(
|
|
270
|
+
input: WorkMutationTransitionInput & { readonly status: "failed_retryable" | "failed_terminal"; readonly failure: WorkMutationFailure },
|
|
271
|
+
): Promise<WorkMutationRecord>;
|
|
272
|
+
markUnknown(input: WorkMutationTransitionInput & { readonly failure?: WorkMutationFailure }): Promise<WorkMutationRecord>;
|
|
273
|
+
resolveUnknown(
|
|
274
|
+
input: WorkMutationKey & {
|
|
275
|
+
readonly expectedVersion: number;
|
|
276
|
+
readonly status: "failed_retryable" | "failed_terminal";
|
|
277
|
+
readonly failure?: WorkMutationFailure;
|
|
278
|
+
},
|
|
279
|
+
): Promise<WorkMutationRecord>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface WorkApprovalCheckInput {
|
|
283
|
+
readonly draftId: string;
|
|
284
|
+
readonly op: string;
|
|
285
|
+
readonly identity: AgentIdentity;
|
|
286
|
+
readonly revision?: number;
|
|
287
|
+
readonly payloadDigest?: string;
|
|
288
|
+
readonly recipients?: readonly string[];
|
|
289
|
+
readonly policyRevision?: string;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export interface WorkApprovalGate {
|
|
293
|
+
/** Return true or an approval decision when host already authorized this draft/mutation. */
|
|
294
|
+
isApproved(
|
|
295
|
+
input: WorkApprovalCheckInput | { draftId: string; op: string; identity: AgentIdentity },
|
|
296
|
+
): Promise<boolean | WorkDraftApproval> | boolean | WorkDraftApproval;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export interface ExternalRecipientPolicy {
|
|
300
|
+
/** Return true when address/domain may receive external share/mail. Default deny. */
|
|
301
|
+
allow(address: string): boolean;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export interface WorkCliExecResult {
|
|
305
|
+
readonly exitCode: number | null;
|
|
306
|
+
readonly stdout: string;
|
|
307
|
+
readonly stderr: string;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export interface WorkCliRunner {
|
|
311
|
+
exec(argv: readonly string[], options?: { signal?: AbortSignal; env?: Readonly<Record<string, string>> }): Promise<WorkCliExecResult>;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Late-bound, per-identity connector token source. Returns env vars to inject (e.g.
|
|
316
|
+
* `{ M365_ACCESSTOKEN: "…" }`) or undefined when the credential is missing/expired/revoked,
|
|
317
|
+
* which fails the call closed. Tokens never appear in argv or model context.
|
|
318
|
+
*/
|
|
319
|
+
export interface WorkTokenProvider {
|
|
320
|
+
tokenEnv(
|
|
321
|
+
identity: AgentIdentity,
|
|
322
|
+
signal?: AbortSignal,
|
|
323
|
+
): Promise<Readonly<Record<string, string>> | undefined> | Readonly<Record<string, string>> | undefined;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface Microsoft365Adapter {
|
|
327
|
+
readonly provider: "microsoft365";
|
|
328
|
+
readonly identity: AgentIdentity;
|
|
329
|
+
readonly allowedOps: ReadonlySet<Microsoft365Op>;
|
|
330
|
+
readonly draftStore?: WorkDraftStore;
|
|
331
|
+
ensureReady(signal?: AbortSignal): Promise<string>;
|
|
332
|
+
runOp(op: Microsoft365Op, args: JsonObject, signal?: AbortSignal): Promise<unknown>;
|
|
333
|
+
createDraft(
|
|
334
|
+
op: Microsoft365Op,
|
|
335
|
+
payload: JsonObject,
|
|
336
|
+
options?: { draftId?: string; policyRevision?: string },
|
|
337
|
+
): Promise<WorkDraft> | WorkDraft;
|
|
338
|
+
getDraft(draftId: string): Promise<WorkDraft | undefined> | WorkDraft | undefined;
|
|
339
|
+
updateDraft?(
|
|
340
|
+
draftId: string,
|
|
341
|
+
payload: JsonObject,
|
|
342
|
+
options?: { expectedRevision?: number; concurrencyToken?: string },
|
|
343
|
+
): Promise<WorkDraft> | WorkDraft;
|
|
344
|
+
approveDraft?(approval: WorkDraftApproval): Promise<WorkDraft> | WorkDraft;
|
|
345
|
+
markDraft(draftId: string, status: WorkDraft["status"], concurrencyToken?: string): Promise<WorkDraft> | WorkDraft;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export interface GoogleWorkspaceAdapter {
|
|
349
|
+
readonly provider: "google-workspace";
|
|
350
|
+
readonly identity: AgentIdentity;
|
|
351
|
+
readonly allowedOps: ReadonlySet<GoogleWorkspaceOp>;
|
|
352
|
+
readonly draftStore?: WorkDraftStore;
|
|
353
|
+
ensureReady(signal?: AbortSignal): Promise<string>;
|
|
354
|
+
runOp(op: GoogleWorkspaceOp, args: JsonObject, signal?: AbortSignal): Promise<unknown>;
|
|
355
|
+
createDraft(
|
|
356
|
+
op: GoogleWorkspaceOp,
|
|
357
|
+
payload: JsonObject,
|
|
358
|
+
options?: { draftId?: string; policyRevision?: string },
|
|
359
|
+
): Promise<WorkDraft> | WorkDraft;
|
|
360
|
+
getDraft(draftId: string): Promise<WorkDraft | undefined> | WorkDraft | undefined;
|
|
361
|
+
updateDraft?(
|
|
362
|
+
draftId: string,
|
|
363
|
+
payload: JsonObject,
|
|
364
|
+
options?: { expectedRevision?: number; concurrencyToken?: string },
|
|
365
|
+
): Promise<WorkDraft> | WorkDraft;
|
|
366
|
+
approveDraft?(approval: WorkDraftApproval): Promise<WorkDraft> | WorkDraft;
|
|
367
|
+
markDraft(draftId: string, status: WorkDraft["status"], concurrencyToken?: string): Promise<WorkDraft> | WorkDraft;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export interface WorkFileArtifactStore {
|
|
371
|
+
readonly bodies: ArtifactBodyStore;
|
|
372
|
+
/** Host owns artifact naming and ownership; work tools supply verified download metadata. */
|
|
373
|
+
createRef(input: {
|
|
374
|
+
readonly context: ToolExecutionContext;
|
|
375
|
+
readonly provider: WorkProvider;
|
|
376
|
+
readonly itemId?: string;
|
|
377
|
+
readonly mime: string;
|
|
378
|
+
readonly byteLength: number;
|
|
379
|
+
readonly contentHash: string;
|
|
380
|
+
}): ArtifactBodyRef;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export interface WorkToolsOptions {
|
|
384
|
+
readonly microsoft365?: Microsoft365Adapter;
|
|
385
|
+
/** File-byte limit for tool-side artifact and filesystem transfers; match adapter limits when overridden. */
|
|
386
|
+
readonly limits?: WorkLimits;
|
|
387
|
+
readonly googleWorkspace?: GoogleWorkspaceAdapter;
|
|
388
|
+
readonly approval?: WorkApprovalGate;
|
|
389
|
+
readonly idempotencyStore?: IdempotencyStore;
|
|
390
|
+
readonly externalRecipients?: ExternalRecipientPolicy;
|
|
391
|
+
/** Optional contained filesystem used for downloaded files and sandbox-source uploads. */
|
|
392
|
+
readonly filesystem?: WorkSandboxFilesystem;
|
|
393
|
+
/** Optional host-owned artifact body store for downloaded files and sandbox-source uploads. */
|
|
394
|
+
readonly artifacts?: WorkFileArtifactStore;
|
|
395
|
+
/** Optional attachment/file scan before inbound use. Required for inbound attachment bytes. */
|
|
396
|
+
readonly scanAttachment?: (input: { bytes: number; name?: string }) => Promise<void> | void;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export type WorkToolSet = readonly ToolDefinition[];
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { XMLParser } from "fast-xml-parser";
|
|
2
|
+
import { type DrawioXmlCaps, type ResolvedDrawioXmlCaps, resolveDiagramsCaps, validateByteCap } from "./caps.js";
|
|
3
|
+
import { DiagramsModelInvalidError, DiagramsXxeError } from "./errors.js";
|
|
4
|
+
import type { DiagramsTelemetry } from "./telemetry.js";
|
|
5
|
+
import { assertNotVisio, UNSAFE_XML_DECLARATION_PATTERN } from "./xml.js";
|
|
6
|
+
|
|
7
|
+
export interface DrawioCanonicalizeOptions {
|
|
8
|
+
readonly caps?: DrawioXmlCaps;
|
|
9
|
+
readonly telemetry?: DiagramsTelemetry;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Produces byte-stable, canonicalized XML formatting for draw.io diagrams.
|
|
14
|
+
*
|
|
15
|
+
* Properties:
|
|
16
|
+
* - Attributes on all elements are sorted lexicographically by name ascending.
|
|
17
|
+
* - Double quotes are consistently used for attribute values.
|
|
18
|
+
* - XML entities are escaped in attribute values and text nodes.
|
|
19
|
+
* - Insignificant inter-tag and surrounding whitespace is stripped while element sequence is preserved.
|
|
20
|
+
* - Empty elements are normalized to self-closing form (`<element attr="val"/>`).
|
|
21
|
+
* - Deterministic output suitable for SHA-256 content hashing across systems.
|
|
22
|
+
*/
|
|
23
|
+
export function canonicalizeDrawioXml(xml: string | Uint8Array, options?: DrawioCanonicalizeOptions): string {
|
|
24
|
+
const startTime = Date.now();
|
|
25
|
+
const span = options?.telemetry?.startSpan("diagrams.canonicalize");
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
assertNotVisio(xml);
|
|
29
|
+
|
|
30
|
+
const caps: ResolvedDrawioXmlCaps = resolveDiagramsCaps(options?.caps);
|
|
31
|
+
const xmlString = typeof xml === "string" ? xml : Buffer.from(xml).toString("utf8");
|
|
32
|
+
const byteLength = Buffer.byteLength(xmlString, "utf8");
|
|
33
|
+
|
|
34
|
+
validateByteCap(byteLength, caps);
|
|
35
|
+
|
|
36
|
+
if (UNSAFE_XML_DECLARATION_PATTERN.test(xmlString)) {
|
|
37
|
+
throw new DiagramsXxeError("XML contains forbidden DOCTYPE or ENTITY declaration (XXE security policy)");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const parser = new XMLParser({
|
|
41
|
+
preserveOrder: true,
|
|
42
|
+
ignoreAttributes: false,
|
|
43
|
+
attributeNamePrefix: "@_",
|
|
44
|
+
allowBooleanAttributes: false,
|
|
45
|
+
processEntities: false,
|
|
46
|
+
htmlEntities: false,
|
|
47
|
+
trimValues: false,
|
|
48
|
+
stopNodes: [],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const parsed: unknown = parser.parse(xmlString);
|
|
52
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
53
|
+
throw new DiagramsModelInvalidError("Parsed XML does not produce an ordered element tree");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Filter out top-level whitespace and XML processing instructions (?xml)
|
|
57
|
+
const rootNodes = (parsed as OrderedNode[]).filter(
|
|
58
|
+
(node) => !("#text" in node) && Object.keys(node).some((k) => !k.startsWith("?") && k !== ":@"),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (rootNodes.length === 0) {
|
|
62
|
+
throw new DiagramsModelInvalidError("No valid root XML element found");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const canonicalOutput = serializeNodes(rootNodes);
|
|
66
|
+
|
|
67
|
+
span?.setAttribute("diagrams.inputBytes", byteLength);
|
|
68
|
+
span?.setAttribute("diagrams.outputBytes", Buffer.byteLength(canonicalOutput, "utf8"));
|
|
69
|
+
span?.setAttribute("diagrams.durationMs", Date.now() - startTime);
|
|
70
|
+
|
|
71
|
+
return canonicalOutput;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
span?.recordError();
|
|
74
|
+
throw error;
|
|
75
|
+
} finally {
|
|
76
|
+
span?.end();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type OrderedNode = Record<string, unknown>;
|
|
81
|
+
|
|
82
|
+
function serializeNodes(nodes: OrderedNode[]): string {
|
|
83
|
+
let result = "";
|
|
84
|
+
for (const node of nodes) {
|
|
85
|
+
result += serializeNode(node);
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function serializeNode(node: OrderedNode): string {
|
|
91
|
+
if ("#text" in node) {
|
|
92
|
+
const rawText = String(node["#text"]);
|
|
93
|
+
return escapeXmlText(rawText);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Find the element tag name (key that is not ":@" and does not start with "?")
|
|
97
|
+
let tagName: string | undefined;
|
|
98
|
+
for (const key of Object.keys(node)) {
|
|
99
|
+
if (key !== ":@" && !key.startsWith("?")) {
|
|
100
|
+
tagName = key;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!tagName) {
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const rawAttrs = (node[":@"] ?? {}) as Record<string, unknown>;
|
|
110
|
+
const sortedAttrs = Object.keys(rawAttrs)
|
|
111
|
+
.map((k) => (k.startsWith("@_") ? k.slice(2) : k))
|
|
112
|
+
.sort()
|
|
113
|
+
.map((attrName) => {
|
|
114
|
+
const originalKey = `@_${attrName}` in rawAttrs ? `@_${attrName}` : attrName;
|
|
115
|
+
const val = rawAttrs[originalKey];
|
|
116
|
+
return `${attrName}="${escapeXmlAttr(String(val ?? ""))}"`;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const attrString = sortedAttrs.length > 0 ? ` ${sortedAttrs.join(" ")}` : "";
|
|
120
|
+
const children = node[tagName];
|
|
121
|
+
|
|
122
|
+
if (!children || (Array.isArray(children) && children.length === 0)) {
|
|
123
|
+
return `<${tagName}${attrString}/>`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (Array.isArray(children)) {
|
|
127
|
+
// If element contains child elements, filter out insignificant whitespace-only text nodes
|
|
128
|
+
const hasElementChildren = children.some((c) => !("#text" in c));
|
|
129
|
+
const effectiveChildren = hasElementChildren
|
|
130
|
+
? children.filter((c) => !("#text" in c) || String(c["#text"]).trim().length > 0)
|
|
131
|
+
: children;
|
|
132
|
+
|
|
133
|
+
if (effectiveChildren.length === 0) {
|
|
134
|
+
return `<${tagName}${attrString}/>`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (effectiveChildren.length === 1 && "#text" in effectiveChildren[0]!) {
|
|
138
|
+
const textVal = String(effectiveChildren[0]!["#text"]).trim();
|
|
139
|
+
if (textVal.length === 0) {
|
|
140
|
+
return `<${tagName}${attrString}/>`;
|
|
141
|
+
}
|
|
142
|
+
return `<${tagName}${attrString}>${escapeXmlText(textVal)}</${tagName}>`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const childrenString = serializeNodes(effectiveChildren as OrderedNode[]);
|
|
146
|
+
return `<${tagName}${attrString}>${childrenString}</${tagName}>`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (typeof children === "string" || typeof children === "number" || typeof children === "boolean") {
|
|
150
|
+
const textStr = String(children).trim();
|
|
151
|
+
if (textStr.length === 0) {
|
|
152
|
+
return `<${tagName}${attrString}/>`;
|
|
153
|
+
}
|
|
154
|
+
return `<${tagName}${attrString}>${escapeXmlText(textStr)}</${tagName}>`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return `<${tagName}${attrString}/>`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function escapeXmlAttr(value: string): string {
|
|
161
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function escapeXmlText(value: string): string {
|
|
165
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
166
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { DiagramsCapError } from "./errors.js";
|
|
2
|
+
|
|
3
|
+
// --- Default and Hard Ceiling Constants ---
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_MAX_XML_BYTES = 32 * 1024 * 1024; // 32 MiB
|
|
6
|
+
export const HARD_MAX_XML_BYTES = 512 * 1024 * 1024; // 512 MiB
|
|
7
|
+
|
|
8
|
+
export const DEFAULT_MAX_ELEMENTS = 100_000;
|
|
9
|
+
export const HARD_MAX_ELEMENTS = 500_000;
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_MAX_ATTRIBUTES = 500_000;
|
|
12
|
+
export const HARD_MAX_ATTRIBUTES = 2_000_000;
|
|
13
|
+
|
|
14
|
+
export interface DrawioXmlCaps {
|
|
15
|
+
readonly maxBytes?: number;
|
|
16
|
+
readonly maxElements?: number;
|
|
17
|
+
readonly maxAttributes?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ResolvedDrawioXmlCaps {
|
|
21
|
+
readonly maxBytes: number;
|
|
22
|
+
readonly maxElements: number;
|
|
23
|
+
readonly maxAttributes: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function resolveCap(name: string, value: number | undefined, defaultVal: number, hardCap: number): number {
|
|
27
|
+
if (value === undefined) return defaultVal;
|
|
28
|
+
if (!Number.isInteger(value) || value <= 0 || value > hardCap) {
|
|
29
|
+
throw new DiagramsCapError(`diagrams cap ${name} must be a positive integer in (0, ${hardCap}], got ${value}`);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolves user-supplied caps against safe defaults and hard ceilings.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveDiagramsCaps(caps?: DrawioXmlCaps): ResolvedDrawioXmlCaps {
|
|
38
|
+
return {
|
|
39
|
+
maxBytes: resolveCap("maxBytes", caps?.maxBytes, DEFAULT_MAX_XML_BYTES, HARD_MAX_XML_BYTES),
|
|
40
|
+
maxElements: resolveCap("maxElements", caps?.maxElements, DEFAULT_MAX_ELEMENTS, HARD_MAX_ELEMENTS),
|
|
41
|
+
maxAttributes: resolveCap("maxAttributes", caps?.maxAttributes, DEFAULT_MAX_ATTRIBUTES, HARD_MAX_ATTRIBUTES),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Enforces input byte size ceiling. Throws {@link DiagramsCapError} if exceeded.
|
|
47
|
+
*/
|
|
48
|
+
export function validateByteCap(byteLength: number, caps: ResolvedDrawioXmlCaps): void {
|
|
49
|
+
if (byteLength > caps.maxBytes) {
|
|
50
|
+
throw new DiagramsCapError(`XML input size ${byteLength} bytes exceeds maxBytes cap (${caps.maxBytes})`);
|
|
51
|
+
}
|
|
52
|
+
}
|