Compare commits

...

4 Commits

Author SHA1 Message Date
Chris Farhood fa6c115be4 0.1.39 2026-04-30 09:03:07 -04:00
Chris Farhood 480f7cf3d1 fix(ui-parser): bundle as CJS so the sandboxed worker can load it
The Paperclip UI loads each adapter's ui-parser.js inside a sandboxed
Web Worker via `new Function(...)` to render the run transcript. The
worker can only evaluate CJS — ESM `export` syntax silently fails to
register `parseStdoutLine`, and the run window falls back to dumping
raw JSONL.

tsc was emitting ESM `export function parseStdoutLine`, so every
published version since the parser was added has shipped a parser the
UI can't load. Add the same esbuild step the claude-k8s adapter uses
(0.2.4) to overwrite dist/ui-parser.js with a CJS bundle that assigns
to module.exports.

Also bump @paperclipai/adapter-utils from a stale 2026.415.0-canary.7
pin to ^2026.428.0 (current stable). All 406 tests pass against the
new types; no API drift in the imported surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 09:03:03 -04:00
Chris Farhood 5ed041fd84 Create LOGGINGCHANGE.md 2026-04-27 17:03:22 -04:00
Chris Farhood fe6bc0c2d6 fix(config): remove instructionsFilePath from UI config schema
Handled by the framework via instructionsPathKey/supportsInstructionsBundle.
Surfacing it as an editable field in the config schema was redundant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 07:25:33 -04:00
4 changed files with 841 additions and 18 deletions
+361
View File
@@ -0,0 +1,361 @@
You are implementing two coordinated changes to a Paperclip adapter plugin.
The repo is at /Users/Repositories/paperclip-adapter-opencode-k8s on branch
master. Work on a new branch off master — do NOT commit directly to master.
Before you start, read these files fully:
- src/server/execute.ts (~1218 lines; this is the main file you'll edit)
- src/server/job-manifest.ts
- src/server/log-dedup.ts (you will delete this)
- src/server/parse.ts
- src/server/index.ts
- src/index.ts
Run `npm install` and then `npm test`. Confirm green. Note the test count
for later comparison. Do NOT run `npm run build` — CI handles that.
=============================================================================
WHY WE ARE DOING THIS
=============================================================================
Two tightly coupled bugs:
(A) The adapter doesn't declare `hasOutOfProcessLiveness: true` on its
ServerAdapterModule. The revitalize reaper therefore treats it as an
in-process adapter, expects a local child PID, finds none, and marks
every run `process_lost` after 5 minutes of staleness.
(B) The adapter reads pod logs via the Kubernetes log API (follow mode).
At production scale the stream drops every few seconds, exhausting
the 50-reconnect cap within 2.5 minutes. Long runs lose live UI
output, and combined with (A) they fail entirely.
Fix both in one PR:
1. Declare `hasOutOfProcessLiveness: true` in createServerAdapter().
2. Have the pod tee opencode's stdout to a file on the shared PVC, and
have the adapter tail that file from the Paperclip server process.
We are NOT going to:
- wrap the opencode binary
- use hooks
- add a sidecar
- change revitalize
- keep the k8s log API as a fallback
We ARE going to:
- replace k8s log streaming with filesystem tailing entirely
- delete all reconnect logic and the log-dedup filter
- keep `kubectl logs -f` working (tee preserves stdout)
- add the liveness flag so the reaper uses staleness-based liveness
=============================================================================
SCOPE OF CHANGES
=============================================================================
--- hasOutOfProcessLiveness flag (src/server/index.ts) ---
The file today returns a plain object from createServerAdapter(). Add
`hasOutOfProcessLiveness: true` to the returned object, matching the
pattern from paperclip-adapter-claude-k8s. The adapter-utils type predates
this field, so the return needs a cast.
Before (approximately):
export function createServerAdapter(): ServerAdapterModule {
return {
type,
execute,
// ... other fields ...
};
}
After:
export function createServerAdapter(): ServerAdapterModule {
return {
type,
execute,
// ... other fields ...
// Tells the reaper to skip local PID checks and use the staleness-based
// liveness window instead (adapter spawns K8s Jobs in separate pods).
// Cast required: adapter-utils ServerAdapterModule type predates this field.
hasOutOfProcessLiveness: true,
} as ServerAdapterModule;
}
--- Job manifest (src/server/job-manifest.ts) ---
1. MODIFY the main container command to tee stdout. Current code at
approximately line 409:
const mainCommand = `${configSetup}cat /tmp/prompt/prompt.txt | opencode ${opencodeArgsEscaped}`;
Change to:
const podLogPath =
`/paperclip/instances/default/run-logs/${companyId}/${agentId}/${runId}.pod.ndjson`;
const mainCommand = `${configSetup}cat /tmp/prompt/prompt.txt | opencode ${opencodeArgsEscaped} | tee ${podLogPath}`;
`companyId`, `agentId`, `runId` are already in scope in buildJobManifest
via the destructuring at line 219 (`agent`, `runId`) — use `agent.id`
and `agent.companyId`. If you prefer cleaner code, add a local:
const companyId = agent.companyId;
const agentId = agent.id;
2. MODIFY the init container command to create the parent directory before
the main container starts. The existing init container today writes the
prompt file with `printf`. Amend its command to also `mkdir -p` the log
directory. The init container is at approximately line 444 (prompt
secret path) and line 451 (direct printf path) — there are TWO init
container variants. Amend BOTH to prepend the mkdir:
Variant 1 (large-prompt path, approx line 444):
Before: `cp /tmp/prompt-secret/prompt /tmp/prompt/prompt.txt`
After: `mkdir -p /paperclip/instances/default/run-logs/${companyId}/${agentId} && cp /tmp/prompt-secret/prompt /tmp/prompt/prompt.txt`
Variant 2 (direct path, approx line 451):
Before: `printf '%s' "$PROMPT_CONTENT" > /tmp/prompt/prompt.txt`
After: `mkdir -p /paperclip/instances/default/run-logs/${companyId}/${agentId} && printf '%s' "$PROMPT_CONTENT" > /tmp/prompt/prompt.txt`
Use template substitution for companyId/agentId/runId — these are all
in scope in the builder.
3. EXPORT the log path builder so execute.ts can compute the same path:
export function buildPodLogPath(companyId: string, agentId: string, runId: string): string {
return `/paperclip/instances/default/run-logs/${companyId}/${agentId}/${runId}.pod.ndjson`;
}
Return this path from buildJobManifest alongside other fields in
JobBuildResult (add `podLogPath: string` to the interface at approx
line 46). Update the final `return { job, jobName, namespace, prompt,
opencodeArgs, promptMetrics }` (approx line 482) to include podLogPath.
4. ID SANITIZATION: before using companyId/agentId/runId in the path,
validate they match `^[a-zA-Z0-9-]+$`. Add a helper at the top of
job-manifest.ts:
function assertSafePathComponent(field: string, value: string): void {
if (!/^[a-zA-Z0-9-]+$/.test(value)) {
throw new Error(`Invalid ${field} for log path: ${value}`);
}
}
Call it for companyId, agentId, and runId before computing podLogPath
and before interpolating into the init container commands.
--- Adapter (src/server/execute.ts) ---
1. DELETE the `LogLineDedupFilter` import (approx line 13).
2. DELETE constants (approx lines 19-26):
LOG_STREAM_RECONNECT_DELAY_MS
LOG_STREAM_RECONNECT_MAX_DELAY_MS
MAX_LOG_RECONNECT_ATTEMPTS
LOG_STREAM_BAIL_TIMEOUT_MS
3. DELETE functions:
streamPodLogsOnce (approx line 168)
streamPodLogs (approx line 252)
readPodLogs (approx line 330)
waitForPodTermination (approx line 355) — only used by the fallback
4. DELETE the bail timer machinery inside any function being removed
(bailTimer, bailResolve, bailPromise, stopPoller).
5. DELETE the fallback path in `execute` around lines 675-693:
if (!stdout.trim()) {
// ... waitForPodTermination + readPodLogs fallback
} else if (!parseOpenCodeJsonl(stdout).sessionId) {
// ... partial-stdout fallback
}
6. ADD a new function `tailPodLogFile` in execute.ts. Inline is fine; do
not create a new module. Signature:
interface TailOptions {
onLog: AdapterExecutionContext["onLog"];
stopSignal: { stopped: boolean };
}
async function tailPodLogFile(
filePath: string,
opts: TailOptions,
): Promise<string> { ... }
Behavior:
- Wait up to 30 seconds for the file to exist. Poll with
fs.promises.stat every 250ms. If the file doesn't appear in 30s,
throw an Error: `Pod log file never appeared at ${filePath}`.
- Once it exists, open with fs.promises.open(filePath, 'r').
- Track a byte offset starting at 0.
- Poll loop: 250ms active cadence, backs off to 1000ms if the file
hasn't grown for 5 consecutive polls (reset to 250ms on any
growth). For each poll:
a. stat the file, compare size to offset
b. if size > offset, read bytes from [offset, size) into a Buffer
c. update offset = size
d. concatenate any pending partial line with the new buffer,
split on '\n'
e. last element is the new pending partial line (if no trailing
newline) or empty
f. for every complete line, call onLog("stdout", line + "\n")
and append to an in-memory accumulator (string)
- Exit when opts.stopSignal.stopped === true. Before returning, do
ONE final read-to-EOF to drain tail bytes. Close the handle.
Return the accumulator.
Use fs.promises.open / FileHandle.read / FileHandle.close. Do NOT use
fs.watch or chokidar.
7. REPLACE the existing log-streaming section of `execute`. Find where
streamPodLogs is invoked inside a `Promise.allSettled` with
waitForJobCompletion (approx line 660). Replace that call with
tailPodLogFile. Pattern:
const { /* ..., */ podLogPath } = built;
// ... create secret, create job, wait for pod ...
const stopSignal = { stopped: false };
const [tailResult, completionResult] = await Promise.allSettled([
tailPodLogFile(podLogPath, { onLog, stopSignal }),
waitForJobCompletion(namespace, jobName, ...).then(r => { stopSignal.stopped = true; return r; }),
]);
const stdout = tailResult.status === "fulfilled" ? tailResult.value : "";
Keep waitForJobCompletion unchanged. Keep the existing `keepaliveTimer`
and `cancelSignal` / cancel-polling machinery unchanged — those are
independent of log streaming.
8. ADD log file cleanup. Find `cleanupJob` (the function that deletes the
K8s Job). After successful deletion, best-effort delete the log file:
try { await fs.promises.unlink(podLogPath); } catch { /* non-fatal */ }
Skip the unlink if `retainJobs === true`.
cleanupJob will need podLogPath passed in; thread it from the caller.
--- Delete entire files ---
- src/server/log-dedup.ts
- src/server/log-dedup.test.ts
--- Tests ---
- Delete any execute.test.ts tests covering streamPodLogsOnce,
streamPodLogs, readPodLogs, waitForPodTermination, the bail timer, or
LogLineDedupFilter. Search for those identifiers; remove matching
describe/it blocks. Non-log-streaming tests in the same file stay.
- Add test cases for tailPodLogFile to execute.test.ts. Cover:
1. File appears within 30s; content is tailed line-by-line
2. File never appears; function throws with expected message
3. Partial trailing line buffered and emitted on next poll
4. Stop signal exits the loop; final drain reads remaining bytes
5. Adaptive backoff: idle polls slow; active polls speed up
Use vitest fake timers (vi.useFakeTimers) and a tmpdir via
`fs.mkdtempSync(path.join(os.tmpdir(), 'opencode-tailer-'))`.
=============================================================================
TESTING
=============================================================================
After all changes:
1. `npm run typecheck` — must pass (the `as ServerAdapterModule` cast
may be needed; mirror claude-k8s's pattern)
2. `npm test` — must pass. Test count will drop vs baseline because you
deleted tests. Record the new passing count.
Do NOT run the adapter end-to-end. Do NOT require a k8s cluster.
=============================================================================
BRANCH, COMMIT, PUSH, PR
=============================================================================
1. Create a new branch off master:
git checkout master && git pull && git checkout -b feat/filesystem-log-tail-and-liveness-flag
2. Make all changes above. Commit as ONE commit:
feat: declare hasOutOfProcessLiveness and tail pod log from filesystem
Two coordinated fixes for long-running agent failures:
(1) Declare hasOutOfProcessLiveness: true on the ServerAdapterModule.
Without it the reaper treated this adapter as in-process, expected
a local child PID, and marked every run process_lost after 5min
staleness. Flag tells the reaper to use the staleness-based
liveness window for out-of-process adapters.
(2) Replace k8s log API streaming with filesystem tailing. The k8s
follow stream drops every ~3 seconds at production scale,
exhausting the 50-attempt reconnect cap within 2.5 minutes. Pod
now tees opencode's stdout to
/paperclip/instances/default/run-logs/<companyId>/<agentId>/<runId>.pod.ndjson
on the shared PVC; adapter tails the file directly. kubectl logs -f
still works (tee preserves stdout).
Deletes:
- LogLineDedupFilter and all reconnect logic
- streamPodLogsOnce, streamPodLogs, readPodLogs, waitForPodTermination
- Both fallback paths (empty-stream and missing-sessionId)
Adds:
- tailPodLogFile: adaptive 250ms/1s poll loop with partial-line
buffering and tail-drain on stopSignal
- Log file cleanup tied to retainJobs
- Path-component sanitization (companyId/agentId/runId must match
[a-zA-Z0-9-]+)
Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
3. Push:
git push -u origin feat/filesystem-log-tail-and-liveness-flag
4. Open a PR against master with `gh pr create`:
Title: `feat: declare hasOutOfProcessLiveness and tail pod log from filesystem`
Body (use a heredoc):
## Summary
- Declares `hasOutOfProcessLiveness: true` so the reaper uses
staleness-based liveness instead of expecting a local PID
- Pod tees opencode stdout to PVC; adapter tails the file directly
- Eliminates k8s log API dependency for streaming
- Deletes LogLineDedupFilter, reconnect logic, both fallback paths
## Why
At production scale (144 concurrent runs), two bugs combined:
(a) no liveness flag → reaper marked runs process_lost at 5min
(b) k8s log follow stream drops every ~3s, exhausting the 50-reconnect
cap. Runs over ~2.5min lost live output; over 5min failed outright.
Both must be fixed together — the flag alone doesn't help if the log
stream still drops, and the log tail alone doesn't help if the reaper
kills the run for missing PID.
## Path
`/paperclip/instances/default/run-logs/<companyId>/<agentId>/<runId>.pod.ndjson`
— the `.pod.ndjson` suffix distinguishes the pod-written file from
revitalize's server-side `<runId>.ndjson` log store.
## Breaking
Old Job manifests (pre-tee) are incompatible — the tailer's 30s
"file missing" window will surface an error on in-flight runs at
deploy time. Operator retry required. Consistent with the companion
change in paperclip-adapter-claude-k8s.
## Test plan
- [ ] npm test passes
- [ ] Manual: deploy to cluster, run a >5min agent, confirm live UI
output and no reaper fire
- [ ] Manual: verify kubectl logs -f still works on the Job pod
- [ ] Manual: confirm log file is cleaned up when Job cleanup runs
(retainJobs=false) and preserved when retainJobs=true
=============================================================================
WRAPPING UP
=============================================================================
Report back with:
1. Branch name and commit hash
2. PR URL
3. Final test count (numbers will drop vs baseline because you deleted
tests — record baseline and final)
4. Line count of execute.ts before and after (should drop significantly)
5. Any deviation from these instructions, with reason
If ANY of the following happens, STOP and report instead of improvising:
- A file path doesn't match what's described (e.g. the mainCommand
pattern has changed)
- A function you're supposed to delete has other callers you didn't
expect (streamPodLogsOnce in particular may have test-only imports
that need untangling)
- A test you're supposed to keep depends on something you deleted
- Typecheck fails and the fix is non-obvious
- The `as ServerAdapterModule` cast doesn't satisfy TypeScript
Do NOT push to master. Do NOT tag a version. Do NOT bump package.json
version — leave it as-is.
+474 -7
View File
@@ -1,26 +1,27 @@
{
"name": "paperclip-adapter-opencode-k8s",
"version": "0.1.30",
"version": "0.1.39",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "paperclip-adapter-opencode-k8s",
"version": "0.1.30",
"version": "0.1.39",
"license": "MIT",
"dependencies": {
"@kubernetes/client-node": "^1.0.0",
"picocolors": "^1.1.1"
},
"devDependencies": {
"@paperclipai/adapter-utils": "2026.415.0-canary.7",
"@paperclipai/adapter-utils": "^2026.428.0",
"@types/node": "^24.6.0",
"@vitest/coverage-v8": "^4.1.5",
"esbuild": "^0.24.0",
"typescript": "^5.7.3",
"vitest": "^4.1.4"
},
"peerDependencies": {
"@paperclipai/adapter-utils": ">=2026.415.0-canary.7"
"@paperclipai/adapter-utils": ">=2026.428.0"
}
},
"node_modules/@babel/helper-string-parser": {
@@ -117,6 +118,431 @@
"tslib": "^2.4.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -223,9 +649,9 @@
}
},
"node_modules/@paperclipai/adapter-utils": {
"version": "2026.415.0-canary.7",
"resolved": "https://registry.npmjs.org/@paperclipai/adapter-utils/-/adapter-utils-2026.415.0-canary.7.tgz",
"integrity": "sha512-VNzIZmu1lrK6QM8Ad9WkOihZItfkj21NHKQf+artDcbwFT2hHbDAD9hdW2W9NMVxYdFvvnws3w76FI/BUbCMbQ==",
"version": "2026.428.0",
"resolved": "https://registry.npmjs.org/@paperclipai/adapter-utils/-/adapter-utils-2026.428.0.tgz",
"integrity": "sha512-kGHpE7rhePPCbnG3OwXbNuHZZuI+XyuFgNSiDnrEeiSbkI2c5XHM2WnWDCZ/NGHULfJW3lWhSxGMFoYqiy38vQ==",
"dev": true,
"license": "MIT"
},
@@ -1033,6 +1459,47 @@
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.24.2",
"@esbuild/android-arm": "0.24.2",
"@esbuild/android-arm64": "0.24.2",
"@esbuild/android-x64": "0.24.2",
"@esbuild/darwin-arm64": "0.24.2",
"@esbuild/darwin-x64": "0.24.2",
"@esbuild/freebsd-arm64": "0.24.2",
"@esbuild/freebsd-x64": "0.24.2",
"@esbuild/linux-arm": "0.24.2",
"@esbuild/linux-arm64": "0.24.2",
"@esbuild/linux-ia32": "0.24.2",
"@esbuild/linux-loong64": "0.24.2",
"@esbuild/linux-mips64el": "0.24.2",
"@esbuild/linux-ppc64": "0.24.2",
"@esbuild/linux-riscv64": "0.24.2",
"@esbuild/linux-s390x": "0.24.2",
"@esbuild/linux-x64": "0.24.2",
"@esbuild/netbsd-arm64": "0.24.2",
"@esbuild/netbsd-x64": "0.24.2",
"@esbuild/openbsd-arm64": "0.24.2",
"@esbuild/openbsd-x64": "0.24.2",
"@esbuild/sunos-x64": "0.24.2",
"@esbuild/win32-arm64": "0.24.2",
"@esbuild/win32-ia32": "0.24.2",
"@esbuild/win32-x64": "0.24.2"
}
},
"node_modules/estree-walker": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+6 -4
View File
@@ -1,6 +1,6 @@
{
"name": "paperclip-adapter-opencode-k8s",
"version": "0.1.37",
"version": "0.1.39",
"description": "Paperclip adapter plugin that runs OpenCode agents as Kubernetes Jobs",
"license": "MIT",
"type": "module",
@@ -17,7 +17,8 @@
"dist"
],
"scripts": {
"build": "tsc",
"build": "tsc && npm run build:ui-parser",
"build:ui-parser": "esbuild src/ui-parser.ts --bundle --format=cjs --target=es2020 --outfile=dist/ui-parser.js --log-level=warning",
"clean": "rm -rf dist",
"typecheck": "tsc --noEmit",
"test": "vitest run",
@@ -28,12 +29,13 @@
"picocolors": "^1.1.1"
},
"peerDependencies": {
"@paperclipai/adapter-utils": ">=2026.415.0-canary.7"
"@paperclipai/adapter-utils": ">=2026.428.0"
},
"devDependencies": {
"@paperclipai/adapter-utils": "2026.415.0-canary.7",
"@paperclipai/adapter-utils": "^2026.428.0",
"@types/node": "^24.6.0",
"@vitest/coverage-v8": "^4.1.5",
"esbuild": "^0.24.0",
"typescript": "^5.7.3",
"vitest": "^4.1.4"
}
-7
View File
@@ -11,13 +11,6 @@ export function getConfigSchema(): AdapterConfigSchema {
hint: "Provider-specific reasoning/profile variant passed as --variant",
group: "Core",
},
{
key: "instructionsFilePath",
label: "Instructions File Path",
type: "text",
hint: "Absolute path to a markdown file (e.g. AGENTS.md) prepended as system instructions before the task prompt",
group: "Core",
},
{
key: "dangerouslySkipPermissions",
label: "Skip Permission Checks",