@bybrawe/opencode-loop 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +4 -2
- package/examples/OPENCODE2-0.6.1.md +60 -0
- package/package.json +10 -7
- package/scripts/install-node.mjs +16 -3
- package/scripts/native-install-test.mjs +10 -3
- package/scripts/native-release.mjs +3 -3
- package/scripts/package-native-smoke.mjs +2 -2
- package/scripts/publish-workflow-test.mjs +4 -4
- package/src/plugin.js +19 -0
- package/src/v2.js +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.1 - 2026-09-29
|
|
4
|
+
|
|
5
|
+
- Expose the root/server and `./v2` npm entrypoints through the official `@opencode/plugin` `Plugin.define({ id, setup })` contract while keeping V1 `server()` as a separate compatibility implementation.
|
|
6
|
+
- Ship `@opencode/plugin@^2.0.18` as a production dependency and lock its non-optional runtime peer closure for reproducible clean installs.
|
|
7
|
+
- Make native local-file installs use the same real V2 entrypoint: `plugins/opencode-loop/index.js` is the `Plugin.define()` wrapper and `native.js` is only its supporting generated runtime.
|
|
8
|
+
- Preserve configured package options while migrating `plugin` to `plugins`; remove old single-file local copies during native upgrades and uninstall.
|
|
9
|
+
- Extend Windows production-tarball smoke time only at the harness boundary so the larger V2 dependency graph can install without weakening runtime assertions.
|
|
10
|
+
- Pair trusted publication with OpenCode Goal 1.3.40 from its exact immutable source and re-run the installed npm pair on OpenCode 2.0.18.
|
|
11
|
+
|
|
3
12
|
## 0.6.0 - 2026-09-29
|
|
4
13
|
|
|
5
14
|
- Fix #164: persisted core user-message events cannot abort compaction; trusted admission hooks alone may steer legacy turns.
|
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# OpenCode Loop
|
|
2
2
|
|
|
3
|
-
## Native OpenCode 2 (0.6.
|
|
3
|
+
## Native OpenCode 2 (0.6.1)
|
|
4
|
+
|
|
5
|
+
The npm and local-file V2 entrypoints now use the official `@opencode/plugin` `Plugin.define()` contract. The local installer writes a discovered plugin package directory with the generated runtime as a supporting module, while explicit `--legacy-v1` remains a separate compatibility path.
|
|
4
6
|
|
|
5
7
|
The installer now defaults to the standalone native V2 plugin. It needs no V1 SDK or legacy command files for a local installation. Existing npm registrations migrate from `plugin` to `plugins` without dropping object options. Use `--legacy-v1` explicitly for the compatibility installation.
|
|
6
8
|
|
|
@@ -15,7 +17,7 @@ Prompt/command timers, watch and stop conditions, verification, preflight/postru
|
|
|
15
17
|
|
|
16
18
|
OpenCode Loop adds `/loop`, scheduled prompt/command/shell jobs, compact scheduling, verification/checkpoints, and the `opencode-loopd` background daemon.
|
|
17
19
|
|
|
18
|
-
> **Current stable release: `0.
|
|
20
|
+
> **Current stable release: `0.6.1`.**
|
|
19
21
|
|
|
20
22
|
## Install or update
|
|
21
23
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# OpenCode Loop 0.6.1
|
|
2
|
+
|
|
3
|
+
Official OpenCode 2 plugin-definition release, paired with OpenCode Goal 1.3.40.
|
|
4
|
+
|
|
5
|
+
## V2 package and local entrypoints
|
|
6
|
+
|
|
7
|
+
The npm root/server entry and the explicit `./v2` entry now use
|
|
8
|
+
`@opencode/plugin` `Plugin.define({ id, setup })`. V1 `server()` compatibility
|
|
9
|
+
remains a separate implementation rather than an adapter that translates V1 hooks.
|
|
10
|
+
|
|
11
|
+
A native local install uses the same contract. It creates
|
|
12
|
+
`plugins/opencode-loop/index.js` as the real V2 entrypoint and keeps the generated
|
|
13
|
+
single-file runtime beside it as `native.js`, where it is a support module rather
|
|
14
|
+
than a second auto-discovered plugin. Upgrades remove the old root
|
|
15
|
+
`opencode-loop.ts`/JS copies; explicit `--legacy-v1` still installs the V1 path.
|
|
16
|
+
|
|
17
|
+
## Runtime and packaging
|
|
18
|
+
|
|
19
|
+
`@opencode/plugin@^2.0.18` is a production dependency and its required runtime
|
|
20
|
+
peer closure is locked for reproducible npm installs. Clean Windows package smoke
|
|
21
|
+
keeps all previous assertions but permits six minutes for the larger V2 dependency
|
|
22
|
+
graph to install.
|
|
23
|
+
|
|
24
|
+
The native prompt, command, scheduling, session-location, process-tree, Goal
|
|
25
|
+
ownership, compaction-safety and `--until` control-state protections from 0.6.0
|
|
26
|
+
remain unchanged.
|
|
27
|
+
|
|
28
|
+
## Paired Goal release
|
|
29
|
+
|
|
30
|
+
Goal 1.3.40 defines its server, native-only and TUI entries with the official V2
|
|
31
|
+
plugin APIs while preserving separate V1 compatibility. Loop publication is pinned
|
|
32
|
+
to Goal's immutable 1.3.40 source and refuses publication until that exact package
|
|
33
|
+
is visible on npm.
|
|
34
|
+
|
|
35
|
+
## Publication and verification
|
|
36
|
+
|
|
37
|
+
The main-only OIDC publisher requires Windows/Linux installed-package gates plus
|
|
38
|
+
normal exact-main CI and Native V2 CI. It builds the declared Goal source, installs
|
|
39
|
+
the actual Loop and Goal tarballs together, and exercises them on OpenCode 2.0.18.
|
|
40
|
+
|
|
41
|
+
After publication, registry latest, gitHead, exports, bins and integrity are checked.
|
|
42
|
+
A fresh npm consumer then installs Loop 0.6.1 and Goal 1.3.40 and re-runs joint
|
|
43
|
+
installation/options/uninstall, real native commands and the two-turn
|
|
44
|
+
single-session daemon before the GitHub v0.6.1 tag/release is created.
|
|
45
|
+
|
|
46
|
+
## Capability boundary
|
|
47
|
+
|
|
48
|
+
OpenCode 2.0.18's published plugin context still does not expose
|
|
49
|
+
`session.compact`; scheduled `/loop-compact` therefore rejects before durable job
|
|
50
|
+
creation while automatic compaction stays host-owned. Controlled-provider canaries
|
|
51
|
+
are functional verification, not live-model cost or endurance measurements.
|
|
52
|
+
|
|
53
|
+
Install the verified pair after registry publication:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npx -y @bybrawe/opencode-goal@1.3.40
|
|
57
|
+
npx -y @bybrawe/opencode-loop@0.6.1 --loop-only --without-loop-goals
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Fully restart OpenCode after installation. Existing project state is preserved.
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bybrawe/opencode-loop",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Claude Code/Codex style /loop and experimental goal mode for OpenCode: heartbeat scheduler, idle-safe loops, scheduled commands, compact scheduling, verification, checkpoints, and persistent coding goals.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "src/
|
|
6
|
+
"main": "src/plugin.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
".": "./src/
|
|
9
|
-
"./server": "./src/
|
|
10
|
-
"./v2": "./src/
|
|
8
|
+
".": "./src/plugin.js",
|
|
9
|
+
"./server": "./src/plugin.js",
|
|
10
|
+
"./v2": "./src/v2.js",
|
|
11
11
|
"./v1": "./src/index.js"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build:plugin": "bun build src/source/v1.js --outfile=src/index.js --target=bun --format=esm --external=@opencode-ai/plugin/tool && bun build src/source/server.js --outfile=src/server.js --target=bun --format=esm --external=@opencode-ai/plugin/tool && bun build src/source/native.js --outfile=src/native.js --target=bun --format=esm",
|
|
25
25
|
"build:plugin:npm": "npm run build:plugin",
|
|
26
|
-
"prepack": "node --check src/index.js && node --check src/server.js && node --check src/native.js",
|
|
27
|
-
"check": "node --check src/source/v1.js && node --check src/source/core/args.js && node --check src/source/core/continuation.js && node --check src/source/core/schedule-syntax.js && node --check src/source/core/state.js && node --check src/source/core/jobs.js && node --check src/source/core/process.js && node --check src/source/opencode/sdk.js && node --check src/source/opencode/session-context.js && node --check src/source/opencode/command-router.js && node --check src/source/opencode/goal-commands.js && node --check src/source/opencode/loop-commands.js && node --check src/source/opencode/loop-registration.js && node --check src/source/runtime/session-activity.js && node --check src/source/runtime/session-status.js && node --check src/source/runtime/schedule-policy.js && node --check src/source/runtime/scheduler-diagnostics.js && node --check src/source/runtime/companion-goal.js && node --check src/source/runtime/loop-diagnostics.js && node --check src/source/runtime/compaction.js && node --check src/source/runtime/action-dispatch.js && node --check src/source/runtime/network-recovery.js && node --check src/source/runtime/empty-turn.js && node --check src/source/runtime/terminal-guard.js && node --check src/source/runtime/run-finalization.js && node --check src/source/runtime/run-admission.js && node --check src/source/runtime/executor.js && node --check src/source/runtime/scheduler.js && node --check src/source/runtime/goal-prompt.js && node --check src/source/runtime/goal-report.js && node --check src/source/runtime/goal-evidence.js && node --check src/source/runtime/goal-runtime.js && node --check src/source/runtime/goal-policy.js && node --check src/source/runtime/goal-steering.js && node --check src/source/runtime/job-workspace.js && node --check src/source/opencode2/prompt-runtime.js && node --check src/source/opencode2/diagnostics.js && node --check src/source/opencode2/logging.js && node --check src/source/server.js && node --check src/source/legacy-v1.js && node --check src/index.js && node --check src/server.js && node --check scripts/install-node.mjs && node --check scripts/install-with-goals.mjs && node --check scripts/loopd.mjs && node --check scripts/pulse-check.mjs && node --check scripts/install-test.mjs && node --check scripts/goal-companion-test.mjs && node --check scripts/loopd-test.mjs && node --check scripts/smoke-test.mjs && node --check scripts/host-adapter-contract-test.mjs && node --check scripts/server-entry-test.mjs && node --check scripts/command-router-test.mjs && node --check scripts/goal-command-handlers-test.mjs && node --check scripts/loop-command-handlers-test.mjs && node --check scripts/loop-registration-test.mjs && node --check scripts/schedule-syntax-test.mjs && node --check scripts/schedule-policy-test.mjs && node --check scripts/session-activity-test.mjs && node --check scripts/session-status-test.mjs && node --check scripts/session-status-idle-recovery-test.mjs && node --check scripts/session-error-recovery-test.mjs && node --check scripts/empty-turn-recovery-test.mjs && node --check scripts/network-recovery-test.mjs && node --check scripts/compaction-runtime-test.mjs && node --check scripts/executor-runtime-test.mjs && node --check scripts/scheduler-runtime-test.mjs && node --check scripts/goal-runtime-test.mjs && node --check scripts/goal-policy-test.mjs && node --check scripts/goal-steering-test.mjs && node --check scripts/job-workspace-test.mjs && node --check scripts/v2-prompt-runtime-test.mjs && node --check scripts/v2-prompt-interval-test.mjs && node --check scripts/v2-command-runtime-test.mjs && node --check scripts/v2-command-adapter-test.mjs && node --check scripts/v2-diagnostics-test.mjs && node --check scripts/v2-logging-test.mjs && node --check scripts/comprehensive-watchdog.mjs && node --check scripts/comprehensive-test.mjs && node --check scripts/host-loop-canary.mjs && node --check scripts/host-goal-steering-canary.mjs && node --check scripts/publish-workflow-test.mjs && node --check scripts/goal-compaction-safety-test.mjs && node --check src/source/native.js && node --check src/source/opencode2/native-plugin.js && node --check src/source/opencode2/native-runtime.js && node --check src/source/opencode2/native-policy.js && node --check src/native.js && node --check scripts/native-entry-test.mjs && node --check scripts/package-native-smoke.mjs && node --check src/v1.js && node --check scripts/native-cli-io-test.mjs && node --check scripts/native-lifecycle-test.mjs && node --check scripts/native-plugin-lifetime-test.mjs && node --check src/source/core/progress.js && node --check scripts/native-presets-test.mjs && node --check scripts/control-plane-until-test.mjs && node --check src/source/opencode2/session-location.js && node --check scripts/native-session-location-test.mjs",
|
|
26
|
+
"prepack": "node --check src/plugin.js && node --check src/v2.js && node --check src/index.js && node --check src/server.js && node --check src/native.js",
|
|
27
|
+
"check": "node --check src/source/v1.js && node --check src/source/core/args.js && node --check src/source/core/continuation.js && node --check src/source/core/schedule-syntax.js && node --check src/source/core/state.js && node --check src/source/core/jobs.js && node --check src/source/core/process.js && node --check src/source/opencode/sdk.js && node --check src/source/opencode/session-context.js && node --check src/source/opencode/command-router.js && node --check src/source/opencode/goal-commands.js && node --check src/source/opencode/loop-commands.js && node --check src/source/opencode/loop-registration.js && node --check src/source/runtime/session-activity.js && node --check src/source/runtime/session-status.js && node --check src/source/runtime/schedule-policy.js && node --check src/source/runtime/scheduler-diagnostics.js && node --check src/source/runtime/companion-goal.js && node --check src/source/runtime/loop-diagnostics.js && node --check src/source/runtime/compaction.js && node --check src/source/runtime/action-dispatch.js && node --check src/source/runtime/network-recovery.js && node --check src/source/runtime/empty-turn.js && node --check src/source/runtime/terminal-guard.js && node --check src/source/runtime/run-finalization.js && node --check src/source/runtime/run-admission.js && node --check src/source/runtime/executor.js && node --check src/source/runtime/scheduler.js && node --check src/source/runtime/goal-prompt.js && node --check src/source/runtime/goal-report.js && node --check src/source/runtime/goal-evidence.js && node --check src/source/runtime/goal-runtime.js && node --check src/source/runtime/goal-policy.js && node --check src/source/runtime/goal-steering.js && node --check src/source/runtime/job-workspace.js && node --check src/source/opencode2/prompt-runtime.js && node --check src/source/opencode2/diagnostics.js && node --check src/source/opencode2/logging.js && node --check src/source/server.js && node --check src/source/legacy-v1.js && node --check src/index.js && node --check src/server.js && node --check scripts/install-node.mjs && node --check scripts/install-with-goals.mjs && node --check scripts/loopd.mjs && node --check scripts/pulse-check.mjs && node --check scripts/install-test.mjs && node --check scripts/goal-companion-test.mjs && node --check scripts/loopd-test.mjs && node --check scripts/smoke-test.mjs && node --check scripts/host-adapter-contract-test.mjs && node --check scripts/server-entry-test.mjs && node --check scripts/command-router-test.mjs && node --check scripts/goal-command-handlers-test.mjs && node --check scripts/loop-command-handlers-test.mjs && node --check scripts/loop-registration-test.mjs && node --check scripts/schedule-syntax-test.mjs && node --check scripts/schedule-policy-test.mjs && node --check scripts/session-activity-test.mjs && node --check scripts/session-status-test.mjs && node --check scripts/session-status-idle-recovery-test.mjs && node --check scripts/session-error-recovery-test.mjs && node --check scripts/empty-turn-recovery-test.mjs && node --check scripts/network-recovery-test.mjs && node --check scripts/compaction-runtime-test.mjs && node --check scripts/executor-runtime-test.mjs && node --check scripts/scheduler-runtime-test.mjs && node --check scripts/goal-runtime-test.mjs && node --check scripts/goal-policy-test.mjs && node --check scripts/goal-steering-test.mjs && node --check scripts/job-workspace-test.mjs && node --check scripts/v2-prompt-runtime-test.mjs && node --check scripts/v2-prompt-interval-test.mjs && node --check scripts/v2-command-runtime-test.mjs && node --check scripts/v2-command-adapter-test.mjs && node --check scripts/v2-diagnostics-test.mjs && node --check scripts/v2-logging-test.mjs && node --check scripts/comprehensive-watchdog.mjs && node --check scripts/comprehensive-test.mjs && node --check scripts/host-loop-canary.mjs && node --check scripts/host-goal-steering-canary.mjs && node --check scripts/publish-workflow-test.mjs && node --check scripts/goal-compaction-safety-test.mjs && node --check src/source/native.js && node --check src/source/opencode2/native-plugin.js && node --check src/source/opencode2/native-runtime.js && node --check src/source/opencode2/native-policy.js && node --check src/native.js && node --check scripts/native-entry-test.mjs && node --check scripts/package-native-smoke.mjs && node --check src/v1.js && node --check scripts/native-cli-io-test.mjs && node --check scripts/native-lifecycle-test.mjs && node --check scripts/native-plugin-lifetime-test.mjs && node --check src/source/core/progress.js && node --check scripts/native-presets-test.mjs && node --check scripts/control-plane-until-test.mjs && node --check src/source/opencode2/session-location.js && node --check scripts/native-session-location-test.mjs && node --check src/plugin.js && node --check src/v2.js",
|
|
28
28
|
"test": "node --test scripts/native-session-location-test.mjs && node --test scripts/control-plane-until-test.mjs && node --test scripts/native-presets-test.mjs && node --test scripts/native-plugin-lifetime-test.mjs && node --test scripts/native-cli-io-test.mjs scripts/native-lifecycle-test.mjs && node scripts/native-entry-test.mjs && node scripts/native-coexistence-test.mjs && node scripts/native-shell-test.mjs && node scripts/native-install-test.mjs && node scripts/native-v2-test.mjs && node scripts/goal-compaction-safety-test.mjs && node scripts/publish-workflow-test.mjs && node scripts/server-entry-test.mjs && node scripts/command-router-test.mjs && node scripts/goal-command-handlers-test.mjs && node scripts/loop-command-handlers-test.mjs && node scripts/loop-registration-test.mjs && node scripts/schedule-syntax-test.mjs && node scripts/schedule-policy-test.mjs && node scripts/session-activity-test.mjs && node scripts/session-status-test.mjs && node scripts/session-status-idle-recovery-test.mjs && node scripts/session-error-recovery-test.mjs && node scripts/empty-turn-recovery-test.mjs && node scripts/network-recovery-test.mjs && node scripts/compaction-runtime-test.mjs && node scripts/action-dispatch-test.mjs && node scripts/run-finalization-test.mjs && node scripts/run-admission-test.mjs && node scripts/executor-runtime-test.mjs && node scripts/scheduler-runtime-test.mjs && node scripts/goal-runtime-test.mjs && node scripts/goal-policy-test.mjs && node scripts/goal-steering-test.mjs && node scripts/job-workspace-test.mjs && node scripts/v2-prompt-runtime-test.mjs && node scripts/v2-prompt-interval-test.mjs && node scripts/v2-command-runtime-test.mjs && node scripts/v2-command-adapter-test.mjs && node scripts/v2-diagnostics-test.mjs && node scripts/v2-logging-test.mjs && node scripts/install-test.mjs && node scripts/goal-companion-test.mjs && node scripts/loopd-test.mjs && node scripts/smoke-test.mjs && node scripts/host-adapter-contract-test.mjs && node scripts/comprehensive-watchdog.mjs",
|
|
29
29
|
"canary:host": "node scripts/host-loop-canary.mjs && node scripts/host-goal-steering-canary.mjs",
|
|
30
30
|
"install:global": "node scripts/install-with-goals.mjs",
|
|
@@ -73,5 +73,8 @@
|
|
|
73
73
|
},
|
|
74
74
|
"publishConfig": {
|
|
75
75
|
"access": "public"
|
|
76
|
+
},
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"@opencode/plugin": "^2.0.18"
|
|
76
79
|
}
|
|
77
80
|
}
|
package/scripts/install-node.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from "node:url"
|
|
|
8
8
|
const root = dirname(dirname(fileURLToPath(import.meta.url)))
|
|
9
9
|
const config = process.env.OPENCODE_CONFIG_DIR || join(homedir(), ".config", "opencode")
|
|
10
10
|
const pluginDir = join(config, "plugins")
|
|
11
|
+
const nativePluginDir = join(pluginDir, "opencode-loop")
|
|
11
12
|
const commandDir = join(config, "commands")
|
|
12
13
|
const agentDir = join(config, "agents")
|
|
13
14
|
const packagePath = join(config, "package.json")
|
|
@@ -343,6 +344,7 @@ async function uninstall() {
|
|
|
343
344
|
for (const plan of plans) if (plan.updated !== plan.source) await writeFile(plan.target, plan.updated, "utf8")
|
|
344
345
|
await rm(join(pluginDir, "opencode-loop.ts"), { force: true })
|
|
345
346
|
await rm(join(pluginDir, "opencode-loop.js"), { force: true })
|
|
347
|
+
await rm(nativePluginDir, { recursive: true, force: true })
|
|
346
348
|
await removePackagedFiles(join(root, "commands"), commandDir)
|
|
347
349
|
await removePackagedFiles(join(root, "agents"), agentDir)
|
|
348
350
|
const changed = plans.filter((plan) => plan.source !== plan.updated).length
|
|
@@ -358,10 +360,21 @@ async function installOrUpdate() {
|
|
|
358
360
|
if (packageConfig.configured) {
|
|
359
361
|
await rm(join(pluginDir, "opencode-loop.ts"), { force: true })
|
|
360
362
|
await rm(join(pluginDir, "opencode-loop.js"), { force: true })
|
|
363
|
+
await rm(nativePluginDir, { recursive: true, force: true })
|
|
364
|
+
} else if (legacyV1) {
|
|
365
|
+
await ensureDependency()
|
|
366
|
+
await rm(nativePluginDir, { recursive: true, force: true })
|
|
367
|
+
await copyFile(join(root, "src", "index.js"), join(pluginDir, "opencode-loop.ts"))
|
|
368
|
+
await rm(join(pluginDir, "opencode-loop.js"), { force: true })
|
|
361
369
|
} else {
|
|
362
|
-
|
|
363
|
-
|
|
370
|
+
// V2 local plugins use the same Plugin.define entrypoint as the npm package.
|
|
371
|
+
// Keep the generated native runtime beside it as a supporting module instead
|
|
372
|
+
// of auto-discovering that implementation object as a second plugin.
|
|
373
|
+
await rm(join(pluginDir, "opencode-loop.ts"), { force: true })
|
|
364
374
|
await rm(join(pluginDir, "opencode-loop.js"), { force: true })
|
|
375
|
+
await mkdir(nativePluginDir, { recursive: true })
|
|
376
|
+
await copyFile(join(root, "src", "v2.js"), join(nativePluginDir, "index.js"))
|
|
377
|
+
await copyFile(join(root, "src", "native.js"), join(nativePluginDir, "native.js"))
|
|
365
378
|
}
|
|
366
379
|
if (legacyV1) {
|
|
367
380
|
await mkdir(commandDir, { recursive: true })
|
|
@@ -375,7 +388,7 @@ async function installOrUpdate() {
|
|
|
375
388
|
if (packageConfig.configured) {
|
|
376
389
|
const pinResult = packageConfig.updatedFiles.length ? `pinned the config entry to ${packageSpec}` : `the config entry is already pinned to ${packageSpec}`
|
|
377
390
|
console.log(`OpenCode Loop is already configured as a package in ${config}; ${pinResult} and removed the duplicate local plugin copy.`)
|
|
378
|
-
} else console.log(`Installed OpenCode Loop plugin to ${
|
|
391
|
+
} else console.log(`Installed OpenCode Loop plugin to ${legacyV1 ? join(pluginDir, "opencode-loop.ts") : nativePluginDir}`)
|
|
379
392
|
if (legacyV1) {
|
|
380
393
|
console.log(`Installed ${packageName} commands to ${commandDir}`)
|
|
381
394
|
console.log(`Installed ${packageName} local command agent to ${agentDir}`)
|
|
@@ -27,8 +27,13 @@ try {
|
|
|
27
27
|
const fresh = await config("fresh")
|
|
28
28
|
run(fresh)
|
|
29
29
|
const native = await readFile(path.join(root, "src/native.js"), "utf8")
|
|
30
|
-
|
|
30
|
+
const v2 = await readFile(path.join(root, "src/v2.js"), "utf8")
|
|
31
|
+
assert.equal(await readFile(path.join(fresh, "plugins/opencode-loop/index.js"), "utf8"), v2)
|
|
32
|
+
assert.equal(await readFile(path.join(fresh, "plugins/opencode-loop/native.js"), "utf8"), native)
|
|
33
|
+
assert.match(v2, /from\s+["']@opencode\/plugin["']/)
|
|
34
|
+
assert.match(v2, /Plugin\.define\s*\(/)
|
|
31
35
|
assert.doesNotMatch(native, /(?:from\s*|import\s*\()["']@opencode-ai\/plugin/)
|
|
36
|
+
assert.equal(await exists(path.join(fresh, "plugins/opencode-loop.ts")), false)
|
|
32
37
|
assert.equal(await exists(path.join(fresh, "package.json")), false)
|
|
33
38
|
assert.equal(await exists(path.join(fresh, "commands/loop.md")), false)
|
|
34
39
|
assert.equal(await exists(path.join(fresh, "agents/opencode-loop-local.md")), false)
|
|
@@ -43,7 +48,7 @@ try {
|
|
|
43
48
|
const before = await readFile(path.join(obj, "opencode.json"), "utf8")
|
|
44
49
|
run(obj)
|
|
45
50
|
assert.equal(await readFile(path.join(obj, "opencode.json"), "utf8"), before)
|
|
46
|
-
assert.equal(await exists(path.join(obj, "plugins/opencode-loop
|
|
51
|
+
assert.equal(await exists(path.join(obj, "plugins/opencode-loop")), false)
|
|
47
52
|
cases++
|
|
48
53
|
|
|
49
54
|
const both = await config("both", { plugin: ["legacy-unrelated", "@bybrawe/opencode-loop@old"], plugins: [options, "native-unrelated"] })
|
|
@@ -83,7 +88,9 @@ try {
|
|
|
83
88
|
run(upgrade)
|
|
84
89
|
assert.equal(await exists(path.join(upgrade, "commands/loop.md")), false)
|
|
85
90
|
assert.equal(await readFile(path.join(upgrade, "commands/loop-help.md"), "utf8"), "This is a custom user command; keep it.")
|
|
86
|
-
assert.equal(await readFile(path.join(upgrade, "plugins/opencode-loop.
|
|
91
|
+
assert.equal(await readFile(path.join(upgrade, "plugins/opencode-loop/index.js"), "utf8"), v2)
|
|
92
|
+
assert.equal(await readFile(path.join(upgrade, "plugins/opencode-loop/native.js"), "utf8"), native)
|
|
93
|
+
assert.equal(await exists(path.join(upgrade, "plugins/opencode-loop.ts")), false)
|
|
87
94
|
cases++
|
|
88
95
|
|
|
89
96
|
await mkdir(path.join(obj, ".opencode/opencode-loop"), { recursive: true })
|
|
@@ -7,12 +7,12 @@ export function validateRelease(request, pkg, env) {
|
|
|
7
7
|
assert.equal(env.GITHUB_REPOSITORY, "ByBrawe/opencode-loop", "wrong release repository")
|
|
8
8
|
assert.equal(env.GITHUB_REF, "refs/heads/main", "npm release must come from main")
|
|
9
9
|
assert.match(env.GITHUB_SHA ?? "", /^[a-f0-9]{40}$/, "immutable release SHA required")
|
|
10
|
-
assert.equal(request?.version, "0.6.
|
|
10
|
+
assert.equal(request?.version, "0.6.1", "release version is not authorized")
|
|
11
11
|
assert.equal(request?.name, "@bybrawe/opencode-loop")
|
|
12
12
|
assert.equal(pkg.name, request.name)
|
|
13
13
|
assert.equal(pkg.version, request.version)
|
|
14
|
-
assert.equal(request.predecessor, "0.
|
|
15
|
-
assert.equal(request.goal?.version, "1.3.
|
|
14
|
+
assert.equal(request.predecessor, "0.6.0")
|
|
15
|
+
assert.equal(request.goal?.version, "1.3.40")
|
|
16
16
|
assert.match(request.goal?.sha ?? "", /^[a-f0-9]{40}$/)
|
|
17
17
|
return request
|
|
18
18
|
}
|
|
@@ -9,7 +9,7 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
|
9
9
|
const npmCLI = process.env.npm_execpath
|
|
10
10
|
assert.ok(npmCLI, "run this through npm run package:smoke")
|
|
11
11
|
function run(command, args, cwd) {
|
|
12
|
-
const result = spawnSync(command, args, { cwd, encoding: "utf8", timeout:
|
|
12
|
+
const result = spawnSync(command, args, { cwd, encoding: "utf8", timeout: 360_000, maxBuffer: 8 * 1024 * 1024, windowsHide: true })
|
|
13
13
|
if (result.error) throw result.error
|
|
14
14
|
assert.equal(result.status, 0, [result.stdout, result.stderr].filter(Boolean).join("\n"))
|
|
15
15
|
return result.stdout
|
|
@@ -19,7 +19,7 @@ try {
|
|
|
19
19
|
const packed = JSON.parse(run(process.execPath, [npmCLI, "pack", root, "--json", "--ignore-scripts"], temp))
|
|
20
20
|
assert.equal(packed.length, 1)
|
|
21
21
|
const files = new Set(packed[0].files.map((entry) => entry.path))
|
|
22
|
-
for (const entry of ["src/server.js", "src/native.js", "src/v1.js", "src/index.js"]) assert.ok(files.has(entry), `missing ${entry}`)
|
|
22
|
+
for (const entry of ["src/plugin.js", "src/v2.js", "src/server.js", "src/native.js", "src/v1.js", "src/index.js"]) assert.ok(files.has(entry), `missing ${entry}`)
|
|
23
23
|
const consumer = path.join(temp, "consumer with spaces")
|
|
24
24
|
await mkdir(consumer)
|
|
25
25
|
await writeFile(path.join(consumer, "package.json"), JSON.stringify({ private: true, type: "module" }))
|
|
@@ -41,15 +41,15 @@ const publisher = workflow.slice(workflow.indexOf(" publish:\n"), workflow.inde
|
|
|
41
41
|
assert.doesNotMatch(publisher, /contents:\s*write/, "OIDC publisher must not gain repository mutation privileges")
|
|
42
42
|
|
|
43
43
|
const sha = "a".repeat(40)
|
|
44
|
-
const request = { name: "@bybrawe/opencode-loop", version: "0.6.
|
|
44
|
+
const request = { name: "@bybrawe/opencode-loop", version: "0.6.1", predecessor: "0.6.0", goal: { version: "1.3.40", sha: "b".repeat(40) } }
|
|
45
45
|
const pkg = { name: request.name, version: request.version }
|
|
46
46
|
const env = { GITHUB_REPOSITORY: "ByBrawe/opencode-loop", GITHUB_REF: "refs/heads/main", GITHUB_SHA: sha }
|
|
47
47
|
assert.strictEqual(validateRelease(request, pkg, env), request)
|
|
48
48
|
for (const change of [{ GITHUB_REPOSITORY: "someone/fork" }, { GITHUB_REF: "refs/heads/release" }, { GITHUB_SHA: "main" }]) assert.throws(() => validateRelease(request, pkg, { ...env, ...change }))
|
|
49
49
|
assert.throws(() => validateRelease({ ...request, version: "9.0.0" }, pkg, env))
|
|
50
|
-
assert.throws(() => validateRelease(request, { ...pkg, version: "0.
|
|
51
|
-
assert.equal(releaseDecision(404, null, request, sha, "0.
|
|
52
|
-
for (const status of [401, 403, 429, 500, 503]) assert.throws(() => releaseDecision(status, null, request, sha, "0.
|
|
50
|
+
assert.throws(() => validateRelease(request, { ...pkg, version: "0.6.0" }, env))
|
|
51
|
+
assert.equal(releaseDecision(404, null, request, sha, "0.6.0"), true)
|
|
52
|
+
for (const status of [401, 403, 429, 500, 503]) assert.throws(() => releaseDecision(status, null, request, sha, "0.6.0"))
|
|
53
53
|
assert.throws(() => releaseDecision(404, null, request, sha, "0.7.0"))
|
|
54
54
|
const manifest = { ...pkg, gitHead: sha }
|
|
55
55
|
assert.equal(releaseDecision(200, manifest, request, sha), false)
|
package/src/plugin.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Plugin } from "@opencode/plugin"
|
|
2
|
+
import bundled from "./server.js"
|
|
3
|
+
|
|
4
|
+
export * from "./server.js"
|
|
5
|
+
|
|
6
|
+
const native = Plugin.define({
|
|
7
|
+
id: "@bybrawe/opencode-loop",
|
|
8
|
+
setup: (context) => bundled.setup(context),
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
export const OpenCodeLoopPluginModule = Object.freeze({
|
|
12
|
+
...native,
|
|
13
|
+
|
|
14
|
+
// OpenCode 1.x compatibility remains a separate implementation. V2 uses
|
|
15
|
+
// id/setup; V1 1.18.29+ uses server().
|
|
16
|
+
server: bundled.server,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export default OpenCodeLoopPluginModule
|
package/src/v2.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Plugin } from "@opencode/plugin"
|
|
2
|
+
import bundled from "./native.js"
|
|
3
|
+
|
|
4
|
+
const native = Plugin.define({
|
|
5
|
+
id: "@bybrawe/opencode-loop",
|
|
6
|
+
setup: (context) => bundled.setup(context),
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
export const OpenCodeLoopNativePlugin = native
|
|
10
|
+
export default native
|