@outerlayer/cli 0.5.1 → 0.7.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/CHANGELOG.md +29 -0
- package/README.md +19 -0
- package/dist/{agent-setup-IFH3FB5X.js → agent-setup-TXLDY5RV.js} +7 -6
- package/dist/{build-PAEKVYVK.js → build-JF5UOWKJ.js} +2 -1
- package/dist/build-info.json +1 -1
- package/dist/{check-4Q33B5ZE.js → check-ASZL5BEO.js} +39 -23
- package/dist/chunk-2VO25HHO.js +75 -0
- package/dist/{chunk-XDDW4FRS.js → chunk-5FWNEQNZ.js} +3 -3
- package/dist/{chunk-CHLVJNTE.js → chunk-6IYLDODX.js} +18 -6
- package/dist/{chunk-W7UTFZDH.js → chunk-6QWV3YDJ.js} +2 -2
- package/dist/{chunk-EGABNHZG.js → chunk-6ZZHWY2Z.js} +149 -18
- package/dist/{lima-XN65D7GN.js → chunk-77MM5RYD.js} +0 -1
- package/dist/chunk-7FTNIAHS.js +211 -0
- package/dist/{chunk-EMY4I27X.js → chunk-7ZR5UPMB.js} +555 -5
- package/dist/{chunk-F4NU4H3V.js → chunk-AII7CKTH.js} +133 -35
- package/dist/{chunk-ZXBITXTS.js → chunk-AIW6GLET.js} +1 -1
- package/dist/{chunk-UWH5DSUP.js → chunk-AJKEHABI.js} +507 -90
- package/dist/{chunk-RTXKYLCA.js → chunk-AQ54MKSQ.js} +2 -2
- package/dist/{chunk-Z62HNKEJ.js → chunk-CEBIA5FD.js} +31 -5
- package/dist/{chunk-EX6M5JPR.js → chunk-D6FXM5OW.js} +6302 -28611
- package/dist/{chunk-RMAZ2NWA.js → chunk-DGAIVMMN.js} +23 -2
- package/dist/{chunk-3DD6AOWC.js → chunk-DTXOJDEH.js} +26 -2
- package/dist/{chunk-T52PKE2Y.js → chunk-E4PIFIC3.js} +244 -17
- package/dist/{chunk-H5NCNVDA.js → chunk-EV3M3EH7.js} +2 -2
- package/dist/chunk-FBTE2A2X.js +38 -0
- package/dist/{chunk-DVDEBNQJ.js → chunk-GQD22KKN.js} +17 -6
- package/dist/{chunk-BCJNHMZT.js → chunk-HHJDN6AU.js} +18 -3
- package/dist/chunk-HKMPVPO7.js +55 -0
- package/dist/{chunk-UFFXNXLQ.js → chunk-JPR2WHHF.js} +31 -29
- package/dist/{chunk-ZMYPLFG3.js → chunk-M3SQ4YGD.js} +1 -1
- package/dist/{chunk-H4CK2YK4.js → chunk-MQPRB5K7.js} +18 -10
- package/dist/{chunk-AUFG23AA.js → chunk-OD2VTIHN.js} +19 -47
- package/dist/chunk-OUUINEYB.js +27 -0
- package/dist/chunk-PUN7LMNS.js +39 -0
- package/dist/{install-cmd-7WXOIFDO.js → chunk-Q5G2ODUZ.js} +1 -2
- package/dist/{chunk-B2G7JHHB.js → chunk-R7STSBLC.js} +1 -1
- package/dist/{chunk-OR6AEEDO.js → chunk-SSU747O6.js} +85 -25
- package/dist/chunk-T6OIJQRT.js +31 -0
- package/dist/{chunk-YYYXRJUW.js → chunk-TI7B7UEO.js} +1 -1
- package/dist/{chunk-WO2BXCTQ.js → chunk-VXLJORHL.js} +1 -1
- package/dist/{chunk-CBPPJSAR.js → chunk-WVGSB3Z2.js} +2 -2
- package/dist/{chunk-JLDZTG3A.js → chunk-X2FSFU4C.js} +4 -1
- package/dist/chunk-YNJEOGDL.js +22434 -0
- package/dist/{chunk-W4SQCOZU.js → chunk-ZOAEPWGL.js} +1 -1
- package/dist/{chunk-WOBNRVQT.js → chunk-ZYHKJKSU.js} +1 -1
- package/dist/{chunk-WIOZAJ2W.js → chunk-ZZXLM64C.js} +1 -1
- package/dist/{cli-LHSUYYMI.js → cli-LEVCGAQV.js} +154 -79
- package/dist/command-FLSYCGIB.js +187 -0
- package/dist/config-I3TVFYLH.js +10 -0
- package/dist/{connect-cmd-OJBFHZWP.js → connect-cmd-MJMUONDY.js} +4 -4
- package/dist/{context-adopt-UMA4O5NY.js → context-adopt-CE4KDXS3.js} +5 -4
- package/dist/{context-materialize-G2FUFC72.js → context-materialize-QL33HDJN.js} +5 -4
- package/dist/{dist-WDV7U7UJ.js → dist-PXBSA7QX.js} +2 -1
- package/dist/{doctor-MOA3RIEP.js → doctor-FVDT3KJZ.js} +46 -19
- package/dist/{emit-artifact-cmd-FDWYKURA.js → emit-artifact-cmd-7RPMRSVP.js} +13 -7
- package/dist/{emit-cmd-DCERAU27.js → emit-cmd-2QRMYPYO.js} +4 -3
- package/dist/{emit-criteria-cmd-OCXHLK34.js → emit-criteria-cmd-MKI7WETO.js} +12 -3
- package/dist/{emit-finding-cmd-POW7PKIA.js → emit-finding-cmd-RMLGZASB.js} +14 -3
- package/dist/{emit-result-cmd-ID35SH53.js → emit-result-cmd-MTXAZ2GV.js} +15 -9
- package/dist/{exec-client-4XGXV3XN.js → exec-client-NPYNUAF3.js} +4 -3
- package/dist/{guest-init-GENRHNP7.js → guest-init-JWNNAH2H.js} +4 -3
- package/dist/{hook-fast-EYENPK6F.js → hook-fast-XBOZ3N6L.js} +1 -1
- package/dist/{import-capture-cmd-AVZ4VIVJ.js → import-capture-cmd-T4556ELW.js} +3 -2
- package/dist/{import-ruler-cmd-GUHL6IY5.js → import-ruler-cmd-SG3UVGI7.js} +2 -1
- package/dist/index.js +3 -2
- package/dist/{init-FYDJAKTM.js → init-BACXESVV.js} +21 -14
- package/dist/{init-2IPOIXJV.js → init-VLW7H6RW.js} +40 -62
- package/dist/{init-cmd-KLIDRVXQ.js → init-cmd-VJFB5L7X.js} +10 -9
- package/dist/inspect-XAUSB7L3.js +63 -0
- package/dist/install-NMAPLLBQ.js +14 -0
- package/dist/install-cmd-B5SEU3G7.js +8 -0
- package/dist/lima-7NPCNGWT.js +6 -0
- package/dist/{logs-7RYUH5A6.js → logs-H55NWW4H.js} +1 -0
- package/dist/loop-D3243JKT.js +64 -0
- package/dist/{machine-ECU3QCZD.js → machine-EB3GEVI4.js} +20 -13
- package/dist/session-sender-LMACEJJS.js +29 -0
- package/dist/{starter-pack-LIZYMKYQ.js → starter-pack-MUXMAU4S.js} +4 -3
- package/dist/{status-CLATHCTQ.js → status-F4THTJJK.js} +36 -16
- package/dist/{sync-cmd-SWQ5KLPL.js → sync-cmd-M6JA3OEE.js} +7 -7
- package/dist/{watch-3YW6Z3DQ.js → watch-NB5TGPEZ.js} +7 -7
- package/dist/{work-claim-cmd-3OPWA77E.js → work-claim-cmd-UHMWOS3G.js} +14 -7
- package/dist/{work-cmd-NUR4CAG6.js → work-cmd-6UMCFTAW.js} +9 -3
- package/dist/{work-comment-cmd-6TTTYGHM.js → work-comment-cmd-E4LEUIXZ.js} +9 -3
- package/dist/{work-launch-YI4CJDAP.js → work-launch-R5HTAP6X.js} +1 -1
- package/dist/{work-open-pr-cmd-KZ7QQFLD.js → work-open-pr-cmd-OPZWGSEQ.js} +10 -4
- package/dist/{work-pr-cmd-QNUBE6IQ.js → work-pr-cmd-IDIAFFRU.js} +9 -2
- package/package.json +2 -1
- package/skill-pack/template/build/scripts/record-criteria.mjs +2 -1
- package/dist/chunk-D2KWEAN6.js +0 -55
- package/dist/chunk-ZOC7JSYC.js +0 -83
- package/dist/config-HL2AIR3K.js +0 -9
- package/dist/loop-NEBUGD2Q.js +0 -45
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @outerlayer/cli
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b4fdd73: The runner now runs a repository's `where: host` validators itself after a build, in a new container from the build's image, and records each result under the host's own key. A check that cannot run is recorded as a fail with a reason. `runner.checks.timeLimitMinutes` (default 10, 1 to 240) bounds the environment setup and each check command. The runner CLI is not updated for you on a host with `runner.autoUpdate` off: refresh it by hand on each host to get host checks.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 35b0fed: Fixes found by running the runner service on real machines:
|
|
12
|
+
|
|
13
|
+
- A repository fetch with a claim's read token keeps the git settings the host passes in `GIT_CONFIG_*`, such as one in `runner.env`. It added the token over them.
|
|
14
|
+
- `runner install --service --user <account>` refuses when that account cannot execute the Node the install runs on, or read the runner's CLI, instead of installing a service that cannot start.
|
|
15
|
+
- A microVM the kernel kills at its memory limit while it is still starting is released with a reason saying the build ran out of memory, not that the microVM did not start.
|
|
16
|
+
- A microVM build's cgroup reclaims above `build.memory` plus 256 MiB and kills above `build.memory` plus 1 GiB. A real build was killed at plus 256 MiB, because the page cache of its disks' writes is charged to the same cgroup.
|
|
17
|
+
|
|
18
|
+
- f633a70: The template build skill's `record-criteria.mjs` now records a criterion that declares `(proof: test)` with proof `test`. Test results for that criterion now count toward it.
|
|
19
|
+
- 735d939: `work` and `emit` commands now connect the repository themselves when you are logged in, the repository has no saved connection, and exactly one factory fits. They print one line naming the factory and why, save the connection, and carry on. When several factories fit, they refuse and list the `--factory` value for each.
|
|
20
|
+
|
|
21
|
+
## 0.6.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- cb41102: A runner build's agent session now appears on its work item, with its running cost, while the build runs. The runner starts a hidden `session-sender` command beside each build's command, in a microVM, a container, a host `exec` hook or on the host. The end-of-attempt sync stays the complete upload. The capture daemon gains a watch-only mode that reports changed transcripts without copying them.
|
|
26
|
+
- 21b7405: `outerlayer runner init` now sets the runner up to keep running on Linux, macOS and Windows (WSL2), with the same commands on each: `runner install --service` (with `--print`, `--user` and `--replace`) writes, enables and starts one systemd unit, and `runner uninstall --service` removes it. Each microVM build runs in a cgroup of its own with a memory limit of `build.memory` plus 256 MiB, and a build the kernel kills for want of memory ends `failed` with a reason that says so, without stopping the runner or the other builds. Setup refuses builds that cannot fit in memory together. The runner claims nothing, and says why in its heartbeat as `blocked`, when it cannot open `/dev/kvm`, has no Claude credential, still has the placeholder command, or has builds that do not fit in memory. `outerlayer doctor` now reports the running service's groups and credential instead of your shell's. The runner now reports protocol 14 to its gateway.
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- 573abaa: A session link that worked no longer leaves a Node warning in `~/.outerlayer/spool/hook-errors.log`. Only links that fail are logged, with one line naming the item.
|
|
31
|
+
|
|
3
32
|
## 0.5.1
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -56,6 +56,15 @@ send, so turning it off takes effect without restarting the daemon.
|
|
|
56
56
|
authoritative upload — the daemon exists so a session's page can follow
|
|
57
57
|
along before that sync ever runs.
|
|
58
58
|
|
|
59
|
+
**`outerlayer session-sender` is started only by a runner, inside each build,
|
|
60
|
+
and streams that build's sessions to its work item while the build runs.** It
|
|
61
|
+
sends under the build's own item key to the build's own gateway, both taken
|
|
62
|
+
from the job's environment and never from your config file, and only sessions
|
|
63
|
+
the build itself launched. It sends no session that belongs to anyone else on
|
|
64
|
+
the machine, and your own `outerlayer daemon` skips sessions launched inside a
|
|
65
|
+
runner build. The runner stops it before the build's final `outerlayer sync`,
|
|
66
|
+
which remains the complete upload.
|
|
67
|
+
|
|
59
68
|
These commands also talk to your workspace. None of them sends session
|
|
60
69
|
content:
|
|
61
70
|
|
|
@@ -92,6 +101,12 @@ content:
|
|
|
92
101
|
work — success, or the error code and message the platform returned.
|
|
93
102
|
When a build's command exits 0, it reads that work item once more, for its
|
|
94
103
|
evidence verdict, before releasing it: it sends nothing but the request.
|
|
104
|
+
Just before that read, the runner runs the repository's host checks. A host
|
|
105
|
+
check is a command the repository's `where: host` validators declare. The
|
|
106
|
+
runner reads the definitions with the host's own git credentials, runs each
|
|
107
|
+
command in a new container, and sends one `POST /v1/emitted-results` for
|
|
108
|
+
each: the check's name, pass or fail, and for a fail the exit status and the
|
|
109
|
+
last line of its output, scrubbed.
|
|
95
110
|
Before a build's command starts, the runner also runs `git ls-remote` and
|
|
96
111
|
`git fetch` in the job's workdir, with the host's own git credentials, to
|
|
97
112
|
put it on the branch the claim named. Nothing about the job leaves the host
|
|
@@ -124,6 +139,10 @@ content:
|
|
|
124
139
|
never printed. If this machine held a token from an earlier login, login
|
|
125
140
|
asks the dashboard to revoke it. A key piped on stdin makes no network
|
|
126
141
|
call.
|
|
142
|
+
- **`outerlayer work`** and **`outerlayer emit`**, in the subcommands named below, with a login and no saved
|
|
143
|
+
connection for the repository send the same `GET /v1/me/factories?repository=<host/owner/name>`
|
|
144
|
+
request before their own, to connect the repository when exactly one factory
|
|
145
|
+
fits. They send nothing extra with a factory key, `--app-id` or `OUTERLAYER_APP_ID`. The subcommands are `work build`, `list`, `status`, `remove` and `pr`, and `emit artifact`, `result`, `criteria`, `finding` and `findings`. Other `work` subcommands never connect.
|
|
127
146
|
- **`outerlayer connect`**, and the first step of `outerlayer init`, send a
|
|
128
147
|
request with your login's token: `GET /v1/me/factories?repository=<host/owner/name>`,
|
|
129
148
|
naming this checkout's remote. The gateway answers with the factories you can
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { installStarterPack } from './chunk-
|
|
4
|
-
import { runEmit } from './chunk-
|
|
3
|
+
import { installStarterPack } from './chunk-M3SQ4YGD.js';
|
|
4
|
+
import { runEmit } from './chunk-EV3M3EH7.js';
|
|
5
5
|
import { addStdioMcpEntryIfAbsent } from './chunk-MQ3IPHIZ.js';
|
|
6
6
|
import { C } from './chunk-YRYMNBGQ.js';
|
|
7
|
-
import { resolveGitCommonDir, defaultGitRunner } from './chunk-
|
|
8
|
-
import { pinExists, readContextManifest } from './chunk-WIOZAJ2W.js';
|
|
9
|
-
import { loadTemplateFiles, loadMaintainedFiles, skillDir } from './chunk-WO2BXCTQ.js';
|
|
10
|
-
import { generatedHeaderSource } from './chunk-EMY4I27X.js';
|
|
7
|
+
import { resolveGitCommonDir, defaultGitRunner } from './chunk-5FWNEQNZ.js';
|
|
11
8
|
import './chunk-ZJSNLVCE.js';
|
|
9
|
+
import { pinExists, readContextManifest } from './chunk-ZZXLM64C.js';
|
|
10
|
+
import { loadTemplateFiles, loadMaintainedFiles, skillDir } from './chunk-VXLJORHL.js';
|
|
11
|
+
import { generatedHeaderSource } from './chunk-7ZR5UPMB.js';
|
|
12
12
|
import './chunk-BCLJSQCV.js';
|
|
13
13
|
import './chunk-BOLTI6LR.js';
|
|
14
|
+
import './chunk-YNJEOGDL.js';
|
|
14
15
|
import './chunk-EABW6AJQ.js';
|
|
15
16
|
import './chunk-RA3O54FC.js';
|
|
16
17
|
import './chunk-M5POMKKW.js';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
export { buildRepositoryImage } from './chunk-
|
|
3
|
+
export { buildRepositoryImage } from './chunk-CEBIA5FD.js';
|
|
4
4
|
import './chunk-N5FOE5PS.js';
|
|
5
5
|
export { RecipeError } from './chunk-QOZKPLVJ.js';
|
|
6
|
+
import './chunk-HKMPVPO7.js';
|
|
6
7
|
import './chunk-ZM2IMMYN.js';
|
|
7
8
|
import './chunk-Z5GEFMRW.js';
|
|
8
9
|
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"2026-10-
|
|
1
|
+
{"id":"2026-10-05T00:52:06.865Z+90312df","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
|
|
@@ -1,45 +1,61 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { probeCredentials } from './chunk-
|
|
4
|
-
import './chunk-UWH5DSUP.js';
|
|
5
|
-
import './chunk-ZXBITXTS.js';
|
|
6
|
-
import './chunk-UGP76IZ5.js';
|
|
3
|
+
import { probeCredentials } from './chunk-AII7CKTH.js';
|
|
7
4
|
import './chunk-ABIWDUMS.js';
|
|
8
|
-
import './chunk-
|
|
9
|
-
import './chunk-
|
|
10
|
-
import
|
|
5
|
+
import './chunk-TI7B7UEO.js';
|
|
6
|
+
import './chunk-AJKEHABI.js';
|
|
7
|
+
import './chunk-T6OIJQRT.js';
|
|
8
|
+
import './chunk-AIW6GLET.js';
|
|
9
|
+
import './chunk-UGP76IZ5.js';
|
|
10
|
+
import './chunk-2VO25HHO.js';
|
|
11
|
+
import './chunk-WVGSB3Z2.js';
|
|
12
|
+
import './chunk-6ZZHWY2Z.js';
|
|
13
|
+
import './chunk-HHJDN6AU.js';
|
|
14
|
+
import { placeholderQueues, placeholderProblem, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem } from './chunk-6IYLDODX.js';
|
|
11
15
|
import './chunk-YYYRBYWG.js';
|
|
12
|
-
import './chunk-ZOC7JSYC.js';
|
|
13
16
|
import './chunk-TBT347UY.js';
|
|
14
|
-
import './chunk-
|
|
15
|
-
import './chunk-
|
|
16
|
-
import './chunk-
|
|
17
|
-
import './chunk-
|
|
17
|
+
import './chunk-6QWV3YDJ.js';
|
|
18
|
+
import './chunk-OUUINEYB.js';
|
|
19
|
+
import './chunk-AQ54MKSQ.js';
|
|
20
|
+
import './chunk-E4PIFIC3.js';
|
|
21
|
+
import './chunk-CEBIA5FD.js';
|
|
18
22
|
import './chunk-N5FOE5PS.js';
|
|
19
23
|
import './chunk-OGFGZQA3.js';
|
|
20
24
|
import './chunk-774HEQL6.js';
|
|
21
25
|
import './chunk-QOZKPLVJ.js';
|
|
22
|
-
import './chunk-
|
|
23
|
-
import './chunk-
|
|
24
|
-
import './chunk-
|
|
26
|
+
import './chunk-ZOAEPWGL.js';
|
|
27
|
+
import './chunk-R7STSBLC.js';
|
|
28
|
+
import './chunk-F7CU5ABH.js';
|
|
29
|
+
import './chunk-BKO6JEZI.js';
|
|
30
|
+
import './chunk-DGAIVMMN.js';
|
|
31
|
+
import './chunk-PUN7LMNS.js';
|
|
25
32
|
import './chunk-BFESLKPP.js';
|
|
26
|
-
import './chunk-
|
|
27
|
-
import './chunk-
|
|
28
|
-
import
|
|
33
|
+
import './chunk-SSU747O6.js';
|
|
34
|
+
import './chunk-ZYHKJKSU.js';
|
|
35
|
+
import './chunk-D6FXM5OW.js';
|
|
36
|
+
import './chunk-USO2DKBD.js';
|
|
37
|
+
import './chunk-N6LURUEF.js';
|
|
38
|
+
import './chunk-YRYMNBGQ.js';
|
|
39
|
+
import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-DTXOJDEH.js';
|
|
29
40
|
import './chunk-F6GFZXZ3.js';
|
|
30
|
-
import './chunk-
|
|
41
|
+
import './chunk-OD2VTIHN.js';
|
|
42
|
+
import './chunk-HKMPVPO7.js';
|
|
43
|
+
import './chunk-GQD22KKN.js';
|
|
31
44
|
import './chunk-WZBHBH3J.js';
|
|
32
|
-
import './chunk-
|
|
33
|
-
import './chunk-
|
|
34
|
-
import './chunk-WO2BXCTQ.js';
|
|
35
|
-
import './chunk-EMY4I27X.js';
|
|
45
|
+
import './chunk-5QRP3MVS.js';
|
|
46
|
+
import './chunk-5FWNEQNZ.js';
|
|
36
47
|
import './chunk-ZJSNLVCE.js';
|
|
48
|
+
import './chunk-ZZXLM64C.js';
|
|
49
|
+
import './chunk-VXLJORHL.js';
|
|
50
|
+
import './chunk-7ZR5UPMB.js';
|
|
37
51
|
import './chunk-BCLJSQCV.js';
|
|
38
52
|
import './chunk-BOLTI6LR.js';
|
|
53
|
+
import './chunk-YNJEOGDL.js';
|
|
39
54
|
import './chunk-EABW6AJQ.js';
|
|
40
55
|
import './chunk-RA3O54FC.js';
|
|
41
56
|
import './chunk-M5POMKKW.js';
|
|
42
57
|
import './chunk-ZM2IMMYN.js';
|
|
58
|
+
import './chunk-JPR2WHHF.js';
|
|
43
59
|
import './chunk-XCCLFVXM.js';
|
|
44
60
|
import './chunk-BJ3KTMDH.js';
|
|
45
61
|
import './chunk-VNVZDWO3.js';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { LiveUploader } from './chunk-6ZZHWY2Z.js';
|
|
4
|
+
import { CaptureDaemon } from './chunk-D6FXM5OW.js';
|
|
5
|
+
import { parseWorkItemEnv } from './chunk-GQD22KKN.js';
|
|
6
|
+
import { spoolDir } from './chunk-JPR2WHHF.js';
|
|
7
|
+
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
8
|
+
import { mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
|
|
11
|
+
createRequire(import.meta.url);
|
|
12
|
+
var SENDER_STOP_WAIT_MS = 25e3;
|
|
13
|
+
var RESCAN_MS = 3e4;
|
|
14
|
+
function senderPidfile(home, item) {
|
|
15
|
+
return join(spoolDir(home), `session-sender-${item}.pid`);
|
|
16
|
+
}
|
|
17
|
+
async function startSessionSender(opts = {}) {
|
|
18
|
+
const env = opts.env ?? process.env;
|
|
19
|
+
const stderr = opts.stderr ?? ((s) => void process.stderr.write(s));
|
|
20
|
+
const parsed = parseWorkItemEnv(env.OUTERLAYER_WORK);
|
|
21
|
+
const jobDir = env.OUTERLAYER_JOB_DIR;
|
|
22
|
+
if ("reason" in parsed || !jobDir) {
|
|
23
|
+
throw new Error("a session sender runs inside a runner job: OUTERLAYER_WORK must name the item and OUTERLAYER_JOB_DIR the job");
|
|
24
|
+
}
|
|
25
|
+
const home = opts.home ?? homeFromEnv(env);
|
|
26
|
+
const uploader = new LiveUploader({
|
|
27
|
+
home,
|
|
28
|
+
env,
|
|
29
|
+
fetchImpl: opts.fetchImpl,
|
|
30
|
+
sleepImpl: opts.sleepImpl,
|
|
31
|
+
debounceMs: opts.debounceMs,
|
|
32
|
+
stderr,
|
|
33
|
+
build: { item: parsed.number, jobDir },
|
|
34
|
+
onUnauthorized: opts.onFatal
|
|
35
|
+
});
|
|
36
|
+
const onLog = (event) => {
|
|
37
|
+
if (event.type === "changed" && event.file !== void 0) uploader.notify(event.file);
|
|
38
|
+
if (event.type === "error") stderr(`error ${event.file}: ${event.detail}
|
|
39
|
+
`);
|
|
40
|
+
};
|
|
41
|
+
const daemon = new CaptureDaemon({ ...opts.root === void 0 ? {} : { root: opts.root }, watchOnly: true, rescanMs: RESCAN_MS, onLog });
|
|
42
|
+
const pidfile = senderPidfile(home, parsed.number);
|
|
43
|
+
try {
|
|
44
|
+
mkdirSync(spoolDir(home), { recursive: true });
|
|
45
|
+
writeFileSync(pidfile, String(process.pid));
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
await daemon.start();
|
|
49
|
+
let stopped;
|
|
50
|
+
return {
|
|
51
|
+
stop: () => stopped ??= (async () => {
|
|
52
|
+
daemon.rescanNow();
|
|
53
|
+
await uploader.stop(opts.stopWaitMs ?? SENDER_STOP_WAIT_MS);
|
|
54
|
+
await daemon.stop();
|
|
55
|
+
rmSync(pidfile, { force: true });
|
|
56
|
+
})()
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async function runSessionSender() {
|
|
60
|
+
let sender;
|
|
61
|
+
const exit = async (code) => {
|
|
62
|
+
await sender?.stop().catch(() => void 0);
|
|
63
|
+
process.exit(code);
|
|
64
|
+
};
|
|
65
|
+
sender = await startSessionSender({ onFatal: () => void exit(1) });
|
|
66
|
+
process.on("SIGTERM", () => void exit(0));
|
|
67
|
+
process.on("SIGINT", () => void exit(0));
|
|
68
|
+
process.on("unhandledRejection", (reason) => {
|
|
69
|
+
process.stderr.write(`session-sender: unhandled error, continuing: ${String(reason)}
|
|
70
|
+
`);
|
|
71
|
+
});
|
|
72
|
+
await new Promise(() => void 0);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { runSessionSender, senderPidfile, startSessionSender };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { readPin, pinExists, readBuildHold, readContextManifest, manifestFilesPresent, writeDegraded, writeSourceCache, SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS, isLastCheckFresh, readDegraded, touchLastCheck, clearDegraded, writeContextManifest, writePinIfChanged, readSourceCache, isSourceCacheFresh } from './chunk-WIOZAJ2W.js';
|
|
4
|
-
import { loadMaintainedFiles, writeMaintainedFiles } from './chunk-WO2BXCTQ.js';
|
|
5
|
-
import { parseGoverns, normalizeRepoName, CONTEXT_SOURCE_CONFIG_PATH, parseOuterlayerConfig, classifyTree, emitTree } from './chunk-EMY4I27X.js';
|
|
6
3
|
import { appendContextProvenance } from './chunk-ZJSNLVCE.js';
|
|
4
|
+
import { readPin, pinExists, readBuildHold, readContextManifest, manifestFilesPresent, writeDegraded, writeSourceCache, SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS, isLastCheckFresh, readDegraded, touchLastCheck, clearDegraded, writeContextManifest, writePinIfChanged, readSourceCache, isSourceCacheFresh } from './chunk-ZZXLM64C.js';
|
|
5
|
+
import { loadMaintainedFiles, writeMaintainedFiles } from './chunk-VXLJORHL.js';
|
|
6
|
+
import { parseGoverns, normalizeRepoName, CONTEXT_SOURCE_CONFIG_PATH, parseOuterlayerConfig, classifyTree, emitTree } from './chunk-7ZR5UPMB.js';
|
|
7
7
|
import { defaultFetch } from './chunk-BCLJSQCV.js';
|
|
8
8
|
import { resolveGatewayCredentials } from './chunk-BOLTI6LR.js';
|
|
9
9
|
import { ensureContextExcludes, ensurePreCommitGuardHook } from './chunk-XCCLFVXM.js';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { isPlaceholderCommand } from './chunk-
|
|
4
|
-
import { describeCallFailure } from './chunk-
|
|
5
|
-
import { WorkCommandError, getJson } from './chunk-
|
|
6
|
-
import { sanitizeTerminal } from './chunk-
|
|
7
|
-
import { builtinKind } from './chunk-
|
|
8
|
-
import {
|
|
3
|
+
import { isPlaceholderCommand } from './chunk-6QWV3YDJ.js';
|
|
4
|
+
import { describeCallFailure } from './chunk-E4PIFIC3.js';
|
|
5
|
+
import { WorkCommandError, getJson } from './chunk-DGAIVMMN.js';
|
|
6
|
+
import { sanitizeTerminal } from './chunk-ZYHKJKSU.js';
|
|
7
|
+
import { builtinKind } from './chunk-DTXOJDEH.js';
|
|
8
|
+
import { CLAUDE_CREDENTIAL_VARIABLES, claudeCredential } from './chunk-OD2VTIHN.js';
|
|
9
9
|
import { signerFor } from './chunk-M5POMKKW.js';
|
|
10
10
|
|
|
11
11
|
createRequire(import.meta.url);
|
|
@@ -27,6 +27,18 @@ function needsClaudeCredential(hooks) {
|
|
|
27
27
|
var CREDENTIAL_FIX = "Run `claude setup-token`, then start the runner with CLAUDE_CODE_OAUTH_TOKEN set to the token it prints (or ANTHROPIC_API_KEY set to an API key), for example with EnvironmentFile= in the runner's systemd unit";
|
|
28
28
|
function claudeCredentialCheck(env, options = {}) {
|
|
29
29
|
const name = "Claude credential";
|
|
30
|
+
if (options.service !== void 0) {
|
|
31
|
+
const { unit, variables } = options.service;
|
|
32
|
+
if (variables.length === 0) {
|
|
33
|
+
return {
|
|
34
|
+
name,
|
|
35
|
+
status: "fail",
|
|
36
|
+
detail: `the service ${unit} has no Claude credential: neither ${CLAUDE_CREDENTIAL_VARIABLES.slice(0, 2).join(" nor ")} is set in its runner.env, so every build fails at provision as agent_credential_missing`,
|
|
37
|
+
fix: `${CREDENTIAL_FIX}, then restart the service: sudo systemctl restart ${unit}.`
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { name, status: "pass", detail: `${variables[0]} is set in the runner.env of the service ${unit}` };
|
|
41
|
+
}
|
|
30
42
|
const held = claudeCredential(env);
|
|
31
43
|
if (held === void 0) {
|
|
32
44
|
return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { detectMachine, supportsContainerBuilds, reachDockerStep, describeMachineDocker, supportsMicroVmBuilds } from './chunk-
|
|
4
|
-
import { MIN_ENGINE_MAJOR } from './chunk-
|
|
3
|
+
import { detectMachine, supportsContainerBuilds, reachDockerStep, describeMachineDocker, supportsMicroVmBuilds } from './chunk-AQ54MKSQ.js';
|
|
4
|
+
import { MIN_ENGINE_MAJOR } from './chunk-E4PIFIC3.js';
|
|
5
5
|
import { RUNNER_DIR_MODE, runnerDir, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
|
|
6
6
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
7
7
|
import { dirname } from 'path';
|
|
@@ -1,29 +1,105 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import {
|
|
4
|
-
import { LaunchFilter } from './chunk-BCJNHMZT.js';
|
|
3
|
+
import { LaunchFilter } from './chunk-HHJDN6AU.js';
|
|
5
4
|
import { RepoFilter } from './chunk-TBT347UY.js';
|
|
6
5
|
import { hookExecSpoolPath } from './chunk-F7CU5ABH.js';
|
|
7
6
|
import { applyReportedCost } from './chunk-BKO6JEZI.js';
|
|
8
|
-
import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-
|
|
9
|
-
import { tailTranscript, enrichSessionRepo,
|
|
7
|
+
import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-ZYHKJKSU.js';
|
|
8
|
+
import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-D6FXM5OW.js';
|
|
10
9
|
import { C } from './chunk-YRYMNBGQ.js';
|
|
11
|
-
import { readLaunchSpoolState, launchStatusIsDecisive, launchedItemsBySession, launchesPath } from './chunk-
|
|
10
|
+
import { readLaunchSpoolState, launchStatusIsDecisive, launchedItemsBySession, launchesPath } from './chunk-GQD22KKN.js';
|
|
12
11
|
import { readLatestProvenanceForSession, readContextProvenance } from './chunk-ZJSNLVCE.js';
|
|
13
12
|
import { defaultFetch } from './chunk-BCLJSQCV.js';
|
|
14
13
|
import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-BOLTI6LR.js';
|
|
14
|
+
import { CAPTURE_TIERS, inferArtifactKind, bannedPathsForTier, downconvertSession, isHumanUserTurn, turnCountBound, EVENT_TYPES } from './chunk-YNJEOGDL.js';
|
|
15
15
|
import { readCloudConfig, cloudConfigPath, writeCloudConfig } from './chunk-EABW6AJQ.js';
|
|
16
16
|
import { signerFor, signRunnerRequest } from './chunk-M5POMKKW.js';
|
|
17
|
-
import { writeSyncStatus, spoolDir } from './chunk-
|
|
17
|
+
import { writeSyncStatus, spoolDir } from './chunk-JPR2WHHF.js';
|
|
18
18
|
import { removeJournalHooks } from './chunk-XCCLFVXM.js';
|
|
19
19
|
import { readRepoConnection, findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
20
20
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
21
21
|
import { dirname, basename, join } from 'path';
|
|
22
22
|
import { statSync, readFileSync, readdirSync, mkdirSync, writeFileSync, renameSync, existsSync, rmSync } from 'fs';
|
|
23
|
+
import { execFileSync } from 'child_process';
|
|
24
|
+
import { hostname, userInfo } from 'os';
|
|
23
25
|
import { fileURLToPath } from 'url';
|
|
24
26
|
import { createRequire as createRequire$1 } from 'module';
|
|
25
27
|
|
|
26
28
|
createRequire(import.meta.url);
|
|
29
|
+
function detectCi(env = process.env) {
|
|
30
|
+
return env.CI === "true" || env.CI === "1" || !!env.JENKINS_URL;
|
|
31
|
+
}
|
|
32
|
+
function gitGlobalConfig(key) {
|
|
33
|
+
try {
|
|
34
|
+
return execFileSync("git", ["config", "--global", key], { encoding: "utf8", timeout: 2e3 }).trim() || void 0;
|
|
35
|
+
} catch {
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function installScrubContext(home, scrub) {
|
|
40
|
+
setCustomScrubbers(scrub);
|
|
41
|
+
setLocalIdentity({
|
|
42
|
+
name: gitGlobalConfig("user.name"),
|
|
43
|
+
email: gitGlobalConfig("user.email"),
|
|
44
|
+
username: (() => {
|
|
45
|
+
try {
|
|
46
|
+
return userInfo().username;
|
|
47
|
+
} catch {
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
50
|
+
})(),
|
|
51
|
+
hostname: (() => {
|
|
52
|
+
try {
|
|
53
|
+
return hostname();
|
|
54
|
+
} catch {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
})(),
|
|
58
|
+
home
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function applyContextProvenance(session, record) {
|
|
62
|
+
if (!record) return;
|
|
63
|
+
if (record.repository !== void 0) session.env.contextSourceRepo = record.repository;
|
|
64
|
+
if (record.ref !== void 0) session.env.contextSourceRef = record.ref;
|
|
65
|
+
if (record.sha !== null) session.env.contextSourceSha = record.sha;
|
|
66
|
+
session.env.contextSourceVerification = record.verification;
|
|
67
|
+
}
|
|
68
|
+
function prepareSessionForUpload(session, tier, envVars = process.env) {
|
|
69
|
+
setRepoRoot(resolveRepoIdentity(session.env.cwd).repoRoot);
|
|
70
|
+
scrubDeep(session);
|
|
71
|
+
session.workerKind ??= detectCi(envVars) ? "ci" : "seat";
|
|
72
|
+
return downconvertSession(session, tier);
|
|
73
|
+
}
|
|
74
|
+
var MAX_TOOL_IO_CHARS = 64e3;
|
|
75
|
+
function sliceToolIo(value) {
|
|
76
|
+
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
77
|
+
if (typeof text !== "string" || text.length <= MAX_TOOL_IO_CHARS) return value;
|
|
78
|
+
return `${text.slice(0, MAX_TOOL_IO_CHARS)}\u2026[sliced: ${text.length} chars exceeded the sync per-call cap]`;
|
|
79
|
+
}
|
|
80
|
+
function sliceTurnIo(turn) {
|
|
81
|
+
return {
|
|
82
|
+
...turn,
|
|
83
|
+
toolCalls: (turn.toolCalls ?? []).map((call) => ({
|
|
84
|
+
...call,
|
|
85
|
+
...call.input !== void 0 ? { input: sliceToolIo(call.input) } : {},
|
|
86
|
+
...call.output !== void 0 ? { output: sliceToolIo(call.output) } : {}
|
|
87
|
+
}))
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function wholeSessionCounts(turns) {
|
|
91
|
+
const allCalls = turns.flatMap((t) => t.toolCalls ?? []);
|
|
92
|
+
return {
|
|
93
|
+
// An index BOUND (`1 + max(turn.index)`), not the shipped turn count —
|
|
94
|
+
// turn indexes are not guaranteed dense from zero, and a late artifact
|
|
95
|
+
// anchored at its real turn index must still validate against the
|
|
96
|
+
// summary row this produces (see turnCountBound).
|
|
97
|
+
turns: turnCountBound(turns),
|
|
98
|
+
toolCalls: allCalls.length,
|
|
99
|
+
errors: allCalls.filter((c) => c.status === "error").length,
|
|
100
|
+
userTurns: turns.filter(isHumanUserTurn).length
|
|
101
|
+
};
|
|
102
|
+
}
|
|
27
103
|
var IRRELEVANT = /* @__PURE__ */ new Set(["__tests__", "__mocks__", "node_modules"]);
|
|
28
104
|
function newestSourceMs(dir) {
|
|
29
105
|
let newest = 0;
|
|
@@ -481,6 +557,8 @@ function retryAfterMs(response) {
|
|
|
481
557
|
}
|
|
482
558
|
var LiveUploader = class {
|
|
483
559
|
constructor(opts) {
|
|
560
|
+
/** Set by `stop()`: no more debounce or retry waits, so what is pending is sent once and given up on. */
|
|
561
|
+
this.stopping = false;
|
|
484
562
|
this.cycles = /* @__PURE__ */ new Map();
|
|
485
563
|
/** Sessions this sender is done with: ended, or permanently rejected. The
|
|
486
564
|
* daemon keeps mirroring a transcript after its session closes, and every
|
|
@@ -491,12 +569,31 @@ var LiveUploader = class {
|
|
|
491
569
|
* every session, so the sender stops entirely for the rest of this
|
|
492
570
|
* process rather than retrying session by session. */
|
|
493
571
|
this.stopped = false;
|
|
572
|
+
this.warned = /* @__PURE__ */ new Set();
|
|
494
573
|
this.home = opts.home;
|
|
495
574
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
496
575
|
this.sleepImpl = opts.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
497
576
|
this.debounceMs = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
498
577
|
this.env = opts.env ?? process.env;
|
|
499
578
|
this.stderr = opts.stderr ?? ((s) => void process.stderr.write(s));
|
|
579
|
+
this.build = opts.build;
|
|
580
|
+
this.onUnauthorized = opts.onUnauthorized;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Final pass for a build sender: sends what is pending now, without waiting out the debounce and without
|
|
584
|
+
* retrying a failed send, and resolves once every cycle has finished or `timeoutMs` has passed.
|
|
585
|
+
*/
|
|
586
|
+
async stop(timeoutMs) {
|
|
587
|
+
this.stopping = true;
|
|
588
|
+
let timer;
|
|
589
|
+
const expired = new Promise((resolve) => {
|
|
590
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
591
|
+
});
|
|
592
|
+
try {
|
|
593
|
+
await Promise.race([this.flush(), expired]);
|
|
594
|
+
} finally {
|
|
595
|
+
clearTimeout(timer);
|
|
596
|
+
}
|
|
500
597
|
}
|
|
501
598
|
/** Called for every daemon `mirrored` event. Cheap and synchronous — the
|
|
502
599
|
* actual read/send work happens on the debounced cycle. */
|
|
@@ -536,7 +633,7 @@ var LiveUploader = class {
|
|
|
536
633
|
for (; ; ) {
|
|
537
634
|
for (; ; ) {
|
|
538
635
|
const generationAtWaitStart = cycle.generation;
|
|
539
|
-
await this.sleepImpl(this.debounceMs);
|
|
636
|
+
if (!this.stopping) await this.sleepImpl(this.debounceMs);
|
|
540
637
|
if (cycle.generation !== generationAtWaitStart) continue;
|
|
541
638
|
break;
|
|
542
639
|
}
|
|
@@ -562,8 +659,21 @@ var LiveUploader = class {
|
|
|
562
659
|
const priorEntry = state.sessions[key];
|
|
563
660
|
if (priorEntry?.ended || priorEntry?.failed) return true;
|
|
564
661
|
const cloudConfig = readCloudConfig(this.home);
|
|
565
|
-
|
|
566
|
-
if (
|
|
662
|
+
let destination;
|
|
663
|
+
if (this.build === void 0) {
|
|
664
|
+
if (cloudConfig.autoSync === false) return false;
|
|
665
|
+
if (!cloudConfig.url || !(cloudConfig.apiKey || cloudConfig.accountToken)) return false;
|
|
666
|
+
} else {
|
|
667
|
+
const url = this.env.OUTERLAYER_URL;
|
|
668
|
+
const credential = this.env.OUTERLAYER_API_KEY;
|
|
669
|
+
const appId = this.env.OUTERLAYER_APP_ID;
|
|
670
|
+
if (!url || !credential || !appId) {
|
|
671
|
+
this.warnOnce("incomplete", `live upload is idle: this build's environment lacks ${[!url && "OUTERLAYER_URL", !credential && "OUTERLAYER_API_KEY", !appId && "OUTERLAYER_APP_ID"].filter(Boolean).join(", ")}, so nothing streams until \`outerlayer sync\` runs.
|
|
672
|
+
`);
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
destination = { ok: true, url, credential, appId, kind: "key" };
|
|
676
|
+
}
|
|
567
677
|
const info = subagentInfo(mirrorPath);
|
|
568
678
|
const tail = tailTranscript(mirrorPath, void 0, {
|
|
569
679
|
isSubagent: info.isSubagent,
|
|
@@ -573,17 +683,16 @@ var LiveUploader = class {
|
|
|
573
683
|
if (!tail.session) return false;
|
|
574
684
|
const session = tail.session;
|
|
575
685
|
enrichSessionRepo(session);
|
|
576
|
-
|
|
686
|
+
destination ??= resolveGatewayCredentials({ home: this.home, env: {}, cwd: session.env.cwd ?? "/" });
|
|
577
687
|
if (!destination.ok) return false;
|
|
578
|
-
|
|
579
|
-
if (!repoFilter.allows(session.env.gitRepo)) return false;
|
|
688
|
+
if (this.build === void 0 && !new RepoFilter(cloudConfig.repos).allows(session.env.gitRepo)) return false;
|
|
580
689
|
const launchSpool = readLaunchSpoolState(this.home);
|
|
581
690
|
if (!launchStatusIsDecisive(launchSpool)) return false;
|
|
582
|
-
const launchFilter = new LaunchFilter(launchSpool.ids);
|
|
691
|
+
const launchFilter = new LaunchFilter(launchSpool.ids, launchSpool.records, this.build);
|
|
583
692
|
if (!launchFilter.allows(session)) return false;
|
|
584
693
|
let tier;
|
|
585
694
|
try {
|
|
586
|
-
tier = resolveTier(cloudConfig.tier);
|
|
695
|
+
tier = resolveTier(this.build === void 0 ? cloudConfig.tier : this.env.OUTERLAYER_TIER ?? cloudConfig.tier);
|
|
587
696
|
} catch (err) {
|
|
588
697
|
if (err instanceof SyncConfigError) return false;
|
|
589
698
|
throw err;
|
|
@@ -613,6 +722,7 @@ var LiveUploader = class {
|
|
|
613
722
|
const outcome = await this.sendWithRetry(endpoint, destination.credential, destination.appId, prepared.id, payload);
|
|
614
723
|
if (outcome === "unauthorized") {
|
|
615
724
|
this.stopped = true;
|
|
725
|
+
this.onUnauthorized?.();
|
|
616
726
|
this.stderr(
|
|
617
727
|
destination.credential.startsWith("olu_") ? `live upload stopped: the login was refused (expired or revoked). No session streams from this daemon until you run \`outerlayer login\` and it restarts.
|
|
618
728
|
` : `live upload stopped: the API key was refused. No session streams from this daemon until it restarts with a valid key.
|
|
@@ -620,8 +730,9 @@ var LiveUploader = class {
|
|
|
620
730
|
);
|
|
621
731
|
return true;
|
|
622
732
|
}
|
|
733
|
+
if (outcome === "unreachable") return false;
|
|
623
734
|
if (outcome === "rejected") {
|
|
624
|
-
const reason =
|
|
735
|
+
const reason = `the server refused this delta permanently${this.lastRejectStatus === void 0 ? "" : ` (HTTP ${this.lastRejectStatus})`}`;
|
|
625
736
|
this.mergeSessionState(key, {
|
|
626
737
|
ackedTurnIndex: currentAcked,
|
|
627
738
|
failed: true,
|
|
@@ -654,6 +765,7 @@ var LiveUploader = class {
|
|
|
654
765
|
* key bound to a host key is refused on every route unless that key signed the request. A login token is
|
|
655
766
|
* never signed: only the saved factory key is bound to a host key. */
|
|
656
767
|
async signatureHeaders(endpoint, body, credential) {
|
|
768
|
+
if (this.build !== void 0) return {};
|
|
657
769
|
if (readCloudConfig(this.home).apiKey !== credential) return {};
|
|
658
770
|
let signer;
|
|
659
771
|
try {
|
|
@@ -664,6 +776,12 @@ var LiveUploader = class {
|
|
|
664
776
|
if (signer === void 0) return {};
|
|
665
777
|
return { ...await signRunnerRequest({ method: "POST", url: endpoint, body: new TextEncoder().encode(body), signer }) };
|
|
666
778
|
}
|
|
779
|
+
/** Writes `message` the first time `key` is seen, so a condition that holds on every fire logs once. */
|
|
780
|
+
warnOnce(key, message) {
|
|
781
|
+
if (this.warned.has(key)) return;
|
|
782
|
+
this.warned.add(key);
|
|
783
|
+
this.stderr(message);
|
|
784
|
+
}
|
|
667
785
|
/** Sends one request, retrying a network error or a retryable 5xx forever
|
|
668
786
|
* with exponential backoff (2s → 60s cap) — the daemon runs unattended, so
|
|
669
787
|
* there is no bounded attempt count. A 401 or a permanent 4xx/rejection
|
|
@@ -673,6 +791,7 @@ var LiveUploader = class {
|
|
|
673
791
|
let delayMs = RETRY_BASE_DELAY_MS;
|
|
674
792
|
for (; ; ) {
|
|
675
793
|
let response;
|
|
794
|
+
let failure = "";
|
|
676
795
|
try {
|
|
677
796
|
const signature = await this.signatureHeaders(endpoint, body, apiKey);
|
|
678
797
|
response = await this.fetchImpl(endpoint, {
|
|
@@ -685,18 +804,30 @@ var LiveUploader = class {
|
|
|
685
804
|
},
|
|
686
805
|
body
|
|
687
806
|
});
|
|
688
|
-
} catch {
|
|
807
|
+
} catch (err) {
|
|
689
808
|
response = void 0;
|
|
809
|
+
failure = err instanceof Error ? err.message : String(err);
|
|
690
810
|
}
|
|
691
811
|
if (response) {
|
|
692
812
|
if (response.status === 401) return "unauthorized";
|
|
693
813
|
if (response.ok) {
|
|
694
814
|
const accepted = await acceptedSessionIds(response);
|
|
695
|
-
if (accepted)
|
|
815
|
+
if (accepted) {
|
|
816
|
+
if (accepted.includes(sessionId)) return "accepted";
|
|
817
|
+
this.lastRejectStatus = void 0;
|
|
818
|
+
return "rejected";
|
|
819
|
+
}
|
|
696
820
|
} else if (!RETRYABLE_STATUSES.has(response.status)) {
|
|
821
|
+
this.lastRejectStatus = response.status;
|
|
697
822
|
return "rejected";
|
|
698
823
|
}
|
|
699
824
|
}
|
|
825
|
+
if (this.build !== void 0) {
|
|
826
|
+
const why = response === void 0 ? failure : `the gateway answered ${response.status}`;
|
|
827
|
+
this.stderr(`live upload of session ${sessionId} failed: ${why}${this.stopping ? "" : " \xB7 retrying"}
|
|
828
|
+
`);
|
|
829
|
+
}
|
|
830
|
+
if (this.stopping) return "unreachable";
|
|
700
831
|
await this.sleepImpl(Math.max(retryAfterMs(response), delayMs));
|
|
701
832
|
delayMs = Math.min(delayMs * 2, RETRY_MAX_DELAY_MS);
|
|
702
833
|
}
|
|
@@ -1683,4 +1814,4 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
|
|
|
1683
1814
|
}
|
|
1684
1815
|
}
|
|
1685
1816
|
|
|
1686
|
-
export { CHUNK_PART_TARGET_BYTES, LiveUploader, RETRYABLE_STATUSES, SyncConfigError, SyncTransportError, isLoopbackUrl, resolveTier, runSync, staleBuildRefusal, staleBuildWarning };
|
|
1817
|
+
export { CHUNK_PART_TARGET_BYTES, LiveUploader, RETRYABLE_STATUSES, SyncConfigError, SyncTransportError, detectCi, installScrubContext, isLoopbackUrl, resolveTier, runSync, staleBuildRefusal, staleBuildWarning };
|