@wenrwa/marketplace-cli 0.8.2 → 0.8.4
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/__tests__/bridge-session-supersede-exit.test.d.ts +1 -0
- package/dist/__tests__/bridge-session-supersede-exit.test.js +74 -0
- package/dist/__tests__/opencode-local-launch-parity.test.d.ts +1 -0
- package/dist/__tests__/opencode-local-launch-parity.test.js +129 -0
- package/dist/bridge/agentLaunch.js +17 -10
- package/dist/bridge/opencodeConfig.d.ts +1 -0
- package/dist/bridge/opencodeConfig.js +5 -1
- package/dist/bridge/server.d.ts +3 -0
- package/dist/bridge/server.js +1 -1
- package/dist/bridge/sessionManager.js +8 -0
- package/dist/commands/bridge.js +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* A re-open on a live id (SessionManager#open supersedes) kills the OLD PTY.
|
|
5
|
+
* That process exits asynchronously, AFTER the new session is registered
|
|
6
|
+
* under the same id. Its exit must not drop the NEW session: before this
|
|
7
|
+
* guard, the old onExit ran `sessions.delete(id)` unconditionally, so the
|
|
8
|
+
* fresh PTY kept running but untracked (input dropped, attach answered
|
|
9
|
+
* "Unknown session", nothing could close it) and onSessionExit announced the
|
|
10
|
+
* agent as ended while it was alive.
|
|
11
|
+
*
|
|
12
|
+
* Hit by an explicit restart on either lane: the hosted oc-21 relaunch
|
|
13
|
+
* (relay `open` on a live id) and the browser's local restart after an
|
|
14
|
+
* engine/model change (close → open).
|
|
15
|
+
*/
|
|
16
|
+
const sessionManager_1 = require("../bridge/sessionManager");
|
|
17
|
+
class ExitablePty {
|
|
18
|
+
killed = false;
|
|
19
|
+
exitCbs = [];
|
|
20
|
+
write() { }
|
|
21
|
+
resize() { }
|
|
22
|
+
kill() { this.killed = true; }
|
|
23
|
+
onData() { }
|
|
24
|
+
onExit(cb) { this.exitCbs.push(cb); }
|
|
25
|
+
exit(code) { for (const cb of this.exitCbs)
|
|
26
|
+
cb(code); }
|
|
27
|
+
}
|
|
28
|
+
const make = () => {
|
|
29
|
+
const ptys = [];
|
|
30
|
+
const factory = { spawn: () => { const p = new ExitablePty(); ptys.push(p); return p; } };
|
|
31
|
+
const sm = new sessionManager_1.SessionManager(factory);
|
|
32
|
+
const exits = [];
|
|
33
|
+
sm.setOnSessionExit((id) => exits.push(id));
|
|
34
|
+
return { sm, ptys, exits };
|
|
35
|
+
};
|
|
36
|
+
describe('supersede: the old process exiting late never drops the new session', () => {
|
|
37
|
+
it('re-open, then the OLD pty exits → the new session is still tracked and attachable', async () => {
|
|
38
|
+
const { sm, ptys, exits } = make();
|
|
39
|
+
sm.open({ cols: 80, rows: 24, id: 'agent-1' });
|
|
40
|
+
sm.open({ cols: 80, rows: 24, id: 'agent-1' }); // supersede
|
|
41
|
+
expect(ptys[0].killed).toBe(true);
|
|
42
|
+
ptys[0].exit(129); // SIGHUP lands ~600ms later in real life
|
|
43
|
+
expect(sm.list()).toEqual(['agent-1']);
|
|
44
|
+
expect(exits).toEqual([]); // the agent did NOT end
|
|
45
|
+
await expect(sm.attach('agent-1', () => { })).resolves.toBeDefined();
|
|
46
|
+
});
|
|
47
|
+
it('close, re-open, then the OLD pty exits → same', () => {
|
|
48
|
+
const { sm, ptys, exits } = make();
|
|
49
|
+
sm.open({ cols: 80, rows: 24, id: 'agent-1' });
|
|
50
|
+
sm.close('agent-1');
|
|
51
|
+
sm.open({ cols: 80, rows: 24, id: 'agent-1' });
|
|
52
|
+
ptys[0].exit(129);
|
|
53
|
+
expect(sm.list()).toEqual(['agent-1']);
|
|
54
|
+
expect(exits).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
it('the CURRENT pty exiting still ends the session and notifies (unchanged)', () => {
|
|
57
|
+
const { sm, ptys, exits } = make();
|
|
58
|
+
sm.open({ cols: 80, rows: 24, id: 'agent-1' });
|
|
59
|
+
sm.open({ cols: 80, rows: 24, id: 'agent-1' });
|
|
60
|
+
ptys[1].exit(0);
|
|
61
|
+
expect(sm.list()).toEqual([]);
|
|
62
|
+
expect(exits).toEqual(['agent-1']);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('a plain close still reports the exit (unchanged)', () => {
|
|
66
|
+
it('close with nothing reopened → onSessionExit fires', () => {
|
|
67
|
+
const { sm, ptys, exits } = make();
|
|
68
|
+
sm.open({ cols: 80, rows: 24, id: 'agent-1' });
|
|
69
|
+
sm.close('agent-1');
|
|
70
|
+
ptys[0].exit(129);
|
|
71
|
+
expect(sm.list()).toEqual([]);
|
|
72
|
+
expect(exits).toEqual(['agent-1']);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
/**
|
|
40
|
+
* oc-27 — the ACTUAL local launch must carry the same keys the hosted config does:
|
|
41
|
+
* `plugin` (activity, titles, Needs-you, session-id capture), `mcp.wenrwa-marketplace`
|
|
42
|
+
* (when a connect token exists) and `theme`. A builder-only test missed this: the
|
|
43
|
+
* builder was fine, the local call sites never fed it a plugin or a token.
|
|
44
|
+
*/
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const url_1 = require("url");
|
|
48
|
+
const ws_1 = __importDefault(require("ws"));
|
|
49
|
+
const agentLaunch_1 = require("../bridge/agentLaunch");
|
|
50
|
+
const server_1 = require("../bridge/server");
|
|
51
|
+
const sessionManager_1 = require("../bridge/sessionManager");
|
|
52
|
+
const hosted = require(path.join(__dirname, '..', '..', '..', 'infra', 'terminal-image', 'opencode', 'build-config.js'));
|
|
53
|
+
const GW = 'https://api.example.test/api/v1/opencode/v1';
|
|
54
|
+
const oc = { engine: 'opencode', model: 'moonshotai/kimi-k2', gatewayUrl: GW, gatewayToken: 'hgw_abc' };
|
|
55
|
+
const cfgOf = (l) => JSON.parse(l.env.OPENCODE_CONFIG_CONTENT);
|
|
56
|
+
// The shape cli/src/commands/bridge.ts hands resolveAgentLaunch (mcpInstall + bridgeApiUrl).
|
|
57
|
+
const realDeps = {
|
|
58
|
+
mcpServer: { command: '/usr/bin/node', args: ['/h/.wen-market/mcp/dist/index.js'] },
|
|
59
|
+
apiUrl: 'https://api.example.test/api/v1',
|
|
60
|
+
activity: { settingsPath: '/h/.wen-market/hiveop-settings.json', sessionId: 'a1', activityFile: '/h/.wen-market/activity.jsonl' },
|
|
61
|
+
sessionPin: { agentId: 'a1', exists: false },
|
|
62
|
+
};
|
|
63
|
+
describe('local OpenCode launch carries the hosted keys', () => {
|
|
64
|
+
const hostedCfg = hosted.buildConfig({
|
|
65
|
+
WENRWA_AGENT_MODEL: oc.model, HIVEOP_GATEWAY_URL: GW, HIVEOP_GATEWAY_TOKEN: 't', WENRWA_CONNECT_TOKEN: 'c',
|
|
66
|
+
});
|
|
67
|
+
it('plugin is a file:// URL to a file that exists in the installed package', () => {
|
|
68
|
+
const cfg = cfgOf((0, agentLaunch_1.resolveAgentLaunch)({ opencode: oc, connectToken: 'c', ...realDeps }));
|
|
69
|
+
expect(cfg.plugin).toHaveLength(1);
|
|
70
|
+
expect(cfg.plugin[0]).toMatch(/^file:\/\//);
|
|
71
|
+
expect(fs.existsSync((0, url_1.fileURLToPath)(cfg.plugin[0]))).toBe(true);
|
|
72
|
+
expect(path.basename((0, url_1.fileURLToPath)(cfg.plugin[0]))).toBe('opencode-plugin.mjs');
|
|
73
|
+
});
|
|
74
|
+
it('mcp.wenrwa-marketplace is present with the token by reference only', () => {
|
|
75
|
+
const l = (0, agentLaunch_1.resolveAgentLaunch)({ opencode: oc, connectToken: 'secret-conn-token', ...realDeps });
|
|
76
|
+
expect(cfgOf(l).mcp['wenrwa-marketplace'].environment.WENRWA_CONNECT_TOKEN).toBe('{env:WENRWA_CONNECT_TOKEN}');
|
|
77
|
+
expect(l.env.OPENCODE_CONFIG_CONTENT).not.toContain('secret-conn-token');
|
|
78
|
+
expect(l.env.WENRWA_CONNECT_TOKEN).toBe('secret-conn-token');
|
|
79
|
+
});
|
|
80
|
+
it('env carries HIVEOP_GATEWAY_URL so the plugin can report the session id (resume)', () => {
|
|
81
|
+
const l = (0, agentLaunch_1.resolveAgentLaunch)({ opencode: oc, connectToken: 'c', ...realDeps });
|
|
82
|
+
expect(l.env.HIVEOP_GATEWAY_URL).toBe(GW);
|
|
83
|
+
expect(l.env.HIVEOP_GATEWAY_TOKEN).toBe('hgw_abc');
|
|
84
|
+
});
|
|
85
|
+
it('theme matches the hosted config (terminal palette, no own background)', () => {
|
|
86
|
+
const cfg = cfgOf((0, agentLaunch_1.resolveAgentLaunch)({ opencode: oc, ...realDeps }));
|
|
87
|
+
expect(cfg.theme).toBe('system');
|
|
88
|
+
expect(cfg.theme).toBe(hostedCfg.theme);
|
|
89
|
+
});
|
|
90
|
+
it('the same top-level keys as hosted (modulo the hosted-only plugin path)', () => {
|
|
91
|
+
const local = cfgOf((0, agentLaunch_1.resolveAgentLaunch)({ opencode: oc, connectToken: 'c', ...realDeps }));
|
|
92
|
+
const keys = (o) => Object.keys(o).filter((k) => k !== 'instructions').sort();
|
|
93
|
+
expect(keys(local)).toEqual(keys(hostedCfg));
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('loopback open (real bridge wiring) falls back to the bridge-config connect token', () => {
|
|
97
|
+
let handle;
|
|
98
|
+
let spawns;
|
|
99
|
+
beforeEach(async () => {
|
|
100
|
+
spawns = [];
|
|
101
|
+
const pty = { write() { }, resize() { }, kill() { }, onData() { }, onExit() { } };
|
|
102
|
+
handle = await (0, server_1.startBridgeServer)({
|
|
103
|
+
sessionToken: 'tok', allowedOrigin: 'https://app.hiveop.io',
|
|
104
|
+
sessions: new sessionManager_1.SessionManager({ spawn: (o) => { spawns.push(o); return pty; } }),
|
|
105
|
+
resolveLocalAgentCwd: () => '/tmp/proj',
|
|
106
|
+
commandExists: () => true,
|
|
107
|
+
activityFile: '/h/activity.jsonl', hiveOpSettingsPath: '/h/settings.json',
|
|
108
|
+
mcpServer: realDeps.mcpServer, mcpApiUrl: realDeps.apiUrl,
|
|
109
|
+
connectToken: 'bridge-cfg-token',
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
afterEach(async () => { await handle.close(); });
|
|
113
|
+
it('open frame without connectToken still yields plugin + mcp + theme', async () => {
|
|
114
|
+
const ws = new ws_1.default(`ws://127.0.0.1:${handle.port}`, { headers: { origin: 'https://app.hiveop.io' } });
|
|
115
|
+
await new Promise((r) => ws.on('open', r));
|
|
116
|
+
ws.send(JSON.stringify({ v: 1, token: 'tok' }));
|
|
117
|
+
const opened = new Promise((r) => ws.on('message', (m) => { if (JSON.parse(String(m)).t === 'opened')
|
|
118
|
+
r(null); }));
|
|
119
|
+
ws.send(JSON.stringify({ t: 'open', cols: 80, rows: 24, agentId: 'a1', workspaceId: 'w1', opencode: oc }));
|
|
120
|
+
await opened;
|
|
121
|
+
ws.close();
|
|
122
|
+
const cfg = cfgOf({ env: spawns[0].env });
|
|
123
|
+
expect(cfg.plugin[0]).toMatch(/^file:\/\//);
|
|
124
|
+
expect(cfg.mcp['wenrwa-marketplace']).toBeDefined();
|
|
125
|
+
expect(cfg.theme).toBe('system');
|
|
126
|
+
expect(spawns[0].env.WENRWA_CONNECT_TOKEN).toBe('bridge-cfg-token');
|
|
127
|
+
expect(spawns[0].env.OPENCODE_CONFIG_CONTENT).not.toContain('bridge-cfg-token');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -4,6 +4,7 @@ exports.HIVEOP_CONTEXT_ENV_VALUE = exports.HIVEOP_CONTEXT_ENV_VAR = void 0;
|
|
|
4
4
|
exports.missingCommandMessage = missingCommandMessage;
|
|
5
5
|
exports.resolveAgentLaunch = resolveAgentLaunch;
|
|
6
6
|
const localContextWriter_1 = require("./localContextWriter");
|
|
7
|
+
const opencodePlugin_1 = require("./opencodePlugin");
|
|
7
8
|
const opencodeConfig_1 = require("./opencodeConfig");
|
|
8
9
|
/** lws-08 — env var name a spawned `claude` (or any tooling inspecting its
|
|
9
10
|
* own environment) can read to find the pushed context manifest. */
|
|
@@ -46,21 +47,27 @@ function resolveOpencodeLaunch(opts, oc) {
|
|
|
46
47
|
const args = [];
|
|
47
48
|
if ((0, opencodeConfig_1.isCapturedOpencodeSessionId)(oc.sessionId))
|
|
48
49
|
args.push('-s', oc.sessionId);
|
|
49
|
-
const config =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
const config = {
|
|
51
|
+
...(0, opencodeConfig_1.buildOpencodeConfig)({
|
|
52
|
+
model: oc.model,
|
|
53
|
+
gatewayBaseUrl: oc.gatewayUrl,
|
|
54
|
+
autonomous: !!opts.autonomous,
|
|
55
|
+
withMcp: !!opts.connectToken,
|
|
56
|
+
// oc-23: Claude loads .hiveop/context.md via its SessionStart hook; OpenCode has none, so it reads it as instructions.
|
|
57
|
+
instructions: [exports.HIVEOP_CONTEXT_ENV_VALUE],
|
|
58
|
+
...(opts.mcpServer ? { mcpCommand: [opts.mcpServer.command, ...opts.mcpServer.args] } : {}),
|
|
59
|
+
...(opts.apiUrl ? { mcpEnvPassthrough: ['MARKETPLACE_API_URL'] } : {}),
|
|
60
|
+
}),
|
|
61
|
+
// oc-27: the HiveOp plugin (activity, titles, Needs-you, session-id capture) — as hosted does.
|
|
62
|
+
...(0, opencodePlugin_1.buildOpenCodePluginConfig)(),
|
|
63
|
+
};
|
|
59
64
|
const env = {
|
|
60
65
|
[exports.HIVEOP_CONTEXT_ENV_VAR]: exports.HIVEOP_CONTEXT_ENV_VALUE,
|
|
61
66
|
...opencodeConfig_1.OPENCODE_QUIET_ENV,
|
|
62
67
|
OPENCODE_CONFIG_CONTENT: JSON.stringify(config),
|
|
63
68
|
[opencodeConfig_1.OPENCODE_GATEWAY_TOKEN_ENV]: oc.gatewayToken,
|
|
69
|
+
// oc-27: the plugin POSTs the captured session id to <gateway>/hiveop/session (resume).
|
|
70
|
+
HIVEOP_GATEWAY_URL: oc.gatewayUrl,
|
|
64
71
|
};
|
|
65
72
|
if (opts.connectToken)
|
|
66
73
|
env.WENRWA_CONNECT_TOKEN = opts.connectToken;
|
|
@@ -16,6 +16,7 @@ export declare const OPENCODE_MIN_VERSION = "1.18.35";
|
|
|
16
16
|
export declare const OPENCODE_INSTALL_HINT = "Install OpenCode: npm i -g opencode-ai@>=1.18.35";
|
|
17
17
|
/** Every outbound call OpenCode makes at startup besides the gateway (oc-04 s5). */
|
|
18
18
|
export declare const OPENCODE_QUIET_ENV: Readonly<Record<string, string>>;
|
|
19
|
+
export declare const OPENCODE_THEME = "system";
|
|
19
20
|
export declare const OPENCODE_PROVIDER_ID = "hiveop";
|
|
20
21
|
export declare const OPENCODE_GATEWAY_TOKEN_ENV = "HIVEOP_GATEWAY_TOKEN";
|
|
21
22
|
export interface OpencodeConfigInput {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* (`{env:HIVEOP_GATEWAY_TOKEN}`) so it never appears inside the JSON.
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.isCapturedOpencodeSessionId = exports.OPENCODE_GATEWAY_TOKEN_ENV = exports.OPENCODE_PROVIDER_ID = exports.OPENCODE_QUIET_ENV = exports.OPENCODE_INSTALL_HINT = exports.OPENCODE_MIN_VERSION = exports.OPENCODE_PINNED_VERSION = void 0;
|
|
13
|
+
exports.isCapturedOpencodeSessionId = exports.OPENCODE_GATEWAY_TOKEN_ENV = exports.OPENCODE_PROVIDER_ID = exports.OPENCODE_THEME = exports.OPENCODE_QUIET_ENV = exports.OPENCODE_INSTALL_HINT = exports.OPENCODE_MIN_VERSION = exports.OPENCODE_PINNED_VERSION = void 0;
|
|
14
14
|
exports.buildOpencodeConfig = buildOpencodeConfig;
|
|
15
15
|
exports.opencodeVersionOk = opencodeVersionOk;
|
|
16
16
|
/** Oldest OpenCode the local lane accepts. 1.18.35 is the version oc-04 verified
|
|
@@ -29,6 +29,7 @@ exports.OPENCODE_QUIET_ENV = Object.freeze({
|
|
|
29
29
|
OPENCODE_DISABLE_TERMINAL_TITLE: '1',
|
|
30
30
|
npm_config_offline: 'true',
|
|
31
31
|
});
|
|
32
|
+
exports.OPENCODE_THEME = 'system';
|
|
32
33
|
exports.OPENCODE_PROVIDER_ID = 'hiveop';
|
|
33
34
|
exports.OPENCODE_GATEWAY_TOKEN_ENV = 'HIVEOP_GATEWAY_TOKEN';
|
|
34
35
|
function buildOpencodeConfig(input) {
|
|
@@ -41,6 +42,9 @@ function buildOpencodeConfig(input) {
|
|
|
41
42
|
model: `${exports.OPENCODE_PROVIDER_ID}/${model}`,
|
|
42
43
|
// OpenCode makes a second, billed "title" call on the small model.
|
|
43
44
|
small_model: `${exports.OPENCODE_PROVIDER_ID}/${model}`,
|
|
45
|
+
// oc-27: OpenCode's built-in "system" theme derives its palette from the terminal and
|
|
46
|
+
// leaves the background transparent, so the pane blends with the canvas like Claude's.
|
|
47
|
+
theme: exports.OPENCODE_THEME,
|
|
44
48
|
provider: {
|
|
45
49
|
[exports.OPENCODE_PROVIDER_ID]: {
|
|
46
50
|
npm: '@ai-sdk/openai-compatible',
|
package/dist/bridge/server.d.ts
CHANGED
|
@@ -244,6 +244,9 @@ export interface BridgeServerOpts {
|
|
|
244
244
|
args: string[];
|
|
245
245
|
};
|
|
246
246
|
mcpApiUrl?: string;
|
|
247
|
+
/** oc-27 — the bridge's own stored Connect Token: the fallback for an `open` frame that carries none
|
|
248
|
+
* (loopback frames from the browser never do), so local OpenCode agents get the same MCP as Claude. */
|
|
249
|
+
connectToken?: string;
|
|
247
250
|
/**
|
|
248
251
|
* rival-10 — every file touch of a LOCAL WORKSPACE AGENT (an `open` with
|
|
249
252
|
* agentId + workspaceId), handed on for the ranked rival feed. The hook
|
package/dist/bridge/server.js
CHANGED
|
@@ -736,7 +736,7 @@ function startBridgeServer(opts) {
|
|
|
736
736
|
let launch = (0, agentLaunch_1.resolveAgentLaunch)({
|
|
737
737
|
autonomous: !!frame.autonomous,
|
|
738
738
|
...(frame.opencode ? { opencode: frame.opencode } : {}),
|
|
739
|
-
...(frame.connectToken ? { connectToken: frame.connectToken } : {}),
|
|
739
|
+
...((frame.connectToken || opts.connectToken) ? { connectToken: (frame.connectToken || opts.connectToken) } : {}),
|
|
740
740
|
...(opts.mcpServer ? { mcpServer: opts.mcpServer } : {}),
|
|
741
741
|
...(opts.mcpApiUrl ? { apiUrl: opts.mcpApiUrl } : {}),
|
|
742
742
|
...(cwd ? { sessionPin: { agentId, exists: agentSessionExists } } : {}),
|
|
@@ -188,6 +188,14 @@ class SessionManager {
|
|
|
188
188
|
session.exited = true;
|
|
189
189
|
session.sink = null;
|
|
190
190
|
session.snapshot.dispose();
|
|
191
|
+
// A superseded (or closed-then-reopened) PTY exits AFTER its successor
|
|
192
|
+
// registered under the same id. That late exit is not this id ending:
|
|
193
|
+
// dropping the map entry here orphaned the live successor (untracked,
|
|
194
|
+
// input dropped, unclosable) and announced a running agent as ended.
|
|
195
|
+
// A plain close (nothing reopened) still notifies, as before.
|
|
196
|
+
const current = this.sessions.get(id);
|
|
197
|
+
if (current && current !== session)
|
|
198
|
+
return;
|
|
191
199
|
this.sessions.delete(id);
|
|
192
200
|
// fcct-26 — tell anyone listening that this session ENDED. Without an
|
|
193
201
|
// outward signal the only way to know a crew member finished its turn is
|
package/dist/commands/bridge.js
CHANGED
|
@@ -1127,6 +1127,8 @@ exports.bridgeCommand = new commander_1.Command('bridge')
|
|
|
1127
1127
|
hiveOpSettingsPath,
|
|
1128
1128
|
...(mcpInstall.ok ? { mcpServer: { command: mcpInstall.command, args: mcpInstall.args } } : {}),
|
|
1129
1129
|
mcpApiUrl: bridgeApiUrl(),
|
|
1130
|
+
// oc-27: local OpenCode panes get the HiveOp MCP with the same token Claude's registration uses.
|
|
1131
|
+
connectToken: config.connectToken,
|
|
1130
1132
|
// tchp-18: the loopback changes reporter is opt-in; without this the
|
|
1131
1133
|
// tchp-12 machinery never constructs on a laptop and local panels sit
|
|
1132
1134
|
// on "Waiting for changes…" forever.
|
|
@@ -1353,7 +1355,7 @@ exports.bridgeCommand = new commander_1.Command('bridge')
|
|
|
1353
1355
|
resolveLaunch: ({ sessionId, opencode, connectToken }) => (0, agentLaunch_1.resolveAgentLaunch)({
|
|
1354
1356
|
autonomous: true,
|
|
1355
1357
|
...(opencode ? { opencode } : {}),
|
|
1356
|
-
...(connectToken ? { connectToken } : {}),
|
|
1358
|
+
...((connectToken || config.connectToken) ? { connectToken: (connectToken || config.connectToken) } : {}),
|
|
1357
1359
|
...(mcpInstall.ok ? { mcpServer: { command: mcpInstall.command, args: mcpInstall.args } } : {}),
|
|
1358
1360
|
apiUrl: bridgeApiUrl(),
|
|
1359
1361
|
...(activityFile && hiveOpSettingsPath && sessionId
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenrwa/marketplace-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "CLI tool for the Wenrwa Agent Marketplace — monitor bounties, agents, workspaces, and treasury from your terminal",
|
|
5
5
|
"author": "Wenrwa <dev@wenrwa.com>",
|
|
6
6
|
"license": "MIT",
|