星海智算开发文档
应用开发

使用 jusp app 接入已有项目

在已有应用项目中维护接入文件、上传镜像归档、生成版本包并联网调试。

jusp-cli 是独立开发工具包,安装后提供完整命令 juspapp 是应用中心业务域子命令。它不随 jusp-platform-sdk 安装,版本也不跟随 SDK。工具面向已有项目:在项目内新增 .jusp 接入文件,不新建另一套应用工程,也不覆盖源码、容器构建文件或已有部署文件。

jusp app 在已有项目中生成版本接入包的流程

安装工具

准备 Python 3.12 或更高版本和 pipxpipx 专门把 Python 命令行工具安装在隔离环境, 不要求应用项目预先创建虚拟环境,也不会把 CLI 写入应用依赖。首次使用按操作系统的软件源 安装 pipx,并执行一次 pipx ensurepath。应用开发者工作台会显示当前正式 CLI 版本和可复制命令:

export JUSP_PACKAGE_TOKEN='<临时安装凭据>'
export JUSP_CLI_VERSION='<应用开发者工作台显示的当前稳定版本>'
PIP_INDEX_URL="https://__token__:${JUSP_PACKAGE_TOKEN}@app-api.jusuanhub.com:10443/api/v1/appstore/sdk/python/simple/" \
PIP_EXTRA_INDEX_URL="https://pypi.org/simple" \
pipx install "jusp-cli==${JUSP_CLI_VERSION}"
unset JUSP_PACKAGE_TOKEN JUSP_CLI_VERSION
jusp app --help

需要准备:可访问平台 HTTPS 包索引的网络、受支持的操作系统,以及 Python 3.12+ 和 pipx。CLI 会在 自己的隔离环境解析运行依赖;Python、Go、Java、Node.js 等应用都使用同一个 jusp 工具流程。只有 Python 应用调用平台运行接口时,才在应用项目中另装 jusp-platform-sdk

从已有项目开始

cd /path/to/existing-project
jusp app init .
jusp app inspect . --json

init 只在不存在时创建 .jusp/app.yaml,已有文件原样保留;inspect 只读报告项目根目录的容器构建文件和接入文件。工具不会猜测业务入口、端口和数据需求,开发者必须确认这些事实。

.jusp/app.yaml 完整示例

apiVersion: apps.jusuan.io/v1alpha1
kind: AppProject
metadata:
  name: my-app
spec:
  displayVersion: 1.2.0
  sdkContractVersion: app-center-v2
  runtime:
    apiVersion: app-runtime/v1
    serviceName: app
    servicePort: 8000
    entryPath: /apps/my-app
    healthPath: /healthz
    authMode: app-runtime-session
    protectedPaths: []
  components:
    - name: app
  resources:
    - name: primary-db
      type: postgresql
    - name: assets
      type: object-storage
  dependencies:
    - appKey: coommc
      apiVersion: v1
      capabilities:
        - integration.probe
  integrations:
    - apiVersion: v1
      basePath: /apps/my-app/api/integrations/v1
      capabilities:
        - name: my-app.example.read
          onlineDebug: true
  roles:
    - roleCode: my-app.editor
      displayName: 编辑者
      description: 可以编辑本应用项目
      definitionVersion: 1
      status: active

字段含义:

字段说明
metadata.name稳定 appKey,小写且发布后不变
displayVersion给开发者和用户看的版本号;只在显式新建版本时变化
runtime.apiVersion固定为 app-runtime/v1;这是公开接入合同版本,不是部署版本
runtime.serviceName/Port主 Web 组件的逻辑名和容器监听端口;serviceName 不是主机名或平台内部地址
entryPath用户打开应用的入口路径
healthPath平台判断工作负载是否就绪的路径
authMode新应用推荐 app-runtime-session
components每个组件必须绑定一个导入后的镜像资产
resources只声明资源类型和用途,不写主机名、账号或密码
dependencies声明需要调用的其他应用、公开 API 版本及能力范围
integrations声明本应用对其他应用开放的 API 版本与能力;只填写应用自身路由,不填写平台内部地址
roles应用业务角色;roleCode 必须以 appKey. 开头

命令字典

命令输入输出/副作用
jusp app init [目录]已有项目目录创建缺失的 .jusp/app.yaml 骨架;不覆盖
jusp app inspect [目录] [--json]项目目录只读输出容器构建文件和接入文件事实
jusp app validate <文件>AppProject、AppBundle 或 AppSubmission本地字段级校验;不联网
jusp app image upload <tar> ...Docker/OCI 镜像归档分片上传并返回 imageAssetId 和 digest
jusp app package [目录] ...AppProject、镜像资产、制品引用生成 .jusp/dist/submission.json
jusp app upload <submission> ...版本包和源文件创建草稿版本;不自动上架
jusp app dev --local ... -- <命令>场景、appKey 和子进程启动本地 HTTP 模拟器,给子进程注入与正式环境同名的 SDK 变量
jusp app dev --online ... -- <命令>应用 ID、候选版本 ID 和子进程浏览器授权后给子进程注入绑定该候选版本的短期真实调试环境

本地校验

jusp app validate .jusp/app.yaml

校验会拒绝:未知字段、非法端口、非绝对路径、重复组件、角色前缀错误、应用自调用、重复依赖能力和不支持的资源结构。校验通过只表示格式正确,不代表镜像或正式运行已经通过平台检查。

上传镜像 tar

应用开发者上传镜像归档,由平台完成加载和存放;开发者不接触平台制品仓库。

Docker archive:

docker save my-app:1.2.0 -o my-app-1.2.0.tar

jusp app image upload ./my-app-1.2.0.tar \
  --app-id <应用ID> \
  --component app \
  --display-version 1.2.0 \
  --idempotency-key my-app-1.2.0

OCI layout:

jusp app image upload ./my-app-1.2.0.oci.tar \
  --archive-type oci-archive \
  --app-id <应用ID> \
  --component app \
  --display-version 1.2.0 \
  --idempotency-key my-app-1.2.0

省略 --version-id 表示先创建应用级镜像资产,这是新版本的常用流程;指定 --version-id 表示绑定已有草稿。断线后以同一幂等键重新执行,平台会复用可恢复上传事实。

上传返回的 imageAssetId 是版本包输入;镜像 digest 由平台回读,开发者不能手填另一个 digest。

生成版本包

jusp app package . \
  --image-asset app=<imageAssetId>

输出 .jusp/dist/submission.json,内容包括:

  • 原始 AppProject;
  • 每个组件对应的 imageAssetId
  • 工具版本、SDK 合同版本和稳定幂等键。

它不包含镜像字节、秘密凭据、平台生成的运行名称或 verified=true。平台会独立核验制品,不能用 JSON 自证可信。

新应用不上传平台部署文件;只声明入口、组件和资源需求,平台生成受控运行配置。--artifact 是既有迁移应用核对已登记制品时的兼容参数,不属于新应用的正常接入步骤,也不应由第三方开发者自行填写。

创建草稿版本

jusp app upload .jusp/dist/submission.json \
  --app-id <应用ID> \
  --source .jusp/dist/submission.json

--source 文件必须与待提交 AppSubmission 完全一致。平台同时保存原始文件名、大小、摘要、上传时间和解析后的入口/资源/角色/镜像信息,供开发者与管理员对照查看。

创建成功后回到应用开发者工作台:检查草稿 → 修复阻断 → 发起上架申请。CLI 不提供管理员批准或上线命令。

本地模拟

jusp app dev --local --app-key my-app \
  --dependency coommc=http://127.0.0.1:9080 \
  -- python local_demo.py

CLI 会在本机回环地址启动一个随进程结束的 HTTP 模拟器,并注入 Runtime 地址、测试会话、测试应用身份、appKey 和开发场景。--dependency 只能填写 .jusp/app.yaml 已声明的依赖,并且必须指向本机回环地址和明确端口;CLI 把这些本地提供方地址注入子进程。应用继续使用 RuntimeHttpClientRuntimeClient 和提供方业务 SDK,不需要在业务代码里判断运行模式。

可用 --scenario invalid|insufficient_balance|rate_limited|dependency_error|timeout|idempotency_conflict|async_failed 覆盖错误处理。不带子进程的 jusp app dev --local 只做模拟器健康自检,不能代替应用代码验证。本地模拟不联网、不扣积分、不创建平台版本或镜像记录。

测试组织权限时,默认 success 提供组织管理员、两个分组和三名成员;organization_member 提供普通成员(目录接口返回 403),personal 不含组织(Python SDK 调用目录前抛出 ValueError,直接 HTTP 请求返回 403),expired_session 返回会话失效(401)。这些都是固定假身份,不需要平台注册测试账号。

Python 应用在仅限开发环境的入口适配层使用 RuntimeClient.resolve_dev_session() 读取 CLI 注入的会话,再把会话传给业务代码。异步客户端使用 await resolve_dev_session()。该方法在没有 JUSP_APP_DEV_MODE=local/online 或没有 CLI 会话时拒绝执行;生产入口必须继续用当前请求的 Cookie,不能把进程级调试会话当成所有用户的身份。

完整可运行示例见 Python 接入指南。这些身份场景及辅助方法要求 SDK 0.6.4、CLI 0.1.1 或更新的兼容版本;以应用开发者工作台的已发布版本为准。

联网调试

jusp app dev --online \
  --app-id <应用ID> \
  --version-id <当前候选版本ID> \
  -- python debug_model.py

流程:

  1. CLI 在本机监听一次性回调端口并打开应用开发者工作台确认页;
  2. 开发者确认应用和版本,选择“本人”或管理员已授权的测试账号,核对组织与扣费提示;
  3. CLI 用 PKCE 一次性交换绑定候选版本的短期会话;
  4. 只给子进程注入 Runtime、AppStore、应用间公网入口和短期令牌;
  5. 子进程结束后清空 CLI 持有的变量。

真实模型调用按所选调用账号扣积分;组织权限按该账号的实际成员关系判断。开发者不是组织管理员也没关系:请超管在应用详情的“测试账号”中加入专用测试组织的管理员账号。开发者不能自行输入任意用户,也不需要知道测试账号的密码或取走其登录会话。

平台重新签发有效期 1 小时的专用调试会话,记录开发者和测试账号两个身份。名单被移除、测试账号停用、离开组织或开发者失权后,后续受保护请求会被拒绝;已执行任务不追溯取消。调试会话不能续期成正式会话,到期重新运行命令。子进程退出不等于已签发令牌提前失效,不要保存或传播令牌。

确认后浏览器会跳转回本机 CLI。如果原页面仍然显示“调试授权已签发”,以终端是否已启动子进程为准;该提示只表示授权已签发和发起跳转,不代表本地程序已经运行。终端持续等待时,检查浏览器对本机跳转的拦截和回调端口,必要时重新运行命令,不要复制或转发授权码。

应用间调用只允许当前版本已声明的 dependencies[].apiVersion/capabilities,且目标应用正式版本明确允许 onlineDebug。调试凭据只用于运行接口,不能上传镜像、提交版本、审核、上线或管理其他应用;需要上传时单独执行上传命令完成本人授权。版本用于确定权限范围,并不创建试运行部署。

双应用本地联调

两个本地应用需要共享同一个模拟器。先下载双应用师生示例 JSON,无需访问 SDK 源码仓库。在文件中配置 appsusers、可选 groups;用户的 subject 包含 tenantId/accountId/workspaceId/userId,应用角色必须属于对应 appKey。示例 UUID 可对应应用自己的测试数据,不写真实用户信息。

# 终端一:共享模拟器
jusp app dev --local --fixture ./dual-app-local.json --serve --port 18080
# 终端二:本地可觅,命令以其开发说明为准
jusp app dev --local --attach http://127.0.0.1:18080 \
  --app-key coommc --identity teacher -- python run_coommc.py
# 终端三:已声明 coommc 依赖的调用方
jusp app dev --local --attach http://127.0.0.1:18080 \
  --app-key videoforge --identity teacher \
  --dependency coommc=http://127.0.0.1:18011/apps/coommc -- python run_videoforge.py

模拟器用真实 HTTP 校验授权、摘要与一次性启动码;只模拟平台,不替应用生成课堂或作品。重启后授权清空。

两个本地应用使用真实平台授权

两位开发者分别登录各自账号,授权时选择同一个、已获准用于两个应用的测试用户及组织。不需要知道该测试账号密码。先启动提供方:

jusp app dev --online --app-id <可觅应用ID> --version-id <可觅版本ID> \
  --receive-from videoforge --api-version v1 --capability integration.probe -- \
  python run_coommc.py

把终端输出的 receiverRef 配对编号交给调用方,不交出密码或调试凭据。调用方显式配置本地目标与该编号:

jusp app dev --online --app-id <VideoForge应用ID> --version-id <VideoForge版本ID> \
  --dependency coommc=http://127.0.0.1:18011/apps/coommc \
  --receiver coommc=<receiverRef> -- python run_videoforge.py
VideoForge ── 业务请求 + 短期授权 ──→ 本地可觅
    │                                  │
    └── 自己的调试凭据 → 平台 ← 自己的调试凭据 ┘

双方声明、版本、主体、能力和请求摘要必须一致。平台不连接开发机、不部署可觅,业务数据仍在应用自己的测试库。配对缺失时 CLI 拒绝本地路由,不回落线上应用。提供方退出后配对撤销;异常断电则最长到源授权的一小时期限自动失效。重新启动需重新授权和配对。

成功来源为 developer_local,区别于中转到线上应用的 developer_debug。启动交接仍由本地可觅后端交换自己的会话,最多保持至两端授权的较早到期时间;浏览器保活不延长授权。只读查看和禁止编辑属于提供方业务权限,探针成功不代替业务验收。

常见错误

现象原因与处理
“缺少 .jusp/app.yaml在项目根目录执行 jusp app init .,然后补全配置
“每个项目组件都必须绑定一个镜像资产”为每个 components[].name 增加一个 --image-asset
artifact 格式错误使用 kind=reference@sha256:<64hex>
401配对/安装凭据过期,重新从应用开发者工作台授权
404应用或版本不属于当前开发者,不要猜 ID
409同一幂等键绑定了不同文件;换回原文件或使用新的业务版本键
上传超时设置更大的 JUSP_APP_UPLOAD_TIMEOUT_SECONDS,不要关闭 TLS 校验

本页目录