@falkenslab/agent-kit 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/extension-launcher.mjs +28 -5
- package/dist/chat/chatController.d.ts +59 -0
- package/dist/chat/chatController.js +209 -2
- package/dist/chat/extensions.d.ts +30 -2
- package/dist/chat/extensions.js +62 -3
- package/dist/chat/runs.d.ts +3 -1
- package/dist/chat/runs.js +1 -1
- package/dist/core/agentSpec.d.ts +11 -0
- package/dist/core/elicitation.d.ts +54 -0
- package/dist/core/elicitation.js +120 -0
- package/dist/core/extensionCatalog.d.ts +94 -0
- package/dist/core/extensionCatalog.js +228 -0
- package/dist/core/extensionOptions.d.ts +56 -0
- package/dist/core/extensionOptions.js +139 -0
- package/dist/core/extensions.d.ts +7 -1
- package/dist/core/extensions.js +2 -1
- package/dist/core/externalExtensions.d.ts +83 -8
- package/dist/core/externalExtensions.js +233 -30
- package/dist/core/hooks/planGate.d.ts +2 -0
- package/dist/core/hooks/planGate.js +3 -0
- package/dist/core/interaction.d.ts +7 -0
- package/dist/core/liveExtensions.d.ts +11 -0
- package/dist/core/liveExtensions.js +22 -0
- package/dist/core/marketplaces.d.ts +6 -2
- package/dist/core/marketplaces.js +6 -4
- package/dist/core/messages/de.js +33 -1
- package/dist/core/messages/en.d.ts +36 -0
- package/dist/core/messages/en.js +33 -1
- package/dist/core/messages/es.js +33 -1
- package/dist/core/messages/fr.js +33 -1
- package/dist/core/npxPackages.d.ts +48 -0
- package/dist/core/npxPackages.js +121 -0
- package/dist/core/pluginDownloads.d.ts +2 -1
- package/dist/core/pluginDownloads.js +2 -2
- package/dist/core/readOnlyHints.d.ts +13 -0
- package/dist/core/readOnlyHints.js +26 -0
- package/dist/core/runner.js +2 -0
- package/dist/core/session.d.ts +13 -1
- package/dist/core/session.js +31 -7
- package/dist/core/sessionFacts.d.ts +5 -1
- package/dist/core/workspaceTrust.d.ts +12 -0
- package/dist/core/workspaceTrust.js +60 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +5 -1
- package/dist/tui/extensionCommand.d.ts +10 -2
- package/dist/tui/extensionCommand.js +182 -33
- package/extensions/awareness/.claude-plugin/plugin.json +13 -5
- package/extensions/knowledge/.claude-plugin/plugin.json +13 -5
- package/extensions/memory/.claude-plugin/plugin.json +13 -5
- package/extensions/sources/.claude-plugin/plugin.json +13 -5
- package/package.json +7 -5
- package/schemas/agent-kit-metadata.schema.json +62 -0
- package/schemas/marketplace-catalog.schema.json +64 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const TYPES = new Set(["string", "number", "boolean", "directory", "file"]);
|
|
4
|
+
const FIELDS = new Set(["type", "title", "description", "required", "default", "sensitive", "options", "multiple", "min", "max"]);
|
|
5
|
+
/**
|
|
6
|
+
* A manifest's `userConfig`, checked as Claude Code checks it (an option with an unknown field
|
|
7
|
+
* makes the plugin fail to load there): throws saying what's wrong.
|
|
8
|
+
*/
|
|
9
|
+
export function parseUserConfig(raw) {
|
|
10
|
+
if (raw === undefined)
|
|
11
|
+
return {};
|
|
12
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
13
|
+
throw new Error("its userConfig isn't an object");
|
|
14
|
+
const options = {};
|
|
15
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
16
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
17
|
+
throw new Error(`its userConfig option "${key}" isn't a valid name (letters, digits and _)`);
|
|
18
|
+
if (!value || typeof value !== "object")
|
|
19
|
+
throw new Error(`its userConfig option "${key}" isn't an object`);
|
|
20
|
+
const option = value;
|
|
21
|
+
const unknown = Object.keys(option).filter((field) => !FIELDS.has(field));
|
|
22
|
+
if (unknown.length)
|
|
23
|
+
throw new Error(`its userConfig option "${key}" has unknown fields (${unknown.join(", ")})`);
|
|
24
|
+
if (typeof option.type !== "string" || !TYPES.has(option.type))
|
|
25
|
+
throw new Error(`its userConfig option "${key}" has no valid type (string, number, boolean, directory or file)`);
|
|
26
|
+
if (typeof option.description !== "string")
|
|
27
|
+
throw new Error(`its userConfig option "${key}" has no description`);
|
|
28
|
+
options[key] = { ...option, title: typeof option.title === "string" ? option.title : key };
|
|
29
|
+
}
|
|
30
|
+
return options;
|
|
31
|
+
}
|
|
32
|
+
/** A typed value for `option` from what the person typed; throws saying why it isn't one. */
|
|
33
|
+
export function parseOptionValue(option, typed) {
|
|
34
|
+
const one = (text) => {
|
|
35
|
+
const value = text.trim();
|
|
36
|
+
if (option.type === "number") {
|
|
37
|
+
const number = Number(value);
|
|
38
|
+
if (!value || Number.isNaN(number))
|
|
39
|
+
throw new Error(`"${value}" isn't a number`);
|
|
40
|
+
if (option.min !== undefined && number < option.min)
|
|
41
|
+
throw new Error(`${number} is below ${option.min}`);
|
|
42
|
+
if (option.max !== undefined && number > option.max)
|
|
43
|
+
throw new Error(`${number} is above ${option.max}`);
|
|
44
|
+
if (option.options && !option.options.includes(number))
|
|
45
|
+
throw new Error(`${number} isn't one of ${option.options.join(", ")}`);
|
|
46
|
+
return number;
|
|
47
|
+
}
|
|
48
|
+
if (option.type === "boolean") {
|
|
49
|
+
if (/^(true|yes|y|1|on)$/i.test(value))
|
|
50
|
+
return true;
|
|
51
|
+
if (/^(false|no|n|0|off)$/i.test(value))
|
|
52
|
+
return false;
|
|
53
|
+
throw new Error(`"${value}" isn't true or false`);
|
|
54
|
+
}
|
|
55
|
+
if (option.options && !option.options.map(String).includes(value))
|
|
56
|
+
throw new Error(`"${value}" isn't one of ${option.options.join(", ")}`);
|
|
57
|
+
return option.type === "directory" || option.type === "file" ? path.resolve(value) : value;
|
|
58
|
+
};
|
|
59
|
+
if (option.multiple)
|
|
60
|
+
return typed.split(",").filter((part) => part.trim()).map((part) => String(one(part)));
|
|
61
|
+
return one(typed);
|
|
62
|
+
}
|
|
63
|
+
/** A value as it goes into a server's argument or variable: a list comma-separated. */
|
|
64
|
+
export function optionText(value) {
|
|
65
|
+
return value === undefined ? "" : Array.isArray(value) ? value.join(",") : String(value);
|
|
66
|
+
}
|
|
67
|
+
/** Where a scope keeps an extension's sensitive values: outside its plugin, its lock and its data. */
|
|
68
|
+
export function extensionSecretsPath(scopeDir, name) {
|
|
69
|
+
return path.join(scopeDir, ".secrets", `${name}.json`);
|
|
70
|
+
}
|
|
71
|
+
/** The folder of a scope's sensitive values, which the session's file tools never reach. */
|
|
72
|
+
export function secretsDir(scopeDir) {
|
|
73
|
+
return path.join(scopeDir, ".secrets");
|
|
74
|
+
}
|
|
75
|
+
async function readJson(file) {
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(await readFile(file, "utf8"));
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (error.code === "ENOENT")
|
|
81
|
+
return {};
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** The values an installed extension's options have now: what the person set (the lock's and the secrets'), else their defaults. */
|
|
86
|
+
export async function readOptionValues(scopeDir, name, declared, stored = {}) {
|
|
87
|
+
const secrets = (await readJson(extensionSecretsPath(scopeDir, name)));
|
|
88
|
+
const values = {};
|
|
89
|
+
for (const [key, option] of Object.entries(declared)) {
|
|
90
|
+
const value = option.sensitive ? secrets[key] : stored[key];
|
|
91
|
+
if (value !== undefined)
|
|
92
|
+
values[key] = value;
|
|
93
|
+
else if (option.default !== undefined)
|
|
94
|
+
values[key] = option.default;
|
|
95
|
+
}
|
|
96
|
+
return values;
|
|
97
|
+
}
|
|
98
|
+
/** The required options without a value. */
|
|
99
|
+
export function missingOptions(declared, values) {
|
|
100
|
+
return Object.entries(declared)
|
|
101
|
+
.filter(([key, option]) => option.required && (values[key] === undefined || values[key] === ""))
|
|
102
|
+
.map(([key]) => key);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Sets (or with `undefined`, clears) some of an extension's option values: the sensitive ones in
|
|
106
|
+
* its secrets file, the others returned for its lock entry (the caller writes the lock).
|
|
107
|
+
*/
|
|
108
|
+
export async function storeOptionValues(scopeDir, name, declared, stored, changes) {
|
|
109
|
+
const next = { ...stored };
|
|
110
|
+
const file = extensionSecretsPath(scopeDir, name);
|
|
111
|
+
const secrets = (await readJson(file));
|
|
112
|
+
let secretsChanged = false;
|
|
113
|
+
for (const [key, value] of Object.entries(changes)) {
|
|
114
|
+
const option = declared[key];
|
|
115
|
+
if (!option)
|
|
116
|
+
throw new Error(`${name} has no option "${key}"`);
|
|
117
|
+
const target = option.sensitive ? secrets : next;
|
|
118
|
+
if (value === undefined)
|
|
119
|
+
delete target[key];
|
|
120
|
+
else
|
|
121
|
+
target[key] = value;
|
|
122
|
+
if (option.sensitive)
|
|
123
|
+
secretsChanged = true;
|
|
124
|
+
}
|
|
125
|
+
if (secretsChanged) {
|
|
126
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
127
|
+
await writeFile(file, `${JSON.stringify(secrets, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
128
|
+
await chmod(file, 0o600).catch(() => undefined);
|
|
129
|
+
}
|
|
130
|
+
return next;
|
|
131
|
+
}
|
|
132
|
+
/** Removes an extension's sensitive values. */
|
|
133
|
+
export async function removeSecrets(scopeDir, name) {
|
|
134
|
+
await rm(extensionSecretsPath(scopeDir, name), { force: true });
|
|
135
|
+
}
|
|
136
|
+
/** The `${user_config.KEY}` a server's values name. */
|
|
137
|
+
export function optionsNamed(values) {
|
|
138
|
+
return [...new Set(values.flatMap((value) => [...value.matchAll(/\$\{user_config\.([A-Za-z_][A-Za-z0-9_]*)\}/g)].map((match) => match[1])))];
|
|
139
|
+
}
|
|
@@ -4,7 +4,7 @@ import type { ToolLabels } from "./toolLabels.js";
|
|
|
4
4
|
import type { SessionFacts } from "./sessionFacts.js";
|
|
5
5
|
/**
|
|
6
6
|
* Extensions (ADR-025): what an agent is besides the kit's core. An extension is a Claude Code
|
|
7
|
-
* plugin with the kit's data in its manifest (`.claude-plugin/plugin.json`,
|
|
7
|
+
* plugin with the kit's data in its manifest (`.claude-plugin/plugin.json`, under `metadata["agent-kit"]`:
|
|
8
8
|
* the capabilities it `provides` and `requires`), plus, for an internal one, the code that
|
|
9
9
|
* says what it brings to a session. An agent enables the ones it wants (`AgentSpec.extensions`),
|
|
10
10
|
* each made with its own options (the kit's through their factories, `knowledge({ dir })`…);
|
|
@@ -57,6 +57,12 @@ export interface ExtensionContribution {
|
|
|
57
57
|
}[];
|
|
58
58
|
/** Its MCP tools that only read: plan mode lets them through. */
|
|
59
59
|
readOnlyTools?: string[];
|
|
60
|
+
/**
|
|
61
|
+
* Its MCP servers whose tools that only read plan mode learns from the servers themselves
|
|
62
|
+
* (`readOnlyHint`), asked while the session runs: an installed extension whose manifest lists
|
|
63
|
+
* no `readOnlyTools`. A list in the manifest wins: its servers aren't here.
|
|
64
|
+
*/
|
|
65
|
+
readOnlyHintServers?: string[];
|
|
60
66
|
/** Its MCP tools that ask the person themselves: interactive mode's step gate doesn't ask first. */
|
|
61
67
|
selfAskingTools?: string[];
|
|
62
68
|
/** How the chat shows its tools, by full name, in the kit's language: its line and how it counts in a folded group. */
|
package/dist/core/extensions.js
CHANGED
|
@@ -12,7 +12,8 @@ export function readExtensionManifest(pluginRoot) {
|
|
|
12
12
|
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
13
13
|
if (!raw.name)
|
|
14
14
|
throw new Error(`${file} has no "name".`);
|
|
15
|
-
|
|
15
|
+
const kit = raw.metadata?.["agent-kit"];
|
|
16
|
+
return { name: raw.name, description: raw.description ?? "", provides: kit?.provides ?? [], requires: kit?.requires ?? [] };
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
19
|
* Which of the `extensions` run in this session, in the order given: those the session lacks
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { Extension } from "./extensions.js";
|
|
2
2
|
import type { Language } from "./language.js";
|
|
3
3
|
import { type ToolPhrase } from "./messages/index.js";
|
|
4
|
+
import { type ExtensionOption, type OptionValue } from "./extensionOptions.js";
|
|
5
|
+
import { type CachedPackage } from "./npxPackages.js";
|
|
4
6
|
/**
|
|
5
7
|
* External extensions (ADR-025, #37): installed into an agent, for all its projects (the
|
|
6
8
|
* agent's scope) or for one (the project's, which wins), from a folder or a git repository.
|
|
7
9
|
* Each scope is a folder of extensions, one per subfolder, with a lock (`extensions.lock.json`)
|
|
8
10
|
* that records where each came from, a SHA-256 over its files and whether it's enabled. An
|
|
9
11
|
* extension is a Claude Code plugin as it is (#38): its MCP servers come from its `.mcp.json`,
|
|
10
|
-
* and the kit starts each with Node through a launcher that leaves it only the system's
|
|
11
|
-
* variables and its own `env`. The `"agent-kit"`
|
|
12
|
+
* and the kit starts each with Node (an `npx` one from its package cache, #54) through a launcher that leaves it only the system's
|
|
13
|
+
* variables and its own `env`. The `metadata["agent-kit"]` of its `plugin.json` is optional: it adds
|
|
12
14
|
* what the kit's gates and chat can use (capabilities, read-only tools, labels, help). Each has a
|
|
13
15
|
* data folder of its own (`${CLAUDE_PLUGIN_DATA}`, as Claude Code gives a plugin), kept across
|
|
14
16
|
* sessions and updates; one being developed is linked rather than copied (#52).
|
|
@@ -42,7 +44,7 @@ export interface ExtensionAuthor {
|
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
44
46
|
* An external extension's manifest: its `plugin.json`, the plugin's own metadata (Claude Code's
|
|
45
|
-
* fields, which its validator checks) and the kit's
|
|
47
|
+
* fields, which its validator checks) and the kit's data (`metadata["agent-kit"]`, which it doesn't read).
|
|
46
48
|
*/
|
|
47
49
|
export interface ExternalManifest {
|
|
48
50
|
name: string;
|
|
@@ -61,12 +63,23 @@ export interface ExternalManifest {
|
|
|
61
63
|
needs: string[];
|
|
62
64
|
/** Its MCP servers, by name (`mcp__<name>__<tool>`), from the plugin's `.mcp.json` or `plugin.json`. */
|
|
63
65
|
servers: Record<string, PluginServer>;
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Its tools that only read (short names, of any of its servers): plan mode lets them through.
|
|
68
|
+
* Without the key, plan mode asks its servers which (their tools' `readOnlyHint`); with it, the
|
|
69
|
+
* list is the whole answer, even empty.
|
|
70
|
+
*/
|
|
71
|
+
readOnlyTools?: string[];
|
|
66
72
|
/** Its tools' chat labels, by short name (of any of its servers) and language (English when the kit's isn't there). */
|
|
67
73
|
labels: Record<string, Partial<Record<Language, ExternalToolLabel>>>;
|
|
68
74
|
/** What it says about itself in a session, for the agent to tell the person (`SessionFacts`). */
|
|
69
75
|
help?: string;
|
|
76
|
+
/** Its options (`userConfig`, as Claude Code declares them, #55): its servers take them as `${user_config.KEY}`. */
|
|
77
|
+
userConfig: Record<string, ExtensionOption>;
|
|
78
|
+
/** How a catalog shows it (#59): its `logo` (square) and `banner` (wide), paths inside its folder, as its manifest gives them. */
|
|
79
|
+
catalog?: {
|
|
80
|
+
logo?: string;
|
|
81
|
+
banner?: string;
|
|
82
|
+
};
|
|
70
83
|
}
|
|
71
84
|
/** One extension in a scope's lock. */
|
|
72
85
|
export interface LockEntry {
|
|
@@ -79,10 +92,22 @@ export interface LockEntry {
|
|
|
79
92
|
linked?: boolean;
|
|
80
93
|
/** The commit it was cloned at, for a git source. */
|
|
81
94
|
commit?: string;
|
|
95
|
+
/** The folder inside its source it was installed from (a git repository's subfolder). */
|
|
96
|
+
subdir?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Its code is in the agent's store (`<agent scope>/.store/<name>/<hash>/`), not in its scope's
|
|
99
|
+
* folder (#57): a project's scope keeps only its lock, so a workspace shared with others carries
|
|
100
|
+
* what it uses and at which version, never the code itself (`restoreExtensions()` gets it).
|
|
101
|
+
*/
|
|
102
|
+
stored?: boolean;
|
|
82
103
|
/** The marketplace it was installed from (`<name>@<marketplace>`), if any. */
|
|
83
104
|
marketplace?: string;
|
|
84
105
|
/** SHA-256 over its files, checked when it loads; empty for a linked one. */
|
|
85
106
|
sha256: string;
|
|
107
|
+
/** Its options' non-sensitive values (#55); the sensitive ones are in the scope's `.secrets/`. */
|
|
108
|
+
options?: Record<string, OptionValue>;
|
|
109
|
+
/** The npm packages its servers run with npx (#54), by `<package>@<version>`: in the agent's package cache, checked when it loads. */
|
|
110
|
+
packages?: Record<string, CachedPackage>;
|
|
86
111
|
enabled: boolean;
|
|
87
112
|
/** When it was installed, ISO 8601 in UTC. */
|
|
88
113
|
installed: string;
|
|
@@ -95,11 +120,34 @@ export declare const LOCK_FILE = "extensions.lock.json";
|
|
|
95
120
|
* asks before deleting it.
|
|
96
121
|
*/
|
|
97
122
|
export declare function extensionDataDir(scopeDir: string, name: string): string;
|
|
123
|
+
/**
|
|
124
|
+
* Where a scope keeps what's the person's, not the workspace's: its extensions' data and secrets
|
|
125
|
+
* (#57). The agent's scope, its own folder; a project's, a folder of its own in the agent's
|
|
126
|
+
* (`<agent scope>/.projects/<project>-<hash>/`), so nothing private travels with a project. An
|
|
127
|
+
* agent without a scope of its own keeps it in the project's.
|
|
128
|
+
*/
|
|
129
|
+
export declare function scopePrivateDir(dirs: ExtensionDirs, scope: ExtensionScope): string | undefined;
|
|
130
|
+
/** The agent's store of extensions' code, by content (#57): where a project's scope keeps its extensions' code. */
|
|
131
|
+
export declare function codeStoreDir(dirs: ExtensionDirs): string | undefined;
|
|
132
|
+
/** Where a stored extension's code is: by its name and its files' hash. */
|
|
133
|
+
export declare function storedCodeDir(store: string, name: string, sha256: string): string;
|
|
134
|
+
/**
|
|
135
|
+
* How an install into `scope` is done: its npx packages into the agent's cache, and a project's
|
|
136
|
+
* code into the agent's store when the agent has a scope of its own.
|
|
137
|
+
*/
|
|
138
|
+
export declare function installTarget(dirs: ExtensionDirs, scope: ExtensionScope): {
|
|
139
|
+
packages?: string;
|
|
140
|
+
store?: string;
|
|
141
|
+
};
|
|
98
142
|
/** A scope's lock: its extensions by name. */
|
|
99
143
|
export declare function readLock(scopeDir: string): Promise<Record<string, LockEntry>>;
|
|
100
144
|
/** Reads an extension's manifest, checking what the kit needs from it. */
|
|
101
145
|
export declare function readExternalManifest(dir: string): Promise<ExternalManifest>;
|
|
102
|
-
/**
|
|
146
|
+
/**
|
|
147
|
+
* SHA-256 over an extension's files (their paths and contents, in order), `.git` aside. A text
|
|
148
|
+
* file's line endings don't count (CRLF is taken as LF): git checks the same commit out with CRLF
|
|
149
|
+
* on one computer and LF on another, and a workspace's lock must match on both (#57).
|
|
150
|
+
*/
|
|
103
151
|
export declare function hashExtension(dir: string): Promise<string>;
|
|
104
152
|
/**
|
|
105
153
|
* Whether `version` (x.y.z) is in `range`: comparators separated by spaces, all of which must
|
|
@@ -114,8 +162,10 @@ export interface InstalledExtension {
|
|
|
114
162
|
scopeDir: string;
|
|
115
163
|
/** Its plugin: in its scope's folder, or where it is when it's linked. */
|
|
116
164
|
dir: string;
|
|
117
|
-
/** Its data folder (`${CLAUDE_PLUGIN_DATA}`), which may not exist yet. */
|
|
165
|
+
/** Its data folder (`${CLAUDE_PLUGIN_DATA}`), which may not exist yet: in its scope's private folder. */
|
|
118
166
|
dataDir: string;
|
|
167
|
+
/** Its scope's private folder (`scopePrivateDir()`): its data and secrets. */
|
|
168
|
+
privateDir: string;
|
|
119
169
|
lock: LockEntry;
|
|
120
170
|
/** Also installed in the project's scope, which wins: this one isn't used. */
|
|
121
171
|
shadowed: boolean;
|
|
@@ -133,6 +183,7 @@ export declare function loadExternalExtensions(dirs: ExtensionDirs): Promise<{
|
|
|
133
183
|
name: string;
|
|
134
184
|
reason: string;
|
|
135
185
|
}[];
|
|
186
|
+
untrusted: InstalledExtension[];
|
|
136
187
|
}>;
|
|
137
188
|
/** Why an installed extension can't load, or its manifest when it can. */
|
|
138
189
|
/** Whether a program is on the `PATH`, as a shell would find it (with `PATHEXT`'s extensions on Windows). */
|
|
@@ -145,7 +196,9 @@ export declare function isGitSource(source: string): boolean;
|
|
|
145
196
|
* manifest, and locks it, enabled, with the marketplace it came from when it did (`origin`, for a
|
|
146
197
|
* temporary folder, is what the lock says it came from). An extension with the same name in that
|
|
147
198
|
* scope is replaced; its data folder stays. With `link`, a folder is registered where it is
|
|
148
|
-
* instead, to develop it: not copied, and its files not checked.
|
|
199
|
+
* instead, to develop it: not copied, and its files not checked. The packages its npx servers
|
|
200
|
+
* run are fetched into `packages` (#54), the agent's package cache (`packageCacheDir()`; by
|
|
201
|
+
* default the scope's).
|
|
149
202
|
*/
|
|
150
203
|
export declare function addExtension(source: string, scopeDir: string, options?: {
|
|
151
204
|
subdir?: string;
|
|
@@ -153,6 +206,8 @@ export declare function addExtension(source: string, scopeDir: string, options?:
|
|
|
153
206
|
marketplace?: string;
|
|
154
207
|
origin?: string;
|
|
155
208
|
link?: boolean;
|
|
209
|
+
packages?: string;
|
|
210
|
+
store?: string;
|
|
156
211
|
}): Promise<{
|
|
157
212
|
name: string;
|
|
158
213
|
replaced: boolean;
|
|
@@ -164,8 +219,28 @@ export declare function addExtension(source: string, scopeDir: string, options?:
|
|
|
164
219
|
*/
|
|
165
220
|
export declare function removeExtension(scopeDir: string, name: string, options?: {
|
|
166
221
|
deleteData?: boolean;
|
|
222
|
+
privateDir?: string;
|
|
167
223
|
}): Promise<boolean>;
|
|
168
224
|
/** The size in bytes of the files under `dir` (0 when it doesn't exist). */
|
|
169
225
|
export declare function folderSize(dir: string): Promise<number>;
|
|
170
226
|
/** Enables or disables an installed extension in a scope; false when it isn't there. */
|
|
171
227
|
export declare function setExtensionEnabled(scopeDir: string, name: string, enabled: boolean): Promise<boolean>;
|
|
228
|
+
/**
|
|
229
|
+
* Sets an installed extension's options (#55), each from what the person typed (`undefined`
|
|
230
|
+
* clears it), checked against its `userConfig`: sensitive ones into the scope's secrets, the rest
|
|
231
|
+
* into its lock. A running session's servers take them when they start again.
|
|
232
|
+
*/
|
|
233
|
+
export declare function setExtensionOptions(where: string | InstalledExtension, name: string, typed: Record<string, string | undefined>): Promise<void>;
|
|
234
|
+
/** One of an installed extension's options and its value now (a sensitive one's value is never given: only whether it's set). */
|
|
235
|
+
export interface ExtensionOptionState {
|
|
236
|
+
key: string;
|
|
237
|
+
option: ExtensionOption;
|
|
238
|
+
/** Its value, when it has one and isn't sensitive. */
|
|
239
|
+
value?: OptionValue;
|
|
240
|
+
/** Whether it has a value (its own or its default). */
|
|
241
|
+
set: boolean;
|
|
242
|
+
/** Its value is its default, not one the person gave. */
|
|
243
|
+
isDefault: boolean;
|
|
244
|
+
}
|
|
245
|
+
/** An installed extension's options, as they are now. */
|
|
246
|
+
export declare function extensionOptionStates(installed: InstalledExtension): Promise<ExtensionOptionState[]>;
|