@treeseed/core 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@ This repository is the package root. Run package commands from [`core`](./), not
6
6
 
7
7
  ## Requirements
8
8
 
9
- - Node `>=20`
9
+ - Node `>=22`
10
10
  - npm `>=10`
11
11
 
12
12
  ## Install
@@ -0,0 +1 @@
1
+ export declare function createTreeseedTenantSite(manifestPath?: string): import('astro').AstroUserConfig<never, never, never>;
@@ -0,0 +1,10 @@
1
+ export declare function createTreeseedTenantCollections(manifestPath?: string): {
2
+ pages: any;
3
+ notes: any;
4
+ questions: any;
5
+ objectives: any;
6
+ people: any;
7
+ agents: any;
8
+ books: any;
9
+ docs: any;
10
+ };
package/dist/content.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  PEOPLE_MODEL_DEFAULTS,
15
15
  QUESTION_MODEL_DEFAULTS
16
16
  } from "./utils/site-config.js";
17
+ import { preprocessAliasedRecord } from "@treeseed/sdk/field-aliases";
17
18
  const statusValues = ["live", "in progress", "exploratory", "planned", "speculative"];
18
19
  const pageLayoutValues = ["article", "bridge"];
19
20
  const questionTypeValues = ["research", "implementation", "strategy", "evaluation"];
@@ -63,6 +64,39 @@ function resolveDocsCollectionProvider(tenantConfig, dependencies) {
63
64
  throw new Error(`Treeseed docs provider "${selectedId}" is not registered.`);
64
65
  }
65
66
  function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
67
+ const pageFieldAliases = {
68
+ pageLayout: { key: "pageLayout", aliases: ["page_layout"] },
69
+ seoTitle: { key: "seoTitle", aliases: ["seo_title"] },
70
+ seoDescription: { key: "seoDescription", aliases: ["seo_description"] }
71
+ };
72
+ const questionFieldAliases = {
73
+ questionType: { key: "questionType", aliases: ["question_type"] },
74
+ primaryContributor: { key: "primaryContributor", aliases: ["primary_contributor"] },
75
+ relatedObjectives: { key: "relatedObjectives", aliases: ["related_objectives"] },
76
+ relatedBooks: { key: "relatedBooks", aliases: ["related_books"] }
77
+ };
78
+ const objectiveFieldAliases = {
79
+ timeHorizon: { key: "timeHorizon", aliases: ["time_horizon"] },
80
+ primaryContributor: { key: "primaryContributor", aliases: ["primary_contributor"] },
81
+ relatedQuestions: { key: "relatedQuestions", aliases: ["related_questions"] },
82
+ relatedBooks: { key: "relatedBooks", aliases: ["related_books"] }
83
+ };
84
+ const agentFieldAliases = {
85
+ runtimeStatus: { key: "runtimeStatus", aliases: ["runtime_status"] },
86
+ systemPrompt: { key: "systemPrompt", aliases: ["system_prompt"] },
87
+ triggerPolicy: { key: "triggerPolicy", aliases: ["trigger_policy"] }
88
+ };
89
+ const bookFieldAliases = {
90
+ sectionLabel: { key: "sectionLabel", aliases: ["section_label"] },
91
+ basePath: { key: "basePath", aliases: ["base_path"] },
92
+ landingPath: { key: "landingPath", aliases: ["landing_path"] },
93
+ outlinePath: { key: "outlinePath", aliases: ["outline_path"] },
94
+ downloadFileName: { key: "downloadFileName", aliases: ["download_file_name"] },
95
+ downloadHref: { key: "downloadHref", aliases: ["download_href"] },
96
+ downloadTitle: { key: "downloadTitle", aliases: ["download_title"] },
97
+ exportRoots: { key: "exportRoots", aliases: ["export_roots"] },
98
+ sidebarItems: { key: "sidebarItems", aliases: ["sidebar_items"] }
99
+ };
66
100
  const contributorReference = z.union([reference("people"), reference("agents")]);
67
101
  const sidebarItemSchema = z.lazy(
68
102
  () => z.object({
@@ -72,7 +106,7 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
72
106
  items: z.array(sidebarItemSchema).optional()
73
107
  })
74
108
  );
75
- const pageSchema = z.object({
109
+ const pageSchema = z.preprocess((value) => preprocessAliasedRecord(pageFieldAliases, value), z.object({
76
110
  title: z.string(),
77
111
  description: z.string(),
78
112
  slug: z.string(),
@@ -84,7 +118,7 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
84
118
  updated: z.coerce.date(),
85
119
  seoTitle: z.string().optional(),
86
120
  seoDescription: z.string().optional()
87
- });
121
+ }));
88
122
  const noteSchema = z.object({
89
123
  title: z.string(),
90
124
  description: z.string(),
@@ -96,7 +130,7 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
96
130
  draft: z.boolean().default(NOTE_MODEL_DEFAULTS.draft ?? false),
97
131
  canonicalRoute: z.string().optional()
98
132
  });
99
- const questionSchema = z.object({
133
+ const questionSchema = z.preprocess((value) => preprocessAliasedRecord(questionFieldAliases, value), z.object({
100
134
  title: z.string(),
101
135
  description: z.string(),
102
136
  date: z.coerce.date(),
@@ -109,8 +143,8 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
109
143
  primaryContributor: contributorReference,
110
144
  relatedObjectives: z.array(reference("objectives")).default([]),
111
145
  relatedBooks: z.array(reference("books")).default([])
112
- });
113
- const objectiveSchema = z.object({
146
+ }));
147
+ const objectiveSchema = z.preprocess((value) => preprocessAliasedRecord(objectiveFieldAliases, value), z.object({
114
148
  title: z.string(),
115
149
  description: z.string(),
116
150
  date: z.coerce.date(),
@@ -123,7 +157,7 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
123
157
  primaryContributor: contributorReference,
124
158
  relatedQuestions: z.array(reference("questions")).default([]),
125
159
  relatedBooks: z.array(reference("books")).default([])
126
- });
160
+ }));
127
161
  const profileLinkSchema = z.object({ label: z.string(), href: z.string() });
128
162
  const agentCliSchema = z.object({
129
163
  model: z.string().optional(),
@@ -170,7 +204,7 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
170
204
  relatedQuestions: z.array(reference("questions")).default([]),
171
205
  relatedObjectives: z.array(reference("objectives")).default([])
172
206
  });
173
- const agentSchema = z.object({
207
+ const agentSchema = z.preprocess((value) => preprocessAliasedRecord(agentFieldAliases, value), z.object({
174
208
  name: z.string(),
175
209
  slug: z.string(),
176
210
  handler: z.string(),
@@ -192,8 +226,8 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
192
226
  permissions: z.array(agentPermissionSchema).min(1),
193
227
  execution: agentExecutionSchema.default({}),
194
228
  outputs: agentOutputSchema.default({})
195
- });
196
- const bookSchema = z.object({
229
+ }));
230
+ const bookSchema = z.preprocess((value) => preprocessAliasedRecord(bookFieldAliases, value), z.object({
197
231
  order: z.number().int().nonnegative(),
198
232
  slug: z.string(),
199
233
  title: z.string(),
@@ -209,7 +243,7 @@ function createTreeseedCollections(tenantConfig, { docsLoader, docsSchema }) {
209
243
  exportRoots: z.array(z.string()).min(1).optional(),
210
244
  sidebarItems: z.array(sidebarItemSchema).min(1),
211
245
  tags: z.array(z.string()).default(BOOK_MODEL_DEFAULTS.tags ?? [])
212
- });
246
+ }));
213
247
  const docsCollectionProvider = resolveDocsCollectionProvider(tenantConfig, { docsLoader, docsSchema });
214
248
  return {
215
249
  pages: defineCollection({ loader: glob({ pattern: "**/*.{md,mdx}", base: tenantConfig.content.pages }), schema: pageSchema }),
@@ -1,130 +1 @@
1
- export type TreeseedFeatureName = 'docs' | 'books' | 'notes' | 'questions' | 'objectives' | 'agents' | 'forms';
2
- export type TreeseedContentCollection = 'pages' | 'notes' | 'questions' | 'objectives' | 'people' | 'agents' | 'books' | 'docs';
3
- export interface TreeseedFeatureModules {
4
- docs?: boolean;
5
- books?: boolean;
6
- notes?: boolean;
7
- questions?: boolean;
8
- objectives?: boolean;
9
- agents?: boolean;
10
- forms?: boolean;
11
- [key: string]: boolean | undefined;
12
- }
13
- export interface TreeseedContentMap {
14
- pages: string;
15
- notes: string;
16
- questions: string;
17
- objectives: string;
18
- people: string;
19
- agents: string;
20
- books: string;
21
- docs: string;
22
- }
23
- export interface TreeseedBookDefinition {
24
- order: number;
25
- slug: string;
26
- title: string;
27
- description: string;
28
- summary: string;
29
- sectionLabel: string;
30
- basePath: string;
31
- landingPath: string;
32
- outlinePath?: string;
33
- downloadFileName: string;
34
- downloadHref: string;
35
- downloadTitle: string;
36
- exportRoots?: string[];
37
- sidebarItems: Array<{
38
- label: string;
39
- link?: string;
40
- autogenerate?: {
41
- directory: string;
42
- };
43
- items?: TreeseedBookDefinition['sidebarItems'];
44
- }>;
45
- tags?: string[];
46
- id?: string;
47
- }
48
- export interface TreeseedThemeConfig {
49
- surfaces?: {
50
- background?: string;
51
- backgroundElevated?: string;
52
- backgroundSoft?: string;
53
- panel?: string;
54
- panelStrong?: string;
55
- };
56
- text?: {
57
- body?: string;
58
- muted?: string;
59
- soft?: string;
60
- };
61
- border?: {
62
- base?: string;
63
- strong?: string;
64
- grid?: string;
65
- };
66
- accent?: {
67
- base?: string;
68
- strong?: string;
69
- soft?: string;
70
- };
71
- info?: {
72
- base?: string;
73
- strong?: string;
74
- soft?: string;
75
- };
76
- warm?: {
77
- base?: string;
78
- strong?: string;
79
- };
80
- }
81
- export interface TreeseedPluginReference {
82
- package: string;
83
- enabled?: boolean;
84
- config?: Record<string, unknown>;
85
- }
86
- export interface TreeseedProviderSelections {
87
- forms: string;
88
- agents: {
89
- execution: string;
90
- mutation: string;
91
- repository: string;
92
- verification: string;
93
- notification: string;
94
- research: string;
95
- };
96
- deploy: string;
97
- content?: {
98
- docs: string;
99
- };
100
- site?: string;
101
- }
102
- export interface TreeseedDeployConfig {
103
- name: string;
104
- slug: string;
105
- siteUrl: string;
106
- contactEmail: string;
107
- cloudflare: {
108
- accountId: string;
109
- workerName?: string;
110
- };
111
- plugins: TreeseedPluginReference[];
112
- providers: TreeseedProviderSelections;
113
- smtp?: {
114
- enabled?: boolean;
115
- };
116
- turnstile?: {
117
- enabled?: boolean;
118
- };
119
- }
120
- export interface TreeseedTenantConfig {
121
- id: string;
122
- siteConfigPath: string;
123
- content: TreeseedContentMap;
124
- features: TreeseedFeatureModules;
125
- overrides?: {
126
- pagesRoot?: string;
127
- stylesRoot?: string;
128
- componentsRoot?: string;
129
- };
130
- }
1
+ export type { TreeseedBookDefinition, TreeseedContentCollection, TreeseedContentMap, TreeseedDeployConfig, TreeseedFeatureModules, TreeseedFeatureName, TreeseedManagedServiceConfig, TreeseedManagedServiceEnvironmentConfig, TreeseedManagedServiceRailwayConfig, TreeseedManagedServicesConfig, TreeseedPluginReference, TreeseedProviderSelections, TreeseedTenantConfig, TreeseedThemeConfig, } from '@treeseed/sdk/platform/contracts';
@@ -1,4 +1 @@
1
- import type { TreeseedDeployConfig } from '../contracts';
2
- export declare function resolveTreeseedDeployConfigPath(configPath?: string): string;
3
- export declare function deriveCloudflareWorkerName(config: TreeseedDeployConfig): string;
4
- export declare function loadTreeseedDeployConfig(configPath?: string): TreeseedDeployConfig;
1
+ export { deriveCloudflareWorkerName, loadTreeseedDeployConfig, resolveTreeseedDeployConfigPath, } from '@treeseed/sdk/platform/deploy-config';
@@ -1,152 +1,8 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { dirname, resolve } from "node:path";
3
- import { parse as parseYaml } from "yaml";
4
- import { resolveTreeseedTenantRoot } from "../tenant/config.js";
5
1
  import {
6
- TREESEED_DEFAULT_PLUGIN_REFERENCES,
7
- TREESEED_DEFAULT_PROVIDER_SELECTIONS
8
- } from "../plugins/constants.js";
9
- function expectString(value, label) {
10
- if (typeof value !== "string" || !value.trim()) {
11
- throw new Error(`Invalid deploy config: expected ${label} to be a non-empty string.`);
12
- }
13
- return value.trim();
14
- }
15
- function optionalString(value) {
16
- if (typeof value !== "string" || !value.trim()) {
17
- return void 0;
18
- }
19
- return value.trim();
20
- }
21
- function optionalBoolean(value, label) {
22
- if (value === void 0) {
23
- return void 0;
24
- }
25
- if (typeof value !== "boolean") {
26
- throw new Error(`Invalid deploy config: expected ${label} to be a boolean when provided.`);
27
- }
28
- return value;
29
- }
30
- function optionalRecord(value, label) {
31
- if (value === void 0 || value === null) {
32
- return void 0;
33
- }
34
- if (typeof value !== "object" || Array.isArray(value)) {
35
- throw new Error(`Invalid deploy config: expected ${label} to be an object when provided.`);
36
- }
37
- return value;
38
- }
39
- function parsePluginReferences(value) {
40
- if (value === void 0) {
41
- return [...TREESEED_DEFAULT_PLUGIN_REFERENCES];
42
- }
43
- if (!Array.isArray(value)) {
44
- throw new Error("Invalid deploy config: expected plugins to be an array.");
45
- }
46
- return value.map((entry, index) => {
47
- const record = optionalRecord(entry, `plugins[${index}]`);
48
- return {
49
- package: expectString(record?.package, `plugins[${index}].package`),
50
- enabled: record?.enabled === void 0 ? true : optionalBoolean(record.enabled, `plugins[${index}].enabled`),
51
- config: record?.config === void 0 ? {} : optionalRecord(record.config, `plugins[${index}].config`)
52
- };
53
- });
54
- }
55
- function parseProviderSelections(value) {
56
- const record = optionalRecord(value, "providers");
57
- if (!record) {
58
- return structuredClone(TREESEED_DEFAULT_PROVIDER_SELECTIONS);
59
- }
60
- const agentProviders = optionalRecord(record.agents, "providers.agents") ?? {};
61
- const contentProviders = optionalRecord(record.content, "providers.content") ?? {};
62
- return {
63
- forms: expectString(record.forms ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.forms, "providers.forms"),
64
- agents: {
65
- execution: expectString(
66
- agentProviders.execution ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.agents.execution,
67
- "providers.agents.execution"
68
- ),
69
- mutation: expectString(
70
- agentProviders.mutation ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.agents.mutation,
71
- "providers.agents.mutation"
72
- ),
73
- repository: expectString(
74
- agentProviders.repository ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.agents.repository,
75
- "providers.agents.repository"
76
- ),
77
- verification: expectString(
78
- agentProviders.verification ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.agents.verification,
79
- "providers.agents.verification"
80
- ),
81
- notification: expectString(
82
- agentProviders.notification ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.agents.notification,
83
- "providers.agents.notification"
84
- ),
85
- research: expectString(
86
- agentProviders.research ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.agents.research,
87
- "providers.agents.research"
88
- )
89
- },
90
- deploy: expectString(record.deploy ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.deploy, "providers.deploy"),
91
- content: {
92
- docs: expectString(
93
- contentProviders.docs ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.docs,
94
- "providers.content.docs"
95
- )
96
- },
97
- site: expectString(record.site ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.site, "providers.site")
98
- };
99
- }
100
- function parseDeployConfig(raw) {
101
- const parsed = parseYaml(raw) ?? {};
102
- const cloudflare = optionalRecord(parsed.cloudflare, "cloudflare") ?? {};
103
- const smtp = optionalRecord(parsed.smtp, "smtp") ?? {};
104
- const turnstile = optionalRecord(parsed.turnstile, "turnstile") ?? {};
105
- optionalBoolean(turnstile.enabled, "turnstile.enabled");
106
- return {
107
- name: expectString(parsed.name, "name"),
108
- slug: expectString(parsed.slug, "slug"),
109
- siteUrl: expectString(parsed.siteUrl, "siteUrl"),
110
- contactEmail: expectString(parsed.contactEmail, "contactEmail"),
111
- cloudflare: {
112
- accountId: optionalString(cloudflare.accountId) ?? optionalString(process.env.CLOUDFLARE_ACCOUNT_ID) ?? "replace-with-cloudflare-account-id",
113
- workerName: optionalString(cloudflare.workerName)
114
- },
115
- plugins: parsePluginReferences(parsed.plugins),
116
- providers: parseProviderSelections(parsed.providers),
117
- smtp: {
118
- enabled: optionalBoolean(smtp.enabled, "smtp.enabled")
119
- },
120
- turnstile: {
121
- enabled: true
122
- }
123
- };
124
- }
125
- function resolveTreeseedDeployConfigPath(configPath = "treeseed.site.yaml") {
126
- const tenantRoot = resolveTreeseedTenantRoot();
127
- const candidate = resolve(tenantRoot, configPath);
128
- if (!existsSync(candidate)) {
129
- throw new Error(`Unable to resolve Treeseed deploy config at "${candidate}".`);
130
- }
131
- return candidate;
132
- }
133
- function deriveCloudflareWorkerName(config) {
134
- return config.cloudflare.workerName?.trim() || config.slug;
135
- }
136
- function loadTreeseedDeployConfig(configPath = "treeseed.site.yaml") {
137
- const resolvedConfigPath = resolveTreeseedDeployConfigPath(configPath);
138
- const tenantRoot = dirname(resolvedConfigPath);
139
- const parsed = parseDeployConfig(readFileSync(resolvedConfigPath, "utf8"));
140
- Object.defineProperty(parsed, "__tenantRoot", {
141
- value: tenantRoot,
142
- enumerable: false
143
- });
144
- Object.defineProperty(parsed, "__configPath", {
145
- value: resolvedConfigPath,
146
- enumerable: false
147
- });
148
- return parsed;
149
- }
2
+ deriveCloudflareWorkerName,
3
+ loadTreeseedDeployConfig,
4
+ resolveTreeseedDeployConfigPath
5
+ } from "@treeseed/sdk/platform/deploy-config";
150
6
  export {
151
7
  deriveCloudflareWorkerName,
152
8
  loadTreeseedDeployConfig,
@@ -46,6 +46,9 @@ function resetTreeseedDeployConfigForTests() {
46
46
  function getTreeseedFormsProvider() {
47
47
  return getTreeseedDeployConfig().providers?.forms ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.forms;
48
48
  }
49
+ function getTreeseedOperationsProvider() {
50
+ return getTreeseedDeployConfig().providers?.operations ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.operations;
51
+ }
49
52
  function getTreeseedAgentProviderSelections() {
50
53
  return getTreeseedDeployConfig().providers?.agents ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.agents;
51
54
  }
@@ -70,6 +73,7 @@ export {
70
73
  getTreeseedDeployProvider,
71
74
  getTreeseedDocsProvider,
72
75
  getTreeseedFormsProvider,
76
+ getTreeseedOperationsProvider,
73
77
  getTreeseedSiteProvider,
74
78
  isTreeseedSmtpEnabled,
75
79
  isTreeseedTurnstileEnabled,
package/dist/env.yaml CHANGED
@@ -1,38 +1,109 @@
1
1
  entries:
2
- CLOUDFLARE_ACCOUNT_ID:
3
- label: Cloudflare account ID
4
- group: cloudflare
5
- description: Identifies the Cloudflare account Treeseed should provision and deploy into.
6
- howToGet: In the Cloudflare dashboard, open Workers & Pages or Account Home and copy the account ID.
7
- sensitivity: plain
2
+ GH_TOKEN:
3
+ label: GitHub access token
4
+ group: auth
5
+ description: Personal access token used by Treeseed for GitHub CLI automation, repository setup, CI secret sync, and GitHub Copilot SDK-backed workflows.
6
+ howToGet: "Create a GitHub fine-grained personal access token scoped to the TreeSeed repository. Required repository permissions: Contents read/write, Environments read/write, Secrets and variables read/write, Actions and workflows read/write, Pull requests read/write, Issues read/write. Then paste it here as GH_TOKEN."
7
+ sensitivity: secret
8
8
  targets:
9
9
  - local-file
10
- - github-variable
11
- - config-file
10
+ - railway-secret
12
11
  scopes:
12
+ - local
13
13
  - staging
14
14
  - prod
15
15
  requirement: required
16
16
  purposes:
17
17
  - save
18
- - deploy
19
- - destroy
20
18
  - config
21
19
  validation:
22
20
  kind: nonempty
23
21
  sourcePriority:
24
22
  - machine-config
25
23
  - process-env
26
- - treeseed.site.yaml
27
24
  CLOUDFLARE_API_TOKEN:
28
25
  label: Cloudflare API token
29
- group: cloudflare
30
- description: Allows Wrangler and CI to create resources, upload secrets, and publish the Worker.
31
- howToGet: Create an API token in the Cloudflare dashboard with Workers and D1 permissions for this account.
26
+ group: auth
27
+ description: Account-level Cloudflare API token used by Wrangler and CI to manage Pages, Workers, Workers KV, D1, Queues, DNS, secrets, and deploys.
28
+ howToGet: "Create a Cloudflare account-level API token scoped to the target domain and account. Required permissions: Account Cloudflare Pages edit, Account Workers Scripts edit, Account Workers KV Storage edit, Account D1 edit, Account Queues edit, Zone DNS edit. Then paste it here as CLOUDFLARE_API_TOKEN."
32
29
  sensitivity: secret
33
30
  targets:
34
31
  - local-file
35
32
  - github-secret
33
+ scopes:
34
+ - local
35
+ - staging
36
+ - prod
37
+ requirement: required
38
+ purposes:
39
+ - save
40
+ - deploy
41
+ - destroy
42
+ - config
43
+ validation:
44
+ kind: nonempty
45
+ sourcePriority:
46
+ - machine-config
47
+ - process-env
48
+ RAILWAY_API_TOKEN:
49
+ label: Railway API token
50
+ group: auth
51
+ description: Primary Railway token for user or workspace scoped access, including project creation and most Treeseed-managed Railway flows.
52
+ howToGet: In Railway, create a user or workspace API token that can create and manage the target project, then paste it here as RAILWAY_API_TOKEN.
53
+ sensitivity: secret
54
+ targets:
55
+ - local-file
56
+ - railway-secret
57
+ scopes:
58
+ - local
59
+ - staging
60
+ - prod
61
+ requirement: conditional
62
+ purposes:
63
+ - deploy
64
+ - destroy
65
+ - config
66
+ validation:
67
+ kind: nonempty
68
+ sourcePriority:
69
+ - machine-config
70
+ - process-env
71
+ relevanceRef: railwayManagedEnabled
72
+ requiredWhenRef: railwayManagedEnabled
73
+ RAILWAY_TOKEN:
74
+ label: Railway project token
75
+ group: auth
76
+ description: Optional Railway token for project-scoped resource management after a project already exists.
77
+ howToGet: In Railway, generate a project-scoped token only if you need project resource management separate from the primary API token, then paste it here as RAILWAY_TOKEN.
78
+ sensitivity: secret
79
+ targets:
80
+ - local-file
81
+ - railway-secret
82
+ scopes:
83
+ - local
84
+ - staging
85
+ - prod
86
+ requirement: optional
87
+ purposes:
88
+ - deploy
89
+ - destroy
90
+ - config
91
+ validation:
92
+ kind: nonempty
93
+ sourcePriority:
94
+ - machine-config
95
+ - process-env
96
+ relevanceRef: railwayManagedEnabled
97
+ CLOUDFLARE_ACCOUNT_ID:
98
+ label: Cloudflare account ID
99
+ group: cloudflare
100
+ description: Identifies the Cloudflare account Treeseed should provision and deploy into.
101
+ howToGet: In the Cloudflare dashboard, open Workers & Pages or Account Home and copy the account ID.
102
+ sensitivity: plain
103
+ targets:
104
+ - local-file
105
+ - github-variable
106
+ - config-file
36
107
  scopes:
37
108
  - staging
38
109
  - prod
@@ -47,6 +118,7 @@ entries:
47
118
  sourcePriority:
48
119
  - machine-config
49
120
  - process-env
121
+ - treeseed.site.yaml
50
122
  TREESEED_FORM_TOKEN_SECRET:
51
123
  label: Forms token secret
52
124
  group: forms