@gobing-ai/knowledge-kit 0.0.17 → 0.0.19

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.
Files changed (76) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +74 -0
  3. package/dist/index.js +108 -107
  4. package/package.json +19 -1
  5. package/plugins/generations/content-gen/dist/index.js +42 -94
  6. package/plugins/generations/core-facts-gen/dist/index.js +51 -100
  7. package/plugins/generations/daily-article-gen/dist/index.js +57 -99
  8. package/plugins/generations/daily-article-gen/src/index.ts +12 -4
  9. package/plugins/generations/dailynews-gen/dist/index.js +57 -102
  10. package/plugins/generations/dailynews-gen/src/script-builder.ts +9 -1
  11. package/plugins/generations/episode-plan-gen/dist/index.js +120 -113
  12. package/plugins/generations/episode-plan-gen/src/index.ts +102 -22
  13. package/plugins/generations/image-gen/dist/index.js +63 -115
  14. package/plugins/generations/news-report-gen/dist/index.js +117 -99
  15. package/plugins/generations/news-report-gen/src/index.ts +117 -1
  16. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/pipeline.py +1 -1
  17. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/voicescript.py +8 -0
  18. package/plugins/generations/voice-gen/dist/index.js +161 -120
  19. package/plugins/generations/voice-gen/src/concat.ts +34 -0
  20. package/plugins/generations/voice-gen/src/index.ts +14 -4
  21. package/plugins/generations/voice-gen/src/voicebox-server.ts +91 -0
  22. package/plugins/generations/voice-gen/src/voicescript.ts +8 -0
  23. package/plugins/ingestions/aihot-ingest/dist/index.js +47 -97
  24. package/plugins/ingestions/horizon-ingest/dist/index.js +47 -96
  25. package/plugins/ingestions/karakeep-local/dist/index.js +217 -266
  26. package/plugins/ingestions/last30days-ingest/dist/index.js +42 -91
  27. package/plugins/ingestions/web-search/dist/index.js +68 -117
  28. package/plugins/kk/commands/workflow-run.md +10 -6
  29. package/plugins/kk/plugin.json +1 -1
  30. package/plugins/kk/scripts/kk-workflow-stages.ts +376 -70
  31. package/plugins/kk/skills/publish/SKILL.md +80 -0
  32. package/plugins/kk/workflows/kk-daily-ai-voice.yaml +136 -224
  33. package/plugins/kk/workflows/leftover-publish-fanout-example.yaml +53 -0
  34. package/plugins/publishings/emdash-pub/dist/index.js +42 -91
  35. package/plugins/publishings/infoq-pub/dist/index.js +21994 -0
  36. package/plugins/publishings/infoq-pub/package.json +17 -0
  37. package/plugins/publishings/infoq-pub/plugin.json +7 -0
  38. package/plugins/publishings/infoq-pub/src/index.ts +123 -0
  39. package/plugins/publishings/infoq-pub/tsconfig.json +4 -0
  40. package/plugins/publishings/juejin-pub/dist/index.js +21994 -0
  41. package/plugins/publishings/juejin-pub/package.json +17 -0
  42. package/plugins/publishings/juejin-pub/plugin.json +7 -0
  43. package/plugins/publishings/juejin-pub/src/index.ts +123 -0
  44. package/plugins/publishings/juejin-pub/tsconfig.json +4 -0
  45. package/plugins/publishings/medium-pub/dist/index.js +22041 -0
  46. package/plugins/publishings/medium-pub/package.json +17 -0
  47. package/plugins/publishings/medium-pub/plugin.json +7 -0
  48. package/plugins/publishings/medium-pub/src/index.ts +215 -0
  49. package/plugins/publishings/medium-pub/tsconfig.json +4 -0
  50. package/plugins/publishings/podcast-pub/dist/index.js +183 -123
  51. package/plugins/publishings/podcast-pub/src/index.ts +47 -3
  52. package/plugins/publishings/podcast-pub/src/show-notes.ts +153 -34
  53. package/plugins/publishings/qiita-pub/dist/index.js +42 -91
  54. package/plugins/publishings/substack-pub/dist/index.js +21994 -0
  55. package/plugins/publishings/substack-pub/package.json +17 -0
  56. package/plugins/publishings/substack-pub/plugin.json +7 -0
  57. package/plugins/publishings/substack-pub/src/index.ts +123 -0
  58. package/plugins/publishings/substack-pub/tsconfig.json +4 -0
  59. package/plugins/publishings/surfdash-pub/dist/index.js +172 -103
  60. package/plugins/publishings/surfdash-pub/src/index.ts +32 -2
  61. package/plugins/publishings/wechatmp-pub/dist/index.js +22119 -0
  62. package/plugins/publishings/wechatmp-pub/package.json +17 -0
  63. package/plugins/publishings/wechatmp-pub/plugin.json +7 -0
  64. package/plugins/publishings/wechatmp-pub/src/index.ts +304 -0
  65. package/plugins/publishings/wechatmp-pub/tsconfig.json +4 -0
  66. package/plugins/publishings/x-pub/dist/index.js +22110 -0
  67. package/plugins/publishings/x-pub/package.json +17 -0
  68. package/plugins/publishings/x-pub/plugin.json +7 -0
  69. package/plugins/publishings/x-pub/src/index.ts +222 -0
  70. package/plugins/publishings/x-pub/tsconfig.json +4 -0
  71. package/plugins/publishings/xhs-pub/dist/index.js +21994 -0
  72. package/plugins/publishings/xhs-pub/package.json +17 -0
  73. package/plugins/publishings/xhs-pub/plugin.json +7 -0
  74. package/plugins/publishings/xhs-pub/src/index.ts +123 -0
  75. package/plugins/publishings/xhs-pub/tsconfig.json +4 -0
  76. package/plugins/publishings/zenn-pub/dist/index.js +42 -91
@@ -14642,72 +14642,6 @@ var init_zod = __esm(() => {
14642
14642
  init_external();
14643
14643
  });
14644
14644
 
14645
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.39/node_modules/@gobing-ai/ts-utils/dist/errors.js
14646
- var ErrorCode;
14647
- var init_errors3 = __esm(() => {
14648
- ErrorCode = {
14649
- NotFound: "NOT_FOUND",
14650
- Validation: "VALIDATION",
14651
- Conflict: "CONFLICT",
14652
- Internal: "INTERNAL"
14653
- };
14654
- });
14655
-
14656
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.39/node_modules/@gobing-ai/ts-utils/dist/api-response.js
14657
- var API_ERROR_CODES, ERROR_CODE_TO_HTTP, CLIENT_SAFE_CODES;
14658
- var init_api_response = __esm(() => {
14659
- init_errors3();
14660
- API_ERROR_CODES = {
14661
- SUCCESS: 0,
14662
- NOT_FOUND: 404,
14663
- VALIDATION_ERROR: 422,
14664
- BAD_REQUEST: 400,
14665
- UNAUTHORIZED: 401,
14666
- FORBIDDEN: 403,
14667
- CONFLICT: 409,
14668
- INTERNAL_ERROR: 500
14669
- };
14670
- ERROR_CODE_TO_HTTP = {
14671
- [ErrorCode.NotFound]: API_ERROR_CODES.NOT_FOUND,
14672
- [ErrorCode.Validation]: API_ERROR_CODES.VALIDATION_ERROR,
14673
- [ErrorCode.Conflict]: API_ERROR_CODES.CONFLICT,
14674
- [ErrorCode.Internal]: API_ERROR_CODES.INTERNAL_ERROR
14675
- };
14676
- CLIENT_SAFE_CODES = new Set([ErrorCode.NotFound, ErrorCode.Validation, ErrorCode.Conflict]);
14677
- });
14678
-
14679
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.39/node_modules/@gobing-ai/ts-utils/dist/cursor.js
14680
- var init_cursor = () => {};
14681
-
14682
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.39/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
14683
- var init_event_severity = () => {};
14684
-
14685
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.39/node_modules/@gobing-ai/ts-utils/dist/output.js
14686
- function processStream(name) {
14687
- const proc = globalThis.process;
14688
- const stream = proc?.[name];
14689
- if (stream === undefined) {
14690
- throw new Error(`No ${name} target available: set one via setDefaultOutputTargets or pass an explicit target`);
14691
- }
14692
- return stream;
14693
- }
14694
- function writeLine(message, target) {
14695
- target.write(`${message}
14696
- `);
14697
- }
14698
- function echoError(message, target = defaultStderrTarget ?? processStream("stderr")) {
14699
- writeLine(message, target);
14700
- }
14701
- var defaultStderrTarget;
14702
-
14703
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.39/node_modules/@gobing-ai/ts-utils/dist/index.js
14704
- var init_dist = __esm(() => {
14705
- init_api_response();
14706
- init_cursor();
14707
- init_errors3();
14708
- init_event_severity();
14709
- });
14710
-
14711
14645
  // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js
14712
14646
  var require_identity = __commonJS((exports) => {
14713
14647
  var ALIAS = Symbol.for("yaml.alias");
@@ -17612,15 +17546,15 @@ var require_timestamp = __commonJS((exports) => {
17612
17546
  throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd");
17613
17547
  const [, year, month, day, hour, minute, second] = match.map(Number);
17614
17548
  const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0;
17615
- let date6 = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec);
17549
+ let date5 = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec);
17616
17550
  const tz = match[8];
17617
17551
  if (tz && tz !== "Z") {
17618
17552
  let d = parseSexagesimal(tz, false);
17619
17553
  if (Math.abs(d) < 30)
17620
17554
  d *= 60;
17621
- date6 -= 60000 * d;
17555
+ date5 -= 60000 * d;
17622
17556
  }
17623
- return new Date(date6);
17557
+ return new Date(date5);
17624
17558
  },
17625
17559
  stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? ""
17626
17560
  };
@@ -19480,7 +19414,7 @@ var require_composer = __commonJS((exports) => {
19480
19414
  var node_process = __require("process");
19481
19415
  var directives = require_directives();
19482
19416
  var Document = require_Document();
19483
- var errors4 = require_errors();
19417
+ var errors3 = require_errors();
19484
19418
  var identity = require_identity();
19485
19419
  var composeDoc = require_compose_doc();
19486
19420
  var resolveEnd = require_resolve_end();
@@ -19531,9 +19465,9 @@ var require_composer = __commonJS((exports) => {
19531
19465
  this.onError = (source, code, message, warning) => {
19532
19466
  const pos = getErrorPos(source);
19533
19467
  if (warning)
19534
- this.warnings.push(new errors4.YAMLWarning(pos, code, message));
19468
+ this.warnings.push(new errors3.YAMLWarning(pos, code, message));
19535
19469
  else
19536
- this.errors.push(new errors4.YAMLParseError(pos, code, message));
19470
+ this.errors.push(new errors3.YAMLParseError(pos, code, message));
19537
19471
  };
19538
19472
  this.directives = new directives.Directives({ version: options.version || "1.2" });
19539
19473
  this.options = options;
@@ -19619,7 +19553,7 @@ ${cb}` : comment;
19619
19553
  break;
19620
19554
  case "error": {
19621
19555
  const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message;
19622
- const error51 = new errors4.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg);
19556
+ const error51 = new errors3.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg);
19623
19557
  if (this.atDirectives || !this.doc)
19624
19558
  this.errors.push(error51);
19625
19559
  else
@@ -19629,7 +19563,7 @@ ${cb}` : comment;
19629
19563
  case "doc-end": {
19630
19564
  if (!this.doc) {
19631
19565
  const msg = "Unexpected doc-end without preceding document";
19632
- this.errors.push(new errors4.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg));
19566
+ this.errors.push(new errors3.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg));
19633
19567
  break;
19634
19568
  }
19635
19569
  this.doc.directives.docEnd = true;
@@ -19644,7 +19578,7 @@ ${end.comment}` : end.comment;
19644
19578
  break;
19645
19579
  }
19646
19580
  default:
19647
- this.errors.push(new errors4.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`));
19581
+ this.errors.push(new errors3.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`));
19648
19582
  }
19649
19583
  }
19650
19584
  *end(forceDoc = false, endOffset = -1) {
@@ -19670,7 +19604,7 @@ ${end.comment}` : end.comment;
19670
19604
  var require_cst_scalar = __commonJS((exports) => {
19671
19605
  var resolveBlockScalar = require_resolve_block_scalar();
19672
19606
  var resolveFlowScalar = require_resolve_flow_scalar();
19673
- var errors4 = require_errors();
19607
+ var errors3 = require_errors();
19674
19608
  var stringifyString = require_stringifyString();
19675
19609
  function resolveAsScalar(token, strict = true, onError) {
19676
19610
  if (token) {
@@ -19679,7 +19613,7 @@ var require_cst_scalar = __commonJS((exports) => {
19679
19613
  if (onError)
19680
19614
  onError(offset, code, message);
19681
19615
  else
19682
- throw new errors4.YAMLParseError([offset, offset + 1], code, message);
19616
+ throw new errors3.YAMLParseError([offset, offset + 1], code, message);
19683
19617
  };
19684
19618
  switch (token.type) {
19685
19619
  case "scalar":
@@ -21559,7 +21493,7 @@ var require_parser = __commonJS((exports) => {
21559
21493
  var require_public_api = __commonJS((exports) => {
21560
21494
  var composer = require_composer();
21561
21495
  var Document = require_Document();
21562
- var errors4 = require_errors();
21496
+ var errors3 = require_errors();
21563
21497
  var log = require_log();
21564
21498
  var identity = require_identity();
21565
21499
  var lineCounter = require_line_counter();
@@ -21576,8 +21510,8 @@ var require_public_api = __commonJS((exports) => {
21576
21510
  const docs = Array.from(composer$1.compose(parser$1.parse(source)));
21577
21511
  if (prettyErrors && lineCounter2)
21578
21512
  for (const doc2 of docs) {
21579
- doc2.errors.forEach(errors4.prettifyError(source, lineCounter2));
21580
- doc2.warnings.forEach(errors4.prettifyError(source, lineCounter2));
21513
+ doc2.errors.forEach(errors3.prettifyError(source, lineCounter2));
21514
+ doc2.warnings.forEach(errors3.prettifyError(source, lineCounter2));
21581
21515
  }
21582
21516
  if (docs.length > 0)
21583
21517
  return docs;
@@ -21592,13 +21526,13 @@ var require_public_api = __commonJS((exports) => {
21592
21526
  if (!doc2)
21593
21527
  doc2 = _doc;
21594
21528
  else if (doc2.options.logLevel !== "silent") {
21595
- doc2.errors.push(new errors4.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()"));
21529
+ doc2.errors.push(new errors3.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()"));
21596
21530
  break;
21597
21531
  }
21598
21532
  }
21599
21533
  if (prettyErrors && lineCounter2) {
21600
- doc2.errors.forEach(errors4.prettifyError(source, lineCounter2));
21601
- doc2.warnings.forEach(errors4.prettifyError(source, lineCounter2));
21534
+ doc2.errors.forEach(errors3.prettifyError(source, lineCounter2));
21535
+ doc2.warnings.forEach(errors3.prettifyError(source, lineCounter2));
21602
21536
  }
21603
21537
  return doc2;
21604
21538
  }
@@ -21650,12 +21584,12 @@ var require_public_api = __commonJS((exports) => {
21650
21584
  });
21651
21585
 
21652
21586
  // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/index.js
21653
- var composer, Document, Schema, errors4, Alias, identity, Pair, Scalar, YAMLMap, YAMLSeq, cst, lexer, lineCounter, parser, publicApi, visit, $Composer, $Document, $Schema, $YAMLError, $YAMLParseError, $YAMLWarning, $Alias, $isAlias, $isCollection, $isDocument, $isMap, $isNode, $isPair, $isScalar, $isSeq, $Pair, $Scalar, $YAMLMap, $YAMLSeq, $Lexer, $LineCounter, $Parser, $parse, $parseAllDocuments, $parseDocument, $stringify, $visit, $visitAsync;
21654
- var init_dist2 = __esm(() => {
21587
+ var composer, Document, Schema, errors3, Alias, identity, Pair, Scalar, YAMLMap, YAMLSeq, cst, lexer, lineCounter, parser, publicApi, visit, $Composer, $Document, $Schema, $YAMLError, $YAMLParseError, $YAMLWarning, $Alias, $isAlias, $isCollection, $isDocument, $isMap, $isNode, $isPair, $isScalar, $isSeq, $Pair, $Scalar, $YAMLMap, $YAMLSeq, $Lexer, $LineCounter, $Parser, $parse, $parseAllDocuments, $parseDocument, $stringify, $visit, $visitAsync;
21588
+ var init_dist = __esm(() => {
21655
21589
  composer = require_composer();
21656
21590
  Document = require_Document();
21657
21591
  Schema = require_Schema();
21658
- errors4 = require_errors();
21592
+ errors3 = require_errors();
21659
21593
  Alias = require_Alias();
21660
21594
  identity = require_identity();
21661
21595
  Pair = require_Pair();
@@ -21671,9 +21605,9 @@ var init_dist2 = __esm(() => {
21671
21605
  $Composer = composer.Composer;
21672
21606
  $Document = Document.Document;
21673
21607
  $Schema = Schema.Schema;
21674
- $YAMLError = errors4.YAMLError;
21675
- $YAMLParseError = errors4.YAMLParseError;
21676
- $YAMLWarning = errors4.YAMLWarning;
21608
+ $YAMLError = errors3.YAMLError;
21609
+ $YAMLParseError = errors3.YAMLParseError;
21610
+ $YAMLWarning = errors3.YAMLWarning;
21677
21611
  $Alias = Alias.Alias;
21678
21612
  $isAlias = identity.isAlias;
21679
21613
  $isCollection = identity.isCollection;
@@ -21701,7 +21635,7 @@ var init_dist2 = __esm(() => {
21701
21635
  // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.39/node_modules/@gobing-ai/ts-runtime/dist/config.js
21702
21636
  var configSchema;
21703
21637
  var init_config = __esm(() => {
21704
- init_dist2();
21638
+ init_dist();
21705
21639
  init_zod();
21706
21640
  configSchema = exports_external.object({
21707
21641
  app: exports_external.object({
@@ -21810,7 +21744,7 @@ function findProjectRoot(startDir) {
21810
21744
  var init_file_system_node = () => {};
21811
21745
 
21812
21746
  // ../../plugins/generations/voice-gen/src/index.ts
21813
- import { basename, dirname as dirname2, extname, join, resolve } from "path";
21747
+ import { basename, dirname as dirname2, extname, join as join2, resolve } from "path";
21814
21748
  import { parseArgs } from "util";
21815
21749
 
21816
21750
  // ../../packages/kk-core/src/kinds.ts
@@ -21887,11 +21821,26 @@ var PluginManifestSchema = exports_external.object({
21887
21821
  init_file_system_node();
21888
21822
  init_config();
21889
21823
 
21824
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.67/node_modules/@gobing-ai/ts-utils/dist/output.js
21825
+ var defaultStderrTarget;
21826
+ function processStream(name) {
21827
+ const proc = globalThis.process;
21828
+ const stream = proc?.[name];
21829
+ if (stream === undefined) {
21830
+ throw new Error(`No ${name} target available: set one via setDefaultOutputTargets or pass an explicit target`);
21831
+ }
21832
+ return stream;
21833
+ }
21834
+ function writeLine(message, target) {
21835
+ target.write(`${message}
21836
+ `);
21837
+ }
21838
+ function echoError(message, target = defaultStderrTarget ?? processStream("stderr")) {
21839
+ writeLine(message, target);
21840
+ }
21841
+
21890
21842
  // ../../packages/kk-core/src/render-md.ts
21891
21843
  if (false) {}
21892
- // ../../plugins/generations/voice-gen/src/index.ts
21893
- init_dist();
21894
-
21895
21844
  // ../../plugins/generations/voice-gen/src/concat.ts
21896
21845
  function parseWav(buffer) {
21897
21846
  if (buffer.length < 44) {
@@ -22007,21 +21956,21 @@ function concatWavs(wavBuffers, gapsMs = []) {
22007
21956
  }
22008
21957
  const headerSize = 44;
22009
21958
  const totalFileSize = headerSize + totalDataLength;
22010
- const output2 = new Uint8Array(totalFileSize);
22011
- const view = new DataView(output2.buffer, output2.byteOffset, output2.byteLength);
22012
- output2[0] = 82;
22013
- output2[1] = 73;
22014
- output2[2] = 70;
22015
- output2[3] = 70;
21959
+ const output = new Uint8Array(totalFileSize);
21960
+ const view = new DataView(output.buffer, output.byteOffset, output.byteLength);
21961
+ output[0] = 82;
21962
+ output[1] = 73;
21963
+ output[2] = 70;
21964
+ output[3] = 70;
22016
21965
  view.setUint32(4, 36 + totalDataLength, true);
22017
- output2[8] = 87;
22018
- output2[9] = 65;
22019
- output2[10] = 86;
22020
- output2[11] = 69;
22021
- output2[12] = 102;
22022
- output2[13] = 109;
22023
- output2[14] = 116;
22024
- output2[15] = 32;
21966
+ output[8] = 87;
21967
+ output[9] = 65;
21968
+ output[10] = 86;
21969
+ output[11] = 69;
21970
+ output[12] = 102;
21971
+ output[13] = 109;
21972
+ output[14] = 116;
21973
+ output[15] = 32;
22025
21974
  view.setUint32(16, 16, true);
22026
21975
  view.setUint16(20, first.audioFormat, true);
22027
21976
  view.setUint16(22, first.numChannels, true);
@@ -22029,17 +21978,44 @@ function concatWavs(wavBuffers, gapsMs = []) {
22029
21978
  view.setUint32(28, first.byteRate, true);
22030
21979
  view.setUint16(32, first.blockAlign, true);
22031
21980
  view.setUint16(34, first.bitsPerSample, true);
22032
- output2[36] = 100;
22033
- output2[37] = 97;
22034
- output2[38] = 116;
22035
- output2[39] = 97;
21981
+ output[36] = 100;
21982
+ output[37] = 97;
21983
+ output[38] = 116;
21984
+ output[39] = 97;
22036
21985
  view.setUint32(40, totalDataLength, true);
22037
21986
  let currentOffset = headerSize;
22038
21987
  for (const piece of pieces) {
22039
- output2.set(piece, currentOffset);
21988
+ output.set(piece, currentOffset);
22040
21989
  currentOffset += piece.length;
22041
21990
  }
22042
- return output2;
21991
+ return output;
21992
+ }
21993
+ function applySpeed(wav, speed) {
21994
+ if (speed === 1)
21995
+ return wav;
21996
+ const proc = Bun.spawnSync([
21997
+ "ffmpeg",
21998
+ "-hide_banner",
21999
+ "-loglevel",
22000
+ "error",
22001
+ "-f",
22002
+ "wav",
22003
+ "-i",
22004
+ "pipe:0",
22005
+ "-filter:a",
22006
+ `atempo=${speed}`,
22007
+ "-f",
22008
+ "wav",
22009
+ "pipe:1"
22010
+ ], {
22011
+ stdin: Buffer.from(wav),
22012
+ stdout: "pipe",
22013
+ stderr: "pipe"
22014
+ });
22015
+ if (proc.exitCode !== 0 || !proc.stdout) {
22016
+ throw new Error(`ffmpeg atempo failed (exit ${proc.exitCode}): ${proc.stderr?.toString().trim()}`);
22017
+ }
22018
+ return new Uint8Array(proc.stdout);
22043
22019
  }
22044
22020
 
22045
22021
  // ../../plugins/generations/voice-gen/src/mp3.ts
@@ -22437,6 +22413,60 @@ function createVoiceboxClient(options = {}) {
22437
22413
  };
22438
22414
  }
22439
22415
 
22416
+ // ../../plugins/generations/voice-gen/src/voicebox-server.ts
22417
+ import { spawn as nodeSpawn } from "child_process";
22418
+ import { existsSync as existsSync2, openSync } from "fs";
22419
+ import { homedir } from "os";
22420
+ import { join } from "path";
22421
+ var BOOT_WAIT_MS = 30000;
22422
+ var POLL_MS = 500;
22423
+ var LOG_PATH = "/tmp/voicebox-server.log";
22424
+ async function probeVoiceboxHealth(url2, customFetch) {
22425
+ try {
22426
+ return (await customFetch(`${url2}/health`)).ok;
22427
+ } catch {
22428
+ return false;
22429
+ }
22430
+ }
22431
+ async function ensureVoiceboxRunning(options = {}) {
22432
+ const rawUrl = options.url ?? process.env.VOICEBOX_URL ?? "http://127.0.0.1:17493";
22433
+ const customFetch = options.fetch ?? fetch;
22434
+ const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
22435
+ const customSpawn = options.spawn ?? nodeSpawn;
22436
+ if (await probeVoiceboxHealth(rawUrl, customFetch)) {
22437
+ return;
22438
+ }
22439
+ const url2 = new URL(rawUrl);
22440
+ const local = ["127.0.0.1", "localhost", "::1"].includes(url2.hostname);
22441
+ if (!local) {
22442
+ throw new Error(`voice-gen: Voicebox not reachable at ${rawUrl} \u2014 auto-start only applies to local URLs; is the remote server up? (fix VOICEBOX_URL)`);
22443
+ }
22444
+ const serverBin = options.serverBin ?? process.env.VOICEBOX_SERVER_BIN ?? "/Applications/Voicebox.app/Contents/MacOS/voicebox-server";
22445
+ if (!existsSync2(serverBin)) {
22446
+ throw new Error(`voice-gen: Voicebox not reachable at ${rawUrl} and VOICEBOX_SERVER_BIN is missing (${serverBin}) \u2014 start Voicebox.app or set VOICEBOX_SERVER_BIN`);
22447
+ }
22448
+ const dataDir = options.dataDir ?? process.env.VOICEBOX_DATA_DIR ?? join(homedir(), "Library/Application Support/sh.voicebox.app");
22449
+ const logPath = options.logPath ?? LOG_PATH;
22450
+ const port = url2.port === "" ? 17493 : Number(url2.port);
22451
+ echoError(`voice-gen: Voicebox not reachable at ${rawUrl} \u2014 auto-starting ${serverBin} (log: ${logPath})`);
22452
+ const child = customSpawn(serverBin, ["--port", String(port), "--data-dir", dataDir], {
22453
+ detached: true,
22454
+ stdio: ["ignore", "ignore", openSync(logPath, "a")]
22455
+ });
22456
+ child.unref?.();
22457
+ const bootWaitMs = options.bootWaitMs ?? BOOT_WAIT_MS;
22458
+ const pollMs = options.pollMs ?? POLL_MS;
22459
+ const deadline = Date.now() + bootWaitMs;
22460
+ while (Date.now() < deadline) {
22461
+ await sleep(pollMs);
22462
+ if (await probeVoiceboxHealth(rawUrl, customFetch)) {
22463
+ echoError(`voice-gen: Voicebox ready at ${rawUrl}`);
22464
+ return;
22465
+ }
22466
+ }
22467
+ throw new Error(`voice-gen: Voicebox did not become healthy at ${rawUrl} within ${bootWaitMs}ms of auto-start \u2014 check ${logPath}`);
22468
+ }
22469
+
22440
22470
  // ../../plugins/generations/voice-gen/src/voicescript.ts
22441
22471
  var VOICEBOX_TEXT_MAX = 50000;
22442
22472
  var VOICEBOX_INSTRUCT_MAX = 500;
@@ -22817,6 +22847,11 @@ function validateVoiceScript(script) {
22817
22847
  throw new Error("gap_ms must be non-negative");
22818
22848
  }
22819
22849
  }
22850
+ if (segment.speed !== undefined) {
22851
+ if (typeof segment.speed !== "number" || segment.speed < 0.5 || segment.speed > 2) {
22852
+ throw new Error("speed must be between 0.5 and 2.0");
22853
+ }
22854
+ }
22820
22855
  if (segment.seed !== undefined) {
22821
22856
  if (typeof segment.seed !== "number" || !Number.isInteger(segment.seed) || segment.seed < 0) {
22822
22857
  throw new Error("seed must be a non-negative integer");
@@ -22840,8 +22875,8 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
22840
22875
  const fs = createNodeFileSystem();
22841
22876
  const outDir = dirname2(outputPath);
22842
22877
  const outStem = basename(outputPath, extname(outputPath));
22843
- const audioPath = resolve(join(outDir, `${outStem}.wav`));
22844
- const mp3Path = resolve(join(outDir, `${outStem}.mp3`));
22878
+ const audioPath = resolve(join2(outDir, `${outStem}.wav`));
22879
+ const mp3Path = resolve(join2(outDir, `${outStem}.mp3`));
22845
22880
  const wantMp3 = isMp3Requested();
22846
22881
  await fs.deleteFile(outputPath);
22847
22882
  await fs.deleteFile(audioPath);
@@ -22859,6 +22894,9 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
22859
22894
  return;
22860
22895
  }
22861
22896
  const client = clientOverride ?? createVoiceboxClient();
22897
+ if (!clientOverride) {
22898
+ await ensureVoiceboxRunning();
22899
+ }
22862
22900
  try {
22863
22901
  await client.health();
22864
22902
  const envDefaultProfile = process.env.VOICEBOX_DEFAULT_PROFILE;
@@ -22979,18 +23017,20 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
22979
23017
  throw lastError instanceof Error ? lastError : new Error(String(lastError ?? "no audio"));
22980
23018
  }
22981
23019
  const gap = segment.gap_ms ?? 0;
22982
- segmentWavs.push(wavBytes);
23020
+ const speed = segment.speed ?? 1;
23021
+ segmentWavs.push(applySpeed(wavBytes, speed));
22983
23022
  segmentGaps.push(gap);
22984
- totalDuration += segDuration + gap / 1000;
23023
+ totalDuration += segDuration / speed + gap / 1000;
22985
23024
  segmentMetadata.push({
22986
23025
  generationId,
22987
23026
  profile: resolvedProfile.name,
22988
23027
  speaker: segment.speaker,
22989
23028
  emotion: rawEmotion,
22990
- duration: segDuration,
23029
+ duration: segDuration / speed,
22991
23030
  verifyRetries,
22992
23031
  transcription,
22993
- loudnessDip
23032
+ loudnessDip,
23033
+ ...speed !== 1 ? { speed } : {}
22994
23034
  });
22995
23035
  echoError(`voice-gen: segment ${segIndex + 1}/${combinedScript.segments.length} done in ${((Date.now() - segStartedAt) / 1000).toFixed(1)}s${verifyRetries ? ` (${verifyRetries} verify ${verifyRetries === 1 ? "retry" : "retries"})` : ""}`);
22996
23036
  }
@@ -23071,6 +23111,7 @@ export {
23071
23111
  concatWavs,
23072
23112
  computeExpectedDurationRange,
23073
23113
  auditVoiceSegments,
23114
+ applySpeed,
23074
23115
  VOICEBOX_TEXT_MAX,
23075
23116
  VOICEBOX_LANGUAGES,
23076
23117
  VOICEBOX_INSTRUCT_MAX,
@@ -218,3 +218,37 @@ export function concatWavs(wavBuffers: Uint8Array[], gapsMs: number[] = []): Uin
218
218
 
219
219
  return output;
220
220
  }
221
+
222
+ /**
223
+ * Resample a segment's playback rate with ffmpeg `atempo` (in-memory stdin/stdout).
224
+ * Voicebox has no rate parameter, so per-segment `speed` is a local post-step; 1.0 skips.
225
+ */
226
+ export function applySpeed(wav: Uint8Array, speed: number): Uint8Array {
227
+ if (speed === 1) return wav;
228
+ const proc = Bun.spawnSync(
229
+ [
230
+ 'ffmpeg',
231
+ '-hide_banner',
232
+ '-loglevel',
233
+ 'error',
234
+ '-f',
235
+ 'wav',
236
+ '-i',
237
+ 'pipe:0',
238
+ '-filter:a',
239
+ `atempo=${speed}`,
240
+ '-f',
241
+ 'wav',
242
+ 'pipe:1',
243
+ ],
244
+ {
245
+ stdin: Buffer.from(wav),
246
+ stdout: 'pipe',
247
+ stderr: 'pipe',
248
+ },
249
+ );
250
+ if (proc.exitCode !== 0 || !proc.stdout) {
251
+ throw new Error(`ffmpeg atempo failed (exit ${proc.exitCode}): ${proc.stderr?.toString().trim()}`);
252
+ }
253
+ return new Uint8Array(proc.stdout);
254
+ }
@@ -3,10 +3,11 @@ import { parseArgs } from 'node:util';
3
3
  import { type Content, ContentSchema, type Doc, DocListSchema } from '@gobing-ai/kk-core';
4
4
  import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
5
5
  import { echoError } from '@gobing-ai/ts-utils';
6
- import { concatWavs } from './concat';
6
+ import { applySpeed, concatWavs } from './concat';
7
7
  import { isMp3Requested, type Mp3Transcoder, transcodeWavToMp3 } from './mp3';
8
8
  import { auditVoiceSegments, detectLoudnessDip, TRANSCRIPTION_FIDELITY_FLOOR, transcriptionFidelity } from './qc';
9
9
  import { createVoiceboxClient, type VoiceboxClient, type VoiceboxGenerateBody } from './voicebox-client';
10
+ import { ensureVoiceboxRunning } from './voicebox-server';
10
11
  import {
11
12
  mergeVoiceScripts,
12
13
  parseDocToVoiceScript,
@@ -71,6 +72,13 @@ export async function processGeneratorIO(
71
72
 
72
73
  const client = clientOverride ?? createVoiceboxClient();
73
74
 
75
+ // Auto-start the local server when it is down — only on the default client path;
76
+ // tests inject clientOverride and skip the spawn entirely. With vdriver=ominivoice
77
+ // (the default) this plugin never runs, so no server is needed at all.
78
+ if (!clientOverride) {
79
+ await ensureVoiceboxRunning();
80
+ }
81
+
74
82
  try {
75
83
  // 1. Health check
76
84
  await client.health();
@@ -281,20 +289,22 @@ export async function processGeneratorIO(
281
289
  }
282
290
 
283
291
  const gap = segment.gap_ms ?? 0;
292
+ const speed = segment.speed ?? 1;
284
293
 
285
- segmentWavs.push(wavBytes);
294
+ segmentWavs.push(applySpeed(wavBytes, speed));
286
295
  segmentGaps.push(gap);
287
296
 
288
- totalDuration += segDuration + gap / 1000;
297
+ totalDuration += segDuration / speed + gap / 1000;
289
298
  segmentMetadata.push({
290
299
  generationId,
291
300
  profile: resolvedProfile.name,
292
301
  speaker: segment.speaker,
293
302
  emotion: rawEmotion,
294
- duration: segDuration,
303
+ duration: segDuration / speed,
295
304
  verifyRetries,
296
305
  transcription,
297
306
  loudnessDip,
307
+ ...(speed !== 1 ? { speed } : {}),
298
308
  });
299
309
  echoError(
300
310
  `voice-gen: segment ${segIndex + 1}/${combinedScript.segments.length} done in ${((Date.now() - segStartedAt) / 1000).toFixed(1)}s${verifyRetries ? ` (${verifyRetries} verify ${verifyRetries === 1 ? 'retry' : 'retries'})` : ''}`,
@@ -0,0 +1,91 @@
1
+ import { type ChildProcess, spawn as nodeSpawn } from 'node:child_process';
2
+ import { existsSync, openSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { echoError } from '@gobing-ai/ts-utils';
6
+
7
+ /** Boot wait / poll cadence for a freshly spawned local server (Voicebox cold start ≈ 2-5s). */
8
+ const BOOT_WAIT_MS = 30_000;
9
+ const POLL_MS = 500;
10
+ const LOG_PATH = '/tmp/voicebox-server.log';
11
+
12
+ export interface VoiceboxServerOptions {
13
+ url?: string;
14
+ serverBin?: string;
15
+ dataDir?: string;
16
+ logPath?: string;
17
+ bootWaitMs?: number;
18
+ pollMs?: number;
19
+ fetch?: typeof fetch;
20
+ sleep?: (ms: number) => Promise<void>;
21
+ spawn?: typeof nodeSpawn;
22
+ }
23
+
24
+ async function probeVoiceboxHealth(url: string, customFetch: typeof fetch): Promise<boolean> {
25
+ try {
26
+ return (await customFetch(`${url}/health`)).ok;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Ensure a local Voicebox server is reachable: probe `/health`, and when it is down and the
34
+ * URL points at this machine, spawn the app's server binary detached (stderr appended to the
35
+ * log file, survives plugin exit) and poll until healthy — fail loud otherwise. Remote URLs
36
+ * and a missing binary are configuration errors, not auto-start candidates.
37
+ */
38
+ export async function ensureVoiceboxRunning(options: VoiceboxServerOptions = {}): Promise<void> {
39
+ const rawUrl = options.url ?? process.env.VOICEBOX_URL ?? 'http://127.0.0.1:17493';
40
+ const customFetch = options.fetch ?? fetch;
41
+ const sleep = options.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));
42
+ const customSpawn = options.spawn ?? nodeSpawn;
43
+
44
+ if (await probeVoiceboxHealth(rawUrl, customFetch)) {
45
+ return;
46
+ }
47
+
48
+ const url = new URL(rawUrl);
49
+ const local = ['127.0.0.1', 'localhost', '::1'].includes(url.hostname);
50
+ if (!local) {
51
+ throw new Error(
52
+ `voice-gen: Voicebox not reachable at ${rawUrl} — auto-start only applies to local URLs; is the remote server up? (fix VOICEBOX_URL)`,
53
+ );
54
+ }
55
+ const serverBin =
56
+ options.serverBin ??
57
+ process.env.VOICEBOX_SERVER_BIN ??
58
+ '/Applications/Voicebox.app/Contents/MacOS/voicebox-server';
59
+ if (!existsSync(serverBin)) {
60
+ throw new Error(
61
+ `voice-gen: Voicebox not reachable at ${rawUrl} and VOICEBOX_SERVER_BIN is missing (${serverBin}) — start Voicebox.app or set VOICEBOX_SERVER_BIN`,
62
+ );
63
+ }
64
+ const dataDir =
65
+ options.dataDir ??
66
+ process.env.VOICEBOX_DATA_DIR ??
67
+ join(homedir(), 'Library/Application Support/sh.voicebox.app');
68
+ const logPath = options.logPath ?? LOG_PATH;
69
+ const port = url.port === '' ? 17493 : Number(url.port);
70
+
71
+ echoError(`voice-gen: Voicebox not reachable at ${rawUrl} — auto-starting ${serverBin} (log: ${logPath})`);
72
+ const child: ChildProcess = customSpawn(serverBin, ['--port', String(port), '--data-dir', dataDir], {
73
+ detached: true,
74
+ stdio: ['ignore', 'ignore', openSync(logPath, 'a')],
75
+ });
76
+ child.unref?.();
77
+
78
+ const bootWaitMs = options.bootWaitMs ?? BOOT_WAIT_MS;
79
+ const pollMs = options.pollMs ?? POLL_MS;
80
+ const deadline = Date.now() + bootWaitMs;
81
+ while (Date.now() < deadline) {
82
+ await sleep(pollMs);
83
+ if (await probeVoiceboxHealth(rawUrl, customFetch)) {
84
+ echoError(`voice-gen: Voicebox ready at ${rawUrl}`);
85
+ return;
86
+ }
87
+ }
88
+ throw new Error(
89
+ `voice-gen: Voicebox did not become healthy at ${rawUrl} within ${bootWaitMs}ms of auto-start — check ${logPath}`,
90
+ );
91
+ }