@initx-plugin/env 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +234 -2
- package/dist/index.d.mts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.mjs +78 -0
- package/package.json +68 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 imba97 <https://github.com/imba97>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,235 @@
|
|
|
1
|
-
|
|
1
|
+
## @initx-plugin/env
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`initx` plugin to manage env vars and secrets from one private GitHub repo.
|
|
4
|
+
|
|
5
|
+
One private repository holds **one JSON file per store** (`stores/<name>.json`). Pull a store into a
|
|
6
|
+
local `.env`, inject it into a child process or CI job, or write it into GitHub Actions secrets.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
initx plugin add env
|
|
10
|
+
```
|
|
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.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm i -g initx # optional, otherwise use npx
|
|
26
|
+
initx plugin add env
|
|
27
|
+
|
|
28
|
+
initx env-config set repo <owner/repo> # the private repo that holds your stores
|
|
29
|
+
initx env-config set token <pat> # classic PAT with the `repo` scope is enough
|
|
30
|
+
initx env-config set path stores # optional, default: stores
|
|
31
|
+
initx env-config status # prints the config (token masked) + checks connectivity
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The config lives in `~/.initx/stores/@initx-plugin/env/store.json`. The token is stored **in
|
|
35
|
+
plaintext** there (same trade-off as `@initx-plugin/cp`) — set `INITX_ENV_TOKEN` instead if you would
|
|
36
|
+
rather keep it out of a file.
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
| Command | What it does |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `initx env list` | List the stores in the vault repo |
|
|
43
|
+
| `initx env pull [store]` | Pull a store into a local `.env` (asks which store / which file when ambiguous) |
|
|
44
|
+
| `initx env set <store> K=V ...` | Write variables into a store (the store name **is** the file name) |
|
|
45
|
+
| `initx env push <store>` | Update a store from a local `.env` |
|
|
46
|
+
| `initx env setup [store]` | Inject a store into **this** environment (CI job or shell) |
|
|
47
|
+
| `initx env run <store> -- <command> [...args]` | Inject a store directly into one child process and return its exit status |
|
|
48
|
+
| `initx env apply <store> --repo o/r` | Write a store into a repo's GitHub Actions secrets |
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# write / update a store
|
|
52
|
+
initx env set mail-peon-publish EXTENSION_ID=abc CHROME_REFRESH_TOKEN=1//0g...
|
|
53
|
+
|
|
54
|
+
# pull it into a local .env (keeps your other keys, comments and order)
|
|
55
|
+
initx env pull mail-peon-publish
|
|
56
|
+
initx env pull mail-peon-publish --to .env.local --dry-run
|
|
57
|
+
|
|
58
|
+
# push local changes back
|
|
59
|
+
initx env push mail-peon-publish --from .env.local
|
|
60
|
+
initx env push mail-peon-publish --prune # also drop remote keys missing locally
|
|
61
|
+
|
|
62
|
+
# run a script with the store's original values (including real newlines)
|
|
63
|
+
initx env run mail-peon-publish -- node scripts/publish.mjs
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Run a command locally or in CI
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
initx env run demo -- node scripts/check.mjs
|
|
70
|
+
initx env run demo -- node --version
|
|
71
|
+
initx env run acewill-slidev-production -- pnpm deploy
|
|
72
|
+
initx env run demo -- bash scripts/deploy.sh
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The store and the first `--` are required. Put the plugin's optional `--token <token>` before `--`;
|
|
76
|
+
every argument after it belongs to the child, including `--help`, `--version`, `--token`, another
|
|
77
|
+
`--`, empty strings and arguments with spaces. Extra positional arguments and other plugin flags
|
|
78
|
+
(including `--repo`, `--json`, `--stdout`, `--only`, `--shell`, `--cwd`) are rejected before reading
|
|
79
|
+
the vault. Help before `--` is handled by initx; help after it is passed to the child.
|
|
80
|
+
|
|
81
|
+
The child inherits the caller's working directory, stdin, stdout, stderr and environment. Store
|
|
82
|
+
variables override inherited values without changing the calling shell. Empty values, CRLF, real
|
|
83
|
+
newlines, Unicode and punctuation are preserved; values are never expanded. Empty stores are valid.
|
|
84
|
+
Variable names must match `^[A-Za-z_][A-Za-z0-9_]*$`, and values cannot contain NUL. All values are
|
|
85
|
+
validated before starting the child. Windows names are case insensitive: a store's `PATH` replaces
|
|
86
|
+
an inherited `Path`, and conflicting store names such as `FOO` / `foo` abort. Other commands retain
|
|
87
|
+
their existing name validation rules.
|
|
88
|
+
|
|
89
|
+
Arguments are passed as an array. The plugin does not interpret shell expressions, pipelines or
|
|
90
|
+
redirection. Explicitly run a shell or, preferably, write a script for multiple operations. One
|
|
91
|
+
`run` affects only that child and its descendants; the next independent CI command does not inherit
|
|
92
|
+
the fetched store. `cross-spawn` is the runtime dependency used to resolve executables and escape
|
|
93
|
+
Windows npm/pnpm `.cmd` launchers without enabling `shell: true` for every command
|
|
94
|
+
([upstream behavior](https://github.com/moxystudio/node-cross-spawn#why)).
|
|
95
|
+
|
|
96
|
+
Normal exits preserve the child's code, including nonzero codes. Missing commands return 127,
|
|
97
|
+
permission failures return 126, and parameter/configuration/read/validation/other startup failures
|
|
98
|
+
return 1. On POSIX, signal termination is reproduced after initx has finished its store cleanup,
|
|
99
|
+
with a nonzero `128 + signal number` fallback. Parent SIGINT/SIGTERM events are forwarded once to
|
|
100
|
+
the direct child, with a 5-second grace period followed by best-effort SIGKILL. Listeners are removed
|
|
101
|
+
when execution finishes. Background processes detached from the direct child are outside this
|
|
102
|
+
contract; deployment scripts should stay in the foreground.
|
|
103
|
+
|
|
104
|
+
On Windows, Node's process termination APIs do not deliver catchable POSIX signals: forwarding
|
|
105
|
+
SIGINT/SIGTERM can terminate the direct child immediately, and the wrapper returns 130/143. A
|
|
106
|
+
console Ctrl+C event received by Node uses the same forwarding handler. `.cmd` launchers can have
|
|
107
|
+
descendant processes; this version does not guarantee termination of that whole tree. Windows
|
|
108
|
+
tests exercise the real direct-child termination and listener cleanup by triggering the parent's
|
|
109
|
+
signal event over IPC; interactive console Ctrl+C still needs a manual console check.
|
|
110
|
+
|
|
111
|
+
`run` writes no `.env`, `$GITHUB_ENV`, dotenv artifact or secret cache, and emits no GitHub masking
|
|
112
|
+
protocol. Its own diagnostics go to stderr without values, tokens, complete response bodies or
|
|
113
|
+
command argument lists. **Child output is inherited without masking**: a script can still print
|
|
114
|
+
secrets. The parent environment, including inherited GitHub tokens, is retained. This is process
|
|
115
|
+
injection for trusted scripts and stores, not an isolation sandbox or encrypted secret manager.
|
|
116
|
+
The stdout form of `setup` remains intended for inspection/legacy use; use `run` for exact values.
|
|
117
|
+
|
|
118
|
+
## Configuration precedence
|
|
119
|
+
|
|
120
|
+
| Setting | Highest to lowest priority |
|
|
121
|
+
| --- | --- |
|
|
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` |
|
|
126
|
+
|
|
127
|
+
Empty strings fall back to the next source. Environment settings affect the current invocation
|
|
128
|
+
only and are never written back to the plugin store. `env-config status --offline` shows the
|
|
129
|
+
effective settings with a masked token; `get` / `list` show the persisted settings. `env apply
|
|
130
|
+
--repo o/r` still selects the destination Actions repository, not the vault. `run` has no `--repo`.
|
|
131
|
+
|
|
132
|
+
## Use in GitLab CI
|
|
133
|
+
|
|
134
|
+
Preinstall fixed versions of initx and this plugin in the CI image, or prepare them with an explicit
|
|
135
|
+
noninteractive installation step. Do not rely on a first-run plugin installation prompt. This
|
|
136
|
+
implementation is smoke-tested with **initx 0.4.3**; compatible versions must expose the untouched
|
|
137
|
+
tail as `ctx.cliOptions['--']: string[]`, keep child flags out of prefix help/version handling, and
|
|
138
|
+
preserve `process.exitCode` after handlers finish. Older versions have not been verified.
|
|
139
|
+
|
|
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:
|
|
142
|
+
|
|
143
|
+
```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
|
|
150
|
+
script:
|
|
151
|
+
- initx env run acewill-slidev-production -- pnpm deploy
|
|
152
|
+
```
|
|
153
|
+
|
|
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
|
|
157
|
+
materialize multiline SSH keys as needed, set file permissions and clean them up; `run` does not
|
|
158
|
+
create key files or pass them through artifacts/cache.
|
|
159
|
+
|
|
160
|
+
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
|
|
162
|
+
store filename: use separate repositories for separate trust boundaries. The private JSON store and
|
|
163
|
+
its Git history remain plaintext.
|
|
164
|
+
|
|
165
|
+
## Use in GitHub Actions
|
|
166
|
+
|
|
167
|
+
The built-in `GITHUB_TOKEN` can only read **the repository the workflow runs in**, so a separate vault
|
|
168
|
+
repo needs its own token (one secret):
|
|
169
|
+
|
|
170
|
+
```yaml
|
|
171
|
+
- run: npx --yes initx plugin add env
|
|
172
|
+
|
|
173
|
+
# inject into this job: every later step sees $EXTENSION_ID / $CHROME_REFRESH_TOKEN
|
|
174
|
+
- run: npx --yes initx env setup mail-peon-publish --token "${{ secrets.ENV_VAULT_TOKEN }}"
|
|
175
|
+
|
|
176
|
+
# or write them into the repo's Actions secrets once (needs secrets write permission)
|
|
177
|
+
- run: npx --yes initx env apply mail-peon-publish --repo "${{ github.repository }}"
|
|
178
|
+
env:
|
|
179
|
+
INITX_ENV_TOKEN: ${{ secrets.ENV_VAULT_TOKEN }}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`env setup` writes to `$GITHUB_ENV` and emits `::add-mask::` for every value before it does, so the
|
|
183
|
+
values do not show up in logs. Values with newlines use the heredoc (`KEY<<EOF`) form.
|
|
184
|
+
|
|
185
|
+
## Store format
|
|
186
|
+
|
|
187
|
+
```jsonc
|
|
188
|
+
// stores/mail-peon-publish.json
|
|
189
|
+
{
|
|
190
|
+
"schema": 1,
|
|
191
|
+
"vars": {
|
|
192
|
+
"EXTENSION_ID": "abcdefghijklmnopabcdefghijklmnop",
|
|
193
|
+
"CHROME_REFRESH_TOKEN": "1//0g..."
|
|
194
|
+
},
|
|
195
|
+
"targets": { // optional routing hints
|
|
196
|
+
"envFiles": [".env.local"], // pull: skip the file picker
|
|
197
|
+
"secrets": [{ "repo": "mail-peon/mail-peon", "env": null }] // apply: default destination
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Good to know
|
|
203
|
+
|
|
204
|
+
- **Values are stored as plaintext JSON in v1.** The repo is private, but anything committed stays in
|
|
205
|
+
git history forever — rotate the values if that repo ever becomes public. Encryption is planned and
|
|
206
|
+
will be orthogonal to the command surface (dotenvx / sops + age).
|
|
207
|
+
- **No local cache.** Values are fetched on every invocation. `pull` writes its target `.env` file
|
|
208
|
+
and backup; `setup` may write `$GITHUB_ENV`; `run` does not persist fetched values.
|
|
209
|
+
- **`apply` constraints** (GitHub's, not ours): secret values are write-only; a single secret is
|
|
210
|
+
capped at 48 KB; names may only contain `[A-Za-z0-9_]`, cannot start with a digit or `GITHUB_`, and
|
|
211
|
+
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).
|
|
215
|
+
- **Output is masked**: tokens print as `***`, values are never printed by `list` / `get` / `status`.
|
|
216
|
+
|
|
217
|
+
## Development
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
pnpm install
|
|
221
|
+
pnpm stub # unbuild --stub, then: initx plugin add .
|
|
222
|
+
pnpm test # vitest
|
|
223
|
+
pnpm build
|
|
224
|
+
pnpm lint
|
|
225
|
+
pnpm typecheck
|
|
226
|
+
```
|
|
227
|
+
|
|
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
|
|
231
|
+
coverage and the local verification record are in [env run implementation](docs/env-run-implementation.md).
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { InitxPlugin, InitxMatcherRules, InitxContext } from '@initx-plugin/core';
|
|
2
|
+
|
|
3
|
+
/** 插件自己的持久化配置(`src/index.ts` 的 defaultStore 就是它) */
|
|
4
|
+
interface EnvPluginStore {
|
|
5
|
+
repo?: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
branch?: string;
|
|
8
|
+
path?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare class EnvPlugin extends InitxPlugin<EnvPluginStore> {
|
|
12
|
+
/**
|
|
13
|
+
* 插件自己的配置(vault 仓库地址 + token)。
|
|
14
|
+
* initx 会把它存到 `~/.initx/stores/@initx-plugin/env/store.json`,退出时自动落盘。
|
|
15
|
+
*/
|
|
16
|
+
defaultStore: EnvPluginStore;
|
|
17
|
+
rules: InitxMatcherRules;
|
|
18
|
+
handle(ctx: InitxContext<EnvPluginStore>, ...args: (string | undefined)[]): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { EnvPlugin as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { InitxPlugin, InitxMatcherRules, InitxContext } from '@initx-plugin/core';
|
|
2
|
+
|
|
3
|
+
/** 插件自己的持久化配置(`src/index.ts` 的 defaultStore 就是它) */
|
|
4
|
+
interface EnvPluginStore {
|
|
5
|
+
repo?: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
branch?: string;
|
|
8
|
+
path?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare class EnvPlugin extends InitxPlugin<EnvPluginStore> {
|
|
12
|
+
/**
|
|
13
|
+
* 插件自己的配置(vault 仓库地址 + token)。
|
|
14
|
+
* initx 会把它存到 `~/.initx/stores/@initx-plugin/env/store.json`,退出时自动落盘。
|
|
15
|
+
*/
|
|
16
|
+
defaultStore: EnvPluginStore;
|
|
17
|
+
rules: InitxMatcherRules;
|
|
18
|
+
handle(ctx: InitxContext<EnvPluginStore>, ...args: (string | undefined)[]): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { EnvPlugin as default };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
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
|
|
10
|
+
`)?`\r
|
|
11
|
+
`:`
|
|
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(`
|
|
16
|
+
`)}).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
|
|
22
|
+
${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
|
|
24
|
+
${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
|
|
26
|
+
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(`
|
|
28
|
+
\u7528\u6CD5\uFF1Anpx initx env-config <\u547D\u4EE4>
|
|
29
|
+
|
|
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
|
|
32
|
+
get <key> \u67E5\u770B\u6301\u4E45\u5316\u5355\u9879\uFF08token \u4F1A\u8131\u654F\uFF09
|
|
33
|
+
list \u67E5\u770B\u5168\u90E8\u6301\u4E45\u5316\u914D\u7F6E\uFF08token \u4F1A\u8131\u654F\uFF09
|
|
34
|
+
rm <key> \u6E05\u9664\u5355\u9879
|
|
35
|
+
status [--offline] \u67E5\u770B\u5B9E\u9645\u751F\u6548\u914D\u7F6E + \u8FDE\u901A\u6027\u68C0\u67E5
|
|
36
|
+
|
|
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
|
|
39
|
+
|
|
40
|
+
\u793A\u4F8B\uFF1A
|
|
41
|
+
npx initx env-config set repo imba97/env-vault
|
|
42
|
+
npx initx env-config set token github_pat_xxx
|
|
43
|
+
npx initx env-config set path stores
|
|
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(`
|
|
48
|
+
\u7528\u6CD5\uFF1Anpx initx env <\u547D\u4EE4>
|
|
49
|
+
|
|
50
|
+
list \u5217\u51FA vault \u4ED3\u5E93\u91CC\u6240\u6709 store
|
|
51
|
+
pull [store] \u62C9\u53D6\u5230\u672C\u5730 .env\uFF08\u4E0D\u5199\u540D\u5B57\u5C31\u5217\u51FA\u6765\u9009\uFF09
|
|
52
|
+
--to .env.local \u6307\u5B9A\u76EE\u6807\u6587\u4EF6\uFF08\u53EF\u9017\u53F7\u5206\u9694\u591A\u4E2A\uFF09
|
|
53
|
+
--dry-run \u53EA\u770B\u4F1A\u6539\u54EA\u4E9B\u952E\uFF0C\u4E0D\u5199\u6587\u4EF6
|
|
54
|
+
set <store> KEY=value ... \u76F4\u63A5\u5F80\u4ED3\u5E93\u91CC\u7684 store \u5199\u53D8\u91CF\uFF08store \u540D\u5C31\u662F\u6587\u4EF6\u540D\uFF09
|
|
55
|
+
push <store> \u4ECE\u672C\u5730 .env \u53CD\u5411\u66F4\u65B0 store
|
|
56
|
+
--from .env.local \u6307\u5B9A\u6765\u6E90\u6587\u4EF6\uFF08\u9ED8\u8BA4 .env.local \u2192 .env\uFF09
|
|
57
|
+
--prune \u5220\u6389\u4ED3\u5E93\u91CC\u672C\u5730\u6CA1\u6709\u7684\u952E
|
|
58
|
+
--force \u8FDC\u7AEF\u88AB\u6539\u8FC7\u65F6\u5F3A\u5236\u8986\u76D6
|
|
59
|
+
setup [store] \u628A store \u6CE8\u5165**\u672C\u6B21**\u8FD0\u884C\u7684\u73AF\u5883\uFF1A
|
|
60
|
+
GitHub Actions \u91CC\u5199 $GITHUB_ENV\uFF08\u540E\u7EED step \u53EF\u89C1\uFF09\u5E76\u81EA\u52A8\u8131\u654F
|
|
61
|
+
\u672C\u5730\u5219\u6253\u5230 stdout\uFF08--stdout \u5F3A\u5236\uFF1B--json \u8F93\u51FA JSON\uFF09
|
|
62
|
+
apply <store> --repo o/r \u628A store \u5199\u8FDB\u67D0\u4E2A\u4ED3\u5E93\u7684 Actions secrets
|
|
63
|
+
--env <\u73AF\u5883\u540D> \u5199\u73AF\u5883\u7EA7 secret
|
|
64
|
+
--only A,B \u53EA\u5199\u8FD9\u51E0\u4E2A
|
|
65
|
+
--dry-run \u53EA\u5217\u51FA\u5C06\u5199\u5165\u7684\u540D\u5B57
|
|
66
|
+
--skip-invalid \u8DF3\u8FC7\u540D\u5B57\u4E0D\u5408\u6CD5\u7684\u53D8\u91CF\uFF08\u9ED8\u8BA4\u4E2D\u6B62\uFF09
|
|
67
|
+
run <store> -- cmd [args] \u76F4\u63A5\u6CE8\u5165\u5B50\u8FDB\u7A0B\uFF0C\u7EE7\u627F stdio / cwd\uFF0C\u4F20\u9012\u9000\u51FA\u72B6\u6001
|
|
68
|
+
--token <pat> \u4E34\u65F6\u51ED\u636E\uFF08CI \u63A8\u8350 INITX_ENV_TOKEN\uFF09
|
|
69
|
+
|
|
70
|
+
\u914D\u7F6E vault \u4ED3\u5E93\u7528\uFF1Anpx initx env-config --help
|
|
71
|
+
|
|
72
|
+
\u793A\u4F8B\uFF1A
|
|
73
|
+
npx initx env-config set repo imba97/env-vault
|
|
74
|
+
npx initx env set mail-peon-publish EXTENSION_ID=abc CHROME_REFRESH_TOKEN=1//0g...
|
|
75
|
+
npx initx env pull mail-peon-publish
|
|
76
|
+
npx initx env setup mail-peon-publish --token "$GITHUB_TOKEN"
|
|
77
|
+
npx initx env apply mail-peon-publish --repo mail-peon/mail-peon
|
|
78
|
+
`)}export{Ht as default};
|
package/package.json
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@initx-plugin/env",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"packageManager": "pnpm@12.10.1",
|
|
6
|
+
"description": "initx plugin to manage env vars and secrets from one private GitHub repo",
|
|
7
|
+
"author": "imba97",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://github.com/initx-collective/initx-plugin-env#readme",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git@github.com:initx-collective/initx-plugin-env.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/initx-collective/initx-plugin-env/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"initx",
|
|
19
|
+
"initx-plugin",
|
|
20
|
+
"env",
|
|
21
|
+
"dotenv",
|
|
22
|
+
"secrets",
|
|
23
|
+
"github-actions"
|
|
24
|
+
],
|
|
25
|
+
"main": "dist/index.mjs",
|
|
26
|
+
"module": "dist/index.mjs",
|
|
27
|
+
"types": "dist/index.d.ts",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"stub": "unbuild --stub",
|
|
33
|
+
"build": "unbuild",
|
|
34
|
+
"release": "bumpp",
|
|
35
|
+
"lint": "eslint --cache",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"prepare": "simple-git-hooks"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@initx-plugin/core": "*",
|
|
42
|
+
"@initx-plugin/utils": "*"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"cross-spawn": "^7.0.6",
|
|
46
|
+
"libsodium-wrappers": "^0.8.4",
|
|
47
|
+
"zod": "^4.6.5"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@antfu/eslint-config": "^9.5.1",
|
|
51
|
+
"@initx-plugin/core": "^0.4.3",
|
|
52
|
+
"@initx-plugin/utils": "^0.4.3",
|
|
53
|
+
"@types/cross-spawn": "^6.0.6",
|
|
54
|
+
"@types/node": "^26.6.4",
|
|
55
|
+
"bumpp": "^12.3.0",
|
|
56
|
+
"esbuild": "^0.28.2",
|
|
57
|
+
"eslint": "^10.12.0",
|
|
58
|
+
"nano-staged": "^1.0.2",
|
|
59
|
+
"simple-git-hooks": "^2.14.0",
|
|
60
|
+
"typescript": "6.0.3",
|
|
61
|
+
"unbuild": "^3.6.1",
|
|
62
|
+
"vitest": "^5.0.3"
|
|
63
|
+
},
|
|
64
|
+
"simple-git-hooks": {
|
|
65
|
+
"pre-commit": "pnpm nano-staged"
|
|
66
|
+
},
|
|
67
|
+
"nano-staged": {
|
|
68
|
+
"*": "eslint --cache --fix"
|
|
69
|
+
}
|
|
70
|
+
}
|