loki-mode 9.22.12 → 9.24.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/SKILL.md +3 -3
- package/VERSION +1 -1
- package/autonomy/lib/claude-flags.sh +69 -5
- package/autonomy/lib/contract-scaffold/scaffold.sh +1 -1
- package/autonomy/loki +324 -74
- package/autonomy/run.sh +176 -40
- package/autonomy/verify.sh +16 -3
- package/bin/loki +25 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/control.py +88 -4
- package/dashboard/server.py +50 -48
- package/docs/INSTALLATION.md +2 -2
- package/docs/RARV-C-100X-PLAN.md +9 -1
- package/docs/enterprise/performance.md +51 -9
- package/docs/exit-codes.md +4 -0
- package/loki-ts/dist/cockpit.js +1 -1
- package/loki-ts/dist/loki.js +634 -549
- package/loki-ts/package.json +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +6 -5
- package/plugins/loki-mode/.claude-plugin/plugin.json +2 -3
- package/providers/loader.sh +2 -2
- package/providers/model_catalog.json +26 -8
- package/providers/models.sh +1 -1
- package/providers/opencode.sh +1 -1
- package/skills/providers.md +42 -14
- package/tools/normalize-package-permissions.mjs +34 -0
- package/web-app/dist/assets/{AdminPage-DLhWX6si.js → AdminPage-PyyVXg4b.js} +1 -1
- package/web-app/dist/assets/{Avatar-CqyDcUxl.js → Avatar-CTUlr_Xf.js} +1 -1
- package/web-app/dist/assets/{Badge-BKGSt1n2.js → Badge-BQeXvmg8.js} +1 -1
- package/web-app/dist/assets/{Button-DuuKHXre.js → Button-Dc6-P6pz.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-DdClYERC.js → CockpitPage-BspOJ3Fv.js} +1 -1
- package/web-app/dist/assets/{ComparePage-CUWlqBxv.js → ComparePage-CFXIM--Z.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-RwfGKyRJ.js → ErrorBoundary-C7vQYDW9.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-CzIgk9sg.js → EvidenceReceiptPanel-CqjsjTOs.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-CsXVKuyJ.js → GitHubIssuesPanel-DyuNDMgD.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-CYRp1eKS.js → GitHubPRsPanel-BtAq20xF.js} +1 -1
- package/web-app/dist/assets/{HomePage-CVJcI5pQ.js → HomePage-BuVFOrO8.js} +1 -1
- package/web-app/dist/assets/{LoginPage-BBJNV9Qb.js → LoginPage-CuhhrxGb.js} +1 -1
- package/web-app/dist/assets/{MagicPage-CIi9P8jK.js → MagicPage-D2KZPX3F.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-Bm34ipiy.js → MetricsPage-DFEYJl9z.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-DpTbcC2P.js → NotFoundPage-CTnnaWlL.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-CY2SMGx9.js → ProjectPage-uJFaoM8-.js} +55 -55
- package/web-app/dist/assets/{ProjectsPage-DSrXUUKJ.js → ProjectsPage-8lKAOBIW.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-jvDyOqYi.js → SettingsPage-D1BBd5qy.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-C8tFWKau.js → ShowcasePage-CmQ0n8iO.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-DH4FTH3K.js → SystemSettingsPage-CSHaVyuD.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-C1fQYauF.js → TeamsPage-BS0TtVUa.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-CSTFt-4P.js → TemplatesPage-C2zPnnf2.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Cu4NsyN6.js → TerminalOutput-Dle1klqq.js} +1 -1
- package/web-app/dist/assets/{activity-CtjsDzmh.js → activity-DQp5U9f9.js} +1 -1
- package/web-app/dist/assets/{bell-C5es1xAs.js → bell-BqkTmgL7.js} +1 -1
- package/web-app/dist/assets/{bot-DE04U5Lc.js → bot-DILhaIGH.js} +1 -1
- package/web-app/dist/assets/{check-DSKKA9EU.js → check-BC9x7oPm.js} +1 -1
- package/web-app/dist/assets/{chevron-left-DJ13W5AR.js → chevron-left--yqvAfjx.js} +1 -1
- package/web-app/dist/assets/{circle-alert-CS0BfqAd.js → circle-alert-DOH6PcgZ.js} +1 -1
- package/web-app/dist/assets/{clock-D1Y5nruk.js → clock-DKSDFsE6.js} +1 -1
- package/web-app/dist/assets/{cloud-nU6LBoZL.js → cloud-DRBU3JYs.js} +1 -1
- package/web-app/dist/assets/{code-xml-BZfvIdFo.js → code-xml-D2LqszR9.js} +1 -1
- package/web-app/dist/assets/{copy-CK_2O1Xv.js → copy-BKW4U4Qr.js} +1 -1
- package/web-app/dist/assets/{database-DJiwznAm.js → database-D7ceedlh.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-pO20A3QY.js → dollar-sign-D0A47egY.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-Besy6vek.js → file-code-corner-CH_Qbtjo.js} +1 -1
- package/web-app/dist/assets/{file-plus-DeaswxYY.js → file-plus-CUNMjHk9.js} +1 -1
- package/web-app/dist/assets/{folder-open-cr1UvQLI.js → folder-open-CQ_SVq94.js} +1 -1
- package/web-app/dist/assets/{git-commit-horizontal-BLxnfbSn.js → git-commit-horizontal-DQUBe0oD.js} +1 -1
- package/web-app/dist/assets/{globe-DrhG1NfX.js → globe-gZnaGg8N.js} +1 -1
- package/web-app/dist/assets/{hammer-CeZtPBfE.js → hammer-BhJC5DMK.js} +1 -1
- package/web-app/dist/assets/{index-CLvjO22Z.js → index-BMmYYws0.js} +2 -2
- package/web-app/dist/assets/{layers-fldADzyE.js → layers-glXeoUbJ.js} +1 -1
- package/web-app/dist/assets/{lightbulb-CznOfsPb.js → lightbulb-gXn8H0Lf.js} +1 -1
- package/web-app/dist/assets/{loader-circle---lPB1Fs.js → loader-circle-Do2ke8xm.js} +1 -1
- package/web-app/dist/assets/{lock-D5jVF-tO.js → lock-Bgplb1_n.js} +1 -1
- package/web-app/dist/assets/{mail-D7HkLnlW.js → mail-j4Lo8hfB.js} +1 -1
- package/web-app/dist/assets/{minus-C-dT19Ct.js → minus-dhrzKSK5.js} +1 -1
- package/web-app/dist/assets/{package-Cm9B-cKE.js → package-CInWQQ97.js} +1 -1
- package/web-app/dist/assets/{plus-CQwOffLD.js → plus-BpYvoLlZ.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CvWJZLbN.js → refresh-cw-F6koqEgF.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-C2Q1LYgM.js → rotate-ccw-CQ2W5S8f.js} +1 -1
- package/web-app/dist/assets/{save-BP0MvpoQ.js → save-DMCYGm06.js} +1 -1
- package/web-app/dist/assets/{server-4i4VZXix.js → server-Bj8EX4Z1.js} +1 -1
- package/web-app/dist/assets/{shield-alert-D2_bgb7j.js → shield-alert-CiYuulCU.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-vACbzeXj.js → thumbs-up-BqEh1puP.js} +1 -1
- package/web-app/dist/assets/{trash-2-EYjdESLs.js → trash-2-CCwouZJJ.js} +1 -1
- package/web-app/dist/assets/{trending-down-BWBdcvE3.js → trending-down-Cx1vhE4o.js} +1 -1
- package/web-app/dist/assets/{trending-up-Bix6V_-Y.js → trending-up-BUhVYJ2T.js} +1 -1
- package/web-app/dist/assets/{upload-B7hh6ERF.js → upload-D5IsZeFM.js} +1 -1
- package/web-app/dist/assets/{usePolling-b_e-k-IN.js → usePolling-CJbfhFcH.js} +1 -1
- package/web-app/dist/assets/{user-pmjZKWEg.js → user-BRLhezsD.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/web-app/requirements.txt +1 -0
- package/web-app/server.py +160 -31
package/dashboard/server.py
CHANGED
|
@@ -58,7 +58,12 @@ from . import audit
|
|
|
58
58
|
from . import app_secrets as secrets_mod
|
|
59
59
|
from . import telemetry as _telemetry
|
|
60
60
|
from . import build_supervisor as _build_execution
|
|
61
|
-
from .control import
|
|
61
|
+
from .control import (
|
|
62
|
+
atomic_write_json,
|
|
63
|
+
browser_mutation_origin_allowed,
|
|
64
|
+
find_skill_dir,
|
|
65
|
+
is_process_running,
|
|
66
|
+
)
|
|
62
67
|
from .activity_logger import get_activity_logger
|
|
63
68
|
from .api_v2 import (
|
|
64
69
|
TenantContext,
|
|
@@ -187,53 +192,46 @@ def _rate_key(base: str, request: Optional[Request]) -> str:
|
|
|
187
192
|
logger = logging.getLogger(__name__)
|
|
188
193
|
|
|
189
194
|
|
|
190
|
-
#
|
|
191
|
-
#
|
|
192
|
-
#
|
|
195
|
+
# Public API metadata needed before a caller can authenticate or before the UI
|
|
196
|
+
# can choose a provider. Everything else under an API namespace is
|
|
197
|
+
# state-bearing unless it is explicitly admitted here.
|
|
193
198
|
#
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
#
|
|
199
|
+
# This is deliberately an ALLOWLIST, not the old list of sensitive prefixes.
|
|
200
|
+
# That list silently started every new read family public. Measured from a
|
|
201
|
+
# routable remote address with auth off, routes added after the list --
|
|
202
|
+
# /api/context, /api/notifications, /api/agents, /api/usage,
|
|
203
|
+
# /api/prd-observations and every /api/v2/* read -- all reached their handlers
|
|
204
|
+
# and returned project, tenant or audit state.
|
|
197
205
|
#
|
|
198
|
-
# /health
|
|
199
|
-
#
|
|
200
|
-
#
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"/api/
|
|
204
|
-
"/api/
|
|
205
|
-
"/api/
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
"
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
"
|
|
218
|
-
"/
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
"/api/failures",
|
|
228
|
-
"/api/prompt",
|
|
229
|
-
"/api/quality",
|
|
230
|
-
"/api/migration",
|
|
231
|
-
"/api/managed",
|
|
232
|
-
"/api/app-runner",
|
|
233
|
-
"/api/playwright",
|
|
234
|
-
"/api/checklist",
|
|
235
|
-
"/api/control",
|
|
236
|
-
)
|
|
206
|
+
# /health, /metrics, /.well-known/*, docs and static/UI routes are outside the
|
|
207
|
+
# API namespaces and remain public. The three entries below contain only
|
|
208
|
+
# capability/auth bootstrap metadata and are already pinned as public by the
|
|
209
|
+
# dashboard auth inventory tests.
|
|
210
|
+
_PUBLIC_API_GET_PATHS = frozenset({
|
|
211
|
+
"/api/auth/info",
|
|
212
|
+
"/api/enterprise/status",
|
|
213
|
+
"/api/providers/models",
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _is_state_bearing_get(path: str) -> bool:
|
|
218
|
+
"""Classify dashboard reads at the namespace boundary.
|
|
219
|
+
|
|
220
|
+
A future GET under /api or the mounted Purple Lab's /lab/api namespace is
|
|
221
|
+
private by default. Exact public metadata is admitted above; probes,
|
|
222
|
+
discovery documents and UI/static files live outside these namespaces.
|
|
223
|
+
Normalize one trailing slash so FastAPI's redirect spelling cannot turn a
|
|
224
|
+
public metadata request into a remote-only failure.
|
|
225
|
+
"""
|
|
226
|
+
normalized = path.rstrip("/") or "/"
|
|
227
|
+
if normalized in _PUBLIC_API_GET_PATHS:
|
|
228
|
+
return False
|
|
229
|
+
return (
|
|
230
|
+
normalized == "/api"
|
|
231
|
+
or normalized.startswith("/api/")
|
|
232
|
+
or normalized == "/lab/api"
|
|
233
|
+
or normalized.startswith("/lab/api/")
|
|
234
|
+
)
|
|
237
235
|
|
|
238
236
|
|
|
239
237
|
def _trusted_proxies() -> frozenset:
|
|
@@ -1263,9 +1261,13 @@ async def dashboard_control_boundary(request: Request, call_next):
|
|
|
1263
1261
|
# /health and /metrics are intentionally NOT in the sensitive list, so a
|
|
1264
1262
|
# container health probe and a Prometheus scrape keep working unconfigured.
|
|
1265
1263
|
gated = request.method in ("POST", "PUT", "PATCH", "DELETE")
|
|
1264
|
+
if gated and not browser_mutation_origin_allowed(request, _cors_origins):
|
|
1265
|
+
return JSONResponse(
|
|
1266
|
+
status_code=403,
|
|
1267
|
+
content={"detail": "cross-origin mutation refused"},
|
|
1268
|
+
)
|
|
1266
1269
|
if not gated:
|
|
1267
|
-
|
|
1268
|
-
gated = any(path.startswith(pfx) for pfx in _SENSITIVE_READ_PREFIXES)
|
|
1270
|
+
gated = _is_state_bearing_get(request.url.path)
|
|
1269
1271
|
if gated:
|
|
1270
1272
|
try:
|
|
1271
1273
|
require_local_or_authenticated(request)
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
|
|
4
4
|
|
|
5
|
-
**Version:** v9.
|
|
5
|
+
**Version:** v9.24.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -343,7 +343,7 @@ The `HUMAN_INPUT.md` file has security controls:
|
|
|
343
343
|
|
|
344
344
|
## Multi-Provider Support
|
|
345
345
|
|
|
346
|
-
Loki Mode supports
|
|
346
|
+
Loki Mode supports five active providers across three capability modes, plus historical/upcoming entries. Pick by capability + cost.
|
|
347
347
|
|
|
348
348
|
### Supported Providers
|
|
349
349
|
|
package/docs/RARV-C-100X-PLAN.md
CHANGED
|
@@ -220,7 +220,15 @@ Every commit proven on BOTH routes (`bin/loki` + `LOKI_LEGACY_BASH=1 bin/loki`).
|
|
|
220
220
|
- **Post-release smoke from SHIPPED artifacts on BOTH routes**: `npm pack loki-mode@8.0.0` + `bun run bin/loki
|
|
221
221
|
version` and `LOKI_LEGACY_BASH=1 ... version`; `docker run --rm asklokesh/loki-mode:8.0.0 doctor --json`;
|
|
222
222
|
WebFetch brew formula version+sha.
|
|
223
|
-
- **Cleanup after every local-ci/validation**:
|
|
223
|
+
- **Cleanup after every local-ci/validation**: create one exact run-owned directory with `mktemp -d`
|
|
224
|
+
under the canonical `${TMPDIR:-/tmp}` root and keep all validation artifacts and recorded child
|
|
225
|
+
PIDs inside it. Use the executable operator-side sequence in
|
|
226
|
+
[`enterprise/performance.md`](enterprise/performance.md#cleanup); it does not depend on a shipped
|
|
227
|
+
cleanup helper. Stop only the exact verified child PIDs. Before removing the exact saved path,
|
|
228
|
+
require its `.loki-run-owned` marker to contain that path, require the target and marker to belong
|
|
229
|
+
to the current UID, require the target to be a canonical direct child of the temp root, refuse
|
|
230
|
+
symlinks, and refuse any target with `.git` worktree metadata. A failed guard leaves the path in
|
|
231
|
+
place for inspection.
|
|
224
232
|
|
|
225
233
|
---
|
|
226
234
|
|
|
@@ -267,19 +267,61 @@ When enterprise features are active, background processes consume resources:
|
|
|
267
267
|
|
|
268
268
|
### Cleanup
|
|
269
269
|
|
|
270
|
-
|
|
270
|
+
Keep cleanup scoped to the processes and paths created by the current invocation. Record the exact
|
|
271
|
+
child process IDs when starting background services and stop only those verified processes. Do not
|
|
272
|
+
kill by a shared port or process-name pattern.
|
|
271
273
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
lsof -ti:57374 | xargs kill -9 2>/dev/null || true
|
|
275
|
-
|
|
276
|
-
# Kill background enterprise processes
|
|
277
|
-
pkill -f "loki-run-" 2>/dev/null || true
|
|
274
|
+
Create one run-owned temporary directory before writing any temporary package, log, PID, or test
|
|
275
|
+
artifact. Run the creation and cleanup blocks in the same shell so `run_tmp` retains the exact path:
|
|
278
276
|
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
```bash
|
|
278
|
+
# Create the only temporary directory this invocation may remove.
|
|
279
|
+
temp_root="$(cd "${TMPDIR:-/tmp}" 2>/dev/null && pwd -P)" || exit 1
|
|
280
|
+
run_tmp="$(mktemp -d "${temp_root}/loki-run.XXXXXXXX")" || exit 1
|
|
281
|
+
chmod 700 "$run_tmp" || exit 1
|
|
282
|
+
run_marker="${run_tmp}/.loki-run-owned"
|
|
283
|
+
printf '%s\n' "$run_tmp" >"$run_marker" || exit 1
|
|
284
|
+
chmod 600 "$run_marker" || exit 1
|
|
285
|
+
|
|
286
|
+
# Put this invocation's temporary artifacts under "$run_tmp" and retain exact child PIDs there.
|
|
287
|
+
|
|
288
|
+
# Immediately before removal, fail closed unless every ownership boundary still holds.
|
|
289
|
+
cleanup_target="$run_tmp"
|
|
290
|
+
cleanup_root="$(cd "${TMPDIR:-/tmp}" 2>/dev/null && pwd -P)" || exit 64
|
|
291
|
+
case "$cleanup_target" in
|
|
292
|
+
"${cleanup_root}"/loki-run.*) ;;
|
|
293
|
+
*) printf '%s\n' "Refusing cleanup outside the run-owned namespace" >&2; exit 64 ;;
|
|
294
|
+
esac
|
|
295
|
+
[ "$(dirname "$cleanup_target")" = "$cleanup_root" ] || exit 64
|
|
296
|
+
[ -d "$cleanup_target" ] && [ ! -L "$cleanup_target" ] || exit 64
|
|
297
|
+
|
|
298
|
+
cleanup_marker="${cleanup_target}/.loki-run-owned"
|
|
299
|
+
[ -f "$cleanup_marker" ] && [ ! -L "$cleanup_marker" ] || exit 64
|
|
300
|
+
IFS= read -r marker_value <"$cleanup_marker" || exit 64
|
|
301
|
+
[ "$marker_value" = "$cleanup_target" ] || exit 64
|
|
302
|
+
|
|
303
|
+
target_uid="$(stat -f '%u' "$cleanup_target" 2>/dev/null || stat -c '%u' "$cleanup_target")" || exit 64
|
|
304
|
+
marker_uid="$(stat -f '%u' "$cleanup_marker" 2>/dev/null || stat -c '%u' "$cleanup_marker")" || exit 64
|
|
305
|
+
[ "$target_uid" = "$(id -u)" ] && [ "$marker_uid" = "$(id -u)" ] || exit 64
|
|
306
|
+
|
|
307
|
+
target_real="$(cd "$cleanup_target" 2>/dev/null && pwd -P)" || exit 64
|
|
308
|
+
[ "$target_real" = "$cleanup_target" ] || exit 64
|
|
309
|
+
[ "$(dirname "$target_real")" = "$cleanup_root" ] || exit 64
|
|
310
|
+
|
|
311
|
+
# Refuse primary worktrees, linked worktrees, and a broken .git symlink.
|
|
312
|
+
if [ -e "${cleanup_target}/.git" ] || [ -L "${cleanup_target}/.git" ]; then
|
|
313
|
+
printf '%s\n' "Refusing to remove a Git worktree" >&2
|
|
314
|
+
exit 64
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
rm -rf -- "$cleanup_target"
|
|
318
|
+
[ ! -e "$cleanup_target" ] || exit 1
|
|
281
319
|
```
|
|
282
320
|
|
|
321
|
+
These are operator-side shell commands, not a Loki cleanup helper. Never replace the exact target
|
|
322
|
+
with a wildcard under `/tmp` or `$TMPDIR`, and never remove a path whose marker, UID ownership,
|
|
323
|
+
canonical location, symlink status, or worktree status cannot be verified.
|
|
324
|
+
|
|
283
325
|
## Performance Monitoring Checklist
|
|
284
326
|
|
|
285
327
|
1. **Verify no-op behavior** -- When enterprise env vars are unset, confirm zero overhead by checking that no background threads are created and no disk I/O occurs.
|
package/docs/exit-codes.md
CHANGED
|
@@ -84,6 +84,10 @@ Machine-readable output is `--format json` here, not `--json`.
|
|
|
84
84
|
| 0 | Every required check passed. Optional warnings do not fail the command. |
|
|
85
85
|
| nonzero | At least one required check failed; the output names which |
|
|
86
86
|
|
|
87
|
+
The contract is identical for human-readable output and `--json`. JSON is
|
|
88
|
+
still emitted in full before the command exits nonzero, so automation can save
|
|
89
|
+
or parse the report while also using the process status as a readiness gate.
|
|
90
|
+
|
|
87
91
|
Safe as a preflight gate in an init container or pipeline step:
|
|
88
92
|
|
|
89
93
|
```sh
|
package/loki-ts/dist/cockpit.js
CHANGED
|
@@ -16,4 +16,4 @@ var EJ=Object.defineProperty;var TJ=(J)=>J;function WJ(J,Q){this[J]=TJ.bind(null
|
|
|
16
16
|
`),3}let G=process.stdout.columns||Number(process.env.COLUMNS)||0,q=G>4?G-2:void 0,K=await CJ($,{protocol:Q,forceText:X,cols:q});if(Z)try{let{writeFileSync:P}=await import("fs");P(Z,K.svg)}catch{}if(K.kind==="image"&&K.data)return process.stdout.write(K.data),0;return process.stderr.write(`FALLBACK ${K.reason||"image unavailable"}
|
|
17
17
|
`),3}if(import.meta.main)rJ().then((J)=>process.exit(J));export{rJ as main};
|
|
18
18
|
|
|
19
|
-
//# debugId=
|
|
19
|
+
//# debugId=FA320A456FDE2DB7437C9AA473A12AC3
|