@guanjinke/artifact-client 1.4.0 → 1.5.1
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 +186 -363
- package/dist/cli/environment.d.ts +2 -1
- package/dist/cli/environment.d.ts.map +1 -1
- package/dist/cli/environment.js +71 -5
- package/dist/cli/environment.js.map +1 -1
- package/dist/cli/setup.js +1 -1
- package/dist/cli/setup.js.map +1 -1
- package/dist/environment/update.d.ts +26 -5
- package/dist/environment/update.d.ts.map +1 -1
- package/dist/environment/update.js +252 -86
- package/dist/environment/update.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +48 -49
package/README.md
CHANGED
|
@@ -1,363 +1,186 @@
|
|
|
1
|
-
# @guanjinke/artifact-client
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @guanjinke/artifact-client
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
|
|
90
|
-
afm
|
|
91
|
-
afm
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
afm
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
afm
|
|
109
|
-
afm
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
afm
|
|
136
|
-
afm
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
## Storage Layout
|
|
188
|
-
|
|
189
|
-
```
|
|
190
|
-
<container/bucket>/
|
|
191
|
-
└── plugin_artifacts/
|
|
192
|
-
├── pluginA/
|
|
193
|
-
│ ├── version1.0.txt # Tracks latest 1.0.x version
|
|
194
|
-
│ ├── version9.1-hotfix1.txt # Tracks latest version of the 9.1-hotfix1 major version
|
|
195
|
-
│ ├── versiondev.txt # Tracks latest dev version
|
|
196
|
-
│ ├── 1.0.5/
|
|
197
|
-
│ │ ├── bundle.zip
|
|
198
|
-
│ │ └── checksum.txt
|
|
199
|
-
│ └── 1.0.6/
|
|
200
|
-
│ └── bundle.zip
|
|
201
|
-
└── pluginB/
|
|
202
|
-
└── ...
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
- **Version files** (`version<major>.txt`, `versiondev.txt`): track the latest full version per major version
|
|
206
|
-
- The dev major version is usually the whole `0.x` range: the dev pipeline publishes a plugin-specific minor
|
|
207
|
-
(`0.0`, `0.1`, `0.2` and `0.3` are all in use) and always tracks it in `versiondev.txt`, so
|
|
208
|
-
read it with `get-latest <plugin> dev` (the numeric `0.x` spelling is accepted as an alias).
|
|
209
|
-
Some plugins publish dev artifacts under a different core (the production `gcef-identity-service`
|
|
210
|
-
currently points `dev` at `8.1.7-beta`), which is exactly why the version file is authoritative.
|
|
211
|
-
- Azure: latest version stored in blob metadata (`latestversion`)
|
|
212
|
-
- OSS: latest version stored in object tag (`latestversion`)
|
|
213
|
-
|
|
214
|
-
## API Reference
|
|
215
|
-
|
|
216
|
-
### `ArtifactClient`
|
|
217
|
-
|
|
218
|
-
| Method | Description |
|
|
219
|
-
|---|---|
|
|
220
|
-
| `listPlugins(opts?)` | List all plugins |
|
|
221
|
-
| `listMajorVersions(plugin, opts?)` | List major versions with latest version and count |
|
|
222
|
-
| `getLatestVersion(plugin, major, opts?)` | Get latest minor version for a major |
|
|
223
|
-
| `listMinorVersions(plugin, major, opts?)` | List all minor versions under a major |
|
|
224
|
-
| `listArtifacts(plugin, version, opts?)` | List all artifact files in a version |
|
|
225
|
-
| `uploadArtifact(plugin, version, name, localPath)` | Upload a file into a version directory; major version pointers are left untouched |
|
|
226
|
-
| `updateLatestVersion(plugin, major, version)` | Record a version as a major version's latest (creates `version<major>.txt` when absent) |
|
|
227
|
-
| `downloadArtifactByPath(fullPath, localDir)` | Download by full storage path |
|
|
228
|
-
| `downloadArtifact(plugin, version, fileName, localDir)` | Download by name |
|
|
229
|
-
| `downloadVersion(plugin, version, localDir)` | Download all artifacts for a version |
|
|
230
|
-
|
|
231
|
-
## Create a Wyn Environment
|
|
232
|
-
|
|
233
|
-
`create-env` resolves and assembles a **fresh Windows embedded environment** without installing Windows services or starting anything. It supports product labels `9.1`, `9.1-*` and `dev`. Dry-run reports `schema: "pending"`; execution reads the monitoring bundle's XML template and accepts only verified V91/9.1 or V100/10.0, with matching Monitor XSD assets. Missing Server XSD assets produce a warning rather than preventing assembly. Numeric artifact versions never determine the schema. Other numeric product majors fail explicitly.
|
|
234
|
-
|
|
235
|
-
A resolved dry-run is not proof that independently published packages are compatible. Some dev Server packages omit `xsd/V100/Wyn.conf.xsd`, while the Monitor template selects V100. Wyn's configuration reader falls back to ordinary XML deserialization when its XSD assets are absent. Assembly therefore records and returns a warning for missing Server XSD rather than rejecting that combination or copying schemas from another package. Monitor's matching XSD remains required. This does not verify the Server assembly's XML reader compatibility or successful runtime startup.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
# Core environment only; Server, Account, Dataset, AnalysisModel and CotWorker are automatic
|
|
240
|
-
afm oss create-env D:/wyn-core --name wyn-core --major 9.1
|
|
241
|
-
|
|
242
|
-
# Resolve exact per-module artifacts; no downloads or filesystem writes
|
|
243
|
-
afm oss create-env D:/wyn-test --name wyn-test --major 9.1 --modules dashboard,reporting --dry-run
|
|
244
|
-
afm az create-env D:/wyn-test --name wyn-test --major dev --modules analysis --edition en --dry-run
|
|
245
|
-
|
|
246
|
-
# Assemble a fresh environment (Windows only)
|
|
247
|
-
afm oss create-env D:/wyn-test --name wyn-test --major 9.1 --modules dashboard reporting --port 51980
|
|
248
|
-
|
|
249
|
-
# With a configured environment root (afm env root D:/wyn-envs) the path is optional:
|
|
250
|
-
afm oss create-env --name wyn-test --major 9.1
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
The default edition is `cn`, profile is `embedded`, and Web/embedded identity port is `51980`. The core Wyn components—Server, Account, Dataset, AnalysisModel and CotWorker—are always included and do not need to be listed in `--modules`. Optional modules are `dashboard`, `reporting` and EN-only `sheets`. `documents` is deliberately rejected until its CollaborationService dependency and embedded configuration are verified. Monitoring is also always included to run the environment. Dashboard no longer requires a separate AnalysisModel selection. Module names may be space- or comma-separated. Each plugin resolves its major once, and only its exact runtime ZIP is selected: unrelated language, styles and support archives are never selected by size.
|
|
254
|
-
|
|
255
|
-
Saved credentials and inline credential flags work as with other commands. This command honors saved `basePrefix` and `--base-prefix`; an explicit empty string selects the container/bucket root. Provider defaults are Azure `plugin_artifacts` and OSS root. Existing commands retain their current prefix behavior. The JSON plan includes exact remote object names and mappings, not credentials or a SAS URL.
|
|
256
|
-
|
|
257
|
-
Console progress is written to stderr with elapsed time and artifact counts: version resolution, download, SHA256, ZIP validation/extraction, directory assembly, configuration generation and publication. Long stages emit a waiting message every 15 seconds; this is not a byte percentage. Stdout remains a single JSON plan/result, so redirecting stdout preserves machine-readable output. Library callers can opt into the same stage messages using the third `buildEnvironmentPlan` argument or `executeEnvironmentPlan`'s `onProgress` dependency; library calls remain silent by default.
|
|
258
|
-
|
|
259
|
-
The target must be absent or empty, must not be a filesystem root or protected installed/system location, and must not have a linked/reparse-point ancestor. There is no overwrite or force option. Download, streaming ZIP extraction, assembly verification and configuration generation happen in owned sibling temporary directories. ZIP paths, case-insensitive conflicts, links, reserved Windows names and resource limits are checked for every entry, including unused support files. The defaults cap each archive at 100,000 entries and 20 GiB uncompressed. Final files are published exclusively without replacing racing files; failure removes only owned paths and preserves unrelated data.
|
|
260
|
-
|
|
261
|
-
Server-root `appsettings.json` and `appsettings.Development.json` are excluded from deployment (case-insensitive) so bundled startup overrides cannot affect the generated environment. This does not remove Monitor settings, nested helper settings or other environment-specific settings, and does not modify the downloaded ZIP or existing environments.
|
|
262
|
-
|
|
263
|
-
The canonical editable configuration is `<root>/conf/Wyn.conf`. `<root>/Monitor/conf` is a Windows directory junction to it because the monitoring process resolves configuration from its own working directory. Other monitoring-bundle `conf` files are preserved in the canonical directory. The environment contains explicit plugin/embedded worker lists, actual assembly names (including known reporting naming variants), SQLite server databases and DuckDB warehouse settings. Monitored worker services are disabled to avoid parallel launches while embedded Server loads the deployed worker assemblies.
|
|
264
|
-
|
|
265
|
-
`environment.lock.json` records selections, independent resolved versions, SHA256 hashes of downloaded ZIPs and runtime framework requirements read from required `.runtimeconfig.json` files. No host license, database or sample data is copied. Optional theme recompilation/styles-cache, additional language archives, bootstrap/sample provisioning, standalone identity patches and service installation are outside this command. Successful assembly is **not** proof of license validity, database initialization or successful runtime startup. Review the generated requirements and provide the appropriate .NET runtimes and licensing before starting anything manually.
|
|
266
|
-
|
|
267
|
-
### Environment Library API
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
import { ArtifactClient, buildEnvironmentPlan, executeEnvironmentPlan } from '@guanjinke/artifact-client';
|
|
271
|
-
|
|
272
|
-
const client = new ArtifactClient('oss', { keyId: 'id', key: 'secret' });
|
|
273
|
-
const plan = await buildEnvironmentPlan({
|
|
274
|
-
root: 'D:/wyn-test', major: '9.1', modules: ['dashboard'],
|
|
275
|
-
}, client);
|
|
276
|
-
// Planning never downloads or writes; execution uses these exact resolved versions.
|
|
277
|
-
const result = await executeEnvironmentPlan(plan, { client });
|
|
278
|
-
console.log(result.root, result.requirements);
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
Both APIs accept the existing client through a small typed interface, enabling cloud-independent testing. Execution defaults to the host platform; the optional injected platform is intended for isolated layout tests, not a claim of cross-platform Wyn support.
|
|
282
|
-
|
|
283
|
-
## Update an Existing Environment
|
|
284
|
-
|
|
285
|
-
`update-env` replaces selected modules of an already registered environment, or points a module at a local developer build without copying any files:
|
|
286
|
-
|
|
287
|
-
```bash
|
|
288
|
-
afm oss update-env <name> --modules <spec...> [--dry-run]
|
|
289
|
-
afm az update-env <name> --modules <spec...> [--dry-run]
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
Specs are space- or comma-separated, and each entry has one of these forms:
|
|
293
|
-
|
|
294
|
-
| Spec | Meaning |
|
|
295
|
-
| --- | --- |
|
|
296
|
-
| `all` | every configured module from remote, skipping modules that have a local source |
|
|
297
|
-
| `dashboard` | latest artifact of that module for the environment's recorded major |
|
|
298
|
-
| `dashboard:0.1.12345` | pinned version |
|
|
299
|
-
| `dashboard=D:\build\Wyn.Dashboards.Plugin.dll` | local source (no download, no copy) |
|
|
300
|
-
|
|
301
|
-
`all` must be used alone and covers exactly the modules that are both updateable and currently installed in that environment (`common`, `analysis`, plus any of `dashboard`/`reporting`/`sheets` it has). Modules with a local source are skipped and reported (manifest `moduleSources`, registry `localModules`, the result's `skipped` list and a stderr line); `server` and `monitoring` are never included. Because `all` only selects remote specs, it follows the remote state rule below and requires the environment to be `ready` or `stopped`. If nothing is left to update the command fails without writing.
|
|
302
|
-
|
|
303
|
-
A spec may not combine a pinned version with a local source, duplicate modules in one call are refused, and the module name must be `dashboard`, `reporting`, `sheets`, `analysis` or `common`. `server` and `monitoring` are refused because they define the runtime and configuration schema (recreate the environment instead), `documents` stays unsupported, and `sheets` still requires an `en` edition environment. Updating a module that was never installed installs it: its plugin entry is added and the module is listed in the registry; a remote spec also adds the manifest artifact/download entries, while a local spec records only `moduleSources`/`localModules` (there is no downloaded artifact to describe).
|
|
304
|
-
|
|
305
|
-
Remote updates resolve exactly one exact artifact name for the resolved version, download into a sibling staging workspace (`<dirname(root)>/.afm-env-*`), verify its SHA256, extract and validate it, check every mapping's source directory and that exactly one runtime assembly is present, and only then replace the module's deployed directories (`Plugins/<X>`, its worker directories, and `<root>/Web Contents` when the dashboard archive contains it). The plugin entry is rewritten as the normal relative `Plugins\<dest>\<assembly>` path and the deployment is recorded in the manifest and registry.
|
|
306
|
-
|
|
307
|
-
Local source behavior is configuration-only:
|
|
308
|
-
|
|
309
|
-
* Nothing is copied into or out of the environment.
|
|
310
|
-
* The already deployed `Plugins/<destination>` directory is deleted so the stale artifact cannot be loaded twice; the worker directories are left alone.
|
|
311
|
-
* `<root>/conf/Wyn.conf` `<Server><Plugins>` is rewritten to the **absolute** local DLL path (forward slashes are normalized) so the Server loads the developer build directly.
|
|
312
|
-
* The override is recorded in `environment.lock.json` `moduleSources` and in the registry as `localModules` (shown by `env list` in a `local` column); `dashboard`, `reporting`, `analysis` and `sheets` are supported. `common` deploys two plugin directories and is rejected for local sources. A directory may be given instead of a DLL when it contains exactly one of the module's known plugin assemblies, and the DLL must be outside the deployed plugin directory.
|
|
313
|
-
* Only configuration changed, so the environment must be **restarted** (`afm env restart <name>`) to load the build; the CLI never restarts it automatically.
|
|
314
|
-
* Switching a module back to a remote spec deletes the override, restores the relative entry and copies the freshly downloaded artifact again.
|
|
315
|
-
|
|
316
|
-
Configuration rewriting always parses `<root>/conf/Wyn.conf`, mutates only the `Plugins` `sys:string` list (a single string or an array are both accepted), rebuilds it with the same options the creation path uses, and validates the result with `XMLValidator` before writing. Dashboard stays the first `Plugins` entry and unrelated entries keep their order.
|
|
317
|
-
|
|
318
|
-
State rules: a remote update requires the observed state `ready` or `stopped`; `running`/`unknown` are refused with a request to run `afm env stop <name>` first, because the deployed files may be in use. A local-only update may run while the environment is `running` (the CLI then reports that a restart is required), but `unknown` is always refused. `--dry-run` performs the spec, ownership, tree and state validation plus remote resolution, prints the planned changes and downloads or writes nothing. Progress and the restart reminder go to stderr; stdout remains a single JSON result. JSON and configuration writes go through `<file>.afm-tmp` followed by an atomic rename, and the staging workspace is always removed. Unlike `create`/`start`/`stop`/`restart`, `update-env` does not take the per-environment lifecycle lock — those state rules are its only guard, so do not run an update concurrently with a lifecycle command for the same environment.
|
|
319
|
-
|
|
320
|
-
## Managed Local Environments
|
|
321
|
-
|
|
322
|
-
Create a named environment with `node dist/cli.js oss create-env D:/wyn/demo --name demo --major 9.1` (use `az` similarly). `--name` is required for managed creation and names are case-insensitively unique. The environment path is optional once a unified environment root is configured (see below). `--port` defaults to `51980`; reserved or occupied ports fail without automatic reassignment. `--dry-run` checks options, registry conflicts and TCP availability without reserving or writing files.
|
|
323
|
-
|
|
324
|
-
### Unified Environment Root
|
|
325
|
-
|
|
326
|
-
Configure one directory for all environments so the path no longer has to be typed:
|
|
327
|
-
|
|
328
|
-
```bash
|
|
329
|
-
afm env root D:/wyn-envs # validate, create if missing and remember the directory
|
|
330
|
-
afm env root # print the configured directory (path on stdout)
|
|
331
|
-
afm env root --json # script-friendly state: configured/root/exists/configPath
|
|
332
|
-
afm env root --unset # forget it; <root> is required again
|
|
333
|
-
|
|
334
|
-
# No path needed: creates D:/wyn-envs/demo
|
|
335
|
-
afm oss create-env --name demo --major 9.1
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
The setting is stored as an absolute path in `~/.afm/config.json` (`AFM_CONFIG_DIR` redirects the directory) alongside credentials, so it is per OS user/profile; credentials and other settings are preserved when it is set or cleared. Every new environment is created at `<environment root>/<name>`, and the name must be a single safe directory segment (`A-Za-z0-9`, `.`, `_`, `-`, max 64 characters), so it can never escape the root.
|
|
339
|
-
|
|
340
|
-
The configured directory is re-validated on every use. It must not be a filesystem root, the user profile itself, the AFM configuration directory or one of its descendants, or a Windows/Program Files/SystemRoot location, and no existing ancestor may be a symlink or junction. `afm env root <path>` creates a missing directory and verifies it is a regular directory; `--dry-run` validates the configured root without creating anything. An explicit `<root>` argument still wins over the configured root; when it points outside the configured directory a note is printed to stderr and the environment is still registered normally.
|
|
341
|
-
|
|
342
|
-
Registered environments live in `~/.afm/environments.json` (set `AFM_CONFIG_DIR` for an isolated profile); credentials and the optional unified environment root remain in `config.json`. Use `node dist/cli.js env list [--json]` (the table shows a `local` column listing modules with a local build override; the JSON rows carry `localModules`), `env root [<path>] [--unset] [--json]`, `env start <name> [--foreground|--background]`, `env stop <name>`, and `env restart <name>`. Background output is appended to `<root>/logs/server.stdout.log` and `server.stderr.log`; foreground Ctrl+C stops the verified monitor process tree before returning.
|
|
343
|
-
|
|
344
|
-
Start defaults to foreground. Restart preserves the previous mode, falling back to foreground. A stopped environment keeps its reserved port until deletion. List includes only environments registered for the current user/profile; in table mode it also prints the configured environment root (if any) to stderr. Lifecycle stages, verified monitor PID/mode and background log paths are printed to stderr; stdout remains available for inherited foreground output or list JSON.
|
|
345
|
-
|
|
346
|
-
Background mode starts the monitor in its own hidden console through a short launcher, then confirms the monitor by its exact command line (absolute DLL, `--RunAsMonitor`) before recording it; the recorded process is independent of the CLI, and its stdout/stderr are appended to the log files. This is required because `Wyn.Server` assigns `Console.Title` at the top of `Main` and throws `IOException` without an attached console, while a process that merely inherits the CLI's console does not outlive the CLI. Foreground mode inherits the terminal's console and stdio, so run it from a real console; if a start fails, the error includes the monitor's last log output and, when the log shows the `Console.Title` failure, points at that call. Command execution still uses the OS process query (`Get-CimInstance`); if that query is unavailable or fails twice, start/stop report an uncertain state instead of guessing.
|
|
347
|
-
|
|
348
|
-
`env delete <name> --yes` stops the verified monitor, removes the owned environment directory and then removes its registry record. Without `--yes`, deletion requires a TTY confirmation. Existing directories created before managed registration are not adopted.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
Registered states are `creating`, `ready`, `running` and `stopped`. List checks live identity and also reports `unknown`, `identity-mismatch`, `missing` and `broken`; `running` means a verified monitor process, not HTTP readiness. Start/stop/restart are serialized per environment. Names, ports and overlapping roots are reserved atomically before cloud access; interrupted creation remains visible rather than silently releasing an active reservation. Creator command lines are stored as digests so inline credentials are not persisted.
|
|
352
|
-
|
|
353
|
-
Startup requires the owned marker and manifest, Server DLL/runtimeconfig, and a configuration port matching the registry. Stop verifies PID, creation timestamp, executable and exact argument tokens, then uses forced Windows `taskkill /T /F` tree termination and confirms captured descendants exited. A final process identity recheck reduces PID reuse risk; Windows native process handles/Job Objects are not used, so hostile process reuse between inspection and `taskkill` is outside this guarantee. Unknown identity or failed tree confirmation retains the record and refuses deletion. Uncertain startup cleanup keeps a recovery identity in the registry and reports failure. If registry persistence also fails, it attempts to write `<root>/.afm-runtime-recovery.json`; any such file makes lifecycle operations uncertain and requires manual recovery. Durable identity storage cannot be guaranteed if both registry and environment filesystems are unwritable. A manual relative `dotnet Wyn.Server.dll` launch cannot be safely attributed from CIM metadata, so such an ambiguous candidate makes list/stop/delete report `unknown` and refuse; a start is still allowed because environments are only ever launched with an absolute DLL path, so that process cannot be the one being replaced.
|
|
354
|
-
|
|
355
|
-
Deletion preserves the ownership marker and manifest while removing other payload entries. If payload removal fails, the registry and metadata remain for retry. If the final directory removal fails (for example an editor or terminal still has the folder open), the command retries briefly, then attempts exclusive metadata restoration and reports an actionable error that names the processes whose command line mentions the directory, states that metadata and the registry record were retained, and asks for the same `env delete <name>` command to be re-run after those holders are closed; it does not silently unregister a partial directory.
|
|
356
|
-
|
|
357
|
-
Locks are exclusive and bounded, with no automatic stale-lock deletion. A terminated CLI can leave `registry.lock` or `env-<uuid>.lock` in the configuration directory. Recover manually only after positively establishing that its owning operation is no longer active; elapsed age alone is not sufficient. Reservation/process recovery may require manual inspection when Windows denies process metadata. No command automatically adopts old environments or scans arbitrary Wyn installs.
|
|
358
|
-
|
|
359
|
-
The standalone `buildEnvironmentPlan`/`executeEnvironmentPlan` APIs retain their previous behavior without global registration; managed callers can use `EnvironmentManager` and inject registry/process/port adapters for isolated tests. Runtime management is Windows-only; metadata planning/listing can run elsewhere but unavailable process inspection is reported as uncertain.
|
|
360
|
-
|
|
361
|
-
## License
|
|
362
|
-
|
|
363
|
-
MIT
|
|
1
|
+
# @guanjinke/artifact-client
|
|
2
|
+
|
|
3
|
+
Upload, browse and download build artifacts on **Azure Blob Storage** and **Aliyun OSS**, and build and manage local **Wyn environments** from those artifacts.
|
|
4
|
+
|
|
5
|
+
Requires Node.js >= 20. `az` commands talk to Azure, `oss` commands to Aliyun OSS; below they are interchangeable unless noted.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @guanjinke/artifact-client
|
|
11
|
+
afm --version
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Credentials
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
afm az login --sasurl "https://<account>.blob.core.windows.net/<container>?<SAS>"
|
|
18
|
+
afm oss login --keyid <id> --key <secret> --bucket <bucket> [--region oss-cn-hangzhou]
|
|
19
|
+
|
|
20
|
+
# or pass them inline on any command: --sasurl, --keyid/--key/--bucket/--region
|
|
21
|
+
afm az list-plugins --sasurl "https://..."
|
|
22
|
+
|
|
23
|
+
afm az config show # print the saved credentials
|
|
24
|
+
afm az config clear # remove them
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Saved settings live in `~/.afm/config.json`; set `AFM_CONFIG_DIR` to use another directory.
|
|
28
|
+
|
|
29
|
+
## Browse artifacts
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
afm az list-plugins [--size]
|
|
33
|
+
afm az list-majors <plugin> [--size] [--sort asc|desc]
|
|
34
|
+
afm az get-latest <plugin> <major> [--size] [--no-verify]
|
|
35
|
+
afm az list-minors <plugin> <major> [--size] [--sort asc|desc]
|
|
36
|
+
afm az list-artifacts <plugin> <version> [--size]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A **major** (`9.1`, `9.0`, `9.1-hotfix1`, `9.1-oem`, `dev`, ...) is a product label, not an artifact version. Each major owns a version file (`version9.1.txt`, `versiondev.txt`) that records the version it currently points at; `get-latest` reads it and warns on stderr when that version no longer holds artifacts (`--no-verify` skips the check).
|
|
40
|
+
|
|
41
|
+
## Upload
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Write the file into the version directory (major pointers untouched)
|
|
45
|
+
afm az upload <plugin> <version> <artifact-name> <local-file>
|
|
46
|
+
|
|
47
|
+
# ... and record it as the latest of a major in the same step
|
|
48
|
+
afm az upload <plugin> <version> <artifact-name> <local-file> --set-latest --major 9.1
|
|
49
|
+
|
|
50
|
+
# Or move a major separately
|
|
51
|
+
afm az set-latest <plugin> <major> <version> [--no-verify]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Uploading and moving a major are deliberately separate: a hotfix upload cannot accidentally move `version9.1.txt`.
|
|
55
|
+
|
|
56
|
+
## Download
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
afm az download --path "<full/storage/path>" [--out ./dir]
|
|
60
|
+
afm az download --plugin <plugin> --ver <version> --file <name> [--out ./dir]
|
|
61
|
+
afm az download --plugin <plugin> --ver <version> --all [--out ./dir]
|
|
62
|
+
afm az download --plugin <plugin> --major <major> [--file <name>] [--out ./dir]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`--major` resolves that major's latest version first, so `--all` is implied; `--major` and `--ver` are mutually exclusive. Artifacts keep their sub-directory: `patched/X.dll` is written to `<out>/patched/X.dll`.
|
|
66
|
+
|
|
67
|
+
## Local Wyn environments (Windows)
|
|
68
|
+
|
|
69
|
+
Assembling and running environments is Windows-only. `oss`/`az` only decide where the artifacts come from.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Optionally pick one directory for every environment (stored in config.json)
|
|
73
|
+
afm env root D:\wyn-envs # validate, create if missing, remember
|
|
74
|
+
afm env root # print it
|
|
75
|
+
afm env root --json # { configured, root, exists, configPath }
|
|
76
|
+
afm env root --unset
|
|
77
|
+
|
|
78
|
+
# Assemble a fresh embedded environment (nothing is started or registered as a Windows service)
|
|
79
|
+
afm oss create-env D:\wyn-envs\demo --name demo --major 9.1 \
|
|
80
|
+
[--modules dashboard reporting] [--edition en] [--port 51980] [--dry-run]
|
|
81
|
+
|
|
82
|
+
# With a configured environment root the path can be omitted
|
|
83
|
+
afm oss create-env --name demo --major 9.1
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Server, Monitor, Account, Dataset, AnalysisModel and CotWorker are always included; `--modules` adds `dashboard`, `reporting` and (EN edition only) `sheets`. The default port is `51980`. `--dry-run` resolves everything and writes nothing. The result warns when the Server package ships no XSD for the detected configuration schema.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
afm env list [--json] # state, port, edition, major, root, local overrides, PID
|
|
90
|
+
afm env start <name> [--foreground|--background]
|
|
91
|
+
afm env stop <name>
|
|
92
|
+
afm env restart <name> [--foreground|--background]
|
|
93
|
+
afm env delete <name> [--yes]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
* Background output is appended to `<root>\logs\server.stdout.log` and `server.stderr.log`.
|
|
97
|
+
* `running` means the monitor process is alive — not that Wyn finished starting or that the port answers.
|
|
98
|
+
* A stopped environment keeps its reserved port and name; nothing is reassigned automatically.
|
|
99
|
+
* `delete` stops the environment, then removes its directory and its registry record; `--yes` is required when stdin is not a terminal.
|
|
100
|
+
* Environments are listed from `~/.afm/environments.json` (`AFM_CONFIG_DIR` for another profile).
|
|
101
|
+
|
|
102
|
+
## Update the modules of an environment
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
afm oss update-env <name> --modules dashboard # latest for the recorded major
|
|
106
|
+
afm oss update-env <name> --modules dashboard:0.1.12345 # pinned version
|
|
107
|
+
afm oss update-env <name> --modules dashboard=D:\build\Wyn.Dashboards.Plugin.dll
|
|
108
|
+
afm oss update-env <name> --modules all # every configured module except local ones
|
|
109
|
+
afm oss update-env <name> --modules dashboard --dry-run
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Remote updates download, verify (SHA256 + archive checks) and replace the deployed files; the environment must be observed `ready` or `stopped` (`running`/`unknown` is refused — stop it first). A **local source copies nothing**: the deployed `Plugins\<module>` directory is removed and `conf\Wyn.conf` is pointed straight at your build, so the Server loads it; restart the environment to load the change. Modules that were never installed are installed.
|
|
113
|
+
|
|
114
|
+
A module can own several assemblies (`gces-common` = two plugins + one worker; `dashboard`/`reporting` = plugin + worker), so local sources resolve per assembly:
|
|
115
|
+
|
|
116
|
+
| Spec | Selects |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `dashboard=D:\build\Wyn.Dashboards.Plugin.dll` | the target whose file name matches |
|
|
119
|
+
| `dashboard=D:\build\out` | every assembly of that module found in the directory |
|
|
120
|
+
| `dashboard#Wyn.Dashboards.Worker.dll=D:\build\worker.dll` | that target explicitly |
|
|
121
|
+
| repeated `common=` specs | one target each, as long as no target is selected twice |
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
afm oss update-env demo --modules \
|
|
125
|
+
common=D:\gces-common\src\Wyn.Dataset.Plugin\bin\release\net8.0\Wyn.Dataset.Plugin.dll \
|
|
126
|
+
common=D:\gces-common\src\Wyn.Account.Plugin\bin\release\net8.0\Wyn.Account.Plugin.dll \
|
|
127
|
+
common=D:\gces-common\src\Wyn.CotWorker\bin\release\net8.0\Wyn.CotWorker.dll
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The override is recorded in the environment manifest (`moduleSources`) and shown by `env list` in a `local` column; switching the module back to a remote spec deletes the override and restores the deployed files.
|
|
131
|
+
|
|
132
|
+
## Apply a local build while starting
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
afm env start demo --background --modules dashboard=D:\build\Wyn.Dashboards.Plugin.dll
|
|
136
|
+
afm env restart demo --modules common=D:\build\common,common#Wyn.CotWorker.dll=D:\build\Wyn.CotWorker.dll
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Same specs as `update-env`, but only local sources are accepted here (`env` has no cloud provider — use `update-env` for artifacts). `start` applies the override and then starts; `restart` stops, applies and starts again, which is the usual "rebuild, then reload" loop.
|
|
140
|
+
|
|
141
|
+
## Library
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { ArtifactClient, buildEnvironmentPlan, executeEnvironmentPlan } from '@guanjinke/artifact-client';
|
|
145
|
+
|
|
146
|
+
const client = new ArtifactClient('oss', { keyId: 'id', key: 'secret', bucket: 'my-bucket' });
|
|
147
|
+
|
|
148
|
+
await client.listPlugins({ includeSize: true });
|
|
149
|
+
await client.listMajorVersions('myPlugin', { sortOrder: 'asc' });
|
|
150
|
+
await client.getLatestVersion('gces-server', '9.1');
|
|
151
|
+
await client.listArtifacts('gces-server', '9.1.21042', { includeSize: true });
|
|
152
|
+
await client.uploadArtifact('myPlugin', '1.0.6', 'bundle.zip', './dist/bundle.zip');
|
|
153
|
+
await client.updateLatestVersion('myPlugin', '9.1-hotfix1', '1.0.6');
|
|
154
|
+
await client.downloadArtifact('myPlugin', '1.0.5', 'bundle.zip', './downloads');
|
|
155
|
+
await client.downloadArtifactByPath('plugin_artifacts/myPlugin/1.0.5/bundle.zip', './downloads');
|
|
156
|
+
const files = await client.downloadVersion('myPlugin', '1.0.5', './downloads');
|
|
157
|
+
|
|
158
|
+
// Plan resolutely (no downloads or writes), then assemble and register an environment
|
|
159
|
+
const plan = await buildEnvironmentPlan({ root: 'D:/wyn/demo', major: '9.1', modules: ['dashboard'] }, client);
|
|
160
|
+
const result = await executeEnvironmentPlan(plan, { client });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
| Method | Description |
|
|
164
|
+
|---|---|
|
|
165
|
+
| `listPlugins(opts?)` | List plugins (optionally with total size) |
|
|
166
|
+
| `listMajorVersions(plugin, opts?)` | Majors with the version each one points at |
|
|
167
|
+
| `getLatestVersion(plugin, major, opts?)` | Version a major currently points at |
|
|
168
|
+
| `listMinorVersions(plugin, major, opts?)` | Versions under a major |
|
|
169
|
+
| `listArtifacts(plugin, version, opts?)` | Files of a version (optionally with sizes) |
|
|
170
|
+
| `uploadArtifact(plugin, version, name, localPath)` | Upload one file into a version directory |
|
|
171
|
+
| `updateLatestVersion(plugin, major, version)` | Move a major's latest pointer |
|
|
172
|
+
| `downloadArtifactByPath(fullPath, localDir)` | Download by full storage path |
|
|
173
|
+
| `downloadArtifact(plugin, version, fileName, localDir)` | Download by name |
|
|
174
|
+
| `downloadVersion(plugin, version, localDir)` | Download every file of a version |
|
|
175
|
+
|
|
176
|
+
`client` is a small typed interface, so tests can inject a stub. Environment APIs are Windows-only for execution; planning and listing work anywhere.
|
|
177
|
+
|
|
178
|
+
## Notes
|
|
179
|
+
|
|
180
|
+
* Stopping is a forced process-tree termination, not a graceful HTTP shutdown.
|
|
181
|
+
* A missing or unknown process identity blocks start/stop/delete instead of guessing.
|
|
182
|
+
* Version resolution never selects a different archive by size: only the exact expected file name is used.
|
|
183
|
+
|
|
184
|
+
Design details, safety semantics and development notes live in the repository.
|
|
185
|
+
|
|
186
|
+
MIT
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { EnvironmentManager } from '../environment/manager';
|
|
3
3
|
import { EnvironmentRecord } from '../environment/registry';
|
|
4
|
+
import { EnvironmentUpdater } from '../environment/update';
|
|
4
5
|
export declare function confirmDeletion(entry: EnvironmentRecord, yes: boolean): Promise<boolean>;
|
|
5
|
-
export declare function addEnvironmentCommands(program: Command, factory?: () => EnvironmentManager): void;
|
|
6
|
+
export declare function addEnvironmentCommands(program: Command, factory?: () => EnvironmentManager, updaterFactory?: () => EnvironmentUpdater): void;
|
|
6
7
|
//# sourceMappingURL=environment.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/cli/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAW,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/cli/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAW,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAoB,MAAM,uBAAuB,CAAC;AA+C7E,wBAAsB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9F;AA0CD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,MAAM,kBAAoG,EAAE,cAAc,GAAE,MAAM,kBAAoG,GAAG,IAAI,CA4C9S"}
|