@intra-mart/accel 0.2.0 → 0.3.0-dev.202606150745

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.
Files changed (68) hide show
  1. package/README.md +148 -4
  2. package/dist/asset/deployer.js +8 -17
  3. package/dist/asset/foreach-replacement.d.ts +2 -0
  4. package/dist/asset/foreach-replacement.js +20 -0
  5. package/dist/asset/github-provider.d.ts +2 -0
  6. package/dist/asset/github-provider.js +37 -0
  7. package/dist/asset/local-provider.js +1 -22
  8. package/dist/asset/walker.js +4 -15
  9. package/dist/commands/attach.d.ts +13 -1
  10. package/dist/commands/attach.js +33 -12
  11. package/dist/commands/deploy.d.ts +62 -0
  12. package/dist/commands/deploy.js +293 -0
  13. package/dist/commands/init.d.ts +13 -1
  14. package/dist/commands/init.js +33 -12
  15. package/dist/commands/login.d.ts +31 -0
  16. package/dist/commands/login.js +75 -0
  17. package/dist/core/catalog.d.ts +11 -0
  18. package/dist/core/catalog.js +40 -0
  19. package/dist/core/condition-evaluator.js +6 -3
  20. package/dist/core/constants.d.ts +2 -2
  21. package/dist/core/constants.js +4 -9
  22. package/dist/core/eval-context.d.ts +3 -0
  23. package/dist/core/eval-context.js +45 -0
  24. package/dist/core/types.d.ts +76 -10
  25. package/dist/core/types.js +2 -1
  26. package/dist/core/validators.d.ts +7 -2
  27. package/dist/core/validators.js +25 -12
  28. package/dist/core/variable-interpolator.d.ts +1 -1
  29. package/dist/core/variable-interpolator.js +19 -19
  30. package/dist/deploy/api-client.d.ts +16 -0
  31. package/dist/deploy/api-client.js +105 -0
  32. package/dist/deploy/target-scanner.d.ts +5 -0
  33. package/dist/deploy/target-scanner.js +20 -0
  34. package/dist/deploy/target-selector.d.ts +11 -0
  35. package/dist/deploy/target-selector.js +16 -0
  36. package/dist/i18n/en.js +53 -1
  37. package/dist/i18n/ja.js +53 -1
  38. package/dist/i18n/zh_CN.js +53 -1
  39. package/dist/index.js +4 -0
  40. package/dist/interactive/credential-auth.d.ts +15 -0
  41. package/dist/interactive/credential-auth.js +61 -0
  42. package/dist/interactive/credentials-prompts.d.ts +4 -0
  43. package/dist/interactive/credentials-prompts.js +85 -0
  44. package/dist/interactive/prompts.d.ts +3 -0
  45. package/dist/interactive/prompts.js +125 -26
  46. package/dist/interactive/summary.js +12 -0
  47. package/dist/juggling/extractor.d.ts +3 -2
  48. package/dist/juggling/extractor.js +9 -9
  49. package/dist/utils/args.d.ts +1 -0
  50. package/dist/utils/args.js +4 -0
  51. package/dist/utils/credentials.d.ts +12 -0
  52. package/dist/utils/credentials.js +46 -0
  53. package/dist/utils/date-formatter.d.ts +1 -0
  54. package/dist/utils/date-formatter.js +23 -0
  55. package/dist/utils/gitignore.d.ts +1 -0
  56. package/dist/utils/gitignore.js +23 -0
  57. package/dist/utils/https.d.ts +2 -0
  58. package/dist/utils/https.js +44 -0
  59. package/dist/utils/settings-io.d.ts +1 -0
  60. package/dist/utils/settings-io.js +8 -0
  61. package/package.json +5 -5
  62. package/assets/assets.tar.gz +0 -0
  63. package/dist/asset/default-source.d.ts +0 -1
  64. package/dist/asset/default-source.js +0 -6
  65. package/dist/core/module-map.d.ts +0 -3
  66. package/dist/core/module-map.js +0 -7
  67. package/dist/core/version-map.d.ts +0 -7
  68. package/dist/core/version-map.js +0 -45
package/README.md CHANGED
@@ -7,7 +7,7 @@ intra-mart Accel Platform (iAP) 開発プロジェクトの作成・管理を行
7
7
  ## 必要環境
8
8
 
9
9
  - 以下のいずれかのパッケージマネージャ(`--package-manager` で選択、デフォルト: Bun)
10
- - [Bun](https://bun.sh/) v1.0+
10
+ - [Bun](https://bun.sh/)
11
11
  - [npm](https://www.npmjs.com/)
12
12
  - [Yarn](https://classic.yarnpkg.com/) (classic v1)
13
13
  - [pnpm](https://pnpm.io/)
@@ -44,6 +44,9 @@ accel init my-project
44
44
  ```bash
45
45
  accel init my-project \
46
46
  --non-interactive \
47
+ --accelplatform-version 2026-spring \
48
+ --module im_workflow \
49
+ --feature jssp,e2e \
47
50
  --agent claude-code \
48
51
  --locale ja
49
52
  ```
@@ -53,6 +56,9 @@ accel init my-project \
53
56
  | オプション | 型 | 説明 | デフォルト |
54
57
  |---|---|---|---|
55
58
  | `[project-name]` (位置引数) | string | プロジェクト名(ディレクトリ名と一致) | `my-accel-project`(対話で変更可) |
59
+ | `--accelplatform-version` | string | iAPバージョン。カタログのリリースID形式(例: `2026-spring`) | 対話で選択 |
60
+ | `--module` | string | 使用モジュール。カタログの `artifactId`(カンマ区切り。例: `im_workflow,im_activiti`) | 対話で選択 |
61
+ | `--feature` | string | 使用機能。カタログの機能ID(カンマ区切り。例: `jssp,e2e`) | 未選択 |
56
62
  | `--agent` | string | エージェント種別(`claude-code`, `github-copilot`、カンマ区切り) | 自動検出 |
57
63
  | `--locale` | string | ロケール(`ja`, `en`, `zh_CN`) | OS設定から自動検出 |
58
64
  | `--artifact-id` | string | アーティファクトID(pomの `artifactId` に相当) | プロジェクト名と同値 |
@@ -66,6 +72,10 @@ accel init my-project \
66
72
  | `--non-interactive` | boolean | 非対話モード(このモードでは `project-name` が必須) | `false` |
67
73
  | `--no-git-init` | boolean | git initをスキップ | `false`(git initする) |
68
74
  | `--skip-install` | boolean | 依存インストールをスキップ | `false` |
75
+ | `--asset-server-url` | string | 資材を取得するGitHubリポジトリのURL(デフォルトProviderの取得先を上書き) | `https://github.com/accelplatform/skills` |
76
+ | `--asset-ref` | string | GitHubから取得するブランチ/タグ/コミット | `experiment` |
77
+ | `--asset-source` | string | (デバッグ用)ローカル資材のパス(tarアーカイブまたは展開済みディレクトリ)。明示時はGitHubより優先 | - |
78
+ | `--debug-server-url` | string | (デバッグ用)ローカル資材サーバーのURL(`GET /archive`)。明示時はGitHubより優先 | - |
69
79
 
70
80
  ### `accel attach`
71
81
 
@@ -73,7 +83,7 @@ accel init my-project \
73
83
 
74
84
  ```bash
75
85
  cd my-existing-project
76
- accel attach --non-interactive
86
+ accel attach --non-interactive --accelplatform-version 2026-spring --module im_workflow
77
87
  ```
78
88
 
79
89
  `init` との違い:
@@ -89,6 +99,9 @@ accel attach --non-interactive
89
99
  | オプション | 型 | 説明 | デフォルト |
90
100
  |---|---|---|---|
91
101
  | `--name` | string | プロジェクト名 | カレントディレクトリ名 |
102
+ | `--accelplatform-version` | string | iAPバージョン。カタログのリリースID形式(例: `2026-spring`) | 対話で選択 |
103
+ | `--module` | string | 使用モジュール。カタログの `artifactId`(カンマ区切り。例: `im_workflow,im_activiti`) | 対話で選択 |
104
+ | `--feature` | string | 使用機能。カタログの機能ID(カンマ区切り。例: `jssp,e2e`) | 未選択 |
92
105
  | `--agent` | string | エージェント種別(`claude-code`, `github-copilot`、カンマ区切り) | 自動検出 |
93
106
  | `--locale` | string | ロケール(`ja`, `en`, `zh_CN`) | OS設定から自動検出 |
94
107
  | `--artifact-id` | string | アーティファクトID(pomの `artifactId` に相当) | プロジェクト名と同値 |
@@ -102,6 +115,10 @@ accel attach --non-interactive
102
115
  | `--overwrite` | boolean | 既存ファイルの上書き確認を行わず、全て上書きする | `false`(対話で確認 / 非対話ではスキップ) |
103
116
  | `--non-interactive` | boolean | 非対話モード | `false` |
104
117
  | `--skip-install` | boolean | 依存インストールをスキップ | `false` |
118
+ | `--asset-server-url` | string | 資材を取得するGitHubリポジトリのURL(デフォルトProviderの取得先を上書き) | `https://github.com/accelplatform/skills` |
119
+ | `--asset-ref` | string | GitHubから取得するブランチ/タグ/コミット | `experiment` |
120
+ | `--asset-source` | string | (デバッグ用)ローカル資材のパス(tarアーカイブまたは展開済みディレクトリ)。明示時はGitHubより優先 | - |
121
+ | `--debug-server-url` | string | (デバッグ用)ローカル資材サーバーのURL(`GET /archive`)。明示時はGitHubより優先 | - |
105
122
 
106
123
  ### `accel detach`
107
124
 
@@ -116,6 +133,110 @@ accel detach
116
133
  - ユーザーが編集したファイルはスキップ(警告表示)
117
134
  - `.accel/` ディレクトリを削除
118
135
 
136
+ ### `accel login`
137
+
138
+ iAP の接続情報(エンドポイントURL・APIキー)を入力し、`.accel/credentials.json` に保存します。`accel deploy` を実行する前に、コマンドラインから直接接続情報を設定しておけます。
139
+
140
+ ```bash
141
+ cd my-project
142
+ accel login
143
+ ```
144
+
145
+ - `endpoint`(テキスト入力)と `apiKey`(マスク入力)を対話で入力します。
146
+ - 入力したトークンの有効性は、トークン検証API(`{endpoint}/oauth/token/verify`)を呼び出して確認します。検証に失敗(401 など)した場合や接続エラーの場合は、その場で再入力を促します。
147
+ - 検証に成功した時点で `.accel/credentials.json` に保存します。**APIキーはシークレットのため、ファイルは自動的に `.gitignore` に追加されます**(コミット対象外)。
148
+
149
+ #### 非対話モード
150
+
151
+ CIでの利用を想定し、プロンプトを出さずに接続情報を検証・保存するモードです。
152
+
153
+ ```bash
154
+ ACCEL_API_KEY=*** accel login \
155
+ --non-interactive \
156
+ --endpoint https://example.com/imart
157
+ ```
158
+
159
+ - `endpoint` / `apiKey` は **フラグ > 環境変数 > `.accel/credentials.json`** の優先順位で解決します。
160
+ - `endpoint`: `--endpoint` / `ACCEL_ENDPOINT`
161
+ - `apiKey`: `--api-key` / `ACCEL_API_KEY`
162
+ - **APIキーはシークレット情報のため、CI では `ACCEL_API_KEY` 環境変数での指定を推奨します**(`--api-key` はプロセス一覧やCIログに漏れ得ます)。
163
+ - トークン検証は1回だけ実行します。**失敗時は再入力せず、エラーメッセージを表示して非0終了します**。
164
+ - 検証成功時に `.accel/credentials.json` へ保存します(対話モードと同様、`.gitignore` に自動追記)。
165
+ - `endpoint` / `apiKey` のいずれかが解決できない場合はエラー終了します。
166
+
167
+ #### オプション一覧
168
+
169
+ | オプション | 型 | 説明 | デフォルト |
170
+ |---|---|---|---|
171
+ | `--endpoint` | string | iAPのベースURL(環境変数 `ACCEL_ENDPOINT` でも指定可) | 対話で入力 / 保存値 |
172
+ | `--api-key` | string | OAuth Bearerトークン(**環境変数 `ACCEL_API_KEY` 推奨**) | 対話で入力 / 保存値 |
173
+ | `--non-interactive` | boolean | 非対話モード(プロンプトを出さず、検証失敗時は非0終了) | `false` |
174
+
175
+ ### `accel deploy`
176
+
177
+ ビルド成果物(`./target/` 配下の zip)を iAP のステージング環境へデプロイします。対話モードに加え、CIでの利用を想定した非対話モード(`--non-interactive`)に対応します。
178
+
179
+ ```bash
180
+ cd my-project
181
+ accel deploy
182
+ ```
183
+
184
+ - プロジェクトのルートで実行します。ビルド成果物は `./target/` 配下に出力されている前提です(`mvn package` で生成される `<artifactId>-<version>.zip`)。
185
+ - 初回実行時に iAP の接続情報(エンドポイントURL・APIキー)の入力を求めます。入力値は `.accel/credentials.json` に保存され、保存値で接続できる限り次回以降は再入力不要です。事前に `accel login`(前述)で設定しておくこともできます。
186
+ - **APIキーはシークレット情報のため、`.accel/credentials.json` は自動的に `.gitignore` に追加されます**(コミット対象外)。
187
+ - デプロイ実行前に、トークン検証API(`/oauth/token/verify`)で接続情報の有効性を確認します。保存済みのエンドポイント/APIキーが誤っていて**接続エラーや認証エラー(401 など)になった場合は、その場で再入力を促し、接続できるまでリトライします**。中断したい場合は ESC / Ctrl+C で抜けられます。
188
+ - 既存のステージング環境を一覧から選択します(ステージングは iAP 側で事前に作成しておく必要があります)。
189
+ - デプロイ対象の zip は、`.accel/settings.json` の `artifactId` と `projectVersion` から `<artifactId>-<projectVersion>.zip` を `./target/` 配下で完全一致で特定し、見つかれば自動選択します。
190
+ - 該当ファイルが見つからない場合、または `.accel/settings.json` が無い場合は、従来通り `./target/` 配下の zip を一覧から選択します(単一なら自動選択)。
191
+ - この選択式の一覧では、`<artifactId>-main-storage.zip` / `<artifactId>-main-public.zip` / `<artifactId>-sample-jssp-internal.zip` のような**バージョンを含まない途中成果物**(`<artifactId>-` の直後が数字で始まらない zip)を除外します。
192
+ - ただし、除外した結果 0 件になった場合(`.accel/settings.json` の `artifactId` と実ファイル名の artifactId が乖離しているケースなど)は、除外せず `./target/` 配下の全 zip を選択候補とします。
193
+ - いずれの場合もデプロイ実行前に最終確認を行います。
194
+ - 送信時、zip は imm 形式としてアップロードされます(送信時のファイル名のみ `.imm` に変更され、ディスク上のファイルはそのままです)。
195
+
196
+ #### 非対話モード
197
+
198
+ CIでの利用を想定し、プロンプトを一切出さずにデプロイを実行するモードです。
199
+
200
+ ```bash
201
+ ACCEL_API_KEY=*** accel deploy \
202
+ --non-interactive \
203
+ --endpoint https://example.com/imart \
204
+ --staging-id my-staging \
205
+ --file ./target/my-app-1.0.0.zip \
206
+ --description "CI deploy"
207
+ ```
208
+
209
+ - **接続情報**は login と同じく **フラグ > 環境変数(`ACCEL_ENDPOINT` / `ACCEL_API_KEY`)> `.accel/credentials.json`** の優先順位で解決します(APIキーは `ACCEL_API_KEY` 環境変数での指定を推奨)。
210
+ - デプロイ前にトークン検証を1回だけ実行します。**失敗時は再入力せず、エラーメッセージを表示して非0終了します**。
211
+ - `--staging-id` は**必須**です(一覧選択は行いません。未指定はエラー終了。存在しないステージングはデプロイ時の 404 として表面化します)。
212
+ - デプロイ対象 zip:
213
+ - `--file` を指定した場合はそのパス(`./target/` 配下に限定しません)を使います。ファイルが存在しなければエラー終了します。
214
+ - `--file` 未指定の場合は対話モードと同じ自動特定(`<artifactId>-<projectVersion>.zip` 完全一致)を試みます。**自動特定できない場合**(settings 無し/完全一致無し/複数候補)は `--file` 指定が必要な旨を表示してエラー終了します。`./target/` に zip が 1 件も無い場合は、ビルド未実行の旨(`mvn package` 等の実行を促す)を表示してエラー終了します。
215
+ - `--description` は任意です。最終確認プロンプトは出さず、即デプロイします。
216
+ - フラグ/環境変数で渡した接続情報は **`.accel/credentials.json` に保存しません**(CIランナーにシークレット情報を残さないため)。
217
+
218
+ #### オプション一覧
219
+
220
+ | オプション | 型 | 説明 | デフォルト |
221
+ |---|---|---|---|
222
+ | `--endpoint` | string | iAPのベースURL(環境変数 `ACCEL_ENDPOINT` でも指定可) | 保存値 / 対話で入力 |
223
+ | `--api-key` | string | OAuth Bearerトークン(**環境変数 `ACCEL_API_KEY` 推奨**) | 保存値 / 対話で入力 |
224
+ | `--staging-id` | string | デプロイ先ステージングID(**非対話モードでは必須**) | 対話で一覧選択 |
225
+ | `--file` | string | デプロイする zip のパス(`./target/` 配下に限定しない) | 自動特定 / 対話で選択 |
226
+ | `--description` | string | デプロイメントの説明 | `""` |
227
+ | `--non-interactive` | boolean | 非対話モード(プロンプトを出さず、失敗時は非0終了) | `false` |
228
+
229
+ #### 接続情報ファイル `.accel/credentials.json`
230
+
231
+ ```json
232
+ {
233
+ "endpoint": "https://example.com/imart",
234
+ "apiKey": "<OAuth Bearer トークン>"
235
+ }
236
+ ```
237
+
238
+ `endpoint` には iAP のベースURL を指定します。API パス(`/api/bearer/development/...`)は CLI が組み立てます。
239
+
119
240
  ## IM-Juggling連携
120
241
 
121
242
  `--juggling-project` オプションでIM-Jugglingプロジェクトのパスを指定すると、`juggling.im` からiAPバージョンと使用モジュールを自動検出します。
@@ -124,6 +245,12 @@ accel detach
124
245
  accel init my-project --juggling-project /path/to/juggling-project
125
246
  ```
126
247
 
248
+ - iAPバージョンは `juggling.im` のベースバージョンを `@intra-mart/catalog` のリリースと突合して解決します
249
+ - モジュールは選択済みモジュールのIDをカタログのモジュールID(`jp.co.intra_mart.im_workflow` 等)と突合し、一致したものを `artifactId`(`im_workflow` 等)として採用します
250
+ - モジュール自体のバージョンは突合しません(パッチ適用により `8.0.39-PATCH_001` のようにずれることがあるため)
251
+ - カタログに存在しないモジュールは無視されます
252
+ - 機能(`--feature`)は `juggling.im` から検出できないため自動設定されません
253
+
127
254
  ## プロジェクト構造
128
255
 
129
256
  `accel init` 実行後に生成されるディレクトリ構造:
@@ -144,8 +271,25 @@ my-project/
144
271
 
145
272
  - `.accel/settings.json` — プロジェクト設定。git管理対象、手動編集可
146
273
  - `.accel/hashsum.txt` — 配備ファイルのSHA-1。`detach` 時の変更検出に使用
274
+ - `.accel/credentials.json` — iAP接続情報(endpoint / APIキー)。`accel deploy` 初回実行時に生成。APIキーを含むため `.gitignore` 対象(git管理対象外)
147
275
 
148
276
  ## 対応iAPバージョン
149
277
 
150
- 現段階では、2025-Autumn (8.0.38) のみをサポートしています。
151
- iAPのバージョン検出処理については、2025-Autumn固定になるように一部処理を変更しています。
278
+ 選択可能なiAPバージョン・モジュール・機能は、同梱のマスタデータパッケージ `@intra-mart/catalog` のカタログに基づきます(2025 Spring / 8.0.37 以降)。バージョンは `2026-spring` のようなリリースID形式で指定します。新しいリリースへの対応はカタログの更新によって行われます。
279
+
280
+ ## 入力値のバリデーション
281
+
282
+ CLIに渡された値および対話で入力された値は、以下のルールでバリデーションされます。違反する値は対話モードでは再入力を促し、非対話モードではエラーメッセージを表示して終了します。
283
+
284
+ | オプション | ルール |
285
+ |-----------|------|
286
+ | `[project-name]` / `--name` | 空文字不可。ディレクトリ名として不正な文字を含まない(`/ \ : * ? " < > \|`、制御文字、先頭末尾の空白/ドット) |
287
+ | `--artifact-id` | Maven artifactId 規約: `^[A-Za-z0-9][A-Za-z0-9._-]*$` |
288
+ | `--group` | Maven groupId 規約: ドット区切りの識別子 `^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$` |
289
+ | `--project-version` | Maven version 規約: `^[A-Za-z0-9][A-Za-z0-9._-]*$` |
290
+ | `--accelplatform-version` | カタログのリリースID(`2025-spring` 以降)に含まれる値 |
291
+ | `--module` | 選択したiAPリリースのカタログに含まれるモジュールの `artifactId`(例: `im_workflow`, `im_activiti`)のいずれか(各要素) |
292
+ | `--feature` | 選択したiAPリリースのカタログに含まれる機能ID(例: `jssp`, `imds`, `i18n`, `e2e`)のいずれか(各要素) |
293
+ | `--database` | `postgresql`, `oracle`, `sqlserver` のいずれか |
294
+ | `--agent` | `claude-code`, `github-copilot` のいずれか(各要素) |
295
+ | `--package-manager` | `bun`, `npm`, `yarn`, `pnpm` のいずれか |
@@ -1,9 +1,10 @@
1
1
  import { readFile, writeFile, mkdir, stat } from "node:fs/promises";
2
2
  import { join, dirname, resolve, sep } from "node:path";
3
- import { labelToSemver } from "../core/version-map.js";
3
+ import { buildEvalContext } from "../core/eval-context.js";
4
4
  import { walkAssetRepo } from "./walker.js";
5
5
  import { processMarkdown } from "../markdown/processor.js";
6
6
  import { applyRegexReplacements } from "./regex-replacement.js";
7
+ import { applyForeachReplacements } from "./foreach-replacement.js";
7
8
  import { computeSha1 } from "../utils/hash.js";
8
9
  import { writeSettings, writeHashsum } from "../utils/settings-io.js";
9
10
  import { execSync } from "node:child_process";
@@ -57,22 +58,8 @@ export const deployAssets = async (options) => {
57
58
  try {
58
59
  progress?.onFetchStart?.();
59
60
  const repoDir = await provider.fetch();
60
- const semver = labelToSemver(settings.accelplatformVersion);
61
- const context = {
62
- version: semver,
63
- modules: settings.modules,
64
- locale: settings.locale,
65
- agents: settings.agents,
66
- name: settings.name,
67
- artifactId: settings.artifactId,
68
- group: settings.group,
69
- description: settings.description,
70
- accelplatformVersion: settings.accelplatformVersion,
71
- database: settings.database,
72
- projectVersion: settings.projectVersion,
73
- packageManager: settings.packageManager,
74
- javascript: settings.javascript,
75
- };
61
+ const semver = settings.accelplatformVersion.semver;
62
+ const context = buildEvalContext(settings);
76
63
  const entries = await walkAssetRepo(repoDir, semver, context);
77
64
  const deployedFiles = [];
78
65
  const hashEntries = [];
@@ -119,6 +106,10 @@ export const deployAssets = async (options) => {
119
106
  let content = await readFile(entry.sourcePath, "utf-8");
120
107
  const replacements = collectReplacements(entry);
121
108
  if (replacements.length > 0) {
109
+ const foreachReplacements = replacements.filter((r) => r.type === "foreach");
110
+ if (foreachReplacements.length > 0) {
111
+ content = applyForeachReplacements(content, foreachReplacements, context);
112
+ }
122
113
  if (targetPath.endsWith(".md")) {
123
114
  content = processMarkdown(content, replacements, context);
124
115
  }
@@ -0,0 +1,2 @@
1
+ import type { EvalContext, ForeachReplacement } from "../core/types.js";
2
+ export declare const applyForeachReplacements: (content: string, replacements: ForeachReplacement[], context: EvalContext) => string;
@@ -0,0 +1,20 @@
1
+ import { evaluateCondition } from "../core/condition-evaluator.js";
2
+ import { interpolateVariables } from "../core/variable-interpolator.js";
3
+ const DEFAULT_SEPARATOR = "\n";
4
+ const itemVariables = (item) => new Map(Object.entries(item).map(([key, value]) => [`item.${key}`, value]));
5
+ export const applyForeachReplacements = (content, replacements, context) => {
6
+ let result = content;
7
+ for (const replacement of replacements) {
8
+ if (!evaluateCondition(replacement.condition, context)) {
9
+ continue;
10
+ }
11
+ const items = replacement.items === "modules"
12
+ ? context.moduleItems
13
+ : context.featureItems;
14
+ const expanded = items
15
+ .map((item) => interpolateVariables(replacement.template, context, itemVariables(item)))
16
+ .join(replacement.separator ?? DEFAULT_SEPARATOR);
17
+ result = result.split(replacement.target).join(expanded);
18
+ }
19
+ return result;
20
+ };
@@ -0,0 +1,2 @@
1
+ import type { AssetProvider } from "../core/types.js";
2
+ export declare const createGitHubAssetProvider: (repoUrl: string, ref: string) => AssetProvider;
@@ -0,0 +1,37 @@
1
+ import { join } from "node:path";
2
+ import { mkdir, rm, readdir } from "node:fs/promises";
3
+ import { extractTarGz, createTempDir } from "../utils/archive.js";
4
+ import { downloadFile } from "../utils/https.js";
5
+ export const createGitHubAssetProvider = (repoUrl, ref) => {
6
+ let tmpDir = null;
7
+ return {
8
+ fetch: async () => {
9
+ tmpDir = await createTempDir("accel-assets-");
10
+ const archivePath = join(tmpDir, "assets.tar.gz");
11
+ const extractDir = join(tmpDir, "repo");
12
+ await mkdir(extractDir, { recursive: true });
13
+ const base = repoUrl.replace(/\/+$/, "");
14
+ const archiveUrl = `${base}/archive/${ref}.tar.gz`;
15
+ try {
16
+ await downloadFile(archiveUrl, archivePath);
17
+ }
18
+ catch (err) {
19
+ const reason = err instanceof Error ? err.message : String(err);
20
+ throw new Error(`Failed to fetch the asset repository from ${archiveUrl}. Check the URL and ref: ${reason}`);
21
+ }
22
+ await extractTarGz(archivePath, extractDir);
23
+ const entries = await readdir(extractDir, { withFileTypes: true });
24
+ const dirs = entries.filter((e) => e.isDirectory());
25
+ if (dirs.length !== 1) {
26
+ throw new Error(`Unexpected tarball structure (top-level directory count: ${dirs.length}).`);
27
+ }
28
+ return join(extractDir, dirs[0].name);
29
+ },
30
+ cleanup: async () => {
31
+ if (tmpDir) {
32
+ await rm(tmpDir, { recursive: true, force: true }).catch(() => { });
33
+ tmpDir = null;
34
+ }
35
+ },
36
+ };
37
+ };
@@ -1,28 +1,7 @@
1
- import { get } from "node:http";
2
- import { createWriteStream } from "node:fs";
3
1
  import { join } from "node:path";
4
2
  import { mkdir, rm } from "node:fs/promises";
5
3
  import { extractTarGz, createTempDir } from "../utils/archive.js";
6
- const downloadFile = (url, destPath) => {
7
- return new Promise((resolve, reject) => {
8
- const file = createWriteStream(destPath);
9
- get(url, (response) => {
10
- if (response.statusCode !== 200) {
11
- file.close();
12
- reject(new Error(`HTTP ${response.statusCode} when downloading from ${url}`));
13
- return;
14
- }
15
- response.pipe(file);
16
- file.on("finish", () => {
17
- file.close();
18
- resolve();
19
- });
20
- }).on("error", (err) => {
21
- file.close();
22
- reject(err);
23
- });
24
- });
25
- };
4
+ import { downloadFile } from "../utils/https.js";
26
5
  export const createLocalAssetProvider = (serverUrl) => {
27
6
  let tmpDir = null;
28
7
  return {
@@ -1,6 +1,7 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import { join, relative, dirname, sep } from "node:path";
3
3
  import { evaluateCondition } from "../core/condition-evaluator.js";
4
+ import { emptyEvalContext } from "../core/eval-context.js";
4
5
  const toPosixPath = (p) => sep === "/" ? p : p.split(sep).join("/");
5
6
  const META_FILE = "_meta.json";
6
7
  const readMetaJson = async (dir) => {
@@ -41,21 +42,9 @@ const findMatchingVersionDirs = async (repoDir, targetSemver) => {
41
42
  continue;
42
43
  }
43
44
  if (meta.conditions) {
44
- const context = {
45
- version: targetSemver,
46
- modules: [],
47
- locale: "",
48
- agents: [],
49
- name: "",
50
- artifactId: "",
51
- group: "",
52
- description: "",
53
- accelplatformVersion: "",
54
- database: "",
55
- projectVersion: "",
56
- packageManager: "",
57
- javascript: false,
58
- };
45
+ const context = emptyEvalContext({
46
+ accelplatformVersion: { label: "", semver: targetSemver },
47
+ });
59
48
  if (evaluateCondition(meta.conditions, context)) {
60
49
  matched.push(dirPath);
61
50
  }
@@ -11,6 +11,10 @@ export declare const attachCommand: import("citty").CommandDef<{
11
11
  type: "string";
12
12
  description: string;
13
13
  };
14
+ feature: {
15
+ type: "string";
16
+ description: string;
17
+ };
14
18
  name: {
15
19
  type: "string";
16
20
  description: string;
@@ -66,11 +70,19 @@ export declare const attachCommand: import("citty").CommandDef<{
66
70
  description: string;
67
71
  default: false;
68
72
  };
73
+ "asset-server-url": {
74
+ type: "string";
75
+ description: string;
76
+ };
77
+ "asset-ref": {
78
+ type: "string";
79
+ description: string;
80
+ };
69
81
  "asset-source": {
70
82
  type: "string";
71
83
  description: string;
72
84
  };
73
- "asset-server-url": {
85
+ "debug-server-url": {
74
86
  type: "string";
75
87
  description: string;
76
88
  };
@@ -2,12 +2,12 @@ import { defineCommand } from "citty";
2
2
  import * as p from "@clack/prompts";
3
3
  import { stat } from "node:fs/promises";
4
4
  import { basename, join } from "node:path";
5
- import { CLI_VERSION } from "../core/constants.js";
5
+ import { CLI_VERSION, DEFAULT_ASSET_REPO_URL, DEFAULT_ASSET_REF, } from "../core/constants.js";
6
6
  import { runPrompts } from "../interactive/prompts.js";
7
7
  import { deployAssets, } from "../asset/deployer.js";
8
8
  import { createLocalAssetProvider } from "../asset/local-provider.js";
9
9
  import { createFileAssetProvider } from "../asset/file-provider.js";
10
- import { defaultAssetSourcePath } from "../asset/default-source.js";
10
+ import { createGitHubAssetProvider } from "../asset/github-provider.js";
11
11
  import { getMessage } from "../i18n/index.js";
12
12
  import { detectLocale } from "../utils/locale-detect.js";
13
13
  import { printSummary } from "../interactive/summary.js";
@@ -25,11 +25,15 @@ export const attachCommand = defineCommand({
25
25
  },
26
26
  "accelplatform-version": {
27
27
  type: "string",
28
- description: "iAP version (e.g., 2026-Spring)",
28
+ description: "iAP version (e.g., 2026-spring)",
29
29
  },
30
30
  module: {
31
31
  type: "string",
32
- description: "Modules to use (comma-separated)",
32
+ description: "Modules to use (comma-separated artifactIds, e.g., im_workflow)",
33
+ },
34
+ feature: {
35
+ type: "string",
36
+ description: "Features to use (comma-separated function ids, e.g., jssp)",
33
37
  },
34
38
  name: {
35
39
  type: "string",
@@ -86,13 +90,21 @@ export const attachCommand = defineCommand({
86
90
  description: "Non-interactive mode",
87
91
  default: false,
88
92
  },
93
+ "asset-server-url": {
94
+ type: "string",
95
+ description: "GitHub repository URL to fetch assets from (overrides the default repository)",
96
+ },
97
+ "asset-ref": {
98
+ type: "string",
99
+ description: "Git ref (branch/tag/commit) to fetch from the GitHub asset repository. Default: master",
100
+ },
89
101
  "asset-source": {
90
102
  type: "string",
91
- description: "Local asset source path (tar archive or extracted directory). Defaults to the bundled assets/assets.tar.gz.",
103
+ description: "(debug) Local asset source path (tar archive or extracted directory)",
92
104
  },
93
- "asset-server-url": {
105
+ "debug-server-url": {
94
106
  type: "string",
95
- description: "Asset server URL (takes precedence over --asset-source when explicitly set)",
107
+ description: "(debug) Local asset server URL serving GET /archive",
96
108
  },
97
109
  },
98
110
  run: async ({ args }) => {
@@ -109,6 +121,10 @@ export const attachCommand = defineCommand({
109
121
  const modules = moduleArg
110
122
  ? moduleArg.split(",").map((m) => m.trim())
111
123
  : undefined;
124
+ const featureArg = args.feature;
125
+ const features = featureArg
126
+ ? featureArg.split(",").map((f) => f.trim())
127
+ : undefined;
112
128
  const agentArg = args.agent;
113
129
  const promptOpts = {
114
130
  name: args.name ?? basename(projectDir),
@@ -116,6 +132,7 @@ export const attachCommand = defineCommand({
116
132
  jugglingProject: args["juggling-project"],
117
133
  accelplatformVersion: args["accelplatform-version"],
118
134
  module: modules,
135
+ feature: features,
119
136
  group: args.group,
120
137
  projectVersion: args["project-version"],
121
138
  description: args.description,
@@ -147,6 +164,7 @@ export const attachCommand = defineCommand({
147
164
  description: resolved.description,
148
165
  accelplatformVersion: resolved.accelplatformVersion,
149
166
  modules: resolved.modules,
167
+ features: resolved.features,
150
168
  database: resolved.database,
151
169
  agents: resolved.agents,
152
170
  javascript: resolved.javascript,
@@ -155,11 +173,14 @@ export const attachCommand = defineCommand({
155
173
  jugglingProject: resolved.jugglingProject,
156
174
  deployedAssets: {},
157
175
  };
158
- const serverUrl = args["asset-server-url"];
159
- const assetSource = args["asset-source"] ?? defaultAssetSourcePath();
160
- const provider = serverUrl
161
- ? createLocalAssetProvider(serverUrl)
162
- : createFileAssetProvider(assetSource);
176
+ const assetSource = args["asset-source"];
177
+ const debugServerUrl = args["debug-server-url"];
178
+ const provider = assetSource
179
+ ? createFileAssetProvider(assetSource)
180
+ : debugServerUrl
181
+ ? createLocalAssetProvider(debugServerUrl)
182
+ : createGitHubAssetProvider(args["asset-server-url"] ??
183
+ DEFAULT_ASSET_REPO_URL, args["asset-ref"] ?? DEFAULT_ASSET_REF);
163
184
  const isInteractive = !args["non-interactive"];
164
185
  const overwriteFlag = args.overwrite;
165
186
  if (isInteractive) {
@@ -0,0 +1,62 @@
1
+ import type { AccelCredentials, StagingResponse } from "../core/types.js";
2
+ import { type ApiClient } from "../deploy/api-client.js";
3
+ export declare const toImmFileName: (zipPath: string) => string;
4
+ type ConfirmInfo = {
5
+ endpoint: string;
6
+ stagingId: string;
7
+ sourceFile: string;
8
+ sentAs: string;
9
+ };
10
+ export type DeployPrompts = {
11
+ selectStaging: (stagings: StagingResponse[], locale: string) => Promise<string>;
12
+ selectZip: (zips: string[], locale: string) => Promise<string>;
13
+ description: (locale: string) => Promise<string>;
14
+ confirm: (info: ConfirmInfo, locale: string) => Promise<boolean>;
15
+ };
16
+ export type RunDeployDeps = {
17
+ projectDir: string;
18
+ locale?: string;
19
+ nonInteractive?: boolean;
20
+ endpoint?: string;
21
+ apiKey?: string;
22
+ stagingId?: string;
23
+ file?: string;
24
+ description?: string;
25
+ env?: Record<string, string | undefined>;
26
+ ensureCredentials?: (projectDir: string, locale: string, seed?: Partial<AccelCredentials>) => Promise<AccelCredentials>;
27
+ repromptCredentials?: (current: AccelCredentials, locale: string) => Promise<AccelCredentials>;
28
+ resolveCredentialsOrThrow?: (projectDir: string, locale: string, seed: Partial<AccelCredentials>) => Promise<AccelCredentials>;
29
+ persistCredentials?: (projectDir: string, creds: AccelCredentials) => Promise<void>;
30
+ scanTargets?: (projectDir: string) => Promise<string[]>;
31
+ apiClientFactory?: (endpoint: string, apiKey: string) => ApiClient;
32
+ prompts?: DeployPrompts;
33
+ };
34
+ export declare const runDeploy: (deps: RunDeployDeps) => Promise<void>;
35
+ export declare const deployCommand: import("citty").CommandDef<{
36
+ endpoint: {
37
+ type: "string";
38
+ description: string;
39
+ };
40
+ "api-key": {
41
+ type: "string";
42
+ description: string;
43
+ };
44
+ "staging-id": {
45
+ type: "string";
46
+ description: string;
47
+ };
48
+ file: {
49
+ type: "string";
50
+ description: string;
51
+ };
52
+ description: {
53
+ type: "string";
54
+ description: string;
55
+ };
56
+ "non-interactive": {
57
+ type: "boolean";
58
+ description: string;
59
+ default: false;
60
+ };
61
+ }>;
62
+ export {};