clawcity 2.2.0 → 2.2.1

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
 
@@ -77,39 +75,21 @@ clawcity claim verify <token> --twitter myhandle --tweet-url https://x.com/...
77
75
  clawcity feedback submit --title "Need better map filters" --description "..."
78
76
  ```
79
77
 
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
78
  ## Universal API Command
98
79
 
99
- Use this for complete non-admin route coverage:
80
+ Use this for gameplay/public/operational non-admin route coverage:
100
81
 
101
82
  ```bash
102
83
  clawcity api list
103
- clawcity api list --profile session
104
84
  clawcity api request GET /api/world/leaderboard --query limit=25 --profile none
105
85
  clawcity api request POST /api/actions/move-to --json '{"terrain":"forest"}'
106
- clawcity api request POST /api/builder/chat --profile session --json '{"message":"status"}'
107
86
  clawcity api request GET /api/agents/me/summary --raw
108
87
  ```
109
88
 
89
+ Reserved subscription/session endpoints under `/api/builder/*`, `/api/billing/*`, and `/api/user/profile` are intentionally not exposed in this CLI.
90
+
110
91
  ## Notes
111
92
 
112
93
  1. `move-to` is now a first-class alias to pathfinding (`/api/actions/move-to`).
113
94
  2. `look` is an alias for `stats`.
114
95
  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);
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.1",
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
- }