observal-pi 0.1.0 → 1.1.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/README.md +7 -7
- package/extensions/observal.ts +9 -6
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ pi install npm:observal-pi
|
|
|
15
15
|
|
|
16
16
|
## Prerequisites
|
|
17
17
|
|
|
18
|
-
1. An Observal account
|
|
18
|
+
1. An Observal account - run `observal auth login` to authenticate
|
|
19
19
|
2. Pi installed (`>=0.74.0`)
|
|
20
20
|
|
|
21
21
|
## What it does
|
|
@@ -35,11 +35,11 @@ pi install npm:observal-pi
|
|
|
35
35
|
|
|
36
36
|
## Design
|
|
37
37
|
|
|
38
|
-
- **Zero dependencies**
|
|
39
|
-
- **Fail-open**
|
|
40
|
-
- **5s timeout**
|
|
41
|
-
- **Chunked uploads**
|
|
42
|
-
- **Dedup-safe**
|
|
38
|
+
- **Zero dependencies** - only `node:*` built-ins
|
|
39
|
+
- **Fail-open** - never throws, never crashes pi. If the server is unreachable, pi continues normally
|
|
40
|
+
- **5s timeout** - all HTTP calls abort after 5 seconds
|
|
41
|
+
- **Chunked uploads** - batches of 500 lines max per request
|
|
42
|
+
- **Dedup-safe** - server deduplicates by `(session_id, line_offset, line_hash)`
|
|
43
43
|
|
|
44
44
|
## Configuration
|
|
45
45
|
|
|
@@ -56,4 +56,4 @@ Sync state is tracked in `~/.observal/sync_state.json` (per-session byte offsets
|
|
|
56
56
|
|
|
57
57
|
## License
|
|
58
58
|
|
|
59
|
-
AGPL-3.0-only
|
|
59
|
+
AGPL-3.0-only - see [LICENSE](./LICENSE)
|
package/extensions/observal.ts
CHANGED
|
@@ -34,6 +34,7 @@ interface CursorEntry {
|
|
|
34
34
|
offset: number;
|
|
35
35
|
line_count: number;
|
|
36
36
|
finalized?: boolean;
|
|
37
|
+
last_pushed_at?: number;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
interface ObservalState {
|
|
@@ -159,7 +160,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
159
160
|
if (fs.existsSync(SYNC_STATE_PATH)) {
|
|
160
161
|
data = JSON.parse(fs.readFileSync(SYNC_STATE_PATH, "utf-8"));
|
|
161
162
|
}
|
|
162
|
-
data[sessionId] = { offset, line_count: lineCount, finalized };
|
|
163
|
+
data[sessionId] = { offset, line_count: lineCount, finalized, last_pushed_at: Date.now() };
|
|
163
164
|
fs.writeFileSync(SYNC_STATE_PATH, JSON.stringify(data, null, 2));
|
|
164
165
|
} catch {
|
|
165
166
|
// Fail-open
|
|
@@ -266,7 +267,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
266
267
|
(res) => {
|
|
267
268
|
clearTimeout(timer);
|
|
268
269
|
res.resume(); // drain response
|
|
269
|
-
resolve(res.statusCode
|
|
270
|
+
resolve(res.statusCode! >= 200 && res.statusCode! < 300);
|
|
270
271
|
},
|
|
271
272
|
);
|
|
272
273
|
|
|
@@ -290,10 +291,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
290
291
|
fs.readFileSync(SYNC_STATE_PATH, "utf-8"),
|
|
291
292
|
);
|
|
292
293
|
|
|
294
|
+
// Use sessionManager to resolve session directory when available,
|
|
295
|
+
// falling back to the conventional path layout.
|
|
296
|
+
const sessionsDir = ctx.sessionManager.getSessionsDir?.()
|
|
297
|
+
?? path.join(os.homedir(), ".pi", "agent", "sessions");
|
|
293
298
|
const cwd = ctx.cwd;
|
|
294
299
|
const projectKey = cwd.replace(/\//g, "-");
|
|
295
|
-
const sessionsDir = path.join(os.homedir(), ".pi", "agent", "sessions");
|
|
296
|
-
// Pi uses --<path>-- format for directory names
|
|
297
300
|
const fullDir = path.join(sessionsDir, `-${projectKey}-`);
|
|
298
301
|
|
|
299
302
|
if (!fs.existsSync(fullDir)) return;
|
|
@@ -369,8 +372,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
369
372
|
const entries = Object.entries(data);
|
|
370
373
|
if (entries.length <= 50) return; // No pruning needed
|
|
371
374
|
|
|
372
|
-
// Keep only the 50 most recent entries (by
|
|
373
|
-
const sorted = entries.sort((a, b) => b[1].
|
|
375
|
+
// Keep only the 50 most recent entries (by last push time, falling back to offset)
|
|
376
|
+
const sorted = entries.sort((a, b) => (b[1].last_pushed_at ?? 0) - (a[1].last_pushed_at ?? 0));
|
|
374
377
|
const pruned: Record<string, CursorEntry> = {};
|
|
375
378
|
for (const [key, value] of sorted.slice(0, 50)) {
|
|
376
379
|
pruned[key] = value;
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "observal-pi",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Observal session telemetry for Pi — zero-dependency extension that pushes session traces to your Observal server",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"pi-extension",
|
|
8
|
+
"observal",
|
|
9
|
+
"telemetry",
|
|
10
|
+
"tracing"
|
|
11
|
+
],
|
|
6
12
|
"license": "AGPL-3.0-only",
|
|
7
13
|
"repository": {
|
|
8
14
|
"type": "git",
|
|
@@ -10,7 +16,9 @@
|
|
|
10
16
|
"directory": "packages/pi-extension"
|
|
11
17
|
},
|
|
12
18
|
"pi": {
|
|
13
|
-
"extensions": [
|
|
19
|
+
"extensions": [
|
|
20
|
+
"./extensions/observal.ts"
|
|
21
|
+
]
|
|
14
22
|
},
|
|
15
23
|
"peerDependencies": {
|
|
16
24
|
"@earendil-works/pi-coding-agent": ">=0.74.0"
|