libretto 0.2.4 → 0.2.6

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.
@@ -1,21 +1,23 @@
1
- import { appendFileSync, existsSync } from "node:fs";
2
- import { mkdir, writeFile } from "node:fs/promises";
1
+ import { appendFileSync, existsSync, readFileSync } from "node:fs";
2
+ import { writeFile } from "node:fs/promises";
3
3
  import { cwd } from "node:process";
4
4
  import { isAbsolute, resolve } from "node:path";
5
5
  import { pathToFileURL } from "node:url";
6
6
  import {
7
7
  launchBrowser
8
8
  } from "../../index.js";
9
+ import { setSessionForPause } from "../../shared/debug/pause.js";
10
+ import { parseSessionStateContent } from "../../shared/state/index.js";
9
11
  import { getProfilePath, normalizeDomain } from "../core/browser.js";
10
12
  import {
11
13
  getSessionActionsLogPath,
12
- getSessionDir,
13
- getSessionNetworkLogPath
14
+ getSessionNetworkLogPath,
15
+ getSessionStatePath
14
16
  } from "../core/context.js";
15
17
  import { getPauseSignalPaths, removeSignalIfExists } from "../core/pause-signals.js";
16
18
  import { installSessionTelemetry } from "../core/session-telemetry.js";
17
19
  const LIBRETTO_WORKFLOW_BRAND = /* @__PURE__ */ Symbol.for("libretto.workflow");
18
- const RESUME_POLL_INTERVAL_MS = 250;
20
+ const FAILURE_HOLD_POLL_INTERVAL_MS = 250;
19
21
  function mirrorStdoutToFile(filePath) {
20
22
  const stdout = process.stdout;
21
23
  const originalWrite = stdout.write.bind(stdout);
@@ -31,23 +33,32 @@ function mirrorStdoutToFile(filePath) {
31
33
  stdout.write = originalWrite;
32
34
  };
33
35
  }
34
- async function waitForResumeSignal(args) {
35
- const { pausedSignalPath, resumeSignalPath } = args.signalPaths;
36
- await mkdir(getSessionDir(args.session), { recursive: true });
37
- await removeSignalIfExists(resumeSignalPath);
38
- await writeFile(
39
- pausedSignalPath,
40
- JSON.stringify(args.details, null, 2),
41
- "utf8"
42
- );
43
- await args.onPaused?.(args.details);
44
- while (!existsSync(resumeSignalPath)) {
36
+ function readSessionStatePid(session) {
37
+ const statePath = getSessionStatePath(session);
38
+ if (!existsSync(statePath)) return null;
39
+ try {
40
+ return parseSessionStateContent(readFileSync(statePath, "utf8"), statePath).pid;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+ async function waitForFailureSessionRelease(args) {
46
+ const { session, expectedPid, logger } = args;
47
+ logger.info("run-failure-session-hold", { session, expectedPid });
48
+ while (true) {
49
+ const currentPid = readSessionStatePid(session);
50
+ if (currentPid !== expectedPid) {
51
+ logger.info("run-failure-session-released", {
52
+ session,
53
+ expectedPid,
54
+ currentPid
55
+ });
56
+ return;
57
+ }
45
58
  await new Promise(
46
- (resolveWait) => setTimeout(resolveWait, RESUME_POLL_INTERVAL_MS)
59
+ (resolveWait) => setTimeout(resolveWait, FAILURE_HOLD_POLL_INTERVAL_MS)
47
60
  );
48
61
  }
49
- await removeSignalIfExists(resumeSignalPath);
50
- await removeSignalIfExists(pausedSignalPath);
51
62
  }
52
63
  function isLoadedLibrettoWorkflow(value) {
53
64
  if (!value || typeof value !== "object") return false;
@@ -57,13 +68,6 @@ function isLoadedLibrettoWorkflow(value) {
57
68
  function resolveLocalAuthProfilePath(domain) {
58
69
  return getProfilePath(normalizeDomain(domain));
59
70
  }
60
- function resolveWorkflowStorageStatePath(workflow) {
61
- const authProfile = workflow.metadata.authProfile;
62
- if (authProfile?.type !== "local") {
63
- return void 0;
64
- }
65
- return resolveLocalAuthProfilePath(authProfile.domain);
66
- }
67
71
  function getMissingLocalAuthProfileError(args) {
68
72
  const normalizedDomain = normalizeDomain(args.domain);
69
73
  return [
@@ -101,7 +105,24 @@ async function loadWorkflowExport(absolutePath, exportName) {
101
105
  }
102
106
  if (!isLoadedLibrettoWorkflow(targetExport)) {
103
107
  throw new Error(
104
- `Export "${exportName}" in ${absolutePath} must be a Libretto workflow instance. Use workflow(...) from "libretto".`
108
+ [
109
+ `Export "${exportName}" in ${absolutePath} is not a valid Libretto workflow.`,
110
+ "",
111
+ 'A workflow must be created using the workflow() function from "libretto":',
112
+ "",
113
+ ' import { workflow } from "libretto";',
114
+ "",
115
+ ` export const ${exportName} = workflow<InputType, OutputType>(`,
116
+ " {},",
117
+ " async (ctx, input) => {",
118
+ " // ctx.page \u2014 Playwright Page instance",
119
+ " // ctx.logger \u2014 MinimalLogger",
120
+ " // ctx.services \u2014 injected dependencies (generic, default {})",
121
+ " // input \u2014 JSON-serializable input matching InputType",
122
+ " return output; // must match OutputType",
123
+ " },",
124
+ " );"
125
+ ].join("\n")
105
126
  );
106
127
  }
107
128
  return targetExport;
@@ -125,12 +146,12 @@ async function runIntegrationInternal(args, options) {
125
146
  integrationExport: args.exportName,
126
147
  session: args.session
127
148
  });
128
- const authProfile = workflow.metadata.authProfile;
129
- const storageStatePath = resolveWorkflowStorageStatePath(workflow);
130
- if (authProfile?.type === "local" && storageStatePath && !existsSync(storageStatePath)) {
149
+ const authProfileDomain = args.authProfileDomain;
150
+ const storageStatePath = authProfileDomain ? resolveLocalAuthProfilePath(authProfileDomain) : void 0;
151
+ if (authProfileDomain && storageStatePath && !existsSync(storageStatePath)) {
131
152
  throw new Error(
132
153
  getMissingLocalAuthProfileError({
133
- domain: authProfile.domain,
154
+ domain: authProfileDomain,
134
155
  profilePath: storageStatePath,
135
156
  session: args.session
136
157
  })
@@ -154,50 +175,35 @@ async function runIntegrationInternal(args, options) {
154
175
  appendFileSync(networkLogPath, JSON.stringify(entry) + "\n");
155
176
  }
156
177
  });
178
+ setSessionForPause(args.session);
157
179
  const workflowContext = {
158
180
  logger: integrationLogger,
159
181
  page: browserSession.page,
160
- context: browserSession.context,
161
- browser: browserSession.browser,
162
- session: args.session,
163
- integrationPath: absolutePath,
164
- exportName: args.exportName,
165
- headless: args.headless,
166
- debug: args.debug,
167
- pause: async () => {
168
- const details = {
169
- sessionName: args.session,
170
- pausedAt: (/* @__PURE__ */ new Date()).toISOString(),
171
- url: browserSession.page.url()
172
- };
173
- console.log(`[pause] Paused at ${details.url}`);
174
- console.log("[pause] Waiting for resume signal...");
175
- await waitForResumeSignal({
176
- signalPaths,
177
- session: args.session,
178
- details,
179
- onPaused: options.onPaused
180
- });
181
- console.log("[pause] Resume signal received. Continuing workflow...");
182
- }
182
+ services: {}
183
183
  };
184
184
  try {
185
185
  try {
186
186
  await workflow.run(workflowContext, args.params ?? {});
187
187
  } catch (error) {
188
+ const errorMessage = error instanceof Error ? error.message : String(error);
188
189
  await writeFile(
189
190
  signalPaths.failedSignalPath,
190
191
  JSON.stringify(
191
192
  {
192
193
  failedAt: (/* @__PURE__ */ new Date()).toISOString(),
193
- message: error instanceof Error ? error.message : String(error)
194
+ message: errorMessage
194
195
  },
195
196
  null,
196
197
  2
197
198
  ),
198
199
  "utf8"
199
200
  );
200
- throw error;
201
+ await waitForFailureSessionRelease({
202
+ session: args.session,
203
+ expectedPid: process.pid,
204
+ logger
205
+ });
206
+ return { status: "failed-held" };
201
207
  }
202
208
  await writeFile(
203
209
  signalPaths.completedSignalPath,
@@ -211,10 +217,9 @@ async function runIntegrationInternal(args, options) {
211
217
  await browserSession.close();
212
218
  }
213
219
  }
214
- async function runIntegrationFromFileInWorker(args, logger, onPaused) {
220
+ async function runIntegrationFromFileInWorker(args, logger) {
215
221
  return await runIntegrationInternal(args, {
216
- logger,
217
- onPaused
222
+ logger
218
223
  });
219
224
  }
220
225
  export {
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ const RunIntegrationWorkerRequestSchema = z.object({
3
+ integrationPath: z.string().min(1),
4
+ exportName: z.string().min(1),
5
+ session: z.string().min(1),
6
+ params: z.unknown(),
7
+ headless: z.boolean(),
8
+ authProfileDomain: z.string().optional()
9
+ });
10
+ export {
11
+ RunIntegrationWorkerRequestSchema
12
+ };
@@ -1,17 +1,14 @@
1
1
  import { writeFile } from "node:fs/promises";
2
+ import { ZodError } from "zod";
3
+ import {
4
+ RunIntegrationWorkerRequestSchema
5
+ } from "./run-integration-worker-protocol.js";
2
6
  import { runIntegrationFromFileInWorker } from "./run-integration-runtime.js";
3
7
  import {
4
8
  ensureLibrettoSetup,
5
9
  withSessionLogger
6
10
  } from "../core/context.js";
7
11
  import { getPauseSignalPaths } from "../core/pause-signals.js";
8
- function sendMessage(message) {
9
- if (typeof process.send !== "function" || !process.connected) return;
10
- try {
11
- process.send(message);
12
- } catch {
13
- }
14
- }
15
12
  function parseWorkerRequest(argv) {
16
13
  const rawPayload = argv[2];
17
14
  if (!rawPayload) {
@@ -25,21 +22,15 @@ function parseWorkerRequest(argv) {
25
22
  `Invalid worker payload JSON: ${error instanceof Error ? error.message : String(error)}`
26
23
  );
27
24
  }
28
- if (!parsed || typeof parsed !== "object") {
29
- throw new Error("Worker payload must be an object.");
30
- }
31
- const candidate = parsed;
32
- if (typeof candidate.integrationPath !== "string" || typeof candidate.exportName !== "string" || typeof candidate.session !== "string" || typeof candidate.headless !== "boolean" || typeof candidate.debug !== "boolean" || !("params" in candidate)) {
33
- throw new Error("Worker payload is missing required fields.");
25
+ try {
26
+ return RunIntegrationWorkerRequestSchema.parse(parsed);
27
+ } catch (error) {
28
+ if (error instanceof ZodError) {
29
+ const details = error.issues.map((issue) => `${issue.path.join(".") || "root"}: ${issue.message}`).join("; ");
30
+ throw new Error(`Worker payload is invalid: ${details}`);
31
+ }
32
+ throw error;
34
33
  }
35
- return {
36
- integrationPath: candidate.integrationPath,
37
- exportName: candidate.exportName,
38
- session: candidate.session,
39
- headless: candidate.headless,
40
- debug: candidate.debug,
41
- params: candidate.params
42
- };
43
34
  }
44
35
  async function main() {
45
36
  let request = null;
@@ -49,15 +40,8 @@ async function main() {
49
40
  const workerRequest = request;
50
41
  ensureLibrettoSetup();
51
42
  await withSessionLogger(workerRequest.session, async (logger) => {
52
- await runIntegrationFromFileInWorker(
53
- workerRequest,
54
- logger,
55
- async (details) => {
56
- sendMessage({ type: "paused", details });
57
- }
58
- );
43
+ await runIntegrationFromFileInWorker(workerRequest, logger);
59
44
  });
60
- sendMessage({ type: "completed" });
61
45
  } catch (error) {
62
46
  const message = error instanceof Error ? error.message : String(error);
63
47
  if (request) {
@@ -75,7 +59,6 @@ async function main() {
75
59
  "utf8"
76
60
  );
77
61
  }
78
- sendMessage({ type: "failed", message });
79
62
  exitCode = 1;
80
63
  }
81
64
  process.exit(exitCode);
package/dist/index.cjs CHANGED
@@ -18,11 +18,9 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var index_exports = {};
20
20
  __export(index_exports, {
21
- DebugPauseSignal: () => import_pause.DebugPauseSignal,
22
21
  LIBRETTO_WORKFLOW_BRAND: () => import_workflow.LIBRETTO_WORKFLOW_BRAND,
23
22
  LibrettoWorkflow: () => import_workflow.LibrettoWorkflow,
24
23
  Logger: () => import_logger.Logger,
25
- RunDebugPauseSignal: () => import_api.DebugPauseSignal,
26
24
  SESSION_STATE_VERSION: () => import_state.SESSION_STATE_VERSION,
27
25
  SessionStateFileSchema: () => import_state.SessionStateFileSchema,
28
26
  SessionStatusSchema: () => import_state.SessionStatusSchema,
@@ -30,7 +28,6 @@ __export(index_exports, {
30
28
  clearHighlights: () => import_highlight.clearHighlights,
31
29
  createFileLogSink: () => import_sinks.createFileLogSink,
32
30
  createLLMClientFromModel: () => import_ai_sdk_adapter.createLLMClientFromModel,
33
- debugPause: () => import_pause.debugPause,
34
31
  defaultLogger: () => import_logger.defaultLogger,
35
32
  detectSubmissionError: () => import_errors.detectSubmissionError,
36
33
  downloadAndSave: () => import_download.downloadAndSave,
@@ -45,17 +42,15 @@ __export(index_exports, {
45
42
  instrumentContext: () => import_instrument.instrumentContext,
46
43
  instrumentPage: () => import_instrument.instrumentPage,
47
44
  isDebugMode: () => import_config.isDebugMode,
48
- isDebugPauseSignal: () => import_pause.isDebugPauseSignal,
49
45
  isDryRun: () => import_config.isDryRun,
50
- isRunDebugPauseSignal: () => import_api.isDebugPauseSignal,
51
46
  jsonlConsoleSink: () => import_sinks.jsonlConsoleSink,
52
47
  launchBrowser: () => import_api.launchBrowser,
53
48
  moveGhostCursor: () => import_ghost_cursor.moveGhostCursor,
54
49
  pageRequest: () => import_network.pageRequest,
55
50
  parseSessionStateContent: () => import_state.parseSessionStateContent,
56
51
  parseSessionStateData: () => import_state.parseSessionStateData,
52
+ pause: () => import_pause.pause,
57
53
  prettyConsoleSink: () => import_sinks.prettyConsoleSink,
58
- runDebugPause: () => import_api.debugPause,
59
54
  serializeSessionState: () => import_state.serializeSessionState,
60
55
  shouldPauseBeforeMutation: () => import_config.shouldPauseBeforeMutation,
61
56
  showHighlight: () => import_highlight.showHighlight,
@@ -81,11 +76,9 @@ var import_api = require("./shared/run/api.js");
81
76
  var import_workflow = require("./shared/workflow/workflow.js");
82
77
  // Annotate the CommonJS export names for ESM import in node:
83
78
  0 && (module.exports = {
84
- DebugPauseSignal,
85
79
  LIBRETTO_WORKFLOW_BRAND,
86
80
  LibrettoWorkflow,
87
81
  Logger,
88
- RunDebugPauseSignal,
89
82
  SESSION_STATE_VERSION,
90
83
  SessionStateFileSchema,
91
84
  SessionStatusSchema,
@@ -93,7 +86,6 @@ var import_workflow = require("./shared/workflow/workflow.js");
93
86
  clearHighlights,
94
87
  createFileLogSink,
95
88
  createLLMClientFromModel,
96
- debugPause,
97
89
  defaultLogger,
98
90
  detectSubmissionError,
99
91
  downloadAndSave,
@@ -108,17 +100,15 @@ var import_workflow = require("./shared/workflow/workflow.js");
108
100
  instrumentContext,
109
101
  instrumentPage,
110
102
  isDebugMode,
111
- isDebugPauseSignal,
112
103
  isDryRun,
113
- isRunDebugPauseSignal,
114
104
  jsonlConsoleSink,
115
105
  launchBrowser,
116
106
  moveGhostCursor,
117
107
  pageRequest,
118
108
  parseSessionStateContent,
119
109
  parseSessionStateData,
110
+ pause,
120
111
  prettyConsoleSink,
121
- runDebugPause,
122
112
  serializeSessionState,
123
113
  shouldPauseBeforeMutation,
124
114
  showHighlight,
package/dist/index.d.cts CHANGED
@@ -9,13 +9,13 @@ export { DetectedSubmissionError, KnownSubmissionError, detectSubmissionError }
9
9
  export { ExtractOptions, extractFromPage } from './runtime/extract/extract.cjs';
10
10
  export { PageRequestOptions, RequestConfig, pageRequest } from './runtime/network/network.cjs';
11
11
  export { DownloadResult, DownloadViaClickOptions, SaveDownloadOptions, downloadAndSave, downloadViaClick } from './runtime/download/download.cjs';
12
- export { DebugPauseContext, DebugPauseDetails, DebugPauseSignal, DebugPauseContext as RunDebugPauseContext, DebugPauseDetails as RunDebugPauseDetails, DebugPauseSignal as RunDebugPauseSignal, debugPause, isDebugPauseSignal, isDebugPauseSignal as isRunDebugPauseSignal, debugPause as runDebugPause } from './shared/debug/pause.cjs';
12
+ export { pause } from './shared/debug/pause.cjs';
13
13
  export { isDebugMode, isDryRun, shouldPauseBeforeMutation } from './shared/config/config.cjs';
14
14
  export { InstrumentationOptions, InstrumentedPage, installInstrumentation, instrumentContext, instrumentPage } from './shared/instrumentation/instrument.cjs';
15
15
  export { GhostCursorOptions, ensureGhostCursor, ghostClick, hideGhostCursor, moveGhostCursor } from './shared/visualization/ghost-cursor.cjs';
16
16
  export { HighlightOptions, clearHighlights, ensureHighlightLayer, showHighlight } from './shared/visualization/highlight.cjs';
17
17
  export { BrowserSession, LaunchBrowserArgs, launchBrowser } from './shared/run/browser.cjs';
18
- export { LIBRETTO_WORKFLOW_BRAND, LibrettoAuthProfile, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, LibrettoWorkflowMetadata, workflow } from './shared/workflow/workflow.cjs';
18
+ export { LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, LibrettoWorkflowMetadata, workflow } from './shared/workflow/workflow.cjs';
19
19
  import 'zod';
20
20
  import 'ai';
21
21
  import 'playwright';
package/dist/index.d.ts CHANGED
@@ -9,13 +9,13 @@ export { DetectedSubmissionError, KnownSubmissionError, detectSubmissionError }
9
9
  export { ExtractOptions, extractFromPage } from './runtime/extract/extract.js';
10
10
  export { PageRequestOptions, RequestConfig, pageRequest } from './runtime/network/network.js';
11
11
  export { DownloadResult, DownloadViaClickOptions, SaveDownloadOptions, downloadAndSave, downloadViaClick } from './runtime/download/download.js';
12
- export { DebugPauseContext, DebugPauseDetails, DebugPauseSignal, DebugPauseContext as RunDebugPauseContext, DebugPauseDetails as RunDebugPauseDetails, DebugPauseSignal as RunDebugPauseSignal, debugPause, isDebugPauseSignal, isDebugPauseSignal as isRunDebugPauseSignal, debugPause as runDebugPause } from './shared/debug/pause.js';
12
+ export { pause } from './shared/debug/pause.js';
13
13
  export { isDebugMode, isDryRun, shouldPauseBeforeMutation } from './shared/config/config.js';
14
14
  export { InstrumentationOptions, InstrumentedPage, installInstrumentation, instrumentContext, instrumentPage } from './shared/instrumentation/instrument.js';
15
15
  export { GhostCursorOptions, ensureGhostCursor, ghostClick, hideGhostCursor, moveGhostCursor } from './shared/visualization/ghost-cursor.js';
16
16
  export { HighlightOptions, clearHighlights, ensureHighlightLayer, showHighlight } from './shared/visualization/highlight.js';
17
17
  export { BrowserSession, LaunchBrowserArgs, launchBrowser } from './shared/run/browser.js';
18
- export { LIBRETTO_WORKFLOW_BRAND, LibrettoAuthProfile, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, LibrettoWorkflowMetadata, workflow } from './shared/workflow/workflow.js';
18
+ export { LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, LibrettoWorkflowMetadata, workflow } from './shared/workflow/workflow.js';
19
19
  import 'zod';
20
20
  import 'ai';
21
21
  import 'playwright';
package/dist/index.js CHANGED
@@ -26,11 +26,7 @@ import {
26
26
  downloadViaClick,
27
27
  downloadAndSave
28
28
  } from "./runtime/download/download.js";
29
- import {
30
- debugPause,
31
- DebugPauseSignal,
32
- isDebugPauseSignal
33
- } from "./shared/debug/pause.js";
29
+ import { pause } from "./shared/debug/pause.js";
34
30
  import {
35
31
  isDebugMode,
36
32
  isDryRun,
@@ -53,10 +49,7 @@ import {
53
49
  clearHighlights
54
50
  } from "./shared/visualization/highlight.js";
55
51
  import {
56
- launchBrowser,
57
- debugPause as debugPause2,
58
- DebugPauseSignal as DebugPauseSignal2,
59
- isDebugPauseSignal as isDebugPauseSignal2
52
+ launchBrowser
60
53
  } from "./shared/run/api.js";
61
54
  import {
62
55
  LibrettoWorkflow,
@@ -64,11 +57,9 @@ import {
64
57
  workflow
65
58
  } from "./shared/workflow/workflow.js";
66
59
  export {
67
- DebugPauseSignal,
68
60
  LIBRETTO_WORKFLOW_BRAND,
69
61
  LibrettoWorkflow,
70
62
  Logger,
71
- DebugPauseSignal2 as RunDebugPauseSignal,
72
63
  SESSION_STATE_VERSION,
73
64
  SessionStateFileSchema,
74
65
  SessionStatusSchema,
@@ -76,7 +67,6 @@ export {
76
67
  clearHighlights,
77
68
  createFileLogSink,
78
69
  createLLMClientFromModel,
79
- debugPause,
80
70
  defaultLogger,
81
71
  detectSubmissionError,
82
72
  downloadAndSave,
@@ -91,17 +81,15 @@ export {
91
81
  instrumentContext,
92
82
  instrumentPage,
93
83
  isDebugMode,
94
- isDebugPauseSignal,
95
84
  isDryRun,
96
- isDebugPauseSignal2 as isRunDebugPauseSignal,
97
85
  jsonlConsoleSink,
98
86
  launchBrowser,
99
87
  moveGhostCursor,
100
88
  pageRequest,
101
89
  parseSessionStateContent,
102
90
  parseSessionStateData,
91
+ pause,
103
92
  prettyConsoleSink,
104
- debugPause2 as runDebugPause,
105
93
  serializeSessionState,
106
94
  shouldPauseBeforeMutation,
107
95
  showHighlight,
@@ -18,15 +18,13 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var debug_exports = {};
20
20
  __export(debug_exports, {
21
- DebugPauseSignal: () => import_pause.DebugPauseSignal,
22
- debugPause: () => import_pause.debugPause,
23
- isDebugPauseSignal: () => import_pause.isDebugPauseSignal
21
+ pause: () => import_pause.pause,
22
+ setSessionForPause: () => import_pause.setSessionForPause
24
23
  });
25
24
  module.exports = __toCommonJS(debug_exports);
26
25
  var import_pause = require("./pause.js");
27
26
  // Annotate the CommonJS export names for ESM import in node:
28
27
  0 && (module.exports = {
29
- DebugPauseSignal,
30
- debugPause,
31
- isDebugPauseSignal
28
+ pause,
29
+ setSessionForPause
32
30
  });
@@ -1,2 +1 @@
1
- export { DebugPauseContext, DebugPauseDetails, DebugPauseSignal, debugPause, isDebugPauseSignal } from './pause.cjs';
2
- import 'playwright';
1
+ export { pause, setSessionForPause } from './pause.cjs';
@@ -1,2 +1 @@
1
- export { DebugPauseContext, DebugPauseDetails, DebugPauseSignal, debugPause, isDebugPauseSignal } from './pause.js';
2
- import 'playwright';
1
+ export { pause, setSessionForPause } from './pause.js';
@@ -1,10 +1,5 @@
1
- import {
2
- debugPause,
3
- DebugPauseSignal,
4
- isDebugPauseSignal
5
- } from "./pause.js";
1
+ import { pause, setSessionForPause } from "./pause.js";
6
2
  export {
7
- DebugPauseSignal,
8
- debugPause,
9
- isDebugPauseSignal
3
+ pause,
4
+ setSessionForPause
10
5
  };
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,42 +17,74 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var pause_exports = {};
20
30
  __export(pause_exports, {
21
- DebugPauseSignal: () => DebugPauseSignal,
22
- debugPause: () => debugPause,
23
- isDebugPauseSignal: () => isDebugPauseSignal
31
+ pause: () => pause,
32
+ setSessionForPause: () => setSessionForPause
24
33
  });
25
34
  module.exports = __toCommonJS(pause_exports);
26
- class DebugPauseSignal extends Error {
27
- details;
28
- constructor(details) {
29
- super(`Workflow paused at ${details.url}`);
30
- this.name = "DebugPauseSignal";
31
- this.details = details;
35
+ var import_node_fs = require("node:fs");
36
+ var import_promises = require("node:fs/promises");
37
+ let _sessionName;
38
+ function setSessionForPause(session) {
39
+ _sessionName = session;
40
+ }
41
+ function getSessionFromProcessArgs() {
42
+ const rawPayload = process.argv[2];
43
+ if (!rawPayload) return void 0;
44
+ try {
45
+ const parsed = JSON.parse(rawPayload);
46
+ return typeof parsed.session === "string" ? parsed.session : void 0;
47
+ } catch {
48
+ return void 0;
32
49
  }
33
50
  }
34
- function isDebugPauseSignal(error) {
35
- if (!error || typeof error !== "object") return false;
36
- const candidate = error;
37
- if (candidate.name !== "DebugPauseSignal") return false;
38
- return typeof candidate.details?.sessionName === "string" && typeof candidate.details?.pausedAt === "string" && typeof candidate.details?.url === "string";
51
+ function resolveSession() {
52
+ return _sessionName ?? getSessionFromProcessArgs();
39
53
  }
40
- async function debugPause(context) {
41
- const url = context.page.url();
54
+ async function pause() {
55
+ if (process.env.NODE_ENV === "production") {
56
+ return;
57
+ }
58
+ const session = resolveSession();
59
+ if (!session) {
60
+ return;
61
+ }
62
+ const { getPauseSignalPaths, removeSignalIfExists } = await import("../../cli/core/pause-signals.js");
63
+ const { getSessionDir } = await import("../../cli/core/context.js");
64
+ const signalPaths = getPauseSignalPaths(session);
65
+ const { pausedSignalPath, resumeSignalPath } = signalPaths;
66
+ await (0, import_promises.mkdir)(getSessionDir(session), { recursive: true });
67
+ await removeSignalIfExists(resumeSignalPath);
42
68
  const details = {
43
- sessionName: context.session,
69
+ sessionName: session,
44
70
  pausedAt: (/* @__PURE__ */ new Date()).toISOString(),
45
- url
71
+ url: "unknown"
46
72
  };
47
- console.log(`[debugPause] Paused at ${url}`);
48
- console.log("[debugPause] Signaling pause to supervisor...");
49
- throw new DebugPauseSignal(details);
73
+ await (0, import_promises.writeFile)(pausedSignalPath, JSON.stringify(details, null, 2), "utf8");
74
+ console.log(`[pause] Paused (session: ${session})`);
75
+ console.log("[pause] Waiting for resume signal...");
76
+ const RESUME_POLL_INTERVAL_MS = 250;
77
+ while (!(0, import_node_fs.existsSync)(resumeSignalPath)) {
78
+ await new Promise(
79
+ (resolve) => setTimeout(resolve, RESUME_POLL_INTERVAL_MS)
80
+ );
81
+ }
82
+ await removeSignalIfExists(resumeSignalPath);
83
+ await removeSignalIfExists(pausedSignalPath);
84
+ console.log("[pause] Resume signal received. Continuing workflow...");
50
85
  }
51
86
  // Annotate the CommonJS export names for ESM import in node:
52
87
  0 && (module.exports = {
53
- DebugPauseSignal,
54
- debugPause,
55
- isDebugPauseSignal
88
+ pause,
89
+ setSessionForPause
56
90
  });
@@ -1,23 +1,16 @@
1
- import { Page } from 'playwright';
2
-
3
- type DebugPauseContext = {
4
- page: Page;
5
- session: string;
6
- };
7
- type DebugPauseDetails = {
8
- sessionName: string;
9
- pausedAt: string;
10
- url: string;
11
- };
12
- declare class DebugPauseSignal extends Error {
13
- readonly details: DebugPauseDetails;
14
- constructor(details: DebugPauseDetails);
15
- }
16
- declare function isDebugPauseSignal(error: unknown): error is DebugPauseSignal;
17
1
  /**
18
- * Signals a workflow pause to the caller.
19
- * This always throws a typed signal that supervisors can intercept.
2
+ * Called by the CLI runtime to make the session name available to `pause()`.
3
+ */
4
+ declare function setSessionForPause(session: string): void;
5
+ /**
6
+ * Standalone pause function.
7
+ *
8
+ * - In production (`NODE_ENV === "production"`), returns immediately (no-op).
9
+ * - Otherwise, writes a `.paused` signal file and polls for a `.resume` signal,
10
+ * using the same file-based mechanism as the CLI runner.
11
+ *
12
+ * Import directly: `import { pause } from "libretto";`
20
13
  */
21
- declare function debugPause(context: DebugPauseContext): Promise<never>;
14
+ declare function pause(): Promise<void>;
22
15
 
23
- export { type DebugPauseContext, type DebugPauseDetails, DebugPauseSignal, debugPause, isDebugPauseSignal };
16
+ export { pause, setSessionForPause };