poddrill 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/workspace.js CHANGED
@@ -4,11 +4,11 @@
4
4
  */
5
5
  import { randomBytes } from "node:crypto";
6
6
  import { mkdir, realpath, rename, writeFile } from "node:fs/promises";
7
- import { homedir } from "node:os";
8
7
  import { isAbsolute, join, resolve } from "node:path";
9
8
  import { asRecord, errorCode, pathExists, readJsonFile } from "./disk.js";
10
9
  import { isModelId, modelIds } from "./models.js";
11
- import { isProviderId, providerIds } from "./provider.js";
10
+ import { homeFromEnv } from "./python-env.js";
11
+ import { isProviderId, providerIds, refuseUnwiredTransport, providerTransport } from "./provider.js";
12
12
  /** Every artifact an episode lists, in the order it lists them. */
13
13
  export const artifactKinds = ["transcript", "embedding"];
14
14
  const CONFIG_FILE = "poddrill.json";
@@ -18,10 +18,17 @@ const ARTIFACT_OUTPUTS = {
18
18
  transcript: "transcript.vtt",
19
19
  embedding: "embedding.json",
20
20
  };
21
+ /** `PODDRILL_WORKSPACE`, else `~/poddrill`. Setup and every other command use this path. */
22
+ export function defaultWorkspacePath(env) {
23
+ const configured = env["PODDRILL_WORKSPACE"];
24
+ if (configured !== undefined && configured !== "") {
25
+ return resolve(configured);
26
+ }
27
+ return resolve(join(homeFromEnv(env), "poddrill"));
28
+ }
21
29
  /** `PODDRILL_WORKSPACE`, else `~/poddrill`, resolved with realpath. */
22
30
  export async function resolveWorkspace(env) {
23
- const configured = env["PODDRILL_WORKSPACE"];
24
- const candidate = resolve(configured === undefined || configured === "" ? join(homedir(), "poddrill") : configured);
31
+ const candidate = defaultWorkspacePath(env);
25
32
  let root;
26
33
  try {
27
34
  root = asWorkspaceRoot(await realpath(candidate));
@@ -56,6 +63,7 @@ export async function setupWorkspace(input) {
56
63
  if (!isProviderId(input.provider)) {
57
64
  throw new Error(`unknown provider ${input.provider}. Use one of ${providerIds.join(", ")}.`);
58
65
  }
66
+ refuseUnwiredTransport(input.provider, providerTransport(input.provider));
59
67
  if (!isCount(input.queueLength)) {
60
68
  throw new Error(`queue length must be a positive integer, got ${input.queueLength}`);
61
69
  }
@@ -65,12 +73,16 @@ export async function setupWorkspace(input) {
65
73
  if (input.embeddingLimit !== undefined && !isCount(input.embeddingLimit)) {
66
74
  throw new Error(`embedding limit must be a positive integer, got ${input.embeddingLimit}`);
67
75
  }
76
+ if (input.python !== undefined && !isAbsolute(input.python)) {
77
+ throw new Error(`python must be an absolute path, got ${input.python}`);
78
+ }
68
79
  const requested = resolve(input.root);
69
80
  const current = parseConfig(await readJsonFile(join(requested, CONFIG_FILE)));
70
81
  const modelFleet = input.modelFleet ?? current?.modelFleet ?? {};
71
82
  const modelWeights = input.modelWeights ?? current?.modelWeights ?? {};
72
83
  assertModelFleet(modelFleet);
73
84
  assertModelWeights(modelWeights);
85
+ const python = pythonPath(input.python, current?.python);
74
86
  const config = {
75
87
  provider: input.provider,
76
88
  queueLength: input.queueLength,
@@ -78,6 +90,7 @@ export async function setupWorkspace(input) {
78
90
  modelFleet,
79
91
  modelWeights,
80
92
  embeddingLimit: input.embeddingLimit ?? current?.embeddingLimit ?? 1,
93
+ ...(python === undefined ? {} : { python }),
81
94
  };
82
95
  const unchanged = current !== undefined && sameConfig(current, config);
83
96
  if (input.ownerAlive && current !== undefined && !unchanged) {
@@ -89,7 +102,7 @@ export async function setupWorkspace(input) {
89
102
  `poddrill serve is running with provider ${current.provider},`,
90
103
  `queue length ${current.queueLength}, fleet limit ${current.fleetLimit},`,
91
104
  `and embedding limit ${current.embeddingLimit}.`,
92
- "Stop it before changing the setup.",
105
+ "Stop it with poddrill stop before changing the setup.",
93
106
  ].join(" "),
94
107
  };
95
108
  }
@@ -181,6 +194,7 @@ function parseConfig(value) {
181
194
  }
182
195
  const fleetLimit = readFleetLimit(record);
183
196
  const embeddingLimit = optionalCount(record["embeddingLimit"]);
197
+ const python = readPython(record);
184
198
  if (fleetLimit === undefined || embeddingLimit === undefined) {
185
199
  return undefined;
186
200
  }
@@ -191,8 +205,19 @@ function parseConfig(value) {
191
205
  modelFleet: readModelFleet(record["modelFleet"]),
192
206
  modelWeights: readModelWeights(record["modelWeights"]),
193
207
  embeddingLimit,
208
+ ...(python === undefined ? {} : { python }),
194
209
  };
195
210
  }
211
+ function readPython(record) {
212
+ if (!Object.prototype.hasOwnProperty.call(record, "python")) {
213
+ return undefined;
214
+ }
215
+ const python = record["python"];
216
+ if (typeof python !== "string" || !isAbsolute(python)) {
217
+ throw new Error(`python must be an absolute path, got ${String(python)}`);
218
+ }
219
+ return python;
220
+ }
196
221
  /**
197
222
  * `fleetLimit` wins. An old file that only has `whistleLimit` uses that number.
198
223
  * Neither field reads as 1. A present field that is not a positive integer does not parse.
@@ -275,8 +300,14 @@ function writtenConfig(config) {
275
300
  body["modelWeights"] = config.modelWeights;
276
301
  }
277
302
  body["embeddingLimit"] = config.embeddingLimit;
303
+ if (config.python !== undefined) {
304
+ body["python"] = config.python;
305
+ }
278
306
  return `${JSON.stringify(body, null, 2)}\n`;
279
307
  }
308
+ function pythonPath(requested, current) {
309
+ return requested ?? current;
310
+ }
280
311
  /** Absent reads as 1. Present and not a positive integer is `undefined`. */
281
312
  function optionalCount(value) {
282
313
  if (value === undefined) {
@@ -289,6 +320,7 @@ function sameConfig(a, b) {
289
320
  a.queueLength === b.queueLength &&
290
321
  a.fleetLimit === b.fleetLimit &&
291
322
  a.embeddingLimit === b.embeddingLimit &&
323
+ a.python === b.python &&
292
324
  sameById(a.modelFleet, b.modelFleet) &&
293
325
  sameById(a.modelWeights, b.modelWeights));
294
326
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poddrill",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Transcribe episode audio into timed text, group the voices, and store a summary.",
5
5
  "license": "MIT",
6
6
  "author": "Jianyu Zhao",
@@ -10,10 +10,11 @@
10
10
  },
11
11
  "files": [
12
12
  "dist",
13
- ".agents/skills/poddrill"
13
+ ".agents/skills/poddrill",
14
+ "requirements.txt"
14
15
  ],
15
16
  "engines": {
16
- "node": ">=22.18.0"
17
+ "node": ">=20.19.0"
17
18
  },
18
19
  "repository": {
19
20
  "type": "git",
@@ -22,7 +23,8 @@
22
23
  "cactusNeedle": "3.2.0",
23
24
  "scripts": {
24
25
  "build": "node scripts/build-js.mjs",
25
- "test": "node --experimental-strip-types --test test/episode.test.ts test/fleet.test.ts test/idle-fleet.test.ts test/jobs.test.ts test/cues.test.ts test/summary.test.ts test/cli.test.ts test/audit.test.ts test/needle.test.ts test/publish.test.ts test/client.test.ts test/harness/run.test.ts"
26
+ "test": "node --experimental-strip-types --test test/episode.test.ts test/fleet.test.ts test/idle-fleet.test.ts test/jobs.test.ts test/cues.test.ts test/summary.test.ts test/cli.test.ts test/audit.test.ts test/needle.test.ts test/publish.test.ts test/client.test.ts test/runtime.test.ts test/harness/run.test.ts",
27
+ "test:smoke": "node --experimental-strip-types --test test/smoke.test.ts"
26
28
  },
27
29
  "dependencies": {
28
30
  "yaml": "2.9.1"
@@ -0,0 +1,3 @@
1
+ cactus-needle==3.2.0
2
+ numpy==2.5.4
3
+ onnxruntime==1.31.0