crewx 0.8.9-rc.2 → 0.8.9-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{MarketPage-BpajB_Ex.js → MarketPage-Cl9KvLAo.js} +1 -1
- package/dist/assets/PromptTab-gj_BvClc.js +27 -0
- package/dist/assets/{arc-B0fFLEOc.js → arc-DDzVSXZh.js} +1 -1
- package/dist/assets/{architectureDiagram-UYN6MBPD-CrC7MKTM.js → architectureDiagram-UYN6MBPD-p8OOXe5w.js} +1 -1
- package/dist/assets/{blockDiagram-ZHA2E4KO-DJsG-9x2.js → blockDiagram-ZHA2E4KO-BTlfXGnf.js} +7 -7
- package/dist/assets/{c4Diagram-6F5ED5ID-DlkyjvkS.js → c4Diagram-6F5ED5ID-CKbrDPWE.js} +1 -1
- package/dist/assets/channel-Hzz-cXd0.js +1 -0
- package/dist/assets/{chunk-5HRBRIJM-B_Csqjvw.js → chunk-5HRBRIJM-otR_A_1G.js} +1 -1
- package/dist/assets/{chunk-7U56Z5CX-BlNdzxbh.js → chunk-7U56Z5CX-BliJIH4-.js} +1 -1
- package/dist/assets/{chunk-ASOPGD6M-BiFWfa3w.js → chunk-ASOPGD6M-CmzypD4Z.js} +1 -1
- package/dist/assets/{chunk-KFBOBJHC-ocugjY-9.js → chunk-KFBOBJHC-BW4u3lPL.js} +1 -1
- package/dist/assets/{chunk-T2TOU4HS-kUreJ-bv.js → chunk-T2TOU4HS-BAp6Xscz.js} +1 -1
- package/dist/assets/{chunk-TMUBEWPD-Du19Q8X-.js → chunk-TMUBEWPD-Bv_RKIlz.js} +1 -1
- package/dist/assets/classDiagram-LNE6IOMH-CXR-DJKP.js +1 -0
- package/dist/assets/classDiagram-v2-MQ7JQ4JX-CXR-DJKP.js +1 -0
- package/dist/assets/{dagre-4EVJKHTY-8N5qxubq.js → dagre-4EVJKHTY-sYyP6lkA.js} +1 -1
- package/dist/assets/{diagram-QW4FP2JN-CjLH9uYk.js → diagram-QW4FP2JN-XWldv7Gz.js} +1 -1
- package/dist/assets/{erDiagram-6RL3IURR-CXgt8NtN.js → erDiagram-6RL3IURR-Cd8OVAQ0.js} +1 -1
- package/dist/assets/{flowDiagram-7ASYPVHJ-CdFlmdmT.js → flowDiagram-7ASYPVHJ-Cc3onSB6.js} +1 -1
- package/dist/assets/{ganttDiagram-NTVNEXSI-DDjK4yxy.js → ganttDiagram-NTVNEXSI-Q6HxMUkA.js} +1 -1
- package/dist/assets/{gitGraph-YCYPL57B-Bd9jYZ-C.js → gitGraph-YCYPL57B-BUh0zUco.js} +1 -1
- package/dist/assets/{gitGraphDiagram-NRZ2UAAF-pmwetPkN.js → gitGraphDiagram-NRZ2UAAF-CvPqw-ya.js} +1 -1
- package/dist/assets/{infoDiagram-A4XQUW5V-BoDEaOyd.js → infoDiagram-A4XQUW5V-CbKGdGqb.js} +1 -1
- package/dist/assets/{journeyDiagram-G5WM74LC-D1LRuhB3.js → journeyDiagram-G5WM74LC-BXyAvuxS.js} +1 -1
- package/dist/assets/{kanban-definition-QRCXZQQD-B66SGjA2.js → kanban-definition-QRCXZQQD-CxBFX9if.js} +1 -1
- package/dist/assets/{linear-VMCGabuP.js → linear-Bu0P9vdO.js} +1 -1
- package/dist/assets/main-AYmbxFu4.css +10 -0
- package/dist/assets/main-CcrxU2Tf.js +1454 -0
- package/dist/assets/{mindmap-definition-GWI6TPTV--NljwHD9.js → mindmap-definition-GWI6TPTV-BbXI83yG.js} +1 -1
- package/dist/assets/{pieDiagram-YF2LJOPJ-CP4IPp3Q.js → pieDiagram-YF2LJOPJ-DuXspLPW.js} +1 -1
- package/dist/assets/{quadrantDiagram-OS5C2QUG-baFc-DtQ.js → quadrantDiagram-OS5C2QUG-DnngSA8p.js} +1 -1
- package/dist/assets/{requirementDiagram-MIRIMTAZ-BTWwNcnt.js → requirementDiagram-MIRIMTAZ-ChJE8XYc.js} +1 -1
- package/dist/assets/{sankeyDiagram-Y46BX6SQ-CnrACC1i.js → sankeyDiagram-Y46BX6SQ-BlkQKMk0.js} +1 -1
- package/dist/assets/{sequenceDiagram-G6AWOVSC-Ct5WkHJl.js → sequenceDiagram-G6AWOVSC-BvYCRnWL.js} +1 -1
- package/dist/assets/{stateDiagram-MAYHULR4-DeZHZmIy.js → stateDiagram-MAYHULR4-Cb6BiEq_.js} +1 -1
- package/dist/assets/stateDiagram-v2-4JROLMXI-31Hvt1M0.js +1 -0
- package/dist/assets/{timeline-definition-U7ZMHBDA-BZfUE50X.js → timeline-definition-U7ZMHBDA-w1-dGiEP.js} +1 -1
- package/dist/assets/{xychartDiagram-6QU3TZC5-DV6BFyHM.js → xychartDiagram-6QU3TZC5-B85iIKzT.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/__stubs__/ccusage-codex.js +8 -0
- package/dist-server/app.module.js +2 -0
- package/dist-server/common/limits/defaults.js +3 -3
- package/dist-server/domain/agent/agent.service.js +11 -0
- package/dist-server/domain/cli/cli.service.js +3 -3
- package/dist-server/domain/cron/cron.service.js +5 -1
- package/dist-server/domain/doc/doc.controller.js +22 -0
- package/dist-server/domain/doc/doc.service.js +42 -10
- package/dist-server/domain/git/git.controller.js +180 -0
- package/dist-server/domain/git/git.module.js +2 -0
- package/dist-server/domain/git/git.service.js +327 -34
- package/dist-server/domain/message/message.service.js +18 -8
- package/dist-server/domain/onboarding/onboarding.service.js +1 -1
- package/dist-server/domain/onboarding/presets/custom.js +2 -2
- package/dist-server/domain/onboarding/presets/dev-team.js +7 -7
- package/dist-server/domain/onboarding/presets/marketing-team.js +5 -5
- package/dist-server/domain/onboarding/presets/planning-team.js +5 -5
- package/dist-server/domain/onboarding/presets/solo.js +3 -3
- package/dist-server/domain/settings/settings.module.js +2 -0
- package/dist-server/domain/settings/settings.service.js +75 -8
- package/dist-server/domain/task/task.service.js +7 -7
- package/dist-server/domain/thread/thread.service.js +2 -1
- package/dist-server/domain/usage/usage.controller.js +30 -0
- package/dist-server/domain/usage/usage.service.js +212 -0
- package/dist-server/domain/wi/wi.controller.js +43 -0
- package/dist-server/domain/wi/wi.module.js +22 -0
- package/dist-server/domain/wi/wi.service.js +110 -0
- package/dist-server/domain/wi/wi.types.js +2 -0
- package/dist-server/domain/workspace/workspace.service.js +4 -1
- package/dist-server/modules/crewx-pool.service.js +194 -53
- package/dist-server/repository/task.repository.js +5 -1
- package/package.json +16 -14
- package/packages/cli/dist/commands/agent.js +1 -1
- package/packages/cli/dist/commands/init.d.ts +2 -0
- package/packages/cli/dist/commands/init.js +198 -6
- package/packages/cli/package.json +3 -2
- package/dist/assets/PromptTab-HhD4MQcW.js +0 -37
- package/dist/assets/channel-ClmdPiZd.js +0 -1
- package/dist/assets/classDiagram-LNE6IOMH-Q0hRWLPF.js +0 -1
- package/dist/assets/classDiagram-v2-MQ7JQ4JX-Q0hRWLPF.js +0 -1
- package/dist/assets/main-AqROOc4i.css +0 -10
- package/dist/assets/main-DWCTap27.js +0 -1289
- package/dist/assets/stateDiagram-v2-4JROLMXI-DVsMMyDB.js +0 -1
|
@@ -27,6 +27,7 @@ const workspace_event_bus_js_1 = require("../domain/events/workspace-event.bus.j
|
|
|
27
27
|
* 2. CREWX_CONFIG env var (fallback when no workspace yaml found)
|
|
28
28
|
*
|
|
29
29
|
* Same workspace path → same Crewx instance (Map cache).
|
|
30
|
+
* Instances are lazily evicted when crewx.yaml mtime+size changes.
|
|
30
31
|
* All instances are closed on application shutdown.
|
|
31
32
|
*
|
|
32
33
|
* Lifecycle: NestJS calls onApplicationShutdown() automatically.
|
|
@@ -36,6 +37,8 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
36
37
|
eventBus;
|
|
37
38
|
pool = new Map();
|
|
38
39
|
poolMeta = new Map();
|
|
40
|
+
/** Orphaned metas: evicted while tasks were in-flight. Keyed by unique stamp. */
|
|
41
|
+
orphanedMetas = new Set();
|
|
39
42
|
constructor(eventBus) {
|
|
40
43
|
this.eventBus = eventBus;
|
|
41
44
|
}
|
|
@@ -50,6 +53,23 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
50
53
|
}
|
|
51
54
|
return this.getForPath((0, workspace_context_store_js_1.getWorkspacePath)());
|
|
52
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve the crewx.yaml config path for a workspace WITHOUT creating or
|
|
58
|
+
* caching a Crewx instance. Resolution policy (mirrors getForPath):
|
|
59
|
+
* 1. CREWX_CONFIG env → single shared file (pin)
|
|
60
|
+
* 2. <ws>/crewx.yaml → <ws>/crewx.yml (first existing)
|
|
61
|
+
* 3. CREWX_CONFIG fallback (undefined when neither yaml exists)
|
|
62
|
+
*/
|
|
63
|
+
resolveConfigPath(workspacePath) {
|
|
64
|
+
const configPath = process.env.CREWX_CONFIG;
|
|
65
|
+
if (configPath)
|
|
66
|
+
return configPath;
|
|
67
|
+
const candidates = [
|
|
68
|
+
(0, path_1.join)(workspacePath, 'crewx.yaml'),
|
|
69
|
+
(0, path_1.join)(workspacePath, 'crewx.yml'),
|
|
70
|
+
];
|
|
71
|
+
return candidates.find((p) => (0, fs_1.existsSync)(p)) ?? process.env.CREWX_CONFIG;
|
|
72
|
+
}
|
|
53
73
|
/**
|
|
54
74
|
* Return the Crewx instance for an explicit workspace path.
|
|
55
75
|
* When CREWX_CONFIG is set, all paths resolve to a single shared instance
|
|
@@ -60,18 +80,71 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
60
80
|
if (configPath) {
|
|
61
81
|
return this.getOrCreate('__crewx_config__', configPath);
|
|
62
82
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
return this.getOrCreate(workspacePath, this.resolveConfigPath(workspacePath));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check whether the cached instance for the given key is stale relative
|
|
87
|
+
* to the config file on disk. Returns false when statSync fails (treat as
|
|
88
|
+
* unchanged) or when no entry exists in the pool.
|
|
89
|
+
*
|
|
90
|
+
* Public so that AGT.LIST can share the staleness judgement without
|
|
91
|
+
* triggering expensive instance creation.
|
|
92
|
+
*/
|
|
93
|
+
isStale(key, configPath) {
|
|
94
|
+
if (!configPath)
|
|
95
|
+
return false;
|
|
96
|
+
const meta = this.poolMeta.get(key);
|
|
97
|
+
if (!meta)
|
|
98
|
+
return false;
|
|
99
|
+
const current = this.readFileStat(configPath);
|
|
100
|
+
if (!current)
|
|
101
|
+
return false; // statSync failed → treat as unchanged
|
|
102
|
+
return current.mtimeMs !== meta.fileStat.mtimeMs || current.size !== meta.fileStat.size;
|
|
70
103
|
}
|
|
71
104
|
invalidate(workspacePath) {
|
|
72
105
|
const key = process.env.CREWX_CONFIG ? '__crewx_config__' : workspacePath;
|
|
73
106
|
this.evict(key);
|
|
74
107
|
}
|
|
108
|
+
readFileStat(filePath) {
|
|
109
|
+
try {
|
|
110
|
+
const s = (0, fs_1.statSync)(filePath);
|
|
111
|
+
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// File temporarily unavailable (editor rename, atomic write). Treat as unchanged.
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Lazy evict: remove from pool Map immediately so the next request starts
|
|
120
|
+
* fresh, but defer unsub + close() on the old instance until all in-flight
|
|
121
|
+
* tasks complete (refcount reaches 0).
|
|
122
|
+
*
|
|
123
|
+
* We intentionally keep event listeners alive on the orphaned instance so
|
|
124
|
+
* that task:end can decrement refcount and trigger close when it hits 0.
|
|
125
|
+
*/
|
|
126
|
+
lazyEvict(key) {
|
|
127
|
+
const meta = this.poolMeta.get(key);
|
|
128
|
+
const existing = this.pool.get(key);
|
|
129
|
+
// Remove from active maps so next getOrCreate starts fresh.
|
|
130
|
+
this.pool.delete(key);
|
|
131
|
+
this.poolMeta.delete(key);
|
|
132
|
+
if (!existing)
|
|
133
|
+
return;
|
|
134
|
+
if (!meta || meta.refcount <= 0) {
|
|
135
|
+
// No in-flight tasks — unsub and close immediately.
|
|
136
|
+
meta?.unsub();
|
|
137
|
+
existing.then((c) => c.close()).catch(() => { });
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// In-flight tasks: keep event listeners alive so task:end can fire.
|
|
141
|
+
// Mark meta as orphaned so task:end can find it.
|
|
142
|
+
meta.orphanedInstance = existing;
|
|
143
|
+
this.orphanedMetas.add(meta);
|
|
144
|
+
// checkOrphanedClose is called from task:end handler via orphanedMetas.
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Explicit (immediate) evict — used by invalidate() triggered by UI writes. */
|
|
75
148
|
evict(key) {
|
|
76
149
|
const meta = this.poolMeta.get(key);
|
|
77
150
|
if (meta) {
|
|
@@ -85,14 +158,54 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
85
158
|
}
|
|
86
159
|
}
|
|
87
160
|
getOrCreate(key, configPath) {
|
|
161
|
+
// Check staleness for existing entry — evict lazily if config changed.
|
|
162
|
+
if (this.pool.has(key) && configPath) {
|
|
163
|
+
if (this.isStale(key, configPath)) {
|
|
164
|
+
this.lazyEvict(key);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
88
167
|
if (!this.pool.has(key)) {
|
|
168
|
+
// Capture mtime BEFORE createServerCrewx to prevent TOCTOU self-perpetuation.
|
|
169
|
+
const capturedStat = configPath
|
|
170
|
+
? (this.readFileStat(configPath) ?? { mtimeMs: 0, size: 0 })
|
|
171
|
+
: { mtimeMs: 0, size: 0 };
|
|
89
172
|
const instancePromise = (0, crewx_server_js_1.createServerCrewx)(configPath, this.eventBus);
|
|
90
173
|
this.pool.set(key, instancePromise);
|
|
91
174
|
// Wire up SDK event subscriptions after the instance resolves.
|
|
92
|
-
instancePromise
|
|
175
|
+
instancePromise
|
|
176
|
+
.then((crewx) => {
|
|
177
|
+
try {
|
|
178
|
+
this.wireEvents(key, crewx, capturedStat);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// wireEvents failed (e.g. crewx.on not available in tests/stubs).
|
|
182
|
+
// Ensure poolMeta is at least set so shutdown cleanup works.
|
|
183
|
+
if (!this.poolMeta.has(key)) {
|
|
184
|
+
this.poolMeta.set(key, {
|
|
185
|
+
unsub: () => { },
|
|
186
|
+
poolKey: key,
|
|
187
|
+
fileStat: capturedStat,
|
|
188
|
+
refcount: 0,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
.catch(() => {
|
|
194
|
+
// If creation failed, remove from pool so a subsequent request retries.
|
|
195
|
+
this.pool.delete(key);
|
|
196
|
+
this.poolMeta.delete(key);
|
|
197
|
+
});
|
|
93
198
|
}
|
|
94
199
|
return this.pool.get(key);
|
|
95
200
|
}
|
|
201
|
+
checkOrphanedClose(meta) {
|
|
202
|
+
if (meta.refcount <= 0 && meta.orphanedInstance) {
|
|
203
|
+
// All in-flight tasks have finished — safe to unsub listeners and close.
|
|
204
|
+
meta.unsub();
|
|
205
|
+
meta.orphanedInstance.then((c) => c.close()).catch(() => { });
|
|
206
|
+
this.orphanedMetas.delete(meta);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
96
209
|
/**
|
|
97
210
|
* Subscribe to task:start / task:end / task:output on a newly created Crewx instance and
|
|
98
211
|
* fan-out task.created / task.updated / task.logged events to WorkspaceEventBus.
|
|
@@ -100,9 +213,7 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
100
213
|
* threadId for task.updated is read from task:end metadata (propagated by SDK from task:start).
|
|
101
214
|
* threadId for task.logged is enriched from threadIdByTask (task:output has no threadId).
|
|
102
215
|
*/
|
|
103
|
-
wireEvents(key, crewx) {
|
|
104
|
-
if (!this.eventBus)
|
|
105
|
-
return;
|
|
216
|
+
wireEvents(key, crewx, capturedStat) {
|
|
106
217
|
const bus = this.eventBus;
|
|
107
218
|
const wsId = crewx.workspaceId;
|
|
108
219
|
// task.logged: taskId → threadId (populated on task:start, cleared on task:end)
|
|
@@ -112,25 +223,39 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
112
223
|
const THROTTLE_MS = 200;
|
|
113
224
|
// Hard cap: evict oldest entry when exceeded (guards against missed task:end on crash)
|
|
114
225
|
const MAX_TASK_ENTRIES = 500;
|
|
226
|
+
// Create meta object first so event handlers can reference it directly
|
|
227
|
+
// (avoids stale poolMeta.get(key) after a lazy evict replaces the entry).
|
|
228
|
+
const meta = {
|
|
229
|
+
unsub: () => { }, // filled in below after handlers are registered
|
|
230
|
+
poolKey: key,
|
|
231
|
+
fileStat: capturedStat,
|
|
232
|
+
refcount: 0,
|
|
233
|
+
};
|
|
115
234
|
const unsubStart = crewx.on('task:start', (e) => {
|
|
235
|
+
// Increment refcount directly on the captured meta (not via Map lookup).
|
|
236
|
+
meta.refcount++;
|
|
116
237
|
threadIdByTask.set(e.traceId, e.threadId ?? '');
|
|
117
238
|
if (threadIdByTask.size > MAX_TASK_ENTRIES) {
|
|
118
239
|
const oldest = threadIdByTask.keys().next().value;
|
|
119
240
|
if (oldest !== undefined)
|
|
120
241
|
threadIdByTask.delete(oldest);
|
|
121
242
|
}
|
|
122
|
-
bus
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
243
|
+
if (bus) {
|
|
244
|
+
bus.emit(wsId, {
|
|
245
|
+
type: 'task.created',
|
|
246
|
+
data: {
|
|
247
|
+
taskId: e.traceId,
|
|
248
|
+
threadId: e.threadId ?? '',
|
|
249
|
+
agent: e.agentRef,
|
|
250
|
+
status: 'running',
|
|
251
|
+
updatedAt: e.timestamp.toISOString(),
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
}
|
|
132
255
|
});
|
|
133
256
|
const unsubOutput = crewx.on('task:output', (e) => {
|
|
257
|
+
if (!bus)
|
|
258
|
+
return;
|
|
134
259
|
const taskId = e.traceId;
|
|
135
260
|
if (throttleByTask.has(taskId))
|
|
136
261
|
return; // absorb within 200ms window
|
|
@@ -145,42 +270,50 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
145
270
|
});
|
|
146
271
|
const unsubEnd = crewx.on('task:end', (e) => {
|
|
147
272
|
const taskId = e.traceId;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
273
|
+
if (bus) {
|
|
274
|
+
// flush pending task.logged before emitting task.updated
|
|
275
|
+
const timer = throttleByTask.get(taskId);
|
|
276
|
+
if (timer !== undefined) {
|
|
277
|
+
clearTimeout(timer);
|
|
278
|
+
throttleByTask.delete(taskId);
|
|
279
|
+
bus.emit(wsId, {
|
|
280
|
+
type: 'task.logged',
|
|
281
|
+
data: { taskId, threadId: threadIdByTask.get(taskId) ?? '' },
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
threadIdByTask.delete(taskId);
|
|
285
|
+
const threadId = e.metadata?.threadId ?? '';
|
|
153
286
|
bus.emit(wsId, {
|
|
154
|
-
type: 'task.
|
|
155
|
-
data: {
|
|
287
|
+
type: 'task.updated',
|
|
288
|
+
data: {
|
|
289
|
+
taskId: e.traceId,
|
|
290
|
+
threadId,
|
|
291
|
+
agent: e.agentRef,
|
|
292
|
+
status: e.error ? 'failed' : 'success',
|
|
293
|
+
...(e.error ? { error: e.error.message } : {}),
|
|
294
|
+
...(e.costUsd != null ? { costUsd: e.costUsd } : {}),
|
|
295
|
+
updatedAt: e.timestamp.toISOString(),
|
|
296
|
+
},
|
|
156
297
|
});
|
|
157
298
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
threadId,
|
|
165
|
-
agent: e.agentRef,
|
|
166
|
-
status: e.error ? 'failed' : 'success',
|
|
167
|
-
...(e.error ? { error: e.error.message } : {}),
|
|
168
|
-
...(e.costUsd != null ? { costUsd: e.costUsd } : {}),
|
|
169
|
-
updatedAt: e.timestamp.toISOString(),
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
this.poolMeta.set(key, {
|
|
174
|
-
unsub: () => {
|
|
175
|
-
unsubStart();
|
|
176
|
-
unsubOutput();
|
|
177
|
-
unsubEnd();
|
|
178
|
-
for (const t of throttleByTask.values())
|
|
179
|
-
clearTimeout(t);
|
|
180
|
-
throttleByTask.clear();
|
|
181
|
-
threadIdByTask.clear();
|
|
182
|
-
},
|
|
299
|
+
// Decrement refcount on the captured meta directly.
|
|
300
|
+
meta.refcount = Math.max(0, meta.refcount - 1);
|
|
301
|
+
// If this meta has been orphaned (lazy evict), trigger deferred close.
|
|
302
|
+
if (meta.orphanedInstance) {
|
|
303
|
+
this.checkOrphanedClose(meta);
|
|
304
|
+
}
|
|
183
305
|
});
|
|
306
|
+
// Wire unsub into the meta object now that handlers are registered.
|
|
307
|
+
meta.unsub = () => {
|
|
308
|
+
unsubStart();
|
|
309
|
+
unsubOutput();
|
|
310
|
+
unsubEnd();
|
|
311
|
+
for (const t of throttleByTask.values())
|
|
312
|
+
clearTimeout(t);
|
|
313
|
+
throttleByTask.clear();
|
|
314
|
+
threadIdByTask.clear();
|
|
315
|
+
};
|
|
316
|
+
this.poolMeta.set(key, meta);
|
|
184
317
|
}
|
|
185
318
|
async onApplicationShutdown() {
|
|
186
319
|
// Unsubscribe all SDK listeners before closing instances.
|
|
@@ -188,6 +321,14 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
188
321
|
meta.unsub();
|
|
189
322
|
}
|
|
190
323
|
this.poolMeta.clear();
|
|
324
|
+
// Unsub and close any orphaned instances (deferred close from lazy evict).
|
|
325
|
+
for (const orphan of this.orphanedMetas) {
|
|
326
|
+
orphan.unsub();
|
|
327
|
+
if (orphan.orphanedInstance) {
|
|
328
|
+
orphan.orphanedInstance.then((c) => c.close()).catch(() => { });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
this.orphanedMetas.clear();
|
|
191
332
|
await Promise.all([...this.pool.values()].map((p) => p.then((c) => c.close()).catch(() => { })));
|
|
192
333
|
}
|
|
193
334
|
};
|
|
@@ -135,7 +135,7 @@ let TaskRepository = class TaskRepository extends base_sqlite_repository_js_1.Ba
|
|
|
135
135
|
provider: 'cli',
|
|
136
136
|
input_tokens: r.inputTokens,
|
|
137
137
|
output_tokens: r.outputTokens,
|
|
138
|
-
total_tokens: r.
|
|
138
|
+
total_tokens: r.totalTokens,
|
|
139
139
|
total_cost: r.costUsd,
|
|
140
140
|
task_count: r.totalTasks,
|
|
141
141
|
active_duration_ms: 0,
|
|
@@ -153,6 +153,10 @@ let TaskRepository = class TaskRepository extends base_sqlite_repository_js_1.Ba
|
|
|
153
153
|
output_tokens: r.outputTokens,
|
|
154
154
|
}));
|
|
155
155
|
}
|
|
156
|
+
/** USG.PROVIDER: Aggregate per-provider usage within a date range. */
|
|
157
|
+
getProviderUsage(from, to, workspace) {
|
|
158
|
+
return this.sdk.getProviderUsage(from, to, workspace);
|
|
159
|
+
}
|
|
156
160
|
/** Find a task by id with workspace isolation for stop operation. */
|
|
157
161
|
findTaskForStop(taskId, workspaceId) {
|
|
158
162
|
const row = this.sdk.findTaskForStop(taskId, workspaceId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crewx",
|
|
3
|
-
"version": "0.8.9-rc.
|
|
3
|
+
"version": "0.8.9-rc.21",
|
|
4
4
|
"description": "CrewX — AI agent team dashboard with Electron UI and CLI (Web + Electron + Global CLI)",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"class-validator": "0.15.1",
|
|
51
51
|
"cookie-parser": "1.4.7",
|
|
52
52
|
"date-fns": "^4.3.0",
|
|
53
|
+
"dom-anchor-text-position": "^5.0.0",
|
|
54
|
+
"dom-anchor-text-quote": "^4.0.2",
|
|
53
55
|
"dotenv": "17.2.3",
|
|
54
56
|
"elkjs": "^0.11.1",
|
|
55
57
|
"execa": "8.0.1",
|
|
@@ -78,20 +80,20 @@
|
|
|
78
80
|
"wink-nlp-utils": "2.1.0",
|
|
79
81
|
"yargs": "17.7.0",
|
|
80
82
|
"zod": "3.25.76",
|
|
81
|
-
"@crewx/cli": "0.8.9-rc.
|
|
82
|
-
"@crewx/
|
|
83
|
-
"@crewx/
|
|
84
|
-
"@crewx/dreaming": "0.1.0",
|
|
85
|
-
"@crewx/
|
|
86
|
-
"@crewx/
|
|
87
|
-
"@crewx/
|
|
88
|
-
"@crewx/
|
|
89
|
-
"@crewx/
|
|
90
|
-
"@crewx/
|
|
91
|
-
"@crewx/
|
|
92
|
-
"@crewx/workflow": "0.3.22",
|
|
83
|
+
"@crewx/cli": "0.8.9-rc.21",
|
|
84
|
+
"@crewx/cron": "0.1.10-rc.55",
|
|
85
|
+
"@crewx/chromex": "0.1.0-rc.57",
|
|
86
|
+
"@crewx/dreaming": "0.1.0-rc.39",
|
|
87
|
+
"@crewx/doc": "0.1.9-rc.19",
|
|
88
|
+
"@crewx/knowledge-core": "0.1.17-rc.16",
|
|
89
|
+
"@crewx/sdk": "0.8.9-rc.21",
|
|
90
|
+
"@crewx/memory": "0.1.23-rc.37",
|
|
91
|
+
"@crewx/search": "0.1.10-rc.20",
|
|
92
|
+
"@crewx/shared": "0.0.6-rc.8",
|
|
93
|
+
"@crewx/wbs": "0.1.10-rc.50",
|
|
93
94
|
"@crewx/skill": "0.1.20",
|
|
94
|
-
"@crewx/
|
|
95
|
+
"@crewx/workflow": "0.3.22-rc.37",
|
|
96
|
+
"@crewx/wi": "0.1.10-rc.21"
|
|
95
97
|
},
|
|
96
98
|
"devDependencies": {
|
|
97
99
|
"@ccusage/codex": "0.0.1",
|
|
@@ -237,7 +237,7 @@ Examples:
|
|
|
237
237
|
crewx agent
|
|
238
238
|
crewx agent ls
|
|
239
239
|
crewx agent ls --role=PM
|
|
240
|
-
crewx agent ls --team="CrewX Core
|
|
240
|
+
crewx agent ls --team="CrewX Core Team"
|
|
241
241
|
crewx agent ls --provider=claude
|
|
242
242
|
crewx agent ls --role=PM --provider=claude
|
|
243
243
|
crewx agent ls --role=PM,general
|
|
@@ -13,6 +13,8 @@ export interface InitResult {
|
|
|
13
13
|
skippedReason?: 'yaml-exists';
|
|
14
14
|
workspaceId?: string;
|
|
15
15
|
slug?: string;
|
|
16
|
+
statusLineConfigured?: boolean;
|
|
17
|
+
statusLineSkipped?: boolean;
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Programmatic init — creates crewx.yaml, support dirs, and optionally installs hooks.
|