@msvesper/vesper-link 0.1.4
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/LICENSE.md +44 -0
- package/README.md +26 -0
- package/dist/atomic-json.d.ts +3 -0
- package/dist/atomic-json.js +47 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +170 -0
- package/dist/collectors/claude.d.ts +5 -0
- package/dist/collectors/claude.js +42 -0
- package/dist/collectors/codex.d.ts +5 -0
- package/dist/collectors/codex.js +65 -0
- package/dist/collectors/copilot.d.ts +5 -0
- package/dist/collectors/copilot.js +64 -0
- package/dist/collectors/index.d.ts +4 -0
- package/dist/collectors/index.js +3 -0
- package/dist/collectors/types.d.ts +16 -0
- package/dist/collectors/types.js +1 -0
- package/dist/companion.d.ts +12 -0
- package/dist/companion.js +62 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +41 -0
- package/dist/contracts.d.ts +83 -0
- package/dist/contracts.js +144 -0
- package/dist/diagnostics.d.ts +37 -0
- package/dist/diagnostics.js +64 -0
- package/dist/fs-utils.d.ts +5 -0
- package/dist/fs-utils.js +69 -0
- package/dist/hook-report.d.ts +8 -0
- package/dist/hook-report.js +21 -0
- package/dist/http-client.d.ts +25 -0
- package/dist/http-client.js +228 -0
- package/dist/install-args.d.ts +7 -0
- package/dist/install-args.js +35 -0
- package/dist/installation.d.ts +59 -0
- package/dist/installation.js +524 -0
- package/dist/installer.d.ts +59 -0
- package/dist/installer.js +214 -0
- package/dist/lock.d.ts +5 -0
- package/dist/lock.js +57 -0
- package/dist/managed-runtime.d.ts +33 -0
- package/dist/managed-runtime.js +130 -0
- package/dist/memory-mcp.d.ts +27 -0
- package/dist/memory-mcp.js +155 -0
- package/dist/operational-log.d.ts +17 -0
- package/dist/operational-log.js +48 -0
- package/dist/paths.d.ts +23 -0
- package/dist/paths.js +36 -0
- package/dist/pending.d.ts +7 -0
- package/dist/pending.js +35 -0
- package/dist/process-runner.d.ts +11 -0
- package/dist/process-runner.js +21 -0
- package/dist/provider-integrations.d.ts +42 -0
- package/dist/provider-integrations.js +339 -0
- package/dist/queue.d.ts +19 -0
- package/dist/queue.js +129 -0
- package/dist/scheduler.d.ts +51 -0
- package/dist/scheduler.js +176 -0
- package/dist/session.d.ts +50 -0
- package/dist/session.js +12 -0
- package/dist/source.d.ts +11 -0
- package/dist/source.js +45 -0
- package/dist/state.d.ts +11 -0
- package/dist/state.js +37 -0
- package/dist/sync.d.ts +18 -0
- package/dist/sync.js +247 -0
- package/dist/transport.d.ts +27 -0
- package/dist/transport.js +400 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export declare const SCHEMA_VERSION: 1;
|
|
2
|
+
export declare const MAX_FILE_BYTES: number;
|
|
3
|
+
export declare const MAX_MANIFEST_BYTES: number;
|
|
4
|
+
export declare const PROVIDERS: readonly ["claude-code", "github-copilot", "openai-codex"];
|
|
5
|
+
export type Provider = typeof PROVIDERS[number];
|
|
6
|
+
export declare const PROVIDER_FORMATS: {
|
|
7
|
+
readonly 'claude-code': "claude-code-session-jsonl";
|
|
8
|
+
readonly 'github-copilot': "github-copilot-events-jsonl";
|
|
9
|
+
readonly 'openai-codex': "openai-codex-rollout-jsonl";
|
|
10
|
+
};
|
|
11
|
+
export type ProviderFormat = typeof PROVIDER_FORMATS[Provider];
|
|
12
|
+
export interface TranscriptManifest {
|
|
13
|
+
schemaVersion: typeof SCHEMA_VERSION;
|
|
14
|
+
provider: Provider;
|
|
15
|
+
format: string;
|
|
16
|
+
formatVersion: string;
|
|
17
|
+
providerSessionId: string;
|
|
18
|
+
projectKey?: string;
|
|
19
|
+
cwdUri?: string;
|
|
20
|
+
sourceTranscriptUri: string;
|
|
21
|
+
sourceCreatedAt?: string;
|
|
22
|
+
sourceUpdatedAt: string;
|
|
23
|
+
byteLength: number;
|
|
24
|
+
sha256: string;
|
|
25
|
+
}
|
|
26
|
+
export type UploadStatus = 'needs_content' | 'archived' | 'updated' | 'already_archived';
|
|
27
|
+
export type ProcessingStatus = 'pending_upload' | 'archived' | 'parse_pending' | 'unsupported' | 'analysis_paused_memory_disabled' | 'analyzing' | 'result_persisted' | 'fanout_pending' | 'completed' | 'analysis_partial' | 'retry_wait' | 'failed_permanent' | 'analysis_skipped_empty' | 'analysis_skipped_internal' | 'analysis_stale';
|
|
28
|
+
export interface TranscriptResponse {
|
|
29
|
+
requestId: string;
|
|
30
|
+
transcriptId: string;
|
|
31
|
+
uploadStatus: UploadStatus;
|
|
32
|
+
processingStatus: ProcessingStatus;
|
|
33
|
+
currentSha256: string | null;
|
|
34
|
+
}
|
|
35
|
+
export interface HealthResponse {
|
|
36
|
+
requestId: string;
|
|
37
|
+
archiveReady: boolean;
|
|
38
|
+
memoryReady: boolean;
|
|
39
|
+
serverVersion: string;
|
|
40
|
+
minimumClientVersion: string;
|
|
41
|
+
}
|
|
42
|
+
export interface CapabilitiesResponse {
|
|
43
|
+
requestId: string;
|
|
44
|
+
canUploadSessions: boolean;
|
|
45
|
+
canSearchMemory: boolean;
|
|
46
|
+
canAddFact: boolean;
|
|
47
|
+
sourceTrust: 'trusted' | 'uploader';
|
|
48
|
+
}
|
|
49
|
+
export interface ErrorEnvelope {
|
|
50
|
+
requestId: string;
|
|
51
|
+
error: {
|
|
52
|
+
code: string;
|
|
53
|
+
message: string;
|
|
54
|
+
retryable: boolean;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface MemorySearchItem {
|
|
58
|
+
content: string;
|
|
59
|
+
score: number;
|
|
60
|
+
schemaType: string;
|
|
61
|
+
source: 'extracted' | 'files' | 'kg' | 'wiki' | 'signals';
|
|
62
|
+
provenance: {
|
|
63
|
+
id: string;
|
|
64
|
+
signalId?: string;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export interface MemorySearchResponse {
|
|
68
|
+
requestId: string;
|
|
69
|
+
results: MemorySearchItem[];
|
|
70
|
+
}
|
|
71
|
+
export interface AddFactResponse {
|
|
72
|
+
requestId: string;
|
|
73
|
+
id: string;
|
|
74
|
+
schemaType: string;
|
|
75
|
+
status: 'written' | 'already_present';
|
|
76
|
+
}
|
|
77
|
+
export declare function isProvider(value: unknown): value is Provider;
|
|
78
|
+
export declare function validateManifest(manifest: TranscriptManifest): void;
|
|
79
|
+
export declare function parseTranscriptResponse(value: unknown): TranscriptResponse;
|
|
80
|
+
export declare function parseHealthResponse(value: unknown): HealthResponse;
|
|
81
|
+
export declare function parseCapabilitiesResponse(value: unknown): CapabilitiesResponse;
|
|
82
|
+
export declare function parseMemorySearchResponse(value: unknown): MemorySearchResponse;
|
|
83
|
+
export declare function parseAddFactResponse(value: unknown): AddFactResponse;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export const SCHEMA_VERSION = 1;
|
|
2
|
+
export const MAX_FILE_BYTES = 50 * 1024 * 1024;
|
|
3
|
+
export const MAX_MANIFEST_BYTES = 16 * 1024;
|
|
4
|
+
export const PROVIDERS = ['claude-code', 'github-copilot', 'openai-codex'];
|
|
5
|
+
export const PROVIDER_FORMATS = {
|
|
6
|
+
'claude-code': 'claude-code-session-jsonl',
|
|
7
|
+
'github-copilot': 'github-copilot-events-jsonl',
|
|
8
|
+
'openai-codex': 'openai-codex-rollout-jsonl',
|
|
9
|
+
};
|
|
10
|
+
const LOWERCASE_SHA256 = /^[a-f0-9]{64}$/;
|
|
11
|
+
function hasControlCharacters(value) {
|
|
12
|
+
return [...value].some((character) => {
|
|
13
|
+
const code = character.charCodeAt(0);
|
|
14
|
+
return code <= 31 || code === 127;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function utf8Length(value) {
|
|
18
|
+
return Buffer.byteLength(value, 'utf8');
|
|
19
|
+
}
|
|
20
|
+
function isCanonicalInstant(value) {
|
|
21
|
+
const parsed = new Date(value);
|
|
22
|
+
return !Number.isNaN(parsed.getTime()) && parsed.toISOString() === value;
|
|
23
|
+
}
|
|
24
|
+
export function isProvider(value) {
|
|
25
|
+
return typeof value === 'string' && PROVIDERS.includes(value);
|
|
26
|
+
}
|
|
27
|
+
export function validateManifest(manifest) {
|
|
28
|
+
if (manifest.schemaVersion !== SCHEMA_VERSION) {
|
|
29
|
+
throw new Error('schemaVersion must be 1');
|
|
30
|
+
}
|
|
31
|
+
if (!isProvider(manifest.provider)) {
|
|
32
|
+
throw new Error('unsupported provider');
|
|
33
|
+
}
|
|
34
|
+
if (!manifest.format || utf8Length(manifest.format) > 128 || hasControlCharacters(manifest.format)) {
|
|
35
|
+
throw new Error('format must be 1-128 UTF-8 bytes without control characters');
|
|
36
|
+
}
|
|
37
|
+
if (!manifest.formatVersion || utf8Length(manifest.formatVersion) > 128 || hasControlCharacters(manifest.formatVersion)) {
|
|
38
|
+
throw new Error('formatVersion must be 1-128 UTF-8 bytes without control characters');
|
|
39
|
+
}
|
|
40
|
+
if (!manifest.providerSessionId || utf8Length(manifest.providerSessionId) > 512 || hasControlCharacters(manifest.providerSessionId)) {
|
|
41
|
+
throw new Error('providerSessionId must be 1-512 UTF-8 bytes without control characters');
|
|
42
|
+
}
|
|
43
|
+
if (manifest.cwdUri !== undefined && utf8Length(manifest.cwdUri) > 4096) {
|
|
44
|
+
throw new Error('cwdUri must not exceed 4096 UTF-8 bytes');
|
|
45
|
+
}
|
|
46
|
+
if (!manifest.sourceTranscriptUri.startsWith('file://') || utf8Length(manifest.sourceTranscriptUri) > 4096
|
|
47
|
+
|| hasControlCharacters(manifest.sourceTranscriptUri)) {
|
|
48
|
+
throw new Error('sourceTranscriptUri must be a file URI of at most 4096 UTF-8 bytes without control characters');
|
|
49
|
+
}
|
|
50
|
+
if (manifest.projectKey !== undefined
|
|
51
|
+
&& (!/^[A-Za-z0-9-]+$/.test(manifest.projectKey) || utf8Length(manifest.projectKey) > 96)) {
|
|
52
|
+
throw new Error('projectKey must contain only letters, numbers, and hyphens and not exceed 96 UTF-8 bytes');
|
|
53
|
+
}
|
|
54
|
+
if (manifest.sourceCreatedAt !== undefined && !isCanonicalInstant(manifest.sourceCreatedAt)) {
|
|
55
|
+
throw new Error('sourceCreatedAt must be a canonical ISO-8601 instant');
|
|
56
|
+
}
|
|
57
|
+
if (!isCanonicalInstant(manifest.sourceUpdatedAt)) {
|
|
58
|
+
throw new Error('sourceUpdatedAt must be a canonical ISO-8601 instant');
|
|
59
|
+
}
|
|
60
|
+
if (!Number.isSafeInteger(manifest.byteLength) || manifest.byteLength < 0 || manifest.byteLength > MAX_FILE_BYTES) {
|
|
61
|
+
throw new Error(`byteLength must be an integer between 0 and ${MAX_FILE_BYTES}`);
|
|
62
|
+
}
|
|
63
|
+
if (!LOWERCASE_SHA256.test(manifest.sha256)) {
|
|
64
|
+
throw new Error('sha256 must be 64 lowercase hexadecimal characters');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function parseTranscriptResponse(value) {
|
|
68
|
+
if (!value || typeof value !== 'object') {
|
|
69
|
+
throw new Error('response must be an object');
|
|
70
|
+
}
|
|
71
|
+
const response = value;
|
|
72
|
+
const uploadStatuses = ['needs_content', 'archived', 'updated', 'already_archived'];
|
|
73
|
+
const processingStatuses = [
|
|
74
|
+
'pending_upload', 'archived', 'parse_pending', 'unsupported',
|
|
75
|
+
'analysis_paused_memory_disabled', 'analyzing', 'result_persisted',
|
|
76
|
+
'fanout_pending', 'completed', 'analysis_partial', 'retry_wait', 'failed_permanent',
|
|
77
|
+
'analysis_skipped_empty', 'analysis_skipped_internal', 'analysis_stale',
|
|
78
|
+
];
|
|
79
|
+
if (typeof response.requestId !== 'string' || typeof response.transcriptId !== 'string'
|
|
80
|
+
|| !LOWERCASE_SHA256.test(response.transcriptId)
|
|
81
|
+
|| !uploadStatuses.includes(String(response.uploadStatus))
|
|
82
|
+
|| !processingStatuses.includes(String(response.processingStatus))
|
|
83
|
+
|| !(response.currentSha256 === null || (typeof response.currentSha256 === 'string' && LOWERCASE_SHA256.test(response.currentSha256)))) {
|
|
84
|
+
throw new Error('malformed transcript response');
|
|
85
|
+
}
|
|
86
|
+
return response;
|
|
87
|
+
}
|
|
88
|
+
export function parseHealthResponse(value) {
|
|
89
|
+
if (!value || typeof value !== 'object') {
|
|
90
|
+
throw new Error('health response must be an object');
|
|
91
|
+
}
|
|
92
|
+
const health = value;
|
|
93
|
+
if (typeof health.requestId !== 'string' || typeof health.archiveReady !== 'boolean'
|
|
94
|
+
|| typeof health.memoryReady !== 'boolean' || typeof health.serverVersion !== 'string'
|
|
95
|
+
|| typeof health.minimumClientVersion !== 'string') {
|
|
96
|
+
throw new Error('malformed health response');
|
|
97
|
+
}
|
|
98
|
+
return health;
|
|
99
|
+
}
|
|
100
|
+
export function parseCapabilitiesResponse(value) {
|
|
101
|
+
if (!value || typeof value !== 'object') {
|
|
102
|
+
throw new Error('capabilities response must be an object');
|
|
103
|
+
}
|
|
104
|
+
const capabilities = value;
|
|
105
|
+
if (typeof capabilities.requestId !== 'string'
|
|
106
|
+
|| typeof capabilities.canUploadSessions !== 'boolean'
|
|
107
|
+
|| typeof capabilities.canSearchMemory !== 'boolean'
|
|
108
|
+
|| typeof capabilities.canAddFact !== 'boolean'
|
|
109
|
+
|| (capabilities.sourceTrust !== 'trusted' && capabilities.sourceTrust !== 'uploader')) {
|
|
110
|
+
throw new Error('malformed capabilities response');
|
|
111
|
+
}
|
|
112
|
+
return capabilities;
|
|
113
|
+
}
|
|
114
|
+
export function parseMemorySearchResponse(value) {
|
|
115
|
+
if (!value || typeof value !== 'object') {
|
|
116
|
+
throw new Error('memory search response must be an object');
|
|
117
|
+
}
|
|
118
|
+
const response = value;
|
|
119
|
+
if (typeof response.requestId !== 'string' || !Array.isArray(response.results) || response.results.length > 10) {
|
|
120
|
+
throw new Error('malformed memory search response');
|
|
121
|
+
}
|
|
122
|
+
for (const item of response.results) {
|
|
123
|
+
if (!item || typeof item !== 'object' || typeof item.content !== 'string'
|
|
124
|
+
|| typeof item.score !== 'number' || !Number.isFinite(item.score)
|
|
125
|
+
|| typeof item.schemaType !== 'string' || typeof item.source !== 'string'
|
|
126
|
+
|| !item.provenance || typeof item.provenance.id !== 'string'
|
|
127
|
+
|| (item.provenance.signalId !== undefined && typeof item.provenance.signalId !== 'string')) {
|
|
128
|
+
throw new Error('malformed memory search item');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return response;
|
|
132
|
+
}
|
|
133
|
+
export function parseAddFactResponse(value) {
|
|
134
|
+
if (!value || typeof value !== 'object') {
|
|
135
|
+
throw new Error('add fact response must be an object');
|
|
136
|
+
}
|
|
137
|
+
const response = value;
|
|
138
|
+
if (typeof response.requestId !== 'string' || typeof response.id !== 'string'
|
|
139
|
+
|| typeof response.schemaType !== 'string'
|
|
140
|
+
|| (response.status !== 'written' && response.status !== 'already_present')) {
|
|
141
|
+
throw new Error('malformed add fact response');
|
|
142
|
+
}
|
|
143
|
+
return response;
|
|
144
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getCapabilities, getHealth } from './http-client.js';
|
|
2
|
+
import { type InstallationStatus } from './installation.js';
|
|
3
|
+
import type { LinkPaths } from './paths.js';
|
|
4
|
+
import { type ProcessRunner } from './process-runner.js';
|
|
5
|
+
import { type RemoteTransport } from './transport.js';
|
|
6
|
+
export interface QueueStatus {
|
|
7
|
+
pendingMarkers: number;
|
|
8
|
+
pendingSessions: number;
|
|
9
|
+
uploadedSessions: number;
|
|
10
|
+
failedSessions: number;
|
|
11
|
+
}
|
|
12
|
+
export interface StatusReport extends InstallationStatus {
|
|
13
|
+
queue: QueueStatus;
|
|
14
|
+
enrollment?: {
|
|
15
|
+
state: string;
|
|
16
|
+
tailnetName?: string;
|
|
17
|
+
nodeName?: string;
|
|
18
|
+
ipv4?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function getStatusReport(paths: LinkPaths, runner?: ProcessRunner): Promise<StatusReport>;
|
|
22
|
+
export interface DoctorReport {
|
|
23
|
+
healthy: boolean;
|
|
24
|
+
checks: {
|
|
25
|
+
config: boolean;
|
|
26
|
+
host: boolean;
|
|
27
|
+
installation: InstallationStatus;
|
|
28
|
+
queue: QueueStatus;
|
|
29
|
+
health?: Awaited<ReturnType<typeof getHealth>>;
|
|
30
|
+
capabilities?: Awaited<ReturnType<typeof getCapabilities>>;
|
|
31
|
+
error?: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare function getDoctorReport(paths: LinkPaths, runner?: ProcessRunner, remote?: {
|
|
35
|
+
health: typeof getHealth;
|
|
36
|
+
capabilities: typeof getCapabilities;
|
|
37
|
+
}, acquireTransport?: (paths: LinkPaths, hostUrl: string) => Promise<RemoteTransport>): Promise<DoctorReport>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { readConfig } from './config.js';
|
|
2
|
+
import { getCapabilities, getHealth } from './http-client.js';
|
|
3
|
+
import { getInstallationStatus } from './installation.js';
|
|
4
|
+
import { readPendingMarkers } from './pending.js';
|
|
5
|
+
import { NodeProcessRunner } from './process-runner.js';
|
|
6
|
+
import { readSessionStates } from './state.js';
|
|
7
|
+
import { acquireRemoteTransport } from './transport.js';
|
|
8
|
+
export async function getStatusReport(paths, runner = new NodeProcessRunner()) {
|
|
9
|
+
const [installation, pending, states, progress] = await Promise.all([
|
|
10
|
+
getInstallationStatus(paths, runner), readPendingMarkers(paths.pendingDir), readSessionStates(paths.sessionsDir),
|
|
11
|
+
import('./atomic-json.js').then(({ readJsonFile }) => readJsonFile(paths.installProgressPath)),
|
|
12
|
+
]);
|
|
13
|
+
const queue = { pendingMarkers: pending.length, pendingSessions: 0, uploadedSessions: 0, failedSessions: 0 };
|
|
14
|
+
for (const state of states.values()) {
|
|
15
|
+
if (state.status === 'pending') {
|
|
16
|
+
queue.pendingSessions += 1;
|
|
17
|
+
}
|
|
18
|
+
if (state.status === 'uploaded') {
|
|
19
|
+
queue.uploadedSessions += 1;
|
|
20
|
+
}
|
|
21
|
+
if (state.status === 'failed') {
|
|
22
|
+
queue.failedSessions += 1;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const enrollment = progress && typeof progress.state === 'string' ? {
|
|
26
|
+
state: progress.state,
|
|
27
|
+
tailnetName: typeof progress.tailnetName === 'string' ? progress.tailnetName : undefined,
|
|
28
|
+
nodeName: typeof progress.nodeName === 'string' ? progress.nodeName : undefined,
|
|
29
|
+
ipv4: typeof progress.ipv4 === 'string' ? progress.ipv4 : undefined,
|
|
30
|
+
} : undefined;
|
|
31
|
+
return { ...installation, queue, enrollment };
|
|
32
|
+
}
|
|
33
|
+
export async function getDoctorReport(paths, runner = new NodeProcessRunner(), remote = { health: getHealth, capabilities: getCapabilities }, acquireTransport = (linkPaths, hostUrl) => acquireRemoteTransport({ paths: linkPaths, hostUrl })) {
|
|
34
|
+
const status = await getStatusReport(paths, runner);
|
|
35
|
+
const installation = status;
|
|
36
|
+
const checks = { config: false, host: false, installation, queue: status.queue };
|
|
37
|
+
try {
|
|
38
|
+
const config = await readConfig(paths.configPath);
|
|
39
|
+
checks.config = true;
|
|
40
|
+
const transport = await acquireTransport(paths, config.host);
|
|
41
|
+
let health;
|
|
42
|
+
let capabilities;
|
|
43
|
+
try {
|
|
44
|
+
[health, capabilities] = await Promise.all([
|
|
45
|
+
remote.health(transport.hostUrl, transport.fetch), remote.capabilities(transport.hostUrl, transport.fetch),
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
await transport.release();
|
|
50
|
+
}
|
|
51
|
+
checks.host = health.archiveReady && capabilities.canUploadSessions;
|
|
52
|
+
checks.health = health;
|
|
53
|
+
checks.capabilities = capabilities;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
checks.error = error instanceof Error ? error.message : String(error);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
healthy: checks.config && checks.host && installation.installed && !installation.paused
|
|
60
|
+
&& installation.claudeHookInstalled && installation.copilotHookInstalled && installation.schedulerInstalled
|
|
61
|
+
&& (installation.sourceTrust !== 'trusted' || installation.trustedIntegrationsInstalled),
|
|
62
|
+
checks,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function walkFiles(root: string, accept: (filePath: string) => boolean): Promise<string[]>;
|
|
2
|
+
export declare function pathToOpaqueFileUri(filePath: string): string;
|
|
3
|
+
export declare function normalizeProjectKey(value: string): string | undefined;
|
|
4
|
+
export declare function pathToProjectKey(filePath: string): string | undefined;
|
|
5
|
+
export declare function readFirstLine(filePath: string, maxBytes?: number): Promise<string>;
|
package/dist/fs-utils.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
const MAX_PROJECT_KEY_LENGTH = 96;
|
|
5
|
+
export async function walkFiles(root, accept) {
|
|
6
|
+
const files = [];
|
|
7
|
+
const pending = [root];
|
|
8
|
+
while (pending.length > 0) {
|
|
9
|
+
const current = pending.pop();
|
|
10
|
+
let entries;
|
|
11
|
+
try {
|
|
12
|
+
entries = await fs.readdir(current, { withFileTypes: true });
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
const code = error.code;
|
|
16
|
+
if (code === 'ENOENT' || code === 'EACCES' || code === 'EPERM') {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
const entryPath = path.join(current, entry.name);
|
|
23
|
+
if (entry.isDirectory()) {
|
|
24
|
+
pending.push(entryPath);
|
|
25
|
+
}
|
|
26
|
+
else if (entry.isFile() && accept(entryPath)) {
|
|
27
|
+
files.push(entryPath);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return files;
|
|
32
|
+
}
|
|
33
|
+
export function pathToOpaqueFileUri(filePath) {
|
|
34
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
35
|
+
if (/^[A-Za-z]:\//.test(normalized)) {
|
|
36
|
+
return `file:///${normalized}`;
|
|
37
|
+
}
|
|
38
|
+
return normalized.startsWith('/') ? `file://${normalized}` : `file:///${normalized}`;
|
|
39
|
+
}
|
|
40
|
+
export function normalizeProjectKey(value) {
|
|
41
|
+
const key = value.replace(/[^A-Za-z0-9]/g, '-');
|
|
42
|
+
if (!key) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
if (key.length <= MAX_PROJECT_KEY_LENGTH) {
|
|
46
|
+
return key;
|
|
47
|
+
}
|
|
48
|
+
const suffix = createHash('sha256').update(key).digest('hex').slice(0, 16);
|
|
49
|
+
return `${key.slice(0, MAX_PROJECT_KEY_LENGTH - suffix.length - 1)}-${suffix}`;
|
|
50
|
+
}
|
|
51
|
+
export function pathToProjectKey(filePath) {
|
|
52
|
+
return normalizeProjectKey(filePath);
|
|
53
|
+
}
|
|
54
|
+
export async function readFirstLine(filePath, maxBytes = 64 * 1024) {
|
|
55
|
+
const handle = await fs.open(filePath, 'r');
|
|
56
|
+
try {
|
|
57
|
+
const buffer = Buffer.alloc(maxBytes);
|
|
58
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
59
|
+
const text = buffer.subarray(0, bytesRead).toString('utf8');
|
|
60
|
+
const newline = text.indexOf('\n');
|
|
61
|
+
if (newline < 0 && bytesRead === maxBytes) {
|
|
62
|
+
throw new Error(`First JSONL record exceeds ${maxBytes} bytes`);
|
|
63
|
+
}
|
|
64
|
+
return (newline >= 0 ? text.slice(0, newline) : text).replace(/\r$/, '');
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await handle.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Provider } from './contracts.js';
|
|
2
|
+
import type { LinkPaths } from './paths.js';
|
|
3
|
+
export interface HookReport {
|
|
4
|
+
provider: Provider;
|
|
5
|
+
providerSessionId: string;
|
|
6
|
+
transcriptPath: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function resolveHookReport(values: Map<string, string>, stdin: Record<string, unknown> | null, paths: LinkPaths): HookReport;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { isProvider } from './contracts.js';
|
|
3
|
+
function stringValue(value) {
|
|
4
|
+
return typeof value === 'string' && value ? value : undefined;
|
|
5
|
+
}
|
|
6
|
+
export function resolveHookReport(values, stdin, paths) {
|
|
7
|
+
const providerValue = values.get('provider') ?? stringValue(stdin?.provider);
|
|
8
|
+
const providerSessionId = values.get('session')
|
|
9
|
+
?? stringValue(stdin?.session_id)
|
|
10
|
+
?? stringValue(stdin?.sessionId);
|
|
11
|
+
let transcriptPath = values.get('path')
|
|
12
|
+
?? stringValue(stdin?.transcript_path)
|
|
13
|
+
?? stringValue(stdin?.transcriptPath);
|
|
14
|
+
if (!transcriptPath && providerValue === 'github-copilot' && providerSessionId) {
|
|
15
|
+
transcriptPath = path.join(paths.copilotSessionsDir, providerSessionId, 'events.jsonl');
|
|
16
|
+
}
|
|
17
|
+
if (!isProvider(providerValue) || !providerSessionId || !transcriptPath) {
|
|
18
|
+
throw new Error('report-session requires --provider, --session, and --path');
|
|
19
|
+
}
|
|
20
|
+
return { provider: providerValue, providerSessionId, transcriptPath };
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type AddFactResponse, type CapabilitiesResponse, type HealthResponse, type MemorySearchResponse, type TranscriptManifest, type TranscriptResponse } from './contracts.js';
|
|
2
|
+
export declare function getHealth(host: string, fetchImpl?: typeof fetch): Promise<HealthResponse>;
|
|
3
|
+
export declare function getCapabilities(host: string, fetchImpl?: typeof fetch): Promise<CapabilitiesResponse>;
|
|
4
|
+
export declare function searchMemory(host: string, query: string, limit?: number, fetchImpl?: typeof fetch): Promise<MemorySearchResponse>;
|
|
5
|
+
export declare function addFact(host: string, fact: string, fetchImpl?: typeof fetch): Promise<AddFactResponse>;
|
|
6
|
+
export declare function getTranscriptStatus(host: string, transcriptId: string, fetchImpl?: typeof fetch): Promise<TranscriptResponse>;
|
|
7
|
+
export declare function postManifest(host: string, manifest: TranscriptManifest, fetchImpl?: typeof fetch): Promise<TranscriptResponse>;
|
|
8
|
+
export declare function putContent(options: {
|
|
9
|
+
host: string;
|
|
10
|
+
transcriptId: string;
|
|
11
|
+
filePath: string;
|
|
12
|
+
byteLength: number;
|
|
13
|
+
fetchImpl?: typeof fetch;
|
|
14
|
+
}): Promise<TranscriptResponse>;
|
|
15
|
+
export declare class TaskUploadError extends Error {
|
|
16
|
+
readonly code: string;
|
|
17
|
+
readonly httpStatus?: number | undefined;
|
|
18
|
+
constructor(code: string, message: string, httpStatus?: number | undefined);
|
|
19
|
+
}
|
|
20
|
+
export declare class SystemicUploadError extends Error {
|
|
21
|
+
readonly code: string;
|
|
22
|
+
readonly httpStatus?: number | undefined;
|
|
23
|
+
readonly retryAfterUntil?: string | undefined;
|
|
24
|
+
constructor(code: string, message: string, httpStatus?: number | undefined, retryAfterUntil?: string | undefined);
|
|
25
|
+
}
|