@abgov/nx-adsp 12.6.0 → 12.8.0-beta.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/nx-adsp",
3
- "version": "12.6.0",
3
+ "version": "12.8.0-beta.1",
4
4
  "license": "Apache-2.0",
5
5
  "main": "src/index.js",
6
6
  "description": "Government of Alberta - Nx plugin for ADSP apps.",
@@ -13,16 +13,17 @@
13
13
  "@abgov/nx-oc": "^12.0.0",
14
14
  "@nx-dotnet/core": "^3.0.0",
15
15
  "@nx/angular": "^22.0.0",
16
- "@nx/express": "^22.0.0",
17
16
  "@nx/devkit": "^22.0.0",
18
- "@nx/react": "^22.0.0",
19
17
  "@nx/eslint": "^22.0.0",
18
+ "@nx/express": "^22.0.0",
19
+ "@nx/react": "^22.0.0",
20
20
  "tslib": "^2.0.0"
21
21
  },
22
22
  "dependencies": {
23
23
  "axios": "^1.16.0",
24
24
  "enquirer": "^2.3.6",
25
- "json-schema-to-typescript": "^13.0.1"
25
+ "json-schema-to-typescript": "^13.0.1",
26
+ "socket.io-client": "^4.8.3"
26
27
  },
27
28
  "generators": "./generators.json",
28
29
  "scripts": {},
@@ -50,10 +50,6 @@ function addFiles(host, options) {
50
50
  }
51
51
  return addProxyConf;
52
52
  }
53
- function removeFiles(host, options) {
54
- host.delete(`${options.projectRoot}/src/app/logo.svg`);
55
- host.delete(`${options.projectRoot}/src/app/star.svg`);
56
- }
57
53
  function default_1(host, options) {
58
54
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
59
55
  var _a, _b;
@@ -1 +1 @@
1
- {"version":3,"file":"angular-app.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/angular-app/angular-app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"angular-app.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/angular-app/angular-app.ts"],"names":[],"mappings":"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"}
@@ -6,11 +6,41 @@ const nx_oc_1 = require("@abgov/nx-oc");
6
6
  const devkit_1 = require("@nx/devkit");
7
7
  const eslint_1 = require("@nx/eslint");
8
8
  const path = require("path");
9
+ const agent_1 = require("../../utils/agent");
10
+ // Version of nx-adsp passed to the agent for template compatibility checks.
11
+ // Keep in sync with package.json version.
12
+ const PLUGIN_VERSION = '12.x';
9
13
  function normalizeOptions(host, options) {
10
14
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
15
+ var _a, _b, _c;
11
16
  const projectName = (0, devkit_1.names)(options.name).fileName;
12
17
  const projectRoot = `${(0, devkit_1.getWorkspaceLayout)(host).appsDir}/${projectName}`;
13
- const adsp = yield (0, nx_oc_1.getAdspConfiguration)(host, options);
18
+ let adsp;
19
+ if (options.tenant) {
20
+ // Resolve realm from tenant name via the public tenant service API (no auth required),
21
+ // then do a single browser login for the tenant realm.
22
+ const env = nx_oc_1.environments[(_a = options.env) !== null && _a !== void 0 ? _a : 'prod'];
23
+ const tenantServiceUrl = (yield (0, nx_oc_1.getServiceUrls)(env.directoryServiceUrl))['urn:ads:platform:tenant-service'];
24
+ const { default: axios } = yield Promise.resolve().then(() => require('axios'));
25
+ const { data } = yield axios.get(new URL('/api/tenant/v2/tenants', tenantServiceUrl).href, { params: { name: options.tenant } });
26
+ const tenantInfo = (_b = data === null || data === void 0 ? void 0 : data.results) === null || _b === void 0 ? void 0 : _b[0];
27
+ if (!tenantInfo) {
28
+ throw new Error(`Tenant "${options.tenant}" not found in ${env.directoryServiceUrl}.`);
29
+ }
30
+ const tenantRealm = (_c = options.tenantRealm) !== null && _c !== void 0 ? _c : tenantInfo.realm;
31
+ if (!options.accessToken) {
32
+ options = Object.assign(Object.assign({}, options), { accessToken: yield (0, nx_oc_1.realmLogin)(env.accessServiceUrl, tenantRealm).catch(() => undefined) });
33
+ }
34
+ adsp = {
35
+ tenant: tenantInfo.name,
36
+ tenantRealm,
37
+ accessServiceUrl: env.accessServiceUrl,
38
+ directoryServiceUrl: env.directoryServiceUrl,
39
+ };
40
+ }
41
+ else {
42
+ adsp = yield (0, nx_oc_1.getAdspConfiguration)(host, options);
43
+ }
14
44
  return Object.assign(Object.assign({}, options), { projectName,
15
45
  projectRoot,
16
46
  adsp });
@@ -22,6 +52,7 @@ function addFiles(host, options) {
22
52
  }
23
53
  function default_1(host, options) {
24
54
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
55
+ var _a, _b, _c, _d, _e;
25
56
  const normalizedOptions = yield normalizeOptions(host, options);
26
57
  const { applicationGenerator: initExpress } = yield Promise.resolve().then(() => require('@nx/express'));
27
58
  yield initExpress(host, Object.assign(Object.assign({}, options), { skipFormat: true, skipPackageJson: false, linter: eslint_1.Linter.EsLint, unitTestRunner: 'jest', js: false, directory: `apps/${options.name}` }));
@@ -42,6 +73,47 @@ function default_1(host, options) {
42
73
  });
43
74
  addFiles(host, normalizedOptions);
44
75
  yield (0, devkit_1.formatFiles)(host);
76
+ // Consult the nx-adsp-agent to augment the project with ADSP capabilities.
77
+ // The agent has access to template tools and a workspace; it generates new
78
+ // files and modifications to integration files (main.ts, environment.ts)
79
+ // which are applied directly to the Nx Tree.
80
+ // Falls back silently if agent-service is unreachable or no accessToken.
81
+ if (normalizedOptions.adsp) {
82
+ // getAdspConfiguration authenticates but doesn't return the token.
83
+ // Re-authenticate with the tenant realm to get a token for the agent call.
84
+ process.stdout.write('\nSigning in to connect to the ADSP agent...\n');
85
+ const accessToken = (_a = options.accessToken) !== null && _a !== void 0 ? _a : (yield (0, nx_oc_1.realmLogin)(normalizedOptions.adsp.accessServiceUrl, normalizedOptions.adsp.tenantRealm).catch((err) => {
86
+ var _a;
87
+ process.stdout.write(`Agent sign-in failed (${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}) — skipping agent interaction.\n`);
88
+ return undefined;
89
+ }));
90
+ const mainTs = (_c = (_b = host.read(`${normalizedOptions.projectRoot}/src/main.ts`)) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : '';
91
+ const environmentTs = (_e = (_d = host.read(`${normalizedOptions.projectRoot}/src/environment.ts`)) === null || _d === void 0 ? void 0 : _d.toString()) !== null && _e !== void 0 ? _e : '';
92
+ const agentResult = yield (0, agent_1.consultAgent)(normalizedOptions.adsp.directoryServiceUrl, accessToken, {
93
+ projectName: normalizedOptions.projectName,
94
+ projectType: 'express-service',
95
+ tenant: normalizedOptions.adsp.tenant,
96
+ pluginVersion: PLUGIN_VERSION,
97
+ existingFiles: {
98
+ 'src/main.ts': mainTs,
99
+ 'src/environment.ts': environmentTs,
100
+ },
101
+ }, host, normalizedOptions.projectRoot);
102
+ // When the user was in an active conversation but it ended without the
103
+ // agent generating files, confirm whether to proceed with base scaffolding.
104
+ if ((agentResult === null || agentResult === void 0 ? void 0 : agentResult.userInteracted) && agentResult.filesWritten === 0) {
105
+ const { prompt } = yield Promise.resolve().then(() => require('enquirer'));
106
+ const { proceed } = yield prompt({
107
+ type: 'confirm',
108
+ name: 'proceed',
109
+ message: 'Agent interaction ended without generating files. Continue with base scaffolding?',
110
+ initial: true,
111
+ });
112
+ if (!proceed) {
113
+ throw new Error('Generation aborted.');
114
+ }
115
+ }
116
+ }
45
117
  yield (0, nx_oc_1.deploymentGenerator)(host, Object.assign(Object.assign({}, normalizedOptions), { appType: 'node', project: normalizedOptions.projectName }));
46
118
  return () => {
47
119
  (0, devkit_1.installPackagesTask)(host);
@@ -1 +1 @@
1
- {"version":3,"file":"express-service.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/express-service/express-service.ts"],"names":[],"mappings":";;AA6CA,4BA8CC;;AA3FD,wCAAyE;AACzE,uCAQoB;AACpB,uCAAoC;AACpC,6BAA6B;AAG7B,SAAe,gBAAgB,CAC7B,IAAU,EACV,OAAe;;QAEf,MAAM,WAAW,GAAG,IAAA,cAAK,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC;QACjD,MAAM,WAAW,GAAG,GAAG,IAAA,2BAAkB,EAAC,IAAI,CAAC,CAAC,OAAO,IAAI,WAAW,EAAE,CAAC;QAEzE,MAAM,IAAI,GAAG,MAAM,IAAA,4BAAoB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEvD,uCACK,OAAO,KACV,WAAW;YACX,WAAW;YACX,IAAI,IACJ;IACJ,CAAC;CAAA;AAED,SAAS,QAAQ,CAAC,IAAU,EAAE,OAAyB;IACrD,MAAM,eAAe,iDAChB,OAAO,GACP,OAAO,CAAC,IAAI,KACf,IAAI,EAAE,EAAE,GACT,CAAC;IACF,IAAA,sBAAa,EACX,IAAI,EACJ,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,WAAW,EACnB,eAAe,CAChB,CAAC;AACJ,CAAC;AAED,mBAA+B,IAAU,EAAE,OAAe;;QACxD,MAAM,iBAAiB,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEhE,MAAM,EAAE,oBAAoB,EAAE,WAAW,EAAE,GAAG,2CAAa,aAAa,EAAC,CAAC;QAC1E,MAAM,WAAW,CAAC,IAAI,kCACjB,OAAO,KACV,UAAU,EAAE,IAAI,EAChB,eAAe,EAAE,KAAK,EACtB,MAAM,EAAE,eAAM,CAAC,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,KAAK,EACT,SAAS,EAAE,QAAQ,OAAO,CAAC,IAAI,EAAE,IACjC,CAAC;QAEH,IAAA,qCAA4B,EAC1B,IAAI,EACJ;YACE,yBAAyB,EAAE,QAAQ;YACnC,WAAW,EAAE,QAAQ;YACrB,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,QAAQ;YACjB,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,QAAQ;YAClB,oBAAoB,EAAE,QAAQ;SAC/B,EACD;YACE,oBAAoB,EAAE,QAAQ;YAC9B,aAAa,EAAE,SAAS;YACxB,iBAAiB,EAAE,SAAS;YAC5B,2BAA2B,EAAE,QAAQ;SACtC,CACF,CAAC;QAEF,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QAClC,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;QAExB,MAAM,IAAA,2BAAmB,EAAC,IAAI,kCACzB,iBAAiB,KACpB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,iBAAiB,CAAC,WAAW,IACtC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,IAAA,4BAAmB,EAAC,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC;CAAA"}
1
+ {"version":3,"file":"express-service.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/express-service/express-service.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,8 @@ utilsMock.getAdspConfiguration.mockResolvedValue({
14
14
  accessServiceUrl: environments.test.accessServiceUrl,
15
15
  directoryServiceUrl: environments.test.directoryServiceUrl,
16
16
  });
17
+ utilsMock.deploymentGenerator.mockResolvedValue(undefined);
18
+ utilsMock.realmLogin.mockResolvedValue('test-token');
17
19
 
18
20
  describe('Express Service Generator', () => {
19
21
  const options: Schema = {
@@ -4,6 +4,10 @@ export interface Schema {
4
4
  name: string;
5
5
  env: EnvironmentName;
6
6
  accessToken?: string;
7
+ /** Keycloak realm UUID. When provided with tenant, skips interactive tenant selection. */
8
+ tenantRealm?: string;
9
+ /** ADSP tenant name (e.g. 'my-org'). Required when tenantRealm is provided. */
10
+ tenant?: string;
7
11
  }
8
12
 
9
13
  export interface NormalizedSchema extends Schema {
@@ -35,6 +35,16 @@
35
35
  "type": "string",
36
36
  "description": "Access token for retrieving configuration from ADSP APIs.",
37
37
  "alias": "at"
38
+ },
39
+ "tenantRealm": {
40
+ "type": "string",
41
+ "description": "Keycloak realm UUID. Optional when --tenant is provided \u2014 overrides the realm looked up from the tenant service.",
42
+ "alias": "tr"
43
+ },
44
+ "tenant": {
45
+ "type": "string",
46
+ "description": "ADSP tenant name. When provided, the realm is looked up anonymously and a single browser login is used instead of the full interactive flow.",
47
+ "alias": "t"
38
48
  }
39
49
  },
40
50
  "required": [
@@ -42,4 +52,4 @@
42
52
  "env"
43
53
  ],
44
54
  "additionalProperties": false
45
- }
55
+ }
@@ -14,6 +14,7 @@ utilsMock.getAdspConfiguration.mockResolvedValue({
14
14
  directoryServiceUrl: environments.test.directoryServiceUrl,
15
15
  });
16
16
  utilsMock.deploymentGenerator.mockResolvedValue(undefined);
17
+ utilsMock.realmLogin.mockResolvedValue('test-token');
17
18
 
18
19
  describe('MEAN Generator', () => {
19
20
  const options: Schema = {
@@ -14,6 +14,7 @@ utilsMock.getAdspConfiguration.mockResolvedValue({
14
14
  accessServiceUrl: environments.test.accessServiceUrl,
15
15
  directoryServiceUrl: environments.test.directoryServiceUrl,
16
16
  });
17
+ utilsMock.realmLogin.mockResolvedValue('test-token');
17
18
 
18
19
  describe('MERN Generator', () => {
19
20
  const options: Schema = {
@@ -0,0 +1,36 @@
1
+ import { Tree } from '@nx/devkit';
2
+ export interface AgentCapability {
3
+ generator: string;
4
+ params: Record<string, unknown>;
5
+ description: string;
6
+ }
7
+ export interface CapabilitySpec {
8
+ capabilities: AgentCapability[];
9
+ }
10
+ /**
11
+ * Result returned when the agent conversation completes.
12
+ * filesWritten is the number of files applied to the Nx Tree from the
13
+ * agent workspace (new files and modified integration files like main.ts).
14
+ */
15
+ export interface AgentResult {
16
+ filesWritten: number;
17
+ /** True when the user was in an active conversation before it ended. */
18
+ userInteracted: boolean;
19
+ }
20
+ /**
21
+ * Connect to the ADSP agent-service and conduct a multi-turn conversation
22
+ * with the nx-adsp-agent. The agent uses its workspace tools to write
23
+ * generated and modified files; this function retrieves the workspace state
24
+ * after the conversation and applies all files to the Nx Tree.
25
+ *
26
+ * Returns null if agent-service is unavailable — callers should skip the
27
+ * agent step gracefully in that case.
28
+ */
29
+ export declare function consultAgent(directoryServiceUrl: string, accessToken: string, projectContext: {
30
+ projectName: string;
31
+ projectType: 'express-service' | 'react-app' | 'angular-app';
32
+ tenant: string;
33
+ pluginVersion: string;
34
+ /** Content of key integration files for the agent to read and potentially modify. */
35
+ existingFiles: Record<string, string>;
36
+ }, host: Tree, projectRoot: string): Promise<AgentResult | null>;
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.consultAgent = consultAgent;
4
+ const tslib_1 = require("tslib");
5
+ const readline_1 = require("readline");
6
+ const socket_io_client_1 = require("socket.io-client");
7
+ const nx_oc_1 = require("@abgov/nx-oc");
8
+ const AGENT_SERVICE_URN = 'urn:ads:platform:agent-service:v1';
9
+ const AGENT_ID = 'nxAdspAgent';
10
+ /**
11
+ * Connect to the ADSP agent-service and conduct a multi-turn conversation
12
+ * with the nx-adsp-agent. The agent uses its workspace tools to write
13
+ * generated and modified files; this function retrieves the workspace state
14
+ * after the conversation and applies all files to the Nx Tree.
15
+ *
16
+ * Returns null if agent-service is unavailable — callers should skip the
17
+ * agent step gracefully in that case.
18
+ */
19
+ function consultAgent(directoryServiceUrl, accessToken, projectContext, host, projectRoot) {
20
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
21
+ if (!accessToken) {
22
+ process.stdout.write('\n[nx-adsp] No access token — skipping agent interaction.\n');
23
+ return null;
24
+ }
25
+ const agentServiceUrl = yield resolveAgentServiceUrl(directoryServiceUrl);
26
+ if (!agentServiceUrl) {
27
+ process.stdout.write('\n[nx-adsp] Agent-service not found in directory — skipping agent interaction.\n');
28
+ return null;
29
+ }
30
+ process.stdout.write(`\n[nx-adsp] Connecting to agent at ${agentServiceUrl}...\n`);
31
+ return new Promise((resolve) => {
32
+ const socket = (0, socket_io_client_1.io)(agentServiceUrl, {
33
+ auth: { token: accessToken },
34
+ // Skip polling — go directly to WebSocket to avoid ARO ingress rejecting
35
+ // the polling POST with HTTP 400.
36
+ transports: ['websocket'],
37
+ timeout: 30000,
38
+ reconnection: false,
39
+ });
40
+ const rl = (0, readline_1.createInterface)({ input: process.stdin, output: process.stdout });
41
+ const threadId = crypto.randomUUID();
42
+ let buffer = '';
43
+ let conversationDone = false;
44
+ let agentHasResponded = false;
45
+ let interrupted = false;
46
+ // Ctrl+C — abort the interaction without applying generated files.
47
+ rl.on('SIGINT', () => {
48
+ interrupted = true;
49
+ process.stdout.write('\n');
50
+ cleanup(0);
51
+ });
52
+ // Ctrl+D / stdin EOF — intentional finish: fetch workspace and apply files.
53
+ rl.on('close', () => {
54
+ if (!conversationDone && !interrupted) {
55
+ requestWorkspaceState();
56
+ }
57
+ });
58
+ const buildInitialMessage = () => {
59
+ const fileNames = Object.keys(projectContext.existingFiles).join(', ');
60
+ return (`I am setting up a new ${projectContext.projectType} called "${projectContext.projectName}" ` +
61
+ `for ADSP tenant "${projectContext.tenant}" (nx-adsp plugin version ${projectContext.pluginVersion}). ` +
62
+ `The project files (${fileNames}) have been uploaded to your workspace. ` +
63
+ `What ADSP capabilities would be useful to integrate into this service?`);
64
+ };
65
+ const sendMessage = (content) => {
66
+ socket.emit('message', {
67
+ agent: AGENT_ID,
68
+ threadId,
69
+ content,
70
+ rawChunks: true,
71
+ });
72
+ };
73
+ const requestWorkspaceState = () => {
74
+ socket.emit('workspace-read', { agent: AGENT_ID, threadId });
75
+ };
76
+ const uploadFilesToWorkspace = () => {
77
+ socket.emit('workspace-update', {
78
+ agent: AGENT_ID,
79
+ threadId,
80
+ writes: Object.entries(projectContext.existingFiles).map(([path, content]) => ({
81
+ path,
82
+ content,
83
+ })),
84
+ deletes: [],
85
+ });
86
+ };
87
+ const promptUser = () => {
88
+ rl.question('\n> ', (input) => {
89
+ const trimmed = input.trim();
90
+ if (trimmed) {
91
+ sendMessage(trimmed);
92
+ }
93
+ else {
94
+ // Empty input — apply whatever the agent has generated.
95
+ requestWorkspaceState();
96
+ }
97
+ });
98
+ };
99
+ const applyWorkspaceFiles = (files) => {
100
+ let count = 0;
101
+ for (const file of files) {
102
+ // Skip files we uploaded that the agent hasn't modified — only apply
103
+ // new files and agent-modified versions of existing files.
104
+ const original = projectContext.existingFiles[file.path];
105
+ if (original !== undefined && original === file.content) {
106
+ continue;
107
+ }
108
+ const fullPath = `${projectRoot}/${file.path}`;
109
+ host.write(fullPath, file.content);
110
+ count++;
111
+ }
112
+ return count;
113
+ };
114
+ const cleanup = (filesWritten) => {
115
+ conversationDone = true;
116
+ rl.close();
117
+ socket.disconnect();
118
+ // Return null for silent skips (no agent, no token, connection failed).
119
+ // Return a result with userInteracted:true when the user was in a
120
+ // conversation so the caller can ask whether to proceed.
121
+ resolve(agentHasResponded ? { filesWritten, userInteracted: true } : null);
122
+ };
123
+ socket.on('workspace-updated', () => {
124
+ process.stdout.write('[nx-adsp] Project files uploaded to workspace.\n');
125
+ process.stdout.write('[nx-adsp] Type your replies at the > prompt. Press Ctrl+D or leave blank to apply generated files.\n\n');
126
+ sendMessage(buildInitialMessage());
127
+ });
128
+ socket.on('connect', () => {
129
+ process.stdout.write('[nx-adsp] Connected to agent-service.\n');
130
+ process.stdout.write('[nx-adsp] Uploading project files to workspace...\n');
131
+ uploadFilesToWorkspace();
132
+ // Show periodic dots while waiting for first response, then a warning at 2 minutes.
133
+ const dotInterval = setInterval(() => {
134
+ if (!conversationDone && !agentHasResponded)
135
+ process.stdout.write('.');
136
+ else
137
+ clearInterval(dotInterval);
138
+ }, 3000);
139
+ setTimeout(() => {
140
+ clearInterval(dotInterval);
141
+ if (!conversationDone && !agentHasResponded) {
142
+ process.stdout.write('\n[nx-adsp] No response after 2 minutes. ' +
143
+ 'The nxAdspAgent may still be deploying or the LLM is unresponsive. ' +
144
+ 'Press Ctrl+C to skip and continue generation.\n');
145
+ }
146
+ }, 120000);
147
+ });
148
+ socket.on('stream', ({ chunk, done }) => {
149
+ var _a, _b;
150
+ if ((chunk === null || chunk === void 0 ? void 0 : chunk.type) === 'text-delta') {
151
+ const text = (_b = (_a = chunk.payload) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : '';
152
+ buffer += text;
153
+ agentHasResponded = true;
154
+ process.stdout.write(text);
155
+ }
156
+ if (done) {
157
+ if (buffer.length > 0 && !buffer.endsWith('\n')) {
158
+ process.stdout.write('\n');
159
+ }
160
+ buffer = '';
161
+ // Agent finished its turn — prompt the user to continue the conversation
162
+ // or press Enter to apply whatever the agent has written so far.
163
+ promptUser();
164
+ }
165
+ });
166
+ socket.on('workspace-state', ({ files }) => {
167
+ // Only apply files the agent added or modified — not unchanged uploaded files.
168
+ const written = applyWorkspaceFiles(files !== null && files !== void 0 ? files : []);
169
+ if (written > 0) {
170
+ process.stdout.write(`\nApplied ${written} file(s) from agent workspace.\n`);
171
+ }
172
+ else {
173
+ process.stdout.write('\nNo files generated by agent.\n');
174
+ }
175
+ cleanup(written);
176
+ });
177
+ socket.on('session-expired', () => {
178
+ process.stdout.write('\nAgent session expired.\n');
179
+ requestWorkspaceState();
180
+ });
181
+ socket.on('connect_error', (err) => {
182
+ var _a, _b, _c, _d;
183
+ // Log the full error object so we can diagnose the root cause.
184
+ const errAny = err;
185
+ process.stdout.write(`\n[nx-adsp] Connection failed: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}\n`);
186
+ process.stdout.write(`[nx-adsp] Error detail: ${JSON.stringify((_d = (_c = (_b = errAny === null || errAny === void 0 ? void 0 : errAny.description) !== null && _b !== void 0 ? _b : errAny === null || errAny === void 0 ? void 0 : errAny.context) !== null && _c !== void 0 ? _c : errAny === null || errAny === void 0 ? void 0 : errAny.cause) !== null && _d !== void 0 ? _d : 'none')}\n`);
187
+ cleanup(0);
188
+ });
189
+ socket.on('error', (err) => {
190
+ process.stdout.write(`\n[nx-adsp] Agent error: ${JSON.stringify(err)}\n`);
191
+ requestWorkspaceState();
192
+ });
193
+ });
194
+ });
195
+ }
196
+ function resolveAgentServiceUrl(directoryServiceUrl) {
197
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
198
+ try {
199
+ const urls = yield (0, nx_oc_1.getServiceUrls)(directoryServiceUrl);
200
+ const apiUrl = urls[AGENT_SERVICE_URN];
201
+ if (!apiUrl)
202
+ return null;
203
+ // The directory URL includes the REST API path (e.g. /agent/v1).
204
+ // Socket.io attaches at the server root, so use only the origin.
205
+ return new URL(apiUrl).origin;
206
+ }
207
+ catch (_a) {
208
+ return null;
209
+ }
210
+ });
211
+ }
212
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../../../../packages/nx-adsp/src/utils/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ import { consultAgent } from './agent';
2
+
3
+ jest.mock('readline', () => ({
4
+ createInterface: jest.fn(() => ({
5
+ question: jest.fn((_prompt: string, cb: (answer: string) => void) => cb('')),
6
+ close: jest.fn(),
7
+ on: jest.fn(),
8
+ })),
9
+ }));
10
+
11
+ jest.mock('socket.io-client');
12
+ jest.mock('@abgov/nx-oc', () => ({ getServiceUrls: jest.fn() }));
13
+ jest.mock('@nx/devkit', () => ({ Tree: jest.fn() }));
14
+
15
+ import { io } from 'socket.io-client';
16
+ import { getServiceUrls } from '@abgov/nx-oc';
17
+
18
+ const mockedIo = jest.mocked(io);
19
+ const mockedGetServiceUrls = jest.mocked(getServiceUrls);
20
+
21
+ const PROJECT_CONTEXT = {
22
+ projectName: 'test-service',
23
+ projectType: 'express-service' as const,
24
+ tenant: 'test-tenant',
25
+ pluginVersion: '12.x',
26
+ existingFiles: {
27
+ 'src/main.ts': 'const app = express();',
28
+ 'src/environment.ts': 'export const environment = {};',
29
+ },
30
+ };
31
+
32
+ const mockHost = { write: jest.fn(), read: jest.fn() } as unknown as import('@nx/devkit').Tree;
33
+
34
+ function makeMockSocket() {
35
+ const handlers: Record<string, (...args: unknown[]) => void> = {};
36
+ const socket = {
37
+ on: jest.fn((event: string, handler: (...args: unknown[]) => void) => {
38
+ handlers[event] = handler;
39
+ }),
40
+ emit: jest.fn(),
41
+ disconnect: jest.fn(),
42
+ _handlers: handlers,
43
+ };
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ mockedIo.mockReturnValue(socket as any);
46
+ return socket;
47
+ }
48
+
49
+ const flushPromises = () => new Promise(resolve => setTimeout(resolve, 0));
50
+
51
+ describe('consultAgent', () => {
52
+ beforeEach(() => {
53
+ jest.clearAllMocks();
54
+ jest.spyOn(process.stdout, 'write').mockImplementation(() => true);
55
+ });
56
+
57
+ it('returns null when agent-service is not in directory', async () => {
58
+ mockedGetServiceUrls.mockResolvedValue({});
59
+ const result = await consultAgent('https://directory.example.com', 'token', PROJECT_CONTEXT, mockHost, 'apps/test-service');
60
+ expect(result).toBeNull();
61
+ });
62
+
63
+ it('returns null on socket connect error', async () => {
64
+ mockedGetServiceUrls.mockResolvedValue({
65
+ 'urn:ads:platform:agent-service:v1': 'https://agent.example.com',
66
+ });
67
+ const socket = makeMockSocket();
68
+ const resultPromise = consultAgent('https://directory.example.com', 'token', PROJECT_CONTEXT, mockHost, 'apps/test-service');
69
+ await flushPromises();
70
+ socket._handlers['connect_error']?.();
71
+ expect(await resultPromise).toBeNull();
72
+ });
73
+
74
+ it('applies workspace files to Nx Tree and returns filesWritten count', async () => {
75
+ mockedGetServiceUrls.mockResolvedValue({
76
+ 'urn:ads:platform:agent-service:v1': 'https://agent.example.com',
77
+ });
78
+ const socket = makeMockSocket();
79
+ const resultPromise = consultAgent('https://directory.example.com', 'token', PROJECT_CONTEXT, mockHost, 'apps/test-service');
80
+ await flushPromises();
81
+
82
+ socket._handlers['connect']?.();
83
+ socket._handlers['workspace-updated']?.();
84
+ // Simulate agent responding with text (sets agentHasResponded = true)
85
+ socket._handlers['stream']?.({ chunk: { type: 'text-delta', payload: { text: 'I will add events.' } }, done: false });
86
+ socket._handlers['stream']?.({ chunk: null, done: true });
87
+ socket._handlers['workspace-state']?.({
88
+ files: [
89
+ { path: 'src/roles.ts', content: 'export enum ServiceRoles {}' },
90
+ { path: 'src/main.ts', content: 'updated main.ts' },
91
+ ],
92
+ });
93
+
94
+ const result = await resultPromise;
95
+ expect(result).toEqual({ filesWritten: 2, userInteracted: true });
96
+ expect(mockHost.write).toHaveBeenCalledWith('apps/test-service/src/roles.ts', 'export enum ServiceRoles {}');
97
+ expect(mockHost.write).toHaveBeenCalledWith('apps/test-service/src/main.ts', 'updated main.ts');
98
+ });
99
+
100
+ it('uploads existing files to workspace before sending the initial message', async () => {
101
+ mockedGetServiceUrls.mockResolvedValue({
102
+ 'urn:ads:platform:agent-service:v1': 'https://agent.example.com',
103
+ });
104
+ const socket = makeMockSocket();
105
+ const resultPromise = consultAgent('https://directory.example.com', 'token', PROJECT_CONTEXT, mockHost, 'apps/test-service');
106
+ await flushPromises();
107
+
108
+ // connect → workspace-update emitted, then workspace-updated → message emitted
109
+ socket._handlers['connect']?.();
110
+ socket._handlers['workspace-updated']?.();
111
+ socket._handlers['stream']?.({ chunk: { type: 'text-delta', payload: { text: 'What does this service do?' } }, done: false });
112
+ socket._handlers['stream']?.({ chunk: null, done: true });
113
+ socket._handlers['workspace-state']?.({ files: [] });
114
+ await resultPromise;
115
+
116
+ expect(socket.emit).toHaveBeenCalledWith(
117
+ 'workspace-update',
118
+ expect.objectContaining({
119
+ agent: 'nxAdspAgent',
120
+ writes: expect.arrayContaining([
121
+ expect.objectContaining({ path: 'src/main.ts' }),
122
+ ]),
123
+ })
124
+ );
125
+ expect(socket.emit).toHaveBeenCalledWith(
126
+ 'message',
127
+ expect.objectContaining({
128
+ agent: 'nxAdspAgent',
129
+ content: expect.stringContaining('src/main.ts'),
130
+ })
131
+ );
132
+ });
133
+ });