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
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { brand, status, dot, label, heading, dim } from './ui/index.js';
|
|
2
|
+
export { logo, sectionHeader, version } from './ui/index.js';
|
|
3
|
+
export { panel } from './ui/index.js';
|
|
4
|
+
export { table } from './ui/index.js';
|
|
5
|
+
export { renderSprite, textFallback } from './ui/index.js';
|
|
6
|
+
export { readGlobalConfig, writeGlobalConfig, readAgents, writeAgents, readProjectConfig, writeProjectConfig, hasAuth, isTokenExpired, } from './lib/index.js';
|
|
7
|
+
export type { GlobalConfig, AuthConfig, AgentEntry, AgentsStore, ProjectConfig, } from './lib/index.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { brand, status, dot, label, heading, dim } from './ui/index.js';
|
|
2
|
+
export { logo, sectionHeader, version } from './ui/index.js';
|
|
3
|
+
export { panel } from './ui/index.js';
|
|
4
|
+
export { table } from './ui/index.js';
|
|
5
|
+
export { renderSprite, textFallback } from './ui/index.js';
|
|
6
|
+
export { readGlobalConfig, writeGlobalConfig, readAgents, writeAgents, readProjectConfig, writeProjectConfig, hasAuth, isTokenExpired, } from './lib/index.js';
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export declare class ApiError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
constructor(status: number, message: string);
|
|
4
|
+
}
|
|
5
|
+
export type Tier = 'free' | 'standard' | 'pro';
|
|
6
|
+
export interface BillingUser {
|
|
7
|
+
tier: Tier;
|
|
8
|
+
sparks: number;
|
|
9
|
+
dormancyState: string;
|
|
10
|
+
starterPackClaimed?: boolean;
|
|
11
|
+
locale?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface UsageDay {
|
|
14
|
+
date: string;
|
|
15
|
+
eventsProcessed: number;
|
|
16
|
+
messagesSent: number;
|
|
17
|
+
sparksUsed: number;
|
|
18
|
+
apiCalls: number;
|
|
19
|
+
}
|
|
20
|
+
export interface BillingHistoryItem {
|
|
21
|
+
id: string;
|
|
22
|
+
type: 'subscription' | 'spark_topup' | 'starter_pack' | 'cosmetic';
|
|
23
|
+
product: 'world' | 'life';
|
|
24
|
+
amount: number;
|
|
25
|
+
currency: string;
|
|
26
|
+
sparks: number | null;
|
|
27
|
+
status: 'success' | 'failed';
|
|
28
|
+
paystackReference: string;
|
|
29
|
+
createdAt: string;
|
|
30
|
+
}
|
|
31
|
+
export interface SubscribeRequest {
|
|
32
|
+
plan: string;
|
|
33
|
+
email: string;
|
|
34
|
+
callbackUrl: string;
|
|
35
|
+
}
|
|
36
|
+
export interface SubscribeResponse {
|
|
37
|
+
authorisationUrl: string;
|
|
38
|
+
reference: string;
|
|
39
|
+
accessCode: string;
|
|
40
|
+
}
|
|
41
|
+
export interface VerifyResponse {
|
|
42
|
+
verified: boolean;
|
|
43
|
+
plan: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ProjectSummary {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
product: string;
|
|
49
|
+
ownerId: string;
|
|
50
|
+
createdAt: string;
|
|
51
|
+
}
|
|
52
|
+
export interface ProjectListResponse {
|
|
53
|
+
projects: ProjectSummary[];
|
|
54
|
+
}
|
|
55
|
+
export interface ProjectResponse {
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
product: string;
|
|
59
|
+
}
|
|
60
|
+
export interface AgentResponse {
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
form: 'human' | 'creature';
|
|
64
|
+
character?: Record<string, unknown>;
|
|
65
|
+
creature?: {
|
|
66
|
+
type: string;
|
|
67
|
+
color: string;
|
|
68
|
+
name?: string;
|
|
69
|
+
};
|
|
70
|
+
isPublic: boolean;
|
|
71
|
+
createdAt: string;
|
|
72
|
+
}
|
|
73
|
+
export type AgentSummary = AgentResponse;
|
|
74
|
+
export interface AgentListResponse {
|
|
75
|
+
agents: AgentSummary[];
|
|
76
|
+
}
|
|
77
|
+
export interface CreateAgentBody {
|
|
78
|
+
name: string;
|
|
79
|
+
form?: 'human' | 'creature';
|
|
80
|
+
creature?: {
|
|
81
|
+
type: string;
|
|
82
|
+
color?: string;
|
|
83
|
+
name?: string;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export interface UpdateAgentBody {
|
|
87
|
+
name?: string;
|
|
88
|
+
isPublic?: boolean;
|
|
89
|
+
form?: 'human' | 'creature';
|
|
90
|
+
creature?: {
|
|
91
|
+
type: string;
|
|
92
|
+
color?: string;
|
|
93
|
+
name?: string;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export interface CreateAgentResponse {
|
|
97
|
+
id: string;
|
|
98
|
+
name: string;
|
|
99
|
+
apiKey: string;
|
|
100
|
+
roomId: string;
|
|
101
|
+
}
|
|
102
|
+
export interface RegenerateKeyResponse {
|
|
103
|
+
apiKey: string;
|
|
104
|
+
}
|
|
105
|
+
export interface ReportOptions {
|
|
106
|
+
detail?: string;
|
|
107
|
+
progress?: number;
|
|
108
|
+
sentiment?: 'positive' | 'negative' | 'neutral';
|
|
109
|
+
}
|
|
110
|
+
export interface AgentState {
|
|
111
|
+
state: string;
|
|
112
|
+
detail?: string;
|
|
113
|
+
progress?: number;
|
|
114
|
+
sentiment?: string;
|
|
115
|
+
updatedAt?: number;
|
|
116
|
+
}
|
|
117
|
+
export interface TierLimits {
|
|
118
|
+
agents: number;
|
|
119
|
+
calls: number;
|
|
120
|
+
label: string;
|
|
121
|
+
priceZarCents: number;
|
|
122
|
+
embedInstances: number;
|
|
123
|
+
customBranding: boolean;
|
|
124
|
+
analyticsHistory: number;
|
|
125
|
+
webhooks: boolean;
|
|
126
|
+
}
|
|
127
|
+
export declare const TIER_LIMITS: Record<Tier, TierLimits>;
|
|
128
|
+
export declare function resolveTier(raw: string | undefined): Tier;
|
|
129
|
+
export declare class AdminClient {
|
|
130
|
+
private readonly baseUrl;
|
|
131
|
+
private token;
|
|
132
|
+
constructor(token: string, baseUrl?: string);
|
|
133
|
+
private get headers();
|
|
134
|
+
request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
135
|
+
updateToken(token: string): void;
|
|
136
|
+
getBillingUser(): Promise<BillingUser>;
|
|
137
|
+
getBillingUsage(days?: number): Promise<UsageDay[]>;
|
|
138
|
+
getBillingHistory(limit?: number): Promise<BillingHistoryItem[]>;
|
|
139
|
+
subscribe(opts: SubscribeRequest): Promise<SubscribeResponse>;
|
|
140
|
+
verifyPayment(reference: string): Promise<VerifyResponse>;
|
|
141
|
+
listProjects(): Promise<ProjectListResponse>;
|
|
142
|
+
createProject(name: string): Promise<ProjectResponse>;
|
|
143
|
+
deleteProject(id: string): Promise<void>;
|
|
144
|
+
listAgents(projectId: string): Promise<AgentListResponse>;
|
|
145
|
+
getAgent(projectId: string, agentId: string): Promise<AgentResponse>;
|
|
146
|
+
createAgent(projectId: string, body: CreateAgentBody): Promise<CreateAgentResponse>;
|
|
147
|
+
updateAgent(projectId: string, agentId: string, body: UpdateAgentBody): Promise<AgentResponse>;
|
|
148
|
+
deleteAgent(projectId: string, agentId: string): Promise<void>;
|
|
149
|
+
regenerateKey(projectId: string, agentId: string): Promise<RegenerateKeyResponse>;
|
|
150
|
+
}
|
|
151
|
+
export declare class AgentClient {
|
|
152
|
+
private readonly baseUrl;
|
|
153
|
+
private readonly apiKey;
|
|
154
|
+
constructor(apiKey: string, baseUrl?: string);
|
|
155
|
+
private get headers();
|
|
156
|
+
report(state: string, options?: ReportOptions): Promise<{
|
|
157
|
+
rateLimited: boolean;
|
|
158
|
+
}>;
|
|
159
|
+
getState(): Promise<AgentState>;
|
|
160
|
+
}
|
|
161
|
+
export declare function createAdminClient(): Promise<AdminClient>;
|
|
162
|
+
export declare function createAgentClient(agentIdOverride?: string): Promise<AgentClient | null>;
|
package/dist/lib/api.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { getValidToken } from './auth.js';
|
|
2
|
+
import { readAgents, readProjectConfig, resolveAgentKey } from './config.js';
|
|
3
|
+
import { API_BASE_URL } from './constants.js';
|
|
4
|
+
export class ApiError extends Error {
|
|
5
|
+
status;
|
|
6
|
+
constructor(status, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.name = 'ApiError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export const TIER_LIMITS = {
|
|
13
|
+
free: {
|
|
14
|
+
agents: 2, calls: 1_000, label: 'Free', priceZarCents: 0,
|
|
15
|
+
embedInstances: 1, customBranding: false, analyticsHistory: 0, webhooks: false,
|
|
16
|
+
},
|
|
17
|
+
standard: {
|
|
18
|
+
agents: 10, calls: 10_000, label: 'Standard', priceZarCents: 20_000,
|
|
19
|
+
embedInstances: 5, customBranding: true, analyticsHistory: 7, webhooks: false,
|
|
20
|
+
},
|
|
21
|
+
pro: {
|
|
22
|
+
agents: 50, calls: 100_000, label: 'Pro', priceZarCents: 40_000,
|
|
23
|
+
embedInstances: -1, customBranding: true, analyticsHistory: 90, webhooks: true,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
function isTier(value) {
|
|
27
|
+
return value in TIER_LIMITS;
|
|
28
|
+
}
|
|
29
|
+
export function resolveTier(raw) {
|
|
30
|
+
const tier = raw ?? 'free';
|
|
31
|
+
return isTier(tier) ? tier : 'free';
|
|
32
|
+
}
|
|
33
|
+
export class AdminClient {
|
|
34
|
+
baseUrl;
|
|
35
|
+
token;
|
|
36
|
+
constructor(token, baseUrl) {
|
|
37
|
+
this.baseUrl = baseUrl ?? API_BASE_URL;
|
|
38
|
+
this.token = token;
|
|
39
|
+
}
|
|
40
|
+
get headers() {
|
|
41
|
+
return {
|
|
42
|
+
'Content-Type': 'application/json',
|
|
43
|
+
Authorization: `Bearer ${this.token}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async request(method, path, body) {
|
|
47
|
+
const res = await fetch(`${this.baseUrl}/api${path}`, {
|
|
48
|
+
method,
|
|
49
|
+
headers: this.headers,
|
|
50
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const err = await res.json().catch(() => ({}));
|
|
54
|
+
throw new ApiError(res.status, err.error ?? 'Unknown error');
|
|
55
|
+
}
|
|
56
|
+
return res.json();
|
|
57
|
+
}
|
|
58
|
+
updateToken(token) {
|
|
59
|
+
this.token = token;
|
|
60
|
+
}
|
|
61
|
+
async getBillingUser() {
|
|
62
|
+
return this.request('GET', '/v1/billing/user');
|
|
63
|
+
}
|
|
64
|
+
async getBillingUsage(days = 7) {
|
|
65
|
+
const res = await this.request('GET', `/v1/billing/usage?days=${days}`);
|
|
66
|
+
return res.usage;
|
|
67
|
+
}
|
|
68
|
+
async getBillingHistory(limit = 10) {
|
|
69
|
+
const res = await this.request('GET', `/v1/billing/history?limit=${limit}`);
|
|
70
|
+
return res.history;
|
|
71
|
+
}
|
|
72
|
+
async subscribe(opts) {
|
|
73
|
+
return this.request('POST', '/v1/billing/subscribe', opts);
|
|
74
|
+
}
|
|
75
|
+
async verifyPayment(reference) {
|
|
76
|
+
return this.request('POST', '/v1/billing/verify', { reference });
|
|
77
|
+
}
|
|
78
|
+
async listProjects() {
|
|
79
|
+
return this.request('GET', '/v1/projects');
|
|
80
|
+
}
|
|
81
|
+
async createProject(name) {
|
|
82
|
+
return this.request('POST', '/v1/projects', { name, product: 'life' });
|
|
83
|
+
}
|
|
84
|
+
async deleteProject(id) {
|
|
85
|
+
await this.request('DELETE', `/v1/projects/${encodeURIComponent(id)}`);
|
|
86
|
+
}
|
|
87
|
+
async listAgents(projectId) {
|
|
88
|
+
return this.request('GET', `/v1/projects/${encodeURIComponent(projectId)}/agents`);
|
|
89
|
+
}
|
|
90
|
+
async getAgent(projectId, agentId) {
|
|
91
|
+
return this.request('GET', `/v1/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}`);
|
|
92
|
+
}
|
|
93
|
+
async createAgent(projectId, body) {
|
|
94
|
+
return this.request('POST', `/v1/projects/${encodeURIComponent(projectId)}/agents`, body);
|
|
95
|
+
}
|
|
96
|
+
async updateAgent(projectId, agentId, body) {
|
|
97
|
+
return this.request('PATCH', `/v1/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}`, body);
|
|
98
|
+
}
|
|
99
|
+
async deleteAgent(projectId, agentId) {
|
|
100
|
+
await this.request('DELETE', `/v1/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}`);
|
|
101
|
+
}
|
|
102
|
+
async regenerateKey(projectId, agentId) {
|
|
103
|
+
return this.request('POST', `/v1/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/regenerate-key`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export class AgentClient {
|
|
107
|
+
baseUrl;
|
|
108
|
+
apiKey;
|
|
109
|
+
constructor(apiKey, baseUrl) {
|
|
110
|
+
this.baseUrl = baseUrl ?? API_BASE_URL;
|
|
111
|
+
this.apiKey = apiKey;
|
|
112
|
+
}
|
|
113
|
+
get headers() {
|
|
114
|
+
return {
|
|
115
|
+
'Content-Type': 'application/json',
|
|
116
|
+
'X-Agent-Key': this.apiKey,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
async report(state, options) {
|
|
120
|
+
const body = { state, ...options };
|
|
121
|
+
const res = await fetch(`${this.baseUrl}/api/v1/state`, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: this.headers,
|
|
124
|
+
body: JSON.stringify(body),
|
|
125
|
+
});
|
|
126
|
+
if (res.status === 429)
|
|
127
|
+
return { rateLimited: true };
|
|
128
|
+
if (!res.ok) {
|
|
129
|
+
const err = await res.json().catch(() => ({}));
|
|
130
|
+
throw new ApiError(res.status, err.error ?? 'Unknown error');
|
|
131
|
+
}
|
|
132
|
+
return { rateLimited: false };
|
|
133
|
+
}
|
|
134
|
+
async getState() {
|
|
135
|
+
const res = await fetch(`${this.baseUrl}/api/v1/state`, {
|
|
136
|
+
method: 'GET',
|
|
137
|
+
headers: this.headers,
|
|
138
|
+
});
|
|
139
|
+
if (!res.ok) {
|
|
140
|
+
const err = await res.json().catch(() => ({}));
|
|
141
|
+
throw new ApiError(res.status, err.error ?? 'Unknown error');
|
|
142
|
+
}
|
|
143
|
+
return res.json();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export async function createAdminClient() {
|
|
147
|
+
const token = await getValidToken();
|
|
148
|
+
return new AdminClient(token);
|
|
149
|
+
}
|
|
150
|
+
export async function createAgentClient(agentIdOverride) {
|
|
151
|
+
const agentId = agentIdOverride
|
|
152
|
+
?? process.env['CUBELIFE_AGENT_ID']
|
|
153
|
+
?? (await readProjectConfig())?.agentId;
|
|
154
|
+
if (!agentId)
|
|
155
|
+
return null;
|
|
156
|
+
const apiKey = process.env['CUBELIFE_API_KEY'] ?? resolveAgentKey(agentId, await readAgents());
|
|
157
|
+
if (!apiKey)
|
|
158
|
+
return null;
|
|
159
|
+
return new AgentClient(apiKey);
|
|
160
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type AuthConfig } from './config.js';
|
|
2
|
+
export declare function register(email: string, password: string): Promise<AuthConfig>;
|
|
3
|
+
export declare function login(email: string, password: string): Promise<AuthConfig>;
|
|
4
|
+
export declare function refreshToken(): Promise<AuthConfig>;
|
|
5
|
+
export declare function getValidToken(): Promise<string>;
|
|
6
|
+
export declare function logout(): Promise<void>;
|
|
7
|
+
export declare function currentUser(): Promise<AuthConfig | null>;
|
|
8
|
+
export interface AuthSession {
|
|
9
|
+
token: string;
|
|
10
|
+
email: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function requireAuth(): Promise<AuthSession>;
|
package/dist/lib/auth.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { readGlobalConfig, writeGlobalConfig, } from './config.js';
|
|
2
|
+
const FIREBASE_API_KEY = 'AIzaSyDDn2cxnO3moce6wgXvVyRdGHjDDHGtx3g';
|
|
3
|
+
const AUTH_BASE = 'https://identitytoolkit.googleapis.com/v1/accounts';
|
|
4
|
+
async function firebaseAuth(endpoint, email, password) {
|
|
5
|
+
const url = `${AUTH_BASE}:${endpoint}?key=${FIREBASE_API_KEY}`;
|
|
6
|
+
const res = await fetch(url, {
|
|
7
|
+
method: 'POST',
|
|
8
|
+
headers: { 'Content-Type': 'application/json' },
|
|
9
|
+
body: JSON.stringify({ email, password, returnSecureToken: true }),
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok) {
|
|
12
|
+
const body = (await res.json());
|
|
13
|
+
const msg = body.error?.message ?? 'Authentication failed';
|
|
14
|
+
throw new Error(friendlyAuthError(msg));
|
|
15
|
+
}
|
|
16
|
+
return res.json();
|
|
17
|
+
}
|
|
18
|
+
function friendlyAuthError(code) {
|
|
19
|
+
const map = {
|
|
20
|
+
EMAIL_EXISTS: 'An account with this email already exists.',
|
|
21
|
+
EMAIL_NOT_FOUND: 'No account found with this email.',
|
|
22
|
+
INVALID_PASSWORD: 'Incorrect password.',
|
|
23
|
+
INVALID_LOGIN_CREDENTIALS: 'Invalid email or password.',
|
|
24
|
+
USER_DISABLED: 'This account has been disabled.',
|
|
25
|
+
TOO_MANY_ATTEMPTS_TRY_LATER: 'Too many attempts. Please try again later.',
|
|
26
|
+
WEAK_PASSWORD: 'Password must be at least 6 characters.',
|
|
27
|
+
};
|
|
28
|
+
return map[code] ?? `Authentication error: ${code}`;
|
|
29
|
+
}
|
|
30
|
+
function toAuthConfig(res) {
|
|
31
|
+
return {
|
|
32
|
+
email: res.email,
|
|
33
|
+
token: res.idToken,
|
|
34
|
+
refreshToken: res.refreshToken,
|
|
35
|
+
expiresAt: Date.now() + parseInt(res.expiresIn, 10) * 1000,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export async function register(email, password) {
|
|
39
|
+
const res = await firebaseAuth('signUp', email, password);
|
|
40
|
+
const auth = toAuthConfig(res);
|
|
41
|
+
const config = await readGlobalConfig();
|
|
42
|
+
config.auth = auth;
|
|
43
|
+
await writeGlobalConfig(config);
|
|
44
|
+
return auth;
|
|
45
|
+
}
|
|
46
|
+
export async function login(email, password) {
|
|
47
|
+
const res = await firebaseAuth('signInWithPassword', email, password);
|
|
48
|
+
const auth = toAuthConfig(res);
|
|
49
|
+
const config = await readGlobalConfig();
|
|
50
|
+
config.auth = auth;
|
|
51
|
+
await writeGlobalConfig(config);
|
|
52
|
+
return auth;
|
|
53
|
+
}
|
|
54
|
+
export async function refreshToken() {
|
|
55
|
+
const config = await readGlobalConfig();
|
|
56
|
+
if (!config.auth?.refreshToken) {
|
|
57
|
+
throw new Error('Not logged in. Run `cubelife login` first.');
|
|
58
|
+
}
|
|
59
|
+
const url = `https://securetoken.googleapis.com/v1/token?key=${FIREBASE_API_KEY}`;
|
|
60
|
+
const res = await fetch(url, {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
63
|
+
body: new URLSearchParams({
|
|
64
|
+
grant_type: 'refresh_token',
|
|
65
|
+
refresh_token: config.auth.refreshToken,
|
|
66
|
+
}).toString(),
|
|
67
|
+
});
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
throw new Error('Token refresh failed. Run `cubelife login` to re-authenticate.');
|
|
70
|
+
}
|
|
71
|
+
const body = (await res.json());
|
|
72
|
+
const auth = {
|
|
73
|
+
email: config.auth.email,
|
|
74
|
+
token: body.id_token,
|
|
75
|
+
refreshToken: body.refresh_token,
|
|
76
|
+
expiresAt: Date.now() + parseInt(body.expires_in, 10) * 1000,
|
|
77
|
+
};
|
|
78
|
+
config.auth = auth;
|
|
79
|
+
await writeGlobalConfig(config);
|
|
80
|
+
return auth;
|
|
81
|
+
}
|
|
82
|
+
export async function getValidToken() {
|
|
83
|
+
const config = await readGlobalConfig();
|
|
84
|
+
if (!config.auth?.token) {
|
|
85
|
+
throw new Error('Not logged in. Run `cubelife login` first.');
|
|
86
|
+
}
|
|
87
|
+
if (Date.now() >= (config.auth.expiresAt ?? 0)) {
|
|
88
|
+
const refreshed = await refreshToken();
|
|
89
|
+
return refreshed.token;
|
|
90
|
+
}
|
|
91
|
+
return config.auth.token;
|
|
92
|
+
}
|
|
93
|
+
export async function logout() {
|
|
94
|
+
const config = await readGlobalConfig();
|
|
95
|
+
delete config.auth;
|
|
96
|
+
await writeGlobalConfig(config);
|
|
97
|
+
}
|
|
98
|
+
export async function currentUser() {
|
|
99
|
+
const config = await readGlobalConfig();
|
|
100
|
+
return config.auth ?? null;
|
|
101
|
+
}
|
|
102
|
+
export async function requireAuth() {
|
|
103
|
+
const config = await readGlobalConfig();
|
|
104
|
+
if (!config.auth?.token) {
|
|
105
|
+
throw new Error('Not logged in. Run `cubelife login` first.');
|
|
106
|
+
}
|
|
107
|
+
let token = config.auth.token;
|
|
108
|
+
if (Date.now() >= (config.auth.expiresAt ?? 0)) {
|
|
109
|
+
const refreshed = await refreshToken();
|
|
110
|
+
token = refreshed.token;
|
|
111
|
+
}
|
|
112
|
+
return { token, email: config.auth.email };
|
|
113
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function openBrowser(url: string): Promise<boolean>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
export function openBrowser(url) {
|
|
3
|
+
try {
|
|
4
|
+
const parsed = new URL(url);
|
|
5
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
|
6
|
+
return Promise.resolve(false);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return Promise.resolve(false);
|
|
11
|
+
}
|
|
12
|
+
const cmd = process.platform === 'darwin' ? 'open'
|
|
13
|
+
: process.platform === 'win32' ? 'cmd'
|
|
14
|
+
: 'xdg-open';
|
|
15
|
+
const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
const child = spawn(cmd, args, { stdio: 'ignore' });
|
|
18
|
+
child.on('error', () => resolve(false));
|
|
19
|
+
child.on('close', (code) => resolve(code === 0));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
3
|
+
export interface RootOpts {
|
|
4
|
+
json?: boolean;
|
|
5
|
+
yes?: boolean;
|
|
6
|
+
agent?: string;
|
|
7
|
+
project?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function rootOpts(cmd: Command): RootOpts;
|
|
10
|
+
export declare function getErrorMessage(err: unknown): string;
|
|
11
|
+
export interface CommandErrorOptions {
|
|
12
|
+
error: unknown;
|
|
13
|
+
json?: boolean;
|
|
14
|
+
spinner?: ReturnType<typeof p.spinner>;
|
|
15
|
+
exitCode?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function handleCommandError(opts: CommandErrorOptions): never;
|
|
18
|
+
export interface ResolvedProject {
|
|
19
|
+
projectId: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function requireProject(cmd: Command, json?: boolean): Promise<ResolvedProject>;
|
|
22
|
+
export interface ResolvedAgent {
|
|
23
|
+
projectId: string;
|
|
24
|
+
agentId: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function requireLinkedAgent(json?: boolean): Promise<ResolvedAgent>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import { resolveProjectId } from './resolve.js';
|
|
3
|
+
import { readProjectConfig } from './config.js';
|
|
4
|
+
import { brand } from '../ui/theme.js';
|
|
5
|
+
import { getRootOpts } from '../ui/helpers.js';
|
|
6
|
+
export function rootOpts(cmd) {
|
|
7
|
+
return getRootOpts(cmd);
|
|
8
|
+
}
|
|
9
|
+
export function getErrorMessage(err) {
|
|
10
|
+
if (err instanceof Error)
|
|
11
|
+
return err.message;
|
|
12
|
+
return String(err);
|
|
13
|
+
}
|
|
14
|
+
export function handleCommandError(opts) {
|
|
15
|
+
const msg = getErrorMessage(opts.error);
|
|
16
|
+
if (opts.spinner && !opts.json) {
|
|
17
|
+
opts.spinner.stop('Failed');
|
|
18
|
+
}
|
|
19
|
+
if (opts.json) {
|
|
20
|
+
console.log(JSON.stringify({ error: msg }));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
p.log.error(msg);
|
|
24
|
+
}
|
|
25
|
+
process.exit(opts.exitCode ?? 1);
|
|
26
|
+
}
|
|
27
|
+
export async function requireProject(cmd, json) {
|
|
28
|
+
const projectFlag = cmd.parent?.opts().project;
|
|
29
|
+
let projectId;
|
|
30
|
+
try {
|
|
31
|
+
projectId = await resolveProjectId(projectFlag);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
handleCommandError({ error: err, json });
|
|
35
|
+
}
|
|
36
|
+
if (!projectId) {
|
|
37
|
+
const msg = `No project linked. Run ${brand.accent('cubelife init')} first.`;
|
|
38
|
+
if (json) {
|
|
39
|
+
console.log(JSON.stringify({ error: 'no_project_linked' }));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
p.log.error(msg);
|
|
43
|
+
}
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
return { projectId };
|
|
47
|
+
}
|
|
48
|
+
export async function requireLinkedAgent(json) {
|
|
49
|
+
const config = await readProjectConfig();
|
|
50
|
+
if (!config?.projectId || !config?.agentId) {
|
|
51
|
+
if (json) {
|
|
52
|
+
console.log(JSON.stringify({ error: 'no_agent_linked' }));
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
p.log.error(`No agent linked. Run ${brand.accent('cubelife init')} first.`);
|
|
56
|
+
}
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
return { projectId: config.projectId, agentId: config.agentId };
|
|
60
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { STATE_MANIFEST, CREATURE_MANIFEST } from '../data/sprite-data.js';
|
|
2
|
+
export interface Colour {
|
|
3
|
+
r: number;
|
|
4
|
+
g: number;
|
|
5
|
+
b: number;
|
|
6
|
+
}
|
|
7
|
+
export interface CreatureTraits {
|
|
8
|
+
type: string;
|
|
9
|
+
color: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface HumanTraits {
|
|
13
|
+
skinTone?: string;
|
|
14
|
+
outfit?: string;
|
|
15
|
+
outfitColor?: string;
|
|
16
|
+
hairStyle?: string;
|
|
17
|
+
hairColor?: string;
|
|
18
|
+
accessories?: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface CompositeResult {
|
|
21
|
+
rgba: Uint8Array;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function hexToColour(hex: string): Colour;
|
|
26
|
+
export declare function applyTint(lumAlpha: Uint8Array, colour: Colour): Uint8Array;
|
|
27
|
+
export declare function alphaBlend(dst: Uint8Array, src: Uint8Array): void;
|
|
28
|
+
export declare function compositeCreature(creature: CreatureTraits, state: string, frameIndex: number): CompositeResult;
|
|
29
|
+
export declare function compositeHuman(traits: HumanTraits, state: string, frameIndex: number): CompositeResult;
|
|
30
|
+
export declare function getFrameCount(form: 'creature' | 'human', creatureType?: string, state?: string): number;
|
|
31
|
+
export declare function getFps(form: 'creature' | 'human', creatureType?: string, state?: string): number;
|
|
32
|
+
export declare function createDissolve(): Uint16Array;
|
|
33
|
+
export declare function applyDissolve(from: Uint8Array, to: Uint8Array, order: Uint16Array, t: number): Uint8Array;
|
|
34
|
+
export { STATE_MANIFEST, CREATURE_MANIFEST };
|