@astrale-os/cli 0.7.0-alpha.0 → 0.8.1-alpha.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/README.md +7 -3
- package/dist/astrale.js +128 -98
- package/package.json +11 -5
- package/src/commands/studio.ts +13 -0
- package/src/lib/__tests__/view-assets.test.ts +114 -0
- package/src/lib/instance.ts +0 -11
- package/src/lib/login-flow.ts +4 -41
- package/src/lib/view/assets.ts +64 -0
- package/src/lib/view/server.ts +4 -37
- package/studio/client/dist/assets/elk-api-DVw5WDTH.js +1 -0
- package/studio/client/dist/assets/elk-worker.min-C9JGDOE-.js +6312 -0
- package/studio/client/dist/assets/index-BQnJ5sgd.css +1 -0
- package/studio/client/dist/assets/index-CTbkDp3z.js +81 -0
- package/studio/client/dist/assets/index-ChOr3yP0.css +1 -0
- package/studio/client/dist/assets/index-Dl709Ra1.js +8 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/server/agent/ask.test.ts +74 -0
- package/studio/server/agent/ask.ts +20 -22
- package/studio/server/agent/bridge/client.test.ts +49 -0
- package/studio/server/agent/bridge/client.ts +37 -0
- package/studio/server/agent/bridge/grant.test.ts +62 -0
- package/studio/server/agent/bridge/grant.ts +84 -0
- package/studio/server/agent/bridge/routes.test.ts +151 -0
- package/studio/server/agent/bridge/routes.ts +148 -0
- package/studio/server/agent/{bridge-mcp.ts → bridge/stdio.ts} +27 -21
- package/studio/server/agent/conversation.test.ts +84 -0
- package/studio/server/agent/conversation.ts +110 -0
- package/studio/server/agent/{types.ts → harness/adapter.ts} +63 -12
- package/studio/server/agent/harness/claude/adapter.test.ts +338 -0
- package/studio/server/agent/harness/claude/adapter.ts +71 -0
- package/studio/server/agent/harness/claude/ask.ts +114 -0
- package/studio/server/agent/harness/claude/capabilities.ts +27 -0
- package/studio/server/agent/harness/claude/command.ts +63 -0
- package/studio/server/agent/harness/claude/events.ts +215 -0
- package/studio/server/agent/harness/claude/loadout.ts +132 -0
- package/studio/server/agent/harness/claude/mcp.test.ts +36 -0
- package/studio/server/agent/harness/claude/mcp.ts +41 -0
- package/studio/server/agent/harness/claude/skills.ts +43 -0
- package/studio/server/agent/harness/codex/adapter.test.ts +269 -0
- package/studio/server/agent/harness/codex/adapter.ts +116 -0
- package/studio/server/agent/harness/codex/ask.test.ts +176 -0
- package/studio/server/agent/harness/codex/ask.ts +181 -0
- package/studio/server/agent/harness/codex/command.test.ts +69 -0
- package/studio/server/agent/harness/codex/command.ts +43 -0
- package/studio/server/agent/harness/codex/events.test.ts +94 -0
- package/studio/server/agent/harness/codex/events.ts +132 -0
- package/studio/server/agent/harness/codex/exec.ts +113 -0
- package/studio/server/agent/harness/codex/loadout.ts +63 -0
- package/studio/server/agent/harness/codex/mcp.test.ts +32 -0
- package/studio/server/agent/harness/codex/mcp.ts +27 -0
- package/studio/server/agent/harness/codex/models.test.ts +167 -0
- package/studio/server/agent/harness/codex/models.ts +211 -0
- package/studio/server/agent/harness/codex/skills.ts +33 -0
- package/studio/server/agent/harness/gateway/config.test.ts +98 -0
- package/studio/server/{state/harness-gateway.ts → agent/harness/gateway/config.ts} +30 -8
- package/studio/server/agent/harness/gateway/token.test.ts +133 -0
- package/studio/server/agent/harness/gateway/token.ts +166 -0
- package/studio/server/agent/harness/mock/adapter.ts +190 -0
- package/studio/server/agent/harness/mock/domain-edit.ts +41 -0
- package/studio/server/agent/harness/process.test.ts +46 -0
- package/studio/server/agent/harness/process.ts +104 -0
- package/studio/server/agent/harness/registry.ts +37 -0
- package/studio/server/agent/harness/selection.test.ts +75 -0
- package/studio/server/agent/harness/selection.ts +77 -0
- package/studio/server/agent/harness/skills.test.ts +95 -0
- package/studio/server/agent/harness/skills.ts +100 -0
- package/studio/server/agent/layout.test.ts +95 -0
- package/studio/server/agent/notify.ts +12 -0
- package/studio/server/agent/{schema-map.ts → prompts/anchors.ts} +2 -2
- package/studio/server/agent/prompts/ask.ts +34 -0
- package/studio/server/agent/{prompt.ts → prompts/system.ts} +3 -107
- package/studio/server/agent/prompts/turn.ts +57 -0
- package/studio/server/agent/routes.test.ts +176 -0
- package/studio/server/agent/routes.ts +214 -0
- package/studio/server/agent/run/completion.ts +193 -0
- package/studio/server/agent/run/coordinator.test.ts +337 -0
- package/studio/server/agent/run/coordinator.ts +104 -0
- package/studio/server/agent/run/live-state.ts +73 -0
- package/studio/server/agent/run/preparation.ts +162 -0
- package/studio/server/agent/run/transcript.test.ts +47 -0
- package/studio/server/agent/run/transcript.ts +30 -0
- package/studio/server/agent/run/usage.test.ts +46 -0
- package/studio/server/{state → agent/run}/usage.ts +4 -4
- package/studio/server/agent/stream.ts +37 -0
- package/studio/server/agent/{session-id.ts → telemetry.ts} +1 -1
- package/studio/server/api-agent-loadout.test.ts +69 -0
- package/studio/server/api.ts +3 -155
- package/studio/server/index.ts +6 -5
- package/studio/server/state/comments.test.ts +29 -0
- package/studio/server/state/comments.ts +3 -3
- package/studio/server/state/settings.test.ts +78 -0
- package/studio/server/state/settings.ts +28 -1
- package/studio/shared/agent-effort.test.ts +12 -0
- package/studio/shared/agent-effort.ts +15 -0
- package/studio/shared/agent-models.test.ts +9 -0
- package/studio/shared/agent-models.ts +14 -0
- package/studio/shared/settings-values.test.ts +18 -0
- package/studio/shared/settings-values.ts +20 -0
- package/studio/shared/types.ts +58 -7
- package/studio/tsconfig.json +23 -0
- package/viewer/tsconfig.json +13 -0
- package/src/connect-core.test.ts +0 -42
- package/src/connect-core.ts +0 -53
- package/studio/client/dist/assets/index-DAC1a9vW.js +0 -109
- package/studio/client/dist/assets/index-huaFafBC.css +0 -1
- package/studio/server/agent/bridge.ts +0 -188
- package/studio/server/agent/claude.ts +0 -678
- package/studio/server/agent/mock.ts +0 -186
- package/studio/server/agent/registry.ts +0 -29
- package/studio/server/agent/runner.ts +0 -487
- package/studio/server/state/harness-token.ts +0 -0
package/README.md
CHANGED
|
@@ -30,9 +30,10 @@ npm install -g @astrale-os/cli
|
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
The npm build bundles all dependencies, so no private-registry access is needed.
|
|
33
|
-
`astrale studio` launches a local web GUI to author and inspect a domain
|
|
34
|
-
|
|
35
|
-
(
|
|
33
|
+
`astrale studio` launches a local web GUI to author and inspect a domain, with
|
|
34
|
+
local Claude Code and Codex agent harnesses (`--harness claude|codex`); it runs on
|
|
35
|
+
[Bun](https://bun.sh), so install Bun and keep it on `PATH` to use that command
|
|
36
|
+
(the rest of the CLI needs only Node ≥ 22). The curl-installed standalone binary
|
|
36
37
|
above does not include the studio — use the npm install for `astrale studio`.
|
|
37
38
|
|
|
38
39
|
> Pre-1.0, `npm install -g @astrale-os/cli` installs the latest published
|
|
@@ -139,6 +140,9 @@ CLI state lives under `~/.astrale/`:
|
|
|
139
140
|
|
|
140
141
|
## Development
|
|
141
142
|
|
|
143
|
+
Contributors use Node.js 24.18.0 and pnpm 11.13.1. The source-worktree runtime
|
|
144
|
+
contract is enforced separately from the published CLI's Node ≥ 22 contract.
|
|
145
|
+
|
|
142
146
|
```bash
|
|
143
147
|
# From the workspace root
|
|
144
148
|
pnpm install
|
package/dist/astrale.js
CHANGED
|
@@ -2576,7 +2576,7 @@ var package_default;
|
|
|
2576
2576
|
var init_package = __esm(() => {
|
|
2577
2577
|
package_default = {
|
|
2578
2578
|
name: "@astrale-os/cli",
|
|
2579
|
-
version: "0.
|
|
2579
|
+
version: "0.8.1-alpha.0",
|
|
2580
2580
|
description: "Astrale CLI — connect to existing Astrale kernels",
|
|
2581
2581
|
keywords: [
|
|
2582
2582
|
"astrale",
|
|
@@ -2606,8 +2606,7 @@ var init_package = __esm(() => {
|
|
|
2606
2606
|
type: "module",
|
|
2607
2607
|
exports: {
|
|
2608
2608
|
"./keys": "./src/lib/keys.ts",
|
|
2609
|
-
"./paths": "./src/lib/paths.ts"
|
|
2610
|
-
"./connect-core": "./src/connect-core.ts"
|
|
2609
|
+
"./paths": "./src/lib/paths.ts"
|
|
2611
2610
|
},
|
|
2612
2611
|
publishConfig: {
|
|
2613
2612
|
access: "public",
|
|
@@ -2618,7 +2617,7 @@ var init_package = __esm(() => {
|
|
|
2618
2617
|
prepare: "husky",
|
|
2619
2618
|
build: "bun scripts/build.ts",
|
|
2620
2619
|
prepack: "bun scripts/build.ts",
|
|
2621
|
-
typecheck: "tsgo --noEmit && tsgo --noEmit -p viewer",
|
|
2620
|
+
typecheck: "tsgo --noEmit && tsgo --noEmit -p viewer && tsgo --noEmit -p studio",
|
|
2622
2621
|
lint: "oxlint .",
|
|
2623
2622
|
"lint:fix": "oxlint --fix .",
|
|
2624
2623
|
format: "pnpm exec oxfmt --write .",
|
|
@@ -2664,10 +2663,17 @@ var init_package = __esm(() => {
|
|
|
2664
2663
|
"oxfmt --write"
|
|
2665
2664
|
]
|
|
2666
2665
|
},
|
|
2666
|
+
devEngines: {
|
|
2667
|
+
runtime: {
|
|
2668
|
+
name: "node",
|
|
2669
|
+
version: ">=24 <25",
|
|
2670
|
+
onFail: "error"
|
|
2671
|
+
}
|
|
2672
|
+
},
|
|
2667
2673
|
engines: {
|
|
2668
|
-
node: ">=
|
|
2674
|
+
node: ">=22"
|
|
2669
2675
|
},
|
|
2670
|
-
packageManager: "pnpm@
|
|
2676
|
+
packageManager: "pnpm@11.13.1"
|
|
2671
2677
|
};
|
|
2672
2678
|
});
|
|
2673
2679
|
|
|
@@ -59080,24 +59086,14 @@ async function obtainToken(idp, opts, scope2) {
|
|
|
59080
59086
|
scope: scope2,
|
|
59081
59087
|
audience: opts.audience
|
|
59082
59088
|
});
|
|
59083
|
-
if (
|
|
59084
|
-
|
|
59085
|
-
|
|
59086
|
-
|
|
59087
|
-
|
|
59088
|
-
|
|
59089
|
-
|
|
59090
|
-
});
|
|
59091
|
-
} else {
|
|
59092
|
-
if (device.verification_uri_complete)
|
|
59093
|
-
log.info(`Open: ${device.verification_uri_complete}`);
|
|
59094
|
-
else if (device.verification_uri)
|
|
59095
|
-
log.info(`Open: ${device.verification_uri}`);
|
|
59096
|
-
if (device.user_code)
|
|
59097
|
-
log.info(`Code: ${device.user_code}`);
|
|
59098
|
-
if (device.message)
|
|
59099
|
-
log.dim(` ${device.message}`);
|
|
59100
|
-
}
|
|
59089
|
+
if (device.verification_uri_complete)
|
|
59090
|
+
log.info(`Open: ${device.verification_uri_complete}`);
|
|
59091
|
+
else if (device.verification_uri)
|
|
59092
|
+
log.info(`Open: ${device.verification_uri}`);
|
|
59093
|
+
if (device.user_code)
|
|
59094
|
+
log.info(`Code: ${device.user_code}`);
|
|
59095
|
+
if (device.message)
|
|
59096
|
+
log.dim(` ${device.message}`);
|
|
59101
59097
|
return pollDeviceToken({
|
|
59102
59098
|
idp,
|
|
59103
59099
|
deviceCode: device.device_code,
|
|
@@ -69909,11 +69905,59 @@ var init_resolve7 = __esm(() => {
|
|
|
69909
69905
|
VIEW_PATH_RE = /^\/:[^\s/:@]+:view\.[a-z][a-z0-9-]*$/;
|
|
69910
69906
|
});
|
|
69911
69907
|
|
|
69912
|
-
// src/lib/view/
|
|
69908
|
+
// src/lib/view/assets.ts
|
|
69913
69909
|
import { existsSync as existsSync5 } from "node:fs";
|
|
69910
|
+
import { copyFile as copyFile2 } from "node:fs/promises";
|
|
69911
|
+
import { dirname as dirname8, join as join12 } from "node:path";
|
|
69912
|
+
import { fileURLToPath } from "node:url";
|
|
69913
|
+
function viewerDistDir(moduleUrl = import.meta.url, entry = process.argv[1] ?? ".") {
|
|
69914
|
+
const override = process.env.ASTRALE_VIEWER_DIR;
|
|
69915
|
+
if (override)
|
|
69916
|
+
return override;
|
|
69917
|
+
const moduleDirectory = dirname8(fileURLToPath(moduleUrl));
|
|
69918
|
+
const published = join12(moduleDirectory, "..", "viewer", "dist");
|
|
69919
|
+
const source = join12(moduleDirectory, "..", "..", "..", "viewer", "dist");
|
|
69920
|
+
const legacy = join12(dirname8(entry), "..", "viewer", "dist");
|
|
69921
|
+
const complete = [published, source, legacy].find(hasViewerBundle);
|
|
69922
|
+
if (complete)
|
|
69923
|
+
return complete;
|
|
69924
|
+
if (hasViewerSource(join12(source, "..")))
|
|
69925
|
+
return source;
|
|
69926
|
+
return published;
|
|
69927
|
+
}
|
|
69928
|
+
async function ensureViewerAssets(moduleUrl = import.meta.url, entry = process.argv[1] ?? ".") {
|
|
69929
|
+
const dist = viewerDistDir(moduleUrl, entry);
|
|
69930
|
+
if (hasViewerBundle(dist))
|
|
69931
|
+
return dist;
|
|
69932
|
+
const srcDir = join12(dist, "..");
|
|
69933
|
+
const bun = globalThis.Bun;
|
|
69934
|
+
if (bun && hasViewerSource(srcDir)) {
|
|
69935
|
+
const result = await bun.build({
|
|
69936
|
+
entrypoints: [join12(srcDir, "main.ts")],
|
|
69937
|
+
outdir: dist,
|
|
69938
|
+
target: "browser",
|
|
69939
|
+
minify: false
|
|
69940
|
+
});
|
|
69941
|
+
if (!result.success)
|
|
69942
|
+
throw new Error(`viewer build failed: ${result.logs.join(`
|
|
69943
|
+
`)}`);
|
|
69944
|
+
await copyFile2(join12(srcDir, "index.html"), join12(dist, "index.html"));
|
|
69945
|
+
return dist;
|
|
69946
|
+
}
|
|
69947
|
+
throw new Error(`viewer bundle missing at ${dist} — reinstall the CLI (or run \`bun scripts/build.ts\` in a dev checkout)`);
|
|
69948
|
+
}
|
|
69949
|
+
function hasViewerBundle(directory) {
|
|
69950
|
+
return existsSync5(join12(directory, "main.js")) && existsSync5(join12(directory, "index.html"));
|
|
69951
|
+
}
|
|
69952
|
+
function hasViewerSource(directory) {
|
|
69953
|
+
return existsSync5(join12(directory, "main.ts")) && existsSync5(join12(directory, "index.html"));
|
|
69954
|
+
}
|
|
69955
|
+
var init_assets = () => {};
|
|
69956
|
+
|
|
69957
|
+
// src/lib/view/server.ts
|
|
69914
69958
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
69915
69959
|
import { createServer } from "node:http";
|
|
69916
|
-
import {
|
|
69960
|
+
import { join as join13 } from "node:path";
|
|
69917
69961
|
import { Readable } from "node:stream";
|
|
69918
69962
|
function startViewServer(config2) {
|
|
69919
69963
|
const { session, proxy } = config2;
|
|
@@ -69971,11 +70015,11 @@ function startViewServer(config2) {
|
|
|
69971
70015
|
return;
|
|
69972
70016
|
}
|
|
69973
70017
|
if (sub === "/" || sub === "/index.html") {
|
|
69974
|
-
await serveAsset(res,
|
|
70018
|
+
await serveAsset(res, join13(hostDir, "index.html"), "text/html; charset=utf-8");
|
|
69975
70019
|
return;
|
|
69976
70020
|
}
|
|
69977
70021
|
if (sub === "/main.js") {
|
|
69978
|
-
await serveAsset(res,
|
|
70022
|
+
await serveAsset(res, join13(hostDir, "main.js"), "text/javascript; charset=utf-8");
|
|
69979
70023
|
return;
|
|
69980
70024
|
}
|
|
69981
70025
|
if (sub === "/config.json" && req.method === "GET") {
|
|
@@ -70060,34 +70104,6 @@ function startViewServer(config2) {
|
|
|
70060
70104
|
server.listen(session.port, "127.0.0.1");
|
|
70061
70105
|
return server;
|
|
70062
70106
|
}
|
|
70063
|
-
function viewerDistDir() {
|
|
70064
|
-
const override = process.env.ASTRALE_VIEWER_DIR;
|
|
70065
|
-
if (override)
|
|
70066
|
-
return override;
|
|
70067
|
-
return join12(dirname8(process.argv[1] ?? "."), "..", "viewer", "dist");
|
|
70068
|
-
}
|
|
70069
|
-
async function ensureViewerAssets() {
|
|
70070
|
-
const dist = viewerDistDir();
|
|
70071
|
-
if (existsSync5(join12(dist, "main.js")) && existsSync5(join12(dist, "index.html")))
|
|
70072
|
-
return dist;
|
|
70073
|
-
const srcDir = join12(dist, "..");
|
|
70074
|
-
const bun = globalThis.Bun;
|
|
70075
|
-
if (bun && existsSync5(join12(srcDir, "main.ts"))) {
|
|
70076
|
-
const result = await bun.build({
|
|
70077
|
-
entrypoints: [join12(srcDir, "main.ts")],
|
|
70078
|
-
outdir: dist,
|
|
70079
|
-
target: "browser",
|
|
70080
|
-
minify: false
|
|
70081
|
-
});
|
|
70082
|
-
if (!result.success)
|
|
70083
|
-
throw new Error(`viewer build failed: ${result.logs.join(`
|
|
70084
|
-
`)}`);
|
|
70085
|
-
const { copyFile: copyFile2 } = await import("node:fs/promises");
|
|
70086
|
-
await copyFile2(join12(srcDir, "index.html"), join12(dist, "index.html"));
|
|
70087
|
-
return dist;
|
|
70088
|
-
}
|
|
70089
|
-
throw new Error(`viewer bundle missing at ${dist} — reinstall the CLI (or run \`bun scripts/build.ts\` in a dev checkout)`);
|
|
70090
|
-
}
|
|
70091
70107
|
function jwtExpiry(token) {
|
|
70092
70108
|
try {
|
|
70093
70109
|
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString("utf8"));
|
|
@@ -70144,7 +70160,9 @@ var TOKEN_REFRESH_MARGIN_MS, FALLBACK_TOKEN_TTL_MS = 3600000, IDLE_SWEEP_MS = 60
|
|
|
70144
70160
|
var init_server = __esm(() => {
|
|
70145
70161
|
init_kernel2();
|
|
70146
70162
|
init_ca_fetch();
|
|
70163
|
+
init_assets();
|
|
70147
70164
|
init_session2();
|
|
70165
|
+
init_assets();
|
|
70148
70166
|
TOKEN_REFRESH_MARGIN_MS = 5 * 60000;
|
|
70149
70167
|
});
|
|
70150
70168
|
|
|
@@ -70225,7 +70243,7 @@ __export(exports_view, {
|
|
|
70225
70243
|
import { randomBytes } from "node:crypto";
|
|
70226
70244
|
import { closeSync, existsSync as existsSync6, openSync, statSync } from "node:fs";
|
|
70227
70245
|
import { mkdir as mkdir10, readdir as readdir3, readFile as readFile12, writeFile as writeFile9 } from "node:fs/promises";
|
|
70228
|
-
import { dirname as dirname9, join as
|
|
70246
|
+
import { dirname as dirname9, join as join14 } from "node:path";
|
|
70229
70247
|
async function resolveSession(spec, opts) {
|
|
70230
70248
|
const parsed = spec ? parseViewSpec(spec) : undefined;
|
|
70231
70249
|
if (parsed?.kind === "target" && opts.target) {
|
|
@@ -70301,7 +70319,7 @@ async function resolveServeRuntime() {
|
|
|
70301
70319
|
if (node2 && entry?.endsWith(".js") && existsSync6(entry))
|
|
70302
70320
|
return { file: node2, args: [entry] };
|
|
70303
70321
|
if (node2 && entry?.endsWith(".ts")) {
|
|
70304
|
-
const dist =
|
|
70322
|
+
const dist = join14(dirname9(entry), "..", "dist", "astrale.js");
|
|
70305
70323
|
await ensureDevDist(entry, dist);
|
|
70306
70324
|
if (existsSync6(dist))
|
|
70307
70325
|
return { file: node2, args: [dist] };
|
|
@@ -70319,7 +70337,7 @@ async function ensureDevDist(entry, dist) {
|
|
|
70319
70337
|
const bun = globalThis.Bun;
|
|
70320
70338
|
if (!bun)
|
|
70321
70339
|
return;
|
|
70322
|
-
const srcDir =
|
|
70340
|
+
const srcDir = join14(dirname9(entry), "..", "src");
|
|
70323
70341
|
if (existsSync6(dist) && !await newerThan(srcDir, statSync(dist).mtimeMs))
|
|
70324
70342
|
return;
|
|
70325
70343
|
console.error("(dev) building dist/astrale.js for the session server…");
|
|
@@ -70335,7 +70353,7 @@ async function newerThan(dir, mtimeMs) {
|
|
|
70335
70353
|
for (const item of entries) {
|
|
70336
70354
|
if (!item.isFile())
|
|
70337
70355
|
continue;
|
|
70338
|
-
if (statSync(
|
|
70356
|
+
if (statSync(join14(item.parentPath, item.name)).mtimeMs > mtimeMs)
|
|
70339
70357
|
return true;
|
|
70340
70358
|
}
|
|
70341
70359
|
return false;
|
|
@@ -70944,28 +70962,28 @@ __export(exports_studio, {
|
|
|
70944
70962
|
default: () => studio_default
|
|
70945
70963
|
});
|
|
70946
70964
|
import { existsSync as existsSync8, realpathSync } from "node:fs";
|
|
70947
|
-
import { dirname as dirname10, join as
|
|
70965
|
+
import { dirname as dirname10, join as join15, resolve } from "node:path";
|
|
70948
70966
|
function resolveStudioDir() {
|
|
70949
70967
|
const candidates = [];
|
|
70950
70968
|
if (process.env.ASTRALE_STUDIO_DIR)
|
|
70951
70969
|
candidates.push(process.env.ASTRALE_STUDIO_DIR);
|
|
70952
70970
|
try {
|
|
70953
70971
|
const entryDir = dirname10(realpathSync(process.argv[1] ?? ""));
|
|
70954
|
-
candidates.push(
|
|
70972
|
+
candidates.push(join15(entryDir, "..", "studio"), join15(entryDir, "studio"));
|
|
70955
70973
|
} catch {}
|
|
70956
70974
|
for (const c of candidates) {
|
|
70957
|
-
if (existsSync8(
|
|
70975
|
+
if (existsSync8(join15(c, "server", "index.ts")))
|
|
70958
70976
|
return resolve(c);
|
|
70959
70977
|
}
|
|
70960
70978
|
throw new Error(`Domain Studio assets not found (looked in: ${candidates.join(", ") || "<none>"}). ` + `Reinstall the astrale CLI, or set ASTRALE_STUDIO_DIR to a studio checkout.`);
|
|
70961
70979
|
}
|
|
70962
70980
|
function isDevSource(studioDir) {
|
|
70963
|
-
return existsSync8(
|
|
70981
|
+
return existsSync8(join15(studioDir, "vite.config.ts")) && existsSync8(join15(studioDir, "client", "src"));
|
|
70964
70982
|
}
|
|
70965
70983
|
function resolveViteBin(studioDir) {
|
|
70966
70984
|
for (const c of [
|
|
70967
|
-
|
|
70968
|
-
|
|
70985
|
+
join15(studioDir, "node_modules", ".bin", "vite"),
|
|
70986
|
+
join15(studioDir, "..", "..", "node_modules", ".bin", "vite")
|
|
70969
70987
|
]) {
|
|
70970
70988
|
if (existsSync8(c))
|
|
70971
70989
|
return c;
|
|
@@ -71040,6 +71058,11 @@ var init_studio = __esm(() => {
|
|
|
71040
71058
|
},
|
|
71041
71059
|
{ flags: "--prod", description: "Serve the prebuilt client (the default)" },
|
|
71042
71060
|
{ flags: "--schema-dir <dir>", description: "Schema entry dir to scan for (default: schema)" },
|
|
71061
|
+
{
|
|
71062
|
+
flags: "--harness <name>",
|
|
71063
|
+
description: "Lock the agent harness for this Studio process",
|
|
71064
|
+
choices: ["claude", "codex"]
|
|
71065
|
+
},
|
|
71043
71066
|
...RAW_OUTPUT_OPTIONS
|
|
71044
71067
|
],
|
|
71045
71068
|
afterHelpText: `
|
|
@@ -71063,6 +71086,10 @@ Behavior:
|
|
|
71063
71086
|
By default the command just PRINTS the URL — it does not pop a browser (that's
|
|
71064
71087
|
invasive when you already have a tab open). Pass --open to launch one.
|
|
71065
71088
|
|
|
71089
|
+
Agent harness: choose Claude Code or Codex per domain in Studio Settings. Pass
|
|
71090
|
+
--harness claude|codex to lock this process to one harness. Each harness uses
|
|
71091
|
+
its existing local authentication and keeps an independent resumable session.
|
|
71092
|
+
|
|
71066
71093
|
The command stays attached and supervises its child processes; Ctrl-C tears
|
|
71067
71094
|
them all down (a second Ctrl-C force-kills). With --json it prints a
|
|
71068
71095
|
{ url, port, mode, workspace } descriptor.
|
|
@@ -71071,6 +71098,7 @@ Examples:
|
|
|
71071
71098
|
$ astrale studio # start the studio + print its URL (no browser)
|
|
71072
71099
|
$ astrale studio --open # …and open it in a browser
|
|
71073
71100
|
$ astrale studio ./my-domain
|
|
71101
|
+
$ astrale studio --harness codex
|
|
71074
71102
|
$ astrale studio --port 4400
|
|
71075
71103
|
$ astrale studio --dev # live-edit the studio itself (from source)
|
|
71076
71104
|
`,
|
|
@@ -71173,12 +71201,13 @@ Examples:
|
|
|
71173
71201
|
DOMAIN_STUDIO_DEV: "1",
|
|
71174
71202
|
VITE_URL: `http://127.0.0.1:${vitePort}`,
|
|
71175
71203
|
PORT: String(studioPort),
|
|
71176
|
-
DOMAIN_STUDIO_HOST: "127.0.0.1"
|
|
71204
|
+
DOMAIN_STUDIO_HOST: "127.0.0.1",
|
|
71205
|
+
...opts.harness ? { DOMAIN_STUDIO_HARNESS: opts.harness } : {}
|
|
71177
71206
|
}
|
|
71178
71207
|
}), "server");
|
|
71179
71208
|
} else {
|
|
71180
|
-
const dist =
|
|
71181
|
-
if (!existsSync8(
|
|
71209
|
+
const dist = join15(studioDir, "client", "dist");
|
|
71210
|
+
if (!existsSync8(join15(dist, "index.html"))) {
|
|
71182
71211
|
throw new Error(`studio client not built at ${dist} — run: pnpm --filter @astrale-os/studio build`);
|
|
71183
71212
|
}
|
|
71184
71213
|
serverChild = supervise(spawnHandle("bun", ["server/index.ts", workspace, "--port", String(studioPort), "--no-open"], {
|
|
@@ -71188,7 +71217,8 @@ Examples:
|
|
|
71188
71217
|
...process.env,
|
|
71189
71218
|
DOMAIN_STUDIO_DIST: dist,
|
|
71190
71219
|
PORT: String(studioPort),
|
|
71191
|
-
DOMAIN_STUDIO_HOST: "127.0.0.1"
|
|
71220
|
+
DOMAIN_STUDIO_HOST: "127.0.0.1",
|
|
71221
|
+
...opts.harness ? { DOMAIN_STUDIO_HARNESS: opts.harness } : {}
|
|
71192
71222
|
}
|
|
71193
71223
|
}), "server");
|
|
71194
71224
|
}
|
|
@@ -73033,21 +73063,21 @@ var init_status4 = __esm(() => {
|
|
|
73033
73063
|
|
|
73034
73064
|
// src/telemetry/store.ts
|
|
73035
73065
|
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
|
|
73036
|
-
import { join as
|
|
73066
|
+
import { join as join16 } from "node:path";
|
|
73037
73067
|
function sessionsRoot() {
|
|
73038
|
-
return
|
|
73068
|
+
return join16(createPaths().home, "sessions");
|
|
73039
73069
|
}
|
|
73040
73070
|
function sessionDir(id) {
|
|
73041
|
-
return
|
|
73071
|
+
return join16(sessionsRoot(), id);
|
|
73042
73072
|
}
|
|
73043
73073
|
function eventsPath(id) {
|
|
73044
|
-
return
|
|
73074
|
+
return join16(sessionDir(id), "events.jsonl");
|
|
73045
73075
|
}
|
|
73046
73076
|
function metaPath(id) {
|
|
73047
|
-
return
|
|
73077
|
+
return join16(sessionDir(id), "meta.json");
|
|
73048
73078
|
}
|
|
73049
73079
|
function markerPath(id) {
|
|
73050
|
-
return
|
|
73080
|
+
return join16(sessionDir(id), ".analyzed");
|
|
73051
73081
|
}
|
|
73052
73082
|
function readJsonSafe(path4) {
|
|
73053
73083
|
try {
|
|
@@ -73125,12 +73155,12 @@ var init_list4 = __esm(() => {
|
|
|
73125
73155
|
// src/telemetry/adapters/claude-code.ts
|
|
73126
73156
|
import { existsSync as existsSync10, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
73127
73157
|
import { homedir as homedir3 } from "node:os";
|
|
73128
|
-
import { join as
|
|
73158
|
+
import { join as join17 } from "node:path";
|
|
73129
73159
|
function mungeCwd(cwd) {
|
|
73130
73160
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
73131
73161
|
}
|
|
73132
|
-
function claudeCodeAdapter(base2 =
|
|
73133
|
-
const projectsDir =
|
|
73162
|
+
function claudeCodeAdapter(base2 = join17(homedir3(), ".claude")) {
|
|
73163
|
+
const projectsDir = join17(base2, "projects");
|
|
73134
73164
|
function detect2() {
|
|
73135
73165
|
try {
|
|
73136
73166
|
return existsSync10(projectsDir);
|
|
@@ -73154,7 +73184,7 @@ function claudeCodeAdapter(base2 = join16(homedir3(), ".claude")) {
|
|
|
73154
73184
|
for (const dir of dirs) {
|
|
73155
73185
|
if (dir !== munged && !dir.startsWith(prefix))
|
|
73156
73186
|
continue;
|
|
73157
|
-
const projectPath =
|
|
73187
|
+
const projectPath = join17(projectsDir, dir);
|
|
73158
73188
|
let files;
|
|
73159
73189
|
try {
|
|
73160
73190
|
files = readdirSync3(projectPath);
|
|
@@ -73164,7 +73194,7 @@ function claudeCodeAdapter(base2 = join16(homedir3(), ".claude")) {
|
|
|
73164
73194
|
for (const file2 of files) {
|
|
73165
73195
|
if (!file2.endsWith(".jsonl"))
|
|
73166
73196
|
continue;
|
|
73167
|
-
const transcriptPath =
|
|
73197
|
+
const transcriptPath = join17(projectPath, file2);
|
|
73168
73198
|
try {
|
|
73169
73199
|
const st = statSync3(transcriptPath);
|
|
73170
73200
|
const mtimeMs = st.mtime.getTime();
|
|
@@ -73200,7 +73230,7 @@ var init_claude_code = __esm(() => {
|
|
|
73200
73230
|
import { existsSync as existsSync11, openSync as openSync2, readdirSync as readdirSync4, readSync, statSync as statSync4 } from "node:fs";
|
|
73201
73231
|
import { closeSync as closeSync2 } from "node:fs";
|
|
73202
73232
|
import { homedir as homedir4 } from "node:os";
|
|
73203
|
-
import { join as
|
|
73233
|
+
import { join as join18 } from "node:path";
|
|
73204
73234
|
function readFirstLine(path4) {
|
|
73205
73235
|
let fd = null;
|
|
73206
73236
|
try {
|
|
@@ -73240,8 +73270,8 @@ function numericDirs(path4) {
|
|
|
73240
73270
|
return [];
|
|
73241
73271
|
}
|
|
73242
73272
|
}
|
|
73243
|
-
function codexAdapter(base2 =
|
|
73244
|
-
const sessionsDir =
|
|
73273
|
+
function codexAdapter(base2 = join18(homedir4(), ".codex")) {
|
|
73274
|
+
const sessionsDir = join18(base2, "sessions");
|
|
73245
73275
|
function detect2() {
|
|
73246
73276
|
try {
|
|
73247
73277
|
return existsSync11(sessionsDir);
|
|
@@ -73256,12 +73286,12 @@ function codexAdapter(base2 = join17(homedir4(), ".codex")) {
|
|
|
73256
73286
|
const endMs = window2.end.getTime();
|
|
73257
73287
|
const lowerMs = startMs - DAY_MS;
|
|
73258
73288
|
for (const yyyy of numericDirs(sessionsDir)) {
|
|
73259
|
-
for (const mm of numericDirs(
|
|
73260
|
-
for (const dd of numericDirs(
|
|
73289
|
+
for (const mm of numericDirs(join18(sessionsDir, yyyy))) {
|
|
73290
|
+
for (const dd of numericDirs(join18(sessionsDir, yyyy, mm))) {
|
|
73261
73291
|
const dayStart = Date.UTC(Number(yyyy), Number(mm) - 1, Number(dd));
|
|
73262
73292
|
if (dayStart > endMs + DAY_MS || dayStart + DAY_MS <= lowerMs)
|
|
73263
73293
|
continue;
|
|
73264
|
-
scanDay(
|
|
73294
|
+
scanDay(join18(sessionsDir, yyyy, mm, dd), root, startMs, endMs, sessions);
|
|
73265
73295
|
}
|
|
73266
73296
|
}
|
|
73267
73297
|
}
|
|
@@ -73283,7 +73313,7 @@ function scanDay(dayPath, root, startMs, endMs, out) {
|
|
|
73283
73313
|
for (const file2 of files) {
|
|
73284
73314
|
if (!file2.startsWith("rollout-") || !file2.endsWith(".jsonl"))
|
|
73285
73315
|
continue;
|
|
73286
|
-
const transcriptPath =
|
|
73316
|
+
const transcriptPath = join18(dayPath, file2);
|
|
73287
73317
|
try {
|
|
73288
73318
|
const st = statSync4(transcriptPath);
|
|
73289
73319
|
const mtimeMs = st.mtime.getTime();
|
|
@@ -73430,7 +73460,7 @@ var init_gate = () => {};
|
|
|
73430
73460
|
// src/telemetry/analyze.ts
|
|
73431
73461
|
import { spawn as spawn3 } from "node:child_process";
|
|
73432
73462
|
import { appendFileSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
73433
|
-
import { join as
|
|
73463
|
+
import { join as join19 } from "node:path";
|
|
73434
73464
|
function writeMarker(id, marker) {
|
|
73435
73465
|
writeFileSync2(markerPath(id), JSON.stringify(marker, null, 2) + `
|
|
73436
73466
|
`);
|
|
@@ -73526,7 +73556,7 @@ async function analyzeSession(id, opts = {}) {
|
|
|
73526
73556
|
const guides = new Map(adapters.map((a) => [a.name, a.readingGuide]));
|
|
73527
73557
|
const prompt = buildPrompt({ id, root, signals: signals2, guides, file: opts.file ?? false });
|
|
73528
73558
|
const dir = sessionDir(id);
|
|
73529
|
-
writeFileSync2(
|
|
73559
|
+
writeFileSync2(join19(dir, "analyzer-prompt.md"), prompt);
|
|
73530
73560
|
const outcome = await runClaude(prompt, dir, opts);
|
|
73531
73561
|
const marker = {
|
|
73532
73562
|
analyzedAt: new Date().toISOString(),
|
|
@@ -73534,7 +73564,7 @@ async function analyzeSession(id, opts = {}) {
|
|
|
73534
73564
|
note: outcome.note
|
|
73535
73565
|
};
|
|
73536
73566
|
writeMarker(id, marker);
|
|
73537
|
-
return { ...marker, reportPath:
|
|
73567
|
+
return { ...marker, reportPath: join19(dir, "report.md") };
|
|
73538
73568
|
}
|
|
73539
73569
|
function runClaude(prompt, cwd, opts) {
|
|
73540
73570
|
return new Promise((resolve2) => {
|
|
@@ -73564,7 +73594,7 @@ function runClaude(prompt, cwd, opts) {
|
|
|
73564
73594
|
child.on("close", (code) => {
|
|
73565
73595
|
clearTimeout(timer);
|
|
73566
73596
|
try {
|
|
73567
|
-
appendFileSync(
|
|
73597
|
+
appendFileSync(join19(cwd, "analyzer.log"), out + (err ? `
|
|
73568
73598
|
--- stderr ---
|
|
73569
73599
|
${err}` : ""));
|
|
73570
73600
|
} catch {}
|
|
@@ -73614,9 +73644,9 @@ var init_settings = __esm(() => {
|
|
|
73614
73644
|
|
|
73615
73645
|
// src/telemetry/trigger.ts
|
|
73616
73646
|
import { existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync6, rmSync as rmSync2, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
73617
|
-
import { join as
|
|
73647
|
+
import { join as join20 } from "node:path";
|
|
73618
73648
|
function lockPath() {
|
|
73619
|
-
return
|
|
73649
|
+
return join20(sessionsRoot(), ".analyzer.lock");
|
|
73620
73650
|
}
|
|
73621
73651
|
function releaseLock() {
|
|
73622
73652
|
try {
|
|
@@ -74375,12 +74405,12 @@ function redactArgv(argv) {
|
|
|
74375
74405
|
init_store2();
|
|
74376
74406
|
import { createHash as createHash2 } from "node:crypto";
|
|
74377
74407
|
import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
74378
|
-
import { dirname as dirname12, join as
|
|
74408
|
+
import { dirname as dirname12, join as join21 } from "node:path";
|
|
74379
74409
|
var MAX_ID_LEN = 64;
|
|
74380
74410
|
function findGitRoot(start) {
|
|
74381
74411
|
let dir = start;
|
|
74382
74412
|
for (;; ) {
|
|
74383
|
-
if (existsSync13(
|
|
74413
|
+
if (existsSync13(join21(dir, ".git")))
|
|
74384
74414
|
return dir;
|
|
74385
74415
|
const parent = dirname12(dir);
|
|
74386
74416
|
if (parent === dir)
|
|
@@ -74481,7 +74511,7 @@ init_settings();
|
|
|
74481
74511
|
init_store2();
|
|
74482
74512
|
import { spawn as spawn4 } from "node:child_process";
|
|
74483
74513
|
import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync7, rmSync as rmSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
74484
|
-
import { join as
|
|
74514
|
+
import { join as join22 } from "node:path";
|
|
74485
74515
|
var LOCK_STALE_MS2 = 30 * 60 * 1000;
|
|
74486
74516
|
var GC_AGE_MS2 = 30 * 24 * 60 * 60 * 1000;
|
|
74487
74517
|
var GC_MAX_PER_RUN = 20;
|
|
@@ -74500,7 +74530,7 @@ function gcOldSessions(sessions) {
|
|
|
74500
74530
|
} catch {}
|
|
74501
74531
|
}
|
|
74502
74532
|
function lockPath2() {
|
|
74503
|
-
return
|
|
74533
|
+
return join22(sessionsRoot(), ".analyzer.lock");
|
|
74504
74534
|
}
|
|
74505
74535
|
function claimLock() {
|
|
74506
74536
|
const path4 = lockPath2();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1-alpha.0",
|
|
4
4
|
"description": "Astrale CLI — connect to existing Astrale kernels",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astrale",
|
|
@@ -30,8 +30,7 @@
|
|
|
30
30
|
"type": "module",
|
|
31
31
|
"exports": {
|
|
32
32
|
"./keys": "./src/lib/keys.ts",
|
|
33
|
-
"./paths": "./src/lib/paths.ts"
|
|
34
|
-
"./connect-core": "./src/connect-core.ts"
|
|
33
|
+
"./paths": "./src/lib/paths.ts"
|
|
35
34
|
},
|
|
36
35
|
"publishConfig": {
|
|
37
36
|
"access": "public",
|
|
@@ -75,13 +74,20 @@
|
|
|
75
74
|
"oxfmt --write"
|
|
76
75
|
]
|
|
77
76
|
},
|
|
77
|
+
"devEngines": {
|
|
78
|
+
"runtime": {
|
|
79
|
+
"name": "node",
|
|
80
|
+
"version": ">=24 <25",
|
|
81
|
+
"onFail": "error"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
78
84
|
"engines": {
|
|
79
|
-
"node": ">=
|
|
85
|
+
"node": ">=22"
|
|
80
86
|
},
|
|
81
87
|
"scripts": {
|
|
82
88
|
"preinstall": "node .check-workspace.cjs",
|
|
83
89
|
"build": "bun scripts/build.ts",
|
|
84
|
-
"typecheck": "tsgo --noEmit && tsgo --noEmit -p viewer",
|
|
90
|
+
"typecheck": "tsgo --noEmit && tsgo --noEmit -p viewer && tsgo --noEmit -p studio",
|
|
85
91
|
"lint": "oxlint .",
|
|
86
92
|
"lint:fix": "oxlint --fix .",
|
|
87
93
|
"format": "pnpm exec oxfmt --write .",
|
package/src/commands/studio.ts
CHANGED
|
@@ -16,6 +16,7 @@ type StudioOpts = RawOutputOpts & {
|
|
|
16
16
|
dev?: boolean
|
|
17
17
|
prod?: boolean
|
|
18
18
|
schemaDir?: string
|
|
19
|
+
harness?: string
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
// Uncommon, safe bands in the IANA Registered range (well below the OS
|
|
@@ -162,6 +163,11 @@ export default {
|
|
|
162
163
|
},
|
|
163
164
|
{ flags: '--prod', description: 'Serve the prebuilt client (the default)' },
|
|
164
165
|
{ flags: '--schema-dir <dir>', description: 'Schema entry dir to scan for (default: schema)' },
|
|
166
|
+
{
|
|
167
|
+
flags: '--harness <name>',
|
|
168
|
+
description: 'Lock the agent harness for this Studio process',
|
|
169
|
+
choices: ['claude', 'codex'],
|
|
170
|
+
},
|
|
165
171
|
...RAW_OUTPUT_OPTIONS,
|
|
166
172
|
],
|
|
167
173
|
afterHelpText: `
|
|
@@ -185,6 +191,10 @@ Behavior:
|
|
|
185
191
|
By default the command just PRINTS the URL — it does not pop a browser (that's
|
|
186
192
|
invasive when you already have a tab open). Pass --open to launch one.
|
|
187
193
|
|
|
194
|
+
Agent harness: choose Claude Code or Codex per domain in Studio Settings. Pass
|
|
195
|
+
--harness claude|codex to lock this process to one harness. Each harness uses
|
|
196
|
+
its existing local authentication and keeps an independent resumable session.
|
|
197
|
+
|
|
188
198
|
The command stays attached and supervises its child processes; Ctrl-C tears
|
|
189
199
|
them all down (a second Ctrl-C force-kills). With --json it prints a
|
|
190
200
|
{ url, port, mode, workspace } descriptor.
|
|
@@ -193,6 +203,7 @@ Examples:
|
|
|
193
203
|
$ astrale studio # start the studio + print its URL (no browser)
|
|
194
204
|
$ astrale studio --open # …and open it in a browser
|
|
195
205
|
$ astrale studio ./my-domain
|
|
206
|
+
$ astrale studio --harness codex
|
|
196
207
|
$ astrale studio --port 4400
|
|
197
208
|
$ astrale studio --dev # live-edit the studio itself (from source)
|
|
198
209
|
`,
|
|
@@ -331,6 +342,7 @@ Examples:
|
|
|
331
342
|
VITE_URL: `http://127.0.0.1:${vitePort}`,
|
|
332
343
|
PORT: String(studioPort),
|
|
333
344
|
DOMAIN_STUDIO_HOST: '127.0.0.1',
|
|
345
|
+
...(opts.harness ? { DOMAIN_STUDIO_HARNESS: opts.harness } : {}),
|
|
334
346
|
},
|
|
335
347
|
},
|
|
336
348
|
),
|
|
@@ -355,6 +367,7 @@ Examples:
|
|
|
355
367
|
DOMAIN_STUDIO_DIST: dist,
|
|
356
368
|
PORT: String(studioPort),
|
|
357
369
|
DOMAIN_STUDIO_HOST: '127.0.0.1',
|
|
370
|
+
...(opts.harness ? { DOMAIN_STUDIO_HARNESS: opts.harness } : {}),
|
|
358
371
|
},
|
|
359
372
|
},
|
|
360
373
|
),
|