@valbuild/server 0.122.0 → 0.123.2

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 CHANGED
@@ -1,5 +1,128 @@
1
1
  # @valbuild/server
2
2
 
3
+ ## 0.123.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#619](https://github.com/valbuild/val/pull/619) [`8e58c34`](https://github.com/valbuild/val/commit/8e58c3495d1bf0f221a57082cb0a3045929722a1) Thanks [@freekh](https://github.com/freekh)! - Stop `val validate` reporting published remote gallery images as missing — and `--fix` deleting them
8
+
9
+ A remote gallery (`s.images({ remote: true })`) keys an uploaded entry by its
10
+ remote URL. Two separate checks read that key, normalised it back to the local
11
+ path it encodes, and then required a file to be sitting there:
12
+
13
+ - `val validate` reported _"Gallery … has tracked files that do not exist on
14
+ disk"_ for every published remote image;
15
+ - `val validate --fix` **removed the entry from the gallery**, silently deleting
16
+ the reference to a file that was safely on the content host.
17
+
18
+ Both were wrong for the same reason. Publishing uploads remote files to the
19
+ content host and copies only local ones into the working tree, so an image added
20
+ through the Studio — or over MCP — has no file in the repo by design. Putting one
21
+ there is exactly what remote storage exists to avoid.
22
+
23
+ Remote entries are now exempt from both the missing-file check and the
24
+ metadata-from-disk verification. Whether a remote entry is sound is
25
+ `image:check-remote`'s question, and it already asks it. Nothing changes for
26
+ local entries, or for a remote entry that does have a local file — `--fix`
27
+ promotes a local file to a remote ref and leaves the file where it was, and that
28
+ file is still counted as tracked rather than reported as untracked.
29
+
30
+ - Updated dependencies [[`04b6d4c`](https://github.com/valbuild/val/commit/04b6d4cbbecc131bbaf3c20633af9dad8c857310), [`3e93508`](https://github.com/valbuild/val/commit/3e93508b05d08b0c24947a97c6141a9ad8a3931e)]:
31
+ - @valbuild/shared@0.123.2
32
+ - @valbuild/ui@0.123.2
33
+
34
+ ## 0.123.0
35
+
36
+ ### Minor Changes
37
+
38
+ - [#613](https://github.com/valbuild/val/pull/613) [`c5e7dfd`](https://github.com/valbuild/val/commit/c5e7dfd12aa1371195be642e1c2fb72b6f3e3ce2) Thanks [@freekh](https://github.com/freekh)! - Val's MCP endpoint moves to its own package, and can now upload images
39
+
40
+ Everything that serves Val's content tools over MCP — the tool registry, the
41
+ write path behind it, the request guards and the access-token verification —
42
+ now lives in **`@valbuild/mcp`** instead of being split between
43
+ `@valbuild/server` and `@valbuild/next`.
44
+
45
+ **Nothing changes for an app that already mounts it.** `initValMcp` is still
46
+ exported from `@valbuild/next/server` and behaves exactly as before; it is now a
47
+ ten-line binding over `@valbuild/mcp`, supplying the Next version that
48
+ `initHandlerOptions` asks for. A host that is not Next can call
49
+ `initValMcp` from `@valbuild/mcp` directly.
50
+
51
+ If you built your own host on `createValTools`, import it from `@valbuild/mcp`
52
+ rather than `@valbuild/server`; the tool types moved with it.
53
+
54
+ ## Image uploads
55
+
56
+ An agent can now add an image, with a new `upload_image` tool. It takes a path
57
+ to a file on the machine your app runs on, or the image inline as base64, and
58
+ puts it in an `s.image()` field or an `s.images()` gallery.
59
+
60
+ Uploads are converted **only where the Studio would convert them**: `encode` is
61
+ off unless the schema asks for it (`s.image({ encode: { type: "webp" } })`), and
62
+ when it does, which images are converted, how far they are scaled and when the
63
+ original wins are the same decisions the browser makes — the same code, now
64
+ shared. An upload to a schema without `encode` is stored exactly as it arrived,
65
+ whatever its size.
66
+
67
+ One thing the tool does that the Studio does not: it refuses an image the
68
+ schema's `accept` does not cover, checked on the bytes that would actually be
69
+ stored. The Studio does not need to — its file picker carries `accept` — and
70
+ validation reports a mismatch as server-repairable, so nothing downstream would
71
+ stop it. An agent has no picker. Note the ordering:
72
+ `s.image({ accept: "image/webp", encode: { type: "webp" } })` still takes a PNG,
73
+ because the conversion happens first and it is the result that is checked.
74
+
75
+ It is the one tool you construct yourself, because it needs an image library
76
+ and `sharp` ships a compiled binary per platform. Val does not put one in every
77
+ project that installs it, so you decide:
78
+
79
+ ```sh
80
+ npm install sharp
81
+ ```
82
+
83
+ ```ts
84
+ import sharp from "sharp";
85
+ import { createValImageTools } from "@valbuild/mcp";
86
+ import { sharpImageProcessor } from "@valbuild/mcp/sharp";
87
+
88
+ const { valMcpAuthorize, valMcpTools } = initValMcp(valModules, config, {
89
+ extraTools: createValImageTools(sharpImageProcessor(sharp)),
90
+ });
91
+ ```
92
+
93
+ Leave `extraTools` out and everything else works as before — the agent can read,
94
+ validate and edit content, it just cannot add an image. `sharp` is passed in
95
+ rather than imported, so you can supply another encoder: `ValImageProcessor` is
96
+ two functions, `read` and `encode`.
97
+
98
+ Remotely stored images work too — `s.image().remote()` and
99
+ `s.images({ remote: true })` — and they need nothing extra from the MCP client.
100
+ Adding one does not upload anything to Val's content host: the bytes go into the
101
+ patch store like any other unpublished change, and the push to
102
+ `remote.val.build` happens when you publish, exactly as it does for an image
103
+ added through the Studio. All the tool has to do first is ask the project which
104
+ bucket to name in the ref, and the credential for that is the one your app
105
+ already has — its API key when it has one, and in local development the
106
+ `val login` token in your project, the same one `val validate --fix` uses. If
107
+ you have not logged in, it says so and writes nothing.
108
+
109
+ ## `npm create @valbuild` asks
110
+
111
+ The starter template now ships the MCP endpoint, and `npm create @valbuild`
112
+ asks whether you want it — and, if you do, whether agents should be able to
113
+ upload images, saying that this adds `sharp`. Both default to yes, and both can
114
+ be answered up front for a scripted setup:
115
+
116
+ ```sh
117
+ pnpm create @valbuild my-app --mcp --no-image-uploads
118
+ ```
119
+
120
+ ### Patch Changes
121
+
122
+ - Updated dependencies [[`c5e7dfd`](https://github.com/valbuild/val/commit/c5e7dfd12aa1371195be642e1c2fb72b6f3e3ce2), [`53f670c`](https://github.com/valbuild/val/commit/53f670c0cf2d7a03a6d068c78b7874ce77652c2a)]:
123
+ - @valbuild/shared@0.123.0
124
+ - @valbuild/ui@0.123.0
125
+
3
126
  ## 0.122.0
4
127
 
5
128
  ### Minor Changes
@@ -162,6 +162,35 @@ export declare function handleRemoteGalleryFileUpload(ctx: FixHandlerContext): P
162
162
  export declare function handleRemoteFileDownload(ctx: FixHandlerContext): Promise<FixHandlerResult>;
163
163
  export declare function handleRemoteFileCheck(): Promise<FixHandlerResult>;
164
164
  export declare function handleUniqueFolderCheck(ctx: FixHandlerContext): Promise<FixHandlerResult>;
165
+ /**
166
+ * What is out of step between a gallery's entries and its directory.
167
+ *
168
+ * Two questions, and they treat a remote entry differently — which is the whole
169
+ * reason this is separate from the handler around it:
170
+ *
171
+ * - **Missing**: an entry with no bytes at its local path. Asked of LOCAL
172
+ * entries only. A remote entry's bytes live on the content host, and nothing
173
+ * puts a copy in the working tree: `saveOrUploadFiles` uploads the remote
174
+ * descriptors and copies only the local ones into the tree, so a remote entry
175
+ * added through the Studio (or over MCP) has no local file by design, and
176
+ * demanding one would mean committing remote bytes to git — which is what
177
+ * remote storage exists to avoid. Whether those bytes really are on the host
178
+ * is a different check, `image:check-remote`, which already runs for exactly
179
+ * these entries.
180
+ * - **Untracked**: a file in the directory that no entry claims. Asked of every
181
+ * entry, remote included, and that is why they are normalised to their local
182
+ * path: `val validate --fix` promotes a local file to a remote ref and leaves
183
+ * the file where it was, so a remote entry can perfectly well have one.
184
+ */
185
+ export declare function checkGalleryFiles(input: {
186
+ entryKeys: string[];
187
+ directory: string;
188
+ projectRoot: string;
189
+ fs: Pick<IValFSHost, "fileExists" | "readDirectory">;
190
+ }): {
191
+ missingTrackedFiles: string[];
192
+ untrackedFiles: string[];
193
+ };
165
194
  export declare function handleCheckAllFiles(ctx: FixHandlerContext): Promise<FixHandlerResult>;
166
195
  export declare function handleJsonValuesExtractEntry(ctx: FixHandlerContext): Promise<FixHandlerResult>;
167
196
  /**
@@ -2,10 +2,9 @@ export { createService, Service } from "./Service.js";
2
2
  export { defineExternal, ok, err, isExternalResult, EXTERNAL_RESULT, } from "./externalRecords.js";
3
3
  export type { AdapterFor, BoundExternalRecord, ExternalAuthor, ExternalBuilder, ExternalCtx, ExternalDefinition, ExternalFile, ExternalIssue, ExternalKeyPage, ExternalRecords, ExternalResult, ExternalSearchHit, ExternalSearchPage, ExternalSort, ItemOfModule, ReadonlyRecordHasNoWrites, Returns, } from "./externalRecords.js";
4
4
  export { createValApiRouter, createValServer, safeReadGit } from "./ValRouter.js";
5
- export { createValTools } from "./tools/index.js";
6
- export { VAL_SCOPE_READ, VAL_SCOPE_WRITE, authorIdFromVerifiedSubject, } from "./tools/index.js";
7
- export type { ValScope, ValToolAuth, ValToolContext, ValToolDefinition, ValToolDefinitionJson, ValToolErrorCode, ValToolResult, ValTools, ValToolsOptions, } from "./tools/index.js";
8
5
  export { initHandlerOptions, createValOps } from "./valServerConfig.js";
6
+ export { resolveRemoteFileAuth } from "./valServerConfig.js";
7
+ export type { RemoteFileAuth, ResolveRemoteFileAuthResult, } from "./valServerConfig.js";
9
8
  export { ValModuleLoader } from "./ValModuleLoader.js";
10
9
  export { getCompilerOptions } from "./getCompilerOptions.js";
11
10
  export { ValSourceFileHandler } from "./ValSourceFileHandler.js";
@@ -47,6 +46,9 @@ export { formatPatchSourceError } from "./ValOps.js";
47
46
  export { compareWithCapturedReport, readCapturedReport, replaySnapshot, } from "./debug/replaySnapshot.js";
48
47
  export type { ReplayComparison, ReplayResult } from "./debug/replaySnapshot.js";
49
48
  export type { OrderedPatches, PatchAnalysis, PatchSourceError, PreparedCommit, } from "./ValOps.js";
49
+ export type { ValOps } from "./ValOps.js";
50
+ export type { AuthorId, BinaryFileType, MetadataOfType, Schemas, Sources, } from "./ValOps.js";
51
+ export type { ValServerConfig } from "./ValServer.js";
50
52
  /**
51
53
  * The local-dev patch store, exported so the CLI's debug tooling can read a
52
54
  * snapshot back with the same code the server uses rather than a second
@@ -81,4 +81,41 @@ type CredentialBearingUrl = "valBuildUrl" | "valContentUrl";
81
81
  * internal http host today.
82
82
  */
83
83
  export declare function insecureUrlWarning(name: CredentialBearingUrl, url: string): string | null;
84
+ /**
85
+ * Which credential talks to the content host about REMOTE FILES.
86
+ *
87
+ * A separate question from the one `createValOps` answers, and it has to be:
88
+ * `ValOps` is authenticated per caller, but remote files are project-level —
89
+ * looking up a project's public id and its buckets, and later pushing bytes to
90
+ * them, is the same operation whoever asked for it.
91
+ *
92
+ * The rule, in order:
93
+ *
94
+ * 1. The app's API key, if there is one. Proxy mode always has one; fs mode has
95
+ * one when `VAL_API_KEY` is set.
96
+ * 2. In fs mode, the developer's own `val login` token, read off disk. This is
97
+ * the same file `val validate --fix` reads, and it is why local remote
98
+ * uploads work with no configuration beyond having logged in.
99
+ * 3. Nothing, which is an error rather than a fallback.
100
+ *
101
+ * Lives here rather than inside `createValServer` because the MCP image tool
102
+ * needs the same answer, and this is the file that exists so that two callers
103
+ * cannot disagree about how a project is configured. A registry that decided it
104
+ * had no credential while the Studio in the same process had one would be a
105
+ * genuinely confusing afternoon.
106
+ */
107
+ export type RemoteFileAuth = {
108
+ apiKey: string;
109
+ } | {
110
+ pat: string;
111
+ };
112
+ export type ResolveRemoteFileAuthResult = {
113
+ status: "success";
114
+ auth: RemoteFileAuth;
115
+ } | {
116
+ status: "error";
117
+ errorCode: "project-not-configured" | "pat-error";
118
+ message: string;
119
+ };
120
+ export declare function resolveRemoteFileAuth(options: ValServerConfig): Promise<ResolveRemoteFileAuthResult>;
84
121
  export {};