external-subagent 0.1.4 → 0.1.5
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/cli/commands/config.mjs +18 -2
- package/cli/constants.mjs +1 -1
- package/cli/install/service-activation.mjs +13 -6
- package/cli/install/service-macos.mjs +4 -1
- package/npm/native/darwin-arm64/external-subagent-mcp +0 -0
- package/npm/native/darwin-arm64/external-subagentd +0 -0
- package/npm/native/darwin-arm64/payload.json +4 -4
- package/package.json +1 -1
package/cli/commands/config.mjs
CHANGED
|
@@ -2,6 +2,9 @@ import { readConfig } from '../config/read.mjs';
|
|
|
2
2
|
import { updateConfig, writeConfig } from '../config/write.mjs';
|
|
3
3
|
import { SUBAGENT_IDS } from '../config/schema.mjs';
|
|
4
4
|
import { CliError } from '../errors.mjs';
|
|
5
|
+
import { atomicWrite } from '../fs-atomic.mjs';
|
|
6
|
+
import { hasInstalledService } from '../install/service-activation.mjs';
|
|
7
|
+
import { launchAgentPlist } from '../install/service-macos.mjs';
|
|
5
8
|
|
|
6
9
|
const CONFIG_INPUT_FIELDS = new Set(['operation', 'patch', 'key']);
|
|
7
10
|
const GET_KEYS = new Set([
|
|
@@ -85,6 +88,17 @@ export function parseConfigArgs(args) {
|
|
|
85
88
|
throw new CliError('INVALID_ARGUMENT', `unsupported config operation: ${operation}`, 2);
|
|
86
89
|
}
|
|
87
90
|
|
|
91
|
+
// A config change moves the very values the installed LaunchAgent forwards to
|
|
92
|
+
// launchd (subagent homes, config revision), so every successful write also
|
|
93
|
+
// regenerates the service definition from the new config. Only the file is
|
|
94
|
+
// rewritten — launchd picks the environment up at its next load, so a running
|
|
95
|
+
// daemon is never restarted or disturbed here.
|
|
96
|
+
function refreshServiceDefinition(paths) {
|
|
97
|
+
if (!hasInstalledService(paths)) return false;
|
|
98
|
+
atomicWrite(paths.launchAgent, launchAgentPlist(paths), 0o600);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
88
102
|
export function configCommand(paths, input = {}) {
|
|
89
103
|
rejectUnknownInput(input);
|
|
90
104
|
const operation = input.operation ?? 'get';
|
|
@@ -98,7 +112,8 @@ export function configCommand(paths, input = {}) {
|
|
|
98
112
|
for (const [agent, value] of Object.entries(input.patch.subagents || {})) {
|
|
99
113
|
if (value === null || typeof value !== 'object' || Array.isArray(value)) throw new CliError('CONFIG_INVALID', `subagents.${agent} must be an object`, 2);
|
|
100
114
|
}
|
|
101
|
-
|
|
115
|
+
const config = updateConfig(paths.config, (latest) => mergePatch(latest, input.patch));
|
|
116
|
+
return { config, service_definition_refreshed: refreshServiceDefinition(paths) };
|
|
102
117
|
}
|
|
103
118
|
if (operation === 'show') {
|
|
104
119
|
if (input.patch !== undefined || input.key !== undefined) throw new CliError('INVALID_ARGUMENT', 'config show does not accept key or patch', 2);
|
|
@@ -106,7 +121,8 @@ export function configCommand(paths, input = {}) {
|
|
|
106
121
|
}
|
|
107
122
|
if (operation === 'unset') {
|
|
108
123
|
if (input.patch !== undefined || typeof input.key !== 'string') throw new CliError('INVALID_ARGUMENT', 'config unset requires exactly one supported key', 2);
|
|
109
|
-
|
|
124
|
+
const config = updateConfig(paths.config, (latest) => mergePatch(latest, unsetPatch(input.key)));
|
|
125
|
+
return { config, service_definition_refreshed: refreshServiceDefinition(paths) };
|
|
110
126
|
}
|
|
111
127
|
if (operation === 'get') {
|
|
112
128
|
if (input.patch !== undefined) throw new CliError('INVALID_ARGUMENT', 'config get does not accept patch', 2);
|
package/cli/constants.mjs
CHANGED
|
@@ -8,7 +8,7 @@ const IS_DEBUG_VARIANT = process.env.EXTERNAL_SUBAGENT_VARIANT === 'debug';
|
|
|
8
8
|
|
|
9
9
|
export const PRODUCT_NAME = IS_DEBUG_VARIANT ? 'external-subagent-debug' : 'external-subagent';
|
|
10
10
|
export const PRODUCT_ID = IS_DEBUG_VARIANT ? 'external_subagent_debug' : 'external_subagent';
|
|
11
|
-
export const VERSION = '0.1.
|
|
11
|
+
export const VERSION = '0.1.5';
|
|
12
12
|
export const LAUNCH_AGENT_LABEL = IS_DEBUG_VARIANT ? 'com.external-subagent-debug.daemon' : 'com.external-subagent.daemon';
|
|
13
13
|
export const DAEMON_BIN_NAME = IS_DEBUG_VARIANT ? 'external-subagent-debugd' : 'external-subagentd';
|
|
14
14
|
export const MCP_BIN_NAME = IS_DEBUG_VARIANT ? 'external-subagent-debug-mcp' : 'external-subagent-mcp';
|
|
@@ -4,7 +4,7 @@ import { atomicWrite } from '../fs-atomic.mjs';
|
|
|
4
4
|
import { CliError } from '../errors.mjs';
|
|
5
5
|
import { callDaemon } from '../rpc.mjs';
|
|
6
6
|
import { LAUNCH_AGENT_LABEL } from '../constants.mjs';
|
|
7
|
-
import { launchctl } from './service-macos.mjs';
|
|
7
|
+
import { launchAgentPlist, launchctl } from './service-macos.mjs';
|
|
8
8
|
|
|
9
9
|
const xml = (s) => s.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
10
10
|
const unxml = (s) => s.replaceAll('<', '<').replaceAll('>', '>').replaceAll('&', '&');
|
|
@@ -15,8 +15,11 @@ export function hasInstalledService(paths) {
|
|
|
15
15
|
return Boolean(paths.launchAgent && fs.existsSync(paths.launchAgent));
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
// The service definition is the
|
|
19
|
-
//
|
|
18
|
+
// The service definition is derived from the CURRENT config: activation
|
|
19
|
+
// regenerates the whole plist (environment, revision, runtime forwarding) so a
|
|
20
|
+
// reconfigured installation can never keep serving stale launchd environment.
|
|
21
|
+
// Only rollback restores the previous definition bytes — the exact known state
|
|
22
|
+
// the service ran before — with its executable repointed.
|
|
20
23
|
export async function activateService(paths, candidate, options = {}) {
|
|
21
24
|
const control = options.launchctl || launchctl;
|
|
22
25
|
const rpc = options.callDaemon || callDaemon;
|
|
@@ -93,17 +96,21 @@ export async function activateService(paths, candidate, options = {}) {
|
|
|
93
96
|
} while (Date.now() < deadline);
|
|
94
97
|
throw new CliError('SERVICE_UNLOAD_TIMEOUT', 'launchd service remained registered after bootout');
|
|
95
98
|
};
|
|
96
|
-
const
|
|
99
|
+
const writeDefinition = (entryPath) => atomicWrite(paths.launchAgent, launchAgentPlist(paths, {
|
|
100
|
+
daemonPath: entryPath,
|
|
101
|
+
...(options.zcodeRuntime !== undefined ? { zcodeRuntime: options.zcodeRuntime } : {}),
|
|
102
|
+
}), 0o600);
|
|
103
|
+
const restoreDefinition = (entryPath) => atomicWrite(paths.launchAgent, Buffer.from(oldPlist.toString().replace(program, (_, a, b, c) => `${a}${xml(entryPath)}${c}`)), 0o600);
|
|
97
104
|
const rollbackService = async () => {
|
|
98
105
|
await unload();
|
|
99
106
|
const previous = rollbackExecutable();
|
|
100
|
-
|
|
107
|
+
restoreDefinition(previous.path);
|
|
101
108
|
await control(['bootstrap', `gui/${process.getuid()}`, paths.launchAgent]);
|
|
102
109
|
return healthy({ path: previous.path, sha256: previous.sha256, version: oldStatus?.identity?.daemon?.version }, oldPid, oldStatus?.service_generation);
|
|
103
110
|
};
|
|
104
111
|
try {
|
|
105
112
|
await unload();
|
|
106
|
-
|
|
113
|
+
writeDefinition(candidate.path);
|
|
107
114
|
await control(['bootstrap', `gui/${process.getuid()}`, paths.launchAgent]);
|
|
108
115
|
const health = await healthy(candidate, oldPid, oldStatus?.service_generation);
|
|
109
116
|
return {
|
|
@@ -28,7 +28,10 @@ export function runtimeObservations(paths, options = {}) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function launchAgentPlist(paths, options = {}) {
|
|
31
|
-
|
|
31
|
+
// An activation carries its own verified daemon artifact (the retained
|
|
32
|
+
// payload copy), so the entry may be pinned explicitly; every other caller
|
|
33
|
+
// derives it from the installed package.
|
|
34
|
+
const daemon = options.daemonPath || nativeBinary(DAEMON_BIN_NAME);
|
|
32
35
|
const config = readConfig(paths.config);
|
|
33
36
|
const { runtime_path: dshRuntime, home: dshHome, profile: dshProfile, version: dshVersion } = config.subagents.dsh;
|
|
34
37
|
const { runtime_path: codexRuntime, home: codexHome } = config.subagents.codex;
|
|
Binary file
|
|
Binary file
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"product": "external-subagent",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5",
|
|
5
5
|
"platform": "darwin-arm64",
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"name": "external-subagentd",
|
|
9
|
-
"bytes":
|
|
9
|
+
"bytes": 9443072,
|
|
10
10
|
"mode": "755",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "1c6048c4202733b81a5b80cab82bee3a0a7071f90341f8bf2948707bf794e067"
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"name": "external-subagent-mcp",
|
|
15
15
|
"bytes": 937840,
|
|
16
16
|
"mode": "755",
|
|
17
|
-
"sha256": "
|
|
17
|
+
"sha256": "07f4d3949165419fba81c66ef4fd631615490a13dcf8b12a1c6859762e0396af"
|
|
18
18
|
}
|
|
19
19
|
]
|
|
20
20
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "external-subagent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Manage bounded ZCode, DeepSeek Harness (DSH), and Codex subagent tasks through a local MCP service for any local host (Codex, ZCode, or a custom MCP client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|