ai-hist 0.3.7 → 0.4.1
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/cloud-client.d.ts +1 -0
- package/dist/cloud-client.d.ts.map +1 -1
- package/dist/cloud-client.js +2 -0
- package/dist/cloud-client.js.map +1 -1
- package/dist/cloud-push.d.ts +47 -0
- package/dist/cloud-push.d.ts.map +1 -0
- package/dist/cloud-push.js +111 -0
- package/dist/cloud-push.js.map +1 -0
- package/dist/cloud-push.test.d.ts +2 -0
- package/dist/cloud-push.test.d.ts.map +1 -0
- package/dist/cloud-push.test.js +68 -0
- package/dist/cloud-push.test.js.map +1 -0
- package/package.json +1 -1
package/dist/cloud-client.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-client.d.ts","sourceRoot":"","sources":["../src/cloud-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloud-client.d.ts","sourceRoot":"","sources":["../src/cloud-client.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEtG,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,GACpC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAajC,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CASnF;AAED,wBAAsB,UAAU,CAC9B,gBAAgB,EAAE,MAAM,EACxB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9C,OAAO,CAAC,gBAAgB,CAAC,CAkD3B"}
|
package/dist/cloud-client.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join, dirname } from 'node:path';
|
|
4
|
+
// Re-export the in-process cloud push so `ai-hist/cloud` exposes auth + push.
|
|
5
|
+
export { pushToCloud, resolveAiHistBinary } from './cloud-push.js';
|
|
4
6
|
function authPath() {
|
|
5
7
|
const configDir = process.env.AI_HIST_CONFIG_DIR ?? join(homedir(), '.config', 'ai-hist');
|
|
6
8
|
return join(configDir, 'auth.json');
|
package/dist/cloud-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-client.js","sourceRoot":"","sources":["../src/cloud-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cloud-client.js","sourceRoot":"","sources":["../src/cloud-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process cloud push — a thin wrapper over the `ai-hist` Rust binary.
|
|
3
|
+
*
|
|
4
|
+
* Rather than re-implement the push pipeline (outbox batching, cursor, wire
|
|
5
|
+
* envelopes, dedup hashing) in TypeScript — which would have to stay byte-for-
|
|
6
|
+
* byte compatible with the Rust client forever — this spawns the real
|
|
7
|
+
* `ai-hist push --json` and parses its result. The Rust binary is the single
|
|
8
|
+
* source of truth for the push logic; this is just an ergonomic SDK surface
|
|
9
|
+
* over it, so hosts (e.g. the Agent Relay runtime) can sync without shelling
|
|
10
|
+
* out to a CLI by hand.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
export interface PushReport {
|
|
14
|
+
sent: number;
|
|
15
|
+
accepted: number;
|
|
16
|
+
batchId: string | null;
|
|
17
|
+
cursor?: unknown;
|
|
18
|
+
}
|
|
19
|
+
export interface PushOptions {
|
|
20
|
+
/** Explicit path to the ai-hist binary (overrides discovery). */
|
|
21
|
+
binPath?: string;
|
|
22
|
+
/** Rows scanned per source (forwarded as `--limit`). */
|
|
23
|
+
limit?: number;
|
|
24
|
+
/** Session/trajectory ids to exclude (forwarded as repeated `--incognito`). */
|
|
25
|
+
incognito?: Iterable<string>;
|
|
26
|
+
/** Extra environment for the spawned binary (merged over `process.env`). */
|
|
27
|
+
env?: NodeJS.ProcessEnv;
|
|
28
|
+
/** Injectable spawn for testing. */
|
|
29
|
+
spawnFn?: typeof spawn;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the ai-hist binary. An explicit override is **authoritative** — it is
|
|
33
|
+
* returned verbatim without falling through to discovery, so the caller always
|
|
34
|
+
* runs the binary it asked for (an unusable path surfaces later in `pushToCloud`
|
|
35
|
+
* as a no-op push, never a silent switch to a different binary). With no
|
|
36
|
+
* override: `$AI_HIST_RUST_BIN` → the install.sh location → the `ai-hist`
|
|
37
|
+
* wrapper on `PATH`. Always returns a string.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveAiHistBinary(explicit?: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Push new local history to relayhistory-cloud by driving `ai-hist push --json`.
|
|
42
|
+
* Resolves `null` when the binary isn't installed or the user isn't
|
|
43
|
+
* authenticated (both non-fatal for a background loop); rejects on other
|
|
44
|
+
* non-zero exits or unparseable output.
|
|
45
|
+
*/
|
|
46
|
+
export declare function pushToCloud(opts?: PushOptions): Promise<PushReport | null>;
|
|
47
|
+
//# sourceMappingURL=cloud-push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-push.d.ts","sourceRoot":"","sources":["../src/cloud-push.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAK9D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7B,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAaD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CA6D9E"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process cloud push — a thin wrapper over the `ai-hist` Rust binary.
|
|
3
|
+
*
|
|
4
|
+
* Rather than re-implement the push pipeline (outbox batching, cursor, wire
|
|
5
|
+
* envelopes, dedup hashing) in TypeScript — which would have to stay byte-for-
|
|
6
|
+
* byte compatible with the Rust client forever — this spawns the real
|
|
7
|
+
* `ai-hist push --json` and parses its result. The Rust binary is the single
|
|
8
|
+
* source of truth for the push logic; this is just an ergonomic SDK surface
|
|
9
|
+
* over it, so hosts (e.g. the Agent Relay runtime) can sync without shelling
|
|
10
|
+
* out to a CLI by hand.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import { accessSync, constants } from 'node:fs';
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
const AI_HIST_RUST_BIN_ENV = 'AI_HIST_RUST_BIN';
|
|
17
|
+
function isExecutable(path) {
|
|
18
|
+
try {
|
|
19
|
+
accessSync(path, constants.X_OK);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the ai-hist binary. An explicit override is **authoritative** — it is
|
|
28
|
+
* returned verbatim without falling through to discovery, so the caller always
|
|
29
|
+
* runs the binary it asked for (an unusable path surfaces later in `pushToCloud`
|
|
30
|
+
* as a no-op push, never a silent switch to a different binary). With no
|
|
31
|
+
* override: `$AI_HIST_RUST_BIN` → the install.sh location → the `ai-hist`
|
|
32
|
+
* wrapper on `PATH`. Always returns a string.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveAiHistBinary(explicit) {
|
|
35
|
+
if (typeof explicit === 'string' && explicit.length > 0)
|
|
36
|
+
return explicit;
|
|
37
|
+
const known = [
|
|
38
|
+
process.env[AI_HIST_RUST_BIN_ENV],
|
|
39
|
+
join(homedir(), '.local', 'share', 'ai-hist', 'ai-hist-rust-bin'),
|
|
40
|
+
].filter((c) => typeof c === 'string' && c.length > 0);
|
|
41
|
+
for (const candidate of known) {
|
|
42
|
+
if (isExecutable(candidate))
|
|
43
|
+
return candidate;
|
|
44
|
+
}
|
|
45
|
+
// Let PATH resolution (and ENOENT handling) take over.
|
|
46
|
+
return 'ai-hist';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Push new local history to relayhistory-cloud by driving `ai-hist push --json`.
|
|
50
|
+
* Resolves `null` when the binary isn't installed or the user isn't
|
|
51
|
+
* authenticated (both non-fatal for a background loop); rejects on other
|
|
52
|
+
* non-zero exits or unparseable output.
|
|
53
|
+
*/
|
|
54
|
+
export function pushToCloud(opts = {}) {
|
|
55
|
+
const spawnFn = opts.spawnFn ?? spawn;
|
|
56
|
+
const bin = resolveAiHistBinary(opts.binPath);
|
|
57
|
+
const args = ['push', '--json'];
|
|
58
|
+
if (opts.limit != null)
|
|
59
|
+
args.push('--limit', String(opts.limit));
|
|
60
|
+
for (const id of opts.incognito ?? [])
|
|
61
|
+
args.push('--incognito', id);
|
|
62
|
+
const spawnOpts = {
|
|
63
|
+
env: { ...process.env, ...opts.env },
|
|
64
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
65
|
+
};
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const child = spawnFn(bin, args, spawnOpts);
|
|
68
|
+
let stdout = '';
|
|
69
|
+
let stderr = '';
|
|
70
|
+
child.stdout?.on('data', (chunk) => {
|
|
71
|
+
stdout += String(chunk);
|
|
72
|
+
});
|
|
73
|
+
child.stderr?.on('data', (chunk) => {
|
|
74
|
+
stderr += String(chunk);
|
|
75
|
+
});
|
|
76
|
+
child.on('error', (err) => {
|
|
77
|
+
// Binary unavailable — missing (ENOENT), not executable (EACCES/EPERM), or
|
|
78
|
+
// a directory (ENOTDIR/EISDIR). All mean "nothing to sync", the same
|
|
79
|
+
// no-op contract a background loop relies on.
|
|
80
|
+
if (['ENOENT', 'EACCES', 'EPERM', 'ENOTDIR', 'EISDIR'].includes(err.code ?? '')) {
|
|
81
|
+
resolve(null);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
reject(err);
|
|
85
|
+
});
|
|
86
|
+
child.on('close', (code) => {
|
|
87
|
+
if (code !== 0) {
|
|
88
|
+
// Not logged in yet is expected before `reflex on` completes.
|
|
89
|
+
if (/not authenticated|no relayhistory auth|run `?ai-hist login/i.test(stderr)) {
|
|
90
|
+
resolve(null);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
reject(new Error(`ai-hist push failed (exit ${code}): ${stderr.trim().slice(0, 300)}`));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const parsed = (stdout.trim() ? JSON.parse(stdout) : {});
|
|
98
|
+
resolve({
|
|
99
|
+
sent: typeof parsed.sent === 'number' ? parsed.sent : 0,
|
|
100
|
+
accepted: typeof parsed.accepted === 'number' ? parsed.accepted : 0,
|
|
101
|
+
batchId: typeof parsed.batchId === 'string' ? parsed.batchId : null,
|
|
102
|
+
cursor: parsed.cursor,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
reject(new Error(`could not parse ai-hist push output: ${err instanceof Error ? err.message : String(err)}`));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=cloud-push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-push.js","sourceRoot":"","sources":["../src/cloud-push.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-push.test.d.ts","sourceRoot":"","sources":["../src/cloud-push.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
import { test } from 'node:test';
|
|
4
|
+
import { pushToCloud } from './cloud-push.js';
|
|
5
|
+
/** Minimal fake child process that scripts stdout/stderr/exit for one run. */
|
|
6
|
+
function fakeSpawn(script) {
|
|
7
|
+
const calls = [];
|
|
8
|
+
const spawnFn = ((bin, args) => {
|
|
9
|
+
calls.push({ bin, args });
|
|
10
|
+
const child = new EventEmitter();
|
|
11
|
+
child.stdout = new EventEmitter();
|
|
12
|
+
child.stderr = new EventEmitter();
|
|
13
|
+
// Emit asynchronously so the caller has attached its listeners.
|
|
14
|
+
setImmediate(() => {
|
|
15
|
+
if (script.errorCode) {
|
|
16
|
+
const err = new Error('spawn failed');
|
|
17
|
+
err.code = script.errorCode;
|
|
18
|
+
child.emit('error', err);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (script.stdout)
|
|
22
|
+
child.stdout.emit('data', script.stdout);
|
|
23
|
+
if (script.stderr)
|
|
24
|
+
child.stderr.emit('data', script.stderr);
|
|
25
|
+
child.emit('close', script.code ?? 0);
|
|
26
|
+
});
|
|
27
|
+
return child;
|
|
28
|
+
});
|
|
29
|
+
return { spawnFn, calls };
|
|
30
|
+
}
|
|
31
|
+
test('pushToCloud parses ai-hist push --json output into a report', async () => {
|
|
32
|
+
const { spawnFn, calls } = fakeSpawn({
|
|
33
|
+
stdout: JSON.stringify({ sent: 3, accepted: 3, batchId: 'b_abc', cursor: { history_id: 9 } }),
|
|
34
|
+
});
|
|
35
|
+
const report = await pushToCloud({ binPath: '/bin/echo', spawnFn, limit: 200, incognito: ['s1', 's2'] });
|
|
36
|
+
assert.deepEqual(report, { sent: 3, accepted: 3, batchId: 'b_abc', cursor: { history_id: 9 } });
|
|
37
|
+
// Forwards the flags to the binary.
|
|
38
|
+
assert.deepEqual(calls[0].args, ['push', '--json', '--limit', '200', '--incognito', 's1', '--incognito', 's2']);
|
|
39
|
+
});
|
|
40
|
+
test('pushToCloud returns null when the binary is not installed (ENOENT)', async () => {
|
|
41
|
+
const { spawnFn } = fakeSpawn({ errorCode: 'ENOENT' });
|
|
42
|
+
const report = await pushToCloud({ binPath: '/bin/echo', spawnFn });
|
|
43
|
+
assert.equal(report, null);
|
|
44
|
+
});
|
|
45
|
+
test('pushToCloud returns null when the binary is not executable (EACCES)', async () => {
|
|
46
|
+
const { spawnFn } = fakeSpawn({ errorCode: 'EACCES' });
|
|
47
|
+
const report = await pushToCloud({ binPath: '/some/dir', spawnFn });
|
|
48
|
+
assert.equal(report, null);
|
|
49
|
+
});
|
|
50
|
+
test('pushToCloud returns null when not authenticated', async () => {
|
|
51
|
+
const { spawnFn } = fakeSpawn({ code: 1, stderr: 'error: not authenticated — run `ai-hist login`' });
|
|
52
|
+
const report = await pushToCloud({ binPath: '/bin/echo', spawnFn });
|
|
53
|
+
assert.equal(report, null);
|
|
54
|
+
});
|
|
55
|
+
test('pushToCloud rejects on other non-zero exits', async () => {
|
|
56
|
+
const { spawnFn } = fakeSpawn({ code: 2, stderr: 'boom: server exploded' });
|
|
57
|
+
await assert.rejects(pushToCloud({ binPath: '/bin/echo', spawnFn }), /ai-hist push failed \(exit 2\).*boom/);
|
|
58
|
+
});
|
|
59
|
+
test('pushToCloud treats empty stdout as a zero-record push', async () => {
|
|
60
|
+
const { spawnFn } = fakeSpawn({ stdout: '' });
|
|
61
|
+
const report = await pushToCloud({ binPath: '/bin/echo', spawnFn });
|
|
62
|
+
assert.deepEqual(report, { sent: 0, accepted: 0, batchId: null, cursor: undefined });
|
|
63
|
+
});
|
|
64
|
+
test('pushToCloud rejects when the binary prints malformed JSON', async () => {
|
|
65
|
+
const { spawnFn } = fakeSpawn({ code: 0, stdout: '{ truncated' });
|
|
66
|
+
await assert.rejects(pushToCloud({ binPath: '/bin/echo', spawnFn }), /could not parse ai-hist push output/);
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=cloud-push.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-push.test.js","sourceRoot":"","sources":["../src/cloud-push.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-hist",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "TypeScript SDK and MCP server for AI coding agent session history — Claude Code, Codex, Cursor, Grok, and Agent Relay in one searchable index. Works with or without ai-hist sync via automatic JSONL fallback.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|