@dotcms/ai 26.10.20-1 → 26.10.21-1

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.
Files changed (72) hide show
  1. package/README.md +203 -29
  2. package/adapter.cjs.default.js +1 -0
  3. package/adapter.cjs.js +14 -0
  4. package/adapter.cjs.mjs +2 -0
  5. package/adapter.cjs2.js +0 -0
  6. package/adapter.d.ts +1 -0
  7. package/adapter.esm.js +2 -0
  8. package/adapter.esm2.js +0 -0
  9. package/errors.cjs.js +192 -0
  10. package/errors.esm.js +180 -0
  11. package/format-result.cjs.js +833 -0
  12. package/format-result.esm.js +825 -0
  13. package/package.json +25 -20
  14. package/runtime.cjs.js +0 -0
  15. package/runtime.esm.js +0 -0
  16. package/sandbox.cjs.default.js +1 -0
  17. package/sandbox.cjs.js +69 -0
  18. package/sandbox.cjs.mjs +2 -0
  19. package/sandbox.d.ts +1 -0
  20. package/sandbox.esm.js +50 -0
  21. package/spec.cjs.default.js +1 -0
  22. package/spec.cjs.js +3 -23912
  23. package/spec.cjs.mjs +2 -0
  24. package/spec.cjs2.js +23916 -0
  25. package/spec.d.ts +1 -0
  26. package/spec.esm.js +1 -23914
  27. package/spec.esm2.js +23914 -0
  28. package/src/adapter/context.d.ts +11 -0
  29. package/src/adapter/request-core.d.ts +30 -0
  30. package/src/adapter.d.ts +1 -0
  31. package/src/runtime.d.ts +5 -4
  32. package/src/sandbox/errors.d.ts +23 -1
  33. package/src/sandbox/index.d.ts +1 -1
  34. package/src/sandbox.d.ts +1 -0
  35. package/src/spec.d.ts +1 -0
  36. package/src/tools/definitions/download-assets.d.ts +27 -0
  37. package/src/tools/definitions/execute.d.ts +15 -0
  38. package/src/tools/definitions/page-create.d.ts +23 -0
  39. package/src/tools/definitions/page-place-content.d.ts +32 -0
  40. package/src/tools/definitions/page-verify.d.ts +20 -0
  41. package/src/tools/definitions/search.d.ts +13 -0
  42. package/src/tools/definitions/upload-assets.d.ts +23 -0
  43. package/src/tools/index.d.ts +40 -0
  44. package/src/tools/operations/download-assets.d.ts +55 -0
  45. package/src/tools/operations/page-create.d.ts +81 -0
  46. package/src/tools/operations/page-place-content.d.ts +102 -0
  47. package/src/tools/operations/page-verify.d.ts +164 -0
  48. package/src/tools/operations/shared/asset-common.d.ts +28 -0
  49. package/src/tools/operations/shared/glob.d.ts +22 -0
  50. package/src/tools/operations/shared/local-root.d.ts +11 -0
  51. package/src/tools/operations/shared/page-common.d.ts +45 -0
  52. package/src/tools/operations/shared/page-path.d.ts +49 -0
  53. package/src/tools/operations/shared/resolve.d.ts +43 -0
  54. package/src/tools/operations/upload-assets.d.ts +64 -0
  55. package/src/tools/toolkit/connection.d.ts +56 -0
  56. package/src/tools/toolkit/create-tool.d.ts +66 -0
  57. package/src/tools/toolkit/endpoints.d.ts +52 -0
  58. package/src/tools/toolkit/lenient-boolean.d.ts +14 -0
  59. package/src/tools/toolkit/results.d.ts +32 -0
  60. package/src/tools/toolkit/tool-runtime.d.ts +108 -0
  61. package/src/tools/toolkit/types.d.ts +120 -0
  62. package/src/tools.d.ts +1 -0
  63. package/tools.cjs.default.js +1 -0
  64. package/tools.cjs.js +3850 -0
  65. package/tools.cjs.mjs +2 -0
  66. package/tools.d.ts +1 -0
  67. package/tools.esm.js +3817 -0
  68. package/index.cjs.default.js +0 -1
  69. package/index.cjs.js +0 -7
  70. package/index.cjs.mjs +0 -2
  71. package/index.d.ts +0 -1
  72. package/index.esm.js +0 -1
@@ -0,0 +1,164 @@
1
+ import { LayoutRow } from './shared/page-common';
2
+ import { type DotCMSRuntime } from '../../runtime';
3
+ import { type Endpoint } from '../toolkit/endpoints';
4
+ /**
5
+ * Every endpoint `verifyPage` calls — all reads. The `page_verify` tool enforces exactly this
6
+ * list, and `page-verify.spec.ts` checks every request against it. Add a request, add it here.
7
+ */
8
+ export declare const PAGE_VERIFY_ENDPOINTS: readonly Endpoint[];
9
+ /** Render modes the verify tool supports. LIVE = published; WORKING = latest saved (pre-publish). */
10
+ export type VerifyMode = 'LIVE' | 'WORKING';
11
+ /** The default render mode when the caller does not name one. */
12
+ export declare const DEFAULT_MODE: VerifyMode;
13
+ /** The default language id when the caller does not name one. */
14
+ export declare const DEFAULT_LANGUAGE_ID = 1;
15
+ /**
16
+ * Per-slot verdict:
17
+ * - ok — the slot produced rendered HTML.
18
+ * - empty-vtl-error — content IS placed in the slot, but it rendered empty → the container/widget
19
+ * VTL failed (a #dotParse error swallowed to nothing). Fix lives in Layer 1:
20
+ * re-run that VTL through POST /api/vtl/dynamic to get the parse/eval error.
21
+ * - empty-no-content — the slot resolved but has no contentlets placed. A placement gap, not a code
22
+ * bug — use page_place_content to add content.
23
+ * - cache-stale — the slot rendered HTML, but the full page.rendered came back empty → a
24
+ * page-level cache problem. Set cachettl "0" and re-publish.
25
+ * - not-assembled — the slot rendered HTML in isolation, but distinctive evidence from that
26
+ * HTML is absent from the assembled page → the template/theme omitted it.
27
+ */
28
+ export type SlotVerdict = 'ok' | 'empty-vtl-error' | 'empty-no-content' | 'cache-stale' | 'not-assembled';
29
+ export interface VerifySlotResult {
30
+ /** Container key as the layout addresses it (path for file containers, id for db containers). */
31
+ container: string;
32
+ /** Slot instance uuid. */
33
+ uuid: string;
34
+ /** Whether this slot produced non-empty rendered HTML. */
35
+ rendered: boolean;
36
+ /** Byte length of this slot's rendered HTML (0 when empty). */
37
+ bytes: number;
38
+ /** How many contentlets are placed in the slot (from the render response's contentlets map). */
39
+ contentCount: number;
40
+ /** Classification of the slot's render outcome. */
41
+ verdict: SlotVerdict;
42
+ }
43
+ /** The $URLMapContent resolution outcome — only present for URL-mapped (detail) pages. */
44
+ export interface UrlMapResult {
45
+ /** True when the path resolved to a concrete detail contentlet (vs. the no-match branch). */
46
+ resolved: boolean;
47
+ /** The resolved contentlet identifier, when resolved. */
48
+ contentletId?: string;
49
+ }
50
+ export interface VerifyPageOptions {
51
+ dotcms: DotCMSRuntime;
52
+ /** Page URL path (e.g. "/about-us" or a URL-mapped detail slug like "/blog/my-post"). */
53
+ path: string;
54
+ /**
55
+ * Site to render against — hostname or identifier (UUID). Optional: when omitted the instance's
56
+ * current/default site is used (no host_id sent). Only NON-default sites need this.
57
+ */
58
+ site?: string;
59
+ /** Language id. Default 1. */
60
+ languageId?: number;
61
+ /** Render mode. Default "LIVE" (published). "WORKING" checks the latest saved, pre-publish. */
62
+ mode?: VerifyMode;
63
+ /** Include a bounded prefix of the assembled page HTML in the manifest. Default false. */
64
+ includeHtml?: boolean;
65
+ }
66
+ export interface VerifyHtmlResult {
67
+ /** Bounded prefix of the assembled page HTML. */
68
+ content: string;
69
+ /** Total JavaScript character count before truncation. */
70
+ totalChars: number;
71
+ /** Total UTF-8 byte count before truncation. */
72
+ totalBytes: number;
73
+ /** True when content is only a prefix of the assembled HTML. */
74
+ truncated: boolean;
75
+ /** Maximum number of characters returned in content. */
76
+ limit: number;
77
+ }
78
+ export interface VerifyPageManifest {
79
+ /** The path that was verified. */
80
+ path: string;
81
+ /** The page's resolved url. */
82
+ url: string;
83
+ /** The site the page rendered against (hostname when resolved, else "(default)"). */
84
+ site: string;
85
+ /** The render mode used. */
86
+ mode: VerifyMode;
87
+ /** Language id used. */
88
+ languageId: number;
89
+ /** HTTP status of the render call (200 even when the body is empty — see pageRendered). */
90
+ httpStatus: number;
91
+ /** True when page.rendered came back non-empty. A 200 with pageRendered=false is a swallowed error. */
92
+ pageRendered: boolean;
93
+ /** Byte length of the full page.rendered HTML. */
94
+ pageBytes: number;
95
+ /** Per-slot verdicts, in layout order. */
96
+ slots: VerifySlotResult[];
97
+ /** URL-map resolution for detail pages; null for a regular page. */
98
+ urlMap: UrlMapResult | null;
99
+ /** Actionable notices derived from the verdicts. */
100
+ warnings: string[];
101
+ /** One-line summary plus the next action to take. */
102
+ diagnosis: string;
103
+ /** Bounded assembled HTML, present only when includeHtml=true. */
104
+ html?: VerifyHtmlResult;
105
+ }
106
+ export declare const MAX_INCLUDED_HTML_CHARS = 20000;
107
+ /**
108
+ * Verify that a dotCMS page actually renders — the layer that catches a blank slot, a swallowed
109
+ * #dotParse error, a cache-stale page, or an unpublished edit, none of which a VTL-only check
110
+ * (/api/vtl/dynamic) can see because that runs in a request context, not a render context.
111
+ *
112
+ * Wraps GET /api/v1/page/render/{uri} and absorbs its sharp edges:
113
+ * - Host: the caller passes a hostname (or nothing); the tool resolves it to a host_id. host_id is
114
+ * required only for a NON-default site — omit `site` and the default site is used.
115
+ * - 200 != rendered: #dotParse swallows a VTL error into an empty HTTP 200, so a 200 with an empty
116
+ * body is a failure, surfaced as pageRendered=false, not success.
117
+ * - Two rendered layers that can disagree: each slot's rendered HTML (containers[].rendered[uuid])
118
+ * vs. the assembled page.rendered. The disagreement IS the diagnosis (cache-stale) as opposed to
119
+ * an empty slot (VTL error or no content).
120
+ *
121
+ * Returns a structured verdict — per-slot classification plus a one-line diagnosis with the next
122
+ * action — instead of two JSON blobs the caller has to compare by hand.
123
+ */
124
+ export declare function verifyPage(options: VerifyPageOptions): Promise<VerifyPageManifest>;
125
+ interface RenderResponse {
126
+ entity?: {
127
+ page?: {
128
+ rendered?: string;
129
+ pageURI?: string;
130
+ pageUrl?: string;
131
+ url?: string;
132
+ };
133
+ containers?: Record<string, RenderedContainer>;
134
+ layout?: {
135
+ body?: {
136
+ rows?: LayoutRow[];
137
+ };
138
+ };
139
+ urlContentMap?: {
140
+ identifier?: string;
141
+ inode?: string;
142
+ } | null;
143
+ };
144
+ }
145
+ interface RenderedContainer {
146
+ /** Per-slot rendered HTML, keyed by uuid (ContainerRendered.getRendered()). */
147
+ rendered?: Record<string, string>;
148
+ /** Per-slot placed contentlets, keyed by uuid (ContainerRaw.getContentletsMap()). */
149
+ contentlets?: Record<string, Array<{
150
+ identifier?: string;
151
+ }>>;
152
+ }
153
+ /** Assemble the verdict manifest from a render response. Pure — no I/O, so it is unit-testable. */
154
+ export declare function buildManifest(input: {
155
+ path: string;
156
+ uri: string;
157
+ siteLabel: string;
158
+ mode: VerifyMode;
159
+ languageId: number;
160
+ status: number;
161
+ body: RenderResponse;
162
+ includeHtml?: boolean;
163
+ }): VerifyPageManifest;
164
+ export {};
@@ -0,0 +1,28 @@
1
+ /**
2
+ * What `uploadAssets` and `downloadAssets` share: the entries their manifests are made of, the
3
+ * dotCMS path form both accept, and how a manifest is finished.
4
+ */
5
+ export interface AssetManifestFile {
6
+ path: string;
7
+ bytes: number;
8
+ identifier?: string;
9
+ }
10
+ export interface AssetManifestFailure {
11
+ path: string;
12
+ error: string;
13
+ }
14
+ export interface AssetManifestSkipped {
15
+ path: string;
16
+ reason: string;
17
+ }
18
+ export declare function normalizeDotCMSPath(input: string): {
19
+ siteQualified?: string;
20
+ path: string;
21
+ };
22
+ export declare function sumBytes(files: AssetManifestFile[]): number;
23
+ export declare function sortManifest<T extends {
24
+ files: AssetManifestFile[];
25
+ failures: AssetManifestFailure[];
26
+ skipped?: AssetManifestSkipped[];
27
+ notLive?: AssetManifestFile[];
28
+ }>(manifest: T): T;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The `include` glob filter the asset operations share: `upload_assets` matches it against
3
+ * local paths relative to `src`, `download_assets` against dotCMS paths relative to the folder.
4
+ */
5
+ /**
6
+ * Split an `include` string into its comma-separated patterns — but NOT on commas inside a brace
7
+ * group, so `*.{png,webp,jpg}` stays one pattern while `*.vtl,*.scss` is two. Exported for tests.
8
+ */
9
+ export declare function splitIncludePatterns(include?: string): string[];
10
+ /**
11
+ * Build a matcher over relative POSIX paths from a comma-separated `include` string. A file matches
12
+ * if it matches ANY pattern. No `include` → matches everything. Exported for tests.
13
+ *
14
+ * Supports the glob features callers reasonably assume from a standard glob:
15
+ * - `*` matches any run of chars WITHIN a path segment (does not cross `/`)
16
+ * - `**` matches across segments, including zero (so a leading globstar also matches a top-level file)
17
+ * - `?` matches a single non-`/` char
18
+ * - `{png,webp,jpg}` brace expansion (alternation)
19
+ * A pattern with no `/` matches the file's basename anywhere in the tree; a pattern with a `/` is
20
+ * anchored at the root of `src`.
21
+ */
22
+ export declare function includeMatcher(include?: string): (rel: string) => boolean;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Refuse `path` when it resolves outside `root`. No `root` means no boundary — for trusted,
3
+ * direct callers of the operations; the tools always set one.
4
+ */
5
+ export declare function assertInsideRoot(root: string | undefined, label: 'src' | 'dest', path: string): Promise<void>;
6
+ /**
7
+ * Refuse to write `file` when it would land outside `root`: its directory resolves elsewhere,
8
+ * or the file itself is a symlink — `writeFile` follows one, so a link planted inside the
9
+ * destination would otherwise redirect the write anywhere.
10
+ */
11
+ export declare function assertWritableInsideRoot(root: string | undefined, file: string): Promise<void>;
@@ -0,0 +1,45 @@
1
+ import type { DotCMSColumnContainer } from '@dotcms/types';
2
+ import type { DotCMSRuntime } from '../../../runtime';
3
+ /**
4
+ * Response shapes and probes shared by more than one page tool.
5
+ *
6
+ * Small on purpose: these are the pieces that were previously written out twice and had
7
+ * already started to drift, not a general dumping ground for page types.
8
+ */
9
+ /**
10
+ * One row of a page layout, as `/api/v1/page/json` and `/api/v1/page/render` return it.
11
+ *
12
+ * Hoisted here because `page-verify` and `page-place-content` declared byte-identical
13
+ * copies, which meant two tools parsing the SAME response through two shapes nobody kept in
14
+ * step.
15
+ *
16
+ * The innermost element is DERIVED from `DotCMSColumnContainer` — the SDK's own page-asset
17
+ * contract — so a change to the container shape upstream fails here at compile time instead
18
+ * of drifting silently. `import type` erases at build, so this costs nothing at runtime.
19
+ *
20
+ * The surrounding optionality stays hand-written, and that is deliberate: the canonical
21
+ * `DotPageAssetLayoutRow` declares `columns` (and every field below it) as REQUIRED, which
22
+ * would be a lie about a response this code has not validated. Everything here is optional
23
+ * because the payload is unproven at compile time. `historyUUIDs` is picked off for the same
24
+ * reason — these tools never read it, and requiring it would assert a field that may be
25
+ * absent. `@dotcms/dotcms-models` also ships a `DotLayoutRow`, but it is the editor-side
26
+ * model (fully required, and its barrel pulls Angular into the transitive graph), so it is
27
+ * the wrong contract for a tool parsing a REST response.
28
+ */
29
+ export interface LayoutRow {
30
+ columns?: Array<{
31
+ containers?: Array<Pick<DotCMSColumnContainer, 'identifier' | 'uuid'>>;
32
+ }>;
33
+ }
34
+ /**
35
+ * Whether a contentlet is published.
36
+ *
37
+ * THROWS on a failed read, and that is the contract callers depend on rather than an
38
+ * oversight. The two previous copies disagreed here: one swallowed every error into `false`,
39
+ * which conflates "this is not live" with "we could not find out" — and for the transfer
40
+ * manifest those are opposite conclusions, since a read failure reported as not-live sends
41
+ * the caller off to re-publish assets that were already fine. `upload-assets` needs the
42
+ * distinction, so the shared primitive is the honest one and the caller that wants a
43
+ * best-effort answer catches for itself.
44
+ */
45
+ export declare function isContentLive(dotcms: DotCMSRuntime, identifier: string): Promise<boolean>;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Normalization for the page paths callers hand to the page tools.
3
+ *
4
+ * Shared because all three tools build a request URL by interpolating a caller-supplied path
5
+ * into an endpoint template — `/api/v1/page/render${uri}`, `/api/v1/page/json${uri}` — and
6
+ * only `page_create` was normalizing first. The two that were not had a live correctness bug
7
+ * and a latent security one:
8
+ *
9
+ * - Live today: `page_verify({ path: '/a/../b' })` renders `/b` while the manifest reports
10
+ * `/a/../b`, and a `#` silently truncates the path. The tool's whole job is to report on
11
+ * the page it actually checked, so reporting a different path than it read is a
12
+ * correctness failure, not a cosmetic one.
13
+ * - Latent: `requestCore` policy-checks the RAW request string and normalizes afterwards,
14
+ * so a path like `/../../../../api/v1/users/current` would pass an `/api/v1/page/` prefix
15
+ * allowlist and then resolve somewhere else entirely. No `allow` policy is configured
16
+ * today, which is the only reason this is not already exploitable — and adding one is the
17
+ * natural next hardening step for this tool surface.
18
+ */
19
+ /**
20
+ * The canonical form of a page path: leading slash, `.`/`..` collapsed, query and fragment
21
+ * dropped, percent-encoding decoded, encoded separators refused.
22
+ *
23
+ * Returned so the CALLER can report what it actually requested. A tool that normalizes for
24
+ * the request but echoes the raw input in its manifest is telling the model about a page it
25
+ * did not look at.
26
+ */
27
+ export declare function normalizePagePath(path: string, label?: string): string;
28
+ /**
29
+ * Split a page-relative URL into the parent folder and the leaf url stored on the page.
30
+ *
31
+ * "/books/index" → { folder: "/books", url: "index", fullPath: "/books/index" }
32
+ * "/books" → { folder: "/books", url: "index", fullPath: "/books/index" }
33
+ * "/about-us/" → { folder: "/about-us", url: "index", fullPath: "/about-us/index" }
34
+ * "/" → { folder: "/", url: "index", fullPath: "/index" }
35
+ *
36
+ * Shares its normalization with {@link normalizePagePath} but NOT its output: the folder-vs-leaf
37
+ * decision is page-create's alone. The URL API preserves a trailing slash but does not know that
38
+ * "/about-us/" means a folder index while "/about-us" means a leaf url, and dotCMS pages always
39
+ * have a leaf url (commonly "index"), so a path with no explicit leaf gets one — matching how the
40
+ * admin UI and the rest of the platform address a folder's default page.
41
+ *
42
+ * Segments are returned DECODED here, because they become folder and page names rather than
43
+ * parts of a URL.
44
+ */
45
+ export declare function splitUrlPath(urlPath: string): {
46
+ folder: string;
47
+ url: string;
48
+ fullPath: string;
49
+ };
@@ -0,0 +1,43 @@
1
+ import { type DotCMSRuntime } from '../../../runtime';
2
+ import { type Endpoint } from '../../toolkit/endpoints';
3
+ /**
4
+ * Every endpoint this module calls. Each operation that resolves a site or language includes
5
+ * these in its own list. `resolve.spec.ts` checks every request against them.
6
+ */
7
+ export declare const RESOLVE_ENDPOINTS: readonly Endpoint[];
8
+ /** A resolved site: the identifier the API needs, plus the hostname for manifests. */
9
+ export interface ResolvedSite {
10
+ identifier: string;
11
+ hostname: string;
12
+ }
13
+ /**
14
+ * Resolve a site (hostname OR identifier) to its identifier + hostname.
15
+ *
16
+ * Order is deliberate — cheapest and least fallible first:
17
+ * 1. the session cache, when it actually has sites;
18
+ * 2. a direct `GET /api/v1/site/{id}` when the caller gave something UUID-shaped;
19
+ * 3. a filtered `GET /api/v1/site` lookup by hostname.
20
+ *
21
+ * Step 2 matters most for the reported failure: a caller who already knows the identifier
22
+ * should never be blocked by a cache, because the identifier is precisely what the
23
+ * downstream call needs and no lookup is required to use it.
24
+ */
25
+ export declare function resolveSite(dotcms: DotCMSRuntime, site: string): Promise<ResolvedSite>;
26
+ /**
27
+ * The instance's default site — what a path with no `//host` prefix means.
28
+ *
29
+ * The session cache first; then `GET /api/v1/site/defaultSite`, so a failed context load does
30
+ * not leave the caller unscoped. A failure of that read propagates as the instance error it is:
31
+ * falling back to "no site" would silently widen whatever the caller was about to scope.
32
+ */
33
+ export declare function resolveDefaultSite(dotcms: DotCMSRuntime): Promise<ResolvedSite>;
34
+ /**
35
+ * Resolve a language id against the instance.
36
+ *
37
+ * dotCMS silently falls back to its default language for an unknown id rather than
38
+ * rejecting it, so an unrecognised id does not fail — it quietly writes to a DIFFERENT
39
+ * language than the caller named while the manifest echoes the id they asked for. That is
40
+ * worth catching, but ONLY when the language list actually loaded: with an empty list the
41
+ * caller's explicit id is better evidence than our missing cache, so it is passed through.
42
+ */
43
+ export declare function resolveLanguageId(dotcms: DotCMSRuntime, languageId?: number): Promise<number>;
@@ -0,0 +1,64 @@
1
+ import { type AssetManifestFailure, type AssetManifestFile, type AssetManifestSkipped } from './shared/asset-common';
2
+ import { type DotCMSRuntime } from '../../runtime';
3
+ import { type Endpoint } from '../toolkit/endpoints';
4
+ /**
5
+ * Every endpoint `uploadAssets` calls — the `upload_assets` tool enforces exactly this list,
6
+ * and `upload-assets.spec.ts` checks every request against it. Add a request, add it here.
7
+ */
8
+ export declare const UPLOAD_ASSETS_ENDPOINTS: readonly Endpoint[];
9
+ export interface UploadAssetsOptions {
10
+ dotcms: DotCMSRuntime;
11
+ /** Absolute local directory the files are read from. */
12
+ src: string;
13
+ /** Host-qualified destination folder, e.g. `//demo.dotcms.com/application/themes/travel`. */
14
+ dest: string;
15
+ /** Comma-separated glob filter, matched against each path relative to `src`. */
16
+ include?: string;
17
+ /** Publish (`/api/v2/assets/publish`) rather than only save. */
18
+ publish: boolean;
19
+ /** After publishing, confirm each asset reports live. */
20
+ verify: boolean;
21
+ /**
22
+ * Directory `src` must be inside — after symlinks resolve. Omit only when YOU chose `src`;
23
+ * the `upload_assets` tool always sets it.
24
+ */
25
+ root?: string;
26
+ /**
27
+ * Largest file to send, in bytes. A larger one is recorded as a failure before it is read,
28
+ * and the rest still upload. Each file is held in memory once while it is sent, so this
29
+ * bounds the upload's memory. Unbounded when omitted; the `upload_assets` tool always sets it.
30
+ */
31
+ maxFileBytes?: number;
32
+ /**
33
+ * Most files one upload sends (after `include`). More is refused before anything is sent,
34
+ * and the directory walk stops as soon as it is exceeded. Unbounded when omitted; the
35
+ * `upload_assets` tool always sets it.
36
+ */
37
+ maxFiles?: number;
38
+ /**
39
+ * Most entries — files and directories — the walk of `src` visits, whether or not
40
+ * `include` selects them. More is refused before anything is sent. `maxFiles` alone does
41
+ * not bound the walk: a vast `src` with a narrow `include` matches few files and is walked
42
+ * to the end. Unbounded when omitted; the `upload_assets` tool always sets it.
43
+ */
44
+ maxScannedEntries?: number;
45
+ }
46
+ /** What `uploadAssets` returns — never the file bytes, only what landed and what did not. */
47
+ export interface UploadAssetsManifest {
48
+ src: string;
49
+ dest: string;
50
+ count: number;
51
+ bytes: number;
52
+ files: AssetManifestFile[];
53
+ failures: AssetManifestFailure[];
54
+ skipped: AssetManifestSkipped[];
55
+ /** Published assets that did not report live when verified. */
56
+ notLive: AssetManifestFile[];
57
+ warnings: string[];
58
+ }
59
+ /**
60
+ * Upload every file under a local directory as dotCMS file assets, preserving relative
61
+ * paths. Files are saved or published one at a time; a failed file is recorded in the
62
+ * manifest rather than aborting the batch.
63
+ */
64
+ export declare function uploadAssets(options: UploadAssetsOptions): Promise<UploadAssetsManifest>;
@@ -0,0 +1,56 @@
1
+ import type { RequestCallEvent } from '../../runtime';
2
+ /**
3
+ * A value given as-is, or a resolver read on every tool call — for tokens that rotate, come
4
+ * from a secrets manager, or are read lazily from a host's own configuration. A resolver may
5
+ * return `undefined` (e.g. an unset environment variable); the call then fails with a readable
6
+ * `CONFIGURATION` failure instead of the host crashing.
7
+ */
8
+ export type Resolvable<T> = T | (() => T | undefined | Promise<T | undefined>);
9
+ /** Who is calling which dotCMS instance — everything a tool needs that is not about the tool. */
10
+ export interface DotCMSConnectionConfig {
11
+ /** dotCMS instance URL. */
12
+ url: Resolvable<string>;
13
+ /** dotCMS API token. Injected host-side — it never reaches the model or the sandbox. */
14
+ token: Resolvable<string>;
15
+ /** Observability hook fired around each request (token and sensitive bodies are never passed). */
16
+ onCall?: (event: RequestCallEvent) => void;
17
+ /** Called when loading instance context (sites, content types, …) fails. */
18
+ onContextError?: (label: string, error: unknown) => void;
19
+ /**
20
+ * Called when a `url` / `token` resolver throws — the host's view of the failure. The model
21
+ * is only told the connection could not be resolved: a resolver's own error (a secrets
22
+ * manager's message) can carry paths, key names or identifiers, so its detail comes here.
23
+ */
24
+ onResolveError?: (field: 'url' | 'token', error: unknown) => void;
25
+ }
26
+ /**
27
+ * A dotCMS connection: created once, handed to every tool. Holds no state and makes no request
28
+ * — so a host serving many users creates one per session, each with that user's token.
29
+ */
30
+ export interface DotCMSConnection {
31
+ readonly __dotcmsConnection: true;
32
+ readonly config: Readonly<DotCMSConnectionConfig>;
33
+ }
34
+ /**
35
+ * Describe the dotCMS instance and identity the tools act as.
36
+ *
37
+ * ```ts
38
+ * const dotcms = dotcmsConnection({ url, token: () => secrets.get('dotcms') });
39
+ * const tools = [searchTool(dotcms), pageVerifyTool(dotcms)];
40
+ * ```
41
+ *
42
+ * Nothing is read, validated or fetched here — the values are resolved on each tool call.
43
+ */
44
+ export declare function dotcmsConnection(config: DotCMSConnectionConfig): DotCMSConnection;
45
+ /** Whether `value` came from {@link dotcmsConnection}. */
46
+ export declare function isDotCMSConnection(value: unknown): value is DotCMSConnection;
47
+ /**
48
+ * The URL and token for one call. Throws `ConfigurationError` when the tool was given no
49
+ * connection at all (a JavaScript caller, or a cast) or a resolver failed; an EMPTY value is
50
+ * left to `createToolRuntime`, which reports it the same way.
51
+ */
52
+ export declare function resolveConnection(connection: unknown): Promise<{
53
+ url: string;
54
+ token: string;
55
+ config: DotCMSConnectionConfig;
56
+ }>;
@@ -0,0 +1,66 @@
1
+ import { z } from 'zod';
2
+ import { type DotCMSConnection } from './connection';
3
+ import { type Endpoint } from './endpoints';
4
+ import { type DotCMSRuntime } from '../../runtime';
5
+ import type { DotCMSTool, DotCMSToolAnnotations, ExecuteToolOptions, RequestToolOptions } from './types';
6
+ /** The union of every factory's options — what `createTool` may find on any of them. */
7
+ type AnyToolOptions = Partial<ExecuteToolOptions> & Partial<RequestToolOptions>;
8
+ /** What a tool's handler receives for one call. */
9
+ export interface ToolContext<TOptions extends object> {
10
+ /** The options the tool's factory was called with. */
11
+ options: TOptions;
12
+ /**
13
+ * Build this call's runtime from the resolved connection. Throws `ConfigurationError` when
14
+ * the URL or token is empty, which `execute` turns into a `CONFIGURATION` failure.
15
+ */
16
+ runtime(runtimeOptions?: {
17
+ timeout?: number;
18
+ includeSpec?: boolean;
19
+ }): DotCMSRuntime;
20
+ }
21
+ /**
22
+ * The static half of a tool — everything except the connection and options a consumer passes
23
+ * its factory. `handler` receives input that already passed `inputSchema`, and anything it
24
+ * throws becomes a `ToolFailure`.
25
+ */
26
+ export interface ToolDefinition<TInput extends z.ZodObject, TResult, TOptions extends object> {
27
+ name: string;
28
+ title: string;
29
+ description: string;
30
+ inputSchema: TInput;
31
+ annotations: DotCMSToolAnnotations;
32
+ /**
33
+ * Every endpoint the tool calls — enforced, not documented: any other request fails with
34
+ * a `POLICY` failure before it reaches the wire. `'model-chosen'` is for `execute` alone,
35
+ * where the model picks the endpoints and the consumer's `allow` bounds them instead.
36
+ */
37
+ endpoints: readonly Endpoint[] | 'model-chosen';
38
+ /** Appended to the rejection message when a request falls outside `endpoints`. */
39
+ endpointHint?: string;
40
+ /**
41
+ * The tool's text form, for a tool whose result IS text (`search`, `execute`): shown to the
42
+ * model as text rather than as a JSON string escaped inside an object. Omit it and results
43
+ * are shown as structured JSON. Declared here, typed against `TResult`, so the choice is the
44
+ * tool's — never inferred from what a result happens to look like.
45
+ */
46
+ toText?: (result: TResult) => string;
47
+ handler(input: z.output<TInput>, ctx: ToolContext<TOptions>): Promise<TResult>;
48
+ }
49
+ /** Identity helper that infers the generics, so each handler sees its own input type. */
50
+ export declare function defineTool<TInput extends z.ZodObject, TResult, TOptions extends object = object>(definition: ToolDefinition<TInput, TResult, TOptions>): ToolDefinition<TInput, TResult, TOptions>;
51
+ /**
52
+ * Turn a definition, a connection and a consumer's options into the tool a framework
53
+ * registers.
54
+ *
55
+ * Cheap and side-effect free: nothing is resolved or fetched here. The connection is resolved
56
+ * inside `execute`, on every call — so a host started without credentials still boots and
57
+ * lists its tools, and each call reports the `CONFIGURATION` problem instead of the host
58
+ * crashing at startup.
59
+ *
60
+ * The one thing checked here is the consumer's own static `allow` list (`executeTool`), which
61
+ * becomes the tool's policy now: an entry that is not a path throws a `ValidationError`, so a
62
+ * malformed security boundary fails at startup instead of opening the API (see
63
+ * `toRequestPolicy`).
64
+ */
65
+ export declare function createTool<TInput extends z.ZodObject, TResult, TOptions extends AnyToolOptions>(definition: ToolDefinition<TInput, TResult, TOptions>, connection: DotCMSConnection, options: TOptions): DotCMSTool<TInput, TResult>;
66
+ export {};
@@ -0,0 +1,52 @@
1
+ import { type RequestPolicy } from '../../adapter/request-core';
2
+ import type { RuntimeAllow } from '../../runtime';
3
+ /**
4
+ * One endpoint a tool may call: an HTTP method and a path pattern, e.g.
5
+ * `'POST /api/v1/page/{pageId}/content'`.
6
+ *
7
+ * In the pattern, `{name}` matches exactly one path segment and a trailing `/**` matches
8
+ * anything below that prefix. Patterns are matched against the RESOLVED path the request core
9
+ * hands a policy (dot-segments and encoded dots already collapsed), so `/**` cannot be used
10
+ * to climb out of its prefix.
11
+ */
12
+ export type Endpoint = `${'GET' | 'POST' | 'PUT' | 'DELETE'} /${string}`;
13
+ /**
14
+ * What loading instance context reads. The runtime does it on a tool's behalf, so every tool
15
+ * that touches context includes these — they are the one endpoint list the toolkit owns.
16
+ * Every other list lives next to the operation that makes those requests.
17
+ */
18
+ export declare const CONTEXT_ENDPOINTS: readonly Endpoint[];
19
+ /** Whether `req` is one of `endpoints`. */
20
+ export declare function matchesEndpoint(endpoints: readonly Endpoint[], req: {
21
+ method: string;
22
+ path: string;
23
+ }): boolean;
24
+ /**
25
+ * The calls `endpoints` would refuse, as `METHOD path` strings — empty when every call is
26
+ * covered. Paths are resolved the way the request core resolves them before its policy check.
27
+ *
28
+ * What keeps an allow-list honest: the operation specs run every call their fakes see through
29
+ * this, so a request added to an operation without adding it to the tool's endpoints fails
30
+ * the build instead of being refused in production.
31
+ */
32
+ export declare function unlistedCalls(endpoints: readonly Endpoint[], calls: ReadonlyArray<{
33
+ method?: string;
34
+ path: string;
35
+ }>): string[];
36
+ /**
37
+ * The policy for a fixed-purpose tool: exactly the endpoints it owns, and nothing else.
38
+ *
39
+ * The consumer never configures this — the tool knows what it calls. It is least privilege by
40
+ * construction: a bug in `page_verify`, or a page path shaped to reach somewhere else, cannot
41
+ * get past the render and site-lookup endpoints the tool actually needs.
42
+ */
43
+ export declare function toolPolicy(toolName: string, endpoints: readonly Endpoint[], hint?: string): RequestPolicy;
44
+ /**
45
+ * The policy for `execute`, where the MODEL chooses the endpoints: the consumer's `allow`
46
+ * bounds its code, plus the context reads the runtime makes on the tool's behalf — so a narrow
47
+ * `allow` never silently empties the `sites` / `contentTypes` globals the code relies on.
48
+ * Those reads expose nothing the code does not already hold as globals.
49
+ *
50
+ * No `allow` means no policy: the model's code may call anything the token permits.
51
+ */
52
+ export declare function modelChosenPolicy(allow: RuntimeAllow | undefined): RequestPolicy | undefined;
@@ -0,0 +1,14 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * A boolean that also accepts the string forms tool-calling clients (MCP and others) often
4
+ * send ("true"/"false"/"1"/"0"). Plain `z.coerce.boolean()` is wrong here: it uses JS
5
+ * truthiness, so the string "false" becomes `true`. This maps the string forms to their
6
+ * intended value and leaves real booleans untouched.
7
+ *
8
+ * The default is declared twice, on purpose. The inner one covers a blank string, which the
9
+ * preprocess turns into `undefined` AFTER the outer wrapper has already let it through. The
10
+ * outer one is what marks the field optional in the generated JSON Schema: a preprocess hides
11
+ * the inner default from `z.toJSONSchema`, so without it the schema a model reads lists the
12
+ * field as REQUIRED even though omitting it is the normal case.
13
+ */
14
+ export declare const lenientBoolean: (defaultValue: boolean) => z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodDefault<z.ZodBoolean>>>;
@@ -0,0 +1,32 @@
1
+ import { type ToolFailure } from './tool-runtime';
2
+ /**
3
+ * What the two code tools (`search`, `execute`) resolve to: the capped text of the model's
4
+ * code's result. An object rather than a bare string because some frameworks (Google ADK)
5
+ * require tool results to be objects.
6
+ */
7
+ export interface CodeToolResult {
8
+ result: string;
9
+ }
10
+ /** A JSON value: the shape the Vercel AI SDK requires of a `json` model output. */
11
+ export type JSONValue = null | string | number | boolean | {
12
+ [key: string]: JSONValue | undefined;
13
+ } | JSONValue[];
14
+ /** A tool's model-facing output, in the shape the Vercel AI SDK's `toModelOutput` returns. */
15
+ export type ToolModelOutput = {
16
+ type: 'text';
17
+ value: string;
18
+ } | {
19
+ type: 'json';
20
+ value: JSONValue;
21
+ };
22
+ /**
23
+ * How a tool shows one of its results to the model — the one place that is decided.
24
+ *
25
+ * `toText` is the TOOL's declaration that it has a text form, typed against its own result:
26
+ * nothing is inferred from the value's shape, so a result can never be mistaken for text
27
+ * because it happens to look like one. A tool without it is shown as structured JSON, and so
28
+ * is a failure from any tool.
29
+ */
30
+ export declare function renderToolOutput<TResult>(toText: ((result: TResult) => string) | undefined, output: TResult | ToolFailure): ToolModelOutput;
31
+ /** A model output as the text a text-only transport (an MCP `content` block) carries. */
32
+ export declare function modelOutputText(output: ToolModelOutput): string;