@dsiloed/silo-link 1.5.3 → 1.6.3
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 +41 -5
- package/dist/api/dsiloed-client.d.ts.map +1 -1
- package/dist/api/dsiloed-client.js +7 -1
- package/dist/api/dsiloed-client.js.map +1 -1
- package/dist/cli/claude-md-block.d.ts +3 -1
- package/dist/cli/claude-md-block.d.ts.map +1 -1
- package/dist/cli/claude-md-block.js +10 -39
- package/dist/cli/claude-md-block.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +45 -14
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/daemon.d.ts +3 -3
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +12 -8
- package/dist/cli/daemon.js.map +1 -1
- package/dist/config/config-manager.d.ts +24 -0
- package/dist/config/config-manager.d.ts.map +1 -1
- package/dist/config/config-manager.js +168 -11
- package/dist/config/config-manager.js.map +1 -1
- package/dist/config/jwt-generator.d.ts +11 -6
- package/dist/config/jwt-generator.d.ts.map +1 -1
- package/dist/config/jwt-generator.js +20 -39
- package/dist/config/jwt-generator.js.map +1 -1
- package/dist/core/base-tmux-launcher.d.ts +13 -0
- package/dist/core/base-tmux-launcher.d.ts.map +1 -1
- package/dist/core/base-tmux-launcher.js +96 -4
- package/dist/core/base-tmux-launcher.js.map +1 -1
- package/dist/core/bridge.d.ts.map +1 -1
- package/dist/core/bridge.js +50 -11
- package/dist/core/bridge.js.map +1 -1
- package/dist/core/claude-launcher.d.ts +46 -0
- package/dist/core/claude-launcher.d.ts.map +1 -1
- package/dist/core/claude-launcher.js +259 -29
- package/dist/core/claude-launcher.js.map +1 -1
- package/dist/core/gemini-launcher.d.ts.map +1 -1
- package/dist/core/gemini-launcher.js +36 -13
- package/dist/core/gemini-launcher.js.map +1 -1
- package/dist/mcp/tools/register-tools.d.ts.map +1 -1
- package/dist/mcp/tools/register-tools.js +23 -18
- package/dist/mcp/tools/register-tools.js.map +1 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -10,18 +10,18 @@ function getConfigPath(env) {
|
|
|
10
10
|
const DEFAULT_CONFIG = {
|
|
11
11
|
host: 'https://www.dsiloed.com',
|
|
12
12
|
tenant_id: 'harmoniq',
|
|
13
|
-
shared_key: '',
|
|
14
13
|
user_sub: '',
|
|
14
|
+
jwt: '',
|
|
15
15
|
mcp_port: 3579,
|
|
16
16
|
reconnect_interval_ms: 5000,
|
|
17
17
|
max_reconnect_attempts: 20,
|
|
18
18
|
projects_path: '',
|
|
19
19
|
claude_command: 'claude',
|
|
20
|
-
claude_auto_respawn:
|
|
20
|
+
claude_auto_respawn: true,
|
|
21
21
|
claude_idle_timeout_ms: 30000,
|
|
22
22
|
claude_session_prompt: 'Register with SiloLink as "portablemind" and enter the poll loop.',
|
|
23
23
|
gemini_command: 'gemini',
|
|
24
|
-
gemini_auto_respawn:
|
|
24
|
+
gemini_auto_respawn: true,
|
|
25
25
|
gemini_idle_timeout_ms: 30000,
|
|
26
26
|
gemini_session_prompt: 'Register with SiloLink as "portablemind" and enter the poll loop.',
|
|
27
27
|
};
|
|
@@ -38,15 +38,134 @@ export function loadConfig(env) {
|
|
|
38
38
|
}
|
|
39
39
|
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
40
40
|
const parsed = JSON.parse(raw);
|
|
41
|
-
|
|
41
|
+
const merged = { ...DEFAULT_CONFIG, ...parsed };
|
|
42
|
+
// Per-env staging dir keeps each agent's project-scoped MCP config isolated
|
|
43
|
+
// (see SiloLinkConfig.staging_path). Default only computed when not set
|
|
44
|
+
// explicitly so existing configs keep working.
|
|
45
|
+
if (!merged.staging_path) {
|
|
46
|
+
const slug = env || 'default';
|
|
47
|
+
merged.staging_path = path.join(CONFIG_DIR, 'staging', slug);
|
|
48
|
+
}
|
|
49
|
+
return merged;
|
|
42
50
|
}
|
|
43
51
|
export function saveConfig(config, env) {
|
|
44
52
|
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
45
|
-
|
|
53
|
+
const configPath = getConfigPath(env);
|
|
54
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
55
|
+
// JWT is a credential — make sure the file is only readable by the owner.
|
|
56
|
+
try {
|
|
57
|
+
fs.chmodSync(configPath, 0o600);
|
|
58
|
+
}
|
|
59
|
+
catch { /* best-effort */ }
|
|
46
60
|
}
|
|
47
61
|
export function configExists(env) {
|
|
48
62
|
return fs.existsSync(getConfigPath(env));
|
|
49
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* POST {host}/api/v1/users/login with the user's credentials and Tenant-Id
|
|
66
|
+
* header. Returns the JWT on success. Throws with a descriptive message
|
|
67
|
+
* on failure.
|
|
68
|
+
*/
|
|
69
|
+
export async function login(opts) {
|
|
70
|
+
const isEmail = opts.user_sub.includes('@');
|
|
71
|
+
const body = {
|
|
72
|
+
password: opts.password,
|
|
73
|
+
};
|
|
74
|
+
body[isEmail ? 'email' : 'username'] = opts.user_sub;
|
|
75
|
+
let res;
|
|
76
|
+
try {
|
|
77
|
+
res = await fetch(`${opts.host}/api/v1/users/login`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers: {
|
|
80
|
+
'Content-Type': 'application/json',
|
|
81
|
+
'Tenant-Id': opts.tenant_id,
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify(body),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
throw new Error(`Could not reach ${opts.host}: ${err.message}`);
|
|
88
|
+
}
|
|
89
|
+
let data;
|
|
90
|
+
try {
|
|
91
|
+
data = (await res.json());
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
throw new Error(`Login failed (HTTP ${res.status}) — non-JSON response`);
|
|
95
|
+
}
|
|
96
|
+
if (!res.ok || !data.success || !data.token) {
|
|
97
|
+
throw new Error(data.message || `Login failed (HTTP ${res.status})`);
|
|
98
|
+
}
|
|
99
|
+
return { token: data.token, requires_password_change: data.requires_password_change };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Read a line from stdin without echoing — used to collect a password
|
|
103
|
+
* without printing it to the terminal.
|
|
104
|
+
*/
|
|
105
|
+
function askSilent(prompt) {
|
|
106
|
+
return new Promise((resolve) => {
|
|
107
|
+
process.stdout.write(prompt);
|
|
108
|
+
const stdin = process.stdin;
|
|
109
|
+
const wasRaw = stdin.isTTY ? stdin.isRaw === true : false;
|
|
110
|
+
if (stdin.isTTY)
|
|
111
|
+
stdin.setRawMode(true);
|
|
112
|
+
stdin.resume();
|
|
113
|
+
stdin.setEncoding('utf8');
|
|
114
|
+
const ETX = String.fromCharCode(3); // Ctrl-C
|
|
115
|
+
const BS = String.fromCharCode(8); // backspace
|
|
116
|
+
const DEL = String.fromCharCode(127); // DEL (terminal backspace)
|
|
117
|
+
let buffer = '';
|
|
118
|
+
const onData = (chunk) => {
|
|
119
|
+
for (const ch of chunk) {
|
|
120
|
+
if (ch === '\r' || ch === '\n') {
|
|
121
|
+
stdin.removeListener('data', onData);
|
|
122
|
+
if (stdin.isTTY)
|
|
123
|
+
stdin.setRawMode(wasRaw);
|
|
124
|
+
stdin.pause();
|
|
125
|
+
process.stdout.write('\n');
|
|
126
|
+
resolve(buffer);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (ch === ETX) {
|
|
130
|
+
process.stdout.write('\n');
|
|
131
|
+
process.exit(130);
|
|
132
|
+
}
|
|
133
|
+
if (ch === BS || ch === DEL) {
|
|
134
|
+
buffer = buffer.slice(0, -1);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
buffer += ch;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
stdin.on('data', onData);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
export function nonInteractiveConfig(opts) {
|
|
144
|
+
if (!opts.host || !opts.tenant_id || !opts.user_sub || !opts.jwt) {
|
|
145
|
+
throw new Error('Non-interactive config requires --host, --tenant, --user-sub, and --jwt.');
|
|
146
|
+
}
|
|
147
|
+
if (opts.identity_type && opts.identity_type !== 'user' && opts.identity_type !== 'agent') {
|
|
148
|
+
throw new Error('identity_type must be "user" or "agent".');
|
|
149
|
+
}
|
|
150
|
+
let existing = DEFAULT_CONFIG;
|
|
151
|
+
try {
|
|
152
|
+
existing = loadConfig(opts.env);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// first-time setup
|
|
156
|
+
}
|
|
157
|
+
const config = {
|
|
158
|
+
...existing,
|
|
159
|
+
host: opts.host,
|
|
160
|
+
tenant_id: opts.tenant_id,
|
|
161
|
+
user_sub: opts.user_sub,
|
|
162
|
+
jwt: opts.jwt,
|
|
163
|
+
mcp_port: opts.mcp_port ?? existing.mcp_port,
|
|
164
|
+
projects_path: opts.projects_path ?? existing.projects_path,
|
|
165
|
+
identity_type: opts.identity_type ?? existing.identity_type ?? 'user',
|
|
166
|
+
};
|
|
167
|
+
saveConfig(config, opts.env);
|
|
168
|
+
}
|
|
50
169
|
export async function interactiveConfig(env) {
|
|
51
170
|
const rl = readline.createInterface({
|
|
52
171
|
input: process.stdin,
|
|
@@ -67,25 +186,63 @@ export async function interactiveConfig(env) {
|
|
|
67
186
|
}
|
|
68
187
|
const envLabel = env ? ` (${env})` : '';
|
|
69
188
|
console.log(`\nSiloLink Configuration${envLabel}\n`);
|
|
189
|
+
// Identity type — humans log in with username/password and we mint a
|
|
190
|
+
// JWT against /api/v1/users/login; agents paste the JWT issued from
|
|
191
|
+
// the LLM Manager UI (POST /llm_agents/:id/issue_silolink_jwt) so we
|
|
192
|
+
// never need their User credentials.
|
|
193
|
+
const identityRaw = await ask('Identity type — (u)ser or (a)gent', 'u');
|
|
194
|
+
const isAgent = identityRaw.trim().toLowerCase().startsWith('a');
|
|
70
195
|
const host = await ask('DSiloed host', existing.host);
|
|
71
|
-
const tenant_id = await ask('Tenant ID', existing.tenant_id);
|
|
72
|
-
const
|
|
73
|
-
const user_sub = await ask('User login (email or username)', existing.user_sub || undefined);
|
|
196
|
+
const tenant_id = await ask('Tenant ID (enterprise identifier)', existing.tenant_id);
|
|
197
|
+
const user_sub = await ask(isAgent ? 'Agent name (sub claim from the issued token)' : 'User login (email or username)', existing.user_sub || undefined);
|
|
74
198
|
const mcp_port_str = await ask('MCP port', String(existing.mcp_port));
|
|
75
199
|
const mcp_port = parseInt(mcp_port_str, 10) || existing.mcp_port;
|
|
76
200
|
const projects_path = await ask('Projects path (where your repos live locally)', existing.projects_path || undefined);
|
|
201
|
+
rl.close();
|
|
202
|
+
let token;
|
|
203
|
+
let requires_password_change = false;
|
|
204
|
+
if (isAgent) {
|
|
205
|
+
token = (await askSilent('Agent JWT (paste from LLM Manager → Agent → SiloLink Authentication): ')).trim();
|
|
206
|
+
if (!token) {
|
|
207
|
+
console.error('\nAgent JWT is required. Generate one in the LLM Manager and re-run silolink config.');
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
const password = await askSilent('Password (not stored): ');
|
|
213
|
+
if (!password) {
|
|
214
|
+
console.error('\nPassword is required to obtain a JWT. Aborting.');
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
console.log(`\nLogging in to ${host} as ${user_sub}...`);
|
|
218
|
+
try {
|
|
219
|
+
const result = await login({ host, tenant_id, user_sub, password });
|
|
220
|
+
token = result.token;
|
|
221
|
+
requires_password_change = result.requires_password_change === true;
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
console.error(`Login failed: ${err.message}`);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
77
228
|
const config = {
|
|
78
229
|
...existing,
|
|
79
230
|
host,
|
|
80
231
|
tenant_id,
|
|
81
|
-
shared_key,
|
|
82
232
|
user_sub,
|
|
233
|
+
jwt: token,
|
|
83
234
|
mcp_port,
|
|
84
235
|
projects_path,
|
|
236
|
+
identity_type: isAgent ? 'agent' : 'user',
|
|
85
237
|
};
|
|
86
238
|
saveConfig(config, env);
|
|
87
239
|
const configPath = getConfigPath(env);
|
|
88
|
-
console.log(
|
|
89
|
-
|
|
240
|
+
console.log(isAgent
|
|
241
|
+
? `\nAgent token stored. Config saved to ${configPath}`
|
|
242
|
+
: `\nLogin successful. Config saved to ${configPath}`);
|
|
243
|
+
if (requires_password_change) {
|
|
244
|
+
console.log('Note: this account is flagged for password change — update it via the web UI.');
|
|
245
|
+
}
|
|
246
|
+
console.log('');
|
|
90
247
|
}
|
|
91
248
|
//# sourceMappingURL=config-manager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/config/config-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,QAAQ,MAAM,eAAe,CAAC;AAGrC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AAExD,SAAS,aAAa,CAAC,GAAY;IACjC,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC;IAC5D,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,cAAc,GAAmB;IACrC,IAAI,EAAE,yBAAyB;IAC/B,SAAS,EAAE,UAAU;IACrB,
|
|
1
|
+
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/config/config-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import type { SiloLinkConfig } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Token store for the long-lived JWT obtained at `silolink config` time
|
|
4
|
+
* via POST /api/v1/users/login. The server-issued token has no `exp`
|
|
5
|
+
* claim, so there's nothing to refresh client-side — if it ever stops
|
|
6
|
+
* working, the user has to re-run `silolink config` to log in again.
|
|
7
|
+
*
|
|
8
|
+
* The class keeps the original `JwtGenerator` shape (generate / getToken /
|
|
9
|
+
* startAutoRefresh / stopAutoRefresh) so the rest of the bridge can
|
|
10
|
+
* continue to call into it unchanged.
|
|
11
|
+
*/
|
|
2
12
|
export declare class JwtGenerator {
|
|
3
13
|
private config;
|
|
4
|
-
private currentToken;
|
|
5
|
-
private tokenExpiry;
|
|
6
|
-
private refreshTimer;
|
|
7
|
-
private onRefresh;
|
|
8
14
|
constructor(config: SiloLinkConfig);
|
|
9
15
|
generate(): string;
|
|
10
16
|
getToken(): string;
|
|
11
|
-
|
|
12
|
-
startAutoRefresh(onRefresh: (token: string) => void): void;
|
|
17
|
+
startAutoRefresh(_onRefresh: (token: string) => void): void;
|
|
13
18
|
stopAutoRefresh(): void;
|
|
14
19
|
}
|
|
15
20
|
//# sourceMappingURL=jwt-generator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-generator.d.ts","sourceRoot":"","sources":["../../src/config/jwt-generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jwt-generator.d.ts","sourceRoot":"","sources":["../../src/config/jwt-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;;;;;;GASG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,EAAE,cAAc;IAIlC,QAAQ,IAAI,MAAM;IAIlB,QAAQ,IAAI,MAAM;IAYlB,gBAAgB,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI3D,eAAe,IAAI,IAAI;CAGxB"}
|
|
@@ -1,54 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Token store for the long-lived JWT obtained at `silolink config` time
|
|
3
|
+
* via POST /api/v1/users/login. The server-issued token has no `exp`
|
|
4
|
+
* claim, so there's nothing to refresh client-side — if it ever stops
|
|
5
|
+
* working, the user has to re-run `silolink config` to log in again.
|
|
6
|
+
*
|
|
7
|
+
* The class keeps the original `JwtGenerator` shape (generate / getToken /
|
|
8
|
+
* startAutoRefresh / stopAutoRefresh) so the rest of the bridge can
|
|
9
|
+
* continue to call into it unchanged.
|
|
10
|
+
*/
|
|
4
11
|
export class JwtGenerator {
|
|
5
12
|
config;
|
|
6
|
-
currentToken = null;
|
|
7
|
-
tokenExpiry = 0;
|
|
8
|
-
refreshTimer = null;
|
|
9
|
-
onRefresh = null;
|
|
10
13
|
constructor(config) {
|
|
11
14
|
this.config = config;
|
|
12
15
|
}
|
|
13
16
|
generate() {
|
|
14
|
-
|
|
15
|
-
const exp = now + TOKEN_LIFETIME_HOURS * 3600;
|
|
16
|
-
const payload = {
|
|
17
|
-
sub: this.config.user_sub,
|
|
18
|
-
iat: now,
|
|
19
|
-
exp,
|
|
20
|
-
};
|
|
21
|
-
this.currentToken = jwt.sign(payload, this.config.shared_key, {
|
|
22
|
-
algorithm: 'HS256',
|
|
23
|
-
});
|
|
24
|
-
this.tokenExpiry = exp;
|
|
25
|
-
return this.currentToken;
|
|
17
|
+
return this.getToken();
|
|
26
18
|
}
|
|
27
19
|
getToken() {
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
const token = this.config.jwt;
|
|
21
|
+
if (!token) {
|
|
22
|
+
throw new Error('No JWT in config. Run "silolink config" to log in and obtain a token.');
|
|
30
23
|
}
|
|
31
|
-
return
|
|
24
|
+
return token;
|
|
32
25
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
startAutoRefresh(onRefresh) {
|
|
39
|
-
this.onRefresh = onRefresh;
|
|
40
|
-
this.refreshTimer = setInterval(() => {
|
|
41
|
-
const token = this.generate();
|
|
42
|
-
if (this.onRefresh) {
|
|
43
|
-
this.onRefresh(token);
|
|
44
|
-
}
|
|
45
|
-
}, REFRESH_INTERVAL_HOURS * 3600 * 1000);
|
|
26
|
+
// No-op: server JWTs have no exp, so there is nothing to refresh.
|
|
27
|
+
// Kept so existing callers compile.
|
|
28
|
+
startAutoRefresh(_onRefresh) {
|
|
29
|
+
void _onRefresh;
|
|
46
30
|
}
|
|
47
31
|
stopAutoRefresh() {
|
|
48
|
-
|
|
49
|
-
clearInterval(this.refreshTimer);
|
|
50
|
-
this.refreshTimer = null;
|
|
51
|
-
}
|
|
32
|
+
// no-op
|
|
52
33
|
}
|
|
53
34
|
}
|
|
54
35
|
//# sourceMappingURL=jwt-generator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-generator.js","sourceRoot":"","sources":["../../src/config/jwt-generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jwt-generator.js","sourceRoot":"","sources":["../../src/config/jwt-generator.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,OAAO,YAAY;IACf,MAAM,CAAiB;IAE/B,YAAY,MAAsB;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED,QAAQ;QACN,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;QAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACb,uEAAuE,CACxE,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,kEAAkE;IAClE,oCAAoC;IACpC,gBAAgB,CAAC,UAAmC;QAClD,KAAK,UAAU,CAAC;IAClB,CAAC;IAED,eAAe;QACb,QAAQ;IACV,CAAC;CACF"}
|
|
@@ -115,6 +115,19 @@ export declare abstract class BaseTmuxLauncher implements AgentLauncher {
|
|
|
115
115
|
* by checkTmuxSessions to inspect output for usage-limit patterns etc).
|
|
116
116
|
*/
|
|
117
117
|
protected capturePaneTail(tmuxSession: string, reason: string, onContent?: (content: string) => void): void;
|
|
118
|
+
/**
|
|
119
|
+
* When an agent fails to show its ready indicator within the timeout,
|
|
120
|
+
* grab the pane content and scan for known stuck-pane signatures we've
|
|
121
|
+
* seen in production. Each pattern maps to a clear actionable hint so a
|
|
122
|
+
* future CLI-schema or first-run-flow change shows up as a 30-second
|
|
123
|
+
* diagnostic in the daemon log instead of a multi-hour silent hang.
|
|
124
|
+
*
|
|
125
|
+
* Patterns are intentionally loose (case-insensitive substring) so a
|
|
126
|
+
* minor wording shift in CC/Gemini doesn't blind us. If something stops
|
|
127
|
+
* matching, we fall through to the existing "did not show ready"
|
|
128
|
+
* warning — same posture as before, no regression.
|
|
129
|
+
*/
|
|
130
|
+
protected diagnoseStuckPane(tmuxSession: string): void;
|
|
118
131
|
/**
|
|
119
132
|
* Hook invoked after a tmux pane dies and its final content has been captured.
|
|
120
133
|
* Subclasses override to inspect the content (e.g. for usage-limit messages)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-tmux-launcher.d.ts","sourceRoot":"","sources":["../../src/core/base-tmux-launcher.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"base-tmux-launcher.d.ts","sourceRoot":"","sources":["../../src/core/base-tmux-launcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -156,11 +156,17 @@ export class BaseTmuxLauncher {
|
|
|
156
156
|
isAutoRespawnEnabled() { return this.enabled && this.isAutoRespawnConfigured(); }
|
|
157
157
|
hasLiveProcess() { return this.tmuxSessions.size > 0; }
|
|
158
158
|
startIdleMonitor() {
|
|
159
|
-
|
|
159
|
+
// Run the idle monitor whenever the launcher is enabled. The heartbeat
|
|
160
|
+
// nudge needs to fire even when auto-respawn is off — it's a benign
|
|
161
|
+
// keystroke that keeps Claude's poll loop alive during long quiet
|
|
162
|
+
// stretches. Respawn-specific actions inside the monitor still check
|
|
163
|
+
// isAutoRespawnEnabled() at their own call sites.
|
|
164
|
+
if (!this.enabled)
|
|
160
165
|
return;
|
|
161
166
|
const checkInterval = Math.max(this.getIdleTimeoutMs() / 2, 5000);
|
|
162
167
|
this.idleCheckTimer = setInterval(() => this.checkIdle(), checkInterval);
|
|
163
|
-
|
|
168
|
+
const respawnState = this.isAutoRespawnConfigured() ? 'auto-respawn on' : 'auto-respawn off';
|
|
169
|
+
console.log(` ${this.getLogTag()} idle monitor running (idle timeout: ${this.getIdleTimeoutMs()}ms, ${respawnState})`);
|
|
164
170
|
}
|
|
165
171
|
destroy() {
|
|
166
172
|
if (this.idleCheckTimer) {
|
|
@@ -266,6 +272,59 @@ export class BaseTmuxLauncher {
|
|
|
266
272
|
onContent('');
|
|
267
273
|
}
|
|
268
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* When an agent fails to show its ready indicator within the timeout,
|
|
277
|
+
* grab the pane content and scan for known stuck-pane signatures we've
|
|
278
|
+
* seen in production. Each pattern maps to a clear actionable hint so a
|
|
279
|
+
* future CLI-schema or first-run-flow change shows up as a 30-second
|
|
280
|
+
* diagnostic in the daemon log instead of a multi-hour silent hang.
|
|
281
|
+
*
|
|
282
|
+
* Patterns are intentionally loose (case-insensitive substring) so a
|
|
283
|
+
* minor wording shift in CC/Gemini doesn't blind us. If something stops
|
|
284
|
+
* matching, we fall through to the existing "did not show ready"
|
|
285
|
+
* warning — same posture as before, no regression.
|
|
286
|
+
*/
|
|
287
|
+
diagnoseStuckPane(tmuxSession) {
|
|
288
|
+
try {
|
|
289
|
+
const capture = spawn('tmux', ['capture-pane', '-t', tmuxSession, '-p', '-S', '-200'], {
|
|
290
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
291
|
+
});
|
|
292
|
+
let output = '';
|
|
293
|
+
capture.stdout.on('data', (data) => { output += data.toString(); });
|
|
294
|
+
capture.on('exit', () => {
|
|
295
|
+
if (!output)
|
|
296
|
+
return;
|
|
297
|
+
const lower = output.toLowerCase();
|
|
298
|
+
// Each entry: substring → human-readable explanation. Order matters
|
|
299
|
+
// — first match wins, so put the most specific failure shapes first.
|
|
300
|
+
const signatures = [
|
|
301
|
+
['error: unknown option', 'CLI rejected one of the args getArgs() supplied. Likely a flag introduced/removed across CLI versions — check installed agent CLI version vs. flags in getArgs().'],
|
|
302
|
+
['command not found', 'Agent binary not on PATH for the spawned process. Confirm the binary is in $PATH the daemon was launched with (silolink reads $PATH at start; ~/.local/bin etc. must be present).'],
|
|
303
|
+
['cannot find module', 'Agent binary points at a missing node module. Likely a stale/partial install — try reinstalling the agent CLI.'],
|
|
304
|
+
['do you trust this directory', 'Workspace trust dialog visible — ensureClaudeWorkspaceTrust pre-write either failed or CC changed the gate name. Check ~/.claude.json[projects][cwd].hasTrustDialogAccepted and the cwd CC actually launched in.'],
|
|
305
|
+
['warning: bypass permissions mode', 'Bypass-permissions consent dialog visible — CC is asking the human to confirm. If you can\'t change this gate via flag, bake an answer (`Yes I accept` etc.) into a settings file CC reads at startup.'],
|
|
306
|
+
['approve project-scope mcp', 'Project-scope MCP-enable dialog visible — pre-write enabledMcpjsonServers in ~/.claude.json[projects][cwd] for the server names registered via `mcp add -s project`.'],
|
|
307
|
+
['no mcp servers', 'Agent reports zero MCP servers loaded. Check both per-cwd .mcp.json and user-scope ~/.claude.json[mcpServers], plus any per-project enable list.'],
|
|
308
|
+
['authentication required', 'Agent CLI is asking the human to log in. Ensure JWTs / API keys are baked into config files, not interactive prompts.'],
|
|
309
|
+
];
|
|
310
|
+
for (const [needle, hint] of signatures) {
|
|
311
|
+
if (lower.includes(needle)) {
|
|
312
|
+
console.warn(`${this.getLogTag()} DIAGNOSTIC for "${tmuxSession}": ${hint}`);
|
|
313
|
+
console.warn(`${this.getLogTag()} DIAGNOSTIC matched signature: "${needle}"`);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Fell through — no known pattern matched. Log the pane tail anyway
|
|
318
|
+
// so future runs can be triaged from log alone without a re-spawn.
|
|
319
|
+
console.warn(`${this.getLogTag()} DIAGNOSTIC for "${tmuxSession}": stuck on unrecognised state. Pane tail (last 30 lines):`);
|
|
320
|
+
const tail = output.trim().split('\n').slice(-30).join('\n');
|
|
321
|
+
console.warn(tail);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
catch (err) {
|
|
325
|
+
console.error(`${this.getLogTag()} diagnoseStuckPane failed for ${tmuxSession}: ${err}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
269
328
|
/**
|
|
270
329
|
* Hook invoked after a tmux pane dies and its final content has been captured.
|
|
271
330
|
* Subclasses override to inspect the content (e.g. for usage-limit messages)
|
|
@@ -377,8 +436,40 @@ export class BaseTmuxLauncher {
|
|
|
377
436
|
console.log(` Working directory: ${cwd}`);
|
|
378
437
|
if (workspaceBranch)
|
|
379
438
|
console.log(` Workspace branch: ${workspaceBranch}`);
|
|
380
|
-
|
|
381
|
-
//
|
|
439
|
+
// Deterministic tmux name when we know the conversation: peek
|
|
440
|
+
// (and any UI tooling) can compute the tmux session name from
|
|
441
|
+
// a conversation_id alone, without a metadata round-trip. Falls
|
|
442
|
+
// back to a timestamped name for ad-hoc launches that have no
|
|
443
|
+
// conversation context.
|
|
444
|
+
//
|
|
445
|
+
// If a tmux with the same name already exists (a prior session
|
|
446
|
+
// for this conversation that wasn't cleaned up), kill it first
|
|
447
|
+
// so the new launch doesn't fail with "duplicate session". The
|
|
448
|
+
// server-side end-prior-AgentSession fix makes the duplicate
|
|
449
|
+
// case rare, but defending against it here keeps the daemon
|
|
450
|
+
// robust against orphan tmux drift.
|
|
451
|
+
const tmuxSession = conversationId
|
|
452
|
+
? `${this.getTmuxPrefix()}-conv${conversationId}`
|
|
453
|
+
: `${this.getTmuxPrefix()}-${Date.now()}`;
|
|
454
|
+
try {
|
|
455
|
+
await new Promise((resolve) => {
|
|
456
|
+
const has = spawn('tmux', ['has-session', '-t', tmuxSession], { stdio: 'ignore' });
|
|
457
|
+
has.on('exit', (code) => {
|
|
458
|
+
if (code === 0) {
|
|
459
|
+
console.log(`${this.getLogTag()} Existing tmux "${tmuxSession}" found — killing before relaunch`);
|
|
460
|
+
const kill = spawn('tmux', ['kill-session', '-t', tmuxSession], { stdio: 'ignore' });
|
|
461
|
+
kill.on('exit', () => resolve());
|
|
462
|
+
kill.on('error', () => resolve());
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
resolve();
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
has.on('error', () => resolve());
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
catch { /* best-effort cleanup */ }
|
|
472
|
+
// Use tmux new-session -c to set working directory safely instead of
|
|
382
473
|
// interpolating into a shell string. This prevents shell injection via cwd/command.
|
|
383
474
|
const tmuxArgs = [
|
|
384
475
|
'new-session', '-d', '-s', tmuxSession, '-c', cwd, '-x', '200', '-y', '50',
|
|
@@ -445,6 +536,7 @@ export class BaseTmuxLauncher {
|
|
|
445
536
|
}
|
|
446
537
|
if (!ready) {
|
|
447
538
|
console.warn(`${this.getLogTag()} Agent in "${sessionToPrompt}" did not show ready indicator within ${maxWaitMs / 1000}s — sending prompt anyway`);
|
|
539
|
+
this.diagnoseStuckPane(sessionToPrompt);
|
|
448
540
|
}
|
|
449
541
|
else {
|
|
450
542
|
console.log(`${this.getLogTag()} Agent ready after ${((Date.now() - startWait) / 1000).toFixed(1)}s`);
|