@sibylline-labs/eris 0.0.0-stage → 0.3.1
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 +202 -0
- package/NOTICE +2 -0
- package/README.md +146 -2
- package/bin/eris.ts +42 -0
- package/docs/ARCHITECTURE.md +188 -0
- package/docs/COMPLETION.md +106 -0
- package/docs/ERRORS.md +632 -0
- package/docs/OUTPUT-CONTRACT.md +219 -0
- package/docs/README.md +30 -0
- package/docs/UPDATE.md +159 -0
- package/docs/YAK.md +395 -0
- package/docs/dev/ADDING-A-COMMAND.md +79 -0
- package/docs/dev/DEVELOPMENT.md +88 -0
- package/docs/dev/LAYOUT.md +74 -0
- package/docs/dev/PLUGINS.md +382 -0
- package/docs/dev/RELEASING.md +157 -0
- package/docs/dev/TESTING.md +84 -0
- package/docs/dev/surfaces/secrets.md +95 -0
- package/package.json +55 -4
- package/packages/yak-contract/package.json +28 -0
- package/packages/yak-contract/schema/yak-v2.schema.json +416 -0
- package/packages/yak-contract/src/index.ts +1 -0
- package/packages/yak-contract/src/v2.ts +375 -0
- package/plugins/index.ts +12 -0
- package/plugins/yak/handlers.ts +128 -0
- package/plugins/yak/index.ts +51 -0
- package/plugins/yak/login.ts +300 -0
- package/plugins/yak/yak-context.ts +280 -0
- package/plugins/yak/yak-identity.ts +399 -0
- package/plugins/yak/yak-redact.ts +686 -0
- package/plugins/yak/yak-setup.ts +133 -0
- package/plugins/yak/yak-submit.ts +1166 -0
- package/plugins/yak/yak.ts +485 -0
- package/src/build.ts +248 -0
- package/src/cli/args.ts +434 -0
- package/src/cli/commands/completion/bash.ts +24 -0
- package/src/cli/commands/completion/domain.ts +203 -0
- package/src/cli/commands/completion/emit.ts +246 -0
- package/src/cli/commands/completion/shell.ts +24 -0
- package/src/cli/commands/completion/zsh.ts +24 -0
- package/src/cli/commands/completion.ts +48 -0
- package/src/cli/commands/update/github.ts +328 -0
- package/src/cli/commands/update/opt-in.ts +32 -0
- package/src/cli/commands/update/refusal.ts +21 -0
- package/src/cli/commands/update/release.ts +351 -0
- package/src/cli/commands/update/replace.ts +143 -0
- package/src/cli/commands/update/semver.ts +48 -0
- package/src/cli/commands/update.ts +47 -0
- package/src/cli/commands/usage.ts +68 -0
- package/src/cli/commands/version.ts +29 -0
- package/src/cli/compose.ts +273 -0
- package/src/cli/output.ts +81 -0
- package/src/cli/plugin.ts +85 -0
- package/src/cli/registry.ts +180 -0
- package/src/cli/router.ts +31 -0
- package/src/cli/ts-verb.ts +126 -0
- package/src/main.ts +223 -0
- package/src/runtime/env.ts +16 -0
- package/src/runtime/eris-assets.d.ts +5 -0
- package/src/runtime/home.ts +306 -0
- package/src/runtime/logging.ts +43 -0
- package/src/runtime/process.ts +388 -0
- package/src/runtime/settings.ts +191 -0
- package/src/sdk.ts +62 -0
- package/testing/build-child.ts +55 -0
- package/testing/fake-runner.ts +23 -0
- package/testing/index.ts +16 -0
- package/ts/lib/eris-fail.ts +180 -0
- package/ts/lib/pool.ts +63 -0
package/docs/YAK.md
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# `eris yak` — land a durable observation with the Yaks endpoint
|
|
2
|
+
|
|
3
|
+
Three verbs an agent (or a human) can invoke inline when the harness fights back —
|
|
4
|
+
a broken tool, a confusing error, a config gap — or could serve them better, and never for the task's own
|
|
5
|
+
outcome. `eris yak` redacts, resolves identity, derives a dedup key, makes one
|
|
6
|
+
bounded HTTP call to a Yaks endpoint, and gets out
|
|
7
|
+
of the way. Everything after the receipt — clustering, issue-tracker writes,
|
|
8
|
+
incident routing — belongs to the endpoint, not to this CLI.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
eris yak issue --summary "…" --body "…"
|
|
12
|
+
eris yak suggest --summary "…" --body-file -
|
|
13
|
+
eris yak fatal --summary "…" --body-file -
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Which verb
|
|
17
|
+
|
|
18
|
+
| Verb | When |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `issue` | A harness/tooling defect — a command that errors, a flag that does the wrong thing, a doc that lies. A suspected defect goes here too, with the hedge in the body |
|
|
21
|
+
| `suggest` | An improvement to the harness — a missing verb or flag, a better default, a skill or rule that should exist, change or go. Nothing has to be broken |
|
|
22
|
+
| `fatal` | The harness itself is broken — the thing you're standing on, not the thing you're building |
|
|
23
|
+
|
|
24
|
+
None of the three is for the task's *own* outcome (a test failure in the code
|
|
25
|
+
you're writing, a bug in the feature you're implementing). They exist for
|
|
26
|
+
problems with the tools, not the work.
|
|
27
|
+
|
|
28
|
+
## Flags
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
eris yak <issue|suggest|fatal>
|
|
32
|
+
--summary <text> required, ≤ 200 chars
|
|
33
|
+
--body <text> | --body-file <path|-> required, ≤ 4000 chars (`-` = stdin)
|
|
34
|
+
--evidence <text> | --evidence-file <path|-> optional, ≤ 20000 chars
|
|
35
|
+
--repro <command> optional, ≤ 2000 chars — see below
|
|
36
|
+
--repo <owner/name> optional, repeatable, ≤ 200 chars each,
|
|
37
|
+
≤ 20 total — see below
|
|
38
|
+
--no-redact skip the secret-scrub pass (loud warning)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`--body-file -` and `--evidence-file -` are mutually exclusive — there is only
|
|
42
|
+
one stdin. `--body-file -` with no pipe fails immediately (`yak-stdin-is-a-tty`)
|
|
43
|
+
rather than hanging: a command that blocks forever mid-task is exactly the
|
|
44
|
+
yak-shave this feature exists to remove.
|
|
45
|
+
|
|
46
|
+
Over-length fields fail locally with the actual/max lengths named, rather than
|
|
47
|
+
letting the server truncate silently — a server-side truncation would desync
|
|
48
|
+
the stored payload from the dedup key this CLI already derived from the full
|
|
49
|
+
text (see [Idempotency](#idempotency-and-replay)).
|
|
50
|
+
|
|
51
|
+
## Setup
|
|
52
|
+
|
|
53
|
+
`eris yak` reads its endpoint URL and bearer token from **one file**,
|
|
54
|
+
`~/.eris/yaks.json`, mode `0600`:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{ "url": "https://yaks.example.com", "token": "…" }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The url is `ERIS_YAKS_URL`, then the file's `url`, then the default
|
|
61
|
+
`https://api.sibylline.group`. Set either only to use another endpoint.
|
|
62
|
+
|
|
63
|
+
### `eris login`: a device code
|
|
64
|
+
|
|
65
|
+
Sign in from any terminal:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
eris login
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
That signs in to `https://api.sibylline.group`. For another endpoint that
|
|
72
|
+
offers device login, pass `--url https://yaks.example.com`.
|
|
73
|
+
|
|
74
|
+
It prints a link and a short code (and opens the link in a browser when stderr
|
|
75
|
+
is a terminal; `--no-browser` skips that). Approve the code there, and the
|
|
76
|
+
token the endpoint mints is written to `~/.eris/yaks.json` with the url beside
|
|
77
|
+
it, so later runs need no `--url`. The file is replaced only after a token
|
|
78
|
+
arrives, via a temp file created `0600` and renamed into place: a denied or
|
|
79
|
+
expired code writes nothing (`login-denied`, `login-expired`, exit 1). Neither
|
|
80
|
+
the device code nor the token is printed; the short code is the one value
|
|
81
|
+
shown, and it is useless without the approval. Without `--url` the url is
|
|
82
|
+
`ERIS_YAKS_URL`, then the file's own `url`, then the default. The CLI follows RFC 8628: it
|
|
83
|
+
polls `/v1/device/token` at the advertised interval and slows down when told to.
|
|
84
|
+
|
|
85
|
+
`eris logout` removes the token from the file and keeps the url. No route lets
|
|
86
|
+
a token revoke itself, so it stays valid until you revoke it from the
|
|
87
|
+
endpoint's dashboard (Settings > API Tokens).
|
|
88
|
+
|
|
89
|
+
### `yak-setup.ts`: a hand-registered bearer
|
|
90
|
+
|
|
91
|
+
For a self-hosted endpoint, mint the file with
|
|
92
|
+
`bun plugins/yak/yak-setup.ts --url <endpoint>` (or with `ERIS_YAKS_URL` set)
|
|
93
|
+
from an eris checkout; it refuses without one, because the default endpoint
|
|
94
|
+
signs machines in with `eris login`. It writes the file and prints the
|
|
95
|
+
follow-up step: the endpoint's operator has to register the bearer against
|
|
96
|
+
this machine's source slug, which the script does not automate. Until both
|
|
97
|
+
halves are done, every yak from this machine warns and exits 0 rather than
|
|
98
|
+
failing your task — see [Exit codes](#exit-codes).
|
|
99
|
+
|
|
100
|
+
`ERIS_YAKS_TOKEN` / `ERIS_YAKS_URL` override the file's `token` / `url` fields
|
|
101
|
+
independently. Nothing else is consulted: a per-source token *is* the ingest
|
|
102
|
+
identity, so it never comes from a file in a repository.
|
|
103
|
+
|
|
104
|
+
The directory `~/.eris` itself is `0755`; the **file's** mode is the guard. A
|
|
105
|
+
group- or world-readable `yaks.json` is refused outright — never parsed, never
|
|
106
|
+
logged — rather than silently trusting a permissive `~/.eris`.
|
|
107
|
+
|
|
108
|
+
## Exit codes
|
|
109
|
+
|
|
110
|
+
The table below describes **text mode** (the default). Under `--output json`
|
|
111
|
+
the diagnostics move entirely onto stdout's record — see below.
|
|
112
|
+
|
|
113
|
+
| Situation | Exit |
|
|
114
|
+
|---|---|
|
|
115
|
+
| Missing `--summary`/`--body`, an unreadable `--body-file`, `--body-file -` with no pipe, an over-length field, an unknown verb or flag, a bare positional word (the text belongs on `--summary`/`--body`) | **`1`** — the four-line `ERROR`/`slug:`/`fix:`/`docs:` block, same contract as every other eris failure |
|
|
116
|
+
| Unresolved token/URL/device-id, an unreachable endpoint, a timeout, a non-2xx response | **`0`** — one `WARN` line + slug on stderr, empty stdout in text mode. A non-2xx status gets its own slug when the cause is on the caller's side (credentials, entitlement, size) and a retry changes nothing — see the rungs below |
|
|
117
|
+
| An incomplete `node_modules` on this machine (`yak-deps-not-installed`) | **`0`** — the same four-line `ERROR`/`slug:`/`fix:`/`docs:` block as the exit-`1` row above, but exit `0`: a broken install on the operator's machine isn't something the calling agent could have done differently |
|
|
118
|
+
| No `bun` on PATH | **`1`** — the shim's own `eris-bun-missing` block, printed before any verb is routed; it is not yak-specific |
|
|
119
|
+
| Accepted (fresh or replay) | **`0`** |
|
|
120
|
+
|
|
121
|
+
The line that decides which bucket a failure falls into: *could the agent have
|
|
122
|
+
done anything differently?* A missing `--summary` is the agent's fault and is
|
|
123
|
+
fixable on the spot. An unset token, an unreachable endpoint, or a machine with no
|
|
124
|
+
network right now is not — failing the task over the operator's local
|
|
125
|
+
environment would be worse than the problem this feature exists to report. So
|
|
126
|
+
**environment, config, and transport problems never fail the task** — they warn
|
|
127
|
+
on stderr and let the calling command continue.
|
|
128
|
+
|
|
129
|
+
### Rungs for a non-2xx response
|
|
130
|
+
|
|
131
|
+
Every non-2xx response is `WARN` + exit `0`. The endpoint's refusals that a
|
|
132
|
+
retry does not fix get their own slug, so the `WARN` line (and `error.slug`
|
|
133
|
+
under `--output json`) names the remedy instead of a generic transport failure:
|
|
134
|
+
|
|
135
|
+
| Status | Slug | Meaning and fix |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `401` | [`yak-unauthorized`](ERRORS.md#yak-unauthorized) | The endpoint rejected the bearer token. Check `~/.eris/yaks.json` / `ERIS_YAKS_TOKEN`, and that the token is registered. |
|
|
138
|
+
| `402` | [`yak-not-entitled`](ERRORS.md#yak-not-entitled) | The workspace is not entitled to Yaks (plan or seat). |
|
|
139
|
+
| `413` | [`yak-payload-too-large`](ERRORS.md#yak-payload-too-large) | The submission exceeded the endpoint's whole-body size cap. Trim `--evidence`. |
|
|
140
|
+
| any other non-2xx | [`yak-transport-error`](ERRORS.md#yak-transport-error) | An outage, a missing route, a proxy page: no local remedy is known. |
|
|
141
|
+
|
|
142
|
+
Each rung's `ERRORS.md` entry notes that a problem with eris itself is reported
|
|
143
|
+
on the vendor route, the eris project's own tracker, rather than into the
|
|
144
|
+
workspace.
|
|
145
|
+
|
|
146
|
+
### `--output json`
|
|
147
|
+
|
|
148
|
+
Under `--output json`, `eris yak <issue|suggest|fatal>` writes **exactly one
|
|
149
|
+
JSON record to stdout and nothing at all to stderr**, on every outcome above —
|
|
150
|
+
misuse, warn, and delivered alike, including the dispatcher-level check
|
|
151
|
+
(`yak-deps-not-installed`, `plugins/yak/handlers.ts`) that never reaches the
|
|
152
|
+
yak core. Exit codes are unchanged from the table above. The one exception is
|
|
153
|
+
`eris-bun-missing`: the shim prints it before bun starts, so no record exists.
|
|
154
|
+
|
|
155
|
+
The record: `{verb, delivered, id?, idempotency_key?, replay?,
|
|
156
|
+
contract_version?, key_mismatch?, redactions: {count, classes, skipped},
|
|
157
|
+
error?}`. **`delivered` is the field to branch on — never `id == null`**. Three fields exist specifically because their text-mode stderr
|
|
158
|
+
equivalent is suppressed under json and would otherwise be silently lost:
|
|
159
|
+
|
|
160
|
+
- **`redactions.skipped`** — `true` when `--no-redact` was set for this
|
|
161
|
+
submission. A skipped scrub and a scrub that ran and found nothing both
|
|
162
|
+
leave `count: 0, classes: []`; without this field the two are
|
|
163
|
+
indistinguishable on the wire. Text mode's `yak-no-redact` WARN carries the
|
|
164
|
+
same fact.
|
|
165
|
+
- **`key_mismatch`** — present (`true`/`false`) only when `delivered: true`.
|
|
166
|
+
Mirrors text mode's `yak-key-mismatch` WARN — the record's own
|
|
167
|
+
`idempotency_key` is the *receipt's* key, never the locally-derived one the
|
|
168
|
+
mismatch is measured against, so this is the only way a machine caller can
|
|
169
|
+
detect canonicalization drift.
|
|
170
|
+
- **`error.slug`/`error.reason`** — the content of the `WARN`/`ERROR` line
|
|
171
|
+
itself, on every non-delivered outcome.
|
|
172
|
+
|
|
173
|
+
What is *not* reproduced in the record: the `fix:`/`docs:` lines from the
|
|
174
|
+
text-mode failure block. Those are reconstructable from `error.slug` — every
|
|
175
|
+
slug is a stable heading in [ERRORS.md](ERRORS.md) — the same way any other
|
|
176
|
+
`--output json` failure's remediation is looked up.
|
|
177
|
+
|
|
178
|
+
See [OUTPUT-CONTRACT.md §3](OUTPUT-CONTRACT.md#3-machine-surfaces).
|
|
179
|
+
|
|
180
|
+
## Redaction
|
|
181
|
+
|
|
182
|
+
`summary`, `body`, `evidence`, `repro`, every `--repo` hint, and the gathered provenance are scrubbed before
|
|
183
|
+
anything else sees them — before the dedup key is derived, before the payload
|
|
184
|
+
is validated, before the request leaves the machine. Each match is replaced
|
|
185
|
+
with `«redacted:<class>»`. In text mode the count and class breakdown print
|
|
186
|
+
to **stderr**, never stdout; under `--output json` the same information is
|
|
187
|
+
`redactions.{count,classes}` on the record instead — stderr carries nothing
|
|
188
|
+
either way (see [Exit codes](#--output-json) above).
|
|
189
|
+
|
|
190
|
+
Classes covered: PEM private-key blocks, JWTs, `sib_` service tokens,
|
|
191
|
+
`sibdc_` device codes (what `eris login` polls with),
|
|
192
|
+
AWS access-key ids and `aws_secret_access_key` assignments,
|
|
193
|
+
GitHub/Slack/Google/`sk-`-style API keys, `Authorization: Bearer|token …`
|
|
194
|
+
headers, GCP service-account `"private_key"` values, connection strings with an
|
|
195
|
+
inline password, and **key-name
|
|
196
|
+
assignments** — `NAME=value` or `NAME: value` where `NAME` matches
|
|
197
|
+
`(SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE_KEY|API_KEY|CREDENTIAL|ACCESS_KEY|CLIENT_SECRET|MNEMONIC|SEED|SESSION|COOKIE|AUTH)`.
|
|
198
|
+
|
|
199
|
+
**Redaction is defence in depth, not a boundary.** It is a pattern scrub, and a
|
|
200
|
+
pattern scrub can always miss a shape it wasn't written for. Do not paste
|
|
201
|
+
material into `eris yak` on the assumption that anything sensitive will be
|
|
202
|
+
caught — the honest framing is "this makes an accidental leak less likely," not
|
|
203
|
+
"this makes a report safe by construction." `--no-redact` exists for the
|
|
204
|
+
(rare, and loudly warned) case where the scrub itself is corrupting a report
|
|
205
|
+
that needs to be read verbatim; it does not exist to work around a
|
|
206
|
+
false-positive scrub eating a legitimate report — if that happens, file it as
|
|
207
|
+
its own `eris yak issue` against the redaction pass.
|
|
208
|
+
|
|
209
|
+
## The wire contract
|
|
210
|
+
|
|
211
|
+
`eris yak` speaks the Yaks submission contract v2, the workspace package
|
|
212
|
+
`@sibylline-labs/yak-contract` ([`packages/yak-contract`](../packages/yak-contract)),
|
|
213
|
+
imported by name rather than vendored as a copy. One call per report:
|
|
214
|
+
|
|
215
|
+
- `POST <url>/v1/yaks` with `Authorization: Bearer <token>`. The ingress
|
|
216
|
+
answers `202` with a receipt `{id, idempotency_key, replay, contract_version}`;
|
|
217
|
+
any `2xx` whose body is a receipt counts as delivered, whatever the
|
|
218
|
+
`contract_version` string says.
|
|
219
|
+
- `route` is always `"workspace"`: the report files into the caller's own
|
|
220
|
+
tenant. It is not an input to the idempotency key.
|
|
221
|
+
- There is no `reporter`. The ingress derives the tracker user from the bearer
|
|
222
|
+
token, so the CLI sends no identity of its own.
|
|
223
|
+
- `schema_version` is the package's `YAKS_CONTRACT_VERSION`. The body is
|
|
224
|
+
validated against the package's `YakSubmission` before the request, so a field
|
|
225
|
+
the contract would refuse never leaves the machine.
|
|
226
|
+
|
|
227
|
+
## Idempotency and replay
|
|
228
|
+
|
|
229
|
+
Every submission derives a dedup key from `{device_id, execution_id, verb,
|
|
230
|
+
summary, body, evidence}` **after** redaction — the endpoint re-derives the same
|
|
231
|
+
key from the payload it actually received, so deriving from anything else
|
|
232
|
+
would guarantee a mismatch on every report. Two calls with the same inputs in
|
|
233
|
+
the same task dedupe to one observation: the second gets back the *same*
|
|
234
|
+
receipt with `replay: true`, rendered as `already recorded: <id>` in text mode
|
|
235
|
+
(a fresh delivery renders `recorded: <id>`) — an agent can tell "this landed"
|
|
236
|
+
from "this already landed" without re-reporting.
|
|
237
|
+
|
|
238
|
+
The contract exposes no server-side mismatch signal on the receipt itself
|
|
239
|
+
(only a server console warning, invisible to the client). The one drift check
|
|
240
|
+
`eris yak` can make is comparing its own locally-derived key against the
|
|
241
|
+
receipt's `idempotency_key` — a mismatch prints a `yak-key-mismatch` warning
|
|
242
|
+
alongside a still-successful delivery. It is a symptom of this CLI's contract
|
|
243
|
+
package (`packages/yak-contract`) drifting from the endpoint's derivation, not
|
|
244
|
+
something a caller needs to act on.
|
|
245
|
+
|
|
246
|
+
## Repro
|
|
247
|
+
|
|
248
|
+
`--repro <command>` attaches a command that reproduces the problem being
|
|
249
|
+
reported — a failing test invocation, a `curl`/`eris` one-liner, a short
|
|
250
|
+
repro script's entry point. It rides on the wire inside `provenance` (as
|
|
251
|
+
`provenance.repro.command`) rather than as a new top-level field on
|
|
252
|
+
`YakSubmission`: that contract is the workspace package
|
|
253
|
+
`@sibylline-labs/yak-contract` ([The wire contract](#the-wire-contract)), and
|
|
254
|
+
not something this CLI can extend on its own — `YakProvenance`'s `.catchall()` exists
|
|
255
|
+
precisely so a CLI-side addition like this one doesn't have to wait on a
|
|
256
|
+
contract bump. It is redacted the same as `evidence` (a repro command can
|
|
257
|
+
itself embed a credential — `curl -H "Authorization: Bearer …"` is a
|
|
258
|
+
realistic shape) and bounded to 2000 chars locally, the same courtesy bound
|
|
259
|
+
`summary`/`body`/`evidence` get (see [`yak-repro-too-long`](ERRORS.md#yak-repro-too-long)).
|
|
260
|
+
It does not participate in the idempotency-key derivation — attaching or
|
|
261
|
+
editing a repro never changes an observation's dedup identity.
|
|
262
|
+
|
|
263
|
+
**That stability has a sharp edge: a repro-only resubmission is a silent
|
|
264
|
+
no-op.** The idempotency key is derived from `{device_id, execution_id, verb,
|
|
265
|
+
summary, body, evidence}` (see [Idempotency and replay](#idempotency-and-replay))
|
|
266
|
+
— `repro` deliberately isn't one of those fields, which is the right call
|
|
267
|
+
(you don't want editing a repro to fork a fresh cluster off the same report).
|
|
268
|
+
But it means resubmitting the *same* `summary`/`body`/`evidence` with a
|
|
269
|
+
*different* (or newly-added) `--repro` derives the *same* key, so the endpoint's
|
|
270
|
+
dedup treats it as a replay of the observation it already has: `delivered:
|
|
271
|
+
true` comes back, but no new `YakObservation` is created and the new repro
|
|
272
|
+
text is never stored or seen server-side. There is currently no supported way
|
|
273
|
+
to attach or change a repro on an existing cluster after its first
|
|
274
|
+
observation — only the repro that rode on whichever observation actually
|
|
275
|
+
created (or first populated) the cluster survives.
|
|
276
|
+
|
|
277
|
+
**Sandboxing — what this CLI guarantees, and what it doesn't.** `eris yak`
|
|
278
|
+
never executes the repro command, on this machine or anywhere else; it is
|
|
279
|
+
opaque text from the moment it's typed to the moment it's POSTed. Nothing in
|
|
280
|
+
this codepath (`plugins/yak/handlers.ts`, `plugins/yak/yak.ts`, `plugins/yak/yak-submit.ts`,
|
|
281
|
+
`plugins/yak/yak-context.ts`) shells out, and the submission carries no
|
|
282
|
+
credential, project id, region, or `eris.yaml` env context — `yak-context.ts`
|
|
283
|
+
explicitly excludes a config merge and any cloud-CLI/keychain read from its
|
|
284
|
+
provenance gather (see that file's own header). So the one thing THIS repo
|
|
285
|
+
could leak into a repro — live production access riding along with the
|
|
286
|
+
command — structurally cannot happen here: there is no production
|
|
287
|
+
credential in `eris yak`'s own process to leak.
|
|
288
|
+
|
|
289
|
+
That is the client-side half of the requirement. **Actually rerunning the
|
|
290
|
+
repro is the Yaks endpoint's job, not this CLI's** — "everything after the receipt —
|
|
291
|
+
clustering, issue-tracker writes, incident routing — belongs to the endpoint"
|
|
292
|
+
(see the top of this doc), and the same split applies to a rerun: only the
|
|
293
|
+
endpoint (or whatever runner it dispatches to) has cluster state and knows when
|
|
294
|
+
a fix lands. That side owns its own sandbox — an execution environment with
|
|
295
|
+
no path to a production credential, project, or deploy target. This CLI
|
|
296
|
+
captures and transmits `provenance.repro.command`, and that is all it does
|
|
297
|
+
with it.
|
|
298
|
+
|
|
299
|
+
## Target-repo hints
|
|
300
|
+
|
|
301
|
+
`--repo <owner/name>` names a repo this report concerns — repeatable, for a
|
|
302
|
+
report that spans more than one. It exists so triage doesn't have to run a
|
|
303
|
+
code search across the whole org before it can even clone a worktree: a Yaks
|
|
304
|
+
report with no target-repo hint anywhere forces a manual `search_code` sweep
|
|
305
|
+
just to find out where the problem lives.
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
eris yak issue --summary "…" --body "…" --repo acme/widgets
|
|
309
|
+
eris yak issue --summary "…" --body "…" \
|
|
310
|
+
--repo acme/widgets --repo acme/widgets-api
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Auto-populated, always overridable.** When no `--repo` is given at all,
|
|
314
|
+
`eris yak` derives one hint from the invoking process's own git remote — the
|
|
315
|
+
same `origin` URL `provenance.git.origin_url` already carries for every
|
|
316
|
+
submission (see [Discoverability](#discoverability) below for what that
|
|
317
|
+
provenance gather already promises: best-effort, never a gate). An agent
|
|
318
|
+
filing a yak from inside a repo checkout gets a hint for free; one filed from
|
|
319
|
+
outside any checkout, or from a repo with no `origin` remote, simply carries
|
|
320
|
+
none. Passing `--repo` at all — even a single one — always wins over the
|
|
321
|
+
derived value; the two are never merged.
|
|
322
|
+
|
|
323
|
+
The derivation understands nested namespaces (a GitLab-style
|
|
324
|
+
`group/subgroup/project` derives in full, never truncated to just
|
|
325
|
+
`subgroup/project`) and refuses outright on a local checkout — a `file://`
|
|
326
|
+
origin or a bare filesystem path has no "owner" at all, and guessing one from
|
|
327
|
+
trailing path components would be a plausible-looking but wrong hint, worse
|
|
328
|
+
than none. An auto-derived hint also clears the same length bound an
|
|
329
|
+
explicit one does; one that wouldn't is dropped rather than sent unbounded.
|
|
330
|
+
|
|
331
|
+
**Not validated against a live repo list.** Like `repro`, a target-repo hint
|
|
332
|
+
is opaque text this CLI carries through, never confirms. A typo'd or
|
|
333
|
+
nonexistent repo name is exactly as harmless (and exactly as un-caught) as a
|
|
334
|
+
typo'd `--repro` command — triage reads it, decides whether it's right, and
|
|
335
|
+
moves on.
|
|
336
|
+
|
|
337
|
+
**On the wire.** Like `repro`, this rides inside `provenance` rather than as
|
|
338
|
+
a new top-level field on `YakSubmission` — the same non-breaking,
|
|
339
|
+
forward-compatible reason (`YakSubmission` is the contract package's, and this
|
|
340
|
+
CLI doesn't extend it). Unlike `repro`, it lands in the
|
|
341
|
+
`provenance.repo` category the CLI already had (`provenance.repo.service`,
|
|
342
|
+
the nearest `eris.yaml`'s declared service — see `plugins/yak/yak-context.ts`), as
|
|
343
|
+
`provenance.repo.target_repos`, always an array even for a single hint. The
|
|
344
|
+
contract types that one key and bounds it (at most 20 hints of at most 200
|
|
345
|
+
chars each), the same limits this CLI checks locally first. It
|
|
346
|
+
does not participate in the idempotency-key derivation, for the same reason
|
|
347
|
+
`repro` doesn't: attaching or changing a target-repo hint should never fork a
|
|
348
|
+
fresh cluster off a report that's otherwise identical, and the same
|
|
349
|
+
repro-only-resubmission caveat in [Idempotency and
|
|
350
|
+
replay](#idempotency-and-replay) applies here too — resubmitting the same
|
|
351
|
+
`summary`/`body`/`evidence` with a different `--repo` dedupes as a replay,
|
|
352
|
+
and the new hint is never stored or seen server-side.
|
|
353
|
+
|
|
354
|
+
**Surfacing it on the rendered issue is the endpoint's work, not this CLI's.**
|
|
355
|
+
Exactly the same split as everything after the receipt (see the top of this
|
|
356
|
+
doc): this CLI captures and transmits `provenance.repo.target_repos`;
|
|
357
|
+
rendering it into the issue belongs to the endpoint's issue-tracker write path.
|
|
358
|
+
|
|
359
|
+
## What a receipt does not promise
|
|
360
|
+
|
|
361
|
+
**A receipt is not a promise that an issue was created.** The endpoint accepts,
|
|
362
|
+
stores, and deduplicates every well-formed submission — but it may also
|
|
363
|
+
enforce an issue-creation ceiling per rolling window, and an observation
|
|
364
|
+
deferred by that ceiling is still accepted and stored: it just doesn't (yet)
|
|
365
|
+
become an issue. Whether `fatal` also pages anyone depends on how the
|
|
366
|
+
endpoint is configured; the submission succeeds either way.
|
|
367
|
+
|
|
368
|
+
In short: a `0` exit and a `recorded: <id>` line mean *the observation is
|
|
369
|
+
durably stored and deduplicated*. They do not mean *a human has been paged* or
|
|
370
|
+
*a ticket now exists*. If you need to confirm the latter, that's a question
|
|
371
|
+
for the Yaks endpoint's own surfaces, not for this CLI's receipt.
|
|
372
|
+
|
|
373
|
+
## Discoverability
|
|
374
|
+
|
|
375
|
+
`eris yak --help` and this doc only help someone who already went looking for
|
|
376
|
+
the verb. Teaching an agent that `eris yak` exists is the agent harness's
|
|
377
|
+
job (a skill, a rule, or the project's agent instructions), not this CLI's.
|
|
378
|
+
|
|
379
|
+
## Not built here
|
|
380
|
+
|
|
381
|
+
`eris yak list` / `get`, an offline queue for a machine with no network,
|
|
382
|
+
automatic/hook-triggered reporting, and any agentic triage of what gets
|
|
383
|
+
reported — all deliberately out of scope for v1. Reading cluster state,
|
|
384
|
+
detecting a fix-mark, dispatching a sandboxed repro rerun, and recording
|
|
385
|
+
pass/fail are server-side work — see [Repro](#repro) above. Rendering the
|
|
386
|
+
`--repo` hint(s) onto the issue is likewise server-side — see [Target-repo
|
|
387
|
+
hints](#target-repo-hints) above.
|
|
388
|
+
|
|
389
|
+
## See also
|
|
390
|
+
|
|
391
|
+
- [OUTPUT-CONTRACT.md](OUTPUT-CONTRACT.md) — the stdout/stderr split, exit
|
|
392
|
+
codes, and the `--output json` machine surface this command participates in
|
|
393
|
+
- [ERRORS.md](ERRORS.md) — every `yak-*` slug's `ERROR`/`fix`/`docs` heading
|
|
394
|
+
- `eris login --help` — sign this machine in with a device code
|
|
395
|
+
- `plugins/yak/yak-setup.ts --help` — mint this machine's bearer
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Adding a command to Eris
|
|
2
|
+
|
|
3
|
+
A new subcommand of an existing group, or a new group. Every new verb is
|
|
4
|
+
TypeScript. Read [DEVELOPMENT.md](DEVELOPMENT.md) first. A group that belongs to
|
|
5
|
+
a product rather than to core is a plugin instead: [PLUGINS.md](PLUGINS.md).
|
|
6
|
+
|
|
7
|
+
## A subcommand of an existing core group
|
|
8
|
+
|
|
9
|
+
`completion` is the reference: its dispatcher is `src/cli/commands/completion.ts`
|
|
10
|
+
and each shell is one file under `src/cli/commands/completion/`. Say you are
|
|
11
|
+
adding `eris completion fish`:
|
|
12
|
+
|
|
13
|
+
1. **Handler.** Create `src/cli/commands/completion/fish.ts`, and route to it
|
|
14
|
+
from the dispatcher's `groupArgs("completion", args)` switch.
|
|
15
|
+
|
|
16
|
+
2. **Register it.** A `"completion fish": { summary, flags, pos, usage }` row in
|
|
17
|
+
`src/cli/registry.ts`. The handler reads its argv with
|
|
18
|
+
`readVerbArgs("completion fish", args, ctx, { help: USAGE })`
|
|
19
|
+
(`src/cli/args.ts`): the result's `flags` and `pos` are typed from the row, so
|
|
20
|
+
a flag the row does not declare can be neither passed nor read. The router
|
|
21
|
+
table, the `--output json` allow-list and the completion tree derive from the
|
|
22
|
+
row with no further edit (`tests/cli-router.test.ts` pins the derived table).
|
|
23
|
+
A positional with a completable domain says so on the same row
|
|
24
|
+
(`complete`, [COMPLETION.md](../COMPLETION.md#how-it-works)).
|
|
25
|
+
|
|
26
|
+
3. **Output.** Payload on stdout, every diagnostic on stderr
|
|
27
|
+
([OUTPUT-CONTRACT.md](../OUTPUT-CONTRACT.md)). A machine rendering means
|
|
28
|
+
`json: true` on the registry row and a line in the refusal's fix text
|
|
29
|
+
(`UNSUPPORTED_FIX_SURFACES`, `src/cli/output.ts`); never a per-command
|
|
30
|
+
`--json`. A machine surface is pinned in `tests/output-contract.test.ts` and
|
|
31
|
+
listed in the contract's surface table.
|
|
32
|
+
|
|
33
|
+
4. **Safety.**
|
|
34
|
+
- A failure is a slugged block (`renderFailureBlock`, `ts/lib/eris-fail.ts`),
|
|
35
|
+
and every slug gets a `docs/ERRORS.md` heading (`tests/errors-doc.test.ts`).
|
|
36
|
+
- A subprocess goes through the runner in `src/runtime/process.ts` with a
|
|
37
|
+
deadline. Nothing is backgrounded; fan-out is bounded.
|
|
38
|
+
- A command a CI job runs completes without an answer.
|
|
39
|
+
|
|
40
|
+
5. **Tests.** A red-first `bun test` file under `tests/`: the handler with
|
|
41
|
+
injected deps for logic, and a black-box run through `tests/helpers/blackbox.ts`
|
|
42
|
+
for the CLI surface ([TESTING.md](TESTING.md)).
|
|
43
|
+
|
|
44
|
+
6. **Verify and document.**
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bunx tsc --noEmit && bun test ./tests
|
|
48
|
+
./eris completion fish # run it and read its real output
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Update the group's doc under `docs/`.
|
|
52
|
+
|
|
53
|
+
## A new core group
|
|
54
|
+
|
|
55
|
+
1. **Dispatcher** `src/cli/commands/<group>.ts`: the group usage, the sub switch
|
|
56
|
+
over `groupArgs("<group>", args)` (`bare` and `help` print usage on stdout and
|
|
57
|
+
exit 0), and an `Unknown <group> command: <x>` failure. One file per sub at
|
|
58
|
+
`src/cli/commands/<group>/<sub>.ts`, each reading its argv with `readVerbArgs`.
|
|
59
|
+
|
|
60
|
+
2. **Register it.**
|
|
61
|
+
- A `<group>: { impl: "ts", summary }` row and one row per sub in
|
|
62
|
+
`src/cli/registry.ts`.
|
|
63
|
+
- A lazily imported entry in `HANDLERS` (`src/main.ts`);
|
|
64
|
+
`tests/cli-registry.test.ts` fails for a `ts` group without one.
|
|
65
|
+
- A line in `COMMANDS` (`src/cli/commands/usage.ts`), the top-level help. Its
|
|
66
|
+
`--output` paragraph is derived from the json rows.
|
|
67
|
+
|
|
68
|
+
3. **Tests and docs.** Steps 3–6 above apply per sub. Add `docs/<GROUP>.md` and an
|
|
69
|
+
entry in [docs/README.md](../README.md).
|
|
70
|
+
|
|
71
|
+
## Checklist
|
|
72
|
+
|
|
73
|
+
- [ ] Registry rows (group and every sub, flags exact) and a `HANDLERS` entry
|
|
74
|
+
- [ ] Lazy imports: `--help` works anywhere, with nothing configured
|
|
75
|
+
- [ ] stdout carries only the payload; every failure is a slugged block with an `ERRORS.md` heading and nothing on stdout
|
|
76
|
+
- [ ] A non-prompting path if CI-facing
|
|
77
|
+
- [ ] Nothing backgrounded; any fan-out bounded
|
|
78
|
+
- [ ] Red-first `bun test` coverage at the CLI seam; no network
|
|
79
|
+
- [ ] `bunx tsc --noEmit` and `bun test ./tests` green
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Developing Eris
|
|
2
|
+
|
|
3
|
+
How the codebase is put together: the entrypoint, the router, the layers under
|
|
4
|
+
it, and the conventions every change follows.
|
|
5
|
+
|
|
6
|
+
Companion guides: [TESTING.md](TESTING.md) (the `bun test` suite),
|
|
7
|
+
[ADDING-A-COMMAND.md](ADDING-A-COMMAND.md) (a new subcommand or group, end to end)
|
|
8
|
+
[PLUGINS.md](PLUGINS.md) (a verb group as a build-time plugin) and
|
|
9
|
+
[RELEASING.md](RELEASING.md) (cutting a release: the tag, the workflow, npm).
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git clone https://github.com/sibylline-labs/eris && cd eris
|
|
15
|
+
bun install --frozen-lockfile
|
|
16
|
+
./eris --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`./eris` runs the working tree as it stands; put the checkout on your `PATH` to call it as
|
|
20
|
+
`eris`. There is no build step for the checkout. The gates:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bunx tsc --noEmit
|
|
24
|
+
bun test ./tests
|
|
25
|
+
ast-grep scan # rules/ over eris's own source
|
|
26
|
+
ast-grep test --skip-snapshot-tests # the rules' own cases, rule-tests/
|
|
27
|
+
shellcheck -s sh -S warning eris
|
|
28
|
+
bun scripts/build.ts && ERIS_BIN=dist/eris-<os>-<arch> bun test ./tests
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The last line is the binary oracle: it compiles the binary and runs the same
|
|
32
|
+
suite against it ([TESTING.md](TESTING.md#the-binary-oracle)).
|
|
33
|
+
|
|
34
|
+
## The entrypoint and the router
|
|
35
|
+
|
|
36
|
+
- **`eris`** is a POSIX `sh` shim. It finds bun, removes
|
|
37
|
+
`BUN_CONFIG_VERBOSE_FETCH`, and execs `bun --no-env-file --config=/dev/null
|
|
38
|
+
src/main.ts`. Its header lists its invariants.
|
|
39
|
+
- **`bin/eris.ts`** is the same entry for an installed package (`bin` in `package.json`): a
|
|
40
|
+
`#!/usr/bin/env -S bun --no-env-file --config=/dev/null` shebang that refuses to run under
|
|
41
|
+
`BUN_CONFIG_VERBOSE_FETCH` and then calls `runProgram`. Run it as `bun bin/eris.ts` or directly
|
|
42
|
+
(`./bin/eris.ts`) to see what an `npx`/`bunx` user sees.
|
|
43
|
+
- **`src/cli/registry.ts`** names core's verbs: one `REGISTRY` row per group, sub
|
|
44
|
+
and nested sub, carrying `impl` (`ts`), long flags with value arity, `json`
|
|
45
|
+
(has a machine rendering) and a summary.
|
|
46
|
+
**`src/cli/compose.ts`** merges them with the build's plugins
|
|
47
|
+
(`plugins/index.ts`). The router table, the `--output json` allow-list, the
|
|
48
|
+
shell completion tree and the top-level usage are derived from the composed rows.
|
|
49
|
+
- **`src/main.ts`** routes each command line. A `ts` verb runs in-process behind
|
|
50
|
+
the global `--output` pre-pass; its handler is a lazily imported entry in
|
|
51
|
+
`HANDLERS` (core's, or a plugin's). Anything else is refused as an unknown
|
|
52
|
+
command.
|
|
53
|
+
|
|
54
|
+
## Where things live
|
|
55
|
+
|
|
56
|
+
| Path | Holds |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `bin/` | `eris.ts`, the package's `eris` command ([ARCHITECTURE.md](../ARCHITECTURE.md#the-packages-bin)) |
|
|
59
|
+
| `src/cli/` | The registry, composition, the plugin SDK, the router, argv parsing, the output allow-list, the TS route |
|
|
60
|
+
| `src/cli/commands/` | Core's verbs: `completion` and the top-level usage |
|
|
61
|
+
| `src/runtime/` | Logging, the subprocess runner, settings, env reads, the install root |
|
|
62
|
+
| `ts/lib/eris-fail.ts` | The failure block and the WARN lines |
|
|
63
|
+
| `plugins/` | The plugins this build composes (`plugins/index.ts`): `plugins/yak/` is `eris yak` |
|
|
64
|
+
| `packages/` | Bun workspace packages (root `package.json` `workspaces`): `packages/yak-contract/` is the Yaks wire contract v2, which the root `exports` map also names as `eris/yak-contract`. A package imports only itself and what its own manifest declares, and `tests/yak-contract-v2.test.ts` holds that |
|
|
65
|
+
|
|
66
|
+
## Conventions
|
|
67
|
+
|
|
68
|
+
**The output contract binds every command** ([OUTPUT-CONTRACT.md](../OUTPUT-CONTRACT.md)).
|
|
69
|
+
stdout carries the payload and nothing else; stderr carries every diagnostic;
|
|
70
|
+
`--output json` is global and a command with no JSON rendering refuses it
|
|
71
|
+
(`output-json-unsupported`). Never add a per-command output-format flag.
|
|
72
|
+
|
|
73
|
+
**Failures are slugged.** A failure writes the block from `renderFailureBlock`
|
|
74
|
+
(`ts/lib/eris-fail.ts`) to stderr and nothing to stdout. Every slug has a heading
|
|
75
|
+
in [ERRORS.md](../ERRORS.md); `tests/errors-doc.test.ts` harvests slugs from the
|
|
76
|
+
source and fails on one without.
|
|
77
|
+
|
|
78
|
+
**Argv is read from the row.** A handler reads its flags and operands through the
|
|
79
|
+
registry-driven parser (`readVerbArgs` in core, `readArgs` from the SDK in a
|
|
80
|
+
plugin), never from argv by hand; the `adhoc-argv-parse` rule holds it.
|
|
81
|
+
|
|
82
|
+
**Nothing is backgrounded to render progress, and fan-out is bounded.** A leaked
|
|
83
|
+
descendant holding a capture pipe makes the reader wait forever. Subprocesses go
|
|
84
|
+
through the runner in `src/runtime/process.ts` with a deadline; concurrency is
|
|
85
|
+
capped by `ERIS_BATCH_CONCURRENCY` (`ts/lib/pool.ts`).
|
|
86
|
+
|
|
87
|
+
**Every bun eris launches carries `--no-env-file --config=/dev/null`**, so a
|
|
88
|
+
directory's `.env` and `bunfig.toml` never load.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Source Layout
|
|
2
|
+
|
|
3
|
+
The annotated tree of the eris repo. Read the part for the area you are editing.
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
eris # POSIX sh shim: unsets BUN_CONFIG_VERBOSE_FETCH, execs bun src/main.ts
|
|
9
|
+
bin/eris.ts # the package's `eris` command (package.json `bin`, for an npm or bun install): an
|
|
10
|
+
# `env -S bun --no-env-file --config=/dev/null` shebang, refuses BUN_CONFIG_VERBOSE_FETCH
|
|
11
|
+
# (eris-verbose-fetch-set), then runs src/main.ts's runProgram; tests/bin.test.ts
|
|
12
|
+
package.json # @sibylline-labs/eris: `bin`, `exports`, and `files`, the allow-list of what the npm
|
|
13
|
+
# tarball holds (tests/package.test.ts reads `npm pack --dry-run`)
|
|
14
|
+
src/ # The TS program the shim and the bin run
|
|
15
|
+
├── main.ts # the entry: composes core with the build's plugins (CLI), HANDLERS, main, runProgram
|
|
16
|
+
├── sdk.ts # `eris/sdk`, the one module through which another repository reaches core (package.json exports):
|
|
17
|
+
│ # the plugin SDK, composition, runProgram, the runtime, build()
|
|
18
|
+
├── build.ts # build(): compiles a distribution's entry into one binary (eris:assets, stubs,
|
|
19
|
+
│ # the build id and release repository); scripts/build.ts calls it for this repo
|
|
20
|
+
├── cli/registry.ts # core's verbs (subs, flags, impl, json); the derivations
|
|
21
|
+
│ # (router table, json allow-list) every plane reads
|
|
22
|
+
├── cli/compose.ts # core rows + plugin rows → table, allow-list, handlers, json fix text;
|
|
23
|
+
│ # collision and completion-slot refusals; the menu option; entitlement locks
|
|
24
|
+
├── cli/plugin.ts # the plugin SDK: the one core module a plugin imports
|
|
25
|
+
├── cli/router.ts # the per-verb route, from the derived table
|
|
26
|
+
├── cli/args.ts # the registry-driven argv parser and the global --output pre-pass
|
|
27
|
+
├── cli/output.ts # the --output json allow-list and its refusal block
|
|
28
|
+
├── cli/ts-verb.ts # runTsVerb: pre-pass → allow-list → lock → handler; eris-deps-not-installed
|
|
29
|
+
├── cli/commands/ # completion.ts + completion/ (emit.ts generates the bash/zsh script from the
|
|
30
|
+
│ # composed registry; it imports the registry only, because every shell start
|
|
31
|
+
│ # runs it; domain.ts checks a row's completion domains), usage.ts (the
|
|
32
|
+
│ # top-level usage: bare eris, -h, --help), version.ts (--version),
|
|
33
|
+
│ # update.ts (eris update: prints the npm commands), update/ (RELEASE_UPDATE,
|
|
34
|
+
│ # the in-place verb a distribution opts into: opt-in.ts the row and thunk,
|
|
35
|
+
│ # release.ts the verb, github.ts the releases API and the download,
|
|
36
|
+
│ # replace.ts the staged rename and its restore, semver.ts)
|
|
37
|
+
└── runtime/ # logging, process (the one spawn adapter), settings (~/.eris/settings.json),
|
|
38
|
+
# env (readEnv), home (the install root: the checkout, a distribution's own root
|
|
39
|
+
# when its entry names one, or a compiled binary's
|
|
40
|
+
# per-build asset home)
|
|
41
|
+
ts/lib/eris-fail.ts # the ERROR/slug/fix/docs failure block, and the WARN lines
|
|
42
|
+
ts/lib/pool.ts # the bounded-concurrency runner process.ts's runMany uses
|
|
43
|
+
plugins/index.ts # the plugins this build compiles in, composed by src/main.ts (docs/dev/PLUGINS.md)
|
|
44
|
+
plugins/yak/ # `eris yak` as a plugin: index.ts (the manifest: rows + handler thunks), handlers.ts
|
|
45
|
+
# (the dispatcher), yak.ts (the core), yak-{identity,context,redact,submit}.ts
|
|
46
|
+
# (yak-redact.ts imports nothing; package.json exports it as `eris/yak-redact`),
|
|
47
|
+
# login.ts (`eris login`/`logout`, the device-code client), yak-setup.ts (mints the bearer; docs/YAK.md); the wire contract is packages/yak-contract, imported by name
|
|
48
|
+
packages/yak-contract/ # the Yaks wire contract v2, a bun workspace package that builds standalone;
|
|
49
|
+
# `eris/yak-contract` (package.json exports) is its src/index.ts.
|
|
50
|
+
# src/v2.ts (Zod + inferred types), scripts/emit-json-schema.ts →
|
|
51
|
+
# schema/yak-v2.schema.json (committed; `bun run emit-schema` in the package re-emits)
|
|
52
|
+
skills/eris-yaks/ # SKILL.md: the agent skill that sets up Yaks (`npx skills add sibylline-labs/eris
|
|
53
|
+
# --skill eris-yaks`); the skills CLI discovers skills/<name>/SKILL.md
|
|
54
|
+
examples/plugins/widgets/ # the worked plugin example tests/plugins.test.ts composes
|
|
55
|
+
examples/distribution/ # the worked distribution: an entry importing eris/sdk alone, a demo plugin with a
|
|
56
|
+
# completion domain, a menu, and build.ts; tests/distribution.test.ts builds and runs it
|
|
57
|
+
testing/ # `eris/sdk/testing` (package.json exports): the test helpers a distribution's suites
|
|
58
|
+
# reuse. fake-runner.ts (createFakeRunner), build-child.ts (buildInChild: build() in a
|
|
59
|
+
# child bun, from a module exporting BUILD). Outside src/, so no binary carries it
|
|
60
|
+
scripts/build.ts # `bun scripts/build.ts` → dist/eris-<os>-<arch> (gitignored): this repo's binary,
|
|
61
|
+
# through src/build.ts; exports its options as BUILD
|
|
62
|
+
.github/workflows/ # ci.yml (the PR gates, the npm tarball's install-and-run included) · release.yml (tag → build, oracle, publish, npm)
|
|
63
|
+
sgconfig.yml # the code-quality rules over eris's OWN source (ruleDirs: rules; cases in rule-tests/):
|
|
64
|
+
rules/ # adhoc-argv-parse (one argv parser), env-read-own-property(-plugin) (env names
|
|
65
|
+
rule-tests/ # are read through readEnv), plugin-manifest-lazy-handlers + plugin-imports-sdk-only
|
|
66
|
+
# (the plugin contract), distribution-imports-sdk-only (eris/sdk alone),
|
|
67
|
+
# runtime-imports-no-testing (the program never imports testing/),
|
|
68
|
+
# regex-no-lookbehind (the pinned bun interprets a lookbehind pattern)
|
|
69
|
+
tests/ # `bun test ./tests` — the whole suite. helpers/blackbox.ts drives the real
|
|
70
|
+
# `./eris` in a physical per-test sandbox (PATH fakes, hideReal); helpers/plugins/
|
|
71
|
+
# holds a tripwire plugin; helpers/release-fixture.ts is GitHub's releases API
|
|
72
|
+
# on loopback, for eris update
|
|
73
|
+
docs/ # ARCHITECTURE.md, OUTPUT-CONTRACT.md, ERRORS.md, the verb docs, dev/
|
|
74
|
+
```
|