opencode-heartbeat-approval 0.3.0 → 0.3.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/dist/index.js +18 -17
- package/dist/plugin.js +18 -17
- package/package.json +1 -1
- package/src/plugin.ts +18 -21
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ var __export = (target, all) => {
|
|
|
13
13
|
// src/plugin.ts
|
|
14
14
|
import { readFile } from "fs/promises";
|
|
15
15
|
import { join } from "path";
|
|
16
|
+
import { homedir } from "os";
|
|
16
17
|
|
|
17
18
|
// node_modules/zod/v4/classic/external.js
|
|
18
19
|
var exports_external = {};
|
|
@@ -12337,23 +12338,17 @@ tool.schema = exports_external;
|
|
|
12337
12338
|
// src/plugin.ts
|
|
12338
12339
|
var POLL_INTERVAL_MS = 1e4;
|
|
12339
12340
|
var JSON_HEADERS = { "Content-Type": "application/json" };
|
|
12340
|
-
|
|
12341
|
+
var SESSION_MAP_PATH = join(homedir(), ".config", "opencode", "heartbeat", "session-map.json");
|
|
12342
|
+
async function lookupSessionMapping(sessionId) {
|
|
12341
12343
|
try {
|
|
12342
|
-
const
|
|
12343
|
-
const
|
|
12344
|
-
const
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
if (typeof port === "number" && Number.isInteger(port) && port > 0 && port < 65536) {
|
|
12348
|
-
return `http://127.0.0.1:${port}`;
|
|
12349
|
-
}
|
|
12350
|
-
console.warn(`[heartbeat-approval] heartbeat.json found but web.port invalid: ${JSON.stringify(port)} in ${directory}`);
|
|
12351
|
-
return null;
|
|
12352
|
-
} catch (err) {
|
|
12353
|
-
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
12354
|
-
return null;
|
|
12344
|
+
const text = await readFile(SESSION_MAP_PATH, "utf-8");
|
|
12345
|
+
const mapping = JSON.parse(text);
|
|
12346
|
+
const entry = mapping[sessionId];
|
|
12347
|
+
if (entry && typeof entry.port === "number" && entry.port > 0 && entry.port < 65536) {
|
|
12348
|
+
return `http://127.0.0.1:${entry.port}`;
|
|
12355
12349
|
}
|
|
12356
|
-
|
|
12350
|
+
return null;
|
|
12351
|
+
} catch {
|
|
12357
12352
|
return null;
|
|
12358
12353
|
}
|
|
12359
12354
|
}
|
|
@@ -12425,12 +12420,18 @@ Returns: { status: "approved" | "rejected" | "expired" | "unavailable" | "cancel
|
|
|
12425
12420
|
});
|
|
12426
12421
|
}
|
|
12427
12422
|
const requestType = args.type === "assistance" ? "assistance" : "approval";
|
|
12428
|
-
const
|
|
12423
|
+
const envPort = process.env.APPROVAL_PORT;
|
|
12424
|
+
let resolvedUrl = null;
|
|
12425
|
+
if (envPort && /^\d+$/.test(envPort)) {
|
|
12426
|
+
resolvedUrl = `http://127.0.0.1:${envPort}`;
|
|
12427
|
+
} else {
|
|
12428
|
+
resolvedUrl = await lookupSessionMapping(ctx.sessionID);
|
|
12429
|
+
}
|
|
12429
12430
|
if (!resolvedUrl) {
|
|
12430
12431
|
return JSON.stringify({
|
|
12431
12432
|
status: "unavailable",
|
|
12432
12433
|
type: requestType,
|
|
12433
|
-
error: `
|
|
12434
|
+
error: `No approval server found for session ${ctx.sessionID}. Ensure heartbeat runner is running for this role.`,
|
|
12434
12435
|
approval_id: null
|
|
12435
12436
|
});
|
|
12436
12437
|
}
|
package/dist/plugin.js
CHANGED
|
@@ -13,6 +13,7 @@ var __export = (target, all) => {
|
|
|
13
13
|
// src/plugin.ts
|
|
14
14
|
import { readFile } from "fs/promises";
|
|
15
15
|
import { join } from "path";
|
|
16
|
+
import { homedir } from "os";
|
|
16
17
|
|
|
17
18
|
// node_modules/zod/v4/classic/external.js
|
|
18
19
|
var exports_external = {};
|
|
@@ -12337,23 +12338,17 @@ tool.schema = exports_external;
|
|
|
12337
12338
|
// src/plugin.ts
|
|
12338
12339
|
var POLL_INTERVAL_MS = 1e4;
|
|
12339
12340
|
var JSON_HEADERS = { "Content-Type": "application/json" };
|
|
12340
|
-
|
|
12341
|
+
var SESSION_MAP_PATH = join(homedir(), ".config", "opencode", "heartbeat", "session-map.json");
|
|
12342
|
+
async function lookupSessionMapping(sessionId) {
|
|
12341
12343
|
try {
|
|
12342
|
-
const
|
|
12343
|
-
const
|
|
12344
|
-
const
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
if (typeof port === "number" && Number.isInteger(port) && port > 0 && port < 65536) {
|
|
12348
|
-
return `http://127.0.0.1:${port}`;
|
|
12349
|
-
}
|
|
12350
|
-
console.warn(`[heartbeat-approval] heartbeat.json found but web.port invalid: ${JSON.stringify(port)} in ${directory}`);
|
|
12351
|
-
return null;
|
|
12352
|
-
} catch (err) {
|
|
12353
|
-
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
12354
|
-
return null;
|
|
12344
|
+
const text = await readFile(SESSION_MAP_PATH, "utf-8");
|
|
12345
|
+
const mapping = JSON.parse(text);
|
|
12346
|
+
const entry = mapping[sessionId];
|
|
12347
|
+
if (entry && typeof entry.port === "number" && entry.port > 0 && entry.port < 65536) {
|
|
12348
|
+
return `http://127.0.0.1:${entry.port}`;
|
|
12355
12349
|
}
|
|
12356
|
-
|
|
12350
|
+
return null;
|
|
12351
|
+
} catch {
|
|
12357
12352
|
return null;
|
|
12358
12353
|
}
|
|
12359
12354
|
}
|
|
@@ -12425,12 +12420,18 @@ Returns: { status: "approved" | "rejected" | "expired" | "unavailable" | "cancel
|
|
|
12425
12420
|
});
|
|
12426
12421
|
}
|
|
12427
12422
|
const requestType = args.type === "assistance" ? "assistance" : "approval";
|
|
12428
|
-
const
|
|
12423
|
+
const envPort = process.env.APPROVAL_PORT;
|
|
12424
|
+
let resolvedUrl = null;
|
|
12425
|
+
if (envPort && /^\d+$/.test(envPort)) {
|
|
12426
|
+
resolvedUrl = `http://127.0.0.1:${envPort}`;
|
|
12427
|
+
} else {
|
|
12428
|
+
resolvedUrl = await lookupSessionMapping(ctx.sessionID);
|
|
12429
|
+
}
|
|
12429
12430
|
if (!resolvedUrl) {
|
|
12430
12431
|
return JSON.stringify({
|
|
12431
12432
|
status: "unavailable",
|
|
12432
12433
|
type: requestType,
|
|
12433
|
-
error: `
|
|
12434
|
+
error: `No approval server found for session ${ctx.sessionID}. Ensure heartbeat runner is running for this role.`,
|
|
12434
12435
|
approval_id: null
|
|
12435
12436
|
});
|
|
12436
12437
|
}
|
package/package.json
CHANGED
package/src/plugin.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
3
4
|
import { tool } from "@opencode-ai/plugin";
|
|
4
5
|
import type { PluginInput } from "@opencode-ai/plugin";
|
|
5
6
|
|
|
@@ -26,28 +27,18 @@ interface ConflictResponse {
|
|
|
26
27
|
existing_approval_id: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* Returns null if not found (ENOENT is expected — not a heartbeat dir).
|
|
33
|
-
*/
|
|
34
|
-
async function discoverRunnerUrl(directory: string): Promise<string | null> {
|
|
30
|
+
const SESSION_MAP_PATH = join(homedir(), ".config", "opencode", "heartbeat", "session-map.json");
|
|
31
|
+
|
|
32
|
+
async function lookupSessionMapping(sessionId: string): Promise<string | null> {
|
|
35
33
|
try {
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (typeof port === "number" && Number.isInteger(port) && port > 0 && port < 65536) {
|
|
42
|
-
return `http://127.0.0.1:${port}`;
|
|
34
|
+
const text = await readFile(SESSION_MAP_PATH, "utf-8");
|
|
35
|
+
const mapping = JSON.parse(text) as Record<string, { port: number; role: string; created_at: string }>;
|
|
36
|
+
const entry = mapping[sessionId];
|
|
37
|
+
if (entry && typeof entry.port === "number" && entry.port > 0 && entry.port < 65536) {
|
|
38
|
+
return `http://127.0.0.1:${entry.port}`;
|
|
43
39
|
}
|
|
44
|
-
console.warn(`[heartbeat-approval] heartbeat.json found but web.port invalid: ${JSON.stringify(port)} in ${directory}`);
|
|
45
40
|
return null;
|
|
46
|
-
} catch
|
|
47
|
-
if (err && typeof err === "object" && "code" in err && (err as { code: string }).code === "ENOENT") {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
console.warn(`[heartbeat-approval] Failed to read heartbeat.json in ${directory}:`, err);
|
|
41
|
+
} catch {
|
|
51
42
|
return null;
|
|
52
43
|
}
|
|
53
44
|
}
|
|
@@ -137,12 +128,18 @@ Returns: { status: "approved" | "rejected" | "expired" | "unavailable" | "cancel
|
|
|
137
128
|
}
|
|
138
129
|
const requestType = (args.type === "assistance" ? "assistance" : "approval") as "approval" | "assistance";
|
|
139
130
|
|
|
140
|
-
const
|
|
131
|
+
const envPort = process.env.APPROVAL_PORT;
|
|
132
|
+
let resolvedUrl: string | null = null;
|
|
133
|
+
if (envPort && /^\d+$/.test(envPort)) {
|
|
134
|
+
resolvedUrl = `http://127.0.0.1:${envPort}`;
|
|
135
|
+
} else {
|
|
136
|
+
resolvedUrl = await lookupSessionMapping(ctx.sessionID);
|
|
137
|
+
}
|
|
141
138
|
if (!resolvedUrl) {
|
|
142
139
|
return JSON.stringify({
|
|
143
140
|
status: "unavailable",
|
|
144
141
|
type: requestType,
|
|
145
|
-
error: `
|
|
142
|
+
error: `No approval server found for session ${ctx.sessionID}. Ensure heartbeat runner is running for this role.`,
|
|
146
143
|
approval_id: null,
|
|
147
144
|
});
|
|
148
145
|
}
|