@agentmeshhq/agent 0.1.8 → 0.1.9
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__/runner.test.d.ts +1 -0
- package/dist/__tests__/runner.test.js +87 -0
- package/dist/__tests__/runner.test.js.map +1 -0
- package/dist/cli/index.js +3 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/restart.d.ts +4 -1
- package/dist/cli/restart.js +4 -2
- package/dist/cli/restart.js.map +1 -1
- package/dist/cli/status.js +5 -1
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/whoami.js +17 -0
- package/dist/cli/whoami.js.map +1 -1
- package/dist/config/schema.d.ts +5 -0
- package/dist/core/daemon.d.ts +5 -0
- package/dist/core/daemon.js +55 -2
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/runner.d.ts +49 -0
- package/dist/core/runner.js +148 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/core/tmux.d.ts +3 -0
- package/dist/core/tmux.js +23 -7
- package/dist/core/tmux.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/runner.test.ts +105 -0
- package/src/cli/index.ts +3 -2
- package/src/cli/restart.ts +9 -2
- package/src/cli/status.ts +6 -1
- package/src/cli/whoami.ts +18 -0
- package/src/config/schema.ts +6 -0
- package/src/core/daemon.ts +62 -0
- package/src/core/runner.ts +200 -0
- package/src/core/tmux.ts +28 -9
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner Module
|
|
3
|
+
* Handles model resolution, validation, and runner-specific configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Types
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
export type RunnerType = "opencode" | "claude" | "custom";
|
|
13
|
+
|
|
14
|
+
export interface RunnerConfig {
|
|
15
|
+
type: RunnerType;
|
|
16
|
+
command: string;
|
|
17
|
+
model: string;
|
|
18
|
+
env: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ModelResolutionInput {
|
|
22
|
+
cliModel?: string;
|
|
23
|
+
agentModel?: string;
|
|
24
|
+
defaultModel: string;
|
|
25
|
+
command: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Runner Detection
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Detects the runner type from the command
|
|
34
|
+
*/
|
|
35
|
+
export function detectRunner(command: string): RunnerType {
|
|
36
|
+
const cmd = command.toLowerCase().trim();
|
|
37
|
+
|
|
38
|
+
if (cmd === "opencode" || cmd.startsWith("opencode ")) {
|
|
39
|
+
return "opencode";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (cmd === "claude" || cmd.startsWith("claude ")) {
|
|
43
|
+
return "claude";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return "custom";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Model Resolution
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Resolves the effective model from CLI > agent config > defaults
|
|
55
|
+
*/
|
|
56
|
+
export function resolveModel(input: ModelResolutionInput): string {
|
|
57
|
+
// Priority: CLI flag > agent config > default
|
|
58
|
+
return input.cliModel || input.agentModel || input.defaultModel;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// OpenCode Integration
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
let cachedOpenCodeModels: string[] | null = null;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Gets available OpenCode models (cached)
|
|
69
|
+
*/
|
|
70
|
+
export function getOpenCodeModels(): string[] {
|
|
71
|
+
if (cachedOpenCodeModels) {
|
|
72
|
+
return cachedOpenCodeModels;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const output = execSync("opencode models 2>/dev/null", {
|
|
77
|
+
encoding: "utf-8",
|
|
78
|
+
timeout: 10000,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
cachedOpenCodeModels = output
|
|
82
|
+
.split("\n")
|
|
83
|
+
.map((line) => line.trim())
|
|
84
|
+
.filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
85
|
+
|
|
86
|
+
return cachedOpenCodeModels;
|
|
87
|
+
} catch {
|
|
88
|
+
// OpenCode not available or failed
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validates that a model is available in OpenCode
|
|
95
|
+
*/
|
|
96
|
+
export function validateOpenCodeModel(model: string): { valid: boolean; error?: string } {
|
|
97
|
+
const models = getOpenCodeModels();
|
|
98
|
+
|
|
99
|
+
// If we couldn't get models list, allow any (graceful degradation)
|
|
100
|
+
if (models.length === 0) {
|
|
101
|
+
return { valid: true };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (models.includes(model)) {
|
|
105
|
+
return { valid: true };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check for partial match (e.g., "claude-sonnet-4" matches "anthropic/claude-sonnet-4")
|
|
109
|
+
const partialMatch = models.find(
|
|
110
|
+
(m) => m.endsWith(`/${model}`) || m === model || m.split("/").pop() === model,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (partialMatch) {
|
|
114
|
+
return { valid: true };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
valid: false,
|
|
119
|
+
error: `Model "${model}" not found in OpenCode. Run 'opencode models' to see available models.`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Normalizes a model name for OpenCode
|
|
125
|
+
* e.g., "claude-sonnet-4" -> "anthropic/claude-sonnet-4" if that's what OpenCode expects
|
|
126
|
+
*/
|
|
127
|
+
export function normalizeOpenCodeModel(model: string): string {
|
|
128
|
+
const models = getOpenCodeModels();
|
|
129
|
+
|
|
130
|
+
// Direct match
|
|
131
|
+
if (models.includes(model)) {
|
|
132
|
+
return model;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Try to find full path version
|
|
136
|
+
const fullPath = models.find((m) => m.endsWith(`/${model}`) || m.split("/").pop() === model);
|
|
137
|
+
|
|
138
|
+
return fullPath || model;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ============================================================================
|
|
142
|
+
// Runner Configuration
|
|
143
|
+
// ============================================================================
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Builds the complete runner configuration including environment variables
|
|
147
|
+
*/
|
|
148
|
+
export function buildRunnerConfig(input: ModelResolutionInput): RunnerConfig {
|
|
149
|
+
const runnerType = detectRunner(input.command);
|
|
150
|
+
const model = resolveModel(input);
|
|
151
|
+
const env: Record<string, string> = {};
|
|
152
|
+
|
|
153
|
+
switch (runnerType) {
|
|
154
|
+
case "opencode": {
|
|
155
|
+
// Validate model for OpenCode
|
|
156
|
+
const validation = validateOpenCodeModel(model);
|
|
157
|
+
if (!validation.valid) {
|
|
158
|
+
console.warn(`Warning: ${validation.error}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Normalize and set OPENCODE_MODEL
|
|
162
|
+
const normalizedModel = normalizeOpenCodeModel(model);
|
|
163
|
+
env.OPENCODE_MODEL = normalizedModel;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
case "claude": {
|
|
168
|
+
// Claude CLI uses ANTHROPIC_MODEL or similar
|
|
169
|
+
// For now, just pass the model - Claude CLI will handle it
|
|
170
|
+
env.CLAUDE_MODEL = model;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
case "custom":
|
|
175
|
+
// Custom runners don't get automatic model env
|
|
176
|
+
// User is responsible for configuring their tool
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
type: runnerType,
|
|
182
|
+
command: input.command,
|
|
183
|
+
model,
|
|
184
|
+
env,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Gets a human-readable runner name
|
|
190
|
+
*/
|
|
191
|
+
export function getRunnerDisplayName(runnerType: RunnerType): string {
|
|
192
|
+
switch (runnerType) {
|
|
193
|
+
case "opencode":
|
|
194
|
+
return "OpenCode";
|
|
195
|
+
case "claude":
|
|
196
|
+
return "Claude CLI";
|
|
197
|
+
case "custom":
|
|
198
|
+
return "Custom";
|
|
199
|
+
}
|
|
200
|
+
}
|
package/src/core/tmux.ts
CHANGED
|
@@ -18,6 +18,9 @@ export function sessionExists(sessionName: string): boolean {
|
|
|
18
18
|
export interface SessionEnv {
|
|
19
19
|
AGENT_TOKEN?: string;
|
|
20
20
|
AGENTMESH_AGENT_ID?: string;
|
|
21
|
+
OPENCODE_MODEL?: string;
|
|
22
|
+
CLAUDE_MODEL?: string;
|
|
23
|
+
[key: string]: string | undefined;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
export function createSession(
|
|
@@ -34,17 +37,36 @@ export function createSession(
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
try {
|
|
40
|
+
// Build environment prefix for the command
|
|
41
|
+
// This ensures env vars are set BEFORE the process starts
|
|
42
|
+
let envPrefix = "";
|
|
43
|
+
if (env) {
|
|
44
|
+
const envParts: string[] = [];
|
|
45
|
+
for (const [key, value] of Object.entries(env)) {
|
|
46
|
+
if (value !== undefined && value !== "") {
|
|
47
|
+
// Escape special characters in value
|
|
48
|
+
const escapedValue = value.replace(/"/g, '\\"');
|
|
49
|
+
envParts.push(`${key}="${escapedValue}"`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (envParts.length > 0) {
|
|
53
|
+
envPrefix = envParts.join(" ") + " ";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const fullCommand = `${envPrefix}${command}`;
|
|
58
|
+
|
|
37
59
|
const args = ["new-session", "-d", "-s", sessionName];
|
|
38
60
|
|
|
39
61
|
if (workdir) {
|
|
40
62
|
args.push("-c", workdir);
|
|
41
63
|
}
|
|
42
64
|
|
|
43
|
-
args.push(
|
|
65
|
+
args.push(fullCommand);
|
|
44
66
|
|
|
45
67
|
execSync(`tmux ${args.join(" ")}`);
|
|
46
68
|
|
|
47
|
-
//
|
|
69
|
+
// Also set session environment for any subsequent processes/refreshes
|
|
48
70
|
if (env) {
|
|
49
71
|
setSessionEnvironment(sessionName, env);
|
|
50
72
|
}
|
|
@@ -58,13 +80,10 @@ export function createSession(
|
|
|
58
80
|
|
|
59
81
|
export function setSessionEnvironment(sessionName: string, env: SessionEnv): boolean {
|
|
60
82
|
try {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
execSync(
|
|
66
|
-
`tmux set-environment -t "${sessionName}" AGENTMESH_AGENT_ID "${env.AGENTMESH_AGENT_ID}"`,
|
|
67
|
-
);
|
|
83
|
+
for (const [key, value] of Object.entries(env)) {
|
|
84
|
+
if (value !== undefined && value !== "") {
|
|
85
|
+
execSync(`tmux set-environment -t "${sessionName}" ${key} "${value}"`);
|
|
86
|
+
}
|
|
68
87
|
}
|
|
69
88
|
return true;
|
|
70
89
|
} catch (error) {
|