@treeseed/core 0.4.13 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/adapters/notification.d.ts +16 -1
- package/dist/agents/adapters/notification.js +31 -1
- package/dist/agents/adapters/research.d.ts +13 -1
- package/dist/agents/adapters/research.js +35 -1
- package/dist/agents/contracts/run.d.ts +1 -0
- package/dist/agents/kernel/agent-kernel.d.ts +2 -2
- package/dist/agents/kernel/agent-kernel.js +10 -3
- package/dist/agents/kernel/trigger-resolver.d.ts +1 -0
- package/dist/agents/kernel/trigger-resolver.js +5 -1
- package/dist/agents/runtime-types.d.ts +1 -0
- package/dist/api/app.js +10 -0
- package/dist/api/auth/d1-store.js +5 -0
- package/dist/api/auth/memory-provider.js +6 -1
- package/dist/api/auth/rbac.d.ts +2 -2
- package/dist/api/auth/rbac.js +2 -0
- package/dist/api/capabilities.d.ts +9 -0
- package/dist/api/capabilities.js +33 -0
- package/dist/api/operations-routes.d.ts +4 -0
- package/dist/api/operations-routes.js +49 -1
- package/dist/api/project-routes.d.ts +8 -0
- package/dist/api/project-routes.js +586 -0
- package/dist/api/types.d.ts +7 -0
- package/dist/components/site/NotesList.astro +13 -2
- package/dist/components/site/PublishedContentBody.astro +5 -0
- package/dist/content.js +77 -9
- package/dist/dev.d.ts +2 -2
- package/dist/dev.js +0 -15
- package/dist/env.yaml +39 -26
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -1
- package/dist/launch.d.ts +3 -0
- package/dist/launch.js +8 -0
- package/dist/layouts/AuthoredEntryLayout.astro +76 -28
- package/dist/layouts/ProfileLayout.astro +9 -5
- package/dist/middleware.js +11 -0
- package/dist/pages/[slug].astro +10 -6
- package/dist/pages/agents/[slug].astro +17 -7
- package/dist/pages/agents/index.astro +2 -1
- package/dist/pages/books/[slug].astro +10 -5
- package/dist/pages/books/index.astro +4 -1
- package/dist/pages/decisions/[slug].astro +73 -0
- package/dist/pages/decisions/index.astro +47 -0
- package/dist/pages/docs-runtime/[...slug].astro +102 -0
- package/dist/pages/docs-runtime/index.astro +89 -0
- package/dist/pages/feed.xml.js +2 -1
- package/dist/pages/index.astro +160 -16
- package/dist/pages/notes/[slug].astro +10 -5
- package/dist/pages/notes/index.astro +6 -3
- package/dist/pages/objectives/[slug].astro +27 -9
- package/dist/pages/objectives/index.astro +19 -2
- package/dist/pages/people/[slug].astro +17 -7
- package/dist/pages/people/index.astro +2 -1
- package/dist/pages/proposals/[slug].astro +72 -0
- package/dist/pages/proposals/index.astro +47 -0
- package/dist/pages/questions/[slug].astro +27 -9
- package/dist/pages/questions/index.astro +19 -2
- package/dist/scripts/dev-platform.js +0 -1
- package/dist/scripts/release-verify.js +29 -2
- package/dist/scripts/tenant-build.js +4 -1
- package/dist/scripts/tenant-check.js +4 -1
- package/dist/services/agents.d.ts +1 -12
- package/dist/services/agents.js +28 -9
- package/dist/services/index.d.ts +0 -2
- package/dist/services/index.js +0 -6
- package/dist/services/manager.d.ts +4 -4
- package/dist/services/manager.js +123 -50
- package/dist/services/workday-report.d.ts +3 -3
- package/dist/services/workday-start.d.ts +3 -3
- package/dist/services/worker-capacity.d.ts +58 -0
- package/dist/services/worker-capacity.js +208 -0
- package/dist/services/worker.js +70 -13
- package/dist/site.js +18 -5
- package/dist/tenant/runtime-config.js +8 -1
- package/dist/utils/hub-content.js +14 -0
- package/dist/utils/published-content.js +13 -0
- package/dist/utils/site-config.js +20 -0
- package/dist/utils/site-content-runtime.js +185 -0
- package/dist/utils/web-cache.js +149 -0
- package/package.json +11 -6
- package/scripts/verify-driver.mjs +34 -0
- package/templates/github/deploy.workflow.yml +11 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { createWorkerPoolScaler } from "./worker-pool-scaler.js";
|
|
2
|
+
function integerFromEnv(name, fallback) {
|
|
3
|
+
const value = process.env[name];
|
|
4
|
+
if (!value) return fallback;
|
|
5
|
+
const parsed = Number.parseInt(value, 10);
|
|
6
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
7
|
+
}
|
|
8
|
+
function envValue(name) {
|
|
9
|
+
const value = process.env[name]?.trim();
|
|
10
|
+
return value ? value : "";
|
|
11
|
+
}
|
|
12
|
+
function parseJson(value, fallback) {
|
|
13
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
14
|
+
return fallback;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(value);
|
|
18
|
+
} catch {
|
|
19
|
+
return fallback;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function readNumber(record, ...keys) {
|
|
23
|
+
for (const key of keys) {
|
|
24
|
+
const value = record[key];
|
|
25
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === "string" && value.trim()) {
|
|
29
|
+
const parsed = Number.parseFloat(value);
|
|
30
|
+
if (Number.isFinite(parsed)) {
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function asRecords(value) {
|
|
38
|
+
return Array.isArray(value) ? value : [];
|
|
39
|
+
}
|
|
40
|
+
function resolveAutoscalePolicyFromEnv() {
|
|
41
|
+
return {
|
|
42
|
+
minWorkers: integerFromEnv("TREESEED_AGENT_POOL_MIN_WORKERS", 0),
|
|
43
|
+
maxWorkers: integerFromEnv("TREESEED_AGENT_POOL_MAX_WORKERS", 1),
|
|
44
|
+
targetQueueDepth: Math.max(1, integerFromEnv("TREESEED_AGENT_POOL_TARGET_QUEUE_DEPTH", 1)),
|
|
45
|
+
cooldownSeconds: Math.max(0, integerFromEnv("TREESEED_AGENT_POOL_COOLDOWN_SECONDS", 60))
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function resolveWorkerPoolIdentityFromEnv(projectId) {
|
|
49
|
+
const environment = envValue("TREESEED_DEPLOY_ENVIRONMENT") || (process.env.NODE_ENV === "production" ? "prod" : "local");
|
|
50
|
+
const resolvedProjectId = projectId?.trim() || envValue("TREESEED_PROJECT_ID") || "treeseed-market";
|
|
51
|
+
return {
|
|
52
|
+
projectId: resolvedProjectId,
|
|
53
|
+
environment,
|
|
54
|
+
poolName: envValue("TREESEED_AGENT_POOL_NAME") || `${resolvedProjectId}-${environment}`
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function computeDesiredWorkerCount(autoscale, metrics) {
|
|
58
|
+
const { minWorkers, maxWorkers, targetQueueDepth } = autoscale;
|
|
59
|
+
if (metrics.queuedCount <= 0 && metrics.activeLeases <= 0) {
|
|
60
|
+
return minWorkers;
|
|
61
|
+
}
|
|
62
|
+
const requiredByQueue = Math.ceil(metrics.queuedCount / Math.max(1, targetQueueDepth));
|
|
63
|
+
const minimumActive = metrics.activeLeases > 0 ? 1 : 0;
|
|
64
|
+
return Math.max(minWorkers, Math.min(maxWorkers, Math.max(requiredByQueue, minimumActive)));
|
|
65
|
+
}
|
|
66
|
+
function applyScaleCooldown(autoscale, latestDecision, nextDesired, now) {
|
|
67
|
+
if (!latestDecision) {
|
|
68
|
+
return nextDesired;
|
|
69
|
+
}
|
|
70
|
+
if (nextDesired >= latestDecision.desiredWorkers) {
|
|
71
|
+
return nextDesired;
|
|
72
|
+
}
|
|
73
|
+
const cooldownMs = Math.max(0, autoscale.cooldownSeconds) * 1e3;
|
|
74
|
+
if (cooldownMs === 0) {
|
|
75
|
+
return nextDesired;
|
|
76
|
+
}
|
|
77
|
+
const lastChangedAt = new Date(latestDecision.createdAt);
|
|
78
|
+
if (!Number.isFinite(lastChangedAt.valueOf())) {
|
|
79
|
+
return nextDesired;
|
|
80
|
+
}
|
|
81
|
+
return now.valueOf() - lastChangedAt.valueOf() < cooldownMs ? latestDecision.desiredWorkers : nextDesired;
|
|
82
|
+
}
|
|
83
|
+
function applyInteractiveWakeUpOverride(options) {
|
|
84
|
+
if (options.priorityClass !== "interactive") {
|
|
85
|
+
return options.desiredWorkers;
|
|
86
|
+
}
|
|
87
|
+
if (options.queuedCount <= 0 || options.currentWorkers > 0 || options.desiredWorkers > 0) {
|
|
88
|
+
return options.desiredWorkers;
|
|
89
|
+
}
|
|
90
|
+
return 1;
|
|
91
|
+
}
|
|
92
|
+
async function collectTaskMetrics(sdk, workDayId) {
|
|
93
|
+
const [queuedEnvelope, activeEnvelope] = await Promise.all([
|
|
94
|
+
sdk.searchTasks({
|
|
95
|
+
workDayId: workDayId ?? void 0,
|
|
96
|
+
limit: 500,
|
|
97
|
+
state: ["pending", "queued"]
|
|
98
|
+
}),
|
|
99
|
+
sdk.searchTasks({
|
|
100
|
+
workDayId: workDayId ?? void 0,
|
|
101
|
+
limit: 500,
|
|
102
|
+
state: ["claimed", "running"]
|
|
103
|
+
})
|
|
104
|
+
]);
|
|
105
|
+
const queuedTasks = asRecords(queuedEnvelope.payload);
|
|
106
|
+
const activeTasks = asRecords(activeEnvelope.payload);
|
|
107
|
+
const queuedCredits = queuedTasks.reduce((total, task) => {
|
|
108
|
+
const payload = parseJson(String(task.payloadJson ?? "{}"), {});
|
|
109
|
+
const credits = readNumber(payload, "estimatedCredits") ?? 1;
|
|
110
|
+
return total + credits;
|
|
111
|
+
}, 0);
|
|
112
|
+
return {
|
|
113
|
+
queuedTasks,
|
|
114
|
+
activeTasks,
|
|
115
|
+
queuedCount: queuedTasks.length,
|
|
116
|
+
activeLeases: activeTasks.length,
|
|
117
|
+
queuedCredits
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async function enqueueTaskAndEnsureCapacity(sdk, request) {
|
|
121
|
+
await request.enqueueTask(sdk, {
|
|
122
|
+
taskId: request.taskId,
|
|
123
|
+
queueName: request.queueName,
|
|
124
|
+
deliveryDelaySeconds: request.deliveryDelaySeconds,
|
|
125
|
+
actor: request.actor
|
|
126
|
+
});
|
|
127
|
+
const now = request.now ?? /* @__PURE__ */ new Date();
|
|
128
|
+
const identity = request.identity ?? resolveWorkerPoolIdentityFromEnv(request.projectId);
|
|
129
|
+
const autoscale = request.autoscale ?? resolveAutoscalePolicyFromEnv();
|
|
130
|
+
const scaler = request.scaler ?? createWorkerPoolScaler();
|
|
131
|
+
const metrics = await collectTaskMetrics(sdk);
|
|
132
|
+
const latestScaleDecision = await sdk.getLatestScaleDecision(identity.projectId, identity.environment, identity.poolName);
|
|
133
|
+
const currentWorkers = Math.max(0, Number(latestScaleDecision.payload?.desiredWorkers ?? 0));
|
|
134
|
+
const rawDesiredWorkers = computeDesiredWorkerCount(autoscale, metrics);
|
|
135
|
+
const desiredAfterCooldown = applyScaleCooldown(autoscale, latestScaleDecision.payload, rawDesiredWorkers, now);
|
|
136
|
+
const desiredWorkers = applyInteractiveWakeUpOverride({
|
|
137
|
+
priorityClass: request.priorityClass ?? "background",
|
|
138
|
+
queuedCount: metrics.queuedCount,
|
|
139
|
+
currentWorkers,
|
|
140
|
+
desiredWorkers: desiredAfterCooldown
|
|
141
|
+
});
|
|
142
|
+
const coldStartTriggered = (request.priorityClass ?? "background") === "interactive" && currentWorkers <= 0 && desiredWorkers > 0;
|
|
143
|
+
const scaleReason = desiredWorkers !== desiredAfterCooldown ? "interactive_cold_start" : coldStartTriggered ? "interactive_cold_start" : desiredWorkers !== rawDesiredWorkers ? "cooldown_hold" : request.priorityClass === "interactive" ? "interactive_enqueue" : "enqueue";
|
|
144
|
+
const recordedScaleDecision = await sdk.recordScaleDecision({
|
|
145
|
+
projectId: identity.projectId,
|
|
146
|
+
environment: identity.environment,
|
|
147
|
+
poolName: identity.poolName,
|
|
148
|
+
workDayId: null,
|
|
149
|
+
desiredWorkers,
|
|
150
|
+
observedQueueDepth: metrics.queuedCount,
|
|
151
|
+
observedActiveLeases: metrics.activeLeases,
|
|
152
|
+
reason: scaleReason,
|
|
153
|
+
metadata: {
|
|
154
|
+
priorityClass: request.priorityClass ?? "background",
|
|
155
|
+
taskId: request.taskId
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
const scaleDecision = recordedScaleDecision.payload ?? {
|
|
159
|
+
projectId: identity.projectId,
|
|
160
|
+
environment: identity.environment,
|
|
161
|
+
poolName: identity.poolName,
|
|
162
|
+
workDayId: null,
|
|
163
|
+
desiredWorkers,
|
|
164
|
+
observedQueueDepth: metrics.queuedCount,
|
|
165
|
+
observedActiveLeases: metrics.activeLeases,
|
|
166
|
+
reason: scaleReason,
|
|
167
|
+
metadata: {
|
|
168
|
+
priorityClass: request.priorityClass ?? "background",
|
|
169
|
+
taskId: request.taskId
|
|
170
|
+
},
|
|
171
|
+
createdAt: now.toISOString()
|
|
172
|
+
};
|
|
173
|
+
let scaleResult;
|
|
174
|
+
try {
|
|
175
|
+
scaleResult = await scaler.scale(scaleDecision);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
scaleResult = {
|
|
178
|
+
applied: false,
|
|
179
|
+
provider: "error",
|
|
180
|
+
desiredWorkers,
|
|
181
|
+
metadata: {
|
|
182
|
+
reason: "scale_failed",
|
|
183
|
+
error: error instanceof Error ? error.message : String(error)
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
ok: true,
|
|
189
|
+
taskId: request.taskId,
|
|
190
|
+
queued: true,
|
|
191
|
+
workerState: currentWorkers > 0 ? "warm" : desiredWorkers > 0 ? "cold_starting" : "warm",
|
|
192
|
+
desiredWorkers,
|
|
193
|
+
scaleApplied: scaleResult.applied,
|
|
194
|
+
scaleReason,
|
|
195
|
+
scaleDecision,
|
|
196
|
+
scaleResult,
|
|
197
|
+
metrics
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
export {
|
|
201
|
+
applyInteractiveWakeUpOverride,
|
|
202
|
+
applyScaleCooldown,
|
|
203
|
+
collectTaskMetrics,
|
|
204
|
+
computeDesiredWorkerCount,
|
|
205
|
+
enqueueTaskAndEnsureCapacity,
|
|
206
|
+
resolveAutoscalePolicyFromEnv,
|
|
207
|
+
resolveWorkerPoolIdentityFromEnv
|
|
208
|
+
};
|
package/dist/services/worker.js
CHANGED
|
@@ -1,10 +1,70 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import {
|
|
3
|
+
import { AgentKernel } from "../agents/kernel/agent-kernel.js";
|
|
4
|
+
import { buildTaskContext, createQueueClient, createServiceSdk, resolveServiceRepoRoot, resolveWorkerConfig } from "./common.js";
|
|
5
|
+
function parseTaskPayload(task) {
|
|
6
|
+
const raw = typeof task?.payloadJson === "string" ? task.payloadJson : "{}";
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(raw);
|
|
9
|
+
} catch {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async function executeQueuedTask(options) {
|
|
14
|
+
const context = await buildTaskContext(options.sdk, options.taskId);
|
|
15
|
+
const task = context.task;
|
|
16
|
+
const payload = parseTaskPayload(task);
|
|
17
|
+
const executionKind = typeof payload.executionKind === "string" ? payload.executionKind : null;
|
|
18
|
+
if (executionKind === "workflow_dispatch" || executionKind === "sdk_dispatch") {
|
|
19
|
+
const namespace = typeof payload.namespace === "string" ? payload.namespace : "workflow";
|
|
20
|
+
const operation = typeof payload.operation === "string" ? payload.operation : "";
|
|
21
|
+
if (!operation) {
|
|
22
|
+
throw new Error(`Task ${options.taskId} does not define a dispatch operation.`);
|
|
23
|
+
}
|
|
24
|
+
const input = payload.input && typeof payload.input === "object" ? payload.input : {};
|
|
25
|
+
const result = await options.sdk.dispatch({
|
|
26
|
+
namespace,
|
|
27
|
+
operation,
|
|
28
|
+
input,
|
|
29
|
+
preferredMode: "prefer_local"
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
workerId: options.workerId,
|
|
33
|
+
queueAttempt: options.queueAttempt,
|
|
34
|
+
executionKind,
|
|
35
|
+
namespace,
|
|
36
|
+
operation,
|
|
37
|
+
result,
|
|
38
|
+
summary: {
|
|
39
|
+
status: "completed",
|
|
40
|
+
workerId: options.workerId,
|
|
41
|
+
summary: `Executed ${namespace}:${operation}`
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const agentSlug = typeof payload.agentSlug === "string" && payload.agentSlug ? payload.agentSlug : typeof context.agent?.slug === "string" && context.agent.slug ? context.agent.slug : typeof task?.agentId === "string" && task.agentId ? task.agentId : typeof task?.agent_id === "string" && task.agent_id ? task.agent_id : "";
|
|
46
|
+
if (!agentSlug) {
|
|
47
|
+
throw new Error(`Task ${options.taskId} does not resolve to an agent slug.`);
|
|
48
|
+
}
|
|
49
|
+
const invocation = payload.invocation && typeof payload.invocation === "object" ? payload.invocation : null;
|
|
50
|
+
const agentResult = await options.kernel.runAgent(agentSlug, invocation ? "manual" : "auto", invocation);
|
|
51
|
+
return {
|
|
52
|
+
workerId: options.workerId,
|
|
53
|
+
queueAttempt: options.queueAttempt,
|
|
54
|
+
agentSlug,
|
|
55
|
+
result: agentResult,
|
|
56
|
+
summary: {
|
|
57
|
+
status: agentResult.status,
|
|
58
|
+
workerId: options.workerId,
|
|
59
|
+
summary: agentResult.summary
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
4
63
|
async function runWorkerCycle() {
|
|
5
64
|
const sdk = createServiceSdk();
|
|
6
65
|
const queue = createQueueClient();
|
|
7
66
|
const config = resolveWorkerConfig();
|
|
67
|
+
const kernel = new AgentKernel(sdk, resolveServiceRepoRoot());
|
|
8
68
|
if (!queue) {
|
|
9
69
|
if (process.env.TREESEED_LOCAL_DEV_MODE?.trim()) {
|
|
10
70
|
return { ok: true, processed: 0, idle: true, reason: "queue_unconfigured" };
|
|
@@ -27,9 +87,6 @@ async function runWorkerCycle() {
|
|
|
27
87
|
leaseSeconds: config.leaseSeconds,
|
|
28
88
|
actor: "worker"
|
|
29
89
|
});
|
|
30
|
-
const context = await buildTaskContext(sdk, message.body.taskId);
|
|
31
|
-
const task = context.task;
|
|
32
|
-
const payload = task && typeof task.payloadJson === "string" ? JSON.parse(task.payloadJson) : {};
|
|
33
90
|
await sdk.recordTaskProgress({
|
|
34
91
|
id: message.body.taskId,
|
|
35
92
|
workerId: config.workerId,
|
|
@@ -40,17 +97,17 @@ async function runWorkerCycle() {
|
|
|
40
97
|
},
|
|
41
98
|
actor: "worker"
|
|
42
99
|
});
|
|
100
|
+
const output = await executeQueuedTask({
|
|
101
|
+
sdk,
|
|
102
|
+
kernel,
|
|
103
|
+
taskId: message.body.taskId,
|
|
104
|
+
workerId: config.workerId,
|
|
105
|
+
queueAttempt: message.attempts
|
|
106
|
+
});
|
|
43
107
|
await sdk.completeTask({
|
|
44
108
|
id: message.body.taskId,
|
|
45
|
-
output
|
|
46
|
-
|
|
47
|
-
queueAttempt: message.attempts,
|
|
48
|
-
payload
|
|
49
|
-
},
|
|
50
|
-
summary: {
|
|
51
|
-
status: "completed",
|
|
52
|
-
workerId: config.workerId
|
|
53
|
-
},
|
|
109
|
+
output,
|
|
110
|
+
summary: output.summary,
|
|
54
111
|
actor: "worker"
|
|
55
112
|
});
|
|
56
113
|
await queue.ack([message.leaseId]);
|
package/dist/site.js
CHANGED
|
@@ -11,6 +11,7 @@ import { buildTenantBookRuntime } from "@treeseed/sdk/platform/books-data";
|
|
|
11
11
|
import { getStarlightSidebarConfigFromRuntime } from "./utils/starlight-nav.js";
|
|
12
12
|
import { buildTenantThemeCss } from "./utils/theme.js";
|
|
13
13
|
import { loadTreeseedDeployConfig } from "@treeseed/sdk/platform/deploy-config";
|
|
14
|
+
import { getTreeseedContentServingMode } from "@treeseed/sdk/platform/deploy-runtime";
|
|
14
15
|
import { loadTreeseedPluginRuntime } from "@treeseed/sdk/platform/plugins";
|
|
15
16
|
import {
|
|
16
17
|
buildTreeseedSiteLayers,
|
|
@@ -48,8 +49,12 @@ const PACKAGE_ROUTE_ENTRIES = [
|
|
|
48
49
|
{ pattern: "/notes/[slug]", resourcePath: "pages/notes/[slug].astro", model: "notes" },
|
|
49
50
|
{ pattern: "/objectives", resourcePath: "pages/objectives/index.astro", model: "objectives" },
|
|
50
51
|
{ pattern: "/objectives/[slug]", resourcePath: "pages/objectives/[slug].astro", model: "objectives" },
|
|
52
|
+
{ pattern: "/proposals", resourcePath: "pages/proposals/index.astro", model: "proposals" },
|
|
53
|
+
{ pattern: "/proposals/[slug]", resourcePath: "pages/proposals/[slug].astro", model: "proposals" },
|
|
51
54
|
{ pattern: "/people", resourcePath: "pages/people/index.astro", model: "people" },
|
|
52
55
|
{ pattern: "/people/[slug]", resourcePath: "pages/people/[slug].astro", model: "people" },
|
|
56
|
+
{ pattern: "/decisions", resourcePath: "pages/decisions/index.astro", model: "decisions" },
|
|
57
|
+
{ pattern: "/decisions/[slug]", resourcePath: "pages/decisions/[slug].astro", model: "decisions" },
|
|
53
58
|
{ pattern: "/questions", resourcePath: "pages/questions/index.astro", model: "questions" },
|
|
54
59
|
{ pattern: "/questions/[slug]", resourcePath: "pages/questions/[slug].astro", model: "questions" }
|
|
55
60
|
];
|
|
@@ -223,15 +228,20 @@ function createTreeseedSite(tenantConfig, { starlight }) {
|
|
|
223
228
|
siteConfig,
|
|
224
229
|
deployConfig
|
|
225
230
|
});
|
|
226
|
-
const resolvedRoutes = [...PACKAGE_ROUTE_ENTRIES, ...siteExtensions.routes].map(
|
|
227
|
-
(route) => resolveRouteEntry(route, siteLayers)
|
|
228
|
-
);
|
|
229
231
|
const injectedTenantConfig = JSON.stringify(tenantConfig);
|
|
230
232
|
const injectedProjectRoot = JSON.stringify(projectRoot);
|
|
231
233
|
const injectedSiteConfig = JSON.stringify(siteConfig);
|
|
232
234
|
const injectedDeployConfig = JSON.stringify(deployConfig);
|
|
233
235
|
const resolvedGlobalCss = resolveTreeseedStyleEntrypoint(siteLayers, "styles/global.css");
|
|
234
236
|
const serverRendered = deployConfig.surfaces?.web?.provider === "cloudflare" || deployConfig.providers.deploy === "cloudflare";
|
|
237
|
+
const publishedRuntime = getTreeseedContentServingMode() === "published_runtime";
|
|
238
|
+
const packageRoutes = [
|
|
239
|
+
...PACKAGE_ROUTE_ENTRIES,
|
|
240
|
+
...docsRendered && publishedRuntime ? [
|
|
241
|
+
{ pattern: "/knowledge", resourcePath: "pages/docs-runtime/index.astro", model: "docs" },
|
|
242
|
+
{ pattern: "/knowledge/[...slug]", resourcePath: "pages/docs-runtime/[...slug].astro", model: "docs" }
|
|
243
|
+
] : []
|
|
244
|
+
];
|
|
235
245
|
return defineConfig({
|
|
236
246
|
adapter: serverRendered ? cloudflare({ imageService: "compile" }) : void 0,
|
|
237
247
|
output: serverRendered ? "server" : "static",
|
|
@@ -292,8 +302,11 @@ function createTreeseedSite(tenantConfig, { starlight }) {
|
|
|
292
302
|
}
|
|
293
303
|
},
|
|
294
304
|
integrations: [
|
|
295
|
-
createTreeseedRoutesIntegration(tenantConfig,
|
|
296
|
-
|
|
305
|
+
createTreeseedRoutesIntegration(tenantConfig, [
|
|
306
|
+
...packageRoutes.map((route) => resolveRouteEntry(route, siteLayers)),
|
|
307
|
+
...siteExtensions.routes.map((route) => resolveRouteEntry(route, siteLayers))
|
|
308
|
+
]),
|
|
309
|
+
...docsRendered && !publishedRuntime ? [starlight({
|
|
297
310
|
prerender: !serverRendered,
|
|
298
311
|
pagefind: !serverRendered,
|
|
299
312
|
disable404Route: true,
|
|
@@ -15,6 +15,8 @@ function fallbackTenantConfig(projectRoot) {
|
|
|
15
15
|
notes: resolve(projectRoot, "src/content/notes"),
|
|
16
16
|
questions: resolve(projectRoot, "src/content/questions"),
|
|
17
17
|
objectives: resolve(projectRoot, "src/content/objectives"),
|
|
18
|
+
proposals: resolve(projectRoot, "src/content/proposals"),
|
|
19
|
+
decisions: resolve(projectRoot, "src/content/decisions"),
|
|
18
20
|
people: resolve(projectRoot, "src/content/people"),
|
|
19
21
|
agents: resolve(projectRoot, "src/content/agents"),
|
|
20
22
|
books: resolve(projectRoot, "src/content/books"),
|
|
@@ -25,7 +27,12 @@ function fallbackTenantConfig(projectRoot) {
|
|
|
25
27
|
},
|
|
26
28
|
features: {
|
|
27
29
|
docs: true,
|
|
28
|
-
books: true
|
|
30
|
+
books: true,
|
|
31
|
+
notes: true,
|
|
32
|
+
questions: true,
|
|
33
|
+
objectives: true,
|
|
34
|
+
proposals: true,
|
|
35
|
+
decisions: true
|
|
29
36
|
}
|
|
30
37
|
};
|
|
31
38
|
}
|
|
@@ -25,6 +25,18 @@ async function getPublishedObjectives() {
|
|
|
25
25
|
}
|
|
26
26
|
return sortEntriesByDateDescending(await getCollection("objectives", ({ data }) => !data.draft));
|
|
27
27
|
}
|
|
28
|
+
async function getPublishedProposals() {
|
|
29
|
+
if (!siteModelRendered("proposals")) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return sortEntriesByDateDescending(await getCollection("proposals", ({ data }) => !data.draft));
|
|
33
|
+
}
|
|
34
|
+
async function getPublishedDecisions() {
|
|
35
|
+
if (!siteModelRendered("decisions")) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
return sortEntriesByDateDescending(await getCollection("decisions", ({ data }) => !data.draft));
|
|
39
|
+
}
|
|
28
40
|
async function getPublishedNotes() {
|
|
29
41
|
if (!siteModelRendered("notes")) {
|
|
30
42
|
return [];
|
|
@@ -52,9 +64,11 @@ async function getPublishedBooks() {
|
|
|
52
64
|
export {
|
|
53
65
|
getPublishedAgents,
|
|
54
66
|
getPublishedBooks,
|
|
67
|
+
getPublishedDecisions,
|
|
55
68
|
getPublishedNotes,
|
|
56
69
|
getPublishedObjectives,
|
|
57
70
|
getPublishedPeople,
|
|
71
|
+
getPublishedProposals,
|
|
58
72
|
getPublishedQuestions,
|
|
59
73
|
resolveContributor,
|
|
60
74
|
resolveContributorsForEntries,
|
|
@@ -54,7 +54,20 @@ async function loadHostedBookRuntime(locals) {
|
|
|
54
54
|
}
|
|
55
55
|
return provider.getObject(pointer);
|
|
56
56
|
}
|
|
57
|
+
async function loadHostedDocsTree(locals) {
|
|
58
|
+
const provider = resolveHostedContentRuntimeProvider(locals);
|
|
59
|
+
if (!provider) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const manifest = await provider.getManifest();
|
|
63
|
+
const pointer = manifest.runtime?.docsTree;
|
|
64
|
+
if (!pointer) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return provider.getObject(pointer);
|
|
68
|
+
}
|
|
57
69
|
export {
|
|
58
70
|
loadHostedBookRuntime,
|
|
71
|
+
loadHostedDocsTree,
|
|
59
72
|
resolveHostedContentRuntimeProvider
|
|
60
73
|
};
|
|
@@ -35,6 +35,8 @@ const PAGE_MODEL_DEFAULTS = SITE_CONFIG.models.pages.defaults;
|
|
|
35
35
|
const NOTE_MODEL_DEFAULTS = SITE_CONFIG.models.notes.defaults;
|
|
36
36
|
const QUESTION_MODEL_DEFAULTS = SITE_CONFIG.models.questions.defaults;
|
|
37
37
|
const OBJECTIVE_MODEL_DEFAULTS = SITE_CONFIG.models.objectives.defaults;
|
|
38
|
+
const PROPOSAL_MODEL_DEFAULTS = SITE_CONFIG.models.proposals.defaults;
|
|
39
|
+
const DECISION_MODEL_DEFAULTS = SITE_CONFIG.models.decisions.defaults;
|
|
38
40
|
const PEOPLE_MODEL_DEFAULTS = SITE_CONFIG.models.people.defaults;
|
|
39
41
|
const AGENT_MODEL_DEFAULTS = SITE_CONFIG.models.agents.defaults;
|
|
40
42
|
const BOOK_MODEL_DEFAULTS = SITE_CONFIG.models.books.defaults;
|
|
@@ -67,6 +69,20 @@ function applyObjectiveModelDefaults(value) {
|
|
|
67
69
|
tags: value.tags ?? OBJECTIVE_MODEL_DEFAULTS.tags ?? []
|
|
68
70
|
};
|
|
69
71
|
}
|
|
72
|
+
function applyProposalModelDefaults(value) {
|
|
73
|
+
return {
|
|
74
|
+
...PROPOSAL_MODEL_DEFAULTS,
|
|
75
|
+
...value,
|
|
76
|
+
tags: value.tags ?? PROPOSAL_MODEL_DEFAULTS.tags ?? []
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function applyDecisionModelDefaults(value) {
|
|
80
|
+
return {
|
|
81
|
+
...DECISION_MODEL_DEFAULTS,
|
|
82
|
+
...value,
|
|
83
|
+
tags: value.tags ?? DECISION_MODEL_DEFAULTS.tags ?? []
|
|
84
|
+
};
|
|
85
|
+
}
|
|
70
86
|
function applyPeopleModelDefaults(value) {
|
|
71
87
|
return {
|
|
72
88
|
...PEOPLE_MODEL_DEFAULTS,
|
|
@@ -98,10 +114,12 @@ function applyDocsModelDefaults(value) {
|
|
|
98
114
|
export {
|
|
99
115
|
AGENT_MODEL_DEFAULTS,
|
|
100
116
|
BOOK_MODEL_DEFAULTS,
|
|
117
|
+
DECISION_MODEL_DEFAULTS,
|
|
101
118
|
NOTE_MODEL_DEFAULTS,
|
|
102
119
|
OBJECTIVE_MODEL_DEFAULTS,
|
|
103
120
|
PAGE_MODEL_DEFAULTS,
|
|
104
121
|
PEOPLE_MODEL_DEFAULTS,
|
|
122
|
+
PROPOSAL_MODEL_DEFAULTS,
|
|
105
123
|
QUESTION_MODEL_DEFAULTS,
|
|
106
124
|
SITE,
|
|
107
125
|
SITE_CONFIG,
|
|
@@ -113,10 +131,12 @@ export {
|
|
|
113
131
|
TREESEED_MODEL_DEFAULTS,
|
|
114
132
|
applyAgentModelDefaults,
|
|
115
133
|
applyBookModelDefaults,
|
|
134
|
+
applyDecisionModelDefaults,
|
|
116
135
|
applyDocsModelDefaults,
|
|
117
136
|
applyNoteModelDefaults,
|
|
118
137
|
applyObjectiveModelDefaults,
|
|
119
138
|
applyPageModelDefaults,
|
|
120
139
|
applyPeopleModelDefaults,
|
|
140
|
+
applyProposalModelDefaults,
|
|
121
141
|
applyQuestionModelDefaults
|
|
122
142
|
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { createMarkdownProcessor } from "@astrojs/markdown-remark";
|
|
2
|
+
import rehypeKatex from "rehype-katex";
|
|
3
|
+
import remarkMath from "remark-math";
|
|
4
|
+
import { getTreeseedContentServingMode } from "@treeseed/sdk/platform/deploy-runtime";
|
|
5
|
+
import { resolveHostedContentRuntimeProvider } from "./published-content.js";
|
|
6
|
+
let markdownProcessorPromise = null;
|
|
7
|
+
function markdownProcessor() {
|
|
8
|
+
if (!markdownProcessorPromise) {
|
|
9
|
+
markdownProcessorPromise = createMarkdownProcessor({
|
|
10
|
+
remarkPlugins: [remarkMath],
|
|
11
|
+
rehypePlugins: [[rehypeKatex, { strict: "ignore" }]]
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return markdownProcessorPromise;
|
|
15
|
+
}
|
|
16
|
+
function providerForLocals(locals) {
|
|
17
|
+
return resolveHostedContentRuntimeProvider(locals);
|
|
18
|
+
}
|
|
19
|
+
function record(value) {
|
|
20
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
21
|
+
}
|
|
22
|
+
function strings(value) {
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
|
|
25
|
+
}
|
|
26
|
+
if (typeof value === "string" && value.trim()) {
|
|
27
|
+
return [value.trim()];
|
|
28
|
+
}
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
function dateValue(value) {
|
|
32
|
+
if (value instanceof Date && Number.isFinite(value.valueOf())) {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === "string" && value.trim()) {
|
|
36
|
+
const parsed = new Date(value);
|
|
37
|
+
if (Number.isFinite(parsed.valueOf())) {
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function normalizeTags(value) {
|
|
44
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
|
|
45
|
+
}
|
|
46
|
+
function normalizeLinks(value) {
|
|
47
|
+
if (!Array.isArray(value)) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
return value.map((entry) => record(entry)).map((entry) => ({
|
|
51
|
+
label: typeof entry.label === "string" ? entry.label : "",
|
|
52
|
+
href: typeof entry.href === "string" ? entry.href : ""
|
|
53
|
+
})).filter((entry) => entry.label && entry.href);
|
|
54
|
+
}
|
|
55
|
+
function normalizePublishedEntry(entry, content) {
|
|
56
|
+
const frontmatter = record(content?.frontmatter);
|
|
57
|
+
const data = {
|
|
58
|
+
...frontmatter,
|
|
59
|
+
title: typeof frontmatter.title === "string" ? frontmatter.title : entry.title,
|
|
60
|
+
summary: typeof frontmatter.summary === "string" ? frontmatter.summary : entry.summary,
|
|
61
|
+
status: typeof frontmatter.status === "string" ? frontmatter.status : entry.status,
|
|
62
|
+
slug: typeof frontmatter.slug === "string" ? frontmatter.slug : entry.slug,
|
|
63
|
+
tags: normalizeTags(frontmatter.tags),
|
|
64
|
+
links: normalizeLinks(frontmatter.links)
|
|
65
|
+
};
|
|
66
|
+
const date = dateValue(frontmatter.date ?? entry.publishedAt);
|
|
67
|
+
if (date) {
|
|
68
|
+
data.date = date;
|
|
69
|
+
}
|
|
70
|
+
const updated = dateValue(content?.updatedAt ?? frontmatter.updated ?? entry.updatedAt);
|
|
71
|
+
if (updated) {
|
|
72
|
+
data.updated = updated;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
id: entry.id,
|
|
76
|
+
model: entry.model,
|
|
77
|
+
slug: entry.slug,
|
|
78
|
+
data
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function isPublishedRuntimeContentMode() {
|
|
82
|
+
return getTreeseedContentServingMode() === "published_runtime";
|
|
83
|
+
}
|
|
84
|
+
async function renderPublishedMarkdown(body) {
|
|
85
|
+
const rendered = await (await markdownProcessor()).render(body ?? "", {
|
|
86
|
+
frontmatter: {}
|
|
87
|
+
});
|
|
88
|
+
return rendered.code;
|
|
89
|
+
}
|
|
90
|
+
async function loadPublishedCollection(locals, model) {
|
|
91
|
+
const provider = providerForLocals(locals);
|
|
92
|
+
if (!provider) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
const entries = await provider.listCollection(model);
|
|
96
|
+
const resolved = await Promise.all(entries.map(async (entry) => {
|
|
97
|
+
const content = await provider.getObject(entry.content);
|
|
98
|
+
return normalizePublishedEntry(entry, content);
|
|
99
|
+
}));
|
|
100
|
+
return resolved;
|
|
101
|
+
}
|
|
102
|
+
async function loadPublishedEntry(locals, model, slugOrId) {
|
|
103
|
+
const provider = providerForLocals(locals);
|
|
104
|
+
if (!provider) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const entry = await provider.getEntry(model, slugOrId);
|
|
108
|
+
if (!entry) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const content = await provider.getObject(entry.content);
|
|
112
|
+
if (!content) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
publishedEntry: entry,
|
|
117
|
+
entry: normalizePublishedEntry(entry, content),
|
|
118
|
+
content,
|
|
119
|
+
html: await renderPublishedMarkdown(content.body ?? "")
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async function resolvePublishedReferences(locals, model, references) {
|
|
123
|
+
const provider = providerForLocals(locals);
|
|
124
|
+
if (!provider) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
const resolved = await Promise.all(strings(references).map(async (slugOrId) => {
|
|
128
|
+
const entry = await provider.getEntry(model, slugOrId);
|
|
129
|
+
if (!entry) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const content = await provider.getObject(entry.content);
|
|
133
|
+
return normalizePublishedEntry(entry, content);
|
|
134
|
+
}));
|
|
135
|
+
return resolved.filter(Boolean);
|
|
136
|
+
}
|
|
137
|
+
async function resolvePublishedContributor(locals, reference) {
|
|
138
|
+
const provider = providerForLocals(locals);
|
|
139
|
+
if (!provider) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
for (const model of ["people", "agents"]) {
|
|
143
|
+
for (const slugOrId of strings(reference)) {
|
|
144
|
+
const entry = await provider.getEntry(model, slugOrId);
|
|
145
|
+
if (entry) {
|
|
146
|
+
const content = await provider.getObject(entry.content);
|
|
147
|
+
return normalizePublishedEntry(entry, content);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
function metadataFromPublishedContent(content) {
|
|
154
|
+
return record(content?.frontmatter);
|
|
155
|
+
}
|
|
156
|
+
function publishedDate(content, ...fallbacks) {
|
|
157
|
+
for (const candidate of [content?.frontmatter?.date, ...fallbacks]) {
|
|
158
|
+
const parsed = dateValue(candidate);
|
|
159
|
+
if (parsed) {
|
|
160
|
+
return parsed;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return /* @__PURE__ */ new Date(0);
|
|
164
|
+
}
|
|
165
|
+
function publishedUpdated(content, ...fallbacks) {
|
|
166
|
+
for (const candidate of [content?.updatedAt, content?.frontmatter?.updated, ...fallbacks]) {
|
|
167
|
+
const parsed = dateValue(candidate);
|
|
168
|
+
if (parsed) {
|
|
169
|
+
return parsed;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return /* @__PURE__ */ new Date(0);
|
|
173
|
+
}
|
|
174
|
+
export {
|
|
175
|
+
isPublishedRuntimeContentMode,
|
|
176
|
+
loadPublishedCollection,
|
|
177
|
+
loadPublishedEntry,
|
|
178
|
+
metadataFromPublishedContent,
|
|
179
|
+
normalizePublishedEntry,
|
|
180
|
+
publishedDate,
|
|
181
|
+
publishedUpdated,
|
|
182
|
+
renderPublishedMarkdown,
|
|
183
|
+
resolvePublishedContributor,
|
|
184
|
+
resolvePublishedReferences
|
|
185
|
+
};
|