@qmuse/qmuse-cli 1.0.13-dev.1 → 1.0.14-dev.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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "formatVersion": 1,
3
+ "packageName": "@alipay/qmuse-hono-cli",
4
+ "version": "1.0.0",
5
+ "sourceCommit": "cb29fc55dbe4af79e291ade1156ad1523359a1eb",
6
+ "sourceDirty": false,
7
+ "sourceTreeSha256": "9863b5919c524bc3c97b0bc3bbb18612a7f9a1f690fbcd28458d40a7f5d9cc53",
8
+ "sha256": "4e838be46da6720219c051cfc2d6f4a0cade5c8efc32739f5f228cad1230d227"
9
+ }
package/dist/hono.js ADDED
@@ -0,0 +1,35 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ function reportFailure(argv, message, code) {
5
+ if (argv.includes('--json')) {
6
+ process.stdout.write(`${JSON.stringify({ ok: false, command: null, project: process.cwd(), result: null,
7
+ diagnostics: [{ severity: 'error', code, message }] })}\n`);
8
+ }
9
+ else
10
+ process.stderr.write(`${message}\n`);
11
+ }
12
+ /** Forward the original argv and streams so the generator owns its JSON/exit contract. */
13
+ export async function runHono(argv) {
14
+ const runtime = fileURLToPath(new URL('../dist/hono/cli.cjs', import.meta.url));
15
+ if (!existsSync(runtime)) {
16
+ const message = 'Hono 生成器未包含在当前 CLI 中,请重新安装;源码开发先运行 npm run build。';
17
+ reportFailure(argv, message, 'HONO_RUNTIME_UNAVAILABLE');
18
+ return 3;
19
+ }
20
+ return new Promise(resolve => {
21
+ const child = spawn(process.execPath, [runtime, ...argv], { stdio: 'inherit' });
22
+ const interrupt = () => child.kill('SIGINT');
23
+ const terminate = () => child.kill('SIGTERM');
24
+ process.on('SIGINT', interrupt);
25
+ process.on('SIGTERM', terminate);
26
+ child.once('error', error => {
27
+ reportFailure(argv, `无法启动 Hono 生成器:${error.message}`, 'HONO_RUNTIME_START_FAILED');
28
+ });
29
+ child.once('close', (code, signal) => {
30
+ process.off('SIGINT', interrupt);
31
+ process.off('SIGTERM', terminate);
32
+ resolve(code ?? (signal === 'SIGINT' ? 130 : signal === 'SIGTERM' ? 143 : 3));
33
+ });
34
+ });
35
+ }
@@ -2,8 +2,11 @@ import { recordHttpResult, recordOperation } from '../operation-log.js';
2
2
  import { openAsBlob } from 'node:fs';
3
3
  import { apiCall, authHeaders, buildUrl, requestSourceHeaders, } from '../client.js';
4
4
  import { assertImportArchiveSize } from './limits.js';
5
- export async function assertTemplateProtocolSupported(cfg) {
5
+ export async function assertTemplateProtocolSupported(cfg, miniapp = false) {
6
6
  const result = await apiCall('GET', '/api/app-import/protocol', undefined, cfg);
7
+ if (miniapp && !result.data?.sourceTypes?.includes('TARO_REACT')) {
8
+ throw new Error('当前平台尚不支持 Taro 小程序导入,请升级服务端;未上传源码,不会降级为网页应用');
9
+ }
7
10
  if (!result.ok || !result.data?.templateProtocolVersions?.includes(2)) {
8
11
  throw new Error('当前平台尚不支持新模板架构协议,请等待服务端升级;未提交导入,不会降级为旧架构');
9
12
  }
@@ -194,6 +194,11 @@ export async function preflightImport(directory) {
194
194
  const dependencies = { ...packageJson.devDependencies, ...packageJson.dependencies };
195
195
  if (!dependencies.react)
196
196
  throw new Error('一期仅支持 React 项目');
197
+ const miniapp = Boolean(dependencies['@tarojs/taro']);
198
+ if (miniapp && (!files.some(file => /^src\/app\.config\.[jt]s$/u.test(file.relativePath)) ||
199
+ !files.some(file => /^config\/index\.[jt]s$/u.test(file.relativePath)))) {
200
+ throw new Error('Taro 小程序缺少 src/app.config.ts 或 config/index.ts(也支持 .js)');
201
+ }
197
202
  if (dependencies[UNSUPPORTED_PRIVATE_RUNTIME])
198
203
  rejectUnsupportedPrivateRuntime();
199
204
  const sourceHash = createHash('sha256');
@@ -238,6 +243,8 @@ export async function preflightImport(directory) {
238
243
  });
239
244
  return {
240
245
  rootDir,
246
+ appType: miniapp ? 'MINIAPP' : 'GENERAL',
247
+ sourceType: miniapp ? 'TARO_REACT' : 'REACT_VITE',
241
248
  title: packageJson.name?.trim() || path.basename(rootDir),
242
249
  sourceHash: sourceHash.digest('hex'),
243
250
  fileCount: files.length,
@@ -79,12 +79,12 @@ function parseManifest(value) {
79
79
  if (value.projectKind !== 'QMUSE_TEMPLATE' && value.projectKind !== 'EXTERNAL_REPOSITORY') {
80
80
  throw new Error(`${QMUSE_PROJECT_PATH} 的 projectKind 无效`);
81
81
  }
82
- if (value.appType !== 'GENERAL') {
83
- throw new Error(`${QMUSE_PROJECT_PATH} 的 appType 必须是 GENERAL`);
82
+ if (value.appType !== 'GENERAL' && value.appType !== 'MINIAPP') {
83
+ throw new Error(`${QMUSE_PROJECT_PATH} 的 appType 必须是 GENERAL 或 MINIAPP`);
84
84
  }
85
85
  const isTemplate = value.projectKind === 'QMUSE_TEMPLATE';
86
86
  if (value.schemaVersion === 2) {
87
- if (!isTemplate || !['LEGACY_DIRECT_V1', 'HONO_BACKEND_V1'].includes(String(value.cloudArchitecture))) {
87
+ if ((!isTemplate && value.appType !== 'MINIAPP') || !['LEGACY_DIRECT_V1', 'HONO_BACKEND_V1'].includes(String(value.cloudArchitecture))) {
88
88
  throw new Error(`${QMUSE_PROJECT_PATH} 的新模板协议必须声明有效的 cloudArchitecture`);
89
89
  }
90
90
  }
@@ -108,7 +108,7 @@ function parseManifest(value) {
108
108
  schemaVersion: value.schemaVersion,
109
109
  ...(value.schemaVersion === 2 ? { cloudArchitecture: value.cloudArchitecture } : {}),
110
110
  projectKind: value.projectKind,
111
- appType: 'GENERAL',
111
+ appType: value.appType,
112
112
  ...(isTemplate ? {
113
113
  packageVersion: Number(value.packageVersion),
114
114
  templateCommit: requireNonEmptyString(value.templateCommit, 'templateCommit'),
@@ -186,14 +186,15 @@ export async function saveManagedProject(context) {
186
186
  throw error;
187
187
  }
188
188
  }
189
- export function createExternalProject(rootDir, cfg, explicitEnvironment) {
189
+ export function createExternalProject(rootDir, cfg, explicitEnvironment, appType = 'GENERAL') {
190
190
  return {
191
191
  manifestPath: path.join(rootDir, QMUSE_PROJECT_PATH),
192
192
  environment: resolveProjectEnvironment(cfg, explicitEnvironment),
193
193
  manifest: {
194
- schemaVersion: 1,
194
+ schemaVersion: appType === 'MINIAPP' ? 2 : 1,
195
+ ...(appType === 'MINIAPP' ? { cloudArchitecture: 'HONO_BACKEND_V1' } : {}),
195
196
  projectKind: 'EXTERNAL_REPOSITORY',
196
- appType: 'GENERAL',
197
+ appType,
197
198
  targets: {},
198
199
  },
199
200
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qmuse/qmuse-cli",
3
- "version": "1.0.13-dev.1",
3
+ "version": "1.0.14-dev.1",
4
4
  "description": "QMuse 命令行客户端:在本地创建/导入 React 项目、与 QMuse 对话、拉取发布与云端调试",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,8 +10,10 @@
10
10
  "main": "./dist/cli.js",
11
11
  "scripts": {
12
12
  "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
13
- "build": "npm run clean && tsc -p tsconfig.json",
13
+ "build": "npm run clean && tsc -p tsconfig.json && node scripts/prepare-hono.mjs",
14
+ "hono:sync": "node scripts/sync-hono.mjs",
14
15
  "qmuse": "tsx src/cli.ts",
16
+ "pretest": "node scripts/prepare-hono.mjs",
15
17
  "test": "node --import tsx --test test/*.test.ts",
16
18
  "test:browser": "node --import tsx --test test/dev.browser.ts",
17
19
  "prepack": "npm run build",
@@ -13,8 +13,9 @@ description: 通过 QMuse 创建、导入、修改、调试、同步或发布应
13
13
 
14
14
  | 用户意图 | 入口 | 关键约束 |
15
15
  | --- | --- | --- |
16
- | 初始化新的本地 React 工程 | `qmuse create <dir>` | 创建本地模板;开发后用 import 创建远端应用 |
17
- | 将本地 React 工程导入 QMuse | `qmuse import <dir>` | 首次导入创建应用,已有绑定则更新同一应用 |
16
+ | 初始化新的本地 React 工程 | `qmuse create <dir>` | 新模板固定 Hono 架构;纯前端不创建云资源,开发后用 import 创建远端应用 |
17
+ | 为新模板添加云功能 | 项目内的 `qmuse-hono-cloud` Skill、`qmuse hono` | 按真实业务生成 Schema/后端;由 import 初始化和同步 |
18
+ | 将本地 React Web / Taro React 小程序导入 QMuse | `qmuse import <dir>` | 自动识别应用类型;首次创建、已有绑定则更新;ZIP 先解压 |
18
19
  | 修改已导入的应用 | 修改源码后 `qmuse import .` | 平台基线变化先处理同步冲突,再更新 |
19
20
  | 用自然语言创建应用 | `qmuse app create <web\|h5\|ai> --file <路径\|->` | web 网页、h5 移动页面、ai AI 应用;返回 appId、chatId 等身份 |
20
21
  | 继续已有应用的对话 | `qmuse chat send --app-id <id> --chat-id <id> --file <路径\|->` | send 必须传 appId 和 chatId;缺少会话时先查询历史 |
@@ -1,23 +1,29 @@
1
1
  # 云服务指南
2
2
 
3
- CLI 不复制云规则,只做两件事:识别当前应用的实际架构,然后把操作路由到现有权威入口。
3
+ 先识别项目来源与已保存的架构,再选择入口。已有应用沿用平台持久化的架构,不通过目录、函数名称或环境数量推断,也不自动迁移。
4
4
 
5
5
  ## 1. 识别当前架构
6
6
 
7
+ 新 `qmuse create` 模板的 `.qmuse/project.json` 为 `schemaVersion: 2`、`cloudArchitecture: HONO_BACKEND_V1`。优先读取项目内 `.agents/skills/qmuse-hono-cloud/SKILL.md`,按其流程调用 `qmuse hono init|validate|inspect|generate|check`。这些命令使用 CLI 内置的固定生成器,无需登录,不创建远端资源。
8
+
9
+ 纯前端项目直接开发和 import,不创建 `.qmuse-hono` 占位文件。需要云服务时先准备真实 Schema 和业务代码,再 `qmuse import . --cloud-mode QMUSE --confirm-cloud-service`;首次无需 appId。已有绑定继续更新同一应用。服务端不支持新协议时先升级,不降级到 Legacy。
10
+
11
+ 已有应用可查询:
12
+
7
13
  ```bash
8
14
  qmuse info --json # bindings + cloud 汇总(entitlement / environment)
9
15
  qmuse cloud status --app-id <appId> --json # effectiveMode、activationStatus、publishAllowed、canRetryInitialization
10
16
  ```
11
17
 
12
- - `SINGLE_ENV` / Legacy:应用没有开通多环境;云初始化与首版同步由导入或对话侧完成。
13
- - `MULTI_ENV`(Hono):`qmuse cloud enable` 之后再查询状态;已初始化失败用 `qmuse cloud environment retry --wait`。
18
+ `SINGLE_ENV` / `MULTI_ENV` 表示环境模式,不是 Hono / Legacy 架构判据。Legacy 保持原有 Schema/函数流程,不在其目录运行 Hono 初始化。Hono 初始化与首版同步复用 import;已有初始化失败时按诊断使用 `qmuse cloud environment retry --wait`。
14
19
 
15
20
  ## 2. 路由到现有入口
16
21
 
17
22
  | 判断 | 动作 |
18
23
  | --- | --- |
19
24
  | 权益预检(不伪装已开通) | `qmuse cloud init -d "<功能描述>"` |
20
- | 实际开通 | `qmuse cloud enable --app-id <appId>` |
25
+ | 新模板首次开通与同步 | 按项目 Skill 生成后执行 `qmuse import . --cloud-mode QMUSE --confirm-cloud-service` |
26
+ | 存储能力启用 | `qmuse cloud enable --app-id <appId>`;不能替代 Hono 首次导入 |
21
27
  | 状态/失败原因 | `qmuse cloud status --app-id <appId>` |
22
28
  | 初始化失败重试 | `qmuse cloud environment retry --app-id <appId> --wait` |
23
29
  | 公开连接与运行配置 | `qmuse cloud pull-env --app-id <appId> --output .qmuse/local-dev.json` |
@@ -28,5 +34,5 @@ qmuse cloud status --app-id <appId> --json # effectiveMode、activationStatus
28
34
  ## 3. 约束
29
35
 
30
36
  - 管理凭证只保留在 CLI 会话或服务端;`pull-env` 输出仅含公开配置。
31
- - 预检通过不等于初始化完成:以 `cloud status` 的 `activationStatus` 为准。
37
+ - 预检通过不等于初始化完成。分别检查 import 的云就绪结果、Function 活动部署和真实业务请求;`cloud status` 的激活汇总不能替代业务验收。
32
38
  - 套餐与订阅购买流程在 PC 端;CLI 只读 `qmuse account` 汇总并在缺少契约时返回 `unavailable` 原因。