@tutti-os/nexight-devops 0.6.1 → 0.6.2
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/cli.js +67 -37
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -6,17 +6,6 @@ import { resolve as resolve4 } from "path";
|
|
|
6
6
|
import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "url";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
|
|
9
|
-
// src/constants/admin.ts
|
|
10
|
-
var DEFAULT_ADMIN_API_ORIGIN = "https://tutti.sh";
|
|
11
|
-
|
|
12
|
-
// src/lib/admin-computer.ts
|
|
13
|
-
import { randomUUID } from "crypto";
|
|
14
|
-
import { createWriteStream } from "fs";
|
|
15
|
-
import { link, mkdir, rename, stat, unlink } from "fs/promises";
|
|
16
|
-
import { basename, dirname, join, resolve } from "path";
|
|
17
|
-
import { Readable } from "stream";
|
|
18
|
-
import { pipeline } from "stream/promises";
|
|
19
|
-
|
|
20
9
|
// src/lib/errors.ts
|
|
21
10
|
var CliError = class extends Error {
|
|
22
11
|
exitCode;
|
|
@@ -45,7 +34,36 @@ var toExitCode = (error) => {
|
|
|
45
34
|
return 1;
|
|
46
35
|
};
|
|
47
36
|
|
|
37
|
+
// src/lib/ppe-lane.ts
|
|
38
|
+
var PPE_LANE_MAX_LENGTH = 38;
|
|
39
|
+
var PPE_LANE_PATTERN = /^[a-z0-9](?:[-a-z0-9]*[a-z0-9])?$/;
|
|
40
|
+
var validatePpeLaneName = (value) => {
|
|
41
|
+
const lane = value.trim();
|
|
42
|
+
if (!lane) {
|
|
43
|
+
throw new CliError("PPE lane name is required", 1);
|
|
44
|
+
}
|
|
45
|
+
if (lane.length > PPE_LANE_MAX_LENGTH) {
|
|
46
|
+
throw new CliError(`PPE lane name must be ${PPE_LANE_MAX_LENGTH} characters or fewer`, 1);
|
|
47
|
+
}
|
|
48
|
+
if (!PPE_LANE_PATTERN.test(lane)) {
|
|
49
|
+
throw new CliError(
|
|
50
|
+
"PPE lane name must use lowercase letters, digits, or hyphens, and start and end with a letter or digit",
|
|
51
|
+
1
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return lane;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/constants/admin.ts
|
|
58
|
+
var DEFAULT_ADMIN_API_ORIGIN = "https://tutti.sh";
|
|
59
|
+
|
|
48
60
|
// src/lib/admin-computer.ts
|
|
61
|
+
import { randomUUID } from "crypto";
|
|
62
|
+
import { createWriteStream } from "fs";
|
|
63
|
+
import { link, mkdir, rename, stat, unlink } from "fs/promises";
|
|
64
|
+
import { basename, dirname, join, resolve } from "path";
|
|
65
|
+
import { Readable } from "stream";
|
|
66
|
+
import { pipeline } from "stream/promises";
|
|
49
67
|
var TUTTI_COMPUTER_API_BASE_PATH = "/api/desktop/v2/admin/tutti-v2/computers";
|
|
50
68
|
var TUTTI_COMPUTER_CONNECTION_WIRE_VALUE = {
|
|
51
69
|
online: 1,
|
|
@@ -127,6 +145,12 @@ var parseTuttiComputer = (value) => {
|
|
|
127
145
|
const owner = objectValue(root.owner);
|
|
128
146
|
return {
|
|
129
147
|
computerId: stringField(root, "computerId", "computer_id"),
|
|
148
|
+
...field(root, "canCollect", "can_collect") !== void 0 ? {
|
|
149
|
+
canCollect: booleanField(root, "canCollect", "can_collect"),
|
|
150
|
+
collectionBlockedReason: stringField(root, "collectionBlockedReason", "collection_blocked_reason"),
|
|
151
|
+
computerKind: stringField(root, "computerKind", "computer_kind"),
|
|
152
|
+
computerVersion: stringField(root, "computerVersion", "computer_version")
|
|
153
|
+
} : {},
|
|
130
154
|
displayName: stringField(root, "displayName", "display_name"),
|
|
131
155
|
reportedName: stringField(root, "reportedName", "reported_name"),
|
|
132
156
|
owner: {
|
|
@@ -356,6 +380,9 @@ var AdminApi = class {
|
|
|
356
380
|
constructor(runtime, fetchImpl = fetch) {
|
|
357
381
|
this.runtime = runtime;
|
|
358
382
|
this.fetchImpl = fetchImpl;
|
|
383
|
+
if (runtime.ppeLane !== void 0) {
|
|
384
|
+
this.runtime = { ...runtime, ppeLane: validatePpeLaneName(runtime.ppeLane) };
|
|
385
|
+
}
|
|
359
386
|
}
|
|
360
387
|
async request(method, path, body) {
|
|
361
388
|
const headers = {
|
|
@@ -364,6 +391,9 @@ var AdminApi = class {
|
|
|
364
391
|
if (this.runtime.token) {
|
|
365
392
|
headers.authorization = `Bearer ${this.runtime.token}`;
|
|
366
393
|
}
|
|
394
|
+
if (this.runtime.ppeLane) {
|
|
395
|
+
headers["x-zk-ppe-lane"] = this.runtime.ppeLane;
|
|
396
|
+
}
|
|
367
397
|
let payload;
|
|
368
398
|
if (body !== void 0) {
|
|
369
399
|
headers["content-type"] = "application/json";
|
|
@@ -372,9 +402,22 @@ var AdminApi = class {
|
|
|
372
402
|
const response = await this.fetchImpl(`${this.runtime.apiOrigin}${path}`, {
|
|
373
403
|
method,
|
|
374
404
|
headers,
|
|
375
|
-
body: payload
|
|
405
|
+
body: payload,
|
|
406
|
+
...this.runtime.ppeLane ? { redirect: "error" } : {}
|
|
376
407
|
});
|
|
377
408
|
const parsed = await parseResponseBody(response);
|
|
409
|
+
if (this.runtime.ppeLane) {
|
|
410
|
+
const hit = response.headers.get("x-zk-ppe-lane-hit");
|
|
411
|
+
const fallback = response.headers.get("x-tutti-server-route-fallback");
|
|
412
|
+
if (hit !== this.runtime.ppeLane || fallback) {
|
|
413
|
+
const detail = adminApiErrorDetail(parsed);
|
|
414
|
+
throw new AdminApiError(
|
|
415
|
+
`PPE \u8DEF\u7531\u4E0D\u5339\u914D: \u8BF7\u6C42 ${this.runtime.ppeLane}\uFF0C\u5B9E\u9645 ${hit || "\u672A\u786E\u8BA4\u6CF3\u9053\uFF08\u6B63\u5F0F\u73AF\u5883\u56DE\u843D\u6216\u7F3A\u5C11\u547D\u4E2D\u54CD\u5E94\u5934\uFF09"}${fallback ? `\uFF0CServer \u56DE\u843D\u539F\u56E0 ${fallback}` : ""}; ${method} ${path}; HTTP ${response.status}${detail ? `: ${detail}` : ""}`,
|
|
416
|
+
response.status,
|
|
417
|
+
parsed
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
378
421
|
if (!response.ok) {
|
|
379
422
|
const detail = adminApiErrorDetail(parsed);
|
|
380
423
|
throw new AdminApiError(
|
|
@@ -658,7 +701,7 @@ var logger = {
|
|
|
658
701
|
};
|
|
659
702
|
|
|
660
703
|
// src/commands/admin/shared.ts
|
|
661
|
-
var addAdminConnectionOptions = (command) => command.option("--admin-url <url>", "management API URL or admin.tutti.sh page URL").option("--json", "print the response as JSON without CLI decorations");
|
|
704
|
+
var addAdminConnectionOptions = (command) => command.option("--admin-url <url>", "management API URL or admin.tutti.sh page URL").option("--ppe-lane <lane>", "route every management request to this exact PPE lane").option("--json", "print the response as JSON without CLI decorations");
|
|
662
705
|
var resolveAdminContext = async (options = {}, tokenOverride) => {
|
|
663
706
|
const config = await loadConfig();
|
|
664
707
|
const apiOrigin = normalizeAdminApiOrigin(options.adminUrl ?? config.admin?.apiOrigin);
|
|
@@ -667,7 +710,7 @@ var resolveAdminContext = async (options = {}, tokenOverride) => {
|
|
|
667
710
|
throw new CliError("\u7BA1\u7406\u540E\u53F0 Token \u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u8FD0\u884C nexight-devops admin auth login", 1);
|
|
668
711
|
}
|
|
669
712
|
return {
|
|
670
|
-
api: new AdminApi({ apiOrigin, token }),
|
|
713
|
+
api: new AdminApi({ apiOrigin, token, ppeLane: options.ppeLane }),
|
|
671
714
|
apiOrigin,
|
|
672
715
|
config,
|
|
673
716
|
token
|
|
@@ -852,6 +895,14 @@ var resolveRuntimeLogRecallSelection = (input2, now = /* @__PURE__ */ new Date()
|
|
|
852
895
|
var isUUID = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
|
|
853
896
|
value
|
|
854
897
|
);
|
|
898
|
+
var assertComputerCollectable = (computer) => {
|
|
899
|
+
if (computer.canCollect === false) {
|
|
900
|
+
throw new CliError(`Computer ${computer.computerId} \u65E0\u6CD5\u91C7\u96C6\u65E5\u5FD7: ${computer.collectionBlockedReason || "\u670D\u52A1\u7AEF\u672A\u63D0\u4F9B\u963B\u585E\u539F\u56E0"}`, 1);
|
|
901
|
+
}
|
|
902
|
+
if (computer.canCollect === void 0 && computer.connectionStatus !== "online") {
|
|
903
|
+
throw new CliError(`Computer ${computer.computerId} \u5F53\u524D\u4E0D\u5728\u7EBF\uFF0C\u65E0\u6CD5\u53EC\u56DE\u65E5\u5FD7`, 1);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
855
906
|
var computerNameMatches = (computer, selector) => {
|
|
856
907
|
const expected = selector.trim().toLowerCase();
|
|
857
908
|
return [computer.displayName, computer.reportedName].map((value) => value.trim().toLowerCase()).includes(expected);
|
|
@@ -863,9 +914,7 @@ var resolveOnlineTuttiComputer = async (api, selector) => {
|
|
|
863
914
|
}
|
|
864
915
|
if (isUUID(normalized)) {
|
|
865
916
|
const computer = await api.getTuttiComputer(normalized);
|
|
866
|
-
|
|
867
|
-
throw new CliError(`Computer ${normalized} \u5F53\u524D\u4E0D\u5728\u7EBF\uFF0C\u65E0\u6CD5\u53EC\u56DE\u65E5\u5FD7`, 1);
|
|
868
|
-
}
|
|
917
|
+
assertComputerCollectable(computer);
|
|
869
918
|
return computer;
|
|
870
919
|
}
|
|
871
920
|
const matches = [];
|
|
@@ -977,6 +1026,7 @@ var recallComputerRuntimeLogs = async (input2, dependencies) => {
|
|
|
977
1026
|
const selector = input2.ownerQuery ? `\u7528\u6237\u540D/\u90AE\u7BB1 ${input2.ownerQuery}` : `Computer ${input2.computer}`;
|
|
978
1027
|
progress(`\u6B63\u5728\u89E3\u6790\u552F\u4E00\u5728\u7EBF Computer: ${selector}`);
|
|
979
1028
|
const computer = await resolveOnlineTuttiComputerForLogs(dependencies.api, input2);
|
|
1029
|
+
assertComputerCollectable(computer);
|
|
980
1030
|
progress(
|
|
981
1031
|
`\u5DF2\u5339\u914D ${computer.displayName || computer.reportedName} (${computer.computerId})\uFF0Cdesktopd ${computer.desktopdVersion || "\u672A\u77E5"}`
|
|
982
1032
|
);
|
|
@@ -3283,26 +3333,6 @@ var pollWorkflowRun = async (input2) => {
|
|
|
3283
3333
|
throw new CliError(`Timed out waiting for workflow run in ${input2.repoFullName}`, 1);
|
|
3284
3334
|
};
|
|
3285
3335
|
|
|
3286
|
-
// src/lib/ppe-lane.ts
|
|
3287
|
-
var PPE_LANE_MAX_LENGTH = 38;
|
|
3288
|
-
var PPE_LANE_PATTERN = /^[a-z0-9](?:[-a-z0-9]*[a-z0-9])?$/;
|
|
3289
|
-
var validatePpeLaneName = (value) => {
|
|
3290
|
-
const lane = value.trim();
|
|
3291
|
-
if (!lane) {
|
|
3292
|
-
throw new CliError("PPE lane name is required", 1);
|
|
3293
|
-
}
|
|
3294
|
-
if (lane.length > PPE_LANE_MAX_LENGTH) {
|
|
3295
|
-
throw new CliError(`PPE lane name must be ${PPE_LANE_MAX_LENGTH} characters or fewer`, 1);
|
|
3296
|
-
}
|
|
3297
|
-
if (!PPE_LANE_PATTERN.test(lane)) {
|
|
3298
|
-
throw new CliError(
|
|
3299
|
-
"PPE lane name must use lowercase letters, digits, or hyphens, and start and end with a letter or digit",
|
|
3300
|
-
1
|
|
3301
|
-
);
|
|
3302
|
-
}
|
|
3303
|
-
return lane;
|
|
3304
|
-
};
|
|
3305
|
-
|
|
3306
3336
|
// src/lib/prerequisites.ts
|
|
3307
3337
|
var ensureCommandInstalled = async (command, installHint) => {
|
|
3308
3338
|
const result = await runCommand(command, ["--version"]);
|