@astrale-os/cli 0.7.0-alpha.0 → 0.8.0-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 +3 -2
- package/dist/astrale.js +118 -95
- package/package.json +2 -3
- 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/index-BaqEuIQJ.js +109 -0
- package/studio/client/dist/assets/index-jRdExahh.css +1 -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/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,8 +30,9 @@ 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
|
-
|
|
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
|
|
35
36
|
(the rest of the CLI needs only Node ≥ 20). The curl-installed standalone binary
|
|
36
37
|
above does not include the studio — use the npm install for `astrale studio`.
|
|
37
38
|
|
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.0-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",
|
|
@@ -59080,24 +59079,14 @@ async function obtainToken(idp, opts, scope2) {
|
|
|
59080
59079
|
scope: scope2,
|
|
59081
59080
|
audience: opts.audience
|
|
59082
59081
|
});
|
|
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
|
-
}
|
|
59082
|
+
if (device.verification_uri_complete)
|
|
59083
|
+
log.info(`Open: ${device.verification_uri_complete}`);
|
|
59084
|
+
else if (device.verification_uri)
|
|
59085
|
+
log.info(`Open: ${device.verification_uri}`);
|
|
59086
|
+
if (device.user_code)
|
|
59087
|
+
log.info(`Code: ${device.user_code}`);
|
|
59088
|
+
if (device.message)
|
|
59089
|
+
log.dim(` ${device.message}`);
|
|
59101
59090
|
return pollDeviceToken({
|
|
59102
59091
|
idp,
|
|
59103
59092
|
deviceCode: device.device_code,
|
|
@@ -69909,11 +69898,59 @@ var init_resolve7 = __esm(() => {
|
|
|
69909
69898
|
VIEW_PATH_RE = /^\/:[^\s/:@]+:view\.[a-z][a-z0-9-]*$/;
|
|
69910
69899
|
});
|
|
69911
69900
|
|
|
69912
|
-
// src/lib/view/
|
|
69901
|
+
// src/lib/view/assets.ts
|
|
69913
69902
|
import { existsSync as existsSync5 } from "node:fs";
|
|
69903
|
+
import { copyFile as copyFile2 } from "node:fs/promises";
|
|
69904
|
+
import { dirname as dirname8, join as join12 } from "node:path";
|
|
69905
|
+
import { fileURLToPath } from "node:url";
|
|
69906
|
+
function viewerDistDir(moduleUrl = import.meta.url, entry = process.argv[1] ?? ".") {
|
|
69907
|
+
const override = process.env.ASTRALE_VIEWER_DIR;
|
|
69908
|
+
if (override)
|
|
69909
|
+
return override;
|
|
69910
|
+
const moduleDirectory = dirname8(fileURLToPath(moduleUrl));
|
|
69911
|
+
const published = join12(moduleDirectory, "..", "viewer", "dist");
|
|
69912
|
+
const source = join12(moduleDirectory, "..", "..", "..", "viewer", "dist");
|
|
69913
|
+
const legacy = join12(dirname8(entry), "..", "viewer", "dist");
|
|
69914
|
+
const complete = [published, source, legacy].find(hasViewerBundle);
|
|
69915
|
+
if (complete)
|
|
69916
|
+
return complete;
|
|
69917
|
+
if (hasViewerSource(join12(source, "..")))
|
|
69918
|
+
return source;
|
|
69919
|
+
return published;
|
|
69920
|
+
}
|
|
69921
|
+
async function ensureViewerAssets(moduleUrl = import.meta.url, entry = process.argv[1] ?? ".") {
|
|
69922
|
+
const dist = viewerDistDir(moduleUrl, entry);
|
|
69923
|
+
if (hasViewerBundle(dist))
|
|
69924
|
+
return dist;
|
|
69925
|
+
const srcDir = join12(dist, "..");
|
|
69926
|
+
const bun = globalThis.Bun;
|
|
69927
|
+
if (bun && hasViewerSource(srcDir)) {
|
|
69928
|
+
const result = await bun.build({
|
|
69929
|
+
entrypoints: [join12(srcDir, "main.ts")],
|
|
69930
|
+
outdir: dist,
|
|
69931
|
+
target: "browser",
|
|
69932
|
+
minify: false
|
|
69933
|
+
});
|
|
69934
|
+
if (!result.success)
|
|
69935
|
+
throw new Error(`viewer build failed: ${result.logs.join(`
|
|
69936
|
+
`)}`);
|
|
69937
|
+
await copyFile2(join12(srcDir, "index.html"), join12(dist, "index.html"));
|
|
69938
|
+
return dist;
|
|
69939
|
+
}
|
|
69940
|
+
throw new Error(`viewer bundle missing at ${dist} — reinstall the CLI (or run \`bun scripts/build.ts\` in a dev checkout)`);
|
|
69941
|
+
}
|
|
69942
|
+
function hasViewerBundle(directory) {
|
|
69943
|
+
return existsSync5(join12(directory, "main.js")) && existsSync5(join12(directory, "index.html"));
|
|
69944
|
+
}
|
|
69945
|
+
function hasViewerSource(directory) {
|
|
69946
|
+
return existsSync5(join12(directory, "main.ts")) && existsSync5(join12(directory, "index.html"));
|
|
69947
|
+
}
|
|
69948
|
+
var init_assets = () => {};
|
|
69949
|
+
|
|
69950
|
+
// src/lib/view/server.ts
|
|
69914
69951
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
69915
69952
|
import { createServer } from "node:http";
|
|
69916
|
-
import {
|
|
69953
|
+
import { join as join13 } from "node:path";
|
|
69917
69954
|
import { Readable } from "node:stream";
|
|
69918
69955
|
function startViewServer(config2) {
|
|
69919
69956
|
const { session, proxy } = config2;
|
|
@@ -69971,11 +70008,11 @@ function startViewServer(config2) {
|
|
|
69971
70008
|
return;
|
|
69972
70009
|
}
|
|
69973
70010
|
if (sub === "/" || sub === "/index.html") {
|
|
69974
|
-
await serveAsset(res,
|
|
70011
|
+
await serveAsset(res, join13(hostDir, "index.html"), "text/html; charset=utf-8");
|
|
69975
70012
|
return;
|
|
69976
70013
|
}
|
|
69977
70014
|
if (sub === "/main.js") {
|
|
69978
|
-
await serveAsset(res,
|
|
70015
|
+
await serveAsset(res, join13(hostDir, "main.js"), "text/javascript; charset=utf-8");
|
|
69979
70016
|
return;
|
|
69980
70017
|
}
|
|
69981
70018
|
if (sub === "/config.json" && req.method === "GET") {
|
|
@@ -70060,34 +70097,6 @@ function startViewServer(config2) {
|
|
|
70060
70097
|
server.listen(session.port, "127.0.0.1");
|
|
70061
70098
|
return server;
|
|
70062
70099
|
}
|
|
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
70100
|
function jwtExpiry(token) {
|
|
70092
70101
|
try {
|
|
70093
70102
|
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString("utf8"));
|
|
@@ -70144,7 +70153,9 @@ var TOKEN_REFRESH_MARGIN_MS, FALLBACK_TOKEN_TTL_MS = 3600000, IDLE_SWEEP_MS = 60
|
|
|
70144
70153
|
var init_server = __esm(() => {
|
|
70145
70154
|
init_kernel2();
|
|
70146
70155
|
init_ca_fetch();
|
|
70156
|
+
init_assets();
|
|
70147
70157
|
init_session2();
|
|
70158
|
+
init_assets();
|
|
70148
70159
|
TOKEN_REFRESH_MARGIN_MS = 5 * 60000;
|
|
70149
70160
|
});
|
|
70150
70161
|
|
|
@@ -70225,7 +70236,7 @@ __export(exports_view, {
|
|
|
70225
70236
|
import { randomBytes } from "node:crypto";
|
|
70226
70237
|
import { closeSync, existsSync as existsSync6, openSync, statSync } from "node:fs";
|
|
70227
70238
|
import { mkdir as mkdir10, readdir as readdir3, readFile as readFile12, writeFile as writeFile9 } from "node:fs/promises";
|
|
70228
|
-
import { dirname as dirname9, join as
|
|
70239
|
+
import { dirname as dirname9, join as join14 } from "node:path";
|
|
70229
70240
|
async function resolveSession(spec, opts) {
|
|
70230
70241
|
const parsed = spec ? parseViewSpec(spec) : undefined;
|
|
70231
70242
|
if (parsed?.kind === "target" && opts.target) {
|
|
@@ -70301,7 +70312,7 @@ async function resolveServeRuntime() {
|
|
|
70301
70312
|
if (node2 && entry?.endsWith(".js") && existsSync6(entry))
|
|
70302
70313
|
return { file: node2, args: [entry] };
|
|
70303
70314
|
if (node2 && entry?.endsWith(".ts")) {
|
|
70304
|
-
const dist =
|
|
70315
|
+
const dist = join14(dirname9(entry), "..", "dist", "astrale.js");
|
|
70305
70316
|
await ensureDevDist(entry, dist);
|
|
70306
70317
|
if (existsSync6(dist))
|
|
70307
70318
|
return { file: node2, args: [dist] };
|
|
@@ -70319,7 +70330,7 @@ async function ensureDevDist(entry, dist) {
|
|
|
70319
70330
|
const bun = globalThis.Bun;
|
|
70320
70331
|
if (!bun)
|
|
70321
70332
|
return;
|
|
70322
|
-
const srcDir =
|
|
70333
|
+
const srcDir = join14(dirname9(entry), "..", "src");
|
|
70323
70334
|
if (existsSync6(dist) && !await newerThan(srcDir, statSync(dist).mtimeMs))
|
|
70324
70335
|
return;
|
|
70325
70336
|
console.error("(dev) building dist/astrale.js for the session server…");
|
|
@@ -70335,7 +70346,7 @@ async function newerThan(dir, mtimeMs) {
|
|
|
70335
70346
|
for (const item of entries) {
|
|
70336
70347
|
if (!item.isFile())
|
|
70337
70348
|
continue;
|
|
70338
|
-
if (statSync(
|
|
70349
|
+
if (statSync(join14(item.parentPath, item.name)).mtimeMs > mtimeMs)
|
|
70339
70350
|
return true;
|
|
70340
70351
|
}
|
|
70341
70352
|
return false;
|
|
@@ -70944,28 +70955,28 @@ __export(exports_studio, {
|
|
|
70944
70955
|
default: () => studio_default
|
|
70945
70956
|
});
|
|
70946
70957
|
import { existsSync as existsSync8, realpathSync } from "node:fs";
|
|
70947
|
-
import { dirname as dirname10, join as
|
|
70958
|
+
import { dirname as dirname10, join as join15, resolve } from "node:path";
|
|
70948
70959
|
function resolveStudioDir() {
|
|
70949
70960
|
const candidates = [];
|
|
70950
70961
|
if (process.env.ASTRALE_STUDIO_DIR)
|
|
70951
70962
|
candidates.push(process.env.ASTRALE_STUDIO_DIR);
|
|
70952
70963
|
try {
|
|
70953
70964
|
const entryDir = dirname10(realpathSync(process.argv[1] ?? ""));
|
|
70954
|
-
candidates.push(
|
|
70965
|
+
candidates.push(join15(entryDir, "..", "studio"), join15(entryDir, "studio"));
|
|
70955
70966
|
} catch {}
|
|
70956
70967
|
for (const c of candidates) {
|
|
70957
|
-
if (existsSync8(
|
|
70968
|
+
if (existsSync8(join15(c, "server", "index.ts")))
|
|
70958
70969
|
return resolve(c);
|
|
70959
70970
|
}
|
|
70960
70971
|
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
70972
|
}
|
|
70962
70973
|
function isDevSource(studioDir) {
|
|
70963
|
-
return existsSync8(
|
|
70974
|
+
return existsSync8(join15(studioDir, "vite.config.ts")) && existsSync8(join15(studioDir, "client", "src"));
|
|
70964
70975
|
}
|
|
70965
70976
|
function resolveViteBin(studioDir) {
|
|
70966
70977
|
for (const c of [
|
|
70967
|
-
|
|
70968
|
-
|
|
70978
|
+
join15(studioDir, "node_modules", ".bin", "vite"),
|
|
70979
|
+
join15(studioDir, "..", "..", "node_modules", ".bin", "vite")
|
|
70969
70980
|
]) {
|
|
70970
70981
|
if (existsSync8(c))
|
|
70971
70982
|
return c;
|
|
@@ -71040,6 +71051,11 @@ var init_studio = __esm(() => {
|
|
|
71040
71051
|
},
|
|
71041
71052
|
{ flags: "--prod", description: "Serve the prebuilt client (the default)" },
|
|
71042
71053
|
{ flags: "--schema-dir <dir>", description: "Schema entry dir to scan for (default: schema)" },
|
|
71054
|
+
{
|
|
71055
|
+
flags: "--harness <name>",
|
|
71056
|
+
description: "Lock the agent harness for this Studio process",
|
|
71057
|
+
choices: ["claude", "codex"]
|
|
71058
|
+
},
|
|
71043
71059
|
...RAW_OUTPUT_OPTIONS
|
|
71044
71060
|
],
|
|
71045
71061
|
afterHelpText: `
|
|
@@ -71063,6 +71079,10 @@ Behavior:
|
|
|
71063
71079
|
By default the command just PRINTS the URL — it does not pop a browser (that's
|
|
71064
71080
|
invasive when you already have a tab open). Pass --open to launch one.
|
|
71065
71081
|
|
|
71082
|
+
Agent harness: choose Claude Code or Codex per domain in Studio Settings. Pass
|
|
71083
|
+
--harness claude|codex to lock this process to one harness. Each harness uses
|
|
71084
|
+
its existing local authentication and keeps an independent resumable session.
|
|
71085
|
+
|
|
71066
71086
|
The command stays attached and supervises its child processes; Ctrl-C tears
|
|
71067
71087
|
them all down (a second Ctrl-C force-kills). With --json it prints a
|
|
71068
71088
|
{ url, port, mode, workspace } descriptor.
|
|
@@ -71071,6 +71091,7 @@ Examples:
|
|
|
71071
71091
|
$ astrale studio # start the studio + print its URL (no browser)
|
|
71072
71092
|
$ astrale studio --open # …and open it in a browser
|
|
71073
71093
|
$ astrale studio ./my-domain
|
|
71094
|
+
$ astrale studio --harness codex
|
|
71074
71095
|
$ astrale studio --port 4400
|
|
71075
71096
|
$ astrale studio --dev # live-edit the studio itself (from source)
|
|
71076
71097
|
`,
|
|
@@ -71173,12 +71194,13 @@ Examples:
|
|
|
71173
71194
|
DOMAIN_STUDIO_DEV: "1",
|
|
71174
71195
|
VITE_URL: `http://127.0.0.1:${vitePort}`,
|
|
71175
71196
|
PORT: String(studioPort),
|
|
71176
|
-
DOMAIN_STUDIO_HOST: "127.0.0.1"
|
|
71197
|
+
DOMAIN_STUDIO_HOST: "127.0.0.1",
|
|
71198
|
+
...opts.harness ? { DOMAIN_STUDIO_HARNESS: opts.harness } : {}
|
|
71177
71199
|
}
|
|
71178
71200
|
}), "server");
|
|
71179
71201
|
} else {
|
|
71180
|
-
const dist =
|
|
71181
|
-
if (!existsSync8(
|
|
71202
|
+
const dist = join15(studioDir, "client", "dist");
|
|
71203
|
+
if (!existsSync8(join15(dist, "index.html"))) {
|
|
71182
71204
|
throw new Error(`studio client not built at ${dist} — run: pnpm --filter @astrale-os/studio build`);
|
|
71183
71205
|
}
|
|
71184
71206
|
serverChild = supervise(spawnHandle("bun", ["server/index.ts", workspace, "--port", String(studioPort), "--no-open"], {
|
|
@@ -71188,7 +71210,8 @@ Examples:
|
|
|
71188
71210
|
...process.env,
|
|
71189
71211
|
DOMAIN_STUDIO_DIST: dist,
|
|
71190
71212
|
PORT: String(studioPort),
|
|
71191
|
-
DOMAIN_STUDIO_HOST: "127.0.0.1"
|
|
71213
|
+
DOMAIN_STUDIO_HOST: "127.0.0.1",
|
|
71214
|
+
...opts.harness ? { DOMAIN_STUDIO_HARNESS: opts.harness } : {}
|
|
71192
71215
|
}
|
|
71193
71216
|
}), "server");
|
|
71194
71217
|
}
|
|
@@ -73033,21 +73056,21 @@ var init_status4 = __esm(() => {
|
|
|
73033
73056
|
|
|
73034
73057
|
// src/telemetry/store.ts
|
|
73035
73058
|
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
|
|
73036
|
-
import { join as
|
|
73059
|
+
import { join as join16 } from "node:path";
|
|
73037
73060
|
function sessionsRoot() {
|
|
73038
|
-
return
|
|
73061
|
+
return join16(createPaths().home, "sessions");
|
|
73039
73062
|
}
|
|
73040
73063
|
function sessionDir(id) {
|
|
73041
|
-
return
|
|
73064
|
+
return join16(sessionsRoot(), id);
|
|
73042
73065
|
}
|
|
73043
73066
|
function eventsPath(id) {
|
|
73044
|
-
return
|
|
73067
|
+
return join16(sessionDir(id), "events.jsonl");
|
|
73045
73068
|
}
|
|
73046
73069
|
function metaPath(id) {
|
|
73047
|
-
return
|
|
73070
|
+
return join16(sessionDir(id), "meta.json");
|
|
73048
73071
|
}
|
|
73049
73072
|
function markerPath(id) {
|
|
73050
|
-
return
|
|
73073
|
+
return join16(sessionDir(id), ".analyzed");
|
|
73051
73074
|
}
|
|
73052
73075
|
function readJsonSafe(path4) {
|
|
73053
73076
|
try {
|
|
@@ -73125,12 +73148,12 @@ var init_list4 = __esm(() => {
|
|
|
73125
73148
|
// src/telemetry/adapters/claude-code.ts
|
|
73126
73149
|
import { existsSync as existsSync10, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
73127
73150
|
import { homedir as homedir3 } from "node:os";
|
|
73128
|
-
import { join as
|
|
73151
|
+
import { join as join17 } from "node:path";
|
|
73129
73152
|
function mungeCwd(cwd) {
|
|
73130
73153
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
73131
73154
|
}
|
|
73132
|
-
function claudeCodeAdapter(base2 =
|
|
73133
|
-
const projectsDir =
|
|
73155
|
+
function claudeCodeAdapter(base2 = join17(homedir3(), ".claude")) {
|
|
73156
|
+
const projectsDir = join17(base2, "projects");
|
|
73134
73157
|
function detect2() {
|
|
73135
73158
|
try {
|
|
73136
73159
|
return existsSync10(projectsDir);
|
|
@@ -73154,7 +73177,7 @@ function claudeCodeAdapter(base2 = join16(homedir3(), ".claude")) {
|
|
|
73154
73177
|
for (const dir of dirs) {
|
|
73155
73178
|
if (dir !== munged && !dir.startsWith(prefix))
|
|
73156
73179
|
continue;
|
|
73157
|
-
const projectPath =
|
|
73180
|
+
const projectPath = join17(projectsDir, dir);
|
|
73158
73181
|
let files;
|
|
73159
73182
|
try {
|
|
73160
73183
|
files = readdirSync3(projectPath);
|
|
@@ -73164,7 +73187,7 @@ function claudeCodeAdapter(base2 = join16(homedir3(), ".claude")) {
|
|
|
73164
73187
|
for (const file2 of files) {
|
|
73165
73188
|
if (!file2.endsWith(".jsonl"))
|
|
73166
73189
|
continue;
|
|
73167
|
-
const transcriptPath =
|
|
73190
|
+
const transcriptPath = join17(projectPath, file2);
|
|
73168
73191
|
try {
|
|
73169
73192
|
const st = statSync3(transcriptPath);
|
|
73170
73193
|
const mtimeMs = st.mtime.getTime();
|
|
@@ -73200,7 +73223,7 @@ var init_claude_code = __esm(() => {
|
|
|
73200
73223
|
import { existsSync as existsSync11, openSync as openSync2, readdirSync as readdirSync4, readSync, statSync as statSync4 } from "node:fs";
|
|
73201
73224
|
import { closeSync as closeSync2 } from "node:fs";
|
|
73202
73225
|
import { homedir as homedir4 } from "node:os";
|
|
73203
|
-
import { join as
|
|
73226
|
+
import { join as join18 } from "node:path";
|
|
73204
73227
|
function readFirstLine(path4) {
|
|
73205
73228
|
let fd = null;
|
|
73206
73229
|
try {
|
|
@@ -73240,8 +73263,8 @@ function numericDirs(path4) {
|
|
|
73240
73263
|
return [];
|
|
73241
73264
|
}
|
|
73242
73265
|
}
|
|
73243
|
-
function codexAdapter(base2 =
|
|
73244
|
-
const sessionsDir =
|
|
73266
|
+
function codexAdapter(base2 = join18(homedir4(), ".codex")) {
|
|
73267
|
+
const sessionsDir = join18(base2, "sessions");
|
|
73245
73268
|
function detect2() {
|
|
73246
73269
|
try {
|
|
73247
73270
|
return existsSync11(sessionsDir);
|
|
@@ -73256,12 +73279,12 @@ function codexAdapter(base2 = join17(homedir4(), ".codex")) {
|
|
|
73256
73279
|
const endMs = window2.end.getTime();
|
|
73257
73280
|
const lowerMs = startMs - DAY_MS;
|
|
73258
73281
|
for (const yyyy of numericDirs(sessionsDir)) {
|
|
73259
|
-
for (const mm of numericDirs(
|
|
73260
|
-
for (const dd of numericDirs(
|
|
73282
|
+
for (const mm of numericDirs(join18(sessionsDir, yyyy))) {
|
|
73283
|
+
for (const dd of numericDirs(join18(sessionsDir, yyyy, mm))) {
|
|
73261
73284
|
const dayStart = Date.UTC(Number(yyyy), Number(mm) - 1, Number(dd));
|
|
73262
73285
|
if (dayStart > endMs + DAY_MS || dayStart + DAY_MS <= lowerMs)
|
|
73263
73286
|
continue;
|
|
73264
|
-
scanDay(
|
|
73287
|
+
scanDay(join18(sessionsDir, yyyy, mm, dd), root, startMs, endMs, sessions);
|
|
73265
73288
|
}
|
|
73266
73289
|
}
|
|
73267
73290
|
}
|
|
@@ -73283,7 +73306,7 @@ function scanDay(dayPath, root, startMs, endMs, out) {
|
|
|
73283
73306
|
for (const file2 of files) {
|
|
73284
73307
|
if (!file2.startsWith("rollout-") || !file2.endsWith(".jsonl"))
|
|
73285
73308
|
continue;
|
|
73286
|
-
const transcriptPath =
|
|
73309
|
+
const transcriptPath = join18(dayPath, file2);
|
|
73287
73310
|
try {
|
|
73288
73311
|
const st = statSync4(transcriptPath);
|
|
73289
73312
|
const mtimeMs = st.mtime.getTime();
|
|
@@ -73430,7 +73453,7 @@ var init_gate = () => {};
|
|
|
73430
73453
|
// src/telemetry/analyze.ts
|
|
73431
73454
|
import { spawn as spawn3 } from "node:child_process";
|
|
73432
73455
|
import { appendFileSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
73433
|
-
import { join as
|
|
73456
|
+
import { join as join19 } from "node:path";
|
|
73434
73457
|
function writeMarker(id, marker) {
|
|
73435
73458
|
writeFileSync2(markerPath(id), JSON.stringify(marker, null, 2) + `
|
|
73436
73459
|
`);
|
|
@@ -73526,7 +73549,7 @@ async function analyzeSession(id, opts = {}) {
|
|
|
73526
73549
|
const guides = new Map(adapters.map((a) => [a.name, a.readingGuide]));
|
|
73527
73550
|
const prompt = buildPrompt({ id, root, signals: signals2, guides, file: opts.file ?? false });
|
|
73528
73551
|
const dir = sessionDir(id);
|
|
73529
|
-
writeFileSync2(
|
|
73552
|
+
writeFileSync2(join19(dir, "analyzer-prompt.md"), prompt);
|
|
73530
73553
|
const outcome = await runClaude(prompt, dir, opts);
|
|
73531
73554
|
const marker = {
|
|
73532
73555
|
analyzedAt: new Date().toISOString(),
|
|
@@ -73534,7 +73557,7 @@ async function analyzeSession(id, opts = {}) {
|
|
|
73534
73557
|
note: outcome.note
|
|
73535
73558
|
};
|
|
73536
73559
|
writeMarker(id, marker);
|
|
73537
|
-
return { ...marker, reportPath:
|
|
73560
|
+
return { ...marker, reportPath: join19(dir, "report.md") };
|
|
73538
73561
|
}
|
|
73539
73562
|
function runClaude(prompt, cwd, opts) {
|
|
73540
73563
|
return new Promise((resolve2) => {
|
|
@@ -73564,7 +73587,7 @@ function runClaude(prompt, cwd, opts) {
|
|
|
73564
73587
|
child.on("close", (code) => {
|
|
73565
73588
|
clearTimeout(timer);
|
|
73566
73589
|
try {
|
|
73567
|
-
appendFileSync(
|
|
73590
|
+
appendFileSync(join19(cwd, "analyzer.log"), out + (err ? `
|
|
73568
73591
|
--- stderr ---
|
|
73569
73592
|
${err}` : ""));
|
|
73570
73593
|
} catch {}
|
|
@@ -73614,9 +73637,9 @@ var init_settings = __esm(() => {
|
|
|
73614
73637
|
|
|
73615
73638
|
// src/telemetry/trigger.ts
|
|
73616
73639
|
import { existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync6, rmSync as rmSync2, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
73617
|
-
import { join as
|
|
73640
|
+
import { join as join20 } from "node:path";
|
|
73618
73641
|
function lockPath() {
|
|
73619
|
-
return
|
|
73642
|
+
return join20(sessionsRoot(), ".analyzer.lock");
|
|
73620
73643
|
}
|
|
73621
73644
|
function releaseLock() {
|
|
73622
73645
|
try {
|
|
@@ -74375,12 +74398,12 @@ function redactArgv(argv) {
|
|
|
74375
74398
|
init_store2();
|
|
74376
74399
|
import { createHash as createHash2 } from "node:crypto";
|
|
74377
74400
|
import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
74378
|
-
import { dirname as dirname12, join as
|
|
74401
|
+
import { dirname as dirname12, join as join21 } from "node:path";
|
|
74379
74402
|
var MAX_ID_LEN = 64;
|
|
74380
74403
|
function findGitRoot(start) {
|
|
74381
74404
|
let dir = start;
|
|
74382
74405
|
for (;; ) {
|
|
74383
|
-
if (existsSync13(
|
|
74406
|
+
if (existsSync13(join21(dir, ".git")))
|
|
74384
74407
|
return dir;
|
|
74385
74408
|
const parent = dirname12(dir);
|
|
74386
74409
|
if (parent === dir)
|
|
@@ -74481,7 +74504,7 @@ init_settings();
|
|
|
74481
74504
|
init_store2();
|
|
74482
74505
|
import { spawn as spawn4 } from "node:child_process";
|
|
74483
74506
|
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
|
|
74507
|
+
import { join as join22 } from "node:path";
|
|
74485
74508
|
var LOCK_STALE_MS2 = 30 * 60 * 1000;
|
|
74486
74509
|
var GC_AGE_MS2 = 30 * 24 * 60 * 60 * 1000;
|
|
74487
74510
|
var GC_MAX_PER_RUN = 20;
|
|
@@ -74500,7 +74523,7 @@ function gcOldSessions(sessions) {
|
|
|
74500
74523
|
} catch {}
|
|
74501
74524
|
}
|
|
74502
74525
|
function lockPath2() {
|
|
74503
|
-
return
|
|
74526
|
+
return join22(sessionsRoot(), ".analyzer.lock");
|
|
74504
74527
|
}
|
|
74505
74528
|
function claimLock() {
|
|
74506
74529
|
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.0-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",
|
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
|
),
|