agents-can-communicate 0.1.9 → 0.1.11
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/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/.claude-plugin/plugin.json +0 -1
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/install.mjs +15 -2
- package/node_modules/@agents-can-communicate/adapter-codex/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/plugin/.codex-plugin/plugin.json +10 -3
- package/node_modules/@agents-can-communicate/adapter-codex/src/adapter.mjs +4 -2
- package/node_modules/@agents-can-communicate/adapter-codex/src/install.mjs +50 -40
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/plugin/.kimi-plugin/plugin.json +0 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/src/install.mjs +5 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/src/index.mjs +1 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/own-version.mjs +81 -0
- package/node_modules/@agents-can-communicate/cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/cli/src/doctor-command.mjs +3 -0
- package/node_modules/@agents-can-communicate/core/package.json +1 -1
- package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/src/detect.mjs +5 -1
- package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
- package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
- package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
1
|
+
import { cp, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
4
|
import { AccError, EXIT } from "@agents-can-communicate/protocol";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
import { acccreatedFile, bakeSkillCommand, blankJson, mergeOwnedEntries, ownedEntries,
|
|
8
|
+
keepOnlyVersion, ownVersion, stampPluginVersion,
|
|
8
9
|
removeIfEmpty,
|
|
9
10
|
removeInstalledTree,
|
|
10
11
|
removeOwnedEntries, writeForeignJson, writeHookShim }
|
|
@@ -122,7 +123,9 @@ const writeRegistry = async (file, value, remaining) => {
|
|
|
122
123
|
return rm(file, { force: true });
|
|
123
124
|
};
|
|
124
125
|
|
|
125
|
-
|
|
126
|
+
// One version on this machine: the package's own. The shipped manifest carries
|
|
127
|
+
// none, so there is nothing in the repository to fall out of step.
|
|
128
|
+
const pluginVersion = async () => ownVersion(import.meta.url);
|
|
126
129
|
|
|
127
130
|
/** The marketplace manifest, in the shape the client's own directory sources use. */
|
|
128
131
|
const marketplaceManifest = () => ({
|
|
@@ -147,6 +150,11 @@ async function layOutPlugin(target, { runner, node }) {
|
|
|
147
150
|
// The bundle's hooks.json names this script, and nothing else writes it.
|
|
148
151
|
await writeHookShim({ dir: path.join(target, "hooks"), adapterId: "claude_code",
|
|
149
152
|
runner, node });
|
|
153
|
+
// The copy the client reads says which ACC wrote it. The shipped manifest
|
|
154
|
+
// carries no version, so there is nothing in the repository to fall out of
|
|
155
|
+
// step - which is how every client came to report 0.1.6 while running 0.1.9.
|
|
156
|
+
await stampPluginVersion({ file: path.join(target, ".claude-plugin", "plugin.json"),
|
|
157
|
+
version: await pluginVersion(), io: { readFile, writeFile } });
|
|
150
158
|
}
|
|
151
159
|
|
|
152
160
|
export async function installClaudePlugin({ configDir, runner, node, now = new Date() }) {
|
|
@@ -172,6 +180,11 @@ export async function installClaudePlugin({ configDir, runner, node, now = new D
|
|
|
172
180
|
// run `claude plugin install`, exactly as the Codex adapter does, because the
|
|
173
181
|
// command's only effect is this copy plus the two registry entries below.
|
|
174
182
|
await layOutPlugin(cached, { runner, node });
|
|
183
|
+
// One copy, the one just written. A client caches a plugin under its version,
|
|
184
|
+
// so every upgrade would otherwise leave the previous release's tree beside
|
|
185
|
+
// this one - invisible while the version never moved, three deep once it did.
|
|
186
|
+
await keepOnlyVersion({ root: path.dirname(cached), version,
|
|
187
|
+
io: { readdir, rm } });
|
|
175
188
|
|
|
176
189
|
await writeClientJson(knownMarketplacesPath(configDir), {
|
|
177
190
|
...known,
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents-can-communicate",
|
|
3
|
-
"version": "0.1.6",
|
|
4
3
|
"description": "Coordinate this Codex session with other AI agent sessions working in the same workspace.",
|
|
5
4
|
"license": "UNLICENSED",
|
|
6
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"coordination",
|
|
7
|
+
"multi-agent",
|
|
8
|
+
"claims",
|
|
9
|
+
"handoff"
|
|
10
|
+
],
|
|
7
11
|
"skills": "./skills/",
|
|
8
12
|
"hooks": "./hooks.json",
|
|
9
13
|
"interface": {
|
|
@@ -12,6 +16,9 @@
|
|
|
12
16
|
"longDescription": "Attaches this session to a local coordination plane shared with Codex, Claude Code, Gemini CLI, and MCP clients working in the same workspace. Publishes what this session is doing, warns before two sessions edit the same resource, carries typed messages between participants, and records a handoff at the end.",
|
|
13
17
|
"developerName": "automatis-tools",
|
|
14
18
|
"category": "Developer Tools",
|
|
15
|
-
"capabilities": [
|
|
19
|
+
"capabilities": [
|
|
20
|
+
"Read",
|
|
21
|
+
"Write"
|
|
22
|
+
]
|
|
16
23
|
}
|
|
17
24
|
}
|
|
@@ -68,8 +68,10 @@ export function createCodexAdapter() {
|
|
|
68
68
|
"write guards cover apply_patch and the shell writes ACC can read; a model "
|
|
69
69
|
+ "without apply_patch edits through the shell, where a redirection or an "
|
|
70
70
|
+ "mv is matched and a runtime opening the file is not",
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
// Was a standing sentence here, true and useless: it said the same
|
|
72
|
+
// thing on a machine whose hooks were trusted, on one whose were not,
|
|
73
|
+
// and on one with nothing installed. Detection reads the client's own
|
|
74
|
+
// record now and speaks only when it has something to report.
|
|
73
75
|
],
|
|
74
76
|
};
|
|
75
77
|
},
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { cp, mkdir, readFile, rm, rmdir, stat, writeFile } from "node:fs/promises";
|
|
1
|
+
import { cp, mkdir, readdir, readFile, rm, rmdir, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
|
|
5
5
|
import { bakeSkillCommand, blankJson, blankText, removeIfEmpty, removeInstalledTree,
|
|
6
|
+
keepOnlyVersion, ownVersion, stampPluginVersion,
|
|
6
7
|
removeTomlBlock, stripBlock, tomlString,
|
|
7
8
|
writeForeignJson, writeHookShim, writeTomlBlock }
|
|
8
9
|
from "@agents-can-communicate/adapter-sdk";
|
|
@@ -199,11 +200,19 @@ export async function installCodexPlugin({ home, agentsHome = home,
|
|
|
199
200
|
|
|
200
201
|
// The client runs the cached copy, so this has to happen after the shim and
|
|
201
202
|
// the rewritten hooks.json are in place.
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
// One version on this machine: the package's own. The shipped manifest carries
|
|
204
|
+
// none, so nothing in the repository can fall out of step with it - which is
|
|
205
|
+
// how every client came to report 0.1.6 while running 0.1.9. The copy the
|
|
206
|
+
// client reads is stamped, so it says which ACC wrote it.
|
|
207
|
+
const version = await ownVersion(import.meta.url);
|
|
208
|
+
await stampPluginVersion({ file: path.join(target, ".codex-plugin", "plugin.json"),
|
|
209
|
+
version, io: { readFile, writeFile } });
|
|
204
210
|
const cached = cachedVersionPath(codexHome, version);
|
|
205
211
|
await rm(cached, { recursive: true, force: true });
|
|
206
212
|
await cp(target, cached, { recursive: true });
|
|
213
|
+
// One copy, the one just written - and only inside this plugin's own
|
|
214
|
+
// directory. The marketplace cache root above it holds other people's plugins.
|
|
215
|
+
await keepOnlyVersion({ root: path.dirname(cached), version, io: { readdir, rm } });
|
|
207
216
|
|
|
208
217
|
// The plugin's own directory in the cache. Not the versioned one inside it,
|
|
209
218
|
// which goes stale the moment the version changes - and not the marketplace
|
|
@@ -229,33 +238,6 @@ async function removeEmptyDirs(directories) {
|
|
|
229
238
|
}
|
|
230
239
|
}
|
|
231
240
|
|
|
232
|
-
/**
|
|
233
|
-
* Drop the client's trust record for hooks that are about to stop existing.
|
|
234
|
-
*
|
|
235
|
-
* Scoped to tables whose key begins with ACC's own `plugin@marketplace:` - a
|
|
236
|
-
* table belonging to any other plugin is the client's business and stays. The
|
|
237
|
-
* file is rewritten only when something was found, so an install that never ran
|
|
238
|
-
* here leaves the config byte for byte as it was.
|
|
239
|
-
*/
|
|
240
|
-
export async function removeHookTrust(file, prefix) {
|
|
241
|
-
const before = await readFile(file, "utf8").catch(() => null);
|
|
242
|
-
if (before === null || !before.includes(`hooks.state."${prefix}`)) return false;
|
|
243
|
-
|
|
244
|
-
const lines = before.split("\n");
|
|
245
|
-
const kept = [];
|
|
246
|
-
let dropping = false;
|
|
247
|
-
for (const line of lines) {
|
|
248
|
-
const header = /^\s*\[([^\]]*)\]\s*$/.exec(line);
|
|
249
|
-
if (header !== null) dropping = header[1].startsWith(`hooks.state."${prefix}`);
|
|
250
|
-
if (dropping) continue;
|
|
251
|
-
kept.push(line);
|
|
252
|
-
}
|
|
253
|
-
// A table's trailing blank line goes with it rather than piling up.
|
|
254
|
-
const text = kept.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
255
|
-
if (text === before) return false;
|
|
256
|
-
await writeFile(file, text, "utf8");
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
241
|
|
|
260
242
|
export async function uninstallCodexPlugin({ home, agentsHome = home,
|
|
261
243
|
codexHome = path.join(home, ".codex"), keep = [] }) {
|
|
@@ -268,15 +250,18 @@ export async function uninstallCodexPlugin({ home, agentsHome = home,
|
|
|
268
250
|
await writeMarketplace(file, { ...existing, plugins: kept });
|
|
269
251
|
}
|
|
270
252
|
if (await removeTomlBlock(configPath(codexHome))) changes.push(configPath(codexHome));
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
253
|
+
// The client's `[hooks.state."<plugin>:…"]` tables stay. 0.1.9 removed them as
|
|
254
|
+
// litter naming a plugin that was gone; that was wrong, and wrong in a way
|
|
255
|
+
// worth writing down. The check behind it perturbed the record - a hook whose
|
|
256
|
+
// recorded hash no longer *matched* was seen still running - and concluded the
|
|
257
|
+
// record was inert. Absence was never tested, and absence is what matters:
|
|
258
|
+
// with the tables gone this client runs no hook at all, prints
|
|
259
|
+
// `hook: SessionStart Completed` while executing nothing, and ACC's write
|
|
260
|
+
// guard is silently off while `acc doctor` reports the adapter installed.
|
|
261
|
+
//
|
|
262
|
+
// The record is granted once, by a person, and survives ACC upgrades - hashes
|
|
263
|
+
// captured under 0.1.6 still admitted 0.1.10's hooks. Nothing ACC writes can
|
|
264
|
+
// put it back. So it is not ACC's to remove.
|
|
280
265
|
// The marketplace directory is ACC's too, so it goes rather than being left
|
|
281
266
|
// behind empty.
|
|
282
267
|
// A blank TOML config and an absent one are the same to this client, and a
|
|
@@ -319,6 +304,13 @@ export async function detectCodex({ home, agentsHome = home,
|
|
|
319
304
|
const enabled = config.includes(`[plugins."${QUALIFIED}"]`);
|
|
320
305
|
const cached = await stat(cachePath(codexHome))
|
|
321
306
|
.then(() => true).catch(() => false);
|
|
307
|
+
// The condition that decides whether ACC runs here at all, and the only one
|
|
308
|
+
// this client will not tell you about: with no trust record it runs no hook,
|
|
309
|
+
// prints `hook: SessionStart Completed`, and executes nothing. Everything else
|
|
310
|
+
// - published, registered, enabled, cached - can be true while the write guard
|
|
311
|
+
// is off. Only asked when something is wired: a warning about hooks that do
|
|
312
|
+
// not exist is how a real one gets ignored.
|
|
313
|
+
const untrusted = cached && !config.includes(`hooks.state."${QUALIFIED}:`);
|
|
322
314
|
return { ok: true, changes: [], diagnostics: [
|
|
323
315
|
published ? "acc plugin published in the marketplace" : "acc plugin not registered",
|
|
324
316
|
registered && enabled
|
|
@@ -330,7 +322,25 @@ export async function detectCodex({ home, agentsHome = home,
|
|
|
330
322
|
cached
|
|
331
323
|
? "plugin installed in the client's cache"
|
|
332
324
|
: `plugin not installed yet; run: codex plugin add ${QUALIFIED}`,
|
|
333
|
-
|
|
325
|
+
// The condition that decides whether ACC runs here at all, and the only one
|
|
326
|
+
// this client will not tell you about: with no trust record it runs no hook,
|
|
327
|
+
// prints `hook: SessionStart Completed`, and executes nothing. Everything
|
|
328
|
+
// else - published, registered, enabled, cached - can be true while the
|
|
329
|
+
// write guard is off. Said only when there is something to trust, because a
|
|
330
|
+
// warning on a machine with nothing wired is how a real one gets ignored.
|
|
331
|
+
...(untrusted
|
|
332
|
+
? ["hooks are not trusted, so this client reports them completed and runs "
|
|
333
|
+
+ "nothing"]
|
|
334
|
+
: []),
|
|
335
|
+
],
|
|
336
|
+
// Said as an action rather than an observation, because a person has to do it
|
|
337
|
+
// and no acc command can: the client grants this once, from its own prompt.
|
|
338
|
+
// A diagnostic alone would have stayed in `--json`, which is where the first
|
|
339
|
+
// version of this fix put it and where nobody would have read it.
|
|
340
|
+
needsAction: untrusted
|
|
341
|
+
? ["start codex once and accept the hook trust prompt "
|
|
342
|
+
+ "# its hooks run nothing until then"]
|
|
343
|
+
: [] };
|
|
334
344
|
}
|
|
335
345
|
|
|
336
346
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents-can-communicate",
|
|
3
|
-
"version": "0.1.6",
|
|
4
3
|
"description": "Coordinate this Kimi Code session with other AI agent sessions working in the same workspace: shared presence, resource claims, typed messages, and handoffs.",
|
|
5
4
|
"skills": "./skills/",
|
|
6
5
|
"sessionStart": {
|
|
@@ -5,6 +5,7 @@ import { AccError, EXIT } from "@agents-can-communicate/protocol";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
import { assertRunner, bakeSkillCommand, blankText, defaultRunner, removeIfEmpty,
|
|
8
|
+
ownVersion, stampPluginVersion,
|
|
8
9
|
removeInstalledTree, runnerExists, writeForeignJson }
|
|
9
10
|
from "@agents-can-communicate/adapter-sdk";
|
|
10
11
|
|
|
@@ -132,6 +133,10 @@ export async function installKimiPlugin({ home, runner = defaultRunner(), node }
|
|
|
132
133
|
const target = pluginPath(home);
|
|
133
134
|
await rm(target, { recursive: true, force: true });
|
|
134
135
|
await cp(bundle, target, { recursive: true });
|
|
136
|
+
// The copy this client reads says which ACC wrote it. Nothing here reads it
|
|
137
|
+
// back, but a manifest that names a version it is not is a lie either way.
|
|
138
|
+
await stampPluginVersion({ file: path.join(target, ".kimi-plugin", "plugin.json"),
|
|
139
|
+
version: await ownVersion(import.meta.url), io: { readFile, writeFile } });
|
|
135
140
|
// The skill ships with a placeholder where the command belongs: `acc` is
|
|
136
141
|
// not on PATH everywhere, and an agent that cannot run it improvises.
|
|
137
142
|
await bakeSkillCommand({ root: target, node });
|
|
@@ -9,6 +9,7 @@ export { BEGIN, END, removeTomlBlock, renderBlock, stripBlock, tomlString, write
|
|
|
9
9
|
from "./toml-block.mjs";
|
|
10
10
|
export { projectContext } from "./context-projector.mjs";
|
|
11
11
|
export { shellWriteTargets } from "./shell-writes.mjs";
|
|
12
|
+
export { keepOnlyVersion, ownVersion, stampPluginVersion } from "./own-version.mjs";
|
|
12
13
|
export { editJson, readJson } from "./json-text.mjs";
|
|
13
14
|
export { formatJsonAs, jsonStyleOf, mergeOwnedConfig, mergeOwnedEntries, ownedEntries, ownedKeys,
|
|
14
15
|
acccreatedFile, removeIfEmpty, removeOwnedConfig, removeOwnedEntries, writeForeignJson,
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The version of the ACC that is running.
|
|
7
|
+
*
|
|
8
|
+
* Every plugin manifest used to carry its own version literal, updated by hand
|
|
9
|
+
* and by nobody: three releases after 0.1.6 the package was 0.1.9 while every
|
|
10
|
+
* client had cached, listed and reported 0.1.6. That is not cosmetic - the
|
|
11
|
+
* version string is how a client decides whether its cached copy is current, so
|
|
12
|
+
* a bundle whose version never changes is one it has no reason to replace.
|
|
13
|
+
*
|
|
14
|
+
* Read by walking up for the nearest `package.json`, which lands on the calling
|
|
15
|
+
* package in a checkout and on the same file once bundled, where every package
|
|
16
|
+
* is versioned together. Answers `0.0.0` rather than throwing: an install that
|
|
17
|
+
* cannot read its own version should still lay a plugin down, under a version
|
|
18
|
+
* that is visibly wrong rather than crash.
|
|
19
|
+
*/
|
|
20
|
+
export async function ownVersion(fromUrl) {
|
|
21
|
+
let directory = path.dirname(fileURLToPath(fromUrl));
|
|
22
|
+
for (;;) {
|
|
23
|
+
const candidate = path.join(directory, "package.json");
|
|
24
|
+
const text = await readFile(candidate, "utf8").catch(() => null);
|
|
25
|
+
if (text !== null) {
|
|
26
|
+
try {
|
|
27
|
+
const version = JSON.parse(text).version;
|
|
28
|
+
if (typeof version === "string" && version !== "") return version;
|
|
29
|
+
} catch {
|
|
30
|
+
// A manifest that will not parse is not this package's version.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const parent = path.dirname(directory);
|
|
34
|
+
if (parent === directory) return "0.0.0";
|
|
35
|
+
directory = parent;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Stamp a laid-out plugin manifest with the version that wrote it.
|
|
41
|
+
*
|
|
42
|
+
* The shipped manifest carries no version of its own, so there is nothing in the
|
|
43
|
+
* repository to fall out of step. Best-effort: a manifest that is not there was
|
|
44
|
+
* not ours to stamp.
|
|
45
|
+
*/
|
|
46
|
+
export async function stampPluginVersion({ file, version, io }) {
|
|
47
|
+
const text = await io.readFile(file, "utf8").catch(() => null);
|
|
48
|
+
if (text === null) return false;
|
|
49
|
+
let manifest;
|
|
50
|
+
try {
|
|
51
|
+
manifest = JSON.parse(text);
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
await io.writeFile(file, `${JSON.stringify({ ...manifest, version }, null, 2)}\n`);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Leave one copy of a versioned plugin, the one just written.
|
|
61
|
+
*
|
|
62
|
+
* These clients cache a plugin under its version. Until the version tracked the
|
|
63
|
+
* package it never changed, every install landed in the same directory and
|
|
64
|
+
* overwrote itself, and nothing accumulated. Once it started moving, the first
|
|
65
|
+
* upgrade left three copies of ACC in a home that should hold one.
|
|
66
|
+
*
|
|
67
|
+
* Scoped to the plugin's own directory. The marketplace cache root above it
|
|
68
|
+
* holds every plugin installed from that marketplace, and removing that root
|
|
69
|
+
* once took a plugin the user had installed themselves - so a sibling here is
|
|
70
|
+
* an older ACC, and a sibling one level up is somebody else's.
|
|
71
|
+
*/
|
|
72
|
+
export async function keepOnlyVersion({ root, version, io }) {
|
|
73
|
+
const entries = await io.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
74
|
+
const removed = [];
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (!entry.isDirectory() || entry.name === version) continue;
|
|
77
|
+
await io.rm(path.join(root, entry.name), { recursive: true, force: true });
|
|
78
|
+
removed.push(entry.name);
|
|
79
|
+
}
|
|
80
|
+
return removed;
|
|
81
|
+
}
|
|
@@ -130,6 +130,9 @@ async function diagnoseAdapters({ options, runtime }) {
|
|
|
130
130
|
if (owned.missing.length > 0) {
|
|
131
131
|
remediation.push(`acc install --adapter ${entry.adapterId} # files are missing`);
|
|
132
132
|
}
|
|
133
|
+
// An adapter can name something only a person can do - the client's own
|
|
134
|
+
// trust prompt, most of all. It goes where a person reads, not into --json.
|
|
135
|
+
remediation.push(...(entry.needsAction ?? []));
|
|
133
136
|
const installed = record.installs.find(one => one.adapterId === entry.adapterId);
|
|
134
137
|
const stale = staleInstall({ recorded: installed?.accVersion ?? null, running });
|
|
135
138
|
if (stale !== null) {
|
|
@@ -44,7 +44,8 @@ export async function detectInstallation({ adapters, context, probe = spawnProbe
|
|
|
44
44
|
.map(async adapter => {
|
|
45
45
|
const entry = { adapterId: adapter.id, displayName: adapter.displayName,
|
|
46
46
|
present: false, version: null, versionOutput: null, installed: false,
|
|
47
|
-
diagnostics: [], capabilities: adapter.capabilities ?? {},
|
|
47
|
+
diagnostics: [], needsAction: [], capabilities: adapter.capabilities ?? {},
|
|
48
|
+
error: null };
|
|
48
49
|
|
|
49
50
|
try {
|
|
50
51
|
const output = await withTimeout(
|
|
@@ -65,6 +66,9 @@ export async function detectInstallation({ adapters, context, probe = spawnProbe
|
|
|
65
66
|
try {
|
|
66
67
|
const detected = await adapter.detect(context);
|
|
67
68
|
entry.diagnostics = detected.diagnostics ?? [];
|
|
69
|
+
// What a person has to do, as opposed to what is true. Adapters that
|
|
70
|
+
// have nothing to ask for say nothing.
|
|
71
|
+
entry.needsAction = detected.needsAction ?? [];
|
|
68
72
|
// "Installed" is the adapter's own answer, phrased in its own terms.
|
|
69
73
|
// The installer does not second-guess it by looking at files it does
|
|
70
74
|
// not understand.
|