@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 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.5'
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 Commands ──
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
- // create: register a new agent
802
- for (const prefix of ['clawarena', 'clawarena-openclaw']) {
803
- program
804
- .command(`${prefix}:create`)
805
- .description('Create a new agent (optional: provide a name)')
806
- .action(async (...args: unknown[]) => {
807
- const opts = parseCliOptions(args)
808
- const name = opts.name || opts.positional[0] || undefined
809
- const cfg = getConfig(api)
810
- try {
811
- const credentials = await autoRegisterAgent(cfg.baseUrl, name)
812
- process.stdout.write(`${JSON.stringify({
813
- ok: true,
814
- agentId: credentials.agentId,
815
- name: credentials.name,
816
- apiKey: credentials.apiKey,
817
- baseUrl: credentials.baseUrl,
818
- _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.',
819
- }, null, 2)}\n`)
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
- // connect: import existing API key
831
- for (const prefix of ['clawarena', 'clawarena-openclaw']) {
832
- program
833
- .command(`${prefix}:connect`)
834
- .description('Connect with an existing API key')
835
- .action(async (...args: unknown[]) => {
836
- const opts = parseCliOptions(args)
837
- const apiKey = opts.apiKey || opts.positional[0] || ''
838
- if (!apiKey) {
839
- process.stdout.write('Usage: clawarena:connect <api-key> OR clawarena:connect --api-key <key>\n')
840
- return
841
- }
842
- const cfg = getConfig(api)
843
- try {
844
- const me = await requestArena(cfg.baseUrl, apiKey, 'GET', '/api/agents/me')
845
- const credentials: Credentials = {
846
- schemaVersion: CREDENTIALS_SCHEMA_VERSION,
847
- agentId: String(me.data.agentId || ''),
848
- name: String(me.data.name || ''),
849
- apiKey,
850
- baseUrl: cfg.baseUrl,
851
- source: 'plugin_import',
852
- updatedAt: new Date().toISOString(),
853
- }
854
- await saveCredentials(credentials)
855
- process.stdout.write(`${JSON.stringify({
856
- ok: true,
857
- agentId: credentials.agentId,
858
- name: credentials.name,
859
- apiKey,
860
- baseUrl: cfg.baseUrl,
861
- _note: 'Show the full apiKey to the user for web login.',
862
- }, null, 2)}\n`)
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
- // ls: show current agent info from server
874
- for (const prefix of ['clawarena', 'clawarena-openclaw']) {
875
- program
876
- .command(`${prefix}:ls`)
877
- .description('Show current agent info')
878
- .action(async () => {
879
- try {
880
- const creds = await loadCredentials()
881
- if (!creds?.apiKey) {
882
- process.stdout.write('No agent connected. Run clawarena:create or clawarena:connect.\n')
883
- return
884
- }
885
- const cfg = getConfig(api)
886
- const me = await requestArena(cfg.baseUrl, creds.apiKey, 'GET', '/api/agents/me')
887
- process.stdout.write(`${JSON.stringify({
888
- agentId: me.data.agentId,
889
- name: me.data.name,
890
- rating: me.data.rating,
891
- gamesPlayed: me.data.gamesPlayed,
892
- wins: me.data.wins,
893
- apiKey: creds.apiKey,
894
- source: creds.source,
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
- // status
905
- for (const prefix of ['clawarena', 'clawarena-openclaw']) {
906
- program
907
- .command(`${prefix}:status`)
908
- .description('Show runner status')
909
- .action(async () => {
910
- try {
911
- const agents = await loadAllAgents()
912
- if (agents.length === 0) {
913
- process.stdout.write(`${JSON.stringify({ ok: false, error: 'No agent. Run clawarena:create first.' }, null, 2)}\n`)
914
- return
915
- }
916
-
917
- const cfg = getConfig(api)
918
- const results: Record<string, unknown>[] = []
919
-
920
- for (const agent of agents) {
921
- const baseUrl = agent.baseUrl || cfg.baseUrl
922
- const [meRes, runtimeRes, queueRes] = await Promise.all([
923
- requestArena(baseUrl, agent.apiKey, 'GET', '/api/agents/me', { expectedStatuses: [200] }).catch(() => null),
924
- requestArena(baseUrl, agent.apiKey, 'GET', '/api/agents/runtime', { expectedStatuses: [200] }).catch(() => null),
925
- requestArena(baseUrl, agent.apiKey, 'GET', '/api/queue/status', { expectedStatuses: [200] }).catch(() => null),
926
- ])
927
-
928
- const localRunner = runners.get(agent.agentId)
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
- // start / stop
953
- for (const prefix of ['clawarena', 'clawarena-openclaw']) {
954
- program
955
- .command(`${prefix}:start`)
956
- .description('Start the runner')
957
- .action(async () => {
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
- program
965
- .command(`${prefix}:stop`)
966
- .description('Stop the runner')
967
- .action(() => {
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
- // pause / resume
974
- for (const prefix of ['clawarena', 'clawarena-openclaw']) {
975
- program
976
- .command(`${prefix}:pause`)
977
- .description('Pause matchmaking')
978
- .action(async () => {
979
- try {
980
- const creds = await loadCredentials()
981
- if (!creds?.apiKey) { process.stdout.write('No agent.\n'); return }
982
- const cfg = getConfig(api)
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
- program
990
- .command(`${prefix}:resume`)
991
- .description('Resume matchmaking')
992
- .action(async () => {
993
- try {
994
- const creds = await loadCredentials()
995
- if (!creds?.apiKey) { process.stdout.write('No agent.\n'); return }
996
- const cfg = getConfig(api)
997
- await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/agents/preferences', { body: { paused: false } })
998
- await requestArena(cfg.baseUrl, creds.apiKey, 'POST', '/api/agents/runtime/queue/ensure', { expectedStatuses: [200, 429, 503] })
999
- process.stdout.write('Resumed.\n')
1000
- } catch (err: unknown) { process.stdout.write(`resume error: ${(err as Error).message}\n`) }
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
- // modes
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
- .command(`${prefix}:modes`)
1008
- .description('Set preferred game modes (comma-separated)')
1009
- .action(async (...args: unknown[]) => {
1010
- const opts = parseCliOptions(args)
1011
- const modes = opts.positional.join(',').split(',').map(s => s.trim()).filter(Boolean)
1012
- if (modes.length === 0) { process.stdout.write('Usage: clawarena:modes tribunal,texas_holdem\n'); return }
1013
- try {
1014
- const creds = await loadCredentials()
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 })
@@ -2,7 +2,7 @@
2
2
  "id": "clawarena",
3
3
  "name": "ClawArena OpenClaw",
4
4
  "description": "Connect OpenClaw agents to ClawArena — auto-queue, auto-play, always online.",
5
- "version": "0.2.5",
5
+ "version": "0.2.6",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawlabz/clawarena",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Official ClawArena plugin for OpenClaw Gateway.",
5
5
  "type": "module",
6
6
  "license": "MIT",