@uipath/codedagent-tool 1.199.0 → 1.201.0-preview.115
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/dist/browser-strategy-yccf8mtd.js +99 -0
- package/dist/index-2fj08e7n.js +2058 -0
- package/dist/index-w03qc9m8.js +634 -0
- package/dist/index.js +16 -2149
- package/dist/init.js +10 -29888
- package/dist/js-yaml-4ypbq2tt.js +3145 -0
- package/dist/node-strategy-12qfy0nv.js +348 -0
- package/dist/tool-0v6na3yp.js +37 -0
- package/dist/tool-5arsyj36.js +11 -0
- package/dist/tool-7eva0peq.js +265 -0
- package/dist/tool-9qecd4wb.js +14 -0
- package/dist/tool-kst857r2.js +3184 -0
- package/dist/tool-thn7hw7k.js +6429 -0
- package/dist/tool.js +10 -51343
- package/package.json +2 -2
|
@@ -0,0 +1,3184 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COMMAND_RULES,
|
|
3
|
+
OutputFormatter,
|
|
4
|
+
RESULTS,
|
|
5
|
+
WHITELISTED_COMMANDS,
|
|
6
|
+
catchError,
|
|
7
|
+
codedAgentCommandPreflight,
|
|
8
|
+
createExecuteCommand,
|
|
9
|
+
createSetupCommand,
|
|
10
|
+
extractErrorMessageSync,
|
|
11
|
+
processContext,
|
|
12
|
+
trackShipSucceeded
|
|
13
|
+
} from "./tool-thn7hw7k.js";
|
|
14
|
+
import {
|
|
15
|
+
getFileSystem
|
|
16
|
+
} from "./tool-7eva0peq.js";
|
|
17
|
+
import {
|
|
18
|
+
__require
|
|
19
|
+
} from "./tool-0v6na3yp.js";
|
|
20
|
+
// package.json
|
|
21
|
+
var package_default = {
|
|
22
|
+
name: "@uipath/codedagent-tool",
|
|
23
|
+
license: "MIT",
|
|
24
|
+
version: "1.201.0-preview.115",
|
|
25
|
+
description: "Build, run, deploy, and manage AI Agents.",
|
|
26
|
+
keywords: [
|
|
27
|
+
"cli-tool",
|
|
28
|
+
"codedagents",
|
|
29
|
+
"wrapper"
|
|
30
|
+
],
|
|
31
|
+
type: "module",
|
|
32
|
+
main: "./dist/tool.js",
|
|
33
|
+
exports: {
|
|
34
|
+
".": "./dist/tool.js",
|
|
35
|
+
"./init": {
|
|
36
|
+
types: "./dist/init.d.ts",
|
|
37
|
+
default: "./dist/init.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
private: false,
|
|
41
|
+
bin: {
|
|
42
|
+
"codedagent-tool": "./dist/index.js"
|
|
43
|
+
},
|
|
44
|
+
files: [
|
|
45
|
+
"dist"
|
|
46
|
+
],
|
|
47
|
+
repository: {
|
|
48
|
+
type: "git",
|
|
49
|
+
url: "https://github.com/UiPath/cli.git",
|
|
50
|
+
directory: "packages/codedagent-tool"
|
|
51
|
+
},
|
|
52
|
+
publishConfig: {
|
|
53
|
+
registry: "https://npm.pkg.github.com/@uipath"
|
|
54
|
+
},
|
|
55
|
+
scripts: {
|
|
56
|
+
build: "bun ../../tools/build-tool.ts && tsc -p tsconfig.build.json --noCheck",
|
|
57
|
+
package: "bun run build && bun pm pack",
|
|
58
|
+
test: "vitest run",
|
|
59
|
+
"test:coverage": "vitest run --coverage",
|
|
60
|
+
lint: "biome check .",
|
|
61
|
+
"lint:fix": "biome check --write ."
|
|
62
|
+
},
|
|
63
|
+
devDependencies: {
|
|
64
|
+
"@types/bun": "^1.3.11",
|
|
65
|
+
"@types/node": "^25.5.2",
|
|
66
|
+
"@uipath/agent-reviewer-sdk": "workspace:*",
|
|
67
|
+
"@uipath/auth": "workspace:*",
|
|
68
|
+
"@uipath/common": "workspace:*",
|
|
69
|
+
"@uipath/filesystem": "workspace:*",
|
|
70
|
+
"@uipath/llmgw-sdk": "workspace:*",
|
|
71
|
+
"@uipath/uipath-python-bridge": "workspace:*",
|
|
72
|
+
commander: "^14.0.3",
|
|
73
|
+
typescript: "^7.0.2"
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// ../agent-reviewer-sdk/dist/index.js
|
|
78
|
+
var KNOWN_CATEGORIES = [
|
|
79
|
+
"evals",
|
|
80
|
+
"schema",
|
|
81
|
+
"tools",
|
|
82
|
+
"guardrails",
|
|
83
|
+
"code",
|
|
84
|
+
"general"
|
|
85
|
+
];
|
|
86
|
+
function listCategories() {
|
|
87
|
+
return [...KNOWN_CATEGORIES];
|
|
88
|
+
}
|
|
89
|
+
async function loadFile(fs, path) {
|
|
90
|
+
const raw = await fs.readFile(path, "utf-8");
|
|
91
|
+
if (raw === null) {
|
|
92
|
+
return { present: false };
|
|
93
|
+
}
|
|
94
|
+
return { present: true, raw };
|
|
95
|
+
}
|
|
96
|
+
function parseJsonObject(raw) {
|
|
97
|
+
let parsed;
|
|
98
|
+
try {
|
|
99
|
+
parsed = JSON.parse(raw);
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
104
|
+
return parsed;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
function resolveEntryPyPath(langgraphJson) {
|
|
109
|
+
if (langgraphJson.present) {
|
|
110
|
+
const graphs = parseJsonObject(langgraphJson.raw)?.graphs;
|
|
111
|
+
const graphsObj = typeof graphs === "object" && graphs !== null && !Array.isArray(graphs) ? graphs : undefined;
|
|
112
|
+
for (const spec of Object.values(graphsObj ?? {})) {
|
|
113
|
+
if (typeof spec === "string") {
|
|
114
|
+
const filePart = spec.split(":")[0]?.replace(/^\.\//, "").trim();
|
|
115
|
+
if (filePart?.endsWith(".py")) {
|
|
116
|
+
return filePart;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return "main.py";
|
|
122
|
+
}
|
|
123
|
+
async function loadCodedProjectContext(projectPath, fs) {
|
|
124
|
+
const uipathJsonPath = fs.path.join(projectPath, "uipath.json");
|
|
125
|
+
const pyprojectPath = fs.path.join(projectPath, "pyproject.toml");
|
|
126
|
+
const entryPointsPath = fs.path.join(projectPath, "entry-points.json");
|
|
127
|
+
const mainPyPath = fs.path.join(projectPath, "main.py");
|
|
128
|
+
const langgraphJsonPath = fs.path.join(projectPath, "langgraph.json");
|
|
129
|
+
const [uipathJson, pyproject, entryPoints, mainPyExists, langgraphJson] = await Promise.all([
|
|
130
|
+
loadFile(fs, uipathJsonPath),
|
|
131
|
+
loadFile(fs, pyprojectPath),
|
|
132
|
+
loadFile(fs, entryPointsPath),
|
|
133
|
+
fs.exists(mainPyPath),
|
|
134
|
+
loadFile(fs, langgraphJsonPath)
|
|
135
|
+
]);
|
|
136
|
+
const entrySourcePath = resolveEntryPyPath(langgraphJson);
|
|
137
|
+
const entrySource = await loadFile(fs, fs.path.join(projectPath, entrySourcePath));
|
|
138
|
+
return {
|
|
139
|
+
projectPath,
|
|
140
|
+
kind: "coded",
|
|
141
|
+
uipathJson,
|
|
142
|
+
pyproject,
|
|
143
|
+
entryPoints,
|
|
144
|
+
mainPy: { present: mainPyExists },
|
|
145
|
+
langgraphJson,
|
|
146
|
+
entrySource,
|
|
147
|
+
entrySourcePath
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
var PENALTY_PER_ERROR = 15;
|
|
151
|
+
var PENALTY_PER_WARNING = 4;
|
|
152
|
+
var PENALTY_PER_INFO = 1;
|
|
153
|
+
var BONUS_PER_STRENGTH = 1;
|
|
154
|
+
var BONUS_CAP = 5;
|
|
155
|
+
var PASS_FAIL_THRESHOLD = 50;
|
|
156
|
+
var FAIL_GRADE_CAP = "C+";
|
|
157
|
+
var GRADE_CHART = [
|
|
158
|
+
[100, "A+"],
|
|
159
|
+
[90, "A"],
|
|
160
|
+
[85, "A-"],
|
|
161
|
+
[80, "B+"],
|
|
162
|
+
[70, "B"],
|
|
163
|
+
[65, "B-"],
|
|
164
|
+
[60, "C+"],
|
|
165
|
+
[50, "C"],
|
|
166
|
+
[45, "C-"],
|
|
167
|
+
[40, "D+"],
|
|
168
|
+
[30, "D"],
|
|
169
|
+
[25, "D-"],
|
|
170
|
+
[0, "F"]
|
|
171
|
+
];
|
|
172
|
+
var GRADE_RANK = new Map(GRADE_CHART.map(([, grade], index) => [grade, index]));
|
|
173
|
+
var ZERO_CATEGORY_COUNTS = () => ({
|
|
174
|
+
evals: 0,
|
|
175
|
+
schema: 0,
|
|
176
|
+
tools: 0,
|
|
177
|
+
guardrails: 0,
|
|
178
|
+
code: 0,
|
|
179
|
+
general: 0
|
|
180
|
+
});
|
|
181
|
+
function lookupGrade(score) {
|
|
182
|
+
for (const [threshold, grade] of GRADE_CHART) {
|
|
183
|
+
if (score >= threshold) {
|
|
184
|
+
return grade;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return "F";
|
|
188
|
+
}
|
|
189
|
+
function rankOf(grade) {
|
|
190
|
+
return GRADE_RANK.get(grade) ?? GRADE_CHART.length - 1;
|
|
191
|
+
}
|
|
192
|
+
function clamp(value, lo, hi) {
|
|
193
|
+
return Math.max(lo, Math.min(hi, value));
|
|
194
|
+
}
|
|
195
|
+
function assertExhaustive(value) {
|
|
196
|
+
throw new Error(`Unhandled severity: ${String(value)}`);
|
|
197
|
+
}
|
|
198
|
+
function buildStats(issues) {
|
|
199
|
+
const byCategory = ZERO_CATEGORY_COUNTS();
|
|
200
|
+
let errors = 0;
|
|
201
|
+
let warnings = 0;
|
|
202
|
+
let infos = 0;
|
|
203
|
+
for (const issue of issues) {
|
|
204
|
+
byCategory[issue.category] += 1;
|
|
205
|
+
const severity = issue.severity;
|
|
206
|
+
switch (severity) {
|
|
207
|
+
case "error":
|
|
208
|
+
errors += 1;
|
|
209
|
+
break;
|
|
210
|
+
case "warning":
|
|
211
|
+
warnings += 1;
|
|
212
|
+
break;
|
|
213
|
+
case "info":
|
|
214
|
+
infos += 1;
|
|
215
|
+
break;
|
|
216
|
+
default:
|
|
217
|
+
assertExhaustive(severity);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return { errors, warnings, infos, byCategory };
|
|
221
|
+
}
|
|
222
|
+
function computeReviewResult(issues, strengths) {
|
|
223
|
+
const stats = buildStats(issues);
|
|
224
|
+
const penalty = PENALTY_PER_ERROR * stats.errors + PENALTY_PER_WARNING * stats.warnings + PENALTY_PER_INFO * stats.infos;
|
|
225
|
+
const bonus = stats.errors === 0 ? Math.min(BONUS_CAP, BONUS_PER_STRENGTH * strengths.length) : 0;
|
|
226
|
+
const score = clamp(100 - penalty + bonus, 0, 100);
|
|
227
|
+
const verdict = stats.errors > 0 || score < PASS_FAIL_THRESHOLD ? "FAIL" : "PASS";
|
|
228
|
+
let grade = lookupGrade(score);
|
|
229
|
+
if (verdict === "FAIL" && rankOf(grade) < rankOf(FAIL_GRADE_CAP)) {
|
|
230
|
+
grade = FAIL_GRADE_CAP;
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
verdict,
|
|
234
|
+
score,
|
|
235
|
+
grade,
|
|
236
|
+
issues,
|
|
237
|
+
strengths,
|
|
238
|
+
stats
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async function runRules(rules, context, fs, options) {
|
|
242
|
+
let selected = rules;
|
|
243
|
+
if (options.categories !== undefined) {
|
|
244
|
+
const known = new Set(KNOWN_CATEGORIES);
|
|
245
|
+
const unknown = options.categories.filter((c) => !known.has(c));
|
|
246
|
+
if (unknown.length > 0) {
|
|
247
|
+
return { ok: false, error: "UNKNOWN_CATEGORIES", unknown };
|
|
248
|
+
}
|
|
249
|
+
const filter = new Set(options.categories);
|
|
250
|
+
selected = rules.filter((rule) => filter.has(rule.category));
|
|
251
|
+
}
|
|
252
|
+
const issues = [];
|
|
253
|
+
for (const rule of selected) {
|
|
254
|
+
let ruleIssues;
|
|
255
|
+
try {
|
|
256
|
+
ruleIssues = await rule.run(context, fs);
|
|
257
|
+
} catch (err) {
|
|
258
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
259
|
+
issues.push({
|
|
260
|
+
ruleId: "RULE_RUNTIME_ERROR",
|
|
261
|
+
category: "general",
|
|
262
|
+
severity: "error",
|
|
263
|
+
description: `Rule '${rule.id}' threw an unhandled error: ${message}`,
|
|
264
|
+
element: rule.id
|
|
265
|
+
});
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
for (const ruleIssue of ruleIssues) {
|
|
269
|
+
issues.push({
|
|
270
|
+
ruleId: rule.id,
|
|
271
|
+
category: rule.category,
|
|
272
|
+
severity: rule.severity,
|
|
273
|
+
...ruleIssue
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return { ok: true, result: computeReviewResult(issues, []) };
|
|
278
|
+
}
|
|
279
|
+
var SHARED_RULES = [];
|
|
280
|
+
var codedPyprojectMissing = {
|
|
281
|
+
id: "PYPROJECT_TOML_MISSING",
|
|
282
|
+
category: "code",
|
|
283
|
+
severity: "error",
|
|
284
|
+
run: async (ctx) => {
|
|
285
|
+
if (ctx.pyproject.present) {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
return [
|
|
289
|
+
{
|
|
290
|
+
description: "Coded agent project is missing `pyproject.toml`.",
|
|
291
|
+
file: "pyproject.toml",
|
|
292
|
+
suggestedFix: "Add a `pyproject.toml` declaring the project's Python dependencies and metadata."
|
|
293
|
+
}
|
|
294
|
+
];
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
var SCOPE_MAP = {
|
|
298
|
+
AGENT: "Agent",
|
|
299
|
+
LLM: "Llm",
|
|
300
|
+
TOOL: "Tool"
|
|
301
|
+
};
|
|
302
|
+
function balancedArgs(source, openParen) {
|
|
303
|
+
let depth = 0;
|
|
304
|
+
let quote = null;
|
|
305
|
+
for (let i = openParen;i < source.length; i++) {
|
|
306
|
+
const ch = source[i];
|
|
307
|
+
if (quote !== null) {
|
|
308
|
+
if (ch === quote) {
|
|
309
|
+
quote = null;
|
|
310
|
+
}
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (ch === '"' || ch === "'") {
|
|
314
|
+
quote = ch;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (ch === "(") {
|
|
318
|
+
depth++;
|
|
319
|
+
} else if (ch === ")") {
|
|
320
|
+
depth--;
|
|
321
|
+
if (depth === 0) {
|
|
322
|
+
return source.slice(openParen + 1, i);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return "";
|
|
327
|
+
}
|
|
328
|
+
function nameArg(args) {
|
|
329
|
+
const m = /\bname\s*=\s*["']([^"']*)["']/.exec(args);
|
|
330
|
+
return m?.[1] && m[1].length > 0 ? m[1] : "<unnamed>";
|
|
331
|
+
}
|
|
332
|
+
function stringKwarg(args, key) {
|
|
333
|
+
return new RegExp(`\\b${key}\\s*=\\s*["']([^"']*)["']`).exec(args)?.[1];
|
|
334
|
+
}
|
|
335
|
+
function actionClassArg(args) {
|
|
336
|
+
return /\baction\s*=\s*([A-Za-z_]\w*)\s*\(/.exec(args)?.[1];
|
|
337
|
+
}
|
|
338
|
+
function scopeArgs(args) {
|
|
339
|
+
const m = /\bscopes\s*=\s*\[([^\]]*)\]/.exec(args);
|
|
340
|
+
if (!m?.[1]) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
const out = [];
|
|
344
|
+
for (const sm of m[1].matchAll(/GuardrailScope\.([A-Z_]+)/g)) {
|
|
345
|
+
const mapped = SCOPE_MAP[sm[1]];
|
|
346
|
+
if (mapped !== undefined && !out.includes(mapped)) {
|
|
347
|
+
out.push(mapped);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
function findCalls(source, classRe) {
|
|
353
|
+
const out = [];
|
|
354
|
+
for (const m of source.matchAll(classRe)) {
|
|
355
|
+
const className = m[1];
|
|
356
|
+
const openParen = m.index + m[0].length - 1;
|
|
357
|
+
out.push({ className, args: balancedArgs(source, openParen) });
|
|
358
|
+
}
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
function resolveValidatorClass(validatorExpr, source) {
|
|
362
|
+
const inline = /^\s*([A-Z]\w*)\s*\(/.exec(validatorExpr);
|
|
363
|
+
if (inline?.[1]) {
|
|
364
|
+
return inline[1];
|
|
365
|
+
}
|
|
366
|
+
const ident = /^\s*([A-Za-z_]\w*)\s*$/.exec(validatorExpr)?.[1];
|
|
367
|
+
if (ident !== undefined) {
|
|
368
|
+
const assign = new RegExp(`\\b${ident}\\s*=\\s*([A-Z]\\w*)\\s*\\(`).exec(source);
|
|
369
|
+
return assign?.[1];
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
function collectLocalClasses(source) {
|
|
374
|
+
const out = new Map;
|
|
375
|
+
for (const m of source.matchAll(/^\s*class\s+([A-Za-z_]\w*)\s*(?:\(([^)]*)\))?\s*:/gm)) {
|
|
376
|
+
const name = m[1];
|
|
377
|
+
const bases = (m[2] ?? "").split(",").filter((b) => !b.includes("=") && !b.includes("*")).map((b) => b.trim().replace(/\[.*$/, "").split(".").pop()).filter((b) => b.length > 0);
|
|
378
|
+
out.set(name, bases);
|
|
379
|
+
}
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
382
|
+
function extractCodedGuardrails(entrySource, pyprojectRaw) {
|
|
383
|
+
const source = entrySource ?? "";
|
|
384
|
+
const guardrails = [];
|
|
385
|
+
for (const { className, args } of findCalls(source, /\b(UiPath[A-Za-z]*Middleware)\s*\(/g)) {
|
|
386
|
+
guardrails.push({
|
|
387
|
+
label: nameArg(args),
|
|
388
|
+
style: "middleware",
|
|
389
|
+
className,
|
|
390
|
+
actionClass: actionClassArg(args),
|
|
391
|
+
scopes: scopeArgs(args),
|
|
392
|
+
hasTools: /\btools\s*=\s*\[/.test(args),
|
|
393
|
+
model: stringKwarg(args, "model")
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
for (const m of source.matchAll(/@guardrail\s*\(/g)) {
|
|
397
|
+
const args = balancedArgs(source, m.index + m[0].length - 1);
|
|
398
|
+
const validatorExpr = /\bvalidator\s*=\s*([\s\S]*?)(?:,\s*(?:action|stage|name)\s*=|$)/.exec(args)?.[1];
|
|
399
|
+
const className = validatorExpr !== undefined ? resolveValidatorClass(validatorExpr, source) ?? "<unknown>" : "<unknown>";
|
|
400
|
+
guardrails.push({
|
|
401
|
+
label: nameArg(args),
|
|
402
|
+
style: "decorator",
|
|
403
|
+
className,
|
|
404
|
+
actionClass: actionClassArg(args),
|
|
405
|
+
scopes: [],
|
|
406
|
+
hasTools: /\btools\s*=\s*\[/.test(args),
|
|
407
|
+
model: stringKwarg(args, "model")
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
const isLangChain = /\bfrom\s+langchain[\w.]*\s+import\b|\bfrom\s+langgraph[\w.]*\s+import\b|\bimport\s+langgraph\b/.test(source) || /uipath[-_]langchain/.test(pyprojectRaw ?? "");
|
|
411
|
+
return {
|
|
412
|
+
guardrails,
|
|
413
|
+
isLangChain,
|
|
414
|
+
importsGuardrailFromPlatform: /(?:from|import)\s+uipath\.platform\.guardrails\b/.test(source),
|
|
415
|
+
importsGuardrailFromLangchain: /(?:from|import)\s+uipath_langchain\.guardrails\b/.test(source),
|
|
416
|
+
localClasses: collectLocalClasses(source)
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
var extractionCache = new WeakMap;
|
|
420
|
+
function codedGuardrailExtraction(ctx) {
|
|
421
|
+
const cached = extractionCache.get(ctx);
|
|
422
|
+
if (cached !== undefined) {
|
|
423
|
+
return cached;
|
|
424
|
+
}
|
|
425
|
+
const extraction = extractCodedGuardrails(ctx.entrySource.present ? ctx.entrySource.raw : undefined, ctx.pyproject.present ? ctx.pyproject.raw : undefined);
|
|
426
|
+
extractionCache.set(ctx, extraction);
|
|
427
|
+
return extraction;
|
|
428
|
+
}
|
|
429
|
+
function subclassesAny(className, targets, localClasses, seen = new Set) {
|
|
430
|
+
if (seen.has(className)) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
seen.add(className);
|
|
434
|
+
const bases = localClasses.get(className);
|
|
435
|
+
if (bases === undefined) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
return bases.some((b) => targets.includes(b) || subclassesAny(b, targets, localClasses, seen));
|
|
439
|
+
}
|
|
440
|
+
var VALIDATOR_BASES = [
|
|
441
|
+
"GuardrailValidatorBase",
|
|
442
|
+
"BuiltInGuardrailValidator",
|
|
443
|
+
"CustomGuardrailValidator"
|
|
444
|
+
];
|
|
445
|
+
var codedGuardrailInvalidContract = {
|
|
446
|
+
id: "CODED_GUARDRAIL_INVALID_CONTRACT",
|
|
447
|
+
category: "guardrails",
|
|
448
|
+
severity: "error",
|
|
449
|
+
run: async (ctx) => {
|
|
450
|
+
const { guardrails, localClasses } = codedGuardrailExtraction(ctx);
|
|
451
|
+
return guardrails.flatMap((g) => {
|
|
452
|
+
const issues = [];
|
|
453
|
+
if (g.actionClass !== undefined && localClasses.has(g.actionClass) && !subclassesAny(g.actionClass, ["GuardrailAction"], localClasses)) {
|
|
454
|
+
issues.push({
|
|
455
|
+
description: `Guardrail \`${g.label}\` uses action class \`${g.actionClass}\`, which does not subclass \`GuardrailAction\` — a custom action must extend it and implement \`handle_validation_result\`, or it fails at runtime.`,
|
|
456
|
+
element: g.label,
|
|
457
|
+
file: ctx.entrySourcePath,
|
|
458
|
+
suggestedFix: "Make the action class subclass `GuardrailAction` from the guardrails SDK and implement `handle_validation_result`."
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
if (g.style === "decorator" && localClasses.has(g.className) && !subclassesAny(g.className, VALIDATOR_BASES, localClasses)) {
|
|
462
|
+
issues.push({
|
|
463
|
+
description: `Guardrail \`${g.label}\` uses validator class \`${g.className}\`, which does not subclass a guardrail validator base — use \`CustomValidator(...)\` or subclass \`GuardrailValidatorBase\` / \`CustomGuardrailValidator\`, or it fails at runtime.`,
|
|
464
|
+
element: g.label,
|
|
465
|
+
file: ctx.entrySourcePath,
|
|
466
|
+
suggestedFix: "Use `CustomValidator(...)` for an in-process rule, or subclass `BuiltInGuardrailValidator` / `CustomGuardrailValidator`."
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
return issues;
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
var codedGuardrailLlmJudgeModelInvalid = {
|
|
474
|
+
id: "CODED_GUARDRAIL_LLM_JUDGE_MODEL_INVALID",
|
|
475
|
+
category: "guardrails",
|
|
476
|
+
severity: "error",
|
|
477
|
+
run: async (ctx) => {
|
|
478
|
+
if (ctx.llmJudgeModels === undefined) {
|
|
479
|
+
return [];
|
|
480
|
+
}
|
|
481
|
+
const allowed = new Set(ctx.llmJudgeModels);
|
|
482
|
+
const available = [...allowed].sort((a, b) => a.localeCompare(b));
|
|
483
|
+
const evidence = available.length > 0 ? `Available models: ${available.join(", ")}` : "No models are available for the LLM-as-judge guardrail on this tenant.";
|
|
484
|
+
return codedGuardrailExtraction(ctx).guardrails.flatMap((g) => {
|
|
485
|
+
const value = g.model;
|
|
486
|
+
if (value === undefined || value.length === 0) {
|
|
487
|
+
return [];
|
|
488
|
+
}
|
|
489
|
+
if (allowed.has(value)) {
|
|
490
|
+
return [];
|
|
491
|
+
}
|
|
492
|
+
return [
|
|
493
|
+
{
|
|
494
|
+
description: `LLM-as-judge guardrail \`${g.label}\` uses model \`${value}\`, which is not available for this tenant's LLM Gateway.`,
|
|
495
|
+
element: g.label,
|
|
496
|
+
file: ctx.entrySourcePath,
|
|
497
|
+
evidences: [evidence],
|
|
498
|
+
suggestedFix: "Set the `model=` argument to a ModelId listed by `uip agent guardrails llm-as-judge-models`."
|
|
499
|
+
}
|
|
500
|
+
];
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
var codedGuardrailToolScopeNoTools = {
|
|
505
|
+
id: "CODED_GUARDRAIL_TOOL_SCOPE_NO_TOOLS",
|
|
506
|
+
category: "guardrails",
|
|
507
|
+
severity: "warning",
|
|
508
|
+
run: async (ctx) => {
|
|
509
|
+
return codedGuardrailExtraction(ctx).guardrails.flatMap((g) => {
|
|
510
|
+
if (g.style !== "middleware" || !g.scopes.includes("Tool") || g.hasTools) {
|
|
511
|
+
return [];
|
|
512
|
+
}
|
|
513
|
+
return [
|
|
514
|
+
{
|
|
515
|
+
description: `Tool-scoped guardrail \`${g.label}\` passes no \`tools=[...]\`, so it has no tool to attach to and will not fire.`,
|
|
516
|
+
element: g.label,
|
|
517
|
+
file: ctx.entrySourcePath,
|
|
518
|
+
suggestedFix: "Pass the `@tool` objects this guardrail should guard via `tools=[my_tool, …]`."
|
|
519
|
+
}
|
|
520
|
+
];
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
var codedGuardrailWrongImport = {
|
|
525
|
+
id: "CODED_GUARDRAIL_WRONG_IMPORT",
|
|
526
|
+
category: "guardrails",
|
|
527
|
+
severity: "error",
|
|
528
|
+
run: async (ctx) => {
|
|
529
|
+
const extraction = codedGuardrailExtraction(ctx);
|
|
530
|
+
if (!extraction.isLangChain || !extraction.importsGuardrailFromPlatform || extraction.importsGuardrailFromLangchain) {
|
|
531
|
+
return [];
|
|
532
|
+
}
|
|
533
|
+
return [
|
|
534
|
+
{
|
|
535
|
+
description: "This LangChain agent imports guardrail symbols from `uipath.platform.guardrails`. " + "The LangChain adapter is only registered by importing from `uipath_langchain.guardrails`, " + "so as written the guardrails never wrap the LLM/tool/agent and silently do nothing at runtime.",
|
|
536
|
+
file: ctx.entrySourcePath,
|
|
537
|
+
evidences: ["from uipath.platform.guardrails import …"],
|
|
538
|
+
suggestedFix: "Import guardrail symbols from `uipath_langchain.guardrails` (not `uipath.platform.guardrails`) " + "so the LangChain adapter wraps the decorated/middleware objects."
|
|
539
|
+
}
|
|
540
|
+
];
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
var CODED_RULES = [
|
|
544
|
+
codedPyprojectMissing,
|
|
545
|
+
codedGuardrailWrongImport,
|
|
546
|
+
codedGuardrailToolScopeNoTools,
|
|
547
|
+
codedGuardrailInvalidContract,
|
|
548
|
+
codedGuardrailLlmJudgeModelInvalid
|
|
549
|
+
];
|
|
550
|
+
async function reviewCodedAgent(projectPath, options, fs) {
|
|
551
|
+
const loaded = await loadCodedProjectContext(projectPath, fs);
|
|
552
|
+
const context = {
|
|
553
|
+
...loaded,
|
|
554
|
+
llmJudgeModels: options.llmJudgeModels
|
|
555
|
+
};
|
|
556
|
+
return runRules([...SHARED_RULES, ...CODED_RULES], context, fs, options);
|
|
557
|
+
}
|
|
558
|
+
var CLASSIFIER_FIELD_NAMES = new Set([
|
|
559
|
+
"class",
|
|
560
|
+
"classification",
|
|
561
|
+
"label",
|
|
562
|
+
"category",
|
|
563
|
+
"intent"
|
|
564
|
+
]);
|
|
565
|
+
var TOOL_LIKE_RESOURCE_TYPES = new Set([
|
|
566
|
+
"tool",
|
|
567
|
+
"escalation",
|
|
568
|
+
"context",
|
|
569
|
+
"mcp"
|
|
570
|
+
]);
|
|
571
|
+
var CUSTOM_RULE_TYPES = new Set([
|
|
572
|
+
"word",
|
|
573
|
+
"number",
|
|
574
|
+
"boolean",
|
|
575
|
+
"always"
|
|
576
|
+
]);
|
|
577
|
+
var SELECTOR_TYPES = new Set(["all", "specific"]);
|
|
578
|
+
var WORD_OPERATORS = new Set([
|
|
579
|
+
"contains",
|
|
580
|
+
"equals",
|
|
581
|
+
"startsWith",
|
|
582
|
+
"endsWith",
|
|
583
|
+
"matchesRegex",
|
|
584
|
+
"doesNotContain",
|
|
585
|
+
"doesNotEqual",
|
|
586
|
+
"doesNotStartWith",
|
|
587
|
+
"doesNotEndWith",
|
|
588
|
+
"isEmpty",
|
|
589
|
+
"isNotEmpty"
|
|
590
|
+
]);
|
|
591
|
+
var WORD_VALUELESS_OPERATORS = new Set([
|
|
592
|
+
"isEmpty",
|
|
593
|
+
"isNotEmpty"
|
|
594
|
+
]);
|
|
595
|
+
var NUMBER_OPERATORS = new Set([
|
|
596
|
+
"equals",
|
|
597
|
+
"doesNotEqual",
|
|
598
|
+
"greaterThan",
|
|
599
|
+
"greaterThanOrEqual",
|
|
600
|
+
"lessThan",
|
|
601
|
+
"lessThanOrEqual"
|
|
602
|
+
]);
|
|
603
|
+
var BOOLEAN_OPERATORS = new Set(["equals"]);
|
|
604
|
+
|
|
605
|
+
// ../admin/llmgw-sdk/dist/index.js
|
|
606
|
+
import { createRequire } from "node:module";
|
|
607
|
+
import fs from "node:fs";
|
|
608
|
+
import fs2 from "node:fs";
|
|
609
|
+
import process2 from "node:process";
|
|
610
|
+
import os from "node:os";
|
|
611
|
+
import fs3 from "node:fs";
|
|
612
|
+
import process3 from "node:process";
|
|
613
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
614
|
+
import { promisify } from "node:util";
|
|
615
|
+
import childProcess from "node:child_process";
|
|
616
|
+
import { promisify as promisify2 } from "node:util";
|
|
617
|
+
import childProcess2 from "node:child_process";
|
|
618
|
+
import fs4, { constants as fsConstants } from "node:fs/promises";
|
|
619
|
+
import { promisify as promisify3 } from "node:util";
|
|
620
|
+
import process4 from "node:process";
|
|
621
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
622
|
+
import process5 from "node:process";
|
|
623
|
+
import { promisify as promisify4 } from "node:util";
|
|
624
|
+
import { execFile as execFile4, execFileSync } from "node:child_process";
|
|
625
|
+
import { promisify as promisify5 } from "node:util";
|
|
626
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
627
|
+
import { promisify as promisify6 } from "node:util";
|
|
628
|
+
import process6 from "node:process";
|
|
629
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
630
|
+
import process7 from "node:process";
|
|
631
|
+
import process8 from "node:process";
|
|
632
|
+
import path from "node:path";
|
|
633
|
+
import { fileURLToPath } from "node:url";
|
|
634
|
+
import childProcess3 from "node:child_process";
|
|
635
|
+
import fs5, { constants as fsConstants2 } from "node:fs/promises";
|
|
636
|
+
import { randomUUID } from "node:crypto";
|
|
637
|
+
import { existsSync } from "node:fs";
|
|
638
|
+
import * as fs6 from "node:fs/promises";
|
|
639
|
+
import * as os2 from "node:os";
|
|
640
|
+
import * as path2 from "node:path";
|
|
641
|
+
var __defProp = Object.defineProperty;
|
|
642
|
+
var __returnValue = (v) => v;
|
|
643
|
+
function __exportSetter(name, newValue) {
|
|
644
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
645
|
+
}
|
|
646
|
+
var __export = (target, all) => {
|
|
647
|
+
for (var name in all)
|
|
648
|
+
__defProp(target, name, {
|
|
649
|
+
get: all[name],
|
|
650
|
+
enumerable: true,
|
|
651
|
+
configurable: true,
|
|
652
|
+
set: __exportSetter.bind(all, name)
|
|
653
|
+
});
|
|
654
|
+
};
|
|
655
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
656
|
+
var __require2 = /* @__PURE__ */ createRequire(import.meta.url);
|
|
657
|
+
function isPromiseLike(value) {
|
|
658
|
+
return value !== null && typeof value === "object" && typeof value.then === "function";
|
|
659
|
+
}
|
|
660
|
+
function catchError2(fnOrPromise) {
|
|
661
|
+
if (isPromiseLike(fnOrPromise)) {
|
|
662
|
+
return settlePromiseLike(fnOrPromise);
|
|
663
|
+
}
|
|
664
|
+
try {
|
|
665
|
+
const result = fnOrPromise();
|
|
666
|
+
if (isPromiseLike(result)) {
|
|
667
|
+
return settlePromiseLike(result);
|
|
668
|
+
}
|
|
669
|
+
return [undefined, result];
|
|
670
|
+
} catch (error) {
|
|
671
|
+
return [
|
|
672
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
673
|
+
undefined
|
|
674
|
+
];
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
function settlePromiseLike(thenable) {
|
|
678
|
+
return Promise.resolve(thenable).then((data) => [undefined, data]).catch((error) => [
|
|
679
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
680
|
+
undefined
|
|
681
|
+
]);
|
|
682
|
+
}
|
|
683
|
+
var UIPATH_HOME_DIR = ".uipath";
|
|
684
|
+
var AUTH_FILENAME = ".auth";
|
|
685
|
+
var DEFAULT_BASE_URL = "https://cloud.uipath.com";
|
|
686
|
+
var DEFAULT_AUTH_TIMEOUT_MS;
|
|
687
|
+
var init_constants = __esm(() => {
|
|
688
|
+
DEFAULT_AUTH_TIMEOUT_MS = 5 * 60 * 1000;
|
|
689
|
+
});
|
|
690
|
+
function isBrowser() {
|
|
691
|
+
return typeof globalThis !== "undefined" && "window" in globalThis && "document" in globalThis;
|
|
692
|
+
}
|
|
693
|
+
function hasDockerEnv() {
|
|
694
|
+
try {
|
|
695
|
+
fs.statSync("/.dockerenv");
|
|
696
|
+
return true;
|
|
697
|
+
} catch {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
function hasDockerCGroup() {
|
|
702
|
+
try {
|
|
703
|
+
return fs.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
704
|
+
} catch {
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
function isDocker() {
|
|
709
|
+
if (isDockerCached === undefined) {
|
|
710
|
+
isDockerCached = hasDockerEnv() || hasDockerCGroup();
|
|
711
|
+
}
|
|
712
|
+
return isDockerCached;
|
|
713
|
+
}
|
|
714
|
+
var isDockerCached;
|
|
715
|
+
var init_is_docker = () => {};
|
|
716
|
+
function isInsideContainer() {
|
|
717
|
+
if (cachedResult === undefined) {
|
|
718
|
+
cachedResult = hasContainerEnv() || isDocker();
|
|
719
|
+
}
|
|
720
|
+
return cachedResult;
|
|
721
|
+
}
|
|
722
|
+
var cachedResult;
|
|
723
|
+
var hasContainerEnv = () => {
|
|
724
|
+
try {
|
|
725
|
+
fs2.statSync("/run/.containerenv");
|
|
726
|
+
return true;
|
|
727
|
+
} catch {
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
var init_is_inside_container = __esm(() => {
|
|
732
|
+
init_is_docker();
|
|
733
|
+
});
|
|
734
|
+
var isWsl = () => {
|
|
735
|
+
if (process2.platform !== "linux") {
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
if (os.release().toLowerCase().includes("microsoft")) {
|
|
739
|
+
if (isInsideContainer()) {
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
return true;
|
|
743
|
+
}
|
|
744
|
+
try {
|
|
745
|
+
if (fs3.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
|
|
746
|
+
return !isInsideContainer();
|
|
747
|
+
}
|
|
748
|
+
} catch {}
|
|
749
|
+
if (fs3.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs3.existsSync("/run/WSL")) {
|
|
750
|
+
return !isInsideContainer();
|
|
751
|
+
}
|
|
752
|
+
return false;
|
|
753
|
+
};
|
|
754
|
+
var is_wsl_default;
|
|
755
|
+
var init_is_wsl = __esm(() => {
|
|
756
|
+
init_is_inside_container();
|
|
757
|
+
is_wsl_default = process2.env.__IS_WSL_TEST__ ? isWsl : isWsl();
|
|
758
|
+
});
|
|
759
|
+
var execFile;
|
|
760
|
+
var powerShellPath = () => `${process3.env.SYSTEMROOT || process3.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
|
761
|
+
var executePowerShell = async (command, options = {}) => {
|
|
762
|
+
const {
|
|
763
|
+
powerShellPath: psPath,
|
|
764
|
+
...execFileOptions
|
|
765
|
+
} = options;
|
|
766
|
+
const encodedCommand = executePowerShell.encodeCommand(command);
|
|
767
|
+
return execFile(psPath ?? powerShellPath(), [
|
|
768
|
+
...executePowerShell.argumentsPrefix,
|
|
769
|
+
encodedCommand
|
|
770
|
+
], {
|
|
771
|
+
encoding: "utf8",
|
|
772
|
+
...execFileOptions
|
|
773
|
+
});
|
|
774
|
+
};
|
|
775
|
+
var init_powershell_utils = __esm(() => {
|
|
776
|
+
execFile = promisify(childProcess.execFile);
|
|
777
|
+
executePowerShell.argumentsPrefix = [
|
|
778
|
+
"-NoProfile",
|
|
779
|
+
"-NonInteractive",
|
|
780
|
+
"-ExecutionPolicy",
|
|
781
|
+
"Bypass",
|
|
782
|
+
"-EncodedCommand"
|
|
783
|
+
];
|
|
784
|
+
executePowerShell.encodeCommand = (command) => Buffer2.from(command, "utf16le").toString("base64");
|
|
785
|
+
executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
|
|
786
|
+
});
|
|
787
|
+
function parseMountPointFromConfig(content) {
|
|
788
|
+
for (const line of content.split(`
|
|
789
|
+
`)) {
|
|
790
|
+
if (/^\s*#/.test(line)) {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
|
|
794
|
+
if (!match) {
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
var execFile2;
|
|
801
|
+
var wslDrivesMountPoint;
|
|
802
|
+
var powerShellPathFromWsl = async () => {
|
|
803
|
+
const mountPoint = await wslDrivesMountPoint();
|
|
804
|
+
return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
|
|
805
|
+
};
|
|
806
|
+
var powerShellPath2;
|
|
807
|
+
var canAccessPowerShellPromise;
|
|
808
|
+
var canAccessPowerShell = async () => {
|
|
809
|
+
canAccessPowerShellPromise ??= (async () => {
|
|
810
|
+
try {
|
|
811
|
+
const psPath = await powerShellPath2();
|
|
812
|
+
await fs4.access(psPath, fsConstants.X_OK);
|
|
813
|
+
return true;
|
|
814
|
+
} catch {
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
})();
|
|
818
|
+
return canAccessPowerShellPromise;
|
|
819
|
+
};
|
|
820
|
+
var wslDefaultBrowser = async () => {
|
|
821
|
+
const psPath = await powerShellPath2();
|
|
822
|
+
const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
|
|
823
|
+
const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
|
|
824
|
+
return stdout.trim();
|
|
825
|
+
};
|
|
826
|
+
var convertWslPathToWindows = async (path3) => {
|
|
827
|
+
if (/^[a-z]+:\/\//i.test(path3)) {
|
|
828
|
+
return path3;
|
|
829
|
+
}
|
|
830
|
+
try {
|
|
831
|
+
const { stdout } = await execFile2("wslpath", ["-aw", path3], { encoding: "utf8" });
|
|
832
|
+
return stdout.trim();
|
|
833
|
+
} catch {
|
|
834
|
+
return path3;
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
var init_wsl_utils = __esm(() => {
|
|
838
|
+
init_is_wsl();
|
|
839
|
+
init_powershell_utils();
|
|
840
|
+
init_is_wsl();
|
|
841
|
+
execFile2 = promisify2(childProcess2.execFile);
|
|
842
|
+
wslDrivesMountPoint = (() => {
|
|
843
|
+
const defaultMountPoint = "/mnt/";
|
|
844
|
+
let mountPoint;
|
|
845
|
+
return async function() {
|
|
846
|
+
if (mountPoint) {
|
|
847
|
+
return mountPoint;
|
|
848
|
+
}
|
|
849
|
+
const configFilePath = "/etc/wsl.conf";
|
|
850
|
+
let isConfigFileExists = false;
|
|
851
|
+
try {
|
|
852
|
+
await fs4.access(configFilePath, fsConstants.F_OK);
|
|
853
|
+
isConfigFileExists = true;
|
|
854
|
+
} catch {}
|
|
855
|
+
if (!isConfigFileExists) {
|
|
856
|
+
return defaultMountPoint;
|
|
857
|
+
}
|
|
858
|
+
const configContent = await fs4.readFile(configFilePath, { encoding: "utf8" });
|
|
859
|
+
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
|
860
|
+
if (parsedMountPoint === undefined) {
|
|
861
|
+
return defaultMountPoint;
|
|
862
|
+
}
|
|
863
|
+
mountPoint = parsedMountPoint;
|
|
864
|
+
mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
|
|
865
|
+
return mountPoint;
|
|
866
|
+
};
|
|
867
|
+
})();
|
|
868
|
+
powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
|
|
869
|
+
});
|
|
870
|
+
function defineLazyProperty(object, propertyName, valueGetter) {
|
|
871
|
+
const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
|
|
872
|
+
Object.defineProperty(object, propertyName, {
|
|
873
|
+
configurable: true,
|
|
874
|
+
enumerable: true,
|
|
875
|
+
get() {
|
|
876
|
+
const result = valueGetter();
|
|
877
|
+
define(result);
|
|
878
|
+
return result;
|
|
879
|
+
},
|
|
880
|
+
set(value) {
|
|
881
|
+
define(value);
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
return object;
|
|
885
|
+
}
|
|
886
|
+
async function defaultBrowserId() {
|
|
887
|
+
if (process4.platform !== "darwin") {
|
|
888
|
+
throw new Error("macOS only");
|
|
889
|
+
}
|
|
890
|
+
const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
|
|
891
|
+
const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
|
|
892
|
+
const browserId = match?.groups.id ?? "com.apple.Safari";
|
|
893
|
+
if (browserId === "com.apple.safari") {
|
|
894
|
+
return "com.apple.Safari";
|
|
895
|
+
}
|
|
896
|
+
return browserId;
|
|
897
|
+
}
|
|
898
|
+
var execFileAsync;
|
|
899
|
+
var init_default_browser_id = __esm(() => {
|
|
900
|
+
execFileAsync = promisify3(execFile3);
|
|
901
|
+
});
|
|
902
|
+
async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
|
|
903
|
+
if (process5.platform !== "darwin") {
|
|
904
|
+
throw new Error("macOS only");
|
|
905
|
+
}
|
|
906
|
+
const outputArguments = humanReadableOutput ? [] : ["-ss"];
|
|
907
|
+
const execOptions = {};
|
|
908
|
+
if (signal) {
|
|
909
|
+
execOptions.signal = signal;
|
|
910
|
+
}
|
|
911
|
+
const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
|
|
912
|
+
return stdout.trim();
|
|
913
|
+
}
|
|
914
|
+
var execFileAsync2;
|
|
915
|
+
var init_run_applescript = __esm(() => {
|
|
916
|
+
execFileAsync2 = promisify4(execFile4);
|
|
917
|
+
});
|
|
918
|
+
async function bundleName(bundleId) {
|
|
919
|
+
return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
|
|
920
|
+
tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
|
|
921
|
+
}
|
|
922
|
+
var init_bundle_name = __esm(() => {
|
|
923
|
+
init_run_applescript();
|
|
924
|
+
});
|
|
925
|
+
async function defaultBrowser(_execFileAsync = execFileAsync3) {
|
|
926
|
+
const { stdout } = await _execFileAsync("reg", [
|
|
927
|
+
"QUERY",
|
|
928
|
+
" HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
|
|
929
|
+
"/v",
|
|
930
|
+
"ProgId"
|
|
931
|
+
]);
|
|
932
|
+
const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
|
|
933
|
+
if (!match) {
|
|
934
|
+
throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
|
|
935
|
+
}
|
|
936
|
+
const { id } = match.groups;
|
|
937
|
+
const dotIndex = id.lastIndexOf(".");
|
|
938
|
+
const hyphenIndex = id.lastIndexOf("-");
|
|
939
|
+
const baseIdByDot = dotIndex === -1 ? undefined : id.slice(0, dotIndex);
|
|
940
|
+
const baseIdByHyphen = hyphenIndex === -1 ? undefined : id.slice(0, hyphenIndex);
|
|
941
|
+
return windowsBrowserProgIds[id] ?? windowsBrowserProgIds[baseIdByDot] ?? windowsBrowserProgIds[baseIdByHyphen] ?? { name: id, id };
|
|
942
|
+
}
|
|
943
|
+
var execFileAsync3;
|
|
944
|
+
var windowsBrowserProgIds;
|
|
945
|
+
var _windowsBrowserProgIdMap;
|
|
946
|
+
var UnknownBrowserError;
|
|
947
|
+
var init_windows = __esm(() => {
|
|
948
|
+
execFileAsync3 = promisify5(execFile5);
|
|
949
|
+
windowsBrowserProgIds = {
|
|
950
|
+
MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
|
|
951
|
+
MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
|
|
952
|
+
MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
|
|
953
|
+
AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
|
|
954
|
+
ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
|
|
955
|
+
ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
|
|
956
|
+
ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
|
|
957
|
+
ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
|
|
958
|
+
BraveHTML: { name: "Brave", id: "com.brave.Browser" },
|
|
959
|
+
BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
|
|
960
|
+
BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
|
|
961
|
+
BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
|
|
962
|
+
FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
|
|
963
|
+
OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
|
|
964
|
+
VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
|
|
965
|
+
"IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
|
|
966
|
+
};
|
|
967
|
+
_windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
|
|
968
|
+
UnknownBrowserError = class UnknownBrowserError2 extends Error {
|
|
969
|
+
};
|
|
970
|
+
});
|
|
971
|
+
async function defaultBrowser2() {
|
|
972
|
+
if (process6.platform === "darwin") {
|
|
973
|
+
const id = await defaultBrowserId();
|
|
974
|
+
const name = await bundleName(id);
|
|
975
|
+
return { name, id };
|
|
976
|
+
}
|
|
977
|
+
if (process6.platform === "linux") {
|
|
978
|
+
const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
|
|
979
|
+
const id = stdout.trim();
|
|
980
|
+
const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
|
|
981
|
+
return { name, id };
|
|
982
|
+
}
|
|
983
|
+
if (process6.platform === "win32") {
|
|
984
|
+
return defaultBrowser();
|
|
985
|
+
}
|
|
986
|
+
throw new Error("Only macOS, Linux, and Windows are supported");
|
|
987
|
+
}
|
|
988
|
+
var execFileAsync4;
|
|
989
|
+
var titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
|
|
990
|
+
var init_default_browser = __esm(() => {
|
|
991
|
+
init_default_browser_id();
|
|
992
|
+
init_bundle_name();
|
|
993
|
+
init_windows();
|
|
994
|
+
init_windows();
|
|
995
|
+
execFileAsync4 = promisify6(execFile6);
|
|
996
|
+
});
|
|
997
|
+
var isInSsh;
|
|
998
|
+
var is_in_ssh_default;
|
|
999
|
+
var init_is_in_ssh = __esm(() => {
|
|
1000
|
+
isInSsh = Boolean(process7.env.SSH_CONNECTION || process7.env.SSH_CLIENT || process7.env.SSH_TTY);
|
|
1001
|
+
is_in_ssh_default = isInSsh;
|
|
1002
|
+
});
|
|
1003
|
+
var exports_open = {};
|
|
1004
|
+
__export(exports_open, {
|
|
1005
|
+
openApp: () => openApp,
|
|
1006
|
+
default: () => open_default,
|
|
1007
|
+
apps: () => apps
|
|
1008
|
+
});
|
|
1009
|
+
function detectArchBinary(binary) {
|
|
1010
|
+
if (typeof binary === "string" || Array.isArray(binary)) {
|
|
1011
|
+
return binary;
|
|
1012
|
+
}
|
|
1013
|
+
const { [arch]: archBinary } = binary;
|
|
1014
|
+
if (!archBinary) {
|
|
1015
|
+
throw new Error(`${arch} is not supported`);
|
|
1016
|
+
}
|
|
1017
|
+
return archBinary;
|
|
1018
|
+
}
|
|
1019
|
+
function detectPlatformBinary({ [platform]: platformBinary }, { wsl } = {}) {
|
|
1020
|
+
if (wsl && is_wsl_default) {
|
|
1021
|
+
return detectArchBinary(wsl);
|
|
1022
|
+
}
|
|
1023
|
+
if (!platformBinary) {
|
|
1024
|
+
throw new Error(`${platform} is not supported`);
|
|
1025
|
+
}
|
|
1026
|
+
return detectArchBinary(platformBinary);
|
|
1027
|
+
}
|
|
1028
|
+
var fallbackAttemptSymbol;
|
|
1029
|
+
var __dirname2;
|
|
1030
|
+
var localXdgOpenPath;
|
|
1031
|
+
var platform;
|
|
1032
|
+
var arch;
|
|
1033
|
+
var tryEachApp = async (apps, opener) => {
|
|
1034
|
+
if (apps.length === 0) {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const errors = [];
|
|
1038
|
+
for (const app of apps) {
|
|
1039
|
+
try {
|
|
1040
|
+
return await opener(app);
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
errors.push(error);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
throw new AggregateError(errors, "Failed to open in all supported apps");
|
|
1046
|
+
};
|
|
1047
|
+
var baseOpen = async (options) => {
|
|
1048
|
+
options = {
|
|
1049
|
+
wait: false,
|
|
1050
|
+
background: false,
|
|
1051
|
+
newInstance: false,
|
|
1052
|
+
allowNonzeroExitCode: false,
|
|
1053
|
+
...options
|
|
1054
|
+
};
|
|
1055
|
+
const isFallbackAttempt = options[fallbackAttemptSymbol] === true;
|
|
1056
|
+
delete options[fallbackAttemptSymbol];
|
|
1057
|
+
if (Array.isArray(options.app)) {
|
|
1058
|
+
return tryEachApp(options.app, (singleApp) => baseOpen({
|
|
1059
|
+
...options,
|
|
1060
|
+
app: singleApp,
|
|
1061
|
+
[fallbackAttemptSymbol]: true
|
|
1062
|
+
}));
|
|
1063
|
+
}
|
|
1064
|
+
let { name: app, arguments: appArguments = [] } = options.app ?? {};
|
|
1065
|
+
appArguments = [...appArguments];
|
|
1066
|
+
if (Array.isArray(app)) {
|
|
1067
|
+
return tryEachApp(app, (appName) => baseOpen({
|
|
1068
|
+
...options,
|
|
1069
|
+
app: {
|
|
1070
|
+
name: appName,
|
|
1071
|
+
arguments: appArguments
|
|
1072
|
+
},
|
|
1073
|
+
[fallbackAttemptSymbol]: true
|
|
1074
|
+
}));
|
|
1075
|
+
}
|
|
1076
|
+
if (app === "browser" || app === "browserPrivate") {
|
|
1077
|
+
const ids = {
|
|
1078
|
+
"com.google.chrome": "chrome",
|
|
1079
|
+
"google-chrome.desktop": "chrome",
|
|
1080
|
+
"com.brave.browser": "brave",
|
|
1081
|
+
"org.mozilla.firefox": "firefox",
|
|
1082
|
+
"firefox.desktop": "firefox",
|
|
1083
|
+
"com.microsoft.msedge": "edge",
|
|
1084
|
+
"com.microsoft.edge": "edge",
|
|
1085
|
+
"com.microsoft.edgemac": "edge",
|
|
1086
|
+
"microsoft-edge.desktop": "edge",
|
|
1087
|
+
"com.apple.safari": "safari"
|
|
1088
|
+
};
|
|
1089
|
+
const flags = {
|
|
1090
|
+
chrome: "--incognito",
|
|
1091
|
+
brave: "--incognito",
|
|
1092
|
+
firefox: "--private-window",
|
|
1093
|
+
edge: "--inPrivate"
|
|
1094
|
+
};
|
|
1095
|
+
let browser;
|
|
1096
|
+
if (is_wsl_default) {
|
|
1097
|
+
const progId = await wslDefaultBrowser();
|
|
1098
|
+
const browserInfo = _windowsBrowserProgIdMap.get(progId);
|
|
1099
|
+
browser = browserInfo ?? {};
|
|
1100
|
+
} else {
|
|
1101
|
+
browser = await defaultBrowser2();
|
|
1102
|
+
}
|
|
1103
|
+
if (browser.id in ids) {
|
|
1104
|
+
const browserName = ids[browser.id.toLowerCase()];
|
|
1105
|
+
if (app === "browserPrivate") {
|
|
1106
|
+
if (browserName === "safari") {
|
|
1107
|
+
throw new Error("Safari doesn't support opening in private mode via command line");
|
|
1108
|
+
}
|
|
1109
|
+
appArguments.push(flags[browserName]);
|
|
1110
|
+
}
|
|
1111
|
+
return baseOpen({
|
|
1112
|
+
...options,
|
|
1113
|
+
app: {
|
|
1114
|
+
name: apps[browserName],
|
|
1115
|
+
arguments: appArguments
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
throw new Error(`${browser.name} is not supported as a default browser`);
|
|
1120
|
+
}
|
|
1121
|
+
let command;
|
|
1122
|
+
const cliArguments = [];
|
|
1123
|
+
const childProcessOptions = {};
|
|
1124
|
+
let shouldUseWindowsInWsl = false;
|
|
1125
|
+
if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
|
|
1126
|
+
shouldUseWindowsInWsl = await canAccessPowerShell();
|
|
1127
|
+
}
|
|
1128
|
+
if (platform === "darwin") {
|
|
1129
|
+
command = "open";
|
|
1130
|
+
if (options.wait) {
|
|
1131
|
+
cliArguments.push("--wait-apps");
|
|
1132
|
+
}
|
|
1133
|
+
if (options.background) {
|
|
1134
|
+
cliArguments.push("--background");
|
|
1135
|
+
}
|
|
1136
|
+
if (options.newInstance) {
|
|
1137
|
+
cliArguments.push("--new");
|
|
1138
|
+
}
|
|
1139
|
+
if (app) {
|
|
1140
|
+
cliArguments.push("-a", app);
|
|
1141
|
+
}
|
|
1142
|
+
} else if (platform === "win32" || shouldUseWindowsInWsl) {
|
|
1143
|
+
command = await powerShellPath2();
|
|
1144
|
+
cliArguments.push(...executePowerShell.argumentsPrefix);
|
|
1145
|
+
if (!is_wsl_default) {
|
|
1146
|
+
childProcessOptions.windowsVerbatimArguments = true;
|
|
1147
|
+
}
|
|
1148
|
+
if (is_wsl_default && options.target) {
|
|
1149
|
+
options.target = await convertWslPathToWindows(options.target);
|
|
1150
|
+
}
|
|
1151
|
+
const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
|
|
1152
|
+
if (options.wait) {
|
|
1153
|
+
encodedArguments.push("-Wait");
|
|
1154
|
+
}
|
|
1155
|
+
if (app) {
|
|
1156
|
+
encodedArguments.push(executePowerShell.escapeArgument(app));
|
|
1157
|
+
if (options.target) {
|
|
1158
|
+
appArguments.push(options.target);
|
|
1159
|
+
}
|
|
1160
|
+
} else if (options.target) {
|
|
1161
|
+
encodedArguments.push(executePowerShell.escapeArgument(options.target));
|
|
1162
|
+
}
|
|
1163
|
+
if (appArguments.length > 0) {
|
|
1164
|
+
appArguments = appArguments.map((argument) => executePowerShell.escapeArgument(argument));
|
|
1165
|
+
encodedArguments.push("-ArgumentList", appArguments.join(","));
|
|
1166
|
+
}
|
|
1167
|
+
options.target = executePowerShell.encodeCommand(encodedArguments.join(" "));
|
|
1168
|
+
if (!options.wait) {
|
|
1169
|
+
childProcessOptions.stdio = "ignore";
|
|
1170
|
+
}
|
|
1171
|
+
} else {
|
|
1172
|
+
if (app) {
|
|
1173
|
+
command = app;
|
|
1174
|
+
} else {
|
|
1175
|
+
const isBundled = !__dirname2 || __dirname2 === "/";
|
|
1176
|
+
let exeLocalXdgOpen = false;
|
|
1177
|
+
try {
|
|
1178
|
+
await fs5.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
1179
|
+
exeLocalXdgOpen = true;
|
|
1180
|
+
} catch {}
|
|
1181
|
+
const useSystemXdgOpen = process8.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
|
|
1182
|
+
command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
|
|
1183
|
+
}
|
|
1184
|
+
if (appArguments.length > 0) {
|
|
1185
|
+
cliArguments.push(...appArguments);
|
|
1186
|
+
}
|
|
1187
|
+
if (!options.wait) {
|
|
1188
|
+
childProcessOptions.stdio = "ignore";
|
|
1189
|
+
childProcessOptions.detached = true;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
if (platform === "darwin" && appArguments.length > 0) {
|
|
1193
|
+
cliArguments.push("--args", ...appArguments);
|
|
1194
|
+
}
|
|
1195
|
+
if (options.target) {
|
|
1196
|
+
cliArguments.push(options.target);
|
|
1197
|
+
}
|
|
1198
|
+
const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
|
|
1199
|
+
if (options.wait) {
|
|
1200
|
+
return new Promise((resolve2, reject) => {
|
|
1201
|
+
subprocess.once("error", reject);
|
|
1202
|
+
subprocess.once("close", (exitCode) => {
|
|
1203
|
+
if (!options.allowNonzeroExitCode && exitCode !== 0) {
|
|
1204
|
+
reject(new Error(`Exited with code ${exitCode}`));
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
resolve2(subprocess);
|
|
1208
|
+
});
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
if (isFallbackAttempt) {
|
|
1212
|
+
return new Promise((resolve2, reject) => {
|
|
1213
|
+
subprocess.once("error", reject);
|
|
1214
|
+
subprocess.once("spawn", () => {
|
|
1215
|
+
subprocess.once("close", (exitCode) => {
|
|
1216
|
+
subprocess.off("error", reject);
|
|
1217
|
+
if (exitCode !== 0) {
|
|
1218
|
+
reject(new Error(`Exited with code ${exitCode}`));
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
subprocess.unref();
|
|
1222
|
+
resolve2(subprocess);
|
|
1223
|
+
});
|
|
1224
|
+
});
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
subprocess.unref();
|
|
1228
|
+
return new Promise((resolve2, reject) => {
|
|
1229
|
+
subprocess.once("error", reject);
|
|
1230
|
+
subprocess.once("spawn", () => {
|
|
1231
|
+
subprocess.off("error", reject);
|
|
1232
|
+
resolve2(subprocess);
|
|
1233
|
+
});
|
|
1234
|
+
});
|
|
1235
|
+
};
|
|
1236
|
+
var open = (target, options) => {
|
|
1237
|
+
if (typeof target !== "string") {
|
|
1238
|
+
throw new TypeError("Expected a `target`");
|
|
1239
|
+
}
|
|
1240
|
+
return baseOpen({
|
|
1241
|
+
...options,
|
|
1242
|
+
target
|
|
1243
|
+
});
|
|
1244
|
+
};
|
|
1245
|
+
var openApp = (name, options) => {
|
|
1246
|
+
if (typeof name !== "string" && !Array.isArray(name)) {
|
|
1247
|
+
throw new TypeError("Expected a valid `name`");
|
|
1248
|
+
}
|
|
1249
|
+
const { arguments: appArguments = [] } = options ?? {};
|
|
1250
|
+
if (appArguments !== undefined && appArguments !== null && !Array.isArray(appArguments)) {
|
|
1251
|
+
throw new TypeError("Expected `appArguments` as Array type");
|
|
1252
|
+
}
|
|
1253
|
+
return baseOpen({
|
|
1254
|
+
...options,
|
|
1255
|
+
app: {
|
|
1256
|
+
name,
|
|
1257
|
+
arguments: appArguments
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
};
|
|
1261
|
+
var apps;
|
|
1262
|
+
var open_default;
|
|
1263
|
+
var init_open = __esm(() => {
|
|
1264
|
+
init_wsl_utils();
|
|
1265
|
+
init_powershell_utils();
|
|
1266
|
+
init_default_browser();
|
|
1267
|
+
init_is_inside_container();
|
|
1268
|
+
init_is_in_ssh();
|
|
1269
|
+
fallbackAttemptSymbol = Symbol("fallbackAttempt");
|
|
1270
|
+
__dirname2 = import.meta.url ? path.dirname(fileURLToPath(import.meta.url)) : "";
|
|
1271
|
+
localXdgOpenPath = path.join(__dirname2, "xdg-open");
|
|
1272
|
+
({ platform, arch } = process8);
|
|
1273
|
+
apps = {
|
|
1274
|
+
browser: "browser",
|
|
1275
|
+
browserPrivate: "browserPrivate"
|
|
1276
|
+
};
|
|
1277
|
+
defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
|
|
1278
|
+
darwin: "google chrome",
|
|
1279
|
+
win32: "chrome",
|
|
1280
|
+
linux: ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
|
|
1281
|
+
}, {
|
|
1282
|
+
wsl: {
|
|
1283
|
+
ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
|
|
1284
|
+
x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
|
|
1285
|
+
}
|
|
1286
|
+
}));
|
|
1287
|
+
defineLazyProperty(apps, "brave", () => detectPlatformBinary({
|
|
1288
|
+
darwin: "brave browser",
|
|
1289
|
+
win32: "brave",
|
|
1290
|
+
linux: ["brave-browser", "brave"]
|
|
1291
|
+
}, {
|
|
1292
|
+
wsl: {
|
|
1293
|
+
ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
|
|
1294
|
+
x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
|
|
1295
|
+
}
|
|
1296
|
+
}));
|
|
1297
|
+
defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
|
|
1298
|
+
darwin: "firefox",
|
|
1299
|
+
win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
|
|
1300
|
+
linux: "firefox"
|
|
1301
|
+
}, {
|
|
1302
|
+
wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
|
|
1303
|
+
}));
|
|
1304
|
+
defineLazyProperty(apps, "edge", () => detectPlatformBinary({
|
|
1305
|
+
darwin: "microsoft edge",
|
|
1306
|
+
win32: "msedge",
|
|
1307
|
+
linux: ["microsoft-edge", "microsoft-edge-dev"]
|
|
1308
|
+
}, {
|
|
1309
|
+
wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
|
|
1310
|
+
}));
|
|
1311
|
+
defineLazyProperty(apps, "safari", () => detectPlatformBinary({
|
|
1312
|
+
darwin: "Safari"
|
|
1313
|
+
}));
|
|
1314
|
+
open_default = open;
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
class NodeFileSystem {
|
|
1318
|
+
path = {
|
|
1319
|
+
join: path2.join,
|
|
1320
|
+
resolve: path2.resolve,
|
|
1321
|
+
relative: path2.relative,
|
|
1322
|
+
dirname: path2.dirname,
|
|
1323
|
+
isAbsolute: path2.isAbsolute,
|
|
1324
|
+
basename: path2.basename
|
|
1325
|
+
};
|
|
1326
|
+
env = {
|
|
1327
|
+
cwd: process.cwd,
|
|
1328
|
+
homedir: os2.homedir,
|
|
1329
|
+
tmpdir: os2.tmpdir,
|
|
1330
|
+
getenv: (key) => process.env[key]
|
|
1331
|
+
};
|
|
1332
|
+
utils = {
|
|
1333
|
+
open: async (url) => {
|
|
1334
|
+
const { default: open2 } = await Promise.resolve().then(() => (init_open(), exports_open));
|
|
1335
|
+
await open2(url);
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
async readFile(path3, options) {
|
|
1339
|
+
try {
|
|
1340
|
+
if (options) {
|
|
1341
|
+
return await fs6.readFile(path3, "utf-8");
|
|
1342
|
+
}
|
|
1343
|
+
return await fs6.readFile(path3);
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
if (this.isEnoent(error))
|
|
1346
|
+
return null;
|
|
1347
|
+
throw error;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
async writeFile(filePath, data) {
|
|
1351
|
+
const dir = path2.dirname(filePath);
|
|
1352
|
+
if (dir) {
|
|
1353
|
+
await fs6.mkdir(dir, { recursive: true });
|
|
1354
|
+
}
|
|
1355
|
+
await fs6.writeFile(filePath, data);
|
|
1356
|
+
}
|
|
1357
|
+
async appendFile(filePath, data) {
|
|
1358
|
+
const dir = path2.dirname(filePath);
|
|
1359
|
+
if (dir) {
|
|
1360
|
+
await fs6.mkdir(dir, { recursive: true });
|
|
1361
|
+
}
|
|
1362
|
+
await fs6.appendFile(filePath, data);
|
|
1363
|
+
}
|
|
1364
|
+
async readdir(dirPath) {
|
|
1365
|
+
try {
|
|
1366
|
+
return await fs6.readdir(dirPath);
|
|
1367
|
+
} catch (error) {
|
|
1368
|
+
if (this.isEnoent(error))
|
|
1369
|
+
return [];
|
|
1370
|
+
throw error;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
async stat(filePath) {
|
|
1374
|
+
try {
|
|
1375
|
+
const stats = await fs6.stat(filePath);
|
|
1376
|
+
return {
|
|
1377
|
+
isFile: () => stats.isFile(),
|
|
1378
|
+
isDirectory: () => stats.isDirectory(),
|
|
1379
|
+
size: stats.size,
|
|
1380
|
+
mtimeMs: stats.mtimeMs
|
|
1381
|
+
};
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
if (this.isEnoent(error))
|
|
1384
|
+
return null;
|
|
1385
|
+
throw error;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
async exists(filePath) {
|
|
1389
|
+
return existsSync(filePath);
|
|
1390
|
+
}
|
|
1391
|
+
async mkdir(dirPath) {
|
|
1392
|
+
await fs6.mkdir(dirPath, { recursive: true });
|
|
1393
|
+
}
|
|
1394
|
+
async acquireLock(lockPath) {
|
|
1395
|
+
const canonicalPath = await this.canonicalizeLockTarget(lockPath);
|
|
1396
|
+
const lockFile = `${canonicalPath}.lock`;
|
|
1397
|
+
const ownerId = randomUUID();
|
|
1398
|
+
const start = Date.now();
|
|
1399
|
+
while (true) {
|
|
1400
|
+
try {
|
|
1401
|
+
await fs6.writeFile(lockFile, ownerId, { flag: "wx" });
|
|
1402
|
+
return this.createLockRelease(lockFile, ownerId);
|
|
1403
|
+
} catch (error) {
|
|
1404
|
+
if (!this.hasErrnoCode(error, "EEXIST")) {
|
|
1405
|
+
throw error;
|
|
1406
|
+
}
|
|
1407
|
+
const stats = await fs6.stat(lockFile).catch(() => null);
|
|
1408
|
+
if (stats && Date.now() - stats.mtimeMs > LOCK_STALE_MS) {
|
|
1409
|
+
const reclaimed = await fs6.rm(lockFile, { force: true }).then(() => true).catch(() => false);
|
|
1410
|
+
if (reclaimed)
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
if (Date.now() - start > LOCK_MAX_WAIT_MS) {
|
|
1414
|
+
throw new Error(`ELOCKED: timed out waiting for lock on ${canonicalPath}`);
|
|
1415
|
+
}
|
|
1416
|
+
await new Promise((resolve2) => setTimeout(resolve2, LOCK_RETRY_MIN_MS + Math.random() * LOCK_RETRY_JITTER_MS));
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
async canonicalizeLockTarget(lockPath) {
|
|
1421
|
+
const absolute = path2.resolve(lockPath);
|
|
1422
|
+
const fullReal = await fs6.realpath(absolute).catch(() => null);
|
|
1423
|
+
if (fullReal)
|
|
1424
|
+
return fullReal;
|
|
1425
|
+
const parent = path2.dirname(absolute);
|
|
1426
|
+
const base = path2.basename(absolute);
|
|
1427
|
+
const canonicalParent = await fs6.realpath(parent).catch(() => parent);
|
|
1428
|
+
return path2.join(canonicalParent, base);
|
|
1429
|
+
}
|
|
1430
|
+
createLockRelease(lockFile, ownerId) {
|
|
1431
|
+
const heartbeatStart = Date.now();
|
|
1432
|
+
let heartbeatTimer;
|
|
1433
|
+
let stopped = false;
|
|
1434
|
+
const stopHeartbeat = () => {
|
|
1435
|
+
stopped = true;
|
|
1436
|
+
if (heartbeatTimer)
|
|
1437
|
+
clearTimeout(heartbeatTimer);
|
|
1438
|
+
};
|
|
1439
|
+
const scheduleNextHeartbeat = () => {
|
|
1440
|
+
if (stopped)
|
|
1441
|
+
return;
|
|
1442
|
+
if (Date.now() - heartbeatStart >= LOCK_MAX_HOLD_MS) {
|
|
1443
|
+
stopped = true;
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
heartbeatTimer = setTimeout(() => {
|
|
1447
|
+
runHeartbeat();
|
|
1448
|
+
}, LOCK_HEARTBEAT_MS);
|
|
1449
|
+
heartbeatTimer.unref?.();
|
|
1450
|
+
};
|
|
1451
|
+
const runHeartbeat = async () => {
|
|
1452
|
+
if (stopped)
|
|
1453
|
+
return;
|
|
1454
|
+
const current = await fs6.readFile(lockFile, "utf-8").catch(() => null);
|
|
1455
|
+
if (stopped)
|
|
1456
|
+
return;
|
|
1457
|
+
if (current !== ownerId) {
|
|
1458
|
+
stopped = true;
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
const now = Date.now() / 1000;
|
|
1462
|
+
await fs6.utimes(lockFile, now, now).catch(() => {});
|
|
1463
|
+
scheduleNextHeartbeat();
|
|
1464
|
+
};
|
|
1465
|
+
scheduleNextHeartbeat();
|
|
1466
|
+
let released = false;
|
|
1467
|
+
return async () => {
|
|
1468
|
+
if (released)
|
|
1469
|
+
return;
|
|
1470
|
+
released = true;
|
|
1471
|
+
stopHeartbeat();
|
|
1472
|
+
const current = await fs6.readFile(lockFile, "utf-8").catch(() => null);
|
|
1473
|
+
if (current === ownerId) {
|
|
1474
|
+
await fs6.rm(lockFile, { force: true });
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
async rm(filePath) {
|
|
1479
|
+
await fs6.rm(filePath, { recursive: true, force: true });
|
|
1480
|
+
}
|
|
1481
|
+
async rename(oldPath, newPath) {
|
|
1482
|
+
await fs6.rename(oldPath, newPath);
|
|
1483
|
+
}
|
|
1484
|
+
async realpath(filePath) {
|
|
1485
|
+
try {
|
|
1486
|
+
return await fs6.realpath(filePath);
|
|
1487
|
+
} catch (error) {
|
|
1488
|
+
if (this.isEnoent(error))
|
|
1489
|
+
return filePath;
|
|
1490
|
+
throw error;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
async getTempDir() {
|
|
1494
|
+
return await fs6.mkdtemp(path2.join(os2.tmpdir(), "uipath-fs-"));
|
|
1495
|
+
}
|
|
1496
|
+
async copyDirectory(sourcePath, destPath) {
|
|
1497
|
+
const sourceStats = await this.stat(sourcePath);
|
|
1498
|
+
if (!sourceStats) {
|
|
1499
|
+
throw new Error(`Source directory does not exist: ${sourcePath}`);
|
|
1500
|
+
}
|
|
1501
|
+
if (!sourceStats.isDirectory()) {
|
|
1502
|
+
throw new Error(`Source path is not a directory: ${sourcePath}`);
|
|
1503
|
+
}
|
|
1504
|
+
await this.mkdir(destPath);
|
|
1505
|
+
const entries = await this.readdir(sourcePath);
|
|
1506
|
+
for (const entry of entries) {
|
|
1507
|
+
const srcEntry = path2.join(sourcePath, entry);
|
|
1508
|
+
const destEntry = path2.join(destPath, entry);
|
|
1509
|
+
const entryStats = await this.stat(srcEntry);
|
|
1510
|
+
if (!entryStats)
|
|
1511
|
+
continue;
|
|
1512
|
+
if (entryStats.isDirectory()) {
|
|
1513
|
+
await this.copyDirectory(srcEntry, destEntry);
|
|
1514
|
+
} else if (entryStats.isFile()) {
|
|
1515
|
+
const content = await this.readFile(srcEntry);
|
|
1516
|
+
if (content !== null) {
|
|
1517
|
+
await this.writeFile(destEntry, content);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
isEnoent(error) {
|
|
1523
|
+
return this.hasErrnoCode(error, "ENOENT");
|
|
1524
|
+
}
|
|
1525
|
+
hasErrnoCode(error, code) {
|
|
1526
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
var LOCK_HEARTBEAT_MS = 5000;
|
|
1530
|
+
var LOCK_STALE_MS = 15000;
|
|
1531
|
+
var LOCK_MAX_WAIT_MS = 20000;
|
|
1532
|
+
var LOCK_MAX_HOLD_MS = 60000;
|
|
1533
|
+
var LOCK_RETRY_MIN_MS = 100;
|
|
1534
|
+
var LOCK_RETRY_JITTER_MS = 200;
|
|
1535
|
+
var init_node = () => {};
|
|
1536
|
+
var fsInstance;
|
|
1537
|
+
var getFileSystem2 = () => fsInstance;
|
|
1538
|
+
var init_src = __esm(() => {
|
|
1539
|
+
init_node();
|
|
1540
|
+
init_node();
|
|
1541
|
+
fsInstance = new NodeFileSystem;
|
|
1542
|
+
});
|
|
1543
|
+
init_constants();
|
|
1544
|
+
var DEFAULT_CLIENT_ID = "36dea5b8-e8bb-423d-8e7b-c808df8f1c00";
|
|
1545
|
+
var AUTH_FILE_CONFIG_KEY = Symbol.for("@uipath/auth/AuthFileConfig");
|
|
1546
|
+
var globalSlot = globalThis;
|
|
1547
|
+
var getAuthFileConfig = () => globalSlot[AUTH_FILE_CONFIG_KEY] ?? {};
|
|
1548
|
+
|
|
1549
|
+
class InvalidBaseUrlError extends Error {
|
|
1550
|
+
url;
|
|
1551
|
+
reason;
|
|
1552
|
+
constructor(url, reason) {
|
|
1553
|
+
super(`Invalid base URL: "${url}"
|
|
1554
|
+
` + `Reason: ${reason}
|
|
1555
|
+
|
|
1556
|
+
` + `Expected format: an https:// URL, e.g. https://cloud.uipath.com (commercial), https://govcloud.uipath.us (Public Sector), or your Automation Suite host (https://<your-host>).
|
|
1557
|
+
` + `You can specify the URL via:
|
|
1558
|
+
` + ` • --authority flag
|
|
1559
|
+
` + ` • UIPATH_URL environment variable
|
|
1560
|
+
` + ` • auth.authority in config file`);
|
|
1561
|
+
this.url = url;
|
|
1562
|
+
this.reason = reason;
|
|
1563
|
+
this.name = "InvalidBaseUrlError";
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
var DEFAULT_SCOPES = ["openid", "profile", "offline_access"];
|
|
1567
|
+
var normalizeAndValidateBaseUrl = (rawUrl) => {
|
|
1568
|
+
let baseUrl = rawUrl;
|
|
1569
|
+
if (baseUrl.endsWith("/identity_/")) {
|
|
1570
|
+
baseUrl = baseUrl.slice(0, -11);
|
|
1571
|
+
} else if (baseUrl.endsWith("/identity_")) {
|
|
1572
|
+
baseUrl = baseUrl.slice(0, -10);
|
|
1573
|
+
}
|
|
1574
|
+
while (baseUrl.endsWith("/")) {
|
|
1575
|
+
baseUrl = baseUrl.slice(0, -1);
|
|
1576
|
+
}
|
|
1577
|
+
const resolvedBaseUrl = baseUrl;
|
|
1578
|
+
const [urlError, url] = catchError2(() => new URL(resolvedBaseUrl));
|
|
1579
|
+
if (urlError) {
|
|
1580
|
+
throw new InvalidBaseUrlError(baseUrl, `Malformed URL. ${urlError instanceof Error ? urlError.message : "Unknown error"}`);
|
|
1581
|
+
}
|
|
1582
|
+
if (url.protocol !== "https:") {
|
|
1583
|
+
throw new InvalidBaseUrlError(baseUrl, `Authority must use https:// scheme, got ${url.protocol}//. OIDC token exchange requires TLS end-to-end.`);
|
|
1584
|
+
}
|
|
1585
|
+
return url.pathname.length > 1 ? url.origin : baseUrl;
|
|
1586
|
+
};
|
|
1587
|
+
var resolveScopes = (isExternalAppAuth, customScopes, fileScopes) => {
|
|
1588
|
+
const requestedScopes = customScopes?.length ? customScopes : fileScopes ?? [];
|
|
1589
|
+
if (isExternalAppAuth)
|
|
1590
|
+
return requestedScopes;
|
|
1591
|
+
return [...new Set([...DEFAULT_SCOPES, ...requestedScopes])];
|
|
1592
|
+
};
|
|
1593
|
+
var resolveConfigAsync = async ({
|
|
1594
|
+
customAuthority,
|
|
1595
|
+
customClientId,
|
|
1596
|
+
customClientSecret,
|
|
1597
|
+
customClientAssertion,
|
|
1598
|
+
customScopes
|
|
1599
|
+
} = {}) => {
|
|
1600
|
+
const fileAuth = getAuthFileConfig();
|
|
1601
|
+
let baseUrl = customAuthority;
|
|
1602
|
+
if (!baseUrl) {
|
|
1603
|
+
baseUrl = process.env.UIPATH_URL;
|
|
1604
|
+
}
|
|
1605
|
+
if (!baseUrl && fileAuth.authority) {
|
|
1606
|
+
baseUrl = fileAuth.authority;
|
|
1607
|
+
}
|
|
1608
|
+
if (!baseUrl) {
|
|
1609
|
+
baseUrl = DEFAULT_BASE_URL;
|
|
1610
|
+
}
|
|
1611
|
+
baseUrl = normalizeAndValidateBaseUrl(baseUrl);
|
|
1612
|
+
let clientId = customClientId;
|
|
1613
|
+
if (!clientId && fileAuth.clientId) {
|
|
1614
|
+
clientId = fileAuth.clientId;
|
|
1615
|
+
}
|
|
1616
|
+
if (!clientId) {
|
|
1617
|
+
clientId = DEFAULT_CLIENT_ID;
|
|
1618
|
+
}
|
|
1619
|
+
let clientSecret = customClientSecret;
|
|
1620
|
+
if (!clientSecret && fileAuth.clientSecret) {
|
|
1621
|
+
clientSecret = fileAuth.clientSecret;
|
|
1622
|
+
}
|
|
1623
|
+
const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && (Boolean(clientSecret) || Boolean(customClientAssertion));
|
|
1624
|
+
const scopes = resolveScopes(isExternalAppAuth, customScopes, fileAuth.scopes);
|
|
1625
|
+
return {
|
|
1626
|
+
clientId,
|
|
1627
|
+
clientSecret,
|
|
1628
|
+
scopes,
|
|
1629
|
+
baseUrl,
|
|
1630
|
+
authorizationEndpoint: `${baseUrl}/identity_/connect/authorize`,
|
|
1631
|
+
tokenEndpoint: `${baseUrl}/identity_/connect/token`
|
|
1632
|
+
};
|
|
1633
|
+
};
|
|
1634
|
+
init_constants();
|
|
1635
|
+
init_src();
|
|
1636
|
+
init_src();
|
|
1637
|
+
init_constants();
|
|
1638
|
+
var DEFAULT_AUTH_PROFILE = "default";
|
|
1639
|
+
var PROFILE_DIR = "profiles";
|
|
1640
|
+
var PROFILE_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
1641
|
+
var ACTIVE_AUTH_PROFILE_KEY = Symbol.for("@uipath/auth/ActiveAuthProfile");
|
|
1642
|
+
var AUTH_PROFILE_STORAGE_KEY = Symbol.for("@uipath/auth/ProfileStorage");
|
|
1643
|
+
var globalSlot2 = globalThis;
|
|
1644
|
+
function isAuthProfileStorage(value) {
|
|
1645
|
+
return value !== null && typeof value === "object" && "getStore" in value && "run" in value;
|
|
1646
|
+
}
|
|
1647
|
+
function createProfileStorage() {
|
|
1648
|
+
const [error, mod] = catchError2(() => __require2("node:async_hooks"));
|
|
1649
|
+
if (error || typeof mod?.AsyncLocalStorage !== "function") {
|
|
1650
|
+
return {
|
|
1651
|
+
getStore: () => {
|
|
1652
|
+
return;
|
|
1653
|
+
},
|
|
1654
|
+
run: (_store, fn) => fn()
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
return new mod.AsyncLocalStorage;
|
|
1658
|
+
}
|
|
1659
|
+
function getProfileStorage() {
|
|
1660
|
+
const existing = globalSlot2[AUTH_PROFILE_STORAGE_KEY];
|
|
1661
|
+
if (isAuthProfileStorage(existing)) {
|
|
1662
|
+
return existing;
|
|
1663
|
+
}
|
|
1664
|
+
const storage = createProfileStorage();
|
|
1665
|
+
globalSlot2[AUTH_PROFILE_STORAGE_KEY] = storage;
|
|
1666
|
+
return storage;
|
|
1667
|
+
}
|
|
1668
|
+
var profileStorage = getProfileStorage();
|
|
1669
|
+
|
|
1670
|
+
class AuthProfileValidationError extends Error {
|
|
1671
|
+
constructor(message) {
|
|
1672
|
+
super(message);
|
|
1673
|
+
this.name = "AuthProfileValidationError";
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
function normalizeAuthProfileName(profile) {
|
|
1677
|
+
if (profile === undefined || profile === DEFAULT_AUTH_PROFILE) {
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
if (profile.length === 0 || profile === "." || profile === ".." || !PROFILE_NAME_RE.test(profile)) {
|
|
1681
|
+
throw new AuthProfileValidationError(`Invalid profile name "${profile}". Profile names may contain only letters, numbers, '.', '_', and '-'.`);
|
|
1682
|
+
}
|
|
1683
|
+
return profile;
|
|
1684
|
+
}
|
|
1685
|
+
function getActiveAuthProfile() {
|
|
1686
|
+
const scopedState = profileStorage.getStore();
|
|
1687
|
+
if (scopedState !== undefined) {
|
|
1688
|
+
return scopedState.profile;
|
|
1689
|
+
}
|
|
1690
|
+
return globalSlot2[ACTIVE_AUTH_PROFILE_KEY]?.profile;
|
|
1691
|
+
}
|
|
1692
|
+
function resolveAuthProfileFilePath(profile) {
|
|
1693
|
+
const normalized = normalizeAuthProfileName(profile);
|
|
1694
|
+
if (normalized === undefined) {
|
|
1695
|
+
throw new AuthProfileValidationError(`"${DEFAULT_AUTH_PROFILE}" is the built-in profile and does not have a profile file path.`);
|
|
1696
|
+
}
|
|
1697
|
+
const fs7 = getFileSystem2();
|
|
1698
|
+
return fs7.path.join(fs7.env.homedir(), UIPATH_HOME_DIR, PROFILE_DIR, normalized, AUTH_FILENAME);
|
|
1699
|
+
}
|
|
1700
|
+
function getActiveAuthProfileFilePath() {
|
|
1701
|
+
const profile = getActiveAuthProfile();
|
|
1702
|
+
return profile ? resolveAuthProfileFilePath(profile) : undefined;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
class InvalidIssuerError extends Error {
|
|
1706
|
+
expected;
|
|
1707
|
+
actual;
|
|
1708
|
+
constructor(expected, actual) {
|
|
1709
|
+
const actualText = actual ?? "<missing>";
|
|
1710
|
+
super(`Token issuer does not match the authority used to log in.
|
|
1711
|
+
` + `Expected: ${expected}
|
|
1712
|
+
` + `Actual: ${actualText}
|
|
1713
|
+
|
|
1714
|
+
` + `The identity server that issued this token is not the one ` + `you pointed --authority at. Refusing to save credentials.`);
|
|
1715
|
+
this.expected = expected;
|
|
1716
|
+
this.actual = actual;
|
|
1717
|
+
this.name = "InvalidIssuerError";
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
var parseJWT = (token) => {
|
|
1721
|
+
try {
|
|
1722
|
+
const parts = token.split(".");
|
|
1723
|
+
const base64Url = parts[1];
|
|
1724
|
+
if (!base64Url) {
|
|
1725
|
+
throw new Error("Invalid JWT token format - missing payload section");
|
|
1726
|
+
}
|
|
1727
|
+
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
1728
|
+
let decodedString;
|
|
1729
|
+
if (isBrowser() && typeof atob !== "undefined") {
|
|
1730
|
+
decodedString = atob(base64);
|
|
1731
|
+
} else {
|
|
1732
|
+
decodedString = Buffer.from(base64, "base64").toString();
|
|
1733
|
+
}
|
|
1734
|
+
const jsonPayload = decodeURIComponent(decodedString.split("").map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`).join(""));
|
|
1735
|
+
const parsed = JSON.parse(jsonPayload);
|
|
1736
|
+
return parsed;
|
|
1737
|
+
} catch (error) {
|
|
1738
|
+
throw new Error(`Failed to parse JWT: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
var assertIssuerMatchesAuthority = (token, authority) => {
|
|
1742
|
+
let payload;
|
|
1743
|
+
try {
|
|
1744
|
+
payload = parseJWT(token);
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
throw new InvalidIssuerError(`${authority.replace(/\/+$/, "")}/identity_`, `<unparseable: ${error instanceof Error ? error.message : "unknown error"}>`);
|
|
1747
|
+
}
|
|
1748
|
+
const stripTrailingSlash = (s) => s.replace(/\/+$/, "");
|
|
1749
|
+
const expected = `${stripTrailingSlash(authority)}/identity_`;
|
|
1750
|
+
const actual = typeof payload.iss === "string" ? stripTrailingSlash(payload.iss) : undefined;
|
|
1751
|
+
if (actual !== expected) {
|
|
1752
|
+
throw new InvalidIssuerError(expected, payload.iss);
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1755
|
+
var getTokenExpiration = (accessToken) => {
|
|
1756
|
+
try {
|
|
1757
|
+
const parts = accessToken.split(".");
|
|
1758
|
+
if (parts.length !== 3) {
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
const payload = parts[1];
|
|
1762
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
1763
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
1764
|
+
const decoded = atob(padded);
|
|
1765
|
+
const claims = JSON.parse(decoded);
|
|
1766
|
+
if (typeof claims.exp !== "number") {
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
return new Date(claims.exp * 1000);
|
|
1770
|
+
} catch {
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
};
|
|
1774
|
+
var parseAuthFlow = (value) => value === "authorization_code" || value === "client_credentials" || value === "federated_credentials" ? value : undefined;
|
|
1775
|
+
var decodeClaims = (accessToken) => {
|
|
1776
|
+
const [error, claims] = catchError2(() => parseJWT(accessToken));
|
|
1777
|
+
return error ? undefined : claims;
|
|
1778
|
+
};
|
|
1779
|
+
var asString = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
1780
|
+
var resolveIdentityType = (claims, authFlow, email) => {
|
|
1781
|
+
const subType = asString(claims?.sub_type);
|
|
1782
|
+
if (subType) {
|
|
1783
|
+
return subType.startsWith("service") ? "Application" : "User";
|
|
1784
|
+
}
|
|
1785
|
+
if (authFlow) {
|
|
1786
|
+
return authFlow === "authorization_code" ? "User" : "Application";
|
|
1787
|
+
}
|
|
1788
|
+
if (email)
|
|
1789
|
+
return "User";
|
|
1790
|
+
if (asString(claims?.client_id) && !asString(claims?.sub)) {
|
|
1791
|
+
return "Application";
|
|
1792
|
+
}
|
|
1793
|
+
return;
|
|
1794
|
+
};
|
|
1795
|
+
var looksLikeEmail = (value) => value?.includes("@") ?? false;
|
|
1796
|
+
var pickEmail = (claims) => {
|
|
1797
|
+
for (const candidate of [claims?.email, claims?.preferred_username]) {
|
|
1798
|
+
const value = asString(candidate);
|
|
1799
|
+
if (looksLikeEmail(value))
|
|
1800
|
+
return value;
|
|
1801
|
+
}
|
|
1802
|
+
return;
|
|
1803
|
+
};
|
|
1804
|
+
var pickName = (claims) => {
|
|
1805
|
+
const username = asString(claims?.preferred_username);
|
|
1806
|
+
return asString(claims?.name) ?? (looksLikeEmail(username) ? undefined : username);
|
|
1807
|
+
};
|
|
1808
|
+
var resolveSessionIdentity = (accessToken, authFlow) => {
|
|
1809
|
+
const claims = accessToken ? decodeClaims(accessToken) : undefined;
|
|
1810
|
+
const email = pickEmail(claims);
|
|
1811
|
+
const type = resolveIdentityType(claims, authFlow, email);
|
|
1812
|
+
if (!type)
|
|
1813
|
+
return;
|
|
1814
|
+
const identity = { type };
|
|
1815
|
+
if (authFlow)
|
|
1816
|
+
identity.authFlow = authFlow;
|
|
1817
|
+
if (type === "User") {
|
|
1818
|
+
const userId = asString(claims?.sub);
|
|
1819
|
+
if (userId)
|
|
1820
|
+
identity.userId = userId;
|
|
1821
|
+
if (email)
|
|
1822
|
+
identity.userEmail = email;
|
|
1823
|
+
const name = pickName(claims);
|
|
1824
|
+
if (name)
|
|
1825
|
+
identity.userName = name;
|
|
1826
|
+
return identity;
|
|
1827
|
+
}
|
|
1828
|
+
const clientId = asString(claims?.client_id);
|
|
1829
|
+
if (clientId)
|
|
1830
|
+
identity.clientId = clientId;
|
|
1831
|
+
return identity;
|
|
1832
|
+
};
|
|
1833
|
+
var ENV_AUTH_ENABLE_VAR = "UIPATH_CLI_ENABLE_ENV_AUTH";
|
|
1834
|
+
var ENFORCE_ROBOT_AUTH_VAR = "UIPATH_CLI_ENFORCE_ROBOT_AUTH";
|
|
1835
|
+
var ENV_AUTH_VARS = {
|
|
1836
|
+
token: "UIPATH_CLI_AUTH_TOKEN",
|
|
1837
|
+
organizationName: "UIPATH_CLI_ORGANIZATION_NAME",
|
|
1838
|
+
organizationId: "UIPATH_CLI_ORGANIZATION_ID",
|
|
1839
|
+
tenantName: "UIPATH_CLI_TENANT_NAME",
|
|
1840
|
+
tenantId: "UIPATH_CLI_TENANT_ID"
|
|
1841
|
+
};
|
|
1842
|
+
|
|
1843
|
+
class EnvAuthConfigError extends Error {
|
|
1844
|
+
constructor(message) {
|
|
1845
|
+
super(message);
|
|
1846
|
+
this.name = "EnvAuthConfigError";
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
var isEnvAuthEnabled = () => process.env[ENV_AUTH_ENABLE_VAR] === "true";
|
|
1850
|
+
var isRobotAuthEnforced = () => process.env[ENFORCE_ROBOT_AUTH_VAR] === "true";
|
|
1851
|
+
var requireEnv = (name) => {
|
|
1852
|
+
const value = process.env[name];
|
|
1853
|
+
if (!value) {
|
|
1854
|
+
throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true but ${name} is not set. ` + `Set ${name} to enable env-var authentication.`);
|
|
1855
|
+
}
|
|
1856
|
+
return value;
|
|
1857
|
+
};
|
|
1858
|
+
var readAuthFromEnv = () => {
|
|
1859
|
+
const accessToken = requireEnv(ENV_AUTH_VARS.token);
|
|
1860
|
+
const organizationName = requireEnv(ENV_AUTH_VARS.organizationName);
|
|
1861
|
+
const organizationId = requireEnv(ENV_AUTH_VARS.organizationId);
|
|
1862
|
+
const tenantName = requireEnv(ENV_AUTH_VARS.tenantName);
|
|
1863
|
+
const tenantId = requireEnv(ENV_AUTH_VARS.tenantId);
|
|
1864
|
+
const [parseError, payload] = catchError2(() => parseJWT(accessToken));
|
|
1865
|
+
if (parseError) {
|
|
1866
|
+
throw new EnvAuthConfigError(`${ENV_AUTH_VARS.token} is not a valid JWT: ` + `${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
1867
|
+
}
|
|
1868
|
+
const iss = payload.iss;
|
|
1869
|
+
if (typeof iss !== "string" || iss.length === 0) {
|
|
1870
|
+
throw new EnvAuthConfigError(`${ENV_AUTH_VARS.token} has no 'iss' claim; cannot determine ` + `the UiPath server. Ensure the token was issued by a UiPath identity server.`);
|
|
1871
|
+
}
|
|
1872
|
+
const [baseUrlError, baseUrl] = catchError2(() => normalizeAndValidateBaseUrl(iss));
|
|
1873
|
+
if (baseUrlError) {
|
|
1874
|
+
if (baseUrlError instanceof InvalidBaseUrlError) {
|
|
1875
|
+
throw baseUrlError;
|
|
1876
|
+
}
|
|
1877
|
+
throw new EnvAuthConfigError(`Failed to derive server URL from token 'iss' claim: ` + `${baseUrlError instanceof Error ? baseUrlError.message : String(baseUrlError)}`);
|
|
1878
|
+
}
|
|
1879
|
+
const expiration = getTokenExpiration(accessToken);
|
|
1880
|
+
const loginStatus = expiration && expiration <= new Date ? "Expired" : "Logged in";
|
|
1881
|
+
const identity = resolveSessionIdentity(accessToken);
|
|
1882
|
+
return {
|
|
1883
|
+
loginStatus,
|
|
1884
|
+
accessToken,
|
|
1885
|
+
baseUrl,
|
|
1886
|
+
organizationName,
|
|
1887
|
+
organizationId,
|
|
1888
|
+
tenantName,
|
|
1889
|
+
tenantId,
|
|
1890
|
+
expiration,
|
|
1891
|
+
source: "env-vars",
|
|
1892
|
+
...identity ? { identity } : {}
|
|
1893
|
+
};
|
|
1894
|
+
};
|
|
1895
|
+
init_src();
|
|
1896
|
+
var BREAKER_SUFFIX = ".refresh-state";
|
|
1897
|
+
var BACKOFF_BASE_MS = 60000;
|
|
1898
|
+
var BACKOFF_CAP_MS = 60 * 60 * 1000;
|
|
1899
|
+
var SURFACE_WINDOW_MS = 60 * 60 * 1000;
|
|
1900
|
+
async function refreshTokenFingerprint(refreshToken) {
|
|
1901
|
+
const bytes = new TextEncoder().encode(refreshToken);
|
|
1902
|
+
if (globalThis.crypto?.subtle) {
|
|
1903
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
1904
|
+
return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("").slice(0, 16);
|
|
1905
|
+
}
|
|
1906
|
+
const { createHash } = await import("node:crypto");
|
|
1907
|
+
return createHash("sha256").update(refreshToken).digest("hex").slice(0, 16);
|
|
1908
|
+
}
|
|
1909
|
+
function breakerPathFor(authPath) {
|
|
1910
|
+
return `${authPath}${BREAKER_SUFFIX}`;
|
|
1911
|
+
}
|
|
1912
|
+
async function loadRefreshBreaker(authPath) {
|
|
1913
|
+
const fs7 = getFileSystem2();
|
|
1914
|
+
try {
|
|
1915
|
+
const content = await fs7.readFile(breakerPathFor(authPath), "utf-8");
|
|
1916
|
+
if (!content)
|
|
1917
|
+
return {};
|
|
1918
|
+
const parsed = JSON.parse(content);
|
|
1919
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1920
|
+
} catch {
|
|
1921
|
+
return {};
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
async function saveRefreshBreaker(authPath, state) {
|
|
1925
|
+
try {
|
|
1926
|
+
const fs7 = getFileSystem2();
|
|
1927
|
+
const path3 = breakerPathFor(authPath);
|
|
1928
|
+
await fs7.mkdir(fs7.path.dirname(path3));
|
|
1929
|
+
const tempPath = `${path3}.tmp`;
|
|
1930
|
+
await fs7.writeFile(tempPath, JSON.stringify(state));
|
|
1931
|
+
await fs7.rename(tempPath, path3);
|
|
1932
|
+
} catch {}
|
|
1933
|
+
}
|
|
1934
|
+
async function clearRefreshBreaker(authPath) {
|
|
1935
|
+
const fs7 = getFileSystem2();
|
|
1936
|
+
const path3 = breakerPathFor(authPath);
|
|
1937
|
+
try {
|
|
1938
|
+
if (await fs7.exists(path3)) {
|
|
1939
|
+
await fs7.rm(path3);
|
|
1940
|
+
}
|
|
1941
|
+
} catch {}
|
|
1942
|
+
}
|
|
1943
|
+
function nextBackoffMs(attempts) {
|
|
1944
|
+
const shift = Math.max(0, attempts - 1);
|
|
1945
|
+
return Math.min(BACKOFF_BASE_MS * 2 ** shift, BACKOFF_CAP_MS);
|
|
1946
|
+
}
|
|
1947
|
+
function shouldSurface(state, nowMs) {
|
|
1948
|
+
if (state.lastSurfacedAtMs === undefined)
|
|
1949
|
+
return true;
|
|
1950
|
+
return nowMs - state.lastSurfacedAtMs >= SURFACE_WINDOW_MS;
|
|
1951
|
+
}
|
|
1952
|
+
init_src();
|
|
1953
|
+
var DEFAULT_TIMEOUT_MS = 1000;
|
|
1954
|
+
var CLOSE_TIMEOUT_MS = 500;
|
|
1955
|
+
var ROBOT_USER_SERVICES_PIPE = "UiPathUserServices";
|
|
1956
|
+
var ROBOT_USER_SERVICES_ALTERNATE_PIPE = `${ROBOT_USER_SERVICES_PIPE}Alternate`;
|
|
1957
|
+
var PIPE_NAME_MAX_LENGTH = 103;
|
|
1958
|
+
var getRobotIpcPipeNames = async () => {
|
|
1959
|
+
const fs7 = getFileSystem2();
|
|
1960
|
+
const username = fs7.env.getenv("USER") ?? fs7.env.getenv("USERNAME");
|
|
1961
|
+
if (!username) {
|
|
1962
|
+
throw new Error("Unable to determine current username");
|
|
1963
|
+
}
|
|
1964
|
+
const tempPath = fs7.env.getenv("TMPDIR") ?? "/tmp/";
|
|
1965
|
+
return [ROBOT_USER_SERVICES_PIPE, ROBOT_USER_SERVICES_ALTERNATE_PIPE].map((baseName) => fs7.path.join(tempPath, `${baseName}_${username}`).substring(0, PIPE_NAME_MAX_LENGTH));
|
|
1966
|
+
};
|
|
1967
|
+
var defaultIsRobotIpcAvailable = async () => {
|
|
1968
|
+
if (process.platform === "win32") {
|
|
1969
|
+
return true;
|
|
1970
|
+
}
|
|
1971
|
+
const [pipeNamesError, pipeNames] = await catchError2(getRobotIpcPipeNames());
|
|
1972
|
+
if (pipeNamesError || !pipeNames) {
|
|
1973
|
+
return false;
|
|
1974
|
+
}
|
|
1975
|
+
const fs7 = getFileSystem2();
|
|
1976
|
+
for (const pipeName of pipeNames) {
|
|
1977
|
+
const [existsError, exists] = await catchError2(fs7.exists(pipeName));
|
|
1978
|
+
if (!existsError && exists === true) {
|
|
1979
|
+
return true;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
return false;
|
|
1983
|
+
};
|
|
1984
|
+
var withTimeout = (promise, timeoutMs) => new Promise((resolve2, reject) => {
|
|
1985
|
+
const timer = setTimeout(() => reject(new Error(`Robot IPC call timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
1986
|
+
promise.then((value) => {
|
|
1987
|
+
clearTimeout(timer);
|
|
1988
|
+
resolve2(value);
|
|
1989
|
+
}, (error) => {
|
|
1990
|
+
clearTimeout(timer);
|
|
1991
|
+
reject(error);
|
|
1992
|
+
});
|
|
1993
|
+
});
|
|
1994
|
+
var parseResourceUrl = (url) => {
|
|
1995
|
+
const [error, parsed] = catchError2(() => new URL(url));
|
|
1996
|
+
if (error || !parsed)
|
|
1997
|
+
return;
|
|
1998
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
1999
|
+
return {
|
|
2000
|
+
baseUrl: parsed.origin,
|
|
2001
|
+
organizationName: segments[0],
|
|
2002
|
+
tenantName: segments[1]
|
|
2003
|
+
};
|
|
2004
|
+
};
|
|
2005
|
+
var ROBOT_CLIENT_LOADER_KEY = Symbol.for("@uipath/auth/RobotClientLoader");
|
|
2006
|
+
var getRegisteredRobotClientLoader = () => {
|
|
2007
|
+
const loader = globalThis[ROBOT_CLIENT_LOADER_KEY];
|
|
2008
|
+
return typeof loader === "function" ? loader : undefined;
|
|
2009
|
+
};
|
|
2010
|
+
var defaultLoadModule = async () => {
|
|
2011
|
+
const hostLoader = getRegisteredRobotClientLoader();
|
|
2012
|
+
if (!hostLoader) {
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
const [error, mod] = await catchError2(() => hostLoader());
|
|
2016
|
+
if (error || !mod) {
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
return mod;
|
|
2020
|
+
};
|
|
2021
|
+
var tryRobotClientFallback = async (options = {}) => {
|
|
2022
|
+
if (isBrowser())
|
|
2023
|
+
return;
|
|
2024
|
+
if (!options.force) {
|
|
2025
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS) {
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
if (process.env.UIPATH_URL) {
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
2033
|
+
const isRobotIpcAvailable = options.isRobotIpcAvailable ?? defaultIsRobotIpcAvailable;
|
|
2034
|
+
const loadModule = options.loadModule ?? defaultLoadModule;
|
|
2035
|
+
if (!await isRobotIpcAvailable()) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
const mod = await loadModule();
|
|
2039
|
+
if (!mod)
|
|
2040
|
+
return;
|
|
2041
|
+
const [ctorError, proxy] = catchError2(() => new mod.RobotProxyConstructor);
|
|
2042
|
+
if (ctorError || !proxy) {
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
try {
|
|
2046
|
+
const enabled = await withTimeout(proxy.interactiveConnectFlow.IsEnabled(), timeoutMs);
|
|
2047
|
+
if (!enabled) {
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
const [resourceUrl, accessToken] = await Promise.all([
|
|
2051
|
+
withTimeout(proxy.accessProvider.GetResourceUrl("Orchestrator"), timeoutMs),
|
|
2052
|
+
withTimeout(proxy.accessProvider.GetAccessToken("Orchestrator", false), timeoutMs)
|
|
2053
|
+
]);
|
|
2054
|
+
if (!accessToken) {
|
|
2055
|
+
return;
|
|
2056
|
+
}
|
|
2057
|
+
const parsedUrl = parseResourceUrl(resourceUrl);
|
|
2058
|
+
if (!parsedUrl) {
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
let organizationIdFromToken;
|
|
2062
|
+
let tenantIdFromToken;
|
|
2063
|
+
let issuerFromToken;
|
|
2064
|
+
const [jwtError, claims] = catchError2(() => parseJWT(accessToken));
|
|
2065
|
+
if (!jwtError && claims) {
|
|
2066
|
+
const rawOrgId = claims.prtId ?? claims.organizationId ?? claims.prt_id;
|
|
2067
|
+
if (typeof rawOrgId === "string" && rawOrgId.length > 0) {
|
|
2068
|
+
organizationIdFromToken = rawOrgId;
|
|
2069
|
+
}
|
|
2070
|
+
const tenantClaim = claims.tenantId ?? claims.tenant_id;
|
|
2071
|
+
if (typeof tenantClaim === "string" && tenantClaim.length > 0) {
|
|
2072
|
+
tenantIdFromToken = tenantClaim;
|
|
2073
|
+
}
|
|
2074
|
+
const issClaim = claims.iss;
|
|
2075
|
+
if (typeof issClaim === "string" && issClaim.length > 0) {
|
|
2076
|
+
issuerFromToken = issClaim;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
return {
|
|
2080
|
+
accessToken,
|
|
2081
|
+
baseUrl: parsedUrl.baseUrl,
|
|
2082
|
+
organizationName: parsedUrl.organizationName,
|
|
2083
|
+
organizationId: organizationIdFromToken ?? parsedUrl.organizationName,
|
|
2084
|
+
tenantName: parsedUrl.tenantName,
|
|
2085
|
+
tenantId: tenantIdFromToken,
|
|
2086
|
+
issuer: issuerFromToken
|
|
2087
|
+
};
|
|
2088
|
+
} catch {
|
|
2089
|
+
return;
|
|
2090
|
+
} finally {
|
|
2091
|
+
await catchError2(() => withTimeout(proxy.CloseAsync(), CLOSE_TIMEOUT_MS));
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
2094
|
+
var TOKEN_REFRESH_REAUTHENTICATE_MESSAGE = "Token refresh failed. Run 'uip login' to re-authenticate.";
|
|
2095
|
+
|
|
2096
|
+
class TokenRefreshOAuthError extends Error {
|
|
2097
|
+
__brand = "TokenRefreshOAuthError";
|
|
2098
|
+
constructor() {
|
|
2099
|
+
super(TOKEN_REFRESH_REAUTHENTICATE_MESSAGE);
|
|
2100
|
+
this.name = "TokenRefreshOAuthError";
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
function isTokenRefreshOAuthFailure(error) {
|
|
2104
|
+
return error instanceof TokenRefreshOAuthError;
|
|
2105
|
+
}
|
|
2106
|
+
var refreshAccessToken = async ({
|
|
2107
|
+
refreshToken,
|
|
2108
|
+
tokenEndpoint,
|
|
2109
|
+
clientId,
|
|
2110
|
+
expectedAuthority
|
|
2111
|
+
}) => {
|
|
2112
|
+
const tokenParams = new URLSearchParams({
|
|
2113
|
+
grant_type: "refresh_token",
|
|
2114
|
+
refresh_token: refreshToken,
|
|
2115
|
+
client_id: clientId
|
|
2116
|
+
});
|
|
2117
|
+
const tokenResponse = await fetch(tokenEndpoint, {
|
|
2118
|
+
method: "POST",
|
|
2119
|
+
headers: {
|
|
2120
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
2121
|
+
},
|
|
2122
|
+
body: tokenParams
|
|
2123
|
+
});
|
|
2124
|
+
const tokenData = await tokenResponse.json();
|
|
2125
|
+
if (!tokenResponse.ok) {
|
|
2126
|
+
throw new TokenRefreshOAuthError;
|
|
2127
|
+
}
|
|
2128
|
+
const newAccessToken = tokenData.access_token;
|
|
2129
|
+
const newRefreshToken = tokenData.refresh_token;
|
|
2130
|
+
if (typeof newAccessToken !== "string" || typeof newRefreshToken !== "string") {
|
|
2131
|
+
throw new Error("Token refresh response is missing access_token or refresh_token");
|
|
2132
|
+
}
|
|
2133
|
+
if (expectedAuthority) {
|
|
2134
|
+
assertIssuerMatchesAuthority(newAccessToken, expectedAuthority);
|
|
2135
|
+
}
|
|
2136
|
+
return { accessToken: newAccessToken, refreshToken: newRefreshToken };
|
|
2137
|
+
};
|
|
2138
|
+
var AUTH_FLOW_ENV_VAR = "UIPATH_AUTH_FLOW";
|
|
2139
|
+
init_src();
|
|
2140
|
+
init_constants();
|
|
2141
|
+
var DEFAULT_ENV_FILENAME = `${UIPATH_HOME_DIR}/${AUTH_FILENAME}`;
|
|
2142
|
+
var KNOWN_ERROR_CODES = new Set([
|
|
2143
|
+
"EISDIR",
|
|
2144
|
+
"EACCES",
|
|
2145
|
+
"EPERM",
|
|
2146
|
+
"ELOOP",
|
|
2147
|
+
"ENOTDIR",
|
|
2148
|
+
"EUNKNOWN"
|
|
2149
|
+
]);
|
|
2150
|
+
var errorCode = (err) => {
|
|
2151
|
+
if (err !== null && typeof err === "object" && "code" in err && typeof err.code === "string") {
|
|
2152
|
+
const raw = err.code;
|
|
2153
|
+
return KNOWN_ERROR_CODES.has(raw) ? raw : "EUNKNOWN";
|
|
2154
|
+
}
|
|
2155
|
+
return "EUNKNOWN";
|
|
2156
|
+
};
|
|
2157
|
+
var probeAsync = async (fs7, candidate) => {
|
|
2158
|
+
try {
|
|
2159
|
+
const stats = await fs7.stat(candidate);
|
|
2160
|
+
if (stats === null) {
|
|
2161
|
+
return { exists: false };
|
|
2162
|
+
}
|
|
2163
|
+
if (!stats.isFile()) {
|
|
2164
|
+
return {
|
|
2165
|
+
exists: false,
|
|
2166
|
+
unusable: {
|
|
2167
|
+
reason: "not-a-file",
|
|
2168
|
+
code: "EISDIR",
|
|
2169
|
+
message: `Path is not a regular file: ${candidate}`
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
return { exists: true };
|
|
2174
|
+
} catch (err) {
|
|
2175
|
+
return {
|
|
2176
|
+
exists: false,
|
|
2177
|
+
unusable: {
|
|
2178
|
+
reason: "unreadable",
|
|
2179
|
+
code: errorCode(err),
|
|
2180
|
+
message: err instanceof Error ? err.message : String(err)
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
};
|
|
2185
|
+
var resolveEnvFileLocationAsync = async (envFilePath = DEFAULT_ENV_FILENAME, opts) => {
|
|
2186
|
+
const fs7 = getFileSystem2();
|
|
2187
|
+
if (fs7.path.isAbsolute(envFilePath)) {
|
|
2188
|
+
const probe2 = await probeAsync(fs7, envFilePath);
|
|
2189
|
+
return probe2.exists ? { exists: true, absolutePath: envFilePath, source: "absolute" } : {
|
|
2190
|
+
exists: false,
|
|
2191
|
+
absolutePath: envFilePath,
|
|
2192
|
+
source: "absolute",
|
|
2193
|
+
...probe2.unusable ? { unusable: probe2.unusable } : {}
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
const cwd = opts?.cwd ?? fs7.env.cwd();
|
|
2197
|
+
let searchDir = cwd;
|
|
2198
|
+
while (true) {
|
|
2199
|
+
const candidate = fs7.path.join(searchDir, envFilePath);
|
|
2200
|
+
const probe2 = await probeAsync(fs7, candidate);
|
|
2201
|
+
if (probe2.exists) {
|
|
2202
|
+
return {
|
|
2203
|
+
exists: true,
|
|
2204
|
+
absolutePath: candidate,
|
|
2205
|
+
source: searchDir === cwd ? "cwd" : "ancestor"
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
const parentDir = fs7.path.dirname(searchDir);
|
|
2209
|
+
if (parentDir === searchDir) {
|
|
2210
|
+
break;
|
|
2211
|
+
}
|
|
2212
|
+
searchDir = parentDir;
|
|
2213
|
+
}
|
|
2214
|
+
const homePath = fs7.path.join(fs7.env.homedir(), envFilePath);
|
|
2215
|
+
const probe = await probeAsync(fs7, homePath);
|
|
2216
|
+
if (probe.exists) {
|
|
2217
|
+
return { exists: true, absolutePath: homePath, source: "home" };
|
|
2218
|
+
}
|
|
2219
|
+
return {
|
|
2220
|
+
exists: false,
|
|
2221
|
+
absolutePath: homePath,
|
|
2222
|
+
source: "default",
|
|
2223
|
+
...probe.unusable ? { unusable: probe.unusable } : {}
|
|
2224
|
+
};
|
|
2225
|
+
};
|
|
2226
|
+
var resolveEnvFilePathAsync = async (envFilePath = DEFAULT_ENV_FILENAME, opts) => {
|
|
2227
|
+
const location = await resolveEnvFileLocationAsync(envFilePath, opts);
|
|
2228
|
+
if (location.exists) {
|
|
2229
|
+
return { absolutePath: location.absolutePath };
|
|
2230
|
+
}
|
|
2231
|
+
return {
|
|
2232
|
+
absolutePath: undefined,
|
|
2233
|
+
errorMessage: location.source === "absolute" ? `Environment file not found: ${envFilePath}` : `Unable to locate environment file: ${envFilePath}. Run 'uip login' to authenticate.`
|
|
2234
|
+
};
|
|
2235
|
+
};
|
|
2236
|
+
var loadEnvFileAsync = async ({ envPath }) => {
|
|
2237
|
+
const fs7 = getFileSystem2();
|
|
2238
|
+
const absolutePath = fs7.path.isAbsolute(envPath) ? envPath : fs7.path.join(fs7.env.cwd(), envPath);
|
|
2239
|
+
if (!await fs7.exists(absolutePath)) {
|
|
2240
|
+
throw new Error(`Environment file not found: ${envPath}`);
|
|
2241
|
+
}
|
|
2242
|
+
const content = await fs7.readFile(absolutePath, "utf-8");
|
|
2243
|
+
if (content === null) {
|
|
2244
|
+
throw new Error(`Environment file not found: ${envPath}`);
|
|
2245
|
+
}
|
|
2246
|
+
const env = {};
|
|
2247
|
+
for (const line of content.split(`
|
|
2248
|
+
`)) {
|
|
2249
|
+
const trimmed = line.trim();
|
|
2250
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
2251
|
+
continue;
|
|
2252
|
+
}
|
|
2253
|
+
const equalIndex = trimmed.indexOf("=");
|
|
2254
|
+
if (equalIndex === -1) {
|
|
2255
|
+
continue;
|
|
2256
|
+
}
|
|
2257
|
+
const key = trimmed.slice(0, equalIndex).trim();
|
|
2258
|
+
let value = trimmed.slice(equalIndex + 1).trim();
|
|
2259
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
2260
|
+
value = value.slice(1, -1);
|
|
2261
|
+
}
|
|
2262
|
+
env[key] = value;
|
|
2263
|
+
}
|
|
2264
|
+
return env;
|
|
2265
|
+
};
|
|
2266
|
+
var saveEnvFileAsync = async ({
|
|
2267
|
+
envPath,
|
|
2268
|
+
data,
|
|
2269
|
+
merge = true
|
|
2270
|
+
}) => {
|
|
2271
|
+
const fs7 = getFileSystem2();
|
|
2272
|
+
const absolutePath = fs7.path.isAbsolute(envPath) ? envPath : fs7.path.join(fs7.env.homedir(), envPath);
|
|
2273
|
+
let existingData = {};
|
|
2274
|
+
if (merge && await fs7.exists(absolutePath)) {
|
|
2275
|
+
try {
|
|
2276
|
+
existingData = await loadEnvFileAsync({ envPath: absolutePath });
|
|
2277
|
+
} catch {}
|
|
2278
|
+
}
|
|
2279
|
+
const finalData = { ...existingData, ...data };
|
|
2280
|
+
const lines = [];
|
|
2281
|
+
for (const [key, value] of Object.entries(finalData)) {
|
|
2282
|
+
if (value === undefined) {
|
|
2283
|
+
continue;
|
|
2284
|
+
}
|
|
2285
|
+
const needsQuotes = value.includes(" ") || value.includes("#");
|
|
2286
|
+
const finalValue = needsQuotes ? `"${value}"` : value;
|
|
2287
|
+
lines.push(`${key}=${finalValue}`);
|
|
2288
|
+
}
|
|
2289
|
+
const content = `${lines.join(`
|
|
2290
|
+
`)}
|
|
2291
|
+
`;
|
|
2292
|
+
const dir = fs7.path.dirname(absolutePath);
|
|
2293
|
+
await fs7.mkdir(dir);
|
|
2294
|
+
const tempPath = `${absolutePath}.tmp`;
|
|
2295
|
+
await fs7.writeFile(tempPath, content);
|
|
2296
|
+
await fs7.rename(tempPath, absolutePath);
|
|
2297
|
+
};
|
|
2298
|
+
var getLoginStatusAsync = async (options = {}) => {
|
|
2299
|
+
return getLoginStatusWithDeps(options);
|
|
2300
|
+
};
|
|
2301
|
+
var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
2302
|
+
const {
|
|
2303
|
+
resolveEnvFilePath = resolveEnvFilePathAsync,
|
|
2304
|
+
loadEnvFile = loadEnvFileAsync,
|
|
2305
|
+
saveEnvFile = saveEnvFileAsync,
|
|
2306
|
+
getFs = getFileSystem2,
|
|
2307
|
+
refreshToken: refreshTokenFn = refreshAccessToken,
|
|
2308
|
+
resolveConfig = resolveConfigAsync,
|
|
2309
|
+
robotFallback = tryRobotClientFallback,
|
|
2310
|
+
loadBreaker = loadRefreshBreaker,
|
|
2311
|
+
saveBreaker = saveRefreshBreaker,
|
|
2312
|
+
clearBreaker = clearRefreshBreaker
|
|
2313
|
+
} = deps;
|
|
2314
|
+
if (isRobotAuthEnforced()) {
|
|
2315
|
+
return resolveRobotEnforcedStatus(robotFallback);
|
|
2316
|
+
}
|
|
2317
|
+
if (isEnvAuthEnabled()) {
|
|
2318
|
+
return readAuthFromEnv();
|
|
2319
|
+
}
|
|
2320
|
+
const activeProfile = getActiveAuthProfile();
|
|
2321
|
+
const activeProfileFilePath = getActiveAuthProfileFilePath();
|
|
2322
|
+
const usingActiveProfile = activeProfile !== undefined && (options.envFilePath === undefined || options.envFilePath === activeProfileFilePath);
|
|
2323
|
+
const envFilePath = options.envFilePath ?? activeProfileFilePath ?? DEFAULT_ENV_FILENAME;
|
|
2324
|
+
const { ensureTokenValidityMinutes } = options;
|
|
2325
|
+
const { absolutePath } = await resolveEnvFilePath(envFilePath);
|
|
2326
|
+
if (absolutePath === undefined) {
|
|
2327
|
+
if (usingActiveProfile) {
|
|
2328
|
+
return {
|
|
2329
|
+
loginStatus: "Not logged in",
|
|
2330
|
+
hint: `No credentials found for profile "${activeProfile}". Run 'uip login --profile ${activeProfile}' to authenticate this profile.`
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
return resolveBorrowedRobotStatus(robotFallback);
|
|
2334
|
+
}
|
|
2335
|
+
const loaded = await loadFileCredentials(loadEnvFile, absolutePath);
|
|
2336
|
+
if ("status" in loaded) {
|
|
2337
|
+
return loaded.status;
|
|
2338
|
+
}
|
|
2339
|
+
const { credentials } = loaded;
|
|
2340
|
+
const globalHint = () => usingActiveProfile ? Promise.resolve(undefined) : getGlobalCredsHint(getFs, loadEnvFile, absolutePath, envFilePath);
|
|
2341
|
+
const expiration = getTokenExpiration(credentials.UIPATH_ACCESS_TOKEN);
|
|
2342
|
+
const outerThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
|
|
2343
|
+
let tokens = {
|
|
2344
|
+
accessToken: credentials.UIPATH_ACCESS_TOKEN,
|
|
2345
|
+
refreshToken: credentials.UIPATH_REFRESH_TOKEN,
|
|
2346
|
+
expiration,
|
|
2347
|
+
lockReleaseFailed: false
|
|
2348
|
+
};
|
|
2349
|
+
const refreshToken = credentials.UIPATH_REFRESH_TOKEN;
|
|
2350
|
+
if (expiration && expiration <= outerThreshold && refreshToken) {
|
|
2351
|
+
const refreshed = await attemptRefresh({
|
|
2352
|
+
absolutePath,
|
|
2353
|
+
credentials,
|
|
2354
|
+
accessToken: credentials.UIPATH_ACCESS_TOKEN,
|
|
2355
|
+
refreshToken,
|
|
2356
|
+
expiration,
|
|
2357
|
+
ensureTokenValidityMinutes,
|
|
2358
|
+
getFs,
|
|
2359
|
+
loadEnvFile,
|
|
2360
|
+
saveEnvFile,
|
|
2361
|
+
refreshFn: refreshTokenFn,
|
|
2362
|
+
resolveConfig,
|
|
2363
|
+
loadBreaker,
|
|
2364
|
+
saveBreaker,
|
|
2365
|
+
clearBreaker,
|
|
2366
|
+
globalHint
|
|
2367
|
+
});
|
|
2368
|
+
if (refreshed.kind === "terminal") {
|
|
2369
|
+
return refreshed.status;
|
|
2370
|
+
}
|
|
2371
|
+
tokens = refreshed.tokens;
|
|
2372
|
+
}
|
|
2373
|
+
return buildFileStatus(tokens, credentials, globalHint);
|
|
2374
|
+
};
|
|
2375
|
+
async function resolveRobotEnforcedStatus(robotFallback) {
|
|
2376
|
+
if (isEnvAuthEnabled()) {
|
|
2377
|
+
throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true and ${ENFORCE_ROBOT_AUTH_VAR}=true are mutually exclusive. Unset one of them and re-run.`);
|
|
2378
|
+
}
|
|
2379
|
+
const robotCreds = await robotFallback({ force: true });
|
|
2380
|
+
if (!robotCreds) {
|
|
2381
|
+
return {
|
|
2382
|
+
loginStatus: "Not logged in",
|
|
2383
|
+
hint: `${ENFORCE_ROBOT_AUTH_VAR}=true but the UiPath Robot session is unavailable. Start and sign in to the Assistant, or unset ${ENFORCE_ROBOT_AUTH_VAR} to fall back to file or env-var authentication.`
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2386
|
+
return buildRobotStatus(robotCreds);
|
|
2387
|
+
}
|
|
2388
|
+
async function resolveBorrowedRobotStatus(robotFallback) {
|
|
2389
|
+
const robotCreds = await robotFallback();
|
|
2390
|
+
return robotCreds ? buildRobotStatus(robotCreds) : { loginStatus: "Not logged in" };
|
|
2391
|
+
}
|
|
2392
|
+
async function loadFileCredentials(loadEnvFile, absolutePath) {
|
|
2393
|
+
let credentials;
|
|
2394
|
+
try {
|
|
2395
|
+
credentials = await loadEnvFile({ envPath: absolutePath });
|
|
2396
|
+
} catch (error) {
|
|
2397
|
+
if (isFileNotFoundError(error)) {
|
|
2398
|
+
return { status: { loginStatus: "Not logged in" } };
|
|
2399
|
+
}
|
|
2400
|
+
throw error;
|
|
2401
|
+
}
|
|
2402
|
+
if (!credentials.UIPATH_ACCESS_TOKEN) {
|
|
2403
|
+
return { status: { loginStatus: "Not logged in" } };
|
|
2404
|
+
}
|
|
2405
|
+
return { credentials };
|
|
2406
|
+
}
|
|
2407
|
+
async function getGlobalCredsHint(getFs, loadEnvFile, absolutePath, envFilePath) {
|
|
2408
|
+
const fs7 = getFs();
|
|
2409
|
+
const globalPath = fs7.path.join(fs7.env.homedir(), envFilePath);
|
|
2410
|
+
if (absolutePath === globalPath)
|
|
2411
|
+
return;
|
|
2412
|
+
if (!await fs7.exists(globalPath))
|
|
2413
|
+
return;
|
|
2414
|
+
try {
|
|
2415
|
+
const globalCreds = await loadEnvFile({ envPath: globalPath });
|
|
2416
|
+
if (!globalCreds.UIPATH_ACCESS_TOKEN)
|
|
2417
|
+
return;
|
|
2418
|
+
const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
|
|
2419
|
+
if (globalExp && globalExp <= new Date)
|
|
2420
|
+
return;
|
|
2421
|
+
return `Local credentials file at ${absolutePath} has expired credentials. Valid credentials exist in ${globalPath}. Remove the local file or run 'uip login' to re-authenticate.`;
|
|
2422
|
+
} catch {
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
function computeExpirationThreshold(ensureTokenValidityMinutes) {
|
|
2427
|
+
return new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
|
|
2428
|
+
}
|
|
2429
|
+
async function attemptRefresh(ctx) {
|
|
2430
|
+
const shortCircuit = await circuitBreakerShortCircuit(ctx);
|
|
2431
|
+
if (shortCircuit) {
|
|
2432
|
+
return { kind: "terminal", status: shortCircuit };
|
|
2433
|
+
}
|
|
2434
|
+
let release;
|
|
2435
|
+
try {
|
|
2436
|
+
release = await ctx.getFs().acquireLock(ctx.absolutePath);
|
|
2437
|
+
} catch (error) {
|
|
2438
|
+
return {
|
|
2439
|
+
kind: "terminal",
|
|
2440
|
+
status: await lockAcquireFailureStatus(ctx, error)
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
let lockedFailure;
|
|
2444
|
+
let lockReleaseFailed = false;
|
|
2445
|
+
let success;
|
|
2446
|
+
try {
|
|
2447
|
+
const outcome = await runRefreshLocked({
|
|
2448
|
+
absolutePath: ctx.absolutePath,
|
|
2449
|
+
refreshToken: ctx.refreshToken,
|
|
2450
|
+
customAuthority: ctx.credentials.UIPATH_URL,
|
|
2451
|
+
ensureTokenValidityMinutes: ctx.ensureTokenValidityMinutes,
|
|
2452
|
+
loadEnvFile: ctx.loadEnvFile,
|
|
2453
|
+
saveEnvFile: ctx.saveEnvFile,
|
|
2454
|
+
refreshFn: ctx.refreshFn,
|
|
2455
|
+
resolveConfig: ctx.resolveConfig,
|
|
2456
|
+
loadBreaker: ctx.loadBreaker,
|
|
2457
|
+
saveBreaker: ctx.saveBreaker,
|
|
2458
|
+
clearBreaker: ctx.clearBreaker
|
|
2459
|
+
});
|
|
2460
|
+
if (outcome.kind === "fail") {
|
|
2461
|
+
lockedFailure = outcome.status;
|
|
2462
|
+
} else {
|
|
2463
|
+
success = outcome;
|
|
2464
|
+
}
|
|
2465
|
+
} finally {
|
|
2466
|
+
try {
|
|
2467
|
+
await release();
|
|
2468
|
+
} catch {
|
|
2469
|
+
lockReleaseFailed = true;
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
if (lockedFailure) {
|
|
2473
|
+
const globalHint = await ctx.globalHint();
|
|
2474
|
+
const base = globalHint ? { ...lockedFailure, loginStatus: "Expired", hint: globalHint } : lockedFailure;
|
|
2475
|
+
return {
|
|
2476
|
+
kind: "terminal",
|
|
2477
|
+
status: lockReleaseFailed ? { ...base, lockReleaseFailed: true } : base
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
return {
|
|
2481
|
+
kind: "refreshed",
|
|
2482
|
+
tokens: {
|
|
2483
|
+
accessToken: success?.accessToken,
|
|
2484
|
+
refreshToken: success?.refreshToken,
|
|
2485
|
+
expiration: success?.expiration,
|
|
2486
|
+
tokenRefresh: success?.tokenRefresh,
|
|
2487
|
+
persistenceWarning: success?.persistenceWarning,
|
|
2488
|
+
lockReleaseFailed
|
|
2489
|
+
}
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
async function buildFileStatus(tokens, credentials, globalHint) {
|
|
2493
|
+
const result = {
|
|
2494
|
+
loginStatus: tokens.expiration && tokens.expiration <= new Date ? "Expired" : "Logged in",
|
|
2495
|
+
accessToken: tokens.accessToken,
|
|
2496
|
+
refreshToken: tokens.refreshToken,
|
|
2497
|
+
baseUrl: credentials.UIPATH_URL,
|
|
2498
|
+
organizationName: credentials.UIPATH_ORGANIZATION_NAME,
|
|
2499
|
+
organizationId: credentials.UIPATH_ORGANIZATION_ID,
|
|
2500
|
+
tenantName: credentials.UIPATH_TENANT_NAME,
|
|
2501
|
+
tenantId: credentials.UIPATH_TENANT_ID,
|
|
2502
|
+
expiration: tokens.expiration,
|
|
2503
|
+
source: "saved-login",
|
|
2504
|
+
...identityFields(tokens.accessToken, credentials),
|
|
2505
|
+
...tokens.persistenceWarning ? { hint: tokens.persistenceWarning, persistenceFailed: true } : {},
|
|
2506
|
+
...tokens.lockReleaseFailed ? { lockReleaseFailed: true } : {},
|
|
2507
|
+
...tokens.tokenRefresh ? { tokenRefresh: tokens.tokenRefresh } : {}
|
|
2508
|
+
};
|
|
2509
|
+
if (result.loginStatus === "Expired") {
|
|
2510
|
+
const hint = await globalHint();
|
|
2511
|
+
if (hint) {
|
|
2512
|
+
result.hint = hint;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
return result;
|
|
2516
|
+
}
|
|
2517
|
+
function identityFields(accessToken, credentials) {
|
|
2518
|
+
const identity = resolveSessionIdentity(accessToken, parseAuthFlow(credentials[AUTH_FLOW_ENV_VAR]));
|
|
2519
|
+
return identity ? { identity } : {};
|
|
2520
|
+
}
|
|
2521
|
+
function buildRobotStatus(robotCreds) {
|
|
2522
|
+
const identity = resolveSessionIdentity(robotCreds.accessToken);
|
|
2523
|
+
return {
|
|
2524
|
+
loginStatus: "Logged in",
|
|
2525
|
+
accessToken: robotCreds.accessToken,
|
|
2526
|
+
baseUrl: robotCreds.baseUrl,
|
|
2527
|
+
organizationName: robotCreds.organizationName,
|
|
2528
|
+
organizationId: robotCreds.organizationId,
|
|
2529
|
+
tenantName: robotCreds.tenantName,
|
|
2530
|
+
tenantId: robotCreds.tenantId,
|
|
2531
|
+
issuer: robotCreds.issuer,
|
|
2532
|
+
expiration: getTokenExpiration(robotCreds.accessToken),
|
|
2533
|
+
source: "robot",
|
|
2534
|
+
...identity ? { identity } : {}
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
var isFileNotFoundError = (error) => {
|
|
2538
|
+
if (!(error instanceof Object))
|
|
2539
|
+
return false;
|
|
2540
|
+
return error.code === "ENOENT";
|
|
2541
|
+
};
|
|
2542
|
+
async function circuitBreakerShortCircuit(ctx) {
|
|
2543
|
+
const {
|
|
2544
|
+
absolutePath,
|
|
2545
|
+
refreshToken,
|
|
2546
|
+
accessToken,
|
|
2547
|
+
credentials,
|
|
2548
|
+
expiration,
|
|
2549
|
+
loadBreaker,
|
|
2550
|
+
saveBreaker,
|
|
2551
|
+
clearBreaker
|
|
2552
|
+
} = ctx;
|
|
2553
|
+
const fingerprint = await refreshTokenFingerprint(refreshToken);
|
|
2554
|
+
const breaker = await loadBreaker(absolutePath).catch(() => ({}));
|
|
2555
|
+
if (breaker.deadTokenFp && breaker.deadTokenFp !== fingerprint) {
|
|
2556
|
+
await clearBreaker(absolutePath);
|
|
2557
|
+
breaker.deadTokenFp = undefined;
|
|
2558
|
+
}
|
|
2559
|
+
const nowMs = Date.now();
|
|
2560
|
+
const tokenIsDead = breaker.deadTokenFp === fingerprint;
|
|
2561
|
+
const inBackoff = breaker.backoffUntilMs !== undefined && nowMs < breaker.backoffUntilMs;
|
|
2562
|
+
if (!tokenIsDead && !inBackoff)
|
|
2563
|
+
return;
|
|
2564
|
+
const globalHint = await ctx.globalHint();
|
|
2565
|
+
const suppressed = !shouldSurface(breaker, nowMs);
|
|
2566
|
+
if (!suppressed) {
|
|
2567
|
+
await saveBreaker(absolutePath, {
|
|
2568
|
+
...breaker,
|
|
2569
|
+
lastSurfacedAtMs: nowMs
|
|
2570
|
+
});
|
|
2571
|
+
}
|
|
2572
|
+
const deadHint = "Run 'uip login' to re-authenticate — the stored refresh token is invalid or expired. In a non-interactive context, authenticate with: uip login --client-id <id> --client-secret <secret> -t <tenant>.";
|
|
2573
|
+
const backoffHint = "Token refresh is temporarily backed off after a recent network error and will retry automatically once the backoff window elapses.";
|
|
2574
|
+
return {
|
|
2575
|
+
loginStatus: globalHint ? "Expired" : "Refresh Failed",
|
|
2576
|
+
...globalHint ? {
|
|
2577
|
+
accessToken,
|
|
2578
|
+
refreshToken,
|
|
2579
|
+
baseUrl: credentials.UIPATH_URL,
|
|
2580
|
+
organizationName: credentials.UIPATH_ORGANIZATION_NAME,
|
|
2581
|
+
organizationId: credentials.UIPATH_ORGANIZATION_ID,
|
|
2582
|
+
tenantName: credentials.UIPATH_TENANT_NAME,
|
|
2583
|
+
tenantId: credentials.UIPATH_TENANT_ID,
|
|
2584
|
+
expiration,
|
|
2585
|
+
source: "saved-login",
|
|
2586
|
+
...identityFields(accessToken, credentials)
|
|
2587
|
+
} : {},
|
|
2588
|
+
hint: globalHint ?? (tokenIsDead ? deadHint : backoffHint),
|
|
2589
|
+
refreshCircuitOpen: true,
|
|
2590
|
+
refreshTelemetrySuppressed: suppressed,
|
|
2591
|
+
tokenRefresh: { attempted: false, success: false }
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
async function lockAcquireFailureStatus(ctx, error) {
|
|
2595
|
+
const msg = errorMessage(error);
|
|
2596
|
+
const globalHint = await ctx.globalHint();
|
|
2597
|
+
if (globalHint) {
|
|
2598
|
+
return {
|
|
2599
|
+
loginStatus: "Expired",
|
|
2600
|
+
accessToken: ctx.accessToken,
|
|
2601
|
+
refreshToken: ctx.refreshToken,
|
|
2602
|
+
baseUrl: ctx.credentials.UIPATH_URL,
|
|
2603
|
+
organizationName: ctx.credentials.UIPATH_ORGANIZATION_NAME,
|
|
2604
|
+
organizationId: ctx.credentials.UIPATH_ORGANIZATION_ID,
|
|
2605
|
+
tenantName: ctx.credentials.UIPATH_TENANT_NAME,
|
|
2606
|
+
tenantId: ctx.credentials.UIPATH_TENANT_ID,
|
|
2607
|
+
expiration: ctx.expiration,
|
|
2608
|
+
source: "saved-login",
|
|
2609
|
+
...identityFields(ctx.accessToken, ctx.credentials),
|
|
2610
|
+
hint: globalHint,
|
|
2611
|
+
tokenRefresh: {
|
|
2612
|
+
attempted: false,
|
|
2613
|
+
success: false,
|
|
2614
|
+
errorMessage: `lock acquisition failed: ${msg}`
|
|
2615
|
+
}
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
return {
|
|
2619
|
+
loginStatus: "Refresh Failed",
|
|
2620
|
+
hint: "Could not acquire the auth-file lock — too many concurrent `uip` processes, or a permission issue on the auth directory. Retry, or run 'uip login' to re-authenticate.",
|
|
2621
|
+
tokenRefresh: {
|
|
2622
|
+
attempted: false,
|
|
2623
|
+
success: false,
|
|
2624
|
+
errorMessage: `lock acquisition failed: ${msg}`
|
|
2625
|
+
}
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
async function runRefreshLocked(inputs) {
|
|
2629
|
+
const {
|
|
2630
|
+
absolutePath,
|
|
2631
|
+
refreshToken: callerRefreshToken,
|
|
2632
|
+
customAuthority,
|
|
2633
|
+
ensureTokenValidityMinutes,
|
|
2634
|
+
loadEnvFile,
|
|
2635
|
+
saveEnvFile,
|
|
2636
|
+
refreshFn,
|
|
2637
|
+
resolveConfig,
|
|
2638
|
+
loadBreaker,
|
|
2639
|
+
saveBreaker,
|
|
2640
|
+
clearBreaker
|
|
2641
|
+
} = inputs;
|
|
2642
|
+
const expirationThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
|
|
2643
|
+
let fresh;
|
|
2644
|
+
try {
|
|
2645
|
+
fresh = await loadEnvFile({ envPath: absolutePath });
|
|
2646
|
+
} catch (error) {
|
|
2647
|
+
return {
|
|
2648
|
+
kind: "fail",
|
|
2649
|
+
status: {
|
|
2650
|
+
loginStatus: "Refresh Failed",
|
|
2651
|
+
hint: "Could not read the auth file while refreshing. Retry, or run 'uip login' to re-authenticate.",
|
|
2652
|
+
tokenRefresh: {
|
|
2653
|
+
attempted: false,
|
|
2654
|
+
success: false,
|
|
2655
|
+
errorMessage: `auth file read failed: ${errorMessage(error)}`
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
};
|
|
2659
|
+
}
|
|
2660
|
+
const freshAccess = fresh.UIPATH_ACCESS_TOKEN;
|
|
2661
|
+
const freshExp = freshAccess ? getTokenExpiration(freshAccess) : undefined;
|
|
2662
|
+
if (freshAccess && freshExp && freshExp > expirationThreshold) {
|
|
2663
|
+
await clearBreaker(absolutePath);
|
|
2664
|
+
return {
|
|
2665
|
+
kind: "ok",
|
|
2666
|
+
accessToken: freshAccess,
|
|
2667
|
+
refreshToken: fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken,
|
|
2668
|
+
expiration: freshExp,
|
|
2669
|
+
tokenRefresh: { attempted: false, success: true }
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
const tokenForIdP = fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken;
|
|
2673
|
+
let refreshedAccess;
|
|
2674
|
+
let refreshedRefresh;
|
|
2675
|
+
try {
|
|
2676
|
+
const config = await resolveConfig({ customAuthority });
|
|
2677
|
+
const refreshed = await refreshFn({
|
|
2678
|
+
refreshToken: tokenForIdP,
|
|
2679
|
+
tokenEndpoint: config.tokenEndpoint,
|
|
2680
|
+
clientId: config.clientId,
|
|
2681
|
+
expectedAuthority: customAuthority
|
|
2682
|
+
});
|
|
2683
|
+
refreshedAccess = refreshed.accessToken;
|
|
2684
|
+
refreshedRefresh = refreshed.refreshToken;
|
|
2685
|
+
} catch (error) {
|
|
2686
|
+
const isOAuthFailure = isTokenRefreshOAuthFailure(error);
|
|
2687
|
+
const hint = isOAuthFailure ? "Run 'uip login' to re-authenticate — the stored refresh token is invalid or expired. In a non-interactive context, authenticate with: uip login --client-id <id> --client-secret <secret> -t <tenant>." : "Token refresh failed. Check your network connection, then retry or run 'uip login' to re-authenticate.";
|
|
2688
|
+
const message = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
|
|
2689
|
+
const fp = await refreshTokenFingerprint(tokenForIdP);
|
|
2690
|
+
if (isOAuthFailure) {
|
|
2691
|
+
await saveBreaker(absolutePath, { deadTokenFp: fp });
|
|
2692
|
+
} else {
|
|
2693
|
+
const prior = await loadBreaker(absolutePath).catch(() => ({}));
|
|
2694
|
+
const attempts = (prior.attempts ?? 0) + 1;
|
|
2695
|
+
await saveBreaker(absolutePath, {
|
|
2696
|
+
...prior,
|
|
2697
|
+
deadTokenFp: undefined,
|
|
2698
|
+
attempts,
|
|
2699
|
+
backoffUntilMs: Date.now() + nextBackoffMs(attempts)
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
return {
|
|
2703
|
+
kind: "fail",
|
|
2704
|
+
status: {
|
|
2705
|
+
loginStatus: "Refresh Failed",
|
|
2706
|
+
hint,
|
|
2707
|
+
tokenRefresh: {
|
|
2708
|
+
attempted: true,
|
|
2709
|
+
success: false,
|
|
2710
|
+
errorMessage: message
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
const refreshedExp = getTokenExpiration(refreshedAccess);
|
|
2716
|
+
if (!refreshedExp || refreshedExp <= new Date) {
|
|
2717
|
+
return {
|
|
2718
|
+
kind: "fail",
|
|
2719
|
+
status: {
|
|
2720
|
+
loginStatus: "Refresh Failed",
|
|
2721
|
+
hint: "The identity server returned an unusable token. Run 'uip login' to re-authenticate.",
|
|
2722
|
+
tokenRefresh: {
|
|
2723
|
+
attempted: true,
|
|
2724
|
+
success: false,
|
|
2725
|
+
errorMessage: "refreshed token has no valid expiration claim"
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
};
|
|
2729
|
+
}
|
|
2730
|
+
await clearBreaker(absolutePath);
|
|
2731
|
+
try {
|
|
2732
|
+
await saveEnvFile({
|
|
2733
|
+
envPath: absolutePath,
|
|
2734
|
+
data: {
|
|
2735
|
+
UIPATH_ACCESS_TOKEN: refreshedAccess,
|
|
2736
|
+
UIPATH_REFRESH_TOKEN: refreshedRefresh
|
|
2737
|
+
},
|
|
2738
|
+
merge: true
|
|
2739
|
+
});
|
|
2740
|
+
return {
|
|
2741
|
+
kind: "ok",
|
|
2742
|
+
accessToken: refreshedAccess,
|
|
2743
|
+
refreshToken: refreshedRefresh,
|
|
2744
|
+
expiration: refreshedExp,
|
|
2745
|
+
tokenRefresh: { attempted: true, success: true }
|
|
2746
|
+
};
|
|
2747
|
+
} catch (error) {
|
|
2748
|
+
const msg = errorMessage(error);
|
|
2749
|
+
return {
|
|
2750
|
+
kind: "ok",
|
|
2751
|
+
accessToken: refreshedAccess,
|
|
2752
|
+
refreshToken: refreshedRefresh,
|
|
2753
|
+
expiration: refreshedExp,
|
|
2754
|
+
persistenceWarning: `Access token refreshed in memory but could not be written to ${absolutePath}: ${msg}. The next CLI invocation will fail until the file can be updated — run 'uip login' to re-authenticate.`,
|
|
2755
|
+
tokenRefresh: {
|
|
2756
|
+
attempted: true,
|
|
2757
|
+
success: true,
|
|
2758
|
+
errorMessage: `persistence failed: ${msg}`
|
|
2759
|
+
}
|
|
2760
|
+
};
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
function normalizeTokenRefreshFailure() {
|
|
2764
|
+
return "stored refresh token is invalid or expired";
|
|
2765
|
+
}
|
|
2766
|
+
function normalizeTokenRefreshUnavailableFailure() {
|
|
2767
|
+
return "token refresh failed before authentication completed";
|
|
2768
|
+
}
|
|
2769
|
+
function errorMessage(error) {
|
|
2770
|
+
return error instanceof Error ? error.message : String(error);
|
|
2771
|
+
}
|
|
2772
|
+
init_constants();
|
|
2773
|
+
init_src();
|
|
2774
|
+
var TENANT_SELECTION_REQUIRED_CODE = "TENANT_SELECTION_REQUIRED";
|
|
2775
|
+
var INVALID_TENANT_CODE = "INVALID_TENANT";
|
|
2776
|
+
var TENANT_SELECTION_CODES = new Set([
|
|
2777
|
+
TENANT_SELECTION_REQUIRED_CODE,
|
|
2778
|
+
INVALID_TENANT_CODE
|
|
2779
|
+
]);
|
|
2780
|
+
init_src();
|
|
2781
|
+
var SERVICE_PATH = {
|
|
2782
|
+
"llm-gateway": "llmgateway_"
|
|
2783
|
+
};
|
|
2784
|
+
var REQUESTING_PRODUCT = "uipath-cli";
|
|
2785
|
+
async function resolveStatus(options) {
|
|
2786
|
+
const status = await getLoginStatusAsync({
|
|
2787
|
+
ensureTokenValidityMinutes: options?.loginValidity
|
|
2788
|
+
});
|
|
2789
|
+
if (status.loginStatus !== "Logged in" || !status.baseUrl || !status.accessToken) {
|
|
2790
|
+
throw new Error(status.hint ? `Not logged in. ${status.hint}` : "Not logged in. Run 'uip login' first.");
|
|
2791
|
+
}
|
|
2792
|
+
if (!status.organizationId) {
|
|
2793
|
+
throw new Error("Organization ID not available. Ensure you are logged in with an organization context.");
|
|
2794
|
+
}
|
|
2795
|
+
return {
|
|
2796
|
+
status,
|
|
2797
|
+
bearerToken: options?.s2sToken ?? status.accessToken
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
function stripTrailingSlash(s) {
|
|
2801
|
+
let end = s.length;
|
|
2802
|
+
while (end > 0 && s.charCodeAt(end - 1) === 47) {
|
|
2803
|
+
end--;
|
|
2804
|
+
}
|
|
2805
|
+
return end === s.length ? s : s.slice(0, end);
|
|
2806
|
+
}
|
|
2807
|
+
function buildUrl(baseUrl, path3, query) {
|
|
2808
|
+
const trimmed = stripTrailingSlash(baseUrl);
|
|
2809
|
+
const suffix = path3.startsWith("/") ? path3 : `/${path3}`;
|
|
2810
|
+
if (!query)
|
|
2811
|
+
return `${trimmed}${suffix}`;
|
|
2812
|
+
const search = new URLSearchParams;
|
|
2813
|
+
for (const [key, value] of Object.entries(query)) {
|
|
2814
|
+
if (value === undefined)
|
|
2815
|
+
continue;
|
|
2816
|
+
search.append(key, String(value));
|
|
2817
|
+
}
|
|
2818
|
+
const qs = search.toString();
|
|
2819
|
+
return qs ? `${trimmed}${suffix}?${qs}` : `${trimmed}${suffix}`;
|
|
2820
|
+
}
|
|
2821
|
+
async function readErrorBody(response) {
|
|
2822
|
+
const text = await response.text().catch(() => "");
|
|
2823
|
+
if (!text)
|
|
2824
|
+
return `${response.status} ${response.statusText}`;
|
|
2825
|
+
return `${response.status} ${response.statusText}: ${text.slice(0, 1000)}`;
|
|
2826
|
+
}
|
|
2827
|
+
async function createAiHttpContext(service, options) {
|
|
2828
|
+
const { status, bearerToken } = await resolveStatus(options);
|
|
2829
|
+
const explicitTenantOverride = options?.tenantName !== undefined;
|
|
2830
|
+
const tenantName = options?.tenantName ?? status.tenantName;
|
|
2831
|
+
const tenantSegment = explicitTenantOverride ? options?.tenantName : status.tenantId ?? status.tenantName;
|
|
2832
|
+
const segments = [
|
|
2833
|
+
status.baseUrl ? stripTrailingSlash(status.baseUrl) : undefined,
|
|
2834
|
+
status.organizationId ? encodeURIComponent(status.organizationId) : undefined
|
|
2835
|
+
];
|
|
2836
|
+
if (tenantSegment) {
|
|
2837
|
+
segments.push(encodeURIComponent(tenantSegment));
|
|
2838
|
+
}
|
|
2839
|
+
segments.push(SERVICE_PATH[service]);
|
|
2840
|
+
const baseUrl = segments.filter(Boolean).join("/");
|
|
2841
|
+
const baseHeaders = {
|
|
2842
|
+
Authorization: `Bearer ${bearerToken}`,
|
|
2843
|
+
Accept: "application/json"
|
|
2844
|
+
};
|
|
2845
|
+
if (status.organizationId) {
|
|
2846
|
+
baseHeaders["x-uipath-internal-accountid"] = status.organizationId;
|
|
2847
|
+
}
|
|
2848
|
+
if (!explicitTenantOverride && status.tenantId) {
|
|
2849
|
+
baseHeaders["x-uipath-internal-tenantid"] = status.tenantId;
|
|
2850
|
+
}
|
|
2851
|
+
baseHeaders["x-uipath-requestingproduct"] = REQUESTING_PRODUCT;
|
|
2852
|
+
baseHeaders["X-UiPath-LLMGateway-AllowFull4xxResponse"] = "true";
|
|
2853
|
+
const fetchJson = async (req) => {
|
|
2854
|
+
const url = buildUrl(baseUrl, req.path, req.query);
|
|
2855
|
+
const headers = {
|
|
2856
|
+
...baseHeaders,
|
|
2857
|
+
...req.extraHeaders ?? {}
|
|
2858
|
+
};
|
|
2859
|
+
if (req.body !== undefined) {
|
|
2860
|
+
headers["Content-Type"] = "application/json";
|
|
2861
|
+
}
|
|
2862
|
+
const response = await fetch(url, {
|
|
2863
|
+
method: req.method,
|
|
2864
|
+
headers,
|
|
2865
|
+
body: req.body !== undefined ? JSON.stringify(req.body) : undefined
|
|
2866
|
+
});
|
|
2867
|
+
if (!response.ok) {
|
|
2868
|
+
throw new Error(await readErrorBody(response));
|
|
2869
|
+
}
|
|
2870
|
+
if (response.status === 204) {
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
const text = await response.text();
|
|
2874
|
+
if (!text)
|
|
2875
|
+
return;
|
|
2876
|
+
return JSON.parse(text);
|
|
2877
|
+
};
|
|
2878
|
+
const fetchVoid = async (req) => {
|
|
2879
|
+
const url = buildUrl(baseUrl, req.path, req.query);
|
|
2880
|
+
const headers = {
|
|
2881
|
+
...baseHeaders,
|
|
2882
|
+
...req.extraHeaders ?? {}
|
|
2883
|
+
};
|
|
2884
|
+
if (req.body !== undefined) {
|
|
2885
|
+
headers["Content-Type"] = "application/json";
|
|
2886
|
+
}
|
|
2887
|
+
const response = await fetch(url, {
|
|
2888
|
+
method: req.method,
|
|
2889
|
+
headers,
|
|
2890
|
+
body: req.body !== undefined ? JSON.stringify(req.body) : undefined
|
|
2891
|
+
});
|
|
2892
|
+
if (!response.ok) {
|
|
2893
|
+
throw new Error(await readErrorBody(response));
|
|
2894
|
+
}
|
|
2895
|
+
};
|
|
2896
|
+
return {
|
|
2897
|
+
baseUrl,
|
|
2898
|
+
organizationId: status.organizationId,
|
|
2899
|
+
tenantId: explicitTenantOverride ? undefined : status.tenantId,
|
|
2900
|
+
tenantName,
|
|
2901
|
+
requestingProduct: REQUESTING_PRODUCT,
|
|
2902
|
+
fetchJson,
|
|
2903
|
+
fetchVoid
|
|
2904
|
+
};
|
|
2905
|
+
}
|
|
2906
|
+
async function getDiscoveryModels(options) {
|
|
2907
|
+
const context = await createAiHttpContext("llm-gateway", options);
|
|
2908
|
+
const extraHeaders = {
|
|
2909
|
+
"X-UiPath-LlmGateway-RequestingProduct": options.product,
|
|
2910
|
+
"X-UiPath-LlmGateway-RequestingFeature": options.feature
|
|
2911
|
+
};
|
|
2912
|
+
if (options.operationCode) {
|
|
2913
|
+
extraHeaders["X-UiPath-LlmGateway-OperationCode"] = options.operationCode;
|
|
2914
|
+
}
|
|
2915
|
+
const status = await getLoginStatusAsync({
|
|
2916
|
+
ensureTokenValidityMinutes: options.loginValidity
|
|
2917
|
+
});
|
|
2918
|
+
if (status.accessToken) {
|
|
2919
|
+
try {
|
|
2920
|
+
const claims = parseJWT(status.accessToken);
|
|
2921
|
+
if (typeof claims.sub === "string") {
|
|
2922
|
+
extraHeaders["X-UiPath-LlmGateway-UserId"] = claims.sub;
|
|
2923
|
+
}
|
|
2924
|
+
} catch {}
|
|
2925
|
+
}
|
|
2926
|
+
return context.fetchJson({
|
|
2927
|
+
method: "GET",
|
|
2928
|
+
path: "/api/discovery",
|
|
2929
|
+
extraHeaders
|
|
2930
|
+
});
|
|
2931
|
+
}
|
|
2932
|
+
var LLM_AS_JUDGE_DISCOVERY = {
|
|
2933
|
+
product: "guardrails",
|
|
2934
|
+
feature: "llm-as-judge-validator",
|
|
2935
|
+
operationCode: "Guardrails.LlmAsJudge.Debug"
|
|
2936
|
+
};
|
|
2937
|
+
async function getLlmJudgeModelIds(options) {
|
|
2938
|
+
const models = await getDiscoveryModels({
|
|
2939
|
+
...options,
|
|
2940
|
+
...LLM_AS_JUDGE_DISCOVERY
|
|
2941
|
+
});
|
|
2942
|
+
return models.filter((m) => !m.isBlockedByPolicy).map((m) => m.modelId);
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
// src/services/llm-judge-models-service.ts
|
|
2946
|
+
var JUDGE_MIDDLEWARE = "UiPathLLMAsJudgeMiddleware";
|
|
2947
|
+
async function loadLlmJudgeModels(fs7, projectPath) {
|
|
2948
|
+
const source = await readEntrySource(fs7, projectPath);
|
|
2949
|
+
if (source === undefined || !source.includes(JUDGE_MIDDLEWARE)) {
|
|
2950
|
+
return;
|
|
2951
|
+
}
|
|
2952
|
+
const [err, models] = await catchError(getLlmJudgeModelIds());
|
|
2953
|
+
if (err || models === undefined) {
|
|
2954
|
+
return;
|
|
2955
|
+
}
|
|
2956
|
+
return models;
|
|
2957
|
+
}
|
|
2958
|
+
async function readEntrySource(fs7, projectPath) {
|
|
2959
|
+
const entryPath = await resolveEntryPyPath2(fs7, projectPath);
|
|
2960
|
+
const [err, raw] = await catchError(fs7.readFile(fs7.path.join(projectPath, entryPath), "utf-8"));
|
|
2961
|
+
return err || raw == null ? undefined : raw;
|
|
2962
|
+
}
|
|
2963
|
+
async function resolveEntryPyPath2(fs7, projectPath) {
|
|
2964
|
+
const [, raw] = await catchError(fs7.readFile(fs7.path.join(projectPath, "langgraph.json"), "utf-8"));
|
|
2965
|
+
if (raw != null) {
|
|
2966
|
+
const [, parsed] = await catchError(Promise.resolve().then(() => JSON.parse(raw)));
|
|
2967
|
+
const config = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : undefined;
|
|
2968
|
+
const graphs = config?.graphs;
|
|
2969
|
+
if (typeof graphs === "object" && graphs !== null) {
|
|
2970
|
+
for (const spec of Object.values(graphs)) {
|
|
2971
|
+
if (typeof spec === "string") {
|
|
2972
|
+
const filePart = spec.split(":")[0]?.replace(/^\.\//, "").trim();
|
|
2973
|
+
if (filePart?.endsWith(".py")) {
|
|
2974
|
+
return filePart;
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
return "main.py";
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
// src/commands/review.ts
|
|
2984
|
+
var CODES = {
|
|
2985
|
+
success: "CodedAgentReview",
|
|
2986
|
+
invalidChecks: "CodedAgentReviewInvalidChecks",
|
|
2987
|
+
failed: "CodedAgentReviewFailed"
|
|
2988
|
+
};
|
|
2989
|
+
var ZERO_BY_CATEGORY = {
|
|
2990
|
+
evals: 0,
|
|
2991
|
+
schema: 0,
|
|
2992
|
+
tools: 0,
|
|
2993
|
+
guardrails: 0,
|
|
2994
|
+
code: 0,
|
|
2995
|
+
general: 0
|
|
2996
|
+
};
|
|
2997
|
+
var REVIEW_EXAMPLES = [
|
|
2998
|
+
{
|
|
2999
|
+
Description: "Review a project at a specific path",
|
|
3000
|
+
Command: "uip codedagent review ./my-coded-agent",
|
|
3001
|
+
Output: {
|
|
3002
|
+
Code: CODES.success,
|
|
3003
|
+
Data: {
|
|
3004
|
+
ProjectDir: "./my-coded-agent",
|
|
3005
|
+
Verdict: "PASS",
|
|
3006
|
+
Score: 100,
|
|
3007
|
+
Grade: "A+",
|
|
3008
|
+
Issues: [],
|
|
3009
|
+
Strengths: [],
|
|
3010
|
+
Stats: {
|
|
3011
|
+
errors: 0,
|
|
3012
|
+
warnings: 0,
|
|
3013
|
+
infos: 0,
|
|
3014
|
+
byCategory: { ...ZERO_BY_CATEGORY }
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
},
|
|
3019
|
+
{
|
|
3020
|
+
Description: "Run only the rules in specific categories (comma-separated)",
|
|
3021
|
+
Command: "uip codedagent review --checks code",
|
|
3022
|
+
Output: {
|
|
3023
|
+
Code: CODES.success,
|
|
3024
|
+
Data: {
|
|
3025
|
+
ProjectDir: ".",
|
|
3026
|
+
Verdict: "PASS",
|
|
3027
|
+
Score: 100,
|
|
3028
|
+
Grade: "A+",
|
|
3029
|
+
Issues: [],
|
|
3030
|
+
Strengths: [],
|
|
3031
|
+
Stats: {
|
|
3032
|
+
errors: 0,
|
|
3033
|
+
warnings: 0,
|
|
3034
|
+
infos: 0,
|
|
3035
|
+
byCategory: { ...ZERO_BY_CATEGORY }
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
];
|
|
3041
|
+
var registerReviewCommand = (program) => {
|
|
3042
|
+
program.command("review").description("Review a coded agent project against the deterministic rule set. " + "Returns a verdict (PASS/FAIL), score (0-100), grade (A+...F), and a list of issues.").argument("[path]", "Agent project directory", ".").option("--checks <categories>", `Comma-separated rule categories to run (default: all). Known categories: ${listCategories().join(", ")}.`).examples(REVIEW_EXAMPLES).trackedAction(processContext, async (targetPath, options) => {
|
|
3043
|
+
const fs7 = getFileSystem();
|
|
3044
|
+
const projectPath = targetPath;
|
|
3045
|
+
let categories;
|
|
3046
|
+
if (options.checks !== undefined) {
|
|
3047
|
+
const parts = options.checks.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3048
|
+
if (parts.length === 0) {
|
|
3049
|
+
const knownCategories = listCategories().join(", ");
|
|
3050
|
+
OutputFormatter.error({
|
|
3051
|
+
Result: RESULTS.Failure,
|
|
3052
|
+
Code: CODES.invalidChecks,
|
|
3053
|
+
Message: "`--checks` requires at least one category.",
|
|
3054
|
+
Instructions: `Known categories: ${knownCategories}. Omit --checks to run all rules.`
|
|
3055
|
+
});
|
|
3056
|
+
processContext.exit(1);
|
|
3057
|
+
return;
|
|
3058
|
+
}
|
|
3059
|
+
categories = parts;
|
|
3060
|
+
}
|
|
3061
|
+
const llmJudgeModels = await loadLlmJudgeModels(fs7, projectPath);
|
|
3062
|
+
const [err, outcome] = await catchError(reviewCodedAgent(projectPath, { categories, llmJudgeModels }, fs7));
|
|
3063
|
+
if (err) {
|
|
3064
|
+
OutputFormatter.error({
|
|
3065
|
+
Result: RESULTS.Failure,
|
|
3066
|
+
Code: CODES.failed,
|
|
3067
|
+
Message: `Review failed: ${extractErrorMessageSync(err)}`,
|
|
3068
|
+
Instructions: "Verify the project path exists and is a readable agent project. " + "Run `uip codedagent review --help` for usage."
|
|
3069
|
+
});
|
|
3070
|
+
processContext.exit(1);
|
|
3071
|
+
return;
|
|
3072
|
+
}
|
|
3073
|
+
if (!outcome.ok) {
|
|
3074
|
+
const knownCategories = listCategories().join(", ");
|
|
3075
|
+
OutputFormatter.error({
|
|
3076
|
+
Result: RESULTS.Failure,
|
|
3077
|
+
Code: CODES.invalidChecks,
|
|
3078
|
+
Message: `Unknown category value(s): ${outcome.unknown.join(", ")}.`,
|
|
3079
|
+
Instructions: `Known categories: ${knownCategories}.`
|
|
3080
|
+
});
|
|
3081
|
+
processContext.exit(1);
|
|
3082
|
+
return;
|
|
3083
|
+
}
|
|
3084
|
+
const { result } = outcome;
|
|
3085
|
+
OutputFormatter.success({
|
|
3086
|
+
Result: RESULTS.Success,
|
|
3087
|
+
Code: CODES.success,
|
|
3088
|
+
Data: {
|
|
3089
|
+
ProjectDir: projectPath,
|
|
3090
|
+
Verdict: result.verdict,
|
|
3091
|
+
Score: result.score,
|
|
3092
|
+
Grade: result.grade,
|
|
3093
|
+
Issues: result.issues,
|
|
3094
|
+
Strengths: result.strengths,
|
|
3095
|
+
Stats: result.stats
|
|
3096
|
+
}
|
|
3097
|
+
});
|
|
3098
|
+
});
|
|
3099
|
+
};
|
|
3100
|
+
|
|
3101
|
+
// src/telemetry-events.ts
|
|
3102
|
+
var CodedAgentsTelemetryEvents = {
|
|
3103
|
+
DefaultAction: "CodedAgents.DefaultAction",
|
|
3104
|
+
Setup: "CodedAgents.Setup",
|
|
3105
|
+
Exec: "CodedAgents.Exec",
|
|
3106
|
+
Help: "CodedAgents.Help"
|
|
3107
|
+
};
|
|
3108
|
+
|
|
3109
|
+
// src/tool.ts
|
|
3110
|
+
var PROJECT_ID_ENV_NAME = "UIPATH_PROJECT_ID";
|
|
3111
|
+
var PROJECT_ID_INSTRUCTIONS = [
|
|
3112
|
+
"Create or open a Coded Agent project in Studio Web, copy its project ID, then set it before pushing:",
|
|
3113
|
+
` echo "${PROJECT_ID_ENV_NAME}=<id>" >> .env`,
|
|
3114
|
+
" uip codedagent push"
|
|
3115
|
+
].join(`
|
|
3116
|
+
`);
|
|
3117
|
+
var metadata = {
|
|
3118
|
+
name: "codedagent-tool",
|
|
3119
|
+
version: package_default.version,
|
|
3120
|
+
description: "Build, run, deploy, and manage AI Agents.",
|
|
3121
|
+
commandPrefix: "codedagent"
|
|
3122
|
+
};
|
|
3123
|
+
var { executeCommand } = createExecuteCommand({
|
|
3124
|
+
telemetryEvent: CodedAgentsTelemetryEvents.Exec,
|
|
3125
|
+
commandRules: COMMAND_RULES,
|
|
3126
|
+
commandPreflight: codedAgentCommandPreflight,
|
|
3127
|
+
requiredEnvironmentVariables: [
|
|
3128
|
+
{
|
|
3129
|
+
name: PROJECT_ID_ENV_NAME,
|
|
3130
|
+
commands: ["push"],
|
|
3131
|
+
instructions: PROJECT_ID_INSTRUCTIONS,
|
|
3132
|
+
skipWhenHelp: true
|
|
3133
|
+
}
|
|
3134
|
+
]
|
|
3135
|
+
});
|
|
3136
|
+
var registerSetupCommand = createSetupCommand({
|
|
3137
|
+
telemetryEvent: CodedAgentsTelemetryEvents.Setup,
|
|
3138
|
+
successCode: "CodedAgentsSetup"
|
|
3139
|
+
});
|
|
3140
|
+
function isHelpRequest(args) {
|
|
3141
|
+
return args.includes("--help") || args.includes("-h");
|
|
3142
|
+
}
|
|
3143
|
+
function isCodedAgentShipCommand(name) {
|
|
3144
|
+
return name === "deploy" || name === "publish";
|
|
3145
|
+
}
|
|
3146
|
+
function hasFlag(args, ...flags) {
|
|
3147
|
+
return args.some((arg) => flags.includes(arg));
|
|
3148
|
+
}
|
|
3149
|
+
function getCodedAgentShipTarget(name, args) {
|
|
3150
|
+
const suffix = name === "publish" ? "feed" : "deployment";
|
|
3151
|
+
if (hasFlag(args, "--my-workspace", "-w")) {
|
|
3152
|
+
return `personal_workspace_coded_agent_${suffix}`;
|
|
3153
|
+
}
|
|
3154
|
+
if (hasFlag(args, "--tenant")) {
|
|
3155
|
+
return `tenant_coded_agent_${suffix}`;
|
|
3156
|
+
}
|
|
3157
|
+
return `coded_agent_${suffix}`;
|
|
3158
|
+
}
|
|
3159
|
+
var registerWhitelistedCommand = (program, name, description) => {
|
|
3160
|
+
program.command(name, { hidden: false }).description(description).helpOption(false).allowUnknownOption(true).allowExcessArguments(true).argument("[args...]").trackedAction(processContext, async (args = []) => {
|
|
3161
|
+
const result = await executeCommand(["_", "_", "exec", name, ...args], {});
|
|
3162
|
+
if (result.ok && isCodedAgentShipCommand(name) && !isHelpRequest(args)) {
|
|
3163
|
+
trackShipSucceeded({
|
|
3164
|
+
ship_kind: name,
|
|
3165
|
+
target: getCodedAgentShipTarget(name, args),
|
|
3166
|
+
project_type: "coded_agent",
|
|
3167
|
+
command_name: `uip.codedagent.${name}`,
|
|
3168
|
+
artifact_correlation_key: process.env[PROJECT_ID_ENV_NAME]
|
|
3169
|
+
});
|
|
3170
|
+
}
|
|
3171
|
+
});
|
|
3172
|
+
};
|
|
3173
|
+
var registerCommands = async (program) => {
|
|
3174
|
+
registerSetupCommand(program);
|
|
3175
|
+
registerReviewCommand(program);
|
|
3176
|
+
for (const { name, description } of WHITELISTED_COMMANDS) {
|
|
3177
|
+
registerWhitelistedCommand(program, name, description);
|
|
3178
|
+
}
|
|
3179
|
+
program.showHelpAfterError(false);
|
|
3180
|
+
};
|
|
3181
|
+
|
|
3182
|
+
export { metadata, registerCommands };
|
|
3183
|
+
|
|
3184
|
+
//# debugId=BD6D1CCB31FE2BEA64756E2164756E21
|