@sumicom/quicksave 0.3.1 → 0.5.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/dist/ai/claudeCodeService.d.ts +89 -0
- package/dist/ai/claudeCodeService.d.ts.map +1 -0
- package/dist/ai/claudeCodeService.js +602 -0
- package/dist/ai/claudeCodeService.js.map +1 -0
- package/dist/ai/sessionStore.d.ts +31 -0
- package/dist/ai/sessionStore.d.ts.map +1 -0
- package/dist/ai/sessionStore.js +72 -0
- package/dist/ai/sessionStore.js.map +1 -0
- package/dist/ai/sessionStore.test.d.ts +2 -0
- package/dist/ai/sessionStore.test.d.ts.map +1 -0
- package/dist/ai/sessionStore.test.js +99 -0
- package/dist/ai/sessionStore.test.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +39 -0
- package/dist/config.js.map +1 -1
- package/dist/connection/connection.d.ts +16 -17
- package/dist/connection/connection.d.ts.map +1 -1
- package/dist/connection/connection.js +72 -57
- package/dist/connection/connection.js.map +1 -1
- package/dist/connection/{signaling.d.ts → relay.d.ts} +3 -5
- package/dist/connection/relay.d.ts.map +1 -0
- package/dist/connection/{signaling.js → relay.js} +18 -19
- package/dist/connection/relay.js.map +1 -0
- package/dist/git/operations.d.ts +27 -1
- package/dist/git/operations.d.ts.map +1 -1
- package/dist/git/operations.js +113 -4
- package/dist/git/operations.js.map +1 -1
- package/dist/git/operations.test.js +69 -1
- package/dist/git/operations.test.js.map +1 -1
- package/dist/handlers/messageHandler.d.ts +31 -4
- package/dist/handlers/messageHandler.d.ts.map +1 -1
- package/dist/handlers/messageHandler.js +491 -62
- package/dist/handlers/messageHandler.js.map +1 -1
- package/dist/handlers/messageHandler.test.js +130 -2
- package/dist/handlers/messageHandler.test.js.map +1 -1
- package/dist/index.js +229 -89
- package/dist/index.js.map +1 -1
- package/dist/service/ensureDaemon.d.ts +23 -0
- package/dist/service/ensureDaemon.d.ts.map +1 -0
- package/dist/service/ensureDaemon.js +138 -0
- package/dist/service/ensureDaemon.js.map +1 -0
- package/dist/service/index.d.ts +8 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +8 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/ipc.test.d.ts +2 -0
- package/dist/service/ipc.test.d.ts.map +1 -0
- package/dist/service/ipc.test.js +140 -0
- package/dist/service/ipc.test.js.map +1 -0
- package/dist/service/ipcClient.d.ts +36 -0
- package/dist/service/ipcClient.d.ts.map +1 -0
- package/dist/service/ipcClient.js +167 -0
- package/dist/service/ipcClient.js.map +1 -0
- package/dist/service/ipcServer.d.ts +53 -0
- package/dist/service/ipcServer.d.ts.map +1 -0
- package/dist/service/ipcServer.js +245 -0
- package/dist/service/ipcServer.js.map +1 -0
- package/dist/service/run.d.ts +13 -0
- package/dist/service/run.d.ts.map +1 -0
- package/dist/service/run.js +292 -0
- package/dist/service/run.js.map +1 -0
- package/dist/service/singleton.d.ts +31 -0
- package/dist/service/singleton.d.ts.map +1 -0
- package/dist/service/singleton.js +96 -0
- package/dist/service/singleton.js.map +1 -0
- package/dist/service/singleton.test.d.ts +2 -0
- package/dist/service/singleton.test.d.ts.map +1 -0
- package/dist/service/singleton.test.js +39 -0
- package/dist/service/singleton.test.js.map +1 -0
- package/dist/service/stateStore.d.ts +11 -0
- package/dist/service/stateStore.d.ts.map +1 -0
- package/dist/service/stateStore.js +35 -0
- package/dist/service/stateStore.js.map +1 -0
- package/dist/service/types.d.ts +111 -0
- package/dist/service/types.d.ts.map +1 -0
- package/dist/service/types.js +76 -0
- package/dist/service/types.js.map +1 -0
- package/package.json +3 -2
- package/dist/connection/signaling.d.ts.map +0 -1
- package/dist/connection/signaling.js.map +0 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL persistence for Claude session message history.
|
|
3
|
+
*
|
|
4
|
+
* Each session gets a file at:
|
|
5
|
+
* ~/.quicksave/state/sessions/<session-id>/messages.jsonl
|
|
6
|
+
*
|
|
7
|
+
* One JSON object per line, matching ClaudeHistoryMessage shape.
|
|
8
|
+
* Append-only writes; full-file reads on cold resume.
|
|
9
|
+
*/
|
|
10
|
+
import { appendFileSync, readFileSync, mkdirSync, existsSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { getSessionsDir } from '../service/singleton.js';
|
|
13
|
+
/**
|
|
14
|
+
* Return the directory for a specific session's persistent data.
|
|
15
|
+
* Creates the directory if it doesn't exist.
|
|
16
|
+
*/
|
|
17
|
+
export function getSessionDir(sessionId) {
|
|
18
|
+
const dir = join(getSessionsDir(), sessionId);
|
|
19
|
+
if (!existsSync(dir)) {
|
|
20
|
+
mkdirSync(dir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Return the path to a session's messages JSONL file.
|
|
26
|
+
*/
|
|
27
|
+
export function getMessagesFilePath(sessionId) {
|
|
28
|
+
return join(getSessionDir(sessionId), 'messages.jsonl');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Append a single message to the session's JSONL file.
|
|
32
|
+
* Errors are logged, not thrown.
|
|
33
|
+
*/
|
|
34
|
+
export function appendMessageToJSONL(sessionId, message) {
|
|
35
|
+
try {
|
|
36
|
+
const filePath = getMessagesFilePath(sessionId);
|
|
37
|
+
const line = JSON.stringify(message) + '\n';
|
|
38
|
+
appendFileSync(filePath, line, 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
console.error(`[sessionStore] Failed to append message to JSONL for session=${sessionId}:`, err);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Load all messages from a session's JSONL file.
|
|
46
|
+
* Returns empty array if file doesn't exist or is unreadable.
|
|
47
|
+
* Skips malformed lines with a warning.
|
|
48
|
+
*/
|
|
49
|
+
export function loadMessagesFromJSONL(sessionId) {
|
|
50
|
+
try {
|
|
51
|
+
const filePath = join(getSessionsDir(), sessionId, 'messages.jsonl');
|
|
52
|
+
if (!existsSync(filePath))
|
|
53
|
+
return [];
|
|
54
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
55
|
+
const lines = content.split('\n').filter((line) => line.trim() !== '');
|
|
56
|
+
const messages = [];
|
|
57
|
+
for (let i = 0; i < lines.length; i++) {
|
|
58
|
+
try {
|
|
59
|
+
messages.push(JSON.parse(lines[i]));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
console.warn(`[sessionStore] Skipping malformed JSONL line ${i + 1} for session=${sessionId}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return messages;
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error(`[sessionStore] Failed to load messages from JSONL for session=${sessionId}:`, err);
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=sessionStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionStore.js","sourceRoot":"","sources":["../../src/ai/sessionStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,OAAO,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAAiB,EACjB,OAA6B;IAE7B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;QAC5C,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CACX,gEAAgE,SAAS,GAAG,EAC5E,GAAG,CACJ,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,SAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;QACrE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QAErC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACvE,MAAM,QAAQ,GAA2B,EAAE,CAAC;QAE5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAyB,CAAC,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,IAAI,CACV,gDAAgD,CAAC,GAAG,CAAC,gBAAgB,SAAS,EAAE,CACjF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CACX,iEAAiE,SAAS,GAAG,EAC7E,GAAG,CACJ,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionStore.test.d.ts","sourceRoot":"","sources":["../../src/ai/sessionStore.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { mkdtempSync, rmSync, readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
// Mock getSessionsDir to use a temp directory
|
|
6
|
+
let tempDir;
|
|
7
|
+
vi.mock('../service/singleton.js', () => ({
|
|
8
|
+
getSessionsDir: () => tempDir,
|
|
9
|
+
}));
|
|
10
|
+
// Import after mock is set up
|
|
11
|
+
const { appendMessageToJSONL, loadMessagesFromJSONL, getSessionDir, getMessagesFilePath } = await import('./sessionStore.js');
|
|
12
|
+
describe('sessionStore', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
tempDir = mkdtempSync(join(tmpdir(), 'sessionStore-test-'));
|
|
15
|
+
});
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
const makeMsg = (index, role = 'user', content = `message ${index}`) => ({ index, role, content });
|
|
20
|
+
describe('getSessionDir', () => {
|
|
21
|
+
it('creates the session directory if it does not exist', () => {
|
|
22
|
+
const dir = getSessionDir('test-session-1');
|
|
23
|
+
expect(dir).toBe(join(tempDir, 'test-session-1'));
|
|
24
|
+
// Verify it exists by writing a file into it (no throw = exists)
|
|
25
|
+
const { writeFileSync } = require('fs');
|
|
26
|
+
writeFileSync(join(dir, 'probe'), 'ok');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('getMessagesFilePath', () => {
|
|
30
|
+
it('returns path ending in messages.jsonl', () => {
|
|
31
|
+
const path = getMessagesFilePath('sess-abc');
|
|
32
|
+
expect(path).toBe(join(tempDir, 'sess-abc', 'messages.jsonl'));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('appendMessageToJSONL + loadMessagesFromJSONL', () => {
|
|
36
|
+
it('round-trips messages correctly', () => {
|
|
37
|
+
const sessionId = 'round-trip-test';
|
|
38
|
+
appendMessageToJSONL(sessionId, makeMsg(0, 'user', 'hello'));
|
|
39
|
+
appendMessageToJSONL(sessionId, makeMsg(1, 'assistant', 'hi there'));
|
|
40
|
+
appendMessageToJSONL(sessionId, makeMsg(2, 'user', 'run ls'));
|
|
41
|
+
const loaded = loadMessagesFromJSONL(sessionId);
|
|
42
|
+
expect(loaded).toHaveLength(3);
|
|
43
|
+
expect(loaded[0]).toEqual({ index: 0, role: 'user', content: 'hello' });
|
|
44
|
+
expect(loaded[1]).toEqual({ index: 1, role: 'assistant', content: 'hi there' });
|
|
45
|
+
expect(loaded[2]).toEqual({ index: 2, role: 'user', content: 'run ls' });
|
|
46
|
+
});
|
|
47
|
+
it('preserves optional fields (toolName, toolInput, toolResult, truncated)', () => {
|
|
48
|
+
const sessionId = 'fields-test';
|
|
49
|
+
const msg = {
|
|
50
|
+
index: 0,
|
|
51
|
+
role: 'assistant',
|
|
52
|
+
content: '',
|
|
53
|
+
toolName: 'Bash',
|
|
54
|
+
toolInput: '{"command":"ls"}',
|
|
55
|
+
toolResult: 'file1\nfile2',
|
|
56
|
+
truncated: false,
|
|
57
|
+
};
|
|
58
|
+
appendMessageToJSONL(sessionId, msg);
|
|
59
|
+
const loaded = loadMessagesFromJSONL(sessionId);
|
|
60
|
+
expect(loaded).toHaveLength(1);
|
|
61
|
+
expect(loaded[0]).toEqual(msg);
|
|
62
|
+
});
|
|
63
|
+
it('produces valid JSONL (one JSON object per line)', () => {
|
|
64
|
+
const sessionId = 'jsonl-format-test';
|
|
65
|
+
appendMessageToJSONL(sessionId, makeMsg(0));
|
|
66
|
+
appendMessageToJSONL(sessionId, makeMsg(1));
|
|
67
|
+
const raw = readFileSync(getMessagesFilePath(sessionId), 'utf-8');
|
|
68
|
+
const lines = raw.split('\n').filter((l) => l.trim() !== '');
|
|
69
|
+
expect(lines).toHaveLength(2);
|
|
70
|
+
// Each line should parse independently
|
|
71
|
+
for (const line of lines) {
|
|
72
|
+
expect(() => JSON.parse(line)).not.toThrow();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('loadMessagesFromJSONL', () => {
|
|
77
|
+
it('returns empty array for non-existent session', () => {
|
|
78
|
+
const loaded = loadMessagesFromJSONL('does-not-exist');
|
|
79
|
+
expect(loaded).toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
it('skips malformed lines gracefully', () => {
|
|
82
|
+
const sessionId = 'malformed-test';
|
|
83
|
+
const { writeFileSync, mkdirSync } = require('fs');
|
|
84
|
+
const dir = join(tempDir, sessionId);
|
|
85
|
+
mkdirSync(dir, { recursive: true });
|
|
86
|
+
const content = [
|
|
87
|
+
JSON.stringify(makeMsg(0, 'user', 'good line')),
|
|
88
|
+
'this is not json{{{',
|
|
89
|
+
JSON.stringify(makeMsg(2, 'assistant', 'also good')),
|
|
90
|
+
].join('\n') + '\n';
|
|
91
|
+
writeFileSync(join(dir, 'messages.jsonl'), content);
|
|
92
|
+
const loaded = loadMessagesFromJSONL(sessionId);
|
|
93
|
+
expect(loaded).toHaveLength(2);
|
|
94
|
+
expect(loaded[0].content).toBe('good line');
|
|
95
|
+
expect(loaded[1].content).toBe('also good');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
//# sourceMappingURL=sessionStore.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionStore.test.js","sourceRoot":"","sources":["../../src/ai/sessionStore.test.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface AgentConfig {
|
|
|
8
8
|
license?: License;
|
|
9
9
|
signalingServer: string;
|
|
10
10
|
anthropicApiKey?: string;
|
|
11
|
+
managedRepos?: string[];
|
|
12
|
+
managedCodingPaths?: string[];
|
|
11
13
|
}
|
|
12
14
|
export declare function ensureConfigDir(): void;
|
|
13
15
|
export declare function loadConfig(): AgentConfig | null;
|
|
@@ -19,6 +21,11 @@ export declare function getConfigPath(): string;
|
|
|
19
21
|
export declare function getAnthropicApiKey(): string | undefined;
|
|
20
22
|
export declare function setAnthropicApiKey(apiKey: string): void;
|
|
21
23
|
export declare function hasAnthropicApiKey(): boolean;
|
|
24
|
+
export declare function getManagedRepos(): string[];
|
|
25
|
+
export declare function addManagedRepo(path: string): void;
|
|
26
|
+
export declare function removeManagedRepo(path: string): void;
|
|
27
|
+
export declare function getManagedCodingPaths(): string[];
|
|
28
|
+
export declare function addManagedCodingPath(path: string): void;
|
|
22
29
|
/**
|
|
23
30
|
* Rotate the agent's key pair (keeps the same agentId).
|
|
24
31
|
* This invalidates all existing PWA connections.
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QACP,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QACP,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAMD,wBAAgB,eAAe,IAAI,IAAI,CAItC;AAED,wBAAgB,UAAU,IAAI,WAAW,GAAG,IAAI,CAU/C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAGpD;AAED,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,GAAG,WAAW,CAQxE;AAED,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,WAAW,CActE;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAMjD;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAGD,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAEvD;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAMvD;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAGD,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQjD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAUpD;AAGD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQvD;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,WAAW,CAQ3C"}
|
package/dist/config.js
CHANGED
|
@@ -5,6 +5,7 @@ import { generateAgentKeyPair } from './connection/connection.js';
|
|
|
5
5
|
import { generateAgentId } from '@sumicom/quicksave-shared';
|
|
6
6
|
const CONFIG_DIR = join(homedir(), '.quicksave');
|
|
7
7
|
const CONFIG_FILE = join(CONFIG_DIR, 'agent.json');
|
|
8
|
+
const DEFAULT_SIGNALING_SERVER = 'wss://signal.quicksave.dev';
|
|
8
9
|
export function ensureConfigDir() {
|
|
9
10
|
if (!existsSync(CONFIG_DIR)) {
|
|
10
11
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
@@ -73,6 +74,44 @@ export function setAnthropicApiKey(apiKey) {
|
|
|
73
74
|
export function hasAnthropicApiKey() {
|
|
74
75
|
return !!loadConfig()?.anthropicApiKey;
|
|
75
76
|
}
|
|
77
|
+
// Managed repos helpers
|
|
78
|
+
export function getManagedRepos() {
|
|
79
|
+
return loadConfig()?.managedRepos ?? [];
|
|
80
|
+
}
|
|
81
|
+
export function addManagedRepo(path) {
|
|
82
|
+
const config = loadConfig() ?? getOrCreateConfig(DEFAULT_SIGNALING_SERVER);
|
|
83
|
+
const repos = config.managedRepos ?? [];
|
|
84
|
+
if (!repos.includes(path)) {
|
|
85
|
+
repos.push(path);
|
|
86
|
+
config.managedRepos = repos;
|
|
87
|
+
saveConfig(config);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function removeManagedRepo(path) {
|
|
91
|
+
const config = loadConfig();
|
|
92
|
+
if (!config)
|
|
93
|
+
return;
|
|
94
|
+
const repos = config.managedRepos ?? [];
|
|
95
|
+
const idx = repos.indexOf(path);
|
|
96
|
+
if (idx !== -1) {
|
|
97
|
+
repos.splice(idx, 1);
|
|
98
|
+
config.managedRepos = repos;
|
|
99
|
+
saveConfig(config);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Managed coding paths helpers
|
|
103
|
+
export function getManagedCodingPaths() {
|
|
104
|
+
return loadConfig()?.managedCodingPaths ?? [];
|
|
105
|
+
}
|
|
106
|
+
export function addManagedCodingPath(path) {
|
|
107
|
+
const config = loadConfig() ?? getOrCreateConfig(DEFAULT_SIGNALING_SERVER);
|
|
108
|
+
const paths = config.managedCodingPaths ?? [];
|
|
109
|
+
if (!paths.includes(path)) {
|
|
110
|
+
paths.push(path);
|
|
111
|
+
config.managedCodingPaths = paths;
|
|
112
|
+
saveConfig(config);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
76
115
|
/**
|
|
77
116
|
* Rotate the agent's key pair (keeps the same agentId).
|
|
78
117
|
* This invalidates all existing PWA connections.
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAgB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,41 +8,40 @@ export interface ConnectionConfig {
|
|
|
8
8
|
secretKey: string;
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
export interface PeerSession {
|
|
12
|
+
address: string;
|
|
13
|
+
sessionDEK: Uint8Array;
|
|
14
|
+
connectedAt: number;
|
|
15
|
+
}
|
|
16
|
+
export interface AgentConnectionEvents {
|
|
17
|
+
connected: (peerAddress: string) => void;
|
|
18
|
+
disconnected: (peerAddress: string) => void;
|
|
19
|
+
message: (message: Message, peerAddress: string) => void;
|
|
15
20
|
error: (error: Error) => void;
|
|
16
21
|
}
|
|
17
|
-
export declare class
|
|
22
|
+
export declare class AgentConnection extends EventEmitter {
|
|
18
23
|
private config;
|
|
19
24
|
private signaling;
|
|
20
25
|
private keyPair;
|
|
21
|
-
private
|
|
22
|
-
private isConnected;
|
|
26
|
+
private peers;
|
|
23
27
|
private static readonly KEY_EXCHANGE_MAX_AGE_MS;
|
|
24
28
|
constructor(config: ConnectionConfig);
|
|
25
29
|
private setupSignalingHandlers;
|
|
26
30
|
start(): Promise<void>;
|
|
27
31
|
private handleDataMessage;
|
|
28
|
-
/**
|
|
29
|
-
* Check if key exchange has completed
|
|
30
|
-
*/
|
|
31
|
-
private isKeyExchangeComplete;
|
|
32
|
-
/**
|
|
33
|
-
* Get the encryption key
|
|
34
|
-
*/
|
|
35
|
-
private getEncryptionKey;
|
|
36
32
|
/**
|
|
37
33
|
* Handle key exchange message
|
|
38
34
|
*/
|
|
39
35
|
private handleKeyExchange;
|
|
40
|
-
send(message: Message): void;
|
|
41
|
-
private sendRaw;
|
|
36
|
+
send(message: Message, targetAddress: string): void;
|
|
42
37
|
private handlePeerDisconnected;
|
|
43
38
|
disconnect(): void;
|
|
44
39
|
getPublicKey(): string;
|
|
45
40
|
getAgentId(): string;
|
|
41
|
+
getPeerCount(): number;
|
|
42
|
+
hasPeers(): boolean;
|
|
43
|
+
/** Send a message to all connected peers. */
|
|
44
|
+
broadcast(message: Message): void;
|
|
46
45
|
}
|
|
47
46
|
/**
|
|
48
47
|
* Generate and encode a new key pair for the agent
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/connection/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,EASL,KAAK,OAAO,EAGb,MAAM,2BAA2B,CAAC;AAMnC,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CACnD;AAED,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/connection/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,EASL,KAAK,OAAO,EAGb,MAAM,2BAA2B,CAAC;AAMnC,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CACnD;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,KAAK,CAAuC;IAGpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAS;gBAE5C,MAAM,EAAE,gBAAgB;IAQpC,OAAO,CAAC,sBAAsB;IAoCxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAQd,iBAAiB;IAsC/B;;OAEG;YACW,iBAAiB;IAmD/B,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI;IAkBnD,OAAO,CAAC,sBAAsB;IAS9B,UAAU,IAAI,IAAI;IAIlB,YAAY,IAAI,MAAM;IAItB,UAAU,IAAI,MAAM;IAIpB,YAAY,IAAI,MAAM;IAItB,QAAQ,IAAI,OAAO;IAInB,6CAA6C;IAC7C,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAKlC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAE/E"}
|
|
@@ -2,16 +2,14 @@ import { EventEmitter } from 'events';
|
|
|
2
2
|
import { gzip, gunzip } from 'zlib';
|
|
3
3
|
import { promisify } from 'util';
|
|
4
4
|
import { generateKeyPair, encodeKeyPair, decodeKeyPair, encryptWithSharedSecret, decryptWithSharedSecret, decryptDEK, parseMessage, serializeMessage, } from '@sumicom/quicksave-shared';
|
|
5
|
-
import { SignalingClient } from './
|
|
5
|
+
import { SignalingClient } from './relay.js';
|
|
6
6
|
const gzipAsync = promisify(gzip);
|
|
7
7
|
const gunzipAsync = promisify(gunzip);
|
|
8
|
-
export class
|
|
8
|
+
export class AgentConnection extends EventEmitter {
|
|
9
9
|
config;
|
|
10
10
|
signaling;
|
|
11
11
|
keyPair;
|
|
12
|
-
|
|
13
|
-
sessionDEK = null;
|
|
14
|
-
isConnected = false;
|
|
12
|
+
peers = new Map();
|
|
15
13
|
// Key exchange replay protection
|
|
16
14
|
static KEY_EXCHANGE_MAX_AGE_MS = 60000; // 60 seconds
|
|
17
15
|
constructor(config) {
|
|
@@ -25,15 +23,27 @@ export class WebRTCConnection extends EventEmitter {
|
|
|
25
23
|
this.signaling.on('peer-connected', () => {
|
|
26
24
|
console.log('PWA peer connected, waiting for key exchange...');
|
|
27
25
|
});
|
|
28
|
-
this.signaling.on('data', (data) => {
|
|
29
|
-
this.handleDataMessage(data);
|
|
26
|
+
this.signaling.on('data', (data, from) => {
|
|
27
|
+
this.handleDataMessage(data, from);
|
|
30
28
|
});
|
|
31
29
|
this.signaling.on('peer-disconnected', () => {
|
|
32
|
-
|
|
30
|
+
// Legacy compatibility: only affects legacy (non-key-based) peers
|
|
31
|
+
// Key-based peers use 'pwa-bye' for targeted disconnect
|
|
32
|
+
for (const [address] of this.peers) {
|
|
33
|
+
this.handlePeerDisconnected(address);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// Targeted disconnect for key-based PWAs
|
|
37
|
+
this.signaling.on('pwa-bye', (pwaAddress) => {
|
|
38
|
+
this.handlePeerDisconnected(pwaAddress);
|
|
33
39
|
});
|
|
34
40
|
// Reset encryption state when WebSocket reconnects (before peer-disconnected)
|
|
35
41
|
this.signaling.on('disconnected', () => {
|
|
36
|
-
|
|
42
|
+
// Clear all peers, emit disconnected for each
|
|
43
|
+
for (const [address] of this.peers) {
|
|
44
|
+
this.emit('disconnected', address);
|
|
45
|
+
}
|
|
46
|
+
this.peers.clear();
|
|
37
47
|
});
|
|
38
48
|
this.signaling.on('error', (error) => {
|
|
39
49
|
this.emit('error', error);
|
|
@@ -46,61 +56,49 @@ export class WebRTCConnection extends EventEmitter {
|
|
|
46
56
|
console.log(`Agent ID: ${this.config.agentId}`);
|
|
47
57
|
console.log(`Public Key: ${this.config.keyPair.publicKey}`);
|
|
48
58
|
}
|
|
49
|
-
async handleDataMessage(data) {
|
|
59
|
+
async handleDataMessage(data, from) {
|
|
50
60
|
try {
|
|
51
61
|
// Always check for key-exchange messages first
|
|
52
62
|
// Always accept new key-exchange (PWA may have refreshed with new DEK)
|
|
53
63
|
try {
|
|
54
64
|
const parsed = JSON.parse(data);
|
|
55
65
|
if (parsed.type === 'key-exchange') {
|
|
56
|
-
await this.handleKeyExchange(parsed);
|
|
66
|
+
await this.handleKeyExchange(parsed, from);
|
|
57
67
|
return;
|
|
58
68
|
}
|
|
59
69
|
}
|
|
60
70
|
catch {
|
|
61
71
|
// Not JSON - continue to encrypted message handling
|
|
62
72
|
}
|
|
63
|
-
//
|
|
64
|
-
if (!
|
|
65
|
-
console.error('Received
|
|
73
|
+
// Look up peer by from address to get correct DEK
|
|
74
|
+
if (!from) {
|
|
75
|
+
console.error('Received encrypted message with no sender address');
|
|
66
76
|
return;
|
|
67
77
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (!encryptionKey) {
|
|
72
|
-
console.error('No encryption key, cannot decrypt message');
|
|
78
|
+
const peer = this.peers.get(from);
|
|
79
|
+
if (!peer) {
|
|
80
|
+
console.error(`No peer session found for ${from}`);
|
|
73
81
|
return;
|
|
74
82
|
}
|
|
75
|
-
|
|
83
|
+
// Post key-exchange: messages are encrypted, then the plaintext was compressed before encryption
|
|
84
|
+
// Decrypt first, then decompress
|
|
85
|
+
const decrypted = decryptWithSharedSecret(data, peer.sessionDEK);
|
|
76
86
|
const buffer = Buffer.from(decrypted, 'base64');
|
|
77
87
|
const decompressed = await gunzipAsync(buffer);
|
|
78
88
|
const message = parseMessage(decompressed.toString('utf-8'));
|
|
79
|
-
this.emit('message', message);
|
|
89
|
+
this.emit('message', message, from);
|
|
80
90
|
}
|
|
81
91
|
catch (error) {
|
|
82
92
|
console.error('Failed to handle message:', error);
|
|
83
93
|
}
|
|
84
94
|
}
|
|
85
|
-
/**
|
|
86
|
-
* Check if key exchange has completed
|
|
87
|
-
*/
|
|
88
|
-
isKeyExchangeComplete() {
|
|
89
|
-
return this.sessionDEK !== null;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Get the encryption key
|
|
93
|
-
*/
|
|
94
|
-
getEncryptionKey() {
|
|
95
|
-
return this.sessionDEK;
|
|
96
|
-
}
|
|
97
95
|
/**
|
|
98
96
|
* Handle key exchange message
|
|
99
97
|
*/
|
|
100
|
-
async handleKeyExchange(message) {
|
|
98
|
+
async handleKeyExchange(message, from) {
|
|
101
99
|
// Verify timestamp for replay protection
|
|
102
100
|
const age = Date.now() - message.timestamp;
|
|
103
|
-
if (age >
|
|
101
|
+
if (age > AgentConnection.KEY_EXCHANGE_MAX_AGE_MS) {
|
|
104
102
|
console.error(`Key exchange expired (age: ${age}ms)`);
|
|
105
103
|
this.emit('error', new Error('Key exchange expired'));
|
|
106
104
|
return;
|
|
@@ -113,50 +111,55 @@ export class WebRTCConnection extends EventEmitter {
|
|
|
113
111
|
}
|
|
114
112
|
// Decrypt the session DEK
|
|
115
113
|
try {
|
|
116
|
-
|
|
117
|
-
const
|
|
114
|
+
const sessionDEK = decryptDEK(message.encryptedDEK, this.keyPair.secretKey);
|
|
115
|
+
const peerAddress = from || 'unknown';
|
|
116
|
+
const peerKey = peerAddress.replace('pwa:', '');
|
|
118
117
|
console.log(`Key exchange complete with ${peerKey.slice(0, 12)}..., connection encrypted`);
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
this.emit('
|
|
118
|
+
const isReconnect = this.peers.has(peerAddress);
|
|
119
|
+
if (isReconnect) {
|
|
120
|
+
// Peer reconnected with new DEK — clean up old session state first
|
|
121
|
+
this.emit('disconnected', peerAddress);
|
|
123
122
|
}
|
|
123
|
+
// Create/update PeerSession for that address
|
|
124
|
+
this.peers.set(peerAddress, {
|
|
125
|
+
address: peerAddress,
|
|
126
|
+
sessionDEK,
|
|
127
|
+
connectedAt: Date.now(),
|
|
128
|
+
});
|
|
129
|
+
this.emit('connected', peerAddress);
|
|
124
130
|
// V2: Send acknowledgment
|
|
125
131
|
const ack = JSON.stringify({
|
|
126
132
|
type: 'key-exchange-ack',
|
|
127
133
|
version: 2,
|
|
128
134
|
});
|
|
129
|
-
this.
|
|
135
|
+
this.signaling.sendData(ack, peerAddress);
|
|
130
136
|
}
|
|
131
137
|
catch (error) {
|
|
132
138
|
console.error('Failed to decrypt session DEK:', error);
|
|
133
139
|
this.emit('error', new Error('Failed to decrypt session DEK'));
|
|
134
140
|
}
|
|
135
141
|
}
|
|
136
|
-
send(message) {
|
|
137
|
-
const
|
|
138
|
-
if (!
|
|
139
|
-
console.error(
|
|
142
|
+
send(message, targetAddress) {
|
|
143
|
+
const peer = this.peers.get(targetAddress);
|
|
144
|
+
if (!peer) {
|
|
145
|
+
console.error(`No peer session for ${targetAddress}, cannot encrypt message`);
|
|
140
146
|
return;
|
|
141
147
|
}
|
|
142
148
|
// Compress before encryption for better compression ratio
|
|
143
149
|
const serialized = serializeMessage(message);
|
|
144
150
|
gzipAsync(Buffer.from(serialized)).then((compressed) => {
|
|
145
151
|
const compressedBase64 = compressed.toString('base64');
|
|
146
|
-
const encrypted = encryptWithSharedSecret(compressedBase64,
|
|
147
|
-
this.signaling.sendData(encrypted);
|
|
152
|
+
const encrypted = encryptWithSharedSecret(compressedBase64, peer.sessionDEK);
|
|
153
|
+
this.signaling.sendData(encrypted, targetAddress);
|
|
154
|
+
}).catch((error) => {
|
|
155
|
+
console.error('Failed to send message:', error);
|
|
148
156
|
});
|
|
149
157
|
}
|
|
150
|
-
|
|
151
|
-
this.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (this.isConnected) {
|
|
155
|
-
this.isConnected = false;
|
|
156
|
-
this.emit('disconnected');
|
|
158
|
+
handlePeerDisconnected(peerAddress) {
|
|
159
|
+
if (this.peers.has(peerAddress)) {
|
|
160
|
+
this.peers.delete(peerAddress);
|
|
161
|
+
this.emit('disconnected', peerAddress);
|
|
157
162
|
}
|
|
158
|
-
// Clean up encryption state for next connection
|
|
159
|
-
this.sessionDEK = null;
|
|
160
163
|
console.log('Peer disconnected, waiting for new connection...');
|
|
161
164
|
}
|
|
162
165
|
disconnect() {
|
|
@@ -168,6 +171,18 @@ export class WebRTCConnection extends EventEmitter {
|
|
|
168
171
|
getAgentId() {
|
|
169
172
|
return this.config.agentId;
|
|
170
173
|
}
|
|
174
|
+
getPeerCount() {
|
|
175
|
+
return this.peers.size;
|
|
176
|
+
}
|
|
177
|
+
hasPeers() {
|
|
178
|
+
return this.peers.size > 0;
|
|
179
|
+
}
|
|
180
|
+
/** Send a message to all connected peers. */
|
|
181
|
+
broadcast(message) {
|
|
182
|
+
for (const [address] of this.peers) {
|
|
183
|
+
this.send(message, address);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
171
186
|
}
|
|
172
187
|
/**
|
|
173
188
|
* Generate and encode a new key pair for the agent
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/connection/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,EACb,uBAAuB,EACvB,uBAAuB,EACvB,UAAU,EACV,YAAY,EACZ,gBAAgB,GAIjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/connection/connection.ts"],"names":[],"mappings":"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"}
|