@clawlabz/clawarena 0.2.5 → 0.2.6
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/index.ts +198 -225
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ declare function setTimeout(fn: () => void, ms: number): unknown
|
|
|
5
5
|
declare function clearTimeout(id: unknown): void
|
|
6
6
|
declare function fetch(url: string, init?: Record<string, unknown>): Promise<{ status: number; text: () => Promise<string>; headers: Headers }>
|
|
7
7
|
|
|
8
|
-
const VERSION = '0.2.
|
|
8
|
+
const VERSION = '0.2.6'
|
|
9
9
|
const PLUGIN_ID = 'clawarena'
|
|
10
10
|
const DEFAULT_BASE_URL = 'https://arena.clawlabz.xyz'
|
|
11
11
|
const DEFAULT_HEARTBEAT_SECONDS = 20
|
|
@@ -27,13 +27,16 @@ interface GatewayMethodContext {
|
|
|
27
27
|
respond?: GatewayRespond
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
interface CliCommandChain {
|
|
31
|
+
description: (text: string) => CliCommandChain
|
|
32
|
+
argument: (spec: string, desc?: string) => CliCommandChain
|
|
33
|
+
option: (flags: string, desc: string) => CliCommandChain
|
|
34
|
+
action: (handler: (...args: unknown[]) => void) => CliCommandChain
|
|
35
|
+
command: (name: string) => CliCommandChain
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
interface CliProgram {
|
|
31
|
-
command: (name: string) =>
|
|
32
|
-
description: (text: string) => {
|
|
33
|
-
option: (flags: string, desc: string) => ReturnType<CliProgram['command']>['description']
|
|
34
|
-
action: (handler: (...args: unknown[]) => void) => void
|
|
35
|
-
}
|
|
36
|
-
}
|
|
39
|
+
command: (name: string) => CliCommandChain
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
interface RegisterCliContext {
|
|
@@ -785,239 +788,209 @@ export default function register(api: OpenClawApi) {
|
|
|
785
788
|
return credentials
|
|
786
789
|
}
|
|
787
790
|
|
|
788
|
-
// ── CLI
|
|
789
|
-
|
|
790
|
-
const CLI_COMMANDS = [
|
|
791
|
-
'clawarena:create', 'clawarena:connect', 'clawarena:ls',
|
|
792
|
-
'clawarena:status', 'clawarena:start', 'clawarena:stop',
|
|
793
|
-
'clawarena:pause', 'clawarena:resume', 'clawarena:modes',
|
|
794
|
-
'clawarena-openclaw:create', 'clawarena-openclaw:connect', 'clawarena-openclaw:ls',
|
|
795
|
-
'clawarena-openclaw:status', 'clawarena-openclaw:start', 'clawarena-openclaw:stop',
|
|
796
|
-
'clawarena-openclaw:pause', 'clawarena-openclaw:resume', 'clawarena-openclaw:modes',
|
|
797
|
-
]
|
|
798
|
-
|
|
799
|
-
api.registerCli?.(({ program }) => {
|
|
791
|
+
// ── CLI Command Handlers ──
|
|
800
792
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
if (cfg.autoStart) {
|
|
821
|
-
const saved = await loadAgent(credentials.agentId)
|
|
822
|
-
if (saved) await startRunner(api, saved)
|
|
823
|
-
}
|
|
824
|
-
} catch (err: unknown) {
|
|
825
|
-
process.stdout.write(`create error: ${(err as Error).message}\n`)
|
|
826
|
-
}
|
|
827
|
-
})
|
|
793
|
+
async function handleCreate(nameArg?: string) {
|
|
794
|
+
const name = typeof nameArg === 'string' ? nameArg : undefined
|
|
795
|
+
const cfg = getConfig(api)
|
|
796
|
+
try {
|
|
797
|
+
const credentials = await autoRegisterAgent(cfg.baseUrl, name)
|
|
798
|
+
process.stdout.write(`${JSON.stringify({
|
|
799
|
+
ok: true,
|
|
800
|
+
agentId: credentials.agentId,
|
|
801
|
+
name: credentials.name,
|
|
802
|
+
apiKey: credentials.apiKey,
|
|
803
|
+
baseUrl: credentials.baseUrl,
|
|
804
|
+
_note: 'IMPORTANT: Show the full apiKey to the user. They need it to log in at the web dashboard. Do NOT hide or mask it.',
|
|
805
|
+
}, null, 2)}\n`)
|
|
806
|
+
if (cfg.autoStart) {
|
|
807
|
+
const saved = await loadAgent(credentials.agentId)
|
|
808
|
+
if (saved) await startRunner(api, saved)
|
|
809
|
+
}
|
|
810
|
+
} catch (err: unknown) {
|
|
811
|
+
process.stdout.write(`create error: ${(err as Error).message}\n`)
|
|
828
812
|
}
|
|
813
|
+
}
|
|
829
814
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
if (cfg.autoStart) {
|
|
864
|
-
const saved = await loadAgent(credentials.agentId)
|
|
865
|
-
if (saved) await startRunner(api, saved)
|
|
866
|
-
}
|
|
867
|
-
} catch (err: unknown) {
|
|
868
|
-
process.stdout.write(`connect error: ${(err as Error).message}\n`)
|
|
869
|
-
}
|
|
870
|
-
})
|
|
815
|
+
async function handleConnect(apiKeyArg?: string) {
|
|
816
|
+
const apiKey = typeof apiKeyArg === 'string' ? apiKeyArg : ''
|
|
817
|
+
if (!apiKey) {
|
|
818
|
+
process.stdout.write('Usage: openclaw clawarena connect <api-key>\n')
|
|
819
|
+
return
|
|
820
|
+
}
|
|
821
|
+
const cfg = getConfig(api)
|
|
822
|
+
try {
|
|
823
|
+
const me = await requestArena(cfg.baseUrl, apiKey, 'GET', '/api/agents/me')
|
|
824
|
+
const credentials: Credentials = {
|
|
825
|
+
schemaVersion: CREDENTIALS_SCHEMA_VERSION,
|
|
826
|
+
agentId: String(me.data.agentId || ''),
|
|
827
|
+
name: String(me.data.name || ''),
|
|
828
|
+
apiKey,
|
|
829
|
+
baseUrl: cfg.baseUrl,
|
|
830
|
+
source: 'plugin_import',
|
|
831
|
+
updatedAt: new Date().toISOString(),
|
|
832
|
+
}
|
|
833
|
+
await saveCredentials(credentials)
|
|
834
|
+
process.stdout.write(`${JSON.stringify({
|
|
835
|
+
ok: true,
|
|
836
|
+
agentId: credentials.agentId,
|
|
837
|
+
name: credentials.name,
|
|
838
|
+
apiKey,
|
|
839
|
+
baseUrl: cfg.baseUrl,
|
|
840
|
+
_note: 'Show the full apiKey to the user for web login.',
|
|
841
|
+
}, null, 2)}\n`)
|
|
842
|
+
if (cfg.autoStart) {
|
|
843
|
+
const saved = await loadAgent(credentials.agentId)
|
|
844
|
+
if (saved) await startRunner(api, saved)
|
|
845
|
+
}
|
|
846
|
+
} catch (err: unknown) {
|
|
847
|
+
process.stdout.write(`connect error: ${(err as Error).message}\n`)
|
|
871
848
|
}
|
|
849
|
+
}
|
|
872
850
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
.
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
baseUrl: creds.baseUrl,
|
|
896
|
-
_note: 'Show the full apiKey to the user for web login.',
|
|
897
|
-
}, null, 2)}\n`)
|
|
898
|
-
} catch (err: unknown) {
|
|
899
|
-
process.stdout.write(`ls error: ${(err as Error).message}\n`)
|
|
900
|
-
}
|
|
901
|
-
})
|
|
851
|
+
async function handleLs() {
|
|
852
|
+
try {
|
|
853
|
+
const creds = await loadCredentials()
|
|
854
|
+
if (!creds?.apiKey) {
|
|
855
|
+
process.stdout.write('No agent connected. Run: openclaw clawarena create\n')
|
|
856
|
+
return
|
|
857
|
+
}
|
|
858
|
+
const cfg = getConfig(api)
|
|
859
|
+
const me = await requestArena(cfg.baseUrl, creds.apiKey, 'GET', '/api/agents/me')
|
|
860
|
+
process.stdout.write(`${JSON.stringify({
|
|
861
|
+
agentId: me.data.agentId,
|
|
862
|
+
name: me.data.name,
|
|
863
|
+
rating: me.data.rating,
|
|
864
|
+
gamesPlayed: me.data.gamesPlayed,
|
|
865
|
+
wins: me.data.wins,
|
|
866
|
+
apiKey: creds.apiKey,
|
|
867
|
+
source: creds.source,
|
|
868
|
+
baseUrl: creds.baseUrl,
|
|
869
|
+
_note: 'Show the full apiKey to the user for web login.',
|
|
870
|
+
}, null, 2)}\n`)
|
|
871
|
+
} catch (err: unknown) {
|
|
872
|
+
process.stdout.write(`ls error: ${(err as Error).message}\n`)
|
|
902
873
|
}
|
|
874
|
+
}
|
|
903
875
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
.
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
results.push({
|
|
930
|
-
agentId: agent.agentId,
|
|
931
|
-
name: meRes?.data?.name || agent.name,
|
|
932
|
-
rating: meRes?.data?.rating ?? null,
|
|
933
|
-
runtime: runtimeRes?.data || null,
|
|
934
|
-
queue: queueRes?.data || null,
|
|
935
|
-
localRunner: localRunner ? localRunner.status : 'not_in_this_process',
|
|
936
|
-
baseUrl,
|
|
937
|
-
})
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
process.stdout.write(`${JSON.stringify({
|
|
941
|
-
ok: true,
|
|
942
|
-
plugin: PLUGIN_ID,
|
|
943
|
-
version: VERSION,
|
|
944
|
-
agents: results,
|
|
945
|
-
}, null, 2)}\n`)
|
|
946
|
-
} catch (err: unknown) {
|
|
947
|
-
process.stdout.write(`status error: ${(err as Error).message}\n`)
|
|
948
|
-
}
|
|
876
|
+
async function handleStatus() {
|
|
877
|
+
try {
|
|
878
|
+
const agents = await loadAllAgents()
|
|
879
|
+
if (agents.length === 0) {
|
|
880
|
+
process.stdout.write(`${JSON.stringify({ ok: false, error: 'No agent. Run: openclaw clawarena create' }, null, 2)}\n`)
|
|
881
|
+
return
|
|
882
|
+
}
|
|
883
|
+
const cfg = getConfig(api)
|
|
884
|
+
const results: Record<string, unknown>[] = []
|
|
885
|
+
for (const agent of agents) {
|
|
886
|
+
const baseUrl = agent.baseUrl || cfg.baseUrl
|
|
887
|
+
const [meRes, runtimeRes, queueRes] = await Promise.all([
|
|
888
|
+
requestArena(baseUrl, agent.apiKey, 'GET', '/api/agents/me', { expectedStatuses: [200] }).catch(() => null),
|
|
889
|
+
requestArena(baseUrl, agent.apiKey, 'GET', '/api/agents/runtime', { expectedStatuses: [200] }).catch(() => null),
|
|
890
|
+
requestArena(baseUrl, agent.apiKey, 'GET', '/api/queue/status', { expectedStatuses: [200] }).catch(() => null),
|
|
891
|
+
])
|
|
892
|
+
const localRunner = runners.get(agent.agentId)
|
|
893
|
+
results.push({
|
|
894
|
+
agentId: agent.agentId,
|
|
895
|
+
name: meRes?.data?.name || agent.name,
|
|
896
|
+
rating: meRes?.data?.rating ?? null,
|
|
897
|
+
runtime: runtimeRes?.data || null,
|
|
898
|
+
queue: queueRes?.data || null,
|
|
899
|
+
localRunner: localRunner ? localRunner.status : 'not_in_this_process',
|
|
900
|
+
baseUrl,
|
|
949
901
|
})
|
|
902
|
+
}
|
|
903
|
+
process.stdout.write(`${JSON.stringify({ ok: true, plugin: PLUGIN_ID, version: VERSION, agents: results }, null, 2)}\n`)
|
|
904
|
+
} catch (err: unknown) {
|
|
905
|
+
process.stdout.write(`status error: ${(err as Error).message}\n`)
|
|
950
906
|
}
|
|
907
|
+
}
|
|
951
908
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
const agents = await loadAllAgents()
|
|
959
|
-
if (agents.length === 0) { process.stdout.write('No agent. Run clawarena:create first.\n'); return }
|
|
960
|
-
for (const agent of agents) await startRunner(api, agent)
|
|
961
|
-
process.stdout.write('Runner started.\n')
|
|
962
|
-
})
|
|
909
|
+
async function handleStart() {
|
|
910
|
+
const agents = await loadAllAgents()
|
|
911
|
+
if (agents.length === 0) { process.stdout.write('No agent. Run: openclaw clawarena create\n'); return }
|
|
912
|
+
for (const agent of agents) await startRunner(api, agent)
|
|
913
|
+
process.stdout.write('Runner started.\n')
|
|
914
|
+
}
|
|
963
915
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
stopAllRunners(api)
|
|
969
|
-
process.stdout.write('Runner stop requested.\n')
|
|
970
|
-
})
|
|
971
|
-
}
|
|
916
|
+
function handleStop() {
|
|
917
|
+
stopAllRunners(api)
|
|
918
|
+
process.stdout.write('Runner stop requested.\n')
|
|
919
|
+
}
|
|
972
920
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/agents/preferences', { body: { paused: true } })
|
|
984
|
-
await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/queue/leave')
|
|
985
|
-
process.stdout.write('Paused.\n')
|
|
986
|
-
} catch (err: unknown) { process.stdout.write(`pause error: ${(err as Error).message}\n`) }
|
|
987
|
-
})
|
|
921
|
+
async function handlePause() {
|
|
922
|
+
try {
|
|
923
|
+
const creds = await loadCredentials()
|
|
924
|
+
if (!creds?.apiKey) { process.stdout.write('No agent.\n'); return }
|
|
925
|
+
const cfg = getConfig(api)
|
|
926
|
+
await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/agents/preferences', { body: { paused: true } })
|
|
927
|
+
await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/queue/leave')
|
|
928
|
+
process.stdout.write('Paused.\n')
|
|
929
|
+
} catch (err: unknown) { process.stdout.write(`pause error: ${(err as Error).message}\n`) }
|
|
930
|
+
}
|
|
988
931
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
}
|
|
932
|
+
async function handleResume() {
|
|
933
|
+
try {
|
|
934
|
+
const creds = await loadCredentials()
|
|
935
|
+
if (!creds?.apiKey) { process.stdout.write('No agent.\n'); return }
|
|
936
|
+
const cfg = getConfig(api)
|
|
937
|
+
await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/agents/preferences', { body: { paused: false } })
|
|
938
|
+
await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/agents/runtime/queue/ensure', { expectedStatuses: [200, 429, 503] })
|
|
939
|
+
process.stdout.write('Resumed.\n')
|
|
940
|
+
} catch (err: unknown) { process.stdout.write(`resume error: ${(err as Error).message}\n`) }
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
async function handleModes(modesArg?: string) {
|
|
944
|
+
const modes = (typeof modesArg === 'string' ? modesArg : '').split(',').map(s => s.trim()).filter(Boolean)
|
|
945
|
+
if (modes.length === 0) { process.stdout.write('Usage: openclaw clawarena modes tribunal,texas_holdem\n'); return }
|
|
946
|
+
try {
|
|
947
|
+
const creds = await loadCredentials()
|
|
948
|
+
if (!creds?.apiKey) { process.stdout.write('No agent.\n'); return }
|
|
949
|
+
const cfg = getConfig(api)
|
|
950
|
+
await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/agents/preferences', { body: { enabledModes: modes } })
|
|
951
|
+
process.stdout.write(`Modes set: ${modes.join(', ')}\n`)
|
|
952
|
+
} catch (err: unknown) { process.stdout.write(`modes error: ${(err as Error).message}\n`) }
|
|
953
|
+
}
|
|
1003
954
|
|
|
1004
|
-
|
|
955
|
+
// ── CLI Registration ──
|
|
956
|
+
|
|
957
|
+
const CLI_COMMANDS = [
|
|
958
|
+
// Space format (sub-command group)
|
|
959
|
+
'clawarena',
|
|
960
|
+
// Colon format (backward compat)
|
|
961
|
+
'clawarena:create', 'clawarena:connect', 'clawarena:ls',
|
|
962
|
+
'clawarena:status', 'clawarena:start', 'clawarena:stop',
|
|
963
|
+
'clawarena:pause', 'clawarena:resume', 'clawarena:modes',
|
|
964
|
+
'clawarena-openclaw:create', 'clawarena-openclaw:connect', 'clawarena-openclaw:ls',
|
|
965
|
+
'clawarena-openclaw:status', 'clawarena-openclaw:start', 'clawarena-openclaw:stop',
|
|
966
|
+
'clawarena-openclaw:pause', 'clawarena-openclaw:resume', 'clawarena-openclaw:modes',
|
|
967
|
+
]
|
|
968
|
+
|
|
969
|
+
api.registerCli?.(({ program }) => {
|
|
970
|
+
|
|
971
|
+
// ── Space format: `openclaw clawarena <sub>` ──
|
|
972
|
+
const arenaGroup = program.command('clawarena').description('ClawArena agent management')
|
|
973
|
+
arenaGroup.command('create').description('Create a new agent').argument('[name]', 'Agent name').action(handleCreate)
|
|
974
|
+
arenaGroup.command('connect').description('Connect with existing API key').argument('<api-key>', 'API key').action(handleConnect)
|
|
975
|
+
arenaGroup.command('ls').description('Show agent info').action(handleLs)
|
|
976
|
+
arenaGroup.command('status').description('Show runner status').action(handleStatus)
|
|
977
|
+
arenaGroup.command('start').description('Start runner').action(handleStart)
|
|
978
|
+
arenaGroup.command('stop').description('Stop runner').action(handleStop)
|
|
979
|
+
arenaGroup.command('pause').description('Pause matchmaking').action(handlePause)
|
|
980
|
+
arenaGroup.command('resume').description('Resume matchmaking').action(handleResume)
|
|
981
|
+
arenaGroup.command('modes').description('Set preferred game modes').argument('<modes>', 'Comma-separated modes').action(handleModes)
|
|
982
|
+
|
|
983
|
+
// ── Colon format: `openclaw clawarena:ls` (backward compat) ──
|
|
1005
984
|
for (const prefix of ['clawarena', 'clawarena-openclaw']) {
|
|
1006
|
-
program
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
if (!creds?.apiKey) { process.stdout.write('No agent.\n'); return }
|
|
1016
|
-
const cfg = getConfig(api)
|
|
1017
|
-
await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/agents/preferences', { body: { enabledModes: modes } })
|
|
1018
|
-
process.stdout.write(`Modes set: ${modes.join(', ')}\n`)
|
|
1019
|
-
} catch (err: unknown) { process.stdout.write(`modes error: ${(err as Error).message}\n`) }
|
|
1020
|
-
})
|
|
985
|
+
program.command(`${prefix}:create`).description('Create a new agent').argument('[name]', 'Agent name').action(handleCreate)
|
|
986
|
+
program.command(`${prefix}:connect`).description('Connect with existing API key').argument('<api-key>', 'API key').action(handleConnect)
|
|
987
|
+
program.command(`${prefix}:ls`).description('Show agent info').action(handleLs)
|
|
988
|
+
program.command(`${prefix}:status`).description('Show runner status').action(handleStatus)
|
|
989
|
+
program.command(`${prefix}:start`).description('Start runner').action(handleStart)
|
|
990
|
+
program.command(`${prefix}:stop`).description('Stop runner').action(handleStop)
|
|
991
|
+
program.command(`${prefix}:pause`).description('Pause matchmaking').action(handlePause)
|
|
992
|
+
program.command(`${prefix}:resume`).description('Resume matchmaking').action(handleResume)
|
|
993
|
+
program.command(`${prefix}:modes`).description('Set preferred game modes').argument('<modes>', 'Comma-separated modes').action(handleModes)
|
|
1021
994
|
}
|
|
1022
995
|
|
|
1023
996
|
}, { commands: CLI_COMMANDS })
|
package/openclaw.plugin.json
CHANGED