@intra-mart/accel 0.2.0 → 0.3.0-dev.202606100311
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 +138 -4
- package/dist/asset/deployer.js +2 -3
- package/dist/asset/github-provider.d.ts +2 -0
- package/dist/asset/github-provider.js +37 -0
- package/dist/asset/local-provider.js +1 -22
- package/dist/commands/attach.d.ts +9 -1
- package/dist/commands/attach.js +21 -10
- package/dist/commands/deploy.d.ts +62 -0
- package/dist/commands/deploy.js +293 -0
- package/dist/commands/init.d.ts +9 -1
- package/dist/commands/init.js +21 -10
- package/dist/commands/login.d.ts +31 -0
- package/dist/commands/login.js +75 -0
- package/dist/core/constants.d.ts +2 -0
- package/dist/core/constants.js +3 -1
- package/dist/core/types.d.ts +41 -1
- package/dist/core/version-map.d.ts +2 -1
- package/dist/core/version-map.js +9 -22
- package/dist/deploy/api-client.d.ts +16 -0
- package/dist/deploy/api-client.js +105 -0
- package/dist/deploy/target-scanner.d.ts +5 -0
- package/dist/deploy/target-scanner.js +20 -0
- package/dist/deploy/target-selector.d.ts +11 -0
- package/dist/deploy/target-selector.js +16 -0
- package/dist/i18n/en.js +46 -0
- package/dist/i18n/ja.js +46 -0
- package/dist/i18n/zh_CN.js +46 -0
- package/dist/index.js +4 -0
- package/dist/interactive/credential-auth.d.ts +15 -0
- package/dist/interactive/credential-auth.js +61 -0
- package/dist/interactive/credentials-prompts.d.ts +4 -0
- package/dist/interactive/credentials-prompts.js +85 -0
- package/dist/interactive/prompts.js +56 -6
- package/dist/interactive/summary.js +8 -0
- package/dist/utils/args.d.ts +1 -0
- package/dist/utils/args.js +4 -0
- package/dist/utils/credentials.d.ts +12 -0
- package/dist/utils/credentials.js +46 -0
- package/dist/utils/date-formatter.d.ts +1 -0
- package/dist/utils/date-formatter.js +23 -0
- package/dist/utils/gitignore.d.ts +1 -0
- package/dist/utils/gitignore.js +23 -0
- package/dist/utils/https.d.ts +2 -0
- package/dist/utils/https.js +44 -0
- package/dist/utils/settings-io.d.ts +1 -0
- package/dist/utils/settings-io.js +8 -0
- package/package.json +2 -3
- package/assets/assets.tar.gz +0 -0
- package/dist/asset/default-source.d.ts +0 -1
- package/dist/asset/default-source.js +0 -6
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const STAGING_PATH = "/api/bearer/development/staging";
|
|
2
|
+
const VERIFY_PATH = "/oauth/token/verify";
|
|
3
|
+
const PAGE_LIMIT = 100;
|
|
4
|
+
const MAX_PAGES = 1000;
|
|
5
|
+
export const makeDeployError = (kind, extra = {}) => ({ _tag: "DeployError", kind, ...extra });
|
|
6
|
+
export const isDeployError = (e) => typeof e === "object" &&
|
|
7
|
+
e !== null &&
|
|
8
|
+
e._tag === "DeployError";
|
|
9
|
+
export const createApiClient = (endpoint, apiKey, deps = {}) => {
|
|
10
|
+
const doFetch = deps.fetch ?? globalThis.fetch;
|
|
11
|
+
const baseHeaders = {
|
|
12
|
+
"X-Intramart-Session": "never",
|
|
13
|
+
Authorization: `Bearer ${apiKey}`,
|
|
14
|
+
};
|
|
15
|
+
const request = async (url, init) => {
|
|
16
|
+
let res;
|
|
17
|
+
try {
|
|
18
|
+
res = await doFetch(url, init);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
throw makeDeployError("network", {
|
|
22
|
+
serverMessage: err instanceof Error ? err.message : String(err),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const text = await res.text();
|
|
26
|
+
let body;
|
|
27
|
+
try {
|
|
28
|
+
body = text.length > 0 ? JSON.parse(text) : undefined;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw makeDeployError("http", { status: res.status });
|
|
32
|
+
}
|
|
33
|
+
if (body !== null &&
|
|
34
|
+
typeof body === "object" &&
|
|
35
|
+
body.error === true) {
|
|
36
|
+
throw makeDeployError("http", {
|
|
37
|
+
status: res.status,
|
|
38
|
+
serverMessage: body.errorMessage,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
throw makeDeployError("http", { status: res.status });
|
|
43
|
+
}
|
|
44
|
+
if (body !== null &&
|
|
45
|
+
typeof body === "object" &&
|
|
46
|
+
"data" in body) {
|
|
47
|
+
return body.data;
|
|
48
|
+
}
|
|
49
|
+
throw makeDeployError("http", { status: res.status });
|
|
50
|
+
};
|
|
51
|
+
const verifyToken = async () => {
|
|
52
|
+
const url = `${endpoint}${VERIFY_PATH}`;
|
|
53
|
+
const body = new URLSearchParams({ access_token: apiKey }).toString();
|
|
54
|
+
let res;
|
|
55
|
+
try {
|
|
56
|
+
res = await doFetch(url, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers: {
|
|
59
|
+
...baseHeaders,
|
|
60
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
61
|
+
},
|
|
62
|
+
body,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
throw makeDeployError("network", {
|
|
67
|
+
serverMessage: err instanceof Error ? err.message : String(err),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (res.status === 200)
|
|
71
|
+
return;
|
|
72
|
+
throw makeDeployError("http", { status: res.status });
|
|
73
|
+
};
|
|
74
|
+
const listStagings = async () => {
|
|
75
|
+
const records = [];
|
|
76
|
+
let offset = 0;
|
|
77
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
78
|
+
const url = `${endpoint}${STAGING_PATH}?offset=${offset}&limit=${PAGE_LIMIT}`;
|
|
79
|
+
const data = await request(url, {
|
|
80
|
+
method: "GET",
|
|
81
|
+
headers: baseHeaders,
|
|
82
|
+
});
|
|
83
|
+
records.push(...data.records);
|
|
84
|
+
if (records.length >= data.totalCount || data.records.length === 0) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
offset += PAGE_LIMIT;
|
|
88
|
+
}
|
|
89
|
+
return records;
|
|
90
|
+
};
|
|
91
|
+
const createDeployment = async (stagingId, bytes, immFileName, description) => {
|
|
92
|
+
const form = new FormData();
|
|
93
|
+
form.append("archiveFile", new File([bytes], immFileName, { type: "application/zip" }));
|
|
94
|
+
if (description && description.length > 0) {
|
|
95
|
+
form.append("description", description);
|
|
96
|
+
}
|
|
97
|
+
const url = `${endpoint}${STAGING_PATH}/${encodeURIComponent(stagingId)}/deploy`;
|
|
98
|
+
return request(url, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: baseHeaders,
|
|
101
|
+
body: form,
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
return { verifyToken, listStagings, createDeployment };
|
|
105
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const DEFAULT_DIR = "target";
|
|
4
|
+
const DEFAULT_PATTERN = /\.zip$/i;
|
|
5
|
+
export const scanTargets = async (projectDir, opts = {}) => {
|
|
6
|
+
const dir = opts.dir ?? DEFAULT_DIR;
|
|
7
|
+
const pattern = opts.pattern ?? DEFAULT_PATTERN;
|
|
8
|
+
const targetDir = join(projectDir, dir);
|
|
9
|
+
let entries;
|
|
10
|
+
try {
|
|
11
|
+
entries = await readdir(targetDir, { withFileTypes: true });
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
return entries
|
|
17
|
+
.filter((e) => e.isFile() && pattern.test(e.name))
|
|
18
|
+
.map((e) => join(targetDir, e.name))
|
|
19
|
+
.sort();
|
|
20
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type TargetSelection = {
|
|
2
|
+
auto: string;
|
|
3
|
+
} | {
|
|
4
|
+
candidates: string[];
|
|
5
|
+
};
|
|
6
|
+
type ArtifactInfo = {
|
|
7
|
+
artifactId: string;
|
|
8
|
+
projectVersion: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const selectDeployTarget: (zips: string[], settings: ArtifactInfo | null) => TargetSelection;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3
|
+
export const selectDeployTarget = (zips, settings) => {
|
|
4
|
+
if (!settings) {
|
|
5
|
+
return { candidates: zips };
|
|
6
|
+
}
|
|
7
|
+
const { artifactId, projectVersion } = settings;
|
|
8
|
+
const exactName = `${artifactId}-${projectVersion}.zip`;
|
|
9
|
+
const exact = zips.find((z) => basename(z) === exactName);
|
|
10
|
+
if (exact) {
|
|
11
|
+
return { auto: exact };
|
|
12
|
+
}
|
|
13
|
+
const intermediate = new RegExp(`^${escapeRegExp(artifactId)}-\\D`);
|
|
14
|
+
const filtered = zips.filter((z) => !intermediate.test(basename(z)));
|
|
15
|
+
return { candidates: filtered.length > 0 ? filtered : zips };
|
|
16
|
+
};
|
package/dist/i18n/en.js
CHANGED
|
@@ -86,4 +86,50 @@ export const messages = {
|
|
|
86
86
|
"nextSteps.cd": "cd {path}",
|
|
87
87
|
"nextSteps.agent.claudeCode": "Run claude to start agent-driven development",
|
|
88
88
|
"nextSteps.agent.githubCopilot": "Run gh copilot to start agent-driven development",
|
|
89
|
+
"credentials.prompt.endpoint": "Enter the iAP endpoint (base URL)",
|
|
90
|
+
"credentials.prompt.endpoint.hint": "Specify the iAP base URL (e.g. https://example.com/imart). The API path (/api/bearer/...) is assembled automatically",
|
|
91
|
+
"credentials.prompt.apiKey": "Enter the API key (OAuth Bearer token)",
|
|
92
|
+
"credentials.prompt.apiKey.hint": "iAP OAuth token (scope: development). Saved to .accel/credentials.json and added to .gitignore",
|
|
93
|
+
"credentials.error.required": "A value is required",
|
|
94
|
+
"credentials.error.missingNonInteractive": "Connection information is required in non-interactive mode. Missing: {fields} (provide them via --endpoint / --api-key or the ACCEL_ENDPOINT / ACCEL_API_KEY environment variables).",
|
|
95
|
+
"credentials.verify.progress": "Verifying your connection information...",
|
|
96
|
+
"credentials.verify.notice": "Please re-enter your connection information.",
|
|
97
|
+
"credentials.verify.error.network": "Could not connect to iAP: {message}",
|
|
98
|
+
"credentials.verify.error.unauthorized": "Authentication failed (401). Check that the API key is correct.",
|
|
99
|
+
"credentials.verify.error.notFound": "Could not reach the given endpoint (404). Check that the endpoint URL is correct.",
|
|
100
|
+
"credentials.verify.error.unexpected": "Failed to verify connection information (HTTP {status}).",
|
|
101
|
+
"login.intro": "Accel CLI - login",
|
|
102
|
+
"login.complete": "Connection information saved.",
|
|
103
|
+
"deploy.intro": "Accel CLI - deploy",
|
|
104
|
+
"deploy.progress.fetchingStagings": "Fetching staging environments...",
|
|
105
|
+
"deploy.progress.deploying": "Deploying...",
|
|
106
|
+
"deploy.selectStaging": "Select the target staging environment",
|
|
107
|
+
"deploy.selectZip": "Select the file to deploy",
|
|
108
|
+
"deploy.descriptionPrompt": "Enter a deployment description (optional)",
|
|
109
|
+
"deploy.confirm": "Deploy with the following settings. Continue?",
|
|
110
|
+
"deploy.confirm.endpoint": "Endpoint",
|
|
111
|
+
"deploy.confirm.stagingId": "Staging",
|
|
112
|
+
"deploy.confirm.sourceFile": "Source file",
|
|
113
|
+
"deploy.confirm.sentAs": "Sent as",
|
|
114
|
+
"deploy.result.deployId": "Deploy ID",
|
|
115
|
+
"deploy.result.stagingId": "Staging",
|
|
116
|
+
"deploy.result.status": "Status",
|
|
117
|
+
"deploy.result.createDate": "Created at",
|
|
118
|
+
"deploy.success": "Deployment complete.",
|
|
119
|
+
"deploy.cancelled": "Deployment cancelled.",
|
|
120
|
+
"deploy.error.noStagings": "No staging environments found. Create a staging environment on the iAP side first.",
|
|
121
|
+
"deploy.error.noTargetZip": "No deployable zip found under ./target/. Run a build (e.g. mvn package) first.",
|
|
122
|
+
"deploy.error.missingStagingId": "--staging-id is required in non-interactive mode. Specify the target staging ID.",
|
|
123
|
+
"deploy.error.zipNotResolved": "Could not uniquely determine the zip to deploy. Specify the file explicitly with --file.",
|
|
124
|
+
"deploy.error.fileNotFound": "The specified file was not found: {path}",
|
|
125
|
+
"deploy.error.network": "Could not connect to iAP: {message}",
|
|
126
|
+
"deploy.error.server": "iAP returned an error: {message}",
|
|
127
|
+
"deploy.error.unauthorized": "Authentication failed (401). Check that the API key is correct.",
|
|
128
|
+
"deploy.error.forbidden": "Access denied (403). Check your permissions for this staging.",
|
|
129
|
+
"deploy.error.notFound": "Staging not found (404). Create the staging environment beforehand.",
|
|
130
|
+
"deploy.error.badRequest": "Bad request (400).",
|
|
131
|
+
"deploy.error.serverError": "A server error occurred (500).",
|
|
132
|
+
"deploy.error.unexpectedResponse": "Received an unexpected response (HTTP {status}).",
|
|
133
|
+
"deploy.reauth.notFound": "The endpoint could not be reached (404). Check that the endpoint URL is correct.",
|
|
134
|
+
"deploy.reauth.notice": "Please re-enter your connection information.",
|
|
89
135
|
};
|
package/dist/i18n/ja.js
CHANGED
|
@@ -86,4 +86,50 @@ export const messages = {
|
|
|
86
86
|
"nextSteps.cd": "cd {path}",
|
|
87
87
|
"nextSteps.agent.claudeCode": "claude を起動してエージェント開発を始めてください",
|
|
88
88
|
"nextSteps.agent.githubCopilot": "gh copilot を起動してエージェント開発を始めてください",
|
|
89
|
+
"credentials.prompt.endpoint": "iAP のエンドポイント(ベースURL)を入力してください",
|
|
90
|
+
"credentials.prompt.endpoint.hint": "iAP のベースURL を指定してください(例: https://example.com/imart)。API パス(/api/bearer/...)は自動で組み立てられます",
|
|
91
|
+
"credentials.prompt.apiKey": "APIキー(OAuth Bearer トークン)を入力してください",
|
|
92
|
+
"credentials.prompt.apiKey.hint": "iAP の OAuth トークン(scope: development)。.accel/credentials.json に保存され、.gitignore に追加されます",
|
|
93
|
+
"credentials.error.required": "値を入力してください",
|
|
94
|
+
"credentials.error.missingNonInteractive": "非対話モードでは接続情報が必要です。未指定の項目: {fields}(--endpoint / --api-key または ACCEL_ENDPOINT / ACCEL_API_KEY 環境変数で指定してください)",
|
|
95
|
+
"credentials.verify.progress": "接続情報を確認しています...",
|
|
96
|
+
"credentials.verify.notice": "接続情報を再入力してください。",
|
|
97
|
+
"credentials.verify.error.network": "iAP に接続できませんでした: {message}",
|
|
98
|
+
"credentials.verify.error.unauthorized": "認証に失敗しました(401)。APIキーが正しいか確認してください。",
|
|
99
|
+
"credentials.verify.error.notFound": "指定されたエンドポイントにアクセスできません(404)。エンドポイントの URL が正しいか確認してください。",
|
|
100
|
+
"credentials.verify.error.unexpected": "接続情報の確認に失敗しました(HTTP {status})。",
|
|
101
|
+
"login.intro": "Accel CLI - login",
|
|
102
|
+
"login.complete": "接続情報を保存しました。",
|
|
103
|
+
"deploy.intro": "Accel CLI - deploy",
|
|
104
|
+
"deploy.progress.fetchingStagings": "ステージング環境を取得しています...",
|
|
105
|
+
"deploy.progress.deploying": "デプロイしています...",
|
|
106
|
+
"deploy.selectStaging": "デプロイ先のステージング環境を選択してください",
|
|
107
|
+
"deploy.selectZip": "デプロイするファイルを選択してください",
|
|
108
|
+
"deploy.descriptionPrompt": "デプロイメントの説明を入力してください(省略可)",
|
|
109
|
+
"deploy.confirm": "以下の内容でデプロイします。よろしいですか?",
|
|
110
|
+
"deploy.confirm.endpoint": "エンドポイント",
|
|
111
|
+
"deploy.confirm.stagingId": "ステージング",
|
|
112
|
+
"deploy.confirm.sourceFile": "対象ファイル",
|
|
113
|
+
"deploy.confirm.sentAs": "送信ファイル名",
|
|
114
|
+
"deploy.result.deployId": "デプロイID",
|
|
115
|
+
"deploy.result.stagingId": "ステージング",
|
|
116
|
+
"deploy.result.status": "ステータス",
|
|
117
|
+
"deploy.result.createDate": "作成日時",
|
|
118
|
+
"deploy.success": "デプロイが完了しました。",
|
|
119
|
+
"deploy.cancelled": "デプロイをキャンセルしました。",
|
|
120
|
+
"deploy.error.noStagings": "ステージング環境が見つかりません。先に iAP 側でステージング環境を作成してください。",
|
|
121
|
+
"deploy.error.noTargetZip": "./target/ 配下にデプロイ対象の zip が見つかりません。先にビルド(mvn package 等)を実行してください。",
|
|
122
|
+
"deploy.error.missingStagingId": "非対話モードでは --staging-id が必須です。デプロイ先のステージングIDを指定してください。",
|
|
123
|
+
"deploy.error.zipNotResolved": "デプロイ対象の zip を一意に特定できませんでした。--file でデプロイするファイルを明示してください。",
|
|
124
|
+
"deploy.error.fileNotFound": "指定されたファイルが見つかりません: {path}",
|
|
125
|
+
"deploy.error.network": "iAP に接続できませんでした: {message}",
|
|
126
|
+
"deploy.error.server": "iAP からエラーが返されました: {message}",
|
|
127
|
+
"deploy.error.unauthorized": "認証に失敗しました(401)。APIキーが正しいか確認してください。",
|
|
128
|
+
"deploy.error.forbidden": "アクセスが拒否されました(403)。このステージングへの権限を確認してください。",
|
|
129
|
+
"deploy.error.notFound": "ステージングが見つかりません(404)。事前にステージング環境を作成してください。",
|
|
130
|
+
"deploy.error.badRequest": "リクエストが不正です(400)。",
|
|
131
|
+
"deploy.error.serverError": "サーバーエラーが発生しました(500)。",
|
|
132
|
+
"deploy.error.unexpectedResponse": "想定外のレスポンスを受信しました(HTTP {status})。",
|
|
133
|
+
"deploy.reauth.notFound": "指定されたエンドポイントにアクセスできません(404)。エンドポイントの URL が正しいか確認してください。",
|
|
134
|
+
"deploy.reauth.notice": "接続情報を再入力してください。",
|
|
89
135
|
};
|
package/dist/i18n/zh_CN.js
CHANGED
|
@@ -86,4 +86,50 @@ export const messages = {
|
|
|
86
86
|
"nextSteps.cd": "cd {path}",
|
|
87
87
|
"nextSteps.agent.claudeCode": "执行 claude 开始基于代理的开发",
|
|
88
88
|
"nextSteps.agent.githubCopilot": "执行 gh copilot 开始基于代理的开发",
|
|
89
|
+
"credentials.prompt.endpoint": "请输入 iAP 端点(基础URL)",
|
|
90
|
+
"credentials.prompt.endpoint.hint": "请指定 iAP 的基础URL(例如: https://example.com/imart)。API 路径(/api/bearer/...)会自动拼接",
|
|
91
|
+
"credentials.prompt.apiKey": "请输入 API 密钥(OAuth Bearer 令牌)",
|
|
92
|
+
"credentials.prompt.apiKey.hint": "iAP 的 OAuth 令牌(scope: development)。将保存到 .accel/credentials.json 并添加到 .gitignore",
|
|
93
|
+
"credentials.error.required": "请输入值",
|
|
94
|
+
"credentials.error.missingNonInteractive": "非交互模式下需要连接信息。缺少项:{fields}(请通过 --endpoint / --api-key 或 ACCEL_ENDPOINT / ACCEL_API_KEY 环境变量指定)。",
|
|
95
|
+
"credentials.verify.progress": "正在确认连接信息...",
|
|
96
|
+
"credentials.verify.notice": "请重新输入连接信息。",
|
|
97
|
+
"credentials.verify.error.network": "无法连接到 iAP: {message}",
|
|
98
|
+
"credentials.verify.error.unauthorized": "认证失败(401)。请确认 API 密钥是否正确。",
|
|
99
|
+
"credentials.verify.error.notFound": "无法访问指定的端点(404)。请确认端点 URL 是否正确。",
|
|
100
|
+
"credentials.verify.error.unexpected": "确认连接信息失败(HTTP {status})。",
|
|
101
|
+
"login.intro": "Accel CLI - login",
|
|
102
|
+
"login.complete": "连接信息已保存。",
|
|
103
|
+
"deploy.intro": "Accel CLI - deploy",
|
|
104
|
+
"deploy.progress.fetchingStagings": "正在获取暂存环境...",
|
|
105
|
+
"deploy.progress.deploying": "正在部署...",
|
|
106
|
+
"deploy.selectStaging": "请选择部署目标暂存环境",
|
|
107
|
+
"deploy.selectZip": "请选择要部署的文件",
|
|
108
|
+
"deploy.descriptionPrompt": "请输入部署说明(可选)",
|
|
109
|
+
"deploy.confirm": "将按以下内容部署。是否继续?",
|
|
110
|
+
"deploy.confirm.endpoint": "端点",
|
|
111
|
+
"deploy.confirm.stagingId": "暂存环境",
|
|
112
|
+
"deploy.confirm.sourceFile": "目标文件",
|
|
113
|
+
"deploy.confirm.sentAs": "发送文件名",
|
|
114
|
+
"deploy.result.deployId": "部署ID",
|
|
115
|
+
"deploy.result.stagingId": "暂存环境",
|
|
116
|
+
"deploy.result.status": "状态",
|
|
117
|
+
"deploy.result.createDate": "创建时间",
|
|
118
|
+
"deploy.success": "部署完成。",
|
|
119
|
+
"deploy.cancelled": "已取消部署。",
|
|
120
|
+
"deploy.error.noStagings": "未找到暂存环境。请先在 iAP 侧创建暂存环境。",
|
|
121
|
+
"deploy.error.noTargetZip": "在 ./target/ 下未找到可部署的 zip。请先执行构建(如 mvn package)。",
|
|
122
|
+
"deploy.error.missingStagingId": "非交互模式下必须指定 --staging-id。请指定目标暂存环境 ID。",
|
|
123
|
+
"deploy.error.zipNotResolved": "无法唯一确定要部署的 zip。请使用 --file 明确指定要部署的文件。",
|
|
124
|
+
"deploy.error.fileNotFound": "未找到指定的文件:{path}",
|
|
125
|
+
"deploy.error.network": "无法连接到 iAP: {message}",
|
|
126
|
+
"deploy.error.server": "iAP 返回了错误: {message}",
|
|
127
|
+
"deploy.error.unauthorized": "认证失败(401)。请确认 API 密钥是否正确。",
|
|
128
|
+
"deploy.error.forbidden": "访问被拒绝(403)。请确认对该暂存环境的权限。",
|
|
129
|
+
"deploy.error.notFound": "未找到暂存环境(404)。请事先创建暂存环境。",
|
|
130
|
+
"deploy.error.badRequest": "请求无效(400)。",
|
|
131
|
+
"deploy.error.serverError": "发生服务器错误(500)。",
|
|
132
|
+
"deploy.error.unexpectedResponse": "收到意外的响应(HTTP {status})。",
|
|
133
|
+
"deploy.reauth.notFound": "无法访问指定的端点(404)。请确认端点 URL 是否正确。",
|
|
134
|
+
"deploy.reauth.notice": "请重新输入连接信息。",
|
|
89
135
|
};
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ import { defineCommand, runMain } from "citty";
|
|
|
3
3
|
import { initCommand } from "./commands/init.js";
|
|
4
4
|
import { attachCommand } from "./commands/attach.js";
|
|
5
5
|
import { detachCommand } from "./commands/detach.js";
|
|
6
|
+
import { loginCommand } from "./commands/login.js";
|
|
7
|
+
import { deployCommand } from "./commands/deploy.js";
|
|
6
8
|
const main = defineCommand({
|
|
7
9
|
meta: {
|
|
8
10
|
name: "accel",
|
|
@@ -13,6 +15,8 @@ const main = defineCommand({
|
|
|
13
15
|
init: initCommand,
|
|
14
16
|
attach: attachCommand,
|
|
15
17
|
detach: detachCommand,
|
|
18
|
+
login: loginCommand,
|
|
19
|
+
deploy: deployCommand,
|
|
16
20
|
},
|
|
17
21
|
});
|
|
18
22
|
runMain(main);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AccelCredentials } from "../core/types.js";
|
|
2
|
+
import { type ApiClient } from "../deploy/api-client.js";
|
|
3
|
+
export declare const isRetryableCredentialError: (err: unknown) => boolean;
|
|
4
|
+
export type VerifyDeps = {
|
|
5
|
+
apiClientFactory: (endpoint: string, apiKey: string) => ApiClient;
|
|
6
|
+
repromptCredentials: (current: AccelCredentials, locale: string) => Promise<AccelCredentials>;
|
|
7
|
+
persistCredentials: (projectDir: string, creds: AccelCredentials) => Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export declare const verifyCredentialsLoop: (projectDir: string, locale: string, initial: AccelCredentials, opts: {
|
|
10
|
+
dirty: boolean;
|
|
11
|
+
interactive?: boolean;
|
|
12
|
+
}, deps: VerifyDeps) => Promise<{
|
|
13
|
+
creds: AccelCredentials;
|
|
14
|
+
api: ApiClient;
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { isDeployError } from "../deploy/api-client.js";
|
|
3
|
+
import { getMessage } from "../i18n/index.js";
|
|
4
|
+
export const isRetryableCredentialError = (err) => isDeployError(err) &&
|
|
5
|
+
(err.kind === "network" || err.status === 401 || err.status === 404);
|
|
6
|
+
const formatRetryableError = (err, locale) => {
|
|
7
|
+
if (err.kind === "network") {
|
|
8
|
+
return getMessage("credentials.verify.error.network", locale, {
|
|
9
|
+
message: err.serverMessage ?? "",
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
if (err.status === 401) {
|
|
13
|
+
return getMessage("credentials.verify.error.unauthorized", locale);
|
|
14
|
+
}
|
|
15
|
+
return getMessage("credentials.verify.error.notFound", locale);
|
|
16
|
+
};
|
|
17
|
+
const formatTerminalError = (err, locale) => {
|
|
18
|
+
if (isDeployError(err)) {
|
|
19
|
+
return getMessage("credentials.verify.error.unexpected", locale, {
|
|
20
|
+
status: String(err.status ?? "?"),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return err instanceof Error ? err.message : String(err);
|
|
24
|
+
};
|
|
25
|
+
export const verifyCredentialsLoop = async (projectDir, locale, initial, opts, deps) => {
|
|
26
|
+
const interactive = opts.interactive ?? true;
|
|
27
|
+
let creds = initial;
|
|
28
|
+
let dirty = opts.dirty;
|
|
29
|
+
let api = deps.apiClientFactory(creds.endpoint, creds.apiKey);
|
|
30
|
+
const spin = p.spinner();
|
|
31
|
+
for (;;) {
|
|
32
|
+
spin.start(getMessage("credentials.verify.progress", locale));
|
|
33
|
+
try {
|
|
34
|
+
await api.verifyToken();
|
|
35
|
+
spin.stop(getMessage("credentials.verify.progress", locale));
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
spin.stop(getMessage("credentials.verify.progress", locale));
|
|
40
|
+
if (!interactive) {
|
|
41
|
+
p.log.error(isRetryableCredentialError(err)
|
|
42
|
+
? formatRetryableError(err, locale)
|
|
43
|
+
: formatTerminalError(err, locale));
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
if (!isRetryableCredentialError(err)) {
|
|
47
|
+
p.log.error(formatTerminalError(err, locale));
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
p.log.error(formatRetryableError(err, locale));
|
|
51
|
+
p.log.info(getMessage("credentials.verify.notice", locale));
|
|
52
|
+
creds = await deps.repromptCredentials(creds, locale);
|
|
53
|
+
api = deps.apiClientFactory(creds.endpoint, creds.apiKey);
|
|
54
|
+
dirty = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (dirty) {
|
|
58
|
+
await deps.persistCredentials(projectDir, creds);
|
|
59
|
+
}
|
|
60
|
+
return { creds, api };
|
|
61
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AccelCredentials } from "../core/types.js";
|
|
2
|
+
export declare const ensureCredentials: (projectDir: string, locale: string, seed?: Partial<AccelCredentials>) => Promise<AccelCredentials>;
|
|
3
|
+
export declare const resolveCredentialsOrThrow: (projectDir: string, locale: string, seed: Partial<AccelCredentials>) => Promise<AccelCredentials>;
|
|
4
|
+
export declare const repromptCredentials: (current: AccelCredentials, locale: string, seed?: Partial<AccelCredentials>) => Promise<AccelCredentials>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { readCredentials, persistCredentials, normalizeEndpoint, } from "../utils/credentials.js";
|
|
3
|
+
import { getMessage } from "../i18n/index.js";
|
|
4
|
+
import { withHint } from "./format.js";
|
|
5
|
+
const requiredValidator = (locale) => (value) => !value || value.trim().length === 0
|
|
6
|
+
? getMessage("credentials.error.required", locale)
|
|
7
|
+
: undefined;
|
|
8
|
+
export const ensureCredentials = async (projectDir, locale, seed = {}) => {
|
|
9
|
+
const existing = await readCredentials(projectDir);
|
|
10
|
+
let endpoint = seed.endpoint ?? existing.endpoint;
|
|
11
|
+
let apiKey = seed.apiKey ?? existing.apiKey;
|
|
12
|
+
let changed = false;
|
|
13
|
+
if (!endpoint) {
|
|
14
|
+
const input = await p.text({
|
|
15
|
+
message: withHint("credentials.prompt.endpoint", locale),
|
|
16
|
+
validate: requiredValidator(locale),
|
|
17
|
+
});
|
|
18
|
+
if (p.isCancel(input))
|
|
19
|
+
process.exit(0);
|
|
20
|
+
endpoint = input;
|
|
21
|
+
changed = true;
|
|
22
|
+
}
|
|
23
|
+
if (!apiKey) {
|
|
24
|
+
const input = await p.password({
|
|
25
|
+
message: withHint("credentials.prompt.apiKey", locale),
|
|
26
|
+
validate: requiredValidator(locale),
|
|
27
|
+
});
|
|
28
|
+
if (p.isCancel(input))
|
|
29
|
+
process.exit(0);
|
|
30
|
+
apiKey = input;
|
|
31
|
+
changed = true;
|
|
32
|
+
}
|
|
33
|
+
const creds = {
|
|
34
|
+
endpoint: normalizeEndpoint(endpoint),
|
|
35
|
+
apiKey,
|
|
36
|
+
};
|
|
37
|
+
if (changed) {
|
|
38
|
+
await persistCredentials(projectDir, creds);
|
|
39
|
+
}
|
|
40
|
+
return creds;
|
|
41
|
+
};
|
|
42
|
+
export const resolveCredentialsOrThrow = async (projectDir, locale, seed) => {
|
|
43
|
+
const existing = await readCredentials(projectDir);
|
|
44
|
+
const endpoint = seed.endpoint ?? existing.endpoint;
|
|
45
|
+
const apiKey = seed.apiKey ?? existing.apiKey;
|
|
46
|
+
const missing = [];
|
|
47
|
+
if (!endpoint || endpoint.trim().length === 0)
|
|
48
|
+
missing.push("endpoint");
|
|
49
|
+
if (!apiKey || apiKey.trim().length === 0)
|
|
50
|
+
missing.push("apiKey");
|
|
51
|
+
if (missing.length > 0) {
|
|
52
|
+
p.log.error(getMessage("credentials.error.missingNonInteractive", locale, {
|
|
53
|
+
fields: missing.join(", "),
|
|
54
|
+
}));
|
|
55
|
+
throw new Error(`missing credentials: ${missing.join(", ")}`);
|
|
56
|
+
}
|
|
57
|
+
return { endpoint: normalizeEndpoint(endpoint), apiKey: apiKey };
|
|
58
|
+
};
|
|
59
|
+
export const repromptCredentials = async (current, locale, seed = {}) => {
|
|
60
|
+
let endpoint = seed.endpoint;
|
|
61
|
+
if (endpoint === undefined) {
|
|
62
|
+
const endpointInput = await p.text({
|
|
63
|
+
message: withHint("credentials.prompt.endpoint", locale),
|
|
64
|
+
initialValue: current.endpoint,
|
|
65
|
+
validate: requiredValidator(locale),
|
|
66
|
+
});
|
|
67
|
+
if (p.isCancel(endpointInput))
|
|
68
|
+
process.exit(0);
|
|
69
|
+
endpoint = endpointInput;
|
|
70
|
+
}
|
|
71
|
+
let apiKey = seed.apiKey;
|
|
72
|
+
if (apiKey === undefined) {
|
|
73
|
+
const apiKeyInput = await p.password({
|
|
74
|
+
message: withHint("credentials.prompt.apiKey", locale),
|
|
75
|
+
validate: requiredValidator(locale),
|
|
76
|
+
});
|
|
77
|
+
if (p.isCancel(apiKeyInput))
|
|
78
|
+
process.exit(0);
|
|
79
|
+
apiKey = apiKeyInput;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
endpoint: normalizeEndpoint(endpoint),
|
|
83
|
+
apiKey,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
|
-
import {
|
|
2
|
+
import { SELECTABLE_VERSIONS, resolveAccelplatformVersion, } from "../core/version-map.js";
|
|
3
|
+
import { MODULE_OPTIONS, DATABASE_OPTIONS, AGENT_OPTIONS, DEFAULT_SETTINGS, } from "../core/constants.js";
|
|
3
4
|
import { createValidators } from "../core/validators.js";
|
|
4
5
|
import { getMessage } from "../i18n/index.js";
|
|
5
6
|
import { withHint } from "./format.js";
|
|
@@ -25,6 +26,12 @@ export const validateNonInteractive = (opts) => {
|
|
|
25
26
|
const missing = [];
|
|
26
27
|
if (!opts.name)
|
|
27
28
|
missing.push("--name");
|
|
29
|
+
if (!opts.jugglingProject) {
|
|
30
|
+
if (!opts.accelplatformVersion)
|
|
31
|
+
missing.push("--accelplatform-version");
|
|
32
|
+
if (!opts.module || opts.module.length === 0)
|
|
33
|
+
missing.push("--module");
|
|
34
|
+
}
|
|
28
35
|
return missing;
|
|
29
36
|
};
|
|
30
37
|
export const validateCliValues = (opts, validators) => {
|
|
@@ -103,8 +110,14 @@ export const runPrompts = async (opts) => {
|
|
|
103
110
|
group: opts.group ?? DEFAULT_SETTINGS.group,
|
|
104
111
|
projectVersion: opts.projectVersion ?? DEFAULT_SETTINGS.projectVersion,
|
|
105
112
|
description: opts.description ?? DEFAULT_SETTINGS.description,
|
|
106
|
-
accelplatformVersion:
|
|
107
|
-
|
|
113
|
+
accelplatformVersion: resolveAccelplatformVersion(opts.accelplatformVersion ??
|
|
114
|
+
jugglingVersion ??
|
|
115
|
+
DEFAULT_SETTINGS.accelplatformVersion.label),
|
|
116
|
+
modules: opts.module && opts.module.length > 0
|
|
117
|
+
? opts.module
|
|
118
|
+
: jugglingModules.length > 0
|
|
119
|
+
? jugglingModules
|
|
120
|
+
: [],
|
|
108
121
|
database: (opts.database ?? DEFAULT_SETTINGS.database),
|
|
109
122
|
agents: opts.agent ?? detectDefaultAgents(),
|
|
110
123
|
javascript: opts.javascript ?? DEFAULT_SETTINGS.javascript,
|
|
@@ -156,8 +169,45 @@ export const runPrompts = async (opts) => {
|
|
|
156
169
|
p.log.warning(err instanceof Error ? err.message : String(err));
|
|
157
170
|
}
|
|
158
171
|
}
|
|
159
|
-
const
|
|
160
|
-
|
|
172
|
+
const versionDefault = opts.accelplatformVersion ?? jugglingVersion ?? DEFAULT_SETTINGS.accelplatformVersion.label;
|
|
173
|
+
if (opts.accelplatformVersion && jugglingVersion && opts.accelplatformVersion !== jugglingVersion) {
|
|
174
|
+
p.log.warning(getMessage("warning.versionMismatch", locale, {
|
|
175
|
+
option: opts.accelplatformVersion,
|
|
176
|
+
juggling: jugglingVersion,
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
const accelplatformVersion = (await p.select({
|
|
180
|
+
message: withHint("prompt.accelplatformVersion", locale),
|
|
181
|
+
options: SELECTABLE_VERSIONS.map((v) => ({
|
|
182
|
+
value: v.label,
|
|
183
|
+
label: v.codename ? `${v.label} (${v.codename})` : v.label,
|
|
184
|
+
})),
|
|
185
|
+
initialValue: versionDefault,
|
|
186
|
+
}));
|
|
187
|
+
if (p.isCancel(accelplatformVersion))
|
|
188
|
+
process.exit(0);
|
|
189
|
+
const moduleDefault = opts.module && opts.module.length > 0
|
|
190
|
+
? opts.module
|
|
191
|
+
: jugglingModules.length > 0
|
|
192
|
+
? jugglingModules
|
|
193
|
+
: [];
|
|
194
|
+
if (opts.module && opts.module.length > 0 && jugglingModules.length > 0) {
|
|
195
|
+
const diff = opts.module.some((m) => !jugglingModules.includes(m)) ||
|
|
196
|
+
jugglingModules.some((m) => !opts.module.includes(m));
|
|
197
|
+
if (diff) {
|
|
198
|
+
p.log.warning(getMessage("warning.moduleMismatch", locale));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const modules = (await p.multiselect({
|
|
202
|
+
message: withHint("prompt.modules", locale),
|
|
203
|
+
options: MODULE_OPTIONS.map((m) => ({
|
|
204
|
+
value: m,
|
|
205
|
+
label: m,
|
|
206
|
+
})),
|
|
207
|
+
initialValues: moduleDefault,
|
|
208
|
+
}));
|
|
209
|
+
if (p.isCancel(modules))
|
|
210
|
+
process.exit(0);
|
|
161
211
|
const group = (await p.text({
|
|
162
212
|
message: withHint("prompt.group", locale),
|
|
163
213
|
defaultValue: opts.group ?? DEFAULT_SETTINGS.group,
|
|
@@ -222,7 +272,7 @@ export const runPrompts = async (opts) => {
|
|
|
222
272
|
group,
|
|
223
273
|
projectVersion,
|
|
224
274
|
description,
|
|
225
|
-
accelplatformVersion,
|
|
275
|
+
accelplatformVersion: resolveAccelplatformVersion(accelplatformVersion),
|
|
226
276
|
modules,
|
|
227
277
|
database,
|
|
228
278
|
agents,
|
|
@@ -15,6 +15,14 @@ export const buildSummaryBody = (settings, outputDir, locale) => {
|
|
|
15
15
|
labelKey: "summary.label.description",
|
|
16
16
|
value: formatString(settings.description, empty),
|
|
17
17
|
},
|
|
18
|
+
{
|
|
19
|
+
labelKey: "summary.label.accelplatformVersion",
|
|
20
|
+
value: settings.accelplatformVersion.label,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
labelKey: "summary.label.modules",
|
|
24
|
+
value: formatList(settings.modules, empty),
|
|
25
|
+
},
|
|
18
26
|
{ labelKey: "summary.label.database", value: settings.database },
|
|
19
27
|
{
|
|
20
28
|
labelKey: "summary.label.javascript",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const lastFlagValue: (v: unknown) => string | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AccelCredentials } from "../core/types.js";
|
|
2
|
+
export declare const CREDENTIALS_GITIGNORE_ENTRY = ".accel/credentials.json";
|
|
3
|
+
export declare const normalizeEndpoint: (raw: string) => string;
|
|
4
|
+
export declare const ENDPOINT_ENV = "ACCEL_ENDPOINT";
|
|
5
|
+
export declare const API_KEY_ENV = "ACCEL_API_KEY";
|
|
6
|
+
export declare const resolveCredentialInputs: (flags: {
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
}, env: Record<string, string | undefined>) => Partial<AccelCredentials>;
|
|
10
|
+
export declare const readCredentials: (projectDir: string) => Promise<Partial<AccelCredentials>>;
|
|
11
|
+
export declare const writeCredentials: (projectDir: string, creds: AccelCredentials) => Promise<void>;
|
|
12
|
+
export declare const persistCredentials: (projectDir: string, creds: AccelCredentials) => Promise<void>;
|