tezign-ai-appstore-sdk 0.2.2 → 0.2.4
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/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/open-api/cli-input-help.d.ts.map +1 -1
- package/dist/open-api/cli-input-help.js +7 -0
- package/dist/open-api/cli-input-help.js.map +1 -1
- package/dist/open-api/cli-output-types.d.ts +25 -4
- package/dist/open-api/cli-output-types.d.ts.map +1 -1
- package/dist/open-api/cli-output-types.js +62 -0
- package/dist/open-api/cli-output-types.js.map +1 -1
- package/dist/open-api/cli.d.ts.map +1 -1
- package/dist/open-api/cli.js +436 -20
- package/dist/open-api/cli.js.map +1 -1
- package/dist/open-api/client.d.ts +5 -1
- package/dist/open-api/client.d.ts.map +1 -1
- package/dist/open-api/client.js +75 -4
- package/dist/open-api/client.js.map +1 -1
- package/dist/open-api/types.d.ts +75 -0
- package/dist/open-api/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/open-api/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { clearOpenApiCliStoredKey, readOpenApiCliCurrentEnvironment, readOpenApi
|
|
|
6
6
|
import { createOpenApiClient, OpenApiError } from './client.js';
|
|
7
7
|
import { findOpenApiProjectLocalConfig, writeOpenApiProjectLocalConfig, } from './project-local-config.js';
|
|
8
8
|
import { createOpenApiSourceArchive, inspectOpenApiSourceArchive } from './source-archive.js';
|
|
9
|
-
import { OPEN_API_CLI_STDOUT_TYPESCRIPT_HELP, } from './cli-output-types.js';
|
|
9
|
+
import { OPEN_API_CLI_STDOUT_TYPESCRIPT_HELP, toDeploymentRuntimeLogsStdout, } from './cli-output-types.js';
|
|
10
10
|
import { OPEN_API_CLI_INPUT_TYPESCRIPT_HELP } from './cli-input-help.js';
|
|
11
11
|
class CliUsageError extends Error {
|
|
12
12
|
helpText;
|
|
@@ -68,12 +68,16 @@ const DAILY_WORKFLOW_HELP = [
|
|
|
68
68
|
flag: '5. deploy',
|
|
69
69
|
description: '触发部署并等待结果:tezign-ai-appstore project deploy',
|
|
70
70
|
},
|
|
71
|
+
{
|
|
72
|
+
flag: '6. project deploy-html',
|
|
73
|
+
description: '为 STATIC_SITE 项目直接发布单个 HTML:tezign-ai-appstore project deploy-html --file ./landing.html',
|
|
74
|
+
},
|
|
71
75
|
];
|
|
72
76
|
const COMMAND_GROUP_HELP = {
|
|
73
77
|
auth: '登录、退出和查看本地 Open API CLI 认证状态',
|
|
74
78
|
env: '管理 baseUrl 对应的环境别名',
|
|
75
|
-
project: '创建、绑定、更新、上传源码、部署项目及管理自定义访问域名',
|
|
76
|
-
deployment: '
|
|
79
|
+
project: '创建、绑定、更新、上传源码、部署项目及管理自定义访问域名 / 环境变量',
|
|
80
|
+
deployment: '查询部署状态及 WEB_SERVICE 运行时日志',
|
|
77
81
|
};
|
|
78
82
|
const FINAL_DEPLOYMENT_STATUS = new Set(['READY', 'FAILED', 'SUCCEEDED']);
|
|
79
83
|
const FAILED_DEPLOYMENT_STATUS = new Set(['FAILED']);
|
|
@@ -467,9 +471,45 @@ async function resolveProjectIdForCommand(options, commandKey, io) {
|
|
|
467
471
|
}
|
|
468
472
|
throw createCliUsageError(`${commandKey} requires --projectId,或先在当前目录执行 \`tezign-ai-appstore project init\` 绑定项目`, commandKey);
|
|
469
473
|
}
|
|
474
|
+
function readRequiredDeploymentIdOption(options, commandKey) {
|
|
475
|
+
const deploymentId = readStringOption(options, 'deploymentId') || readStringOption(options, 'id');
|
|
476
|
+
if (!deploymentId) {
|
|
477
|
+
throw createCliUsageError(`${commandKey} requires --deploymentId or --id`, commandKey);
|
|
478
|
+
}
|
|
479
|
+
return deploymentId;
|
|
480
|
+
}
|
|
481
|
+
function readExplicitDeploymentIdOption(options) {
|
|
482
|
+
return readStringOption(options, 'deploymentId') || readStringOption(options, 'id') || null;
|
|
483
|
+
}
|
|
484
|
+
async function resolveDeploymentIdForRuntimeLogs(options, commandKey, io, client) {
|
|
485
|
+
const explicitDeploymentId = readExplicitDeploymentIdOption(options);
|
|
486
|
+
if (explicitDeploymentId) {
|
|
487
|
+
return explicitDeploymentId;
|
|
488
|
+
}
|
|
489
|
+
const projectId = await resolveProjectIdForCommand(options, commandKey, io);
|
|
490
|
+
const projects = await client.listProjects();
|
|
491
|
+
const project = projects.find((item) => item.id === projectId);
|
|
492
|
+
if (!project) {
|
|
493
|
+
throw createCliUsageError(`项目 ${projectId} 不在当前账号可操作列表中`, commandKey);
|
|
494
|
+
}
|
|
495
|
+
const activeDeploymentId = project.activeDeploymentId;
|
|
496
|
+
if (!activeDeploymentId) {
|
|
497
|
+
throw createCliUsageError(`项目 ${projectId} 尚无生效部署(activeDeploymentId 为空),请先执行 project deploy 或显式传入 --deploymentId`, commandKey);
|
|
498
|
+
}
|
|
499
|
+
return activeDeploymentId;
|
|
500
|
+
}
|
|
470
501
|
const PROJECT_LIST_ALLOWED_OPTION_KEYS = new Set(['env', 'baseUrl', 'config']);
|
|
471
502
|
const PROJECT_UPDATE_ALLOWED_OPTION_KEYS = new Set(['env', 'baseUrl', 'config', 'input', 'projectId', 'id']);
|
|
472
503
|
const PROJECT_DEPLOY_ALLOWED_OPTION_KEYS = new Set(['env', 'baseUrl', 'config', 'projectId', 'id']);
|
|
504
|
+
const PROJECT_DEPLOY_HTML_ALLOWED_OPTION_KEYS = new Set([
|
|
505
|
+
'env',
|
|
506
|
+
'baseUrl',
|
|
507
|
+
'config',
|
|
508
|
+
'projectId',
|
|
509
|
+
'id',
|
|
510
|
+
'file',
|
|
511
|
+
'remark',
|
|
512
|
+
]);
|
|
473
513
|
const PROJECT_INIT_ALLOWED_OPTION_KEYS = new Set([
|
|
474
514
|
'env',
|
|
475
515
|
'baseUrl',
|
|
@@ -491,6 +531,32 @@ const PROJECT_UPLOAD_SOURCE_ALLOWED_OPTION_KEYS = new Set([
|
|
|
491
531
|
'remark',
|
|
492
532
|
'dry-run',
|
|
493
533
|
]);
|
|
534
|
+
const PROJECT_GET_ENV_VARS_ALLOWED_OPTION_KEYS = new Set([
|
|
535
|
+
'env',
|
|
536
|
+
'baseUrl',
|
|
537
|
+
'config',
|
|
538
|
+
'projectId',
|
|
539
|
+
'id',
|
|
540
|
+
]);
|
|
541
|
+
const PROJECT_SET_ENV_VARS_ALLOWED_OPTION_KEYS = new Set([
|
|
542
|
+
'env',
|
|
543
|
+
'baseUrl',
|
|
544
|
+
'config',
|
|
545
|
+
'projectId',
|
|
546
|
+
'id',
|
|
547
|
+
'input',
|
|
548
|
+
'replace',
|
|
549
|
+
'clear',
|
|
550
|
+
]);
|
|
551
|
+
const PROJECT_UNSET_ENV_VARS_ALLOWED_OPTION_KEYS = new Set([
|
|
552
|
+
'env',
|
|
553
|
+
'baseUrl',
|
|
554
|
+
'config',
|
|
555
|
+
'projectId',
|
|
556
|
+
'id',
|
|
557
|
+
'key',
|
|
558
|
+
'keys',
|
|
559
|
+
]);
|
|
494
560
|
const PROJECT_LIST_DOMAINS_ALLOWED_OPTION_KEYS = new Set(['env', 'baseUrl', 'config', 'projectId', 'id']);
|
|
495
561
|
const PROJECT_ADD_DOMAIN_ALLOWED_OPTION_KEYS = new Set([
|
|
496
562
|
'env',
|
|
@@ -510,28 +576,72 @@ const PROJECT_REMOVE_DOMAIN_ALLOWED_OPTION_KEYS = new Set([
|
|
|
510
576
|
'id',
|
|
511
577
|
'domain-id',
|
|
512
578
|
]);
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
579
|
+
const DEPLOYMENT_RUNTIME_LOGS_ALLOWED_OPTION_KEYS = new Set([
|
|
580
|
+
'env',
|
|
581
|
+
'baseUrl',
|
|
582
|
+
'config',
|
|
583
|
+
'deploymentId',
|
|
584
|
+
'id',
|
|
585
|
+
'projectId',
|
|
586
|
+
]);
|
|
587
|
+
function parseRequiredJsonObjectOption(options, name, commandKey) {
|
|
588
|
+
const raw = readStringOption(options, name);
|
|
521
589
|
if (!raw) {
|
|
522
|
-
throw createCliUsageError(
|
|
590
|
+
throw createCliUsageError(`${commandKey} 需要 --${name} <json>`, commandKey);
|
|
523
591
|
}
|
|
524
592
|
let parsed;
|
|
525
593
|
try {
|
|
526
594
|
parsed = JSON.parse(raw);
|
|
527
595
|
}
|
|
528
596
|
catch {
|
|
529
|
-
throw createCliUsageError(
|
|
597
|
+
throw createCliUsageError(`Invalid JSON value for --${name}`, commandKey);
|
|
530
598
|
}
|
|
531
599
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
532
|
-
throw createCliUsageError(
|
|
600
|
+
throw createCliUsageError(`--${name} must be a JSON object`, commandKey);
|
|
533
601
|
}
|
|
534
|
-
|
|
602
|
+
return parsed;
|
|
603
|
+
}
|
|
604
|
+
function normalizeEnvVarsRecord(input, commandKey) {
|
|
605
|
+
const envVars = {};
|
|
606
|
+
for (const [key, value] of Object.entries(input)) {
|
|
607
|
+
if (typeof value !== 'string') {
|
|
608
|
+
throw createCliUsageError(`${commandKey} 的 --input 里环境变量 ${key} 的值必须是字符串`, commandKey);
|
|
609
|
+
}
|
|
610
|
+
envVars[key] = value;
|
|
611
|
+
}
|
|
612
|
+
return envVars;
|
|
613
|
+
}
|
|
614
|
+
function readEnvVarKeysToUnset(options, commandKey) {
|
|
615
|
+
const rawSingleKey = readStringOption(options, 'key');
|
|
616
|
+
const rawMultiKeys = readStringOption(options, 'keys');
|
|
617
|
+
if (!rawSingleKey && !rawMultiKeys) {
|
|
618
|
+
throw createCliUsageError(`${commandKey} 需要 --key <name> 或 --keys <a,b,c>`, commandKey);
|
|
619
|
+
}
|
|
620
|
+
const keys = [];
|
|
621
|
+
if (rawSingleKey) {
|
|
622
|
+
const key = rawSingleKey.trim();
|
|
623
|
+
if (!key) {
|
|
624
|
+
throw createCliUsageError(`Invalid value for --key: ${rawSingleKey}`, commandKey);
|
|
625
|
+
}
|
|
626
|
+
keys.push(key);
|
|
627
|
+
}
|
|
628
|
+
if (rawMultiKeys) {
|
|
629
|
+
const items = rawMultiKeys.split(',').map((item) => item.trim());
|
|
630
|
+
if (items.some((item) => !item)) {
|
|
631
|
+
throw createCliUsageError(`${commandKey} 的 --keys 必须是逗号分隔的非空 key 列表`, commandKey);
|
|
632
|
+
}
|
|
633
|
+
keys.push(...items);
|
|
634
|
+
}
|
|
635
|
+
return [...new Set(keys)];
|
|
636
|
+
}
|
|
637
|
+
function buildUpdateProjectInput(options) {
|
|
638
|
+
const commandKey = 'project update';
|
|
639
|
+
for (const key of Object.keys(options)) {
|
|
640
|
+
if (!PROJECT_UPDATE_ALLOWED_OPTION_KEYS.has(key)) {
|
|
641
|
+
throw createCliUsageError(`project update 不再支持 --${key},请使用 --input 传入更新请求体 JSON(与 Open API 更新项目接口一致)`, commandKey);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
const input = parseRequiredJsonObjectOption(options, 'input', commandKey);
|
|
535
645
|
if (Object.keys(input).length === 0) {
|
|
536
646
|
throw createCliUsageError('project update 的 --input 须为非空对象(至少包含一个要更新的字段)', commandKey);
|
|
537
647
|
}
|
|
@@ -562,6 +672,14 @@ function parseOptionArgs(argv, commandKey) {
|
|
|
562
672
|
}
|
|
563
673
|
return options;
|
|
564
674
|
}
|
|
675
|
+
function normalizeCommandArgs(commandKey, argv) {
|
|
676
|
+
if (commandKey === 'project deploy-html' &&
|
|
677
|
+
argv.length > 0 &&
|
|
678
|
+
!argv[0].startsWith('--')) {
|
|
679
|
+
return ['--file', argv[0], ...argv.slice(1)];
|
|
680
|
+
}
|
|
681
|
+
return argv;
|
|
682
|
+
}
|
|
565
683
|
async function resolveCliTarget(options, commandKey, io, behavior = {}) {
|
|
566
684
|
const configPath = readStringOption(options, 'config');
|
|
567
685
|
const explicitEnvName = readStringOption(options, 'env');
|
|
@@ -989,6 +1107,92 @@ async function handleProjectUpdate(options, io) {
|
|
|
989
1107
|
const project = await client.updateProject(projectId, buildUpdateProjectInput(options));
|
|
990
1108
|
printJson(project, io);
|
|
991
1109
|
}
|
|
1110
|
+
async function handleProjectGetEnvVars(options, io) {
|
|
1111
|
+
const commandKey = 'project get-env-vars';
|
|
1112
|
+
for (const key of Object.keys(options)) {
|
|
1113
|
+
if (!PROJECT_GET_ENV_VARS_ALLOWED_OPTION_KEYS.has(key)) {
|
|
1114
|
+
throw createCliUsageError(`Unknown option for project get-env-vars: --${key}`, commandKey);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
const projectId = await resolveProjectIdForCommand(options, commandKey, io);
|
|
1118
|
+
const cliOptions = await resolveCliOptions(options, commandKey, io);
|
|
1119
|
+
const client = createOpenApiCliClient(cliOptions);
|
|
1120
|
+
const payload = await client.getProjectEnvVars(projectId);
|
|
1121
|
+
printJson(payload, io);
|
|
1122
|
+
}
|
|
1123
|
+
function buildSetProjectEnvVarsRequest(options, currentEnvVars) {
|
|
1124
|
+
const commandKey = 'project set-env-vars';
|
|
1125
|
+
const shouldReplace = readBooleanOption(options, 'replace', commandKey) ?? false;
|
|
1126
|
+
const shouldClear = readBooleanOption(options, 'clear', commandKey) ?? false;
|
|
1127
|
+
if (shouldClear) {
|
|
1128
|
+
if (readStringOption(options, 'input')) {
|
|
1129
|
+
throw createCliUsageError('project set-env-vars 传入 --clear 时不能同时传 --input;请二选一', commandKey);
|
|
1130
|
+
}
|
|
1131
|
+
if (shouldReplace) {
|
|
1132
|
+
throw createCliUsageError('project set-env-vars 传入 --clear 时不需要再传 --replace', commandKey);
|
|
1133
|
+
}
|
|
1134
|
+
return { envVars: null };
|
|
1135
|
+
}
|
|
1136
|
+
const input = parseRequiredJsonObjectOption(options, 'input', commandKey);
|
|
1137
|
+
const normalizedInput = normalizeEnvVarsRecord(input, commandKey);
|
|
1138
|
+
if (shouldReplace) {
|
|
1139
|
+
return {
|
|
1140
|
+
envVars: Object.keys(normalizedInput).length > 0 ? normalizedInput : null,
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
return {
|
|
1144
|
+
envVars: {
|
|
1145
|
+
...(currentEnvVars ?? {}),
|
|
1146
|
+
...normalizedInput,
|
|
1147
|
+
},
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
async function handleProjectSetEnvVars(options, io) {
|
|
1151
|
+
const commandKey = 'project set-env-vars';
|
|
1152
|
+
for (const key of Object.keys(options)) {
|
|
1153
|
+
if (!PROJECT_SET_ENV_VARS_ALLOWED_OPTION_KEYS.has(key)) {
|
|
1154
|
+
throw createCliUsageError(`Unknown option for project set-env-vars: --${key}`, commandKey);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
const projectId = await resolveProjectIdForCommand(options, commandKey, io);
|
|
1158
|
+
const cliOptions = await resolveCliOptions(options, commandKey, io);
|
|
1159
|
+
const client = createOpenApiCliClient(cliOptions);
|
|
1160
|
+
const current = await client.getProjectEnvVars(projectId);
|
|
1161
|
+
const requestBody = buildSetProjectEnvVarsRequest(options, current.envVars);
|
|
1162
|
+
const payload = await client.setProjectEnvVars(projectId, requestBody);
|
|
1163
|
+
printJson(payload, io);
|
|
1164
|
+
}
|
|
1165
|
+
async function handleProjectUnsetEnvVars(options, io) {
|
|
1166
|
+
const commandKey = 'project unset-env-vars';
|
|
1167
|
+
for (const key of Object.keys(options)) {
|
|
1168
|
+
if (!PROJECT_UNSET_ENV_VARS_ALLOWED_OPTION_KEYS.has(key)) {
|
|
1169
|
+
throw createCliUsageError(`Unknown option for project unset-env-vars: --${key}`, commandKey);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
const projectId = await resolveProjectIdForCommand(options, commandKey, io);
|
|
1173
|
+
const keysToUnset = readEnvVarKeysToUnset(options, commandKey);
|
|
1174
|
+
const cliOptions = await resolveCliOptions(options, commandKey, io);
|
|
1175
|
+
const client = createOpenApiCliClient(cliOptions);
|
|
1176
|
+
const current = await client.getProjectEnvVars(projectId);
|
|
1177
|
+
const currentEnvVars = current.envVars ?? {};
|
|
1178
|
+
const nextEnvVars = Object.fromEntries(Object.entries(currentEnvVars).filter(([key]) => !keysToUnset.includes(key)));
|
|
1179
|
+
const missingKeys = keysToUnset.filter((key) => !Object.prototype.hasOwnProperty.call(currentEnvVars, key));
|
|
1180
|
+
if (Object.keys(nextEnvVars).length === Object.keys(currentEnvVars).length) {
|
|
1181
|
+
if (missingKeys.length > 0) {
|
|
1182
|
+
io.stderr(`以下环境变量不存在,已跳过:${missingKeys.join(', ')}`);
|
|
1183
|
+
}
|
|
1184
|
+
io.stderr('未发生环境变量变更。');
|
|
1185
|
+
printJson(current, io);
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
const payload = await client.setProjectEnvVars(projectId, {
|
|
1189
|
+
envVars: Object.keys(nextEnvVars).length > 0 ? nextEnvVars : null,
|
|
1190
|
+
});
|
|
1191
|
+
if (missingKeys.length > 0) {
|
|
1192
|
+
io.stderr(`以下环境变量不存在,已跳过:${missingKeys.join(', ')}`);
|
|
1193
|
+
}
|
|
1194
|
+
printJson(payload, io);
|
|
1195
|
+
}
|
|
992
1196
|
async function handleProjectDeploy(options, io) {
|
|
993
1197
|
const commandKey = 'project deploy';
|
|
994
1198
|
for (const key of Object.keys(options)) {
|
|
@@ -1016,6 +1220,48 @@ async function handleProjectDeploy(options, io) {
|
|
|
1016
1220
|
: `部署成功。应用访问链接:\n${urls.map((u) => ` • ${u}`).join('\n')}`);
|
|
1017
1221
|
}
|
|
1018
1222
|
}
|
|
1223
|
+
async function handleProjectDeployHtml(options, io) {
|
|
1224
|
+
const commandKey = 'project deploy-html';
|
|
1225
|
+
for (const key of Object.keys(options)) {
|
|
1226
|
+
if (!PROJECT_DEPLOY_HTML_ALLOWED_OPTION_KEYS.has(key)) {
|
|
1227
|
+
throw createCliUsageError(`Unknown option for project deploy-html: --${key}`, commandKey);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
const projectId = await resolveProjectIdForCommand(options, commandKey, io);
|
|
1231
|
+
const cliOptions = await resolveCliOptions(options, commandKey, io);
|
|
1232
|
+
const client = createOpenApiCliClient(cliOptions);
|
|
1233
|
+
const filePath = readStringOption(options, 'file');
|
|
1234
|
+
if (!filePath) {
|
|
1235
|
+
throw createCliUsageError('project deploy-html requires <html-file> or --file <path>', commandKey);
|
|
1236
|
+
}
|
|
1237
|
+
const filename = basename(filePath);
|
|
1238
|
+
if (!filename.toLowerCase().endsWith('.html') && !filename.toLowerCase().endsWith('.htm')) {
|
|
1239
|
+
throw createCliUsageError('project deploy-html requires a .html or .htm file', commandKey);
|
|
1240
|
+
}
|
|
1241
|
+
const htmlBuffer = await readFile(resolve(filePath)).catch((error) => {
|
|
1242
|
+
throw new Error(`读取 HTML 文件失败:${error instanceof Error ? error.message : String(error || 'unknown error')}`);
|
|
1243
|
+
});
|
|
1244
|
+
const deployment = await client.deployProjectHtml(projectId, {
|
|
1245
|
+
file: new Blob([htmlBuffer], { type: 'text/html' }),
|
|
1246
|
+
filename,
|
|
1247
|
+
remark: readStringOption(options, 'remark') || null,
|
|
1248
|
+
});
|
|
1249
|
+
const finalDeployment = await waitForDeploymentCompletion(deployment, client, cliOptions, io);
|
|
1250
|
+
printJson(finalDeployment, io);
|
|
1251
|
+
if (isFailedDeploymentStatus(finalDeployment.status)) {
|
|
1252
|
+
throw new Error(`部署失败(deploymentId: ${finalDeployment.deploymentId || finalDeployment.id})`);
|
|
1253
|
+
}
|
|
1254
|
+
const urls = Array.isArray(finalDeployment.accessUrls) && finalDeployment.accessUrls.length > 0
|
|
1255
|
+
? finalDeployment.accessUrls
|
|
1256
|
+
: typeof finalDeployment.accessUrl === 'string' && finalDeployment.accessUrl.length > 0
|
|
1257
|
+
? [finalDeployment.accessUrl]
|
|
1258
|
+
: [];
|
|
1259
|
+
if (urls.length > 0) {
|
|
1260
|
+
io.stderr(urls.length === 1
|
|
1261
|
+
? `HTML 发布成功。应用访问链接:${urls[0]}`
|
|
1262
|
+
: `HTML 发布成功。应用访问链接:\n${urls.map((u) => ` • ${u}`).join('\n')}`);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1019
1265
|
async function handleProjectUploadSource(options, io) {
|
|
1020
1266
|
const commandKey = 'project upload-source';
|
|
1021
1267
|
for (const key of Object.keys(options)) {
|
|
@@ -1171,15 +1417,25 @@ async function handleProjectRemoveDomain(options, io) {
|
|
|
1171
1417
|
}
|
|
1172
1418
|
async function handleDeploymentGet(options, io) {
|
|
1173
1419
|
const commandKey = 'deployment get';
|
|
1174
|
-
const deploymentId =
|
|
1175
|
-
if (!deploymentId) {
|
|
1176
|
-
throw createCliUsageError('deployment get requires --deploymentId or --id', commandKey);
|
|
1177
|
-
}
|
|
1420
|
+
const deploymentId = readRequiredDeploymentIdOption(options, commandKey);
|
|
1178
1421
|
const cliOptions = await resolveCliOptions(options, commandKey, io);
|
|
1179
1422
|
const client = createOpenApiCliClient(cliOptions);
|
|
1180
1423
|
const deployment = await client.getDeployment(deploymentId);
|
|
1181
1424
|
printJson(deployment, io);
|
|
1182
1425
|
}
|
|
1426
|
+
async function handleDeploymentRuntimeLogs(options, io) {
|
|
1427
|
+
const commandKey = 'deployment runtime-logs';
|
|
1428
|
+
for (const key of Object.keys(options)) {
|
|
1429
|
+
if (!DEPLOYMENT_RUNTIME_LOGS_ALLOWED_OPTION_KEYS.has(key)) {
|
|
1430
|
+
throw createCliUsageError(`Unknown option for deployment runtime-logs: --${key}`, commandKey);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
const cliOptions = await resolveCliOptions(options, commandKey, io);
|
|
1434
|
+
const client = createOpenApiCliClient(cliOptions);
|
|
1435
|
+
const deploymentId = await resolveDeploymentIdForRuntimeLogs(options, commandKey, io, client);
|
|
1436
|
+
const runtimeLogs = await client.getDeploymentRuntimeLogs(deploymentId);
|
|
1437
|
+
printJson(toDeploymentRuntimeLogsStdout(runtimeLogs), io);
|
|
1438
|
+
}
|
|
1183
1439
|
function commandDefinitions() {
|
|
1184
1440
|
return [
|
|
1185
1441
|
{
|
|
@@ -1415,6 +1671,98 @@ function commandDefinitions() {
|
|
|
1415
1671
|
],
|
|
1416
1672
|
handler: handleProjectUpdate,
|
|
1417
1673
|
},
|
|
1674
|
+
{
|
|
1675
|
+
key: 'project get-env-vars',
|
|
1676
|
+
group: 'project',
|
|
1677
|
+
action: 'get-env-vars',
|
|
1678
|
+
summary: '读取项目当前环境变量(返回真实值,请谨慎处理)',
|
|
1679
|
+
usage: [
|
|
1680
|
+
'tezign-ai-appstore project get-env-vars [--env test | --baseUrl https://platform.example.com] [--projectId proj_123]',
|
|
1681
|
+
],
|
|
1682
|
+
options: [...TARGET_CLI_OPTION_HELP, PROJECT_ID_CLI_OPTION_HELP],
|
|
1683
|
+
notes: [
|
|
1684
|
+
'返回真实环境变量值,可能包含 API Key、Token 等敏感信息;请避免直接贴到日志、工单或公开聊天中。',
|
|
1685
|
+
'需要 Open API scope `project:update`。',
|
|
1686
|
+
'未传 --projectId / --id 时,读取当前目录 .tezign/open-api-project.json 绑定。',
|
|
1687
|
+
],
|
|
1688
|
+
examples: [
|
|
1689
|
+
'tezign-ai-appstore project get-env-vars',
|
|
1690
|
+
'tezign-ai-appstore project get-env-vars --projectId proj_123',
|
|
1691
|
+
],
|
|
1692
|
+
handler: handleProjectGetEnvVars,
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
key: 'project set-env-vars',
|
|
1696
|
+
group: 'project',
|
|
1697
|
+
action: 'set-env-vars',
|
|
1698
|
+
summary: '写入项目环境变量;默认按 key 合并,支持整表替换或清空',
|
|
1699
|
+
usage: [
|
|
1700
|
+
'tezign-ai-appstore project set-env-vars [--env test | --baseUrl https://platform.example.com] [--projectId proj_123] --input \'{"OPENAI_API_KEY":"sk-xxx"}\'',
|
|
1701
|
+
'tezign-ai-appstore project set-env-vars [--projectId proj_123] --replace --input \'{"FEATURE_FLAG":"true"}\'',
|
|
1702
|
+
'tezign-ai-appstore project set-env-vars [--projectId proj_123] --clear',
|
|
1703
|
+
],
|
|
1704
|
+
options: [
|
|
1705
|
+
...TARGET_CLI_OPTION_HELP,
|
|
1706
|
+
PROJECT_ID_CLI_OPTION_HELP,
|
|
1707
|
+
{
|
|
1708
|
+
flag: '--input <json>',
|
|
1709
|
+
description: '可选。环境变量 JSON 对象,value 必须为字符串;默认以 key 为单位合并到现有环境变量中。',
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
flag: '--replace <bool>',
|
|
1713
|
+
description: '可选。为 true 时,用 --input 整体替换当前环境变量集合;可写 --replace 或 --replace true。',
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
flag: '--clear <bool>',
|
|
1717
|
+
description: '可选。清空当前项目全部环境变量;可写 --clear 或 --clear true。传入时不能同时传 --input。',
|
|
1718
|
+
},
|
|
1719
|
+
],
|
|
1720
|
+
notes: [
|
|
1721
|
+
'默认行为是“合并更新”:只覆盖 --input 中给出的 key,未给出的现有 key 会保留。',
|
|
1722
|
+
'如需精确控制最终结果,请先执行 project get-env-vars 查看当前值,再用 --replace 提交完整目标集合。',
|
|
1723
|
+
'保存后需要重新部署,新的环境变量才会注入到运行中的 Web Service 实例。',
|
|
1724
|
+
'需要 Open API scope `project:update`。',
|
|
1725
|
+
],
|
|
1726
|
+
examples: [
|
|
1727
|
+
`tezign-ai-appstore project set-env-vars --projectId proj_123 --input '{"OPENAI_API_KEY":"sk-xxx","FEATURE_FLAG":"true"}'`,
|
|
1728
|
+
`tezign-ai-appstore project set-env-vars --projectId proj_123 --replace --input '{"ONLY_THIS":"1"}'`,
|
|
1729
|
+
'tezign-ai-appstore project set-env-vars --projectId proj_123 --clear',
|
|
1730
|
+
],
|
|
1731
|
+
handler: handleProjectSetEnvVars,
|
|
1732
|
+
},
|
|
1733
|
+
{
|
|
1734
|
+
key: 'project unset-env-vars',
|
|
1735
|
+
group: 'project',
|
|
1736
|
+
action: 'unset-env-vars',
|
|
1737
|
+
summary: '删除指定环境变量 key,支持单个或多个 key',
|
|
1738
|
+
usage: [
|
|
1739
|
+
'tezign-ai-appstore project unset-env-vars [--env test | --baseUrl https://platform.example.com] [--projectId proj_123] --key OPENAI_API_KEY',
|
|
1740
|
+
'tezign-ai-appstore project unset-env-vars [--projectId proj_123] --keys OPENAI_API_KEY,FEATURE_FLAG',
|
|
1741
|
+
],
|
|
1742
|
+
options: [
|
|
1743
|
+
...TARGET_CLI_OPTION_HELP,
|
|
1744
|
+
PROJECT_ID_CLI_OPTION_HELP,
|
|
1745
|
+
{
|
|
1746
|
+
flag: '--key <name>',
|
|
1747
|
+
description: '可选。删除单个环境变量 key。',
|
|
1748
|
+
},
|
|
1749
|
+
{
|
|
1750
|
+
flag: '--keys <a,b,c>',
|
|
1751
|
+
description: '可选。删除多个环境变量 key,使用英文逗号分隔。',
|
|
1752
|
+
},
|
|
1753
|
+
],
|
|
1754
|
+
notes: [
|
|
1755
|
+
'支持 --key 和 --keys 同时传入;CLI 会合并后去重处理。',
|
|
1756
|
+
'如果指定的 key 当前不存在,命令会跳过并在 stderr 提示。',
|
|
1757
|
+
'保存后需要重新部署,新的环境变量才会注入到运行中的 Web Service 实例。',
|
|
1758
|
+
'需要 Open API scope `project:update`。',
|
|
1759
|
+
],
|
|
1760
|
+
examples: [
|
|
1761
|
+
'tezign-ai-appstore project unset-env-vars --key OPENAI_API_KEY',
|
|
1762
|
+
'tezign-ai-appstore project unset-env-vars --projectId proj_123 --keys OPENAI_API_KEY,FEATURE_FLAG',
|
|
1763
|
+
],
|
|
1764
|
+
handler: handleProjectUnsetEnvVars,
|
|
1765
|
+
},
|
|
1418
1766
|
{
|
|
1419
1767
|
key: 'project deploy',
|
|
1420
1768
|
group: 'project',
|
|
@@ -1439,6 +1787,41 @@ function commandDefinitions() {
|
|
|
1439
1787
|
],
|
|
1440
1788
|
handler: handleProjectDeploy,
|
|
1441
1789
|
},
|
|
1790
|
+
{
|
|
1791
|
+
key: 'project deploy-html',
|
|
1792
|
+
group: 'project',
|
|
1793
|
+
action: 'deploy-html',
|
|
1794
|
+
summary: '为 STATIC_SITE 项目直接上传单个 HTML 并发布',
|
|
1795
|
+
usage: [
|
|
1796
|
+
'tezign-ai-appstore project deploy-html <html-file>',
|
|
1797
|
+
'tezign-ai-appstore project deploy-html [--env test | --baseUrl https://platform.example.com] [--projectId proj_123] --file ./landing.html',
|
|
1798
|
+
],
|
|
1799
|
+
options: [
|
|
1800
|
+
...TARGET_CLI_OPTION_HELP,
|
|
1801
|
+
PROJECT_ID_CLI_OPTION_HELP,
|
|
1802
|
+
{
|
|
1803
|
+
flag: '--file <path>',
|
|
1804
|
+
description: '必填。本地 .html / .htm 文件路径;也可将文件路径作为第一个位置参数传入。',
|
|
1805
|
+
},
|
|
1806
|
+
{
|
|
1807
|
+
flag: '--remark <text>',
|
|
1808
|
+
description: '可选。发布备注,会记录到源码包历史中。',
|
|
1809
|
+
},
|
|
1810
|
+
],
|
|
1811
|
+
notes: [
|
|
1812
|
+
'未传 --projectId / --id 时,必须先在当前目录执行 project init 生成项目绑定。',
|
|
1813
|
+
'当用户的网站只有单个 .html / .htm 文件时使用。',
|
|
1814
|
+
'仅 STATIC_SITE 类型项目支持此命令;非 STATIC_SITE 项目会返回业务错误。',
|
|
1815
|
+
'仅支持单个 .html / .htm 文件,会以 index.html 形式直接发布,跳过构建。',
|
|
1816
|
+
'CLI 会自动等待最终状态;成功时 stdout JSON 含 accessUrls(及 accessUrl 首项)。',
|
|
1817
|
+
],
|
|
1818
|
+
examples: [
|
|
1819
|
+
'tezign-ai-appstore project deploy-html ./landing.html',
|
|
1820
|
+
'tezign-ai-appstore project deploy-html --env test --projectId proj_123 --file ./build/index.html',
|
|
1821
|
+
'tezign-ai-appstore project deploy-html --projectId proj_123 --file ./landing.htm --remark "promo page"',
|
|
1822
|
+
],
|
|
1823
|
+
handler: handleProjectDeployHtml,
|
|
1824
|
+
},
|
|
1442
1825
|
{
|
|
1443
1826
|
key: 'project upload-source',
|
|
1444
1827
|
group: 'project',
|
|
@@ -1573,6 +1956,39 @@ function commandDefinitions() {
|
|
|
1573
1956
|
],
|
|
1574
1957
|
handler: handleDeploymentGet,
|
|
1575
1958
|
},
|
|
1959
|
+
{
|
|
1960
|
+
key: 'deployment runtime-logs',
|
|
1961
|
+
group: 'deployment',
|
|
1962
|
+
action: 'runtime-logs',
|
|
1963
|
+
summary: '查看 WEB_SERVICE deployment 的运行时日志快照',
|
|
1964
|
+
usage: [
|
|
1965
|
+
'tezign-ai-appstore deployment runtime-logs [--env test | --baseUrl https://platform.example.com] [--deploymentId dep_123]',
|
|
1966
|
+
],
|
|
1967
|
+
options: [
|
|
1968
|
+
...TARGET_CLI_OPTION_HELP,
|
|
1969
|
+
{
|
|
1970
|
+
flag: '--deploymentId <id>',
|
|
1971
|
+
description: '可选。部署 ID;也可用 --id <id>。未传时从 --projectId 或当前目录绑定项目解析,并取该项目 activeDeploymentId(当前生效部署)。',
|
|
1972
|
+
},
|
|
1973
|
+
{
|
|
1974
|
+
flag: '--projectId <id>',
|
|
1975
|
+
description: '未传 --deploymentId 时可用;也可用 --id <id>。未传且当前目录已绑定项目时,自动使用绑定项目。',
|
|
1976
|
+
},
|
|
1977
|
+
],
|
|
1978
|
+
notes: [
|
|
1979
|
+
'仅适用于 WEB_SERVICE deployment;STATIC_SITE 会返回业务错误。',
|
|
1980
|
+
'返回的是当前相关实例的 stdout/stderr 日志尾部快照,不是实时流式 follow。',
|
|
1981
|
+
'未传 --deploymentId / --id 时,会调用 project list 取绑定项目的 activeDeploymentId。',
|
|
1982
|
+
'未传 --env / --baseUrl 时,按目标平台解析顺序选择查询环境。',
|
|
1983
|
+
],
|
|
1984
|
+
examples: [
|
|
1985
|
+
'tezign-ai-appstore deployment runtime-logs --env test --deploymentId dep_123',
|
|
1986
|
+
'tezign-ai-appstore deployment runtime-logs --deploymentId dep_123',
|
|
1987
|
+
'tezign-ai-appstore deployment runtime-logs',
|
|
1988
|
+
'tezign-ai-appstore deployment runtime-logs --projectId proj_123',
|
|
1989
|
+
],
|
|
1990
|
+
handler: handleDeploymentRuntimeLogs,
|
|
1991
|
+
},
|
|
1576
1992
|
];
|
|
1577
1993
|
}
|
|
1578
1994
|
function formatCliError(error) {
|
|
@@ -1637,7 +2053,7 @@ export async function runOpenApiCli(argv = typeof process !== 'undefined' ? proc
|
|
|
1637
2053
|
output.stdout(renderCommandHelp(command.key));
|
|
1638
2054
|
return;
|
|
1639
2055
|
}
|
|
1640
|
-
await command.handler(parseOptionArgs(restArgs, command.key), output);
|
|
2056
|
+
await command.handler(parseOptionArgs(normalizeCommandArgs(command.key, restArgs), command.key), output);
|
|
1641
2057
|
}
|
|
1642
2058
|
catch (error) {
|
|
1643
2059
|
output.stderr(formatCliError(error));
|