@agentmeshhq/agent 0.2.0 → 0.3.0
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/README.md +39 -0
- package/dist/__tests__/context-template.test.d.ts +4 -0
- package/dist/__tests__/context-template.test.js +233 -0
- package/dist/__tests__/context-template.test.js.map +1 -0
- package/dist/__tests__/loader.test.js +140 -28
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/no-respawn.test.d.ts +1 -0
- package/dist/__tests__/no-respawn.test.js +254 -0
- package/dist/__tests__/no-respawn.test.js.map +1 -0
- package/dist/__tests__/onboard.test.d.ts +5 -0
- package/dist/__tests__/onboard.test.js +341 -0
- package/dist/__tests__/onboard.test.js.map +1 -0
- package/dist/__tests__/orphan-process.test.d.ts +11 -0
- package/dist/__tests__/orphan-process.test.js +286 -0
- package/dist/__tests__/orphan-process.test.js.map +1 -0
- package/dist/__tests__/runner.test.js +16 -0
- package/dist/__tests__/runner.test.js.map +1 -1
- package/dist/__tests__/shared-resource-guards.test.d.ts +7 -0
- package/dist/__tests__/shared-resource-guards.test.js +260 -0
- package/dist/__tests__/shared-resource-guards.test.js.map +1 -0
- package/dist/__tests__/watchdog.test.js +138 -12
- package/dist/__tests__/watchdog.test.js.map +1 -1
- package/dist/cli/status.js +11 -0
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/stop.js +7 -2
- package/dist/cli/stop.js.map +1 -1
- package/dist/config/loader.d.ts +0 -4
- package/dist/config/loader.js +102 -42
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +6 -4
- package/dist/core/daemon/assignment-message.d.ts +12 -0
- package/dist/core/daemon/assignment-message.js +36 -0
- package/dist/core/daemon/assignment-message.js.map +1 -0
- package/dist/core/daemon/bootstrap.d.ts +35 -0
- package/dist/core/daemon/bootstrap.js +52 -0
- package/dist/core/daemon/bootstrap.js.map +1 -0
- package/dist/core/daemon/context-template.d.ts +11 -0
- package/dist/core/daemon/context-template.js +144 -0
- package/dist/core/daemon/context-template.js.map +1 -0
- package/dist/core/daemon/crash-log.d.ts +14 -0
- package/dist/core/daemon/crash-log.js +23 -0
- package/dist/core/daemon/crash-log.js.map +1 -0
- package/dist/core/daemon/git-auth.d.ts +18 -0
- package/dist/core/daemon/git-auth.js +88 -0
- package/dist/core/daemon/git-auth.js.map +1 -0
- package/dist/core/daemon/health-policy.d.ts +17 -0
- package/dist/core/daemon/health-policy.js +24 -0
- package/dist/core/daemon/health-policy.js.map +1 -0
- package/dist/core/daemon/sandbox-config.d.ts +9 -0
- package/dist/core/daemon/sandbox-config.js +17 -0
- package/dist/core/daemon/sandbox-config.js.map +1 -0
- package/dist/core/daemon/state.d.ts +33 -0
- package/dist/core/daemon/state.js +78 -0
- package/dist/core/daemon/state.js.map +1 -0
- package/dist/core/daemon/tmux-session.d.ts +17 -0
- package/dist/core/daemon/tmux-session.js +34 -0
- package/dist/core/daemon/tmux-session.js.map +1 -0
- package/dist/core/daemon/workspace.d.ts +23 -0
- package/dist/core/daemon/workspace.js +90 -0
- package/dist/core/daemon/workspace.js.map +1 -0
- package/dist/core/daemon.d.ts +9 -12
- package/dist/core/daemon.js +293 -393
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/injector.d.ts +5 -1
- package/dist/core/injector.js +83 -0
- package/dist/core/injector.js.map +1 -1
- package/dist/core/registry.d.ts +62 -0
- package/dist/core/registry.js +18 -0
- package/dist/core/registry.js.map +1 -1
- package/dist/core/runner/build.d.ts +9 -0
- package/dist/core/runner/build.js +53 -0
- package/dist/core/runner/build.js.map +1 -0
- package/dist/core/runner/detect.d.ts +5 -0
- package/dist/core/runner/detect.js +14 -0
- package/dist/core/runner/detect.js.map +1 -0
- package/dist/core/runner/index.d.ts +5 -0
- package/dist/core/runner/index.js +5 -0
- package/dist/core/runner/index.js.map +1 -0
- package/dist/core/runner/model.d.ts +5 -0
- package/dist/core/runner/model.js +7 -0
- package/dist/core/runner/model.js.map +1 -0
- package/dist/core/runner/opencode-models.d.ts +15 -0
- package/dist/core/runner/opencode-models.js +70 -0
- package/dist/core/runner/opencode-models.js.map +1 -0
- package/dist/core/runner/types.d.ts +19 -0
- package/dist/core/runner/types.js +8 -0
- package/dist/core/runner/types.js.map +1 -0
- package/dist/core/runner.d.ts +5 -47
- package/dist/core/runner.js +5 -167
- package/dist/core/runner.js.map +1 -1
- package/dist/core/tmux-runtime.d.ts +13 -0
- package/dist/core/tmux-runtime.js +72 -0
- package/dist/core/tmux-runtime.js.map +1 -0
- package/dist/core/tmux.d.ts +7 -1
- package/dist/core/tmux.js +75 -45
- package/dist/core/tmux.js.map +1 -1
- package/dist/core/watchdog.d.ts +18 -1
- package/dist/core/watchdog.js +78 -29
- package/dist/core/watchdog.js.map +1 -1
- package/package.json +24 -4
- package/src/__tests__/context.test.ts +0 -464
- package/src/__tests__/injector.test.ts +0 -29
- package/src/__tests__/jwt.test.ts +0 -112
- package/src/__tests__/loader.test.ts +0 -239
- package/src/__tests__/runner.test.ts +0 -104
- package/src/__tests__/sandbox.test.ts +0 -435
- package/src/__tests__/watchdog.test.ts +0 -368
- package/src/cli/attach.ts +0 -22
- package/src/cli/build.ts +0 -145
- package/src/cli/config.ts +0 -148
- package/src/cli/context.ts +0 -231
- package/src/cli/deploy.ts +0 -155
- package/src/cli/index.ts +0 -376
- package/src/cli/init.ts +0 -75
- package/src/cli/list.ts +0 -70
- package/src/cli/local.ts +0 -183
- package/src/cli/logs.ts +0 -64
- package/src/cli/migrate.ts +0 -212
- package/src/cli/nudge.ts +0 -81
- package/src/cli/restart.ts +0 -59
- package/src/cli/slack.ts +0 -70
- package/src/cli/start.ts +0 -118
- package/src/cli/status.ts +0 -91
- package/src/cli/stop.ts +0 -48
- package/src/cli/test.ts +0 -143
- package/src/cli/token.ts +0 -188
- package/src/cli/whoami.ts +0 -142
- package/src/config/loader.ts +0 -121
- package/src/config/schema.ts +0 -68
- package/src/context/handoff.ts +0 -122
- package/src/context/index.ts +0 -8
- package/src/context/schema.ts +0 -111
- package/src/context/storage.ts +0 -197
- package/src/core/daemon.ts +0 -1317
- package/src/core/heartbeat.ts +0 -129
- package/src/core/injector.ts +0 -292
- package/src/core/registry.ts +0 -159
- package/src/core/runner.ts +0 -225
- package/src/core/sandbox.ts +0 -547
- package/src/core/session-id.ts +0 -111
- package/src/core/tmux.ts +0 -405
- package/src/core/watchdog.ts +0 -238
- package/src/core/websocket.ts +0 -94
- package/src/index.ts +0 -10
- package/src/utils/jwt.ts +0 -87
- package/tsconfig.json +0 -8
- package/vitest.config.ts +0 -12
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
let cachedOpenCodeModels = null;
|
|
3
|
+
/**
|
|
4
|
+
* Returns available OpenCode models, with lightweight in-process caching.
|
|
5
|
+
*/
|
|
6
|
+
export function getOpenCodeModels() {
|
|
7
|
+
if (cachedOpenCodeModels)
|
|
8
|
+
return cachedOpenCodeModels;
|
|
9
|
+
try {
|
|
10
|
+
const output = execSync("opencode models 2>/dev/null", {
|
|
11
|
+
encoding: "utf-8",
|
|
12
|
+
timeout: 10000,
|
|
13
|
+
});
|
|
14
|
+
cachedOpenCodeModels = output
|
|
15
|
+
.split("\n")
|
|
16
|
+
.map((line) => line.trim())
|
|
17
|
+
.filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
18
|
+
return cachedOpenCodeModels;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Degrade gracefully when opencode is unavailable in current environment.
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validates whether the requested model can be resolved by OpenCode.
|
|
27
|
+
*/
|
|
28
|
+
export function validateOpenCodeModel(model) {
|
|
29
|
+
const models = getOpenCodeModels();
|
|
30
|
+
// If model listing is unavailable, do not block startup.
|
|
31
|
+
if (models.length === 0)
|
|
32
|
+
return { valid: true };
|
|
33
|
+
if (models.includes(model))
|
|
34
|
+
return { valid: true };
|
|
35
|
+
const partialMatch = models.find((m) => m.endsWith(`/${model}`) || m === model || m.split("/").pop() === model);
|
|
36
|
+
if (partialMatch)
|
|
37
|
+
return { valid: true };
|
|
38
|
+
return {
|
|
39
|
+
valid: false,
|
|
40
|
+
error: `Model "${model}" not found in OpenCode. Run 'opencode models' to see available models.`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const MODEL_ALIASES = {
|
|
44
|
+
"claude-sonnet-4": "anthropic/claude-sonnet-4-5",
|
|
45
|
+
"claude-sonnet-4-5": "anthropic/claude-sonnet-4-5",
|
|
46
|
+
"claude-opus-4": "anthropic/claude-opus-4-5",
|
|
47
|
+
"claude-opus-4-5": "anthropic/claude-opus-4-5",
|
|
48
|
+
"claude-haiku-4": "anthropic/claude-haiku-4-5",
|
|
49
|
+
"claude-haiku-4-5": "anthropic/claude-haiku-4-5",
|
|
50
|
+
"gpt-4o": "openai/gpt-4o",
|
|
51
|
+
"gpt-4": "openai/gpt-4",
|
|
52
|
+
o3: "openai/o3",
|
|
53
|
+
"o3-mini": "openai/o3-mini",
|
|
54
|
+
codex: "openai/codex",
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Normalizes model aliases into a provider-qualified OpenCode model where possible.
|
|
58
|
+
*/
|
|
59
|
+
export function normalizeOpenCodeModel(model) {
|
|
60
|
+
if (MODEL_ALIASES[model])
|
|
61
|
+
return MODEL_ALIASES[model];
|
|
62
|
+
if (model.includes("/"))
|
|
63
|
+
return model;
|
|
64
|
+
const models = getOpenCodeModels();
|
|
65
|
+
if (models.includes(model))
|
|
66
|
+
return model;
|
|
67
|
+
const fullPath = models.find((m) => m.endsWith(`/${model}`) || m.split("/").pop() === model);
|
|
68
|
+
return fullPath || model;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=opencode-models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode-models.js","sourceRoot":"","sources":["../../../src/core/runner/opencode-models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,IAAI,oBAAoB,GAAoB,IAAI,CAAC;AAEjD;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,IAAI,oBAAoB;QAAE,OAAO,oBAAoB,CAAC;IAEtD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,6BAA6B,EAAE;YACrD,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,oBAAoB,GAAG,MAAM;aAC1B,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;aAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QAE9D,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IAEnC,yDAAyD;IACzD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAChD,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAEnD,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK,CAC9E,CAAC;IAEF,IAAI,YAAY;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAEzC,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,KAAK,EAAE,UAAU,KAAK,yEAAyE;KAChG,CAAC;AACJ,CAAC;AAED,MAAM,aAAa,GAA2B;IAC5C,iBAAiB,EAAE,6BAA6B;IAChD,mBAAmB,EAAE,6BAA6B;IAClD,eAAe,EAAE,2BAA2B;IAC5C,iBAAiB,EAAE,2BAA2B;IAC9C,gBAAgB,EAAE,4BAA4B;IAC9C,kBAAkB,EAAE,4BAA4B;IAChD,QAAQ,EAAE,eAAe;IACzB,OAAO,EAAE,cAAc;IACvB,EAAE,EAAE,WAAW;IACf,SAAS,EAAE,gBAAgB;IAC3B,KAAK,EAAE,cAAc;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,IAAI,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;IACtD,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAEtC,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEzC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC;IAC7F,OAAO,QAAQ,IAAI,KAAK,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner type and config contracts.
|
|
3
|
+
*
|
|
4
|
+
* This file intentionally contains only shared types so runtime modules can stay
|
|
5
|
+
* loosely coupled and easy to test independently.
|
|
6
|
+
*/
|
|
7
|
+
export type RunnerType = "opencode" | "claude" | "codex" | "custom";
|
|
8
|
+
export interface RunnerConfig {
|
|
9
|
+
type: RunnerType;
|
|
10
|
+
command: string;
|
|
11
|
+
model: string;
|
|
12
|
+
env: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export interface ModelResolutionInput {
|
|
15
|
+
cliModel?: string;
|
|
16
|
+
agentModel?: string;
|
|
17
|
+
defaultModel: string;
|
|
18
|
+
command: string;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/runner/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
package/dist/core/runner.d.ts
CHANGED
|
@@ -1,49 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Backward-compatible runner facade.
|
|
3
|
+
*
|
|
4
|
+
* The runner implementation is split into focused modules under ./runner/ to
|
|
5
|
+
* keep detection, model resolution, and provider-specific behavior isolated.
|
|
4
6
|
*/
|
|
5
|
-
export
|
|
6
|
-
export interface RunnerConfig {
|
|
7
|
-
type: RunnerType;
|
|
8
|
-
command: string;
|
|
9
|
-
model: string;
|
|
10
|
-
env: Record<string, string>;
|
|
11
|
-
}
|
|
12
|
-
export interface ModelResolutionInput {
|
|
13
|
-
cliModel?: string;
|
|
14
|
-
agentModel?: string;
|
|
15
|
-
defaultModel: string;
|
|
16
|
-
command: string;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Detects the runner type from the command
|
|
20
|
-
*/
|
|
21
|
-
export declare function detectRunner(command: string): RunnerType;
|
|
22
|
-
/**
|
|
23
|
-
* Resolves the effective model from CLI > agent config > defaults
|
|
24
|
-
*/
|
|
25
|
-
export declare function resolveModel(input: ModelResolutionInput): string;
|
|
26
|
-
/**
|
|
27
|
-
* Gets available OpenCode models (cached)
|
|
28
|
-
*/
|
|
29
|
-
export declare function getOpenCodeModels(): string[];
|
|
30
|
-
/**
|
|
31
|
-
* Validates that a model is available in OpenCode
|
|
32
|
-
*/
|
|
33
|
-
export declare function validateOpenCodeModel(model: string): {
|
|
34
|
-
valid: boolean;
|
|
35
|
-
error?: string;
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Normalizes a model name for OpenCode
|
|
39
|
-
* e.g., "claude-sonnet-4" -> "anthropic/claude-sonnet-4-5" if that's what OpenCode expects
|
|
40
|
-
*/
|
|
41
|
-
export declare function normalizeOpenCodeModel(model: string): string;
|
|
42
|
-
/**
|
|
43
|
-
* Builds the complete runner configuration including environment variables
|
|
44
|
-
*/
|
|
45
|
-
export declare function buildRunnerConfig(input: ModelResolutionInput): RunnerConfig;
|
|
46
|
-
/**
|
|
47
|
-
* Gets a human-readable runner name
|
|
48
|
-
*/
|
|
49
|
-
export declare function getRunnerDisplayName(runnerType: RunnerType): string;
|
|
7
|
+
export * from "./runner/index.js";
|
package/dist/core/runner.js
CHANGED
|
@@ -1,170 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Backward-compatible runner facade.
|
|
3
|
+
*
|
|
4
|
+
* The runner implementation is split into focused modules under ./runner/ to
|
|
5
|
+
* keep detection, model resolution, and provider-specific behavior isolated.
|
|
4
6
|
*/
|
|
5
|
-
|
|
6
|
-
// ============================================================================
|
|
7
|
-
// Runner Detection
|
|
8
|
-
// ============================================================================
|
|
9
|
-
/**
|
|
10
|
-
* Detects the runner type from the command
|
|
11
|
-
*/
|
|
12
|
-
export function detectRunner(command) {
|
|
13
|
-
const cmd = command.toLowerCase().trim();
|
|
14
|
-
if (cmd === "opencode" || cmd.startsWith("opencode ")) {
|
|
15
|
-
return "opencode";
|
|
16
|
-
}
|
|
17
|
-
if (cmd === "claude" || cmd.startsWith("claude ")) {
|
|
18
|
-
return "claude";
|
|
19
|
-
}
|
|
20
|
-
return "custom";
|
|
21
|
-
}
|
|
22
|
-
// ============================================================================
|
|
23
|
-
// Model Resolution
|
|
24
|
-
// ============================================================================
|
|
25
|
-
/**
|
|
26
|
-
* Resolves the effective model from CLI > agent config > defaults
|
|
27
|
-
*/
|
|
28
|
-
export function resolveModel(input) {
|
|
29
|
-
// Priority: CLI flag > agent config > default
|
|
30
|
-
return input.cliModel || input.agentModel || input.defaultModel;
|
|
31
|
-
}
|
|
32
|
-
// ============================================================================
|
|
33
|
-
// OpenCode Integration
|
|
34
|
-
// ============================================================================
|
|
35
|
-
let cachedOpenCodeModels = null;
|
|
36
|
-
/**
|
|
37
|
-
* Gets available OpenCode models (cached)
|
|
38
|
-
*/
|
|
39
|
-
export function getOpenCodeModels() {
|
|
40
|
-
if (cachedOpenCodeModels) {
|
|
41
|
-
return cachedOpenCodeModels;
|
|
42
|
-
}
|
|
43
|
-
try {
|
|
44
|
-
const output = execSync("opencode models 2>/dev/null", {
|
|
45
|
-
encoding: "utf-8",
|
|
46
|
-
timeout: 10000,
|
|
47
|
-
});
|
|
48
|
-
cachedOpenCodeModels = output
|
|
49
|
-
.split("\n")
|
|
50
|
-
.map((line) => line.trim())
|
|
51
|
-
.filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
52
|
-
return cachedOpenCodeModels;
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
// OpenCode not available or failed
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Validates that a model is available in OpenCode
|
|
61
|
-
*/
|
|
62
|
-
export function validateOpenCodeModel(model) {
|
|
63
|
-
const models = getOpenCodeModels();
|
|
64
|
-
// If we couldn't get models list, allow any (graceful degradation)
|
|
65
|
-
if (models.length === 0) {
|
|
66
|
-
return { valid: true };
|
|
67
|
-
}
|
|
68
|
-
if (models.includes(model)) {
|
|
69
|
-
return { valid: true };
|
|
70
|
-
}
|
|
71
|
-
// Check for partial match (e.g., "claude-sonnet-4" matches "anthropic/claude-sonnet-4")
|
|
72
|
-
const partialMatch = models.find((m) => m.endsWith(`/${model}`) || m === model || m.split("/").pop() === model);
|
|
73
|
-
if (partialMatch) {
|
|
74
|
-
return { valid: true };
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
valid: false,
|
|
78
|
-
error: `Model "${model}" not found in OpenCode. Run 'opencode models' to see available models.`,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
// Common model aliases to their full OpenCode names
|
|
82
|
-
const MODEL_ALIASES = {
|
|
83
|
-
"claude-sonnet-4": "anthropic/claude-sonnet-4-5",
|
|
84
|
-
"claude-sonnet-4-5": "anthropic/claude-sonnet-4-5",
|
|
85
|
-
"claude-opus-4": "anthropic/claude-opus-4-5",
|
|
86
|
-
"claude-opus-4-5": "anthropic/claude-opus-4-5",
|
|
87
|
-
"claude-haiku-4": "anthropic/claude-haiku-4-5",
|
|
88
|
-
"claude-haiku-4-5": "anthropic/claude-haiku-4-5",
|
|
89
|
-
"gpt-4o": "openai/gpt-4o",
|
|
90
|
-
"gpt-4": "openai/gpt-4",
|
|
91
|
-
o3: "openai/o3",
|
|
92
|
-
"o3-mini": "openai/o3-mini",
|
|
93
|
-
codex: "openai/codex",
|
|
94
|
-
};
|
|
95
|
-
/**
|
|
96
|
-
* Normalizes a model name for OpenCode
|
|
97
|
-
* e.g., "claude-sonnet-4" -> "anthropic/claude-sonnet-4-5" if that's what OpenCode expects
|
|
98
|
-
*/
|
|
99
|
-
export function normalizeOpenCodeModel(model) {
|
|
100
|
-
// Check hardcoded aliases first (works even if opencode not installed)
|
|
101
|
-
if (MODEL_ALIASES[model]) {
|
|
102
|
-
return MODEL_ALIASES[model];
|
|
103
|
-
}
|
|
104
|
-
// Already has provider prefix
|
|
105
|
-
if (model.includes("/")) {
|
|
106
|
-
return model;
|
|
107
|
-
}
|
|
108
|
-
const models = getOpenCodeModels();
|
|
109
|
-
// Direct match
|
|
110
|
-
if (models.includes(model)) {
|
|
111
|
-
return model;
|
|
112
|
-
}
|
|
113
|
-
// Try to find full path version
|
|
114
|
-
const fullPath = models.find((m) => m.endsWith(`/${model}`) || m.split("/").pop() === model);
|
|
115
|
-
return fullPath || model;
|
|
116
|
-
}
|
|
117
|
-
// ============================================================================
|
|
118
|
-
// Runner Configuration
|
|
119
|
-
// ============================================================================
|
|
120
|
-
/**
|
|
121
|
-
* Builds the complete runner configuration including environment variables
|
|
122
|
-
*/
|
|
123
|
-
export function buildRunnerConfig(input) {
|
|
124
|
-
const runnerType = detectRunner(input.command);
|
|
125
|
-
const model = resolveModel(input);
|
|
126
|
-
const env = {};
|
|
127
|
-
switch (runnerType) {
|
|
128
|
-
case "opencode": {
|
|
129
|
-
// Validate model for OpenCode
|
|
130
|
-
const validation = validateOpenCodeModel(model);
|
|
131
|
-
if (!validation.valid) {
|
|
132
|
-
console.warn(`Warning: ${validation.error}`);
|
|
133
|
-
}
|
|
134
|
-
// Normalize and set OPENCODE_MODEL
|
|
135
|
-
const normalizedModel = normalizeOpenCodeModel(model);
|
|
136
|
-
env.OPENCODE_MODEL = normalizedModel;
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
case "claude": {
|
|
140
|
-
// Claude CLI uses ANTHROPIC_MODEL or similar
|
|
141
|
-
// For now, just pass the model - Claude CLI will handle it
|
|
142
|
-
env.CLAUDE_MODEL = model;
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
case "custom":
|
|
146
|
-
// Custom runners don't get automatic model env
|
|
147
|
-
// User is responsible for configuring their tool
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
return {
|
|
151
|
-
type: runnerType,
|
|
152
|
-
command: input.command,
|
|
153
|
-
model,
|
|
154
|
-
env,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Gets a human-readable runner name
|
|
159
|
-
*/
|
|
160
|
-
export function getRunnerDisplayName(runnerType) {
|
|
161
|
-
switch (runnerType) {
|
|
162
|
-
case "opencode":
|
|
163
|
-
return "OpenCode";
|
|
164
|
-
case "claude":
|
|
165
|
-
return "Claude CLI";
|
|
166
|
-
case "custom":
|
|
167
|
-
return "Custom";
|
|
168
|
-
}
|
|
169
|
-
}
|
|
7
|
+
export * from "./runner/index.js";
|
|
170
8
|
//# sourceMappingURL=runner.js.map
|
package/dist/core/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type RunnerCommandKind = "opencode" | "claude" | "codex" | "other";
|
|
2
|
+
/**
|
|
3
|
+
* Classifies the interactive command so tmux startup can apply runner-specific behavior.
|
|
4
|
+
*/
|
|
5
|
+
export declare function detectRunnerCommandKind(command: string): RunnerCommandKind;
|
|
6
|
+
/**
|
|
7
|
+
* Prepares per-agent OpenCode storage and auth inheritance.
|
|
8
|
+
*/
|
|
9
|
+
export declare function prepareOpenCodeRuntime(agentName: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Builds the final interactive command with runner-specific model/session flags.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildInteractiveCommand(command: string, env?: Record<string, string | undefined>, opencodeSessionId?: string): string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Classifies the interactive command so tmux startup can apply runner-specific behavior.
|
|
6
|
+
*/
|
|
7
|
+
export function detectRunnerCommandKind(command) {
|
|
8
|
+
if (command === "opencode" || command.startsWith("opencode "))
|
|
9
|
+
return "opencode";
|
|
10
|
+
if (command === "claude" || command.startsWith("claude "))
|
|
11
|
+
return "claude";
|
|
12
|
+
if (command === "codex" || command.startsWith("codex "))
|
|
13
|
+
return "codex";
|
|
14
|
+
return "other";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Prepares per-agent OpenCode storage and auth inheritance.
|
|
18
|
+
*/
|
|
19
|
+
export function prepareOpenCodeRuntime(agentName) {
|
|
20
|
+
const agentDataDir = path.join(os.homedir(), ".agentmesh", "opencode-data", agentName);
|
|
21
|
+
const agentOpencodeDir = path.join(agentDataDir, "opencode");
|
|
22
|
+
if (!fs.existsSync(agentOpencodeDir)) {
|
|
23
|
+
fs.mkdirSync(agentOpencodeDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
const agentAuthPath = path.join(agentOpencodeDir, "auth.json");
|
|
26
|
+
const sourceAuthPath = path.join(os.homedir(), ".local", "share", "opencode", "auth.json");
|
|
27
|
+
if (!fs.existsSync(agentAuthPath) && fs.existsSync(sourceAuthPath)) {
|
|
28
|
+
try {
|
|
29
|
+
const auth = JSON.parse(fs.readFileSync(sourceAuthPath, "utf-8"));
|
|
30
|
+
delete auth.xai;
|
|
31
|
+
fs.writeFileSync(agentAuthPath, JSON.stringify(auth, null, 2));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Non-fatal — agent can authenticate manually later.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return agentDataDir;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Builds the final interactive command with runner-specific model/session flags.
|
|
41
|
+
*/
|
|
42
|
+
export function buildInteractiveCommand(command, env, opencodeSessionId) {
|
|
43
|
+
const kind = detectRunnerCommandKind(command);
|
|
44
|
+
let finalCommand = command;
|
|
45
|
+
if (kind === "opencode" &&
|
|
46
|
+
env?.OPENCODE_MODEL &&
|
|
47
|
+
!command.includes("--model") &&
|
|
48
|
+
!command.includes("-m ")) {
|
|
49
|
+
finalCommand = `${command} --model ${env.OPENCODE_MODEL}`;
|
|
50
|
+
}
|
|
51
|
+
if (kind === "claude" &&
|
|
52
|
+
env?.CLAUDE_MODEL &&
|
|
53
|
+
!command.includes("--model") &&
|
|
54
|
+
!command.includes("-m ")) {
|
|
55
|
+
finalCommand = `${command} --model ${env.CLAUDE_MODEL}`;
|
|
56
|
+
}
|
|
57
|
+
if (kind === "codex" &&
|
|
58
|
+
env?.CODEX_MODEL &&
|
|
59
|
+
!command.includes("--model") &&
|
|
60
|
+
!command.includes("-m ")) {
|
|
61
|
+
finalCommand = `${command} --model ${env.CODEX_MODEL}`;
|
|
62
|
+
}
|
|
63
|
+
if (kind === "opencode" &&
|
|
64
|
+
opencodeSessionId &&
|
|
65
|
+
!finalCommand.includes("--session") &&
|
|
66
|
+
!finalCommand.includes("--continue")) {
|
|
67
|
+
finalCommand = `${finalCommand} --session ${opencodeSessionId} --continue`;
|
|
68
|
+
console.log(`[TMUX] Resuming OpenCode session: ${opencodeSessionId}`);
|
|
69
|
+
}
|
|
70
|
+
return finalCommand;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=tmux-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux-runtime.js","sourceRoot":"","sources":["../../src/core/tmux-runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/core/tmux.d.ts
CHANGED
|
@@ -5,12 +5,18 @@ export interface SessionEnv {
|
|
|
5
5
|
AGENTMESH_AGENT_ID?: string;
|
|
6
6
|
OPENCODE_MODEL?: string;
|
|
7
7
|
CLAUDE_MODEL?: string;
|
|
8
|
+
CODEX_MODEL?: string;
|
|
8
9
|
[key: string]: string | undefined;
|
|
9
10
|
}
|
|
10
11
|
export declare function createSession(agentName: string, command: string, workdir?: string, env?: SessionEnv, opencodeSessionId?: string): boolean;
|
|
11
12
|
export declare function setSessionEnvironment(sessionName: string, env: SessionEnv): boolean;
|
|
12
13
|
export declare function updateSessionEnvironment(agentName: string, env: SessionEnv): boolean;
|
|
13
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Kill a list of PIDs with SIGTERM, then escalate to SIGKILL after a timeout.
|
|
16
|
+
* Returns true if all processes were confirmed dead.
|
|
17
|
+
*/
|
|
18
|
+
export declare function killProcessTree(pids: number[], timeoutMs?: number): boolean;
|
|
19
|
+
export declare function destroySession(agentName: string, childPids?: number[]): boolean;
|
|
14
20
|
export declare function sendKeys(agentName: string, message: string): boolean;
|
|
15
21
|
export declare function attachSession(agentName: string): void;
|
|
16
22
|
export declare function listSessions(): string[];
|
package/dist/core/tmux.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { execFileSync, execSync, spawn } from "node:child_process";
|
|
2
|
-
import
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
2
|
+
import { buildInteractiveCommand, detectRunnerCommandKind, prepareOpenCodeRuntime, } from "./tmux-runtime.js";
|
|
5
3
|
const SESSION_PREFIX = "agentmesh-";
|
|
6
4
|
export function getSessionName(agentName) {
|
|
7
5
|
return `${SESSION_PREFIX}${agentName}`;
|
|
@@ -22,34 +20,20 @@ export function createSession(agentName, command, workdir, env, opencodeSessionI
|
|
|
22
20
|
return false;
|
|
23
21
|
}
|
|
24
22
|
try {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (!fs.existsSync(agentOpencodeDir)) {
|
|
31
|
-
fs.mkdirSync(agentOpencodeDir, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
// Copy auth.json from default OpenCode data dir so agents inherit API keys.
|
|
34
|
-
// Strips xAI provider to prevent OpenCode from defaulting to non-Anthropic models.
|
|
35
|
-
const agentAuthPath = path.join(agentOpencodeDir, "auth.json");
|
|
36
|
-
const sourceAuthPath = path.join(os.homedir(), ".local", "share", "opencode", "auth.json");
|
|
37
|
-
if (!fs.existsSync(agentAuthPath) && fs.existsSync(sourceAuthPath)) {
|
|
38
|
-
try {
|
|
39
|
-
const auth = JSON.parse(fs.readFileSync(sourceAuthPath, "utf-8"));
|
|
40
|
-
delete auth.xai;
|
|
41
|
-
fs.writeFileSync(agentAuthPath, JSON.stringify(auth, null, 2));
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
// Non-fatal — agent will just need manual auth
|
|
45
|
-
}
|
|
23
|
+
const commandKind = detectRunnerCommandKind(command);
|
|
24
|
+
const isOpenCodeCommand = commandKind === "opencode";
|
|
25
|
+
let agentDataDir = "";
|
|
26
|
+
if (isOpenCodeCommand) {
|
|
27
|
+
agentDataDir = prepareOpenCodeRuntime(agentName);
|
|
46
28
|
}
|
|
47
29
|
// Build environment prefix for the command
|
|
48
30
|
// This ensures env vars are set BEFORE the process starts
|
|
49
|
-
const mergedEnv =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
31
|
+
const mergedEnv = isOpenCodeCommand
|
|
32
|
+
? {
|
|
33
|
+
XDG_DATA_HOME: agentDataDir,
|
|
34
|
+
...env,
|
|
35
|
+
}
|
|
36
|
+
: { ...env };
|
|
53
37
|
let envPrefix = "";
|
|
54
38
|
const envParts = [];
|
|
55
39
|
for (const [key, value] of Object.entries(mergedEnv)) {
|
|
@@ -62,22 +46,7 @@ export function createSession(agentName, command, workdir, env, opencodeSessionI
|
|
|
62
46
|
if (envParts.length > 0) {
|
|
63
47
|
envPrefix = `${envParts.join(" ")} `;
|
|
64
48
|
}
|
|
65
|
-
|
|
66
|
-
let finalCommand = command;
|
|
67
|
-
if (env?.OPENCODE_MODEL && (command === "opencode" || command.startsWith("opencode "))) {
|
|
68
|
-
// Check if --model is already in the command
|
|
69
|
-
if (!command.includes("--model") && !command.includes("-m ")) {
|
|
70
|
-
finalCommand = `${command} --model ${env.OPENCODE_MODEL}`;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Append --session --continue flags for native session resume
|
|
74
|
-
if (opencodeSessionId &&
|
|
75
|
-
(finalCommand === "opencode" || finalCommand.startsWith("opencode ")) &&
|
|
76
|
-
!finalCommand.includes("--session") &&
|
|
77
|
-
!finalCommand.includes("--continue")) {
|
|
78
|
-
finalCommand = `${finalCommand} --session ${opencodeSessionId} --continue`;
|
|
79
|
-
console.log(`[TMUX] Resuming OpenCode session: ${opencodeSessionId}`);
|
|
80
|
-
}
|
|
49
|
+
const finalCommand = buildInteractiveCommand(command, env, opencodeSessionId);
|
|
81
50
|
const fullCommand = `${envPrefix}${finalCommand}`;
|
|
82
51
|
// Set reasonable terminal size for TUI applications
|
|
83
52
|
const args = ["new-session", "-d", "-s", sessionName, "-x", "200", "-y", "50"];
|
|
@@ -119,8 +88,69 @@ export function updateSessionEnvironment(agentName, env) {
|
|
|
119
88
|
}
|
|
120
89
|
return setSessionEnvironment(sessionName, env);
|
|
121
90
|
}
|
|
122
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Kill a list of PIDs with SIGTERM, then escalate to SIGKILL after a timeout.
|
|
93
|
+
* Returns true if all processes were confirmed dead.
|
|
94
|
+
*/
|
|
95
|
+
export function killProcessTree(pids, timeoutMs = 5000) {
|
|
96
|
+
if (pids.length === 0) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
const alive = (pid) => {
|
|
100
|
+
try {
|
|
101
|
+
process.kill(pid, 0);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const livePids = pids.filter(alive);
|
|
109
|
+
if (livePids.length === 0) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
console.log(`[CLEANUP] Sending SIGTERM to ${livePids.length} processes: ${livePids.join(", ")}`);
|
|
113
|
+
// Step 1: SIGTERM
|
|
114
|
+
for (const pid of livePids) {
|
|
115
|
+
try {
|
|
116
|
+
process.kill(pid, "SIGTERM");
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Already gone
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Step 2: Poll for graceful exit
|
|
123
|
+
const deadline = Date.now() + timeoutMs;
|
|
124
|
+
while (Date.now() < deadline) {
|
|
125
|
+
const stillAlive = livePids.filter(alive);
|
|
126
|
+
if (stillAlive.length === 0) {
|
|
127
|
+
console.log("[CLEANUP] All processes terminated gracefully");
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
// Busy-wait in 100ms increments
|
|
131
|
+
execSync("sleep 0.1");
|
|
132
|
+
}
|
|
133
|
+
// Step 3: SIGKILL stragglers
|
|
134
|
+
const stragglers = livePids.filter(alive);
|
|
135
|
+
if (stragglers.length > 0) {
|
|
136
|
+
console.log(`[CLEANUP] Force killing ${stragglers.length} processes with SIGKILL: ${stragglers.join(", ")}`);
|
|
137
|
+
for (const pid of stragglers) {
|
|
138
|
+
try {
|
|
139
|
+
process.kill(pid, "SIGKILL");
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Ignore
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return livePids.filter(alive).length === 0;
|
|
147
|
+
}
|
|
148
|
+
export function destroySession(agentName, childPids) {
|
|
123
149
|
const sessionName = getSessionName(agentName);
|
|
150
|
+
// Kill child processes first (opencode + LSP servers) before killing the tmux session
|
|
151
|
+
if (childPids && childPids.length > 0) {
|
|
152
|
+
killProcessTree(childPids);
|
|
153
|
+
}
|
|
124
154
|
if (!sessionExists(sessionName)) {
|
|
125
155
|
return true; // Already gone
|
|
126
156
|
}
|