agentworkshop 0.2.2 → 0.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.
@@ -7,7 +7,7 @@
7
7
  "productId": "pd-3ad54846",
8
8
  "productName": "光学膜 BOPP",
9
9
  "startedAt": "2026-08-31T16:20:13.707Z",
10
- "taggedSamples": 29498
10
+ "taggedSamples": 29764
11
11
  },
12
12
  {
13
13
  "lineId": "ln-c377a6bf",
@@ -17,7 +17,7 @@
17
17
  "productId": "pd-7e69e0ee",
18
18
  "productName": "警告验证产品-k2xh",
19
19
  "startedAt": "2026-09-01T03:23:18.233Z",
20
- "taggedSamples": 11559
20
+ "taggedSamples": 11692
21
21
  },
22
22
  {
23
23
  "lineId": "ln-9224d3d7",
@@ -27,7 +27,7 @@
27
27
  "productId": "pd-910c0fa8",
28
28
  "productName": "新协议产品-9xhn",
29
29
  "startedAt": "2026-09-01T05:59:24.062Z",
30
- "taggedSamples": 14533
30
+ "taggedSamples": 14665
31
31
  },
32
32
  {
33
33
  "lineId": "ln-386c53ed",
@@ -37,7 +37,7 @@
37
37
  "productId": "pd-2d4f9357",
38
38
  "productName": "新协议产品-6ofk",
39
39
  "startedAt": "2026-09-01T06:05:53.063Z",
40
- "taggedSamples": 14823
40
+ "taggedSamples": 14955
41
41
  },
42
42
  {
43
43
  "lineId": "ln-5957f6db",
@@ -47,7 +47,7 @@
47
47
  "productId": "pd-a3ed5047",
48
48
  "productName": "新协议产品-x4hd",
49
49
  "startedAt": "2026-09-01T06:09:02.148Z",
50
- "taggedSamples": 14409
50
+ "taggedSamples": 14541
51
51
  },
52
52
  {
53
53
  "lineId": "ln-9ec42f08",
@@ -57,7 +57,7 @@
57
57
  "productId": "pd-5de729c0",
58
58
  "productName": "新协议产品-teti",
59
59
  "startedAt": "2026-09-01T06:13:44.315Z",
60
- "taggedSamples": 14388
60
+ "taggedSamples": 14520
61
61
  },
62
62
  {
63
63
  "lineId": "ln-69e37924",
@@ -67,7 +67,7 @@
67
67
  "productId": "pd-45eb8d43",
68
68
  "productName": "新协议产品-fg8t",
69
69
  "startedAt": "2026-09-01T06:16:47.469Z",
70
- "taggedSamples": 14421
70
+ "taggedSamples": 14553
71
71
  },
72
72
  {
73
73
  "lineId": "ln-c269e692",
@@ -77,7 +77,7 @@
77
77
  "productId": "pd-b3bd0191",
78
78
  "productName": "新协议产品-w2sp",
79
79
  "startedAt": "2026-09-01T06:56:05.893Z",
80
- "taggedSamples": 14472
80
+ "taggedSamples": 14604
81
81
  },
82
82
  {
83
83
  "lineId": "ln-229d1637",
@@ -87,7 +87,7 @@
87
87
  "productId": "pd-06b896f9",
88
88
  "productName": "新协议产品-lp4h",
89
89
  "startedAt": "2026-09-01T06:57:29.288Z",
90
- "taggedSamples": 14344
90
+ "taggedSamples": 14476
91
91
  },
92
92
  {
93
93
  "lineId": "ln-9546f91f",
@@ -97,7 +97,7 @@
97
97
  "productId": "pd-53e55078",
98
98
  "productName": "新协议产品-jri6",
99
99
  "startedAt": "2026-09-01T07:00:25.059Z",
100
- "taggedSamples": 13767
100
+ "taggedSamples": 13899
101
101
  },
102
102
  {
103
103
  "lineId": "ln-087aad5b",
@@ -107,7 +107,7 @@
107
107
  "productId": "pd-e7e969af",
108
108
  "productName": "新协议产品-4c2d",
109
109
  "startedAt": "2026-09-01T07:09:07.307Z",
110
- "taggedSamples": 12758
110
+ "taggedSamples": 12890
111
111
  },
112
112
  {
113
113
  "lineId": "ln-97d44f8a",
@@ -117,7 +117,7 @@
117
117
  "productId": "pd-28a3ab2c",
118
118
  "productName": "新协议产品-b2ni",
119
119
  "startedAt": "2026-09-01T07:13:44.912Z",
120
- "taggedSamples": 12607
120
+ "taggedSamples": 12739
121
121
  },
122
122
  {
123
123
  "lineId": "ln-a9551770",
@@ -127,7 +127,7 @@
127
127
  "productId": "pd-0c6a64c8",
128
128
  "productName": "新协议产品-uwkf",
129
129
  "startedAt": "2026-09-01T07:15:40.113Z",
130
- "taggedSamples": 12252
130
+ "taggedSamples": 12384
131
131
  },
132
132
  {
133
133
  "lineId": "ln-844ad82f",
@@ -137,7 +137,7 @@
137
137
  "productId": "pd-6932b24f",
138
138
  "productName": "新协议产品-kk6l",
139
139
  "startedAt": "2026-09-01T09:06:55.728Z",
140
- "taggedSamples": 10407
140
+ "taggedSamples": 10539
141
141
  },
142
142
  {
143
143
  "lineId": "ln-af002514",
@@ -147,6 +147,66 @@
147
147
  "productId": "pd-6348a595",
148
148
  "productName": "0.8mm 光学膜",
149
149
  "startedAt": "2026-09-01T14:06:28.766Z",
150
- "taggedSamples": 676
150
+ "taggedSamples": 952
151
+ },
152
+ {
153
+ "lineId": "ln-0eccf6ba",
154
+ "runId": "rr-4e0a115f",
155
+ "recipeId": "rc-8a60518e",
156
+ "recipeName": "哨兵配方-mtjku36w",
157
+ "productId": "pd-4b71be77",
158
+ "productName": "哨兵产品-mtjku36w",
159
+ "startedAt": "2026-09-02T04:09:11.482Z",
160
+ "taggedSamples": 5
161
+ },
162
+ {
163
+ "lineId": "ln-e984246e",
164
+ "runId": "rr-39b3edda",
165
+ "recipeId": "rc-70f39a03",
166
+ "recipeName": "配方C-mtjkwyhu",
167
+ "productId": "pd-bb1e0bed",
168
+ "productName": "产品C-mtjkwyhu",
169
+ "startedAt": "2026-09-02T04:11:26.583Z",
170
+ "taggedSamples": 4
171
+ },
172
+ {
173
+ "lineId": "ln-1fe0b5bc",
174
+ "runId": "rr-2baf5f34",
175
+ "recipeId": "rc-25081a45",
176
+ "recipeName": "配方D-mtjkzcfr",
177
+ "productId": "pd-9f2d9507",
178
+ "productName": "产品D-mtjkzcfr",
179
+ "startedAt": "2026-09-02T04:13:17.902Z",
180
+ "taggedSamples": 3
181
+ },
182
+ {
183
+ "lineId": "ln-d826473e",
184
+ "runId": "rr-7585cef0",
185
+ "recipeId": "rc-3ebd1a97",
186
+ "recipeName": "配方E-mtjl43pv",
187
+ "productId": "pd-d230255e",
188
+ "productName": "产品E-mtjl43pv",
189
+ "startedAt": "2026-09-02T04:16:59.891Z",
190
+ "taggedSamples": 2
191
+ },
192
+ {
193
+ "lineId": "ln-156abb71",
194
+ "runId": "rr-d8002452",
195
+ "recipeId": "rc-fce9474f",
196
+ "recipeName": "配方F-mtjl60qf",
197
+ "productId": "pd-6065b4ba",
198
+ "productName": "产品F-mtjl60qf",
199
+ "startedAt": "2026-09-02T04:18:29.340Z",
200
+ "taggedSamples": 1
201
+ },
202
+ {
203
+ "lineId": "ln-bac43927",
204
+ "runId": "rr-7f4a6ac0",
205
+ "recipeId": "rc-e27a0673",
206
+ "recipeName": "配方G-mtjl98nl",
207
+ "productId": "pd-52f54e33",
208
+ "productName": "产品G-mtjl98nl",
209
+ "startedAt": "2026-09-02T04:20:59.682Z",
210
+ "taggedSamples": 0
151
211
  }
152
212
  ]
@@ -11,7 +11,13 @@ import { dirname, resolve } from 'node:path'
11
11
  const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..')
12
12
 
13
13
  export default defineNitroPlugin((nitroApp) => {
14
- void initPluginHost({ cwd: process.cwd(), packageRoot })
14
+ void initPluginHost({ cwd: process.cwd(), packageRoot }).then((host) => {
15
+ // 实际监听端口回填(nitro listen 后 ctx.api 自环 origin 才准确)
16
+ nitroApp.hooks.hookOnce('listen', (listener: { port?: number }) => {
17
+ if (listener?.port)
18
+ host.setSelfOrigin(listener.port)
19
+ })
20
+ })
15
21
  nitroApp.hooks.hookOnce('close', () => {
16
22
  void shutdownPluginHost()
17
23
  })
@@ -9,7 +9,7 @@
9
9
  // —— ctx 由宿主注入,插件运行时零导入依赖(sdk/ 供类型与显式糖)。
10
10
  // 错误隔离:单插件装载/执行失败记入 failures,绝不拖垮主服务。
11
11
  // ============================================================
12
- import { existsSync, readdirSync, readFileSync } from 'node:fs'
12
+ import { existsSync, readdirSync, readFileSync, watch } from 'node:fs'
13
13
  import { join, resolve } from 'node:path'
14
14
  import { pathToFileURL } from 'node:url'
15
15
  import { HookBus, createPluginContext, createRouteTable, validatePluginModule } from '@/sdk/index.mjs'
@@ -75,6 +75,7 @@ export async function initPluginHost({ cwd = process.cwd(), packageRoot } = {})
75
75
  }),
76
76
  routes: createRouteTable(),
77
77
  plugins: new Map(),
78
+ disposables: new Map(), // name → fn[](setup 内 ctx.onDispose 登记;server:close 时回收)
78
79
  failures: [],
79
80
  initedAt: null,
80
81
  logger,
@@ -83,6 +84,7 @@ export async function initPluginHost({ cwd = process.cwd(), packageRoot } = {})
83
84
 
84
85
  // 有效配置(只读面;引擎/模式解析从运行根动态加载,js-yaml 由对应 node_modules 解析)
85
86
  let config = null
87
+ let settingsPath = null
86
88
  let paths = { home: defaultHome(), configRoot: join(cwd, '.AgentWorkShop'), dataDir: join(cwd, '.AgentWorkShop', 'data') }
87
89
  try {
88
90
  const homeMod = await import(pathToUrl(join(cwd, 'shared', 'config', 'home.mjs')))
@@ -91,6 +93,7 @@ export async function initPluginHost({ cwd = process.cwd(), packageRoot } = {})
91
93
  if (engineRoot && existsSync(join(engineRoot, 'shared', 'config', 'engine.mjs'))) {
92
94
  const engine = await import(pathToUrl(join(engineRoot, 'shared', 'config', 'engine.mjs')))
93
95
  config = engine.loadEffective({ configPath: rm.configPath, settingsPath: rm.settingsPath, env: process.env })
96
+ settingsPath = rm.settingsPath
94
97
  }
95
98
  paths = { home: rm.home, configRoot: rm.configRoot, dataDir: rm.dataDir }
96
99
  host.logger.info(`配置根: ${rm.configRoot} (${rm.mode} 模式)`)
@@ -98,6 +101,51 @@ export async function initPluginHost({ cwd = process.cwd(), packageRoot } = {})
98
101
  catch (err) {
99
102
  host.logger.warn('配置引擎加载降级(插件 ctx.config 将为空):', err?.message)
100
103
  }
104
+ host.config = config
105
+ // 自环 origin:PORT env(prod) > argv --port(dev:dev-guard 转发链天然携带) > 配置 dev 端口;
106
+ // nitro listen 钩子(setSelfOrigin)仍保留为最终兜底
107
+ const argPort = (() => {
108
+ const argv = process.argv
109
+ const i = argv.indexOf('--port')
110
+ if (i >= 0 && argv[i + 1]) return argv[i + 1]
111
+ const eq = argv.find(a => a.startsWith('--port='))
112
+ return eq ? eq.slice(7) : null
113
+ })()
114
+ let selfOrigin = `http://127.0.0.1:${process.env.PORT ?? process.env.NITRO_PORT ?? argPort ?? config?.effective?.['server.dev.port'] ?? 3000}`
115
+ host.setSelfOrigin = (port) => {
116
+ selfOrigin = `http://127.0.0.1:${port}`
117
+ host.logger.info(`自环 origin 就绪: ${selfOrigin}`)
118
+ }
119
+ host.selfOrigin = () => selfOrigin
120
+ host.logger.info(`自环 origin: ${selfOrigin} (PORT=${process.env.PORT ?? '∅'} NITRO_PORT=${process.env.NITRO_PORT ?? '∅'} argPort=${argPort ?? '∅'})`)
121
+
122
+ // 配置变更监听:runtime-settings.json 变化 → 原地刷新 effective + config:changed 钩子
123
+ if (settingsPath && existsSync(settingsPath)) {
124
+ try {
125
+ let debounce = null
126
+ watch(settingsPath, () => {
127
+ clearTimeout(debounce)
128
+ debounce = setTimeout(async () => {
129
+ try {
130
+ if (host.config && existsSync(settingsPath)) {
131
+ const homeMod = await import(pathToUrl(join(cwd, 'shared', 'config', 'home.mjs')))
132
+ const rm = homeMod.resolveRunMode({ cwd, packageRoot, env: process.env })
133
+ const engine = await import(pathToUrl(join((rm.mode === 'repo' ? rm.root : packageRoot ?? rm.root) ?? cwd, 'shared', 'config', 'engine.mjs')))
134
+ const fresh = engine.loadEffective({ configPath: rm.configPath, settingsPath: rm.settingsPath, env: process.env })
135
+ Object.assign(host.config.effective, fresh.effective)
136
+ host.config.sources = fresh.sources
137
+ }
138
+ await host.bus.emit('config:changed', { at: new Date().toISOString() })
139
+ host.logger.info('配置变更已广播(config:changed)')
140
+ }
141
+ catch (err) {
142
+ host.logger.warn('config:changed 处理失败:', err?.message)
143
+ }
144
+ }, 300)
145
+ })
146
+ }
147
+ catch { /* fs.watch 不可用时插件可经轮询自行感知 */ }
148
+ }
101
149
 
102
150
  const entries = discoverPluginDirs(cwd)
103
151
  if (entries.length) host.logger.info(`发现 ${entries.length} 个插件,开始装载 ...`)
@@ -129,12 +177,15 @@ export async function initPluginHost({ cwd = process.cwd(), packageRoot } = {})
129
177
  rec.routes.push({ method: String(m).toUpperCase(), path: p })
130
178
  },
131
179
  }
180
+ const perPluginDisposables = []
181
+ host.disposables.set(def.name, perPluginDisposables)
132
182
  const ctx = createPluginContext({
133
183
  name: def.name,
134
184
  scope,
135
185
  dir,
136
186
  hooks: host.bus,
137
187
  logger: {
188
+ debug: (...a) => logger.info(`[${def.name}][debug]`, ...a),
138
189
  info: (...a) => logger.info(`[${def.name}]`, ...a),
139
190
  warn: (...a) => logger.warn(`[${def.name}]`, ...a),
140
191
  error: (...a) => logger.error(`[${def.name}]`, ...a),
@@ -142,6 +193,11 @@ export async function initPluginHost({ cwd = process.cwd(), packageRoot } = {})
142
193
  config,
143
194
  paths,
144
195
  emitter,
196
+ onDispose: (fn) => {
197
+ perPluginDisposables.push(fn)
198
+ return fn
199
+ },
200
+ selfOrigin: host.selfOrigin,
145
201
  })
146
202
 
147
203
  // 声明式 routes + setup 内 ctx.route() 两种形态都支持
@@ -214,10 +270,20 @@ export function pluginManifest() {
214
270
  }))
215
271
  }
216
272
 
217
- /** 关机钩子(nitro close 时调用) */
273
+ /** 关机钩子(nitro close 时调用):先逐插件回收订阅/定时器,再广播 server:close */
218
274
  export async function shutdownPluginHost() {
219
275
  const host = getPluginHost()
220
276
  if (!host) return
277
+ for (const [name, list] of host.disposables ?? []) {
278
+ for (const fn of list.splice(0)) {
279
+ try {
280
+ await fn()
281
+ }
282
+ catch (err) {
283
+ host.logger.warn(`[${name}] onDispose 失败:`, err?.message)
284
+ }
285
+ }
286
+ }
221
287
  await host.bus.emit('server:close', { at: new Date().toISOString() })
222
- host.logger.info('已发出 server:close,插件宿主关闭')
288
+ host.logger.info('插件清理完成,已发出 server:close')
223
289
  }