@sync.so/mcp-server 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +261 -86
- package/deploy/generation-status/app.html.gz +0 -0
- package/deploy/generation-status/release.json +6 -0
- package/dist/abort-signals.d.ts +6 -0
- package/dist/abort-signals.d.ts.map +1 -0
- package/dist/abort-signals.js +23 -0
- package/dist/account-features.d.ts +7 -0
- package/dist/account-features.d.ts.map +1 -0
- package/dist/account-features.js +48 -0
- package/dist/app-upload-relay.d.ts +13 -0
- package/dist/app-upload-relay.d.ts.map +1 -0
- package/dist/app-upload-relay.js +136 -0
- package/dist/asset-thumbnail.d.ts +3 -0
- package/dist/asset-thumbnail.d.ts.map +1 -0
- package/dist/asset-thumbnail.js +2 -0
- package/dist/auth/async-context.d.ts +5 -0
- package/dist/auth/async-context.d.ts.map +1 -0
- package/dist/auth/async-context.js +14 -0
- package/dist/auth/bearer-auth.d.ts +16 -0
- package/dist/auth/bearer-auth.d.ts.map +1 -0
- package/dist/auth/bearer-auth.js +55 -0
- package/dist/auth/client-credentials.d.ts +13 -0
- package/dist/auth/client-credentials.d.ts.map +1 -0
- package/dist/auth/client-credentials.js +67 -0
- package/dist/auth/device-auth.d.ts.map +1 -1
- package/dist/auth/device-auth.js +3 -2
- package/dist/auth/fixture-cleanup.d.ts +4 -0
- package/dist/auth/fixture-cleanup.d.ts.map +1 -0
- package/dist/auth/fixture-cleanup.js +9 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +2 -0
- package/dist/auth/oauth-diagnostics.d.ts +11 -0
- package/dist/auth/oauth-diagnostics.d.ts.map +1 -0
- package/dist/auth/oauth-diagnostics.js +78 -0
- package/dist/auth/oauth-provider.d.ts +16 -0
- package/dist/auth/oauth-provider.d.ts.map +1 -0
- package/dist/auth/oauth-provider.js +107 -0
- package/dist/auth/resource-metadata.d.ts +2 -0
- package/dist/auth/resource-metadata.d.ts.map +1 -0
- package/dist/auth/resource-metadata.js +3 -0
- package/dist/auth/token-verification.d.ts +11 -0
- package/dist/auth/token-verification.d.ts.map +1 -0
- package/dist/auth/token-verification.js +99 -0
- package/dist/auth/verification-cache.d.ts +8 -0
- package/dist/auth/verification-cache.d.ts.map +1 -0
- package/dist/auth/verification-cache.js +111 -0
- package/dist/chatgpt-analytics.d.ts +22 -0
- package/dist/chatgpt-analytics.d.ts.map +1 -0
- package/dist/chatgpt-analytics.js +172 -0
- package/dist/chatgpt-app.d.ts +15 -0
- package/dist/chatgpt-app.d.ts.map +1 -0
- package/dist/chatgpt-app.js +133 -0
- package/dist/chatgpt-release.d.ts +12 -0
- package/dist/chatgpt-release.d.ts.map +1 -0
- package/dist/chatgpt-release.js +30 -0
- package/dist/cli.js +30 -16
- package/dist/client-profile.d.ts +10 -0
- package/dist/client-profile.d.ts.map +1 -0
- package/dist/client-profile.js +45 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +52 -0
- package/dist/generation-status-html.d.ts +2 -0
- package/dist/generation-status-html.d.ts.map +1 -0
- package/dist/generation-status-html.js +26 -0
- package/dist/generation-status.d.ts +6 -0
- package/dist/generation-status.d.ts.map +1 -0
- package/dist/generation-status.js +37 -0
- package/dist/http-client.d.ts +45 -1
- package/dist/http-client.d.ts.map +1 -1
- package/dist/http-client.js +171 -34
- package/dist/http-server.d.ts +53 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +753 -0
- package/dist/http-session.d.ts +18 -0
- package/dist/http-session.d.ts.map +1 -0
- package/dist/http-session.js +189 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/oauth-response.d.ts +3 -0
- package/dist/oauth-response.d.ts.map +1 -0
- package/dist/oauth-response.js +30 -0
- package/dist/openapi/index.d.ts +2 -2
- package/dist/openapi/index.d.ts.map +1 -1
- package/dist/openapi/index.js +1 -1
- package/dist/openapi/parser.d.ts +2 -0
- package/dist/openapi/parser.d.ts.map +1 -1
- package/dist/openapi/parser.js +41 -1
- package/dist/openapi/types.d.ts +3 -0
- package/dist/openapi/types.d.ts.map +1 -1
- package/dist/organization-selection.d.ts +61 -0
- package/dist/organization-selection.d.ts.map +1 -0
- package/dist/organization-selection.js +142 -0
- package/dist/rate-limits.d.ts +13 -0
- package/dist/rate-limits.d.ts.map +1 -0
- package/dist/rate-limits.js +67 -0
- package/dist/revision.d.ts +6 -0
- package/dist/revision.d.ts.map +1 -0
- package/dist/revision.js +17 -0
- package/dist/runtime-diagnostics.d.ts +32 -0
- package/dist/runtime-diagnostics.d.ts.map +1 -0
- package/dist/runtime-diagnostics.js +58 -0
- package/dist/server.d.ts +23 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +893 -32
- package/dist/session-registry.d.ts +54 -0
- package/dist/session-registry.d.ts.map +1 -0
- package/dist/session-registry.js +202 -0
- package/dist/session-telemetry.d.ts +13 -0
- package/dist/session-telemetry.d.ts.map +1 -0
- package/dist/session-telemetry.js +87 -0
- package/dist/spend-confirmation.d.ts +21 -0
- package/dist/spend-confirmation.d.ts.map +1 -0
- package/dist/spend-confirmation.js +105 -0
- package/dist/tool-analytics-context.d.ts +11 -0
- package/dist/tool-analytics-context.d.ts.map +1 -0
- package/dist/tool-analytics-context.js +8 -0
- package/dist/tool-names.d.ts +35 -0
- package/dist/tool-names.d.ts.map +1 -0
- package/dist/tool-names.js +133 -0
- package/dist/tools/account-context.d.ts +5 -0
- package/dist/tools/account-context.d.ts.map +1 -0
- package/dist/tools/account-context.js +131 -0
- package/dist/tools/account-management.d.ts +4 -0
- package/dist/tools/account-management.d.ts.map +1 -0
- package/dist/tools/account-management.js +111 -0
- package/dist/tools/app-tools.d.ts +31 -0
- package/dist/tools/app-tools.d.ts.map +1 -0
- package/dist/tools/app-tools.js +350 -0
- package/dist/tools/asset-analysis.d.ts +4 -0
- package/dist/tools/asset-analysis.d.ts.map +1 -0
- package/dist/tools/asset-analysis.js +172 -0
- package/dist/tools/asset-creators.d.ts +4 -0
- package/dist/tools/asset-creators.d.ts.map +1 -0
- package/dist/tools/asset-creators.js +38 -0
- package/dist/tools/audio-trim.d.ts +6 -0
- package/dist/tools/audio-trim.d.ts.map +1 -0
- package/dist/tools/audio-trim.js +79 -0
- package/dist/tools/batch-results.d.ts +16 -0
- package/dist/tools/batch-results.d.ts.map +1 -0
- package/dist/tools/batch-results.js +103 -0
- package/dist/tools/batches.d.ts +28 -0
- package/dist/tools/batches.d.ts.map +1 -0
- package/dist/tools/batches.js +293 -0
- package/dist/tools/dialogue.d.ts +6 -0
- package/dist/tools/dialogue.d.ts.map +1 -0
- package/dist/tools/dialogue.js +309 -0
- package/dist/tools/estimate-duration.d.ts +35 -0
- package/dist/tools/estimate-duration.d.ts.map +1 -0
- package/dist/tools/estimate-duration.js +66 -0
- package/dist/tools/feedback.d.ts +14 -0
- package/dist/tools/feedback.d.ts.map +1 -0
- package/dist/tools/feedback.js +72 -0
- package/dist/tools/generated-adapters.d.ts +25 -0
- package/dist/tools/generated-adapters.d.ts.map +1 -0
- package/dist/tools/generated-adapters.js +143 -0
- package/dist/tools/generation-context.d.ts +14 -0
- package/dist/tools/generation-context.d.ts.map +1 -0
- package/dist/tools/generation-context.js +39 -0
- package/dist/tools/generation-continuation.d.ts +4 -0
- package/dist/tools/generation-continuation.d.ts.map +1 -0
- package/dist/tools/generation-continuation.js +124 -0
- package/dist/tools/generation-costs.d.ts +6 -0
- package/dist/tools/generation-costs.d.ts.map +1 -0
- package/dist/tools/generation-costs.js +67 -0
- package/dist/tools/generation-output.d.ts +3 -0
- package/dist/tools/generation-output.d.ts.map +1 -0
- package/dist/tools/generation-output.js +39 -0
- package/dist/tools/generator.d.ts +35 -2
- package/dist/tools/generator.d.ts.map +1 -1
- package/dist/tools/generator.js +127 -9
- package/dist/tools/job-status.d.ts +35 -0
- package/dist/tools/job-status.d.ts.map +1 -0
- package/dist/tools/job-status.js +92 -0
- package/dist/tools/job-wait.d.ts +41 -0
- package/dist/tools/job-wait.d.ts.map +1 -0
- package/dist/tools/job-wait.js +84 -0
- package/dist/tools/jobs.d.ts +3 -0
- package/dist/tools/jobs.d.ts.map +1 -0
- package/dist/tools/jobs.js +210 -0
- package/dist/tools/media-upload.d.ts +27 -0
- package/dist/tools/media-upload.d.ts.map +1 -0
- package/dist/tools/media-upload.js +247 -0
- package/dist/tools/model-options.d.ts +36 -0
- package/dist/tools/model-options.d.ts.map +1 -0
- package/dist/tools/model-options.js +48 -0
- package/dist/tools/organization-choice.d.ts +9 -0
- package/dist/tools/organization-choice.d.ts.map +1 -0
- package/dist/tools/organization-choice.js +136 -0
- package/dist/tools/organizations.d.ts +56 -0
- package/dist/tools/organizations.d.ts.map +1 -0
- package/dist/tools/organizations.js +235 -0
- package/dist/tools/output-schemas.d.ts +54 -0
- package/dist/tools/output-schemas.d.ts.map +1 -0
- package/dist/tools/output-schemas.js +58 -0
- package/dist/tools/overrides.d.ts +3 -0
- package/dist/tools/overrides.d.ts.map +1 -1
- package/dist/tools/overrides.js +113 -15
- package/dist/tools/playback-metadata.d.ts +4 -0
- package/dist/tools/playback-metadata.d.ts.map +1 -0
- package/dist/tools/playback-metadata.js +51 -0
- package/dist/tools/project-destination.d.ts +13 -0
- package/dist/tools/project-destination.d.ts.map +1 -0
- package/dist/tools/project-destination.js +45 -0
- package/dist/tools/project-history.d.ts +6 -0
- package/dist/tools/project-history.d.ts.map +1 -0
- package/dist/tools/project-history.js +45 -0
- package/dist/tools/project-intent.d.ts +8 -0
- package/dist/tools/project-intent.d.ts.map +1 -0
- package/dist/tools/project-intent.js +87 -0
- package/dist/tools/running-submissions.d.ts +7 -0
- package/dist/tools/running-submissions.d.ts.map +1 -0
- package/dist/tools/running-submissions.js +91 -0
- package/dist/tools/speech.d.ts +6 -0
- package/dist/tools/speech.d.ts.map +1 -0
- package/dist/tools/speech.js +116 -0
- package/dist/tools/studio-draft.d.ts +7 -0
- package/dist/tools/studio-draft.d.ts.map +1 -0
- package/dist/tools/studio-draft.js +195 -0
- package/dist/tools/studio-view-contract.d.ts +184 -0
- package/dist/tools/studio-view-contract.d.ts.map +1 -0
- package/dist/tools/studio-view-contract.js +67 -0
- package/dist/tools/studio-view.d.ts +6 -0
- package/dist/tools/studio-view.d.ts.map +1 -0
- package/dist/tools/studio-view.js +126 -0
- package/dist/tools/timeline-lipsync.d.ts +316 -0
- package/dist/tools/timeline-lipsync.d.ts.map +1 -0
- package/dist/tools/timeline-lipsync.js +304 -0
- package/dist/tools/translation-preview.d.ts +8 -0
- package/dist/tools/translation-preview.d.ts.map +1 -0
- package/dist/tools/translation-preview.js +146 -0
- package/dist/tools/translation.d.ts +6 -0
- package/dist/tools/translation.d.ts.map +1 -0
- package/dist/tools/translation.js +131 -0
- package/dist/tools/upload-widget.d.ts +13 -0
- package/dist/tools/upload-widget.d.ts.map +1 -0
- package/dist/tools/upload-widget.js +788 -0
- package/dist/toolsets.d.ts +18 -0
- package/dist/toolsets.d.ts.map +1 -0
- package/dist/toolsets.js +157 -0
- package/dist/update-check.d.ts +38 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +150 -0
- package/dist/upload-runtime.d.ts +45 -0
- package/dist/upload-runtime.d.ts.map +1 -0
- package/dist/upload-runtime.js +156 -0
- package/dist/upload-source.d.ts +4 -0
- package/dist/upload-source.d.ts.map +1 -0
- package/dist/upload-source.js +46 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/package.json +77 -31
- package/dist/openapi/parser.test.d.ts +0 -2
- package/dist/openapi/parser.test.d.ts.map +0 -1
- package/dist/openapi/parser.test.js +0 -188
- package/dist/tools/generator.test.d.ts +0 -2
- package/dist/tools/generator.test.d.ts.map +0 -1
- package/dist/tools/generator.test.js +0 -178
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { HttpClient } from '../http-client.js';
|
|
3
|
+
import type { UploadRuntime } from '../upload-runtime.js';
|
|
4
|
+
export declare const fileInput: z.ZodObject<{
|
|
5
|
+
download_url: z.ZodString;
|
|
6
|
+
file_id: z.ZodString;
|
|
7
|
+
mime_type: z.ZodOptional<z.ZodString>;
|
|
8
|
+
file_name: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export type FileInput = z.infer<typeof fileInput>;
|
|
11
|
+
export type MediaKind = 'video' | 'image' | 'audio';
|
|
12
|
+
type ResolvedMedia = {
|
|
13
|
+
type: MediaKind;
|
|
14
|
+
url?: string;
|
|
15
|
+
assetId?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const ASSET_TYPE_BY_KIND: Record<MediaKind, 'VIDEO' | 'IMAGE' | 'AUDIO'>;
|
|
18
|
+
export declare function mediaContentType(kind: MediaKind, ...declared: Array<string | null | undefined>): string;
|
|
19
|
+
export type MediaParam = string | FileInput | undefined;
|
|
20
|
+
export declare function assertValidFileParam(kind: MediaKind, fileParam: MediaParam): asserts fileParam is FileInput | undefined;
|
|
21
|
+
export declare function uploadMediaAsset(httpClient: HttpClient, kind: MediaKind, fileParam: FileInput, runtime: UploadRuntime, signal?: AbortSignal, assetDetails?: {
|
|
22
|
+
projectId?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
}): Promise<string>;
|
|
25
|
+
export declare function resolveMedia(httpClient: HttpClient, kind: MediaKind, urlParam: string | undefined, assetIdParam: string | undefined, fileParam: MediaParam, runtime: UploadRuntime, signal?: AbortSignal): Promise<ResolvedMedia>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=media-upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-upload.d.ts","sourceRoot":"","sources":["../../src/tools/media-upload.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,eAAO,MAAM,SAAS;;;;;iBAKpB,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAClD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AACpD,KAAK,aAAa,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAI7E,CAAC;AAUF,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,GAAG,QAAQ,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,GAC5C,MAAM,CAMR;AA2ND,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;AAExD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,SAAS,IAAI,SAAS,GAAG,SAAS,CAQ5C;AAED,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,aAAa,EACtB,MAAM,CAAC,EAAE,WAAW,EACpB,YAAY,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GACvD,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED,wBAAsB,YAAY,CAChC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,SAAS,EAAE,UAAU,EACrB,OAAO,EAAE,aAAa,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,aAAa,CAAC,CAkBxB"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
2
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { Readable, Transform } from 'node:stream';
|
|
6
|
+
import { finished, pipeline } from 'node:stream/promises';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { createUploadSourceDispatcher, validatedUploadSourceUrl } from '../upload-source.js';
|
|
9
|
+
// A file as ChatGPT delivers it for an `openai/fileParams` field.
|
|
10
|
+
export const fileInput = z.object({
|
|
11
|
+
download_url: z.string(),
|
|
12
|
+
file_id: z.string(),
|
|
13
|
+
mime_type: z.string().optional(),
|
|
14
|
+
file_name: z.string().optional(),
|
|
15
|
+
});
|
|
16
|
+
export const ASSET_TYPE_BY_KIND = {
|
|
17
|
+
video: 'VIDEO',
|
|
18
|
+
image: 'IMAGE',
|
|
19
|
+
audio: 'AUDIO',
|
|
20
|
+
};
|
|
21
|
+
const DEFAULT_CONTENT_TYPE = {
|
|
22
|
+
video: 'video/mp4',
|
|
23
|
+
image: 'image/png',
|
|
24
|
+
audio: 'audio/mpeg',
|
|
25
|
+
};
|
|
26
|
+
// ChatGPT file links often answer with a generic type such as application/octet-stream,
|
|
27
|
+
// which Sync storage rejects, so use the first declared type of the expected kind.
|
|
28
|
+
export function mediaContentType(kind, ...declared) {
|
|
29
|
+
for (const value of declared) {
|
|
30
|
+
const type = value?.split(';')[0]?.trim().toLowerCase();
|
|
31
|
+
if (type?.startsWith(`${kind}/`))
|
|
32
|
+
return type;
|
|
33
|
+
}
|
|
34
|
+
return DEFAULT_CONTENT_TYPE[kind];
|
|
35
|
+
}
|
|
36
|
+
// The host of a URL, for error messages — so a failure says *where* it tried to
|
|
37
|
+
// reach, not just "fetch failed".
|
|
38
|
+
function hostOf(url) {
|
|
39
|
+
try {
|
|
40
|
+
return new URL(url).host;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return 'invalid-url';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Node's fetch throws `TypeError: fetch failed` and hides the real reason (DNS,
|
|
47
|
+
// TLS, connection refused, unsupported protocol) on `.cause`. Surface it.
|
|
48
|
+
function reason(err) {
|
|
49
|
+
if (err instanceof Error) {
|
|
50
|
+
const cause = err.cause;
|
|
51
|
+
if (cause instanceof Error && cause.message)
|
|
52
|
+
return `${err.message} — ${cause.message}`;
|
|
53
|
+
return err.message;
|
|
54
|
+
}
|
|
55
|
+
return String(err);
|
|
56
|
+
}
|
|
57
|
+
async function cancelResponseBody(response) {
|
|
58
|
+
try {
|
|
59
|
+
await response.body?.cancel();
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Preserve the actionable validation or upstream error that caused cancellation.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function validatedUploadUrl(file, kind) {
|
|
66
|
+
const src = file.download_url;
|
|
67
|
+
if (!src) {
|
|
68
|
+
throw new Error(`The uploaded ${kind} is missing its download_url.`);
|
|
69
|
+
}
|
|
70
|
+
if (!/^https?:\/\//i.test(src)) {
|
|
71
|
+
throw new Error(`The uploaded ${kind} isn't a fetchable URL (received "${src.slice(0, 80)}"). ` +
|
|
72
|
+
`Pass a public ${kind} URL via ${kind}Url instead.`);
|
|
73
|
+
}
|
|
74
|
+
return validatedUploadSourceUrl(src);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Copy a media file into Sync storage and return its durable assetId.
|
|
78
|
+
*
|
|
79
|
+
* ChatGPT hands an uploaded file to the tool as a short-lived `download_url`
|
|
80
|
+
* (it is only valid while this tool call is being handled). A Sync generation,
|
|
81
|
+
* though, fetches its input media asynchronously on the worker — long after
|
|
82
|
+
* this call returns — by which point the ChatGPT URL is dead. So we pull the
|
|
83
|
+
* bytes now and re-host them through the assets pipeline; the returned assetId
|
|
84
|
+
* resolves to a durable Sync URL at submit time and survives the job queue.
|
|
85
|
+
*
|
|
86
|
+
* Each hop throws a distinct, host-tagged error: the three external steps
|
|
87
|
+
* (download from ChatGPT, PUT to Sync storage, register) fail in different ways
|
|
88
|
+
* and a bare "fetch failed" can't be acted on — by the model or by us.
|
|
89
|
+
*/
|
|
90
|
+
async function rehostUpload(httpClient, file, kind, runtime, signal, assetDetails = {}) {
|
|
91
|
+
// A real upload is an http(s) URL we can fetch. A sandbox/local reference
|
|
92
|
+
// (e.g. "sandbox:/mnt/data/...") can't be re-hosted — tell the caller to pass
|
|
93
|
+
// a public URL instead, and echo the value so the cause is visible.
|
|
94
|
+
signal.throwIfAborted();
|
|
95
|
+
const src = validatedUploadUrl(file, kind);
|
|
96
|
+
const dispatcher = createUploadSourceDispatcher(signal);
|
|
97
|
+
let download;
|
|
98
|
+
let tempDirectory;
|
|
99
|
+
let tempPath;
|
|
100
|
+
let uploadBody;
|
|
101
|
+
let put;
|
|
102
|
+
let downloadPipelineStarted = false;
|
|
103
|
+
let actualSize = 0;
|
|
104
|
+
try {
|
|
105
|
+
// 1. Download the source through the public-destination guard.
|
|
106
|
+
try {
|
|
107
|
+
download = await fetch(src, { signal, dispatcher, redirect: 'error' });
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
if (signal.aborted)
|
|
111
|
+
throw signal.reason;
|
|
112
|
+
throw new Error(`Could not reach the uploaded ${kind} at ${hostOf(src)}: ${reason(err)}.`, {
|
|
113
|
+
cause: err,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (!download.ok) {
|
|
117
|
+
await cancelResponseBody(download);
|
|
118
|
+
throw new Error(`The uploaded ${kind} URL returned HTTP ${download.status}.`);
|
|
119
|
+
}
|
|
120
|
+
const contentType = mediaContentType(kind, download.headers.get('content-type'), file.mime_type);
|
|
121
|
+
const fileName = file.file_name ?? `chatgpt-upload-${kind}`;
|
|
122
|
+
const declaredSizeHeader = download.headers.get('content-length');
|
|
123
|
+
const declaredSize = declaredSizeHeader === null ? undefined : Number(declaredSizeHeader);
|
|
124
|
+
const encodings = (download.headers.get('content-encoding') ?? '')
|
|
125
|
+
.split(',')
|
|
126
|
+
.map((encoding) => encoding.trim().toLowerCase());
|
|
127
|
+
const compressedEncodings = new Set(['gzip', 'x-gzip', 'deflate', 'br', 'zstd']);
|
|
128
|
+
const hasEncodedBody = encodings.some((encoding) => compressedEncodings.has(encoding));
|
|
129
|
+
if (declaredSize !== undefined && (!Number.isSafeInteger(declaredSize) || declaredSize < 0)) {
|
|
130
|
+
await cancelResponseBody(download);
|
|
131
|
+
throw new Error(`The uploaded ${kind} has an invalid Content-Length header.`);
|
|
132
|
+
}
|
|
133
|
+
if (declaredSize !== undefined && declaredSize > runtime.config.maxBytes) {
|
|
134
|
+
await cancelResponseBody(download);
|
|
135
|
+
throw new Error(`The uploaded ${kind} is too large (${declaredSize} bytes; limit ${runtime.config.maxBytes} bytes).`);
|
|
136
|
+
}
|
|
137
|
+
if (!download.body) {
|
|
138
|
+
await cancelResponseBody(download);
|
|
139
|
+
throw new Error(`The uploaded ${kind} response did not include a body.`);
|
|
140
|
+
}
|
|
141
|
+
tempDirectory = await mkdtemp(join(tmpdir(), 'sync-mcp-upload-'));
|
|
142
|
+
tempPath = join(tempDirectory, 'upload');
|
|
143
|
+
const validateSize = new Transform({
|
|
144
|
+
transform(chunk, _encoding, callback) {
|
|
145
|
+
actualSize += chunk.byteLength;
|
|
146
|
+
runtime.recordDownloadedBytes(chunk.byteLength);
|
|
147
|
+
if (actualSize > runtime.config.maxBytes) {
|
|
148
|
+
callback(new Error(`The uploaded ${kind} is too large (limit ${runtime.config.maxBytes} bytes).`));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
callback(null, chunk);
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
downloadPipelineStarted = true;
|
|
155
|
+
await pipeline(Readable.fromWeb(download.body), validateSize, createWriteStream(tempPath, { flags: 'wx' }), { signal });
|
|
156
|
+
if (declaredSize !== undefined && !hasEncodedBody && actualSize !== declaredSize) {
|
|
157
|
+
throw new Error(`The uploaded ${kind} size did not match Content-Length (expected ${declaredSize}, received ${actualSize}).`);
|
|
158
|
+
}
|
|
159
|
+
// 2. Ask Sync for a presigned upload URL after the bounded stream has been measured.
|
|
160
|
+
signal.throwIfAborted();
|
|
161
|
+
const { uploadUrl, url } = (await httpClient.request('post', '/v2/assets/upload', {
|
|
162
|
+
body: { fileName, contentType, size: actualSize },
|
|
163
|
+
signal,
|
|
164
|
+
}));
|
|
165
|
+
// 3. Upload from disk so large and unknown-length inputs never occupy one large buffer.
|
|
166
|
+
signal.throwIfAborted();
|
|
167
|
+
uploadBody = createReadStream(tempPath);
|
|
168
|
+
try {
|
|
169
|
+
put = await fetch(uploadUrl, {
|
|
170
|
+
method: 'PUT',
|
|
171
|
+
headers: { 'Content-Type': contentType, 'Content-Length': String(actualSize) },
|
|
172
|
+
body: uploadBody,
|
|
173
|
+
duplex: 'half',
|
|
174
|
+
// Redirect replay tees the stream and retains a full upload in memory.
|
|
175
|
+
redirect: 'error',
|
|
176
|
+
signal,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
uploadBody.destroy();
|
|
181
|
+
throw new Error(`Could not upload the ${kind} to Sync storage at ${hostOf(uploadUrl)}: ${reason(err)}.`, { cause: err });
|
|
182
|
+
}
|
|
183
|
+
if (!put.ok) {
|
|
184
|
+
throw new Error(`Sync storage rejected the ${kind} upload (HTTP ${put.status}).`);
|
|
185
|
+
}
|
|
186
|
+
await finished(uploadBody, { signal });
|
|
187
|
+
runtime.recordUploadedBytes(actualSize);
|
|
188
|
+
// 4. Register the uploaded object as an asset.
|
|
189
|
+
signal.throwIfAborted();
|
|
190
|
+
const asset = (await httpClient.request('post', '/v2/assets', {
|
|
191
|
+
body: { url, type: ASSET_TYPE_BY_KIND[kind], ...assetDetails },
|
|
192
|
+
signal,
|
|
193
|
+
}));
|
|
194
|
+
return asset.id;
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
if (signal.aborted)
|
|
198
|
+
throw signal.reason;
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
uploadBody?.destroy();
|
|
203
|
+
if (put)
|
|
204
|
+
await cancelResponseBody(put);
|
|
205
|
+
if (download && !downloadPipelineStarted)
|
|
206
|
+
await cancelResponseBody(download);
|
|
207
|
+
await dispatcher.destroy().catch(() => runtime.recordCleanupFailure());
|
|
208
|
+
if (tempDirectory) {
|
|
209
|
+
try {
|
|
210
|
+
await rm(tempDirectory, { recursive: true, force: true });
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// Cleanup must not replace a durable asset ID or the primary upload error.
|
|
214
|
+
// Count failures without exposing local paths or sensitive error details.
|
|
215
|
+
runtime.recordCleanupFailure();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
export function assertValidFileParam(kind, fileParam) {
|
|
221
|
+
if (typeof fileParam === 'string') {
|
|
222
|
+
throw new Error(`Got a string in the ${kind} file slot ("${fileParam.slice(0, 80)}"). ` +
|
|
223
|
+
`Pass public URLs via ${kind}Url instead; file slots must be ChatGPT file objects.`);
|
|
224
|
+
}
|
|
225
|
+
if (fileParam)
|
|
226
|
+
validatedUploadUrl(fileParam, kind);
|
|
227
|
+
}
|
|
228
|
+
export async function uploadMediaAsset(httpClient, kind, fileParam, runtime, signal, assetDetails = {}) {
|
|
229
|
+
return runtime.run((uploadSignal) => rehostUpload(httpClient, fileParam, kind, runtime, uploadSignal, assetDetails), { signal });
|
|
230
|
+
}
|
|
231
|
+
export async function resolveMedia(httpClient, kind, urlParam, assetIdParam, fileParam, runtime, signal) {
|
|
232
|
+
signal?.throwIfAborted();
|
|
233
|
+
if (assetIdParam)
|
|
234
|
+
return { type: kind, assetId: assetIdParam };
|
|
235
|
+
// An explicit URL the model already holds (tts output, public/asset URL) wins.
|
|
236
|
+
if (urlParam)
|
|
237
|
+
return { type: kind, url: urlParam };
|
|
238
|
+
assertValidFileParam(kind, fileParam);
|
|
239
|
+
if (fileParam) {
|
|
240
|
+
return {
|
|
241
|
+
type: kind,
|
|
242
|
+
assetId: await uploadMediaAsset(httpClient, kind, fileParam, runtime, signal),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
// Unreachable — callers validate presence first.
|
|
246
|
+
throw new Error(`No ${kind} provided.`);
|
|
247
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const modelOptionsSchema: z.ZodObject<{
|
|
3
|
+
sync_mode: z.ZodOptional<z.ZodEnum<{
|
|
4
|
+
bounce: "bounce";
|
|
5
|
+
cut_off: "cut_off";
|
|
6
|
+
loop: "loop";
|
|
7
|
+
remap: "remap";
|
|
8
|
+
silence: "silence";
|
|
9
|
+
}>>;
|
|
10
|
+
temperature: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
11
|
+
active_speaker_detection: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
12
|
+
auto_detect: z.ZodOptional<z.ZodBoolean>;
|
|
13
|
+
use_v2: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
+
v3: z.ZodOptional<z.ZodBoolean>;
|
|
15
|
+
face_image: z.ZodOptional<z.ZodString>;
|
|
16
|
+
frame_number: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
coordinates: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
18
|
+
}, z.core.$strict>]>>;
|
|
19
|
+
occlusion_detection_enabled: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
reasoning_enabled: z.ZodOptional<z.ZodBoolean>;
|
|
21
|
+
model_mode: z.ZodOptional<z.ZodEnum<{
|
|
22
|
+
face: "face";
|
|
23
|
+
head: "head";
|
|
24
|
+
lips: "lips";
|
|
25
|
+
}>>;
|
|
26
|
+
}, z.core.$strict>;
|
|
27
|
+
/**
|
|
28
|
+
* The API takes speaker detection as an object; agents may send a boolean. Agents also
|
|
29
|
+
* turn on reasoning for models without it, and speaker detection for a still image, and
|
|
30
|
+
* the API rejects both, so drop them rather than fail the whole request.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseModelOptions(value: unknown, request: {
|
|
33
|
+
model: string;
|
|
34
|
+
image?: boolean;
|
|
35
|
+
}): z.infer<typeof modelOptionsSchema> | undefined;
|
|
36
|
+
//# sourceMappingURL=model-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-options.d.ts","sourceRoot":"","sources":["../../src/tools/model-options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;kBA0BpB,CAAC;AAIZ;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC1C,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,GAAG,SAAS,CAShD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Public model controls accepted by the generation API. Do not expose service-only options.
|
|
3
|
+
export const modelOptionsSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
sync_mode: z.enum(['bounce', 'loop', 'cut_off', 'silence', 'remap']).optional(),
|
|
6
|
+
temperature: z.number().min(0).max(1).nullable().optional(),
|
|
7
|
+
active_speaker_detection: z
|
|
8
|
+
.union([
|
|
9
|
+
z.boolean(),
|
|
10
|
+
z
|
|
11
|
+
.object({
|
|
12
|
+
auto_detect: z.boolean().optional(),
|
|
13
|
+
use_v2: z.boolean().optional(),
|
|
14
|
+
v3: z.boolean().optional(),
|
|
15
|
+
face_image: z.string().optional(),
|
|
16
|
+
frame_number: z.number().int().nonnegative().optional(),
|
|
17
|
+
coordinates: z.tuple([z.number(), z.number()]).optional(),
|
|
18
|
+
})
|
|
19
|
+
.strict(),
|
|
20
|
+
])
|
|
21
|
+
.optional(),
|
|
22
|
+
occlusion_detection_enabled: z.boolean().optional(),
|
|
23
|
+
reasoning_enabled: z
|
|
24
|
+
.boolean()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('Only lipsync-2-pro supports reasoning; it is ignored for other models.'),
|
|
27
|
+
model_mode: z.enum(['lips', 'face', 'head']).optional(),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
const REASONING_MODELS = new Set(['lipsync-2-pro', 'sync-2-pro']);
|
|
31
|
+
/**
|
|
32
|
+
* The API takes speaker detection as an object; agents may send a boolean. Agents also
|
|
33
|
+
* turn on reasoning for models without it, and speaker detection for a still image, and
|
|
34
|
+
* the API rejects both, so drop them rather than fail the whole request.
|
|
35
|
+
*/
|
|
36
|
+
export function parseModelOptions(value, request) {
|
|
37
|
+
const options = modelOptionsSchema.optional().parse(value);
|
|
38
|
+
if (!options)
|
|
39
|
+
return options;
|
|
40
|
+
if (typeof options.active_speaker_detection === 'boolean') {
|
|
41
|
+
options.active_speaker_detection = { auto_detect: options.active_speaker_detection };
|
|
42
|
+
}
|
|
43
|
+
if (!REASONING_MODELS.has(request.model))
|
|
44
|
+
delete options.reasoning_enabled;
|
|
45
|
+
if (request.image)
|
|
46
|
+
delete options.active_speaker_detection;
|
|
47
|
+
return options;
|
|
48
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type HttpClient } from '../http-client.js';
|
|
2
|
+
import type { McpToolDefinition } from './generator.js';
|
|
3
|
+
import { type CredentialKind } from './organizations.js';
|
|
4
|
+
/**
|
|
5
|
+
* Adds an optional organizationId that runs one paid create in another organization,
|
|
6
|
+
* and reports the organization each call ran in.
|
|
7
|
+
*/
|
|
8
|
+
export declare function withOrganizationChoice(tool: McpToolDefinition, http: HttpClient, credential: CredentialKind): McpToolDefinition;
|
|
9
|
+
//# sourceMappingURL=organization-choice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"organization-choice.d.ts","sourceRoot":"","sources":["../../src/tools/organization-choice.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAQrE,OAAO,KAAK,EAAE,iBAAiB,EAAsB,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EACL,KAAK,cAAc,EAOpB,MAAM,oBAAoB,CAAC;AAuE5B;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,cAAc,GACzB,iBAAiB,CA6FnB"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ApiRequestError } from '../http-client.js';
|
|
3
|
+
import { chosenOrganization, runInOrganization, } from '../organization-selection.js';
|
|
4
|
+
import { sessionOf, summary, defaultOrganization, apiKeyBound, readAccountAs, organizationSummarySchema, } from './organizations.js';
|
|
5
|
+
// Longest a result waits to name its default organization; past it, `organization` is omitted.
|
|
6
|
+
const ORGANIZATION_LOOKUP_MS = 2_000;
|
|
7
|
+
/** Paid creates, and the estimates that quote them, that take a one-call organizationId. */
|
|
8
|
+
const PER_CALL_ORGANIZATION_TOOLS = new Set([
|
|
9
|
+
'lipsync_create',
|
|
10
|
+
'lipsync_create-from-assets',
|
|
11
|
+
'lipsync_create-timeline',
|
|
12
|
+
'dubbing_create',
|
|
13
|
+
'translation-previews_create',
|
|
14
|
+
'translation-previews_estimate',
|
|
15
|
+
'dialogue-previews_create',
|
|
16
|
+
'dialogue-videos_create',
|
|
17
|
+
'dialogue-videos_estimate',
|
|
18
|
+
'generations_create',
|
|
19
|
+
'generations_estimate-cost',
|
|
20
|
+
// From the batch tools (CRAFT-7781); applies once they are registered.
|
|
21
|
+
'batches_create',
|
|
22
|
+
]);
|
|
23
|
+
const organizationOutput = organizationSummarySchema
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('The Sync organization this call ran in.');
|
|
26
|
+
const organizationIdInput = z
|
|
27
|
+
.uuid()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('Run only this call in another of the user’s Sync organizations, by id from organizations_list, when the user asks for it (for example “generate this in the Acme org”). The session keeps its organization; reads of the ids this call returns stay in this organization. Omit to use the session’s organization.');
|
|
30
|
+
// Ids a create returns: its job, generation, project or output asset.
|
|
31
|
+
function returnedIds(result) {
|
|
32
|
+
if (!result || typeof result !== 'object' || Array.isArray(result))
|
|
33
|
+
return [];
|
|
34
|
+
return Object.entries(result).flatMap(([key, value]) => (key === 'id' || key.endsWith('Id')) && z.uuid().safeParse(value).success
|
|
35
|
+
? [value]
|
|
36
|
+
: []);
|
|
37
|
+
}
|
|
38
|
+
async function organizationForCall(http, credential, session, organizationId, signal) {
|
|
39
|
+
const active = chosenOrganization();
|
|
40
|
+
if (active?.id === organizationId)
|
|
41
|
+
return { organization: active, scoped: true };
|
|
42
|
+
// Running in the credential's own organization needs no header.
|
|
43
|
+
if (credential === 'api_key' || !active) {
|
|
44
|
+
const own = credential === 'api_key'
|
|
45
|
+
? await defaultOrganization(http, session, signal)
|
|
46
|
+
: await defaultOrganization(http, session, signal).catch(() => undefined);
|
|
47
|
+
if (own?.id === organizationId)
|
|
48
|
+
return { organization: own, scoped: false };
|
|
49
|
+
if (credential === 'api_key')
|
|
50
|
+
throw apiKeyBound(own);
|
|
51
|
+
}
|
|
52
|
+
const account = await readAccountAs(http, organizationId, signal);
|
|
53
|
+
return { organization: summary(account.organization), scoped: true };
|
|
54
|
+
}
|
|
55
|
+
function withOrganization(result, organization) {
|
|
56
|
+
if (!organization || !result || typeof result !== 'object' || Array.isArray(result))
|
|
57
|
+
return result;
|
|
58
|
+
return { ...result, organization };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Adds an optional organizationId that runs one paid create in another organization,
|
|
62
|
+
* and reports the organization each call ran in.
|
|
63
|
+
*/
|
|
64
|
+
export function withOrganizationChoice(tool, http, credential) {
|
|
65
|
+
if (!PER_CALL_ORGANIZATION_TOOLS.has(tool.name))
|
|
66
|
+
return tool;
|
|
67
|
+
const run = async (args, context, handler) => {
|
|
68
|
+
const { organizationId, ...fields } = args;
|
|
69
|
+
const session = sessionOf();
|
|
70
|
+
// Ids a create returns are read in the organization it ran in, even after a switch.
|
|
71
|
+
// API keys have one organization and never send the header, so they remember nothing.
|
|
72
|
+
// `ranIn` is fixed before the call starts, so it never depends on naming the organization.
|
|
73
|
+
const remember = (ids, ranIn) => {
|
|
74
|
+
if (credential === 'user')
|
|
75
|
+
session.rememberIds(ids, ranIn);
|
|
76
|
+
};
|
|
77
|
+
const execute = async (ranIn) => {
|
|
78
|
+
try {
|
|
79
|
+
return await handler(fields);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
// An uncertain admission names its generation; recovery must read the same organization.
|
|
83
|
+
if (error instanceof ApiRequestError && error.generationId)
|
|
84
|
+
remember([error.generationId], ranIn);
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const finish = (result, ranIn, organization) => {
|
|
89
|
+
remember(returnedIds(tool.resultFormat === 'mcp' ? result.structuredContent : result), ranIn);
|
|
90
|
+
return { result, organization };
|
|
91
|
+
};
|
|
92
|
+
if (typeof organizationId !== 'string') {
|
|
93
|
+
const active = chosenOrganization();
|
|
94
|
+
// Read alongside the call, bounded, so naming the organization never delays a result.
|
|
95
|
+
const deadline = AbortSignal.timeout(ORGANIZATION_LOOKUP_MS);
|
|
96
|
+
const lookup = active
|
|
97
|
+
? Promise.resolve(active)
|
|
98
|
+
: defaultOrganization(http, session, context?.signal ? AbortSignal.any([context.signal, deadline]) : deadline).catch(() => undefined);
|
|
99
|
+
const result = await execute(active ?? null);
|
|
100
|
+
return finish(result, active ?? null, await lookup);
|
|
101
|
+
}
|
|
102
|
+
const { organization, scoped } = await organizationForCall(http, credential, session, organizationId, context?.signal);
|
|
103
|
+
const ranIn = scoped ? organization : null;
|
|
104
|
+
const result = await runInOrganization(ranIn ?? undefined, () => execute(ranIn));
|
|
105
|
+
return finish(result, ranIn, organization);
|
|
106
|
+
};
|
|
107
|
+
const definition = {
|
|
108
|
+
...tool,
|
|
109
|
+
inputSchema: { ...tool.inputSchema, organizationId: organizationIdInput },
|
|
110
|
+
...(tool.outputSchema
|
|
111
|
+
? { outputSchema: { ...tool.outputSchema, organization: organizationOutput } }
|
|
112
|
+
: {}),
|
|
113
|
+
};
|
|
114
|
+
if (tool.resultFormat === 'mcp')
|
|
115
|
+
return {
|
|
116
|
+
...definition,
|
|
117
|
+
resultFormat: 'mcp',
|
|
118
|
+
handler: async (args, context) => {
|
|
119
|
+
const { result, organization } = await run(args, context, (fields) => tool.handler(fields, context));
|
|
120
|
+
if (result.isError || !organization)
|
|
121
|
+
return result;
|
|
122
|
+
return {
|
|
123
|
+
...result,
|
|
124
|
+
structuredContent: withOrganization(result.structuredContent, organization),
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
...definition,
|
|
130
|
+
resultFormat: 'json',
|
|
131
|
+
handler: async (args, context) => {
|
|
132
|
+
const { result, organization } = await run(args, context, (fields) => tool.handler(fields, context));
|
|
133
|
+
return withOrganization(result, organization);
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ApiRequestError, type HttpClient } from '../http-client.js';
|
|
3
|
+
import { OrganizationSession, type OrganizationSummary } from '../organization-selection.js';
|
|
4
|
+
import type { McpToolDefinition } from './generator.js';
|
|
5
|
+
/** GET route listing the caller's organizations (sync-api-v2, CRAFT-7789). */
|
|
6
|
+
export declare const ORGANIZATIONS_PATH = "/v2/account/organizations";
|
|
7
|
+
/** API keys act for one organization; OAuth and device-auth sessions can choose per request. */
|
|
8
|
+
export type CredentialKind = 'api_key' | 'user';
|
|
9
|
+
/**
|
|
10
|
+
* Next step for a refused organization: the API's own suggestion (for a connection without
|
|
11
|
+
* a grant, reconnecting and ticking that organization), then what not to do.
|
|
12
|
+
*/
|
|
13
|
+
export declare function organizationNextStep(error: ApiRequestError): string | undefined;
|
|
14
|
+
export declare function switchHint(credential: CredentialKind): string;
|
|
15
|
+
declare const planSchema: z.ZodObject<{
|
|
16
|
+
id: z.ZodString;
|
|
17
|
+
name: z.ZodString;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
declare const generationLimitsSchema: z.ZodObject<{
|
|
20
|
+
concurrency: z.ZodNumber;
|
|
21
|
+
maxOutputDurationSeconds: z.ZodNumber;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
declare const accountOrganizationSchema: z.ZodObject<{
|
|
24
|
+
id: z.ZodUUID;
|
|
25
|
+
name: z.ZodNullable<z.ZodString>;
|
|
26
|
+
role: z.ZodOptional<z.ZodString>;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
export declare const featuresSchema: z.ZodArray<z.ZodString>;
|
|
29
|
+
export type AccountOverview = {
|
|
30
|
+
organization?: z.infer<typeof accountOrganizationSchema>;
|
|
31
|
+
credentialType?: string;
|
|
32
|
+
plan?: z.infer<typeof planSchema>;
|
|
33
|
+
limits?: z.infer<typeof generationLimitsSchema>;
|
|
34
|
+
features?: string[];
|
|
35
|
+
};
|
|
36
|
+
/** GET /v2/account, read field by field so one unexpected field never hides the rest. */
|
|
37
|
+
export declare function parseAccountOverview(value: unknown): AccountOverview;
|
|
38
|
+
export declare function sessionOf(): OrganizationSession;
|
|
39
|
+
export declare function summary(organization: {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string | null;
|
|
42
|
+
}): OrganizationSummary;
|
|
43
|
+
/** The credential's own organization, read once a minute at most. */
|
|
44
|
+
export declare function defaultOrganization(http: HttpClient, session: OrganizationSession, signal: AbortSignal | undefined): Promise<OrganizationSummary>;
|
|
45
|
+
export declare function apiKeyBound(organization: OrganizationSummary | undefined): Error;
|
|
46
|
+
/** Reads the account as `organizationId`; refuses unless the API answered for that organization. */
|
|
47
|
+
export declare function readAccountAs(http: HttpClient, organizationId: string, signal: AbortSignal | undefined): Promise<AccountOverview & {
|
|
48
|
+
organization: z.infer<typeof accountOrganizationSchema>;
|
|
49
|
+
}>;
|
|
50
|
+
export declare const organizationSummarySchema: z.ZodObject<{
|
|
51
|
+
id: z.ZodUUID;
|
|
52
|
+
name: z.ZodNullable<z.ZodString>;
|
|
53
|
+
}, z.core.$strip>;
|
|
54
|
+
export declare function createOrganizationTools(http: HttpClient, credential: CredentialKind): McpToolDefinition[];
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=organizations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"organizations.d.ts","sourceRoot":"","sources":["../../src/tools/organizations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAGL,mBAAmB,EACnB,KAAK,mBAAmB,EACzB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAE9D,gGAAgG;AAChG,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,CAAC;AAqBhD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAO/E;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,cAAc,GAAG,MAAM,CAE7D;AAED,QAAA,MAAM,UAAU;;;iBAAiD,CAAC;AAClE,QAAA,MAAM,sBAAsB;;;iBAG1B,CAAC;AACH,QAAA,MAAM,yBAAyB;;;;iBAI7B,CAAC;AAEH,eAAO,MAAM,cAAc,yBAIxB,CAAC;AAEJ,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;IAChD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAQF,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAapE;AAeD,wBAAgB,SAAS,IAAI,mBAAmB,CAE/C;AAED,wBAAgB,OAAO,CAAC,YAAY,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,mBAAmB,CAE9F;AAOD,qEAAqE;AACrE,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,mBAAmB,CAAC,CAQ9B;AAED,wBAAgB,WAAW,CAAC,YAAY,EAAE,mBAAmB,GAAG,SAAS,GAAG,KAAK,CAIhF;AAED,oGAAoG;AACpG,wBAAsB,aAAa,CACjC,IAAI,EAAE,UAAU,EAChB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,eAAe,GAAG;IAAE,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;CAAE,CAAC,CAcxF;AAaD,eAAO,MAAM,yBAAyB;;;iBAA0D,CAAC;AAsEjG,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,cAAc,GACzB,iBAAiB,EAAE,CAkErB"}
|