Skip to content

Commit 4b153c7

Browse files
FL4TLiN3claude
andauthored
feat: migrate @perstack/api-client to npm version (#415)
* feat: migrate @perstack/api-client to npm version - Remove local packages/api-client/ directory - Update dependencies to use @perstack/api-client@^0.0.51 - Update API calls to use new response structure - Remove deprecated CLI commands (publish, status, tag) - Remove E2E test for deleted publish command BREAKING CHANGES: - Remove `perstack publish` command (new API uses draft-based publish model) - Remove `perstack status` command (no status update in new API) - Remove `perstack tag` command (no tag update in new API) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: lazy-load resolve-expert to fix test failures The npm @perstack/api-client package imports @perstack/core at runtime, which caused ESM resolution failures when loading tests. The issue was that the helpers barrel file (index.ts) re-exported resolve-expert.ts, triggering the import chain: test → helpers/index.js → resolve-expert.js → @perstack/api-client (npm) → @perstack/core (workspace) → ./adapters/index.js (doesn't exist) Fix by: - Remove resolveExpertToRun export from helpers/index.ts - Use dynamic import in setup-experts.ts to lazy-load resolve-expert.ts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: remove unpublish command and related wizard components For consistency with other removed registry commands (publish, status, tag), also remove the unpublish command since CLI no longer supports registry operations. Removed: - perstack unpublish command - Wizard TUI components (WizardExpertSelector, VersionSelector, useExpertVersionWizard) - Related type definitions (WizardExpertChoice, WizardVersionInfo) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: remove dead code from TUI components Removed unused code after the publish/status/tag/unpublish command cleanup: Components: - QueryRow, CompletionRow, ErrorRow from action-row.tsx - ErrorStep component (entire file) - getStatusColor utility (entire file) Types: - EventResult - RuntimeState - RunHistoryItem Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: rename registry references to published expert The "Perstack Registry" concept no longer exists. Rename all related references to use "published expert" terminology instead. Changes: - Rename e2e/perstack-cli/registry.test.ts → published-expert.test.ts - Rename RegistryExpertData → PublishedExpertData - Rename registryExpert/expertFromRegistry → publishedExpert - Update error messages: "registry response" → "API response" - Update error messages: "remote expert" → "published expert" - Remove Registry description from agents-md-template.ts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent dad7c51 commit 4b153c7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+375
-7378
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"perstack": patch
3+
"@perstack/runtime": patch
4+
---
5+
6+
Migrate @perstack/api-client to npm version (^0.0.51)
7+
8+
BREAKING CHANGES:
9+
- Remove `perstack publish` command (new API uses draft-based publish model)
10+
- Remove `perstack status` command (no status update in new API)
11+
- Remove `perstack tag` command (no tag update in new API)
12+
- Remove `perstack unpublish` command (CLI no longer supports registry operations)
13+
14+
Changes:
15+
- Update API calls from `client.registry.experts.*` to `client.experts.*`
16+
- Adapt response handling for new API structure where experts are nested under `definition.experts`

apps/create-expert/src/lib/agents-md-template.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ Perstack is a package manager and runtime for agent-first development. It enable
1919
Key concepts:
2020
- **Experts**: Modular micro-agents defined in TOML
2121
- **Runtime**: Executes Experts with isolation, observability, and sandbox support
22-
- **Registry**: Public registry for sharing and reusing Experts
2322
2423
## Project Configuration
2524

apps/perstack/bin/cli.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,8 @@ import { Command } from "commander"
44
import packageJson from "../package.json" with { type: "json" }
55
import { installCommand } from "../src/install.js"
66
import { logCommand } from "../src/log.js"
7-
import { publishCommand } from "../src/publish.js"
87
import { runCommand } from "../src/run.js"
98
import { startCommand } from "../src/start.js"
10-
import { statusCommand } from "../src/status.js"
11-
import { tagCommand } from "../src/tag.js"
12-
import { unpublishCommand } from "../src/unpublish.js"
139

1410
const program = new Command()
1511
.name(packageJson.name)
@@ -19,8 +15,4 @@ const program = new Command()
1915
.addCommand(runCommand)
2016
.addCommand(logCommand)
2117
.addCommand(installCommand)
22-
.addCommand(publishCommand)
23-
.addCommand(unpublishCommand)
24-
.addCommand(tagCommand)
25-
.addCommand(statusCommand)
2618
program.parse()

apps/perstack/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"typecheck": "tsc --noEmit"
2121
},
2222
"dependencies": {
23-
"@perstack/api-client": "workspace:*",
23+
"@perstack/api-client": "^0.0.51",
2424
"@perstack/core": "workspace:*",
2525
"@perstack/react": "workspace:*",
2626
"@perstack/runtime": "workspace:*",

apps/perstack/src/install.ts

Lines changed: 117 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { readFile, writeFile } from "node:fs/promises"
22
import path from "node:path"
3-
import { createApiClient, type RegistryExpert } from "@perstack/api-client"
3+
import { createApiClient } from "@perstack/api-client"
44
import {
55
defaultPerstackApiBaseUrl,
66
type Expert,
@@ -36,23 +36,115 @@ async function findConfigPathRecursively(cwd: string): Promise<string> {
3636
}
3737
}
3838

39-
function toRuntimeExpert(key: string, expert: RegistryExpert): Expert {
39+
type PublishedExpertData = {
40+
name: string
41+
version: string
42+
description?: string
43+
instruction: string
44+
skills?: Record<
45+
string,
46+
| {
47+
type: "mcpStdioSkill"
48+
name: string
49+
description: string
50+
rule?: string
51+
pick?: string[]
52+
omit?: string[]
53+
command: "npx" | "uvx"
54+
packageName: string
55+
requiredEnv?: string[]
56+
}
57+
| {
58+
type: "mcpSseSkill"
59+
name: string
60+
description: string
61+
rule?: string
62+
pick?: string[]
63+
omit?: string[]
64+
endpoint: string
65+
}
66+
| {
67+
type: "interactiveSkill"
68+
name: string
69+
description: string
70+
rule?: string
71+
tools: Record<string, { description: string; inputJsonSchema: string }>
72+
}
73+
>
74+
delegates?: string[]
75+
tags?: string[]
76+
}
77+
78+
function toRuntimeExpert(key: string, expert: PublishedExpertData): Expert {
4079
const skills: Record<string, Skill> = Object.fromEntries(
41-
Object.entries(expert.skills).map(([name, skill]) => {
80+
Object.entries(expert.skills ?? {}).map(([name, skill]) => {
4281
switch (skill.type) {
4382
case "mcpStdioSkill":
44-
return [name, { ...skill, name }]
83+
return [
84+
name,
85+
{
86+
type: skill.type,
87+
name,
88+
description: skill.description,
89+
rule: skill.rule,
90+
pick: skill.pick ?? [],
91+
omit: skill.omit ?? [],
92+
command: skill.command,
93+
packageName: skill.packageName,
94+
requiredEnv: skill.requiredEnv ?? [],
95+
lazyInit: false,
96+
},
97+
]
4598
case "mcpSseSkill":
46-
return [name, { ...skill, name }]
99+
return [
100+
name,
101+
{
102+
type: skill.type,
103+
name,
104+
description: skill.description,
105+
rule: skill.rule,
106+
pick: skill.pick ?? [],
107+
omit: skill.omit ?? [],
108+
endpoint: skill.endpoint,
109+
lazyInit: false,
110+
},
111+
]
47112
case "interactiveSkill":
48-
return [name, { ...skill, name }]
113+
return [
114+
name,
115+
{
116+
type: skill.type,
117+
name,
118+
description: skill.description,
119+
rule: skill.rule,
120+
tools: Object.fromEntries(
121+
Object.entries(skill.tools).map(([toolName, tool]) => [
122+
toolName,
123+
{
124+
name: toolName,
125+
description: tool.description,
126+
inputSchema: JSON.parse(tool.inputJsonSchema),
127+
},
128+
]),
129+
),
130+
},
131+
]
49132
default: {
50133
throw new Error(`Unknown skill type: ${(skill as { type: string }).type}`)
51134
}
52135
}
53136
}),
54137
)
55-
return { ...expert, key, skills }
138+
return {
139+
key,
140+
name: expert.name,
141+
version: expert.version,
142+
description: expert.description ?? "",
143+
instruction: expert.instruction,
144+
skills,
145+
delegates: expert.delegates ?? [],
146+
tags: expert.tags ?? [],
147+
}
56148
}
57149

58150
function configExpertToExpert(
@@ -79,10 +171,6 @@ async function resolveAllExperts(
79171
env: Record<string, string>,
80172
): Promise<Record<string, Expert>> {
81173
const experts: Record<string, Expert> = {}
82-
const client = createApiClient({
83-
baseUrl: config.perstackApiBaseUrl ?? defaultPerstackApiBaseUrl,
84-
apiKey: env.PERSTACK_API_KEY,
85-
})
86174
for (const [key, configExpert] of Object.entries(config.experts ?? {})) {
87175
experts[key] = configExpertToExpert(key, configExpert)
88176
}
@@ -94,18 +182,32 @@ async function resolveAllExperts(
94182
}
95183
}
96184
}
185+
if (toResolve.size === 0) {
186+
return experts
187+
}
188+
const apiKey = env.PERSTACK_API_KEY
189+
if (!apiKey) {
190+
throw new Error("PERSTACK_API_KEY is required to resolve remote delegates")
191+
}
192+
const client = createApiClient({
193+
baseUrl: config.perstackApiBaseUrl ?? defaultPerstackApiBaseUrl,
194+
apiKey,
195+
})
97196
while (toResolve.size > 0) {
98197
const delegateKey = toResolve.values().next().value
99198
if (!delegateKey) break
100199
toResolve.delete(delegateKey)
101200
if (experts[delegateKey]) continue
102-
const result = await client.registry.experts.get(delegateKey)
201+
const result = await client.experts.get(delegateKey)
103202
if (!result.ok) {
104203
throw new Error(`Failed to resolve delegate "${delegateKey}": ${result.error.message}`)
105204
}
106-
const registryExpert = result.data
107-
experts[delegateKey] = toRuntimeExpert(delegateKey, registryExpert)
108-
for (const nestedDelegate of registryExpert.delegates) {
205+
const publishedExpert = result.data.data.definition.experts[delegateKey]
206+
if (!publishedExpert) {
207+
throw new Error(`Expert "${delegateKey}" not found in API response`)
208+
}
209+
experts[delegateKey] = toRuntimeExpert(delegateKey, publishedExpert)
210+
for (const nestedDelegate of publishedExpert.delegates ?? []) {
109211
if (!experts[nestedDelegate]) {
110212
toResolve.add(nestedDelegate)
111213
}

apps/perstack/src/publish.ts

Lines changed: 0 additions & 144 deletions
This file was deleted.

0 commit comments

Comments
 (0)