@manyoars/oars 0.3.0 → 0.4.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.
Files changed (161) hide show
  1. package/README.md +44 -29
  2. package/dist/cli.d.ts +4 -1
  3. package/dist/cli.js +102 -32
  4. package/dist/commands/login.d.ts +1 -1
  5. package/dist/commands/login.js +1 -1
  6. package/dist/gate/hook-main.js +13 -1
  7. package/dist/gate/hook-process.d.ts +11 -1
  8. package/dist/gate/hook-process.js +50 -2
  9. package/dist/gate/hook-record.d.ts +3 -0
  10. package/dist/gate/hook-record.js +8 -0
  11. package/dist/gate/hook.d.ts +2 -0
  12. package/dist/gate/hook.js +3 -0
  13. package/dist/gate/replay.js +2 -5
  14. package/dist/gate/report.js +1 -1
  15. package/dist/gate/transcripts.js +1 -1
  16. package/dist/hook-bin.js +27 -2
  17. package/dist/issuer.d.ts +7 -9
  18. package/dist/issuer.js +10 -32
  19. package/dist/main.js +3 -0
  20. package/dist/sessions/actions.js +78 -0
  21. package/dist/sessions/allow.d.ts +10 -1
  22. package/dist/sessions/allow.js +29 -9
  23. package/dist/sessions/baseline.d.ts +1 -1
  24. package/dist/sessions/baseline.js +6 -4
  25. package/dist/sessions/call-shape.d.ts +19 -0
  26. package/dist/sessions/call-shape.js +85 -0
  27. package/dist/sessions/cell-increments.d.ts +5 -1
  28. package/dist/sessions/cell-increments.js +84 -50
  29. package/dist/sessions/cells.d.ts +33 -3
  30. package/dist/sessions/cells.js +89 -9
  31. package/dist/sessions/codex.js +93 -27
  32. package/dist/sessions/command.d.ts +11 -1
  33. package/dist/sessions/command.js +64 -18
  34. package/dist/sessions/coverage.d.ts +19 -0
  35. package/dist/sessions/coverage.js +56 -0
  36. package/dist/sessions/cursor.js +2 -2
  37. package/dist/sessions/derive.js +72 -6
  38. package/dist/sessions/exclusion.d.ts +6 -0
  39. package/dist/sessions/exclusion.js +8 -0
  40. package/dist/sessions/extract.d.ts +4 -2
  41. package/dist/sessions/extract.js +58 -26
  42. package/dist/sessions/hook-calls.js +1 -1
  43. package/dist/sessions/hook-index.d.ts +14 -1
  44. package/dist/sessions/hook-index.js +10 -3
  45. package/dist/sessions/hooks.d.ts +5 -1
  46. package/dist/sessions/hooks.js +133 -22
  47. package/dist/sessions/identity.d.ts +8 -3
  48. package/dist/sessions/identity.js +45 -15
  49. package/dist/sessions/install.d.ts +18 -2
  50. package/dist/sessions/install.js +53 -8
  51. package/dist/sessions/mcp.d.ts +3 -2
  52. package/dist/sessions/mcp.js +8 -4
  53. package/dist/sessions/motif.d.ts +2 -1
  54. package/dist/sessions/motif.js +2 -2
  55. package/dist/sessions/opencode.d.ts +33 -0
  56. package/dist/sessions/opencode.js +319 -0
  57. package/dist/sessions/otel.d.ts +60 -0
  58. package/dist/sessions/otel.js +494 -0
  59. package/dist/sessions/package-books.d.ts +48 -0
  60. package/dist/sessions/package-books.js +120 -0
  61. package/dist/sessions/package-resolve.d.ts +2 -0
  62. package/dist/sessions/package-resolve.js +3 -1
  63. package/dist/sessions/pi.d.ts +36 -0
  64. package/dist/sessions/pi.js +353 -0
  65. package/dist/sessions/pool-books.d.ts +21 -0
  66. package/dist/sessions/pool-books.js +75 -0
  67. package/dist/sessions/pool-choices.d.ts +5 -0
  68. package/dist/sessions/pool-choices.js +11 -0
  69. package/dist/sessions/pool-command.d.ts +76 -5
  70. package/dist/sessions/pool-command.js +263 -136
  71. package/dist/sessions/pool-nightly-deps.d.ts +3 -0
  72. package/dist/sessions/pool-nightly-deps.js +11 -0
  73. package/dist/sessions/pool-nightly.d.ts +10 -0
  74. package/dist/sessions/pool-nightly.js +85 -0
  75. package/dist/sessions/pool-outbox.d.ts +15 -2
  76. package/dist/sessions/pool-outbox.js +79 -26
  77. package/dist/sessions/pool-print.d.ts +1 -1
  78. package/dist/sessions/pool-raw.d.ts +27 -0
  79. package/dist/sessions/pool-raw.js +68 -0
  80. package/dist/sessions/pool-replica.d.ts +35 -0
  81. package/dist/sessions/pool-replica.js +91 -0
  82. package/dist/sessions/pool-send-lock.d.ts +20 -0
  83. package/dist/sessions/pool-send-lock.js +87 -0
  84. package/dist/sessions/pool-sent.d.ts +4 -0
  85. package/dist/sessions/pool-sent.js +26 -0
  86. package/dist/sessions/pool-totals.d.ts +10 -0
  87. package/dist/sessions/pool-totals.js +21 -0
  88. package/dist/sessions/pool-wire.d.ts +240 -30
  89. package/dist/sessions/pool-wire.js +695 -215
  90. package/dist/sessions/public-vocabulary.d.ts +2 -3
  91. package/dist/sessions/public-vocabulary.js +2 -9
  92. package/dist/sessions/query.d.ts +16 -4
  93. package/dist/sessions/query.js +32 -10
  94. package/dist/sessions/read-cost.js +14 -3
  95. package/dist/sessions/read-recovery-edge.d.ts +2 -0
  96. package/dist/sessions/read-recovery-edge.js +26 -0
  97. package/dist/sessions/read.d.ts +8 -1
  98. package/dist/sessions/read.js +6 -3
  99. package/dist/sessions/recall-bundle.d.ts +12 -0
  100. package/dist/sessions/recall-bundle.js +33 -0
  101. package/dist/sessions/recall-frames.d.ts +18 -0
  102. package/dist/sessions/recall-frames.js +44 -0
  103. package/dist/sessions/recall-next.d.ts +145 -0
  104. package/dist/sessions/recall-next.js +241 -0
  105. package/dist/sessions/recall-object.d.ts +2 -0
  106. package/dist/sessions/recall-object.js +1 -0
  107. package/dist/sessions/recall-push.d.ts +2 -2
  108. package/dist/sessions/recall-push.js +3 -51
  109. package/dist/sessions/recall-reads.d.ts +7 -0
  110. package/dist/sessions/recall-reads.js +7 -0
  111. package/dist/sessions/recovery-calls.d.ts +21 -0
  112. package/dist/sessions/recovery-calls.js +25 -0
  113. package/dist/sessions/redact.d.ts +11 -0
  114. package/dist/sessions/redact.js +59 -0
  115. package/dist/sessions/schema.d.ts +59 -9
  116. package/dist/sessions/schema.js +22 -3
  117. package/dist/sessions/session-shape.d.ts +6 -3
  118. package/dist/sessions/session-shape.js +19 -6
  119. package/dist/sessions/sources.js +75 -1
  120. package/dist/sessions/store.d.ts +13 -1
  121. package/dist/sessions/store.js +36 -16
  122. package/dist/sessions/survival.d.ts +2 -0
  123. package/dist/sessions/survival.js +2 -2
  124. package/dist/sessions/transcript-tail.d.ts +14 -0
  125. package/dist/sessions/transcript-tail.js +64 -0
  126. package/dist/sessions/vscode-copilot.js +24 -7
  127. package/dist/telemetry/command.d.ts +8 -0
  128. package/dist/telemetry/command.js +85 -0
  129. package/dist/telemetry/consent.d.ts +17 -0
  130. package/dist/telemetry/consent.js +63 -0
  131. package/dist/telemetry/crash.d.ts +8 -0
  132. package/dist/telemetry/crash.js +52 -0
  133. package/dist/telemetry/emit.d.ts +31 -0
  134. package/dist/telemetry/emit.js +42 -0
  135. package/dist/telemetry/fields.d.ts +16 -0
  136. package/dist/telemetry/fields.js +26 -0
  137. package/dist/telemetry/health.d.ts +21 -0
  138. package/dist/telemetry/health.js +108 -0
  139. package/dist/telemetry/hook-emit.d.ts +4 -0
  140. package/dist/telemetry/hook-emit.js +18 -0
  141. package/dist/telemetry/measures.d.ts +32 -0
  142. package/dist/telemetry/measures.js +128 -0
  143. package/dist/telemetry/observe.d.ts +13 -0
  144. package/dist/telemetry/observe.js +51 -0
  145. package/dist/telemetry/read-events.d.ts +9 -0
  146. package/dist/telemetry/read-events.js +17 -0
  147. package/dist/telemetry/record.d.ts +17 -0
  148. package/dist/telemetry/record.js +26 -0
  149. package/dist/telemetry/report.d.ts +77 -0
  150. package/dist/telemetry/report.js +188 -0
  151. package/dist/telemetry/schema.d.ts +40 -0
  152. package/dist/telemetry/schema.js +187 -0
  153. package/dist/telemetry/send-main.d.ts +1 -0
  154. package/dist/telemetry/send-main.js +11 -0
  155. package/dist/telemetry/send.d.ts +36 -0
  156. package/dist/telemetry/send.js +172 -0
  157. package/dist/telemetry/spool.d.ts +17 -0
  158. package/dist/telemetry/spool.js +70 -0
  159. package/dist/telemetry/wire.d.ts +25 -0
  160. package/dist/telemetry/wire.js +76 -0
  161. package/package.json +2 -2
@@ -0,0 +1,17 @@
1
+ /** One mebibyte per file, so the folder holds at most two. */
2
+ export declare const SPOOL_CAP_BYTES = 1048576;
3
+ export declare const spoolPaths: (dir: string) => {
4
+ current: string;
5
+ previous: string;
6
+ idFile: string;
7
+ settings: string;
8
+ };
9
+ /**
10
+ * This install's id: a random version 4 UUID made on first use and kept in the folder. It comes from the system's random source, never from the
11
+ * machine, the user, or a path, so deleting the file starts a new, unrelated id.
12
+ */
13
+ export declare const installId: (dir: string) => string;
14
+ /** Appends one line, rotating first when it would pass `capBytes`; a line larger than the cap on its own is not written. */
15
+ export declare const appendLine: (dir: string, line: string, capBytes?: number) => void;
16
+ /** Every spooled line, oldest first. */
17
+ export declare const readSpool: (dir: string) => string[];
@@ -0,0 +1,70 @@
1
+ // The telemetry folder: a per-install random id, and an append-only spool of one JSON event per line. Nothing here sends anything. The spool is
2
+ // capped: when a line would pass the cap, the current file becomes the old file (replacing the one before) and a new one starts, so the folder
3
+ // never holds more than twice the cap.
4
+ import { randomUUID } from 'node:crypto';
5
+ import { appendFileSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ /** One mebibyte per file, so the folder holds at most two. */
8
+ export const SPOOL_CAP_BYTES = 1_048_576;
9
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
10
+ export const spoolPaths = (dir) => ({
11
+ current: join(dir, 'events.jsonl'),
12
+ previous: join(dir, 'events.1.jsonl'),
13
+ idFile: join(dir, 'install-id'),
14
+ settings: join(dir, 'settings.json'),
15
+ });
16
+ const readIfPresent = (path) => {
17
+ try {
18
+ return readFileSync(path, 'utf8');
19
+ }
20
+ catch {
21
+ return undefined;
22
+ }
23
+ };
24
+ /**
25
+ * This install's id: a random version 4 UUID made on first use and kept in the folder. It comes from the system's random source, never from the
26
+ * machine, the user, or a path, so deleting the file starts a new, unrelated id.
27
+ */
28
+ export const installId = (dir) => {
29
+ const { idFile } = spoolPaths(dir);
30
+ const existing = readIfPresent(idFile);
31
+ const kept = existing?.trim();
32
+ if (kept !== undefined && UUID.test(kept))
33
+ return kept;
34
+ mkdirSync(dir, { recursive: true });
35
+ const fresh = randomUUID();
36
+ try {
37
+ // Exclusive when no file stood there, so two first runs at once cannot each keep their own id; a file that is not an id is replaced.
38
+ writeFileSync(idFile, `${fresh}\n`, { flag: existing === undefined ? 'wx' : 'w' });
39
+ return fresh;
40
+ }
41
+ catch (error) {
42
+ const winner = readIfPresent(idFile)?.trim();
43
+ if (winner !== undefined && UUID.test(winner))
44
+ return winner;
45
+ throw error;
46
+ }
47
+ };
48
+ /** Appends one line, rotating first when it would pass `capBytes`; a line larger than the cap on its own is not written. */
49
+ export const appendLine = (dir, line, capBytes = SPOOL_CAP_BYTES) => {
50
+ const { current, previous } = spoolPaths(dir);
51
+ const bytes = Buffer.byteLength(line) + 1;
52
+ if (bytes > capBytes)
53
+ return;
54
+ mkdirSync(dir, { recursive: true });
55
+ let size = 0;
56
+ try {
57
+ size = statSync(current).size;
58
+ }
59
+ catch {
60
+ // No spool yet.
61
+ }
62
+ if (size + bytes > capBytes)
63
+ renameSync(current, previous);
64
+ appendFileSync(current, `${line}\n`);
65
+ };
66
+ /** Every spooled line, oldest first. */
67
+ export const readSpool = (dir) => {
68
+ const { current, previous } = spoolPaths(dir);
69
+ return [previous, current].flatMap((path) => (readIfPresent(path) ?? '').split('\n').filter((line) => line !== ''));
70
+ };
@@ -0,0 +1,25 @@
1
+ import { type Emitter } from './emit.js';
2
+ /** One field across the batches of a pass: in how many it appeared, and the bytes of its values (a field that holds others counts them too). */
3
+ interface FieldTally {
4
+ present: number;
5
+ bytes: number;
6
+ }
7
+ interface HarnessTally {
8
+ batches: number;
9
+ bytes: number;
10
+ fields: Map<string, FieldTally>;
11
+ }
12
+ /** The tally of one deposit pass, by the harness that wrote each batch. */
13
+ export type WireTally = Map<string, HarnessTally>;
14
+ export declare const newTally: () => WireTally;
15
+ /** Adds one batch (the parsed skeleton, before it is written) to the tally. It never throws: a batch it cannot read (a cycle) adds nothing, and the deposit goes on. */
16
+ export declare const tallyBatch: (tally: WireTally, batch: unknown) => void;
17
+ /** One `wire` event for each harness the pass built a batch for: the names, and beside them the batches that held each and the bytes it took. */
18
+ export declare const recordWire: (emitter: Emitter, tally: WireTally) => void;
19
+ /** One send of one batch. `code` is the pool's own word for a refusal; the schema drops anything that is not a code. */
20
+ export declare const recordSend: (emitter: Emitter, send: {
21
+ status: number;
22
+ code?: string | undefined;
23
+ bytes: number;
24
+ }) => void;
25
+ export {};
@@ -0,0 +1,76 @@
1
+ // Wire policing: what each deposit batch carried and what a send came to, so a field that is narrow (rarely present) or costly (many bytes nobody reads)
2
+ // shows up. Field names are only those of the register (`FIELD_ROWS`, the same list `oars pool outbox --explain` answers from); a value is never read
3
+ // past its size, and a name the register does not hold is not counted.
4
+ import { FIELD_ROWS } from '../sessions/pool-outbox.js';
5
+ import { HARNESSES, OTHER } from '../sessions/pool-wire.js';
6
+ import { emitEvent } from './emit.js';
7
+ export const newTally = () => new Map();
8
+ const REGISTER = new Set(Object.keys(FIELD_ROWS));
9
+ /** JSON.stringify answers `undefined` for a value it leaves out, which its signature does not say. */
10
+ const asText = (value) => JSON.stringify(value);
11
+ /** The bytes of a value as JSON; a value JSON leaves out (`undefined`) is not on the wire and has none. */
12
+ const sizeOf = (value) => {
13
+ const text = asText(value);
14
+ return text === undefined ? 0 : Buffer.byteLength(text);
15
+ };
16
+ const harnessOf = (batch) => {
17
+ const transcripts = typeof batch === 'object' && batch !== null ? Reflect.get(batch, 'transcripts') : undefined;
18
+ const first = Array.isArray(transcripts) ? transcripts[0] : undefined;
19
+ const harness = typeof first === 'object' && first !== null ? Reflect.get(first, 'harness') : undefined;
20
+ return typeof harness === 'string' && HARNESSES.some((known) => known === harness) ? harness : OTHER;
21
+ };
22
+ const walk = (value, seen) => {
23
+ if (Array.isArray(value)) {
24
+ for (const entry of value)
25
+ walk(entry, seen);
26
+ }
27
+ else if (typeof value === 'object' && value !== null) {
28
+ for (const [name, entry] of Object.entries(value)) {
29
+ if (REGISTER.has(name) && entry !== undefined)
30
+ seen.set(name, (seen.get(name) ?? 0) + sizeOf(entry));
31
+ walk(entry, seen);
32
+ }
33
+ }
34
+ };
35
+ const add = (tally, batch) => {
36
+ const harness = harnessOf(batch);
37
+ const entry = tally.get(harness) ?? { batches: 0, bytes: 0, fields: new Map() };
38
+ entry.batches += 1;
39
+ entry.bytes += sizeOf(batch);
40
+ const seen = new Map();
41
+ walk(batch, seen);
42
+ for (const [name, bytes] of seen) {
43
+ const field = entry.fields.get(name) ?? { present: 0, bytes: 0 };
44
+ field.present += 1;
45
+ field.bytes += bytes;
46
+ entry.fields.set(name, field);
47
+ }
48
+ tally.set(harness, entry);
49
+ };
50
+ /** Adds one batch (the parsed skeleton, before it is written) to the tally. It never throws: a batch it cannot read (a cycle) adds nothing, and the deposit goes on. */
51
+ export const tallyBatch = (tally, batch) => {
52
+ try {
53
+ add(tally, batch);
54
+ }
55
+ catch {
56
+ // Telemetry is never allowed to change what a command does.
57
+ }
58
+ };
59
+ /** One `wire` event for each harness the pass built a batch for: the names, and beside them the batches that held each and the bytes it took. */
60
+ export const recordWire = (emitter, tally) => {
61
+ for (const [harness, entry] of [...tally].sort(([a], [b]) => a.localeCompare(b))) {
62
+ const fields = [...entry.fields].sort(([a], [b]) => a.localeCompare(b));
63
+ emitEvent(emitter, 'wire', {
64
+ harness,
65
+ batches: entry.batches,
66
+ bytes: entry.bytes,
67
+ fields: fields.map(([name]) => name),
68
+ present: fields.map(([, field]) => field.present),
69
+ fieldBytes: fields.map(([, field]) => field.bytes),
70
+ });
71
+ }
72
+ };
73
+ /** One send of one batch. `code` is the pool's own word for a refusal; the schema drops anything that is not a code. */
74
+ export const recordSend = (emitter, send) => {
75
+ emitEvent(emitter, 'wire-send', { sent: send.status === 201 ? 1 : 0, status: send.status, bytes: send.bytes, ...(send.code === undefined ? {} : { code: send.code }) });
76
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@manyoars/oars",
3
- "version": "0.3.0",
4
- "description": "The oars CLI: sign in, manage API keys, and keep a local memory of your own Claude Code sessions (on your machine; `oars sessions` uploads nothing except what `gold arms` sends through your own Claude subscription)",
3
+ "version": "0.4.0",
4
+ "description": "The oars CLI: sign in, manage API keys, and keep a local memory of your own coding-agent sessions on your machine. The session commands upload nothing except what `gold arms` sends through your own Claude subscription; telemetry (on by default) and the nightly pool send after you accept a pool covenant are described in the README",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",