@agent-native/core 0.165.2 → 0.165.4
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/CHANGELOG.md +740 -2
- package/corpus/templates/analytics/changelog/2026-08-20-staged-data-limit-recovery.md +1 -0
- package/corpus/templates/assets/CHANGELOG.md +204 -2
- package/corpus/templates/brain/CHANGELOG.md +145 -2
- package/corpus/templates/calendar/CHANGELOG.md +272 -2
- package/corpus/templates/chat/CHANGELOG.md +89 -2
- package/corpus/templates/clips/CHANGELOG.md +864 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +6 -2
- package/corpus/templates/clips/app/routes/download.tsx +111 -17
- package/corpus/templates/clips/changelog/2026-08-20-cross-tab-recording-controls.md +1 -0
- package/corpus/templates/clips/changelog/2026-08-20-overlay-follow-review-fixes.md +1 -0
- package/corpus/templates/clips/changelog/2026-08-20-preparing-recording-countdown-overlay.md +5 -0
- package/corpus/templates/clips/changelog/2026-08-20-provider-key-recovery.md +1 -0
- package/corpus/templates/clips/desktop/src/overlays/preparing.tsx +4 -6
- package/corpus/templates/clips/desktop/src/styles.css +18 -20
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +9 -17
- package/corpus/templates/clips/server/routes/api/clips-latest.json.get.ts +109 -46
- package/corpus/templates/content/CHANGELOG.md +503 -2
- package/corpus/templates/crm/CHANGELOG.md +31 -2
- package/corpus/templates/design/CHANGELOG.md +870 -2
- package/corpus/templates/dispatch/CHANGELOG.md +196 -2
- package/corpus/templates/factory/CHANGELOG.md +121 -2
- package/corpus/templates/forms/CHANGELOG.md +166 -2
- package/corpus/templates/macros/CHANGELOG.md +49 -2
- package/corpus/templates/mail/CHANGELOG.md +201 -2
- package/corpus/templates/plan/CHANGELOG.md +277 -2
- package/corpus/templates/slides/CHANGELOG.md +437 -2
- package/corpus/templates/tasks/CHANGELOG.md +32 -2
- package/dist/changelog/parse.d.ts +3 -3
- package/dist/changelog/parse.js +35 -16
- package/dist/client/blocks/library/diagram.js +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/workspace-deploy.js +7 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp-client/oauth-routes.d.ts +1 -0
- package/dist/mcp-client/oauth-routes.js +15 -2
- package/dist/mcp-client/remote-store.js +83 -43
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/google-oauth-credentials.js +14 -1
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.js +1 -1
- package/dist/settings/store.d.ts +7 -1
- package/dist/settings/store.js +25 -3
- package/dist/settings/user-settings.d.ts +2 -0
- package/dist/settings/user-settings.js +65 -2
- package/dist/templates/chat/CHANGELOG.md +89 -2
- package/docs/content/deployment.mdx +89 -14
- package/package.json +1 -1
- package/src/templates/chat/CHANGELOG.md +89 -2
|
@@ -27,6 +27,7 @@ import { assertEmittedBackgroundFunctionOnDisk, isRecurringJobsDeployEnabled, }
|
|
|
27
27
|
import { cloneServerBundleForFunction } from "./function-bundle.js";
|
|
28
28
|
import { collectImmutableAssetPaths, IMMUTABLE_ASSET_CACHE_HEADERS, } from "./immutable-assets.js";
|
|
29
29
|
const NETLIFY_WORKSPACE_STATIC_DIR = "_workspace_static";
|
|
30
|
+
const DEFAULT_HOSTED_FEEDBACK_URL = "https://forms.agent-native.com/f/agent-native-feedback/_16ewV";
|
|
30
31
|
const NETLIFY_PUBLIC_ASSET_EXTENSIONS = new Set([
|
|
31
32
|
"avif",
|
|
32
33
|
"css",
|
|
@@ -152,6 +153,12 @@ function buildOneApp(workspaceRoot, app, preset, execFile, workspaceApps) {
|
|
|
152
153
|
AGENT_NATIVE_WORKSPACE_APP_ID: app,
|
|
153
154
|
VITE_AGENT_NATIVE_WORKSPACE: "1",
|
|
154
155
|
VITE_AGENT_NATIVE_WORKSPACE_APP_ID: app,
|
|
156
|
+
...(preset === "netlify"
|
|
157
|
+
? {
|
|
158
|
+
VITE_AGENT_NATIVE_FEEDBACK_URL: process.env.VITE_AGENT_NATIVE_FEEDBACK_URL ??
|
|
159
|
+
DEFAULT_HOSTED_FEEDBACK_URL,
|
|
160
|
+
}
|
|
161
|
+
: {}),
|
|
155
162
|
APP_BASE_PATH: `/${app}`,
|
|
156
163
|
VITE_APP_BASE_PATH: `/${app}`,
|
|
157
164
|
AGENT_NATIVE_WORKSPACE_APP_AUDIENCE: workspaceAppAudience,
|
|
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
137
137
|
inputSchema: {
|
|
138
138
|
type: string;
|
|
139
139
|
properties: {
|
|
140
|
+
count?: undefined;
|
|
140
141
|
query?: undefined;
|
|
141
142
|
minutes?: undefined;
|
|
142
143
|
limit?: undefined;
|
|
143
144
|
clientHint?: undefined;
|
|
144
145
|
timestamp?: undefined;
|
|
145
146
|
chapterId?: undefined;
|
|
146
|
-
count?: undefined;
|
|
147
147
|
startAt?: undefined;
|
|
148
148
|
endAt?: undefined;
|
|
149
149
|
reason?: undefined;
|
|
@@ -159,6 +159,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
159
159
|
inputSchema: {
|
|
160
160
|
type: string;
|
|
161
161
|
properties: {
|
|
162
|
+
count?: undefined;
|
|
162
163
|
query: {
|
|
163
164
|
type: string;
|
|
164
165
|
description: string;
|
|
@@ -174,7 +175,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
174
175
|
clientHint?: undefined;
|
|
175
176
|
timestamp?: undefined;
|
|
176
177
|
chapterId?: undefined;
|
|
177
|
-
count?: undefined;
|
|
178
178
|
startAt?: undefined;
|
|
179
179
|
endAt?: undefined;
|
|
180
180
|
reason?: undefined;
|
|
@@ -190,6 +190,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
190
190
|
inputSchema: {
|
|
191
191
|
type: string;
|
|
192
192
|
properties: {
|
|
193
|
+
count?: undefined;
|
|
193
194
|
minutes: {
|
|
194
195
|
type: string;
|
|
195
196
|
description: string;
|
|
@@ -199,7 +200,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
199
200
|
clientHint?: undefined;
|
|
200
201
|
timestamp?: undefined;
|
|
201
202
|
chapterId?: undefined;
|
|
202
|
-
count?: undefined;
|
|
203
203
|
startAt?: undefined;
|
|
204
204
|
endAt?: undefined;
|
|
205
205
|
reason?: undefined;
|
|
@@ -216,6 +216,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
216
216
|
type: string;
|
|
217
217
|
required: string[];
|
|
218
218
|
properties: {
|
|
219
|
+
count?: undefined;
|
|
219
220
|
query: {
|
|
220
221
|
type: string;
|
|
221
222
|
description: string;
|
|
@@ -234,7 +235,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
234
235
|
};
|
|
235
236
|
timestamp?: undefined;
|
|
236
237
|
chapterId?: undefined;
|
|
237
|
-
count?: undefined;
|
|
238
238
|
startAt?: undefined;
|
|
239
239
|
endAt?: undefined;
|
|
240
240
|
reason?: undefined;
|
|
@@ -250,6 +250,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
250
250
|
type: string;
|
|
251
251
|
required: string[];
|
|
252
252
|
properties: {
|
|
253
|
+
count?: undefined;
|
|
253
254
|
query?: undefined;
|
|
254
255
|
minutes?: undefined;
|
|
255
256
|
limit?: undefined;
|
|
@@ -263,7 +264,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
263
264
|
description: string;
|
|
264
265
|
};
|
|
265
266
|
chapterId?: undefined;
|
|
266
|
-
count?: undefined;
|
|
267
267
|
startAt?: undefined;
|
|
268
268
|
endAt?: undefined;
|
|
269
269
|
includeMicrophone?: undefined;
|
|
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
314
314
|
type: string;
|
|
315
315
|
required: string[];
|
|
316
316
|
properties: {
|
|
317
|
+
count?: undefined;
|
|
317
318
|
query?: undefined;
|
|
318
319
|
minutes?: undefined;
|
|
319
320
|
limit?: undefined;
|
|
320
321
|
clientHint?: undefined;
|
|
321
322
|
timestamp?: undefined;
|
|
322
323
|
chapterId?: undefined;
|
|
323
|
-
count?: undefined;
|
|
324
324
|
startAt: {
|
|
325
325
|
type: string;
|
|
326
326
|
description: string;
|
|
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
349
349
|
type: string;
|
|
350
350
|
required: string[];
|
|
351
351
|
properties: {
|
|
352
|
+
count?: undefined;
|
|
352
353
|
query?: undefined;
|
|
353
354
|
minutes?: undefined;
|
|
354
355
|
limit?: undefined;
|
|
355
356
|
clientHint?: undefined;
|
|
356
357
|
timestamp?: undefined;
|
|
357
358
|
chapterId?: undefined;
|
|
358
|
-
count?: undefined;
|
|
359
359
|
startAt?: undefined;
|
|
360
360
|
endAt?: undefined;
|
|
361
361
|
reason?: undefined;
|
|
@@ -25,6 +25,7 @@ export interface McpOAuthRoutesOptions {
|
|
|
25
25
|
export declare function redirectWithStagedCookies(event: H3Event, location: string): Response;
|
|
26
26
|
export declare function mountMcpOAuthRoutes(nitroApp: any, options: McpOAuthRoutesOptions): void;
|
|
27
27
|
export declare function resolveMcpOAuthScope(serverUrl: URL, requestedScope: unknown): RemoteMcpScope | null;
|
|
28
|
+
export declare function stripMcpOAuthAppBasePath(path: string, basePath: string): string;
|
|
28
29
|
export declare function resolveManagedMcpOAuthClient(serverUrl: URL): Promise<StoredOAuthClientInformation | undefined>;
|
|
29
30
|
export declare function setMcpOAuthFlowCookie(event: H3Event, flow: McpOAuthFlow, secure: boolean): void;
|
|
30
31
|
export declare function readMcpOAuthFlowCookie(event: H3Event): McpOAuthFlow | null;
|
|
@@ -5,7 +5,7 @@ import { decryptSecretValue, encryptSecretValue } from "../secrets/crypto.js";
|
|
|
5
5
|
import { getSession, safeReturnPath } from "../server/auth.js";
|
|
6
6
|
import { resolveSecret } from "../server/credential-provider.js";
|
|
7
7
|
import { getH3App } from "../server/framework-request-handler.js";
|
|
8
|
-
import { getAppUrl, resolveOAuthRedirectUri } from "../server/google-oauth.js";
|
|
8
|
+
import { getAppBasePath, getAppUrl, resolveOAuthRedirectUri, } from "../server/google-oauth.js";
|
|
9
9
|
import { runWithRequestContext } from "../server/request-context.js";
|
|
10
10
|
import { finishMcpOAuthAuthorization, startMcpOAuthAuthorization, validateMcpOAuthCallbackIssuer, } from "./oauth-client.js";
|
|
11
11
|
import { addOAuthRemoteServer, listRemoteServers, normalizeServerName, replaceOAuthRemoteServer, validateRemoteUrl, } from "./remote-store.js";
|
|
@@ -206,6 +206,19 @@ export function resolveMcpOAuthScope(serverUrl, requestedScope) {
|
|
|
206
206
|
}
|
|
207
207
|
return requestedScope === "org" ? "org" : "user";
|
|
208
208
|
}
|
|
209
|
+
export function stripMcpOAuthAppBasePath(path, basePath) {
|
|
210
|
+
const normalizedBase = `/${basePath.replace(/^\/+|\/+$/g, "")}`;
|
|
211
|
+
if (normalizedBase === "/")
|
|
212
|
+
return path;
|
|
213
|
+
const suffixStart = path.search(/[?#]/);
|
|
214
|
+
const pathname = suffixStart === -1 ? path : path.slice(0, suffixStart);
|
|
215
|
+
const suffix = suffixStart === -1 ? "" : path.slice(suffixStart);
|
|
216
|
+
if (pathname === normalizedBase)
|
|
217
|
+
return `/${suffix}`;
|
|
218
|
+
return pathname.startsWith(`${normalizedBase}/`)
|
|
219
|
+
? `${pathname.slice(normalizedBase.length)}${suffix}`
|
|
220
|
+
: path;
|
|
221
|
+
}
|
|
209
222
|
export async function resolveManagedMcpOAuthClient(serverUrl) {
|
|
210
223
|
const client = MANAGED_MCP_OAUTH_CLIENTS.find((candidate) => candidate.serverOrigin === serverUrl.origin);
|
|
211
224
|
if (!client)
|
|
@@ -286,7 +299,7 @@ async function handleMcpOAuthCallback(event, options) {
|
|
|
286
299
|
await options.reconfigure();
|
|
287
300
|
const returnPath = flow.returnUrl ??
|
|
288
301
|
`/settings/integrations?connected=mcp-${encodeURIComponent(flow.name)}`;
|
|
289
|
-
return redirectWithStagedCookies(event, getAppUrl(event, returnPath));
|
|
302
|
+
return redirectWithStagedCookies(event, getAppUrl(event, stripMcpOAuthAppBasePath(returnPath, getAppBasePath())));
|
|
290
303
|
}
|
|
291
304
|
catch {
|
|
292
305
|
setResponseStatus(event, 400);
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { createHash } from "node:crypto";
|
|
23
23
|
import { writeAppSecret, readAppSecret, deleteAppSecret, } from "../secrets/storage.js";
|
|
24
|
-
import { getOrgSetting,
|
|
25
|
-
import { getUserSetting,
|
|
24
|
+
import { getOrgSetting, mutateOrgSetting } from "../settings/org-settings.js";
|
|
25
|
+
import { getUserSetting, mutateUserSetting, } from "../settings/user-settings.js";
|
|
26
26
|
import { getMcpOAuthAccessToken, revokeMcpOAuthCredentials, saveMcpOAuthCredentials, } from "./oauth-client.js";
|
|
27
27
|
import { validateRemoteUrl } from "./remote-url.js";
|
|
28
28
|
export { validateRemoteUrl } from "./remote-url.js";
|
|
@@ -107,20 +107,21 @@ function sanitiseOrgId(orgId) {
|
|
|
107
107
|
return orgId.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
108
108
|
}
|
|
109
109
|
async function readList(scope, scopeId) {
|
|
110
|
-
|
|
110
|
+
return parseServerList(scope === "user"
|
|
111
111
|
? await getUserSetting(scopeId, SETTINGS_KEY)
|
|
112
|
-
: await getOrgSetting(scopeId, SETTINGS_KEY);
|
|
112
|
+
: await getOrgSetting(scopeId, SETTINGS_KEY));
|
|
113
|
+
}
|
|
114
|
+
function parseServerList(raw) {
|
|
113
115
|
if (!raw || !Array.isArray(raw.servers))
|
|
114
116
|
return [];
|
|
115
117
|
return raw.servers.filter((s) => s && typeof s.id === "string" && typeof s.url === "string");
|
|
116
118
|
}
|
|
117
|
-
async function
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
119
|
+
async function mutateServerList(scope, scopeId, updater) {
|
|
120
|
+
const mutate = scope === "user" ? mutateUserSetting : mutateOrgSetting;
|
|
121
|
+
const next = await mutate(scopeId, SETTINGS_KEY, async (raw) => ({
|
|
122
|
+
servers: await updater(parseServerList(raw)),
|
|
123
|
+
}));
|
|
124
|
+
return parseServerList(next);
|
|
124
125
|
}
|
|
125
126
|
export async function listRemoteServers(scope, scopeId) {
|
|
126
127
|
return readList(scope, scopeId);
|
|
@@ -228,6 +229,7 @@ export async function replaceOAuthRemoteServer(scope, scopeId, serverId, credent
|
|
|
228
229
|
...credentials,
|
|
229
230
|
serverUrl: credentialResource.url.toString(),
|
|
230
231
|
};
|
|
232
|
+
let updated;
|
|
231
233
|
try {
|
|
232
234
|
await saveMcpOAuthCredentials({
|
|
233
235
|
key: nextSecretKey,
|
|
@@ -235,21 +237,22 @@ export async function replaceOAuthRemoteServer(scope, scopeId, serverId, credent
|
|
|
235
237
|
scopeId,
|
|
236
238
|
credentials: nextCredentials,
|
|
237
239
|
});
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
240
|
+
updated = await mutateServerList(scope, scopeId, (servers) => {
|
|
241
|
+
const latestIndex = servers.findIndex((server) => server.id === serverId);
|
|
242
|
+
const latest = latestIndex >= 0 ? servers[latestIndex] : undefined;
|
|
243
|
+
if (!latest ||
|
|
244
|
+
latest.oauthSecretKey !== current.oauthSecretKey ||
|
|
245
|
+
latest.url !== current.url) {
|
|
246
|
+
throw new Error("MCP server changed while reconnecting");
|
|
247
|
+
}
|
|
248
|
+
const next = [...servers];
|
|
249
|
+
next[latestIndex] = {
|
|
250
|
+
...latest,
|
|
251
|
+
url: nextCredentials.serverUrl,
|
|
252
|
+
oauthSecretKey: nextSecretKey,
|
|
253
|
+
};
|
|
254
|
+
return next;
|
|
255
|
+
});
|
|
253
256
|
}
|
|
254
257
|
catch (err) {
|
|
255
258
|
await revokeMcpOAuthCredentials({
|
|
@@ -263,6 +266,18 @@ export async function replaceOAuthRemoteServer(scope, scopeId, serverId, credent
|
|
|
263
266
|
error: `Failed to replace MCP OAuth credentials: ${err?.message ?? err}`,
|
|
264
267
|
};
|
|
265
268
|
}
|
|
269
|
+
// The settings row now points at the replacement grant. Old-grant cleanup
|
|
270
|
+
// is best effort and must not roll back or invalidate the committed row.
|
|
271
|
+
await revokeMcpOAuthCredentials({
|
|
272
|
+
key: current.oauthSecretKey,
|
|
273
|
+
scope,
|
|
274
|
+
scopeId,
|
|
275
|
+
serverUrl: current.url,
|
|
276
|
+
}).catch(() => undefined);
|
|
277
|
+
const server = updated.find((candidate) => candidate.id === serverId);
|
|
278
|
+
return server
|
|
279
|
+
? { ok: true, server }
|
|
280
|
+
: { ok: false, error: "MCP server was not found" };
|
|
266
281
|
}
|
|
267
282
|
export async function addFirstPartyRemoteServer(orgId, input) {
|
|
268
283
|
const trust = await isFirstPartyRemoteEndpointTrusted(orgId, input.appId, input.url);
|
|
@@ -327,7 +342,24 @@ async function addRemoteServerInternal(scope, scopeId, input) {
|
|
|
327
342
|
description: input.description?.trim() || undefined,
|
|
328
343
|
createdAt: Date.now(),
|
|
329
344
|
};
|
|
330
|
-
|
|
345
|
+
try {
|
|
346
|
+
await mutateServerList(scope, scopeId, (servers) => {
|
|
347
|
+
if (servers.some((candidate) => candidate.name === name)) {
|
|
348
|
+
throw new Error(`A server named "${name}" already exists`);
|
|
349
|
+
}
|
|
350
|
+
return [...servers, server];
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
catch (err) {
|
|
354
|
+
if (headerSecretKey) {
|
|
355
|
+
await deleteAppSecret({
|
|
356
|
+
key: headerSecretKey,
|
|
357
|
+
scope: toSecretScope(scope),
|
|
358
|
+
scopeId,
|
|
359
|
+
}).catch(() => undefined);
|
|
360
|
+
}
|
|
361
|
+
return { ok: false, error: err?.message ?? String(err) };
|
|
362
|
+
}
|
|
331
363
|
return { ok: true, server };
|
|
332
364
|
}
|
|
333
365
|
function normalizeEndpointPath(pathname) {
|
|
@@ -396,9 +428,29 @@ export async function isFirstPartyRemoteEndpointTrusted(orgId, appId, endpoint)
|
|
|
396
428
|
}
|
|
397
429
|
export async function removeRemoteServer(scope, scopeId, id) {
|
|
398
430
|
const existing = await readList(scope, scopeId);
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
431
|
+
const expected = existing.find((s) => s.id === id);
|
|
432
|
+
if (!expected)
|
|
433
|
+
return false;
|
|
434
|
+
let removed;
|
|
435
|
+
try {
|
|
436
|
+
await mutateServerList(scope, scopeId, (servers) => {
|
|
437
|
+
removed = undefined;
|
|
438
|
+
const latest = servers.find((server) => server.id === id);
|
|
439
|
+
if (!latest)
|
|
440
|
+
return servers;
|
|
441
|
+
if (latest.oauthSecretKey !== expected.oauthSecretKey) {
|
|
442
|
+
throw new Error(`MCP OAuth credentials changed while removing ${expected.name}`);
|
|
443
|
+
}
|
|
444
|
+
removed = latest;
|
|
445
|
+
return servers.filter((server) => server.id !== id);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
catch (err) {
|
|
449
|
+
// eslint-disable-next-line no-console
|
|
450
|
+
console.warn(`[mcp-client] Failed to remove MCP server ${expected.name}: ${err?.message ?? err}`);
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
if (!removed)
|
|
402
454
|
return false;
|
|
403
455
|
if (removed?.oauthSecretKey) {
|
|
404
456
|
try {
|
|
@@ -409,8 +461,7 @@ export async function removeRemoteServer(scope, scopeId, id) {
|
|
|
409
461
|
serverUrl: removed.url,
|
|
410
462
|
});
|
|
411
463
|
if (result.local === "replaced") {
|
|
412
|
-
console.warn(`[mcp-client] MCP OAuth credentials changed while removing ${removed.name}; the server was
|
|
413
|
-
return false;
|
|
464
|
+
console.warn(`[mcp-client] MCP OAuth credentials changed while removing ${removed.name}; the server row was already removed.`);
|
|
414
465
|
}
|
|
415
466
|
if (result.remote === "failed") {
|
|
416
467
|
console.warn(`[mcp-client] MCP OAuth revocation failed for ${removed.name}; local credentials were removed.`);
|
|
@@ -421,17 +472,6 @@ export async function removeRemoteServer(scope, scopeId, id) {
|
|
|
421
472
|
console.warn(`[mcp-client] Failed to delete MCP OAuth credentials ${removed.oauthSecretKey}: ${err?.message ?? err}`);
|
|
422
473
|
}
|
|
423
474
|
}
|
|
424
|
-
if (next.length === 0) {
|
|
425
|
-
if (scope === "user") {
|
|
426
|
-
await deleteUserSetting(scopeId, SETTINGS_KEY);
|
|
427
|
-
}
|
|
428
|
-
else {
|
|
429
|
-
await deleteOrgSetting(scopeId, SETTINGS_KEY);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
await writeList(scope, scopeId, next);
|
|
434
|
-
}
|
|
435
475
|
// Best-effort: drop the encrypted-headers secret too. Errors are logged
|
|
436
476
|
// but don't fail the deletion — the settings row is already gone, so a
|
|
437
477
|
// dangling secret is harmless (it just can't be read back).
|
|
@@ -13,21 +13,21 @@
|
|
|
13
13
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
14
14
|
count: number;
|
|
15
15
|
updated?: undefined;
|
|
16
|
-
ok?: undefined;
|
|
17
16
|
error?: undefined;
|
|
17
|
+
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
|
+
count?: undefined;
|
|
19
20
|
updated: number;
|
|
20
|
-
ok?: undefined;
|
|
21
21
|
error?: undefined;
|
|
22
|
-
|
|
22
|
+
ok?: undefined;
|
|
23
23
|
} | {
|
|
24
|
+
count?: undefined;
|
|
24
25
|
updated?: undefined;
|
|
25
26
|
error: string;
|
|
26
27
|
ok?: undefined;
|
|
27
|
-
count?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
count?: undefined;
|
|
29
30
|
updated?: undefined;
|
|
30
|
-
ok: boolean;
|
|
31
31
|
error?: undefined;
|
|
32
|
-
|
|
32
|
+
ok: boolean;
|
|
33
33
|
}>>;
|
|
@@ -51,8 +51,8 @@ export declare function handleDeleteResource(event: any): Promise<{
|
|
|
51
51
|
error: string;
|
|
52
52
|
ok?: undefined;
|
|
53
53
|
} | {
|
|
54
|
-
error?: undefined;
|
|
55
54
|
ok: boolean;
|
|
55
|
+
error?: undefined;
|
|
56
56
|
}>;
|
|
57
57
|
/** POST /_agent-native/resources/upload — upload a file as a resource */
|
|
58
58
|
export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
|
|
@@ -73,10 +73,10 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
|
|
|
73
73
|
runId: string | null;
|
|
74
74
|
expiresAt: number | null;
|
|
75
75
|
metadata: string | null;
|
|
76
|
-
error?: undefined;
|
|
77
76
|
url: string;
|
|
78
77
|
provider: string;
|
|
79
78
|
storageSetupRequired?: undefined;
|
|
79
|
+
error?: undefined;
|
|
80
80
|
} | {
|
|
81
81
|
error: string;
|
|
82
82
|
storageSetupRequired: boolean;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
status?: undefined;
|
|
38
37
|
ok?: undefined;
|
|
38
|
+
status?: undefined;
|
|
39
39
|
} | {
|
|
40
|
+
error?: undefined;
|
|
40
41
|
ok: boolean;
|
|
41
42
|
status: string;
|
|
42
|
-
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
+
ok?: undefined;
|
|
44
45
|
error: string;
|
|
45
46
|
removed?: undefined;
|
|
46
|
-
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
+
error?: undefined;
|
|
48
49
|
ok: boolean;
|
|
49
50
|
removed: boolean;
|
|
50
|
-
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
+
ok?: undefined;
|
|
57
58
|
error: string;
|
|
58
59
|
note?: undefined;
|
|
59
|
-
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
|
+
error?: undefined;
|
|
61
62
|
ok: boolean;
|
|
62
63
|
note?: undefined;
|
|
63
|
-
error?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
ok: boolean;
|
|
66
67
|
note: string;
|
|
67
|
-
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
+
error?: undefined;
|
|
98
99
|
ok: boolean;
|
|
99
100
|
key: string;
|
|
100
|
-
error?: undefined;
|
|
101
101
|
} | {
|
|
102
|
+
error?: undefined;
|
|
102
103
|
ok: boolean;
|
|
103
104
|
removed: boolean;
|
|
104
|
-
error?: undefined;
|
|
105
105
|
}>>;
|
|
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
+
error?: undefined;
|
|
30
31
|
ok: boolean;
|
|
31
32
|
key: string;
|
|
32
33
|
baseUrlKey?: string;
|
|
33
34
|
scope: AgentEngineApiKeyScope;
|
|
34
|
-
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
@@ -57,7 +57,20 @@ export async function resolveGoogleProviderCredentialCandidatesWithReader(option
|
|
|
57
57
|
* remain the backwards-compatible provider OAuth credentials.
|
|
58
58
|
*/
|
|
59
59
|
export function resolveGoogleSignInCredentials() {
|
|
60
|
-
|
|
60
|
+
const signIn = readCredentialPair("GOOGLE_SIGN_IN_CLIENT_ID", "GOOGLE_SIGN_IN_CLIENT_SECRET");
|
|
61
|
+
const provider = readCredentialPair("GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET");
|
|
62
|
+
// Both pairs configured for different clients means one of them is dead
|
|
63
|
+
// config that nothing will ever read. Silence here cost a fleet-wide beta
|
|
64
|
+
// sign-in outage: GOOGLE_CLIENT_SECRET was repaired and verified while the
|
|
65
|
+
// callback kept using a stale GOOGLE_SIGN_IN_CLIENT_SECRET, so the repaired
|
|
66
|
+
// variable looked correct and changed nothing. Name the loser explicitly.
|
|
67
|
+
if (signIn && provider && signIn.clientId !== provider.clientId) {
|
|
68
|
+
console.warn("[agent-native][google-oauth] GOOGLE_SIGN_IN_CLIENT_ID and GOOGLE_CLIENT_ID " +
|
|
69
|
+
"are set to different Google clients. Sign-in uses GOOGLE_SIGN_IN_CLIENT_ID; " +
|
|
70
|
+
"GOOGLE_CLIENT_ID/SECRET are ignored for sign-in. Editing them will not " +
|
|
71
|
+
"change sign-in behaviour.");
|
|
72
|
+
}
|
|
73
|
+
return signIn ?? provider;
|
|
61
74
|
}
|
|
62
75
|
export function hasGoogleSignInCredentials() {
|
|
63
76
|
return resolveGoogleSignInCredentials() !== null;
|
package/dist/settings/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { getSetting, mutateSetting, putSetting, deleteSetting, getAllSettings, getSettingsEmitter, type StoreWriteOptions, } from "./store.js";
|
|
1
|
+
export { getSetting, mutateSetting, putSetting, deleteSetting, deleteSettingIfValue, getAllSettings, getSettingsEmitter, type StoreWriteOptions, } from "./store.js";
|
|
2
2
|
export { getSettingHandler, putSettingHandler, deleteSettingHandler, } from "./handlers.js";
|
|
3
3
|
export { readSetting, writeSetting, removeSetting } from "./script-helpers.js";
|
|
4
4
|
export { getUserSetting, putUserSetting, deleteUserSetting, } from "./user-settings.js";
|
package/dist/settings/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Store
|
|
2
|
-
export { getSetting, mutateSetting, putSetting, deleteSetting, getAllSettings, getSettingsEmitter, } from "./store.js";
|
|
2
|
+
export { getSetting, mutateSetting, putSetting, deleteSetting, deleteSettingIfValue, getAllSettings, getSettingsEmitter, } from "./store.js";
|
|
3
3
|
// H3 route handlers
|
|
4
4
|
export { getSettingHandler, putSettingHandler, deleteSettingHandler, } from "./handlers.js";
|
|
5
5
|
// Script helpers
|
package/dist/settings/store.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { EventEmitter } from "node:events";
|
|
2
2
|
export declare function getSettingsEmitter(): EventEmitter;
|
|
3
3
|
export declare function ensureTable(): Promise<void>;
|
|
4
|
-
export
|
|
4
|
+
export interface StoreReadOptions {
|
|
5
|
+
/** Skip the per-request snapshot when a cross-request race must be checked. */
|
|
6
|
+
bypassCache?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function getSetting(key: string, options?: StoreReadOptions): Promise<Record<string, unknown> | null>;
|
|
5
9
|
export interface StoreWriteOptions {
|
|
6
10
|
/** Tag identifying who initiated this write (e.g. a tab ID). */
|
|
7
11
|
requestSource?: string;
|
|
@@ -16,6 +20,8 @@ export interface StoreWriteOptions {
|
|
|
16
20
|
export declare function mutateSetting(key: string, updater: (current: Record<string, unknown> | null) => Record<string, unknown> | Promise<Record<string, unknown>>, options?: StoreWriteOptions): Promise<Record<string, unknown>>;
|
|
17
21
|
export declare function putSetting(key: string, value: Record<string, unknown>, options?: StoreWriteOptions): Promise<void>;
|
|
18
22
|
export declare function deleteSetting(key: string, options?: StoreWriteOptions): Promise<boolean>;
|
|
23
|
+
/** Delete a setting only when its stored value still matches the inspected value. */
|
|
24
|
+
export declare function deleteSettingIfValue(key: string, expected: Record<string, unknown>, options?: StoreWriteOptions): Promise<boolean>;
|
|
19
25
|
/**
|
|
20
26
|
* Delete every setting whose key starts with `prefix`. Returns the number of
|
|
21
27
|
* rows removed. Used when an owning entity is deleted (e.g. an organization
|
package/dist/settings/store.js
CHANGED
|
@@ -115,9 +115,9 @@ export async function ensureTable() {
|
|
|
115
115
|
}
|
|
116
116
|
return _initPromise;
|
|
117
117
|
}
|
|
118
|
-
export async function getSetting(key) {
|
|
118
|
+
export async function getSetting(key, options) {
|
|
119
119
|
const cache = requestSettingsCache();
|
|
120
|
-
if (cache?.has(key)) {
|
|
120
|
+
if (!options?.bypassCache && cache?.has(key)) {
|
|
121
121
|
const cached = cache.get(key);
|
|
122
122
|
return cached == null ? null : JSON.parse(cached);
|
|
123
123
|
}
|
|
@@ -129,7 +129,8 @@ export async function getSetting(key) {
|
|
|
129
129
|
args: [key],
|
|
130
130
|
});
|
|
131
131
|
const raw = rows.length === 0 ? null : rows[0].value;
|
|
132
|
-
|
|
132
|
+
if (!options?.bypassCache)
|
|
133
|
+
cache?.set(key, raw);
|
|
133
134
|
return raw == null ? null : JSON.parse(raw);
|
|
134
135
|
}
|
|
135
136
|
const SETTINGS_MUTATION_ATTEMPTS = 25;
|
|
@@ -221,6 +222,27 @@ export async function deleteSetting(key, options) {
|
|
|
221
222
|
}
|
|
222
223
|
return false;
|
|
223
224
|
}
|
|
225
|
+
/** Delete a setting only when its stored value still matches the inspected value. */
|
|
226
|
+
export async function deleteSettingIfValue(key, expected, options) {
|
|
227
|
+
await ensureTable();
|
|
228
|
+
const client = getDbExec();
|
|
229
|
+
const table = settingsTable();
|
|
230
|
+
const result = await client.execute({
|
|
231
|
+
sql: `DELETE FROM ${table} WHERE key = ? AND value = ?`,
|
|
232
|
+
args: [key, JSON.stringify(expected)],
|
|
233
|
+
});
|
|
234
|
+
if (result.rowsAffected === 0)
|
|
235
|
+
return false;
|
|
236
|
+
requestSettingsCache()?.set(key, null);
|
|
237
|
+
invalidateRequestAllSettings();
|
|
238
|
+
settingsEmitter().emit("settings", {
|
|
239
|
+
source: "settings",
|
|
240
|
+
type: "delete",
|
|
241
|
+
key,
|
|
242
|
+
...(options?.requestSource && { requestSource: options.requestSource }),
|
|
243
|
+
});
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
224
246
|
/**
|
|
225
247
|
* Delete every setting whose key starts with `prefix`. Returns the number of
|
|
226
248
|
* rows removed. Used when an owning entity is deleted (e.g. an organization
|
|
@@ -12,5 +12,7 @@ import { type StoreWriteOptions } from "./store.js";
|
|
|
12
12
|
export declare function getUserSetting(email: string, key: string): Promise<Record<string, unknown> | null>;
|
|
13
13
|
/** Write a user-scoped setting. Always writes to the prefixed key. */
|
|
14
14
|
export declare function putUserSetting(email: string, key: string, value: Record<string, unknown>, options?: StoreWriteOptions): Promise<void>;
|
|
15
|
+
/** Atomically derive and persist one user-scoped setting. */
|
|
16
|
+
export declare function mutateUserSetting(email: string, key: string, updater: (current: Record<string, unknown> | null) => Record<string, unknown> | Promise<Record<string, unknown>>, options?: StoreWriteOptions): Promise<Record<string, unknown>>;
|
|
15
17
|
/** Delete a user-scoped setting. */
|
|
16
18
|
export declare function deleteUserSetting(email: string, key: string, options?: StoreWriteOptions): Promise<boolean>;
|