@lanes-sh/link 0.6.3 → 0.6.5
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lanes-sh/link",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "A self-hostable MCP gateway for all your connections, memory, tasks, files, and secrets",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://lanes.sh/link",
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
"instructions",
|
|
44
44
|
"README.md",
|
|
45
45
|
"LICENSE",
|
|
46
|
-
"bun.lock",
|
|
47
46
|
"bunfig.toml",
|
|
48
47
|
".dockerignore"
|
|
49
48
|
],
|
|
@@ -92,6 +92,20 @@ export interface ProvisionInput {
|
|
|
92
92
|
* what a target with no profile to walk still needs.
|
|
93
93
|
*/
|
|
94
94
|
readonly readable?: readonly string[];
|
|
95
|
+
/**
|
|
96
|
+
* The profiles this revision serves, so the bucket conditions can name each
|
|
97
|
+
* one's provider manifests.
|
|
98
|
+
*
|
|
99
|
+
* Needed because Cloud Storage IAM conditions cannot express "any profile
|
|
100
|
+
* segment": their CEL is a restricted subset with no `matches`, so the only
|
|
101
|
+
* way to carve out `data/<profile>/providers.d/` is to enumerate the profiles
|
|
102
|
+
* and write one `startsWith` each. `deploy.ts` already resolved the list.
|
|
103
|
+
*
|
|
104
|
+
* Absent leaves the carve-out off entirely rather than guessing, which is the
|
|
105
|
+
* safe direction: the revision keeps write on its own data and the manifests
|
|
106
|
+
* inside it, exactly as it did before the carve-out existed.
|
|
107
|
+
*/
|
|
108
|
+
readonly profiles?: readonly string[];
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
export interface SurveyInput {
|
|
@@ -18,9 +18,31 @@ WORKDIR /app
|
|
|
18
18
|
# The manifest first, so a source-only change reuses the install layer. One
|
|
19
19
|
# package now, rather than fifteen manifests copied ahead of the install to
|
|
20
20
|
# satisfy workspace resolution.
|
|
21
|
-
|
|
21
|
+
#
|
|
22
|
+
# `bun.lock*`, not `bun.lock`, and the star is the whole point: **npm strips a
|
|
23
|
+
# root lockfile out of a published tarball whatever `files` says.** Verified
|
|
24
|
+
# against npm 12, which is what `release.yml` installs before publishing; npm
|
|
25
|
+
# 11.6 still packs it, so a local `npm pack` says the opposite and the gap only
|
|
26
|
+
# shows up in what people actually install.
|
|
27
|
+
#
|
|
28
|
+
# This image is built from whichever tree `lanes link deploy` is run against, and
|
|
29
|
+
# there are two of them. From a checkout the lockfile is present and pins the
|
|
30
|
+
# whole transitive set. From a bun-global install — the only install method this
|
|
31
|
+
# CLI documents — it cannot be there at all, and naming it outright made deploy
|
|
32
|
+
# impossible for every one of those users: the build pulled the base image,
|
|
33
|
+
# pushed a context, and then died on `stat bun.lock: file does not exist`.
|
|
34
|
+
COPY package.json bunfig.toml bun.lock* ./
|
|
22
35
|
|
|
23
|
-
|
|
36
|
+
# Not `--frozen-lockfile || bun install`: that swallows a genuine
|
|
37
|
+
# lockfile-versus-manifest disagreement in a checkout build, which is the one
|
|
38
|
+
# thing the frozen flag is for. The branch says which tree it is building and
|
|
39
|
+
# holds the guarantee that tree can offer.
|
|
40
|
+
#
|
|
41
|
+
# What the fallback gives up is narrower than it looks. Every runtime dependency
|
|
42
|
+
# in `package.json` is an exact version, so the direct set is identical either
|
|
43
|
+
# way; the lockfile pins what those depend on in turn, and a published package
|
|
44
|
+
# has never been able to carry one.
|
|
45
|
+
RUN if [ -f bun.lock ]; then bun install --frozen-lockfile; else bun install; fi
|
|
24
46
|
|
|
25
47
|
COPY src/ src/
|
|
26
48
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { VAULT_DOCUMENT_REF, type SecretRef } from '#secrets';
|
|
2
2
|
import type { DeployStep, ProvisionInput } from '../driver.ts';
|
|
3
3
|
import { encodeRef } from '../adapters/gcp-secret-manager.ts';
|
|
4
|
+
import { layout } from '#profile';
|
|
4
5
|
import { requireProject } from './gcloud.ts';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -296,22 +297,34 @@ export function provisionSteps(input: ProvisionInput): Promise<DeployStep[]> {
|
|
|
296
297
|
`resource.name == "projects/_/buckets/${bucket}/objects/${path}"`;
|
|
297
298
|
|
|
298
299
|
// A provider manifest is configuration that happens to live inside the
|
|
299
|
-
// profile's directory (ADR-030), so `data/` alone no longer separates
|
|
300
|
-
//
|
|
301
|
-
// profile segment rather than matched loosely: `contains("/providers.d/")`
|
|
302
|
-
// would also catch a blob whose own key happened to spell it.
|
|
300
|
+
// profile's directory (ADR-030), so `data/` alone no longer separates what
|
|
301
|
+
// the revision owns from what declares what it is.
|
|
303
302
|
//
|
|
304
|
-
//
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
303
|
+
// **One `startsWith` per profile, because Cloud Storage IAM conditions
|
|
304
|
+
// cannot express anything else.** Their CEL is a restricted subset —
|
|
305
|
+
// `resource.type`, `resource.name` with `startsWith`/`endsWith`/`==`, and
|
|
306
|
+
// the date functions — and it has no `matches`. This was a regex, and it
|
|
307
|
+
// was refused twice over: first because it spelled the dot `\.`, which is
|
|
308
|
+
// not a CEL escape, so the string literal would not parse; then, with that
|
|
309
|
+
// fixed, because `matches` is `undeclared` in this dialect.
|
|
310
|
+
//
|
|
311
|
+
// Both bindings carry `tolerateFailure`, so each attempt printed a warning
|
|
312
|
+
// and left whatever conditions the bucket already had. On a real
|
|
313
|
+
// deployment that was `expression=true` on the read binding — every object
|
|
314
|
+
// in the bucket, the exact opposite of the narrowing the step title claims,
|
|
315
|
+
// and the state ADR-007 says must not exist.
|
|
316
|
+
//
|
|
317
|
+
// Enumerating the served profiles is expressible in the subset that does
|
|
318
|
+
// exist, and it makes `grants.test.ts` honest as a side effect: that file
|
|
319
|
+
// evaluates these as JavaScript, where `startsWith` means what it means
|
|
320
|
+
// here and `matches` quietly did not.
|
|
321
|
+
const manifestPrefixes = (input.profiles ?? []).map((profile) =>
|
|
322
|
+
objectsUnder(`${layout.providers(profile)}/`),
|
|
323
|
+
);
|
|
324
|
+
// No profiles leaves the carve-out off rather than guessing at one: the
|
|
325
|
+
// revision keeps write on its own data, as it did before this existed.
|
|
313
326
|
const providerManifests =
|
|
314
|
-
`
|
|
327
|
+
manifestPrefixes.length > 0 ? `(${manifestPrefixes.join(' || ')})` : null;
|
|
315
328
|
|
|
316
329
|
steps.push({
|
|
317
330
|
title: 'let the revision write its own data, but not the manifests in it',
|
|
@@ -327,7 +340,7 @@ export function provisionSteps(input: ProvisionInput): Promise<DeployStep[]> {
|
|
|
327
340
|
'--role',
|
|
328
341
|
'roles/storage.objectAdmin',
|
|
329
342
|
'--condition',
|
|
330
|
-
`title=owns-its-data,expression=${objectsUnder('data/')} && !${providerManifests}`,
|
|
343
|
+
`title=owns-its-data,expression=${objectsUnder('data/')}${providerManifests ? ` && !${providerManifests}` : ''}`,
|
|
331
344
|
],
|
|
332
345
|
tolerateFailure: true,
|
|
333
346
|
});
|
|
@@ -348,7 +361,7 @@ export function provisionSteps(input: ProvisionInput): Promise<DeployStep[]> {
|
|
|
348
361
|
// The config the revision reads is the workspace file, the profiles
|
|
349
362
|
// beside it, and each profile's own manifests, so name exactly those.
|
|
350
363
|
'--condition',
|
|
351
|
-
`title=reads-its-config,expression=${objectsUnder('profiles/')} || ${objectIs('lanes-link.yaml')} || ${providerManifests}`,
|
|
364
|
+
`title=reads-its-config,expression=${objectsUnder('profiles/')} || ${objectIs('lanes-link.yaml')}${providerManifests ? ` || ${providerManifests}` : ''}`,
|
|
352
365
|
],
|
|
353
366
|
tolerateFailure: true,
|
|
354
367
|
});
|