@f5-sales-demo/xcsh 21.32.4 → 21.33.2
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/package.json +8 -8
- package/src/cli/plugin-cli.ts +161 -5
- package/src/commands/plugin.ts +2 -0
- package/src/extensibility/extensions/loader.ts +46 -6
- package/src/extensibility/extensions/runner.ts +7 -7
- package/src/extensibility/extensions/types.ts +9 -18
- package/src/extensibility/plugins/marketplace/builtin-marketplace-snapshot.ts +2 -443
- package/src/extensibility/plugins/marketplace/builtin-marketplace.provenance.json +3 -3
- package/src/extensibility/plugins/marketplace/fetcher.ts +74 -51
- package/src/extensibility/plugins/marketplace/types.ts +12 -7
- package/src/integrations/index.ts +3 -0
- package/src/integrations/registry.ts +290 -0
- package/src/integrations/setup.ts +49 -0
- package/src/integrations/types.ts +89 -0
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/modes/components/plugins/plugin-dashboard.ts +22 -43
- package/src/modes/components/plugins/state-manager.ts +2 -2
- package/src/modes/components/plugins/types.ts +4 -8
- package/src/modes/components/welcome-checks.ts +11 -1
- package/src/modes/rpc/rpc-mode.ts +17 -9
- package/src/person-profile/collectors.ts +3 -169
- package/src/person-profile/private-store.ts +3 -1
- package/src/person-profile/schema.ts +48 -3
- package/src/person-profile/service.ts +137 -18
- package/src/slash-commands/builtin-registry.ts +51 -25
- package/src/slash-commands/plugin-reviewed-actions.ts +7 -31
- package/src/extensibility/plugins/marketplace/prerequisites.ts +0 -255
- package/src/person-profile/account-collectors.ts +0 -209
- package/src/person-profile/salesforce-discovery.ts +0 -163
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "21.
|
|
4
|
+
"version": "21.33.2",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/f5-sales-demo/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@agentclientprotocol/sdk": "1.4.0",
|
|
66
|
-
"@f5-sales-demo/pi-agent-core": "21.
|
|
67
|
-
"@f5-sales-demo/pi-ai": "21.
|
|
68
|
-
"@f5-sales-demo/pi-natives": "21.
|
|
69
|
-
"@f5-sales-demo/pi-resource-management": "21.
|
|
70
|
-
"@f5-sales-demo/pi-tui": "21.
|
|
71
|
-
"@f5-sales-demo/pi-utils": "21.
|
|
72
|
-
"@f5-sales-demo/xcsh-stats": "21.
|
|
66
|
+
"@f5-sales-demo/pi-agent-core": "21.33.2",
|
|
67
|
+
"@f5-sales-demo/pi-ai": "21.33.2",
|
|
68
|
+
"@f5-sales-demo/pi-natives": "21.33.2",
|
|
69
|
+
"@f5-sales-demo/pi-resource-management": "21.33.2",
|
|
70
|
+
"@f5-sales-demo/pi-tui": "21.33.2",
|
|
71
|
+
"@f5-sales-demo/pi-utils": "21.33.2",
|
|
72
|
+
"@f5-sales-demo/xcsh-stats": "21.33.2",
|
|
73
73
|
"@mozilla/readability": "^0.6",
|
|
74
74
|
"@sinclair/typebox": "0.34.52",
|
|
75
75
|
"@xterm/headless": "^6.0",
|
package/src/cli/plugin-cli.ts
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
* Handles `xcsh plugin <command>` subcommands for plugin lifecycle management.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { createInterface } from "node:readline/promises";
|
|
7
8
|
import { APP_NAME, getProjectDir } from "@f5-sales-demo/pi-utils";
|
|
8
9
|
import chalk from "chalk";
|
|
9
10
|
import { resolveOrDefaultProjectRegistryPath } from "../discovery/helpers";
|
|
11
|
+
import { discoverAndLoadExtensions } from "../extensibility/extensions";
|
|
10
12
|
import { PluginManager, parseSettingValue, validateSetting } from "../extensibility/plugins";
|
|
11
13
|
import {
|
|
12
14
|
formatMarketplaceRefreshWarning,
|
|
@@ -16,7 +18,10 @@ import {
|
|
|
16
18
|
getPluginsCacheDir,
|
|
17
19
|
MarketplaceManager,
|
|
18
20
|
} from "../extensibility/plugins/marketplace/index.js";
|
|
21
|
+
import { describeSetupPlan, executeReviewedSetup } from "../integrations/setup";
|
|
22
|
+
import type { IntegrationHandle } from "../integrations/types";
|
|
19
23
|
import { theme } from "../modes/theme/theme";
|
|
24
|
+
import { personProfileService } from "../person-profile/service";
|
|
20
25
|
|
|
21
26
|
// =============================================================================
|
|
22
27
|
// Types
|
|
@@ -34,7 +39,9 @@ export type PluginAction =
|
|
|
34
39
|
| "disable"
|
|
35
40
|
| "marketplace"
|
|
36
41
|
| "discover"
|
|
37
|
-
| "upgrade"
|
|
42
|
+
| "upgrade"
|
|
43
|
+
| "status"
|
|
44
|
+
| "setup";
|
|
38
45
|
|
|
39
46
|
export interface PluginCommandArgs {
|
|
40
47
|
action: PluginAction;
|
|
@@ -69,6 +76,8 @@ const VALID_ACTIONS: PluginAction[] = [
|
|
|
69
76
|
"marketplace",
|
|
70
77
|
"discover",
|
|
71
78
|
"upgrade",
|
|
79
|
+
"status",
|
|
80
|
+
"setup",
|
|
72
81
|
];
|
|
73
82
|
|
|
74
83
|
/**
|
|
@@ -187,6 +196,149 @@ export async function runPluginCommand(cmd: PluginCommandArgs): Promise<void> {
|
|
|
187
196
|
case "upgrade":
|
|
188
197
|
await handleUpgrade(cmd.args, cmd.flags);
|
|
189
198
|
break;
|
|
199
|
+
case "status":
|
|
200
|
+
await handleIntegrationStatus(cmd.args, cmd.flags);
|
|
201
|
+
break;
|
|
202
|
+
case "setup":
|
|
203
|
+
await handleIntegrationSetup(cmd.args, cmd.flags);
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function loadIntegrationHandles(): Promise<IntegrationHandle<unknown>[]> {
|
|
209
|
+
const loaded = await discoverAndLoadExtensions([], process.cwd());
|
|
210
|
+
if (loaded.errors.length) throw new Error(`Unable to load ${loaded.errors.length} plugin extension(s)`);
|
|
211
|
+
return loaded.extensions.flatMap(extension => [...extension.integrations.values()]);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function matchesIntegration(handle: IntegrationHandle<unknown>, target: string): boolean {
|
|
215
|
+
return handle.id === target || handle.plugin === target || handle.plugin?.split("@")[0] === target;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function selectSetupIntegration(
|
|
219
|
+
handles: readonly IntegrationHandle<unknown>[],
|
|
220
|
+
target: string,
|
|
221
|
+
): IntegrationHandle<unknown> {
|
|
222
|
+
const matches = handles.filter(handle => matchesIntegration(handle, target));
|
|
223
|
+
if (!matches.length) throw new Error(`No enabled integration is registered for ${target}`);
|
|
224
|
+
const setupMatches = matches.filter(handle => handle.setupPlan !== undefined);
|
|
225
|
+
if (!setupMatches.length) throw new Error(`No setup plan is registered for ${target}`);
|
|
226
|
+
if (setupMatches.length > 1)
|
|
227
|
+
throw new Error(`Plugin ${target} registers multiple setup plans; use an integration id`);
|
|
228
|
+
return setupMatches[0];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function handleIntegrationStatus(args: string[], flags: { json?: boolean }): Promise<void> {
|
|
232
|
+
if (args.length > 1) throw new Error(`Usage: ${APP_NAME} plugin status [plugin] [--json]`);
|
|
233
|
+
const handles = await loadIntegrationHandles();
|
|
234
|
+
const selected = args[0] ? handles.filter(handle => matchesIntegration(handle, args[0])) : handles;
|
|
235
|
+
if (args[0] && !selected.length) throw new Error(`No enabled integration is registered for ${args[0]}`);
|
|
236
|
+
const statuses = await Promise.all(
|
|
237
|
+
selected.map(async handle => {
|
|
238
|
+
const { value: _, ...status } = await handle.get();
|
|
239
|
+
return {
|
|
240
|
+
...status,
|
|
241
|
+
nextAction:
|
|
242
|
+
status.state === "setup_required" ? `${APP_NAME} plugin setup ${handle.plugin ?? handle.id}` : undefined,
|
|
243
|
+
};
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
246
|
+
if (flags.json) {
|
|
247
|
+
process.stdout.write(`${JSON.stringify({ integrations: statuses })}\n`);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (!statuses.length) {
|
|
251
|
+
process.stdout.write("No enabled plugin integrations are registered.\n");
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
for (const status of statuses) {
|
|
255
|
+
process.stdout.write(
|
|
256
|
+
`${status.plugin ?? status.id}: ${status.state}${status.reason ? ` (${status.reason})` : ""}\n`,
|
|
257
|
+
);
|
|
258
|
+
if (status.nextAction) process.stdout.write(` next: ${status.nextAction}\n`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export async function reviewAndExecuteIntegrationSetup(handle: IntegrationHandle<unknown>) {
|
|
263
|
+
const plan = handle.setupPlan;
|
|
264
|
+
if (!plan) throw new Error(`Integration ${handle.id} does not declare setup`);
|
|
265
|
+
const current = await handle.get();
|
|
266
|
+
if (current.state === "ready") return current;
|
|
267
|
+
process.stdout.write(`${describeSetupPlan(handle)}\n`);
|
|
268
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
269
|
+
try {
|
|
270
|
+
const answer = await prompt.question('Type "setup" to run this exact plan: ');
|
|
271
|
+
if (answer !== "setup") throw new Error("Plugin setup cancelled; the installed plugin remains setup_required");
|
|
272
|
+
} finally {
|
|
273
|
+
prompt.close();
|
|
274
|
+
}
|
|
275
|
+
const result = await executeReviewedSetup(handle, plan);
|
|
276
|
+
await personProfileService.reconcileFromCollectors(undefined, 0);
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function handleIntegrationSetup(args: string[], flags: { json?: boolean }): Promise<void> {
|
|
281
|
+
if (flags.json) throw new Error("plugin setup is human-only and does not accept --json");
|
|
282
|
+
if (args.length !== 1) throw new Error(`Usage: ${APP_NAME} plugin setup <plugin>`);
|
|
283
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Plugin setup requires an interactive terminal");
|
|
284
|
+
const handle = selectSetupIntegration(await loadIntegrationHandles(), args[0]);
|
|
285
|
+
const current = await handle.get();
|
|
286
|
+
if (current.state === "ready") {
|
|
287
|
+
process.stdout.write(`${handle.plugin ?? handle.id}: ready (setup is not required)\n`);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const result = await reviewAndExecuteIntegrationSetup(handle);
|
|
291
|
+
process.stdout.write(
|
|
292
|
+
`${handle.plugin ?? handle.id}: ${result.state}${result.reason ? ` (${result.reason})` : ""}\n`,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function reportMarketplaceInstallation(
|
|
297
|
+
plugin: string,
|
|
298
|
+
marketplace: string,
|
|
299
|
+
entry: { version: string; scope: string; setupRequired: boolean },
|
|
300
|
+
flags: { json?: boolean },
|
|
301
|
+
): Promise<void> {
|
|
302
|
+
let handles: IntegrationHandle<unknown>[] = [];
|
|
303
|
+
try {
|
|
304
|
+
handles = (await loadIntegrationHandles()).filter(handle => matchesIntegration(handle, plugin));
|
|
305
|
+
} catch {
|
|
306
|
+
// Installation remains successful even when extension loading cannot establish readiness.
|
|
307
|
+
}
|
|
308
|
+
const statuses = await Promise.all(
|
|
309
|
+
handles.map(async handle => {
|
|
310
|
+
const { value: _, ...status } = await handle.get();
|
|
311
|
+
return status;
|
|
312
|
+
}),
|
|
313
|
+
);
|
|
314
|
+
const setupHandles = handles.filter(handle => handle.setupPlan !== undefined);
|
|
315
|
+
const setupHandle = setupHandles.length === 1 ? setupHandles[0] : undefined;
|
|
316
|
+
const setupStatus = setupHandle ? statuses.find(status => status.id === setupHandle.id) : undefined;
|
|
317
|
+
const nextAction =
|
|
318
|
+
entry.setupRequired && setupStatus?.state !== "ready" ? `${APP_NAME} plugin setup ${plugin}` : undefined;
|
|
319
|
+
if (flags.json || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
320
|
+
const result = {
|
|
321
|
+
installation: { state: "installed", plugin, marketplace, version: entry.version, scope: entry.scope },
|
|
322
|
+
readiness: { integrations: statuses },
|
|
323
|
+
...(nextAction ? { nextAction } : {}),
|
|
324
|
+
};
|
|
325
|
+
if (flags.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
326
|
+
else {
|
|
327
|
+
process.stdout.write(`${theme.status.success} Installed ${plugin} from ${marketplace} (${entry.version})\n`);
|
|
328
|
+
for (const status of statuses)
|
|
329
|
+
process.stdout.write(` ${status.id}: ${status.state}${status.reason ? ` (${status.reason})` : ""}\n`);
|
|
330
|
+
if (nextAction) process.stdout.write(` next: ${nextAction}\n`);
|
|
331
|
+
}
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
process.stdout.write(`${theme.status.success} Installed ${plugin} from ${marketplace} (${entry.version})\n`);
|
|
335
|
+
if (!setupHandle || setupStatus?.state === "ready") return;
|
|
336
|
+
try {
|
|
337
|
+
const result = await reviewAndExecuteIntegrationSetup(setupHandle);
|
|
338
|
+
process.stdout.write(`${plugin}: ${result.state}${result.reason ? ` (${result.reason})` : ""}\n`);
|
|
339
|
+
} catch (error) {
|
|
340
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
341
|
+
process.stderr.write(`Next: ${APP_NAME} plugin setup ${plugin}\n`);
|
|
190
342
|
}
|
|
191
343
|
}
|
|
192
344
|
|
|
@@ -461,6 +613,9 @@ async function handleInstall(
|
|
|
461
613
|
}
|
|
462
614
|
|
|
463
615
|
if (target.type === "marketplace") {
|
|
616
|
+
const catalogEntry = listings.find(
|
|
617
|
+
item => item.marketplace === target.marketplace && item.plugin.name === target.name,
|
|
618
|
+
)?.plugin;
|
|
464
619
|
if (flags.dryRun) {
|
|
465
620
|
const preview = {
|
|
466
621
|
action: "install",
|
|
@@ -477,10 +632,11 @@ async function handleInstall(
|
|
|
477
632
|
force: flags.force,
|
|
478
633
|
scope: flags.scope,
|
|
479
634
|
});
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
635
|
+
await reportMarketplaceInstallation(
|
|
636
|
+
target.name,
|
|
637
|
+
target.marketplace,
|
|
638
|
+
{ ...entry, setupRequired: catalogEntry?.lifecycle.setupRequired ?? false },
|
|
639
|
+
flags,
|
|
484
640
|
);
|
|
485
641
|
} catch (err) {
|
|
486
642
|
console.error(chalk.red(`${theme.status.error} Failed to install ${spec}: ${err}`));
|
package/src/commands/plugin.ts
CHANGED
|
@@ -15,6 +15,8 @@ import { loadCapability } from "../../discovery";
|
|
|
15
15
|
import { getExtensionNameFromPath, getPreloadedPluginRoots } from "../../discovery/helpers";
|
|
16
16
|
import type { ExecOptions } from "../../exec/exec";
|
|
17
17
|
import { execCommand } from "../../exec/exec";
|
|
18
|
+
import { integrationRegistry } from "../../integrations/registry";
|
|
19
|
+
import type { IntegrationDefinition, IntegrationHandle } from "../../integrations/types";
|
|
18
20
|
import { personProfileService } from "../../person-profile/service";
|
|
19
21
|
import type { CustomMessage } from "../../session/messages";
|
|
20
22
|
import { EventBus } from "../../utils/event-bus";
|
|
@@ -33,7 +35,6 @@ import type {
|
|
|
33
35
|
LoadExtensionsResult,
|
|
34
36
|
MessageRenderer,
|
|
35
37
|
RegisteredCommand,
|
|
36
|
-
ServiceStatusContribution,
|
|
37
38
|
ToolDefinition,
|
|
38
39
|
} from "./types";
|
|
39
40
|
|
|
@@ -122,6 +123,7 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
122
123
|
sourceId: string;
|
|
123
124
|
}> = [];
|
|
124
125
|
readonly personProfile: ExtensionAPI["personProfile"];
|
|
126
|
+
readonly integrations: ExtensionAPI["integrations"];
|
|
125
127
|
|
|
126
128
|
constructor(
|
|
127
129
|
public readonly pi: typeof import("@f5-sales-demo/xcsh"),
|
|
@@ -131,6 +133,8 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
131
133
|
public readonly events: EventBus,
|
|
132
134
|
) {
|
|
133
135
|
const registrant = extension.resolvedPath;
|
|
136
|
+
integrationRegistry.unregisterOwner(registrant);
|
|
137
|
+
personProfileService.unregisterProfileCollectorsByRegistrant(registrant);
|
|
134
138
|
this.personProfile = Object.freeze({
|
|
135
139
|
get: () => personProfileService.get(),
|
|
136
140
|
registerCollector: collector => {
|
|
@@ -140,6 +144,46 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
140
144
|
},
|
|
141
145
|
unregisterCollector: (id: string) => personProfileService.unregisterProfileCollector(id, registrant),
|
|
142
146
|
});
|
|
147
|
+
this.integrations = Object.freeze({
|
|
148
|
+
register: <T>(definition: IntegrationDefinition<T>): IntegrationHandle<T> => {
|
|
149
|
+
const handle = integrationRegistry.register(registrant, definition);
|
|
150
|
+
extension.integrations.set(definition.id, handle as IntegrationHandle<unknown>);
|
|
151
|
+
if (definition.profile) {
|
|
152
|
+
const profile = definition.profile;
|
|
153
|
+
personProfileService.registerProfileCollector(
|
|
154
|
+
{
|
|
155
|
+
id: definition.id,
|
|
156
|
+
name: definition.name,
|
|
157
|
+
dependsOn: definition.dependencies,
|
|
158
|
+
available: async () => true,
|
|
159
|
+
collect: async signal => {
|
|
160
|
+
const snapshot = await handle.get(signal);
|
|
161
|
+
if (snapshot.state !== "ready" || snapshot.value === undefined)
|
|
162
|
+
return {
|
|
163
|
+
facts: {},
|
|
164
|
+
observations: [],
|
|
165
|
+
sourceState: {
|
|
166
|
+
state: snapshot.state,
|
|
167
|
+
...(snapshot.reason ? { reason: snapshot.reason } : {}),
|
|
168
|
+
...(snapshot.retryAt ? { retryAt: new Date(snapshot.retryAt).toISOString() } : {}),
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
return { ...profile(snapshot.value), sourceState: { state: snapshot.state } };
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
registrant,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return handle;
|
|
178
|
+
},
|
|
179
|
+
unregister: (id: string): boolean => {
|
|
180
|
+
const removed = integrationRegistry.unregister(registrant, id);
|
|
181
|
+
if (!removed) return false;
|
|
182
|
+
extension.integrations.delete(id);
|
|
183
|
+
personProfileService.unregisterProfileCollector(id, registrant);
|
|
184
|
+
return true;
|
|
185
|
+
},
|
|
186
|
+
});
|
|
143
187
|
}
|
|
144
188
|
|
|
145
189
|
on<F extends HandlerFn>(event: string, handler: F): void {
|
|
@@ -194,10 +238,6 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
194
238
|
this.extension.messageRenderers.set(customType, renderer as MessageRenderer);
|
|
195
239
|
}
|
|
196
240
|
|
|
197
|
-
registerServiceStatus(contribution: ServiceStatusContribution): void {
|
|
198
|
-
this.extension.serviceStatuses.set(contribution.name, contribution);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
241
|
getFlag(name: string): boolean | string | undefined {
|
|
202
242
|
if (!this.extension.flags.has(name)) return undefined;
|
|
203
243
|
return this.runtime.flagValues.get(name);
|
|
@@ -279,7 +319,7 @@ function createExtension(extensionPath: string, resolvedPath: string): Extension
|
|
|
279
319
|
commands: new Map(),
|
|
280
320
|
flags: new Map(),
|
|
281
321
|
shortcuts: new Map(),
|
|
282
|
-
|
|
322
|
+
integrations: new Map(),
|
|
283
323
|
};
|
|
284
324
|
}
|
|
285
325
|
|
|
@@ -6,6 +6,7 @@ import type { ImageContent, Model } from "@f5-sales-demo/pi-ai";
|
|
|
6
6
|
import type { KeyId } from "@f5-sales-demo/pi-tui";
|
|
7
7
|
import { logger } from "@f5-sales-demo/pi-utils";
|
|
8
8
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
9
|
+
import type { IntegrationHandle } from "../../integrations/types";
|
|
9
10
|
import { type Theme, theme } from "../../modes/theme/theme";
|
|
10
11
|
import type { SessionManager } from "../../session/session-manager";
|
|
11
12
|
import type {
|
|
@@ -36,7 +37,6 @@ import type {
|
|
|
36
37
|
RegisteredTool,
|
|
37
38
|
ResourcesDiscoverEvent,
|
|
38
39
|
ResourcesDiscoverResult,
|
|
39
|
-
ServiceStatusContribution,
|
|
40
40
|
SessionBeforeBranchResult,
|
|
41
41
|
SessionBeforeCompactResult,
|
|
42
42
|
SessionBeforeSwitchResult,
|
|
@@ -263,15 +263,15 @@ export class ExtensionRunner {
|
|
|
263
263
|
return tools;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
/** Get all
|
|
267
|
-
|
|
268
|
-
const
|
|
266
|
+
/** Get all integration handles registered by the loaded extensions. */
|
|
267
|
+
getAllRegisteredIntegrations(): IntegrationHandle<unknown>[] {
|
|
268
|
+
const integrations: IntegrationHandle<unknown>[] = [];
|
|
269
269
|
for (const ext of this.extensions) {
|
|
270
|
-
for (const
|
|
271
|
-
|
|
270
|
+
for (const handle of ext.integrations.values()) {
|
|
271
|
+
integrations.push(handle);
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
|
-
return
|
|
274
|
+
return integrations;
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
getFlags(): Map<string, ExtensionFlag> {
|
|
@@ -974,20 +974,6 @@ export interface RegisteredCommand {
|
|
|
974
974
|
handler: (args: string, ctx: ExtensionCommandContext) => Promise<void>;
|
|
975
975
|
}
|
|
976
976
|
|
|
977
|
-
// ============================================================================
|
|
978
|
-
// Service Status Contributions
|
|
979
|
-
// ============================================================================
|
|
980
|
-
|
|
981
|
-
export interface ServiceStatusContribution {
|
|
982
|
-
name: string;
|
|
983
|
-
group?: string;
|
|
984
|
-
check: () => Promise<{ state: "connected" | "unauthenticated" | "unavailable"; hint?: string }>;
|
|
985
|
-
fix?: {
|
|
986
|
-
prompt: string;
|
|
987
|
-
command: string[];
|
|
988
|
-
};
|
|
989
|
-
}
|
|
990
|
-
|
|
991
977
|
// ============================================================================
|
|
992
978
|
// Extension API
|
|
993
979
|
// ============================================================================
|
|
@@ -1020,6 +1006,14 @@ export interface ExtensionAPI {
|
|
|
1020
1006
|
unregisterCollector(id: string): boolean;
|
|
1021
1007
|
};
|
|
1022
1008
|
|
|
1009
|
+
/** Process-wide provider readiness, setup and profile-discovery lifecycle. */
|
|
1010
|
+
readonly integrations: {
|
|
1011
|
+
register<T>(
|
|
1012
|
+
definition: import("../../integrations/types").IntegrationDefinition<T>,
|
|
1013
|
+
): import("../../integrations/types").IntegrationHandle<T>;
|
|
1014
|
+
unregister(id: string): boolean;
|
|
1015
|
+
};
|
|
1016
|
+
|
|
1023
1017
|
// =========================================================================
|
|
1024
1018
|
// Event Subscription
|
|
1025
1019
|
// =========================================================================
|
|
@@ -1129,9 +1123,6 @@ export interface ExtensionAPI {
|
|
|
1129
1123
|
/** Register a custom renderer for CustomMessageEntry. */
|
|
1130
1124
|
registerMessageRenderer<T = unknown>(customType: string, renderer: MessageRenderer<T>): void;
|
|
1131
1125
|
|
|
1132
|
-
/** Register a service status check for the welcome screen. */
|
|
1133
|
-
registerServiceStatus(contribution: ServiceStatusContribution): void;
|
|
1134
|
-
|
|
1135
1126
|
// =========================================================================
|
|
1136
1127
|
// Actions
|
|
1137
1128
|
// =========================================================================
|
|
@@ -1422,7 +1413,7 @@ export interface Extension {
|
|
|
1422
1413
|
commands: Map<string, RegisteredCommand>;
|
|
1423
1414
|
flags: Map<string, ExtensionFlag>;
|
|
1424
1415
|
shortcuts: Map<KeyId, ExtensionShortcut>;
|
|
1425
|
-
|
|
1416
|
+
integrations: Map<string, import("../../integrations/types").IntegrationHandle<unknown>>;
|
|
1426
1417
|
}
|
|
1427
1418
|
|
|
1428
1419
|
/** Result of loading extensions. */
|