@causyne/agent 1.10.18 → 1.10.20
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/docs/ARCHITECTURE.md +20 -0
- package/native-skills/media/creative-studio-pipeline/SKILL.md +52 -0
- package/native-skills/media/photo-studio-automation/SKILL.md +53 -0
- package/native-skills/media/video-timeline-assembly/SKILL.md +52 -0
- package/package.json +1 -1
- package/src/agentLoop.js +4 -0
- package/src/appChannel.js +7 -3
- package/src/cliMode.js +33 -67
- package/src/commands.js +56 -8
- package/src/compiledSkills.js +15 -0
- package/src/config.js +80 -11
- package/src/creative/apps.js +69 -0
- package/src/creative/apps.ts +58 -0
- package/src/creative/blenderBatch.js +233 -0
- package/src/creative/blenderBatch.ts +217 -0
- package/src/creative/pipeline.js +56 -0
- package/src/creative/pipeline.ts +82 -0
- package/src/creative/studioScripts.js +374 -0
- package/src/creative/studioScripts.ts +378 -0
- package/src/decisionPolicy.js +2 -0
- package/src/decisionPolicy.ts +1 -0
- package/src/dynamicCatalogue.js +270 -0
- package/src/dynamicCatalogue.ts +283 -0
- package/src/entrypoint.js +2 -1
- package/src/modelCatalogue.d.ts +5 -0
- package/src/modelCatalogue.js +49 -18
- package/src/providers/anthropic.js +4 -0
- package/src/sandbox.js +4 -0
- package/src/toolPacks.js +1 -0
- package/src/tools/creativeApps.js +106 -0
- package/src/tools/creativeApps.ts +99 -0
- package/src/tools/registry.js +2 -1
- package/src/tui/banner.js +391 -190
- package/src/tui/banner.ts +409 -0
- package/src/tui/chatRenderer.js +1 -1
- package/src/tui/dialogs.js +10 -0
- package/src/tui/home.js +29 -72
- package/src/tui/setup.js +104 -0
- package/src/tui/setup.ts +98 -0
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -190,6 +190,26 @@ sliding window, and structural summaries. Full tool output is spilled before it
|
|
|
190
190
|
is shortened. Material compaction is recorded in the audit log, and summary
|
|
191
191
|
collapse emits a quiet terminal notice so context loss is visible.
|
|
192
192
|
|
|
193
|
+
## Guided setup and creative applications
|
|
194
|
+
|
|
195
|
+
`causyne setup` (also `causyne config`), `/setup` and the home setup menu share
|
|
196
|
+
one TypeScript configuration transaction. Provider, endpoint, model, credentials
|
|
197
|
+
and autonomy choices are reviewed before saving. Cancellation preserves the
|
|
198
|
+
previous configuration. Credentials are not reused across providers or changed
|
|
199
|
+
custom endpoints. Setup validates input locally; it does not claim provider
|
|
200
|
+
connectivity or make a billable model request.
|
|
201
|
+
|
|
202
|
+
The deferred `creative_apps` tool exposes installed-application status and native
|
|
203
|
+
automation adapters for 3D and media creative applications. The file-based Blender batch
|
|
204
|
+
adapter creates and manipulates 3D primitives (cubes, spheres, cylinders, planes, cameras,
|
|
205
|
+
lights, materials, rendering) and saves a new `.blend` scene file without running model-supplied
|
|
206
|
+
arbitrary code. Native ExtendScript adapters connect Photo Studio (image resizing,
|
|
207
|
+
rotations, layer modifications, flattening to PSD/PNG/JPG/WEBP), Video Studio (sequence
|
|
208
|
+
timelines, media import, markers, cuts to PRPROJ/XML), and Motion Studio (compositions,
|
|
209
|
+
footage import, typography to AEP). It validates workspace paths, refuses overwrites,
|
|
210
|
+
and applies timeout, output, and cancellation limits. No inbound control service or expensive
|
|
211
|
+
remote protocols are opened.
|
|
212
|
+
|
|
193
213
|
## Removed boundaries
|
|
194
214
|
|
|
195
215
|
No active pairing, relay, WebRTC/P2P, mobile command surface, background daemon
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: creative-studio-pipeline
|
|
3
|
+
description: Orchestrate end-to-end multi-stage creative production pipelines coordinating 3D modeling and rendering in Blender, Photo Studio grading and graphic compositing, and Video Studio sequence assembly. Use when a task spans multiple creative domains or requires chaining 3D outputs into video edits.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Creative Studio Pipeline Orchestration
|
|
7
|
+
|
|
8
|
+
Multi-stage creative production is most reliable when coordinated through a single declarative pipeline. Rather than switching manual applications or invoking separate tools out of sequence, Causyne Agent provides `creative_apps` with `action: "studio_pipeline"`.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
1. Design the production stages.
|
|
13
|
+
- Stage A (3D Scene / Asset Generation): Blender batch (`3d_batch`) creates geometry, assigns PBR materials, positions lights/camera, and renders high-resolution stills or asset elements.
|
|
14
|
+
- Stage B (Still Grading / Composition): Photo Studio batch (`studio_batch`, host `photo-studio`) adjusts color grading, adds typographic overlays or branding, and exports optimized graphic assets.
|
|
15
|
+
- Stage C (Timeline Editing): Video Studio batch (`studio_batch`, host `video-studio`) constructs the sequence timeline, imports rendered stills/clips, cuts intervals, and saves the final project.
|
|
16
|
+
- **Done when:** stages are mapped in dependency order.
|
|
17
|
+
|
|
18
|
+
2. Verify workspace asset directories.
|
|
19
|
+
- Create organized asset folders inside workspace (e.g. `assets/renders/`, `assets/graphics/`, `projects/`).
|
|
20
|
+
- Ensure all intermediate file paths point to future outputs that prior stages produce.
|
|
21
|
+
- **Done when:** directory structure is prepared.
|
|
22
|
+
|
|
23
|
+
3. Configure pipeline stages.
|
|
24
|
+
- Stage configuration uses an array of `{ id, type, config }`.
|
|
25
|
+
- `id`: Unique identifier for logging and failure isolation (e.g., `stage_render_hero`, `stage_composite_badge`, `stage_assemble_timeline`).
|
|
26
|
+
- `type`: `"3d_batch"` for Blender operations, `"studio_batch"` for Photo/Video/Motion Studio operations.
|
|
27
|
+
- `config`: Complete batch payload conforming to respective engine requirements.
|
|
28
|
+
- **Done when:** stage definitions are fully structured.
|
|
29
|
+
|
|
30
|
+
4. Run the coordinated pipeline.
|
|
31
|
+
- Invoke `creative_apps` with `action: "studio_pipeline"` and `stages`.
|
|
32
|
+
- The runtime executes stages sequentially.
|
|
33
|
+
- If any stage encounters an issue, execution halts immediately with full diagnostic reporting, leaving prior outputs intact for inspection.
|
|
34
|
+
- **Done when:** all stages return success.
|
|
35
|
+
|
|
36
|
+
5. Inspect artifacts.
|
|
37
|
+
- Confirm generation of all planned project artifacts (.blend, .png/.psd, .prproj).
|
|
38
|
+
- Verify file sizes are greater than zero.
|
|
39
|
+
- **Done when:** all artifacts are present and verified.
|
|
40
|
+
|
|
41
|
+
## Failure Modes Worth Naming
|
|
42
|
+
|
|
43
|
+
- **Stage output collision:** Each stage must specify an uncreated file path; existing filenames cause immediate refusal.
|
|
44
|
+
- **Missing intermediate files:** Stage N must only reference outputs created in Stage N-1 or earlier.
|
|
45
|
+
- **Host unavailable:** If a host required by a stage is missing, check environment via `action: "status"` beforehand.
|
|
46
|
+
|
|
47
|
+
## Verification
|
|
48
|
+
|
|
49
|
+
- `action: "studio_pipeline"` completes with `success: true`.
|
|
50
|
+
- `stagesCompleted` equals total stages count.
|
|
51
|
+
- Every intermediate and final asset exists on disk in the workspace.
|
|
52
|
+
- Diagnostic logs demonstrate clean execution across each host engine.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: photo-studio-automation
|
|
3
|
+
description: Automate photo editing, graphic composition, text typography, and image format exports using native Photo Studio automation. Use when a task requires batch image edits, graphic layout creation, resizing, color adjustment, or PSD/PNG/JPG/WEBP asset generation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Photo Studio Automation
|
|
7
|
+
|
|
8
|
+
Direct script execution beats screenshot clicking every time: it is exact, sub-second, costs zero vision tokens, and never misclicks because a window moved. Use the native Creative Studio bridge via `creative_apps` with `action: "studio_edit"` and `host: "photo-studio"`.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
1. Verify environment and inspect installed creative hosts.
|
|
13
|
+
- Run `creative_apps` with `action: "status"`.
|
|
14
|
+
- Confirm Photo Studio availability before assembling jobs.
|
|
15
|
+
- **Done when:** host availability is confirmed.
|
|
16
|
+
|
|
17
|
+
2. Establish workspace paths.
|
|
18
|
+
- All inputs and outputs must reside strictly within the active workspace.
|
|
19
|
+
- The output file must not already exist (the runtime enforces collision prevention).
|
|
20
|
+
- Supported output extensions: `.psd`, `.png`, `.jpg`, `.jpeg`, `.webp`.
|
|
21
|
+
- **Done when:** source asset and clean target output paths are defined.
|
|
22
|
+
|
|
23
|
+
3. Plan layer hierarchy and typography before applying edits.
|
|
24
|
+
- If starting from scratch, omit `input` to let the engine create a canvas (1920x1080 standard).
|
|
25
|
+
- If transforming existing art, provide `input` path.
|
|
26
|
+
- Layer modifications should specify explicit `name`, `opacity`, `visible`, and `blendMode`.
|
|
27
|
+
- Typography actions should define `text`, `x`, `y`, `size`, and font family.
|
|
28
|
+
- **Done when:** sequence of edit actions is organized logically from background to foreground.
|
|
29
|
+
|
|
30
|
+
4. Apply color adjustments and canvas sizing.
|
|
31
|
+
- Use `resize` or `crop` early in the action pipeline to establish composition geometry.
|
|
32
|
+
- Use `adjust_color` with `brightness` (-100..100) and `contrast` (-100..100) on target layers for grading.
|
|
33
|
+
- If delivering flat web assets (e.g., PNG or WEBP), include `flatten` at the end of the action chain.
|
|
34
|
+
- **Done when:** geometric and tonal parameters match design requirements.
|
|
35
|
+
|
|
36
|
+
5. Execute batch and verify output.
|
|
37
|
+
- Invoke `creative_apps` with `action: "studio_edit"`, `host: "photo-studio"`, `output`, and `actions`.
|
|
38
|
+
- The runtime verifies execution receipt and confirms nonempty file generation.
|
|
39
|
+
- **Done when:** output file exists, has nonzero byte count, and matches requested specifications.
|
|
40
|
+
|
|
41
|
+
## Failure Modes Worth Naming
|
|
42
|
+
|
|
43
|
+
- **Overwriting existing file:** The engine halts if output exists. Specify a distinct or versioned filename.
|
|
44
|
+
- **Outside workspace:** All paths must resolve inside the workspace root. Do not reference temporary directories or home root.
|
|
45
|
+
- **Missing host:** If the host is not installed, the tool gracefully reports installation status. Propose alternative image processing or check system paths.
|
|
46
|
+
- **Out-of-range layer index:** Layer indices start at 0. Ensure index matches existing layer count.
|
|
47
|
+
|
|
48
|
+
## Verification
|
|
49
|
+
|
|
50
|
+
- `action: "studio_edit"` returns `success: true`.
|
|
51
|
+
- Output file exists on disk within workspace.
|
|
52
|
+
- File size is non-zero.
|
|
53
|
+
- Format matches target extension (.psd, .png, .jpg, or .webp).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: video-timeline-assembly
|
|
3
|
+
description: Assemble video timelines, import media clips to sequences, place markers, make precision edits, and export project files using native Video Studio automation. Use when a task requires automated video sequence building, cutting, multicam timeline layout, or PRPROJ/XML project generation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Video Timeline Assembly
|
|
7
|
+
|
|
8
|
+
Timeline video editing is vastly faster and more dependable through direct project automation than GUI pixel clicking. Causyne Agent connects to the local Video Studio host via `creative_apps` (`action: "studio_edit"`, `host: "video-studio"`).
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
1. Check host installation.
|
|
13
|
+
- Run `creative_apps` with `action: "status"`.
|
|
14
|
+
- Ensure the Video Studio engine is discovered locally.
|
|
15
|
+
- **Done when:** host availability is confirmed.
|
|
16
|
+
|
|
17
|
+
2. Prepare media assets in workspace.
|
|
18
|
+
- Collect source video, audio, and graphic clips inside the project workspace directory.
|
|
19
|
+
- Keep media paths relative to the current workspace root.
|
|
20
|
+
- Output must be a new `.prproj` or `.xml` file path that does not yet exist.
|
|
21
|
+
- **Done when:** all media files are present on disk and target project path is specified.
|
|
22
|
+
|
|
23
|
+
3. Establish sequence settings.
|
|
24
|
+
- Begin with a `sequence` action specifying sequence `name`, `width`, `height`, and `fps`.
|
|
25
|
+
- Standard 1080p: `width: 1920`, `height: 1080`, `fps: 30` (or 24).
|
|
26
|
+
- Standard 4K UHD: `width: 3840`, `height: 2160`, `fps: 60` (or 30/24).
|
|
27
|
+
- **Done when:** sequence resolution and frame rate are locked.
|
|
28
|
+
|
|
29
|
+
4. Import and place media clips along the timeline.
|
|
30
|
+
- Use `import` actions with `path`, `start` time in seconds, and `track` index (0 for primary video).
|
|
31
|
+
- Specify `duration` if clipping the source media to a specific length.
|
|
32
|
+
- Place voiceover, ambient sound, or music tracks with `mediaType: "audio"`.
|
|
33
|
+
- Add timeline markers with `marker` action (`start`, `name`, `comment`) for scene transitions or cue points.
|
|
34
|
+
- **Done when:** all media elements are arranged sequentially without unexpected track overlaps.
|
|
35
|
+
|
|
36
|
+
5. Execute automation and inspect generated project.
|
|
37
|
+
- Invoke `creative_apps` with `action: "studio_edit"`, `host: "video-studio"`, `output`, and `actions`.
|
|
38
|
+
- Verify that the resulting `.prproj` or `.xml` is written cleanly.
|
|
39
|
+
- **Done when:** project file exists on disk with non-zero size.
|
|
40
|
+
|
|
41
|
+
## Failure Modes Worth Naming
|
|
42
|
+
|
|
43
|
+
- **Media file missing on disk:** Every imported file path must exist before running the automation batch.
|
|
44
|
+
- **Output collision:** Target `.prproj` must be a fresh filename; the runtime refuses to overwrite existing project files.
|
|
45
|
+
- **Timeline drift:** When laying clips sequentially, track cumulative duration so clips don't unintentionally overlap on the same track unless intended.
|
|
46
|
+
|
|
47
|
+
## Verification
|
|
48
|
+
|
|
49
|
+
- `action: "studio_edit"` returns `success: true`.
|
|
50
|
+
- Output `.prproj` or `.xml` exists inside workspace.
|
|
51
|
+
- Output file size is non-zero.
|
|
52
|
+
- All imported media files exist and are referenced in the project structure.
|
package/package.json
CHANGED
package/src/agentLoop.js
CHANGED
|
@@ -568,6 +568,7 @@ export class AgentLoop {
|
|
|
568
568
|
}
|
|
569
569
|
|
|
570
570
|
initializeProvider() {
|
|
571
|
+
if (!this.config?.provider) return null;
|
|
571
572
|
return createProvider(this.config);
|
|
572
573
|
}
|
|
573
574
|
|
|
@@ -808,6 +809,9 @@ export class AgentLoop {
|
|
|
808
809
|
this.rearmAbortController();
|
|
809
810
|
// Refresh tier limits on every task run
|
|
810
811
|
this.updateTierLimits();
|
|
812
|
+
if (!this.llm) {
|
|
813
|
+
throw new Error("API Key Required. Causyne is a 100% Sovereign BYOK Engine. Please configure your API Provider and Key in Settings.");
|
|
814
|
+
}
|
|
811
815
|
|
|
812
816
|
// 1. PERCEIVE — runs once per task so the agent starts with real workspace
|
|
813
817
|
// awareness instead of burning steps rediscovering it. The result used to be
|
package/src/appChannel.js
CHANGED
|
@@ -17,10 +17,9 @@
|
|
|
17
17
|
// ~200 tokens on every request to describe a decision the runtime can make on
|
|
18
18
|
// its own, and a decision the model is allowed to skip is not a guard.
|
|
19
19
|
//
|
|
20
|
-
// EXTENDING IT:
|
|
20
|
+
// EXTENDING IT: A native automation bridge (ExtendScript bridge, Blender Python engine) is
|
|
21
21
|
// one more CHANNEL_A row, not a parallel architecture. That is the whole point
|
|
22
|
-
// of the table —
|
|
23
|
-
// not "build our own Higgsfield".
|
|
22
|
+
// of the table — "a native app API beats pixels".
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
25
|
* Apps that already have a Channel A tool, keyed by matcher.
|
|
@@ -38,6 +37,11 @@ const CHANNEL_A = [
|
|
|
38
37
|
match: /\b(chrome|chromium|safari|firefox|edge|brave|arc)\b/,
|
|
39
38
|
tool: 'browser_act',
|
|
40
39
|
app: 'a web browser'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
match: /\b(photo-studio|video-studio|motion-studio|photostudio|videostudio|motionstudio)\b/,
|
|
43
|
+
tool: 'creative_apps',
|
|
44
|
+
app: 'Creative Studio Bridge'
|
|
41
45
|
}
|
|
42
46
|
];
|
|
43
47
|
|
package/src/cliMode.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { runSetup } from './tui/setup.js';
|
|
1
2
|
import { AgentLoop } from './agentLoop.js';
|
|
2
3
|
import { loadConfig, saveConfig } from './config.js';
|
|
3
4
|
import readline from 'readline';
|
|
4
5
|
import { ChatPrompt, isInteractive } from './tui/prompt.js';
|
|
5
6
|
import { handleInput, listCommands, isCommandInput } from './commands.js';
|
|
6
7
|
import { ChatRenderer } from './tui/chatRenderer.js';
|
|
7
|
-
import { paint, bold, badge, GLYPHS, terminalWidth } from './tui/theme.js';
|
|
8
|
+
import { paint, bold, dim, badge, GLYPHS, terminalWidth } from './tui/theme.js';
|
|
8
9
|
import { wordmark } from './tui/banner.js';
|
|
9
10
|
import { createStageSurface } from './tui/stageSurface.js';
|
|
10
11
|
import { wrapBlock } from './tui/text.js';
|
|
@@ -227,7 +228,7 @@ async function runHomeAction(action, { prompt, conn, finish }) {
|
|
|
227
228
|
if (!prompt) { await interactiveConfig({}); return 'exit'; }
|
|
228
229
|
const chosen = await configureInPrompt(prompt, { loadConfig, saveConfig });
|
|
229
230
|
conn.renderer.write('\n' + (chosen
|
|
230
|
-
? paint('success', ` Saved. Using ${chosen.provider} / ${chosen.model}.`)
|
|
231
|
+
? paint('success', ` Saved. Using ${chosen.provider} / ${chosen.model}. Connection has not been tested.`)
|
|
231
232
|
: dimNotice(' Cancelled. Nothing was changed.')) + '\n');
|
|
232
233
|
reopen();
|
|
233
234
|
return null;
|
|
@@ -547,6 +548,11 @@ export async function chatMode({ home = false } = {}) {
|
|
|
547
548
|
const startedAt = Date.now();
|
|
548
549
|
if (prompt) prompt.setBusy(true);
|
|
549
550
|
try {
|
|
551
|
+
if (!loop.config.provider || (!loop.config.apiKey && loop.config.provider !== 'ollama')) {
|
|
552
|
+
conn.renderer.write('\n' + paint('warn', ' No API provider or key configured.') + '\n' +
|
|
553
|
+
paint('muted', ' Run /setup or select setup from the home menu to configure your provider.\n'));
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
550
556
|
await loop.run();
|
|
551
557
|
} finally {
|
|
552
558
|
// Must be released on the error path too, or one failed turn leaves the
|
|
@@ -604,9 +610,14 @@ export async function execCommand(command) {
|
|
|
604
610
|
export async function interactiveConfig(options) {
|
|
605
611
|
const config = loadConfig();
|
|
606
612
|
|
|
607
|
-
console.log(logoLines() + '\n' + bold('Configuration') + '\n'
|
|
613
|
+
console.log(logoLines() + '\n' + bold(' Causyne Agent · Guided Setup & Configuration') + '\n'
|
|
614
|
+
+ dim(' Standardized UI/UX · Models · Providers · Permissions · Creative Studio Bridge\n'));
|
|
608
615
|
|
|
609
616
|
if (options.provider || options.apikey || options.model) {
|
|
617
|
+
if (options.provider && options.provider !== config.provider) {
|
|
618
|
+
config.apiKey = '';
|
|
619
|
+
delete config.baseUrl;
|
|
620
|
+
}
|
|
610
621
|
if (options.provider) config.provider = options.provider;
|
|
611
622
|
if (options.apikey) config.apiKey = options.apikey;
|
|
612
623
|
if (options.model) config.model = options.model;
|
|
@@ -615,71 +626,26 @@ export async function interactiveConfig(options) {
|
|
|
615
626
|
return;
|
|
616
627
|
}
|
|
617
628
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
lines: [
|
|
638
|
-
`${provider.label}`,
|
|
639
|
-
'',
|
|
640
|
-
'Model ids change often. Type the one you want, or accept the suggestion.'
|
|
641
|
-
],
|
|
642
|
-
value: suggested,
|
|
643
|
-
placeholder: 'model id'
|
|
644
|
-
});
|
|
645
|
-
if (model === null) { console.log(' Cancelled. Nothing was changed.\n'); return; }
|
|
646
|
-
|
|
647
|
-
const apiKey = await askText({
|
|
648
|
-
title: 'API key',
|
|
649
|
-
lines: [
|
|
650
|
-
`${provider.label} ${GLYPHS.arrow} ${model || suggested}`,
|
|
651
|
-
'',
|
|
652
|
-
'Stored encrypted in ~/.causyne/config.json. It never leaves this machine.'
|
|
653
|
-
],
|
|
654
|
-
mask: true,
|
|
655
|
-
placeholder: config.apiKey ? 'blank keeps the current key' : 'paste your key'
|
|
656
|
-
});
|
|
657
|
-
if (apiKey === null) { console.log(' Cancelled. Nothing was changed.\n'); return; }
|
|
658
|
-
|
|
659
|
-
// Only asked where it means something. Every provider used to be shown this,
|
|
660
|
-
// and for eight of the ten the right answer is always blank.
|
|
661
|
-
let baseUrl = config.baseUrl || '';
|
|
662
|
-
if (provider.id === 'openai-compatible' || provider.id === 'dashscope-byok' || provider.id === 'ollama') {
|
|
663
|
-
const answer = await askText({
|
|
664
|
-
title: 'base URL',
|
|
665
|
-
lines: ['Leave blank for the provider default.', 'Required for a custom DashScope workspace endpoint.'],
|
|
666
|
-
value: baseUrl,
|
|
667
|
-
placeholder: 'https://…'
|
|
668
|
-
});
|
|
669
|
-
if (answer === null) { console.log(' Cancelled. Nothing was changed.\n'); return; }
|
|
670
|
-
baseUrl = answer;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
config.provider = provider.id;
|
|
674
|
-
config.model = (model || suggested || '').trim();
|
|
675
|
-
if (apiKey) config.apiKey = apiKey;
|
|
676
|
-
if (baseUrl && baseUrl.trim()) config.baseUrl = baseUrl.trim(); else delete config.baseUrl;
|
|
677
|
-
|
|
678
|
-
saveConfig(config);
|
|
679
|
-
console.log('\n' + paint('success', ` Saved. Using ${config.provider} / ${config.model}.`) + '\n');
|
|
629
|
+
const result = await runSetup({ select: askSelect, text: askText }, PROVIDERS, { loadConfig, saveConfig });
|
|
630
|
+
if (!result) { console.log(' Cancelled. Nothing was changed.\n'); return; }
|
|
631
|
+
|
|
632
|
+
let creativeSummary = '';
|
|
633
|
+
try {
|
|
634
|
+
const { creativeAppStatus } = await import('./creative/apps.js');
|
|
635
|
+
const apps = await creativeAppStatus();
|
|
636
|
+
const ready = apps.filter(a => a.available).map(a => a.label);
|
|
637
|
+
creativeSummary = ready.length > 0
|
|
638
|
+
? ` ${paint('success', GLYPHS.bullet)} Creative Studio Bridge: ${ready.join(', ')} connected`
|
|
639
|
+
: ` ${dim(GLYPHS.bullet + ' Creative Studio Bridge: install Blender, Premiere, Photoshop, or After Effects to activate')}`;
|
|
640
|
+
} catch { /* ignore */ }
|
|
641
|
+
|
|
642
|
+
console.log('\n' + paint('success', ` ✔ Saved configuration successfully!`) + '\n'
|
|
643
|
+
+ ` Active Provider: ${bold(result.provider)}\n`
|
|
644
|
+
+ ` Active Model: ${bold(result.model)}\n`
|
|
645
|
+
+ (creativeSummary ? `${creativeSummary}\n` : '')
|
|
646
|
+
+ ` Launch agent: ${bold('causyne')} or ${bold('causyne chat')}\n`
|
|
647
|
+
+ ` Diagnostics: ${bold('causyne doctor')}\n`);
|
|
680
648
|
|
|
681
649
|
// Seamlessly transition to the home screen.
|
|
682
|
-
//
|
|
683
|
-
// Finishing configuration returns to the local home screen.
|
|
684
650
|
await chatMode({ home: true });
|
|
685
651
|
}
|
package/src/commands.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from './userSkills.js';
|
|
21
21
|
import { buildContextText, buildUsageText, buildCheckpointsText, buildUndoText, buildStatusText } from './reporting.js';
|
|
22
22
|
import { loadConfig, saveConfig } from './config.js';
|
|
23
|
+
import { resolveAvailableModels, getCuratedModels } from './dynamicCatalogue.js';
|
|
23
24
|
import {
|
|
24
25
|
serverStatus, grantServer, revokeServer, addServer, removeServer,
|
|
25
26
|
connectServer, disconnectServer, refreshServer
|
|
@@ -44,6 +45,17 @@ function fail(output) {
|
|
|
44
45
|
// ---------------------------------------------------------------------------
|
|
45
46
|
|
|
46
47
|
export const COMMANDS = [
|
|
48
|
+
{
|
|
49
|
+
name: 'setup', aliases: ['config', 'connect'], scope: 'local', usage: '/setup',
|
|
50
|
+
description: 'Configure provider, endpoint, model and autonomy',
|
|
51
|
+
run: async (_argv, ctx) => {
|
|
52
|
+
if (!ctx.prompt) return ok('Run causyne setup in an interactive terminal.');
|
|
53
|
+
const { configureInPrompt } = await import('./tui/home.js');
|
|
54
|
+
const result = await configureInPrompt(ctx.prompt, { loadConfig, saveConfig });
|
|
55
|
+
return ok(result ? `Saved ${result.provider} / ${result.model}. Connection has not been tested.` : 'Cancelled. Nothing was changed.');
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
47
59
|
{
|
|
48
60
|
name: 'help',
|
|
49
61
|
aliases: ['?', 'commands'],
|
|
@@ -632,22 +644,58 @@ export const COMMANDS = [
|
|
|
632
644
|
|
|
633
645
|
{
|
|
634
646
|
name: 'model',
|
|
647
|
+
aliases: ['models'],
|
|
635
648
|
scope: 'local',
|
|
636
|
-
usage: '/model [provider/model | model]',
|
|
637
|
-
description: 'Show or change the model in use',
|
|
649
|
+
usage: '/model [list | refresh | provider/model | model]',
|
|
650
|
+
description: 'Show, list or change the model in use',
|
|
638
651
|
dialogTitle: 'Model',
|
|
639
|
-
|
|
652
|
+
complete: () => {
|
|
653
|
+
const config = loadConfig();
|
|
654
|
+
const currentProvider = config.provider || '';
|
|
655
|
+
const curated = getCuratedModels(currentProvider);
|
|
656
|
+
const items = [
|
|
657
|
+
{ value: 'list', label: 'list', description: 'list models for active provider' },
|
|
658
|
+
{ value: 'refresh', label: 'refresh', description: 'poll provider API for live models' }
|
|
659
|
+
];
|
|
660
|
+
for (const m of curated) {
|
|
661
|
+
items.push({ value: m, label: m, description: currentProvider || 'model' });
|
|
662
|
+
}
|
|
663
|
+
return items;
|
|
664
|
+
},
|
|
665
|
+
run: async (argv) => {
|
|
640
666
|
const config = loadConfig();
|
|
641
667
|
if (argv.length === 0) {
|
|
642
|
-
return ok(`Provider : ${config.provider || '
|
|
668
|
+
return ok(`Provider : ${config.provider || 'no provider configured'}\nModel : ${config.model || 'none'}\n\nUse /model list to see models, or /model refresh to poll provider API.`);
|
|
669
|
+
}
|
|
670
|
+
const first = argv[0].toLowerCase();
|
|
671
|
+
if (first === 'list' || first === 'ls') {
|
|
672
|
+
const targetProvider = argv[1] || config.provider;
|
|
673
|
+
if (!targetProvider) return fail('No provider specified or configured. Run /setup first.');
|
|
674
|
+
const result = await resolveAvailableModels(targetProvider, config);
|
|
675
|
+
const active = config.model;
|
|
676
|
+
const lines = [
|
|
677
|
+
`Available models for ${targetProvider} (source: ${result.source}):`,
|
|
678
|
+
''
|
|
679
|
+
];
|
|
680
|
+
for (const m of result.models) {
|
|
681
|
+
const isCurrent = m === active;
|
|
682
|
+
lines.push(` ${isCurrent ? '• ' : ' '}${m}${isCurrent ? ' (active)' : ''}`);
|
|
683
|
+
}
|
|
684
|
+
lines.push('');
|
|
685
|
+
lines.push('Use /model <name> to select, or /model refresh to poll the provider API.');
|
|
686
|
+
return ok(lines.join('\n'));
|
|
687
|
+
}
|
|
688
|
+
if (first === 'refresh') {
|
|
689
|
+
if (!config.provider) return fail('No provider configured. Run /setup first.');
|
|
690
|
+
const result = await resolveAvailableModels(config.provider, config, true);
|
|
691
|
+
return ok(`Refreshed ${config.provider} models (${result.models.length} available, source: ${result.source}). Run /model list to view.`);
|
|
643
692
|
}
|
|
644
693
|
const requested = argv.join(' ').trim();
|
|
645
694
|
const separator = requested.indexOf('/');
|
|
646
|
-
// A bare name changes only the model; "provider/model" changes both. Any
|
|
647
|
-
// string is accepted — model ids move faster than any hardcoded list, and
|
|
648
|
-
// guessing from memory is exactly what .agents/AGENTS.md §3 forbids.
|
|
649
695
|
if (separator > 0) {
|
|
650
|
-
|
|
696
|
+
const provider = requested.slice(0, separator);
|
|
697
|
+
if (provider !== config.provider) { config.apiKey = ''; delete config.baseUrl; }
|
|
698
|
+
config.provider = provider;
|
|
651
699
|
config.model = requested.slice(separator + 1);
|
|
652
700
|
} else {
|
|
653
701
|
config.model = requested;
|
package/src/compiledSkills.js
CHANGED
|
@@ -318,6 +318,21 @@ export const COMPILED_SKILLS = {
|
|
|
318
318
|
"name": "cinematic-3d-shot",
|
|
319
319
|
"description": "Build a 3D object in Blender and render it as a short cinematic clip. Use when a task asks for a 3D model, a product shot, a turntable, or a rendered animation of a few seconds.",
|
|
320
320
|
"content": "---\nname: cinematic-3d-shot\ndescription: Build a 3D object in Blender and render it as a short cinematic clip. Use when a task asks for a 3D model, a product shot, a turntable, or a rendered animation of a few seconds.\n---\n\n# Cinematic 3D Shot\n\nA render looks amateur for four reasons, always the same four: default grey\nmaterial, one flat light, the camera where Blender left it, and nobody looked at\nthe result before shipping it. Fix those four and an ordinary model reads as a\nproduct shot.\n\n## Workflow\n\n1. Verify Blender before planning anything.\n - Use the local CLI path. Budget a low-cost `renderFrame` whenever you need\n to inspect the current visual result.\n - **Done when:** the local Blender binary is available and the review loop is planned.\n\n2. Clear the scene.\n - Blender opens with a cube, one lamp and a camera. Building around them is\n how the default cube ends up in a client render.\n - **Done when:** `clearScene` has run.\n\n3. Build the silhouette, then the detail.\n - A shape is read as an outline before it is read as a surface. If the form\n does not identify itself at thumbnail size, no amount of material work will\n save it. Use `smartGeometry` — it applies bevel and subdivision, and a\n bevel is what catches a highlight along an edge. Perfectly sharp edges are\n the most common tell of untouched 3D: nothing in the real world has them.\n - **Done when:** the object is recognisable as a flat grey silhouette.\n\n4. Give every visible object a material.\n - `smartMaterial` on each one. One untouched object in a dressed scene reads\n as unfinished more loudly than a whole scene of grey.\n - Match the material to the object's story: a pen barrel is brushed metal or\n lacquered resin, not plastic; the grip is rubber, and rubber is rough where\n metal is not. Contrast between materials is what tells the eye there are\n two parts.\n - **Done when:** no object still carries the default surface.\n\n5. Light with three lights, in this order.\n - `setupStudioLighting` gives key, fill and rim. The ratio does the work:\n the key states the form, the fill keeps the shadow side from going black,\n and the RIM is what separates the subject from the background. A render\n that looks flat is almost always missing the rim.\n - **Done when:** the subject's edge is visibly brighter than what is behind it.\n\n6. Add atmosphere only after the lights exist.\n - `cinematicAtmosphere`. Fog scatters the light you already have, so adding\n it first means tuning against something that will change. Keep the density\n low — visible haze reads as a mistake unless the shot is about weather.\n - **Done when:** there is depth between foreground and background, and the\n subject is still crisp.\n\n7. Frame it deliberately.\n - `autoFrameCamera`. A longer lens (65mm and up) flatters an object; a wide\n lens distorts it and is why so many AI renders look like a fisheye photo of\n a toy. Put the camera slightly above the eye line and the subject a little\n off-centre.\n - **Done when:** the camera is not where Blender put it.\n\n8. Look at it before you animate.\n - `captureViewport`, or one low-sample `renderFrame`, then `view_image` or\n `vision_inspect_ui`. Ask the specific question: is anything still grey, is\n the subject separated from the background, does the silhouette read?\n - **A render nobody has looked at is not a finished render.** This step is\n the difference between the two outcomes, not a formality.\n - **Done when:** you have described what you actually see, not what you\n intended to build.\n\n9. One camera move, and do the arithmetic out loud.\n - Seconds x fps = frames. 8s at 24fps is frames 1-192; set the range\n explicitly or Blender renders its own 250-frame default.\n - ONE move per short shot: a slow orbit, a push-in, or a rise. Three moves in\n eight seconds reads as a screensaver. Ease the ends — constant velocity is\n what makes a move feel mechanical.\n - **Done when:** the frame range matches the requested duration exactly.\n\n10. Render with settings that fit the budget.\n - EEVEE for anything short. Cycles at default samples will spend an entire\n task budget on one frame and is almost never what a few seconds of\n turntable needs.\n - **Done when:** the file exists, and you have opened it to confirm it is\n not zero bytes or a single black frame.\n\n## Failure modes worth naming\n\n- **Everything is grey.** A material step was skipped, or applied to one object.\n- **It looks flat.** No rim light, or fill too close to key.\n- **The object looks like a toy.** Lens too wide, or the camera is too close.\n- **The animation stutters.** Frame range and fps disagree with the duration.\n- **Blender is not installed.** Say so and offer the alternative. Do not loop\n retrying the same missing binary — that spends budget to learn nothing.\n\n## Verification\n\n- The rendered file exists, is larger than a few kilobytes, and opens.\n- Open the first frame and the middle frame. Neither is black, neither is empty,\n and the subject is in frame in both.\n- No object in shot still carries the default grey surface.\n- The subject's edge is brighter than the background directly behind it.\n- Frame count equals requested seconds x fps, exactly.\n- The camera position differs from Blender's default.\n"
|
|
321
|
+
},
|
|
322
|
+
"creative-studio-pipeline": {
|
|
323
|
+
"name": "creative-studio-pipeline",
|
|
324
|
+
"description": "Orchestrate end-to-end multi-stage creative production pipelines coordinating 3D modeling and rendering in Blender, Photo Studio grading and graphic compositing, and Video Studio sequence assembly. Use when a task spans multiple creative domains or requires chaining 3D outputs into video edits.",
|
|
325
|
+
"content": "---\nname: creative-studio-pipeline\ndescription: Orchestrate end-to-end multi-stage creative production pipelines coordinating 3D modeling and rendering in Blender, Photo Studio grading and graphic compositing, and Video Studio sequence assembly. Use when a task spans multiple creative domains or requires chaining 3D outputs into video edits.\n---\n\n# Creative Studio Pipeline Orchestration\n\nMulti-stage creative production is most reliable when coordinated through a single declarative pipeline. Rather than switching manual applications or invoking separate tools out of sequence, Causyne Agent provides `creative_apps` with `action: \"studio_pipeline\"`.\n\n## Workflow\n\n1. Design the production stages.\n - Stage A (3D Scene / Asset Generation): Blender batch (`3d_batch`) creates geometry, assigns PBR materials, positions lights/camera, and renders high-resolution stills or asset elements.\n - Stage B (Still Grading / Composition): Photo Studio batch (`studio_batch`, host `photo-studio`) adjusts color grading, adds typographic overlays or branding, and exports optimized graphic assets.\n - Stage C (Timeline Editing): Video Studio batch (`studio_batch`, host `video-studio`) constructs the sequence timeline, imports rendered stills/clips, cuts intervals, and saves the final project.\n - **Done when:** stages are mapped in dependency order.\n\n2. Verify workspace asset directories.\n - Create organized asset folders inside workspace (e.g. `assets/renders/`, `assets/graphics/`, `projects/`).\n - Ensure all intermediate file paths point to future outputs that prior stages produce.\n - **Done when:** directory structure is prepared.\n\n3. Configure pipeline stages.\n - Stage configuration uses an array of `{ id, type, config }`.\n - `id`: Unique identifier for logging and failure isolation (e.g., `stage_render_hero`, `stage_composite_badge`, `stage_assemble_timeline`).\n - `type`: `\"3d_batch\"` for Blender operations, `\"studio_batch\"` for Photo/Video/Motion Studio operations.\n - `config`: Complete batch payload conforming to respective engine requirements.\n - **Done when:** stage definitions are fully structured.\n\n4. Run the coordinated pipeline.\n - Invoke `creative_apps` with `action: \"studio_pipeline\"` and `stages`.\n - The runtime executes stages sequentially.\n - If any stage encounters an issue, execution halts immediately with full diagnostic reporting, leaving prior outputs intact for inspection.\n - **Done when:** all stages return success.\n\n5. Inspect artifacts.\n - Confirm generation of all planned project artifacts (.blend, .png/.psd, .prproj).\n - Verify file sizes are greater than zero.\n - **Done when:** all artifacts are present and verified.\n\n## Failure Modes Worth Naming\n\n- **Stage output collision:** Each stage must specify an uncreated file path; existing filenames cause immediate refusal.\n- **Missing intermediate files:** Stage N must only reference outputs created in Stage N-1 or earlier.\n- **Host unavailable:** If a host required by a stage is missing, check environment via `action: \"status\"` beforehand.\n\n## Verification\n\n- `action: \"studio_pipeline\"` completes with `success: true`.\n- `stagesCompleted` equals total stages count.\n- Every intermediate and final asset exists on disk in the workspace.\n- Diagnostic logs demonstrate clean execution across each host engine.\n"
|
|
326
|
+
},
|
|
327
|
+
"photo-studio-automation": {
|
|
328
|
+
"name": "photo-studio-automation",
|
|
329
|
+
"description": "Automate photo editing, graphic composition, text typography, and image format exports using native Photo Studio automation. Use when a task requires batch image edits, graphic layout creation, resizing, color adjustment, or PSD/PNG/JPG/WEBP asset generation.",
|
|
330
|
+
"content": "---\nname: photo-studio-automation\ndescription: Automate photo editing, graphic composition, text typography, and image format exports using native Photo Studio automation. Use when a task requires batch image edits, graphic layout creation, resizing, color adjustment, or PSD/PNG/JPG/WEBP asset generation.\n---\n\n# Photo Studio Automation\n\nDirect script execution beats screenshot clicking every time: it is exact, sub-second, costs zero vision tokens, and never misclicks because a window moved. Use the native Creative Studio bridge via `creative_apps` with `action: \"studio_edit\"` and `host: \"photo-studio\"`.\n\n## Workflow\n\n1. Verify environment and inspect installed creative hosts.\n - Run `creative_apps` with `action: \"status\"`.\n - Confirm Photo Studio availability before assembling jobs.\n - **Done when:** host availability is confirmed.\n\n2. Establish workspace paths.\n - All inputs and outputs must reside strictly within the active workspace.\n - The output file must not already exist (the runtime enforces collision prevention).\n - Supported output extensions: `.psd`, `.png`, `.jpg`, `.jpeg`, `.webp`.\n - **Done when:** source asset and clean target output paths are defined.\n\n3. Plan layer hierarchy and typography before applying edits.\n - If starting from scratch, omit `input` to let the engine create a canvas (1920x1080 standard).\n - If transforming existing art, provide `input` path.\n - Layer modifications should specify explicit `name`, `opacity`, `visible`, and `blendMode`.\n - Typography actions should define `text`, `x`, `y`, `size`, and font family.\n - **Done when:** sequence of edit actions is organized logically from background to foreground.\n\n4. Apply color adjustments and canvas sizing.\n - Use `resize` or `crop` early in the action pipeline to establish composition geometry.\n - Use `adjust_color` with `brightness` (-100..100) and `contrast` (-100..100) on target layers for grading.\n - If delivering flat web assets (e.g., PNG or WEBP), include `flatten` at the end of the action chain.\n - **Done when:** geometric and tonal parameters match design requirements.\n\n5. Execute batch and verify output.\n - Invoke `creative_apps` with `action: \"studio_edit\"`, `host: \"photo-studio\"`, `output`, and `actions`.\n - The runtime verifies execution receipt and confirms nonempty file generation.\n - **Done when:** output file exists, has nonzero byte count, and matches requested specifications.\n\n## Failure Modes Worth Naming\n\n- **Overwriting existing file:** The engine halts if output exists. Specify a distinct or versioned filename.\n- **Outside workspace:** All paths must resolve inside the workspace root. Do not reference temporary directories or home root.\n- **Missing host:** If the host is not installed, the tool gracefully reports installation status. Propose alternative image processing or check system paths.\n- **Out-of-range layer index:** Layer indices start at 0. Ensure index matches existing layer count.\n\n## Verification\n\n- `action: \"studio_edit\"` returns `success: true`.\n- Output file exists on disk within workspace.\n- File size is non-zero.\n- Format matches target extension (.psd, .png, .jpg, or .webp).\n"
|
|
331
|
+
},
|
|
332
|
+
"video-timeline-assembly": {
|
|
333
|
+
"name": "video-timeline-assembly",
|
|
334
|
+
"description": "Assemble video timelines, import media clips to sequences, place markers, make precision edits, and export project files using native Video Studio automation. Use when a task requires automated video sequence building, cutting, multicam timeline layout, or PRPROJ/XML project generation.",
|
|
335
|
+
"content": "---\nname: video-timeline-assembly\ndescription: Assemble video timelines, import media clips to sequences, place markers, make precision edits, and export project files using native Video Studio automation. Use when a task requires automated video sequence building, cutting, multicam timeline layout, or PRPROJ/XML project generation.\n---\n\n# Video Timeline Assembly\n\nTimeline video editing is vastly faster and more dependable through direct project automation than GUI pixel clicking. Causyne Agent connects to the local Video Studio host via `creative_apps` (`action: \"studio_edit\"`, `host: \"video-studio\"`).\n\n## Workflow\n\n1. Check host installation.\n - Run `creative_apps` with `action: \"status\"`.\n - Ensure the Video Studio engine is discovered locally.\n - **Done when:** host availability is confirmed.\n\n2. Prepare media assets in workspace.\n - Collect source video, audio, and graphic clips inside the project workspace directory.\n - Keep media paths relative to the current workspace root.\n - Output must be a new `.prproj` or `.xml` file path that does not yet exist.\n - **Done when:** all media files are present on disk and target project path is specified.\n\n3. Establish sequence settings.\n - Begin with a `sequence` action specifying sequence `name`, `width`, `height`, and `fps`.\n - Standard 1080p: `width: 1920`, `height: 1080`, `fps: 30` (or 24).\n - Standard 4K UHD: `width: 3840`, `height: 2160`, `fps: 60` (or 30/24).\n - **Done when:** sequence resolution and frame rate are locked.\n\n4. Import and place media clips along the timeline.\n - Use `import` actions with `path`, `start` time in seconds, and `track` index (0 for primary video).\n - Specify `duration` if clipping the source media to a specific length.\n - Place voiceover, ambient sound, or music tracks with `mediaType: \"audio\"`.\n - Add timeline markers with `marker` action (`start`, `name`, `comment`) for scene transitions or cue points.\n - **Done when:** all media elements are arranged sequentially without unexpected track overlaps.\n\n5. Execute automation and inspect generated project.\n - Invoke `creative_apps` with `action: \"studio_edit\"`, `host: \"video-studio\"`, `output`, and `actions`.\n - Verify that the resulting `.prproj` or `.xml` is written cleanly.\n - **Done when:** project file exists on disk with non-zero size.\n\n## Failure Modes Worth Naming\n\n- **Media file missing on disk:** Every imported file path must exist before running the automation batch.\n- **Output collision:** Target `.prproj` must be a fresh filename; the runtime refuses to overwrite existing project files.\n- **Timeline drift:** When laying clips sequentially, track cumulative duration so clips don't unintentionally overlap on the same track unless intended.\n\n## Verification\n\n- `action: \"studio_edit\"` returns `success: true`.\n- Output `.prproj` or `.xml` exists inside workspace.\n- Output file size is non-zero.\n- All imported media files exist and are referenced in the project structure.\n"
|
|
321
336
|
}
|
|
322
337
|
},
|
|
323
338
|
"mobile": {
|
package/src/config.js
CHANGED
|
@@ -78,30 +78,41 @@ export function loadConfig() {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
// Self-heal a model id written before src/modelIds.js existed.
|
|
82
|
-
// provider normalises too, but tokenTracker.getPricing() and the /model
|
|
83
|
-
// display both read config.model directly —
|
|
84
|
-
// they must agree with what is actually sent, not with what a stale
|
|
85
|
-
// Android build once wrote here.
|
|
81
|
+
// Self-heal a model id written before src/modelIds.js existed.
|
|
86
82
|
const canonicalModel = canonicalModelId(parsed.model);
|
|
87
83
|
if (canonicalModel !== parsed.model) {
|
|
88
84
|
parsed.model = canonicalModel;
|
|
89
85
|
saveConfig(parsed);
|
|
90
86
|
}
|
|
91
87
|
|
|
88
|
+
// Self-heal legacy unconfigured DashScope bug:
|
|
89
|
+
// If config was written with provider 'dashscope' or 'dashscope-byok' but without an apiKey,
|
|
90
|
+
// it was an unintended default from older builds. Heal to an environment key or unconfigured.
|
|
91
|
+
if ((parsed.provider === 'dashscope' || parsed.provider === 'dashscope-byok') && !parsed.apiKey) {
|
|
92
|
+
const envDetected = detectEnvironmentProvider();
|
|
93
|
+
if (envDetected) {
|
|
94
|
+
parsed.provider = envDetected.provider;
|
|
95
|
+
parsed.model = envDetected.model;
|
|
96
|
+
parsed.apiKey = envDetected.apiKey;
|
|
97
|
+
} else {
|
|
98
|
+
parsed.provider = '';
|
|
99
|
+
parsed.model = '';
|
|
100
|
+
}
|
|
101
|
+
saveConfig(parsed);
|
|
102
|
+
}
|
|
103
|
+
|
|
92
104
|
return parsed;
|
|
93
105
|
} catch (e) {
|
|
94
106
|
console.error('Error reading config.json:', e);
|
|
95
107
|
}
|
|
96
108
|
}
|
|
97
109
|
|
|
98
|
-
|
|
110
|
+
// Default config: auto-detect from environment if available, otherwise unconfigured
|
|
111
|
+
const envDetected = detectEnvironmentProvider();
|
|
99
112
|
const defaultConfig = {
|
|
100
|
-
provider: '
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
model: DEFAULT_MODEL_BY_PROVIDER.dashscope,
|
|
104
|
-
apiKey: '',
|
|
113
|
+
provider: envDetected ? envDetected.provider : '',
|
|
114
|
+
model: envDetected ? envDetected.model : '',
|
|
115
|
+
apiKey: envDetected ? envDetected.apiKey : '',
|
|
105
116
|
autonomyMode: 'B',
|
|
106
117
|
tokenBudget: {
|
|
107
118
|
enabled: false,
|
|
@@ -116,6 +127,64 @@ export function loadConfig() {
|
|
|
116
127
|
return defaultConfig;
|
|
117
128
|
}
|
|
118
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Auto-detect available API keys from environment variables.
|
|
132
|
+
* Precedence matches frontier capability: Gemini, Anthropic, OpenAI, DeepSeek, xAI, Moonshot, Qwen/DashScope.
|
|
133
|
+
*/
|
|
134
|
+
export function detectEnvironmentProvider() {
|
|
135
|
+
const env = process.env;
|
|
136
|
+
if (env.GEMINI_API_KEY || env.GOOGLE_API_KEY) {
|
|
137
|
+
return {
|
|
138
|
+
provider: 'gemini',
|
|
139
|
+
apiKey: env.GEMINI_API_KEY || env.GOOGLE_API_KEY || '',
|
|
140
|
+
model: DEFAULT_MODEL_BY_PROVIDER.gemini
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (env.ANTHROPIC_API_KEY) {
|
|
144
|
+
return {
|
|
145
|
+
provider: 'anthropic',
|
|
146
|
+
apiKey: env.ANTHROPIC_API_KEY,
|
|
147
|
+
model: DEFAULT_MODEL_BY_PROVIDER.anthropic
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
if (env.OPENAI_API_KEY) {
|
|
151
|
+
return {
|
|
152
|
+
provider: 'openai',
|
|
153
|
+
apiKey: env.OPENAI_API_KEY,
|
|
154
|
+
model: DEFAULT_MODEL_BY_PROVIDER.openai
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (env.DEEPSEEK_API_KEY) {
|
|
158
|
+
return {
|
|
159
|
+
provider: 'deepseek',
|
|
160
|
+
apiKey: env.DEEPSEEK_API_KEY,
|
|
161
|
+
model: DEFAULT_MODEL_BY_PROVIDER.deepseek
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (env.XAI_API_KEY || env.GROK_API_KEY) {
|
|
165
|
+
return {
|
|
166
|
+
provider: 'xai',
|
|
167
|
+
apiKey: env.XAI_API_KEY || env.GROK_API_KEY || '',
|
|
168
|
+
model: DEFAULT_MODEL_BY_PROVIDER.xai
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (env.MOONSHOT_API_KEY) {
|
|
172
|
+
return {
|
|
173
|
+
provider: 'moonshot',
|
|
174
|
+
apiKey: env.MOONSHOT_API_KEY,
|
|
175
|
+
model: DEFAULT_MODEL_BY_PROVIDER.moonshot
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (env.DASHSCOPE_API_KEY || env.QWEN_API_KEY) {
|
|
179
|
+
return {
|
|
180
|
+
provider: 'dashscope-byok',
|
|
181
|
+
apiKey: env.DASHSCOPE_API_KEY || env.QWEN_API_KEY || '',
|
|
182
|
+
model: DEFAULT_MODEL_BY_PROVIDER['dashscope-byok']
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
119
188
|
export function saveConfig(config) {
|
|
120
189
|
ensureConfigDir();
|
|
121
190
|
// SECURITY: Deep copy to avoid mutating the in-memory config
|