mulmocast 1.2.63 → 1.2.64

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.
@@ -1,6 +1,5 @@
1
1
  import type { GraphData } from "graphai";
2
2
  import { MulmoStudioContext, MulmoBeat, PublicAPIArgs } from "../types/index.js";
3
- export declare const getBeatAudioPath: (text: string, context: MulmoStudioContext, beat: MulmoBeat, lang?: string) => string | undefined;
4
3
  export declare const getBeatAudioPathOrUrl: (text: string, context: MulmoStudioContext, beat: MulmoBeat, lang?: string) => string | undefined;
5
4
  export declare const localizedPath: (context: MulmoStudioContext, beat: MulmoBeat, index: number, lang: string) => string | undefined;
6
5
  export declare const listLocalizedAudioPaths: (context: MulmoStudioContext) => (string | undefined)[];
@@ -27,10 +27,6 @@ const getAudioPathOrUrl = (context, beat, maybeAudioFile) => {
27
27
  }
28
28
  return maybeAudioFile;
29
29
  };
30
- // for back forward compatible
31
- export const getBeatAudioPath = (text, context, beat, lang) => {
32
- return getBeatAudioPathOrUrl(text, context, beat, lang);
33
- };
34
30
  export const getBeatAudioPathOrUrl = (text, context, beat, lang) => {
35
31
  const audioDirPath = MulmoStudioContextMethods.getAudioDirPath(context);
36
32
  const { voiceId, provider, speechOptions, model } = MulmoStudioContextMethods.getAudioParam(context, beat, lang);
@@ -0,0 +1,2 @@
1
+ import { type MulmoStudioContext } from "../types/index.js";
2
+ export declare const mulmoViewerBundle: (context: MulmoStudioContext) => Promise<void>;
@@ -0,0 +1,66 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { GraphAILogger } from "graphai";
4
+ import { listLocalizedAudioPaths } from "./audio.js";
5
+ import { imagePreprocessAgent } from "./image_agents.js";
6
+ import { mkdir } from "../utils/file.js";
7
+ import { ZipBuilder } from "../utils/zip.js";
8
+ const beatImage = (context) => {
9
+ return async (beat, index) => {
10
+ try {
11
+ const res = await imagePreprocessAgent({ context, beat, index, imageRefs: {} });
12
+ if ("htmlPrompt" in res) {
13
+ return { htmlImageFile: res.htmlImageFile, imagePath: res.imagePath };
14
+ }
15
+ const { imagePath, movieFile, lipSyncFile } = res;
16
+ return { imagePath, movieFile, lipSyncFile };
17
+ }
18
+ catch (e) {
19
+ GraphAILogger.log(e);
20
+ return {};
21
+ }
22
+ };
23
+ };
24
+ const viewJsonFileName = "mulmo_view.json";
25
+ const zipFileName = "mulmo.zip";
26
+ export const mulmoViewerBundle = async (context) => {
27
+ const isZip = true;
28
+ const audios = listLocalizedAudioPaths(context);
29
+ const images = await Promise.all(context.studio.script.beats.map(beatImage(context)));
30
+ const dir = path.resolve(context.fileDirs.fileName);
31
+ mkdir(dir);
32
+ const zipper = new ZipBuilder(path.resolve(dir, zipFileName));
33
+ const resultJson = [];
34
+ audios.forEach((audio) => {
35
+ if (audio) {
36
+ const fileName = path.basename(audio ?? "");
37
+ resultJson.push({ audio: fileName });
38
+ if (fs.existsSync(audio)) {
39
+ fs.copyFileSync(audio, path.resolve(dir, fileName));
40
+ zipper.addFile(audio, fileName);
41
+ }
42
+ }
43
+ else {
44
+ resultJson.push({});
45
+ }
46
+ });
47
+ images.forEach((image, index) => {
48
+ const data = resultJson[index];
49
+ const keys = ["htmlImageFile", "imagePath", "movieFile", "lipSyncFile"];
50
+ keys.forEach((key) => {
51
+ const value = image[key];
52
+ if (value) {
53
+ data[key] = path.basename(value);
54
+ if (fs.existsSync(value)) {
55
+ fs.copyFileSync(value, path.resolve(dir, path.basename(value)));
56
+ zipper.addFile(value);
57
+ }
58
+ }
59
+ });
60
+ });
61
+ fs.writeFileSync(path.resolve(dir, viewJsonFileName), JSON.stringify(resultJson, null, 2));
62
+ zipper.addFile(path.resolve(dir, viewJsonFileName));
63
+ if (isZip) {
64
+ await zipper.finalize();
65
+ }
66
+ };
@@ -1,51 +1,8 @@
1
- import { MulmoStudioContext, MulmoBeat, MulmoCanvasDimension, MulmoImageParams, MulmoMovieParams } from "../types/index.js";
2
- export declare const imagePreprocessAgent: (namedInputs: {
3
- context: MulmoStudioContext;
4
- beat: MulmoBeat;
5
- index: number;
6
- imageRefs: Record<string, string>;
7
- }) => Promise<{
8
- imagePath: string;
9
- htmlPrompt: string | undefined;
10
- htmlImageFile: string;
11
- htmlPath: string;
12
- htmlImageSystemPrompt: string;
13
- } | {
14
- imagePath: string | undefined;
15
- movieFile: string | undefined;
16
- referenceImageForMovie: string | undefined;
17
- imageParams: MulmoImageParams;
18
- soundEffectFile?: string;
19
- soundEffectPrompt?: string;
20
- soundEffectModel?: string;
21
- soundEffectAgentInfo?: {
22
- agentName: string;
23
- defaultModel: string;
24
- };
25
- lipSyncFile?: string;
26
- lipSyncModel?: string;
27
- lipSyncAgentName?: string;
28
- lipSyncTrimAudio?: boolean;
29
- bgmFile?: string | null;
30
- startAt?: number;
31
- duration?: number;
32
- audioFile?: string;
1
+ import { MulmoStudioContext, MulmoBeat, MulmoCanvasDimension, MulmoImageParams, MulmoMovieParams, Text2ImageAgentInfo } from "../types/index.js";
2
+ type ImagePreprocessAgentReturnValue = {
3
+ imageParams?: MulmoImageParams;
4
+ movieFile?: string;
33
5
  beatDuration?: number;
34
- movieAgentInfo?: {
35
- agent: string;
36
- movieParams: MulmoMovieParams;
37
- };
38
- markdown?: string;
39
- html?: string;
40
- htmlPrompt?: undefined;
41
- htmlImageFile?: undefined;
42
- htmlPath?: undefined;
43
- htmlImageSystemPrompt?: undefined;
44
- } | {
45
- imagePath: string;
46
- imageFromMovie: boolean;
47
- imageParams: MulmoImageParams;
48
- movieFile: string | undefined;
49
6
  soundEffectFile?: string;
50
7
  soundEffectPrompt?: string;
51
8
  soundEffectModel?: string;
@@ -57,56 +14,46 @@ export declare const imagePreprocessAgent: (namedInputs: {
57
14
  lipSyncModel?: string;
58
15
  lipSyncAgentName?: string;
59
16
  lipSyncTrimAudio?: boolean;
60
- bgmFile?: string | null;
61
17
  startAt?: number;
62
18
  duration?: number;
19
+ bgmFile?: string | null;
63
20
  audioFile?: string;
64
- beatDuration?: number;
65
21
  movieAgentInfo?: {
66
22
  agent: string;
67
23
  movieParams: MulmoMovieParams;
68
24
  };
69
- markdown?: string;
70
- html?: string;
71
- htmlPrompt?: undefined;
72
- htmlImageFile?: undefined;
73
- htmlPath?: undefined;
74
- htmlImageSystemPrompt?: undefined;
75
- } | {
76
- imagePath: string;
77
- referenceImageForMovie: string;
78
- imageAgentInfo: import("../types/type.js").Text2ImageAgentInfo;
25
+ };
26
+ type ImagePreprocessAgentResponseBase = ImagePreprocessAgentReturnValue & {
27
+ imagePath?: string;
28
+ };
29
+ type ImageGenearalPreprocessAgentResponse = ImagePreprocessAgentResponseBase & {
30
+ imageAgentInfo: Text2ImageAgentInfo;
79
31
  prompt: string;
80
32
  referenceImages: string[];
81
- imageParams: MulmoImageParams;
82
- movieFile: string | undefined;
83
- soundEffectFile?: string;
84
- soundEffectPrompt?: string;
85
- soundEffectModel?: string;
86
- soundEffectAgentInfo?: {
87
- agentName: string;
88
- defaultModel: string;
89
- };
90
- lipSyncFile?: string;
91
- lipSyncModel?: string;
92
- lipSyncAgentName?: string;
93
- lipSyncTrimAudio?: boolean;
94
- bgmFile?: string | null;
95
- startAt?: number;
96
- duration?: number;
97
- audioFile?: string;
98
- beatDuration?: number;
99
- movieAgentInfo?: {
100
- agent: string;
101
- movieParams: MulmoMovieParams;
102
- };
103
- markdown?: string;
104
- html?: string;
105
- htmlPrompt?: undefined;
106
- htmlImageFile?: undefined;
107
- htmlPath?: undefined;
108
- htmlImageSystemPrompt?: undefined;
109
- }>;
33
+ referenceImageForMovie: string;
34
+ };
35
+ type ImageHtmlPreprocessAgentResponse = {
36
+ imagePath: string;
37
+ htmlPrompt: string;
38
+ htmlPath: string;
39
+ htmlImageSystemPrompt: string;
40
+ htmlImageFile: string;
41
+ };
42
+ type ImageOnlyMoviePreprocessAgentResponse = ImagePreprocessAgentResponseBase & {
43
+ imageFromMovie: boolean;
44
+ };
45
+ type ImagePluginPreprocessAgentResponse = ImagePreprocessAgentResponseBase & {
46
+ referenceImageForMovie: string;
47
+ markdown: string;
48
+ html: string;
49
+ };
50
+ type ImagePreprocessAgentResponse = ImagePreprocessAgentResponseBase | ImageHtmlPreprocessAgentResponse | ImagePluginPreprocessAgentResponse | ImageOnlyMoviePreprocessAgentResponse | ImageGenearalPreprocessAgentResponse;
51
+ export declare const imagePreprocessAgent: (namedInputs: {
52
+ context: MulmoStudioContext;
53
+ beat: MulmoBeat;
54
+ index: number;
55
+ imageRefs: Record<string, string>;
56
+ }) => Promise<ImagePreprocessAgentResponse>;
110
57
  export declare const imagePluginAgent: (namedInputs: {
111
58
  context: MulmoStudioContext;
112
59
  beat: MulmoBeat;
@@ -117,3 +64,4 @@ export declare const htmlImageGeneratorAgent: (namedInputs: {
117
64
  canvasSize: MulmoCanvasDimension;
118
65
  htmlText: string;
119
66
  }) => Promise<void>;
67
+ export {};
@@ -17,6 +17,7 @@ export const imagePreprocessAgent = async (namedInputs) => {
17
17
  if (beat.htmlPrompt) {
18
18
  const htmlPrompt = MulmoBeatMethods.getHtmlPrompt(beat);
19
19
  const htmlPath = imagePath.replace(/\.[^/.]+$/, ".html");
20
+ // ImageHtmlPreprocessAgentResponse
20
21
  return { imagePath, htmlPrompt, htmlImageFile, htmlPath, htmlImageSystemPrompt: htmlImageSystemPrompt(context.presentationStyle.canvasSize) };
21
22
  }
22
23
  const imageAgentInfo = MulmoPresentationStyleMethods.getImageAgentInfo(context.presentationStyle, beat);
@@ -62,27 +63,28 @@ export const imagePreprocessAgent = async (namedInputs) => {
62
63
  if (beat.image) {
63
64
  const plugin = MulmoBeatMethods.getPlugin(beat);
64
65
  const pluginPath = plugin.path({ beat, context, imagePath, ...htmlStyle(context, beat) });
65
- if (plugin.markdown) {
66
- returnValue.markdown = plugin.markdown({ beat, context, imagePath, ...htmlStyle(context, beat) });
67
- }
68
- if (plugin.html) {
69
- returnValue.html = await plugin.html({ beat, context, imagePath, ...htmlStyle(context, beat) });
70
- }
66
+ const markdown = plugin.markdown ? plugin.markdown({ beat, context, imagePath, ...htmlStyle(context, beat) }) : undefined;
67
+ const html = plugin.html ? await plugin.html({ beat, context, imagePath, ...htmlStyle(context, beat) }) : undefined;
71
68
  const isTypeMovie = beat.image.type === "movie";
72
69
  // undefined prompt indicates that image generation is not needed
70
+ // ImagePluginPreprocessAgentResponse
73
71
  return {
74
72
  ...returnValue,
75
73
  imagePath: isTypeMovie ? undefined : pluginPath,
76
74
  movieFile: isTypeMovie ? pluginPath : undefined,
77
75
  referenceImageForMovie: pluginPath,
76
+ markdown,
77
+ html,
78
78
  };
79
79
  }
80
80
  if (beat.moviePrompt && !beat.imagePrompt) {
81
+ // ImageOnlyMoviePreprocessAgentResponse
81
82
  return { ...returnValue, imagePath, imageFromMovie: true }; // no image prompt, only movie prompt
82
83
  }
83
84
  // referenceImages for "edit_image", openai agent.
84
85
  const referenceImages = MulmoBeatMethods.getImageReferenceForImageGenerator(beat, imageRefs);
85
86
  const prompt = imagePrompt(beat, imageAgentInfo.imageParams.style);
87
+ // ImageGenearalPreprocessAgentResponse
86
88
  return { ...returnValue, imagePath, referenceImageForMovie: imagePath, imageAgentInfo, prompt, referenceImages };
87
89
  };
88
90
  export const imagePluginAgent = async (namedInputs) => {
@@ -27,17 +27,10 @@ export declare const beat_graph_data: {
27
27
  beat: import("../types/type.js").MulmoBeat;
28
28
  index: number;
29
29
  imageRefs: Record<string, string>;
30
- }) => Promise<{
31
- imagePath: string;
32
- htmlPrompt: string | undefined;
33
- htmlImageFile: string;
34
- htmlPath: string;
35
- htmlImageSystemPrompt: string;
36
- } | {
37
- imagePath: string | undefined;
38
- movieFile: string | undefined;
39
- referenceImageForMovie: string | undefined;
40
- imageParams: MulmoImageParams;
30
+ }) => Promise<({
31
+ imageParams?: MulmoImageParams;
32
+ movieFile?: string;
33
+ beatDuration?: number;
41
34
  soundEffectFile?: string;
42
35
  soundEffectPrompt?: string;
43
36
  soundEffectModel?: string;
@@ -49,26 +42,20 @@ export declare const beat_graph_data: {
49
42
  lipSyncModel?: string;
50
43
  lipSyncAgentName?: string;
51
44
  lipSyncTrimAudio?: boolean;
52
- bgmFile?: string | null;
53
45
  startAt?: number;
54
46
  duration?: number;
47
+ bgmFile?: string | null;
55
48
  audioFile?: string;
56
- beatDuration?: number;
57
49
  movieAgentInfo?: {
58
50
  agent: string;
59
51
  movieParams: import("../types/type.js").MulmoMovieParams;
60
52
  };
61
- markdown?: string;
62
- html?: string;
63
- htmlPrompt?: undefined;
64
- htmlImageFile?: undefined;
65
- htmlPath?: undefined;
66
- htmlImageSystemPrompt?: undefined;
67
- } | {
68
- imagePath: string;
69
- imageFromMovie: boolean;
70
- imageParams: MulmoImageParams;
71
- movieFile: string | undefined;
53
+ } & {
54
+ imagePath?: string;
55
+ }) | ({
56
+ imageParams?: MulmoImageParams;
57
+ movieFile?: string;
58
+ beatDuration?: number;
72
59
  soundEffectFile?: string;
73
60
  soundEffectPrompt?: string;
74
61
  soundEffectModel?: string;
@@ -80,29 +67,31 @@ export declare const beat_graph_data: {
80
67
  lipSyncModel?: string;
81
68
  lipSyncAgentName?: string;
82
69
  lipSyncTrimAudio?: boolean;
83
- bgmFile?: string | null;
84
70
  startAt?: number;
85
71
  duration?: number;
72
+ bgmFile?: string | null;
86
73
  audioFile?: string;
87
- beatDuration?: number;
88
74
  movieAgentInfo?: {
89
75
  agent: string;
90
76
  movieParams: import("../types/type.js").MulmoMovieParams;
91
77
  };
92
- markdown?: string;
93
- html?: string;
94
- htmlPrompt?: undefined;
95
- htmlImageFile?: undefined;
96
- htmlPath?: undefined;
97
- htmlImageSystemPrompt?: undefined;
98
- } | {
99
- imagePath: string;
100
- referenceImageForMovie: string;
78
+ } & {
79
+ imagePath?: string;
80
+ } & {
101
81
  imageAgentInfo: import("../types/type.js").Text2ImageAgentInfo;
102
82
  prompt: string;
103
83
  referenceImages: string[];
104
- imageParams: MulmoImageParams;
105
- movieFile: string | undefined;
84
+ referenceImageForMovie: string;
85
+ }) | {
86
+ imagePath: string;
87
+ htmlPrompt: string;
88
+ htmlPath: string;
89
+ htmlImageSystemPrompt: string;
90
+ htmlImageFile: string;
91
+ } | ({
92
+ imageParams?: MulmoImageParams;
93
+ movieFile?: string;
94
+ beatDuration?: number;
106
95
  soundEffectFile?: string;
107
96
  soundEffectPrompt?: string;
108
97
  soundEffectModel?: string;
@@ -114,22 +103,48 @@ export declare const beat_graph_data: {
114
103
  lipSyncModel?: string;
115
104
  lipSyncAgentName?: string;
116
105
  lipSyncTrimAudio?: boolean;
117
- bgmFile?: string | null;
118
106
  startAt?: number;
119
107
  duration?: number;
108
+ bgmFile?: string | null;
120
109
  audioFile?: string;
110
+ movieAgentInfo?: {
111
+ agent: string;
112
+ movieParams: import("../types/type.js").MulmoMovieParams;
113
+ };
114
+ } & {
115
+ imagePath?: string;
116
+ } & {
117
+ imageFromMovie: boolean;
118
+ }) | ({
119
+ imageParams?: MulmoImageParams;
120
+ movieFile?: string;
121
121
  beatDuration?: number;
122
+ soundEffectFile?: string;
123
+ soundEffectPrompt?: string;
124
+ soundEffectModel?: string;
125
+ soundEffectAgentInfo?: {
126
+ agentName: string;
127
+ defaultModel: string;
128
+ };
129
+ lipSyncFile?: string;
130
+ lipSyncModel?: string;
131
+ lipSyncAgentName?: string;
132
+ lipSyncTrimAudio?: boolean;
133
+ startAt?: number;
134
+ duration?: number;
135
+ bgmFile?: string | null;
136
+ audioFile?: string;
122
137
  movieAgentInfo?: {
123
138
  agent: string;
124
139
  movieParams: import("../types/type.js").MulmoMovieParams;
125
140
  };
126
- markdown?: string;
127
- html?: string;
128
- htmlPrompt?: undefined;
129
- htmlImageFile?: undefined;
130
- htmlPath?: undefined;
131
- htmlImageSystemPrompt?: undefined;
132
- }>;
141
+ } & {
142
+ imagePath?: string;
143
+ } & {
144
+ referenceImageForMovie: string;
145
+ markdown: string;
146
+ html: string;
147
+ })>;
133
148
  inputs: {
134
149
  context: string;
135
150
  beat: string;
@@ -8,3 +8,4 @@ export * from "./pdf.js";
8
8
  export * from "./translate.js";
9
9
  export * from "./markdown.js";
10
10
  export * from "./html.js";
11
+ export * from "./bundle.js";
@@ -8,3 +8,4 @@ export * from "./pdf.js";
8
8
  export * from "./translate.js";
9
9
  export * from "./markdown.js";
10
10
  export * from "./html.js";
11
+ export * from "./bundle.js";
@@ -1,9 +1,9 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
  import { GraphAILogger } from "graphai";
4
- import OpenAI, { toFile, AuthenticationError, RateLimitError } from "openai";
4
+ import OpenAI, { toFile, AuthenticationError, RateLimitError, APIError } from "openai";
5
5
  import { provider2ImageAgent } from "../utils/provider2agent.js";
6
- import { apiKeyMissingError, agentGenerationError, agentIncorrectAPIKeyError, agentAPIRateLimitError, agentInvalidResponseError, imageAction, imageFileTarget, } from "../utils/error_cause.js";
6
+ import { apiKeyMissingError, agentGenerationError, openAIAgentGenerationError, agentIncorrectAPIKeyError, agentAPIRateLimitError, agentInvalidResponseError, imageAction, imageFileTarget, } from "../utils/error_cause.js";
7
7
  // https://platform.openai.com/docs/guides/image-generation
8
8
  export const imageOpenaiAgent = async ({ namedInputs, params, config, }) => {
9
9
  const { prompt, referenceImages } = namedInputs;
@@ -80,6 +80,18 @@ export const imageOpenaiAgent = async ({ namedInputs, params, config, }) => {
80
80
  cause: agentAPIRateLimitError("imageOpenaiAgent", imageAction, imageFileTarget),
81
81
  });
82
82
  }
83
+ if (error instanceof APIError) {
84
+ if (error.code && error.type) {
85
+ throw new Error("Failed to generate image with OpenAI", {
86
+ cause: openAIAgentGenerationError("imageOpenaiAgent", imageAction, error.code, error.type),
87
+ });
88
+ }
89
+ if (error.type === "invalid_request_error" && error?.error?.message?.includes("Your organization must be verified")) {
90
+ throw new Error("Failed to generate image with OpenAI", {
91
+ cause: openAIAgentGenerationError("imageOpenaiAgent", imageAction, "need_verified_organization", error.type),
92
+ });
93
+ }
94
+ }
83
95
  throw new Error("Failed to generate image with OpenAI", {
84
96
  cause: agentGenerationError("imageOpenaiAgent", imageAction, imageFileTarget),
85
97
  });
package/lib/cli/bin.js CHANGED
@@ -11,6 +11,7 @@ import * as imagesCmd from "./commands/image/index.js";
11
11
  import * as movieCmd from "./commands/movie/index.js";
12
12
  import * as pdfCmd from "./commands/pdf/index.js";
13
13
  import * as markdownCmd from "./commands/markdown/index.js";
14
+ import * as bundleCmd from "./commands/bundle/index.js";
14
15
  import * as htmlCmd from "./commands/html/index.js";
15
16
  import * as toolCmd from "./commands/tool/index.js";
16
17
  import { GraphAILogger } from "graphai";
@@ -36,6 +37,7 @@ export const main = async () => {
36
37
  .command(movieCmd)
37
38
  .command(pdfCmd)
38
39
  .command(markdownCmd)
40
+ .command(bundleCmd)
39
41
  .command(htmlCmd)
40
42
  .command(toolCmd)
41
43
  .demandCommand()
@@ -0,0 +1,14 @@
1
+ import type { Argv } from "yargs";
2
+ export declare const builder: (yargs: Argv) => Argv<{
3
+ o: string | undefined;
4
+ } & {
5
+ b: string | undefined;
6
+ } & {
7
+ l: string | undefined;
8
+ } & {
9
+ f: boolean;
10
+ } & {
11
+ p: string | undefined;
12
+ } & {
13
+ file: string | undefined;
14
+ }>;
@@ -0,0 +1,4 @@
1
+ import { commonOptions } from "../../common.js";
2
+ export const builder = (yargs) =>
3
+ // zip
4
+ commonOptions(yargs);
@@ -0,0 +1,4 @@
1
+ import { CliArgs } from "../../../types/cli_types.js";
2
+ export declare const handler: (argv: CliArgs<{
3
+ image_width?: string;
4
+ }>) => Promise<void>;
@@ -0,0 +1,10 @@
1
+ import { mulmoViewerBundle, audio, images } from "../../../actions/index.js";
2
+ import { initializeContext, runTranslateIfNeeded } from "../../helpers.js";
3
+ export const handler = async (argv) => {
4
+ const context = await initializeContext(argv);
5
+ if (!context) {
6
+ process.exit(1);
7
+ }
8
+ await runTranslateIfNeeded(context, true);
9
+ await audio(context).then(images).then(mulmoViewerBundle);
10
+ };
@@ -0,0 +1,4 @@
1
+ export declare const command = "bundle <file>";
2
+ export declare const desc = "Generate bundle files";
3
+ export { builder } from "./builder.js";
4
+ export { handler } from "./handler.js";
@@ -0,0 +1,4 @@
1
+ export const command = "bundle <file>";
2
+ export const desc = "Generate bundle files";
3
+ export { builder } from "./builder.js";
4
+ export { handler } from "./handler.js";
@@ -159,6 +159,14 @@ export declare const agentGenerationError: (agentName: string, action: string, t
159
159
  target: string;
160
160
  agentName: string;
161
161
  };
162
+ export declare const openAIAgentGenerationError: (agentName: string, action: string, errorCode: string, errorType: string, beatIndex?: number) => {
163
+ beatIndex?: number | undefined;
164
+ type: string;
165
+ action: string;
166
+ agentName: string;
167
+ errorCode: string;
168
+ errorType: string;
169
+ };
162
170
  export declare const agentIncorrectAPIKeyError: (agentName: string, action: string, target: string, beatIndex?: number) => {
163
171
  beatIndex?: number | undefined;
164
172
  type: string;
@@ -173,6 +173,17 @@ export const agentGenerationError = (agentName, action, target, beatIndex) => {
173
173
  ...(beatIndex !== undefined && { beatIndex }),
174
174
  };
175
175
  };
176
+ // OpenAI Agent API/Generation Errors
177
+ export const openAIAgentGenerationError = (agentName, action, errorCode, errorType, beatIndex) => {
178
+ return {
179
+ type: apiErrorType,
180
+ action,
181
+ agentName,
182
+ errorCode,
183
+ errorType,
184
+ ...(beatIndex !== undefined && { beatIndex }),
185
+ };
186
+ };
176
187
  // Agent API/Incorrect Key Errors
177
188
  export const agentIncorrectAPIKeyError = (agentName, action, target, beatIndex) => {
178
189
  return {
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Promise-based ZIP archive builder
3
+ *
4
+ * @example
5
+ * const zipper = new ZipBuilder("output.zip");
6
+ * zipper.addFile("file1.txt", "renamed.txt");
7
+ * zipper.addFile("file2.txt");
8
+ * await zipper.finalize();
9
+ */
10
+ export declare class ZipBuilder {
11
+ private archive;
12
+ private output;
13
+ private outputPath;
14
+ private finalized;
15
+ constructor(outputPath: string);
16
+ /**
17
+ * Add a file to the archive
18
+ * @param filePath - Path to the file to add
19
+ * @param nameInZip - Optional name for the file inside the ZIP (defaults to basename)
20
+ */
21
+ addFile(filePath: string, nameInZip?: string): void;
22
+ /**
23
+ * Add multiple files to the archive
24
+ * @param files - Array of file paths or objects with path and name
25
+ */
26
+ addFiles(files: Array<string | {
27
+ path: string;
28
+ name?: string;
29
+ }>): void;
30
+ /**
31
+ * Add a directory to the archive
32
+ * @param dirPath - Path to the directory to add
33
+ * @param destPath - Optional destination path inside the ZIP
34
+ */
35
+ addDirectory(dirPath: string, destPath?: string): void;
36
+ /**
37
+ * Add content from a string or buffer
38
+ * @param content - String or Buffer content
39
+ * @param nameInZip - Name for the file inside the ZIP
40
+ */
41
+ addContent(content: string | Buffer, nameInZip: string): void;
42
+ /**
43
+ * Finalize the archive and return a promise that resolves when complete
44
+ * @returns Promise that resolves with the output file path
45
+ */
46
+ finalize(): Promise<string>;
47
+ /**
48
+ * Get the output file path
49
+ */
50
+ getOutputPath(): string;
51
+ }
@@ -0,0 +1,119 @@
1
+ import fs from "fs";
2
+ import archiver from "archiver";
3
+ import path from "path";
4
+ /**
5
+ * Promise-based ZIP archive builder
6
+ *
7
+ * @example
8
+ * const zipper = new ZipBuilder("output.zip");
9
+ * zipper.addFile("file1.txt", "renamed.txt");
10
+ * zipper.addFile("file2.txt");
11
+ * await zipper.finalize();
12
+ */
13
+ export class ZipBuilder {
14
+ archive;
15
+ output;
16
+ outputPath;
17
+ finalized = false;
18
+ constructor(outputPath) {
19
+ this.outputPath = outputPath;
20
+ this.output = fs.createWriteStream(outputPath);
21
+ this.archive = archiver("zip", {
22
+ zlib: { level: 9 }, // Maximum compression level
23
+ });
24
+ // Pipe archive data to the file
25
+ this.archive.pipe(this.output);
26
+ // Error handling
27
+ this.archive.on("error", (err) => {
28
+ throw err;
29
+ });
30
+ this.archive.on("warning", (err) => {
31
+ if (err.code !== "ENOENT") {
32
+ throw err;
33
+ }
34
+ // console.warn("ZIP warning:", err);
35
+ });
36
+ }
37
+ /**
38
+ * Add a file to the archive
39
+ * @param filePath - Path to the file to add
40
+ * @param nameInZip - Optional name for the file inside the ZIP (defaults to basename)
41
+ */
42
+ addFile(filePath, nameInZip) {
43
+ if (this.finalized) {
44
+ throw new Error("Cannot add files after finalize() has been called");
45
+ }
46
+ if (!fs.existsSync(filePath)) {
47
+ throw new Error(`File not found: ${filePath}`);
48
+ }
49
+ const name = nameInZip ?? path.basename(filePath);
50
+ this.archive.file(filePath, { name });
51
+ }
52
+ /**
53
+ * Add multiple files to the archive
54
+ * @param files - Array of file paths or objects with path and name
55
+ */
56
+ addFiles(files) {
57
+ files.forEach((file) => {
58
+ if (typeof file === "string") {
59
+ this.addFile(file);
60
+ }
61
+ else {
62
+ this.addFile(file.path, file.name);
63
+ }
64
+ });
65
+ }
66
+ /**
67
+ * Add a directory to the archive
68
+ * @param dirPath - Path to the directory to add
69
+ * @param destPath - Optional destination path inside the ZIP
70
+ */
71
+ addDirectory(dirPath, destPath) {
72
+ if (this.finalized) {
73
+ throw new Error("Cannot add directory after finalize() has been called");
74
+ }
75
+ if (!fs.existsSync(dirPath)) {
76
+ throw new Error(`Directory not found: ${dirPath}`);
77
+ }
78
+ const dest = destPath ?? path.basename(dirPath);
79
+ this.archive.directory(dirPath, dest);
80
+ }
81
+ /**
82
+ * Add content from a string or buffer
83
+ * @param content - String or Buffer content
84
+ * @param nameInZip - Name for the file inside the ZIP
85
+ */
86
+ addContent(content, nameInZip) {
87
+ if (this.finalized) {
88
+ throw new Error("Cannot add content after finalize() has been called");
89
+ }
90
+ this.archive.append(content, { name: nameInZip });
91
+ }
92
+ /**
93
+ * Finalize the archive and return a promise that resolves when complete
94
+ * @returns Promise that resolves with the output file path
95
+ */
96
+ async finalize() {
97
+ if (this.finalized) {
98
+ throw new Error("Archive has already been finalized");
99
+ }
100
+ this.finalized = true;
101
+ return new Promise((resolve, reject) => {
102
+ this.output.on("close", () => {
103
+ // const bytes = this.archive.pointer();
104
+ // console.log(`ZIP archive created: ${this.outputPath} (${bytes} bytes)`);
105
+ resolve(this.outputPath);
106
+ });
107
+ this.output.on("error", reject);
108
+ this.archive.on("error", reject);
109
+ // Finalize the archive
110
+ this.archive.finalize();
111
+ });
112
+ }
113
+ /**
114
+ * Get the output file path
115
+ */
116
+ getOutputPath() {
117
+ return this.outputPath;
118
+ }
119
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mulmocast",
3
- "version": "1.2.63",
3
+ "version": "1.2.64",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "lib/index.node.js",
@@ -71,8 +71,8 @@
71
71
  },
72
72
  "homepage": "https://github.com/receptron/mulmocast-cli#readme",
73
73
  "dependencies": {
74
- "@google-cloud/text-to-speech": "^6.3.0",
75
- "@google/genai": "^1.22.0",
74
+ "@google-cloud/text-to-speech": "^6.3.1",
75
+ "@google/genai": "^1.25.0",
76
76
  "@graphai/anthropic_agent": "^2.0.11",
77
77
  "@graphai/browserless_agent": "^2.0.1",
78
78
  "@graphai/gemini_agent": "^2.0.1",
@@ -82,21 +82,22 @@
82
82
  "@graphai/stream_agent_filter": "^2.0.2",
83
83
  "@graphai/vanilla": "^2.0.12",
84
84
  "@graphai/vanilla_node_agents": "^2.0.4",
85
- "@inquirer/input": "^4.2.4",
86
- "@inquirer/select": "^4.3.4",
87
- "@modelcontextprotocol/sdk": "^1.19.1",
85
+ "@inquirer/input": "^4.2.5",
86
+ "@inquirer/select": "^4.4.0",
87
+ "@modelcontextprotocol/sdk": "^1.20.0",
88
88
  "@mozilla/readability": "^0.6.0",
89
89
  "@tavily/core": "^0.5.11",
90
+ "archiver": "^7.0.1",
90
91
  "clipboardy": "^4.0.0",
91
92
  "dotenv": "^17.2.3",
92
93
  "fluent-ffmpeg": "^2.1.3",
93
94
  "graphai": "^2.0.16",
94
95
  "jsdom": "^27.0.0",
95
- "marked": "^16.3.0",
96
+ "marked": "^16.4.0",
96
97
  "mulmocast-vision": "^1.0.4",
97
98
  "ora": "^9.0.0",
98
- "puppeteer": "^24.23.0",
99
- "replicate": "^1.2.0",
99
+ "puppeteer": "^24.24.1",
100
+ "replicate": "^1.3.0",
100
101
  "yaml": "^2.8.1",
101
102
  "yargs": "^18.0.0",
102
103
  "zod": "^3.25.76",
@@ -104,6 +105,7 @@
104
105
  },
105
106
  "devDependencies": {
106
107
  "@receptron/test_utils": "^2.0.3",
108
+ "@types/archiver": "^6.0.3",
107
109
  "@types/fluent-ffmpeg": "^2.1.26",
108
110
  "@types/jsdom": "^27.0.0",
109
111
  "@types/yargs": "^17.0.33",
@@ -114,7 +116,7 @@
114
116
  "prettier": "^3.6.2",
115
117
  "tsx": "^4.20.6",
116
118
  "typescript": "^5.9.3",
117
- "typescript-eslint": "^8.46.0"
119
+ "typescript-eslint": "^8.46.1"
118
120
  },
119
121
  "engines": {
120
122
  "node": ">=20.0.0"