@treeseed/core 0.4.10 → 0.4.12

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.
Files changed (55) hide show
  1. package/dist/api/auth/rbac.d.ts +2 -2
  2. package/dist/api/auth/rbac.js +2 -1
  3. package/dist/components/site/RouteNotFound.astro +25 -0
  4. package/dist/content-config.d.ts +1 -0
  5. package/dist/content.d.ts +1 -0
  6. package/dist/content.js +177 -1
  7. package/dist/dev.d.ts +7 -2
  8. package/dist/dev.js +59 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +9 -1
  11. package/dist/middleware/editorial-preview.d.ts +26 -0
  12. package/dist/middleware/editorial-preview.js +37 -0
  13. package/dist/middleware/starlightRouteData.js +15 -4
  14. package/dist/pages/[slug].astro +12 -10
  15. package/dist/pages/agents/[slug].astro +28 -21
  16. package/dist/pages/books/[slug].astro +19 -12
  17. package/dist/pages/feed.xml.js +6 -4
  18. package/dist/pages/index.astro +43 -14
  19. package/dist/pages/notes/[slug].astro +19 -12
  20. package/dist/pages/objectives/[slug].astro +30 -23
  21. package/dist/pages/people/[slug].astro +28 -21
  22. package/dist/pages/questions/[slug].astro +30 -23
  23. package/dist/scripts/build-dist.js +6 -1
  24. package/dist/scripts/dev-platform.js +9 -1
  25. package/dist/services/agents.d.ts +22 -0
  26. package/dist/services/agents.js +29 -0
  27. package/dist/services/index.d.ts +3 -0
  28. package/dist/services/index.js +11 -0
  29. package/dist/services/manager.d.ts +247 -0
  30. package/dist/services/manager.js +1129 -0
  31. package/dist/services/remote-runner.d.ts +7 -0
  32. package/dist/services/remote-runner.js +6 -0
  33. package/dist/services/workday-content.d.ts +53 -0
  34. package/dist/services/workday-content.js +190 -0
  35. package/dist/services/workday-report.d.ts +160 -2
  36. package/dist/services/workday-report.js +3 -26
  37. package/dist/services/workday-start.d.ts +170 -1
  38. package/dist/services/workday-start.js +3 -7
  39. package/dist/services/worker-pool-scaler.d.ts +27 -0
  40. package/dist/services/worker-pool-scaler.js +109 -0
  41. package/dist/services/worker.d.ts +7 -0
  42. package/dist/services/worker.js +3 -0
  43. package/dist/site.js +43 -27
  44. package/dist/templates.d.ts +98 -0
  45. package/dist/templates.js +170 -0
  46. package/dist/tenant/runtime-config.d.ts +4 -0
  47. package/dist/tenant/runtime-config.js +34 -1
  48. package/dist/utils/hub-content.js +35 -0
  49. package/dist/utils/published-content.js +60 -0
  50. package/dist/utils/site-models.d.ts +6 -0
  51. package/dist/utils/site-models.js +16 -0
  52. package/dist/utils/starlight-nav.js +50 -0
  53. package/package.json +20 -2
  54. package/templates/github/deploy.workflow.yml +404 -9
  55. package/templates/github/hosted-project.workflow.yml +77 -0
@@ -1,6 +1,6 @@
1
- export declare const CONTENT_RESOURCES: readonly ["pages", "notes", "questions", "objectives", "people", "agents", "books", "templates", "knowledge_downloads"];
1
+ export declare const CONTENT_RESOURCES: readonly ["pages", "notes", "questions", "objectives", "people", "agents", "books", "templates", "knowledge_packs", "workdays"];
2
2
  export declare const PLATFORM_RESOURCES: readonly ["users", "roles", "api_tokens", "services", "jobs", "audit", "auth", "sdk", "agent", "operations"];
3
- export declare const ALL_PERMISSION_RESOURCES: readonly ["pages", "notes", "questions", "objectives", "people", "agents", "books", "templates", "knowledge_downloads", "users", "roles", "api_tokens", "services", "jobs", "audit", "auth", "sdk", "agent", "operations"];
3
+ export declare const ALL_PERMISSION_RESOURCES: readonly ["pages", "notes", "questions", "objectives", "people", "agents", "books", "templates", "knowledge_packs", "workdays", "users", "roles", "api_tokens", "services", "jobs", "audit", "auth", "sdk", "agent", "operations"];
4
4
  export type PermissionResource = (typeof ALL_PERMISSION_RESOURCES)[number];
5
5
  export type PermissionAction = 'read' | 'create' | 'update' | 'delete' | 'manage' | 'execute' | 'impersonate';
6
6
  export type PermissionScope = 'self' | 'global';
@@ -7,7 +7,8 @@ const CONTENT_RESOURCES = [
7
7
  "agents",
8
8
  "books",
9
9
  "templates",
10
- "knowledge_downloads"
10
+ "knowledge_packs",
11
+ "workdays"
11
12
  ];
12
13
  const PLATFORM_RESOURCES = [
13
14
  "users",
@@ -0,0 +1,25 @@
1
+ ---
2
+ import MainLayout from '../../layouts/MainLayout.astro';
3
+
4
+ const {
5
+ title = 'Page not found',
6
+ description = 'The requested Treeseed content could not be found.',
7
+ currentPath = '/404/',
8
+ } = Astro.props;
9
+ ---
10
+
11
+ <MainLayout title={title} description={description} currentPath={currentPath}>
12
+ <section class="mx-auto max-w-3xl space-y-6 py-20">
13
+ <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--site-accent-strong)]">404</p>
14
+ <h1 class="font-serif text-5xl text-[color:var(--site-text)]">{title}</h1>
15
+ <p class="text-lg leading-9 text-[color:var(--site-text-muted)]">{description}</p>
16
+ <div class="flex flex-wrap gap-4">
17
+ <a href="/" class="border border-[color:var(--site-accent)] bg-[color:var(--site-accent)] px-5 py-3 text-base font-semibold text-[color:var(--site-text)] transition hover:border-[color:var(--site-blue)] hover:bg-[color:var(--site-blue-soft)]">
18
+ Go home
19
+ </a>
20
+ <a href="/knowledge/" class="border border-[color:var(--site-border-strong)] px-5 py-3 text-base font-semibold text-[color:var(--site-text)] transition hover:border-[color:var(--site-blue)] hover:bg-[color:var(--site-blue-soft)]">
21
+ Open knowledge
22
+ </a>
23
+ </div>
24
+ </section>
25
+ </MainLayout>
@@ -7,4 +7,5 @@ export declare function createTreeseedTenantCollections(manifestPath?: string):
7
7
  agents: any;
8
8
  books: any;
9
9
  docs: any;
10
+ workdays?: any;
10
11
  };
@@ -0,0 +1 @@
1
+ export declare function createTreeseedCollections(tenantConfig: any, dependencies: any): Record<string, any>;
package/dist/content.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { defineCollection, reference } from "astro:content";
2
2
  import { z } from "astro/zod";
3
3
  import { glob } from "astro/loaders";
4
+ import { existsSync, readdirSync } from "node:fs";
4
5
  import { AGENT_CLI_ALLOW_TOOLS } from "@treeseed/sdk/types/agents";
5
6
  import { loadTreeseedPluginRuntime } from "@treeseed/sdk/platform/plugins";
6
7
  import { loadTreeseedDeployConfig } from "@treeseed/sdk/platform/deploy-config";
@@ -22,6 +23,30 @@ const timeHorizonValues = ["near-term", "mid-term", "long-term"];
22
23
  const runtimeStatusValues = ["active", "experimental", "dormant"];
23
24
  const agentTriggerTypeValues = ["schedule", "message", "follow", "startup"];
24
25
  const agentPermissionOperationValues = ["get", "search", "follow", "pick", "create", "update"];
26
+ function hasMarkdownContent(base) {
27
+ if (!existsSync(base)) {
28
+ return false;
29
+ }
30
+ for (const entry of readdirSync(base, { withFileTypes: true, recursive: true })) {
31
+ if (entry.isFile() && /\.(md|mdx)$/iu.test(entry.name)) {
32
+ return true;
33
+ }
34
+ }
35
+ return false;
36
+ }
37
+ function optionalMarkdownGlob(base) {
38
+ const delegate = glob({ pattern: "**/*.{md,mdx}", base });
39
+ return {
40
+ name: `treeseed-optional-markdown-glob:${base}`,
41
+ async load(context) {
42
+ if (!hasMarkdownContent(base)) {
43
+ context.store.clear();
44
+ return;
45
+ }
46
+ await delegate.load(context);
47
+ }
48
+ };
49
+ }
25
50
  function withOptionalDefault(schema, defaultValue) {
26
51
  return defaultValue === void 0 ? schema : schema.default(defaultValue);
27
52
  }
@@ -244,8 +269,140 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
244
269
  sidebarItems: z.array(sidebarItemSchema).min(1),
245
270
  tags: z.array(z.string()).default(BOOK_MODEL_DEFAULTS.tags ?? [])
246
271
  }));
272
+ const publisherSchema = z.object({
273
+ id: z.string(),
274
+ name: z.string(),
275
+ url: z.string().optional()
276
+ });
277
+ const templateGitSourceSchema = z.object({
278
+ kind: z.literal("git"),
279
+ repoUrl: z.string(),
280
+ directory: z.string(),
281
+ ref: z.string(),
282
+ integrity: z.string().optional()
283
+ });
284
+ const templateR2SourceSchema = z.object({
285
+ kind: z.literal("r2"),
286
+ bucket: z.string().optional(),
287
+ objectKey: z.string(),
288
+ version: z.string(),
289
+ publicUrl: z.string().optional(),
290
+ integrity: z.string().optional()
291
+ });
292
+ const templateProductSchema = z.object({
293
+ slug: z.string(),
294
+ title: z.string(),
295
+ description: z.string(),
296
+ summary: z.string(),
297
+ status: z.enum(["draft", "live", "archived"]),
298
+ featured: z.boolean().default(false),
299
+ teamId: z.string().optional(),
300
+ listingEnabled: z.boolean().default(true),
301
+ category: z.string(),
302
+ audience: z.array(z.string()).default([]),
303
+ tags: z.array(z.string()).default([]),
304
+ publisher: publisherSchema,
305
+ publisherVerified: z.boolean().default(false),
306
+ templateVersion: z.string(),
307
+ templateApiVersion: z.number().int().positive(),
308
+ minCliVersion: z.string(),
309
+ minCoreVersion: z.string(),
310
+ fulfillment: z.object({
311
+ mode: z.enum(["packaged", "git", "r2"]).default("packaged"),
312
+ source: z.union([templateGitSourceSchema, templateR2SourceSchema]),
313
+ hooksPolicy: z.enum(["builtin_only", "trusted_only", "disabled"]).default("builtin_only"),
314
+ supportsReconcile: z.boolean().default(true)
315
+ }),
316
+ offer: z.object({
317
+ priceModel: z.enum(["free", "paid", "contact", "one_time_current_version", "subscription_updates", "private"]).default("free"),
318
+ license: z.string().optional(),
319
+ support: z.string().optional()
320
+ }).default({ priceModel: "free" }),
321
+ relatedBooks: z.array(z.string()).default([]),
322
+ relatedKnowledge: z.array(z.string()).default([]),
323
+ relatedObjectives: z.array(z.string()).default([])
324
+ });
325
+ const knowledgePackSchema = z.object({
326
+ slug: z.string(),
327
+ title: z.string(),
328
+ description: z.string(),
329
+ status: z.enum(["draft", "live", "archived"]).default("draft")
330
+ });
331
+ const workdaySummaryTaskSchema = z.object({
332
+ id: z.string(),
333
+ agentId: z.string().optional(),
334
+ type: z.string().optional(),
335
+ state: z.string().optional(),
336
+ priority: z.number().optional(),
337
+ idempotencyKey: z.string().optional(),
338
+ createdAt: z.coerce.date().optional(),
339
+ startedAt: z.coerce.date().optional(),
340
+ completedAt: z.coerce.date().optional(),
341
+ lastErrorCode: z.string().nullable().optional(),
342
+ lastErrorMessage: z.string().nullable().optional(),
343
+ lastEventKind: z.string().optional(),
344
+ outputCount: z.number().int().optional(),
345
+ changedFiles: z.array(z.string()).default([])
346
+ });
347
+ const workdayPriorityItemSchema = z.object({
348
+ id: z.string(),
349
+ model: z.string(),
350
+ slug: z.string().optional(),
351
+ title: z.string().optional(),
352
+ status: z.string().optional(),
353
+ priority: z.number(),
354
+ estimatedCredits: z.number().optional(),
355
+ reason: z.string().optional()
356
+ });
357
+ const workdayReleaseSchema = z.object({
358
+ id: z.string().optional(),
359
+ deploymentKind: z.string(),
360
+ status: z.string(),
361
+ releaseTag: z.string().nullable().optional(),
362
+ commitSha: z.string().nullable().optional(),
363
+ sourceRef: z.string().nullable().optional(),
364
+ startedAt: z.coerce.date().optional(),
365
+ finishedAt: z.coerce.date().optional(),
366
+ createdAt: z.coerce.date().optional()
367
+ });
368
+ const workdaySchema = z.object({
369
+ title: z.string(),
370
+ slug: z.string(),
371
+ workDayId: z.string(),
372
+ reportVersion: z.string(),
373
+ reportKind: z.string().default("workday_summary"),
374
+ projectId: z.string(),
375
+ teamId: z.string().optional(),
376
+ environment: z.string(),
377
+ status: z.string().default("live"),
378
+ visibility: z.enum(["public", "authenticated", "team", "private"]).default("team"),
379
+ workdayState: z.string(),
380
+ startedAt: z.coerce.date(),
381
+ endedAt: z.coerce.date().nullable().optional(),
382
+ generatedAt: z.coerce.date(),
383
+ createdAt: z.coerce.date().optional(),
384
+ summary: z.string(),
385
+ dailyTaskCreditBudget: z.number().default(0),
386
+ usedTaskCredits: z.number().default(0),
387
+ remainingTaskCredits: z.number().default(0),
388
+ creditLedgerEntries: z.number().int().default(0),
389
+ prioritySnapshotId: z.string().nullable().optional(),
390
+ priorityItemCount: z.number().int().default(0),
391
+ priorityItems: z.array(workdayPriorityItemSchema).default([]),
392
+ totalTasks: z.number().int().default(0),
393
+ completedTasks: z.number().int().default(0),
394
+ failedTasks: z.number().int().default(0),
395
+ queuedTasks: z.number().int().default(0),
396
+ activeTasks: z.number().int().default(0),
397
+ taskItems: z.array(workdaySummaryTaskSchema).default([]),
398
+ changedFiles: z.array(z.string()).default([]),
399
+ releases: z.array(workdayReleaseSchema).default([]),
400
+ scaleDecision: z.record(z.any()).default({}),
401
+ scaleResult: z.record(z.any()).default({}),
402
+ metadata: z.record(z.any()).default({})
403
+ });
247
404
  const docsCollectionProvider = resolveDocsCollectionProvider(tenantConfig, { docsLoader, docsSchema });
248
- return {
405
+ const collections = {
249
406
  pages: defineCollection({ loader: glob({ pattern: "**/*.{md,mdx}", base: tenantConfig.content.pages }), schema: pageSchema }),
250
407
  notes: defineCollection({ loader: glob({ pattern: "**/*.{md,mdx}", base: tenantConfig.content.notes }), schema: noteSchema }),
251
408
  questions: defineCollection({ loader: glob({ pattern: "**/*.{md,mdx}", base: tenantConfig.content.questions }), schema: questionSchema }),
@@ -258,6 +415,25 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
258
415
  schema: docsCollectionProvider.schema
259
416
  })
260
417
  };
418
+ if (tenantConfig.content.workdays) {
419
+ collections.workdays = defineCollection({
420
+ loader: optionalMarkdownGlob(tenantConfig.content.workdays),
421
+ schema: workdaySchema
422
+ });
423
+ }
424
+ if (tenantConfig.content.templates) {
425
+ collections.templates = defineCollection({
426
+ loader: optionalMarkdownGlob(tenantConfig.content.templates),
427
+ schema: templateProductSchema
428
+ });
429
+ }
430
+ if (tenantConfig.content.knowledge_packs) {
431
+ collections.knowledge_packs = defineCollection({
432
+ loader: optionalMarkdownGlob(tenantConfig.content.knowledge_packs),
433
+ schema: knowledgePackSchema
434
+ });
435
+ }
436
+ return collections;
261
437
  }
262
438
  export {
263
439
  createTreeseedCollections
package/dist/dev.d.ts CHANGED
@@ -3,7 +3,8 @@ export declare const TREESEED_DEFAULT_WEB_HOST = "127.0.0.1";
3
3
  export declare const TREESEED_DEFAULT_WEB_PORT = 4321;
4
4
  export declare const TREESEED_DEFAULT_API_HOST = "127.0.0.1";
5
5
  export declare const TREESEED_DEFAULT_API_PORT = 3000;
6
- export type TreeseedIntegratedDevSurface = 'integrated' | 'web' | 'api';
6
+ export declare const TREESEED_DEFAULT_MANAGER_PORT = 3100;
7
+ export type TreeseedIntegratedDevSurface = 'integrated' | 'services' | 'web' | 'api' | 'manager' | 'worker' | 'agents';
7
8
  export type TreeseedIntegratedDevOptions = {
8
9
  surface?: TreeseedIntegratedDevSurface;
9
10
  watch?: boolean;
@@ -14,9 +15,13 @@ export type TreeseedIntegratedDevOptions = {
14
15
  webPort?: number;
15
16
  apiHost?: string;
16
17
  apiPort?: number;
18
+ managerPort?: number;
19
+ includeServices?: boolean;
20
+ projectId?: string;
21
+ teamId?: string;
17
22
  };
18
23
  export type TreeseedIntegratedDevCommand = {
19
- id: 'web' | 'api';
24
+ id: 'web' | 'api' | 'manager' | 'worker' | 'agents';
20
25
  label: string;
21
26
  command: string;
22
27
  args: string[];
package/dist/dev.js CHANGED
@@ -10,6 +10,7 @@ const TREESEED_DEFAULT_WEB_HOST = "127.0.0.1";
10
10
  const TREESEED_DEFAULT_WEB_PORT = 4321;
11
11
  const TREESEED_DEFAULT_API_HOST = "127.0.0.1";
12
12
  const TREESEED_DEFAULT_API_PORT = 3e3;
13
+ const TREESEED_DEFAULT_MANAGER_PORT = 3100;
13
14
  function resolvePackageRoot(packageName, tenantRoot) {
14
15
  const resolvedPath = require2.resolve(packageName, {
15
16
  paths: [tenantRoot, packageRoot, process.cwd()]
@@ -74,6 +75,10 @@ function createTreeseedIntegratedDevPlan(options = {}) {
74
75
  const webPort = normalizePort(options.webPort, TREESEED_DEFAULT_WEB_PORT);
75
76
  const apiHost = options.apiHost ?? TREESEED_DEFAULT_API_HOST;
76
77
  const apiPort = normalizePort(options.apiPort, TREESEED_DEFAULT_API_PORT);
78
+ const managerPort = normalizePort(options.managerPort, TREESEED_DEFAULT_MANAGER_PORT);
79
+ const includeServices = options.includeServices ?? (surface === "integrated" || surface === "services");
80
+ const projectId = options.projectId ?? process.env.TREESEED_PROJECT_ID;
81
+ const teamId = options.teamId ?? process.env.TREESEED_HOSTING_TEAM_ID;
77
82
  const mergedEnv = { ...process.env, ...options.env ?? {} };
78
83
  const apiBaseUrl = mergedEnv.TREESEED_API_BASE_URL?.trim() || `http://${apiHost}:${apiPort}`;
79
84
  const sdkPackageRoot = resolvePackageRoot("@treeseed/sdk", tenantRoot);
@@ -88,6 +93,21 @@ function createTreeseedIntegratedDevPlan(options = {}) {
88
93
  "src/api/server.ts",
89
94
  "dist/api/server.js"
90
95
  );
96
+ const managerEntrypoint = resolveNodeEntrypoint(
97
+ packageRoot,
98
+ "src/services/manager.ts",
99
+ "dist/services/manager.js"
100
+ );
101
+ const workerEntrypoint = resolveNodeEntrypoint(
102
+ packageRoot,
103
+ "src/services/worker.ts",
104
+ "dist/services/worker.js"
105
+ );
106
+ const agentsEntrypoint = resolveNodeEntrypoint(
107
+ packageRoot,
108
+ "src/services/agents.ts",
109
+ "dist/services/agents.js"
110
+ );
91
111
  const watchPaths = [
92
112
  resolve(packageRoot, existsSync(resolve(packageRoot, "src")) ? "src" : "dist"),
93
113
  resolve(tenantRoot, "src"),
@@ -97,7 +117,10 @@ function createTreeseedIntegratedDevPlan(options = {}) {
97
117
  const sharedEnv = {
98
118
  ...mergedEnv,
99
119
  TREESEED_LOCAL_DEV_MODE: mergedEnv.TREESEED_LOCAL_DEV_MODE ?? "cloudflare",
100
- TREESEED_API_BASE_URL: apiBaseUrl
120
+ TREESEED_API_BASE_URL: apiBaseUrl,
121
+ TREESEED_MARKET_API_BASE_URL: mergedEnv.TREESEED_MARKET_API_BASE_URL ?? apiBaseUrl,
122
+ TREESEED_PROJECT_ID: projectId ?? mergedEnv.TREESEED_PROJECT_ID,
123
+ TREESEED_HOSTING_TEAM_ID: teamId ?? mergedEnv.TREESEED_HOSTING_TEAM_ID
101
124
  };
102
125
  if (watch) {
103
126
  sharedEnv.TREESEED_PUBLIC_DEV_WATCH_RELOAD = sharedEnv.TREESEED_PUBLIC_DEV_WATCH_RELOAD || "true";
@@ -126,6 +149,40 @@ function createTreeseedIntegratedDevPlan(options = {}) {
126
149
  }
127
150
  });
128
151
  }
152
+ if (includeServices || surface === "manager") {
153
+ commands.push({
154
+ id: "manager",
155
+ label: "Manager",
156
+ command: managerEntrypoint.command,
157
+ args: watch ? withWatchArgs(managerEntrypoint.args, watchPaths) : managerEntrypoint.args,
158
+ cwd: tenantRoot,
159
+ env: {
160
+ ...sharedEnv,
161
+ PORT: sharedEnv.PORT ?? String(managerPort),
162
+ TREESEED_MANAGER_BASE_URL: sharedEnv.TREESEED_MANAGER_BASE_URL ?? `http://${apiHost}:${managerPort}`
163
+ }
164
+ });
165
+ }
166
+ if (includeServices || surface === "worker") {
167
+ commands.push({
168
+ id: "worker",
169
+ label: "Worker",
170
+ command: workerEntrypoint.command,
171
+ args: watch ? withWatchArgs(workerEntrypoint.args, watchPaths) : workerEntrypoint.args,
172
+ cwd: tenantRoot,
173
+ env: sharedEnv
174
+ });
175
+ }
176
+ if (includeServices || surface === "agents") {
177
+ commands.push({
178
+ id: "agents",
179
+ label: "Agents",
180
+ command: agentsEntrypoint.command,
181
+ args: watch ? withWatchArgs(agentsEntrypoint.args, watchPaths) : agentsEntrypoint.args,
182
+ cwd: tenantRoot,
183
+ env: sharedEnv
184
+ });
185
+ }
129
186
  return {
130
187
  surface,
131
188
  tenantRoot,
@@ -205,6 +262,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
205
262
  export {
206
263
  TREESEED_DEFAULT_API_HOST,
207
264
  TREESEED_DEFAULT_API_PORT,
265
+ TREESEED_DEFAULT_MANAGER_PORT,
208
266
  TREESEED_DEFAULT_WEB_HOST,
209
267
  TREESEED_DEFAULT_WEB_PORT,
210
268
  createTreeseedIntegratedDevPlan,
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@ export { createTreeseedApiApp } from './api/app';
5
5
  export { createRailwayTreeseedApiServer } from './api/railway';
6
6
  export { resolveApiConfig } from './api/config';
7
7
  export { createTreeseedIntegratedDevPlan, runTreeseedIntegratedDev, type TreeseedIntegratedDevCommand, type TreeseedIntegratedDevOptions, type TreeseedIntegratedDevPlan, type TreeseedIntegratedDevSurface, } from './dev';
8
+ export { filterSiteRenderedModels, isSiteRenderedModel, siteModelRendered, } from './utils/site-models.ts';
8
9
  export type * from './api/types';
package/dist/index.js CHANGED
@@ -18,6 +18,11 @@ import {
18
18
  createTreeseedIntegratedDevPlan,
19
19
  runTreeseedIntegratedDev
20
20
  } from "./dev.js";
21
+ import {
22
+ filterSiteRenderedModels,
23
+ isSiteRenderedModel,
24
+ siteModelRendered
25
+ } from "./utils/site-models.js";
21
26
  export {
22
27
  TREESEED_PLATFORM_RESOURCE_KINDS,
23
28
  TREESEED_SITE_RESOURCE_KINDS,
@@ -26,11 +31,14 @@ export {
26
31
  createRailwayTreeseedApiServer,
27
32
  createTreeseedApiApp,
28
33
  createTreeseedIntegratedDevPlan,
34
+ filterSiteRenderedModels,
35
+ isSiteRenderedModel,
29
36
  parseSiteConfig,
30
37
  resolveApiConfig,
31
38
  resolveTreeseedPageEntrypoint,
32
39
  resolveTreeseedPlatformResource,
33
40
  resolveTreeseedSiteResource,
34
41
  resolveTreeseedStyleEntrypoint,
35
- runTreeseedIntegratedDev
42
+ runTreeseedIntegratedDev,
43
+ siteModelRendered
36
44
  };
@@ -0,0 +1,26 @@
1
+ import { type EditorialPreviewTokenPayload } from '@treeseed/sdk/platform/published-content';
2
+ type CookieValue = {
3
+ value: string;
4
+ } | undefined;
5
+ export interface EditorialPreviewContextLike {
6
+ url: URL;
7
+ cookies: {
8
+ get(name: string): CookieValue;
9
+ set(name: string, value: string, options?: {
10
+ httpOnly?: boolean;
11
+ path?: string;
12
+ sameSite?: 'lax' | 'strict' | 'none';
13
+ secure?: boolean;
14
+ expires?: Date;
15
+ }): void;
16
+ delete(name: string, options?: {
17
+ path?: string;
18
+ }): void;
19
+ };
20
+ locals: object;
21
+ }
22
+ export declare function resolveEditorialPreview(context: EditorialPreviewContextLike, options?: {
23
+ secret?: string | null;
24
+ cookieName?: string;
25
+ }): EditorialPreviewTokenPayload | null;
26
+ export {};
@@ -0,0 +1,37 @@
1
+ import {
2
+ EDITORIAL_PREVIEW_COOKIE,
3
+ verifyEditorialPreviewToken
4
+ } from "@treeseed/sdk/platform/published-content";
5
+ function previewSecretFromLocals(locals) {
6
+ const runtime = locals?.runtime;
7
+ return typeof runtime?.env?.TREESEED_EDITORIAL_PREVIEW_SECRET === "string" ? runtime.env.TREESEED_EDITORIAL_PREVIEW_SECRET : "";
8
+ }
9
+ function resolveEditorialPreview(context, options = {}) {
10
+ const cookieName = options.cookieName ?? EDITORIAL_PREVIEW_COOKIE;
11
+ const secret = typeof options.secret === "string" ? options.secret : previewSecretFromLocals(context.locals);
12
+ const queryPreview = context.url.searchParams.get("preview");
13
+ if (queryPreview === "clear") {
14
+ context.cookies.delete(cookieName, { path: "/" });
15
+ context.locals.contentPreview = null;
16
+ return null;
17
+ }
18
+ const cookiePreview = context.cookies.get(cookieName)?.value ?? null;
19
+ const activeToken = queryPreview || cookiePreview;
20
+ const resolvedPreview = activeToken && secret ? verifyEditorialPreviewToken(activeToken, secret) : null;
21
+ if (queryPreview && resolvedPreview) {
22
+ context.cookies.set(cookieName, queryPreview, {
23
+ httpOnly: true,
24
+ path: "/",
25
+ sameSite: "lax",
26
+ secure: context.url.protocol === "https:",
27
+ expires: new Date(resolvedPreview.expiresAt)
28
+ });
29
+ } else if (activeToken && !resolvedPreview) {
30
+ context.cookies.delete(cookieName, { path: "/" });
31
+ }
32
+ context.locals.contentPreview = resolvedPreview;
33
+ return resolvedPreview;
34
+ }
35
+ export {
36
+ resolveEditorialPreview
37
+ };
@@ -1,5 +1,8 @@
1
1
  import { defineRouteMiddleware } from "../vendor/starlight/route-data.js";
2
- import { BOOKS, TREESEED_LINKS, normalizeHref } from "../utils/starlight-nav.js";
2
+ import { TREESEED_LINKS, buildStarlightSidebarEntriesFromRuntime, normalizeHref } from "../utils/starlight-nav.js";
3
+ import { loadHostedBookRuntime } from "../utils/published-content.js";
4
+ import { BOOKS, BOOKS_LINK, TREESEED_LIBRARY_DOWNLOAD } from "@treeseed/sdk/platform/books-data";
5
+ import { loadTreeseedManifest, tenantModelRendered } from "@treeseed/sdk/platform/tenant-config";
3
6
  const copyLink = (entry) => ({ ...entry, attrs: { ...entry.attrs } });
4
7
  const copyEntry = (entry) => entry.type === "link" ? copyLink(entry) : {
5
8
  ...entry,
@@ -23,10 +26,18 @@ const setRouteSidebar = (route, currentPath, sidebar, paginationSource) => {
23
26
  route.hasSidebar = sidebar.length > 0;
24
27
  route.pagination = paginationSource ? buildPagination(paginationSource, currentPath) : { prev: void 0, next: void 0 };
25
28
  };
26
- const onRequest = defineRouteMiddleware((context) => {
29
+ const defaultRuntime = { BOOKS, BOOKS_LINK, TREESEED_LIBRARY_DOWNLOAD, TREESEED_LINKS };
30
+ const tenantConfig = loadTreeseedManifest();
31
+ const onRequest = defineRouteMiddleware(async (context) => {
27
32
  const route = context.locals.starlightRoute;
28
33
  const currentPath = normalizeHref(context.url.pathname);
29
- const activeBook = BOOKS.find(
34
+ if (!tenantModelRendered(tenantConfig, "books")) {
35
+ setRouteSidebar(route, currentPath, [], null);
36
+ return;
37
+ }
38
+ const runtime = await loadHostedBookRuntime(context.locals) ?? defaultRuntime;
39
+ route.sidebar = buildStarlightSidebarEntriesFromRuntime(runtime, currentPath);
40
+ const activeBook = runtime.BOOKS.find(
30
41
  (book) => currentPath.startsWith(normalizeHref(book.basePath))
31
42
  );
32
43
  if (activeBook) {
@@ -35,7 +46,7 @@ const onRequest = defineRouteMiddleware((context) => {
35
46
  setRouteSidebar(route, currentPath, [copyEntry(bookGroup)], bookGroup.entries);
36
47
  return;
37
48
  }
38
- if (currentPath === normalizeHref(TREESEED_LINKS.home)) {
49
+ if (currentPath === normalizeHref(runtime.TREESEED_LINKS.home)) {
39
50
  setRouteSidebar(route, currentPath, [], null);
40
51
  return;
41
52
  }
@@ -2,23 +2,25 @@
2
2
  import { getCollection, render } from 'astro:content';
3
3
  import ContentLayout from '../layouts/ContentLayout.astro';
4
4
  import BridgeLayout from '../layouts/BridgeLayout.astro';
5
+ import RouteNotFound from '../components/site/RouteNotFound.astro';
5
6
 
6
- export async function getStaticPaths() {
7
- const entries = await getCollection('pages');
7
+ export const prerender = false;
8
8
 
9
- return entries.map((entry) => ({
10
- params: { slug: entry.data.slug },
11
- props: { entry },
12
- }));
9
+ const slug = String(Astro.params.slug ?? '');
10
+ const entries = await getCollection('pages');
11
+ const entry = entries.find((candidate) => candidate.data.slug === slug) ?? null;
12
+ if (!entry) {
13
+ Astro.response.status = 404;
13
14
  }
14
-
15
- const { entry } = Astro.props;
16
- const { Content } = await render(entry);
15
+ const rendered = entry ? await render(entry) : null;
16
+ const Content = rendered?.Content ?? null;
17
17
  const currentPath = Astro.url.pathname;
18
18
  ---
19
19
 
20
20
  {
21
- entry.data.pageLayout === 'bridge' ? (
21
+ !entry || !Content ? (
22
+ <RouteNotFound title="Page not found" description="The requested page is not available in this Treeseed." currentPath={currentPath} />
23
+ ) : entry.data.pageLayout === 'bridge' ? (
22
24
  <BridgeLayout entry={entry.data} currentPath={currentPath}>
23
25
  <Content />
24
26
  </BridgeLayout>
@@ -2,28 +2,35 @@
2
2
  import { getCollection, render } from 'astro:content';
3
3
  import ProfileLayout from '../../layouts/ProfileLayout.astro';
4
4
  import { resolveReferences } from '../../utils/hub-content';
5
+ import RouteNotFound from '../../components/site/RouteNotFound.astro';
5
6
 
6
- export async function getStaticPaths() {
7
- const agents = await getCollection('agents');
8
- return agents.map((agent) => ({
9
- params: { slug: agent.id },
10
- props: { agent },
11
- }));
12
- }
7
+ export const prerender = false;
13
8
 
14
- const { agent } = Astro.props;
15
- const { Content } = await render(agent);
16
- const relatedQuestions = await resolveReferences(agent.data.relatedQuestions);
17
- const relatedObjectives = await resolveReferences(agent.data.relatedObjectives);
9
+ const slug = String(Astro.params.slug ?? '');
10
+ const agents = await getCollection('agents');
11
+ const agent = agents.find((candidate) => candidate.id === slug) ?? null;
12
+ if (!agent) {
13
+ Astro.response.status = 404;
14
+ }
15
+ const rendered = agent ? await render(agent) : null;
16
+ const Content = rendered?.Content ?? null;
17
+ const relatedQuestions = agent ? await resolveReferences(agent.data.relatedQuestions) : [];
18
+ const relatedObjectives = agent ? await resolveReferences(agent.data.relatedObjectives) : [];
18
19
  ---
19
20
 
20
- <ProfileLayout
21
- entry={agent.data}
22
- currentPath="/agents/"
23
- metaLabel="Operator"
24
- metaValue={`${agent.data.operator} · ${agent.data.runtimeStatus}`}
25
- relatedQuestions={relatedQuestions}
26
- relatedObjectives={relatedObjectives}
27
- >
28
- <Content />
29
- </ProfileLayout>
21
+ {
22
+ !agent || !Content ? (
23
+ <RouteNotFound title="Agent not found" description="The requested agent profile could not be found in this Treeseed." currentPath="/agents/" />
24
+ ) : (
25
+ <ProfileLayout
26
+ entry={agent.data}
27
+ currentPath="/agents/"
28
+ metaLabel="Operator"
29
+ metaValue={`${agent.data.operator} · ${agent.data.runtimeStatus}`}
30
+ relatedQuestions={relatedQuestions}
31
+ relatedObjectives={relatedObjectives}
32
+ >
33
+ <Content />
34
+ </ProfileLayout>
35
+ )
36
+ }
@@ -1,19 +1,26 @@
1
1
  ---
2
2
  import { getCollection, render } from 'astro:content';
3
3
  import BookLayout from '../../layouts/BookLayout.astro';
4
+ import RouteNotFound from '../../components/site/RouteNotFound.astro';
4
5
 
5
- export async function getStaticPaths() {
6
- const books = (await getCollection('books')).sort((a, b) => a.data.order - b.data.order);
7
- return books.map((book) => ({
8
- params: { slug: book.id },
9
- props: { book },
10
- }));
11
- }
6
+ export const prerender = false;
12
7
 
13
- const { book } = Astro.props;
14
- const { Content } = await render(book);
8
+ const slug = String(Astro.params.slug ?? '');
9
+ const books = (await getCollection('books')).sort((a, b) => a.data.order - b.data.order);
10
+ const book = books.find((candidate) => candidate.id === slug) ?? null;
11
+ if (!book) {
12
+ Astro.response.status = 404;
13
+ }
14
+ const rendered = book ? await render(book) : null;
15
+ const Content = rendered?.Content ?? null;
15
16
  ---
16
17
 
17
- <BookLayout entry={book.data} currentPath="/books/">
18
- <Content />
19
- </BookLayout>
18
+ {
19
+ !book || !Content ? (
20
+ <RouteNotFound title="Book not found" description="The requested book could not be found in this Treeseed." currentPath="/books/" />
21
+ ) : (
22
+ <BookLayout entry={book.data} currentPath="/books/">
23
+ <Content />
24
+ </BookLayout>
25
+ )
26
+ }