@lanes-sh/link 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -8
- package/instructions/skills/lanes-link/SKILL.md +59 -14
- package/package.json +1 -1
- package/src/auth/index.ts +3 -1
- package/src/auth/oauth/metadata.ts +83 -9
- package/src/auth/oauth/redirects.ts +70 -0
- package/src/auth/oauth/server.ts +49 -69
- package/src/auth/oauth/store.ts +19 -5
- package/src/cli/argv.ts +50 -0
- package/src/cli/brand.ts +178 -0
- package/src/cli/callback-page.ts +108 -128
- package/src/cli/commands/connect/accounts.ts +5 -0
- package/src/cli/commands/connect/assertion.ts +187 -0
- package/src/cli/commands/connect/authorise.ts +61 -17
- package/src/cli/commands/connect/client.ts +37 -9
- package/src/cli/commands/connect/discover.ts +94 -0
- package/src/cli/commands/connect/family.ts +72 -0
- package/src/cli/commands/connect/index.ts +113 -115
- package/src/cli/commands/connect/method.ts +237 -0
- package/src/cli/commands/connect/outcome.ts +42 -1
- package/src/cli/commands/connect/pasted-token.ts +66 -0
- package/src/cli/commands/connect/requirements.ts +60 -8
- package/src/cli/commands/connect/setup.ts +16 -5
- package/src/cli/commands/connect/target-note.ts +34 -0
- package/src/cli/commands/identity.ts +258 -0
- package/src/cli/commands/knowledge/index.ts +390 -0
- package/src/cli/commands/knowledge/migrate.ts +180 -0
- package/src/cli/commands/knowledge/setup.ts +144 -0
- package/src/cli/commands/knowledge.ts +10 -0
- package/src/cli/commands/mcp/harnesses.ts +16 -2
- package/src/cli/commands/mcp/register.ts +9 -1
- package/src/cli/commands/mcp/stdio.ts +21 -0
- package/src/cli/commands/operate/dashboard.ts +107 -0
- package/src/cli/commands/operate/findings.ts +151 -0
- package/src/cli/commands/operate/inspect.ts +56 -158
- package/src/cli/commands/operate/outputs.ts +38 -11
- package/src/cli/commands/operate/serve.ts +3 -0
- package/src/cli/commands/operate/token.ts +1 -1
- package/src/cli/commands/operate.ts +2 -0
- package/src/cli/commands/profile/declare.ts +154 -0
- package/src/cli/commands/profile/removal.ts +17 -0
- package/src/cli/commands/profile.ts +83 -35
- package/src/cli/commands/setup.ts +22 -6
- package/src/cli/commands/target.ts +65 -83
- package/src/cli/config-edit.ts +48 -144
- package/src/cli/config-repair.ts +186 -0
- package/src/cli/dashboard-page.ts +284 -0
- package/src/cli/dashboard-shell.ts +125 -0
- package/src/cli/identity.ts +12 -1
- package/src/cli/main.ts +68 -4
- package/src/cli/oauth-callback.ts +187 -0
- package/src/cli/oauth-exchange.ts +57 -15
- package/src/cli/oauth.ts +67 -177
- package/src/cli/output.ts +21 -5
- package/src/cli/provider-marks.ts +45 -0
- package/src/cli/runtime/open.ts +67 -49
- package/src/cli/runtime/registry.ts +60 -2
- package/src/cli/runtime/select.ts +26 -13
- package/src/cli/runtime/vault.ts +61 -0
- package/src/cli/runtime.ts +2 -1
- package/src/cli/selection.ts +357 -0
- package/src/cli/usage.ts +32 -9
- package/src/connectivity/auth/README.md +7 -1
- package/src/connectivity/auth/basic/index.ts +1 -1
- package/src/connectivity/auth/index.ts +14 -0
- package/src/connectivity/auth/oauth-authcode/broker.ts +26 -0
- package/src/connectivity/auth/oauth-authcode/index.ts +16 -2
- package/src/connectivity/auth/oauth-authcode/provider.ts +1 -1
- package/src/connectivity/auth/oauth-authcode/refresh.ts +3 -3
- package/src/connectivity/auth/oauth-jwt/README.md +33 -0
- package/src/connectivity/auth/oauth-jwt/index.ts +237 -0
- package/src/connectivity/auth/oauth-jwt/key.ts +148 -0
- package/src/connectivity/auth/resolve.ts +1 -1
- package/src/connectivity/auth/token.ts +11 -0
- package/src/connectivity/index.ts +2 -0
- package/src/connectivity/manifest/auth.ts +99 -2
- package/src/connectivity/manifest/identity.ts +12 -0
- package/src/connectivity/manifest/index.ts +3 -1
- package/src/connectivity/manifest/provider.ts +37 -8
- package/src/connectivity/manifest/requirements.ts +109 -6
- package/src/deployments/adapters/filesystem.ts +10 -1
- package/src/deployments/adapters/github-api.ts +106 -0
- package/src/deployments/adapters/github-commit.ts +103 -0
- package/src/deployments/adapters/github-repo.ts +356 -0
- package/src/deployments/adapters/github-testing.ts +258 -0
- package/src/deployments/adapters/github.ts +125 -0
- package/src/deployments/deploy.ts +48 -19
- package/src/deployments/driver.ts +8 -1
- package/src/deployments/gcp/driver.ts +9 -1
- package/src/deployments/gcp/survey.ts +3 -0
- package/src/deployments/knowledge.ts +119 -0
- package/src/deployments/prepare.ts +2 -2
- package/src/deployments/servable.ts +81 -0
- package/src/deployments/target.ts +3 -2
- package/src/deployments/upload.ts +2 -1
- package/src/dispatch/dispatch.ts +1 -1
- package/src/profile/authorization.ts +13 -4
- package/src/profile/identity.ts +60 -0
- package/src/profile/index.ts +15 -5
- package/src/profile/knowledge.ts +124 -0
- package/src/profile/load.ts +17 -5
- package/src/profile/primitives.ts +24 -1
- package/src/profile/schema.ts +72 -4
- package/src/profile/targets.ts +74 -114
- package/src/profile/workspace.ts +71 -84
- package/src/providers/google/calendar/index.ts +2 -0
- package/src/providers/google/contacts/index.ts +2 -0
- package/src/providers/google/docs/index.ts +2 -0
- package/src/providers/google/drive/index.ts +2 -0
- package/src/providers/google/gmail/index.ts +2 -0
- package/src/providers/google/gmail-imap/index.ts +125 -0
- package/src/providers/google/index.ts +2 -1
- package/src/providers/google/shared/oauth.ts +18 -6
- package/src/providers/google/shared/service-account.ts +110 -0
- package/src/providers/google/shared/setup.ts +5 -2
- package/src/providers/google/sheets/index.ts +2 -0
- package/src/providers/google/tasks/index.ts +2 -0
- package/src/providers/identity/provider.ts +166 -0
- package/src/providers/index.ts +3 -0
- package/src/providers/owner.ts +10 -2
- package/src/providers/scopes.ts +2 -0
- package/src/providers/setup/plan.ts +31 -9
- package/src/providers/setup/provider.ts +23 -0
- package/src/providers/slack/index.ts +81 -33
- package/src/providers/slack/oauth.ts +103 -0
- package/src/providers/slack/scopes.ts +37 -0
- package/src/server/container.ts +18 -1
- package/src/server/cors.ts +252 -0
- package/src/server/dashboard.ts +208 -0
- package/src/server/endpoint.ts +57 -4
- package/src/server/generation.ts +1 -0
- package/src/server/generations.ts +13 -2
- package/src/server/harness.ts +20 -3
- package/src/server/index.ts +76 -12
- package/src/server/mcp/build.ts +1 -1
- package/src/server/mcp/index.ts +1 -0
- package/src/server/mcp/instructions.ts +62 -5
- package/src/server/mcp/visibility.ts +42 -0
- package/src/stores/blobs/route.ts +123 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A brand mark per provider, drawn rather than fetched.
|
|
3
|
+
*
|
|
4
|
+
* Vendored from Simple Icons 16.28.0, which dedicates its paths to the public
|
|
5
|
+
* domain under CC0-1.0. **The trademarks themselves remain their owners'** —
|
|
6
|
+
* these identify the service a connection reaches, which is what nominative use
|
|
7
|
+
* is for, and nothing here implies that any of them endorses this.
|
|
8
|
+
*
|
|
9
|
+
* Inlined because there is nowhere else to put them. The page is served under a
|
|
10
|
+
* strict `default-src 'none'`, so it fetches nothing; the alternative — a CDN —
|
|
11
|
+
* would tell a third party which dashboard the owner is looking at, every time
|
|
12
|
+
* they look. There is also no static-asset pipeline to serve them from.
|
|
13
|
+
*
|
|
14
|
+
* Every path is one shape on a `0 0 24 24` canvas, filled with `currentColor`,
|
|
15
|
+
* so a mark inherits the page's text colour and is correct in either theme
|
|
16
|
+
* without a second copy. That is also why these are monochrome rather than the
|
|
17
|
+
* full-colour originals: `callback-page.ts` reserves colour for status, and a
|
|
18
|
+
* row of brand hues would spend it on decoration.
|
|
19
|
+
*
|
|
20
|
+
* Ids sharing a mark do so deliberately — `gmail` and `gmail_mcp` reach the same
|
|
21
|
+
* mailbox by different transports, and the four `icloud_*` providers are one
|
|
22
|
+
* service. The mark names the vendor; the name beside it names the product.
|
|
23
|
+
*
|
|
24
|
+
* Absent here, `dashboard-page.ts` falls back to a monogram. That covers every
|
|
25
|
+
* provider an operator writes themselves, and the two shipped ones Simple Icons
|
|
26
|
+
* does not carry: Slack, whose mark was withdrawn at the brand's request, and
|
|
27
|
+
* Google Contacts, which it never had.
|
|
28
|
+
*/
|
|
29
|
+
export const PROVIDER_MARKS: Readonly<Record<string, string>> = {
|
|
30
|
+
calendar: 'M18.316 5.684H24v12.632h-5.684V5.684zM5.684 24h12.632v-5.684H5.684V24zM18.316 5.684V0H1.895A1.894 1.894 0 0 0 0 1.895v16.421h5.684V5.684h12.632zm-7.207 6.25v-.065c.272-.144.5-.349.687-.617s.279-.595.279-.982c0-.379-.099-.72-.3-1.025a2.05 2.05 0 0 0-.832-.714 2.703 2.703 0 0 0-1.197-.257c-.6 0-1.094.156-1.481.467-.386.311-.65.671-.793 1.078l1.085.452c.086-.249.224-.461.413-.633.189-.172.445-.257.767-.257.33 0 .602.088.816.264a.86.86 0 0 1 .322.703c0 .33-.12.589-.36.778-.24.19-.535.284-.886.284h-.567v1.085h.633c.407 0 .748.109 1.02.327.272.218.407.499.407.843 0 .336-.129.614-.387.832s-.565.327-.924.327c-.351 0-.651-.103-.897-.311-.248-.208-.422-.502-.521-.881l-1.096.452c.178.616.505 1.082.977 1.401.472.319.984.478 1.538.477a2.84 2.84 0 0 0 1.293-.291c.382-.193.684-.458.902-.794.218-.336.327-.72.327-1.149 0-.429-.115-.797-.344-1.105a2.067 2.067 0 0 0-.881-.689zm2.093-1.931l.602.913L15 10.045v5.744h1.187V8.446h-.827l-2.158 1.557zM22.105 0h-3.289v5.184H24V1.895A1.894 1.894 0 0 0 22.105 0zm-3.289 23.5l4.684-4.684h-4.684V23.5zM0 22.105C0 23.152.848 24 1.895 24h3.289v-5.184H0v3.289z',
|
|
31
|
+
docs: 'M14.727 6.727H14V0H4.91c-.905 0-1.637.732-1.637 1.636v20.728c0 .904.732 1.636 1.636 1.636h14.182c.904 0 1.636-.732 1.636-1.636V6.727h-6zm-.545 10.455H7.09v-1.364h7.09v1.364zm2.727-3.273H7.091v-1.364h9.818v1.364zm0-3.273H7.091V9.273h9.818v1.363zM14.727 6h6l-6-6v6z',
|
|
32
|
+
drive: 'M12.01 1.485c-2.082 0-3.754.02-3.743.047.01.02 1.708 3.001 3.774 6.62l3.76 6.574h3.76c2.081 0 3.753-.02 3.742-.047-.005-.02-1.708-3.001-3.775-6.62l-3.76-6.574zm-4.76 1.73a789.828 789.861 0 0 0-3.63 6.319L0 15.868l1.89 3.298 1.885 3.297 3.62-6.335 3.618-6.33-1.88-3.287C8.1 4.704 7.255 3.22 7.25 3.214zm2.259 12.653-.203.348c-.114.198-.96 1.672-1.88 3.287a423.93 423.948 0 0 1-1.698 2.97c-.01.026 3.24.042 7.222.042h7.244l1.796-3.157c.992-1.734 1.85-3.23 1.906-3.323l.104-.167h-7.249z',
|
|
33
|
+
drive_mcp: 'M12.01 1.485c-2.082 0-3.754.02-3.743.047.01.02 1.708 3.001 3.774 6.62l3.76 6.574h3.76c2.081 0 3.753-.02 3.742-.047-.005-.02-1.708-3.001-3.775-6.62l-3.76-6.574zm-4.76 1.73a789.828 789.861 0 0 0-3.63 6.319L0 15.868l1.89 3.298 1.885 3.297 3.62-6.335 3.618-6.33-1.88-3.287C8.1 4.704 7.255 3.22 7.25 3.214zm2.259 12.653-.203.348c-.114.198-.96 1.672-1.88 3.287a423.93 423.948 0 0 1-1.698 2.97c-.01.026 3.24.042 7.222.042h7.244l1.796-3.157c.992-1.734 1.85-3.23 1.906-3.323l.104-.167h-7.249z',
|
|
34
|
+
github: 'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12',
|
|
35
|
+
gmail: 'M24 5.457v13.909c0 .904-.732 1.636-1.636 1.636h-3.819V11.73L12 16.64l-6.545-4.91v9.273H1.636A1.636 1.636 0 0 1 0 19.366V5.457c0-2.023 2.309-3.178 3.927-1.964L5.455 4.64 12 9.548l6.545-4.91 1.528-1.145C21.69 2.28 24 3.434 24 5.457z',
|
|
36
|
+
gmail_mcp: 'M24 5.457v13.909c0 .904-.732 1.636-1.636 1.636h-3.819V11.73L12 16.64l-6.545-4.91v9.273H1.636A1.636 1.636 0 0 1 0 19.366V5.457c0-2.023 2.309-3.178 3.927-1.964L5.455 4.64 12 9.548l6.545-4.91 1.528-1.145C21.69 2.28 24 3.434 24 5.457z',
|
|
37
|
+
icloud_calendar: 'M13.762 4.29a6.51 6.51 0 0 0-5.669 3.332 3.571 3.571 0 0 0-1.558-.36 3.571 3.571 0 0 0-3.516 3A4.918 4.918 0 0 0 0 14.796a4.918 4.918 0 0 0 4.92 4.914 4.93 4.93 0 0 0 .617-.045h14.42c2.305-.272 4.041-2.258 4.043-4.589v-.009a4.594 4.594 0 0 0-3.727-4.508 6.51 6.51 0 0 0-6.511-6.27z',
|
|
38
|
+
icloud_contacts: 'M13.762 4.29a6.51 6.51 0 0 0-5.669 3.332 3.571 3.571 0 0 0-1.558-.36 3.571 3.571 0 0 0-3.516 3A4.918 4.918 0 0 0 0 14.796a4.918 4.918 0 0 0 4.92 4.914 4.93 4.93 0 0 0 .617-.045h14.42c2.305-.272 4.041-2.258 4.043-4.589v-.009a4.594 4.594 0 0 0-3.727-4.508 6.51 6.51 0 0 0-6.511-6.27z',
|
|
39
|
+
icloud_drive: 'M13.762 4.29a6.51 6.51 0 0 0-5.669 3.332 3.571 3.571 0 0 0-1.558-.36 3.571 3.571 0 0 0-3.516 3A4.918 4.918 0 0 0 0 14.796a4.918 4.918 0 0 0 4.92 4.914 4.93 4.93 0 0 0 .617-.045h14.42c2.305-.272 4.041-2.258 4.043-4.589v-.009a4.594 4.594 0 0 0-3.727-4.508 6.51 6.51 0 0 0-6.511-6.27z',
|
|
40
|
+
icloud_mail: 'M13.762 4.29a6.51 6.51 0 0 0-5.669 3.332 3.571 3.571 0 0 0-1.558-.36 3.571 3.571 0 0 0-3.516 3A4.918 4.918 0 0 0 0 14.796a4.918 4.918 0 0 0 4.92 4.914 4.93 4.93 0 0 0 .617-.045h14.42c2.305-.272 4.041-2.258 4.043-4.589v-.009a4.594 4.594 0 0 0-3.727-4.508 6.51 6.51 0 0 0-6.511-6.27z',
|
|
41
|
+
linear: 'M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z',
|
|
42
|
+
notion: 'M4.459 4.208c.746.606 1.026.56 2.428.466l13.215-.793c.28 0 .047-.28-.046-.326L17.86 1.968c-.42-.326-.981-.7-2.055-.607L3.01 2.295c-.466.046-.56.28-.374.466zm.793 3.08v13.904c0 .747.373 1.027 1.214.98l14.523-.84c.841-.046.935-.56.935-1.167V6.354c0-.606-.233-.933-.748-.887l-15.177.887c-.56.047-.747.327-.747.933zm14.337.745c.093.42 0 .84-.42.888l-.7.14v10.264c-.608.327-1.168.514-1.635.514-.748 0-.935-.234-1.495-.933l-4.577-7.186v6.952L12.21 19s0 .84-1.168.84l-3.222.186c-.093-.186 0-.653.327-.746l.84-.233V9.854L7.822 9.76c-.094-.42.14-1.026.793-1.073l3.456-.233 4.764 7.279v-6.44l-1.215-.139c-.093-.514.28-.887.747-.933zM1.936 1.035l13.31-.98c1.634-.14 2.055-.047 3.082.7l4.249 2.986c.7.513.934.653.934 1.213v16.378c0 1.026-.373 1.634-1.68 1.726l-15.458.934c-.98.047-1.448-.093-1.962-.747l-3.129-4.06c-.56-.747-.793-1.306-.793-1.96V2.667c0-.839.374-1.54 1.447-1.632z',
|
|
43
|
+
sheets: 'M11.318 12.545H7.91v-1.909h3.41v1.91zM14.728 0v6h6l-6-6zm1.363 10.636h-3.41v1.91h3.41v-1.91zm0 3.273h-3.41v1.91h3.41v-1.91zM20.727 6.5v15.864c0 .904-.732 1.636-1.636 1.636H4.909a1.636 1.636 0 0 1-1.636-1.636V1.636C3.273.732 4.005 0 4.909 0h9.318v6.5h6.5zm-3.273 2.773H6.545v7.909h10.91v-7.91zm-6.136 4.636H7.91v1.91h3.41v-1.91z',
|
|
44
|
+
tasks: 'M11.383.617C5.097.617 0 5.714 0 12c0 6.286 5.097 11.383 11.383 11.383 6.286 0 11.38-5.097 11.38-11.383a11.34 11.34 0 0 0-.878-4.389l-3.203 3.203c.062.387.1.782.1 1.186a7.398 7.398 0 1 1-7.4-7.398c1.499 0 2.889.448 4.054 1.214l2.857-2.857a11.325 11.325 0 0 0-6.91-2.342zm9.674.756c-.292 0-.583.112-.805.334-2.97 2.965-5.934 5.934-8.9 8.902L9.596 8.854a1.139 1.139 0 0 0-1.61 0l-1.775 1.773a1.139 1.139 0 0 0 0 1.61l4.166 4.163a1.421 1.421 0 0 0 2.012 0L23.666 5.121a1.136 1.136 0 0 0 0-1.61l-1.805-1.804a1.136 1.136 0 0 0-.804-.334z',
|
|
45
|
+
};
|
package/src/cli/runtime/open.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { AnyConnector, ProviderManifest } from '#connectivity';
|
|
|
7
7
|
import { RateLimiter, allowedConnections } from '#policy';
|
|
8
8
|
import {
|
|
9
9
|
ConfigError,
|
|
10
|
+
KNOWLEDGE_LAYOUT,
|
|
10
11
|
layout,
|
|
11
12
|
listProfiles,
|
|
12
13
|
workspacePath,
|
|
@@ -16,12 +17,7 @@ import {
|
|
|
16
17
|
import { ProviderRegistry, toPolicyDocument } from '#registry';
|
|
17
18
|
import { Dispatcher, createConsoleLogger } from '#dispatch';
|
|
18
19
|
import { PROVIDER_MANIFESTS } from '#providers/index.ts';
|
|
19
|
-
import {
|
|
20
|
-
createBlobVaultStore,
|
|
21
|
-
createFileVaultStore,
|
|
22
|
-
createSecretVaultStore,
|
|
23
|
-
type VaultStore,
|
|
24
|
-
} from '#providers/owner.ts';
|
|
20
|
+
import type { VaultStore } from '#providers/owner.ts';
|
|
25
21
|
import {
|
|
26
22
|
openAuditSinks,
|
|
27
23
|
openState,
|
|
@@ -30,11 +26,14 @@ import {
|
|
|
30
26
|
type StorageFactory,
|
|
31
27
|
type TargetInput,
|
|
32
28
|
} from '#deployments/target.ts';
|
|
29
|
+
import { openKnowledge, type FetchLike, type KnowledgeStores } from '#deployments/knowledge.ts';
|
|
30
|
+
import { routeBlobStore } from '#stores/blobs/route.ts';
|
|
33
31
|
import { connectorFactory } from '#connectivity/transports';
|
|
34
32
|
import { requestAuthorizer } from '#connectivity/auth/index.ts';
|
|
35
33
|
import { resolveProfile, type GlobalFlags } from './select.ts';
|
|
36
|
-
import { buildRegistryWithWorkspace,
|
|
34
|
+
import { buildRegistryWithWorkspace, readSkillsForStart, reloadSkills } from './registry.ts';
|
|
37
35
|
import { discoveryProbe } from './discovery.ts';
|
|
36
|
+
import { openVault } from './vault.ts';
|
|
38
37
|
|
|
39
38
|
/**
|
|
40
39
|
* Assembling a profile's runtime from its declared target.
|
|
@@ -56,6 +55,16 @@ export interface Runtime {
|
|
|
56
55
|
readonly storage: BlobStore;
|
|
57
56
|
/** Where skills are kept — `data/<profile>/skills.d/` locally. */
|
|
58
57
|
readonly skills: BlobStore;
|
|
58
|
+
/**
|
|
59
|
+
* Present only when this target keeps memory and skills in a repository.
|
|
60
|
+
*
|
|
61
|
+
* Nothing on the dispatch path reads it: `storage` and `skills` above are
|
|
62
|
+
* already pointed at the right bytes, which is the whole design. It is here
|
|
63
|
+
* so `target show` and `doctor` can say where those bytes are without
|
|
64
|
+
* re-reading the config, and so the migration can reach the repository it is
|
|
65
|
+
* committing to.
|
|
66
|
+
*/
|
|
67
|
+
readonly knowledge?: KnowledgeStores | undefined;
|
|
59
68
|
/** The vault's own store, so `lanes link vault` reaches the same bytes MCP does. */
|
|
60
69
|
readonly vault: VaultStore;
|
|
61
70
|
readonly registry: ProviderRegistry;
|
|
@@ -99,47 +108,21 @@ function skillStore(storage: StorageFactory, profile: string): BlobStore {
|
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
/**
|
|
102
|
-
*
|
|
111
|
+
* What a caller may hand the runtime that is not a flag somebody typed.
|
|
103
112
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* container filesystem, and every item in it was discarded by the next
|
|
108
|
-
* revision without an error to say so.
|
|
113
|
+
* One field, and it exists for the same reason `connect`'s does: the only thing
|
|
114
|
+
* a runtime reaches over the network at open time is a knowledge repository,
|
|
115
|
+
* and a test that could not replace it would either hit github.com or not run.
|
|
109
116
|
*/
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
credentials: SecretStore,
|
|
114
|
-
): VaultStore {
|
|
115
|
-
const { declared, config, root } = input;
|
|
116
|
-
const vault = declared.vault ?? { adapter: 'file' as const };
|
|
117
|
-
|
|
118
|
-
switch (vault.adapter) {
|
|
119
|
-
case 'file':
|
|
120
|
-
return createFileVaultStore({
|
|
121
|
-
path: workspacePath(root, vault.path ?? layout.vault(config.instance.profile)),
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
case 'secret':
|
|
125
|
-
// The document is sealed under LANES_LINK_VAULT_KEY before it gets here,
|
|
126
|
-
// so the credential store holds ciphertext it cannot read. Separate
|
|
127
|
-
// document, separate key, separate environment variable — the backend
|
|
128
|
-
// was never what kept the two stores apart. ADR-022.
|
|
129
|
-
return createSecretVaultStore({
|
|
130
|
-
store: credentials,
|
|
131
|
-
...(vault.ref !== undefined ? { ref: vault.ref } : {}),
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
case 'blob':
|
|
135
|
-
return createBlobVaultStore({
|
|
136
|
-
store: storage(),
|
|
137
|
-
...(vault.path !== undefined ? { key: vault.path } : {}),
|
|
138
|
-
});
|
|
139
|
-
}
|
|
117
|
+
export interface OpenOptions {
|
|
118
|
+
/** Injected for tests. */
|
|
119
|
+
readonly fetch?: FetchLike | undefined;
|
|
140
120
|
}
|
|
141
121
|
|
|
142
|
-
export async function openRuntime(
|
|
122
|
+
export async function openRuntime(
|
|
123
|
+
flags: GlobalFlags,
|
|
124
|
+
options: OpenOptions = {},
|
|
125
|
+
): Promise<Runtime> {
|
|
143
126
|
const { resolution, config, target } = await resolveProfile(flags);
|
|
144
127
|
const declared = config.targets[target];
|
|
145
128
|
if (!declared) throw new ConfigError(`Target "${target}" is not declared`);
|
|
@@ -154,7 +137,23 @@ export async function openRuntime(flags: GlobalFlags): Promise<Runtime> {
|
|
|
154
137
|
// no schema left to migrate.
|
|
155
138
|
const credentials = await openSecrets(adapters);
|
|
156
139
|
const storageFor = await openStorage(adapters, credentials);
|
|
157
|
-
|
|
140
|
+
|
|
141
|
+
// Memory and skills, where a profile keeps them somewhere else (ADR-041).
|
|
142
|
+
//
|
|
143
|
+
// The redirection happens *here*, on the store every consumer was already
|
|
144
|
+
// handed, and that is deliberate. Memory is not addressed by a name anything
|
|
145
|
+
// declares: `buildProviderContext` scopes the profile's blob root to
|
|
146
|
+
// `memory/<connection>`, and `lanes link memory` reaches the same bytes by
|
|
147
|
+
// calling the same two functions. So the dispatcher, the provider, and the
|
|
148
|
+
// CLI need no knowledge of this and cannot disagree about where an entry
|
|
149
|
+
// went — the one thing they are handed already points at the repository.
|
|
150
|
+
//
|
|
151
|
+
// The audit log, `state.kv`, the credential store and the vault keep their
|
|
152
|
+
// own roots on the target's own storage and are untouched.
|
|
153
|
+
const knowledge = await openKnowledge(adapters, credentials, options.fetch);
|
|
154
|
+
const storage = knowledge
|
|
155
|
+
? routeBlobStore(storageFor(), [{ prefix: `${KNOWLEDGE_LAYOUT.memory}/`, store: knowledge.memory }])
|
|
156
|
+
: storageFor();
|
|
158
157
|
const state = openState(storageFor, config.instance.profile);
|
|
159
158
|
|
|
160
159
|
// The durable log, plus any copies the target declares. `sink` is what
|
|
@@ -168,7 +167,7 @@ export async function openRuntime(flags: GlobalFlags): Promise<Runtime> {
|
|
|
168
167
|
(message) => logger.warn(message),
|
|
169
168
|
);
|
|
170
169
|
|
|
171
|
-
const skills = skillStore(storageFor, config.instance.profile);
|
|
170
|
+
const skills = knowledge?.skills ?? skillStore(storageFor, config.instance.profile);
|
|
172
171
|
|
|
173
172
|
// The vault's own store, beside the credential store and never it: a separate
|
|
174
173
|
// document, a separate key, and a separate environment variable
|
|
@@ -228,21 +227,39 @@ export async function openRuntime(flags: GlobalFlags): Promise<Runtime> {
|
|
|
228
227
|
account: connection.account,
|
|
229
228
|
}));
|
|
230
229
|
|
|
230
|
+
// Read before the registry is built, so a store that cannot be reached is a
|
|
231
|
+
// warning rather than the end of the process — see `readSkillsForStart`. Only
|
|
232
|
+
// tolerated when the store is a repository: a local directory that will not
|
|
233
|
+
// read is a real fault worth failing on, exactly as it always was.
|
|
234
|
+
const loaded = await readSkillsForStart(
|
|
235
|
+
skills,
|
|
236
|
+
knowledge !== undefined,
|
|
237
|
+
(message) => logger.warn(message),
|
|
238
|
+
` --profile ${config.instance.profile} --target ${target}`,
|
|
239
|
+
);
|
|
240
|
+
|
|
231
241
|
registry = await buildRegistryWithWorkspace(root, config.instance.profile, {
|
|
232
242
|
skillStore: skills,
|
|
243
|
+
skills: loaded.skills,
|
|
233
244
|
onSkillsChanged: refreshSkills,
|
|
234
245
|
vault: { store: vaultStore, items: await vaultStore.ids() },
|
|
235
246
|
setup: {
|
|
236
247
|
profile: config.instance.profile,
|
|
248
|
+
target,
|
|
237
249
|
profiles: await listProfiles(root),
|
|
238
250
|
catalogue: PROVIDER_MANIFESTS,
|
|
239
251
|
ownClients: Object.keys(config.oauth_apps),
|
|
240
252
|
reachable,
|
|
241
253
|
},
|
|
254
|
+
// Straight off the config: unlike `reachable`, nothing has to be filtered
|
|
255
|
+
// through policy first. Whether the surface is reachable at all is still
|
|
256
|
+
// policy's answer, given once by `mergeCapabilities` — this only decides
|
|
257
|
+
// what it says when it is.
|
|
258
|
+
identity: { profile: config.instance.profile, target, entries: config.identity },
|
|
242
259
|
});
|
|
243
|
-
// Seeded from the
|
|
244
|
-
//
|
|
245
|
-
fingerprint =
|
|
260
|
+
// Seeded from the read above, so the first refresh compares against what is
|
|
261
|
+
// registered rather than rebuilding once to find out.
|
|
262
|
+
fingerprint = loaded.fingerprint;
|
|
246
263
|
|
|
247
264
|
// Discovered capabilities never come from a live call on the dispatch path —
|
|
248
265
|
// that is what keeps the server stateless. But "not live" is not the same as
|
|
@@ -312,6 +329,7 @@ export async function openRuntime(flags: GlobalFlags): Promise<Runtime> {
|
|
|
312
329
|
credentials,
|
|
313
330
|
storage,
|
|
314
331
|
skills,
|
|
332
|
+
...(knowledge ? { knowledge } : {}),
|
|
315
333
|
vault: vaultStore,
|
|
316
334
|
registry,
|
|
317
335
|
refreshSkills,
|
|
@@ -6,11 +6,13 @@ import { loadProfileProviders } from '#providers/custom/index.ts';
|
|
|
6
6
|
import { loadProfileSkills, type LoadedSkill } from '#providers/skills/store.ts';
|
|
7
7
|
import { exampleProvider } from '#providers/example/provider.ts';
|
|
8
8
|
import {
|
|
9
|
+
createIdentityProvider,
|
|
9
10
|
createMemoryVaultStore,
|
|
10
11
|
createSetupProvider,
|
|
11
12
|
createSkillsProvider,
|
|
12
13
|
createVaultProvider,
|
|
13
14
|
memoryProvider,
|
|
15
|
+
type IdentityProviderOptions,
|
|
14
16
|
type SetupProviderOptions,
|
|
15
17
|
type VaultStore,
|
|
16
18
|
} from '#providers/owner.ts';
|
|
@@ -64,6 +66,15 @@ export interface OwnerLayerOptions {
|
|
|
64
66
|
* two capabilities that would report an empty profile as the truth.
|
|
65
67
|
*/
|
|
66
68
|
readonly setup?: SetupProviderOptions;
|
|
69
|
+
/**
|
|
70
|
+
* Who the profile says its owner is.
|
|
71
|
+
*
|
|
72
|
+
* Absent for a registry built to read manifests, which has no config to read
|
|
73
|
+
* it from. The provider still registers — it reports an empty declaration
|
|
74
|
+
* rather than vanishing, so the difference between "nothing declared" and
|
|
75
|
+
* "this build has no such surface" stays visible.
|
|
76
|
+
*/
|
|
77
|
+
readonly identity?: IdentityProviderOptions;
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
/**
|
|
@@ -72,7 +83,8 @@ export interface OwnerLayerOptions {
|
|
|
72
83
|
* Statically imported for now; the registry does not care where a manifest came
|
|
73
84
|
* from, which is what lets workspace YAML register alongside these.
|
|
74
85
|
*
|
|
75
|
-
* `allowReserved` is what admits `memory`, `skills`,
|
|
86
|
+
* `allowReserved` is what admits `memory`, `skills`, `vault`, `setup`, and
|
|
87
|
+
* `identity`. The guard
|
|
76
88
|
* stays rather than being retired: it exists so a *third-party* provider cannot
|
|
77
89
|
* claim a namespace whose policy rules would then silently mean something else,
|
|
78
90
|
* and that reason survives the owner layer shipping. Only this one construction
|
|
@@ -95,10 +107,12 @@ export function buildRegistry(owner: OwnerLayerOptions = {}): ProviderRegistry {
|
|
|
95
107
|
|
|
96
108
|
registry.register(
|
|
97
109
|
createSetupProvider(
|
|
98
|
-
owner.setup ?? { profile: '', catalogue: PROVIDER_MANIFESTS },
|
|
110
|
+
owner.setup ?? { profile: '', target: '', catalogue: PROVIDER_MANIFESTS },
|
|
99
111
|
),
|
|
100
112
|
);
|
|
101
113
|
|
|
114
|
+
registry.register(createIdentityProvider(owner.identity ?? { profile: '' }));
|
|
115
|
+
|
|
102
116
|
for (const manifest of PROVIDERS) registry.register(manifest);
|
|
103
117
|
return registry;
|
|
104
118
|
}
|
|
@@ -177,6 +191,50 @@ export async function reloadSkills(
|
|
|
177
191
|
return fingerprint;
|
|
178
192
|
}
|
|
179
193
|
|
|
194
|
+
/**
|
|
195
|
+
* The skills a starting runtime should register, and their fingerprint.
|
|
196
|
+
*
|
|
197
|
+
* `tolerant` is for a skills store that is somewhere else — a repository, and
|
|
198
|
+
* therefore a network dependency whose failures are ordinary rather than
|
|
199
|
+
* exceptional: an expired token, a spent rate limit, no connectivity on a
|
|
200
|
+
* train. Without it, one of those takes down `openRuntime` itself, and with it
|
|
201
|
+
* every command in the CLI, **including the two that diagnose and undo the
|
|
202
|
+
* arrangement** (`lanes link doctor` and `lanes link knowledge use local`). A
|
|
203
|
+
* token expiring would brick the profile and hide the fix.
|
|
204
|
+
*
|
|
205
|
+
* So a store that cannot be read comes back empty and says so on the log,
|
|
206
|
+
* rather than throwing. This is the same trade `Generation.refreshSkills`
|
|
207
|
+
* already makes for the poll — the endpoint keeps serving what it has instead
|
|
208
|
+
* of falling over — applied to the one read that had no such guard.
|
|
209
|
+
*
|
|
210
|
+
* **A malformed skill still throws, in either mode.** That is not a store
|
|
211
|
+
* failure, it is a document the owner wrote and wants to hear about, and
|
|
212
|
+
* swallowing it would leave one skill silently missing forever.
|
|
213
|
+
*/
|
|
214
|
+
export async function readSkillsForStart(
|
|
215
|
+
store: BlobStore,
|
|
216
|
+
tolerant: boolean,
|
|
217
|
+
warn: (message: string) => void,
|
|
218
|
+
/** `--profile x --target y`, so the two commands in the warning are pasteable. */
|
|
219
|
+
selection = '',
|
|
220
|
+
): Promise<{ skills: LoadedSkill[]; fingerprint: string }> {
|
|
221
|
+
try {
|
|
222
|
+
return { skills: await loadProfileSkills(store), fingerprint: await skillFingerprint(store) };
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (!tolerant || error instanceof ConfigError) throw error;
|
|
225
|
+
|
|
226
|
+
warn(
|
|
227
|
+
`could not read this profile's skills: ${(error as Error).message}\n` +
|
|
228
|
+
` Nothing else is affected. Run \`lanes link doctor${selection}\` for what is wrong, ` +
|
|
229
|
+
`or \`lanes link knowledge use local --migrate${selection}\` to bring them back onto ` +
|
|
230
|
+
'this machine.',
|
|
231
|
+
);
|
|
232
|
+
// An empty fingerprint rather than one of nothing, so the next poll retries
|
|
233
|
+
// instead of concluding the store is empty and staying that way.
|
|
234
|
+
return { skills: [], fingerprint: '' };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
180
238
|
export async function skillFingerprint(store: BlobStore): Promise<string> {
|
|
181
239
|
return (await store.list())
|
|
182
240
|
.map((blob) => `${blob.key}:${blob.size}:${blob.modifiedAt.getTime()}`)
|
|
@@ -4,9 +4,10 @@ import type { BlobStore } from '#stores/blobs';
|
|
|
4
4
|
import {
|
|
5
5
|
loadProfileConfig,
|
|
6
6
|
resolveSelection,
|
|
7
|
-
|
|
7
|
+
requireTarget,
|
|
8
8
|
undeclaredTarget,
|
|
9
9
|
type Config,
|
|
10
|
+
type ProfileSelection,
|
|
10
11
|
type Resolution,
|
|
11
12
|
} from '#profile';
|
|
12
13
|
import { openSecrets, openStorage } from '#deployments/target.ts';
|
|
@@ -45,23 +46,35 @@ export async function resolveProfile(
|
|
|
45
46
|
// what means "read the real environment".
|
|
46
47
|
const env = options.env !== undefined ? { env: options.env } : {};
|
|
47
48
|
|
|
48
|
-
const selection = await resolveSelection({
|
|
49
|
-
profileFlag: flags.profile,
|
|
50
|
-
targetFlag: flags.target,
|
|
51
|
-
...env,
|
|
52
|
-
});
|
|
49
|
+
const selection = await resolveSelection({ profileFlag: flags.profile, ...env });
|
|
53
50
|
|
|
54
51
|
const { config } = await loadProfileConfig(selection.workspaceRoot, selection.profile);
|
|
55
|
-
const
|
|
52
|
+
const target = requireTarget(config, flags.target, {
|
|
56
53
|
allowUndeclared: options.allowUndeclaredTarget === true,
|
|
57
|
-
|
|
54
|
+
profile: selection.profile,
|
|
58
55
|
});
|
|
59
56
|
|
|
60
|
-
return {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
return { resolution: { ...selection, target }, config, target };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A profile without a target, for the commands that do not open one.
|
|
62
|
+
*
|
|
63
|
+
* `check` validates a YAML file, `config show` prints the whole of it, and
|
|
64
|
+
* `policy list` reads a block that is target-independent by construction. Making
|
|
65
|
+
* those three demand a `--target` would be the ceremony that teaches people to
|
|
66
|
+
* type `--target local` without reading it, which is how a required flag stops
|
|
67
|
+
* being a guard.
|
|
68
|
+
*/
|
|
69
|
+
export async function resolveProfileOnly(
|
|
70
|
+
flags: GlobalFlags,
|
|
71
|
+
options: { env?: Record<string, string | undefined> } = {},
|
|
72
|
+
): Promise<{ selection: ProfileSelection; config: Config }> {
|
|
73
|
+
const env = options.env !== undefined ? { env: options.env } : {};
|
|
74
|
+
const selection = await resolveSelection({ profileFlag: flags.profile, ...env });
|
|
75
|
+
const { config } = await loadProfileConfig(selection.workspaceRoot, selection.profile);
|
|
76
|
+
|
|
77
|
+
return { selection, config };
|
|
65
78
|
}
|
|
66
79
|
|
|
67
80
|
/**
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { layout, workspacePath } from '#profile';
|
|
2
|
+
import type { SecretStore } from '#secrets';
|
|
3
|
+
import {
|
|
4
|
+
createBlobVaultStore,
|
|
5
|
+
createFileVaultStore,
|
|
6
|
+
createSecretVaultStore,
|
|
7
|
+
type VaultStore,
|
|
8
|
+
} from '#providers/owner.ts';
|
|
9
|
+
import type { StorageFactory, TargetInput } from '#deployments/target.ts';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Which document the vault is, and where it lives.
|
|
13
|
+
*
|
|
14
|
+
* Its own file for the reason `#deployments/knowledge.ts` is: `open.ts` is a
|
|
15
|
+
* composition root, and "which of three backends holds one encrypted document"
|
|
16
|
+
* is a decision with its own reasoning rather than a step in assembling a
|
|
17
|
+
* runtime. Knowledge made the same move and could go to `deployments`; this one
|
|
18
|
+
* cannot, because the vault store is a provider and `deployments` may not import
|
|
19
|
+
* `providers` (`src/architecture.test.ts`). So it stops here, one level up.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The vault's encrypted document, wherever this target keeps it.
|
|
24
|
+
*
|
|
25
|
+
* Defaults to `file`, so a profile written before ADR-014 needs no change and a
|
|
26
|
+
* local run needs no vault configuration at all. `blob` exists because the file
|
|
27
|
+
* adapter was unconditional before: a deployed instance wrote its vault to a
|
|
28
|
+
* container filesystem, and every item in it was discarded by the next
|
|
29
|
+
* revision without an error to say so.
|
|
30
|
+
*/
|
|
31
|
+
export function openVault(
|
|
32
|
+
input: TargetInput,
|
|
33
|
+
storage: StorageFactory,
|
|
34
|
+
credentials: SecretStore,
|
|
35
|
+
): VaultStore {
|
|
36
|
+
const { declared, config, root } = input;
|
|
37
|
+
const vault = declared.vault ?? { adapter: 'file' as const };
|
|
38
|
+
|
|
39
|
+
switch (vault.adapter) {
|
|
40
|
+
case 'file':
|
|
41
|
+
return createFileVaultStore({
|
|
42
|
+
path: workspacePath(root, vault.path ?? layout.vault(config.instance.profile)),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
case 'secret':
|
|
46
|
+
// The document is sealed under LANES_LINK_VAULT_KEY before it gets here,
|
|
47
|
+
// so the credential store holds ciphertext it cannot read. Separate
|
|
48
|
+
// document, separate key, separate environment variable — the backend
|
|
49
|
+
// was never what kept the two stores apart. ADR-022.
|
|
50
|
+
return createSecretVaultStore({
|
|
51
|
+
store: credentials,
|
|
52
|
+
...(vault.ref !== undefined ? { ref: vault.ref } : {}),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
case 'blob':
|
|
56
|
+
return createBlobVaultStore({
|
|
57
|
+
store: storage(),
|
|
58
|
+
...(vault.path !== undefined ? { key: vault.path } : {}),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/cli/runtime.ts
CHANGED
|
@@ -22,6 +22,7 @@ export {
|
|
|
22
22
|
openSecretStoreFor,
|
|
23
23
|
ownerPrincipal,
|
|
24
24
|
resolveProfile,
|
|
25
|
+
resolveProfileOnly,
|
|
25
26
|
type GlobalFlags,
|
|
26
27
|
} from './runtime/select.ts';
|
|
27
28
|
|
|
@@ -31,4 +32,4 @@ export {
|
|
|
31
32
|
type OwnerLayerOptions,
|
|
32
33
|
} from './runtime/registry.ts';
|
|
33
34
|
|
|
34
|
-
export { openRuntime, type Runtime } from './runtime/open.ts';
|
|
35
|
+
export { openRuntime, type OpenOptions, type Runtime } from './runtime/open.ts';
|