@codemem/opencode-plugin 0.44.2 → 0.45.0-rc.2
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/.opencode/lib/delegation-context.js +202 -0
- package/.opencode/lib/host-contract.js +90 -0
- package/.opencode/lib/opencode-v2-adapter.js +762 -0
- package/.opencode/lib/runtime.js +5381 -0
- package/.opencode/lib/v2-notification-bridge.js +105 -0
- package/.opencode/package.json +1 -1
- package/.opencode/plugins/codemem.js +141 -4677
- package/README.md +47 -3
- package/index.d.ts +19 -0
- package/index.js +11 -2
- package/package.json +28 -5
- package/rpc.d.ts +12 -0
- package/rpc.js +32 -0
- package/src/opencode-v2-contract-fixture.ts +367 -0
- package/tui.d.ts +16 -0
- package/tui.js +105 -0
- package/v2-contract-fixture/index.ts +1 -0
- package/v2-contract-fixture/package.json +8 -0
- package/.opencode/plugins/runtime.js +0 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { CodememNotifications } from "../../rpc.js";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_MAX_NOTICES = 32;
|
|
5
|
+
const DEFAULT_REGISTRATION_TIMEOUT_MS = 250;
|
|
6
|
+
|
|
7
|
+
const resolvePositiveInteger = (value, fallback) =>
|
|
8
|
+
Number.isSafeInteger(value) && value > 0 ? value : fallback;
|
|
9
|
+
|
|
10
|
+
const disposeLateRegistration = (registrationTask) => {
|
|
11
|
+
void registrationTask
|
|
12
|
+
.then((registration) => registration.dispose())
|
|
13
|
+
.catch(() => {
|
|
14
|
+
// Timed-out advisory registration cleanup is best-effort.
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const registerWithinTimeout = async (context, handlers, timeoutMs) => {
|
|
19
|
+
const registrationTask = Promise.resolve().then(() =>
|
|
20
|
+
context.rpc.register(CodememNotifications, handlers)
|
|
21
|
+
);
|
|
22
|
+
let timeout;
|
|
23
|
+
const timeoutTask = new Promise((resolve) => {
|
|
24
|
+
timeout = setTimeout(() => resolve({ status: "timeout" }), timeoutMs);
|
|
25
|
+
timeout.unref?.();
|
|
26
|
+
});
|
|
27
|
+
const result = await Promise.race([
|
|
28
|
+
registrationTask.then(
|
|
29
|
+
(registration) => ({ status: "registered", registration }),
|
|
30
|
+
() => ({ status: "failed" })
|
|
31
|
+
),
|
|
32
|
+
timeoutTask,
|
|
33
|
+
]);
|
|
34
|
+
clearTimeout(timeout);
|
|
35
|
+
if (result.status === "timeout") disposeLateRegistration(registrationTask);
|
|
36
|
+
return result.status === "registered" ? result.registration : null;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const createNotificationBacklog = ({
|
|
40
|
+
maxNotices = DEFAULT_MAX_NOTICES,
|
|
41
|
+
createID = randomUUID,
|
|
42
|
+
} = {}) => {
|
|
43
|
+
const notices = [];
|
|
44
|
+
const limit = Number.isSafeInteger(maxNotices) && maxNotices > 0 ? maxNotices : DEFAULT_MAX_NOTICES;
|
|
45
|
+
let buffering = true;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
push({ message, variant }) {
|
|
49
|
+
const notice = { id: createID(), message, variant };
|
|
50
|
+
if (!buffering) return notice;
|
|
51
|
+
notices.push(notice);
|
|
52
|
+
while (notices.length > limit) notices.shift();
|
|
53
|
+
return notice;
|
|
54
|
+
},
|
|
55
|
+
drain() {
|
|
56
|
+
buffering = false;
|
|
57
|
+
return notices.splice(0, notices.length);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const registerV2NotificationBridge = async (context, options = {}) => {
|
|
63
|
+
if (typeof context?.rpc?.register !== "function") {
|
|
64
|
+
return { notify: null, registration: null };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const backlog = createNotificationBacklog(options);
|
|
68
|
+
const registrationTimeoutMs = resolvePositiveInteger(
|
|
69
|
+
options.registrationTimeoutMs,
|
|
70
|
+
DEFAULT_REGISTRATION_TIMEOUT_MS
|
|
71
|
+
);
|
|
72
|
+
const registration = await registerWithinTimeout(
|
|
73
|
+
context,
|
|
74
|
+
{ drain: async () => ({ notices: backlog.drain() }) },
|
|
75
|
+
registrationTimeoutMs
|
|
76
|
+
);
|
|
77
|
+
if (!registration) {
|
|
78
|
+
return { notify: null, registration: null };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let active = true;
|
|
82
|
+
return {
|
|
83
|
+
notify: async (input) => {
|
|
84
|
+
if (!active) return;
|
|
85
|
+
const notice = backlog.push(input);
|
|
86
|
+
try {
|
|
87
|
+
void Promise.resolve(registration.events.emit("notice", notice)).catch(() => {
|
|
88
|
+
// Replay remains available when live publication fails.
|
|
89
|
+
});
|
|
90
|
+
} catch {
|
|
91
|
+
// Replay remains available when live publication throws synchronously.
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
registration: {
|
|
95
|
+
async dispose() {
|
|
96
|
+
active = false;
|
|
97
|
+
try {
|
|
98
|
+
await registration.dispose();
|
|
99
|
+
} catch {
|
|
100
|
+
// Notification cleanup must not fail the server plugin lifecycle.
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
};
|