@agentclientprotocol/claude-agent-acp 0.88.1-preview.6 → 0.88.1-preview.7
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/acp-agent.d.ts +26 -1
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +91 -17
- package/dist/air-extension.d.ts +23 -0
- package/dist/air-extension.d.ts.map +1 -1
- package/dist/air-extension.js +23 -0
- package/dist/diff.d.ts +2 -0
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +37 -1
- package/dist/mcp-command.d.ts.map +1 -1
- package/dist/mcp-command.js +5 -0
- package/dist/session-index/activity.d.ts +56 -0
- package/dist/session-index/activity.d.ts.map +1 -0
- package/dist/session-index/activity.js +96 -0
- package/dist/session-index/archive-title.d.ts +52 -0
- package/dist/session-index/archive-title.d.ts.map +1 -0
- package/dist/session-index/archive-title.js +77 -0
- package/dist/session-index/connection.d.ts +152 -0
- package/dist/session-index/connection.d.ts.map +1 -0
- package/dist/session-index/connection.js +310 -0
- package/dist/session-index/dir-listing.d.ts +30 -0
- package/dist/session-index/dir-listing.d.ts.map +1 -0
- package/dist/session-index/dir-listing.js +110 -0
- package/dist/session-index/explicit-title.d.ts +34 -0
- package/dist/session-index/explicit-title.d.ts.map +1 -0
- package/dist/session-index/explicit-title.js +65 -0
- package/dist/session-index/first-prompt.d.ts +37 -0
- package/dist/session-index/first-prompt.d.ts.map +1 -0
- package/dist/session-index/first-prompt.js +195 -0
- package/dist/session-index/list-subscriptions.d.ts +190 -0
- package/dist/session-index/list-subscriptions.d.ts.map +1 -0
- package/dist/session-index/list-subscriptions.js +1163 -0
- package/dist/session-index/live-registry.d.ts +72 -0
- package/dist/session-index/live-registry.d.ts.map +1 -0
- package/dist/session-index/live-registry.js +240 -0
- package/dist/session-index/project-dirs.d.ts +45 -0
- package/dist/session-index/project-dirs.d.ts.map +1 -0
- package/dist/session-index/project-dirs.js +122 -0
- package/dist/session-index/service.d.ts +158 -0
- package/dist/session-index/service.d.ts.map +1 -0
- package/dist/session-index/service.js +546 -0
- package/dist/session-index/session-index.d.ts +201 -0
- package/dist/session-index/session-index.d.ts.map +1 -0
- package/dist/session-index/session-index.js +740 -0
- package/dist/session-index/session-info.d.ts +16 -0
- package/dist/session-index/session-info.d.ts.map +1 -0
- package/dist/session-index/session-info.js +51 -0
- package/dist/session-index/transcript-scan.d.ts +137 -0
- package/dist/session-index/transcript-scan.d.ts.map +1 -0
- package/dist/session-index/transcript-scan.js +545 -0
- package/dist/session-index/worktrees.d.ts +25 -0
- package/dist/session-index/worktrees.d.ts.map +1 -0
- package/dist/session-index/worktrees.js +128 -0
- package/dist/session-titles.d.ts +21 -0
- package/dist/session-titles.d.ts.map +1 -1
- package/dist/session-titles.js +101 -5
- package/dist/utils.d.ts +31 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +60 -2
- package/package.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live Claude Code processes on this machine, from the CLI's registry.
|
|
3
|
+
*
|
|
4
|
+
* Each running CLI writes `<config>/sessions/<pid>.json` with its session id,
|
|
5
|
+
* kind, entrypoint and status. The format is not documented; the reader keeps
|
|
6
|
+
* to the fields it needs and applies the liveness rules of Claude Desktop:
|
|
7
|
+
*
|
|
8
|
+
* - only `<pid>.json` files are read; `.key` files and other names are never
|
|
9
|
+
* opened, and no file is ever written or deleted;
|
|
10
|
+
* - a record whose JSON does not parse is read once more, then skipped;
|
|
11
|
+
* - a record of another pid domain (another machine or pid namespace) is
|
|
12
|
+
* skipped;
|
|
13
|
+
* - the process must be alive: `kill(pid, 0)` succeeds or fails with `EPERM`;
|
|
14
|
+
* - a recorded `procStart` must match the start time of the live process,
|
|
15
|
+
* so a reused pid does not count;
|
|
16
|
+
* - a record older than 24 hours counts only when its `procStart` matches.
|
|
17
|
+
*
|
|
18
|
+
* A snapshot is cached for {@link SNAPSHOT_TTL_MS}. The records tell only the
|
|
19
|
+
* `state` of a session list row; no operation is refused for them.
|
|
20
|
+
*/
|
|
21
|
+
export type LiveRecord = {
|
|
22
|
+
pid: number;
|
|
23
|
+
sessionId: string;
|
|
24
|
+
cwd?: string;
|
|
25
|
+
kind?: string;
|
|
26
|
+
entrypoint?: string;
|
|
27
|
+
status?: string;
|
|
28
|
+
statusUpdatedAt?: number;
|
|
29
|
+
};
|
|
30
|
+
export type LiveSnapshot = ReadonlyMap<string, LiveRecord>;
|
|
31
|
+
export type LiveRegistryDeps = {
|
|
32
|
+
/** The registry directory. Defaults to `<config>/sessions`. */
|
|
33
|
+
dir?: () => string;
|
|
34
|
+
now?: () => number;
|
|
35
|
+
/** Whether the process is alive. Defaults to `kill(pid, 0)`. */
|
|
36
|
+
isAlive?: (pid: number) => boolean;
|
|
37
|
+
/** The `ps -o lstart=` start time of each live pid. */
|
|
38
|
+
processStarts?: (pids: number[]) => Promise<Map<number, string>>;
|
|
39
|
+
/** This machine's pid domain, as the CLI computes it. */
|
|
40
|
+
pidDomain?: () => Promise<string>;
|
|
41
|
+
};
|
|
42
|
+
/** `<config>/sessions`, where each CLI process registers itself. */
|
|
43
|
+
export declare function liveRegistryDir(): string;
|
|
44
|
+
export declare class LiveSessionRegistry {
|
|
45
|
+
private cached?;
|
|
46
|
+
private readonly dir;
|
|
47
|
+
private readonly now;
|
|
48
|
+
private readonly isAlive;
|
|
49
|
+
private readonly processStarts;
|
|
50
|
+
private readonly pidDomain;
|
|
51
|
+
/** The start times `ps` gave, by pid, and when. */
|
|
52
|
+
private readonly starts;
|
|
53
|
+
constructor(deps?: LiveRegistryDeps);
|
|
54
|
+
/** The live records by session id. Cached for 5 s. */
|
|
55
|
+
snapshot(): Promise<LiveSnapshot>;
|
|
56
|
+
/** Every live record, several per session when several processes hold it. */
|
|
57
|
+
private read;
|
|
58
|
+
/**
|
|
59
|
+
* The live record of each of the registry files `names`, by name:
|
|
60
|
+
* undefined for a file that is gone or holds no live record. A name that
|
|
61
|
+
* is not `<pid>.json` (a `.key` file, a temporary file) is never opened and
|
|
62
|
+
* not in the result. The rules of {@link read} apply. `recentStarts` takes
|
|
63
|
+
* a start time that `ps` gave in the last 10 s when it matches the record,
|
|
64
|
+
* so a record that changes often does not run `ps` each time.
|
|
65
|
+
*/
|
|
66
|
+
readFiles(names: readonly string[], options?: {
|
|
67
|
+
recentStarts?: boolean;
|
|
68
|
+
}): Promise<Map<string, LiveRecord | undefined>>;
|
|
69
|
+
}
|
|
70
|
+
/** One record per session: the one with the newest status. */
|
|
71
|
+
export declare function bySession(records: Iterable<LiveRecord>): LiveSnapshot;
|
|
72
|
+
//# sourceMappingURL=live-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-registry.d.ts","sourceRoot":"","sources":["../../src/session-index/live-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAiBH,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE3D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,uDAAuD;IACvD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjE,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAwFD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAC,CAAkD;IACjE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmD;IACjF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoD;gBAE/D,IAAI,GAAE,gBAAqB;IAQvC,sDAAsD;IACtD,QAAQ,IAAI,OAAO,CAAC,YAAY,CAAC;IAYjC,6EAA6E;YAC/D,IAAI;IAYlB;;;;;;;OAOG;IACG,SAAS,CACb,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,GAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAO,GACvC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC,CAAC;CAqEhD;AAED,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY,CASrE"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live Claude Code processes on this machine, from the CLI's registry.
|
|
3
|
+
*
|
|
4
|
+
* Each running CLI writes `<config>/sessions/<pid>.json` with its session id,
|
|
5
|
+
* kind, entrypoint and status. The format is not documented; the reader keeps
|
|
6
|
+
* to the fields it needs and applies the liveness rules of Claude Desktop:
|
|
7
|
+
*
|
|
8
|
+
* - only `<pid>.json` files are read; `.key` files and other names are never
|
|
9
|
+
* opened, and no file is ever written or deleted;
|
|
10
|
+
* - a record whose JSON does not parse is read once more, then skipped;
|
|
11
|
+
* - a record of another pid domain (another machine or pid namespace) is
|
|
12
|
+
* skipped;
|
|
13
|
+
* - the process must be alive: `kill(pid, 0)` succeeds or fails with `EPERM`;
|
|
14
|
+
* - a recorded `procStart` must match the start time of the live process,
|
|
15
|
+
* so a reused pid does not count;
|
|
16
|
+
* - a record older than 24 hours counts only when its `procStart` matches.
|
|
17
|
+
*
|
|
18
|
+
* A snapshot is cached for {@link SNAPSHOT_TTL_MS}. The records tell only the
|
|
19
|
+
* `state` of a session list row; no operation is refused for them.
|
|
20
|
+
*/
|
|
21
|
+
import { execFile } from "node:child_process";
|
|
22
|
+
import * as fs from "node:fs/promises";
|
|
23
|
+
import * as os from "node:os";
|
|
24
|
+
import * as path from "node:path";
|
|
25
|
+
import { claudeConfigDir } from "../paths.js";
|
|
26
|
+
import { errorCode } from "./project-dirs.js";
|
|
27
|
+
const SNAPSHOT_TTL_MS = 5_000;
|
|
28
|
+
const STALE_RECORD_MS = 24 * 60 * 60 * 1000;
|
|
29
|
+
const PS_TIMEOUT_MS = 1_000;
|
|
30
|
+
const RECORD_FILE_PATTERN = /^(\d+)\.json$/;
|
|
31
|
+
/** How long a process start time read with `ps` serves a reader that asks
|
|
32
|
+
* for recent ones (see {@link LiveSessionRegistry.readFiles}). */
|
|
33
|
+
const RECENT_START_MS = 10_000;
|
|
34
|
+
/** `<config>/sessions`, where each CLI process registers itself. */
|
|
35
|
+
export function liveRegistryDir() {
|
|
36
|
+
return path.join(claudeConfigDir(), "sessions");
|
|
37
|
+
}
|
|
38
|
+
function defaultIsAlive(pid) {
|
|
39
|
+
try {
|
|
40
|
+
process.kill(pid, 0);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return errorCode(error) === "EPERM";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** The start times of `pids`, from one `ps` call, in the format the CLI
|
|
48
|
+
* records (`LC_ALL=C TZ=UTC ps -o lstart=`). */
|
|
49
|
+
function defaultProcessStarts(pids) {
|
|
50
|
+
const result = new Map();
|
|
51
|
+
if (pids.length === 0 || process.platform === "win32")
|
|
52
|
+
return Promise.resolve(result);
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
execFile("ps", ["-o", "pid=,lstart=", "-p", pids.join(",")], { timeout: PS_TIMEOUT_MS, env: { ...process.env, LC_ALL: "C", TZ: "UTC" } }, (_error, stdout) => {
|
|
55
|
+
// `ps` exits 1 when one of the pids is gone; its output still holds
|
|
56
|
+
// the live ones.
|
|
57
|
+
for (const line of String(stdout ?? "").split("\n")) {
|
|
58
|
+
const match = /^\s*(\d+)\s+(.+?)\s*$/.exec(line);
|
|
59
|
+
if (match)
|
|
60
|
+
result.set(Number(match[1]), normalizeStart(match[2]));
|
|
61
|
+
}
|
|
62
|
+
resolve(result);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function normalizeStart(value) {
|
|
67
|
+
return value.trim().replace(/\s+/g, " ");
|
|
68
|
+
}
|
|
69
|
+
let cachedPidDomain;
|
|
70
|
+
/** The pid domain the CLI records: the platform on macOS, the machine id and
|
|
71
|
+
* pid namespace on Linux, the host name on Windows. */
|
|
72
|
+
function defaultPidDomain() {
|
|
73
|
+
cachedPidDomain ??= (async () => {
|
|
74
|
+
if (process.platform === "win32")
|
|
75
|
+
return `win32:${os.hostname().toLowerCase()}`;
|
|
76
|
+
if (process.platform !== "linux")
|
|
77
|
+
return process.platform;
|
|
78
|
+
const [machineId, pidNamespace] = await Promise.all([
|
|
79
|
+
fs.readFile("/etc/machine-id", "utf8").then((value) => value.trim(), () => ""),
|
|
80
|
+
fs.readlink("/proc/self/ns/pid").catch(() => ""),
|
|
81
|
+
]);
|
|
82
|
+
return `linux:${machineId}:${pidNamespace}`;
|
|
83
|
+
})();
|
|
84
|
+
return cachedPidDomain;
|
|
85
|
+
}
|
|
86
|
+
async function readRecord(file) {
|
|
87
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
88
|
+
let text;
|
|
89
|
+
try {
|
|
90
|
+
text = await fs.readFile(file, "utf8");
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const value = JSON.parse(text);
|
|
97
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
98
|
+
? value
|
|
99
|
+
: undefined;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// The CLI may be rewriting the file; read it once more.
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
function finiteNumber(value) {
|
|
108
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
109
|
+
}
|
|
110
|
+
function optionalString(value) {
|
|
111
|
+
return typeof value === "string" ? value : undefined;
|
|
112
|
+
}
|
|
113
|
+
export class LiveSessionRegistry {
|
|
114
|
+
cached;
|
|
115
|
+
dir;
|
|
116
|
+
now;
|
|
117
|
+
isAlive;
|
|
118
|
+
processStarts;
|
|
119
|
+
pidDomain;
|
|
120
|
+
/** The start times `ps` gave, by pid, and when. */
|
|
121
|
+
starts = new Map();
|
|
122
|
+
constructor(deps = {}) {
|
|
123
|
+
this.dir = deps.dir ?? liveRegistryDir;
|
|
124
|
+
this.now = deps.now ?? Date.now;
|
|
125
|
+
this.isAlive = deps.isAlive ?? defaultIsAlive;
|
|
126
|
+
this.processStarts = deps.processStarts ?? defaultProcessStarts;
|
|
127
|
+
this.pidDomain = deps.pidDomain ?? defaultPidDomain;
|
|
128
|
+
}
|
|
129
|
+
/** The live records by session id. Cached for 5 s. */
|
|
130
|
+
snapshot() {
|
|
131
|
+
const now = this.now();
|
|
132
|
+
if (this.cached && now - this.cached.at < SNAPSHOT_TTL_MS) {
|
|
133
|
+
return this.cached.snapshot;
|
|
134
|
+
}
|
|
135
|
+
const snapshot = this.read()
|
|
136
|
+
.then(bySession)
|
|
137
|
+
.catch(() => new Map());
|
|
138
|
+
this.cached = { at: now, snapshot };
|
|
139
|
+
return snapshot;
|
|
140
|
+
}
|
|
141
|
+
/** Every live record, several per session when several processes hold it. */
|
|
142
|
+
async read() {
|
|
143
|
+
let names;
|
|
144
|
+
try {
|
|
145
|
+
names = await fs.readdir(this.dir());
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
return [...(await this.readFiles(names)).values()].filter((record) => record !== undefined);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The live record of each of the registry files `names`, by name:
|
|
154
|
+
* undefined for a file that is gone or holds no live record. A name that
|
|
155
|
+
* is not `<pid>.json` (a `.key` file, a temporary file) is never opened and
|
|
156
|
+
* not in the result. The rules of {@link read} apply. `recentStarts` takes
|
|
157
|
+
* a start time that `ps` gave in the last 10 s when it matches the record,
|
|
158
|
+
* so a record that changes often does not run `ps` each time.
|
|
159
|
+
*/
|
|
160
|
+
async readFiles(names, options = {}) {
|
|
161
|
+
const dir = this.dir();
|
|
162
|
+
const result = new Map();
|
|
163
|
+
const domain = await this.pidDomain();
|
|
164
|
+
const candidates = [];
|
|
165
|
+
await Promise.all(names.map(async (name) => {
|
|
166
|
+
const match = RECORD_FILE_PATTERN.exec(name);
|
|
167
|
+
if (!match)
|
|
168
|
+
return;
|
|
169
|
+
result.set(name, undefined);
|
|
170
|
+
const pid = Number(match[1]);
|
|
171
|
+
const raw = await readRecord(path.join(dir, name));
|
|
172
|
+
if (!raw || raw.pid !== pid || typeof raw.sessionId !== "string")
|
|
173
|
+
return;
|
|
174
|
+
if (typeof raw.pidDomain === "string" && raw.pidDomain !== domain)
|
|
175
|
+
return;
|
|
176
|
+
if (!this.isAlive(pid))
|
|
177
|
+
return;
|
|
178
|
+
candidates.push({ name, pid, raw });
|
|
179
|
+
}));
|
|
180
|
+
const now = this.now();
|
|
181
|
+
const starts = new Map();
|
|
182
|
+
const needStarts = [];
|
|
183
|
+
for (const { pid, raw } of candidates) {
|
|
184
|
+
if (typeof raw.procStart !== "string")
|
|
185
|
+
continue;
|
|
186
|
+
const recent = this.starts.get(pid);
|
|
187
|
+
// A start that differs may be a new process under the same pid.
|
|
188
|
+
if (options.recentStarts &&
|
|
189
|
+
recent &&
|
|
190
|
+
now - recent.at < RECENT_START_MS &&
|
|
191
|
+
recent.start === normalizeStart(raw.procStart)) {
|
|
192
|
+
starts.set(pid, recent.start);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
needStarts.push(pid);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (needStarts.length > 0) {
|
|
199
|
+
for (const [pid, start] of await this.processStarts(needStarts)) {
|
|
200
|
+
starts.set(pid, start);
|
|
201
|
+
this.starts.set(pid, { start, at: now });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
for (const [pid, { at }] of this.starts) {
|
|
205
|
+
if (now - at >= RECENT_START_MS)
|
|
206
|
+
this.starts.delete(pid);
|
|
207
|
+
}
|
|
208
|
+
for (const { name, pid, raw } of candidates) {
|
|
209
|
+
const recorded = typeof raw.procStart === "string" ? normalizeStart(raw.procStart) : undefined;
|
|
210
|
+
const actual = starts.get(pid);
|
|
211
|
+
if (recorded !== undefined && actual !== undefined && recorded !== actual)
|
|
212
|
+
continue;
|
|
213
|
+
const procStartMatches = recorded !== undefined && actual === recorded;
|
|
214
|
+
const lastSeen = Math.max(finiteNumber(raw.updatedAt) ?? 0, finiteNumber(raw.statusUpdatedAt) ?? 0, finiteNumber(raw.startedAt) ?? 0);
|
|
215
|
+
if (now - lastSeen > STALE_RECORD_MS && !procStartMatches)
|
|
216
|
+
continue;
|
|
217
|
+
result.set(name, {
|
|
218
|
+
pid,
|
|
219
|
+
sessionId: raw.sessionId,
|
|
220
|
+
cwd: optionalString(raw.cwd),
|
|
221
|
+
kind: optionalString(raw.kind),
|
|
222
|
+
entrypoint: optionalString(raw.entrypoint),
|
|
223
|
+
status: optionalString(raw.status),
|
|
224
|
+
statusUpdatedAt: finiteNumber(raw.statusUpdatedAt),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/** One record per session: the one with the newest status. */
|
|
231
|
+
export function bySession(records) {
|
|
232
|
+
const result = new Map();
|
|
233
|
+
for (const record of records) {
|
|
234
|
+
const previous = result.get(record.sessionId);
|
|
235
|
+
if (!previous || (record.statusUpdatedAt ?? 0) > (previous.statusUpdatedAt ?? 0)) {
|
|
236
|
+
result.set(record.sessionId, record);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where Claude Code keeps the transcripts of a working directory.
|
|
3
|
+
*
|
|
4
|
+
* The CLI stores the transcripts of `cwd` in `<config>/projects/<encoded cwd>/`.
|
|
5
|
+
* The encoding replaces every character that is not ASCII alphanumeric with `-`.
|
|
6
|
+
* A name longer than {@link MAX_SANITIZED_LENGTH} is cut and gets a hash
|
|
7
|
+
* suffix; the CLI (Bun) and the SDK (Node) compute different hashes, so a long
|
|
8
|
+
* name is matched by its prefix, as the SDK does.
|
|
9
|
+
*
|
|
10
|
+
* The encoding is lossy (`/a/b-c` and `/a/b/c` share a directory), so a
|
|
11
|
+
* directory name is never decoded: a path belongs to a directory only when
|
|
12
|
+
* the path encodes to the name.
|
|
13
|
+
*/
|
|
14
|
+
/** Whether `value` is a Claude session id (a UUID). Only such ids name a
|
|
15
|
+
* transcript, and only such ids are safe to use as a file name. */
|
|
16
|
+
export declare function isSessionId(value: unknown): value is string;
|
|
17
|
+
/** `<config>/projects`, normalized like the SDK does. */
|
|
18
|
+
export declare function projectsRoot(): string;
|
|
19
|
+
/** The project directory name of `cwd`, exactly as the SDK computes it. */
|
|
20
|
+
export declare function encodeProjectPath(cwd: string): string;
|
|
21
|
+
/** NFC on darwin, as the SDK normalizes paths there. */
|
|
22
|
+
export declare function normalizePath(value: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Whether `dirName` is the project directory of `cwd`: the exact encoding, or,
|
|
25
|
+
* for a long path, a directory with the same cut prefix and any hash suffix.
|
|
26
|
+
*/
|
|
27
|
+
export declare function projectDirMatches(dirName: string, cwd: string): boolean;
|
|
28
|
+
/** Whether a transcript's `cwd` belongs to `projectPath`, compared the way
|
|
29
|
+
* the SDK does: the full sanitized forms, ignoring case where the file
|
|
30
|
+
* system does. */
|
|
31
|
+
export declare function sameProjectPath(cwd: string, projectPath: string): boolean;
|
|
32
|
+
/** Whether `dirName` is the exact project directory of `cwd`, as the SDK
|
|
33
|
+
* finds it by `dir`: the exact encoding, on macOS in any case (the file
|
|
34
|
+
* system ignores it). */
|
|
35
|
+
export declare function isExactProjectDir(dirName: string, cwd: string): boolean;
|
|
36
|
+
/** The real path of `cwd`, like the SDK resolves it before encoding. Falls
|
|
37
|
+
* back to `cwd` when it does not exist. */
|
|
38
|
+
export declare function canonicalPath(cwd: string): Promise<string>;
|
|
39
|
+
/** The names of the project directories of `cwd` among `rootEntries`. */
|
|
40
|
+
export declare function projectDirsOf(cwd: string, rootEntries: readonly string[]): string[];
|
|
41
|
+
/** `value` and each of its ancestors, nearest first. */
|
|
42
|
+
export declare function pathAndAncestors(value: string): string[];
|
|
43
|
+
/** The `code` of a Node.js system error, if it has one. */
|
|
44
|
+
export declare function errorCode(error: unknown): string | undefined;
|
|
45
|
+
//# sourceMappingURL=project-dirs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-dirs.d.ts","sourceRoot":"","sources":["../../src/session-index/project-dirs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH;oEACoE;AACpE,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE3D;AAED,yDAAyD;AACzD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAeD,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIrD;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAOvE;AAED;;mBAEmB;AACnB,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAMzE;AAED;;0BAE0B;AAC1B,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAIvE;AAED;4CAC4C;AAC5C,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMhE;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAOnF;AAED,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CASxD;AAED,2DAA2D;AAC3D,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAG5D"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where Claude Code keeps the transcripts of a working directory.
|
|
3
|
+
*
|
|
4
|
+
* The CLI stores the transcripts of `cwd` in `<config>/projects/<encoded cwd>/`.
|
|
5
|
+
* The encoding replaces every character that is not ASCII alphanumeric with `-`.
|
|
6
|
+
* A name longer than {@link MAX_SANITIZED_LENGTH} is cut and gets a hash
|
|
7
|
+
* suffix; the CLI (Bun) and the SDK (Node) compute different hashes, so a long
|
|
8
|
+
* name is matched by its prefix, as the SDK does.
|
|
9
|
+
*
|
|
10
|
+
* The encoding is lossy (`/a/b-c` and `/a/b/c` share a directory), so a
|
|
11
|
+
* directory name is never decoded: a path belongs to a directory only when
|
|
12
|
+
* the path encodes to the name.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from "node:fs/promises";
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
import { claudeConfigDir } from "../paths.js";
|
|
17
|
+
/** The SDK cuts an encoded path at this length and appends a hash. */
|
|
18
|
+
const MAX_SANITIZED_LENGTH = 200;
|
|
19
|
+
const SESSION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
20
|
+
/** Whether `value` is a Claude session id (a UUID). Only such ids name a
|
|
21
|
+
* transcript, and only such ids are safe to use as a file name. */
|
|
22
|
+
export function isSessionId(value) {
|
|
23
|
+
return typeof value === "string" && SESSION_ID_PATTERN.test(value);
|
|
24
|
+
}
|
|
25
|
+
/** `<config>/projects`, normalized like the SDK does. */
|
|
26
|
+
export function projectsRoot() {
|
|
27
|
+
return path.join(claudeConfigDir(), "projects").normalize("NFC");
|
|
28
|
+
}
|
|
29
|
+
function sanitize(value) {
|
|
30
|
+
return value.replace(/[^a-zA-Z0-9]/g, "-");
|
|
31
|
+
}
|
|
32
|
+
/** The SDK's 32-bit string hash for long project names. */
|
|
33
|
+
function hashCode(value) {
|
|
34
|
+
let hash = 0;
|
|
35
|
+
for (let i = 0; i < value.length; i++) {
|
|
36
|
+
hash = ((hash << 5) - hash + value.charCodeAt(i)) | 0;
|
|
37
|
+
}
|
|
38
|
+
return hash;
|
|
39
|
+
}
|
|
40
|
+
/** The project directory name of `cwd`, exactly as the SDK computes it. */
|
|
41
|
+
export function encodeProjectPath(cwd) {
|
|
42
|
+
const sanitized = sanitize(normalizePath(cwd));
|
|
43
|
+
if (sanitized.length <= MAX_SANITIZED_LENGTH)
|
|
44
|
+
return sanitized;
|
|
45
|
+
return `${sanitized.slice(0, MAX_SANITIZED_LENGTH)}-${Math.abs(hashCode(normalizePath(cwd))).toString(36)}`;
|
|
46
|
+
}
|
|
47
|
+
/** NFC on darwin, as the SDK normalizes paths there. */
|
|
48
|
+
export function normalizePath(value) {
|
|
49
|
+
return process.platform === "darwin" ? value.normalize("NFC") : value;
|
|
50
|
+
}
|
|
51
|
+
function sameName(a, b) {
|
|
52
|
+
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Whether `dirName` is the project directory of `cwd`: the exact encoding, or,
|
|
56
|
+
* for a long path, a directory with the same cut prefix and any hash suffix.
|
|
57
|
+
*/
|
|
58
|
+
export function projectDirMatches(dirName, cwd) {
|
|
59
|
+
if (sameName(dirName, encodeProjectPath(cwd)))
|
|
60
|
+
return true;
|
|
61
|
+
const sanitized = sanitize(normalizePath(cwd));
|
|
62
|
+
if (sanitized.length <= MAX_SANITIZED_LENGTH)
|
|
63
|
+
return false;
|
|
64
|
+
const prefix = `${sanitized.slice(0, MAX_SANITIZED_LENGTH)}-`;
|
|
65
|
+
const name = process.platform === "win32" ? dirName.toLowerCase() : dirName;
|
|
66
|
+
return name.startsWith(process.platform === "win32" ? prefix.toLowerCase() : prefix);
|
|
67
|
+
}
|
|
68
|
+
/** Whether a transcript's `cwd` belongs to `projectPath`, compared the way
|
|
69
|
+
* the SDK does: the full sanitized forms, ignoring case where the file
|
|
70
|
+
* system does. */
|
|
71
|
+
export function sameProjectPath(cwd, projectPath) {
|
|
72
|
+
const a = sanitize(normalizePath(cwd));
|
|
73
|
+
const b = sanitize(normalizePath(projectPath));
|
|
74
|
+
return process.platform === "win32" || process.platform === "darwin"
|
|
75
|
+
? a.toLowerCase() === b.toLowerCase()
|
|
76
|
+
: a === b;
|
|
77
|
+
}
|
|
78
|
+
/** Whether `dirName` is the exact project directory of `cwd`, as the SDK
|
|
79
|
+
* finds it by `dir`: the exact encoding, on macOS in any case (the file
|
|
80
|
+
* system ignores it). */
|
|
81
|
+
export function isExactProjectDir(dirName, cwd) {
|
|
82
|
+
const exact = encodeProjectPath(cwd);
|
|
83
|
+
if (sameName(dirName, exact))
|
|
84
|
+
return true;
|
|
85
|
+
return process.platform === "darwin" && dirName.toLowerCase() === exact.toLowerCase();
|
|
86
|
+
}
|
|
87
|
+
/** The real path of `cwd`, like the SDK resolves it before encoding. Falls
|
|
88
|
+
* back to `cwd` when it does not exist. */
|
|
89
|
+
export async function canonicalPath(cwd) {
|
|
90
|
+
try {
|
|
91
|
+
return normalizePath(await fs.realpath(cwd));
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return normalizePath(cwd);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** The names of the project directories of `cwd` among `rootEntries`. */
|
|
98
|
+
export function projectDirsOf(cwd, rootEntries) {
|
|
99
|
+
const exact = encodeProjectPath(cwd);
|
|
100
|
+
const sanitizedLength = sanitize(normalizePath(cwd)).length;
|
|
101
|
+
if (sanitizedLength <= MAX_SANITIZED_LENGTH) {
|
|
102
|
+
return rootEntries.filter((name) => sameName(name, exact));
|
|
103
|
+
}
|
|
104
|
+
return rootEntries.filter((name) => projectDirMatches(name, cwd));
|
|
105
|
+
}
|
|
106
|
+
/** `value` and each of its ancestors, nearest first. */
|
|
107
|
+
export function pathAndAncestors(value) {
|
|
108
|
+
const result = [];
|
|
109
|
+
let current = value;
|
|
110
|
+
for (;;) {
|
|
111
|
+
result.push(current);
|
|
112
|
+
const parent = path.dirname(current);
|
|
113
|
+
if (parent === current)
|
|
114
|
+
return result;
|
|
115
|
+
current = parent;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** The `code` of a Node.js system error, if it has one. */
|
|
119
|
+
export function errorCode(error) {
|
|
120
|
+
const code = error?.code;
|
|
121
|
+
return typeof code === "string" ? code : undefined;
|
|
122
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `sessionIndex` AIR extension: the session list, rename, archive and
|
|
3
|
+
* delete of a client that declared the capability.
|
|
4
|
+
*
|
|
5
|
+
* Wire contract: docs/air-extensions.md, "Session index". Everything here is
|
|
6
|
+
* reached only for a `sessionIndex` client, except the archive that an AIR
|
|
7
|
+
* client without the capability gets in place of a delete (see
|
|
8
|
+
* `ClaudeAcpAgent.deleteSession`).
|
|
9
|
+
*/
|
|
10
|
+
import { RequestError, type ListSessionsRequest, type ListSessionsResponse } from "@agentclientprotocol/sdk";
|
|
11
|
+
import type { OwnSessionState } from "./activity.js";
|
|
12
|
+
import { type ListChanges, type ListSubscribeResponse } from "./list-subscriptions.js";
|
|
13
|
+
import { LiveSessionRegistry } from "./live-registry.js";
|
|
14
|
+
import { SessionIndex, type ArchivedFilter, type GetSessionInfo, type ListCursor } from "./session-index.js";
|
|
15
|
+
export declare const SESSION_RENAME_METHOD = "_session/rename";
|
|
16
|
+
export declare const SESSION_ARCHIVE_METHOD = "_session/archive";
|
|
17
|
+
export declare const SESSION_UNARCHIVE_METHOD = "_session/unarchive";
|
|
18
|
+
export type SessionIdRequest = {
|
|
19
|
+
sessionId: string;
|
|
20
|
+
};
|
|
21
|
+
export type RenameSessionRequest = {
|
|
22
|
+
sessionId: string;
|
|
23
|
+
title: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function sessionNotFound(sessionId: string): RequestError;
|
|
26
|
+
export declare function parseSessionIdRequest(value: unknown): SessionIdRequest;
|
|
27
|
+
export declare function parseRenameSessionRequest(value: unknown): RenameSessionRequest;
|
|
28
|
+
type ListOptions = {
|
|
29
|
+
limit: number;
|
|
30
|
+
archived: ArchivedFilter;
|
|
31
|
+
includeWorktrees: boolean;
|
|
32
|
+
};
|
|
33
|
+
/** What a cursor is bound to: the request values that select the rows. */
|
|
34
|
+
export type ListScope = {
|
|
35
|
+
cwd: string | null;
|
|
36
|
+
archived: ArchivedFilter;
|
|
37
|
+
includeWorktrees: boolean;
|
|
38
|
+
};
|
|
39
|
+
/** `_meta.jetbrains.air.list` of a list request. */
|
|
40
|
+
export declare function parseListOptions(meta: unknown): ListOptions;
|
|
41
|
+
export declare function encodeListCursor(cursor: ListCursor, scope: ListScope): string;
|
|
42
|
+
/** The position a cursor names. A cursor of another cwd or filter, or one
|
|
43
|
+
* this adapter did not issue, is rejected. */
|
|
44
|
+
export declare function decodeListCursor(cursor: string, scope: ListScope): ListCursor;
|
|
45
|
+
/** Writes `<projectDir>/<sessionId>/custom-title.json` the way the CLI's
|
|
46
|
+
* `/rename` does: file 0600 in a 0700 directory, replaced atomically. The
|
|
47
|
+
* temporary file has a random name of the CLI's `custom-title.json.tmp.*`
|
|
48
|
+
* pattern, and only a temporary file this call created is removed. */
|
|
49
|
+
export declare function writeCustomTitleSidecar(transcriptPath: string, title: string): Promise<void>;
|
|
50
|
+
/** The title a change stores for a copy whose title is `current`
|
|
51
|
+
* (undefined: none yet), or undefined to leave the copy as it is. */
|
|
52
|
+
export type TitleChange = (current: string | undefined, sessionId: string) => string | undefined;
|
|
53
|
+
/** `_session/rename`: the new title, with the archive prefix on a copy
|
|
54
|
+
* that is archived, so a rename keeps the archive state. */
|
|
55
|
+
export declare function renameTo(title: string): TitleChange;
|
|
56
|
+
/** `_session/archive` and `_session/unarchive`: the current title with or
|
|
57
|
+
* without the archive prefix; a copy already in that state is left alone. */
|
|
58
|
+
export declare function archiveTo(archived: boolean): TitleChange;
|
|
59
|
+
/**
|
|
60
|
+
* `session/delete` of an AIR client without `sessionIndex`, which uses delete
|
|
61
|
+
* to mark a session done: archives it instead, so the transcript survives. A
|
|
62
|
+
* session without a transcript fails as the SDK delete did.
|
|
63
|
+
*/
|
|
64
|
+
export declare function archiveInsteadOfDelete(sessionId: string, service: SessionIndexService): Promise<void>;
|
|
65
|
+
/** How {@link SessionIndexService.retitle} reaches the session. */
|
|
66
|
+
export type RetitleOptions = {
|
|
67
|
+
/** The CLI of the session runs here: it stores the title of its own
|
|
68
|
+
* transcript (`rename_session`). `stored`: the title last stored through
|
|
69
|
+
* it, which it holds; `shown`: the title last shown for a session that has
|
|
70
|
+
* neither that nor a transcript. */
|
|
71
|
+
live?: {
|
|
72
|
+
cwd: string;
|
|
73
|
+
rename: (title: string) => Promise<void>;
|
|
74
|
+
/** Read once the session's earlier title changes are done. */
|
|
75
|
+
stored: () => string | undefined;
|
|
76
|
+
/** Records a title the CLI took, before the next change starts. */
|
|
77
|
+
remember: (title: string) => void;
|
|
78
|
+
shown?: string;
|
|
79
|
+
};
|
|
80
|
+
/** A session without a transcript is not unknown: a new session this
|
|
81
|
+
* connection runs, or a live one being renamed. */
|
|
82
|
+
mayBeUnwritten?: boolean;
|
|
83
|
+
/** `always` writes the CLI title sidecar next to every titled copy;
|
|
84
|
+
* `existing` rewrites only one that is there already. */
|
|
85
|
+
sidecar: "always" | "existing";
|
|
86
|
+
};
|
|
87
|
+
export type SessionIndexDeps = {
|
|
88
|
+
getSessionInfo?: GetSessionInfo;
|
|
89
|
+
deleteSession?: (sessionId: string) => Promise<void>;
|
|
90
|
+
registry?: LiveSessionRegistry;
|
|
91
|
+
now?: () => number;
|
|
92
|
+
/** What this connection knows of a session it runs. */
|
|
93
|
+
ownSessionState?: (sessionId: string) => OwnSessionState | undefined;
|
|
94
|
+
/** Sends `_session/list/changes`. */
|
|
95
|
+
notifyListChanges?: (changes: ListChanges) => Promise<void>;
|
|
96
|
+
logError: (message: string, error: unknown) => void;
|
|
97
|
+
};
|
|
98
|
+
export declare class SessionIndexService {
|
|
99
|
+
private readonly deps;
|
|
100
|
+
readonly index: SessionIndex;
|
|
101
|
+
readonly registry: LiveSessionRegistry;
|
|
102
|
+
private subscriptions?;
|
|
103
|
+
/** Set for good by {@link dispose}: no subscription is made after it. */
|
|
104
|
+
private disposed;
|
|
105
|
+
/** The mutation in flight per session, which the next one waits for. */
|
|
106
|
+
private readonly mutations;
|
|
107
|
+
private readonly now;
|
|
108
|
+
private readonly deleteSession;
|
|
109
|
+
constructor(deps: SessionIndexDeps);
|
|
110
|
+
/** `session/list` of a `sessionIndex` client. `own` reports the sessions
|
|
111
|
+
* that this connection runs. */
|
|
112
|
+
list(params: ListSessionsRequest, own: (sessionId: string) => OwnSessionState | undefined): Promise<ListSessionsResponse>;
|
|
113
|
+
/** Runs `mutation` after the previous mutation of the session ended. */
|
|
114
|
+
private exclusive;
|
|
115
|
+
/**
|
|
116
|
+
* Retitles every transcript of a session with `change`: rename, archive
|
|
117
|
+
* and unarchive. Returns the title stored through the CLI that runs here,
|
|
118
|
+
* else the title of the first copy that got one; undefined when nothing
|
|
119
|
+
* was stored.
|
|
120
|
+
*
|
|
121
|
+
* - The CLI runs here (`live`): it titles its own transcript and sidecar
|
|
122
|
+
* (`rename_session`), the only writer that cannot be overtaken by the
|
|
123
|
+
* title the CLI holds. The other copies get the records and the sidecar
|
|
124
|
+
* here, as a best effort once the CLI has the title: a failure is
|
|
125
|
+
* logged, not returned.
|
|
126
|
+
* - Otherwise every transcript gets the records (and the sidecar, see
|
|
127
|
+
* {@link RetitleOptions.sidecar}).
|
|
128
|
+
*
|
|
129
|
+
* As the CLI's `/rename`, the records are written whether or not another
|
|
130
|
+
* process has the session open.
|
|
131
|
+
*
|
|
132
|
+
* A session without a transcript is unknown (`-32002`), unless
|
|
133
|
+
* {@link RetitleOptions.mayBeUnwritten}.
|
|
134
|
+
*/
|
|
135
|
+
retitle(sessionId: string, change: TitleChange, options: RetitleOptions): Promise<string | undefined>;
|
|
136
|
+
/** Appends the title records `change` gives each copy, then its sidecar.
|
|
137
|
+
* A copy already in the requested state gets nothing, but its sidecar is
|
|
138
|
+
* brought in line with it. Returns the title the first titled copy got. */
|
|
139
|
+
private titleCopies;
|
|
140
|
+
/** Deletes every transcript of the session, whoever else has it open, as
|
|
141
|
+
* the CLI does. `known`: the session was loaded here (and is torn down
|
|
142
|
+
* already), so a missing transcript is no error.
|
|
143
|
+
*
|
|
144
|
+
* The SDK deletes the first non-empty transcript it finds and its
|
|
145
|
+
* `<sessionId>/` directory, one copy per call; empty transcripts, which it
|
|
146
|
+
* skips, are removed here. */
|
|
147
|
+
delete(sessionId: string, known: boolean): Promise<void>;
|
|
148
|
+
/** `_session/list/subscribe`: pushes the changed rows of `cwd` (an
|
|
149
|
+
* absolute path) until {@link unsubscribeList} or {@link dispose}. */
|
|
150
|
+
subscribeList(cwd: string): Promise<ListSubscribeResponse>;
|
|
151
|
+
/** `_session/list/unsubscribe`. Idempotent. */
|
|
152
|
+
unsubscribeList(subscriptionId: string): void;
|
|
153
|
+
/** A session this connection runs changed what its row shows. */
|
|
154
|
+
ownSessionChanged(sessionId: string): void;
|
|
155
|
+
dispose(): void;
|
|
156
|
+
}
|
|
157
|
+
export {};
|
|
158
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/session-index/service.ts"],"names":[],"mappings":"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"}
|