@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 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
- if (computer.connectionStatus !== "online") {
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"]);