Compare commits

..

2 Commits

Author SHA1 Message Date
GitHub Action
c5370ea456 [automated] Apply ESLint and Oxfmt fixes 2026-06-02 00:48:18 +00:00
synap5e
a422c47b4a [chore] Update Ingest API types from cloud@b8bc948 2026-06-02 00:44:47 +00:00
15 changed files with 1450 additions and 539 deletions

View File

@@ -35,6 +35,9 @@ export type {
BulkRevokeWorkspaceMemberApiKeysErrors,
BulkRevokeWorkspaceMemberApiKeysResponse,
BulkRevokeWorkspaceMemberApiKeysResponses,
CancelAssetSeedData,
CancelAssetSeedResponse,
CancelAssetSeedResponses,
CancelJobData,
CancelJobError,
CancelJobErrors,
@@ -208,6 +211,8 @@ export type {
ForkWorkflowRequest,
ForkWorkflowResponse,
ForkWorkflowResponses,
FreeMemoryData,
FreeMemoryResponses,
GetAllSettingsData,
GetAllSettingsError,
GetAllSettingsErrors,
@@ -221,6 +226,9 @@ export type {
GetAssetByIdErrors,
GetAssetByIdResponse,
GetAssetByIdResponses,
GetAssetSeedStatusData,
GetAssetSeedStatusResponse,
GetAssetSeedStatusResponses,
GetAssetTagHistogramData,
GetAssetTagHistogramError,
GetAssetTagHistogramErrors,
@@ -259,6 +267,9 @@ export type {
GetDeletionRequestErrors,
GetDeletionRequestResponse,
GetDeletionRequestResponses,
GetEmbeddingsData,
GetEmbeddingsResponse,
GetEmbeddingsResponses,
GetExtensionsData,
GetExtensionsResponse,
GetExtensionsResponses,
@@ -305,6 +316,18 @@ export type {
GetHubWorkflowErrors,
GetHubWorkflowResponse,
GetHubWorkflowResponses,
GetI18nData,
GetI18nResponse,
GetI18nResponses,
GetInternalFolderPathsData,
GetInternalFolderPathsResponse,
GetInternalFolderPathsResponses,
GetInternalLogsData,
GetInternalLogsRawData,
GetInternalLogsRawResponse,
GetInternalLogsRawResponses,
GetInternalLogsResponse,
GetInternalLogsResponses,
GetJobDetailData,
GetJobDetailError,
GetJobDetailErrors,
@@ -356,10 +379,7 @@ export type {
GetModelFoldersResponse,
GetModelFoldersResponses,
GetModelPreviewData,
GetModelPreviewError,
GetModelPreviewErrors,
GetModelPreviewResponse,
GetModelPreviewResponses,
GetModelsInFolderData,
GetModelsInFolderError,
GetModelsInFolderErrors,
@@ -389,8 +409,26 @@ export type {
GetNodeReplacementsErrors,
GetNodeReplacementsResponse,
GetNodeReplacementsResponses,
GetOpenapiSpecData,
GetOpenapiSpecResponses,
GetOAuthAuthorizationServerData,
GetOAuthAuthorizationServerError,
GetOAuthAuthorizationServerErrors,
GetOAuthAuthorizationServerResponse,
GetOAuthAuthorizationServerResponses,
GetOAuthAuthorizeData,
GetOAuthAuthorizeError,
GetOAuthAuthorizeErrors,
GetOAuthAuthorizeResponse,
GetOAuthAuthorizeResponses,
GetOAuthProtectedResourceByPathData,
GetOAuthProtectedResourceByPathError,
GetOAuthProtectedResourceByPathErrors,
GetOAuthProtectedResourceByPathResponse,
GetOAuthProtectedResourceByPathResponses,
GetOAuthProtectedResourceData,
GetOAuthProtectedResourceError,
GetOAuthProtectedResourceErrors,
GetOAuthProtectedResourceResponse,
GetOAuthProtectedResourceResponses,
GetPaymentPortalData,
GetPaymentPortalError,
GetPaymentPortalErrors,
@@ -427,11 +465,11 @@ export type {
GetSecretErrors,
GetSecretResponse,
GetSecretResponses,
GetSettingByKeyData,
GetSettingByKeyError,
GetSettingByKeyErrors,
GetSettingByKeyResponse,
GetSettingByKeyResponses,
GetSettingByIdData,
GetSettingByIdError,
GetSettingByIdErrors,
GetSettingByIdResponse,
GetSettingByIdResponses,
GetStaticExtensionsData,
GetStaticExtensionsErrors,
GetStaticExtensionsResponses,
@@ -447,6 +485,7 @@ export type {
GetTaskResponses,
GetTemplateProxyData,
GetTemplateProxyErrors,
GetTemplateProxyResponses,
GetUserData,
GetUserdataData,
GetUserdataError,
@@ -534,6 +573,11 @@ export type {
ImportPublishedAssetsResponse,
ImportPublishedAssetsResponse2,
ImportPublishedAssetsResponses,
InsertDynamicConfigData,
InsertDynamicConfigError,
InsertDynamicConfigErrors,
InsertDynamicConfigResponse,
InsertDynamicConfigResponses,
InterruptJobData,
InterruptJobError,
InterruptJobErrors,
@@ -642,6 +686,17 @@ export type {
MoveUserdataFileResponse,
MoveUserdataFileResponses,
NodeInfo,
OAuthAuthorizationServerMetadata,
OAuthAuthorizeRedirectResponse,
OAuthConsentChallenge,
OAuthConsentChallengeWorkspace,
OAuthProtectedResourceMetadata,
OAuthRegisterBadRequestResponse,
OAuthRegisterError,
OAuthRegisterRequest,
OAuthRegisterResponse,
OAuthTokenError,
OAuthTokenResponse,
PaginationInfo,
PartnerUsageRequest,
PartnerUsageResponse,
@@ -663,6 +718,21 @@ export type {
PostMonitoringTasksSubpathData,
PostMonitoringTasksSubpathErrors,
PostMonitoringTasksSubpathResponses,
PostOAuthAuthorizeData,
PostOAuthAuthorizeError,
PostOAuthAuthorizeErrors,
PostOAuthAuthorizeResponse,
PostOAuthAuthorizeResponses,
PostOAuthRegisterData,
PostOAuthRegisterError,
PostOAuthRegisterErrors,
PostOAuthRegisterResponse,
PostOAuthRegisterResponses,
PostOAuthTokenData,
PostOAuthTokenError,
PostOAuthTokenErrors,
PostOAuthTokenResponse,
PostOAuthTokenResponses,
PostPprofSymbolData,
PostPprofSymbolResponses,
PostUserdataFileData,
@@ -687,6 +757,9 @@ export type {
PromptInfo,
PromptRequest,
PromptResponse,
PruneAssetsData,
PruneAssetsResponse,
PruneAssetsResponses,
PublishedWorkflowDetail,
PublishHubWorkflowData,
PublishHubWorkflowError,
@@ -732,6 +805,9 @@ export type {
RevokeWorkspaceInviteResponses,
SecretListResponse,
SecretResponse,
SeedAssetsData,
SeedAssetsResponse,
SeedAssetsResponses,
SetReviewStatusData,
SetReviewStatusError,
SetReviewStatusErrors,
@@ -751,6 +827,8 @@ export type {
SubscribeResponse,
SubscribeResponse2,
SubscribeResponses,
SubscribeToLogsData,
SubscribeToLogsResponses,
SubscriptionDuration,
SubscriptionTier,
SyncApiKeyData,
@@ -799,11 +877,11 @@ export type {
UpdateSecretRequest,
UpdateSecretResponse,
UpdateSecretResponses,
UpdateSettingByKeyData,
UpdateSettingByKeyError,
UpdateSettingByKeyErrors,
UpdateSettingByKeyResponse,
UpdateSettingByKeyResponses,
UpdateSettingByIdData,
UpdateSettingByIdError,
UpdateSettingByIdErrors,
UpdateSettingByIdResponse,
UpdateSettingByIdResponses,
UpdateSubscriptionCacheData,
UpdateSubscriptionCacheError,
UpdateSubscriptionCacheErrors,

File diff suppressed because it is too large Load Diff

View File

@@ -879,6 +879,153 @@ export const zJwkKey = z.object({
y: z.string()
})
/**
* RFC 6749 §5.2 error response.
*/
export const zOAuthTokenError = z.object({
error: z.string(),
error_description: z.string().optional()
})
/**
* RFC 6749 §5.1 successful token response.
*/
export const zOAuthTokenResponse = z.object({
access_token: z.string(),
token_type: z.enum(['Bearer']),
expires_in: z.number().int(),
refresh_token: z.string(),
scope: z.string()
})
/**
* One workspace option presented in the OAuth consent challenge. Promoted to a named schema so the generated Go type is referenceable in handlers and tests rather than re-declared as an anonymous struct at every callsite.
*
*/
export const zOAuthConsentChallengeWorkspace = z.object({
id: z.string(),
name: z.string(),
type: z.enum(['personal', 'team']),
role: z.enum(['owner', 'member'])
})
/**
* Redirect target produced after a JSON consent submission. The frontend must navigate the browser to this URL so custom-scheme client callbacks work without relying on fetch-visible 302 headers.
*/
export const zOAuthAuthorizeRedirectResponse = z.object({
redirect_url: z.string().url()
})
/**
* Server-side state describing the OAuth consent decision the user is being asked to make. Returned by GET /oauth/authorize when a valid Cloud session exists; the frontend renders the consent UI from this payload and POSTs the decision back. Browser never sees the original OAuth params on resume.
*
*/
export const zOAuthConsentChallenge = z.object({
oauth_request_id: z.string().uuid(),
csrf_token: z.string(),
client_display_name: z.string(),
resource_display_name: z.string(),
scopes: z.array(z.string()),
workspaces: z.array(zOAuthConsentChallengeWorkspace)
})
/**
* OAuth 2.1 protected-resource metadata (RFC 9728).
*/
export const zOAuthProtectedResourceMetadata = z.object({
resource: z.string().url(),
authorization_servers: z.array(z.string().url()),
scopes_supported: z.array(z.string()),
bearer_methods_supported: z.array(z.string()).optional()
})
/**
* RFC 7591 §3.2.2 error response.
*/
export const zOAuthRegisterError = z.object({
error: z.enum(['invalid_redirect_uri', 'invalid_client_metadata']),
error_description: z.string().nullish()
})
/**
* Error shape returned when request binding or validation fails before the handler runs.
*/
export const zBindingErrorResponse = z.object({
message: z.string()
})
/**
* Union of the two 400 shapes /oauth/register can emit. `OAuthRegisterError` is the handler-shaped RFC 7591 §3.2.2 error; `BindingErrorResponse` is the strict-server binding-layer error fired when the request body fails OpenAPI-schema validation before the handler runs.
*
*/
export const zOAuthRegisterBadRequestResponse = z.union([
zOAuthRegisterError,
zBindingErrorResponse
])
/**
* RFC 7591 §3.2.1 successful registration response.
*/
export const zOAuthRegisterResponse = z.object({
client_id: z.string(),
client_id_issued_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
message: 'Invalid value: Expected int64 to be >= -9223372036854775808'
})
.max(BigInt('9223372036854775807'), {
message: 'Invalid value: Expected int64 to be <= 9223372036854775807'
}),
client_name: z.string().optional(),
redirect_uris: z.array(z.string()),
grant_types: z.array(z.string()),
response_types: z.array(z.string()),
token_endpoint_auth_method: z.enum(['none']),
application_type: z.enum(['native', 'web'])
})
/**
* RFC 7591 §2 client metadata document. Only the fields the server honors are listed; presence of `scope` or `resource_grants` in the request is rejected (`invalid_client_metadata`) because those are server-owned for dynamic clients. `additionalProperties: false` mirrors the runtime middleware that rejects any unknown metadata key.
*
*/
export const zOAuthRegisterRequest = z.object({
redirect_uris: z.array(z.string()).min(1).max(5),
client_name: z.string().max(100).optional(),
application_type: z.enum(['native', 'web']).optional(),
token_endpoint_auth_method: z.enum(['none']).optional(),
grant_types: z
.array(z.enum(['authorization_code', 'refresh_token']))
.optional(),
response_types: z.array(z.enum(['code'])).optional(),
scope: z.string().nullish(),
resource_grants: z.record(z.array(z.string())).nullish(),
client_uri: z.string().nullish(),
logo_uri: z.string().nullish(),
tos_uri: z.string().nullish(),
policy_uri: z.string().nullish(),
software_id: z.string().nullish(),
software_version: z.string().nullish(),
contacts: z.array(z.string()).nullish(),
jwks: z.record(z.unknown()).nullish(),
jwks_uri: z.string().nullish()
})
/**
* OAuth 2.1 authorization-server metadata (RFC 8414).
*/
export const zOAuthAuthorizationServerMetadata = z.object({
issuer: z.string().url(),
authorization_endpoint: z.string().url(),
token_endpoint: z.string().url(),
jwks_uri: z.string().url(),
registration_endpoint: z.string().url().optional(),
response_types_supported: z.array(z.string()),
grant_types_supported: z.array(z.string()),
code_challenge_methods_supported: z.array(z.string()),
token_endpoint_auth_methods_supported: z.array(z.string()),
scopes_supported: z.array(z.string()).optional()
})
/**
* JSON Web Key Set containing the public keys used to verify Cloud JWTs.
*/
@@ -940,6 +1087,7 @@ export const zWorkspaceApiKeyInfo = z.object({
workspace_id: z.string(),
user_id: z.string(),
name: z.string(),
description: z.string().max(5000),
key_prefix: z.string(),
expires_at: z.string().datetime().optional(),
last_used_at: z.string().datetime().optional(),
@@ -960,6 +1108,7 @@ export const zListWorkspaceApiKeysResponse = z.object({
export const zCreateWorkspaceApiKeyResponse = z.object({
id: z.string().uuid(),
name: z.string(),
description: z.string().max(5000),
key: z.string(),
key_prefix: z.string(),
expires_at: z.string().datetime().optional(),
@@ -971,6 +1120,7 @@ export const zCreateWorkspaceApiKeyResponse = z.object({
*/
export const zCreateWorkspaceApiKeyRequest = z.object({
name: z.string(),
description: z.string().max(5000).optional(),
expires_at: z.string().datetime().optional()
})
@@ -1353,6 +1503,7 @@ export const zListTagsResponse = z.object({
export const zAsset = z.object({
id: z.string().uuid(),
name: z.string(),
display_name: z.string().nullish(),
asset_hash: z
.string()
.regex(/^blake3:[a-f0-9]{64}$/)
@@ -1385,7 +1536,8 @@ export const zAsset = z.object({
export const zListAssetsResponse = z.object({
assets: z.array(zAsset),
total: z.number().int(),
has_more: z.boolean()
has_more: z.boolean(),
next_cursor: z.string().optional()
})
/**
@@ -1394,6 +1546,7 @@ export const zListAssetsResponse = z.object({
export const zAssetUpdated = z.object({
id: z.string().uuid(),
name: z.string().optional(),
display_name: z.string().nullish(),
asset_hash: z
.string()
.regex(/^blake3:[a-f0-9]{64}$/)
@@ -1753,13 +1906,6 @@ export const zExportDownloadUrlResponse = z.object({
expires_at: z.string().datetime().optional()
})
/**
* Error shape returned when request binding or validation fails before the handler runs.
*/
export const zBindingErrorResponse = z.object({
message: z.string()
})
/**
* Standard error response with a machine-readable code and human-readable message.
*/
@@ -1796,6 +1942,7 @@ export const zPromptRequest = z.object({
export const zAssetWritable = z.object({
id: z.string().uuid(),
name: z.string(),
display_name: z.string().nullish(),
asset_hash: z
.string()
.regex(/^blake3:[a-f0-9]{64}$/)
@@ -1827,7 +1974,8 @@ export const zAssetWritable = z.object({
export const zListAssetsResponseWritable = z.object({
assets: z.array(zAssetWritable),
total: z.number().int(),
has_more: z.boolean()
has_more: z.boolean(),
next_cursor: z.string().optional()
})
/**
@@ -1961,21 +2109,6 @@ export const zGetModelsInFolderData = z.object({
*/
export const zGetModelsInFolderResponse = z.array(zModelFile)
export const zGetModelPreviewData = z.object({
body: z.never().optional(),
path: z.object({
folder: z.string(),
path_index: z.number().int(),
filename: z.string()
}),
query: z.never().optional()
})
/**
* Success - Model preview image
*/
export const zGetModelPreviewResponse = z.string()
export const zGetLegacyHistoryData = z.object({
body: z.never().optional(),
path: z.never().optional(),
@@ -2132,9 +2265,9 @@ export const zListAssetsData = z.object({
.enum(['name', 'created_at', 'updated_at', 'size', 'last_access_time'])
.optional(),
order: z.enum(['asc', 'desc']).optional(),
job_ids: z.array(z.string().uuid()).optional(),
include_public: z.boolean().optional().default(true),
asset_hash: z.string().optional()
asset_hash: z.string().optional(),
after: z.string().optional()
})
.optional()
})
@@ -2157,7 +2290,7 @@ export const zUploadAssetData = z.object({
})
/**
* Asset already exists (returned existing asset)
* Asset created successfully
*/
export const zUploadAssetResponse = zAssetCreated
@@ -2174,7 +2307,7 @@ export const zCreateAssetFromHashData = z.object({
})
/**
* Asset reference already exists (returned existing)
* Asset reference created successfully
*/
export const zCreateAssetFromHashResponse = zAssetCreated
@@ -2214,7 +2347,8 @@ export const zCreateAssetExportData = z.object({
naming_strategy: z
.enum(['group_by_job_id', 'preserve', 'asset_id', 'group_by_job_time'])
.optional(),
job_asset_name_filters: z.record(z.array(z.string()).min(1)).optional()
job_asset_name_filters: z.record(z.array(z.string()).min(1)).optional(),
include_previews: z.boolean().optional().default(false)
}),
path: z.never().optional(),
query: z.never().optional()
@@ -2509,10 +2643,10 @@ export const zUpdateMultipleSettingsData = z.object({
*/
export const zUpdateMultipleSettingsResponse = z.record(z.unknown())
export const zGetSettingByKeyData = z.object({
export const zGetSettingByIdData = z.object({
body: z.never().optional(),
path: z.object({
key: z.string()
id: z.string()
}),
query: z.never().optional()
})
@@ -2520,14 +2654,14 @@ export const zGetSettingByKeyData = z.object({
/**
* Setting value response
*/
export const zGetSettingByKeyResponse = z.object({
export const zGetSettingByIdResponse = z.object({
value: z.unknown().optional()
})
export const zUpdateSettingByKeyData = z.object({
export const zUpdateSettingByIdData = z.object({
body: z.unknown(),
path: z.object({
key: z.string()
id: z.string()
}),
query: z.never().optional()
})
@@ -2535,7 +2669,7 @@ export const zUpdateSettingByKeyData = z.object({
/**
* Updated setting value response
*/
export const zUpdateSettingByKeyResponse = z.object({
export const zUpdateSettingByIdResponse = z.object({
value: z.unknown().optional()
})
@@ -2691,21 +2825,7 @@ export const zUploadMaskData = z.object({
export const zUploadMaskResponse = z.object({
name: z.string().optional(),
subfolder: z.string().optional(),
type: z.string().optional(),
metadata: z
.object({
is_mask: z.boolean().optional(),
original_hash: z.string().optional(),
mask_type: z.string().optional(),
related_files: z
.object({
mask: z.string().optional(),
paint: z.string().optional(),
painted: z.string().optional()
})
.optional()
})
.optional()
type: z.string().optional()
})
export const zGetLogsData = z.object({
@@ -2774,6 +2894,115 @@ export const zGetJwksData = z.object({
*/
export const zGetJwksResponse = zJwksResponse
export const zGetOAuthAuthorizationServerData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Authorization-server metadata
*/
export const zGetOAuthAuthorizationServerResponse =
zOAuthAuthorizationServerMetadata
export const zGetOAuthProtectedResourceData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Protected-resource metadata
*/
export const zGetOAuthProtectedResourceResponse =
zOAuthProtectedResourceMetadata
export const zGetOAuthProtectedResourceByPathData = z.object({
body: z.never().optional(),
path: z.object({
resourcePath: z.string().regex(/^[a-zA-Z0-9._-]+$/)
}),
query: z.never().optional()
})
/**
* Protected-resource metadata
*/
export const zGetOAuthProtectedResourceByPathResponse =
zOAuthProtectedResourceMetadata
export const zGetOAuthAuthorizeData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z
.object({
response_type: z.string().optional(),
client_id: z.string().optional(),
redirect_uri: z.string().optional(),
scope: z.string().optional(),
state: z.string().optional(),
code_challenge: z.string().optional(),
code_challenge_method: z.string().optional(),
resource: z.string().optional(),
oauth_request_id: z.string().optional()
})
.optional()
})
/**
* Consent challenge payload (cookie present, email verified). Frontend renders the consent UI from this payload and POSTs back to /oauth/authorize.
*
*/
export const zGetOAuthAuthorizeResponse = zOAuthConsentChallenge
export const zPostOAuthAuthorizeData = z.object({
body: z.object({
oauth_request_id: z.string().uuid(),
csrf_token: z.string(),
decision: z.enum(['allow', 'deny']),
workspace_id: z.string()
}),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Redirect URL for the frontend to navigate to (allow → with code+state; deny → with error+state)
*/
export const zPostOAuthAuthorizeResponse = zOAuthAuthorizeRedirectResponse
export const zPostOAuthTokenData = z.object({
body: z.object({
grant_type: z.enum(['authorization_code', 'refresh_token']),
client_id: z.string(),
code: z.string().optional(),
redirect_uri: z.string().optional(),
code_verifier: z.string().optional(),
refresh_token: z.string().optional(),
scope: z.string().optional(),
client_secret: z.string().optional()
}),
path: z.never().optional(),
query: z.never().optional()
})
/**
* New token pair
*/
export const zPostOAuthTokenResponse = zOAuthTokenResponse
export const zPostOAuthRegisterData = z.object({
body: zOAuthRegisterRequest,
path: z.never().optional(),
query: z.never().optional()
})
/**
* Registered. Body echoes the metadata RFC 7591 §3.2.1 requires.
*/
export const zPostOAuthRegisterResponse = zOAuthRegisterResponse
export const zListWorkspacesData = z.object({
body: z.never().optional(),
path: z.never().optional(),
@@ -3078,6 +3307,28 @@ export const zUpdateSubscriptionCacheResponse = z.object({
status: z.string().optional()
})
export const zInsertDynamicConfigData = z.object({
body: z.record(z.unknown()),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Config inserted successfully
*/
export const zInsertDynamicConfigResponse = z.object({
id: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
message: 'Invalid value: Expected int64 to be >= -9223372036854775808'
})
.max(BigInt('9223372036854775807'), {
message: 'Invalid value: Expected int64 to be <= 9223372036854775807'
})
.optional(),
message: z.string().optional()
})
export const zSyncApiKeyData = z.object({
body: zSyncApiKeyRequest,
path: z.never().optional(),
@@ -3671,12 +3922,6 @@ export const zGetHealthData = z.object({
*/
export const zGetHealthResponse = z.string()
export const zGetOpenapiSpecData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
export const zGetMonitoringTasksData = z.object({
body: z.never().optional(),
path: z.never().optional(),
@@ -3757,6 +4002,16 @@ export const zPostCustomNodeProxyData = z.object({
query: z.never().optional()
})
export const zGetModelPreviewData = z.object({
body: z.never().optional(),
path: z.object({
folder: z.string(),
path_index: z.number().int(),
filename: z.string()
}),
query: z.never().optional()
})
export const zGetLegacyPromptByIdData = z.object({
body: z.never().optional(),
path: z.object({
@@ -3832,3 +4087,150 @@ export const zGetLegacyViewMetadataData = z.object({
}),
query: z.never().optional()
})
export const zGetEmbeddingsData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Embedding names
*/
export const zGetEmbeddingsResponse = z.array(z.string())
export const zFreeMemoryData = z.object({
body: z
.object({
unload_models: z.boolean().optional(),
free_memory: z.boolean().optional()
})
.optional(),
path: z.never().optional(),
query: z.never().optional()
})
export const zGetI18nData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Nested map of locale to translation key-value pairs
*/
export const zGetI18nResponse = z.record(z.unknown())
export const zGetInternalFolderPathsData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Map of folder type name to list of path entries
*/
export const zGetInternalFolderPathsResponse = z.record(
z.array(z.array(z.string()))
)
export const zGetInternalLogsData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Log text
*/
export const zGetInternalLogsResponse = z.string()
export const zGetInternalLogsRawData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Structured log data
*/
export const zGetInternalLogsRawResponse = z.object({
entries: z
.array(
z.object({
t: z.number().optional(),
m: z.string().optional()
})
)
.optional(),
size: z
.object({
cols: z.number().int().optional(),
rows: z.number().int().optional()
})
.optional()
})
export const zSubscribeToLogsData = z.object({
body: z.object({
clientId: z.string(),
enabled: z.boolean()
}),
path: z.never().optional(),
query: z.never().optional()
})
export const zPruneAssetsData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Prune result
*/
export const zPruneAssetsResponse = z.object({
status: z.string().optional(),
marked: z.number().int().optional()
})
export const zSeedAssetsData = z.object({
body: z
.object({
roots: z.array(z.string()).optional()
})
.optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Seed started
*/
export const zSeedAssetsResponse = z.object({
status: z.string().optional()
})
export const zGetAssetSeedStatusData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Scan progress details (files scanned, total, status, etc.)
*/
export const zGetAssetSeedStatusResponse = z.record(z.unknown())
export const zCancelAssetSeedData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional()
})
/**
* Scan cancelled
*/
export const zCancelAssetSeedResponse = z.object({
status: z.string().optional()
})

View File

@@ -1,110 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { Ref } from 'vue'
import { effectScope, nextTick, ref } from 'vue'
import type { AppMode } from './useAppMode'
const hoisted = vi.hoisted(() => ({
telemetry: null as { trackModeTimeSpent: ReturnType<typeof vi.fn> } | null,
mode: null as unknown as Ref<AppMode>
}))
vi.mock('@/platform/telemetry', () => ({
useTelemetry: () => hoisted.telemetry
}))
vi.mock('./useAppMode', () => ({
useAppMode: () => ({ mode: hoisted.mode })
}))
import { useModeTimeTracking } from './useModeTimeTracking'
function setVisibility(state: 'visible' | 'hidden') {
Object.defineProperty(document, 'visibilityState', {
value: state,
configurable: true
})
document.dispatchEvent(new Event('visibilitychange'))
}
describe('useModeTimeTracking', () => {
let scope: ReturnType<typeof effectScope>
beforeEach(() => {
vi.useFakeTimers()
vi.setSystemTime(0)
hoisted.telemetry = { trackModeTimeSpent: vi.fn() }
hoisted.mode = ref<AppMode>('graph')
setVisibility('visible')
scope = effectScope()
})
afterEach(() => {
scope.stop()
vi.useRealTimers()
})
const track = () => hoisted.telemetry!.trackModeTimeSpent
it('emits the elapsed seconds for the previous mode on mode change', async () => {
scope.run(() => useModeTimeTracking())
vi.setSystemTime(5000)
hoisted.mode.value = 'app'
await nextTick()
expect(track()).toHaveBeenCalledWith({
mode: 'graph',
duration_seconds: 5
})
})
it('excludes time while the tab is hidden', async () => {
hoisted.mode = ref<AppMode>('app')
scope.run(() => useModeTimeTracking())
vi.setSystemTime(2000)
setVisibility('hidden')
expect(track()).toHaveBeenCalledWith({ mode: 'app', duration_seconds: 2 })
track().mockClear()
vi.setSystemTime(10_000)
setVisibility('visible')
vi.setSystemTime(11_000)
hoisted.mode.value = 'graph'
await nextTick()
expect(track()).toHaveBeenCalledTimes(1)
expect(track()).toHaveBeenCalledWith({ mode: 'app', duration_seconds: 1 })
})
it('does not emit sub-second durations', async () => {
scope.run(() => useModeTimeTracking())
vi.setSystemTime(300)
hoisted.mode.value = 'app'
await nextTick()
expect(track()).not.toHaveBeenCalled()
})
it('flushes the final mode when the scope is disposed', () => {
hoisted.mode = ref<AppMode>('app')
scope.run(() => useModeTimeTracking())
vi.setSystemTime(3000)
scope.stop()
expect(track()).toHaveBeenCalledWith({ mode: 'app', duration_seconds: 3 })
})
it('no-ops without a telemetry provider', async () => {
hoisted.telemetry = null
expect(() => scope.run(() => useModeTimeTracking())).not.toThrow()
vi.setSystemTime(5000)
hoisted.mode.value = 'app'
await expect(nextTick()).resolves.not.toThrow()
})
})

View File

@@ -1,65 +0,0 @@
import { tryOnScopeDispose, useEventListener } from '@vueuse/core'
import { watch } from 'vue'
import { useTelemetry } from '@/platform/telemetry'
import type { AppMode } from './useAppMode'
import { useAppMode } from './useAppMode'
/**
* Tracks active (tab-visible) time spent in each {@link AppMode} and emits an
* `app:mode_time_spent` event whenever the user leaves a mode, backgrounds the
* tab, or tears down. Hidden-tab time is excluded, and flushing on tab-hide
* means a closing tab is still captured (`visibilitychange` fires before
* unload). Summing `duration_seconds` grouped by `mode` yields time per mode.
*
* Side-effecting composable: call once from a long-lived scope (GraphView).
*/
export function useModeTimeTracking() {
const dispatcher = useTelemetry()
if (!dispatcher) return
const trackModeTimeSpent = dispatcher.trackModeTimeSpent.bind(dispatcher)
const { mode } = useAppMode()
const isVisible = () => document.visibilityState === 'visible'
let trackedMode: AppMode = mode.value
let accumulatedMs = 0
let segmentStart: number | null = isVisible() ? Date.now() : null
function closeSegment() {
if (segmentStart !== null) {
accumulatedMs += Date.now() - segmentStart
segmentStart = null
}
}
function flush() {
closeSegment()
const durationSeconds = Math.round(accumulatedMs / 1000)
accumulatedMs = 0
if (durationSeconds >= 1) {
trackModeTimeSpent({
mode: trackedMode,
duration_seconds: durationSeconds
})
}
}
watch(mode, (newMode) => {
flush()
trackedMode = newMode
segmentStart = isVisible() ? Date.now() : null
})
useEventListener(document, 'visibilitychange', () => {
if (isVisible()) {
segmentStart = Date.now()
} else {
flush()
}
})
tryOnScopeDispose(flush)
}

View File

@@ -5,7 +5,6 @@ import type {
BeginCheckoutMetadata,
DefaultViewSetMetadata,
EnterLinearMetadata,
ModeTimeSpentMetadata,
ShareFlowMetadata,
ExecutionErrorMetadata,
ExecutionSuccessMetadata,
@@ -177,10 +176,6 @@ export class TelemetryRegistry implements TelemetryDispatcher {
this.dispatch((provider) => provider.trackEnterLinear?.(metadata))
}
trackModeTimeSpent(metadata: ModeTimeSpentMetadata): void {
this.dispatch((provider) => provider.trackModeTimeSpent?.(metadata))
}
trackShareFlow(metadata: ShareFlowMetadata): void {
this.dispatch((provider) => provider.trackShareFlow?.(metadata))
}

View File

@@ -3,7 +3,6 @@ import type {
BeginCheckoutMetadata,
DefaultViewSetMetadata,
EnterLinearMetadata,
ModeTimeSpentMetadata,
ExecutionErrorMetadata,
ExecutionSuccessMetadata,
ExecutionTriggerSource,
@@ -281,15 +280,7 @@ export class GtmTelemetryProvider implements TelemetryProvider {
trackEnterLinear(metadata: EnterLinearMetadata): void {
this.pushEvent('app_mode_opened', {
source: metadata.source,
open_source: metadata.open_source
})
}
trackModeTimeSpent(metadata: ModeTimeSpentMetadata): void {
this.pushEvent('mode_time_spent', {
mode: metadata.mode,
duration_seconds: metadata.duration_seconds
source: metadata.source
})
}

View File

@@ -61,7 +61,6 @@ import type {
EnterLinearMetadata,
ExecutionErrorMetadata,
ExecutionSuccessMetadata,
ModeTimeSpentMetadata,
ShareFlowMetadata,
SurveyResponses,
TemplateLibraryClosedMetadata,
@@ -288,10 +287,6 @@ describe('MixpanelTelemetryProvider — direct event tracking methods', () => {
default_view: 'graph'
}
const enterLinearMetadata: EnterLinearMetadata = {}
const modeTimeSpentMetadata: ModeTimeSpentMetadata = {
mode: 'app',
duration_seconds: 42
}
const shareFlowMetadata: ShareFlowMetadata = { step: 'dialog_opened' }
const executionErrorMetadata: ExecutionErrorMetadata = { jobId: 'job-1' }
const executionSuccessMetadata: ExecutionSuccessMetadata = { jobId: 'job-1' }
@@ -355,11 +350,6 @@ describe('MixpanelTelemetryProvider — direct event tracking methods', () => {
(p) => p.trackEnterLinear(enterLinearMetadata),
TelemetryEvents.ENTER_LINEAR_MODE
],
[
'trackModeTimeSpent',
(p) => p.trackModeTimeSpent(modeTimeSpentMetadata),
TelemetryEvents.MODE_TIME_SPENT
],
[
'trackShareFlow',
(p) => p.trackShareFlow(shareFlowMetadata),

View File

@@ -17,7 +17,6 @@ import type {
CreditTopupMetadata,
DefaultViewSetMetadata,
EnterLinearMetadata,
ModeTimeSpentMetadata,
ShareFlowMetadata,
ExecutionContext,
ExecutionTriggerSource,
@@ -381,10 +380,6 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
this.trackEvent(TelemetryEvents.ENTER_LINEAR_MODE, metadata)
}
trackModeTimeSpent(metadata: ModeTimeSpentMetadata): void {
this.trackEvent(TelemetryEvents.MODE_TIME_SPENT, metadata)
}
trackShareFlow(metadata: ShareFlowMetadata): void {
this.trackEvent(TelemetryEvents.SHARE_FLOW, metadata)
}

View File

@@ -13,7 +13,6 @@ import type {
AuthMetadata,
DefaultViewSetMetadata,
EnterLinearMetadata,
ModeTimeSpentMetadata,
ShareFlowMetadata,
ExecutionContext,
ExecutionErrorMetadata,
@@ -392,10 +391,6 @@ export class PostHogTelemetryProvider implements TelemetryProvider {
this.trackEvent(TelemetryEvents.ENTER_LINEAR_MODE, metadata)
}
trackModeTimeSpent(metadata: ModeTimeSpentMetadata): void {
this.trackEvent(TelemetryEvents.MODE_TIME_SPENT, metadata)
}
trackShareFlow(metadata: ShareFlowMetadata): void {
this.trackEvent(TelemetryEvents.SHARE_FLOW, metadata)
}

View File

@@ -12,7 +12,6 @@
* 3. Check dist/assets/*.js files contain no tracking code
*/
import type { AppMode } from '@/composables/useAppMode'
import type { SubscriptionDialogReason } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'
import type { TierKey } from '@/platform/cloud/subscription/constants/tierPricing'
import type { BillingCycle } from '@/platform/cloud/subscription/utils/subscriptionTierRank'
@@ -166,12 +165,6 @@ export interface WorkflowImportMetadata {
export interface EnterLinearMetadata {
source?: string
/**
* The workflow's entry path for this session. `'shared_url'` means App Mode
* was reached via a link someone shared (`?share=`), as opposed to the user
* opening their own/original workflow.
*/
open_source?: AppModeOpenSource
workflow_id?: string
}
@@ -181,16 +174,6 @@ export interface WorkflowSavedMetadata {
workflow_id?: string
}
/**
* Mode time-spent metadata. Emitted when leaving a mode (or hiding the tab),
* reporting the active (tab-visible) seconds spent in that mode. Summing
* `duration_seconds` grouped by `mode` yields time spent per mode.
*/
export interface ModeTimeSpentMetadata {
mode: AppMode
duration_seconds: number
}
export interface DefaultViewSetMetadata {
default_view: 'app' | 'graph'
}
@@ -218,12 +201,6 @@ export type WorkflowOpenSource = NonNullable<
WorkflowImportMetadata['open_source']
>
/**
* Narrower type for app mode entry tracking.
* Maps WorkflowOpenSource values to allowed app mode sources.
*/
export type AppModeOpenSource = 'shared_url' | 'original' | 'unknown'
/**
* Template library metadata
*/
@@ -462,7 +439,6 @@ export interface TelemetryProvider {
trackWorkflowSaved?(metadata: WorkflowSavedMetadata): void
trackDefaultViewSet?(metadata: DefaultViewSetMetadata): void
trackEnterLinear?(metadata: EnterLinearMetadata): void
trackModeTimeSpent?(metadata: ModeTimeSpentMetadata): void
trackShareFlow?(metadata: ShareFlowMetadata): void
// Page visibility events
@@ -550,7 +526,6 @@ export const TelemetryEvents = {
WORKFLOW_IMPORTED: 'app:workflow_imported',
WORKFLOW_OPENED: 'app:workflow_opened',
ENTER_LINEAR_MODE: 'app:app_mode_opened',
MODE_TIME_SPENT: 'app:mode_time_spent',
SHARE_FLOW: 'app:share_flow',
// Page Visibility
@@ -627,7 +602,6 @@ export type TelemetryEventProperties =
| HelpCenterClosedMetadata
| WorkflowCreatedMetadata
| EnterLinearMetadata
| ModeTimeSpentMetadata
| ShareFlowMetadata
| WorkflowSavedMetadata
| DefaultViewSetMetadata

View File

@@ -61,12 +61,10 @@ function makeWorkflowData(
}
}
const { mockConfirm, mockTrackWorkflowSaved, mockTrackEnterLinear } =
vi.hoisted(() => ({
mockConfirm: vi.fn(),
mockTrackWorkflowSaved: vi.fn(),
mockTrackEnterLinear: vi.fn()
}))
const { mockConfirm, mockTrackWorkflowSaved } = vi.hoisted(() => ({
mockConfirm: vi.fn(),
mockTrackWorkflowSaved: vi.fn()
}))
const draftStoreMocks = vi.hoisted(() => ({
saveDraft: vi.fn(() => true),
@@ -110,7 +108,7 @@ vi.mock('@/platform/telemetry', () => ({
useTelemetry: () => ({
trackDefaultViewSet: vi.fn(),
trackWorkflowSaved: mockTrackWorkflowSaved,
trackEnterLinear: mockTrackEnterLinear
trackEnterLinear: vi.fn()
})
}))
@@ -846,69 +844,6 @@ describe('useWorkflowService', () => {
})
})
describe('app:app_mode_opened open_source', () => {
beforeEach(() => {
mockOpenWorkflow()
mockTrackEnterLinear.mockClear()
})
it('tags the entry as shared_url when loaded from a shared link', async () => {
const workflow = createModeTestWorkflow({ loaded: false })
await service.afterLoadNewGraph(
workflow,
makeWorkflowData({ linearMode: true }),
'shared_url'
)
expect(mockTrackEnterLinear).toHaveBeenCalledWith({
source: 'workflow',
open_source: 'shared_url'
})
})
it('defaults open_source to unknown when no source is provided', async () => {
const workflow = createModeTestWorkflow({ loaded: false })
await service.afterLoadNewGraph(
workflow,
makeWorkflowData({ linearMode: true })
)
expect(mockTrackEnterLinear).toHaveBeenCalledWith({
source: 'workflow',
open_source: 'unknown'
})
})
it('tags the entry as original when loaded from a non-shared source', async () => {
const workflow = createModeTestWorkflow({ loaded: false })
await service.afterLoadNewGraph(
workflow,
makeWorkflowData({ linearMode: true }),
'template'
)
expect(mockTrackEnterLinear).toHaveBeenCalledWith({
source: 'workflow',
open_source: 'original'
})
})
it('does not fire when the workflow is not in app mode', async () => {
const workflow = createModeTestWorkflow({ loaded: false })
await service.afterLoadNewGraph(
workflow,
makeWorkflowData({ linearMode: false }),
'shared_url'
)
expect(mockTrackEnterLinear).not.toHaveBeenCalled()
})
})
describe('round-trip mode preservation', () => {
it('each workflow retains its own mode across tab switches', () => {
const workflow1 = createModeTestWorkflow({

View File

@@ -16,10 +16,6 @@ import {
useWorkflowStore
} from '@/platform/workflow/management/stores/workflowStore'
import { useTelemetry } from '@/platform/telemetry'
import type {
WorkflowOpenSource,
AppModeOpenSource
} from '@/platform/telemetry/types'
import { workflowTelemetryId } from '@/platform/telemetry/utils/workflowTelemetryId'
import type { ComfyWorkflowJSON } from '@/platform/workflow/validation/schemas/workflowSchema'
// eslint-disable-next-line import-x/no-restricted-paths
@@ -48,13 +44,6 @@ function linearModeToAppMode(linearMode: unknown): AppMode | null {
return linearMode ? 'app' : 'graph'
}
function normalizeOpenSource(
source: WorkflowOpenSource | undefined
): AppModeOpenSource {
if (source === 'shared_url') return 'shared_url'
return source ? 'original' : 'unknown'
}
export const useWorkflowService = () => {
const settingStore = useSettingStore()
const workflowStore = useWorkflowStore()
@@ -467,8 +456,7 @@ export const useWorkflowService = () => {
*/
const afterLoadNewGraph = async (
value: string | ComfyWorkflow | null,
workflowData: ComfyWorkflowJSON,
openSource?: WorkflowOpenSource
workflowData: ComfyWorkflowJSON
) => {
const workflowStore = useWorkspaceStore().workflow
const { isAppMode } = useAppMode()
@@ -483,7 +471,6 @@ export const useWorkflowService = () => {
if (!wasAppMode && workflow.initialMode === 'app') {
useTelemetry()?.trackEnterLinear({
source: 'workflow',
open_source: normalizeOpenSource(openSource),
workflow_id: workflowTelemetryId(workflow)
})
}

View File

@@ -1433,11 +1433,7 @@ export class ComfyApp {
}
useTelemetry()?.trackWorkflowOpened(telemetryPayload)
useTelemetry()?.trackWorkflowImported(telemetryPayload)
await useWorkflowService().afterLoadNewGraph(
workflow,
serializedGraph,
openSource
)
await useWorkflowService().afterLoadNewGraph(workflow, serializedGraph)
// If the canvas was not visible and we're a fresh load, resize the canvas and fit the view
// This fixes switching from app mode to a new graph mode workflow (e.g. load template)

View File

@@ -54,7 +54,6 @@ import InviteAcceptedToast from '@/platform/workspace/components/toasts/InviteAc
import RerouteMigrationToast from '@/components/toast/RerouteMigrationToast.vue'
import { useBrowserTabTitle } from '@/composables/useBrowserTabTitle'
import { useCoreCommands } from '@/composables/useCoreCommands'
import { useModeTimeTracking } from '@/composables/useModeTimeTracking'
import { useQueuePolling } from '@/platform/remote/comfyui/useQueuePolling'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { useReconnectQueueRefresh } from '@/composables/useReconnectQueueRefresh'
@@ -124,12 +123,6 @@ const telemetry = useTelemetry()
const authStore = useAuthStore()
let hasTrackedLogin = false
// Track active time spent in each mode (cloud only). Must run in the
// synchronous setup scope so the composable's scope-bound cleanup registers.
if (isCloud && telemetry) {
useModeTimeTracking()
}
watch(
() => colorPaletteStore.completedActivePalette,
(newTheme) => {