cubelife 0.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/LICENSE +21 -0
- package/README.md +81 -0
- package/SPRITE-LICENSE +14 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +39 -0
- package/dist/commands/agents.d.ts +2 -0
- package/dist/commands/agents.js +303 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +233 -0
- package/dist/commands/billing.d.ts +2 -0
- package/dist/commands/billing.js +362 -0
- package/dist/commands/creature.d.ts +2 -0
- package/dist/commands/creature.js +166 -0
- package/dist/commands/default.d.ts +2 -0
- package/dist/commands/default.js +87 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +48 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +200 -0
- package/dist/commands/mcp.d.ts +2 -0
- package/dist/commands/mcp.js +9 -0
- package/dist/commands/projects.d.ts +2 -0
- package/dist/commands/projects.js +122 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +453 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +89 -0
- package/dist/commands/tutorial.d.ts +2 -0
- package/dist/commands/tutorial.js +9 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +262 -0
- package/dist/data/sprite-data.d.ts +32 -0
- package/dist/data/sprite-data.js +865 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/lib/api.d.ts +162 -0
- package/dist/lib/api.js +160 -0
- package/dist/lib/auth.d.ts +12 -0
- package/dist/lib/auth.js +113 -0
- package/dist/lib/browser.d.ts +1 -0
- package/dist/lib/browser.js +21 -0
- package/dist/lib/command-helpers.d.ts +26 -0
- package/dist/lib/command-helpers.js +60 -0
- package/dist/lib/compositor.d.ts +34 -0
- package/dist/lib/compositor.js +232 -0
- package/dist/lib/config.d.ts +39 -0
- package/dist/lib/config.js +89 -0
- package/dist/lib/constants.d.ts +12 -0
- package/dist/lib/constants.js +39 -0
- package/dist/lib/detect.d.ts +17 -0
- package/dist/lib/detect.js +99 -0
- package/dist/lib/doctor.d.ts +18 -0
- package/dist/lib/doctor.js +321 -0
- package/dist/lib/index.d.ts +11 -0
- package/dist/lib/index.js +6 -0
- package/dist/lib/integration.d.ts +66 -0
- package/dist/lib/integration.js +337 -0
- package/dist/lib/poll.d.ts +11 -0
- package/dist/lib/poll.js +31 -0
- package/dist/lib/resolve.d.ts +1 -0
- package/dist/lib/resolve.js +10 -0
- package/dist/lib/services/account-service.d.ts +17 -0
- package/dist/lib/services/account-service.js +30 -0
- package/dist/lib/services/agent-service.d.ts +17 -0
- package/dist/lib/services/agent-service.js +62 -0
- package/dist/lib/services/creature-service.d.ts +12 -0
- package/dist/lib/services/creature-service.js +35 -0
- package/dist/lib/services/project-service.d.ts +9 -0
- package/dist/lib/services/project-service.js +22 -0
- package/dist/lib/tutorial.d.ts +12 -0
- package/dist/lib/tutorial.js +358 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +116 -0
- package/dist/ui/banner.d.ts +3 -0
- package/dist/ui/banner.js +27 -0
- package/dist/ui/half-block.d.ts +6 -0
- package/dist/ui/half-block.js +45 -0
- package/dist/ui/helpers.d.ts +3 -0
- package/dist/ui/helpers.js +11 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.js +5 -0
- package/dist/ui/panel.d.ts +7 -0
- package/dist/ui/panel.js +21 -0
- package/dist/ui/preview.d.ts +7 -0
- package/dist/ui/preview.js +21 -0
- package/dist/ui/table.d.ts +8 -0
- package/dist/ui/table.js +20 -0
- package/dist/ui/theme.d.ts +24 -0
- package/dist/ui/theme.js +32 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { SPRITE_DATA, STATE_MANIFEST, CREATURE_MANIFEST, } from '../data/sprite-data.js';
|
|
2
|
+
const FRAME_SIZE = 48;
|
|
3
|
+
const SPRITE_HALF = FRAME_SIZE / 2;
|
|
4
|
+
const PIXELS = FRAME_SIZE * FRAME_SIZE;
|
|
5
|
+
const RGBA_BYTES = PIXELS * 4;
|
|
6
|
+
const CREATURE_BODY_SCALE = 0.6;
|
|
7
|
+
export function hexToColour(hex) {
|
|
8
|
+
const h = hex.replace('#', '');
|
|
9
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
10
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
11
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
12
|
+
return {
|
|
13
|
+
r: Number.isNaN(r) ? 128 : r,
|
|
14
|
+
g: Number.isNaN(g) ? 128 : g,
|
|
15
|
+
b: Number.isNaN(b) ? 128 : b,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function decodeFrames(asset) {
|
|
19
|
+
return asset.frames.map(b64 => new Uint8Array(Buffer.from(b64, 'base64').buffer));
|
|
20
|
+
}
|
|
21
|
+
export function applyTint(lumAlpha, colour) {
|
|
22
|
+
const rgba = new Uint8Array(lumAlpha.length * 2);
|
|
23
|
+
for (let i = 0; i < lumAlpha.length; i += 2) {
|
|
24
|
+
const lum = lumAlpha[i];
|
|
25
|
+
const alpha = lumAlpha[i + 1];
|
|
26
|
+
const j = i * 2;
|
|
27
|
+
rgba[j] = Math.round(lum * colour.r / 255);
|
|
28
|
+
rgba[j + 1] = Math.round(lum * colour.g / 255);
|
|
29
|
+
rgba[j + 2] = Math.round(lum * colour.b / 255);
|
|
30
|
+
rgba[j + 3] = alpha;
|
|
31
|
+
}
|
|
32
|
+
return rgba;
|
|
33
|
+
}
|
|
34
|
+
export function alphaBlend(dst, src) {
|
|
35
|
+
for (let i = 0; i < dst.length; i += 4) {
|
|
36
|
+
const srcA = src[i + 3] / 255;
|
|
37
|
+
if (srcA === 0)
|
|
38
|
+
continue;
|
|
39
|
+
const dstA = dst[i + 3] / 255;
|
|
40
|
+
const outA = srcA + dstA * (1 - srcA);
|
|
41
|
+
if (outA === 0)
|
|
42
|
+
continue;
|
|
43
|
+
dst[i] = Math.round((src[i] * srcA + dst[i] * dstA * (1 - srcA)) / outA);
|
|
44
|
+
dst[i + 1] = Math.round((src[i + 1] * srcA + dst[i + 1] * dstA * (1 - srcA)) / outA);
|
|
45
|
+
dst[i + 2] = Math.round((src[i + 2] * srcA + dst[i + 2] * dstA * (1 - srcA)) / outA);
|
|
46
|
+
dst[i + 3] = Math.round(outA * 255);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function resolveRgba(key, frameIndex, tint) {
|
|
50
|
+
const asset = SPRITE_DATA[key];
|
|
51
|
+
if (!asset)
|
|
52
|
+
return null;
|
|
53
|
+
const frames = decodeFrames(asset);
|
|
54
|
+
const idx = Math.min(frameIndex, frames.length - 1);
|
|
55
|
+
const frame = frames[idx];
|
|
56
|
+
if (asset.type === 'tintable' && tint) {
|
|
57
|
+
return applyTint(frame, tint);
|
|
58
|
+
}
|
|
59
|
+
if (asset.type === 'tintable') {
|
|
60
|
+
return applyTint(frame, { r: 200, g: 200, b: 200 });
|
|
61
|
+
}
|
|
62
|
+
return frame;
|
|
63
|
+
}
|
|
64
|
+
function scaleAndPlace(src, scale, anchorX, anchorY) {
|
|
65
|
+
if (scale >= 1)
|
|
66
|
+
return src;
|
|
67
|
+
const scaledSize = Math.round(FRAME_SIZE * scale);
|
|
68
|
+
const offsetX = Math.round(anchorX - scaledSize * (anchorX / FRAME_SIZE));
|
|
69
|
+
const offsetY = Math.round(anchorY - scaledSize * (anchorY / FRAME_SIZE));
|
|
70
|
+
const out = new Uint8Array(RGBA_BYTES);
|
|
71
|
+
for (let dy = 0; dy < scaledSize; dy++) {
|
|
72
|
+
const dstY = offsetY + dy;
|
|
73
|
+
if (dstY < 0 || dstY >= FRAME_SIZE)
|
|
74
|
+
continue;
|
|
75
|
+
const srcY = Math.floor(dy * FRAME_SIZE / scaledSize);
|
|
76
|
+
for (let dx = 0; dx < scaledSize; dx++) {
|
|
77
|
+
const dstX = offsetX + dx;
|
|
78
|
+
if (dstX < 0 || dstX >= FRAME_SIZE)
|
|
79
|
+
continue;
|
|
80
|
+
const srcX = Math.floor(dx * FRAME_SIZE / scaledSize);
|
|
81
|
+
const si = (srcY * FRAME_SIZE + srcX) * 4;
|
|
82
|
+
const di = (dstY * FRAME_SIZE + dstX) * 4;
|
|
83
|
+
out[di] = src[si];
|
|
84
|
+
out[di + 1] = src[si + 1];
|
|
85
|
+
out[di + 2] = src[si + 2];
|
|
86
|
+
out[di + 3] = src[si + 3];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
export function compositeCreature(creature, state, frameIndex) {
|
|
92
|
+
const canvas = new Uint8Array(RGBA_BYTES);
|
|
93
|
+
const colour = hexToColour(creature.color);
|
|
94
|
+
const stateConfig = STATE_MANIFEST[state];
|
|
95
|
+
if (stateConfig) {
|
|
96
|
+
if (stateConfig.background) {
|
|
97
|
+
const bgLayer = resolveRgba(stateConfig.background, 0);
|
|
98
|
+
if (bgLayer)
|
|
99
|
+
alphaBlend(canvas, bgLayer);
|
|
100
|
+
}
|
|
101
|
+
if (stateConfig.prop && stateConfig.propLayer === 'back') {
|
|
102
|
+
const propLayer = resolveRgba(stateConfig.prop, 0);
|
|
103
|
+
if (propLayer) {
|
|
104
|
+
const propScale = stateConfig.propScale ?? 1;
|
|
105
|
+
const anchorX = SPRITE_HALF + (stateConfig.propOffsetX ?? 0);
|
|
106
|
+
const anchorY = SPRITE_HALF + (stateConfig.propOffsetY ?? 0);
|
|
107
|
+
alphaBlend(canvas, scaleAndPlace(propLayer, propScale, anchorX, anchorY));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const creatureKey = `creatures/${creature.type}/idle`;
|
|
112
|
+
const creatureFrame = resolveRgba(creatureKey, frameIndex, colour);
|
|
113
|
+
if (creatureFrame) {
|
|
114
|
+
const scaled = scaleAndPlace(creatureFrame, CREATURE_BODY_SCALE, SPRITE_HALF, SPRITE_HALF);
|
|
115
|
+
alphaBlend(canvas, scaled);
|
|
116
|
+
}
|
|
117
|
+
if (stateConfig?.prop && stateConfig.propLayer === 'front') {
|
|
118
|
+
const propLayer = resolveRgba(stateConfig.prop, 0);
|
|
119
|
+
if (propLayer) {
|
|
120
|
+
const propScale = stateConfig.propScale ?? 1;
|
|
121
|
+
const anchorX = SPRITE_HALF + (stateConfig.propOffsetX ?? 0);
|
|
122
|
+
const anchorY = SPRITE_HALF + (stateConfig.propOffsetY ?? 0);
|
|
123
|
+
alphaBlend(canvas, scaleAndPlace(propLayer, propScale, anchorX, anchorY));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { rgba: canvas, width: FRAME_SIZE, height: FRAME_SIZE };
|
|
127
|
+
}
|
|
128
|
+
export function compositeHuman(traits, state, frameIndex) {
|
|
129
|
+
const canvas = new Uint8Array(RGBA_BYTES);
|
|
130
|
+
const stateConfig = STATE_MANIFEST[state] ?? STATE_MANIFEST['idle'];
|
|
131
|
+
const facing = stateConfig.facing ?? 'south';
|
|
132
|
+
const variant = facing === 'north' ? 'back' : 'standing';
|
|
133
|
+
if (stateConfig.background) {
|
|
134
|
+
const bgLayer = resolveRgba(stateConfig.background, 0);
|
|
135
|
+
if (bgLayer)
|
|
136
|
+
alphaBlend(canvas, bgLayer);
|
|
137
|
+
}
|
|
138
|
+
if (stateConfig.prop && stateConfig.propLayer === 'back') {
|
|
139
|
+
const propLayer = resolveRgba(stateConfig.prop, 0);
|
|
140
|
+
if (propLayer) {
|
|
141
|
+
const propScale = stateConfig.propScale ?? 1;
|
|
142
|
+
const anchorX = SPRITE_HALF + (stateConfig.propOffsetX ?? 0);
|
|
143
|
+
const anchorY = SPRITE_HALF + (stateConfig.propOffsetY ?? 0);
|
|
144
|
+
alphaBlend(canvas, scaleAndPlace(propLayer, propScale, anchorX, anchorY));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const skinColour = traits.skinTone ? hexToColour(traits.skinTone) : { r: 220, g: 180, b: 140 };
|
|
148
|
+
const bodyFrame = resolveRgba(stateConfig.bodySheet, frameIndex, skinColour);
|
|
149
|
+
if (bodyFrame)
|
|
150
|
+
alphaBlend(canvas, bodyFrame);
|
|
151
|
+
const outfit = traits.outfit ?? 'default_tee';
|
|
152
|
+
const outfitColour = traits.outfitColor ? hexToColour(traits.outfitColor) : { r: 100, g: 150, b: 200 };
|
|
153
|
+
const outfitKey = `outfits/${outfit}_${variant}`;
|
|
154
|
+
const outfitFrame = resolveRgba(outfitKey, 0, outfitColour);
|
|
155
|
+
if (outfitFrame)
|
|
156
|
+
alphaBlend(canvas, outfitFrame);
|
|
157
|
+
const hair = traits.hairStyle ?? 'short';
|
|
158
|
+
const hairColour = traits.hairColor ? hexToColour(traits.hairColor) : { r: 80, g: 50, b: 30 };
|
|
159
|
+
const hairKey = `hair/${hair}_${variant}`;
|
|
160
|
+
const hairFrame = resolveRgba(hairKey, 0, hairColour);
|
|
161
|
+
if (hairFrame)
|
|
162
|
+
alphaBlend(canvas, hairFrame);
|
|
163
|
+
if (traits.accessories) {
|
|
164
|
+
for (const acc of traits.accessories) {
|
|
165
|
+
if (acc === 'glasses' && facing === 'north')
|
|
166
|
+
continue;
|
|
167
|
+
const accKey = facing === 'north'
|
|
168
|
+
? `accessories/${acc}_back`
|
|
169
|
+
: `accessories/${acc}`;
|
|
170
|
+
const accFrame = resolveRgba(accKey, 0);
|
|
171
|
+
if (accFrame)
|
|
172
|
+
alphaBlend(canvas, accFrame);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (stateConfig.prop && stateConfig.propLayer === 'front') {
|
|
176
|
+
const propLayer = resolveRgba(stateConfig.prop, 0);
|
|
177
|
+
if (propLayer) {
|
|
178
|
+
const propScale = stateConfig.propScale ?? 1;
|
|
179
|
+
const anchorX = SPRITE_HALF + (stateConfig.propOffsetX ?? 0);
|
|
180
|
+
const anchorY = SPRITE_HALF + (stateConfig.propOffsetY ?? 0);
|
|
181
|
+
alphaBlend(canvas, scaleAndPlace(propLayer, propScale, anchorX, anchorY));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return { rgba: canvas, width: FRAME_SIZE, height: FRAME_SIZE };
|
|
185
|
+
}
|
|
186
|
+
export function getFrameCount(form, creatureType, state) {
|
|
187
|
+
if (form === 'creature' && creatureType) {
|
|
188
|
+
return CREATURE_MANIFEST[creatureType]?.idleFrameCount ?? 5;
|
|
189
|
+
}
|
|
190
|
+
const stateConfig = STATE_MANIFEST[state ?? 'idle'];
|
|
191
|
+
return stateConfig?.frameCount ?? 4;
|
|
192
|
+
}
|
|
193
|
+
export function getFps(form, creatureType, state) {
|
|
194
|
+
if (form === 'creature' && creatureType) {
|
|
195
|
+
return CREATURE_MANIFEST[creatureType]?.fps ?? 2;
|
|
196
|
+
}
|
|
197
|
+
const stateConfig = STATE_MANIFEST[state ?? 'idle'];
|
|
198
|
+
return stateConfig?.fps ?? 2;
|
|
199
|
+
}
|
|
200
|
+
function shuffledIndices(count) {
|
|
201
|
+
const arr = new Uint16Array(count);
|
|
202
|
+
for (let i = 0; i < count; i++)
|
|
203
|
+
arr[i] = i;
|
|
204
|
+
for (let i = count - 1; i > 0; i--) {
|
|
205
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
206
|
+
const tmp = arr[i];
|
|
207
|
+
arr[i] = arr[j];
|
|
208
|
+
arr[j] = tmp;
|
|
209
|
+
}
|
|
210
|
+
return arr;
|
|
211
|
+
}
|
|
212
|
+
export function createDissolve() {
|
|
213
|
+
return shuffledIndices(PIXELS);
|
|
214
|
+
}
|
|
215
|
+
export function applyDissolve(from, to, order, t) {
|
|
216
|
+
const revealed = Math.round(Math.max(0, Math.min(1, t)) * order.length);
|
|
217
|
+
const out = new Uint8Array(from.length);
|
|
218
|
+
const revealedSet = new Uint8Array(order.length);
|
|
219
|
+
for (let i = 0; i < revealed; i++) {
|
|
220
|
+
revealedSet[order[i]] = 1;
|
|
221
|
+
}
|
|
222
|
+
for (let px = 0; px < order.length; px++) {
|
|
223
|
+
const off = px * 4;
|
|
224
|
+
const src = revealedSet[px] ? to : from;
|
|
225
|
+
out[off] = src[off];
|
|
226
|
+
out[off + 1] = src[off + 1];
|
|
227
|
+
out[off + 2] = src[off + 2];
|
|
228
|
+
out[off + 3] = src[off + 3];
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
export { STATE_MANIFEST, CREATURE_MANIFEST };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface AuthConfig {
|
|
2
|
+
email: string;
|
|
3
|
+
token: string;
|
|
4
|
+
refreshToken: string;
|
|
5
|
+
expiresAt: number;
|
|
6
|
+
}
|
|
7
|
+
export interface GlobalConfig {
|
|
8
|
+
auth?: AuthConfig;
|
|
9
|
+
defaults?: {
|
|
10
|
+
projectId?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface AgentEntry {
|
|
14
|
+
key: string;
|
|
15
|
+
name: string;
|
|
16
|
+
project: string;
|
|
17
|
+
}
|
|
18
|
+
export interface AgentsStore {
|
|
19
|
+
agents: Record<string, AgentEntry>;
|
|
20
|
+
}
|
|
21
|
+
export interface ProjectConfig {
|
|
22
|
+
projectId: string;
|
|
23
|
+
agentId?: string;
|
|
24
|
+
integration?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function globalDir(): string;
|
|
27
|
+
export declare function globalConfigPath(): string;
|
|
28
|
+
export declare function agentsPath(): string;
|
|
29
|
+
export declare function projectConfigPath(cwd?: string): string;
|
|
30
|
+
export declare function projectDirPath(cwd?: string): string;
|
|
31
|
+
export declare function readGlobalConfig(): Promise<GlobalConfig>;
|
|
32
|
+
export declare function writeGlobalConfig(config: GlobalConfig): Promise<void>;
|
|
33
|
+
export declare function readAgents(): Promise<AgentsStore>;
|
|
34
|
+
export declare function writeAgents(store: AgentsStore): Promise<void>;
|
|
35
|
+
export declare function readProjectConfig(cwd?: string): Promise<ProjectConfig | null>;
|
|
36
|
+
export declare function writeProjectConfig(config: ProjectConfig, cwd?: string): Promise<void>;
|
|
37
|
+
export declare function resolveAgentKey(agentId: string, agents: AgentsStore): string | null;
|
|
38
|
+
export declare function hasAuth(config: GlobalConfig): boolean;
|
|
39
|
+
export declare function isTokenExpired(config: GlobalConfig): boolean;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir, chmod } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
const GLOBAL_DIR = join(homedir(), '.cubelife');
|
|
6
|
+
const GLOBAL_CONFIG = join(GLOBAL_DIR, 'config.json');
|
|
7
|
+
const AGENTS_FILE = join(GLOBAL_DIR, 'agents.json');
|
|
8
|
+
const PROJECT_DIR = '.cubelife';
|
|
9
|
+
const PROJECT_CONFIG = join(PROJECT_DIR, 'config.json');
|
|
10
|
+
export function globalDir() {
|
|
11
|
+
return GLOBAL_DIR;
|
|
12
|
+
}
|
|
13
|
+
export function globalConfigPath() {
|
|
14
|
+
return GLOBAL_CONFIG;
|
|
15
|
+
}
|
|
16
|
+
export function agentsPath() {
|
|
17
|
+
return AGENTS_FILE;
|
|
18
|
+
}
|
|
19
|
+
export function projectConfigPath(cwd) {
|
|
20
|
+
const base = cwd ?? process.cwd();
|
|
21
|
+
return resolve(base, PROJECT_CONFIG);
|
|
22
|
+
}
|
|
23
|
+
export function projectDirPath(cwd) {
|
|
24
|
+
const base = cwd ?? process.cwd();
|
|
25
|
+
return resolve(base, PROJECT_DIR);
|
|
26
|
+
}
|
|
27
|
+
async function ensureGlobalDir() {
|
|
28
|
+
if (!existsSync(GLOBAL_DIR)) {
|
|
29
|
+
await mkdir(GLOBAL_DIR, { recursive: true });
|
|
30
|
+
await chmod(GLOBAL_DIR, 0o700);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function readJson(path) {
|
|
34
|
+
let raw;
|
|
35
|
+
try {
|
|
36
|
+
raw = await readFile(path, 'utf-8');
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(raw);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
console.warn(`Warning: corrupted JSON in ${path}`);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function writeJson(path, data, secure = false) {
|
|
50
|
+
await writeFile(path, JSON.stringify(data, null, 2) + '\n', 'utf-8');
|
|
51
|
+
if (secure) {
|
|
52
|
+
await chmod(path, 0o600);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function readGlobalConfig() {
|
|
56
|
+
return (await readJson(GLOBAL_CONFIG)) ?? {};
|
|
57
|
+
}
|
|
58
|
+
export async function writeGlobalConfig(config) {
|
|
59
|
+
await ensureGlobalDir();
|
|
60
|
+
await writeJson(GLOBAL_CONFIG, config, true);
|
|
61
|
+
}
|
|
62
|
+
export async function readAgents() {
|
|
63
|
+
return (await readJson(AGENTS_FILE)) ?? { agents: {} };
|
|
64
|
+
}
|
|
65
|
+
export async function writeAgents(store) {
|
|
66
|
+
await ensureGlobalDir();
|
|
67
|
+
await writeJson(AGENTS_FILE, store, true);
|
|
68
|
+
}
|
|
69
|
+
export async function readProjectConfig(cwd) {
|
|
70
|
+
return readJson(projectConfigPath(cwd));
|
|
71
|
+
}
|
|
72
|
+
export async function writeProjectConfig(config, cwd) {
|
|
73
|
+
const dir = projectDirPath(cwd);
|
|
74
|
+
if (!existsSync(dir)) {
|
|
75
|
+
await mkdir(dir, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
await writeJson(projectConfigPath(cwd), config);
|
|
78
|
+
}
|
|
79
|
+
export function resolveAgentKey(agentId, agents) {
|
|
80
|
+
return agents.agents[agentId]?.key ?? null;
|
|
81
|
+
}
|
|
82
|
+
export function hasAuth(config) {
|
|
83
|
+
return !!(config.auth?.token && config.auth?.refreshToken);
|
|
84
|
+
}
|
|
85
|
+
export function isTokenExpired(config) {
|
|
86
|
+
if (!config.auth?.expiresAt)
|
|
87
|
+
return true;
|
|
88
|
+
return Date.now() >= config.auth.expiresAt;
|
|
89
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const API_BASE_URL = "https://cubelife-2dd2e.web.app";
|
|
2
|
+
export declare const ID_DISPLAY_LENGTH = 8;
|
|
3
|
+
export declare const LABEL_WIDTH = 14;
|
|
4
|
+
export declare const DEFAULT_PANEL_WIDTH = 50;
|
|
5
|
+
export declare const API_TIMEOUT_MS = 5000;
|
|
6
|
+
export declare const CREATURE_TYPES: readonly ["gremlin", "cat", "owl", "axolotl", "robot", "fox", "dragon", "blob"];
|
|
7
|
+
export declare const CREATURE_DEFAULT_COLORS: Record<typeof CREATURE_TYPES[number], string>;
|
|
8
|
+
export declare const CREATURE_DESCRIPTIONS: Record<typeof CREATURE_TYPES[number], string>;
|
|
9
|
+
export declare const CREATURE_NAME_MAX = 24;
|
|
10
|
+
export declare function isValidHexColor(value: string): boolean;
|
|
11
|
+
export declare const VALID_STATES: readonly ["coding", "reading", "researching", "designing", "testing", "reviewing", "thinking", "writing", "error", "complete", "waiting", "awaiting_input"];
|
|
12
|
+
export declare const VALID_SENTIMENTS: readonly ["positive", "negative", "neutral"];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const API_BASE_URL = 'https://cubelife-2dd2e.web.app';
|
|
2
|
+
export const ID_DISPLAY_LENGTH = 8;
|
|
3
|
+
export const LABEL_WIDTH = 14;
|
|
4
|
+
export const DEFAULT_PANEL_WIDTH = 50;
|
|
5
|
+
export const API_TIMEOUT_MS = 5000;
|
|
6
|
+
export const CREATURE_TYPES = [
|
|
7
|
+
'gremlin', 'cat', 'owl', 'axolotl', 'robot', 'fox', 'dragon', 'blob',
|
|
8
|
+
];
|
|
9
|
+
export const CREATURE_DEFAULT_COLORS = {
|
|
10
|
+
gremlin: '#7B9F35',
|
|
11
|
+
cat: '#D4874E',
|
|
12
|
+
owl: '#8B7355',
|
|
13
|
+
axolotl: '#E8A0BF',
|
|
14
|
+
robot: '#7EAAC1',
|
|
15
|
+
fox: '#C75B39',
|
|
16
|
+
dragon: '#5B8C5A',
|
|
17
|
+
blob: '#9B7ED8',
|
|
18
|
+
};
|
|
19
|
+
export const CREATURE_DESCRIPTIONS = {
|
|
20
|
+
gremlin: 'Mischievous tinkerer. Loves breaking things to understand them.',
|
|
21
|
+
cat: 'Independent and curious. Watches everything with quiet judgement.',
|
|
22
|
+
owl: 'Wise observer. Thinks before acting, acts with precision.',
|
|
23
|
+
axolotl: 'Cheerful and resilient. Always regenerating, always smiling.',
|
|
24
|
+
robot: 'Logical and efficient. Runs on structure and clear objectives.',
|
|
25
|
+
fox: 'Clever and adaptable. Finds the shortcut others miss.',
|
|
26
|
+
dragon: 'Bold and protective. Guards the codebase with fire.',
|
|
27
|
+
blob: 'Easygoing shapeshifter. Goes with the flow, absorbs everything.',
|
|
28
|
+
};
|
|
29
|
+
export const CREATURE_NAME_MAX = 24;
|
|
30
|
+
const HEX_COLOR_RE = /^#[0-9A-Fa-f]{6}$/;
|
|
31
|
+
export function isValidHexColor(value) {
|
|
32
|
+
return HEX_COLOR_RE.test(value);
|
|
33
|
+
}
|
|
34
|
+
export const VALID_STATES = [
|
|
35
|
+
'coding', 'reading', 'researching', 'designing', 'testing',
|
|
36
|
+
'reviewing', 'thinking', 'writing', 'error', 'complete', 'waiting',
|
|
37
|
+
'awaiting_input',
|
|
38
|
+
];
|
|
39
|
+
export const VALID_SENTIMENTS = ['positive', 'negative', 'neutral'];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface DetectedTool {
|
|
2
|
+
name: string;
|
|
3
|
+
id: string;
|
|
4
|
+
configPath: string;
|
|
5
|
+
installed: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ToolDef {
|
|
8
|
+
name: string;
|
|
9
|
+
id: string;
|
|
10
|
+
paths: (home: string, cwd: string) => string[];
|
|
11
|
+
}
|
|
12
|
+
export declare const CLINE_EXTENSION_IDS: string[];
|
|
13
|
+
export declare function vscodeUserPath(): string;
|
|
14
|
+
export declare function vscodeGlobalStoragePath(): string;
|
|
15
|
+
export declare function detectTools(): DetectedTool[];
|
|
16
|
+
export declare function detectInstalledTools(): DetectedTool[];
|
|
17
|
+
export declare function getToolDef(id: string): ToolDef | undefined;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
export const CLINE_EXTENSION_IDS = [
|
|
5
|
+
'saoudrizwan.claude-dev',
|
|
6
|
+
'kodu-ai.claude-dev-experimental',
|
|
7
|
+
];
|
|
8
|
+
export function vscodeUserPath() {
|
|
9
|
+
const h = homedir();
|
|
10
|
+
switch (process.platform) {
|
|
11
|
+
case 'darwin':
|
|
12
|
+
return join(h, 'Library', 'Application Support', 'Code', 'User');
|
|
13
|
+
case 'win32':
|
|
14
|
+
return join(process.env['APPDATA'] ?? join(h, 'AppData', 'Roaming'), 'Code', 'User');
|
|
15
|
+
default:
|
|
16
|
+
return join(h, '.config', 'Code', 'User');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function vscodeGlobalStoragePath() {
|
|
20
|
+
return join(vscodeUserPath(), 'globalStorage');
|
|
21
|
+
}
|
|
22
|
+
function clinePaths() {
|
|
23
|
+
const gs = vscodeGlobalStoragePath();
|
|
24
|
+
return CLINE_EXTENSION_IDS.map((id) => join(gs, id, 'settings', 'cline_mcp_settings.json'));
|
|
25
|
+
}
|
|
26
|
+
const TOOL_DEFS = [
|
|
27
|
+
{
|
|
28
|
+
name: 'Claude Code',
|
|
29
|
+
id: 'claude-code',
|
|
30
|
+
paths: (h, c) => [
|
|
31
|
+
join(h, '.claude', 'settings.json'),
|
|
32
|
+
join(c, '.claude', 'settings.json'),
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Cursor',
|
|
37
|
+
id: 'cursor',
|
|
38
|
+
paths: (h, c) => [
|
|
39
|
+
join(c, '.cursor', 'mcp.json'),
|
|
40
|
+
join(h, '.cursor', 'mcp.json'),
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'Windsurf',
|
|
45
|
+
id: 'windsurf',
|
|
46
|
+
paths: (h) => [
|
|
47
|
+
join(h, '.codeium', 'windsurf', 'mcp_config.json'),
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Cline',
|
|
52
|
+
id: 'cline',
|
|
53
|
+
paths: () => clinePaths(),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Continue.dev',
|
|
57
|
+
id: 'continue',
|
|
58
|
+
paths: (h, c) => [
|
|
59
|
+
join(c, '.continue', 'config.yaml'),
|
|
60
|
+
join(h, '.continue', 'config.yaml'),
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'GitHub Copilot',
|
|
65
|
+
id: 'copilot',
|
|
66
|
+
paths: (_h, c) => [
|
|
67
|
+
join(c, '.vscode', 'mcp.json'),
|
|
68
|
+
join(vscodeUserPath(), 'mcp.json'),
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'Codex CLI',
|
|
73
|
+
id: 'codex',
|
|
74
|
+
paths: (h, c) => [
|
|
75
|
+
join(c, '.codex'),
|
|
76
|
+
join(h, '.codex'),
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
export function detectTools() {
|
|
81
|
+
const h = homedir();
|
|
82
|
+
const c = process.cwd();
|
|
83
|
+
return TOOL_DEFS.map((def) => {
|
|
84
|
+
const paths = def.paths(h, c);
|
|
85
|
+
const found = paths.find((p) => existsSync(p));
|
|
86
|
+
return {
|
|
87
|
+
name: def.name,
|
|
88
|
+
id: def.id,
|
|
89
|
+
configPath: found ?? paths[0],
|
|
90
|
+
installed: !!found,
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export function detectInstalledTools() {
|
|
95
|
+
return detectTools().filter((t) => t.installed);
|
|
96
|
+
}
|
|
97
|
+
export function getToolDef(id) {
|
|
98
|
+
return TOOL_DEFS.find((d) => d.id === id);
|
|
99
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AdminClient } from './api.js';
|
|
2
|
+
export type CheckStatus = 'pass' | 'fail' | 'warn';
|
|
3
|
+
export interface CheckResult {
|
|
4
|
+
name: string;
|
|
5
|
+
status: CheckStatus;
|
|
6
|
+
message: string;
|
|
7
|
+
fix?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function checkAuth(autoFix: boolean): Promise<CheckResult>;
|
|
10
|
+
export declare function checkTier(adminClient?: AdminClient): Promise<CheckResult>;
|
|
11
|
+
export declare function checkProject(adminClient?: AdminClient): Promise<CheckResult>;
|
|
12
|
+
export declare function checkAgent(adminClient?: AdminClient): Promise<CheckResult>;
|
|
13
|
+
export declare function checkApiKey(): Promise<CheckResult>;
|
|
14
|
+
export declare function checkIntegration(): Promise<CheckResult>;
|
|
15
|
+
export declare function checkHookScript(): Promise<CheckResult>;
|
|
16
|
+
export declare function checkConnectivity(): Promise<CheckResult>;
|
|
17
|
+
export declare function checkStateReport(): Promise<CheckResult>;
|
|
18
|
+
export declare function runAllChecks(autoFix: boolean): Promise<CheckResult[]>;
|