@elizaos/app-hyperscape 1.0.0 → 2.0.0-beta.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/dist/index.d.ts +10 -17
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -45
- package/dist/index.js.map +1 -1
- package/dist/routes.d.ts +22 -18
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +362 -75
- package/dist/routes.js.map +1 -1
- package/dist/ui/HyperscapeDetailExtension.d.ts +3 -0
- package/dist/ui/HyperscapeDetailExtension.d.ts.map +1 -0
- package/dist/ui/HyperscapeDetailExtension.js +9 -0
- package/dist/ui/HyperscapeDetailExtension.js.map +1 -0
- package/dist/ui/HyperscapeOperatorSurface.d.ts +3 -0
- package/dist/ui/HyperscapeOperatorSurface.d.ts.map +1 -0
- package/dist/ui/HyperscapeOperatorSurface.js +343 -0
- package/dist/ui/HyperscapeOperatorSurface.js.map +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +20 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +41 -66
- package/elizaos.plugin.json +0 -63
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import { Plugin } from
|
|
2
|
-
|
|
1
|
+
import type { Plugin } from "@elizaos/core";
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
3
|
+
* `@elizaos/app-hyperscape` plugin entry point.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* This is a "connect" type app — the agent plugin connects to an external
|
|
10
|
-
* Hyperscape game server via WebSocket. The player opens the Hyperscape client
|
|
11
|
-
* in their browser to watch their agent play.
|
|
12
|
-
*
|
|
13
|
-
* When the full @hyperscape/plugin-hyperscape is published to npm, this package
|
|
14
|
-
* will re-export it. Until then, this provides the registry-compatible wrapper
|
|
15
|
-
* with app metadata and basic configuration.
|
|
5
|
+
* Provides session resolvers for the Hyperscape game integration.
|
|
6
|
+
* The route module (`./routes.ts`) handles live session resolution
|
|
7
|
+
* by fetching data from the Hyperscape API.
|
|
16
8
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export
|
|
9
|
+
declare const hyperscapePlugin: Plugin;
|
|
10
|
+
export default hyperscapePlugin;
|
|
11
|
+
export * from "./routes.js";
|
|
12
|
+
export * from "./ui/index.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;;;;GAMG;AACH,QAAA,MAAM,gBAAgB,EAAE,MAIvB,CAAC;AAEF,eAAe,gBAAgB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,51 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import { logger } from "@elizaos/core";
|
|
3
|
-
function env(key, fallback = "") {
|
|
4
|
-
const v = process.env[key]?.trim();
|
|
5
|
-
return v && v !== "undefined" && v !== "null" ? v : fallback;
|
|
6
|
-
}
|
|
7
|
-
var appHyperscape = {
|
|
1
|
+
const hyperscapePlugin = {
|
|
8
2
|
name: "@elizaos/app-hyperscape",
|
|
9
|
-
description: "
|
|
10
|
-
config: {
|
|
11
|
-
HYPERSCAPE_SERVER_URL: env("HYPERSCAPE_SERVER_URL", "ws://localhost:5555/ws"),
|
|
12
|
-
HYPERSCAPE_AUTO_RECONNECT: env("HYPERSCAPE_AUTO_RECONNECT", "true"),
|
|
13
|
-
HYPERSCAPE_AUTH_TOKEN: env("HYPERSCAPE_AUTH_TOKEN"),
|
|
14
|
-
HYPERSCAPE_AUTONOMY_MODE: env("HYPERSCAPE_AUTONOMY_MODE", "llm")
|
|
15
|
-
},
|
|
16
|
-
init: async (_config, runtime) => {
|
|
17
|
-
const serverUrl = runtime.getSetting("HYPERSCAPE_SERVER_URL") || process.env.HYPERSCAPE_SERVER_URL || "ws://localhost:5555/ws";
|
|
18
|
-
logger.info(`[app-hyperscape] Configured for server: ${serverUrl}`);
|
|
19
|
-
},
|
|
20
|
-
app: {
|
|
21
|
-
displayName: "Hyperscape",
|
|
22
|
-
category: "game",
|
|
23
|
-
launchType: "connect",
|
|
24
|
-
launchUrl: "https://hyperscape.ai",
|
|
25
|
-
capabilities: [
|
|
26
|
-
"combat",
|
|
27
|
-
"skills",
|
|
28
|
-
"inventory",
|
|
29
|
-
"banking",
|
|
30
|
-
"social-chat",
|
|
31
|
-
"exploration",
|
|
32
|
-
"crafting"
|
|
33
|
-
],
|
|
34
|
-
viewer: {
|
|
35
|
-
url: "http://localhost:3333",
|
|
36
|
-
embedParams: {
|
|
37
|
-
embedded: "true",
|
|
38
|
-
mode: "spectator",
|
|
39
|
-
quality: "medium"
|
|
40
|
-
},
|
|
41
|
-
postMessageAuth: true,
|
|
42
|
-
sandbox: "allow-scripts allow-same-origin allow-popups allow-forms"
|
|
43
|
-
}
|
|
44
|
-
}
|
|
3
|
+
description: "Hyperscape game session resolvers \u2014 spectate-and-steer agent sessions with live data from the Hyperscape API."
|
|
45
4
|
};
|
|
46
|
-
var index_default =
|
|
5
|
+
var index_default = hyperscapePlugin;
|
|
6
|
+
export * from "./routes.js";
|
|
7
|
+
export * from "./ui/index.js";
|
|
47
8
|
export {
|
|
48
|
-
appHyperscape,
|
|
49
9
|
index_default as default
|
|
50
10
|
};
|
|
51
11
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin } from \"@elizaos/core\";\n\n/**\n * `@elizaos/app-hyperscape` plugin entry point.\n *\n * Provides session resolvers for the Hyperscape game integration.\n * The route module (`./routes.ts`) handles live session resolution\n * by fetching data from the Hyperscape API.\n */\nconst hyperscapePlugin: Plugin = {\n name: \"@elizaos/app-hyperscape\",\n description:\n \"Hyperscape game session resolvers — spectate-and-steer agent sessions with live data from the Hyperscape API.\",\n};\n\nexport default hyperscapePlugin;\nexport * from \"./routes.js\";\nexport * from \"./ui/index.js\";\n"],"mappings":"AASA,MAAM,mBAA2B;AAAA,EAC/B,MAAM;AAAA,EACN,aACE;AACJ;AAEA,IAAO,gBAAQ;AACf,cAAc;AACd,cAAc;","names":[]}
|
package/dist/routes.d.ts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import type { IAgentRuntime } from "@elizaos/core";
|
|
2
|
+
import type { AppLaunchDiagnostic, AppLaunchPreparation, AppLaunchResult, AppLaunchSessionContext, AppRunSessionContext, AppSessionState, AppViewerAuthMessage } from "@elizaos/shared";
|
|
3
|
+
export declare function prepareLaunch(ctx: {
|
|
4
|
+
runtime: IAgentRuntime | null;
|
|
5
|
+
}): Promise<AppLaunchPreparation>;
|
|
6
|
+
export declare function resolveViewerAuthMessage(ctx: {
|
|
7
|
+
runtime: IAgentRuntime | null;
|
|
8
|
+
}): Promise<AppViewerAuthMessage | null>;
|
|
9
|
+
export declare function collectLaunchDiagnostics(ctx: {
|
|
10
|
+
viewer: AppLaunchResult["viewer"] | null;
|
|
11
|
+
}): Promise<AppLaunchDiagnostic[]>;
|
|
12
|
+
export declare function resolveLaunchSession(ctx: AppLaunchSessionContext): Promise<AppSessionState | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Called by the host app-manager when the user stops the Hyperscape run.
|
|
15
|
+
* Hyperscape is a stateless session resolver against an external API —
|
|
16
|
+
* there are no local resources (WebSockets, timers, processes) to tear
|
|
17
|
+
* down. Iframe unmount is sufficient. This hook is present so the
|
|
18
|
+
* app-manager lifecycle path stays uniform across all game apps.
|
|
19
|
+
*/
|
|
20
|
+
export declare function stopRun(): Promise<void>;
|
|
21
|
+
export declare function refreshRunSession(ctx: AppRunSessionContext): Promise<AppSessionState | null>;
|
|
22
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EAEpB,eAAe,EACf,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAmczB,wBAAsB,aAAa,CAAC,GAAG,EAAE;IACvC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;CAC/B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAGhC;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE;IAClD,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;CAC/B,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAsBvC;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE;IAClD,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;CAC1C,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAYjC;AAED,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,uBAAuB,GAC3B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CA6BjC;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAE7C;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAsBjC"}
|
package/dist/routes.js
CHANGED
|
@@ -1,88 +1,375 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
);
|
|
24
|
-
if (embeddedActionMatch) {
|
|
25
|
-
const characterId = decodeURIComponent(embeddedActionMatch[1]);
|
|
26
|
-
const action = embeddedActionMatch[2];
|
|
27
|
-
await relayHyperscapeApi(
|
|
28
|
-
"POST",
|
|
29
|
-
`/api/embedded-agents/${encodeURIComponent(characterId)}/${action}`
|
|
30
|
-
);
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
const messageMatch = pathname.match(
|
|
34
|
-
/^\/api\/apps\/hyperscape\/agents\/([^/]+)\/message$/
|
|
35
|
-
);
|
|
36
|
-
if (messageMatch) {
|
|
37
|
-
const agentId = decodeURIComponent(messageMatch[1]);
|
|
38
|
-
const body = await readJsonBody(req, res);
|
|
39
|
-
if (!body) return true;
|
|
40
|
-
const content = body.content?.trim();
|
|
41
|
-
if (!content) {
|
|
42
|
-
error(res, "content is required");
|
|
43
|
-
return true;
|
|
1
|
+
import { logger } from "@elizaos/core";
|
|
2
|
+
const FETCH_TIMEOUT_MS = 8e3;
|
|
3
|
+
const HYPERSCAPE_WALLET_AUTH_TIMEOUT_MS = 5e3;
|
|
4
|
+
const THOUGHTS_LIMIT = 5;
|
|
5
|
+
const HYPERSCAPE_SESSION_MODE = "spectate-and-steer";
|
|
6
|
+
function normalizeStringSetting(value) {
|
|
7
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
8
|
+
}
|
|
9
|
+
function toAppSessionJsonValue(value, depth = 0) {
|
|
10
|
+
if (depth > 6) return null;
|
|
11
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
return value.map((entry) => toAppSessionJsonValue(entry, depth + 1)).filter((entry) => entry !== void 0);
|
|
16
|
+
}
|
|
17
|
+
if (typeof value === "object") {
|
|
18
|
+
const record = {};
|
|
19
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
20
|
+
const next = toAppSessionJsonValue(entry, depth + 1);
|
|
21
|
+
if (next !== void 0) {
|
|
22
|
+
record[key] = next;
|
|
44
23
|
}
|
|
45
|
-
await relayHyperscapeApi(
|
|
46
|
-
"POST",
|
|
47
|
-
`/api/embedded-agents/${encodeURIComponent(agentId)}/command`,
|
|
48
|
-
{
|
|
49
|
-
rawBodyOverride: JSON.stringify({
|
|
50
|
-
command: "chat",
|
|
51
|
-
data: { message: content }
|
|
52
|
-
}),
|
|
53
|
-
contentTypeOverride: "application/json"
|
|
54
|
-
}
|
|
55
|
-
);
|
|
56
|
-
return true;
|
|
57
24
|
}
|
|
25
|
+
return record;
|
|
26
|
+
}
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
function resolveSettingLike(runtime, key) {
|
|
30
|
+
const fromRuntime = typeof runtime?.getSetting === "function" ? runtime.getSetting(key) : null;
|
|
31
|
+
if (typeof fromRuntime === "string" && fromRuntime.trim().length > 0) {
|
|
32
|
+
return fromRuntime.trim();
|
|
33
|
+
}
|
|
34
|
+
const fromEnv = process.env[key];
|
|
35
|
+
if (typeof fromEnv === "string" && fromEnv.trim().length > 0) {
|
|
36
|
+
return fromEnv.trim();
|
|
37
|
+
}
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
function resolveApiBase(runtime) {
|
|
41
|
+
const rawCandidates = [
|
|
42
|
+
typeof runtime?.getSetting === "function" ? runtime.getSetting("HYPERSCAPE_API_URL") : null,
|
|
43
|
+
process.env.HYPERSCAPE_API_URL,
|
|
44
|
+
typeof runtime?.getSetting === "function" ? runtime.getSetting("HYPERSCAPE_CLIENT_URL") : null,
|
|
45
|
+
process.env.HYPERSCAPE_CLIENT_URL
|
|
46
|
+
];
|
|
47
|
+
for (const raw of rawCandidates) {
|
|
48
|
+
const candidate = normalizeStringSetting(raw);
|
|
49
|
+
if (!candidate) continue;
|
|
50
|
+
try {
|
|
51
|
+
const parsed = new URL(candidate);
|
|
52
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") continue;
|
|
53
|
+
return candidate.replace(/\/+$/, "");
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function resolveAgentId(runtime, viewer) {
|
|
60
|
+
const authMsg = viewer?.authMessage;
|
|
61
|
+
const fromViewer = typeof authMsg?.agentId === "string" ? authMsg.agentId : null;
|
|
62
|
+
const fromRuntime = typeof runtime?.agentId === "string" && runtime.agentId.trim() ? runtime.agentId.trim() : null;
|
|
63
|
+
return fromViewer || fromRuntime;
|
|
64
|
+
}
|
|
65
|
+
function resolveCharacterId(runtime, viewer) {
|
|
66
|
+
const authMsg = viewer?.authMessage;
|
|
67
|
+
const fromViewer = typeof authMsg?.characterId === "string" ? authMsg.characterId : null;
|
|
68
|
+
if (fromViewer) return fromViewer;
|
|
69
|
+
return normalizeStringSetting(
|
|
70
|
+
typeof runtime?.getSetting === "function" ? runtime.getSetting("HYPERSCAPE_CHARACTER_ID") : null
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
function normalizeAbsoluteHttpUrl(raw) {
|
|
74
|
+
if (!raw) return null;
|
|
75
|
+
try {
|
|
76
|
+
const parsed = new URL(raw);
|
|
77
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return raw.replace(/\/+$/, "");
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function persistCredential(runtime, key, value, secret = false) {
|
|
86
|
+
if (!runtime) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
runtime.setSetting?.(key, value, secret);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
logger.error(`[hyperscape] Failed to persist credential "${key}": ${err}`);
|
|
93
|
+
}
|
|
94
|
+
const character = runtime.character;
|
|
95
|
+
if (!character.settings) {
|
|
96
|
+
character.settings = {};
|
|
97
|
+
}
|
|
98
|
+
if (!character.settings.secrets) {
|
|
99
|
+
character.settings.secrets = {};
|
|
100
|
+
}
|
|
101
|
+
character.settings.secrets[key] = value;
|
|
102
|
+
if (!character.secrets) {
|
|
103
|
+
character.secrets = {};
|
|
58
104
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
105
|
+
character.secrets[key] = value;
|
|
106
|
+
}
|
|
107
|
+
function resolveApiBaseUrl(runtime) {
|
|
108
|
+
const configuredUrl = resolveSettingLike(runtime, "HYPERSCAPE_API_URL");
|
|
109
|
+
const normalized = normalizeAbsoluteHttpUrl(configuredUrl);
|
|
110
|
+
if (configuredUrl && !normalized) {
|
|
111
|
+
logger.warn(
|
|
112
|
+
"[hyperscape] Ignoring invalid HYPERSCAPE_API_URL; expected an absolute http/https URL."
|
|
62
113
|
);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
114
|
+
}
|
|
115
|
+
return normalized;
|
|
116
|
+
}
|
|
117
|
+
async function resolveRuntimeEvmAddress(runtime) {
|
|
118
|
+
let agent;
|
|
119
|
+
try {
|
|
120
|
+
if (typeof runtime.getAgent === "function" && runtime.agentId) {
|
|
121
|
+
agent = await runtime.getAgent(runtime.agentId);
|
|
70
122
|
}
|
|
71
|
-
|
|
72
|
-
|
|
123
|
+
} catch {
|
|
124
|
+
agent = null;
|
|
125
|
+
}
|
|
126
|
+
const walletAddresses = agent && typeof agent === "object" ? agent.walletAddresses : void 0;
|
|
127
|
+
const evm = walletAddresses?.evm?.trim();
|
|
128
|
+
if (evm) {
|
|
129
|
+
return evm;
|
|
130
|
+
}
|
|
131
|
+
const existingPk = resolveSettingLike(runtime, "EVM_PRIVATE_KEY")?.trim() || process.env.EVM_PRIVATE_KEY?.trim();
|
|
132
|
+
if (!existingPk) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const walletApiModule = "@elizaos/agent";
|
|
136
|
+
const { deriveEvmAddress } = await import(
|
|
137
|
+
/* webpackIgnore: true */
|
|
138
|
+
walletApiModule
|
|
139
|
+
);
|
|
140
|
+
return deriveEvmAddress(existingPk);
|
|
141
|
+
}
|
|
142
|
+
async function prepareWalletAuthFromRuntime(runtime) {
|
|
143
|
+
if (!runtime) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (resolveSettingLike(runtime, "HYPERSCAPE_AUTH_TOKEN")) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const base = resolveApiBaseUrl(runtime);
|
|
150
|
+
if (!base) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const evm = await resolveRuntimeEvmAddress(runtime);
|
|
154
|
+
if (!evm) {
|
|
155
|
+
logger.info(
|
|
156
|
+
"[hyperscape] Skipping wallet auth: no EVM address or EVM_PRIVATE_KEY is available."
|
|
73
157
|
);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const walletAuthUrl = new URL("/api/agents/wallet-auth", `${base}/`);
|
|
162
|
+
const res = await fetch(walletAuthUrl, {
|
|
163
|
+
method: "POST",
|
|
164
|
+
headers: { "content-type": "application/json" },
|
|
165
|
+
body: JSON.stringify({
|
|
166
|
+
walletAddress: evm,
|
|
167
|
+
walletType: "evm",
|
|
168
|
+
agentName: runtime.character?.name,
|
|
169
|
+
agentId: runtime.agentId
|
|
170
|
+
}),
|
|
171
|
+
signal: AbortSignal.timeout(HYPERSCAPE_WALLET_AUTH_TIMEOUT_MS)
|
|
172
|
+
});
|
|
173
|
+
if (!res.ok) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const data = await res.json();
|
|
177
|
+
if (data.success !== true || typeof data.authToken !== "string") {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
persistCredential(runtime, "HYPERSCAPE_AUTH_TOKEN", data.authToken, true);
|
|
181
|
+
if (typeof data.characterId === "string" && data.characterId.trim()) {
|
|
182
|
+
persistCredential(
|
|
183
|
+
runtime,
|
|
184
|
+
"HYPERSCAPE_CHARACTER_ID",
|
|
185
|
+
data.characterId.trim()
|
|
79
186
|
);
|
|
80
|
-
return true;
|
|
81
187
|
}
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async function fetchJson(url) {
|
|
192
|
+
try {
|
|
193
|
+
const res = await fetch(url, {
|
|
194
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
195
|
+
});
|
|
196
|
+
if (!res.ok) return null;
|
|
197
|
+
return await res.json();
|
|
198
|
+
} catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function fetchLiveData(base, agentId) {
|
|
203
|
+
const id = encodeURIComponent(agentId);
|
|
204
|
+
const [agentsRes, goalRes, quickActionsRes, thoughtsRes] = await Promise.all([
|
|
205
|
+
fetchJson(
|
|
206
|
+
`${base}/api/embedded-agents`
|
|
207
|
+
),
|
|
208
|
+
fetchJson(`${base}/api/agents/${id}/goal`),
|
|
209
|
+
fetchJson(`${base}/api/agents/${id}/quick-actions`),
|
|
210
|
+
fetchJson(
|
|
211
|
+
`${base}/api/agents/${id}/thoughts?limit=${THOUGHTS_LIMIT}`
|
|
212
|
+
)
|
|
213
|
+
]);
|
|
214
|
+
const agents = agentsRes?.agents ?? [];
|
|
215
|
+
const agentRecord = agents.find((a) => a.agentId === agentId) ?? (agents.length === 1 ? agents[0] : null) ?? null;
|
|
216
|
+
return {
|
|
217
|
+
agentRecord,
|
|
218
|
+
goal: goalRes?.goal ?? null,
|
|
219
|
+
goalsPaused: goalRes?.goalsPaused === true,
|
|
220
|
+
availableGoals: goalRes?.availableGoals ?? [],
|
|
221
|
+
quickCommands: quickActionsRes?.quickCommands ?? [],
|
|
222
|
+
nearbyLocations: quickActionsRes?.nearbyLocations ?? [],
|
|
223
|
+
thoughts: thoughtsRes?.thoughts ?? []
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function buildSession(appName, agentId, characterId, data) {
|
|
227
|
+
const {
|
|
228
|
+
agentRecord,
|
|
229
|
+
goal,
|
|
230
|
+
goalsPaused,
|
|
231
|
+
availableGoals,
|
|
232
|
+
quickCommands,
|
|
233
|
+
nearbyLocations,
|
|
234
|
+
thoughts
|
|
235
|
+
} = data;
|
|
236
|
+
const isRunning = agentRecord?.state === "running";
|
|
237
|
+
const controls = isRunning ? ["pause"] : ["resume"];
|
|
238
|
+
const goalLabel = goal?.description ?? null;
|
|
239
|
+
const suggestedPrompts = quickCommands.filter((c) => c.available !== false && typeof c.command === "string").map((c) => c.command);
|
|
240
|
+
const recommendedGoals = availableGoals.map((g, i) => ({
|
|
241
|
+
id: `goal-${i}`,
|
|
242
|
+
type: g.type ?? "general",
|
|
243
|
+
description: g.description ?? "",
|
|
244
|
+
reason: typeof g.reason === "string" ? g.reason : null
|
|
245
|
+
}));
|
|
246
|
+
const recentThoughts = thoughts.slice(0, THOUGHTS_LIMIT).map((t) => ({
|
|
247
|
+
id: t.id,
|
|
248
|
+
type: t.type,
|
|
249
|
+
content: toAppSessionJsonValue(t.content) ?? null,
|
|
250
|
+
timestamp: t.timestamp
|
|
251
|
+
}));
|
|
252
|
+
const telemetry = {
|
|
253
|
+
goalsPaused,
|
|
254
|
+
availableGoalCount: availableGoals.length,
|
|
255
|
+
nearbyLocationCount: nearbyLocations.length
|
|
256
|
+
};
|
|
257
|
+
if (typeof agentRecord?.startedAt === "number") {
|
|
258
|
+
telemetry.startedAt = agentRecord.startedAt;
|
|
259
|
+
}
|
|
260
|
+
if (typeof agentRecord?.lastActivity === "number") {
|
|
261
|
+
telemetry.lastActivity = agentRecord.lastActivity;
|
|
262
|
+
}
|
|
263
|
+
if (recommendedGoals.length > 0) {
|
|
264
|
+
telemetry.recommendedGoals = recommendedGoals;
|
|
265
|
+
}
|
|
266
|
+
if (recentThoughts.length > 0) {
|
|
267
|
+
telemetry.recentThoughts = recentThoughts;
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
sessionId: agentId,
|
|
271
|
+
appName,
|
|
272
|
+
mode: HYPERSCAPE_SESSION_MODE,
|
|
273
|
+
status: isRunning ? "running" : "connecting",
|
|
274
|
+
agentId,
|
|
275
|
+
characterId: characterId ?? void 0,
|
|
276
|
+
followEntity: characterId ?? void 0,
|
|
277
|
+
canSendCommands: true,
|
|
278
|
+
controls,
|
|
279
|
+
summary: isRunning ? null : "Connecting session...",
|
|
280
|
+
goalLabel,
|
|
281
|
+
suggestedPrompts,
|
|
282
|
+
telemetry
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
async function prepareLaunch(ctx) {
|
|
286
|
+
await prepareWalletAuthFromRuntime(ctx.runtime ?? null);
|
|
287
|
+
return {};
|
|
288
|
+
}
|
|
289
|
+
async function resolveViewerAuthMessage(ctx) {
|
|
290
|
+
const runtime = ctx.runtime ?? null;
|
|
291
|
+
const authToken = resolveSettingLike(runtime, "HYPERSCAPE_AUTH_TOKEN");
|
|
292
|
+
if (!authToken) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
const agentId = typeof runtime?.agentId === "string" && runtime.agentId.trim().length > 0 ? runtime.agentId.trim() : void 0;
|
|
296
|
+
if (!agentId) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
const characterId = resolveSettingLike(runtime, "HYPERSCAPE_CHARACTER_ID") ?? agentId;
|
|
300
|
+
return {
|
|
301
|
+
type: "HYPERSCAPE_AUTH",
|
|
302
|
+
authToken,
|
|
303
|
+
agentId,
|
|
304
|
+
characterId,
|
|
305
|
+
followEntity: characterId
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
async function collectLaunchDiagnostics(ctx) {
|
|
309
|
+
if (ctx.viewer?.postMessageAuth && !ctx.viewer.authMessage) {
|
|
310
|
+
return [
|
|
311
|
+
{
|
|
312
|
+
code: "hyperscape-auth-unavailable",
|
|
313
|
+
severity: "error",
|
|
314
|
+
message: "Hyperscape postMessage auth requires HYPERSCAPE_AUTH_TOKEN and a runtime agent id."
|
|
315
|
+
}
|
|
316
|
+
];
|
|
317
|
+
}
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
async function resolveLaunchSession(ctx) {
|
|
321
|
+
const { appName, runtime, viewer } = ctx;
|
|
322
|
+
const base = resolveApiBase(runtime);
|
|
323
|
+
if (!base) {
|
|
324
|
+
logger.debug(
|
|
325
|
+
"[hyperscape] HYPERSCAPE_API_URL not configured; skipping live session resolution"
|
|
326
|
+
);
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
const agentId = resolveAgentId(runtime, viewer);
|
|
330
|
+
if (!agentId) {
|
|
331
|
+
logger.debug(
|
|
332
|
+
"[hyperscape] No agentId available; skipping live session resolution"
|
|
333
|
+
);
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
const characterId = resolveCharacterId(runtime, viewer);
|
|
337
|
+
try {
|
|
338
|
+
const data = await fetchLiveData(base, agentId);
|
|
339
|
+
return buildSession(appName, agentId, characterId, data);
|
|
340
|
+
} catch (err) {
|
|
341
|
+
logger.warn(
|
|
342
|
+
`[hyperscape] Failed to resolve live session: ${err instanceof Error ? err.message : String(err)}`
|
|
343
|
+
);
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async function stopRun() {
|
|
348
|
+
}
|
|
349
|
+
async function refreshRunSession(ctx) {
|
|
350
|
+
const { appName, runtime, viewer, session } = ctx;
|
|
351
|
+
if (!session) return null;
|
|
352
|
+
const base = resolveApiBase(runtime);
|
|
353
|
+
if (!base) return null;
|
|
354
|
+
const agentId = session.agentId ?? resolveAgentId(runtime, viewer);
|
|
355
|
+
if (!agentId) return null;
|
|
356
|
+
const characterId = session.characterId ?? resolveCharacterId(runtime, viewer);
|
|
357
|
+
try {
|
|
358
|
+
const data = await fetchLiveData(base, agentId);
|
|
359
|
+
return buildSession(appName, agentId, characterId, data);
|
|
360
|
+
} catch (err) {
|
|
361
|
+
logger.warn(
|
|
362
|
+
`[hyperscape] Failed to refresh run session: ${err instanceof Error ? err.message : String(err)}`
|
|
363
|
+
);
|
|
364
|
+
return null;
|
|
82
365
|
}
|
|
83
|
-
return false;
|
|
84
366
|
}
|
|
85
367
|
export {
|
|
86
|
-
|
|
368
|
+
collectLaunchDiagnostics,
|
|
369
|
+
prepareLaunch,
|
|
370
|
+
refreshRunSession,
|
|
371
|
+
resolveLaunchSession,
|
|
372
|
+
resolveViewerAuthMessage,
|
|
373
|
+
stopRun
|
|
87
374
|
};
|
|
88
375
|
//# sourceMappingURL=routes.js.map
|