@nocobase/plugin-ai 2.1.0-beta.32 → 2.1.0-beta.33

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.
@@ -11,6 +11,10 @@ keywords: "cluster mode,multi-instance,load balancing,shared storage,Redis,Kuber
11
11
 
12
12
  Starting from v1.6.0, NocoBase supports running applications in cluster mode. When an application runs in cluster mode, it can improve its performance in handling concurrent access by using multiple instances and a multi-core mode.
13
13
 
14
+ Based on cluster mode, you can achieve application-level high availability: traffic is distributed by a load balancer across multiple NocoBase instances within the same cluster, so if a single instance fails, restarts, or is being released, other instances can continue serving traffic. In practice, a single cluster should usually be deployed within the same low-latency network environment.
15
+
16
+ It is important to note that NocoBase cluster mode addresses horizontal scaling and high availability of application instances at the application layer. If you need warm standby or disaster recovery across availability zones or regions, you would typically deploy multiple independent clusters, and the operations team would be responsible for the data replication and switchover strategy for the database, shared storage, and other infrastructure.
17
+
14
18
  ## System Architecture
15
19
 
16
20
 
@@ -33,4 +37,4 @@ This document only introduces the basic concepts and components of NocoBase's cl
33
37
  - [Operations](./operations)
34
38
  - Advanced
35
39
  - [Service Splitting](./services-splitting)
36
- - [Development Reference](./development)
40
+ - [Development Reference](./development)
@@ -25,12 +25,14 @@ First, please ensure you have obtained licenses for the above plugins (you can p
25
25
 
26
26
  ## System Components
27
27
 
28
- Other system components, besides the application instance itself, can be selected by operations personnel based on the team's operational needs.
28
+ In addition to the application instances themselves, cluster deployment also requires system components such as the database, middleware, shared storage, and load balancing. Different teams can choose the specific implementation of these components based on their own operating model.
29
29
 
30
30
  ### Database
31
31
 
32
32
  Since the current cluster mode only targets application instances, the database temporarily supports only a single node. If you have a database architecture like master-slave, you need to implement it yourself through middleware and ensure it is transparent to the NocoBase application.
33
33
 
34
+ If you need warm standby or disaster recovery across availability zones or regions, the database synchronization and switchover strategy must be designed and implemented by your operations team.
35
+
34
36
  ### Middleware
35
37
 
36
38
  NocoBase's cluster mode relies on some middleware to achieve inter-cluster communication and coordination, including:
@@ -49,10 +51,37 @@ When all middleware components use Redis, you can start a single Redis service w
49
51
 
50
52
  ### Shared Storage
51
53
 
52
- NocoBase needs to use the storage directory to store system-related files. In multi-node mode, you should mount a cloud disk (or NFS) to support shared access across multiple nodes. Otherwise, local storage will not be automatically synchronized, and it will not function properly.
54
+ NocoBase needs to use the `storage` directory to store system-related files, and shared storage is also a required component of cluster deployment. In multi-node mode, you can choose different implementations based on your infrastructure environment, such as cloud disks, NFS, or EFS, to support shared access across multiple nodes. Otherwise, system files will not be synchronized automatically and the application will not work properly.
53
55
 
54
56
  When deploying with Kubernetes, please refer to the [Kubernetes Deployment: Shared Storage](./kubernetes#shared-storage) section.
55
57
 
58
+ #### What is typically stored in the `storage` directory
59
+
60
+ The contents of the `storage` directory vary depending on the enabled plugins and the deployment method. Based on the current implementation, common contents include:
61
+
62
+ | Path | Purpose | Usage recommendation |
63
+ | --- | --- | --- |
64
+ | `storage/uploads` | Uploaded files when using local storage mode | In production clusters, prefer object storage such as S3 / OSS / COS |
65
+ | `storage/plugins` | Local plugin packages installed, uploaded, or discovered at runtime | If you rely on local plugins, this directory must be shared; if plugins are built into the image, this dependency can be reduced |
66
+ | `storage/apps/<app>/jwt_secret.dat` | Default token secret generated automatically when `APP_KEY` is not explicitly configured | Do not rely on this file in production; explicitly configure `APP_KEY` instead |
67
+ | `storage/apps/<app>/aes_key.dat` | Default AES key generated automatically when `APP_AES_SECRET_KEY` is not explicitly configured | Do not rely on this file in production; explicitly configure `APP_AES_SECRET_KEY` instead |
68
+ | `storage/environment-variables/<app>/aes_key.dat` | AES key file used in environment-variable plugin scenarios | A read-only mounted key file is recommended |
69
+ | `storage/logs` | Default log directory and some migration logs | It is recommended to integrate with an external logging platform in the future |
70
+ | `storage/tmp` | Temporary files for import, export, migration, etc. | It can be temporary, but if it needs to be reused across nodes, it must be shared, or the operation should be fixed to a single management node |
71
+ | `storage/backups`, `storage/duplicator`, `storage/migration-manager` | Artifacts related to backup, restore, and migration | These should be treated as operations directories, stored persistently, and not modified concurrently across multiple nodes |
72
+
73
+ The table above is not exhaustive, but it illustrates an important point: `storage` mixes business files, secret files, plugin directories, logs, and operations-related temporary artifacts. Therefore, in cluster deployment, the baseline is usually to persist and share the entire `/app/nocobase/storage`.
74
+
75
+ #### Storage recommendations
76
+
77
+ Cluster consistency in NocoBase mainly relies on the database, Redis, message queues, and distributed locks, rather than treating shared file systems as a high-concurrency coordination medium.
78
+
79
+ Therefore, the following is recommended:
80
+
81
+ - For high-frequency business files such as attachments, prefer object storage. In production clusters, long-term reliance on local storage is not recommended.
82
+ - Shared storage should mainly be used to host the `storage` directory, rather than as a high-throughput file storage service.
83
+ - Operations such as plugin installation, plugin upgrade, backup, restore, and migration should be performed only after scaling the cluster down to a single node, and the cluster can be scaled out again after completion.
84
+
56
85
  ### Load Balancing
57
86
 
58
87
  Cluster mode requires a load balancer to distribute requests, as well as for health checks and failover of application instances. This part should be selected and configured according to the team's operational needs.
@@ -61,7 +90,6 @@ Taking a self-hosted Nginx as an example, add the following content to the confi
61
90
 
62
91
  ```
63
92
  upstream myapp {
64
- # ip_hash; # Can be used for session persistence. When enabled, requests from the same client are always sent to the same backend server.
65
93
  server 172.31.0.1:13000; # Internal node 1
66
94
  server 172.31.0.2:13000; # Internal node 2
67
95
  server 172.31.0.3:13000; # Internal node 3
@@ -82,10 +110,37 @@ This means that requests are reverse-proxied and distributed to different server
82
110
 
83
111
  For load balancing middleware provided by other cloud service providers, please refer to the configuration documentation provided by the specific provider.
84
112
 
113
+ For high-availability deployments, the following is recommended:
114
+
115
+ - Run at least 2 application instances within the same cluster, and let the load balancer handle instance failover.
116
+ - The health check of the load balancer should reflect actual application availability, not just whether the port is open.
117
+ - If you need warm standby across availability zones or regions, you would typically deploy multiple independent clusters, and the operations team would be responsible for synchronizing and switching the database, shared storage, and other infrastructure.
118
+
85
119
  ## Environment Variable Configuration
86
120
 
87
121
  All nodes in the cluster should use the same environment variable configuration. In addition to NocoBase's basic [environment variables](../api/app/env), the following middleware-related environment variables also need to be configured.
88
122
 
123
+ ### Key Secrets
124
+
125
+ In addition to the middleware environment variables, all nodes in the cluster should also explicitly configure the same key secrets:
126
+
127
+ ```ini
128
+ APP_KEY=
129
+ APP_AES_SECRET_KEY=
130
+ # Or use a read-only mounted key file
131
+ # APP_AES_SECRET_KEY_PATH=
132
+ ```
133
+
134
+ - `APP_KEY` is used for token / JWT signing. If it is not explicitly configured, the application falls back to the default secret file under `storage`.
135
+ - `APP_AES_SECRET_KEY` is used to decrypt sensitive fields in the database. If it is not explicitly configured, the application also falls back to the default secret file under `storage`.
136
+ - In ephemeral containers or multi-node deployments, relying on automatically generated local secret files can cause tokens to become invalid after restart, or historical encrypted data to become undecryptable.
137
+
138
+ :::info{title=Tip}
139
+ `APP_AES_SECRET_KEY` must be a 32-byte AES-256 key, represented by 64 hexadecimal characters.
140
+
141
+ In cloud environments, it is recommended to manage these values centrally through services such as Secrets Manager, SSM Parameter Store, Kubernetes Secret, or a read-only mounted key file.
142
+ :::
143
+
89
144
  ### Multi-core Mode
90
145
 
91
146
  When the application runs on a multi-core node, you can enable the node's multi-core mode:
@@ -8,22 +8,22 @@
8
8
  */
9
9
 
10
10
  module.exports = {
11
- "@nocobase/plugin-acl": "2.1.0-beta.32",
12
- "@nocobase/plugin-workflow": "2.1.0-beta.32",
13
- "@nocobase/client": "2.1.0-beta.32",
14
- "@nocobase/utils": "2.1.0-beta.32",
15
- "@nocobase/client-v2": "2.1.0-beta.32",
16
- "@nocobase/database": "2.1.0-beta.32",
17
- "@nocobase/server": "2.1.0-beta.32",
18
- "@nocobase/plugin-file-manager": "2.1.0-beta.32",
19
- "@nocobase/actions": "2.1.0-beta.32",
20
- "@nocobase/ai": "2.1.0-beta.32",
11
+ "@nocobase/plugin-acl": "2.1.0-beta.33",
12
+ "@nocobase/plugin-workflow": "2.1.0-beta.33",
13
+ "@nocobase/client": "2.1.0-beta.33",
14
+ "@nocobase/utils": "2.1.0-beta.33",
15
+ "@nocobase/client-v2": "2.1.0-beta.33",
16
+ "@nocobase/database": "2.1.0-beta.33",
17
+ "@nocobase/server": "2.1.0-beta.33",
18
+ "@nocobase/plugin-file-manager": "2.1.0-beta.33",
19
+ "@nocobase/actions": "2.1.0-beta.33",
20
+ "@nocobase/ai": "2.1.0-beta.33",
21
21
  "langchain": "1.2.24",
22
22
  "react": "18.2.0",
23
23
  "antd": "5.24.2",
24
24
  "@formily/core": "2.3.7",
25
25
  "@formily/react": "2.3.7",
26
- "@nocobase/flow-engine": "2.1.0-beta.32",
26
+ "@nocobase/flow-engine": "2.1.0-beta.33",
27
27
  "@ant-design/icons": "5.6.1",
28
28
  "@formily/antd-v5": "1.2.3",
29
29
  "react-router-dom": "6.30.1",
@@ -39,14 +39,14 @@ module.exports = {
39
39
  "@langchain/deepseek": "1.0.11",
40
40
  "@langchain/google-genai": "2.1.18",
41
41
  "@langchain/ollama": "1.2.2",
42
- "@nocobase/acl": "2.1.0-beta.32",
43
- "@nocobase/cache": "2.1.0-beta.32",
44
- "@nocobase/resourcer": "2.1.0-beta.32",
42
+ "@nocobase/acl": "2.1.0-beta.33",
43
+ "@nocobase/cache": "2.1.0-beta.33",
44
+ "@nocobase/resourcer": "2.1.0-beta.33",
45
45
  "@emotion/css": "11.13.0",
46
46
  "dayjs": "1.11.13",
47
47
  "react-i18next": "11.18.6",
48
- "@nocobase/plugin-data-source-manager": "2.1.0-beta.32",
48
+ "@nocobase/plugin-data-source-manager": "2.1.0-beta.33",
49
49
  "@langchain/langgraph-checkpoint": "1.0.0",
50
- "@nocobase/data-source-manager": "2.1.0-beta.32",
50
+ "@nocobase/data-source-manager": "2.1.0-beta.33",
51
51
  "react-dom": "18.2.0"
52
52
  };
@@ -1 +1 @@
1
- {"name":"@langchain/xai","version":"1.3.3","description":"xAI integration for LangChain.js","author":"LangChain","license":"MIT","type":"module","engines":{"node":">=20"},"repository":{"type":"git","url":"git@github.com:langchain-ai/langchainjs.git"},"homepage":"https://github.com/langchain-ai/langchainjs/tree/main/libs/langchain-xai/","dependencies":{"@langchain/openai":"1.2.7"},"peerDependencies":{"@langchain/core":"^1.0.0"},"devDependencies":{"@tsconfig/recommended":"^1.0.3","@types/uuid":"^9","@vitest/coverage-v8":"^3.2.4","dotenv":"^16.3.1","dpdm":"^3.14.0","eslint":"^9.34.0","prettier":"^3.5.0","typescript":"~5.8.3","vitest":"^3.2.4","zod":"^3.25.76","@langchain/core":"^1.1.21","@langchain/eslint":"0.1.1","@langchain/openai":"^1.2.7","@langchain/tsconfig":"0.0.1","@langchain/standard-tests":"0.0.23"},"publishConfig":{"access":"public"},"main":"./dist/index.cjs","types":"./dist/index.d.cts","exports":{".":{"input":"./src/index.ts","require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"},"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"./package.json":"./package.json"},"files":["dist/","CHANGELOG.md","README.md","LICENSE"],"module":"./dist/index.js","scripts":{"build":"turbo build:compile --filter @langchain/xai --output-logs new-only","build:compile":"tsdown","lint:eslint":"eslint --cache src/","lint:dpdm":"dpdm --skip-dynamic-imports circular --exit-code circular:1 --no-warning --no-tree src/*.ts src/**/*.ts","lint":"pnpm lint:eslint && pnpm lint:dpdm","lint:fix":"pnpm lint:eslint --fix && pnpm lint:dpdm","clean":"rm -rf .turbo dist/","test":"vitest run","test:watch":"vitest --watch","test:int":"vitest --mode int","test:standard:unit":"vitest --mode standard-unit","test:standard:int":"vitest --mode standard-int","test:standard":"pnpm test:standard:unit && pnpm test:standard:int","format":"prettier --write \"src\"","format:check":"prettier --check \"src\"","typegen":"pnpm run typegen:profiles","typegen:profiles":"pnpm --filter @langchain/model-profiles make --config profiles.toml"},"_lastModified":"2026-05-13T05:25:55.259Z"}
1
+ {"name":"@langchain/xai","version":"1.3.3","description":"xAI integration for LangChain.js","author":"LangChain","license":"MIT","type":"module","engines":{"node":">=20"},"repository":{"type":"git","url":"git@github.com:langchain-ai/langchainjs.git"},"homepage":"https://github.com/langchain-ai/langchainjs/tree/main/libs/langchain-xai/","dependencies":{"@langchain/openai":"1.2.7"},"peerDependencies":{"@langchain/core":"^1.0.0"},"devDependencies":{"@tsconfig/recommended":"^1.0.3","@types/uuid":"^9","@vitest/coverage-v8":"^3.2.4","dotenv":"^16.3.1","dpdm":"^3.14.0","eslint":"^9.34.0","prettier":"^3.5.0","typescript":"~5.8.3","vitest":"^3.2.4","zod":"^3.25.76","@langchain/core":"^1.1.21","@langchain/eslint":"0.1.1","@langchain/openai":"^1.2.7","@langchain/tsconfig":"0.0.1","@langchain/standard-tests":"0.0.23"},"publishConfig":{"access":"public"},"main":"./dist/index.cjs","types":"./dist/index.d.cts","exports":{".":{"input":"./src/index.ts","require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"},"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"./package.json":"./package.json"},"files":["dist/","CHANGELOG.md","README.md","LICENSE"],"module":"./dist/index.js","scripts":{"build":"turbo build:compile --filter @langchain/xai --output-logs new-only","build:compile":"tsdown","lint:eslint":"eslint --cache src/","lint:dpdm":"dpdm --skip-dynamic-imports circular --exit-code circular:1 --no-warning --no-tree src/*.ts src/**/*.ts","lint":"pnpm lint:eslint && pnpm lint:dpdm","lint:fix":"pnpm lint:eslint --fix && pnpm lint:dpdm","clean":"rm -rf .turbo dist/","test":"vitest run","test:watch":"vitest --watch","test:int":"vitest --mode int","test:standard:unit":"vitest --mode standard-unit","test:standard:int":"vitest --mode standard-int","test:standard":"pnpm test:standard:unit && pnpm test:standard:int","format":"prettier --write \"src\"","format:check":"prettier --check \"src\"","typegen":"pnpm run typegen:profiles","typegen:profiles":"pnpm --filter @langchain/model-profiles make --config profiles.toml"},"_lastModified":"2026-05-15T15:54:57.807Z"}
@@ -1 +1 @@
1
- {"name":"fs-extra","version":"9.1.0","description":"fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as recursive mkdir, copy, and remove.","engines":{"node":">=10"},"homepage":"https://github.com/jprichardson/node-fs-extra","repository":{"type":"git","url":"https://github.com/jprichardson/node-fs-extra"},"keywords":["fs","file","file system","copy","directory","extra","mkdirp","mkdir","mkdirs","recursive","json","read","write","extra","delete","remove","touch","create","text","output","move","promise"],"author":"JP Richardson <jprichardson@gmail.com>","license":"MIT","dependencies":{"at-least-node":"^1.0.0","graceful-fs":"^4.2.0","jsonfile":"^6.0.1","universalify":"^2.0.0"},"devDependencies":{"coveralls":"^3.0.0","klaw":"^2.1.1","klaw-sync":"^3.0.2","minimist":"^1.1.1","mocha":"^5.0.5","nyc":"^15.0.0","proxyquire":"^2.0.1","read-dir-files":"^0.1.1","standard":"^14.1.0"},"main":"./lib/index.js","files":["lib/","!lib/**/__tests__/"],"scripts":{"full-ci":"npm run lint && npm run coverage","coverage":"nyc -r lcovonly npm run unit","coveralls":"coveralls < coverage/lcov.info","lint":"standard","test-find":"find ./lib/**/__tests__ -name *.test.js | xargs mocha","test":"npm run lint && npm run unit","unit":"node test.js"},"_lastModified":"2026-05-13T05:25:55.429Z"}
1
+ {"name":"fs-extra","version":"9.1.0","description":"fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as recursive mkdir, copy, and remove.","engines":{"node":">=10"},"homepage":"https://github.com/jprichardson/node-fs-extra","repository":{"type":"git","url":"https://github.com/jprichardson/node-fs-extra"},"keywords":["fs","file","file system","copy","directory","extra","mkdirp","mkdir","mkdirs","recursive","json","read","write","extra","delete","remove","touch","create","text","output","move","promise"],"author":"JP Richardson <jprichardson@gmail.com>","license":"MIT","dependencies":{"at-least-node":"^1.0.0","graceful-fs":"^4.2.0","jsonfile":"^6.0.1","universalify":"^2.0.0"},"devDependencies":{"coveralls":"^3.0.0","klaw":"^2.1.1","klaw-sync":"^3.0.2","minimist":"^1.1.1","mocha":"^5.0.5","nyc":"^15.0.0","proxyquire":"^2.0.1","read-dir-files":"^0.1.1","standard":"^14.1.0"},"main":"./lib/index.js","files":["lib/","!lib/**/__tests__/"],"scripts":{"full-ci":"npm run lint && npm run coverage","coverage":"nyc -r lcovonly npm run unit","coveralls":"coveralls < coverage/lcov.info","lint":"standard","test-find":"find ./lib/**/__tests__ -name *.test.js | xargs mocha","test":"npm run lint && npm run unit","unit":"node test.js"},"_lastModified":"2026-05-15T15:54:57.963Z"}
@@ -1 +1 @@
1
- {"name":"jsonrepair","version":"3.13.1","description":"Repair broken JSON documents","repository":{"type":"git","url":"https://github.com/josdejong/jsonrepair.git"},"type":"module","main":"lib/cjs/index.js","module":"lib/esm/index.js","browser":"lib/umd/jsonrepair.min.js","types":"lib/types/index.d.ts","sideEffects":false,"exports":{".":{"import":"./lib/esm/index.js","require":"./lib/cjs/index.js","types":"./lib/types/index.d.ts"},"./stream":{"import":"./lib/esm/stream.js","require":"./lib/cjs/stream.js","types":"./lib/types/stream.d.ts"}},"keywords":["simple","json","repair","fix","invalid","stream","streaming"],"bin":{"jsonrepair":"./bin/cli.js"},"scripts":{"test":"vitest watch src","test:it":"vitest run src","build":"npm-run-all build:**","build:clean":"del-cli lib","build:esm":"babel src --out-dir lib/esm --extensions \".ts\" --source-maps --config-file ./babel.config.json","build:cjs":"babel src --out-dir lib/cjs --extensions \".ts\" --source-maps --config-file ./babel-cjs.config.json && cpy tools/cjs lib/cjs --flat","build:umd":"rollup lib/esm/index.js --format umd --name JSONRepair --sourcemap --output.file lib/umd/jsonrepair.js && cpy tools/cjs/package.json lib/umd --flat","build:umd:min":"uglifyjs --compress --mangle --source-map --comments --output lib/umd/jsonrepair.min.js -- lib/umd/jsonrepair.js","build:types":"tsc --project tsconfig-types.json","build:validate":"vitest run test-lib","lint":"biome check","format":"biome check --write","benchmark":"npm run build:esm && node tools/benchmark/run.mjs","build-and-test":"npm run lint && npm run test:it && npm run build","release":"npm-run-all release:**","release:build-and-test":"npm run build-and-test","release:version":"standard-version","release:push":"git push && git push --tag","release:publish":"npm publish","release-dry-run":"npm run build-and-test && standard-version --dry-run","prepare":"husky"},"files":["README.md","LICENSE.md","lib"],"author":"Jos de Jong","license":"ISC","devDependencies":{"@babel/cli":"7.28.3","@babel/core":"7.28.4","@babel/plugin-transform-typescript":"7.28.0","@babel/preset-env":"7.28.3","@babel/preset-typescript":"7.27.1","@biomejs/biome":"2.2.4","@commitlint/cli":"19.8.1","@commitlint/config-conventional":"19.8.1","@types/node":"24.5.2","cpy-cli":"6.0.0","del-cli":"7.0.0","husky":"9.1.7","npm-run-all":"4.1.5","rollup":"4.51.0","standard-version":"9.5.0","tinybench":"5.0.1","ts-node":"10.9.2","typescript":"5.9.2","uglify-js":"3.19.3","vitest":"3.2.4"},"_lastModified":"2026-05-13T05:26:02.119Z"}
1
+ {"name":"jsonrepair","version":"3.13.1","description":"Repair broken JSON documents","repository":{"type":"git","url":"https://github.com/josdejong/jsonrepair.git"},"type":"module","main":"lib/cjs/index.js","module":"lib/esm/index.js","browser":"lib/umd/jsonrepair.min.js","types":"lib/types/index.d.ts","sideEffects":false,"exports":{".":{"import":"./lib/esm/index.js","require":"./lib/cjs/index.js","types":"./lib/types/index.d.ts"},"./stream":{"import":"./lib/esm/stream.js","require":"./lib/cjs/stream.js","types":"./lib/types/stream.d.ts"}},"keywords":["simple","json","repair","fix","invalid","stream","streaming"],"bin":{"jsonrepair":"./bin/cli.js"},"scripts":{"test":"vitest watch src","test:it":"vitest run src","build":"npm-run-all build:**","build:clean":"del-cli lib","build:esm":"babel src --out-dir lib/esm --extensions \".ts\" --source-maps --config-file ./babel.config.json","build:cjs":"babel src --out-dir lib/cjs --extensions \".ts\" --source-maps --config-file ./babel-cjs.config.json && cpy tools/cjs lib/cjs --flat","build:umd":"rollup lib/esm/index.js --format umd --name JSONRepair --sourcemap --output.file lib/umd/jsonrepair.js && cpy tools/cjs/package.json lib/umd --flat","build:umd:min":"uglifyjs --compress --mangle --source-map --comments --output lib/umd/jsonrepair.min.js -- lib/umd/jsonrepair.js","build:types":"tsc --project tsconfig-types.json","build:validate":"vitest run test-lib","lint":"biome check","format":"biome check --write","benchmark":"npm run build:esm && node tools/benchmark/run.mjs","build-and-test":"npm run lint && npm run test:it && npm run build","release":"npm-run-all release:**","release:build-and-test":"npm run build-and-test","release:version":"standard-version","release:push":"git push && git push --tag","release:publish":"npm publish","release-dry-run":"npm run build-and-test && standard-version --dry-run","prepare":"husky"},"files":["README.md","LICENSE.md","lib"],"author":"Jos de Jong","license":"ISC","devDependencies":{"@babel/cli":"7.28.3","@babel/core":"7.28.4","@babel/plugin-transform-typescript":"7.28.0","@babel/preset-env":"7.28.3","@babel/preset-typescript":"7.27.1","@biomejs/biome":"2.2.4","@commitlint/cli":"19.8.1","@commitlint/config-conventional":"19.8.1","@types/node":"24.5.2","cpy-cli":"6.0.0","del-cli":"7.0.0","husky":"9.1.7","npm-run-all":"4.1.5","rollup":"4.51.0","standard-version":"9.5.0","tinybench":"5.0.1","ts-node":"10.9.2","typescript":"5.9.2","uglify-js":"3.19.3","vitest":"3.2.4"},"_lastModified":"2026-05-15T15:55:04.228Z"}
@@ -1 +1 @@
1
- {"name":"just-bash","version":"2.14.3","description":"A simulated bash environment with virtual filesystem","repository":{"type":"git","url":"git+https://github.com/vercel-labs/just-bash.git"},"homepage":"https://github.com/vercel-labs/just-bash#readme","bugs":{"url":"https://github.com/vercel-labs/just-bash/issues"},"type":"module","main":"dist/bundle/index.js","types":"dist/index.d.ts","exports":{".":{"browser":"./dist/bundle/browser.js","require":{"types":"./dist/index.d.cts","default":"./dist/bundle/index.cjs"},"import":{"types":"./dist/index.d.ts","default":"./dist/bundle/index.js"}},"./browser":{"types":"./dist/browser.d.ts","import":"./dist/bundle/browser.js"}},"files":["dist/bundle/","dist/bin/","dist/*.d.ts","dist/*.d.cts","dist/ast/*.d.ts","dist/commands/**/*.d.ts","dist/fs/**/*.d.ts","dist/interpreter/**/*.d.ts","dist/network/**/*.d.ts","dist/parser/*.d.ts","dist/sandbox/*.d.ts","dist/utils/*.d.ts","vendor/cpython-emscripten/","README.md","CHANGELOG.md","dist/AGENTS.md"],"bin":{"just-bash":"./dist/bin/just-bash.js","just-bash-shell":"./dist/bin/shell/shell.js"},"publishConfig":{"access":"public"},"keywords":[],"author":"Malte and Claude","license":"Apache-2.0","devDependencies":{"@types/ini":"^4.1.1","@types/node":"^25.0.3","@types/papaparse":"^5.5.2","@types/sprintf-js":"^1.1.4","@types/sql.js":"^1.4.9","@types/turndown":"^5.0.6","@vitest/coverage-v8":"^4.0.18","esbuild":"^0.27.2","fast-check":"^3.23.2","knip":"^5.41.1","typescript":"^5.9.3","vitest":"^4.0.16"},"dependencies":{"seek-bzip":"^2.0.0","diff":"^8.0.2","fast-xml-parser":"5.3.3","file-type":"^21.2.0","ini":"^6.0.0","minimatch":"^10.1.1","modern-tar":"^0.7.3","papaparse":"^5.5.3","quickjs-emscripten":"^0.32.0","re2js":"^1.2.1","smol-toml":"^1.6.0","sprintf-js":"^1.1.3","sql.js":"^1.13.0","turndown":"^7.2.2","yaml":"^2.8.2"},"optionalDependencies":{"@mongodb-js/zstd":"^7.0.0","node-liblzma":"^2.0.3"},"scripts":{"build":"rm -rf dist && tsc && pnpm build:lib && pnpm build:lib:cjs && pnpm build:browser && pnpm build:cli && pnpm build:shell && pnpm build:worker && pnpm build:clean && cp dist/index.d.ts dist/index.d.cts && sed '1,/^-->/d' AGENTS.npm.md > dist/AGENTS.md","build:clean":"find dist -name '*.test.js' -delete && find dist -name '*.test.d.ts' -delete","build:worker":"esbuild src/commands/python3/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/python3/worker.js --external:../../../vendor/cpython-emscripten/* && cp src/commands/python3/worker.js dist/commands/python3/worker.js && mkdir -p dist/bin/chunks && cp src/commands/python3/worker.js dist/bin/chunks/worker.js && mkdir -p dist/bundle/chunks && cp src/commands/python3/worker.js dist/bundle/chunks/worker.js && esbuild src/commands/js-exec/js-exec-worker.ts --bundle --platform=node --format=esm --outfile=src/commands/js-exec/js-exec-worker.js --external:quickjs-emscripten && cp src/commands/js-exec/js-exec-worker.js dist/commands/js-exec/js-exec-worker.js && cp src/commands/js-exec/js-exec-worker.js dist/bin/chunks/js-exec-worker.js && cp src/commands/js-exec/js-exec-worker.js dist/bundle/chunks/js-exec-worker.js","build:lib":"esbuild dist/index.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bundle --chunk-names=chunks/[name]-[hash] --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip","build:lib:cjs":"esbuild dist/index.js --bundle --platform=node --format=cjs --minify --outfile=dist/bundle/index.cjs --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip","build:browser":"esbuild dist/browser.js --bundle --platform=browser --format=esm --minify --outfile=dist/bundle/browser.js --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:node:zlib --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip --define:__BROWSER__=true --alias:node:dns=./src/shims/browser-unsupported.js","build:cli":"esbuild dist/cli/just-bash.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip","build:shell":"esbuild dist/cli/shell.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin/shell --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip","validate":"pnpm lint && pnpm knip && pnpm typecheck && pnpm build && pnpm check:worker-sync && pnpm test:run && pnpm test:wasm && pnpm test:dist","typecheck":"tsc --noEmit","lint":"pnpm lint:banned","check:worker-sync":"node scripts/check-worker-sync.js","lint:banned":"node scripts/check-banned-patterns.js","lint:fix":"pnpm --workspace-root lint:fix","knip":"knip","test":"vitest","test:run":"vitest run --exclude src/security/fuzzing/ --exclude src/commands/python3/ --exclude src/commands/sqlite3/ --exclude src/commands/js-exec/ --exclude src/agent-examples/python-scripting.test.ts","test:dist":"vitest run src/cli/just-bash.bundle.test.ts","test:unit":"vitest run --config vitest.unit.config.ts","test:wasm":"vitest run --config vitest.wasm.config.ts","test:comparison":"vitest run --config vitest.comparison.config.ts","test:comparison:record":"RECORD_FIXTURES=1 vitest run --config vitest.comparison.config.ts","test:coverage":"vitest run --coverage","test:coverage:unit":"vitest run --config vitest.unit.config.ts --coverage","test:fuzz":"vitest run src/security/fuzzing/","test:fuzz:long":"FUZZ_RUNS=10000 vitest run src/security/fuzzing/","shell":"npx tsx src/cli/shell.ts","dev:exec":"npx tsx src/cli/exec.ts"},"_lastModified":"2026-05-13T05:26:01.883Z"}
1
+ {"name":"just-bash","version":"2.14.3","description":"A simulated bash environment with virtual filesystem","repository":{"type":"git","url":"git+https://github.com/vercel-labs/just-bash.git"},"homepage":"https://github.com/vercel-labs/just-bash#readme","bugs":{"url":"https://github.com/vercel-labs/just-bash/issues"},"type":"module","main":"dist/bundle/index.js","types":"dist/index.d.ts","exports":{".":{"browser":"./dist/bundle/browser.js","require":{"types":"./dist/index.d.cts","default":"./dist/bundle/index.cjs"},"import":{"types":"./dist/index.d.ts","default":"./dist/bundle/index.js"}},"./browser":{"types":"./dist/browser.d.ts","import":"./dist/bundle/browser.js"}},"files":["dist/bundle/","dist/bin/","dist/*.d.ts","dist/*.d.cts","dist/ast/*.d.ts","dist/commands/**/*.d.ts","dist/fs/**/*.d.ts","dist/interpreter/**/*.d.ts","dist/network/**/*.d.ts","dist/parser/*.d.ts","dist/sandbox/*.d.ts","dist/utils/*.d.ts","vendor/cpython-emscripten/","README.md","CHANGELOG.md","dist/AGENTS.md"],"bin":{"just-bash":"./dist/bin/just-bash.js","just-bash-shell":"./dist/bin/shell/shell.js"},"publishConfig":{"access":"public"},"keywords":[],"author":"Malte and Claude","license":"Apache-2.0","devDependencies":{"@types/ini":"^4.1.1","@types/node":"^25.0.3","@types/papaparse":"^5.5.2","@types/sprintf-js":"^1.1.4","@types/sql.js":"^1.4.9","@types/turndown":"^5.0.6","@vitest/coverage-v8":"^4.0.18","esbuild":"^0.27.2","fast-check":"^3.23.2","knip":"^5.41.1","typescript":"^5.9.3","vitest":"^4.0.16"},"dependencies":{"seek-bzip":"^2.0.0","diff":"^8.0.2","fast-xml-parser":"5.3.3","file-type":"^21.2.0","ini":"^6.0.0","minimatch":"^10.1.1","modern-tar":"^0.7.3","papaparse":"^5.5.3","quickjs-emscripten":"^0.32.0","re2js":"^1.2.1","smol-toml":"^1.6.0","sprintf-js":"^1.1.3","sql.js":"^1.13.0","turndown":"^7.2.2","yaml":"^2.8.2"},"optionalDependencies":{"@mongodb-js/zstd":"^7.0.0","node-liblzma":"^2.0.3"},"scripts":{"build":"rm -rf dist && tsc && pnpm build:lib && pnpm build:lib:cjs && pnpm build:browser && pnpm build:cli && pnpm build:shell && pnpm build:worker && pnpm build:clean && cp dist/index.d.ts dist/index.d.cts && sed '1,/^-->/d' AGENTS.npm.md > dist/AGENTS.md","build:clean":"find dist -name '*.test.js' -delete && find dist -name '*.test.d.ts' -delete","build:worker":"esbuild src/commands/python3/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/python3/worker.js --external:../../../vendor/cpython-emscripten/* && cp src/commands/python3/worker.js dist/commands/python3/worker.js && mkdir -p dist/bin/chunks && cp src/commands/python3/worker.js dist/bin/chunks/worker.js && mkdir -p dist/bundle/chunks && cp src/commands/python3/worker.js dist/bundle/chunks/worker.js && esbuild src/commands/js-exec/js-exec-worker.ts --bundle --platform=node --format=esm --outfile=src/commands/js-exec/js-exec-worker.js --external:quickjs-emscripten && cp src/commands/js-exec/js-exec-worker.js dist/commands/js-exec/js-exec-worker.js && cp src/commands/js-exec/js-exec-worker.js dist/bin/chunks/js-exec-worker.js && cp src/commands/js-exec/js-exec-worker.js dist/bundle/chunks/js-exec-worker.js","build:lib":"esbuild dist/index.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bundle --chunk-names=chunks/[name]-[hash] --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip","build:lib:cjs":"esbuild dist/index.js --bundle --platform=node --format=cjs --minify --outfile=dist/bundle/index.cjs --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip","build:browser":"esbuild dist/browser.js --bundle --platform=browser --format=esm --minify --outfile=dist/bundle/browser.js --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:node:zlib --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip --define:__BROWSER__=true --alias:node:dns=./src/shims/browser-unsupported.js","build:cli":"esbuild dist/cli/just-bash.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip","build:shell":"esbuild dist/cli/shell.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin/shell --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip","validate":"pnpm lint && pnpm knip && pnpm typecheck && pnpm build && pnpm check:worker-sync && pnpm test:run && pnpm test:wasm && pnpm test:dist","typecheck":"tsc --noEmit","lint":"pnpm lint:banned","check:worker-sync":"node scripts/check-worker-sync.js","lint:banned":"node scripts/check-banned-patterns.js","lint:fix":"pnpm --workspace-root lint:fix","knip":"knip","test":"vitest","test:run":"vitest run --exclude src/security/fuzzing/ --exclude src/commands/python3/ --exclude src/commands/sqlite3/ --exclude src/commands/js-exec/ --exclude src/agent-examples/python-scripting.test.ts","test:dist":"vitest run src/cli/just-bash.bundle.test.ts","test:unit":"vitest run --config vitest.unit.config.ts","test:wasm":"vitest run --config vitest.wasm.config.ts","test:comparison":"vitest run --config vitest.comparison.config.ts","test:comparison:record":"RECORD_FIXTURES=1 vitest run --config vitest.comparison.config.ts","test:coverage":"vitest run --coverage","test:coverage:unit":"vitest run --config vitest.unit.config.ts --coverage","test:fuzz":"vitest run src/security/fuzzing/","test:fuzz:long":"FUZZ_RUNS=10000 vitest run src/security/fuzzing/","shell":"npx tsx src/cli/shell.ts","dev:exec":"npx tsx src/cli/exec.ts"},"_lastModified":"2026-05-15T15:55:04.008Z"}
@@ -1 +1 @@
1
- {"name":"nodejs-snowflake","collaborators":["Utkarsh Srivastava <utkarsh@sagacious.dev>"],"description":"Generate time sortable 64 bits unique ids for distributed systems (inspired from twitter snowflake)","version":"2.0.1","license":"Apache 2.0","repository":{"type":"git","url":"https://github.com/utkarsh-pro/nodejs-snowflake.git"},"files":["nodejs_snowflake_bg.wasm","nodejs_snowflake.js","nodejs_snowflake.d.ts"],"main":"nodejs_snowflake.js","types":"nodejs_snowflake.d.ts","_lastModified":"2026-05-13T05:25:50.094Z"}
1
+ {"name":"nodejs-snowflake","collaborators":["Utkarsh Srivastava <utkarsh@sagacious.dev>"],"description":"Generate time sortable 64 bits unique ids for distributed systems (inspired from twitter snowflake)","version":"2.0.1","license":"Apache 2.0","repository":{"type":"git","url":"https://github.com/utkarsh-pro/nodejs-snowflake.git"},"files":["nodejs_snowflake_bg.wasm","nodejs_snowflake.js","nodejs_snowflake.d.ts"],"main":"nodejs_snowflake.js","types":"nodejs_snowflake.d.ts","_lastModified":"2026-05-15T15:54:53.233Z"}
@@ -1 +1 @@
1
- {"name":"openai","version":"6.22.0","description":"The official TypeScript library for the OpenAI API","author":"OpenAI <support@openai.com>","types":"./index.d.ts","main":"./index.js","type":"commonjs","repository":"github:openai/openai-node","license":"Apache-2.0","packageManager":"yarn@1.22.22","files":["**/*"],"private":false,"publishConfig":{"access":"public"},"scripts":{"test":"./scripts/test","build":"./scripts/build","format":"./scripts/format","tsn":"ts-node -r tsconfig-paths/register","lint":"./scripts/lint","fix":"./scripts/format"},"dependencies":{},"bin":{"openai":"bin/cli"},"exports":{".":{"require":{"types":"./index.d.ts","default":"./index.js"},"types":"./index.d.mts","default":"./index.mjs"},"./_vendor/*.mjs":{"default":"./_vendor/*.mjs"},"./_vendor/*.js":{"default":"./_vendor/*.js"},"./_vendor/*":{"import":"./_vendor/*.mjs","require":"./_vendor/*.js"},"./api-promise":{"import":"./api-promise.mjs","require":"./api-promise.js"},"./api-promise.js":{"default":"./api-promise.js"},"./api-promise.mjs":{"default":"./api-promise.mjs"},"./azure":{"import":"./azure.mjs","require":"./azure.js"},"./azure.js":{"default":"./azure.js"},"./azure.mjs":{"default":"./azure.mjs"},"./beta/*.mjs":{"default":"./beta/*.mjs"},"./beta/*.js":{"default":"./beta/*.js"},"./beta/*":{"import":"./beta/*.mjs","require":"./beta/*.js"},"./client":{"import":"./client.mjs","require":"./client.js"},"./client.js":{"default":"./client.js"},"./client.mjs":{"default":"./client.mjs"},"./core/*.mjs":{"default":"./core/*.mjs"},"./core/*.js":{"default":"./core/*.js"},"./core/*":{"import":"./core/*.mjs","require":"./core/*.js"},"./error":{"import":"./error.mjs","require":"./error.js"},"./error.js":{"default":"./error.js"},"./error.mjs":{"default":"./error.mjs"},"./helpers/*.mjs":{"default":"./helpers/*.mjs"},"./helpers/*.js":{"default":"./helpers/*.js"},"./helpers/*":{"import":"./helpers/*.mjs","require":"./helpers/*.js"},"./index":{"import":"./index.mjs","require":"./index.js"},"./index.js":{"default":"./index.js"},"./index.mjs":{"default":"./index.mjs"},"./lib/*.mjs":{"default":"./lib/*.mjs"},"./lib/*.js":{"default":"./lib/*.js"},"./lib/*":{"import":"./lib/*.mjs","require":"./lib/*.js"},"./pagination":{"import":"./pagination.mjs","require":"./pagination.js"},"./pagination.js":{"default":"./pagination.js"},"./pagination.mjs":{"default":"./pagination.mjs"},"./realtime/*.mjs":{"default":"./realtime/*.mjs"},"./realtime/*.js":{"default":"./realtime/*.js"},"./realtime/*":{"import":"./realtime/*.mjs","require":"./realtime/*.js"},"./resource":{"import":"./resource.mjs","require":"./resource.js"},"./resource.js":{"default":"./resource.js"},"./resource.mjs":{"default":"./resource.mjs"},"./resources/*.mjs":{"default":"./resources/*.mjs"},"./resources/*.js":{"default":"./resources/*.js"},"./resources/*":{"import":"./resources/*.mjs","require":"./resources/*.js"},"./resources":{"import":"./resources.mjs","require":"./resources.js"},"./resources.js":{"default":"./resources.js"},"./resources.mjs":{"default":"./resources.mjs"},"./streaming":{"import":"./streaming.mjs","require":"./streaming.js"},"./streaming.js":{"default":"./streaming.js"},"./streaming.mjs":{"default":"./streaming.mjs"},"./uploads":{"import":"./uploads.mjs","require":"./uploads.js"},"./uploads.js":{"default":"./uploads.js"},"./uploads.mjs":{"default":"./uploads.mjs"},"./version":{"import":"./version.mjs","require":"./version.js"},"./version.js":{"default":"./version.js"},"./version.mjs":{"default":"./version.mjs"}},"peerDependencies":{"ws":"^8.18.0","zod":"^3.25 || ^4.0"},"peerDependenciesMeta":{"ws":{"optional":true},"zod":{"optional":true}},"_lastModified":"2026-05-13T05:25:52.727Z"}
1
+ {"name":"openai","version":"6.22.0","description":"The official TypeScript library for the OpenAI API","author":"OpenAI <support@openai.com>","types":"./index.d.ts","main":"./index.js","type":"commonjs","repository":"github:openai/openai-node","license":"Apache-2.0","packageManager":"yarn@1.22.22","files":["**/*"],"private":false,"publishConfig":{"access":"public"},"scripts":{"test":"./scripts/test","build":"./scripts/build","format":"./scripts/format","tsn":"ts-node -r tsconfig-paths/register","lint":"./scripts/lint","fix":"./scripts/format"},"dependencies":{},"bin":{"openai":"bin/cli"},"exports":{".":{"require":{"types":"./index.d.ts","default":"./index.js"},"types":"./index.d.mts","default":"./index.mjs"},"./_vendor/*.mjs":{"default":"./_vendor/*.mjs"},"./_vendor/*.js":{"default":"./_vendor/*.js"},"./_vendor/*":{"import":"./_vendor/*.mjs","require":"./_vendor/*.js"},"./api-promise":{"import":"./api-promise.mjs","require":"./api-promise.js"},"./api-promise.js":{"default":"./api-promise.js"},"./api-promise.mjs":{"default":"./api-promise.mjs"},"./azure":{"import":"./azure.mjs","require":"./azure.js"},"./azure.js":{"default":"./azure.js"},"./azure.mjs":{"default":"./azure.mjs"},"./beta/*.mjs":{"default":"./beta/*.mjs"},"./beta/*.js":{"default":"./beta/*.js"},"./beta/*":{"import":"./beta/*.mjs","require":"./beta/*.js"},"./client":{"import":"./client.mjs","require":"./client.js"},"./client.js":{"default":"./client.js"},"./client.mjs":{"default":"./client.mjs"},"./core/*.mjs":{"default":"./core/*.mjs"},"./core/*.js":{"default":"./core/*.js"},"./core/*":{"import":"./core/*.mjs","require":"./core/*.js"},"./error":{"import":"./error.mjs","require":"./error.js"},"./error.js":{"default":"./error.js"},"./error.mjs":{"default":"./error.mjs"},"./helpers/*.mjs":{"default":"./helpers/*.mjs"},"./helpers/*.js":{"default":"./helpers/*.js"},"./helpers/*":{"import":"./helpers/*.mjs","require":"./helpers/*.js"},"./index":{"import":"./index.mjs","require":"./index.js"},"./index.js":{"default":"./index.js"},"./index.mjs":{"default":"./index.mjs"},"./lib/*.mjs":{"default":"./lib/*.mjs"},"./lib/*.js":{"default":"./lib/*.js"},"./lib/*":{"import":"./lib/*.mjs","require":"./lib/*.js"},"./pagination":{"import":"./pagination.mjs","require":"./pagination.js"},"./pagination.js":{"default":"./pagination.js"},"./pagination.mjs":{"default":"./pagination.mjs"},"./realtime/*.mjs":{"default":"./realtime/*.mjs"},"./realtime/*.js":{"default":"./realtime/*.js"},"./realtime/*":{"import":"./realtime/*.mjs","require":"./realtime/*.js"},"./resource":{"import":"./resource.mjs","require":"./resource.js"},"./resource.js":{"default":"./resource.js"},"./resource.mjs":{"default":"./resource.mjs"},"./resources/*.mjs":{"default":"./resources/*.mjs"},"./resources/*.js":{"default":"./resources/*.js"},"./resources/*":{"import":"./resources/*.mjs","require":"./resources/*.js"},"./resources":{"import":"./resources.mjs","require":"./resources.js"},"./resources.js":{"default":"./resources.js"},"./resources.mjs":{"default":"./resources.mjs"},"./streaming":{"import":"./streaming.mjs","require":"./streaming.js"},"./streaming.js":{"default":"./streaming.js"},"./streaming.mjs":{"default":"./streaming.mjs"},"./uploads":{"import":"./uploads.mjs","require":"./uploads.js"},"./uploads.js":{"default":"./uploads.js"},"./uploads.mjs":{"default":"./uploads.mjs"},"./version":{"import":"./version.mjs","require":"./version.js"},"./version.js":{"default":"./version.js"},"./version.mjs":{"default":"./version.mjs"}},"peerDependencies":{"ws":"^8.18.0","zod":"^3.25 || ^4.0"},"peerDependenciesMeta":{"ws":{"optional":true},"zod":{"optional":true}},"_lastModified":"2026-05-15T15:54:55.524Z"}
@@ -1 +1 @@
1
- {"name":"zod","version":"4.3.5","type":"module","license":"MIT","author":"Colin McDonnell <zod@colinhacks.com>","description":"TypeScript-first schema declaration and validation library with static type inference","homepage":"https://zod.dev","llms":"https://zod.dev/llms.txt","llmsFull":"https://zod.dev/llms-full.txt","mcpServer":"https://mcp.inkeep.com/zod/mcp","funding":"https://github.com/sponsors/colinhacks","sideEffects":false,"files":["src","**/*.js","**/*.mjs","**/*.cjs","**/*.d.ts","**/*.d.mts","**/*.d.cts","**/package.json"],"keywords":["typescript","schema","validation","type","inference"],"main":"./index.cjs","types":"./index.d.cts","module":"./index.js","zshy":{"exports":{"./package.json":"./package.json",".":"./src/index.ts","./mini":"./src/mini/index.ts","./locales":"./src/locales/index.ts","./v3":"./src/v3/index.ts","./v4":"./src/v4/index.ts","./v4-mini":"./src/v4-mini/index.ts","./v4/mini":"./src/v4/mini/index.ts","./v4/core":"./src/v4/core/index.ts","./v4/locales":"./src/v4/locales/index.ts","./v4/locales/*":"./src/v4/locales/*"},"conditions":{"@zod/source":"src"}},"exports":{"./package.json":"./package.json",".":{"@zod/source":"./src/index.ts","types":"./index.d.cts","import":"./index.js","require":"./index.cjs"},"./mini":{"@zod/source":"./src/mini/index.ts","types":"./mini/index.d.cts","import":"./mini/index.js","require":"./mini/index.cjs"},"./locales":{"@zod/source":"./src/locales/index.ts","types":"./locales/index.d.cts","import":"./locales/index.js","require":"./locales/index.cjs"},"./v3":{"@zod/source":"./src/v3/index.ts","types":"./v3/index.d.cts","import":"./v3/index.js","require":"./v3/index.cjs"},"./v4":{"@zod/source":"./src/v4/index.ts","types":"./v4/index.d.cts","import":"./v4/index.js","require":"./v4/index.cjs"},"./v4-mini":{"@zod/source":"./src/v4-mini/index.ts","types":"./v4-mini/index.d.cts","import":"./v4-mini/index.js","require":"./v4-mini/index.cjs"},"./v4/mini":{"@zod/source":"./src/v4/mini/index.ts","types":"./v4/mini/index.d.cts","import":"./v4/mini/index.js","require":"./v4/mini/index.cjs"},"./v4/core":{"@zod/source":"./src/v4/core/index.ts","types":"./v4/core/index.d.cts","import":"./v4/core/index.js","require":"./v4/core/index.cjs"},"./v4/locales":{"@zod/source":"./src/v4/locales/index.ts","types":"./v4/locales/index.d.cts","import":"./v4/locales/index.js","require":"./v4/locales/index.cjs"},"./v4/locales/*":{"@zod/source":"./src/v4/locales/*","types":"./v4/locales/*","import":"./v4/locales/*","require":"./v4/locales/*"}},"repository":{"type":"git","url":"git+https://github.com/colinhacks/zod.git"},"bugs":{"url":"https://github.com/colinhacks/zod/issues"},"support":{"backing":{"npm-funding":true}},"scripts":{"clean":"git clean -xdf . -e node_modules","build":"zshy --project tsconfig.build.json","postbuild":"tsx ../../scripts/write-stub-package-jsons.ts && pnpm biome check --write .","test:watch":"pnpm vitest","test":"pnpm vitest run","prepublishOnly":"tsx ../../scripts/check-versions.ts"},"_lastModified":"2026-05-13T05:25:51.099Z"}
1
+ {"name":"zod","version":"4.3.5","type":"module","license":"MIT","author":"Colin McDonnell <zod@colinhacks.com>","description":"TypeScript-first schema declaration and validation library with static type inference","homepage":"https://zod.dev","llms":"https://zod.dev/llms.txt","llmsFull":"https://zod.dev/llms-full.txt","mcpServer":"https://mcp.inkeep.com/zod/mcp","funding":"https://github.com/sponsors/colinhacks","sideEffects":false,"files":["src","**/*.js","**/*.mjs","**/*.cjs","**/*.d.ts","**/*.d.mts","**/*.d.cts","**/package.json"],"keywords":["typescript","schema","validation","type","inference"],"main":"./index.cjs","types":"./index.d.cts","module":"./index.js","zshy":{"exports":{"./package.json":"./package.json",".":"./src/index.ts","./mini":"./src/mini/index.ts","./locales":"./src/locales/index.ts","./v3":"./src/v3/index.ts","./v4":"./src/v4/index.ts","./v4-mini":"./src/v4-mini/index.ts","./v4/mini":"./src/v4/mini/index.ts","./v4/core":"./src/v4/core/index.ts","./v4/locales":"./src/v4/locales/index.ts","./v4/locales/*":"./src/v4/locales/*"},"conditions":{"@zod/source":"src"}},"exports":{"./package.json":"./package.json",".":{"@zod/source":"./src/index.ts","types":"./index.d.cts","import":"./index.js","require":"./index.cjs"},"./mini":{"@zod/source":"./src/mini/index.ts","types":"./mini/index.d.cts","import":"./mini/index.js","require":"./mini/index.cjs"},"./locales":{"@zod/source":"./src/locales/index.ts","types":"./locales/index.d.cts","import":"./locales/index.js","require":"./locales/index.cjs"},"./v3":{"@zod/source":"./src/v3/index.ts","types":"./v3/index.d.cts","import":"./v3/index.js","require":"./v3/index.cjs"},"./v4":{"@zod/source":"./src/v4/index.ts","types":"./v4/index.d.cts","import":"./v4/index.js","require":"./v4/index.cjs"},"./v4-mini":{"@zod/source":"./src/v4-mini/index.ts","types":"./v4-mini/index.d.cts","import":"./v4-mini/index.js","require":"./v4-mini/index.cjs"},"./v4/mini":{"@zod/source":"./src/v4/mini/index.ts","types":"./v4/mini/index.d.cts","import":"./v4/mini/index.js","require":"./v4/mini/index.cjs"},"./v4/core":{"@zod/source":"./src/v4/core/index.ts","types":"./v4/core/index.d.cts","import":"./v4/core/index.js","require":"./v4/core/index.cjs"},"./v4/locales":{"@zod/source":"./src/v4/locales/index.ts","types":"./v4/locales/index.d.cts","import":"./v4/locales/index.js","require":"./v4/locales/index.cjs"},"./v4/locales/*":{"@zod/source":"./src/v4/locales/*","types":"./v4/locales/*","import":"./v4/locales/*","require":"./v4/locales/*"}},"repository":{"type":"git","url":"git+https://github.com/colinhacks/zod.git"},"bugs":{"url":"https://github.com/colinhacks/zod/issues"},"support":{"backing":{"npm-funding":true}},"scripts":{"clean":"git clean -xdf . -e node_modules","build":"zshy --project tsconfig.build.json","postbuild":"tsx ../../scripts/write-stub-package-jsons.ts && pnpm biome check --write .","test:watch":"pnpm vitest","test":"pnpm vitest run","prepublishOnly":"tsx ../../scripts/check-versions.ts"},"_lastModified":"2026-05-15T15:54:54.107Z"}
@@ -7,6 +7,11 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import PluginAIServer from '../plugin';
10
+ type LLMStreamOptions = {
11
+ pollInterval?: number;
12
+ initialWaitTimeout?: number;
13
+ signal?: AbortSignal;
14
+ };
10
15
  export declare class LLMStreamCachedManager {
11
16
  private readonly plugin;
12
17
  private cachePromise?;
@@ -14,14 +19,10 @@ export declare class LLMStreamCachedManager {
14
19
  getCached(sessionId: string): LLMStreamCached;
15
20
  clear(sessionId: string): Promise<void>;
16
21
  append(sessionId: string, chunk: string): Promise<void>;
17
- stream(sessionId: string, options?: {
18
- pollInterval?: number;
19
- initialWaitTimeout?: number;
20
- }): AsyncGenerator<string, void, void>;
22
+ stream(sessionId: string, options?: LLMStreamOptions): AsyncGenerator<string, void, void>;
21
23
  private getChunks;
22
24
  private getCache;
23
- private withLock;
24
- private getLockKey;
25
+ private get store();
25
26
  }
26
27
  export declare class LLMStreamCached {
27
28
  private readonly sessionId;
@@ -30,8 +31,13 @@ export declare class LLMStreamCached {
30
31
  clear(): Promise<void>;
31
32
  append(chunk: string): Promise<void>;
32
33
  skipped(): Promise<void>;
33
- stream(options?: {
34
- pollInterval?: number;
35
- initialWaitTimeout?: number;
36
- }): AsyncGenerator<string, void, void>;
34
+ stream(options?: LLMStreamOptions): AsyncGenerator<string, void, void>;
37
35
  }
36
+ export declare class BufferedLLMStreamCached extends LLMStreamCached {
37
+ private buffer;
38
+ private interval;
39
+ clear(): Promise<void>;
40
+ append(chunk: string): Promise<void>;
41
+ private flush;
42
+ }
43
+ export {};
@@ -26,54 +26,59 @@ var __copyProps = (to, from, except, desc) => {
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
  var llm_stream_manager_exports = {};
28
28
  __export(llm_stream_manager_exports, {
29
+ BufferedLLMStreamCached: () => BufferedLLMStreamCached,
29
30
  LLMStreamCached: () => LLMStreamCached,
30
31
  LLMStreamCachedManager: () => LLMStreamCachedManager
31
32
  });
32
33
  module.exports = __toCommonJS(llm_stream_manager_exports);
33
- var import_utils = require("@nocobase/utils");
34
34
  const CACHE_NAME = "ai-llm-stream-cache";
35
- const LOCK_KEY_PREFIX = "ai-llm-stream-lock";
36
- const WRITE_LOCK_TTL = 3e3;
37
35
  const CACHE_TTL = 10 * 60 * 1e3;
38
36
  const STREAM_END_MARK = '"type":"stream_end"';
39
37
  const SKIPPED_MARK = "__skipped__";
40
- const DEFAULT_POLL_INTERVAL = 50;
41
- const DEFAULT_INITIAL_WAIT_TIMEOUT = 1e3;
38
+ const DEFAULT_INITIAL_WAIT_TIMEOUT = 1e4;
42
39
  class LLMStreamCachedManager {
43
40
  constructor(plugin) {
44
41
  this.plugin = plugin;
45
42
  }
46
43
  cachePromise;
47
44
  getCached(sessionId) {
45
+ if (this.store !== "memory") {
46
+ return new BufferedLLMStreamCached(sessionId, this);
47
+ }
48
48
  return new LLMStreamCached(sessionId, this);
49
49
  }
50
50
  async clear(sessionId) {
51
- await this.withLock(sessionId, async () => {
52
- const cache = await this.getCache();
53
- await cache.del(sessionId);
54
- });
51
+ const cache = await this.getCache();
52
+ await cache.del(sessionId);
55
53
  }
56
54
  async append(sessionId, chunk) {
57
- await this.withLock(sessionId, async () => {
58
- const cache = await this.getCache();
59
- const chunks = await cache.get(sessionId) ?? [];
60
- chunks.push(chunk);
61
- await cache.set(sessionId, chunks, CACHE_TTL);
62
- });
55
+ const cache = await this.getCache();
56
+ const chunks = await cache.get(sessionId) ?? [];
57
+ chunks.push(chunk);
58
+ await cache.set(sessionId, chunks, CACHE_TTL);
63
59
  }
64
60
  async *stream(sessionId, options) {
65
- const pollInterval = (options == null ? void 0 : options.pollInterval) ?? DEFAULT_POLL_INTERVAL;
61
+ const pollInterval = (options == null ? void 0 : options.pollInterval) ?? (this.store !== "memory" ? 1e3 : 100);
66
62
  const initialWaitTimeout = (options == null ? void 0 : options.initialWaitTimeout) ?? DEFAULT_INITIAL_WAIT_TIMEOUT;
63
+ const signal = options == null ? void 0 : options.signal;
67
64
  let offset = 0;
68
65
  let waited = 0;
69
66
  let completed = false;
70
67
  while (!completed) {
68
+ if (signal == null ? void 0 : signal.aborted) {
69
+ return;
70
+ }
71
71
  const chunks = await this.getChunks(sessionId);
72
72
  const lastSkippedIndex = chunks.lastIndexOf(SKIPPED_MARK);
73
+ let hasNewChunks = false;
73
74
  if (lastSkippedIndex >= offset) {
74
75
  offset = lastSkippedIndex + 1;
75
76
  }
76
77
  while (offset < chunks.length) {
78
+ if (signal == null ? void 0 : signal.aborted) {
79
+ return;
80
+ }
81
+ hasNewChunks = true;
77
82
  const chunk = chunks[offset++];
78
83
  yield chunk;
79
84
  waited = 0;
@@ -85,16 +90,15 @@ class LLMStreamCachedManager {
85
90
  if (completed) {
86
91
  return;
87
92
  }
88
- if (!chunks.length) {
89
- if (offset > 0) {
90
- return;
91
- }
93
+ if (!hasNewChunks) {
92
94
  if (waited >= initialWaitTimeout) {
93
95
  return;
94
96
  }
95
97
  waited += pollInterval;
96
98
  }
97
- await (0, import_utils.sleep)(pollInterval);
99
+ if (await abortableSleep(pollInterval, signal)) {
100
+ return;
101
+ }
98
102
  }
99
103
  }
100
104
  async getChunks(sessionId) {
@@ -104,15 +108,13 @@ class LLMStreamCachedManager {
104
108
  async getCache() {
105
109
  this.cachePromise ??= this.plugin.app.cacheManager.createCache({
106
110
  name: CACHE_NAME,
107
- store: this.plugin.app.cacheManager.defaultStore
111
+ store: this.store
108
112
  });
109
113
  return this.cachePromise;
110
114
  }
111
- async withLock(sessionId, fn) {
112
- return await this.plugin.app.lockManager.runExclusive(this.getLockKey(sessionId), fn, WRITE_LOCK_TTL);
113
- }
114
- getLockKey(sessionId) {
115
- return `${LOCK_KEY_PREFIX}:${sessionId}`;
115
+ get store() {
116
+ var _a;
117
+ return ((_a = this.plugin.app.options.cacheManager) == null ? void 0 : _a.defaultStore) ?? "memory";
116
118
  }
117
119
  }
118
120
  class LLMStreamCached {
@@ -135,8 +137,100 @@ class LLMStreamCached {
135
137
  }
136
138
  }
137
139
  }
140
+ class BufferedLLMStreamCached extends LLMStreamCached {
141
+ buffer = new ChunksBuffer();
142
+ interval;
143
+ async clear() {
144
+ clearInterval(this.interval);
145
+ delete this.interval;
146
+ await this.flush();
147
+ await super.clear();
148
+ }
149
+ async append(chunk) {
150
+ if (!this.interval) {
151
+ this.interval = setInterval(() => {
152
+ void this.flush();
153
+ }, 1e3);
154
+ }
155
+ this.buffer.append(chunk);
156
+ }
157
+ async flush() {
158
+ if (this.buffer.isEmpty()) {
159
+ return;
160
+ }
161
+ const chunks = this.buffer.compact();
162
+ this.buffer.clear();
163
+ const appendChunks = async () => {
164
+ for (const chunk of chunks) {
165
+ await super.append(chunk);
166
+ }
167
+ };
168
+ await appendChunks();
169
+ }
170
+ }
171
+ class ChunksBuffer {
172
+ _size = 0;
173
+ _chunks = [];
174
+ append(chunk) {
175
+ this._size += chunk.length;
176
+ this._chunks.push(chunk);
177
+ }
178
+ clear() {
179
+ this._size = 0;
180
+ this._chunks = [];
181
+ }
182
+ isEmpty() {
183
+ return this._size === 0;
184
+ }
185
+ get size() {
186
+ return this._size;
187
+ }
188
+ get chunks() {
189
+ return this._chunks;
190
+ }
191
+ compact() {
192
+ const chunks = [];
193
+ let buffer = "";
194
+ for (const chunk of this._chunks) {
195
+ if (chunk === SKIPPED_MARK) {
196
+ if (buffer) {
197
+ chunks.push(buffer);
198
+ buffer = "";
199
+ }
200
+ chunks.push(SKIPPED_MARK);
201
+ } else {
202
+ buffer += chunk;
203
+ }
204
+ }
205
+ if (buffer) {
206
+ chunks.push(buffer);
207
+ }
208
+ return chunks;
209
+ }
210
+ }
211
+ function abortableSleep(ms, signal) {
212
+ if (signal == null ? void 0 : signal.aborted) {
213
+ return Promise.resolve(true);
214
+ }
215
+ return new Promise((resolve) => {
216
+ const timeout = setTimeout(() => {
217
+ cleanup();
218
+ resolve(false);
219
+ }, ms);
220
+ const cleanup = () => {
221
+ clearTimeout(timeout);
222
+ signal == null ? void 0 : signal.removeEventListener("abort", abort);
223
+ };
224
+ const abort = () => {
225
+ cleanup();
226
+ resolve(true);
227
+ };
228
+ signal == null ? void 0 : signal.addEventListener("abort", abort, { once: true });
229
+ });
230
+ }
138
231
  // Annotate the CommonJS export names for ESM import in node:
139
232
  0 && (module.exports = {
233
+ BufferedLLMStreamCached,
140
234
  LLMStreamCached,
141
235
  LLMStreamCachedManager
142
236
  });
@@ -33,7 +33,7 @@ var import_server = require("@nocobase/server");
33
33
  class ai_employee_knowledge_base_add_key_default extends import_server.Migration {
34
34
  on = "afterSync";
35
35
  // 'beforeLoad' or 'afterLoad'
36
- appVersion = "<2.1.0";
36
+ appVersion = "<2.2.0";
37
37
  async up() {
38
38
  var _a, _b, _c, _d;
39
39
  const aiEmployeesRepo = this.app.db.getRepository("aiEmployees");
@@ -7,28 +7,27 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { Context, Next } from '@nocobase/actions';
10
- declare function parallelConversationsLimit(ctx: Context, next: Next): Promise<never>;
10
+ declare function loginInCheck(ctx: Context, next: Next): Promise<never>;
11
11
  declare const _default: {
12
12
  name: string;
13
13
  middlewares: {
14
- only: string[];
15
- handler: typeof parallelConversationsLimit;
14
+ handler: typeof loginInCheck;
16
15
  }[];
17
16
  actions: {
18
17
  list(ctx: Context, next: Next): Promise<void>;
19
- unreadCount(ctx: Context, next: Next): Promise<never>;
20
- unreadCounts(ctx: Context, next: Next): Promise<never>;
21
- create(ctx: Context, next: Next): Promise<never>;
22
- update(ctx: Context, next: Next): Promise<never>;
18
+ unreadCount(ctx: Context, next: Next): Promise<void>;
19
+ unreadCounts(ctx: Context, next: Next): Promise<void>;
20
+ create(ctx: Context, next: Next): Promise<void>;
21
+ update(ctx: Context, next: Next): Promise<void>;
23
22
  updateOptions(ctx: Context, next: Next): Promise<never>;
24
23
  destroy(ctx: Context, next: Next): Promise<void>;
25
- getMessages(ctx: Context, next: Next): Promise<never>;
24
+ getMessages(ctx: Context, next: Next): Promise<void>;
26
25
  updateToolArgs(ctx: Context, next: Next): Promise<any>;
27
- sendMessages(ctx: Context, next: Next): Promise<any>;
28
- abort(ctx: Context, next: Next): Promise<never>;
29
- resumeStream(ctx: Context, next: Next): Promise<never>;
30
- resendMessages(ctx: Context, next: Next): Promise<any>;
31
- updateUserDecision(ctx: Context, next: Next): Promise<never>;
26
+ sendMessages(ctx: Context, next: Next): Promise<void>;
27
+ abort(ctx: Context, next: Next): Promise<void>;
28
+ resumeStream(ctx: Context, next: Next): Promise<void>;
29
+ resendMessages(ctx: Context, next: Next): Promise<void>;
30
+ updateUserDecision(ctx: Context, next: Next): Promise<void>;
32
31
  resumeToolCall(ctx: Context, next: Next): Promise<any>;
33
32
  };
34
33
  };
@@ -43,6 +43,7 @@ var import_actions = __toESM(require("@nocobase/actions"));
43
43
  var import_database = require("@nocobase/database");
44
44
  var import_utils = require("../utils");
45
45
  var import_ai_employee = require("../ai-employees/ai-employee");
46
+ var import_ai_chat_conversation = require("../manager/ai-chat-conversation");
46
47
  async function getAIEmployee(ctx, username) {
47
48
  var _a;
48
49
  const filter = {
@@ -67,12 +68,17 @@ function setupSSEHeaders(ctx) {
67
68
  function sendErrorResponse(ctx, errorMessage) {
68
69
  (0, import_utils.sendSSEError)(ctx, errorMessage);
69
70
  }
70
- async function parallelConversationsLimit(ctx, next) {
71
+ async function loginInCheck(ctx, next) {
71
72
  var _a;
72
73
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
73
74
  if (!userId) {
74
75
  return ctx.throw(403);
75
76
  }
77
+ await next();
78
+ }
79
+ const isReachParallelLimit = async (ctx) => {
80
+ var _a;
81
+ const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
76
82
  const activeStreamCount = await ctx.db.getModel("aiConversations").count({
77
83
  where: {
78
84
  userId,
@@ -82,27 +88,32 @@ async function parallelConversationsLimit(ctx, next) {
82
88
  }
83
89
  }
84
90
  });
85
- if (activeStreamCount > 2) {
86
- sendErrorResponse(ctx, ctx.t("There are conversations in progress. Please try again later."));
91
+ return activeStreamCount > 2;
92
+ };
93
+ const saveUserMessages = async (ctx, sessionId, messages, messageId) => {
94
+ const userMessages = messages.filter((message) => message.role === "user");
95
+ if (!userMessages.length) {
87
96
  return;
88
97
  }
89
- await next();
90
- }
98
+ const aiChatConversation = (0, import_ai_chat_conversation.createAIChatConversation)(ctx, sessionId);
99
+ await aiChatConversation.withTransaction(async (conversation) => {
100
+ if (messageId && await conversation.getMessage(messageId)) {
101
+ await conversation.removeMessages({ messageId });
102
+ }
103
+ await conversation.addMessages(userMessages);
104
+ });
105
+ };
91
106
  var aiConversations_default = {
92
107
  name: "aiConversations",
93
108
  middlewares: [
94
109
  {
95
- only: ["sendMessages", "resendMessages", "resumeStream"],
96
- handler: parallelConversationsLimit
110
+ handler: loginInCheck
97
111
  }
98
112
  ],
99
113
  actions: {
100
114
  async list(ctx, next) {
101
115
  var _a;
102
116
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
103
- if (!userId) {
104
- return ctx.throw(403);
105
- }
106
117
  const filter = ctx.action.params.filter || {};
107
118
  ctx.action.mergeParams({
108
119
  filter: {
@@ -117,9 +128,6 @@ var aiConversations_default = {
117
128
  async unreadCount(ctx, next) {
118
129
  var _a;
119
130
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
120
- if (!userId) {
121
- return ctx.throw(403);
122
- }
123
131
  const count = await ctx.db.getModel("aiConversations").count({
124
132
  where: {
125
133
  userId,
@@ -136,9 +144,6 @@ var aiConversations_default = {
136
144
  async unreadCounts(ctx, next) {
137
145
  var _a;
138
146
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
139
- if (!userId) {
140
- return ctx.throw(403);
141
- }
142
147
  const [conversationUnreadCount, workflowTaskUnreadCount] = await Promise.all([
143
148
  ctx.db.getModel("aiConversations").count({
144
149
  where: {
@@ -165,9 +170,6 @@ var aiConversations_default = {
165
170
  var _a;
166
171
  const plugin = ctx.app.pm.get("ai");
167
172
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
168
- if (!userId) {
169
- return ctx.throw(403);
170
- }
171
173
  const { aiEmployee, systemMessage, skillSettings, conversationSettings, modelSettings } = ctx.action.params.values || {};
172
174
  const employee = await getAIEmployee(ctx, aiEmployee.username);
173
175
  if (!employee) {
@@ -196,9 +198,6 @@ var aiConversations_default = {
196
198
  var _a;
197
199
  const plugin = ctx.app.pm.get("ai");
198
200
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
199
- if (!userId) {
200
- return ctx.throw(403);
201
- }
202
201
  const { filterByTk: sessionId } = ctx.action.params;
203
202
  const { title } = ctx.action.params.values || {};
204
203
  ctx.body = await plugin.aiConversationsManager.update({ userId, sessionId, title });
@@ -208,9 +207,6 @@ var aiConversations_default = {
208
207
  var _a;
209
208
  const plugin = ctx.app.pm.get("ai");
210
209
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
211
- if (!userId) {
212
- return ctx.throw(403);
213
- }
214
210
  const { filterByTk: sessionId } = ctx.action.params;
215
211
  if (!sessionId) {
216
212
  return ctx.throw(400, "invalid sessionId");
@@ -236,9 +232,6 @@ var aiConversations_default = {
236
232
  async destroy(ctx, next) {
237
233
  var _a;
238
234
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
239
- if (!userId) {
240
- return ctx.throw(403);
241
- }
242
235
  ctx.action.mergeParams({
243
236
  filter: {
244
237
  userId
@@ -250,9 +243,6 @@ var aiConversations_default = {
250
243
  var _a, _b;
251
244
  const plugin = ctx.app.pm.get("ai");
252
245
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
253
- if (!userId) {
254
- return ctx.throw(403);
255
- }
256
246
  const { sessionId, cursor, updateRead: originalUpdateRead } = ctx.action.params || {};
257
247
  if (!sessionId) {
258
248
  ctx.throw(400);
@@ -279,9 +269,6 @@ var aiConversations_default = {
279
269
  var _a;
280
270
  const plugin = ctx.app.pm.get("ai");
281
271
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
282
- if (!userId) {
283
- return ctx.throw(403);
284
- }
285
272
  const {
286
273
  sessionId,
287
274
  messageId,
@@ -320,11 +307,8 @@ var aiConversations_default = {
320
307
  },
321
308
  async sendMessages(ctx, next) {
322
309
  var _a, _b, _c, _d;
323
- const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
324
- if (!userId) {
325
- return ctx.throw(403);
326
- }
327
310
  const plugin = ctx.app.pm.get("ai");
311
+ const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
328
312
  const {
329
313
  sessionId,
330
314
  aiEmployee: employeeName,
@@ -338,38 +322,27 @@ var aiConversations_default = {
338
322
  if (shouldStream) {
339
323
  setupSSEHeaders(ctx);
340
324
  }
341
- if (!sessionId) {
342
- if (shouldStream) {
343
- sendErrorResponse(ctx, "sessionId is required");
344
- } else {
345
- ctx.status = 400;
346
- ctx.body = { error: "sessionId is required" };
325
+ try {
326
+ if (!sessionId) {
327
+ throw new import_utils.ResourceActionError(400, ctx.t("sessionId is required"));
347
328
  }
348
- return next();
349
- }
350
- const userMessage = messages.find((message) => message.role === "user");
351
- if (!userMessage) {
352
- if (shouldStream) {
353
- sendErrorResponse(ctx, "user message is required");
354
- } else {
355
- ctx.status = 400;
356
- ctx.body = { error: "user message is required" };
329
+ if (!Array.isArray(messages)) {
330
+ throw new import_utils.ResourceActionError(400, ctx.t("messages must be an array"));
331
+ }
332
+ const userMessage = messages.find((message) => message.role === "user");
333
+ if (!userMessage) {
334
+ throw new import_utils.ResourceActionError(400, ctx.t("user message is required"));
357
335
  }
358
- return next();
359
- }
360
- try {
361
336
  const conversation = await plugin.aiConversationsManager.getConversation({
362
337
  sessionId,
363
338
  userId
364
339
  });
365
340
  if (!conversation) {
366
- if (shouldStream) {
367
- sendErrorResponse(ctx, "conversation not found");
368
- } else {
369
- ctx.status = 404;
370
- ctx.body = { error: "conversation not found" };
371
- }
372
- return next();
341
+ throw new import_utils.ResourceActionError(400, ctx.t("conversation not found"));
342
+ }
343
+ const employee = await getAIEmployee(ctx, employeeName);
344
+ if (!employee) {
345
+ throw new import_utils.ResourceActionError(400, ctx.t("AI employee not found"));
373
346
  }
374
347
  if (!conversation.title) {
375
348
  const textUserMessage = messages.find(
@@ -384,15 +357,9 @@ var aiConversations_default = {
384
357
  await conversation.save();
385
358
  }
386
359
  }
387
- const employee = await getAIEmployee(ctx, employeeName);
388
- if (!employee) {
389
- if (shouldStream) {
390
- sendErrorResponse(ctx, "AI employee not found");
391
- } else {
392
- ctx.status = 404;
393
- ctx.body = { error: "AI employee not found" };
394
- }
395
- return next();
360
+ if (await isReachParallelLimit(ctx)) {
361
+ await saveUserMessages(ctx, sessionId, messages, editingMessageId);
362
+ throw new import_utils.ResourceActionError(400, ctx.t("There are conversations in progress. Please try again later."));
396
363
  }
397
364
  const legacy = conversation.thread === 0;
398
365
  const resolvedModel = await plugin.aiEmployeesManager.resolveModel(employee, model);
@@ -442,11 +409,19 @@ var aiConversations_default = {
442
409
  }
443
410
  } catch (err) {
444
411
  ctx.log.error(err);
412
+ let status = 500;
413
+ let message = ctx.t("Server unexpected error occur");
414
+ if (err instanceof import_utils.ResourceActionError) {
415
+ status = err.status;
416
+ message = err.message;
417
+ } else if (err instanceof Error) {
418
+ status = 500;
419
+ message = err.message;
420
+ }
445
421
  if (shouldStream) {
446
- sendErrorResponse(ctx, err.message || "Tool call error");
422
+ sendErrorResponse(ctx, message);
447
423
  } else {
448
- ctx.status = 500;
449
- ctx.body = { error: err.message || "Tool call error" };
424
+ ctx.throw(status, message);
450
425
  }
451
426
  } finally {
452
427
  await next();
@@ -455,9 +430,6 @@ var aiConversations_default = {
455
430
  async abort(ctx, next) {
456
431
  var _a;
457
432
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
458
- if (!userId) {
459
- return ctx.throw(403);
460
- }
461
433
  const { sessionId } = ctx.action.params.values || {};
462
434
  const plugin = ctx.app.pm.get("ai");
463
435
  const conversation = await plugin.aiConversationsManager.getConversation({
@@ -474,30 +446,44 @@ var aiConversations_default = {
474
446
  var _a, _b, _c, _d, _e;
475
447
  const plugin = ctx.app.pm.get("ai");
476
448
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
477
- if (!userId) {
478
- return ctx.throw(403);
479
- }
449
+ const abortController = new AbortController();
450
+ const abortStream = () => abortController.abort();
451
+ const shouldStopStream = () => abortController.signal.aborted || ctx.res.destroyed || ctx.res.writableEnded;
480
452
  setupSSEHeaders(ctx);
481
453
  const sessionId = ((_b = ctx.action.params) == null ? void 0 : _b.sessionId) || ((_d = (_c = ctx.action.params) == null ? void 0 : _c.values) == null ? void 0 : _d.sessionId) || ((_e = ctx.action.params) == null ? void 0 : _e.filterByTk);
482
454
  if (!sessionId) {
483
455
  sendErrorResponse(ctx, "sessionId is required");
484
456
  return;
485
457
  }
458
+ ctx.req.once("aborted", abortStream);
459
+ ctx.res.once("close", abortStream);
486
460
  try {
487
461
  const conversation = await plugin.aiConversationsManager.getConversation({
488
462
  sessionId,
489
463
  userId
490
464
  });
465
+ if (shouldStopStream()) {
466
+ return;
467
+ }
491
468
  if (!conversation) {
492
469
  sendErrorResponse(ctx, "conversation not found");
493
470
  return;
494
471
  }
472
+ const reachLimit = await isReachParallelLimit(ctx);
473
+ if (shouldStopStream()) {
474
+ return;
475
+ }
495
476
  let hasChunks = false;
496
- for await (const chunk of plugin.llmStreamCachedManager.getCached(sessionId).stream()) {
497
- hasChunks = true;
498
- ctx.res.write(chunk);
477
+ if (!reachLimit) {
478
+ for await (const chunk of plugin.llmStreamCachedManager.getCached(sessionId).stream({ signal: abortController.signal })) {
479
+ if (shouldStopStream()) {
480
+ break;
481
+ }
482
+ hasChunks = true;
483
+ ctx.res.write(chunk);
484
+ }
499
485
  }
500
- if (!hasChunks) {
486
+ if (!hasChunks && !shouldStopStream()) {
501
487
  const currentConversation = await plugin.aiConversationsManager.getConversation({
502
488
  sessionId,
503
489
  userId
@@ -510,11 +496,16 @@ var aiConversations_default = {
510
496
  }
511
497
  }
512
498
  } catch (err) {
499
+ if (shouldStopStream()) {
500
+ return;
501
+ }
513
502
  ctx.log.error(err);
514
503
  sendErrorResponse(ctx, err.message || "Resume stream error");
515
504
  return;
516
505
  } finally {
517
- if (!ctx.res.writableEnded) {
506
+ ctx.req.off("aborted", abortStream);
507
+ ctx.res.off("close", abortStream);
508
+ if (!shouldStopStream()) {
518
509
  ctx.res.end();
519
510
  }
520
511
  await next();
@@ -524,24 +515,26 @@ var aiConversations_default = {
524
515
  var _a, _b, _c, _d;
525
516
  const plugin = ctx.app.pm.get("ai");
526
517
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
527
- if (!userId) {
528
- return ctx.throw(403);
529
- }
530
- setupSSEHeaders(ctx);
531
- const { sessionId, webSearch, model } = ctx.action.params.values || {};
518
+ const { sessionId, webSearch, model, stream = true } = ctx.action.params.values || {};
532
519
  let { messageId } = ctx.action.params.values || {};
533
- if (!sessionId) {
534
- sendErrorResponse(ctx, "sessionId is required");
535
- return next();
520
+ const shouldStream = stream !== false;
521
+ if (shouldStream) {
522
+ setupSSEHeaders(ctx);
536
523
  }
537
524
  try {
525
+ if (!sessionId) {
526
+ throw new import_utils.ResourceActionError(400, ctx.t("sessionId is required"));
527
+ }
538
528
  const conversation = await plugin.aiConversationsManager.getConversation({
539
529
  sessionId,
540
530
  userId
541
531
  });
542
532
  if (!conversation) {
543
- sendErrorResponse(ctx, "conversation not found");
544
- return next();
533
+ throw new import_utils.ResourceActionError(400, ctx.t("conversation not found"));
534
+ }
535
+ const employee = await getAIEmployee(ctx, conversation.aiEmployeeUsername);
536
+ if (!employee) {
537
+ throw new import_utils.ResourceActionError(400, ctx.t("AI employee not found"));
545
538
  }
546
539
  const resendMessages = [];
547
540
  if (messageId) {
@@ -551,8 +544,7 @@ var aiConversations_default = {
551
544
  }
552
545
  });
553
546
  if (!message) {
554
- sendErrorResponse(ctx, "message not found");
555
- return next();
547
+ throw new import_utils.ResourceActionError(400, ctx.t("message not found"));
556
548
  }
557
549
  } else {
558
550
  const message = await ctx.db.getRepository("aiConversations.messages", sessionId).findOne({
@@ -562,8 +554,7 @@ var aiConversations_default = {
562
554
  sort: ["-messageId"]
563
555
  });
564
556
  if (!message) {
565
- sendErrorResponse(ctx, "No messages to resend");
566
- return next();
557
+ throw new import_utils.ResourceActionError(400, ctx.t("message not found"));
567
558
  }
568
559
  messageId = message.messageId;
569
560
  if (["user", "tool"].includes(message.role)) {
@@ -577,10 +568,8 @@ var aiConversations_default = {
577
568
  });
578
569
  }
579
570
  }
580
- const employee = await getAIEmployee(ctx, conversation.aiEmployeeUsername);
581
- if (!employee) {
582
- sendErrorResponse(ctx, "AI employee not found");
583
- return next();
571
+ if (await isReachParallelLimit(ctx)) {
572
+ throw new import_utils.ResourceActionError(400, ctx.t("There are conversations in progress. Please try again later."));
584
573
  }
585
574
  const resolvedModel = await plugin.aiEmployeesManager.resolveModel(employee, model);
586
575
  const aiEmployee = new import_ai_employee.AIEmployee({
@@ -593,20 +582,38 @@ var aiConversations_default = {
593
582
  webSearch,
594
583
  model: resolvedModel
595
584
  });
596
- await aiEmployee.stream({ messageId, userMessages: resendMessages.length ? resendMessages : void 0 });
585
+ if (shouldStream) {
586
+ await aiEmployee.stream({ messageId, userMessages: resendMessages.length ? resendMessages : void 0 });
587
+ } else {
588
+ ctx.body = await aiEmployee.invoke({
589
+ messageId,
590
+ userMessages: resendMessages.length ? resendMessages : void 0
591
+ });
592
+ }
597
593
  } catch (err) {
598
594
  ctx.log.error(err);
599
- sendErrorResponse(ctx, err.message || "Chat error warning");
595
+ let status = 500;
596
+ let message = ctx.t("Server unexpected error occur");
597
+ if (err instanceof import_utils.ResourceActionError) {
598
+ status = err.status;
599
+ message = err.message;
600
+ } else if (err instanceof Error) {
601
+ status = 500;
602
+ message = err.message;
603
+ }
604
+ if (shouldStream) {
605
+ sendErrorResponse(ctx, message);
606
+ } else {
607
+ ctx.throw(status, message);
608
+ }
609
+ } finally {
610
+ await next();
600
611
  }
601
- await next();
602
612
  },
603
613
  async updateUserDecision(ctx, next) {
604
614
  var _a;
605
615
  const plugin = ctx.app.pm.get("ai");
606
616
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
607
- if (!userId) {
608
- return ctx.throw(403);
609
- }
610
617
  const { sessionId, messageId, toolCallId, userDecision } = ctx.action.params.values || {};
611
618
  if (!sessionId) {
612
619
  ctx.throw(400);
@@ -686,9 +693,6 @@ var aiConversations_default = {
686
693
  async resumeToolCall(ctx, next) {
687
694
  var _a, _b, _c, _d;
688
695
  const userId = (_a = ctx.auth) == null ? void 0 : _a.user.id;
689
- if (!userId) {
690
- return ctx.throw(403);
691
- }
692
696
  setupSSEHeaders(ctx);
693
697
  const plugin = ctx.app.pm.get("ai");
694
698
  const { sessionId, messageId, model, webSearch } = ctx.action.params.values || {};
@@ -21,3 +21,7 @@ export declare function encodeLocalFile(url: string): Promise<string>;
21
21
  export declare function encodeFile(ctx: Context, url: string): Promise<string>;
22
22
  export declare function parseVariables(ctx: Context, value: string): Promise<any>;
23
23
  export declare const buildTool: (toolsEntry: ToolsEntry) => import("@langchain/core/dist/tools").DynamicTool<any>;
24
+ export declare class ResourceActionError extends Error {
25
+ readonly status: number;
26
+ constructor(status: number, message: string, options?: ErrorOptions);
27
+ }
@@ -36,6 +36,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
  var utils_exports = {};
38
38
  __export(utils_exports, {
39
+ ResourceActionError: () => ResourceActionError,
39
40
  buildTool: () => buildTool,
40
41
  encodeFile: () => encodeFile,
41
42
  encodeLocalFile: () => encodeLocalFile,
@@ -179,8 +180,16 @@ const buildTool = (toolsEntry) => {
179
180
  }
180
181
  );
181
182
  };
183
+ class ResourceActionError extends Error {
184
+ constructor(status, message, options) {
185
+ super(message, options);
186
+ this.status = status;
187
+ this.name = "ResourceActionError";
188
+ }
189
+ }
182
190
  // Annotate the CommonJS export names for ESM import in node:
183
191
  0 && (module.exports = {
192
+ ResourceActionError,
184
193
  buildTool,
185
194
  encodeFile,
186
195
  encodeLocalFile,
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Create AI employees with diverse skills to collaborate with humans, build systems, and handle business operations.",
7
7
  "description.ru-RU": "Поддержка интеграции с AI-сервисами: предоставляются AI-узлы для рабочих процессов, расширяя возможности бизнес-обработки.",
8
8
  "description.zh-CN": "创建各种技能的 AI 员工,与人类协同,搭建系统,处理业务。",
9
- "version": "2.1.0-beta.32",
9
+ "version": "2.1.0-beta.33",
10
10
  "main": "dist/server/index.js",
11
11
  "homepage": "https://docs.nocobase.com/handbook/action-ai",
12
12
  "homepage.ru-RU": "https://docs-ru.nocobase.com/handbook/action-ai",
@@ -64,5 +64,5 @@
64
64
  "keywords": [
65
65
  "AI"
66
66
  ],
67
- "gitHead": "659c5efe992da7118d33c768bbd9e837a2c4716f"
67
+ "gitHead": "4815c394e80a264fa8ed619246280923c47aeb72"
68
68
  }