数据测试
- 作者仓库星标 0
- 作者更新于 实时读取
- 作者仓库 skills-registry
- 领域
- 数据
- 兼容 Agent
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- 信任分
- 88 / 100 · 社区维护
- 作者 / 版本 / 许可
- @tomevault-io · 未声明 license
- Token 消耗评级
- 低消耗
- 接入复杂程度
- 需手动接入
- 是否需要外部 API Key
- 需要 · Vendor-specific
- 兼容的系统
- Windows · Docker
- 底层运行要求
- Node.js · Docker
- 文件与系统权限
-
- 只读
- 允许写入 / 修改
- 网络行为
- 允许外网请求
- 安装命令数
- 26 条
档案由构建时根据 SKILL.md 与安装命令自动衍生,可能与作者实际意图存在差异。
需要注意: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: data-fair-session
description: How to use the @data-fair/lib session management system in services that consume sessions (not l…
category: 数据
runtime: Node.js / Docker
---
# data-fair-session 输出预览
## PART A: 任务判断
- 适用问题:表格、CSV、数据集、指标或分析流程。
- 输入要求:目标材料、限制条件、期望输出和验收方式。
- 证据边界:围绕“Architecture Overview / Key Types / Express Backend”读取原文规则,不把推断写成作者承诺。
## PART B: 执行结果
- **01** 任务判断:确认你的需求是否属于表格、CSV、数据集、指标或分析流程,并标出输入、限制和预期结果。
- **02** 执行计划:优先按“Architecture Overview / Key Types / Express Backend”拆成步骤,说明每一步会读取什么、修改什么、产出什么。
- **03** 交付结果:给出可复制的命令、文件改动、检查清单或内容草稿,并说明如何继续迭代。
- **04** 风险边界:结合 读取文件、写入/修改文件、会按任务需要访问外部网络、需要准备 Vendor-specific API Key 给出执行前确认项。
## Running Rules
- 读取文件、写入/修改文件;会按任务需要访问外部网络;需要准备 Vendor-specific API Key。
- 先小样例验证,再放大到真实任务。
- 交付时同时给结果、检查口径和下一步迭代建议。 原文没有稳定的斜杠命令要求。安装验证后通常全局生效,直接在对话里点名这个 Skill 并描述任务即可。
告诉 Agent 目标文件或材料、期望结果、不可改范围、是否允许联网或执行命令。本 Skill 的权限画像是:读取文件、写入/修改文件。
先用一个小任务确认它会围绕“Architecture Overview / Key Types / Express Backend”工作;涉及文件或命令时,先看 diff、日志、预览或测试结果。
检查最终产物是否包含明确结果、必要证据和下一步动作;如果输出泛泛而谈,就补充输入、边界和验收标准后重跑。
---
name: data-fair-session
description: How to use the @data-fair/lib session management system in services that consume sessions (not l…
category: 数据
source: tomevault-io/skills-registry
---
# data-fair-session
## 什么时候使用
- 把数据处理方向的常用动作沉淀成 Agent 可调用的技能 适合处理表格、CSV、指标、数据集、分析和可视化报告,核心价值是把输入、判断、执行、验证和交付边界固定下来,避免 Agent 泛泛回答。 把任务拆成可执行、可检查、可继续迭代的步…
- 面向表格、CSV、数据集、指标或分析流程,优先处理能明确输入、步骤和验收标准的工作。
## 需要提供什么
- 目标材料、目录范围、期望结果和不可改动内容。
- 是否允许联网、执行命令、读写文件或调用外部服务。
## 执行规则
- 围绕「Architecture Overview / Key Types / Express Backend」组织步骤,不把推断写成作者事实。
- 读取文件、写入/修改文件;会按任务需要访问外部网络;需要准备 Vendor-specific API Key。
- 先跑小样例,确认结果可检查后再扩大任务范围。
## 输出要求
- 给出最终产物、关键证据、验证方式和下一步动作。
- 信息不足时标记 unknown,不编造命令、平台或依赖。 作者原文负责流程事实;仓库文件负责来源和命令;流狐只补充适用场景、限制和质量判断。
skill "data-fair-session" {
输入层 -> 用户目标 + 目标文件 + 禁止范围 + 验收标准
上下文层 -> Architecture Overview / Key Types / Express Backend
规则层 -> SKILL.md 触发条件 / 执行顺序 / 输出格式
运行层 -> Node.js / Docker | 读取文件、写入/修改文件 | 会按任务需要访问外部网络
安全层 -> 需要准备 Vendor-specific API Key + 小任务验证 + diff / 日志复核
输出层 -> 可复制结果 + 检查清单 + 下一步迭代
} data-fair Session Management -- Consumer Guide
This skill covers how services consume sessions produced by Simple Directory (the identity provider). It does NOT cover login flows or account management -- only how a service reads, verifies, and uses session data for authentication and authorization.
Architecture Overview
Sessions are stateless JWT cookies set by Simple Directory. Consuming services never store sessions -- they verify and read them on every request. The JWT is split across two cookies for security: id_token (readable by JavaScript, contains header+payload) and id_token_sign (httpOnly, contains the signature).
Additional cookies carry context: id_token_org (active organization), id_token_dep (active department), id_token_role (switched role), i18n_lang (language).
Key Types
// The central session object -- always present, even for anonymous users
interface SessionState {
user?: User // present if authenticated
organization?: OrganizationMembership // active org (if user switched to one)
account?: Account // derived: who is currently acting
accountRole?: string // role in the active account
lang: string // always present, defaults to 'fr'
}
// Authenticated variant -- user, account, accountRole guaranteed non-null
type SessionStateAuthenticated = SessionState & Required<Pick<SessionState, 'user' | 'account' | 'accountRole'>>
// The polymorphic "owner" of a resource
interface Account {
type: 'user' | 'organization'
id: string
name: string
department?: string
departmentName?: string
}
// Minimal key for matching ownership (used in permission checks)
type AccountKeys = Pick<Account, 'type' | 'id' | 'department'>
The account field is the key abstraction: it normalizes "who is currently acting" regardless of whether it's a personal user or an organization. Resources are owned by an Account; permission checks compare the session's account against the resource's owner.
The user.adminMode flag indicates a platform super-admin in admin mode -- it bypasses all permission checks.
Express Backend
Package: @data-fair/lib-express
Import paths:
import { session } from '@data-fair/lib-express/session.js'
// or the default export:
import session from '@data-fair/lib-express/session.js'
// Sync accessors and helpers (also re-exported from session.js):
import {
reqSession,
reqSessionAuthenticated,
reqAdminMode,
reqUser,
reqUserAuthenticated,
setReqUser,
setReqSession,
assertAccountRole,
assertAdminMode,
getAccountRole,
isAuthenticated,
} from '@data-fair/lib-express/session.js'
Initialization (at server startup)
import { session } from '@data-fair/lib-express/session.js'
// Point to Simple Directory's internal URL for JWKS key fetching
session.init(config.privateDirectoryUrl)
// Typically: 'http://simple-directory:8080' in Docker
Middleware (applied to routes)
import { session } from '@data-fair/lib-express/session.js'
// Parse session on all API routes (anonymous access allowed)
app.use('/api', session.middleware())
// Require authentication
app.use('/api/private', session.middleware({ required: true }))
// Require super-admin
app.use('/api/admin', session.middleware({ adminOnly: true }))
The middleware parses cookies, verifies the JWT via JWKS, and caches the result on the request object. It also blocks non-GET requests for pseudoSession tokens (limited API key sessions).
Reading Session in Route Handlers
After middleware has run, use the sync accessors:
app.get('/api/data', (req, res) => {
const s = reqSession(req) // SessionState (may be anonymous)
if (s.user) { /* authenticated */ }
})
app.post('/api/data', (req, res) => {
const s = reqSessionAuthenticated(req) // throws 401 if not logged in
// s.user, s.account, s.accountRole are guaranteed present
})
app.delete('/api/admin/thing', (req, res) => {
const s = reqAdminMode(req) // throws 401/403 if not super-admin
})
Permission Checking
The permission model is account-based ownership. Resources have an owner: Account field. Check access with:
import { reqSessionAuthenticated, assertAccountRole, assertAdminMode } from '@data-fair/lib-express/session.js'
// Check the user has 'admin' role on the resource's owner account
app.put('/api/resources/:id', async (req, res) => {
const session = reqSessionAuthenticated(req)
const resource = await db.findById(req.params.id)
assertAccountRole(session, resource.owner, 'admin')
// proceed with update...
})
// Accept multiple roles
assertAccountRole(session, resource.owner, ['admin', 'contrib'])
// Super-admin-only operations
assertAdminMode(session)
// Non-throwing check (returns role string or null)
const role = getAccountRole(session, resource.owner)
if (role === 'admin') { /* can edit */ }
getAccountRole resolution order:
- Not authenticated ->
null user.adminMode->'admin'(super-admin bypass)- Target is
type:'user'matchinguser.id->'admin'(self-ownership) - Match against
session.account->session.accountRole - Otherwise ->
null
Options for getAccountRole / assertAccountRole:
allAccounts: true-- check all user's org memberships, not just the currently active oneacceptDepAsRoot: true-- users in the root org (no department) can access department-scoped resources
Filtering Lists by Ownership
A common pattern for listing resources scoped to the current account:
app.get('/api/resources', async (req, res) => {
const session = reqSessionAuthenticated(req)
const query: any = {}
if (req.query.showAll === 'true') {
// Only super-admins can see all resources
assertAdminMode(session)
} else {
// Scope to current account
query['owner.type'] = session.account.type
query['owner.id'] = session.account.id
if (session.account.department) {
query['owner.department'] = session.account.department
}
}
const results = await db.find(query)
res.json(results)
})
Default Owner on Resource Creation
When creating a resource, default the owner to the session's active account:
app.post('/api/resources', async (req, res) => {
const session = reqSessionAuthenticated(req)
const resource = {
...req.body,
owner: req.body.owner ?? session.account
}
// Verify user has permission on the specified owner
assertAccountRole(session, resource.owner, 'admin')
await db.insert(resource)
})
Ownership Transfer
When changing a resource's owner, check permission on both old and new:
if (patch.owner) {
assertAccountRole(session, resource.owner, 'admin') // can remove from old
assertAccountRole(session, patch.owner, 'admin') // can assign to new
}
Synthetic Sessions (API Keys, Internal Calls)
Use setReqUser or setReqSession to create a pseudo-session from an API key or service-to-service call, bypassing normal cookie parsing:
import { setReqUser } from '@data-fair/lib-express/session.js'
// Create a session from an API key lookup
app.use(async (req, res, next) => {
const apiKey = req.headers['x-api-key']
if (apiKey) {
const keyRecord = await db.apiKeys.findOne({ key: apiKey })
setReqUser(req, keyRecord.user, 'fr', keyRecord.account, keyRecord.role)
}
next()
})
Passing Session to Service Layer
Thread the session state as a parameter to service functions rather than relying on request context:
// router.ts
const session = reqSessionAuthenticated(req)
await updateResource(session, req.params.id, req.body)
// service.ts
export async function updateResource(
sessionState: SessionStateAuthenticated,
id: string,
body: any
) {
const resource = await db.findById(id)
assertAccountRole(sessionState, resource.owner, 'admin')
// ...
}
Vue Frontend
Package: @data-fair/lib-vue
Import paths:
import { createSession, useSession, useSessionAuthenticated, getAccountRole } from '@data-fair/lib-vue/session.js'
import type { Session, SessionAuthenticated, SiteInfo, Account } from '@data-fair/lib-vue/session.js'
Setup -- Plain Vue SPA
// main.ts
import { createSession } from '@data-fair/lib-vue/session.js'
const session = await createSession({
// All options are optional, these are the defaults:
// directoryUrl: '/simple-directory',
// sitePath: '',
// defaultLang: 'fr',
})
const i18n = createI18n({ locale: session.state.lang })
createApp(App)
.use(session) // provides session via Vue's provide/inject
.use(i18n)
.mount('#app')
Setup -- Nuxt 3 SSR
Server-side (Nitro plugin):
// server/plugins/session.ts
import { SessionHandler } from '@data-fair/lib-node/session.js'
export const session = new SessionHandler()
export default defineNitroPlugin(async () => {
const config = useRuntimeConfig()
session.initJWKS(config.privateDirectoryUrl)
})
Client-side (Nuxt plugin):
// plugins/session.ts
import { createSession } from '@data-fair/lib-vue/session.js'
export default defineNuxtPlugin(async (app) => {
app.vueApp.use(await createSession({
req: app.ssrContext?.event.node.req, // pass request for SSR cookie reading
route: useRoute(),
}))
})
Auto-imports in nuxt.config.ts:
imports: {
presets: [{
from: '@data-fair/lib-vue/session.js',
imports: ['useSession', 'useSessionAuthenticated']
}]
}
Using Session in Components
useSession() -- returns Session with possibly-undefined user. Use for public-facing pages:
<script setup>
const session = useSession()
</script>
<template>
<div v-if="session.user.value">
Logged in as {{ session.user.value.name }}
<button @click="session.logout()">Logout</button>
</div>
<div v-else>
<button @click="session.login()">Login</button>
</div>
</template>
useSessionAuthenticated() -- returns SessionAuthenticated where user, account, accountRole are guaranteed. Use for protected pages (throws if not logged in):
<script setup>
const session = useSessionAuthenticated()
// Access current account
const accountType = session.state.account.type // 'user' | 'organization'
const accountId = session.state.account.id
const role = session.state.accountRole // 'admin' | 'contrib' | 'user'
</script>
Client-Side Permission Checks
<script setup>
import { getAccountRole } from '@data-fair/lib-vue/session.js'
const session = useSessionAuthenticated()
// Check role for a specific resource owner
const canEdit = computed(() => {
return getAccountRole(session.state, resource.value.owner) === 'admin'
})
// Super-admin check
const isSuperAdmin = computed(() => !!session.state.user?.adminMode)
</script>
Organization Switching
<script setup>
const session = useSessionAuthenticated()
// List available accounts (personal + organizations)
const accounts = computed(() => {
const items = [{ label: session.state.user.name, value: null }]
for (const org of session.state.user.organizations) {
items.push({
label: org.department ? `${org.name} / ${org.departmentName}` : org.name,
value: org.department ? `${org.id}:${org.department}` : org.id
})
}
return items
})
function onSwitch(value: string | null) {
if (!value) {
session.switchOrganization(null)
} else {
const [org, dep] = value.split(':')
session.switchOrganization(org, dep)
}
}
</script>
Session Properties Reference
The Session object returned by useSession():
| Property | Type | Description |
|---|---|---|
state |
reactive(SessionState) |
The raw reactive state object |
user |
ComputedRef<User | undefined> |
Current user (null if anonymous) |
organization |
ComputedRef |
Active organization membership |
account |
ComputedRef<Account | undefined> |
Active account (user or org) |
accountRole |
ComputedRef<string | undefined> |
Role in active account |
lang |
ComputedRef<string> |
Current language |
theme |
Ref<Theme> |
Current theme |
site |
Ref<SiteInfo | null> |
Site info (colors, auth mode) |
| Method | Description |
|---|---|
login(redirect?) |
Navigate to Simple Directory login |
logout(redirect?) |
Delete auth cookies and redirect |
switchOrganization(orgId, dep?, role?) |
Switch active organization |
switchLang(lang) |
Change language (triggers page reload) |
keepalive() |
Refresh the JWT token |
Keepalive runs automatically every 10 minutes on non-iframe top windows. Changing account, lang, or dark triggers a full page reload to ensure data consistency.
Common Patterns Summary
- Express init:
session.init(directoryUrl)at startup - Express middleware:
session.middleware()on route groups - Read session:
reqSession(req)orreqSessionAuthenticated(req)(sync, after middleware) - Check permission:
assertAccountRole(session, resource.owner, 'admin') - Super-admin gate:
assertAdminMode(session) - List filtering: scope queries to
session.account.{type, id, department} - Default owner:
body.owner ?? session.account - Vue setup:
createSession({})as Vue plugin - Vue access:
useSession()for public pages,useSessionAuthenticated()for protected pages - Vue permission:
getAccountRole(session.state, owner)for conditional UI
Source: data-fair/lib — distributed by TomeVault.
先判断是否适合
作者设计意图
作者的方法与取舍
边界和复核