Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da709770f0 |
@@ -1,59 +0,0 @@
|
|||||||
# CLAUDE.md
|
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
This is a Paperclip adapter plugin that runs Claude Code agents as isolated Kubernetes Jobs instead of inside the main Paperclip process. It uses the `@kubernetes/client-node` library to interact with the K8s API.
|
|
||||||
|
|
||||||
## CI/CD
|
|
||||||
|
|
||||||
Build and publish are handled by GitHub Actions on tag push — do **not** build locally. To release a new version, bump `package.json` with `npm version` and push the tag — CI handles the rest.
|
|
||||||
|
|
||||||
## Common Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run typecheck # Type-check without emitting (local dev only)
|
|
||||||
npm test # Run tests (vitest run)
|
|
||||||
npm run test:watch # Run tests in watch mode
|
|
||||||
npm run coverage # Run tests with coverage
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not run `npm run build` locally — it's run by the CI pipeline. To release: bump version (`npm version`), push, and CI publishes automatically.
|
|
||||||
|
|
||||||
Single test file: `npx vitest run src/server/execute.test.ts`
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Entry Point
|
|
||||||
`src/index.ts` exports `createServerAdapter()` which returns a `ServerAdapterModule` with all adapter capabilities. It re-exports types, models, and the execute function.
|
|
||||||
|
|
||||||
### Server Module (`src/server/`)
|
|
||||||
- **`execute.ts`** — Core execution flow: checks for concurrent runs, creates a K8s Job, waits for pod scheduling, streams logs, waits for job completion, parses Claude's stream-json output, and returns the result. Also handles cleanup and retention.
|
|
||||||
- **`job-manifest.ts`** — Builds the K8s Job manifest. Key design: an init container (busybox) writes the prompt to an emptyDir volume, then the main `claude` container reads it via stdin. The shared PVC is mounted at `/paperclip` with `HOME=/paperclip` to enable session resume.
|
|
||||||
- **`k8s-client.ts`** — Wrapper around `@kubernetes/client-node`. Caches the KubeConfig and self-pod introspection (`getSelfPodInfo`) which discovers the container image, imagePullSecrets, DNS config, PVC claim name, and env vars to forward to Job pods.
|
|
||||||
- **`config-schema.ts`** — Returns the UI config schema (typed as `ConfigFieldSchema[]`) that Paperclip's web UI renders as a form.
|
|
||||||
- **`parse.ts`** — Parses Claude's `stream-json` output format to extract session IDs, token usage, cost, and summaries.
|
|
||||||
- **`session.ts`** — Session codec for session resume.
|
|
||||||
- **`skills.ts`** / **`models.ts`** — Implement `listSkills`/`syncSkills` and `listModels` for the `ServerAdapterModule` interface.
|
|
||||||
|
|
||||||
### CLI Module (`src/cli/`)
|
|
||||||
- **`format-event.ts`** — Formats Claude stream events for terminal output.
|
|
||||||
|
|
||||||
### UI Parser (`src/ui-parser.ts`)
|
|
||||||
- Parses adapter-specific UI configuration fields.
|
|
||||||
|
|
||||||
### Config Schema Note
|
|
||||||
The types in `config-schema.ts` (`ConfigFieldSchema`) must match what Paperclip's `SchemaConfigFields` component expects, since Paperclip's server calls `adapter.getConfigSchema()` and the UI reads the JSON at runtime.
|
|
||||||
|
|
||||||
## Key Design Decisions
|
|
||||||
|
|
||||||
1. **Pod introspection** — On first `execute()` call, `getSelfPodInfo()` reads the running pod's spec via K8s API and caches it. Every subsequent Job inherits the Deployment's image, secrets, DNS, and PVC without additional config.
|
|
||||||
|
|
||||||
2. **Concurrency guard** — Before creating a Job, `execute.ts` lists existing Jobs labeled with the agent ID and blocks if any are still running (prevents session conflicts on the shared PVC).
|
|
||||||
|
|
||||||
3. **Prompt delivery** — The prompt is written by a busybox init container to an `emptyDir` volume, then read by the main `claude` container via `stdin`. This avoids escaping issues with env vars containing complex characters.
|
|
||||||
|
|
||||||
4. **Log streaming** — Uses `k8s.Log` follow mode with automatic reconnection. If the follow stream ends before the job completes (API disconnect), a one-shot log read is used as fallback.
|
|
||||||
|
|
||||||
5. **Session resume** — Works via the shared `/paperclip` PVC mounted as `HOME`. The `runtimeSessionId` is passed via `--resume` to the Claude CLI.
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
body, html {
|
|
||||||
margin:0; padding: 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: Helvetica Neue, Helvetica, Arial;
|
|
||||||
font-size: 14px;
|
|
||||||
color:#333;
|
|
||||||
}
|
|
||||||
.small { font-size: 12px; }
|
|
||||||
*, *:after, *:before {
|
|
||||||
-webkit-box-sizing:border-box;
|
|
||||||
-moz-box-sizing:border-box;
|
|
||||||
box-sizing:border-box;
|
|
||||||
}
|
|
||||||
h1 { font-size: 20px; margin: 0;}
|
|
||||||
h2 { font-size: 14px; }
|
|
||||||
pre {
|
|
||||||
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-moz-tab-size: 2;
|
|
||||||
-o-tab-size: 2;
|
|
||||||
tab-size: 2;
|
|
||||||
}
|
|
||||||
a { color:#0074D9; text-decoration:none; }
|
|
||||||
a:hover { text-decoration:underline; }
|
|
||||||
.strong { font-weight: bold; }
|
|
||||||
.space-top1 { padding: 10px 0 0 0; }
|
|
||||||
.pad2y { padding: 20px 0; }
|
|
||||||
.pad1y { padding: 10px 0; }
|
|
||||||
.pad2x { padding: 0 20px; }
|
|
||||||
.pad2 { padding: 20px; }
|
|
||||||
.pad1 { padding: 10px; }
|
|
||||||
.space-left2 { padding-left:55px; }
|
|
||||||
.space-right2 { padding-right:20px; }
|
|
||||||
.center { text-align:center; }
|
|
||||||
.clearfix { display:block; }
|
|
||||||
.clearfix:after {
|
|
||||||
content:'';
|
|
||||||
display:block;
|
|
||||||
height:0;
|
|
||||||
clear:both;
|
|
||||||
visibility:hidden;
|
|
||||||
}
|
|
||||||
.fl { float: left; }
|
|
||||||
@media only screen and (max-width:640px) {
|
|
||||||
.col3 { width:100%; max-width:100%; }
|
|
||||||
.hide-mobile { display:none!important; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.quiet {
|
|
||||||
color: #7f7f7f;
|
|
||||||
color: rgba(0,0,0,0.5);
|
|
||||||
}
|
|
||||||
.quiet a { opacity: 0.7; }
|
|
||||||
|
|
||||||
.fraction {
|
|
||||||
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
|
||||||
font-size: 10px;
|
|
||||||
color: #555;
|
|
||||||
background: #E8E8E8;
|
|
||||||
padding: 4px 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.path a:link, div.path a:visited { color: #333; }
|
|
||||||
table.coverage {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 10px 0 0 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.coverage td {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
table.coverage td.line-count {
|
|
||||||
text-align: right;
|
|
||||||
padding: 0 5px 0 20px;
|
|
||||||
}
|
|
||||||
table.coverage td.line-coverage {
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 10px;
|
|
||||||
min-width:20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.coverage td span.cline-any {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0 5px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.missing-if-branch {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
position: relative;
|
|
||||||
padding: 0 4px;
|
|
||||||
background: #333;
|
|
||||||
color: yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skip-if-branch {
|
|
||||||
display: none;
|
|
||||||
margin-right: 10px;
|
|
||||||
position: relative;
|
|
||||||
padding: 0 4px;
|
|
||||||
background: #ccc;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.missing-if-branch .typ, .skip-if-branch .typ {
|
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
.coverage-summary {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.coverage-summary tr { border-bottom: 1px solid #bbb; }
|
|
||||||
.keyline-all { border: 1px solid #ddd; }
|
|
||||||
.coverage-summary td, .coverage-summary th { padding: 10px; }
|
|
||||||
.coverage-summary tbody { border: 1px solid #bbb; }
|
|
||||||
.coverage-summary td { border-right: 1px solid #bbb; }
|
|
||||||
.coverage-summary td:last-child { border-right: none; }
|
|
||||||
.coverage-summary th {
|
|
||||||
text-align: left;
|
|
||||||
font-weight: normal;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.coverage-summary th.file { border-right: none !important; }
|
|
||||||
.coverage-summary th.pct { }
|
|
||||||
.coverage-summary th.pic,
|
|
||||||
.coverage-summary th.abs,
|
|
||||||
.coverage-summary td.pct,
|
|
||||||
.coverage-summary td.abs { text-align: right; }
|
|
||||||
.coverage-summary td.file { white-space: nowrap; }
|
|
||||||
.coverage-summary td.pic { min-width: 120px !important; }
|
|
||||||
.coverage-summary tfoot td { }
|
|
||||||
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
height: 10px;
|
|
||||||
width: 7px;
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 0.5em;
|
|
||||||
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
|
|
||||||
}
|
|
||||||
.coverage-summary .sorted .sorter {
|
|
||||||
background-position: 0 -20px;
|
|
||||||
}
|
|
||||||
.coverage-summary .sorted-desc .sorter {
|
|
||||||
background-position: 0 -10px;
|
|
||||||
}
|
|
||||||
.status-line { height: 10px; }
|
|
||||||
/* yellow */
|
|
||||||
.cbranch-no { background: yellow !important; color: #111; }
|
|
||||||
/* dark red */
|
|
||||||
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
|
|
||||||
.low .chart { border:1px solid #C21F39 }
|
|
||||||
.highlighted,
|
|
||||||
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
|
|
||||||
background: #C21F39 !important;
|
|
||||||
}
|
|
||||||
/* medium red */
|
|
||||||
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
|
|
||||||
/* light red */
|
|
||||||
.low, .cline-no { background:#FCE1E5 }
|
|
||||||
/* light green */
|
|
||||||
.high, .cline-yes { background:rgb(230,245,208) }
|
|
||||||
/* medium green */
|
|
||||||
.cstat-yes { background:rgb(161,215,106) }
|
|
||||||
/* dark green */
|
|
||||||
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
|
|
||||||
.high .chart { border:1px solid rgb(77,146,33) }
|
|
||||||
/* dark yellow (gold) */
|
|
||||||
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
|
|
||||||
.medium .chart { border:1px solid #f9cd0b; }
|
|
||||||
/* light yellow */
|
|
||||||
.medium { background: #fff4c2; }
|
|
||||||
|
|
||||||
.cstat-skip { background: #ddd; color: #111; }
|
|
||||||
.fstat-skip { background: #ddd; color: #111 !important; }
|
|
||||||
.cbranch-skip { background: #ddd !important; color: #111; }
|
|
||||||
|
|
||||||
span.cline-neutral { background: #eaeaea; }
|
|
||||||
|
|
||||||
.coverage-summary td.empty {
|
|
||||||
opacity: .5;
|
|
||||||
padding-top: 4px;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
line-height: 1;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover-fill, .cover-empty {
|
|
||||||
display:inline-block;
|
|
||||||
height: 12px;
|
|
||||||
}
|
|
||||||
.chart {
|
|
||||||
line-height: 0;
|
|
||||||
}
|
|
||||||
.cover-empty {
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
.cover-full {
|
|
||||||
border-right: none !important;
|
|
||||||
}
|
|
||||||
pre.prettyprint {
|
|
||||||
border: none !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
.com { color: #999 !important; }
|
|
||||||
.ignore-none { color: #999; font-weight: normal; }
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
min-height: 100%;
|
|
||||||
height: auto !important;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0 auto -48px;
|
|
||||||
}
|
|
||||||
.footer, .push {
|
|
||||||
height: 48px;
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
var jumpToCode = (function init() {
|
|
||||||
// Classes of code we would like to highlight in the file view
|
|
||||||
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
|
|
||||||
|
|
||||||
// Elements to highlight in the file listing view
|
|
||||||
var fileListingElements = ['td.pct.low'];
|
|
||||||
|
|
||||||
// We don't want to select elements that are direct descendants of another match
|
|
||||||
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
|
|
||||||
|
|
||||||
// Selector that finds elements on the page to which we can jump
|
|
||||||
var selector =
|
|
||||||
fileListingElements.join(', ') +
|
|
||||||
', ' +
|
|
||||||
notSelector +
|
|
||||||
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
|
|
||||||
|
|
||||||
// The NodeList of matching elements
|
|
||||||
var missingCoverageElements = document.querySelectorAll(selector);
|
|
||||||
|
|
||||||
var currentIndex;
|
|
||||||
|
|
||||||
function toggleClass(index) {
|
|
||||||
missingCoverageElements
|
|
||||||
.item(currentIndex)
|
|
||||||
.classList.remove('highlighted');
|
|
||||||
missingCoverageElements.item(index).classList.add('highlighted');
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeCurrent(index) {
|
|
||||||
toggleClass(index);
|
|
||||||
currentIndex = index;
|
|
||||||
missingCoverageElements.item(index).scrollIntoView({
|
|
||||||
behavior: 'smooth',
|
|
||||||
block: 'center',
|
|
||||||
inline: 'center'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function goToPrevious() {
|
|
||||||
var nextIndex = 0;
|
|
||||||
if (typeof currentIndex !== 'number' || currentIndex === 0) {
|
|
||||||
nextIndex = missingCoverageElements.length - 1;
|
|
||||||
} else if (missingCoverageElements.length > 1) {
|
|
||||||
nextIndex = currentIndex - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
makeCurrent(nextIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
function goToNext() {
|
|
||||||
var nextIndex = 0;
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof currentIndex === 'number' &&
|
|
||||||
currentIndex < missingCoverageElements.length - 1
|
|
||||||
) {
|
|
||||||
nextIndex = currentIndex + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
makeCurrent(nextIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return function jump(event) {
|
|
||||||
if (
|
|
||||||
document.getElementById('fileSearch') === document.activeElement &&
|
|
||||||
document.activeElement != null
|
|
||||||
) {
|
|
||||||
// if we're currently focused on the search input, we don't want to navigate
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (event.which) {
|
|
||||||
case 78: // n
|
|
||||||
case 74: // j
|
|
||||||
goToNext();
|
|
||||||
break;
|
|
||||||
case 66: // b
|
|
||||||
case 75: // k
|
|
||||||
case 80: // p
|
|
||||||
goToPrevious();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
window.addEventListener('keydown', jumpToCode);
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 445 B |
@@ -1,131 +0,0 @@
|
|||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Code coverage report for All files</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="prettify.css" />
|
|
||||||
<link rel="stylesheet" href="base.css" />
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style type='text/css'>
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
background-image: url(sort-arrow-sprite.png);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class='wrapper'>
|
|
||||||
<div class='pad1'>
|
|
||||||
<h1>All files</h1>
|
|
||||||
<div class='clearfix'>
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">46.93% </span>
|
|
||||||
<span class="quiet">Statements</span>
|
|
||||||
<span class='fraction'>322/686</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">44.58% </span>
|
|
||||||
<span class="quiet">Branches</span>
|
|
||||||
<span class='fraction'>280/628</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">37.97% </span>
|
|
||||||
<span class="quiet">Functions</span>
|
|
||||||
<span class='fraction'>30/79</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">46.32% </span>
|
|
||||||
<span class="quiet">Lines</span>
|
|
||||||
<span class='fraction'>284/613</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p class="quiet">
|
|
||||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
||||||
</p>
|
|
||||||
<template id="filterTemplate">
|
|
||||||
<div class="quiet">
|
|
||||||
Filter:
|
|
||||||
<input type="search" id="fileSearch">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class='status-line low'></div>
|
|
||||||
<div class="pad1">
|
|
||||||
<table class="coverage-summary">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
|
|
||||||
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
|
|
||||||
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
|
|
||||||
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
|
|
||||||
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
|
|
||||||
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
|
|
||||||
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody><tr>
|
|
||||||
<td class="file high" data-value="src"><a href="src/index.html">src</a></td>
|
|
||||||
<td data-value="92.94" class="pic high">
|
|
||||||
<div class="chart"><div class="cover-fill" style="width: 92%"></div><div class="cover-empty" style="width: 8%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="92.94" class="pct high">92.94%</td>
|
|
||||||
<td data-value="85" class="abs high">79/85</td>
|
|
||||||
<td data-value="74.07" class="pct medium">74.07%</td>
|
|
||||||
<td data-value="108" class="abs medium">80/108</td>
|
|
||||||
<td data-value="100" class="pct high">100%</td>
|
|
||||||
<td data-value="5" class="abs high">5/5</td>
|
|
||||||
<td data-value="95.94" class="pct high">95.94%</td>
|
|
||||||
<td data-value="74" class="abs high">71/74</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="file low" data-value="src/server"><a href="src/server/index.html">src/server</a></td>
|
|
||||||
<td data-value="40.43" class="pic low">
|
|
||||||
<div class="chart"><div class="cover-fill" style="width: 40%"></div><div class="cover-empty" style="width: 60%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="40.43" class="pct low">40.43%</td>
|
|
||||||
<td data-value="601" class="abs low">243/601</td>
|
|
||||||
<td data-value="38.46" class="pct low">38.46%</td>
|
|
||||||
<td data-value="520" class="abs low">200/520</td>
|
|
||||||
<td data-value="33.78" class="pct low">33.78%</td>
|
|
||||||
<td data-value="74" class="abs low">25/74</td>
|
|
||||||
<td data-value="39.51" class="pct low">39.51%</td>
|
|
||||||
<td data-value="539" class="abs low">213/539</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class='push'></div><!-- for sticky footer -->
|
|
||||||
</div><!-- /wrapper -->
|
|
||||||
<div class='footer quiet pad2 space-top1 center small'>
|
|
||||||
Code coverage generated by
|
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
||||||
at 2026-04-12T12:19:30.601Z
|
|
||||||
</div>
|
|
||||||
<script src="prettify.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
prettyPrint();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="sorter.js"></script>
|
|
||||||
<script src="block-navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
|
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 138 B |
@@ -1,210 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
var addSorting = (function() {
|
|
||||||
'use strict';
|
|
||||||
var cols,
|
|
||||||
currentSort = {
|
|
||||||
index: 0,
|
|
||||||
desc: false
|
|
||||||
};
|
|
||||||
|
|
||||||
// returns the summary table element
|
|
||||||
function getTable() {
|
|
||||||
return document.querySelector('.coverage-summary');
|
|
||||||
}
|
|
||||||
// returns the thead element of the summary table
|
|
||||||
function getTableHeader() {
|
|
||||||
return getTable().querySelector('thead tr');
|
|
||||||
}
|
|
||||||
// returns the tbody element of the summary table
|
|
||||||
function getTableBody() {
|
|
||||||
return getTable().querySelector('tbody');
|
|
||||||
}
|
|
||||||
// returns the th element for nth column
|
|
||||||
function getNthColumn(n) {
|
|
||||||
return getTableHeader().querySelectorAll('th')[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
function onFilterInput() {
|
|
||||||
const searchValue = document.getElementById('fileSearch').value;
|
|
||||||
const rows = document.getElementsByTagName('tbody')[0].children;
|
|
||||||
|
|
||||||
// Try to create a RegExp from the searchValue. If it fails (invalid regex),
|
|
||||||
// it will be treated as a plain text search
|
|
||||||
let searchRegex;
|
|
||||||
try {
|
|
||||||
searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive
|
|
||||||
} catch (error) {
|
|
||||||
searchRegex = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
const row = rows[i];
|
|
||||||
let isMatch = false;
|
|
||||||
|
|
||||||
if (searchRegex) {
|
|
||||||
// If a valid regex was created, use it for matching
|
|
||||||
isMatch = searchRegex.test(row.textContent);
|
|
||||||
} else {
|
|
||||||
// Otherwise, fall back to the original plain text search
|
|
||||||
isMatch = row.textContent
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchValue.toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
row.style.display = isMatch ? '' : 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// loads the search box
|
|
||||||
function addSearchBox() {
|
|
||||||
var template = document.getElementById('filterTemplate');
|
|
||||||
var templateClone = template.content.cloneNode(true);
|
|
||||||
templateClone.getElementById('fileSearch').oninput = onFilterInput;
|
|
||||||
template.parentElement.appendChild(templateClone);
|
|
||||||
}
|
|
||||||
|
|
||||||
// loads all columns
|
|
||||||
function loadColumns() {
|
|
||||||
var colNodes = getTableHeader().querySelectorAll('th'),
|
|
||||||
colNode,
|
|
||||||
cols = [],
|
|
||||||
col,
|
|
||||||
i;
|
|
||||||
|
|
||||||
for (i = 0; i < colNodes.length; i += 1) {
|
|
||||||
colNode = colNodes[i];
|
|
||||||
col = {
|
|
||||||
key: colNode.getAttribute('data-col'),
|
|
||||||
sortable: !colNode.getAttribute('data-nosort'),
|
|
||||||
type: colNode.getAttribute('data-type') || 'string'
|
|
||||||
};
|
|
||||||
cols.push(col);
|
|
||||||
if (col.sortable) {
|
|
||||||
col.defaultDescSort = col.type === 'number';
|
|
||||||
colNode.innerHTML =
|
|
||||||
colNode.innerHTML + '<span class="sorter"></span>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cols;
|
|
||||||
}
|
|
||||||
// attaches a data attribute to every tr element with an object
|
|
||||||
// of data values keyed by column name
|
|
||||||
function loadRowData(tableRow) {
|
|
||||||
var tableCols = tableRow.querySelectorAll('td'),
|
|
||||||
colNode,
|
|
||||||
col,
|
|
||||||
data = {},
|
|
||||||
i,
|
|
||||||
val;
|
|
||||||
for (i = 0; i < tableCols.length; i += 1) {
|
|
||||||
colNode = tableCols[i];
|
|
||||||
col = cols[i];
|
|
||||||
val = colNode.getAttribute('data-value');
|
|
||||||
if (col.type === 'number') {
|
|
||||||
val = Number(val);
|
|
||||||
}
|
|
||||||
data[col.key] = val;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
// loads all row data
|
|
||||||
function loadData() {
|
|
||||||
var rows = getTableBody().querySelectorAll('tr'),
|
|
||||||
i;
|
|
||||||
|
|
||||||
for (i = 0; i < rows.length; i += 1) {
|
|
||||||
rows[i].data = loadRowData(rows[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// sorts the table using the data for the ith column
|
|
||||||
function sortByIndex(index, desc) {
|
|
||||||
var key = cols[index].key,
|
|
||||||
sorter = function(a, b) {
|
|
||||||
a = a.data[key];
|
|
||||||
b = b.data[key];
|
|
||||||
return a < b ? -1 : a > b ? 1 : 0;
|
|
||||||
},
|
|
||||||
finalSorter = sorter,
|
|
||||||
tableBody = document.querySelector('.coverage-summary tbody'),
|
|
||||||
rowNodes = tableBody.querySelectorAll('tr'),
|
|
||||||
rows = [],
|
|
||||||
i;
|
|
||||||
|
|
||||||
if (desc) {
|
|
||||||
finalSorter = function(a, b) {
|
|
||||||
return -1 * sorter(a, b);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < rowNodes.length; i += 1) {
|
|
||||||
rows.push(rowNodes[i]);
|
|
||||||
tableBody.removeChild(rowNodes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.sort(finalSorter);
|
|
||||||
|
|
||||||
for (i = 0; i < rows.length; i += 1) {
|
|
||||||
tableBody.appendChild(rows[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// removes sort indicators for current column being sorted
|
|
||||||
function removeSortIndicators() {
|
|
||||||
var col = getNthColumn(currentSort.index),
|
|
||||||
cls = col.className;
|
|
||||||
|
|
||||||
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
|
|
||||||
col.className = cls;
|
|
||||||
}
|
|
||||||
// adds sort indicators for current column being sorted
|
|
||||||
function addSortIndicators() {
|
|
||||||
getNthColumn(currentSort.index).className += currentSort.desc
|
|
||||||
? ' sorted-desc'
|
|
||||||
: ' sorted';
|
|
||||||
}
|
|
||||||
// adds event listeners for all sorter widgets
|
|
||||||
function enableUI() {
|
|
||||||
var i,
|
|
||||||
el,
|
|
||||||
ithSorter = function ithSorter(i) {
|
|
||||||
var col = cols[i];
|
|
||||||
|
|
||||||
return function() {
|
|
||||||
var desc = col.defaultDescSort;
|
|
||||||
|
|
||||||
if (currentSort.index === i) {
|
|
||||||
desc = !currentSort.desc;
|
|
||||||
}
|
|
||||||
sortByIndex(i, desc);
|
|
||||||
removeSortIndicators();
|
|
||||||
currentSort.index = i;
|
|
||||||
currentSort.desc = desc;
|
|
||||||
addSortIndicators();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
for (i = 0; i < cols.length; i += 1) {
|
|
||||||
if (cols[i].sortable) {
|
|
||||||
// add the click event handler on the th so users
|
|
||||||
// dont have to click on those tiny arrows
|
|
||||||
el = getNthColumn(i).querySelector('.sorter').parentElement;
|
|
||||||
if (el.addEventListener) {
|
|
||||||
el.addEventListener('click', ithSorter(i));
|
|
||||||
} else {
|
|
||||||
el.attachEvent('onclick', ithSorter(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// adds sorting functionality to the UI
|
|
||||||
return function() {
|
|
||||||
if (!getTable()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cols = loadColumns();
|
|
||||||
loadData();
|
|
||||||
addSearchBox();
|
|
||||||
addSortIndicators();
|
|
||||||
enableUI();
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
window.addEventListener('load', addSorting);
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Code coverage report for src</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="../prettify.css" />
|
|
||||||
<link rel="stylesheet" href="../base.css" />
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style type='text/css'>
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
background-image: url(../sort-arrow-sprite.png);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class='wrapper'>
|
|
||||||
<div class='pad1'>
|
|
||||||
<h1><a href="../index.html">All files</a> src</h1>
|
|
||||||
<div class='clearfix'>
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">92.94% </span>
|
|
||||||
<span class="quiet">Statements</span>
|
|
||||||
<span class='fraction'>79/85</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">74.07% </span>
|
|
||||||
<span class="quiet">Branches</span>
|
|
||||||
<span class='fraction'>80/108</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">100% </span>
|
|
||||||
<span class="quiet">Functions</span>
|
|
||||||
<span class='fraction'>5/5</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">95.94% </span>
|
|
||||||
<span class="quiet">Lines</span>
|
|
||||||
<span class='fraction'>71/74</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p class="quiet">
|
|
||||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
||||||
</p>
|
|
||||||
<template id="filterTemplate">
|
|
||||||
<div class="quiet">
|
|
||||||
Filter:
|
|
||||||
<input type="search" id="fileSearch">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class='status-line high'></div>
|
|
||||||
<div class="pad1">
|
|
||||||
<table class="coverage-summary">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
|
|
||||||
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
|
|
||||||
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
|
|
||||||
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
|
|
||||||
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
|
|
||||||
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
|
|
||||||
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody><tr>
|
|
||||||
<td class="file high" data-value="ui-parser.ts"><a href="ui-parser.ts.html">ui-parser.ts</a></td>
|
|
||||||
<td data-value="92.94" class="pic high">
|
|
||||||
<div class="chart"><div class="cover-fill" style="width: 92%"></div><div class="cover-empty" style="width: 8%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="92.94" class="pct high">92.94%</td>
|
|
||||||
<td data-value="85" class="abs high">79/85</td>
|
|
||||||
<td data-value="74.07" class="pct medium">74.07%</td>
|
|
||||||
<td data-value="108" class="abs medium">80/108</td>
|
|
||||||
<td data-value="100" class="pct high">100%</td>
|
|
||||||
<td data-value="5" class="abs high">5/5</td>
|
|
||||||
<td data-value="95.94" class="pct high">95.94%</td>
|
|
||||||
<td data-value="74" class="abs high">71/74</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class='push'></div><!-- for sticky footer -->
|
|
||||||
</div><!-- /wrapper -->
|
|
||||||
<div class='footer quiet pad2 space-top1 center small'>
|
|
||||||
Code coverage generated by
|
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
||||||
at 2026-04-12T12:19:30.601Z
|
|
||||||
</div>
|
|
||||||
<script src="../prettify.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
prettyPrint();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="../sorter.js"></script>
|
|
||||||
<script src="../block-navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,206 +0,0 @@
|
|||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Code coverage report for src/server</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="../../prettify.css" />
|
|
||||||
<link rel="stylesheet" href="../../base.css" />
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style type='text/css'>
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
background-image: url(../../sort-arrow-sprite.png);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class='wrapper'>
|
|
||||||
<div class='pad1'>
|
|
||||||
<h1><a href="../../index.html">All files</a> src/server</h1>
|
|
||||||
<div class='clearfix'>
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">40.43% </span>
|
|
||||||
<span class="quiet">Statements</span>
|
|
||||||
<span class='fraction'>243/601</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">38.46% </span>
|
|
||||||
<span class="quiet">Branches</span>
|
|
||||||
<span class='fraction'>200/520</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">33.78% </span>
|
|
||||||
<span class="quiet">Functions</span>
|
|
||||||
<span class='fraction'>25/74</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">39.51% </span>
|
|
||||||
<span class="quiet">Lines</span>
|
|
||||||
<span class='fraction'>213/539</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p class="quiet">
|
|
||||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
||||||
</p>
|
|
||||||
<template id="filterTemplate">
|
|
||||||
<div class="quiet">
|
|
||||||
Filter:
|
|
||||||
<input type="search" id="fileSearch">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class='status-line low'></div>
|
|
||||||
<div class="pad1">
|
|
||||||
<table class="coverage-summary">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
|
|
||||||
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
|
|
||||||
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
|
|
||||||
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
|
|
||||||
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
|
|
||||||
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
|
|
||||||
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody><tr>
|
|
||||||
<td class="file low" data-value="execute.ts"><a href="execute.ts.html">execute.ts</a></td>
|
|
||||||
<td data-value="0" class="pic low">
|
|
||||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="198" class="abs low">0/198</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="201" class="abs low">0/201</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="25" class="abs low">0/25</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="178" class="abs low">0/178</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="file low" data-value="index.ts"><a href="index.ts.html">index.ts</a></td>
|
|
||||||
<td data-value="0" class="pic low">
|
|
||||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="1" class="abs low">0/1</td>
|
|
||||||
<td data-value="100" class="pct high">100%</td>
|
|
||||||
<td data-value="0" class="abs high">0/0</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="1" class="abs low">0/1</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="1" class="abs low">0/1</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="file high" data-value="job-manifest.ts"><a href="job-manifest.ts.html">job-manifest.ts</a></td>
|
|
||||||
<td data-value="84.45" class="pic high">
|
|
||||||
<div class="chart"><div class="cover-fill" style="width: 84%"></div><div class="cover-empty" style="width: 16%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="84.45" class="pct high">84.45%</td>
|
|
||||||
<td data-value="148" class="abs high">125/148</td>
|
|
||||||
<td data-value="66.07" class="pct medium">66.07%</td>
|
|
||||||
<td data-value="112" class="abs medium">74/112</td>
|
|
||||||
<td data-value="90.9" class="pct high">90.9%</td>
|
|
||||||
<td data-value="11" class="abs high">10/11</td>
|
|
||||||
<td data-value="84.73" class="pct high">84.73%</td>
|
|
||||||
<td data-value="131" class="abs high">111/131</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="file low" data-value="k8s-client.ts"><a href="k8s-client.ts.html">k8s-client.ts</a></td>
|
|
||||||
<td data-value="0" class="pic low">
|
|
||||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="60" class="abs low">0/60</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="32" class="abs low">0/32</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="13" class="abs low">0/13</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="56" class="abs low">0/56</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="file high" data-value="parse.ts"><a href="parse.ts.html">parse.ts</a></td>
|
|
||||||
<td data-value="91.08" class="pic high">
|
|
||||||
<div class="chart"><div class="cover-fill" style="width: 91%"></div><div class="cover-empty" style="width: 9%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="91.08" class="pct high">91.08%</td>
|
|
||||||
<td data-value="101" class="abs high">92/101</td>
|
|
||||||
<td data-value="78.4" class="pct medium">78.4%</td>
|
|
||||||
<td data-value="88" class="abs medium">69/88</td>
|
|
||||||
<td data-value="100" class="pct high">100%</td>
|
|
||||||
<td data-value="11" class="abs high">11/11</td>
|
|
||||||
<td data-value="91.01" class="pct high">91.01%</td>
|
|
||||||
<td data-value="89" class="abs high">81/89</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="file high" data-value="session.ts"><a href="session.ts.html">session.ts</a></td>
|
|
||||||
<td data-value="100" class="pic high">
|
|
||||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="100" class="pct high">100%</td>
|
|
||||||
<td data-value="26" class="abs high">26/26</td>
|
|
||||||
<td data-value="100" class="pct high">100%</td>
|
|
||||||
<td data-value="57" class="abs high">57/57</td>
|
|
||||||
<td data-value="100" class="pct high">100%</td>
|
|
||||||
<td data-value="4" class="abs high">4/4</td>
|
|
||||||
<td data-value="100" class="pct high">100%</td>
|
|
||||||
<td data-value="21" class="abs high">21/21</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="file low" data-value="test.ts"><a href="test.ts.html">test.ts</a></td>
|
|
||||||
<td data-value="0" class="pic low">
|
|
||||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
|
||||||
</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="67" class="abs low">0/67</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="30" class="abs low">0/30</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="9" class="abs low">0/9</td>
|
|
||||||
<td data-value="0" class="pct low">0%</td>
|
|
||||||
<td data-value="63" class="abs low">0/63</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class='push'></div><!-- for sticky footer -->
|
|
||||||
</div><!-- /wrapper -->
|
|
||||||
<div class='footer quiet pad2 space-top1 center small'>
|
|
||||||
Code coverage generated by
|
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
||||||
at 2026-04-12T12:19:30.601Z
|
|
||||||
</div>
|
|
||||||
<script src="../../prettify.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
prettyPrint();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="../../sorter.js"></script>
|
|
||||||
<script src="../../block-navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Code coverage report for src/server/index.ts</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="../../prettify.css" />
|
|
||||||
<link rel="stylesheet" href="../../base.css" />
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style type='text/css'>
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
background-image: url(../../sort-arrow-sprite.png);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class='wrapper'>
|
|
||||||
<div class='pad1'>
|
|
||||||
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/server</a> index.ts</h1>
|
|
||||||
<div class='clearfix'>
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Statements</span>
|
|
||||||
<span class='fraction'>0/1</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">100% </span>
|
|
||||||
<span class="quiet">Branches</span>
|
|
||||||
<span class='fraction'>0/0</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Functions</span>
|
|
||||||
<span class='fraction'>0/1</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Lines</span>
|
|
||||||
<span class='fraction'>0/1</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p class="quiet">
|
|
||||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
||||||
</p>
|
|
||||||
<template id="filterTemplate">
|
|
||||||
<div class="quiet">
|
|
||||||
Filter:
|
|
||||||
<input type="search" id="fileSearch">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class='status-line low'></div>
|
|
||||||
<pre><table class="coverage">
|
|
||||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
|
||||||
<a name='L2'></a><a href='#L2'>2</a>
|
|
||||||
<a name='L3'></a><a href='#L3'>3</a>
|
|
||||||
<a name='L4'></a><a href='#L4'>4</a>
|
|
||||||
<a name='L5'></a><a href='#L5'>5</a>
|
|
||||||
<a name='L6'></a><a href='#L6'>6</a>
|
|
||||||
<a name='L7'></a><a href='#L7'>7</a>
|
|
||||||
<a name='L8'></a><a href='#L8'>8</a>
|
|
||||||
<a name='L9'></a><a href='#L9'>9</a>
|
|
||||||
<a name='L10'></a><a href='#L10'>10</a>
|
|
||||||
<a name='L11'></a><a href='#L11'>11</a>
|
|
||||||
<a name='L12'></a><a href='#L12'>12</a>
|
|
||||||
<a name='L13'></a><a href='#L13'>13</a>
|
|
||||||
<a name='L14'></a><a href='#L14'>14</a>
|
|
||||||
<a name='L15'></a><a href='#L15'>15</a>
|
|
||||||
<a name='L16'></a><a href='#L16'>16</a>
|
|
||||||
<a name='L17'></a><a href='#L17'>17</a>
|
|
||||||
<a name='L18'></a><a href='#L18'>18</a>
|
|
||||||
<a name='L19'></a><a href='#L19'>19</a>
|
|
||||||
<a name='L20'></a><a href='#L20'>20</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import type { ServerAdapterModule } from "@paperclipai/adapter-utils";
|
|
||||||
import { type, models, agentConfigurationDoc } from "../index.js";
|
|
||||||
import { execute } from "./execute.js";
|
|
||||||
import { testEnvironment } from "./test.js";
|
|
||||||
import { sessionCodec } from "./session.js";
|
|
||||||
|
|
||||||
export function <span class="fstat-no" title="function not covered" >createServerAdapter(): ServerAdapterModule {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > return {</span>
|
|
||||||
type,
|
|
||||||
execute,
|
|
||||||
testEnvironment,
|
|
||||||
sessionCodec,
|
|
||||||
models,
|
|
||||||
supportsLocalAgentJwt: true,
|
|
||||||
agentConfigurationDoc,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export { execute, testEnvironment, sessionCodec };
|
|
||||||
</pre></td></tr></table></pre>
|
|
||||||
|
|
||||||
<div class='push'></div><!-- for sticky footer -->
|
|
||||||
</div><!-- /wrapper -->
|
|
||||||
<div class='footer quiet pad2 space-top1 center small'>
|
|
||||||
Code coverage generated by
|
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
||||||
at 2026-04-12T12:19:30.601Z
|
|
||||||
</div>
|
|
||||||
<script src="../../prettify.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
prettyPrint();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="../../sorter.js"></script>
|
|
||||||
<script src="../../block-navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,601 +0,0 @@
|
|||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Code coverage report for src/server/k8s-client.ts</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="../../prettify.css" />
|
|
||||||
<link rel="stylesheet" href="../../base.css" />
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style type='text/css'>
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
background-image: url(../../sort-arrow-sprite.png);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class='wrapper'>
|
|
||||||
<div class='pad1'>
|
|
||||||
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/server</a> k8s-client.ts</h1>
|
|
||||||
<div class='clearfix'>
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Statements</span>
|
|
||||||
<span class='fraction'>0/60</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Branches</span>
|
|
||||||
<span class='fraction'>0/32</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Functions</span>
|
|
||||||
<span class='fraction'>0/13</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Lines</span>
|
|
||||||
<span class='fraction'>0/56</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p class="quiet">
|
|
||||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
||||||
</p>
|
|
||||||
<template id="filterTemplate">
|
|
||||||
<div class="quiet">
|
|
||||||
Filter:
|
|
||||||
<input type="search" id="fileSearch">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class='status-line low'></div>
|
|
||||||
<pre><table class="coverage">
|
|
||||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
|
||||||
<a name='L2'></a><a href='#L2'>2</a>
|
|
||||||
<a name='L3'></a><a href='#L3'>3</a>
|
|
||||||
<a name='L4'></a><a href='#L4'>4</a>
|
|
||||||
<a name='L5'></a><a href='#L5'>5</a>
|
|
||||||
<a name='L6'></a><a href='#L6'>6</a>
|
|
||||||
<a name='L7'></a><a href='#L7'>7</a>
|
|
||||||
<a name='L8'></a><a href='#L8'>8</a>
|
|
||||||
<a name='L9'></a><a href='#L9'>9</a>
|
|
||||||
<a name='L10'></a><a href='#L10'>10</a>
|
|
||||||
<a name='L11'></a><a href='#L11'>11</a>
|
|
||||||
<a name='L12'></a><a href='#L12'>12</a>
|
|
||||||
<a name='L13'></a><a href='#L13'>13</a>
|
|
||||||
<a name='L14'></a><a href='#L14'>14</a>
|
|
||||||
<a name='L15'></a><a href='#L15'>15</a>
|
|
||||||
<a name='L16'></a><a href='#L16'>16</a>
|
|
||||||
<a name='L17'></a><a href='#L17'>17</a>
|
|
||||||
<a name='L18'></a><a href='#L18'>18</a>
|
|
||||||
<a name='L19'></a><a href='#L19'>19</a>
|
|
||||||
<a name='L20'></a><a href='#L20'>20</a>
|
|
||||||
<a name='L21'></a><a href='#L21'>21</a>
|
|
||||||
<a name='L22'></a><a href='#L22'>22</a>
|
|
||||||
<a name='L23'></a><a href='#L23'>23</a>
|
|
||||||
<a name='L24'></a><a href='#L24'>24</a>
|
|
||||||
<a name='L25'></a><a href='#L25'>25</a>
|
|
||||||
<a name='L26'></a><a href='#L26'>26</a>
|
|
||||||
<a name='L27'></a><a href='#L27'>27</a>
|
|
||||||
<a name='L28'></a><a href='#L28'>28</a>
|
|
||||||
<a name='L29'></a><a href='#L29'>29</a>
|
|
||||||
<a name='L30'></a><a href='#L30'>30</a>
|
|
||||||
<a name='L31'></a><a href='#L31'>31</a>
|
|
||||||
<a name='L32'></a><a href='#L32'>32</a>
|
|
||||||
<a name='L33'></a><a href='#L33'>33</a>
|
|
||||||
<a name='L34'></a><a href='#L34'>34</a>
|
|
||||||
<a name='L35'></a><a href='#L35'>35</a>
|
|
||||||
<a name='L36'></a><a href='#L36'>36</a>
|
|
||||||
<a name='L37'></a><a href='#L37'>37</a>
|
|
||||||
<a name='L38'></a><a href='#L38'>38</a>
|
|
||||||
<a name='L39'></a><a href='#L39'>39</a>
|
|
||||||
<a name='L40'></a><a href='#L40'>40</a>
|
|
||||||
<a name='L41'></a><a href='#L41'>41</a>
|
|
||||||
<a name='L42'></a><a href='#L42'>42</a>
|
|
||||||
<a name='L43'></a><a href='#L43'>43</a>
|
|
||||||
<a name='L44'></a><a href='#L44'>44</a>
|
|
||||||
<a name='L45'></a><a href='#L45'>45</a>
|
|
||||||
<a name='L46'></a><a href='#L46'>46</a>
|
|
||||||
<a name='L47'></a><a href='#L47'>47</a>
|
|
||||||
<a name='L48'></a><a href='#L48'>48</a>
|
|
||||||
<a name='L49'></a><a href='#L49'>49</a>
|
|
||||||
<a name='L50'></a><a href='#L50'>50</a>
|
|
||||||
<a name='L51'></a><a href='#L51'>51</a>
|
|
||||||
<a name='L52'></a><a href='#L52'>52</a>
|
|
||||||
<a name='L53'></a><a href='#L53'>53</a>
|
|
||||||
<a name='L54'></a><a href='#L54'>54</a>
|
|
||||||
<a name='L55'></a><a href='#L55'>55</a>
|
|
||||||
<a name='L56'></a><a href='#L56'>56</a>
|
|
||||||
<a name='L57'></a><a href='#L57'>57</a>
|
|
||||||
<a name='L58'></a><a href='#L58'>58</a>
|
|
||||||
<a name='L59'></a><a href='#L59'>59</a>
|
|
||||||
<a name='L60'></a><a href='#L60'>60</a>
|
|
||||||
<a name='L61'></a><a href='#L61'>61</a>
|
|
||||||
<a name='L62'></a><a href='#L62'>62</a>
|
|
||||||
<a name='L63'></a><a href='#L63'>63</a>
|
|
||||||
<a name='L64'></a><a href='#L64'>64</a>
|
|
||||||
<a name='L65'></a><a href='#L65'>65</a>
|
|
||||||
<a name='L66'></a><a href='#L66'>66</a>
|
|
||||||
<a name='L67'></a><a href='#L67'>67</a>
|
|
||||||
<a name='L68'></a><a href='#L68'>68</a>
|
|
||||||
<a name='L69'></a><a href='#L69'>69</a>
|
|
||||||
<a name='L70'></a><a href='#L70'>70</a>
|
|
||||||
<a name='L71'></a><a href='#L71'>71</a>
|
|
||||||
<a name='L72'></a><a href='#L72'>72</a>
|
|
||||||
<a name='L73'></a><a href='#L73'>73</a>
|
|
||||||
<a name='L74'></a><a href='#L74'>74</a>
|
|
||||||
<a name='L75'></a><a href='#L75'>75</a>
|
|
||||||
<a name='L76'></a><a href='#L76'>76</a>
|
|
||||||
<a name='L77'></a><a href='#L77'>77</a>
|
|
||||||
<a name='L78'></a><a href='#L78'>78</a>
|
|
||||||
<a name='L79'></a><a href='#L79'>79</a>
|
|
||||||
<a name='L80'></a><a href='#L80'>80</a>
|
|
||||||
<a name='L81'></a><a href='#L81'>81</a>
|
|
||||||
<a name='L82'></a><a href='#L82'>82</a>
|
|
||||||
<a name='L83'></a><a href='#L83'>83</a>
|
|
||||||
<a name='L84'></a><a href='#L84'>84</a>
|
|
||||||
<a name='L85'></a><a href='#L85'>85</a>
|
|
||||||
<a name='L86'></a><a href='#L86'>86</a>
|
|
||||||
<a name='L87'></a><a href='#L87'>87</a>
|
|
||||||
<a name='L88'></a><a href='#L88'>88</a>
|
|
||||||
<a name='L89'></a><a href='#L89'>89</a>
|
|
||||||
<a name='L90'></a><a href='#L90'>90</a>
|
|
||||||
<a name='L91'></a><a href='#L91'>91</a>
|
|
||||||
<a name='L92'></a><a href='#L92'>92</a>
|
|
||||||
<a name='L93'></a><a href='#L93'>93</a>
|
|
||||||
<a name='L94'></a><a href='#L94'>94</a>
|
|
||||||
<a name='L95'></a><a href='#L95'>95</a>
|
|
||||||
<a name='L96'></a><a href='#L96'>96</a>
|
|
||||||
<a name='L97'></a><a href='#L97'>97</a>
|
|
||||||
<a name='L98'></a><a href='#L98'>98</a>
|
|
||||||
<a name='L99'></a><a href='#L99'>99</a>
|
|
||||||
<a name='L100'></a><a href='#L100'>100</a>
|
|
||||||
<a name='L101'></a><a href='#L101'>101</a>
|
|
||||||
<a name='L102'></a><a href='#L102'>102</a>
|
|
||||||
<a name='L103'></a><a href='#L103'>103</a>
|
|
||||||
<a name='L104'></a><a href='#L104'>104</a>
|
|
||||||
<a name='L105'></a><a href='#L105'>105</a>
|
|
||||||
<a name='L106'></a><a href='#L106'>106</a>
|
|
||||||
<a name='L107'></a><a href='#L107'>107</a>
|
|
||||||
<a name='L108'></a><a href='#L108'>108</a>
|
|
||||||
<a name='L109'></a><a href='#L109'>109</a>
|
|
||||||
<a name='L110'></a><a href='#L110'>110</a>
|
|
||||||
<a name='L111'></a><a href='#L111'>111</a>
|
|
||||||
<a name='L112'></a><a href='#L112'>112</a>
|
|
||||||
<a name='L113'></a><a href='#L113'>113</a>
|
|
||||||
<a name='L114'></a><a href='#L114'>114</a>
|
|
||||||
<a name='L115'></a><a href='#L115'>115</a>
|
|
||||||
<a name='L116'></a><a href='#L116'>116</a>
|
|
||||||
<a name='L117'></a><a href='#L117'>117</a>
|
|
||||||
<a name='L118'></a><a href='#L118'>118</a>
|
|
||||||
<a name='L119'></a><a href='#L119'>119</a>
|
|
||||||
<a name='L120'></a><a href='#L120'>120</a>
|
|
||||||
<a name='L121'></a><a href='#L121'>121</a>
|
|
||||||
<a name='L122'></a><a href='#L122'>122</a>
|
|
||||||
<a name='L123'></a><a href='#L123'>123</a>
|
|
||||||
<a name='L124'></a><a href='#L124'>124</a>
|
|
||||||
<a name='L125'></a><a href='#L125'>125</a>
|
|
||||||
<a name='L126'></a><a href='#L126'>126</a>
|
|
||||||
<a name='L127'></a><a href='#L127'>127</a>
|
|
||||||
<a name='L128'></a><a href='#L128'>128</a>
|
|
||||||
<a name='L129'></a><a href='#L129'>129</a>
|
|
||||||
<a name='L130'></a><a href='#L130'>130</a>
|
|
||||||
<a name='L131'></a><a href='#L131'>131</a>
|
|
||||||
<a name='L132'></a><a href='#L132'>132</a>
|
|
||||||
<a name='L133'></a><a href='#L133'>133</a>
|
|
||||||
<a name='L134'></a><a href='#L134'>134</a>
|
|
||||||
<a name='L135'></a><a href='#L135'>135</a>
|
|
||||||
<a name='L136'></a><a href='#L136'>136</a>
|
|
||||||
<a name='L137'></a><a href='#L137'>137</a>
|
|
||||||
<a name='L138'></a><a href='#L138'>138</a>
|
|
||||||
<a name='L139'></a><a href='#L139'>139</a>
|
|
||||||
<a name='L140'></a><a href='#L140'>140</a>
|
|
||||||
<a name='L141'></a><a href='#L141'>141</a>
|
|
||||||
<a name='L142'></a><a href='#L142'>142</a>
|
|
||||||
<a name='L143'></a><a href='#L143'>143</a>
|
|
||||||
<a name='L144'></a><a href='#L144'>144</a>
|
|
||||||
<a name='L145'></a><a href='#L145'>145</a>
|
|
||||||
<a name='L146'></a><a href='#L146'>146</a>
|
|
||||||
<a name='L147'></a><a href='#L147'>147</a>
|
|
||||||
<a name='L148'></a><a href='#L148'>148</a>
|
|
||||||
<a name='L149'></a><a href='#L149'>149</a>
|
|
||||||
<a name='L150'></a><a href='#L150'>150</a>
|
|
||||||
<a name='L151'></a><a href='#L151'>151</a>
|
|
||||||
<a name='L152'></a><a href='#L152'>152</a>
|
|
||||||
<a name='L153'></a><a href='#L153'>153</a>
|
|
||||||
<a name='L154'></a><a href='#L154'>154</a>
|
|
||||||
<a name='L155'></a><a href='#L155'>155</a>
|
|
||||||
<a name='L156'></a><a href='#L156'>156</a>
|
|
||||||
<a name='L157'></a><a href='#L157'>157</a>
|
|
||||||
<a name='L158'></a><a href='#L158'>158</a>
|
|
||||||
<a name='L159'></a><a href='#L159'>159</a>
|
|
||||||
<a name='L160'></a><a href='#L160'>160</a>
|
|
||||||
<a name='L161'></a><a href='#L161'>161</a>
|
|
||||||
<a name='L162'></a><a href='#L162'>162</a>
|
|
||||||
<a name='L163'></a><a href='#L163'>163</a>
|
|
||||||
<a name='L164'></a><a href='#L164'>164</a>
|
|
||||||
<a name='L165'></a><a href='#L165'>165</a>
|
|
||||||
<a name='L166'></a><a href='#L166'>166</a>
|
|
||||||
<a name='L167'></a><a href='#L167'>167</a>
|
|
||||||
<a name='L168'></a><a href='#L168'>168</a>
|
|
||||||
<a name='L169'></a><a href='#L169'>169</a>
|
|
||||||
<a name='L170'></a><a href='#L170'>170</a>
|
|
||||||
<a name='L171'></a><a href='#L171'>171</a>
|
|
||||||
<a name='L172'></a><a href='#L172'>172</a>
|
|
||||||
<a name='L173'></a><a href='#L173'>173</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import * as k8s from "@kubernetes/client-node";
|
|
||||||
import { readFileSync } from "node:fs";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cached self-pod introspection result. Queried once on first execute(),
|
|
||||||
* then reused for all subsequent Job builds so every Job inherits the
|
|
||||||
* Deployment's image, imagePullSecrets, DNS config, and PVC claim.
|
|
||||||
*/
|
|
||||||
export interface SelfPodSecretVolume {
|
|
||||||
volumeName: string;
|
|
||||||
secretName: string;
|
|
||||||
mountPath: string;
|
|
||||||
defaultMode: number | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SelfPodInfo {
|
|
||||||
namespace: string;
|
|
||||||
image: string;
|
|
||||||
imagePullSecrets: Array<{ name: string }>;
|
|
||||||
dnsConfig: k8s.V1PodDNSConfig | undefined;
|
|
||||||
pvcClaimName: string | null;
|
|
||||||
secretVolumes: SelfPodSecretVolume[];
|
|
||||||
/** Env vars inherited from the Deployment container. */
|
|
||||||
inheritedEnv: Record<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Keys forwarded from the Deployment container env into Job pods. */
|
|
||||||
const INHERITED_ENV_KEYS = <span class="cstat-no" title="statement not covered" >[</span>
|
|
||||||
"CLAUDE_CODE_USE_BEDROCK",
|
|
||||||
"AWS_REGION",
|
|
||||||
"AWS_BEARER_TOKEN_BEDROCK",
|
|
||||||
"ANTHROPIC_API_KEY",
|
|
||||||
"OPENAI_API_KEY",
|
|
||||||
"PAPERCLIP_API_URL",
|
|
||||||
];
|
|
||||||
|
|
||||||
let cachedSelfPod: SelfPodInfo | null = <span class="cstat-no" title="statement not covered" >null;</span>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache keyed by kubeconfig path (empty string = in-cluster).
|
|
||||||
* Supports multiple agents with different kubeconfigs.
|
|
||||||
*/
|
|
||||||
const kcCache = <span class="cstat-no" title="statement not covered" >new Map<string, k8s.KubeConfig>();</span>
|
|
||||||
|
|
||||||
function <span class="fstat-no" title="function not covered" >getKubeConfig(k</span>ubeconfigPath?: string): k8s.KubeConfig {
|
|
||||||
const key = <span class="cstat-no" title="statement not covered" >kubeconfigPath ?? "";</span>
|
|
||||||
let kc = <span class="cstat-no" title="statement not covered" >kcCache.get(key);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (!kc) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > kc = new k8s.KubeConfig();</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (kubeconfigPath) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > kc.loadFromFile(kubeconfigPath);</span>
|
|
||||||
} else {
|
|
||||||
<span class="cstat-no" title="statement not covered" > kc.loadFromCluster();</span>
|
|
||||||
}
|
|
||||||
<span class="cstat-no" title="statement not covered" > kcCache.set(key, kc);</span>
|
|
||||||
}
|
|
||||||
<span class="cstat-no" title="statement not covered" > return kc;</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function <span class="fstat-no" title="function not covered" >getBatchApi(k</span>ubeconfigPath?: string): k8s.BatchV1Api {
|
|
||||||
<span class="cstat-no" title="statement not covered" > return getKubeConfig(kubeconfigPath).makeApiClient(k8s.BatchV1Api);</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function <span class="fstat-no" title="function not covered" >getCoreApi(k</span>ubeconfigPath?: string): k8s.CoreV1Api {
|
|
||||||
<span class="cstat-no" title="statement not covered" > return getKubeConfig(kubeconfigPath).makeApiClient(k8s.CoreV1Api);</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function <span class="fstat-no" title="function not covered" >getAuthzApi(k</span>ubeconfigPath?: string): k8s.AuthorizationV1Api {
|
|
||||||
<span class="cstat-no" title="statement not covered" > return getKubeConfig(kubeconfigPath).makeApiClient(k8s.AuthorizationV1Api);</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function <span class="fstat-no" title="function not covered" >getLogApi(k</span>ubeconfigPath?: string): k8s.Log {
|
|
||||||
<span class="cstat-no" title="statement not covered" > return new k8s.Log(getKubeConfig(kubeconfigPath));</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the current pod's namespace. Checks (in order):
|
|
||||||
* 1. PAPERCLIP_NAMESPACE env var (set explicitly in Deployment)
|
|
||||||
* 2. Service account namespace file (standard in-cluster path)
|
|
||||||
* 3. POD_NAMESPACE env var (Downward API convention)
|
|
||||||
* Falls back to "default" only if none of the above are available.
|
|
||||||
*/
|
|
||||||
function <span class="fstat-no" title="function not covered" >readInClusterNamespace(): string {</span>
|
|
||||||
const fromEnv = <span class="cstat-no" title="statement not covered" >process.env.PAPERCLIP_NAMESPACE ?? process.env.POD_NAMESPACE;</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (fromEnv?.trim()) <span class="cstat-no" title="statement not covered" >return fromEnv.trim();</span></span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > return readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/namespace", "utf-8").trim();</span>
|
|
||||||
} catch {
|
|
||||||
<span class="cstat-no" title="statement not covered" > return "default";</span>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query the K8s API for our own pod spec and cache the result.
|
|
||||||
* Extracts image, imagePullSecrets, dnsConfig, PVC claim name,
|
|
||||||
* and environment variables to forward to Job pods.
|
|
||||||
*/
|
|
||||||
export async function <span class="fstat-no" title="function not covered" >getSelfPodInfo(k</span>ubeconfigPath?: string): Promise<SelfPodInfo> {
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (cachedSelfPod) <span class="cstat-no" title="statement not covered" >return cachedSelfPod;</span></span>
|
|
||||||
|
|
||||||
const hostname = <span class="cstat-no" title="statement not covered" >process.env.HOSTNAME;</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (!hostname) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error("claude_k8s: HOSTNAME env var not set — cannot introspect running pod");</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
const namespace = <span class="cstat-no" title="statement not covered" >readInClusterNamespace();</span>
|
|
||||||
const coreApi = <span class="cstat-no" title="statement not covered" >getCoreApi(kubeconfigPath);</span>
|
|
||||||
const pod = <span class="cstat-no" title="statement not covered" >await coreApi.readNamespacedPod({ name: hostname, namespace });</span>
|
|
||||||
|
|
||||||
const spec = <span class="cstat-no" title="statement not covered" >pod.spec;</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (!spec) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error(`claude_k8s: pod ${hostname} has no spec`);</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
const mainContainer = <span class="cstat-no" title="statement not covered" >spec.containers[0];</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (!mainContainer?.image) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error(`claude_k8s: pod ${hostname} has no container image`);</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find PVC claim name from volumes mounted at /paperclip
|
|
||||||
let pvcClaimName: string | null = <span class="cstat-no" title="statement not covered" >null;</span>
|
|
||||||
const dataMount = <span class="cstat-no" title="statement not covered" >mainContainer.volumeMounts?.<span class="fstat-no" title="function not covered" >find(</span></span>
|
|
||||||
(vm) => <span class="cstat-no" title="statement not covered" >vm.mountPath === "/paperclip",</span>
|
|
||||||
);
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (dataMount) {</span>
|
|
||||||
const volume = <span class="cstat-no" title="statement not covered" >spec.volumes?.<span class="fstat-no" title="function not covered" >find((v</span>) => <span class="cstat-no" title="statement not covered" >v.name === dataMount.name);</span></span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > pvcClaimName = volume?.persistentVolumeClaim?.claimName ?? null;</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discover secret volumes mounted on the main container
|
|
||||||
const secretVolumes: SelfPodSecretVolume[] = <span class="cstat-no" title="statement not covered" >[];</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > for (const vm of mainContainer.volumeMounts ?? []) {</span>
|
|
||||||
const vol = <span class="cstat-no" title="statement not covered" >spec.volumes?.<span class="fstat-no" title="function not covered" >find((v</span>) => <span class="cstat-no" title="statement not covered" >v.name === vm.name);</span></span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (vol?.secret?.secretName) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > secretVolumes.push({</span>
|
|
||||||
volumeName: vm.name,
|
|
||||||
secretName: vol.secret.secretName,
|
|
||||||
mountPath: vm.mountPath,
|
|
||||||
defaultMode: vol.secret.defaultMode,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect inherited env vars from process.env (these came from the Deployment spec)
|
|
||||||
const inheritedEnv: Record<string, string> = <span class="cstat-no" title="statement not covered" >{};</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > for (const key of INHERITED_ENV_KEYS) {</span>
|
|
||||||
const value = <span class="cstat-no" title="statement not covered" >process.env[key];</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (value !== undefined) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > inheritedEnv[key] = value;</span>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > cachedSelfPod = {</span>
|
|
||||||
namespace,
|
|
||||||
image: mainContainer.image,
|
|
||||||
imagePullSecrets: (spec.imagePullSecrets ?? []).<span class="fstat-no" title="function not covered" >map((s</span>) => (<span class="cstat-no" title="statement not covered" >{</span>
|
|
||||||
name: s.name ?? "",
|
|
||||||
})).<span class="fstat-no" title="function not covered" >filter((s</span>) => <span class="cstat-no" title="statement not covered" >s.name.length > 0),</span>
|
|
||||||
dnsConfig: spec.dnsConfig,
|
|
||||||
pvcClaimName,
|
|
||||||
secretVolumes,
|
|
||||||
inheritedEnv,
|
|
||||||
};
|
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > return cachedSelfPod;</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reset cached state — useful for tests. */
|
|
||||||
export function <span class="fstat-no" title="function not covered" >resetCache(): void {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > kcCache.clear();</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > cachedSelfPod = null;</span>
|
|
||||||
}
|
|
||||||
</pre></td></tr></table></pre>
|
|
||||||
|
|
||||||
<div class='push'></div><!-- for sticky footer -->
|
|
||||||
</div><!-- /wrapper -->
|
|
||||||
<div class='footer quiet pad2 space-top1 center small'>
|
|
||||||
Code coverage generated by
|
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
||||||
at 2026-04-12T12:19:30.601Z
|
|
||||||
</div>
|
|
||||||
<script src="../../prettify.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
prettyPrint();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="../../sorter.js"></script>
|
|
||||||
<script src="../../block-navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,622 +0,0 @@
|
|||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Code coverage report for src/server/parse.ts</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="../../prettify.css" />
|
|
||||||
<link rel="stylesheet" href="../../base.css" />
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style type='text/css'>
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
background-image: url(../../sort-arrow-sprite.png);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class='wrapper'>
|
|
||||||
<div class='pad1'>
|
|
||||||
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/server</a> parse.ts</h1>
|
|
||||||
<div class='clearfix'>
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">91.08% </span>
|
|
||||||
<span class="quiet">Statements</span>
|
|
||||||
<span class='fraction'>92/101</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">78.4% </span>
|
|
||||||
<span class="quiet">Branches</span>
|
|
||||||
<span class='fraction'>69/88</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">100% </span>
|
|
||||||
<span class="quiet">Functions</span>
|
|
||||||
<span class='fraction'>11/11</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">91.01% </span>
|
|
||||||
<span class="quiet">Lines</span>
|
|
||||||
<span class='fraction'>81/89</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p class="quiet">
|
|
||||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
||||||
</p>
|
|
||||||
<template id="filterTemplate">
|
|
||||||
<div class="quiet">
|
|
||||||
Filter:
|
|
||||||
<input type="search" id="fileSearch">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class='status-line high'></div>
|
|
||||||
<pre><table class="coverage">
|
|
||||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
|
||||||
<a name='L2'></a><a href='#L2'>2</a>
|
|
||||||
<a name='L3'></a><a href='#L3'>3</a>
|
|
||||||
<a name='L4'></a><a href='#L4'>4</a>
|
|
||||||
<a name='L5'></a><a href='#L5'>5</a>
|
|
||||||
<a name='L6'></a><a href='#L6'>6</a>
|
|
||||||
<a name='L7'></a><a href='#L7'>7</a>
|
|
||||||
<a name='L8'></a><a href='#L8'>8</a>
|
|
||||||
<a name='L9'></a><a href='#L9'>9</a>
|
|
||||||
<a name='L10'></a><a href='#L10'>10</a>
|
|
||||||
<a name='L11'></a><a href='#L11'>11</a>
|
|
||||||
<a name='L12'></a><a href='#L12'>12</a>
|
|
||||||
<a name='L13'></a><a href='#L13'>13</a>
|
|
||||||
<a name='L14'></a><a href='#L14'>14</a>
|
|
||||||
<a name='L15'></a><a href='#L15'>15</a>
|
|
||||||
<a name='L16'></a><a href='#L16'>16</a>
|
|
||||||
<a name='L17'></a><a href='#L17'>17</a>
|
|
||||||
<a name='L18'></a><a href='#L18'>18</a>
|
|
||||||
<a name='L19'></a><a href='#L19'>19</a>
|
|
||||||
<a name='L20'></a><a href='#L20'>20</a>
|
|
||||||
<a name='L21'></a><a href='#L21'>21</a>
|
|
||||||
<a name='L22'></a><a href='#L22'>22</a>
|
|
||||||
<a name='L23'></a><a href='#L23'>23</a>
|
|
||||||
<a name='L24'></a><a href='#L24'>24</a>
|
|
||||||
<a name='L25'></a><a href='#L25'>25</a>
|
|
||||||
<a name='L26'></a><a href='#L26'>26</a>
|
|
||||||
<a name='L27'></a><a href='#L27'>27</a>
|
|
||||||
<a name='L28'></a><a href='#L28'>28</a>
|
|
||||||
<a name='L29'></a><a href='#L29'>29</a>
|
|
||||||
<a name='L30'></a><a href='#L30'>30</a>
|
|
||||||
<a name='L31'></a><a href='#L31'>31</a>
|
|
||||||
<a name='L32'></a><a href='#L32'>32</a>
|
|
||||||
<a name='L33'></a><a href='#L33'>33</a>
|
|
||||||
<a name='L34'></a><a href='#L34'>34</a>
|
|
||||||
<a name='L35'></a><a href='#L35'>35</a>
|
|
||||||
<a name='L36'></a><a href='#L36'>36</a>
|
|
||||||
<a name='L37'></a><a href='#L37'>37</a>
|
|
||||||
<a name='L38'></a><a href='#L38'>38</a>
|
|
||||||
<a name='L39'></a><a href='#L39'>39</a>
|
|
||||||
<a name='L40'></a><a href='#L40'>40</a>
|
|
||||||
<a name='L41'></a><a href='#L41'>41</a>
|
|
||||||
<a name='L42'></a><a href='#L42'>42</a>
|
|
||||||
<a name='L43'></a><a href='#L43'>43</a>
|
|
||||||
<a name='L44'></a><a href='#L44'>44</a>
|
|
||||||
<a name='L45'></a><a href='#L45'>45</a>
|
|
||||||
<a name='L46'></a><a href='#L46'>46</a>
|
|
||||||
<a name='L47'></a><a href='#L47'>47</a>
|
|
||||||
<a name='L48'></a><a href='#L48'>48</a>
|
|
||||||
<a name='L49'></a><a href='#L49'>49</a>
|
|
||||||
<a name='L50'></a><a href='#L50'>50</a>
|
|
||||||
<a name='L51'></a><a href='#L51'>51</a>
|
|
||||||
<a name='L52'></a><a href='#L52'>52</a>
|
|
||||||
<a name='L53'></a><a href='#L53'>53</a>
|
|
||||||
<a name='L54'></a><a href='#L54'>54</a>
|
|
||||||
<a name='L55'></a><a href='#L55'>55</a>
|
|
||||||
<a name='L56'></a><a href='#L56'>56</a>
|
|
||||||
<a name='L57'></a><a href='#L57'>57</a>
|
|
||||||
<a name='L58'></a><a href='#L58'>58</a>
|
|
||||||
<a name='L59'></a><a href='#L59'>59</a>
|
|
||||||
<a name='L60'></a><a href='#L60'>60</a>
|
|
||||||
<a name='L61'></a><a href='#L61'>61</a>
|
|
||||||
<a name='L62'></a><a href='#L62'>62</a>
|
|
||||||
<a name='L63'></a><a href='#L63'>63</a>
|
|
||||||
<a name='L64'></a><a href='#L64'>64</a>
|
|
||||||
<a name='L65'></a><a href='#L65'>65</a>
|
|
||||||
<a name='L66'></a><a href='#L66'>66</a>
|
|
||||||
<a name='L67'></a><a href='#L67'>67</a>
|
|
||||||
<a name='L68'></a><a href='#L68'>68</a>
|
|
||||||
<a name='L69'></a><a href='#L69'>69</a>
|
|
||||||
<a name='L70'></a><a href='#L70'>70</a>
|
|
||||||
<a name='L71'></a><a href='#L71'>71</a>
|
|
||||||
<a name='L72'></a><a href='#L72'>72</a>
|
|
||||||
<a name='L73'></a><a href='#L73'>73</a>
|
|
||||||
<a name='L74'></a><a href='#L74'>74</a>
|
|
||||||
<a name='L75'></a><a href='#L75'>75</a>
|
|
||||||
<a name='L76'></a><a href='#L76'>76</a>
|
|
||||||
<a name='L77'></a><a href='#L77'>77</a>
|
|
||||||
<a name='L78'></a><a href='#L78'>78</a>
|
|
||||||
<a name='L79'></a><a href='#L79'>79</a>
|
|
||||||
<a name='L80'></a><a href='#L80'>80</a>
|
|
||||||
<a name='L81'></a><a href='#L81'>81</a>
|
|
||||||
<a name='L82'></a><a href='#L82'>82</a>
|
|
||||||
<a name='L83'></a><a href='#L83'>83</a>
|
|
||||||
<a name='L84'></a><a href='#L84'>84</a>
|
|
||||||
<a name='L85'></a><a href='#L85'>85</a>
|
|
||||||
<a name='L86'></a><a href='#L86'>86</a>
|
|
||||||
<a name='L87'></a><a href='#L87'>87</a>
|
|
||||||
<a name='L88'></a><a href='#L88'>88</a>
|
|
||||||
<a name='L89'></a><a href='#L89'>89</a>
|
|
||||||
<a name='L90'></a><a href='#L90'>90</a>
|
|
||||||
<a name='L91'></a><a href='#L91'>91</a>
|
|
||||||
<a name='L92'></a><a href='#L92'>92</a>
|
|
||||||
<a name='L93'></a><a href='#L93'>93</a>
|
|
||||||
<a name='L94'></a><a href='#L94'>94</a>
|
|
||||||
<a name='L95'></a><a href='#L95'>95</a>
|
|
||||||
<a name='L96'></a><a href='#L96'>96</a>
|
|
||||||
<a name='L97'></a><a href='#L97'>97</a>
|
|
||||||
<a name='L98'></a><a href='#L98'>98</a>
|
|
||||||
<a name='L99'></a><a href='#L99'>99</a>
|
|
||||||
<a name='L100'></a><a href='#L100'>100</a>
|
|
||||||
<a name='L101'></a><a href='#L101'>101</a>
|
|
||||||
<a name='L102'></a><a href='#L102'>102</a>
|
|
||||||
<a name='L103'></a><a href='#L103'>103</a>
|
|
||||||
<a name='L104'></a><a href='#L104'>104</a>
|
|
||||||
<a name='L105'></a><a href='#L105'>105</a>
|
|
||||||
<a name='L106'></a><a href='#L106'>106</a>
|
|
||||||
<a name='L107'></a><a href='#L107'>107</a>
|
|
||||||
<a name='L108'></a><a href='#L108'>108</a>
|
|
||||||
<a name='L109'></a><a href='#L109'>109</a>
|
|
||||||
<a name='L110'></a><a href='#L110'>110</a>
|
|
||||||
<a name='L111'></a><a href='#L111'>111</a>
|
|
||||||
<a name='L112'></a><a href='#L112'>112</a>
|
|
||||||
<a name='L113'></a><a href='#L113'>113</a>
|
|
||||||
<a name='L114'></a><a href='#L114'>114</a>
|
|
||||||
<a name='L115'></a><a href='#L115'>115</a>
|
|
||||||
<a name='L116'></a><a href='#L116'>116</a>
|
|
||||||
<a name='L117'></a><a href='#L117'>117</a>
|
|
||||||
<a name='L118'></a><a href='#L118'>118</a>
|
|
||||||
<a name='L119'></a><a href='#L119'>119</a>
|
|
||||||
<a name='L120'></a><a href='#L120'>120</a>
|
|
||||||
<a name='L121'></a><a href='#L121'>121</a>
|
|
||||||
<a name='L122'></a><a href='#L122'>122</a>
|
|
||||||
<a name='L123'></a><a href='#L123'>123</a>
|
|
||||||
<a name='L124'></a><a href='#L124'>124</a>
|
|
||||||
<a name='L125'></a><a href='#L125'>125</a>
|
|
||||||
<a name='L126'></a><a href='#L126'>126</a>
|
|
||||||
<a name='L127'></a><a href='#L127'>127</a>
|
|
||||||
<a name='L128'></a><a href='#L128'>128</a>
|
|
||||||
<a name='L129'></a><a href='#L129'>129</a>
|
|
||||||
<a name='L130'></a><a href='#L130'>130</a>
|
|
||||||
<a name='L131'></a><a href='#L131'>131</a>
|
|
||||||
<a name='L132'></a><a href='#L132'>132</a>
|
|
||||||
<a name='L133'></a><a href='#L133'>133</a>
|
|
||||||
<a name='L134'></a><a href='#L134'>134</a>
|
|
||||||
<a name='L135'></a><a href='#L135'>135</a>
|
|
||||||
<a name='L136'></a><a href='#L136'>136</a>
|
|
||||||
<a name='L137'></a><a href='#L137'>137</a>
|
|
||||||
<a name='L138'></a><a href='#L138'>138</a>
|
|
||||||
<a name='L139'></a><a href='#L139'>139</a>
|
|
||||||
<a name='L140'></a><a href='#L140'>140</a>
|
|
||||||
<a name='L141'></a><a href='#L141'>141</a>
|
|
||||||
<a name='L142'></a><a href='#L142'>142</a>
|
|
||||||
<a name='L143'></a><a href='#L143'>143</a>
|
|
||||||
<a name='L144'></a><a href='#L144'>144</a>
|
|
||||||
<a name='L145'></a><a href='#L145'>145</a>
|
|
||||||
<a name='L146'></a><a href='#L146'>146</a>
|
|
||||||
<a name='L147'></a><a href='#L147'>147</a>
|
|
||||||
<a name='L148'></a><a href='#L148'>148</a>
|
|
||||||
<a name='L149'></a><a href='#L149'>149</a>
|
|
||||||
<a name='L150'></a><a href='#L150'>150</a>
|
|
||||||
<a name='L151'></a><a href='#L151'>151</a>
|
|
||||||
<a name='L152'></a><a href='#L152'>152</a>
|
|
||||||
<a name='L153'></a><a href='#L153'>153</a>
|
|
||||||
<a name='L154'></a><a href='#L154'>154</a>
|
|
||||||
<a name='L155'></a><a href='#L155'>155</a>
|
|
||||||
<a name='L156'></a><a href='#L156'>156</a>
|
|
||||||
<a name='L157'></a><a href='#L157'>157</a>
|
|
||||||
<a name='L158'></a><a href='#L158'>158</a>
|
|
||||||
<a name='L159'></a><a href='#L159'>159</a>
|
|
||||||
<a name='L160'></a><a href='#L160'>160</a>
|
|
||||||
<a name='L161'></a><a href='#L161'>161</a>
|
|
||||||
<a name='L162'></a><a href='#L162'>162</a>
|
|
||||||
<a name='L163'></a><a href='#L163'>163</a>
|
|
||||||
<a name='L164'></a><a href='#L164'>164</a>
|
|
||||||
<a name='L165'></a><a href='#L165'>165</a>
|
|
||||||
<a name='L166'></a><a href='#L166'>166</a>
|
|
||||||
<a name='L167'></a><a href='#L167'>167</a>
|
|
||||||
<a name='L168'></a><a href='#L168'>168</a>
|
|
||||||
<a name='L169'></a><a href='#L169'>169</a>
|
|
||||||
<a name='L170'></a><a href='#L170'>170</a>
|
|
||||||
<a name='L171'></a><a href='#L171'>171</a>
|
|
||||||
<a name='L172'></a><a href='#L172'>172</a>
|
|
||||||
<a name='L173'></a><a href='#L173'>173</a>
|
|
||||||
<a name='L174'></a><a href='#L174'>174</a>
|
|
||||||
<a name='L175'></a><a href='#L175'>175</a>
|
|
||||||
<a name='L176'></a><a href='#L176'>176</a>
|
|
||||||
<a name='L177'></a><a href='#L177'>177</a>
|
|
||||||
<a name='L178'></a><a href='#L178'>178</a>
|
|
||||||
<a name='L179'></a><a href='#L179'>179</a>
|
|
||||||
<a name='L180'></a><a href='#L180'>180</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-yes">15x</span>
|
|
||||||
<span class="cline-any cline-yes">15x</span>
|
|
||||||
<span class="cline-any cline-yes">13x</span>
|
|
||||||
<span class="cline-any cline-yes">13x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">9x</span>
|
|
||||||
<span class="cline-any cline-yes">9x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">15x</span>
|
|
||||||
<span class="cline-any cline-yes">15x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">15x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">15x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">11x</span>
|
|
||||||
<span class="cline-any cline-yes">11x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">20x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">9x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import type { UsageSummary } from "@paperclipai/adapter-utils";
|
|
||||||
import { asString, asNumber, parseObject, parseJson } from "@paperclipai/adapter-utils/server-utils";
|
|
||||||
|
|
||||||
const CLAUDE_AUTH_REQUIRED_RE = /(?:not\s+logged\s+in|please\s+log\s+in|please\s+run\s+`?claude\s+login`?|login\s+required|requires\s+login|unauthorized|authentication\s+required)/i;
|
|
||||||
const URL_RE = /(https?:\/\/[^\s'"`<>()[\]{};,!?]+[^\s'"`<>()[\]{};,!.?:]+)/gi;
|
|
||||||
|
|
||||||
export function parseClaudeStreamJson(stdout: string) {
|
|
||||||
let sessionId: string | null = null;
|
|
||||||
let model = "";
|
|
||||||
let finalResult: Record<string, unknown> | null = null;
|
|
||||||
const assistantTexts: string[] = [];
|
|
||||||
|
|
||||||
for (const rawLine of stdout.split(/\r?\n/)) {
|
|
||||||
const line = rawLine.trim();
|
|
||||||
if (!line) continue;
|
|
||||||
const event = parseJson(line);
|
|
||||||
if (!event) continue;
|
|
||||||
|
|
||||||
const type = asString(event.type, "");
|
|
||||||
if (type === "system" && asString(event.subtype, "") === "init") {
|
|
||||||
sessionId = asString(event.session_id, sessionId ?? "") || <span class="branch-1 cbranch-no" title="branch not covered" >sessionId;</span>
|
|
||||||
model = asString(event.model, model);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "assistant") {
|
|
||||||
sessionId = asString(event.session_id, sessionId ?? "") || sessionId;
|
|
||||||
const message = parseObject(event.message);
|
|
||||||
const content = Array.isArray(message.content) ? message.content : <span class="branch-1 cbranch-no" title="branch not covered" >[];</span>
|
|
||||||
for (const entry of content) {
|
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (typeof entry !== "object" || entry === null || Array.isArray(entry)) <span class="cstat-no" title="statement not covered" >continue;</span>
|
|
||||||
const block = entry as Record<string, unknown>;
|
|
||||||
if (asString(block.type, "") === "text") {
|
|
||||||
const text = asString(block.text, "");
|
|
||||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (text) assistantTexts.push(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (type === "result") {
|
|
||||||
finalResult = event;
|
|
||||||
sessionId = asString(event.session_id, sessionId ?? "") || sessionId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!finalResult) {
|
|
||||||
return {
|
|
||||||
sessionId,
|
|
||||||
model,
|
|
||||||
costUsd: null as number | null,
|
|
||||||
usage: null as UsageSummary | null,
|
|
||||||
summary: assistantTexts.join("\n\n").trim(),
|
|
||||||
resultJson: null as Record<string, unknown> | null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const usageObj = parseObject(finalResult.usage);
|
|
||||||
const usage: UsageSummary = {
|
|
||||||
inputTokens: asNumber(usageObj.input_tokens, 0),
|
|
||||||
cachedInputTokens: asNumber(usageObj.cache_read_input_tokens, 0),
|
|
||||||
outputTokens: asNumber(usageObj.output_tokens, 0),
|
|
||||||
};
|
|
||||||
const costRaw = finalResult.total_cost_usd;
|
|
||||||
const costUsd = typeof costRaw === "number" && Number.isFinite(costRaw) ? costRaw : null;
|
|
||||||
const summary = asString(finalResult.result, assistantTexts.join("\n\n")).trim();
|
|
||||||
|
|
||||||
return {
|
|
||||||
sessionId,
|
|
||||||
model,
|
|
||||||
costUsd,
|
|
||||||
usage,
|
|
||||||
summary,
|
|
||||||
resultJson: finalResult,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractClaudeErrorMessages(parsed: Record<string, unknown>): string[] {
|
|
||||||
const raw = Array.isArray(parsed.errors) ? parsed.errors : [];
|
|
||||||
const messages: string[] = [];
|
|
||||||
|
|
||||||
for (const entry of raw) {
|
|
||||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (typeof entry === "string") {
|
|
||||||
const msg = entry.trim();
|
|
||||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (msg) messages.push(msg);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > continue;</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = <span class="cstat-no" title="statement not covered" >entry as Record<string, unknown>;</span>
|
|
||||||
const <span class="cstat-no" title="statement not covered" >msg = asString(obj.message, "") || asString(obj.error, "") || asString(obj.code, "");</span>
|
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (msg) {
|
|
||||||
<span class="cstat-no" title="statement not covered" > messages.push(msg);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > continue;</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > messages.push(JSON.stringify(obj));</span>
|
|
||||||
} catch {
|
|
||||||
// skip non-serializable entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function extractClaudeLoginUrl(text: string): string | null {
|
|
||||||
const match = text.match(URL_RE);
|
|
||||||
if (!match || match.length === 0) return null;
|
|
||||||
for (const rawUrl of match) {
|
|
||||||
const cleaned = rawUrl.replace(/[\])}.!,?;:'\"]+$/g, "");
|
|
||||||
if (cleaned.includes("claude") || cleaned.includes("anthropic") || cleaned.includes("auth")) {
|
|
||||||
return cleaned;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return match[0]?.replace(/[\])}.!,?;:'\"]+$/g, "") ?? <span class="branch-1 cbranch-no" title="branch not covered" >null;</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function detectClaudeLoginRequired(input: {
|
|
||||||
parsed: Record<string, unknown> | null;
|
|
||||||
stdout: string;
|
|
||||||
stderr: string;
|
|
||||||
}): { requiresLogin: boolean; loginUrl: string | null } {
|
|
||||||
const resultText = asString(input.parsed?.result, "").trim();
|
|
||||||
const messages = [resultText, ...extractClaudeErrorMessages(input.parsed ?? <span class="branch-1 cbranch-no" title="branch not covered" >{})</span>, input.stdout, input.stderr]
|
|
||||||
.join("\n")
|
|
||||||
.split(/\r?\n/)
|
|
||||||
.map((line) => line.trim())
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
const requiresLogin = messages.some((line) => CLAUDE_AUTH_REQUIRED_RE.test(line));
|
|
||||||
return {
|
|
||||||
requiresLogin,
|
|
||||||
loginUrl: extractClaudeLoginUrl([input.stdout, input.stderr].join("\n")),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function describeClaudeFailure(parsed: Record<string, unknown>): string | null {
|
|
||||||
const subtype = asString(parsed.subtype, "");
|
|
||||||
const resultText = asString(parsed.result, "").trim();
|
|
||||||
const errors = extractClaudeErrorMessages(parsed);
|
|
||||||
|
|
||||||
let detail = resultText;
|
|
||||||
if (!detail && errors.length > 0) {
|
|
||||||
detail = errors[0] ?? <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts = ["Claude run failed"];
|
|
||||||
if (subtype) parts.push(`subtype=${subtype}`);
|
|
||||||
if (detail) parts.push(detail);
|
|
||||||
return parts.length > 1 ? parts.join(": ") : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isClaudeMaxTurnsResult(parsed: Record<string, unknown> | null | undefined): boolean {
|
|
||||||
if (!parsed) return false;
|
|
||||||
|
|
||||||
const subtype = asString(parsed.subtype, "").trim().toLowerCase();
|
|
||||||
if (subtype === "error_max_turns") return true;
|
|
||||||
|
|
||||||
const stopReason = asString(parsed.stop_reason, "").trim().toLowerCase();
|
|
||||||
if (stopReason === "max_turns") return true;
|
|
||||||
|
|
||||||
const resultText = asString(parsed.result, "").trim();
|
|
||||||
return /max(?:imum)?\s+turns?/i.test(resultText);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isClaudeUnknownSessionError(parsed: Record<string, unknown>): boolean {
|
|
||||||
const resultText = asString(parsed.result, "").trim();
|
|
||||||
const allMessages = [resultText, ...extractClaudeErrorMessages(parsed)]
|
|
||||||
.map((msg) => msg.trim())
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
return allMessages.some((msg) =>
|
|
||||||
/no conversation found with session id|unknown session|session .* not found/i.test(msg),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
</pre></td></tr></table></pre>
|
|
||||||
|
|
||||||
<div class='push'></div><!-- for sticky footer -->
|
|
||||||
</div><!-- /wrapper -->
|
|
||||||
<div class='footer quiet pad2 space-top1 center small'>
|
|
||||||
Code coverage generated by
|
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
||||||
at 2026-04-12T12:19:30.601Z
|
|
||||||
</div>
|
|
||||||
<script src="../../prettify.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
prettyPrint();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="../../sorter.js"></script>
|
|
||||||
<script src="../../block-navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Code coverage report for src/server/session.ts</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="../../prettify.css" />
|
|
||||||
<link rel="stylesheet" href="../../base.css" />
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style type='text/css'>
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
background-image: url(../../sort-arrow-sprite.png);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class='wrapper'>
|
|
||||||
<div class='pad1'>
|
|
||||||
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/server</a> session.ts</h1>
|
|
||||||
<div class='clearfix'>
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">100% </span>
|
|
||||||
<span class="quiet">Statements</span>
|
|
||||||
<span class='fraction'>26/26</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">100% </span>
|
|
||||||
<span class="quiet">Branches</span>
|
|
||||||
<span class='fraction'>57/57</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">100% </span>
|
|
||||||
<span class="quiet">Functions</span>
|
|
||||||
<span class='fraction'>4/4</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">100% </span>
|
|
||||||
<span class="quiet">Lines</span>
|
|
||||||
<span class='fraction'>21/21</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p class="quiet">
|
|
||||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
||||||
</p>
|
|
||||||
<template id="filterTemplate">
|
|
||||||
<div class="quiet">
|
|
||||||
Filter:
|
|
||||||
<input type="search" id="fileSearch">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class='status-line high'></div>
|
|
||||||
<pre><table class="coverage">
|
|
||||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
|
||||||
<a name='L2'></a><a href='#L2'>2</a>
|
|
||||||
<a name='L3'></a><a href='#L3'>3</a>
|
|
||||||
<a name='L4'></a><a href='#L4'>4</a>
|
|
||||||
<a name='L5'></a><a href='#L5'>5</a>
|
|
||||||
<a name='L6'></a><a href='#L6'>6</a>
|
|
||||||
<a name='L7'></a><a href='#L7'>7</a>
|
|
||||||
<a name='L8'></a><a href='#L8'>8</a>
|
|
||||||
<a name='L9'></a><a href='#L9'>9</a>
|
|
||||||
<a name='L10'></a><a href='#L10'>10</a>
|
|
||||||
<a name='L11'></a><a href='#L11'>11</a>
|
|
||||||
<a name='L12'></a><a href='#L12'>12</a>
|
|
||||||
<a name='L13'></a><a href='#L13'>13</a>
|
|
||||||
<a name='L14'></a><a href='#L14'>14</a>
|
|
||||||
<a name='L15'></a><a href='#L15'>15</a>
|
|
||||||
<a name='L16'></a><a href='#L16'>16</a>
|
|
||||||
<a name='L17'></a><a href='#L17'>17</a>
|
|
||||||
<a name='L18'></a><a href='#L18'>18</a>
|
|
||||||
<a name='L19'></a><a href='#L19'>19</a>
|
|
||||||
<a name='L20'></a><a href='#L20'>20</a>
|
|
||||||
<a name='L21'></a><a href='#L21'>21</a>
|
|
||||||
<a name='L22'></a><a href='#L22'>22</a>
|
|
||||||
<a name='L23'></a><a href='#L23'>23</a>
|
|
||||||
<a name='L24'></a><a href='#L24'>24</a>
|
|
||||||
<a name='L25'></a><a href='#L25'>25</a>
|
|
||||||
<a name='L26'></a><a href='#L26'>26</a>
|
|
||||||
<a name='L27'></a><a href='#L27'>27</a>
|
|
||||||
<a name='L28'></a><a href='#L28'>28</a>
|
|
||||||
<a name='L29'></a><a href='#L29'>29</a>
|
|
||||||
<a name='L30'></a><a href='#L30'>30</a>
|
|
||||||
<a name='L31'></a><a href='#L31'>31</a>
|
|
||||||
<a name='L32'></a><a href='#L32'>32</a>
|
|
||||||
<a name='L33'></a><a href='#L33'>33</a>
|
|
||||||
<a name='L34'></a><a href='#L34'>34</a>
|
|
||||||
<a name='L35'></a><a href='#L35'>35</a>
|
|
||||||
<a name='L36'></a><a href='#L36'>36</a>
|
|
||||||
<a name='L37'></a><a href='#L37'>37</a>
|
|
||||||
<a name='L38'></a><a href='#L38'>38</a>
|
|
||||||
<a name='L39'></a><a href='#L39'>39</a>
|
|
||||||
<a name='L40'></a><a href='#L40'>40</a>
|
|
||||||
<a name='L41'></a><a href='#L41'>41</a>
|
|
||||||
<a name='L42'></a><a href='#L42'>42</a>
|
|
||||||
<a name='L43'></a><a href='#L43'>43</a>
|
|
||||||
<a name='L44'></a><a href='#L44'>44</a>
|
|
||||||
<a name='L45'></a><a href='#L45'>45</a>
|
|
||||||
<a name='L46'></a><a href='#L46'>46</a>
|
|
||||||
<a name='L47'></a><a href='#L47'>47</a>
|
|
||||||
<a name='L48'></a><a href='#L48'>48</a>
|
|
||||||
<a name='L49'></a><a href='#L49'>49</a>
|
|
||||||
<a name='L50'></a><a href='#L50'>50</a>
|
|
||||||
<a name='L51'></a><a href='#L51'>51</a>
|
|
||||||
<a name='L52'></a><a href='#L52'>52</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">203x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">25x</span>
|
|
||||||
<span class="cline-any cline-yes">20x</span>
|
|
||||||
<span class="cline-any cline-yes">20x</span>
|
|
||||||
<span class="cline-any cline-yes">25x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">16x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">25x</span>
|
|
||||||
<span class="cline-any cline-yes">25x</span>
|
|
||||||
<span class="cline-any cline-yes">25x</span>
|
|
||||||
<span class="cline-any cline-yes">25x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import type { AdapterSessionCodec } from "@paperclipai/adapter-utils";
|
|
||||||
|
|
||||||
function readNonEmptyString(value: unknown): string | null {
|
|
||||||
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sessionCodec: AdapterSessionCodec = {
|
|
||||||
deserialize(raw: unknown) {
|
|
||||||
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
||||||
const record = raw as Record<string, unknown>;
|
|
||||||
const sessionId = readNonEmptyString(record.sessionId) ?? readNonEmptyString(record.session_id);
|
|
||||||
if (!sessionId) return null;
|
|
||||||
const cwd =
|
|
||||||
readNonEmptyString(record.cwd) ??
|
|
||||||
readNonEmptyString(record.workdir) ??
|
|
||||||
readNonEmptyString(record.folder);
|
|
||||||
const workspaceId = readNonEmptyString(record.workspaceId) ?? readNonEmptyString(record.workspace_id);
|
|
||||||
const repoUrl = readNonEmptyString(record.repoUrl) ?? readNonEmptyString(record.repo_url);
|
|
||||||
const repoRef = readNonEmptyString(record.repoRef) ?? readNonEmptyString(record.repo_ref);
|
|
||||||
return {
|
|
||||||
sessionId,
|
|
||||||
...(cwd ? { cwd } : {}),
|
|
||||||
...(workspaceId ? { workspaceId } : {}),
|
|
||||||
...(repoUrl ? { repoUrl } : {}),
|
|
||||||
...(repoRef ? { repoRef } : {}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
serialize(params: Record<string, unknown> | null) {
|
|
||||||
if (!params) return null;
|
|
||||||
const sessionId = readNonEmptyString(params.sessionId) ?? readNonEmptyString(params.session_id);
|
|
||||||
if (!sessionId) return null;
|
|
||||||
const cwd =
|
|
||||||
readNonEmptyString(params.cwd) ??
|
|
||||||
readNonEmptyString(params.workdir) ??
|
|
||||||
readNonEmptyString(params.folder);
|
|
||||||
const workspaceId = readNonEmptyString(params.workspaceId) ?? readNonEmptyString(params.workspace_id);
|
|
||||||
const repoUrl = readNonEmptyString(params.repoUrl) ?? readNonEmptyString(params.repo_url);
|
|
||||||
const repoRef = readNonEmptyString(params.repoRef) ?? readNonEmptyString(params.repo_ref);
|
|
||||||
return {
|
|
||||||
sessionId,
|
|
||||||
...(cwd ? { cwd } : {}),
|
|
||||||
...(workspaceId ? { workspaceId } : {}),
|
|
||||||
...(repoUrl ? { repoUrl } : {}),
|
|
||||||
...(repoRef ? { repoRef } : {}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getDisplayId(params: Record<string, unknown> | null) {
|
|
||||||
if (!params) return null;
|
|
||||||
return readNonEmptyString(params.sessionId) ?? readNonEmptyString(params.session_id);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</pre></td></tr></table></pre>
|
|
||||||
|
|
||||||
<div class='push'></div><!-- for sticky footer -->
|
|
||||||
</div><!-- /wrapper -->
|
|
||||||
<div class='footer quiet pad2 space-top1 center small'>
|
|
||||||
Code coverage generated by
|
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
||||||
at 2026-04-12T12:19:30.601Z
|
|
||||||
</div>
|
|
||||||
<script src="../../prettify.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
prettyPrint();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="../../sorter.js"></script>
|
|
||||||
<script src="../../block-navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,808 +0,0 @@
|
|||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Code coverage report for src/server/test.ts</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="../../prettify.css" />
|
|
||||||
<link rel="stylesheet" href="../../base.css" />
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style type='text/css'>
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
background-image: url(../../sort-arrow-sprite.png);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class='wrapper'>
|
|
||||||
<div class='pad1'>
|
|
||||||
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/server</a> test.ts</h1>
|
|
||||||
<div class='clearfix'>
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Statements</span>
|
|
||||||
<span class='fraction'>0/67</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Branches</span>
|
|
||||||
<span class='fraction'>0/30</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Functions</span>
|
|
||||||
<span class='fraction'>0/9</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">0% </span>
|
|
||||||
<span class="quiet">Lines</span>
|
|
||||||
<span class='fraction'>0/63</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p class="quiet">
|
|
||||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
||||||
</p>
|
|
||||||
<template id="filterTemplate">
|
|
||||||
<div class="quiet">
|
|
||||||
Filter:
|
|
||||||
<input type="search" id="fileSearch">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class='status-line low'></div>
|
|
||||||
<pre><table class="coverage">
|
|
||||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
|
||||||
<a name='L2'></a><a href='#L2'>2</a>
|
|
||||||
<a name='L3'></a><a href='#L3'>3</a>
|
|
||||||
<a name='L4'></a><a href='#L4'>4</a>
|
|
||||||
<a name='L5'></a><a href='#L5'>5</a>
|
|
||||||
<a name='L6'></a><a href='#L6'>6</a>
|
|
||||||
<a name='L7'></a><a href='#L7'>7</a>
|
|
||||||
<a name='L8'></a><a href='#L8'>8</a>
|
|
||||||
<a name='L9'></a><a href='#L9'>9</a>
|
|
||||||
<a name='L10'></a><a href='#L10'>10</a>
|
|
||||||
<a name='L11'></a><a href='#L11'>11</a>
|
|
||||||
<a name='L12'></a><a href='#L12'>12</a>
|
|
||||||
<a name='L13'></a><a href='#L13'>13</a>
|
|
||||||
<a name='L14'></a><a href='#L14'>14</a>
|
|
||||||
<a name='L15'></a><a href='#L15'>15</a>
|
|
||||||
<a name='L16'></a><a href='#L16'>16</a>
|
|
||||||
<a name='L17'></a><a href='#L17'>17</a>
|
|
||||||
<a name='L18'></a><a href='#L18'>18</a>
|
|
||||||
<a name='L19'></a><a href='#L19'>19</a>
|
|
||||||
<a name='L20'></a><a href='#L20'>20</a>
|
|
||||||
<a name='L21'></a><a href='#L21'>21</a>
|
|
||||||
<a name='L22'></a><a href='#L22'>22</a>
|
|
||||||
<a name='L23'></a><a href='#L23'>23</a>
|
|
||||||
<a name='L24'></a><a href='#L24'>24</a>
|
|
||||||
<a name='L25'></a><a href='#L25'>25</a>
|
|
||||||
<a name='L26'></a><a href='#L26'>26</a>
|
|
||||||
<a name='L27'></a><a href='#L27'>27</a>
|
|
||||||
<a name='L28'></a><a href='#L28'>28</a>
|
|
||||||
<a name='L29'></a><a href='#L29'>29</a>
|
|
||||||
<a name='L30'></a><a href='#L30'>30</a>
|
|
||||||
<a name='L31'></a><a href='#L31'>31</a>
|
|
||||||
<a name='L32'></a><a href='#L32'>32</a>
|
|
||||||
<a name='L33'></a><a href='#L33'>33</a>
|
|
||||||
<a name='L34'></a><a href='#L34'>34</a>
|
|
||||||
<a name='L35'></a><a href='#L35'>35</a>
|
|
||||||
<a name='L36'></a><a href='#L36'>36</a>
|
|
||||||
<a name='L37'></a><a href='#L37'>37</a>
|
|
||||||
<a name='L38'></a><a href='#L38'>38</a>
|
|
||||||
<a name='L39'></a><a href='#L39'>39</a>
|
|
||||||
<a name='L40'></a><a href='#L40'>40</a>
|
|
||||||
<a name='L41'></a><a href='#L41'>41</a>
|
|
||||||
<a name='L42'></a><a href='#L42'>42</a>
|
|
||||||
<a name='L43'></a><a href='#L43'>43</a>
|
|
||||||
<a name='L44'></a><a href='#L44'>44</a>
|
|
||||||
<a name='L45'></a><a href='#L45'>45</a>
|
|
||||||
<a name='L46'></a><a href='#L46'>46</a>
|
|
||||||
<a name='L47'></a><a href='#L47'>47</a>
|
|
||||||
<a name='L48'></a><a href='#L48'>48</a>
|
|
||||||
<a name='L49'></a><a href='#L49'>49</a>
|
|
||||||
<a name='L50'></a><a href='#L50'>50</a>
|
|
||||||
<a name='L51'></a><a href='#L51'>51</a>
|
|
||||||
<a name='L52'></a><a href='#L52'>52</a>
|
|
||||||
<a name='L53'></a><a href='#L53'>53</a>
|
|
||||||
<a name='L54'></a><a href='#L54'>54</a>
|
|
||||||
<a name='L55'></a><a href='#L55'>55</a>
|
|
||||||
<a name='L56'></a><a href='#L56'>56</a>
|
|
||||||
<a name='L57'></a><a href='#L57'>57</a>
|
|
||||||
<a name='L58'></a><a href='#L58'>58</a>
|
|
||||||
<a name='L59'></a><a href='#L59'>59</a>
|
|
||||||
<a name='L60'></a><a href='#L60'>60</a>
|
|
||||||
<a name='L61'></a><a href='#L61'>61</a>
|
|
||||||
<a name='L62'></a><a href='#L62'>62</a>
|
|
||||||
<a name='L63'></a><a href='#L63'>63</a>
|
|
||||||
<a name='L64'></a><a href='#L64'>64</a>
|
|
||||||
<a name='L65'></a><a href='#L65'>65</a>
|
|
||||||
<a name='L66'></a><a href='#L66'>66</a>
|
|
||||||
<a name='L67'></a><a href='#L67'>67</a>
|
|
||||||
<a name='L68'></a><a href='#L68'>68</a>
|
|
||||||
<a name='L69'></a><a href='#L69'>69</a>
|
|
||||||
<a name='L70'></a><a href='#L70'>70</a>
|
|
||||||
<a name='L71'></a><a href='#L71'>71</a>
|
|
||||||
<a name='L72'></a><a href='#L72'>72</a>
|
|
||||||
<a name='L73'></a><a href='#L73'>73</a>
|
|
||||||
<a name='L74'></a><a href='#L74'>74</a>
|
|
||||||
<a name='L75'></a><a href='#L75'>75</a>
|
|
||||||
<a name='L76'></a><a href='#L76'>76</a>
|
|
||||||
<a name='L77'></a><a href='#L77'>77</a>
|
|
||||||
<a name='L78'></a><a href='#L78'>78</a>
|
|
||||||
<a name='L79'></a><a href='#L79'>79</a>
|
|
||||||
<a name='L80'></a><a href='#L80'>80</a>
|
|
||||||
<a name='L81'></a><a href='#L81'>81</a>
|
|
||||||
<a name='L82'></a><a href='#L82'>82</a>
|
|
||||||
<a name='L83'></a><a href='#L83'>83</a>
|
|
||||||
<a name='L84'></a><a href='#L84'>84</a>
|
|
||||||
<a name='L85'></a><a href='#L85'>85</a>
|
|
||||||
<a name='L86'></a><a href='#L86'>86</a>
|
|
||||||
<a name='L87'></a><a href='#L87'>87</a>
|
|
||||||
<a name='L88'></a><a href='#L88'>88</a>
|
|
||||||
<a name='L89'></a><a href='#L89'>89</a>
|
|
||||||
<a name='L90'></a><a href='#L90'>90</a>
|
|
||||||
<a name='L91'></a><a href='#L91'>91</a>
|
|
||||||
<a name='L92'></a><a href='#L92'>92</a>
|
|
||||||
<a name='L93'></a><a href='#L93'>93</a>
|
|
||||||
<a name='L94'></a><a href='#L94'>94</a>
|
|
||||||
<a name='L95'></a><a href='#L95'>95</a>
|
|
||||||
<a name='L96'></a><a href='#L96'>96</a>
|
|
||||||
<a name='L97'></a><a href='#L97'>97</a>
|
|
||||||
<a name='L98'></a><a href='#L98'>98</a>
|
|
||||||
<a name='L99'></a><a href='#L99'>99</a>
|
|
||||||
<a name='L100'></a><a href='#L100'>100</a>
|
|
||||||
<a name='L101'></a><a href='#L101'>101</a>
|
|
||||||
<a name='L102'></a><a href='#L102'>102</a>
|
|
||||||
<a name='L103'></a><a href='#L103'>103</a>
|
|
||||||
<a name='L104'></a><a href='#L104'>104</a>
|
|
||||||
<a name='L105'></a><a href='#L105'>105</a>
|
|
||||||
<a name='L106'></a><a href='#L106'>106</a>
|
|
||||||
<a name='L107'></a><a href='#L107'>107</a>
|
|
||||||
<a name='L108'></a><a href='#L108'>108</a>
|
|
||||||
<a name='L109'></a><a href='#L109'>109</a>
|
|
||||||
<a name='L110'></a><a href='#L110'>110</a>
|
|
||||||
<a name='L111'></a><a href='#L111'>111</a>
|
|
||||||
<a name='L112'></a><a href='#L112'>112</a>
|
|
||||||
<a name='L113'></a><a href='#L113'>113</a>
|
|
||||||
<a name='L114'></a><a href='#L114'>114</a>
|
|
||||||
<a name='L115'></a><a href='#L115'>115</a>
|
|
||||||
<a name='L116'></a><a href='#L116'>116</a>
|
|
||||||
<a name='L117'></a><a href='#L117'>117</a>
|
|
||||||
<a name='L118'></a><a href='#L118'>118</a>
|
|
||||||
<a name='L119'></a><a href='#L119'>119</a>
|
|
||||||
<a name='L120'></a><a href='#L120'>120</a>
|
|
||||||
<a name='L121'></a><a href='#L121'>121</a>
|
|
||||||
<a name='L122'></a><a href='#L122'>122</a>
|
|
||||||
<a name='L123'></a><a href='#L123'>123</a>
|
|
||||||
<a name='L124'></a><a href='#L124'>124</a>
|
|
||||||
<a name='L125'></a><a href='#L125'>125</a>
|
|
||||||
<a name='L126'></a><a href='#L126'>126</a>
|
|
||||||
<a name='L127'></a><a href='#L127'>127</a>
|
|
||||||
<a name='L128'></a><a href='#L128'>128</a>
|
|
||||||
<a name='L129'></a><a href='#L129'>129</a>
|
|
||||||
<a name='L130'></a><a href='#L130'>130</a>
|
|
||||||
<a name='L131'></a><a href='#L131'>131</a>
|
|
||||||
<a name='L132'></a><a href='#L132'>132</a>
|
|
||||||
<a name='L133'></a><a href='#L133'>133</a>
|
|
||||||
<a name='L134'></a><a href='#L134'>134</a>
|
|
||||||
<a name='L135'></a><a href='#L135'>135</a>
|
|
||||||
<a name='L136'></a><a href='#L136'>136</a>
|
|
||||||
<a name='L137'></a><a href='#L137'>137</a>
|
|
||||||
<a name='L138'></a><a href='#L138'>138</a>
|
|
||||||
<a name='L139'></a><a href='#L139'>139</a>
|
|
||||||
<a name='L140'></a><a href='#L140'>140</a>
|
|
||||||
<a name='L141'></a><a href='#L141'>141</a>
|
|
||||||
<a name='L142'></a><a href='#L142'>142</a>
|
|
||||||
<a name='L143'></a><a href='#L143'>143</a>
|
|
||||||
<a name='L144'></a><a href='#L144'>144</a>
|
|
||||||
<a name='L145'></a><a href='#L145'>145</a>
|
|
||||||
<a name='L146'></a><a href='#L146'>146</a>
|
|
||||||
<a name='L147'></a><a href='#L147'>147</a>
|
|
||||||
<a name='L148'></a><a href='#L148'>148</a>
|
|
||||||
<a name='L149'></a><a href='#L149'>149</a>
|
|
||||||
<a name='L150'></a><a href='#L150'>150</a>
|
|
||||||
<a name='L151'></a><a href='#L151'>151</a>
|
|
||||||
<a name='L152'></a><a href='#L152'>152</a>
|
|
||||||
<a name='L153'></a><a href='#L153'>153</a>
|
|
||||||
<a name='L154'></a><a href='#L154'>154</a>
|
|
||||||
<a name='L155'></a><a href='#L155'>155</a>
|
|
||||||
<a name='L156'></a><a href='#L156'>156</a>
|
|
||||||
<a name='L157'></a><a href='#L157'>157</a>
|
|
||||||
<a name='L158'></a><a href='#L158'>158</a>
|
|
||||||
<a name='L159'></a><a href='#L159'>159</a>
|
|
||||||
<a name='L160'></a><a href='#L160'>160</a>
|
|
||||||
<a name='L161'></a><a href='#L161'>161</a>
|
|
||||||
<a name='L162'></a><a href='#L162'>162</a>
|
|
||||||
<a name='L163'></a><a href='#L163'>163</a>
|
|
||||||
<a name='L164'></a><a href='#L164'>164</a>
|
|
||||||
<a name='L165'></a><a href='#L165'>165</a>
|
|
||||||
<a name='L166'></a><a href='#L166'>166</a>
|
|
||||||
<a name='L167'></a><a href='#L167'>167</a>
|
|
||||||
<a name='L168'></a><a href='#L168'>168</a>
|
|
||||||
<a name='L169'></a><a href='#L169'>169</a>
|
|
||||||
<a name='L170'></a><a href='#L170'>170</a>
|
|
||||||
<a name='L171'></a><a href='#L171'>171</a>
|
|
||||||
<a name='L172'></a><a href='#L172'>172</a>
|
|
||||||
<a name='L173'></a><a href='#L173'>173</a>
|
|
||||||
<a name='L174'></a><a href='#L174'>174</a>
|
|
||||||
<a name='L175'></a><a href='#L175'>175</a>
|
|
||||||
<a name='L176'></a><a href='#L176'>176</a>
|
|
||||||
<a name='L177'></a><a href='#L177'>177</a>
|
|
||||||
<a name='L178'></a><a href='#L178'>178</a>
|
|
||||||
<a name='L179'></a><a href='#L179'>179</a>
|
|
||||||
<a name='L180'></a><a href='#L180'>180</a>
|
|
||||||
<a name='L181'></a><a href='#L181'>181</a>
|
|
||||||
<a name='L182'></a><a href='#L182'>182</a>
|
|
||||||
<a name='L183'></a><a href='#L183'>183</a>
|
|
||||||
<a name='L184'></a><a href='#L184'>184</a>
|
|
||||||
<a name='L185'></a><a href='#L185'>185</a>
|
|
||||||
<a name='L186'></a><a href='#L186'>186</a>
|
|
||||||
<a name='L187'></a><a href='#L187'>187</a>
|
|
||||||
<a name='L188'></a><a href='#L188'>188</a>
|
|
||||||
<a name='L189'></a><a href='#L189'>189</a>
|
|
||||||
<a name='L190'></a><a href='#L190'>190</a>
|
|
||||||
<a name='L191'></a><a href='#L191'>191</a>
|
|
||||||
<a name='L192'></a><a href='#L192'>192</a>
|
|
||||||
<a name='L193'></a><a href='#L193'>193</a>
|
|
||||||
<a name='L194'></a><a href='#L194'>194</a>
|
|
||||||
<a name='L195'></a><a href='#L195'>195</a>
|
|
||||||
<a name='L196'></a><a href='#L196'>196</a>
|
|
||||||
<a name='L197'></a><a href='#L197'>197</a>
|
|
||||||
<a name='L198'></a><a href='#L198'>198</a>
|
|
||||||
<a name='L199'></a><a href='#L199'>199</a>
|
|
||||||
<a name='L200'></a><a href='#L200'>200</a>
|
|
||||||
<a name='L201'></a><a href='#L201'>201</a>
|
|
||||||
<a name='L202'></a><a href='#L202'>202</a>
|
|
||||||
<a name='L203'></a><a href='#L203'>203</a>
|
|
||||||
<a name='L204'></a><a href='#L204'>204</a>
|
|
||||||
<a name='L205'></a><a href='#L205'>205</a>
|
|
||||||
<a name='L206'></a><a href='#L206'>206</a>
|
|
||||||
<a name='L207'></a><a href='#L207'>207</a>
|
|
||||||
<a name='L208'></a><a href='#L208'>208</a>
|
|
||||||
<a name='L209'></a><a href='#L209'>209</a>
|
|
||||||
<a name='L210'></a><a href='#L210'>210</a>
|
|
||||||
<a name='L211'></a><a href='#L211'>211</a>
|
|
||||||
<a name='L212'></a><a href='#L212'>212</a>
|
|
||||||
<a name='L213'></a><a href='#L213'>213</a>
|
|
||||||
<a name='L214'></a><a href='#L214'>214</a>
|
|
||||||
<a name='L215'></a><a href='#L215'>215</a>
|
|
||||||
<a name='L216'></a><a href='#L216'>216</a>
|
|
||||||
<a name='L217'></a><a href='#L217'>217</a>
|
|
||||||
<a name='L218'></a><a href='#L218'>218</a>
|
|
||||||
<a name='L219'></a><a href='#L219'>219</a>
|
|
||||||
<a name='L220'></a><a href='#L220'>220</a>
|
|
||||||
<a name='L221'></a><a href='#L221'>221</a>
|
|
||||||
<a name='L222'></a><a href='#L222'>222</a>
|
|
||||||
<a name='L223'></a><a href='#L223'>223</a>
|
|
||||||
<a name='L224'></a><a href='#L224'>224</a>
|
|
||||||
<a name='L225'></a><a href='#L225'>225</a>
|
|
||||||
<a name='L226'></a><a href='#L226'>226</a>
|
|
||||||
<a name='L227'></a><a href='#L227'>227</a>
|
|
||||||
<a name='L228'></a><a href='#L228'>228</a>
|
|
||||||
<a name='L229'></a><a href='#L229'>229</a>
|
|
||||||
<a name='L230'></a><a href='#L230'>230</a>
|
|
||||||
<a name='L231'></a><a href='#L231'>231</a>
|
|
||||||
<a name='L232'></a><a href='#L232'>232</a>
|
|
||||||
<a name='L233'></a><a href='#L233'>233</a>
|
|
||||||
<a name='L234'></a><a href='#L234'>234</a>
|
|
||||||
<a name='L235'></a><a href='#L235'>235</a>
|
|
||||||
<a name='L236'></a><a href='#L236'>236</a>
|
|
||||||
<a name='L237'></a><a href='#L237'>237</a>
|
|
||||||
<a name='L238'></a><a href='#L238'>238</a>
|
|
||||||
<a name='L239'></a><a href='#L239'>239</a>
|
|
||||||
<a name='L240'></a><a href='#L240'>240</a>
|
|
||||||
<a name='L241'></a><a href='#L241'>241</a>
|
|
||||||
<a name='L242'></a><a href='#L242'>242</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import type {
|
|
||||||
AdapterEnvironmentCheck,
|
|
||||||
AdapterEnvironmentTestContext,
|
|
||||||
AdapterEnvironmentTestResult,
|
|
||||||
} from "@paperclipai/adapter-utils";
|
|
||||||
import { asString, parseObject } from "@paperclipai/adapter-utils/server-utils";
|
|
||||||
import { getSelfPodInfo, getCoreApi, getAuthzApi } from "./k8s-client.js";
|
|
||||||
|
|
||||||
function <span class="fstat-no" title="function not covered" >summarizeStatus(c</span>hecks: AdapterEnvironmentCheck[]): AdapterEnvironmentTestResult["status"] {
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (checks.<span class="fstat-no" title="function not covered" >some((c</span>) => <span class="cstat-no" title="statement not covered" >c.level === "error"))</span> <span class="cstat-no" title="statement not covered" >return "fail";</span></span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (checks.<span class="fstat-no" title="function not covered" >some((c</span>) => <span class="cstat-no" title="statement not covered" >c.level === "warn"))</span> <span class="cstat-no" title="statement not covered" >return "warn";</span></span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > return "pass";</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
async function <span class="fstat-no" title="function not covered" >checkApiReachable(c</span>hecks: AdapterEnvironmentCheck[], kubeconfigPath?: string): Promise<boolean> {
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
||||||
const selfPod = <span class="cstat-no" title="statement not covered" >await getSelfPodInfo(kubeconfigPath);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_api_reachable",
|
|
||||||
level: "info",
|
|
||||||
message: `Kubernetes API reachable; running in namespace ${selfPod.namespace}`,
|
|
||||||
detail: `Image: ${selfPod.image}`,
|
|
||||||
});
|
|
||||||
<span class="cstat-no" title="statement not covered" > return true;</span>
|
|
||||||
} catch (err) {
|
|
||||||
const msg = <span class="cstat-no" title="statement not covered" >err instanceof Error ? err.message : String(err);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_api_unreachable",
|
|
||||||
level: "error",
|
|
||||||
message: `Cannot reach Kubernetes API: ${msg}`,
|
|
||||||
hint: "Ensure the pod has a valid service account token mounted and the API server is accessible.",
|
|
||||||
});
|
|
||||||
<span class="cstat-no" title="statement not covered" > return false;</span>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function <span class="fstat-no" title="function not covered" >checkNamespace(</span>
|
|
||||||
namespace: string,
|
|
||||||
selfPodNamespace: string,
|
|
||||||
checks: AdapterEnvironmentCheck[],
|
|
||||||
kubeconfigPath?: string,
|
|
||||||
): Promise<boolean> {
|
|
||||||
// If targeting the same namespace we're running in, skip the cluster-scoped
|
|
||||||
// readNamespace call — we know it exists, and the SA may lack cluster-level
|
|
||||||
// namespace get permissions.
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (namespace === selfPodNamespace) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_namespace_exists",
|
|
||||||
level: "info",
|
|
||||||
message: `Target namespace is the pod namespace: ${namespace}`,
|
|
||||||
});
|
|
||||||
<span class="cstat-no" title="statement not covered" > return true;</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
||||||
const <span class="cstat-no" title="statement not covered" >coreApi = getCoreApi(kubeconfigPath);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > await coreApi.readNamespace({ name: namespace });</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_namespace_exists",
|
|
||||||
level: "info",
|
|
||||||
message: `Target namespace exists: ${namespace}`,
|
|
||||||
});
|
|
||||||
<span class="cstat-no" title="statement not covered" > return true;</span>
|
|
||||||
} catch (err) {
|
|
||||||
const msg = <span class="cstat-no" title="statement not covered" >err instanceof Error ? err.message : String(err);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_namespace_check_failed",
|
|
||||||
level: "warn",
|
|
||||||
message: `Cannot verify namespace "${namespace}": ${msg}`,
|
|
||||||
hint: "The service account may lack cluster-level namespace read permissions. The namespace may still be usable — verify RBAC checks below.",
|
|
||||||
});
|
|
||||||
// Don't block on this — RBAC checks below will catch actual permission issues
|
|
||||||
<span class="cstat-no" title="statement not covered" > return true;</span>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function <span class="fstat-no" title="function not covered" >checkRbac(</span>
|
|
||||||
namespace: string,
|
|
||||||
checks: AdapterEnvironmentCheck[],
|
|
||||||
kubeconfigPath?: string,
|
|
||||||
): Promise<void> {
|
|
||||||
const <span class="cstat-no" title="statement not covered" >authzApi = getAuthzApi(kubeconfigPath);</span>
|
|
||||||
|
|
||||||
const rbacChecks = <span class="cstat-no" title="statement not covered" >[</span>
|
|
||||||
{ resource: "jobs", group: "batch", verb: "create", code: "k8s_rbac_job_create", label: "create Jobs" },
|
|
||||||
{ resource: "jobs", group: "batch", verb: "delete", code: "k8s_rbac_job_delete", label: "delete Jobs" },
|
|
||||||
{ resource: "jobs", group: "batch", verb: "get", code: "k8s_rbac_job_get", label: "get Jobs" },
|
|
||||||
{ resource: "pods", group: "", verb: "list", code: "k8s_rbac_pod_list", label: "list Pods" },
|
|
||||||
{ resource: "pods/log", group: "", verb: "get", code: "k8s_rbac_pod_log", label: "get Pod logs" },
|
|
||||||
];
|
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > for (const check of rbacChecks) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
||||||
const review = <span class="cstat-no" title="statement not covered" >await authzApi.createSelfSubjectAccessReview({</span>
|
|
||||||
body: {
|
|
||||||
apiVersion: "authorization.k8s.io/v1",
|
|
||||||
kind: "SelfSubjectAccessReview",
|
|
||||||
spec: {
|
|
||||||
resourceAttributes: {
|
|
||||||
namespace,
|
|
||||||
verb: check.verb,
|
|
||||||
resource: check.resource,
|
|
||||||
group: check.group,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (review.status?.allowed) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: check.code,
|
|
||||||
level: "info",
|
|
||||||
message: `RBAC: allowed to ${check.label} in ${namespace}`,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: check.code,
|
|
||||||
level: "error",
|
|
||||||
message: `RBAC: not allowed to ${check.label} in ${namespace}`,
|
|
||||||
hint: `Grant the service account permission to ${check.verb} ${check.resource} in namespace ${namespace}.`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const msg = <span class="cstat-no" title="statement not covered" >err instanceof Error ? err.message : String(err);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: check.code,
|
|
||||||
level: "warn",
|
|
||||||
message: `RBAC check failed for ${check.label}: ${msg}`,
|
|
||||||
hint: "SelfSubjectAccessReview may not be available; verify permissions manually.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function <span class="fstat-no" title="function not covered" >checkSecret(</span>
|
|
||||||
namespace: string,
|
|
||||||
secretName: string,
|
|
||||||
checks: AdapterEnvironmentCheck[],
|
|
||||||
kubeconfigPath?: string,
|
|
||||||
): Promise<void> {
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
||||||
const <span class="cstat-no" title="statement not covered" >coreApi = getCoreApi(kubeconfigPath);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > await coreApi.readNamespacedSecret({ name: secretName, namespace });</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_secret_exists",
|
|
||||||
level: "info",
|
|
||||||
message: `Secret "${secretName}" exists in namespace ${namespace}`,
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_secret_missing",
|
|
||||||
level: "warn",
|
|
||||||
message: `Secret "${secretName}" not found in namespace ${namespace}`,
|
|
||||||
hint: `Ensure the paperclip-secrets Secret exists with keys for ANTHROPIC_API_KEY and/or AWS_BEARER_TOKEN_BEDROCK.`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function <span class="fstat-no" title="function not covered" >checkPvc(</span>
|
|
||||||
selfPod: { pvcClaimName: string | null; namespace: string },
|
|
||||||
checks: AdapterEnvironmentCheck[],
|
|
||||||
kubeconfigPath?: string,
|
|
||||||
): Promise<void> {
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (!selfPod.pvcClaimName) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_pvc_not_detected",
|
|
||||||
level: "warn",
|
|
||||||
message: "No PVC detected on /paperclip mount — session resume and workspace sharing will not work.",
|
|
||||||
hint: "Ensure the Paperclip Deployment has a PVC mounted at /paperclip with ReadWriteMany access mode.",
|
|
||||||
});
|
|
||||||
<span class="cstat-no" title="statement not covered" > return;</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
||||||
const <span class="cstat-no" title="statement not covered" >coreApi = getCoreApi(kubeconfigPath);</span>
|
|
||||||
const pvc = <span class="cstat-no" title="statement not covered" >await coreApi.readNamespacedPersistentVolumeClaim({</span>
|
|
||||||
name: selfPod.pvcClaimName,
|
|
||||||
namespace: selfPod.namespace,
|
|
||||||
});
|
|
||||||
const accessModes = <span class="cstat-no" title="statement not covered" >pvc.spec?.accessModes ?? [];</span>
|
|
||||||
const isRwx = <span class="cstat-no" title="statement not covered" >accessModes.includes("ReadWriteMany");</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (isRwx) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_pvc_rwx",
|
|
||||||
level: "info",
|
|
||||||
message: `PVC "${selfPod.pvcClaimName}" has ReadWriteMany access — Job pods can mount it.`,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_pvc_not_rwx",
|
|
||||||
level: "warn",
|
|
||||||
message: `PVC "${selfPod.pvcClaimName}" access modes: ${accessModes.join(", ")}. ReadWriteMany is required for Job pods to share the volume.`,
|
|
||||||
hint: "Change the PVC accessMode to ReadWriteMany in Helm values.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const msg = <span class="cstat-no" title="statement not covered" >err instanceof Error ? err.message : String(err);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > checks.push({</span>
|
|
||||||
code: "k8s_pvc_check_failed",
|
|
||||||
level: "warn",
|
|
||||||
message: `Could not read PVC "${selfPod.pvcClaimName}": ${msg}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function <span class="fstat-no" title="function not covered" >testEnvironment(</span>
|
|
||||||
ctx: AdapterEnvironmentTestContext,
|
|
||||||
): Promise<AdapterEnvironmentTestResult> {
|
|
||||||
const checks: AdapterEnvironmentCheck[] = <span class="cstat-no" title="statement not covered" >[];</span>
|
|
||||||
const <span class="cstat-no" title="statement not covered" >config = parseObject(ctx.config);</span>
|
|
||||||
const <span class="cstat-no" title="statement not covered" >secretRef = asString(config.secretRef, "paperclip-secrets");</span>
|
|
||||||
const <span class="cstat-no" title="statement not covered" >kubeconfigPath = asString(config.kubeconfig, "") || undefined;</span>
|
|
||||||
|
|
||||||
// 1. K8s API reachable + self-pod introspection
|
|
||||||
const apiOk = <span class="cstat-no" title="statement not covered" >await checkApiReachable(checks, kubeconfigPath);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (!apiOk) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > return { adapterType: ctx.adapterType, status: summarizeStatus(checks), checks, testedAt: new Date().toISOString() };</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
const selfPod = <span class="cstat-no" title="statement not covered" >await getSelfPodInfo(kubeconfigPath);</span>
|
|
||||||
const <span class="cstat-no" title="statement not covered" >namespace = asString(config.namespace, "") || selfPod.namespace;</span>
|
|
||||||
|
|
||||||
// 2. Target namespace exists
|
|
||||||
const nsOk = <span class="cstat-no" title="statement not covered" >await checkNamespace(namespace, selfPod.namespace, checks, kubeconfigPath);</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > if (!nsOk) {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > return { adapterType: ctx.adapterType, status: summarizeStatus(checks), checks, testedAt: new Date().toISOString() };</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3-5. Run remaining checks in parallel
|
|
||||||
<span class="cstat-no" title="statement not covered" > await Promise.all([</span>
|
|
||||||
checkRbac(namespace, checks, kubeconfigPath),
|
|
||||||
checkSecret(namespace, secretRef, checks, kubeconfigPath),
|
|
||||||
checkPvc(selfPod, checks, kubeconfigPath),
|
|
||||||
]);
|
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > return {</span>
|
|
||||||
adapterType: ctx.adapterType,
|
|
||||||
status: summarizeStatus(checks),
|
|
||||||
checks,
|
|
||||||
testedAt: new Date().toISOString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
</pre></td></tr></table></pre>
|
|
||||||
|
|
||||||
<div class='push'></div><!-- for sticky footer -->
|
|
||||||
</div><!-- /wrapper -->
|
|
||||||
<div class='footer quiet pad2 space-top1 center small'>
|
|
||||||
Code coverage generated by
|
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
||||||
at 2026-04-12T12:19:30.601Z
|
|
||||||
</div>
|
|
||||||
<script src="../../prettify.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
prettyPrint();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="../../sorter.js"></script>
|
|
||||||
<script src="../../block-navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,559 +0,0 @@
|
|||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Code coverage report for src/ui-parser.ts</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="../prettify.css" />
|
|
||||||
<link rel="stylesheet" href="../base.css" />
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style type='text/css'>
|
|
||||||
.coverage-summary .sorter {
|
|
||||||
background-image: url(../sort-arrow-sprite.png);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class='wrapper'>
|
|
||||||
<div class='pad1'>
|
|
||||||
<h1><a href="../index.html">All files</a> / <a href="index.html">src</a> ui-parser.ts</h1>
|
|
||||||
<div class='clearfix'>
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">92.94% </span>
|
|
||||||
<span class="quiet">Statements</span>
|
|
||||||
<span class='fraction'>79/85</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">74.07% </span>
|
|
||||||
<span class="quiet">Branches</span>
|
|
||||||
<span class='fraction'>80/108</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">100% </span>
|
|
||||||
<span class="quiet">Functions</span>
|
|
||||||
<span class='fraction'>5/5</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
|
||||||
<span class="strong">95.94% </span>
|
|
||||||
<span class="quiet">Lines</span>
|
|
||||||
<span class='fraction'>71/74</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p class="quiet">
|
|
||||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
||||||
</p>
|
|
||||||
<template id="filterTemplate">
|
|
||||||
<div class="quiet">
|
|
||||||
Filter:
|
|
||||||
<input type="search" id="fileSearch">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class='status-line high'></div>
|
|
||||||
<pre><table class="coverage">
|
|
||||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
|
||||||
<a name='L2'></a><a href='#L2'>2</a>
|
|
||||||
<a name='L3'></a><a href='#L3'>3</a>
|
|
||||||
<a name='L4'></a><a href='#L4'>4</a>
|
|
||||||
<a name='L5'></a><a href='#L5'>5</a>
|
|
||||||
<a name='L6'></a><a href='#L6'>6</a>
|
|
||||||
<a name='L7'></a><a href='#L7'>7</a>
|
|
||||||
<a name='L8'></a><a href='#L8'>8</a>
|
|
||||||
<a name='L9'></a><a href='#L9'>9</a>
|
|
||||||
<a name='L10'></a><a href='#L10'>10</a>
|
|
||||||
<a name='L11'></a><a href='#L11'>11</a>
|
|
||||||
<a name='L12'></a><a href='#L12'>12</a>
|
|
||||||
<a name='L13'></a><a href='#L13'>13</a>
|
|
||||||
<a name='L14'></a><a href='#L14'>14</a>
|
|
||||||
<a name='L15'></a><a href='#L15'>15</a>
|
|
||||||
<a name='L16'></a><a href='#L16'>16</a>
|
|
||||||
<a name='L17'></a><a href='#L17'>17</a>
|
|
||||||
<a name='L18'></a><a href='#L18'>18</a>
|
|
||||||
<a name='L19'></a><a href='#L19'>19</a>
|
|
||||||
<a name='L20'></a><a href='#L20'>20</a>
|
|
||||||
<a name='L21'></a><a href='#L21'>21</a>
|
|
||||||
<a name='L22'></a><a href='#L22'>22</a>
|
|
||||||
<a name='L23'></a><a href='#L23'>23</a>
|
|
||||||
<a name='L24'></a><a href='#L24'>24</a>
|
|
||||||
<a name='L25'></a><a href='#L25'>25</a>
|
|
||||||
<a name='L26'></a><a href='#L26'>26</a>
|
|
||||||
<a name='L27'></a><a href='#L27'>27</a>
|
|
||||||
<a name='L28'></a><a href='#L28'>28</a>
|
|
||||||
<a name='L29'></a><a href='#L29'>29</a>
|
|
||||||
<a name='L30'></a><a href='#L30'>30</a>
|
|
||||||
<a name='L31'></a><a href='#L31'>31</a>
|
|
||||||
<a name='L32'></a><a href='#L32'>32</a>
|
|
||||||
<a name='L33'></a><a href='#L33'>33</a>
|
|
||||||
<a name='L34'></a><a href='#L34'>34</a>
|
|
||||||
<a name='L35'></a><a href='#L35'>35</a>
|
|
||||||
<a name='L36'></a><a href='#L36'>36</a>
|
|
||||||
<a name='L37'></a><a href='#L37'>37</a>
|
|
||||||
<a name='L38'></a><a href='#L38'>38</a>
|
|
||||||
<a name='L39'></a><a href='#L39'>39</a>
|
|
||||||
<a name='L40'></a><a href='#L40'>40</a>
|
|
||||||
<a name='L41'></a><a href='#L41'>41</a>
|
|
||||||
<a name='L42'></a><a href='#L42'>42</a>
|
|
||||||
<a name='L43'></a><a href='#L43'>43</a>
|
|
||||||
<a name='L44'></a><a href='#L44'>44</a>
|
|
||||||
<a name='L45'></a><a href='#L45'>45</a>
|
|
||||||
<a name='L46'></a><a href='#L46'>46</a>
|
|
||||||
<a name='L47'></a><a href='#L47'>47</a>
|
|
||||||
<a name='L48'></a><a href='#L48'>48</a>
|
|
||||||
<a name='L49'></a><a href='#L49'>49</a>
|
|
||||||
<a name='L50'></a><a href='#L50'>50</a>
|
|
||||||
<a name='L51'></a><a href='#L51'>51</a>
|
|
||||||
<a name='L52'></a><a href='#L52'>52</a>
|
|
||||||
<a name='L53'></a><a href='#L53'>53</a>
|
|
||||||
<a name='L54'></a><a href='#L54'>54</a>
|
|
||||||
<a name='L55'></a><a href='#L55'>55</a>
|
|
||||||
<a name='L56'></a><a href='#L56'>56</a>
|
|
||||||
<a name='L57'></a><a href='#L57'>57</a>
|
|
||||||
<a name='L58'></a><a href='#L58'>58</a>
|
|
||||||
<a name='L59'></a><a href='#L59'>59</a>
|
|
||||||
<a name='L60'></a><a href='#L60'>60</a>
|
|
||||||
<a name='L61'></a><a href='#L61'>61</a>
|
|
||||||
<a name='L62'></a><a href='#L62'>62</a>
|
|
||||||
<a name='L63'></a><a href='#L63'>63</a>
|
|
||||||
<a name='L64'></a><a href='#L64'>64</a>
|
|
||||||
<a name='L65'></a><a href='#L65'>65</a>
|
|
||||||
<a name='L66'></a><a href='#L66'>66</a>
|
|
||||||
<a name='L67'></a><a href='#L67'>67</a>
|
|
||||||
<a name='L68'></a><a href='#L68'>68</a>
|
|
||||||
<a name='L69'></a><a href='#L69'>69</a>
|
|
||||||
<a name='L70'></a><a href='#L70'>70</a>
|
|
||||||
<a name='L71'></a><a href='#L71'>71</a>
|
|
||||||
<a name='L72'></a><a href='#L72'>72</a>
|
|
||||||
<a name='L73'></a><a href='#L73'>73</a>
|
|
||||||
<a name='L74'></a><a href='#L74'>74</a>
|
|
||||||
<a name='L75'></a><a href='#L75'>75</a>
|
|
||||||
<a name='L76'></a><a href='#L76'>76</a>
|
|
||||||
<a name='L77'></a><a href='#L77'>77</a>
|
|
||||||
<a name='L78'></a><a href='#L78'>78</a>
|
|
||||||
<a name='L79'></a><a href='#L79'>79</a>
|
|
||||||
<a name='L80'></a><a href='#L80'>80</a>
|
|
||||||
<a name='L81'></a><a href='#L81'>81</a>
|
|
||||||
<a name='L82'></a><a href='#L82'>82</a>
|
|
||||||
<a name='L83'></a><a href='#L83'>83</a>
|
|
||||||
<a name='L84'></a><a href='#L84'>84</a>
|
|
||||||
<a name='L85'></a><a href='#L85'>85</a>
|
|
||||||
<a name='L86'></a><a href='#L86'>86</a>
|
|
||||||
<a name='L87'></a><a href='#L87'>87</a>
|
|
||||||
<a name='L88'></a><a href='#L88'>88</a>
|
|
||||||
<a name='L89'></a><a href='#L89'>89</a>
|
|
||||||
<a name='L90'></a><a href='#L90'>90</a>
|
|
||||||
<a name='L91'></a><a href='#L91'>91</a>
|
|
||||||
<a name='L92'></a><a href='#L92'>92</a>
|
|
||||||
<a name='L93'></a><a href='#L93'>93</a>
|
|
||||||
<a name='L94'></a><a href='#L94'>94</a>
|
|
||||||
<a name='L95'></a><a href='#L95'>95</a>
|
|
||||||
<a name='L96'></a><a href='#L96'>96</a>
|
|
||||||
<a name='L97'></a><a href='#L97'>97</a>
|
|
||||||
<a name='L98'></a><a href='#L98'>98</a>
|
|
||||||
<a name='L99'></a><a href='#L99'>99</a>
|
|
||||||
<a name='L100'></a><a href='#L100'>100</a>
|
|
||||||
<a name='L101'></a><a href='#L101'>101</a>
|
|
||||||
<a name='L102'></a><a href='#L102'>102</a>
|
|
||||||
<a name='L103'></a><a href='#L103'>103</a>
|
|
||||||
<a name='L104'></a><a href='#L104'>104</a>
|
|
||||||
<a name='L105'></a><a href='#L105'>105</a>
|
|
||||||
<a name='L106'></a><a href='#L106'>106</a>
|
|
||||||
<a name='L107'></a><a href='#L107'>107</a>
|
|
||||||
<a name='L108'></a><a href='#L108'>108</a>
|
|
||||||
<a name='L109'></a><a href='#L109'>109</a>
|
|
||||||
<a name='L110'></a><a href='#L110'>110</a>
|
|
||||||
<a name='L111'></a><a href='#L111'>111</a>
|
|
||||||
<a name='L112'></a><a href='#L112'>112</a>
|
|
||||||
<a name='L113'></a><a href='#L113'>113</a>
|
|
||||||
<a name='L114'></a><a href='#L114'>114</a>
|
|
||||||
<a name='L115'></a><a href='#L115'>115</a>
|
|
||||||
<a name='L116'></a><a href='#L116'>116</a>
|
|
||||||
<a name='L117'></a><a href='#L117'>117</a>
|
|
||||||
<a name='L118'></a><a href='#L118'>118</a>
|
|
||||||
<a name='L119'></a><a href='#L119'>119</a>
|
|
||||||
<a name='L120'></a><a href='#L120'>120</a>
|
|
||||||
<a name='L121'></a><a href='#L121'>121</a>
|
|
||||||
<a name='L122'></a><a href='#L122'>122</a>
|
|
||||||
<a name='L123'></a><a href='#L123'>123</a>
|
|
||||||
<a name='L124'></a><a href='#L124'>124</a>
|
|
||||||
<a name='L125'></a><a href='#L125'>125</a>
|
|
||||||
<a name='L126'></a><a href='#L126'>126</a>
|
|
||||||
<a name='L127'></a><a href='#L127'>127</a>
|
|
||||||
<a name='L128'></a><a href='#L128'>128</a>
|
|
||||||
<a name='L129'></a><a href='#L129'>129</a>
|
|
||||||
<a name='L130'></a><a href='#L130'>130</a>
|
|
||||||
<a name='L131'></a><a href='#L131'>131</a>
|
|
||||||
<a name='L132'></a><a href='#L132'>132</a>
|
|
||||||
<a name='L133'></a><a href='#L133'>133</a>
|
|
||||||
<a name='L134'></a><a href='#L134'>134</a>
|
|
||||||
<a name='L135'></a><a href='#L135'>135</a>
|
|
||||||
<a name='L136'></a><a href='#L136'>136</a>
|
|
||||||
<a name='L137'></a><a href='#L137'>137</a>
|
|
||||||
<a name='L138'></a><a href='#L138'>138</a>
|
|
||||||
<a name='L139'></a><a href='#L139'>139</a>
|
|
||||||
<a name='L140'></a><a href='#L140'>140</a>
|
|
||||||
<a name='L141'></a><a href='#L141'>141</a>
|
|
||||||
<a name='L142'></a><a href='#L142'>142</a>
|
|
||||||
<a name='L143'></a><a href='#L143'>143</a>
|
|
||||||
<a name='L144'></a><a href='#L144'>144</a>
|
|
||||||
<a name='L145'></a><a href='#L145'>145</a>
|
|
||||||
<a name='L146'></a><a href='#L146'>146</a>
|
|
||||||
<a name='L147'></a><a href='#L147'>147</a>
|
|
||||||
<a name='L148'></a><a href='#L148'>148</a>
|
|
||||||
<a name='L149'></a><a href='#L149'>149</a>
|
|
||||||
<a name='L150'></a><a href='#L150'>150</a>
|
|
||||||
<a name='L151'></a><a href='#L151'>151</a>
|
|
||||||
<a name='L152'></a><a href='#L152'>152</a>
|
|
||||||
<a name='L153'></a><a href='#L153'>153</a>
|
|
||||||
<a name='L154'></a><a href='#L154'>154</a>
|
|
||||||
<a name='L155'></a><a href='#L155'>155</a>
|
|
||||||
<a name='L156'></a><a href='#L156'>156</a>
|
|
||||||
<a name='L157'></a><a href='#L157'>157</a>
|
|
||||||
<a name='L158'></a><a href='#L158'>158</a>
|
|
||||||
<a name='L159'></a><a href='#L159'>159</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">48x</span>
|
|
||||||
<span class="cline-any cline-yes">43x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">16x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">20x</span>
|
|
||||||
<span class="cline-any cline-yes">20x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">20x</span>
|
|
||||||
<span class="cline-any cline-yes">20x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">18x</span>
|
|
||||||
<span class="cline-any cline-yes">20x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">16x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">6x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">9x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">3x</span>
|
|
||||||
<span class="cline-any cline-yes">3x</span>
|
|
||||||
<span class="cline-any cline-yes">3x</span>
|
|
||||||
<span class="cline-any cline-yes">3x</span>
|
|
||||||
<span class="cline-any cline-yes">3x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">3x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">/**
|
|
||||||
* Self-contained stdout parser for Claude stream-json output.
|
|
||||||
* Zero external imports — required by the Paperclip adapter plugin UI parser contract.
|
|
||||||
*/
|
|
||||||
|
|
||||||
type TranscriptEntry =
|
|
||||||
| { kind: "assistant"; ts: string; text: string }
|
|
||||||
| { kind: "thinking"; ts: string; text: string }
|
|
||||||
| { kind: "user"; ts: string; text: string }
|
|
||||||
| { kind: "tool_call"; ts: string; name: string; input: unknown; toolUseId?: string }
|
|
||||||
| { kind: "tool_result"; ts: string; toolUseId: string; content: string; isError: boolean }
|
|
||||||
| { kind: "init"; ts: string; model: string; sessionId: string }
|
|
||||||
| { kind: "result"; ts: string; text: string; inputTokens: number; outputTokens: number; cachedTokens: number; costUsd: number; subtype: string; isError: boolean; errors: string[] }
|
|
||||||
| { kind: "stderr"; ts: string; text: string }
|
|
||||||
| { kind: "system"; ts: string; text: string }
|
|
||||||
| { kind: "stdout"; ts: string; text: string };
|
|
||||||
|
|
||||||
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
||||||
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
||||||
return value as Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function asNumber(value: unknown): number {
|
|
||||||
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function errorText(value: unknown): string {
|
|
||||||
if (typeof value === "string") return value;
|
|
||||||
const rec = asRecord(value);
|
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!rec) <span class="cstat-no" title="statement not covered" >return "";</span>
|
|
||||||
const msg =
|
|
||||||
(typeof rec.message === "string" && rec.message) ||
|
|
||||||
(<span class="branch-2 cbranch-no" title="branch not covered" >typeof rec.error === "string" && <span class="branch-3 cbranch-no" title="branch not covered" >r</span>ec.error) ||</span>
|
|
||||||
(<span class="branch-4 cbranch-no" title="branch not covered" >typeof rec.code === "string" && <span class="branch-5 cbranch-no" title="branch not covered" >r</span>ec.code) ||</span>
|
|
||||||
<span class="branch-6 cbranch-no" title="branch not covered" > "";</span>
|
|
||||||
if (msg) return msg;
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
||||||
<span class="cstat-no" title="statement not covered" > return JSON.stringify(rec);</span>
|
|
||||||
} catch {
|
|
||||||
<span class="cstat-no" title="statement not covered" > return "";</span>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeJsonParse(text: string): unknown {
|
|
||||||
try {
|
|
||||||
return JSON.parse(text);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseStdoutLine(line: string, ts: string): TranscriptEntry[] {
|
|
||||||
const parsed = asRecord(safeJsonParse(line));
|
|
||||||
if (!parsed) {
|
|
||||||
return [{ kind: "stdout", ts, text: line }];
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = typeof parsed.type === "string" ? parsed.type : <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
|
|
||||||
if (type === "system" && parsed.subtype === "init") {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
kind: "init",
|
|
||||||
ts,
|
|
||||||
model: typeof parsed.model === "string" ? parsed.model : "unknown",
|
|
||||||
sessionId: typeof parsed.session_id === "string" ? parsed.session_id : "",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "assistant") {
|
|
||||||
const message = asRecord(parsed.message) ?? <span class="branch-1 cbranch-no" title="branch not covered" >{};</span>
|
|
||||||
const content = Array.isArray(message.content) ? message.content : <span class="branch-1 cbranch-no" title="branch not covered" >[];</span>
|
|
||||||
const entries: TranscriptEntry[] = [];
|
|
||||||
for (const blockRaw of content) {
|
|
||||||
const block = asRecord(blockRaw);
|
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!block) <span class="cstat-no" title="statement not covered" >continue;</span>
|
|
||||||
const blockType = typeof block.type === "string" ? block.type : <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
|
|
||||||
if (blockType === "text") {
|
|
||||||
const text = typeof block.text === "string" ? block.text : <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
|
|
||||||
if (text) entries.push({ kind: "assistant", ts, text });
|
|
||||||
} else if (blockType === "thinking") {
|
|
||||||
const text = typeof block.thinking === "string" ? block.thinking : <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
|
|
||||||
if (text) entries.push({ kind: "thinking", ts, text });
|
|
||||||
} else if (<span class="missing-if-branch" title="else path not taken" >E</span>blockType === "tool_use") {
|
|
||||||
entries.push({
|
|
||||||
kind: "tool_call",
|
|
||||||
ts,
|
|
||||||
name: typeof block.name === "string" ? block.name : <span class="branch-1 cbranch-no" title="branch not covered" >"unknown",</span>
|
|
||||||
toolUseId:
|
|
||||||
typeof block.id === "string"
|
|
||||||
? block.id
|
|
||||||
: typeof block.tool_use_id === "string"
|
|
||||||
? block.tool_use_id
|
|
||||||
: <span class="branch-1 cbranch-no" title="branch not covered" >undefined,</span>
|
|
||||||
input: block.input ?? <span class="branch-1 cbranch-no" title="branch not covered" >{},</span>
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return entries.length > 0 ? entries : [{ kind: "stdout", ts, text: line }];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "user") {
|
|
||||||
const message = asRecord(parsed.message) ?? <span class="branch-1 cbranch-no" title="branch not covered" >{};</span>
|
|
||||||
const content = Array.isArray(message.content) ? message.content : <span class="branch-1 cbranch-no" title="branch not covered" >[];</span>
|
|
||||||
const entries: TranscriptEntry[] = [];
|
|
||||||
for (const blockRaw of content) {
|
|
||||||
const block = asRecord(blockRaw);
|
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!block) <span class="cstat-no" title="statement not covered" >continue;</span>
|
|
||||||
const blockType = typeof block.type === "string" ? block.type : <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
|
|
||||||
if (blockType === "text") {
|
|
||||||
const text = typeof block.text === "string" ? block.text : <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
|
|
||||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (text) entries.push({ kind: "user", ts, text });
|
|
||||||
} else if (<span class="missing-if-branch" title="else path not taken" >E</span>blockType === "tool_result") {
|
|
||||||
const toolUseId = typeof block.tool_use_id === "string" ? block.tool_use_id : <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
|
|
||||||
const isError = block.is_error === true;
|
|
||||||
let text = "";
|
|
||||||
if (typeof block.content === "string") {
|
|
||||||
text = block.content;
|
|
||||||
} else if (<span class="missing-if-branch" title="else path not taken" >E</span>Array.isArray(block.content)) {
|
|
||||||
const parts: string[] = [];
|
|
||||||
for (const part of block.content) {
|
|
||||||
const p = asRecord(part);
|
|
||||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (p && typeof p.text === "string") parts.push(p.text);
|
|
||||||
}
|
|
||||||
text = parts.join("\n");
|
|
||||||
}
|
|
||||||
entries.push({ kind: "tool_result", ts, toolUseId, content: text, isError });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (entries.length > 0) return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "result") {
|
|
||||||
const usage = asRecord(parsed.usage) ?? {};
|
|
||||||
const inputTokens = asNumber(usage.input_tokens);
|
|
||||||
const outputTokens = asNumber(usage.output_tokens);
|
|
||||||
const cachedTokens = asNumber(usage.cache_read_input_tokens);
|
|
||||||
const costUsd = asNumber(parsed.total_cost_usd);
|
|
||||||
const subtype = typeof parsed.subtype === "string" ? parsed.subtype : "";
|
|
||||||
const isError = parsed.is_error === true;
|
|
||||||
const errors = Array.isArray(parsed.errors) ? parsed.errors.map(errorText).filter(Boolean) : [];
|
|
||||||
const text = typeof parsed.result === "string" ? parsed.result : "";
|
|
||||||
return [{
|
|
||||||
kind: "result",
|
|
||||||
ts,
|
|
||||||
text,
|
|
||||||
inputTokens,
|
|
||||||
outputTokens,
|
|
||||||
cachedTokens,
|
|
||||||
costUsd,
|
|
||||||
subtype,
|
|
||||||
isError,
|
|
||||||
errors,
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [{ kind: "stdout", ts, text: line }];
|
|
||||||
}
|
|
||||||
</pre></td></tr></table></pre>
|
|
||||||
|
|
||||||
<div class='push'></div><!-- for sticky footer -->
|
|
||||||
</div><!-- /wrapper -->
|
|
||||||
<div class='footer quiet pad2 space-top1 center small'>
|
|
||||||
Code coverage generated by
|
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
||||||
at 2026-04-12T12:19:30.601Z
|
|
||||||
</div>
|
|
||||||
<script src="../prettify.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
prettyPrint();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="../sorter.js"></script>
|
|
||||||
<script src="../block-navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
-1471
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/server/execute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AA2SlG,wBAAsB,OAAO,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAgS3F"}
|
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/server/execute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AA2SlG,wBAAsB,OAAO,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAkQ3F"}
|
||||||
Vendored
+1
-31
@@ -364,41 +364,11 @@ export async function execute(ctx) {
|
|||||||
await onLog("stdout", stdout);
|
await onLog("stdout", stdout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the follow stream missed output (container exited quickly), do a
|
|
||||||
// one-shot log read as fallback before the pod is cleaned up.
|
|
||||||
if (!stdout.trim()) {
|
|
||||||
await onLog("stdout", `[paperclip] Log stream returned empty — reading pod logs directly...\n`);
|
|
||||||
stdout = await readPodLogs(namespace, podName, kubeconfigPath);
|
|
||||||
if (stdout.trim()) {
|
|
||||||
await onLog("stdout", stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (completionResult.status === "fulfilled") {
|
if (completionResult.status === "fulfilled") {
|
||||||
jobTimedOut = completionResult.value.timedOut;
|
jobTimedOut = completionResult.value.timedOut;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// waitForJobCompletion threw — re-check job state to avoid returning
|
jobTimedOut = true;
|
||||||
// while the job is still running (which would cause UI staleness and
|
|
||||||
// concurrency errors on retry).
|
|
||||||
jobTimedOut = false;
|
|
||||||
const actualState = await waitForJobCompletion(namespace, jobName, 0, kubeconfigPath);
|
|
||||||
if (actualState.timedOut) {
|
|
||||||
// Truly a timeout after re-check — treat as timed out.
|
|
||||||
jobTimedOut = true;
|
|
||||||
}
|
|
||||||
else if (!actualState.succeeded) {
|
|
||||||
// Job still not terminal — the completion error was likely transient.
|
|
||||||
// Return an error so the UI knows the run is not done, rather than
|
|
||||||
// returning with parsed (potentially incomplete) stdout.
|
|
||||||
await onLog("stderr", `[paperclip] Job ${jobName} still not terminal after log/completion mismatch — returning error to keep UI in sync.\n`);
|
|
||||||
return {
|
|
||||||
exitCode,
|
|
||||||
signal: null,
|
|
||||||
timedOut: false,
|
|
||||||
errorMessage: `Job ${jobName} did not complete cleanly (log stream ended before job reached terminal state)`,
|
|
||||||
errorCode: "k8s_job_state_mismatch",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
exitCode = await getPodExitCode(namespace, jobName, kubeconfigPath);
|
exitCode = await getPodExitCode(namespace, jobName, kubeconfigPath);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@farhoodliquor/paperclip-adapter-claude-k8s",
|
"name": "@farhoodliquor/paperclip-adapter-claude-k8s",
|
||||||
"version": "0.1.22",
|
"version": "0.1.12",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@farhoodliquor/paperclip-adapter-claude-k8s",
|
"name": "@farhoodliquor/paperclip-adapter-claude-k8s",
|
||||||
"version": "0.1.22",
|
"version": "0.1.12",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kubernetes/client-node": "^1.0.0",
|
"@kubernetes/client-node": "^1.0.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@farhoodliquor/paperclip-adapter-claude-k8s",
|
"name": "@farhoodliquor/paperclip-adapter-claude-k8s",
|
||||||
"version": "0.1.22",
|
"version": "0.1.12",
|
||||||
"description": "Paperclip adapter plugin that runs Claude Code agents as Kubernetes Jobs",
|
"description": "Paperclip adapter plugin that runs Claude Code agents as Kubernetes Jobs",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
+7
-1
@@ -1,7 +1,13 @@
|
|||||||
export const type = "claude_k8s";
|
export const type = "claude_k8s";
|
||||||
export const label = "Claude (Kubernetes)";
|
export const label = "Claude (Kubernetes)";
|
||||||
|
|
||||||
export const models: undefined = undefined;
|
export const models = [
|
||||||
|
{ id: "claude-opus-4-6", label: "Claude Opus 4.6" },
|
||||||
|
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
|
||||||
|
{ id: "claude-haiku-4-6", label: "Claude Haiku 4.6" },
|
||||||
|
{ id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5" },
|
||||||
|
{ id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5" },
|
||||||
|
];
|
||||||
|
|
||||||
export const agentConfigurationDoc = `# claude_k8s agent configuration
|
export const agentConfigurationDoc = `# claude_k8s agent configuration
|
||||||
|
|
||||||
|
|||||||
@@ -42,12 +42,6 @@ export function getConfigSchema(): AdapterConfigSchema {
|
|||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
// Kubernetes
|
// Kubernetes
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
key: "serviceAccountName",
|
|
||||||
label: "Service Account",
|
|
||||||
hint: "Service Account name for Job pods. Defaults to the cluster default.",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
key: "namespace",
|
key: "namespace",
|
||||||
|
|||||||
+19
-130
@@ -305,10 +305,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
|||||||
const retainJobs = asBoolean(config.retainJobs, false);
|
const retainJobs = asBoolean(config.retainJobs, false);
|
||||||
const kubeconfigPath = asString(config.kubeconfig, "") || undefined;
|
const kubeconfigPath = asString(config.kubeconfig, "") || undefined;
|
||||||
|
|
||||||
// Guard: claude_k8s must not run concurrently for the same agent (shared PVC/session).
|
// Guard: claude_k8s must not run concurrently for the same agent (shared PVC/session)
|
||||||
// After a server restart, orphaned K8s Jobs from previous (now-failed) runs may
|
|
||||||
// still be running. We detect those by comparing the Job's run-id label against
|
|
||||||
// the current runId and clean them up so this execution can proceed.
|
|
||||||
const agentId = ctx.agent.id;
|
const agentId = ctx.agent.id;
|
||||||
const selfPod = await getSelfPodInfo(kubeconfigPath);
|
const selfPod = await getSelfPodInfo(kubeconfigPath);
|
||||||
const guardNamespace = asString(config.namespace, "") || selfPod.namespace;
|
const guardNamespace = asString(config.namespace, "") || selfPod.namespace;
|
||||||
@@ -322,39 +319,15 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
|||||||
(j) => !j.status?.conditions?.some((c) => (c.type === "Complete" || c.type === "Failed") && c.status === "True"),
|
(j) => !j.status?.conditions?.some((c) => (c.type === "Complete" || c.type === "Failed") && c.status === "True"),
|
||||||
);
|
);
|
||||||
if (running.length > 0) {
|
if (running.length > 0) {
|
||||||
// Separate orphaned jobs (from a previous server-side run) from truly
|
const names = running.map((j) => j.metadata?.name).join(", ");
|
||||||
// concurrent jobs (same runId — shouldn't happen but guard defensively).
|
await onLog("stderr", `[paperclip] Concurrent run blocked: existing Job(s) still running for this agent: ${names}\n`);
|
||||||
const orphaned = running.filter(
|
return {
|
||||||
(j) => (j.metadata?.labels?.["paperclip.io/run-id"] ?? "") !== runId,
|
exitCode: null,
|
||||||
);
|
signal: null,
|
||||||
const samRun = running.filter(
|
timedOut: false,
|
||||||
(j) => (j.metadata?.labels?.["paperclip.io/run-id"] ?? "") === runId,
|
errorMessage: `Concurrent run blocked: Job ${names} is still running for this agent`,
|
||||||
);
|
errorCode: "k8s_concurrent_run_blocked",
|
||||||
|
};
|
||||||
if (orphaned.length > 0) {
|
|
||||||
const orphanNames = orphaned.map((j) => j.metadata?.name).join(", ");
|
|
||||||
await onLog("stdout", `[paperclip] Cleaning up ${orphaned.length} orphaned K8s Job(s) from previous run(s): ${orphanNames}\n`);
|
|
||||||
for (const j of orphaned) {
|
|
||||||
const name = j.metadata?.name;
|
|
||||||
if (name) {
|
|
||||||
await cleanupJob(guardNamespace, name, onLog, kubeconfigPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are still running Jobs that belong to THIS run (shouldn't happen
|
|
||||||
// since we haven't created the Job yet), block execution.
|
|
||||||
if (samRun.length > 0) {
|
|
||||||
const names = samRun.map((j) => j.metadata?.name).join(", ");
|
|
||||||
await onLog("stderr", `[paperclip] Concurrent run blocked: existing Job(s) still running for this run: ${names}\n`);
|
|
||||||
return {
|
|
||||||
exitCode: null,
|
|
||||||
signal: null,
|
|
||||||
timedOut: false,
|
|
||||||
errorMessage: `Concurrent run blocked: Job ${names} is still running for this agent`,
|
|
||||||
errorCode: "k8s_concurrent_run_blocked",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// If we can't check, proceed — the heartbeat service enforces concurrency too
|
// If we can't check, proceed — the heartbeat service enforces concurrency too
|
||||||
@@ -406,9 +379,6 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
|||||||
let exitCode: number | null = null;
|
let exitCode: number | null = null;
|
||||||
let jobTimedOut = false;
|
let jobTimedOut = false;
|
||||||
let keepaliveTimer: ReturnType<typeof setInterval> | null = null;
|
let keepaliveTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
// Set when we return a mismatch error so the finally block knows not to
|
|
||||||
// delete a job that is still alive and the UI is waiting on.
|
|
||||||
let skipCleanup = false;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Wait for pod to be ready for log streaming
|
// Wait for pod to be ready for log streaming
|
||||||
@@ -418,9 +388,11 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
|||||||
podName = await waitForPod(namespace, jobName, scheduleTimeoutMs, onLog, kubeconfigPath);
|
podName = await waitForPod(namespace, jobName, scheduleTimeoutMs, onLog, kubeconfigPath);
|
||||||
await onLog("stdout", `[paperclip] Pod running: ${podName}\n`);
|
await onLog("stdout", `[paperclip] Pod running: ${podName}\n`);
|
||||||
|
|
||||||
// Notify the server that execution has started. This sets
|
// Notify the server that execution has started. Without this call,
|
||||||
// processStartedAt and refreshes updatedAt in the DB, which the
|
// the server has no processStartedAt timestamp for the run, so the
|
||||||
// stale-run reaper (reapOrphanedRuns) uses to decide liveness.
|
// stale-run reaper (reapOrphanedRuns) cannot distinguish a live K8s
|
||||||
|
// job from an orphaned run and may mark it as failed — causing the
|
||||||
|
// UI to show no active runs and triggering duplicate run attempts.
|
||||||
if (ctx.onSpawn) {
|
if (ctx.onSpawn) {
|
||||||
await ctx.onSpawn({
|
await ctx.onSpawn({
|
||||||
pid: -1, // no local process; sentinel for K8s Job
|
pid: -1, // no local process; sentinel for K8s Job
|
||||||
@@ -449,61 +421,10 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
|||||||
// Keepalive: periodically send a status line via onLog so the
|
// Keepalive: periodically send a status line via onLog so the
|
||||||
// Paperclip server knows the adapter is still alive even when the
|
// Paperclip server knows the adapter is still alive even when the
|
||||||
// pod produces no output (e.g. Claude is in a long thinking phase).
|
// pod produces no output (e.g. Claude is in a long thinking phase).
|
||||||
//
|
|
||||||
// IMPORTANT: onLog alone does NOT update the run's updatedAt in the
|
|
||||||
// DB — it only appends to the log store and publishes SSE events.
|
|
||||||
// The stale-run reaper checks updatedAt, so we must also call
|
|
||||||
// onSpawn periodically to refresh it. Without this, multi-instance
|
|
||||||
// deployments can reap a live run from another server instance
|
|
||||||
// after the 5-minute staleness window.
|
|
||||||
//
|
|
||||||
// BUT: the keepalive must NEVER refresh updatedAt if the underlying
|
|
||||||
// K8s Job is already terminal. Otherwise, if execute() stalls after
|
|
||||||
// the pod finishes (e.g. a slow K8s API call, a hung log stream
|
|
||||||
// drain, or a Job whose Complete condition lags pod termination),
|
|
||||||
// we would keep the run marked "alive" indefinitely while the pod
|
|
||||||
// is actually gone — the exact "UI thinks jobs are running when
|
|
||||||
// they are not" bug. We verify Job liveness every tick and stop
|
|
||||||
// refreshing as soon as the Job reaches a terminal state; if
|
|
||||||
// execute() is truly stuck, the reaper will then catch it within
|
|
||||||
// the normal 5-minute staleness window.
|
|
||||||
let lastLogAt = Date.now();
|
let lastLogAt = Date.now();
|
||||||
let keepaliveTick = 0;
|
|
||||||
let keepaliveJobTerminal = false;
|
|
||||||
keepaliveTimer = setInterval(() => {
|
keepaliveTimer = setInterval(() => {
|
||||||
// Fire-and-forget the async work; setInterval callbacks must be
|
const silenceSec = Math.round((Date.now() - lastLogAt) / 1000);
|
||||||
// synchronous or the timer will drift.
|
void onLog("stdout", `[paperclip] keepalive — job ${jobName} running (${silenceSec}s since last output)\n`);
|
||||||
void (async () => {
|
|
||||||
if (keepaliveJobTerminal) return;
|
|
||||||
|
|
||||||
// Verify the Job is still alive before announcing or refreshing.
|
|
||||||
try {
|
|
||||||
const job = await batchApi.readNamespacedJob({ name: jobName, namespace });
|
|
||||||
const terminal = job.status?.conditions?.some(
|
|
||||||
(c) => (c.type === "Complete" || c.type === "Failed") && c.status === "True",
|
|
||||||
);
|
|
||||||
if (terminal) {
|
|
||||||
keepaliveJobTerminal = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Job may have been deleted out from under us, or the API call
|
|
||||||
// transiently failed. Either way, do not refresh updatedAt —
|
|
||||||
// either the Job really is gone, or the next tick will re-check.
|
|
||||||
keepaliveJobTerminal = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const silenceSec = Math.round((Date.now() - lastLogAt) / 1000);
|
|
||||||
void onLog("stdout", `[paperclip] keepalive — job ${jobName} running (${silenceSec}s since last output)\n`);
|
|
||||||
|
|
||||||
// Refresh updatedAt every ~4 minutes (16 ticks × 15s) to stay
|
|
||||||
// well within the 5-minute reaper staleness window.
|
|
||||||
keepaliveTick++;
|
|
||||||
if (ctx.onSpawn && keepaliveTick % 16 === 0) {
|
|
||||||
void ctx.onSpawn({ pid: -1, processGroupId: null, startedAt: new Date().toISOString() }).catch(() => {});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, KEEPALIVE_INTERVAL_MS);
|
}, KEEPALIVE_INTERVAL_MS);
|
||||||
const wrappedOnLog: typeof onLog = async (stream, chunk) => {
|
const wrappedOnLog: typeof onLog = async (stream, chunk) => {
|
||||||
lastLogAt = Date.now();
|
lastLogAt = Date.now();
|
||||||
@@ -522,15 +443,6 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Stop the keepalive immediately once the job has reached a terminal
|
|
||||||
// state — do not wait for the finally block. Any K8s API call or
|
|
||||||
// cleanup that happens after this point should not keep the run
|
|
||||||
// marked "alive" in the DB via onSpawn refreshes.
|
|
||||||
if (keepaliveTimer) {
|
|
||||||
clearInterval(keepaliveTimer);
|
|
||||||
keepaliveTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logResult.status === "fulfilled") {
|
if (logResult.status === "fulfilled") {
|
||||||
stdout = logResult.value;
|
stdout = logResult.value;
|
||||||
}
|
}
|
||||||
@@ -548,36 +460,13 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
|||||||
if (completionResult.status === "fulfilled") {
|
if (completionResult.status === "fulfilled") {
|
||||||
jobTimedOut = completionResult.value.timedOut;
|
jobTimedOut = completionResult.value.timedOut;
|
||||||
} else {
|
} else {
|
||||||
// waitForJobCompletion threw — re-check job state to avoid returning
|
jobTimedOut = true;
|
||||||
// while the job is still running (which would cause UI staleness and
|
|
||||||
// concurrency errors on retry).
|
|
||||||
jobTimedOut = false;
|
|
||||||
const actualState = await waitForJobCompletion(namespace, jobName, 0, kubeconfigPath);
|
|
||||||
if (actualState.timedOut) {
|
|
||||||
// Truly a timeout after re-check — treat as timed out.
|
|
||||||
jobTimedOut = true;
|
|
||||||
} else if (!actualState.succeeded) {
|
|
||||||
// Job still not terminal — the completion error was likely transient.
|
|
||||||
// Return an error so the UI knows the run is not done, rather than
|
|
||||||
// returning with parsed (potentially incomplete) stdout.
|
|
||||||
await onLog("stderr", `[paperclip] Job ${jobName} still not terminal after log/completion mismatch — returning error to keep UI in sync.\n`);
|
|
||||||
skipCleanup = true;
|
|
||||||
return {
|
|
||||||
exitCode,
|
|
||||||
signal: null,
|
|
||||||
timedOut: false,
|
|
||||||
errorMessage: `Job ${jobName} did not complete cleanly (log stream ended before job reached terminal state)`,
|
|
||||||
errorCode: "k8s_job_state_mismatch",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exitCode = await getPodExitCode(namespace, jobName, kubeconfigPath);
|
exitCode = await getPodExitCode(namespace, jobName, kubeconfigPath);
|
||||||
} finally {
|
} finally {
|
||||||
if (keepaliveTimer) clearInterval(keepaliveTimer);
|
if (keepaliveTimer) clearInterval(keepaliveTimer);
|
||||||
if (skipCleanup) {
|
if (!retainJobs) {
|
||||||
await onLog("stdout", `[paperclip] Retaining job ${jobName} (state mismatch — UI is waiting on it)\n`);
|
|
||||||
} else if (!retainJobs) {
|
|
||||||
await cleanupJob(namespace, jobName, onLog, kubeconfigPath);
|
await cleanupJob(namespace, jobName, onLog, kubeconfigPath);
|
||||||
} else {
|
} else {
|
||||||
await onLog("stdout", `[paperclip] Retaining job ${jobName} for debugging (retainJobs=true)\n`);
|
await onLog("stdout", `[paperclip] Retaining job ${jobName} for debugging (retainJobs=true)\n`);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ describe("listK8sModels", () => {
|
|||||||
|
|
||||||
it("returns direct API models by default", async () => {
|
it("returns direct API models by default", async () => {
|
||||||
const models = await listK8sModels();
|
const models = await listK8sModels();
|
||||||
expect(models.some((m) => m.id === "claude-opus-4-7")).toBe(true);
|
|
||||||
expect(models.some((m) => m.id === "claude-opus-4-6")).toBe(true);
|
expect(models.some((m) => m.id === "claude-opus-4-6")).toBe(true);
|
||||||
expect(models.every((m) => !m.id.includes("anthropic."))).toBe(true);
|
expect(models.every((m) => !m.id.includes("anthropic."))).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -47,6 +46,6 @@ describe("listK8sModels", () => {
|
|||||||
it("ignores blank ANTHROPIC_BEDROCK_BASE_URL", async () => {
|
it("ignores blank ANTHROPIC_BEDROCK_BASE_URL", async () => {
|
||||||
process.env.ANTHROPIC_BEDROCK_BASE_URL = " ";
|
process.env.ANTHROPIC_BEDROCK_BASE_URL = " ";
|
||||||
const models = await listK8sModels();
|
const models = await listK8sModels();
|
||||||
expect(models.some((m) => m.id === "claude-opus-4-7")).toBe(true);
|
expect(models.some((m) => m.id === "claude-opus-4-6")).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-10
@@ -1,16 +1,7 @@
|
|||||||
import type { AdapterModel } from "@paperclipai/adapter-utils";
|
import type { AdapterModel } from "@paperclipai/adapter-utils";
|
||||||
|
import { models as DIRECT_MODELS } from "../index.js";
|
||||||
const DIRECT_MODELS: AdapterModel[] = [
|
|
||||||
{ id: "claude-opus-4-7", label: "Claude Opus 4.7" },
|
|
||||||
{ id: "claude-opus-4-6", label: "Claude Opus 4.6" },
|
|
||||||
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
|
|
||||||
{ id: "claude-haiku-4-6", label: "Claude Haiku 4.6" },
|
|
||||||
{ id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5" },
|
|
||||||
{ id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const BEDROCK_MODELS: AdapterModel[] = [
|
const BEDROCK_MODELS: AdapterModel[] = [
|
||||||
{ id: "us.anthropic.claude-opus-4-7", label: "Bedrock Opus 4.7" },
|
|
||||||
{ id: "us.anthropic.claude-opus-4-6-v1", label: "Bedrock Opus 4.6" },
|
{ id: "us.anthropic.claude-opus-4-6-v1", label: "Bedrock Opus 4.6" },
|
||||||
{ id: "us.anthropic.claude-sonnet-4-5-20250929-v2:0", label: "Bedrock Sonnet 4.5" },
|
{ id: "us.anthropic.claude-sonnet-4-5-20250929-v2:0", label: "Bedrock Sonnet 4.5" },
|
||||||
{ id: "us.anthropic.claude-haiku-4-5-20251001-v1:0", label: "Bedrock Haiku 4.5" },
|
{ id: "us.anthropic.claude-haiku-4-5-20251001-v1:0", label: "Bedrock Haiku 4.5" },
|
||||||
|
|||||||
Reference in New Issue
Block a user