@llblab/pi-actors 0.53.0 → 0.53.2
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/AGENTS.md +4 -1
- package/CHANGELOG.md +10 -0
- package/README.md +2 -2
- package/dist/index.js +4 -1
- package/dist/lib/inspector-overlay.js +2 -1
- package/dist/lib/paths.d.ts +6 -0
- package/dist/lib/paths.js +19 -1
- package/dist/lib/trace-projection.d.ts +2 -0
- package/dist/lib/trace-projection.js +11 -7
- package/dist/scripts/build-dist.mjs +94 -30
- package/docs/actor-inspector.md +1 -1
- package/index.ts +6 -3
- package/lib/inspector-overlay.ts +2 -1
- package/lib/paths.ts +27 -1
- package/lib/trace-projection.ts +13 -6
- package/package.json +3 -8
- package/scripts/build-dist.mjs +94 -30
package/AGENTS.md
CHANGED
|
@@ -44,6 +44,8 @@ Pi host
|
|
|
44
44
|
|
|
45
45
|
`index.ts` wires Pi ports and must not own domain behavior. Keep the local TypeScript import graph acyclic. For architecture-affecting work, load and follow `.agents/skills/domain-dag/SKILL.md`; its validator is local agent tooling, not an npm, CI, or release gate.
|
|
46
46
|
|
|
47
|
+
The committed `dist/` tree is required for npm and git package installs. `npm run build` compiles into a temporary candidate and atomically replaces the distributive; `npm run build:check` must reject source/artifact drift without rewriting the working tree.
|
|
48
|
+
|
|
47
49
|
## Key Domains
|
|
48
50
|
|
|
49
51
|
- `extension-runtime.ts`: low-level Pi session lifecycle, tool adaptation, and runtime service composition behind the thin `index.ts` event-registration root.
|
|
@@ -84,7 +86,7 @@ Canonical event:
|
|
|
84
86
|
{"id":"…","ts":"…","kind":"…","summary":"…","data":{},"level":"info","attention":"notify"}
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
Trace is a bounded retained suffix, not an audit archive. Every first-party writer must call `appendRunTraceEvent`; under the canonical token-owned lock it appends within both fixed bounds or atomically retains the newest suffix plus one cumulative warning-only `runtime.trace_compacted` marker. The marker means older history was discarded; terminal/result/execution/artifact files remain authoritative independently. Reads are newline-safe and order equal timestamps by same-source ordinal, fixed source rank, then stable id without exposing ordering metadata. Never write `trace.jsonl` directly. Generic command lifecycle is Trace-only: `command.done` preserves complete bounded execution evidence but never requests or projects any attention value, and Recipe grammar has no command-completion delivery switch. Semantic attention is explicit: `notify` is visible status, `followup` is an ordinary checkpoint hint, and actor-authored `steer` is durable urgent delivery at Pi's next safe boundary. Never infer attention from status or exit code. Admit steer by exact event and Run generation before `deliverAs: "steer"`; acknowledge only its exact model-bound context envelope, and preserve the later root-terminal completion batch. Sequence, parallel, repeat, and import branches remain internal, while each separately launched Run owns a separate generation.
|
|
89
|
+
Trace is a bounded retained suffix, not an audit archive. Every first-party writer must call `appendRunTraceEvent`; under the canonical token-owned lock it appends within both fixed bounds or atomically retains the newest suffix plus one cumulative warning-only `runtime.trace_compacted` marker. The marker means older history was discarded; terminal/result/execution/artifact files remain authoritative independently. Reads are newline-safe and order equal timestamps by same-source ordinal, fixed source rank, then stable id without exposing ordering metadata. Actor Inspector derives stable source-local row sequences before applying its visible-tail bound, including cumulative compaction offsets for canonical Trace, so appends never renumber retained rows. Never write `trace.jsonl` directly. Generic command lifecycle is Trace-only: `command.done` preserves complete bounded execution evidence but never requests or projects any attention value, and Recipe grammar has no command-completion delivery switch. Semantic attention is explicit: `notify` is visible status, `followup` is an ordinary checkpoint hint, and actor-authored `steer` is durable urgent delivery at Pi's next safe boundary. Never infer attention from status or exit code. Admit steer by exact event and Run generation before `deliverAs: "steer"`; acknowledge only its exact model-bound context envelope, and preserve the later root-terminal completion batch. Sequence, parallel, repeat, and import branches remain internal, while each separately launched Run owns a separate generation.
|
|
88
90
|
|
|
89
91
|
### Control
|
|
90
92
|
|
|
@@ -150,4 +152,5 @@ When deferred Run results gate the next step, wait for their completion batch. I
|
|
|
150
152
|
- Before release run the normal product validation and dependency audit. Use the project-local Domain DAG Skill during architecture-affecting development, not as publication automation.
|
|
151
153
|
- `.github/workflows/release.yml` owns the immutable sequence reusable validation → npm Trusted Publisher publication/verification → GitHub Release convergence; follow [docs/releasing.md](docs/releasing.md).
|
|
152
154
|
- Keep npm publication tokenless: use the exact npm Trusted Publisher binding and job-scoped OIDC permission, never a long-lived npm token or token fallback.
|
|
155
|
+
- Manifest-loaded packages expose bundled Skills only through `pi.skills`, preserving package resource filters and package-owned provenance. Only a checkout auto-discovered directly below a user or project Pi `extensions/` root may contribute its source Skill root through `resources_discover`; compiled-vs-source filename shape does not determine ownership.
|
|
153
156
|
- Until a stable version beyond `1.x`, prefer clean breaking simplification over compatibility aliases or renamed legacy abstractions.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
> Each release keeps at most 8 outcome records of at most 512 characters.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.53.2: Stable Inspector Trace Sequences
|
|
8
|
+
|
|
9
|
+
- `Stable Inspector Trace Sequences`: Actor Inspector now numbers Trace rows from stable source-local chronology before applying the 100-row projection bound. Canonical Trace rows include cumulative compaction offsets and owned Pi turns retain their native sequence, so adding `#100` no longer renumbers the previous `#99`; newest-first ordering, source filters, public Trace JSON, and focus-by-id remain unchanged.
|
|
10
|
+
|
|
11
|
+
## 0.53.1: Filterable Skills and drift-safe Git installs
|
|
12
|
+
|
|
13
|
+
- `Filterable Packaged Skills`: Manifest-loaded runtime leaves all six bundled Skills to `pi.skills`, preserving filters and provenance. Auto-discovered checkouts contribute source Skills even when Pi selects compiled code; resolver regressions cover both modes. Unsupported source manifest aliases are removed, and the committed distributive supports git installs with drift-checking atomic builds.
|
|
14
|
+
|
|
5
15
|
## 0.53.0: Tree-Compressed Completion Delivery
|
|
6
16
|
|
|
7
17
|
- `Quiet Draft Reloads`: Keeps automatic Recipe reload active for draft-directory changes while showing “Recipe tools refreshed” only for active non-draft Recipe changes or registry-root replacement, so ordinary spawn draft capture no longer produces misleading tool-refresh noise.
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ For local development:
|
|
|
31
31
|
pi install /path/to/pi-actors
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
The package contributes the extension and six capability-owning Skills for actors, artifacts, music playback, project work, Recipe memory, and swarm orchestration.
|
|
34
|
+
The package contributes the extension and six capability-owning Skills for actors, artifacts, music playback, project work, Recipe memory, and swarm orchestration. Manifest-loaded packages expose those Skills through the package manifest so Pi resource filters and package provenance remain authoritative; a checkout auto-discovered directly under Pi's user or project extensions directory contributes its source Skills even when Pi selects the checkout's compiled entrypoint.
|
|
35
35
|
|
|
36
36
|
## Public Tools
|
|
37
37
|
|
|
@@ -238,7 +238,7 @@ npm run validate
|
|
|
238
238
|
npm run test:preservation
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
The build produces the JavaScript runtime used by
|
|
241
|
+
The atomic build produces the committed JavaScript runtime used by npm and git installs; `npm run build:check` verifies that `dist/` matches source without rewriting it. Pi can load a TypeScript extension entrypoint, but standalone Node processes cannot type-strip modules under `node_modules`; keeping compiled Run modules preserves process isolation without a runtime TypeScript loader.
|
|
242
242
|
|
|
243
243
|
See the [documentation index](./docs/README.md), [Run lifecycle](./docs/async-runs.md), and [Recipe library](./docs/recipe-library.md).
|
|
244
244
|
|
package/dist/index.js
CHANGED
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as ExtensionRuntime from "./lib/extension-runtime.js";
|
|
7
7
|
import * as InspectorCommand from "./lib/inspector-command.js";
|
|
8
|
+
import * as Paths from "./lib/paths.js";
|
|
8
9
|
export default function toolRegistryExtension(pi) {
|
|
9
10
|
const runtime = ExtensionRuntime.createActorExtensionRuntime(pi);
|
|
10
|
-
|
|
11
|
+
if (Paths.isRawExtensionCheckout(import.meta.url)) {
|
|
12
|
+
pi.on("resources_discover", async () => runtime.discoverResources(import.meta.url));
|
|
13
|
+
}
|
|
11
14
|
pi.on("session_start", async (_event, ctx) => runtime.onSessionStart(ctx));
|
|
12
15
|
pi.on("agent_settled", async (_event, ctx) => runtime.onAgentSettled(ctx));
|
|
13
16
|
pi.on("context", async (event, ctx) => ({
|
|
@@ -583,7 +583,8 @@ export class ActorInspectorOverlay {
|
|
|
583
583
|
const prefix = this.focus === "list" && index === this.rowIndex
|
|
584
584
|
? this.theme.fg("accent", " ▶ ")
|
|
585
585
|
: " ";
|
|
586
|
-
const
|
|
586
|
+
const sequence = item[TraceProjection.TRACE_ITEM_SEQUENCE] ?? items.length - index - 1;
|
|
587
|
+
const row = `${prefix}${this.theme.fg("text", `#${sequence}`)} ${marker} ${this.theme.fg("muted", item.source)}/${this.theme.fg(item.level === "error" ? "error" : "accent", item.kind)} ${this.theme.fg("text", item.summary)}`;
|
|
587
588
|
return this.focus === "list" && index === this.rowIndex
|
|
588
589
|
? this.theme.fg("accent", row)
|
|
589
590
|
: row;
|
package/dist/lib/paths.d.ts
CHANGED
|
@@ -21,6 +21,12 @@ export declare function getToolReviewStatePath(agentDir?: string): string;
|
|
|
21
21
|
export declare function getToolReviewBatchDir(reviewId: string, agentDir?: string): string;
|
|
22
22
|
export declare function getExtensionRuntimePaths(agentDir?: string): ExtensionRuntimePaths;
|
|
23
23
|
export declare const EXTENSION_RUNTIME_PATHS: ExtensionRuntimePaths;
|
|
24
|
+
export declare function getExtensionPackageRoot(extensionUrl: string): string;
|
|
25
|
+
export interface RawExtensionCheckoutOptions {
|
|
26
|
+
agentDir?: string;
|
|
27
|
+
cwd?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function isRawExtensionCheckout(extensionUrl: string, options?: RawExtensionCheckoutOptions): boolean;
|
|
24
30
|
export declare function getExtensionSkillsDir(extensionUrl: string): string;
|
|
25
31
|
export declare function getExistingExtensionSkillPaths(extensionUrl: string): string[];
|
|
26
32
|
export declare function getRecipeRoot(agentDir?: string): string;
|
package/dist/lib/paths.js
CHANGED
|
@@ -57,8 +57,26 @@ export function getExtensionRuntimePaths(agentDir = getAgentDir()) {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
export const EXTENSION_RUNTIME_PATHS = getExtensionRuntimePaths();
|
|
60
|
+
export function getExtensionPackageRoot(extensionUrl) {
|
|
61
|
+
let current = dirname(fileURLToPath(extensionUrl));
|
|
62
|
+
while (true) {
|
|
63
|
+
if (existsSync(join(current, "package.json")))
|
|
64
|
+
return current;
|
|
65
|
+
const parent = dirname(current);
|
|
66
|
+
if (parent === current)
|
|
67
|
+
return dirname(fileURLToPath(extensionUrl));
|
|
68
|
+
current = parent;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function isRawExtensionCheckout(extensionUrl, options = {}) {
|
|
72
|
+
const packageRoot = resolve(getExtensionPackageRoot(extensionUrl));
|
|
73
|
+
const agentDir = resolve(options.agentDir ?? getAgentDir());
|
|
74
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
75
|
+
return dirname(packageRoot) === join(agentDir, "extensions") ||
|
|
76
|
+
dirname(packageRoot) === join(cwd, ".pi", "extensions");
|
|
77
|
+
}
|
|
60
78
|
export function getExtensionSkillsDir(extensionUrl) {
|
|
61
|
-
return join(
|
|
79
|
+
return join(getExtensionPackageRoot(extensionUrl), "skills");
|
|
62
80
|
}
|
|
63
81
|
export function getExistingExtensionSkillPaths(extensionUrl) {
|
|
64
82
|
const skillsDir = getExtensionSkillsDir(extensionUrl);
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
import { type RunArtifactDeclaration } from "./runs-artifacts.ts";
|
|
7
7
|
export type TraceSource = "lifecycle" | "control" | "process" | "agent" | "artifact" | "runtime";
|
|
8
8
|
export type TraceSourceFilter = TraceSource | "all";
|
|
9
|
+
export declare const TRACE_ITEM_SEQUENCE: unique symbol;
|
|
9
10
|
export interface TraceItem {
|
|
10
11
|
id: string;
|
|
11
12
|
ts: string;
|
|
13
|
+
[TRACE_ITEM_SEQUENCE]?: number;
|
|
12
14
|
source: TraceSource;
|
|
13
15
|
kind: string;
|
|
14
16
|
summary: string;
|
|
@@ -14,6 +14,7 @@ import { tailFile } from "./runs-status.js";
|
|
|
14
14
|
import * as RunsTrace from "./runs-trace.js";
|
|
15
15
|
import * as SessionEvidence from "./session-evidence.js";
|
|
16
16
|
import { readJsonFileResilient, } from "./state-readers.js";
|
|
17
|
+
export const TRACE_ITEM_SEQUENCE = Symbol("trace-item-sequence");
|
|
17
18
|
const LIFECYCLE_PREFIXES = ["run.", "command."];
|
|
18
19
|
const SOURCE_LIMIT = 100;
|
|
19
20
|
const SOURCE_RANK = {
|
|
@@ -32,7 +33,8 @@ function sourceForEvent(event) {
|
|
|
32
33
|
? "lifecycle"
|
|
33
34
|
: "runtime";
|
|
34
35
|
}
|
|
35
|
-
function ordered(item, sourceKey, ordinal) {
|
|
36
|
+
function ordered(item, sourceKey, ordinal, sequence = ordinal) {
|
|
37
|
+
Object.defineProperty(item, TRACE_ITEM_SEQUENCE, { value: sequence });
|
|
36
38
|
return { item, ordinal, sourceKey };
|
|
37
39
|
}
|
|
38
40
|
function compareTraceItems(left, right) {
|
|
@@ -83,6 +85,8 @@ function controlDiagnosticItems(diagnostics) {
|
|
|
83
85
|
}
|
|
84
86
|
function traceEventItems(stateDir) {
|
|
85
87
|
const read = RunsTrace.readRunTraceJournal(stateDir);
|
|
88
|
+
const summary = RunsTrace.summarizeRunTraceJournal(read);
|
|
89
|
+
const sequenceOffset = summary.dropped_events + Math.max(0, summary.compactions_total - 1);
|
|
86
90
|
const retained = read.events.slice(-SOURCE_LIMIT);
|
|
87
91
|
const projected = retained.map(({ event, ordinal }) => ordered({
|
|
88
92
|
id: event.id,
|
|
@@ -95,7 +99,7 @@ function traceEventItems(stateDir) {
|
|
|
95
99
|
...(event.attention ? { attention: event.attention } : {}),
|
|
96
100
|
...(event.data !== undefined ? { data: event.data } : {}),
|
|
97
101
|
}),
|
|
98
|
-
}, "trace", ordinal));
|
|
102
|
+
}, "trace", ordinal, sequenceOffset + Math.max(0, ordinal - 1)));
|
|
99
103
|
const diagnostics = diagnosticItems(read.diagnostics).map((item, index) => ordered(item, "trace", read.events.length + index + 1));
|
|
100
104
|
const hasMarker = read.events.some(({ event }) => event.kind === "runtime.trace_compacted");
|
|
101
105
|
if (read.omittedPrefixBytes > 0 && !hasMarker) {
|
|
@@ -128,7 +132,7 @@ function controlItems(stateDir) {
|
|
|
128
132
|
summary: `${value.action} ${value.status}`,
|
|
129
133
|
...(value.status === "failed" ? { level: "error" } : {}),
|
|
130
134
|
detail: value,
|
|
131
|
-
}, "control", start + index + 1);
|
|
135
|
+
}, "control", start + index + 1, start + index);
|
|
132
136
|
});
|
|
133
137
|
return [
|
|
134
138
|
...projected,
|
|
@@ -153,7 +157,7 @@ function agentItems(stateDir) {
|
|
|
153
157
|
});
|
|
154
158
|
}
|
|
155
159
|
function processItems(stateDir) {
|
|
156
|
-
return ["stdout", "stderr"].flatMap((stream) => {
|
|
160
|
+
return ["stdout", "stderr"].flatMap((stream, index) => {
|
|
157
161
|
const path = join(stateDir, `${stream}.log`);
|
|
158
162
|
if (!existsSync(path))
|
|
159
163
|
return [];
|
|
@@ -169,7 +173,7 @@ function processItems(stateDir) {
|
|
|
169
173
|
summary: `${stream} (${stat.size} bytes)`,
|
|
170
174
|
...(stream === "stderr" ? { level: "warning" } : {}),
|
|
171
175
|
detail: redact({ bytes: stat.size, path, tail }),
|
|
172
|
-
}, `process:${stream}`, 1)];
|
|
176
|
+
}, `process:${stream}`, 1, index)];
|
|
173
177
|
});
|
|
174
178
|
}
|
|
175
179
|
function resultItems(stateDir) {
|
|
@@ -190,7 +194,7 @@ function resultItems(stateDir) {
|
|
|
190
194
|
summary: failed ? "Run process failed" : "Run process completed",
|
|
191
195
|
...(failed ? { level: "error" } : {}),
|
|
192
196
|
detail: redact(read.value),
|
|
193
|
-
}, "process:result", 1), ...diagnosticItems(read.diagnostics).map((item, index) => ordered(item, "process:result", index + 2))];
|
|
197
|
+
}, "process:result", 1, 2), ...diagnosticItems(read.diagnostics).map((item, index) => ordered(item, "process:result", index + 2))];
|
|
194
198
|
}
|
|
195
199
|
function artifactItems(stateDir, declarations) {
|
|
196
200
|
const manifest = resolveArtifactManifest(declarations);
|
|
@@ -211,7 +215,7 @@ function artifactItems(stateDir, declarations) {
|
|
|
211
215
|
required: artifact.required,
|
|
212
216
|
...(artifact.size !== undefined ? { size: artifact.size } : {}),
|
|
213
217
|
}),
|
|
214
|
-
}, "artifact", index + 1));
|
|
218
|
+
}, "artifact", index + 1, index));
|
|
215
219
|
}
|
|
216
220
|
export function projectRunTrace(stateDir, options = {}) {
|
|
217
221
|
const source = options.source ?? "all";
|
|
@@ -1,47 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* This is intentionally standalone: it is package/build glue, not reusable
|
|
7
|
-
* actor-domain behavior. It cleans dist, compiles TypeScript, mirrors runtime
|
|
8
|
-
* assets, and syntax-checks packaged scripts.
|
|
4
|
+
* Builds or verifies the JavaScript-only distributive tree without exposing a
|
|
5
|
+
* partial tree to Pi or destroying the previous distributive on failure.
|
|
9
6
|
*/
|
|
10
7
|
|
|
11
8
|
import { spawnSync } from "node:child_process";
|
|
12
9
|
import {
|
|
13
10
|
cpSync,
|
|
11
|
+
existsSync,
|
|
14
12
|
mkdirSync,
|
|
13
|
+
mkdtempSync,
|
|
14
|
+
readFileSync,
|
|
15
15
|
readdirSync,
|
|
16
|
+
renameSync,
|
|
16
17
|
rmSync,
|
|
17
18
|
writeFileSync,
|
|
18
19
|
} from "node:fs";
|
|
19
|
-
import { join } from "node:path";
|
|
20
|
+
import { join, relative } from "node:path";
|
|
21
|
+
|
|
22
|
+
const DIST_DIR = "dist";
|
|
23
|
+
const checkOnly = process.argv.includes("--check");
|
|
20
24
|
|
|
21
25
|
function run(command, args) {
|
|
22
26
|
const result = spawnSync(command, args, { stdio: "inherit" });
|
|
23
27
|
if (result.error) throw result.error;
|
|
24
|
-
if (result.status !== 0)
|
|
28
|
+
if (result.status !== 0) {
|
|
29
|
+
throw new Error(`${command} exited with status ${result.status ?? "unknown"}.`);
|
|
30
|
+
}
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"-p",
|
|
33
|
-
"tsconfig.build.json",
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
mkdirSync(join("dist", "pi-actors"), { recursive: true });
|
|
37
|
-
writeFileSync(
|
|
38
|
-
join("dist", "pi-actors", "index.js"),
|
|
39
|
-
'export { default } from "../index.js";\n',
|
|
40
|
-
"utf8",
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
for (const dir of ["scripts", "fixtures", "skills"]) {
|
|
44
|
-
cpSync(dir, join("dist", dir), { recursive: true });
|
|
33
|
+
function listFiles(root, current = root) {
|
|
34
|
+
return readdirSync(current, { withFileTypes: true }).flatMap((entry) => {
|
|
35
|
+
const path = join(current, entry.name);
|
|
36
|
+
return entry.isDirectory() ? listFiles(root, path) : [relative(root, path)];
|
|
37
|
+
}).sort();
|
|
45
38
|
}
|
|
46
39
|
|
|
47
40
|
function listModuleScripts(root) {
|
|
@@ -52,9 +45,80 @@ function listModuleScripts(root) {
|
|
|
52
45
|
});
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
function normalizeTextFiles(root) {
|
|
49
|
+
const textSuffixes = [".js", ".ts", ".mjs", ".json", ".md"];
|
|
50
|
+
for (const path of listFiles(root)) {
|
|
51
|
+
if (!textSuffixes.some((suffix) => path.endsWith(suffix))) continue;
|
|
52
|
+
const absolutePath = join(root, path);
|
|
53
|
+
const source = readFileSync(absolutePath, "utf8");
|
|
54
|
+
const normalized = source.replace(/\r\n?/g, "\n");
|
|
55
|
+
if (normalized !== source) writeFileSync(absolutePath, normalized, "utf8");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function assertTreesEqual(expectedRoot, actualRoot) {
|
|
60
|
+
if (!existsSync(expectedRoot)) {
|
|
61
|
+
throw new Error(`${expectedRoot} is missing; run npm run build.`);
|
|
62
|
+
}
|
|
63
|
+
const expectedFiles = listFiles(expectedRoot);
|
|
64
|
+
const actualFiles = listFiles(actualRoot);
|
|
65
|
+
if (JSON.stringify(expectedFiles) !== JSON.stringify(actualFiles)) {
|
|
66
|
+
throw new Error("dist file inventory is stale; run npm run build.");
|
|
67
|
+
}
|
|
68
|
+
for (const path of expectedFiles) {
|
|
69
|
+
if (!readFileSync(join(expectedRoot, path)).equals(readFileSync(join(actualRoot, path)))) {
|
|
70
|
+
throw new Error(`dist/${path} is stale; run npm run build.`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function replaceDist(candidate) {
|
|
76
|
+
const backup = `.dist-backup-${process.pid}-${Date.now()}`;
|
|
77
|
+
const hadDist = existsSync(DIST_DIR);
|
|
78
|
+
if (hadDist) renameSync(DIST_DIR, backup);
|
|
79
|
+
try {
|
|
80
|
+
renameSync(candidate, DIST_DIR);
|
|
81
|
+
if (hadDist) rmSync(backup, { recursive: true, force: true });
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (hadDist && existsSync(backup) && !existsSync(DIST_DIR)) {
|
|
84
|
+
renameSync(backup, DIST_DIR);
|
|
85
|
+
}
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
59
89
|
|
|
60
|
-
|
|
90
|
+
const candidate = mkdtempSync(join(process.cwd(), ".dist-build-"));
|
|
91
|
+
try {
|
|
92
|
+
run(process.execPath, [
|
|
93
|
+
join("node_modules", "typescript", "bin", "tsc"),
|
|
94
|
+
"-p",
|
|
95
|
+
"tsconfig.build.json",
|
|
96
|
+
"--outDir",
|
|
97
|
+
candidate,
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
mkdirSync(join(candidate, "pi-actors"), { recursive: true });
|
|
101
|
+
writeFileSync(
|
|
102
|
+
join(candidate, "pi-actors", "index.js"),
|
|
103
|
+
'export { default } from "../index.js";\n',
|
|
104
|
+
"utf8",
|
|
105
|
+
);
|
|
106
|
+
normalizeTextFiles(candidate);
|
|
107
|
+
for (const dir of ["scripts", "fixtures", "skills"]) {
|
|
108
|
+
cpSync(dir, join(candidate, dir), { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
const builtScripts = [
|
|
111
|
+
...listModuleScripts(join(candidate, "scripts")),
|
|
112
|
+
...listModuleScripts(join(candidate, "skills")),
|
|
113
|
+
];
|
|
114
|
+
run(process.execPath, ["--check", ...builtScripts]);
|
|
115
|
+
|
|
116
|
+
if (checkOnly) {
|
|
117
|
+
assertTreesEqual(DIST_DIR, candidate);
|
|
118
|
+
console.log("pi-actors: dist is current");
|
|
119
|
+
} else {
|
|
120
|
+
replaceDist(candidate);
|
|
121
|
+
}
|
|
122
|
+
} finally {
|
|
123
|
+
rmSync(candidate, { recursive: true, force: true });
|
|
124
|
+
}
|
package/docs/actor-inspector.md
CHANGED
|
@@ -25,7 +25,7 @@ Captured Recipe evidence belongs to the Run generation and does not change when
|
|
|
25
25
|
|
|
26
26
|
Shows the unified bounded Trace projection. Sources include lifecycle/runtime observations, Controls, owned Pi turns, command-log tails, results, artifacts, and diagnostics. The source selector displays only `all` plus sources present in the current projection from `lifecycle`, `control`, `process`, `agent`, `artifact`, and `runtime`. Select a row to open structured detail.
|
|
27
27
|
|
|
28
|
-
Trace ordering stays deterministic and newest-first: timestamp descending, same-source physical ordinal descending, fixed internal source rank, then stable id.
|
|
28
|
+
Trace ordering stays deterministic and newest-first: timestamp descending, same-source physical ordinal descending, fixed internal source rank, then stable id. Row numbers use stable zero-based source sequences rather than positions inside the visible 100-row tail: when `#100` arrives, the existing `#99` remains `#99`. Canonical journal rows reconstruct their absolute sequence from retained ordinals plus cumulative compaction evidence; owned Pi turns keep their native turn sequence. Sequences are source-local and never imply cross-source causality. The summary states whether retained history is complete; `runtime.trace_compacted` means older history was discarded and shows bounded cumulative drop evidence. Terminal/result/execution/artifact evidence keeps its own authority. The projection applies path containment and redaction before rendering.
|
|
29
29
|
|
|
30
30
|
## Control
|
|
31
31
|
|
package/index.ts
CHANGED
|
@@ -6,13 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
import * as ExtensionRuntime from "./lib/extension-runtime.ts";
|
|
8
8
|
import * as InspectorCommand from "./lib/inspector-command.ts";
|
|
9
|
+
import * as Paths from "./lib/paths.ts";
|
|
9
10
|
import * as Pi from "./lib/pi.ts";
|
|
10
11
|
|
|
11
12
|
export default function toolRegistryExtension(pi: Pi.ExtensionAPI) {
|
|
12
13
|
const runtime = ExtensionRuntime.createActorExtensionRuntime(pi);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
if (Paths.isRawExtensionCheckout(import.meta.url)) {
|
|
15
|
+
pi.on("resources_discover", async () =>
|
|
16
|
+
runtime.discoverResources(import.meta.url),
|
|
17
|
+
);
|
|
18
|
+
}
|
|
16
19
|
pi.on("session_start", async (_event, ctx) => runtime.onSessionStart(ctx));
|
|
17
20
|
pi.on("agent_settled", async (_event, ctx) => runtime.onAgentSettled(ctx));
|
|
18
21
|
pi.on("context", async (event, ctx) => ({
|
package/lib/inspector-overlay.ts
CHANGED
|
@@ -653,7 +653,8 @@ export class ActorInspectorOverlay {
|
|
|
653
653
|
const prefix = this.focus === "list" && index === this.rowIndex
|
|
654
654
|
? this.theme.fg("accent", " ▶ ")
|
|
655
655
|
: " ";
|
|
656
|
-
const
|
|
656
|
+
const sequence = item[TraceProjection.TRACE_ITEM_SEQUENCE] ?? items.length - index - 1;
|
|
657
|
+
const row = `${prefix}${this.theme.fg("text", `#${sequence}`)} ${marker} ${this.theme.fg("muted", item.source)}/${this.theme.fg(item.level === "error" ? "error" : "accent", item.kind)} ${this.theme.fg("text", item.summary)}`;
|
|
657
658
|
return this.focus === "list" && index === this.rowIndex
|
|
658
659
|
? this.theme.fg("accent", row)
|
|
659
660
|
: row;
|
package/lib/paths.ts
CHANGED
|
@@ -93,8 +93,34 @@ export function getExtensionRuntimePaths(
|
|
|
93
93
|
|
|
94
94
|
export const EXTENSION_RUNTIME_PATHS = getExtensionRuntimePaths();
|
|
95
95
|
|
|
96
|
+
export function getExtensionPackageRoot(extensionUrl: string): string {
|
|
97
|
+
let current = dirname(fileURLToPath(extensionUrl));
|
|
98
|
+
while (true) {
|
|
99
|
+
if (existsSync(join(current, "package.json"))) return current;
|
|
100
|
+
const parent = dirname(current);
|
|
101
|
+
if (parent === current) return dirname(fileURLToPath(extensionUrl));
|
|
102
|
+
current = parent;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface RawExtensionCheckoutOptions {
|
|
107
|
+
agentDir?: string;
|
|
108
|
+
cwd?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function isRawExtensionCheckout(
|
|
112
|
+
extensionUrl: string,
|
|
113
|
+
options: RawExtensionCheckoutOptions = {},
|
|
114
|
+
): boolean {
|
|
115
|
+
const packageRoot = resolve(getExtensionPackageRoot(extensionUrl));
|
|
116
|
+
const agentDir = resolve(options.agentDir ?? getAgentDir());
|
|
117
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
118
|
+
return dirname(packageRoot) === join(agentDir, "extensions") ||
|
|
119
|
+
dirname(packageRoot) === join(cwd, ".pi", "extensions");
|
|
120
|
+
}
|
|
121
|
+
|
|
96
122
|
export function getExtensionSkillsDir(extensionUrl: string): string {
|
|
97
|
-
return join(
|
|
123
|
+
return join(getExtensionPackageRoot(extensionUrl), "skills");
|
|
98
124
|
}
|
|
99
125
|
|
|
100
126
|
export function getExistingExtensionSkillPaths(extensionUrl: string): string[] {
|
package/lib/trace-projection.ts
CHANGED
|
@@ -34,9 +34,12 @@ export type TraceSource =
|
|
|
34
34
|
|
|
35
35
|
export type TraceSourceFilter = TraceSource | "all";
|
|
36
36
|
|
|
37
|
+
export const TRACE_ITEM_SEQUENCE = Symbol("trace-item-sequence");
|
|
38
|
+
|
|
37
39
|
export interface TraceItem {
|
|
38
40
|
id: string;
|
|
39
41
|
ts: string;
|
|
42
|
+
[TRACE_ITEM_SEQUENCE]?: number;
|
|
40
43
|
source: TraceSource;
|
|
41
44
|
kind: string;
|
|
42
45
|
summary: string;
|
|
@@ -75,7 +78,9 @@ function ordered(
|
|
|
75
78
|
item: TraceItem,
|
|
76
79
|
sourceKey: string,
|
|
77
80
|
ordinal: number,
|
|
81
|
+
sequence = ordinal,
|
|
78
82
|
): OrderedTraceItem {
|
|
83
|
+
Object.defineProperty(item, TRACE_ITEM_SEQUENCE, { value: sequence });
|
|
79
84
|
return { item, ordinal, sourceKey };
|
|
80
85
|
}
|
|
81
86
|
|
|
@@ -133,6 +138,8 @@ function controlDiagnosticItems(
|
|
|
133
138
|
|
|
134
139
|
function traceEventItems(stateDir: string): OrderedTraceItem[] {
|
|
135
140
|
const read = RunsTrace.readRunTraceJournal(stateDir);
|
|
141
|
+
const summary = RunsTrace.summarizeRunTraceJournal(read);
|
|
142
|
+
const sequenceOffset = summary.dropped_events + Math.max(0, summary.compactions_total - 1);
|
|
136
143
|
const retained = read.events.slice(-SOURCE_LIMIT);
|
|
137
144
|
const projected = retained.map(({ event, ordinal }) => ordered({
|
|
138
145
|
id: event.id,
|
|
@@ -145,7 +152,7 @@ function traceEventItems(stateDir: string): OrderedTraceItem[] {
|
|
|
145
152
|
...(event.attention ? { attention: event.attention } : {}),
|
|
146
153
|
...(event.data !== undefined ? { data: event.data } : {}),
|
|
147
154
|
}),
|
|
148
|
-
}, "trace", ordinal));
|
|
155
|
+
}, "trace", ordinal, sequenceOffset + Math.max(0, ordinal - 1)));
|
|
149
156
|
const diagnostics = diagnosticItems(read.diagnostics).map((item, index) =>
|
|
150
157
|
ordered(item, "trace", read.events.length + index + 1)
|
|
151
158
|
);
|
|
@@ -183,7 +190,7 @@ function controlItems(stateDir: string): OrderedTraceItem[] {
|
|
|
183
190
|
summary: `${value.action} ${value.status}`,
|
|
184
191
|
...(value.status === "failed" ? { level: "error" as const } : {}),
|
|
185
192
|
detail: value,
|
|
186
|
-
}, "control", start + index + 1);
|
|
193
|
+
}, "control", start + index + 1, start + index);
|
|
187
194
|
});
|
|
188
195
|
return [
|
|
189
196
|
...projected,
|
|
@@ -212,7 +219,7 @@ function agentItems(stateDir: string): OrderedTraceItem[] {
|
|
|
212
219
|
}
|
|
213
220
|
|
|
214
221
|
function processItems(stateDir: string): OrderedTraceItem[] {
|
|
215
|
-
return ["stdout", "stderr"].flatMap((stream) => {
|
|
222
|
+
return ["stdout", "stderr"].flatMap((stream, index) => {
|
|
216
223
|
const path = join(stateDir, `${stream}.log`);
|
|
217
224
|
if (!existsSync(path)) return [];
|
|
218
225
|
const stat = statSync(path);
|
|
@@ -226,7 +233,7 @@ function processItems(stateDir: string): OrderedTraceItem[] {
|
|
|
226
233
|
summary: `${stream} (${stat.size} bytes)`,
|
|
227
234
|
...(stream === "stderr" ? { level: "warning" as const } : {}),
|
|
228
235
|
detail: redact({ bytes: stat.size, path, tail }),
|
|
229
|
-
}, `process:${stream}`, 1)];
|
|
236
|
+
}, `process:${stream}`, 1, index)];
|
|
230
237
|
});
|
|
231
238
|
}
|
|
232
239
|
|
|
@@ -250,7 +257,7 @@ function resultItems(stateDir: string): OrderedTraceItem[] {
|
|
|
250
257
|
summary: failed ? "Run process failed" : "Run process completed",
|
|
251
258
|
...(failed ? { level: "error" as const } : {}),
|
|
252
259
|
detail: redact(read.value),
|
|
253
|
-
}, "process:result", 1), ...diagnosticItems(read.diagnostics).map(
|
|
260
|
+
}, "process:result", 1, 2), ...diagnosticItems(read.diagnostics).map(
|
|
254
261
|
(item, index) => ordered(item, "process:result", index + 2),
|
|
255
262
|
)];
|
|
256
263
|
}
|
|
@@ -277,7 +284,7 @@ function artifactItems(
|
|
|
277
284
|
required: artifact.required,
|
|
278
285
|
...(artifact.size !== undefined ? { size: artifact.size } : {}),
|
|
279
286
|
}),
|
|
280
|
-
}, "artifact", index + 1),
|
|
287
|
+
}, "artifact", index + 1, index),
|
|
281
288
|
);
|
|
282
289
|
}
|
|
283
290
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-actors",
|
|
3
|
-
"version": "0.53.
|
|
3
|
+
"version": "0.53.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Local Actor Kernel for Pi",
|
|
6
6
|
"keywords": [
|
|
@@ -31,9 +31,10 @@
|
|
|
31
31
|
"test:preservation": "node --experimental-strip-types --test --test-concurrency=1 tests/async-runs.test.ts tests/observability.test.ts tests/session-evidence.test.ts tests/inspector-security.test.ts tests/inspector-overlay.test.ts tests/review-control.test.ts",
|
|
32
32
|
"pack:dry": "npm pack --dry-run",
|
|
33
33
|
"recipes:qa": "node skills/actors/scripts/validate-recipe.mjs skills --skills --qa --summary",
|
|
34
|
-
"validate": "npx tsc --noEmit && npm run recipes:qa && npm run build && npm run check && npm test && npm run conformance && npm run pack:dry",
|
|
34
|
+
"validate": "npx tsc --noEmit && npm run recipes:qa && npm run build:check && npm run check && npm test && npm run conformance && npm run pack:dry",
|
|
35
35
|
"audit:dependencies": "npm audit --audit-level=high --omit=peer",
|
|
36
36
|
"build": "node scripts/build-dist.mjs",
|
|
37
|
+
"build:check": "node scripts/build-dist.mjs --check",
|
|
37
38
|
"prepack": "npm run build"
|
|
38
39
|
},
|
|
39
40
|
"files": [
|
|
@@ -54,15 +55,9 @@
|
|
|
54
55
|
"extensions": [
|
|
55
56
|
"./dist/pi-actors/index.js"
|
|
56
57
|
],
|
|
57
|
-
"sourceExtensions": [
|
|
58
|
-
"./index.ts"
|
|
59
|
-
],
|
|
60
58
|
"skills": [
|
|
61
59
|
"./dist/skills"
|
|
62
60
|
],
|
|
63
|
-
"sourceSkills": [
|
|
64
|
-
"./skills"
|
|
65
|
-
],
|
|
66
61
|
"image": "https://raw.githubusercontent.com/llblab/pi-actors/main/banner.jpg"
|
|
67
62
|
},
|
|
68
63
|
"peerDependencies": {
|
package/scripts/build-dist.mjs
CHANGED
|
@@ -1,47 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* This is intentionally standalone: it is package/build glue, not reusable
|
|
7
|
-
* actor-domain behavior. It cleans dist, compiles TypeScript, mirrors runtime
|
|
8
|
-
* assets, and syntax-checks packaged scripts.
|
|
4
|
+
* Builds or verifies the JavaScript-only distributive tree without exposing a
|
|
5
|
+
* partial tree to Pi or destroying the previous distributive on failure.
|
|
9
6
|
*/
|
|
10
7
|
|
|
11
8
|
import { spawnSync } from "node:child_process";
|
|
12
9
|
import {
|
|
13
10
|
cpSync,
|
|
11
|
+
existsSync,
|
|
14
12
|
mkdirSync,
|
|
13
|
+
mkdtempSync,
|
|
14
|
+
readFileSync,
|
|
15
15
|
readdirSync,
|
|
16
|
+
renameSync,
|
|
16
17
|
rmSync,
|
|
17
18
|
writeFileSync,
|
|
18
19
|
} from "node:fs";
|
|
19
|
-
import { join } from "node:path";
|
|
20
|
+
import { join, relative } from "node:path";
|
|
21
|
+
|
|
22
|
+
const DIST_DIR = "dist";
|
|
23
|
+
const checkOnly = process.argv.includes("--check");
|
|
20
24
|
|
|
21
25
|
function run(command, args) {
|
|
22
26
|
const result = spawnSync(command, args, { stdio: "inherit" });
|
|
23
27
|
if (result.error) throw result.error;
|
|
24
|
-
if (result.status !== 0)
|
|
28
|
+
if (result.status !== 0) {
|
|
29
|
+
throw new Error(`${command} exited with status ${result.status ?? "unknown"}.`);
|
|
30
|
+
}
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"-p",
|
|
33
|
-
"tsconfig.build.json",
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
mkdirSync(join("dist", "pi-actors"), { recursive: true });
|
|
37
|
-
writeFileSync(
|
|
38
|
-
join("dist", "pi-actors", "index.js"),
|
|
39
|
-
'export { default } from "../index.js";\n',
|
|
40
|
-
"utf8",
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
for (const dir of ["scripts", "fixtures", "skills"]) {
|
|
44
|
-
cpSync(dir, join("dist", dir), { recursive: true });
|
|
33
|
+
function listFiles(root, current = root) {
|
|
34
|
+
return readdirSync(current, { withFileTypes: true }).flatMap((entry) => {
|
|
35
|
+
const path = join(current, entry.name);
|
|
36
|
+
return entry.isDirectory() ? listFiles(root, path) : [relative(root, path)];
|
|
37
|
+
}).sort();
|
|
45
38
|
}
|
|
46
39
|
|
|
47
40
|
function listModuleScripts(root) {
|
|
@@ -52,9 +45,80 @@ function listModuleScripts(root) {
|
|
|
52
45
|
});
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
function normalizeTextFiles(root) {
|
|
49
|
+
const textSuffixes = [".js", ".ts", ".mjs", ".json", ".md"];
|
|
50
|
+
for (const path of listFiles(root)) {
|
|
51
|
+
if (!textSuffixes.some((suffix) => path.endsWith(suffix))) continue;
|
|
52
|
+
const absolutePath = join(root, path);
|
|
53
|
+
const source = readFileSync(absolutePath, "utf8");
|
|
54
|
+
const normalized = source.replace(/\r\n?/g, "\n");
|
|
55
|
+
if (normalized !== source) writeFileSync(absolutePath, normalized, "utf8");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function assertTreesEqual(expectedRoot, actualRoot) {
|
|
60
|
+
if (!existsSync(expectedRoot)) {
|
|
61
|
+
throw new Error(`${expectedRoot} is missing; run npm run build.`);
|
|
62
|
+
}
|
|
63
|
+
const expectedFiles = listFiles(expectedRoot);
|
|
64
|
+
const actualFiles = listFiles(actualRoot);
|
|
65
|
+
if (JSON.stringify(expectedFiles) !== JSON.stringify(actualFiles)) {
|
|
66
|
+
throw new Error("dist file inventory is stale; run npm run build.");
|
|
67
|
+
}
|
|
68
|
+
for (const path of expectedFiles) {
|
|
69
|
+
if (!readFileSync(join(expectedRoot, path)).equals(readFileSync(join(actualRoot, path)))) {
|
|
70
|
+
throw new Error(`dist/${path} is stale; run npm run build.`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function replaceDist(candidate) {
|
|
76
|
+
const backup = `.dist-backup-${process.pid}-${Date.now()}`;
|
|
77
|
+
const hadDist = existsSync(DIST_DIR);
|
|
78
|
+
if (hadDist) renameSync(DIST_DIR, backup);
|
|
79
|
+
try {
|
|
80
|
+
renameSync(candidate, DIST_DIR);
|
|
81
|
+
if (hadDist) rmSync(backup, { recursive: true, force: true });
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (hadDist && existsSync(backup) && !existsSync(DIST_DIR)) {
|
|
84
|
+
renameSync(backup, DIST_DIR);
|
|
85
|
+
}
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
59
89
|
|
|
60
|
-
|
|
90
|
+
const candidate = mkdtempSync(join(process.cwd(), ".dist-build-"));
|
|
91
|
+
try {
|
|
92
|
+
run(process.execPath, [
|
|
93
|
+
join("node_modules", "typescript", "bin", "tsc"),
|
|
94
|
+
"-p",
|
|
95
|
+
"tsconfig.build.json",
|
|
96
|
+
"--outDir",
|
|
97
|
+
candidate,
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
mkdirSync(join(candidate, "pi-actors"), { recursive: true });
|
|
101
|
+
writeFileSync(
|
|
102
|
+
join(candidate, "pi-actors", "index.js"),
|
|
103
|
+
'export { default } from "../index.js";\n',
|
|
104
|
+
"utf8",
|
|
105
|
+
);
|
|
106
|
+
normalizeTextFiles(candidate);
|
|
107
|
+
for (const dir of ["scripts", "fixtures", "skills"]) {
|
|
108
|
+
cpSync(dir, join(candidate, dir), { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
const builtScripts = [
|
|
111
|
+
...listModuleScripts(join(candidate, "scripts")),
|
|
112
|
+
...listModuleScripts(join(candidate, "skills")),
|
|
113
|
+
];
|
|
114
|
+
run(process.execPath, ["--check", ...builtScripts]);
|
|
115
|
+
|
|
116
|
+
if (checkOnly) {
|
|
117
|
+
assertTreesEqual(DIST_DIR, candidate);
|
|
118
|
+
console.log("pi-actors: dist is current");
|
|
119
|
+
} else {
|
|
120
|
+
replaceDist(candidate);
|
|
121
|
+
}
|
|
122
|
+
} finally {
|
|
123
|
+
rmSync(candidate, { recursive: true, force: true });
|
|
124
|
+
}
|