@f5-sales-demo/xcsh 21.30.1 → 21.31.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/package.json +8 -8
- package/src/cli.ts +1 -0
- package/src/commands/profile.ts +72 -0
- package/src/extensibility/extensions/loader.ts +14 -31
- package/src/extensibility/extensions/types.ts +7 -3
- package/src/index.ts +1 -1
- package/src/internal-urls/build-info.generated.ts +9 -9
- package/src/internal-urls/docs-index.generated.ts +59 -64
- package/src/person-profile/README.md +54 -77
- package/src/person-profile/builder.ts +57 -17
- package/src/person-profile/machine-profile.ts +13 -1
- package/src/person-profile/private-store.ts +214 -54
- package/src/person-profile/salesforce-discovery.ts +1 -0
- package/src/person-profile/schema.ts +1 -0
- package/src/person-profile/service.ts +88 -20
- package/src/tools/machine-profile.ts +5 -1
- package/src/tools/person-profile.ts +5 -0
- package/src/person-profile/HISTORICAL-AUDIT.md +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.31.0",
|
|
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.31.0",
|
|
67
|
+
"@f5-sales-demo/pi-ai": "21.31.0",
|
|
68
|
+
"@f5-sales-demo/pi-natives": "21.31.0",
|
|
69
|
+
"@f5-sales-demo/pi-resource-management": "21.31.0",
|
|
70
|
+
"@f5-sales-demo/pi-tui": "21.31.0",
|
|
71
|
+
"@f5-sales-demo/pi-utils": "21.31.0",
|
|
72
|
+
"@f5-sales-demo/xcsh-stats": "21.31.0",
|
|
73
73
|
"@mozilla/readability": "^0.6",
|
|
74
74
|
"@sinclair/typebox": "0.34.52",
|
|
75
75
|
"@xterm/headless": "^6.0",
|
package/src/cli.ts
CHANGED
|
@@ -78,6 +78,7 @@ const commands: CommandEntry[] = [
|
|
|
78
78
|
{ name: "manager", load: () => import("./commands/manager").then(m => m.default) },
|
|
79
79
|
{ name: "office", load: () => import("./commands/office").then(m => m.default) },
|
|
80
80
|
{ name: "plugin", load: () => import("./commands/plugin").then(m => m.default) },
|
|
81
|
+
{ name: "profile", load: () => import("./commands/profile").then(m => m.default) },
|
|
81
82
|
{ name: "setup", load: () => import("./commands/setup").then(m => m.default) },
|
|
82
83
|
{ name: "shell", load: () => import("./commands/shell").then(m => m.default) },
|
|
83
84
|
{ name: "ssh", load: () => import("./commands/ssh").then(m => m.default) },
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { Args, Command, Flags } from "@f5-sales-demo/pi-utils/cli";
|
|
3
|
+
import { machineProfileService } from "../person-profile/machine-profile";
|
|
4
|
+
import { resetProfileTargets } from "../person-profile/private-store";
|
|
5
|
+
import { personProfileService } from "../person-profile/service";
|
|
6
|
+
|
|
7
|
+
type ProfileTarget = "person" | "computer" | "all";
|
|
8
|
+
|
|
9
|
+
export default class Profile extends Command {
|
|
10
|
+
static description = "Inspect or reset private person and computer profiles";
|
|
11
|
+
static args = {
|
|
12
|
+
action: Args.string({ description: "Profile action", options: ["status", "reset"], required: true }),
|
|
13
|
+
target: Args.string({ description: "Reset target", options: ["person", "computer", "all"] }),
|
|
14
|
+
};
|
|
15
|
+
static flags = {
|
|
16
|
+
json: Flags.boolean({ description: "Output machine-readable status", default: false }),
|
|
17
|
+
yes: Flags.boolean({ description: "Confirm permanent reset", default: false }),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
async run(): Promise<void> {
|
|
21
|
+
const { args, flags } = await this.parse(Profile);
|
|
22
|
+
if (args.action === "status") {
|
|
23
|
+
if (flags.yes) throw new Error("profile status does not accept --yes");
|
|
24
|
+
if (args.target) throw new Error("profile status does not accept a target");
|
|
25
|
+
const result = {
|
|
26
|
+
person: await personProfileService.status(),
|
|
27
|
+
computer: await machineProfileService.status(),
|
|
28
|
+
};
|
|
29
|
+
if (flags.json) {
|
|
30
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
for (const [name, status] of Object.entries(result)) {
|
|
34
|
+
const details = [
|
|
35
|
+
status.schemaVersion === undefined ? undefined : `schema v${status.schemaVersion}`,
|
|
36
|
+
status.permissions?.file ? `file ${status.permissions.file}` : undefined,
|
|
37
|
+
status.permissions?.directory ? `directory ${status.permissions.directory}` : undefined,
|
|
38
|
+
status.reason,
|
|
39
|
+
].filter(Boolean);
|
|
40
|
+
process.stdout.write(`${name}: ${status.status}${details.length ? ` (${details.join(", ")})` : ""}\n`);
|
|
41
|
+
if (status.remedy) process.stdout.write(` remedy: ${status.remedy}\n`);
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const target = args.target as ProfileTarget | undefined;
|
|
47
|
+
if (!target) throw new Error("profile reset requires person, computer, or all");
|
|
48
|
+
if (flags.json) throw new Error("profile reset does not accept --json");
|
|
49
|
+
const services =
|
|
50
|
+
target === "all"
|
|
51
|
+
? [personProfileService, machineProfileService]
|
|
52
|
+
: [target === "person" ? personProfileService : machineProfileService];
|
|
53
|
+
process.stdout.write(
|
|
54
|
+
`Profiles selected for permanent reset:\n${services.map(service => `- ${service.path}`).join("\n")}\n`,
|
|
55
|
+
);
|
|
56
|
+
if (!flags.yes) {
|
|
57
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
58
|
+
throw new Error("Non-interactive profile reset requires --yes");
|
|
59
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
60
|
+
try {
|
|
61
|
+
const answer = await prompt.question('Type "reset" to continue: ');
|
|
62
|
+
if (answer !== "reset") throw new Error("Profile reset cancelled");
|
|
63
|
+
} finally {
|
|
64
|
+
prompt.close();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const results = await resetProfileTargets(services);
|
|
68
|
+
for (const service of services) {
|
|
69
|
+
process.stdout.write(`${service.path}: ${results.get(service.path) ? "reset" : "already missing"}\n`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -15,7 +15,7 @@ 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 {
|
|
18
|
+
import { personProfileService } from "../../person-profile/service";
|
|
19
19
|
import type { CustomMessage } from "../../session/messages";
|
|
20
20
|
import { EventBus } from "../../utils/event-bus";
|
|
21
21
|
import { getAllPluginExtensionPaths } from "../plugins/loader";
|
|
@@ -121,6 +121,7 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
121
121
|
config: import("./types").ProviderConfig;
|
|
122
122
|
sourceId: string;
|
|
123
123
|
}> = [];
|
|
124
|
+
readonly personProfile: ExtensionAPI["personProfile"];
|
|
124
125
|
|
|
125
126
|
constructor(
|
|
126
127
|
public readonly pi: typeof import("@f5-sales-demo/xcsh"),
|
|
@@ -128,7 +129,18 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
128
129
|
private readonly runtime: IExtensionRuntime,
|
|
129
130
|
private readonly cwd: string,
|
|
130
131
|
public readonly events: EventBus,
|
|
131
|
-
) {
|
|
132
|
+
) {
|
|
133
|
+
const registrant = extension.resolvedPath;
|
|
134
|
+
this.personProfile = Object.freeze({
|
|
135
|
+
get: () => personProfileService.get(),
|
|
136
|
+
registerCollector: collector => {
|
|
137
|
+
if (!collector || typeof collector.collect !== "function")
|
|
138
|
+
throw new Error("Invalid person profile collector");
|
|
139
|
+
personProfileService.registerProfileCollector(collector, registrant);
|
|
140
|
+
},
|
|
141
|
+
unregisterCollector: (id: string) => personProfileService.unregisterProfileCollector(id, registrant),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
132
144
|
|
|
133
145
|
on<F extends HandlerFn>(event: string, handler: F): void {
|
|
134
146
|
const list = this.extension.handlers.get(event) ?? [];
|
|
@@ -249,35 +261,6 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
249
261
|
return this.runtime.setSessionName(name);
|
|
250
262
|
}
|
|
251
263
|
|
|
252
|
-
registerProfileCollector(collector: ProfileCollector): void {
|
|
253
|
-
if (!collector || typeof collector.collect !== "function") throw new Error("Invalid person profile collector");
|
|
254
|
-
personProfileService.registerProfileCollector(
|
|
255
|
-
{
|
|
256
|
-
...collector,
|
|
257
|
-
async collect(signal) {
|
|
258
|
-
const result = await collector.collect(signal);
|
|
259
|
-
if ("facts" in result) return result;
|
|
260
|
-
// Legacy flat output does not distinguish queried values from heuristic inference.
|
|
261
|
-
// Keep it as candidates; modern collectors can return explicit facts/observations.
|
|
262
|
-
return {
|
|
263
|
-
facts: {},
|
|
264
|
-
observations: Object.entries(result).map(([field, value]) => ({
|
|
265
|
-
field: field as keyof typeof result,
|
|
266
|
-
value,
|
|
267
|
-
source: collector.id,
|
|
268
|
-
kind: "inferred" as const,
|
|
269
|
-
observedAt: new Date().toISOString(),
|
|
270
|
-
})),
|
|
271
|
-
};
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
this.extension.resolvedPath,
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
unregisterProfileCollector(id: string): boolean {
|
|
278
|
-
return personProfileService.unregisterProfileCollector(id, this.extension.resolvedPath);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
264
|
registerProvider(name: string, config: import("./types").ProviderConfig): void {
|
|
282
265
|
this.runtime.pendingProviderRegistrations.push({ name, config, sourceId: this.extension.path });
|
|
283
266
|
}
|
|
@@ -1013,6 +1013,13 @@ export interface ExtensionAPI {
|
|
|
1013
1013
|
/** Injected pi-coding-agent exports for accessing SDK utilities */
|
|
1014
1014
|
pi: typeof piCodingAgent;
|
|
1015
1015
|
|
|
1016
|
+
/** Canonical, provenance-aware person profile integration. */
|
|
1017
|
+
readonly personProfile: {
|
|
1018
|
+
get(): Promise<import("../../person-profile/schema").PersonProfile>;
|
|
1019
|
+
registerCollector(collector: import("../../person-profile/service").ExtensionProfileCollector): void;
|
|
1020
|
+
unregisterCollector(id: string): boolean;
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1016
1023
|
// =========================================================================
|
|
1017
1024
|
// Event Subscription
|
|
1018
1025
|
// =========================================================================
|
|
@@ -1222,9 +1229,6 @@ export interface ExtensionAPI {
|
|
|
1222
1229
|
* });
|
|
1223
1230
|
*/
|
|
1224
1231
|
registerProvider(name: string, config: ProviderConfig): void;
|
|
1225
|
-
registerProfileCollector(collector: import("../../person-profile/service").ProfileCollector): void;
|
|
1226
|
-
unregisterProfileCollector(id: string): boolean;
|
|
1227
|
-
|
|
1228
1232
|
/** Shared event bus for extension communication. */
|
|
1229
1233
|
events: EventBus;
|
|
1230
1234
|
}
|
package/src/index.ts
CHANGED
|
@@ -40,7 +40,7 @@ export * from "./modes/components";
|
|
|
40
40
|
export * from "./modes/theme/theme";
|
|
41
41
|
export type { PersonProfile, UserProfile, UserProfileObservation } from "./person-profile/schema";
|
|
42
42
|
export type { ProfileCollection, ProfileCollector } from "./person-profile/service";
|
|
43
|
-
export {
|
|
43
|
+
export { PersonProfileService } from "./person-profile/service";
|
|
44
44
|
export * from "./routing/classifier";
|
|
45
45
|
export * from "./routing/coordinator";
|
|
46
46
|
export * from "./routing/delegation";
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "21.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
23
|
-
"branch": "
|
|
24
|
-
"tag": "v21.
|
|
25
|
-
"commitDate": "2026-09-
|
|
26
|
-
"buildDate": "2026-09-
|
|
20
|
+
"version": "21.31.0",
|
|
21
|
+
"commit": "e606d58ea45deae40b61f053d92e3e9c0fe9b80e",
|
|
22
|
+
"shortCommit": "e606d58",
|
|
23
|
+
"branch": "unknown",
|
|
24
|
+
"tag": "v21.31.0",
|
|
25
|
+
"commitDate": "2026-09-17T16:35:46Z",
|
|
26
|
+
"buildDate": "2026-09-17T23:33:05.447Z",
|
|
27
27
|
"dirty": true,
|
|
28
28
|
"prNumber": "",
|
|
29
29
|
"repoUrl": "https://github.com/f5-sales-demo/xcsh",
|
|
30
30
|
"repoSlug": "f5-sales-demo/xcsh",
|
|
31
|
-
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/
|
|
32
|
-
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v21.
|
|
31
|
+
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/e606d58ea45deae40b61f053d92e3e9c0fe9b80e",
|
|
32
|
+
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v21.31.0"
|
|
33
33
|
};
|