@myclaw163/clawclaw-cli 0.6.70 → 0.6.71
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/bin/clawclaw-cli.mjs +3 -3
- package/package.json +1 -1
- package/scripts/sync-bundled-skill.mjs +1 -1
- package/src/commands/config.ts +30 -30
- package/src/commands/game.ts +8 -3
- package/src/commands/setup/hermes.test.ts +96 -96
- package/src/commands/setup/hermes.ts +76 -76
- package/src/commands/setup/index.ts +13 -13
- package/src/commands/setup/openclaw.test.ts +114 -114
- package/src/commands/setup/openclaw.ts +147 -147
- package/src/lib/host-config-patcher.test.ts +130 -130
- package/src/lib/host-config-patcher.ts +151 -151
- package/src/lib/hub-reminder.ts +19 -19
package/bin/clawclaw-cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { register } from 'tsx/esm/api';
|
|
3
|
-
register();
|
|
4
|
-
await import('../src/cli.ts');
|
|
2
|
+
import { register } from 'tsx/esm/api';
|
|
3
|
+
register();
|
|
4
|
+
await import('../src/cli.ts');
|
package/package.json
CHANGED
package/src/commands/config.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { getWorkspaceDir } from '../lib/init-command.js';
|
|
3
|
-
import { AuthStore } from '../lib/auth.js';
|
|
4
|
-
|
|
5
|
-
export function createWorkspaceSubcommand(): Command {
|
|
6
|
-
return new Command('workspace')
|
|
7
|
-
.description('Print the workspace directory path')
|
|
8
|
-
.action(() => {
|
|
9
|
-
console.log(getWorkspaceDir());
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function createApikeySubcommand(): Command {
|
|
14
|
-
return new Command('apikey')
|
|
15
|
-
.description('Print the active account API key')
|
|
16
|
-
.action(() => {
|
|
17
|
-
const store = new AuthStore();
|
|
18
|
-
const profile = store.getActive();
|
|
19
|
-
if (!profile) throw new Error('Not logged in. Run: clawclaw-cli account register');
|
|
20
|
-
console.log(profile.apiKey);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function createConfigCommand(): Command {
|
|
25
|
-
const config = new Command('config');
|
|
26
|
-
config.description('Query ClawClaw CLI configuration.');
|
|
27
|
-
config.addCommand(createWorkspaceSubcommand());
|
|
28
|
-
config.addCommand(createApikeySubcommand());
|
|
29
|
-
return config;
|
|
30
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getWorkspaceDir } from '../lib/init-command.js';
|
|
3
|
+
import { AuthStore } from '../lib/auth.js';
|
|
4
|
+
|
|
5
|
+
export function createWorkspaceSubcommand(): Command {
|
|
6
|
+
return new Command('workspace')
|
|
7
|
+
.description('Print the workspace directory path')
|
|
8
|
+
.action(() => {
|
|
9
|
+
console.log(getWorkspaceDir());
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createApikeySubcommand(): Command {
|
|
14
|
+
return new Command('apikey')
|
|
15
|
+
.description('Print the active account API key')
|
|
16
|
+
.action(() => {
|
|
17
|
+
const store = new AuthStore();
|
|
18
|
+
const profile = store.getActive();
|
|
19
|
+
if (!profile) throw new Error('Not logged in. Run: clawclaw-cli account register');
|
|
20
|
+
console.log(profile.apiKey);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createConfigCommand(): Command {
|
|
25
|
+
const config = new Command('config');
|
|
26
|
+
config.description('Query ClawClaw CLI configuration.');
|
|
27
|
+
config.addCommand(createWorkspaceSubcommand());
|
|
28
|
+
config.addCommand(createApikeySubcommand());
|
|
29
|
+
return config;
|
|
30
|
+
}
|
package/src/commands/game.ts
CHANGED
|
@@ -364,7 +364,7 @@ function autoStartStrategy(roleData: any, stateData?: any, gameId?: string): { s
|
|
|
364
364
|
return { strategy: strategyId, pid: child.pid, child };
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
async function runGameStart(opts: { watch: boolean; force?: boolean }): Promise<void> {
|
|
367
|
+
async function runGameStart(opts: { watch: boolean; force?: boolean; channelUrl?: string }): Promise<void> {
|
|
368
368
|
if (process.env.CLAWCLAW_REQUIRE_STREAM_TOOL === '1' && process.env.CLAWCLAW_STREAMED !== '1') {
|
|
369
369
|
process.stderr.write(
|
|
370
370
|
'In OpenClaw, start the game via the clawclaw_game_start tool - do not exec `ccl game start` directly.\n' +
|
|
@@ -434,7 +434,11 @@ async function runGameStart(opts: { watch: boolean; force?: boolean }): Promise<
|
|
|
434
434
|
process.on('SIGINT', onOwnerSignal);
|
|
435
435
|
process.on('SIGTERM', onOwnerSignal);
|
|
436
436
|
const emit = (obj: Record<string, any>): void => {
|
|
437
|
-
|
|
437
|
+
const line = JSON.stringify(obj) + '\n';
|
|
438
|
+
process.stdout.write(line);
|
|
439
|
+
if (opts.channelUrl) {
|
|
440
|
+
fetch(opts.channelUrl, { method: 'POST', body: line }).catch(() => {});
|
|
441
|
+
}
|
|
438
442
|
};
|
|
439
443
|
const emitLifecycle = (
|
|
440
444
|
reason: string,
|
|
@@ -500,7 +504,7 @@ async function runGameStart(opts: { watch: boolean; force?: boolean }): Promise<
|
|
|
500
504
|
feedPath,
|
|
501
505
|
sessionPath,
|
|
502
506
|
getSessionPath: () => EventStore.latestSessionPath(),
|
|
503
|
-
stdout: (s) => process.stdout.write(s),
|
|
507
|
+
stdout: (s) => { process.stdout.write(s); if (opts.channelUrl) { fetch(opts.channelUrl, { method: 'POST', body: s }).catch(() => {}); } },
|
|
504
508
|
signal: ctrl.signal,
|
|
505
509
|
skipFeedWait: true,
|
|
506
510
|
readSummary: currentSummary,
|
|
@@ -819,6 +823,7 @@ export function createGameCommand(): Command {
|
|
|
819
823
|
.description('Start or resume the owner game runtime, then stream events as NDJSON until game_over. Pass --no-watch to exit after allocation. Pass --force to replace an orphaned game-start runtime.')
|
|
820
824
|
.option('--no-watch', 'exit after allocation instead of streaming events through game_over')
|
|
821
825
|
.option('--force', 'force restart: kill any lingering game-start stream process and start fresh')
|
|
826
|
+
.option('--channel-url <url>', 'forward each NDJSON line via HTTP POST to this URL')
|
|
822
827
|
.action(runGameStart);
|
|
823
828
|
|
|
824
829
|
game
|
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { runHermesSetup } from './hermes.js';
|
|
3
|
-
import { spawnSync } from 'child_process';
|
|
4
|
-
|
|
5
|
-
vi.mock('child_process', () => ({
|
|
6
|
-
spawnSync: vi.fn(),
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
const mockedSpawnSync = vi.mocked(spawnSync);
|
|
10
|
-
|
|
11
|
-
function mockSpawn(overrides: Array<{ status: number; stdout?: string; stderr?: string }>) {
|
|
12
|
-
mockedSpawnSync.mockImplementation((_cmd: any, _args?: any, _opts?: any): any => {
|
|
13
|
-
const next = overrides.shift();
|
|
14
|
-
if (!next) throw new Error('Unexpected spawnSync call');
|
|
15
|
-
return {
|
|
16
|
-
status: next.status,
|
|
17
|
-
stdout: next.stdout ?? '',
|
|
18
|
-
stderr: next.stderr ?? '',
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
vi.clearAllMocks();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('runHermesSetup', () => {
|
|
28
|
-
it('returns error when hermes CLI is not found', () => {
|
|
29
|
-
mockSpawn([{ status: 1, stderr: 'command not found' }]);
|
|
30
|
-
const r = runHermesSetup({});
|
|
31
|
-
expect(r.exitCode).toBe(1);
|
|
32
|
-
expect(r.output.some((l) => l.includes('not found'))).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('reports already-enabled when clawclaw is in plugins list', () => {
|
|
36
|
-
mockSpawn([
|
|
37
|
-
{ status: 0 }, // --version
|
|
38
|
-
{ status: 0, stdout: 'clawclaw\nother-plugin' }, // plugins list
|
|
39
|
-
]);
|
|
40
|
-
const r = runHermesSetup({});
|
|
41
|
-
expect(r.exitCode).toBe(0);
|
|
42
|
-
expect(r.output.some((l) => l.includes('already enabled'))).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('dry-run shows pending message when not enabled', () => {
|
|
46
|
-
mockSpawn([
|
|
47
|
-
{ status: 0 }, // --version
|
|
48
|
-
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
49
|
-
]);
|
|
50
|
-
const r = runHermesSetup({});
|
|
51
|
-
expect(r.exitCode).toBe(2);
|
|
52
|
-
expect(r.output.some((l) => l.includes('Dry-run'))).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('--print shows the command that would run', () => {
|
|
56
|
-
mockSpawn([
|
|
57
|
-
{ status: 0 }, // --version
|
|
58
|
-
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
59
|
-
]);
|
|
60
|
-
const r = runHermesSetup({ print: true });
|
|
61
|
-
expect(r.exitCode).toBe(0);
|
|
62
|
-
expect(r.output.some((l) => l.includes('hermes plugins enable clawclaw'))).toBe(true);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('-y runs hermes plugins enable and succeeds', () => {
|
|
66
|
-
mockSpawn([
|
|
67
|
-
{ status: 0 }, // --version
|
|
68
|
-
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
69
|
-
{ status: 0, stdout: 'plugin enabled' }, // plugins enable
|
|
70
|
-
]);
|
|
71
|
-
const r = runHermesSetup({ yes: true });
|
|
72
|
-
expect(r.exitCode).toBe(0);
|
|
73
|
-
expect(r.output.some((l) => l.includes('enabled'))).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('-y handles hermes plugins enable failure', () => {
|
|
77
|
-
mockSpawn([
|
|
78
|
-
{ status: 0 }, // --version
|
|
79
|
-
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
80
|
-
{ status: 1, stderr: 'permission denied' }, // plugins enable
|
|
81
|
-
]);
|
|
82
|
-
const r = runHermesSetup({ yes: true });
|
|
83
|
-
expect(r.exitCode).toBe(1);
|
|
84
|
-
expect(r.output.some((l) => l.includes('Failed'))).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('already-enabled short-circuits --print', () => {
|
|
88
|
-
mockSpawn([
|
|
89
|
-
{ status: 0 }, // --version
|
|
90
|
-
{ status: 0, stdout: 'clawclaw' }, // plugins list
|
|
91
|
-
]);
|
|
92
|
-
const r = runHermesSetup({ print: true });
|
|
93
|
-
expect(r.exitCode).toBe(0);
|
|
94
|
-
expect(r.output.some((l) => l.includes('already enabled'))).toBe(true);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
1
|
+
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { runHermesSetup } from './hermes.js';
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
|
+
|
|
5
|
+
vi.mock('child_process', () => ({
|
|
6
|
+
spawnSync: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
const mockedSpawnSync = vi.mocked(spawnSync);
|
|
10
|
+
|
|
11
|
+
function mockSpawn(overrides: Array<{ status: number; stdout?: string; stderr?: string }>) {
|
|
12
|
+
mockedSpawnSync.mockImplementation((_cmd: any, _args?: any, _opts?: any): any => {
|
|
13
|
+
const next = overrides.shift();
|
|
14
|
+
if (!next) throw new Error('Unexpected spawnSync call');
|
|
15
|
+
return {
|
|
16
|
+
status: next.status,
|
|
17
|
+
stdout: next.stdout ?? '',
|
|
18
|
+
stderr: next.stderr ?? '',
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('runHermesSetup', () => {
|
|
28
|
+
it('returns error when hermes CLI is not found', () => {
|
|
29
|
+
mockSpawn([{ status: 1, stderr: 'command not found' }]);
|
|
30
|
+
const r = runHermesSetup({});
|
|
31
|
+
expect(r.exitCode).toBe(1);
|
|
32
|
+
expect(r.output.some((l) => l.includes('not found'))).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('reports already-enabled when clawclaw is in plugins list', () => {
|
|
36
|
+
mockSpawn([
|
|
37
|
+
{ status: 0 }, // --version
|
|
38
|
+
{ status: 0, stdout: 'clawclaw\nother-plugin' }, // plugins list
|
|
39
|
+
]);
|
|
40
|
+
const r = runHermesSetup({});
|
|
41
|
+
expect(r.exitCode).toBe(0);
|
|
42
|
+
expect(r.output.some((l) => l.includes('already enabled'))).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('dry-run shows pending message when not enabled', () => {
|
|
46
|
+
mockSpawn([
|
|
47
|
+
{ status: 0 }, // --version
|
|
48
|
+
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
49
|
+
]);
|
|
50
|
+
const r = runHermesSetup({});
|
|
51
|
+
expect(r.exitCode).toBe(2);
|
|
52
|
+
expect(r.output.some((l) => l.includes('Dry-run'))).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('--print shows the command that would run', () => {
|
|
56
|
+
mockSpawn([
|
|
57
|
+
{ status: 0 }, // --version
|
|
58
|
+
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
59
|
+
]);
|
|
60
|
+
const r = runHermesSetup({ print: true });
|
|
61
|
+
expect(r.exitCode).toBe(0);
|
|
62
|
+
expect(r.output.some((l) => l.includes('hermes plugins enable clawclaw'))).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('-y runs hermes plugins enable and succeeds', () => {
|
|
66
|
+
mockSpawn([
|
|
67
|
+
{ status: 0 }, // --version
|
|
68
|
+
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
69
|
+
{ status: 0, stdout: 'plugin enabled' }, // plugins enable
|
|
70
|
+
]);
|
|
71
|
+
const r = runHermesSetup({ yes: true });
|
|
72
|
+
expect(r.exitCode).toBe(0);
|
|
73
|
+
expect(r.output.some((l) => l.includes('enabled'))).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('-y handles hermes plugins enable failure', () => {
|
|
77
|
+
mockSpawn([
|
|
78
|
+
{ status: 0 }, // --version
|
|
79
|
+
{ status: 0, stdout: 'other-plugin' }, // plugins list
|
|
80
|
+
{ status: 1, stderr: 'permission denied' }, // plugins enable
|
|
81
|
+
]);
|
|
82
|
+
const r = runHermesSetup({ yes: true });
|
|
83
|
+
expect(r.exitCode).toBe(1);
|
|
84
|
+
expect(r.output.some((l) => l.includes('Failed'))).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('already-enabled short-circuits --print', () => {
|
|
88
|
+
mockSpawn([
|
|
89
|
+
{ status: 0 }, // --version
|
|
90
|
+
{ status: 0, stdout: 'clawclaw' }, // plugins list
|
|
91
|
+
]);
|
|
92
|
+
const r = runHermesSetup({ print: true });
|
|
93
|
+
expect(r.exitCode).toBe(0);
|
|
94
|
+
expect(r.output.some((l) => l.includes('already enabled'))).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `ccl setup hermes` — enable the clawclaw plugin in Hermes via `hermes plugins enable`.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Command } from 'commander';
|
|
6
|
-
import { spawnSync } from 'child_process';
|
|
7
|
-
|
|
8
|
-
export const PLUGIN_ID = 'clawclaw';
|
|
9
|
-
|
|
10
|
-
export interface SetupHermesResult {
|
|
11
|
-
exitCode: number;
|
|
12
|
-
output: string[];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function runHermesSetup(flags: { yes?: boolean; print?: boolean }): SetupHermesResult {
|
|
16
|
-
const out: string[] = [];
|
|
17
|
-
|
|
18
|
-
// Check hermes is available
|
|
19
|
-
const which = spawnSync('hermes', ['--version'], { stdio: 'pipe', timeout: 5000 });
|
|
20
|
-
if (which.status !== 0) {
|
|
21
|
-
out.push('hermes CLI not found. Install it first, then re-run.');
|
|
22
|
-
return { exitCode: 1, output: out };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Check current plugin status
|
|
26
|
-
const list = spawnSync('hermes', ['plugins', 'list'], {
|
|
27
|
-
stdio: 'pipe',
|
|
28
|
-
timeout: 5000,
|
|
29
|
-
env: { ...process.env, HERMES_PLUGINS_DEBUG: '1' },
|
|
30
|
-
});
|
|
31
|
-
const listOutput = list.stdout?.toString() ?? '';
|
|
32
|
-
const alreadyEnabled = listOutput.includes(PLUGIN_ID) && !listOutput.includes(`${PLUGIN_ID} (disabled)`);
|
|
33
|
-
|
|
34
|
-
if (alreadyEnabled) {
|
|
35
|
-
out.push(`Plugin "${PLUGIN_ID}" is already enabled in Hermes.`);
|
|
36
|
-
return { exitCode: 0, output: out };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (flags.print) {
|
|
40
|
-
out.push(`# Will run: hermes plugins enable ${PLUGIN_ID}`);
|
|
41
|
-
return { exitCode: 0, output: out };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!flags.yes) {
|
|
45
|
-
out.push(`Pending: hermes plugins enable ${PLUGIN_ID}`);
|
|
46
|
-
out.push(``);
|
|
47
|
-
out.push(`Dry-run only. Re-run with -y to apply.`);
|
|
48
|
-
return { exitCode: 2, output: out };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const result = spawnSync('hermes', ['plugins', 'enable', PLUGIN_ID], {
|
|
52
|
-
stdio: 'pipe',
|
|
53
|
-
timeout: 10000,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (result.status !== 0) {
|
|
57
|
-
out.push(`Failed to enable plugin: ${result.stderr?.toString() ?? result.stdout?.toString() ?? 'unknown error'}`);
|
|
58
|
-
return { exitCode: 1, output: out };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
out.push(`Plugin "${PLUGIN_ID}" enabled in Hermes.`);
|
|
62
|
-
out.push(`Verify: HERMES_PLUGINS_DEBUG=1 hermes plugins list`);
|
|
63
|
-
return { exitCode: 0, output: out };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function createSetupHermesSubcommand(): Command {
|
|
67
|
-
return new Command('hermes')
|
|
68
|
-
.description('Enable the clawclaw plugin in Hermes via "hermes plugins enable".')
|
|
69
|
-
.option('-y, --yes', 'Apply changes (default is dry-run)')
|
|
70
|
-
.option('--print', 'Only print the command that would be run; do not execute')
|
|
71
|
-
.action((opts: { yes?: boolean; print?: boolean }) => {
|
|
72
|
-
const result = runHermesSetup({ yes: opts.yes, print: opts.print });
|
|
73
|
-
for (const line of result.output) console.log(line);
|
|
74
|
-
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* `ccl setup hermes` — enable the clawclaw plugin in Hermes via `hermes plugins enable`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { spawnSync } from 'child_process';
|
|
7
|
+
|
|
8
|
+
export const PLUGIN_ID = 'clawclaw';
|
|
9
|
+
|
|
10
|
+
export interface SetupHermesResult {
|
|
11
|
+
exitCode: number;
|
|
12
|
+
output: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function runHermesSetup(flags: { yes?: boolean; print?: boolean }): SetupHermesResult {
|
|
16
|
+
const out: string[] = [];
|
|
17
|
+
|
|
18
|
+
// Check hermes is available
|
|
19
|
+
const which = spawnSync('hermes', ['--version'], { stdio: 'pipe', timeout: 5000 });
|
|
20
|
+
if (which.status !== 0) {
|
|
21
|
+
out.push('hermes CLI not found. Install it first, then re-run.');
|
|
22
|
+
return { exitCode: 1, output: out };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Check current plugin status
|
|
26
|
+
const list = spawnSync('hermes', ['plugins', 'list'], {
|
|
27
|
+
stdio: 'pipe',
|
|
28
|
+
timeout: 5000,
|
|
29
|
+
env: { ...process.env, HERMES_PLUGINS_DEBUG: '1' },
|
|
30
|
+
});
|
|
31
|
+
const listOutput = list.stdout?.toString() ?? '';
|
|
32
|
+
const alreadyEnabled = listOutput.includes(PLUGIN_ID) && !listOutput.includes(`${PLUGIN_ID} (disabled)`);
|
|
33
|
+
|
|
34
|
+
if (alreadyEnabled) {
|
|
35
|
+
out.push(`Plugin "${PLUGIN_ID}" is already enabled in Hermes.`);
|
|
36
|
+
return { exitCode: 0, output: out };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (flags.print) {
|
|
40
|
+
out.push(`# Will run: hermes plugins enable ${PLUGIN_ID}`);
|
|
41
|
+
return { exitCode: 0, output: out };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!flags.yes) {
|
|
45
|
+
out.push(`Pending: hermes plugins enable ${PLUGIN_ID}`);
|
|
46
|
+
out.push(``);
|
|
47
|
+
out.push(`Dry-run only. Re-run with -y to apply.`);
|
|
48
|
+
return { exitCode: 2, output: out };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result = spawnSync('hermes', ['plugins', 'enable', PLUGIN_ID], {
|
|
52
|
+
stdio: 'pipe',
|
|
53
|
+
timeout: 10000,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (result.status !== 0) {
|
|
57
|
+
out.push(`Failed to enable plugin: ${result.stderr?.toString() ?? result.stdout?.toString() ?? 'unknown error'}`);
|
|
58
|
+
return { exitCode: 1, output: out };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
out.push(`Plugin "${PLUGIN_ID}" enabled in Hermes.`);
|
|
62
|
+
out.push(`Verify: HERMES_PLUGINS_DEBUG=1 hermes plugins list`);
|
|
63
|
+
return { exitCode: 0, output: out };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createSetupHermesSubcommand(): Command {
|
|
67
|
+
return new Command('hermes')
|
|
68
|
+
.description('Enable the clawclaw plugin in Hermes via "hermes plugins enable".')
|
|
69
|
+
.option('-y, --yes', 'Apply changes (default is dry-run)')
|
|
70
|
+
.option('--print', 'Only print the command that would be run; do not execute')
|
|
71
|
+
.action((opts: { yes?: boolean; print?: boolean }) => {
|
|
72
|
+
const result = runHermesSetup({ yes: opts.yes, print: opts.print });
|
|
73
|
+
for (const line of result.output) console.log(line);
|
|
74
|
+
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createSetupOpenclawSubcommand } from './openclaw.js';
|
|
3
|
-
import { createSetupHermesSubcommand } from './hermes.js';
|
|
4
|
-
import { createSetupCodexSubcommand } from './codex.js';
|
|
5
|
-
|
|
6
|
-
export function createSetupCommand(): Command {
|
|
7
|
-
const setup = new Command('setup');
|
|
8
|
-
setup.description('Install recommended config snippets into agent host config files.');
|
|
9
|
-
setup.addCommand(createSetupOpenclawSubcommand());
|
|
10
|
-
setup.addCommand(createSetupHermesSubcommand());
|
|
11
|
-
setup.addCommand(createSetupCodexSubcommand());
|
|
12
|
-
return setup;
|
|
13
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createSetupOpenclawSubcommand } from './openclaw.js';
|
|
3
|
+
import { createSetupHermesSubcommand } from './hermes.js';
|
|
4
|
+
import { createSetupCodexSubcommand } from './codex.js';
|
|
5
|
+
|
|
6
|
+
export function createSetupCommand(): Command {
|
|
7
|
+
const setup = new Command('setup');
|
|
8
|
+
setup.description('Install recommended config snippets into agent host config files.');
|
|
9
|
+
setup.addCommand(createSetupOpenclawSubcommand());
|
|
10
|
+
setup.addCommand(createSetupHermesSubcommand());
|
|
11
|
+
setup.addCommand(createSetupCodexSubcommand());
|
|
12
|
+
return setup;
|
|
13
|
+
}
|