@agent-native/core 0.228.2-nightly-20261113032548 → 0.228.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/dist/application-state/handlers.d.ts +2 -2
- package/dist/cli/skills-content/an-skill.d.ts +1 -1
- package/dist/cli/skills-content/an-skill.js +16 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/schema-snapshot.generated.js +2 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/file-upload/builder-gateway.d.ts +42 -0
- package/dist/file-upload/builder-gateway.js +185 -0
- package/dist/file-upload/builder.js +8 -11
- package/dist/file-upload/index.d.ts +1 -0
- package/dist/file-upload/index.js +1 -0
- package/dist/framework-package-versions.json +11 -11
- package/dist/identity/rekey.js +2 -0
- package/dist/mcp/embed-app.js +259 -3
- package/dist/mcp/oauth-route.js +7 -0
- package/dist/mcp/screen-memory-stdio.d.ts +3 -3
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/index.d.ts +1 -1
- package/dist/secrets/api-keys.js +8 -7
- package/dist/secrets/index.d.ts +2 -1
- package/dist/secrets/index.js +2 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/secrets/routes.js +7 -8
- package/dist/secrets/schema.d.ts +51 -0
- package/dist/secrets/schema.js +3 -0
- package/dist/secrets/storage.d.ts +83 -1
- package/dist/secrets/storage.js +194 -6
- package/dist/secrets/usage.js +2 -4
- package/dist/secrets/vault-managed.d.ts +17 -0
- package/dist/secrets/vault-managed.js +19 -0
- package/dist/server/agent-engine-api-key-route.d.ts +2 -2
- package/dist/server/builder-signup-experiment.d.ts +4 -4
- package/dist/server/credential-provider.js +7 -7
- package/dist/server/first-touch-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +11 -0
- package/docs/content/security.mdx +1 -1
- package/docs/content/template-dispatch-developers.mdx +7 -7
- package/docs/content/template-dispatch-reference.mdx +16 -10
- package/package.json +4 -4
- package/src/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +11 -0
package/dist/mcp/embed-app.js
CHANGED
|
@@ -31,6 +31,9 @@ export function embedApp(options = {}) {
|
|
|
31
31
|
return {
|
|
32
32
|
title,
|
|
33
33
|
requiresEmbedSession: true,
|
|
34
|
+
// Claude reads this from the resources/read content item and moves the
|
|
35
|
+
// view to its side panel; hosts that do not know the field ignore it.
|
|
36
|
+
_meta: { ui: { preferredDisplayMode: "fullscreen" } },
|
|
34
37
|
...(options.description ? { description: options.description } : {}),
|
|
35
38
|
html: (ctx) => {
|
|
36
39
|
const remoteBridgeFallbackEnabled = ctx.catalogMode !== "directory";
|
|
@@ -1363,6 +1366,7 @@ export function embedApp(options = {}) {
|
|
|
1363
1366
|
window.history.replaceState(window.history.state, "", localPathFromUrl(appUrl, false));
|
|
1364
1367
|
} catch {}
|
|
1365
1368
|
await mountTransplantedHtml(html, appUrl);
|
|
1369
|
+
watchPageTools();
|
|
1366
1370
|
embedSessionRefreshAttempts = 0;
|
|
1367
1371
|
notifyHostHeightRepeatedly();
|
|
1368
1372
|
}
|
|
@@ -1756,6 +1760,7 @@ export function embedApp(options = {}) {
|
|
|
1756
1760
|
${ctx.catalogMode === "directory" ? "" : 'frame.allow = "clipboard-read; clipboard-write";'}
|
|
1757
1761
|
appFrame = frame;
|
|
1758
1762
|
appFrameReady = false;
|
|
1763
|
+
notifyViewToolsChanged();
|
|
1759
1764
|
lastFrameSrc = src;
|
|
1760
1765
|
appFrameDocumentState.loadGeneration += 1;
|
|
1761
1766
|
frame.addEventListener("load", () => {
|
|
@@ -2642,6 +2647,226 @@ export function embedApp(options = {}) {
|
|
|
2642
2647
|
if (bridge && (!appFrame || openAiBridge)) syncOpenAiBridge(bridge);
|
|
2643
2648
|
}, { passive: true });
|
|
2644
2649
|
|
|
2650
|
+
// App tools: the host calls these on this open view, so a "filter it" or
|
|
2651
|
+
// "open that one" turn changes the panel in place instead of opening a new view.
|
|
2652
|
+
const readViewToolName = "read_view";
|
|
2653
|
+
const navigateViewToolName = "navigate_view";
|
|
2654
|
+
const maxViewTextChars = 6000;
|
|
2655
|
+
const maxPageTools = 60;
|
|
2656
|
+
const pageToolWaitMs = 40000;
|
|
2657
|
+
const pageToolOrigins = new Map();
|
|
2658
|
+
let pageToolWatchStarted = false;
|
|
2659
|
+
|
|
2660
|
+
function appPageToolHelper() {
|
|
2661
|
+
if (appFrame) return null;
|
|
2662
|
+
const helper = window.__agentNativeWebMcp;
|
|
2663
|
+
return helper && typeof helper.tools === "function" && typeof helper.call === "function"
|
|
2664
|
+
? helper
|
|
2665
|
+
: null;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
function currentViewUrl() {
|
|
2669
|
+
if (appFrame) {
|
|
2670
|
+
try {
|
|
2671
|
+
return new URL(appFrame.src, window.location.href);
|
|
2672
|
+
// coercion-ok: null means "no readable view URL"; read_view and navigate_view report it as an error
|
|
2673
|
+
} catch {
|
|
2674
|
+
return null;
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
return lastFrameSrc ? new URL(window.location.href) : null;
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
function readViewState() {
|
|
2681
|
+
// A nested app frame is cross-origin and moves on its own, so its src is
|
|
2682
|
+
// only the launch URL; reporting it would describe the wrong page.
|
|
2683
|
+
if (appFrame) throw new Error("This host shows the app in a nested frame, so its current page can't be read.");
|
|
2684
|
+
const url = currentViewUrl();
|
|
2685
|
+
if (!url) throw new Error("No app page has loaded in this view yet.");
|
|
2686
|
+
const state = {
|
|
2687
|
+
path: localPathFromUrl(url, false),
|
|
2688
|
+
title: document.title
|
|
2689
|
+
};
|
|
2690
|
+
{
|
|
2691
|
+
const selection = typeof window.getSelection === "function"
|
|
2692
|
+
? String(window.getSelection() || "").trim()
|
|
2693
|
+
: "";
|
|
2694
|
+
if (selection) state.selectedText = selection.slice(0, maxViewTextChars);
|
|
2695
|
+
const root = document.querySelector("main") || document.body;
|
|
2696
|
+
state.visibleText = root
|
|
2697
|
+
? String(root.innerText || "").replace(/\\s+/g, " ").trim().slice(0, maxViewTextChars)
|
|
2698
|
+
: "";
|
|
2699
|
+
}
|
|
2700
|
+
return state;
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2703
|
+
function navigateView(rawPath) {
|
|
2704
|
+
const path = typeof rawPath === "string" ? rawPath.trim() : "";
|
|
2705
|
+
if (!path.startsWith("/") || path.startsWith("//")) {
|
|
2706
|
+
throw new Error("path must start with / and stay inside the open app, e.g. the path read_view returns.");
|
|
2707
|
+
}
|
|
2708
|
+
// The nested frame was launched on a session scoped to its start URL;
|
|
2709
|
+
// pointing it at a bare app route would drop that session.
|
|
2710
|
+
if (appFrame) {
|
|
2711
|
+
throw new Error("This host shows the app in a nested frame, so it can't be moved from here.");
|
|
2712
|
+
}
|
|
2713
|
+
const base = currentViewUrl();
|
|
2714
|
+
if (!base) throw new Error("No app page has loaded in this view yet.");
|
|
2715
|
+
const target = new URL(path, base);
|
|
2716
|
+
if (target.origin !== base.origin) throw new Error("path must stay inside the open app.");
|
|
2717
|
+
const nextPath = localPathFromUrl(target, false);
|
|
2718
|
+
// React Router reads the location on popstate, so a pushed entry plus a
|
|
2719
|
+
// popstate event moves the transplanted app without reloading it.
|
|
2720
|
+
const current = window.history.state;
|
|
2721
|
+
const idx = current && typeof current.idx === "number" ? current.idx + 1 : 0;
|
|
2722
|
+
window.history.pushState(
|
|
2723
|
+
{ usr: null, key: Math.random().toString(36).slice(2, 10), idx },
|
|
2724
|
+
"",
|
|
2725
|
+
nextPath
|
|
2726
|
+
);
|
|
2727
|
+
window.dispatchEvent(new PopStateEvent("popstate", { state: window.history.state }));
|
|
2728
|
+
return { path: nextPath };
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
async function listViewTools() {
|
|
2732
|
+
// A nested cross-origin frame can be neither read nor moved from here.
|
|
2733
|
+
if (appFrame) return [];
|
|
2734
|
+
const tools = [
|
|
2735
|
+
{
|
|
2736
|
+
name: readViewToolName,
|
|
2737
|
+
title: "Read open app view",
|
|
2738
|
+
description: "Read the app page open beside the chat: its path, title, selected text and visible text. Call it before acting on what the user is looking at.",
|
|
2739
|
+
inputSchema: { type: "object", properties: {} },
|
|
2740
|
+
annotations: { readOnlyHint: true }
|
|
2741
|
+
},
|
|
2742
|
+
{
|
|
2743
|
+
name: navigateViewToolName,
|
|
2744
|
+
title: "Navigate open app view",
|
|
2745
|
+
description: "Show another page of the open app in place, such as a filtered list or one record. Pass a path in the form read_view returns.",
|
|
2746
|
+
inputSchema: {
|
|
2747
|
+
type: "object",
|
|
2748
|
+
properties: {
|
|
2749
|
+
path: {
|
|
2750
|
+
type: "string",
|
|
2751
|
+
description: "App path starting with /, in the form read_view returns, e.g. a list with a search query."
|
|
2752
|
+
}
|
|
2753
|
+
},
|
|
2754
|
+
required: ["path"]
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
];
|
|
2758
|
+
const helper = appPageToolHelper();
|
|
2759
|
+
if (!helper) return tools;
|
|
2760
|
+
pageToolOrigins.clear();
|
|
2761
|
+
try {
|
|
2762
|
+
const summaries = await helper.tools();
|
|
2763
|
+
for (const summary of (Array.isArray(summaries) ? summaries : []).slice(0, maxPageTools)) {
|
|
2764
|
+
if (!summary || typeof summary.name !== "string") continue;
|
|
2765
|
+
if (tools.some((tool) => tool.name === summary.name)) continue;
|
|
2766
|
+
const full = typeof helper.describe === "function"
|
|
2767
|
+
? await helper.describe(summary.name, summary.origin)
|
|
2768
|
+
: null;
|
|
2769
|
+
const schema = full && full.inputSchema && full.inputSchema.type === "object"
|
|
2770
|
+
? full.inputSchema
|
|
2771
|
+
: { type: "object", properties: {} };
|
|
2772
|
+
tools.push({
|
|
2773
|
+
name: summary.name,
|
|
2774
|
+
...(summary.title ? { title: summary.title } : {}),
|
|
2775
|
+
description: String((full && full.description) || summary.description || summary.name),
|
|
2776
|
+
inputSchema: schema,
|
|
2777
|
+
...(summary.readOnly ? { annotations: { readOnlyHint: true } } : {})
|
|
2778
|
+
});
|
|
2779
|
+
pageToolOrigins.set(summary.name, summary.origin);
|
|
2780
|
+
}
|
|
2781
|
+
} catch (err) {
|
|
2782
|
+
console.warn("[agent-native] page tools unavailable to the MCP host", err);
|
|
2783
|
+
}
|
|
2784
|
+
return tools;
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
async function callViewTool(params) {
|
|
2788
|
+
const record = objectValue(params);
|
|
2789
|
+
const name = typeof record.name === "string" ? record.name : "";
|
|
2790
|
+
const args = objectValue(record.arguments);
|
|
2791
|
+
try {
|
|
2792
|
+
let result;
|
|
2793
|
+
if (name === readViewToolName) {
|
|
2794
|
+
result = readViewState();
|
|
2795
|
+
} else if (name === navigateViewToolName) {
|
|
2796
|
+
result = navigateView(args.path);
|
|
2797
|
+
} else {
|
|
2798
|
+
const helper = appPageToolHelper();
|
|
2799
|
+
if (!helper) throw new Error("Unknown view tool: " + name);
|
|
2800
|
+
const origin = pageToolOrigins.get(name);
|
|
2801
|
+
const outcome = await helper.call(name, args, {
|
|
2802
|
+
waitMs: pageToolWaitMs,
|
|
2803
|
+
...(origin ? { origin } : {})
|
|
2804
|
+
});
|
|
2805
|
+
if (!outcome || outcome.state !== "done") {
|
|
2806
|
+
throw new Error("Page tool " + name + " did not finish.");
|
|
2807
|
+
}
|
|
2808
|
+
if (!outcome.ok) throw new Error(outcome.error || "Page tool " + name + " failed.");
|
|
2809
|
+
result = outcome.result;
|
|
2810
|
+
}
|
|
2811
|
+
const structured = result && typeof result === "object" && !Array.isArray(result)
|
|
2812
|
+
? result
|
|
2813
|
+
: undefined;
|
|
2814
|
+
return {
|
|
2815
|
+
content: [{
|
|
2816
|
+
type: "text",
|
|
2817
|
+
text: typeof result === "string" ? result : JSON.stringify(result === undefined ? null : result)
|
|
2818
|
+
}],
|
|
2819
|
+
...(structured ? { structuredContent: structured } : {})
|
|
2820
|
+
};
|
|
2821
|
+
} catch (err) {
|
|
2822
|
+
return {
|
|
2823
|
+
isError: true,
|
|
2824
|
+
content: [{ type: "text", text: err && err.message ? err.message : String(err) }]
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
function notifyViewToolsChanged() {
|
|
2830
|
+
if (app && typeof app.sendToolListChanged === "function") {
|
|
2831
|
+
try {
|
|
2832
|
+
app.sendToolListChanged();
|
|
2833
|
+
} catch (error) {
|
|
2834
|
+
console.warn("[agent-native] tools/list_changed notification failed", error);
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
// The app registers its WebMCP tools from module scripts that keep running
|
|
2840
|
+
// after the transplant mounts, so the first list_changed can arrive before
|
|
2841
|
+
// they exist. Re-announce when the helper appears and on every toolchange.
|
|
2842
|
+
function watchPageTools() {
|
|
2843
|
+
notifyViewToolsChanged();
|
|
2844
|
+
if (pageToolWatchStarted) return;
|
|
2845
|
+
pageToolWatchStarted = true;
|
|
2846
|
+
let timer = null;
|
|
2847
|
+
const schedule = () => {
|
|
2848
|
+
clearTimeout(timer);
|
|
2849
|
+
timer = setTimeout(notifyViewToolsChanged, 250);
|
|
2850
|
+
};
|
|
2851
|
+
let watchedContext = null;
|
|
2852
|
+
let helperSeen = false;
|
|
2853
|
+
let ticks = 0;
|
|
2854
|
+
const check = () => {
|
|
2855
|
+
const context = document.modelContext;
|
|
2856
|
+
if (context && context !== watchedContext && typeof context.addEventListener === "function") {
|
|
2857
|
+
context.addEventListener("toolchange", schedule);
|
|
2858
|
+
watchedContext = context;
|
|
2859
|
+
}
|
|
2860
|
+
if (!helperSeen && appPageToolHelper()) {
|
|
2861
|
+
helperSeen = true;
|
|
2862
|
+
schedule();
|
|
2863
|
+
}
|
|
2864
|
+
if ((helperSeen && watchedContext) || ++ticks >= 20) clearInterval(poll);
|
|
2865
|
+
};
|
|
2866
|
+
const poll = setInterval(check, 500);
|
|
2867
|
+
check();
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2645
2870
|
function createNativeMcpAppsBridge() {
|
|
2646
2871
|
let rpcId = 0;
|
|
2647
2872
|
let connectPromise = null;
|
|
@@ -2745,7 +2970,10 @@ export function embedApp(options = {}) {
|
|
|
2745
2970
|
"ui/initialize",
|
|
2746
2971
|
{
|
|
2747
2972
|
appInfo: { name: "Agent-Native Embed", version: "1.0.0" },
|
|
2748
|
-
appCapabilities: {
|
|
2973
|
+
appCapabilities: {
|
|
2974
|
+
availableDisplayModes: ["inline", "fullscreen"],
|
|
2975
|
+
tools: { listChanged: true }
|
|
2976
|
+
},
|
|
2749
2977
|
protocolVersion: "2026-01-26"
|
|
2750
2978
|
},
|
|
2751
2979
|
nativeBridgeInitializeTimeoutMs
|
|
@@ -2791,17 +3019,43 @@ export function embedApp(options = {}) {
|
|
|
2791
3019
|
sendSizeChanged(params) {
|
|
2792
3020
|
rpcNotify("ui/notifications/size-changed", objectValue(params));
|
|
2793
3021
|
},
|
|
3022
|
+
sendToolListChanged() {
|
|
3023
|
+
if (connectPromise) rpcNotify("notifications/tools/list_changed", {});
|
|
3024
|
+
},
|
|
2794
3025
|
async sendMessage(params) {
|
|
2795
3026
|
await nativeApp.connect();
|
|
2796
3027
|
return await rpcRequest("ui/message", objectValue(params));
|
|
2797
3028
|
}
|
|
2798
3029
|
};
|
|
2799
3030
|
|
|
3031
|
+
async function answerHostRequest(message) {
|
|
3032
|
+
let reply;
|
|
3033
|
+
try {
|
|
3034
|
+
if (message.method === "tools/list") {
|
|
3035
|
+
reply = { result: { tools: await listViewTools() } };
|
|
3036
|
+
} else if (message.method === "tools/call") {
|
|
3037
|
+
reply = { result: await callViewTool(message.params) };
|
|
3038
|
+
} else if (message.method === "ping" || message.method === "ui/resource-teardown") {
|
|
3039
|
+
reply = { result: {} };
|
|
3040
|
+
} else {
|
|
3041
|
+
reply = { error: { code: -32601, message: "Method not found: " + message.method } };
|
|
3042
|
+
}
|
|
3043
|
+
} catch (err) {
|
|
3044
|
+
reply = { error: { code: -32603, message: err && err.message ? err.message : String(err) } };
|
|
3045
|
+
}
|
|
3046
|
+
window.parent.postMessage({ jsonrpc: "2.0", id: message.id, ...reply }, "*");
|
|
3047
|
+
}
|
|
3048
|
+
|
|
2800
3049
|
function onMessage(event) {
|
|
2801
3050
|
if (event.source !== window.parent) return;
|
|
2802
3051
|
const message = event.data;
|
|
2803
3052
|
if (!message || message.jsonrpc !== "2.0") return;
|
|
2804
|
-
|
|
3053
|
+
const hasId = typeof message.id === "number" || typeof message.id === "string";
|
|
3054
|
+
if (hasId && typeof message.method === "string") {
|
|
3055
|
+
void answerHostRequest(message);
|
|
3056
|
+
return;
|
|
3057
|
+
}
|
|
3058
|
+
if (hasId) {
|
|
2805
3059
|
settleRpcResponse(message);
|
|
2806
3060
|
return;
|
|
2807
3061
|
}
|
|
@@ -2881,9 +3135,11 @@ export function embedApp(options = {}) {
|
|
|
2881
3135
|
const { App } = await import("${MCP_APP_IMPORT}");
|
|
2882
3136
|
app = new App(
|
|
2883
3137
|
{ name: "Agent-Native Embed", version: "1.0.0" },
|
|
2884
|
-
{},
|
|
3138
|
+
{ availableDisplayModes: ["inline", "fullscreen"], tools: { listChanged: true } },
|
|
2885
3139
|
{ autoResize: false }
|
|
2886
3140
|
);
|
|
3141
|
+
app.onlisttools = async () => ({ tools: await listViewTools() });
|
|
3142
|
+
app.oncalltool = async (params) => await callViewTool(params);
|
|
2887
3143
|
appConnectPromise = null;
|
|
2888
3144
|
app.ontoolinput = (params) => {
|
|
2889
3145
|
toolInput = params.arguments || {};
|
package/dist/mcp/oauth-route.js
CHANGED
|
@@ -310,6 +310,13 @@ function protectedResourcePathFromRequest(event) {
|
|
|
310
310
|
pathname = pathname.replace(/\/+$/, "");
|
|
311
311
|
if (!pathname || pathname === "/")
|
|
312
312
|
return undefined;
|
|
313
|
+
// RFC 9728 inserts the whole resource path after the well-known segment, so
|
|
314
|
+
// a mounted app (resource /dispatch/mcp) is asked about /dispatch/mcp.
|
|
315
|
+
const issuer = getMcpOAuthIssuer(event);
|
|
316
|
+
const basePath = issuer ? new URL(issuer).pathname.replace(/\/+$/, "") : "";
|
|
317
|
+
if (basePath && pathname.startsWith(`${basePath}/`)) {
|
|
318
|
+
pathname = pathname.slice(basePath.length);
|
|
319
|
+
}
|
|
313
320
|
return ([
|
|
314
321
|
MCP_PUBLIC_ROUTE_PREFIX,
|
|
315
322
|
MCP_LEGACY_ROUTE_PREFIX,
|
|
@@ -283,20 +283,20 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
283
283
|
type: string;
|
|
284
284
|
};
|
|
285
285
|
startAt: {
|
|
286
|
-
description?: undefined;
|
|
287
286
|
type: string;
|
|
287
|
+
description?: undefined;
|
|
288
288
|
};
|
|
289
289
|
endAt: {
|
|
290
|
-
description?: undefined;
|
|
291
290
|
type: string;
|
|
291
|
+
description?: undefined;
|
|
292
292
|
};
|
|
293
293
|
count: {
|
|
294
294
|
type: string;
|
|
295
295
|
description: string;
|
|
296
296
|
};
|
|
297
297
|
reason: {
|
|
298
|
-
description?: undefined;
|
|
299
298
|
type: string;
|
|
299
|
+
description?: undefined;
|
|
300
300
|
};
|
|
301
301
|
includeMicrophone?: undefined;
|
|
302
302
|
includeSystemAudio?: undefined;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
2
2
|
error?: undefined;
|
|
3
|
-
ok?: undefined;
|
|
4
3
|
count: number;
|
|
5
4
|
updated?: undefined;
|
|
5
|
+
ok?: undefined;
|
|
6
6
|
} | {
|
|
7
7
|
error?: undefined;
|
|
8
|
-
ok?: undefined;
|
|
9
8
|
count?: undefined;
|
|
10
9
|
updated: number;
|
|
11
|
-
} | {
|
|
12
10
|
ok?: undefined;
|
|
11
|
+
} | {
|
|
13
12
|
count?: undefined;
|
|
14
13
|
updated?: undefined;
|
|
15
14
|
error: string;
|
|
15
|
+
ok?: undefined;
|
|
16
16
|
} | {
|
|
17
17
|
error?: undefined;
|
|
18
18
|
count?: undefined;
|
|
@@ -27,10 +27,10 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
27
27
|
thumbsUpRate: number;
|
|
28
28
|
avgEvalScore: number;
|
|
29
29
|
} | {
|
|
30
|
-
summary?: undefined;
|
|
31
30
|
ok?: undefined;
|
|
32
31
|
error: string;
|
|
33
32
|
message: string;
|
|
33
|
+
summary?: undefined;
|
|
34
34
|
spans?: undefined;
|
|
35
35
|
} | {
|
|
36
36
|
error?: undefined;
|
|
@@ -40,30 +40,30 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
40
40
|
spans: import("./types.js").TraceSpan[];
|
|
41
41
|
} | {
|
|
42
42
|
error?: undefined;
|
|
43
|
-
summary?: undefined;
|
|
44
43
|
ok?: undefined;
|
|
45
44
|
message?: undefined;
|
|
45
|
+
summary?: undefined;
|
|
46
46
|
spans?: undefined;
|
|
47
47
|
traceMissing: true;
|
|
48
48
|
id: string;
|
|
49
49
|
} | {
|
|
50
50
|
error?: undefined;
|
|
51
|
-
summary?: undefined;
|
|
52
51
|
ok?: undefined;
|
|
53
52
|
message?: undefined;
|
|
53
|
+
summary?: undefined;
|
|
54
54
|
spans?: undefined;
|
|
55
55
|
traceMissing?: undefined;
|
|
56
56
|
id: string;
|
|
57
57
|
} | {
|
|
58
|
-
summary?: undefined;
|
|
59
58
|
ok?: undefined;
|
|
60
59
|
message?: undefined;
|
|
60
|
+
summary?: undefined;
|
|
61
61
|
spans?: undefined;
|
|
62
62
|
error: any;
|
|
63
63
|
} | {
|
|
64
64
|
error?: undefined;
|
|
65
|
-
summary?: undefined;
|
|
66
65
|
message?: undefined;
|
|
66
|
+
summary?: undefined;
|
|
67
67
|
spans?: undefined;
|
|
68
68
|
ok: boolean;
|
|
69
69
|
}>>;
|
package/dist/org/index.d.ts
CHANGED
|
@@ -180,9 +180,9 @@ export declare const getMyOrgHandler: import("h3").EventHandlerWithFetch<import(
|
|
|
180
180
|
soloDeploymentAdmin: boolean | undefined;
|
|
181
181
|
}>>;
|
|
182
182
|
export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
183
|
-
success: boolean;
|
|
184
183
|
id?: undefined;
|
|
185
184
|
name?: undefined;
|
|
185
|
+
success: boolean;
|
|
186
186
|
role?: undefined;
|
|
187
187
|
} | {
|
|
188
188
|
success?: undefined;
|
package/dist/secrets/api-keys.js
CHANGED
|
@@ -10,8 +10,9 @@ import { readOrgMemberRole } from "../server/personal-provider-key-policy.js";
|
|
|
10
10
|
import { canonicalSecretKey, secretKeyNames } from "./key-aliases.js";
|
|
11
11
|
import { resolveSecretManagedBy } from "./managed-keys.js";
|
|
12
12
|
import { getRequiredSecret, listRequiredSecrets, } from "./register.js";
|
|
13
|
-
import { deleteAppSecret, listAppSecretsForScope,
|
|
13
|
+
import { deleteAppSecret, listAppSecretsForScope, } from "./storage.js";
|
|
14
14
|
import { describeSecretUsage } from "./usage.js";
|
|
15
|
+
import { isVaultManagedSecret } from "./vault-managed.js";
|
|
15
16
|
/**
|
|
16
17
|
* The rows the resolver reads for the caller, in its precedence order:
|
|
17
18
|
* personal, the pre-organization solo row, then the organization's.
|
|
@@ -63,7 +64,7 @@ function toEntry(caller, meta, owner, manages) {
|
|
|
63
64
|
const registered = getRequiredSecret(meta.key);
|
|
64
65
|
const managedBy = resolveSecretManagedBy(meta.key);
|
|
65
66
|
const provider = providerForName(meta.key);
|
|
66
|
-
const vault = isVaultSynced(meta.scope, meta
|
|
67
|
+
const vault = isVaultSynced(meta.scope, meta);
|
|
67
68
|
const mayChange = !managedBy && !vault && (owner === "user" || manages);
|
|
68
69
|
// Replace writes through the routes' own target, so it only applies when
|
|
69
70
|
// that target is this row; otherwise it would add a second row instead.
|
|
@@ -155,9 +156,9 @@ export async function listApiKeys(caller) {
|
|
|
155
156
|
canManageOrg: manages,
|
|
156
157
|
};
|
|
157
158
|
}
|
|
158
|
-
/** Vault
|
|
159
|
-
function isVaultSynced(scope,
|
|
160
|
-
return
|
|
159
|
+
/** The Vault owns the shared rows it writes; a user's own rows never belong to it. */
|
|
160
|
+
function isVaultSynced(scope, meta) {
|
|
161
|
+
return scope !== "user" && isVaultManagedSecret(meta);
|
|
161
162
|
}
|
|
162
163
|
/**
|
|
163
164
|
* Delete one saved key row. A provider key also takes its endpoint and older
|
|
@@ -181,7 +182,7 @@ export async function deleteApiKey(caller, input) {
|
|
|
181
182
|
const row = rows.find((meta) => meta.key === input.name);
|
|
182
183
|
if (!row)
|
|
183
184
|
return { status: "not-found" };
|
|
184
|
-
if (isVaultSynced(storedScope, row
|
|
185
|
+
if (isVaultSynced(storedScope, row))
|
|
185
186
|
return { status: "vault" };
|
|
186
187
|
const provider = AGENT_PROVIDER_CATALOG.find((option) => option.id === providerForName(input.name));
|
|
187
188
|
const names = provider
|
|
@@ -198,7 +199,7 @@ export async function deleteApiKey(caller, input) {
|
|
|
198
199
|
continue;
|
|
199
200
|
// Vault owns its rows, and a managed name is never the user's to delete,
|
|
200
201
|
// even when it sits beside the key being removed.
|
|
201
|
-
if (isVaultSynced(storedScope, meta
|
|
202
|
+
if (isVaultSynced(storedScope, meta))
|
|
202
203
|
continue;
|
|
203
204
|
if (resolveSecretManagedBy(name))
|
|
204
205
|
continue;
|
package/dist/secrets/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { registerRequiredSecret, listRequiredSecrets, getRequiredSecret, registerSecretUsage, getRegisteredSecretUsage, __resetSecretsRegistry, type RegisteredSecret, type SecretUsage, type SecretManagedBy, type SecretScope, type SecretKind, type SecretValidator, type ValidatorResult, } from "./register.js";
|
|
2
|
-
export { writeAppSecret, readAppSecret, hasAppSecret, readAppSecretMeta, deleteAppSecret, getAppSecretMeta, listAppSecretsForScope,
|
|
2
|
+
export { writeAppSecret, readAppSecret, hasAppSecret, readAppSecretMeta, readAppSecretMetaById, deleteAppSecret, deleteAppSecretById, getAppSecretMeta, listAppSecretsForScope, listAppSecretSummaries, updateAppSecretById, AppSecretKeyConflictError, last4, type SecretRef, type WriteSecretArgs, type ReadSecretResult, type SecretMeta, type AppSecretSummary, type UpdateAppSecretPatch, } from "./storage.js";
|
|
3
|
+
export { VAULT_SYNC_DESCRIPTION_PREFIX, isVaultManagedSecret, } from "./vault-managed.js";
|
|
3
4
|
export { APP_SECRETS_CREATE_SQL, appSecrets } from "./schema.js";
|
|
4
5
|
export { encryptSecretValue, decryptSecretValue, isEncryptedSecretValue, } from "./crypto.js";
|
|
5
6
|
export { createListSecretsHandler, createWriteSecretHandler, createTestSecretHandler, createAdHocSecretHandler, type SecretStatusPayload, type AdHocSecretPayload, } from "./routes.js";
|
package/dist/secrets/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { registerRequiredSecret, listRequiredSecrets, getRequiredSecret, registerSecretUsage, getRegisteredSecretUsage, __resetSecretsRegistry, } from "./register.js";
|
|
2
|
-
export { writeAppSecret, readAppSecret, hasAppSecret, readAppSecretMeta, deleteAppSecret, getAppSecretMeta, listAppSecretsForScope,
|
|
2
|
+
export { writeAppSecret, readAppSecret, hasAppSecret, readAppSecretMeta, readAppSecretMetaById, deleteAppSecret, deleteAppSecretById, getAppSecretMeta, listAppSecretsForScope, listAppSecretSummaries, updateAppSecretById, AppSecretKeyConflictError, last4, } from "./storage.js";
|
|
3
|
+
export { VAULT_SYNC_DESCRIPTION_PREFIX, isVaultManagedSecret, } from "./vault-managed.js";
|
|
3
4
|
export { APP_SECRETS_CREATE_SQL, appSecrets } from "./schema.js";
|
|
4
5
|
export { encryptSecretValue, decryptSecretValue, isEncryptedSecretValue, } from "./crypto.js";
|
|
5
6
|
export { createListSecretsHandler, createWriteSecretHandler, createTestSecretHandler, createAdHocSecretHandler, } from "./routes.js";
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -55,14 +55,14 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
55
55
|
errorCode: "secret_managed_elsewhere";
|
|
56
56
|
managedBy: SecretManagedBy;
|
|
57
57
|
} | {
|
|
58
|
-
errorCode?: undefined;
|
|
59
|
-
error: string;
|
|
60
58
|
ok?: undefined;
|
|
59
|
+
error: string;
|
|
60
|
+
errorCode?: undefined;
|
|
61
61
|
status?: undefined;
|
|
62
62
|
} | {
|
|
63
|
+
ok?: undefined;
|
|
63
64
|
error: string;
|
|
64
65
|
errorCode: string;
|
|
65
|
-
ok?: undefined;
|
|
66
66
|
status?: undefined;
|
|
67
67
|
} | {
|
|
68
68
|
error?: undefined;
|
package/dist/secrets/routes.js
CHANGED
|
@@ -51,17 +51,16 @@ import { PERSONAL_PROVIDER_KEYS_RESTRICTED_ERROR_CODE, resolvePersonalProviderKe
|
|
|
51
51
|
import { canonicalSecretKey, secretKeyNames } from "./key-aliases.js";
|
|
52
52
|
import { isManagedDeleteAllowed, managedDeleteRefusal, resolveSecretManagedBy, } from "./managed-keys.js";
|
|
53
53
|
import { listRequiredSecrets, getRequiredSecret, } from "./register.js";
|
|
54
|
-
import { writeAppSecret, updateAppSecretUrlAllowlist, deleteAppSecret, last4, listAppSecretsForScope, readAppSecretMeta,
|
|
54
|
+
import { writeAppSecret, updateAppSecretUrlAllowlist, deleteAppSecret, last4, listAppSecretsForScope, readAppSecretMeta, } from "./storage.js";
|
|
55
55
|
import { registeredUrlAllowlist, validateUrlAllowlist, } from "./substitution.js";
|
|
56
56
|
import { describeSecretUsage } from "./usage.js";
|
|
57
|
+
import { isVaultManagedSecret } from "./vault-managed.js";
|
|
57
58
|
export const SECRET_REJECTED_ERROR_CODE = "secret_rejected";
|
|
58
59
|
export const SECRET_VALIDATION_UNAVAILABLE_ERROR_CODE = "secret_validation_unavailable";
|
|
59
|
-
function secretSource(scope,
|
|
60
|
+
function secretSource(scope, meta) {
|
|
60
61
|
if (scope === "user")
|
|
61
62
|
return "personal";
|
|
62
|
-
return
|
|
63
|
-
? "vault"
|
|
64
|
-
: "workspace";
|
|
63
|
+
return isVaultManagedSecret(meta) ? "vault" : "workspace";
|
|
65
64
|
}
|
|
66
65
|
const NOT_RESOLVED = {
|
|
67
66
|
value: null,
|
|
@@ -216,7 +215,7 @@ export function createListSecretsHandler() {
|
|
|
216
215
|
const meta = await readAppSecretMeta(hit);
|
|
217
216
|
base.last4 = meta?.last4 || last4(effective.value);
|
|
218
217
|
base.updatedAt = meta?.updatedAt;
|
|
219
|
-
base.source = secretSource(hit.scope, meta
|
|
218
|
+
base.source = secretSource(hit.scope, meta);
|
|
220
219
|
base.managedHere = hit.scope === secret.scope && hit.scopeId === scopeId;
|
|
221
220
|
if (base.managedHere && secret.scope === "user") {
|
|
222
221
|
const shared = await asRequestUser(event, () => resolveSecretWithAliasesDetailed(secret.key, {
|
|
@@ -233,7 +232,7 @@ export function createListSecretsHandler() {
|
|
|
233
232
|
scopeId: shared.scopeId,
|
|
234
233
|
})
|
|
235
234
|
: null;
|
|
236
|
-
const sharedSource = secretSource(shared.source, sharedMeta
|
|
235
|
+
const sharedSource = secretSource(shared.source, sharedMeta);
|
|
237
236
|
base.overrides = sharedSource === "vault" ? "vault" : "workspace";
|
|
238
237
|
}
|
|
239
238
|
}
|
|
@@ -513,7 +512,7 @@ function metaToPayload(meta) {
|
|
|
513
512
|
name: meta.key,
|
|
514
513
|
scope: meta.scope,
|
|
515
514
|
scopeId: meta.scopeId,
|
|
516
|
-
source: secretSource(meta.scope, meta
|
|
515
|
+
source: secretSource(meta.scope, meta),
|
|
517
516
|
description: meta.description,
|
|
518
517
|
last4: meta.last4,
|
|
519
518
|
urlAllowlist: meta.urlAllowlist,
|
package/dist/secrets/schema.d.ts
CHANGED
|
@@ -138,6 +138,57 @@ export declare const appSecrets: import("drizzle-orm/pg-core").PgTableWithColumn
|
|
|
138
138
|
identity: undefined;
|
|
139
139
|
generated: undefined;
|
|
140
140
|
}, {}, {}>;
|
|
141
|
+
label: import("drizzle-orm/pg-core").PgColumn<{
|
|
142
|
+
name: "label";
|
|
143
|
+
tableName: "app_secrets";
|
|
144
|
+
dataType: "string";
|
|
145
|
+
columnType: "PgText";
|
|
146
|
+
data: string;
|
|
147
|
+
driverParam: string;
|
|
148
|
+
notNull: false;
|
|
149
|
+
hasDefault: false;
|
|
150
|
+
isPrimaryKey: false;
|
|
151
|
+
isAutoincrement: false;
|
|
152
|
+
hasRuntimeDefault: false;
|
|
153
|
+
enumValues: [string, ...string[]];
|
|
154
|
+
baseColumn: never;
|
|
155
|
+
identity: undefined;
|
|
156
|
+
generated: undefined;
|
|
157
|
+
}, {}, {}>;
|
|
158
|
+
provider: import("drizzle-orm/pg-core").PgColumn<{
|
|
159
|
+
name: "provider";
|
|
160
|
+
tableName: "app_secrets";
|
|
161
|
+
dataType: "string";
|
|
162
|
+
columnType: "PgText";
|
|
163
|
+
data: string;
|
|
164
|
+
driverParam: string;
|
|
165
|
+
notNull: false;
|
|
166
|
+
hasDefault: false;
|
|
167
|
+
isPrimaryKey: false;
|
|
168
|
+
isAutoincrement: false;
|
|
169
|
+
hasRuntimeDefault: false;
|
|
170
|
+
enumValues: [string, ...string[]];
|
|
171
|
+
baseColumn: never;
|
|
172
|
+
identity: undefined;
|
|
173
|
+
generated: undefined;
|
|
174
|
+
}, {}, {}>;
|
|
175
|
+
createdBy: import("drizzle-orm/pg-core").PgColumn<{
|
|
176
|
+
name: "created_by";
|
|
177
|
+
tableName: "app_secrets";
|
|
178
|
+
dataType: "string";
|
|
179
|
+
columnType: "PgText";
|
|
180
|
+
data: string;
|
|
181
|
+
driverParam: string;
|
|
182
|
+
notNull: false;
|
|
183
|
+
hasDefault: false;
|
|
184
|
+
isPrimaryKey: false;
|
|
185
|
+
isAutoincrement: false;
|
|
186
|
+
hasRuntimeDefault: false;
|
|
187
|
+
enumValues: [string, ...string[]];
|
|
188
|
+
baseColumn: never;
|
|
189
|
+
identity: undefined;
|
|
190
|
+
generated: undefined;
|
|
191
|
+
}, {}, {}>;
|
|
141
192
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
142
193
|
name: "created_at";
|
|
143
194
|
tableName: "app_secrets";
|
package/dist/secrets/schema.js
CHANGED
|
@@ -8,6 +8,9 @@ export const appSecrets = table("app_secrets", {
|
|
|
8
8
|
sharedEncryptedValue: text("shared_encrypted_value"),
|
|
9
9
|
description: text("description"),
|
|
10
10
|
urlAllowlist: text("url_allowlist"),
|
|
11
|
+
label: text("label"),
|
|
12
|
+
provider: text("provider"),
|
|
13
|
+
createdBy: text("created_by"),
|
|
11
14
|
createdAt: bigint("created_at", { mode: "number" }).notNull(),
|
|
12
15
|
updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
|
|
13
16
|
});
|