@ait-co/console-cli 0.1.35 → 0.1.36
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.mjs +120 -19
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -7,8 +7,8 @@ import { isMap, parse, parseDocument } from "yaml";
|
|
|
7
7
|
import { unzipSync } from "fflate";
|
|
8
8
|
import { checkbox, confirm, editor, input, password, select } from "@inquirer/prompts";
|
|
9
9
|
import { imageSize } from "image-size";
|
|
10
|
-
import { execFile, spawn } from "node:child_process";
|
|
11
|
-
import { constants, createReadStream, existsSync } from "node:fs";
|
|
10
|
+
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
11
|
+
import { constants, createReadStream, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { createInterface } from "node:readline/promises";
|
|
13
13
|
import { promisify } from "node:util";
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
@@ -682,14 +682,15 @@ async function postBundleTestPush(workspaceId, miniAppId, deploymentId, cookies,
|
|
|
682
682
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
683
683
|
return raw;
|
|
684
684
|
}
|
|
685
|
-
async function fetchBundleTestLinks(workspaceId, miniAppId, cookies, opts = {}) {
|
|
685
|
+
async function fetchBundleTestLinks(workspaceId, miniAppId, deploymentId, cookies, opts = {}) {
|
|
686
686
|
const raw = await requestConsoleApi({
|
|
687
|
-
url: `${BASE$4}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/test-links`,
|
|
687
|
+
url: `${BASE$4}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/test-links?deploymentId=${encodeURIComponent(deploymentId)}`,
|
|
688
688
|
cookies,
|
|
689
689
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
690
690
|
});
|
|
691
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
692
|
-
|
|
691
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected test-links shape for app=${miniAppId}`);
|
|
692
|
+
const data = raw;
|
|
693
|
+
return { linkUri: typeof data.linkUri === "string" ? data.linkUri : "" };
|
|
693
694
|
}
|
|
694
695
|
async function createMiniApp(workspaceId, payload, cookies, opts = {}) {
|
|
695
696
|
return normalizeCreateResult(await requestConsoleApi({
|
|
@@ -4527,7 +4528,7 @@ const bundlesCommand = defineCommand({
|
|
|
4527
4528
|
"test-links": defineCommand({
|
|
4528
4529
|
meta: {
|
|
4529
4530
|
name: "test-links",
|
|
4530
|
-
description: "Show
|
|
4531
|
+
description: "Show the test deep-link URL for a specific bundle."
|
|
4531
4532
|
},
|
|
4532
4533
|
args: {
|
|
4533
4534
|
id: {
|
|
@@ -4535,6 +4536,10 @@ const bundlesCommand = defineCommand({
|
|
|
4535
4536
|
description: "Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.",
|
|
4536
4537
|
required: false
|
|
4537
4538
|
},
|
|
4539
|
+
"deployment-id": {
|
|
4540
|
+
type: "string",
|
|
4541
|
+
description: "deploymentId of the bundle to fetch the test link for."
|
|
4542
|
+
},
|
|
4538
4543
|
workspace: {
|
|
4539
4544
|
type: "string",
|
|
4540
4545
|
description: "Workspace ID. Defaults to the selected workspace."
|
|
@@ -4546,6 +4551,15 @@ const bundlesCommand = defineCommand({
|
|
|
4546
4551
|
}
|
|
4547
4552
|
},
|
|
4548
4553
|
async run({ args }) {
|
|
4554
|
+
const deploymentId = typeof args["deployment-id"] === "string" ? args["deployment-id"] : "";
|
|
4555
|
+
if (deploymentId === "") {
|
|
4556
|
+
if (args.json) emitJson({
|
|
4557
|
+
ok: false,
|
|
4558
|
+
reason: "missing-deployment-id"
|
|
4559
|
+
});
|
|
4560
|
+
else process.stderr.write("app bundles test-links: --deployment-id <uuid> is required.\n");
|
|
4561
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
4562
|
+
}
|
|
4549
4563
|
const ctx = await resolveAppOrFail({
|
|
4550
4564
|
json: args.json,
|
|
4551
4565
|
appIdRaw: args.id,
|
|
@@ -4557,26 +4571,22 @@ const bundlesCommand = defineCommand({
|
|
|
4557
4571
|
printContextHeader(ctx, { json: args.json });
|
|
4558
4572
|
const { session, workspaceId } = ctx;
|
|
4559
4573
|
try {
|
|
4560
|
-
const
|
|
4574
|
+
const { linkUri } = await fetchBundleTestLinks(workspaceId, appId, deploymentId, session.cookies);
|
|
4561
4575
|
if (args.json) {
|
|
4562
4576
|
emitJson({
|
|
4563
4577
|
ok: true,
|
|
4564
4578
|
workspaceId,
|
|
4565
4579
|
appId,
|
|
4566
|
-
|
|
4580
|
+
deploymentId,
|
|
4581
|
+
linkUri
|
|
4567
4582
|
});
|
|
4568
4583
|
return exitAfterFlush(ExitCode.Ok);
|
|
4569
4584
|
}
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
process.stdout.write(`App ${appId} (ws ${workspaceId}): no test links available\n`);
|
|
4585
|
+
if (linkUri === "") {
|
|
4586
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): no test link available for deployment ${deploymentId}\n`);
|
|
4573
4587
|
return exitAfterFlush(ExitCode.Ok);
|
|
4574
4588
|
}
|
|
4575
|
-
process.stdout.write(`App ${appId} (ws ${workspaceId}):\n`);
|
|
4576
|
-
for (const k of keys) {
|
|
4577
|
-
const v = links[k];
|
|
4578
|
-
process.stdout.write(` ${k}\t${typeof v === "string" ? v : JSON.stringify(v)}\n`);
|
|
4579
|
-
}
|
|
4589
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}):\n ${linkUri}\n`);
|
|
4580
4590
|
return exitAfterFlush(ExitCode.Ok);
|
|
4581
4591
|
} catch (err) {
|
|
4582
4592
|
return emitFailureFromError(args.json, err);
|
|
@@ -6975,6 +6985,81 @@ const completionCommand = defineCommand({
|
|
|
6975
6985
|
}
|
|
6976
6986
|
});
|
|
6977
6987
|
//#endregion
|
|
6988
|
+
//#region src/ait-token-profile.ts
|
|
6989
|
+
const AIT_CREDENTIALS_PATH = join(homedir(), ".ait", "credentials");
|
|
6990
|
+
function credentialsPath() {
|
|
6991
|
+
const override = process.env._AIT_CREDENTIALS_PATH_OVERRIDE;
|
|
6992
|
+
if (override && override.length > 0) return override;
|
|
6993
|
+
return AIT_CREDENTIALS_PATH;
|
|
6994
|
+
}
|
|
6995
|
+
function readCredentials(path) {
|
|
6996
|
+
if (!existsSync(path)) return {};
|
|
6997
|
+
try {
|
|
6998
|
+
const raw = readFileSync(path, "utf8");
|
|
6999
|
+
const parsed = JSON.parse(raw);
|
|
7000
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
7001
|
+
return {};
|
|
7002
|
+
} catch {
|
|
7003
|
+
return {};
|
|
7004
|
+
}
|
|
7005
|
+
}
|
|
7006
|
+
function writeCredentials(path, map) {
|
|
7007
|
+
const dir = join(path, "..");
|
|
7008
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
7009
|
+
writeFileSync(path, JSON.stringify(map, null, 2) + "\n", {
|
|
7010
|
+
encoding: "utf8",
|
|
7011
|
+
mode: 384
|
|
7012
|
+
});
|
|
7013
|
+
}
|
|
7014
|
+
/**
|
|
7015
|
+
* Save a Deploy Key to the `ait` token profile store.
|
|
7016
|
+
*
|
|
7017
|
+
* Tries direct-write first. If that fails and `ait` is on PATH, falls back
|
|
7018
|
+
* to spawning `ait token add --api-key ... <profile>`.
|
|
7019
|
+
*
|
|
7020
|
+
* SECRET-HANDLING: `apiKey` must not appear in any log or thrown message.
|
|
7021
|
+
* The `detail` field in failure results contains only non-secret context.
|
|
7022
|
+
*/
|
|
7023
|
+
function saveAitTokenProfile(profile, apiKey) {
|
|
7024
|
+
const path = credentialsPath();
|
|
7025
|
+
try {
|
|
7026
|
+
const map = readCredentials(path);
|
|
7027
|
+
map[profile] = apiKey;
|
|
7028
|
+
writeCredentials(path, map);
|
|
7029
|
+
return {
|
|
7030
|
+
ok: true,
|
|
7031
|
+
method: "direct",
|
|
7032
|
+
profile
|
|
7033
|
+
};
|
|
7034
|
+
} catch (err) {
|
|
7035
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
7036
|
+
try {
|
|
7037
|
+
execFileSync("ait", [
|
|
7038
|
+
"token",
|
|
7039
|
+
"add",
|
|
7040
|
+
"--api-key",
|
|
7041
|
+
apiKey,
|
|
7042
|
+
profile
|
|
7043
|
+
], {
|
|
7044
|
+
stdio: "ignore",
|
|
7045
|
+
timeout: 1e4,
|
|
7046
|
+
env: { ...process.env }
|
|
7047
|
+
});
|
|
7048
|
+
return {
|
|
7049
|
+
ok: true,
|
|
7050
|
+
method: "spawn",
|
|
7051
|
+
profile
|
|
7052
|
+
};
|
|
7053
|
+
} catch {
|
|
7054
|
+
return {
|
|
7055
|
+
ok: false,
|
|
7056
|
+
reason: "write-failed",
|
|
7057
|
+
detail
|
|
7058
|
+
};
|
|
7059
|
+
}
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
//#endregion
|
|
6978
7063
|
//#region src/api/api-keys.ts
|
|
6979
7064
|
const BASE$2 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
6980
7065
|
async function fetchApiKeys(workspaceId, cookies, opts = {}) {
|
|
@@ -7130,6 +7215,10 @@ const createCommand = defineCommand({
|
|
|
7130
7215
|
type: "string",
|
|
7131
7216
|
description: "Workspace ID. Defaults to the selected workspace."
|
|
7132
7217
|
},
|
|
7218
|
+
"save-profile": {
|
|
7219
|
+
type: "string",
|
|
7220
|
+
description: "After issuing, save the key as an `ait` token profile (written to `~/.ait/credentials`). The named profile can then be used with `ait deploy --profile <name>` immediately. If omitted, the key is printed to stdout once and not persisted locally."
|
|
7221
|
+
},
|
|
7133
7222
|
json: {
|
|
7134
7223
|
type: "boolean",
|
|
7135
7224
|
description: "Emit machine-readable JSON to stdout.",
|
|
@@ -7179,6 +7268,14 @@ const createCommand = defineCommand({
|
|
|
7179
7268
|
name,
|
|
7180
7269
|
target
|
|
7181
7270
|
}, session.cookies);
|
|
7271
|
+
const saveProfileName = args["save-profile"] ? String(args["save-profile"]) : void 0;
|
|
7272
|
+
let savedProfile;
|
|
7273
|
+
let saveProfileWarning;
|
|
7274
|
+
if (saveProfileName !== void 0) {
|
|
7275
|
+
const saveResult = saveAitTokenProfile(saveProfileName, result.apiKey);
|
|
7276
|
+
if (saveResult.ok) savedProfile = saveResult.profile;
|
|
7277
|
+
else saveProfileWarning = `Could not save to ait profile "${saveProfileName}": ${saveResult.detail}. Save the key manually with \`ait token add --api-key <key> ` + saveProfileName + "`.";
|
|
7278
|
+
}
|
|
7182
7279
|
if (args.json) {
|
|
7183
7280
|
emitJson({
|
|
7184
7281
|
ok: true,
|
|
@@ -7189,12 +7286,16 @@ const createCommand = defineCommand({
|
|
|
7189
7286
|
isAll: target.isAll,
|
|
7190
7287
|
appNames: [...target.appNames]
|
|
7191
7288
|
},
|
|
7289
|
+
...savedProfile !== void 0 ? { savedProfile } : {},
|
|
7290
|
+
...saveProfileWarning !== void 0 ? { saveProfileWarning } : {},
|
|
7192
7291
|
extra: result.extra
|
|
7193
7292
|
});
|
|
7194
7293
|
return exitAfterFlush(ExitCode.Ok);
|
|
7195
7294
|
}
|
|
7196
7295
|
process.stdout.write(`${result.apiKey}\n`);
|
|
7197
|
-
process.stderr.write(
|
|
7296
|
+
if (savedProfile !== void 0) process.stderr.write(`Saved as ait profile "${savedProfile}". Run: ait deploy --profile ${savedProfile}\n`);
|
|
7297
|
+
else process.stderr.write("⚠️ This key is shown only once. Save it to a secret manager now — it cannot be retrieved later.\n");
|
|
7298
|
+
if (saveProfileWarning !== void 0) process.stderr.write(`Warning: ${saveProfileWarning}\n`);
|
|
7198
7299
|
return exitAfterFlush(ExitCode.Ok);
|
|
7199
7300
|
} catch (err) {
|
|
7200
7301
|
return emitFailureFromError(args.json, err);
|
|
@@ -9237,7 +9338,7 @@ function resolveVersion() {
|
|
|
9237
9338
|
if (typeof injected === "string" && injected.length > 0) return injected;
|
|
9238
9339
|
} catch {}
|
|
9239
9340
|
try {
|
|
9240
|
-
return "0.1.
|
|
9341
|
+
return "0.1.36";
|
|
9241
9342
|
} catch {}
|
|
9242
9343
|
return "0.0.0-dev";
|
|
9243
9344
|
}
|