code-workspace-zhuiyi 1.0.0-beta.1 → 1.0.0-beta.2
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 +12 -27
- package/README.zh-CN.md +12 -27
- package/artifacts/manifest.json +1 -1
- package/docs/extension-architecture.zh-CN.md +2 -2
- package/docs/extension-development/extension-development-guide.zh-CN.md +4 -4
- package/docs/extension-development/nexus-publish.zh-CN.md +2 -4
- package/package.json +1 -1
- package/src/cli/commands/ext.js +1 -6
- package/src/cli/commands/extension.js +122 -36
- package/src/cli/commands/init.js +82 -31
- package/src/cli/registry.js +3 -3
- package/src/core/extension-package-providers.js +8 -1
- package/src/core/extension-registry-lifecycle.js +226 -40
- package/src/core/extension-store.js +8 -0
- package/src/core/extensions.js +49 -8
- package/src/core/nexus-extension-provider.js +6 -3
- package/src/core/system-extensions.js +3 -0
- package/src/init/extension-picker.js +295 -0
- package/src/init/registry-picker.js +58 -0
- package/src/init/ui.js +5 -1
- package/src/init/wizard.js +62 -18
- package/extensions/monitor/1.0.0/assets/i18n-icon.svg +0 -1
- package/extensions/monitor/1.0.0/assets/logo-vector.svg +0 -30
- package/extensions/monitor/1.0.0/assets/request_tip.mp3 +0 -0
- package/extensions/monitor/1.0.0/assets/session_finish.mp3 +0 -0
- package/extensions/monitor/1.0.0/i18n/index.js +0 -33
- package/extensions/monitor/1.0.0/i18n/locales/en-US.js +0 -85
- package/extensions/monitor/1.0.0/i18n/locales/zh-CN.js +0 -79
- package/extensions/monitor/1.0.0/init.js +0 -38
- package/extensions/monitor/1.0.0/manifest.json +0 -40
- package/extensions/monitor/1.0.0/monitor.js +0 -347
- package/extensions/monitor/1.0.0/page.js +0 -118
- package/extensions/monitor/1.0.0/runtime.js +0 -76
- package/extensions/monitor/1.1.0/assets/i18n-icon.svg +0 -1
- package/extensions/monitor/1.1.0/assets/logo-vector.svg +0 -30
- package/extensions/monitor/1.1.0/assets/request_tip.mp3 +0 -0
- package/extensions/monitor/1.1.0/assets/session_finish.mp3 +0 -0
- package/extensions/monitor/1.1.0/config-yaml.js +0 -69
- package/extensions/monitor/1.1.0/i18n/index.js +0 -33
- package/extensions/monitor/1.1.0/i18n/locales/en-US.js +0 -85
- package/extensions/monitor/1.1.0/i18n/locales/zh-CN.js +0 -79
- package/extensions/monitor/1.1.0/init.js +0 -38
- package/extensions/monitor/1.1.0/manifest.json +0 -40
- package/extensions/monitor/1.1.0/monitor.js +0 -373
- package/extensions/monitor/1.1.0/page.js +0 -118
- package/extensions/monitor/1.1.0/runtime.js +0 -105
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/SKILL.md +0 -60
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/codex/openai.yaml +0 -4
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/original-specs/ARCHIVE-MANIFEST.md +0 -14
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/original-specs//345/217/214/345/215/201/347/272/242/347/272/277/350/247/204/350/214/203_GLM-OCR.md +0 -288
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/original-specs//347/240/224/345/217/221/350/277/207/347/250/213/345/205/263/351/224/256/347/216/257/350/212/202/345/205/263/346/263/250/347/202/271_GLM-OCR.md +0 -69
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/references/critical-correctness.md +0 -53
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/references/data-performance.md +0 -57
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/references/process-checkpoints.md +0 -73
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/references/release-and-change.md +0 -41
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/assets/references/security.md +0 -107
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/init.js +0 -71
- package/extensions/zhuiyi-guangda-coding-spec/1.0.0/manifest.json +0 -29
- package/extensions/zhuiyi-jira-issue-fix-summary/1.0.0/assets/SKILL.md +0 -57
- package/extensions/zhuiyi-jira-issue-fix-summary/1.0.0/assets/codex/openai.yaml +0 -7
- package/extensions/zhuiyi-jira-issue-fix-summary/1.0.0/init.js +0 -62
- package/extensions/zhuiyi-jira-issue-fix-summary/1.0.0/manifest.json +0 -36
- package/extensions/zhuiyi-jira-mcp/0.1.0/artifacts/claude/server.json +0 -16
- package/extensions/zhuiyi-jira-mcp/0.1.0/artifacts/codex/config.toml +0 -13
- package/extensions/zhuiyi-jira-mcp/0.1.0/artifacts/gitignore +0 -2
- package/extensions/zhuiyi-jira-mcp/0.1.0/init.js +0 -56
- package/extensions/zhuiyi-jira-mcp/0.1.0/lib/archive.js +0 -200
- package/extensions/zhuiyi-jira-mcp/0.1.0/manifest.json +0 -46
- package/extensions/zhuiyi-jira-mcp/0.1.0/release.json +0 -15
- package/extensions/zhuiyi-jira-mcp/1.0.0/artifacts/claude/server.json +0 -21
- package/extensions/zhuiyi-jira-mcp/1.0.0/artifacts/codex/config.toml +0 -18
- package/extensions/zhuiyi-jira-mcp/1.0.0/artifacts/gitignore +0 -2
- package/extensions/zhuiyi-jira-mcp/1.0.0/init.js +0 -56
- package/extensions/zhuiyi-jira-mcp/1.0.0/lib/archive.js +0 -200
- package/extensions/zhuiyi-jira-mcp/1.0.0/manifest.json +0 -46
- package/extensions/zhuiyi-jira-mcp/1.0.0/release.json +0 -15
- package/extensions/zhuiyi-jira-mcp/1.1.0/artifacts/claude/server.json +0 -21
- package/extensions/zhuiyi-jira-mcp/1.1.0/artifacts/codex/config.toml +0 -18
- package/extensions/zhuiyi-jira-mcp/1.1.0/artifacts/gitignore +0 -2
- package/extensions/zhuiyi-jira-mcp/1.1.0/init.js +0 -54
- package/extensions/zhuiyi-jira-mcp/1.1.0/lib/archive.js +0 -200
- package/extensions/zhuiyi-jira-mcp/1.1.0/manifest.json +0 -40
- package/extensions/zhuiyi-jira-mcp/1.1.0/mcp-launcher.js +0 -69
- package/extensions/zhuiyi-jira-mcp/1.1.0/release.json +0 -15
- package/extensions/zhuiyi-jira-prd-analysis/1.0.0/assets/SKILL.md +0 -82
- package/extensions/zhuiyi-jira-prd-analysis/1.0.0/assets/codex/openai.yaml +0 -7
- package/extensions/zhuiyi-jira-prd-analysis/1.0.0/init.js +0 -62
- package/extensions/zhuiyi-jira-prd-analysis/1.0.0/manifest.json +0 -36
- package/extensions/zhuiyi-jira-task-breakdown/1.0.0/assets/SKILL.md +0 -56
- package/extensions/zhuiyi-jira-task-breakdown/1.0.0/assets/codex/openai.yaml +0 -7
- package/extensions/zhuiyi-jira-task-breakdown/1.0.0/init.js +0 -62
- package/extensions/zhuiyi-jira-task-breakdown/1.0.0/manifest.json +0 -36
- package/extensions/zhuiyi-opensvn-mcp/0.1.0/artifacts/claude/server.json +0 -12
- package/extensions/zhuiyi-opensvn-mcp/0.1.0/artifacts/codex/config.toml +0 -9
- package/extensions/zhuiyi-opensvn-mcp/0.1.0/artifacts/gitignore +0 -2
- package/extensions/zhuiyi-opensvn-mcp/0.1.0/init.js +0 -56
- package/extensions/zhuiyi-opensvn-mcp/0.1.0/lib/archive.js +0 -200
- package/extensions/zhuiyi-opensvn-mcp/0.1.0/manifest.json +0 -46
- package/extensions/zhuiyi-opensvn-mcp/0.1.0/release.json +0 -15
- package/extensions/zhuiyi-opensvn-mcp/0.2.0/artifacts/claude/server.json +0 -12
- package/extensions/zhuiyi-opensvn-mcp/0.2.0/artifacts/codex/config.toml +0 -9
- package/extensions/zhuiyi-opensvn-mcp/0.2.0/artifacts/gitignore +0 -2
- package/extensions/zhuiyi-opensvn-mcp/0.2.0/init.js +0 -53
- package/extensions/zhuiyi-opensvn-mcp/0.2.0/lib/archive.js +0 -200
- package/extensions/zhuiyi-opensvn-mcp/0.2.0/manifest.json +0 -40
- package/extensions/zhuiyi-opensvn-mcp/0.2.0/mcp-launcher.js +0 -69
- package/extensions/zhuiyi-opensvn-mcp/0.2.0/release.json +0 -15
- /package/extensions/{.system/codew-workspace-guard → codew-workspace-guard}/1.0.0/assets/claude/add-projects.md +0 -0
- /package/extensions/{.system/codew-workspace-guard → codew-workspace-guard}/1.0.0/assets/codew-add-projects.SKILL.md +0 -0
- /package/extensions/{.system/codew-workspace-guard → codew-workspace-guard}/1.0.0/assets/codew-resolve-branch.SKILL.md +0 -0
- /package/extensions/{.system/codew-workspace-guard → codew-workspace-guard}/1.0.0/assets/codex/add-projects-openai.yaml +0 -0
- /package/extensions/{.system/codew-workspace-guard → codew-workspace-guard}/1.0.0/assets/codex/resolve-branch-openai.yaml +0 -0
- /package/extensions/{.system/codew-workspace-guard → codew-workspace-guard}/1.0.0/assets/templates/WORKSPACE_GUARD.md.template +0 -0
- /package/extensions/{.system/codew-workspace-guard → codew-workspace-guard}/1.0.0/init.js +0 -0
- /package/extensions/{.system/codew-workspace-guard → codew-workspace-guard}/1.0.0/manifest.json +0 -0
package/README.md
CHANGED
|
@@ -45,23 +45,16 @@ Initialization writes only Workspace-owned state and integrations:
|
|
|
45
45
|
|
|
46
46
|
It does not create `openspec/`, install native `/opsx` commands, or install native `openspec-*` skills.
|
|
47
47
|
|
|
48
|
-
### Experimental
|
|
48
|
+
### Experimental extensions
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
Ordinary extensions are published to the configured `@codew-ext` Nexus Registry and installed into the verified local Extension Store. Interactive `init` opens the shared Nexus picker: type in the search field, press Tab or Enter to focus the list, use Space to select, ←/→ to move between the visible pages, and press Enter to confirm (ESC cancels without changes). The picker marks installed latest versions as disabled and offers installed older versions as updates. If Nexus is unavailable or a page request fails, the ordinary-extension step can be skipped while core initialization continues. The package-local `extensions/` directory contains only the system-managed `codew-workspace-guard`:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
codew init . --extensions
|
|
54
|
-
codew init . --extensions zhuiyi-opensvn-mcp --yes
|
|
53
|
+
codew init . --extensions monitor --yes
|
|
55
54
|
codew init . --extensions none --yes
|
|
56
55
|
codew extension install
|
|
57
|
-
codew extension install
|
|
58
|
-
codew extension
|
|
59
|
-
codew extension install zhuiyi-jira-prd-analysis --yes
|
|
60
|
-
codew extension install zhuiyi-jira-task-breakdown --yes
|
|
61
|
-
codew extension install zhuiyi-jira-issue-fix-summary --yes
|
|
62
|
-
codew extension install zhuiyi-guangda-coding-spec --yes
|
|
63
|
-
codew extension uninstall zhuiyi-jira-mcp --yes
|
|
64
|
-
codew extension uninstall zhuiyi-jira-prd-analysis --yes
|
|
56
|
+
codew extension install monitor --yes
|
|
57
|
+
codew extension uninstall monitor --yes
|
|
65
58
|
codew extension search jira --json
|
|
66
59
|
codew extension info zhuiyi-jira-mcp --json
|
|
67
60
|
codew extension install zhuiyi-jira-mcp --version 1.1.0 --yes
|
|
@@ -71,29 +64,21 @@ codew extension upgrade zhuiyi-jira-mcp --yes
|
|
|
71
64
|
|
|
72
65
|
The Workspace operation lock shared by init, extension install, and extension uninstall is configured in the Code Workspace project's `.env` (not in the target Workspace). `CODE_WORKSPACE_INIT_LOCK_UPDATE_MS` defaults to `5000`, and `CODE_WORKSPACE_INIT_LOCK_STALE_MS` defaults to `30000`; process environment variables take precedence. See `.env.example` for the project configuration names.
|
|
73
66
|
|
|
74
|
-
Users select names, not `name@version` positions. A default install resolves the highest compatible stable, non-deprecated version
|
|
67
|
+
Users select names, not `name@version` positions. A default install resolves the highest compatible stable, non-deprecated version from the configured `@codew-ext` Nexus Registry and the verified local Store. A single-target install may instead pass an exact SemVer with `--version`; prereleases require that exact form, and a deprecated version additionally requires `--allow-deprecated`. `--offline` disables Registry access and resolves only local facts. `codew-workspace-guard` is the system extension containing the Workspace Guard, `codew-add-projects`, and `codew-resolve-branch`: `init` installs or upgrades it automatically, it is hidden from extension selection, and it cannot be managed through `extension install/uninstall/upgrade`. A new non-interactive Workspace installs no ordinary extensions unless `--extensions` is provided; `init` does not implicitly query Nexus. `none` skips ordinary extension work and does not uninstall existing artifacts or disable system extension processing.
|
|
75
68
|
|
|
76
|
-
`extension install` does not rerun core Workspace initialization. In JSON, non-TTY, or `--yes` mode, at least one extension name is required. Multiple names are installed in order with one confirmation boundary and independent transactions; any failure makes the install command fail while later extensions still run. When the Registry is configured and a default resolution cannot obtain the remote metadata, installation fails before Workspace writes instead of silently using an older
|
|
69
|
+
`extension install` does not rerun core Workspace initialization. In JSON, non-TTY, or `--yes` mode, at least one extension name is required. Multiple names are installed in order with one confirmation boundary and independent transactions; any failure makes the install command fail while later extensions still run. When the Registry is configured and a default resolution cannot obtain the remote metadata, installation fails before Workspace writes instead of silently using an older package.
|
|
77
70
|
|
|
78
|
-
`extension search`
|
|
71
|
+
`extension info` remains a Workspace-independent Registry read. In a TTY, `extension search` opens the same paged picker as `init`; selecting an uninstalled extension installs it, while selecting an outdated installed extension updates it. The picker hides system extensions and disables installed latest versions. JSON, non-TTY, or `--yes` search remains read-only and returns the Registry result envelope. `extension upgrade` accepts one or more installed ordinary extensions, freezes the default target for each, confirms once, and reuses the same per-extension transaction and rollback as install. An already-current target is skipped.
|
|
79
72
|
|
|
80
73
|
`extension pack` creates a Nexus/npm-ready `codew-ext-<extension-id>-<version>.tgz` from an extension package directory:
|
|
81
74
|
|
|
82
75
|
```bash
|
|
83
|
-
code-workspace extension pack
|
|
76
|
+
code-workspace extension pack /path/to/extension/1.0.0 --output dist/extensions --json
|
|
84
77
|
```
|
|
85
78
|
|
|
86
79
|
The command is Workspace-independent, creates a missing output directory recursively, never executes extension or npm lifecycle code, and reopens the generated tarball to verify the npm envelope, manifest, entry digest, and unchanged `packageSha256` before atomically committing it. Existing outputs are never overwritten. It does not publish or store Registry credentials; CI can pass the verified tarball to `npm publish --registry`.
|
|
87
80
|
|
|
88
|
-
The
|
|
89
|
-
|
|
90
|
-
The bundled `zhuiyi-jira-prd-analysis`, `zhuiyi-jira-task-breakdown`, and `zhuiyi-jira-issue-fix-summary` extensions install optional Codex and Claude Code skill files. They are independent extensions, are not installed by core `init` or `update`, and can be uninstalled separately. Install `zhuiyi-jira-mcp` separately when they need Jira access.
|
|
91
|
-
|
|
92
|
-
The bundled `zhuiyi-guangda-coding-spec` extension installs a Codex and Claude Code skill that applies the China Everbright Bank research, security, testing, and release rules to Guangda customer projects. It is independent of core `init` and `update`, can be uninstalled separately, and requires no MCP service.
|
|
93
|
-
|
|
94
|
-
The MCP runtime packages are not copied into the Workspace. Only their configuration is stored there; a launcher prepares and caches each package on demand in the user-level Extension Store.
|
|
95
|
-
|
|
96
|
-
`zhuiyi-opensvn-mcp` accesses OpenSVN static resources through `opssvn.in.wezhuiyi.com`. Its bundled configuration sets `SVN_OUTPUT_DIR` to `.mcp-cache-opensvn`, requires `SVN_AUTHORIZATION`, and defaults `SVN_MAX_RESOURCES` to `200`; credentials are not stored in the extension package and should be supplied through local configuration or the runtime environment.
|
|
81
|
+
The system-managed `codew-workspace-guard` extension is installed or upgraded automatically by `init`; it is hidden from ordinary extension selection and cannot be installed, upgraded, or uninstalled manually. Ordinary extension packages are downloaded from Nexus, verified, and cached in the user-level Extension Store.
|
|
97
82
|
|
|
98
83
|
Extension entries run in separate Node processes and generate files in temporary staging directories. The host rejects undeclared, missing, symbolic-link, non-file, path-escaping, conflicting, and checksum-mismatched artifacts before transactionally installing them. Per-Workspace state is stored in `.codew/ext-manifest.json`. A failed extension is reported as a warning and does not roll back successful core initialization or stop later extensions; a failed upgrade restores and retains the previous installed version.
|
|
99
84
|
|
|
@@ -104,7 +89,7 @@ verified by Code Workspace; extensions never patch the real Workspace directly.
|
|
|
104
89
|
recorded installed state and does not execute extension code. Unknown changes to extension-owned
|
|
105
90
|
files or contributions stop the operation instead of being overwritten.
|
|
106
91
|
|
|
107
|
-
This is fault isolation, not a malicious-code security sandbox. The experimental release trusts extension code shipped with Code Workspace
|
|
92
|
+
This is fault isolation, not a malicious-code security sandbox. The experimental release trusts system extension code shipped with Code Workspace and ordinary extension code downloaded from the configured company Nexus after archive, identity, manifest, entry, runtime, and package-digest verification. External extension directories, dependencies, arbitrary patches, force uninstall, disable commands, and automatic extension updates through `codew update` are not supported. The developer contract is in `docs/extensions.md`; a step-by-step guide is in `docs/extension-development/extension-development-guide.zh-CN.md`.
|
|
108
93
|
|
|
109
94
|
## Register projects
|
|
110
95
|
|
|
@@ -211,7 +196,7 @@ code-workspace language --json
|
|
|
211
196
|
|
|
212
197
|
## Monitor
|
|
213
198
|
|
|
214
|
-
Monitor
|
|
199
|
+
Monitor is an ordinary Nexus extension. Install it into a workspace and run its dashboard through the generic extension runtime:
|
|
215
200
|
|
|
216
201
|
```bash
|
|
217
202
|
code-workspace extension install monitor --yes
|
package/README.zh-CN.md
CHANGED
|
@@ -45,23 +45,16 @@ code-workspace init . \
|
|
|
45
45
|
|
|
46
46
|
它不会创建 `openspec/`,不会安装原生 `/opsx` 命令,也不会安装原生 `openspec-*` Skill。
|
|
47
47
|
|
|
48
|
-
###
|
|
48
|
+
### 试验性扩展
|
|
49
49
|
|
|
50
|
-
`init`
|
|
50
|
+
普通扩展发布到配置的 `@codew-ext` Nexus Registry,并安装到经过验证的本地 Extension Store。`init` 和独立安装命令从 Nexus/Store 选择普通扩展;npm 包内的 `extensions/` 目录只保留系统扩展 `codew-workspace-guard`。交互选择按 ESC 可无修改退出:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
codew init . --extensions
|
|
54
|
-
codew init . --extensions zhuiyi-opensvn-mcp --yes
|
|
53
|
+
codew init . --extensions monitor --yes
|
|
55
54
|
codew init . --extensions none --yes
|
|
56
55
|
codew extension install
|
|
57
|
-
codew extension install
|
|
58
|
-
codew extension
|
|
59
|
-
codew extension install zhuiyi-jira-prd-analysis --yes
|
|
60
|
-
codew extension install zhuiyi-jira-task-breakdown --yes
|
|
61
|
-
codew extension install zhuiyi-jira-issue-fix-summary --yes
|
|
62
|
-
codew extension install zhuiyi-guangda-coding-spec --yes
|
|
63
|
-
codew extension uninstall zhuiyi-jira-mcp --yes
|
|
64
|
-
codew extension uninstall zhuiyi-jira-prd-analysis --yes
|
|
56
|
+
codew extension install monitor --yes
|
|
57
|
+
codew extension uninstall monitor --yes
|
|
65
58
|
codew extension search jira --json
|
|
66
59
|
codew extension info zhuiyi-jira-mcp --json
|
|
67
60
|
codew extension install zhuiyi-jira-mcp --version 1.1.0 --yes
|
|
@@ -71,29 +64,21 @@ codew extension upgrade zhuiyi-jira-mcp --yes
|
|
|
71
64
|
|
|
72
65
|
`init`、扩展安装和扩展卸载共享的 Workspace 操作锁配置在 Code Workspace 项目自身的 `.env` 中(不在目标 Workspace 中)。`CODE_WORKSPACE_INIT_LOCK_UPDATE_MS` 默认值为 `5000`,`CODE_WORKSPACE_INIT_LOCK_STALE_MS` 默认值为 `30000`;进程环境变量优先于 `.env`。配置项名称见 `.env.example`。
|
|
73
66
|
|
|
74
|
-
用户选择扩展名,不使用 `name@version`
|
|
67
|
+
用户选择扩展名,不使用 `name@version` 位置语法。默认安装会从已配置的 `@codew-ext` Nexus Registry 和已验证本地 Store 中选择最高的兼容、稳定、非 deprecated 版本。单目标安装可用 `--version` 指定精确 SemVer; prerelease 必须通过精确版本请求,deprecated 版本还必须加 `--allow-deprecated`。`--offline` 禁止 Registry 访问,只基于本地事实解析。`codew-workspace-guard` 是包含 Workspace Guard、`codew-add-projects` 和 `codew-resolve-branch` 的系统扩展,由 `init` 自动安装或升级,不出现在扩展选择列表中,也不能通过 `extension install/uninstall/upgrade` 手动管理。新 Workspace 非交互初始化时,未传 `--extensions` 就不安装普通扩展;`init` 不会隐式查询 Nexus。`none` 只跳过本次普通扩展初始化,不会卸载已有制品,也不会取消系统扩展处理。
|
|
75
68
|
|
|
76
|
-
`extension install` 不会重新执行 Workspace 核心初始化。在 JSON、非 TTY 或 `--yes` 模式下,必须至少提供一个扩展名。多个名称按顺序安装,只确认一次且各自使用独立事务;任一扩展失败会使安装命令失败,但后续扩展仍会继续执行。Registry 已配置且默认解析无法取得远端 metadata 时,安装会在 Workspace
|
|
69
|
+
`extension install` 不会重新执行 Workspace 核心初始化。在 JSON、非 TTY 或 `--yes` 模式下,必须至少提供一个扩展名。多个名称按顺序安装,只确认一次且各自使用独立事务;任一扩展失败会使安装命令失败,但后续扩展仍会继续执行。Registry 已配置且默认解析无法取得远端 metadata 时,安装会在 Workspace 写入前失败,不会静默选择旧包。
|
|
77
70
|
|
|
78
|
-
`extension search`
|
|
71
|
+
`extension info` 仍是与 Workspace 无关的 Registry 读取命令。在 TTY 中,`extension search` 会打开与 `init` 相同的分页扩展选择器;选择未安装扩展会执行安装,选择已安装但过期的扩展会执行更新。系统扩展不会出现在列表中,已是最新版的扩展不可选择。JSON、非 TTY 或使用 `--yes` 时,search 保持只读并返回 Registry 结果 envelope。`extension upgrade` 接受一个或多个已安装普通扩展,逐个冻结默认目标,统一确认后复用安装的逐扩展事务和回滚;已是当前版本的目标返回 skipped。
|
|
79
72
|
|
|
80
73
|
`extension pack` 可以从扩展包目录生成可交给 Nexus/npm 的 `codew-ext-<extension-id>-<version>.tgz`:
|
|
81
74
|
|
|
82
75
|
```bash
|
|
83
|
-
code-workspace extension pack
|
|
76
|
+
code-workspace extension pack /path/to/extension/1.1.0 --output dist/extensions --json
|
|
84
77
|
```
|
|
85
78
|
|
|
86
79
|
该命令与 Workspace 无关,会在输出目录缺失时递归创建,不执行扩展入口或任何 npm 生命周期脚本;生成 tarball 后会重新读取并验证 npm envelope、manifest、入口摘要和未变化的 `packageSha256`,再原子提交。目标文件已存在时拒绝覆盖。命令本身不发布、不保存 Registry 凭证;CI 可将已验证的 tarball 交给 `npm publish --registry`。
|
|
87
80
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
随包还提供 `zhuiyi-jira-prd-analysis`、`zhuiyi-jira-task-breakdown` 和 `zhuiyi-jira-issue-fix-summary` 三个 Skill 扩展,用于安装可选的 Codex 与 Claude Code Skill 文件。它们不随核心 `init` 或 `update` 安装,可以独立安装和卸载;需要 Jira 访问时另行安装 `zhuiyi-jira-mcp`。
|
|
91
|
-
|
|
92
|
-
随包还提供 `zhuiyi-guangda-coding-spec` Skill 扩展,用于在光大客户项目按风险域落实研发、安全、测试与投产规范。它同样不随核心 `init` 或 `update` 安装,可以独立安装和卸载,且不依赖任何 MCP 服务。
|
|
93
|
-
|
|
94
|
-
两个 MCP 的运行包不会复制到 Workspace;Workspace 只保存 MCP 配置,运行包由用户级 Extension Store 中的 launcher 按需缓存。
|
|
95
|
-
|
|
96
|
-
`zhuiyi-opensvn-mcp` 使用 `opssvn.in.wezhuiyi.com` 访问 OpenSVN 静态资源。内置配置将 `SVN_OUTPUT_DIR` 设为 `.mcp-cache-opensvn`,运行时必须提供 `SVN_AUTHORIZATION`,`SVN_MAX_RESOURCES` 默认为 `200`;认证信息不会写入扩展包,应通过本地配置或运行环境注入。
|
|
81
|
+
系统管理的 `codew-workspace-guard` 由 `init` 自动安装或升级,不出现在普通扩展选择列表中,也不能手动安装、升级或卸载。普通扩展包从 Nexus 下载、验证,并缓存到用户级 Extension Store。
|
|
97
82
|
|
|
98
83
|
扩展入口在独立 Node 进程中运行,只向临时 staging 目录生成文件。Host 会在事务安装前拒绝未声明、缺失、符号链接、非文件、路径逃逸、目标冲突和 hash 不匹配的制品。Workspace 状态存放在 `.codew/ext-manifest.json`。扩展失败以 warning 报告,不回滚已成功的核心初始化,也不阻止后续扩展;升级失败会恢复并保留旧的已安装版本。
|
|
99
84
|
|
|
@@ -102,7 +87,7 @@ code-workspace extension pack extensions/zhuiyi-jira-mcp/1.1.0 --output dist/ext
|
|
|
102
87
|
Workspace 合成和验证,扩展不会直接 patch 真实 Workspace。卸载只使用已安装状态,不执行
|
|
103
88
|
扩展代码;扩展所有的文件或贡献存在未知修改时会拒绝覆盖或删除。
|
|
104
89
|
|
|
105
|
-
|
|
90
|
+
这是故障隔离,不是恶意代码安全沙箱。试验版本信任随 Code Workspace 发布的系统扩展代码,以及从已配置公司 Nexus 下载并通过归档、身份、manifest、入口、runtime 和 package digest 验证的普通扩展包。外部扩展目录、扩展依赖、任意 patch、强制卸载、禁用命令和通过 `codew update` 自动更新扩展仍不支持。开发契约见 `docs/extensions.zh-CN.md`;从目录结构到打包发布的完整流程见 `docs/extension-development/extension-development-guide.zh-CN.md`。
|
|
106
91
|
|
|
107
92
|
## 注册项目
|
|
108
93
|
|
|
@@ -209,7 +194,7 @@ code-workspace language --json
|
|
|
209
194
|
|
|
210
195
|
## 监控
|
|
211
196
|
|
|
212
|
-
|
|
197
|
+
监控以 Nexus 中的 `monitor` 扩展提供。先在 Workspace 中安装扩展,再通过通用扩展运行时启动面板:
|
|
213
198
|
|
|
214
199
|
```bash
|
|
215
200
|
code-workspace extension install monitor --yes
|
package/artifacts/manifest.json
CHANGED
|
@@ -30,7 +30,7 @@ Host 不理解 Jira、npm、tar.gz 或某个具体 Agent 的业务含义,但
|
|
|
30
30
|
|
|
31
31
|
- 不执行来自任意第三方的不可信扩展。
|
|
32
32
|
- 不提供安全沙箱、容器或操作系统级文件和网络隔离。
|
|
33
|
-
-
|
|
33
|
+
- 普通扩展通过配置的 Nexus Registry 发现和安装;系统扩展仍随 Host 发布。
|
|
34
34
|
- 不提供任意 Workspace 外部副作用及其补偿机制。
|
|
35
35
|
- 不允许扩展自定义卸载脚本。
|
|
36
36
|
- 不负责管理扩展运行后产生的业务数据,例如 Jira 附件。
|
|
@@ -62,7 +62,7 @@ Host 是 Code Workspace 中负责扩展生命周期治理的核心实现。Host
|
|
|
62
62
|
|
|
63
63
|
### 4.2 扩展仓库
|
|
64
64
|
|
|
65
|
-
扩展仓库是 Code Workspace
|
|
65
|
+
扩展仓库是 Code Workspace 发布包中用于保存系统扩展的目录集合。普通扩展不从该目录发现,而是通过配置的 Nexus Registry 和已验证 Extension Store 获取。
|
|
66
66
|
|
|
67
67
|
扩展还可以通过 manifest 的 `hooks` 声明抽象 Workspace Hook。Hook 声明由 Host 通过
|
|
68
68
|
Codex/Claude adaptor 转换为原生配置,并随扩展安装、升级和卸载动态插拔;扩展不得把
|
|
@@ -35,7 +35,7 @@ Host 负责确认、校验、事务提交、状态记录、升级和卸载。
|
|
|
35
35
|
|
|
36
36
|
## 2. 包目录
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
系统扩展使用固定版本目录;普通扩展在独立仓库中维护并发布到 Nexus:
|
|
39
39
|
|
|
40
40
|
```text
|
|
41
41
|
extensions/<extension-id>/<version>/
|
|
@@ -248,7 +248,7 @@ Host 拒绝:
|
|
|
248
248
|
|
|
249
249
|
## 5. 本地验证与安装测试
|
|
250
250
|
|
|
251
|
-
在临时 Workspace
|
|
251
|
+
在临时 Workspace 安装已发布扩展:
|
|
252
252
|
|
|
253
253
|
```bash
|
|
254
254
|
codew init /tmp/example-workspace --tools codex --extensions example-extension --yes
|
|
@@ -292,7 +292,7 @@ codew extension install example-extension --offline --version 1.0.0 --yes
|
|
|
292
292
|
codew extension upgrade example-extension --yes
|
|
293
293
|
```
|
|
294
294
|
|
|
295
|
-
默认安装选择最高的兼容、稳定、非 deprecated 版本;`name@version` 位置语法和 SemVer range 不支持。prerelease 必须通过精确 `--version` 请求;deprecated 版本必须同时使用 `--version` 和 `--allow-deprecated`。`--offline`
|
|
295
|
+
默认安装选择最高的兼容、稳定、非 deprecated 版本;`name@version` 位置语法和 SemVer range 不支持。prerelease 必须通过精确 `--version` 请求;deprecated 版本必须同时使用 `--version` 和 `--allow-deprecated`。`--offline` 禁止网络,只使用已验证 Store。`init` 只在用户明确选择普通扩展时查询 Nexus;系统扩展仍由 Host 自动管理。
|
|
296
296
|
|
|
297
297
|
## 6. npm/Nexus 打包
|
|
298
298
|
|
|
@@ -304,7 +304,7 @@ code-workspace extension pack extensions/example-extension/1.0.0 --output dist/e
|
|
|
304
304
|
|
|
305
305
|
输出目录缺失时会递归创建。目标文件已存在时命令拒绝覆盖;重复打包前使用新的输出目录,或清理上一次构建产物。
|
|
306
306
|
|
|
307
|
-
|
|
307
|
+
扩展仓库可以一次打包并校验全部普通版本(系统扩展会被排除):
|
|
308
308
|
|
|
309
309
|
```bash
|
|
310
310
|
npm run pack:extensions
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
本文面向需要把 Code Workspace 扩展发布到公司 Nexus 的开发者,覆盖从登录、打包、上传到远端验证的完整流程。
|
|
4
4
|
|
|
5
|
-
本文只描述 **普通扩展**
|
|
5
|
+
本文只描述 **普通扩展** 的发布。系统扩展由 Host 随包管理,不进入 Nexus 普通扩展发布集合;不要把 `extensions/codew-workspace-guard/` 下的包上传到 Registry。
|
|
6
6
|
|
|
7
7
|
## 1. 前置条件
|
|
8
8
|
|
|
@@ -37,9 +37,7 @@ npm config set @codew-ext:registry "$NEXUS"
|
|
|
37
37
|
登录:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
npm login
|
|
41
|
-
--auth-type=legacy \
|
|
42
|
-
--registry="$NEXUS"
|
|
40
|
+
npm login --auth-type=legacy --registry="$NEXUS"
|
|
43
41
|
```
|
|
44
42
|
|
|
45
43
|
确认登录状态:
|
package/package.json
CHANGED
package/src/cli/commands/ext.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
const { commandResult, success } = require("../result");
|
|
2
2
|
const { executeExtensionRuntime } = require("../../core/extension-runtime");
|
|
3
|
-
const {
|
|
4
|
-
const { discoverExtensions } = require("../../core/extensions");
|
|
3
|
+
const { defaultExtensionStoreRoot } = require("../../core/extension-store");
|
|
5
4
|
|
|
6
5
|
async function executeExt(invocation) {
|
|
7
6
|
const id = invocation.args[0];
|
|
8
7
|
const argv = invocation.args.slice(1);
|
|
9
8
|
const extensionStoreRoot = invocation.dependencies?.extensionStoreRoot || defaultExtensionStoreRoot();
|
|
10
|
-
const builtin = discoverExtensions({ tolerant: true }).catalog.find((entry) => entry.id === id)?.latestSupported;
|
|
11
|
-
if (builtin) {
|
|
12
|
-
ensureStoredExtensionPackage({ storeRoot: extensionStoreRoot, sourceRoot: builtin.sourceRoot, source: "builtin" });
|
|
13
|
-
}
|
|
14
9
|
const runtime = await executeExtensionRuntime({
|
|
15
10
|
id,
|
|
16
11
|
argv,
|
|
@@ -3,7 +3,6 @@ const { WorkspaceError } = require("../../core/errors");
|
|
|
3
3
|
const {
|
|
4
4
|
applyExtensionUninstall,
|
|
5
5
|
EXTENSION_STATE_FILE,
|
|
6
|
-
discoverExtensions,
|
|
7
6
|
discoverSystemExtensions,
|
|
8
7
|
inspectExtensionState,
|
|
9
8
|
normalizeExtensionNames,
|
|
@@ -23,6 +22,7 @@ const {
|
|
|
23
22
|
} = require("../../core/extension-registry-lifecycle");
|
|
24
23
|
const { resolveWorkspaceTools } = require("../../core/tools");
|
|
25
24
|
const { createInteractiveUi, formatExtensionChoice } = require("../../init/ui");
|
|
25
|
+
const { createRegistryExtensionPicker } = require("../../init/registry-picker");
|
|
26
26
|
const { confirm } = require("../confirmation");
|
|
27
27
|
const { selectionResult, success } = require("../result");
|
|
28
28
|
|
|
@@ -35,11 +35,13 @@ function formatUninstallPlan(plan) {
|
|
|
35
35
|
].join("\n");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function formatInstallPlan(plans) {
|
|
38
|
+
function formatInstallPlan(plans, options = {}) {
|
|
39
|
+
const actionFor = options.actionFor || (() => "install");
|
|
40
|
+
const title = options.title || `Install ${plans.length} extension${plans.length === 1 ? "" : "s"}:`;
|
|
39
41
|
return [
|
|
40
|
-
|
|
42
|
+
title,
|
|
41
43
|
...plans.flatMap((plan) => [
|
|
42
|
-
` ${plan.id}@${plan.version} [${plan.source || "local"}] [Extension Spec ${plan.extensionSpecVersion}]`,
|
|
44
|
+
` ${actionFor(plan) === "update" ? "Update" : "Install"} ${plan.id}@${plan.version} [${plan.source || "local"}] [Extension Spec ${plan.extensionSpecVersion}]`,
|
|
43
45
|
` PACKAGE ${plan.packageSha256}`,
|
|
44
46
|
...(plan.capabilities.networkHosts || []).map((host) => ` NETWORK https://${host}`),
|
|
45
47
|
...plan.artifacts.map((artifact) => ` WRITE ${artifact.target} (${artifact.kind})`),
|
|
@@ -68,28 +70,6 @@ function formatRegistryInfoText(result) {
|
|
|
68
70
|
return lines.join("\n");
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
async function collectExtensionInstallSelection(catalog, state, options = {}) {
|
|
72
|
-
const ui = options.ui || await createInteractiveUi({
|
|
73
|
-
...options,
|
|
74
|
-
cancelCode: "EXTENSION_INSTALL_CANCELLED",
|
|
75
|
-
cancelMessage: "Extension installation cancelled. No changes were made.",
|
|
76
|
-
});
|
|
77
|
-
const installed = new Set(Object.entries(state.extensions || {}).filter(([, value]) => value.installed).map(([id]) => id));
|
|
78
|
-
const choices = catalog.map((entry) => ({
|
|
79
|
-
value: entry.id,
|
|
80
|
-
label: formatExtensionChoice(entry, { includeId: true, installed: installed.has(entry.id) }),
|
|
81
|
-
...(entry.latestSupported ? {} : { disabled: true }),
|
|
82
|
-
}));
|
|
83
|
-
ui.intro("Code Workspace extensions");
|
|
84
|
-
if (choices.length === 0) {
|
|
85
|
-
ui.close("No built-in extensions are available.");
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
const selected = await ui.multiselect("Extensions (select any)", choices, []);
|
|
89
|
-
ui.close(selected.length > 0 ? "Extension selection ready." : "No extensions selected.");
|
|
90
|
-
return normalizeExtensionNames(selected);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
73
|
async function collectRegistryExtensionInstallSelection(choices, state, options = {}) {
|
|
94
74
|
const ui = options.ui || await createInteractiveUi({
|
|
95
75
|
...options,
|
|
@@ -147,15 +127,17 @@ function lifecycleResultEntry(entry, action) {
|
|
|
147
127
|
}
|
|
148
128
|
|
|
149
129
|
function lifecycleResultText(results, action) {
|
|
130
|
+
const actionFor = typeof action === "function" ? action : () => action;
|
|
150
131
|
const lines = results.map((entry) => entry.status === "installed"
|
|
151
|
-
? `${
|
|
132
|
+
? `${actionFor(entry) === "upgrade" || actionFor(entry) === "update" ? "Updated" : "Installed"} ${entry.id}@${entry.version}.`
|
|
152
133
|
: entry.status === "skipped"
|
|
153
134
|
? `Skipped ${entry.id}@${entry.version}: already current.`
|
|
154
135
|
: `Failed ${entry.id}${entry.version ? `@${entry.version}` : ""}: ${entry.message}`);
|
|
155
136
|
const installed = results.filter((entry) => entry.status === "installed").length;
|
|
156
137
|
const skipped = results.filter((entry) => entry.status === "skipped").length;
|
|
157
138
|
const failed = results.filter((entry) => entry.status === "failed").length;
|
|
158
|
-
|
|
139
|
+
const hasUpdate = results.some((entry) => actionFor(entry) === "upgrade" || actionFor(entry) === "update");
|
|
140
|
+
lines.push(`Extensions: ${installed} ${hasUpdate ? "applied" : "installed"}, ${skipped} skipped, ${failed} failed.`);
|
|
159
141
|
return lines.join("\n");
|
|
160
142
|
}
|
|
161
143
|
|
|
@@ -198,7 +180,6 @@ async function executeExtensionInstall(invocation) {
|
|
|
198
180
|
});
|
|
199
181
|
}
|
|
200
182
|
|
|
201
|
-
const catalogResult = discoverExtensions({ tolerant: true, ...(dependencies.extensionsRoot ? { extensionsRoot: dependencies.extensionsRoot } : {}) });
|
|
202
183
|
const systemCatalogResult = discoverSystemExtensions({ tolerant: true, ...(dependencies.extensionsRoot ? { extensionsRoot: dependencies.extensionsRoot } : {}) });
|
|
203
184
|
const systemIds = new Set(systemCatalogResult.catalog.map((entry) => entry.id));
|
|
204
185
|
const requestedSystemId = requested?.find((id) => systemIds.has(id));
|
|
@@ -217,8 +198,6 @@ async function executeExtensionInstall(invocation) {
|
|
|
217
198
|
offline: invocation.options.offline,
|
|
218
199
|
});
|
|
219
200
|
requested = await dependencies.collectRegistryExtensionInstallSelection(choices, stateInspection.state, dependencies);
|
|
220
|
-
} else if (dependencies.collectExtensionInstallSelection) {
|
|
221
|
-
requested = await dependencies.collectExtensionInstallSelection(catalogResult.catalog, stateInspection.state, dependencies);
|
|
222
201
|
} else {
|
|
223
202
|
const choices = await listRegistryExtensionChoices({
|
|
224
203
|
...dependencies,
|
|
@@ -299,11 +278,109 @@ async function executeExtensionInstall(invocation) {
|
|
|
299
278
|
}
|
|
300
279
|
|
|
301
280
|
async function executeExtensionSearch(invocation) {
|
|
302
|
-
const
|
|
281
|
+
const dependencies = invocation.dependencies || {};
|
|
282
|
+
const interactive = dependencies.interactive ?? (!invocation.options.json && invocation.options.yes !== true && process.stdin.isTTY && process.stdout.isTTY);
|
|
283
|
+
if (!interactive) {
|
|
284
|
+
const result = await searchRegistryExtensions({
|
|
285
|
+
query: invocation.args[0] || "",
|
|
286
|
+
...dependencies,
|
|
287
|
+
});
|
|
288
|
+
return success("extension.search", result, formatRegistrySearchText(result), result.diagnostics || []);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const workspace = invocation.config?.workspace;
|
|
292
|
+
if (!workspace) {
|
|
293
|
+
throw new WorkspaceError("WORKSPACE_CONFIG_REQUIRED", "Interactive extension search requires an initialized Workspace.", {
|
|
294
|
+
remediation: "Run `codew init <path>` first, or use `codew extension search <query> --json` for read-only Registry results.",
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
const stateInspection = inspectExtensionState(invocation.root);
|
|
298
|
+
const systemIds = new Set(discoverSystemExtensions({
|
|
299
|
+
tolerant: true,
|
|
300
|
+
...(dependencies.extensionsRoot ? { extensionsRoot: dependencies.extensionsRoot } : {}),
|
|
301
|
+
}).catalog.map((entry) => entry.id));
|
|
302
|
+
const ui = dependencies.ui || await createInteractiveUi({
|
|
303
|
+
...dependencies,
|
|
304
|
+
cancelCode: "EXTENSION_SEARCH_CANCELLED",
|
|
305
|
+
cancelMessage: "Extension search cancelled. No changes were made.",
|
|
306
|
+
});
|
|
307
|
+
ui.intro?.("Code Workspace extension search");
|
|
308
|
+
const selected = await createRegistryExtensionPicker({
|
|
309
|
+
dependencies,
|
|
310
|
+
state: stateInspection.state,
|
|
311
|
+
systemIds,
|
|
312
|
+
})({
|
|
313
|
+
ui,
|
|
303
314
|
query: invocation.args[0] || "",
|
|
304
|
-
...(invocation.dependencies || {}),
|
|
305
315
|
});
|
|
306
|
-
|
|
316
|
+
if (!selected.length) {
|
|
317
|
+
ui.close?.("No extensions selected. No changes were made.");
|
|
318
|
+
return success("extension.search", {
|
|
319
|
+
action: "skip",
|
|
320
|
+
scope: "selection",
|
|
321
|
+
requested: [],
|
|
322
|
+
results: [],
|
|
323
|
+
summary: { total: 0, succeeded: 0, skipped: 0, failed: 0 },
|
|
324
|
+
}, "No extensions selected. No changes were made.");
|
|
325
|
+
}
|
|
326
|
+
const requested = normalizeExtensionNames(selected.map((entry) => entry.id));
|
|
327
|
+
const actionById = new Map(selected.map((entry) => [entry.id, entry.action === "update" ? "update" : "install"]));
|
|
328
|
+
const tools = resolveWorkspaceTools({ state: loadState(invocation.root), manifestTools: loadInitManifest().tools }).tools;
|
|
329
|
+
const preparation = await prepareRegistryExtensionPlans({
|
|
330
|
+
...dependencies,
|
|
331
|
+
requested,
|
|
332
|
+
tools,
|
|
333
|
+
state: stateInspection.state,
|
|
334
|
+
stateError: stateInspection.error,
|
|
335
|
+
extensionsRoot: dependencies.extensionsRoot,
|
|
336
|
+
extensionStoreRoot: dependencies.extensionStoreRoot || defaultExtensionStoreRoot(),
|
|
337
|
+
provider: dependencies.nexusProvider,
|
|
338
|
+
});
|
|
339
|
+
if (preparation.plans.length > 0) {
|
|
340
|
+
const planText = formatInstallPlan(preparation.plans, {
|
|
341
|
+
title: `Apply ${preparation.plans.length} extension${preparation.plans.length === 1 ? "" : "s"}:`,
|
|
342
|
+
actionFor: (plan) => actionById.get(plan.id) || "install",
|
|
343
|
+
});
|
|
344
|
+
if (!(await (dependencies.confirm || confirm)(`${planText}\nContinue?`, invocation.options))) {
|
|
345
|
+
throw new WorkspaceError("CLI_CANCELLED", "Extension search selection cancelled.");
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const batch = runExtensionBatch(invocation.root, preparation.plans, (extension) => ({
|
|
349
|
+
schemaVersion: 1,
|
|
350
|
+
extensionSpecVersion: extension.extensionSpecVersion,
|
|
351
|
+
extension: { id: extension.id, version: extension.version },
|
|
352
|
+
workspace: { name: workspace.name, uuid: workspace.uuid, language: workspace.language },
|
|
353
|
+
tools,
|
|
354
|
+
}), {
|
|
355
|
+
requested,
|
|
356
|
+
preFailures: preparation.failures,
|
|
357
|
+
useExtensionStore: true,
|
|
358
|
+
extensionStoreRoot: dependencies.extensionStoreRoot || defaultExtensionStoreRoot(),
|
|
359
|
+
});
|
|
360
|
+
const failedIds = new Set(batch.results.filter((entry) => entry.status === "failed").map((entry) => entry.id));
|
|
361
|
+
const diagnostics = [
|
|
362
|
+
...preparation.diagnostics.filter((entry) => !entry.extension || !failedIds.has(entry.extension)),
|
|
363
|
+
...batch.results.filter((entry) => entry.status === "failed").map((entry) => ({
|
|
364
|
+
code: entry.code || "EXTENSION_SEARCH_APPLY_FAILED",
|
|
365
|
+
severity: "error",
|
|
366
|
+
message: entry.message || `Extension ${entry.id} failed to apply.`,
|
|
367
|
+
extension: entry.id,
|
|
368
|
+
version: entry.version,
|
|
369
|
+
})),
|
|
370
|
+
...batch.results.flatMap((entry) => (entry.warnings || []).map((warning) => ({
|
|
371
|
+
code: warning.code,
|
|
372
|
+
severity: "warning",
|
|
373
|
+
message: warning.message,
|
|
374
|
+
extension: entry.id,
|
|
375
|
+
version: entry.version,
|
|
376
|
+
}))),
|
|
377
|
+
];
|
|
378
|
+
const results = decorateBatchResults(batch, preparation.plans, invocation.options);
|
|
379
|
+
ui.close?.("Extension selection applied.");
|
|
380
|
+
return selectionResult("extension.search", requested, results.map((entry) => lifecycleResultEntry(entry, actionById.get(entry.id) || "install")), {
|
|
381
|
+
diagnostics,
|
|
382
|
+
text: lifecycleResultText(results, (entry) => actionById.get(entry.id) || "install"),
|
|
383
|
+
});
|
|
307
384
|
}
|
|
308
385
|
|
|
309
386
|
async function executeExtensionInfo(invocation) {
|
|
@@ -406,7 +483,17 @@ async function executeExtensionPack(invocation) {
|
|
|
406
483
|
async function executeExtension(invocation) {
|
|
407
484
|
const command = invocation.definition.path.join(".");
|
|
408
485
|
if (command === "extension.pack") return await executeExtensionPack(invocation);
|
|
409
|
-
if (command === "extension.search")
|
|
486
|
+
if (command === "extension.search") {
|
|
487
|
+
const dependencies = invocation.dependencies || {};
|
|
488
|
+
const interactive = dependencies.interactive ?? (!invocation.options.json && invocation.options.yes !== true && process.stdin.isTTY && process.stdout.isTTY);
|
|
489
|
+
if (!interactive) return await executeExtensionSearch(invocation);
|
|
490
|
+
const releaseLock = await (dependencies.acquireInitLock || acquireInitLock)(invocation.root);
|
|
491
|
+
try {
|
|
492
|
+
return await executeExtensionSearch(invocation);
|
|
493
|
+
} finally {
|
|
494
|
+
await releaseLock();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
410
497
|
if (command === "extension.info") return await executeExtensionInfo(invocation);
|
|
411
498
|
const releaseLock = await (invocation.dependencies?.acquireInitLock || acquireInitLock)(invocation.root);
|
|
412
499
|
try {
|
|
@@ -420,7 +507,6 @@ async function executeExtension(invocation) {
|
|
|
420
507
|
}
|
|
421
508
|
|
|
422
509
|
module.exports = {
|
|
423
|
-
collectExtensionInstallSelection,
|
|
424
510
|
collectRegistryExtensionInstallSelection,
|
|
425
511
|
executeExtension,
|
|
426
512
|
executeExtensionInstall,
|