@agentproto/corpus-cli 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +482 -50
- package/dist/cli.mjs.map +1 -1
- package/dist/ports/index.d.ts +34 -6
- package/dist/ports/index.mjs +53 -24
- package/dist/ports/index.mjs.map +1 -1
- package/dist/specs/resources/aip-14/draft/TOOL.schema.json +1 -1
- package/dist/specs/resources/aip-45/draft/AGENT-CLI.schema.json +62 -0
- package/dist/specs/resources/aip-52/draft/HARNESS.schema.json +254 -0
- package/package.json +3 -3
package/dist/ports/index.d.ts
CHANGED
|
@@ -133,12 +133,22 @@ declare class OpenAiWhisperStt implements SttPort {
|
|
|
133
133
|
* transcribes it with an injected SttPort (Whisper). Captions-independent
|
|
134
134
|
* and robust: works whether or not the video has subtitles.
|
|
135
135
|
*
|
|
136
|
-
* video URL → yt-dlp -f bestaudio -x (mp3) → SttPort.transcribe → text
|
|
136
|
+
* video URL → yt-dlp -f bestaudio/best -x (mp3) → SttPort.transcribe → text
|
|
137
137
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
138
|
+
* Modern YouTube requires three workarounds beyond a plain yt-dlp call:
|
|
139
|
+
* • `-f bestaudio/best` — many videos no longer expose a standalone
|
|
140
|
+
* bestaudio format; the /best fallback avoids "format not available".
|
|
141
|
+
* • `--remote-components ejs:github` — solves the nsig "n challenge"
|
|
142
|
+
* that otherwise yields "Only images are available".
|
|
143
|
+
* • `--extractor-args youtube:player_client=<client>` — both the android
|
|
144
|
+
* client (unauthenticated) and the web_creator client (authenticated)
|
|
145
|
+
* fall back to progressive format 18, which requires no PO token and
|
|
146
|
+
* avoids the HTTP 403 that DASH segment requests trigger. The client is
|
|
147
|
+
* chosen based on whether cookies are provided: android does not support
|
|
148
|
+
* cookie auth (yt-dlp skips it when cookies are present), so web_creator
|
|
149
|
+
* is used instead for authenticated sessions.
|
|
150
|
+
* Cookies (--cookies-from-browser or --cookies) are still recommended to
|
|
151
|
+
* avoid bot-check rate-limits on burst downloads.
|
|
142
152
|
*
|
|
143
153
|
* The yt-dlp invocation is injected as `download` so the adapter is unit-
|
|
144
154
|
* testable with a fake downloader + fake SttPort — no binary, no network.
|
|
@@ -178,6 +188,13 @@ interface YtDlpWhisperFetcherOptions {
|
|
|
178
188
|
readonly cookiesFromBrowser?: string;
|
|
179
189
|
/** Path to a Netscape cookies.txt — passed to yt-dlp as `--cookies`. */
|
|
180
190
|
readonly cookiesFile?: string;
|
|
191
|
+
/**
|
|
192
|
+
* Directory that contains the `ffmpeg` / `ffprobe` binaries (e.g.
|
|
193
|
+
* `/opt/homebrew/bin`). Passed as `--ffmpeg-location` to yt-dlp so the
|
|
194
|
+
* postprocessor can find them even when PATH is not inherited by the
|
|
195
|
+
* subprocess. Omit to rely on yt-dlp's default PATH search.
|
|
196
|
+
*/
|
|
197
|
+
readonly ffmpegLocation?: string;
|
|
181
198
|
}
|
|
182
199
|
declare class YtDlpWhisperFetcher implements FetcherPort {
|
|
183
200
|
private readonly stt;
|
|
@@ -386,6 +403,8 @@ interface AnthropicDistillerOptions {
|
|
|
386
403
|
readonly maxItems?: number;
|
|
387
404
|
/** Optional sink for per-call token usage (cost + Langfuse export). */
|
|
388
405
|
readonly onUsage?: (usage: DistillUsage) => void;
|
|
406
|
+
/** Output language code (e.g. "fr"). Absent → English (default). */
|
|
407
|
+
readonly lang?: string;
|
|
389
408
|
}
|
|
390
409
|
declare class AnthropicDistiller implements DistillPort {
|
|
391
410
|
private readonly apiKey;
|
|
@@ -393,6 +412,7 @@ declare class AnthropicDistiller implements DistillPort {
|
|
|
393
412
|
private readonly baseUrl;
|
|
394
413
|
private readonly maxItems;
|
|
395
414
|
private readonly onUsage;
|
|
415
|
+
private readonly lang;
|
|
396
416
|
constructor(opts: AnthropicDistillerOptions);
|
|
397
417
|
distill(input: DistillInput): Promise<readonly DistilledItem[]>;
|
|
398
418
|
}
|
|
@@ -435,9 +455,14 @@ interface CliEngine {
|
|
|
435
455
|
readonly subscriptionBilled: boolean;
|
|
436
456
|
/** Executable to invoke — must be on PATH and logged in. */
|
|
437
457
|
readonly command: string;
|
|
438
|
-
/**
|
|
458
|
+
/**
|
|
459
|
+
* Build the argv for one completion.
|
|
460
|
+
* Most engines receive the prompt over stdin and ignore `opts.prompt`.
|
|
461
|
+
* Arg-based engines (hermes `-z`) embed the prompt in argv instead.
|
|
462
|
+
*/
|
|
439
463
|
buildArgs(opts: {
|
|
440
464
|
readonly model?: string;
|
|
465
|
+
readonly prompt?: string;
|
|
441
466
|
}): string[];
|
|
442
467
|
/** Extract the model's text from captured stdout; null → fall back to raw stdout. */
|
|
443
468
|
parseOutput(stdout: string): string | null;
|
|
@@ -473,6 +498,8 @@ interface CliAgentDistillerOptions {
|
|
|
473
498
|
readonly timeoutMs?: number;
|
|
474
499
|
/** Working dir. Default os.tmpdir() — neutral, no project CLAUDE.md / repo. */
|
|
475
500
|
readonly cwd?: string;
|
|
501
|
+
/** Output language code (e.g. "fr"). Absent → English (default). */
|
|
502
|
+
readonly lang?: string;
|
|
476
503
|
}
|
|
477
504
|
declare class CliAgentDistiller implements DistillPort {
|
|
478
505
|
private readonly engine;
|
|
@@ -480,6 +507,7 @@ declare class CliAgentDistiller implements DistillPort {
|
|
|
480
507
|
private readonly maxItems;
|
|
481
508
|
private readonly timeoutMs;
|
|
482
509
|
private readonly cwd;
|
|
510
|
+
private readonly lang;
|
|
483
511
|
constructor(opts: CliAgentDistillerOptions);
|
|
484
512
|
distill(input: DistillInput): Promise<readonly DistilledItem[]>;
|
|
485
513
|
}
|
package/dist/ports/index.mjs
CHANGED
|
@@ -339,7 +339,8 @@ var YtDlpWhisperFetcher = class {
|
|
|
339
339
|
this.download = opts.download ?? defaultYtDlpDownloader(opts.ytDlpBin ?? "yt-dlp", {
|
|
340
340
|
maxDurationSec: opts.maxDurationSec,
|
|
341
341
|
cookiesFromBrowser: opts.cookiesFromBrowser,
|
|
342
|
-
cookiesFile: opts.cookiesFile
|
|
342
|
+
cookiesFile: opts.cookiesFile,
|
|
343
|
+
ffmpegLocation: opts.ffmpegLocation
|
|
343
344
|
});
|
|
344
345
|
this.extraHosts = opts.extraVideoHosts ? new Set(opts.extraVideoHosts) : void 0;
|
|
345
346
|
}
|
|
@@ -383,32 +384,44 @@ function isAuthError(e) {
|
|
|
383
384
|
const m = msg(e).toLowerCase();
|
|
384
385
|
return m.includes(" 401") || m.includes(" 403") || m.includes("unauthorized") || m.includes("forbidden") || m.includes("api key") || m.includes("api_key");
|
|
385
386
|
}
|
|
387
|
+
function buildYtDlpArgs(url, dir, opts) {
|
|
388
|
+
const durationGuard = opts?.maxDurationSec && opts.maxDurationSec > 0 ? ["--match-filter", `duration <= ${Math.floor(opts.maxDurationSec)}`] : [];
|
|
389
|
+
const cookieArgs = opts?.cookiesFromBrowser ? ["--cookies-from-browser", opts.cookiesFromBrowser] : opts?.cookiesFile ? ["--cookies", opts.cookiesFile] : [];
|
|
390
|
+
const ffmpegArgs = opts?.ffmpegLocation ? ["--ffmpeg-location", opts.ffmpegLocation] : [];
|
|
391
|
+
const client = cookieArgs.length === 0 ? "android" : "web_creator";
|
|
392
|
+
const extractorArgs = ["--extractor-args", `youtube:player_client=${client}`];
|
|
393
|
+
return [
|
|
394
|
+
"-f",
|
|
395
|
+
"bestaudio/best",
|
|
396
|
+
// /best fallback: many videos lack a standalone bestaudio
|
|
397
|
+
"-x",
|
|
398
|
+
"--audio-format",
|
|
399
|
+
"mp3",
|
|
400
|
+
"--audio-quality",
|
|
401
|
+
"64K",
|
|
402
|
+
"--no-playlist",
|
|
403
|
+
"--no-progress",
|
|
404
|
+
"--remote-components",
|
|
405
|
+
"ejs:github",
|
|
406
|
+
// solves YouTube nsig "n challenge"
|
|
407
|
+
...extractorArgs,
|
|
408
|
+
...durationGuard,
|
|
409
|
+
...cookieArgs,
|
|
410
|
+
...ffmpegArgs,
|
|
411
|
+
"--write-info-json",
|
|
412
|
+
"-o",
|
|
413
|
+
join(dir, "track.%(ext)s"),
|
|
414
|
+
url
|
|
415
|
+
];
|
|
416
|
+
}
|
|
386
417
|
function defaultYtDlpDownloader(bin, opts) {
|
|
387
418
|
return async (url) => {
|
|
388
419
|
const dir = await mkdtemp(join(tmpdir(), "corpus-ytdlp-"));
|
|
389
420
|
const cleanup = () => rm(dir, { recursive: true, force: true });
|
|
390
421
|
try {
|
|
391
|
-
const durationGuard = opts?.maxDurationSec && opts.maxDurationSec > 0 ? ["--match-filter", `duration <= ${Math.floor(opts.maxDurationSec)}`] : [];
|
|
392
|
-
const cookieArgs = opts?.cookiesFromBrowser ? ["--cookies-from-browser", opts.cookiesFromBrowser] : opts?.cookiesFile ? ["--cookies", opts.cookiesFile] : [];
|
|
393
422
|
await execFileAsync(
|
|
394
423
|
bin,
|
|
395
|
-
|
|
396
|
-
"-f",
|
|
397
|
-
"bestaudio",
|
|
398
|
-
"-x",
|
|
399
|
-
"--audio-format",
|
|
400
|
-
"mp3",
|
|
401
|
-
"--audio-quality",
|
|
402
|
-
"64K",
|
|
403
|
-
"--no-playlist",
|
|
404
|
-
"--no-progress",
|
|
405
|
-
...durationGuard,
|
|
406
|
-
...cookieArgs,
|
|
407
|
-
"--write-info-json",
|
|
408
|
-
"-o",
|
|
409
|
-
join(dir, "track.%(ext)s"),
|
|
410
|
-
url
|
|
411
|
-
],
|
|
424
|
+
buildYtDlpArgs(url, dir, opts),
|
|
412
425
|
{ maxBuffer: 16 * 1024 * 1024 }
|
|
413
426
|
);
|
|
414
427
|
const files = await readdir(dir);
|
|
@@ -761,15 +774,17 @@ var AnthropicDistiller = class {
|
|
|
761
774
|
baseUrl;
|
|
762
775
|
maxItems;
|
|
763
776
|
onUsage;
|
|
777
|
+
lang;
|
|
764
778
|
constructor(opts) {
|
|
765
779
|
this.apiKey = opts.apiKey;
|
|
766
780
|
this.model = opts.model ?? "claude-sonnet-4-6";
|
|
767
781
|
this.baseUrl = (opts.baseUrl ?? "https://api.anthropic.com/v1").replace(/\/+$/, "");
|
|
768
782
|
this.maxItems = opts.maxItems ?? 8;
|
|
769
783
|
this.onUsage = opts.onUsage;
|
|
784
|
+
this.lang = opts.lang;
|
|
770
785
|
}
|
|
771
786
|
async distill(input) {
|
|
772
|
-
const prompt = buildDistillPrompt(input, this.maxItems);
|
|
787
|
+
const prompt = buildDistillPrompt(input, this.maxItems, { lang: this.lang });
|
|
773
788
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
774
789
|
const res = await fetch(`${this.baseUrl}/messages`, {
|
|
775
790
|
method: "POST",
|
|
@@ -808,18 +823,20 @@ var CliAgentDistiller = class {
|
|
|
808
823
|
maxItems;
|
|
809
824
|
timeoutMs;
|
|
810
825
|
cwd;
|
|
826
|
+
lang;
|
|
811
827
|
constructor(opts) {
|
|
812
828
|
this.engine = opts.engine;
|
|
813
829
|
this.model = opts.model;
|
|
814
830
|
this.maxItems = opts.maxItems ?? 8;
|
|
815
831
|
this.timeoutMs = opts.timeoutMs ?? 12e4;
|
|
816
832
|
this.cwd = opts.cwd ?? tmpdir();
|
|
833
|
+
this.lang = opts.lang;
|
|
817
834
|
}
|
|
818
835
|
async distill(input) {
|
|
819
|
-
const prompt = buildDistillPrompt(input, this.maxItems);
|
|
836
|
+
const prompt = buildDistillPrompt(input, this.maxItems, { lang: this.lang });
|
|
820
837
|
const stdout = await spawnWithStdin({
|
|
821
838
|
command: this.engine.command,
|
|
822
|
-
args: this.engine.buildArgs({ ...this.model ? { model: this.model } : {} }),
|
|
839
|
+
args: this.engine.buildArgs({ ...this.model ? { model: this.model } : {}, prompt }),
|
|
823
840
|
stdin: prompt,
|
|
824
841
|
cwd: this.cwd,
|
|
825
842
|
timeoutMs: this.timeoutMs
|
|
@@ -894,11 +911,23 @@ var OPENCODE = {
|
|
|
894
911
|
buildArgs: ({ model }) => ["run", ...model ? ["-m", model] : []],
|
|
895
912
|
parseOutput: plainTextOutput
|
|
896
913
|
};
|
|
914
|
+
var HERMES = {
|
|
915
|
+
id: "hermes",
|
|
916
|
+
subscriptionBilled: true,
|
|
917
|
+
command: "hermes",
|
|
918
|
+
buildArgs: ({ model, prompt }) => [
|
|
919
|
+
"--oneshot",
|
|
920
|
+
prompt ?? "",
|
|
921
|
+
...model ? ["-m", model] : []
|
|
922
|
+
],
|
|
923
|
+
parseOutput: plainTextOutput
|
|
924
|
+
};
|
|
897
925
|
var CLI_ENGINES = {
|
|
898
926
|
[CLAUDE_CODE.id]: CLAUDE_CODE,
|
|
899
927
|
[GEMINI.id]: GEMINI,
|
|
900
928
|
[CODEX.id]: CODEX,
|
|
901
|
-
[OPENCODE.id]: OPENCODE
|
|
929
|
+
[OPENCODE.id]: OPENCODE,
|
|
930
|
+
[HERMES.id]: HERMES
|
|
902
931
|
};
|
|
903
932
|
var RPC_RESPONSE = z.object({ result: z.unknown().optional(), error: z.unknown().optional() }).loose();
|
|
904
933
|
var RPC_MESSAGE = z.object({
|