@optima-chat/dev-skills 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: cn-deploy
3
- description: 当用户请求把服务发布/部署/构建到 cn-stage、跑云效流水线、cn-stage 发版、部署某个分支到 cn-stage、触发 cn 构建、yunxiao 部署、"把 xx 发到 cn"时,使用此技能。覆盖 cn-stage 全部 20 个服务(构建→DB迁移→SAE 发布一条龙)。
3
+ description: 当用户请求把服务发布/部署/构建到 cn-stage、跑云效流水线、cn-stage 发版、部署某个分支到 cn-stage、触发 cn 构建、yunxiao 部署、"把 xx 发到 cn"时,使用此技能。覆盖 cn-stage 全部 21 个服务(构建→DB迁移→SAE 发布一条龙;含 build-only 的 agent-runtime)。
4
4
  ---
5
5
 
6
6
  # cn-deploy - 云效 cn-stage 流水线发布
@@ -21,12 +21,13 @@ optima-cn-deploy --list # 列出全部可发服务
21
21
 
22
22
  ## 使用要点
23
23
 
24
- 1. **先 `--list` 确认服务名**(20 个服务,含 commerce-backend / optima-scout)。
24
+ 1. **先 `--list` 确认服务名**(21 个服务,含 commerce-backend / optima-scout / build-only 的 agent-runtime)。
25
25
  2. 全链约 3-8 分钟(agentic-chat 最重)。触发后会输出云效 run 链接,可发给用户自行围观。
26
26
  3. **凭证零配置**:构建凭证由云效变量组 `cn-stage-build-credentials`(41970) 供给,不需要注入任何 secret。
27
27
  4. 分支构建会把该分支代码部署到 cn-stage(替换现运行版本)——用户明确要求部署分支时才用。
28
28
  5. 失败排查:命令输出的 flow.aliyun.com 链接里看具体 step 日志;常见原因是该分支/仓的 Codeup mirror 凭证问题(命令会在 mirror 步就报错并中止,不会构建旧代码)。
29
29
  6. 成功标准:最后一行 `✓ SAE ImageUrl tag=<sha>` —— 流水线绿且 SAE 真的钉到了目标 commit。只看流水线绿不算完成。
30
+ 7. **build-only 服务(`agent-runtime`)**:非 SAE 常驻(gateway-core 按 session 拉起的镜像)。成功标准不是 SAE ImageUrl,而是流水线绿即可——release 段自动把 ACR digest 回写 Infisical `/services/gateway-core/ALIYUN_AGENT_RUNTIME_IMAGE`(#807) 并滚动重启 gateway-core(任一步失败即 exit 非 0)。工具最后一行为 `✓ 流水线 SUCCESS(build-only)…` —— CLI 以流水线终态为准,未独立校验 Infisical/gateway-core(digest 与重启变更单见 run『发版』段日志)。
30
31
 
31
32
 
32
33
  ## cn-prod vtag 发版(⚠️ 待 svenyang 首次验证通过后再日常使用)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: cn-deploy
3
- description: 当用户请求把服务发布/部署/构建到 cn-stage、跑云效流水线、cn-stage 发版、部署某个分支到 cn-stage、触发 cn 构建、yunxiao 部署、"把 xx 发到 cn"时,使用此技能。覆盖 cn-stage 全部 20 个服务(构建→DB迁移→SAE 发布一条龙)。
3
+ description: 当用户请求把服务发布/部署/构建到 cn-stage、跑云效流水线、cn-stage 发版、部署某个分支到 cn-stage、触发 cn 构建、yunxiao 部署、"把 xx 发到 cn"时,使用此技能。覆盖 cn-stage 全部 21 个服务(构建→DB迁移→SAE 发布一条龙;含 build-only 的 agent-runtime)。
4
4
  ---
5
5
 
6
6
  # cn-deploy - 云效 cn-stage 流水线发布
@@ -21,12 +21,13 @@ optima-cn-deploy --list # 列出全部可发服务
21
21
 
22
22
  ## 使用要点
23
23
 
24
- 1. **先 `--list` 确认服务名**(20 个服务,含 commerce-backend / optima-scout)。
24
+ 1. **先 `--list` 确认服务名**(21 个服务,含 commerce-backend / optima-scout / build-only 的 agent-runtime)。
25
25
  2. 全链约 3-8 分钟(agentic-chat 最重)。触发后会输出云效 run 链接,可发给用户自行围观。
26
26
  3. **凭证零配置**:构建凭证由云效变量组 `cn-stage-build-credentials`(41970) 供给,不需要注入任何 secret。
27
27
  4. 分支构建会把该分支代码部署到 cn-stage(替换现运行版本)——用户明确要求部署分支时才用。
28
28
  5. 失败排查:命令输出的 flow.aliyun.com 链接里看具体 step 日志;常见原因是该分支/仓的 Codeup mirror 凭证问题(命令会在 mirror 步就报错并中止,不会构建旧代码)。
29
29
  6. 成功标准:最后一行 `✓ SAE ImageUrl tag=<sha>` —— 流水线绿且 SAE 真的钉到了目标 commit。只看流水线绿不算完成。
30
+ 7. **build-only 服务(`agent-runtime`)**:非 SAE 常驻(gateway-core 按 session 拉起的镜像)。成功标准不是 SAE ImageUrl,而是流水线绿即可——release 段自动把 ACR digest 回写 Infisical `/services/gateway-core/ALIYUN_AGENT_RUNTIME_IMAGE`(#807) 并滚动重启 gateway-core(任一步失败即 exit 非 0)。工具最后一行为 `✓ 流水线 SUCCESS(build-only)…` —— CLI 以流水线终态为准,未独立校验 Infisical/gateway-core(digest 与重启变更单见 run『发版』段日志)。
30
31
 
31
32
 
32
33
  ## cn-prod vtag 发版(⚠️ 待 svenyang 首次验证通过后再日常使用)
package/AGENTS.md CHANGED
@@ -14,7 +14,7 @@ Prefer the installed CLI tools over reimplementing long shell workflows:
14
14
  - `optima-product <create|update|add-channel|toggle-channel|show> [options]` — manage paid-plugin marketplace Products + Stripe channels (Wave 1.5 admin endpoints; stage default)
15
15
  - `optima-entitlement <grant|revoke|list> [options]` — admin-grant / revoke / list paid-plugin entitlements (refuses revoke of PAYMENT / PARTNER source)
16
16
  - `optima-cn-deploy <service> [--branch feat/xxx] [--no-wait]` — 云效 Flow 发布到 cn-stage(mirror 同步→构建→DB 迁移→SAE 发布→sha 校验;20 服务,凭证由云效变量组供给零配置)
17
- - `optima-plugin <show|set-paid|set-default> [options]` — flip a plugin's skills-side paid/free state (isPaid) + defaultForUser (the user-facing gate; pairs with optima-product for the billing side)
17
+ - `optima-plugin <show|set-paid|set-default|set-status> [options]` — flip a plugin's skills-side paid/free state (isPaid) + defaultForUser (the user-facing gate; pairs with optima-product for the billing side) + lifecycle status (ACTIVE|BETA|DEPRECATED — retire/restore a marketplace plugin)
18
18
 
19
19
  For code-reading tasks across Optima repositories, use `gh` commands against `Optima-Chat/<repo>`.
20
20
 
@@ -34,9 +34,13 @@ const PROFILE = process.env.OPTIMA_ALIYUN_PROFILE || 'aliyun-optima';
34
34
  const CODEUP_BASE = `https://codeup.aliyun.com/${ORG}`;
35
35
 
36
36
  // 服务注册表(快照自 optima-terraform services.stage.env + gen-pipelines.py,2026-07-13)
37
- interface Svc { pipelineId: number; repo: string; saeAppId?: string; prodSaeAppId?: string; }
37
+ interface Svc { pipelineId: number; repo: string; saeAppId?: string; prodSaeAppId?: string; buildOnly?: boolean; }
38
38
  const SERVICES: Record<string, Svc> = {
39
39
  'agent-portal': { pipelineId: 5118519, repo: 'optima-portals', saeAppId: 'fe757f78-d18d-4480-9402-fe59d4721055', prodSaeAppId: '9211db38-a255-4393-ae92-7a7bb41b583d' },
40
+ // build-only:非 SAE 常驻(gateway-core 按 session 拉起的镜像)。release 段=解析 ACR digest
41
+ // → 回写 Infisical /services/gateway-core/ALIYUN_AGENT_RUNTIME_IMAGE(#807)→ 滚动重启 gateway-core。
42
+ // 无自身 saeAppId;prod 流水线(agent-runtime-cn-prod,5124970)按名实时解析(见下方 ListPipelines 逻辑)。
43
+ 'agent-runtime': { pipelineId: 5124962, repo: 'optima-gateway', buildOnly: true },
40
44
  'agentic-chat': { pipelineId: 5118520, repo: 'agentic-chat', saeAppId: '6aea1ce1-f813-4e1c-8e97-d1ecb5398e37', prodSaeAppId: '6e290c73-a646-43ef-9da5-ad0b2e7eff73' },
41
45
  'billing': { pipelineId: 5118521, repo: 'optima-billing', saeAppId: '09d8e292-dc64-4af8-bce5-0a56cb666921', prodSaeAppId: '6c31cf82-8802-4d45-b6a2-e9d7c83ccce9' },
42
46
  'browser-backend': { pipelineId: 5118522, repo: 'optima-browser-use', saeAppId: '1fced3f6-a80a-41a4-8f23-e4d5a467f8eb', prodSaeAppId: 'eb782ed9-c468-4e81-a0fe-87e5b4264192' },
@@ -161,7 +165,17 @@ async function main() {
161
165
  console.log(`${status === 'SUCCESS' ? '✅' : '❌'} ${svcName} run#${runId}: ${status}`);
162
166
  if (status !== 'SUCCESS') process.exit(1);
163
167
 
164
- // 4. SAE ImageUrl 校验(空返回重试守卫)。prod digest 寻址,校验含 @sha256 即部署成功。
168
+ // 4. 成功标准。build-only(agent-runtime)无自身 SAE app 可查:digest 回写 Infisical +
169
+ // 滚动重启 gateway-core 都在流水线 release 段内完成(任一步失败即 exit 非 0)。CLI 不碰
170
+ // Infisical 凭证,只以流水线终态为准、不独立校验回写是否真落地——故措辞用「应已」而非「已」;
171
+ // 真实 digest / 重启变更单在 run『发版』段日志里(见上方链接)。
172
+ if (svc.buildOnly) {
173
+ const infEnv = envName === 'prod' ? 'prod' : 'staging';
174
+ console.log(`✓ 流水线 SUCCESS(build-only)。release 段应已回写 ${infEnv} Infisical ALIYUN_AGENT_RUNTIME_IMAGE(@sha256 digest)并滚动重启 gateway-core;CLI 未独立校验,digest 见上方 run『发版』段日志`);
175
+ return;
176
+ }
177
+
178
+ // SAE ImageUrl 校验(空返回重试守卫)。prod 是 digest 寻址,校验含 @sha256 即部署成功。
165
179
  const appId = envName === 'prod' ? svc.prodSaeAppId : svc.saeAppId;
166
180
  if (appId) {
167
181
  let img = '';
@@ -0,0 +1,90 @@
1
+ import { callSkills, validateEnvCnProd } from '../billing-http';
2
+ import { confirmIfProd } from '../confirm-prompt';
3
+
4
+ export const VALID_PLUGIN_STATUSES = ['ACTIVE', 'BETA', 'DEPRECATED'] as const;
5
+ export type PluginStatus = (typeof VALID_PLUGIN_STATUSES)[number];
6
+
7
+ // Aligned with agent-runtime skill-sync-handler's isValidSlug (and the
8
+ // deprecate-plugin workflow guard in optima-default-skills#40).
9
+ const SLUG_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;
10
+
11
+ export interface SetStatusArgs {
12
+ slug: string;
13
+ status: PluginStatus;
14
+ yes: boolean;
15
+ env: string;
16
+ }
17
+
18
+ export function parseSetStatusArgs(argv: string[]): SetStatusArgs {
19
+ if (argv.length === 0 || argv[0] === '-h' || argv[0] === '--help') {
20
+ console.log(`Usage: optima-plugin set-status --slug <slug> --status ACTIVE|BETA|DEPRECATED [options]
21
+
22
+ Required:
23
+ --slug <slug>
24
+ --status <status> Sets Plugin.status (marketplace lifecycle gate).
25
+ DEPRECATED retires the plugin: registry sync stops
26
+ serving it and agents unload it on their next sync
27
+ (in-flight sessions keep it until then). ACTIVE
28
+ restores it. Fully reversible.
29
+
30
+ Optional:
31
+ --yes Skip prod confirmation prompt (no-op on stage)
32
+ --env <env> stage|prod|cn-prod|cn-stage (default: stage)
33
+
34
+ Note: this PATCH's 404 is the authoritative "slug has no marketplace row in
35
+ this env" signal. Don't use 'optima-plugin show' to check existence — it reads
36
+ the public endpoint, which also 404s for non-ACTIVE (e.g. already-DEPRECATED)
37
+ plugins.`);
38
+ process.exit(0);
39
+ }
40
+ const out: Partial<SetStatusArgs> = { env: 'stage', yes: false };
41
+ for (let i = 0; i < argv.length; i++) {
42
+ const a = argv[i];
43
+ const next = argv[i + 1];
44
+ switch (a) {
45
+ case '--slug': out.slug = next; i++; break;
46
+ case '--status': {
47
+ const upper = (next ?? '').toUpperCase();
48
+ if (!(VALID_PLUGIN_STATUSES as readonly string[]).includes(upper)) {
49
+ throw new Error(`--status must be one of: ${VALID_PLUGIN_STATUSES.join('|')}`);
50
+ }
51
+ out.status = upper as PluginStatus; i++; break;
52
+ }
53
+ case '--yes': out.yes = true; break;
54
+ case '--env': out.env = next; i++; break;
55
+ default: throw new Error(`Unknown arg: ${a}`);
56
+ }
57
+ }
58
+ if (!out.slug) throw new Error('--slug required');
59
+ if (!SLUG_RE.test(out.slug)) throw new Error(`--slug must match ${SLUG_RE} (lowercase slug, e.g. onboarding-research)`);
60
+ if (!out.status) throw new Error(`--status required (${VALID_PLUGIN_STATUSES.join('|')})`);
61
+ return out as SetStatusArgs;
62
+ }
63
+
64
+ export async function runSetStatus(argv: string[]): Promise<void> {
65
+ const args = parseSetStatusArgs(argv);
66
+ validateEnvCnProd(args.env);
67
+
68
+ await confirmIfProd(
69
+ args.env,
70
+ `Action: set status=${args.status} on plugin '${args.slug}' (${args.env.toUpperCase()})`,
71
+ args.yes,
72
+ );
73
+
74
+ console.log(`\n🚦 Setting status=${args.status} on ${args.slug} (${args.env.toUpperCase()})...`);
75
+ const res = await callSkills(
76
+ args.env,
77
+ 'PATCH',
78
+ `/api/admin/plugins/${encodeURIComponent(args.slug)}`,
79
+ { status: args.status },
80
+ );
81
+ console.log(`✓ Updated plugin (HTTP ${res.status}):`);
82
+ console.log(JSON.stringify(res.body, null, 2));
83
+ if (args.status === 'DEPRECATED') {
84
+ console.log(`\nℹ️ Takes effect on each user's next skill sync (new session / billing event): registry stops serving the plugin and agents unload its skills. In-flight sessions keep it until then. Restore anytime with --status ACTIVE. Verify retirement end-to-end by confirming an agent session no longer loads the plugin's skills.`);
85
+ } else if (args.status === 'ACTIVE') {
86
+ console.log(`\nℹ️ Plugin restored: registry serves it again on each user's next skill sync.`);
87
+ } else if (args.status === 'BETA') {
88
+ console.log(`\n⚠️ BETA is NOT served either: registry sync / system load / user install all filter status='ACTIVE' (optima-skills internal.ts & user-plugins.ts). Users lose the plugin on their next sync, same as DEPRECATED — it's a pre-GA gate, not a soft-launch channel.`);
89
+ }
90
+ }
@@ -3,6 +3,7 @@
3
3
  import { runShow } from './plugin/show';
4
4
  import { runSetPaid } from './plugin/set-paid';
5
5
  import { runSetDefault } from './plugin/set-default';
6
+ import { runSetStatus } from './plugin/set-status';
6
7
 
7
8
  function printHelp() {
8
9
  console.log(`Usage: optima-plugin <subcommand> [options]
@@ -11,6 +12,7 @@ Subcommands:
11
12
  show Show a plugin's marketplace state (isPaid, salesUrl, ... ACTIVE plugins only)
12
13
  set-paid Flip a plugin's isPaid flag (the user-facing paid/free gate)
13
14
  set-default Flip a plugin's defaultForUser flag
15
+ set-status Set a plugin's lifecycle status (ACTIVE|BETA|DEPRECATED — retire/restore)
14
16
 
15
17
  Run 'optima-plugin <subcommand> --help' for subcommand-specific options.`);
16
18
  }
@@ -22,6 +24,7 @@ async function main() {
22
24
  case 'show': await runShow(rest); break;
23
25
  case 'set-paid': await runSetPaid(rest); break;
24
26
  case 'set-default': await runSetDefault(rest); break;
27
+ case 'set-status': await runSetStatus(rest); break;
25
28
  default:
26
29
  console.error(`Unknown subcommand: ${subcommand}`);
27
30
  printHelp();
@@ -35,6 +35,10 @@ const PROFILE = process.env.OPTIMA_ALIYUN_PROFILE || 'aliyun-optima';
35
35
  const CODEUP_BASE = `https://codeup.aliyun.com/${ORG}`;
36
36
  const SERVICES = {
37
37
  'agent-portal': { pipelineId: 5118519, repo: 'optima-portals', saeAppId: 'fe757f78-d18d-4480-9402-fe59d4721055', prodSaeAppId: '9211db38-a255-4393-ae92-7a7bb41b583d' },
38
+ // build-only:非 SAE 常驻(gateway-core 按 session 拉起的镜像)。release 段=解析 ACR digest
39
+ // → 回写 Infisical /services/gateway-core/ALIYUN_AGENT_RUNTIME_IMAGE(#807)→ 滚动重启 gateway-core。
40
+ // 无自身 saeAppId;prod 流水线(agent-runtime-cn-prod,5124970)按名实时解析(见下方 ListPipelines 逻辑)。
41
+ 'agent-runtime': { pipelineId: 5124962, repo: 'optima-gateway', buildOnly: true },
38
42
  'agentic-chat': { pipelineId: 5118520, repo: 'agentic-chat', saeAppId: '6aea1ce1-f813-4e1c-8e97-d1ecb5398e37', prodSaeAppId: '6e290c73-a646-43ef-9da5-ad0b2e7eff73' },
39
43
  'billing': { pipelineId: 5118521, repo: 'optima-billing', saeAppId: '09d8e292-dc64-4af8-bce5-0a56cb666921', prodSaeAppId: '6c31cf82-8802-4d45-b6a2-e9d7c83ccce9' },
40
44
  'browser-backend': { pipelineId: 5118522, repo: 'optima-browser-use', saeAppId: '1fced3f6-a80a-41a4-8f23-e4d5a467f8eb', prodSaeAppId: 'eb782ed9-c468-4e81-a0fe-87e5b4264192' },
@@ -191,7 +195,16 @@ async function main() {
191
195
  console.log(`${status === 'SUCCESS' ? '✅' : '❌'} ${svcName} run#${runId}: ${status}`);
192
196
  if (status !== 'SUCCESS')
193
197
  process.exit(1);
194
- // 4. SAE ImageUrl 校验(空返回重试守卫)。prod digest 寻址,校验含 @sha256 即部署成功。
198
+ // 4. 成功标准。build-only(agent-runtime)无自身 SAE app 可查:digest 回写 Infisical +
199
+ // 滚动重启 gateway-core 都在流水线 release 段内完成(任一步失败即 exit 非 0)。CLI 不碰
200
+ // Infisical 凭证,只以流水线终态为准、不独立校验回写是否真落地——故措辞用「应已」而非「已」;
201
+ // 真实 digest / 重启变更单在 run『发版』段日志里(见上方链接)。
202
+ if (svc.buildOnly) {
203
+ const infEnv = envName === 'prod' ? 'prod' : 'staging';
204
+ console.log(`✓ 流水线 SUCCESS(build-only)。release 段应已回写 ${infEnv} Infisical ALIYUN_AGENT_RUNTIME_IMAGE(@sha256 digest)并滚动重启 gateway-core;CLI 未独立校验,digest 见上方 run『发版』段日志`);
205
+ return;
206
+ }
207
+ // SAE ImageUrl 校验(空返回重试守卫)。prod 是 digest 寻址,校验含 @sha256 即部署成功。
195
208
  const appId = envName === 'prod' ? svc.prodSaeAppId : svc.saeAppId;
196
209
  if (appId) {
197
210
  let img = '';
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VALID_PLUGIN_STATUSES = void 0;
4
+ exports.parseSetStatusArgs = parseSetStatusArgs;
5
+ exports.runSetStatus = runSetStatus;
6
+ const billing_http_1 = require("../billing-http");
7
+ const confirm_prompt_1 = require("../confirm-prompt");
8
+ exports.VALID_PLUGIN_STATUSES = ['ACTIVE', 'BETA', 'DEPRECATED'];
9
+ // Aligned with agent-runtime skill-sync-handler's isValidSlug (and the
10
+ // deprecate-plugin workflow guard in optima-default-skills#40).
11
+ const SLUG_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;
12
+ function parseSetStatusArgs(argv) {
13
+ if (argv.length === 0 || argv[0] === '-h' || argv[0] === '--help') {
14
+ console.log(`Usage: optima-plugin set-status --slug <slug> --status ACTIVE|BETA|DEPRECATED [options]
15
+
16
+ Required:
17
+ --slug <slug>
18
+ --status <status> Sets Plugin.status (marketplace lifecycle gate).
19
+ DEPRECATED retires the plugin: registry sync stops
20
+ serving it and agents unload it on their next sync
21
+ (in-flight sessions keep it until then). ACTIVE
22
+ restores it. Fully reversible.
23
+
24
+ Optional:
25
+ --yes Skip prod confirmation prompt (no-op on stage)
26
+ --env <env> stage|prod|cn-prod|cn-stage (default: stage)
27
+
28
+ Note: this PATCH's 404 is the authoritative "slug has no marketplace row in
29
+ this env" signal. Don't use 'optima-plugin show' to check existence — it reads
30
+ the public endpoint, which also 404s for non-ACTIVE (e.g. already-DEPRECATED)
31
+ plugins.`);
32
+ process.exit(0);
33
+ }
34
+ const out = { env: 'stage', yes: false };
35
+ for (let i = 0; i < argv.length; i++) {
36
+ const a = argv[i];
37
+ const next = argv[i + 1];
38
+ switch (a) {
39
+ case '--slug':
40
+ out.slug = next;
41
+ i++;
42
+ break;
43
+ case '--status': {
44
+ const upper = (next ?? '').toUpperCase();
45
+ if (!exports.VALID_PLUGIN_STATUSES.includes(upper)) {
46
+ throw new Error(`--status must be one of: ${exports.VALID_PLUGIN_STATUSES.join('|')}`);
47
+ }
48
+ out.status = upper;
49
+ i++;
50
+ break;
51
+ }
52
+ case '--yes':
53
+ out.yes = true;
54
+ break;
55
+ case '--env':
56
+ out.env = next;
57
+ i++;
58
+ break;
59
+ default: throw new Error(`Unknown arg: ${a}`);
60
+ }
61
+ }
62
+ if (!out.slug)
63
+ throw new Error('--slug required');
64
+ if (!SLUG_RE.test(out.slug))
65
+ throw new Error(`--slug must match ${SLUG_RE} (lowercase slug, e.g. onboarding-research)`);
66
+ if (!out.status)
67
+ throw new Error(`--status required (${exports.VALID_PLUGIN_STATUSES.join('|')})`);
68
+ return out;
69
+ }
70
+ async function runSetStatus(argv) {
71
+ const args = parseSetStatusArgs(argv);
72
+ (0, billing_http_1.validateEnvCnProd)(args.env);
73
+ await (0, confirm_prompt_1.confirmIfProd)(args.env, `Action: set status=${args.status} on plugin '${args.slug}' (${args.env.toUpperCase()})`, args.yes);
74
+ console.log(`\n🚦 Setting status=${args.status} on ${args.slug} (${args.env.toUpperCase()})...`);
75
+ const res = await (0, billing_http_1.callSkills)(args.env, 'PATCH', `/api/admin/plugins/${encodeURIComponent(args.slug)}`, { status: args.status });
76
+ console.log(`✓ Updated plugin (HTTP ${res.status}):`);
77
+ console.log(JSON.stringify(res.body, null, 2));
78
+ if (args.status === 'DEPRECATED') {
79
+ console.log(`\nℹ️ Takes effect on each user's next skill sync (new session / billing event): registry stops serving the plugin and agents unload its skills. In-flight sessions keep it until then. Restore anytime with --status ACTIVE. Verify retirement end-to-end by confirming an agent session no longer loads the plugin's skills.`);
80
+ }
81
+ else if (args.status === 'ACTIVE') {
82
+ console.log(`\nℹ️ Plugin restored: registry serves it again on each user's next skill sync.`);
83
+ }
84
+ else if (args.status === 'BETA') {
85
+ console.log(`\n⚠️ BETA is NOT served either: registry sync / system load / user install all filter status='ACTIVE' (optima-skills internal.ts & user-plugins.ts). Users lose the plugin on their next sync, same as DEPRECATED — it's a pre-GA gate, not a soft-launch channel.`);
86
+ }
87
+ }
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const show_1 = require("./plugin/show");
5
5
  const set_paid_1 = require("./plugin/set-paid");
6
6
  const set_default_1 = require("./plugin/set-default");
7
+ const set_status_1 = require("./plugin/set-status");
7
8
  function printHelp() {
8
9
  console.log(`Usage: optima-plugin <subcommand> [options]
9
10
 
@@ -11,6 +12,7 @@ Subcommands:
11
12
  show Show a plugin's marketplace state (isPaid, salesUrl, ... ACTIVE plugins only)
12
13
  set-paid Flip a plugin's isPaid flag (the user-facing paid/free gate)
13
14
  set-default Flip a plugin's defaultForUser flag
15
+ set-status Set a plugin's lifecycle status (ACTIVE|BETA|DEPRECATED — retire/restore)
14
16
 
15
17
  Run 'optima-plugin <subcommand> --help' for subcommand-specific options.`);
16
18
  }
@@ -30,6 +32,9 @@ async function main() {
30
32
  case 'set-default':
31
33
  await (0, set_default_1.runSetDefault)(rest);
32
34
  break;
35
+ case 'set-status':
36
+ await (0, set_status_1.runSetStatus)(rest);
37
+ break;
33
38
  default:
34
39
  console.error(`Unknown subcommand: ${subcommand}`);
35
40
  printHelp();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/dev-skills",
3
- "version": "0.13.1",
3
+ "version": "0.15.0",
4
4
  "description": "Claude Code Skills for Optima development team - cross-environment collaboration tools",
5
5
  "main": "index.js",
6
6
  "bin": {