zd-agent-cli 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/cli.js +85 -0
- package/dist/cmds/auth-check.js +63 -0
- package/dist/cmds/auth-login.js +38 -0
- package/dist/cmds/doctor.js +76 -0
- package/dist/cmds/queue-list.js +32 -0
- package/dist/cmds/queue-read.js +44 -0
- package/dist/cmds/search-tickets.js +23 -0
- package/dist/cmds/ticket-read.js +52 -0
- package/dist/core/api.js +327 -0
- package/dist/core/automation.js +149 -0
- package/dist/core/browser-cdp.js +285 -0
- package/dist/core/config.js +287 -0
- package/dist/core/constants.js +13 -0
- package/dist/core/dom.js +360 -0
- package/dist/core/facade.js +2 -0
- package/dist/core/index.js +58 -0
- package/dist/core/runtime.js +316 -0
- package/dist/core/storage.js +114 -0
- package/dist/core/util.js +42 -0
- package/dist/types.js +2 -0
- package/package.json +57 -0
- package/zendesk.config.example.json +28 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.sleep = sleep;
|
|
40
|
+
exports.normalizeHttpUrl = normalizeHttpUrl;
|
|
41
|
+
exports.parsePort = parsePort;
|
|
42
|
+
exports.buildCdpUrlWithPort = buildCdpUrlWithPort;
|
|
43
|
+
exports.getWsEndpoint = getWsEndpoint;
|
|
44
|
+
exports.isCdpReachable = isCdpReachable;
|
|
45
|
+
exports.launchChromeForCdp = launchChromeForCdp;
|
|
46
|
+
exports.checkCdpOwnership = checkCdpOwnership;
|
|
47
|
+
exports.ensureCdp = ensureCdp;
|
|
48
|
+
exports.prepareInteractionContext = prepareInteractionContext;
|
|
49
|
+
const fs_1 = __importDefault(require("fs"));
|
|
50
|
+
const child_process_1 = require("child_process");
|
|
51
|
+
const path_1 = __importDefault(require("path"));
|
|
52
|
+
const get_port_1 = __importStar(require("get-port"));
|
|
53
|
+
function sleep(ms) {
|
|
54
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
55
|
+
}
|
|
56
|
+
function normalizeHttpUrl(cdpUrl) {
|
|
57
|
+
if (cdpUrl.startsWith('http://') || cdpUrl.startsWith('https://')) {
|
|
58
|
+
return cdpUrl;
|
|
59
|
+
}
|
|
60
|
+
return `http://${cdpUrl}`;
|
|
61
|
+
}
|
|
62
|
+
function parsePort(cdpUrl) {
|
|
63
|
+
const normalized = normalizeHttpUrl(cdpUrl);
|
|
64
|
+
const url = new URL(normalized);
|
|
65
|
+
const port = Number(url.port || '9222');
|
|
66
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
67
|
+
throw new Error(`Invalid CDP URL: ${cdpUrl}`);
|
|
68
|
+
}
|
|
69
|
+
return port;
|
|
70
|
+
}
|
|
71
|
+
function buildCdpUrlWithPort(cdpUrl, port) {
|
|
72
|
+
const normalized = normalizeHttpUrl(cdpUrl);
|
|
73
|
+
const url = new URL(normalized);
|
|
74
|
+
url.port = String(port);
|
|
75
|
+
return `${url.protocol}//${url.hostname}:${port}`;
|
|
76
|
+
}
|
|
77
|
+
async function getWsEndpoint(cdpUrl) {
|
|
78
|
+
const base = normalizeHttpUrl(cdpUrl);
|
|
79
|
+
const versionUrl = new URL('/json/version', base).toString();
|
|
80
|
+
const response = await fetch(versionUrl);
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
throw new Error(`CDP endpoint returned ${response.status} at ${versionUrl}`);
|
|
83
|
+
}
|
|
84
|
+
const json = await response.json();
|
|
85
|
+
if (!json.webSocketDebuggerUrl) {
|
|
86
|
+
throw new Error(`No webSocketDebuggerUrl at ${versionUrl}`);
|
|
87
|
+
}
|
|
88
|
+
return json.webSocketDebuggerUrl;
|
|
89
|
+
}
|
|
90
|
+
async function isCdpReachable(cdpUrl) {
|
|
91
|
+
try {
|
|
92
|
+
await getWsEndpoint(cdpUrl);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
catch (_) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function waitForCdp(cdpUrl, launchTimeoutMs, pollMs) {
|
|
100
|
+
const started = Date.now();
|
|
101
|
+
while (Date.now() - started < launchTimeoutMs) {
|
|
102
|
+
if (await isCdpReachable(cdpUrl)) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
await sleep(pollMs);
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
function launchChromeForCdp(profileDir, port) {
|
|
110
|
+
fs_1.default.mkdirSync(profileDir, { recursive: true });
|
|
111
|
+
const chromeArgs = [
|
|
112
|
+
`--user-data-dir=${profileDir}`,
|
|
113
|
+
`--remote-debugging-port=${port}`,
|
|
114
|
+
'--no-first-run',
|
|
115
|
+
'--no-default-browser-check',
|
|
116
|
+
'--disable-search-engine-choice-screen'
|
|
117
|
+
];
|
|
118
|
+
const child = (0, child_process_1.spawn)('open', ['-na', 'Google Chrome', '--args', ...chromeArgs], { stdio: 'ignore', detached: true });
|
|
119
|
+
child.unref();
|
|
120
|
+
}
|
|
121
|
+
function getListenerPid(port) {
|
|
122
|
+
try {
|
|
123
|
+
const raw = (0, child_process_1.execFileSync)('lsof', ['-n', '-P', `-iTCP:${port}`, '-sTCP:LISTEN', '-t'], { encoding: 'utf8' }).trim();
|
|
124
|
+
const first = raw.split('\n').map((x) => x.trim()).find(Boolean);
|
|
125
|
+
return first || '';
|
|
126
|
+
}
|
|
127
|
+
catch (_) {
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function getProcessCommand(pid) {
|
|
132
|
+
if (!pid)
|
|
133
|
+
return '';
|
|
134
|
+
try {
|
|
135
|
+
return (0, child_process_1.execFileSync)('ps', ['-p', String(pid), '-o', 'command='], { encoding: 'utf8' }).trim();
|
|
136
|
+
}
|
|
137
|
+
catch (_) {
|
|
138
|
+
return '';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function extractUserDataDirArg(commandText = '') {
|
|
142
|
+
const text = String(commandText || '');
|
|
143
|
+
if (!text)
|
|
144
|
+
return '';
|
|
145
|
+
const m = text.match(/--user-data-dir=(?:"([^"]+)"|'([^']+)'|([^\s]+))/);
|
|
146
|
+
if (!m)
|
|
147
|
+
return '';
|
|
148
|
+
return m[1] || m[2] || m[3] || '';
|
|
149
|
+
}
|
|
150
|
+
function normalizePath(p) {
|
|
151
|
+
const raw = String(p || '').trim();
|
|
152
|
+
if (!raw)
|
|
153
|
+
return '';
|
|
154
|
+
try {
|
|
155
|
+
return fs_1.default.realpathSync(raw);
|
|
156
|
+
}
|
|
157
|
+
catch (_) {
|
|
158
|
+
return path_1.default.resolve(raw);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function checkCdpOwnership({ cdpUrl, expectedProfileDir }) {
|
|
162
|
+
const port = parsePort(cdpUrl);
|
|
163
|
+
const pid = getListenerPid(port);
|
|
164
|
+
const command = getProcessCommand(pid);
|
|
165
|
+
const actualRaw = extractUserDataDirArg(command);
|
|
166
|
+
const expected = normalizePath(expectedProfileDir);
|
|
167
|
+
const actual = normalizePath(actualRaw);
|
|
168
|
+
const matches = Boolean(expected && actual && expected === actual);
|
|
169
|
+
return {
|
|
170
|
+
port,
|
|
171
|
+
pid: pid || null,
|
|
172
|
+
actualProfileDir: actualRaw || null,
|
|
173
|
+
expectedProfileDir: expectedProfileDir || null,
|
|
174
|
+
matches,
|
|
175
|
+
command: command || null
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function findOwnedReachableCdp({ cdpUrl, expectedProfileDir, portSpan }) {
|
|
179
|
+
const startPort = parsePort(cdpUrl);
|
|
180
|
+
const endPort = startPort + Math.max(0, Number(portSpan) || 0);
|
|
181
|
+
for (let port = startPort; port <= endPort; port += 1) {
|
|
182
|
+
const candidateUrl = buildCdpUrlWithPort(cdpUrl, port);
|
|
183
|
+
if (!(await isCdpReachable(candidateUrl))) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const ownership = checkCdpOwnership({
|
|
187
|
+
cdpUrl: candidateUrl,
|
|
188
|
+
expectedProfileDir
|
|
189
|
+
});
|
|
190
|
+
if (ownership.matches) {
|
|
191
|
+
return {
|
|
192
|
+
cdpUrl: candidateUrl,
|
|
193
|
+
ownership
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
async function findFreePortInRange(cdpUrl, portSpan) {
|
|
200
|
+
const startPort = parsePort(cdpUrl);
|
|
201
|
+
const endPort = startPort + Math.max(0, Number(portSpan) || 0);
|
|
202
|
+
return (0, get_port_1.default)({ port: (0, get_port_1.portNumbers)(startPort, endPort) });
|
|
203
|
+
}
|
|
204
|
+
async function ensureCdp({ cdpUrl, profileDir, noLaunch = false, launchTimeoutMs = 20000, pollMs = 500, allowSharedCdp = false, autoPort = true, cdpPortSpan = 10 }) {
|
|
205
|
+
const span = Math.max(0, Number(cdpPortSpan) || 0);
|
|
206
|
+
if (await isCdpReachable(cdpUrl)) {
|
|
207
|
+
if (!allowSharedCdp) {
|
|
208
|
+
const ownership = checkCdpOwnership({ cdpUrl, expectedProfileDir: profileDir });
|
|
209
|
+
if (!ownership.matches) {
|
|
210
|
+
if (autoPort) {
|
|
211
|
+
const ownedExisting = await findOwnedReachableCdp({
|
|
212
|
+
cdpUrl,
|
|
213
|
+
expectedProfileDir: profileDir,
|
|
214
|
+
portSpan: span
|
|
215
|
+
});
|
|
216
|
+
if (ownedExisting) {
|
|
217
|
+
return {
|
|
218
|
+
launchedChrome: false,
|
|
219
|
+
cdpUrl: ownedExisting.cdpUrl,
|
|
220
|
+
wsEndpoint: await getWsEndpoint(ownedExisting.cdpUrl)
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
if (!noLaunch) {
|
|
224
|
+
const freePort = await findFreePortInRange(cdpUrl, span);
|
|
225
|
+
const launchUrl = buildCdpUrlWithPort(cdpUrl, freePort);
|
|
226
|
+
launchChromeForCdp(profileDir, freePort);
|
|
227
|
+
const up = await waitForCdp(launchUrl, launchTimeoutMs, pollMs);
|
|
228
|
+
if (up) {
|
|
229
|
+
return {
|
|
230
|
+
launchedChrome: true,
|
|
231
|
+
cdpUrl: launchUrl,
|
|
232
|
+
wsEndpoint: await getWsEndpoint(launchUrl)
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
throw new Error(`CDP endpoint did not come up at ${launchUrl}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const actual = ownership.actualProfileDir || 'unknown';
|
|
239
|
+
const pid = ownership.pid || 'unknown';
|
|
240
|
+
throw new Error(`CDP endpoint ${cdpUrl} is already in use by Chrome pid=${pid} with user-data-dir=${actual}. Expected ${profileDir}. ` +
|
|
241
|
+
'Stop that Chrome instance, use a different --cdp-url, pass --allow-shared-cdp, or keep auto port fallback enabled.');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return { launchedChrome: false, cdpUrl, wsEndpoint: await getWsEndpoint(cdpUrl) };
|
|
245
|
+
}
|
|
246
|
+
if (noLaunch) {
|
|
247
|
+
if (autoPort) {
|
|
248
|
+
const ownedExisting = await findOwnedReachableCdp({
|
|
249
|
+
cdpUrl,
|
|
250
|
+
expectedProfileDir: profileDir,
|
|
251
|
+
portSpan: span
|
|
252
|
+
});
|
|
253
|
+
if (ownedExisting) {
|
|
254
|
+
return {
|
|
255
|
+
launchedChrome: false,
|
|
256
|
+
cdpUrl: ownedExisting.cdpUrl,
|
|
257
|
+
wsEndpoint: await getWsEndpoint(ownedExisting.cdpUrl)
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
throw new Error(`CDP endpoint is not reachable at ${cdpUrl} and --no-launch is set.`);
|
|
262
|
+
}
|
|
263
|
+
const launchPort = autoPort ? await findFreePortInRange(cdpUrl, span) : parsePort(cdpUrl);
|
|
264
|
+
const launchUrl = buildCdpUrlWithPort(cdpUrl, launchPort);
|
|
265
|
+
launchChromeForCdp(profileDir, launchPort);
|
|
266
|
+
const up = await waitForCdp(launchUrl, launchTimeoutMs, pollMs);
|
|
267
|
+
if (up) {
|
|
268
|
+
return { launchedChrome: true, cdpUrl: launchUrl, wsEndpoint: await getWsEndpoint(launchUrl) };
|
|
269
|
+
}
|
|
270
|
+
throw new Error(`CDP endpoint did not come up at ${launchUrl}`);
|
|
271
|
+
}
|
|
272
|
+
async function prepareInteractionContext(page, uiWaitMs = 1000, opts = {}) {
|
|
273
|
+
const x = Number.isFinite(opts.x) ? opts.x : 40;
|
|
274
|
+
const y = Number.isFinite(opts.y) ? opts.y : 40;
|
|
275
|
+
await page
|
|
276
|
+
.evaluate(() => {
|
|
277
|
+
const active = document.activeElement;
|
|
278
|
+
if (active && typeof active.blur === 'function') {
|
|
279
|
+
active.blur();
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
.catch(() => undefined);
|
|
283
|
+
await page.click('body', { position: { x, y } }).catch(() => undefined);
|
|
284
|
+
await sleep(Math.max(120, Math.floor(uiWaitMs / 4)));
|
|
285
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.toAbsPath = toAbsPath;
|
|
7
|
+
exports.normalizeAgentPath = normalizeAgentPath;
|
|
8
|
+
exports.validateConfigContract = validateConfigContract;
|
|
9
|
+
exports.resolveQueueInput = resolveQueueInput;
|
|
10
|
+
exports.loadResolvedConfig = loadResolvedConfig;
|
|
11
|
+
exports.pickBool = pickBool;
|
|
12
|
+
exports.pickString = pickString;
|
|
13
|
+
exports.pickNumber = pickNumber;
|
|
14
|
+
exports.safeOptionSource = safeOptionSource;
|
|
15
|
+
const fs_1 = __importDefault(require("fs"));
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const util_1 = require("./util");
|
|
18
|
+
const CONFIG_BASENAMES = ['zendesk.config.json', 'zendesk.json'];
|
|
19
|
+
function asObject(value) {
|
|
20
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
21
|
+
}
|
|
22
|
+
function truthyString(value) {
|
|
23
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
24
|
+
return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
|
|
25
|
+
}
|
|
26
|
+
function resolveUp(startDir, fileName) {
|
|
27
|
+
let dir = path_1.default.resolve(startDir || process.cwd());
|
|
28
|
+
while (true) {
|
|
29
|
+
const candidate = path_1.default.join(dir, fileName);
|
|
30
|
+
if (fs_1.default.existsSync(candidate)) {
|
|
31
|
+
return candidate;
|
|
32
|
+
}
|
|
33
|
+
const parent = path_1.default.dirname(dir);
|
|
34
|
+
if (parent === dir) {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
dir = parent;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function findRepoRoot(startDir) {
|
|
41
|
+
let dir = path_1.default.resolve(startDir || process.cwd());
|
|
42
|
+
while (true) {
|
|
43
|
+
const gitDir = path_1.default.join(dir, '.git');
|
|
44
|
+
if (fs_1.default.existsSync(gitDir)) {
|
|
45
|
+
return dir;
|
|
46
|
+
}
|
|
47
|
+
const parent = path_1.default.dirname(dir);
|
|
48
|
+
if (parent === dir) {
|
|
49
|
+
return path_1.default.resolve(startDir || process.cwd());
|
|
50
|
+
}
|
|
51
|
+
dir = parent;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function resolveConfigPath(explicitPath, cwd) {
|
|
55
|
+
if (explicitPath) {
|
|
56
|
+
return path_1.default.resolve(cwd, explicitPath);
|
|
57
|
+
}
|
|
58
|
+
for (const basename of CONFIG_BASENAMES) {
|
|
59
|
+
const found = resolveUp(cwd, basename);
|
|
60
|
+
if (found) {
|
|
61
|
+
return found;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Fallback for npm-link/dev workflows: allow config in the CLI package root.
|
|
65
|
+
const packageRoot = path_1.default.resolve(__dirname, '..', '..');
|
|
66
|
+
for (const basename of CONFIG_BASENAMES) {
|
|
67
|
+
const candidate = path_1.default.join(packageRoot, basename);
|
|
68
|
+
if (fs_1.default.existsSync(candidate)) {
|
|
69
|
+
return candidate;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
function readConfigFile(filePath) {
|
|
75
|
+
if (!filePath) {
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
const raw = fs_1.default.readFileSync(filePath, 'utf8');
|
|
79
|
+
const parsed = JSON.parse(raw);
|
|
80
|
+
return asObject(parsed);
|
|
81
|
+
}
|
|
82
|
+
function toAbsPath(value, baseDir) {
|
|
83
|
+
const raw = String(value || '').trim();
|
|
84
|
+
if (!raw) {
|
|
85
|
+
return '';
|
|
86
|
+
}
|
|
87
|
+
return path_1.default.isAbsolute(raw) ? raw : path_1.default.resolve(baseDir, raw);
|
|
88
|
+
}
|
|
89
|
+
function normalizeAgentPath(rawPath, fallback = '') {
|
|
90
|
+
const base = String(rawPath || '').trim();
|
|
91
|
+
const selected = base || String(fallback || '').trim();
|
|
92
|
+
if (!selected) {
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
if (selected.startsWith('/')) {
|
|
96
|
+
return selected;
|
|
97
|
+
}
|
|
98
|
+
return `/${selected}`;
|
|
99
|
+
}
|
|
100
|
+
function normalizeQueues(rawQueues) {
|
|
101
|
+
const queues = asObject(rawQueues);
|
|
102
|
+
const out = {};
|
|
103
|
+
for (const [alias, value] of Object.entries(queues)) {
|
|
104
|
+
const row = asObject(value);
|
|
105
|
+
const name = (0, util_1.clean)(row.name || row.displayName || '');
|
|
106
|
+
const displayName = (0, util_1.clean)(row.displayName || row.name || '');
|
|
107
|
+
const queuePath = normalizeAgentPath(row.path || '');
|
|
108
|
+
if (!queuePath) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
out[alias] = {
|
|
112
|
+
name,
|
|
113
|
+
displayName,
|
|
114
|
+
path: queuePath,
|
|
115
|
+
team: (0, util_1.clean)(row.team || '')
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
function validateConfigContract(config = {}, queueConfig = {}) {
|
|
121
|
+
const issues = [];
|
|
122
|
+
const cfg = asObject(config);
|
|
123
|
+
const queuesRaw = asObject(cfg.queues);
|
|
124
|
+
const aliases = Object.keys(queuesRaw);
|
|
125
|
+
if (!(0, util_1.clean)(cfg.domain || '')) {
|
|
126
|
+
issues.push('Missing required field: domain');
|
|
127
|
+
}
|
|
128
|
+
const startPath = normalizeAgentPath(cfg.startPath || '');
|
|
129
|
+
if (!startPath) {
|
|
130
|
+
issues.push('Missing required field: startPath');
|
|
131
|
+
}
|
|
132
|
+
else if (!/^\/agent\//i.test(startPath)) {
|
|
133
|
+
issues.push(`Invalid startPath "${startPath}". startPath must begin with "/agent/".`);
|
|
134
|
+
}
|
|
135
|
+
if (!(0, util_1.clean)(cfg.defaultQueue || '')) {
|
|
136
|
+
issues.push('Missing required field: defaultQueue');
|
|
137
|
+
}
|
|
138
|
+
if (!aliases.length) {
|
|
139
|
+
issues.push('Missing required field: queues (object with at least one alias)');
|
|
140
|
+
}
|
|
141
|
+
for (const [alias, row] of Object.entries(queuesRaw)) {
|
|
142
|
+
const queuePath = normalizeAgentPath(asObject(row).path || '');
|
|
143
|
+
if (!queuePath) {
|
|
144
|
+
issues.push(`queues.${alias}.path is required`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (!/^\/agent\//i.test(queuePath)) {
|
|
148
|
+
issues.push(`queues.${alias}.path must begin with "/agent/" (got "${queuePath}")`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const defaultQueue = (0, util_1.clean)(queueConfig.defaultQueue || cfg.defaultQueue || '');
|
|
152
|
+
if (defaultQueue && !queueConfig.queues[defaultQueue]) {
|
|
153
|
+
issues.push(`defaultQueue "${defaultQueue}" is not defined in queues`);
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
ok: issues.length === 0,
|
|
157
|
+
issues
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function resolveQueueInput(rawInput, queueConfig) {
|
|
161
|
+
const requestedRaw = (0, util_1.clean)(rawInput || '');
|
|
162
|
+
const defaultQueue = (0, util_1.clean)(queueConfig.defaultQueue || '');
|
|
163
|
+
const requested = requestedRaw || defaultQueue;
|
|
164
|
+
const aliases = asObject(queueConfig.queues);
|
|
165
|
+
if (!requested) {
|
|
166
|
+
return {
|
|
167
|
+
queueName: '',
|
|
168
|
+
queueDisplayName: '',
|
|
169
|
+
queuePath: '',
|
|
170
|
+
alias: '',
|
|
171
|
+
team: ''
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (aliases[requested]) {
|
|
175
|
+
return {
|
|
176
|
+
queueName: aliases[requested].name || aliases[requested].displayName || requested,
|
|
177
|
+
queueDisplayName: aliases[requested].displayName || aliases[requested].name || '',
|
|
178
|
+
queuePath: aliases[requested].path || '',
|
|
179
|
+
alias: requested,
|
|
180
|
+
team: aliases[requested].team || ''
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const requestedLow = requested.toLowerCase();
|
|
184
|
+
for (const [alias, row] of Object.entries(aliases)) {
|
|
185
|
+
if (alias.toLowerCase() === requestedLow) {
|
|
186
|
+
return {
|
|
187
|
+
queueName: row.name || row.displayName || alias,
|
|
188
|
+
queueDisplayName: row.displayName || row.name || '',
|
|
189
|
+
queuePath: row.path || '',
|
|
190
|
+
alias,
|
|
191
|
+
team: row.team || ''
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
for (const [alias, row] of Object.entries(aliases)) {
|
|
196
|
+
const rowName = String(row.name || '').toLowerCase();
|
|
197
|
+
const rowDisplayName = String(row.displayName || '').toLowerCase();
|
|
198
|
+
if (rowName === requestedLow || rowDisplayName === requestedLow) {
|
|
199
|
+
return {
|
|
200
|
+
queueName: row.name || row.displayName || alias,
|
|
201
|
+
queueDisplayName: row.displayName || row.name || '',
|
|
202
|
+
queuePath: row.path || '',
|
|
203
|
+
alias,
|
|
204
|
+
team: row.team || ''
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
queueName: requested,
|
|
210
|
+
queueDisplayName: requested,
|
|
211
|
+
queuePath: '',
|
|
212
|
+
alias: '',
|
|
213
|
+
team: ''
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function loadResolvedConfig(options = {}) {
|
|
217
|
+
const cwd = path_1.default.resolve(options.cwd || process.cwd());
|
|
218
|
+
const explicitPath = options.configPath || process.env.ZENDESK_CONFIG || '';
|
|
219
|
+
const configPath = resolveConfigPath(explicitPath, cwd);
|
|
220
|
+
const configDir = configPath ? path_1.default.dirname(configPath) : cwd;
|
|
221
|
+
const repoRoot = configPath ? findRepoRoot(configDir) : findRepoRoot(cwd);
|
|
222
|
+
const config = readConfigFile(configPath);
|
|
223
|
+
const queueConfig = {
|
|
224
|
+
defaultQueue: (0, util_1.clean)(config.defaultQueue || ''),
|
|
225
|
+
queues: normalizeQueues(config.queues)
|
|
226
|
+
};
|
|
227
|
+
return {
|
|
228
|
+
repoRoot,
|
|
229
|
+
configPath,
|
|
230
|
+
configDir,
|
|
231
|
+
config,
|
|
232
|
+
queueConfig
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function pickBool({ cliValue, cliSource, envValue, configValue, fallback }) {
|
|
236
|
+
if (cliSource === 'cli') {
|
|
237
|
+
return Boolean(cliValue);
|
|
238
|
+
}
|
|
239
|
+
if (envValue !== undefined && envValue !== null && String(envValue).trim()) {
|
|
240
|
+
return truthyString(envValue);
|
|
241
|
+
}
|
|
242
|
+
if (configValue !== undefined && configValue !== null && String(configValue).trim()) {
|
|
243
|
+
return truthyString(configValue);
|
|
244
|
+
}
|
|
245
|
+
return Boolean(fallback);
|
|
246
|
+
}
|
|
247
|
+
function pickString({ cliValue, cliSource, envValue, configValue, fallback }) {
|
|
248
|
+
if (cliSource === 'cli') {
|
|
249
|
+
return String(cliValue || '').trim();
|
|
250
|
+
}
|
|
251
|
+
if (envValue !== undefined && envValue !== null && String(envValue).trim()) {
|
|
252
|
+
return String(envValue).trim();
|
|
253
|
+
}
|
|
254
|
+
if (configValue !== undefined && configValue !== null && String(configValue).trim()) {
|
|
255
|
+
return String(configValue).trim();
|
|
256
|
+
}
|
|
257
|
+
return String(fallback || '').trim();
|
|
258
|
+
}
|
|
259
|
+
function pickNumber({ cliValue, cliSource, envValue, configValue, fallback }) {
|
|
260
|
+
const resolve = (raw) => {
|
|
261
|
+
const n = Number(raw);
|
|
262
|
+
return Number.isFinite(n) ? n : NaN;
|
|
263
|
+
};
|
|
264
|
+
if (cliSource === 'cli') {
|
|
265
|
+
const n = resolve(cliValue);
|
|
266
|
+
if (Number.isFinite(n))
|
|
267
|
+
return n;
|
|
268
|
+
}
|
|
269
|
+
if (envValue !== undefined && envValue !== null && String(envValue).trim()) {
|
|
270
|
+
const n = resolve(envValue);
|
|
271
|
+
if (Number.isFinite(n))
|
|
272
|
+
return n;
|
|
273
|
+
}
|
|
274
|
+
if (configValue !== undefined && configValue !== null && String(configValue).trim()) {
|
|
275
|
+
const n = resolve(configValue);
|
|
276
|
+
if (Number.isFinite(n))
|
|
277
|
+
return n;
|
|
278
|
+
}
|
|
279
|
+
return Number(fallback);
|
|
280
|
+
}
|
|
281
|
+
function safeOptionSource(program, name) {
|
|
282
|
+
if (!program || typeof program.getOptionValueSource !== 'function') {
|
|
283
|
+
return '';
|
|
284
|
+
}
|
|
285
|
+
const src = program.getOptionValueSource(name);
|
|
286
|
+
return typeof src === 'string' ? src : '';
|
|
287
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_STORE_ROOT = exports.DEFAULT_UI_WAIT_MS = exports.DEFAULT_START_PATH = exports.DEFAULT_DOMAIN = exports.DEFAULT_PROFILE_DIR = exports.DEFAULT_CDP_URL = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
exports.DEFAULT_CDP_URL = 'http://127.0.0.1:9223';
|
|
9
|
+
exports.DEFAULT_PROFILE_DIR = path_1.default.join('.', 'output', 'zendesk', 'chrome-profile');
|
|
10
|
+
exports.DEFAULT_DOMAIN = '';
|
|
11
|
+
exports.DEFAULT_START_PATH = '/agent/filters';
|
|
12
|
+
exports.DEFAULT_UI_WAIT_MS = 1200;
|
|
13
|
+
exports.DEFAULT_STORE_ROOT = './output/zendesk';
|