@sibylline-labs/eris 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +146 -2
  4. package/bin/eris.ts +42 -0
  5. package/docs/ARCHITECTURE.md +188 -0
  6. package/docs/COMPLETION.md +106 -0
  7. package/docs/ERRORS.md +632 -0
  8. package/docs/OUTPUT-CONTRACT.md +219 -0
  9. package/docs/README.md +30 -0
  10. package/docs/UPDATE.md +159 -0
  11. package/docs/YAK.md +395 -0
  12. package/docs/dev/ADDING-A-COMMAND.md +79 -0
  13. package/docs/dev/DEVELOPMENT.md +88 -0
  14. package/docs/dev/LAYOUT.md +74 -0
  15. package/docs/dev/PLUGINS.md +382 -0
  16. package/docs/dev/RELEASING.md +157 -0
  17. package/docs/dev/TESTING.md +84 -0
  18. package/docs/dev/surfaces/secrets.md +95 -0
  19. package/package.json +55 -4
  20. package/packages/yak-contract/package.json +28 -0
  21. package/packages/yak-contract/schema/yak-v2.schema.json +416 -0
  22. package/packages/yak-contract/src/index.ts +1 -0
  23. package/packages/yak-contract/src/v2.ts +375 -0
  24. package/plugins/index.ts +12 -0
  25. package/plugins/yak/handlers.ts +128 -0
  26. package/plugins/yak/index.ts +51 -0
  27. package/plugins/yak/login.ts +300 -0
  28. package/plugins/yak/yak-context.ts +280 -0
  29. package/plugins/yak/yak-identity.ts +399 -0
  30. package/plugins/yak/yak-redact.ts +686 -0
  31. package/plugins/yak/yak-setup.ts +133 -0
  32. package/plugins/yak/yak-submit.ts +1166 -0
  33. package/plugins/yak/yak.ts +485 -0
  34. package/src/build.ts +248 -0
  35. package/src/cli/args.ts +434 -0
  36. package/src/cli/commands/completion/bash.ts +24 -0
  37. package/src/cli/commands/completion/domain.ts +203 -0
  38. package/src/cli/commands/completion/emit.ts +246 -0
  39. package/src/cli/commands/completion/shell.ts +24 -0
  40. package/src/cli/commands/completion/zsh.ts +24 -0
  41. package/src/cli/commands/completion.ts +48 -0
  42. package/src/cli/commands/update/github.ts +328 -0
  43. package/src/cli/commands/update/opt-in.ts +32 -0
  44. package/src/cli/commands/update/refusal.ts +21 -0
  45. package/src/cli/commands/update/release.ts +351 -0
  46. package/src/cli/commands/update/replace.ts +143 -0
  47. package/src/cli/commands/update/semver.ts +48 -0
  48. package/src/cli/commands/update.ts +47 -0
  49. package/src/cli/commands/usage.ts +68 -0
  50. package/src/cli/commands/version.ts +29 -0
  51. package/src/cli/compose.ts +273 -0
  52. package/src/cli/output.ts +81 -0
  53. package/src/cli/plugin.ts +85 -0
  54. package/src/cli/registry.ts +180 -0
  55. package/src/cli/router.ts +31 -0
  56. package/src/cli/ts-verb.ts +126 -0
  57. package/src/main.ts +223 -0
  58. package/src/runtime/env.ts +16 -0
  59. package/src/runtime/eris-assets.d.ts +5 -0
  60. package/src/runtime/home.ts +306 -0
  61. package/src/runtime/logging.ts +43 -0
  62. package/src/runtime/process.ts +388 -0
  63. package/src/runtime/settings.ts +191 -0
  64. package/src/sdk.ts +62 -0
  65. package/testing/build-child.ts +55 -0
  66. package/testing/fake-runner.ts +23 -0
  67. package/testing/index.ts +16 -0
  68. package/ts/lib/eris-fail.ts +180 -0
  69. package/ts/lib/pool.ts +63 -0
@@ -0,0 +1,95 @@
1
+ # Secrets surface: pattern-class vocabulary
2
+
3
+ Repo-local addendum for the `secrets` surface registered in `.claude/surfaces.yml`:
4
+ the yak redaction pass (`plugins/yak/yak-redact.ts`), its call site
5
+ (`plugins/yak/yak-submit.ts`) and its tests (`tests/yak-redact.test.ts`). Format
6
+ per the universal class core; class IDs use the prefix `S`.
7
+
8
+ Seeded by hand from failure modes the pass's own header and tests already record,
9
+ not from an audit run. Extend it when an audit of this surface names a new class.
10
+
11
+ Tests are cited by a substring of their title, so `rg -F '<substring>' tests/yak-redact.test.ts`
12
+ finds them.
13
+
14
+ ## S1: Pass-ordering shadowing
15
+
16
+ **Shape:** a class whose pattern can match a run *inside* another class's token runs
17
+ before that token's own class, so the inner class replaces only the embedded run.
18
+ Triggered by adding a pass, or moving one, in the fixed pipeline order.
19
+
20
+ **Why it kills:** the token's head survives in plaintext next to a `«redacted:…»`
21
+ marker, so the output looks handled while a live credential prefix ships. A token's
22
+ secret is base64url, so it can contain `-sk-`, `ghp_` and other shapes by chance.
23
+
24
+ **Concrete fingerprint:** a `sib_` secret containing an `sk-key` run comes out as
25
+ `sib_<orgId>_<head>-«redacted:sk-key»` instead of one `«redacted:sib-token»`. The
26
+ mirror case: an earlier pass whose run is wider than its token swallows the anchor of
27
+ text glued after it (`<token>Authorization: Basic <cred>` gives
28
+ `«redacted:sib-token»: Basic <cred>`), so the later pass never fires.
29
+
30
+ **Typical severity:** High. Critical when the surviving head carries the org id and a
31
+ usable secret prefix.
32
+
33
+ **Check mode:** hybrid. Deterministic for known instances: `embeds another class's shape`
34
+ (the `sib-token` over `sk-key` and `github-token` case) and
35
+ `classified once as sib-token, not as assignment` (a broader `assignment` claiming first),
36
+ and `the glued-on text is left to the later passes` (the mirror case).
37
+ LLM for a new or moved pass: could its pattern match inside another class's token alphabet?
38
+ A new pass needs a test where a longer token embeds its shape.
39
+
40
+ ## S2: Partial-match plaintext tail
41
+
42
+ **Shape:** a bounded value capture (`{1,N}`, a fixed-length token body, an anchored
43
+ length) shorter than the real secret, so the match ends mid-secret.
44
+
45
+ **Why it kills:** the first N characters become the marker and the remainder follows it
46
+ in plaintext. That is worse than no redaction because the output looks handled. The
47
+ bound is an input property the author does not control: the evidence field runs to
48
+ 20,000 chars, and issuers lengthen secrets. Widening is right only where the issuer's
49
+ length is not fixed: `sib_` secrets are exactly 43 characters, so `SIB_TOKEN_RE` pins 43
50
+ and leaves anything glued on to the later passes (S1).
51
+
52
+ **Concrete fingerprint:** `«redacted:assignment»` directly followed by the rest of a
53
+ secret. A later pass that would have consumed that tail can hide it again by declining the run
54
+ because it starts with a marker (S3): `Authorization: Bearer «redacted:sk-key»<tail>`.
55
+
56
+ **Typical severity:** High. Critical when the tail is most of the secret.
57
+
58
+ **Check mode:** deterministic. `no plaintext run of the secret survives, even at 10,000 chars`,
59
+ and `fully consumes a long JSON-quoted value` assert that no
60
+ slice of the secret survives past the old bound. Its counterweight is
61
+ `does not swallow the closing brace or a following key`: widening a capture must not eat
62
+ the next JSON key. `runs on past an earlier class's marker` (auth-header and assignment) and
63
+ `is an earlier class's marker, alone or with a tail` (conn-string) assert that a later pass
64
+ still consumes a tail left after a marker.
65
+
66
+ ## S3: Marker re-match and double count
67
+
68
+ **Shape:** a later pass treats an earlier pass's `«redacted:<class>»` marker as its own
69
+ value, typically a quoted value under a governed name such as `"api_key": "«redacted:sk-key»"`.
70
+ The fix is a guard in a pass that takes an arbitrary run as its value. A pass may decline
71
+ only where declining leaves nothing but the marker and text that cannot be secret.
72
+ `assignment` declines a value that is exactly one marker, bare or alone in quotes.
73
+ `auth-header` declines an exact marker after a known scheme word. `conn-string` never
74
+ declines: its scheme and host runs take whatever is pasted against them. A guard that
75
+ declines any value that merely starts with a marker reintroduces S2, because whatever
76
+ follows the marker in that run ships in plaintext. Redacting that run whole has its own
77
+ failure: with no whitespace in between, the run swallows the name of the next
78
+ assignment, and that assignment's value ships. `assignment` therefore redacts both
79
+ readings. Prefer over-redaction and a double count to either leak.
80
+
81
+ **Why it kills:** no plaintext leaks, but the marker is reclassified (here as the generic
82
+ `assignment`) and counted twice. The class counts in the stderr summary line become wrong,
83
+ and `redact()` stops being idempotent, so redacting already-redacted text changes it.
84
+
85
+ **Concrete fingerprint:** a secret redacted as the generic `assignment` instead of its own
86
+ class (`"api_key": "«redacted:sk-key»"` comes back as `«redacted:assignment»`), or a second
87
+ `redact()` call that reports a nonzero total.
88
+
89
+ **Typical severity:** Medium. Low when only the summary line is affected.
90
+
91
+ **Check mode:** deterministic. `the marker itself is never re-matched` (every class's
92
+ marker, in each position a guard covers), `redacting twice equals redacting once`, and
93
+ `not re-redacted and reclassified as 'assignment'` pin the decline. The S2 tests named
94
+ `runs on past an earlier class's marker`, `is an earlier class's marker, alone or with a tail`
95
+ and `runs straight into another assignment` pin its limits.
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "@sibylline-labs/eris",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.0",
4
+ "description": "A plugin-composed command-line tool in TypeScript on Bun, shipping eris yak",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/sibylline-labs/eris.git"
9
+ },
10
+ "type": "module",
11
+ "engines": {
12
+ "bun": ">=1.4.4"
13
+ },
14
+ "bin": {
15
+ "eris": "bin/eris.ts"
16
+ },
17
+ "exports": {
18
+ ".": "./src/main.ts",
19
+ "./sdk": "./src/sdk.ts",
20
+ "./sdk/testing": "./testing/index.ts",
21
+ "./yak-contract": "./packages/yak-contract/src/index.ts",
22
+ "./yak-redact": "./plugins/yak/yak-redact.ts"
23
+ },
24
+ "files": [
25
+ "bin",
26
+ "src",
27
+ "plugins",
28
+ "packages/yak-contract/src",
29
+ "packages/yak-contract/schema",
30
+ "packages/yak-contract/package.json",
31
+ "testing",
32
+ "ts",
33
+ "docs",
34
+ "LICENSE",
35
+ "NOTICE",
36
+ "README.md"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "packageManager": "bun@1.4.4",
42
+ "workspaces": [
43
+ "packages/*"
44
+ ],
45
+ "scripts": {
46
+ "typecheck": "bunx tsc --noEmit",
47
+ "test": "bun test"
48
+ },
49
+ "dependencies": {
50
+ "zod": "^3.23.0"
51
+ },
52
+ "devDependencies": {
53
+ "ajv": "8.20.0",
54
+ "bun-types": "1.4.4",
55
+ "typescript": "^6.0.3"
56
+ }
57
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@sibylline-labs/yak-contract",
3
+ "version": "0.1.0",
4
+ "description": "The Eris Yaks wire contract, v2: Zod schemas, inferred types and the JSON Schema they emit",
5
+ "license": "Apache-2.0",
6
+ "private": false,
7
+ "type": "module",
8
+ "exports": {
9
+ ".": "./src/index.ts",
10
+ "./schema": "./schema/yak-v2.schema.json"
11
+ },
12
+ "files": [
13
+ "src",
14
+ "schema"
15
+ ],
16
+ "scripts": {
17
+ "emit-schema": "bun scripts/emit-json-schema.ts",
18
+ "typecheck": "tsc --noEmit -p ."
19
+ },
20
+ "dependencies": {
21
+ "zod": "^3.23.0"
22
+ },
23
+ "devDependencies": {
24
+ "bun-types": "1.4.4",
25
+ "typescript": "^6.0.3",
26
+ "zod-to-json-schema": "3.23.5"
27
+ }
28
+ }
@@ -0,0 +1,416 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Eris Yaks wire contract v2",
4
+ "$ref": "#/definitions/YakSubmission",
5
+ "definitions": {
6
+ "YakSubmission": {
7
+ "type": "object",
8
+ "properties": {
9
+ "schema_version": {
10
+ "type": "string",
11
+ "enum": [
12
+ "2.0.0"
13
+ ]
14
+ },
15
+ "verb": {
16
+ "type": "string",
17
+ "enum": [
18
+ "issue",
19
+ "suggest",
20
+ "fatal"
21
+ ]
22
+ },
23
+ "route": {
24
+ "type": "string",
25
+ "enum": [
26
+ "workspace",
27
+ "vendor"
28
+ ]
29
+ },
30
+ "summary": {
31
+ "type": "string",
32
+ "minLength": 1,
33
+ "maxLength": 200
34
+ },
35
+ "body": {
36
+ "type": "string",
37
+ "minLength": 1,
38
+ "maxLength": 4000
39
+ },
40
+ "evidence": {
41
+ "type": "string",
42
+ "maxLength": 20000
43
+ },
44
+ "device_id": {
45
+ "type": "string",
46
+ "minLength": 1,
47
+ "maxLength": 200
48
+ },
49
+ "execution_id": {
50
+ "type": "string",
51
+ "minLength": 1,
52
+ "maxLength": 200
53
+ },
54
+ "idempotency_key": {
55
+ "type": "string",
56
+ "maxLength": 128
57
+ },
58
+ "provenance": {
59
+ "type": "object",
60
+ "properties": {
61
+ "process": {
62
+ "type": "object",
63
+ "additionalProperties": {}
64
+ },
65
+ "device": {
66
+ "type": "object",
67
+ "additionalProperties": {}
68
+ },
69
+ "harness": {
70
+ "type": "object",
71
+ "additionalProperties": {}
72
+ },
73
+ "repo": {
74
+ "type": "object",
75
+ "properties": {
76
+ "target_repos": {
77
+ "type": "array",
78
+ "items": {
79
+ "type": "string",
80
+ "minLength": 1,
81
+ "maxLength": 200
82
+ },
83
+ "maxItems": 20
84
+ }
85
+ },
86
+ "additionalProperties": {}
87
+ },
88
+ "git": {
89
+ "type": "object",
90
+ "additionalProperties": {}
91
+ },
92
+ "execution": {
93
+ "type": "object",
94
+ "properties": {
95
+ "rung": {
96
+ "type": "string",
97
+ "enum": [
98
+ "override",
99
+ "session",
100
+ "host-session",
101
+ "shell-session",
102
+ "shell",
103
+ "ai-agent",
104
+ "codex",
105
+ "cursor"
106
+ ]
107
+ }
108
+ },
109
+ "additionalProperties": {}
110
+ }
111
+ },
112
+ "additionalProperties": {}
113
+ }
114
+ },
115
+ "required": [
116
+ "schema_version",
117
+ "verb",
118
+ "route",
119
+ "summary",
120
+ "body",
121
+ "device_id",
122
+ "execution_id"
123
+ ],
124
+ "additionalProperties": true
125
+ },
126
+ "YakReceipt": {
127
+ "type": "object",
128
+ "properties": {
129
+ "id": {
130
+ "type": "string"
131
+ },
132
+ "idempotency_key": {
133
+ "type": "string"
134
+ },
135
+ "replay": {
136
+ "type": "boolean"
137
+ },
138
+ "contract_version": {
139
+ "type": "string",
140
+ "const": "2.0.0"
141
+ }
142
+ },
143
+ "required": [
144
+ "id",
145
+ "idempotency_key",
146
+ "replay",
147
+ "contract_version"
148
+ ],
149
+ "additionalProperties": true
150
+ },
151
+ "YakObservation": {
152
+ "type": "object",
153
+ "properties": {
154
+ "id": {
155
+ "type": "string"
156
+ },
157
+ "idempotency_key": {
158
+ "type": "string"
159
+ },
160
+ "verb": {
161
+ "type": "string",
162
+ "enum": [
163
+ "issue",
164
+ "suggest",
165
+ "fatal"
166
+ ]
167
+ },
168
+ "summary": {
169
+ "type": "string"
170
+ },
171
+ "source": {
172
+ "type": "string"
173
+ },
174
+ "received_at": {
175
+ "type": "string",
176
+ "format": "date-time"
177
+ },
178
+ "status": {
179
+ "type": "string",
180
+ "enum": [
181
+ "pending",
182
+ "processed",
183
+ "failed"
184
+ ]
185
+ },
186
+ "cluster_id": {
187
+ "type": "string"
188
+ },
189
+ "attempts": {
190
+ "type": "integer",
191
+ "minimum": 0
192
+ },
193
+ "next_attempt_at": {
194
+ "type": "string",
195
+ "format": "date-time"
196
+ },
197
+ "last_error": {
198
+ "type": "string"
199
+ },
200
+ "data": {
201
+ "type": "object",
202
+ "properties": {
203
+ "schema_version": {
204
+ "type": "string",
205
+ "enum": [
206
+ "2.0.0"
207
+ ]
208
+ },
209
+ "verb": {
210
+ "type": "string",
211
+ "enum": [
212
+ "issue",
213
+ "suggest",
214
+ "fatal"
215
+ ]
216
+ },
217
+ "route": {
218
+ "type": "string",
219
+ "enum": [
220
+ "workspace",
221
+ "vendor"
222
+ ]
223
+ },
224
+ "summary": {
225
+ "type": "string",
226
+ "minLength": 1,
227
+ "maxLength": 200
228
+ },
229
+ "body": {
230
+ "type": "string",
231
+ "minLength": 1,
232
+ "maxLength": 4000
233
+ },
234
+ "evidence": {
235
+ "type": "string",
236
+ "maxLength": 20000
237
+ },
238
+ "device_id": {
239
+ "type": "string",
240
+ "minLength": 1,
241
+ "maxLength": 200
242
+ },
243
+ "execution_id": {
244
+ "type": "string",
245
+ "minLength": 1,
246
+ "maxLength": 200
247
+ },
248
+ "idempotency_key": {
249
+ "type": "string",
250
+ "maxLength": 128
251
+ },
252
+ "provenance": {
253
+ "type": "object",
254
+ "properties": {
255
+ "process": {
256
+ "type": "object",
257
+ "additionalProperties": {}
258
+ },
259
+ "device": {
260
+ "type": "object",
261
+ "additionalProperties": {}
262
+ },
263
+ "harness": {
264
+ "type": "object",
265
+ "additionalProperties": {}
266
+ },
267
+ "repo": {
268
+ "type": "object",
269
+ "properties": {
270
+ "target_repos": {
271
+ "type": "array",
272
+ "items": {
273
+ "type": "string",
274
+ "minLength": 1,
275
+ "maxLength": 200
276
+ },
277
+ "maxItems": 20
278
+ }
279
+ },
280
+ "additionalProperties": {}
281
+ },
282
+ "git": {
283
+ "type": "object",
284
+ "additionalProperties": {}
285
+ },
286
+ "execution": {
287
+ "type": "object",
288
+ "properties": {
289
+ "rung": {
290
+ "type": "string",
291
+ "enum": [
292
+ "override",
293
+ "session",
294
+ "host-session",
295
+ "shell-session",
296
+ "shell",
297
+ "ai-agent",
298
+ "codex",
299
+ "cursor"
300
+ ]
301
+ }
302
+ },
303
+ "additionalProperties": {}
304
+ }
305
+ },
306
+ "additionalProperties": {}
307
+ }
308
+ },
309
+ "required": [
310
+ "schema_version",
311
+ "verb",
312
+ "route",
313
+ "summary",
314
+ "body",
315
+ "device_id",
316
+ "execution_id"
317
+ ],
318
+ "additionalProperties": true
319
+ }
320
+ },
321
+ "required": [
322
+ "id",
323
+ "idempotency_key",
324
+ "verb",
325
+ "summary",
326
+ "source",
327
+ "received_at",
328
+ "status",
329
+ "attempts",
330
+ "data"
331
+ ],
332
+ "additionalProperties": true
333
+ },
334
+ "YakCluster": {
335
+ "type": "object",
336
+ "properties": {
337
+ "id": {
338
+ "type": "string"
339
+ },
340
+ "kind": {
341
+ "type": "string",
342
+ "enum": [
343
+ "fatal",
344
+ "report"
345
+ ]
346
+ },
347
+ "canonical_summary": {
348
+ "type": "string"
349
+ },
350
+ "keywords": {
351
+ "type": "array",
352
+ "items": {
353
+ "type": "string"
354
+ },
355
+ "default": []
356
+ },
357
+ "observation_count": {
358
+ "type": "integer",
359
+ "minimum": 0
360
+ },
361
+ "first_seen": {
362
+ "type": "string",
363
+ "format": "date-time"
364
+ },
365
+ "last_seen": {
366
+ "type": "string",
367
+ "format": "date-time"
368
+ },
369
+ "current_issue_id": {
370
+ "type": "string"
371
+ },
372
+ "current_issue_identifier": {
373
+ "type": "string"
374
+ },
375
+ "issue_history": {
376
+ "type": "array",
377
+ "items": {
378
+ "type": "object",
379
+ "properties": {
380
+ "issue_id": {
381
+ "type": "string"
382
+ },
383
+ "issue_identifier": {
384
+ "type": "string"
385
+ },
386
+ "linked_at": {
387
+ "type": "string",
388
+ "format": "date-time"
389
+ },
390
+ "closed_observed_at": {
391
+ "type": "string",
392
+ "format": "date-time"
393
+ }
394
+ },
395
+ "required": [
396
+ "issue_id",
397
+ "issue_identifier",
398
+ "linked_at"
399
+ ],
400
+ "additionalProperties": true
401
+ },
402
+ "default": []
403
+ }
404
+ },
405
+ "required": [
406
+ "id",
407
+ "kind",
408
+ "canonical_summary",
409
+ "observation_count",
410
+ "first_seen",
411
+ "last_seen"
412
+ ],
413
+ "additionalProperties": true
414
+ }
415
+ }
416
+ }
@@ -0,0 +1 @@
1
+ export * from "./v2";