@thinkrun/cli 0.1.27
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/README.md +349 -0
- package/dist/bin/thinkrun.d.ts +6 -0
- package/dist/bin/thinkrun.d.ts.map +1 -0
- package/dist/bin/thinkrun.js +124 -0
- package/dist/bin/thinkrun.js.map +1 -0
- package/dist/scripts/browse.sh +1107 -0
- package/dist/src/adapters/cloud.d.ts +79 -0
- package/dist/src/adapters/cloud.d.ts.map +1 -0
- package/dist/src/adapters/cloud.js +637 -0
- package/dist/src/adapters/cloud.js.map +1 -0
- package/dist/src/adapters/index.d.ts +47 -0
- package/dist/src/adapters/index.d.ts.map +1 -0
- package/dist/src/adapters/index.js +211 -0
- package/dist/src/adapters/index.js.map +1 -0
- package/dist/src/adapters/local-command-retry.d.ts +12 -0
- package/dist/src/adapters/local-command-retry.d.ts.map +1 -0
- package/dist/src/adapters/local-command-retry.js +224 -0
- package/dist/src/adapters/local-command-retry.js.map +1 -0
- package/dist/src/adapters/local.d.ts +136 -0
- package/dist/src/adapters/local.d.ts.map +1 -0
- package/dist/src/adapters/local.js +1273 -0
- package/dist/src/adapters/local.js.map +1 -0
- package/dist/src/adapters/types.d.ts +45 -0
- package/dist/src/adapters/types.d.ts.map +1 -0
- package/dist/src/adapters/types.js +6 -0
- package/dist/src/adapters/types.js.map +1 -0
- package/dist/src/commands/actions.d.ts +135 -0
- package/dist/src/commands/actions.d.ts.map +1 -0
- package/dist/src/commands/actions.js +2207 -0
- package/dist/src/commands/actions.js.map +1 -0
- package/dist/src/commands/agent-init.d.ts +16 -0
- package/dist/src/commands/agent-init.d.ts.map +1 -0
- package/dist/src/commands/agent-init.js +222 -0
- package/dist/src/commands/agent-init.js.map +1 -0
- package/dist/src/commands/analyze.d.ts +11 -0
- package/dist/src/commands/analyze.d.ts.map +1 -0
- package/dist/src/commands/analyze.js +238 -0
- package/dist/src/commands/analyze.js.map +1 -0
- package/dist/src/commands/cache.d.ts +6 -0
- package/dist/src/commands/cache.d.ts.map +1 -0
- package/dist/src/commands/cache.js +147 -0
- package/dist/src/commands/cache.js.map +1 -0
- package/dist/src/commands/cloud.d.ts +6 -0
- package/dist/src/commands/cloud.d.ts.map +1 -0
- package/dist/src/commands/cloud.js +332 -0
- package/dist/src/commands/cloud.js.map +1 -0
- package/dist/src/commands/config.d.ts +7 -0
- package/dist/src/commands/config.d.ts.map +1 -0
- package/dist/src/commands/config.js +208 -0
- package/dist/src/commands/config.js.map +1 -0
- package/dist/src/commands/doctor.d.ts +127 -0
- package/dist/src/commands/doctor.d.ts.map +1 -0
- package/dist/src/commands/doctor.js +684 -0
- package/dist/src/commands/doctor.js.map +1 -0
- package/dist/src/commands/evaluate-helpers.d.ts +6 -0
- package/dist/src/commands/evaluate-helpers.d.ts.map +1 -0
- package/dist/src/commands/evaluate-helpers.js +13 -0
- package/dist/src/commands/evaluate-helpers.js.map +1 -0
- package/dist/src/commands/install.d.ts +118 -0
- package/dist/src/commands/install.d.ts.map +1 -0
- package/dist/src/commands/install.js +975 -0
- package/dist/src/commands/install.js.map +1 -0
- package/dist/src/commands/release.d.ts +7 -0
- package/dist/src/commands/release.d.ts.map +1 -0
- package/dist/src/commands/release.js +123 -0
- package/dist/src/commands/release.js.map +1 -0
- package/dist/src/commands/reset-connection.d.ts +17 -0
- package/dist/src/commands/reset-connection.d.ts.map +1 -0
- package/dist/src/commands/reset-connection.js +141 -0
- package/dist/src/commands/reset-connection.js.map +1 -0
- package/dist/src/commands/session-debug.d.ts +23 -0
- package/dist/src/commands/session-debug.d.ts.map +1 -0
- package/dist/src/commands/session-debug.js +267 -0
- package/dist/src/commands/session-debug.js.map +1 -0
- package/dist/src/commands/setup.d.ts +53 -0
- package/dist/src/commands/setup.d.ts.map +1 -0
- package/dist/src/commands/setup.js +249 -0
- package/dist/src/commands/setup.js.map +1 -0
- package/dist/src/config/store.d.ts +39 -0
- package/dist/src/config/store.d.ts.map +1 -0
- package/dist/src/config/store.js +290 -0
- package/dist/src/config/store.js.map +1 -0
- package/dist/src/daemon/access.d.ts +53 -0
- package/dist/src/daemon/access.d.ts.map +1 -0
- package/dist/src/daemon/access.js +87 -0
- package/dist/src/daemon/access.js.map +1 -0
- package/dist/src/daemon/bridge-envelope.d.ts +96 -0
- package/dist/src/daemon/bridge-envelope.d.ts.map +1 -0
- package/dist/src/daemon/bridge-envelope.js +235 -0
- package/dist/src/daemon/bridge-envelope.js.map +1 -0
- package/dist/src/daemon/utils.d.ts +43 -0
- package/dist/src/daemon/utils.d.ts.map +1 -0
- package/dist/src/daemon/utils.js +134 -0
- package/dist/src/daemon/utils.js.map +1 -0
- package/dist/src/errors.d.ts +60 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +87 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/local-bridge-timing.d.ts +31 -0
- package/dist/src/local-bridge-timing.d.ts.map +1 -0
- package/dist/src/local-bridge-timing.js +41 -0
- package/dist/src/local-bridge-timing.js.map +1 -0
- package/dist/src/obstacle-recovery/classify-script.d.ts +16 -0
- package/dist/src/obstacle-recovery/classify-script.d.ts.map +1 -0
- package/dist/src/obstacle-recovery/classify-script.js +53 -0
- package/dist/src/obstacle-recovery/classify-script.js.map +1 -0
- package/dist/src/obstacle-recovery/obstacle-classifier.d.ts +21 -0
- package/dist/src/obstacle-recovery/obstacle-classifier.d.ts.map +1 -0
- package/dist/src/obstacle-recovery/obstacle-classifier.js +37 -0
- package/dist/src/obstacle-recovery/obstacle-classifier.js.map +1 -0
- package/dist/src/obstacle-recovery/state-fingerprint.d.ts +26 -0
- package/dist/src/obstacle-recovery/state-fingerprint.d.ts.map +1 -0
- package/dist/src/obstacle-recovery/state-fingerprint.js +85 -0
- package/dist/src/obstacle-recovery/state-fingerprint.js.map +1 -0
- package/dist/src/obstacle-recovery/types.d.ts +44 -0
- package/dist/src/obstacle-recovery/types.d.ts.map +1 -0
- package/dist/src/obstacle-recovery/types.js +16 -0
- package/dist/src/obstacle-recovery/types.js.map +1 -0
- package/dist/src/output/formatter.d.ts +55 -0
- package/dist/src/output/formatter.d.ts.map +1 -0
- package/dist/src/output/formatter.js +55 -0
- package/dist/src/output/formatter.js.map +1 -0
- package/dist/src/output/mode.d.ts +11 -0
- package/dist/src/output/mode.d.ts.map +1 -0
- package/dist/src/output/mode.js +16 -0
- package/dist/src/output/mode.js.map +1 -0
- package/dist/src/protected-flow/detector.d.ts +26 -0
- package/dist/src/protected-flow/detector.d.ts.map +1 -0
- package/dist/src/protected-flow/detector.js +75 -0
- package/dist/src/protected-flow/detector.js.map +1 -0
- package/dist/src/protected-flow/types.d.ts +24 -0
- package/dist/src/protected-flow/types.d.ts.map +1 -0
- package/dist/src/protected-flow/types.js +28 -0
- package/dist/src/protected-flow/types.js.map +1 -0
- package/dist/src/session/agent-identity.d.ts +65 -0
- package/dist/src/session/agent-identity.d.ts.map +1 -0
- package/dist/src/session/agent-identity.js +133 -0
- package/dist/src/session/agent-identity.js.map +1 -0
- package/dist/src/session/cli-session-sync.d.ts +72 -0
- package/dist/src/session/cli-session-sync.d.ts.map +1 -0
- package/dist/src/session/cli-session-sync.js +244 -0
- package/dist/src/session/cli-session-sync.js.map +1 -0
- package/dist/src/session/context.d.ts +24 -0
- package/dist/src/session/context.d.ts.map +1 -0
- package/dist/src/session/context.js +165 -0
- package/dist/src/session/context.js.map +1 -0
- package/dist/src/session/continuity.d.ts +33 -0
- package/dist/src/session/continuity.d.ts.map +1 -0
- package/dist/src/session/continuity.js +179 -0
- package/dist/src/session/continuity.js.map +1 -0
- package/dist/src/session/errors.d.ts +9 -0
- package/dist/src/session/errors.d.ts.map +1 -0
- package/dist/src/session/errors.js +31 -0
- package/dist/src/session/errors.js.map +1 -0
- package/dist/src/session/local-continuity.d.ts +16 -0
- package/dist/src/session/local-continuity.d.ts.map +1 -0
- package/dist/src/session/local-continuity.js +146 -0
- package/dist/src/session/local-continuity.js.map +1 -0
- package/dist/src/session/signal-handler.d.ts +24 -0
- package/dist/src/session/signal-handler.d.ts.map +1 -0
- package/dist/src/session/signal-handler.js +35 -0
- package/dist/src/session/signal-handler.js.map +1 -0
- package/dist/src/shared/local-recovery-policy.d.ts +40 -0
- package/dist/src/shared/local-recovery-policy.d.ts.map +1 -0
- package/dist/src/shared/local-recovery-policy.js +59 -0
- package/dist/src/shared/local-recovery-policy.js.map +1 -0
- package/dist/src/shared/recovery-state.d.ts +3 -0
- package/dist/src/shared/recovery-state.d.ts.map +1 -0
- package/dist/src/shared/recovery-state.js +9 -0
- package/dist/src/shared/recovery-state.js.map +1 -0
- package/dist/src/types.d.ts +131 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +5 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils.d.ts +50 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +147 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/src/working-location.d.ts +107 -0
- package/dist/src/working-location.d.ts.map +1 -0
- package/dist/src/working-location.js +651 -0
- package/dist/src/working-location.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in CLI session persistence helpers.
|
|
3
|
+
*
|
|
4
|
+
* When a ThinkRun API key is configured, local-mode commands can
|
|
5
|
+
* optionally register a session document in the cloud activity feed so
|
|
6
|
+
* that screenshots and other actions appear in the UI alongside cloud
|
|
7
|
+
* and extension sessions.
|
|
8
|
+
*
|
|
9
|
+
* ALL exported functions are fire-and-forget — they never throw and
|
|
10
|
+
* never block the caller. The sync is a best-effort side-channel; if
|
|
11
|
+
* the cloud API is unreachable the CLI continues to function normally.
|
|
12
|
+
*
|
|
13
|
+
* File written to disk: `~/.thinkrun/local-session-{tabId}.json`
|
|
14
|
+
* (same directory as working-location.json; THINKRUN_LOCK_DIR for
|
|
15
|
+
* test isolation).
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync, writeFileSync, readFileSync, unlinkSync } from 'node:fs';
|
|
18
|
+
import { spawnSync } from 'node:child_process';
|
|
19
|
+
import { config } from '../config/store.js';
|
|
20
|
+
import { getCliSessionFilePath } from '../working-location.js';
|
|
21
|
+
import { resolveAgentId } from './agent-identity.js';
|
|
22
|
+
// ─── Abort signal helper ──────────────────────────────────────────────────────
|
|
23
|
+
/**
|
|
24
|
+
* Create an AbortSignal that fires after `ms` milliseconds, backed by a timer
|
|
25
|
+
* that is `unref()`d so it does NOT keep a short-lived CLI process alive.
|
|
26
|
+
*
|
|
27
|
+
* `AbortSignal.timeout(ms)` internally creates a referenced timer; even when
|
|
28
|
+
* the caller fire-and-forgets the fetch, that timer prevents `thinkrun attach`
|
|
29
|
+
* (and similar commands) from exiting for up to `ms` milliseconds after the
|
|
30
|
+
* command otherwise completes. Using an unref'd timer avoids that stall while
|
|
31
|
+
* preserving the network-timeout behaviour for long-running sessions.
|
|
32
|
+
*/
|
|
33
|
+
function unrefAbortSignal(ms) {
|
|
34
|
+
const controller = new AbortController();
|
|
35
|
+
const timer = setTimeout(() => controller.abort(), ms);
|
|
36
|
+
// unref() exists in Node.js and Bun — fall back gracefully in other runtimes.
|
|
37
|
+
if (typeof timer.unref === 'function')
|
|
38
|
+
timer.unref();
|
|
39
|
+
// Note: if the fetch settles before the timeout, the setTimeout callback still
|
|
40
|
+
// fires after `ms` and calls abort() on an already-settled signal. This is
|
|
41
|
+
// harmless (aborting a settled signal is a no-op) and not a resource leak.
|
|
42
|
+
return controller.signal;
|
|
43
|
+
}
|
|
44
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
45
|
+
/** Return the API key and validated HTTPS apiUrl, or null if either is absent
|
|
46
|
+
* or insecure (to prevent credential exfiltration to non-HTTPS endpoints). */
|
|
47
|
+
function getApiCredentials() {
|
|
48
|
+
const apiKey = config.get('apiKey');
|
|
49
|
+
if (!apiKey)
|
|
50
|
+
return null;
|
|
51
|
+
const rawApiUrl = config.get('apiUrl') || 'https://api.thinkbrowse.io';
|
|
52
|
+
if (!rawApiUrl.startsWith('https://'))
|
|
53
|
+
return null;
|
|
54
|
+
return { apiKey, apiUrl: rawApiUrl };
|
|
55
|
+
}
|
|
56
|
+
// ─── File I/O ─────────────────────────────────────────────────────────────────
|
|
57
|
+
/**
|
|
58
|
+
* Read the persisted cloud sessionId for a given local tabId.
|
|
59
|
+
* Returns undefined if no session file exists or it is malformed.
|
|
60
|
+
*/
|
|
61
|
+
export function readCliSessionId(tabId) {
|
|
62
|
+
try {
|
|
63
|
+
const filePath = getCliSessionFilePath(tabId);
|
|
64
|
+
if (!existsSync(filePath))
|
|
65
|
+
return undefined;
|
|
66
|
+
const data = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
67
|
+
return typeof data.sessionId === 'string' ? data.sessionId : undefined;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function writeCliSessionId(tabId, sessionId) {
|
|
74
|
+
try {
|
|
75
|
+
writeFileSync(getCliSessionFilePath(tabId), JSON.stringify({ sessionId, tabId }), 'utf-8');
|
|
76
|
+
}
|
|
77
|
+
catch { /* ignore — best-effort */ }
|
|
78
|
+
}
|
|
79
|
+
function deleteCliSessionFile(tabId) {
|
|
80
|
+
try {
|
|
81
|
+
unlinkSync(getCliSessionFilePath(tabId));
|
|
82
|
+
}
|
|
83
|
+
catch { /* ignore */ }
|
|
84
|
+
}
|
|
85
|
+
// ─── Async helpers (used in normal operation) ─────────────────────────────────
|
|
86
|
+
/**
|
|
87
|
+
* Call POST /api/sessions/local-init to register a CLI session in the
|
|
88
|
+
* cloud activity feed. Persists the returned sessionId to disk.
|
|
89
|
+
*
|
|
90
|
+
* @returns The sessionId on success, undefined on any failure.
|
|
91
|
+
*/
|
|
92
|
+
export async function initCliSession(tabId, fetchFn = globalThis.fetch) {
|
|
93
|
+
const creds = getApiCredentials();
|
|
94
|
+
if (!creds)
|
|
95
|
+
return undefined;
|
|
96
|
+
const { apiKey, apiUrl } = creds;
|
|
97
|
+
try {
|
|
98
|
+
const res = await fetchFn(`${apiUrl}/api/sessions/local-init`, {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
|
|
101
|
+
body: JSON.stringify({ source: 'cli', tabId, agentId: resolveAgentId() }),
|
|
102
|
+
signal: unrefAbortSignal(10_000),
|
|
103
|
+
});
|
|
104
|
+
if (!res.ok)
|
|
105
|
+
return undefined;
|
|
106
|
+
const body = (await res.json());
|
|
107
|
+
const sessionId = body?.data?.sessionId;
|
|
108
|
+
if (!sessionId)
|
|
109
|
+
return undefined;
|
|
110
|
+
writeCliSessionId(tabId, sessionId);
|
|
111
|
+
return sessionId;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Fire-and-forget: POST a browser action to the cloud session action log.
|
|
119
|
+
* Never throws, never blocks the caller.
|
|
120
|
+
*
|
|
121
|
+
* @param sessionId The cloud session ID returned by initCliSession.
|
|
122
|
+
* @param type Action type — must be in ALLOWED_ACTION_TYPES on the server.
|
|
123
|
+
* @param details Arbitrary key-value pairs describing the action (url, selector, etc.)
|
|
124
|
+
* @param fetchFn Injectable fetch for testing.
|
|
125
|
+
*/
|
|
126
|
+
export function syncBrowserAction(sessionId, type, details, fetchFn = globalThis.fetch) {
|
|
127
|
+
const creds = getApiCredentials();
|
|
128
|
+
if (!creds)
|
|
129
|
+
return;
|
|
130
|
+
const { apiKey, apiUrl } = creds;
|
|
131
|
+
// try/catch covers synchronous errors (e.g. JSON.stringify throws, or fetchFn
|
|
132
|
+
// throws synchronously before returning a Promise). The .catch covers async rejection.
|
|
133
|
+
try {
|
|
134
|
+
fetchFn(`${apiUrl}/api/sessions/${sessionId}/local-sync/action`, {
|
|
135
|
+
method: 'POST',
|
|
136
|
+
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
|
|
137
|
+
body: JSON.stringify({ type, details }),
|
|
138
|
+
signal: unrefAbortSignal(10_000),
|
|
139
|
+
}).catch(() => { });
|
|
140
|
+
}
|
|
141
|
+
catch { /* swallow synchronous errors */ }
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Fire-and-forget: POST a screenshot action to the cloud session.
|
|
145
|
+
* Never throws, never blocks the caller.
|
|
146
|
+
*/
|
|
147
|
+
export function syncScreenshotAction(sessionId, artifactBase64, artifactMimeType, caption, fetchFn = globalThis.fetch) {
|
|
148
|
+
const creds = getApiCredentials();
|
|
149
|
+
if (!creds)
|
|
150
|
+
return;
|
|
151
|
+
const { apiKey, apiUrl } = creds;
|
|
152
|
+
try {
|
|
153
|
+
fetchFn(`${apiUrl}/api/sessions/${sessionId}/local-sync/action`, {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
|
|
156
|
+
body: JSON.stringify({ type: 'screenshot', artifactBase64, artifactMimeType, caption }),
|
|
157
|
+
signal: unrefAbortSignal(15_000),
|
|
158
|
+
}).then((res) => {
|
|
159
|
+
if (!res.ok) {
|
|
160
|
+
console.debug(`[thinkrun] syncScreenshotAction: HTTP ${res.status} from server`);
|
|
161
|
+
}
|
|
162
|
+
res.body?.cancel().catch(() => { });
|
|
163
|
+
}).catch(() => { });
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// fire-and-forget — synchronous fetch setup errors are intentionally swallowed
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Fire-and-forget: PATCH /api/sessions/:sessionId/local-close, then
|
|
171
|
+
* clean up the persisted session file.
|
|
172
|
+
* Never throws, never blocks the caller.
|
|
173
|
+
*
|
|
174
|
+
* The session file is deleted BEFORE the HTTP call to prevent TOCTOU
|
|
175
|
+
* races with a concurrent closeCliSessionSync (e.g. Ctrl-C fires while
|
|
176
|
+
* stopSession is already in flight).
|
|
177
|
+
*/
|
|
178
|
+
export function closeCliSession(tabId, status, fetchFn = globalThis.fetch) {
|
|
179
|
+
const sessionId = readCliSessionId(tabId);
|
|
180
|
+
if (!sessionId)
|
|
181
|
+
return;
|
|
182
|
+
// Delete immediately — prevents a concurrent closeCliSessionSync from
|
|
183
|
+
// double-reading the same sessionId and issuing a second PATCH.
|
|
184
|
+
deleteCliSessionFile(tabId);
|
|
185
|
+
const creds = getApiCredentials();
|
|
186
|
+
if (!creds)
|
|
187
|
+
return;
|
|
188
|
+
const { apiKey, apiUrl } = creds;
|
|
189
|
+
fetchFn(`${apiUrl}/api/sessions/${sessionId}/local-close`, {
|
|
190
|
+
method: 'PATCH',
|
|
191
|
+
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
|
|
192
|
+
body: JSON.stringify({ status }),
|
|
193
|
+
signal: unrefAbortSignal(10_000),
|
|
194
|
+
}).catch(() => { });
|
|
195
|
+
// Session file already deleted above; no .finally cleanup needed.
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Synchronous version of closeCliSession for use in process exit handlers
|
|
199
|
+
* where async operations are not guaranteed to run to completion.
|
|
200
|
+
*
|
|
201
|
+
* Uses `curl` via spawnSync (-K - to read config from stdin) so the HTTP
|
|
202
|
+
* request completes before the process exits and the API key never appears
|
|
203
|
+
* in `ps aux` / /proc/<pid>/cmdline.
|
|
204
|
+
*
|
|
205
|
+
* The session file is deleted BEFORE the HTTP request to prevent TOCTOU
|
|
206
|
+
* races with a concurrent async closeCliSession call.
|
|
207
|
+
*
|
|
208
|
+
* @param spawnFn — injectable for testing; defaults to node:child_process spawnSync
|
|
209
|
+
*/
|
|
210
|
+
export function closeCliSessionSync(tabId, status, spawnFn = spawnSync) {
|
|
211
|
+
const sessionId = readCliSessionId(tabId);
|
|
212
|
+
if (!sessionId)
|
|
213
|
+
return;
|
|
214
|
+
// Delete immediately — prevents TOCTOU with concurrent closeCliSession.
|
|
215
|
+
deleteCliSessionFile(tabId);
|
|
216
|
+
const creds = getApiCredentials();
|
|
217
|
+
if (!creds)
|
|
218
|
+
return;
|
|
219
|
+
const { apiKey, apiUrl } = creds;
|
|
220
|
+
// Sanitize apiKey for curl config interpolation:
|
|
221
|
+
// - strip CR/LF (would terminate the header = "…" line prematurely)
|
|
222
|
+
// - escape `\` and `"` (curl config format: `"` → `\"`, `\` → `\\`)
|
|
223
|
+
const safeKey = apiKey
|
|
224
|
+
.replace(/[\r\n]/g, '')
|
|
225
|
+
.replace(/\\/g, '\\\\')
|
|
226
|
+
.replace(/"/g, '\\"');
|
|
227
|
+
// Pass credentials via curl's -K - (config from stdin) so the API key
|
|
228
|
+
// is never an argument in the process list (not visible in `ps`).
|
|
229
|
+
const curlConfig = [
|
|
230
|
+
`header = "x-api-key: ${safeKey}"`,
|
|
231
|
+
'header = "Content-Type: application/json"',
|
|
232
|
+
].join('\n') + '\n';
|
|
233
|
+
try {
|
|
234
|
+
spawnFn('curl', [
|
|
235
|
+
'-s',
|
|
236
|
+
'-X', 'PATCH',
|
|
237
|
+
'-K', '-',
|
|
238
|
+
'-d', JSON.stringify({ status }),
|
|
239
|
+
`${apiUrl}/api/sessions/${sessionId}/local-close`,
|
|
240
|
+
], { input: curlConfig, timeout: 5_000 });
|
|
241
|
+
}
|
|
242
|
+
catch { /* ignore — best-effort */ }
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=cli-session-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-session-sync.js","sourceRoot":"","sources":["../../../src/session/cli-session-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface LocalSessionContext {
|
|
2
|
+
mode: 'local';
|
|
3
|
+
tabId: string;
|
|
4
|
+
agentSessionId?: string;
|
|
5
|
+
controlSessionId?: string;
|
|
6
|
+
windowId?: number;
|
|
7
|
+
updatedAt?: string;
|
|
8
|
+
source: 'typed' | 'legacy';
|
|
9
|
+
}
|
|
10
|
+
export interface CloudSessionContext {
|
|
11
|
+
mode: 'cloud';
|
|
12
|
+
sessionId: string;
|
|
13
|
+
source: 'typed' | 'legacy';
|
|
14
|
+
}
|
|
15
|
+
export type SessionContext = LocalSessionContext | CloudSessionContext;
|
|
16
|
+
export declare function hasMixedTypedSessionState(): boolean;
|
|
17
|
+
export declare function getSessionContext(): SessionContext | undefined;
|
|
18
|
+
export declare function getLocalSessionContext(): LocalSessionContext | undefined;
|
|
19
|
+
export declare function getCloudSessionContext(): CloudSessionContext | undefined;
|
|
20
|
+
export declare function setLocalSessionContext(tabId: string, agentSessionId?: string, windowId?: number): void;
|
|
21
|
+
export declare function clearLocalSessionContext(): void;
|
|
22
|
+
export declare function setCloudSessionContext(sessionId: string): void;
|
|
23
|
+
export declare function clearCloudSessionContext(expectedSessionId?: string): void;
|
|
24
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/session/context.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC5B;AAED,MAAM,MAAM,cAAc,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AA4FvE,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAED,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CA0C9D;AAED,wBAAgB,sBAAsB,IAAI,mBAAmB,GAAG,SAAS,CAGxE;AAED,wBAAgB,sBAAsB,IAAI,mBAAmB,GAAG,SAAS,CAGxE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAOtG;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAU/C;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAI9D;AAED,wBAAgB,wBAAwB,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAezE"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { config } from '../config/store.js';
|
|
2
|
+
import { mixedSessionStateError } from './errors.js';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { getAgentFilePath, resolveAgentId } from './agent-identity.js';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { updateWorkingLocationControlSession } from '../working-location.js';
|
|
8
|
+
function sanitizeFilePart(value) {
|
|
9
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, '_') || 'default';
|
|
10
|
+
}
|
|
11
|
+
function getPrivateLocalSessionStatePath() {
|
|
12
|
+
const lockDir = dirname(getAgentFilePath());
|
|
13
|
+
return join(lockDir, 'agent-sessions', `${sanitizeFilePart(resolveAgentId())}.json`);
|
|
14
|
+
}
|
|
15
|
+
function readPrivateLocalSessionState() {
|
|
16
|
+
try {
|
|
17
|
+
const path = getPrivateLocalSessionStatePath();
|
|
18
|
+
if (!existsSync(path))
|
|
19
|
+
return undefined;
|
|
20
|
+
const data = JSON.parse(readFileSync(path, 'utf-8'));
|
|
21
|
+
if (typeof data.tabId !== 'string' || data.tabId.length === 0)
|
|
22
|
+
return undefined;
|
|
23
|
+
if (typeof data.controlSessionId !== 'string' || data.controlSessionId.length === 0)
|
|
24
|
+
return undefined;
|
|
25
|
+
return {
|
|
26
|
+
tabId: data.tabId,
|
|
27
|
+
...(typeof data.agentSessionId === 'string' ? { agentSessionId: data.agentSessionId } : {}),
|
|
28
|
+
controlSessionId: data.controlSessionId,
|
|
29
|
+
...(typeof data.windowId === 'number' ? { windowId: data.windowId } : {}),
|
|
30
|
+
updatedAt: typeof data.updatedAt === 'string' ? data.updatedAt : new Date(0).toISOString(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function writePrivateLocalSessionState(tabId, agentSessionId, controlSessionId, windowId) {
|
|
38
|
+
const path = getPrivateLocalSessionStatePath();
|
|
39
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
40
|
+
const resolvedControlSessionId = controlSessionId ?? agentSessionId ?? `local_${randomUUID()}`;
|
|
41
|
+
const payload = {
|
|
42
|
+
tabId,
|
|
43
|
+
...(agentSessionId ? { agentSessionId } : {}),
|
|
44
|
+
controlSessionId: resolvedControlSessionId,
|
|
45
|
+
...(typeof windowId === 'number' ? { windowId } : {}),
|
|
46
|
+
updatedAt: new Date().toISOString(),
|
|
47
|
+
};
|
|
48
|
+
writeFileSync(path, JSON.stringify(payload), 'utf-8');
|
|
49
|
+
updateWorkingLocationControlSession(resolvedControlSessionId);
|
|
50
|
+
}
|
|
51
|
+
function clearPrivateLocalSessionState() {
|
|
52
|
+
try {
|
|
53
|
+
rmSync(getPrivateLocalSessionStatePath(), { force: true });
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// best-effort
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function isNumericId(value) {
|
|
60
|
+
return !!value && /^\d+$/.test(value);
|
|
61
|
+
}
|
|
62
|
+
function getLegacyContext() {
|
|
63
|
+
const activeSessionId = config.get('activeSessionId');
|
|
64
|
+
const agentSessionId = config.get('agentSessionId');
|
|
65
|
+
if (!activeSessionId)
|
|
66
|
+
return undefined;
|
|
67
|
+
if (isNumericId(activeSessionId)) {
|
|
68
|
+
return {
|
|
69
|
+
mode: 'local',
|
|
70
|
+
tabId: activeSessionId,
|
|
71
|
+
agentSessionId,
|
|
72
|
+
source: 'legacy',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
mode: 'cloud',
|
|
77
|
+
sessionId: activeSessionId,
|
|
78
|
+
source: 'legacy',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function hasMixedTypedSessionState() {
|
|
82
|
+
return !!config.get('localTabId') && !!config.get('cloudSessionId');
|
|
83
|
+
}
|
|
84
|
+
export function getSessionContext() {
|
|
85
|
+
if (hasMixedTypedSessionState()) {
|
|
86
|
+
throw mixedSessionStateError();
|
|
87
|
+
}
|
|
88
|
+
const localTabId = config.get('localTabId');
|
|
89
|
+
const localAgentSessionId = config.get('localAgentSessionId');
|
|
90
|
+
const cloudSessionId = config.get('cloudSessionId');
|
|
91
|
+
const privateLocalState = readPrivateLocalSessionState();
|
|
92
|
+
if (localTabId) {
|
|
93
|
+
const privateAgentSessionId = privateLocalState?.tabId === localTabId
|
|
94
|
+
? privateLocalState.agentSessionId
|
|
95
|
+
: undefined;
|
|
96
|
+
return {
|
|
97
|
+
mode: 'local',
|
|
98
|
+
tabId: localTabId,
|
|
99
|
+
...(privateAgentSessionId || localAgentSessionId
|
|
100
|
+
? { agentSessionId: privateAgentSessionId ?? localAgentSessionId }
|
|
101
|
+
: {}),
|
|
102
|
+
...(privateLocalState?.tabId === localTabId && privateLocalState.controlSessionId
|
|
103
|
+
? { controlSessionId: privateLocalState.controlSessionId }
|
|
104
|
+
: {}),
|
|
105
|
+
...(privateLocalState?.tabId === localTabId && typeof privateLocalState.windowId === 'number'
|
|
106
|
+
? { windowId: privateLocalState.windowId }
|
|
107
|
+
: {}),
|
|
108
|
+
...(privateLocalState?.tabId === localTabId && typeof privateLocalState.updatedAt === 'string'
|
|
109
|
+
? { updatedAt: privateLocalState.updatedAt }
|
|
110
|
+
: {}),
|
|
111
|
+
source: 'typed',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (cloudSessionId) {
|
|
115
|
+
return {
|
|
116
|
+
mode: 'cloud',
|
|
117
|
+
sessionId: cloudSessionId,
|
|
118
|
+
source: 'typed',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return getLegacyContext();
|
|
122
|
+
}
|
|
123
|
+
export function getLocalSessionContext() {
|
|
124
|
+
const context = getSessionContext();
|
|
125
|
+
return context?.mode === 'local' ? context : undefined;
|
|
126
|
+
}
|
|
127
|
+
export function getCloudSessionContext() {
|
|
128
|
+
const context = getSessionContext();
|
|
129
|
+
return context?.mode === 'cloud' ? context : undefined;
|
|
130
|
+
}
|
|
131
|
+
export function setLocalSessionContext(tabId, agentSessionId, windowId) {
|
|
132
|
+
config.set('localTabId', tabId);
|
|
133
|
+
config.set('activeSessionId', tabId);
|
|
134
|
+
config.delete('cloudSessionId');
|
|
135
|
+
config.delete('localAgentSessionId');
|
|
136
|
+
config.delete('agentSessionId');
|
|
137
|
+
writePrivateLocalSessionState(tabId, agentSessionId, undefined, windowId);
|
|
138
|
+
}
|
|
139
|
+
export function clearLocalSessionContext() {
|
|
140
|
+
config.delete('localTabId');
|
|
141
|
+
config.delete('localAgentSessionId');
|
|
142
|
+
clearPrivateLocalSessionState();
|
|
143
|
+
if (isNumericId(config.get('activeSessionId'))) {
|
|
144
|
+
config.delete('activeSessionId');
|
|
145
|
+
}
|
|
146
|
+
config.delete('agentSessionId');
|
|
147
|
+
}
|
|
148
|
+
export function setCloudSessionContext(sessionId) {
|
|
149
|
+
clearLocalSessionContext();
|
|
150
|
+
config.set('cloudSessionId', sessionId);
|
|
151
|
+
config.set('activeSessionId', sessionId);
|
|
152
|
+
}
|
|
153
|
+
export function clearCloudSessionContext(expectedSessionId) {
|
|
154
|
+
const cloudSessionId = config.get('cloudSessionId');
|
|
155
|
+
const activeSessionId = config.get('activeSessionId');
|
|
156
|
+
if (!expectedSessionId || cloudSessionId === expectedSessionId) {
|
|
157
|
+
config.delete('cloudSessionId');
|
|
158
|
+
}
|
|
159
|
+
if (activeSessionId &&
|
|
160
|
+
!isNumericId(activeSessionId) &&
|
|
161
|
+
(!expectedSessionId || activeSessionId === expectedSessionId)) {
|
|
162
|
+
config.delete('activeSessionId');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/session/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,mCAAmC,EAAE,MAAM,wBAAwB,CAAC;AA4B7E,SAAS,gBAAgB,CAAC,KAAa;IACrC,OAAO,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,IAAI,SAAS,CAAC;AAC9D,CAAC;AAED,SAAS,+BAA+B;IACtC,MAAM,OAAO,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC5C,OAAO,IAAI,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,4BAA4B;IACnC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,+BAA+B,EAAE,CAAC;QAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAsC,CAAC;QAC1F,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAChF,IAAI,OAAO,IAAI,CAAC,gBAAgB,KAAK,QAAQ,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACtG,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,GAAG,CAAC,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3F,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,GAAG,CAAC,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,SAAS,EAAE,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;SAC3F,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,6BAA6B,CACpC,KAAa,EACb,cAAuB,EACvB,gBAAyB,EACzB,QAAiB;IAEjB,MAAM,IAAI,GAAG,+BAA+B,EAAE,CAAC;IAC/C,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,wBAAwB,GAAG,gBAAgB,IAAI,cAAc,IAAI,SAAS,UAAU,EAAE,EAAE,CAAC;IAC/F,MAAM,OAAO,GAA6B;QACxC,KAAK;QACL,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,gBAAgB,EAAE,wBAAwB;QAC1C,GAAG,CAAC,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IACF,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IACtD,mCAAmC,CAAC,wBAAwB,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,6BAA6B;IACpC,IAAI,CAAC;QACH,MAAM,CAAC,+BAA+B,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,cAAc;IAChB,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,KAAyB;IAC5C,OAAO,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,gBAAgB;IACvB,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAEpD,IAAI,CAAC,eAAe;QAAE,OAAO,SAAS,CAAC;IAEvC,IAAI,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC;QACjC,OAAO;YACL,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,eAAe;YACtB,cAAc;YACd,MAAM,EAAE,QAAQ;SACjB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,eAAe;QAC1B,MAAM,EAAE,QAAQ;KACjB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yBAAyB;IACvC,OAAO,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,IAAI,yBAAyB,EAAE,EAAE,CAAC;QAChC,MAAM,sBAAsB,EAAE,CAAC;IACjC,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC9D,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACpD,MAAM,iBAAiB,GAAG,4BAA4B,EAAE,CAAC;IAEzD,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,qBAAqB,GAAG,iBAAiB,EAAE,KAAK,KAAK,UAAU;YACnE,CAAC,CAAC,iBAAiB,CAAC,cAAc;YAClC,CAAC,CAAC,SAAS,CAAC;QACd,OAAO;YACL,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,UAAU;YACjB,GAAG,CAAC,qBAAqB,IAAI,mBAAmB;gBAC9C,CAAC,CAAC,EAAE,cAAc,EAAE,qBAAqB,IAAI,mBAAmB,EAAE;gBAClE,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,iBAAiB,EAAE,KAAK,KAAK,UAAU,IAAI,iBAAiB,CAAC,gBAAgB;gBAC/E,CAAC,CAAC,EAAE,gBAAgB,EAAE,iBAAiB,CAAC,gBAAgB,EAAE;gBAC1D,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,iBAAiB,EAAE,KAAK,KAAK,UAAU,IAAI,OAAO,iBAAiB,CAAC,QAAQ,KAAK,QAAQ;gBAC3F,CAAC,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,QAAQ,EAAE;gBAC1C,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,iBAAiB,EAAE,KAAK,KAAK,UAAU,IAAI,OAAO,iBAAiB,CAAC,SAAS,KAAK,QAAQ;gBAC5F,CAAC,CAAC,EAAE,SAAS,EAAE,iBAAiB,CAAC,SAAS,EAAE;gBAC5C,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,EAAE,OAAO;SAChB,CAAC;IACJ,CAAC;IAED,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO;YACL,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,cAAc;YACzB,MAAM,EAAE,OAAO;SAChB,CAAC;IACJ,CAAC;IAED,OAAO,gBAAgB,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,OAAO,OAAO,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,OAAO,OAAO,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAa,EAAE,cAAuB,EAAE,QAAiB;IAC9F,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IACrC,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAChC,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACrC,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAChC,6BAA6B,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,wBAAwB;IACtC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC5B,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACrC,6BAA6B,EAAE,CAAC;IAEhC,IAAI,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,SAAiB;IACtD,wBAAwB,EAAE,CAAC;IAC3B,MAAM,CAAC,GAAG,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,iBAA0B;IACjE,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACpD,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAEtD,IAAI,CAAC,iBAAiB,IAAI,cAAc,KAAK,iBAAiB,EAAE,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAClC,CAAC;IAED,IACE,eAAe;QACf,CAAC,WAAW,CAAC,eAAe,CAAC;QAC7B,CAAC,CAAC,iBAAiB,IAAI,eAAe,KAAK,iBAAiB,CAAC,EAC7D,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACnC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { WorkingLocation } from '../working-location.js';
|
|
2
|
+
export declare const DEFAULT_CONTINUITY_STALE_LEASE_WINDOW_MS: number;
|
|
3
|
+
export type LocalContinuityState = 'unbound' | 'same_controller_resumable' | 'same_controller_missing_runtime' | 'foreign_controller_live' | 'stale_reclaimable' | 'ambiguous_blocked';
|
|
4
|
+
export type RuntimeSessionState = 'recognized' | 'missing' | 'unknown';
|
|
5
|
+
export type TabIdentityState = 'verified' | 'mismatch' | 'unknown';
|
|
6
|
+
export type LegacyStateStatus = 'current' | 'legacy_safe' | 'legacy_conflict';
|
|
7
|
+
export interface ContinuityPersistedBinding {
|
|
8
|
+
tabId: string;
|
|
9
|
+
agentSessionId?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ContinuityEvidence {
|
|
12
|
+
currentAgentId: string;
|
|
13
|
+
persistedBinding?: ContinuityPersistedBinding | null;
|
|
14
|
+
workingLocation?: Pick<WorkingLocation, 'tabId' | 'agentId' | 'controlSessionId' | 'setAt'> | null;
|
|
15
|
+
lockOwner?: Pick<WorkingLocation, 'tabId' | 'agentId' | 'controlSessionId' | 'setAt'> | null;
|
|
16
|
+
runtimeSessionState: RuntimeSessionState;
|
|
17
|
+
ownerShellAlive?: boolean | null;
|
|
18
|
+
tabIdentityState?: TabIdentityState;
|
|
19
|
+
legacyStateStatus?: LegacyStateStatus;
|
|
20
|
+
staleLeaseWindowMs?: number;
|
|
21
|
+
now?: Date | string | number;
|
|
22
|
+
}
|
|
23
|
+
export interface ContinuityAssessment {
|
|
24
|
+
state: LocalContinuityState;
|
|
25
|
+
effectiveTabId: string | null;
|
|
26
|
+
sameController: boolean;
|
|
27
|
+
canRepairRuntime: boolean;
|
|
28
|
+
canReclaim: boolean;
|
|
29
|
+
reason: 'no_binding' | 'same_controller_live_runtime' | 'same_controller_runtime_missing' | 'runtime_status_unknown' | 'foreign_controller_live' | 'stale_lease_reclaimable' | 'tab_identity_mismatch' | 'legacy_state_conflict' | 'conflicting_controller_evidence' | 'missing_continuity_proof' | 'insufficient_stale_evidence';
|
|
30
|
+
}
|
|
31
|
+
export declare function isContinuityLeaseStale(setAt: Date | string | number | undefined, now?: Date | string | number, staleLeaseWindowMs?: number): boolean;
|
|
32
|
+
export declare function assessLocalContinuity(evidence: ContinuityEvidence): ContinuityAssessment;
|
|
33
|
+
//# sourceMappingURL=continuity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"continuity.d.ts","sourceRoot":"","sources":["../../../src/session/continuity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAI9D,eAAO,MAAM,wCAAwC,QAAqB,CAAC;AAE3E,MAAM,MAAM,oBAAoB,GAC5B,SAAS,GACT,2BAA2B,GAC3B,iCAAiC,GACjC,yBAAyB,GACzB,mBAAmB,GACnB,mBAAmB,CAAC;AAExB,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;AACvE,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,aAAa,GAAG,iBAAiB,CAAC;AAE9E,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACrD,eAAe,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,SAAS,GAAG,kBAAkB,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC;IACnG,SAAS,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,SAAS,GAAG,kBAAkB,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7F,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,eAAe,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,oBAAoB,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EACF,YAAY,GACZ,8BAA8B,GAC9B,iCAAiC,GACjC,wBAAwB,GACxB,yBAAyB,GACzB,yBAAyB,GACzB,uBAAuB,GACvB,uBAAuB,GACvB,iCAAiC,GACjC,0BAA0B,GAC1B,6BAA6B,CAAC;CACnC;AAgBD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,EACzC,GAAG,GAAE,IAAI,GAAG,MAAM,GAAG,MAAmB,EACxC,kBAAkB,GAAE,MAAiD,GACpE,OAAO,CAKT;AA6CD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,oBAAoB,CA6HxF"}
|