trajectories 0.1.1 → 0.1.3
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 +25 -0
- package/README.md +35 -5
- package/dist/cli.js +25044 -1718
- package/dist/convert.d.ts +8 -0
- package/dist/convert.js +26 -4
- package/dist/deepagents_checkpoint.py +362 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +1205 -125
- package/dist/log-stage.js +3 -1
- package/dist/log-state.d.ts +1 -1
- package/dist/log-switch.js +4 -3
- package/dist/readers/atif.d.ts +10 -0
- package/dist/readers/atif.js +54 -0
- package/dist/shared-managed.d.ts +31 -0
- package/dist/shared-managed.js +333 -0
- package/dist/shared-observed.d.ts +96 -0
- package/dist/shared-observed.js +286 -0
- package/dist/shared.d.ts +40 -0
- package/dist/shared.js +357 -0
- package/dist/types.d.ts +41 -3
- package/dist/types.js +7 -1
- package/docs/shared-history.md +133 -0
- package/legacy/src/cli.ts +2 -2
- package/legacy/src/db.ts +11 -7
- package/legacy/src/reader.ts +21 -0
- package/legacy/src/shared.ts +217 -0
- package/legacy/src/sync.ts +17 -3
- package/ops/README.md +31 -1
- package/ops/install-native.sh +35 -0
- package/package.json +51 -12
- package/dist/cli.d.ts +0 -2
package/legacy/src/reader.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { SCHEMA_VERSION } from "./schema.js";
|
|
|
15
15
|
import { classifyReadStore, formatMismatchMessage, incompleteStoreMessage } from "./schema-gate.js";
|
|
16
16
|
import type {
|
|
17
17
|
CanonicalDbRecord,
|
|
18
|
+
IngestDiagnosticRecord,
|
|
18
19
|
QueryResult,
|
|
19
20
|
RecordMatchType,
|
|
20
21
|
RecordSearchHit,
|
|
@@ -1220,6 +1221,26 @@ export class TrajectoryReader {
|
|
|
1220
1221
|
return sessionRecords(this.db, sessionId);
|
|
1221
1222
|
}
|
|
1222
1223
|
|
|
1224
|
+
/** Read normalization uncertainty without opening a writer or reviving a tombstone. */
|
|
1225
|
+
getSessionDiagnostics(sessionId: string): IngestDiagnosticRecord[] {
|
|
1226
|
+
if (!this.getSession(sessionId)) return [];
|
|
1227
|
+
return this.db.query<IngestDiagnosticRecord, [string]>(
|
|
1228
|
+
`SELECT session_id, source, code, occurrence_count, sample, first_seen_at, last_seen_at
|
|
1229
|
+
FROM ingest_diagnostics WHERE session_id = ? ORDER BY code ASC`,
|
|
1230
|
+
).all(sessionId);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
/** Pin one source generation before sending its complete history to a remote. */
|
|
1234
|
+
readSessionForSync(sessionId: string): {
|
|
1235
|
+
session: SessionRecord; records: CanonicalDbRecord[]; diagnostics: IngestDiagnosticRecord[];
|
|
1236
|
+
} | null {
|
|
1237
|
+
return this.db.transaction(() => {
|
|
1238
|
+
const session = this.getSession(sessionId);
|
|
1239
|
+
if (!session) return null;
|
|
1240
|
+
return { session, records: this.getSessionRecords(sessionId), diagnostics: this.getSessionDiagnostics(sessionId) };
|
|
1241
|
+
}).deferred();
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1223
1244
|
getSessionCount(): number {
|
|
1224
1245
|
return countSessions(this.db);
|
|
1225
1246
|
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { closeSync, constants, existsSync, fstatSync, openSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { resolveCanonicalDbPath } from './canonical.js';
|
|
6
|
+
import { ensurePrivateDirectory } from './private-storage.js';
|
|
7
|
+
import { acquireWriterLease, isMemoryStorePath } from './writer-lease.js';
|
|
8
|
+
import { TrajectoryReader } from './reader.js';
|
|
9
|
+
import { TrajectoryDb } from './db.js';
|
|
10
|
+
import { listSessions } from '../../src/log.js';
|
|
11
|
+
import type { PageCursor } from '../../src/log-types.js';
|
|
12
|
+
import { TrajectoriesError } from '../../src/log-types.js';
|
|
13
|
+
import { exportManagedDelta } from '../../src/shared-managed.js';
|
|
14
|
+
import { safeId } from '../../src/log-io.js';
|
|
15
|
+
import { createSharedClient, createSharedHandler, sharedObservationId } from '../../src/shared.js';
|
|
16
|
+
import type { ObservedState } from '../../src/shared.js';
|
|
17
|
+
|
|
18
|
+
const help = `trajectories remote — cross-machine canonical history
|
|
19
|
+
remote serve --store <shared-directory> --token-file <0600-file>
|
|
20
|
+
[--host 127.0.0.1] [--port 8789]
|
|
21
|
+
remote push --url <endpoint> --token-file <0600-file> --machine <stable-id>
|
|
22
|
+
[--db <SQLite>] [--store <managed-store>] [--session <id>]
|
|
23
|
+
remote pull --url <endpoint> --token-file <0600-file> [--db <SQLite>]
|
|
24
|
+
[--store <managed-mirror>] [--machine <stable-origin-id>]
|
|
25
|
+
remote list --url <endpoint> --token-file <0600-file>
|
|
26
|
+
Token path fallback: TRAJECTORIES_REMOTE_TOKEN_FILE. Tokens never belong in URLs.
|
|
27
|
+
Push publishes observed canonical records; native files are not uploaded by this lane.
|
|
28
|
+
--store additionally synchronizes managed logs and their referenced objects.
|
|
29
|
+
Pull updates the existing SQLite lexical index; it never launches native harnesses.
|
|
30
|
+
Pull --machine skips that originating machine's observed rows already stored locally.
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
function required(value: string | undefined, option: string): string {
|
|
34
|
+
if (!value) throw new Error(`${option} is required.`);
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Read an owner-only regular file through the validated descriptor. */
|
|
39
|
+
function readToken(path: string): string {
|
|
40
|
+
const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
41
|
+
try {
|
|
42
|
+
const stat = fstatSync(fd);
|
|
43
|
+
if (!stat.isFile() || stat.nlink !== 1 || (stat.mode & 0o777) !== 0o600 || (process.getuid && stat.uid !== process.getuid())) {
|
|
44
|
+
throw new Error('Remote token file must be an owner-owned regular file with mode 0600.');
|
|
45
|
+
}
|
|
46
|
+
if (stat.size > 4096) throw new Error('Remote token file is too large.');
|
|
47
|
+
const token = readFileSync(fd, 'utf8').trim();
|
|
48
|
+
if (!token || /[\s\x00-\x1f\x7f]/u.test(token)) throw new Error('Remote token file must contain one nonempty token.');
|
|
49
|
+
return token;
|
|
50
|
+
} finally { closeSync(fd); }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function project(state: ObservedState) {
|
|
54
|
+
const id = `shared:${state.id}`;
|
|
55
|
+
return {
|
|
56
|
+
session: { ...state.session, id, source_path: `shared://${state.machineId}/${encodeURIComponent(state.session.id)}`,
|
|
57
|
+
// The remote immutable head covers both record and metadata changes.
|
|
58
|
+
content_hash: state.head,
|
|
59
|
+
parent_session_id: state.session.parent_session_id
|
|
60
|
+
? `shared:${sharedObservationId(state.machineId, state.session.parent_session_id)}` : null,
|
|
61
|
+
},
|
|
62
|
+
records: state.records.map(record => ({ ...record, id: `${id}:${record.id}`, session_id: id })),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function runSharedCli(argv: string[]): Promise<void> {
|
|
67
|
+
// Help must not open SQLite, managed stores, credentials or sockets.
|
|
68
|
+
if (!argv.length || argv.some(value => value === '--help' || value === '-h') || argv[0] === 'help') { console.log(help); return; }
|
|
69
|
+
const command = argv[0];
|
|
70
|
+
if (!['serve', 'push', 'pull', 'list'].includes(command!)) throw new Error('Expected remote serve, push, pull or list.');
|
|
71
|
+
const { values, positionals } = parseArgs({ args: argv.slice(1), strict: true, allowPositionals: true, options: {
|
|
72
|
+
store: { type: 'string' }, host: { type: 'string' }, port: { type: 'string' },
|
|
73
|
+
url: { type: 'string' }, 'token-file': { type: 'string' }, machine: { type: 'string' },
|
|
74
|
+
db: { type: 'string' }, session: { type: 'string' }, robot: { type: 'boolean' }, json: { type: 'boolean' },
|
|
75
|
+
}});
|
|
76
|
+
if (positionals.length) throw new Error('Unexpected remote positional arguments.');
|
|
77
|
+
const allowed: Record<string, Set<string>> = {
|
|
78
|
+
serve: new Set(['store', 'host', 'port', 'token-file', 'json', 'robot']),
|
|
79
|
+
push: new Set(['url', 'token-file', 'machine', 'db', 'store', 'session', 'json', 'robot']),
|
|
80
|
+
pull: new Set(['url', 'token-file', 'db', 'store', 'machine', 'json', 'robot']),
|
|
81
|
+
list: new Set(['url', 'token-file', 'json', 'robot']),
|
|
82
|
+
};
|
|
83
|
+
for (const option of Object.keys(values)) {
|
|
84
|
+
if (!allowed[command!]!.has(option)) throw new Error(`--${option} is not supported by remote ${command}.`);
|
|
85
|
+
}
|
|
86
|
+
const machine = values.machine === undefined ? undefined : safeId(values.machine);
|
|
87
|
+
const port = values.port === undefined ? 8789 : Number(values.port);
|
|
88
|
+
if (command === 'serve' && (!Number.isInteger(port) || port < 0 || port > 65535)) throw new Error('--port must be an integer from 0 through 65535.');
|
|
89
|
+
const token = readToken(required(values['token-file'] ?? process.env.TRAJECTORIES_REMOTE_TOKEN_FILE, '--token-file'));
|
|
90
|
+
const emit = (value: unknown) => console.log(JSON.stringify(value));
|
|
91
|
+
if (command === 'serve') {
|
|
92
|
+
const store = resolve(required(values.store, '--store'));
|
|
93
|
+
const server = Bun.serve({ hostname: values.host ?? '127.0.0.1', port,
|
|
94
|
+
maxRequestBodySize: 64 * 1024 * 1024, fetch: createSharedHandler({ store, token }),
|
|
95
|
+
});
|
|
96
|
+
const close = () => { server.stop(true); process.removeListener('SIGINT', close); process.removeListener('SIGTERM', close); };
|
|
97
|
+
process.on('SIGINT', close); process.on('SIGTERM', close);
|
|
98
|
+
emit({ command: 'serve', host: server.hostname, port: server.port });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const client = createSharedClient({ url: required(values.url, '--url'), token });
|
|
102
|
+
if (command === 'list') {
|
|
103
|
+
const observed = await client.listObserved();
|
|
104
|
+
const managed = await client.listManaged();
|
|
105
|
+
emit({ observed: observed.map(item => ({ id: item.id, head: item.head, machineId: item.machineId,
|
|
106
|
+
source: item.session.source, records: item.session.record_count })),
|
|
107
|
+
managed: managed.map(item => ({ id: item.id, head: item.head })),
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const dbPath = values.db ?? resolveCanonicalDbPath();
|
|
112
|
+
if (command === 'push') {
|
|
113
|
+
const machineId = required(machine, '--machine');
|
|
114
|
+
const observed = [];
|
|
115
|
+
let selected = false;
|
|
116
|
+
// An explicit managed store without --db selects only the managed lane.
|
|
117
|
+
if (!values.store || values.db) {
|
|
118
|
+
const reader = new TrajectoryReader(dbPath);
|
|
119
|
+
try {
|
|
120
|
+
const ids = values.session ? [values.session] : reader.getAllSessions().map(session => session.id);
|
|
121
|
+
for (const id of ids) {
|
|
122
|
+
const captured = reader.readSessionForSync(id);
|
|
123
|
+
if (!captured) continue;
|
|
124
|
+
const { session, records } = captured;
|
|
125
|
+
// Derived mirrors and managed projections cannot become native originals.
|
|
126
|
+
if (session.source === 'managed' || session.id.startsWith('managed:') || session.source_path.startsWith('shared://')) continue;
|
|
127
|
+
selected = true;
|
|
128
|
+
const diagnostics = captured.diagnostics.map(item => ({ code: item.code,
|
|
129
|
+
occurrenceCount: item.occurrence_count, sample: item.sample, firstSeenAt: item.first_seen_at }));
|
|
130
|
+
observed.push(await client.pushObserved({ machineId, session: { ...session, diagnostics }, records }));
|
|
131
|
+
}
|
|
132
|
+
} finally { reader.close(); }
|
|
133
|
+
}
|
|
134
|
+
const managed = [];
|
|
135
|
+
if (values.store) {
|
|
136
|
+
const store = resolve(values.store);
|
|
137
|
+
let cursor: PageCursor | undefined;
|
|
138
|
+
do {
|
|
139
|
+
const page = await listSessions(store, { cursor, limit: 1000 });
|
|
140
|
+
for (const session of page.items) {
|
|
141
|
+
if (values.session && session.id !== values.session) continue;
|
|
142
|
+
selected = true;
|
|
143
|
+
managed.push(await client.pushManaged(store, session.id));
|
|
144
|
+
}
|
|
145
|
+
cursor = page.nextCursor ?? undefined;
|
|
146
|
+
} while (cursor);
|
|
147
|
+
}
|
|
148
|
+
if (values.session && !selected) throw new Error('Selected original session was not found; managed projections require --store.');
|
|
149
|
+
emit({ command, observed, managed });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (!isMemoryStorePath(dbPath)) {
|
|
153
|
+
if (!values.db && !process.env.TRAJECTORIES_DB && !process.env.LEDGER_DB) ensurePrivateDirectory(dirname(dbPath));
|
|
154
|
+
else await mkdir(dirname(dbPath), { recursive: true, mode: 0o700 });
|
|
155
|
+
}
|
|
156
|
+
const observed = [];
|
|
157
|
+
for (const item of await client.listObserved()) {
|
|
158
|
+
if (item.machineId === machine) {
|
|
159
|
+
observed.push({ id: item.id, head: item.head, outcome: 'skipped-origin' });
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (!isMemoryStorePath(dbPath) && existsSync(dbPath)) {
|
|
163
|
+
const reader = new TrajectoryReader(dbPath);
|
|
164
|
+
try {
|
|
165
|
+
const local = reader.getSession(`shared:${item.id}`);
|
|
166
|
+
if (local?.source_path.startsWith('shared://') && local.content_hash === item.head) {
|
|
167
|
+
observed.push({ id: item.id, head: item.head, records: local.record_count, outcome: 'unchanged' });
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
} finally { reader.close(); }
|
|
171
|
+
}
|
|
172
|
+
const state = await client.getObserved(item.id);
|
|
173
|
+
const lease = isMemoryStorePath(dbPath) ? null : acquireWriterLease(dbPath, { holder: 'trajectories remote pull' });
|
|
174
|
+
try {
|
|
175
|
+
const db = new TrajectoryDb(dbPath);
|
|
176
|
+
try {
|
|
177
|
+
const projection = project(state);
|
|
178
|
+
if (db.getTombstone(projection.session.id)) { observed.push({ id: item.id, head: state.head, outcome: 'deleted' }); continue; }
|
|
179
|
+
const saved = db.saveSession(projection.session, projection.records, state.session.diagnostics);
|
|
180
|
+
observed.push({ id: item.id, head: state.head, records: projection.records.length,
|
|
181
|
+
outcome: saved.inserted ? 'inserted' : saved.updated ? 'updated' : 'unchanged' });
|
|
182
|
+
} finally { db.close(); }
|
|
183
|
+
} finally { lease?.close(); }
|
|
184
|
+
}
|
|
185
|
+
const managed = [];
|
|
186
|
+
let conflicts = 0;
|
|
187
|
+
if (values.store) {
|
|
188
|
+
const store = resolve(values.store);
|
|
189
|
+
for (const session of await client.listManaged()) {
|
|
190
|
+
try { managed.push({ ...await client.pullManaged(store, session.id), outcome: 'pulled' }); }
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (!(error instanceof TrajectoriesError) || !['STALE_HEAD', 'INTENT_CONFLICT'].includes(error.code)) throw error;
|
|
193
|
+
// The failed pull may have observed a newer branch than the original list.
|
|
194
|
+
const remote = (await client.listManaged()).find(item => item.id === session.id);
|
|
195
|
+
let ahead;
|
|
196
|
+
try { if (remote) ahead = await exportManagedDelta(store, session.id, { afterHead: remote.head }); }
|
|
197
|
+
catch (proofError) {
|
|
198
|
+
if (!(proofError instanceof TrajectoriesError) || !['STALE_HEAD', 'NOT_FOUND'].includes(proofError.code)) throw proofError;
|
|
199
|
+
}
|
|
200
|
+
if (remote && ahead?.frames.length && ahead.head !== remote.head) {
|
|
201
|
+
managed.push({ id: session.id, head: ahead.head, remoteHead: remote.head, outcome: 'ahead' });
|
|
202
|
+
} else {
|
|
203
|
+
conflicts++;
|
|
204
|
+
managed.push({ id: session.id, remoteHead: remote?.head, outcome: 'conflict', code: error.code });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Existing indexer preserves accepted managed ordering, FTS and tombstones.
|
|
209
|
+
// Its summary is emitted separately and contains no record bodies.
|
|
210
|
+
if (managed.length) {
|
|
211
|
+
const { runManagedCli } = await import('./managed.js');
|
|
212
|
+
await runManagedCli('index', ['--store', resolve(values.store), '--db', dbPath, '--robot']);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
emit({ command, observed, managed });
|
|
216
|
+
if (conflicts) throw new TrajectoriesError('STALE_HEAD', `${conflicts} managed session(s) conflict; other histories were pulled and indexed. Local branches were preserved.`);
|
|
217
|
+
}
|
package/legacy/src/sync.ts
CHANGED
|
@@ -630,12 +630,26 @@ function retryAt(now: Date, attempts: number): string {
|
|
|
630
630
|
return new Date(now.getTime() + delayMs).toISOString();
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
+
declare const TRAJECTORIES_STANDALONE: boolean;
|
|
634
|
+
declare const TRAJECTORIES_BUNDLED_CLI: boolean;
|
|
635
|
+
|
|
636
|
+
function syncEntry(command: string): string[] {
|
|
637
|
+
// Compiled distributions execute the same private verbs in a fresh process.
|
|
638
|
+
if (typeof TRAJECTORIES_STANDALONE !== "undefined" && TRAJECTORIES_STANDALONE) {
|
|
639
|
+
return [process.execPath, command];
|
|
640
|
+
}
|
|
641
|
+
if (typeof TRAJECTORIES_BUNDLED_CLI !== "undefined" && TRAJECTORIES_BUNDLED_CLI) {
|
|
642
|
+
return [process.execPath, process.argv[1]!, command];
|
|
643
|
+
}
|
|
644
|
+
return [process.execPath, fileURLToPath(new URL("../bin/ledger-core.ts", import.meta.url)), command];
|
|
645
|
+
}
|
|
646
|
+
|
|
633
647
|
function workerEntry(): string[] {
|
|
634
|
-
return
|
|
648
|
+
return syncEntry("__sync-worker");
|
|
635
649
|
}
|
|
636
650
|
|
|
637
651
|
function maintenanceEntry(): string[] {
|
|
638
|
-
return
|
|
652
|
+
return syncEntry("__sync-maintenance");
|
|
639
653
|
}
|
|
640
654
|
|
|
641
655
|
export function workerReservation(
|
|
@@ -863,7 +877,7 @@ async function prepareInChild(options: SyncOptions, scanLimit: number, maxRecord
|
|
|
863
877
|
if (remaining <= 0) throw new SyncPreparationError("timed_out");
|
|
864
878
|
const result = await spawnJsonChild<SyncPreparationReply | PreparationFailure>({ protocol_version: SYNC_PROTOCOL_VERSION,
|
|
865
879
|
db_path: options.dbPath, sources: options.sources, roots: options.roots, scan_limit: scanLimit, max_record_bytes: maxRecordBytes },
|
|
866
|
-
remaining, options.preparationCommand ??
|
|
880
|
+
remaining, options.preparationCommand ?? syncEntry("__sync-prepare"),
|
|
867
881
|
parsePreparationReply, { label: "sync_preparation" });
|
|
868
882
|
if (result.status !== "ok" || !result.reply) throw new SyncPreparationError(result.status);
|
|
869
883
|
if (result.reply.type === "sync_preparation_error") {
|
package/ops/README.md
CHANGED
|
@@ -7,10 +7,40 @@ session/log store, which requires an explicit `index --store` command.
|
|
|
7
7
|
|
|
8
8
|
## Install the CLI
|
|
9
9
|
|
|
10
|
+
### Native CLI
|
|
11
|
+
|
|
12
|
+
Native releases require no Bun, Node or npm. The repository is currently private,
|
|
13
|
+
so GitHub downloads require an authenticated `gh` client with repository access.
|
|
14
|
+
On an Apple Silicon Mac:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
gh release download 'trajectories@0.1.2' --repo archeism/oh-my-ai \
|
|
18
|
+
--pattern 'trajectories-0.1.2-darwin-arm64.tar.gz*'
|
|
19
|
+
shasum -a 256 --check trajectories-0.1.2-darwin-arm64.tar.gz.sha256
|
|
20
|
+
mkdir -p "$HOME/.local/share/trajectories/native"
|
|
21
|
+
tar -xzf trajectories-0.1.2-darwin-arm64.tar.gz -C "$HOME/.local/share/trajectories/native"
|
|
22
|
+
bash "$HOME/.local/share/trajectories/native/trajectories-0.1.2-darwin-arm64/install.sh"
|
|
23
|
+
trajectories --version
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
On Linux, choose `linux-x64` or `linux-arm64`, and use `sha256sum --check`.
|
|
27
|
+
The installer refuses existing commands unless `--force` explicitly authorizes
|
|
28
|
+
replacement. It creates a symlink to the retained release directory; do not
|
|
29
|
+
delete that directory or separate the executable from `lib/`. It does not change
|
|
30
|
+
the search database or enable an indexing service. Ensure `~/.local/bin` is on PATH.
|
|
31
|
+
Local ONNX inference requires glibc 2.28+ on Linux or macOS 14+ on Apple Silicon. Deep Agents SQLite conversion
|
|
32
|
+
still uses the harness's own Python/LangGraph environment.
|
|
33
|
+
|
|
34
|
+
The service installers below are for source/npm installations and invoke Bun.
|
|
35
|
+
For a native service, its command is the installed `trajectories sync` executable
|
|
36
|
+
directly; the CLI's bounded sync and scheduler protocol are unchanged.
|
|
37
|
+
|
|
38
|
+
### npm or source CLI
|
|
39
|
+
|
|
10
40
|
For the npm distribution, use Bun 1.4.2 or newer:
|
|
11
41
|
|
|
12
42
|
```sh
|
|
13
|
-
bun add --global trajectories@0.1.
|
|
43
|
+
bun add --global trajectories@0.1.2
|
|
14
44
|
trajectories --version
|
|
15
45
|
trajectories status --robot
|
|
16
46
|
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Run from an extracted native release kept in a permanent directory.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
RELEASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
5
|
+
BIN_DIR="${TRAJECTORIES_BIN_DIR:-$HOME/.local/bin}"
|
|
6
|
+
FORCE=0
|
|
7
|
+
while [[ $# -gt 0 ]]; do
|
|
8
|
+
case "$1" in
|
|
9
|
+
--bin-dir) [[ $# -ge 2 && -n "$2" ]] || { echo '--bin-dir requires a directory.' >&2; exit 1; }; BIN_DIR="$2"; shift 2 ;;
|
|
10
|
+
--force) FORCE=1; shift ;;
|
|
11
|
+
--help|-h) echo 'Usage: install.sh [--bin-dir DIR] [--force]'; exit 0 ;;
|
|
12
|
+
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
13
|
+
esac
|
|
14
|
+
done
|
|
15
|
+
[[ -x "$RELEASE_DIR/trajectories" && -d "$RELEASE_DIR/lib" ]] || {
|
|
16
|
+
echo 'Keep install.sh, the trajectories executable, and lib/ together in the extracted release.' >&2; exit 1;
|
|
17
|
+
}
|
|
18
|
+
"$RELEASE_DIR/trajectories" --version
|
|
19
|
+
mkdir -p "$BIN_DIR"
|
|
20
|
+
TARGET="$BIN_DIR/trajectories"
|
|
21
|
+
if [[ -e "$TARGET" || -L "$TARGET" ]]; then
|
|
22
|
+
if [[ -L "$TARGET" && "$(readlink "$TARGET")" == "$RELEASE_DIR/trajectories" ]]; then
|
|
23
|
+
echo "Already installed: $TARGET"; exit 0
|
|
24
|
+
fi
|
|
25
|
+
[[ "$FORCE" == 1 && ! -d "$TARGET" ]] || {
|
|
26
|
+
echo "Refusing to overwrite $TARGET; use --force for an explicit cutover." >&2; exit 1;
|
|
27
|
+
}
|
|
28
|
+
fi
|
|
29
|
+
STAGE="$(mktemp -d "$BIN_DIR/.trajectories-install.XXXXXX")"
|
|
30
|
+
trap 'rm -rf "$STAGE"' EXIT
|
|
31
|
+
ln -s "$RELEASE_DIR/trajectories" "$STAGE/trajectories"
|
|
32
|
+
mv -f "$STAGE/trajectories" "$TARGET"
|
|
33
|
+
echo "Installed: $TARGET"
|
|
34
|
+
echo "Keep the release directory: $RELEASE_DIR"
|
|
35
|
+
case ":$PATH:" in *":$BIN_DIR:"*) ;; *) echo "Add $BIN_DIR to PATH." ;; esac
|
package/package.json
CHANGED
|
@@ -1,22 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trajectories",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Portable coding-agent sessions: read, search, convert, and continue across harnesses.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
-
"engines": {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.13.0",
|
|
9
|
+
"bun": ">=1.4.2"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"bin": {
|
|
18
|
+
"trajectories": "./dist/cli.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"docs",
|
|
23
|
+
"legacy/src",
|
|
24
|
+
"legacy/bin",
|
|
25
|
+
"legacy/scripts",
|
|
26
|
+
"legacy/NOTICE.md",
|
|
27
|
+
"ops",
|
|
28
|
+
"skills",
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
11
33
|
"scripts": {
|
|
12
|
-
"build": "tsc -p tsconfig.build.json && bun build src/index.ts --target node --format esm --outfile dist/index.js && bun build src/normalization-runtime.ts --target bun --format esm --outfile dist/normalization-runtime.js && bun build src/cli.ts --target bun --format esm --outfile dist/cli.js && bun -
|
|
34
|
+
"build": "tsc -p tsconfig.build.json && bun build src/index.ts --target node --format esm --outfile dist/index.js && bun build src/normalization-runtime.ts --target bun --format esm --outfile dist/normalization-runtime.js && bun build src/cli.ts --target bun --format esm --external onnxruntime-node --define TRAJECTORIES_BUNDLED_CLI=true --outfile dist/cli.js && bun scripts/build-assets.ts",
|
|
35
|
+
"build:native": "bun run build && bun scripts/build-native.ts",
|
|
13
36
|
"test": "bun test ./test/*.test.ts",
|
|
14
37
|
"verify": "bun run build && tsc -p tsconfig.legacy.json && bun run test && bun test ./legacy/tests",
|
|
15
|
-
"test:
|
|
38
|
+
"test:native": "bun scripts/check-native.ts",
|
|
39
|
+
"test:package": "bun run build && bun test ./smoke/package.test.ts && bun run test:native"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@letta-ai/trajectory": "0.4.3"
|
|
43
|
+
},
|
|
44
|
+
"optionalDependencies": {
|
|
45
|
+
"onnxruntime-node": "^1.29.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.20.1",
|
|
49
|
+
"@types/bun": "1.3.14",
|
|
50
|
+
"typescript": "^5.9.3"
|
|
51
|
+
},
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/archeism/oh-my-ai.git",
|
|
55
|
+
"directory": "packages/trajectories"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
16
59
|
},
|
|
17
|
-
"
|
|
18
|
-
"optionalDependencies": { "onnxruntime-node": "^1.29.0" },
|
|
19
|
-
"devDependencies": { "@types/node": "^22.20.1", "@types/bun": "1.3.14", "typescript": "^5.9.3" },
|
|
20
|
-
"repository": { "type": "git", "url": "git+https://github.com/archeism/oh-my-ai.git", "directory": "packages/trajectories" },
|
|
21
|
-
"publishConfig": { "access": "public" }
|
|
60
|
+
"gitHead": "fa1591e54eaeec7d48a8edfaa086d46fd9b2d4f3"
|
|
22
61
|
}
|
package/dist/cli.d.ts
DELETED