pi-crew 0.10.6 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +99 -0
- package/dist/index.mjs +34850 -34889
- package/package.json +170 -169
- package/scripts/watch-agent-transcript.mjs +120 -0
- package/src/extension/crew-input-router.ts +2 -0
- package/src/extension/crew-vibes/config.ts +1 -56
- package/src/extension/crew-vibes/figures.ts +5 -84
- package/src/extension/crew-vibes/font-detect.ts +1 -1
- package/src/extension/crew-vibes/index.ts +36 -328
- package/src/extension/crew-vibes/render.ts +2 -58
- package/src/extension/help.ts +1 -0
- package/src/extension/registration/command-registration.ts +5 -1
- package/src/extension/registration/commands/schedules.ts +212 -0
- package/src/extension/registration/commands/shared.ts +56 -1
- package/src/extension/registration/lifecycle-handlers.ts +16 -2
- package/src/extension/registration/schedule-toast-bridge.ts +109 -0
- package/src/extension/registration/viewers.ts +66 -1
- package/src/extension/team-tool/handle-schedule.ts +120 -1
- package/src/runtime/scheduling/scheduler.ts +120 -6
- package/src/runtime/settings-store.ts +28 -2
- package/src/state/coordination/locks.ts +17 -2
- package/src/ui/agents-jobs-browser.ts +785 -0
- package/src/ui/dashboard-panes/agents-pane.ts +7 -4
- package/src/ui/dashboard-panes/schedules-pane.ts +154 -0
- package/src/ui/inline-panel/crew-editor.ts +19 -6
- package/src/ui/inline-panel/index.ts +6 -0
- package/src/ui/key-utils.ts +3 -0
- package/src/ui/keybinding-map.ts +56 -2
- package/src/ui/mascot.ts +2 -1
- package/src/ui/overlays/agent-picker-overlay.ts +3 -3
- package/src/ui/overlays/confirm-overlay.ts +3 -2
- package/src/ui/overlays/mailbox-compose-overlay.ts +4 -3
- package/src/ui/overlays/mailbox-detail-overlay.ts +3 -3
- package/src/ui/run-dashboard.ts +257 -6
- package/src/ui/settings-overlay.ts +3 -3
- package/src/ui/transcript-viewer.ts +3 -2
- package/src/ui/widget/index.ts +61 -88
- package/src/ui/widget/widget-formatters.ts +7 -4
- package/src/ui/widget/widget-renderer.ts +134 -174
- package/src/ui/widget/widget-types.ts +0 -7
- package/src/utils/relative-time.ts +33 -0
- package/src/extension/crew-vibes/footer.ts +0 -313
- package/src/extension/crew-vibes/speed.ts +0 -286
- package/src/ui/dock-footer.ts +0 -49
package/package.json
CHANGED
|
@@ -1,171 +1,172 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
2
|
+
"name": "pi-crew",
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
|
|
5
|
+
"author": "baphuongna",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.0.0"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/baphuongna/pi-crew.git"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/baphuongna/pi-crew#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/baphuongna/pi-crew/issues"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"bin": {
|
|
20
|
+
"pi-crew": "install.mjs"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"pi-package",
|
|
24
|
+
"pi",
|
|
25
|
+
"pi-coding-agent",
|
|
26
|
+
"teams",
|
|
27
|
+
"agents",
|
|
28
|
+
"multi-agent",
|
|
29
|
+
"orchestration"
|
|
30
|
+
],
|
|
31
|
+
"files": [
|
|
32
|
+
"src/",
|
|
33
|
+
"!src/benchmark",
|
|
34
|
+
"dist/index.mjs",
|
|
35
|
+
"index.ts",
|
|
36
|
+
"install.mjs",
|
|
37
|
+
"agents/",
|
|
38
|
+
"teams/",
|
|
39
|
+
"workflows/",
|
|
40
|
+
"skills/**",
|
|
41
|
+
"themes/",
|
|
42
|
+
"assets/crew-vibes.ttf",
|
|
43
|
+
"scripts/clean-strip-types.mjs",
|
|
44
|
+
"scripts/postinstall.mjs",
|
|
45
|
+
"scripts/install-crew-vibes-font.mjs",
|
|
46
|
+
"scripts/build-crew-vibes-font.py",
|
|
47
|
+
"scripts/pty_probe.py",
|
|
48
|
+
"README.md",
|
|
49
|
+
"AGENTS.md",
|
|
50
|
+
"docs/actions-reference.md",
|
|
51
|
+
"docs/commands-reference.md",
|
|
52
|
+
"docs/troubleshooting.md",
|
|
53
|
+
"docs/usage.md",
|
|
54
|
+
"docs/architecture.md",
|
|
55
|
+
"docs/dynamic-workflows.md",
|
|
56
|
+
"docs/runtime-flow.md",
|
|
57
|
+
"docs/resource-formats.md",
|
|
58
|
+
"docs/publishing.md",
|
|
59
|
+
"docs/migration-v0.4-v0.5.md",
|
|
60
|
+
"tsconfig.json",
|
|
61
|
+
"schema.json",
|
|
62
|
+
"types/",
|
|
63
|
+
"CHANGELOG.md",
|
|
64
|
+
"LICENSE",
|
|
65
|
+
"NOTICE.md",
|
|
66
|
+
"scripts/resource-sampler.mjs",
|
|
67
|
+
"scripts/analyze-run.mjs",
|
|
68
|
+
"scripts/watch-agent-transcript.mjs"
|
|
69
|
+
],
|
|
70
|
+
"scripts": {
|
|
71
|
+
"check": "npm run ci",
|
|
72
|
+
"ci": "npm run check:lockfile-sync && npm run typecheck && npm run lint && npm run format:check && npm run check:conflict-markers && npm run check:decision-drift && npm run check:env-vars && npm run check:event-types && npm run check:lazy-imports && npm run check:wc-gate && npm run check:bundle-staleness && npm run build:bundle && npm run check:bundle-size && npm run test:bundle && npm test && npm pack --dry-run",
|
|
73
|
+
"ci:fast": "npm run check:lockfile-sync && npm run typecheck && npm run lint && npm run format:check && npm run check:conflict-markers && npm run check:decision-drift && npm run check:env-vars && npm run check:event-types && npm run check:lazy-imports && npm run check:wc-gate && npm run check:bundle-staleness && npm run build:bundle && npm run check:bundle-size && npm run test:bundle && npm run test:critical && npm pack --dry-run",
|
|
74
|
+
"check:lockfile-sync": "node scripts/check-lockfile-sync.mjs",
|
|
75
|
+
"check:lazy-imports": "node scripts/check-lazy-imports.mjs",
|
|
76
|
+
"check:bundle-staleness": "node scripts/check-bundle-staleness.mjs",
|
|
77
|
+
"check:bundle-size": "node scripts/check-bundle-size.mjs",
|
|
78
|
+
"check:conflict-markers": "node scripts/check-conflict-markers.mjs",
|
|
79
|
+
"check:decision-drift": "node scripts/check-decision-drift.mjs",
|
|
80
|
+
"check:env-vars": "node scripts/check-env-vars.mjs",
|
|
81
|
+
"check:event-types": "node scripts/check-event-types-registry.mjs",
|
|
82
|
+
"typecheck": "tsc --noEmit && node --experimental-strip-types -e \"await import('./index.ts'); console.log('strip-types import ok')\"",
|
|
83
|
+
"lint": "biome check --linter-enabled=true --formatter-enabled=false --max-diagnostics=50 .",
|
|
84
|
+
"format:check": "biome format .",
|
|
85
|
+
"test": "npm run test:unit && npm run test:integration",
|
|
86
|
+
"test:full": "npm run test:unit && npm run test:integration && npm run test:integration:slow",
|
|
87
|
+
"test:unit": "node scripts/test-runner.mjs --test-concurrency=4 --test-timeout=180000 --test-force-exit 'test/unit/**/*.test.ts'",
|
|
88
|
+
"test:critical": "node scripts/test-runner.mjs --test-concurrency=4 --test-timeout=30000 --test-force-exit test/unit/runtime/broker/crew-broker-handshake.test.ts test/unit/runtime/broker/crew-broker-stale-socket.test.ts test/unit/runtime/broker/crew-broker-feature-flag.test.ts test/unit/runtime/broker/crew-broker-server-gate.test.ts test/unit/runtime/broker/crew-broker-client-fallback.test.ts test/unit/runtime/broker/crew-broker-mailbox-observer.test.ts test/unit/runtime/broker/crew-broker-close-during-reconnect.test.ts test/unit/runtime/broker/crew-broker-steer-dedup.test.ts test/unit/runtime/broker/crew-broker-symlink-steering.test.ts test/unit/ui/keybinding-map.parity.test.ts test/unit/ui/pi-tui-dispatch-probe.test.ts test/unit/utils/session-utils-extract.test.ts test/unit/config/config-schema-sync.test.ts test/unit/runtime/child-pi/child-pi-env-spread.test.ts",
|
|
89
|
+
"test:watch": "node scripts/test-runner.mjs --watch --test-concurrency=4 --test-timeout=30000 'test/unit/**/*.test.ts'",
|
|
90
|
+
"test:integration": "node scripts/test-runner.mjs --test-concurrency=1 --test-timeout=300000 test/integration/*.test.ts",
|
|
91
|
+
"test:integration:slow": "node scripts/test-runner.mjs --test-concurrency=1 --test-timeout=600000 test/integration/slow/*.test.ts",
|
|
92
|
+
"test:system": "node scripts/test-runner.mjs --test-concurrency=1 --test-timeout=180000 --test-force-exit test/system/*.e2e.test.ts",
|
|
93
|
+
"test:smoke": "node scripts/test-runner.mjs --test-concurrency=1 --test-timeout=180000 test/smoke/*.smoke.ts",
|
|
94
|
+
"test:spike": "node scripts/test-runner.mjs --test-concurrency=2 --test-timeout=120000 --test-force-exit test/runtime/scratchpad/*.test.ts",
|
|
95
|
+
"test:functional": "node scripts/test-runner.mjs --test-concurrency=1 --test-timeout=120000 test/functional/pi-crew-functional.test.ts",
|
|
96
|
+
"test:platform": "node scripts/test-runner.mjs --test-concurrency=1 --test-timeout=60000 test/platform/*.test.ts",
|
|
97
|
+
"build:bundle": "node scripts/build-bundle.mjs",
|
|
98
|
+
"test:bundle": "node --experimental-strip-types --test --test-force-exit test/unit/bundle-load.test.ts",
|
|
99
|
+
"watch:bundle": "node scripts/watch-bundle.mjs",
|
|
100
|
+
"build:bundle:once": "node scripts/watch-bundle.mjs --once",
|
|
101
|
+
"dev": "node scripts/dev-runner.mjs",
|
|
102
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
103
|
+
"install:crew-font": "node scripts/install-crew-vibes-font.mjs",
|
|
104
|
+
"build:crew-font": "python scripts/build-crew-vibes-font.py",
|
|
105
|
+
"bench": "node scripts/run-bench.mjs",
|
|
106
|
+
"bench:check": "node scripts/bench-check.mjs",
|
|
107
|
+
"test:new": "node scripts/test-runner.mjs --test-concurrency=4 --test-timeout=30000 'test/unit/**/*.test.ts'",
|
|
108
|
+
"test:changed": "node scripts/test-changed.mjs",
|
|
109
|
+
"bench:capture": "node scripts/run-bench.mjs && node -e \"require('node:fs').copyFileSync('test/bench/results.json','test/bench/baseline.json')\"",
|
|
110
|
+
"profile:startup": "node scripts/profile-startup.mjs",
|
|
111
|
+
"smoke:pi": "pi install .",
|
|
112
|
+
"smoke:release": "node scripts/release-smoke.mjs",
|
|
113
|
+
"prepack": "node scripts/clean-strip-types.mjs",
|
|
114
|
+
"test:fast": "npm run test:critical && npm run test:bundle",
|
|
115
|
+
"check:wc-gate": "node scripts/wc-gate.mjs"
|
|
116
|
+
},
|
|
117
|
+
"exports": {
|
|
118
|
+
"./schema.json": "./schema.json",
|
|
119
|
+
"./workflow": {
|
|
120
|
+
"types": "./types/dwf.d.ts"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"pi": {
|
|
124
|
+
"extensions": [
|
|
125
|
+
"./index.ts"
|
|
126
|
+
],
|
|
127
|
+
"skills": [
|
|
128
|
+
"./skills"
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
"peerDependencies": {
|
|
132
|
+
"@earendil-works/pi-agent-core": "*",
|
|
133
|
+
"@earendil-works/pi-ai": "*",
|
|
134
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
135
|
+
"@earendil-works/pi-tui": "*"
|
|
136
|
+
},
|
|
137
|
+
"dependencies": {
|
|
138
|
+
"@sinclair/typebox": "^0.34.50",
|
|
139
|
+
"acorn": "^8.17.0",
|
|
140
|
+
"ajv": "^8.20.0",
|
|
141
|
+
"cli-highlight": "^2.1.11",
|
|
142
|
+
"diff": "^5.2.0",
|
|
143
|
+
"esbuild": "^0.28.1",
|
|
144
|
+
"jiti": "^2.7.0",
|
|
145
|
+
"yaml": "^2.9.0"
|
|
146
|
+
},
|
|
147
|
+
"devDependencies": {
|
|
148
|
+
"@biomejs/biome": "^2.5.3",
|
|
149
|
+
"@earendil-works/pi-agent-core": "^0.84.0",
|
|
150
|
+
"@earendil-works/pi-ai": "^0.84.0",
|
|
151
|
+
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
152
|
+
"@earendil-works/pi-tui": "^0.84.0",
|
|
153
|
+
"@types/node": "^25.9.5",
|
|
154
|
+
"tsx": "^4.23.0",
|
|
155
|
+
"typescript": "^7.0.2"
|
|
156
|
+
},
|
|
157
|
+
"peerDependenciesMeta": {
|
|
158
|
+
"@earendil-works/pi-agent-core": {
|
|
159
|
+
"optional": true
|
|
160
|
+
},
|
|
161
|
+
"@earendil-works/pi-ai": {
|
|
162
|
+
"optional": true
|
|
163
|
+
},
|
|
164
|
+
"@earendil-works/pi-coding-agent": {
|
|
165
|
+
"optional": true
|
|
166
|
+
},
|
|
167
|
+
"@earendil-works/pi-tui": {
|
|
168
|
+
"optional": true
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"readmeFilename": "README.md"
|
|
171
172
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Watch a pi session transcript (JSONL stream events) and print one readable
|
|
3
|
+
// line per message — the "session subagent" pane companion for the Agents &
|
|
4
|
+
// Jobs browser (`p` key). Unlike `tail -F` on the raw file, every line here is
|
|
5
|
+
// a formatted summary (role badge + preview), so a tmux/herdr pane shows WHAT
|
|
6
|
+
// the agent is doing, not walls of JSON.
|
|
7
|
+
//
|
|
8
|
+
// Usage: node watch-agent-transcript.mjs <transcript.jsonl>
|
|
9
|
+
import { open as openFile } from "node:fs/promises";
|
|
10
|
+
import { basename } from "node:path";
|
|
11
|
+
|
|
12
|
+
const path = process.argv[2];
|
|
13
|
+
if (!path) {
|
|
14
|
+
console.error("usage: node watch-agent-transcript.mjs <transcript.jsonl>");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const WIDTH = Math.min(200, Number(process.env.COLUMNS || 160) || 160);
|
|
19
|
+
|
|
20
|
+
function oneLine(text) {
|
|
21
|
+
return String(text ?? "")
|
|
22
|
+
.replace(/\s+/g, " ")
|
|
23
|
+
.trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function clip(text, max = WIDTH - 10) {
|
|
27
|
+
const s = oneLine(text);
|
|
28
|
+
return s.length > max ? `${s.slice(0, max - 1)}…` : s;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function stamp(iso) {
|
|
32
|
+
const m = /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})/.exec(iso ?? "");
|
|
33
|
+
return m ? `${m[2]}` : "";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function renderEvent(evt) {
|
|
37
|
+
if (evt.type === "session") return "── session start ──";
|
|
38
|
+
if (evt.type === "agent_start") return "── agent start ──";
|
|
39
|
+
if (evt.type !== "message_end" || !evt.message) return null;
|
|
40
|
+
const msg = evt.message;
|
|
41
|
+
const who =
|
|
42
|
+
msg.role === "assistant" ? "🤖" : msg.role === "user" ? "👤" : msg.role === "toolResult" ? "📤" : (msg.role ?? "?");
|
|
43
|
+
const parts = Array.isArray(msg.content) ? msg.content : [{ type: "text", text: String(msg.content ?? "") }];
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const part of parts) {
|
|
46
|
+
if (part.type === "thinking") out.push(`💭 ${clip(part.thinking, 120)}`);
|
|
47
|
+
else if (part.type === "text") out.push(`💬 ${clip(part.text)}`);
|
|
48
|
+
else if (part.type === "tool_use") {
|
|
49
|
+
const input = typeof part.input === "object" ? JSON.stringify(part.input) : String(part.input ?? "");
|
|
50
|
+
out.push(`🔧 ${part.name ?? "tool"} ${clip(input, 100)}`);
|
|
51
|
+
} else if (part.type === "tool_result") {
|
|
52
|
+
const content = typeof part.content === "string" ? part.content : JSON.stringify(part.content ?? "");
|
|
53
|
+
out.push(`📤 ${clip(content, 120)}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (out.length === 0) return null;
|
|
57
|
+
return `${stamp(evt.timestamp ?? msg.timestamp) || " "} ${who} ${out.join(" │ ")}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Clear the pane first — herdr/tmux panes boot via the shell, so the echoed
|
|
61
|
+
// prompt + full node command would otherwise sit above the watcher output.
|
|
62
|
+
process.stdout.write("\x1b[2J\x1b[H");
|
|
63
|
+
console.log(`── watching ${basename(path)} ──`);
|
|
64
|
+
|
|
65
|
+
let offset = 0;
|
|
66
|
+
let buffer = "";
|
|
67
|
+
const POLL_MS = 400;
|
|
68
|
+
let waitingSince = 0;
|
|
69
|
+
let lastWaitingLog = 0;
|
|
70
|
+
|
|
71
|
+
function logWaiting(now) {
|
|
72
|
+
if (!waitingSince) waitingSince = now;
|
|
73
|
+
if (now - lastWaitingLog >= 2500) {
|
|
74
|
+
const waitedS = Math.round((now - waitingSince) / 1000);
|
|
75
|
+
console.log(`… waiting for ${basename(path)} to appear (${waitedS}s)`);
|
|
76
|
+
lastWaitingLog = now;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function pump() {
|
|
81
|
+
try {
|
|
82
|
+
const fh = await openFile(path, "r");
|
|
83
|
+
try {
|
|
84
|
+
const stat = await fh.stat();
|
|
85
|
+
if (waitingSince) {
|
|
86
|
+
console.log(`✓ transcript appeared (waited ${Math.round((Date.now() - waitingSince) / 1000)}s)`);
|
|
87
|
+
waitingSince = 0;
|
|
88
|
+
}
|
|
89
|
+
if (stat.size < offset) offset = 0; // truncated/rotated
|
|
90
|
+
if (stat.size > offset) {
|
|
91
|
+
const len = stat.size - offset;
|
|
92
|
+
const buf = Buffer.alloc(len);
|
|
93
|
+
await fh.read(buf, 0, len, offset);
|
|
94
|
+
offset += len;
|
|
95
|
+
buffer += buf.toString("utf8");
|
|
96
|
+
const lines = buffer.split("\n");
|
|
97
|
+
buffer = lines.pop() ?? "";
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
const trimmed = line.trim();
|
|
100
|
+
if (!trimmed) continue;
|
|
101
|
+
try {
|
|
102
|
+
const rendered = renderEvent(JSON.parse(trimmed));
|
|
103
|
+
if (rendered) console.log(rendered);
|
|
104
|
+
} catch {
|
|
105
|
+
console.log(`· ${clip(trimmed)}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} finally {
|
|
110
|
+
await fh.close();
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// File not there yet (cold-start window): visible waiting state so the
|
|
114
|
+
// pane explains itself instead of looking dead.
|
|
115
|
+
logWaiting(Date.now());
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setInterval(pump, POLL_MS);
|
|
120
|
+
await pump();
|
|
@@ -34,6 +34,8 @@ export const CREW_PHRASES: ReadonlyArray<{ phrase: string; command: string }> =
|
|
|
34
34
|
{ phrase: "crew commands", command: "/team-help" },
|
|
35
35
|
{ phrase: "crew doctor", command: "/team-doctor" },
|
|
36
36
|
{ phrase: "crew diagnose", command: "/team-doctor" },
|
|
37
|
+
{ phrase: "crew schedule", command: "/schedules" },
|
|
38
|
+
{ phrase: "scheduled jobs", command: "/schedules" },
|
|
37
39
|
{ phrase: "teams", command: "/teams" },
|
|
38
40
|
];
|
|
39
41
|
|
|
@@ -4,12 +4,11 @@ import { getCrewEnv } from "../../config/env-vars.ts";
|
|
|
4
4
|
import { hasCrewFontFile, isWebTerminal } from "./font-detect.ts";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Self-contained config for the crew-vibes module (
|
|
7
|
+
* Self-contained config for the crew-vibes module (provider quota).
|
|
8
8
|
* Stored in its own JSON file so it never touches the strict typebox schema
|
|
9
9
|
* used by the rest of pi-crew.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
export const SPEED_STATUS_ID = "pi-crew-speed";
|
|
13
12
|
export const CAPACITY_STATUS_ID = "pi-crew-bar";
|
|
14
13
|
export const PROVIDER_STATUS_ID = "pi-crew-bar";
|
|
15
14
|
|
|
@@ -23,21 +22,6 @@ export function configPath(): string {
|
|
|
23
22
|
|
|
24
23
|
export type TokenDisplay = "off" | "tokens" | "percentage";
|
|
25
24
|
|
|
26
|
-
export interface SpeedConfig {
|
|
27
|
-
enabled: boolean;
|
|
28
|
-
footer: boolean;
|
|
29
|
-
indicator: boolean;
|
|
30
|
-
label: string;
|
|
31
|
-
renderIntervalMs: number;
|
|
32
|
-
slidingWindowMs: number;
|
|
33
|
-
minReliableDurationMs: number;
|
|
34
|
-
maxDisplayTokS: number;
|
|
35
|
-
defaultIntervalMs: number;
|
|
36
|
-
minIntervalMs: number;
|
|
37
|
-
maxIntervalMs: number;
|
|
38
|
-
scale: number;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
25
|
export interface CapacityConfig {
|
|
42
26
|
enabled: boolean;
|
|
43
27
|
tokenDisplay: TokenDisplay;
|
|
@@ -51,26 +35,11 @@ export interface CapacityConfig {
|
|
|
51
35
|
|
|
52
36
|
export interface CrewVibesConfig {
|
|
53
37
|
enabled: boolean;
|
|
54
|
-
speed: SpeedConfig;
|
|
55
38
|
capacity: CapacityConfig;
|
|
56
39
|
}
|
|
57
40
|
|
|
58
41
|
export const DEFAULT_CONFIG: CrewVibesConfig = {
|
|
59
42
|
enabled: true,
|
|
60
|
-
speed: {
|
|
61
|
-
enabled: true,
|
|
62
|
-
footer: false,
|
|
63
|
-
indicator: true,
|
|
64
|
-
label: "tok/s",
|
|
65
|
-
renderIntervalMs: 250,
|
|
66
|
-
slidingWindowMs: 1000,
|
|
67
|
-
minReliableDurationMs: 1000,
|
|
68
|
-
maxDisplayTokS: 500,
|
|
69
|
-
defaultIntervalMs: 167,
|
|
70
|
-
minIntervalMs: 50,
|
|
71
|
-
maxIntervalMs: 250,
|
|
72
|
-
scale: 6000,
|
|
73
|
-
},
|
|
74
43
|
capacity: {
|
|
75
44
|
enabled: true,
|
|
76
45
|
tokenDisplay: "tokens",
|
|
@@ -133,29 +102,6 @@ function tokenDisplayFrom(raw: unknown, fallback: TokenDisplay): TokenDisplay {
|
|
|
133
102
|
return raw === "off" || raw === "tokens" || raw === "percentage" ? raw : fallback;
|
|
134
103
|
}
|
|
135
104
|
|
|
136
|
-
function normalizeSpeed(raw: unknown): SpeedConfig {
|
|
137
|
-
const input = asRecord(raw);
|
|
138
|
-
const speed: SpeedConfig = {
|
|
139
|
-
enabled: boolFrom(input.enabled, DEFAULT_CONFIG.speed.enabled),
|
|
140
|
-
footer: boolFrom(input.footer, DEFAULT_CONFIG.speed.footer),
|
|
141
|
-
indicator: boolFrom(input.indicator, DEFAULT_CONFIG.speed.indicator),
|
|
142
|
-
label: stringFrom(input.label, DEFAULT_CONFIG.speed.label),
|
|
143
|
-
renderIntervalMs: positiveFrom(input.renderIntervalMs, DEFAULT_CONFIG.speed.renderIntervalMs),
|
|
144
|
-
slidingWindowMs: positiveFrom(input.slidingWindowMs, DEFAULT_CONFIG.speed.slidingWindowMs),
|
|
145
|
-
minReliableDurationMs: positiveFrom(input.minReliableDurationMs, DEFAULT_CONFIG.speed.minReliableDurationMs),
|
|
146
|
-
maxDisplayTokS: positiveFrom(input.maxDisplayTokS, DEFAULT_CONFIG.speed.maxDisplayTokS),
|
|
147
|
-
defaultIntervalMs: positiveFrom(input.defaultIntervalMs, DEFAULT_CONFIG.speed.defaultIntervalMs),
|
|
148
|
-
minIntervalMs: positiveFrom(input.minIntervalMs, DEFAULT_CONFIG.speed.minIntervalMs),
|
|
149
|
-
maxIntervalMs: positiveFrom(input.maxIntervalMs, DEFAULT_CONFIG.speed.maxIntervalMs),
|
|
150
|
-
scale: positiveFrom(input.scale, DEFAULT_CONFIG.speed.scale),
|
|
151
|
-
};
|
|
152
|
-
if (speed.minIntervalMs > speed.maxIntervalMs) {
|
|
153
|
-
speed.minIntervalMs = DEFAULT_CONFIG.speed.minIntervalMs;
|
|
154
|
-
speed.maxIntervalMs = DEFAULT_CONFIG.speed.maxIntervalMs;
|
|
155
|
-
}
|
|
156
|
-
return speed;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
105
|
function normalizeCapacity(raw: unknown): CapacityConfig {
|
|
160
106
|
const input = asRecord(raw);
|
|
161
107
|
return {
|
|
@@ -174,7 +120,6 @@ export function normalizeConfig(raw: unknown): CrewVibesConfig {
|
|
|
174
120
|
const input = asRecord(raw);
|
|
175
121
|
return {
|
|
176
122
|
enabled: boolFrom(input.enabled, DEFAULT_CONFIG.enabled),
|
|
177
|
-
speed: normalizeSpeed(input.speed),
|
|
178
123
|
capacity: normalizeCapacity(input.capacity),
|
|
179
124
|
};
|
|
180
125
|
}
|