@agimon-ai/po-workflow 0.0.0-alpha.36 → 0.0.0-alpha.38

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.
@@ -378,6 +378,7 @@ const StepFieldsSchema = zod.z.object({
378
378
  artifacts: StepArtifactsSchema.optional(),
379
379
  "timeout-minutes": zod.z.number().optional(),
380
380
  env: CoercedEnvRecord.optional(),
381
+ secrets: zod.z.array(zod.z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "Secret names are environment variable names.")).min(1).optional().describe("Env names a run step's command receives from its Po profile; values never reach logs or run records."),
381
382
  with: zod.z.record(zod.z.string(), zod.z.string()).optional(),
382
383
  "working-directory": zod.z.string().optional(),
383
384
  if: zod.z.string().optional(),
@@ -418,6 +419,11 @@ function refineStep(step, context) {
418
419
  message: `${key} is only supported with a templated interactiveRun.`,
419
420
  path: [key]
420
421
  });
422
+ if (step.secrets !== void 0 && (step.run === void 0 || step.interactiveRun || step.customRun)) context.addIssue({
423
+ code: "custom",
424
+ message: "secrets are only supported on run steps.",
425
+ path: ["secrets"]
426
+ });
421
427
  if (step["restart-from"] !== void 0 && (step.run === void 0 || step.interactiveRun || step.customRun)) context.addIssue({
422
428
  code: "custom",
423
429
  message: "restart-from is only supported on run steps.",
@@ -773,6 +779,7 @@ const WorkflowRunRecordSchema = zod.z.object({
773
779
  runner: zod.z.string().optional(),
774
780
  command: zod.z.string().optional().describe("Command the launch selected for templated interactiveRun steps."),
775
781
  choice: zod.z.string().optional().describe("Choice the launch selected for steps that do not pin one."),
782
+ profile: zod.z.string().optional().describe("Po profile name the launch selected as the default runConfig.profile; never its secrets."),
776
783
  runId: zod.z.string().uuid().optional(),
777
784
  runKey: zod.z.string(),
778
785
  secretFile: zod.z.string().optional(),
@@ -4759,6 +4766,7 @@ var WorkflowRegistryService = class {
4759
4766
  runner: input.runner,
4760
4767
  ...input.command === void 0 ? {} : { command: input.command },
4761
4768
  ...input.choice === void 0 ? {} : { choice: input.choice },
4769
+ ...input.profile === void 0 ? {} : { profile: input.profile },
4762
4770
  runDirectory: input.runDirectory,
4763
4771
  runId,
4764
4772
  secretFile: input.secretFile,
@@ -6620,6 +6628,10 @@ const INTERACTIVE_RUN_KEY = "interactiveRun";
6620
6628
  const CUSTOM_RUN_KEY = "customRun";
6621
6629
  const CUSTOM_RUN_UNSUPPORTED_MESSAGE = "customRun needs a host step executor and no run or interactiveRun fallback exists for step";
6622
6630
  const IN_PROCESS_ONLY_UNSUPPORTED_MESSAGE = "has no run template, and this host cannot run it in-process";
6631
+ const SECRETS_UNAVAILABLE_MESSAGE = "profile secrets are unavailable for step";
6632
+ const SECRETS_LOG_PREFIX = "secrets: ";
6633
+ const RUN_CONFIG_PROFILE_KEY = "profile";
6634
+ const REDACTED = "[redacted]";
6623
6635
  const WORKFLOW_RUN_CONFIG_ENV_KEY = "WORKFLOW_RUN_CONFIG";
6624
6636
  const DEFAULT_CHOICE = "default";
6625
6637
  const OUTPUT_TAIL_LINES = 50;
@@ -6736,6 +6748,21 @@ const RESULT_STATUS_PAUSE_REQUESTED = "pause_requested";
6736
6748
  /** The reason a step failure carries when nothing more specific was recorded. */
6737
6749
  const STEP_FAILED_REASON = "The step failed.";
6738
6750
  /**
6751
+ * Mask each secret value in step output, in its raw, JSON-escaped and
6752
+ * URL-encoded forms, and line by line for a value that spans lines.
6753
+ */
6754
+ function secretRedactor(values) {
6755
+ const forms = /* @__PURE__ */ new Set();
6756
+ for (const value of values) for (const form of [
6757
+ value,
6758
+ JSON.stringify(value).slice(1, -1),
6759
+ encodeURIComponent(value),
6760
+ ...value.split("\n")
6761
+ ]) if (form) forms.add(form);
6762
+ const longestFirst = [...forms].sort((left, right) => right.length - left.length);
6763
+ return (text) => longestFirst.reduce((current, form) => current.split(form).join(REDACTED), text);
6764
+ }
6765
+ /**
6739
6766
  * Executes workflow steps in child processes and tracks the active process so
6740
6767
  * the workflow runner can interrupt it safely.
6741
6768
  *
@@ -6753,6 +6780,7 @@ var StepRunnerService = class StepRunnerService {
6753
6780
  runDirectoryService;
6754
6781
  ownsTerminal;
6755
6782
  stepExecutor;
6783
+ resolveSecrets;
6756
6784
  activeStep = null;
6757
6785
  activeExternal = null;
6758
6786
  lastInteractiveReap = null;
@@ -6772,7 +6800,7 @@ var StepRunnerService = class StepRunnerService {
6772
6800
  * must not hand a step's stdin to it, and must not `stty` or reset it
6773
6801
  * afterwards, however cooperative the intent.
6774
6802
  */
6775
- constructor(runtime, parser, logger, quotaService = new CodexQuotaService(runtime), watchStatusFile = runtime.fs.watch.bind(runtime.fs), runDirectoryService = new RunDirectoryService(runtime), ownsTerminal = true, stepExecutor) {
6803
+ constructor(runtime, parser, logger, quotaService = new CodexQuotaService(runtime), watchStatusFile = runtime.fs.watch.bind(runtime.fs), runDirectoryService = new RunDirectoryService(runtime), ownsTerminal = true, stepExecutor, resolveSecrets) {
6776
6804
  this.runtime = runtime;
6777
6805
  this.parser = parser;
6778
6806
  this.logger = logger;
@@ -6781,6 +6809,7 @@ var StepRunnerService = class StepRunnerService {
6781
6809
  this.runDirectoryService = runDirectoryService;
6782
6810
  this.ownsTerminal = ownsTerminal;
6783
6811
  this.stepExecutor = stepExecutor;
6812
+ this.resolveSecrets = resolveSecrets;
6784
6813
  }
6785
6814
  /**
6786
6815
  * Use a workflow's command templates and choices, and what the launch
@@ -6797,7 +6826,7 @@ var StepRunnerService = class StepRunnerService {
6797
6826
  * Release it once its step has finished.
6798
6827
  */
6799
6828
  fork(label) {
6800
- const child = new StepRunnerService(this.runtime, this.parser, labelledLogger(this.logger, label), this.quotaService, this.watchStatusFile, this.runDirectoryService, this.ownsTerminal, this.stepExecutor);
6829
+ const child = new StepRunnerService(this.runtime, this.parser, labelledLogger(this.logger, label), this.quotaService, this.watchStatusFile, this.runDirectoryService, this.ownsTerminal, this.stepExecutor, this.resolveSecrets);
6801
6830
  child.commandSelection = this.commandSelection;
6802
6831
  child.interruptedSignal = this.interruptedSignal;
6803
6832
  child.terminalLock = this.terminalLock;
@@ -6964,7 +6993,12 @@ var StepRunnerService = class StepRunnerService {
6964
6993
  stepEnv[PROJECT_PATH_ENV_KEY] = workDir;
6965
6994
  stepEnv[WORKFLOW_STEP_NAME_ENV_KEY] = stepName;
6966
6995
  stepEnv[WORKFLOW_STEP_DISPLAY_ENV_KEY] = this.formatStepDisplay(stepEnv[WORKFLOW_JOB_NAME_ENV_KEY], stepName);
6967
- const runConfig = resolvedStepCommand?.runConfig ?? step.runConfig;
6996
+ const stepRunConfig = resolvedStepCommand?.runConfig ?? step.runConfig;
6997
+ const launchProfile = this.commandSelection.profile;
6998
+ const runConfig = launchProfile === void 0 || stepRunConfig?.[RUN_CONFIG_PROFILE_KEY] !== void 0 ? stepRunConfig : {
6999
+ ...stepRunConfig,
7000
+ [RUN_CONFIG_PROFILE_KEY]: launchProfile
7001
+ };
6968
7002
  stepEnv[WORKFLOW_RUN_CONFIG_ENV_KEY] = JSON.stringify(runConfig ?? {});
6969
7003
  if (step.if !== void 0 && !options.dryRun) {
6970
7004
  const condition = await this.evaluateStepCondition(step.if, stepContext, workDir, stepEnv, stepName, timeoutMs);
@@ -7024,6 +7058,7 @@ var StepRunnerService = class StepRunnerService {
7024
7058
  if (options.dryRun) {
7025
7059
  this.logger.info(` ${CYAN$2}${STATUS_DRY}${RESET$3} ${stepName}`);
7026
7060
  this.logger.info(`${COMMAND_LOG_INDENT}${DIM$3}${COMMAND_LOG_PROMPT}${command.split(COMMAND_LINE_BREAK).join(COMMAND_LOG_CONTINUATION)}${RESET$3}`);
7061
+ if (step.secrets !== void 0 && step.secrets.length > 0) this.logger.info(`${COMMAND_LOG_INDENT}${DIM$3}${SECRETS_LOG_PREFIX}${step.secrets.join(", ")}${RESET$3}`);
7027
7062
  await this.logStepLifecycle("info", "workflow.step.finished", stepEnv, {
7028
7063
  "workflow.step.duration_ms": Date.now() - stepStartedAt,
7029
7064
  "workflow.step.outcome": "dry-run"
@@ -7055,6 +7090,19 @@ var StepRunnerService = class StepRunnerService {
7055
7090
  this.logger.error(` ${RED$2}${STATUS_FAIL}${RESET$3} ${stepName}\n`);
7056
7091
  return false;
7057
7092
  }
7093
+ let secretEnv;
7094
+ if (step.secrets !== void 0 && step.secrets.length > 0) {
7095
+ secretEnv = await this.resolveStepSecrets(step.secrets, runConfig, interactive || custom !== void 0 || inProcessOnly === true);
7096
+ if (secretEnv === void 0) {
7097
+ fail(`${SECRETS_UNAVAILABLE_MESSAGE} "${stepName}"`);
7098
+ if (shouldContinue) {
7099
+ this.logger.warn(` ${YELLOW$2}${STATUS_FAIL}${RESET$3} ${stepName} ${CONTINUE_ON_ERROR_SUFFIX}\n`);
7100
+ return true;
7101
+ }
7102
+ this.logger.error(` ${RED$2}${STATUS_FAIL}${RESET$3} ${stepName}\n`);
7103
+ return false;
7104
+ }
7105
+ }
7058
7106
  let quotaFailureRetried = false;
7059
7107
  for (let attempt = 0; attempt <= maxTimeoutRetries; attempt++) {
7060
7108
  await this.waitForCodexQuotaAvailability(stepName, command, isCodexCommand);
@@ -7082,6 +7130,7 @@ var StepRunnerService = class StepRunnerService {
7082
7130
  interactive,
7083
7131
  onExecutionStarted: options.onExecutionStarted,
7084
7132
  runConfig,
7133
+ secretEnv,
7085
7134
  stepEnv,
7086
7135
  stepName,
7087
7136
  jobEnvironment: this.definedEnv({
@@ -7342,10 +7391,30 @@ var StepRunnerService = class StepRunnerService {
7342
7391
  };
7343
7392
  }
7344
7393
  /**
7394
+ * Resolve a run step's secrets for its child alone, or undefined when they
7395
+ * cannot be: this host has no resolver, the step is not a plain run, no
7396
+ * profile applies, or the resolver fails. The failure stays generic so no
7397
+ * value or alias detail reaches the log.
7398
+ */
7399
+ async resolveStepSecrets(names, runConfig, hostedStep) {
7400
+ const profile = runConfig?.[RUN_CONFIG_PROFILE_KEY];
7401
+ if (this.resolveSecrets === void 0 || hostedStep || typeof profile !== "string" || profile === "") return;
7402
+ try {
7403
+ return await this.resolveSecrets({
7404
+ cwd: this.runtime.cwd,
7405
+ profile,
7406
+ names
7407
+ });
7408
+ } catch {
7409
+ return;
7410
+ }
7411
+ }
7412
+ /**
7345
7413
  * Spawn a child shell for the step command and normalize its termination
7346
7414
  * into a typed result.
7347
7415
  */
7348
- async executeCommand(command, workDir, stepEnv, stepName, interactive, timeoutMs) {
7416
+ async executeCommand(command, workDir, stepEnv, stepName, interactive, timeoutMs, secretEnv) {
7417
+ const redact = secretEnv === void 0 ? void 0 : secretRedactor(Object.values(secretEnv));
7349
7418
  if (interactive) this.logger.info(` ${DIM$3}${INTERACTIVE_OUTPUT_NOTICE}${RESET$3}`);
7350
7419
  return await new Promise((resolveResult) => {
7351
7420
  const terminalState = interactive ? this.captureTerminalState() : null;
@@ -7372,6 +7441,7 @@ var StepRunnerService = class StepRunnerService {
7372
7441
  detached: this.runtime.workflowHost.platform !== PLATFORM_WINDOWS,
7373
7442
  env: {
7374
7443
  ...stepEnv,
7444
+ ...secretEnv,
7375
7445
  [FORCE_COLOR_KEY]: FORCE_COLOR_VALUE
7376
7446
  },
7377
7447
  shell: DEFAULT_SHELL
@@ -7439,18 +7509,30 @@ var StepRunnerService = class StepRunnerService {
7439
7509
  outputTail.push(line);
7440
7510
  if (outputTail.length > OUTPUT_TAIL_LINES) outputTail.shift();
7441
7511
  };
7442
- child.stdout?.on("data", (data) => {
7443
- for (const line of data.toString().split("\n")) if (line) {
7444
- keep(line);
7445
- this.logger.info(line);
7446
- }
7447
- });
7448
- child.stderr?.on("data", (data) => {
7449
- for (const line of data.toString().split("\n")) if (line) {
7450
- keep(line);
7451
- this.logger.error(line);
7452
- }
7453
- });
7512
+ const lineReader = (emit) => {
7513
+ let pending = "";
7514
+ const show = (line) => {
7515
+ if (!line) return;
7516
+ const shown = redact === void 0 ? line : redact(line);
7517
+ keep(shown);
7518
+ emit(shown);
7519
+ };
7520
+ return {
7521
+ push: (data) => {
7522
+ const lines = (pending + data.toString()).split("\n");
7523
+ pending = redact === void 0 ? "" : lines.pop() ?? "";
7524
+ for (const line of lines) show(line);
7525
+ },
7526
+ flush: () => {
7527
+ show(pending);
7528
+ pending = "";
7529
+ }
7530
+ };
7531
+ };
7532
+ const stdout = lineReader((line) => this.logger.info(line));
7533
+ const stderr = lineReader((line) => this.logger.error(line));
7534
+ child.stdout?.on("data", stdout.push);
7535
+ child.stderr?.on("data", stderr.push);
7454
7536
  child.on("error", (error) => {
7455
7537
  settle({
7456
7538
  status: "spawn_error",
@@ -7465,6 +7547,8 @@ var StepRunnerService = class StepRunnerService {
7465
7547
  child.on("exit", async (code, signal) => {
7466
7548
  if (!interactive) await this.cleanupProcessGroup();
7467
7549
  await outputDrained(child);
7550
+ stdout.flush();
7551
+ stderr.flush();
7468
7552
  if (timedOut && timeoutMs !== null) {
7469
7553
  settle({
7470
7554
  status: "timed_out",
@@ -7506,6 +7590,7 @@ var StepRunnerService = class StepRunnerService {
7506
7590
  */
7507
7591
  startStepExecution(request) {
7508
7592
  const { command, custom, inProcessOnly, interactive, stepEnv, stepName, template, timeoutMs, workDir } = request;
7593
+ if (request.secretEnv !== void 0) return this.executeCommand(command, workDir, stepEnv, stepName, false, timeoutMs, request.secretEnv);
7509
7594
  const executor = this.stepExecutor;
7510
7595
  const base = {
7511
7596
  traceparent: request.traceparent,
@@ -9551,7 +9636,7 @@ var RunWorkflowService = class {
9551
9636
  this.launcherMetadata = dependencies.launcherMetadata;
9552
9637
  this.parser = dependencies.parser ?? new WorkflowParserService(this.runtime);
9553
9638
  this.serviceManager = dependencies.serviceManager ?? new BackgroundServiceManager(this.runtime, this.logger);
9554
- this.stepRunner = dependencies.stepRunner ?? new StepRunnerService(this.runtime, this.parser, this.logger, void 0, void 0, void 0, dependencies.ownsTerminal, dependencies.stepExecutor);
9639
+ this.stepRunner = dependencies.stepRunner ?? new StepRunnerService(this.runtime, this.parser, this.logger, void 0, void 0, void 0, dependencies.ownsTerminal, dependencies.stepExecutor, dependencies.resolveSecrets);
9555
9640
  this.registry = dependencies.registry ?? new WorkflowRegistryService(this.runtime);
9556
9641
  this.runDirectoryService = dependencies.runDirectoryService ?? new RunDirectoryService(this.runtime, this.registry.writeCoordinator);
9557
9642
  this.jobRunner = dependencies.jobRunner ?? new JobRunnerService(this.runtime, this.parser, this.stepRunner, this.logger, this.runDirectoryService);
@@ -9745,7 +9830,8 @@ var RunWorkflowService = class {
9745
9830
  commands: workflow.commands,
9746
9831
  choices: workflow.choices,
9747
9832
  command: input.command,
9748
- choice: input.choice
9833
+ choice: input.choice,
9834
+ profile: input.profile
9749
9835
  });
9750
9836
  }
9751
9837
  resolveRunner(input, workflow) {
@@ -9897,6 +9983,7 @@ var RunWorkflowService = class {
9897
9983
  runner,
9898
9984
  command: input.command,
9899
9985
  choice: input.choice,
9986
+ profile: input.profile,
9900
9987
  runDirectory: workflow["run-directory"],
9901
9988
  secretFile: input.secretFile,
9902
9989
  skipLaunch: input.skipLaunch,
@@ -10888,6 +10975,7 @@ var RunWorkflowService = class {
10888
10975
  if (runner) parts.push("--runner", require_workflowCommandTemplate.escapeShellArg(runner));
10889
10976
  if (input.command) parts.push("--command", require_workflowCommandTemplate.escapeShellArg(input.command));
10890
10977
  if (input.choice) parts.push("--choice", require_workflowCommandTemplate.escapeShellArg(input.choice));
10978
+ if (input.profile) parts.push("--profile", require_workflowCommandTemplate.escapeShellArg(input.profile));
10891
10979
  if (input.dryRun) parts.push("--dry-run");
10892
10980
  if (input.continueOnError) parts.push("--continue-on-error");
10893
10981
  if (input.keepWorktree) parts.push("--keep-worktree");
@@ -11211,6 +11299,7 @@ var RecoverWorkflowService = class {
11211
11299
  runner,
11212
11300
  command: record.command,
11213
11301
  choice: record.choice,
11302
+ profile: record.profile,
11214
11303
  workflowPath: record.workflowPath,
11215
11304
  job: recoveredJob,
11216
11305
  inputs: record.inputs,
@@ -11456,4 +11545,4 @@ Object.defineProperty(exports, "workflowShellOutput", {
11456
11545
  }
11457
11546
  });
11458
11547
 
11459
- //# sourceMappingURL=RecoverWorkflowService-BLehH1qi.cjs.map
11548
+ //# sourceMappingURL=RecoverWorkflowService-CbyTPDY-.cjs.map