Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

build-agent-spec.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

build-agent-spec.tsxBlame619 lines · 1 contributor
4a80519Claude1/**
2 * /docs/build-agent-integration — public spec page for AI build agents
3 * integrating with Gluecron (Holden Mercer, Cursor, Claude Code, etc.).
4 *
5 * Renders a solid-white copyable spec block (high contrast against the
6 * dark theme) telling the integrating vendor exactly what to change on
7 * their side: detect Gluecron, swap base URL, swap auth token prefix,
8 * call our REST v2 endpoints. Mirrors GitHub REST v3 by design so most
9 * existing code reuses with a base-URL swap.
10 *
11 * Scoped CSS under `.ba-spec-*`. Public — no auth required.
12 */
13
14import { Hono } from "hono";
15import { Layout } from "../views/layout";
16import { softAuth } from "../middleware/auth";
17import type { AuthEnv } from "../middleware/auth";
18
19const buildAgentSpec = new Hono<AuthEnv>();
20buildAgentSpec.use("*", softAuth);
21
22const styles = `
eed4684Claude23 .ba-spec-wrap { max-width: 1320px; margin: 0 auto; padding: var(--space-6) var(--space-4); }
4a80519Claude24
25 .ba-spec-hero {
26 position: relative;
27 margin-bottom: var(--space-5);
28 padding: var(--space-5) var(--space-6);
29 background: var(--bg-elevated);
30 border: 1px solid var(--border);
31 border-radius: 16px;
32 overflow: hidden;
33 }
34 .ba-spec-hero::before {
35 content: '';
36 position: absolute;
37 top: 0; left: 0; right: 0;
38 height: 2px;
39 background: linear-gradient(90deg, transparent 0%, #8c6dff 30%, #36c5d6 70%, transparent 100%);
40 opacity: 0.7;
41 pointer-events: none;
42 }
43 .ba-spec-hero-orb {
44 position: absolute;
45 inset: -20% -10% auto auto;
46 width: 420px; height: 420px;
47 background: radial-gradient(circle, rgba(140,109,255,0.20), rgba(54,197,214,0.10) 45%, transparent 70%);
48 filter: blur(80px);
49 opacity: 0.7;
50 pointer-events: none;
51 z-index: 0;
52 }
53 .ba-spec-hero-inner { position: relative; z-index: 1; max-width: 760px; }
54 .ba-spec-eyebrow {
55 font-size: 12px;
56 color: var(--text-muted);
57 margin-bottom: var(--space-2);
58 letter-spacing: 0.02em;
59 display: inline-flex;
60 align-items: center;
61 gap: 8px;
62 }
63 .ba-spec-eyebrow .pill {
64 display: inline-flex;
65 align-items: center;
66 justify-content: center;
67 width: 18px; height: 18px;
68 border-radius: 6px;
69 background: rgba(140,109,255,0.14);
70 color: #b69dff;
71 box-shadow: inset 0 0 0 1px rgba(140,109,255,0.35);
72 }
73 .ba-spec-title {
74 font-size: clamp(28px, 4vw, 44px);
75 font-family: var(--font-display);
76 font-weight: 800;
77 letter-spacing: -0.028em;
78 line-height: 1.05;
79 margin: 0 0 var(--space-2);
80 color: var(--text-strong);
81 }
82 .ba-spec-title-grad {
83 background-image: linear-gradient(135deg, #a48bff 0%, #8c6dff 50%, #36c5d6 100%);
84 -webkit-background-clip: text;
85 background-clip: text;
86 -webkit-text-fill-color: transparent;
87 color: transparent;
88 }
89 .ba-spec-sub {
90 font-size: 15px;
91 color: var(--text-muted);
92 margin: 0;
93 line-height: 1.5;
94 max-width: 680px;
95 }
96
97 /* Solid white spec block — high contrast against dark theme. */
98 .ba-spec-block {
99 margin-bottom: var(--space-5);
100 background: #ffffff;
101 color: #0a0a0a;
102 border: 1px solid #e5e7eb;
103 border-radius: 14px;
104 overflow: hidden;
105 box-shadow: 0 1px 0 rgba(255,255,255,0.04), 0 8px 32px rgba(0,0,0,0.18);
106 }
107 .ba-spec-head {
108 display: flex;
109 align-items: center;
110 justify-content: space-between;
111 gap: 12px;
112 padding: 12px 16px;
113 background: #f9fafb;
114 border-bottom: 1px solid #e5e7eb;
115 flex-wrap: wrap;
116 }
117 .ba-spec-title-bar {
118 display: flex;
119 align-items: center;
120 gap: 10px;
121 font-family: var(--font-display, system-ui, sans-serif);
122 font-size: 14px;
123 font-weight: 700;
124 color: #111827;
125 letter-spacing: -0.005em;
126 margin: 0;
127 }
128 .ba-spec-dot {
129 width: 8px; height: 8px;
130 border-radius: 9999px;
131 background: linear-gradient(135deg, #8c6dff, #36c5d6);
132 box-shadow: 0 0 0 3px rgba(140,109,255,0.18);
133 }
134 .ba-spec-copy {
135 display: inline-flex;
136 align-items: center;
137 gap: 6px;
138 padding: 6px 12px;
139 font-size: 12.5px;
140 font-weight: 600;
141 color: #111827;
142 background: #ffffff;
143 border: 1px solid #d1d5db;
144 border-radius: 8px;
145 cursor: pointer;
146 font-family: inherit;
147 transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
148 }
149 .ba-spec-copy:hover {
150 background: #f3f4f6;
151 border-color: #9ca3af;
152 }
153 .ba-spec-copy.is-copied {
154 background: #ecfdf5;
155 border-color: #6ee7b7;
156 color: #047857;
157 }
158 .ba-spec-copy svg { display: block; }
159 .ba-spec-pre {
160 margin: 0;
161 padding: 22px 24px;
162 font-family: var(--font-mono, ui-monospace, "SF Mono", Menlo, monospace);
163 font-size: 12.5px;
164 line-height: 1.7;
165 color: #0a0a0a;
166 background: #ffffff;
167 white-space: pre;
168 overflow-x: auto;
169 tab-size: 2;
170 max-height: 70vh;
171 overflow-y: auto;
172 }
173 .ba-spec-foot {
174 padding: 10px 16px;
175 border-top: 1px solid #e5e7eb;
176 background: #f9fafb;
177 font-size: 12px;
178 color: #6b7280;
179 }
180 .ba-spec-foot code {
181 background: #eef2ff;
182 color: #4338ca;
183 padding: 1px 6px;
184 border-radius: 4px;
185 font-size: 11.5px;
186 }
187
188 .ba-spec-callout {
189 margin-top: var(--space-5);
190 padding: var(--space-4);
191 text-align: center;
192 color: var(--text-muted);
193 font-size: 13px;
194 border: 1px dashed var(--border);
195 border-radius: 12px;
196 }
197 .ba-spec-callout a {
198 color: var(--accent);
199 text-decoration: none;
200 font-weight: 600;
201 }
202 .ba-spec-callout a:hover { text-decoration: underline; }
203`;
204
205const SPEC_TEXT = `═══════════════════════════════════════════════════════════════════════════════
206HOLDEN MERCER × GLUECRON INTEGRATION SPEC
207What HM needs to change on its side to support Gluecron-hosted repos
208Gluecron API v1.1 — all endpoints below are LIVE (rolled out 2026-05-20)
209═══════════════════════════════════════════════════════════════════════════════
210
211TL;DR
212─────────────────────────────────────────────────────────────────────────────
213Your earlier read of Gluecron's API was based on v1, which lacked
214/contents, /git/*, and /actions/workflows/:filename/dispatches. Those
215gaps are now closed. Shapes match GitHub REST v3 exactly — a base-URL
216swap reuses most of your existing GitHub integration. The "clone +
217push" workaround you architected still works (path A below); the
218native dispatch flow (path B) is now also available if you want it.
219
220Required changes on HM's side
221─────────────────────────────────────────────────────────────────────────────
222
2231. ADD GLUECRON AS A DESTINATION
224 Surface Gluecron alongside GitHub in your repo picker. Detect with:
225
226 hostname === "gluecron.com"
227 || process.env.GLUECRON_HOST // self-hosted instances
228
2292. SWAP THE BASE URL
230 GitHub: https://api.github.com
231 Gluecron: https://gluecron.com/api/v2
232 (or https://<GLUECRON_HOST>/api/v2 for self-hosted)
233
2343. SWAP THE AUTH TOKEN PREFIX — header name stays
235 GitHub: Authorization: Bearer ghp_…
236 Gluecron: Authorization: Bearer glc_… (PAT, scope=repo)
237 Or OAuth: Bearer glct_… (admin scope NOT issuable)
238 Or App install:Bearer ghi_…
239
2404. CHANGE NOTHING ELSE IN YOUR REQUEST/RESPONSE PARSING
241 - Same field names (snake_case)
242 - Same status codes
243 - Same base64 content encoding
244 - Same git-objects API surface
245
246Endpoints HM will call
247─────────────────────────────────────────────────────────────────────────────
248
249READ FILES
250 GET /api/v2/repos/:owner/:repo/contents/:path?ref=<branch_or_sha>
251 → { type, name, path, size, sha, encoding:"base64", content,
252 html_url, download_url }
253
254ATOMIC MULTI-FILE WRITE (preferred for agent turns)
255 POST /api/v2/repos/:owner/:repo/git/blobs
256 body: { content, encoding: "utf-8" | "base64" }
257 → { sha, url, size }
258
259 POST /api/v2/repos/:owner/:repo/git/trees
260 body: { base_tree?, tree: [{ path, mode:"100644",
261 type:"blob", sha|null }] }
262 → { sha, url, tree[] }
263 (sha:null in a tree entry = delete that path from base_tree)
264
265 POST /api/v2/repos/:owner/:repo/git/commits
266 body: { message, tree, parents: [<head_sha>] }
267 → { sha, tree, message, parents, author, html_url }
268
269 GET /api/v2/repos/:owner/:repo/git/refs/heads/:branch
270 → { ref, object: { sha, type:"commit" } }
271
272 PATCH /api/v2/repos/:owner/:repo/git/refs/heads/:branch
273 body: { sha: <new_commit>, force?: false }
274 → { ref, object } (422 if not fast-forward and force=false)
275
276OPEN A PULL REQUEST
277 POST /api/v2/repos/:owner/:repo/pulls
278 body: { title, body, base, head }
279 → { id, number, title, state, html_url, ... }
280
281DISPATCH A NATIVE BUILD ON GLUECRON ACTIONS (optional — path B)
282 POST /api/v2/repos/:owner/:repo/actions/workflows/:filename/dispatches
283 body: { ref:"main", inputs?: { prompt, model, … } }
284 → 204 No Content
285
286 GET /api/v2/repos/:owner/:repo/actions/workflows/:filename/runs
287 ?branch=&head_sha=&per_page=30&page=1
288 → { total_count, workflow_runs:[...] }
289
290 GET /api/v2/repos/:owner/:repo/actions/runs/:run_id
291 → { id, name, head_branch, head_sha, status, conclusion,
292 event, created_at, updated_at, run_started_at, html_url }
293
294 GET /api/v2/repos/:owner/:repo/actions/runs/:run_id/logs
295 → application/zip (one .log file per job)
296
297 POST /api/v2/repos/:owner/:repo/actions/runs/:run_id/cancel
298 → 202 Accepted (409 if already terminal)
299
300User-side workflow file (one-time setup per repo, for path B)
301─────────────────────────────────────────────────────────────────────────────
302
303If HM dispatches via path B, the user's repo needs a YAML committed to
304.gluecron/workflows/holden-mercer.yml. Recommend this template:
305
306 name: Holden Mercer build agent
307 on:
308 workflow_dispatch:
309 inputs:
310 prompt:
311 description: What the agent should build / fix
312 required: true
313 type: string
314 model:
315 description: Anthropic model to use
316 required: false
317 default: claude-haiku-4-5
318 type: string
319 jobs:
320 agent:
321 runs-on: ubuntu-latest
322 steps:
323 - uses: actions/checkout@v4
324 - name: Run HM agent
325 env:
326 ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
327 HM_PROMPT: \${{ inputs.prompt }}
328 HM_MODEL: \${{ inputs.model }}
329 run: npx @holden-mercer/agent run
330
331Two integration paths — pick one (or offer both in HM's UI)
332─────────────────────────────────────────────────────────────────────────────
333
334PATH A — Your-backend mode (matches your original v1 workaround)
335 1. Clone the repo using a credential helper (NOT URL-embedded token):
336 git -c credential.helper=… clone https://gluecron.com/:owner/:repo.git
337 2. Run the tool-use loop on HM's backend against local files
338 3. Push the result via the git-objects API (one atomic commit):
339 POST /git/blobs → /git/trees → /git/commits → PATCH /git/refs/heads
340 4. POST /pulls to open the PR
341 Pros: full control, you own compute. Cons: you bear all build minutes.
342
343PATH B — Gluecron-native mode (uses our runner)
344 1. POST /actions/workflows/holden-mercer.yml/dispatches with inputs
345 2. Your agent code runs inside Gluecron Actions on the user's repo
346 3. The runner pushes commits + opens the PR itself
347 Pros: zero infra on your side, lower egress. Cons: Gluecron concurrency
348 + timeout limits apply per tier.
349
350Both paths supported. Most vendors ship A first, add B later as an opt-in.
351
352Webhook callback (optional but recommended)
353─────────────────────────────────────────────────────────────────────────────
354Subscribe to pull_request + workflow_run events at
355 /:owner/:repo/settings/webhooks
356HMAC signature header: X-Gluecron-Signature: sha256=<hex>
357Delivery: at-least-once with exponential backoff
358 (30s, 2m, 10m, 1h, 6h — max 6 attempts then
359 dead-letter)
360Dedupe with: X-Gluecron-Delivery: <uuid>
361
362Compatibility checklist (matches GitHub REST v3 by design)
363─────────────────────────────────────────────────────────────────────────────
364[x] Same endpoint shapes
365[x] Same field names (snake_case)
366[x] Same base64 content encoding
367[x] Same status codes
368[x] Same git plumbing API (blobs / trees / commits / refs)
369[x] Same workflow_dispatch shape
370[x] HMAC-SHA256 webhook signatures
371[x] PKCE OAuth (RFC 7636)
372[x] Pagination via ?limit, ?offset (default 30, max 100)
373
374Differences to flag in HM's client code
375─────────────────────────────────────────────────────────────────────────────
376- Token prefix: glc_ (PAT) / glct_ (OAuth) / ghi_ (app install)
377- Pagination: ?limit not ?per_page (recommend limit)
378- OAuth scopes: namespaced — read:repo / write:repo (admin NOT issuable)
379- Webhook header: X-Gluecron-Signature not X-Hub-Signature-256
380
381Estimated work on HM's side
382─────────────────────────────────────────────────────────────────────────────
383- UI change (add Gluecron destination) ~2-4 hrs
384- API client base-URL/token swap ~2-3 hrs
385- Webhook handler for X-Gluecron-Signature ~1 hr
386- Path B dispatch integration (optional) ~4-6 hrs
387TOTAL minimum (paths A only): ~5-8 hrs
388TOTAL with path B as well: ~10-15 hrs
389
390Support
391─────────────────────────────────────────────────────────────────────────────
392Live spec: https://gluecron.com/docs/build-agent-integration (this page)
393Status: https://gluecron.com/admin/health
394Issues: https://gluecron.com/ccantynz/Gluecron.com/issues
395Contact: ccantynz on gluecron.com
396═══════════════════════════════════════════════════════════════════════════════`;
397
0feb720Claude398const MCP_TOOLS_TEXT = `═══════════════════════════════════════════════════════════════════════════════
399GLUECRON MCP TOOL SURFACE (50+ tools, model-context-protocol/2025-06-18)
400═══════════════════════════════════════════════════════════════════════════════
401
402Transport: POST /mcp — JSON-RPC 2.0 (single or batch)
403 GET /mcp — discovery handshake
404Auth: same as REST v2 — Bearer glc_… (PAT) or session cookie
405 agent tokens (agt_…) routed via agent-multiplayer
406
407Call shape:
408 {
409 "jsonrpc": "2.0", "id": 1,
410 "method": "tools/call",
411 "params": { "name": "<tool_name>", "arguments": { … } }
412 }
413
414─── READ TOOLS (public, no auth) ────────────────────────────────────────────
415gluecron_repo_search Search public repos by keyword
416gluecron_repo_read_file Read a file at a ref
417gluecron_repo_list_issues List open issues on a repo
418gluecron_repo_explain_codebase Cached AI 'explain this codebase' markdown
419gluecron_repo_health Health score + breakdown for a repo
420
421─── REPOS (write — requires 'repo' or 'admin') ──────────────────────────────
422gluecron_fork_repo Fork a repo to the caller's namespace
423gluecron_delete_repo Permanently delete a repo (admin)
424gluecron_update_repo Description / visibility / default branch
425gluecron_search_repos Full search (sort, limit, filters)
426gluecron_clone_url Authed HTTPS clone URL + helper hint
427
428─── ISSUES (write — requires 'repo') ────────────────────────────────────────
429gluecron_create_issue Open a new issue
430gluecron_comment_issue Add a comment
431gluecron_close_issue Close an open issue (idempotent)
432gluecron_reopen_issue Reopen a closed issue
433gluecron_label_issue Attach labels (auto-creates missing)
434gluecron_unlabel_issue Detach a single label
435gluecron_assign_issue Assign to a user (via assignee:* label)
436gluecron_search_issues Title/body keyword search per repo
437
438─── PULL REQUESTS (write — requires 'repo') ─────────────────────────────────
439gluecron_create_pr Open a PR
440gluecron_open_draft_pr Open a draft PR
441gluecron_get_pr Fetch full PR record
442gluecron_list_prs List PRs by state
443gluecron_search_prs Keyword search on title/body
444gluecron_comment_pr Add a PR comment
445gluecron_request_changes Post an AI-review 'changes requested' comment
446gluecron_merge_pr Merge a PR (enforces every gate + risk score)
447gluecron_close_pr Close without merging (idempotent)
448gluecron_generate_pr_description AI commit-message-style description
449
450─── FILES & GIT PLUMBING (write — requires 'repo') ──────────────────────────
451gluecron_read_file GET /contents wrapper
452gluecron_write_file PUT /contents wrapper (utf8 or base64)
453gluecron_delete_file DELETE /contents wrapper
454gluecron_list_tree GET /tree wrapper (recursive supported)
455gluecron_get_commit GET /commits/:sha wrapper
456gluecron_create_branch Create a new branch ref at a sha
457gluecron_atomic_multi_file_commit blob/tree/commit/ref-update in one call
458 — the killer agent tool
459
460─── AI WORKFLOWS (Gluecron-native — requires 'repo') ────────────────────────
461gluecron_ship_spec Drop .gluecron/specs/*.md status:ready
462gluecron_voice_to_pr Transcript → spec or issue (auto-classified)
463gluecron_refactor_across_repos Plan + execute a multi-repo refactor
464gluecron_explain_repo Cached 'explain this repo' markdown
465gluecron_chat_with_repo Start a chat + send the first message
466gluecron_chat_continue Send another message in a chat
467gluecron_generate_tests AI tests for a PR (follow-up-pr / append)
468gluecron_generate_commit_message Conventional / plain commit message for a diff
469gluecron_generate_release_notes Section-bucketed notes between two tags
470gluecron_propose_migration Dep-upgrade PR via migration-assistant
471gluecron_propose_doc_update Doc-drift scan + PR opens
472
473─── CI / DEPLOYS (write — requires 'repo') ──────────────────────────────────
474gluecron_trigger_workflow workflow_dispatch
475gluecron_get_workflow_run Run status + metadata
476gluecron_get_workflow_logs Per-job log payload (JSON; ZIP via REST)
477gluecron_cancel_workflow_run Cancel queued/running run
478gluecron_get_preview_url Branch-preview URL + status
479gluecron_provision_pr_sandbox Re-provision a PR sandbox
480
481─── AGENTS (multiplayer surface — admin to mint, repo to lease) ─────────────
482gluecron_create_agent_session Mint agent token (returned ONCE) (admin)
483gluecron_acquire_lease Grab an exclusive target lease
484gluecron_release_lease Release a held lease
485gluecron_get_agent_budget spent / cap / remaining cents
486
487─── SEMANTIC ────────────────────────────────────────────────────────────────
488gluecron_semantic_search Vector-index query (Voyage or hash)
489gluecron_find_symbol findDefinitions wrapper
490
491─── INSIGHTS ────────────────────────────────────────────────────────────────
492gluecron_pr_status_summary State + risk + trio verdict for a PR
493gluecron_repo_health (also under READ; included here for parity)
494gluecron_ai_cost_summary Spend rollup (user / repo / agent)
495
496═══════════════════════════════════════════════════════════════════════════════`;
497
4a80519Claude498buildAgentSpec.get("/docs/build-agent-integration", (c) => {
499 const user = c.get("user");
500 return c.html(
501 <Layout title="Build agent integration spec — Gluecron" user={user}>
502 <div class="ba-spec-wrap">
503 <section class="ba-spec-hero">
504 <div class="ba-spec-hero-orb" aria-hidden="true" />
505 <div class="ba-spec-hero-inner">
506 <div class="ba-spec-eyebrow">
507 <span class="pill" aria-hidden="true">
508 <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
509 <polyline points="16 18 22 12 16 6" />
510 <polyline points="8 6 2 12 8 18" />
511 </svg>
512 </span>
513 Integration spec · For AI build-agent vendors (Holden Mercer, Cursor, Claude Code)
514 </div>
515 <h1 class="ba-spec-title">
516 <span class="ba-spec-title-grad">Wire your agent into Gluecron.</span>
517 </h1>
518 <p class="ba-spec-sub">
519 Drop-in compatible with GitHub REST v3. One base-URL swap reuses your existing
520 GitHub integration. Copy the block below and paste it to your engineering team —
521 everything they need to ship is in there.
522 </p>
523 </div>
524 </section>
525
526 <section class="ba-spec-block" aria-labelledby="ba-spec-title">
527 <header class="ba-spec-head">
528 <div>
529 <p class="ba-spec-title-bar" id="ba-spec-title">
530 <span class="ba-spec-dot" aria-hidden="true" />
531 HM × Gluecron integration spec
532 </p>
533 </div>
534 <button
535 type="button"
536 class="ba-spec-copy"
537 data-spec-copy
538 aria-label="Copy spec to clipboard"
539 >
540 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
541 <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
542 <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
543 </svg>
544 <span data-spec-copy-label>Copy spec</span>
545 </button>
546 </header>
547 <pre class="ba-spec-pre" data-spec-text>{SPEC_TEXT}</pre>
548 <div class="ba-spec-foot">
549 Share this page directly: <code>https://gluecron.com/docs/build-agent-integration</code>
550 </div>
551 </section>
552
0feb720Claude553 {/* ─── MCP tools surface ─────────────────────────────────────── */}
554 <section class="ba-spec-block" aria-labelledby="ba-mcp-title">
555 <header class="ba-spec-head">
556 <div>
557 <p class="ba-spec-title-bar" id="ba-mcp-title">
558 <span class="ba-spec-dot" aria-hidden="true" />
559 MCP tools — every Gluecron action callable from any AI agent
560 </p>
561 </div>
562 </header>
563 <pre class="ba-spec-pre">{MCP_TOOLS_TEXT}</pre>
564 <div class="ba-spec-foot">
565 JSON-RPC 2.0 endpoint: <code>POST https://gluecron.com/mcp</code> —
566 send <code>{"{ method: \"tools/list\" }"}</code> to enumerate at runtime.
567 </div>
568 </section>
569
4a80519Claude570 <div class="ba-spec-callout">
571 Looking for the full Gluecron platform spec? See{" "}
572 <a href="/help">/help</a> and{" "}
573 <a href="/admin/integrations">/admin/integrations</a> for env-var setup.
574 </div>
575 </div>
576 <style dangerouslySetInnerHTML={{ __html: styles }} />
577 <script
578 dangerouslySetInnerHTML={{
579 __html: `
580 (function(){
581 var btn = document.querySelector('[data-spec-copy]');
582 var pre = document.querySelector('[data-spec-text]');
583 var label = document.querySelector('[data-spec-copy-label]');
584 if (!btn || !pre || !label) return;
585 btn.addEventListener('click', function(){
586 var text = pre.textContent || '';
587 var done = function(){
588 btn.classList.add('is-copied');
589 label.textContent = 'Copied';
590 setTimeout(function(){
591 btn.classList.remove('is-copied');
592 label.textContent = 'Copy spec';
593 }, 1800);
594 };
595 if (navigator.clipboard && navigator.clipboard.writeText) {
596 navigator.clipboard.writeText(text).then(done).catch(function(){
597 var ta = document.createElement('textarea');
598 ta.value = text; ta.style.position='fixed'; ta.style.left='-9999px';
599 document.body.appendChild(ta); ta.select();
600 try { document.execCommand('copy'); done(); } catch(e){}
601 document.body.removeChild(ta);
602 });
603 } else {
604 var ta = document.createElement('textarea');
605 ta.value = text; ta.style.position='fixed'; ta.style.left='-9999px';
606 document.body.appendChild(ta); ta.select();
607 try { document.execCommand('copy'); done(); } catch(e){}
608 document.body.removeChild(ta);
609 }
610 });
611 })();
612 `,
613 }}
614 />
615 </Layout>
616 );
617});
618
619export default buildAgentSpec;