loki-mode 11.17.58 → 11.17.60
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 +13 -4
- package/SKILL.md +3 -3
- package/VERSION +1 -1
- package/autonomy/lib/engine10-push.sh +99 -5
- package/autonomy/lib/proof_redact.py +26 -2
- package/autonomy/loki +384 -9
- package/autonomy/run.sh +11 -1
- package/autonomy/telemetry.sh +2 -2
- package/bin/loki +105 -0
- package/completions/_loki +13 -0
- package/completions/loki.bash +15 -1
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +3 -1
- package/docs/COMPARE-OPENCODE.md +18 -6
- package/docs/INSTALLATION.md +6 -2
- package/docs/QUICKSTART.md +7 -0
- package/docs/v10/CLI-MODERN.md +5 -3
- package/docs/v10/FAILURE-CLASSES.md +13 -0
- package/loki-ts/dist/cockpit.js +10 -10
- package/loki-ts/dist/loki.js +1243 -1231
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +23 -8
- package/packages/control-plane/ui/dist/assets/{index-CxZWgwie.css → index-DeSVrWgd.css} +1 -1
- package/packages/control-plane/ui/dist/assets/{index-u8cd6TRQ.js → index-Dww-8G06.js} +10 -10
- package/packages/control-plane/ui/dist/index.html +2 -2
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-BKyeQyvY.js → AdminPage-DUuYBy7U.js} +1 -1
- package/web-app/dist/assets/{Avatar-Dc4GsZ99.js → Avatar-apqPQq7o.js} +1 -1
- package/web-app/dist/assets/{Badge-CmDNBkNC.js → Badge-BCKC5rw5.js} +1 -1
- package/web-app/dist/assets/{Button-_HvRTrtz.js → Button-Ii2qz5ro.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-CivGuFnR.js → CockpitPage-DCRCbvnY.js} +1 -1
- package/web-app/dist/assets/{ComparePage-WPTJgORO.js → ComparePage-BoEYx63C.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-CLEe9Zqz.js → ErrorBoundary-XXVx8_4-.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-DAHnqKD9.js → EvidenceReceiptPanel-k40RtZB_.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-B5b-um7U.js → GitHubIssuesPanel-BgPkc-75.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-MhdImRpI.js → GitHubPRsPanel-CwhorB4G.js} +1 -1
- package/web-app/dist/assets/{HomePage-CvvJdIZn.js → HomePage-CIZPEgSM.js} +2 -2
- package/web-app/dist/assets/{LoginPage-Byj8MMzz.js → LoginPage-DZSoNZ3y.js} +1 -1
- package/web-app/dist/assets/{MagicPage-C-5_-KNn.js → MagicPage-eThBQUgO.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-Vt6R8gT2.js → MetricsPage-BWtdAivK.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-3ZuTdABy.js → NotFoundPage-CNeOANG9.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-DT4gY_or.js → ProjectPage-CdFR-RTO.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DHxhds01.js → ProjectsPage-BPdXEovl.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DIOVf1vJ.js → SettingsPage-B3vsyBHf.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-JEvMj4vs.js → ShowcasePage-DhtEGTId.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-C2zCt5HQ.js → SystemSettingsPage-CCRMF5oP.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-C5RgWC4V.js → TeamsPage-B5jpVrT8.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-Dp2yg177.js → TemplatesPage-B0NOrY9y.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Ci63VyQZ.js → TerminalOutput-Y7S9_kCF.js} +1 -1
- package/web-app/dist/assets/{activity-BIP9izqH.js → activity-DDGuxXV8.js} +1 -1
- package/web-app/dist/assets/{bell-DLap5m5M.js → bell-v1a0v0aO.js} +1 -1
- package/web-app/dist/assets/{bot-YX6dHd86.js → bot-Dh9apAJT.js} +1 -1
- package/web-app/dist/assets/{check-CooDPKFF.js → check-BIqFwS-y.js} +1 -1
- package/web-app/dist/assets/{chevron-left-BcmSPVT2.js → chevron-left-EBM7yWz9.js} +1 -1
- package/web-app/dist/assets/{circle-alert-CSmTAxJL.js → circle-alert-Czhu4uRg.js} +1 -1
- package/web-app/dist/assets/{clock-DFweV2mu.js → clock-BdJ55LJy.js} +1 -1
- package/web-app/dist/assets/{cloud-BtPtouD8.js → cloud-CC4nJD4m.js} +1 -1
- package/web-app/dist/assets/{code-xml-V628GOYR.js → code-xml-2TmM3ZXT.js} +1 -1
- package/web-app/dist/assets/{database-ChLXHopT.js → database-0TieR4KZ.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-DIZWYLK1.js → dollar-sign-pTh1osY0.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-Jy00ateq.js → file-code-corner-DQ8FfkCJ.js} +1 -1
- package/web-app/dist/assets/{file-plus-fiEcUbz1.js → file-plus-CYgNMECJ.js} +1 -1
- package/web-app/dist/assets/{globe-DKpU9VEw.js → globe-KQA_9R6O.js} +1 -1
- package/web-app/dist/assets/{hammer-Bhw-veCn.js → hammer-CZMQWpu3.js} +1 -1
- package/web-app/dist/assets/{index-BzFRyGry.js → index-BRCtE1U8.js} +3 -3
- package/web-app/dist/assets/{layers-CQWnlC67.js → layers-CPYePIbK.js} +1 -1
- package/web-app/dist/assets/{loader-circle-CZle8WBp.js → loader-circle-DmvGxkYe.js} +1 -1
- package/web-app/dist/assets/{lock-C2GowAkd.js → lock-BTKaeWpE.js} +1 -1
- package/web-app/dist/assets/{package-Q2oBgqlC.js → package-Blxc3ohw.js} +1 -1
- package/web-app/dist/assets/{plus-BH1scfMy.js → plus-DxtJyvK_.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-B8tSKF5G.js → refresh-cw-hA4AfO_9.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-C4WOu54e.js → rotate-ccw-CEqVnL9N.js} +1 -1
- package/web-app/dist/assets/{scroll-text-Bub_kemX.js → scroll-text-tJPLGtKd.js} +1 -1
- package/web-app/dist/assets/{server-D1OdElLz.js → server-TuWGPUjH.js} +1 -1
- package/web-app/dist/assets/{shield-alert-xnpxbh6i.js → shield-alert-C8d0Z4Ax.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DGmIPcAx.js → thumbs-up-C6hv5hUr.js} +1 -1
- package/web-app/dist/assets/{trash-2-4iYf66Di.js → trash-2-Dk9XPU3k.js} +1 -1
- package/web-app/dist/assets/{trending-down-Ce1pcJ6W.js → trending-down-BJp7dvps.js} +1 -1
- package/web-app/dist/assets/{trending-up-S9Uk6w_R.js → trending-up-Rh6ULiPp.js} +1 -1
- package/web-app/dist/assets/{upload-Bx2aCcjx.js → upload-CRP_Npdp.js} +1 -1
- package/web-app/dist/assets/{usePolling-D2WAlKAr.js → usePolling-UXubXtkC.js} +1 -1
- package/web-app/dist/assets/{user-DyuaLsHp.js → user-AiyHiPpF.js} +1 -1
- package/web-app/dist/index.html +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,14 @@ The npm package installs the Bun runtime the engine needs as an optional depende
|
|
|
26
26
|
Want to see a result before you install or spend anything?
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npx loki-mode
|
|
29
|
+
npx loki-mode start # no git repo: a real receipt, no key, no spend
|
|
30
|
+
npx loki-mode tour # the same receipt when you are already inside a repo
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`npx` downloads the package the first time, so that step needs the network. The receipt itself replays offline and checks with no provider and no key.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx loki-mode wow # a provider is ready: one tiny real task in a temp repo, its own receipt
|
|
30
37
|
```
|
|
31
38
|
|
|
32
39
|
## First run
|
|
@@ -57,7 +64,7 @@ Every run starts from a delivery contract derived from your task, writes checks
|
|
|
57
64
|
loki verify # newest run; or: loki verify <run-id>
|
|
58
65
|
```
|
|
59
66
|
|
|
60
|
-
`loki verify` re-hashes the receipt, checks its signature and checks it against the run's event log. It prints a verdict and exits 0 (VERIFIED), 1 (TAMPERED), 2 (UNCHECKED), 3 (UNSIGNED, refused unless `--allow-unsigned`), 4 (run outcome not verified) or 66 (no runs); `[ $rc -eq 0 ]` is the only safe test for "verified". Full table: [docs/exit-codes.md](docs/exit-codes.md). On another machine, export the public key with `loki keys export` and pass it with `loki verify --pubkey <file>`. A receipt claims only what its checks ran, and lists what they did not; signing proves who produced it, not that the code is bug-free. Details: [docs/SIGNED-RECEIPTS.md](docs/SIGNED-RECEIPTS.md).
|
|
67
|
+
`loki verify` re-hashes the receipt, checks its signature and checks it against the run's event log. It prints a verdict and exits 0 (VERIFIED), 1 (TAMPERED), 2 (UNCHECKED), 3 (UNSIGNED, refused unless `--allow-unsigned`), 4 (run outcome not verified) or 66 (no runs); `[ $rc -eq 0 ]` is the only safe test for "verified". `loki start` also requires fail-to-pass for a declared behavior change: a Wall test must fail on the old code and pass after, or the verdict stays PARTIAL (`LOKI_MUTATION_STRICT=0` opts out). Full table: [docs/exit-codes.md](docs/exit-codes.md). On another machine, export the public key with `loki keys export` and pass it with `loki verify --pubkey <file>`. A receipt claims only what its checks ran, and lists what they did not; signing proves who produced it, not that the code is bug-free. Details: [docs/SIGNED-RECEIPTS.md](docs/SIGNED-RECEIPTS.md).
|
|
61
68
|
|
|
62
69
|
For a multi-repo change set, `loki start --workspace <name> "<brief>"` seals one signed `composite.json` over every child receipt, and `loki verify <run-dir>/composite.json` re-checks it offline ([docs/WORKSPACES.md](docs/WORKSPACES.md)). A composite never reads higher than its weakest child.
|
|
63
70
|
|
|
@@ -91,7 +98,7 @@ Time: 4m12s (intake 11s, plan 20s, implement 3m10s, verify 31s)
|
|
|
91
98
|
|
|
92
99
|
| Command | What it does |
|
|
93
100
|
|---------|--------------|
|
|
94
|
-
| `loki start "<task>"` | The
|
|
101
|
+
| `loki start "<task>"` | The command you run: same provider model as a raw session, then the harness re-runs the checks and seals a receipt (`--no-pr`, `--provider`, `--max-cost`, `--haiku` to opt the first implement onto haiku). `loki "<task>"` and `loki owner/repo#N` are shorthand. No task and no git repo shows a receipt and spends nothing |
|
|
95
102
|
| `loki edit "<change>"` | One targeted change in one pass, no PR, plan or council (`--verify`, `--receipt`, `--commit`, `--dry-run`) |
|
|
96
103
|
| `loki quick "<task>"` | One small task with a verify loop, no PR |
|
|
97
104
|
| `loki chat` | Interactive alternative: Plan and Build modes, slash commands, `/run` starts a `loki start` run, `--attach` joins one |
|
|
@@ -106,7 +113,9 @@ Time: 4m12s (intake 11s, plan 20s, implement 3m10s, verify 31s)
|
|
|
106
113
|
| `loki doctor [--fix] [--json]` | Check prerequisites, providers and login; `--fix` applies safe fixes |
|
|
107
114
|
| `loki control serve` | Start the Control Plane (see below) |
|
|
108
115
|
| `loki dashboard` | Open the Control Plane UI, starting it if needed |
|
|
109
|
-
| `loki tour` | Show a sample receipt offline |
|
|
116
|
+
| `loki tour` | Show a sample receipt offline. `loki start` does this when you are not in a git repo and did not pass a task |
|
|
117
|
+
| `loki wow` | Same live mini-task as a normal start, in a temp repo. Offline sample when no provider is ready |
|
|
118
|
+
| `loki factory "<task>"` | Same as `loki start`. The policy line is printed by start |
|
|
110
119
|
| `loki --help` | Everything else; `loki help <command>` for one command |
|
|
111
120
|
|
|
112
121
|
Core verbs: [docs/CLI-CORE-VERBS.md](docs/CLI-CORE-VERBS.md). Every command (generated): [docs/CLI-REFERENCE.md](docs/CLI-REFERENCE.md). Engine guide, provider table, model routes, custom agents and chat: [docs/v10/GUIDE.md](docs/v10/GUIDE.md). Honest comparison with opencode: [docs/COMPARE-OPENCODE.md](docs/COMPARE-OPENCODE.md).
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: An autonomous software factory that knows what it is supposed to deliver, and proves it did. Use when the user says Loki Mode or asks to build, fix or verify software autonomously.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v11.17.
|
|
6
|
+
# Loki Mode v11.17.60
|
|
7
7
|
|
|
8
8
|
**Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the previous bash loop. PRD-file starts (`loki start ./prd.md`) and the opencode provider still run on it until FC38-SWEEP ports them; the legacy-engine command and the LOKI_ENGINE switch remain removed. Prefer `loki owner/repo#N` for issues.
|
|
9
9
|
|
|
@@ -200,7 +200,7 @@ This protocol governs **skill module** loading -- task-scoped instruction files
|
|
|
200
200
|
<!-- generated:commands -->
|
|
201
201
|
Commands (generated from `loki-ts/src/cli/registry.ts`; full reference in `docs/CLI-REFERENCE.md`):
|
|
202
202
|
|
|
203
|
-
`version`, `status`, `doctor`, `provider`, `memory`, `rollback`, `backup`, `restore`, `proof`, `wiki`, `control`, `intent`, `kpis`, `report`, `trust`, `crash`, `contract`, `start`, `slack`, `queue`, `issues`, `undo`, `verify-pr`, `answer`, `engine10`, `help`, `chat`, `edit`, `diff`, `models`, `setup`, `quick`, `quickstart`, `init`, `template`, `verify`, `keys`, `review`, `dashboard`, `config`, `mcp`, `acp`, `stop`, `pause`, `resume`, `cancel`, `why`, `next`, `logs`, `ship`, `deploy`, `import`, `github`, `issue`, `ci`, `modernize`, `share`, `assets`, `export`, `notify`, `tour`, `welcome`, `onboard`, `setup-skill`, `self-update`, `remote`, `cockpit`, `code`, `context`, `secrets`, `api`, `sandbox`, `docker`, `web`, `preview`, `telemetry`, `syslog`, `explain`, `docs`, `test`, `bench`, `voice`, `own`, `secure`, `compliance`, `enterprise`, `projects`, `audit`, `cost`, `metrics`, `sentrux`, `magic`, `steer`
|
|
203
|
+
`version`, `status`, `doctor`, `provider`, `memory`, `rollback`, `backup`, `restore`, `proof`, `wiki`, `control`, `intent`, `kpis`, `report`, `trust`, `crash`, `contract`, `start`, `slack`, `queue`, `issues`, `undo`, `verify-pr`, `answer`, `engine10`, `help`, `chat`, `edit`, `diff`, `models`, `setup`, `quick`, `quickstart`, `init`, `template`, `verify`, `keys`, `review`, `dashboard`, `config`, `mcp`, `acp`, `stop`, `pause`, `resume`, `cancel`, `why`, `next`, `logs`, `ship`, `deploy`, `import`, `github`, `issue`, `ci`, `modernize`, `share`, `assets`, `export`, `notify`, `tour`, `wow`, `factory`, `welcome`, `onboard`, `setup-skill`, `self-update`, `remote`, `cockpit`, `code`, `context`, `secrets`, `api`, `sandbox`, `docker`, `web`, `preview`, `telemetry`, `syslog`, `explain`, `docs`, `test`, `bench`, `voice`, `own`, `secure`, `compliance`, `enterprise`, `projects`, `audit`, `cost`, `metrics`, `sentrux`, `magic`, `steer`
|
|
204
204
|
<!-- /generated -->
|
|
205
205
|
|
|
206
206
|
---
|
|
@@ -483,4 +483,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
483
483
|
|
|
484
484
|
---
|
|
485
485
|
|
|
486
|
-
**v11.17.
|
|
486
|
+
**v11.17.60 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
11.17.
|
|
1
|
+
11.17.60
|
|
@@ -8,6 +8,16 @@
|
|
|
8
8
|
# engine10-push.sh issue-comment [--update] <issue-ref> <body-file>
|
|
9
9
|
# engine10-push.sh status <sha> <pending|success|failure|error> <description>
|
|
10
10
|
#
|
|
11
|
+
# Fork mode: when gh reports the authenticated user cannot push to the pinned
|
|
12
|
+
# repo (permissions.push false), push-pr pushes to that user's fork (created
|
|
13
|
+
# if missing, verified as a fork of the pinned repo owned by that user) and
|
|
14
|
+
# opens the PR upstream from <user>:<branch>. A draft run in fork mode pushes
|
|
15
|
+
# to the fork only and exits 4: Loki does not open draft PRs on a repository
|
|
16
|
+
# the user cannot push to. issue-comment (status and backstop comments) is a
|
|
17
|
+
# no-op there for the same reason; a receipt comment on the user's own PR is
|
|
18
|
+
# still posted. LOKI_PR_FORK=0 turns fork mode off (the direct push runs and
|
|
19
|
+
# fails as before). No token is needed beyond the user's gh/git login.
|
|
20
|
+
#
|
|
11
21
|
# Holds GitHub credentials, runs no LLM, reads no untrusted text. Inputs come
|
|
12
22
|
# only from argv and the origin pinned by P0 before any provider ran
|
|
13
23
|
# (_LOKI_ORIGIN_PINNED=1 _LOKI_PINNED_ORIGIN=<url>). The target repository is
|
|
@@ -128,6 +138,37 @@ _e10_find_pr() {
|
|
|
128
138
|
--jq "[.[] | select(.isCrossRepository == false and .state == \"OPEN\" and (.headRepositoryOwner.login | ascii_downcase) == \"$(printf '%s' "$owner" | tr '[:upper:]' '[:lower:]')\")] | .[0].url // empty"
|
|
129
139
|
}
|
|
130
140
|
|
|
141
|
+
# Fork mode probe. Prints true, false, or nothing (unknown: gh failed or did not say). Only a literal false means no push
|
|
142
|
+
# access; anything else keeps the direct path (fail open, so a gh hiccup never reroutes a push the user could make).
|
|
143
|
+
_e10_can_push() { _e10_gh api "repos/$_e10_repo" --jq '.permissions.push' 2>/dev/null | head -n 1; }
|
|
144
|
+
_e10_fork_off() { [ "${LOKI_PR_FORK:-}" = "0" ]; }
|
|
145
|
+
# Creates or finds the gh user's fork of the pinned repo; prints OWNER/REPO only after gh confirms it is a fork of
|
|
146
|
+
# that repo owned by that user. A fork can take a few seconds to appear after the create call.
|
|
147
|
+
_e10_fork() {
|
|
148
|
+
local me="$1" fork="" info i
|
|
149
|
+
fork="$(_e10_gh api -X POST "repos/$_e10_repo/forks" --jq .full_name 2>/dev/null | head -n 1)" || fork=""
|
|
150
|
+
[[ "$fork" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || return 1
|
|
151
|
+
for i in 1 2 3 4 5 6; do
|
|
152
|
+
info="$(_e10_gh api "repos/$fork" --jq '[(.fork|tostring), (.parent.full_name // ""), .owner.login] | join(" ")' 2>/dev/null)" || info=""
|
|
153
|
+
if [ -n "$info" ]; then
|
|
154
|
+
[ "$(printf '%s' "$info" | tr '[:upper:]' '[:lower:]')" = "$(printf 'true %s %s' "$_e10_repo" "$me" | tr '[:upper:]' '[:lower:]')" ] || return 1
|
|
155
|
+
printf '%s\n' "$fork"
|
|
156
|
+
return 0
|
|
157
|
+
fi
|
|
158
|
+
[ "$i" -lt 6 ] && sleep 2
|
|
159
|
+
done
|
|
160
|
+
return 3 # the fork exists by name but GitHub has not served it yet
|
|
161
|
+
}
|
|
162
|
+
# Fork-mode twin of _e10_find_pr: the open cross-repo PR whose head is <me>:<branch> (--head matches the branch name
|
|
163
|
+
# only; the jq owner filter is what pins it to <me>).
|
|
164
|
+
_e10_find_fork_pr() {
|
|
165
|
+
local me="$1"
|
|
166
|
+
case "$me" in '' | *[!A-Za-z0-9_.-]*) return 1 ;; esac
|
|
167
|
+
_e10_gh pr list --repo "$_e10_repo" --head "$2" --state open \
|
|
168
|
+
--json url,isCrossRepository,headRepositoryOwner,headRefName,state \
|
|
169
|
+
--jq "[.[] | select(.isCrossRepository == true and .state == \"OPEN\" and (.headRepositoryOwner.login | ascii_downcase) == \"$(printf '%s' "$me" | tr '[:upper:]' '[:lower:]')\")] | .[0].url // empty"
|
|
170
|
+
}
|
|
171
|
+
|
|
131
172
|
mode="${1:-}"
|
|
132
173
|
shift || true
|
|
133
174
|
case "$mode" in
|
|
@@ -157,6 +198,33 @@ case "$mode" in
|
|
|
157
198
|
printf 'local://%s#%s\n' "$_LOKI_PINNED_ORIGIN" "$branch"
|
|
158
199
|
exit 0
|
|
159
200
|
fi
|
|
201
|
+
if ! _e10_fork_off && [ "$(_e10_can_push)" = "false" ]; then
|
|
202
|
+
# A stacked parent published in fork mode lives on the fork, not upstream, so an upstream base would not exist.
|
|
203
|
+
[ "${#base[@]}" -eq 0 ] || die "no push access to $_e10_repo: a stacked PR (--base) needs push access there; rerun without LOKI_PR_BASE"
|
|
204
|
+
_e10_me="$(_e10_gh api user --jq .login 2>/dev/null)" || _e10_me=""
|
|
205
|
+
[[ "$_e10_me" =~ ^[A-Za-z0-9_.-]+$ ]] || die "no push access to $_e10_repo and the gh user could not be resolved for a fork; run gh auth login"
|
|
206
|
+
log_info "no push access to $_e10_repo: creating or reusing the fork under $_e10_me (LOKI_PR_FORK=0 turns this off)"
|
|
207
|
+
_e10_fork_repo="$(_e10_fork "$_e10_me")" || { [ "$?" -eq 3 ] && die "no push access to $_e10_repo: the fork under $_e10_me is still being created on GitHub; rerun to push once it exists"
|
|
208
|
+
die "no push access to $_e10_repo and its fork under $_e10_me could not be created or verified"; }
|
|
209
|
+
log_info "no push access to $_e10_repo: pushing to the fork $_e10_fork_repo"
|
|
210
|
+
_loki_trusted_push _loki_with_github_tokens "$dir" "$branch" "$_e10_fork_repo" || die "push to fork $_e10_fork_repo refused or failed (rc=$?)"
|
|
211
|
+
if [ "${#draft[@]}" -gt 0 ]; then
|
|
212
|
+
printf 'fork-only: pushed %s to %s; no draft PR is opened on %s, which you cannot push to. Open one yourself: gh pr create --repo %s --head %s:%s\n' \
|
|
213
|
+
"$branch" "$_e10_fork_repo" "$_e10_repo" "$_e10_repo" "$_e10_me" "$branch" >&2
|
|
214
|
+
exit 4
|
|
215
|
+
fi
|
|
216
|
+
url="$(_e10_find_fork_pr "$_e10_me" "$branch")" || die "gh pr list failed"
|
|
217
|
+
_e10_pr_state=reused
|
|
218
|
+
if [ -z "$url" ]; then
|
|
219
|
+
_e10_pr_state=created
|
|
220
|
+
url="$(_e10_gh pr create --repo "$_e10_repo" --head "$_e10_me:$branch" --title "$title" --body-file "$body" "${base[@]}")" \
|
|
221
|
+
|| die "gh pr create failed"
|
|
222
|
+
url="$(printf '%s\n' "$url" | tail -n 1)"
|
|
223
|
+
fi
|
|
224
|
+
printf '%s\n' "$_e10_pr_state"
|
|
225
|
+
printf '%s\n' "$url"
|
|
226
|
+
exit 0
|
|
227
|
+
fi
|
|
160
228
|
# RECOVERY: a resumed publish skips the push when the remote branch is already at the local commit.
|
|
161
229
|
_e10_want="$(_e10_git -C "$dir" rev-parse -q --verify "refs/heads/$branch" 2>/dev/null)" || _e10_want=""
|
|
162
230
|
_e10_have=""
|
|
@@ -207,6 +275,7 @@ case "$mode" in
|
|
|
207
275
|
# --update: the status projector. One comment carrying the marker below is edited in place (created when absent),
|
|
208
276
|
# so an issue shows live run state without a new comment per event. Only a comment written by the authenticated
|
|
209
277
|
# gh user counts, and the body file must carry the marker (it is built by the supervisor, never from task text).
|
|
278
|
+
# The PR receipt comment (<!-- loki-receipt -->) uses the same upsert: a PR number is an issue number to the API.
|
|
210
279
|
_e10_upd=""
|
|
211
280
|
[ "${1:-}" != "--update" ] || { _e10_upd=1; shift; }
|
|
212
281
|
[ "$#" -eq 2 ] || die "usage: issue-comment [--update] <issue-ref> <body-file>"
|
|
@@ -214,17 +283,42 @@ case "$mode" in
|
|
|
214
283
|
case "$num" in '' | *[!0-9]*) die "cannot extract issue number from: $1" ;; esac
|
|
215
284
|
[ -f "$2" ] || die "body file not found: $2"
|
|
216
285
|
[ -z "$_e10_local" ] || { log_info "local origin: no issue to comment on (no-op)"; exit 0; }
|
|
286
|
+
# Status and backstop comments are for a repo the user maintains; on one they cannot push to they would land on
|
|
287
|
+
# someone else's issue. The receipt comment rides the user's own PR and is still posted.
|
|
288
|
+
# The receipt body starts with its marker (receipt_surface.ts); only that first line exempts a comment, so issue
|
|
289
|
+
# text quoting the marker inside a status body cannot. Unlike push-pr this fails closed: a status or backstop
|
|
290
|
+
# comment is posted only when gh confirms push access, since a wrong guess is a public comment on someone's issue.
|
|
291
|
+
if [ "$(head -n 1 -- "$2")" != '<!-- loki-receipt -->' ] && [ "$(_e10_can_push)" != "true" ]; then
|
|
292
|
+
log_info "push access to $_e10_repo not confirmed: not commenting on its issue $num"
|
|
293
|
+
exit 0
|
|
294
|
+
fi
|
|
217
295
|
if [ -n "$_e10_upd" ]; then
|
|
218
|
-
_e10_marker=
|
|
219
|
-
|
|
296
|
+
_e10_marker=""
|
|
297
|
+
for _e10_m in '<!-- loki-status -->' '<!-- loki-receipt -->'; do
|
|
298
|
+
if grep -qF -- "$_e10_m" "$2"; then _e10_marker="$_e10_m"; break; fi
|
|
299
|
+
done
|
|
300
|
+
[ -n "$_e10_marker" ] || die "status body must carry <!-- loki-status --> or <!-- loki-receipt -->"
|
|
220
301
|
_e10_me="$(_e10_gh api user --jq .login 2>/dev/null)" || _e10_me=""
|
|
221
302
|
_e10_cid=""
|
|
303
|
+
# A receipt upsert never posts on a failed lookup: that would stack a duplicate and still report success.
|
|
304
|
+
if [ -z "$_e10_me" ] && [ "$_e10_marker" = '<!-- loki-receipt -->' ]; then die "cannot resolve the gh user for the receipt comment lookup"; fi
|
|
222
305
|
if [ -n "$_e10_me" ]; then
|
|
223
|
-
|
|
224
|
-
--jq ".[] | select(.user.login == \"$_e10_me\") | select(.body | contains(\"$_e10_marker\")) | .id" 2>/dev/null
|
|
306
|
+
if _e10_ids="$(_e10_gh api "repos/$_e10_repo/issues/$num/comments" --paginate \
|
|
307
|
+
--jq ".[] | select(.user.login == \"$_e10_me\") | select(.body | contains(\"$_e10_marker\")) | .id" 2>/dev/null)"; then
|
|
308
|
+
_e10_cid="$(printf '%s\n' "$_e10_ids" | head -n 1)"
|
|
309
|
+
elif [ "$_e10_marker" = '<!-- loki-receipt -->' ]; then
|
|
310
|
+
die "receipt comment lookup failed"
|
|
311
|
+
fi
|
|
225
312
|
fi
|
|
226
313
|
case "$_e10_cid" in
|
|
227
|
-
'')
|
|
314
|
+
'')
|
|
315
|
+
# A PR number is not an issue to `gh issue comment`; the receipt marker only ever rides a PR.
|
|
316
|
+
if [ "$_e10_marker" = '<!-- loki-receipt -->' ]; then
|
|
317
|
+
_e10_gh pr comment "$num" --repo "$_e10_repo" --body-file "$2" || die "gh pr comment failed"
|
|
318
|
+
else
|
|
319
|
+
_e10_gh issue comment "$num" --repo "$_e10_repo" --body-file "$2" || die "gh issue comment failed"
|
|
320
|
+
fi
|
|
321
|
+
;;
|
|
228
322
|
*[!0-9]*) die "unexpected comment id" ;;
|
|
229
323
|
*) _e10_gh api -X PATCH "repos/$_e10_repo/issues/comments/$_e10_cid" -F "body=@$2" >/dev/null || die "gh api comment update failed" ;;
|
|
230
324
|
esac
|
|
@@ -194,8 +194,31 @@ def _env_assign_sub(m):
|
|
|
194
194
|
# The password class is [^\s@]+ (stops only at the closing @) so passwords that
|
|
195
195
|
# contain "/" (e.g. postgres://user:p/ss@host) are still fully captured.
|
|
196
196
|
_URI_CREDENTIAL = re.compile(
|
|
197
|
-
r"\b([A-Za-z][A-Za-z0-9+.\-]
|
|
197
|
+
r"\b([A-Za-z][A-Za-z0-9+.\-]{0,63}://[^\s:/@]*:)([^\s@]+)(@)" # bounded scheme: linear (TEN-SECURITY-26)
|
|
198
198
|
)
|
|
199
|
+
_TOKEN = re.compile(r"\S+")
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _redact_uri_credentials(s):
|
|
203
|
+
"""Apply _URI_CREDENTIAL to the part of each whitespace-delimited token up to
|
|
204
|
+
its last "@". No match ends past that "@" (the password class stops at "@"),
|
|
205
|
+
and scanning an "@"-free tail from every scheme start is quadratic even with
|
|
206
|
+
the bounded scheme ("a://" * 16000 took 5.6s). TEN-SECURITY-26 follow-up.
|
|
207
|
+
Returns (new_string, count)."""
|
|
208
|
+
total = 0
|
|
209
|
+
|
|
210
|
+
def tok(m):
|
|
211
|
+
nonlocal total
|
|
212
|
+
t = m.group(0)
|
|
213
|
+
cut = t.rfind("@") + 1
|
|
214
|
+
if not cut:
|
|
215
|
+
return t
|
|
216
|
+
head, n = _URI_CREDENTIAL.subn(r"\1[REDACTED]\3", t[:cut])
|
|
217
|
+
total += n
|
|
218
|
+
return head + t[cut:]
|
|
219
|
+
|
|
220
|
+
return _TOKEN.sub(tok, s), total
|
|
221
|
+
|
|
199
222
|
|
|
200
223
|
# Absolute user home paths -> ~ . Applied to every string (paths, diffs,
|
|
201
224
|
# brief, summaries). Windows form handled separately to keep the backslash
|
|
@@ -289,7 +312,8 @@ def _redact_value(s):
|
|
|
289
312
|
# Connection-string / URI credentials: scheme://user:PASSWORD@ -> redact
|
|
290
313
|
# the password. Runs before the generic env-assign so the "scheme://user:"
|
|
291
314
|
# prefix is consumed and the assign rule does not double-process it.
|
|
292
|
-
|
|
315
|
+
# The helper applies _URI_CREDENTIAL per token; keep the two in sync.
|
|
316
|
+
s, n = _redact_uri_credentials(s)
|
|
293
317
|
total += n
|
|
294
318
|
|
|
295
319
|
# Secret assignments (bare, JSON-quoted, YAML-quoted): keep key, redact value.
|