poe-code 3.0.350 → 3.0.352

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-code",
3
- "version": "3.0.350",
3
+ "version": "3.0.352",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -251,6 +251,9 @@ function parseTaskStatus(value, availableSteps, taskId) {
251
251
  throw new Error(`Invalid status for task "${taskId}": expected at least one step status.`);
252
252
  }
253
253
  for (const [stepName, stepStatus] of stepStatuses) {
254
+ if (stepName.length === 0) {
255
+ throw new Error(`Invalid status for task "${taskId}": step names must be non-empty.`);
256
+ }
254
257
  if (availableSteps && !Object.hasOwn(availableSteps, stepName)) {
255
258
  throw new Error(`Unknown step "${stepName}" referenced by task "${taskId}".`);
256
259
  }
@@ -442,11 +445,17 @@ export function parsePlan(planContent, options = {}) {
442
445
  "mcp"
443
446
  ]);
444
447
  const kind = getOwnEntry(document, "kind");
445
- if (kind !== undefined && kind !== "pipeline") {
448
+ if (kind === undefined) {
449
+ throw new Error('Invalid plan YAML: missing required "kind".');
450
+ }
451
+ if (kind !== "pipeline") {
446
452
  throw new Error('Invalid plan YAML: "kind" must be "pipeline".');
447
453
  }
448
454
  const version = getOwnEntry(document, "version");
449
- if (version !== undefined && version !== 1) {
455
+ if (version === undefined) {
456
+ throw new Error('Invalid plan YAML: missing required "version".');
457
+ }
458
+ if (version !== 1) {
450
459
  throw new Error('Invalid plan YAML: "version" must be 1.');
451
460
  }
452
461
  let extendsName = "default";
@@ -465,6 +474,9 @@ export function parsePlan(planContent, options = {}) {
465
474
  }
466
475
  stepOverrides = {};
467
476
  for (const [stepName, value] of Object.entries(stepsValue)) {
477
+ if (stepName.length === 0) {
478
+ throw new Error('Invalid plan YAML: step names must be non-empty.');
479
+ }
468
480
  defineRecordEntry(stepOverrides, stepName, parseStepOverride(value, `steps.${stepName}`));
469
481
  }
470
482
  }
@@ -477,6 +489,7 @@ export function parsePlan(planContent, options = {}) {
477
489
  if (!isRecord(value)) {
478
490
  throw new Error(`Invalid tasks[${index}]: expected an object.`);
479
491
  }
492
+ rejectUnknownProperties(value, ["id", "title", "prompt", "status"], `tasks[${index}].`);
480
493
  const id = asRequiredString(getOwnEntry(value, "id"), `tasks[${index}].id`);
481
494
  if (ids.has(id)) {
482
495
  throw new Error(`Duplicate task id "${id}".`);
@@ -5,7 +5,7 @@ function selectFromTask(task) {
5
5
  return task.status === "done" ? { kind: "completed" } : { kind: "run", task };
6
6
  }
7
7
  const stepName = Object.entries(task.status).find(([, value]) => value !== "done")?.[0];
8
- return stepName ? { kind: "run", task, stepName } : { kind: "completed" };
8
+ return stepName !== undefined ? { kind: "run", task, stepName } : { kind: "completed" };
9
9
  }
10
10
  export function selectNextExecution(plan, taskId) {
11
11
  const tasks = taskId ? plan.tasks.filter((task) => task.id === taskId) : plan.tasks;
@@ -22,7 +22,13 @@ function looksLikeDocPath(value) {
22
22
  }
23
23
  async function resolveDocVarFromPath(options) {
24
24
  const trimmed = options.value.trim();
25
- const absolutePath = path.isAbsolute(trimmed) ? trimmed : path.join(options.cwd, trimmed);
25
+ const absolutePath = path.resolve(options.cwd, trimmed);
26
+ const relativePath = path.relative(options.cwd, absolutePath);
27
+ if (relativePath === ".." ||
28
+ relativePath.startsWith(`..${path.sep}`) ||
29
+ path.isAbsolute(relativePath)) {
30
+ throw new Error(`Pipeline doc var "${options.key}" resolves outside the project root: ${trimmed}`);
31
+ }
26
32
  try {
27
33
  return await options.readFile(absolutePath, "utf8");
28
34
  }