@omniharmonic/openspace-cli 0.0.0-stage → 0.1.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.
Files changed (211) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +74 -2
  3. package/dist/agents-md-2DTXWUZQ.js +8 -0
  4. package/dist/agents-md-2DTXWUZQ.js.map +1 -0
  5. package/dist/agents-md-TX6V4M64.js +8 -0
  6. package/dist/agents-md-TX6V4M64.js.map +1 -0
  7. package/dist/bin.d.ts +1 -0
  8. package/dist/bin.js +11 -0
  9. package/dist/bin.js.map +1 -0
  10. package/dist/chunk-2KUIUUKV.js +41167 -0
  11. package/dist/chunk-2KUIUUKV.js.map +1 -0
  12. package/dist/chunk-3F6TAUAT.js +41167 -0
  13. package/dist/chunk-3F6TAUAT.js.map +1 -0
  14. package/dist/chunk-LPHUKIJU.js +17120 -0
  15. package/dist/chunk-LPHUKIJU.js.map +1 -0
  16. package/dist/chunk-LTJEM3DN.js +17161 -0
  17. package/dist/chunk-LTJEM3DN.js.map +1 -0
  18. package/dist/index.d.ts +83 -0
  19. package/dist/index.js +23 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/starter/site/.github/workflows/publish.yml +38 -0
  22. package/dist/starter/site/AGENTS.md +392 -0
  23. package/dist/starter/site/astro.config.mjs +8 -0
  24. package/dist/starter/site/package.json +21 -0
  25. package/dist/starter/site/public/.gitkeep +0 -0
  26. package/dist/starter/site/src/pages/[...slug].astro +10 -0
  27. package/dist/starter/site/tsconfig.json +5 -0
  28. package/dist/starter/variants/cloudflare/.github/openspace/gated-sync.mjs +121 -0
  29. package/dist/starter/variants/cloudflare/.github/workflows/publish.yml +70 -0
  30. package/dist/starter/variants/cloudflare/astro.config.mjs +11 -0
  31. package/dist/starter/variants/cloudflare/package.json +23 -0
  32. package/dist/starter/variants/cloudflare/wrangler.jsonc +13 -0
  33. package/dist/templates/atelier/content/collections/projects/_collection.json +13 -0
  34. package/dist/templates/atelier/content/collections/projects/cordillera.mdoc +32 -0
  35. package/dist/templates/atelier/content/collections/projects/field-studies.mdoc +32 -0
  36. package/dist/templates/atelier/content/collections/projects/salt-and-glass.mdoc +32 -0
  37. package/dist/templates/atelier/content/collections/projects/white-city.mdoc +32 -0
  38. package/dist/templates/atelier/content/pages/about.json +180 -0
  39. package/dist/templates/atelier/content/pages/commissions.json +307 -0
  40. package/dist/templates/atelier/content/pages/contact.json +117 -0
  41. package/dist/templates/atelier/content/pages/home.json +524 -0
  42. package/dist/templates/atelier/content/pages/work.json +332 -0
  43. package/dist/templates/atelier/content/site.json +190 -0
  44. package/dist/templates/atelier/content/styles.json +25 -0
  45. package/dist/templates/atelier/media/LICENSES.json +218 -0
  46. package/dist/templates/atelier/openspace.json +21 -0
  47. package/dist/templates/atelier/template.json +115 -0
  48. package/dist/templates/commons/content/collections/gatherings/_collection.json +13 -0
  49. package/dist/templates/commons/content/collections/gatherings/grant-writing-workshop.mdoc +17 -0
  50. package/dist/templates/commons/content/collections/gatherings/seed-swap.mdoc +16 -0
  51. package/dist/templates/commons/content/collections/gatherings/winter-general-assembly.mdoc +17 -0
  52. package/dist/templates/commons/content/pages/about.json +227 -0
  53. package/dist/templates/commons/content/pages/home.json +540 -0
  54. package/dist/templates/commons/content/pages/join.json +304 -0
  55. package/dist/templates/commons/content/pages/members.json +499 -0
  56. package/dist/templates/commons/content/site.json +306 -0
  57. package/dist/templates/commons/content/styles.json +27 -0
  58. package/dist/templates/commons/media/LICENSES.json +108 -0
  59. package/dist/templates/commons/openspace.json +21 -0
  60. package/dist/templates/commons/template.json +113 -0
  61. package/dist/templates/encore/content/collections/sessions/_collection.json +19 -0
  62. package/dist/templates/encore/content/collections/sessions/session-10-headphones-on.mdoc +16 -0
  63. package/dist/templates/encore/content/collections/sessions/session-11-neon-tide.mdoc +16 -0
  64. package/dist/templates/encore/content/collections/sessions/session-12-slow-static.mdoc +16 -0
  65. package/dist/templates/encore/content/collections/tour/_collection.json +13 -0
  66. package/dist/templates/encore/content/collections/tour/brooklyn-elsewhere.mdoc +18 -0
  67. package/dist/templates/encore/content/collections/tour/chicago-thalia-hall.mdoc +18 -0
  68. package/dist/templates/encore/content/collections/tour/london-electric-ballroom.mdoc +18 -0
  69. package/dist/templates/encore/content/collections/tour/minneapolis-first-avenue.mdoc +18 -0
  70. package/dist/templates/encore/content/collections/tour/toronto-danforth.mdoc +18 -0
  71. package/dist/templates/encore/content/pages/about.json +251 -0
  72. package/dist/templates/encore/content/pages/contact.json +122 -0
  73. package/dist/templates/encore/content/pages/home.json +363 -0
  74. package/dist/templates/encore/content/pages/music.json +297 -0
  75. package/dist/templates/encore/content/site.json +293 -0
  76. package/dist/templates/encore/content/styles.json +17 -0
  77. package/dist/templates/encore/media/LICENSES.json +97 -0
  78. package/dist/templates/encore/openspace.json +21 -0
  79. package/dist/templates/encore/template.json +103 -0
  80. package/dist/templates/field-notes/content/collections/essays/_collection.json +31 -0
  81. package/dist/templates/field-notes/content/collections/essays/a-year-of-apples.mdoc +25 -0
  82. package/dist/templates/field-notes/content/collections/essays/learning-a-field.mdoc +32 -0
  83. package/dist/templates/field-notes/content/collections/essays/small-weather.mdoc +25 -0
  84. package/dist/templates/field-notes/content/collections/essays/the-wall-builders.mdoc +29 -0
  85. package/dist/templates/field-notes/content/pages/about.json +236 -0
  86. package/dist/templates/field-notes/content/pages/contact.json +116 -0
  87. package/dist/templates/field-notes/content/pages/home.json +428 -0
  88. package/dist/templates/field-notes/content/pages/subscribe.json +273 -0
  89. package/dist/templates/field-notes/content/site.json +218 -0
  90. package/dist/templates/field-notes/content/styles.json +27 -0
  91. package/dist/templates/field-notes/media/LICENSES.json +119 -0
  92. package/dist/templates/field-notes/openspace.json +21 -0
  93. package/dist/templates/field-notes/template.json +112 -0
  94. package/dist/templates/harbor/content/collections/events/_collection.json +13 -0
  95. package/dist/templates/harbor/content/collections/events/coat-drive-sorting-day.mdoc +17 -0
  96. package/dist/templates/harbor/content/collections/events/safe-at-home-build-day.mdoc +17 -0
  97. package/dist/templates/harbor/content/collections/events/spring-community-supper.mdoc +17 -0
  98. package/dist/templates/harbor/content/collections/events/winter-coat-giveaway.mdoc +16 -0
  99. package/dist/templates/harbor/content/collections/news/2026-08-28-back-to-school-at-homework-harbor.mdoc +17 -0
  100. package/dist/templates/harbor/content/collections/news/2026-09-15-meet-the-repair-crew.mdoc +21 -0
  101. package/dist/templates/harbor/content/collections/news/2026-10-08-the-2025-annual-report.mdoc +25 -0
  102. package/dist/templates/harbor/content/collections/news/_collection.json +34 -0
  103. package/dist/templates/harbor/content/pages/about.json +366 -0
  104. package/dist/templates/harbor/content/pages/contact.json +233 -0
  105. package/dist/templates/harbor/content/pages/get-involved.json +425 -0
  106. package/dist/templates/harbor/content/pages/home.json +524 -0
  107. package/dist/templates/harbor/content/pages/programs.json +611 -0
  108. package/dist/templates/harbor/content/site.json +377 -0
  109. package/dist/templates/harbor/content/styles.json +25 -0
  110. package/dist/templates/harbor/media/LICENSES.json +141 -0
  111. package/dist/templates/harbor/openspace.json +21 -0
  112. package/dist/templates/harbor/template.json +115 -0
  113. package/dist/templates/hearth/content/pages/about.json +246 -0
  114. package/dist/templates/hearth/content/pages/home.json +937 -0
  115. package/dist/templates/hearth/content/pages/menu.json +396 -0
  116. package/dist/templates/hearth/content/pages/private-dining.json +288 -0
  117. package/dist/templates/hearth/content/pages/visit.json +257 -0
  118. package/dist/templates/hearth/content/site.json +214 -0
  119. package/dist/templates/hearth/content/styles.json +25 -0
  120. package/dist/templates/hearth/media/LICENSES.json +196 -0
  121. package/dist/templates/hearth/openspace.json +21 -0
  122. package/dist/templates/hearth/template.json +116 -0
  123. package/dist/templates/kiln/content/collections/classes/_collection.json +13 -0
  124. package/dist/templates/kiln/content/collections/classes/glaze-day-march.mdoc +17 -0
  125. package/dist/templates/kiln/content/collections/classes/hand-building-february.mdoc +17 -0
  126. package/dist/templates/kiln/content/collections/classes/spring-studio-sale.mdoc +15 -0
  127. package/dist/templates/kiln/content/collections/classes/wheel-weekend-january.mdoc +17 -0
  128. package/dist/templates/kiln/content/collections/shop/_collection.json +14 -0
  129. package/dist/templates/kiln/content/collections/shop/ash-bud-vases.mdoc +18 -0
  130. package/dist/templates/kiln/content/collections/shop/harbor-planter.mdoc +18 -0
  131. package/dist/templates/kiln/content/collections/shop/morning-cup-pair.mdoc +24 -0
  132. package/dist/templates/kiln/content/collections/shop/sea-glass-planter.mdoc +18 -0
  133. package/dist/templates/kiln/content/collections/shop/tide-vase.mdoc +23 -0
  134. package/dist/templates/kiln/content/collections/shop/walnut-step-stool.mdoc +18 -0
  135. package/dist/templates/kiln/content/pages/about.json +344 -0
  136. package/dist/templates/kiln/content/pages/contact.json +232 -0
  137. package/dist/templates/kiln/content/pages/home.json +754 -0
  138. package/dist/templates/kiln/content/pages/workshops.json +352 -0
  139. package/dist/templates/kiln/content/site.json +263 -0
  140. package/dist/templates/kiln/content/styles.json +17 -0
  141. package/dist/templates/kiln/media/LICENSES.json +119 -0
  142. package/dist/templates/kiln/openspace.json +21 -0
  143. package/dist/templates/kiln/template.json +103 -0
  144. package/dist/templates/ledger/content/collections/case-studies/_collection.json +14 -0
  145. package/dist/templates/ledger/content/collections/case-studies/fieldwork-coffee.mdoc +29 -0
  146. package/dist/templates/ledger/content/collections/case-studies/larkspur-foods.mdoc +29 -0
  147. package/dist/templates/ledger/content/collections/case-studies/ostrander-clinics.mdoc +29 -0
  148. package/dist/templates/ledger/content/collections/insights/2026-08-10-one-page-of-numbers.mdoc +12 -0
  149. package/dist/templates/ledger/content/collections/insights/2026-09-07-hiring-your-first-coo.mdoc +17 -0
  150. package/dist/templates/ledger/content/collections/insights/2026-10-05-the-decision-log.mdoc +15 -0
  151. package/dist/templates/ledger/content/collections/insights/_collection.json +21 -0
  152. package/dist/templates/ledger/content/pages/about.json +298 -0
  153. package/dist/templates/ledger/content/pages/contact.json +158 -0
  154. package/dist/templates/ledger/content/pages/home.json +621 -0
  155. package/dist/templates/ledger/content/pages/services.json +639 -0
  156. package/dist/templates/ledger/content/site.json +336 -0
  157. package/dist/templates/ledger/content/styles.json +17 -0
  158. package/dist/templates/ledger/media/LICENSES.json +86 -0
  159. package/dist/templates/ledger/openspace.json +21 -0
  160. package/dist/templates/ledger/template.json +102 -0
  161. package/dist/templates/link/content/pages/home.json +278 -0
  162. package/dist/templates/link/content/site.json +168 -0
  163. package/dist/templates/link/content/styles.json +17 -0
  164. package/dist/templates/link/media/LICENSES.json +97 -0
  165. package/dist/templates/link/openspace.json +21 -0
  166. package/dist/templates/link/template.json +87 -0
  167. package/dist/templates/meadow/content/collections/journal/2026-09-02-meet-daniel.mdoc +20 -0
  168. package/dist/templates/meadow/content/collections/journal/2026-09-28-a-practice-for-dark-mornings.mdoc +24 -0
  169. package/dist/templates/meadow/content/collections/journal/2026-10-12-why-twelve-mats.mdoc +26 -0
  170. package/dist/templates/meadow/content/collections/journal/_collection.json +34 -0
  171. package/dist/templates/meadow/content/collections/workshops/_collection.json +13 -0
  172. package/dist/templates/meadow/content/collections/workshops/breath-and-rest-workshop.mdoc +17 -0
  173. package/dist/templates/meadow/content/collections/workshops/foundations-four-weeks.mdoc +17 -0
  174. package/dist/templates/meadow/content/collections/workshops/manzanita-spring-retreat.mdoc +17 -0
  175. package/dist/templates/meadow/content/collections/workshops/winter-solstice-restorative.mdoc +17 -0
  176. package/dist/templates/meadow/content/pages/about.json +344 -0
  177. package/dist/templates/meadow/content/pages/bodywork.json +330 -0
  178. package/dist/templates/meadow/content/pages/classes.json +820 -0
  179. package/dist/templates/meadow/content/pages/contact.json +240 -0
  180. package/dist/templates/meadow/content/pages/home.json +556 -0
  181. package/dist/templates/meadow/content/site.json +290 -0
  182. package/dist/templates/meadow/content/styles.json +25 -0
  183. package/dist/templates/meadow/media/LICENSES.json +130 -0
  184. package/dist/templates/meadow/openspace.json +21 -0
  185. package/dist/templates/meadow/template.json +116 -0
  186. package/dist/templates/signal/content/collections/changelog/2026-09-08-faster-sms.mdoc +11 -0
  187. package/dist/templates/signal/content/collections/changelog/2026-09-22-postmortem-drafts.mdoc +11 -0
  188. package/dist/templates/signal/content/collections/changelog/2026-10-06-quiet-hours.mdoc +13 -0
  189. package/dist/templates/signal/content/collections/changelog/2026-10-20-status-pages-that-update-themselves.mdoc +17 -0
  190. package/dist/templates/signal/content/collections/changelog/_collection.json +39 -0
  191. package/dist/templates/signal/content/pages/about.json +258 -0
  192. package/dist/templates/signal/content/pages/contact.json +122 -0
  193. package/dist/templates/signal/content/pages/home.json +1062 -0
  194. package/dist/templates/signal/content/pages/pricing.json +460 -0
  195. package/dist/templates/signal/content/site.json +345 -0
  196. package/dist/templates/signal/content/styles.json +17 -0
  197. package/dist/templates/signal/media/LICENSES.json +119 -0
  198. package/dist/templates/signal/openspace.json +21 -0
  199. package/dist/templates/signal/template.json +103 -0
  200. package/dist/templates/vow/content/pages/about.json +314 -0
  201. package/dist/templates/vow/content/pages/home.json +400 -0
  202. package/dist/templates/vow/content/pages/registry.json +207 -0
  203. package/dist/templates/vow/content/pages/rsvp.json +145 -0
  204. package/dist/templates/vow/content/pages/schedule.json +281 -0
  205. package/dist/templates/vow/content/pages/travel.json +326 -0
  206. package/dist/templates/vow/content/site.json +200 -0
  207. package/dist/templates/vow/content/styles.json +17 -0
  208. package/dist/templates/vow/media/LICENSES.json +130 -0
  209. package/dist/templates/vow/openspace.json +21 -0
  210. package/dist/templates/vow/template.json +113 -0
  211. package/package.json +71 -3
@@ -0,0 +1,121 @@
1
+ // Members-only and password pages → the Worker's private R2 bucket (generated; managed by
2
+ // site-kit updates). Runs in the publish workflow's `gated` job — the only job allowed to mint
3
+ // the run's GitHub OIDC identity token — with no packages installed: Node built-ins only.
4
+ //
5
+ // The committed page files are stubs (`contentRef`, no sections); the OpenSpace Hub keeps their
6
+ // content. This asks the Hub for exactly the versions this commit names and puts them at
7
+ // `pages/<pageId>/<contentRef>.json` through the Cloudflare API, before the Worker deploys.
8
+ import { readdir, readFile } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { pathToFileURL } from "node:url";
11
+
12
+ const CF_API = "https://api.cloudflare.com/client/v4";
13
+
14
+ async function pageFiles(dir) {
15
+ let entries = [];
16
+ try {
17
+ entries = await readdir(dir, { withFileTypes: true });
18
+ } catch {
19
+ return [];
20
+ }
21
+ const out = [];
22
+ for (const e of entries) {
23
+ const p = join(dir, e.name);
24
+ if (e.isDirectory()) out.push(...(await pageFiles(p)));
25
+ else if (e.name.endsWith(".json")) out.push(p);
26
+ }
27
+ return out;
28
+ }
29
+
30
+ /** The OS_GATED bucket name from wrangler.jsonc (line comments and trailing commas allowed). */
31
+ function bucketName(text) {
32
+ const json = text
33
+ .replace(/\/\*[\s\S]*?\*\//g, "")
34
+ .replace(/^\s*\/\/.*$/gm, "")
35
+ .replace(/,(\s*[}\]])/g, "$1");
36
+ return JSON.parse(json).r2_buckets?.find((b) => b.binding === "OS_GATED")?.bucket_name;
37
+ }
38
+
39
+ /** The Hub must be https (plain http only for a local Hub). */
40
+ function hubOrigin(raw) {
41
+ const url = new URL(String(raw));
42
+ const local = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
43
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && local))
44
+ throw new Error(`The Hub address must use https (got ${url.origin}).`);
45
+ return String(raw).replace(/\/+$/, "");
46
+ }
47
+
48
+ export async function main({
49
+ cwd = process.cwd(),
50
+ env = process.env,
51
+ fetch = globalThis.fetch,
52
+ log = console.log,
53
+ } = {}) {
54
+ const config = JSON.parse(await readFile(join(cwd, "openspace.json"), "utf8"));
55
+ const stubs = [];
56
+ for (const file of await pageFiles(join(cwd, "content/pages"))) {
57
+ const page = JSON.parse(await readFile(file, "utf8"));
58
+ if (typeof page.id === "string" && typeof page.contentRef === "string")
59
+ stubs.push({ id: page.id, ref: page.contentRef });
60
+ }
61
+ if (!stubs.length) {
62
+ log("No members-only or password pages to upload.");
63
+ return 0;
64
+ }
65
+ const hub = hubOrigin(config.hub);
66
+ const bucket = bucketName(await readFile(join(cwd, "wrangler.jsonc"), "utf8"));
67
+ if (!bucket) throw new Error('wrangler.jsonc has no "bucket_name" for the OS_GATED binding.');
68
+ const { CLOUDFLARE_API_TOKEN: cfToken, CLOUDFLARE_ACCOUNT_ID: account } = env;
69
+ if (!cfToken || !account)
70
+ throw new Error("CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are needed.");
71
+ if (!env.ACTIONS_ID_TOKEN_REQUEST_URL || !env.ACTIONS_ID_TOKEN_REQUEST_TOKEN)
72
+ throw new Error("This job needs `permissions: id-token: write`.");
73
+ const tokenUrl = new URL(env.ACTIONS_ID_TOKEN_REQUEST_URL);
74
+ tokenUrl.searchParams.set("audience", hub);
75
+ const idRes = await fetch(tokenUrl.toString(), {
76
+ headers: { authorization: `Bearer ${env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}` },
77
+ });
78
+ const idToken = idRes.ok ? (await idRes.json()).value : undefined;
79
+ if (!idToken) throw new Error(`GitHub didn't issue an identity token (${idRes.status}).`);
80
+ const res = await fetch(`${hub}/ci/sites/${encodeURIComponent(config.siteId)}/gated`, {
81
+ headers: { authorization: `Bearer ${idToken}`, accept: "application/json" },
82
+ });
83
+ const body = await res.json().catch(() => ({}));
84
+ if (!res.ok || !Array.isArray(body.pages))
85
+ throw new Error(
86
+ `The Hub didn't hand over the private pages (${res.status} ${body.error?.message ?? ""}).`,
87
+ );
88
+ const byKey = new Map(body.pages.map((p) => [`${p.id}/${p.ref}`, p]));
89
+ const missing = stubs.filter((s) => !byKey.has(`${s.id}/${s.ref}`));
90
+ if (missing.length)
91
+ throw new Error(`Missing private page content: ${missing.map((m) => m.id).join(", ")}.`);
92
+ const cf = (path, init = {}) =>
93
+ fetch(`${CF_API}/accounts/${account}${path}`, {
94
+ ...init,
95
+ headers: { authorization: `Bearer ${cfToken}`, ...init.headers },
96
+ });
97
+ // Create the bucket on the first run (an existing bucket answers 409).
98
+ const created = await cf("/r2/buckets", {
99
+ method: "POST",
100
+ headers: { "content-type": "application/json" },
101
+ body: JSON.stringify({ name: bucket }),
102
+ });
103
+ if (!created.ok && created.status !== 409)
104
+ throw new Error(`Creating ${bucket} failed (${created.status}).`);
105
+ for (const s of stubs) {
106
+ const put = await cf(`/r2/buckets/${bucket}/objects/pages/${s.id}/${s.ref}.json`, {
107
+ method: "PUT",
108
+ headers: { "content-type": "application/json" },
109
+ body: JSON.stringify(byKey.get(`${s.id}/${s.ref}`).page),
110
+ });
111
+ if (!put.ok) throw new Error(`Uploading ${s.id} failed (${put.status}).`);
112
+ }
113
+ log(`Uploaded ${stubs.length} private page(s) to ${bucket}.`);
114
+ return stubs.length;
115
+ }
116
+
117
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
118
+ main().catch((err) => {
119
+ console.error(`::error::${err.message}`);
120
+ process.exit(1);
121
+ });
@@ -0,0 +1,70 @@
1
+ # .github/workflows/publish.yml (generated; managed by site-kit updates) — Cloudflare target.
2
+ # Prerendered pages deploy as static assets of a Worker in the owner's Cloudflare account; the
3
+ # Worker serves members pages, sign-in and password pages. The D1 database is provisioned
4
+ # automatically on the first deploy. Members-only / password page content is never in this
5
+ # repository: the Hub keeps it, and this run copies the versions its commit names into the
6
+ # Worker's private R2 bucket (authorized by the run's GitHub OIDC identity, no stored token).
7
+ name: Publish
8
+ on:
9
+ push: { branches: [main] }
10
+ workflow_dispatch:
11
+ permissions: { contents: read }
12
+ concurrency: { group: publish, cancel-in-progress: true }
13
+ jobs:
14
+ # Members-only and password pages → the Worker's private R2 bucket (from the Hub). The only job
15
+ # that may mint the run's identity token, so it installs nothing: one Node script, built-ins only.
16
+ gated:
17
+ runs-on: ubuntu-latest
18
+ permissions: { contents: read, id-token: write }
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+ with: { persist-credentials: false }
22
+ - uses: actions/setup-node@v7
23
+ with: { node-version: 22 }
24
+ - run: node .github/openspace/gated-sync.mjs
25
+ env:
26
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
27
+ CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
28
+ publish:
29
+ needs: gated
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v7
33
+ - uses: actions/setup-node@v7
34
+ # package.json keys the npm cache: Hub-created sites have no lockfile at first.
35
+ with: { node-version: 22, cache: npm, cache-dependency-path: package.json }
36
+ - uses: actions/cache@v6
37
+ with:
38
+ path: node_modules/.astro
39
+ key: os-assets-${{ hashFiles('media/**') }}
40
+ restore-keys: os-assets-
41
+ # Sites created by the Hub start without a lockfile.
42
+ - run: if [ -f package-lock.json ]; then npm ci; else npm install; fi
43
+ - run: npx openspace check --ci # schema, links, budgets, secret scan
44
+ - run: npm run build # astro build (+ pagefind, og images, Worker)
45
+ env:
46
+ OPENSPACE_TURNSTILE_SITE_KEY: ${{ vars.OPENSPACE_TURNSTILE_SITE_KEY }}
47
+ # Self-host the font pairing (downloaded once from Google Fonts during the build).
48
+ OPENSPACE_GOOGLE_FONTS: download
49
+ OPENSPACE_REMOTE_IMAGES: download
50
+ # Worker secrets from this repository's Actions secrets (the Hub sets them): the member
51
+ # session key, page password hashes and, in sovereign mode, the owner's own Stripe keys.
52
+ # Before the deploy, so the new version starts with them: on the very first run
53
+ # `wrangler secret bulk` creates the Worker as a draft (non-interactive default), and
54
+ # `wrangler deploy` never deletes secrets. Re-running is idempotent.
55
+ - name: Worker secrets
56
+ env:
57
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
58
+ CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
59
+ OS_SESSION_SECRET: ${{ secrets.OS_SESSION_SECRET }}
60
+ OS_PAGE_PASSWORDS: ${{ secrets.OS_PAGE_PASSWORDS }}
61
+ STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
62
+ STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }}
63
+ run: |
64
+ node -e 'const o={};for(const k of ["OS_SESSION_SECRET","OS_PAGE_PASSWORDS","STRIPE_SECRET_KEY","STRIPE_WEBHOOK_SECRET"])if(process.env[k])o[k]=process.env[k];if(Object.keys(o).length)require("fs").writeFileSync(process.env.RUNNER_TEMP+"/os-secrets.json",JSON.stringify(o),{mode:0o600})'
65
+ if [ -f "$RUNNER_TEMP/os-secrets.json" ]; then npx wrangler secret bulk "$RUNNER_TEMP/os-secrets.json"; fi
66
+ rm -f "$RUNNER_TEMP/os-secrets.json"
67
+ - run: npx wrangler deploy
68
+ env:
69
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
70
+ CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
@@ -0,0 +1,11 @@
1
+ // OpenSpace site shell — Cloudflare target. Pages are prerendered as static assets; the Worker
2
+ // serves members pages, sign-in and password pages. Everything else comes from openspace.json
3
+ // and content/ via the site-kit integration.
4
+ import cloudflare from "@astrojs/cloudflare";
5
+ import openspace, { CLOUDFLARE_ADAPTER_OPTIONS } from "@omniharmonic/openspace-site-kit";
6
+ import { defineConfig } from "astro/config";
7
+
8
+ export default defineConfig({
9
+ adapter: cloudflare(CLOUDFLARE_ADAPTER_OPTIONS),
10
+ integrations: [openspace()],
11
+ });
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "openspace-site",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "astro dev",
7
+ "build": "astro build",
8
+ "preview": "astro preview",
9
+ "check": "openspace check"
10
+ },
11
+ "dependencies": {
12
+ "@astrojs/cloudflare": "^14.4.2",
13
+ "@omniharmonic/openspace-site-kit": "^0.1.0",
14
+ "astro": "^7.4.2"
15
+ },
16
+ "devDependencies": {
17
+ "@omniharmonic/openspace-cli": "^0.1.0",
18
+ "wrangler": "^4.152.0"
19
+ },
20
+ "engines": {
21
+ "node": ">=22.23.4"
22
+ }
23
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "./node_modules/wrangler/config-schema.json",
3
+ // Renamed to the site's repository name when Cloudflare hosting is connected.
4
+ "name": "openspace-site",
5
+ "compatibility_date": "2026-10-01",
6
+ "compatibility_flags": ["nodejs_compat"],
7
+ // Gated (members / password) page content — never stored in the public repository. The
8
+ // publish workflow (`openspace gated-sync`) creates the bucket and fills it from the Hub.
9
+ "r2_buckets": [{ "binding": "OS_GATED", "bucket_name": "openspace-site-gated" }],
10
+ // Stock and orders for the store (Phase 5).
11
+ "d1_databases": [{ "binding": "OS_DB" }],
12
+ "observability": { "enabled": true }
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "kind": "portfolio",
3
+ "label": "Projects",
4
+ "itemLabel": "Project",
5
+ "list": {
6
+ "layout": "grid",
7
+ "pageSize": 24,
8
+ "sort": "date-desc"
9
+ },
10
+ "itemPage": {
11
+ "layout": "gallery"
12
+ }
13
+ }
@@ -0,0 +1,32 @@
1
+ ---
2
+ status: published
3
+ title: Cordillera
4
+ date: 2026-06-01
5
+ year: 2026
6
+ client: Personal project
7
+ role: Photography
8
+ excerpt: Three winters above the treeline in the San Juan Mountains, waiting for the light to go flat.
9
+ image: https://images.unsplash.com/photo-1783611066016-59e16b0ac1c8?w=1600&q=80
10
+ imageAlt: Cordillera, cover photograph
11
+ gallery:
12
+ - src: https://images.unsplash.com/photo-1783611066016-59e16b0ac1c8?w=2400&q=80
13
+ alt: Snowy peaks reflected in a still mountain lake
14
+ width: 2400
15
+ height: 1600
16
+ - src: https://images.unsplash.com/photo-1501785888041-af3ef285b470?w=2400&q=80
17
+ alt: A rowing boat on a turquoise lake below jagged mountains
18
+ width: 2400
19
+ height: 1600
20
+ - src: https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=2400&q=80
21
+ alt: A misty river valley with pine trees and granite cliffs at dawn
22
+ width: 2400
23
+ height: 1602
24
+ - src: https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=2400&q=80
25
+ alt: Mist rolling over green cliffs at sunrise
26
+ width: 2400
27
+ height: 1430
28
+ ---
29
+
30
+ Three winters above the treeline in the San Juan Mountains, waiting for the light to go flat.
31
+
32
+ Shot on large-format film and printed in the studio. Prints from this series are available in editions of 25 and 10.
@@ -0,0 +1,32 @@
1
+ ---
2
+ status: published
3
+ title: Field Studies
4
+ date: 2024-06-01
5
+ year: 2024
6
+ client: Exhibition, Lowry Gallery
7
+ role: Photography
8
+ excerpt: Meadows, back roads and wildflowers from a summer spent driving the high plains.
9
+ image: https://images.unsplash.com/photo-1465146344425-f00d5f5c8f07?w=1600&q=80
10
+ imageAlt: Field Studies, cover photograph
11
+ gallery:
12
+ - src: https://images.unsplash.com/photo-1465146344425-f00d5f5c8f07?w=2400&q=80
13
+ alt: Wild red poppies scattered through a summer meadow
14
+ width: 2400
15
+ height: 1590
16
+ - src: https://images.unsplash.com/photo-1490750967868-88aa4486c946?w=2400&q=80
17
+ alt: Orange poppies against a clear blue sky
18
+ width: 2400
19
+ height: 1601
20
+ - src: https://images.unsplash.com/photo-1501854140801-50d01698950b?w=2400&q=80
21
+ alt: Rolling green hills seen from above, ridges catching the light
22
+ width: 2400
23
+ height: 1429
24
+ - src: https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?w=2400&q=80
25
+ alt: A desert road running straight toward red rock mountains
26
+ width: 2400
27
+ height: 3600
28
+ ---
29
+
30
+ Meadows, back roads and wildflowers from a summer spent driving the high plains.
31
+
32
+ Shot on large-format film and printed in the studio. Prints from this series are available in editions of 25 and 10.
@@ -0,0 +1,32 @@
1
+ ---
2
+ status: published
3
+ title: Salt & Glass
4
+ date: 2025-06-01
5
+ year: 2025
6
+ client: Commission, Tidewater Hotels
7
+ role: Photography
8
+ excerpt: The Pacific coast from above and at eye level, for a hotel group's new coastal lodges.
9
+ image: https://images.unsplash.com/photo-1505142468610-359e7d316be0?w=1600&q=80
10
+ imageAlt: Salt & Glass, cover photograph
11
+ gallery:
12
+ - src: https://images.unsplash.com/photo-1505142468610-359e7d316be0?w=2400&q=80
13
+ alt: An aerial view of turquoise surf meeting a sandy shore
14
+ width: 2400
15
+ height: 4272
16
+ - src: https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=2400&q=80
17
+ alt: Gentle waves washing onto an empty beach at sunset
18
+ width: 2400
19
+ height: 1595
20
+ - src: https://images.unsplash.com/photo-1518837695005-2083093ee35b?w=2400&q=80
21
+ alt: Deep blue ocean water under a pale sky
22
+ width: 2400
23
+ height: 1600
24
+ - src: https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=2400&q=80
25
+ alt: Mist rolling over green cliffs at sunrise
26
+ width: 2400
27
+ height: 1430
28
+ ---
29
+
30
+ The Pacific coast from above and at eye level, for a hotel group's new coastal lodges.
31
+
32
+ Shot on large-format film and printed in the studio. Prints from this series are available in editions of 25 and 10.
@@ -0,0 +1,32 @@
1
+ ---
2
+ status: published
3
+ title: White City
4
+ date: 2025-06-01
5
+ year: 2025
6
+ client: Monograph, Halden Press
7
+ role: Photography
8
+ excerpt: "Modernist facades in noon sun: a study of edges, shadows and the colour white."
9
+ image: https://images.unsplash.com/photo-1487958449943-2429e8be8625?w=1600&q=80
10
+ imageAlt: White City, cover photograph
11
+ gallery:
12
+ - src: https://images.unsplash.com/photo-1487958449943-2429e8be8625?w=2400&q=80
13
+ alt: A white angular building against a bright, clear sky
14
+ width: 2400
15
+ height: 1600
16
+ - src: https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=2400&q=80
17
+ alt: A city skyline stretching to the lake at golden hour
18
+ width: 2400
19
+ height: 1476
20
+ - src: https://images.unsplash.com/photo-1449824913935-59a10b8d2000?w=1920&q=80
21
+ alt: A downtown street lined with tall buildings and yellow cabs
22
+ width: 1920
23
+ height: 1280
24
+ - src: https://images.unsplash.com/photo-1512917774080-9991f1c4c750?w=2400&q=80
25
+ alt: A white modern villa with a pool framed by palms
26
+ width: 2400
27
+ height: 1600
28
+ ---
29
+
30
+ Modernist facades in noon sun: a study of edges, shadows and the colour white.
31
+
32
+ Shot on large-format film and printed in the studio. Prints from this series are available in editions of 25 and 10.
@@ -0,0 +1,180 @@
1
+ {
2
+ "$schema": "https://schema.openspace.dev/v1/page.json",
3
+ "id": "pg_0fq27dh81s",
4
+ "title": "About",
5
+ "slug": "about",
6
+ "seo": {
7
+ "description": "Elias Varga grew up in the Front Range and trained as an architect before turning to photography. Biography, press and clients."
8
+ },
9
+ "sections": [
10
+ {
11
+ "id": "sec_f0bnekkq85",
12
+ "preset": "about/story-split@1",
13
+ "label": "Story with portrait",
14
+ "layout": {
15
+ "mode": "stack",
16
+ "columns": {
17
+ "d": [
18
+ 5,
19
+ 7
20
+ ],
21
+ "m": [
22
+ 12
23
+ ]
24
+ },
25
+ "align": "center",
26
+ "gap": "2xl"
27
+ },
28
+ "style": {
29
+ "colorTheme": "light"
30
+ },
31
+ "blocks": [
32
+ {
33
+ "id": "blk_js8zwd5hff",
34
+ "type": "image",
35
+ "v": 1,
36
+ "fields": {
37
+ "image": {
38
+ "src": "https://images.unsplash.com/photo-1539571696357-5a69c17a67c6?w=1040&q=80",
39
+ "alt": "Portrait of the photographer Elias Varga",
40
+ "width": 1040,
41
+ "height": 1560
42
+ },
43
+ "aspect": "4:5",
44
+ "lightbox": false
45
+ },
46
+ "column": 0,
47
+ "variant": "default"
48
+ },
49
+ {
50
+ "id": "blk_qfk3h8z7ve",
51
+ "type": "heading",
52
+ "v": 1,
53
+ "fields": {
54
+ "text": "Elias Varga",
55
+ "level": "h1",
56
+ "size": "h1",
57
+ "eyebrow": "About"
58
+ },
59
+ "column": 1,
60
+ "variant": "plain"
61
+ },
62
+ {
63
+ "id": "blk_83r60tpwnc",
64
+ "type": "text",
65
+ "v": 1,
66
+ "fields": {
67
+ "content": {
68
+ "type": "doc",
69
+ "content": [
70
+ {
71
+ "type": "paragraph",
72
+ "content": [
73
+ {
74
+ "type": "text",
75
+ "text": "I grew up in Golden, Colorado, and spent my twenties drawing buildings for an architecture firm in Chicago. Photography started as a way to remember the places I was drawing. It became the work when I realised I cared more about how light moved across a wall than about the wall itself."
76
+ }
77
+ ]
78
+ },
79
+ {
80
+ "type": "paragraph",
81
+ "content": [
82
+ {
83
+ "type": "text",
84
+ "text": "Since 2016 I have photographed mountains, coastlines and modernist buildings for publishers, architects and a handful of hotels. I shoot slowly, mostly on a large-format camera, and print every edition by hand in my studio in RiNo."
85
+ }
86
+ ]
87
+ }
88
+ ]
89
+ }
90
+ },
91
+ "column": 1,
92
+ "variant": "default"
93
+ },
94
+ {
95
+ "id": "blk_g65wqxa9y4",
96
+ "type": "button",
97
+ "v": 1,
98
+ "fields": {
99
+ "label": "Get in touch",
100
+ "link": {
101
+ "type": "page",
102
+ "page": "pg_h72a8wvyfr"
103
+ },
104
+ "role": "secondary"
105
+ },
106
+ "column": 1,
107
+ "variant": "default"
108
+ }
109
+ ]
110
+ },
111
+ {
112
+ "id": "sec_st2tzbz12y",
113
+ "preset": "about/milestones@1",
114
+ "label": "Milestones timeline",
115
+ "layout": {
116
+ "mode": "stack",
117
+ "columns": {
118
+ "d": [
119
+ 5,
120
+ 7
121
+ ],
122
+ "m": [
123
+ 12
124
+ ]
125
+ },
126
+ "gap": "2xl"
127
+ },
128
+ "style": {
129
+ "colorTheme": "light-accent"
130
+ },
131
+ "blocks": [
132
+ {
133
+ "id": "blk_xhsve10pt1",
134
+ "type": "heading",
135
+ "v": 1,
136
+ "fields": {
137
+ "text": "Exhibitions & books",
138
+ "level": "h2",
139
+ "size": "auto",
140
+ "eyebrow": "Selected",
141
+ "subheading": "Solo shows, publications and awards since 2018."
142
+ },
143
+ "column": 0,
144
+ "variant": "plain"
145
+ },
146
+ {
147
+ "id": "blk_sjqb42c4j3",
148
+ "type": "timeline",
149
+ "v": 1,
150
+ "fields": {
151
+ "items": [
152
+ {
153
+ "date": "2026",
154
+ "title": "Cordillera",
155
+ "text": "Solo exhibition at the Lowry Gallery, Denver, with a limited-run catalogue."
156
+ },
157
+ {
158
+ "date": "2025",
159
+ "title": "White City",
160
+ "text": "Monograph published by Halden Press; shortlisted for the Field & Form book prize."
161
+ },
162
+ {
163
+ "date": "2023",
164
+ "title": "Coastlines",
165
+ "text": "Group show at the Kessler Foundation for Photography, Portland."
166
+ },
167
+ {
168
+ "date": "2018",
169
+ "title": "First commission",
170
+ "text": "Twelve modernist houses in Palm Springs for The Modern House Review."
171
+ }
172
+ ]
173
+ },
174
+ "column": 1,
175
+ "variant": "vertical"
176
+ }
177
+ ]
178
+ }
179
+ ]
180
+ }