Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/rename-draft-scope-id.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@perstack/studio": patch
"perstack": patch
---

Rename `draftId` to `draftScopeId` in CLI arguments, handler parameters, and documentation to clarify the distinction between draft scope IDs and draft ref IDs.
38 changes: 25 additions & 13 deletions .github/workflows/expert-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,38 @@ jobs:
echo "should-publish=true" >> "$GITHUB_OUTPUT"
fi

- name: Resolve draft ID
- name: Resolve or create draft scope
if: steps.check.outputs.should-publish == 'true'
id: draft
env:
PERSTACK_API_KEY: ${{ secrets.PERSTACK_PRODUCTION_API_KEY }}
run: |
OUTPUT=$(bun ./apps/perstack/bin/cli.ts expert list --filter ${{ matrix.definition.name }} \
--api-key ${{ secrets.PERSTACK_PRODUCTION_API_KEY }})
echo "$OUTPUT"
DRAFT_ID=$(echo "$OUTPUT" | grep "ID:" | head -1 | awk '{print $2}')
if [ -z "$DRAFT_ID" ]; then
echo "::error::Draft scope not found for ${{ matrix.definition.name }}"
exit 1
fi
echo "draft-id=$DRAFT_ID" >> "$GITHUB_OUTPUT"
echo "Resolved draft ID: $DRAFT_ID"
DRAFT_SCOPE_ID=$(bun -e "
import { createApiClient } from '@perstack/api-client';
const client = createApiClient({ apiKey: process.env.PERSTACK_API_KEY });
const list = await client.expertDrafts.list({ filter: '${{ matrix.definition.name }}' });
if (list.ok && list.data.data.length > 0) {
console.log(list.data.data[0].id);
} else {
const apps = await client.applications.list();
if (!apps.ok) throw new Error('Failed to list applications: ' + apps.error.message);
const appId = apps.data.data.applications[0].id;
const create = await client.expertDrafts.create({
scopeName: '${{ matrix.definition.name }}',
applicationId: appId,
});
if (!create.ok) throw new Error('Failed to create draft: ' + create.error.message);
console.log(create.data.data.id);
}
")
echo "draft-scope-id=$DRAFT_SCOPE_ID" >> "$GITHUB_OUTPUT"
echo "Resolved draft scope ID: $DRAFT_SCOPE_ID"

- name: Push to draft
if: steps.check.outputs.should-publish == 'true'
id: push
run: |
OUTPUT=$(bun ./apps/perstack/bin/cli.ts expert push ${{ steps.draft.outputs.draft-id }} \
OUTPUT=$(bun ./apps/perstack/bin/cli.ts expert push ${{ steps.draft.outputs.draft-scope-id }} \
--config ${{ matrix.definition.path }}/perstack.toml \
--api-key ${{ secrets.PERSTACK_PRODUCTION_API_KEY }})
echo "$OUTPUT"
Expand All @@ -80,7 +92,7 @@ jobs:
if: steps.check.outputs.should-publish == 'true'
run: |
bun ./apps/perstack/bin/cli.ts expert version \
${{ steps.draft.outputs.draft-id }} \
${{ steps.draft.outputs.draft-scope-id }} \
${{ steps.push.outputs.ref-id }} \
${{ steps.version.outputs.version }} \
--tag latest \
Expand Down
30 changes: 18 additions & 12 deletions apps/perstack/bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,44 +204,50 @@ expertCmd
expertCmd
.command("delete")
.description("Delete a draft scope")
.argument("<draftId>", "Draft scope ID")
.action(async function (this: InstanceType<typeof Command>, draftId) {
.argument("<draftScopeId>", "Draft scope ID")
.action(async function (this: InstanceType<typeof Command>, draftScopeId) {
const parent = getParentOptions(this)
await expertDeleteHandler(draftId, parent)
await expertDeleteHandler(draftScopeId, parent)
})

expertCmd
.command("push")
.description("Push local expert definitions to a draft ref")
.argument("<draftId>", "Draft scope ID")
.argument("<draftScopeId>", "Draft scope ID")
.option("--config <path>", "Path to perstack.toml config file")
.action(async function (this: InstanceType<typeof Command>, draftId, options) {
.action(async function (this: InstanceType<typeof Command>, draftScopeId, options) {
const parent = getParentOptions(this)
await expertPushHandler(draftId, { ...parent, ...options })
await expertPushHandler(draftScopeId, { ...parent, ...options })
})

expertCmd
.command("refs")
.description("List draft refs for a draft scope")
.argument("<draftId>", "Draft scope ID")
.argument("<draftScopeId>", "Draft scope ID")
.option("--take <n>", "Limit results", Number.parseInt)
.option("--skip <n>", "Offset", Number.parseInt)
.action(async function (this: InstanceType<typeof Command>, draftId, options) {
.action(async function (this: InstanceType<typeof Command>, draftScopeId, options) {
const parent = getParentOptions(this)
await expertRefsHandler(draftId, { ...parent, ...options })
await expertRefsHandler(draftScopeId, { ...parent, ...options })
})

expertCmd
.command("version")
.description("Assign a version to a draft ref")
.argument("<draftId>", "Draft scope ID")
.argument("<draftScopeId>", "Draft scope ID")
.argument("<refId>", "Draft ref ID")
.argument("<version>", "Semantic version (e.g., 1.0.0)")
.option("--tag <tag>", "Version tag (e.g., latest)")
.option("--readme <path>", "Path to README file")
.action(async function (this: InstanceType<typeof Command>, draftId, refId, version, options) {
.action(async function (
this: InstanceType<typeof Command>,
draftScopeId,
refId,
version,
options,
) {
const parent = getParentOptions(this)
await expertVersionHandler(draftId, refId, version, { ...parent, ...options })
await expertVersionHandler(draftScopeId, refId, version, { ...parent, ...options })
})

expertCmd
Expand Down
22 changes: 11 additions & 11 deletions docs/references/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,24 +258,24 @@ perstack expert create <scopeName> --app <id>
Delete a draft scope.

```bash
perstack expert delete <draftId>
perstack expert delete <draftScopeId>
```

| Argument | Required | Description |
| ----------- | -------- | -------------- |
| `<draftId>` | Yes | Draft scope ID |
| `<draftScopeId>` | Yes | Draft scope ID |

### `expert push`

Push local expert definitions to a draft ref.

```bash
perstack expert push <draftId> [options]
perstack expert push <draftScopeId> [options]
```

| Argument | Required | Description |
| ----------- | -------- | -------------- |
| `<draftId>` | Yes | Draft scope ID |
| `<draftScopeId>` | Yes | Draft scope ID |

| Option | Description |
| ----------------- | ----------------------- |
Expand All @@ -288,12 +288,12 @@ Reads experts from `perstack.toml` and creates a new draft ref.
List draft refs for a draft scope.

```bash
perstack expert refs <draftId> [options]
perstack expert refs <draftScopeId> [options]
```

| Argument | Required | Description |
| ----------- | -------- | -------------- |
| `<draftId>` | Yes | Draft scope ID |
| `<draftScopeId>` | Yes | Draft scope ID |

| Option | Description |
| ------------ | ------------- |
Expand All @@ -305,12 +305,12 @@ perstack expert refs <draftId> [options]
Assign a semantic version to a draft ref.

```bash
perstack expert version <draftId> <refId> <version> [options]
perstack expert version <draftScopeId> <refId> <version> [options]
```

| Argument | Required | Description |
| ----------- | -------- | ---------------------------- |
| `<draftId>` | Yes | Draft scope ID |
| `<draftScopeId>` | Yes | Draft scope ID |
| `<refId>` | Yes | Draft ref ID |
| `<version>` | Yes | Semantic version (e.g., `1.0.0`) |

Expand Down Expand Up @@ -407,13 +407,13 @@ perstack install

# Expert lifecycle
perstack expert create my-expert --app app_123
perstack expert push clxxx --config ./perstack.toml
perstack expert version clxxx rfxxx 1.0.0 --tag latest
perstack expert push <draftScopeId> --config ./perstack.toml
perstack expert version <draftScopeId> <refId> 1.0.0 --tag latest
perstack expert versions my-expert
perstack expert publish my-expert
perstack expert yank my-expert@1.0.0
perstack expert unpublish my-expert
perstack expert delete clxxx
perstack expert delete <draftScopeId>

# View execution logs
perstack log
Expand Down
20 changes: 10 additions & 10 deletions e2e/studio/lifecycle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe("Studio Lifecycle", () => {
const scopeName = `e2e-studio-test-${Date.now()}`

let applicationId: string
let draftId: string
let draftScopeId: string
let refId: string

beforeAll(async () => {
Expand All @@ -28,8 +28,8 @@ describe("Studio Lifecycle", () => {
})

afterAll(async () => {
if (draftId) {
await client.expertDrafts.delete(draftId).catch(() => {})
if (draftScopeId) {
await client.expertDrafts.delete(draftScopeId).catch(() => {})
}
})

Expand Down Expand Up @@ -57,15 +57,15 @@ describe("Studio Lifecycle", () => {
if (!result.ok) throw new Error(`Failed to create draft: ${result.error.message}`)
expect(result.data.data).toHaveProperty("id")
expect(result.data.data.name).toBe(scopeName)
draftId = result.data.data.id
draftScopeId = result.data.data.id
},
API_TIMEOUT,
)

it(
"should push experts (create ref)",
async () => {
expect(draftId).toBeDefined()
expect(draftScopeId).toBeDefined()
const experts = [
{
key: scopeName,
Expand All @@ -88,7 +88,7 @@ describe("Studio Lifecycle", () => {
tags: [],
},
]
const result = await client.expertDrafts.refs.create(draftId, { experts })
const result = await client.expertDrafts.refs.create(draftScopeId, { experts })
expect(result.ok).toBe(true)
if (!result.ok) throw new Error(`Failed to push: ${result.error.message}`)
expect(result.data.data.draftRef).toHaveProperty("id")
Expand All @@ -100,8 +100,8 @@ describe("Studio Lifecycle", () => {
it(
"should list refs",
async () => {
expect(draftId).toBeDefined()
const result = await client.expertDrafts.refs.list(draftId)
expect(draftScopeId).toBeDefined()
const result = await client.expertDrafts.refs.list(draftScopeId)
expect(result.ok).toBe(true)
if (!result.ok) return
expect(result.data.data.length).toBeGreaterThan(0)
Expand All @@ -114,9 +114,9 @@ describe("Studio Lifecycle", () => {
it(
"should assign version",
async () => {
expect(draftId).toBeDefined()
expect(draftScopeId).toBeDefined()
expect(refId).toBeDefined()
const result = await client.expertDrafts.refs.assignVersion(draftId, refId, {
const result = await client.expertDrafts.refs.assignVersion(draftScopeId, refId, {
version: "0.0.1-e2e-test",
})
expect(result.ok).toBe(true)
Expand Down
14 changes: 7 additions & 7 deletions packages/studio/src/draft-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ export interface ExpertDeleteOptions {
baseUrl?: string
}

export async function expertDeleteHandler(draftId: string, options: ExpertDeleteOptions) {
export async function expertDeleteHandler(draftScopeId: string, options: ExpertDeleteOptions) {
const apiKey = resolveApiKey(options.apiKey)
const client = createStudioClient({ apiKey, baseUrl: options.baseUrl })
const result = await client.expertDrafts.delete(draftId)
const result = await client.expertDrafts.delete(draftScopeId)
if (!result.ok) {
throw new PerstackError(`Failed to delete draft: ${result.error.message}`)
}
console.log(`Draft scope deleted: ${draftId}`)
console.log(`Draft scope deleted: ${draftScopeId}`)
}

export interface ExpertPushOptions {
Expand All @@ -81,7 +81,7 @@ export interface ExpertPushOptions {
config?: string
}

export async function expertPushHandler(draftId: string, options: ExpertPushOptions) {
export async function expertPushHandler(draftScopeId: string, options: ExpertPushOptions) {
const apiKey = resolveApiKey(options.apiKey)
const client = createStudioClient({ apiKey, baseUrl: options.baseUrl })
const perstackConfig = await getPerstackConfig(options.config)
Expand All @@ -94,7 +94,7 @@ export async function expertPushHandler(draftId: string, options: ExpertPushOpti
// Expert type from @perstack/core has slightly looser types than the API expects
// (e.g., skill.description is optional in core but required in API)
// The runtime values are compatible since expertSchema enforces defaults
const result = await client.expertDrafts.refs.create(draftId, {
const result = await client.expertDrafts.refs.create(draftScopeId, {
experts: experts as Parameters<typeof client.expertDrafts.refs.create>[1]["experts"],
})
if (!result.ok) {
Expand All @@ -114,10 +114,10 @@ export interface ExpertRefsOptions {
skip?: number
}

export async function expertRefsHandler(draftId: string, options: ExpertRefsOptions) {
export async function expertRefsHandler(draftScopeId: string, options: ExpertRefsOptions) {
const apiKey = resolveApiKey(options.apiKey)
const client = createStudioClient({ apiKey, baseUrl: options.baseUrl })
const result = await client.expertDrafts.refs.list(draftId, {
const result = await client.expertDrafts.refs.list(draftScopeId, {
take: options.take,
skip: options.skip,
})
Expand Down
4 changes: 2 additions & 2 deletions packages/studio/src/version-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface ExpertVersionOptions {
}

export async function expertVersionHandler(
draftId: string,
draftScopeId: string,
refId: string,
version: string,
options: ExpertVersionOptions,
Expand All @@ -21,7 +21,7 @@ export async function expertVersionHandler(
if (options.readme) {
readme = await readFile(options.readme, "utf-8")
}
const result = await client.expertDrafts.refs.assignVersion(draftId, refId, {
const result = await client.expertDrafts.refs.assignVersion(draftScopeId, refId, {
version,
tag: options.tag,
readme,
Expand Down