@llblab/pi-actors 0.48.0 → 0.49.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/AGENTS.md +1 -1
- package/BACKLOG.md +4 -1
- package/CHANGELOG.md +16 -0
- package/README.md +4 -4
- package/dist/lib/async-runs.d.ts +7 -0
- package/dist/lib/async-runs.js +63 -8
- package/dist/lib/extension-runtime.js +3 -0
- package/dist/lib/file-state.js +15 -1
- package/dist/lib/inspector.js +4 -0
- package/dist/lib/observability.js +2 -5
- package/dist/lib/recipes-discovery.js +3 -1
- package/dist/lib/recipes-references.d.ts +3 -0
- package/dist/lib/recipes-references.js +15 -0
- package/dist/lib/registry.d.ts +2 -0
- package/dist/lib/registry.js +8 -4
- package/dist/lib/runs-start.d.ts +2 -1
- package/dist/lib/runs-start.js +11 -5
- package/dist/lib/tools-local.js +12 -8
- package/dist/lib/tools.d.ts +1 -0
- package/dist/lib/tools.js +1 -0
- package/dist/skills/actors/SKILL.md +3 -3
- package/dist/skills/actors/references/persistent-tools.md +3 -1
- package/dist/skills/actors/references/recipes.md +2 -2
- package/dist/skills/actors/references/runs.md +2 -0
- package/dist/skills/actors/scripts/validate-recipe.mjs +12 -0
- package/dist/skills/music-player/SKILL.md +53 -0
- package/dist/skills/music-player/genapps/music-player.mjs +360 -0
- package/{skills/media/recipes/player.json → dist/skills/music-player/recipes/playback.json} +4 -3
- package/dist/skills/music-player/scripts/playback-client.mjs +143 -0
- package/dist/skills/{media/scripts/music-player.mjs → music-player/scripts/playback.mjs} +596 -53
- package/docs/async-runs.md +1 -1
- package/docs/recipe-library.md +2 -5
- package/docs/template-recipes.md +7 -0
- package/docs/tool-registry.md +2 -2
- package/lib/async-runs.ts +92 -15
- package/lib/extension-runtime.ts +4 -0
- package/lib/file-state.ts +14 -1
- package/lib/inspector.ts +4 -0
- package/lib/observability.ts +2 -5
- package/lib/recipes-discovery.ts +2 -1
- package/lib/recipes-references.ts +20 -0
- package/lib/registry.ts +8 -6
- package/lib/runs-start.ts +16 -6
- package/lib/tools-local.ts +10 -8
- package/lib/tools.ts +2 -0
- package/package.json +1 -1
- package/skills/actors/SKILL.md +3 -3
- package/skills/actors/references/persistent-tools.md +3 -1
- package/skills/actors/references/recipes.md +2 -2
- package/skills/actors/references/runs.md +2 -0
- package/skills/actors/scripts/validate-recipe.mjs +12 -0
- package/skills/music-player/SKILL.md +53 -0
- package/skills/music-player/genapps/music-player.mjs +360 -0
- package/{dist/skills/media/recipes/player.json → skills/music-player/recipes/playback.json} +4 -3
- package/skills/music-player/scripts/playback-client.mjs +143 -0
- package/skills/{media/scripts/music-player.mjs → music-player/scripts/playback.mjs} +596 -53
- package/dist/skills/media/SKILL.md +0 -44
- package/dist/skills/media/recipes/library.json +0 -45
- package/dist/skills/media/recipes/playlist-build.json +0 -15
- package/dist/skills/media/recipes/playlist-scan.json +0 -11
- package/dist/skills/media/scripts/media-utils.mjs +0 -47
- package/skills/media/SKILL.md +0 -44
- package/skills/media/recipes/library.json +0 -45
- package/skills/media/recipes/playlist-build.json +0 -15
- package/skills/media/recipes/playlist-scan.json +0 -11
- package/skills/media/scripts/media-utils.mjs +0 -47
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: media
|
|
3
|
-
description: Use for local media discovery, filtering, library summaries, playlist construction, or controllable playback.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Media
|
|
7
|
-
|
|
8
|
-
Use this Skill for caller-selected local media. For generic Recipe execution, persistent-tool setup, Run lifecycle, or diagnosis, follow `actors`; this Skill only selects media behavior.
|
|
9
|
-
|
|
10
|
-
## Choose the operation
|
|
11
|
-
|
|
12
|
-
| Intent | Recipe | Use when |
|
|
13
|
-
| --- | --- | --- |
|
|
14
|
-
| Start or control playback | `media/player` | A local file, directory, or playlist should become a controlled service |
|
|
15
|
-
| Build a library summary | `media/library` | A filtered playlist should feed bounded report content |
|
|
16
|
-
| Build a filtered playlist | `media/playlist-build` | Extension filtering and `paths`, `m3u`, or `inline` output are required |
|
|
17
|
-
| Scan a directory for raw file paths | `media/playlist-scan` | A shallow unfiltered inventory is intentionally sufficient |
|
|
18
|
-
|
|
19
|
-
Use `playlist-build` rather than `playlist-scan` for normal media selection. `library` composes playlist output into report content; its `artifact_path` identifies the intended report target but does not by itself prove a durable write.
|
|
20
|
-
|
|
21
|
-
## Controlled playback
|
|
22
|
-
|
|
23
|
-
`media/player` is an async controlled service. Start one owned Run, then use only its declared actions: `play`, `pause`, `resume`, `toggle`, `next`, `previous`, `stop`, and `status`.
|
|
24
|
-
|
|
25
|
-
```text
|
|
26
|
-
spawn recipe=media/player as=run:music values={"source":"~/Music","player":"auto"}
|
|
27
|
-
message target=run:music action=pause
|
|
28
|
-
inspect target=run:music view=control
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
For recurring playback as a persistent callable tool, use the persistent-capability workflow in `actors` with `from=media/player`; do not copy the player contract.
|
|
32
|
-
|
|
33
|
-
## Paths and selection
|
|
34
|
-
|
|
35
|
-
- `source` for playback must name caller-approved local media; do not broaden it to unrelated directories.
|
|
36
|
-
- `source_dir` must be an intended readable directory. `~` is accepted by maintained media helpers.
|
|
37
|
-
- Keep `max_depth` bounded. Use explicit comma-separated extensions for `playlist-build` and `library`.
|
|
38
|
-
- Choose `output_mode=m3u` only when playlist text is wanted; it does not create a playlist file.
|
|
39
|
-
- Choose an explicit `artifact_path` and model for `library` report generation.
|
|
40
|
-
- `player=auto` selects an available supported backend; do not install or substitute a player silently.
|
|
41
|
-
|
|
42
|
-
## Stop rules
|
|
43
|
-
|
|
44
|
-
Stop if the source is missing, unreadable, contains no selected media, or no supported player is available. Do not claim playback from process start alone; confirm through Run evidence or `status`. Prefer the declared `stop` action for a responsive player. If the service is unresponsive, return to `actors` for bounded Run recovery rather than shell process control.
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"async": true,
|
|
3
|
-
"imports": {
|
|
4
|
-
"playlist": "media/playlist-build",
|
|
5
|
-
"artifact": "artifacts/report"
|
|
6
|
-
},
|
|
7
|
-
"args": [
|
|
8
|
-
"source_dir:path",
|
|
9
|
-
"extensions:string",
|
|
10
|
-
"max_depth:int",
|
|
11
|
-
"output_mode:enum(paths,m3u,inline)",
|
|
12
|
-
"artifact_path:path",
|
|
13
|
-
"model:string",
|
|
14
|
-
"tools:string"
|
|
15
|
-
],
|
|
16
|
-
"defaults": {
|
|
17
|
-
"source_dir": "~/Music",
|
|
18
|
-
"extensions": ".mp3,.ogg,.wav,.flac,.m4a",
|
|
19
|
-
"max_depth": "2",
|
|
20
|
-
"output_mode": "m3u",
|
|
21
|
-
"artifact_path": "./media-library.md",
|
|
22
|
-
"tools": ""
|
|
23
|
-
},
|
|
24
|
-
"template": [
|
|
25
|
-
{
|
|
26
|
-
"name": "playlist",
|
|
27
|
-
"values": {
|
|
28
|
-
"source_dir": "{source_dir}",
|
|
29
|
-
"extensions": "{extensions}",
|
|
30
|
-
"max_depth": "{max_depth}",
|
|
31
|
-
"output_mode": "{output_mode}"
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"name": "artifact",
|
|
36
|
-
"values": {
|
|
37
|
-
"input": "Use playlist/media listing from stdin. Source directory: {source_dir}. Output mode: {output_mode}.",
|
|
38
|
-
"artifact_path": "{artifact_path}",
|
|
39
|
-
"artifact_format": "Markdown sections: Source, Playlist Output, Playback Notes, Next Actions.",
|
|
40
|
-
"model": "{model}",
|
|
41
|
-
"tools": "{tools}"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"args": [
|
|
3
|
-
"source_dir:path",
|
|
4
|
-
"extensions:string",
|
|
5
|
-
"max_depth:int",
|
|
6
|
-
"output_mode:enum(paths,m3u,inline)"
|
|
7
|
-
],
|
|
8
|
-
"defaults": {
|
|
9
|
-
"source_dir": "~/Music",
|
|
10
|
-
"extensions": ".mp3,.ogg,.wav,.flac,.m4a",
|
|
11
|
-
"max_depth": "2",
|
|
12
|
-
"output_mode": "paths"
|
|
13
|
-
},
|
|
14
|
-
"template": "{skill_dir}/scripts/media-utils.mjs playlist {source_dir} {extensions} {max_depth} {output_mode}"
|
|
15
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/** Media-owned playlist construction helper. */
|
|
4
|
-
|
|
5
|
-
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
6
|
-
import { extname, join, resolve } from "node:path";
|
|
7
|
-
|
|
8
|
-
function fail(message) {
|
|
9
|
-
console.error(message);
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function walkFiles(dir, maxDepth = 2, depth = 0, out = []) {
|
|
14
|
-
if (depth > maxDepth || !existsSync(dir)) return out;
|
|
15
|
-
for (const entry of readdirSync(dir)) {
|
|
16
|
-
const path = join(dir, entry);
|
|
17
|
-
const stat = statSync(path);
|
|
18
|
-
if (stat.isDirectory()) walkFiles(path, maxDepth, depth + 1, out);
|
|
19
|
-
else if (stat.isFile()) out.push(path);
|
|
20
|
-
}
|
|
21
|
-
return out;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function playlist(
|
|
25
|
-
sourceValue,
|
|
26
|
-
extensionsValue = ".mp3,.ogg,.wav,.flac,.m4a",
|
|
27
|
-
maxDepthValue = "2",
|
|
28
|
-
outputMode = "paths",
|
|
29
|
-
) {
|
|
30
|
-
const source = resolve(String(sourceValue).replace(/^~(?=\/|$)/, process.env.HOME ?? "~"));
|
|
31
|
-
const maxDepth = Number.parseInt(maxDepthValue, 10);
|
|
32
|
-
const extensions = new Set(extensionsValue.split(",").map((item) => item.trim().toLowerCase()).filter(Boolean));
|
|
33
|
-
const files = walkFiles(source, Number.isFinite(maxDepth) ? maxDepth : 2)
|
|
34
|
-
.filter((file) => extensions.has(extname(file).toLowerCase()))
|
|
35
|
-
.sort((a, b) => a.localeCompare(b));
|
|
36
|
-
if (outputMode === "m3u") console.log(["#EXTM3U", ...files].join("\n"));
|
|
37
|
-
else if (outputMode === "inline") console.log(files.join("|"));
|
|
38
|
-
else console.log(files.join("\n"));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const [command, ...args] = process.argv.slice(2);
|
|
42
|
-
try {
|
|
43
|
-
if (command === "playlist") playlist(args[0] ?? "~/Music", args[1], args[2], args[3]);
|
|
44
|
-
else fail(`Unknown media command: ${command ?? ""}`);
|
|
45
|
-
} catch (error) {
|
|
46
|
-
fail(error instanceof Error ? error.message : String(error));
|
|
47
|
-
}
|
package/skills/media/SKILL.md
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: media
|
|
3
|
-
description: Use for local media discovery, filtering, library summaries, playlist construction, or controllable playback.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Media
|
|
7
|
-
|
|
8
|
-
Use this Skill for caller-selected local media. For generic Recipe execution, persistent-tool setup, Run lifecycle, or diagnosis, follow `actors`; this Skill only selects media behavior.
|
|
9
|
-
|
|
10
|
-
## Choose the operation
|
|
11
|
-
|
|
12
|
-
| Intent | Recipe | Use when |
|
|
13
|
-
| --- | --- | --- |
|
|
14
|
-
| Start or control playback | `media/player` | A local file, directory, or playlist should become a controlled service |
|
|
15
|
-
| Build a library summary | `media/library` | A filtered playlist should feed bounded report content |
|
|
16
|
-
| Build a filtered playlist | `media/playlist-build` | Extension filtering and `paths`, `m3u`, or `inline` output are required |
|
|
17
|
-
| Scan a directory for raw file paths | `media/playlist-scan` | A shallow unfiltered inventory is intentionally sufficient |
|
|
18
|
-
|
|
19
|
-
Use `playlist-build` rather than `playlist-scan` for normal media selection. `library` composes playlist output into report content; its `artifact_path` identifies the intended report target but does not by itself prove a durable write.
|
|
20
|
-
|
|
21
|
-
## Controlled playback
|
|
22
|
-
|
|
23
|
-
`media/player` is an async controlled service. Start one owned Run, then use only its declared actions: `play`, `pause`, `resume`, `toggle`, `next`, `previous`, `stop`, and `status`.
|
|
24
|
-
|
|
25
|
-
```text
|
|
26
|
-
spawn recipe=media/player as=run:music values={"source":"~/Music","player":"auto"}
|
|
27
|
-
message target=run:music action=pause
|
|
28
|
-
inspect target=run:music view=control
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
For recurring playback as a persistent callable tool, use the persistent-capability workflow in `actors` with `from=media/player`; do not copy the player contract.
|
|
32
|
-
|
|
33
|
-
## Paths and selection
|
|
34
|
-
|
|
35
|
-
- `source` for playback must name caller-approved local media; do not broaden it to unrelated directories.
|
|
36
|
-
- `source_dir` must be an intended readable directory. `~` is accepted by maintained media helpers.
|
|
37
|
-
- Keep `max_depth` bounded. Use explicit comma-separated extensions for `playlist-build` and `library`.
|
|
38
|
-
- Choose `output_mode=m3u` only when playlist text is wanted; it does not create a playlist file.
|
|
39
|
-
- Choose an explicit `artifact_path` and model for `library` report generation.
|
|
40
|
-
- `player=auto` selects an available supported backend; do not install or substitute a player silently.
|
|
41
|
-
|
|
42
|
-
## Stop rules
|
|
43
|
-
|
|
44
|
-
Stop if the source is missing, unreadable, contains no selected media, or no supported player is available. Do not claim playback from process start alone; confirm through Run evidence or `status`. Prefer the declared `stop` action for a responsive player. If the service is unresponsive, return to `actors` for bounded Run recovery rather than shell process control.
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"async": true,
|
|
3
|
-
"imports": {
|
|
4
|
-
"playlist": "media/playlist-build",
|
|
5
|
-
"artifact": "artifacts/report"
|
|
6
|
-
},
|
|
7
|
-
"args": [
|
|
8
|
-
"source_dir:path",
|
|
9
|
-
"extensions:string",
|
|
10
|
-
"max_depth:int",
|
|
11
|
-
"output_mode:enum(paths,m3u,inline)",
|
|
12
|
-
"artifact_path:path",
|
|
13
|
-
"model:string",
|
|
14
|
-
"tools:string"
|
|
15
|
-
],
|
|
16
|
-
"defaults": {
|
|
17
|
-
"source_dir": "~/Music",
|
|
18
|
-
"extensions": ".mp3,.ogg,.wav,.flac,.m4a",
|
|
19
|
-
"max_depth": "2",
|
|
20
|
-
"output_mode": "m3u",
|
|
21
|
-
"artifact_path": "./media-library.md",
|
|
22
|
-
"tools": ""
|
|
23
|
-
},
|
|
24
|
-
"template": [
|
|
25
|
-
{
|
|
26
|
-
"name": "playlist",
|
|
27
|
-
"values": {
|
|
28
|
-
"source_dir": "{source_dir}",
|
|
29
|
-
"extensions": "{extensions}",
|
|
30
|
-
"max_depth": "{max_depth}",
|
|
31
|
-
"output_mode": "{output_mode}"
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"name": "artifact",
|
|
36
|
-
"values": {
|
|
37
|
-
"input": "Use playlist/media listing from stdin. Source directory: {source_dir}. Output mode: {output_mode}.",
|
|
38
|
-
"artifact_path": "{artifact_path}",
|
|
39
|
-
"artifact_format": "Markdown sections: Source, Playlist Output, Playback Notes, Next Actions.",
|
|
40
|
-
"model": "{model}",
|
|
41
|
-
"tools": "{tools}"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"args": [
|
|
3
|
-
"source_dir:path",
|
|
4
|
-
"extensions:string",
|
|
5
|
-
"max_depth:int",
|
|
6
|
-
"output_mode:enum(paths,m3u,inline)"
|
|
7
|
-
],
|
|
8
|
-
"defaults": {
|
|
9
|
-
"source_dir": "~/Music",
|
|
10
|
-
"extensions": ".mp3,.ogg,.wav,.flac,.m4a",
|
|
11
|
-
"max_depth": "2",
|
|
12
|
-
"output_mode": "paths"
|
|
13
|
-
},
|
|
14
|
-
"template": "{skill_dir}/scripts/media-utils.mjs playlist {source_dir} {extensions} {max_depth} {output_mode}"
|
|
15
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/** Media-owned playlist construction helper. */
|
|
4
|
-
|
|
5
|
-
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
6
|
-
import { extname, join, resolve } from "node:path";
|
|
7
|
-
|
|
8
|
-
function fail(message) {
|
|
9
|
-
console.error(message);
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function walkFiles(dir, maxDepth = 2, depth = 0, out = []) {
|
|
14
|
-
if (depth > maxDepth || !existsSync(dir)) return out;
|
|
15
|
-
for (const entry of readdirSync(dir)) {
|
|
16
|
-
const path = join(dir, entry);
|
|
17
|
-
const stat = statSync(path);
|
|
18
|
-
if (stat.isDirectory()) walkFiles(path, maxDepth, depth + 1, out);
|
|
19
|
-
else if (stat.isFile()) out.push(path);
|
|
20
|
-
}
|
|
21
|
-
return out;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function playlist(
|
|
25
|
-
sourceValue,
|
|
26
|
-
extensionsValue = ".mp3,.ogg,.wav,.flac,.m4a",
|
|
27
|
-
maxDepthValue = "2",
|
|
28
|
-
outputMode = "paths",
|
|
29
|
-
) {
|
|
30
|
-
const source = resolve(String(sourceValue).replace(/^~(?=\/|$)/, process.env.HOME ?? "~"));
|
|
31
|
-
const maxDepth = Number.parseInt(maxDepthValue, 10);
|
|
32
|
-
const extensions = new Set(extensionsValue.split(",").map((item) => item.trim().toLowerCase()).filter(Boolean));
|
|
33
|
-
const files = walkFiles(source, Number.isFinite(maxDepth) ? maxDepth : 2)
|
|
34
|
-
.filter((file) => extensions.has(extname(file).toLowerCase()))
|
|
35
|
-
.sort((a, b) => a.localeCompare(b));
|
|
36
|
-
if (outputMode === "m3u") console.log(["#EXTM3U", ...files].join("\n"));
|
|
37
|
-
else if (outputMode === "inline") console.log(files.join("|"));
|
|
38
|
-
else console.log(files.join("\n"));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const [command, ...args] = process.argv.slice(2);
|
|
42
|
-
try {
|
|
43
|
-
if (command === "playlist") playlist(args[0] ?? "~/Music", args[1], args[2], args[3]);
|
|
44
|
-
else fail(`Unknown media command: ${command ?? ""}`);
|
|
45
|
-
} catch (error) {
|
|
46
|
-
fail(error instanceof Error ? error.message : String(error));
|
|
47
|
-
}
|