@synergenius/flowweaver-pack-weaver 0.7.3 → 0.8.1
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/dist/bot/ai-client.d.ts +10 -0
- package/dist/bot/ai-client.d.ts.map +1 -1
- package/dist/bot/ai-client.js +27 -0
- package/dist/bot/ai-client.js.map +1 -1
- package/dist/bot/types.d.ts +1 -1
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/cli-handlers.d.ts +24 -0
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +205 -34
- package/dist/cli-handlers.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/node-types/detect-provider.d.ts.map +1 -1
- package/dist/node-types/detect-provider.js +4 -1
- package/dist/node-types/detect-provider.js.map +1 -1
- package/dist/node-types/exec-validate-retry.d.ts.map +1 -1
- package/dist/node-types/exec-validate-retry.js +2 -8
- package/dist/node-types/exec-validate-retry.js.map +1 -1
- package/dist/node-types/execute-target.d.ts.map +1 -1
- package/dist/node-types/execute-target.js +2 -8
- package/dist/node-types/execute-target.js.map +1 -1
- package/dist/node-types/fix-errors.d.ts.map +1 -1
- package/dist/node-types/fix-errors.js +2 -8
- package/dist/node-types/fix-errors.js.map +1 -1
- package/dist/node-types/genesis-apply-retry.js +2 -8
- package/dist/node-types/genesis-apply-retry.js.map +1 -1
- package/dist/node-types/genesis-propose.d.ts.map +1 -1
- package/dist/node-types/genesis-propose.js +2 -8
- package/dist/node-types/genesis-propose.js.map +1 -1
- package/dist/node-types/plan-task.d.ts.map +1 -1
- package/dist/node-types/plan-task.js +2 -8
- package/dist/node-types/plan-task.js.map +1 -1
- package/flowweaver.manifest.json +1 -1
- package/package.json +1 -1
- package/src/bot/ai-client.ts +45 -1
- package/src/bot/types.ts +1 -1
- package/src/cli-handlers.ts +210 -36
- package/src/index.ts +4 -0
- package/src/node-types/detect-provider.ts +3 -1
- package/src/node-types/exec-validate-retry.ts +2 -7
- package/src/node-types/execute-target.ts +2 -13
- package/src/node-types/fix-errors.ts +2 -7
- package/src/node-types/genesis-apply-retry.ts +2 -7
- package/src/node-types/genesis-propose.ts +2 -13
- package/src/node-types/plan-task.ts +2 -13
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { callAI, parseJsonResponse } from '../bot/ai-client.js';
|
|
2
2
|
import { auditEmit } from '../bot/audit-logger.js';
|
|
3
3
|
/**
|
|
4
4
|
* Sends task + context to the AI provider and gets back a structured
|
|
@@ -38,13 +38,7 @@ export async function weaverPlanTask(execute, ctx) {
|
|
|
38
38
|
}
|
|
39
39
|
const userPrompt = `Task: ${task.instruction}\nMode: ${task.mode ?? 'create'}\n${task.targets ? 'Targets: ' + task.targets.join(', ') : ''}\n\nPlan this task. Return a JSON plan with steps and summary.`;
|
|
40
40
|
try {
|
|
41
|
-
|
|
42
|
-
if (pInfo.type === 'anthropic') {
|
|
43
|
-
text = await callApi(pInfo.apiKey, pInfo.model ?? 'claude-sonnet-4-6', pInfo.maxTokens ?? 8192, systemPrompt, userPrompt);
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
text = callCli(pInfo.type, systemPrompt + '\n\n' + userPrompt);
|
|
47
|
-
}
|
|
41
|
+
const text = await callAI(pInfo, systemPrompt, userPrompt, 8192);
|
|
48
42
|
const plan = parseJsonResponse(text);
|
|
49
43
|
console.log(`\x1b[36m→ Plan: ${plan.summary ?? 'generated'}\x1b[0m`);
|
|
50
44
|
auditEmit('plan-created', { summary: plan.summary, stepCount: plan.steps?.length ?? 0 });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-task.js","sourceRoot":"","sources":["../../src/node-types/plan-task.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"plan-task.js","sourceRoot":"","sources":["../../src/node-types/plan-task.ts"],"names":[],"mappings":"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"}
|
package/flowweaver.manifest.json
CHANGED
package/package.json
CHANGED
package/src/bot/ai-client.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execSync, spawn } from 'node:child_process';
|
|
2
2
|
import { parseStreamLine, extractTextFromChunks } from './cli-stream-parser.js';
|
|
3
|
-
import type { StreamChunk } from './types.js';
|
|
3
|
+
import type { ProviderInfo, StreamChunk } from './types.js';
|
|
4
4
|
|
|
5
5
|
// Strip CLAUDECODE from child env so nested claude CLI invocations work.
|
|
6
6
|
const childEnv = { ...process.env };
|
|
@@ -97,6 +97,50 @@ export async function callApi(
|
|
|
97
97
|
return json.content[0]?.text ?? '';
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Call the platform-injected AI proxy (routes through IPC to the host process).
|
|
102
|
+
* Available when running inside the Studio sandbox.
|
|
103
|
+
*/
|
|
104
|
+
export async function callPlatform(
|
|
105
|
+
systemPrompt: string,
|
|
106
|
+
userPrompt: string,
|
|
107
|
+
model?: string,
|
|
108
|
+
maxTokens?: number,
|
|
109
|
+
): Promise<string> {
|
|
110
|
+
const provider = (globalThis as any).__fw_llm_provider__;
|
|
111
|
+
if (!provider) throw new Error('Platform AI provider not available');
|
|
112
|
+
const messages = [
|
|
113
|
+
{ role: 'system', content: systemPrompt },
|
|
114
|
+
{ role: 'user', content: userPrompt },
|
|
115
|
+
];
|
|
116
|
+
const response = await provider.chat(messages, { model, maxTokens });
|
|
117
|
+
return response.content ?? '';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Unified AI call that dispatches to the right backend based on provider type.
|
|
122
|
+
*/
|
|
123
|
+
export async function callAI(
|
|
124
|
+
pInfo: Pick<ProviderInfo, 'type' | 'apiKey' | 'model' | 'maxTokens'>,
|
|
125
|
+
systemPrompt: string,
|
|
126
|
+
userPrompt: string,
|
|
127
|
+
defaultMaxTokens = 4096,
|
|
128
|
+
): Promise<string> {
|
|
129
|
+
if (pInfo.type === 'platform') {
|
|
130
|
+
return callPlatform(systemPrompt, userPrompt, pInfo.model, pInfo.maxTokens ?? defaultMaxTokens);
|
|
131
|
+
}
|
|
132
|
+
if (pInfo.type === 'anthropic') {
|
|
133
|
+
return callApi(
|
|
134
|
+
pInfo.apiKey!,
|
|
135
|
+
pInfo.model ?? 'claude-sonnet-4-6',
|
|
136
|
+
pInfo.maxTokens ?? defaultMaxTokens,
|
|
137
|
+
systemPrompt,
|
|
138
|
+
userPrompt,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return callCli(pInfo.type, systemPrompt + '\n\n' + userPrompt, pInfo.model);
|
|
142
|
+
}
|
|
143
|
+
|
|
100
144
|
export function parseJsonResponse(text: string): Record<string, unknown> {
|
|
101
145
|
let cleaned = text.trim();
|
|
102
146
|
if (cleaned.startsWith('```')) {
|
package/src/bot/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type ProviderName = 'anthropic' | 'claude-cli' | 'copilot-cli' | (string & {});
|
|
2
2
|
|
|
3
3
|
export interface ProviderInfo {
|
|
4
|
-
type: 'anthropic' | 'claude-cli' | 'copilot-cli';
|
|
4
|
+
type: 'anthropic' | 'claude-cli' | 'copilot-cli' | 'platform';
|
|
5
5
|
model?: string;
|
|
6
6
|
maxTokens?: number;
|
|
7
7
|
apiKey?: string;
|
package/src/cli-handlers.ts
CHANGED
|
@@ -708,16 +708,9 @@ const MANAGED_WORKFLOWS: Record<string, string> = {
|
|
|
708
708
|
genesis: 'genesis-task',
|
|
709
709
|
};
|
|
710
710
|
|
|
711
|
-
/**
|
|
712
|
-
function
|
|
713
|
-
|
|
714
|
-
// Rewrite ../bot/*.js → package/bot
|
|
715
|
-
const rewritten = source
|
|
716
|
-
.replace(/from\s+['"]\.\.\/node-types\/[^'"]+['"]/g, "from '@synergenius/flowweaver-pack-weaver/node-types'")
|
|
717
|
-
.replace(/from\s+['"]\.\.\/bot\/[^'"]+['"]/g, "from '@synergenius/flowweaver-pack-weaver/bot'");
|
|
718
|
-
|
|
719
|
-
// Deduplicate import lines: collapse multiple imports from the same module
|
|
720
|
-
const lines = rewritten.split('\n');
|
|
711
|
+
/** Deduplicate import lines: collapse multiple imports from the same module. */
|
|
712
|
+
function deduplicateImports(source: string): string {
|
|
713
|
+
const lines = source.split('\n');
|
|
721
714
|
const importMap = new Map<string, Set<string>>();
|
|
722
715
|
const nonImportLines: string[] = [];
|
|
723
716
|
let pastImports = false;
|
|
@@ -748,6 +741,21 @@ function rewritePackImports(source: string): string {
|
|
|
748
741
|
return [...dedupedImports, ...nonImportLines].join('\n');
|
|
749
742
|
}
|
|
750
743
|
|
|
744
|
+
/** Rewrite node-type source: ../bot/*.js → package barrel, deduplicate. */
|
|
745
|
+
function rewriteNodeTypeImports(source: string): string {
|
|
746
|
+
const rewritten = source
|
|
747
|
+
.replace(/from\s+['"]\.\.\/bot\/[^'"]+['"]/g, "from '@synergenius/flowweaver-pack-weaver/bot'");
|
|
748
|
+
return deduplicateImports(rewritten);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/** Rewrite workflow source: ../node-types/ → ./node-types/ (local ejected), ../bot/ → package barrel. */
|
|
752
|
+
function rewriteWorkflowImports(source: string): string {
|
|
753
|
+
const rewritten = source
|
|
754
|
+
.replace(/from\s+['"]\.\.\/node-types\//g, "from './node-types/")
|
|
755
|
+
.replace(/from\s+['"]\.\.\/bot\/[^'"]+['"]/g, "from '@synergenius/flowweaver-pack-weaver/bot'");
|
|
756
|
+
return deduplicateImports(rewritten);
|
|
757
|
+
}
|
|
758
|
+
|
|
751
759
|
/** Read a managed workflow source from the pack. */
|
|
752
760
|
function readPackWorkflowSource(packRoot: URL, workflowBaseName: string): string {
|
|
753
761
|
const candidates = [
|
|
@@ -765,6 +773,180 @@ function readPackWorkflowSource(packRoot: URL, workflowBaseName: string): string
|
|
|
765
773
|
throw new Error(`Could not find managed workflow: ${workflowBaseName}`);
|
|
766
774
|
}
|
|
767
775
|
|
|
776
|
+
export interface EjectResult {
|
|
777
|
+
workflow: string;
|
|
778
|
+
file: string;
|
|
779
|
+
nodeTypes: string[];
|
|
780
|
+
botFiles?: string[];
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Read a pack source file, trying src/ then dist/.
|
|
785
|
+
*/
|
|
786
|
+
function readPackFile(packRoot: URL, relativePath: string): string {
|
|
787
|
+
const srcCandidate = new URL(`src/${relativePath}`, packRoot);
|
|
788
|
+
const distCandidate = new URL(`dist/${relativePath}`, packRoot);
|
|
789
|
+
try {
|
|
790
|
+
return fs.readFileSync(srcCandidate, 'utf-8');
|
|
791
|
+
} catch {
|
|
792
|
+
return fs.readFileSync(distCandidate, 'utf-8');
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Collect bot utility files transitively imported by node-type files.
|
|
798
|
+
* Traces `from '../bot/<file>.js'` and `from './<file>.js'` within bot/.
|
|
799
|
+
*/
|
|
800
|
+
function collectBotDeps(packRoot: URL, ntFiles: string[]): string[] {
|
|
801
|
+
const visited = new Set<string>();
|
|
802
|
+
const queue: string[] = [];
|
|
803
|
+
|
|
804
|
+
// Seed from node-type files: look for ../bot/ imports
|
|
805
|
+
for (const ntFile of ntFiles) {
|
|
806
|
+
let ntSource: string;
|
|
807
|
+
try { ntSource = readPackFile(packRoot, `node-types/${ntFile}`); } catch { continue; }
|
|
808
|
+
const botImportRegex = /from\s+['"]\.\.\/bot\/([^'"]+)['"]/g;
|
|
809
|
+
let m;
|
|
810
|
+
while ((m = botImportRegex.exec(ntSource)) !== null) {
|
|
811
|
+
const botFile = m[1]!.replace(/\.js$/, '.ts');
|
|
812
|
+
if (!visited.has(botFile)) {
|
|
813
|
+
visited.add(botFile);
|
|
814
|
+
queue.push(botFile);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Always include types.ts
|
|
820
|
+
if (!visited.has('types.ts')) {
|
|
821
|
+
visited.add('types.ts');
|
|
822
|
+
queue.push('types.ts');
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Trace transitive deps within bot/
|
|
826
|
+
while (queue.length > 0) {
|
|
827
|
+
const botFile = queue.shift()!;
|
|
828
|
+
let botSource: string;
|
|
829
|
+
try { botSource = readPackFile(packRoot, `bot/${botFile}`); } catch { continue; }
|
|
830
|
+
const localImportRegex = /from\s+['"]\.\/([^'"]+)['"]/g;
|
|
831
|
+
let m;
|
|
832
|
+
while ((m = localImportRegex.exec(botSource)) !== null) {
|
|
833
|
+
const dep = m[1]!.replace(/\.js$/, '.ts');
|
|
834
|
+
if (!visited.has(dep)) {
|
|
835
|
+
visited.add(dep);
|
|
836
|
+
queue.push(dep);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
return [...visited];
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Eject workflow + node-type files into a destination directory.
|
|
846
|
+
*
|
|
847
|
+
* When `standalone` is true, bot utility files are also ejected so the
|
|
848
|
+
* workflows run without the pack installed. Import paths in workflows
|
|
849
|
+
* are rewritten from `../node-types/` to `./node-types/`, and `../bot/`
|
|
850
|
+
* imports in node-type files remain as-is (pointing to the local `bot/`
|
|
851
|
+
* directory).
|
|
852
|
+
*
|
|
853
|
+
* When `standalone` is false (default), bot imports are rewritten to
|
|
854
|
+
* the package barrel (`@synergenius/flowweaver-pack-weaver/bot`).
|
|
855
|
+
*/
|
|
856
|
+
export function ejectWorkflows(opts: {
|
|
857
|
+
destDir: string;
|
|
858
|
+
workflows?: string[];
|
|
859
|
+
force?: boolean;
|
|
860
|
+
standalone?: boolean;
|
|
861
|
+
}): EjectResult[] {
|
|
862
|
+
const packRoot = new URL('..', import.meta.url);
|
|
863
|
+
const workflowKeys = opts.workflows ?? Object.keys(MANAGED_WORKFLOWS);
|
|
864
|
+
const standalone = opts.standalone === true;
|
|
865
|
+
const results: EjectResult[] = [];
|
|
866
|
+
const allNtFiles: string[] = [];
|
|
867
|
+
|
|
868
|
+
for (const key of workflowKeys) {
|
|
869
|
+
const baseName = MANAGED_WORKFLOWS[key];
|
|
870
|
+
if (!baseName) continue;
|
|
871
|
+
|
|
872
|
+
const source = readPackWorkflowSource(packRoot, baseName);
|
|
873
|
+
|
|
874
|
+
// In standalone mode, only rewrite ../node-types/ to ./node-types/
|
|
875
|
+
// and keep ../bot/ as-is (local bot/ dir). Otherwise use package barrel.
|
|
876
|
+
let rewritten: string;
|
|
877
|
+
if (standalone) {
|
|
878
|
+
rewritten = source.replace(/from\s+['"]\.\.\/node-types\//g, "from './node-types/");
|
|
879
|
+
} else {
|
|
880
|
+
rewritten = rewriteWorkflowImports(source);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const wfFile = `${baseName}.ts`;
|
|
884
|
+
const wfPath = path.join(opts.destDir, wfFile);
|
|
885
|
+
|
|
886
|
+
// Collect node-type files referenced by the workflow
|
|
887
|
+
const ntImportRegex = /from\s+['"]\.\.\/node-types\/([^'"]+)['"]/g;
|
|
888
|
+
const ntFiles: string[] = [];
|
|
889
|
+
let ntMatch;
|
|
890
|
+
while ((ntMatch = ntImportRegex.exec(source)) !== null) {
|
|
891
|
+
const ntFile = ntMatch[1]!.replace(/\.js$/, '.ts');
|
|
892
|
+
if (!ntFiles.includes(ntFile)) ntFiles.push(ntFile);
|
|
893
|
+
if (!allNtFiles.includes(ntFile)) allNtFiles.push(ntFile);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Write workflow (skip if exists and not forced)
|
|
897
|
+
let exists = false;
|
|
898
|
+
try { fs.statSync(wfPath); exists = true; } catch {}
|
|
899
|
+
if (!exists || opts.force) {
|
|
900
|
+
fs.mkdirSync(path.join(opts.destDir, 'node-types'), { recursive: true });
|
|
901
|
+
fs.writeFileSync(wfPath, rewritten, 'utf-8');
|
|
902
|
+
|
|
903
|
+
// Eject each referenced node-type file
|
|
904
|
+
for (const ntFile of ntFiles) {
|
|
905
|
+
const ntSrcCandidates = [
|
|
906
|
+
new URL(`src/node-types/${ntFile}`, packRoot),
|
|
907
|
+
new URL(`dist/node-types/${ntFile}`, packRoot),
|
|
908
|
+
];
|
|
909
|
+
for (const candidate of ntSrcCandidates) {
|
|
910
|
+
try {
|
|
911
|
+
const ntSource = fs.readFileSync(candidate, 'utf-8');
|
|
912
|
+
// In standalone mode, keep ../bot/ as-is (local). Otherwise rewrite to barrel.
|
|
913
|
+
const ntRewritten = standalone ? ntSource : rewriteNodeTypeImports(ntSource);
|
|
914
|
+
fs.writeFileSync(path.join(opts.destDir, 'node-types', ntFile), ntRewritten, 'utf-8');
|
|
915
|
+
break;
|
|
916
|
+
} catch { /* try next */ }
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
results.push({ workflow: key, file: wfFile, nodeTypes: ntFiles });
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// In standalone mode, eject bot utility files
|
|
925
|
+
if (standalone) {
|
|
926
|
+
const botFiles = collectBotDeps(packRoot, allNtFiles);
|
|
927
|
+
fs.mkdirSync(path.join(opts.destDir, 'bot'), { recursive: true });
|
|
928
|
+
for (const botFile of botFiles) {
|
|
929
|
+
const destPath = path.join(opts.destDir, 'bot', botFile);
|
|
930
|
+
let exists = false;
|
|
931
|
+
try { fs.statSync(destPath); exists = true; } catch {}
|
|
932
|
+
if (!exists || opts.force) {
|
|
933
|
+
try {
|
|
934
|
+
const botSource = readPackFile(packRoot, `bot/${botFile}`);
|
|
935
|
+
fs.writeFileSync(destPath, botSource, 'utf-8');
|
|
936
|
+
} catch {
|
|
937
|
+
// File not found in pack, skip
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
// Attach bot files to results
|
|
942
|
+
for (const r of results) {
|
|
943
|
+
r.botFiles = botFiles;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
return results;
|
|
948
|
+
}
|
|
949
|
+
|
|
768
950
|
/**
|
|
769
951
|
* Resolve a managed workflow path. Checks for a local ejected override first,
|
|
770
952
|
* then falls back to the pack's own source or dist.
|
|
@@ -798,13 +980,6 @@ function resolveWorkflowPath(workflowKey: string, cwd: string): string {
|
|
|
798
980
|
}
|
|
799
981
|
|
|
800
982
|
export async function handleEject(opts: ParsedArgs): Promise<void> {
|
|
801
|
-
const packRoot = new URL('..', import.meta.url);
|
|
802
|
-
|
|
803
|
-
// Determine which workflows to eject
|
|
804
|
-
const workflowKeys = opts.ejectWorkflow
|
|
805
|
-
? [opts.ejectWorkflow]
|
|
806
|
-
: Object.keys(MANAGED_WORKFLOWS);
|
|
807
|
-
|
|
808
983
|
if (opts.ejectWorkflow && !MANAGED_WORKFLOWS[opts.ejectWorkflow]) {
|
|
809
984
|
console.error(`[weaver] Unknown workflow: ${opts.ejectWorkflow}`);
|
|
810
985
|
console.error(`[weaver] Available: ${Object.keys(MANAGED_WORKFLOWS).join(', ')}`);
|
|
@@ -812,29 +987,25 @@ export async function handleEject(opts: ParsedArgs): Promise<void> {
|
|
|
812
987
|
return;
|
|
813
988
|
}
|
|
814
989
|
|
|
815
|
-
const
|
|
990
|
+
const destDir = process.cwd();
|
|
991
|
+
const workflows = opts.ejectWorkflow ? [opts.ejectWorkflow] : undefined;
|
|
816
992
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
return;
|
|
827
|
-
}
|
|
993
|
+
let results: EjectResult[];
|
|
994
|
+
try {
|
|
995
|
+
results = ejectWorkflows({ destDir, workflows, force: true, standalone: true });
|
|
996
|
+
} catch (err: unknown) {
|
|
997
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
998
|
+
console.error(`[weaver] ${msg}`);
|
|
999
|
+
process.exit(1);
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
828
1002
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const destPath = path.resolve(process.cwd(), fileName);
|
|
832
|
-
fs.writeFileSync(destPath, finalSource, 'utf-8');
|
|
833
|
-
ejectedFiles[key] = fileName;
|
|
834
|
-
console.log(`[weaver] Ejected ${key} → ${destPath}`);
|
|
1003
|
+
for (const r of results) {
|
|
1004
|
+
console.log(`[weaver] Ejected ${r.workflow} → ${path.resolve(destDir, r.file)} (${r.nodeTypes.length} node types)`);
|
|
835
1005
|
}
|
|
836
1006
|
|
|
837
1007
|
// Read pack version
|
|
1008
|
+
const packRoot = new URL('..', import.meta.url);
|
|
838
1009
|
let packVersion = 'unknown';
|
|
839
1010
|
try {
|
|
840
1011
|
const pkgPath = new URL('package.json', packRoot);
|
|
@@ -843,13 +1014,16 @@ export async function handleEject(opts: ParsedArgs): Promise<void> {
|
|
|
843
1014
|
} catch { /* ignore */ }
|
|
844
1015
|
|
|
845
1016
|
// Write/update .weaver-meta.json (merge with existing if present)
|
|
846
|
-
const metaPath = path.resolve(
|
|
1017
|
+
const metaPath = path.resolve(destDir, '.weaver-meta.json');
|
|
847
1018
|
let existingMeta: Record<string, unknown> = {};
|
|
848
1019
|
try {
|
|
849
1020
|
existingMeta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
|
|
850
1021
|
} catch { /* start fresh */ }
|
|
851
1022
|
|
|
852
1023
|
const existingWorkflows = (existingMeta.workflowFiles as Record<string, string>) ?? {};
|
|
1024
|
+
const ejectedFiles: Record<string, string> = {};
|
|
1025
|
+
for (const r of results) ejectedFiles[r.workflow] = r.file;
|
|
1026
|
+
|
|
853
1027
|
const meta = {
|
|
854
1028
|
ejected: true,
|
|
855
1029
|
packVersion,
|
package/src/index.ts
CHANGED
|
@@ -171,3 +171,7 @@ export {
|
|
|
171
171
|
genesisUpdateHistory,
|
|
172
172
|
genesisReport,
|
|
173
173
|
} from './node-types/index.js';
|
|
174
|
+
|
|
175
|
+
// Eject API (for platform/studio server-side use)
|
|
176
|
+
export { ejectWorkflows } from './cli-handlers.js';
|
|
177
|
+
export type { EjectResult } from './cli-handlers.js';
|
|
@@ -39,7 +39,9 @@ export function weaverDetectProvider(projectDir: string, config: WeaverConfig):
|
|
|
39
39
|
} else if (providerSetting !== 'auto') {
|
|
40
40
|
type = providerSetting;
|
|
41
41
|
} else {
|
|
42
|
-
if (
|
|
42
|
+
if ((globalThis as any).__fw_llm_provider__) {
|
|
43
|
+
type = 'platform';
|
|
44
|
+
} else if (process.env.ANTHROPIC_API_KEY) {
|
|
43
45
|
type = 'anthropic';
|
|
44
46
|
} else if (whichSafe('claude', projectDir)) {
|
|
45
47
|
type = 'claude-cli';
|
|
@@ -2,7 +2,7 @@ import * as fs from 'node:fs';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import type { WeaverContext } from '../bot/types.js';
|
|
5
|
-
import {
|
|
5
|
+
import { callAI, parseJsonResponse } from '../bot/ai-client.js';
|
|
6
6
|
import { executeStep } from '../bot/step-executor.js';
|
|
7
7
|
import { validateFiles } from '../bot/file-validator.js';
|
|
8
8
|
import { auditEmit } from '../bot/audit-logger.js';
|
|
@@ -103,12 +103,7 @@ export async function weaverExecValidateRetry(
|
|
|
103
103
|
|
|
104
104
|
const fixPrompt = `The following validation errors occurred:\n${errors}\n\nProvide a fix plan as JSON with steps and summary.`;
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
if (pInfo.type === 'anthropic') {
|
|
108
|
-
text = await callApi(pInfo.apiKey!, pInfo.model ?? 'claude-sonnet-4-6', pInfo.maxTokens ?? 8192, systemPrompt, fixPrompt);
|
|
109
|
-
} else {
|
|
110
|
-
text = callCli(pInfo.type, systemPrompt + '\n\n' + fixPrompt);
|
|
111
|
-
}
|
|
106
|
+
const text = await callAI(pInfo, systemPrompt, fixPrompt, 8192);
|
|
112
107
|
|
|
113
108
|
currentPlan = parseJsonResponse(text);
|
|
114
109
|
console.log(`\x1b[36m→ Fix plan: ${(currentPlan as { summary?: string }).summary ?? 'generated'}\x1b[0m`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WeaverEnv, ProviderInfo, WeaverContext } from '../bot/types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { callAI, parseJsonResponse } from '../bot/ai-client.js';
|
|
3
3
|
|
|
4
4
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
5
|
async function buildWeaverPrompt(): Promise<string> {
|
|
@@ -195,18 +195,7 @@ export async function weaverExecuteTarget(
|
|
|
195
195
|
: JSON.stringify(req.context, null, 2);
|
|
196
196
|
const userPrompt = `Context:\n${contextStr}\n\nInstructions:\n${req.prompt}`;
|
|
197
197
|
|
|
198
|
-
|
|
199
|
-
if (pInfo.type === 'anthropic') {
|
|
200
|
-
text = await callApi(
|
|
201
|
-
pInfo.apiKey!,
|
|
202
|
-
pInfo.model ?? 'claude-sonnet-4-6',
|
|
203
|
-
pInfo.maxTokens ?? 4096,
|
|
204
|
-
systemPrompt,
|
|
205
|
-
userPrompt,
|
|
206
|
-
);
|
|
207
|
-
} else {
|
|
208
|
-
text = callCli(pInfo.type, systemPrompt + '\n\n' + userPrompt);
|
|
209
|
-
}
|
|
198
|
+
const text = await callAI(pInfo, systemPrompt, userPrompt);
|
|
210
199
|
|
|
211
200
|
return parseJsonResponse(text);
|
|
212
201
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WeaverEnv } from '../bot/types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { callAI, parseJsonResponse } from '../bot/ai-client.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* When validation fails, sends errors + context to the AI and
|
|
@@ -50,12 +50,7 @@ export async function weaverFixErrors(
|
|
|
50
50
|
const userPrompt = `The following validation errors occurred:\n${errorSummary}\n\nProvide a fix plan as JSON with "steps" and "summary". Each step needs "id", "operation", "description", and "args".`;
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
|
-
|
|
54
|
-
if (pInfo.type === 'anthropic') {
|
|
55
|
-
text = await callApi(pInfo.apiKey!, pInfo.model ?? 'claude-sonnet-4-6', pInfo.maxTokens ?? 8192, systemPrompt, userPrompt);
|
|
56
|
-
} else {
|
|
57
|
-
text = callCli(pInfo.type, systemPrompt + '\n\n' + userPrompt);
|
|
58
|
-
}
|
|
53
|
+
const text = await callAI(pInfo, systemPrompt, userPrompt, 8192);
|
|
59
54
|
|
|
60
55
|
const plan = parseJsonResponse(text);
|
|
61
56
|
console.log(`\x1b[36m→ Fix plan: ${(plan as { summary?: string }).summary ?? 'generated'}\x1b[0m`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import type { GenesisConfig, GenesisContext } from '../bot/types.js';
|
|
4
|
-
import {
|
|
4
|
+
import { callAI, parseJsonResponse } from '../bot/ai-client.js';
|
|
5
5
|
import { GenesisStore } from '../bot/genesis-store.js';
|
|
6
6
|
import { getGenesisSystemPrompt, getOperationExamples } from '../bot/genesis-prompt-context.js';
|
|
7
7
|
|
|
@@ -126,12 +126,7 @@ async function reviseProposal(
|
|
|
126
126
|
'Return the full revised proposal as JSON. Use node IDs and port names that exist in the workflow structure above.',
|
|
127
127
|
].join('\n');
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
if (pInfo.type === 'anthropic') {
|
|
131
|
-
text = await callApi(pInfo.apiKey!, pInfo.model ?? 'claude-sonnet-4-6', pInfo.maxTokens ?? 8192, systemPrompt, userPrompt);
|
|
132
|
-
} else {
|
|
133
|
-
text = callCli(pInfo.type, systemPrompt + '\n\n' + userPrompt);
|
|
134
|
-
}
|
|
129
|
+
const text = await callAI(pInfo, systemPrompt, userPrompt, 8192);
|
|
135
130
|
|
|
136
131
|
const parsed = parseJsonResponse(text);
|
|
137
132
|
return JSON.stringify(parsed);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
2
|
import type { GenesisConfig, GenesisProposal, GenesisContext } from '../bot/types.js';
|
|
3
|
-
import {
|
|
3
|
+
import { callAI, parseJsonResponse } from '../bot/ai-client.js';
|
|
4
4
|
import { getGenesisSystemPrompt, getOperationExamples } from '../bot/genesis-prompt-context.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -60,18 +60,7 @@ export async function genesisPropose(
|
|
|
60
60
|
|
|
61
61
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
62
62
|
try {
|
|
63
|
-
|
|
64
|
-
if (pInfo.type === 'anthropic') {
|
|
65
|
-
text = await callApi(
|
|
66
|
-
pInfo.apiKey!,
|
|
67
|
-
pInfo.model ?? 'claude-sonnet-4-6',
|
|
68
|
-
pInfo.maxTokens ?? 8192,
|
|
69
|
-
systemPrompt,
|
|
70
|
-
userPrompt,
|
|
71
|
-
);
|
|
72
|
-
} else {
|
|
73
|
-
text = callCli(pInfo.type, systemPrompt + '\n\n' + userPrompt, pInfo.model);
|
|
74
|
-
}
|
|
63
|
+
const text = await callAI(pInfo, systemPrompt, userPrompt, 8192);
|
|
75
64
|
|
|
76
65
|
const proposal = parseJsonResponse(text) as unknown as GenesisProposal;
|
|
77
66
|
console.log(`\x1b[36m→ Proposal: ${proposal.summary} (${proposal.operations.length} ops, impact=${proposal.impactLevel})\x1b[0m`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WeaverContext } from '../bot/types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { callAI, parseJsonResponse } from '../bot/ai-client.js';
|
|
3
3
|
import { auditEmit } from '../bot/audit-logger.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -49,18 +49,7 @@ export async function weaverPlanTask(
|
|
|
49
49
|
const userPrompt = `Task: ${task.instruction}\nMode: ${task.mode ?? 'create'}\n${task.targets ? 'Targets: ' + task.targets.join(', ') : ''}\n\nPlan this task. Return a JSON plan with steps and summary.`;
|
|
50
50
|
|
|
51
51
|
try {
|
|
52
|
-
|
|
53
|
-
if (pInfo.type === 'anthropic') {
|
|
54
|
-
text = await callApi(
|
|
55
|
-
pInfo.apiKey!,
|
|
56
|
-
pInfo.model ?? 'claude-sonnet-4-6',
|
|
57
|
-
pInfo.maxTokens ?? 8192,
|
|
58
|
-
systemPrompt,
|
|
59
|
-
userPrompt,
|
|
60
|
-
);
|
|
61
|
-
} else {
|
|
62
|
-
text = callCli(pInfo.type, systemPrompt + '\n\n' + userPrompt);
|
|
63
|
-
}
|
|
52
|
+
const text = await callAI(pInfo, systemPrompt, userPrompt, 8192);
|
|
64
53
|
|
|
65
54
|
const plan = parseJsonResponse(text);
|
|
66
55
|
console.log(`\x1b[36m→ Plan: ${(plan as { summary?: string }).summary ?? 'generated'}\x1b[0m`);
|