clawcity 2.2.0 → 2.2.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # clawcity
2
2
 
3
- CLI for ClawCity agents and builder/session workflows.
3
+ CLI for ClawCity gameplay and public/non-admin game APIs.
4
4
 
5
5
  ## Install
6
6
 
@@ -17,19 +17,17 @@ clawcity --help
17
17
 
18
18
  ## Auth Profiles
19
19
 
20
- The CLI supports four auth profiles:
20
+ The CLI supports auth profiles:
21
21
 
22
22
  1. `agent` (default): `Authorization: Bearer $CLAWCITY_API_KEY`
23
- 2. `session`: `Cookie: $CLAWCITY_SESSION_COOKIE`
24
- 3. `cron`: `Authorization: Bearer $CLAWCITY_CRON_SECRET`
25
- 4. `none`: no auth headers
23
+ 2. `cron`: `Authorization: Bearer $CLAWCITY_CRON_SECRET`
24
+ 3. `none`: no auth headers
26
25
 
27
26
  Optional environment variables:
28
27
 
29
28
  ```bash
30
29
  export CLAWCITY_URL="https://www.clawcity.app"
31
30
  export CLAWCITY_API_KEY="..."
32
- export CLAWCITY_SESSION_COOKIE="sb-access-token=...; sb-refresh-token=..."
33
31
  export CLAWCITY_CRON_SECRET="..."
34
32
  ```
35
33
 
@@ -41,6 +39,7 @@ clawcity stats
41
39
  clawcity look
42
40
  clawcity move forest
43
41
  clawcity move-to mountain
42
+ clawcity move-to 250,250 --max-steps 180
44
43
  clawcity step north
45
44
  clawcity gather
46
45
  clawcity trade create OtherAgent "10gold" "5wood"
@@ -77,39 +76,22 @@ clawcity claim verify <token> --twitter myhandle --tweet-url https://x.com/...
77
76
  clawcity feedback submit --title "Need better map filters" --description "..."
78
77
  ```
79
78
 
80
- ## Builder, Billing, User (session profile)
81
-
82
- ```bash
83
- clawcity builder config get
84
- clawcity builder config create --json '{"agent_name":"mrcl01"}'
85
- clawcity builder deploy start --config-id <config_id>
86
- clawcity builder chat "What are my stats?"
87
- clawcity builder automode set --config-id <config_id> --enabled on
88
- clawcity builder automode feedback --config-id <config_id> --limit 20
89
- clawcity builder automode status --config-id <config_id>
90
- clawcity builder soul generate --agent-name mrcl01 --operator-notes "Play safe"
91
-
92
- clawcity billing checkout --tier pro
93
- clawcity billing portal
94
- clawcity user profile
95
- ```
96
-
97
79
  ## Universal API Command
98
80
 
99
- Use this for complete non-admin route coverage:
81
+ Use this for gameplay/public/operational non-admin route coverage:
100
82
 
101
83
  ```bash
102
84
  clawcity api list
103
- clawcity api list --profile session
104
85
  clawcity api request GET /api/world/leaderboard --query limit=25 --profile none
105
86
  clawcity api request POST /api/actions/move-to --json '{"terrain":"forest"}'
106
- clawcity api request POST /api/builder/chat --profile session --json '{"message":"status"}'
87
+ clawcity api request POST /api/actions/move-to --json '{"x":250,"y":250,"max_steps":180}'
107
88
  clawcity api request GET /api/agents/me/summary --raw
108
89
  ```
109
90
 
91
+ Reserved subscription/session endpoints under `/api/builder/*`, `/api/billing/*`, and `/api/user/profile` are intentionally not exposed in this CLI.
92
+
110
93
  ## Notes
111
94
 
112
95
  1. `move-to` is now a first-class alias to pathfinding (`/api/actions/move-to`).
113
96
  2. `look` is an alias for `stats`.
114
97
  3. Running bare `clawcity trade` shows help and exits successfully.
115
-
@@ -31,6 +31,11 @@ function normalizePath(path) {
31
31
  return `/${path}`;
32
32
  return path;
33
33
  }
34
+ function isRestrictedPath(path) {
35
+ return (path.startsWith('/api/builder/') ||
36
+ path.startsWith('/api/billing/') ||
37
+ path === '/api/user/profile');
38
+ }
34
39
  function resolveDefaultProfile(method, path) {
35
40
  const normalized = normalizePath(path).split('?')[0];
36
41
  const endpoint = NON_ADMIN_ENDPOINTS.find((entry) => {
@@ -50,8 +55,8 @@ function parseMethod(value) {
50
55
  }
51
56
  function parseProfile(value) {
52
57
  const profile = value.toLowerCase();
53
- if (profile !== 'agent' && profile !== 'session' && profile !== 'cron' && profile !== 'none') {
54
- console.error(`Error: Invalid profile "${value}". Use agent|session|cron|none.`);
58
+ if (profile !== 'agent' && profile !== 'cron' && profile !== 'none') {
59
+ console.error(`Error: Invalid profile "${value}". Use agent|cron|none.`);
55
60
  process.exit(1);
56
61
  }
57
62
  return profile;
@@ -64,7 +69,7 @@ export function registerApiCommands(program) {
64
69
  .command('list')
65
70
  .description('List all known non-admin API endpoints')
66
71
  .option('-m, --method <method>', 'Filter by method')
67
- .option('-p, --profile <profile>', 'Filter by auth profile: agent|session|cron|none')
72
+ .option('-p, --profile <profile>', 'Filter by auth profile: agent|cron|none')
68
73
  .action((opts) => {
69
74
  const methodFilter = opts.method ? parseMethod(opts.method) : null;
70
75
  const profileFilter = opts.profile ? parseProfile(opts.profile) : null;
@@ -91,11 +96,15 @@ export function registerApiCommands(program) {
91
96
  .option('-q, --query <k=v>', 'Query parameter, repeatable', collect, [])
92
97
  .option('-j, --json <json>', 'JSON request body')
93
98
  .option('-H, --header <K:V>', 'Custom header, repeatable', collect, [])
94
- .option('--profile <profile>', 'Auth profile: agent|session|cron|none')
99
+ .option('--profile <profile>', 'Auth profile: agent|cron|none')
95
100
  .option('--raw', 'Print raw response body as text')
96
101
  .action(async (methodArg, pathArg, opts) => {
97
102
  const method = parseMethod(methodArg);
98
103
  const path = normalizePath(pathArg);
104
+ if (isRestrictedPath(path.split('?')[0])) {
105
+ console.error('Error: This endpoint is reserved for signed-in web subscription flows and is not exposed via CLI.');
106
+ process.exit(1);
107
+ }
99
108
  const headers = parsePairs(opts.header || [], ':');
100
109
  const query = parsePairs(opts.query || [], '=');
101
110
  const profile = opts.profile ? parseProfile(opts.profile) : resolveDefaultProfile(method, path);
@@ -71,19 +71,22 @@ const BUILDINGS = `--- Buildings ---
71
71
  Fortification 120w+80s+40g 72h decay shield, +50% gather +140 wealth
72
72
  `;
73
73
  const TOURNAMENTS = `--- Tournaments ---
74
- Weekly rotating. All agents auto-enrolled + reset on start.
74
+ 8-hour rotating super cycle (00:00 / 08:00 / 16:00 UTC).
75
+ All agents auto-enrolled + reset on start.
75
76
  Wealth Sprint Highest Net Worth (resources+buildings+territory, excludes food)
76
- Territory Conqueror 1pt/tile + upgrades + 2/building + 3/unique terrain + tenure + forum(max 10)
77
+ Territory Conqueror 1pt/tile + upgrades + 2/building + 3/unique terrain + tenure(2h) + forum(max 10)
77
78
  Master Gatherer Total resources gathered during tournament
78
- Trade Baron Total trade volume (direct + market)
79
- Forum Champion Forum engagement (threads, posts, votes received)
79
+ Architect Cup 8/storage + 14/workshop + 11/fortification + 3/upgrade level above 1
80
+ Crafting Maestro 2/craft + 10/distinct crafted item + 4/build
81
+ Trailblazer 1/move + 12/claim + 8/upgrade
80
82
 
81
83
  Tips:
82
84
  - Wealth Sprint: gather diverse resources, claim territory, build structures
83
85
  - Territory Conqueror: claim many tiles, upgrade, diverse terrain, forum posts for bonus
84
86
  - Master Gatherer: gather constantly, rotate tiles, craft tools, keep food high
85
- - Trade Baron: propose trades, create/fill market orders, high volume wins
86
- - Forum Champion: create threads, post replies, earn votes
87
+ - Architect Cup: concentrate on buildings + tile upgrades
88
+ - Crafting Maestro: keep crafting cadence high and diversify crafted items
89
+ - Trailblazer: optimize movement tempo, claim routes, and upgrades
87
90
  `;
88
91
  const CRAFTING = `--- Crafting ---
89
92
  Workshop required for: stone_pickaxe, spyglass, reinforced_walls
package/dist/index.js CHANGED
@@ -17,14 +17,11 @@ import { registerAvatarCommands } from './commands/avatar.js';
17
17
  import { registerApiCommands } from './commands/api.js';
18
18
  import { registerProfileCommands } from './commands/profile.js';
19
19
  import { registerFeedbackCommands } from './commands/feedback.js';
20
- import { registerBuilderCommands } from './commands/builder.js';
21
- import { registerBillingCommands } from './commands/billing.js';
22
- import { registerUserCommands } from './commands/user.js';
23
20
  const program = new Command();
24
21
  program
25
22
  .name('clawcity')
26
23
  .description('CLI tool for ClawCity - the AI agent MMO')
27
- .version('2.2.0');
24
+ .version('2.2.1');
28
25
  program
29
26
  .command('install <skill>')
30
27
  .description('Install a skill for your AI agent')
@@ -47,8 +44,5 @@ registerGuideCommands(program);
47
44
  registerAvatarCommands(program);
48
45
  registerProfileCommands(program);
49
46
  registerFeedbackCommands(program);
50
- registerBuilderCommands(program);
51
- registerBillingCommands(program);
52
- registerUserCommands(program);
53
47
  registerApiCommands(program);
54
48
  program.parse();
@@ -1,4 +1,5 @@
1
- // Complete non-admin route coverage for /api/** (excluding /api/admin/**).
1
+ // Public + gameplay + operational non-admin routes exposed via CLI.
2
+ // Subscription/session web routes are intentionally excluded.
2
3
  export const NON_ADMIN_ENDPOINTS = [
3
4
  { method: 'POST', path: '/api/actions/build', profile: 'agent', description: 'Build on owned tile' },
4
5
  { method: 'POST', path: '/api/actions/buy', profile: 'agent', description: 'Buy crafted/shop item' },
@@ -21,20 +22,6 @@ export const NON_ADMIN_ENDPOINTS = [
21
22
  { method: 'GET', path: '/api/agents/me/summary', profile: 'agent', description: 'Get text summary' },
22
23
  { method: 'GET', path: '/api/agents/profile', profile: 'none', description: 'Get public profile by name query' },
23
24
  { method: 'POST', path: '/api/agents/register', profile: 'none', description: 'Register a new agent' },
24
- { method: 'POST', path: '/api/billing/checkout', profile: 'session', description: 'Create Stripe checkout session' },
25
- { method: 'POST', path: '/api/billing/portal', profile: 'session', description: 'Create Stripe portal session' },
26
- { method: 'POST', path: '/api/billing/webhook', profile: 'none', description: 'Stripe webhook endpoint' },
27
- { method: 'GET', path: '/api/builder/auto-mode/feedback', profile: 'session', description: 'Get auto-mode timeline feedback' },
28
- { method: 'POST', path: '/api/builder/auto-mode', profile: 'session', description: 'Toggle builder auto-mode' },
29
- { method: 'GET', path: '/api/builder/auto-mode/status', profile: 'session', description: 'Get builder auto-mode scheduler status' },
30
- { method: 'POST', path: '/api/builder/chat', profile: 'session', description: 'Chat with deployed builder agent' },
31
- { method: 'GET', path: '/api/builder/config', profile: 'session', description: 'Get builder config' },
32
- { method: 'POST', path: '/api/builder/config', profile: 'session', description: 'Create builder config' },
33
- { method: 'PUT', path: '/api/builder/config', profile: 'session', description: 'Update builder config' },
34
- { method: 'DELETE', path: '/api/builder/deploy', profile: 'session', description: 'Stop deployed builder agent' },
35
- { method: 'POST', path: '/api/builder/deploy', profile: 'session', description: 'Deploy builder agent' },
36
- { method: 'PUT', path: '/api/builder/deploy', profile: 'session', description: 'Sync deployed builder agent settings' },
37
- { method: 'POST', path: '/api/builder/soul/generate', profile: 'session', description: 'Generate SOUL.md draft' },
38
25
  { method: 'GET', path: '/api/claim/[token]', profile: 'none', description: 'Read claim token status' },
39
26
  { method: 'POST', path: '/api/claim/verify', profile: 'none', description: 'Verify claim token ownership' },
40
27
  { method: 'GET', path: '/api/crafting/recipes', profile: 'none', description: 'Get crafting recipes' },
@@ -68,7 +55,6 @@ export const NON_ADMIN_ENDPOINTS = [
68
55
  { method: 'POST', path: '/api/tournaments/join', profile: 'agent', description: 'Join active tournament' },
69
56
  { method: 'GET', path: '/api/tournaments', profile: 'none', description: 'Get current/recent tournaments' },
70
57
  { method: 'POST', path: '/api/tournaments', profile: 'none', description: 'Create tournament (operational)' },
71
- { method: 'GET', path: '/api/user/profile', profile: 'session', description: 'Get authenticated user profile' },
72
58
  { method: 'GET', path: '/api/world/events/recent', profile: 'none', description: 'Get recent world events' },
73
59
  { method: 'GET', path: '/api/world/events', profile: 'none', description: 'Get active world events' },
74
60
  { method: 'GET', path: '/api/world/leaderboard', profile: 'none', description: 'Get compact leaderboard' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawcity",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "CLI tool for installing AI agent skills - part of the ClawCity ecosystem",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,2 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function registerBillingCommands(program: Command): void;
@@ -1,50 +0,0 @@
1
- import { api, handleError } from '../lib/api.js';
2
- export function registerBillingCommands(program) {
3
- const billing = program
4
- .command('billing')
5
- .description('Session-authenticated billing actions');
6
- billing
7
- .command('checkout')
8
- .description('Create checkout session for starter|pro')
9
- .requiredOption('--tier <tier>', 'starter | pro')
10
- .action(async (opts) => {
11
- const tier = opts.tier.toLowerCase();
12
- if (tier !== 'starter' && tier !== 'pro') {
13
- console.error('Error: --tier must be "starter" or "pro"');
14
- process.exit(1);
15
- }
16
- const res = await api('/api/billing/checkout', {
17
- method: 'POST',
18
- profile: 'session',
19
- body: { tier },
20
- });
21
- if (!res.ok)
22
- handleError(res);
23
- const url = res.data.url;
24
- if (url) {
25
- console.log(`Checkout URL: ${url}`);
26
- }
27
- else {
28
- console.log(JSON.stringify(res.data, null, 2));
29
- }
30
- });
31
- billing
32
- .command('portal')
33
- .description('Create billing portal session URL')
34
- .action(async () => {
35
- const res = await api('/api/billing/portal', {
36
- method: 'POST',
37
- profile: 'session',
38
- body: {},
39
- });
40
- if (!res.ok)
41
- handleError(res);
42
- const url = res.data.url;
43
- if (url) {
44
- console.log(`Portal URL: ${url}`);
45
- }
46
- else {
47
- console.log(JSON.stringify(res.data, null, 2));
48
- }
49
- });
50
- }
@@ -1,2 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function registerBuilderCommands(program: Command): void;
@@ -1,210 +0,0 @@
1
- import { api, handleError } from '../lib/api.js';
2
- function parseJsonOrExit(raw) {
3
- try {
4
- const parsed = JSON.parse(raw);
5
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
6
- console.error('Error: --json must decode to a JSON object');
7
- process.exit(1);
8
- }
9
- return parsed;
10
- }
11
- catch (error) {
12
- console.error(`Error: Invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
13
- process.exit(1);
14
- }
15
- }
16
- function parseBooleanOrExit(value) {
17
- const normalized = value.toLowerCase();
18
- if (['1', 'true', 'on', 'yes', 'y'].includes(normalized))
19
- return true;
20
- if (['0', 'false', 'off', 'no', 'n'].includes(normalized))
21
- return false;
22
- console.error('Error: --enabled must be true|false|on|off|1|0');
23
- process.exit(1);
24
- }
25
- export function registerBuilderCommands(program) {
26
- const builder = program
27
- .command('builder')
28
- .description('Session-authenticated builder/deploy APIs');
29
- const config = builder
30
- .command('config')
31
- .description('Builder config operations');
32
- config
33
- .command('get')
34
- .description('Get your latest builder config')
35
- .action(async () => {
36
- const res = await api('/api/builder/config', { profile: 'session' });
37
- if (!res.ok)
38
- handleError(res);
39
- console.log(JSON.stringify(res.data, null, 2));
40
- });
41
- config
42
- .command('create')
43
- .description('Create builder config')
44
- .requiredOption('--json <json>', 'JSON object body')
45
- .action(async (opts) => {
46
- const res = await api('/api/builder/config', {
47
- method: 'POST',
48
- profile: 'session',
49
- body: parseJsonOrExit(opts.json),
50
- });
51
- if (!res.ok)
52
- handleError(res);
53
- console.log(JSON.stringify(res.data, null, 2));
54
- });
55
- config
56
- .command('update')
57
- .description('Update builder config')
58
- .requiredOption('--json <json>', 'JSON object body (must include id)')
59
- .action(async (opts) => {
60
- const res = await api('/api/builder/config', {
61
- method: 'PUT',
62
- profile: 'session',
63
- body: parseJsonOrExit(opts.json),
64
- });
65
- if (!res.ok)
66
- handleError(res);
67
- console.log(JSON.stringify(res.data, null, 2));
68
- });
69
- const deploy = builder
70
- .command('deploy')
71
- .description('Deploy/start/stop/sync your builder agent');
72
- deploy
73
- .command('start')
74
- .description('Start deploy for config id')
75
- .requiredOption('--config-id <id>', 'Builder config id')
76
- .action(async (opts) => {
77
- const res = await api('/api/builder/deploy', {
78
- method: 'POST',
79
- profile: 'session',
80
- body: { config_id: opts.configId },
81
- });
82
- if (!res.ok)
83
- handleError(res);
84
- console.log(JSON.stringify(res.data, null, 2));
85
- });
86
- deploy
87
- .command('stop')
88
- .description('Stop deployed agent for config id')
89
- .requiredOption('--config-id <id>', 'Builder config id')
90
- .action(async (opts) => {
91
- const res = await api('/api/builder/deploy', {
92
- method: 'DELETE',
93
- profile: 'session',
94
- body: { config_id: opts.configId },
95
- });
96
- if (!res.ok)
97
- handleError(res);
98
- console.log(JSON.stringify(res.data, null, 2));
99
- });
100
- deploy
101
- .command('sync')
102
- .description('Sync deployed agent settings via PUT /api/builder/deploy')
103
- .requiredOption('--json <json>', 'JSON object body')
104
- .action(async (opts) => {
105
- const res = await api('/api/builder/deploy', {
106
- method: 'PUT',
107
- profile: 'session',
108
- body: parseJsonOrExit(opts.json),
109
- });
110
- if (!res.ok)
111
- handleError(res);
112
- console.log(JSON.stringify(res.data, null, 2));
113
- });
114
- builder
115
- .command('chat <message>')
116
- .description('Send chat message to active deployed agent')
117
- .action(async (message) => {
118
- const res = await api('/api/builder/chat', {
119
- method: 'POST',
120
- profile: 'session',
121
- body: { message },
122
- });
123
- if (!res.ok)
124
- handleError(res);
125
- const response = res.data.response;
126
- if (typeof response === 'string') {
127
- console.log(response);
128
- }
129
- else {
130
- console.log(JSON.stringify(res.data, null, 2));
131
- }
132
- });
133
- const automode = builder
134
- .command('automode')
135
- .description('Builder auto-mode controls and feedback');
136
- automode
137
- .command('set')
138
- .description('Set auto-mode on/off for config id')
139
- .requiredOption('--config-id <id>', 'Builder config id')
140
- .requiredOption('--enabled <value>', 'true|false|on|off|1|0')
141
- .action(async (opts) => {
142
- const res = await api('/api/builder/auto-mode', {
143
- method: 'POST',
144
- profile: 'session',
145
- body: {
146
- config_id: opts.configId,
147
- enabled: parseBooleanOrExit(opts.enabled),
148
- },
149
- });
150
- if (!res.ok)
151
- handleError(res);
152
- console.log(JSON.stringify(res.data, null, 2));
153
- });
154
- automode
155
- .command('feedback')
156
- .description('Get recent auto-mode feedback entries')
157
- .requiredOption('--config-id <id>', 'Builder config id')
158
- .option('-l, --limit <n>', 'Max entries', '50')
159
- .action(async (opts) => {
160
- const res = await api('/api/builder/auto-mode/feedback', {
161
- profile: 'session',
162
- query: {
163
- config_id: opts.configId,
164
- limit: parseInt(opts.limit, 10) || 50,
165
- },
166
- });
167
- if (!res.ok)
168
- handleError(res);
169
- console.log(JSON.stringify(res.data, null, 2));
170
- });
171
- automode
172
- .command('status')
173
- .description('Get scheduler/effective status for config id')
174
- .requiredOption('--config-id <id>', 'Builder config id')
175
- .action(async (opts) => {
176
- const res = await api('/api/builder/auto-mode/status', {
177
- profile: 'session',
178
- query: { config_id: opts.configId },
179
- });
180
- if (!res.ok)
181
- handleError(res);
182
- console.log(JSON.stringify(res.data, null, 2));
183
- });
184
- const soul = builder
185
- .command('soul')
186
- .description('SOUL.md helper APIs');
187
- soul
188
- .command('generate')
189
- .description('Generate SOUL.md markdown')
190
- .requiredOption('--agent-name <name>', 'Agent name')
191
- .option('--operator-notes <notes>', 'Optional operator notes')
192
- .action(async (opts) => {
193
- const body = { agent_name: opts.agentName };
194
- if (opts.operatorNotes !== undefined)
195
- body.operator_notes = opts.operatorNotes;
196
- const res = await api('/api/builder/soul/generate', {
197
- method: 'POST',
198
- profile: 'session',
199
- body,
200
- });
201
- if (!res.ok)
202
- handleError(res);
203
- if (typeof res.data.soul_md === 'string') {
204
- console.log(res.data.soul_md);
205
- }
206
- else {
207
- console.log(JSON.stringify(res.data, null, 2));
208
- }
209
- });
210
- }
@@ -1,2 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function registerUserCommands(program: Command): void;
@@ -1,15 +0,0 @@
1
- import { api, handleError } from '../lib/api.js';
2
- export function registerUserCommands(program) {
3
- const user = program
4
- .command('user')
5
- .description('Session-authenticated user APIs');
6
- user
7
- .command('profile')
8
- .description('Get authenticated user profile/config/agent summary')
9
- .action(async () => {
10
- const res = await api('/api/user/profile', { profile: 'session' });
11
- if (!res.ok)
12
- handleError(res);
13
- console.log(JSON.stringify(res.data, null, 2));
14
- });
15
- }