Skip to content

Commit d572406

Browse files
committed
feat: add inspector to manager
1 parent 16782e1 commit d572406

Some content is hidden

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

49 files changed

+1210
-556
lines changed

packages/actor-core-cli/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@
3333
"check-types": "tsc --noEmit"
3434
},
3535
"dependencies": {
36+
"@actor-core/nodejs": "workspace:^",
3637
"@sentry/profiling-node": "^9.3.0",
3738
"bundle-require": "^5.1.0",
39+
"chokidar": "^4.0.3",
3840
"esbuild": "^0.25.1",
41+
"open": "^10.1.0",
3942
"yoga-wasm-web": "0.3.3"
4043
},
4144
"devDependencies": {

packages/actor-core-cli/src/cli.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { PACKAGE_JSON } from "./macros" with { type: "macro" };
2-
import { create, deploy, program } from "./mod";
2+
import { create, deploy, dev, program } from "./mod";
33

44
export default program
55
.name(PACKAGE_JSON.name)
66
.version(PACKAGE_JSON.version)
77
.description(PACKAGE_JSON.description)
88
.addCommand(deploy)
99
.addCommand(create)
10+
.addCommand(dev)
1011
.parse();

packages/actor-core-cli/src/commands/deploy.tsx

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import semver from "semver";
88
import which from "which";
99
import { MIN_RIVET_CLI_VERSION } from "../constants";
1010
import { VERSION } from "../macros" with { type: "macro" };
11-
import { isBundleError, isNotFoundError, validateConfig } from "../utils/mod";
1211
import { workflow } from "../workflow";
1312
import { z } from "zod";
1413
import { RivetClient } from "@rivet-gg/api";
@@ -17,6 +16,7 @@ import {
1716
createRivetApi,
1817
getServiceToken,
1918
} from "../utils/rivet-api";
19+
import { validateConfigTask } from "../workflows/validate-config";
2020

2121
export const deploy = new Command()
2222
.name("deploy")
@@ -46,36 +46,7 @@ export const deploy = new Command()
4646
const { config, cli } = yield* ctx.task(
4747
"Prepare",
4848
async function* (ctx) {
49-
const config = yield* ctx.task("Validate config", async () => {
50-
try {
51-
return await validateConfig(cwd);
52-
} catch (error) {
53-
const indexFile = path.relative(
54-
process.cwd(),
55-
path.join(cwd, "src", "index.ts"),
56-
);
57-
if (isBundleError(error)) {
58-
throw ctx.error(
59-
`Could not parse Actors index file (${indexFile})\n${error.details}`,
60-
{
61-
hint: "Please make sure that the file exists and does not have any syntax errors.",
62-
},
63-
);
64-
} else if (isNotFoundError(error)) {
65-
throw ctx.error(
66-
`Could not find Actors index file (${indexFile})`,
67-
{
68-
hint: "Please make sure that the file exists and not empty.",
69-
},
70-
);
71-
} else {
72-
console.error(error);
73-
throw ctx.error("Failed to validate config.", {
74-
hint: "Please check the logs above for more information.",
75-
});
76-
}
77-
}
78-
});
49+
const config = yield* validateConfigTask(ctx, cwd);
7950

8051
const cli = yield* ctx.task(
8152
"Locale rivet-cli",
@@ -92,7 +63,7 @@ export const deploy = new Command()
9263

9364
if (cliLocation) {
9465
// check version
95-
const { stdout } = yield* ctx.$`${cliLocation} --version`;
66+
const { stdout } = yield* exec`${cliLocation} --version`;
9667
const semVersion = semver.coerce(
9768
stdout.split("\n")[2].split(" ")[1].trim(),
9869
);
@@ -152,7 +123,7 @@ export const deploy = new Command()
152123

153124
const envName =
154125
opts.env ??
155-
(yield* ctx.task("Select environment", async function* () {
126+
(yield* ctx.task("Select environment", async function* (ctx) {
156127
const { stdout } = await exec`${cli} env ls --json`;
157128
const envs = JSON.parse(stdout);
158129
return yield* ctx.prompt("Select environment", {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import * as path from "node:path";
2+
import { Argument, Command, Option } from "commander";
3+
import { workflow } from "../workflow";
4+
5+
import { validateConfigTask } from "../workflows/validate-config";
6+
import { serve } from "@actor-core/nodejs";
7+
import chokidar from "chokidar";
8+
import { Text } from "ink";
9+
import open from "open";
10+
import { withResolvers } from "../utils/mod";
11+
12+
export const dev = new Command()
13+
.name("dev")
14+
.description("Run locally your ActorCore project.")
15+
.addArgument(new Argument("[path]", "Location of the project"))
16+
.addOption(
17+
new Option("-p, --port [port]", "Specify which platform to use").default(
18+
"6420",
19+
),
20+
)
21+
.addOption(
22+
new Option("--open", "Open the browser with ActorCore Studio").default(
23+
true,
24+
),
25+
)
26+
.option("--no-open", "Do not open the browser with ActorCore Studio")
27+
.action(action);
28+
29+
export async function action(
30+
cmdPath = ".",
31+
opts: {
32+
port?: string;
33+
open?: boolean;
34+
} = {},
35+
) {
36+
const cwd = path.join(process.cwd(), cmdPath);
37+
await workflow("Run locally your ActorCore project", async function* (ctx) {
38+
let server: ReturnType<typeof serve>;
39+
40+
if (opts.open) {
41+
open(
42+
process.env._ACTOR_CORE_CLI_DEV
43+
? "http://localhost:43708"
44+
: "http://studio.actorcore.org",
45+
);
46+
}
47+
48+
const watcher = chokidar.watch(cwd, {
49+
awaitWriteFinish: true,
50+
ignoreInitial: true,
51+
ignored: (path) => path.includes("node_modules"),
52+
});
53+
54+
let lock: ReturnType<typeof withResolvers> = withResolvers();
55+
56+
watcher.on("all", async (event, path) => {
57+
if (path.includes("node_modules") || path.includes("/.")) return;
58+
59+
server?.close();
60+
if (lock) {
61+
lock.resolve(undefined);
62+
lock = withResolvers();
63+
}
64+
});
65+
66+
while (true) {
67+
const config = yield* validateConfigTask(ctx, cwd);
68+
config.app.config.inspector = {
69+
enabled: true,
70+
};
71+
server = serve(config.app, {
72+
port: Number.parseInt(opts.port || "6420", 10) || 6420,
73+
});
74+
yield* ctx.task(
75+
"Watching for changes...",
76+
async () => {
77+
await lock.promise;
78+
},
79+
{ success: <Text dimColor> (Changes detected, restarting!)</Text> },
80+
);
81+
}
82+
}).render();
83+
}

packages/actor-core-cli/src/mod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "./instrument";
22
export { deploy } from "./commands/deploy";
33
export { create, action as createAction } from "./commands/create";
4+
export { dev } from "./commands/dev";
45
export { program } from "commander";
56
export default {};

packages/actor-core-cli/src/ui/Workflow.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import { ExecaError } from "execa";
1010
import { Box, Text, type TextProps } from "ink";
1111
import Spinner from "ink-spinner";
12-
import { useState } from "react";
12+
import { type ReactNode, useState } from "react";
1313
import stripAnsi from "strip-ansi";
1414
import { type WorkflowAction, WorkflowError } from "../workflow";
1515

@@ -63,22 +63,23 @@ function Tasks({
6363
interactive?: boolean;
6464
}) {
6565
const currentTasks = tasks.filter((task) => task.meta.parent === parent);
66+
6667
if (currentTasks.length === 0) {
6768
return null;
6869
}
6970
return (
7071
<Box flexDirection="column">
7172
{currentTasks.map((task) => (
7273
<Box
73-
key={task.meta.name}
74+
key={task.meta.id}
7475
flexDirection="column"
7576
marginLeft={parent && parentOpts?.showLabel !== false ? 2 : 0}
7677
>
7778
<Task task={task} parent={parent} interactive={interactive} />
78-
{"status" in task && task.status === "done" ? null : (
79+
{"status" in task && task.status === "done" && interactive ? null : (
7980
<Tasks
8081
tasks={tasks}
81-
parent={task.meta.name}
82+
parent={task.meta.id}
8283
parentOpts={task.meta.opts}
8384
interactive={interactive}
8485
/>
@@ -171,7 +172,11 @@ export function Task({
171172
<>
172173
{task.meta.opts?.showLabel === false &&
173174
task.status !== "error" ? null : (
174-
<Status value={task.status} interactive={interactive}>
175+
<Status
176+
value={task.status}
177+
interactive={interactive}
178+
done={task.meta.opts?.success}
179+
>
175180
{task.meta.name}
176181
</Status>
177182
)}
@@ -209,10 +214,12 @@ export function Status({
209214
value,
210215
children,
211216
interactive,
217+
done = <Text dimColor> (Done)</Text>,
212218
...rest
213219
}: TextProps & {
214220
value: WorkflowAction.Progress["status"];
215221
interactive?: boolean;
222+
done?: ReactNode;
216223
}) {
217224
return (
218225
<Text {...rest}>
@@ -229,7 +236,7 @@ export function Status({
229236
</Text>{" "}
230237
{children}
231238
{value === "running" && !interactive ? <Text></Text> : null}
232-
{value === "done" ? <Text dimColor> (Done)</Text> : null}
239+
{value === "done" ? done : null}
233240
</Text>
234241
);
235242
}
@@ -307,7 +314,7 @@ export function Logs({ logs }: { logs: WorkflowAction.Log[] }) {
307314
if (log.type === "warn") {
308315
return (
309316
<Text key={i} color="yellow">
310-
<Text> </Text>
317+
<Text> </Text>
311318
{log.message}
312319
</Text>
313320
);

0 commit comments

Comments
 (0)