Skip to content

Commit eb9528c

Browse files
author
Shaw
committed
chore: preserve eliza launchpad worktree
2 parents 31d5163 + 3b41268 commit eb9528c

87 files changed

Lines changed: 6610 additions & 540 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/app-clawville/assets/hero.png

-349 KB
Loading
-238 KB
Loading

apps/app-lifeops/src/components/LifeOpsScreenTimeSection.tsx

Lines changed: 1 addition & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from "lucide-react";
2222
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2323
import type {
24-
LifeOpsScreenTimeBreakdown,
24+
LifeOpsScreenTimeHistoryResponse,
2525
LifeOpsSocialHabitSummary,
2626
} from "../api/client-lifeops.js";
2727
import type { LifeOpsSection } from "../hooks/useLifeOpsSection.js";
@@ -46,7 +46,6 @@ const RANGE_OPTIONS: Array<{ key: RangeKey; label: string }> = [
4646

4747
const CACHE_TTL_MS = 30_000;
4848

49-
type Period = { since: string; until: string };
5049
type WebsiteBlockerStatus = Awaited<
5150
ReturnType<typeof client.getWebsiteBlockerStatus>
5251
>;
@@ -59,76 +58,6 @@ type UnifiedSocialBlockStatus = {
5958
details: string[];
6059
};
6160

62-
function startOfLocalDay(date: Date): Date {
63-
const start = new Date(date);
64-
start.setHours(0, 0, 0, 0);
65-
return start;
66-
}
67-
68-
function addDays(date: Date, days: number): Date {
69-
const next = new Date(date);
70-
next.setDate(next.getDate() + days);
71-
return next;
72-
}
73-
74-
function computeRange(range: RangeKey): Period {
75-
const now = new Date();
76-
const until = now.toISOString();
77-
if (range === "today") {
78-
return { since: startOfLocalDay(now).toISOString(), until };
79-
}
80-
if (range === "this-week") {
81-
const startToday = startOfLocalDay(now);
82-
const dayOfWeek = startToday.getDay(); // Sunday = 0
83-
const since = addDays(startToday, -dayOfWeek);
84-
return { since: since.toISOString(), until };
85-
}
86-
if (range === "7d") {
87-
const since = addDays(startOfLocalDay(now), -6);
88-
return { since: since.toISOString(), until };
89-
}
90-
const since = addDays(startOfLocalDay(now), -29);
91-
return { since: since.toISOString(), until };
92-
}
93-
94-
function computePriorRange(range: RangeKey): Period | null {
95-
if (range === "today") return null;
96-
const current = computeRange(range);
97-
const sinceMs = Date.parse(current.since);
98-
const untilMs = Date.parse(current.until);
99-
const span = untilMs - sinceMs;
100-
return {
101-
since: new Date(sinceMs - span).toISOString(),
102-
until: current.since,
103-
};
104-
}
105-
106-
function enumerateDays(period: Period): Date[] {
107-
const days: Date[] = [];
108-
const start = startOfLocalDay(new Date(Date.parse(period.since)));
109-
const endMs = Date.parse(period.until);
110-
let cursor = start;
111-
while (cursor.getTime() <= endMs) {
112-
days.push(cursor);
113-
cursor = addDays(cursor, 1);
114-
}
115-
return days;
116-
}
117-
118-
function formatDayLabel(date: Date): string {
119-
return new Intl.DateTimeFormat(undefined, {
120-
month: "numeric",
121-
day: "numeric",
122-
}).format(date);
123-
}
124-
125-
function socialServiceSeconds(
126-
summary: LifeOpsSocialHabitSummary | null,
127-
key: string,
128-
): number {
129-
return summary?.services.find((item) => item.key === key)?.totalSeconds ?? 0;
130-
}
131-
13261
function formatInlineList(values: string[]): string {
13362
if (values.length === 0) return "";
13463
if (values.length === 1) return values[0];
@@ -231,13 +160,6 @@ function sourceTone(state: "live" | "partial" | "unwired"): string {
231160
}
232161
}
233162

234-
function deltaPercent(current: number, prior: number): number | null {
235-
if (prior <= 0) {
236-
return current > 0 ? null : 0;
237-
}
238-
return Math.round(((current - prior) / prior) * 100);
239-
}
240-
241163
function DeltaBadge({ percent }: { percent: number | null }) {
242164
if (percent === null) {
243165
return (

apps/app-lifeops/src/lifeops/service-mixin-screentime.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,11 @@ describe("LifeOps screen-time social data sources", () => {
136136

137137
expect(social.dataSources).toEqual(
138138
expect.arrayContaining([
139-
expect.objectContaining({ id: "android_usage_stats", state: "live" }),
139+
expect.objectContaining({
140+
id: "android_usage_stats",
141+
state: "partial",
142+
statusLabel: "Snapshot only",
143+
}),
140144
]),
141145
);
142146
});

apps/app-steward/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"dependencies": {
2222
"@noble/curves": "^2.0.1",
2323
"@simplewebauthn/browser": "^13.0.0",
24+
"@solana/web3.js": "^1.98.4",
2425
"@stwd/sdk": "^0.8.0",
2526
"@elizaos/core": "workspace:*",
2627
"@elizaos/shared": "workspace:*",

apps/app-steward/src/browser-workspace-wallet.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export interface BrowserWorkspaceWalletState {
3232
solanaAddress: string | null;
3333
solanaConnected: boolean;
3434
solanaMessageSigningAvailable: boolean;
35+
solanaTransactionSigningAvailable: boolean;
3536
}
3637

3738
export interface BrowserWorkspaceWalletTransactionResult
@@ -53,6 +54,20 @@ export interface BrowserWorkspaceSolanaMessageSignatureResult {
5354
signatureBase64: string;
5455
}
5556

57+
export interface BrowserWorkspaceSolanaTransactionResult {
58+
address: string;
59+
mode: "local-key" | "steward";
60+
/** Base64-encoded fully-signed transaction (always present on success). */
61+
signedTransactionBase64: string;
62+
/**
63+
* Optional broadcast signature (base58) when the steward broadcast the
64+
* transaction. Omitted when the caller asked for signing only.
65+
*/
66+
signature?: string;
67+
/** Cluster the steward signed/broadcast against. */
68+
cluster: "mainnet" | "devnet" | "testnet";
69+
}
70+
5671
export type BrowserWorkspaceWalletRpcMethod =
5772
| "eth_accounts"
5873
| "eth_requestAccounts"
@@ -64,7 +79,9 @@ export type BrowserWorkspaceWalletRpcMethod =
6479

6580
export type BrowserWorkspaceSolanaMethod =
6681
| "solana_connect"
67-
| "solana_signMessage";
82+
| "solana_signMessage"
83+
| "solana_signTransaction"
84+
| "solana_signAndSendTransaction";
6885

6986
export type BrowserWorkspaceWalletMethod =
7087
| "getState"
@@ -109,6 +126,7 @@ export const EMPTY_BROWSER_WORKSPACE_WALLET_STATE: BrowserWorkspaceWalletState =
109126
solanaAddress: null,
110127
solanaConnected: false,
111128
solanaMessageSigningAvailable: false,
129+
solanaTransactionSigningAvailable: false,
112130
};
113131

114132
export function getBrowserWorkspaceWalletAddress(
@@ -206,10 +224,14 @@ export function buildBrowserWorkspaceWalletState(params: {
206224
solanaAddress,
207225
solanaConnected,
208226
solanaMessageSigningAvailable: false,
227+
solanaTransactionSigningAvailable: solanaConnected,
209228
};
210229
}
211230

212231
if (mode === "local") {
232+
const solanaTransactionSigningAvailable = Boolean(
233+
solanaAddress && walletConfig?.solanaSigningAvailable,
234+
);
213235
return {
214236
address,
215237
connected: evmConnected || solanaConnected,
@@ -229,10 +251,12 @@ export function buildBrowserWorkspaceWalletState(params: {
229251
),
230252
signingAvailable:
231253
Boolean(evmAddress && walletConfig?.executionReady) ||
232-
solanaMessageSigningAvailable,
254+
solanaMessageSigningAvailable ||
255+
solanaTransactionSigningAvailable,
233256
solanaAddress,
234257
solanaConnected,
235258
solanaMessageSigningAvailable,
259+
solanaTransactionSigningAvailable,
236260
};
237261
}
238262

@@ -256,6 +280,7 @@ export function buildBrowserWorkspaceWalletState(params: {
256280
solanaAddress,
257281
solanaConnected,
258282
solanaMessageSigningAvailable: false,
283+
solanaTransactionSigningAvailable: false,
259284
};
260285
}
261286

apps/app-steward/src/routes/wallet-browser-compat-routes.ts

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,85 @@ async function signLocalBrowserSolanaMessage(
267267
};
268268
}
269269

270+
type SolanaCluster = "mainnet" | "devnet" | "testnet";
271+
272+
function normalizeSolanaCluster(value: unknown): SolanaCluster {
273+
if (value === "devnet" || value === "testnet" || value === "mainnet") {
274+
return value;
275+
}
276+
return "mainnet";
277+
}
278+
279+
function clusterRpcUrl(cluster: SolanaCluster): string {
280+
switch (cluster) {
281+
case "devnet":
282+
return "https://api.devnet.solana.com";
283+
case "testnet":
284+
return "https://api.testnet.solana.com";
285+
default:
286+
return "https://api.mainnet-beta.solana.com";
287+
}
288+
}
289+
290+
async function signLocalBrowserSolanaTransaction(
291+
body: Record<string, unknown>,
292+
): Promise<{
293+
address: string;
294+
mode: "local-key";
295+
signedTransactionBase64: string;
296+
signature?: string;
297+
cluster: SolanaCluster;
298+
}> {
299+
const transactionBase64 = normalizeString(body.transactionBase64);
300+
if (!transactionBase64) {
301+
throw new Error("transactionBase64 is required.");
302+
}
303+
const broadcast = normalizeBoolean(body.broadcast, false);
304+
const cluster = normalizeSolanaCluster(body.cluster);
305+
306+
const { address, seed } = resolveLocalSolanaSeed();
307+
308+
// Lazy import — @solana/web3.js is a transitive dep through
309+
// @elizaos/agent and only needed when an actual Solana transaction
310+
// signing request lands.
311+
const web3 = await import("@solana/web3.js");
312+
const { Keypair, VersionedTransaction, Transaction, Connection } = web3;
313+
314+
const keypair = Keypair.fromSeed(new Uint8Array(seed));
315+
const txBytes = Buffer.from(transactionBase64, "base64");
316+
317+
// Solana transactions have a single-byte version prefix on v0/versioned
318+
// transactions (high bit set). Try the versioned path first; fall back to
319+
// legacy on parse failure to support both shapes uniformly.
320+
let signedBytes: Uint8Array;
321+
let broadcastSignature: string | undefined;
322+
try {
323+
const versioned = VersionedTransaction.deserialize(txBytes);
324+
versioned.sign([keypair]);
325+
signedBytes = versioned.serialize();
326+
if (broadcast) {
327+
const conn = new Connection(clusterRpcUrl(cluster), "confirmed");
328+
broadcastSignature = await conn.sendRawTransaction(signedBytes);
329+
}
330+
} catch (_err) {
331+
const legacy = Transaction.from(txBytes);
332+
legacy.partialSign(keypair);
333+
signedBytes = legacy.serialize();
334+
if (broadcast) {
335+
const conn = new Connection(clusterRpcUrl(cluster), "confirmed");
336+
broadcastSignature = await conn.sendRawTransaction(signedBytes);
337+
}
338+
}
339+
340+
return {
341+
address,
342+
mode: "local-key",
343+
signedTransactionBase64: Buffer.from(signedBytes).toString("base64"),
344+
...(broadcastSignature ? { signature: broadcastSignature } : {}),
345+
cluster,
346+
};
347+
}
348+
270349
export async function handleWalletBrowserCompatRoutes(
271350
req: http.IncomingMessage,
272351
res: http.ServerResponse,
@@ -279,7 +358,8 @@ export async function handleWalletBrowserCompatRoutes(
279358
method !== "POST" ||
280359
(url.pathname !== "/api/wallet/browser-transaction" &&
281360
url.pathname !== "/api/wallet/browser-sign-message" &&
282-
url.pathname !== "/api/wallet/browser-solana-sign-message")
361+
url.pathname !== "/api/wallet/browser-solana-sign-message" &&
362+
url.pathname !== "/api/wallet/browser-solana-transaction")
283363
) {
284364
return false;
285365
}
@@ -349,6 +429,31 @@ export async function handleWalletBrowserCompatRoutes(
349429
return true;
350430
}
351431

432+
if (url.pathname === "/api/wallet/browser-solana-transaction") {
433+
if (hasLocalSolanaKey) {
434+
try {
435+
sendJsonResponse(
436+
res,
437+
200,
438+
await signLocalBrowserSolanaTransaction(body),
439+
);
440+
return true;
441+
} catch (error) {
442+
const failureMessage =
443+
error instanceof Error ? error.message : String(error);
444+
sendJsonErrorResponse(res, 503, failureMessage);
445+
return true;
446+
}
447+
}
448+
449+
sendJsonErrorResponse(
450+
res,
451+
503,
452+
"No browser Solana transaction signer is available.",
453+
);
454+
return true;
455+
}
456+
352457
const request: StewardSignRequest = {
353458
broadcast: normalizeBoolean(body.broadcast, true),
354459
chainId:

0 commit comments

Comments
 (0)