Token导航 LogoToken导航

Agent Skill · 开发

workspace-api(workspace API 文档)

用于辅助 API 设计、接口文档、请求响应结构和服务集成说明。它适合让 Agent 梳理 endpoint、生成 OpenAPI 草稿、检查字段命名、整理错误码或辅助前后端联调。使用时需要确认真实业务语义、鉴权方式、分页和错误处理规则;涉及生成接口文档时,应避免凭空补字段,最好从现有代码、schema 或接口样例中提取事实。

workspace-api 是开发类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为操作浏览器;许可证信息还不明确,第三方扫描已通过,按安装量在开发类的 21,746 个 Skill 里排第 15,640。

总安装 576周安装 24GitHub Stars 4,539下载 192核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:workspace-api(workspace API 文档)
来源仓库:https://github.com/epicenterhq/epicenter
仓库路径:skills/workspace-api
安装命令:
npx skills add https://github.com/epicenterhq/epicenter --skill workspace-api
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/epicenterhq/epicenter --skill workspace-api

这个 Skill 是做什么的

开发类 Skill 面向写代码、改代码和排查问题,一般会读写仓库文件,部分还会执行构建或测试命令。

用于辅助 API 设计、接口文档和请求响应结构梳理。适合生成 OpenAPI 草稿、检查字段命名或整理错误码。使用时需确认业务语义、鉴权方式和分页规则。适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。涉及文档生成时应避免凭空补字段,优先提取现有代码事实。

workspace-api 属于开发类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/workspace-api

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/epicenterhq/epicenter --skill workspace-api;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要操作浏览器,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

workspace-api 怎么安装?

在终端执行 npx skills add https://github.com/epicenterhq/epicenter --skill workspace-api。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

workspace-api 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

workspace-api 需要什么权限?

权限级别是操作浏览器。该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

workspace-api 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

workspace-api 用的人多吗?

公开数据是累计安装 576 次,近一周安装 24 次,下载 192 次,GitHub 4,539 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

workspace-api 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。
  • 来源仓库有 4,539 星,维护活跃度相对可查。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理开发类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

开发类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
skill-vetter

技能审查员 · OpenClaw

8,779,018986操作浏览器MIT-0通过
ontology

本体论 · OpenClaw

6,636,960576只读MIT-0通过
entra-app-registration

内部应用程序注册 · Codex、Claude

6,631,872764接触敏感数据未明确通过
entra-app-registration

内部应用程序注册 · Codex、Claude

3,757,680201接触敏感数据未明确通过
nano-banana-pro

纳米香蕉专业版 · OpenClaw

3,526,898364接触敏感数据MIT-0有提醒
workspace-api

workspace API 文档 · 本页

5764,539操作浏览器未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Workspace API

Reference Repositories

  • Yjs — CRDT framework (foundation of workspace data layer)

Type-safe schema definitions for tables and KV stores.

Related Skills: See yjs for Yjs CRDT patterns and shared types. See svelte for reactive wrappers (fromTable, fromKv).

When to Apply This Skill

  • Defining a new table or KV store with defineTable() or defineKv()
  • Adding a new version to an existing table definition
  • Writing table migration functions
  • Reading, writing, or observing table/KV data
  • Attaching actions to a workspace client via .withActions()
  • Chaining extensions with .withExtension() or .withWorkspaceExtension()
  • Writing server-side Bun scripts with connectWorkspace()

Tables

Shorthand (Single Version)

Use when a table has only one version:

import { defineTable } from '@epicenter/workspace';
import { type } from 'arktype';

const usersTable = defineTable(type({ id: UserId, email: 'string', _v: '1' }));
export type User = InferTableRow<typeof usersTable>;

Every table schema must include _v with a number literal. The type system enforces this — passing a schema without _v to defineTable() is a compile error.

Variadic (Multiple Versions)

Use when you need to evolve a schema over time:

const posts = defineTable(
	type({ id: 'string', title: 'string', _v: '1' }),
	type({ id: 'string', title: 'string', views: 'number', _v: '2' }),
).migrate((row) => {
	switch (row._v) {
		case 1:
			return { ...row, views: 0, _v: 2 };
		case 2:
			return row;
	}
});

KV Stores

KV stores use defineKv(schema, defaultValue). No versioning, no migration—invalid stored data falls back to the default.

import { defineKv } from '@epicenter/workspace';
import { type } from 'arktype';

const sidebar = defineKv(type({ collapsed: 'boolean', width: 'number' }), { collapsed: false, width: 300 });
const fontSize = defineKv(type('number'), 14);
const enabled = defineKv(type('boolean'), true);

KV Design Convention: One Scalar Per Key

Use dot-namespaced keys for logical groupings of scalar values:

// ✅ Correct — each preference is an independent scalar
'theme.mode': defineKv(type("'light' | 'dark' | 'system'"), 'light'),
'theme.fontSize': defineKv(type('number'), 14),

// ❌ Wrong — structured object invites migration needs
'theme': defineKv(type({ mode: "'light' | 'dark'", fontSize: 'number' }), { mode: 'light', fontSize: 14 }),

With scalar values, schema changes either don't break validation (widening 'light' | 'dark' to 'light' | 'dark' | 'system' still validates old data) or the default fallback is acceptable (resetting a toggle takes one click).

Exception: discriminated unions and Record<string, T> | null are acceptable when they represent a single atomic value.

Branded Table IDs (Required)

Every table's id field and every string foreign key field MUST use a branded type instead of plain 'string'. This prevents accidental mixing of IDs from different tables at compile time.

Pattern

Define a branded type + arktype validator + generator in the same file as the workspace definition:

import type { Brand } from 'wellcrafted/brand';
import { type } from 'arktype';
import { generateId, type Id } from '@epicenter/workspace';

// 1. Branded type + arktype validator (co-located with workspace definition)
export type ConversationId = Id & Brand<'ConversationId'>;
export const ConversationId = type('string').as<ConversationId>();

// 2. Generator function — the ONLY place with the cast
export const generateConversationId = (): ConversationId =>
	generateId() as ConversationId;

// 3. Use in defineTable + co-locate type export
const conversationsTable = defineTable(
	type({
		id: ConversationId,              // Primary key — branded
		title: 'string',
		'parentId?': ConversationId.or('undefined'),  // Self-referencing FK
		_v: '1',
	}),
);
export type Conversation = InferTableRow<typeof conversationsTable>;

// 4. At call sites — use the generator, never cast directly
const newId = generateConversationId();  // Good
// const newId = generateId() as string as ConversationId;  // Bad

Actions (.withActions())

Actions wrap workspace operations as defineMutation (writes) or defineQuery (reads). Attach them via .withActions() on a workspace builder—the call is non-terminal, so you can chain .withExtension() after it.

import { createWorkspace, defineMutation, defineQuery, defineWorkspace } from '@epicenter/workspace';

export function createBlogWorkspace() {
	return createWorkspace(blogDefinition).withActions(({ tables }) => ({
		/**
		 * Mark a post as published and record the publication timestamp.
		 *
		 * Separated from a raw `tables.posts.update()` call because publish
		 * involves setting multiple fields atomically and may trigger side
		 * effects (notifications, RSS rebuild) in future versions.
		 */
		publish: defineMutation({
			description: 'Publish a draft post',
			input: type({ id: PostId }),
			handler: ({ id }) => {
				tables.posts.update({ id, published: true, publishedAt: Date.now() });
			},
		}),
	}));
}

JSDoc on Action Methods

Every action method inside .withActions() should have a JSDoc comment. The JSDoc and the description field serve different audiences:

  • description — consumed by MCP servers, CLI help text, and OpenAPI specs. Keep it short and declarative ("Import skills from disk").
  • JSDoc — consumed by developers hovering in an IDE. Explain *why* the action exists as a separate operation, what non-obvious behavior it has, or what assumptions it makes.
// ❌ Parrots the description
/** Import skills from an agentskills.io-compliant directory. */
importFromDisk: defineMutation({ description: 'Import skills from an agentskills.io-compliant directory', ... })

// ✅ Adds distinct value
/**
 * Scan a directory of SKILL.md files and upsert them into the workspace.
 *
 * Skills without a `metadata.id` in their frontmatter get one generated
 * and written back to the file, so future imports produce stable IDs
 * across machines.
 */
importFromDisk: defineMutation({ description: 'Import skills from an agentskills.io-compliant directory', ... })

Workspace File Structure

Each app splits workspace code into an isomorphic workspace/ folder and a runtime-specific client.ts:

src/lib/
│
├── workspace/                          ← 100% isomorphic (safe for Node, Bun, browser)
│   ├── definition.ts                   ← Schema: defineWorkspace, defineTable, branded IDs
│   ├── workspace.ts                    ← Factory: createWorkspace(definition) + isomorphic actions
│   └── index.ts                        ← Barrel: re-exports definition + workspace only
│
└── client.ts                           ← Runtime singleton: extensions, encryption, sync,
                                           runtime-specific actions (browser APIs, Node fs, etc.)
                    ┌─────────────────────────┐
                    │     definition.ts        │
                    │  tables, KV, branded IDs │
                    └────────────┬────────────┘
                                 │ imports
                    ┌────────────▼────────────┐
                    │     workspace.ts         │
                    │  createX() factory       │
                    │  + isomorphic actions    │
                    └────────────┬────────────┘
                                 │ imports
   ┌─────────────────────────────┼─────────────────────────────┐
   │                             │                             │
   ▼                             ▼                             ▼
┌──────────────┐   ┌──────────────────┐   ┌──────────────────┐
│ client.ts    │   │ server-client.ts │   │ cli-client.ts    │
│ (browser)    │   │ (Node/Bun)       │   │ (CLI)            │
│ IndexedDB    │   │ SQLite           │   │ filesystem       │
│ WebSocket    │   │ TCP sync         │   │ persistence      │
│ Chrome APIs  │   │ Node fs APIs     │   │                  │
└──────────────┘   └──────────────────┘   └──────────────────┘

Layering Rules

  1. definition.ts — Pure schema. defineWorkspace(), defineTable(), defineKv(), branded ID types and generators. Isomorphic.
  2. workspace.ts — Factory function that calls createWorkspace(definition). May chain .withActions() for isomorphic actions (table reads/writes only). Isomorphic.
  3. index.ts — Barrel that re-exports from definition.ts and workspace.ts only. Never re-exports from client.ts. This is the import path for $lib/workspace and the package.json subpath export.
  4. client.ts — Lives outside the workspace/ folder at src/lib/client.ts. Calls the factory, chains .withEncryption(), .withExtension(), and runtime-specific .withActions(). Exports the singleton as a named export (export const workspace =...).

Import Convention

// Components/state that need the live workspace instance:
import { workspace, auth } from '$lib/client';

// Components that only need types or the definition:
import { type Note, NoteId } from '$lib/workspace';

// Other packages in the monorepo:
import { createHoneycrisp } from '@epicenter/honeycrisp/workspace';
import { honeycrisp } from '@epicenter/honeycrisp/definition';

Package.json Subpath Exports

Each app exports a single ./workspace subpath pointing to the barrel:

{
  "exports": {
    "./workspace": "./src/lib/workspace/index.ts"
  }
}

The barrel is 100% isomorphic, so this single subpath is safe for any consumer (server, CLI, other apps). The separate ./definition subpath is no longer needed since the barrel already re-exports everything from definition.ts.

Isomorphic vs Runtime-Specific Actions

Isomorphic actions (table reads/writes, portable logic) belong in the exported workspace.ts factory. Runtime-specific actions—whether browser APIs, Chrome extension APIs, Node/Bun filesystem calls, or Tauri commands—are chained via .withActions() in the client file closest to that runtime.

// workspace.ts — isomorphic actions (exported via barrel)
export function createMyApp() {
  return createWorkspace(definition).withActions(({ tables }) => ({
    devices: {
      list: defineQuery({
        title: 'List Devices',
        description: 'List all synced devices.',
        input: Type.Object({}),
        handler: () => ({ devices: tables.devices.getAllValid() }),
      }),
    },
  }));
}

// src/lib/client.ts — browser-specific actions chained at the runtime boundary
export const workspace = createMyApp()
  .withExtension('persistence', indexeddbPersistence)
  .withExtension('sync', createSyncExtension({ ... }))
  .withActions(({ tables }) => ({
    tabs: {
      close: defineMutation({
        title: 'Close Tabs',
        description: 'Close browser tabs by ID.',
        input: Type.Object({ tabIds: Type.Array(Type.Number()) }),
        handler: async ({ tabIds }) => {
          await browser.tabs.remove(tabIds);  // Chrome API
          return { closedCount: tabIds.length };
        },
      }),
    },
  }));

// OR: src/lib/server-client.ts — Node/Bun-specific at the server boundary
export const workspace = createMyApp()
  .withExtension('persistence', sqlitePersistence)
  .withActions(({ tables }) => ({
    files: {
      importFromDisk: defineMutation({
        title: 'Import Files',
        description: 'Import files from a local directory.',
        input: Type.Object({ dirPath: Type.String() }),
        handler: async ({ dirPath }) => {
          const entries = await readdir(dirPath);  // Node fs API
          // ...
        },
      }),
    },
  }));

Extension Ordering

Extensions initialize in registration order. Each extension's factory receives a whenReady promise that resolves when all previously registered extensions have finished initializing. Whether this creates a waterfall depends on whether each extension awaits it:

ExtensionAwaits prior whenReady?Behavior
filesystemPersistenceNoStarts loading SQLite immediately
indexeddbPersistenceNoStarts loading IndexedDB immediately
createCliUnlockYesWaits for persistence, then applies encryption keys
createSyncExtensionYesWaits for everything before it, then opens WebSocket
createMarkdownMaterializerYesWaits for persistence + sync, then materializes

The standard chain is persistence → unlock → sync:

persistence starts loading ────────────────────→ done
                                                   ↓
                        unlock waits... ──────────→ applies keys → done
                                                                    ↓
                        sync waits... ─────────────────────────────→ connects

This ordering matters because sync only exchanges the delta between local state and the server. Without persistence loading first, every cold start downloads the full document.

// ✅ Correct — persistence loads first, sync exchanges delta only
createWorkspace(definition)
  .withExtension('persistence', filesystemPersistence({ filePath: '...' }))
  .withWorkspaceExtension('unlock', createCliUnlock(sessions, SERVER_URL))
  .withExtension('sync', createSyncExtension({ url: ..., getToken: ... }))

// ❌ Wrong — sync starts before local state is loaded, downloads full document
createWorkspace(definition)
  .withExtension('sync', createSyncExtension({ url: ..., getToken: ... }))
  .withExtension('persistence', filesystemPersistence({ filePath: '...' }))

connectWorkspace (CLI/Script Shortcut)

For server-side Bun scripts, connectWorkspace from @epicenter/cli handles the unlock → sync chain automatically. It is ephemeral by design — no local persistence, so a script can coexist with a long-running epicenter start daemon without fighting over the same SQLite file:

import { connectWorkspace } from '@epicenter/cli';
import { createFujiWorkspace } from '@epicenter/fuji/workspace';

const workspace = await connectWorkspace(createFujiWorkspace);
// Ready. Authenticated. Syncing. Full doc downloaded from server.

const entries = workspace.tables.entries.getAllValid();
await workspace.dispose();

Writes propagate through sync to the daemon, which owns the materializer (markdown, SQLite mirror, etc.).

Use connectWorkspace for one-off scripts and agent-written automation. Use epicenter.config.ts for long-running daemons and materializers that need persistence and custom workspace-specific extensions.

The _v Convention

  • _v is a number discriminant field ('1' in arktype = the literal number 1)
  • Required for tables — enforced at the type level via CombinedStandardSchema<{id: string; _v: number}>
  • Not used by KV stores — KV has no versioning; defineKv(schema, defaultValue) is the only pattern
  • In arktype schemas: _v: '1', _v: '2', _v: '3' (number literals)
  • In migration returns: _v: 2 (TypeScript narrows automatically, as const is unnecessary)
  • Convention: _v goes last in the object ({id,...fields, _v: '1'})

References

Load these on demand based on what you're working on:

Code references:

  • packages/workspace/src/workspace/define-table.ts
  • packages/workspace/src/workspace/define-kv.ts
  • packages/workspace/src/workspace/index.ts
  • packages/workspace/src/workspace/create-tables.ts
  • packages/workspace/src/workspace/create-kv.ts
  • packages/workspace/src/workspace/create-workspace.ts

平台分布

Codex

37.66%
按下载量换算72

Claude

27.97%
按下载量换算54

Cursor

18.76%
按下载量换算36

Gemini CLI

8.39%
按下载量换算16

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills