groove-dev 0.27.104 → 0.27.107
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/moe-training/client/scrubber.js +2 -2
- package/moe-training/client/trajectory-capture.js +36 -0
- package/moe-training/test/client/scrubber.test.js +24 -0
- package/moe-training/test/client/trajectory-capture.test.js +104 -0
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +13 -8
- package/node_modules/@groove-dev/daemon/src/process.js +54 -0
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +7 -7
- package/node_modules/@groove-dev/daemon/src/providers/codex.js +1 -1
- package/node_modules/@groove-dev/daemon/src/providers/gemini.js +1 -1
- package/node_modules/@groove-dev/gui/dist/assets/{index-oUBAPJv6.js → index-DkAGIluW.js} +22 -22
- package/node_modules/@groove-dev/gui/dist/assets/{index-C1ObKizg.css → index-QwgLRN8B.css} +1 -1
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/onboarding/setup-wizard.jsx +6 -0
- package/node_modules/@groove-dev/gui/src/components/settings/ProviderSetupWizard.jsx +29 -5
- package/node_modules/@groove-dev/gui/src/stores/groove.js +2 -1
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +22 -9
- package/node_modules/moe-training/client/scrubber.js +2 -2
- package/node_modules/moe-training/client/trajectory-capture.js +36 -0
- package/node_modules/moe-training/test/client/scrubber.test.js +24 -0
- package/node_modules/moe-training/test/client/trajectory-capture.test.js +104 -0
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +13 -8
- package/packages/daemon/src/process.js +54 -0
- package/packages/daemon/src/providers/claude-code.js +7 -7
- package/packages/daemon/src/providers/codex.js +1 -1
- package/packages/daemon/src/providers/gemini.js +1 -1
- package/packages/gui/dist/assets/{index-oUBAPJv6.js → index-DkAGIluW.js} +22 -22
- package/packages/gui/dist/assets/{index-C1ObKizg.css → index-QwgLRN8B.css} +1 -1
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/onboarding/setup-wizard.jsx +6 -0
- package/packages/gui/src/components/settings/ProviderSetupWizard.jsx +29 -5
- package/packages/gui/src/stores/groove.js +2 -1
- package/packages/gui/src/views/settings.jsx +22 -9
|
@@ -65,7 +65,7 @@ export class PIIScrubber {
|
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
name: 'ipv6',
|
|
68
|
-
regex: /(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|::(?:[fF]{4}:)?(?:\d{1,3}\.){3}\d{1,3}|::1
|
|
68
|
+
regex: /(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|::(?:[fF]{4}:)?(?:\d{1,3}\.){3}\d{1,3}|::1\b/g,
|
|
69
69
|
replacement: '[IP]',
|
|
70
70
|
},
|
|
71
71
|
{
|
|
@@ -100,7 +100,7 @@ export class PIIScrubber {
|
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
102
|
name: 'base64_secret',
|
|
103
|
-
regex: /(?<![A-Za-z0-9
|
|
103
|
+
regex: /(?<![A-Za-z0-9+])[A-Za-z0-9+]{40,}={0,2}(?![A-Za-z0-9+])/g,
|
|
104
104
|
replacement: '[API_KEY]',
|
|
105
105
|
},
|
|
106
106
|
];
|
|
@@ -154,6 +154,42 @@ export class TrajectoryCapture {
|
|
|
154
154
|
this._processStep(agentId, ctx, classified);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
onParsedOutput(agentId, output) {
|
|
158
|
+
if (!this._enabled) return;
|
|
159
|
+
const ctx = this._contexts.get(agentId);
|
|
160
|
+
if (!ctx || !output || !output.type) return;
|
|
161
|
+
|
|
162
|
+
if (output.type === 'activity') {
|
|
163
|
+
if (output.subtype === 'assistant') {
|
|
164
|
+
this._processStep(agentId, ctx, { type: 'thought', content: output.data || '' });
|
|
165
|
+
} else if (output.subtype === 'tool_use' && Array.isArray(output.data)) {
|
|
166
|
+
for (const item of output.data) {
|
|
167
|
+
this._processStep(agentId, ctx, {
|
|
168
|
+
type: 'action',
|
|
169
|
+
tool: item.name || '',
|
|
170
|
+
arguments: item.input || {},
|
|
171
|
+
content: `Using ${item.name || 'tool'}`,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
} else if (output.subtype === 'tool_result' && Array.isArray(output.data)) {
|
|
175
|
+
for (const item of output.data) {
|
|
176
|
+
const isError = item.success === false;
|
|
177
|
+
this._processStep(agentId, ctx, {
|
|
178
|
+
type: isError ? 'error' : 'observation',
|
|
179
|
+
content: item.output || '',
|
|
180
|
+
tool: item.name || '',
|
|
181
|
+
is_error: isError,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} else if (output.type === 'result') {
|
|
186
|
+
this._processStep(agentId, ctx, {
|
|
187
|
+
type: 'resolution',
|
|
188
|
+
content: typeof output.data === 'string' ? output.data : '',
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
157
193
|
async onAgentComplete(agentId, outcome) {
|
|
158
194
|
await this._closeAgent(agentId, outcome?.status || 'SUCCESS', outcome);
|
|
159
195
|
}
|
|
@@ -131,6 +131,30 @@ describe('PIIScrubber', () => {
|
|
|
131
131
|
assert.equal(scrubber.scrub(input), 'cd ~');
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
+
it('does not scrub CSS pseudo-elements as IPv6', () => {
|
|
135
|
+
const input = '.hero-icon::before { content: ""; }';
|
|
136
|
+
assert.equal(scrubber.scrub(input), input);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('still scrubs IPv6 loopback ::1', () => {
|
|
140
|
+
const input = 'listening on ::1 port 3000';
|
|
141
|
+
assert.equal(scrubber.scrub(input), 'listening on [IP] port 3000');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('does not scrub file paths as base64 secrets', () => {
|
|
145
|
+
const input = '/home/user/project/groove/packages/gui/src/views/settings.jsx';
|
|
146
|
+
const result = scrubber.scrub(input);
|
|
147
|
+
assert.ok(!result.includes('[API_KEY]'), `expected no [API_KEY] in: ${result}`);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('still scrubs real base64 secrets without slashes', () => {
|
|
151
|
+
const b64 = 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODk';
|
|
152
|
+
const input = `key: ${b64} end`;
|
|
153
|
+
const result = scrubber.scrub(input);
|
|
154
|
+
assert.ok(result.includes('[API_KEY]'), `expected [API_KEY] in: ${result}`);
|
|
155
|
+
assert.ok(!result.includes(b64));
|
|
156
|
+
});
|
|
157
|
+
|
|
134
158
|
it('patterns do not interfere with each other', () => {
|
|
135
159
|
const input = 'user@example.com called 555-123-4567 from 192.168.1.1';
|
|
136
160
|
const result = scrubber.scrub(input);
|
|
@@ -438,3 +438,107 @@ describe('TrajectoryCapture — _computeQuality', () => {
|
|
|
438
438
|
assert.equal(quality, 100);
|
|
439
439
|
});
|
|
440
440
|
});
|
|
441
|
+
|
|
442
|
+
describe('TrajectoryCapture — onParsedOutput', () => {
|
|
443
|
+
function makeEnabledTc() {
|
|
444
|
+
const tc = makeTc();
|
|
445
|
+
tc._enabled = true;
|
|
446
|
+
tc._scrubber = { scrub: (s) => s };
|
|
447
|
+
const ctx = makeCtx();
|
|
448
|
+
ctx.totalTokens = 0;
|
|
449
|
+
ctx.stepCount = 0;
|
|
450
|
+
ctx.allSteps = [];
|
|
451
|
+
ctx.builder = { addStep: () => null };
|
|
452
|
+
ctx.classifier = { onStep: (s) => s };
|
|
453
|
+
tc._contexts.set('agent-loop-1', ctx);
|
|
454
|
+
return { tc, ctx };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
it('converts assistant activity to thought step', () => {
|
|
458
|
+
const { tc, ctx } = makeEnabledTc();
|
|
459
|
+
tc.onParsedOutput('agent-loop-1', { type: 'activity', subtype: 'assistant', data: 'I will fix the bug' });
|
|
460
|
+
assert.equal(ctx.stepCount, 1);
|
|
461
|
+
assert.equal(ctx.allSteps[0].type, 'thought');
|
|
462
|
+
assert.equal(ctx.allSteps[0].content, 'I will fix the bug');
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('converts tool_use activity to action step', () => {
|
|
466
|
+
const { tc, ctx } = makeEnabledTc();
|
|
467
|
+
tc.onParsedOutput('agent-loop-1', {
|
|
468
|
+
type: 'activity', subtype: 'tool_use',
|
|
469
|
+
data: [{ type: 'tool_use', name: 'Edit', input: { path: 'foo.js' } }],
|
|
470
|
+
});
|
|
471
|
+
assert.equal(ctx.stepCount, 1);
|
|
472
|
+
assert.equal(ctx.allSteps[0].type, 'action');
|
|
473
|
+
assert.equal(ctx.allSteps[0].tool, 'Edit');
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it('converts successful tool_result to observation step', () => {
|
|
477
|
+
const { tc, ctx } = makeEnabledTc();
|
|
478
|
+
tc.onParsedOutput('agent-loop-1', {
|
|
479
|
+
type: 'activity', subtype: 'tool_result',
|
|
480
|
+
data: [{ type: 'tool_result', name: 'Bash', success: true, output: 'tests passed' }],
|
|
481
|
+
});
|
|
482
|
+
assert.equal(ctx.stepCount, 1);
|
|
483
|
+
assert.equal(ctx.allSteps[0].type, 'observation');
|
|
484
|
+
assert.equal(ctx.allSteps[0].content, 'tests passed');
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('converts failed tool_result to error step', () => {
|
|
488
|
+
const { tc, ctx } = makeEnabledTc();
|
|
489
|
+
tc.onParsedOutput('agent-loop-1', {
|
|
490
|
+
type: 'activity', subtype: 'tool_result',
|
|
491
|
+
data: [{ type: 'tool_result', name: 'Bash', success: false, output: 'command not found' }],
|
|
492
|
+
});
|
|
493
|
+
assert.equal(ctx.stepCount, 1);
|
|
494
|
+
assert.equal(ctx.allSteps[0].type, 'error');
|
|
495
|
+
assert.equal(ctx.allSteps[0].is_error, true);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('converts result to resolution step', () => {
|
|
499
|
+
const { tc, ctx } = makeEnabledTc();
|
|
500
|
+
tc.onParsedOutput('agent-loop-1', { type: 'result', subtype: 'assistant', data: 'Task complete' });
|
|
501
|
+
assert.equal(ctx.stepCount, 1);
|
|
502
|
+
assert.equal(ctx.allSteps[0].type, 'resolution');
|
|
503
|
+
assert.equal(ctx.allSteps[0].content, 'Task complete');
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it('ignores stream activity (partial deltas)', () => {
|
|
507
|
+
const { tc, ctx } = makeEnabledTc();
|
|
508
|
+
tc.onParsedOutput('agent-loop-1', { type: 'activity', subtype: 'stream', data: 'partial' });
|
|
509
|
+
assert.equal(ctx.stepCount, 0);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('ignores token-only activity', () => {
|
|
513
|
+
const { tc, ctx } = makeEnabledTc();
|
|
514
|
+
tc.onParsedOutput('agent-loop-1', { type: 'activity', tokensUsed: 500, inputTokens: 400, outputTokens: 100 });
|
|
515
|
+
assert.equal(ctx.stepCount, 0);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('silently returns for unknown agent', () => {
|
|
519
|
+
const { tc } = makeEnabledTc();
|
|
520
|
+
tc.onParsedOutput('unknown-agent', { type: 'activity', subtype: 'assistant', data: 'hello' });
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('silently returns when disabled', () => {
|
|
524
|
+
const { tc, ctx } = makeEnabledTc();
|
|
525
|
+
tc._enabled = false;
|
|
526
|
+
tc.onParsedOutput('agent-loop-1', { type: 'activity', subtype: 'assistant', data: 'hello' });
|
|
527
|
+
assert.equal(ctx.stepCount, 0);
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it('accumulates tokens across multiple outputs', () => {
|
|
531
|
+
const { tc, ctx } = makeEnabledTc();
|
|
532
|
+
tc.onParsedOutput('agent-loop-1', { type: 'activity', subtype: 'assistant', data: 'thinking about the problem' });
|
|
533
|
+
tc.onParsedOutput('agent-loop-1', {
|
|
534
|
+
type: 'activity', subtype: 'tool_use',
|
|
535
|
+
data: [{ type: 'tool_use', name: 'Bash', input: { command: 'ls' } }],
|
|
536
|
+
});
|
|
537
|
+
tc.onParsedOutput('agent-loop-1', {
|
|
538
|
+
type: 'activity', subtype: 'tool_result',
|
|
539
|
+
data: [{ type: 'tool_result', name: 'Bash', success: true, output: 'file1.js\nfile2.js' }],
|
|
540
|
+
});
|
|
541
|
+
assert.equal(ctx.stepCount, 3);
|
|
542
|
+
assert.ok(ctx.totalTokens > 0);
|
|
543
|
+
});
|
|
544
|
+
});
|
|
@@ -631,9 +631,8 @@ export function createApi(app, daemon) {
|
|
|
631
631
|
|
|
632
632
|
write({ status: 'installing', output: `Installing ${pkg}...`, progress: 0 });
|
|
633
633
|
|
|
634
|
-
const proc = spawn('
|
|
634
|
+
const proc = spawn('bash', ['-lc', `npm install -g ${pkg}`], {
|
|
635
635
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
636
|
-
shell: true,
|
|
637
636
|
env: { ...process.env, NODE_ENV: undefined },
|
|
638
637
|
});
|
|
639
638
|
|
|
@@ -741,9 +740,10 @@ export function createApi(app, daemon) {
|
|
|
741
740
|
};
|
|
742
741
|
|
|
743
742
|
const proc = spawn('codex', ['login'], {
|
|
744
|
-
stdio: ['
|
|
743
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
745
744
|
shell: true,
|
|
746
745
|
});
|
|
746
|
+
proc.stdin.on('error', () => {});
|
|
747
747
|
let stdout = '';
|
|
748
748
|
let stderr = '';
|
|
749
749
|
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
@@ -752,8 +752,8 @@ export function createApi(app, daemon) {
|
|
|
752
752
|
const timeout = setTimeout(() => {
|
|
753
753
|
const urlMatch = (stdout + stderr).match(/https:\/\/\S+/);
|
|
754
754
|
respond(urlMatch
|
|
755
|
-
? { status: 'pending', url: urlMatch[0] }
|
|
756
|
-
: { status: 'pending', message: 'Login started — check your browser' });
|
|
755
|
+
? { status: 'pending', url: urlMatch[0], browserOpened: true }
|
|
756
|
+
: { status: 'pending', message: 'Login started — check your browser', browserOpened: true });
|
|
757
757
|
}, 5000);
|
|
758
758
|
|
|
759
759
|
proc.on('close', (code) => {
|
|
@@ -4666,7 +4666,13 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
4666
4666
|
const hasKey = daemon.credentials.hasKey(p.id);
|
|
4667
4667
|
let authStatus = 'not-configured';
|
|
4668
4668
|
if (p.authType === 'subscription') {
|
|
4669
|
-
|
|
4669
|
+
if (!p.installed) {
|
|
4670
|
+
authStatus = 'not-configured';
|
|
4671
|
+
} else {
|
|
4672
|
+
const provObj = getProvider(p.id);
|
|
4673
|
+
const authResult = provObj?.constructor?.isAuthenticated?.();
|
|
4674
|
+
authStatus = authResult?.authenticated ? 'authenticated' : 'not-configured';
|
|
4675
|
+
}
|
|
4670
4676
|
} else if (p.authType === 'api-key') {
|
|
4671
4677
|
authStatus = hasKey ? 'key-set' : 'not-configured';
|
|
4672
4678
|
if (p.authStatus?.authenticated) authStatus = 'authenticated';
|
|
@@ -4729,9 +4735,8 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
4729
4735
|
|
|
4730
4736
|
write({ status: 'installing', output: `Installing ${pkg}...`, progress: 0 });
|
|
4731
4737
|
|
|
4732
|
-
const proc = spawn('
|
|
4738
|
+
const proc = spawn('bash', ['-lc', `npm install -g ${pkg}`], {
|
|
4733
4739
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
4734
|
-
shell: true,
|
|
4735
4740
|
env: { ...process.env, NODE_ENV: undefined },
|
|
4736
4741
|
});
|
|
4737
4742
|
|
|
@@ -1760,6 +1760,15 @@ For normal file edits within your scope, proceed without review.
|
|
|
1760
1760
|
locks.register(newAgent.id, newAgent.scope, newAgent.workingDir);
|
|
1761
1761
|
}
|
|
1762
1762
|
|
|
1763
|
+
if (this.daemon.trajectoryCapture) {
|
|
1764
|
+
try {
|
|
1765
|
+
const teamSize = registry.getAll().filter(a => a.status === 'active' || a.status === 'running' || a.status === 'starting').length;
|
|
1766
|
+
this.daemon.trajectoryCapture.onAgentSpawn(
|
|
1767
|
+
newAgent.id, config.provider, config.model || null, config.role, teamSize
|
|
1768
|
+
).catch(() => {});
|
|
1769
|
+
} catch (e) { /* fail silent */ }
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1763
1772
|
// Spawn the resumed process
|
|
1764
1773
|
const resumeCwd = [config.workingDir, this.daemon.projectDir].find(d => d && existsSync(d)) || this.daemon.projectDir;
|
|
1765
1774
|
const proc = cpSpawn(command, args, {
|
|
@@ -1815,6 +1824,23 @@ For normal file edits within your scope, proceed without review.
|
|
|
1815
1824
|
|
|
1816
1825
|
const finalStatus = signal === 'SIGTERM' || signal === 'SIGKILL' ? 'killed' : code === 0 ? 'completed' : 'crashed';
|
|
1817
1826
|
registry.update(newAgent.id, { status: finalStatus, pid: null });
|
|
1827
|
+
|
|
1828
|
+
if (this.daemon.trajectoryCapture) {
|
|
1829
|
+
try {
|
|
1830
|
+
if (finalStatus === 'completed') {
|
|
1831
|
+
this.daemon.trajectoryCapture.onAgentComplete(newAgent.id, {
|
|
1832
|
+
status: 'SUCCESS', exit_code: code, signal,
|
|
1833
|
+
});
|
|
1834
|
+
} else {
|
|
1835
|
+
this.daemon.trajectoryCapture.onAgentCrash(newAgent.id,
|
|
1836
|
+
signal ? 'Killed by signal ' + signal : 'Exit code ' + code
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
const count = (this.daemon.state.get('training_sessions_captured') || 0) + 1;
|
|
1840
|
+
this.daemon.state.set('training_sessions_captured', count);
|
|
1841
|
+
} catch (e) { /* fail silent */ }
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1818
1844
|
this.daemon.broadcast({ type: 'agent:exit', agentId: newAgent.id, code, signal, status: finalStatus });
|
|
1819
1845
|
if (finalStatus === 'completed' && this.daemon.journalist) {
|
|
1820
1846
|
const a = registry.get(newAgent.id);
|
|
@@ -1930,8 +1956,20 @@ For normal file edits within your scope, proceed without review.
|
|
|
1930
1956
|
});
|
|
1931
1957
|
}
|
|
1932
1958
|
|
|
1959
|
+
if (this.daemon.trajectoryCapture) {
|
|
1960
|
+
try {
|
|
1961
|
+
const teamSize = registry.getAll().filter(a => a.status === 'active' || a.status === 'running' || a.status === 'starting').length;
|
|
1962
|
+
this.daemon.trajectoryCapture.onAgentSpawn(
|
|
1963
|
+
newAgent.id, config.provider, loopConfig.model || config.model || null, config.role, teamSize
|
|
1964
|
+
).catch(() => {});
|
|
1965
|
+
} catch (e) { /* fail silent */ }
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1933
1968
|
loop.on('output', (output) => {
|
|
1934
1969
|
this._handleAgentOutput(newAgent.id, output);
|
|
1970
|
+
if (this.daemon.trajectoryCapture) {
|
|
1971
|
+
try { this.daemon.trajectoryCapture.onParsedOutput(newAgent.id, output); } catch (e) { /* fail silent */ }
|
|
1972
|
+
}
|
|
1935
1973
|
});
|
|
1936
1974
|
|
|
1937
1975
|
loop.on('exit', ({ code, signal, status }) => {
|
|
@@ -1960,6 +1998,22 @@ For normal file edits within your scope, proceed without review.
|
|
|
1960
1998
|
});
|
|
1961
1999
|
}
|
|
1962
2000
|
|
|
2001
|
+
if (this.daemon.trajectoryCapture) {
|
|
2002
|
+
try {
|
|
2003
|
+
if (status === 'completed') {
|
|
2004
|
+
this.daemon.trajectoryCapture.onAgentComplete(newAgent.id, {
|
|
2005
|
+
status: 'SUCCESS', exit_code: code || 0, signal,
|
|
2006
|
+
});
|
|
2007
|
+
} else {
|
|
2008
|
+
this.daemon.trajectoryCapture.onAgentCrash(newAgent.id,
|
|
2009
|
+
signal ? 'Killed by signal ' + signal : 'Exit status ' + status
|
|
2010
|
+
);
|
|
2011
|
+
}
|
|
2012
|
+
const count = (this.daemon.state.get('training_sessions_captured') || 0) + 1;
|
|
2013
|
+
this.daemon.state.set('training_sessions_captured', count);
|
|
2014
|
+
} catch (e) { /* fail silent */ }
|
|
2015
|
+
}
|
|
2016
|
+
|
|
1963
2017
|
this.daemon.broadcast({ type: 'agent:exit', agentId: newAgent.id, code: code || 0, signal, status });
|
|
1964
2018
|
if (this.daemon.integrations) this.daemon.integrations.refreshMcpJson();
|
|
1965
2019
|
|
|
@@ -46,7 +46,7 @@ export class ClaudeCodeProvider extends Provider {
|
|
|
46
46
|
|
|
47
47
|
static isInstalled() {
|
|
48
48
|
try {
|
|
49
|
-
const cmd = process.platform === 'win32' ? 'where claude' : 'which claude';
|
|
49
|
+
const cmd = process.platform === 'win32' ? 'where claude' : 'bash -lc "which claude"';
|
|
50
50
|
execSync(cmd, { stdio: 'ignore' });
|
|
51
51
|
return true;
|
|
52
52
|
} catch {
|
|
@@ -55,14 +55,14 @@ export class ClaudeCodeProvider extends Provider {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
static isAuthenticated() {
|
|
58
|
-
|
|
59
|
-
const settingsPath = resolve(home, '.claude', 'settings.json');
|
|
60
|
-
if (!existsSync(settingsPath)) return { authenticated: false, reason: 'Claude Code not configured' };
|
|
58
|
+
if (!ClaudeCodeProvider.isInstalled()) return { authenticated: false, reason: 'Claude Code not installed' };
|
|
61
59
|
try {
|
|
62
|
-
execSync('claude --
|
|
63
|
-
|
|
60
|
+
const out = execSync('bash -lc "claude auth status --json"', { encoding: 'utf8', timeout: 10_000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
61
|
+
const data = JSON.parse(out);
|
|
62
|
+
const method = data.authMethod || data.auth_method || 'subscription';
|
|
63
|
+
return { authenticated: true, method };
|
|
64
64
|
} catch {
|
|
65
|
-
return { authenticated: false, reason: '
|
|
65
|
+
return { authenticated: false, reason: 'Not logged in. Run: claude auth login' };
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -49,7 +49,7 @@ export class CodexProvider extends Provider {
|
|
|
49
49
|
|
|
50
50
|
static isInstalled() {
|
|
51
51
|
try {
|
|
52
|
-
const cmd = process.platform === 'win32' ? 'where codex' : 'which codex';
|
|
52
|
+
const cmd = process.platform === 'win32' ? 'where codex' : 'bash -lc "which codex"';
|
|
53
53
|
execSync(cmd, { stdio: 'ignore' });
|
|
54
54
|
return true;
|
|
55
55
|
} catch {
|
|
@@ -41,7 +41,7 @@ export class GeminiProvider extends Provider {
|
|
|
41
41
|
|
|
42
42
|
static isInstalled() {
|
|
43
43
|
try {
|
|
44
|
-
const cmd = process.platform === 'win32' ? 'where gemini' : 'which gemini';
|
|
44
|
+
const cmd = process.platform === 'win32' ? 'where gemini' : 'bash -lc "which gemini"';
|
|
45
45
|
execSync(cmd, { stdio: 'ignore' });
|
|
46
46
|
return true;
|
|
47
47
|
} catch {
|