@agent-native/core 0.77.7 → 0.77.10
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 +2 -2
- package/corpus/core/CHANGELOG.md +46 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/task-store.ts +25 -3
- package/corpus/core/src/agent/harness/store.ts +89 -55
- package/corpus/core/src/agent/observational-memory/store.ts +40 -14
- package/corpus/core/src/agent/production-agent.ts +18 -0
- package/corpus/core/src/agent/run-store.ts +116 -59
- package/corpus/core/src/application-state/store.ts +36 -2
- package/corpus/core/src/audit/store.ts +37 -6
- package/corpus/core/src/browser-sessions/store.ts +31 -12
- package/corpus/core/src/chat-threads/store.ts +67 -3
- package/corpus/core/src/checkpoints/store.ts +14 -3
- package/corpus/core/src/cli/create.ts +9 -0
- package/corpus/core/src/collab/storage.ts +20 -2
- package/corpus/core/src/db/ddl-guard.ts +302 -0
- package/corpus/core/src/extensions/store.ts +76 -3
- package/corpus/core/src/integrations/a2a-continuations-store.ts +69 -26
- package/corpus/core/src/integrations/config-store.ts +19 -18
- package/corpus/core/src/integrations/pending-tasks-store.ts +40 -16
- package/corpus/core/src/integrations/remote-commands-store.ts +37 -23
- package/corpus/core/src/integrations/remote-devices-store.ts +64 -20
- package/corpus/core/src/integrations/remote-push-store.ts +75 -35
- package/corpus/core/src/integrations/remote-run-events-store.ts +38 -12
- package/corpus/core/src/integrations/thread-mapping-store.ts +11 -2
- package/corpus/core/src/mcp/connect-store.ts +53 -16
- package/corpus/core/src/mcp/oauth-store.ts +32 -7
- package/corpus/core/src/notifications/store.ts +20 -4
- package/corpus/core/src/oauth-tokens/store.ts +45 -2
- package/corpus/core/src/observability/store.ts +154 -47
- package/corpus/core/src/progress/store.ts +36 -21
- package/corpus/core/src/provider-api/corpus-jobs-store.ts +25 -5
- package/corpus/core/src/provider-api/custom-registry.ts +15 -4
- package/corpus/core/src/provider-api/quota-governor.ts +15 -3
- package/corpus/core/src/provider-api/staged-datasets-store.ts +22 -5
- package/corpus/core/src/resources/store.ts +66 -31
- package/corpus/core/src/secrets/storage.ts +32 -2
- package/corpus/core/src/server/agent-teams-run-queue.ts +22 -10
- package/corpus/core/src/server/auth.ts +22 -4
- package/corpus/core/src/server/better-auth-instance.ts +49 -15
- package/corpus/core/src/server/embed-session.ts +19 -4
- package/corpus/core/src/server/identity-sso-store.ts +40 -11
- package/corpus/core/src/server/recap-image-store.ts +28 -9
- package/corpus/core/src/settings/store.ts +35 -6
- package/corpus/core/src/usage/store.ts +51 -16
- package/corpus/core/src/workspace-connections/store.ts +200 -22
- package/corpus/templates/calendar/changelog/2026-06-25-booking-link-previews-now-show-the-content-directly-on-the-g.md +6 -0
- package/corpus/templates/calendar/server/lib/booking-og-image.ts +0 -2
- package/corpus/templates/forms/changelog/2026-06-25-form-previews-now-show-the-content-directly-on-the-grid-back.md +6 -0
- package/corpus/templates/forms/server/lib/form-og-image.ts +0 -2
- package/corpus/templates/plan/app/global.css +6 -11
- package/corpus/templates/plan/app/pages/PlansPage.tsx +12 -10
- package/corpus/templates/plan/changelog/2026-06-25-plan-access-request-pages-stay-visible-during-background-ref.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-25-plan-documents-keep-their-full-reading-width-below-1200px-an.md +6 -0
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +19 -3
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/agent/harness/store.d.ts.map +1 -1
- package/dist/agent/harness/store.js +63 -34
- package/dist/agent/harness/store.js.map +1 -1
- package/dist/agent/observational-memory/store.d.ts.map +1 -1
- package/dist/agent/observational-memory/store.js +30 -8
- package/dist/agent/observational-memory/store.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +18 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +103 -46
- package/dist/agent/run-store.js.map +1 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +28 -2
- package/dist/application-state/store.js.map +1 -1
- package/dist/audit/store.d.ts.map +1 -1
- package/dist/audit/store.js +18 -3
- package/dist/audit/store.js.map +1 -1
- package/dist/browser-sessions/store.d.ts.map +1 -1
- package/dist/browser-sessions/store.js +22 -7
- package/dist/browser-sessions/store.js.map +1 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +51 -3
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/checkpoints/store.d.ts.map +1 -1
- package/dist/checkpoints/store.js +12 -3
- package/dist/checkpoints/store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +9 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/collab/storage.d.ts.map +1 -1
- package/dist/collab/storage.js +14 -2
- package/dist/collab/storage.js.map +1 -1
- package/dist/db/ddl-guard.d.ts +151 -0
- package/dist/db/ddl-guard.d.ts.map +1 -0
- package/dist/db/ddl-guard.js +265 -0
- package/dist/db/ddl-guard.js.map +1 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +32 -3
- package/dist/extensions/store.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +44 -24
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/config-store.d.ts.map +1 -1
- package/dist/integrations/config-store.js +17 -11
- package/dist/integrations/config-store.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +24 -16
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/remote-commands-store.d.ts.map +1 -1
- package/dist/integrations/remote-commands-store.js +29 -21
- package/dist/integrations/remote-commands-store.js.map +1 -1
- package/dist/integrations/remote-devices-store.d.ts.map +1 -1
- package/dist/integrations/remote-devices-store.js +33 -18
- package/dist/integrations/remote-devices-store.js.map +1 -1
- package/dist/integrations/remote-push-store.d.ts.map +1 -1
- package/dist/integrations/remote-push-store.js +53 -31
- package/dist/integrations/remote-push-store.js.map +1 -1
- package/dist/integrations/remote-run-events-store.d.ts.map +1 -1
- package/dist/integrations/remote-run-events-store.js +26 -10
- package/dist/integrations/remote-run-events-store.js.map +1 -1
- package/dist/integrations/thread-mapping-store.d.ts.map +1 -1
- package/dist/integrations/thread-mapping-store.js +10 -2
- package/dist/integrations/thread-mapping-store.js.map +1 -1
- package/dist/mcp/connect-store.d.ts.map +1 -1
- package/dist/mcp/connect-store.js +34 -16
- package/dist/mcp/connect-store.js.map +1 -1
- package/dist/mcp/oauth-store.d.ts.map +1 -1
- package/dist/mcp/oauth-store.js +22 -7
- package/dist/mcp/oauth-store.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/notifications/store.d.ts.map +1 -1
- package/dist/notifications/store.js +15 -3
- package/dist/notifications/store.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +33 -2
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/observability/store.d.ts.map +1 -1
- package/dist/observability/store.js +72 -29
- package/dist/observability/store.js.map +1 -1
- package/dist/progress/store.d.ts.map +1 -1
- package/dist/progress/store.js +31 -16
- package/dist/progress/store.js.map +1 -1
- package/dist/provider-api/corpus-jobs-store.d.ts.map +1 -1
- package/dist/provider-api/corpus-jobs-store.js +16 -5
- package/dist/provider-api/corpus-jobs-store.js.map +1 -1
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js +14 -4
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/provider-api/quota-governor.d.ts.map +1 -1
- package/dist/provider-api/quota-governor.js +12 -3
- package/dist/provider-api/quota-governor.js.map +1 -1
- package/dist/provider-api/staged-datasets-store.d.ts.map +1 -1
- package/dist/provider-api/staged-datasets-store.js +16 -5
- package/dist/provider-api/staged-datasets-store.js.map +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +56 -25
- package/dist/resources/store.js.map +1 -1
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +22 -2
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-teams-run-queue.d.ts.map +1 -1
- package/dist/server/agent-teams-run-queue.js +15 -4
- package/dist/server/agent-teams-run-queue.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +16 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +55 -21
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +15 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/server/identity-sso-store.d.ts.map +1 -1
- package/dist/server/identity-sso-store.js +30 -11
- package/dist/server/identity-sso-store.js.map +1 -1
- package/dist/server/recap-image-store.d.ts.map +1 -1
- package/dist/server/recap-image-store.js +21 -7
- package/dist/server/recap-image-store.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +30 -6
- package/dist/settings/store.js.map +1 -1
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +36 -11
- package/dist/usage/store.js.map +1 -1
- package/dist/workspace-connections/store.d.ts.map +1 -1
- package/dist/workspace-connections/store.js +57 -7
- package/dist/workspace-connections/store.js.map +1 -1
- package/package.json +1 -1
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
setResponseHeader,
|
|
10
10
|
} from "h3";
|
|
11
11
|
|
|
12
|
-
import { getDbExec, intType } from "../db/client.js";
|
|
12
|
+
import { getDbExec, intType, isPostgres } from "../db/client.js";
|
|
13
|
+
import { ensureTableExists } from "../db/ddl-guard.js";
|
|
13
14
|
import {
|
|
14
15
|
EMBED_MODE_QUERY_PARAM,
|
|
15
16
|
EMBED_SESSION_COOKIE,
|
|
@@ -103,8 +104,10 @@ export type ResolvedEmbedSession = {
|
|
|
103
104
|
async function ensureTable(): Promise<void> {
|
|
104
105
|
if (!_initPromise) {
|
|
105
106
|
_initPromise = (async () => {
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
// Build the CREATE SQL here (not at module scope) so intType() runs at
|
|
108
|
+
// RUNTIME, not import time — a module-scope call breaks any consumer whose
|
|
109
|
+
// db/client mock doesn't stub intType (e.g. db-admin specs).
|
|
110
|
+
const embedTicketsCreateSql = `
|
|
108
111
|
CREATE TABLE IF NOT EXISTS agent_native_embed_tickets (
|
|
109
112
|
ticket_hash TEXT PRIMARY KEY,
|
|
110
113
|
owner_email TEXT NOT NULL,
|
|
@@ -115,7 +118,19 @@ async function ensureTable(): Promise<void> {
|
|
|
115
118
|
expires_at ${intType()} NOT NULL,
|
|
116
119
|
consumed_at ${intType()}
|
|
117
120
|
)
|
|
118
|
-
|
|
121
|
+
`;
|
|
122
|
+
if (isPostgres()) {
|
|
123
|
+
// PG guard: probe → guarded DDL → re-probe; skips lock on already-migrated path
|
|
124
|
+
await ensureTableExists(
|
|
125
|
+
"agent_native_embed_tickets",
|
|
126
|
+
embedTicketsCreateSql,
|
|
127
|
+
);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// SQLite (local dev): no lock problem — keep the original behaviour.
|
|
132
|
+
const client = getDbExec();
|
|
133
|
+
await client.execute(embedTicketsCreateSql);
|
|
119
134
|
})().catch((err) => {
|
|
120
135
|
_initPromise = undefined;
|
|
121
136
|
throw err;
|
|
@@ -24,7 +24,13 @@
|
|
|
24
24
|
|
|
25
25
|
import { randomBytes } from "node:crypto";
|
|
26
26
|
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
getDbExec,
|
|
29
|
+
isConnectionError,
|
|
30
|
+
intType,
|
|
31
|
+
isPostgres,
|
|
32
|
+
} from "../db/client.js";
|
|
33
|
+
import { ensureTableExists } from "../db/ddl-guard.js";
|
|
28
34
|
|
|
29
35
|
let _initPromise: Promise<void> | undefined;
|
|
30
36
|
|
|
@@ -98,13 +104,11 @@ export const SSO_STATE_TTL_MS = 10 * 60_000;
|
|
|
98
104
|
export const SSO_LOGIN_MAX = 60;
|
|
99
105
|
export const SSO_LOGIN_WINDOW_MS = 60_000;
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// deploy context (preview/branch/prod) for hosted templates.
|
|
107
|
-
await client.execute(`
|
|
107
|
+
// Build the CREATE SQL lazily (not at module scope) so intType() runs at
|
|
108
|
+
// RUNTIME, not import time — a module-scope call breaks any consumer whose
|
|
109
|
+
// db/client mock doesn't stub intType (e.g. db-admin specs).
|
|
110
|
+
function buildIdentitySsoStateCreateSql(): string {
|
|
111
|
+
return `
|
|
108
112
|
CREATE TABLE IF NOT EXISTS identity_sso_state (
|
|
109
113
|
state TEXT PRIMARY KEY,
|
|
110
114
|
return_path TEXT,
|
|
@@ -112,13 +116,38 @@ async function ensureTable(): Promise<void> {
|
|
|
112
116
|
expires_at ${intType()},
|
|
113
117
|
consumed_at ${intType()}
|
|
114
118
|
)
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
`;
|
|
120
|
+
}
|
|
121
|
+
function buildIdentitySsoJtiCreateSql(): string {
|
|
122
|
+
return `
|
|
117
123
|
CREATE TABLE IF NOT EXISTS identity_sso_jti (
|
|
118
124
|
jti TEXT PRIMARY KEY,
|
|
119
125
|
seen_at ${intType()}
|
|
120
126
|
)
|
|
121
|
-
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function ensureTable(): Promise<void> {
|
|
131
|
+
if (!_initPromise) {
|
|
132
|
+
_initPromise = (async () => {
|
|
133
|
+
const client = getDbExec();
|
|
134
|
+
const identitySsoStateCreateSql = buildIdentitySsoStateCreateSql();
|
|
135
|
+
const identitySsoJtiCreateSql = buildIdentitySsoJtiCreateSql();
|
|
136
|
+
// Additive only. Never DROP / ALTER — this DB is shared across every
|
|
137
|
+
// deploy context (preview/branch/prod) for hosted templates.
|
|
138
|
+
if (isPostgres()) {
|
|
139
|
+
// PG guard: probe → guarded DDL → re-probe; skips lock on already-migrated path
|
|
140
|
+
await ensureTableExists(
|
|
141
|
+
"identity_sso_state",
|
|
142
|
+
identitySsoStateCreateSql,
|
|
143
|
+
);
|
|
144
|
+
await ensureTableExists("identity_sso_jti", identitySsoJtiCreateSql);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// SQLite (local dev): no lock problem — keep the original behaviour.
|
|
149
|
+
await client.execute(identitySsoStateCreateSql);
|
|
150
|
+
await client.execute(identitySsoJtiCreateSql);
|
|
122
151
|
})().catch((err) => {
|
|
123
152
|
// Don't cache a rejection — let the next caller retry a fresh init.
|
|
124
153
|
_initPromise = undefined;
|
|
@@ -19,7 +19,13 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { randomBytes } from "node:crypto";
|
|
21
21
|
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
getDbExec,
|
|
24
|
+
intType,
|
|
25
|
+
isPostgres,
|
|
26
|
+
retryOnDdlRace,
|
|
27
|
+
} from "../db/client.js";
|
|
28
|
+
import { ensureTableExists } from "../db/ddl-guard.js";
|
|
23
29
|
|
|
24
30
|
/** Maximum stored image size (~5 MB of raw PNG bytes). */
|
|
25
31
|
export const RECAP_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
@@ -44,12 +50,11 @@ const TOKEN_PATTERN = /^[0-9a-f]{32,128}$/;
|
|
|
44
50
|
|
|
45
51
|
let _initPromise: Promise<void> | undefined;
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
client.execute(`
|
|
53
|
+
// Build the CREATE SQL lazily (not at module scope) so intType() runs at
|
|
54
|
+
// RUNTIME, not import time — a module-scope call breaks any consumer whose
|
|
55
|
+
// db/client mock doesn't stub intType (e.g. db-admin specs).
|
|
56
|
+
function buildRecapImagesCreateSql(): string {
|
|
57
|
+
return `
|
|
53
58
|
CREATE TABLE IF NOT EXISTS recap_images (
|
|
54
59
|
token TEXT PRIMARY KEY,
|
|
55
60
|
png_base64 TEXT NOT NULL,
|
|
@@ -58,8 +63,22 @@ export async function ensureRecapImageTable(): Promise<void> {
|
|
|
58
63
|
owner_email TEXT,
|
|
59
64
|
created_at ${intType()} NOT NULL
|
|
60
65
|
)
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function ensureRecapImageTable(): Promise<void> {
|
|
70
|
+
if (!_initPromise) {
|
|
71
|
+
_initPromise = (async () => {
|
|
72
|
+
const recapImagesCreateSql = buildRecapImagesCreateSql();
|
|
73
|
+
if (isPostgres()) {
|
|
74
|
+
// PG guard: probe → guarded DDL → re-probe; skips lock on already-migrated path
|
|
75
|
+
await ensureTableExists("recap_images", recapImagesCreateSql);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// SQLite (local dev): no lock problem — keep the original behaviour.
|
|
80
|
+
const client = getDbExec();
|
|
81
|
+
await retryOnDdlRace(() => client.execute(recapImagesCreateSql));
|
|
63
82
|
})().catch((error) => {
|
|
64
83
|
// Allow a later call to retry if the first init lost a DDL race.
|
|
65
84
|
_initPromise = undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
2
|
|
|
3
3
|
import { getDbExec, isPostgres, intType } from "../db/client.js";
|
|
4
|
+
import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
4
5
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
5
6
|
|
|
6
7
|
let _initPromise: Promise<void> | undefined;
|
|
@@ -20,17 +21,45 @@ async function ensureTable(): Promise<void> {
|
|
|
20
21
|
_initPromise = (async () => {
|
|
21
22
|
const client = getDbExec();
|
|
22
23
|
const table = settingsTable();
|
|
23
|
-
|
|
24
|
+
const createSql = `
|
|
24
25
|
CREATE TABLE IF NOT EXISTS ${table} (
|
|
25
26
|
key TEXT PRIMARY KEY,
|
|
26
27
|
value TEXT NOT NULL,
|
|
27
28
|
updated_at ${intType()} NOT NULL
|
|
28
29
|
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
if (isPostgres()) {
|
|
33
|
+
// Hot path: the `settings` table and its poll index are virtually
|
|
34
|
+
// always already present in production. Issuing `CREATE TABLE`/
|
|
35
|
+
// `CREATE INDEX` still takes a lock that, in a fresh background-worker
|
|
36
|
+
// process behind a concurrent connection on the shared Neon DB, can
|
|
37
|
+
// block ~indefinitely (ACCESS EXCLUSIVE for CREATE TABLE; a write-
|
|
38
|
+
// blocking SHARE lock for CREATE INDEX). `ensureTableExists` /
|
|
39
|
+
// `ensureIndexExists` probe `information_schema`/`pg_indexes` first
|
|
40
|
+
// (plain reads, no lock) and run DDL ONLY for what is actually missing,
|
|
41
|
+
// bounding any DDL with a transaction-scoped `lock_timeout`. They also
|
|
42
|
+
// re-probe after a swallowed lock-timeout and THROW if the schema is
|
|
43
|
+
// still missing, so a timed-out DDL never poisons this init memo with
|
|
44
|
+
// missing schema. `settingsTable()` is `public.settings` on Postgres;
|
|
45
|
+
// the existence checks use the unqualified table name.
|
|
46
|
+
await ensureTableExists("settings", createSql);
|
|
47
|
+
// Older deployments (pre BIGINT-compat) have a 32-bit `updated_at`; on
|
|
48
|
+
// Postgres the `Date.now()` written on every setSetting overflows int4.
|
|
49
|
+
// widenIntColumnsToBigInt already probes information_schema and only
|
|
50
|
+
// ALTERs columns that are still int4 — a no-op on fresh/widened DBs.
|
|
51
|
+
await widenIntColumnsToBigInt("settings", ["updated_at"]);
|
|
52
|
+
// Index for the poll watermark query: `SELECT MAX(updated_at)`.
|
|
53
|
+
await ensureIndexExists(
|
|
54
|
+
"settings_updated_at_idx",
|
|
55
|
+
`CREATE INDEX IF NOT EXISTS settings_updated_at_idx ON ${table} (updated_at)`,
|
|
56
|
+
);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// SQLite (local dev): no lock problem — keep the original behaviour.
|
|
61
|
+
await client.execute(createSql);
|
|
62
|
+
// No-op on SQLite (INTEGER is already 64-bit).
|
|
34
63
|
await widenIntColumnsToBigInt("settings", ["updated_at"]);
|
|
35
64
|
// Index for the poll watermark query: `SELECT MAX(updated_at) FROM settings`.
|
|
36
65
|
// MAX on an indexed column avoids a full-table scan on every poll cycle.
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
* Cost is stored as "centicents" (1/100th of a cent) for integer precision.
|
|
9
9
|
*/
|
|
10
10
|
import { getDbExec, intType, isPostgres } from "../db/client.js";
|
|
11
|
+
import {
|
|
12
|
+
ensureColumnExists,
|
|
13
|
+
ensureIndexExists,
|
|
14
|
+
ensureTableExists,
|
|
15
|
+
} from "../db/ddl-guard.js";
|
|
11
16
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
12
17
|
|
|
13
18
|
/**
|
|
@@ -191,7 +196,7 @@ async function ensureUsageTable(): Promise<void> {
|
|
|
191
196
|
if (!_initPromise) {
|
|
192
197
|
_initPromise = (async () => {
|
|
193
198
|
const client = getDbExec();
|
|
194
|
-
|
|
199
|
+
const createSql = `
|
|
195
200
|
CREATE TABLE IF NOT EXISTS token_usage (
|
|
196
201
|
id ${intType()} PRIMARY KEY,
|
|
197
202
|
owner_email TEXT NOT NULL,
|
|
@@ -206,11 +211,9 @@ async function ensureUsageTable(): Promise<void> {
|
|
|
206
211
|
ref_id TEXT NOT NULL DEFAULT '',
|
|
207
212
|
created_at ${intType()} NOT NULL
|
|
208
213
|
)
|
|
209
|
-
|
|
214
|
+
`;
|
|
210
215
|
|
|
211
|
-
//
|
|
212
|
-
// fields. Each ALTER is wrapped so a dialect without IF NOT EXISTS
|
|
213
|
-
// (SQLite) still makes progress if only some columns are missing.
|
|
216
|
+
// Additive columns for older deployments that pre-date the label/cache fields.
|
|
214
217
|
const additions: Array<[string, string]> = [
|
|
215
218
|
["cache_read_tokens", `${intType()} NOT NULL DEFAULT 0`],
|
|
216
219
|
["cache_write_tokens", `${intType()} NOT NULL DEFAULT 0`],
|
|
@@ -218,27 +221,59 @@ async function ensureUsageTable(): Promise<void> {
|
|
|
218
221
|
["app", `TEXT NOT NULL DEFAULT ''`],
|
|
219
222
|
["ref_id", `TEXT NOT NULL DEFAULT ''`],
|
|
220
223
|
];
|
|
224
|
+
|
|
225
|
+
if (isPostgres()) {
|
|
226
|
+
// Hot path: the `token_usage` table and its index are virtually always
|
|
227
|
+
// already present in production. Issuing `CREATE TABLE`/`ALTER TABLE`/
|
|
228
|
+
// `CREATE INDEX` still takes a lock that, in a fresh background-worker
|
|
229
|
+
// process behind a concurrent connection on the shared Neon DB, can
|
|
230
|
+
// block ~indefinitely. The ensure* wrappers probe `information_schema`/
|
|
231
|
+
// `pg_indexes` first (plain reads, no lock) and run DDL ONLY for what is
|
|
232
|
+
// actually missing, bounded by a transaction-scoped `lock_timeout`. If a
|
|
233
|
+
// swallowed lock-timeout leaves the schema still missing they RE-PROBE
|
|
234
|
+
// and THROW rather than letting init memoize success against absent
|
|
235
|
+
// schema.
|
|
236
|
+
await ensureTableExists("token_usage", createSql);
|
|
237
|
+
// Add columns on older deployments — guarded so the hot path (columns
|
|
238
|
+
// already present) skips the ACCESS EXCLUSIVE ALTER.
|
|
239
|
+
for (const [col, def] of additions) {
|
|
240
|
+
await ensureColumnExists(
|
|
241
|
+
"token_usage",
|
|
242
|
+
col,
|
|
243
|
+
`ALTER TABLE token_usage ADD COLUMN IF NOT EXISTS ${col} ${def}`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
// Older deployments created `created_at` as 32-bit `INTEGER`; on Postgres
|
|
247
|
+
// the `Date.now()` written per run by recordUsage() overflows int4. Widen
|
|
248
|
+
// it in place (no-op once done / on fresh BIGINT databases).
|
|
249
|
+
await widenIntColumnsToBigInt("token_usage", ["created_at"]);
|
|
250
|
+
// Probe pg_indexes first (no lock) and skip the SHARE-locking CREATE
|
|
251
|
+
// INDEX when the index is already present.
|
|
252
|
+
await ensureIndexExists(
|
|
253
|
+
"idx_token_usage_owner_created",
|
|
254
|
+
`CREATE INDEX IF NOT EXISTS idx_token_usage_owner_created ON token_usage (owner_email, created_at)`,
|
|
255
|
+
);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// SQLite (local dev): no lock problem — keep the original behaviour.
|
|
260
|
+
await client.execute(createSql);
|
|
261
|
+
// Add columns on older deployments that pre-date the label/cache
|
|
262
|
+
// fields. Each ALTER is wrapped so a dialect without IF NOT EXISTS
|
|
263
|
+
// (SQLite) still makes progress if only some columns are missing.
|
|
221
264
|
for (const [col, def] of additions) {
|
|
222
265
|
try {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
);
|
|
227
|
-
} else {
|
|
228
|
-
await client.execute(
|
|
229
|
-
`ALTER TABLE token_usage ADD COLUMN ${col} ${def}`,
|
|
230
|
-
);
|
|
231
|
-
}
|
|
266
|
+
await client.execute(
|
|
267
|
+
`ALTER TABLE token_usage ADD COLUMN ${col} ${def}`,
|
|
268
|
+
);
|
|
232
269
|
} catch {
|
|
233
270
|
// Column already exists — ignore
|
|
234
271
|
}
|
|
235
272
|
}
|
|
236
|
-
|
|
237
273
|
// Older deployments created `created_at` as 32-bit `INTEGER`; on Postgres
|
|
238
274
|
// the `Date.now()` written per run by recordUsage() overflows int4. Widen
|
|
239
275
|
// it in place (no-op once done / on fresh BIGINT databases).
|
|
240
276
|
await widenIntColumnsToBigInt("token_usage", ["created_at"]);
|
|
241
|
-
|
|
242
277
|
try {
|
|
243
278
|
await client.execute(
|
|
244
279
|
`CREATE INDEX IF NOT EXISTS idx_token_usage_owner_created ON token_usage (owner_email, created_at)`,
|
|
@@ -15,6 +15,11 @@ import {
|
|
|
15
15
|
safeJsonParse,
|
|
16
16
|
type DbExec,
|
|
17
17
|
} from "../db/client.js";
|
|
18
|
+
import {
|
|
19
|
+
ensureColumnExists,
|
|
20
|
+
ensureIndexExists,
|
|
21
|
+
ensureTableExists,
|
|
22
|
+
} from "../db/ddl-guard.js";
|
|
18
23
|
import {
|
|
19
24
|
getRequestOrgId,
|
|
20
25
|
getRequestUserEmail,
|
|
@@ -459,8 +464,8 @@ export async function ensureWorkspaceConnectionsTable(): Promise<void> {
|
|
|
459
464
|
const client = getDbExec();
|
|
460
465
|
const table = workspaceConnectionsTable();
|
|
461
466
|
const grantsTable = workspaceConnectionGrantsTable();
|
|
462
|
-
|
|
463
|
-
|
|
467
|
+
|
|
468
|
+
const createConnectionsSql = `
|
|
464
469
|
CREATE TABLE IF NOT EXISTS ${table} (
|
|
465
470
|
id TEXT PRIMARY KEY,
|
|
466
471
|
provider TEXT NOT NULL DEFAULT '',
|
|
@@ -480,23 +485,8 @@ export async function ensureWorkspaceConnectionsTable(): Promise<void> {
|
|
|
480
485
|
last_checked_at ${intType()},
|
|
481
486
|
last_error TEXT
|
|
482
487
|
)
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
await ensureWorkspaceConnectionColumns(client, table);
|
|
487
|
-
|
|
488
|
-
await retryOnDdlRace(() =>
|
|
489
|
-
client.execute(
|
|
490
|
-
`CREATE INDEX IF NOT EXISTS idx_workspace_connections_scope_provider ON ${table} (org_id, owner_email, provider)`,
|
|
491
|
-
),
|
|
492
|
-
);
|
|
493
|
-
await retryOnDdlRace(() =>
|
|
494
|
-
client.execute(
|
|
495
|
-
`CREATE INDEX IF NOT EXISTS idx_workspace_connections_updated_at ON ${table} (updated_at)`,
|
|
496
|
-
),
|
|
497
|
-
);
|
|
498
|
-
await retryOnDdlRace(() =>
|
|
499
|
-
client.execute(`
|
|
488
|
+
`;
|
|
489
|
+
const createGrantsSql = `
|
|
500
490
|
CREATE TABLE IF NOT EXISTS ${grantsTable} (
|
|
501
491
|
id TEXT PRIMARY KEY,
|
|
502
492
|
connection_id TEXT NOT NULL DEFAULT '',
|
|
@@ -512,11 +502,199 @@ export async function ensureWorkspaceConnectionsTable(): Promise<void> {
|
|
|
512
502
|
updated_at ${intType()} NOT NULL DEFAULT 0,
|
|
513
503
|
last_used_at ${intType()}
|
|
514
504
|
)
|
|
515
|
-
|
|
516
|
-
|
|
505
|
+
`;
|
|
506
|
+
|
|
507
|
+
if (isPostgres()) {
|
|
508
|
+
// PG-guard: probe information_schema / pg_indexes first (no lock) and
|
|
509
|
+
// only issue DDL when the table/column/index is actually missing,
|
|
510
|
+
// wrapped in a transaction-scoped lock_timeout so a contended lock
|
|
511
|
+
// fails fast. Uses unqualified table names for the probe (the helpers
|
|
512
|
+
// always search the `public` schema).
|
|
513
|
+
|
|
514
|
+
// --- workspace_connections ---
|
|
515
|
+
await ensureTableExists("workspace_connections", createConnectionsSql);
|
|
516
|
+
await ensureColumnExists(
|
|
517
|
+
"workspace_connections",
|
|
518
|
+
"provider",
|
|
519
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS provider TEXT NOT NULL DEFAULT ''`,
|
|
520
|
+
);
|
|
521
|
+
await ensureColumnExists(
|
|
522
|
+
"workspace_connections",
|
|
523
|
+
"label",
|
|
524
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS label TEXT NOT NULL DEFAULT ''`,
|
|
525
|
+
);
|
|
526
|
+
await ensureColumnExists(
|
|
527
|
+
"workspace_connections",
|
|
528
|
+
"account_id",
|
|
529
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS account_id TEXT`,
|
|
530
|
+
);
|
|
531
|
+
await ensureColumnExists(
|
|
532
|
+
"workspace_connections",
|
|
533
|
+
"account_label",
|
|
534
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS account_label TEXT`,
|
|
535
|
+
);
|
|
536
|
+
await ensureColumnExists(
|
|
537
|
+
"workspace_connections",
|
|
538
|
+
"status",
|
|
539
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS status TEXT NOT NULL DEFAULT 'connected'`,
|
|
540
|
+
);
|
|
541
|
+
await ensureColumnExists(
|
|
542
|
+
"workspace_connections",
|
|
543
|
+
"scopes_json",
|
|
544
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS scopes_json TEXT NOT NULL DEFAULT '[]'`,
|
|
545
|
+
);
|
|
546
|
+
await ensureColumnExists(
|
|
547
|
+
"workspace_connections",
|
|
548
|
+
"config_json",
|
|
549
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS config_json TEXT NOT NULL DEFAULT '{}'`,
|
|
550
|
+
);
|
|
551
|
+
await ensureColumnExists(
|
|
552
|
+
"workspace_connections",
|
|
553
|
+
"allowed_apps_json",
|
|
554
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS allowed_apps_json TEXT NOT NULL DEFAULT '[]'`,
|
|
555
|
+
);
|
|
556
|
+
await ensureColumnExists(
|
|
557
|
+
"workspace_connections",
|
|
558
|
+
"credential_refs_json",
|
|
559
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS credential_refs_json TEXT NOT NULL DEFAULT '[]'`,
|
|
560
|
+
);
|
|
561
|
+
await ensureColumnExists(
|
|
562
|
+
"workspace_connections",
|
|
563
|
+
"owner_email",
|
|
564
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS owner_email TEXT NOT NULL DEFAULT ''`,
|
|
565
|
+
);
|
|
566
|
+
await ensureColumnExists(
|
|
567
|
+
"workspace_connections",
|
|
568
|
+
"org_id",
|
|
569
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS org_id TEXT`,
|
|
570
|
+
);
|
|
571
|
+
await ensureColumnExists(
|
|
572
|
+
"workspace_connections",
|
|
573
|
+
"created_at",
|
|
574
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS created_at ${intType()} NOT NULL DEFAULT 0`,
|
|
575
|
+
);
|
|
576
|
+
await ensureColumnExists(
|
|
577
|
+
"workspace_connections",
|
|
578
|
+
"updated_at",
|
|
579
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS updated_at ${intType()} NOT NULL DEFAULT 0`,
|
|
580
|
+
);
|
|
581
|
+
await ensureColumnExists(
|
|
582
|
+
"workspace_connections",
|
|
583
|
+
"last_checked_at",
|
|
584
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS last_checked_at ${intType()}`,
|
|
585
|
+
);
|
|
586
|
+
await ensureColumnExists(
|
|
587
|
+
"workspace_connections",
|
|
588
|
+
"last_used_at",
|
|
589
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS last_used_at ${intType()}`,
|
|
590
|
+
);
|
|
591
|
+
await ensureColumnExists(
|
|
592
|
+
"workspace_connections",
|
|
593
|
+
"last_error",
|
|
594
|
+
`ALTER TABLE workspace_connections ADD COLUMN IF NOT EXISTS last_error TEXT`,
|
|
595
|
+
);
|
|
596
|
+
await ensureIndexExists(
|
|
597
|
+
"idx_workspace_connections_scope_provider",
|
|
598
|
+
`CREATE INDEX IF NOT EXISTS idx_workspace_connections_scope_provider ON ${table} (org_id, owner_email, provider)`,
|
|
599
|
+
);
|
|
600
|
+
await ensureIndexExists(
|
|
601
|
+
"idx_workspace_connections_updated_at",
|
|
602
|
+
`CREATE INDEX IF NOT EXISTS idx_workspace_connections_updated_at ON ${table} (updated_at)`,
|
|
603
|
+
);
|
|
517
604
|
|
|
518
|
-
|
|
605
|
+
// --- workspace_connection_grants ---
|
|
606
|
+
await ensureTableExists("workspace_connection_grants", createGrantsSql);
|
|
607
|
+
await ensureColumnExists(
|
|
608
|
+
"workspace_connection_grants",
|
|
609
|
+
"connection_id",
|
|
610
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS connection_id TEXT NOT NULL DEFAULT ''`,
|
|
611
|
+
);
|
|
612
|
+
await ensureColumnExists(
|
|
613
|
+
"workspace_connection_grants",
|
|
614
|
+
"provider",
|
|
615
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS provider TEXT NOT NULL DEFAULT ''`,
|
|
616
|
+
);
|
|
617
|
+
await ensureColumnExists(
|
|
618
|
+
"workspace_connection_grants",
|
|
619
|
+
"app_id",
|
|
620
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS app_id TEXT NOT NULL DEFAULT ''`,
|
|
621
|
+
);
|
|
622
|
+
await ensureColumnExists(
|
|
623
|
+
"workspace_connection_grants",
|
|
624
|
+
"scopes_json",
|
|
625
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS scopes_json TEXT NOT NULL DEFAULT '[]'`,
|
|
626
|
+
);
|
|
627
|
+
await ensureColumnExists(
|
|
628
|
+
"workspace_connection_grants",
|
|
629
|
+
"config_json",
|
|
630
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS config_json TEXT NOT NULL DEFAULT '{}'`,
|
|
631
|
+
);
|
|
632
|
+
await ensureColumnExists(
|
|
633
|
+
"workspace_connection_grants",
|
|
634
|
+
"credential_refs_json",
|
|
635
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS credential_refs_json TEXT NOT NULL DEFAULT '[]'`,
|
|
636
|
+
);
|
|
637
|
+
await ensureColumnExists(
|
|
638
|
+
"workspace_connection_grants",
|
|
639
|
+
"granted_by_email",
|
|
640
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS granted_by_email TEXT NOT NULL DEFAULT ''`,
|
|
641
|
+
);
|
|
642
|
+
await ensureColumnExists(
|
|
643
|
+
"workspace_connection_grants",
|
|
644
|
+
"owner_email",
|
|
645
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS owner_email TEXT NOT NULL DEFAULT ''`,
|
|
646
|
+
);
|
|
647
|
+
await ensureColumnExists(
|
|
648
|
+
"workspace_connection_grants",
|
|
649
|
+
"org_id",
|
|
650
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS org_id TEXT`,
|
|
651
|
+
);
|
|
652
|
+
await ensureColumnExists(
|
|
653
|
+
"workspace_connection_grants",
|
|
654
|
+
"created_at",
|
|
655
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS created_at ${intType()} NOT NULL DEFAULT 0`,
|
|
656
|
+
);
|
|
657
|
+
await ensureColumnExists(
|
|
658
|
+
"workspace_connection_grants",
|
|
659
|
+
"updated_at",
|
|
660
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS updated_at ${intType()} NOT NULL DEFAULT 0`,
|
|
661
|
+
);
|
|
662
|
+
await ensureColumnExists(
|
|
663
|
+
"workspace_connection_grants",
|
|
664
|
+
"last_used_at",
|
|
665
|
+
`ALTER TABLE workspace_connection_grants ADD COLUMN IF NOT EXISTS last_used_at ${intType()}`,
|
|
666
|
+
);
|
|
667
|
+
await ensureIndexExists(
|
|
668
|
+
"idx_workspace_connection_grants_connection_app",
|
|
669
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_connection_grants_connection_app ON ${grantsTable} (connection_id, app_id)`,
|
|
670
|
+
);
|
|
671
|
+
await ensureIndexExists(
|
|
672
|
+
"idx_workspace_connection_grants_scope_app",
|
|
673
|
+
`CREATE INDEX IF NOT EXISTS idx_workspace_connection_grants_scope_app ON ${grantsTable} (org_id, owner_email, app_id)`,
|
|
674
|
+
);
|
|
675
|
+
await ensureIndexExists(
|
|
676
|
+
"idx_workspace_connection_grants_updated_at",
|
|
677
|
+
`CREATE INDEX IF NOT EXISTS idx_workspace_connection_grants_updated_at ON ${grantsTable} (updated_at)`,
|
|
678
|
+
);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
519
681
|
|
|
682
|
+
// SQLite (local dev): no ACCESS EXCLUSIVE lock problem — keep existing
|
|
683
|
+
// retryOnDdlRace + ensureColumn behaviour.
|
|
684
|
+
await retryOnDdlRace(() => client.execute(createConnectionsSql));
|
|
685
|
+
await ensureWorkspaceConnectionColumns(client, table);
|
|
686
|
+
await retryOnDdlRace(() =>
|
|
687
|
+
client.execute(
|
|
688
|
+
`CREATE INDEX IF NOT EXISTS idx_workspace_connections_scope_provider ON ${table} (org_id, owner_email, provider)`,
|
|
689
|
+
),
|
|
690
|
+
);
|
|
691
|
+
await retryOnDdlRace(() =>
|
|
692
|
+
client.execute(
|
|
693
|
+
`CREATE INDEX IF NOT EXISTS idx_workspace_connections_updated_at ON ${table} (updated_at)`,
|
|
694
|
+
),
|
|
695
|
+
);
|
|
696
|
+
await retryOnDdlRace(() => client.execute(createGrantsSql));
|
|
697
|
+
await ensureWorkspaceConnectionGrantColumns(client, grantsTable);
|
|
520
698
|
await retryOnDdlRace(() =>
|
|
521
699
|
client.execute(
|
|
522
700
|
`CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_connection_grants_connection_app ON ${grantsTable} (connection_id, app_id)`,
|
|
@@ -267,8 +267,6 @@ export function renderBookingOgImageSvg(input: BookingOgImageInput): string {
|
|
|
267
267
|
</defs>
|
|
268
268
|
<rect width="${WIDTH}" height="${HEIGHT}" fill="${BG}"/>
|
|
269
269
|
<rect width="${WIDTH}" height="${HEIGHT}" fill="url(#grid)"/>
|
|
270
|
-
<rect x="64" y="64" width="1072" height="502" rx="28" fill="${BG}" fill-opacity="0.72" stroke="${BORDER}" stroke-width="1"/>
|
|
271
|
-
<path d="M80 154 H1120" stroke="${BORDER}"/>
|
|
272
270
|
<g transform="translate(80 86)">
|
|
273
271
|
<g transform="scale(0.62)">
|
|
274
272
|
${LOGO_MARK}
|
|
@@ -168,8 +168,6 @@ export function renderFormOgImageSvg(input: FormOgImageInput = {}): string {
|
|
|
168
168
|
</defs>
|
|
169
169
|
<rect width="${WIDTH}" height="${HEIGHT}" fill="${BG}"/>
|
|
170
170
|
<rect width="${WIDTH}" height="${HEIGHT}" fill="url(#grid)"/>
|
|
171
|
-
<rect x="64" y="64" width="1072" height="502" rx="28" fill="${BG}" fill-opacity="0.72" stroke="${BORDER}" stroke-width="1"/>
|
|
172
|
-
<path d="M80 154 H1120" stroke="${BORDER}"/>
|
|
173
171
|
<g transform="translate(80 86)">
|
|
174
172
|
<g transform="scale(0.62)">
|
|
175
173
|
${LOGO_MARK}
|