@initx-plugin/env 0.1.2 → 0.1.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ## @initx-plugin/env
2
2
 
3
- `initx` plugin to manage env vars and secrets from one private GitHub repo.
3
+ `initx` plugin to manage env vars and secrets from a private GitHub or GitLab repository.
4
4
 
5
5
  One private repository holds **one JSON file per store** (`stores/<name>.json`). Pull a store into a
6
6
  local `.env`, inject it into a child process or CI job, or write it into GitHub Actions secrets.
@@ -9,15 +9,10 @@ local `.env`, inject it into a child process or CI job, or write it into GitHub
9
9
  initx plugin add env
10
10
  ```
11
11
 
12
- ## Why another secrets tool
13
-
14
- Almost every existing tool (dotenvx, sops, 1Password, Doppler, Infisical, Vault) **injects** secrets
15
- into a process. None of them writes values back into GitHub's secret storage — that is the gap this
16
- plugin fills. It also keeps one honest boundary:
17
-
18
- > **CI → local sync is impossible.** GitHub Actions secrets are write-only: the API returns
19
- > `name` / `created_at` / `updated_at` and never the value. So the vault repo is always the source of
20
- > truth and CI is always a one-way projection.
12
+ The vault is the source of truth. Storage and execution platforms are independent: GitLab CI can
13
+ read a GitHub vault, and a GitLab vault can supply GitHub Actions secrets via `apply`. GitHub
14
+ Actions secrets are write-only; this plugin projects values from the vault into that target.
15
+ GitLab CI/CD Variables synchronization is a separate future target, not part of this release.
21
16
 
22
17
  ## Setup
23
18
 
@@ -35,6 +30,70 @@ The config lives in `~/.initx/stores/@initx-plugin/env/store.json`. The token is
35
30
  plaintext** there (same trade-off as `@initx-plugin/cp`) — set `INITX_ENV_TOKEN` instead if you would
36
31
  rather keep it out of a file.
37
32
 
33
+ ## Project configuration (GitHub or GitLab)
34
+
35
+ Commit a non-sensitive `.initx-env.json` in the business project's working directory:
36
+
37
+ ```json
38
+ {
39
+ "schema": 1,
40
+ "storage": {
41
+ "type": "gitlab",
42
+ "apiBaseUrl": "https://gitlab.example.com/api/v4",
43
+ "repo": "example-group/subgroup/env-vault",
44
+ "branch": "main",
45
+ "path": "stores"
46
+ }
47
+ }
48
+ ```
49
+
50
+ `type` and `repo` are required. GitLab accepts a numeric project ID (a string such as `"12345"`)
51
+ or the full namespace, including multiple subgroups. `apiBaseUrl` means the API base, including
52
+ `/api/v4` for GitLab or `/api/v3` for GitHub Enterprise. Defaults are
53
+ `https://gitlab.com/api/v4` and `https://api.github.com`. Branch defaults to `main`, directory to
54
+ `stores`; `"path": ""` explicitly selects repository root. For a GitHub vault, use `"type": "github"`
55
+ and `"repo": "owner/vault"`. Examples are in [examples](examples/).
56
+
57
+ The vault project/repository must already exist, but it can be empty. The first `env push <store>`
58
+ or `env set <store> K=V` creates the configured branch (default `main`) and the store file in one
59
+ commit, on both GitHub and GitLab. No README or manual branch creation is required. `list` and
60
+ `env-config status` report zero stores for a confirmed empty vault without writing anything.
61
+ An initialized vault with a missing configured branch still fails. The token must have permission
62
+ to create the first commit; GitLab instance rules for the initial/default branch may require a
63
+ Maintainer even when a Developer can write to other branches.
64
+
65
+ All storage commands and `env-config status` accept `--config <file>`. Relative paths resolve
66
+ against the caller's cwd. Without the flag, only cwd's `.initx-env.json` is checked; parents are
67
+ never searched. An explicitly missing file, invalid JSON, unknown schema or unknown field fails
68
+ instead of falling back. Project files cannot contain tokens, credentials or `vars`; business
69
+ values belong to the remote store (whose schema remains 1). Config files are only read.
70
+
71
+ ```bash
72
+ initx env-config status --config .initx-env.json --offline
73
+ initx env run wukong-content-production --config .initx-env.json -- npm run pipeline
74
+ # When running from an isolated HOME/cwd, pass the business config's absolute path.
75
+ initx env run demo --config /workspace/business/.initx-env.json -- node /workspace/business/task.mjs
76
+ ```
77
+
78
+ The config's location never changes the child cwd. `npm run pipeline` above is an example business
79
+ entry point, implemented by the consuming project. Set only `INITX_ENV_TOKEN` in CI; tokens and
80
+ business values do not belong in this file. Existing Slides invocations using `INITX_ENV_REPO`,
81
+ `INITX_ENV_BRANCH`, `INITX_ENV_PATH` and a GitHub token continue to work without new flags.
82
+
83
+ For local persisted GitLab settings (independent of a project file):
84
+
85
+ ```bash
86
+ initx env-config set type gitlab
87
+ initx env-config set apiBaseUrl https://gitlab.example.com/api/v4
88
+ initx env-config set repo example-group/subgroup/env-vault
89
+ # Prefer INITX_ENV_TOKEN; if saving a token locally, select type and API address first.
90
+ initx env-config set token <gitlab-access-token>
91
+ ```
92
+
93
+ Changing local type/API clears the old repo/branch/path to prevent accidental reuse. New persisted
94
+ tokens are keyed by provider and normalized full API base (including its path), not just hostname.
95
+ Legacy flat config remains GitHub config; its token is accepted only on the default GitHub API.
96
+
38
97
  ## Commands
39
98
 
40
99
  | Command | What it does |
@@ -47,6 +106,14 @@ rather keep it out of a file.
47
106
  | `initx env run <store> -- <command> [...args]` | Inject a store directly into one child process and return its exit status |
48
107
  | `initx env apply <store> --repo o/r` | Write a store into a repo's GitHub Actions secrets |
49
108
 
109
+ Command failures print only the error message to stderr and exit with code 1, without a stack
110
+ trace or minified source dump. A failed connectivity check in `env-config status` also exits
111
+ with code 1. `env run` continues to forward the child's own exit status, signals and output.
112
+ Terminal messages use the existing utils color builders: errors are red, successful changes
113
+ green, warnings/modified keys yellow, and configuration values/store names blue. Message-body
114
+ colors are disabled when redirected or when `NO_COLOR`/`FORCE_COLOR=0` is set; `FORCE_COLOR=1`
115
+ enables them explicitly. JSON, exported variable values and child output are not colorized.
116
+
50
117
  ```bash
51
118
  # write / update a store
52
119
  initx env set mail-peon-publish EXTENSION_ID=abc CHROME_REFRESH_TOKEN=1//0g...
@@ -72,7 +139,7 @@ initx env run acewill-slidev-production -- pnpm deploy
72
139
  initx env run demo -- bash scripts/deploy.sh
73
140
  ```
74
141
 
75
- The store and the first `--` are required. Put the plugin's optional `--token <token>` before `--`;
142
+ The store and the first `--` are required. Put optional `--token <token>` and `--config <file>` before `--`;
76
143
  every argument after it belongs to the child, including `--help`, `--version`, `--token`, another
77
144
  `--`, empty strings and arguments with spaces. Extra positional arguments and other plugin flags
78
145
  (including `--repo`, `--json`, `--stdout`, `--only`, `--shell`, `--cwd`) are rejected before reading
@@ -119,16 +186,25 @@ The stdout form of `setup` remains intended for inspection/legacy use; use `run`
119
186
 
120
187
  | Setting | Highest to lowest priority |
121
188
  | --- | --- |
122
- | Token | Explicit override / `--token` → `INITX_ENV_TOKEN` → `GH_TOKEN` → `GITHUB_TOKEN` → persisted token |
123
- | Vault repo | Explicit override → `INITX_ENV_REPO` → persisted `repo` |
124
- | Branch | Explicit override → `INITX_ENV_BRANCH` → persisted `branch` → `main` |
125
- | Store path | Explicit override → `INITX_ENV_PATH` → persisted `path` → `stores` |
189
+ | Provider | Explicit override → `INITX_ENV_TYPE` → project `storage.type` → local storage type → `github` |
190
+ | API base | Explicit override → `INITX_ENV_API_BASE_URL` → project API → same-provider local API → provider default |
191
+ | Token | Explicit override / `--token` → `INITX_ENV_TOKEN` → `GH_TOKEN` → `GITHUB_TOKEN` (last two only for default GitHub API) → scoped local token |
192
+ | Vault repo | Explicit override → `INITX_ENV_REPO` → project `storage.repo` → same-provider/API local `repo` |
193
+ | Branch | Explicit override → `INITX_ENV_BRANCH` → project branch → same-provider/API local branch → `main` |
194
+ | Store path | Explicit override → `INITX_ENV_PATH` → project path → same-provider/API local path → `stores` |
126
195
 
127
196
  Empty strings fall back to the next source. Environment settings affect the current invocation
128
197
  only and are never written back to the plugin store. `env-config status --offline` shows the
129
198
  effective settings with a masked token; `get` / `list` show the persisted settings. `env apply
130
199
  --repo o/r` still selects the destination Actions repository, not the vault. `run` has no `--repo`.
131
200
 
201
+ The provider is resolved first; values from another provider/API are never inherited. `GH_TOKEN`
202
+ and `GITHUB_TOKEN` are never sent to GitLab or custom API bases. `--token` and `INITX_ENV_TOKEN`
203
+ explicitly authorize the selected current target. Authenticated requests do not follow redirects,
204
+ have a 30-second timeout, and are not automatically retried (a failed write may have committed).
205
+ Status shows per-field sources, provider, API base, repo/ref/path, config file and fully masked
206
+ token source. `get/list/set/rm` operate only on persisted local settings, never project files.
207
+
132
208
  ## Use in GitLab CI
133
209
 
134
210
  Preinstall fixed versions of initx and this plugin in the CI image, or prepare them with an explicit
@@ -137,28 +213,26 @@ implementation is smoke-tested with **initx 0.4.3**; compatible versions must ex
137
213
  tail as `ctx.cliOptions['--']: string[]`, keep child flags out of prefix help/version handling, and
138
214
  preserve `process.exitCode` after handlers finish. Older versions have not been verified.
139
215
 
140
- Set a protected, masked `INITX_ENV_TOKEN` in GitLab with **Contents: read** access to the vault only.
141
- Repository, branch and path can be ordinary YAML variables:
216
+ Commit `.initx-env.json` as above and set a protected, masked `INITX_ENV_TOKEN` in GitLab. For a
217
+ GitLab vault, use a Personal/Project Access Token with `read_api` and access to the vault project;
218
+ local writes require `api` plus branch permissions. Check both Files and Tree access on your
219
+ instance. For a GitHub vault, use **Contents: read** access. Runner platform does not select storage.
142
220
 
143
221
  ```yaml
144
- deploy:
145
- stage: deploy
146
- variables:
147
- INITX_ENV_REPO: example/private-env-vault
148
- INITX_ENV_BRANCH: main
149
- INITX_ENV_PATH: stores
222
+ pipeline:
150
223
  script:
151
- - initx env run acewill-slidev-production -- pnpm deploy
224
+ - initx env run wukong-content-production --config "$CI_PROJECT_DIR/.initx-env.json" -- npm run pipeline
152
225
  ```
153
226
 
154
- This illustrates the integration point, not a complete pipeline. Keep deployment variables out of
155
- the build job. Let `pnpm deploy` orchestrate flash-dns, uploads, release switching and verification;
156
- the plugin does not hard-code their variable names or perform deployment operations. Scripts may
227
+ This illustrates the integration point; prepare pinned initx/plugin versions noninteractively in
228
+ your CI image first. This feature is not published by this change: use a tested local build until
229
+ a separate release exists, then pin that released version. Let the business script orchestrate its
230
+ own workflow; the plugin does not hard-code business variable names or perform deployments. Scripts may
157
231
  materialize multiline SSH keys as needed, set file permissions and clean them up; `run` does not
158
232
  create key files or pass them through artifacts/cache.
159
233
 
160
234
  Fetched variables are **not automatically registered as GitLab masked variables**. Disable command
161
- tracing that could print them. Vault Contents permissions apply to the entire repository, not to a
235
+ tracing that could print them. Vault read permissions apply to the entire repository, not to a
162
236
  store filename: use separate repositories for separate trust boundaries. The private JSON store and
163
237
  its Git history remain plaintext.
164
238
 
@@ -182,6 +256,65 @@ repo needs its own token (one secret):
182
256
  `env setup` writes to `$GITHUB_ENV` and emits `::add-mask::` for every value before it does, so the
183
257
  values do not show up in logs. Values with newlines use the heredoc (`KEY<<EOF`) form.
184
258
 
259
+ ## Apply to GitHub Actions
260
+
261
+ `apply --repo owner/target` always selects the destination GitHub repository. Its vault is resolved
262
+ independently via `--config`/environment/local settings. For a GitLab or custom-host source, set
263
+ `INITX_ENV_TARGET_TOKEN` (recommended for CI) or `--target-token <github-token>` with Secrets write
264
+ access to the target. The source token never crosses providers/hosts. `GH_TOKEN`/`GITHUB_TOKEN`
265
+ are not implicit target credentials for these cross-platform calls. `--dry-run` also validates
266
+ that independent target credentials are configured, but makes no target API call.
267
+
268
+ ```bash
269
+ initx env apply demo --config .initx-env.json --repo owner/target --dry-run
270
+ # Same invocation without --dry-run writes GitHub Actions secrets.
271
+ # Supply INITX_ENV_TOKEN for the vault and INITX_ENV_TARGET_TOKEN for the target in the environment.
272
+ ```
273
+
274
+ Existing default GitHub API vault → default GitHub Actions calls may continue reusing the source
275
+ token. Explicit target credentials take precedence. `targets.secrets` retains its schema-1 shape.
276
+ GitLab CI Variables writing and CI_JOB_TOKEN support are deferred. Deploy Tokens used for git clone
277
+ are not Files/Tree API tokens.
278
+
279
+ ## Storage guarantees and GitLab compatibility
280
+
281
+ Commands use an explicit adapter factory; the store service deals only in UTF-8 content and opaque
282
+ `version` values. GitHub uses the file SHA; GitLab uses the file's `last_commit_id`, never Tree blob
283
+ IDs. Creates are explicit, updates/deletes carry the expected version. Conflict responses are
284
+ verified by a safe reread; GitHub 422 and GitLab 400 are not automatically conflicts. GitLab's
285
+ unchanged-content 400 is accepted only if the file still has the expected version and exact content.
286
+ `push --force` rereads and retries once; permission/validation/network errors do not trigger it.
287
+ The existing force semantics overwrite using the locally prepared store, including its routing
288
+ hints; concurrent remote-only changes can be replaced. Pull/review before force if those matter.
289
+
290
+ GitLab uses Files GET/POST/PUT, a conditional Commits delete action, and Tree offset pagination
291
+ (`per_page=100`). It supports full subgroups and numeric IDs, and does not require keyset pagination, newer Tree metadata or job-token
292
+ features. Missing paths are empty only after validating repository/ref access or confirming an
293
+ empty repository (GitLab project `empty_repo`, GitHub's explicit empty-repository response). Tree accepts both
294
+ legacy 200 empty arrays and newer 404 responses. Directory listing is not recursive. Errors expose
295
+ only a category, safe hint and HTTP status, never upstream response text or request URLs.
296
+
297
+ The implementation targets GitLab CE **15.9.4**, with mock coverage for its API behavior. This is
298
+ not a claim of real-instance acceptance. Use a temporary vault with non-sensitive dummy values to
299
+ verify read_api Files/Tree/commit access, pagination, encoded refs/paths, protected-branch errors,
300
+ create races and stale update/delete rejection on your actual instance. Record token type, scope,
301
+ role and instance version. See [implementation and acceptance record](docs/gitlab-storage-implementation.md).
302
+ API references: [Files](https://docs.gitlab.com/api/repository_files/),
303
+ [Tree](https://docs.gitlab.com/api/repositories/),
304
+ [Deploy Token limitations](https://docs.gitlab.com/user/project/deploy_tokens/).
305
+
306
+ One deliberate change from the initial plan: 15.9.4's Files DELETE does not declare
307
+ `last_commit_id`, so it discards that condition. Deletion therefore uses the existing Commits API
308
+ with one `delete` action containing `last_commit_id`; it does not rely on a client-side precheck.
309
+ The versioned [Files route](https://github.com/gitlabhq/gitlabhq/blob/v15.9.4/lib/api/files.rb),
310
+ [Commits route](https://github.com/gitlabhq/gitlabhq/blob/v15.9.4/lib/api/commits.rb) and
311
+ [MultiService check](https://github.com/gitlabhq/gitlabhq/blob/v15.9.4/app/services/files/multi_service.rb)
312
+ provide the compatibility basis; real-instance testing remains required.
313
+
314
+ The known legacy protected-branch 400 message is recognized as permission failure using an exact
315
+ literal from [v15.9.4 CreateService](https://github.com/gitlabhq/gitlabhq/blob/v15.9.4/app/services/commits/create_service.rb).
316
+ Arbitrary response messages are never forwarded to the caller or printed.
317
+
185
318
  ## Store format
186
319
 
187
320
  ```jsonc
@@ -209,13 +342,18 @@ values do not show up in logs. Values with newlines use the heredoc (`KEY<<EOF`)
209
342
  - **`apply` constraints** (GitHub's, not ours): secret values are write-only; a single secret is
210
343
  capped at 48 KB; names may only contain `[A-Za-z0-9_]`, cannot start with a digit or `GITHUB_`, and
211
344
  are stored uppercased. Illegal names abort the command unless you pass `--skip-invalid`.
212
- - **Token permissions**: read/write **Contents** on the vault repo, plus **Secrets: write** on the
213
- target repo for `apply`. A classic PAT with `repo` covers both; organisation-level secrets would
214
- additionally need `admin:org` (not supported in v1).
345
+ - **Token permissions**: GitHub vaults need **Contents** read/write; GitLab vaults use `read_api`
346
+ for reads or `api` for writes, plus project/branch access. `apply` needs **Secrets: write** on the
347
+ GitHub target. Default GitHub API calls can share a classic PAT with `repo`; other sources use
348
+ independent target credentials. Organization secrets are not supported.
215
349
  - **Output is masked**: tokens print as `***`, values are never printed by `list` / `get` / `status`.
216
350
 
217
351
  ## Development
218
352
 
353
+ Do not install the `initx` CLI as a dependency or test tool in this project. Use the existing
354
+ core/utils SDKs, mocks and command-handler fixtures. Historical CLI checks and business CI
355
+ installation examples do not change this rule; see [AGENTS.md](AGENTS.md).
356
+
219
357
  ```bash
220
358
  pnpm install
221
359
  pnpm stub # unbuild --stub, then: initx plugin add .
@@ -225,9 +363,11 @@ pnpm lint
225
363
  pnpm typecheck
226
364
  ```
227
365
 
228
- `pnpm test` runs unit tests and isolated child-process fixtures with fake GitHub responses and
229
- temporary directories. It does not install or invoke the initx CLI. No real vault, DNS, SSH or
230
- GitHub write endpoint is used. The Linux/Windows CI matrix runs the same checks. Platform-specific
366
+ `pnpm test` runs unit tests and isolated child-process fixtures with fake GitHub/GitLab responses and
367
+ temporary directories. Tests use the existing core matcher and direct command handlers, without
368
+ installing or importing the initx CLI. Child-process tests check execution markers and nonzero
369
+ exit codes. No real vault, DNS, SSH or
370
+ GitHub/GitLab write endpoint is used. The Linux/Windows CI matrix runs the same checks. Platform-specific
231
371
  coverage and the local verification record are in [env run implementation](docs/env-run-implementation.md).
232
372
 
233
373
  ## License
package/dist/index.d.mts CHANGED
@@ -1,11 +1,22 @@
1
1
  import { InitxPlugin, InitxMatcherRules, InitxContext } from '@initx-plugin/core';
2
2
 
3
- /** 插件自己的持久化配置(`src/index.ts` 的 defaultStore 就是它) */
3
+ type StorageType = 'github' | 'gitlab';
4
+ interface StorageLocation {
5
+ type: StorageType;
6
+ apiBaseUrl: string;
7
+ repo: string;
8
+ branch: string;
9
+ path: string;
10
+ }
11
+
12
+ /** Legacy flat fields remain GitHub-only; new local locations and credentials are scoped. */
4
13
  interface EnvPluginStore {
5
14
  repo?: string;
6
15
  token?: string;
7
16
  branch?: string;
8
17
  path?: string;
18
+ storage?: Partial<StorageLocation>;
19
+ credentials?: Record<string, string>;
9
20
  }
10
21
 
11
22
  declare class EnvPlugin extends InitxPlugin<EnvPluginStore> {
@@ -16,6 +27,7 @@ declare class EnvPlugin extends InitxPlugin<EnvPluginStore> {
16
27
  defaultStore: EnvPluginStore;
17
28
  rules: InitxMatcherRules;
18
29
  handle(ctx: InitxContext<EnvPluginStore>, ...args: (string | undefined)[]): Promise<void>;
30
+ private dispatch;
19
31
  }
20
32
 
21
33
  export { EnvPlugin as default };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,22 @@
1
1
  import { InitxPlugin, InitxMatcherRules, InitxContext } from '@initx-plugin/core';
2
2
 
3
- /** 插件自己的持久化配置(`src/index.ts` 的 defaultStore 就是它) */
3
+ type StorageType = 'github' | 'gitlab';
4
+ interface StorageLocation {
5
+ type: StorageType;
6
+ apiBaseUrl: string;
7
+ repo: string;
8
+ branch: string;
9
+ path: string;
10
+ }
11
+
12
+ /** Legacy flat fields remain GitHub-only; new local locations and credentials are scoped. */
4
13
  interface EnvPluginStore {
5
14
  repo?: string;
6
15
  token?: string;
7
16
  branch?: string;
8
17
  path?: string;
18
+ storage?: Partial<StorageLocation>;
19
+ credentials?: Record<string, string>;
9
20
  }
10
21
 
11
22
  declare class EnvPlugin extends InitxPlugin<EnvPluginStore> {
@@ -16,6 +27,7 @@ declare class EnvPlugin extends InitxPlugin<EnvPluginStore> {
16
27
  defaultStore: EnvPluginStore;
17
28
  rules: InitxMatcherRules;
18
29
  handle(ctx: InitxContext<EnvPluginStore>, ...args: (string | undefined)[]): Promise<void>;
30
+ private dispatch;
19
31
  }
20
32
 
21
33
  export { EnvPlugin as default };
package/dist/index.mjs CHANGED
@@ -1,50 +1,53 @@
1
- import{InitxPlugin as du}from"@initx-plugin/core";import{readFile as W,copyFile as Au,writeFile as Y,stat as wu,readdir as Du}from"node:fs/promises";import p from"node:process";import{logger as F,inquirer as g}from"@initx-plugin/utils";import{Buffer as j}from"node:buffer";import{z as D}from"zod";import{randomBytes as M}from"node:crypto";import{appendFileSync as Cu}from"node:fs";import z from"libsodium-wrappers";import{constants as gu}from"node:os";import Bu from"cross-spawn";function S(u){return u.replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function G(u,e){return u[e]??u[S(e)]}function $u(u){if(typeof u=="string")return u===""?void 0:u;if(typeof u=="number")return String(u);if(Array.isArray(u))return u.length?u.map(String).join(","):void 0}function yu(u={},e=[]){const t=new Set(e);return{raw:u,string:n=>$u(G(u,n)),boolean:n=>{const r=G(u,n);return typeof r=="boolean"?r:t.has(`--${n}`)||t.has(`--${S(n)}`)},has:n=>G(u,n)!==void 0?!0:t.has(`--${n}`)||t.has(`--${S(n)}`)}}const b="main",x="stores",vu=["INITX_ENV_TOKEN","GH_TOKEN","GITHUB_TOKEN"];function mu(u,e={}){const t=e.env??p.env;if(e.token)return{token:e.token,source:"flag"};for(const n of vu){const r=t[n];if(r)return{token:r,source:n}}return u.token?{token:u.token,source:"config"}:{token:"",source:"none"}}function T(u,e={}){const t=e.env??p.env,n=e.repo||t.INITX_ENV_REPO||u.repo||"",{token:r,source:o}=mu(u,e);if(!n)throw new Error("\u8FD8\u6CA1\u914D vault \u4ED3\u5E93\uFF0C\u5148\u8DD1\uFF1Anpx initx env-config set repo <owner/repo>");if(!/^[^/\s]+\/[^/\s]+$/.test(n))throw new Error(`\u4ED3\u5E93\u8981\u5199\u6210 owner/repo \u5F62\u5F0F\uFF0C\u5F53\u524D\u662F "${n}"`);if(!r)throw new Error(`\u6CA1\u6709\u53EF\u7528\u7684 GitHub token\u3002\u4E09\u9009\u4E00\uFF1A
2
- 1) npx initx env-config set token <pat>
3
- 2) \u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF INITX_ENV_TOKEN\uFF08\u6216 GH_TOKEN / GITHUB_TOKEN\uFF09
4
- 3) \u4EFB\u4F55\u547D\u4EE4\u90FD\u652F\u6301 --token <pat> \u4E34\u65F6\u4F20\u5165`);return{repo:n,token:r,branch:e.branch||t.INITX_ENV_BRANCH||u.branch||b,path:e.path||t.INITX_ENV_PATH||u.path||x,tokenSource:o}}function N(u){return u?u.length<=12?`***\uFF08${u.length} \u5B57\u7B26\uFF09`:`${u.slice(0,4)}\u2026${u.slice(-4)}\uFF08${u.length} \u5B57\u7B26\uFF09`:"(\u672A\u8BBE\u7F6E)"}class C extends Error{constructor(e,t,n){super(`GitHub API ${e} ${t}${n?` - ${n}`:""}`),this.status=e,this.statusText=t,this.body=n,this.name="GitHubApiError"}}class H extends Error{constructor(e){super(e),this.name="GitHubConflictError"}}function ku(u){return u instanceof H||u instanceof C&&(u.status===409||u.status===422)}function bu(u){if(!/^[^/\s]+\/[^/\s]+$/.test(u))throw new Error(`\u4ED3\u5E93\u8981\u5199\u6210 owner/repo \u5F62\u5F0F\uFF0C\u5F53\u524D\u662F "${u}"`)}function q(u){if(!u||u.startsWith("/")||/^[A-Z]:/i.test(u))throw new Error(`\u8DEF\u5F84\u5FC5\u987B\u662F\u4ED3\u5E93\u5185\u7684\u76F8\u5BF9\u8DEF\u5F84\uFF1A"${u}"`);if(u.includes("\\"))throw new Error(`\u8DEF\u5F84\u5206\u9694\u7B26\u53EA\u80FD\u7528 /\uFF1A"${u}"`);if(u.split("/").some(e=>e===""||e==="."||e===".."))throw new Error(`\u8DEF\u5F84\u4E0D\u80FD\u5305\u542B\u7A7A\u6BB5\u6216 ..\uFF1A"${u}"`)}function y(u){return{Authorization:`Bearer ${u}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28","User-Agent":"initx-plugin-env"}}async function Z(u,e,t={}){const n=await fetch(u,{...t,headers:{...y(e),...t.headers??{}}});if(!n.ok)throw new C(n.status,n.statusText,await n.text().catch(()=>{}));return n}function xu(u){return u.split("/").map(encodeURIComponent).join("/")}class I{constructor(e,t,n="main"){this.repo=e,this.token=t,this.branch=n,bu(e)}get repoName(){return this.repo}get branchName(){return this.branch}apiUrl(e){q(e);const[t,n]=this.repo.split("/",2);return`https://api.github.com/repos/${encodeURIComponent(t)}/${encodeURIComponent(n)}/contents/${xu(e)}?ref=${encodeURIComponent(this.branch)}`}async getFile(e){const t=await fetch(this.apiUrl(e),{headers:y(this.token)});if(t.status===404)return null;if(!t.ok)throw new C(t.status,t.statusText,await t.text().catch(()=>{}));const n=await t.json();if(n.type!=="file")throw new C(t.status,t.statusText,`\u8DEF\u5F84 "${e}" \u4E0D\u662F\u6587\u4EF6`);return n}async listDir(e){const t=await fetch(this.apiUrl(e),{headers:y(this.token)});if(t.status===404)return[];if(!t.ok)throw new C(t.status,t.statusText,await t.text().catch(()=>{}));const n=await t.json();if(!Array.isArray(n))throw new C(t.status,t.statusText,`\u8DEF\u5F84 "${e}" \u4E0D\u662F\u76EE\u5F55`);return n}async getText(e){const t=await this.getFile(e);return t?Q(t):null}async putFile(e,t,n,r){const o={message:n,content:j.from(t,"utf8").toString("base64"),branch:this.branch,...r?{sha:r}:{}},s=await fetch(this.apiUrl(e),{method:"PUT",headers:{...y(this.token),"Content-Type":"application/json"},body:JSON.stringify(o)});if(s.status===409||s.status===422)throw new H(`"${e}" \u5728\u8FDC\u7AEF\u5DF2\u7ECF\u88AB\u6539\u8FC7\uFF08sha \u8FC7\u671F\uFF09`);if(!s.ok)throw new C(s.status,s.statusText,await s.text().catch(()=>{}))}async deleteFile(e,t,n){const r=await fetch(this.apiUrl(e),{method:"DELETE",headers:{...y(this.token),"Content-Type":"application/json"},body:JSON.stringify({message:n,sha:t,branch:this.branch})});if(r.status===409||r.status===422)throw new H(`"${e}" \u5728\u8FDC\u7AEF\u5DF2\u7ECF\u88AB\u6539\u8FC7\uFF08sha \u8FC7\u671F\uFF09`);if(!r.ok)throw new C(r.status,r.statusText,await r.text().catch(()=>{}))}}function Q(u){return j.from(u.content.replace(/\n/g,""),u.encoding||"base64").toString("utf8")}const U=1,Tu=D.object({repo:D.string().regex(/^[^/\s]+\/[^/\s]+$/,"\u5FC5\u987B\u662F owner/repo \u5F62\u5F0F"),env:D.string().nullish()}),Nu=D.object({schema:D.number().int().positive().default(U),vars:D.record(D.string(),D.string()).default({}),targets:D.object({envFiles:D.array(D.string().min(1)).optional(),secrets:D.array(Tu).optional()}).optional()});function uu(){return{schema:U,vars:{}}}function B(u){const e=u.trim().replace(/\.json$/i,"");if(!e)throw new Error("store \u540D\u4E0D\u80FD\u4E3A\u7A7A");if(e.includes("/")||e.includes("\\"))throw new Error(`store \u540D\u4E0D\u80FD\u5305\u542B\u8DEF\u5F84\u5206\u9694\u7B26\uFF1A"${u}"`);if(e==="."||e==="..")throw new Error(`store \u540D\u4E0D\u80FD\u662F "${e}"`);if(/[\u0000-\u001F\u007F]/.test(e))throw new Error("store \u540D\u4E0D\u80FD\u5305\u542B\u63A7\u5236\u5B57\u7B26");if(e.length>100)throw new Error("store \u540D\u592A\u957F\uFF08\u4E0A\u9650 100 \u5B57\u7B26\uFF09");return e}function Iu(u){return/^[A-Z_]\w*$/i.test(u)?u.toUpperCase().startsWith("GITHUB_")?"GITHUB_ \u524D\u7F00\u662F GitHub \u4FDD\u7559\u7684":null:"\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\uFF0C\u4E14\u4E0D\u80FD\u4EE5\u6570\u5B57\u5F00\u5934"}function _u(u){return/^[A-Z_][\w.-]*$/i.test(u)}function tu(u,e){const t=B(e),n=(u||"").trim().replace(/^\/+|\/+$/g,""),r=n?`${n}/${t}.json`:`${t}.json`;return q(r),r}function Ou(u,e){let t;try{t=JSON.parse(u)}catch{throw new Error(`store "${e}" \u4E0D\u662F\u5408\u6CD5 JSON`)}const n=Nu.safeParse(t);if(!n.success)throw new Error(`store "${e}" \u7ED3\u6784\u4E0D\u5408\u6CD5\uFF1A${n.error.issues.map(r=>`${r.path.join(".")||"(root)"} ${r.message}`).join("; ")}`);return n.data}function ju(u){return`${JSON.stringify({...u,schema:u.schema??U},null,2)}
5
- `}async function R(u,e){const t=(e||"").trim().replace(/^\/+|\/+$/g,"");return(await u.listDir(t)).filter(n=>n.type==="file"&&n.name.toLowerCase().endsWith(".json")).map(n=>n.name.replace(/\.json$/i,"")).sort((n,r)=>n.localeCompare(r))}async function v(u,e,t){const n=tu(e,t),r=await u.getFile(n);if(!r)return null;const o=B(t);return{name:o,path:n,sha:r.sha,store:Ou(Q(r),o)}}async function K(u,e,t,n,r,o){await u.putFile(tu(e,t),ju(n),r,o)}function eu(u,e){const t={added:[],updated:[],unchanged:[]};for(const[n,r]of Object.entries(e)){const o=u.vars[n];o===void 0?(u.vars[n]=r,t.added.push(n)):o===r?t.unchanged.push(n):(u.vars[n]=r,t.updated.push(n))}return t}const nu=/^export[ \t]+/,Su=/^([^=]*)=(.*)$/;function Gu(u){const e=u.replace(/[ \t]+$/,"");return e===""||/^[ \t]/.test(e)?null:e}const Hu=/^[ \t]*#/;function ru(u){return typeof u=="object"&&u!==null&&u.code==="ENOENT"}async function Uu(u){try{return await wu(u)}catch(e){if(ru(e))return null;throw e}}function Ru(u){return u.replace(/\\([nrt"\\])/g,(e,t)=>t==="n"?`
6
- `:t==="r"?"\r":t==="t"?" ":t)}function Ku(u){const e=u.replace(/^[ \t]+/,"");for(const t of e.split(`
7
- `)){const n=/[ \t]#/.exec(t);if(n!==null)return t.slice(0,n.index).replace(/[ \t]+$/,"")}return e.replace(/[ \t]+$/,"")}function Lu(u){if(u==="")return[];const e=u.split(/\r?\n/);return u.endsWith(`
8
- `)&&e.pop(),e}function ou(u){for(let e=0;e<u.length;e+=1){if(u[e]==="\\"){e+=1;continue}if(u[e]==='"')return e}return-1}function Pu(u,e){const t=u[e];if(Hu.test(t))return null;const n=nu.test(t),r=Su.exec(n?t.replace(nu,""):t);if(r===null)return null;const o=Gu(r[1]);if(o===null)return null;const s=r[2];let i=e,c="none",l;if(s.startsWith('"')){let a=s.slice(1),E=ou(a);for(;E===-1&&i+1<u.length;)i+=1,a+=`
9
- ${u[i]}`,E=ou(a);c="double",l=Ru(E===-1?a:a.slice(0,E))}else if(s.startsWith("'")){const a=s.indexOf("'",1);c="single",l=a===-1?s.slice(1):s.slice(1,a)}else l=Ku(s);return{key:o,value:l,exportPrefix:n,quote:c,raw:t,startLine:e,endLine:i}}function _(u){const e=u.includes(`\r
1
+ import{InitxPlugin as Iu}from"@initx-plugin/core";import{readFile as au,copyFile as Su,writeFile as cu,stat as Ou,readdir as Uu}from"node:fs/promises";import f from"node:process";import{useColors as ju,logger as l,inquirer as x}from"@initx-plugin/utils";import{readFileSync as Ru,appendFileSync as Gu}from"node:fs";import{resolve as Pu}from"node:path";import{z as B}from"zod";import{Buffer as S}from"node:buffer";import{randomBytes as Fu}from"node:crypto";import lu from"libsodium-wrappers";import{constants as Lu}from"node:os";import Vu from"cross-spawn";function z(u){return u.replace(/-([a-z])/g,(t,e)=>e.toUpperCase())}function q(u,t){return u[t]??u[z(t)]}function Hu(u){if(typeof u=="string")return u===""?void 0:u;if(typeof u=="number")return String(u);if(Array.isArray(u))return u.length?u.map(String).join(","):void 0}function Ku(u={},t=[]){const e=new Set(t);return{raw:u,string:n=>Hu(q(u,n)),boolean:n=>{const r=q(u,n);return typeof r=="boolean"?r:e.has(`--${n}`)||e.has(`--${z(n)}`)},has:n=>q(u,n)!==void 0?!0:e.has(`--${n}`)||e.has(`--${z(n)}`)}}function P(u){if(!u.has("config"))return;const t=u.raw.config;if(typeof t!="string"||!t.trim())throw new Error("--config \u9700\u8981\u5355\u4E2A\u6587\u4EF6\u8DEF\u5F84");return t}const Xu={authentication:"\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5F53\u524D\u540E\u7AEF\u7684 token",permission:"\u6CA1\u6709\u6743\u9650\uFF0C\u8BF7\u68C0\u67E5 token \u6388\u6743\u4E0E\u5206\u652F\u4FDD\u62A4","not-found":"\u4ED3\u5E93\u6216 ref \u4E0D\u5B58\u5728\uFF0C\u6216\u5F53\u524D token \u65E0\u6743\u8BBF\u95EE",conflict:"\u8FDC\u7AEF\u7248\u672C\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\uFF1Bpush \u53EF\u7528 --force \u91CD\u8BFB\u540E\u91CD\u8BD5\u4E00\u6B21",validation:"\u8BF7\u6C42\u6216\u54CD\u5E94\u4E0D\u5408\u6CD5\uFF0C\u8BF7\u68C0\u67E5\u8DEF\u5F84\u3001ref \u4E0E\u914D\u7F6E","rate-limit":"API \u8BF7\u6C42\u88AB\u9650\u6D41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",network:"\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25\u6216\u8D85\u65F6\uFF1B\u5199\u5165\u7ED3\u679C\u53EF\u80FD\u672A\u77E5\uFF0C\u8BF7\u5148\u91CD\u8BFB\u786E\u8BA4",unsupported:"\u5B58\u50A8\u540E\u7AEF\u4E0D\u652F\u6301\u6B64\u64CD\u4F5C"};class g extends Error{constructor(t,e){super(`${Xu[t]}${e?`\uFF08HTTP ${e}\uFF09`:""}`),this.code=t,this.status=e,this.name="StorageError"}}function Ju(u){return u instanceof g&&u.code==="conflict"}function Z(u){const t=u.status,e=t===429||t===403&&(u.headers.get("x-ratelimit-remaining")==="0"||u.headers.has("retry-after"));return new g(e?"rate-limit":t===401?"authentication":t===403?"permission":t===404?"not-found":t>=500?"network":"validation",t)}const b={github:"https://api.github.com",gitlab:"https://gitlab.com/api/v4"};function y(u,t=!1){if(!(u===""&&t)){if(u.split("/").some(e=>e==="."||e===".."))throw Object.assign(new g("validation"),{message:"\u8DEF\u5F84\u4E0D\u80FD\u5305\u542B . \u6216 .."});if(!u||/[\\\x00-\x1F\x7F]/.test(u)||u.startsWith("/")||/^[A-Z]:/i.test(u)||u.split("/").some(e=>!e||e==="."||e===".."))throw new g("validation")}}function _(u){try{const t=new URL(u);if(!["https:","http:"].includes(t.protocol)||t.username||t.password||t.search||t.hash)throw new Error("Invalid API base URL");return t.href.replace(/\/+$/,"")}catch{throw new g("validation")}}function T(u,t){if(y(t),/\s/.test(t)||(u==="github"?t.split("/").length!==2:!/^\d+$/.test(t)&&t.split("/").length<2))throw new Error(u==="github"?"\u4ED3\u5E93\u8981\u5199\u6210 owner/repo \u5F62\u5F0F":"GitLab \u4ED3\u5E93\u5FC5\u987B\u662F\u6570\u5B57\u9879\u76EE ID \u6216 group/subgroup/project")}const Yu=B.object({schema:B.literal(1),storage:B.object({type:B.enum(["github","gitlab"]),apiBaseUrl:B.string().min(1).optional(),repo:B.string().min(1),branch:B.string().min(1).optional(),path:B.string().optional()}).strict()}).strict();class L extends Error{constructor(t){super(t),this.name="ProjectConfigError"}}function Wu(u,t=f.cwd()){const e=Pu(t,u||".initx-env.json");let n;try{n=Ru(e,"utf8")}catch(o){if(!u&&o.code==="ENOENT")return;throw new L("\u65E0\u6CD5\u8BFB\u53D6\u9879\u76EE\u914D\u7F6E\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5 --config \u8DEF\u5F84\u4E0E\u6587\u4EF6\u6743\u9650")}let r;try{r=Yu.parse(JSON.parse(n))}catch{throw new L("\u9879\u76EE\u914D\u7F6E\u5FC5\u987B\u662F schema 1 \u7684\u5408\u6CD5 JSON\uFF0C\u4E14\u53EA\u80FD\u5305\u542B\u975E\u654F\u611F storage \u5B9A\u4F4D\u5B57\u6BB5\uFF08\u7981\u6B62 token / vars\uFF09")}try{const o={...r.storage,apiBaseUrl:_(r.storage.apiBaseUrl||b[r.storage.type])};return T(o.type,o.repo),o.path!==void 0&&y(o.path,!0),{file:e,storage:o}}catch{throw new L("\u9879\u76EE storage \u7684 API \u5730\u5740\u3001\u4ED3\u5E93\u6216\u8DEF\u5F84\u4E0D\u5408\u6CD5")}}const V="main",H="stores";function K(u,t){return`${u}:${_(t)}`}function O(u){const t=u.storage?.type||"github";if(t!=="github"&&t!=="gitlab")throw new Error("\u672C\u5730 storage.type \u5FC5\u987B\u662F github \u6216 gitlab");return{type:t,apiBaseUrl:_(u.storage?.apiBaseUrl||b[t])}}function Mu(u,t){const e=t.env??f.env,n=Wu(t.config,t.cwd),r=O(u),o=t.type||e.INITX_ENV_TYPE||n?.storage.type||r.type;if(o!=="github"&&o!=="gitlab")throw new Error("storage.type \u5FC5\u987B\u662F github \u6216 gitlab");const i=o,s=n?.storage.type===i,a=t.apiBaseUrl||e.INITX_ENV_API_BASE_URL||(s?n.storage.apiBaseUrl:void 0)||(r.type===i?r.apiBaseUrl:b[i]),E=_(a),F=r.type===i&&r.apiBaseUrl===E,p=s&&n.storage.apiBaseUrl===E,d=F?u.storage||u:{},h=p?n.storage:void 0,C={type:t.type?"override":e.INITX_ENV_TYPE?"INITX_ENV_TYPE":n?"project":"local/default",apiBaseUrl:t.apiBaseUrl?"override":e.INITX_ENV_API_BASE_URL?"INITX_ENV_API_BASE_URL":p?"project":F?"local/default":"default",repo:"",branch:"",path:""};function A($,w){const I=`INITX_ENV_${$.toUpperCase()}`,v=[[t[$]||void 0,"override"],[e[I]||void 0,I],[h?.[$],"project"],[d[$]||void 0,"local"],[w,"default"]].find(([Nu])=>Nu!==void 0);return C[$]=v[1],v[0]}return{type:i,apiBaseUrl:E,repo:A("repo",""),branch:A("branch",V),path:A("path",H),sources:C,configSource:n?.file||"local/env/default"}}function zu(u,t,e){const n=t.env??f.env;if(t.token)return{token:t.token,source:"flag"};if(n.INITX_ENV_TOKEN)return{token:n.INITX_ENV_TOKEN,source:"INITX_ENV_TOKEN"};const r=e.type==="github"&&e.apiBaseUrl===b.github;if(r){for(const i of["GH_TOKEN","GITHUB_TOKEN"])if(n[i])return{token:n[i],source:i}}const o=u.credentials?.[K(e.type,e.apiBaseUrl)];return o?{token:o,source:"config"}:r&&u.token?{token:u.token,source:"config"}:{token:"",source:"none"}}function X(u,t={}){const e=Mu(u,t);if(!e.repo)throw new Error("\u8FD8\u6CA1\u914D vault \u4ED3\u5E93\uFF0C\u8BF7\u914D\u7F6E .initx-env.json \u6216 env-config set repo <owner/repo>");T(e.type,e.repo),y(e.path,!0);const{token:n,source:r}=zu(u,t,e);if(!n)throw new Error("\u6CA1\u6709\u53EF\u7528\u7684\u5F53\u524D\u540E\u7AEF token\uFF0C\u8BF7\u8BBE\u7F6E INITX_ENV_TOKEN\u3001--token \u6216\u7ED1\u5B9A\u5F53\u524D API \u5730\u5740\u7684\u672C\u5730\u51ED\u636E");return{...e,token:n,tokenSource:r}}function qu(u){return u?"***":"(\u672A\u8BBE\u7F6E)"}function Q(u){return qu(u)}async function D(u,t={}){try{return await fetch(u,{...t,redirect:"error",signal:AbortSignal.timeout(3e4)})}catch{throw new g("network")}}async function k(u){try{return await u.json()}catch(t){throw new g(t instanceof SyntaxError?"validation":"network")}}function m(u){if(!u.ok)throw Z(u)}function pu(u){return{Authorization:`Bearer ${u}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28","User-Agent":"initx-plugin-env"}}class Zu{constructor(t,e){this.location=t,T("github",t.repo),this.base=`${_(t.apiBaseUrl)}/repos/${t.repo.split("/").map(encodeURIComponent).join("/")}`,this.headers=pu(e)}capabilities={read:!0,list:!0,write:!0,delete:!0,conditionalWrite:!0};base;headers;url(t){y(t,!0);const e=t?`/${t.split("/").map(encodeURIComponent).join("/")}`:"";return`${this.base}/contents${e}?${new URLSearchParams({ref:this.location.branch})}`}async checkTarget(){const t=await D(`${this.base}/commits/${encodeURIComponent(this.location.branch)}`,{headers:this.headers});if(!await this.isEmptyRepository(t)){if(t.status===404){const e=await D(`${this.base}/commits?per_page=1`,{headers:this.headers});if(await this.isEmptyRepository(e))return;m(e)}m(t)}}async isEmptyRepository(t){return t.status!==409?!1:(await k(t))?.message==="Git Repository is empty."}async read(t){y(t);const e=await D(this.url(t),{headers:this.headers});if(e.status===404||await this.isEmptyRepository(e))return await this.checkTarget(),null;m(e);const n=await k(e);if(!n||n.type!=="file"||typeof n.sha!="string"||!n.sha||typeof n.content!="string"||n.encoding!=="base64")throw new g("validation");return{content:S.from(n.content,"base64").toString("utf8"),version:n.sha}}async list(t){const e=await D(this.url(t),{headers:this.headers});if(e.status===404||await this.isEmptyRepository(e))return await this.checkTarget(),[];m(e);const n=await k(e);if(!Array.isArray(n)||n.some(r=>!r||typeof r.name!="string"||typeof r.path!="string"||typeof r.type!="string"))throw new g("validation");return n.map(r=>({name:r.name,path:r.path,kind:r.type==="file"?"file":r.type==="dir"?"directory":"other"}))}async write(t,e,n){y(t);const r=n.condition;if(r.kind!=="create"&&(r.kind!=="update"||!r.expectedVersion))throw new g("validation");const o=await D(this.url(t),{method:"PUT",headers:{...this.headers,"Content-Type":"application/json"},body:JSON.stringify({branch:this.location.branch,message:n.message||"env: write store",content:S.from(e).toString("base64"),...r.kind==="update"?{sha:r.expectedVersion}:{}})});if([404,409,422].includes(o.status)){const i=await this.read(t);if(r.kind==="create"?i!==null:i?.version!==r.expectedVersion)throw new g("conflict",o.status)}if(!o.ok)throw Z(o)}async remove(t,e){if(y(t),!e.expectedVersion)throw new g("validation");const n=await D(this.url(t),{method:"DELETE",headers:{...this.headers,"Content-Type":"application/json"},body:JSON.stringify({branch:this.location.branch,message:e.message||"env: remove store",sha:e.expectedVersion})});if([404,409,422].includes(n.status)&&(await this.read(t))?.version!==e.expectedVersion)throw new g("conflict",n.status);m(n)}}async function Eu(u){if(u.status!==400)return;let t;try{t=await k(u)}catch(e){if(e instanceof g&&e.code==="network")throw e}if(t?.message==="You are not allowed to push into this branch")throw new g("permission",400)}class Qu{constructor(t,e){this.location=t,T("gitlab",t.repo),this.projectUrl=`${_(t.apiBaseUrl)}/projects/${encodeURIComponent(t.repo)}`,this.base=`${this.projectUrl}/repository`,this.headers={"PRIVATE-TOKEN":e,"Content-Type":"application/json"}}capabilities={read:!0,list:!0,write:!0,delete:!0,conditionalWrite:!0};projectUrl;base;headers;fileUrl(t){return y(t),`${this.base}/files/${encodeURIComponent(t)}`}async checkTarget(){const t=await D(`${this.base}/commits/${encodeURIComponent(this.location.branch)}`,{headers:this.headers});if(t.status===404){const e=await D(this.projectUrl,{headers:this.headers});m(e);const n=await k(e);if(!n||typeof n.empty_repo!="boolean")throw new g("validation");if(n.empty_repo)return!1}return m(t),!0}async read(t){const e=await D(`${this.fileUrl(t)}?${new URLSearchParams({ref:this.location.branch})}`,{headers:this.headers});if(e.status===404)return await this.checkTarget(),null;m(e);const n=await k(e);if(!n||typeof n.content!="string"||n.encoding!=="base64"||typeof n.last_commit_id!="string"||!n.last_commit_id)throw new g("validation");return{content:S.from(n.content,"base64").toString("utf8"),version:n.last_commit_id}}async list(t){y(t,!0);const e=[];if(!await this.checkTarget())return e;for(let n=1;;n++){const r=new URLSearchParams({ref:this.location.branch,per_page:"100",page:String(n),...t?{path:t}:{}}),o=await D(`${this.base}/tree?${r}`,{headers:this.headers});if(o.status===404)return await this.checkTarget(),e;m(o);const i=await k(o);if(!Array.isArray(i)||i.some(a=>!a||typeof a.name!="string"||typeof a.path!="string"||typeof a.type!="string"))throw new g("validation");e.push(...i.map(a=>({name:a.name,path:a.path,kind:a.type==="blob"?"file":a.type==="tree"?"directory":"other"})));const s=o.headers.get("x-next-page");if(!i.length||s===""||s===null&&i.length<100)return e;if(s!==null&&s!==String(n+1))throw new g("validation")}}async write(t,e,n){const r=n.condition;if(r.kind!=="create"&&(r.kind!=="update"||!r.expectedVersion))throw new g("validation");const o=await D(this.fileUrl(t),{method:r.kind==="create"?"POST":"PUT",headers:this.headers,body:JSON.stringify({branch:this.location.branch,commit_message:n.message||"env: write store",content:S.from(e).toString("base64"),encoding:"base64",...r.kind==="update"?{last_commit_id:r.expectedVersion}:{}})});if(await Eu(o),[400,404,409,422].includes(o.status)){const i=await this.read(t);if(r.kind==="create"?i!==null:i?.version!==r.expectedVersion)throw new g("conflict",o.status);if(r.kind==="update"&&o.status===400&&i?.content===e)return}if(!o.ok)throw Z(o)}async remove(t,e){if(y(t),!e.expectedVersion)throw new g("validation");const n=await D(`${this.base}/commits`,{method:"POST",headers:this.headers,body:JSON.stringify({branch:this.location.branch,commit_message:e.message||"env: remove store",actions:[{action:"delete",file_path:t,last_commit_id:e.expectedVersion}]})});if(await Eu(n),[400,404,409,422].includes(n.status)&&(await this.read(t))?.version!==e.expectedVersion)throw new g("conflict",n.status);m(n)}}const ut={github:Zu,gitlab:Qu};function J(u){const t=ut[u.type];if(!t)throw new g("unsupported");return new t(u,u.token)}function Y(u,t){if(!u.capabilities[t])throw new g("unsupported")}const uu=1,tt=B.object({repo:B.string().regex(/^[^/\s]+\/[^/\s]+$/,"\u5FC5\u987B\u662F owner/repo \u5F62\u5F0F"),env:B.string().nullish()}),et=B.object({schema:B.number().int().positive().default(uu),vars:B.record(B.string(),B.string()).default({}),targets:B.object({envFiles:B.array(B.string().min(1)).optional(),secrets:B.array(tt).optional()}).optional()});function fu(){return{schema:uu,vars:{}}}function N(u){const t=u.trim().replace(/\.json$/i,"");if(!t)throw new Error("store \u540D\u4E0D\u80FD\u4E3A\u7A7A");if(t.includes("/")||t.includes("\\"))throw new Error(`store \u540D\u4E0D\u80FD\u5305\u542B\u8DEF\u5F84\u5206\u9694\u7B26\uFF1A"${u}"`);if(t==="."||t==="..")throw new Error(`store \u540D\u4E0D\u80FD\u662F "${t}"`);if(/[\u0000-\u001F\u007F]/.test(t))throw new Error("store \u540D\u4E0D\u80FD\u5305\u542B\u63A7\u5236\u5B57\u7B26");if(t.length>100)throw new Error("store \u540D\u592A\u957F\uFF08\u4E0A\u9650 100 \u5B57\u7B26\uFF09");return t}function nt(u){return/^[A-Z_]\w*$/i.test(u)?u.toUpperCase().startsWith("GITHUB_")?"GITHUB_ \u524D\u7F00\u662F GitHub \u4FDD\u7559\u7684":null:"\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\uFF0C\u4E14\u4E0D\u80FD\u4EE5\u6570\u5B57\u5F00\u5934"}function rt(u){return/^[A-Z_][\w.-]*$/i.test(u)}function hu(u,t){const e=N(t),n=(u||"").trim().replace(/^\/+|\/+$/g,""),r=n?`${n}/${e}.json`:`${e}.json`;return y(r),r}function ot(u,t){let e;try{e=JSON.parse(u)}catch{throw new Error(`store "${t}" \u4E0D\u662F\u5408\u6CD5 JSON`)}const n=et.safeParse(e);if(!n.success)throw new Error(`store "${t}" \u7ED3\u6784\u4E0D\u5408\u6CD5\uFF1A${n.error.issues.map(r=>`${r.path.join(".")||"(root)"} ${r.message}`).join("; ")}`);return n.data}function it(u){return`${JSON.stringify({...u,schema:u.schema??uu},null,2)}
2
+ `}async function tu(u,t){Y(u,"list");const e=(t||"").trim().replace(/^\/+|\/+$/g,"");return(await u.list(e)).filter(n=>n.kind==="file"&&n.name.toLowerCase().endsWith(".json")).map(n=>n.name.replace(/\.json$/i,"")).sort((n,r)=>n.localeCompare(r))}async function U(u,t,e){const n=hu(t,e);Y(u,"read");const r=await u.read(n);if(!r)return null;const o=N(e);return{name:o,path:n,version:r.version,store:ot(r.content,o)}}async function eu(u,t,e,n,r,o){Y(u,"write"),Y(u,"conditionalWrite"),await u.write(hu(t,e),it(n),{message:r,condition:o===void 0?{kind:"create"}:{kind:"update",expectedVersion:o}})}function du(u,t){const e={added:[],updated:[],unchanged:[]};for(const[n,r]of Object.entries(t)){const o=u.vars[n];o===void 0?(u.vars[n]=r,e.added.push(n)):o===r?e.unchanged.push(n):(u.vars[n]=r,e.updated.push(n))}return e}const gu=/^export[ \t]+/,st=/^([^=]*)=(.*)$/;function at(u){const t=u.replace(/[ \t]+$/,"");return t===""||/^[ \t]/.test(t)?null:t}const ct=/^[ \t]*#/;function wu(u){return typeof u=="object"&&u!==null&&u.code==="ENOENT"}async function Ft(u){try{return await Ou(u)}catch(t){if(wu(t))return null;throw t}}function lt(u){return u.replace(/\\([nrt"\\])/g,(t,e)=>e==="n"?`
3
+ `:e==="r"?"\r":e==="t"?" ":e)}function pt(u){const t=u.replace(/^[ \t]+/,"");for(const e of t.split(`
4
+ `)){const n=/[ \t]#/.exec(e);if(n!==null)return e.slice(0,n.index).replace(/[ \t]+$/,"")}return t.replace(/[ \t]+$/,"")}function Et(u){if(u==="")return[];const t=u.split(/\r?\n/);return u.endsWith(`
5
+ `)&&t.pop(),t}function Cu(u){for(let t=0;t<u.length;t+=1){if(u[t]==="\\"){t+=1;continue}if(u[t]==='"')return t}return-1}function ft(u,t){const e=u[t];if(ct.test(e))return null;const n=gu.test(e),r=st.exec(n?e.replace(gu,""):e);if(r===null)return null;const o=at(r[1]);if(o===null)return null;const i=r[2];let s=t,a="none",E;if(i.startsWith('"')){let F=i.slice(1),p=Cu(F);for(;p===-1&&s+1<u.length;)s+=1,F+=`
6
+ ${u[s]}`,p=Cu(F);a="double",E=lt(p===-1?F:F.slice(0,p))}else if(i.startsWith("'")){const F=i.indexOf("'",1);a="single",E=F===-1?i.slice(1):i.slice(1,F)}else E=pt(i);return{key:o,value:E,exportPrefix:n,quote:a,raw:e,startLine:t,endLine:s}}function W(u){const t=u.includes(`\r
10
7
  `)?`\r
11
8
  `:`
12
- `,t=u.endsWith(`
13
- `),n=Lu(u),r=new Map,o=[],s=[];for(let i=0;i<n.length;){const c=Pu(n,i);if(c===null){i+=1;continue}r.has(c.key)&&!o.includes(c.key)&&o.push(c.key),r.set(c.key,c.value),s.push(c),i=c.endLine+1}return{lines:n,pairs:r,duplicates:o,eol:e,trailingNewline:t,entries:s}}const Vu=/[\s"'\\#$`]/;function Xu(u){return u!==""&&!Vu.test(u)}function Ju(u){return Xu(u)?u:`"${u.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t")}"`}function L(u,e,t){return`${t?"export ":""}${u}=${Ju(e)}`}function su(u,e,t){return`${u.join(e)}${t?e:""}`}function iu(u,e){const{pairs:t}=_(u),n=[],r=[],o=[],s=new Map;for(const[i,c]of Object.entries(e))s.set(i,c),t.has(i)?t.get(i)===c?o.push(i):r.push(i):n.push(i);return{added:n,updated:r,unchanged:o,target:s}}function Wu(u,e){if(u===""){const a=Yu(e);return{content:a,added:Object.keys(e),updated:[],unchanged:[],duplicates:[],changed:a!==u}}const t=_(u),{added:n,updated:r,unchanged:o,target:s}=iu(u,e),i=t.lines,c=new Map;t.entries.forEach((a,E)=>c.set(a.key,E));for(let a=t.entries.length-1;a>=0;a-=1){const E=t.entries[a];if(c.get(E.key)!==a)continue;const f=s.get(E.key);if(f===void 0||f===E.value)continue;const h=L(E.key,f,E.exportPrefix);i.splice(E.startLine,E.endLine-E.startLine+1,h)}if(n.length>0){i.length>0&&i[i.length-1].trim()!==""&&i.push("");for(const a of n)i.push(L(a,e[a],!1))}const l=su(i,t.eol,t.trailingNewline);return{content:l,added:n,updated:r,unchanged:o,duplicates:t.duplicates,changed:l!==u}}function Yu(u,e=`
14
- `){const t=Object.entries(u).map(([n,r])=>L(n,r,!1));return t.length===0?"":su(t,e,!0)}function Mu(u,e){const{added:t,updated:n,unchanged:r}=iu(u,e),o=[..._(u).pairs.keys()].filter(s=>!(s in e));return{added:t,updated:n,unchanged:r,localOnly:o}}async function P(u){try{return await W(u,"utf8")}catch(e){if(ru(e))return null;throw e}}async function zu(u,e){const t=await Uu(u);if(t!==null&&t.isFile()){if(await W(u,"utf8")!==e){const n=`${u}.bak`;return await Au(u,n),await Y(u,e,"utf8"),{backupPath:n}}return{backupPath:null}}return await Y(u,e,"utf8"),{backupPath:null}}function au(){return p.env.GITHUB_ACTIONS==="true"?!0:typeof p.env.GITHUB_ENV=="string"&&p.env.GITHUB_ENV.length>0}function qu(u){let e=`EOF_${M(4).toString("hex")}`;for(;u.includes(e);)e=`EOF_${M(4).toString("hex")}`;return e}function Zu(u){return Object.entries(u).map(([e,t])=>{if(!t.includes(`
15
- `))return`${e}=${t}`;const n=qu(t);return[`${e}<<${n}`,t,n].join(`
9
+ `,e=u.endsWith(`
10
+ `),n=Et(u),r=new Map,o=[],i=[];for(let s=0;s<n.length;){const a=ft(n,s);if(a===null){s+=1;continue}r.has(a.key)&&!o.includes(a.key)&&o.push(a.key),r.set(a.key,a.value),i.push(a),s=a.endLine+1}return{lines:n,pairs:r,duplicates:o,eol:t,trailingNewline:e,entries:i}}const ht=/[\s"'\\#$`]/;function dt(u){return u!==""&&!ht.test(u)}function gt(u){return dt(u)?u:`"${u.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t")}"`}function nu(u,t,e){return`${e?"export ":""}${u}=${gt(t)}`}function Bu(u,t,e){return`${u.join(t)}${e?t:""}`}function Au(u,t){const{pairs:e}=W(u),n=[],r=[],o=[],i=new Map;for(const[s,a]of Object.entries(t))i.set(s,a),e.has(s)?e.get(s)===a?o.push(s):r.push(s):n.push(s);return{added:n,updated:r,unchanged:o,target:i}}function wt(u,t){if(u===""){const F=Ct(t);return{content:F,added:Object.keys(t),updated:[],unchanged:[],duplicates:[],changed:F!==u}}const e=W(u),{added:n,updated:r,unchanged:o,target:i}=Au(u,t),s=e.lines,a=new Map;e.entries.forEach((F,p)=>a.set(F.key,p));for(let F=e.entries.length-1;F>=0;F-=1){const p=e.entries[F];if(a.get(p.key)!==F)continue;const d=i.get(p.key);if(d===void 0||d===p.value)continue;const h=nu(p.key,d,p.exportPrefix);s.splice(p.startLine,p.endLine-p.startLine+1,h)}if(n.length>0){s.length>0&&s[s.length-1].trim()!==""&&s.push("");for(const F of n)s.push(nu(F,t[F],!1))}const E=Bu(s,e.eol,e.trailingNewline);return{content:E,added:n,updated:r,unchanged:o,duplicates:e.duplicates,changed:E!==u}}function Ct(u,t=`
11
+ `){const e=Object.entries(u).map(([n,r])=>nu(n,r,!1));return e.length===0?"":Bu(e,t,!0)}function Bt(u,t){const{added:e,updated:n,unchanged:r}=Au(u,t),o=[...W(u).pairs.keys()].filter(i=>!(i in t));return{added:e,updated:n,unchanged:r,localOnly:o}}async function ru(u){try{return await au(u,"utf8")}catch(t){if(wu(t))return null;throw t}}async function At(u,t){const e=await Ft(u);if(e!==null&&e.isFile()){if(await au(u,"utf8")!==t){const n=`${u}.bak`;return await Su(u,n),await cu(u,t,"utf8"),{backupPath:n}}return{backupPath:null}}return await cu(u,t,"utf8"),{backupPath:null}}function Du(){return f.env.GITHUB_ACTIONS==="true"?!0:typeof f.env.GITHUB_ENV=="string"&&f.env.GITHUB_ENV.length>0}function Dt(u){let t=`EOF_${Fu(4).toString("hex")}`;for(;u.includes(t);)t=`EOF_${Fu(4).toString("hex")}`;return t}function yt(u){return Object.entries(u).map(([t,e])=>{if(!e.includes(`
12
+ `))return`${t}=${e}`;const n=Dt(e);return[`${t}<<${n}`,e,n].join(`
16
13
  `)}).join(`
17
- `)}const Qu=/[\s'"$]/,ut=/[\\"$`]/g;function tt(u){return Object.entries(u).map(([e,t])=>{if(t.includes(`
18
- `)||t.includes("\r")){const n=t.replace(ut,r=>`\\${r}`).replace(/\r\n|\r|\n/g,"\\n");return`${e}="${n}"`}return Qu.test(t)?`${e}='${t.replace(/'/g,"'\\''")}'`:`${e}=${t}`}).join(`
19
- `)}function et(u){if(u.includes(`
20
- `)||u.includes("\r"))for(const e of u.split(/\r?\n/))e!==""&&console.log(`::add-mask::${e}`);console.log(`::add-mask::${u}`)}function nt(u,e={}){const{forceStdout:t=!1,mask:n=!0}=e,r=Object.keys(u),o=[];if(n&&r.length>0&&au())for(const c of r)et(u[c]),o.push(c);const s=t?void 0:p.env.GITHUB_ENV;if(s){const c=Zu(u);return c!==""&&Cu(s,`
21
- ${c}`,{encoding:"utf8"}),{target:"github_env",injected:r,masked:o}}const i=tt(u);return i!==""&&console.log(i),{target:"stdout",injected:r,masked:o}}const rt=/^\.env(?:\..+)?$/,ot=[".bak",".example",".sample",".template",".keys",".enc"];async function st(u,e){const[t="list",...n]=e;switch(t){case"list":case"ls":return it(u);case"pull":return ct(u,n);case"set":return lt(u,n);case"push":return ft(u,n);case"setup":return ht(u,n);default:throw new Error(`\u672A\u77E5\u5B50\u547D\u4EE4 "${t}"\uFF0C\u53EF\u7528\uFF1Alist | pull | set | push | setup | apply\uFF08npx initx env help\uFF09`)}}function m(u,e={}){const t=T(u.store,{token:u.options.string("token"),repo:e.repo});return{config:t,client:new I(t.repo,t.token,t.branch)}}async function it(u){const{config:e,client:t}=m(u),n=await R(t,e.path);if(!n.length){F.warn(`${e.repo} \u7684 ${e.path}/ \u4E0B\u8FD8\u6CA1\u6709 store`),F.info("\u5EFA\u4E00\u4E2A\uFF1Anpx initx env set <\u540D\u5B57> KEY=value");return}F.info(`${e.repo} \xB7 ${e.path}/ \u5171 ${n.length} \u4E2A store\uFF1A`);for(const r of n)F.info(` - ${r}`)}async function Fu(u,e,t){if(t)return B(t);const n=await R(u,e);if(!n.length)throw new Error("\u4ED3\u5E93\u91CC\u8FD8\u6CA1\u6709 store\u3002\u5148\u5EFA\u4E00\u4E2A\uFF1Anpx initx env set <\u540D\u5B57> KEY=value");if(n.length===1)return n[0];if(!p.stdin.isTTY)throw new Error(`\u6709\u591A\u4E2A store\uFF0C\u975E\u4EA4\u4E92\u73AF\u5883\u4E0B\u5FC5\u987B\u6307\u5B9A\u540D\u5B57\uFF1A
14
+ `)}const mt=/[\s'"$]/,$t=/[\\"$`]/g;function vt(u){return Object.entries(u).map(([t,e])=>{if(e.includes(`
15
+ `)||e.includes("\r")){const n=e.replace($t,r=>`\\${r}`).replace(/\r\n|\r|\n/g,"\\n");return`${t}="${n}"`}return mt.test(e)?`${t}='${e.replace(/'/g,"'\\''")}'`:`${t}=${e}`}).join(`
16
+ `)}function bt(u){if(u.includes(`
17
+ `)||u.includes("\r"))for(const t of u.split(/\r?\n/))t!==""&&console.log(`::add-mask::${t}`);console.log(`::add-mask::${u}`)}function kt(u,t={}){const{forceStdout:e=!1,mask:n=!0}=t,r=Object.keys(u),o=[];if(n&&r.length>0&&Du())for(const a of r)bt(u[a]),o.push(a);const i=e?void 0:f.env.GITHUB_ENV;if(i){const a=yt(u);return a!==""&&Gu(i,`
18
+ ${a}`,{encoding:"utf8"}),{target:"github_env",injected:r,masked:o}}const s=vt(u);return s!==""&&console.log(s),{target:"stdout",injected:r,masked:o}}function c(u,t,e="stdout"){return f.env.NO_COLOR!==void 0||f.env.FORCE_COLOR==="0"||!f[e].isTTY&&!f.env.FORCE_COLOR?u:ju(u)[t]().toString()}function xt(u){const t=u instanceof Error&&u.message?u.message:"\u547D\u4EE4\u6267\u884C\u5931\u8D25";f.stderr.write(`${c(t,"red","stderr")}
19
+ `),f.exitCode=1}const _t=/^\.env(?:\..+)?$/,Tt=[".bak",".example",".sample",".template",".keys",".enc"];async function Nt(u,t){const[e="list",...n]=t;switch(e){case"list":case"ls":return It(u);case"pull":return Ut(u,n);case"set":return Rt(u,n);case"push":return Gt(u,n);case"setup":return Lt(u,n);default:throw new Error(`\u672A\u77E5\u5B50\u547D\u4EE4 "${e}"\uFF0C\u53EF\u7528\uFF1Alist | pull | set | push | setup | apply\uFF08npx initx env help\uFF09`)}}function j(u,t={}){const e=X(u.store,{token:u.options.string("token"),config:P(u.options),repo:t.repo});return{config:e,client:J(e)}}async function It(u){const{config:t,client:e}=j(u),n=await tu(e,t.path);if(!n.length){l.warn(c(`${t.repo} \u7684 ${t.path}/ \u4E0B\u8FD8\u6CA1\u6709 store`,"yellow")),l.info("\u5EFA\u4E00\u4E2A\uFF1Anpx initx env set <\u540D\u5B57> KEY=value");return}l.info(`${c(t.repo,"blue")} \xB7 ${c(`${t.path}/`,"blue")} \u5171 ${n.length} \u4E2A store\uFF1A`);for(const r of n)l.info(` - ${c(r,"blue")}`)}async function yu(u,t,e){if(e)return N(e);const n=await tu(u,t);if(!n.length)throw new Error("\u4ED3\u5E93\u91CC\u8FD8\u6CA1\u6709 store\u3002\u5148\u5EFA\u4E00\u4E2A\uFF1Anpx initx env set <\u540D\u5B57> KEY=value");if(n.length===1)return n[0];if(!f.stdin.isTTY)throw new Error(`\u6709\u591A\u4E2A store\uFF0C\u975E\u4EA4\u4E92\u73AF\u5883\u4E0B\u5FC5\u987B\u6307\u5B9A\u540D\u5B57\uFF1A
22
20
  ${n.join(`
23
- `)}`);return g.search("\u9009\u4E00\u4E2A store",n)}async function V(u,e,t){const n=await v(u,e,t);if(!n)throw new Error(`\u4ED3\u5E93\u91CC\u6CA1\u6709 store "${t}"\u3002\u7528 npx initx env list \u770B\u770B\u6709\u54EA\u4E9B`);return n}async function at(u,e){const t=u.options.string("to");if(t)return t.split(",").map(s=>s.trim()).filter(Boolean);const n=e.store.targets?.envFiles;if(n?.length)return n;const r=await Ft();if(r.length===1)return r;if(r.length===0){if(!p.stdin.isTTY)return[".env.local"];if(!await g.confirm("\u5F53\u524D\u76EE\u5F55\u6CA1\u6709 .env \u6587\u4EF6\uFF0C\u65B0\u5EFA .env.local\uFF1F"))throw new Error("\u5DF2\u53D6\u6D88\uFF08\u4E5F\u53EF\u4EE5\u7528 --to <\u6587\u4EF6> \u6307\u5B9A\u76EE\u6807\uFF09");return[".env.local"]}if(!p.stdin.isTTY)throw new Error(`\u5F53\u524D\u76EE\u5F55\u6709\u591A\u4E2A .env \u6587\u4EF6\uFF0C\u975E\u4EA4\u4E92\u73AF\u5883\u4E0B\u8BF7\u7528 --to \u6307\u5B9A\uFF1A
21
+ `)}`);return x.search("\u9009\u4E00\u4E2A store",n)}async function ou(u,t,e){const n=await U(u,t,e);if(!n)throw new Error(`\u4ED3\u5E93\u91CC\u6CA1\u6709 store "${e}"\u3002\u7528 npx initx env list \u770B\u770B\u6709\u54EA\u4E9B`);return n}async function St(u,t){const e=u.options.string("to");if(e)return e.split(",").map(i=>i.trim()).filter(Boolean);const n=t.store.targets?.envFiles;if(n?.length)return n;const r=await Ot();if(r.length===1)return r;if(r.length===0){if(!f.stdin.isTTY)return[".env.local"];if(!await x.confirm("\u5F53\u524D\u76EE\u5F55\u6CA1\u6709 .env \u6587\u4EF6\uFF0C\u65B0\u5EFA .env.local\uFF1F"))throw new Error("\u5DF2\u53D6\u6D88\uFF08\u4E5F\u53EF\u4EE5\u7528 --to <\u6587\u4EF6> \u6307\u5B9A\u76EE\u6807\uFF09");return[".env.local"]}if(!f.stdin.isTTY)throw new Error(`\u5F53\u524D\u76EE\u5F55\u6709\u591A\u4E2A .env \u6587\u4EF6\uFF0C\u975E\u4EA4\u4E92\u73AF\u5883\u4E0B\u8BF7\u7528 --to \u6307\u5B9A\uFF1A
24
22
  ${r.join(`
25
- `)}`);const o=await g.select("\u5199\u5230\u54EA\u4E2A\u6587\u4EF6",r);return[r[o]]}async function Ft(){return(await Du(p.cwd(),{withFileTypes:!0})).filter(u=>u.isFile()&&rt.test(u.name)).map(u=>u.name).filter(u=>!ot.some(e=>u.endsWith(e))).sort((u,e)=>{const t=n=>n===".env.local"?0:n===".env"?1:2;return t(u)-t(e)||u.localeCompare(e)})}async function ct(u,e){const{config:t,client:n}=m(u),r=await Fu(n,t.path,e[0]),o=await V(n,t.path,r),s=o.store.vars,i=Object.keys(s);if(!i.length){F.warn(`store "${r}" \u91CC\u6CA1\u6709\u4EFB\u4F55\u53D8\u91CF`);return}const c=await at(u,o),l=u.options.boolean("dry-run");for(const a of c){const E=await P(a)??"",f=Wu(E,s);if(!f.changed){F.info(`${a}: \u5DF2\u7ECF\u662F\u6700\u65B0\u7684\uFF08${i.length} \u4E2A\u53D8\u91CF\uFF0C\u65E0\u9700\u6539\u52A8\uFF09`);continue}if(l){const d=Mu(E,s);F.info(`${a}\uFF08--dry-run\uFF0C\u672A\u5199\u5165\uFF09\uFF1A`);for(const w of d.added)F.info(` + ${w}`);for(const w of d.updated)F.info(` ~ ${w}`);continue}const{backupPath:h}=await zu(a,f.content);F.success(`${a}: \u65B0\u589E ${f.added.length} \xB7 \u66F4\u65B0 ${f.updated.length} \xB7 \u672A\u53D8 ${f.unchanged.length}`),h&&F.info(` \u65E7\u6587\u4EF6\u5907\u4EFD\u5728 ${h}`),f.duplicates.length&&F.warn(` \u6CE8\u610F\uFF1A${f.duplicates.join(", ")} \u5728\u6587\u4EF6\u91CC\u51FA\u73B0\u591A\u6B21\uFF0C\u53EA\u66F4\u65B0\u4E86\u6700\u540E\u4E00\u884C`)}}function Et(u){const e={};for(const t of u){const n=t.indexOf("=");if(n<=0)throw new Error(`\u53C2\u6570\u8981\u5199\u6210 KEY=value \u5F62\u5F0F\uFF1A"${t}"`);const r=t.slice(0,n).trim(),o=t.slice(n+1);if(!_u(r))throw new Error(`\u53D8\u91CF\u540D\u4E0D\u5408\u6CD5\uFF1A"${r}"`);e[r]=o}if(!Object.keys(e).length)throw new Error("\u81F3\u5C11\u7ED9\u4E00\u4E2A KEY=value");return e}async function lt(u,e){const[t,...n]=e;if(!t)throw new Error("\u7528\u6CD5\uFF1Anpx initx env set <store \u540D> KEY=value [KEY2=value2 ...]");const r=B(t),o=Et(n),{config:s,client:i}=m(u),c=await v(i,s.path,r),l=c?.store??uu(),a=eu(l,o);if(!a.added.length&&!a.updated.length){F.info(`store "${r}" \u65E0\u9700\u6539\u52A8\uFF08${a.unchanged.length} \u4E2A\u53D8\u91CF\u503C\u76F8\u540C\uFF09`);return}await K(i,s.path,r,l,`env: set ${Object.keys(o).join(", ")} in ${r}`,c?.sha),F.success(`store "${r}" \u5DF2\u66F4\u65B0\uFF1A\u65B0\u589E ${a.added.length} \xB7 \u4FEE\u6539 ${a.updated.length}`);for(const E of a.added)F.info(` + ${E}`);for(const E of a.updated)F.info(` ~ ${E}`)}async function ft(u,e){const[t]=e;if(!t)throw new Error("\u7528\u6CD5\uFF1Anpx initx env push <store \u540D> [--from .env.local] [--prune] [--force]");const n=B(t),r=u.options.string("from")??await pt(),o=await P(r);if(o===null)throw new Error(`\u627E\u4E0D\u5230\u6587\u4EF6 "${r}"\uFF08\u7528 --from \u6307\u5B9A\uFF09`);const s=Object.fromEntries(_(o).pairs);if(!Object.keys(s).length)throw new Error(`"${r}" \u91CC\u6CA1\u89E3\u6790\u51FA\u4EFB\u4F55 KEY=value`);const{config:i,client:c}=m(u),l=await v(c,i.path,n),a=l?.store??uu(),E=eu(a,s),f=[];if(u.options.boolean("prune"))for(const d of Object.keys(a.vars))d in s||(delete a.vars[d],f.push(d));if(!E.added.length&&!E.updated.length&&!f.length){F.info(`store "${n}" \u65E0\u9700\u6539\u52A8\uFF08${r} \u91CC\u7684\u503C\u90FD\u4E00\u6837\uFF09`);return}const h=`env: push ${n} from ${r}`;try{await K(c,i.path,n,a,h,l?.sha)}catch(d){if(!ku(d)||!u.options.boolean("force"))throw d;const w=await V(c,i.path,n);F.warn("\u8FDC\u7AEF\u5DF2\u66F4\u65B0\uFF0C--force\uFF1A\u4EE5\u672C\u5730\u6587\u4EF6\u4E3A\u51C6\u91CD\u65B0\u5199\u5165"),await K(c,i.path,n,a,h,w.sha)}F.success(`store "${n}" \u5DF2\u66F4\u65B0\uFF1A\u65B0\u589E ${E.added.length} \xB7 \u4FEE\u6539 ${E.updated.length}${f.length?` \xB7 \u5220\u9664 ${f.length}`:""}`)}async function pt(){for(const u of[".env.local",".env"])if(await P(u)!==null)return u;throw new Error("\u5F53\u524D\u76EE\u5F55\u6CA1\u6709 .env.local / .env\uFF08\u7528 --from \u6307\u5B9A\u6587\u4EF6\uFF09")}async function ht(u,e){const{config:t,client:n}=m(u),r=await Fu(n,t.path,e[0]),o=(await V(n,t.path,r)).store.vars;if(!Object.keys(o).length)throw new Error(`store "${r}" \u91CC\u6CA1\u6709\u4EFB\u4F55\u53D8\u91CF`);if(u.options.boolean("json")){console.log(JSON.stringify(o)),F.info(`\u5DF2\u8F93\u51FA ${Object.keys(o).length} \u4E2A\u53D8\u91CF\uFF08JSON\uFF09`);return}const s=nt(o,{forceStdout:u.options.boolean("stdout"),mask:!u.options.boolean("no-mask")});if(s.target==="github_env"){F.success(`\u5DF2\u6CE8\u5165\u672C\u6B21 job \u7684\u540E\u7EED\u6B65\u9AA4\uFF08${s.injected.length} \u4E2A\u53D8\u91CF\uFF0C${s.masked.length} \u4E2A\u5DF2\u8131\u654F\uFF09`);return}au()||F.warn("\u5F53\u524D\u4E0D\u5728 GitHub Actions \u91CC\uFF0C\u53D8\u91CF\u6253\u5230 stdout\uFF08\u53EF\u4EE5 eval \u6216\u91CD\u5B9A\u5411\uFF09")}const cu=32;let X;function dt(){return X||(X=z.ready.then(()=>z)),X}function At(u,e){let t;try{t=u.from_base64(e.trim(),u.base64_variants.ORIGINAL)}catch{throw new Error("GitHub \u516C\u94A5\u89E3\u7801\u5931\u8D25\uFF1A\u5B83\u4E0D\u662F\u5408\u6CD5\u7684 base64\uFF08ORIGINAL \u53D8\u4F53\uFF09\u5B57\u7B26\u4E32\uFF1B\u8BF7\u786E\u8BA4\u4F20\u5165\u7684\u662F public-key \u63A5\u53E3\u8FD4\u56DE\u7684 key \u5B57\u6BB5\uFF0C\u800C\u4E0D\u662F key_id")}if(t.length!==cu)throw new Error(`GitHub \u516C\u94A5\u957F\u5EA6\u4E0D\u6B63\u786E\uFF1AX25519 \u516C\u94A5\u5FC5\u987B\u662F ${cu} \u5B57\u8282\uFF0C\u5B9E\u9645 ${t.length} \u5B57\u8282\u3002 \u8FD9\u79CD\u60C5\u51B5\u901A\u5E38\u662F key_id \u4E0E key \u4E24\u4E2A\u5B57\u6BB5\u5F04\u53CD\u4E86\uFF08\u6216\u628A key_id \u5F53\u6210\u4E86\u516C\u94A5\uFF09\uFF1B\u8BF7\u6539\u7528 public-key \u63A5\u53E3\u8FD4\u56DE\u7684 key \u5B57\u6BB5`);return t}async function wt(u,e){const t=await dt(),n=At(t,u),r=t.crypto_box_seal(t.from_string(e),n);return t.to_base64(r,t.base64_variants.ORIGINAL)}function Eu(u,e){const[t,n]=u.split("/",2),r=`https://api.github.com/repos/${encodeURIComponent(t)}/${encodeURIComponent(n)}`;return e?`${r}/environments/${encodeURIComponent(e)}/secrets`:`${r}/actions/secrets`}async function Dt(u,e,t){const n=await(await Z(`${Eu(u,t)}/public-key`,e)).json();if(!n.key_id||!n.key)throw new Error(`\u62FF\u4E0D\u5230 ${u} \u7684\u516C\u94A5\uFF08\u8FD4\u56DE\u4F53\u91CC\u6CA1\u6709 key_id/key\uFF09`);return{keyId:n.key_id,key:n.key}}async function Ct(u,e,t,n,r={}){const{keyId:o,key:s}=await Dt(u,e,r.env),i=await wt(s,n);return(await Z(`${Eu(u,r.env)}/${encodeURIComponent(t)}`,e,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({encrypted_value:i,key_id:o})})).status===201?"created":"updated"}const gt=48*1024;async function Bt(u,e){const[t]=e;if(!t)throw new Error("\u7528\u6CD5\uFF1Anpx initx env apply <store \u540D> --repo <owner/repo> [--env <\u73AF\u5883\u540D>] [--only A,B] [--dry-run]");const n=B(t),r=T(u.store,{token:u.options.string("token")}),o=new I(r.repo,r.token,r.branch),s=await v(o,r.path,n);if(!s)throw new Error(`\u4ED3\u5E93\u91CC\u6CA1\u6709 store "${n}"\u3002\u7528 npx initx env list \u770B\u770B\u6709\u54EA\u4E9B`);const i=s.store.targets?.secrets?.[0],c=u.options.string("repo")??i?.repo;if(!c)throw new Error("\u8981\u6307\u5B9A\u5199\u5165\u76EE\u6807\uFF1A--repo <owner/repo>\uFF0C\u6216\u5728 store \u91CC\u5199 targets.secrets");const l=u.options.string("env")??i?.env??null,a=u.options.string("only")?.split(",").map(A=>A.trim()).filter(Boolean),E=Object.entries(s.store.vars).filter(([A])=>!a||a.includes(A));if(!E.length)throw new Error(a?.length?`--only \u91CC\u6CA1\u6709\u4EFB\u4F55\u53D8\u91CF\u547D\u4E2D store "${n}"`:`store "${n}" \u91CC\u6CA1\u6709\u53D8\u91CF`);const f=[];for(const[A,O]of E){const $=Iu(A);if($){f.push(`${A} \u2014 \u540D\u5B57\u4E0D\u5408\u6CD5\uFF1A${$}`);continue}j.byteLength(O,"utf8")>gt&&f.push(`${A} \u2014 \u503C\u8D85\u8FC7 48 KB \u4E0A\u9650`)}if(f.length){F.error("\u8FD9\u4E9B\u53D8\u91CF\u4E0D\u80FD\u5199\u8FDB Actions secrets\uFF1A");for(const A of f)F.error(` ${A}`);if(!u.options.boolean("skip-invalid"))throw new Error(`\u6709 ${f.length} \u4E2A\u53D8\u91CF\u4E0D\u5408\u6CD5\uFF0C\u5DF2\u4E2D\u6B62\uFF08\u6539\u540D\u540E\u7528 --only \u6392\u9664\uFF0C\u6216\u52A0 --skip-invalid \u8DF3\u8FC7\uFF09`);F.warn("--skip-invalid\uFF1A\u8DF3\u8FC7\u8FD9\u4E9B\u53D8\u91CF")}const h=new Set(f.map(A=>A.split(" \u2014 ")[0])),d=E.filter(([A])=>!h.has(A));if(u.options.boolean("dry-run")){F.info(`--dry-run\uFF1A\u5C06\u5199\u5165 ${d.length} \u4E2A secret \u2192 ${c}${l?`\uFF08\u73AF\u5883 ${l}\uFF09`:"\uFF08\u4ED3\u5E93\u7EA7\uFF09"}`);for(const[A]of d)F.info(` ${A}`);return}F.info(`\u5199\u5165 ${c}${l?`\uFF08\u73AF\u5883 ${l}\uFF09`:"\uFF08\u4ED3\u5E93\u7EA7\uFF09"} \u2026`);const w=[];for(const[A,O]of d)try{const $=await Ct(c,r.token,A,O,{env:l});F.success(`${A}: ${$==="created"?"\u65B0\u5EFA":"\u66F4\u65B0"}`)}catch($){w.push(A),F.error(`${A}: \u5931\u8D25 \u2014 ${$.message}`)}if(w.length)throw new Error(`${w.length} \u4E2A secret \u5199\u5165\u5931\u8D25\uFF1A${w.join(", ")}`);F.success(`\u5B8C\u6210\uFF1A${d.length} \u4E2A secret \u2192 ${c}`),l&&F.info("\u73AF\u5883\u7EA7 secret \u9700\u8981\u8BE5 environment \u5DF2\u5B58\u5728\uFF0C\u4E14 job \u5F15\u7528\u5B83\u65F6\u624D\u53EF\u7528")}const k=["repo","token","branch","path"],lu="token \u4F1A\u4EE5**\u660E\u6587**\u5B58\u5728 ~/.initx/stores/@initx-plugin/env/store.json\uFF1B\u4ECB\u610F\u7684\u8BDD\u6539\u7528\u73AF\u5883\u53D8\u91CF INITX_ENV_TOKEN\uFF0C\u6216\u6BCF\u6B21\u7528 --token \u4F20\u3002";function J(u){return k.includes(u)}async function $t(u,e,t){const[n="status",...r]=e;switch(n){case"setup":return yt(u);case"set":return vt(u,r);case"get":return mt(u,r);case"list":case"ls":return kt(u);case"rm":case"remove":return bt(u,r);case"status":return xt(u,t);default:fu()}}async function yt(u){if(!p.stdin.isTTY)throw new Error(`setup \u9700\u8981\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u3002\u975E\u4EA4\u4E92\u73AF\u5883\u8BF7\u7528\uFF1A
23
+ `)}`);const o=await x.select("\u5199\u5230\u54EA\u4E2A\u6587\u4EF6",r);return[r[o]]}async function Ot(){return(await Uu(f.cwd(),{withFileTypes:!0})).filter(u=>u.isFile()&&_t.test(u.name)).map(u=>u.name).filter(u=>!Tt.some(t=>u.endsWith(t))).sort((u,t)=>{const e=n=>n===".env.local"?0:n===".env"?1:2;return e(u)-e(t)||u.localeCompare(t)})}async function Ut(u,t){const{config:e,client:n}=j(u),r=await yu(n,e.path,t[0]),o=await ou(n,e.path,r),i=o.store.vars,s=Object.keys(i);if(!s.length){l.warn(c(`store "${r}" \u91CC\u6CA1\u6709\u4EFB\u4F55\u53D8\u91CF`,"yellow"));return}const a=await St(u,o),E=u.options.boolean("dry-run");for(const F of a){const p=await ru(F)??"",d=wt(p,i);if(!d.changed){l.info(`${c(F,"blue")}: \u5DF2\u7ECF\u662F\u6700\u65B0\u7684\uFF08${s.length} \u4E2A\u53D8\u91CF\uFF0C\u65E0\u9700\u6539\u52A8\uFF09`);continue}if(E){const C=Bt(p,i);l.info(`${c(F,"blue")}\uFF08${c("--dry-run\uFF0C\u672A\u5199\u5165","yellow")}\uFF09\uFF1A`);for(const A of C.added)l.info(c(` + ${A}`,"green"));for(const A of C.updated)l.info(c(` ~ ${A}`,"yellow"));continue}const{backupPath:h}=await At(F,d.content);l.success(c(`${F}: \u65B0\u589E ${d.added.length} \xB7 \u66F4\u65B0 ${d.updated.length} \xB7 \u672A\u53D8 ${d.unchanged.length}`,"green")),h&&l.info(` \u65E7\u6587\u4EF6\u5907\u4EFD\u5728 ${c(h,"blue")}`),d.duplicates.length&&l.warn(c(` \u6CE8\u610F\uFF1A${d.duplicates.join(", ")} \u5728\u6587\u4EF6\u91CC\u51FA\u73B0\u591A\u6B21\uFF0C\u53EA\u66F4\u65B0\u4E86\u6700\u540E\u4E00\u884C`,"yellow"))}}function jt(u){const t={};for(const e of u){const n=e.indexOf("=");if(n<=0)throw new Error("\u53C2\u6570\u8981\u5199\u6210 KEY=value \u5F62\u5F0F");const r=e.slice(0,n).trim(),o=e.slice(n+1);if(!rt(r))throw new Error(`\u53D8\u91CF\u540D\u4E0D\u5408\u6CD5\uFF1A"${r}"`);t[r]=o}if(!Object.keys(t).length)throw new Error("\u81F3\u5C11\u7ED9\u4E00\u4E2A KEY=value");return t}async function Rt(u,t){const[e,...n]=t;if(!e)throw new Error("\u7528\u6CD5\uFF1Anpx initx env set <store \u540D> KEY=value [KEY2=value2 ...]");const r=N(e),o=jt(n),{config:i,client:s}=j(u),a=await U(s,i.path,r),E=a?.store??fu(),F=du(E,o);if(!F.added.length&&!F.updated.length){l.info(`store "${c(r,"blue")}" \u65E0\u9700\u6539\u52A8\uFF08${F.unchanged.length} \u4E2A\u53D8\u91CF\u503C\u76F8\u540C\uFF09`);return}await eu(s,i.path,r,E,`env: set ${Object.keys(o).join(", ")} in ${r}`,a?.version),l.success(c(`store "${r}" \u5DF2\u66F4\u65B0\uFF1A\u65B0\u589E ${F.added.length} \xB7 \u4FEE\u6539 ${F.updated.length}`,"green"));for(const p of F.added)l.info(c(` + ${p}`,"green"));for(const p of F.updated)l.info(c(` ~ ${p}`,"yellow"))}async function Gt(u,t){const[e]=t;if(!e)throw new Error("\u7528\u6CD5\uFF1Anpx initx env push <store \u540D> [--from .env.local] [--prune] [--force]");const n=N(e),r=u.options.string("from")??await Pt(),o=await ru(r);if(o===null)throw new Error(`\u627E\u4E0D\u5230\u6587\u4EF6 "${r}"\uFF08\u7528 --from \u6307\u5B9A\uFF09`);const i=Object.fromEntries(W(o).pairs);if(!Object.keys(i).length)throw new Error(`"${r}" \u91CC\u6CA1\u89E3\u6790\u51FA\u4EFB\u4F55 KEY=value`);const{config:s,client:a}=j(u),E=await U(a,s.path,n),F=E?.store??fu(),p=du(F,i),d=[];if(u.options.boolean("prune"))for(const C of Object.keys(F.vars))C in i||(delete F.vars[C],d.push(C));if(!p.added.length&&!p.updated.length&&!d.length){l.info(`store "${c(n,"blue")}" \u65E0\u9700\u6539\u52A8\uFF08${c(r,"blue")} \u91CC\u7684\u503C\u90FD\u4E00\u6837\uFF09`);return}const h=`env: push ${n} from ${r}`;try{await eu(a,s.path,n,F,h,E?.version)}catch(C){if(!Ju(C)||!u.options.boolean("force"))throw C;const A=await ou(a,s.path,n);l.warn(c("\u8FDC\u7AEF\u5DF2\u66F4\u65B0\uFF0C--force\uFF1A\u4EE5\u672C\u5730\u6587\u4EF6\u4E3A\u51C6\u91CD\u65B0\u5199\u5165","yellow")),await eu(a,s.path,n,F,h,A.version)}l.success(c(`store "${n}" \u5DF2\u66F4\u65B0\uFF1A\u65B0\u589E ${p.added.length} \xB7 \u4FEE\u6539 ${p.updated.length}${d.length?` \xB7 \u5220\u9664 ${d.length}`:""}`,"green"))}async function Pt(){for(const u of[".env.local",".env"])if(await ru(u)!==null)return u;throw new Error("\u5F53\u524D\u76EE\u5F55\u6CA1\u6709 .env.local / .env\uFF08\u7528 --from \u6307\u5B9A\u6587\u4EF6\uFF09")}async function Lt(u,t){const{config:e,client:n}=j(u),r=await yu(n,e.path,t[0]),o=(await ou(n,e.path,r)).store.vars;if(!Object.keys(o).length)throw new Error(`store "${r}" \u91CC\u6CA1\u6709\u4EFB\u4F55\u53D8\u91CF`);if(u.options.boolean("json")){console.log(JSON.stringify(o)),l.info(`\u5DF2\u8F93\u51FA ${Object.keys(o).length} \u4E2A\u53D8\u91CF\uFF08JSON\uFF09`);return}const i=kt(o,{forceStdout:u.options.boolean("stdout"),mask:!u.options.boolean("no-mask")});if(i.target==="github_env"){l.success(c(`\u5DF2\u6CE8\u5165\u672C\u6B21 job \u7684\u540E\u7EED\u6B65\u9AA4\uFF08${i.injected.length} \u4E2A\u53D8\u91CF\uFF0C${i.masked.length} \u4E2A\u5DF2\u8131\u654F\uFF09`,"green"));return}Du()||l.warn(c("\u5F53\u524D\u4E0D\u5728 GitHub Actions \u91CC\uFF0C\u53D8\u91CF\u6253\u5230 stdout\uFF08\u53EF\u4EE5 eval \u6216\u91CD\u5B9A\u5411\uFF09","yellow"))}const mu=32;let iu;function Vt(){return iu||(iu=lu.ready.then(()=>lu)),iu}function Ht(u,t){let e;try{e=u.from_base64(t.trim(),u.base64_variants.ORIGINAL)}catch{throw new Error("GitHub \u516C\u94A5\u89E3\u7801\u5931\u8D25\uFF1A\u5B83\u4E0D\u662F\u5408\u6CD5\u7684 base64\uFF08ORIGINAL \u53D8\u4F53\uFF09\u5B57\u7B26\u4E32\uFF1B\u8BF7\u786E\u8BA4\u4F20\u5165\u7684\u662F public-key \u63A5\u53E3\u8FD4\u56DE\u7684 key \u5B57\u6BB5\uFF0C\u800C\u4E0D\u662F key_id")}if(e.length!==mu)throw new Error(`GitHub \u516C\u94A5\u957F\u5EA6\u4E0D\u6B63\u786E\uFF1AX25519 \u516C\u94A5\u5FC5\u987B\u662F ${mu} \u5B57\u8282\uFF0C\u5B9E\u9645 ${e.length} \u5B57\u8282\u3002 \u8FD9\u79CD\u60C5\u51B5\u901A\u5E38\u662F key_id \u4E0E key \u4E24\u4E2A\u5B57\u6BB5\u5F04\u53CD\u4E86\uFF08\u6216\u628A key_id \u5F53\u6210\u4E86\u516C\u94A5\uFF09\uFF1B\u8BF7\u6539\u7528 public-key \u63A5\u53E3\u8FD4\u56DE\u7684 key \u5B57\u6BB5`);return e}async function Kt(u,t){const e=await Vt(),n=Ht(e,u),r=e.crypto_box_seal(e.from_string(t),n);return e.to_base64(r,e.base64_variants.ORIGINAL)}async function $u(u,t,e={}){const n=await D(u,{...e,headers:{...pu(t),...e.headers??{}}});return m(n),n}function vu(u,t){const[e,n]=u.split("/",2),r=`https://api.github.com/repos/${encodeURIComponent(e)}/${encodeURIComponent(n)}`;return t?`${r}/environments/${encodeURIComponent(t)}/secrets`:`${r}/actions/secrets`}async function Xt(u,t,e){const n=await $u(`${vu(u,e)}/public-key`,t),r=await k(n);if(!r.key_id||!r.key)throw new Error(`\u62FF\u4E0D\u5230 ${u} \u7684\u516C\u94A5\uFF08\u8FD4\u56DE\u4F53\u91CC\u6CA1\u6709 key_id/key\uFF09`);return{keyId:r.key_id,key:r.key}}async function Jt(u,t,e,n,r={}){const{keyId:o,key:i}=await Xt(u,t,r.env),s=await Kt(i,n);return(await $u(`${vu(u,r.env)}/${encodeURIComponent(e)}`,t,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({encrypted_value:s,key_id:o})})).status===201?"created":"updated"}function Yt(u,t,e=f.env){const n=t||e.INITX_ENV_TARGET_TOKEN||(u.type==="github"&&u.apiBaseUrl===b.github?u.token:"");if(!n)throw new Error("GitHub Actions \u76EE\u6807\u9700\u8981\u72EC\u7ACB\u51ED\u636E\uFF1A--target-token \u6216 INITX_ENV_TARGET_TOKEN");return{write(r,o,i,s){return T("github",r),Jt(r,n,o,i,{env:s})}}}const Wt=48*1024;async function Mt(u,t){const[e]=t;if(!e)throw new Error("\u7528\u6CD5\uFF1Anpx initx env apply <store \u540D> --repo <owner/repo> [--env <\u73AF\u5883\u540D>] [--only A,B] [--dry-run]");const n=N(e),r=X(u.store,{token:u.options.string("token"),config:P(u.options)}),o=J(r),i=Yt(r,u.options.string("target-token")),s=await U(o,r.path,n);if(!s)throw new Error(`\u4ED3\u5E93\u91CC\u6CA1\u6709 store "${n}"\u3002\u7528 npx initx env list \u770B\u770B\u6709\u54EA\u4E9B`);const a=s.store.targets?.secrets?.[0],E=u.options.string("repo")??a?.repo;if(!E)throw new Error("\u8981\u6307\u5B9A\u5199\u5165\u76EE\u6807\uFF1A--repo <owner/repo>\uFF0C\u6216\u5728 store \u91CC\u5199 targets.secrets");T("github",E);const F=u.options.string("env")??a?.env??null,p=u.options.string("only")?.split(",").map(w=>w.trim()).filter(Boolean),d=Object.entries(s.store.vars).filter(([w])=>!p||p.includes(w));if(!d.length)throw new Error(p?.length?`--only \u91CC\u6CA1\u6709\u4EFB\u4F55\u53D8\u91CF\u547D\u4E2D store "${n}"`:`store "${n}" \u91CC\u6CA1\u6709\u53D8\u91CF`);const h=[];for(const[w,I]of d){const v=nt(w);if(v){h.push(`${w} \u2014 \u540D\u5B57\u4E0D\u5408\u6CD5\uFF1A${v}`);continue}S.byteLength(I,"utf8")>Wt&&h.push(`${w} \u2014 \u503C\u8D85\u8FC7 48 KB \u4E0A\u9650`)}if(h.length){l.error(c("\u8FD9\u4E9B\u53D8\u91CF\u4E0D\u80FD\u5199\u8FDB Actions secrets\uFF1A","red"));for(const w of h)l.error(c(` ${w}`,"red"));if(!u.options.boolean("skip-invalid"))throw new Error(`\u6709 ${h.length} \u4E2A\u53D8\u91CF\u4E0D\u5408\u6CD5\uFF0C\u5DF2\u4E2D\u6B62\uFF08\u6539\u540D\u540E\u7528 --only \u6392\u9664\uFF0C\u6216\u52A0 --skip-invalid \u8DF3\u8FC7\uFF09`);l.warn(c("--skip-invalid\uFF1A\u8DF3\u8FC7\u8FD9\u4E9B\u53D8\u91CF","yellow"))}const C=new Set(h.map(w=>w.split(" \u2014 ")[0])),A=d.filter(([w])=>!C.has(w));if(u.options.boolean("dry-run")){l.info(`${c("--dry-run","yellow")}\uFF1A\u5C06\u5199\u5165 ${A.length} \u4E2A secret \u2192 ${c(E,"blue")}${F?`\uFF08\u73AF\u5883 ${c(F,"blue")}\uFF09`:"\uFF08\u4ED3\u5E93\u7EA7\uFF09"}`);for(const[w]of A)l.info(` ${c(w,"blue")}`);return}l.info(`\u5199\u5165 ${c(E,"blue")}${F?`\uFF08\u73AF\u5883 ${c(F,"blue")}\uFF09`:"\uFF08\u4ED3\u5E93\u7EA7\uFF09"} \u2026`);const $=[];for(const[w,I]of A)try{const v=await i.write(E,w,I,F);l.success(c(`${w}: ${v==="created"?"\u65B0\u5EFA":"\u66F4\u65B0"}`,"green"))}catch(v){$.push(w),l.error(c(`${w}: \u5931\u8D25 \u2014 ${v.message}`,"red"))}if($.length)throw new Error(`${$.length} \u4E2A secret \u5199\u5165\u5931\u8D25\uFF1A${$.join(", ")}`);l.success(c(`\u5B8C\u6210\uFF1A${A.length} \u4E2A secret \u2192 ${E}`,"green")),F&&l.info("\u73AF\u5883\u7EA7 secret \u9700\u8981\u8BE5 environment \u5DF2\u5B58\u5728\uFF0C\u4E14 job \u5F15\u7528\u5B83\u65F6\u624D\u53EF\u7528")}const R=["type","apiBaseUrl","repo","token","branch","path"],bu="token \u4F1A\u4EE5**\u660E\u6587**\u5B58\u5728 ~/.initx/stores/@initx-plugin/env/store.json\uFF1B\u4ECB\u610F\u7684\u8BDD\u6539\u7528\u73AF\u5883\u53D8\u91CF INITX_ENV_TOKEN\uFF0C\u6216\u6BCF\u6B21\u7528 --token \u4F20\u3002";function su(u){return R.includes(u)}async function zt(u,t,e){const[n="status",...r]=t;switch(n){case"setup":return qt(u);case"set":return G(u,r);case"get":return Zt(u,r);case"list":case"ls":return Qt(u);case"rm":case"remove":return ue(u,r);case"status":return te(u,e);default:xu()}}async function qt(u){if(!f.stdin.isTTY)throw new Error(`setup \u9700\u8981\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u3002\u975E\u4EA4\u4E92\u73AF\u5883\u8BF7\u7528\uFF1A
26
24
  npx initx env-config set repo <owner/repo>
27
- npx initx env-config set token <pat>`);const e=await g.input("vault \u4ED3\u5E93\uFF08owner/repo\uFF09",{default:u.repo,required:!0,pattern:/^[^/\s]+\/[^/\s]+$/,patternError:"\u8981\u5199\u6210 owner/repo \u5F62\u5F0F"});u.repo=e;const t=await g.password("GitHub token\uFF08\u56DE\u8F66\u4FDD\u7559\u5F53\u524D\u503C\uFF09",{mask:"*"});t&&(u.token=t);const n=await g.input("\u5206\u652F",{default:u.branch||b});u.branch=n||b;const r=await g.input("store \u76EE\u5F55",{default:u.path||x});u.path=r||x,F.success(`\u5DF2\u4FDD\u5B58\uFF1A${u.repo}\uFF08\u5206\u652F ${u.branch}\uFF0C\u76EE\u5F55 ${u.path}\uFF09`),F.warn(lu)}function vt(u,e){const[t,...n]=e;if(!t||!J(t))throw new Error(`\u7528\u6CD5\uFF1Anpx initx env-config set <${k.join("|")}> <\u503C>`);const r=n.join(" ").trim();if(!r)throw new Error(`${t} \u7684\u503C\u4E0D\u80FD\u4E3A\u7A7A`);if(t==="repo"&&!/^[^/\s]+\/[^/\s]+$/.test(r))throw new Error("repo \u8981\u5199\u6210 owner/repo \u5F62\u5F0F");if(u[t]=r,t==="token"){F.success(`token \u5DF2\u4FDD\u5B58\uFF1A${N(r)}`),F.warn(lu);return}F.success(`${t} = ${r}`)}function mt(u,e){const[t]=e;if(!t||!J(t))throw new Error(`\u7528\u6CD5\uFF1Anpx initx env-config get <${k.join("|")}>`);F.info(`${t} = ${t==="token"?N(u.token??""):u[t]??"(\u672A\u8BBE\u7F6E)"}`)}function kt(u){for(const e of k)F.info(`${e}: ${e==="token"?N(u.token??""):u[e]??"(\u672A\u8BBE\u7F6E)"}`)}function bt(u,e){const[t]=e;if(!t||!J(t))throw new Error(`\u7528\u6CD5\uFF1Anpx initx env-config rm <${k.join("|")}>`);delete u[t],F.success(`\u5DF2\u6E05\u9664 ${t}`)}async function xt(u,e){let t;try{t=T(u,{token:e.string("token")})}catch(n){F.warn(n.message);return}if(F.info(`vault \u4ED3\u5E93: ${t.repo}`),F.info(`\u5206\u652F: ${t.branch}`),F.info(`store \u76EE\u5F55: ${t.path}`),F.info(`token: ${N(t.token)}\uFF08\u6765\u81EA ${t.tokenSource}\uFF09`),e.boolean("offline")){F.info("\uFF08--offline\uFF1A\u8DF3\u8FC7\u8FDE\u901A\u6027\u68C0\u67E5\uFF09");return}try{const n=new I(t.repo,t.token,t.branch),r=await R(n,t.path);F.success(`\u8FDE\u901A\u6B63\u5E38\uFF0C${t.path}/ \u4E0B\u6709 ${r.length} \u4E2A store`)}catch(n){F.error(`\u8FDE\u4E0D\u4E0A\u6216\u6CA1\u6743\u9650\uFF1A${n.message}`),F.warn("\u68C0\u67E5\uFF1A\u4ED3\u5E93\u540D\u5BF9\u4E0D\u5BF9\u3001token \u6709\u6CA1\u6709 Contents \u8BFB\u5199\u6743\u9650\u3001\u5206\u652F\u662F\u5426\u5B58\u5728")}}function Tt(){fu()}function fu(){console.log(`
25
+ npx initx env-config set token <pat>`);const t=O(u),e=await x.input(`vault \u4ED3\u5E93\uFF08${t.type}\uFF09`,{default:M(u,"repo"),required:!0});G(u,["repo",e]);const n=await x.password(`${t.type} token\uFF08\u56DE\u8F66\u4FDD\u7559\u5F53\u524D\u503C\uFF09`,{mask:"*"});n&&G(u,["token",n]);const r=await x.input("\u5206\u652F",{default:M(u,"branch")||V});G(u,["branch",r||V]);const o=await x.input("store \u76EE\u5F55",{default:M(u,"path")||H});G(u,["path",o||H]),l.success(c("\u672C\u5730 vault \u914D\u7F6E\u5DF2\u4FDD\u5B58","green")),l.warn(c(bu,"yellow"))}function G(u,t){const[e,...n]=t;if(!e||!su(e))throw new Error(`\u7528\u6CD5\uFF1Anpx initx env-config set <${R.join("|")}> <\u503C>`);const r=n.join(" ").trim();if(!r)throw new Error(`${e} \u7684\u503C\u4E0D\u80FD\u4E3A\u7A7A`);const o=O(u);if(e==="type"||e==="apiBaseUrl"){if(e==="type"&&r!=="github"&&r!=="gitlab")throw new Error("type \u5FC5\u987B\u662F github \u6216 gitlab");const i=e==="type"?r:o.type,s=e==="apiBaseUrl"?_(r):i===o.type?o.apiBaseUrl:b[i],a=o.type===i&&o.apiBaseUrl===s;u.storage={...a?u.storage||{repo:u.repo,branch:u.branch,path:u.path}:{},type:i,apiBaseUrl:s}}else e==="token"?u.storage?(u.credentials??={})[K(o.type,o.apiBaseUrl)]=r:u.token=r:(e==="repo"&&T(o.type,r),e==="path"&&y(r),u.storage?u.storage[e]=r:u[e]=r);if(e==="token"){l.success(c(`token \u5DF2\u4FDD\u5B58\uFF1A${Q(r)}`,"green")),l.warn(c(bu,"yellow"));return}l.success(`${e} = ${c(r,"green")}`)}function Zt(u,t){const[e]=t;if(!e||!su(e))throw new Error(`\u7528\u6CD5\uFF1Anpx initx env-config get <${R.join("|")}>`);l.info(`${e} = ${c(ku(u,e),"blue")}`)}function Qt(u){for(const t of R)l.info(`${t}: ${c(ku(u,t),"blue")}`)}function ue(u,t){const[e]=t;if(!e||!su(e))throw new Error(`\u7528\u6CD5\uFF1Anpx initx env-config rm <${R.join("|")}>`);if(e==="token"){const n=O(u);delete u.credentials?.[K(n.type,n.apiBaseUrl)],n.type==="github"&&n.apiBaseUrl===b.github&&delete u.token}else e==="type"||e==="apiBaseUrl"?delete u.storage:u.storage?delete u.storage[e]:delete u[e];l.success(c(`\u5DF2\u6E05\u9664 ${e}`,"green"))}async function te(u,t){const e=P(t);let n;try{n=X(u,{token:t.string("token"),config:e})}catch(i){if(i instanceof L)throw i;l.warn(c(i.message,"yellow"));return}if(l.info(`\u5B58\u50A8\u540E\u7AEF: ${c(n.type,"blue")}`),l.info(`API \u5730\u5740: ${c(n.apiBaseUrl,"blue")}`),l.info(`\u914D\u7F6E\u6765\u6E90: ${c(n.configSource,"blue")}`),l.info(`\u5B57\u6BB5\u6765\u6E90: ${c(JSON.stringify(n.sources),"gray")}`),l.info(`vault \u4ED3\u5E93: ${c(n.repo,"blue")}`),l.info(`\u5206\u652F: ${c(n.branch,"blue")}`),l.info(`store \u76EE\u5F55: ${c(n.path,"blue")}`),l.info(`token: ${c(Q(n.token),"yellow")}\uFF08\u6765\u81EA ${n.tokenSource}\uFF09`),t.boolean("offline")){l.info(c("\uFF08--offline\uFF1A\u8DF3\u8FC7\u8FDE\u901A\u6027\u68C0\u67E5\uFF09","gray"));return}const r=J(n),o=await tu(r,n.path);l.success(c(`\u8FDE\u901A\u6B63\u5E38\uFF0C${n.path}/ \u4E0B\u6709 ${o.length} \u4E2A store`,"green"))}function M(u,t){const e=O(u);return t==="type"||t==="apiBaseUrl"?e[t]:t==="token"?u.credentials?.[K(e.type,e.apiBaseUrl)]||(e.type==="github"&&e.apiBaseUrl===b.github?u.token:void 0):u.storage?u.storage[t]:u[t]}function ku(u,t){const e=M(u,t);return t==="token"?Q(e||""):e??"(\u672A\u8BBE\u7F6E)"}function ee(){xu()}function xu(){console.log(`
28
26
  \u7528\u6CD5\uFF1Anpx initx env-config <\u547D\u4EE4>
29
27
 
30
- setup \u4EA4\u4E92\u5F0F\u914D\u7F6E\uFF08repo / token / branch / path\uFF09
31
- set <key> <value> \u8BBE\u7F6E\u5355\u9879\uFF0Ckey \u53D6 repo | token | branch | path
28
+ setup \u4EA4\u4E92\u5F0F\u914D\u7F6E\u5F53\u524D\u672C\u5730\u540E\u7AEF\uFF08repo / token / branch / path\uFF09
29
+ set <key> <value> \u8BBE\u7F6E\u5355\u9879\uFF0Ckey \u53D6 type | apiBaseUrl | repo | token | branch | path
32
30
  get <key> \u67E5\u770B\u6301\u4E45\u5316\u5355\u9879\uFF08token \u4F1A\u8131\u654F\uFF09
33
31
  list \u67E5\u770B\u5168\u90E8\u6301\u4E45\u5316\u914D\u7F6E\uFF08token \u4F1A\u8131\u654F\uFF09
34
32
  rm <key> \u6E05\u9664\u5355\u9879
35
- status [--offline] \u67E5\u770B\u5B9E\u9645\u751F\u6548\u914D\u7F6E + \u8FDE\u901A\u6027\u68C0\u67E5
33
+ status [--config <file>] [--offline] \u67E5\u770B\u5B9E\u9645\u751F\u6548\u914D\u7F6E\u3001\u6765\u6E90 + \u8FDE\u901A\u6027\u68C0\u67E5
36
34
 
37
- \u89E3\u6790\u4F18\u5148\u7EA7\uFF1A\u663E\u5F0F override > INITX_ENV_REPO / INITX_ENV_BRANCH / INITX_ENV_PATH > \u6301\u4E45\u5316\u914D\u7F6E > \u9ED8\u8BA4\u503C\u3002
38
- Token\uFF1A--token > INITX_ENV_TOKEN > GH_TOKEN > GITHUB_TOKEN > \u6301\u4E45\u5316\u914D\u7F6E\u3002
35
+ \u89E3\u6790\u4F18\u5148\u7EA7\uFF1A\u663E\u5F0F override > INITX_ENV_* > \u9879\u76EE .initx-env.json > \u540C\u540E\u7AEF/API \u7684\u6301\u4E45\u5316\u914D\u7F6E > \u9ED8\u8BA4\u503C\u3002
36
+ Token\uFF1A--token > INITX_ENV_TOKEN > GH_TOKEN / GITHUB_TOKEN\uFF08\u4EC5 GitHub \u9ED8\u8BA4 API\uFF09> \u5F53\u524D API \u7ED1\u5B9A\u7684\u6301\u4E45\u5316\u51ED\u636E\u3002
37
+ get/list/set/rm \u4EC5\u64CD\u4F5C\u672C\u5730\u914D\u7F6E\uFF1B\u5207\u6362 type / apiBaseUrl \u4F1A\u6E05\u9664\u65E7\u76EE\u6807\u5B9A\u4F4D\uFF0C\u51ED\u636E\u4ECD\u7ED1\u5B9A\u539F API\u3002
38
+ \u9879\u76EE\u6587\u4EF6 schema 1\uFF0C\u4EC5\u53EF\u5B58 storage.type/apiBaseUrl/repo/branch/path\uFF0C\u7981\u6B62 token \u4E0E\u4E1A\u52A1\u53D8\u91CF\u3002
39
39
 
40
40
  \u793A\u4F8B\uFF1A
41
41
  npx initx env-config set repo imba97/env-vault
42
42
  npx initx env-config set token github_pat_xxx
43
43
  npx initx env-config set path stores
44
44
  npx initx env-config status
45
- `)}function pu(u){return 128+(gu.signals[u]??1)}function Nt(u,e,t){return new Promise(n=>{let r,o,s,i=!1;const c=h=>{const d=h.code;return{code:d==="ENOENT"?127:d==="EACCES"?126:1,errorCode:d??"UNKNOWN"}},l=h=>{i||(i=!0,clearTimeout(o),p.removeListener("SIGINT",E),p.removeListener("SIGTERM",f),n(h))},a=h=>{if(!(i||s||r.exitCode!==null||r.signalCode!==null)){s=h;try{r.kill(h)}catch{}o=setTimeout(()=>{try{r.kill("SIGKILL")}catch{}finally{r.unref(),l({code:pu(h),signal:h})}},5e3)}};function E(){a("SIGINT")}function f(){a("SIGTERM")}try{r=Bu(u,e,{env:t,stdio:"inherit",shell:!1})}catch(h){l(c(h));return}p.on("SIGINT",E),p.on("SIGTERM",f),r.once("error",h=>l(c(h))),r.once("close",(h,d)=>{const w=s??d??void 0;l(w?{code:pu(w),signal:w}:{code:h??1})})})}function It(u){if(p.exitCode=u.code,u.signal&&p.platform!=="win32"){const e=u.signal;p.once("beforeExit",()=>{try{p.kill(p.pid,e)}catch{p.exitCode=u.code}})}}const hu="\u7528\u6CD5\uFF1Ainitx env run <store> [--token <token>] -- <command> [...args]";function _t(u,e){if(u.length!==1||!u[0])throw new Error(hu);const t=B(u[0]);if(!Array.isArray(e)||!e.every(n=>typeof n=="string")||!e[0]?.trim())throw new Error(hu);if(e.some(n=>n.includes("\0")))throw new Error("\u547D\u4EE4\u6216\u53C2\u6570\u4E0D\u80FD\u5305\u542B NUL");return{name:t,command:e[0],args:e.slice(1)}}function Ot(u,e=p.env,t=p.platform){const n={...e},r=new Set;for(const[o,s]of Object.entries(u)){if(!/^[A-Z_]\w*$/i.test(o))throw new Error(`\u53D8\u91CF\u540D ${JSON.stringify(o)} \u4E0D\u5408\u6CD5\uFF1A\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\uFF0C\u4E0D\u80FD\u4EE5\u6570\u5B57\u5F00\u5934`);if(typeof s!="string"||s.includes("\0"))throw new Error(`\u53D8\u91CF ${JSON.stringify(o)} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u4E14\u4E0D\u80FD\u5305\u542B NUL`);const i=t==="win32"?o.toUpperCase():o;if(r.has(i))throw new Error(`\u53D8\u91CF ${JSON.stringify(o)} \u5728 Windows \u4E0A\u5B58\u5728\u5927\u5C0F\u5199\u51B2\u7A81`);r.add(i)}if(t==="win32"){const o=new Set;for(const s of Object.keys(n)){const i=s.toUpperCase();r.has(i)||o.has(i)?delete n[s]:o.add(i)}}return{...n,...u}}async function jt(u,e){let t="\u53C2\u6570";try{const n=_t(e,u.options.raw["--"]),r=new Set(["--","token"]);if(Object.keys(u.options.raw).some(E=>!r.has(E)))throw new Error("run \u53EA\u652F\u6301 --token\uFF1B\u5B50\u8FDB\u7A0B\u53C2\u6570\u5FC5\u987B\u653E\u5728 -- \u540E");const o=u.options.raw.token;if(o!==void 0&&typeof o!="string"&&typeof o!="number")throw new Error("--token \u9700\u8981\u5355\u4E2A\u503C");t="\u914D\u7F6E";const s=T(u.store,{token:u.options.string("token")});t="\u8BFB\u53D6 store";const i=new I(s.repo,s.token,s.branch),c=await v(i,s.path,n.name);if(!c)throw new Error("store \u4E0D\u5B58\u5728");t="\u53D8\u91CF\u6821\u9A8C";const l=Ot(c.store.vars);t="\u542F\u52A8\u5B50\u8FDB\u7A0B";const a=await Nt(n.command,n.args,l);a.errorCode&&p.stderr.write(`env run: \u542F\u52A8\u5931\u8D25\uFF08${a.errorCode}\uFF09
46
- `),It(a)}catch(n){const r=t==="\u53D8\u91CF\u6821\u9A8C"||t==="\u53C2\u6570"?`\uFF1A${n.message}`:"";p.stderr.write(`env run: ${t}\u5931\u8D25${r}
47
- `),p.exitCode=1}}const St=["list","ls","pull","set","push","setup","apply","run","help"],Gt=["setup","set","get","list","ls","rm","remove","status","help"];class Ht extends du{defaultStore={branch:b,path:x};rules=[{matching:"env",description:"Env vars from a private repo: env list | pull | set | push | setup | apply | run",optional:[void 0,...St]},{matching:"env-config",description:"Configure the vault repo (repo / token / branch / path) and check connectivity",optional:[void 0,...Gt]}];async handle(e,...t){const n=t.filter(c=>typeof c=="string"),r=yu(e.cliOptions,e.optionsList),o=e.store??{};if(e.key==="env-config"){if(n[0]==="help"||r.boolean("help")){Tt();return}await $t(o,n,r);return}if(n[0]==="run"&&!r.boolean("help")&&!r.boolean("h")){await jt({store:o,options:r},n.slice(1));return}if(!n.length||n[0]==="help"||r.boolean("help")||r.boolean("h")){Ut();return}const[s,...i]=n;if(s==="apply"){await Bt({store:o,options:r},i);return}await st({store:o,options:r},[s,...i])}}function Ut(){console.log(`
45
+ npx initx env-config set type gitlab
46
+ npx initx env-config set apiBaseUrl https://gitlab.example.com/api/v4
47
+ npx initx env-config set repo group/subgroup/vault
48
+ `)}function _u(u){return 128+(Lu.signals[u]??1)}function ne(u,t,e){return new Promise(n=>{let r,o,i,s=!1;const a=h=>{const C=h.code;return{code:C==="ENOENT"?127:C==="EACCES"?126:1,errorCode:C??"UNKNOWN"}},E=h=>{s||(s=!0,clearTimeout(o),f.removeListener("SIGINT",p),f.removeListener("SIGTERM",d),n(h))},F=h=>{if(!(s||i||r.exitCode!==null||r.signalCode!==null)){i=h;try{r.kill(h)}catch{}o=setTimeout(()=>{try{r.kill("SIGKILL")}catch{}finally{r.unref(),E({code:_u(h),signal:h})}},5e3)}};function p(){F("SIGINT")}function d(){F("SIGTERM")}try{r=Vu(u,t,{env:e,stdio:"inherit",shell:!1})}catch(h){E(a(h));return}f.on("SIGINT",p),f.on("SIGTERM",d),r.once("error",h=>E(a(h))),r.once("close",(h,C)=>{const A=i??C??void 0;E(A?{code:_u(A),signal:A}:{code:h??1})})})}function re(u){if(f.exitCode=u.code,u.signal&&f.platform!=="win32"){const t=u.signal;f.once("beforeExit",()=>{try{f.kill(f.pid,t)}catch{f.exitCode=u.code}})}}const Tu="\u7528\u6CD5\uFF1Ainitx env run <store> [--config <file>] [--token <token>] -- <command> [...args]";function oe(u,t){if(u.length!==1||!u[0])throw new Error(Tu);const e=N(u[0]);if(!Array.isArray(t)||!t.every(n=>typeof n=="string")||!t[0]?.trim())throw new Error(Tu);if(t.some(n=>n.includes("\0")))throw new Error("\u547D\u4EE4\u6216\u53C2\u6570\u4E0D\u80FD\u5305\u542B NUL");return{name:e,command:t[0],args:t.slice(1)}}function ie(u,t=f.env,e=f.platform){const n={...t},r=new Set;for(const[o,i]of Object.entries(u)){if(!/^[A-Z_]\w*$/i.test(o))throw new Error(`\u53D8\u91CF\u540D ${JSON.stringify(o)} \u4E0D\u5408\u6CD5\uFF1A\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\uFF0C\u4E0D\u80FD\u4EE5\u6570\u5B57\u5F00\u5934`);if(typeof i!="string"||i.includes("\0"))throw new Error(`\u53D8\u91CF ${JSON.stringify(o)} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u4E14\u4E0D\u80FD\u5305\u542B NUL`);const s=e==="win32"?o.toUpperCase():o;if(r.has(s))throw new Error(`\u53D8\u91CF ${JSON.stringify(o)} \u5728 Windows \u4E0A\u5B58\u5728\u5927\u5C0F\u5199\u51B2\u7A81`);r.add(s)}if(e==="win32"){const o=new Set;for(const i of Object.keys(n)){const s=i.toUpperCase();r.has(s)||o.has(s)?delete n[i]:o.add(s)}}return{...n,...u}}async function se(u,t){let e="\u53C2\u6570";try{const n=oe(t,u.options.raw["--"]),r=new Set(["--","token","config"]);if(Object.keys(u.options.raw).some(d=>!r.has(d)))throw new Error("run \u53EA\u652F\u6301 --token / --config\uFF1B\u5B50\u8FDB\u7A0B\u53C2\u6570\u5FC5\u987B\u653E\u5728 -- \u540E");const o=u.options.raw.token;if(o!==void 0&&typeof o!="string"&&typeof o!="number")throw new Error("--token \u9700\u8981\u5355\u4E2A\u503C");const i=u.options.raw.config;if(i!==void 0&&(typeof i!="string"||!i))throw new Error("--config \u9700\u8981\u5355\u4E2A\u6587\u4EF6\u8DEF\u5F84");e="\u914D\u7F6E";const s=X(u.store,{token:u.options.string("token"),config:P(u.options)});e="\u8BFB\u53D6 store";const a=J(s),E=await U(a,s.path,n.name);if(!E)throw new Error("store \u4E0D\u5B58\u5728");e="\u53D8\u91CF\u6821\u9A8C";const F=ie(E.store.vars);e="\u542F\u52A8\u5B50\u8FDB\u7A0B";const p=await ne(n.command,n.args,F);p.errorCode&&f.stderr.write(`${c(`env run: \u542F\u52A8\u5931\u8D25\uFF08${p.errorCode}\uFF09`,"red","stderr")}
49
+ `),re(p)}catch(n){const r=e==="\u53D8\u91CF\u6821\u9A8C"||e==="\u53C2\u6570"?`\uFF1A${n.message}`:"";f.stderr.write(`${c(`env run: ${e}\u5931\u8D25${r}`,"red","stderr")}
50
+ `),f.exitCode=1}}const ae=["list","ls","pull","set","push","setup","apply","run","help"],ce=["setup","set","get","list","ls","rm","remove","status","help"];class Fe extends Iu{defaultStore={branch:V,path:H};rules=[{matching:"env",description:"Env vars from a private repo: env list | pull | set | push | setup | apply | run",optional:[void 0,...ae]},{matching:"env-config",description:"Configure GitHub/GitLab vault storage and check connectivity",optional:[void 0,...ce]}];async handle(t,...e){try{await this.dispatch(t,...e)}catch(n){xt(n)}}async dispatch(t,...e){const n=e.filter(a=>typeof a=="string"),r=Ku(t.cliOptions,t.optionsList),o=t.store??{};if(t.key==="env-config"){if(n[0]==="help"||r.boolean("help")){ee();return}await zt(o,n,r);return}if(n[0]==="run"&&!r.boolean("help")&&!r.boolean("h")){await se({store:o,options:r},n.slice(1));return}if(!n.length||n[0]==="help"||r.boolean("help")||r.boolean("h")){le();return}const[i,...s]=n;if(i==="apply"){await Mt({store:o,options:r},s);return}await Nt({store:o,options:r},[i,...s])}}function le(){console.log(`
48
51
  \u7528\u6CD5\uFF1Anpx initx env <\u547D\u4EE4>
49
52
 
50
53
  list \u5217\u51FA vault \u4ED3\u5E93\u91CC\u6240\u6709 store
@@ -60,6 +63,7 @@ Token\uFF1A--token > INITX_ENV_TOKEN > GH_TOKEN > GITHUB_TOKEN > \u6301\u4E45\u5
60
63
  GitHub Actions \u91CC\u5199 $GITHUB_ENV\uFF08\u540E\u7EED step \u53EF\u89C1\uFF09\u5E76\u81EA\u52A8\u8131\u654F
61
64
  \u672C\u5730\u5219\u6253\u5230 stdout\uFF08--stdout \u5F3A\u5236\uFF1B--json \u8F93\u51FA JSON\uFF09
62
65
  apply <store> --repo o/r \u628A store \u5199\u8FDB\u67D0\u4E2A\u4ED3\u5E93\u7684 Actions secrets
66
+ --target-token <pat> \u72EC\u7ACB GitHub \u76EE\u6807\u51ED\u636E
63
67
  --env <\u73AF\u5883\u540D> \u5199\u73AF\u5883\u7EA7 secret
64
68
  --only A,B \u53EA\u5199\u8FD9\u51E0\u4E2A
65
69
  --dry-run \u53EA\u5217\u51FA\u5C06\u5199\u5165\u7684\u540D\u5B57
@@ -67,6 +71,11 @@ Token\uFF1A--token > INITX_ENV_TOKEN > GH_TOKEN > GITHUB_TOKEN > \u6301\u4E45\u5
67
71
  run <store> -- cmd [args] \u76F4\u63A5\u6CE8\u5165\u5B50\u8FDB\u7A0B\uFF0C\u7EE7\u627F stdio / cwd\uFF0C\u4F20\u9012\u9000\u51FA\u72B6\u6001
68
72
  --token <pat> \u4E34\u65F6\u51ED\u636E\uFF08CI \u63A8\u8350 INITX_ENV_TOKEN\uFF09
69
73
 
74
+ \u5168\u90E8\u5B58\u50A8\u547D\u4EE4\u652F\u6301 --config <file>\uFF08\u76F8\u5BF9\u8C03\u7528\u8005 cwd\uFF1B\u9ED8\u8BA4\u53EA\u8BFB cwd \u7684 .initx-env.json\uFF09\u3002
75
+ run \u7684 --config / --token \u653E\u5728 -- \u524D\uFF1B\u4E4B\u540E\u7684\u5168\u90E8\u53C2\u6570\u539F\u6837\u900F\u4F20\u5B50\u8FDB\u7A0B\u3002
76
+ \u652F\u6301 GitHub / GitLab vault\uFF1Bapply \u7684 --repo \u59CB\u7EC8\u662F GitHub Actions \u76EE\u6807\u3002
77
+ \u8DE8\u5E73\u53F0 apply \u5FC5\u987B\u8BBE\u7F6E --target-token \u6216 INITX_ENV_TARGET_TOKEN\u3002
78
+
70
79
  \u914D\u7F6E vault \u4ED3\u5E93\u7528\uFF1Anpx initx env-config --help
71
80
 
72
81
  \u793A\u4F8B\uFF1A
@@ -75,4 +84,4 @@ Token\uFF1A--token > INITX_ENV_TOKEN > GH_TOKEN > GITHUB_TOKEN > \u6301\u4E45\u5
75
84
  npx initx env pull mail-peon-publish
76
85
  npx initx env setup mail-peon-publish --token "$GITHUB_TOKEN"
77
86
  npx initx env apply mail-peon-publish --repo mail-peon/mail-peon
78
- `)}export{Ht as default};
87
+ `)}export{Fe as default};
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@initx-plugin/env",
3
3
  "type": "module",
4
- "version": "0.1.2",
5
- "description": "initx plugin to manage env vars and secrets from one private GitHub repo",
4
+ "version": "0.1.4",
5
+ "description": "initx plugin to manage env vars and secrets from private GitHub or GitLab repositories",
6
6
  "author": "imba97",
7
7
  "license": "MIT",
8
8
  "homepage": "https://github.com/initx-collective/initx-plugin-env#readme",