onbuzz 3.3.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/LICENSE +267 -0
- package/README.md +425 -0
- package/bin/cli.js +556 -0
- package/bin/loxia-terminal-v2.js +162 -0
- package/bin/loxia-terminal.js +90 -0
- package/bin/start-with-terminal.js +200 -0
- package/node_modules/@isaacs/balanced-match/LICENSE.md +23 -0
- package/node_modules/@isaacs/balanced-match/README.md +60 -0
- package/node_modules/@isaacs/balanced-match/dist/commonjs/index.d.ts +9 -0
- package/node_modules/@isaacs/balanced-match/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/@isaacs/balanced-match/dist/commonjs/index.js +59 -0
- package/node_modules/@isaacs/balanced-match/dist/commonjs/index.js.map +1 -0
- package/node_modules/@isaacs/balanced-match/dist/commonjs/package.json +3 -0
- package/node_modules/@isaacs/balanced-match/dist/esm/index.d.ts +9 -0
- package/node_modules/@isaacs/balanced-match/dist/esm/index.d.ts.map +1 -0
- package/node_modules/@isaacs/balanced-match/dist/esm/index.js +54 -0
- package/node_modules/@isaacs/balanced-match/dist/esm/index.js.map +1 -0
- package/node_modules/@isaacs/balanced-match/dist/esm/package.json +3 -0
- package/node_modules/@isaacs/balanced-match/package.json +79 -0
- package/node_modules/@isaacs/brace-expansion/LICENSE +23 -0
- package/node_modules/@isaacs/brace-expansion/README.md +97 -0
- package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.d.ts +6 -0
- package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js +199 -0
- package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js.map +1 -0
- package/node_modules/@isaacs/brace-expansion/dist/commonjs/package.json +3 -0
- package/node_modules/@isaacs/brace-expansion/dist/esm/index.d.ts +6 -0
- package/node_modules/@isaacs/brace-expansion/dist/esm/index.d.ts.map +1 -0
- package/node_modules/@isaacs/brace-expansion/dist/esm/index.js +195 -0
- package/node_modules/@isaacs/brace-expansion/dist/esm/index.js.map +1 -0
- package/node_modules/@isaacs/brace-expansion/dist/esm/package.json +3 -0
- package/node_modules/@isaacs/brace-expansion/package.json +60 -0
- package/node_modules/glob/LICENSE.md +63 -0
- package/node_modules/glob/README.md +1177 -0
- package/node_modules/glob/dist/commonjs/glob.d.ts +388 -0
- package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -0
- package/node_modules/glob/dist/commonjs/glob.js +247 -0
- package/node_modules/glob/dist/commonjs/glob.js.map +1 -0
- package/node_modules/glob/dist/commonjs/has-magic.d.ts +14 -0
- package/node_modules/glob/dist/commonjs/has-magic.d.ts.map +1 -0
- package/node_modules/glob/dist/commonjs/has-magic.js +27 -0
- package/node_modules/glob/dist/commonjs/has-magic.js.map +1 -0
- package/node_modules/glob/dist/commonjs/ignore.d.ts +24 -0
- package/node_modules/glob/dist/commonjs/ignore.d.ts.map +1 -0
- package/node_modules/glob/dist/commonjs/ignore.js +119 -0
- package/node_modules/glob/dist/commonjs/ignore.js.map +1 -0
- package/node_modules/glob/dist/commonjs/index.d.ts +97 -0
- package/node_modules/glob/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/glob/dist/commonjs/index.js +68 -0
- package/node_modules/glob/dist/commonjs/index.js.map +1 -0
- package/node_modules/glob/dist/commonjs/index.min.js +4 -0
- package/node_modules/glob/dist/commonjs/index.min.js.map +7 -0
- package/node_modules/glob/dist/commonjs/package.json +3 -0
- package/node_modules/glob/dist/commonjs/pattern.d.ts +76 -0
- package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -0
- package/node_modules/glob/dist/commonjs/pattern.js +219 -0
- package/node_modules/glob/dist/commonjs/pattern.js.map +1 -0
- package/node_modules/glob/dist/commonjs/processor.d.ts +59 -0
- package/node_modules/glob/dist/commonjs/processor.d.ts.map +1 -0
- package/node_modules/glob/dist/commonjs/processor.js +301 -0
- package/node_modules/glob/dist/commonjs/processor.js.map +1 -0
- package/node_modules/glob/dist/commonjs/walker.d.ts +97 -0
- package/node_modules/glob/dist/commonjs/walker.d.ts.map +1 -0
- package/node_modules/glob/dist/commonjs/walker.js +387 -0
- package/node_modules/glob/dist/commonjs/walker.js.map +1 -0
- package/node_modules/glob/dist/esm/glob.d.ts +388 -0
- package/node_modules/glob/dist/esm/glob.d.ts.map +1 -0
- package/node_modules/glob/dist/esm/glob.js +243 -0
- package/node_modules/glob/dist/esm/glob.js.map +1 -0
- package/node_modules/glob/dist/esm/has-magic.d.ts +14 -0
- package/node_modules/glob/dist/esm/has-magic.d.ts.map +1 -0
- package/node_modules/glob/dist/esm/has-magic.js +23 -0
- package/node_modules/glob/dist/esm/has-magic.js.map +1 -0
- package/node_modules/glob/dist/esm/ignore.d.ts +24 -0
- package/node_modules/glob/dist/esm/ignore.d.ts.map +1 -0
- package/node_modules/glob/dist/esm/ignore.js +115 -0
- package/node_modules/glob/dist/esm/ignore.js.map +1 -0
- package/node_modules/glob/dist/esm/index.d.ts +97 -0
- package/node_modules/glob/dist/esm/index.d.ts.map +1 -0
- package/node_modules/glob/dist/esm/index.js +55 -0
- package/node_modules/glob/dist/esm/index.js.map +1 -0
- package/node_modules/glob/dist/esm/index.min.js +4 -0
- package/node_modules/glob/dist/esm/index.min.js.map +7 -0
- package/node_modules/glob/dist/esm/package.json +3 -0
- package/node_modules/glob/dist/esm/pattern.d.ts +76 -0
- package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -0
- package/node_modules/glob/dist/esm/pattern.js +215 -0
- package/node_modules/glob/dist/esm/pattern.js.map +1 -0
- package/node_modules/glob/dist/esm/processor.d.ts +59 -0
- package/node_modules/glob/dist/esm/processor.d.ts.map +1 -0
- package/node_modules/glob/dist/esm/processor.js +294 -0
- package/node_modules/glob/dist/esm/processor.js.map +1 -0
- package/node_modules/glob/dist/esm/walker.d.ts +97 -0
- package/node_modules/glob/dist/esm/walker.d.ts.map +1 -0
- package/node_modules/glob/dist/esm/walker.js +381 -0
- package/node_modules/glob/dist/esm/walker.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/LICENSE.md +55 -0
- package/node_modules/glob/node_modules/minimatch/README.md +453 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts +2 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js +14 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts +20 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js +591 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts +8 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js +152 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts +15 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js +30 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts +94 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js +1029 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/package.json +3 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts +22 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js +38 -0
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts +2 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js +10 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts +20 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js +587 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts +8 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js +148 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts +15 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js +26 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts +94 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.js +1016 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/package.json +3 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts +22 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts.map +1 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js +34 -0
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js.map +1 -0
- package/node_modules/glob/node_modules/minimatch/package.json +67 -0
- package/node_modules/glob/package.json +101 -0
- package/node_modules/minipass/LICENSE +15 -0
- package/node_modules/minipass/README.md +825 -0
- package/node_modules/minipass/dist/commonjs/index.d.ts +549 -0
- package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/minipass/dist/commonjs/index.js +1028 -0
- package/node_modules/minipass/dist/commonjs/index.js.map +1 -0
- package/node_modules/minipass/dist/commonjs/package.json +3 -0
- package/node_modules/minipass/dist/esm/index.d.ts +549 -0
- package/node_modules/minipass/dist/esm/index.d.ts.map +1 -0
- package/node_modules/minipass/dist/esm/index.js +1018 -0
- package/node_modules/minipass/dist/esm/index.js.map +1 -0
- package/node_modules/minipass/dist/esm/package.json +3 -0
- package/node_modules/minipass/package.json +82 -0
- package/node_modules/package-json-from-dist/LICENSE.md +63 -0
- package/node_modules/package-json-from-dist/README.md +110 -0
- package/node_modules/package-json-from-dist/dist/commonjs/index.d.ts +89 -0
- package/node_modules/package-json-from-dist/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/package-json-from-dist/dist/commonjs/index.js +134 -0
- package/node_modules/package-json-from-dist/dist/commonjs/index.js.map +1 -0
- package/node_modules/package-json-from-dist/dist/commonjs/package.json +3 -0
- package/node_modules/package-json-from-dist/dist/esm/index.d.ts +89 -0
- package/node_modules/package-json-from-dist/dist/esm/index.d.ts.map +1 -0
- package/node_modules/package-json-from-dist/dist/esm/index.js +129 -0
- package/node_modules/package-json-from-dist/dist/esm/index.js.map +1 -0
- package/node_modules/package-json-from-dist/dist/esm/package.json +3 -0
- package/node_modules/package-json-from-dist/package.json +68 -0
- package/node_modules/path-scurry/LICENSE.md +55 -0
- package/node_modules/path-scurry/README.md +636 -0
- package/node_modules/path-scurry/dist/commonjs/index.d.ts +1115 -0
- package/node_modules/path-scurry/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/path-scurry/dist/commonjs/index.js +2018 -0
- package/node_modules/path-scurry/dist/commonjs/index.js.map +1 -0
- package/node_modules/path-scurry/dist/commonjs/package.json +3 -0
- package/node_modules/path-scurry/dist/esm/index.d.ts +1115 -0
- package/node_modules/path-scurry/dist/esm/index.d.ts.map +1 -0
- package/node_modules/path-scurry/dist/esm/index.js +1983 -0
- package/node_modules/path-scurry/dist/esm/index.js.map +1 -0
- package/node_modules/path-scurry/dist/esm/package.json +3 -0
- package/node_modules/path-scurry/node_modules/lru-cache/LICENSE.md +55 -0
- package/node_modules/path-scurry/node_modules/lru-cache/README.md +383 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts +1323 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +1589 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/package.json +3 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts +1323 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +1585 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/package.json +3 -0
- package/node_modules/path-scurry/node_modules/lru-cache/package.json +101 -0
- package/node_modules/path-scurry/package.json +88 -0
- package/node_modules/rimraf/LICENSE.md +55 -0
- package/node_modules/rimraf/README.md +226 -0
- package/node_modules/rimraf/dist/commonjs/default-tmp.d.ts +3 -0
- package/node_modules/rimraf/dist/commonjs/default-tmp.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/default-tmp.js +58 -0
- package/node_modules/rimraf/dist/commonjs/default-tmp.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/error.d.ts +6 -0
- package/node_modules/rimraf/dist/commonjs/error.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/error.js +10 -0
- package/node_modules/rimraf/dist/commonjs/error.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/fix-eperm.d.ts +3 -0
- package/node_modules/rimraf/dist/commonjs/fix-eperm.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/fix-eperm.js +38 -0
- package/node_modules/rimraf/dist/commonjs/fix-eperm.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/fs.d.ts +15 -0
- package/node_modules/rimraf/dist/commonjs/fs.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/fs.js +33 -0
- package/node_modules/rimraf/dist/commonjs/fs.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/ignore-enoent.d.ts +3 -0
- package/node_modules/rimraf/dist/commonjs/ignore-enoent.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/ignore-enoent.js +24 -0
- package/node_modules/rimraf/dist/commonjs/ignore-enoent.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/index.d.ts +50 -0
- package/node_modules/rimraf/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/index.js +78 -0
- package/node_modules/rimraf/dist/commonjs/index.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/opt-arg.d.ts +34 -0
- package/node_modules/rimraf/dist/commonjs/opt-arg.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/opt-arg.js +53 -0
- package/node_modules/rimraf/dist/commonjs/opt-arg.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/package.json +3 -0
- package/node_modules/rimraf/dist/commonjs/path-arg.d.ts +4 -0
- package/node_modules/rimraf/dist/commonjs/path-arg.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/path-arg.js +48 -0
- package/node_modules/rimraf/dist/commonjs/path-arg.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/readdir-or-error.d.ts +3 -0
- package/node_modules/rimraf/dist/commonjs/readdir-or-error.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/readdir-or-error.js +19 -0
- package/node_modules/rimraf/dist/commonjs/readdir-or-error.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/retry-busy.d.ts +8 -0
- package/node_modules/rimraf/dist/commonjs/retry-busy.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/retry-busy.js +65 -0
- package/node_modules/rimraf/dist/commonjs/retry-busy.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-manual.d.ts +3 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-manual.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-manual.js +8 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-manual.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.d.ts +4 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.js +138 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-native.d.ts +4 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-native.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-native.js +24 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-native.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-posix.d.ts +4 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-posix.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-posix.js +103 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-posix.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-windows.d.ts +4 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-windows.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-windows.js +159 -0
- package/node_modules/rimraf/dist/commonjs/rimraf-windows.js.map +1 -0
- package/node_modules/rimraf/dist/commonjs/use-native.d.ts +4 -0
- package/node_modules/rimraf/dist/commonjs/use-native.d.ts.map +1 -0
- package/node_modules/rimraf/dist/commonjs/use-native.js +18 -0
- package/node_modules/rimraf/dist/commonjs/use-native.js.map +1 -0
- package/node_modules/rimraf/dist/esm/bin.d.mts +3 -0
- package/node_modules/rimraf/dist/esm/bin.d.mts.map +1 -0
- package/node_modules/rimraf/dist/esm/bin.mjs +250 -0
- package/node_modules/rimraf/dist/esm/bin.mjs.map +1 -0
- package/node_modules/rimraf/dist/esm/default-tmp.d.ts +3 -0
- package/node_modules/rimraf/dist/esm/default-tmp.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/default-tmp.js +55 -0
- package/node_modules/rimraf/dist/esm/default-tmp.js.map +1 -0
- package/node_modules/rimraf/dist/esm/error.d.ts +6 -0
- package/node_modules/rimraf/dist/esm/error.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/error.js +5 -0
- package/node_modules/rimraf/dist/esm/error.js.map +1 -0
- package/node_modules/rimraf/dist/esm/fix-eperm.d.ts +3 -0
- package/node_modules/rimraf/dist/esm/fix-eperm.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/fix-eperm.js +33 -0
- package/node_modules/rimraf/dist/esm/fix-eperm.js.map +1 -0
- package/node_modules/rimraf/dist/esm/fs.d.ts +15 -0
- package/node_modules/rimraf/dist/esm/fs.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/fs.js +18 -0
- package/node_modules/rimraf/dist/esm/fs.js.map +1 -0
- package/node_modules/rimraf/dist/esm/ignore-enoent.d.ts +3 -0
- package/node_modules/rimraf/dist/esm/ignore-enoent.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/ignore-enoent.js +19 -0
- package/node_modules/rimraf/dist/esm/ignore-enoent.js.map +1 -0
- package/node_modules/rimraf/dist/esm/index.d.ts +50 -0
- package/node_modules/rimraf/dist/esm/index.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/index.js +70 -0
- package/node_modules/rimraf/dist/esm/index.js.map +1 -0
- package/node_modules/rimraf/dist/esm/opt-arg.d.ts +34 -0
- package/node_modules/rimraf/dist/esm/opt-arg.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/opt-arg.js +46 -0
- package/node_modules/rimraf/dist/esm/opt-arg.js.map +1 -0
- package/node_modules/rimraf/dist/esm/package.json +3 -0
- package/node_modules/rimraf/dist/esm/path-arg.d.ts +4 -0
- package/node_modules/rimraf/dist/esm/path-arg.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/path-arg.js +46 -0
- package/node_modules/rimraf/dist/esm/path-arg.js.map +1 -0
- package/node_modules/rimraf/dist/esm/readdir-or-error.d.ts +3 -0
- package/node_modules/rimraf/dist/esm/readdir-or-error.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/readdir-or-error.js +14 -0
- package/node_modules/rimraf/dist/esm/readdir-or-error.js.map +1 -0
- package/node_modules/rimraf/dist/esm/retry-busy.d.ts +8 -0
- package/node_modules/rimraf/dist/esm/retry-busy.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/retry-busy.js +60 -0
- package/node_modules/rimraf/dist/esm/retry-busy.js.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-manual.d.ts +3 -0
- package/node_modules/rimraf/dist/esm/rimraf-manual.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-manual.js +5 -0
- package/node_modules/rimraf/dist/esm/rimraf-manual.js.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-move-remove.d.ts +4 -0
- package/node_modules/rimraf/dist/esm/rimraf-move-remove.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-move-remove.js +133 -0
- package/node_modules/rimraf/dist/esm/rimraf-move-remove.js.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-native.d.ts +4 -0
- package/node_modules/rimraf/dist/esm/rimraf-native.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-native.js +19 -0
- package/node_modules/rimraf/dist/esm/rimraf-native.js.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-posix.d.ts +4 -0
- package/node_modules/rimraf/dist/esm/rimraf-posix.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-posix.js +98 -0
- package/node_modules/rimraf/dist/esm/rimraf-posix.js.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-windows.d.ts +4 -0
- package/node_modules/rimraf/dist/esm/rimraf-windows.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/rimraf-windows.js +154 -0
- package/node_modules/rimraf/dist/esm/rimraf-windows.js.map +1 -0
- package/node_modules/rimraf/dist/esm/use-native.d.ts +4 -0
- package/node_modules/rimraf/dist/esm/use-native.d.ts.map +1 -0
- package/node_modules/rimraf/dist/esm/use-native.js +15 -0
- package/node_modules/rimraf/dist/esm/use-native.js.map +1 -0
- package/node_modules/rimraf/package.json +92 -0
- package/package.json +152 -0
- package/scripts/install-scanners.js +258 -0
- package/scripts/watchdog.js +147 -0
- package/src/analyzers/CSSAnalyzer.js +297 -0
- package/src/analyzers/ConfigValidator.js +690 -0
- package/src/analyzers/ESLintAnalyzer.js +320 -0
- package/src/analyzers/JavaScriptAnalyzer.js +261 -0
- package/src/analyzers/PrettierFormatter.js +247 -0
- package/src/analyzers/PythonAnalyzer.js +283 -0
- package/src/analyzers/SecurityAnalyzer.js +729 -0
- package/src/analyzers/SparrowAnalyzer.js +341 -0
- package/src/analyzers/TypeScriptAnalyzer.js +247 -0
- package/src/analyzers/codeCloneDetector/analyzer.js +344 -0
- package/src/analyzers/codeCloneDetector/detector.js +250 -0
- package/src/analyzers/codeCloneDetector/index.js +192 -0
- package/src/analyzers/codeCloneDetector/parser.js +199 -0
- package/src/analyzers/codeCloneDetector/reporter.js +148 -0
- package/src/analyzers/codeCloneDetector/scanner.js +88 -0
- package/src/core/agentPool.js +1957 -0
- package/src/core/agentScheduler.js +3212 -0
- package/src/core/contextManager.js +709 -0
- package/src/core/flowExecutor.js +928 -0
- package/src/core/messageProcessor.js +808 -0
- package/src/core/orchestrator.js +584 -0
- package/src/core/stateManager.js +1500 -0
- package/src/index.js +972 -0
- package/src/interfaces/cli.js +553 -0
- package/src/interfaces/terminal/__tests__/smoke/advancedFeatures.test.js +208 -0
- package/src/interfaces/terminal/__tests__/smoke/agentControl.test.js +236 -0
- package/src/interfaces/terminal/__tests__/smoke/agents.test.js +138 -0
- package/src/interfaces/terminal/__tests__/smoke/components.test.js +137 -0
- package/src/interfaces/terminal/__tests__/smoke/connection.test.js +350 -0
- package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +156 -0
- package/src/interfaces/terminal/__tests__/smoke/imports.test.js +332 -0
- package/src/interfaces/terminal/__tests__/smoke/messages.test.js +256 -0
- package/src/interfaces/terminal/__tests__/smoke/tools.test.js +388 -0
- package/src/interfaces/terminal/api/apiClient.js +299 -0
- package/src/interfaces/terminal/api/messageRouter.js +262 -0
- package/src/interfaces/terminal/api/session.js +266 -0
- package/src/interfaces/terminal/api/websocket.js +497 -0
- package/src/interfaces/terminal/components/AgentCreator.js +705 -0
- package/src/interfaces/terminal/components/AgentEditor.js +678 -0
- package/src/interfaces/terminal/components/AgentSwitcher.js +330 -0
- package/src/interfaces/terminal/components/ErrorBoundary.js +92 -0
- package/src/interfaces/terminal/components/ErrorPanel.js +264 -0
- package/src/interfaces/terminal/components/Header.js +28 -0
- package/src/interfaces/terminal/components/HelpPanel.js +231 -0
- package/src/interfaces/terminal/components/InputBox.js +118 -0
- package/src/interfaces/terminal/components/Layout.js +603 -0
- package/src/interfaces/terminal/components/LoadingSpinner.js +71 -0
- package/src/interfaces/terminal/components/MessageList.js +281 -0
- package/src/interfaces/terminal/components/MultilineTextInput.js +251 -0
- package/src/interfaces/terminal/components/SearchPanel.js +265 -0
- package/src/interfaces/terminal/components/SettingsPanel.js +415 -0
- package/src/interfaces/terminal/components/StatusBar.js +65 -0
- package/src/interfaces/terminal/components/TextInput.js +127 -0
- package/src/interfaces/terminal/config/agentEditorConstants.js +227 -0
- package/src/interfaces/terminal/config/constants.js +393 -0
- package/src/interfaces/terminal/index.js +168 -0
- package/src/interfaces/terminal/state/useAgentControl.js +496 -0
- package/src/interfaces/terminal/state/useAgents.js +537 -0
- package/src/interfaces/terminal/state/useConnection.js +444 -0
- package/src/interfaces/terminal/state/useMessages.js +630 -0
- package/src/interfaces/terminal/state/useTools.js +554 -0
- package/src/interfaces/terminal/utils/debugLogger.js +44 -0
- package/src/interfaces/terminal/utils/settingsStorage.js +232 -0
- package/src/interfaces/terminal/utils/theme.js +85 -0
- package/src/interfaces/webServer.js +5457 -0
- package/src/modules/fileExplorer/controller.js +413 -0
- package/src/modules/fileExplorer/index.js +37 -0
- package/src/modules/fileExplorer/middleware.js +92 -0
- package/src/modules/fileExplorer/routes.js +158 -0
- package/src/modules/fileExplorer/types.js +44 -0
- package/src/services/agentActivityService.js +399 -0
- package/src/services/aiService.js +2618 -0
- package/src/services/apiKeyManager.js +334 -0
- package/src/services/benchmarkService.js +196 -0
- package/src/services/budgetService.js +565 -0
- package/src/services/contextInjectionService.js +268 -0
- package/src/services/conversationCompactionService.js +1103 -0
- package/src/services/credentialVault.js +685 -0
- package/src/services/errorHandler.js +810 -0
- package/src/services/fileAttachmentService.js +547 -0
- package/src/services/flowContextService.js +189 -0
- package/src/services/memoryService.js +521 -0
- package/src/services/modelRouterService.js +365 -0
- package/src/services/modelsService.js +323 -0
- package/src/services/ollamaService.js +452 -0
- package/src/services/portRegistry.js +336 -0
- package/src/services/portTracker.js +223 -0
- package/src/services/projectDetector.js +404 -0
- package/src/services/promptService.js +372 -0
- package/src/services/qualityInspector.js +796 -0
- package/src/services/scheduleService.js +725 -0
- package/src/services/serviceRegistry.js +386 -0
- package/src/services/skillsService.js +486 -0
- package/src/services/telegramService.js +920 -0
- package/src/services/tokenCountingService.js +316 -0
- package/src/services/visualEditorBridge.js +1033 -0
- package/src/services/visualEditorServer.js +1727 -0
- package/src/services/whatsappService.js +663 -0
- package/src/tools/__tests__/webTool.e2e.test.js +569 -0
- package/src/tools/__tests__/webTool.unit.test.js +195 -0
- package/src/tools/agentCommunicationTool.js +1343 -0
- package/src/tools/agentDelayTool.js +498 -0
- package/src/tools/asyncToolManager.js +604 -0
- package/src/tools/baseTool.js +887 -0
- package/src/tools/browserTool.js +897 -0
- package/src/tools/cloneDetectionTool.js +581 -0
- package/src/tools/codeMapTool.js +857 -0
- package/src/tools/dependencyResolverTool.js +1212 -0
- package/src/tools/docxTool.js +623 -0
- package/src/tools/excelTool.js +636 -0
- package/src/tools/fileContentReplaceTool.js +840 -0
- package/src/tools/fileTreeTool.js +833 -0
- package/src/tools/filesystemTool.js +1217 -0
- package/src/tools/helpTool.js +198 -0
- package/src/tools/imageTool.js +1034 -0
- package/src/tools/importAnalyzerTool.js +1056 -0
- package/src/tools/jobDoneTool.js +388 -0
- package/src/tools/memoryTool.js +554 -0
- package/src/tools/pdfTool.js +627 -0
- package/src/tools/seekTool.js +883 -0
- package/src/tools/skillsTool.js +276 -0
- package/src/tools/staticAnalysisTool.js +2146 -0
- package/src/tools/taskManagerTool.js +2836 -0
- package/src/tools/terminalTool.js +2486 -0
- package/src/tools/userPromptTool.js +474 -0
- package/src/tools/videoTool.js +1139 -0
- package/src/tools/visionTool.js +507 -0
- package/src/tools/visualEditorTool.js +1175 -0
- package/src/tools/webTool.js +3114 -0
- package/src/tools/whatsappTool.js +457 -0
- package/src/types/agent.js +519 -0
- package/src/types/contextReference.js +972 -0
- package/src/types/conversation.js +730 -0
- package/src/types/toolCommand.js +747 -0
- package/src/utilities/attachmentValidator.js +288 -0
- package/src/utilities/browserStealth.js +630 -0
- package/src/utilities/configManager.js +618 -0
- package/src/utilities/constants.js +870 -0
- package/src/utilities/directoryAccessManager.js +566 -0
- package/src/utilities/fileProcessor.js +307 -0
- package/src/utilities/humanBehavior.js +453 -0
- package/src/utilities/jsonRepair.js +242 -0
- package/src/utilities/logger.js +436 -0
- package/src/utilities/platformUtils.js +255 -0
- package/src/utilities/platformUtils.test.js +98 -0
- package/src/utilities/stealthConstants.js +377 -0
- package/src/utilities/structuredFileValidator.js +699 -0
- package/src/utilities/tagParser.js +878 -0
- package/src/utilities/toolConstants.js +415 -0
- package/src/utilities/userDataDir.js +300 -0
- package/web-ui/build/brands/autopilot/favicon.svg +1 -0
- package/web-ui/build/brands/autopilot/logo.webp +0 -0
- package/web-ui/build/brands/onbuzz/favicon.svg +1 -0
- package/web-ui/build/brands/onbuzz/logo-text.webp +0 -0
- package/web-ui/build/brands/onbuzz/logo.webp +0 -0
- package/web-ui/build/index.html +15 -0
- package/web-ui/build/logo.png +0 -0
- package/web-ui/build/logo2.png +0 -0
- package/web-ui/build/static/index-SmQFfvBs.js +746 -0
- package/web-ui/build/static/index-V2ySwjHp.css +1 -0
|
@@ -0,0 +1,1500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StateManager - Handles state persistence, recovery, and project state management
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* - Project state persistence and recovery
|
|
6
|
+
* - Agent state management across sessions
|
|
7
|
+
* - Multi-model conversation state handling
|
|
8
|
+
* - Context reference state management
|
|
9
|
+
* - Session recovery and resume functionality
|
|
10
|
+
*
|
|
11
|
+
* IMPORTANT: State is now stored in a platform-appropriate user data directory
|
|
12
|
+
* that persists across npm package updates. See userDataDir.js for details.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { promises as fs } from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { getUserDataPaths, ensureUserDataDirs } from '../utilities/userDataDir.js';
|
|
18
|
+
|
|
19
|
+
class StateManager {
|
|
20
|
+
constructor(config, logger) {
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.logger = logger;
|
|
23
|
+
|
|
24
|
+
// UPDATED: Use persistent user data directory instead of relative path
|
|
25
|
+
// This ensures data survives npm package updates
|
|
26
|
+
const userPaths = getUserDataPaths();
|
|
27
|
+
this.stateDirectory = userPaths.state;
|
|
28
|
+
this.userDataPaths = userPaths;
|
|
29
|
+
|
|
30
|
+
// Legacy: Keep for backwards compatibility detection
|
|
31
|
+
this.legacyStateDirectory = config.system?.stateDirectory || '.loxia-state';
|
|
32
|
+
this.stateVersion = '1.0.0';
|
|
33
|
+
|
|
34
|
+
// State file paths
|
|
35
|
+
this.stateFiles = {
|
|
36
|
+
projectState: 'project-state.json',
|
|
37
|
+
agentIndex: 'agent-index.json',
|
|
38
|
+
teamIndex: 'team-index.json',
|
|
39
|
+
flowIndex: 'flow-index.json',
|
|
40
|
+
flowRunIndex: 'flow-run-index.json',
|
|
41
|
+
conversationIndex: 'conversation-index.json',
|
|
42
|
+
lastSession: 'last-session.json',
|
|
43
|
+
contextReferences: 'context-references.json',
|
|
44
|
+
asyncOperations: 'operations/async-operations.json',
|
|
45
|
+
pausedAgents: 'operations/paused-agents.json',
|
|
46
|
+
toolHistory: 'operations/tool-history.json',
|
|
47
|
+
modelRouterCache: 'models/model-router-cache.json',
|
|
48
|
+
errorRecoveryLog: 'models/error-recovery-log.json'
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the state directory path
|
|
54
|
+
* UPDATED: Now returns the user data directory (absolute path)
|
|
55
|
+
* The projectDir parameter is kept for API compatibility but is ignored
|
|
56
|
+
* @param {string} projectDir - Ignored, kept for compatibility
|
|
57
|
+
* @returns {string} Absolute path to state directory
|
|
58
|
+
*/
|
|
59
|
+
getStateDir(projectDir) {
|
|
60
|
+
// Always use the persistent user data directory
|
|
61
|
+
return this.stateDirectory;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get the agents subdirectory path
|
|
66
|
+
* @returns {string} Absolute path to agents directory
|
|
67
|
+
*/
|
|
68
|
+
getAgentsDir() {
|
|
69
|
+
return this.userDataPaths.agents;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Initialize state directory structure
|
|
74
|
+
* @param {string} projectDir - Project directory path (now ignored, uses user data dir)
|
|
75
|
+
* @returns {Promise<void>}
|
|
76
|
+
*/
|
|
77
|
+
async initializeStateDirectory(projectDir) {
|
|
78
|
+
// UPDATED: Use persistent user data directory instead of project-relative path
|
|
79
|
+
// The projectDir parameter is kept for API compatibility but now ignored
|
|
80
|
+
try {
|
|
81
|
+
// Use the centralized utility to create all necessary directories
|
|
82
|
+
const paths = await ensureUserDataDirs();
|
|
83
|
+
|
|
84
|
+
this.logger.info(`State directory initialized in user data location`, {
|
|
85
|
+
stateDir: paths.state,
|
|
86
|
+
platform: process.platform
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
} catch (error) {
|
|
90
|
+
this.logger.error(`Failed to initialize state directory: ${error.message}`);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Resume project from saved state
|
|
97
|
+
* @param {string} projectDir - Project directory path
|
|
98
|
+
* @returns {Promise<Object>} Resumed project state
|
|
99
|
+
*/
|
|
100
|
+
async resumeProject(projectDir) {
|
|
101
|
+
try {
|
|
102
|
+
await this.initializeStateDirectory(projectDir);
|
|
103
|
+
|
|
104
|
+
// Load project state
|
|
105
|
+
const projectState = await this.loadProjectState(projectDir);
|
|
106
|
+
const agentIndex = await this.loadAgentIndex(projectDir);
|
|
107
|
+
|
|
108
|
+
// Restore agents with multi-model conversations
|
|
109
|
+
const restoredAgents = [];
|
|
110
|
+
for (const [agentId, agentInfo] of Object.entries(agentIndex)) {
|
|
111
|
+
try {
|
|
112
|
+
const agent = await this.restoreAgent(agentId, agentInfo, projectDir);
|
|
113
|
+
restoredAgents.push(agent);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
this.logger.warn(`Failed to restore agent: ${agentId}`, error.message);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Restore async operations
|
|
120
|
+
const asyncOperations = await this.restoreAsyncOperations(projectDir);
|
|
121
|
+
|
|
122
|
+
// Restore paused agents
|
|
123
|
+
const pausedAgents = await this.restorePausedAgents(projectDir);
|
|
124
|
+
|
|
125
|
+
// Restore context references
|
|
126
|
+
const contextReferences = await this.restoreContextReferences(projectDir);
|
|
127
|
+
|
|
128
|
+
const resumedState = {
|
|
129
|
+
projectState,
|
|
130
|
+
agents: restoredAgents,
|
|
131
|
+
asyncOperations,
|
|
132
|
+
pausedAgents,
|
|
133
|
+
contextReferences,
|
|
134
|
+
resumedSuccessfully: true,
|
|
135
|
+
resumedAt: new Date().toISOString()
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Update last session
|
|
139
|
+
await this.saveLastSession(projectDir, {
|
|
140
|
+
resumedAt: new Date().toISOString(),
|
|
141
|
+
agentCount: restoredAgents.length,
|
|
142
|
+
operationCount: asyncOperations.length
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
this.logger.info(`Project resumed successfully`, {
|
|
146
|
+
projectDir,
|
|
147
|
+
agentCount: restoredAgents.length,
|
|
148
|
+
operationCount: asyncOperations.length
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return resumedState;
|
|
152
|
+
|
|
153
|
+
} catch (error) {
|
|
154
|
+
this.logger.error(`Project resume failed: ${error.message}`, {
|
|
155
|
+
projectDir,
|
|
156
|
+
error: error.stack
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
projectState: null,
|
|
161
|
+
agents: [],
|
|
162
|
+
asyncOperations: [],
|
|
163
|
+
pausedAgents: [],
|
|
164
|
+
contextReferences: [],
|
|
165
|
+
resumedSuccessfully: false,
|
|
166
|
+
error: error.message
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Persist agent state to storage
|
|
173
|
+
* @param {Object} agent - Agent object to persist
|
|
174
|
+
* @param {string} projectDir - Project directory path
|
|
175
|
+
* @returns {Promise<void>}
|
|
176
|
+
*/
|
|
177
|
+
async persistAgentState(agent, projectDir = process.cwd()) {
|
|
178
|
+
const stateDir = this.getStateDir(projectDir);
|
|
179
|
+
const agentStateFile = path.join(stateDir, 'agents', `agent-${agent.id}-state.json`);
|
|
180
|
+
const agentConversationsFile = path.join(stateDir, 'agents', `agent-${agent.id}-conversations.json`);
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
// Separate conversations from main agent state
|
|
184
|
+
const { conversations, ...agentState } = agent;
|
|
185
|
+
|
|
186
|
+
// Save agent state
|
|
187
|
+
await this.saveJSON(agentStateFile, {
|
|
188
|
+
version: this.stateVersion,
|
|
189
|
+
agentId: agent.id,
|
|
190
|
+
state: agentState,
|
|
191
|
+
lastPersisted: new Date().toISOString()
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Save conversations separately
|
|
195
|
+
await this.saveJSON(agentConversationsFile, {
|
|
196
|
+
version: this.stateVersion,
|
|
197
|
+
agentId: agent.id,
|
|
198
|
+
conversations,
|
|
199
|
+
lastPersisted: new Date().toISOString()
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Update agent index
|
|
203
|
+
await this.updateAgentIndex(agent, projectDir);
|
|
204
|
+
|
|
205
|
+
this.logger.debug(`Agent state persisted: ${agent.id}`);
|
|
206
|
+
|
|
207
|
+
} catch (error) {
|
|
208
|
+
this.logger.error(`Failed to persist agent state: ${error.message}`, {
|
|
209
|
+
agentId: agent.id,
|
|
210
|
+
error: error.stack
|
|
211
|
+
});
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get project state
|
|
218
|
+
* @param {string} projectDir - Project directory path
|
|
219
|
+
* @returns {Promise<Object>} Project state object
|
|
220
|
+
*/
|
|
221
|
+
async getProjectState(projectDir) {
|
|
222
|
+
return await this.loadProjectState(projectDir);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Load project state from storage
|
|
227
|
+
* @param {string} projectDir - Project directory path
|
|
228
|
+
* @returns {Promise<Object>} Project state object
|
|
229
|
+
*/
|
|
230
|
+
async loadProjectState(projectDir) {
|
|
231
|
+
const stateFile = path.join(this.stateDirectory, this.stateFiles.projectState);
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
const data = await this.loadJSON(stateFile);
|
|
235
|
+
return data;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
// Return default project state if file doesn't exist
|
|
238
|
+
const defaultState = {
|
|
239
|
+
version: this.stateVersion,
|
|
240
|
+
projectDir,
|
|
241
|
+
createdAt: new Date().toISOString(),
|
|
242
|
+
lastModified: new Date().toISOString(),
|
|
243
|
+
activeAgents: [],
|
|
244
|
+
lastActiveSession: null,
|
|
245
|
+
configuration: {
|
|
246
|
+
defaultModel: this.config.system?.defaultModel || 'anthropic-sonnet',
|
|
247
|
+
allowedTools: ['terminal', 'filesystem', 'browser'],
|
|
248
|
+
budgetLimit: 100.00
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
await this.saveProjectState(projectDir, defaultState);
|
|
253
|
+
return defaultState;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Save project state to storage
|
|
259
|
+
* @param {string} projectDir - Project directory path
|
|
260
|
+
* @param {Object} projectState - Project state object
|
|
261
|
+
* @returns {Promise<void>}
|
|
262
|
+
*/
|
|
263
|
+
async saveProjectState(projectDir, projectState) {
|
|
264
|
+
const stateFile = path.join(this.stateDirectory, this.stateFiles.projectState);
|
|
265
|
+
|
|
266
|
+
const stateData = {
|
|
267
|
+
...projectState,
|
|
268
|
+
lastModified: new Date().toISOString()
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
await this.saveJSON(stateFile, stateData);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Load agent index
|
|
276
|
+
* @param {string} projectDir - Project directory path
|
|
277
|
+
* @returns {Promise<Object>} Agent index object
|
|
278
|
+
*/
|
|
279
|
+
async loadAgentIndex(projectDir) {
|
|
280
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.agentIndex);
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
return await this.loadJSON(indexFile);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
return {}; // Return empty index if file doesn't exist
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Update agent index
|
|
291
|
+
* @param {Object} agent - Agent object
|
|
292
|
+
* @param {string} projectDir - Project directory path
|
|
293
|
+
* @returns {Promise<void>}
|
|
294
|
+
*/
|
|
295
|
+
async updateAgentIndex(agent, projectDir) {
|
|
296
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.agentIndex);
|
|
297
|
+
|
|
298
|
+
let agentIndex;
|
|
299
|
+
try {
|
|
300
|
+
agentIndex = await this.loadJSON(indexFile);
|
|
301
|
+
} catch {
|
|
302
|
+
agentIndex = {};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
agentIndex[agent.id] = {
|
|
306
|
+
name: agent.name,
|
|
307
|
+
type: agent.type,
|
|
308
|
+
stateFile: `agents/agent-${agent.id}-state.json`,
|
|
309
|
+
conversationsFile: `agents/agent-${agent.id}-conversations.json`,
|
|
310
|
+
lastActivity: agent.lastActivity,
|
|
311
|
+
model: agent.currentModel,
|
|
312
|
+
status: agent.status,
|
|
313
|
+
capabilities: agent.capabilities || []
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
await this.saveJSON(indexFile, agentIndex);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ==================== TEAM INDEX METHODS ====================
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Load team index
|
|
323
|
+
* @param {string} projectDir - Project directory path (ignored, uses user data dir)
|
|
324
|
+
* @returns {Promise<Object>} Team index object
|
|
325
|
+
*/
|
|
326
|
+
async loadTeamIndex(projectDir) {
|
|
327
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.teamIndex);
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
return await this.loadJSON(indexFile);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
return {}; // Return empty index if file doesn't exist
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Save team index
|
|
338
|
+
* @param {Object} teamIndex - Team index object to save
|
|
339
|
+
* @returns {Promise<void>}
|
|
340
|
+
*/
|
|
341
|
+
async saveTeamIndex(teamIndex) {
|
|
342
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.teamIndex);
|
|
343
|
+
await this.saveJSON(indexFile, teamIndex);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Get all teams
|
|
348
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
349
|
+
* @returns {Promise<Array>} Array of team objects
|
|
350
|
+
*/
|
|
351
|
+
async getAllTeams(projectDir) {
|
|
352
|
+
const teamIndex = await this.loadTeamIndex(projectDir);
|
|
353
|
+
return Object.entries(teamIndex).map(([id, team]) => ({
|
|
354
|
+
id,
|
|
355
|
+
...team
|
|
356
|
+
}));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Get a single team by ID
|
|
361
|
+
* @param {string} teamId - Team identifier
|
|
362
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
363
|
+
* @returns {Promise<Object|null>} Team object or null if not found
|
|
364
|
+
*/
|
|
365
|
+
async getTeam(teamId, projectDir) {
|
|
366
|
+
const teamIndex = await this.loadTeamIndex(projectDir);
|
|
367
|
+
if (teamIndex[teamId]) {
|
|
368
|
+
return { id: teamId, ...teamIndex[teamId] };
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Create a new team
|
|
375
|
+
* @param {Object} teamData - Team data { name, description, color }
|
|
376
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
377
|
+
* @returns {Promise<Object>} Created team object
|
|
378
|
+
*/
|
|
379
|
+
async createTeam(teamData, projectDir) {
|
|
380
|
+
const teamIndex = await this.loadTeamIndex(projectDir);
|
|
381
|
+
|
|
382
|
+
// Generate team ID
|
|
383
|
+
const safeName = (teamData.name || 'team').toLowerCase().replace(/[^a-z0-9]/g, '-').slice(0, 20);
|
|
384
|
+
const teamId = `team-${safeName}-${Date.now()}`;
|
|
385
|
+
|
|
386
|
+
const team = {
|
|
387
|
+
name: teamData.name,
|
|
388
|
+
description: teamData.description || '',
|
|
389
|
+
memberAgentIds: [],
|
|
390
|
+
color: teamData.color || '#3B82F6', // Default blue
|
|
391
|
+
createdAt: new Date().toISOString(),
|
|
392
|
+
updatedAt: new Date().toISOString()
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
teamIndex[teamId] = team;
|
|
396
|
+
await this.saveTeamIndex(teamIndex);
|
|
397
|
+
|
|
398
|
+
this.logger.info(`Team created: ${teamId}`, { name: team.name });
|
|
399
|
+
|
|
400
|
+
return { id: teamId, ...team };
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Update an existing team
|
|
405
|
+
* @param {string} teamId - Team identifier
|
|
406
|
+
* @param {Object} updates - Fields to update
|
|
407
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
408
|
+
* @returns {Promise<Object>} Updated team object
|
|
409
|
+
*/
|
|
410
|
+
async updateTeam(teamId, updates, projectDir) {
|
|
411
|
+
const teamIndex = await this.loadTeamIndex(projectDir);
|
|
412
|
+
|
|
413
|
+
if (!teamIndex[teamId]) {
|
|
414
|
+
throw new Error(`Team ${teamId} not found`);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Only allow updating specific fields
|
|
418
|
+
const allowedFields = ['name', 'description', 'color', 'memberAgentIds'];
|
|
419
|
+
for (const field of allowedFields) {
|
|
420
|
+
if (updates[field] !== undefined) {
|
|
421
|
+
teamIndex[teamId][field] = updates[field];
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
teamIndex[teamId].updatedAt = new Date().toISOString();
|
|
425
|
+
|
|
426
|
+
await this.saveTeamIndex(teamIndex);
|
|
427
|
+
|
|
428
|
+
this.logger.info(`Team updated: ${teamId}`, { updates: Object.keys(updates) });
|
|
429
|
+
|
|
430
|
+
return { id: teamId, ...teamIndex[teamId] };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Delete a team
|
|
435
|
+
* @param {string} teamId - Team identifier
|
|
436
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
437
|
+
* @returns {Promise<boolean>} True if deleted
|
|
438
|
+
*/
|
|
439
|
+
async deleteTeam(teamId, projectDir) {
|
|
440
|
+
const teamIndex = await this.loadTeamIndex(projectDir);
|
|
441
|
+
|
|
442
|
+
if (!teamIndex[teamId]) {
|
|
443
|
+
throw new Error(`Team ${teamId} not found`);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const teamName = teamIndex[teamId].name;
|
|
447
|
+
delete teamIndex[teamId];
|
|
448
|
+
await this.saveTeamIndex(teamIndex);
|
|
449
|
+
|
|
450
|
+
this.logger.info(`Team deleted: ${teamId}`, { name: teamName });
|
|
451
|
+
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Add an agent to a team
|
|
457
|
+
* @param {string} teamId - Team identifier
|
|
458
|
+
* @param {string} agentId - Agent identifier to add
|
|
459
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
460
|
+
* @returns {Promise<Object>} Updated team object
|
|
461
|
+
*/
|
|
462
|
+
async addAgentToTeam(teamId, agentId, projectDir) {
|
|
463
|
+
const teamIndex = await this.loadTeamIndex(projectDir);
|
|
464
|
+
|
|
465
|
+
if (!teamIndex[teamId]) {
|
|
466
|
+
throw new Error(`Team ${teamId} not found`);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Check if agent already in team
|
|
470
|
+
if (!teamIndex[teamId].memberAgentIds.includes(agentId)) {
|
|
471
|
+
teamIndex[teamId].memberAgentIds.push(agentId);
|
|
472
|
+
teamIndex[teamId].updatedAt = new Date().toISOString();
|
|
473
|
+
await this.saveTeamIndex(teamIndex);
|
|
474
|
+
|
|
475
|
+
this.logger.info(`Agent added to team`, { teamId, agentId });
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return { id: teamId, ...teamIndex[teamId] };
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Remove an agent from a team
|
|
483
|
+
* @param {string} teamId - Team identifier
|
|
484
|
+
* @param {string} agentId - Agent identifier to remove
|
|
485
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
486
|
+
* @returns {Promise<Object>} Updated team object
|
|
487
|
+
*/
|
|
488
|
+
async removeAgentFromTeam(teamId, agentId, projectDir) {
|
|
489
|
+
const teamIndex = await this.loadTeamIndex(projectDir);
|
|
490
|
+
|
|
491
|
+
if (!teamIndex[teamId]) {
|
|
492
|
+
throw new Error(`Team ${teamId} not found`);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const index = teamIndex[teamId].memberAgentIds.indexOf(agentId);
|
|
496
|
+
if (index > -1) {
|
|
497
|
+
teamIndex[teamId].memberAgentIds.splice(index, 1);
|
|
498
|
+
teamIndex[teamId].updatedAt = new Date().toISOString();
|
|
499
|
+
await this.saveTeamIndex(teamIndex);
|
|
500
|
+
|
|
501
|
+
this.logger.info(`Agent removed from team`, { teamId, agentId });
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return { id: teamId, ...teamIndex[teamId] };
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Get all teams that contain a specific agent
|
|
509
|
+
* @param {string} agentId - Agent identifier
|
|
510
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
511
|
+
* @returns {Promise<Array>} Array of team objects containing the agent
|
|
512
|
+
*/
|
|
513
|
+
async getAgentTeams(agentId, projectDir) {
|
|
514
|
+
const teams = await this.getAllTeams(projectDir);
|
|
515
|
+
return teams.filter(team => team.memberAgentIds.includes(agentId));
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// ==================== END TEAM INDEX METHODS ====================
|
|
519
|
+
|
|
520
|
+
// ==================== FLOW INDEX METHODS ====================
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Load flow index
|
|
524
|
+
* @param {string} projectDir - Project directory path (ignored, uses user data dir)
|
|
525
|
+
* @returns {Promise<Object>} Flow index object
|
|
526
|
+
*/
|
|
527
|
+
async loadFlowIndex(projectDir) {
|
|
528
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.flowIndex);
|
|
529
|
+
|
|
530
|
+
try {
|
|
531
|
+
return await this.loadJSON(indexFile);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
return {}; // Return empty index if file doesn't exist
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Save flow index
|
|
539
|
+
* @param {Object} flowIndex - Flow index object to save
|
|
540
|
+
* @returns {Promise<void>}
|
|
541
|
+
*/
|
|
542
|
+
async saveFlowIndex(flowIndex) {
|
|
543
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.flowIndex);
|
|
544
|
+
await this.saveJSON(indexFile, flowIndex);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Get all flows
|
|
549
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
550
|
+
* @returns {Promise<Array>} Array of flow objects
|
|
551
|
+
*/
|
|
552
|
+
async getAllFlows(projectDir) {
|
|
553
|
+
const flowIndex = await this.loadFlowIndex(projectDir);
|
|
554
|
+
return Object.entries(flowIndex).map(([id, flow]) => ({
|
|
555
|
+
id,
|
|
556
|
+
...flow
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Get a single flow by ID
|
|
562
|
+
* @param {string} flowId - Flow identifier
|
|
563
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
564
|
+
* @returns {Promise<Object|null>} Flow object or null if not found
|
|
565
|
+
*/
|
|
566
|
+
async getFlow(flowId, projectDir) {
|
|
567
|
+
const flowIndex = await this.loadFlowIndex(projectDir);
|
|
568
|
+
if (flowIndex[flowId]) {
|
|
569
|
+
return { id: flowId, ...flowIndex[flowId] };
|
|
570
|
+
}
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Create a new flow
|
|
576
|
+
* @param {Object} flowData - Flow data { name, description, nodes, edges, variables }
|
|
577
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
578
|
+
* @returns {Promise<Object>} Created flow object
|
|
579
|
+
*/
|
|
580
|
+
async createFlow(flowData, projectDir) {
|
|
581
|
+
const flowIndex = await this.loadFlowIndex(projectDir);
|
|
582
|
+
|
|
583
|
+
// Generate flow ID
|
|
584
|
+
const safeName = (flowData.name || 'flow').toLowerCase().replace(/[^a-z0-9]/g, '-').slice(0, 20);
|
|
585
|
+
const flowId = `flow-${safeName}-${Date.now()}`;
|
|
586
|
+
|
|
587
|
+
const flow = {
|
|
588
|
+
name: flowData.name,
|
|
589
|
+
description: flowData.description || '',
|
|
590
|
+
nodes: flowData.nodes || [],
|
|
591
|
+
edges: flowData.edges || [],
|
|
592
|
+
variables: flowData.variables || {},
|
|
593
|
+
createdAt: new Date().toISOString(),
|
|
594
|
+
updatedAt: new Date().toISOString()
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
flowIndex[flowId] = flow;
|
|
598
|
+
await this.saveFlowIndex(flowIndex);
|
|
599
|
+
|
|
600
|
+
this.logger.info(`Flow created: ${flowId}`, { name: flow.name });
|
|
601
|
+
|
|
602
|
+
return { id: flowId, ...flow };
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Update an existing flow
|
|
607
|
+
* @param {string} flowId - Flow identifier
|
|
608
|
+
* @param {Object} updates - Fields to update
|
|
609
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
610
|
+
* @returns {Promise<Object>} Updated flow object
|
|
611
|
+
*/
|
|
612
|
+
async updateFlow(flowId, updates, projectDir) {
|
|
613
|
+
const flowIndex = await this.loadFlowIndex(projectDir);
|
|
614
|
+
|
|
615
|
+
if (!flowIndex[flowId]) {
|
|
616
|
+
throw new Error(`Flow ${flowId} not found`);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Only allow updating specific fields
|
|
620
|
+
const allowedFields = ['name', 'description', 'nodes', 'edges', 'variables'];
|
|
621
|
+
for (const field of allowedFields) {
|
|
622
|
+
if (updates[field] !== undefined) {
|
|
623
|
+
flowIndex[flowId][field] = updates[field];
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
flowIndex[flowId].updatedAt = new Date().toISOString();
|
|
627
|
+
|
|
628
|
+
await this.saveFlowIndex(flowIndex);
|
|
629
|
+
|
|
630
|
+
this.logger.info(`Flow updated: ${flowId}`, { updates: Object.keys(updates) });
|
|
631
|
+
|
|
632
|
+
return { id: flowId, ...flowIndex[flowId] };
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Delete a flow
|
|
637
|
+
* @param {string} flowId - Flow identifier
|
|
638
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
639
|
+
* @returns {Promise<boolean>} True if deleted
|
|
640
|
+
*/
|
|
641
|
+
async deleteFlow(flowId, projectDir) {
|
|
642
|
+
const flowIndex = await this.loadFlowIndex(projectDir);
|
|
643
|
+
|
|
644
|
+
if (!flowIndex[flowId]) {
|
|
645
|
+
throw new Error(`Flow ${flowId} not found`);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const flowName = flowIndex[flowId].name;
|
|
649
|
+
delete flowIndex[flowId];
|
|
650
|
+
await this.saveFlowIndex(flowIndex);
|
|
651
|
+
|
|
652
|
+
this.logger.info(`Flow deleted: ${flowId}`, { name: flowName });
|
|
653
|
+
|
|
654
|
+
return true;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// ==================== FLOW RUN METHODS ====================
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Load flow run index
|
|
661
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
662
|
+
* @returns {Promise<Object>} Flow run index object
|
|
663
|
+
*/
|
|
664
|
+
async loadFlowRunIndex(projectDir) {
|
|
665
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.flowRunIndex);
|
|
666
|
+
|
|
667
|
+
try {
|
|
668
|
+
return await this.loadJSON(indexFile);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
return {}; // Return empty index if file doesn't exist
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Save flow run index
|
|
676
|
+
* @param {Object} runIndex - Flow run index object to save
|
|
677
|
+
* @returns {Promise<void>}
|
|
678
|
+
*/
|
|
679
|
+
async saveFlowRunIndex(runIndex) {
|
|
680
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.flowRunIndex);
|
|
681
|
+
await this.saveJSON(indexFile, runIndex);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Create a new flow run
|
|
686
|
+
* @param {string} flowId - Flow identifier
|
|
687
|
+
* @param {Object} initialInput - Initial input for the flow
|
|
688
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
689
|
+
* @returns {Promise<Object>} Created flow run object
|
|
690
|
+
*/
|
|
691
|
+
async createFlowRun(flowId, initialInput, projectDir) {
|
|
692
|
+
const runIndex = await this.loadFlowRunIndex(projectDir);
|
|
693
|
+
|
|
694
|
+
const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
695
|
+
|
|
696
|
+
const run = {
|
|
697
|
+
flowId,
|
|
698
|
+
status: 'pending', // pending, running, completed, failed, stopped
|
|
699
|
+
initialInput,
|
|
700
|
+
nodeStates: {},
|
|
701
|
+
startedAt: new Date().toISOString(),
|
|
702
|
+
completedAt: null,
|
|
703
|
+
error: null
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
runIndex[runId] = run;
|
|
707
|
+
await this.saveFlowRunIndex(runIndex);
|
|
708
|
+
|
|
709
|
+
this.logger.info(`Flow run created: ${runId}`, { flowId });
|
|
710
|
+
|
|
711
|
+
return { id: runId, ...run };
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Update a flow run
|
|
716
|
+
* @param {string} runId - Run identifier
|
|
717
|
+
* @param {Object} updates - Fields to update
|
|
718
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
719
|
+
* @returns {Promise<Object>} Updated flow run object
|
|
720
|
+
*/
|
|
721
|
+
async updateFlowRun(runId, updates, projectDir) {
|
|
722
|
+
const runIndex = await this.loadFlowRunIndex(projectDir);
|
|
723
|
+
|
|
724
|
+
if (!runIndex[runId]) {
|
|
725
|
+
throw new Error(`Flow run ${runId} not found`);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Only allow updating specific fields
|
|
729
|
+
const allowedFields = ['status', 'nodeStates', 'completedAt', 'error', 'output'];
|
|
730
|
+
for (const field of allowedFields) {
|
|
731
|
+
if (updates[field] !== undefined) {
|
|
732
|
+
runIndex[runId][field] = updates[field];
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
await this.saveFlowRunIndex(runIndex);
|
|
737
|
+
|
|
738
|
+
this.logger.info(`Flow run updated: ${runId}`, { status: updates.status });
|
|
739
|
+
|
|
740
|
+
return { id: runId, ...runIndex[runId] };
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Get a flow run by ID
|
|
745
|
+
* @param {string} runId - Run identifier
|
|
746
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
747
|
+
* @returns {Promise<Object|null>} Flow run object or null if not found
|
|
748
|
+
*/
|
|
749
|
+
async getFlowRun(runId, projectDir) {
|
|
750
|
+
const runIndex = await this.loadFlowRunIndex(projectDir);
|
|
751
|
+
if (runIndex[runId]) {
|
|
752
|
+
return { id: runId, ...runIndex[runId] };
|
|
753
|
+
}
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Get all runs for a specific flow
|
|
759
|
+
* @param {string} flowId - Flow identifier
|
|
760
|
+
* @param {string} projectDir - Project directory path (ignored)
|
|
761
|
+
* @returns {Promise<Array>} Array of flow run objects
|
|
762
|
+
*/
|
|
763
|
+
async getFlowRuns(flowId, projectDir) {
|
|
764
|
+
const runIndex = await this.loadFlowRunIndex(projectDir);
|
|
765
|
+
return Object.entries(runIndex)
|
|
766
|
+
.filter(([, run]) => run.flowId === flowId)
|
|
767
|
+
.map(([id, run]) => ({ id, ...run }))
|
|
768
|
+
.sort((a, b) => new Date(b.startedAt) - new Date(a.startedAt));
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// ==================== END FLOW INDEX METHODS ====================
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Restore agent from saved state
|
|
775
|
+
* @param {string} agentId - Agent identifier
|
|
776
|
+
* @param {Object} agentInfo - Agent info from index
|
|
777
|
+
* @param {string} projectDir - Project directory path
|
|
778
|
+
* @returns {Promise<Object>} Restored agent object
|
|
779
|
+
*/
|
|
780
|
+
async restoreAgent(agentId, agentInfo, projectDir) {
|
|
781
|
+
const stateDir = this.getStateDir(projectDir);
|
|
782
|
+
const stateFile = path.join(stateDir, agentInfo.stateFile);
|
|
783
|
+
const conversationsFile = path.join(stateDir, agentInfo.conversationsFile);
|
|
784
|
+
|
|
785
|
+
try {
|
|
786
|
+
// Load agent state
|
|
787
|
+
const stateData = await this.loadJSON(stateFile);
|
|
788
|
+
const conversationsData = await this.loadJSON(conversationsFile);
|
|
789
|
+
|
|
790
|
+
// Validate model conversations integrity
|
|
791
|
+
await this.validateModelConversations(conversationsData.conversations);
|
|
792
|
+
|
|
793
|
+
// Check if agent is paused
|
|
794
|
+
const pauseStatus = await this.checkAgentPauseStatus(agentId, projectDir);
|
|
795
|
+
|
|
796
|
+
const restoredAgent = {
|
|
797
|
+
...stateData.state,
|
|
798
|
+
conversations: conversationsData.conversations,
|
|
799
|
+
isPaused: pauseStatus.isPaused,
|
|
800
|
+
pausedUntil: pauseStatus.pausedUntil,
|
|
801
|
+
isRestored: true,
|
|
802
|
+
restoredAt: new Date().toISOString()
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
// CRITICAL: Restore interAgentTracking as a Map (it comes as plain object from JSON)
|
|
806
|
+
if (!restoredAgent.interAgentTracking || typeof restoredAgent.interAgentTracking !== 'object') {
|
|
807
|
+
restoredAgent.interAgentTracking = new Map();
|
|
808
|
+
} else if (!(restoredAgent.interAgentTracking instanceof Map)) {
|
|
809
|
+
restoredAgent.interAgentTracking = new Map(Object.entries(restoredAgent.interAgentTracking));
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
this.logger.info(`Agent restored: ${agentId}`, {
|
|
813
|
+
name: restoredAgent.name,
|
|
814
|
+
status: restoredAgent.status,
|
|
815
|
+
messageCount: restoredAgent.conversations?.full?.messages?.length || 0
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
return restoredAgent;
|
|
819
|
+
|
|
820
|
+
} catch (error) {
|
|
821
|
+
this.logger.error(`Agent restoration failed: ${agentId}`, error.message);
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Restore async operations
|
|
828
|
+
* @param {string} projectDir - Project directory path
|
|
829
|
+
* @returns {Promise<Array>} Array of active async operations
|
|
830
|
+
*/
|
|
831
|
+
async restoreAsyncOperations(projectDir) {
|
|
832
|
+
const operationsFile = path.join(this.stateDirectory, this.stateFiles.asyncOperations);
|
|
833
|
+
|
|
834
|
+
try {
|
|
835
|
+
const data = await this.loadJSON(operationsFile);
|
|
836
|
+
return data.operations || [];
|
|
837
|
+
} catch {
|
|
838
|
+
return [];
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Restore paused agents
|
|
844
|
+
* @param {string} projectDir - Project directory path
|
|
845
|
+
* @returns {Promise<Object>} Paused agents data
|
|
846
|
+
*/
|
|
847
|
+
async restorePausedAgents(projectDir) {
|
|
848
|
+
const pausedFile = path.join(this.stateDirectory, this.stateFiles.pausedAgents);
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
const data = await this.loadJSON(pausedFile);
|
|
852
|
+
const now = Date.now();
|
|
853
|
+
|
|
854
|
+
// Check which agents should be resumed
|
|
855
|
+
const toResume = [];
|
|
856
|
+
for (const [agentId, pauseInfo] of Object.entries(data.pausedAgents || {})) {
|
|
857
|
+
const pausedUntil = new Date(pauseInfo.pausedUntil).getTime();
|
|
858
|
+
|
|
859
|
+
if (now >= pausedUntil) {
|
|
860
|
+
toResume.push(agentId);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Move expired pauses to history
|
|
865
|
+
for (const agentId of toResume) {
|
|
866
|
+
const pauseInfo = data.pausedAgents[agentId];
|
|
867
|
+
delete data.pausedAgents[agentId];
|
|
868
|
+
|
|
869
|
+
data.pauseHistory = data.pauseHistory || [];
|
|
870
|
+
data.pauseHistory.push({
|
|
871
|
+
agentId,
|
|
872
|
+
pausedAt: pauseInfo.pausedAt,
|
|
873
|
+
resumedAt: new Date().toISOString(),
|
|
874
|
+
reason: pauseInfo.reason,
|
|
875
|
+
actualDuration: Math.round((now - new Date(pauseInfo.pausedAt).getTime()) / 1000)
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// Save updated data
|
|
880
|
+
await this.saveJSON(pausedFile, data);
|
|
881
|
+
|
|
882
|
+
return data;
|
|
883
|
+
|
|
884
|
+
} catch {
|
|
885
|
+
return {
|
|
886
|
+
pausedAgents: {},
|
|
887
|
+
pauseHistory: []
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Restore context references
|
|
894
|
+
* @param {string} projectDir - Project directory path
|
|
895
|
+
* @returns {Promise<Object>} Context references data
|
|
896
|
+
*/
|
|
897
|
+
async restoreContextReferences(projectDir) {
|
|
898
|
+
const contextFile = path.join(this.stateDirectory, this.stateFiles.contextReferences);
|
|
899
|
+
|
|
900
|
+
try {
|
|
901
|
+
const data = await this.loadJSON(contextFile);
|
|
902
|
+
|
|
903
|
+
// Validate context references (implementation would validate file existence, etc.)
|
|
904
|
+
const validatedReferences = [];
|
|
905
|
+
for (const reference of data.references || []) {
|
|
906
|
+
// Add validation logic here
|
|
907
|
+
reference.isValid = true; // Placeholder
|
|
908
|
+
reference.lastValidated = new Date().toISOString();
|
|
909
|
+
validatedReferences.push(reference);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
data.references = validatedReferences;
|
|
913
|
+
await this.saveJSON(contextFile, data);
|
|
914
|
+
|
|
915
|
+
return data;
|
|
916
|
+
|
|
917
|
+
} catch {
|
|
918
|
+
return {
|
|
919
|
+
references: [],
|
|
920
|
+
lastCleanup: new Date().toISOString()
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* Save last session data
|
|
927
|
+
* @param {string} projectDir - Project directory path
|
|
928
|
+
* @param {Object} sessionData - Session data to save
|
|
929
|
+
* @returns {Promise<void>}
|
|
930
|
+
*/
|
|
931
|
+
async saveLastSession(projectDir, sessionData) {
|
|
932
|
+
const sessionFile = path.join(this.stateDirectory, this.stateFiles.lastSession);
|
|
933
|
+
|
|
934
|
+
const data = {
|
|
935
|
+
...sessionData,
|
|
936
|
+
savedAt: new Date().toISOString(),
|
|
937
|
+
projectDir
|
|
938
|
+
};
|
|
939
|
+
|
|
940
|
+
await this.saveJSON(sessionFile, data);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Load last session data
|
|
945
|
+
* @param {string} projectDir - Project directory path
|
|
946
|
+
* @returns {Promise<Object>} Last session data
|
|
947
|
+
*/
|
|
948
|
+
async loadLastSession(projectDir) {
|
|
949
|
+
const sessionFile = path.join(this.stateDirectory, this.stateFiles.lastSession);
|
|
950
|
+
|
|
951
|
+
try {
|
|
952
|
+
return await this.loadJSON(sessionFile);
|
|
953
|
+
} catch {
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Save paused agent data
|
|
960
|
+
* @param {string} projectDir - Project directory path
|
|
961
|
+
* @param {string} agentId - Agent identifier
|
|
962
|
+
* @param {Object} pauseData - Pause information
|
|
963
|
+
* @returns {Promise<void>}
|
|
964
|
+
*/
|
|
965
|
+
async savePausedAgent(projectDir, agentId, pauseData) {
|
|
966
|
+
const pausedFile = path.join(this.stateDirectory, this.stateFiles.pausedAgents);
|
|
967
|
+
|
|
968
|
+
let data;
|
|
969
|
+
try {
|
|
970
|
+
data = await this.loadJSON(pausedFile);
|
|
971
|
+
} catch {
|
|
972
|
+
data = { pausedAgents: {}, pauseHistory: [] };
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
data.pausedAgents[agentId] = pauseData;
|
|
976
|
+
await this.saveJSON(pausedFile, data);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Remove paused agent data
|
|
981
|
+
* @param {string} projectDir - Project directory path
|
|
982
|
+
* @param {string} agentId - Agent identifier
|
|
983
|
+
* @returns {Promise<void>}
|
|
984
|
+
*/
|
|
985
|
+
async removePausedAgent(projectDir, agentId) {
|
|
986
|
+
const pausedFile = path.join(this.stateDirectory, this.stateFiles.pausedAgents);
|
|
987
|
+
|
|
988
|
+
try {
|
|
989
|
+
const data = await this.loadJSON(pausedFile);
|
|
990
|
+
delete data.pausedAgents[agentId];
|
|
991
|
+
await this.saveJSON(pausedFile, data);
|
|
992
|
+
} catch {
|
|
993
|
+
// File doesn't exist, nothing to remove
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Check agent pause status
|
|
999
|
+
* @param {string} agentId - Agent identifier
|
|
1000
|
+
* @param {string} projectDir - Project directory path
|
|
1001
|
+
* @returns {Promise<Object>} Pause status
|
|
1002
|
+
*/
|
|
1003
|
+
async checkAgentPauseStatus(agentId, projectDir) {
|
|
1004
|
+
const pausedFile = path.join(this.stateDirectory, this.stateFiles.pausedAgents);
|
|
1005
|
+
|
|
1006
|
+
try {
|
|
1007
|
+
const data = await this.loadJSON(pausedFile);
|
|
1008
|
+
const pauseInfo = data.pausedAgents[agentId];
|
|
1009
|
+
|
|
1010
|
+
if (!pauseInfo) {
|
|
1011
|
+
return { isPaused: false, pausedUntil: null };
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
const now = Date.now();
|
|
1015
|
+
const pausedUntil = new Date(pauseInfo.pausedUntil).getTime();
|
|
1016
|
+
|
|
1017
|
+
return {
|
|
1018
|
+
isPaused: now < pausedUntil,
|
|
1019
|
+
pausedUntil: pauseInfo.pausedUntil,
|
|
1020
|
+
reason: pauseInfo.reason
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
} catch {
|
|
1024
|
+
return { isPaused: false, pausedUntil: null };
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Validate model conversations integrity
|
|
1030
|
+
* @param {Object} conversations - Conversations object
|
|
1031
|
+
* @returns {Promise<void>}
|
|
1032
|
+
*/
|
|
1033
|
+
async validateModelConversations(conversations) {
|
|
1034
|
+
if (!conversations || !conversations.full) {
|
|
1035
|
+
throw new Error('Invalid conversations structure - missing full conversation');
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const fullMessages = conversations.full.messages || [];
|
|
1039
|
+
const fullLastUpdated = new Date(conversations.full.lastUpdated);
|
|
1040
|
+
|
|
1041
|
+
// Validate each model conversation against full conversation
|
|
1042
|
+
for (const [modelName, modelConv] of Object.entries(conversations)) {
|
|
1043
|
+
if (modelName === 'full') continue;
|
|
1044
|
+
|
|
1045
|
+
if (!modelConv.messages) {
|
|
1046
|
+
this.logger.warn(`Model conversation ${modelName} missing messages array`);
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
const modelLastUpdated = new Date(modelConv.lastUpdated);
|
|
1051
|
+
|
|
1052
|
+
if (fullLastUpdated > modelLastUpdated) {
|
|
1053
|
+
this.logger.warn(`Model conversation ${modelName} is outdated, will sync on next use`);
|
|
1054
|
+
modelConv.needsSync = true;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* Save JSON data to file
|
|
1061
|
+
* @private
|
|
1062
|
+
*/
|
|
1063
|
+
async saveJSON(filePath, data) {
|
|
1064
|
+
const dir = path.dirname(filePath);
|
|
1065
|
+
await fs.mkdir(dir, { recursive: true });
|
|
1066
|
+
|
|
1067
|
+
const jsonData = JSON.stringify(data, null, 2);
|
|
1068
|
+
await fs.writeFile(filePath, jsonData, 'utf8');
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* Load JSON data from file
|
|
1073
|
+
* @private
|
|
1074
|
+
*/
|
|
1075
|
+
async loadJSON(filePath) {
|
|
1076
|
+
const data = await fs.readFile(filePath, 'utf8');
|
|
1077
|
+
return JSON.parse(data);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Delete agent state from storage
|
|
1082
|
+
* @param {string} agentId - Agent identifier
|
|
1083
|
+
* @param {string} projectDir - Project directory path
|
|
1084
|
+
* @returns {Promise<void>}
|
|
1085
|
+
*/
|
|
1086
|
+
async deleteAgentState(agentId, projectDir = process.cwd()) {
|
|
1087
|
+
const stateDir = this.getStateDir(projectDir);
|
|
1088
|
+
const agentStateFile = path.join(stateDir, 'agents', `agent-${agentId}-state.json`);
|
|
1089
|
+
const agentConversationsFile = path.join(stateDir, 'agents', `agent-${agentId}-conversations.json`);
|
|
1090
|
+
|
|
1091
|
+
try {
|
|
1092
|
+
// Delete agent state file
|
|
1093
|
+
try {
|
|
1094
|
+
await fs.unlink(agentStateFile);
|
|
1095
|
+
this.logger.debug(`Deleted agent state file: ${agentId}`);
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
if (error.code !== 'ENOENT') {
|
|
1098
|
+
this.logger.warn(`Failed to delete agent state file: ${error.message}`, { agentId });
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Delete agent conversations file
|
|
1103
|
+
try {
|
|
1104
|
+
await fs.unlink(agentConversationsFile);
|
|
1105
|
+
this.logger.debug(`Deleted agent conversations file: ${agentId}`);
|
|
1106
|
+
} catch (error) {
|
|
1107
|
+
if (error.code !== 'ENOENT') {
|
|
1108
|
+
this.logger.warn(`Failed to delete agent conversations file: ${error.message}`, { agentId });
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Remove from agent index
|
|
1113
|
+
await this.removeFromAgentIndex(agentId, projectDir);
|
|
1114
|
+
|
|
1115
|
+
this.logger.info(`Agent state deleted: ${agentId}`);
|
|
1116
|
+
|
|
1117
|
+
} catch (error) {
|
|
1118
|
+
this.logger.error(`Failed to delete agent state: ${error.message}`, {
|
|
1119
|
+
agentId,
|
|
1120
|
+
error: error.stack
|
|
1121
|
+
});
|
|
1122
|
+
throw error;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* Remove agent from agent index
|
|
1128
|
+
* @param {string} agentId - Agent identifier
|
|
1129
|
+
* @param {string} projectDir - Project directory path
|
|
1130
|
+
* @returns {Promise<void>}
|
|
1131
|
+
*/
|
|
1132
|
+
async removeFromAgentIndex(agentId, projectDir) {
|
|
1133
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.agentIndex);
|
|
1134
|
+
|
|
1135
|
+
try {
|
|
1136
|
+
const agentIndex = await this.loadJSON(indexFile);
|
|
1137
|
+
delete agentIndex[agentId];
|
|
1138
|
+
await this.saveJSON(indexFile, agentIndex);
|
|
1139
|
+
this.logger.debug(`Removed agent from index: ${agentId}`);
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
// If index doesn't exist or can't be updated, log but don't throw
|
|
1142
|
+
this.logger.warn(`Failed to remove agent from index: ${error.message}`, { agentId });
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/**
|
|
1147
|
+
* Check if state directory exists
|
|
1148
|
+
* @param {string} projectDir - Project directory path
|
|
1149
|
+
* @returns {Promise<boolean>} True if state directory exists
|
|
1150
|
+
*/
|
|
1151
|
+
async stateDirectoryExists(projectDir) {
|
|
1152
|
+
const stateDir = this.getStateDir(projectDir);
|
|
1153
|
+
|
|
1154
|
+
try {
|
|
1155
|
+
const stats = await fs.stat(stateDir);
|
|
1156
|
+
return stats.isDirectory();
|
|
1157
|
+
} catch {
|
|
1158
|
+
return false;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/**
|
|
1163
|
+
* Clean up old state files
|
|
1164
|
+
* @param {string} projectDir - Project directory path
|
|
1165
|
+
* @param {number} maxAge - Maximum age in days
|
|
1166
|
+
* @returns {Promise<void>}
|
|
1167
|
+
*/
|
|
1168
|
+
async cleanupOldState(projectDir, maxAge = 30) {
|
|
1169
|
+
const stateDir = this.getStateDir(projectDir);
|
|
1170
|
+
const cutoffDate = Date.now() - (maxAge * 24 * 60 * 60 * 1000);
|
|
1171
|
+
|
|
1172
|
+
try {
|
|
1173
|
+
const agentsDir = path.join(stateDir, 'agents');
|
|
1174
|
+
const files = await fs.readdir(agentsDir);
|
|
1175
|
+
|
|
1176
|
+
for (const file of files) {
|
|
1177
|
+
const filePath = path.join(agentsDir, file);
|
|
1178
|
+
const stats = await fs.stat(filePath);
|
|
1179
|
+
|
|
1180
|
+
if (stats.mtime.getTime() < cutoffDate) {
|
|
1181
|
+
await fs.unlink(filePath);
|
|
1182
|
+
this.logger.info(`Cleaned up old state file: ${file}`);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
} catch (error) {
|
|
1187
|
+
this.logger.warn(`State cleanup failed: ${error.message}`);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* Get all available agents (active + archived) from filesystem
|
|
1193
|
+
* Scans both the index AND the agents directory to find all agents,
|
|
1194
|
+
* including those that may be missing from the index.
|
|
1195
|
+
* @param {string} projectDir - Project directory path
|
|
1196
|
+
* @param {Object} agentPool - Agent pool instance to check active agents
|
|
1197
|
+
* @returns {Promise<Array>} List of all agents with metadata
|
|
1198
|
+
*/
|
|
1199
|
+
async getAllAvailableAgents(projectDir, agentPool) {
|
|
1200
|
+
try {
|
|
1201
|
+
const agentIndex = await this.loadAgentIndex(projectDir);
|
|
1202
|
+
const activeAgentIds = agentPool ? (await agentPool.getAllAgents()).map(a => a.id) : [];
|
|
1203
|
+
const agentsDir = this.getAgentsDir();
|
|
1204
|
+
|
|
1205
|
+
// Track which agent IDs we've already processed
|
|
1206
|
+
const processedAgentIds = new Set();
|
|
1207
|
+
const agents = [];
|
|
1208
|
+
|
|
1209
|
+
// First, process agents from the index
|
|
1210
|
+
for (const [agentId, info] of Object.entries(agentIndex)) {
|
|
1211
|
+
// Skip invalid or undefined entries
|
|
1212
|
+
if (!info || !info.name) {
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
processedAgentIds.add(agentId);
|
|
1217
|
+
agents.push({
|
|
1218
|
+
agentId,
|
|
1219
|
+
name: info.name,
|
|
1220
|
+
type: info.type,
|
|
1221
|
+
model: info.model,
|
|
1222
|
+
lastActivity: info.lastActivity,
|
|
1223
|
+
status: info.status,
|
|
1224
|
+
stateFile: info.stateFile,
|
|
1225
|
+
conversationsFile: info.conversationsFile,
|
|
1226
|
+
capabilities: info.capabilities || [],
|
|
1227
|
+
isLoaded: activeAgentIds.includes(agentId),
|
|
1228
|
+
canImport: !activeAgentIds.includes(agentId)
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// Second, scan the agents directory for any agents not in the index
|
|
1233
|
+
try {
|
|
1234
|
+
const files = await fs.readdir(agentsDir);
|
|
1235
|
+
const stateFiles = files.filter(f => f.endsWith('-state.json'));
|
|
1236
|
+
|
|
1237
|
+
let indexUpdated = false;
|
|
1238
|
+
|
|
1239
|
+
for (const stateFile of stateFiles) {
|
|
1240
|
+
// Extract agent ID from filename: agent-{agentId}-state.json
|
|
1241
|
+
const match = stateFile.match(/^agent-(.+)-state\.json$/);
|
|
1242
|
+
if (!match) continue;
|
|
1243
|
+
|
|
1244
|
+
const agentId = match[1];
|
|
1245
|
+
if (processedAgentIds.has(agentId)) continue;
|
|
1246
|
+
|
|
1247
|
+
// Found an agent not in the index - load its state
|
|
1248
|
+
try {
|
|
1249
|
+
const statePath = path.join(agentsDir, stateFile);
|
|
1250
|
+
const stateData = await this.loadJSON(statePath);
|
|
1251
|
+
const state = stateData.state || stateData;
|
|
1252
|
+
|
|
1253
|
+
// Build agent info from state file
|
|
1254
|
+
const agentInfo = {
|
|
1255
|
+
agentId,
|
|
1256
|
+
name: state.name || stateData.name || `Recovered Agent ${agentId.slice(-8)}`,
|
|
1257
|
+
type: state.type || 'user-created',
|
|
1258
|
+
model: state.currentModel || state.preferredModel || 'unknown',
|
|
1259
|
+
lastActivity: state.lastActivity || stateData.timestamp || null,
|
|
1260
|
+
status: state.status || 'idle',
|
|
1261
|
+
stateFile: `agents/${stateFile}`,
|
|
1262
|
+
conversationsFile: `agents/agent-${agentId}-conversations.json`,
|
|
1263
|
+
capabilities: state.capabilities || [],
|
|
1264
|
+
isLoaded: activeAgentIds.includes(agentId),
|
|
1265
|
+
canImport: !activeAgentIds.includes(agentId)
|
|
1266
|
+
};
|
|
1267
|
+
|
|
1268
|
+
agents.push(agentInfo);
|
|
1269
|
+
processedAgentIds.add(agentId);
|
|
1270
|
+
|
|
1271
|
+
// Update the index with this recovered agent
|
|
1272
|
+
agentIndex[agentId] = {
|
|
1273
|
+
name: agentInfo.name,
|
|
1274
|
+
type: agentInfo.type,
|
|
1275
|
+
stateFile: agentInfo.stateFile,
|
|
1276
|
+
conversationsFile: agentInfo.conversationsFile,
|
|
1277
|
+
lastActivity: agentInfo.lastActivity,
|
|
1278
|
+
model: agentInfo.model,
|
|
1279
|
+
status: agentInfo.status,
|
|
1280
|
+
capabilities: agentInfo.capabilities
|
|
1281
|
+
};
|
|
1282
|
+
indexUpdated = true;
|
|
1283
|
+
|
|
1284
|
+
this.logger.info(`Recovered agent from disk: ${agentInfo.name} (${agentId})`);
|
|
1285
|
+
} catch (err) {
|
|
1286
|
+
this.logger.warn(`Failed to recover agent from ${stateFile}: ${err.message}`);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// Save the updated index if we found missing agents
|
|
1291
|
+
if (indexUpdated) {
|
|
1292
|
+
const indexFile = path.join(this.stateDirectory, this.stateFiles.agentIndex);
|
|
1293
|
+
await this.saveJSON(indexFile, agentIndex);
|
|
1294
|
+
this.logger.info(`Updated agent index with ${agents.length - Object.keys(agentIndex).length + Object.keys(processedAgentIds).size} recovered agents`);
|
|
1295
|
+
}
|
|
1296
|
+
} catch (err) {
|
|
1297
|
+
this.logger.warn(`Failed to scan agents directory: ${err.message}`);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// Sort by last activity (most recent first)
|
|
1301
|
+
agents.sort((a, b) => {
|
|
1302
|
+
const dateA = a.lastActivity ? new Date(a.lastActivity) : new Date(0);
|
|
1303
|
+
const dateB = b.lastActivity ? new Date(b.lastActivity) : new Date(0);
|
|
1304
|
+
return dateB - dateA;
|
|
1305
|
+
});
|
|
1306
|
+
|
|
1307
|
+
// Enrich agents with firstUserMessage snippet
|
|
1308
|
+
await this._enrichAgentsWithSnippets(agents, agentPool);
|
|
1309
|
+
|
|
1310
|
+
this.logger.info(`Found ${agents.length} available agents (${agents.filter(a => a.isLoaded).length} active, ${agents.filter(a => !a.isLoaded).length} archived)`);
|
|
1311
|
+
|
|
1312
|
+
return agents;
|
|
1313
|
+
} catch (error) {
|
|
1314
|
+
this.logger.error(`Failed to get available agents: ${error.message}`);
|
|
1315
|
+
throw error;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/**
|
|
1320
|
+
* Enrich agent list with firstUserMessage snippets.
|
|
1321
|
+
* For loaded agents, reads from agentPool. For archived agents, peeks into conversations file.
|
|
1322
|
+
*/
|
|
1323
|
+
async _enrichAgentsWithSnippets(agents, agentPool) {
|
|
1324
|
+
const extractSnippet = (messages) => {
|
|
1325
|
+
if (!messages || messages.length === 0) return null;
|
|
1326
|
+
const firstUser = messages.find(m =>
|
|
1327
|
+
m.role === 'user' && m.content && m.type !== 'task-boundary'
|
|
1328
|
+
);
|
|
1329
|
+
if (!firstUser) return null;
|
|
1330
|
+
const text = typeof firstUser.content === 'string'
|
|
1331
|
+
? firstUser.content
|
|
1332
|
+
: Array.isArray(firstUser.content)
|
|
1333
|
+
? firstUser.content.filter(b => b.type === 'text').map(b => b.text).join('\n')
|
|
1334
|
+
: null;
|
|
1335
|
+
if (!text) return null;
|
|
1336
|
+
const lines = text.split('\n').filter(l => l.trim());
|
|
1337
|
+
const snippet = lines.slice(0, 2).join('\n');
|
|
1338
|
+
return snippet.length > 120 ? snippet.slice(0, 117) + '...' : snippet;
|
|
1339
|
+
};
|
|
1340
|
+
|
|
1341
|
+
for (const agent of agents) {
|
|
1342
|
+
try {
|
|
1343
|
+
if (agent.isLoaded && agentPool) {
|
|
1344
|
+
// For loaded agents, get from the in-memory agent
|
|
1345
|
+
const liveAgent = await agentPool.getAgent(agent.agentId);
|
|
1346
|
+
if (liveAgent) {
|
|
1347
|
+
agent.firstUserMessage = extractSnippet(liveAgent.conversations?.full?.messages);
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
// For archived agents, peek into the conversations file on disk
|
|
1352
|
+
if (agent.conversationsFile) {
|
|
1353
|
+
const convPath = path.join(this.stateDirectory, agent.conversationsFile);
|
|
1354
|
+
try {
|
|
1355
|
+
const convData = await this.loadJSON(convPath);
|
|
1356
|
+
const messages = convData?.conversations?.full?.messages || convData?.full?.messages;
|
|
1357
|
+
agent.firstUserMessage = extractSnippet(messages);
|
|
1358
|
+
} catch {
|
|
1359
|
+
// File may not exist — that's fine
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
} catch {
|
|
1363
|
+
// Non-critical — just skip this agent's snippet
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* Get agent metadata without full restoration (lightweight preview)
|
|
1370
|
+
* @param {string} agentId - Agent ID
|
|
1371
|
+
* @param {string} projectDir - Project directory path
|
|
1372
|
+
* @returns {Promise<Object>} Agent metadata for preview
|
|
1373
|
+
*/
|
|
1374
|
+
async getAgentMetadata(agentId, projectDir) {
|
|
1375
|
+
try {
|
|
1376
|
+
// Load agent index
|
|
1377
|
+
const agentIndex = await this.loadAgentIndex(projectDir);
|
|
1378
|
+
const agentInfo = agentIndex[agentId];
|
|
1379
|
+
|
|
1380
|
+
if (!agentInfo) {
|
|
1381
|
+
throw new Error(`Agent ${agentId} not found in index`);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// Load just the state file (lightweight)
|
|
1385
|
+
const stateDir = this.getStateDir(projectDir);
|
|
1386
|
+
const stateFile = path.join(stateDir, agentInfo.stateFile);
|
|
1387
|
+
const conversationsFile = path.join(stateDir, agentInfo.conversationsFile);
|
|
1388
|
+
|
|
1389
|
+
// Check if files exist
|
|
1390
|
+
const stateExists = await fs.access(stateFile).then(() => true).catch(() => false);
|
|
1391
|
+
const conversationsExist = await fs.access(conversationsFile).then(() => true).catch(() => false);
|
|
1392
|
+
|
|
1393
|
+
if (!stateExists) {
|
|
1394
|
+
throw new Error(`State file not found for agent ${agentId}`);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// Load state
|
|
1398
|
+
const stateData = await this.loadJSON(stateFile);
|
|
1399
|
+
const state = stateData.state || {};
|
|
1400
|
+
|
|
1401
|
+
// Load conversation count without loading full messages (for performance)
|
|
1402
|
+
let messageCount = 0;
|
|
1403
|
+
let lastMessage = null;
|
|
1404
|
+
if (conversationsExist) {
|
|
1405
|
+
try {
|
|
1406
|
+
const conversations = await this.loadJSON(conversationsFile);
|
|
1407
|
+
messageCount = Array.isArray(conversations) ? conversations.length : 0;
|
|
1408
|
+
|
|
1409
|
+
// Get last message for preview
|
|
1410
|
+
if (messageCount > 0) {
|
|
1411
|
+
const lastMsg = conversations[conversations.length - 1];
|
|
1412
|
+
lastMessage = lastMsg?.content?.substring(0, 100) || null;
|
|
1413
|
+
}
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
this.logger.warn(`Failed to load conversations for ${agentId}: ${error.message}`);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
const metadata = {
|
|
1420
|
+
agentId,
|
|
1421
|
+
name: agentInfo.name || state.name,
|
|
1422
|
+
model: agentInfo.model || state.preferredModel || state.currentModel,
|
|
1423
|
+
lastActivity: agentInfo.lastActivity,
|
|
1424
|
+
status: agentInfo.status,
|
|
1425
|
+
capabilities: state.capabilities || [],
|
|
1426
|
+
messageCount,
|
|
1427
|
+
lastMessage,
|
|
1428
|
+
taskCount: state.taskList?.tasks?.length || 0,
|
|
1429
|
+
createdAt: state.createdAt,
|
|
1430
|
+
workingDirectory: state.directoryAccess?.workingDirectory,
|
|
1431
|
+
mode: state.mode,
|
|
1432
|
+
systemPrompt: state.originalSystemPrompt
|
|
1433
|
+
};
|
|
1434
|
+
|
|
1435
|
+
this.logger.info(`Loaded metadata for agent ${agentId}: ${metadata.messageCount} messages, ${metadata.taskCount} tasks`);
|
|
1436
|
+
|
|
1437
|
+
return metadata;
|
|
1438
|
+
} catch (error) {
|
|
1439
|
+
this.logger.error(`Failed to get agent metadata for ${agentId}: ${error.message}`);
|
|
1440
|
+
throw error;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* Import archived agent from filesystem and add to agent pool
|
|
1446
|
+
* @param {string} agentId - Agent ID to import
|
|
1447
|
+
* @param {string} projectDir - Project directory path
|
|
1448
|
+
* @param {Object} agentPool - Agent pool instance
|
|
1449
|
+
* @returns {Promise<Object>} Imported agent object
|
|
1450
|
+
*/
|
|
1451
|
+
async importArchivedAgent(agentId, projectDir, agentPool) {
|
|
1452
|
+
try {
|
|
1453
|
+
// Validate agent ID format for security
|
|
1454
|
+
const AGENT_ID_REGEX = /^agent-[a-z0-9-]+-\d+$/;
|
|
1455
|
+
if (!AGENT_ID_REGEX.test(agentId)) {
|
|
1456
|
+
throw new Error('Invalid agent ID format');
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
// Check if already loaded in agent pool
|
|
1460
|
+
if (agentPool && await agentPool.getAgent(agentId)) {
|
|
1461
|
+
throw new Error(`Agent ${agentId} is already loaded. Use switchAgent() instead.`);
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
// Load from agent index
|
|
1465
|
+
const agentIndex = await this.loadAgentIndex(projectDir);
|
|
1466
|
+
const agentInfo = agentIndex[agentId];
|
|
1467
|
+
|
|
1468
|
+
if (!agentInfo) {
|
|
1469
|
+
throw new Error(`Agent ${agentId} not found in index`);
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
this.logger.info(`Importing archived agent: ${agentId} (${agentInfo.name})`);
|
|
1473
|
+
|
|
1474
|
+
// Restore agent using existing restore logic
|
|
1475
|
+
const agent = await this.restoreAgent(agentId, agentInfo, projectDir);
|
|
1476
|
+
|
|
1477
|
+
// Update agent's last activity
|
|
1478
|
+
agent.lastActivity = new Date().toISOString();
|
|
1479
|
+
|
|
1480
|
+
// Add to agent pool if provided
|
|
1481
|
+
if (agentPool) {
|
|
1482
|
+
agentPool.agents.set(agent.id, agent);
|
|
1483
|
+
agentPool._updateAgentDirectory(agent);
|
|
1484
|
+
this.logger.info(`Agent ${agentId} added to agent pool`);
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
// Update agent index with new last activity
|
|
1488
|
+
await this.updateAgentIndex(agent, projectDir);
|
|
1489
|
+
|
|
1490
|
+
this.logger.info(`Successfully imported agent ${agentId}: ${agent.name}`);
|
|
1491
|
+
|
|
1492
|
+
return agent;
|
|
1493
|
+
} catch (error) {
|
|
1494
|
+
this.logger.error(`Failed to import agent ${agentId}: ${error.message}`);
|
|
1495
|
+
throw error;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
export default StateManager;
|