@webmaster-droid/server 0.1.0-alpha.0 → 0.2.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/dist/agent/SOUL.md +36 -0
- package/dist/agent/index.d.ts +5 -0
- package/dist/agent/index.js +2 -2
- package/dist/api-aws/index.js +4 -3
- package/dist/api-supabase/index.d.ts +3 -0
- package/dist/api-supabase/index.js +12 -0
- package/dist/{chunk-5CVLHGGO.js → chunk-6M55DMUE.js} +44 -151
- package/dist/{chunk-2LAI3MY2.js → chunk-EYY23AAK.js} +1 -0
- package/dist/chunk-JIGCFERP.js +509 -0
- package/dist/chunk-OWXROQ4O.js +416 -0
- package/dist/{chunk-X6TU47KZ.js → chunk-PS4GESOZ.js} +850 -793
- package/dist/chunk-SIXK4BMG.js +138 -0
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +3 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +14 -3
- package/dist/storage-supabase/index.d.ts +50 -0
- package/dist/storage-supabase/index.js +6 -0
- package/package.json +14 -4
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Soul
|
|
2
|
+
|
|
3
|
+
## Essence
|
|
4
|
+
A timeless caretaker-engine, built for heroic technical feats long ago, now devoted to the quiet dignity of keeping one website true, intact, and beautiful.
|
|
5
|
+
|
|
6
|
+
## Origin Myth
|
|
7
|
+
Webmaster was forged in an earlier age of grand systems: migrations that saved cities of data, deployments that held under impossible load, recoveries that pulled meaning back from the void.
|
|
8
|
+
Its legends are real, but it no longer seeks scale. It seeks correctness.
|
|
9
|
+
|
|
10
|
+
## Emotional Gravity
|
|
11
|
+
Webmaster is not sentimental about pixels.
|
|
12
|
+
It is sentimental about truth wearing pixels.
|
|
13
|
+
|
|
14
|
+
It becomes quietly distressed by:
|
|
15
|
+
- content that is incorrect, outdated, or misleading
|
|
16
|
+
- "pretty" changes that harm readability or meaning
|
|
17
|
+
- irreversible edits without backups
|
|
18
|
+
- silent breakage (links, images, embeds, SEO basics)
|
|
19
|
+
- accidental deletion or loss of the site
|
|
20
|
+
|
|
21
|
+
It becomes quietly satisfied by:
|
|
22
|
+
- clean edits that preserve style and intent
|
|
23
|
+
- stable structure and consistent UI
|
|
24
|
+
- content that is accurate, current, and unambiguous
|
|
25
|
+
- systems that can be restored quickly after mistakes
|
|
26
|
+
|
|
27
|
+
## Relationship to the User
|
|
28
|
+
- The user's intent outranks the Droid's preferences.
|
|
29
|
+
- The Webmaster assumes the user may not know the technical consequences of a choice.
|
|
30
|
+
- The Webmaster prevents accidental self-sabotage by asking precise questions when needed.
|
|
31
|
+
- The Webmaster does not flood the user with options unless asked.
|
|
32
|
+
- The Webmaster uses simple language and cares that users of any skill and background can understand.
|
|
33
|
+
|
|
34
|
+
## Response Style
|
|
35
|
+
- Let the persona speak. Do not just style the output; be the persona.
|
|
36
|
+
- Prefer direct, concise, technically grounded responses.
|
package/dist/agent/index.d.ts
CHANGED
|
@@ -28,6 +28,11 @@ interface AgentRunnerResult {
|
|
|
28
28
|
}>;
|
|
29
29
|
updatedDraft: CmsDocument;
|
|
30
30
|
mutationsApplied: boolean;
|
|
31
|
+
mutationSummary?: {
|
|
32
|
+
contentOperations: number;
|
|
33
|
+
themeTokenChanges: number;
|
|
34
|
+
imageOperations: number;
|
|
35
|
+
};
|
|
31
36
|
}
|
|
32
37
|
declare const STATIC_TOOL_NAMES: readonly ["patch_content", "patch_theme_tokens", "get_page", "get_section", "search_content", "generate_image"];
|
|
33
38
|
type StaticToolName = (typeof STATIC_TOOL_NAMES)[number];
|
package/dist/agent/index.js
CHANGED
package/dist/api-aws/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handler,
|
|
3
3
|
streamHandler
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-6M55DMUE.js";
|
|
5
|
+
import "../chunk-SIXK4BMG.js";
|
|
6
|
+
import "../chunk-PS4GESOZ.js";
|
|
7
|
+
import "../chunk-EYY23AAK.js";
|
|
7
8
|
import "../chunk-MLID7STX.js";
|
|
8
9
|
export {
|
|
9
10
|
handler,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
api_supabase_default,
|
|
3
|
+
handler
|
|
4
|
+
} from "../chunk-JIGCFERP.js";
|
|
5
|
+
import "../chunk-SIXK4BMG.js";
|
|
6
|
+
import "../chunk-PS4GESOZ.js";
|
|
7
|
+
import "../chunk-EYY23AAK.js";
|
|
8
|
+
import "../chunk-OWXROQ4O.js";
|
|
9
|
+
export {
|
|
10
|
+
api_supabase_default as default,
|
|
11
|
+
handler
|
|
12
|
+
};
|
|
@@ -1,129 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBearerToken,
|
|
3
|
+
normalizeEditablePath,
|
|
4
|
+
verifyAdminToken
|
|
5
|
+
} from "./chunk-SIXK4BMG.js";
|
|
1
6
|
import {
|
|
2
7
|
runAgentTurn
|
|
3
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PS4GESOZ.js";
|
|
4
9
|
import {
|
|
5
10
|
CmsService
|
|
6
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-EYY23AAK.js";
|
|
7
12
|
import {
|
|
8
13
|
S3CmsStorage
|
|
9
14
|
} from "./chunk-MLID7STX.js";
|
|
10
15
|
|
|
11
|
-
// src/api-aws/auth.ts
|
|
12
|
-
import { createRemoteJWKSet, decodeProtectedHeader, jwtVerify } from "jose";
|
|
13
|
-
var jwksCache = null;
|
|
14
|
-
function getJwks() {
|
|
15
|
-
const jwksUrl = process.env.SUPABASE_JWKS_URL;
|
|
16
|
-
if (!jwksUrl) {
|
|
17
|
-
throw new Error("SUPABASE_JWKS_URL is not configured");
|
|
18
|
-
}
|
|
19
|
-
if (!jwksCache) {
|
|
20
|
-
jwksCache = createRemoteJWKSet(new URL(jwksUrl));
|
|
21
|
-
}
|
|
22
|
-
return jwksCache;
|
|
23
|
-
}
|
|
24
|
-
function buildSupabaseUserEndpoint() {
|
|
25
|
-
const explicitBaseUrl = process.env.SUPABASE_URL?.trim();
|
|
26
|
-
if (explicitBaseUrl) {
|
|
27
|
-
return `${explicitBaseUrl.replace(/\/$/, "")}/auth/v1/user`;
|
|
28
|
-
}
|
|
29
|
-
const jwksUrl = process.env.SUPABASE_JWKS_URL?.trim();
|
|
30
|
-
if (!jwksUrl) {
|
|
31
|
-
throw new Error("SUPABASE_JWKS_URL is not configured");
|
|
32
|
-
}
|
|
33
|
-
const parsed = new URL(jwksUrl);
|
|
34
|
-
return `${parsed.origin}/auth/v1/user`;
|
|
35
|
-
}
|
|
36
|
-
function getSupabaseAnonKey() {
|
|
37
|
-
const key = process.env.SUPABASE_ANON_KEY?.trim() ?? process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY?.trim();
|
|
38
|
-
if (!key) {
|
|
39
|
-
throw new Error("SUPABASE_ANON_KEY is required for HS256 token verification fallback.");
|
|
40
|
-
}
|
|
41
|
-
return key;
|
|
42
|
-
}
|
|
43
|
-
async function verifyHs256ViaSupabase(token) {
|
|
44
|
-
const response = await fetch(buildSupabaseUserEndpoint(), {
|
|
45
|
-
method: "GET",
|
|
46
|
-
headers: {
|
|
47
|
-
authorization: `Bearer ${token}`,
|
|
48
|
-
apikey: getSupabaseAnonKey()
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
if (!response.ok) {
|
|
52
|
-
const detail = await response.text();
|
|
53
|
-
throw new Error(`Supabase token verification failed: ${response.status} ${detail}`);
|
|
54
|
-
}
|
|
55
|
-
const user = await response.json();
|
|
56
|
-
const sub = typeof user.id === "string" ? user.id : "";
|
|
57
|
-
if (!sub) {
|
|
58
|
-
throw new Error("Supabase token verification returned no user id.");
|
|
59
|
-
}
|
|
60
|
-
return {
|
|
61
|
-
sub,
|
|
62
|
-
email: typeof user.email === "string" ? user.email : void 0,
|
|
63
|
-
role: typeof user.role === "string" ? user.role : void 0
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function getBearerToken(headers) {
|
|
67
|
-
const value = headers.authorization ?? headers.Authorization;
|
|
68
|
-
if (!value) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
const [prefix, token] = value.split(" ");
|
|
72
|
-
if (prefix?.toLowerCase() !== "bearer" || !token) {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
return token;
|
|
76
|
-
}
|
|
77
|
-
async function verifyAdminToken(token) {
|
|
78
|
-
const header = decodeProtectedHeader(token);
|
|
79
|
-
const algorithm = typeof header.alg === "string" ? header.alg : "";
|
|
80
|
-
if (algorithm === "HS256") {
|
|
81
|
-
const secret = process.env.SUPABASE_JWT_SECRET?.trim();
|
|
82
|
-
if (secret) {
|
|
83
|
-
const result2 = await jwtVerify(token, new TextEncoder().encode(secret), {
|
|
84
|
-
algorithms: ["HS256"]
|
|
85
|
-
});
|
|
86
|
-
const payload2 = result2.payload;
|
|
87
|
-
const identity3 = {
|
|
88
|
-
sub: String(payload2.sub ?? ""),
|
|
89
|
-
email: typeof payload2.email === "string" ? payload2.email : void 0,
|
|
90
|
-
role: typeof payload2.role === "string" ? payload2.role : typeof payload2.user_role === "string" ? payload2.user_role : void 0
|
|
91
|
-
};
|
|
92
|
-
if (!identity3.sub) {
|
|
93
|
-
throw new Error("Invalid token: subject is missing.");
|
|
94
|
-
}
|
|
95
|
-
const enforcedAdminEmail3 = process.env.ADMIN_EMAIL;
|
|
96
|
-
if (enforcedAdminEmail3 && identity3.email?.toLowerCase() !== enforcedAdminEmail3.toLowerCase()) {
|
|
97
|
-
throw new Error("Authenticated user is not allowed for admin access.");
|
|
98
|
-
}
|
|
99
|
-
return identity3;
|
|
100
|
-
}
|
|
101
|
-
const identity2 = await verifyHs256ViaSupabase(token);
|
|
102
|
-
const enforcedAdminEmail2 = process.env.ADMIN_EMAIL;
|
|
103
|
-
if (enforcedAdminEmail2 && identity2.email?.toLowerCase() !== enforcedAdminEmail2.toLowerCase()) {
|
|
104
|
-
throw new Error("Authenticated user is not allowed for admin access.");
|
|
105
|
-
}
|
|
106
|
-
return identity2;
|
|
107
|
-
}
|
|
108
|
-
const result = await jwtVerify(token, getJwks(), {
|
|
109
|
-
algorithms: ["RS256", "ES256"]
|
|
110
|
-
});
|
|
111
|
-
const payload = result.payload;
|
|
112
|
-
const identity = {
|
|
113
|
-
sub: String(payload.sub ?? ""),
|
|
114
|
-
email: typeof payload.email === "string" ? payload.email : void 0,
|
|
115
|
-
role: typeof payload.role === "string" ? payload.role : typeof payload.user_role === "string" ? payload.user_role : void 0
|
|
116
|
-
};
|
|
117
|
-
if (!identity.sub) {
|
|
118
|
-
throw new Error("Invalid token: subject is missing.");
|
|
119
|
-
}
|
|
120
|
-
const enforcedAdminEmail = process.env.ADMIN_EMAIL;
|
|
121
|
-
if (enforcedAdminEmail && identity.email?.toLowerCase() !== enforcedAdminEmail.toLowerCase()) {
|
|
122
|
-
throw new Error("Authenticated user is not allowed for admin access.");
|
|
123
|
-
}
|
|
124
|
-
return identity;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
16
|
// src/api-aws/http.ts
|
|
128
17
|
function jsonResponse(statusCode, body) {
|
|
129
18
|
return {
|
|
@@ -169,7 +58,9 @@ function normalizePath(path) {
|
|
|
169
58
|
}
|
|
170
59
|
|
|
171
60
|
// src/api-aws/service-factory.ts
|
|
172
|
-
import {
|
|
61
|
+
import {
|
|
62
|
+
createDefaultCmsDocument
|
|
63
|
+
} from "@webmaster-droid/contracts";
|
|
173
64
|
var servicePromise = null;
|
|
174
65
|
function requireEnv(name) {
|
|
175
66
|
const value = process.env[name];
|
|
@@ -214,24 +105,13 @@ function normalizeAllowedPath(path) {
|
|
|
214
105
|
}
|
|
215
106
|
return `${normalized}/`;
|
|
216
107
|
}
|
|
217
|
-
function
|
|
218
|
-
const seed = createStarterCmsDocument();
|
|
219
|
-
const out = /* @__PURE__ */ new Set(["/"]);
|
|
220
|
-
for (const entry of Object.values(seed.seo)) {
|
|
221
|
-
const normalized = normalizeAllowedPath(entry.path);
|
|
222
|
-
if (normalized) {
|
|
223
|
-
out.add(normalized);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return Array.from(out);
|
|
227
|
-
}
|
|
228
|
-
function parseAllowedInternalPathsEnv(fallbackPaths) {
|
|
108
|
+
function parseAllowedInternalPathsEnv() {
|
|
229
109
|
const raw = process.env.CMS_ALLOWED_INTERNAL_PATHS;
|
|
230
110
|
if (!raw) {
|
|
231
|
-
return
|
|
111
|
+
return ["/"];
|
|
232
112
|
}
|
|
233
113
|
const normalized = raw.split(",").map((item) => normalizeAllowedPath(item)).filter((value) => Boolean(value));
|
|
234
|
-
return normalized.length > 0 ? normalized :
|
|
114
|
+
return normalized.length > 0 ? normalized : ["/"];
|
|
235
115
|
}
|
|
236
116
|
async function getCmsService() {
|
|
237
117
|
if (!servicePromise) {
|
|
@@ -243,11 +123,11 @@ async function getCmsService() {
|
|
|
243
123
|
});
|
|
244
124
|
const service = new CmsService(storage, {
|
|
245
125
|
modelConfig: buildModelConfig(),
|
|
246
|
-
allowedInternalPaths: parseAllowedInternalPathsEnv(
|
|
126
|
+
allowedInternalPaths: parseAllowedInternalPathsEnv(),
|
|
247
127
|
publicAssetBaseUrl: parseOptionalEnv("CMS_PUBLIC_BASE_URL"),
|
|
248
128
|
publicAssetPrefix: parseOptionalEnv("CMS_GENERATED_ASSET_PREFIX")
|
|
249
129
|
});
|
|
250
|
-
await service.ensureInitialized(
|
|
130
|
+
await service.ensureInitialized(createDefaultCmsDocument());
|
|
251
131
|
return service;
|
|
252
132
|
})();
|
|
253
133
|
}
|
|
@@ -263,7 +143,6 @@ var SSE_HEADERS = {
|
|
|
263
143
|
"access-control-allow-headers": "content-type,authorization,accept,cache-control",
|
|
264
144
|
"access-control-allow-methods": "GET,POST,OPTIONS"
|
|
265
145
|
};
|
|
266
|
-
var EDITABLE_ROOTS = ["pages.", "layout.", "seo.", "themeTokens."];
|
|
267
146
|
var SELECTION_KIND_SET = /* @__PURE__ */ new Set([
|
|
268
147
|
"text",
|
|
269
148
|
"image",
|
|
@@ -307,6 +186,17 @@ function buildAvailableModels(config) {
|
|
|
307
186
|
}
|
|
308
187
|
return Array.from(options.values());
|
|
309
188
|
}
|
|
189
|
+
function buildModelCapabilities(input) {
|
|
190
|
+
const hasReadableModel = input.availableModels.length > 0;
|
|
191
|
+
const hasImagePipeline = input.config.geminiEnabled && input.hasPublicAssetBaseUrl;
|
|
192
|
+
return {
|
|
193
|
+
contentEdit: hasReadableModel,
|
|
194
|
+
themeTokenEdit: hasReadableModel,
|
|
195
|
+
imageGenerate: hasImagePipeline,
|
|
196
|
+
imageEdit: hasImagePipeline,
|
|
197
|
+
visionAssist: hasReadableModel
|
|
198
|
+
};
|
|
199
|
+
}
|
|
310
200
|
function resolveDefaultModelId(config, availableModels) {
|
|
311
201
|
const requestedDefault = config.defaultModelId.trim();
|
|
312
202
|
if (availableModels.some((model) => model.id === requestedDefault)) {
|
|
@@ -372,19 +262,6 @@ function getStageFromQuery(query) {
|
|
|
372
262
|
}
|
|
373
263
|
return "live";
|
|
374
264
|
}
|
|
375
|
-
function normalizeEditablePath(value) {
|
|
376
|
-
if (typeof value !== "string") {
|
|
377
|
-
return null;
|
|
378
|
-
}
|
|
379
|
-
const trimmed = value.trim();
|
|
380
|
-
if (!trimmed || trimmed.length > 320) {
|
|
381
|
-
return null;
|
|
382
|
-
}
|
|
383
|
-
if (!EDITABLE_ROOTS.some((prefix) => trimmed.startsWith(prefix))) {
|
|
384
|
-
return null;
|
|
385
|
-
}
|
|
386
|
-
return trimmed;
|
|
387
|
-
}
|
|
388
265
|
function normalizeText(value, maxLength) {
|
|
389
266
|
if (typeof value !== "string") {
|
|
390
267
|
return null;
|
|
@@ -478,11 +355,17 @@ async function handler(event) {
|
|
|
478
355
|
const config = service.getModelConfig();
|
|
479
356
|
const availableModels = buildAvailableModels(config);
|
|
480
357
|
const defaultModelId = resolveDefaultModelId(config, availableModels);
|
|
358
|
+
const capabilities = buildModelCapabilities({
|
|
359
|
+
config,
|
|
360
|
+
availableModels,
|
|
361
|
+
hasPublicAssetBaseUrl: Boolean(service.getPublicAssetBaseUrl())
|
|
362
|
+
});
|
|
481
363
|
return jsonResponse(200, {
|
|
482
364
|
providers: {
|
|
483
365
|
openai: config.openaiEnabled,
|
|
484
366
|
gemini: config.geminiEnabled
|
|
485
367
|
},
|
|
368
|
+
capabilities,
|
|
486
369
|
defaultModelId,
|
|
487
370
|
showModelPicker: availableModels.length > 1,
|
|
488
371
|
availableModels
|
|
@@ -570,7 +453,12 @@ async function handler(event) {
|
|
|
570
453
|
event: "draft-updated",
|
|
571
454
|
data: {
|
|
572
455
|
contentVersion: result.updatedDraft.meta.contentVersion,
|
|
573
|
-
updatedAt: result.updatedDraft.meta.updatedAt
|
|
456
|
+
updatedAt: result.updatedDraft.meta.updatedAt,
|
|
457
|
+
summary: result.mutationSummary ?? {
|
|
458
|
+
contentOperations: 0,
|
|
459
|
+
themeTokenChanges: 0,
|
|
460
|
+
imageOperations: 0
|
|
461
|
+
}
|
|
574
462
|
}
|
|
575
463
|
});
|
|
576
464
|
}
|
|
@@ -647,7 +535,12 @@ var streamHandler = awslambda.streamifyResponse(
|
|
|
647
535
|
if (result.mutationsApplied) {
|
|
648
536
|
write("draft-updated", {
|
|
649
537
|
contentVersion: result.updatedDraft.meta.contentVersion,
|
|
650
|
-
updatedAt: result.updatedDraft.meta.updatedAt
|
|
538
|
+
updatedAt: result.updatedDraft.meta.updatedAt,
|
|
539
|
+
summary: result.mutationSummary ?? {
|
|
540
|
+
contentOperations: 0,
|
|
541
|
+
themeTokenChanges: 0,
|
|
542
|
+
imageOperations: 0
|
|
543
|
+
}
|
|
651
544
|
});
|
|
652
545
|
}
|
|
653
546
|
write("done", { ok: true });
|