@jr2/cli 0.3.0 → 0.4.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @jr2/cli
2
2
 
3
- The `jr2` binary: the interface to a jr2 [Instance](https://github.com/snapwich/jr2/blob/main/CONTEXT.md) — a folder of
3
+ The `jr2` binary: the interface to a jr2 [Instance](https://github.com/snapwich/jr2/blob/main/GLOSSARY.md) — a folder of
4
4
  workflows deployed to whatever cluster the current `kubectl` context names.
5
5
 
6
6
  ```sh
package/bin/jr2.js CHANGED
@@ -5,9 +5,9 @@
5
5
  // loader to erase types the same way Node would, then hands over to the `.ts` sources.
6
6
  //
7
7
  // One entry for both worlds, deliberately: a checkout-only `.ts` bin plus an installed-only `.js`
8
- // bin would mean the binary users run is not the binary the checkout's tiers run. The image
9
- // bundle solves the same problem with `tsx` (see INSTANCE_DOCKERFILE) because there the entry is
10
- // the orchestrator's, not this one.
8
+ // bin would mean the binary users run is not the binary the checkout's tiers run. The
9
+ // Orchestrator Kit image solves the same problem with `tsx` (see deploy/orchestrator/Dockerfile)
10
+ // because there the entry is the orchestrator's, not this one.
11
11
  //
12
12
  // After the hook, the launcher's one decision (ADR-0056): inside an Instance that resolves its own
13
13
  // `@jr2/cli` to a different copy, hand off — run THAT copy's binary with the same argv and stdio
@@ -516,6 +516,36 @@ spec:
516
516
  of ADR-0005. Unset leaves volume ownership to the images' own uids.
517
517
  format: int64
518
518
  type: integer
519
+ gitCredentials:
520
+ description: |-
521
+ GitCredentials are the push Secrets of the `git.credentials` entries
522
+ this Sandbox's pushes spend (ADR-0051, ADR-0068): one per entry the
523
+ Orchestrator matched to a pushed Repo Slot's url at provision, named
524
+ as a Repo's secretRef names its Secret. The operator projects each
525
+ into the sidecar named "effector" — the Sandbox Effector — beside its
526
+ secrets, at `@git/<name>/` in the projected volume that container
527
+ mounts at /run/jr2/secrets, one file per key jr2 writes there
528
+ (`entry.json`, `username`, `password`, `identity`, `identity.pub`,
529
+ `known_hosts`, `sign.identity`, `sign.private.asc`), each when the
530
+ Secret holds it. No other container gets it. The operator reads no
531
+ Secret: the kubelet projects it.
532
+ items:
533
+ description: |-
534
+ SandboxGitCredential names one entry's push Secret, in the Sandbox's
535
+ namespace.
536
+ properties:
537
+ name:
538
+ description: Name is the Secret's name.
539
+ maxLength: 63
540
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
541
+ type: string
542
+ required:
543
+ - name
544
+ type: object
545
+ type: array
546
+ x-kubernetes-list-map-keys:
547
+ - name
548
+ x-kubernetes-list-type: map
519
549
  idleTimeout:
520
550
  description: |-
521
551
  IdleTimeout garbage-collects the Sandbox after this duration with no
@@ -532,8 +562,8 @@ spec:
532
562
  Scheduled verbatim — the operator adds nothing to them, not even the
533
563
  hardened securityContext default it gives sidecars, because an init step
534
564
  is composed by whoever built the spec and it must be able to say exactly
535
- what it needs. This is how jr2's runtime reaches a Sandbox (ADR-0037): one
536
- step populates an /opt/jr2 volume, a second proves the primary image on it.
565
+ what it needs. This is how jr2 proves a Sandbox's image (ADR-0037): one
566
+ step runs the primary image with the /opt/jr2 image volume mounted.
537
567
  items:
538
568
  description: A single application container that you want to run
539
569
  within a pod.
@@ -2064,6 +2094,70 @@ spec:
2064
2094
  it; its own soft affinity toward nodes holding this Sandbox's Repo caches
2065
2095
  sits beside it, and a preference never conflicts with a requirement.
2066
2096
  type: object
2097
+ podResources:
2098
+ description: |-
2099
+ PodResources is the pod-level ceiling (KEP-2837's `spec.resources` on
2100
+ the Pod): the whole Size, cpu and memory, requests = limits (ADR-0060).
2101
+ It is what lets a container with no limit of its own — a User Container
2102
+ the Machine gave no split — share the pod's budget, and it holds the pod
2103
+ Guaranteed. Copied verbatim; unset leaves the Pod without one.
2104
+ properties:
2105
+ claims:
2106
+ description: |-
2107
+ Claims lists the names of resources, defined in spec.resourceClaims,
2108
+ that are used by this container.
2109
+
2110
+ This field depends on the
2111
+ DynamicResourceAllocation feature gate.
2112
+
2113
+ This field is immutable. It can only be set for containers.
2114
+ items:
2115
+ description: ResourceClaim references one entry in PodSpec.ResourceClaims.
2116
+ properties:
2117
+ name:
2118
+ description: |-
2119
+ Name must match the name of one entry in pod.spec.resourceClaims of
2120
+ the Pod where this field is used. It makes that resource available
2121
+ inside a container.
2122
+ type: string
2123
+ request:
2124
+ description: |-
2125
+ Request is the name chosen for a request in the referenced claim.
2126
+ If empty, everything from the claim is made available, otherwise
2127
+ only the result of this request.
2128
+ type: string
2129
+ required:
2130
+ - name
2131
+ type: object
2132
+ type: array
2133
+ x-kubernetes-list-map-keys:
2134
+ - name
2135
+ x-kubernetes-list-type: map
2136
+ limits:
2137
+ additionalProperties:
2138
+ anyOf:
2139
+ - type: integer
2140
+ - type: string
2141
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
2142
+ x-kubernetes-int-or-string: true
2143
+ description: |-
2144
+ Limits describes the maximum amount of compute resources allowed.
2145
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
2146
+ type: object
2147
+ requests:
2148
+ additionalProperties:
2149
+ anyOf:
2150
+ - type: integer
2151
+ - type: string
2152
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
2153
+ x-kubernetes-int-or-string: true
2154
+ description: |-
2155
+ Requests describes the minimum amount of compute resources required.
2156
+ If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
2157
+ otherwise to an implementation-defined value. Requests cannot exceed Limits.
2158
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
2159
+ type: object
2160
+ type: object
2067
2161
  port:
2068
2162
  default: 8080
2069
2163
  description: |-
@@ -2073,6 +2167,12 @@ spec:
2073
2167
  maximum: 65535
2074
2168
  minimum: 1
2075
2169
  type: integer
2170
+ priorityClassName:
2171
+ description: |-
2172
+ PriorityClassName is the Pod's priority class, copied verbatim
2173
+ (ADR-0060): the Instance's sandbox class, `jr2-sandbox` unless the
2174
+ cluster names its own. Empty leaves the cluster's default.
2175
+ type: string
2076
2176
  readinessProbe:
2077
2177
  description: |-
2078
2178
  ReadinessProbe overrides the primary container's readiness probe. When
@@ -2237,8 +2337,8 @@ spec:
2237
2337
  needs it mounts the same volume by name. Scheduling prefers nodes whose
2238
2338
  `Repo` status reports the key present; `Ready` waits for every key to be
2239
2339
  present on the pod's node and fetched since this Sandbox was created.
2240
- This is the whole of what the CRD knows about git: clone and worktree
2241
- stay the Orchestrator's post-Ready step (ADR-0004).
2340
+ With gitCredentials, this is the whole of what the CRD knows about git:
2341
+ clone and worktree stay the Orchestrator's post-Ready step (ADR-0004).
2242
2342
  items:
2243
2343
  description: SandboxRepo names one Repo a Sandbox attaches.
2244
2344
  properties:
@@ -2265,7 +2365,15 @@ spec:
2265
2365
  - key
2266
2366
  x-kubernetes-list-type: map
2267
2367
  resources:
2268
- description: Resources are the compute resources for the primary container.
2368
+ description: |-
2369
+ Resources are the primary container's resources — the Harness's share
2370
+ of the Workspace's Size (ADR-0060), copied onto the container verbatim.
2371
+ The Orchestrator splits the Size and sets requests = limits for cpu and
2372
+ memory, plus the `/work` disk as an ephemeral-storage request with no
2373
+ limit (a limit would evict the pod and lose `/work`). The operator
2374
+ computes nothing here: every number is the Orchestrator's. The other
2375
+ containers' shares ride their own fragments — Sidecars and
2376
+ InitContainers are whole Containers, `resources` included.
2269
2377
  properties:
2270
2378
  claims:
2271
2379
  description: |-
@@ -2521,6 +2629,21 @@ spec:
2521
2629
  type: string
2522
2630
  type: object
2523
2631
  type: object
2632
+ shmSize:
2633
+ anyOf:
2634
+ - type: integer
2635
+ - type: string
2636
+ description: |-
2637
+ ShmSize is the sizeLimit of the memory-backed emptyDir the operator
2638
+ mounts at /dev/shm in the primary container only (ADR-0060). The
2639
+ Orchestrator computes it from the Harness's share; shm is charged inside
2640
+ that container's memory limit, so it reserves nothing extra, and a full
2641
+ shm is ENOSPC or SIGBUS — a tool error, never a memory kill. Unset
2642
+ mounts nothing and leaves the runtime's own /dev/shm: an unbounded
2643
+ memory emptyDir is never the default, because filling it kills the
2644
+ container.
2645
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
2646
+ x-kubernetes-int-or-string: true
2524
2647
  sidecars:
2525
2648
  description: |-
2526
2649
  Sidecars are generic Kubernetes container fragments scheduled alongside
@@ -6070,8 +6193,14 @@ spec:
6070
6193
  description: status defines the observed state of Sandbox
6071
6194
  properties:
6072
6195
  conditions:
6073
- description: Conditions represent the current state of the Sandbox
6074
- resource.
6196
+ description: |-
6197
+ Conditions represent the current state of the Sandbox resource: Ready
6198
+ (the gate), ReposFresh (the Repo gate's verdict), Scheduled — the Pod's
6199
+ PodScheduled condition restated with the scheduler's own reason and
6200
+ message, or QuotaExceeded with the API server's words while a
6201
+ ResourceQuota refuses the Pod, so a Pending Sandbox says why it waits
6202
+ (ADR-0063, ADR-0064) — and Lost, True with the reason and the Pod's own
6203
+ words once the phase is Lost (ADR-0021).
6075
6204
  items:
6076
6205
  description: Condition contains details for one aspect of the current
6077
6206
  state of this API Resource.
@@ -6130,27 +6259,109 @@ spec:
6130
6259
  x-kubernetes-list-map-keys:
6131
6260
  - type
6132
6261
  x-kubernetes-list-type: map
6262
+ effector:
6263
+ description: |-
6264
+ Effector is the same report for the sidecar named "effector", the
6265
+ Sandbox Effector (ADR-0068): its restarts and how it last ended. An
6266
+ Effector that cannot serve says why in its termination message — a
6267
+ node whose kernel cannot confine a credentialed command's child says
6268
+ `landlock: unavailable on node <node> (...)` — so a provision fails on
6269
+ it before any Turn. Absent when the pod has no Effector, or until the
6270
+ kubelet reports it.
6271
+ properties:
6272
+ lastTerminated:
6273
+ description: |-
6274
+ LastTerminated is how the container's previous run ended — the Pod's
6275
+ `lastState.terminated`. Absent while the container has never restarted.
6276
+ properties:
6277
+ exitCode:
6278
+ description: ExitCode is the run's exit code (137 for a kernel
6279
+ kill).
6280
+ format: int32
6281
+ type: integer
6282
+ finishedAt:
6283
+ description: FinishedAt is when the run ended.
6284
+ format: date-time
6285
+ type: string
6286
+ message:
6287
+ description: |-
6288
+ Message is the kubelet's termination message. For the Harness container
6289
+ it is the run's last log lines (`FallbackToLogsOnError`, ADR-0063), so a
6290
+ Harness that dies before Ready is named in its own words.
6291
+ type: string
6292
+ reason:
6293
+ description: Reason is the kubelet's reason, e.g. `OOMKilled`,
6294
+ `Error`, `Completed`.
6295
+ type: string
6296
+ type: object
6297
+ restartCount:
6298
+ description: RestartCount is the container's restartCount on the
6299
+ Sandbox's Pod.
6300
+ format: int32
6301
+ type: integer
6302
+ type: object
6133
6303
  endpoint:
6134
6304
  description: |-
6135
6305
  Endpoint is the in-cluster Service DNS the Orchestrator uses to reach the
6136
6306
  Harness, e.g. http://<name>.<ns>.svc:8080.
6137
6307
  type: string
6308
+ harness:
6309
+ description: |-
6310
+ Harness is what the kubelet reports about the primary container of the
6311
+ Pod status.podUID names (ADR-0063): its restarts and how it last ended.
6312
+ The Orchestrator never reads a Pod; this is how it tells a
6313
+ crash-looping Harness, and a memory kill (`OOMKilled`, ADR-0061), from
6314
+ a lost conversation. Absent until the kubelet reports the container.
6315
+ properties:
6316
+ lastTerminated:
6317
+ description: |-
6318
+ LastTerminated is how the container's previous run ended — the Pod's
6319
+ `lastState.terminated`. Absent while the container has never restarted.
6320
+ properties:
6321
+ exitCode:
6322
+ description: ExitCode is the run's exit code (137 for a kernel
6323
+ kill).
6324
+ format: int32
6325
+ type: integer
6326
+ finishedAt:
6327
+ description: FinishedAt is when the run ended.
6328
+ format: date-time
6329
+ type: string
6330
+ message:
6331
+ description: |-
6332
+ Message is the kubelet's termination message. For the Harness container
6333
+ it is the run's last log lines (`FallbackToLogsOnError`, ADR-0063), so a
6334
+ Harness that dies before Ready is named in its own words.
6335
+ type: string
6336
+ reason:
6337
+ description: Reason is the kubelet's reason, e.g. `OOMKilled`,
6338
+ `Error`, `Completed`.
6339
+ type: string
6340
+ type: object
6341
+ restartCount:
6342
+ description: RestartCount is the container's restartCount on the
6343
+ Sandbox's Pod.
6344
+ format: int32
6345
+ type: integer
6346
+ type: object
6138
6347
  node:
6139
6348
  description: |-
6140
6349
  Node is the node the Pod was scheduled onto, once it was — the node
6141
6350
  whose Repo caches this Sandbox mounts, and so the key into each named
6142
6351
  Repo's `status.nodes[]` for whoever reads the Sandbox — a human at
6143
- `kubectl get`. Nothing in the control plane reads it back:
6144
- the operator places and gates on the Pod's own `spec.nodeName`, and the
6145
- cache agent takes demand off the pods on its node, never off a Sandbox
6146
- (ADR-0051).
6352
+ `kubectl get`. The operator reads it back only to choose which
6353
+ Sandboxes a Repo's status write wakes (ADR-0001); it places and gates
6354
+ on the Pod's own `spec.nodeName`, and the cache agent takes demand off
6355
+ the pods on its node, never off a Sandbox (ADR-0051).
6147
6356
  type: string
6148
6357
  phase:
6149
- description: 'Phase is a coarse lifecycle summary: Pending -> Ready
6150
- -> Terminating.'
6358
+ description: |-
6359
+ Phase is a coarse lifecycle summary: Pending -> Ready -> Terminating,
6360
+ or Lost from Pending or Ready once the Pod is gone or terminal (ADR-0021).
6151
6361
  enum:
6152
6362
  - Pending
6153
6363
  - Ready
6364
+ - Lost
6154
6365
  - Terminating
6155
6366
  type: string
6156
6367
  podRef:
@@ -6169,12 +6380,11 @@ spec:
6169
6380
  x-kubernetes-map-type: atomic
6170
6381
  podUID:
6171
6382
  description: |-
6172
- PodUID is the identity of the Pod backing this Sandbox. Pod names are
6173
- deterministic, so a name alone cannot distinguish the Pod a client
6174
- attached to from a replacement scheduled after an eviction or node loss —
6175
- and a replacement comes up with an empty `work` volume, so every clone,
6176
- worktree, and unpushed commit is gone. The UID changes exactly when that
6177
- happens, which is what lets a client detect it (ADR-0021).
6383
+ PodUID is the identity of the one Pod the operator created for this
6384
+ Sandbox (ADR-0021). Set once, when that Pod is created, and never
6385
+ changed: it is how the operator knows, across its own restarts, that the
6386
+ Pod was already created once — so a Pod that is later absent is Lost, never
6387
+ created again. Clients read phase Lost, not a UID comparison.
6178
6388
  type: string
6179
6389
  repos:
6180
6390
  description: |-
@@ -6248,6 +6458,38 @@ spec:
6248
6458
  type: string
6249
6459
  type: object
6250
6460
  x-kubernetes-map-type: atomic
6461
+ waiting:
6462
+ description: |-
6463
+ Waiting is every container of the current Pod that is waiting, init
6464
+ containers first, with the kubelet's reason and message (ADR-0063). A
6465
+ container that never starts — a root image under runAsNonRoot, a bad
6466
+ image name, a pull that keeps failing — leaves no log and no
6467
+ termination, so this is the Orchestrator's only evidence of it. Copied,
6468
+ not judged: the Orchestrator decides which reasons end a provision.
6469
+ items:
6470
+ description: SandboxContainerWaiting is one waiting container, in
6471
+ the kubelet's words.
6472
+ properties:
6473
+ container:
6474
+ description: Container is the container's name, e.g. `preflight`,
6475
+ `harness`.
6476
+ type: string
6477
+ message:
6478
+ description: Message is the kubelet's message, when it gives
6479
+ one.
6480
+ type: string
6481
+ reason:
6482
+ description: |-
6483
+ Reason is the kubelet's reason, e.g. `CreateContainerConfigError`,
6484
+ `ImagePullBackOff`, `PodInitializing`.
6485
+ type: string
6486
+ required:
6487
+ - container
6488
+ type: object
6489
+ type: array
6490
+ x-kubernetes-list-map-keys:
6491
+ - container
6492
+ x-kubernetes-list-type: map
6251
6493
  type: object
6252
6494
  required:
6253
6495
  - spec
@@ -6648,11 +6890,10 @@ spec:
6648
6890
  periodSeconds: 10
6649
6891
  resources:
6650
6892
  limits:
6651
- cpu: 500m
6652
- memory: 128Mi
6893
+ memory: 256Mi
6653
6894
  requests:
6654
- cpu: 10m
6655
- memory: 64Mi
6895
+ cpu: 50m
6896
+ memory: 256Mi
6656
6897
  securityContext:
6657
6898
  allowPrivilegeEscalation: false
6658
6899
  capabilities:
@@ -6660,6 +6901,7 @@ spec:
6660
6901
  - ALL
6661
6902
  readOnlyRootFilesystem: true
6662
6903
  volumeMounts: []
6904
+ priorityClassName: jr2-control
6663
6905
  securityContext:
6664
6906
  runAsNonRoot: true
6665
6907
  seccompProfile:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jr2/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "The jr2 CLI: init, up, run — the interface to a jr2 Instance.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -39,14 +39,14 @@
39
39
  "ts-blank-space": "^0.9.0"
40
40
  },
41
41
  "peerDependencies": {
42
- "@jr2/orchestrator": "0.3.0"
42
+ "@jr2/orchestrator": "0.4.1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@anthropic-ai/sdk": "0.91.1",
46
46
  "@types/node": "^26.0.1",
47
47
  "xstate": "^5.18.0",
48
48
  "zod": "^4.4.3",
49
- "@jr2/orchestrator": "0.3.0"
49
+ "@jr2/orchestrator": "0.4.1"
50
50
  },
51
51
  "files": [
52
52
  "bin",
@@ -55,7 +55,7 @@
55
55
  ],
56
56
  "scripts": {
57
57
  "typecheck": "tsc --noEmit",
58
- "test": "node --test 'test/**/*.test.ts'",
59
- "test:custodian": "node --test --test-concurrency=1 test/custodian.docker.ts"
58
+ "test": "node --test --test-global-setup=../../scripts/test-tmpdir.ts 'test/**/*.test.ts'",
59
+ "test:custodian": "node --test --test-global-setup=../../scripts/test-tmpdir.ts --test-concurrency=1 test/custodian.docker.ts"
60
60
  }
61
61
  }