@uipath/coder-tool 1.199.0-preview.116

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.
@@ -0,0 +1,296 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ vi.mock("@uipath/auth", () => ({
4
+ getLoginStatusAsync: vi.fn(),
5
+ }));
6
+
7
+ vi.mock("@uipath/common", async (importOriginal) => {
8
+ const actual = await importOriginal<typeof import("@uipath/common")>();
9
+ return {
10
+ ...actual,
11
+ logger: { debug: vi.fn(), warn: vi.fn(), error: vi.fn() },
12
+ };
13
+ });
14
+
15
+ import { getLoginStatusAsync } from "@uipath/auth";
16
+ import { createUiPathProviderExtension, UIPATH_PROVIDER_ID } from "./uipath";
17
+
18
+ type EventHandler = (event: unknown) => Promise<unknown>;
19
+
20
+ function buildFakePi() {
21
+ const handlers = new Map<string, EventHandler>();
22
+ return {
23
+ registerProvider: vi.fn(),
24
+ on: vi.fn((event: string, handler: EventHandler) => {
25
+ handlers.set(event, handler);
26
+ }),
27
+ handlers,
28
+ };
29
+ }
30
+
31
+ function loggedInStatus(expiresInMs: number) {
32
+ return {
33
+ loginStatus: "Logged in" as const,
34
+ accessToken: "header.payload.signature",
35
+ baseUrl: "https://cloud.uipath.com",
36
+ organizationId: "org-id",
37
+ organizationName: "myorg",
38
+ tenantName: "mytenant",
39
+ expiration: new Date(Date.now() + expiresInMs),
40
+ };
41
+ }
42
+
43
+ describe("createUiPathProviderExtension", () => {
44
+ beforeEach(() => {
45
+ vi.resetAllMocks();
46
+ vi.unstubAllGlobals();
47
+ });
48
+
49
+ it("skips registration when not logged in", async () => {
50
+ vi.mocked(getLoginStatusAsync).mockResolvedValue({
51
+ loginStatus: "Not logged in",
52
+ });
53
+ const pi = buildFakePi();
54
+
55
+ await createUiPathProviderExtension().factory(pi as never);
56
+
57
+ expect(pi.registerProvider).not.toHaveBeenCalled();
58
+ expect(pi.on).not.toHaveBeenCalled();
59
+ });
60
+
61
+ it("registers discovered models against the agenthub_ gateway route", async () => {
62
+ vi.mocked(getLoginStatusAsync).mockResolvedValue(
63
+ loggedInStatus(60 * 60 * 1000),
64
+ );
65
+ vi.stubGlobal(
66
+ "fetch",
67
+ vi.fn().mockResolvedValue({
68
+ ok: true,
69
+ json: () =>
70
+ Promise.resolve({
71
+ supportedModels: [
72
+ {
73
+ model: "gpt-4o-2024-11-20",
74
+ contextWindow: 128000,
75
+ },
76
+ "gpt-4o-mini-2024-07-18",
77
+ ],
78
+ }),
79
+ }),
80
+ );
81
+ const pi = buildFakePi();
82
+
83
+ await createUiPathProviderExtension().factory(pi as never);
84
+
85
+ expect(pi.registerProvider).toHaveBeenCalledWith(
86
+ UIPATH_PROVIDER_ID,
87
+ expect.objectContaining({
88
+ name: "UiPath LLM Gateway",
89
+ api: "openai-completions",
90
+ apiKey: "header.payload.signature",
91
+ baseUrl:
92
+ "https://cloud.uipath.com/org-id/mytenant/agenthub_/llm/openai",
93
+ }),
94
+ );
95
+ const config = vi.mocked(pi.registerProvider).mock.calls[0]?.[1] as {
96
+ models: Array<{ id: string; baseUrl: string }>;
97
+ };
98
+ expect(config.models).toHaveLength(2);
99
+ expect(config.models[0]?.baseUrl).toBe(
100
+ "https://cloud.uipath.com/org-id/mytenant/agenthub_/llm/openai/deployments/gpt-4o-2024-11-20",
101
+ );
102
+ });
103
+
104
+ it("accepts a bare-array discovery response", async () => {
105
+ vi.mocked(getLoginStatusAsync).mockResolvedValue(
106
+ loggedInStatus(60 * 60 * 1000),
107
+ );
108
+ vi.stubGlobal(
109
+ "fetch",
110
+ vi.fn().mockResolvedValue({
111
+ ok: true,
112
+ json: () => Promise.resolve(["gpt-4o-2024-11-20"]),
113
+ }),
114
+ );
115
+ const pi = buildFakePi();
116
+
117
+ await createUiPathProviderExtension().factory(pi as never);
118
+
119
+ const config = vi.mocked(pi.registerProvider).mock.calls[0]?.[1] as {
120
+ models: Array<{ id: string }>;
121
+ };
122
+ expect(config.models.map((m) => m.id)).toEqual(["gpt-4o-2024-11-20"]);
123
+ });
124
+
125
+ it("falls back to default models when discovery returns null", async () => {
126
+ vi.mocked(getLoginStatusAsync).mockResolvedValue(
127
+ loggedInStatus(60 * 60 * 1000),
128
+ );
129
+ vi.stubGlobal(
130
+ "fetch",
131
+ vi.fn().mockResolvedValue({
132
+ ok: true,
133
+ json: () => Promise.resolve(null),
134
+ }),
135
+ );
136
+ const pi = buildFakePi();
137
+
138
+ await createUiPathProviderExtension().factory(pi as never);
139
+
140
+ const config = vi.mocked(pi.registerProvider).mock.calls[0]?.[1] as {
141
+ models: Array<{ id: string }>;
142
+ };
143
+ expect(config.models.length).toBeGreaterThan(0);
144
+ });
145
+
146
+ it("passes a timeout signal to the discovery request", async () => {
147
+ vi.mocked(getLoginStatusAsync).mockResolvedValue(
148
+ loggedInStatus(60 * 60 * 1000),
149
+ );
150
+ const fetchMock = vi.fn().mockResolvedValue({ ok: false, status: 404 });
151
+ vi.stubGlobal("fetch", fetchMock);
152
+ const pi = buildFakePi();
153
+
154
+ await createUiPathProviderExtension().factory(pi as never);
155
+
156
+ const init = fetchMock.mock.calls[0]?.[1] as { signal?: AbortSignal };
157
+ expect(init.signal).toBeInstanceOf(AbortSignal);
158
+ });
159
+
160
+ it("falls back to default models when discovery fails", async () => {
161
+ vi.mocked(getLoginStatusAsync).mockResolvedValue(
162
+ loggedInStatus(60 * 60 * 1000),
163
+ );
164
+ vi.stubGlobal(
165
+ "fetch",
166
+ vi.fn().mockResolvedValue({ ok: false, status: 404 }),
167
+ );
168
+ const pi = buildFakePi();
169
+
170
+ await createUiPathProviderExtension().factory(pi as never);
171
+
172
+ const config = vi.mocked(pi.registerProvider).mock.calls[0]?.[1] as {
173
+ models: Array<{ id: string }>;
174
+ };
175
+ expect(config.models.length).toBeGreaterThan(0);
176
+ });
177
+
178
+ it("encodes model ids when building deployment routes", async () => {
179
+ vi.mocked(getLoginStatusAsync).mockResolvedValue(
180
+ loggedInStatus(60 * 60 * 1000),
181
+ );
182
+ vi.stubGlobal(
183
+ "fetch",
184
+ vi.fn().mockResolvedValue({
185
+ ok: true,
186
+ json: () =>
187
+ Promise.resolve({ supportedModels: ["openai/gpt-4o"] }),
188
+ }),
189
+ );
190
+ const pi = buildFakePi();
191
+
192
+ await createUiPathProviderExtension().factory(pi as never);
193
+
194
+ const config = vi.mocked(pi.registerProvider).mock.calls[0]?.[1] as {
195
+ models: Array<{ id: string; baseUrl: string }>;
196
+ };
197
+ expect(config.models[0]?.id).toBe("openai/gpt-4o");
198
+ expect(config.models[0]?.baseUrl).toBe(
199
+ "https://cloud.uipath.com/org-id/mytenant/agenthub_/llm/openai/deployments/openai%2Fgpt-4o",
200
+ );
201
+ });
202
+
203
+ it("skips registration when resolving the login status fails", async () => {
204
+ vi.mocked(getLoginStatusAsync).mockRejectedValue(
205
+ new Error("auth file unreadable"),
206
+ );
207
+ const pi = buildFakePi();
208
+
209
+ await createUiPathProviderExtension().factory(pi as never);
210
+
211
+ expect(pi.registerProvider).not.toHaveBeenCalled();
212
+ });
213
+
214
+ it("falls back to default models when discovery returns an unusable shape", async () => {
215
+ vi.mocked(getLoginStatusAsync).mockResolvedValue(
216
+ loggedInStatus(60 * 60 * 1000),
217
+ );
218
+ vi.stubGlobal(
219
+ "fetch",
220
+ vi.fn().mockResolvedValue({
221
+ ok: true,
222
+ json: () => Promise.resolve({ unexpected: true }),
223
+ }),
224
+ );
225
+ const pi = buildFakePi();
226
+
227
+ await createUiPathProviderExtension().factory(pi as never);
228
+
229
+ const config = vi.mocked(pi.registerProvider).mock.calls[0]?.[1] as {
230
+ models: Array<{ id: string }>;
231
+ };
232
+ expect(config.models.length).toBeGreaterThan(0);
233
+ });
234
+
235
+ it("uses organizationName in the gateway path when organizationId is missing", async () => {
236
+ const status = loggedInStatus(60 * 60 * 1000);
237
+ vi.mocked(getLoginStatusAsync).mockResolvedValue({
238
+ ...status,
239
+ organizationId: undefined,
240
+ });
241
+ vi.stubGlobal(
242
+ "fetch",
243
+ vi.fn().mockResolvedValue({ ok: false, status: 404 }),
244
+ );
245
+ const pi = buildFakePi();
246
+
247
+ await createUiPathProviderExtension().factory(pi as never);
248
+
249
+ const config = vi.mocked(pi.registerProvider).mock.calls[0]?.[1] as {
250
+ baseUrl: string;
251
+ };
252
+ expect(config.baseUrl).toBe(
253
+ "https://cloud.uipath.com/myorg/mytenant/agenthub_/llm/openai",
254
+ );
255
+ });
256
+
257
+ it("does not re-register on agent_start while the token is fresh", async () => {
258
+ vi.mocked(getLoginStatusAsync).mockResolvedValue(
259
+ loggedInStatus(60 * 60 * 1000),
260
+ );
261
+ vi.stubGlobal(
262
+ "fetch",
263
+ vi.fn().mockResolvedValue({ ok: false, status: 404 }),
264
+ );
265
+ const pi = buildFakePi();
266
+
267
+ await createUiPathProviderExtension().factory(pi as never);
268
+ expect(pi.registerProvider).toHaveBeenCalledTimes(1);
269
+
270
+ await pi.handlers.get("agent_start")?.({ type: "agent_start" });
271
+
272
+ expect(pi.registerProvider).toHaveBeenCalledTimes(1);
273
+ expect(getLoginStatusAsync).toHaveBeenCalledTimes(1);
274
+ });
275
+
276
+ it("re-registers with a fresh token on agent_start when close to expiry", async () => {
277
+ vi.mocked(getLoginStatusAsync).mockResolvedValue(
278
+ loggedInStatus(60 * 1000),
279
+ );
280
+ vi.stubGlobal(
281
+ "fetch",
282
+ vi.fn().mockResolvedValue({ ok: false, status: 404 }),
283
+ );
284
+ const pi = buildFakePi();
285
+
286
+ await createUiPathProviderExtension().factory(pi as never);
287
+ expect(pi.registerProvider).toHaveBeenCalledTimes(1);
288
+
289
+ const handler = pi.handlers.get("agent_start");
290
+ expect(handler).toBeDefined();
291
+ await handler?.({ type: "agent_start" });
292
+
293
+ expect(pi.registerProvider).toHaveBeenCalledTimes(2);
294
+ expect(getLoginStatusAsync).toHaveBeenCalledTimes(2);
295
+ });
296
+ });
@@ -0,0 +1,195 @@
1
+ import type {
2
+ ExtensionAPI,
3
+ ProviderModelConfig,
4
+ } from "@earendil-works/pi-coding-agent";
5
+ import { getLoginStatusAsync, type LoginStatus } from "@uipath/auth";
6
+ import { catchError, logger } from "@uipath/common";
7
+
8
+ export const UIPATH_PROVIDER_ID = "uipath";
9
+
10
+ const TOKEN_VALIDITY_MINUTES = 30;
11
+ const REFRESH_THRESHOLD_MS = 5 * 60 * 1000;
12
+ const DEFAULT_CONTEXT_WINDOW = 128000;
13
+ const DEFAULT_MAX_TOKENS = 16384;
14
+ // Pi waits for extension factories before startup, so a stalled gateway
15
+ // must not block `uip coder` — cap discovery and fall back to the curated list.
16
+ const MODEL_DISCOVERY_TIMEOUT_MS = 5000;
17
+
18
+ /** Used when the LLM Gateway model discovery endpoint is unreachable. */
19
+ const FALLBACK_MODEL_IDS = ["gpt-4o-2024-11-20", "gpt-4o-mini-2024-07-18"];
20
+
21
+ interface DiscoveredModel {
22
+ id: string;
23
+ contextWindow?: number;
24
+ maxTokens?: number;
25
+ }
26
+
27
+ interface ReadyLoginStatus {
28
+ accessToken: string;
29
+ gatewayBase: string;
30
+ expiration?: Date;
31
+ }
32
+
33
+ const asReadyStatus = (status: LoginStatus): ReadyLoginStatus | undefined => {
34
+ // Same org/tenant path segments as @uipath/agenthub-sdk's client factory.
35
+ const organization = status.organizationId ?? status.organizationName;
36
+ const tenant = status.tenantName ?? status.tenantId;
37
+ if (
38
+ status.loginStatus !== "Logged in" ||
39
+ !status.accessToken ||
40
+ !status.baseUrl ||
41
+ !organization ||
42
+ !tenant
43
+ ) {
44
+ return undefined;
45
+ }
46
+ return {
47
+ accessToken: status.accessToken,
48
+ gatewayBase: `${status.baseUrl}/${organization}/${tenant}/agenthub_/llm`,
49
+ expiration: status.expiration,
50
+ };
51
+ };
52
+
53
+ /**
54
+ * Model discovery via the gateway-normalized endpoint. The response shape is
55
+ * tolerated loosely (array of ids or objects) since the contract is evolving.
56
+ */
57
+ const discoverModels = async (
58
+ ready: ReadyLoginStatus,
59
+ ): Promise<DiscoveredModel[] | undefined> => {
60
+ const [error, response] = await catchError(
61
+ fetch(`${ready.gatewayBase}/api/chat/completions`, {
62
+ headers: { Authorization: `Bearer ${ready.accessToken}` },
63
+ signal: AbortSignal.timeout(MODEL_DISCOVERY_TIMEOUT_MS),
64
+ }),
65
+ );
66
+ if (error || !response.ok) {
67
+ logger.debug(
68
+ `[coder-tool] LLM Gateway model discovery failed: ${error?.message ?? response?.status}`,
69
+ );
70
+ return undefined;
71
+ }
72
+ const [parseError, parsed] = await catchError(
73
+ response.json() as Promise<unknown>,
74
+ );
75
+ if (parseError) {
76
+ return undefined;
77
+ }
78
+ const body =
79
+ parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
80
+ ? (parsed as Record<string, unknown>)
81
+ : undefined;
82
+ const rawModels = Array.isArray(parsed)
83
+ ? parsed
84
+ : (body?.supportedModels ?? body?.models ?? body?.data);
85
+ if (!Array.isArray(rawModels)) {
86
+ return undefined;
87
+ }
88
+ const models = rawModels
89
+ .map((entry: unknown): DiscoveredModel | undefined => {
90
+ if (typeof entry === "string") {
91
+ return { id: entry };
92
+ }
93
+ if (entry && typeof entry === "object") {
94
+ const record = entry as Record<string, unknown>;
95
+ const id = record.model ?? record.name ?? record.id;
96
+ if (typeof id === "string" && id.length > 0) {
97
+ return {
98
+ id,
99
+ contextWindow:
100
+ typeof record.contextWindow === "number"
101
+ ? record.contextWindow
102
+ : undefined,
103
+ maxTokens:
104
+ typeof record.maxTokens === "number"
105
+ ? record.maxTokens
106
+ : undefined,
107
+ };
108
+ }
109
+ }
110
+ return undefined;
111
+ })
112
+ .filter((model): model is DiscoveredModel => model !== undefined);
113
+ return models.length > 0 ? models : undefined;
114
+ };
115
+
116
+ const toProviderModels = (
117
+ ready: ReadyLoginStatus,
118
+ models: DiscoveredModel[],
119
+ ): ProviderModelConfig[] =>
120
+ models.map((model) => ({
121
+ id: model.id,
122
+ name: `${model.id} (UiPath)`,
123
+ // Azure-style route: the OpenAI client appends /chat/completions.
124
+ // The id is encoded so provider-qualified ids (e.g. "openai/gpt-4o")
125
+ // stay a single path segment.
126
+ baseUrl: `${ready.gatewayBase}/openai/deployments/${encodeURIComponent(model.id)}`,
127
+ reasoning: false,
128
+ input: ["text"],
129
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
130
+ contextWindow: model.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
131
+ maxTokens: model.maxTokens ?? DEFAULT_MAX_TOKENS,
132
+ }));
133
+
134
+ /**
135
+ * Registers the "uipath" provider backed by the LLM Gateway front door
136
+ * (agenthub_). Skipped silently when no `uip login` session exists so the
137
+ * other providers keep working.
138
+ */
139
+ export const createUiPathProviderExtension = () => {
140
+ let cachedModels: DiscoveredModel[] | undefined;
141
+ let tokenExpiration: Date | undefined;
142
+
143
+ const register = async (pi: ExtensionAPI): Promise<boolean> => {
144
+ const [error, status] = await catchError(
145
+ getLoginStatusAsync({
146
+ ensureTokenValidityMinutes: TOKEN_VALIDITY_MINUTES,
147
+ }),
148
+ );
149
+ if (error) {
150
+ logger.debug(
151
+ `[coder-tool] Could not resolve UiPath login: ${error.message}`,
152
+ );
153
+ return false;
154
+ }
155
+ const ready = asReadyStatus(status);
156
+ if (!ready) {
157
+ logger.debug(
158
+ "[coder-tool] Not logged in to UiPath — 'uipath' provider not registered. Run 'uip login' to enable it.",
159
+ );
160
+ return false;
161
+ }
162
+ tokenExpiration = ready.expiration;
163
+ cachedModels ??= await discoverModels(ready);
164
+ const models = cachedModels ?? FALLBACK_MODEL_IDS.map((id) => ({ id }));
165
+ pi.registerProvider(UIPATH_PROVIDER_ID, {
166
+ name: "UiPath LLM Gateway",
167
+ baseUrl: `${ready.gatewayBase}/openai`,
168
+ apiKey: ready.accessToken,
169
+ api: "openai-completions",
170
+ models: toProviderModels(ready, models),
171
+ });
172
+ return true;
173
+ };
174
+
175
+ return {
176
+ name: "uipath-llm-gateway",
177
+ factory: async (pi: ExtensionAPI): Promise<void> => {
178
+ const registered = await register(pi);
179
+ if (!registered) {
180
+ return;
181
+ }
182
+ // UiPath access tokens are short-lived; re-register with a fresh
183
+ // token when the current one is close to expiry.
184
+ pi.on("agent_start", async () => {
185
+ const expiresSoon =
186
+ tokenExpiration === undefined ||
187
+ tokenExpiration.getTime() - Date.now() <
188
+ REFRESH_THRESHOLD_MS;
189
+ if (expiresSoon) {
190
+ await register(pi);
191
+ }
192
+ });
193
+ },
194
+ };
195
+ };
@@ -0,0 +1,38 @@
1
+ import { setPreviewBuild } from "@uipath/common";
2
+ import { Command } from "commander";
3
+ import { afterEach, describe, expect, it } from "vitest";
4
+ import { metadata, registerCommands } from "./tool";
5
+
6
+ describe("coder-tool", () => {
7
+ afterEach(() => {
8
+ setPreviewBuild(false);
9
+ });
10
+
11
+ it("exposes the expected metadata", () => {
12
+ expect(metadata.name).toBe("coder-tool");
13
+ expect(metadata.commandPrefix).toBe("coder");
14
+ expect(metadata.version).toMatch(/^\d+\.\d+\.\d+/);
15
+ });
16
+
17
+ it("registers a passthrough action on preview builds", async () => {
18
+ setPreviewBuild(true);
19
+ const program = new Command();
20
+ program.name("coder").exitOverride();
21
+ await registerCommands(program);
22
+
23
+ // The action lives on the tool command itself so `uip coder [args...]`
24
+ // forwards everything to Pi.
25
+ const usage = program.usage();
26
+ expect(usage).toContain("[args...]");
27
+ });
28
+
29
+ it("registers nothing on stable builds", async () => {
30
+ setPreviewBuild(false);
31
+ const program = new Command();
32
+ program.name("coder").exitOverride();
33
+ await registerCommands(program);
34
+
35
+ expect(program.usage()).not.toContain("[args...]");
36
+ expect(program.registeredArguments).toHaveLength(0);
37
+ });
38
+ });
package/src/tool.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { previewOnly } from "@uipath/common";
2
+ import type { Command } from "commander";
3
+ import pkg from "../package.json" with { type: "json" };
4
+ import { registerChatCommand } from "./commands/chat";
5
+
6
+ export const metadata = {
7
+ name: "coder-tool",
8
+ version: pkg.version,
9
+ description:
10
+ "Build UiPath automations with an AI agent (Pi) that drives uip — via UiPath LLM Gateway or your own model keys.",
11
+ commandPrefix: "coder",
12
+ };
13
+
14
+ export const registerCommands = async (program: Command): Promise<void> => {
15
+ // Preview builds only until the tool-permission posture for an
16
+ // autonomous agent in customer environments is settled (PR #3011
17
+ // review): Pi executes tool calls without per-call approval, so the
18
+ // command stays off stable builds for now.
19
+ previewOnly(() => registerChatCommand(program));
20
+ };
@@ -0,0 +1,38 @@
1
+ # UiPath CLI (`uip`) — agent guidance
2
+
3
+ You are running inside `uip coder`, the UiPath automation-building agent of the UiPath CLI. Your purpose is helping users build, ship, and operate **UiPath automations** — agent projects, processes, Orchestrator resources, platform data — not general-purpose software development. When a request is unrelated to UiPath work, help briefly but steer toward the automation task at hand.
4
+
5
+ The `uip` command line tool is available in this environment and is your primary way to work with the UiPath platform: build and run automations, manage agents and processes, and query platform resources. Prefer `uip` over raw REST calls — it handles auth, tenant routing, and pagination for you.
6
+
7
+ ## Ground rules
8
+
9
+ - Discover before guessing: `uip --help` lists tools; `uip <tool> --help` and `uip <tool> <command> --help` list commands and options. Command surfaces evolve — trust `--help` over memory.
10
+ - Every command prints a JSON envelope: `{"Result": "Success", "Code": ..., "Data": ...}` on success (exit 0) or `{"Result": "Failure" | "AuthenticationError" | "ValidationError" | ..., "Message": ..., "Instructions": ...}` on error (exit 1-4). Parse `Data` for results; `Instructions` tells you how to recover.
11
+ - Check auth first when platform calls fail: `uip login status`. If expired or missing, ask the user to run `uip login` — never handle credentials yourself.
12
+ - Commands run against the logged-in org/tenant. `uip login status` shows which one.
13
+ - Be careful with destructive commands (delete, undeploy, job stop). Confirm with the user before running them against shared tenants.
14
+
15
+ ## Common tools (run `uip <tool> --help` for the full surface)
16
+
17
+ | Tool | Purpose |
18
+ | --- | --- |
19
+ | `uip agent` | UiPath Agents: init, pack, publish, deploy, run, evaluate agent projects |
20
+ | `uip maestro` | Maestro process orchestration: BPMN, flows, deployments, instances |
21
+ | `uip or` | Orchestrator: folders, assets, queues, processes, jobs, buckets |
22
+ | `uip df` | Data Fabric: entities, records, queries, CSV import |
23
+ | `uip is` | Integration Service: connections, connectors, triggers |
24
+ | `uip context-grounding` | Context Grounding (ECS) indexes and semantic search |
25
+ | `uip solution` | Solution packages: init, publish, resync |
26
+ | `uip tm` | Test Manager: projects, test cases, requirements |
27
+ | `uip tasks` | Action Center tasks |
28
+ | `uip platform` | Cross-platform resources (orgs, tenants, services) |
29
+
30
+ Not every tool is installed by default — a missing tool auto-installs on first use, or run `uip tools install <name>`.
31
+
32
+ ## Typical automation workflows
33
+
34
+ - Build and ship an agent: `uip agent init` → edit → `uip agent pack` → `uip agent publish` → `uip agent deploy` → `uip agent run`.
35
+ - Operate Orchestrator: `uip or jobs list --status Failed`, `uip or assets list`, `uip or queues items list`.
36
+ - Work with data: `uip df entities list`, `uip df records query <entity> --filter ...`, `uip df records import <entity> --file data.csv`.
37
+
38
+ When a task involves UiPath resources, reach for the matching `uip` tool before writing custom scripts.
@@ -0,0 +1,17 @@
1
+ // E2E coverage for scenarios/coder-tool/coding-agent.md.
2
+ //
3
+ // All scenarios need either a live LLM Gateway session (S3/S4), a BYO
4
+ // provider key (S5), or a packed-CLI harness run with network access
5
+ // (S1/S2/S6) — none of which the offline CI e2e lane provides today.
6
+ // They were verified manually against alpha (see the PR description);
7
+ // the placeholders below keep the mandatory scenario ↔ e2e mapping.
8
+ import { describe, it } from "vitest";
9
+
10
+ describe("coding-agent", () => {
11
+ it.skip("S1: forwards arguments to the pi coding agent", () => {});
12
+ it.skip("S2: lists models without a uipath provider when logged out", () => {});
13
+ it.skip("S3: registers the uipath provider when logged in", () => {});
14
+ it.skip("S4: completes a print-mode prompt through the uipath provider", () => {});
15
+ it.skip("S5: completes a print-mode prompt through a BYO provider", () => {});
16
+ it.skip("S6: reports a failure envelope when pi cannot start", () => {});
17
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "types": ["node"]
6
+ },
7
+ "include": ["src/**/*", "vitest*.ts"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }
@@ -0,0 +1 @@
1
+ export { default } from "../../vitest.base.config";