@agent-native/core 0.201.0-nightly-20261005153421 → 0.201.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 +22 -22
- package/dist/a2a/server.d.ts +2 -2
- package/dist/a2a/server.js +13 -2
- package/dist/agent/thread-data-builder.d.ts +4 -0
- package/dist/agent/thread-data-builder.js +8 -0
- package/dist/agent-sql/analysis.d.ts +31 -0
- package/dist/agent-sql/analysis.js +169 -0
- package/dist/agent-sql/index.d.ts +4 -0
- package/dist/agent-sql/index.js +4 -0
- package/dist/agent-sql/lexer.d.ts +37 -0
- package/dist/agent-sql/lexer.js +357 -0
- package/dist/agent-sql/postgres.d.ts +51 -0
- package/dist/agent-sql/postgres.js +197 -0
- package/dist/agent-sql/query.d.ts +30 -0
- package/dist/agent-sql/query.js +514 -0
- package/dist/catalog.json +24 -24
- package/dist/client/analytics/index.d.ts +1 -1
- package/dist/client/analytics/index.js +1 -1
- package/dist/client/analytics.d.ts +8 -0
- package/dist/client/analytics.js +17 -2
- package/dist/client/session-replay.js +3 -2
- package/dist/identity/offboard.js +31 -3
- package/dist/identity/rekey.d.ts +12 -1
- package/dist/identity/rekey.js +77 -6
- package/dist/identity/retired-emails.d.ts +23 -0
- package/dist/identity/retired-emails.js +55 -0
- package/dist/mcp/actions/create-org-service-token.js +20 -8
- package/dist/mcp/actions/service-token-access.d.ts +1 -0
- package/dist/mcp/actions/service-token-access.js +41 -24
- package/dist/mcp/build-server.d.ts +20 -6
- package/dist/mcp/build-server.js +131 -35
- package/dist/mcp/connect-route.d.ts +4 -3
- package/dist/mcp/connect-route.js +140 -82
- package/dist/mcp/connect-store.d.ts +17 -11
- package/dist/mcp/connect-store.js +88 -60
- package/dist/mcp/credential-issuance.d.ts +23 -0
- package/dist/mcp/credential-issuance.js +77 -0
- package/dist/mcp/credential-membership.d.ts +26 -0
- package/dist/mcp/credential-membership.js +67 -0
- package/dist/mcp/oauth-route.js +133 -45
- package/dist/mcp/oauth-store.d.ts +15 -5
- package/dist/mcp/oauth-store.js +119 -34
- package/dist/mcp/oauth-token.d.ts +4 -0
- package/dist/mcp/oauth-token.js +5 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/server.js +14 -0
- package/dist/onboarding/default-steps.js +1 -1
- package/dist/org/handlers.d.ts +1 -1
- package/dist/org/index.d.ts +2 -2
- package/dist/org/membership.d.ts +3 -1
- package/dist/org/membership.js +5 -2
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +10 -10
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resource-changes/store.d.ts +2 -0
- package/dist/resource-changes/store.js +4 -0
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/db/exec.js +28 -101
- package/dist/scripts/db/patch.js +14 -34
- package/dist/scripts/db/postgres-client.d.ts +8 -1
- package/dist/scripts/db/postgres-client.js +20 -6
- package/dist/scripts/db/query.js +31 -33
- package/dist/scripts/db/safety.d.ts +47 -2
- package/dist/scripts/db/safety.js +383 -137
- package/dist/scripts/db/scoping.d.ts +1 -0
- package/dist/scripts/db/scoping.js +3 -2
- package/dist/search/index-store.d.ts +21 -0
- package/dist/search/index-store.js +47 -0
- package/dist/search/indexer.js +35 -32
- package/dist/search/registry.d.ts +6 -3
- package/dist/search/registry.js +30 -8
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat-plugin.js +18 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-run-context.js +9 -0
- package/dist/server/attribution.d.ts +2 -0
- package/dist/server/attribution.js +7 -1
- package/dist/server/auth.js +9 -0
- package/dist/server/credential-membership-unavailable.d.ts +13 -0
- package/dist/server/credential-membership-unavailable.js +22 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/onboarding-html.js +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/recap-image-route.js +4 -0
- package/dist/server/release-schema.js +4 -0
- package/dist/shared/docs-url.d.ts +3 -1
- package/dist/shared/docs-url.js +6 -3
- package/dist/shared/environment-lanes.d.ts +8 -0
- package/dist/shared/environment-lanes.js +26 -0
- package/dist/templates/chat/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/chat/README.md +2 -2
- package/dist/templates/default/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/default/app/routes/home.tsx +5 -1
- package/dist/templates/headless/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/tracking/providers.js +13 -1
- package/docs/content/authentication.mdx +5 -1
- package/docs/content/durable-background-runs.mdx +17 -8
- package/docs/content/external-agents.mdx +3 -1
- package/docs/content/mcp-protocol.mdx +6 -2
- package/docs/content/security.mdx +1 -1
- package/package.json +5 -4
- package/src/templates/chat/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/chat/README.md +2 -2
- package/src/templates/default/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/default/app/routes/home.tsx +5 -1
- package/src/templates/headless/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
package/dist/mcp/oauth-token.js
CHANGED
|
@@ -11,6 +11,7 @@ export const MCP_OAUTH_SCOPES = [
|
|
|
11
11
|
"offline_access",
|
|
12
12
|
];
|
|
13
13
|
export const MCP_OAUTH_DEFAULT_SCOPE = MCP_OAUTH_SCOPES.join(" ");
|
|
14
|
+
const MCP_OAUTH_CREDENTIAL_VERSION = 2;
|
|
14
15
|
function signingSecret() {
|
|
15
16
|
return new TextEncoder().encode(process.env.A2A_SECRET?.trim() || getAuthSecret());
|
|
16
17
|
}
|
|
@@ -60,6 +61,7 @@ export function parseMcpOAuthOrgIdClaim(payload) {
|
|
|
60
61
|
export async function signMcpOAuthAccessToken(params) {
|
|
61
62
|
return new jose.SignJWT({
|
|
62
63
|
typ: "agent-native-mcp-oauth",
|
|
64
|
+
credential_version: MCP_OAUTH_CREDENTIAL_VERSION,
|
|
63
65
|
sub: params.ownerEmail,
|
|
64
66
|
...(params.orgId !== undefined ? { org_id: params.orgId } : {}),
|
|
65
67
|
...(params.orgDomain ? { org_domain: params.orgDomain } : {}),
|
|
@@ -122,6 +124,8 @@ export async function verifyMcpOAuthAccessToken(token, resource) {
|
|
|
122
124
|
try {
|
|
123
125
|
if (payload.typ !== "agent-native-mcp-oauth")
|
|
124
126
|
return null;
|
|
127
|
+
if (payload.credential_version !== MCP_OAUTH_CREDENTIAL_VERSION)
|
|
128
|
+
return null;
|
|
125
129
|
if (typeof payload.resource !== "string")
|
|
126
130
|
return null;
|
|
127
131
|
const embeddedResource = normaliseResource(payload.resource);
|
|
@@ -148,6 +152,7 @@ export async function verifyMcpOAuthAccessToken(token, resource) {
|
|
|
148
152
|
clientId: payload.client_id,
|
|
149
153
|
jti: typeof payload.jti === "string" ? payload.jti : undefined,
|
|
150
154
|
...(payload.catalog_scope === "full" ? { catalogScope: "full" } : {}),
|
|
155
|
+
...(typeof payload.iat === "number" ? { issuedAt: payload.iat } : {}),
|
|
151
156
|
};
|
|
152
157
|
}
|
|
153
158
|
catch {
|
|
@@ -140,13 +140,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
140
140
|
clientHint?: undefined;
|
|
141
141
|
timestamp?: undefined;
|
|
142
142
|
chapterId?: undefined;
|
|
143
|
-
count?: undefined;
|
|
144
143
|
startAt?: undefined;
|
|
145
144
|
endAt?: undefined;
|
|
146
145
|
reason?: undefined;
|
|
147
146
|
includeMicrophone?: undefined;
|
|
148
147
|
includeSystemAudio?: undefined;
|
|
149
148
|
requestId?: undefined;
|
|
149
|
+
count?: undefined;
|
|
150
150
|
};
|
|
151
151
|
required?: undefined;
|
|
152
152
|
};
|
|
@@ -171,13 +171,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
171
171
|
clientHint?: undefined;
|
|
172
172
|
timestamp?: undefined;
|
|
173
173
|
chapterId?: undefined;
|
|
174
|
-
count?: undefined;
|
|
175
174
|
startAt?: undefined;
|
|
176
175
|
endAt?: undefined;
|
|
177
176
|
reason?: undefined;
|
|
178
177
|
includeMicrophone?: undefined;
|
|
179
178
|
includeSystemAudio?: undefined;
|
|
180
179
|
requestId?: undefined;
|
|
180
|
+
count?: undefined;
|
|
181
181
|
};
|
|
182
182
|
required?: undefined;
|
|
183
183
|
};
|
|
@@ -196,13 +196,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
196
196
|
clientHint?: undefined;
|
|
197
197
|
timestamp?: undefined;
|
|
198
198
|
chapterId?: undefined;
|
|
199
|
-
count?: undefined;
|
|
200
199
|
startAt?: undefined;
|
|
201
200
|
endAt?: undefined;
|
|
202
201
|
reason?: undefined;
|
|
203
202
|
includeMicrophone?: undefined;
|
|
204
203
|
includeSystemAudio?: undefined;
|
|
205
204
|
requestId?: undefined;
|
|
205
|
+
count?: undefined;
|
|
206
206
|
};
|
|
207
207
|
required?: undefined;
|
|
208
208
|
};
|
|
@@ -231,13 +231,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
231
231
|
};
|
|
232
232
|
timestamp?: undefined;
|
|
233
233
|
chapterId?: undefined;
|
|
234
|
-
count?: undefined;
|
|
235
234
|
startAt?: undefined;
|
|
236
235
|
endAt?: undefined;
|
|
237
236
|
reason?: undefined;
|
|
238
237
|
includeMicrophone?: undefined;
|
|
239
238
|
includeSystemAudio?: undefined;
|
|
240
239
|
requestId?: undefined;
|
|
240
|
+
count?: undefined;
|
|
241
241
|
};
|
|
242
242
|
};
|
|
243
243
|
} | {
|
|
@@ -260,12 +260,12 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
260
260
|
description: string;
|
|
261
261
|
};
|
|
262
262
|
chapterId?: undefined;
|
|
263
|
-
count?: undefined;
|
|
264
263
|
startAt?: undefined;
|
|
265
264
|
endAt?: undefined;
|
|
266
265
|
includeMicrophone?: undefined;
|
|
267
266
|
includeSystemAudio?: undefined;
|
|
268
267
|
requestId?: undefined;
|
|
268
|
+
count?: undefined;
|
|
269
269
|
};
|
|
270
270
|
};
|
|
271
271
|
} | {
|
|
@@ -317,7 +317,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
317
317
|
clientHint?: undefined;
|
|
318
318
|
timestamp?: undefined;
|
|
319
319
|
chapterId?: undefined;
|
|
320
|
-
count?: undefined;
|
|
321
320
|
startAt: {
|
|
322
321
|
type: string;
|
|
323
322
|
description: string;
|
|
@@ -337,6 +336,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
337
336
|
type: string;
|
|
338
337
|
};
|
|
339
338
|
requestId?: undefined;
|
|
339
|
+
count?: undefined;
|
|
340
340
|
};
|
|
341
341
|
};
|
|
342
342
|
} | {
|
|
@@ -352,7 +352,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
352
352
|
clientHint?: undefined;
|
|
353
353
|
timestamp?: undefined;
|
|
354
354
|
chapterId?: undefined;
|
|
355
|
-
count?: undefined;
|
|
356
355
|
startAt?: undefined;
|
|
357
356
|
endAt?: undefined;
|
|
358
357
|
reason?: undefined;
|
|
@@ -361,6 +360,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
361
360
|
requestId: {
|
|
362
361
|
type: string;
|
|
363
362
|
};
|
|
363
|
+
count?: undefined;
|
|
364
364
|
};
|
|
365
365
|
};
|
|
366
366
|
})[];
|
package/dist/mcp/server.js
CHANGED
|
@@ -2,7 +2,9 @@ import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, ge
|
|
|
2
2
|
import { getAppConfig } from "../app-config/store.js";
|
|
3
3
|
import { getConfiguredAppBasePath } from "../server/app-base-path.js";
|
|
4
4
|
import { isLoopbackRequest } from "../server/auth.js";
|
|
5
|
+
import { CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE } from "../server/credential-membership-unavailable.js";
|
|
5
6
|
import { getH3App } from "../server/framework-request-handler.js";
|
|
7
|
+
import { getOrigin } from "../server/google-oauth.js";
|
|
6
8
|
import { readBody } from "../server/h3-helpers.js";
|
|
7
9
|
import { trackMcpInitialize } from "./analytics.js";
|
|
8
10
|
import { createMCPServerForRequest, verifyAuth, getAccessTokens, resolveOrgIdFromDomain, buildLinkArtifacts, McpDirectoryProfileValidationError, validateMcpDirectoryProfile, validateMcpDirectoryWidgetDomain, selectMcpActionSurface, } from "./build-server.js";
|
|
@@ -186,7 +188,19 @@ export async function handleMcpRequest(event, config, routePath = MCP_PUBLIC_ROU
|
|
|
186
188
|
isLoopbackOrigin(requestMeta.origin) &&
|
|
187
189
|
(hasLocalOwnerHint || process.env.AGENT_NATIVE_MCP_DEV_OPEN === "1"),
|
|
188
190
|
resourceUrl: getMcpOAuthAudiences(event, routePath),
|
|
191
|
+
requestOrigin: getOrigin(event),
|
|
189
192
|
});
|
|
193
|
+
if (!authResult.authed && authResult.unavailable) {
|
|
194
|
+
// The token is valid but its org membership could not be checked. No auth
|
|
195
|
+
// challenge: re-authenticating would not help, and the client must keep
|
|
196
|
+
// its tokens and retry.
|
|
197
|
+
setResponseStatus(event, 503);
|
|
198
|
+
setResponseHeader(event, "Retry-After", "5");
|
|
199
|
+
return {
|
|
200
|
+
error: "Service Unavailable",
|
|
201
|
+
message: CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
190
204
|
if (!authResult.authed) {
|
|
191
205
|
setResponseStatus(event, 401);
|
|
192
206
|
setResponseHeader(event, "WWW-Authenticate", buildMcpOAuthChallenge(event, routePath));
|
|
@@ -230,7 +230,7 @@ const emailStep = {
|
|
|
230
230
|
label: "Set email variables on your host",
|
|
231
231
|
description: "See which variables each email provider needs and how to set them.",
|
|
232
232
|
payload: {
|
|
233
|
-
url: "https://www.agent-native.com/docs/deployment#email-provider",
|
|
233
|
+
url: "https://www.agent-native.com/docs/deployment?utm_source=app&utm_medium=product&utm_content=onboarding-email-provider#email-provider",
|
|
234
234
|
external: true,
|
|
235
235
|
},
|
|
236
236
|
},
|
package/dist/org/handlers.d.ts
CHANGED
|
@@ -72,9 +72,9 @@ export declare const setWorkspaceAppDefaultVisibilityHandler: import("h3").Event
|
|
|
72
72
|
}>>;
|
|
73
73
|
export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
74
74
|
success: boolean;
|
|
75
|
-
id?: undefined;
|
|
76
75
|
name?: undefined;
|
|
77
76
|
role?: undefined;
|
|
77
|
+
id?: undefined;
|
|
78
78
|
} | {
|
|
79
79
|
success?: undefined;
|
|
80
80
|
id: string;
|
package/dist/org/index.d.ts
CHANGED
|
@@ -179,13 +179,13 @@ export declare const getMyOrgHandler: import("h3").EventHandlerWithFetch<import(
|
|
|
179
179
|
export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
180
180
|
name?: undefined;
|
|
181
181
|
success: boolean;
|
|
182
|
-
id?: undefined;
|
|
183
182
|
role?: undefined;
|
|
183
|
+
id?: undefined;
|
|
184
184
|
} | {
|
|
185
|
+
success?: undefined;
|
|
185
186
|
id: string;
|
|
186
187
|
name: string;
|
|
187
188
|
role: import("./types.js").OrgRole;
|
|
188
|
-
success?: undefined;
|
|
189
189
|
}>>;
|
|
190
190
|
export declare const updateOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
191
191
|
orgId: string;
|
package/dist/org/membership.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export declare function isMissingOrganizationTableError(error: unknown): boolean;
|
|
2
|
-
export declare function isOrgMember(orgId: string, email: string
|
|
2
|
+
export declare function isOrgMember(orgId: string, email: string, options?: {
|
|
3
|
+
requireOrganizationMetadata?: boolean;
|
|
4
|
+
}): Promise<boolean>;
|
package/dist/org/membership.js
CHANGED
|
@@ -17,7 +17,7 @@ export function isMissingOrganizationTableError(error) {
|
|
|
17
17
|
}
|
|
18
18
|
return false;
|
|
19
19
|
}
|
|
20
|
-
export async function isOrgMember(orgId, email) {
|
|
20
|
+
export async function isOrgMember(orgId, email, options = {}) {
|
|
21
21
|
const normalized = email.trim().toLowerCase();
|
|
22
22
|
if (!orgId || !normalized)
|
|
23
23
|
return false;
|
|
@@ -41,11 +41,14 @@ export async function isOrgMember(orgId, email) {
|
|
|
41
41
|
})).rows;
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
44
|
-
if (
|
|
44
|
+
if (options.requireOrganizationMetadata ||
|
|
45
|
+
!isMissingOrganizationTableError(error))
|
|
45
46
|
throw error;
|
|
46
47
|
return true;
|
|
47
48
|
}
|
|
48
49
|
const organization = organizationRows[0];
|
|
50
|
+
if (!organization && options.requireOrganizationMetadata)
|
|
51
|
+
return false;
|
|
49
52
|
const linked = String(organization?.identity_authority ?? "").trim() ||
|
|
50
53
|
String(organization?.identity_id ?? "").trim();
|
|
51
54
|
if (!linked)
|
|
@@ -76,8 +76,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
78
|
id?: undefined;
|
|
79
|
-
message?: undefined;
|
|
80
79
|
deleted?: undefined;
|
|
80
|
+
provider?: undefined;
|
|
81
81
|
providers: {
|
|
82
82
|
id: string;
|
|
83
83
|
label: string;
|
|
@@ -88,45 +88,45 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
88
88
|
updatedAt: number;
|
|
89
89
|
}[];
|
|
90
90
|
count: number;
|
|
91
|
-
provider?: undefined;
|
|
92
91
|
found?: undefined;
|
|
93
92
|
registered?: undefined;
|
|
94
93
|
label?: undefined;
|
|
94
|
+
message?: undefined;
|
|
95
95
|
} | {
|
|
96
96
|
id?: undefined;
|
|
97
|
-
count?: undefined;
|
|
98
|
-
message?: undefined;
|
|
99
97
|
deleted?: undefined;
|
|
100
98
|
providers?: undefined;
|
|
99
|
+
count?: undefined;
|
|
101
100
|
found: boolean;
|
|
102
101
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
103
102
|
registered?: undefined;
|
|
104
103
|
label?: undefined;
|
|
105
|
-
} | {
|
|
106
|
-
count?: undefined;
|
|
107
104
|
message?: undefined;
|
|
105
|
+
} | {
|
|
108
106
|
deleted?: undefined;
|
|
109
|
-
providers?: undefined;
|
|
110
107
|
provider?: undefined;
|
|
108
|
+
providers?: undefined;
|
|
109
|
+
count?: undefined;
|
|
111
110
|
found: boolean;
|
|
112
111
|
id: string;
|
|
113
112
|
registered?: undefined;
|
|
114
113
|
label?: undefined;
|
|
115
|
-
} | {
|
|
116
|
-
count?: undefined;
|
|
117
114
|
message?: undefined;
|
|
118
|
-
|
|
115
|
+
} | {
|
|
119
116
|
provider?: undefined;
|
|
117
|
+
providers?: undefined;
|
|
118
|
+
count?: undefined;
|
|
120
119
|
found?: undefined;
|
|
121
120
|
deleted: boolean;
|
|
122
121
|
id: string;
|
|
123
122
|
registered?: undefined;
|
|
124
123
|
label?: undefined;
|
|
124
|
+
message?: undefined;
|
|
125
125
|
} | {
|
|
126
|
-
count?: undefined;
|
|
127
126
|
deleted?: undefined;
|
|
128
|
-
providers?: undefined;
|
|
129
127
|
provider?: undefined;
|
|
128
|
+
providers?: undefined;
|
|
129
|
+
count?: undefined;
|
|
130
130
|
found?: undefined;
|
|
131
131
|
registered: boolean;
|
|
132
132
|
id: string;
|
|
@@ -312,8 +312,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
312
312
|
scope?: "org" | "user" | undefined;
|
|
313
313
|
}, {
|
|
314
314
|
provider?: undefined;
|
|
315
|
-
id?: undefined;
|
|
316
315
|
deleted?: undefined;
|
|
316
|
+
message?: undefined;
|
|
317
|
+
id?: undefined;
|
|
317
318
|
providers: {
|
|
318
319
|
id: string;
|
|
319
320
|
label: string;
|
|
@@ -327,42 +328,41 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
327
328
|
found?: undefined;
|
|
328
329
|
registered?: undefined;
|
|
329
330
|
label?: undefined;
|
|
330
|
-
message?: undefined;
|
|
331
331
|
} | {
|
|
332
|
-
|
|
332
|
+
count?: undefined;
|
|
333
333
|
deleted?: undefined;
|
|
334
|
+
message?: undefined;
|
|
335
|
+
id?: undefined;
|
|
334
336
|
providers?: undefined;
|
|
335
|
-
count?: undefined;
|
|
336
337
|
found: boolean;
|
|
337
338
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
338
339
|
registered?: undefined;
|
|
339
340
|
label?: undefined;
|
|
340
|
-
message?: undefined;
|
|
341
341
|
} | {
|
|
342
342
|
provider?: undefined;
|
|
343
|
+
count?: undefined;
|
|
343
344
|
deleted?: undefined;
|
|
345
|
+
message?: undefined;
|
|
344
346
|
providers?: undefined;
|
|
345
|
-
count?: undefined;
|
|
346
347
|
found: boolean;
|
|
347
348
|
id: string;
|
|
348
349
|
registered?: undefined;
|
|
349
350
|
label?: undefined;
|
|
350
|
-
message?: undefined;
|
|
351
351
|
} | {
|
|
352
352
|
provider?: undefined;
|
|
353
|
-
providers?: undefined;
|
|
354
353
|
count?: undefined;
|
|
354
|
+
message?: undefined;
|
|
355
|
+
providers?: undefined;
|
|
355
356
|
found?: undefined;
|
|
356
357
|
deleted: boolean;
|
|
357
358
|
id: string;
|
|
358
359
|
registered?: undefined;
|
|
359
360
|
label?: undefined;
|
|
360
|
-
message?: undefined;
|
|
361
361
|
} | {
|
|
362
362
|
provider?: undefined;
|
|
363
|
+
count?: undefined;
|
|
363
364
|
deleted?: undefined;
|
|
364
365
|
providers?: undefined;
|
|
365
|
-
count?: undefined;
|
|
366
366
|
found?: undefined;
|
|
367
367
|
registered: boolean;
|
|
368
368
|
id: string;
|
|
@@ -73,6 +73,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
73
73
|
offset?: unknown;
|
|
74
74
|
limit?: unknown;
|
|
75
75
|
}, {
|
|
76
|
+
deleted?: undefined;
|
|
76
77
|
jobs: {
|
|
77
78
|
id: string;
|
|
78
79
|
name: string;
|
|
@@ -83,7 +84,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
83
84
|
updatedAt: string;
|
|
84
85
|
}[];
|
|
85
86
|
total: number;
|
|
86
|
-
deleted?: undefined;
|
|
87
87
|
jobId?: undefined;
|
|
88
88
|
} | {
|
|
89
89
|
jobs?: undefined;
|
|
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
91
91
|
deleted: boolean;
|
|
92
92
|
jobId: string;
|
|
93
93
|
} | {
|
|
94
|
+
deleted?: undefined;
|
|
94
95
|
jobs?: undefined;
|
|
95
96
|
total?: undefined;
|
|
96
|
-
deleted?: undefined;
|
|
97
97
|
jobId?: undefined;
|
|
98
98
|
hits: Record<string, unknown>[];
|
|
99
99
|
offset: number;
|
|
@@ -73,6 +73,8 @@ export declare function resourceChangeTriggerNames(source: ResourceChangeSource)
|
|
|
73
73
|
* committed between a drop and a create would never be recorded.
|
|
74
74
|
*/
|
|
75
75
|
export declare function resourceChangeTriggerSql(source: ResourceChangeSource): string[];
|
|
76
|
+
/** A hash of the capture SQL for a source, which changes whenever it does. */
|
|
77
|
+
export declare function resourceChangeCaptureFingerprint(source: ResourceChangeSource): string;
|
|
76
78
|
/**
|
|
77
79
|
* Installs change capture for a source and subscribes a consumer to it.
|
|
78
80
|
* Apps call this from a named migration, so the triggers ship with the app's
|
|
@@ -230,6 +230,10 @@ $an_rc$`,
|
|
|
230
230
|
`CREATE OR REPLACE TRIGGER "${names.truncateTrigger}" BEFORE TRUNCATE ON "${table}" FOR EACH STATEMENT EXECUTE FUNCTION "${names.function}"()`,
|
|
231
231
|
];
|
|
232
232
|
}
|
|
233
|
+
/** A hash of the capture SQL for a source, which changes whenever it does. */
|
|
234
|
+
export function resourceChangeCaptureFingerprint(source) {
|
|
235
|
+
return shortHash(resourceChangeTriggerSql(source).join("\n"));
|
|
236
|
+
}
|
|
233
237
|
/**
|
|
234
238
|
* Installs change capture for a source and subscribes a consumer to it.
|
|
235
239
|
* Apps call this from a named migration, so the triggers ship with the app's
|
|
@@ -39,8 +39,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
39
39
|
error: string;
|
|
40
40
|
}>;
|
|
41
41
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
42
|
-
ok?: undefined;
|
|
43
42
|
error: string;
|
|
43
|
+
ok?: undefined;
|
|
44
44
|
} | {
|
|
45
45
|
error?: undefined;
|
|
46
46
|
ok: boolean;
|
package/dist/scripts/db/exec.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { getDatabaseUrl
|
|
2
|
+
import { getDatabaseUrl } from "../../db/client.js";
|
|
3
3
|
import { parseArgs, fail } from "../utils.js";
|
|
4
4
|
import { createPostgresScriptClient } from "./postgres-client.js";
|
|
5
|
-
import {
|
|
5
|
+
import { finalRawDbSql, rawDbInsertTarget, readRawDbWriteStatement, verifyRawDbStatement, } from "./safety.js";
|
|
6
6
|
import { buildScopingPostgres } from "./scoping.js";
|
|
7
7
|
function parseSqlArgs(raw, label = "--args") {
|
|
8
8
|
if (!raw)
|
|
@@ -50,106 +50,20 @@ function parseStatements(parsed) {
|
|
|
50
50
|
}
|
|
51
51
|
return [{ sql: parsed.sql, args: parseSqlArgs(parsed.args) }];
|
|
52
52
|
}
|
|
53
|
-
function stripLeadingSqlComments(sql) {
|
|
54
|
-
return sql
|
|
55
|
-
.replace(/^\s*--[^\n]*\n/gm, "")
|
|
56
|
-
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
57
|
-
.trim();
|
|
58
|
-
}
|
|
59
|
-
function hasAdditionalStatement(sql) {
|
|
60
|
-
let state = "normal";
|
|
61
|
-
for (let i = 0; i < sql.length; i++) {
|
|
62
|
-
const ch = sql[i];
|
|
63
|
-
const next = sql[i + 1];
|
|
64
|
-
if (state === "line") {
|
|
65
|
-
if (ch === "\n")
|
|
66
|
-
state = "normal";
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
if (state === "block") {
|
|
70
|
-
if (ch === "*" && next === "/") {
|
|
71
|
-
i++;
|
|
72
|
-
state = "normal";
|
|
73
|
-
}
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (state === "single") {
|
|
77
|
-
if (ch === "'" && next === "'")
|
|
78
|
-
i++;
|
|
79
|
-
else if (ch === "'")
|
|
80
|
-
state = "normal";
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (state === "double") {
|
|
84
|
-
if (ch === '"' && next === '"')
|
|
85
|
-
i++;
|
|
86
|
-
else if (ch === '"')
|
|
87
|
-
state = "normal";
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
if (ch === "-" && next === "-") {
|
|
91
|
-
i++;
|
|
92
|
-
state = "line";
|
|
93
|
-
}
|
|
94
|
-
else if (ch === "/" && next === "*") {
|
|
95
|
-
i++;
|
|
96
|
-
state = "block";
|
|
97
|
-
}
|
|
98
|
-
else if (ch === "'") {
|
|
99
|
-
state = "single";
|
|
100
|
-
}
|
|
101
|
-
else if (ch === '"') {
|
|
102
|
-
state = "double";
|
|
103
|
-
}
|
|
104
|
-
else if (ch === ";") {
|
|
105
|
-
return sql.slice(i + 1).trim().length > 0;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
function normalizeUserSql(sql, index) {
|
|
111
|
-
const normalized = stripLeadingSqlComments(sql);
|
|
112
|
-
if (!normalized)
|
|
113
|
-
fail(`Statement ${index} is empty`);
|
|
114
|
-
if (hasAdditionalStatement(normalized)) {
|
|
115
|
-
fail(`Statement ${index} contains multiple SQL statements. Use --statements for batches so each write can be validated and run transactionally.`);
|
|
116
|
-
}
|
|
117
|
-
return normalized.replace(/;\s*$/, "");
|
|
118
|
-
}
|
|
119
|
-
function validateWriteSql(sql, index) {
|
|
120
|
-
const normalized = normalizeUserSql(sql, index);
|
|
121
|
-
const upper = normalized.toUpperCase();
|
|
122
|
-
const allowed = ["INSERT", "UPDATE", "DELETE"];
|
|
123
|
-
const blocked = ["SELECT", "WITH", "EXPLAIN"];
|
|
124
|
-
if (blocked.some((keyword) => upper.startsWith(keyword))) {
|
|
125
|
-
fail(`Statement ${index}: use db-query for read statements. db-exec is for writes only.`);
|
|
126
|
-
}
|
|
127
|
-
if (upper.startsWith("CREATE") || upper.startsWith("ALTER")) {
|
|
128
|
-
fail(`Statement ${index}: schema changes are not allowed through db-exec. Additive schema changes must go through reviewed migrations or startup code.`);
|
|
129
|
-
}
|
|
130
|
-
if (!allowed.some((keyword) => upper.startsWith(keyword))) {
|
|
131
|
-
fail(`Statement ${index}: only ${allowed.join(", ")} statements are allowed. Dangerous operations such as DROP, TRUNCATE, GRANT, and REVOKE are blocked.`);
|
|
132
|
-
}
|
|
133
|
-
assertNoSensitiveFrameworkTables(normalized, "write");
|
|
134
|
-
assertNoRawDbAccessControlWrite(normalized);
|
|
135
|
-
assertNoSchemaQualifiedTables(normalized, "write");
|
|
136
|
-
return normalized;
|
|
137
|
-
}
|
|
138
53
|
const INSERT_INTO = "INSERT\\s+INTO";
|
|
139
|
-
function injectOwnership(
|
|
140
|
-
|
|
54
|
+
function injectOwnership(statement, scoping) {
|
|
55
|
+
const sql = statement.sql;
|
|
56
|
+
if (!scoping.active || statement.keyword !== "insert")
|
|
141
57
|
return sql;
|
|
142
|
-
|
|
58
|
+
const target = rawDbInsertTarget(statement);
|
|
59
|
+
if (!target)
|
|
143
60
|
return sql;
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
if (!match)
|
|
147
|
-
return sql;
|
|
148
|
-
const tableName = match[1];
|
|
61
|
+
const tableName = target.table;
|
|
62
|
+
const listed = new Set(target.columns ?? []);
|
|
149
63
|
const injections = [];
|
|
150
64
|
if (scoping.userEmail &&
|
|
151
65
|
scoping.ownerEmailTables.has(tableName) &&
|
|
152
|
-
|
|
66
|
+
!listed.has("owner_email")) {
|
|
153
67
|
injections.push({
|
|
154
68
|
column: "owner_email",
|
|
155
69
|
value: `'${scoping.userEmail.replace(/'/g, "''")}'`,
|
|
@@ -157,7 +71,7 @@ function injectOwnership(sql, scoping) {
|
|
|
157
71
|
}
|
|
158
72
|
if (scoping.orgId &&
|
|
159
73
|
scoping.orgIdTables.has(tableName) &&
|
|
160
|
-
|
|
74
|
+
!listed.has("org_id")) {
|
|
161
75
|
injections.push({
|
|
162
76
|
column: "org_id",
|
|
163
77
|
value: `'${scoping.orgId.replace(/'/g, "''")}'`,
|
|
@@ -227,7 +141,7 @@ Options:
|
|
|
227
141
|
return;
|
|
228
142
|
}
|
|
229
143
|
const statements = parseStatements(parsed).map((statement, index) => ({
|
|
230
|
-
|
|
144
|
+
statement: readRawDbWriteStatement(statement.sql, `Statement ${index + 1}`),
|
|
231
145
|
args: statement.args,
|
|
232
146
|
}));
|
|
233
147
|
const url = parsed.db
|
|
@@ -243,14 +157,27 @@ Options:
|
|
|
243
157
|
try {
|
|
244
158
|
for (let index = 0; index < statements.length; index++) {
|
|
245
159
|
const statement = statements[index];
|
|
246
|
-
const
|
|
160
|
+
const label = `Statement ${index + 1}`;
|
|
161
|
+
// Ownership injection rewrites the text, so the guards re-read the
|
|
162
|
+
// final SQL and check it against this transaction's state right
|
|
163
|
+
// before it runs, after any earlier statement in the batch.
|
|
164
|
+
const executed = finalRawDbSql(injectOwnership(statement.statement, scoping), "write", label);
|
|
165
|
+
await verifyRawDbStatement(tx, executed.statement, {
|
|
166
|
+
columnsByTable: scoping.columnsByTable,
|
|
167
|
+
label,
|
|
168
|
+
});
|
|
169
|
+
const sql = executed.sql;
|
|
247
170
|
try {
|
|
248
|
-
const result = await tx.unsafe(sql, statement.args
|
|
171
|
+
const result = await tx.unsafe(sql, statement.args, {
|
|
172
|
+
singleStatement: true,
|
|
173
|
+
});
|
|
249
174
|
results.push({
|
|
250
175
|
index: index + 1,
|
|
251
176
|
sql,
|
|
252
177
|
changes: result.count ?? 0,
|
|
253
|
-
rows:
|
|
178
|
+
rows: executed.statement.tokens.some((token) => token.kind === "word" && token.value === "returning")
|
|
179
|
+
? Array.from(result)
|
|
180
|
+
: [],
|
|
254
181
|
});
|
|
255
182
|
}
|
|
256
183
|
catch (error) {
|