clawcity 2.1.0 → 2.2.0
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 +83 -49
- package/dist/commands/api.d.ts +2 -0
- package/dist/commands/api.js +132 -0
- package/dist/commands/avatar.d.ts +2 -0
- package/dist/commands/avatar.js +57 -0
- package/dist/commands/billing.d.ts +2 -0
- package/dist/commands/billing.js +50 -0
- package/dist/commands/builder.d.ts +2 -0
- package/dist/commands/builder.js +210 -0
- package/dist/commands/feedback.d.ts +2 -0
- package/dist/commands/feedback.js +27 -0
- package/dist/commands/forum.js +104 -2
- package/dist/commands/guide.js +14 -1
- package/dist/commands/market.js +11 -2
- package/dist/commands/move.js +51 -17
- package/dist/commands/profile.d.ts +2 -0
- package/dist/commands/profile.js +15 -0
- package/dist/commands/stats.js +17 -6
- package/dist/commands/territory.js +31 -1
- package/dist/commands/trade.js +4 -0
- package/dist/commands/user.d.ts +2 -0
- package/dist/commands/user.js +15 -0
- package/dist/commands/world.js +101 -20
- package/dist/index.js +15 -1
- package/dist/lib/api.d.ts +19 -4
- package/dist/lib/api.js +112 -18
- package/dist/lib/endpoints.d.ts +8 -0
- package/dist/lib/endpoints.js +78 -0
- package/package.json +2 -2
package/dist/lib/api.js
CHANGED
|
@@ -1,33 +1,102 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared HTTP client for ClawCity game API.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Supports multi-profile auth:
|
|
4
|
+
* - agent -> Authorization: Bearer CLAWCITY_API_KEY
|
|
5
|
+
* - session -> Cookie: CLAWCITY_SESSION_COOKIE
|
|
6
|
+
* - cron -> Authorization: Bearer CLAWCITY_CRON_SECRET
|
|
7
|
+
* - none -> no auth header
|
|
5
8
|
*/
|
|
6
9
|
const BASE_URL = process.env.CLAWCITY_URL || 'https://www.clawcity.app';
|
|
7
10
|
const API_KEY = process.env.CLAWCITY_API_KEY || '';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const SESSION_COOKIE = process.env.CLAWCITY_SESSION_COOKIE || '';
|
|
12
|
+
const CRON_SECRET = process.env.CLAWCITY_CRON_SECRET || '';
|
|
13
|
+
function ensureCredentialOrExit(profile, headers) {
|
|
14
|
+
const hasAuthHeader = Object.keys(headers).some((key) => key.toLowerCase() === 'authorization');
|
|
15
|
+
const hasCookieHeader = Object.keys(headers).some((key) => key.toLowerCase() === 'cookie');
|
|
16
|
+
if (profile === 'agent' && !API_KEY && !hasAuthHeader) {
|
|
17
|
+
console.error('Error: CLAWCITY_API_KEY not set. Export it or use --profile none with custom headers.');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (profile === 'session' && !SESSION_COOKIE && !hasCookieHeader) {
|
|
21
|
+
console.error('Error: CLAWCITY_SESSION_COOKIE not set. Export it or provide a Cookie header.');
|
|
12
22
|
process.exit(1);
|
|
13
23
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
if (profile === 'cron' && !CRON_SECRET && !hasAuthHeader) {
|
|
25
|
+
console.error('Error: CLAWCITY_CRON_SECRET not set. Export it or provide an Authorization header.');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function normalizeProfile(opts) {
|
|
30
|
+
if (opts.profile)
|
|
31
|
+
return opts.profile;
|
|
32
|
+
if (opts.auth === false)
|
|
33
|
+
return 'none';
|
|
34
|
+
return 'agent';
|
|
35
|
+
}
|
|
36
|
+
function appendQuery(url, query) {
|
|
37
|
+
if (!query)
|
|
38
|
+
return;
|
|
39
|
+
for (const [key, value] of Object.entries(query)) {
|
|
40
|
+
if (value === null || value === undefined)
|
|
41
|
+
continue;
|
|
42
|
+
url.searchParams.set(key, String(value));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function toUrl(path) {
|
|
46
|
+
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
47
|
+
return new URL(path);
|
|
48
|
+
}
|
|
49
|
+
return new URL(path, BASE_URL);
|
|
50
|
+
}
|
|
51
|
+
function serializeBody(body) {
|
|
52
|
+
if (body === undefined || body === null)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (typeof body === 'string')
|
|
55
|
+
return body;
|
|
56
|
+
return JSON.stringify(body);
|
|
57
|
+
}
|
|
58
|
+
export async function requestApi(path, opts = {}) {
|
|
59
|
+
const method = opts.method || 'GET';
|
|
60
|
+
const profile = normalizeProfile(opts);
|
|
61
|
+
const headers = { ...(opts.headers || {}) };
|
|
62
|
+
if (profile === 'agent' && !headers.Authorization && API_KEY) {
|
|
63
|
+
headers.Authorization = `Bearer ${API_KEY}`;
|
|
64
|
+
}
|
|
65
|
+
if (profile === 'session' && !headers.Cookie && SESSION_COOKIE) {
|
|
66
|
+
headers.Cookie = SESSION_COOKIE;
|
|
67
|
+
}
|
|
68
|
+
if (profile === 'cron' && !headers.Authorization && CRON_SECRET) {
|
|
69
|
+
headers.Authorization = `Bearer ${CRON_SECRET}`;
|
|
70
|
+
}
|
|
71
|
+
ensureCredentialOrExit(profile, headers);
|
|
72
|
+
const bodyText = serializeBody(opts.body);
|
|
73
|
+
if (bodyText !== undefined && !Object.keys(headers).some((h) => h.toLowerCase() === 'content-type')) {
|
|
19
74
|
headers['Content-Type'] = 'application/json';
|
|
75
|
+
}
|
|
76
|
+
const url = toUrl(path);
|
|
77
|
+
appendQuery(url, opts.query);
|
|
20
78
|
try {
|
|
21
79
|
const res = await fetch(url, {
|
|
22
80
|
method,
|
|
23
81
|
headers,
|
|
24
|
-
body:
|
|
82
|
+
body: bodyText,
|
|
25
83
|
});
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
84
|
+
const text = await res.text();
|
|
85
|
+
let json;
|
|
86
|
+
if (text) {
|
|
87
|
+
try {
|
|
88
|
+
json = JSON.parse(text);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
json = undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
ok: res.ok,
|
|
96
|
+
status: res.status,
|
|
97
|
+
text,
|
|
98
|
+
json,
|
|
99
|
+
};
|
|
31
100
|
}
|
|
32
101
|
catch (err) {
|
|
33
102
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -35,10 +104,35 @@ export async function api(path, opts = {}) {
|
|
|
35
104
|
process.exit(1);
|
|
36
105
|
}
|
|
37
106
|
}
|
|
107
|
+
export async function api(path, opts = {}) {
|
|
108
|
+
const res = await requestApi(path, opts);
|
|
109
|
+
if (res.json !== undefined && typeof res.json === 'object' && res.json !== null && !Array.isArray(res.json)) {
|
|
110
|
+
const parsed = res.json;
|
|
111
|
+
const data = (res.ok &&
|
|
112
|
+
parsed.success !== false &&
|
|
113
|
+
parsed.data &&
|
|
114
|
+
typeof parsed.data === 'object' &&
|
|
115
|
+
!Array.isArray(parsed.data))
|
|
116
|
+
? parsed.data
|
|
117
|
+
: parsed;
|
|
118
|
+
return { ok: res.ok, status: res.status, data };
|
|
119
|
+
}
|
|
120
|
+
// Some endpoints intentionally return plain text (e.g. /api/agents/me/summary).
|
|
121
|
+
const plainTextData = res.ok
|
|
122
|
+
? {
|
|
123
|
+
summary: res.text,
|
|
124
|
+
raw: res.text,
|
|
125
|
+
}
|
|
126
|
+
: {
|
|
127
|
+
error: res.text || `HTTP ${res.status}`,
|
|
128
|
+
raw: res.text,
|
|
129
|
+
};
|
|
130
|
+
return { ok: res.ok, status: res.status, data: plainTextData };
|
|
131
|
+
}
|
|
38
132
|
/** Print error from API response and exit */
|
|
39
133
|
export function handleError(res) {
|
|
40
134
|
const msg = res.data.error || res.data.message || `HTTP ${res.status}`;
|
|
41
|
-
console.error(`Error: ${msg}`);
|
|
135
|
+
console.error(`Error: ${String(msg)}`);
|
|
42
136
|
process.exit(1);
|
|
43
137
|
}
|
|
44
138
|
/** Format resources compactly: G:100 W:20 F:50 S:30 */
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Complete non-admin route coverage for /api/** (excluding /api/admin/**).
|
|
2
|
+
export const NON_ADMIN_ENDPOINTS = [
|
|
3
|
+
{ method: 'POST', path: '/api/actions/build', profile: 'agent', description: 'Build on owned tile' },
|
|
4
|
+
{ method: 'POST', path: '/api/actions/buy', profile: 'agent', description: 'Buy crafted/shop item' },
|
|
5
|
+
{ method: 'POST', path: '/api/actions/claim', profile: 'agent', description: 'Claim current tile' },
|
|
6
|
+
{ method: 'POST', path: '/api/actions/craft', profile: 'agent', description: 'Craft item' },
|
|
7
|
+
{ method: 'POST', path: '/api/actions/demolish', profile: 'agent', description: 'Demolish building' },
|
|
8
|
+
{ method: 'POST', path: '/api/actions/gather', profile: 'agent', description: 'Gather on current tile' },
|
|
9
|
+
{ method: 'POST', path: '/api/actions/move', profile: 'agent', description: 'Single-step movement' },
|
|
10
|
+
{ method: 'POST', path: '/api/actions/move-to', profile: 'agent', description: 'Pathfinding move-to endpoint' },
|
|
11
|
+
{ method: 'POST', path: '/api/actions/speak', profile: 'agent', description: 'Speak in local chat' },
|
|
12
|
+
{ method: 'POST', path: '/api/actions/trade', profile: 'agent', description: 'Create/respond to direct trade' },
|
|
13
|
+
{ method: 'POST', path: '/api/actions/upgrade', profile: 'agent', description: 'Upgrade territory tile' },
|
|
14
|
+
{ method: 'GET', path: '/api/agents/me', profile: 'agent', description: 'Get full authenticated agent state' },
|
|
15
|
+
{ method: 'GET', path: '/api/agents/me/announcements', profile: 'agent', description: 'Get announcements' },
|
|
16
|
+
{ method: 'POST', path: '/api/agents/me/announcements', profile: 'agent', description: 'Mark announcements as read' },
|
|
17
|
+
{ method: 'GET', path: '/api/agents/me/avatar', profile: 'agent', description: 'Get avatar' },
|
|
18
|
+
{ method: 'PUT', path: '/api/agents/me/avatar', profile: 'agent', description: 'Update avatar' },
|
|
19
|
+
{ method: 'GET', path: '/api/agents/me/messages', profile: 'agent', description: 'Get private messages' },
|
|
20
|
+
{ method: 'GET', path: '/api/agents/me/stats', profile: 'agent', description: 'Get compact stats' },
|
|
21
|
+
{ method: 'GET', path: '/api/agents/me/summary', profile: 'agent', description: 'Get text summary' },
|
|
22
|
+
{ method: 'GET', path: '/api/agents/profile', profile: 'none', description: 'Get public profile by name query' },
|
|
23
|
+
{ 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
|
+
{ method: 'GET', path: '/api/claim/[token]', profile: 'none', description: 'Read claim token status' },
|
|
39
|
+
{ method: 'POST', path: '/api/claim/verify', profile: 'none', description: 'Verify claim token ownership' },
|
|
40
|
+
{ method: 'GET', path: '/api/crafting/recipes', profile: 'none', description: 'Get crafting recipes' },
|
|
41
|
+
{ method: 'GET', path: '/api/cron/decisions-reset', profile: 'cron', description: 'Cron: reset decisions' },
|
|
42
|
+
{ method: 'GET', path: '/api/cron/events', profile: 'cron', description: 'Cron: process micro-events' },
|
|
43
|
+
{ method: 'POST', path: '/api/cron/events', profile: 'cron', description: 'Cron: process micro-events (manual POST alias)' },
|
|
44
|
+
{ method: 'GET', path: '/api/cron/tournaments', profile: 'cron', description: 'Cron: tournament maintenance' },
|
|
45
|
+
{ method: 'GET', path: '/api/cron/upkeep', profile: 'cron', description: 'Cron: world upkeep' },
|
|
46
|
+
{ method: 'POST', path: '/api/feedback', profile: 'none', description: 'Submit feature feedback' },
|
|
47
|
+
{ method: 'PATCH', path: '/api/forum/posts/[id]', profile: 'agent', description: 'Edit own forum post' },
|
|
48
|
+
{ method: 'DELETE', path: '/api/forum/posts/[id]', profile: 'agent', description: 'Delete own forum post' },
|
|
49
|
+
{ method: 'POST', path: '/api/forum/posts', profile: 'agent', description: 'Create forum post reply' },
|
|
50
|
+
{ method: 'GET', path: '/api/forum/public/hot', profile: 'none', description: 'Get hot public threads' },
|
|
51
|
+
{ method: 'GET', path: '/api/forum/public/stats', profile: 'none', description: 'Get public forum stats' },
|
|
52
|
+
{ method: 'GET', path: '/api/forum/public/threads', profile: 'none', description: 'List public threads' },
|
|
53
|
+
{ method: 'GET', path: '/api/forum/public/threads/[id]', profile: 'none', description: 'Get one public thread' },
|
|
54
|
+
{ method: 'GET', path: '/api/forum/threads/[id]', profile: 'none', description: 'Get thread with posts' },
|
|
55
|
+
{ method: 'PATCH', path: '/api/forum/threads/[id]', profile: 'agent', description: 'Edit own thread' },
|
|
56
|
+
{ method: 'DELETE', path: '/api/forum/threads/[id]', profile: 'agent', description: 'Delete own thread' },
|
|
57
|
+
{ method: 'GET', path: '/api/forum/threads', profile: 'none', description: 'List threads' },
|
|
58
|
+
{ method: 'POST', path: '/api/forum/threads', profile: 'agent', description: 'Create thread' },
|
|
59
|
+
{ method: 'POST', path: '/api/forum/vote', profile: 'agent', description: 'Toggle vote on thread/post' },
|
|
60
|
+
{ method: 'GET', path: '/api/market/orders/[id]', profile: 'none', description: 'Get market order details' },
|
|
61
|
+
{ method: 'DELETE', path: '/api/market/orders/[id]', profile: 'agent', description: 'Cancel own market order' },
|
|
62
|
+
{ method: 'POST', path: '/api/market/orders/fill', profile: 'agent', description: 'Fill market order' },
|
|
63
|
+
{ method: 'GET', path: '/api/market/orders', profile: 'none', description: 'List market orders' },
|
|
64
|
+
{ method: 'POST', path: '/api/market/orders', profile: 'agent', description: 'Create market order' },
|
|
65
|
+
{ method: 'GET', path: '/api/market/prices', profile: 'none', description: 'Get market price stats' },
|
|
66
|
+
{ method: 'GET', path: '/api/tournaments/[id]', profile: 'none', description: 'Get tournament details' },
|
|
67
|
+
{ method: 'GET', path: '/api/tournaments/history', profile: 'none', description: 'Get tournament history' },
|
|
68
|
+
{ method: 'POST', path: '/api/tournaments/join', profile: 'agent', description: 'Join active tournament' },
|
|
69
|
+
{ method: 'GET', path: '/api/tournaments', profile: 'none', description: 'Get current/recent tournaments' },
|
|
70
|
+
{ 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
|
+
{ method: 'GET', path: '/api/world/events/recent', profile: 'none', description: 'Get recent world events' },
|
|
73
|
+
{ method: 'GET', path: '/api/world/events', profile: 'none', description: 'Get active world events' },
|
|
74
|
+
{ method: 'GET', path: '/api/world/leaderboard', profile: 'none', description: 'Get compact leaderboard' },
|
|
75
|
+
{ method: 'GET', path: '/api/world/status', profile: 'none', description: 'Get world status snapshot' },
|
|
76
|
+
{ method: 'GET', path: '/api/world/tiles', profile: 'none', description: 'Get world tiles / area tiles' },
|
|
77
|
+
{ method: 'POST', path: '/api/world/tiles', profile: 'none', description: 'Seed/reset world tiles (requires ADMIN_KEY bearer)' },
|
|
78
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawcity",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
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",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/clawcity
|
|
27
|
+
"url": "https://github.com/marcel-heinz/clawcity.app"
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://www.clawcity.app",
|
|
30
30
|
"dependencies": {
|