@coreplane/switchboard 0.0.0 → 1.18.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/LICENSE +201 -0
- package/README.md +18 -1
- package/dist/assets/.dockerignore +27 -0
- package/dist/assets/.env.example +33 -0
- package/dist/assets/Dockerfile +111 -0
- package/dist/assets/config/config.example.yaml +359 -0
- package/dist/assets/deploy/bin/build-stamp.d.mts +15 -0
- package/dist/assets/deploy/bin/build-stamp.mjs +98 -0
- package/dist/assets/deploy/bin/cf-logs +32 -0
- package/dist/assets/deploy/cloudflare/package.json +29 -0
- package/dist/assets/deploy/cloudflare/preflight.mjs +243 -0
- package/dist/assets/deploy/cloudflare/tsconfig.json +18 -0
- package/dist/assets/deploy/cloudflare/worker.ts +382 -0
- package/dist/assets/deploy/cloudflare/wrangler.template.jsonc +67 -0
- package/dist/assets/deploy/cloudflare/write-build.d.mts +7 -0
- package/dist/assets/deploy/cloudflare/write-build.mjs +53 -0
- package/dist/assets/deploy/cloudflare-docs/package.json +18 -0
- package/dist/assets/deploy/cloudflare-docs/wrangler.template.jsonc +30 -0
- package/dist/assets/deploy/cloudflare-memory/package.json +25 -0
- package/dist/assets/deploy/cloudflare-memory/tsconfig.json +17 -0
- package/dist/assets/deploy/cloudflare-memory/worker.ts +2635 -0
- package/dist/assets/deploy/cloudflare-memory/wrangler.template.jsonc +50 -0
- package/dist/assets/deploy/cloudflare-resident/Dockerfile +91 -0
- package/dist/assets/deploy/cloudflare-resident/gc.ts +287 -0
- package/dist/assets/deploy/cloudflare-resident/node-async-hooks.d.ts +11 -0
- package/dist/assets/deploy/cloudflare-resident/package.json +29 -0
- package/dist/assets/deploy/cloudflare-resident/preflight.mjs +224 -0
- package/dist/assets/deploy/cloudflare-resident/tsconfig.json +19 -0
- package/dist/assets/deploy/cloudflare-resident/worker.ts +6637 -0
- package/dist/assets/deploy/cloudflare-resident/wrangler.template.jsonc +120 -0
- package/dist/assets/deploy/cloudflare-sandbox/Dockerfile +67 -0
- package/dist/assets/deploy/cloudflare-sandbox/docker-wrapper.sh +37 -0
- package/dist/assets/deploy/cloudflare-sandbox/package.json +26 -0
- package/dist/assets/deploy/cloudflare-sandbox/tsconfig.json +20 -0
- package/dist/assets/deploy/cloudflare-sandbox/worker.ts +410 -0
- package/dist/assets/deploy/cloudflare-sandbox/wrangler.template.jsonc +67 -0
- package/dist/assets/deploy/profile.example.json +13 -0
- package/dist/assets/deploy/secrets.manifest.json +108 -0
- package/dist/assets/docker-entrypoint.sh +15 -0
- package/dist/assets/package-lock.json +18407 -0
- package/dist/assets/package.json +104 -0
- package/dist/assets/project.json +219 -0
- package/dist/assets/source.json +5 -0
- package/dist/assets/src/core/authz/actor.ts +100 -0
- package/dist/assets/src/core/authz/authorize.ts +169 -0
- package/dist/assets/src/core/authz/grants.ts +347 -0
- package/dist/assets/src/core/authz/policy.ts +281 -0
- package/dist/assets/src/core/authz/resource.ts +147 -0
- package/dist/assets/src/core/authz/types.ts +164 -0
- package/dist/assets/src/core/drain.ts +54 -0
- package/dist/assets/src/core/ingressTokens.ts +64 -0
- package/dist/assets/src/core/memory/engine.ts +115 -0
- package/dist/assets/src/core/memory/scorer.ts +147 -0
- package/dist/assets/src/core/memory/types.ts +120 -0
- package/dist/assets/src/core/normalizeSpans.ts +299 -0
- package/dist/assets/src/core/prDescriptionTypes.ts +54 -0
- package/dist/assets/src/core/redact.ts +113 -0
- package/dist/assets/src/core/runEvents.ts +537 -0
- package/dist/assets/src/core/runFriction.ts +665 -0
- package/dist/assets/src/core/runLedger/decisions.ts +126 -0
- package/dist/assets/src/core/runLedger/types.ts +177 -0
- package/dist/assets/src/core/runRecord.ts +627 -0
- package/dist/assets/src/core/runShape.ts +61 -0
- package/dist/assets/src/core/schedules.ts +452 -0
- package/dist/assets/src/core/time/formatDuration.ts +61 -0
- package/dist/assets/src/core/trace/attrs.ts +203 -0
- package/dist/assets/src/core/trace/classify.ts +49 -0
- package/dist/assets/src/core/trace/clock.ts +6 -0
- package/dist/assets/src/core/trace/context.ts +9 -0
- package/dist/assets/src/core/trace/ids.ts +23 -0
- package/dist/assets/src/core/trace/partition.ts +235 -0
- package/dist/assets/src/core/trace/sinks.ts +68 -0
- package/dist/assets/src/core/trace/streamSpans.ts +163 -0
- package/dist/assets/src/core/trace/traceparent.ts +29 -0
- package/dist/assets/src/core/trace/tracer.ts +247 -0
- package/dist/assets/src/core/trace/types.ts +125 -0
- package/dist/assets/src/core/trace/workerTrace.ts +97 -0
- package/dist/assets/src/deploy/buildStamp.ts +93 -0
- package/dist/assets/src/deploy/liveGate.ts +203 -0
- package/dist/assets/src/deploy/profile.ts +162 -0
- package/dist/assets/src/deploy/restart.ts +393 -0
- package/dist/assets/src/effort.ts +17 -0
- package/dist/assets/src/execution/bashTimeout.ts +78 -0
- package/dist/assets/src/execution/bindingPurge.ts +43 -0
- package/dist/assets/src/execution/residentBackupTransfer.ts +50 -0
- package/dist/assets/src/execution/residentCleanliness.ts +95 -0
- package/dist/assets/src/execution/residentCredentials.ts +81 -0
- package/dist/assets/src/execution/residentDepCache.ts +321 -0
- package/dist/assets/src/execution/residentDepsStore.ts +326 -0
- package/dist/assets/src/execution/residentDetach.ts +48 -0
- package/dist/assets/src/execution/residentDisk.ts +107 -0
- package/dist/assets/src/execution/residentDiskBudget.ts +448 -0
- package/dist/assets/src/execution/residentExecWrap.ts +100 -0
- package/dist/assets/src/execution/residentHead.ts +85 -0
- package/dist/assets/src/execution/residentReadonly.ts +72 -0
- package/dist/assets/src/execution/residentRefresh.ts +429 -0
- package/dist/assets/src/execution/residentRestoreExtract.ts +130 -0
- package/dist/assets/src/execution/residentState.ts +47 -0
- package/dist/assets/src/execution/residentStepReport.ts +98 -0
- package/dist/assets/src/execution/residentStepTrace.ts +97 -0
- package/dist/assets/src/execution/residentSteps.ts +99 -0
- package/dist/assets/src/execution/residentText.ts +83 -0
- package/dist/assets/src/execution/residentTrace.ts +119 -0
- package/dist/assets/src/execution/sandboxEnv.ts +42 -0
- package/dist/assets/src/execution/sandboxErrors.ts +159 -0
- package/dist/assets/src/execution/sandboxKeepalive.ts +118 -0
- package/dist/assets/src/execution/shellQuote.ts +8 -0
- package/dist/assets/src/mcp/registry.ts +242 -0
- package/dist/assets/src/providers/types.ts +152 -0
- package/dist/assets/web/dist/.vite/manifest.json +176 -0
- package/dist/assets/web/dist/assets/AppShell-Bk2gbvet.js +1 -0
- package/dist/assets/web/dist/assets/CostsPage-CTZcMYYx.js +1 -0
- package/dist/assets/web/dist/assets/NotFoundPage-C-BuaSm8.js +1 -0
- package/dist/assets/web/dist/assets/ResidentDetailPage-D3shEnzl.js +1 -0
- package/dist/assets/web/dist/assets/ResidentsIndexPage-DWIubQ05.js +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-BMjuE-oX.js +126 -0
- package/dist/assets/web/dist/assets/RunRoutePage-XVFj0XDc.css +1 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-C3_jYIo0.js +1 -0
- package/dist/assets/web/dist/assets/RunsTabs-C4krAL9o.js +1 -0
- package/dist/assets/web/dist/assets/ScheduledPage-g1W58mtN.js +1 -0
- package/dist/assets/web/dist/assets/StatusDot-DcPRw3zu.js +1 -0
- package/dist/assets/web/dist/assets/Tooltip-DJUkMYjo.js +1 -0
- package/dist/assets/web/dist/assets/favicon-DL1rdWJt.js +1 -0
- package/dist/assets/web/dist/assets/localIso-L06jV29p.js +1 -0
- package/dist/assets/web/dist/assets/main-BsBGUyMH.css +2 -0
- package/dist/assets/web/dist/assets/main-CyM5f4JC.js +28 -0
- package/dist/assets/web/dist/assets/residentDiskBudget-BMBKlYRH.js +1 -0
- package/dist/assets/web/dist/assets/seed-BglCRKLA.js +6 -0
- package/dist/assets/web/dist/assets/wallClock-Ckv3sKoR.js +1 -0
- package/dist/cli.js +34494 -0
- package/package.json +43 -10
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# Switchboard configuration.
|
|
2
|
+
# Copy to config/config.yaml and adjust. API keys come from env vars only.
|
|
3
|
+
|
|
4
|
+
# The GitHub organization (or user) this installation serves — the account the
|
|
5
|
+
# GitHub App is installed on. Required. It names the shared memory scope
|
|
6
|
+
# (`org:<organization>`) and the About block every model run carries; nothing
|
|
7
|
+
# in the code assumes a particular organization.
|
|
8
|
+
organization: acme
|
|
9
|
+
|
|
10
|
+
providers:
|
|
11
|
+
anthropic:
|
|
12
|
+
type: anthropic
|
|
13
|
+
apiKeyEnv: ANTHROPIC_API_KEY
|
|
14
|
+
# Any OpenAI-compatible endpoint works with the same adapter:
|
|
15
|
+
openai:
|
|
16
|
+
type: openai-compatible
|
|
17
|
+
baseUrl: https://api.openai.com/v1
|
|
18
|
+
apiKeyEnv: OPENAI_API_KEY
|
|
19
|
+
# groq:
|
|
20
|
+
# type: openai-compatible
|
|
21
|
+
# baseUrl: https://api.groq.com/openai/v1
|
|
22
|
+
# apiKeyEnv: GROQ_API_KEY
|
|
23
|
+
# local:
|
|
24
|
+
# type: openai-compatible
|
|
25
|
+
# baseUrl: http://localhost:11434/v1 # Ollama
|
|
26
|
+
|
|
27
|
+
defaults:
|
|
28
|
+
# Agent used when none is specified (per-channel/user/request can override).
|
|
29
|
+
agent: general
|
|
30
|
+
# Default model per agent, as <provider>/<model>.
|
|
31
|
+
models:
|
|
32
|
+
general: anthropic/claude-haiku-4-5
|
|
33
|
+
coding: anthropic/claude-opus-5
|
|
34
|
+
review: anthropic/claude-opus-5
|
|
35
|
+
# Default model effort per agent: low | medium | high (how hard the model
|
|
36
|
+
# thinks per turn; lower = much faster turns). Same layering as models —
|
|
37
|
+
# per-channel/user `effort` / `efforts.<agent>` and a per-request `effort:`
|
|
38
|
+
# directive override these; unset → the agent's built-in effort, else the
|
|
39
|
+
# provider default. Skipped for models without effort support.
|
|
40
|
+
# efforts:
|
|
41
|
+
# coding: medium
|
|
42
|
+
|
|
43
|
+
# PR reviews and the reading diff (docs/reference/specs/reading-diff.md). Every
|
|
44
|
+
# review records its full `git diff`; the ABRIDGED version (meat.dev, run on
|
|
45
|
+
# the bot host over the complete diff GitHub serves, with this bot's own
|
|
46
|
+
# Anthropic key) is on demand — `review abridge <run id>`, grant `review:write`
|
|
47
|
+
# — unless `provider: meat` makes it automatic after every review's record is
|
|
48
|
+
# written (one Opus-class call per review; meat caches by model + diff). `off`
|
|
49
|
+
# records no reading diff at all. `SWITCHBOARD_READING_DIFF=git|meat|off` in
|
|
50
|
+
# the environment overrides `provider` on a deployed bot. Needs `runHistory`.
|
|
51
|
+
# review:
|
|
52
|
+
# readingDiff:
|
|
53
|
+
# provider: git # git (default) | meat | off
|
|
54
|
+
# meatModel: claude-opus-5 # meat's -model; Opus-class is the floor that actually abridges
|
|
55
|
+
# meatTimeoutS: 240 # meat's own budget on the host; past it the abridging fails by name
|
|
56
|
+
|
|
57
|
+
# Static per-channel defaults (channel ID -> scope). Runtime overrides set via
|
|
58
|
+
# "config set channel ..." are stored in data/overrides.json and win over these.
|
|
59
|
+
# channels:
|
|
60
|
+
# slack:C012345:
|
|
61
|
+
# agent: review
|
|
62
|
+
# models:
|
|
63
|
+
# review: anthropic/claude-opus-5
|
|
64
|
+
# efforts:
|
|
65
|
+
# coding: medium
|
|
66
|
+
|
|
67
|
+
# Static per-user defaults (user ID -> scope).
|
|
68
|
+
# users:
|
|
69
|
+
# slack:U012345:
|
|
70
|
+
# model: openai/gpt-5
|
|
71
|
+
|
|
72
|
+
# Authorization (docs/reference/specs/authorization.md item 9, docs/reference/authorization.md):
|
|
73
|
+
# two blocks. `restrict` names what is CLOSED unless granted — an agent listed
|
|
74
|
+
# here runs only for an actor whose grants hold agent:run:<name> (or `all`); a
|
|
75
|
+
# repo listed here is used only by an actor whose `repos` axis names it (or
|
|
76
|
+
# `all`). Everything unlisted is open to everyone who can reach the bot.
|
|
77
|
+
# Enforcement is at run time against the resolved agent/repo, so it can't be
|
|
78
|
+
# bypassed via directives or config scopes. Names must be registered agents /
|
|
79
|
+
# owner/name slugs, or the load fails.
|
|
80
|
+
# restrict:
|
|
81
|
+
# agents: [coding]
|
|
82
|
+
# repos: [acme/api]
|
|
83
|
+
#
|
|
84
|
+
# `grants`: what ANY actor holds, in one shape, keyed by its platform-namespaced
|
|
85
|
+
# id — `slack:U…`, `http:<subject>`, `mcp:<subject>`, `access:<sub>`,
|
|
86
|
+
# `access:svc:<common_name>`, `schedule:<name>` — or by a whole surface,
|
|
87
|
+
# `<ns>:*`: `access:*` is every Cloudflare Access browser session (the org,
|
|
88
|
+
# granted once instead of enumerated), `slack:*` every workspace member the bot
|
|
89
|
+
# hears, `http:*` / `mcp:*` every ingress token. Each entry has up to three
|
|
90
|
+
# axes; every axis is a list of names or the explicit word `all`, and an ABSENT
|
|
91
|
+
# axis is the empty set (fail-closed). A `slack:` user holds the open chat
|
|
92
|
+
# commands and every unrestricted agent whether listed or not — an entry ADDS to
|
|
93
|
+
# that; an Access browser session holds every group's read the same way;
|
|
94
|
+
# credentials (`http:`, `mcp:`, `access:svc:`) hold exactly their entry and an
|
|
95
|
+
# unlisted one holds nothing, not even `dispatch`. A surface entry is unioned
|
|
96
|
+
# into every actor of its surface on top of its own entry — a person's entry
|
|
97
|
+
# never narrows it; `access:*` never reaches an `access:svc:` token. `config:write`
|
|
98
|
+
# (channel config), `repo:write` (repo onboard/offboard/…, friction propose) and
|
|
99
|
+
# every `runs:*` are never a baseline: nobody holds them until granted, and
|
|
100
|
+
# nobody is an admin without an `all` entry. An unknown id prefix, a misspelled
|
|
101
|
+
# `all` or a `*` that is not a whole surface (`slack:U*`, `schedule:*`,
|
|
102
|
+
# `agent:*`) fails the load, as does any top-level key this file does not document.
|
|
103
|
+
# grants:
|
|
104
|
+
# access:*: # everyone in the org: every Access browser session
|
|
105
|
+
# actions: all # holds everything — the org-wide pattern
|
|
106
|
+
# channels: all
|
|
107
|
+
# repos: all
|
|
108
|
+
# slack:U0ADMIN: # an admin, spelled out
|
|
109
|
+
# actions: all
|
|
110
|
+
# channels: all
|
|
111
|
+
# repos: all
|
|
112
|
+
# slack:U0456DEV:
|
|
113
|
+
# actions: [agent:run:coding, repo:write, friction:write]
|
|
114
|
+
# repos: [acme/api] # the repos this user may use
|
|
115
|
+
# access:svc:ops-bot: # an Access service token (its common_name)
|
|
116
|
+
# actions: [runs:read, runs:write, friction:read]
|
|
117
|
+
# channels: all # sees runs from every channel
|
|
118
|
+
# http:ci: # an ingress token's subject, as http: …
|
|
119
|
+
# actions: [dispatch, runs:read]
|
|
120
|
+
# channels: [http:ops] # … the channels whose runs it may read (a
|
|
121
|
+
# # token with no `channel` key and no entry
|
|
122
|
+
# # here reads none — the fail-closed default)
|
|
123
|
+
# mcp:ci: # … and the same subject over MCP
|
|
124
|
+
# actions: [dispatch, runs:read]
|
|
125
|
+
# channels: [mcp:ops]
|
|
126
|
+
# http:cron: # the identity the shim fires schedules as
|
|
127
|
+
# actions: [friction:read, friction:write, repo:write]
|
|
128
|
+
# channels: all # the weekly pass analyzes the whole fleet
|
|
129
|
+
# schedule:self-improvement: # the weekly cron's own actor (src/core/schedules.ts
|
|
130
|
+
# actions: [friction:write] # declares its grants; an entry here REPLACES them)
|
|
131
|
+
# channels: all # analyzes the whole fleet, not one channel
|
|
132
|
+
|
|
133
|
+
# Where agent tools execute. "local" runs them on the bot host (dev/CLI);
|
|
134
|
+
# "e2b" runs each thread's tools in its own E2B micro-VM — the repo checkout
|
|
135
|
+
# and GH_TOKEN live in the sandbox, never on the bot host. Recommended for
|
|
136
|
+
# any deployment where untrusted users can reach the coding agent.
|
|
137
|
+
execution:
|
|
138
|
+
type: local
|
|
139
|
+
# Cloudflare Sandbox via the proxy Worker (deploy/cloudflare-sandbox/):
|
|
140
|
+
# type: cloudflare
|
|
141
|
+
# url: https://switchboard-sandbox.<account>.workers.dev
|
|
142
|
+
# apiKeyEnv: SANDBOX_TOKEN # bearer secret shared with the Worker
|
|
143
|
+
# E2B:
|
|
144
|
+
# type: e2b
|
|
145
|
+
# apiKeyEnv: E2B_API_KEY
|
|
146
|
+
# timeoutMinutes: 30 # sandbox idle lifetime; thread follow-ups reconnect
|
|
147
|
+
# Resident repo environments (deploy/cloudflare-resident/): when set, a
|
|
148
|
+
# request whose target repo is onboarded and warm runs against the resident
|
|
149
|
+
# Worker; any other resident state falls back to the backend above with a
|
|
150
|
+
# named reason on the status card. Works alongside any per-thread type.
|
|
151
|
+
# Repos are onboarded from chat: `repo onboard <owner/name>` (see
|
|
152
|
+
# docs/how-to/onboard-a-repo.md; needs the `repo:write` grant — never a
|
|
153
|
+
# baseline, admins only until granted). `repo list` shows lifecycle;
|
|
154
|
+
# `repo offboard`/`repo rebuild` accept --dry-run for an itemized plan
|
|
155
|
+
# without executing.
|
|
156
|
+
# Deterministic ops (no model turn): `repo test <owner/name> [<ref>]` /
|
|
157
|
+
# `repo build <owner/name> [<ref>]` — and natural asks like "run the tests
|
|
158
|
+
# on main in acme/api" — run the repo's onboarded command in a disposable
|
|
159
|
+
# resident checkout and post the result with zero model calls. Operator-
|
|
160
|
+
# level: gated like a coding run — `restrict.agents` / `restrict.repos`
|
|
161
|
+
# against the caller's grants (NOT `repo:write`). Anything ambiguous falls through to the agent. With
|
|
162
|
+
# local execution the same asks run fixed Node commands in the thread's
|
|
163
|
+
# local workspace (dev-only).
|
|
164
|
+
# resident:
|
|
165
|
+
# baseUrl: https://switchboard-resident.<account>.workers.dev
|
|
166
|
+
# tokenEnv: RESIDENT_OPERATOR_TOKEN # operator bearer secret (default)
|
|
167
|
+
# adminTokenEnv: RESIDENT_ADMIN_TOKEN # admin bearer for `repo ...` commands (default)
|
|
168
|
+
# probeTimeoutMs: 2000 # /status probe budget; timeout = not warm
|
|
169
|
+
|
|
170
|
+
# Where per-conversation agent workspaces are created (local execution only).
|
|
171
|
+
# Under docker compose this directory is a named volume, so checkouts survive a restart.
|
|
172
|
+
workspaceDir: ./workspaces
|
|
173
|
+
|
|
174
|
+
# Cross-session self-learning memory (docs/reference/specs/memory.md). DEFAULT OFF: absent, or
|
|
175
|
+
# enabled:false, means the dispatcher uses a NullMemoryStore and the model input
|
|
176
|
+
# is byte-identical to memory-off — zero behavior change. When enabled, before
|
|
177
|
+
# each model turn the dispatcher retrieves scope-relevant distilled records
|
|
178
|
+
# (keyword + recency ranked, hard-budgeted) and injects them as a DEDICATED
|
|
179
|
+
# advisory context block on the system prompt ("Background memory for <resource>
|
|
180
|
+
# (may be outdated — verify before acting):"), never mixed into history. After
|
|
181
|
+
# each reply, a run that did real work (used tools, or a thread ≥4 turns deep)
|
|
182
|
+
# fires ONE async reflection call on `model` that distills ≤5 durable facts + 1
|
|
183
|
+
# summary into the store (secret-redacted, confidence-gated, dedup/supersede on
|
|
184
|
+
# write) — fire-and-forget, so it never delays or fails the reply. Records live
|
|
185
|
+
# in the Memory Worker (deploy/cloudflare-memory/: one SQLite Durable Object per
|
|
186
|
+
# scope, survives bot restarts); with no `worker` configured the bot falls back
|
|
187
|
+
# to an IN-PROCESS store and warns at startup — dev only. See docs/reference/specs/memory.md.
|
|
188
|
+
# memory:
|
|
189
|
+
# enabled: false # master switch (default false)
|
|
190
|
+
# worker:
|
|
191
|
+
# baseUrl: https://switchboard-memory.example.com
|
|
192
|
+
# tokenEnv: MEMORY_TOKEN # env var holding the Worker's bearer (default)
|
|
193
|
+
# # Scopes are derived per request, not configured: every request reads the
|
|
194
|
+
# # shared org scope (org:acme), the channel's scope (channel:slack:C…)
|
|
195
|
+
# # and — when the run is bound to a repo — that repo's scope (repo:owner/name)
|
|
196
|
+
# # plus the requesting user's own scope (user:slack:U…) — a
|
|
197
|
+
# # person's records never surface for anyone else.
|
|
198
|
+
# limit: 8 # max records retrieved/injected per request (default 8)
|
|
199
|
+
# maxTokens: 800 # hard token budget for the injected block (default 800)
|
|
200
|
+
# maxRecordsPerScope: 500
|
|
201
|
+
# # per-scope cap on ACTIVE records (default 500): a write
|
|
202
|
+
# # that would exceed it evicts the least recently used
|
|
203
|
+
# # records (soft delete, status `evicted`) in the same write.
|
|
204
|
+
# model: anthropic/claude-haiku-4-5
|
|
205
|
+
# # <provider>/<model> for the reflection pass — pick a
|
|
206
|
+
# # cheap tier. Absent → the run's own resolved model.
|
|
207
|
+
|
|
208
|
+
# Self-improvement proposals (docs/reference/specs/run-friction.md). Every finished run's friction
|
|
209
|
+
# diagnosis (docs/reference/specs/run-friction.md) is stored with its run record, so the
|
|
210
|
+
# runs these commands see are run history's (`runHistory` below; its retention
|
|
211
|
+
# bounds them — without `runHistory` there are no recent runs to analyze).
|
|
212
|
+
# `friction report` (open to everyone) shows the patterns that recur across
|
|
213
|
+
# recent runs; `friction propose [--dry-run]` (admin-gated like repo management)
|
|
214
|
+
# files the top patterns as labeled GitHub issues — evidence, affected runs, a
|
|
215
|
+
# concrete suggested fix — deduped against the proposals already open. It only
|
|
216
|
+
# ever PROPOSES: no PRs, nothing merged. Without `repo`, `friction propose`
|
|
217
|
+
# refuses. See docs/reference/specs/self-improvement.md.
|
|
218
|
+
# selfImprovement:
|
|
219
|
+
# repo: acme/switchboard # owner/name the proposals are filed against
|
|
220
|
+
# label: self-improvement # triage label (created if missing; default shown)
|
|
221
|
+
# minRuns: 2 # a pattern must recur in ≥ this many distinct runs
|
|
222
|
+
# top: 3 # proposals filed per pass
|
|
223
|
+
|
|
224
|
+
# Scheduled jobs. The schedule registry (src/core/schedules.ts) lists every
|
|
225
|
+
# cron the Cloudflare Worker shim runs; a `run` schedule fires as an ordinary run
|
|
226
|
+
# through /ingress as the `cron` identity — give that identity a token in
|
|
227
|
+
# SWITCHBOARD_INGRESS_TOKENS ({"…": {"subject": "cron", "channel": "cron"}}) and
|
|
228
|
+
# grant it what its command needs in `grants.http:cron` (`friction propose` →
|
|
229
|
+
# `friction:write`; a text command through /ingress is decided from the actor's
|
|
230
|
+
# grants like a tool call) with `channels: all` so the pass reads the fleet's
|
|
231
|
+
# runs, not the cron channel's own. The shim records each firing on the state Worker's
|
|
232
|
+
# ScheduleDO; this block tells the /runs "Scheduled" panel where to read them.
|
|
233
|
+
# Without it the panel lists the schedules with no firing history.
|
|
234
|
+
# schedules:
|
|
235
|
+
# worker:
|
|
236
|
+
# baseUrl: https://switchboard-memory.example.com
|
|
237
|
+
# tokenEnv: MEMORY_TOKEN # env var holding the Worker's bearer (default)
|
|
238
|
+
|
|
239
|
+
# agent:ship pipeline caps (docs/reference/specs/agent-ship.md). `agent:ship in owner/repo:
|
|
240
|
+
# <task>` runs the coding → review → fix loop to LGTM as one pipeline: at most
|
|
241
|
+
# `maxRounds` review rounds and `maxMinutes` minutes of wall clock — whichever
|
|
242
|
+
# hits first ends the loop, and each child round's own budget is clipped to the
|
|
243
|
+
# remaining pipeline time. Defaults shown; both must be integers >= 1.
|
|
244
|
+
# ship:
|
|
245
|
+
# maxRounds: 3 # review rounds per pipeline
|
|
246
|
+
# maxMinutes: 120 # pipeline wall-clock budget in minutes
|
|
247
|
+
|
|
248
|
+
# Spend reporting: GET /costs (Access-gated). Optional. See docs/reference/specs/costs.md.
|
|
249
|
+
# costs:
|
|
250
|
+
# cloudflareAccountId: <32-hex account id>
|
|
251
|
+
# cloudflareTokenEnv: CF_ANALYTICS_TOKEN # token with Account Analytics:Read
|
|
252
|
+
# anthropicAdminKeyEnv: ANTHROPIC_ADMIN_KEY # optional: sk-ant-admin… for LLM spend
|
|
253
|
+
# groups:
|
|
254
|
+
# myapp:
|
|
255
|
+
# label: My App
|
|
256
|
+
# workers: [myapp, myapp-worker] # Worker script names — the attribution root: their Durable
|
|
257
|
+
# # Objects, their requests/CPU, and the R2 buckets named <worker>-cache
|
|
258
|
+
# containerApps: # container application id → label (no dataset ties an app to a Worker)
|
|
259
|
+
# a0000000-0000-0000-0000-000000000000: server
|
|
260
|
+
# durableObjectNamespaces: # optional: DO namespace id → label; a namespace hosted by a listed
|
|
261
|
+
# <32-hex namespace id>: server DO # Worker is attributed anyway, labelled by that Worker
|
|
262
|
+
# r2Buckets: # optional: bucket name → label for buckets not named <worker>-cache
|
|
263
|
+
# some-other-bucket: uploads
|
|
264
|
+
# anthropicWorkspaceId: wrkspc_... # this app's Anthropic workspace
|
|
265
|
+
|
|
266
|
+
# Dashboard authentication (docs/reference/specs/access-gate.md). Everything the dashboard
|
|
267
|
+
# serves — /runs*, /residents*, /costs*, /mcp/connect/*, and every /api/* command
|
|
268
|
+
# — sits behind ONE identity gate; `auth` picks which credential it checks:
|
|
269
|
+
# access — the Cloudflare Access JWT the edge injects (ACCESS_TEAM_DOMAIN and
|
|
270
|
+
# ACCESS_AUD in the environment). Production behind an Access app.
|
|
271
|
+
# token — `Authorization: Bearer <token>`, the secret read from the env var
|
|
272
|
+
# `token.env` names (default DASHBOARD_TOKEN); a match is the one
|
|
273
|
+
# actor `token.actor` names, granted under `grants` like any browser
|
|
274
|
+
# session. For an Access-free deployment fronted by a proxy, curl or
|
|
275
|
+
# a script — a bare browser cannot send the header.
|
|
276
|
+
# none — no credential; served ONLY to loopback callers on a localhost
|
|
277
|
+
# deployment (PUBLIC_BASE_URL unset or naming localhost). A remote
|
|
278
|
+
# caller is refused, and an explicit `none` on a deployment with a
|
|
279
|
+
# public PUBLIC_BASE_URL is a startup error. Local development.
|
|
280
|
+
# Default when the block is absent: `access` if both ACCESS_* are set, else `none`.
|
|
281
|
+
# A strategy whose inputs are missing fails at startup, naming the piece.
|
|
282
|
+
# dashboard:
|
|
283
|
+
# auth: token
|
|
284
|
+
# token:
|
|
285
|
+
# env: DASHBOARD_TOKEN # env var holding the bearer (default)
|
|
286
|
+
# actor: access:ops # the actor the bearer resolves to — key its grants entry by this id
|
|
287
|
+
|
|
288
|
+
# Slack adapter — reconnect catch-up (docs/decisions/0012-reconnect-catch-up-as-recovery.md). Socket Mode drops every mention
|
|
289
|
+
# posted while the websocket is down (each bot deploy = drain + cold start).
|
|
290
|
+
# On every (re)connect the bot re-reads recent history of the channels it is in
|
|
291
|
+
# and runs whatever has no receipt from it (no :eyes:, no bot reply after it).
|
|
292
|
+
# Defaults: enabled, 30-minute window. The drain closes the socket on SIGTERM
|
|
293
|
+
# and the next container starts only after this one exits, so a deploy over a
|
|
294
|
+
# run in flight blacks Slack out for up to the 15-minute drain deadline plus a
|
|
295
|
+
# cold start — the window must be >= 20 minutes to cover that; a smaller
|
|
296
|
+
# value is kept but warned about at startup. Needs channels:read + groups:read
|
|
297
|
+
# (channel listing) on top of the history scopes.
|
|
298
|
+
# slack:
|
|
299
|
+
# catchUp:
|
|
300
|
+
# enabled: true
|
|
301
|
+
# windowMinutes: 30
|
|
302
|
+
|
|
303
|
+
# Where chat-set runtime overrides (`config set`, `config instructions`, `config clear`)
|
|
304
|
+
# persist (docs/reference/specs/routing-and-config.md item 12). Without this block they go to
|
|
305
|
+
# data/overrides.json on the host disk — EPHEMERAL on Cloudflare Containers. In
|
|
306
|
+
# production name the state Worker's ConfigDO (same Worker + bearer as memory and
|
|
307
|
+
# run history); the bot and the CLI then write one versioned document.
|
|
308
|
+
# runtimeOverrides:
|
|
309
|
+
# worker:
|
|
310
|
+
# baseUrl: https://switchboard-memory.example.com
|
|
311
|
+
# tokenEnv: MEMORY_TOKEN # default
|
|
312
|
+
|
|
313
|
+
# Persistent run history (docs/reference/specs/run-history.md). Every finished run's
|
|
314
|
+
# record — identity, timing, terminal status, the redacted event stream, the
|
|
315
|
+
# friction diagnosis — is kept for a retention window instead of evicted 60 s
|
|
316
|
+
# after finish, and the friction ledger's "recent runs" are read from it.
|
|
317
|
+
# Without this block history is OFF (runs stay live-only, as before).
|
|
318
|
+
# runHistory:
|
|
319
|
+
# retentionDays: 30 # days a finished run stays readable (1–365; default 30)
|
|
320
|
+
# maxRuns: 5000 # newest runs kept (1–20000; default 5000)
|
|
321
|
+
# maxBytes: 2147483648 # total stored bytes kept (16 MiB–8 GiB; default 2 GiB)
|
|
322
|
+
# includeContext: true # include the thread-context turns fed to the model in the
|
|
323
|
+
# # run stream — live page and persisted record (default true)
|
|
324
|
+
# store: worker # `worker` (default when `worker` is set) or `file` — an
|
|
325
|
+
# # explicit opt-in that writes data/runs/<id>.json on the
|
|
326
|
+
# # host disk (ephemeral on Cloudflare Containers)
|
|
327
|
+
# # The durable store: a RunHistoryDO on the state Worker (deploy/cloudflare-memory/,
|
|
328
|
+
# # the same Worker + bearer as memory and the friction ledger). Must be https:.
|
|
329
|
+
# worker:
|
|
330
|
+
# baseUrl: https://switchboard-memory.example.com
|
|
331
|
+
# tokenEnv: MEMORY_TOKEN # env var holding the Worker's bearer (default)
|
|
332
|
+
|
|
333
|
+
# External MCP servers as agent tools (docs/reference/specs/mcp-tools.md). Servers are a
|
|
334
|
+
# SETTING ON THE CONFIG SCOPES, not a list here: `defaults.mcpServers` (org-wide),
|
|
335
|
+
# `channels.<id>.mcpServers`, `users.<id>.mcpServers` — static below, or added at run
|
|
336
|
+
# time with `mcp add` (persisted with the other runtime overrides). A run gets the
|
|
337
|
+
# union of the three tiers; a name in more than one tier resolves to the highest-
|
|
338
|
+
# trust tier (org > channel > user). Only org entries may name coding/review/ship.
|
|
339
|
+
# Tools appear as `mcp__<name>__<tool>`; descriptions and results are untrusted
|
|
340
|
+
# data; every call is budgeted and recorded. URLs are SSRF-checked at load and at
|
|
341
|
+
# connect. This block turns the feature on and names the deployment knobs; without
|
|
342
|
+
# it MCP is off and the `mcp.*` commands answer `unavailable`.
|
|
343
|
+
# mcp:
|
|
344
|
+
# credentialKeyEnv: MCP_CREDENTIAL_KEY # default; 32 bytes base64 (`openssl rand -base64 32`),
|
|
345
|
+
# # bot-only — seals tokens entered on the connect page
|
|
346
|
+
# # secretsPath: ./data/mcp-secrets.json # sealed blobs + tickets when no runtimeOverrides.worker
|
|
347
|
+
#
|
|
348
|
+
# defaults:
|
|
349
|
+
# mcpServers:
|
|
350
|
+
# github:
|
|
351
|
+
# url: https://api.githubcopilot.com/mcp/
|
|
352
|
+
# auth: bearer
|
|
353
|
+
# tokenEnv: MCP_GITHUB_TOKEN # static bearer from the bot env (startup fails if unset)
|
|
354
|
+
# agents: [general, research, coding] # default [general, research]
|
|
355
|
+
# channels:
|
|
356
|
+
# "slack:C0123":
|
|
357
|
+
# mcpServers:
|
|
358
|
+
# notion: { url: https://mcp.notion.so/mcp, auth: none } # general/research only
|
|
359
|
+
# vanta: { url: https://mcp.vanta.com/mcp, auth: oauth } # OAuth 2.1: a channel-config holder signs in once via `mcp connect vanta --scope channel`
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Types for build-stamp.mjs (plain-Node ESM so a Worker's `npm run deploy`
|
|
2
|
+
// needs no build step). The runtime shape it produces is read back by
|
|
3
|
+
// `src/deploy/buildStamp.ts` (`BuildStamp`).
|
|
4
|
+
export type Stamp = { commit: string; builtAt: string };
|
|
5
|
+
export const DEFINE_COMMIT: "SWITCHBOARD_BUILD_COMMIT";
|
|
6
|
+
export const DEFINE_BUILT_AT: "SWITCHBOARD_BUILT_AT";
|
|
7
|
+
export function buildStamp(input: { commit: string; dirty: boolean; now?: Date }): Stamp;
|
|
8
|
+
export function stampFromEnv(env: Record<string, string | undefined>, now?: Date): Stamp | undefined;
|
|
9
|
+
export function defineArgs(stamp: Stamp): string[];
|
|
10
|
+
export function spawnOutcome(res: {
|
|
11
|
+
error?: { message: string };
|
|
12
|
+
signal?: NodeJS.Signals | null;
|
|
13
|
+
status?: number | null;
|
|
14
|
+
}): { code: number; message?: string };
|
|
15
|
+
export function main(extraArgs?: string[]): number;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Stamp the build identity into a Worker SCRIPT deploy (docs/reference/specs/execution.md
|
|
3
|
+
// item 13) — the resident, memory and sandbox Workers' `npm run deploy` runs
|
|
4
|
+
// this instead of bare `wrangler deploy`.
|
|
5
|
+
//
|
|
6
|
+
// It derives `{commit, builtAt}` from the tree being deployed and hands them to
|
|
7
|
+
// `wrangler deploy --define`, so the bundle carries the commit and the Worker
|
|
8
|
+
// answers it on `/healthz` (`src/deploy/buildStamp.ts` is the reader). The bot
|
|
9
|
+
// does the same thing through a COPYed `build.json` (`../cloudflare/write-build.mjs`)
|
|
10
|
+
// because a container needs a file; a Worker script needs a substitution — the
|
|
11
|
+
// reader module explains why a generated file cannot work here.
|
|
12
|
+
//
|
|
13
|
+
// Dependency-free Node. `buildStamp` and `defineArgs` are pure given their
|
|
14
|
+
// inputs and unit-tested from `src/deploy/buildStamp.test.ts`; `main()` does
|
|
15
|
+
// the git reads and the spawn.
|
|
16
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
17
|
+
import { dirname, join } from "node:path";
|
|
18
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
19
|
+
|
|
20
|
+
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
21
|
+
|
|
22
|
+
/** The identifiers substituted into the bundle. `src/deploy/buildStamp.ts`
|
|
23
|
+
* declares and reads exactly these two. */
|
|
24
|
+
export const DEFINE_COMMIT = "SWITCHBOARD_BUILD_COMMIT";
|
|
25
|
+
export const DEFINE_BUILT_AT = "SWITCHBOARD_BUILT_AT";
|
|
26
|
+
|
|
27
|
+
/** `{ commit, builtAt }` — `commit` gets a `-dirty` suffix when the tree has
|
|
28
|
+
* uncommitted changes, because wrangler bundles the TREE, not the commit. */
|
|
29
|
+
export function buildStamp({ commit, dirty, now = new Date() }) {
|
|
30
|
+
return { commit: `${commit}${dirty ? "-dirty" : ""}`, builtAt: now.toISOString() };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The `--define` argv for a stamp. `JSON.stringify` is what makes each value
|
|
34
|
+
* a JS string LITERAL rather than an expression: esbuild substitutes the text
|
|
35
|
+
* verbatim, so an unquoted value would be parsed as code. */
|
|
36
|
+
export function defineArgs(stamp) {
|
|
37
|
+
return [
|
|
38
|
+
"--define",
|
|
39
|
+
`${DEFINE_COMMIT}:${JSON.stringify(stamp.commit)}`,
|
|
40
|
+
"--define",
|
|
41
|
+
`${DEFINE_BUILT_AT}:${JSON.stringify(stamp.builtAt)}`,
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The identity the environment hands a deploy that has no tree to read: a
|
|
46
|
+
* deploy from the published npm package runs this script in a materialised
|
|
47
|
+
* copy of the Worker's directory, where git knows nothing, and sets
|
|
48
|
+
* `SWITCHBOARD_BUILD_COMMIT` to the commit the package was built from
|
|
49
|
+
* (src/deploy/run.ts). `builtAt` is still now — each deploy is its own build.
|
|
50
|
+
* Undefined when the variable is unset or blank: git decides. Pure. */
|
|
51
|
+
export function stampFromEnv(env, now = new Date()) {
|
|
52
|
+
const commit = env[DEFINE_COMMIT]?.trim();
|
|
53
|
+
return commit ? { commit, builtAt: now.toISOString() } : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Read the tree's identity: the environment's when it names one, else git. A
|
|
57
|
+
* git failure (deploying from an export, no git on PATH) warns and stamps
|
|
58
|
+
* `unknown` rather than blocking the deploy — the Worker then reports
|
|
59
|
+
* `commit: "unknown"`, which is the honest answer. */
|
|
60
|
+
function readStamp() {
|
|
61
|
+
const given = stampFromEnv(process.env);
|
|
62
|
+
if (given) return given;
|
|
63
|
+
try {
|
|
64
|
+
const git = (args) => execFileSync("git", args, { cwd: REPO_ROOT, encoding: "utf8" }).trim();
|
|
65
|
+
return buildStamp({ commit: git(["rev-parse", "HEAD"]), dirty: git(["status", "--porcelain"]) !== "" });
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.warn(`[build-stamp] git failed (${err?.message ?? err}) — deploying with commit "unknown"`);
|
|
68
|
+
return { commit: "unknown", builtAt: new Date().toISOString() };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** How a finished spawn becomes this script's exit code, plus the line to
|
|
73
|
+
* print. A signal kill (Ctrl-C, a CI timeout) reports `status: null`, so
|
|
74
|
+
* without naming the signal an interrupted deploy is indistinguishable from
|
|
75
|
+
* a wrangler that simply failed. */
|
|
76
|
+
export function spawnOutcome({ error, signal, status }) {
|
|
77
|
+
if (error) return { code: 1, message: `could not run wrangler: ${error.message}` };
|
|
78
|
+
if (signal) return { code: 1, message: `wrangler was killed by ${signal} — the deploy did not finish` };
|
|
79
|
+
if (typeof status !== "number") return { code: 1, message: "wrangler exited with no status" };
|
|
80
|
+
return { code: status };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function main(extraArgs = []) {
|
|
84
|
+
const stamp = readStamp();
|
|
85
|
+
const args = ["deploy", ...defineArgs(stamp), ...extraArgs];
|
|
86
|
+
console.log(`[build-stamp] ${stamp.commit} @ ${stamp.builtAt}`);
|
|
87
|
+
// `wrangler` resolves through the calling package's node_modules/.bin, which
|
|
88
|
+
// npm puts on PATH for a `npm run deploy`. shell:false — no argument of this
|
|
89
|
+
// command may ever be interpreted by a shell.
|
|
90
|
+
const outcome = spawnOutcome(spawnSync("wrangler", args, { stdio: "inherit", cwd: process.cwd() }));
|
|
91
|
+
if (outcome.message) console.error(`[build-stamp] ${outcome.message}`);
|
|
92
|
+
return outcome.code;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Run only when executed directly, not when imported by the tests.
|
|
96
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
97
|
+
process.exitCode = main(process.argv.slice(2));
|
|
98
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Query historical Workers Logs for one of the installation's Workers (the
|
|
3
|
+
# "what did the sandbox/resident/bot Worker log at 15:26?" question `wrangler
|
|
4
|
+
# tail` can't answer — tail is live-only, and the wrangler OAuth login has no
|
|
5
|
+
# observability scope). Needs an API token with Account → Workers
|
|
6
|
+
# Observability: Read (+ Workers Scripts: Read) in CLOUDFLARE_OBS_TOKEN —
|
|
7
|
+
# deliberately NOT CLOUDFLARE_API_TOKEN, which wrangler would pick up. The
|
|
8
|
+
# account is the deployment profile's (deploy/profile.json, or the file
|
|
9
|
+
# SWITCHBOARD_DEPLOY_PROFILE names).
|
|
10
|
+
#
|
|
11
|
+
# deploy/bin/cf-logs <service> [minutes-back=60] [grep-pattern]
|
|
12
|
+
# deploy/bin/cf-logs switchboard-sandbox 240 'exit|OOM|error'
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
PROFILE=${SWITCHBOARD_DEPLOY_PROFILE:-"$(dirname "$0")/../profile.json"}
|
|
15
|
+
ACCOUNT=$(node -p 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).account' "$PROFILE")
|
|
16
|
+
SERVICE=${1:?service (switchboard | switchboard-sandbox | switchboard-resident | switchboard-memory)}
|
|
17
|
+
MINUTES=${2:-60}
|
|
18
|
+
PATTERN=${3:-}
|
|
19
|
+
: "${CLOUDFLARE_OBS_TOKEN:?set CLOUDFLARE_OBS_TOKEN (Workers Observability: Read on the profile's account)}"
|
|
20
|
+
NOW=$(date +%s)
|
|
21
|
+
FROM=$(( (NOW - MINUTES * 60) * 1000 )); TO=$(( NOW * 1000 ))
|
|
22
|
+
BODY=$(cat <<JSON
|
|
23
|
+
{"queryId":"cf-logs","timeframe":{"from":$FROM,"to":$TO},
|
|
24
|
+
"parameters":{"datasets":["cloudflare-workers"],
|
|
25
|
+
"filters":[{"key":"\$metadata.service","operation":"eq","value":"$SERVICE","type":"string"}]},
|
|
26
|
+
"view":"events","limit":500}
|
|
27
|
+
JSON
|
|
28
|
+
)
|
|
29
|
+
curl -sS -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT/workers/observability/telemetry/query" \
|
|
30
|
+
-H "Authorization: Bearer $CLOUDFLARE_OBS_TOKEN" -H 'content-type: application/json' -d "$BODY" \
|
|
31
|
+
| jq -r '.result.events.events[]? | "\(.timestamp | tostring | .[0:10] | tonumber | todate) \(.["$metadata"].level // "log") \(.["$metadata"].message // (.["$workers"].event // "" | tostring))"' \
|
|
32
|
+
| { if [ -n "$PATTERN" ]; then grep -E "$PATTERN" || true; else cat; fi; }
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "switchboard-worker",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=22"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"check:image": "docker build --quiet ../..",
|
|
10
|
+
"deploy": "node preflight.mjs && node write-build.mjs && wrangler deploy",
|
|
11
|
+
"preflight": "node preflight.mjs",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"verify": "npm --prefix ../.. run --silent deploy:gen && npm run typecheck && npm test",
|
|
14
|
+
"dev": "wrangler dev",
|
|
15
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
16
|
+
"typegen": "wrangler types",
|
|
17
|
+
"tail": "wrangler tail",
|
|
18
|
+
"secrets": "npm --prefix ../.. run --silent cli -- deploy secrets bot"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@cloudflare/containers": "^0.3.7"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@cloudflare/workers-types": "^5.20260904.1",
|
|
25
|
+
"typescript": "^5.9.3",
|
|
26
|
+
"vitest": "^5.0.0",
|
|
27
|
+
"wrangler": "^4.129.0"
|
|
28
|
+
}
|
|
29
|
+
}
|