chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
import type {
LaunchDarklyCreateFlagParams,
LaunchDarklyCreateFlagResponse,
} from '@/tools/launchdarkly/types'
import { FLAG_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyCreateFlagTool: ToolConfig<
LaunchDarklyCreateFlagParams,
LaunchDarklyCreateFlagResponse
> = {
id: 'launchdarkly_create_flag',
name: 'LaunchDarkly Create Flag',
description: 'Create a new feature flag in a LaunchDarkly project.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
projectKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The project key to create the flag in',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Human-readable name for the feature flag',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Unique key for the feature flag (used in code)',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Description of the feature flag',
},
tags: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of tags',
},
temporary: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the flag is temporary (default true)',
},
},
request: {
url: (params) =>
`https://app.launchdarkly.com/api/v2/flags/${encodeURIComponent(params.projectKey.trim())}`,
method: 'POST',
headers: (params) => ({
Authorization: params.apiKey.trim(),
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
name: params.name,
key: params.key,
}
if (params.description) body.description = params.description
if (params.tags) body.tags = params.tags.split(',').map((t) => t.trim())
if (params.temporary !== undefined) body.temporary = params.temporary
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return {
success: false,
output: {
key: '',
name: '',
kind: '',
description: null,
temporary: false,
archived: false,
deprecated: false,
creationDate: 0,
tags: [],
variations: [],
maintainerId: null,
maintainerEmail: null,
},
error: error.message,
}
}
const data = await response.json()
return {
success: true,
output: {
key: data.key ?? null,
name: data.name ?? null,
kind: data.kind ?? null,
description: data.description ?? null,
temporary: data.temporary ?? false,
archived: data.archived ?? false,
deprecated: data.deprecated ?? false,
creationDate: data.creationDate ?? null,
tags: data.tags ?? [],
variations: data.variations ?? [],
maintainerId: data.maintainerId ?? null,
maintainerEmail: data._maintainer?.email ?? null,
},
}
},
outputs: FLAG_OUTPUT_PROPERTIES,
}
@@ -0,0 +1,61 @@
import type {
LaunchDarklyDeleteFlagParams,
LaunchDarklyDeleteFlagResponse,
} from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyDeleteFlagTool: ToolConfig<
LaunchDarklyDeleteFlagParams,
LaunchDarklyDeleteFlagResponse
> = {
id: 'launchdarkly_delete_flag',
name: 'LaunchDarkly Delete Flag',
description: 'Delete a feature flag from a LaunchDarkly project.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
projectKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The project key',
},
flagKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The feature flag key to delete',
},
},
request: {
url: (params) =>
`https://app.launchdarkly.com/api/v2/flags/${encodeURIComponent(params.projectKey.trim())}/${encodeURIComponent(params.flagKey.trim())}`,
method: 'DELETE',
headers: (params) => ({
Authorization: params.apiKey.trim(),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return { success: false, output: { deleted: false }, error: error.message }
}
return {
success: true,
output: { deleted: true },
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether the flag was successfully deleted' },
},
}
@@ -0,0 +1,96 @@
import type {
LaunchDarklyGetAuditLogParams,
LaunchDarklyGetAuditLogResponse,
} from '@/tools/launchdarkly/types'
import { AUDIT_LOG_ENTRY_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyGetAuditLogTool: ToolConfig<
LaunchDarklyGetAuditLogParams,
LaunchDarklyGetAuditLogResponse
> = {
id: 'launchdarkly_get_audit_log',
name: 'LaunchDarkly Get Audit Log',
description: 'List audit log entries from your LaunchDarkly account.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of entries to return (default 10, max 20)',
},
spec: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Resource specifier filter (e.g. "proj/*:env/*:flag/*" for all flag changes, or "proj/default:env/production:flag/my-flag" for one flag in one environment)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.limit) queryParams.set('limit', String(params.limit))
if (params.spec) queryParams.set('spec', params.spec)
const qs = queryParams.toString()
return `https://app.launchdarkly.com/api/v2/auditlog${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: params.apiKey.trim(),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return { success: false, output: { entries: [], totalCount: 0 }, error: error.message }
}
const data = await response.json()
const entries = (data.items ?? []).map((item: Record<string, unknown>) => {
const member = item.member as Record<string, unknown> | undefined
const target = item.target as Record<string, unknown> | undefined
return {
id: (item._id as string) ?? null,
date: item.date ?? null,
kind: item.kind ?? null,
name: item.name ?? null,
description: item.description ?? null,
shortDescription: item.shortDescription ?? null,
memberEmail: member?.email ?? null,
targetName: target?.name ?? null,
targetKind: (target?.resources as string[] | undefined)?.[0] ?? null,
}
})
return {
success: true,
output: {
entries,
totalCount: (data.totalCount as number) ?? entries.length,
},
}
},
outputs: {
entries: {
type: 'array',
description: 'List of audit log entries',
items: {
type: 'object',
properties: AUDIT_LOG_ENTRY_OUTPUT_PROPERTIES,
},
},
totalCount: { type: 'number', description: 'Total number of audit log entries' },
},
}
+126
View File
@@ -0,0 +1,126 @@
import type {
LaunchDarklyGetFlagParams,
LaunchDarklyGetFlagResponse,
} from '@/tools/launchdarkly/types'
import { FLAG_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyGetFlagTool: ToolConfig<
LaunchDarklyGetFlagParams,
LaunchDarklyGetFlagResponse
> = {
id: 'launchdarkly_get_flag',
name: 'LaunchDarkly Get Flag',
description: 'Get a single feature flag by key from a LaunchDarkly project.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
projectKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The project key',
},
flagKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The feature flag key',
},
environmentKey: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter flag configuration to a specific environment',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.environmentKey) queryParams.set('env', params.environmentKey)
const qs = queryParams.toString()
return `https://app.launchdarkly.com/api/v2/flags/${encodeURIComponent(params.projectKey.trim())}/${encodeURIComponent(params.flagKey.trim())}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: params.apiKey.trim(),
}),
},
transformResponse: async (response: Response, params?: LaunchDarklyGetFlagParams) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return {
success: false,
output: {
key: '',
name: '',
kind: '',
description: null,
temporary: false,
archived: false,
deprecated: false,
creationDate: 0,
tags: [],
variations: [],
maintainerId: null,
maintainerEmail: null,
on: null,
},
error: error.message,
}
}
const data = await response.json()
const environments = data.environments as Record<string, Record<string, unknown>> | undefined
let on: boolean | null = null
if (environments) {
const envKey = params?.environmentKey?.trim()
if (envKey && environments[envKey]) {
on = (environments[envKey].on as boolean) ?? null
} else if (!envKey) {
const envKeys = Object.keys(environments)
if (envKeys.length === 1) {
on = (environments[envKeys[0]].on as boolean) ?? null
}
}
}
return {
success: true,
output: {
key: data.key ?? null,
name: data.name ?? null,
kind: data.kind ?? null,
description: data.description ?? null,
temporary: data.temporary ?? false,
archived: data.archived ?? false,
deprecated: data.deprecated ?? false,
creationDate: data.creationDate ?? null,
tags: data.tags ?? [],
variations: data.variations ?? [],
maintainerId: data.maintainerId ?? null,
maintainerEmail: data._maintainer?.email ?? null,
on,
},
}
},
outputs: {
...FLAG_OUTPUT_PROPERTIES,
on: {
type: 'boolean',
description:
'Whether the flag is on in the requested environment (null when the flag spans multiple environments and no environment key was provided)',
optional: true,
},
},
}
@@ -0,0 +1,81 @@
import type {
LaunchDarklyGetFlagStatusParams,
LaunchDarklyGetFlagStatusResponse,
} from '@/tools/launchdarkly/types'
import { FLAG_STATUS_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyGetFlagStatusTool: ToolConfig<
LaunchDarklyGetFlagStatusParams,
LaunchDarklyGetFlagStatusResponse
> = {
id: 'launchdarkly_get_flag_status',
name: 'LaunchDarkly Get Flag Status',
description:
'Get the status of a feature flag across environments (active, inactive, launched, etc.).',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
projectKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The project key',
},
flagKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The feature flag key',
},
environmentKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The environment key',
},
},
request: {
url: (params) =>
`https://app.launchdarkly.com/api/v2/flag-statuses/${encodeURIComponent(params.projectKey.trim())}/${encodeURIComponent(params.environmentKey.trim())}/${encodeURIComponent(params.flagKey.trim())}`,
method: 'GET',
headers: (params) => ({
Authorization: params.apiKey.trim(),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return {
success: false,
output: {
name: '',
lastRequested: null,
defaultVal: null,
},
error: error.message,
}
}
const data = await response.json()
return {
success: true,
output: {
name: data.name ?? null,
lastRequested: data.lastRequested ?? null,
defaultVal: data.default ?? null,
},
}
},
outputs: FLAG_STATUS_OUTPUT_PROPERTIES,
}
+12
View File
@@ -0,0 +1,12 @@
export { launchDarklyCreateFlagTool } from '@/tools/launchdarkly/create_flag'
export { launchDarklyDeleteFlagTool } from '@/tools/launchdarkly/delete_flag'
export { launchDarklyGetAuditLogTool } from '@/tools/launchdarkly/get_audit_log'
export { launchDarklyGetFlagTool } from '@/tools/launchdarkly/get_flag'
export { launchDarklyGetFlagStatusTool } from '@/tools/launchdarkly/get_flag_status'
export { launchDarklyListEnvironmentsTool } from '@/tools/launchdarkly/list_environments'
export { launchDarklyListFlagsTool } from '@/tools/launchdarkly/list_flags'
export { launchDarklyListMembersTool } from '@/tools/launchdarkly/list_members'
export { launchDarklyListProjectsTool } from '@/tools/launchdarkly/list_projects'
export { launchDarklyListSegmentsTool } from '@/tools/launchdarkly/list_segments'
export { launchDarklyToggleFlagTool } from '@/tools/launchdarkly/toggle_flag'
export { launchDarklyUpdateFlagTool } from '@/tools/launchdarkly/update_flag'
@@ -0,0 +1,92 @@
import type {
LaunchDarklyListEnvironmentsParams,
LaunchDarklyListEnvironmentsResponse,
} from '@/tools/launchdarkly/types'
import { ENVIRONMENT_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyListEnvironmentsTool: ToolConfig<
LaunchDarklyListEnvironmentsParams,
LaunchDarklyListEnvironmentsResponse
> = {
id: 'launchdarkly_list_environments',
name: 'LaunchDarkly List Environments',
description: 'List environments in a LaunchDarkly project.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
projectKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The project key to list environments for',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of environments to return (default 20)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.limit) queryParams.set('limit', String(params.limit))
const qs = queryParams.toString()
return `https://app.launchdarkly.com/api/v2/projects/${encodeURIComponent(params.projectKey.trim())}/environments${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: params.apiKey.trim(),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return {
success: false,
output: { environments: [], totalCount: 0 },
error: error.message,
}
}
const data = await response.json()
const environments = (data.items ?? []).map((item: Record<string, unknown>) => ({
id: (item._id as string) ?? null,
key: item.key ?? null,
name: item.name ?? null,
color: item.color ?? null,
apiKey: item.apiKey ?? null,
mobileKey: item.mobileKey ?? null,
tags: (item.tags as string[]) ?? [],
}))
return {
success: true,
output: {
environments,
totalCount: (data.totalCount as number) ?? environments.length,
},
}
},
outputs: {
environments: {
type: 'array',
description: 'List of environments',
items: {
type: 'object',
properties: ENVIRONMENT_OUTPUT_PROPERTIES,
},
},
totalCount: { type: 'number', description: 'Total number of environments' },
},
}
+107
View File
@@ -0,0 +1,107 @@
import type {
LaunchDarklyListFlagsParams,
LaunchDarklyListFlagsResponse,
} from '@/tools/launchdarkly/types'
import { FLAG_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyListFlagsTool: ToolConfig<
LaunchDarklyListFlagsParams,
LaunchDarklyListFlagsResponse
> = {
id: 'launchdarkly_list_flags',
name: 'LaunchDarkly List Flags',
description: 'List feature flags in a LaunchDarkly project.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
projectKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The project key to list flags for',
},
environmentKey: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter flag configurations to a specific environment',
},
tag: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter flags by tag name',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of flags to return (default 20)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.environmentKey) queryParams.set('env', params.environmentKey)
if (params.tag) queryParams.set('tag', params.tag)
if (params.limit) queryParams.set('limit', String(params.limit))
const qs = queryParams.toString()
return `https://app.launchdarkly.com/api/v2/flags/${encodeURIComponent(params.projectKey.trim())}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: params.apiKey.trim(),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return { success: false, output: { flags: [], totalCount: 0 }, error: error.message }
}
const data = await response.json()
const flags = (data.items ?? []).map((item: Record<string, unknown>) => ({
key: item.key ?? null,
name: item.name ?? null,
kind: item.kind ?? null,
description: item.description ?? null,
temporary: item.temporary ?? false,
archived: item.archived ?? false,
deprecated: item.deprecated ?? false,
creationDate: item.creationDate ?? null,
tags: (item.tags as string[]) ?? [],
variations: (item.variations as Array<Record<string, unknown>>) ?? [],
maintainerId: item.maintainerId ?? null,
maintainerEmail: (item._maintainer as Record<string, unknown> | undefined)?.email ?? null,
}))
return {
success: true,
output: {
flags,
totalCount: (data.totalCount as number) ?? flags.length,
},
}
},
outputs: {
flags: {
type: 'array',
description: 'List of feature flags',
items: {
type: 'object',
properties: FLAG_OUTPUT_PROPERTIES,
},
},
totalCount: { type: 'number', description: 'Total number of flags' },
},
}
@@ -0,0 +1,83 @@
import type {
LaunchDarklyListMembersParams,
LaunchDarklyListMembersResponse,
} from '@/tools/launchdarkly/types'
import { MEMBER_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyListMembersTool: ToolConfig<
LaunchDarklyListMembersParams,
LaunchDarklyListMembersResponse
> = {
id: 'launchdarkly_list_members',
name: 'LaunchDarkly List Members',
description: 'List account members in your LaunchDarkly organization.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of members to return (default 20)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.limit) queryParams.set('limit', String(params.limit))
const qs = queryParams.toString()
return `https://app.launchdarkly.com/api/v2/members${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: params.apiKey.trim(),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return { success: false, output: { members: [], totalCount: 0 }, error: error.message }
}
const data = await response.json()
const members = (data.items ?? []).map((item: Record<string, unknown>) => ({
id: (item._id as string) ?? null,
email: item.email ?? null,
firstName: item.firstName ?? null,
lastName: item.lastName ?? null,
role: item.role ?? null,
lastSeen: item._lastSeen ?? null,
creationDate: item.creationDate ?? null,
verified: item._verified ?? false,
}))
return {
success: true,
output: {
members,
totalCount: (data.totalCount as number) ?? members.length,
},
}
},
outputs: {
members: {
type: 'array',
description: 'List of account members',
items: {
type: 'object',
properties: MEMBER_OUTPUT_PROPERTIES,
},
},
totalCount: { type: 'number', description: 'Total number of members' },
},
}
@@ -0,0 +1,79 @@
import type {
LaunchDarklyListProjectsParams,
LaunchDarklyListProjectsResponse,
} from '@/tools/launchdarkly/types'
import { PROJECT_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyListProjectsTool: ToolConfig<
LaunchDarklyListProjectsParams,
LaunchDarklyListProjectsResponse
> = {
id: 'launchdarkly_list_projects',
name: 'LaunchDarkly List Projects',
description: 'List all projects in your LaunchDarkly account.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of projects to return (default 20)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.limit) queryParams.set('limit', String(params.limit))
const qs = queryParams.toString()
return `https://app.launchdarkly.com/api/v2/projects${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: params.apiKey.trim(),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return { success: false, output: { projects: [], totalCount: 0 }, error: error.message }
}
const data = await response.json()
const projects = (data.items ?? []).map((item: Record<string, unknown>) => ({
id: (item._id as string) ?? null,
key: item.key ?? null,
name: item.name ?? null,
tags: (item.tags as string[]) ?? [],
}))
return {
success: true,
output: {
projects,
totalCount: (data.totalCount as number) ?? projects.length,
},
}
},
outputs: {
projects: {
type: 'array',
description: 'List of projects',
items: {
type: 'object',
properties: PROJECT_OUTPUT_PROPERTIES,
},
},
totalCount: { type: 'number', description: 'Total number of projects' },
},
}
@@ -0,0 +1,95 @@
import type {
LaunchDarklyListSegmentsParams,
LaunchDarklyListSegmentsResponse,
} from '@/tools/launchdarkly/types'
import { SEGMENT_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyListSegmentsTool: ToolConfig<
LaunchDarklyListSegmentsParams,
LaunchDarklyListSegmentsResponse
> = {
id: 'launchdarkly_list_segments',
name: 'LaunchDarkly List Segments',
description: 'List user segments in a LaunchDarkly project and environment.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
projectKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The project key',
},
environmentKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The environment key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of segments to return (default 20)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.limit) queryParams.set('limit', String(params.limit))
const qs = queryParams.toString()
return `https://app.launchdarkly.com/api/v2/segments/${encodeURIComponent(params.projectKey.trim())}/${encodeURIComponent(params.environmentKey.trim())}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: params.apiKey.trim(),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return { success: false, output: { segments: [], totalCount: 0 }, error: error.message }
}
const data = await response.json()
const segments = (data.items ?? []).map((item: Record<string, unknown>) => ({
key: item.key ?? null,
name: item.name ?? null,
description: item.description ?? null,
tags: (item.tags as string[]) ?? [],
creationDate: item.creationDate ?? null,
unbounded: item.unbounded ?? false,
included: (item.included as string[]) ?? [],
excluded: (item.excluded as string[]) ?? [],
}))
return {
success: true,
output: {
segments,
totalCount: (data.totalCount as number) ?? segments.length,
},
}
},
outputs: {
segments: {
type: 'array',
description: 'List of user segments',
items: {
type: 'object',
properties: SEGMENT_OUTPUT_PROPERTIES,
},
},
totalCount: { type: 'number', description: 'Total number of segments' },
},
}
+127
View File
@@ -0,0 +1,127 @@
import type {
LaunchDarklyToggleFlagParams,
LaunchDarklyToggleFlagResponse,
} from '@/tools/launchdarkly/types'
import { FLAG_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyToggleFlagTool: ToolConfig<
LaunchDarklyToggleFlagParams,
LaunchDarklyToggleFlagResponse
> = {
id: 'launchdarkly_toggle_flag',
name: 'LaunchDarkly Toggle Flag',
description: 'Toggle a feature flag on or off in a specific LaunchDarkly environment.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
projectKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The project key',
},
flagKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The feature flag key to toggle',
},
environmentKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The environment key to toggle the flag in',
},
enabled: {
type: 'boolean',
required: true,
visibility: 'user-or-llm',
description: 'Whether to turn the flag on (true) or off (false)',
},
},
request: {
url: (params) =>
`https://app.launchdarkly.com/api/v2/flags/${encodeURIComponent(params.projectKey.trim())}/${encodeURIComponent(params.flagKey.trim())}`,
method: 'PATCH',
headers: (params) => ({
Authorization: params.apiKey.trim(),
'Content-Type': 'application/json; domain-model=launchdarkly.semanticpatch',
}),
body: (params) => ({
environmentKey: params.environmentKey,
instructions: [{ kind: params.enabled ? 'turnFlagOn' : 'turnFlagOff' }],
}),
},
transformResponse: async (response: Response, params?: LaunchDarklyToggleFlagParams) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return {
success: false,
output: {
key: '',
name: '',
kind: '',
description: null,
temporary: false,
archived: false,
deprecated: false,
creationDate: 0,
tags: [],
variations: [],
maintainerId: null,
maintainerEmail: null,
on: null,
},
error: error.message,
}
}
const data = await response.json()
const environments = data.environments as Record<string, Record<string, unknown>> | undefined
let on: boolean | null = null
if (environments) {
const envKey = params?.environmentKey?.trim()
if (envKey && environments[envKey]) {
on = (environments[envKey].on as boolean) ?? null
}
}
return {
success: true,
output: {
key: data.key ?? null,
name: data.name ?? null,
kind: data.kind ?? null,
description: data.description ?? null,
temporary: data.temporary ?? false,
archived: data.archived ?? false,
deprecated: data.deprecated ?? false,
creationDate: data.creationDate ?? null,
tags: data.tags ?? [],
variations: data.variations ?? [],
maintainerId: data.maintainerId ?? null,
maintainerEmail: data._maintainer?.email ?? null,
on,
},
}
},
outputs: {
...FLAG_OUTPUT_PROPERTIES,
on: {
type: 'boolean',
description: 'Whether the flag is now on in the target environment',
optional: true,
},
},
}
+375
View File
@@ -0,0 +1,375 @@
import type { OutputProperty, ToolResponse } from '@/tools/types'
/**
* Shared output property definitions for LaunchDarkly API responses.
* Based on LaunchDarkly REST API v2: https://apidocs.launchdarkly.com/
*/
export const FLAG_OUTPUT_PROPERTIES = {
key: { type: 'string', description: 'The unique key of the feature flag' },
name: { type: 'string', description: 'The human-readable name of the feature flag' },
kind: { type: 'string', description: 'The type of flag (boolean or multivariate)' },
description: { type: 'string', description: 'Description of the feature flag', optional: true },
temporary: { type: 'boolean', description: 'Whether the flag is temporary' },
archived: { type: 'boolean', description: 'Whether the flag is archived' },
deprecated: { type: 'boolean', description: 'Whether the flag is deprecated' },
creationDate: {
type: 'number',
description: 'Unix timestamp in milliseconds when the flag was created',
},
tags: {
type: 'array',
description: 'Tags applied to the flag',
items: { type: 'string', description: 'Tag name' },
},
variations: {
type: 'array',
description: 'The variations for this feature flag',
items: {
type: 'object',
properties: {
value: {
type: 'string',
description: 'The variation value (any JSON type, shown as text)',
},
name: { type: 'string', description: 'The variation name', optional: true },
description: { type: 'string', description: 'The variation description', optional: true },
},
},
},
maintainerId: {
type: 'string',
description: 'The ID of the member who maintains this flag',
optional: true,
},
maintainerEmail: {
type: 'string',
description: 'The email of the member who maintains this flag',
optional: true,
},
} as const satisfies Record<string, OutputProperty>
export const PROJECT_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'The project ID' },
key: { type: 'string', description: 'The unique project key' },
name: { type: 'string', description: 'The project name' },
tags: {
type: 'array',
description: 'Tags applied to the project',
items: { type: 'string', description: 'Tag name' },
},
} as const satisfies Record<string, OutputProperty>
export const ENVIRONMENT_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'The environment ID' },
key: { type: 'string', description: 'The unique environment key' },
name: { type: 'string', description: 'The environment name' },
color: { type: 'string', description: 'The color assigned to this environment' },
apiKey: { type: 'string', description: 'The server-side SDK key for this environment' },
mobileKey: { type: 'string', description: 'The mobile SDK key for this environment' },
tags: {
type: 'array',
description: 'Tags applied to the environment',
items: { type: 'string', description: 'Tag name' },
},
} as const satisfies Record<string, OutputProperty>
export const AUDIT_LOG_ENTRY_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'The audit log entry ID' },
date: { type: 'number', description: 'Unix timestamp in milliseconds' },
kind: { type: 'string', description: 'The type of action performed' },
name: { type: 'string', description: 'The name of the resource acted on' },
description: { type: 'string', description: 'Full description of the action', optional: true },
shortDescription: {
type: 'string',
description: 'Short description of the action',
optional: true,
},
memberEmail: {
type: 'string',
description: 'Email of the member who performed the action',
optional: true,
},
targetName: { type: 'string', description: 'Name of the target resource', optional: true },
targetKind: {
type: 'string',
description: 'Resource specifier of the target (e.g. proj/default:env/production:flag/my-flag)',
optional: true,
},
} as const satisfies Record<string, OutputProperty>
export const SEGMENT_OUTPUT_PROPERTIES = {
key: { type: 'string', description: 'The unique segment key' },
name: { type: 'string', description: 'The segment name' },
description: { type: 'string', description: 'The segment description', optional: true },
tags: {
type: 'array',
description: 'Tags applied to the segment',
items: { type: 'string', description: 'Tag name' },
},
creationDate: {
type: 'number',
description: 'Unix timestamp in milliseconds when the segment was created',
},
unbounded: { type: 'boolean', description: 'Whether this is an unbounded (big) segment' },
included: {
type: 'array',
description: 'User keys explicitly included in the segment',
items: { type: 'string', description: 'User key' },
},
excluded: {
type: 'array',
description: 'User keys explicitly excluded from the segment',
items: { type: 'string', description: 'User key' },
},
} as const satisfies Record<string, OutputProperty>
export const FLAG_STATUS_OUTPUT_PROPERTIES = {
name: { type: 'string', description: 'The flag status (new, active, inactive, launched)' },
lastRequested: {
type: 'string',
description: 'Timestamp of the last evaluation',
optional: true,
},
defaultVal: { type: 'string', description: 'The default variation value', optional: true },
} as const satisfies Record<string, OutputProperty>
export const MEMBER_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'The member ID' },
email: { type: 'string', description: 'The member email address' },
firstName: { type: 'string', description: 'The member first name', optional: true },
lastName: { type: 'string', description: 'The member last name', optional: true },
role: { type: 'string', description: 'The member role (reader, writer, admin, owner)' },
lastSeen: { type: 'number', description: 'Unix timestamp of last activity', optional: true },
creationDate: { type: 'number', description: 'Unix timestamp when the member was created' },
verified: { type: 'boolean', description: 'Whether the member email is verified' },
} as const satisfies Record<string, OutputProperty>
export interface LaunchDarklyListFlagsParams {
apiKey: string
projectKey: string
environmentKey?: string
tag?: string
limit?: number
}
export interface LaunchDarklyGetFlagParams {
apiKey: string
projectKey: string
flagKey: string
environmentKey?: string
}
export interface LaunchDarklyCreateFlagParams {
apiKey: string
projectKey: string
name: string
key: string
description?: string
tags?: string
temporary?: boolean
}
export interface LaunchDarklyToggleFlagParams {
apiKey: string
projectKey: string
flagKey: string
environmentKey: string
enabled: boolean
}
export interface LaunchDarklyDeleteFlagParams {
apiKey: string
projectKey: string
flagKey: string
}
export interface LaunchDarklyListProjectsParams {
apiKey: string
limit?: number
}
export interface LaunchDarklyListEnvironmentsParams {
apiKey: string
projectKey: string
limit?: number
}
interface FlagItem {
key: string
name: string
kind: string
description: string | null
temporary: boolean
archived: boolean
deprecated: boolean
creationDate: number
tags: string[]
variations: Array<{ value: unknown; name?: string; description?: string }>
maintainerId: string | null
maintainerEmail: string | null
}
interface ProjectItem {
id: string
key: string
name: string
tags: string[]
}
interface EnvironmentItem {
id: string
key: string
name: string
color: string
apiKey: string
mobileKey: string
tags: string[]
}
export interface LaunchDarklyListFlagsResponse extends ToolResponse {
output: {
flags: FlagItem[]
totalCount: number
}
}
export interface LaunchDarklyGetFlagResponse extends ToolResponse {
output: FlagItem & {
on: boolean | null
}
}
export interface LaunchDarklyCreateFlagResponse extends ToolResponse {
output: FlagItem
}
export interface LaunchDarklyToggleFlagResponse extends ToolResponse {
output: FlagItem & {
on: boolean | null
}
}
export interface LaunchDarklyDeleteFlagResponse extends ToolResponse {
output: {
deleted: boolean
}
}
export interface LaunchDarklyListProjectsResponse extends ToolResponse {
output: {
projects: ProjectItem[]
totalCount: number
}
}
export interface LaunchDarklyListEnvironmentsResponse extends ToolResponse {
output: {
environments: EnvironmentItem[]
totalCount: number
}
}
export interface LaunchDarklyUpdateFlagParams {
apiKey: string
projectKey: string
flagKey: string
updateName?: string
updateDescription?: string
addTags?: string
removeTags?: string
archive?: boolean
comment?: string
}
export interface LaunchDarklyUpdateFlagResponse extends ToolResponse {
output: FlagItem
}
export interface LaunchDarklyGetAuditLogParams {
apiKey: string
limit?: number
spec?: string
}
interface AuditLogEntry {
id: string
date: number | null
kind: string | null
name: string | null
description: string | null
shortDescription: string | null
memberEmail: string | null
targetName: string | null
targetKind: string | null
}
export interface LaunchDarklyGetAuditLogResponse extends ToolResponse {
output: {
entries: AuditLogEntry[]
totalCount: number
}
}
export interface LaunchDarklyListSegmentsParams {
apiKey: string
projectKey: string
environmentKey: string
limit?: number
}
interface SegmentItem {
key: string
name: string
description: string | null
tags: string[]
creationDate: number | null
unbounded: boolean
included: string[]
excluded: string[]
}
export interface LaunchDarklyListSegmentsResponse extends ToolResponse {
output: {
segments: SegmentItem[]
totalCount: number
}
}
export interface LaunchDarklyGetFlagStatusParams {
apiKey: string
projectKey: string
flagKey: string
environmentKey: string
}
export interface LaunchDarklyGetFlagStatusResponse extends ToolResponse {
output: {
name: string
lastRequested: string | null
defaultVal: string | null
}
}
export interface LaunchDarklyListMembersParams {
apiKey: string
limit?: number
}
interface MemberItem {
id: string
email: string | null
firstName: string | null
lastName: string | null
role: string | null
lastSeen: number | null
creationDate: number | null
verified: boolean
}
export interface LaunchDarklyListMembersResponse extends ToolResponse {
output: {
members: MemberItem[]
totalCount: number
}
}
+167
View File
@@ -0,0 +1,167 @@
import type {
LaunchDarklyUpdateFlagParams,
LaunchDarklyUpdateFlagResponse,
} from '@/tools/launchdarkly/types'
import { FLAG_OUTPUT_PROPERTIES } from '@/tools/launchdarkly/types'
import type { ToolConfig } from '@/tools/types'
export const launchDarklyUpdateFlagTool: ToolConfig<
LaunchDarklyUpdateFlagParams,
LaunchDarklyUpdateFlagResponse
> = {
id: 'launchdarkly_update_flag',
name: 'LaunchDarkly Update Flag',
description:
'Update feature flag metadata (name, description, tags, temporary, archived) using semantic patch.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'LaunchDarkly API key',
},
projectKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The project key',
},
flagKey: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The feature flag key to update',
},
updateName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New name for the flag',
},
updateDescription: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New description for the flag',
},
addTags: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated tags to add',
},
removeTags: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated tags to remove',
},
archive: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Set to true to archive, false to restore',
},
comment: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional comment explaining the update',
},
},
request: {
url: (params) =>
`https://app.launchdarkly.com/api/v2/flags/${encodeURIComponent(params.projectKey.trim())}/${encodeURIComponent(params.flagKey.trim())}`,
method: 'PATCH',
headers: (params) => ({
Authorization: params.apiKey.trim(),
'Content-Type': 'application/json; domain-model=launchdarkly.semanticpatch',
}),
body: (params) => {
const instructions: Array<Record<string, unknown>> = []
if (params.updateName) {
instructions.push({ kind: 'updateName', value: params.updateName })
}
if (params.updateDescription) {
instructions.push({ kind: 'updateDescription', value: params.updateDescription })
}
if (params.addTags) {
instructions.push({
kind: 'addTags',
values: params.addTags.split(',').map((t: string) => t.trim()),
})
}
if (params.removeTags) {
instructions.push({
kind: 'removeTags',
values: params.removeTags.split(',').map((t: string) => t.trim()),
})
}
if (params.archive === true) {
instructions.push({ kind: 'archiveFlag' })
} else if (params.archive === false) {
instructions.push({ kind: 'restoreFlag' })
}
if (instructions.length === 0) {
throw new Error(
'At least one update field must be provided (updateName, updateDescription, addTags, removeTags, or archive)'
)
}
const body: Record<string, unknown> = { instructions }
if (params.comment) body.comment = params.comment
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }))
return {
success: false,
output: {
key: '',
name: '',
kind: '',
description: null,
temporary: false,
archived: false,
deprecated: false,
creationDate: 0,
tags: [],
variations: [],
maintainerId: null,
maintainerEmail: null,
},
error: error.message,
}
}
const data = await response.json()
return {
success: true,
output: {
key: data.key ?? null,
name: data.name ?? null,
kind: data.kind ?? null,
description: data.description ?? null,
temporary: data.temporary ?? false,
archived: data.archived ?? false,
deprecated: data.deprecated ?? false,
creationDate: data.creationDate ?? null,
tags: data.tags ?? [],
variations: data.variations ?? [],
maintainerId: data.maintainerId ?? null,
maintainerEmail: data._maintainer?.email ?? null,
},
}
},
outputs: FLAG_OUTPUT_PROPERTIES,
}