@ait-co/console-cli 0.1.16 → 0.1.17

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/README.md CHANGED
@@ -99,3 +99,15 @@ Every command accepts `--json`. When set:
99
99
  ## Status
100
100
 
101
101
  `login`, `logout`, `whoami`, and `upgrade` are implemented end-to-end — `login` drives a real browser over CDP and `whoami` reads the live console member API. `deploy`, `logs`, `status` are next — see [TODO.md](./TODO.md). See the [organization landing page](https://apps-in-toss-community.github.io/) for the full roadmap.
102
+
103
+ ## Pre-commit hook
104
+
105
+ Optional but recommended. After cloning, activate the standard pre-commit hook (runs `biome check` on staged files):
106
+
107
+ ```sh
108
+ git config core.hooksPath .githooks
109
+ ```
110
+
111
+ This is a developer convenience for fast feedback before push. CI runs the same checks as the enforcement layer, so contributors who don't activate the hook will still see lint failures in their PR.
112
+
113
+ 선택 사항이지만 권장합니다. clone 후 표준 pre-commit hook을 활성화하면 staged 파일에 `biome check`가 자동으로 돕니다 (push 전에 빠른 피드백). 활성화하지 않아도 동일한 검사가 CI에서 실행되므로 PR 단계에서 lint 실패를 볼 수 있습니다.
package/dist/cli.mjs CHANGED
@@ -7,7 +7,8 @@ import { unzipSync } from "fflate";
7
7
  import { parse } from "yaml";
8
8
  import { imageSize } from "image-size";
9
9
  import { spawn } from "node:child_process";
10
- import { constants } from "node:fs";
10
+ import { constants, createReadStream } from "node:fs";
11
+ import { createHash } from "node:crypto";
11
12
  //#region src/api/http.ts
12
13
  var TossApiError = class extends Error {
13
14
  constructor(status, errorCode, reason, errorType) {
@@ -718,7 +719,8 @@ const ExitCode = {
718
719
  LoginCookieCaptureFailed: 16,
719
720
  ApiError: 17,
720
721
  UpgradeUnavailable: 20,
721
- UpgradeAlreadyLatest: 21
722
+ UpgradeAlreadyLatest: 21,
723
+ UpgradeChecksumFailed: 22
722
724
  };
723
725
  //#endregion
724
726
  //#region src/flush.ts
@@ -5459,6 +5461,9 @@ async function fetchLatestReleaseConditional(previousEtag) {
5459
5461
  etag
5460
5462
  };
5461
5463
  }
5464
+ function findSha256SumsAsset(release) {
5465
+ return release.assets.find((a) => a.name === "SHA256SUMS");
5466
+ }
5462
5467
  function versionFromTag(tag) {
5463
5468
  const at = tag.lastIndexOf("@");
5464
5469
  const candidate = at >= 0 ? tag.slice(at + 1) : tag;
@@ -5522,6 +5527,31 @@ function compareSemver(a, b) {
5522
5527
  return pa.pre > pb.pre ? 1 : -1;
5523
5528
  }
5524
5529
  //#endregion
5530
+ //#region src/sha256.ts
5531
+ function parseSha256Sums(text) {
5532
+ const out = /* @__PURE__ */ new Map();
5533
+ for (const rawLine of text.split(/\r?\n/)) {
5534
+ const line = rawLine.trim();
5535
+ if (line === "" || line.startsWith("#")) continue;
5536
+ const match = line.match(/^([0-9a-fA-F]{64})\s+\*?(.+)$/);
5537
+ if (!match) continue;
5538
+ const hash = match[1]?.toLowerCase();
5539
+ const name = match[2]?.trim();
5540
+ if (!hash || !name) continue;
5541
+ out.set(name, hash);
5542
+ }
5543
+ return out;
5544
+ }
5545
+ function sha256OfFile(path) {
5546
+ return new Promise((resolve, reject) => {
5547
+ const hash = createHash("sha256");
5548
+ const stream = createReadStream(path);
5549
+ stream.on("error", reject);
5550
+ stream.on("data", (chunk) => hash.update(chunk));
5551
+ stream.on("end", () => resolve(hash.digest("hex")));
5552
+ });
5553
+ }
5554
+ //#endregion
5525
5555
  //#region src/version.ts
5526
5556
  function resolveVersion() {
5527
5557
  try {
@@ -5529,7 +5559,7 @@ function resolveVersion() {
5529
5559
  if (typeof injected === "string" && injected.length > 0) return injected;
5530
5560
  } catch {}
5531
5561
  try {
5532
- return "0.1.16";
5562
+ return "0.1.17";
5533
5563
  } catch {}
5534
5564
  return "0.0.0-dev";
5535
5565
  }
@@ -5650,6 +5680,50 @@ const upgradeCommand = defineCommand({
5650
5680
  }, `Failed to download new binary: ${err.message}`);
5651
5681
  process.exit(ExitCode.NetworkError);
5652
5682
  }
5683
+ const sumsAsset = findSha256SumsAsset(release);
5684
+ if (!sumsAsset) {
5685
+ await unlink(stagingPath).catch(() => {});
5686
+ emitError({
5687
+ reason: "sums-missing",
5688
+ tag: release.tag_name
5689
+ }, `Release ${release.tag_name} has no SHA256SUMS asset. It may still be uploading; retry shortly.`);
5690
+ process.exit(ExitCode.UpgradeChecksumFailed);
5691
+ }
5692
+ let expected;
5693
+ let actual;
5694
+ try {
5695
+ const sumsRes = await fetch(sumsAsset.browser_download_url);
5696
+ if (!sumsRes.ok) throw new Error(`SHA256SUMS download failed: ${sumsRes.status} ${sumsRes.statusText}`);
5697
+ expected = parseSha256Sums(await sumsRes.text()).get(platform.assetName);
5698
+ actual = (await sha256OfFile(stagingPath)).toLowerCase();
5699
+ } catch (err) {
5700
+ await unlink(stagingPath).catch(() => {});
5701
+ emitError({
5702
+ reason: "sums-fetch-failed",
5703
+ message: err.message
5704
+ }, `Failed to verify checksum: ${err.message}`);
5705
+ process.exit(ExitCode.UpgradeChecksumFailed);
5706
+ }
5707
+ if (!expected) {
5708
+ await unlink(stagingPath).catch(() => {});
5709
+ emitError({
5710
+ reason: "sums-no-entry",
5711
+ assetName: platform.assetName,
5712
+ tag: release.tag_name
5713
+ }, `SHA256SUMS for ${release.tag_name} has no entry for ${platform.assetName}.`);
5714
+ process.exit(ExitCode.UpgradeChecksumFailed);
5715
+ }
5716
+ if (expected.toLowerCase() !== actual) {
5717
+ await unlink(stagingPath).catch(() => {});
5718
+ emitError({
5719
+ reason: "sha256-mismatch",
5720
+ assetName: platform.assetName,
5721
+ expected: expected.toLowerCase(),
5722
+ actual
5723
+ }, `Checksum mismatch for ${platform.assetName}: expected ${expected.toLowerCase()}, got ${actual}.`);
5724
+ process.exit(ExitCode.UpgradeChecksumFailed);
5725
+ }
5726
+ if (!args.json) process.stdout.write("Checksum OK.\n");
5653
5727
  try {
5654
5728
  if (process.platform === "win32") {
5655
5729
  await rename(exePath, `${exePath}.old`);