summer-engine 1.0.1 → 1.1.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/dist/bin/summer.js
CHANGED
|
@@ -11,6 +11,7 @@ import { installCommand } from "../commands/install.js";
|
|
|
11
11
|
import { createCommand } from "../commands/create.js";
|
|
12
12
|
import { listCommand } from "../commands/list.js";
|
|
13
13
|
import { skillsCommand } from "../commands/skills.js";
|
|
14
|
+
import { orchestratorCommand } from "../commands/orchestrator.js";
|
|
14
15
|
const require = createRequire(import.meta.url);
|
|
15
16
|
const { version } = require("../../package.json");
|
|
16
17
|
const program = new Command();
|
|
@@ -28,6 +29,7 @@ program.addCommand(createCommand);
|
|
|
28
29
|
program.addCommand(listCommand);
|
|
29
30
|
program.addCommand(skillsCommand);
|
|
30
31
|
program.addCommand(mcpCommand);
|
|
32
|
+
program.addCommand(orchestratorCommand);
|
|
31
33
|
program.parseAsync().catch((err) => {
|
|
32
34
|
console.error(err instanceof Error ? err.message : String(err));
|
|
33
35
|
process.exit(1);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: summer agent
|
|
3
|
+
* Launches the web app locally with direct engine execution enabled.
|
|
4
|
+
* The web app handles all AI logic (prompts, tools, RAG, billing).
|
|
5
|
+
* This command just starts it with LOCAL_ENGINE_PORT set.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
export declare const orchestratorCommand: Command;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: summer agent
|
|
3
|
+
* Launches the web app locally with direct engine execution enabled.
|
|
4
|
+
* The web app handles all AI logic (prompts, tools, RAG, billing).
|
|
5
|
+
* This command just starts it with LOCAL_ENGINE_PORT set.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { readFile, access, writeFile } from "node:fs/promises";
|
|
10
|
+
import { join, resolve, dirname } from "node:path";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import { getApiToken, getApiPort, checkEngineHealth } from "../lib/engine.js";
|
|
13
|
+
import { getSummerDir } from "../lib/auth.js";
|
|
14
|
+
async function findWebAppPath() {
|
|
15
|
+
// 1. Check env var
|
|
16
|
+
if (process.env.SUMMER_WEB_APP_PATH) {
|
|
17
|
+
return process.env.SUMMER_WEB_APP_PATH;
|
|
18
|
+
}
|
|
19
|
+
// 2. Check ~/.summer/web-app-path
|
|
20
|
+
try {
|
|
21
|
+
const stored = (await readFile(join(getSummerDir(), "web-app-path"), "utf-8")).trim();
|
|
22
|
+
if (stored) {
|
|
23
|
+
await access(join(stored, "package.json"));
|
|
24
|
+
return stored;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Not stored yet
|
|
29
|
+
}
|
|
30
|
+
// 3. Search common sibling locations
|
|
31
|
+
const cliDir = dirname(dirname(dirname(new URL(import.meta.url).pathname)));
|
|
32
|
+
const searchPaths = [
|
|
33
|
+
resolve(cliDir, "../../publicsummerengine"), // sibling in development/
|
|
34
|
+
resolve(cliDir, "../../../publicsummerengine"), // one level up
|
|
35
|
+
resolve(homedir(), "development/publicsummerengine"),
|
|
36
|
+
];
|
|
37
|
+
for (const candidate of searchPaths) {
|
|
38
|
+
try {
|
|
39
|
+
await access(join(candidate, "package.json"));
|
|
40
|
+
const pkg = JSON.parse(await readFile(join(candidate, "package.json"), "utf-8"));
|
|
41
|
+
if (pkg.name && pkg.name.includes("summer")) {
|
|
42
|
+
// Store for future use
|
|
43
|
+
try {
|
|
44
|
+
await writeFile(join(getSummerDir(), "web-app-path"), candidate, "utf-8");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Non-critical
|
|
48
|
+
}
|
|
49
|
+
return candidate;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
async function loadEnvFile() {
|
|
59
|
+
const envVars = {};
|
|
60
|
+
try {
|
|
61
|
+
const envPath = join(getSummerDir(), "env");
|
|
62
|
+
const content = await readFile(envPath, "utf-8");
|
|
63
|
+
for (const line of content.split("\n")) {
|
|
64
|
+
const trimmed = line.trim();
|
|
65
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
66
|
+
continue;
|
|
67
|
+
const eqIdx = trimmed.indexOf("=");
|
|
68
|
+
if (eqIdx > 0) {
|
|
69
|
+
envVars[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// No env file — user will need to have env vars set
|
|
75
|
+
}
|
|
76
|
+
return envVars;
|
|
77
|
+
}
|
|
78
|
+
export const orchestratorCommand = new Command("agent")
|
|
79
|
+
.description("Start the web app locally with direct engine execution. " +
|
|
80
|
+
"Tools call localhost:6550 instead of the Redis bridge.")
|
|
81
|
+
.option("-p, --port <number>", "Web app port", "3000")
|
|
82
|
+
.option("--web-path <path>", "Path to publicsummerengine (auto-detected if not set)")
|
|
83
|
+
.option("--dev", "Run in dev mode (pnpm dev) instead of production")
|
|
84
|
+
.action(async (opts) => {
|
|
85
|
+
const port = parseInt(opts.port, 10);
|
|
86
|
+
// 1. Verify engine is running
|
|
87
|
+
const enginePort = await getApiPort();
|
|
88
|
+
const engineToken = await getApiToken();
|
|
89
|
+
const health = await checkEngineHealth(enginePort);
|
|
90
|
+
if (!health) {
|
|
91
|
+
console.error("Summer Engine is not running. Open Summer Engine first.");
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
console.log(`Engine running on port ${enginePort} | project: ${health.project_name ?? "unknown"}`);
|
|
95
|
+
// 2. Find web app
|
|
96
|
+
const webAppPath = opts.webPath ?? (await findWebAppPath());
|
|
97
|
+
if (!webAppPath) {
|
|
98
|
+
console.error("Could not find publicsummerengine. Set --web-path or SUMMER_WEB_APP_PATH.");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
console.log(`Web app: ${webAppPath}`);
|
|
102
|
+
// 3. Load env vars from ~/.summer/env
|
|
103
|
+
const storedEnv = await loadEnvFile();
|
|
104
|
+
// 4. Build env for the web app
|
|
105
|
+
const childEnv = {
|
|
106
|
+
...process.env,
|
|
107
|
+
...storedEnv,
|
|
108
|
+
LOCAL_ENGINE_PORT: String(enginePort),
|
|
109
|
+
LOCAL_ENGINE_TOKEN: engineToken ?? "",
|
|
110
|
+
PORT: String(port),
|
|
111
|
+
};
|
|
112
|
+
// 5. Start the web app
|
|
113
|
+
const cmd = opts.dev ? "pnpm" : "pnpm";
|
|
114
|
+
const args = opts.dev ? ["dev", "--port", String(port)] : ["start", "--port", String(port)];
|
|
115
|
+
console.log(`\nStarting web app (${opts.dev ? "dev" : "production"} mode) on port ${port}...`);
|
|
116
|
+
console.log(` LOCAL_ENGINE_PORT=${enginePort}`);
|
|
117
|
+
console.log(` Tools will execute via localhost:${enginePort} (direct, no bridge)\n`);
|
|
118
|
+
const child = spawn(cmd, args, {
|
|
119
|
+
cwd: webAppPath,
|
|
120
|
+
env: childEnv,
|
|
121
|
+
stdio: "inherit",
|
|
122
|
+
});
|
|
123
|
+
// Write agent port for engine to discover
|
|
124
|
+
try {
|
|
125
|
+
await writeFile(join(getSummerDir(), "agent-port"), String(port), "utf-8");
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Non-critical
|
|
129
|
+
}
|
|
130
|
+
child.on("error", (err) => {
|
|
131
|
+
console.error("Failed to start web app:", err.message);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
});
|
|
134
|
+
child.on("exit", (code) => {
|
|
135
|
+
process.exit(code ?? 0);
|
|
136
|
+
});
|
|
137
|
+
// Graceful shutdown
|
|
138
|
+
const shutdown = () => {
|
|
139
|
+
child.kill("SIGTERM");
|
|
140
|
+
};
|
|
141
|
+
process.on("SIGINT", shutdown);
|
|
142
|
+
process.on("SIGTERM", shutdown);
|
|
143
|
+
});
|
package/dist/commands/run.js
CHANGED
|
@@ -8,6 +8,7 @@ const MAC_PATHS = [
|
|
|
8
8
|
`${process.env.HOME}/Applications/Summer.app/Contents/MacOS/Summer`,
|
|
9
9
|
];
|
|
10
10
|
const WIN_PATHS = [
|
|
11
|
+
`${process.env.LOCALAPPDATA}\\SummerEngine\\current\\Summer.exe`,
|
|
11
12
|
`${process.env.LOCALAPPDATA}\\Programs\\Summer Engine\\Summer.exe`,
|
|
12
13
|
`${process.env.PROGRAMFILES}\\Summer Engine\\Summer.exe`,
|
|
13
14
|
];
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -14,4 +14,10 @@ export declare class EngineApiClient {
|
|
|
14
14
|
getScriptErrors(path: string): Promise<unknown>;
|
|
15
15
|
play(scene?: string): Promise<unknown>;
|
|
16
16
|
stop(): Promise<unknown>;
|
|
17
|
+
readFile(path: string, maxBytes?: number): Promise<unknown>;
|
|
18
|
+
getFsTree(root?: string, limit?: number): Promise<unknown>;
|
|
19
|
+
getSelection(): Promise<unknown>;
|
|
20
|
+
viewportSnapshot(): Promise<Buffer>;
|
|
21
|
+
gameSnapshot(): Promise<Buffer>;
|
|
22
|
+
getPort(): number;
|
|
17
23
|
}
|
package/dist/lib/api-client.js
CHANGED
|
@@ -66,4 +66,43 @@ export class EngineApiClient {
|
|
|
66
66
|
async stop() {
|
|
67
67
|
return this.request("POST", "/api/stop");
|
|
68
68
|
}
|
|
69
|
+
async readFile(path, maxBytes) {
|
|
70
|
+
const params = new URLSearchParams({ path });
|
|
71
|
+
if (maxBytes)
|
|
72
|
+
params.set("maxBytes", String(maxBytes));
|
|
73
|
+
return this.request("GET", `/api/state/read-file?${params}`);
|
|
74
|
+
}
|
|
75
|
+
async getFsTree(root = "res://", limit = 2000) {
|
|
76
|
+
const params = new URLSearchParams({
|
|
77
|
+
root,
|
|
78
|
+
limit: String(limit),
|
|
79
|
+
});
|
|
80
|
+
return this.request("GET", `/api/state/fs-tree?${params}`);
|
|
81
|
+
}
|
|
82
|
+
async getSelection() {
|
|
83
|
+
return this.request("GET", "/api/state/selection");
|
|
84
|
+
}
|
|
85
|
+
async viewportSnapshot() {
|
|
86
|
+
const url = `http://127.0.0.1:${this.port}/api/snapshot/viewport`;
|
|
87
|
+
const res = await fetch(url, {
|
|
88
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
89
|
+
signal: AbortSignal.timeout(15000),
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok)
|
|
92
|
+
throw new Error(`Viewport snapshot failed: ${res.status}`);
|
|
93
|
+
return Buffer.from(await res.arrayBuffer());
|
|
94
|
+
}
|
|
95
|
+
async gameSnapshot() {
|
|
96
|
+
const url = `http://127.0.0.1:${this.port}/api/snapshot/game`;
|
|
97
|
+
const res = await fetch(url, {
|
|
98
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
99
|
+
signal: AbortSignal.timeout(15000),
|
|
100
|
+
});
|
|
101
|
+
if (!res.ok)
|
|
102
|
+
throw new Error(`Game snapshot failed: ${res.status}`);
|
|
103
|
+
return Buffer.from(await res.arrayBuffer());
|
|
104
|
+
}
|
|
105
|
+
getPort() {
|
|
106
|
+
return this.port;
|
|
107
|
+
}
|
|
69
108
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "summer-engine",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "CLI and MCP tools for Summer Engine — the AI-native game engine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"game-engine",
|
|
@@ -37,12 +37,16 @@
|
|
|
37
37
|
"node": ">=18"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
+
"@ai-sdk/anthropic": "^3.0.0",
|
|
40
41
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
42
|
+
"ai": "^6.0.0",
|
|
41
43
|
"commander": "^12.0.0",
|
|
44
|
+
"diff": "^7.0.0",
|
|
42
45
|
"open": "^10.0.0",
|
|
43
46
|
"zod": "^3.23.0"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
49
|
+
"@types/diff": "^7.0.0",
|
|
46
50
|
"@types/node": "^20.0.0",
|
|
47
51
|
"typescript": "^5.5.0"
|
|
48
52
|
}
|