@sogni-ai/sogni-creative-agent-skill 3.3.5 → 3.4.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 +26 -4
- package/SKILL.md +79 -10
- package/generated/creative-agent-runtime.mjs +50 -18
- package/node-version-check.mjs +37 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +5 -3
- package/scripts/check-creative-agent-runtime.mjs +1 -3
- package/scripts/sync-creative-agent-runtime.mjs +98 -0
- package/skill-package.json +1 -1
- package/sogni-agent.mjs +698 -91
- package/version.mjs +1 -1
package/README.md
CHANGED
|
@@ -70,14 +70,23 @@ With this skill, an agent can:
|
|
|
70
70
|
## Quick Start
|
|
71
71
|
|
|
72
72
|
1. Get a Sogni API key from [dashboard.sogni.ai](https://dashboard.sogni.ai) (open the account menu) and save it — see [Setup](#setup-sogni-api-key).
|
|
73
|
-
2. Install
|
|
73
|
+
2. Install (one command):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx setup-sogni-agent-skill
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This auto-detects Claude Code, Codex CLI, and Hermes; installs the CLI globally;
|
|
80
|
+
registers the skill into each detected runtime; and prompts for your API key.
|
|
81
|
+
|
|
82
|
+
Prefer to do it manually? Install the CLI directly:
|
|
74
83
|
|
|
75
84
|
```bash
|
|
76
85
|
npm install -g @sogni-ai/sogni-creative-agent-skill@latest
|
|
77
86
|
sogni-agent --version
|
|
78
87
|
```
|
|
79
88
|
|
|
80
|
-
|
|
89
|
+
Then point your agent runtime at this repository's [`SKILL.md`](./SKILL.md).
|
|
81
90
|
|
|
82
91
|
Then ask your agent to do something:
|
|
83
92
|
|
|
@@ -133,6 +142,8 @@ openclaw plugins install sogni-creative-agent-skill
|
|
|
133
142
|
|
|
134
143
|
The installed plugin loads its behavior from [`SKILL.md`](./SKILL.md) via [`openclaw.plugin.json`](./openclaw.plugin.json).
|
|
135
144
|
|
|
145
|
+
> **API key under OpenClaw:** the plugin config holds non-secret defaults only (models, timeouts, paths) — it does **not** carry your API key. Provide `SOGNI_API_KEY` via the environment the OpenClaw gateway passes to the CLI, or save it to `~/.config/sogni/credentials` (`SOGNI_API_KEY=<your-key>`). This keeps your key out of plugin config files.
|
|
146
|
+
|
|
136
147
|
For a local checkout that you want to update continuously, link the minimal OpenClaw surface (`.openclaw-link/`) — not the repository root, which contains development tests that OpenClaw correctly blocks during plugin safety scanning:
|
|
137
148
|
|
|
138
149
|
```bash
|
|
@@ -451,7 +462,7 @@ LTX-2.3 prompt: "A medium cinematic shot frames a woman in her 30s standing in a
|
|
|
451
462
|
|
|
452
463
|
## Photobooth (Face Transfer)
|
|
453
464
|
|
|
454
|
-
Generate stylized portraits from a face photo using InstantID ControlNet:
|
|
465
|
+
Generate new stylized portraits from a face photo using InstantID ControlNet:
|
|
455
466
|
|
|
456
467
|
```bash
|
|
457
468
|
sogni-agent --photobooth --ref face.jpg "80s fashion portrait"
|
|
@@ -460,6 +471,8 @@ sogni-agent --photobooth --ref face.jpg -n 4 "LinkedIn professional headshot"
|
|
|
460
471
|
|
|
461
472
|
Uses SDXL Turbo (`coreml-sogniXLturbo_alpha1_ad`) at 1024×1024 by default. The face image is passed via `--ref` and styled by the prompt. Cannot be combined with `--video` or `-c` / `--context`.
|
|
462
473
|
|
|
474
|
+
`--photobooth` is face-reference generation, not full-image editing. If the request is "same image, different style" — for example an anime version that must keep the same face, pose, clothing, background, framing, and composition — use Qwen image editing with `-c/--context` instead.
|
|
475
|
+
|
|
463
476
|
Multi-angle mode (`--multi-angle` / `--angles-360`) auto-builds the `<sks>` prompt and applies the `multiple_angles` LoRA. `--angles-360-video` generates i2v clips between consecutive angles (including last → first) and concatenates them with `ffmpeg` into a seamless loop.
|
|
464
477
|
|
|
465
478
|
`--balance` / `--balances` does not require a prompt and prints current `SPARK` and `SOGNI` balances before exiting.
|
|
@@ -577,7 +590,7 @@ Use `--token-type auto` to retry native Sogni models with SOGNI tokens when SPAR
|
|
|
577
590
|
sogni-agent --token-type auto "a dragon eating tacos"
|
|
578
591
|
```
|
|
579
592
|
|
|
580
|
-
Tries SPARK first
|
|
593
|
+
Tries SPARK first, then falls back to SOGNI if the balance is too low. Vendor models such as Seedance and GPT Image 2 require Premium Spark eligibility and never use SOGNI fallback. If usable balance is still insufficient, buy Spark Packs at https://docs.sogni.ai/pricing/#spark-packs.
|
|
581
594
|
|
|
582
595
|
---
|
|
583
596
|
|
|
@@ -632,6 +645,15 @@ Reusable workflow rules should come from the shared Sogni runtime before they ar
|
|
|
632
645
|
|
|
633
646
|
Public-skill regex should stay limited to CLI argument/fact extraction such as file paths, URLs, extensions, dimensions, durations, and explicit positions. Hosted-style decisions such as latest-video continuation, uploaded-video modification, image-selection waits, stitch-after-batch state, and repair/control routing belong upstream in typed planner/runtime fields before they are synced here.
|
|
634
647
|
|
|
648
|
+
### Execution boundaries: local CLI vs. hosted surfaces
|
|
649
|
+
|
|
650
|
+
`sogni-agent.mjs` is a **local command-line tool** (`#!/usr/bin/env node`, the package `bin`). It is the only place that may assume a local filesystem and a local `ffmpeg`/`ffprobe` binary. Flags like `--concat-videos`, `--remix-audio`, `--extract-first-frame`, `--extract-last-frame`, and `--angles-360-video` shell out to ffmpeg behind `ensureFfmpegAvailable()` and run only when those flags are passed.
|
|
651
|
+
|
|
652
|
+
Hosted surfaces — including the chat.sogni.ai web app — do **not** run `sogni-agent.mjs`. They consume `@sogni-ai/sogni-intelligence-client` and the hosted `/v1/chat/completions` and `/v1/creative-agent/workflows` APIs, where there is no local ffmpeg and no local filesystem. Therefore:
|
|
653
|
+
|
|
654
|
+
- Keep ffmpeg- and filesystem-dependent helpers (frame extraction, concat, audio remux, media listing) **local to `sogni-agent.mjs`**. Do not move them into the shared runtime or `@sogni-ai/sogni-intelligence-client`, and never make hosted code paths depend on a local binary.
|
|
655
|
+
- Server-side equivalents of these capabilities (e.g. `stitch_video`, `overlay_video`, `extend_video`) live in the hosted creative-agent tool surface and belong upstream, not in the CLI.
|
|
656
|
+
|
|
635
657
|
Issues and feature requests: [github.com/Sogni-AI/sogni-creative-agent-skill/issues](https://github.com/Sogni-AI/sogni-creative-agent-skill/issues).
|
|
636
658
|
|
|
637
659
|
---
|
package/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: sogni-creative-agent-skill
|
|
3
3
|
description: "Sogni Creative Agent Skill: agent skill and CLI for image, video, and music generation using Sogni AI's decentralized GPU network. Supports personas (named people with saved reference photos and voice clips), persistent memories, custom personality, style transfer, angle synthesis, Seedance/LTX/WAN video, music/lyrics, hosted chat, durable workflows, replay records, and multi-step creative workflows. Ask the agent to \"draw\", \"generate\", \"create an image\", \"make a video/animate\", \"make music\", \"apply a style\", or \"generate me as a superhero\"."
|
|
4
4
|
metadata:
|
|
5
|
-
version: "3.
|
|
5
|
+
version: "3.4.0"
|
|
6
6
|
homepage: https://sogni.ai
|
|
7
7
|
clawdbot:
|
|
8
8
|
emoji: "🎨"
|
|
@@ -72,6 +72,10 @@ npm --prefix "$DEST" install
|
|
|
72
72
|
|
|
73
73
|
If that checkout does not exist, prefer the npm-based local skill install below, or ask before cloning.
|
|
74
74
|
|
|
75
|
+
## Uninstall Request Policy
|
|
76
|
+
|
|
77
|
+
When a user asks to uninstall, run `npx setup-sogni-agent-skill --uninstall --remove-cli --purge`. This removes the skill files, the global CLI, and the user's data in `~/.config/sogni/` after backing it up to `~/.config/sogni.backup-<timestamp>.tar.gz`. Always tell the user the backup path and that it contains their API key. To keep their data, omit `--purge`.
|
|
78
|
+
|
|
75
79
|
## Setup
|
|
76
80
|
|
|
77
81
|
1. **Get your Sogni API key** by logging into https://dashboard.sogni.ai and opening the account menu.
|
|
@@ -463,9 +467,19 @@ positions.
|
|
|
463
467
|
| `--strict-size` | Do not auto-adjust i2v video size for reference resizing constraints | false |
|
|
464
468
|
| `-q, --quiet` | No progress output | false |
|
|
465
469
|
| `--extract-last-frame <video> <image>` | Extract last frame from video (safe ffmpeg wrapper) | - |
|
|
466
|
-
| `--
|
|
470
|
+
| `--extract-first-frame <video> <image>` | Extract first frame from video (safe ffmpeg wrapper) | - |
|
|
471
|
+
| `--concat-videos <out> <clips...>` | Concatenate video clips; normalizes fps/size and fills silent audio so mismatched clips stitch cleanly (safe ffmpeg wrapper) | - |
|
|
472
|
+
| `--concat-fps <n>` | Override target fps for `--concat-videos` | highest clip fps |
|
|
467
473
|
| `--concat-audio <path>` | Optional audio track to mux over `--concat-videos` output | - |
|
|
468
474
|
| `--concat-audio-start <sec>` | Start offset into `--concat-audio` | - |
|
|
475
|
+
| `--remix-audio <in> <out>` | Rebuild a video's audio (loop/fade/mix) without re-encoding video (safe ffmpeg wrapper) | - |
|
|
476
|
+
| `--bed-audio <path>` | Audio bed for `--remix-audio` (path or video; defaults to input's own audio) | - |
|
|
477
|
+
| `--audio-loop` | Loop the bed to cover the full video duration (`--remix-audio`) | false |
|
|
478
|
+
| `--audio-fade-in <sec>` | Fade the bed in over `<sec>` (`--remix-audio`) | - |
|
|
479
|
+
| `--audio-fade-out <sec>` | Fade the bed out over `<sec>` at the tail (`--remix-audio`) | - |
|
|
480
|
+
| `--mix-audio <path>` | Overlay one extra audio track, mixed with the bed (`--remix-audio`) | - |
|
|
481
|
+
| `--mix-at <sec>` | Start offset for `--mix-audio` | 0 |
|
|
482
|
+
| `--mix-gain <db>` | Gain in dB applied to `--mix-audio` | 0 |
|
|
469
483
|
| `--list-media [type]` | List recent inbound media (images\|audio\|all) | images |
|
|
470
484
|
| `--api-chat` | Call OpenAI-compatible `/v1/chat/completions`; CLI default sends the hosted `creative-agent` tool surface | - |
|
|
471
485
|
| `--durable-chat` | Start and stream a durable `/v1/chat/runs` record through SDK transport; requires `SOGNI_SKILL_USE_SDK_TRANSPORT=1` | - |
|
|
@@ -665,9 +679,17 @@ sogni-agent --last-image "make it more vibrant"
|
|
|
665
679
|
|
|
666
680
|
When context images are provided without `-m`, defaults to `qwen_image_edit_2511_fp8_lightning`. Select `-m gpt-image-2` for GPT Image 2's higher reference-image limit and OpenAI-backed image editing.
|
|
667
681
|
|
|
682
|
+
Use context-image editing for source-preserving edits. If the user says "use this image as the base", "keep everything the same", "only change the style", "anime version of this image", or asks to preserve pose, clothing, background, framing, or composition, use `-c/--context` with a Qwen image edit model instead of `--photobooth`. For stronger preservation than the lightning default, prefer:
|
|
683
|
+
|
|
684
|
+
```bash
|
|
685
|
+
sogni-agent -c photo.jpg -m qwen_image_edit_2511_fp8 "turn this into anime style; keep the same face, pose, clothing, background, framing, and composition"
|
|
686
|
+
```
|
|
687
|
+
|
|
668
688
|
## Photobooth (Face Transfer)
|
|
669
689
|
|
|
670
|
-
Generate stylized portraits from a face photo using InstantID ControlNet.
|
|
690
|
+
Generate new stylized portraits from a face photo using InstantID ControlNet. Use `--photobooth` with `--ref` when the user explicitly asks for photobooth/face-transfer mode, wants a new portrait or headshot based on their face, or asks to place their face identity into a different portrait concept.
|
|
691
|
+
|
|
692
|
+
Do not use `--photobooth` for full-image style edits where the original photo must stay intact. `--photobooth` treats the input as a face reference, not as a base image, so it can change pose, clothing, background, framing, and composition. For "same image, different style" requests, route to Qwen context editing with `-c/--context`.
|
|
671
693
|
|
|
672
694
|
```bash
|
|
673
695
|
# Basic photobooth
|
|
@@ -744,7 +766,7 @@ When a user requests a "360 video", follow this workflow:
|
|
|
744
766
|
|
|
745
767
|
### Transition Video Rule
|
|
746
768
|
|
|
747
|
-
For **any transition video work**, always use the **Sogni skill/plugin** (not raw ffmpeg or other shell commands). Use the built-in `--extract-last-frame`, `--concat-videos`, and `--looping` flags for video manipulation.
|
|
769
|
+
For **any transition video work**, always use the **Sogni skill/plugin** (not raw ffmpeg or other shell commands). Use the built-in `--extract-last-frame`, `--extract-first-frame`, `--concat-videos`, `--remix-audio`, and `--looping` flags for video and audio manipulation.
|
|
748
770
|
|
|
749
771
|
### Insufficient Funds Handling
|
|
750
772
|
|
|
@@ -752,7 +774,9 @@ Use `--token-type auto` to automatically retry native Sogni models with SOGNI to
|
|
|
752
774
|
|
|
753
775
|
When you see **"Debit Error: Insufficient funds"** even with auto-fallback, reply:
|
|
754
776
|
|
|
755
|
-
"Insufficient funds.
|
|
777
|
+
"Insufficient funds. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
778
|
+
|
|
779
|
+
Do not collect payment details, quote a custom price, or simulate a purchase in the terminal.
|
|
756
780
|
|
|
757
781
|
## Video Generation
|
|
758
782
|
|
|
@@ -877,10 +901,11 @@ sogni-agent --json --list-media images
|
|
|
877
901
|
|
|
878
902
|
**Do NOT use `ls`, `cp`, or other shell commands to browse user files.** Always use `--list-media` to find inbound media.
|
|
879
903
|
|
|
880
|
-
##
|
|
904
|
+
## Photobooth Routing Rule
|
|
881
905
|
|
|
882
|
-
- If the user
|
|
883
|
-
-
|
|
906
|
+
- If the user explicitly asks to use "photobooth", "photobooth path", or "face transfer", use `--photobooth` with `--ref` set to the user-provided face image.
|
|
907
|
+
- If the same request also requires preserving the whole source image (same pose, clothes, background, framing, composition, or "keep everything the same"), explain that photobooth is face-reference generation and prefer Qwen context editing unless the user insists on photobooth.
|
|
908
|
+
- Do not route to `--photobooth` merely because the user asks to preserve a face in a style edit. Face-preserving full-image edits should use `-c/--context` with Qwen image edit.
|
|
884
909
|
|
|
885
910
|
## LTX-2.3 Prompt Rule
|
|
886
911
|
|
|
@@ -1017,7 +1042,7 @@ If clips need different source images, end frames, durations, audio windows, or
|
|
|
1017
1042
|
|
|
1018
1043
|
### Token Auto-Fallback
|
|
1019
1044
|
|
|
1020
|
-
Use `--token-type auto` when the user's SPARK balance might be low. It tries SPARK first
|
|
1045
|
+
Use `--token-type auto` when the user's SPARK balance might be low. It tries SPARK first and automatically retries with SOGNI if insufficient.
|
|
1021
1046
|
|
|
1022
1047
|
## High-Res Video Routing
|
|
1023
1048
|
|
|
@@ -1032,7 +1057,7 @@ When the user asks for video in **"hd"**, **"1080p"**, **"4k"**, **"uhd"**, or *
|
|
|
1032
1057
|
- Rewrite the user's request using the **LTX-2.3 Prompt Rule** before invoking the command. Do not send short slogan-style prompts to LTX.
|
|
1033
1058
|
- Treat "4k" as a signal to use the highest practical LTX path exposed by this skill, even though the current wrapper caps non-WAN video dimensions at 2048px on the long side.
|
|
1034
1059
|
|
|
1035
|
-
**Security:** Agents must use the CLI's built-in flags (`--extract-last-frame`, `--concat-videos`, `--list-media`) for all file operations and video manipulation. Never run raw shell commands (`ffmpeg`, `ls`, `cp`, etc.) directly.
|
|
1060
|
+
**Security:** Agents must use the CLI's built-in flags (`--extract-last-frame`, `--extract-first-frame`, `--concat-videos`, `--remix-audio`, `--list-media`) for all file operations and video/audio manipulation. Never run raw shell commands (`ffmpeg`, `ls`, `cp`, etc.) directly.
|
|
1036
1061
|
|
|
1037
1062
|
## Animate Between Two Images (First-Frame / Last-Frame)
|
|
1038
1063
|
|
|
@@ -1071,6 +1096,50 @@ When the final stitched output needs a single external soundtrack, add `--concat
|
|
|
1071
1096
|
- User says "animate this video to this image" → extract last frame, use as `--ref`, target image as `--ref-end`, then stitch
|
|
1072
1097
|
- User says "continue this video" with a target image → same as above
|
|
1073
1098
|
|
|
1099
|
+
### Transition Between Two Videos (Bridge Clip)
|
|
1100
|
+
|
|
1101
|
+
When a user asks to **create a transition between two existing videos** (A → B), bridge them with a generated clip anchored on both boundary frames:
|
|
1102
|
+
|
|
1103
|
+
1. **Extract the last frame of video A** and the **first frame of video B**:
|
|
1104
|
+
```bash
|
|
1105
|
+
sogni-agent --extract-last-frame ./videoA.mp4 ./A_last.png
|
|
1106
|
+
sogni-agent --extract-first-frame ./videoB.mp4 ./B_first.png
|
|
1107
|
+
```
|
|
1108
|
+
2. **Generate the transition** with i2v, anchoring start→end so both seams are clean. Match `--fps` to the surrounding clips:
|
|
1109
|
+
```bash
|
|
1110
|
+
sogni-agent -q --video -m wan_v2.2-14b-fp8_i2v_lightx2v \
|
|
1111
|
+
--ref ./A_last.png --ref-end ./B_first.png --fps 24 \
|
|
1112
|
+
-o ./transition.mp4 "descriptive morph between the two scenes"
|
|
1113
|
+
```
|
|
1114
|
+
3. **Concatenate A → transition → B**:
|
|
1115
|
+
```bash
|
|
1116
|
+
sogni-agent --concat-videos ./merged.mp4 ./videoA.mp4 ./transition.mp4 ./videoB.mp4
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
> **i2v clips are silent and use the model's own frame rate** (often not 24). `--concat-videos` now normalizes fps/size and fills silent audio automatically, so mismatched clips stitch correctly — but passing `--fps` to the transition generation keeps things clean from the start. Use `--concat-fps <n>` to force a specific output frame rate.
|
|
1120
|
+
|
|
1121
|
+
### Remix / Layer Audio After Stitching
|
|
1122
|
+
|
|
1123
|
+
After concatenating, use `--remix-audio` to rebuild the audio track **without re-encoding the video** (it is stream-copied, so it is fast and lossless on the picture). Combine the audio flags:
|
|
1124
|
+
|
|
1125
|
+
```bash
|
|
1126
|
+
# Loop one clip's audio across the whole merged video and fade it out at the end
|
|
1127
|
+
sogni-agent --remix-audio ./merged.mp4 ./final.mp4 \
|
|
1128
|
+
--bed-audio ./clip1.mp4 --audio-loop --audio-fade-out 2
|
|
1129
|
+
|
|
1130
|
+
# Same, but also layer a second clip's original audio back in starting at 18s
|
|
1131
|
+
sogni-agent --remix-audio ./merged.mp4 ./final.mp4 \
|
|
1132
|
+
--bed-audio ./clip1.mp4 --audio-loop --audio-fade-out 2 \
|
|
1133
|
+
--mix-audio ./clip3.mp4 --mix-at 18.01 --mix-gain -3
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
- `--bed-audio` accepts a video or audio file; if omitted, the input video's own audio is the bed.
|
|
1137
|
+
- `--audio-loop` loops the bed to cover the full video; `--audio-fade-in` / `--audio-fade-out` fade it.
|
|
1138
|
+
- `--mix-audio` overlays one extra track (mixed at full level with a peak limiter so it never clips); position it with `--mix-at` and adjust level with `--mix-gain` (dB).
|
|
1139
|
+
- To mix more than two layers, chain `--remix-audio` passes (each only re-encodes audio).
|
|
1140
|
+
|
|
1141
|
+
**Do NOT run raw `ffmpeg` commands** for any of this. Use `--extract-first-frame`, `--extract-last-frame`, `--concat-videos`, and `--remix-audio`.
|
|
1142
|
+
|
|
1074
1143
|
## JSON Output
|
|
1075
1144
|
|
|
1076
1145
|
```json
|
|
@@ -556,7 +556,7 @@ const REPAIR_RECIPES = [
|
|
|
556
556
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
557
557
|
"mode": "stopAndAsk",
|
|
558
558
|
"maxRetries": 0,
|
|
559
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
559
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
560
560
|
},
|
|
561
561
|
{
|
|
562
562
|
"recipeId": "edit_image.cost_limit_exceeded",
|
|
@@ -565,7 +565,7 @@ const REPAIR_RECIPES = [
|
|
|
565
565
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
566
566
|
"mode": "stopAndAsk",
|
|
567
567
|
"maxRetries": 0,
|
|
568
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
568
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
569
569
|
},
|
|
570
570
|
{
|
|
571
571
|
"recipeId": "restore_photo.cost_limit_exceeded",
|
|
@@ -574,7 +574,7 @@ const REPAIR_RECIPES = [
|
|
|
574
574
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
575
575
|
"mode": "stopAndAsk",
|
|
576
576
|
"maxRetries": 0,
|
|
577
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
577
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
578
578
|
},
|
|
579
579
|
{
|
|
580
580
|
"recipeId": "apply_style.cost_limit_exceeded",
|
|
@@ -583,7 +583,7 @@ const REPAIR_RECIPES = [
|
|
|
583
583
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
584
584
|
"mode": "stopAndAsk",
|
|
585
585
|
"maxRetries": 0,
|
|
586
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
586
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
587
587
|
},
|
|
588
588
|
{
|
|
589
589
|
"recipeId": "refine_result.cost_limit_exceeded",
|
|
@@ -592,7 +592,7 @@ const REPAIR_RECIPES = [
|
|
|
592
592
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
593
593
|
"mode": "stopAndAsk",
|
|
594
594
|
"maxRetries": 0,
|
|
595
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
595
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
596
596
|
},
|
|
597
597
|
{
|
|
598
598
|
"recipeId": "animate_photo.cost_limit_exceeded",
|
|
@@ -601,7 +601,7 @@ const REPAIR_RECIPES = [
|
|
|
601
601
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
602
602
|
"mode": "stopAndAsk",
|
|
603
603
|
"maxRetries": 0,
|
|
604
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
604
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
605
605
|
},
|
|
606
606
|
{
|
|
607
607
|
"recipeId": "change_angle.cost_limit_exceeded",
|
|
@@ -610,7 +610,7 @@ const REPAIR_RECIPES = [
|
|
|
610
610
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
611
611
|
"mode": "stopAndAsk",
|
|
612
612
|
"maxRetries": 0,
|
|
613
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
613
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
614
614
|
},
|
|
615
615
|
{
|
|
616
616
|
"recipeId": "generate_video.cost_limit_exceeded",
|
|
@@ -619,7 +619,7 @@ const REPAIR_RECIPES = [
|
|
|
619
619
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
620
620
|
"mode": "stopAndAsk",
|
|
621
621
|
"maxRetries": 0,
|
|
622
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
622
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
623
623
|
},
|
|
624
624
|
{
|
|
625
625
|
"recipeId": "sound_to_video.cost_limit_exceeded",
|
|
@@ -628,7 +628,7 @@ const REPAIR_RECIPES = [
|
|
|
628
628
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
629
629
|
"mode": "stopAndAsk",
|
|
630
630
|
"maxRetries": 0,
|
|
631
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
631
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
632
632
|
},
|
|
633
633
|
{
|
|
634
634
|
"recipeId": "video_to_video.cost_limit_exceeded",
|
|
@@ -637,7 +637,7 @@ const REPAIR_RECIPES = [
|
|
|
637
637
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
638
638
|
"mode": "stopAndAsk",
|
|
639
639
|
"maxRetries": 0,
|
|
640
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
640
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
641
641
|
},
|
|
642
642
|
{
|
|
643
643
|
"recipeId": "generate_music.cost_limit_exceeded",
|
|
@@ -646,7 +646,7 @@ const REPAIR_RECIPES = [
|
|
|
646
646
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
647
647
|
"mode": "stopAndAsk",
|
|
648
648
|
"maxRetries": 0,
|
|
649
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
649
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
650
650
|
},
|
|
651
651
|
{
|
|
652
652
|
"recipeId": "extend_video.cost_limit_exceeded",
|
|
@@ -655,7 +655,7 @@ const REPAIR_RECIPES = [
|
|
|
655
655
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
656
656
|
"mode": "stopAndAsk",
|
|
657
657
|
"maxRetries": 0,
|
|
658
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
658
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
659
659
|
},
|
|
660
660
|
{
|
|
661
661
|
"recipeId": "replace_video_segment.cost_limit_exceeded",
|
|
@@ -664,7 +664,7 @@ const REPAIR_RECIPES = [
|
|
|
664
664
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
665
665
|
"mode": "stopAndAsk",
|
|
666
666
|
"maxRetries": 0,
|
|
667
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
667
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
668
668
|
},
|
|
669
669
|
{
|
|
670
670
|
"recipeId": "overlay_video.cost_limit_exceeded",
|
|
@@ -673,7 +673,7 @@ const REPAIR_RECIPES = [
|
|
|
673
673
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
674
674
|
"mode": "stopAndAsk",
|
|
675
675
|
"maxRetries": 0,
|
|
676
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
676
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
677
677
|
},
|
|
678
678
|
{
|
|
679
679
|
"recipeId": "add_subtitles.cost_limit_exceeded",
|
|
@@ -682,7 +682,7 @@ const REPAIR_RECIPES = [
|
|
|
682
682
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
683
683
|
"mode": "stopAndAsk",
|
|
684
684
|
"maxRetries": 0,
|
|
685
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
685
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
686
686
|
},
|
|
687
687
|
{
|
|
688
688
|
"recipeId": "stitch_video.cost_limit_exceeded",
|
|
@@ -691,7 +691,7 @@ const REPAIR_RECIPES = [
|
|
|
691
691
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
692
692
|
"mode": "stopAndAsk",
|
|
693
693
|
"maxRetries": 0,
|
|
694
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
694
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
695
695
|
},
|
|
696
696
|
{
|
|
697
697
|
"recipeId": "orbit_video.cost_limit_exceeded",
|
|
@@ -700,7 +700,7 @@ const REPAIR_RECIPES = [
|
|
|
700
700
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
701
701
|
"mode": "stopAndAsk",
|
|
702
702
|
"maxRetries": 0,
|
|
703
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
703
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
704
704
|
},
|
|
705
705
|
{
|
|
706
706
|
"recipeId": "dance_montage.cost_limit_exceeded",
|
|
@@ -709,7 +709,7 @@ const REPAIR_RECIPES = [
|
|
|
709
709
|
"errorCode": "COST_LIMIT_EXCEEDED",
|
|
710
710
|
"mode": "stopAndAsk",
|
|
711
711
|
"maxRetries": 0,
|
|
712
|
-
"repairNoteTemplate": "You have hit the credit limit for this turn.
|
|
712
|
+
"repairNoteTemplate": "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs"
|
|
713
713
|
},
|
|
714
714
|
{
|
|
715
715
|
"recipeId": "generate_image.asset_not_found",
|
|
@@ -2550,6 +2550,38 @@ const PROMPT_CONTRACTS = [
|
|
|
2550
2550
|
// '../public-skill-runtime/index.js' (and downstream consumers that still
|
|
2551
2551
|
// reference the creative-agent subpath) keep working unchanged.
|
|
2552
2552
|
export * from '@sogni-ai/sogni-intelligence-client/public-skill-runtime';
|
|
2553
|
+
|
|
2554
|
+
// PUBLIC_SKILL_SPARK_PACKS_COST_LIMIT_OVERRIDE
|
|
2555
|
+
import {
|
|
2556
|
+
PUBLIC_SKILL_DEFAULT_POLICIES as SOGNI_PUBLIC_SKILL_DEFAULT_POLICIES,
|
|
2557
|
+
PUBLIC_SKILL_DEFAULT_PROMPT_CONTRACTS as SOGNI_PUBLIC_SKILL_DEFAULT_PROMPT_CONTRACTS,
|
|
2558
|
+
PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES as SOGNI_PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES,
|
|
2559
|
+
createPublicSkillContractRuntime as createSogniPublicSkillContractRuntime,
|
|
2560
|
+
} from '@sogni-ai/sogni-intelligence-client/public-skill-runtime';
|
|
2561
|
+
|
|
2562
|
+
export const PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES = SOGNI_PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES.map((recipe) =>
|
|
2563
|
+
recipe.errorCode === 'COST_LIMIT_EXCEEDED'
|
|
2564
|
+
? { ...recipe, message: "You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs" }
|
|
2565
|
+
: recipe
|
|
2566
|
+
);
|
|
2567
|
+
|
|
2568
|
+
export function createPublicSkillDefaultContractRuntime(input = {}) {
|
|
2569
|
+
return createSogniPublicSkillContractRuntime({
|
|
2570
|
+
policies: [
|
|
2571
|
+
...SOGNI_PUBLIC_SKILL_DEFAULT_POLICIES,
|
|
2572
|
+
...(input.policies ?? []),
|
|
2573
|
+
],
|
|
2574
|
+
promptContracts: [
|
|
2575
|
+
...SOGNI_PUBLIC_SKILL_DEFAULT_PROMPT_CONTRACTS,
|
|
2576
|
+
...(input.promptContracts ?? []),
|
|
2577
|
+
],
|
|
2578
|
+
repairRecipes: [
|
|
2579
|
+
...PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES,
|
|
2580
|
+
...(input.repairRecipes ?? []),
|
|
2581
|
+
],
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2553
2585
|
// Moved to @sogni-ai/sogni-intelligence-client/skill-runtime-source in Phase 8.4 follow-up.
|
|
2554
2586
|
// This file is kept as a thin re-export so existing internal `testing/`
|
|
2555
2587
|
// callers keep working. New code should import from the public mid-tier:
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Zero-dependency Node.js version guard.
|
|
2
|
+
//
|
|
3
|
+
// This module is imported FIRST by sogni-agent.mjs (before `sharp` and the
|
|
4
|
+
// Sogni SDK). ES modules evaluate their dependencies in source order, so this
|
|
5
|
+
// runs — and can exit cleanly — before any modern-syntax/native dependency is
|
|
6
|
+
// loaded. That turns "wrong Node version" from a cryptic native/ESM stack
|
|
7
|
+
// trace into a one-line, actionable message.
|
|
8
|
+
//
|
|
9
|
+
// Keep this file import-free and syntactically conservative so it parses and
|
|
10
|
+
// runs on old Node versions too.
|
|
11
|
+
|
|
12
|
+
var MIN_NODE_VERSION = [22, 11, 0];
|
|
13
|
+
|
|
14
|
+
function isVersionAtLeast(current, required) {
|
|
15
|
+
for (var i = 0; i < required.length; i++) {
|
|
16
|
+
var currentValue = current[i] || 0;
|
|
17
|
+
var requiredValue = required[i] || 0;
|
|
18
|
+
if (currentValue > requiredValue) return true;
|
|
19
|
+
if (currentValue < requiredValue) return false;
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
var raw = (process && process.versions && process.versions.node) || '0';
|
|
26
|
+
var current = String(raw).split('.').map(function (part) { return Number(part); });
|
|
27
|
+
if (!isVersionAtLeast(current, MIN_NODE_VERSION)) {
|
|
28
|
+
var required = MIN_NODE_VERSION.join('.');
|
|
29
|
+
process.stderr.write(
|
|
30
|
+
'Error: Sogni requires Node.js >= ' + required + ' (you have ' + raw + ').\n' +
|
|
31
|
+
'Hint: Upgrade Node — e.g. with nvm (`nvm install ' + MIN_NODE_VERSION[0] + '`), fnm, or volta — then re-run.\n'
|
|
32
|
+
);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
} catch (err) {
|
|
36
|
+
// Never let the guard itself crash the CLI; fall through to normal startup.
|
|
37
|
+
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "sogni-creative-agent-skill",
|
|
3
3
|
"name": "Sogni Creative Agent Skill — Image, Video & Music Generation",
|
|
4
4
|
"description": "Agent skill and CLI for Sogni AI image, video, and music generation.",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.4.0",
|
|
6
6
|
"skills": [
|
|
7
7
|
"."
|
|
8
8
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sogni-ai/sogni-creative-agent-skill",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Sogni Creative Agent Skill: agent skill and CLI for Sogni AI image, video, and music generation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "sogni-agent.mjs",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"openclaw:sync": "node scripts/sync-openclaw-plugin.mjs",
|
|
12
|
-
"sync:creative-agent-runtime": "node
|
|
12
|
+
"sync:creative-agent-runtime": "node scripts/sync-creative-agent-runtime.mjs",
|
|
13
13
|
"check:creative-agent-source": "node scripts/check-creative-agent-source.mjs",
|
|
14
14
|
"check:creative-agent-source:strict": "node scripts/check-creative-agent-source.mjs --strict-network",
|
|
15
15
|
"check:creative-agent-runtime": "node scripts/check-creative-agent-runtime.mjs",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"skill-package.json",
|
|
57
57
|
"scripts/check-creative-agent-runtime.mjs",
|
|
58
58
|
"scripts/check-creative-agent-source.mjs",
|
|
59
|
+
"scripts/sync-creative-agent-runtime.mjs",
|
|
59
60
|
"version.mjs",
|
|
60
61
|
"scripts/sync-openclaw-plugin.mjs",
|
|
61
62
|
"openclaw-plugin.mjs",
|
|
@@ -63,11 +64,12 @@
|
|
|
63
64
|
"env.mjs",
|
|
64
65
|
"ssrf-guard.mjs",
|
|
65
66
|
"update-check.mjs",
|
|
67
|
+
"node-version-check.mjs",
|
|
66
68
|
"generated/creative-agent-runtime.mjs",
|
|
67
69
|
"sogni-agent.mjs"
|
|
68
70
|
],
|
|
69
71
|
"dependencies": {
|
|
70
|
-
"@sogni-ai/sogni-intelligence-client": "^3.0.
|
|
72
|
+
"@sogni-ai/sogni-intelligence-client": "^3.0.13",
|
|
71
73
|
"execa": "^9.6.1",
|
|
72
74
|
"json5": "^2.2.3",
|
|
73
75
|
"sharp": "^0.34.5"
|
|
@@ -4,9 +4,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
5
|
|
|
6
6
|
const repoRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
7
|
-
const syncScript =
|
|
8
|
-
? process.env.SOGNI_CREATIVE_AGENT_SYNC_SCRIPT
|
|
9
|
-
: join(repoRoot, '..', 'sogni-creative-agent', 'scripts', 'sync-skill-runtime.mjs');
|
|
7
|
+
const syncScript = join(repoRoot, 'scripts', 'sync-creative-agent-runtime.mjs');
|
|
10
8
|
const generatedPath = join(repoRoot, 'generated', 'creative-agent-runtime.mjs');
|
|
11
9
|
const generatedRelativePath = 'generated/creative-agent-runtime.mjs';
|
|
12
10
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
const repoRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
const upstreamSyncScript = process.env.SOGNI_CREATIVE_AGENT_SYNC_SCRIPT
|
|
8
|
+
? process.env.SOGNI_CREATIVE_AGENT_SYNC_SCRIPT
|
|
9
|
+
: join(repoRoot, '..', 'sogni-creative-agent', 'scripts', 'sync-skill-runtime.mjs');
|
|
10
|
+
const generatedPath = join(repoRoot, 'generated', 'creative-agent-runtime.mjs');
|
|
11
|
+
|
|
12
|
+
const STALE_COST_LIMIT_REPAIR_NOTE =
|
|
13
|
+
'You have hit the credit limit for this turn. Top up credits or wait for the daily refill.';
|
|
14
|
+
const SPARK_PACKS_COST_LIMIT_REPAIR_NOTE =
|
|
15
|
+
'You have hit the credit limit for this turn. Buy Spark Packs to continue: https://docs.sogni.ai/pricing/#spark-packs';
|
|
16
|
+
const PUBLIC_RUNTIME_REEXPORT = "export * from '@sogni-ai/sogni-intelligence-client/public-skill-runtime';";
|
|
17
|
+
const PUBLIC_RUNTIME_OVERRIDE_MARKER = 'PUBLIC_SKILL_SPARK_PACKS_COST_LIMIT_OVERRIDE';
|
|
18
|
+
|
|
19
|
+
function run(command, args, options = {}) {
|
|
20
|
+
const result = spawnSync(command, args, {
|
|
21
|
+
cwd: repoRoot,
|
|
22
|
+
encoding: 'utf8',
|
|
23
|
+
...options,
|
|
24
|
+
});
|
|
25
|
+
if (result.error) throw result.error;
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function applyPublicRuntimeOverrides(source) {
|
|
30
|
+
let updated = source.replaceAll(STALE_COST_LIMIT_REPAIR_NOTE, SPARK_PACKS_COST_LIMIT_REPAIR_NOTE);
|
|
31
|
+
if (updated.includes(PUBLIC_RUNTIME_REEXPORT) && !updated.includes(PUBLIC_RUNTIME_OVERRIDE_MARKER)) {
|
|
32
|
+
const costLimitMessage = JSON.stringify(SPARK_PACKS_COST_LIMIT_REPAIR_NOTE);
|
|
33
|
+
const overrideBlock = `
|
|
34
|
+
// ${PUBLIC_RUNTIME_OVERRIDE_MARKER}
|
|
35
|
+
import {
|
|
36
|
+
PUBLIC_SKILL_DEFAULT_POLICIES as SOGNI_PUBLIC_SKILL_DEFAULT_POLICIES,
|
|
37
|
+
PUBLIC_SKILL_DEFAULT_PROMPT_CONTRACTS as SOGNI_PUBLIC_SKILL_DEFAULT_PROMPT_CONTRACTS,
|
|
38
|
+
PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES as SOGNI_PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES,
|
|
39
|
+
createPublicSkillContractRuntime as createSogniPublicSkillContractRuntime,
|
|
40
|
+
} from '@sogni-ai/sogni-intelligence-client/public-skill-runtime';
|
|
41
|
+
|
|
42
|
+
export const PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES = SOGNI_PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES.map((recipe) =>
|
|
43
|
+
recipe.errorCode === 'COST_LIMIT_EXCEEDED'
|
|
44
|
+
? { ...recipe, message: ${costLimitMessage} }
|
|
45
|
+
: recipe
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
export function createPublicSkillDefaultContractRuntime(input = {}) {
|
|
49
|
+
return createSogniPublicSkillContractRuntime({
|
|
50
|
+
policies: [
|
|
51
|
+
...SOGNI_PUBLIC_SKILL_DEFAULT_POLICIES,
|
|
52
|
+
...(input.policies ?? []),
|
|
53
|
+
],
|
|
54
|
+
promptContracts: [
|
|
55
|
+
...SOGNI_PUBLIC_SKILL_DEFAULT_PROMPT_CONTRACTS,
|
|
56
|
+
...(input.promptContracts ?? []),
|
|
57
|
+
],
|
|
58
|
+
repairRecipes: [
|
|
59
|
+
...PUBLIC_SKILL_DEFAULT_REPAIR_RECIPES,
|
|
60
|
+
...(input.repairRecipes ?? []),
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
updated = updated.replace(PUBLIC_RUNTIME_REEXPORT, `${PUBLIC_RUNTIME_REEXPORT}\n${overrideBlock}`);
|
|
66
|
+
}
|
|
67
|
+
return updated;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!existsSync(upstreamSyncScript)) {
|
|
71
|
+
console.error(`Missing Sogni Creative Agent runtime sync script: ${upstreamSyncScript}`);
|
|
72
|
+
console.error('Set SOGNI_CREATIVE_AGENT_SYNC_SCRIPT or check out sogni-creative-agent as a sibling repo.');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const syncResult = run(process.execPath, [upstreamSyncScript], {
|
|
77
|
+
env: {
|
|
78
|
+
...process.env,
|
|
79
|
+
SOGNI_CREATIVE_AGENT_SKILL_DIR: repoRoot,
|
|
80
|
+
},
|
|
81
|
+
stdio: 'inherit',
|
|
82
|
+
});
|
|
83
|
+
if (syncResult.status !== 0) {
|
|
84
|
+
process.exit(syncResult.status || 1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!existsSync(generatedPath)) {
|
|
88
|
+
console.error('Runtime sync completed but generated/creative-agent-runtime.mjs was not created.');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const generated = readFileSync(generatedPath, 'utf8');
|
|
93
|
+
const updated = applyPublicRuntimeOverrides(generated);
|
|
94
|
+
if (updated !== generated) {
|
|
95
|
+
mkdirSync(dirname(generatedPath), { recursive: true });
|
|
96
|
+
writeFileSync(generatedPath, updated);
|
|
97
|
+
console.log(`Applied public skill runtime overrides to ${generatedPath}`);
|
|
98
|
+
}
|