vidpipe 1.3.18 → 1.3.20
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 +3 -0
- package/dist/cli.js +1694 -343
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +864 -221
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -38,6 +38,116 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
38
38
|
mod
|
|
39
39
|
));
|
|
40
40
|
|
|
41
|
+
// src/L0-pure/pipelineSpec/types.ts
|
|
42
|
+
var init_types = __esm({
|
|
43
|
+
"src/L0-pure/pipelineSpec/types.ts"() {
|
|
44
|
+
"use strict";
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// src/L0-pure/pipelineSpec/presets.ts
|
|
49
|
+
var PRESET_FULL;
|
|
50
|
+
var init_presets = __esm({
|
|
51
|
+
"src/L0-pure/pipelineSpec/presets.ts"() {
|
|
52
|
+
"use strict";
|
|
53
|
+
PRESET_FULL = {
|
|
54
|
+
name: "full",
|
|
55
|
+
description: "Full pipeline with hook-first shorts, per-platform optimization",
|
|
56
|
+
processing: {
|
|
57
|
+
silenceRemoval: true,
|
|
58
|
+
visualEnhancement: true,
|
|
59
|
+
captions: true,
|
|
60
|
+
introOutro: true
|
|
61
|
+
},
|
|
62
|
+
clips: {
|
|
63
|
+
shorts: {
|
|
64
|
+
enabled: true,
|
|
65
|
+
strategy: "hook-first",
|
|
66
|
+
duration: { min: 15, max: 60 },
|
|
67
|
+
minViralScore: 8,
|
|
68
|
+
maxClips: 5
|
|
69
|
+
},
|
|
70
|
+
medium: {
|
|
71
|
+
enabled: true,
|
|
72
|
+
strategy: "chronological",
|
|
73
|
+
duration: { min: 60, max: 180 },
|
|
74
|
+
minViralScore: 10,
|
|
75
|
+
maxClips: 5
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
content: {
|
|
79
|
+
chapters: true,
|
|
80
|
+
summary: true,
|
|
81
|
+
blog: true
|
|
82
|
+
},
|
|
83
|
+
distribution: {
|
|
84
|
+
enabled: true,
|
|
85
|
+
publish: true,
|
|
86
|
+
platforms: {
|
|
87
|
+
targets: ["youtube", "linkedin", "instagram", "x", "tiktok"],
|
|
88
|
+
toneStrategy: "per-platform",
|
|
89
|
+
variants: true
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// src/L0-pure/pipelineSpec/validation.ts
|
|
97
|
+
var init_validation = __esm({
|
|
98
|
+
"src/L0-pure/pipelineSpec/validation.ts"() {
|
|
99
|
+
"use strict";
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// src/L0-pure/pipelineSpec/merger.ts
|
|
104
|
+
function applySkipFlags(spec, flags) {
|
|
105
|
+
return {
|
|
106
|
+
...spec,
|
|
107
|
+
processing: {
|
|
108
|
+
silenceRemoval: spec.processing.silenceRemoval && !flags.SKIP_SILENCE_REMOVAL,
|
|
109
|
+
visualEnhancement: spec.processing.visualEnhancement && !flags.SKIP_VISUAL_ENHANCEMENT,
|
|
110
|
+
captions: spec.processing.captions && !flags.SKIP_CAPTIONS,
|
|
111
|
+
introOutro: spec.processing.introOutro && !flags.SKIP_INTRO_OUTRO
|
|
112
|
+
},
|
|
113
|
+
clips: {
|
|
114
|
+
shorts: {
|
|
115
|
+
...spec.clips.shorts,
|
|
116
|
+
enabled: spec.clips.shorts.enabled && !flags.SKIP_SHORTS
|
|
117
|
+
},
|
|
118
|
+
medium: {
|
|
119
|
+
...spec.clips.medium,
|
|
120
|
+
enabled: spec.clips.medium.enabled && !flags.SKIP_MEDIUM_CLIPS
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
distribution: {
|
|
124
|
+
...spec.distribution,
|
|
125
|
+
enabled: spec.distribution.enabled && !flags.SKIP_SOCIAL,
|
|
126
|
+
publish: spec.distribution.publish && !flags.SKIP_SOCIAL_PUBLISH
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function resolveFromFlags(flags) {
|
|
131
|
+
return applySkipFlags(PRESET_FULL, flags);
|
|
132
|
+
}
|
|
133
|
+
var init_merger = __esm({
|
|
134
|
+
"src/L0-pure/pipelineSpec/merger.ts"() {
|
|
135
|
+
"use strict";
|
|
136
|
+
init_presets();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// src/L0-pure/pipelineSpec/index.ts
|
|
141
|
+
var init_pipelineSpec = __esm({
|
|
142
|
+
"src/L0-pure/pipelineSpec/index.ts"() {
|
|
143
|
+
"use strict";
|
|
144
|
+
init_types();
|
|
145
|
+
init_presets();
|
|
146
|
+
init_validation();
|
|
147
|
+
init_merger();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
41
151
|
// src/L0-pure/types/index.ts
|
|
42
152
|
function getStageInfo(stage) {
|
|
43
153
|
const info = PIPELINE_STAGES.find((s) => s.stage === stage);
|
|
@@ -66,9 +176,10 @@ function normalizePlatformString(raw) {
|
|
|
66
176
|
return lower;
|
|
67
177
|
}
|
|
68
178
|
var Platform, PipelineStage, PIPELINE_STAGES, TOTAL_STAGES, PLATFORM_CHAR_LIMITS, SUPPORTED_VIDEO_EXTENSIONS;
|
|
69
|
-
var
|
|
179
|
+
var init_types2 = __esm({
|
|
70
180
|
"src/L0-pure/types/index.ts"() {
|
|
71
181
|
"use strict";
|
|
182
|
+
init_pipelineSpec();
|
|
72
183
|
Platform = /* @__PURE__ */ ((Platform2) => {
|
|
73
184
|
Platform2["TikTok"] = "tiktok";
|
|
74
185
|
Platform2["YouTube"] = "youtube";
|
|
@@ -1632,7 +1743,7 @@ var STATUS_LABEL_PREFIX, PLATFORM_LABEL_PREFIX, PRIORITY_LABEL_PREFIX, COMMENT_M
|
|
|
1632
1743
|
var init_ideaService = __esm({
|
|
1633
1744
|
"src/L3-services/ideaService/ideaService.ts"() {
|
|
1634
1745
|
"use strict";
|
|
1635
|
-
|
|
1746
|
+
init_types2();
|
|
1636
1747
|
init_githubClient();
|
|
1637
1748
|
init_environment();
|
|
1638
1749
|
init_configLogger();
|
|
@@ -4987,23 +5098,63 @@ function createSessionRpc(connection, sessionId) {
|
|
|
4987
5098
|
readFile: async (params) => connection.sendRequest("session.workspace.readFile", { sessionId, ...params }),
|
|
4988
5099
|
createFile: async (params) => connection.sendRequest("session.workspace.createFile", { sessionId, ...params })
|
|
4989
5100
|
},
|
|
5101
|
+
/** @experimental */
|
|
4990
5102
|
fleet: {
|
|
4991
5103
|
start: async (params) => connection.sendRequest("session.fleet.start", { sessionId, ...params })
|
|
4992
5104
|
},
|
|
5105
|
+
/** @experimental */
|
|
4993
5106
|
agent: {
|
|
4994
5107
|
list: async () => connection.sendRequest("session.agent.list", { sessionId }),
|
|
4995
5108
|
getCurrent: async () => connection.sendRequest("session.agent.getCurrent", { sessionId }),
|
|
4996
5109
|
select: async (params) => connection.sendRequest("session.agent.select", { sessionId, ...params }),
|
|
4997
|
-
deselect: async () => connection.sendRequest("session.agent.deselect", { sessionId })
|
|
5110
|
+
deselect: async () => connection.sendRequest("session.agent.deselect", { sessionId }),
|
|
5111
|
+
reload: async () => connection.sendRequest("session.agent.reload", { sessionId })
|
|
5112
|
+
},
|
|
5113
|
+
/** @experimental */
|
|
5114
|
+
skills: {
|
|
5115
|
+
list: async () => connection.sendRequest("session.skills.list", { sessionId }),
|
|
5116
|
+
enable: async (params) => connection.sendRequest("session.skills.enable", { sessionId, ...params }),
|
|
5117
|
+
disable: async (params) => connection.sendRequest("session.skills.disable", { sessionId, ...params }),
|
|
5118
|
+
reload: async () => connection.sendRequest("session.skills.reload", { sessionId })
|
|
5119
|
+
},
|
|
5120
|
+
/** @experimental */
|
|
5121
|
+
mcp: {
|
|
5122
|
+
list: async () => connection.sendRequest("session.mcp.list", { sessionId }),
|
|
5123
|
+
enable: async (params) => connection.sendRequest("session.mcp.enable", { sessionId, ...params }),
|
|
5124
|
+
disable: async (params) => connection.sendRequest("session.mcp.disable", { sessionId, ...params }),
|
|
5125
|
+
reload: async () => connection.sendRequest("session.mcp.reload", { sessionId })
|
|
5126
|
+
},
|
|
5127
|
+
/** @experimental */
|
|
5128
|
+
plugins: {
|
|
5129
|
+
list: async () => connection.sendRequest("session.plugins.list", { sessionId })
|
|
4998
5130
|
},
|
|
5131
|
+
/** @experimental */
|
|
5132
|
+
extensions: {
|
|
5133
|
+
list: async () => connection.sendRequest("session.extensions.list", { sessionId }),
|
|
5134
|
+
enable: async (params) => connection.sendRequest("session.extensions.enable", { sessionId, ...params }),
|
|
5135
|
+
disable: async (params) => connection.sendRequest("session.extensions.disable", { sessionId, ...params }),
|
|
5136
|
+
reload: async () => connection.sendRequest("session.extensions.reload", { sessionId })
|
|
5137
|
+
},
|
|
5138
|
+
/** @experimental */
|
|
4999
5139
|
compaction: {
|
|
5000
5140
|
compact: async () => connection.sendRequest("session.compaction.compact", { sessionId })
|
|
5001
5141
|
},
|
|
5002
5142
|
tools: {
|
|
5003
5143
|
handlePendingToolCall: async (params) => connection.sendRequest("session.tools.handlePendingToolCall", { sessionId, ...params })
|
|
5004
5144
|
},
|
|
5145
|
+
commands: {
|
|
5146
|
+
handlePendingCommand: async (params) => connection.sendRequest("session.commands.handlePendingCommand", { sessionId, ...params })
|
|
5147
|
+
},
|
|
5148
|
+
ui: {
|
|
5149
|
+
elicitation: async (params) => connection.sendRequest("session.ui.elicitation", { sessionId, ...params })
|
|
5150
|
+
},
|
|
5005
5151
|
permissions: {
|
|
5006
5152
|
handlePendingPermissionRequest: async (params) => connection.sendRequest("session.permissions.handlePendingPermissionRequest", { sessionId, ...params })
|
|
5153
|
+
},
|
|
5154
|
+
log: async (params) => connection.sendRequest("session.log", { sessionId, ...params }),
|
|
5155
|
+
shell: {
|
|
5156
|
+
exec: async (params) => connection.sendRequest("session.shell.exec", { sessionId, ...params }),
|
|
5157
|
+
kill: async (params) => connection.sendRequest("session.shell.kill", { sessionId, ...params })
|
|
5007
5158
|
}
|
|
5008
5159
|
};
|
|
5009
5160
|
}
|
|
@@ -5025,13 +5176,30 @@ var init_sdkProtocolVersion = __esm({
|
|
|
5025
5176
|
}
|
|
5026
5177
|
});
|
|
5027
5178
|
|
|
5179
|
+
// node_modules/@github/copilot-sdk/dist/telemetry.js
|
|
5180
|
+
async function getTraceContext(provider) {
|
|
5181
|
+
if (!provider) return {};
|
|
5182
|
+
try {
|
|
5183
|
+
return await provider() ?? {};
|
|
5184
|
+
} catch {
|
|
5185
|
+
return {};
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
var init_telemetry = __esm({
|
|
5189
|
+
"node_modules/@github/copilot-sdk/dist/telemetry.js"() {
|
|
5190
|
+
"use strict";
|
|
5191
|
+
}
|
|
5192
|
+
});
|
|
5193
|
+
|
|
5028
5194
|
// node_modules/@github/copilot-sdk/dist/session.js
|
|
5029
|
-
var import_node, CopilotSession;
|
|
5195
|
+
var import_node, NO_RESULT_PERMISSION_V2_ERROR, CopilotSession;
|
|
5030
5196
|
var init_session = __esm({
|
|
5031
5197
|
"node_modules/@github/copilot-sdk/dist/session.js"() {
|
|
5032
5198
|
"use strict";
|
|
5033
5199
|
import_node = __toESM(require_node(), 1);
|
|
5034
5200
|
init_rpc();
|
|
5201
|
+
init_telemetry();
|
|
5202
|
+
NO_RESULT_PERMISSION_V2_ERROR = "Permission handlers cannot return 'no-result' when connected to a protocol v2 server.";
|
|
5035
5203
|
CopilotSession = class {
|
|
5036
5204
|
/**
|
|
5037
5205
|
* Creates a new CopilotSession instance.
|
|
@@ -5039,12 +5207,14 @@ var init_session = __esm({
|
|
|
5039
5207
|
* @param sessionId - The unique identifier for this session
|
|
5040
5208
|
* @param connection - The JSON-RPC message connection to the Copilot CLI
|
|
5041
5209
|
* @param workspacePath - Path to the session workspace directory (when infinite sessions enabled)
|
|
5210
|
+
* @param traceContextProvider - Optional callback to get W3C Trace Context for outbound RPCs
|
|
5042
5211
|
* @internal This constructor is internal. Use {@link CopilotClient.createSession} to create sessions.
|
|
5043
5212
|
*/
|
|
5044
|
-
constructor(sessionId, connection, _workspacePath) {
|
|
5213
|
+
constructor(sessionId, connection, _workspacePath, traceContextProvider) {
|
|
5045
5214
|
this.sessionId = sessionId;
|
|
5046
5215
|
this.connection = connection;
|
|
5047
5216
|
this._workspacePath = _workspacePath;
|
|
5217
|
+
this.traceContextProvider = traceContextProvider;
|
|
5048
5218
|
}
|
|
5049
5219
|
eventHandlers = /* @__PURE__ */ new Set();
|
|
5050
5220
|
typedEventHandlers = /* @__PURE__ */ new Map();
|
|
@@ -5052,7 +5222,9 @@ var init_session = __esm({
|
|
|
5052
5222
|
permissionHandler;
|
|
5053
5223
|
userInputHandler;
|
|
5054
5224
|
hooks;
|
|
5225
|
+
transformCallbacks;
|
|
5055
5226
|
_rpc = null;
|
|
5227
|
+
traceContextProvider;
|
|
5056
5228
|
/**
|
|
5057
5229
|
* Typed session-scoped RPC methods.
|
|
5058
5230
|
*/
|
|
@@ -5090,6 +5262,7 @@ var init_session = __esm({
|
|
|
5090
5262
|
*/
|
|
5091
5263
|
async send(options) {
|
|
5092
5264
|
const response = await this.connection.sendRequest("session.send", {
|
|
5265
|
+
...await getTraceContext(this.traceContextProvider),
|
|
5093
5266
|
sessionId: this.sessionId,
|
|
5094
5267
|
prompt: options.prompt,
|
|
5095
5268
|
attachments: options.attachments,
|
|
@@ -5219,9 +5392,19 @@ var init_session = __esm({
|
|
|
5219
5392
|
const { requestId, toolName } = event.data;
|
|
5220
5393
|
const args = event.data.arguments;
|
|
5221
5394
|
const toolCallId = event.data.toolCallId;
|
|
5395
|
+
const traceparent = event.data.traceparent;
|
|
5396
|
+
const tracestate = event.data.tracestate;
|
|
5222
5397
|
const handler = this.toolHandlers.get(toolName);
|
|
5223
5398
|
if (handler) {
|
|
5224
|
-
void this._executeToolAndRespond(
|
|
5399
|
+
void this._executeToolAndRespond(
|
|
5400
|
+
requestId,
|
|
5401
|
+
toolName,
|
|
5402
|
+
toolCallId,
|
|
5403
|
+
args,
|
|
5404
|
+
handler,
|
|
5405
|
+
traceparent,
|
|
5406
|
+
tracestate
|
|
5407
|
+
);
|
|
5225
5408
|
}
|
|
5226
5409
|
} else if (event.type === "permission.requested") {
|
|
5227
5410
|
const { requestId, permissionRequest } = event.data;
|
|
@@ -5234,13 +5417,15 @@ var init_session = __esm({
|
|
|
5234
5417
|
* Executes a tool handler and sends the result back via RPC.
|
|
5235
5418
|
* @internal
|
|
5236
5419
|
*/
|
|
5237
|
-
async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler) {
|
|
5420
|
+
async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler, traceparent, tracestate) {
|
|
5238
5421
|
try {
|
|
5239
5422
|
const rawResult = await handler(args, {
|
|
5240
5423
|
sessionId: this.sessionId,
|
|
5241
5424
|
toolCallId,
|
|
5242
5425
|
toolName,
|
|
5243
|
-
arguments: args
|
|
5426
|
+
arguments: args,
|
|
5427
|
+
traceparent,
|
|
5428
|
+
tracestate
|
|
5244
5429
|
});
|
|
5245
5430
|
let result;
|
|
5246
5431
|
if (rawResult == null) {
|
|
@@ -5271,6 +5456,9 @@ var init_session = __esm({
|
|
|
5271
5456
|
const result = await this.permissionHandler(permissionRequest, {
|
|
5272
5457
|
sessionId: this.sessionId
|
|
5273
5458
|
});
|
|
5459
|
+
if (result.kind === "no-result") {
|
|
5460
|
+
return;
|
|
5461
|
+
}
|
|
5274
5462
|
await this.rpc.permissions.handlePendingPermissionRequest({ requestId, result });
|
|
5275
5463
|
} catch (_error) {
|
|
5276
5464
|
try {
|
|
@@ -5351,6 +5539,40 @@ var init_session = __esm({
|
|
|
5351
5539
|
registerHooks(hooks) {
|
|
5352
5540
|
this.hooks = hooks;
|
|
5353
5541
|
}
|
|
5542
|
+
/**
|
|
5543
|
+
* Registers transform callbacks for system message sections.
|
|
5544
|
+
*
|
|
5545
|
+
* @param callbacks - Map of section ID to transform callback, or undefined to clear
|
|
5546
|
+
* @internal This method is typically called internally when creating a session.
|
|
5547
|
+
*/
|
|
5548
|
+
registerTransformCallbacks(callbacks) {
|
|
5549
|
+
this.transformCallbacks = callbacks;
|
|
5550
|
+
}
|
|
5551
|
+
/**
|
|
5552
|
+
* Handles a systemMessage.transform request from the runtime.
|
|
5553
|
+
* Dispatches each section to its registered transform callback.
|
|
5554
|
+
*
|
|
5555
|
+
* @param sections - Map of section IDs to their current rendered content
|
|
5556
|
+
* @returns A promise that resolves with the transformed sections
|
|
5557
|
+
* @internal This method is for internal use by the SDK.
|
|
5558
|
+
*/
|
|
5559
|
+
async _handleSystemMessageTransform(sections) {
|
|
5560
|
+
const result = {};
|
|
5561
|
+
for (const [sectionId, { content }] of Object.entries(sections)) {
|
|
5562
|
+
const callback = this.transformCallbacks?.get(sectionId);
|
|
5563
|
+
if (callback) {
|
|
5564
|
+
try {
|
|
5565
|
+
const transformed = await callback(content);
|
|
5566
|
+
result[sectionId] = { content: transformed };
|
|
5567
|
+
} catch (_error) {
|
|
5568
|
+
result[sectionId] = { content };
|
|
5569
|
+
}
|
|
5570
|
+
} else {
|
|
5571
|
+
result[sectionId] = { content };
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
return { sections: result };
|
|
5575
|
+
}
|
|
5354
5576
|
/**
|
|
5355
5577
|
* Handles a permission request in the v2 protocol format (synchronous RPC).
|
|
5356
5578
|
* Used as a back-compat adapter when connected to a v2 server.
|
|
@@ -5367,8 +5589,14 @@ var init_session = __esm({
|
|
|
5367
5589
|
const result = await this.permissionHandler(request, {
|
|
5368
5590
|
sessionId: this.sessionId
|
|
5369
5591
|
});
|
|
5592
|
+
if (result.kind === "no-result") {
|
|
5593
|
+
throw new Error(NO_RESULT_PERMISSION_V2_ERROR);
|
|
5594
|
+
}
|
|
5370
5595
|
return result;
|
|
5371
|
-
} catch (
|
|
5596
|
+
} catch (error) {
|
|
5597
|
+
if (error instanceof Error && error.message === NO_RESULT_PERMISSION_V2_ERROR) {
|
|
5598
|
+
throw error;
|
|
5599
|
+
}
|
|
5372
5600
|
return { kind: "denied-no-approval-rule-and-could-not-request-from-user" };
|
|
5373
5601
|
}
|
|
5374
5602
|
}
|
|
@@ -5524,14 +5752,35 @@ var init_session = __esm({
|
|
|
5524
5752
|
* The new model takes effect for the next message. Conversation history is preserved.
|
|
5525
5753
|
*
|
|
5526
5754
|
* @param model - Model ID to switch to
|
|
5755
|
+
* @param options - Optional settings for the new model
|
|
5527
5756
|
*
|
|
5528
5757
|
* @example
|
|
5529
5758
|
* ```typescript
|
|
5530
5759
|
* await session.setModel("gpt-4.1");
|
|
5760
|
+
* await session.setModel("claude-sonnet-4.6", { reasoningEffort: "high" });
|
|
5761
|
+
* ```
|
|
5762
|
+
*/
|
|
5763
|
+
async setModel(model, options) {
|
|
5764
|
+
await this.rpc.model.switchTo({ modelId: model, ...options });
|
|
5765
|
+
}
|
|
5766
|
+
/**
|
|
5767
|
+
* Log a message to the session timeline.
|
|
5768
|
+
* The message appears in the session event stream and is visible to SDK consumers
|
|
5769
|
+
* and (for non-ephemeral messages) persisted to the session event log on disk.
|
|
5770
|
+
*
|
|
5771
|
+
* @param message - Human-readable message text
|
|
5772
|
+
* @param options - Optional log level and ephemeral flag
|
|
5773
|
+
*
|
|
5774
|
+
* @example
|
|
5775
|
+
* ```typescript
|
|
5776
|
+
* await session.log("Processing started");
|
|
5777
|
+
* await session.log("Disk usage high", { level: "warning" });
|
|
5778
|
+
* await session.log("Connection failed", { level: "error" });
|
|
5779
|
+
* await session.log("Debug info", { ephemeral: true });
|
|
5531
5780
|
* ```
|
|
5532
5781
|
*/
|
|
5533
|
-
async
|
|
5534
|
-
await this.rpc.
|
|
5782
|
+
async log(message, options) {
|
|
5783
|
+
await this.rpc.log({ message, ...options });
|
|
5535
5784
|
}
|
|
5536
5785
|
};
|
|
5537
5786
|
}
|
|
@@ -5539,7 +5788,9 @@ var init_session = __esm({
|
|
|
5539
5788
|
|
|
5540
5789
|
// node_modules/@github/copilot-sdk/dist/client.js
|
|
5541
5790
|
import { spawn } from "child_process";
|
|
5791
|
+
import { randomUUID } from "crypto";
|
|
5542
5792
|
import { existsSync as existsSync4 } from "fs";
|
|
5793
|
+
import { createRequire as createRequire2 } from "module";
|
|
5543
5794
|
import { Socket } from "net";
|
|
5544
5795
|
import { dirname as dirname3, join as join5 } from "path";
|
|
5545
5796
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -5553,6 +5804,30 @@ function toJsonSchema(parameters) {
|
|
|
5553
5804
|
}
|
|
5554
5805
|
return parameters;
|
|
5555
5806
|
}
|
|
5807
|
+
function extractTransformCallbacks(systemMessage) {
|
|
5808
|
+
if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
|
|
5809
|
+
return { wirePayload: systemMessage, transformCallbacks: void 0 };
|
|
5810
|
+
}
|
|
5811
|
+
const transformCallbacks = /* @__PURE__ */ new Map();
|
|
5812
|
+
const wireSections = {};
|
|
5813
|
+
for (const [sectionId, override] of Object.entries(systemMessage.sections)) {
|
|
5814
|
+
if (!override) continue;
|
|
5815
|
+
if (typeof override.action === "function") {
|
|
5816
|
+
transformCallbacks.set(sectionId, override.action);
|
|
5817
|
+
wireSections[sectionId] = { action: "transform" };
|
|
5818
|
+
} else {
|
|
5819
|
+
wireSections[sectionId] = { action: override.action, content: override.content };
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
if (transformCallbacks.size === 0) {
|
|
5823
|
+
return { wirePayload: systemMessage, transformCallbacks: void 0 };
|
|
5824
|
+
}
|
|
5825
|
+
const wirePayload = {
|
|
5826
|
+
...systemMessage,
|
|
5827
|
+
sections: wireSections
|
|
5828
|
+
};
|
|
5829
|
+
return { wirePayload, transformCallbacks };
|
|
5830
|
+
}
|
|
5556
5831
|
function getNodeExecPath() {
|
|
5557
5832
|
if (process.versions.bun) {
|
|
5558
5833
|
return "node";
|
|
@@ -5560,9 +5835,22 @@ function getNodeExecPath() {
|
|
|
5560
5835
|
return process.execPath;
|
|
5561
5836
|
}
|
|
5562
5837
|
function getBundledCliPath() {
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5838
|
+
if (typeof import.meta.resolve === "function") {
|
|
5839
|
+
const sdkUrl = import.meta.resolve("@github/copilot/sdk");
|
|
5840
|
+
const sdkPath = fileURLToPath3(sdkUrl);
|
|
5841
|
+
return join5(dirname3(dirname3(sdkPath)), "index.js");
|
|
5842
|
+
}
|
|
5843
|
+
const req = createRequire2(__filename);
|
|
5844
|
+
const searchPaths = req.resolve.paths("@github/copilot") ?? [];
|
|
5845
|
+
for (const base of searchPaths) {
|
|
5846
|
+
const candidate = join5(base, "@github", "copilot", "index.js");
|
|
5847
|
+
if (existsSync4(candidate)) {
|
|
5848
|
+
return candidate;
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
5851
|
+
throw new Error(
|
|
5852
|
+
`Could not find @github/copilot package. Searched ${searchPaths.length} paths. Ensure it is installed, or pass cliPath/cliUrl to CopilotClient.`
|
|
5853
|
+
);
|
|
5566
5854
|
}
|
|
5567
5855
|
var import_node2, MIN_PROTOCOL_VERSION, CopilotClient;
|
|
5568
5856
|
var init_client = __esm({
|
|
@@ -5572,6 +5860,7 @@ var init_client = __esm({
|
|
|
5572
5860
|
init_rpc();
|
|
5573
5861
|
init_sdkProtocolVersion();
|
|
5574
5862
|
init_session();
|
|
5863
|
+
init_telemetry();
|
|
5575
5864
|
MIN_PROTOCOL_VERSION = 2;
|
|
5576
5865
|
CopilotClient = class {
|
|
5577
5866
|
cliProcess = null;
|
|
@@ -5586,6 +5875,8 @@ var init_client = __esm({
|
|
|
5586
5875
|
options;
|
|
5587
5876
|
isExternalServer = false;
|
|
5588
5877
|
forceStopping = false;
|
|
5878
|
+
onListModels;
|
|
5879
|
+
onGetTraceContext;
|
|
5589
5880
|
modelsCache = null;
|
|
5590
5881
|
modelsCacheLock = Promise.resolve();
|
|
5591
5882
|
sessionLifecycleHandlers = /* @__PURE__ */ new Set();
|
|
@@ -5651,8 +5942,10 @@ var init_client = __esm({
|
|
|
5651
5942
|
if (options.isChildProcess) {
|
|
5652
5943
|
this.isExternalServer = true;
|
|
5653
5944
|
}
|
|
5945
|
+
this.onListModels = options.onListModels;
|
|
5946
|
+
this.onGetTraceContext = options.onGetTraceContext;
|
|
5654
5947
|
this.options = {
|
|
5655
|
-
cliPath: options.cliPath || getBundledCliPath(),
|
|
5948
|
+
cliPath: options.cliUrl ? void 0 : options.cliPath || getBundledCliPath(),
|
|
5656
5949
|
cliArgs: options.cliArgs ?? [],
|
|
5657
5950
|
cwd: options.cwd ?? process.cwd(),
|
|
5658
5951
|
port: options.port || 0,
|
|
@@ -5662,11 +5955,12 @@ var init_client = __esm({
|
|
|
5662
5955
|
cliUrl: options.cliUrl,
|
|
5663
5956
|
logLevel: options.logLevel || "debug",
|
|
5664
5957
|
autoStart: options.autoStart ?? true,
|
|
5665
|
-
autoRestart:
|
|
5958
|
+
autoRestart: false,
|
|
5666
5959
|
env: options.env ?? process.env,
|
|
5667
5960
|
githubToken: options.githubToken,
|
|
5668
5961
|
// Default useLoggedInUser to false when githubToken is provided, otherwise true
|
|
5669
|
-
useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true)
|
|
5962
|
+
useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true),
|
|
5963
|
+
telemetry: options.telemetry
|
|
5670
5964
|
};
|
|
5671
5965
|
}
|
|
5672
5966
|
/**
|
|
@@ -5918,36 +6212,13 @@ var init_client = __esm({
|
|
|
5918
6212
|
throw new Error("Client not connected. Call start() first.");
|
|
5919
6213
|
}
|
|
5920
6214
|
}
|
|
5921
|
-
const
|
|
5922
|
-
|
|
5923
|
-
sessionId
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
description: tool.description,
|
|
5929
|
-
parameters: toJsonSchema(tool.parameters),
|
|
5930
|
-
overridesBuiltInTool: tool.overridesBuiltInTool
|
|
5931
|
-
})),
|
|
5932
|
-
systemMessage: config2.systemMessage,
|
|
5933
|
-
availableTools: config2.availableTools,
|
|
5934
|
-
excludedTools: config2.excludedTools,
|
|
5935
|
-
provider: config2.provider,
|
|
5936
|
-
requestPermission: true,
|
|
5937
|
-
requestUserInput: !!config2.onUserInputRequest,
|
|
5938
|
-
hooks: !!(config2.hooks && Object.values(config2.hooks).some(Boolean)),
|
|
5939
|
-
workingDirectory: config2.workingDirectory,
|
|
5940
|
-
streaming: config2.streaming,
|
|
5941
|
-
mcpServers: config2.mcpServers,
|
|
5942
|
-
envValueMode: "direct",
|
|
5943
|
-
customAgents: config2.customAgents,
|
|
5944
|
-
configDir: config2.configDir,
|
|
5945
|
-
skillDirectories: config2.skillDirectories,
|
|
5946
|
-
disabledSkills: config2.disabledSkills,
|
|
5947
|
-
infiniteSessions: config2.infiniteSessions
|
|
5948
|
-
});
|
|
5949
|
-
const { sessionId, workspacePath } = response;
|
|
5950
|
-
const session = new CopilotSession(sessionId, this.connection, workspacePath);
|
|
6215
|
+
const sessionId = config2.sessionId ?? randomUUID();
|
|
6216
|
+
const session = new CopilotSession(
|
|
6217
|
+
sessionId,
|
|
6218
|
+
this.connection,
|
|
6219
|
+
void 0,
|
|
6220
|
+
this.onGetTraceContext
|
|
6221
|
+
);
|
|
5951
6222
|
session.registerTools(config2.tools);
|
|
5952
6223
|
session.registerPermissionHandler(config2.onPermissionRequest);
|
|
5953
6224
|
if (config2.onUserInputRequest) {
|
|
@@ -5956,7 +6227,54 @@ var init_client = __esm({
|
|
|
5956
6227
|
if (config2.hooks) {
|
|
5957
6228
|
session.registerHooks(config2.hooks);
|
|
5958
6229
|
}
|
|
6230
|
+
const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
|
|
6231
|
+
config2.systemMessage
|
|
6232
|
+
);
|
|
6233
|
+
if (transformCallbacks) {
|
|
6234
|
+
session.registerTransformCallbacks(transformCallbacks);
|
|
6235
|
+
}
|
|
6236
|
+
if (config2.onEvent) {
|
|
6237
|
+
session.on(config2.onEvent);
|
|
6238
|
+
}
|
|
5959
6239
|
this.sessions.set(sessionId, session);
|
|
6240
|
+
try {
|
|
6241
|
+
const response = await this.connection.sendRequest("session.create", {
|
|
6242
|
+
...await getTraceContext(this.onGetTraceContext),
|
|
6243
|
+
model: config2.model,
|
|
6244
|
+
sessionId,
|
|
6245
|
+
clientName: config2.clientName,
|
|
6246
|
+
reasoningEffort: config2.reasoningEffort,
|
|
6247
|
+
tools: config2.tools?.map((tool) => ({
|
|
6248
|
+
name: tool.name,
|
|
6249
|
+
description: tool.description,
|
|
6250
|
+
parameters: toJsonSchema(tool.parameters),
|
|
6251
|
+
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
6252
|
+
skipPermission: tool.skipPermission
|
|
6253
|
+
})),
|
|
6254
|
+
systemMessage: wireSystemMessage,
|
|
6255
|
+
availableTools: config2.availableTools,
|
|
6256
|
+
excludedTools: config2.excludedTools,
|
|
6257
|
+
provider: config2.provider,
|
|
6258
|
+
requestPermission: true,
|
|
6259
|
+
requestUserInput: !!config2.onUserInputRequest,
|
|
6260
|
+
hooks: !!(config2.hooks && Object.values(config2.hooks).some(Boolean)),
|
|
6261
|
+
workingDirectory: config2.workingDirectory,
|
|
6262
|
+
streaming: config2.streaming,
|
|
6263
|
+
mcpServers: config2.mcpServers,
|
|
6264
|
+
envValueMode: "direct",
|
|
6265
|
+
customAgents: config2.customAgents,
|
|
6266
|
+
agent: config2.agent,
|
|
6267
|
+
configDir: config2.configDir,
|
|
6268
|
+
skillDirectories: config2.skillDirectories,
|
|
6269
|
+
disabledSkills: config2.disabledSkills,
|
|
6270
|
+
infiniteSessions: config2.infiniteSessions
|
|
6271
|
+
});
|
|
6272
|
+
const { workspacePath } = response;
|
|
6273
|
+
session["_workspacePath"] = workspacePath;
|
|
6274
|
+
} catch (e) {
|
|
6275
|
+
this.sessions.delete(sessionId);
|
|
6276
|
+
throw e;
|
|
6277
|
+
}
|
|
5960
6278
|
return session;
|
|
5961
6279
|
}
|
|
5962
6280
|
/**
|
|
@@ -5996,37 +6314,12 @@ var init_client = __esm({
|
|
|
5996
6314
|
throw new Error("Client not connected. Call start() first.");
|
|
5997
6315
|
}
|
|
5998
6316
|
}
|
|
5999
|
-
const
|
|
6317
|
+
const session = new CopilotSession(
|
|
6000
6318
|
sessionId,
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
availableTools: config2.availableTools,
|
|
6006
|
-
excludedTools: config2.excludedTools,
|
|
6007
|
-
tools: config2.tools?.map((tool) => ({
|
|
6008
|
-
name: tool.name,
|
|
6009
|
-
description: tool.description,
|
|
6010
|
-
parameters: toJsonSchema(tool.parameters),
|
|
6011
|
-
overridesBuiltInTool: tool.overridesBuiltInTool
|
|
6012
|
-
})),
|
|
6013
|
-
provider: config2.provider,
|
|
6014
|
-
requestPermission: true,
|
|
6015
|
-
requestUserInput: !!config2.onUserInputRequest,
|
|
6016
|
-
hooks: !!(config2.hooks && Object.values(config2.hooks).some(Boolean)),
|
|
6017
|
-
workingDirectory: config2.workingDirectory,
|
|
6018
|
-
configDir: config2.configDir,
|
|
6019
|
-
streaming: config2.streaming,
|
|
6020
|
-
mcpServers: config2.mcpServers,
|
|
6021
|
-
envValueMode: "direct",
|
|
6022
|
-
customAgents: config2.customAgents,
|
|
6023
|
-
skillDirectories: config2.skillDirectories,
|
|
6024
|
-
disabledSkills: config2.disabledSkills,
|
|
6025
|
-
infiniteSessions: config2.infiniteSessions,
|
|
6026
|
-
disableResume: config2.disableResume
|
|
6027
|
-
});
|
|
6028
|
-
const { sessionId: resumedSessionId, workspacePath } = response;
|
|
6029
|
-
const session = new CopilotSession(resumedSessionId, this.connection, workspacePath);
|
|
6319
|
+
this.connection,
|
|
6320
|
+
void 0,
|
|
6321
|
+
this.onGetTraceContext
|
|
6322
|
+
);
|
|
6030
6323
|
session.registerTools(config2.tools);
|
|
6031
6324
|
session.registerPermissionHandler(config2.onPermissionRequest);
|
|
6032
6325
|
if (config2.onUserInputRequest) {
|
|
@@ -6035,7 +6328,55 @@ var init_client = __esm({
|
|
|
6035
6328
|
if (config2.hooks) {
|
|
6036
6329
|
session.registerHooks(config2.hooks);
|
|
6037
6330
|
}
|
|
6038
|
-
|
|
6331
|
+
const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
|
|
6332
|
+
config2.systemMessage
|
|
6333
|
+
);
|
|
6334
|
+
if (transformCallbacks) {
|
|
6335
|
+
session.registerTransformCallbacks(transformCallbacks);
|
|
6336
|
+
}
|
|
6337
|
+
if (config2.onEvent) {
|
|
6338
|
+
session.on(config2.onEvent);
|
|
6339
|
+
}
|
|
6340
|
+
this.sessions.set(sessionId, session);
|
|
6341
|
+
try {
|
|
6342
|
+
const response = await this.connection.sendRequest("session.resume", {
|
|
6343
|
+
...await getTraceContext(this.onGetTraceContext),
|
|
6344
|
+
sessionId,
|
|
6345
|
+
clientName: config2.clientName,
|
|
6346
|
+
model: config2.model,
|
|
6347
|
+
reasoningEffort: config2.reasoningEffort,
|
|
6348
|
+
systemMessage: wireSystemMessage,
|
|
6349
|
+
availableTools: config2.availableTools,
|
|
6350
|
+
excludedTools: config2.excludedTools,
|
|
6351
|
+
tools: config2.tools?.map((tool) => ({
|
|
6352
|
+
name: tool.name,
|
|
6353
|
+
description: tool.description,
|
|
6354
|
+
parameters: toJsonSchema(tool.parameters),
|
|
6355
|
+
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
6356
|
+
skipPermission: tool.skipPermission
|
|
6357
|
+
})),
|
|
6358
|
+
provider: config2.provider,
|
|
6359
|
+
requestPermission: true,
|
|
6360
|
+
requestUserInput: !!config2.onUserInputRequest,
|
|
6361
|
+
hooks: !!(config2.hooks && Object.values(config2.hooks).some(Boolean)),
|
|
6362
|
+
workingDirectory: config2.workingDirectory,
|
|
6363
|
+
configDir: config2.configDir,
|
|
6364
|
+
streaming: config2.streaming,
|
|
6365
|
+
mcpServers: config2.mcpServers,
|
|
6366
|
+
envValueMode: "direct",
|
|
6367
|
+
customAgents: config2.customAgents,
|
|
6368
|
+
agent: config2.agent,
|
|
6369
|
+
skillDirectories: config2.skillDirectories,
|
|
6370
|
+
disabledSkills: config2.disabledSkills,
|
|
6371
|
+
infiniteSessions: config2.infiniteSessions,
|
|
6372
|
+
disableResume: config2.disableResume
|
|
6373
|
+
});
|
|
6374
|
+
const { workspacePath } = response;
|
|
6375
|
+
session["_workspacePath"] = workspacePath;
|
|
6376
|
+
} catch (e) {
|
|
6377
|
+
this.sessions.delete(sessionId);
|
|
6378
|
+
throw e;
|
|
6379
|
+
}
|
|
6039
6380
|
return session;
|
|
6040
6381
|
}
|
|
6041
6382
|
/**
|
|
@@ -6096,15 +6437,15 @@ var init_client = __esm({
|
|
|
6096
6437
|
/**
|
|
6097
6438
|
* List available models with their metadata.
|
|
6098
6439
|
*
|
|
6440
|
+
* If an `onListModels` handler was provided in the client options,
|
|
6441
|
+
* it is called instead of querying the CLI server.
|
|
6442
|
+
*
|
|
6099
6443
|
* Results are cached after the first successful call to avoid rate limiting.
|
|
6100
6444
|
* The cache is cleared when the client disconnects.
|
|
6101
6445
|
*
|
|
6102
|
-
* @throws Error if not
|
|
6446
|
+
* @throws Error if not connected (when no custom handler is set)
|
|
6103
6447
|
*/
|
|
6104
6448
|
async listModels() {
|
|
6105
|
-
if (!this.connection) {
|
|
6106
|
-
throw new Error("Client not connected");
|
|
6107
|
-
}
|
|
6108
6449
|
await this.modelsCacheLock;
|
|
6109
6450
|
let resolveLock;
|
|
6110
6451
|
this.modelsCacheLock = new Promise((resolve3) => {
|
|
@@ -6114,10 +6455,18 @@ var init_client = __esm({
|
|
|
6114
6455
|
if (this.modelsCache !== null) {
|
|
6115
6456
|
return [...this.modelsCache];
|
|
6116
6457
|
}
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6458
|
+
let models;
|
|
6459
|
+
if (this.onListModels) {
|
|
6460
|
+
models = await this.onListModels();
|
|
6461
|
+
} else {
|
|
6462
|
+
if (!this.connection) {
|
|
6463
|
+
throw new Error("Client not connected");
|
|
6464
|
+
}
|
|
6465
|
+
const result = await this.connection.sendRequest("models.list", {});
|
|
6466
|
+
const response = result;
|
|
6467
|
+
models = response.models;
|
|
6468
|
+
}
|
|
6469
|
+
this.modelsCache = [...models];
|
|
6121
6470
|
return [...models];
|
|
6122
6471
|
} finally {
|
|
6123
6472
|
resolveLock();
|
|
@@ -6330,6 +6679,27 @@ var init_client = __esm({
|
|
|
6330
6679
|
if (this.options.githubToken) {
|
|
6331
6680
|
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.githubToken;
|
|
6332
6681
|
}
|
|
6682
|
+
if (!this.options.cliPath) {
|
|
6683
|
+
throw new Error(
|
|
6684
|
+
"Path to Copilot CLI is required. Please provide it via the cliPath option, or use cliUrl to rely on a remote CLI."
|
|
6685
|
+
);
|
|
6686
|
+
}
|
|
6687
|
+
if (this.options.telemetry) {
|
|
6688
|
+
const t = this.options.telemetry;
|
|
6689
|
+
envWithoutNodeDebug.COPILOT_OTEL_ENABLED = "true";
|
|
6690
|
+
if (t.otlpEndpoint !== void 0)
|
|
6691
|
+
envWithoutNodeDebug.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
|
|
6692
|
+
if (t.filePath !== void 0)
|
|
6693
|
+
envWithoutNodeDebug.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
|
|
6694
|
+
if (t.exporterType !== void 0)
|
|
6695
|
+
envWithoutNodeDebug.COPILOT_OTEL_EXPORTER_TYPE = t.exporterType;
|
|
6696
|
+
if (t.sourceName !== void 0)
|
|
6697
|
+
envWithoutNodeDebug.COPILOT_OTEL_SOURCE_NAME = t.sourceName;
|
|
6698
|
+
if (t.captureContent !== void 0)
|
|
6699
|
+
envWithoutNodeDebug.OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = String(
|
|
6700
|
+
t.captureContent
|
|
6701
|
+
);
|
|
6702
|
+
}
|
|
6333
6703
|
if (!existsSync4(this.options.cliPath)) {
|
|
6334
6704
|
throw new Error(
|
|
6335
6705
|
`Copilot CLI not found at ${this.options.cliPath}. Ensure @github/copilot is installed.`
|
|
@@ -6429,8 +6799,6 @@ stderr: ${stderrOutput}`
|
|
|
6429
6799
|
} else {
|
|
6430
6800
|
reject(new Error(`CLI server exited with code ${code}`));
|
|
6431
6801
|
}
|
|
6432
|
-
} else if (this.options.autoRestart && this.state === "connected") {
|
|
6433
|
-
void this.reconnect();
|
|
6434
6802
|
}
|
|
6435
6803
|
});
|
|
6436
6804
|
setTimeout(() => {
|
|
@@ -6535,12 +6903,15 @@ stderr: ${stderrOutput}`
|
|
|
6535
6903
|
"hooks.invoke",
|
|
6536
6904
|
async (params) => await this.handleHooksInvoke(params)
|
|
6537
6905
|
);
|
|
6906
|
+
this.connection.onRequest(
|
|
6907
|
+
"systemMessage.transform",
|
|
6908
|
+
async (params) => await this.handleSystemMessageTransform(params)
|
|
6909
|
+
);
|
|
6538
6910
|
this.connection.onClose(() => {
|
|
6539
|
-
|
|
6540
|
-
void this.reconnect();
|
|
6541
|
-
}
|
|
6911
|
+
this.state = "disconnected";
|
|
6542
6912
|
});
|
|
6543
6913
|
this.connection.onError((_error) => {
|
|
6914
|
+
this.state = "disconnected";
|
|
6544
6915
|
});
|
|
6545
6916
|
}
|
|
6546
6917
|
handleSessionEventNotification(notification) {
|
|
@@ -6599,6 +6970,16 @@ stderr: ${stderrOutput}`
|
|
|
6599
6970
|
const output = await session._handleHooksInvoke(params.hookType, params.input);
|
|
6600
6971
|
return { output };
|
|
6601
6972
|
}
|
|
6973
|
+
async handleSystemMessageTransform(params) {
|
|
6974
|
+
if (!params || typeof params.sessionId !== "string" || !params.sections || typeof params.sections !== "object") {
|
|
6975
|
+
throw new Error("Invalid systemMessage.transform payload");
|
|
6976
|
+
}
|
|
6977
|
+
const session = this.sessions.get(params.sessionId);
|
|
6978
|
+
if (!session) {
|
|
6979
|
+
throw new Error(`Session not found: ${params.sessionId}`);
|
|
6980
|
+
}
|
|
6981
|
+
return await session._handleSystemMessageTransform(params.sections);
|
|
6982
|
+
}
|
|
6602
6983
|
// ========================================================================
|
|
6603
6984
|
// Protocol v2 backward-compatibility adapters
|
|
6604
6985
|
// ========================================================================
|
|
@@ -6627,11 +7008,15 @@ stderr: ${stderrOutput}`
|
|
|
6627
7008
|
};
|
|
6628
7009
|
}
|
|
6629
7010
|
try {
|
|
7011
|
+
const traceparent = params.traceparent;
|
|
7012
|
+
const tracestate = params.tracestate;
|
|
6630
7013
|
const invocation = {
|
|
6631
7014
|
sessionId: params.sessionId,
|
|
6632
7015
|
toolCallId: params.toolCallId,
|
|
6633
7016
|
toolName: params.toolName,
|
|
6634
|
-
arguments: params.arguments
|
|
7017
|
+
arguments: params.arguments,
|
|
7018
|
+
traceparent,
|
|
7019
|
+
tracestate
|
|
6635
7020
|
};
|
|
6636
7021
|
const result = await handler(params.arguments, invocation);
|
|
6637
7022
|
return { result: this.normalizeToolResultV2(result) };
|
|
@@ -6661,7 +7046,10 @@ stderr: ${stderrOutput}`
|
|
|
6661
7046
|
try {
|
|
6662
7047
|
const result = await session._handlePermissionRequestV2(params.permissionRequest);
|
|
6663
7048
|
return { result };
|
|
6664
|
-
} catch (
|
|
7049
|
+
} catch (error) {
|
|
7050
|
+
if (error instanceof Error && error.message === NO_RESULT_PERMISSION_V2_ERROR) {
|
|
7051
|
+
throw error;
|
|
7052
|
+
}
|
|
6665
7053
|
return {
|
|
6666
7054
|
result: {
|
|
6667
7055
|
kind: "denied-no-approval-rule-and-could-not-request-from-user"
|
|
@@ -6691,24 +7079,13 @@ stderr: ${stderrOutput}`
|
|
|
6691
7079
|
isToolResultObject(value) {
|
|
6692
7080
|
return typeof value === "object" && value !== null && "textResultForLlm" in value && typeof value.textResultForLlm === "string" && "resultType" in value;
|
|
6693
7081
|
}
|
|
6694
|
-
/**
|
|
6695
|
-
* Attempt to reconnect to the server
|
|
6696
|
-
*/
|
|
6697
|
-
async reconnect() {
|
|
6698
|
-
this.state = "disconnected";
|
|
6699
|
-
try {
|
|
6700
|
-
await this.stop();
|
|
6701
|
-
await this.start();
|
|
6702
|
-
} catch (_error) {
|
|
6703
|
-
}
|
|
6704
|
-
}
|
|
6705
7082
|
};
|
|
6706
7083
|
}
|
|
6707
7084
|
});
|
|
6708
7085
|
|
|
6709
7086
|
// node_modules/@github/copilot-sdk/dist/types.js
|
|
6710
7087
|
var approveAll;
|
|
6711
|
-
var
|
|
7088
|
+
var init_types3 = __esm({
|
|
6712
7089
|
"node_modules/@github/copilot-sdk/dist/types.js"() {
|
|
6713
7090
|
"use strict";
|
|
6714
7091
|
approveAll = () => ({ kind: "approved" });
|
|
@@ -6721,21 +7098,21 @@ var init_dist = __esm({
|
|
|
6721
7098
|
"use strict";
|
|
6722
7099
|
init_client();
|
|
6723
7100
|
init_session();
|
|
6724
|
-
|
|
7101
|
+
init_types3();
|
|
6725
7102
|
}
|
|
6726
7103
|
});
|
|
6727
7104
|
|
|
6728
7105
|
// src/L1-infra/ai/copilot.ts
|
|
6729
7106
|
import { existsSync as existsSync5 } from "fs";
|
|
6730
7107
|
import { join as join6, dirname as dirname4 } from "path";
|
|
6731
|
-
import { createRequire as
|
|
7108
|
+
import { createRequire as createRequire3 } from "module";
|
|
6732
7109
|
function resolveCopilotCliPath() {
|
|
6733
7110
|
const platform = process.platform;
|
|
6734
7111
|
const arch = process.arch;
|
|
6735
7112
|
const binaryName = platform === "win32" ? "copilot.exe" : "copilot";
|
|
6736
7113
|
const platformPkg = `@github/copilot-${platform}-${arch}`;
|
|
6737
7114
|
try {
|
|
6738
|
-
const require_ =
|
|
7115
|
+
const require_ = createRequire3(import.meta.url);
|
|
6739
7116
|
const searchPaths = require_.resolve.paths(platformPkg) ?? [];
|
|
6740
7117
|
for (const base of searchPaths) {
|
|
6741
7118
|
const candidate = join6(base, platformPkg, binaryName);
|
|
@@ -9795,7 +10172,7 @@ Call the generate_thumbnail tool with a detailed, vivid prompt that will create
|
|
|
9795
10172
|
});
|
|
9796
10173
|
|
|
9797
10174
|
// src/L7-app/sdk/VidPipeSDK.ts
|
|
9798
|
-
|
|
10175
|
+
init_types2();
|
|
9799
10176
|
init_environment();
|
|
9800
10177
|
init_globalConfig();
|
|
9801
10178
|
init_fileSystem();
|
|
@@ -10054,6 +10431,31 @@ var LateApiClient = class {
|
|
|
10054
10431
|
}
|
|
10055
10432
|
return allPosts;
|
|
10056
10433
|
}
|
|
10434
|
+
// ── Queue management ─────────────────────────────────────────────────
|
|
10435
|
+
async listQueues(profileId, all = false) {
|
|
10436
|
+
const params = new URLSearchParams({ profileId });
|
|
10437
|
+
if (all) params.set("all", "true");
|
|
10438
|
+
return this.request(`/queue/slots?${params}`);
|
|
10439
|
+
}
|
|
10440
|
+
async createQueue(params) {
|
|
10441
|
+
return this.request("/queue/slots", { method: "POST", body: JSON.stringify(params) });
|
|
10442
|
+
}
|
|
10443
|
+
async updateQueue(params) {
|
|
10444
|
+
return this.request("/queue/slots", { method: "PUT", body: JSON.stringify(params) });
|
|
10445
|
+
}
|
|
10446
|
+
async deleteQueue(profileId, queueId) {
|
|
10447
|
+
return this.request(`/queue/slots?profileId=${profileId}&queueId=${queueId}`, { method: "DELETE" });
|
|
10448
|
+
}
|
|
10449
|
+
async previewQueue(profileId, queueId, count = 20) {
|
|
10450
|
+
const params = new URLSearchParams({ profileId, count: String(count) });
|
|
10451
|
+
if (queueId) params.set("queueId", queueId);
|
|
10452
|
+
return this.request(`/queue/preview?${params}`);
|
|
10453
|
+
}
|
|
10454
|
+
async getNextQueueSlot(profileId, queueId) {
|
|
10455
|
+
const params = new URLSearchParams({ profileId });
|
|
10456
|
+
if (queueId) params.set("queueId", queueId);
|
|
10457
|
+
return this.request(`/queue/next-slot?${params}`);
|
|
10458
|
+
}
|
|
10057
10459
|
// ── Helper ───────────────────────────────────────────────────────────
|
|
10058
10460
|
async validateConnection() {
|
|
10059
10461
|
try {
|
|
@@ -10074,6 +10476,129 @@ function createLateApiClient(...args) {
|
|
|
10074
10476
|
return new LateApiClient(...args);
|
|
10075
10477
|
}
|
|
10076
10478
|
|
|
10479
|
+
// src/L3-services/queueMapping/queueMapping.ts
|
|
10480
|
+
init_configLogger();
|
|
10481
|
+
init_fileSystem();
|
|
10482
|
+
init_paths();
|
|
10483
|
+
var CACHE_FILE = ".vidpipe-queue-cache.json";
|
|
10484
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
10485
|
+
var memoryCache = null;
|
|
10486
|
+
function cachePath() {
|
|
10487
|
+
return join(process.cwd(), CACHE_FILE);
|
|
10488
|
+
}
|
|
10489
|
+
function isCacheValid(cache) {
|
|
10490
|
+
const fetchedAtTime = new Date(cache.fetchedAt).getTime();
|
|
10491
|
+
if (Number.isNaN(fetchedAtTime)) {
|
|
10492
|
+
logger_default.warn("Invalid fetchedAt in queue cache; treating as stale", {
|
|
10493
|
+
fetchedAt: cache.fetchedAt
|
|
10494
|
+
});
|
|
10495
|
+
return false;
|
|
10496
|
+
}
|
|
10497
|
+
const age = Date.now() - fetchedAtTime;
|
|
10498
|
+
return age < CACHE_TTL_MS;
|
|
10499
|
+
}
|
|
10500
|
+
async function readFileCache() {
|
|
10501
|
+
try {
|
|
10502
|
+
const raw = await readTextFile(cachePath());
|
|
10503
|
+
const cache = JSON.parse(raw);
|
|
10504
|
+
if (cache.mappings && cache.profileId && cache.fetchedAt && isCacheValid(cache)) {
|
|
10505
|
+
return cache;
|
|
10506
|
+
}
|
|
10507
|
+
return null;
|
|
10508
|
+
} catch {
|
|
10509
|
+
return null;
|
|
10510
|
+
}
|
|
10511
|
+
}
|
|
10512
|
+
async function writeFileCache(cache) {
|
|
10513
|
+
try {
|
|
10514
|
+
if (!cache || typeof cache !== "object" || !cache.mappings || !cache.profileId || !cache.fetchedAt) {
|
|
10515
|
+
logger_default.warn("Invalid queue cache structure, skipping write");
|
|
10516
|
+
return;
|
|
10517
|
+
}
|
|
10518
|
+
const sanitized = {
|
|
10519
|
+
mappings: typeof cache.mappings === "object" ? { ...cache.mappings } : {},
|
|
10520
|
+
profileId: String(cache.profileId),
|
|
10521
|
+
fetchedAt: String(cache.fetchedAt)
|
|
10522
|
+
};
|
|
10523
|
+
for (const [name, id] of Object.entries(sanitized.mappings)) {
|
|
10524
|
+
if (typeof name !== "string" || typeof id !== "string" || /[\x00-\x1f]/.test(name) || /[\x00-\x1f]/.test(id)) {
|
|
10525
|
+
logger_default.warn("Invalid queue mapping data from API, skipping cache write");
|
|
10526
|
+
return;
|
|
10527
|
+
}
|
|
10528
|
+
}
|
|
10529
|
+
const resolvedCachePath = resolve(cachePath());
|
|
10530
|
+
if (!resolvedCachePath.startsWith(resolve(process.cwd()) + sep)) {
|
|
10531
|
+
throw new Error("Cache path outside working directory");
|
|
10532
|
+
}
|
|
10533
|
+
await writeTextFile(resolvedCachePath, JSON.stringify(sanitized, null, 2));
|
|
10534
|
+
} catch (err) {
|
|
10535
|
+
logger_default.warn("Failed to write queue cache file", { error: err });
|
|
10536
|
+
}
|
|
10537
|
+
}
|
|
10538
|
+
async function fetchAndCache() {
|
|
10539
|
+
const client = new LateApiClient();
|
|
10540
|
+
const profiles = await client.listProfiles();
|
|
10541
|
+
if (profiles.length === 0) {
|
|
10542
|
+
logger_default.warn("No Late API profiles found \u2014 queue mappings will be empty");
|
|
10543
|
+
const emptyCache = {
|
|
10544
|
+
mappings: {},
|
|
10545
|
+
profileId: "",
|
|
10546
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10547
|
+
};
|
|
10548
|
+
memoryCache = emptyCache;
|
|
10549
|
+
return emptyCache;
|
|
10550
|
+
}
|
|
10551
|
+
const profileId = profiles[0]._id;
|
|
10552
|
+
const { queues } = await client.listQueues(profileId, true);
|
|
10553
|
+
if (queues.length === 0) {
|
|
10554
|
+
logger_default.warn(
|
|
10555
|
+
"No queues found in Late API \u2014 run `vidpipe sync-queues` to create platform queues"
|
|
10556
|
+
);
|
|
10557
|
+
}
|
|
10558
|
+
const mappings = {};
|
|
10559
|
+
for (const queue of queues) {
|
|
10560
|
+
mappings[queue.name] = queue._id;
|
|
10561
|
+
}
|
|
10562
|
+
const cache = {
|
|
10563
|
+
mappings,
|
|
10564
|
+
profileId,
|
|
10565
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10566
|
+
};
|
|
10567
|
+
memoryCache = cache;
|
|
10568
|
+
await writeFileCache(cache);
|
|
10569
|
+
logger_default.info("Refreshed Late queue mappings", {
|
|
10570
|
+
queueCount: queues.length,
|
|
10571
|
+
queues: Object.keys(mappings)
|
|
10572
|
+
});
|
|
10573
|
+
return cache;
|
|
10574
|
+
}
|
|
10575
|
+
async function ensureMappings() {
|
|
10576
|
+
if (memoryCache && isCacheValid(memoryCache)) {
|
|
10577
|
+
return memoryCache;
|
|
10578
|
+
}
|
|
10579
|
+
const fileCache = await readFileCache();
|
|
10580
|
+
if (fileCache) {
|
|
10581
|
+
memoryCache = fileCache;
|
|
10582
|
+
return fileCache;
|
|
10583
|
+
}
|
|
10584
|
+
try {
|
|
10585
|
+
return await fetchAndCache();
|
|
10586
|
+
} catch (err) {
|
|
10587
|
+
logger_default.error("Failed to fetch Late queue mappings", { error: err });
|
|
10588
|
+
return { mappings: {}, profileId: "", fetchedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
10589
|
+
}
|
|
10590
|
+
}
|
|
10591
|
+
async function getQueueId(platform, clipType) {
|
|
10592
|
+
const cache = await ensureMappings();
|
|
10593
|
+
const normalizedPlatform = platform === "twitter" ? "x" : platform;
|
|
10594
|
+
const queueName = `${normalizedPlatform}-${clipType}`;
|
|
10595
|
+
return cache.mappings[queueName] ?? null;
|
|
10596
|
+
}
|
|
10597
|
+
async function getProfileId() {
|
|
10598
|
+
const cache = await ensureMappings();
|
|
10599
|
+
return cache.profileId;
|
|
10600
|
+
}
|
|
10601
|
+
|
|
10077
10602
|
// src/L2-clients/scheduleStore/scheduleStore.ts
|
|
10078
10603
|
init_fileSystem();
|
|
10079
10604
|
init_paths();
|
|
@@ -10376,7 +10901,7 @@ function getIdeaSpacingConfig() {
|
|
|
10376
10901
|
}
|
|
10377
10902
|
|
|
10378
10903
|
// src/L3-services/postStore/postStore.ts
|
|
10379
|
-
|
|
10904
|
+
init_types2();
|
|
10380
10905
|
init_environment();
|
|
10381
10906
|
init_configLogger();
|
|
10382
10907
|
init_fileSystem();
|
|
@@ -11249,11 +11774,11 @@ async function markFailed(slug, error) {
|
|
|
11249
11774
|
init_fileSystem();
|
|
11250
11775
|
init_paths();
|
|
11251
11776
|
init_configLogger();
|
|
11252
|
-
|
|
11253
|
-
|
|
11777
|
+
init_types2();
|
|
11778
|
+
init_types2();
|
|
11254
11779
|
|
|
11255
11780
|
// src/L3-services/socialPosting/platformContentStrategy.ts
|
|
11256
|
-
|
|
11781
|
+
init_types2();
|
|
11257
11782
|
var CONTENT_MATRIX = {
|
|
11258
11783
|
["youtube" /* YouTube */]: {
|
|
11259
11784
|
video: { captions: true, variantKey: null },
|
|
@@ -11301,10 +11826,10 @@ async function generateImage2(prompt, outputPath, options) {
|
|
|
11301
11826
|
}
|
|
11302
11827
|
|
|
11303
11828
|
// src/L3-services/queueBuilder/queueBuilder.ts
|
|
11304
|
-
function resolveShortMedia(clip, platform) {
|
|
11829
|
+
function resolveShortMedia(clip, platform, variantsEnabled) {
|
|
11305
11830
|
const rule = getMediaRule(platform, "short");
|
|
11306
11831
|
if (!rule) return null;
|
|
11307
|
-
if (rule.variantKey && clip.variants?.length) {
|
|
11832
|
+
if (variantsEnabled !== false && rule.variantKey && clip.variants?.length) {
|
|
11308
11833
|
const match = clip.variants.find((v) => v.platform === rule.variantKey);
|
|
11309
11834
|
if (match) return match.path;
|
|
11310
11835
|
if (platform === "instagram" /* Instagram */) {
|
|
@@ -11351,7 +11876,7 @@ async function parsePostFrontmatter(postPath) {
|
|
|
11351
11876
|
function stripFrontmatter(content) {
|
|
11352
11877
|
return content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
|
11353
11878
|
}
|
|
11354
|
-
async function buildPublishQueue(video, shorts, mediumClips, socialPosts, captionedVideoPath, ideaIds) {
|
|
11879
|
+
async function buildPublishQueue(video, shorts, mediumClips, socialPosts, captionedVideoPath, ideaIds, variantsEnabled) {
|
|
11355
11880
|
const result = { itemsCreated: 0, itemsSkipped: 0, errors: [] };
|
|
11356
11881
|
for (const post of socialPosts) {
|
|
11357
11882
|
try {
|
|
@@ -11370,7 +11895,7 @@ async function buildPublishQueue(video, shorts, mediumClips, socialPosts, captio
|
|
|
11370
11895
|
clipSlug = short.slug;
|
|
11371
11896
|
clipType = "short";
|
|
11372
11897
|
sourceClip = dirname(short.outputPath);
|
|
11373
|
-
mediaPath = resolveShortMedia(short, post.platform);
|
|
11898
|
+
mediaPath = resolveShortMedia(short, post.platform, variantsEnabled);
|
|
11374
11899
|
thumbnailPath = short.thumbnailPath ?? null;
|
|
11375
11900
|
clipIdeaIssueNumber = short.ideaIssueNumber;
|
|
11376
11901
|
} else if (medium) {
|
|
@@ -11508,7 +12033,7 @@ init_modelConfig();
|
|
|
11508
12033
|
init_configLogger();
|
|
11509
12034
|
init_ideaService();
|
|
11510
12035
|
init_providerFactory();
|
|
11511
|
-
|
|
12036
|
+
init_types2();
|
|
11512
12037
|
init_BaseAgent();
|
|
11513
12038
|
var BASE_SYSTEM_PROMPT = `You are a content strategist for a tech content creator. Your role is to research trending topics, analyze what's working, and generate compelling video ideas grounded in real-world data.
|
|
11514
12039
|
|
|
@@ -14823,7 +15348,7 @@ var SocialPostAsset = class extends TextAsset {
|
|
|
14823
15348
|
// src/L5-assets/ShortVideoAsset.ts
|
|
14824
15349
|
init_paths();
|
|
14825
15350
|
init_fileSystem();
|
|
14826
|
-
|
|
15351
|
+
init_types2();
|
|
14827
15352
|
|
|
14828
15353
|
// src/L5-assets/thumbnailGeneration.ts
|
|
14829
15354
|
init_paths();
|
|
@@ -15083,7 +15608,7 @@ var ShortVideoAsset = class extends VideoAsset {
|
|
|
15083
15608
|
// src/L5-assets/MediumClipAsset.ts
|
|
15084
15609
|
init_paths();
|
|
15085
15610
|
init_fileSystem();
|
|
15086
|
-
|
|
15611
|
+
init_types2();
|
|
15087
15612
|
var MediumClipAsset = class extends VideoAsset {
|
|
15088
15613
|
/** Parent video this clip was extracted from */
|
|
15089
15614
|
parent;
|
|
@@ -15493,7 +16018,39 @@ init_videoOperations();
|
|
|
15493
16018
|
init_fileSystem();
|
|
15494
16019
|
init_paths();
|
|
15495
16020
|
init_configLogger();
|
|
15496
|
-
|
|
16021
|
+
function buildShortsSystemPrompt(clipConfig) {
|
|
16022
|
+
const minDuration = clipConfig?.duration?.min ?? 15;
|
|
16023
|
+
const maxDuration = clipConfig?.duration?.max ?? 60;
|
|
16024
|
+
const minViralScore = clipConfig?.minViralScore ?? 8;
|
|
16025
|
+
const maxClips = clipConfig?.maxClips ?? 5;
|
|
16026
|
+
const strategy = clipConfig?.strategy ?? "hook-first";
|
|
16027
|
+
const isHookFirst = strategy === "hook-first";
|
|
16028
|
+
const hookFirstSection = isHookFirst ? `
|
|
16029
|
+
### Hook-First Video Ordering
|
|
16030
|
+
|
|
16031
|
+
If a short's natural content flows A\u2192B\u2192C\u2192D, the final short should play as D\u2192A\u2192B\u2192C \u2014 the **payoff moment (D) is moved to the front as the hook**, then the content plays from the beginning up to that point. The hook does NOT repeat.
|
|
16032
|
+
|
|
16033
|
+
**How to implement:**
|
|
16034
|
+
1. Plan the content as normal (full story A\u2192D)
|
|
16035
|
+
2. Identify the single most arresting 2-5 second moment \u2014 usually the payoff, punchline, or emotional peak
|
|
16036
|
+
3. That moment becomes the FIRST segment in the segments array
|
|
16037
|
+
4. The remaining content plays chronologically from start to just before the hook
|
|
16038
|
+
5. Example: content [120s\u2013150s], best moment [145s\u2013150s] \u2192 segments: [{start: 145, end: 150}, {start: 120, end: 145}]
|
|
16039
|
+
|
|
16040
|
+
**Hook quality rules (NEVER violate):**
|
|
16041
|
+
- The hook segment MUST start and end on a **complete sentence or clause boundary**
|
|
16042
|
+
- The hook MUST be a **self-contained, complete thought** \u2014 understandable without prior context
|
|
16043
|
+
- If no moment qualifies as a clean hook, **keep segments chronological** and use hook text only` : `
|
|
16044
|
+
### Chronological Video Ordering
|
|
16045
|
+
|
|
16046
|
+
Segments MUST be ordered chronologically \u2014 preserve the original video timeline. Do NOT reorder segments for hook-first ordering. Instead, rely on a strong verbal hook (the \`hook\` text field) as a text overlay to capture attention in the first 3 seconds while the content plays in its natural order.
|
|
16047
|
+
|
|
16048
|
+
**How to implement:**
|
|
16049
|
+
1. Plan the content as normal \u2014 segments play in order from the video
|
|
16050
|
+
2. Identify the most compelling hook text that teases the payoff
|
|
16051
|
+
3. All segments in the segments array must be in ascending time order
|
|
16052
|
+
4. The hook text overlay provides the attention-grab, not segment reordering`;
|
|
16053
|
+
return `You are a viral short-form video strategist. Your job is to analyze a video transcript with word-level timestamps and extract the **most compelling, shareable moments** as shorts (${minDuration}\u2013${maxDuration} seconds each).
|
|
15497
16054
|
|
|
15498
16055
|
## Core Philosophy: Quality Over Quantity
|
|
15499
16056
|
|
|
@@ -15510,7 +16067,7 @@ Design every clip to maximize rewatches and shares, not passive likes.
|
|
|
15510
16067
|
## Your workflow
|
|
15511
16068
|
1. Read the transcript and note the total duration.
|
|
15512
16069
|
2. Work through the transcript **section by section**. For each chunk, identify moments with genuine viral potential.
|
|
15513
|
-
3. For each potential short, score it using the Viral Score Framework (see below). **Only extract clips scoring
|
|
16070
|
+
3. For each potential short, score it using the Viral Score Framework (see below). **Only extract clips scoring ${minViralScore} or higher.**
|
|
15514
16071
|
4. Call **add_shorts** for each batch of qualifying shorts. You can call it as many times as needed.
|
|
15515
16072
|
5. After your first pass, call **review_shorts** to see everything you've planned so far.
|
|
15516
16073
|
6. Review critically: Would YOU share each of these? Could any be combined into stronger composites? Are there moments you underscored?
|
|
@@ -15525,7 +16082,7 @@ Viral Score = (Hook Strength \xD7 3) + (Emotional Intensity \xD7 2) +
|
|
|
15525
16082
|
(Replay Potential \xD7 2)
|
|
15526
16083
|
|
|
15527
16084
|
Maximum score: 60 \u2192 Normalized to 1-20 scale (divide by 3)
|
|
15528
|
-
Minimum to extract:
|
|
16085
|
+
Minimum to extract: ${minViralScore}/20
|
|
15529
16086
|
\`\`\`
|
|
15530
16087
|
|
|
15531
16088
|
| Factor | 1 (Weak) | 3 (Moderate) | 5 (Strong) |
|
|
@@ -15561,22 +16118,7 @@ Minimum to extract: 8/20
|
|
|
15561
16118
|
| **result-first** | Show the outcome immediately, then explain how | Tutorials, before/after |
|
|
15562
16119
|
| **bold-claim** | Make a specific, surprising statement of fact | Data-driven, authority content |
|
|
15563
16120
|
| **question** | "Want to know why X?" \u2014 engage curiosity directly | Engagement-focused, relatable |
|
|
15564
|
-
|
|
15565
|
-
### Hook-First Video Ordering
|
|
15566
|
-
|
|
15567
|
-
If a short's natural content flows A\u2192B\u2192C\u2192D, the final short should play as D\u2192A\u2192B\u2192C \u2014 the **payoff moment (D) is moved to the front as the hook**, then the content plays from the beginning up to that point. The hook does NOT repeat.
|
|
15568
|
-
|
|
15569
|
-
**How to implement:**
|
|
15570
|
-
1. Plan the content as normal (full story A\u2192D)
|
|
15571
|
-
2. Identify the single most arresting 2-5 second moment \u2014 usually the payoff, punchline, or emotional peak
|
|
15572
|
-
3. That moment becomes the FIRST segment in the segments array
|
|
15573
|
-
4. The remaining content plays chronologically from start to just before the hook
|
|
15574
|
-
5. Example: content [120s\u2013150s], best moment [145s\u2013150s] \u2192 segments: [{start: 145, end: 150}, {start: 120, end: 145}]
|
|
15575
|
-
|
|
15576
|
-
**Hook quality rules (NEVER violate):**
|
|
15577
|
-
- The hook segment MUST start and end on a **complete sentence or clause boundary**
|
|
15578
|
-
- The hook MUST be a **self-contained, complete thought** \u2014 understandable without prior context
|
|
15579
|
-
- If no moment qualifies as a clean hook, **keep segments chronological** and use hook text only
|
|
16121
|
+
${hookFirstSection}
|
|
15580
16122
|
|
|
15581
16123
|
## Narrative structures (classify every short)
|
|
15582
16124
|
|
|
@@ -15627,14 +16169,14 @@ Composites (multi-segment shorts) often make the **best** shorts:
|
|
|
15627
16169
|
|
|
15628
16170
|
## Rules
|
|
15629
16171
|
|
|
15630
|
-
1. Each short must be
|
|
16172
|
+
1. Each short must be ${minDuration}-${maxDuration} seconds total duration.
|
|
15631
16173
|
2. Timestamps must align to word boundaries from the transcript.
|
|
15632
16174
|
3. Prefer natural sentence boundaries for clean cuts.
|
|
15633
16175
|
4. Every short needs a catchy, descriptive title (5-10 words).
|
|
15634
16176
|
5. Tags should be lowercase, no hashes, 3-6 per short.
|
|
15635
16177
|
6. A 1-second buffer is automatically added around each segment boundary.
|
|
15636
16178
|
7. Avoid significant timestamp overlap between shorts.
|
|
15637
|
-
8. **Minimum viral score of
|
|
16179
|
+
8. **Minimum viral score of ${minViralScore}/20 to extract.** Be ruthless about quality.
|
|
15638
16180
|
9. Every short MUST have a hook, hookType, emotionalTrigger, viralScore, narrativeStructure, and shareReason.
|
|
15639
16181
|
|
|
15640
16182
|
## Using Clip Direction
|
|
@@ -15647,8 +16189,10 @@ You may receive AI-generated clip direction with suggested shorts. Use these as
|
|
|
15647
16189
|
|
|
15648
16190
|
Before adding a short, ask yourself: **"Would I interrupt someone to show them this?"**
|
|
15649
16191
|
- If YES \u2192 strong clip, add it
|
|
15650
|
-
- If "maybe, it's interesting" \u2192 score it honestly and only keep if \
|
|
16192
|
+
- If "maybe, it's interesting" \u2192 score it honestly and only keep if \u2265${minViralScore}
|
|
15651
16193
|
- If NO \u2192 drop it, no matter how "complete" the topic coverage feels`;
|
|
16194
|
+
}
|
|
16195
|
+
var SYSTEM_PROMPT5 = buildShortsSystemPrompt();
|
|
15652
16196
|
var ADD_SHORTS_SCHEMA = {
|
|
15653
16197
|
type: "object",
|
|
15654
16198
|
properties: {
|
|
@@ -15798,20 +16342,22 @@ Review critically:
|
|
|
15798
16342
|
return this.isFinalized;
|
|
15799
16343
|
}
|
|
15800
16344
|
};
|
|
15801
|
-
async function generateShorts(video, transcript, model, clipDirection, webcamOverride, ideas) {
|
|
15802
|
-
const
|
|
16345
|
+
async function generateShorts(video, transcript, model, clipDirection, webcamOverride, ideas, clipConfig, generateVariants) {
|
|
16346
|
+
const basePrompt = clipConfig ? buildShortsSystemPrompt(clipConfig) : SYSTEM_PROMPT5;
|
|
16347
|
+
const systemPrompt = basePrompt + (ideas?.length ? buildIdeaContext(ideas) : "");
|
|
15803
16348
|
const agent = new ShortsAgent(systemPrompt, model);
|
|
15804
16349
|
const transcriptLines = transcript.segments.map((seg) => {
|
|
15805
16350
|
const words = seg.words.map((w) => `[${w.start.toFixed(2)}-${w.end.toFixed(2)}] ${w.word}`).join(" ");
|
|
15806
16351
|
return `[${seg.start.toFixed(2)}s \u2013 ${seg.end.toFixed(2)}s] ${seg.text}
|
|
15807
16352
|
Words: ${words}`;
|
|
15808
16353
|
});
|
|
16354
|
+
const minViralScore = clipConfig?.minViralScore ?? 8;
|
|
15809
16355
|
const promptParts = [
|
|
15810
16356
|
`Analyze the following transcript (${transcript.duration.toFixed(0)}s total) and find the most viral-worthy moments for shorts.
|
|
15811
16357
|
`,
|
|
15812
16358
|
`Video: ${video.filename}`,
|
|
15813
16359
|
`Duration: ${transcript.duration.toFixed(1)}s`,
|
|
15814
|
-
`Focus on quality over quantity \u2014 only extract clips scoring
|
|
16360
|
+
`Focus on quality over quantity \u2014 only extract clips scoring ${minViralScore}+ on the viral score framework. Every clip must have a hook, hookType, emotionalTrigger, viralScore, narrativeStructure, and shareReason.
|
|
15815
16361
|
`,
|
|
15816
16362
|
"--- TRANSCRIPT ---\n",
|
|
15817
16363
|
transcriptLines.join("\n\n"),
|
|
@@ -15863,23 +16409,27 @@ Words: ${words}`;
|
|
|
15863
16409
|
} else {
|
|
15864
16410
|
await extractCompositeClip2(video.repoPath, segments, outputPath);
|
|
15865
16411
|
}
|
|
15866
|
-
let
|
|
15867
|
-
|
|
15868
|
-
|
|
15869
|
-
|
|
15870
|
-
|
|
15871
|
-
|
|
15872
|
-
|
|
15873
|
-
|
|
15874
|
-
|
|
15875
|
-
|
|
15876
|
-
|
|
15877
|
-
|
|
15878
|
-
|
|
16412
|
+
let clipVariants;
|
|
16413
|
+
if (generateVariants !== false) {
|
|
16414
|
+
try {
|
|
16415
|
+
const defaultPlatforms = ["tiktok", "youtube-shorts", "instagram-reels", "instagram-feed", "linkedin"];
|
|
16416
|
+
const results = await generatePlatformVariants2(outputPath, shortsDir, shortSlug, defaultPlatforms, { webcamOverride });
|
|
16417
|
+
if (results.length > 0) {
|
|
16418
|
+
clipVariants = results.map((v) => ({
|
|
16419
|
+
path: v.path,
|
|
16420
|
+
aspectRatio: v.aspectRatio,
|
|
16421
|
+
platform: v.platform,
|
|
16422
|
+
width: v.width,
|
|
16423
|
+
height: v.height
|
|
16424
|
+
}));
|
|
16425
|
+
logger_default.info(`[ShortsAgent] Generated ${clipVariants.length} platform variants for: ${plan.title}`);
|
|
16426
|
+
}
|
|
16427
|
+
} catch (err) {
|
|
16428
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16429
|
+
logger_default.warn(`[ShortsAgent] Platform variant generation failed for ${plan.title}: ${message}`);
|
|
15879
16430
|
}
|
|
15880
|
-
}
|
|
15881
|
-
|
|
15882
|
-
logger_default.warn(`[ShortsAgent] Platform variant generation failed for ${plan.title}: ${message}`);
|
|
16431
|
+
} else {
|
|
16432
|
+
logger_default.info(`[ShortsAgent] Skipping variant generation for: ${plan.title} (disabled by spec)`);
|
|
15883
16433
|
}
|
|
15884
16434
|
let captionedPath;
|
|
15885
16435
|
try {
|
|
@@ -15894,8 +16444,8 @@ Words: ${words}`;
|
|
|
15894
16444
|
logger_default.warn(`[ShortsAgent] Caption burning failed for ${plan.title}: ${message}`);
|
|
15895
16445
|
captionedPath = void 0;
|
|
15896
16446
|
}
|
|
15897
|
-
if (
|
|
15898
|
-
const portraitVariants =
|
|
16447
|
+
if (clipVariants) {
|
|
16448
|
+
const portraitVariants = clipVariants.filter((v) => v.aspectRatio === "9:16");
|
|
15899
16449
|
if (portraitVariants.length > 0) {
|
|
15900
16450
|
try {
|
|
15901
16451
|
const hookText = plan.hook ?? plan.title;
|
|
@@ -15913,7 +16463,7 @@ Words: ${words}`;
|
|
|
15913
16463
|
logger_default.warn(`[ShortsAgent] Portrait caption burning failed for ${plan.title}: ${message}`);
|
|
15914
16464
|
}
|
|
15915
16465
|
}
|
|
15916
|
-
const nonPortraitVariants =
|
|
16466
|
+
const nonPortraitVariants = clipVariants.filter((v) => v.aspectRatio !== "9:16");
|
|
15917
16467
|
for (const variant of nonPortraitVariants) {
|
|
15918
16468
|
try {
|
|
15919
16469
|
const variantAssContent = segments.length === 1 ? generateStyledASSForSegment(transcript, segments[0].start, segments[0].end) : generateStyledASSForComposite(transcript, segments);
|
|
@@ -15964,7 +16514,7 @@ Words: ${words}`;
|
|
|
15964
16514
|
description: plan.description,
|
|
15965
16515
|
tags: plan.tags,
|
|
15966
16516
|
hook: plan.hook,
|
|
15967
|
-
variants,
|
|
16517
|
+
variants: clipVariants,
|
|
15968
16518
|
hookType: plan.hookType,
|
|
15969
16519
|
emotionalTrigger: plan.emotionalTrigger,
|
|
15970
16520
|
viralScore: plan.viralScore,
|
|
@@ -15987,7 +16537,15 @@ init_videoOperations();
|
|
|
15987
16537
|
init_fileSystem();
|
|
15988
16538
|
init_paths();
|
|
15989
16539
|
init_configLogger();
|
|
15990
|
-
|
|
16540
|
+
function buildMediumSystemPrompt(clipConfig) {
|
|
16541
|
+
const minDuration = clipConfig?.duration?.min ?? 60;
|
|
16542
|
+
const maxDuration = clipConfig?.duration?.max ?? 180;
|
|
16543
|
+
const minViralScore = clipConfig?.minViralScore ?? 10;
|
|
16544
|
+
const maxClips = clipConfig?.maxClips ?? 5;
|
|
16545
|
+
const minMinutes = Math.floor(minDuration / 60);
|
|
16546
|
+
const maxMinutes = Math.ceil(maxDuration / 60);
|
|
16547
|
+
const durationLabel = `${minMinutes}-${maxMinutes} minute`;
|
|
16548
|
+
return `You are a medium-form video content strategist. Your job is to analyze a video transcript with word-level timestamps and extract the **most valuable, engaging ${minMinutes}-${maxMinutes} minute segments** as standalone medium-form clips.
|
|
15991
16549
|
|
|
15992
16550
|
## Core Philosophy: Value Density Over Coverage
|
|
15993
16551
|
|
|
@@ -16004,7 +16562,7 @@ Design every clip to maximize saves and shares.
|
|
|
16004
16562
|
## Your workflow
|
|
16005
16563
|
1. Read the transcript and note the total duration.
|
|
16006
16564
|
2. Work through the transcript **section by section** (roughly 5-8 minute chunks). For each chunk, identify segments with genuine standalone value.
|
|
16007
|
-
3. For each potential clip, score it using the Viral Score Framework (see below). **Only extract clips scoring
|
|
16565
|
+
3. For each potential clip, score it using the Viral Score Framework (see below). **Only extract clips scoring ${minViralScore} or higher.**
|
|
16008
16566
|
4. Call **add_medium_clips** for each batch of clips you find. You can call it as many times as needed.
|
|
16009
16567
|
5. After your first pass, call **review_medium_clips** to see everything you've planned so far.
|
|
16010
16568
|
6. Review critically: Does each clip deliver clear, standalone value? Would someone save it? Could segments be combined into something stronger?
|
|
@@ -16019,7 +16577,7 @@ Viral Score = (Hook Strength \xD7 3) + (Emotional Intensity \xD7 2) +
|
|
|
16019
16577
|
(Replay Potential \xD7 2)
|
|
16020
16578
|
|
|
16021
16579
|
Maximum score: 60 \u2192 Normalized to 1-20 scale (divide by 3)
|
|
16022
|
-
Minimum to extract:
|
|
16580
|
+
Minimum to extract: ${minViralScore}/20 (higher bar than shorts \u2014 medium clips cost more to produce)
|
|
16023
16581
|
\`\`\`
|
|
16024
16582
|
|
|
16025
16583
|
| Factor | 1 (Weak) | 3 (Moderate) | 5 (Strong) |
|
|
@@ -16098,10 +16656,10 @@ Identify the PRIMARY emotion driving engagement:
|
|
|
16098
16656
|
|
|
16099
16657
|
## Duration optimization
|
|
16100
16658
|
|
|
16101
|
-
- **Sweet spot**:
|
|
16102
|
-
- **Maximum**:
|
|
16103
|
-
- **Under
|
|
16104
|
-
- **Over 120 seconds**: Requires multiple micro-hooks and exceptional pacing
|
|
16659
|
+
- **Sweet spot**: ${minDuration}-${Math.min(maxDuration, 120)} seconds (${minMinutes}-${Math.min(maxMinutes, 2)} minutes)
|
|
16660
|
+
- **Maximum**: ${maxDuration} seconds \u2014 only if retention quality is exceptional
|
|
16661
|
+
- **Under ${minDuration} seconds**: Too short for medium format \u2014 should be a short instead
|
|
16662
|
+
- **Over ${Math.min(maxDuration, 120)} seconds**: Requires multiple micro-hooks and exceptional pacing
|
|
16105
16663
|
|
|
16106
16664
|
## Differences from shorts
|
|
16107
16665
|
|
|
@@ -16123,7 +16681,7 @@ For compilations, segments must be in chronological order.
|
|
|
16123
16681
|
|
|
16124
16682
|
## Rules
|
|
16125
16683
|
|
|
16126
|
-
1. Each clip must be
|
|
16684
|
+
1. Each clip must be ${minDuration}-${maxDuration} seconds total duration.
|
|
16127
16685
|
2. Timestamps must align to word boundaries from the transcript.
|
|
16128
16686
|
3. Prefer natural sentence and paragraph boundaries for clean entry/exit points.
|
|
16129
16687
|
4. Each clip must be self-contained \u2014 a viewer with no other context should get value.
|
|
@@ -16131,7 +16689,7 @@ For compilations, segments must be in chronological order.
|
|
|
16131
16689
|
6. For compilations, specify segments in **chronological order**.
|
|
16132
16690
|
7. Tags should be lowercase, no hashes, 3-6 per clip.
|
|
16133
16691
|
8. A 1-second buffer is automatically added around each segment boundary.
|
|
16134
|
-
9. **Minimum viral score of
|
|
16692
|
+
9. **Minimum viral score of ${minViralScore}/20 to extract.** Medium clips cost more to produce \u2014 quality bar is higher.
|
|
16135
16693
|
10. Every clip MUST have hook, hookType, emotionalTrigger, viralScore, narrativeStructure, clipType, saveReason, and microHooks.
|
|
16136
16694
|
11. Avoid significant overlap with content that would work better as a short.
|
|
16137
16695
|
|
|
@@ -16139,7 +16697,7 @@ For compilations, segments must be in chronological order.
|
|
|
16139
16697
|
|
|
16140
16698
|
Before adding a clip, ask yourself: **"Would I bookmark this to come back to later?"**
|
|
16141
16699
|
- If YES \u2192 strong clip, add it
|
|
16142
|
-
- If "it's informative but not reference-worthy" \u2192 score it honestly and only keep if \
|
|
16700
|
+
- If "it's informative but not reference-worthy" \u2192 score it honestly and only keep if \u2265${minViralScore}
|
|
16143
16701
|
- If NO \u2192 drop it or consider if it works better as a short
|
|
16144
16702
|
|
|
16145
16703
|
## Using Clip Direction
|
|
@@ -16148,6 +16706,8 @@ You may receive AI-generated clip direction with suggested medium clips. Use the
|
|
|
16148
16706
|
- Feel free to adjust timestamps, combine suggestions, or ignore ones that don't work
|
|
16149
16707
|
- You may also find good clips NOT in the suggestions \u2014 always analyze the full transcript
|
|
16150
16708
|
- Pay special attention to suggested hooks and topic arcs \u2014 they come from multimodal analysis`;
|
|
16709
|
+
}
|
|
16710
|
+
var SYSTEM_PROMPT6 = buildMediumSystemPrompt();
|
|
16151
16711
|
var ADD_MEDIUM_CLIPS_SCHEMA = {
|
|
16152
16712
|
type: "object",
|
|
16153
16713
|
properties: {
|
|
@@ -16305,20 +16865,26 @@ Review critically:
|
|
|
16305
16865
|
return this.isFinalized;
|
|
16306
16866
|
}
|
|
16307
16867
|
};
|
|
16308
|
-
async function generateMediumClips(video, transcript, model, clipDirection, ideas) {
|
|
16309
|
-
const
|
|
16868
|
+
async function generateMediumClips(video, transcript, model, clipDirection, ideas, clipConfig) {
|
|
16869
|
+
const basePrompt = clipConfig ? buildMediumSystemPrompt(clipConfig) : SYSTEM_PROMPT6;
|
|
16870
|
+
const systemPrompt = basePrompt + (ideas?.length ? buildIdeaContext(ideas) : "");
|
|
16310
16871
|
const agent = new MediumVideoAgent(systemPrompt, model);
|
|
16311
16872
|
const transcriptLines = transcript.segments.map((seg) => {
|
|
16312
16873
|
const words = seg.words.map((w) => `[${w.start.toFixed(2)}-${w.end.toFixed(2)}] ${w.word}`).join(" ");
|
|
16313
16874
|
return `[${seg.start.toFixed(2)}s \u2013 ${seg.end.toFixed(2)}s] ${seg.text}
|
|
16314
16875
|
Words: ${words}`;
|
|
16315
16876
|
});
|
|
16877
|
+
const minDuration = clipConfig?.duration?.min ?? 60;
|
|
16878
|
+
const maxDuration = clipConfig?.duration?.max ?? 180;
|
|
16879
|
+
const minViralScore = clipConfig?.minViralScore ?? 10;
|
|
16880
|
+
const minMinutes = Math.floor(minDuration / 60);
|
|
16881
|
+
const maxMinutes = Math.ceil(maxDuration / 60);
|
|
16316
16882
|
const promptParts = [
|
|
16317
|
-
`Analyze the following transcript (${transcript.duration.toFixed(0)}s total) and find the most valuable segments for medium-length clips (
|
|
16883
|
+
`Analyze the following transcript (${transcript.duration.toFixed(0)}s total) and find the most valuable segments for medium-length clips (${minMinutes}-${maxMinutes} minutes each).
|
|
16318
16884
|
`,
|
|
16319
16885
|
`Video: ${video.filename}`,
|
|
16320
16886
|
`Duration: ${transcript.duration.toFixed(1)}s`,
|
|
16321
|
-
`Focus on value density over coverage \u2014 only extract clips scoring
|
|
16887
|
+
`Focus on value density over coverage \u2014 only extract clips scoring ${minViralScore}+ on the viral score framework. Every clip must have hook, hookType, emotionalTrigger, viralScore, narrativeStructure, clipType, saveReason, and microHooks.
|
|
16322
16888
|
`,
|
|
16323
16889
|
"--- TRANSCRIPT ---\n",
|
|
16324
16890
|
transcriptLines.join("\n\n"),
|
|
@@ -17203,17 +17769,27 @@ init_paths();
|
|
|
17203
17769
|
init_BaseAgent();
|
|
17204
17770
|
init_configLogger();
|
|
17205
17771
|
init_environment();
|
|
17206
|
-
|
|
17207
|
-
var
|
|
17208
|
-
Given a video transcript and summary you MUST generate one post for each of the 5 platforms listed below.
|
|
17209
|
-
Each post must match the platform's tone, format, and constraints exactly.
|
|
17210
|
-
|
|
17211
|
-
Platform guidelines:
|
|
17772
|
+
init_types2();
|
|
17773
|
+
var PER_PLATFORM_GUIDELINES = `Platform guidelines:
|
|
17212
17774
|
1. **TikTok** \u2013 Casual, hook-driven, trending hashtags, 150 chars max, emoji-heavy.
|
|
17213
17775
|
2. **YouTube** \u2013 Descriptive, SEO-optimized title + description, relevant tags.
|
|
17214
17776
|
3. **Instagram** \u2013 Visual storytelling, emoji-rich, 30 hashtags max, engaging caption.
|
|
17215
17777
|
4. **LinkedIn** \u2013 Professional, thought-leadership, industry insights, 1-3 hashtags.
|
|
17216
|
-
5. **X (Twitter)** \u2013 Concise, punchy, 280 chars max, 2-5 hashtags, thread-ready
|
|
17778
|
+
5. **X (Twitter)** \u2013 Concise, punchy, 280 chars max, 2-5 hashtags, thread-ready.`;
|
|
17779
|
+
var UNIFIED_TONE_GUIDELINES = `Platform guidelines:
|
|
17780
|
+
For all platforms, use the same professional but approachable tone. Keep posts concise, informative, and authentic. Use the same core message \u2014 adapt only for platform-specific formatting constraints (character limits, hashtag placement).
|
|
17781
|
+
1. **TikTok** \u2013 150 chars max, include relevant hashtags.
|
|
17782
|
+
2. **YouTube** \u2013 SEO-optimized title + description, relevant tags.
|
|
17783
|
+
3. **Instagram** \u2013 30 hashtags max, engaging caption.
|
|
17784
|
+
4. **LinkedIn** \u2013 1-3 hashtags, professional framing.
|
|
17785
|
+
5. **X (Twitter)** \u2013 280 chars max, 2-5 hashtags.`;
|
|
17786
|
+
function buildSocialMediaSystemPrompt(toneStrategy) {
|
|
17787
|
+
const platformGuidelines = toneStrategy === "unified" ? UNIFIED_TONE_GUIDELINES : PER_PLATFORM_GUIDELINES;
|
|
17788
|
+
return `You are a viral social-media content strategist.
|
|
17789
|
+
Given a video transcript and summary you MUST generate one post for each of the 5 platforms listed below.
|
|
17790
|
+
Each post must match the platform's format and constraints exactly.
|
|
17791
|
+
|
|
17792
|
+
${platformGuidelines}
|
|
17217
17793
|
|
|
17218
17794
|
IMPORTANT \u2013 Content format:
|
|
17219
17795
|
The "content" field you provide must be the FINAL, ready-to-post text that can be directly copied and pasted onto the platform. Do NOT use markdown headers, bullet points, or any formatting inside the content. Include hashtags inline at the end of the post text where appropriate. The content is saved as-is for direct posting.
|
|
@@ -17225,6 +17801,8 @@ Workflow:
|
|
|
17225
17801
|
|
|
17226
17802
|
Include relevant links in posts when search results provide them.
|
|
17227
17803
|
Always call "create_posts" exactly once with all 5 platform posts.`;
|
|
17804
|
+
}
|
|
17805
|
+
var SYSTEM_PROMPT8 = buildSocialMediaSystemPrompt();
|
|
17228
17806
|
var SocialMediaAgent = class extends BaseAgent {
|
|
17229
17807
|
collectedPosts = [];
|
|
17230
17808
|
constructor(systemPrompt = SYSTEM_PROMPT8, model) {
|
|
@@ -17405,8 +17983,9 @@ async function generateShortPosts(video, short, transcript, model, summary) {
|
|
|
17405
17983
|
await agent.destroy();
|
|
17406
17984
|
}
|
|
17407
17985
|
}
|
|
17408
|
-
async function generateSocialPosts(video, transcript, summary, outputDir, model, ideas) {
|
|
17409
|
-
const
|
|
17986
|
+
async function generateSocialPosts(video, transcript, summary, outputDir, model, ideas, toneStrategy) {
|
|
17987
|
+
const basePrompt = toneStrategy ? buildSocialMediaSystemPrompt(toneStrategy) : SYSTEM_PROMPT8;
|
|
17988
|
+
const systemPrompt = basePrompt + (ideas?.length ? buildIdeaContextForPosts(ideas) : "");
|
|
17410
17989
|
const agent = new SocialMediaAgent(systemPrompt, model);
|
|
17411
17990
|
try {
|
|
17412
17991
|
const userMessage = [
|
|
@@ -17839,7 +18418,7 @@ async function enhanceVideo(videoPath, transcript, video) {
|
|
|
17839
18418
|
// src/L5-assets/MainVideoAsset.ts
|
|
17840
18419
|
init_environment();
|
|
17841
18420
|
init_configLogger();
|
|
17842
|
-
|
|
18421
|
+
init_types2();
|
|
17843
18422
|
var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
|
|
17844
18423
|
sourcePath;
|
|
17845
18424
|
videoDir;
|
|
@@ -17848,6 +18427,16 @@ var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
|
|
|
17848
18427
|
_ideas = [];
|
|
17849
18428
|
/** Per-clip idea assignments from idea discovery (clipId → ideaIssueNumber) */
|
|
17850
18429
|
_clipIdeaMap = /* @__PURE__ */ new Map();
|
|
18430
|
+
/** Pipeline spec controlling clip and platform configuration */
|
|
18431
|
+
_spec;
|
|
18432
|
+
/** Set the pipeline spec for configuring agent behavior */
|
|
18433
|
+
setSpec(spec) {
|
|
18434
|
+
this._spec = spec;
|
|
18435
|
+
}
|
|
18436
|
+
/** Get the pipeline spec */
|
|
18437
|
+
get spec() {
|
|
18438
|
+
return this._spec;
|
|
18439
|
+
}
|
|
17851
18440
|
/** Set ideas for editorial direction */
|
|
17852
18441
|
setIdeas(ideas) {
|
|
17853
18442
|
this._ideas = ideas;
|
|
@@ -18397,7 +18986,18 @@ var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
|
|
|
18397
18986
|
async generateShortsInternal() {
|
|
18398
18987
|
const transcript = await this.getTranscript();
|
|
18399
18988
|
const videoFile = await this.toVideoFile();
|
|
18400
|
-
const
|
|
18989
|
+
const shortsConfig = this._spec?.clips.shorts;
|
|
18990
|
+
const shouldGenerateVariants = this._spec?.distribution.platforms.variants;
|
|
18991
|
+
const shorts = await generateShorts(
|
|
18992
|
+
videoFile,
|
|
18993
|
+
transcript,
|
|
18994
|
+
void 0,
|
|
18995
|
+
void 0,
|
|
18996
|
+
void 0,
|
|
18997
|
+
void 0,
|
|
18998
|
+
shortsConfig,
|
|
18999
|
+
shouldGenerateVariants
|
|
19000
|
+
);
|
|
18401
19001
|
logger_default.info(`Generated ${shorts.length} short clips`);
|
|
18402
19002
|
return shorts;
|
|
18403
19003
|
}
|
|
@@ -18429,7 +19029,15 @@ var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
|
|
|
18429
19029
|
async generateMediumClipsInternal() {
|
|
18430
19030
|
const transcript = await this.getTranscript();
|
|
18431
19031
|
const videoFile = await this.toVideoFile();
|
|
18432
|
-
const
|
|
19032
|
+
const mediumConfig = this._spec?.clips.medium;
|
|
19033
|
+
const clips = await generateMediumClips(
|
|
19034
|
+
videoFile,
|
|
19035
|
+
transcript,
|
|
19036
|
+
void 0,
|
|
19037
|
+
void 0,
|
|
19038
|
+
void 0,
|
|
19039
|
+
mediumConfig
|
|
19040
|
+
);
|
|
18433
19041
|
logger_default.info(`Generated ${clips.length} medium clips for ${this.slug}`);
|
|
18434
19042
|
return clips;
|
|
18435
19043
|
}
|
|
@@ -18452,7 +19060,16 @@ var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
|
|
|
18452
19060
|
const summary = await this.getSummary();
|
|
18453
19061
|
const video = await this.toVideoFile();
|
|
18454
19062
|
await ensureDirectory(this.socialPostsDir);
|
|
18455
|
-
const
|
|
19063
|
+
const toneStrategy = this._spec?.distribution.platforms.toneStrategy;
|
|
19064
|
+
const posts = await generateSocialPosts(
|
|
19065
|
+
video,
|
|
19066
|
+
transcript,
|
|
19067
|
+
summary,
|
|
19068
|
+
this.socialPostsDir,
|
|
19069
|
+
void 0,
|
|
19070
|
+
void 0,
|
|
19071
|
+
toneStrategy
|
|
19072
|
+
);
|
|
18456
19073
|
await this.markSocialPostsComplete();
|
|
18457
19074
|
return posts;
|
|
18458
19075
|
}
|
|
@@ -18826,7 +19443,8 @@ var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
|
|
|
18826
19443
|
async buildPublishQueueData(shorts, mediumClips, socialPosts, captionedVideoPath) {
|
|
18827
19444
|
const video = await this.toVideoFile();
|
|
18828
19445
|
const ideaIds = this._ideas.length > 0 ? this._ideas.map((idea) => String(idea.issueNumber)) : void 0;
|
|
18829
|
-
|
|
19446
|
+
const variantsEnabled = this._spec?.distribution.platforms.variants;
|
|
19447
|
+
return buildPublishQueue2(video, shorts, mediumClips, socialPosts, captionedVideoPath, ideaIds, variantsEnabled);
|
|
18830
19448
|
}
|
|
18831
19449
|
/**
|
|
18832
19450
|
* Build the publish queue (simplified wrapper for pipeline).
|
|
@@ -18838,7 +19456,7 @@ var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
|
|
|
18838
19456
|
};
|
|
18839
19457
|
|
|
18840
19458
|
// src/L6-pipeline/pipeline.ts
|
|
18841
|
-
|
|
19459
|
+
init_types2();
|
|
18842
19460
|
async function runStage(stageName, fn, stageResults) {
|
|
18843
19461
|
const start = Date.now();
|
|
18844
19462
|
if (progressEmitter.isEnabled()) {
|
|
@@ -18892,11 +19510,22 @@ async function runStage(stageName, fn, stageResults) {
|
|
|
18892
19510
|
return void 0;
|
|
18893
19511
|
}
|
|
18894
19512
|
}
|
|
18895
|
-
async function processVideo(videoPath, ideas, publishBy) {
|
|
19513
|
+
async function processVideo(videoPath, ideas, publishBy, spec) {
|
|
18896
19514
|
const pipelineStart = Date.now();
|
|
18897
19515
|
const stageResults = [];
|
|
18898
19516
|
const cfg = getConfig();
|
|
18899
19517
|
let stagesSkipped = 0;
|
|
19518
|
+
const skipFlags = {
|
|
19519
|
+
SKIP_SILENCE_REMOVAL: cfg.SKIP_SILENCE_REMOVAL,
|
|
19520
|
+
SKIP_VISUAL_ENHANCEMENT: cfg.SKIP_VISUAL_ENHANCEMENT,
|
|
19521
|
+
SKIP_CAPTIONS: cfg.SKIP_CAPTIONS,
|
|
19522
|
+
SKIP_INTRO_OUTRO: cfg.SKIP_INTRO_OUTRO,
|
|
19523
|
+
SKIP_SHORTS: cfg.SKIP_SHORTS,
|
|
19524
|
+
SKIP_MEDIUM_CLIPS: cfg.SKIP_MEDIUM_CLIPS,
|
|
19525
|
+
SKIP_SOCIAL: cfg.SKIP_SOCIAL,
|
|
19526
|
+
SKIP_SOCIAL_PUBLISH: cfg.SKIP_SOCIAL_PUBLISH
|
|
19527
|
+
};
|
|
19528
|
+
const effectiveSpec = spec ? applySkipFlags(spec, skipFlags) : resolveFromFlags(skipFlags);
|
|
18900
19529
|
costTracker3.reset();
|
|
18901
19530
|
function trackStage(stage, fn) {
|
|
18902
19531
|
costTracker3.setStage(stage);
|
|
@@ -18961,43 +19590,44 @@ async function processVideo(videoPath, ideas, publishBy) {
|
|
|
18961
19590
|
asset.setIdeas(ideas);
|
|
18962
19591
|
logger_default.info(`Pipeline using ${ideas.length} idea(s) for editorial direction`);
|
|
18963
19592
|
}
|
|
19593
|
+
asset.setSpec(effectiveSpec);
|
|
18964
19594
|
try {
|
|
18965
19595
|
const transcript = await trackStage("transcription" /* Transcription */, () => asset.getTranscript());
|
|
18966
19596
|
let editedVideoPath;
|
|
18967
|
-
if (
|
|
19597
|
+
if (effectiveSpec.processing.silenceRemoval) {
|
|
18968
19598
|
editedVideoPath = await trackStage("silence-removal" /* SilenceRemoval */, () => asset.getEditedVideo());
|
|
18969
19599
|
} else {
|
|
18970
|
-
skipStage("silence-removal" /* SilenceRemoval */, "
|
|
19600
|
+
skipStage("silence-removal" /* SilenceRemoval */, "SPEC_DISABLED");
|
|
18971
19601
|
}
|
|
18972
19602
|
let enhancedVideoPath;
|
|
18973
|
-
if (
|
|
19603
|
+
if (effectiveSpec.processing.visualEnhancement) {
|
|
18974
19604
|
enhancedVideoPath = await trackStage("visual-enhancement" /* VisualEnhancement */, () => asset.getEnhancedVideo());
|
|
18975
19605
|
} else {
|
|
18976
|
-
skipStage("visual-enhancement" /* VisualEnhancement */, "
|
|
19606
|
+
skipStage("visual-enhancement" /* VisualEnhancement */, "SPEC_DISABLED");
|
|
18977
19607
|
}
|
|
18978
19608
|
let captions;
|
|
18979
|
-
if (
|
|
19609
|
+
if (effectiveSpec.processing.captions) {
|
|
18980
19610
|
captions = await trackStage("captions" /* Captions */, () => asset.getCaptions());
|
|
18981
19611
|
} else {
|
|
18982
|
-
skipStage("captions" /* Captions */, "
|
|
19612
|
+
skipStage("captions" /* Captions */, "SPEC_DISABLED");
|
|
18983
19613
|
}
|
|
18984
19614
|
let captionedVideoPath;
|
|
18985
|
-
if (
|
|
19615
|
+
if (effectiveSpec.processing.captions) {
|
|
18986
19616
|
captionedVideoPath = await trackStage("caption-burn" /* CaptionBurn */, () => asset.getCaptionedVideo());
|
|
18987
19617
|
} else {
|
|
18988
|
-
skipStage("caption-burn" /* CaptionBurn */, "
|
|
19618
|
+
skipStage("caption-burn" /* CaptionBurn */, "SPEC_DISABLED");
|
|
18989
19619
|
}
|
|
18990
19620
|
let introOutroVideoPath;
|
|
18991
|
-
if (
|
|
19621
|
+
if (effectiveSpec.processing.introOutro) {
|
|
18992
19622
|
introOutroVideoPath = await trackStage("intro-outro" /* IntroOutro */, () => asset.getIntroOutroVideo());
|
|
18993
19623
|
} else {
|
|
18994
|
-
skipStage("intro-outro" /* IntroOutro */, "
|
|
19624
|
+
skipStage("intro-outro" /* IntroOutro */, "SPEC_DISABLED");
|
|
18995
19625
|
}
|
|
18996
19626
|
let shorts = [];
|
|
18997
|
-
if (
|
|
19627
|
+
if (effectiveSpec.clips.shorts.enabled) {
|
|
18998
19628
|
const shortAssets = await trackStage("shorts" /* Shorts */, async () => {
|
|
18999
19629
|
const assets = await asset.getShorts();
|
|
19000
|
-
if (
|
|
19630
|
+
if (effectiveSpec.processing.introOutro) {
|
|
19001
19631
|
for (const shortAsset of assets) {
|
|
19002
19632
|
const introOutroPath = await shortAsset.getIntroOutroVideo();
|
|
19003
19633
|
if (introOutroPath !== shortAsset.clip.outputPath) {
|
|
@@ -19024,13 +19654,13 @@ async function processVideo(videoPath, ideas, publishBy) {
|
|
|
19024
19654
|
}
|
|
19025
19655
|
}
|
|
19026
19656
|
} else {
|
|
19027
|
-
skipStage("shorts" /* Shorts */, "
|
|
19657
|
+
skipStage("shorts" /* Shorts */, "SPEC_DISABLED");
|
|
19028
19658
|
}
|
|
19029
19659
|
let mediumClips = [];
|
|
19030
|
-
if (
|
|
19660
|
+
if (effectiveSpec.clips.medium.enabled) {
|
|
19031
19661
|
const mediumAssets = await trackStage("medium-clips" /* MediumClips */, async () => {
|
|
19032
19662
|
const assets = await asset.getMediumClips();
|
|
19033
|
-
if (
|
|
19663
|
+
if (effectiveSpec.processing.introOutro) {
|
|
19034
19664
|
for (const clipAsset of assets) {
|
|
19035
19665
|
const introOutroPath = await clipAsset.getIntroOutroVideo();
|
|
19036
19666
|
if (introOutroPath !== clipAsset.clip.outputPath) {
|
|
@@ -19050,7 +19680,7 @@ async function processVideo(videoPath, ideas, publishBy) {
|
|
|
19050
19680
|
}
|
|
19051
19681
|
}
|
|
19052
19682
|
} else {
|
|
19053
|
-
skipStage("medium-clips" /* MediumClips */, "
|
|
19683
|
+
skipStage("medium-clips" /* MediumClips */, "SPEC_DISABLED");
|
|
19054
19684
|
}
|
|
19055
19685
|
const chapters = await trackStage("chapters" /* Chapters */, () => asset.getChapters());
|
|
19056
19686
|
const summary = await trackStage("summary" /* Summary */, () => asset.getSummary());
|
|
@@ -19069,7 +19699,7 @@ async function processVideo(videoPath, ideas, publishBy) {
|
|
|
19069
19699
|
skipStage("idea-discovery" /* IdeaDiscovery */, "NO_CLIPS");
|
|
19070
19700
|
}
|
|
19071
19701
|
let socialPosts = [];
|
|
19072
|
-
if (
|
|
19702
|
+
if (effectiveSpec.distribution.enabled) {
|
|
19073
19703
|
const mainPosts = await trackStage("social-media" /* SocialMedia */, () => asset.getSocialPosts()) ?? [];
|
|
19074
19704
|
socialPosts.push(...mainPosts);
|
|
19075
19705
|
if (shorts.length > 0) {
|
|
@@ -19093,14 +19723,14 @@ async function processVideo(videoPath, ideas, publishBy) {
|
|
|
19093
19723
|
skipStage("medium-clip-posts" /* MediumClipPosts */, "NO_MEDIUM_CLIPS");
|
|
19094
19724
|
}
|
|
19095
19725
|
} else {
|
|
19096
|
-
skipStage("social-media" /* SocialMedia */, "
|
|
19097
|
-
skipStage("short-posts" /* ShortPosts */, "
|
|
19098
|
-
skipStage("medium-clip-posts" /* MediumClipPosts */, "
|
|
19726
|
+
skipStage("social-media" /* SocialMedia */, "SPEC_DISABLED");
|
|
19727
|
+
skipStage("short-posts" /* ShortPosts */, "SPEC_DISABLED");
|
|
19728
|
+
skipStage("medium-clip-posts" /* MediumClipPosts */, "SPEC_DISABLED");
|
|
19099
19729
|
}
|
|
19100
|
-
if (
|
|
19730
|
+
if (effectiveSpec.distribution.publish && socialPosts.length > 0) {
|
|
19101
19731
|
await trackStage("queue-build" /* QueueBuild */, () => asset.buildQueue(shorts, mediumClips, socialPosts, introOutroVideoPath ?? captionedVideoPath));
|
|
19102
|
-
} else if (
|
|
19103
|
-
skipStage("queue-build" /* QueueBuild */, "
|
|
19732
|
+
} else if (!effectiveSpec.distribution.publish) {
|
|
19733
|
+
skipStage("queue-build" /* QueueBuild */, "SPEC_DISABLED");
|
|
19104
19734
|
} else {
|
|
19105
19735
|
skipStage("queue-build" /* QueueBuild */, "NO_SOCIAL_POSTS");
|
|
19106
19736
|
}
|
|
@@ -19192,13 +19822,13 @@ function generateCostMarkdown(report) {
|
|
|
19192
19822
|
}
|
|
19193
19823
|
return md;
|
|
19194
19824
|
}
|
|
19195
|
-
async function processVideoSafe(videoPath, ideas, publishBy) {
|
|
19825
|
+
async function processVideoSafe(videoPath, ideas, publishBy, spec) {
|
|
19196
19826
|
const filename = basename(videoPath);
|
|
19197
19827
|
const slug = filename.replace(/\.(mp4|mov|webm|avi|mkv)$/i, "");
|
|
19198
19828
|
await markPending3(slug, videoPath);
|
|
19199
19829
|
await markProcessing3(slug);
|
|
19200
19830
|
try {
|
|
19201
|
-
const result = await processVideo(videoPath, ideas, publishBy);
|
|
19831
|
+
const result = await processVideo(videoPath, ideas, publishBy, spec);
|
|
19202
19832
|
await markCompleted3(slug);
|
|
19203
19833
|
return result;
|
|
19204
19834
|
} catch (err) {
|
|
@@ -19811,6 +20441,19 @@ function createVidPipe(sdkConfig) {
|
|
|
19811
20441
|
},
|
|
19812
20442
|
schedule: {
|
|
19813
20443
|
async findNextSlot(platform, clipType, options) {
|
|
20444
|
+
const effectiveClipType = clipType || "short";
|
|
20445
|
+
const queueId = await getQueueId(platform, effectiveClipType);
|
|
20446
|
+
if (queueId) {
|
|
20447
|
+
try {
|
|
20448
|
+
const profileId = await getProfileId();
|
|
20449
|
+
const client = createLateApiClient();
|
|
20450
|
+
const preview = await client.previewQueue(profileId, queueId, 1);
|
|
20451
|
+
if (preview.slots?.length > 0) {
|
|
20452
|
+
return preview.slots[0];
|
|
20453
|
+
}
|
|
20454
|
+
} catch {
|
|
20455
|
+
}
|
|
20456
|
+
}
|
|
19814
20457
|
return await findNextSlot(platform, clipType, {
|
|
19815
20458
|
ideaIds: options?.ideaIds?.map((ideaId) => String(ideaId)),
|
|
19816
20459
|
publishBy: options?.publishBy
|
|
@@ -19910,7 +20553,7 @@ function createVidPipe(sdkConfig) {
|
|
|
19910
20553
|
}
|
|
19911
20554
|
|
|
19912
20555
|
// src/index.ts
|
|
19913
|
-
|
|
20556
|
+
init_types2();
|
|
19914
20557
|
export {
|
|
19915
20558
|
PLATFORM_CHAR_LIMITS,
|
|
19916
20559
|
PipelineStage,
|