appduct 0.14.0 → 0.15.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.
Files changed (55) hide show
  1. package/README.md +52 -22
  2. package/dist/artifact-inspect.d.ts +38 -39
  3. package/dist/bin.js +45 -3
  4. package/dist/cli/result-types.d.ts +27 -1
  5. package/dist/cli/routes/daemon/index.js +32 -1
  6. package/dist/cli/routes/daemon/run.js +962 -521
  7. package/dist/cli/routes/daemon/start.js +40 -1
  8. package/dist/cli/routes/daemon/status.js +41 -1
  9. package/dist/cli/routes/daemon/stop.js +40 -1
  10. package/dist/cli/routes/doctor.js +40 -2
  11. package/dist/cli/routes/events/index.js +32 -1
  12. package/dist/cli/routes/events/ls.js +40 -1
  13. package/dist/cli/routes/events/since.js +40 -1
  14. package/dist/cli/routes/events/tail.js +40 -1
  15. package/dist/cli/routes/init.js +207 -107
  16. package/dist/cli/routes/keygen.js +32 -1
  17. package/dist/cli/routes/mcp.js +135 -24
  18. package/dist/cli/routes/sessions/index.js +32 -1
  19. package/dist/cli/routes/sessions/link.js +141 -21
  20. package/dist/cli/routes/sessions/ls.js +40 -1
  21. package/dist/cli/routes/sessions/revoke.js +40 -1
  22. package/dist/cli/routes/tools/call.js +40 -1
  23. package/dist/cli/routes/tools/describe.js +40 -1
  24. package/dist/cli/routes/tools/index.js +32 -1
  25. package/dist/cli/routes/tools/ls.js +40 -1
  26. package/dist/client/attach-page.d.ts +39 -0
  27. package/dist/client/bootstrap.d.ts +17 -3
  28. package/dist/client/index.d.ts +2 -1
  29. package/dist/client/index.js +286 -112
  30. package/dist/commands/doctor.d.ts +3 -4
  31. package/dist/commands/init.d.ts +8 -2
  32. package/dist/commands/link.d.ts +10 -1
  33. package/dist/daemon/calls.d.ts +1 -1
  34. package/dist/daemon/config.d.ts +6 -0
  35. package/dist/daemon/daemon.d.ts +6 -1
  36. package/dist/daemon/links.d.ts +9 -5
  37. package/dist/daemon/listener.d.ts +28 -1
  38. package/dist/daemon/rpc-errors.d.ts +3 -0
  39. package/dist/daemon/sessions.d.ts +11 -7
  40. package/dist/daemon/tls.d.ts +0 -3
  41. package/dist/devtools-relay/attach-tab.d.ts +33 -0
  42. package/dist/devtools-relay/index.d.ts +8 -0
  43. package/dist/devtools-relay/memory-daemon-sockets.d.ts +20 -0
  44. package/dist/devtools-relay/memory-devtools-browser.d.ts +10 -0
  45. package/dist/devtools-relay/memory-page-channel.d.ts +16 -0
  46. package/dist/devtools-relay/node-daemon-socket.d.ts +3 -0
  47. package/dist/devtools-relay/node-devtools-browser.d.ts +3 -0
  48. package/dist/devtools-relay/ports.d.ts +48 -0
  49. package/dist/devtools-relay/relay.d.ts +16 -0
  50. package/dist/index.js +45 -3
  51. package/dist/link.d.ts +35 -1
  52. package/dist/mcp/connect-tool.d.ts +15 -2
  53. package/dist/native-scheme.d.ts +11 -7
  54. package/dist/scheme.d.ts +5 -1
  55. package/package.json +15 -5
@@ -24,6 +24,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
24
24
  "daemonLogMaxBytes",
25
25
  "eventsLogMaxBytes",
26
26
  "policy",
27
+ "webOrigins",
27
28
  "advertisedIp",
28
29
  "scheme",
29
30
  "restartDaemonOnVersionMismatch"
@@ -84,6 +85,7 @@ var defaultConfig = (paths) => {
84
85
  daemonLogMaxBytes: DEFAULT_DAEMON_LOG_MAX_BYTES,
85
86
  eventsLogMaxBytes: 10 * 1024 * 1024,
86
87
  restartDaemonOnVersionMismatch: false,
88
+ webOrigins: [],
87
89
  policy: {
88
90
  default: "allow",
89
91
  destructive: "allow"
@@ -154,6 +156,12 @@ var loadConfig = async (paths, options = {}) => {
154
156
  "restartDaemonOnVersionMismatch"
155
157
  );
156
158
  }
159
+ if (parsed.webOrigins !== void 0) {
160
+ if (!Array.isArray(parsed.webOrigins)) {
161
+ throw configError("webOrigins", "must be an array of origin strings.");
162
+ }
163
+ config.webOrigins = parsed.webOrigins.map((origin) => requireNonEmptyString(origin, "webOrigins"));
164
+ }
157
165
  if (parsed.policy !== void 0) {
158
166
  if (typeof parsed.policy !== "object" || parsed.policy === null || Array.isArray(parsed.policy)) {
159
167
  throw configError("policy", "must be an object.");
@@ -923,7 +931,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
923
931
  // EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
924
932
  // missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
925
933
  // code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
926
- // assertion failure (docs/tasks/08-appduct-doctor.md).
934
+ // assertion failure.
927
935
  inspection_error: 66,
928
936
  connection_error: 70,
929
937
  session_error: 71,
@@ -1387,6 +1395,36 @@ var renderInvokeData = (colors, data, flags) => {
1387
1395
  return [colors.green("Result"), formatScalar(data, flags)];
1388
1396
  };
1389
1397
  var renderLinkData = (colors, data, flags, qr) => {
1398
+ if ("attached" in data) {
1399
+ return [
1400
+ colors.green("Page Attached"),
1401
+ ...renderFields(
1402
+ "Page",
1403
+ [
1404
+ ["Session", data.sessionId],
1405
+ ["Tab", data.targetId],
1406
+ ["URL", data.url],
1407
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1408
+ ],
1409
+ flags
1410
+ )
1411
+ ];
1412
+ }
1413
+ if ("script" in data) {
1414
+ return [
1415
+ colors.green("Link Created"),
1416
+ ...renderFields(
1417
+ "Link",
1418
+ [
1419
+ ["Session", data.sessionId],
1420
+ ["URL", data.url],
1421
+ ["Script", data.script],
1422
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1423
+ ],
1424
+ flags
1425
+ )
1426
+ ];
1427
+ }
1390
1428
  const lines = [
1391
1429
  colors.green("Link Created"),
1392
1430
  ...renderFields(
@@ -1507,6 +1545,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
1507
1545
  ["PID", data.daemon.pid],
1508
1546
  ["Started at", data.daemon.started_at],
1509
1547
  ["WSS port", data.daemon.wss_port],
1548
+ ["Web port", data.daemon.web_port],
1510
1549
  ["Pinned keys", data.daemon.pinned_keys.length],
1511
1550
  ["Sessions", data.daemon.session_count]
1512
1551
  ],
@@ -44,6 +44,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
44
44
  "daemonLogMaxBytes",
45
45
  "eventsLogMaxBytes",
46
46
  "policy",
47
+ "webOrigins",
47
48
  "advertisedIp",
48
49
  "scheme",
49
50
  "restartDaemonOnVersionMismatch"
@@ -104,6 +105,7 @@ var defaultConfig = (paths) => {
104
105
  daemonLogMaxBytes: DEFAULT_DAEMON_LOG_MAX_BYTES,
105
106
  eventsLogMaxBytes: 10 * 1024 * 1024,
106
107
  restartDaemonOnVersionMismatch: false,
108
+ webOrigins: [],
107
109
  policy: {
108
110
  default: "allow",
109
111
  destructive: "allow"
@@ -174,6 +176,12 @@ var loadConfig = async (paths, options = {}) => {
174
176
  "restartDaemonOnVersionMismatch"
175
177
  );
176
178
  }
179
+ if (parsed.webOrigins !== void 0) {
180
+ if (!Array.isArray(parsed.webOrigins)) {
181
+ throw configError("webOrigins", "must be an array of origin strings.");
182
+ }
183
+ config.webOrigins = parsed.webOrigins.map((origin) => requireNonEmptyString(origin, "webOrigins"));
184
+ }
177
185
  if (parsed.policy !== void 0) {
178
186
  if (typeof parsed.policy !== "object" || parsed.policy === null || Array.isArray(parsed.policy)) {
179
187
  throw configError("policy", "must be an object.");
@@ -1077,7 +1085,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
1077
1085
  // EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
1078
1086
  // missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
1079
1087
  // code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
1080
- // assertion failure (docs/tasks/08-appduct-doctor.md).
1088
+ // assertion failure.
1081
1089
  inspection_error: 66,
1082
1090
  connection_error: 70,
1083
1091
  session_error: 71,
@@ -1575,6 +1583,36 @@ var renderInvokeData = (colors, data, flags) => {
1575
1583
  return [colors.green("Result"), formatScalar(data, flags)];
1576
1584
  };
1577
1585
  var renderLinkData = (colors, data, flags, qr) => {
1586
+ if ("attached" in data) {
1587
+ return [
1588
+ colors.green("Page Attached"),
1589
+ ...renderFields(
1590
+ "Page",
1591
+ [
1592
+ ["Session", data.sessionId],
1593
+ ["Tab", data.targetId],
1594
+ ["URL", data.url],
1595
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1596
+ ],
1597
+ flags
1598
+ )
1599
+ ];
1600
+ }
1601
+ if ("script" in data) {
1602
+ return [
1603
+ colors.green("Link Created"),
1604
+ ...renderFields(
1605
+ "Link",
1606
+ [
1607
+ ["Session", data.sessionId],
1608
+ ["URL", data.url],
1609
+ ["Script", data.script],
1610
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1611
+ ],
1612
+ flags
1613
+ )
1614
+ ];
1615
+ }
1578
1616
  const lines = [
1579
1617
  colors.green("Link Created"),
1580
1618
  ...renderFields(
@@ -1695,6 +1733,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
1695
1733
  ["PID", data.daemon.pid],
1696
1734
  ["Started at", data.daemon.started_at],
1697
1735
  ["WSS port", data.daemon.wss_port],
1736
+ ["Web port", data.daemon.web_port],
1698
1737
  ["Pinned keys", data.daemon.pinned_keys.length],
1699
1738
  ["Sessions", data.daemon.session_count]
1700
1739
  ],
@@ -24,6 +24,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
24
24
  "daemonLogMaxBytes",
25
25
  "eventsLogMaxBytes",
26
26
  "policy",
27
+ "webOrigins",
27
28
  "advertisedIp",
28
29
  "scheme",
29
30
  "restartDaemonOnVersionMismatch"
@@ -84,6 +85,7 @@ var defaultConfig = (paths) => {
84
85
  daemonLogMaxBytes: DEFAULT_DAEMON_LOG_MAX_BYTES,
85
86
  eventsLogMaxBytes: 10 * 1024 * 1024,
86
87
  restartDaemonOnVersionMismatch: false,
88
+ webOrigins: [],
87
89
  policy: {
88
90
  default: "allow",
89
91
  destructive: "allow"
@@ -154,6 +156,12 @@ var loadConfig = async (paths, options = {}) => {
154
156
  "restartDaemonOnVersionMismatch"
155
157
  );
156
158
  }
159
+ if (parsed.webOrigins !== void 0) {
160
+ if (!Array.isArray(parsed.webOrigins)) {
161
+ throw configError("webOrigins", "must be an array of origin strings.");
162
+ }
163
+ config.webOrigins = parsed.webOrigins.map((origin) => requireNonEmptyString(origin, "webOrigins"));
164
+ }
157
165
  if (parsed.policy !== void 0) {
158
166
  if (typeof parsed.policy !== "object" || parsed.policy === null || Array.isArray(parsed.policy)) {
159
167
  throw configError("policy", "must be an object.");
@@ -913,7 +921,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
913
921
  // EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
914
922
  // missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
915
923
  // code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
916
- // assertion failure (docs/tasks/08-appduct-doctor.md).
924
+ // assertion failure.
917
925
  inspection_error: 66,
918
926
  connection_error: 70,
919
927
  session_error: 71,
@@ -1517,6 +1525,36 @@ var renderInvokeData = (colors, data, flags) => {
1517
1525
  return [colors.green("Result"), formatScalar(data, flags)];
1518
1526
  };
1519
1527
  var renderLinkData = (colors, data, flags, qr) => {
1528
+ if ("attached" in data) {
1529
+ return [
1530
+ colors.green("Page Attached"),
1531
+ ...renderFields(
1532
+ "Page",
1533
+ [
1534
+ ["Session", data.sessionId],
1535
+ ["Tab", data.targetId],
1536
+ ["URL", data.url],
1537
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1538
+ ],
1539
+ flags
1540
+ )
1541
+ ];
1542
+ }
1543
+ if ("script" in data) {
1544
+ return [
1545
+ colors.green("Link Created"),
1546
+ ...renderFields(
1547
+ "Link",
1548
+ [
1549
+ ["Session", data.sessionId],
1550
+ ["URL", data.url],
1551
+ ["Script", data.script],
1552
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1553
+ ],
1554
+ flags
1555
+ )
1556
+ ];
1557
+ }
1520
1558
  const lines = [
1521
1559
  colors.green("Link Created"),
1522
1560
  ...renderFields(
@@ -1637,6 +1675,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
1637
1675
  ["PID", data.daemon.pid],
1638
1676
  ["Started at", data.daemon.started_at],
1639
1677
  ["WSS port", data.daemon.wss_port],
1678
+ ["Web port", data.daemon.web_port],
1640
1679
  ["Pinned keys", data.daemon.pinned_keys.length],
1641
1680
  ["Sessions", data.daemon.session_count]
1642
1681
  ],
@@ -93,7 +93,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
93
93
  // EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
94
94
  // missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
95
95
  // code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
96
- // assertion failure (docs/tasks/08-appduct-doctor.md).
96
+ // assertion failure.
97
97
  inspection_error: 66,
98
98
  connection_error: 70,
99
99
  session_error: 71,
@@ -549,6 +549,36 @@ var renderInvokeData = (colors, data, flags) => {
549
549
  return [colors.green("Result"), formatScalar(data, flags)];
550
550
  };
551
551
  var renderLinkData = (colors, data, flags, qr) => {
552
+ if ("attached" in data) {
553
+ return [
554
+ colors.green("Page Attached"),
555
+ ...renderFields(
556
+ "Page",
557
+ [
558
+ ["Session", data.sessionId],
559
+ ["Tab", data.targetId],
560
+ ["URL", data.url],
561
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
562
+ ],
563
+ flags
564
+ )
565
+ ];
566
+ }
567
+ if ("script" in data) {
568
+ return [
569
+ colors.green("Link Created"),
570
+ ...renderFields(
571
+ "Link",
572
+ [
573
+ ["Session", data.sessionId],
574
+ ["URL", data.url],
575
+ ["Script", data.script],
576
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
577
+ ],
578
+ flags
579
+ )
580
+ ];
581
+ }
552
582
  const lines = [
553
583
  colors.green("Link Created"),
554
584
  ...renderFields(
@@ -669,6 +699,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
669
699
  ["PID", data.daemon.pid],
670
700
  ["Started at", data.daemon.started_at],
671
701
  ["WSS port", data.daemon.wss_port],
702
+ ["Web port", data.daemon.web_port],
672
703
  ["Pinned keys", data.daemon.pinned_keys.length],
673
704
  ["Sessions", data.daemon.session_count]
674
705
  ],
@@ -24,6 +24,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
24
24
  "daemonLogMaxBytes",
25
25
  "eventsLogMaxBytes",
26
26
  "policy",
27
+ "webOrigins",
27
28
  "advertisedIp",
28
29
  "scheme",
29
30
  "restartDaemonOnVersionMismatch"
@@ -84,6 +85,7 @@ var defaultConfig = (paths) => {
84
85
  daemonLogMaxBytes: DEFAULT_DAEMON_LOG_MAX_BYTES,
85
86
  eventsLogMaxBytes: 10 * 1024 * 1024,
86
87
  restartDaemonOnVersionMismatch: false,
88
+ webOrigins: [],
87
89
  policy: {
88
90
  default: "allow",
89
91
  destructive: "allow"
@@ -154,6 +156,12 @@ var loadConfig = async (paths, options = {}) => {
154
156
  "restartDaemonOnVersionMismatch"
155
157
  );
156
158
  }
159
+ if (parsed.webOrigins !== void 0) {
160
+ if (!Array.isArray(parsed.webOrigins)) {
161
+ throw configError("webOrigins", "must be an array of origin strings.");
162
+ }
163
+ config.webOrigins = parsed.webOrigins.map((origin) => requireNonEmptyString(origin, "webOrigins"));
164
+ }
157
165
  if (parsed.policy !== void 0) {
158
166
  if (typeof parsed.policy !== "object" || parsed.policy === null || Array.isArray(parsed.policy)) {
159
167
  throw configError("policy", "must be an object.");
@@ -913,7 +921,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
913
921
  // EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
914
922
  // missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
915
923
  // code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
916
- // assertion failure (docs/tasks/08-appduct-doctor.md).
924
+ // assertion failure.
917
925
  inspection_error: 66,
918
926
  connection_error: 70,
919
927
  session_error: 71,
@@ -1511,6 +1519,36 @@ var renderInvokeData = (colors, data, flags) => {
1511
1519
  return [colors.green("Result"), formatScalar(data, flags)];
1512
1520
  };
1513
1521
  var renderLinkData = (colors, data, flags, qr) => {
1522
+ if ("attached" in data) {
1523
+ return [
1524
+ colors.green("Page Attached"),
1525
+ ...renderFields(
1526
+ "Page",
1527
+ [
1528
+ ["Session", data.sessionId],
1529
+ ["Tab", data.targetId],
1530
+ ["URL", data.url],
1531
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1532
+ ],
1533
+ flags
1534
+ )
1535
+ ];
1536
+ }
1537
+ if ("script" in data) {
1538
+ return [
1539
+ colors.green("Link Created"),
1540
+ ...renderFields(
1541
+ "Link",
1542
+ [
1543
+ ["Session", data.sessionId],
1544
+ ["URL", data.url],
1545
+ ["Script", data.script],
1546
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1547
+ ],
1548
+ flags
1549
+ )
1550
+ ];
1551
+ }
1514
1552
  const lines = [
1515
1553
  colors.green("Link Created"),
1516
1554
  ...renderFields(
@@ -1631,6 +1669,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
1631
1669
  ["PID", data.daemon.pid],
1632
1670
  ["Started at", data.daemon.started_at],
1633
1671
  ["WSS port", data.daemon.wss_port],
1672
+ ["Web port", data.daemon.web_port],
1634
1673
  ["Pinned keys", data.daemon.pinned_keys.length],
1635
1674
  ["Sessions", data.daemon.session_count]
1636
1675
  ],
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `attachPage()` (issue #177): runs a web page's Appduct session through a Playwright page instead of
3
+ * a socket the page opens itself. The page and the daemon talk through a binding Playwright exposes,
4
+ * so it works on an `https` page with no permission prompt and no debugging port. The binding belongs
5
+ * to the page's target: a popup, or a navigation that creates a new target, is not relayed, and the
6
+ * session stays on the page it was attached to.
7
+ */
8
+ import { type RelayLink } from "../devtools-relay/index.js";
9
+ /** The part of a Playwright `Page` that `attachPage` uses, so `appduct` has no Playwright dependency. */
10
+ export type AttachablePage = {
11
+ exposeBinding(name: string, callback: (source: {
12
+ frame: unknown;
13
+ }, json: string) => void): Promise<void>;
14
+ addInitScript(script: string): Promise<void>;
15
+ evaluate(expression: string): Promise<unknown>;
16
+ waitForFunction(expression: string): Promise<unknown>;
17
+ mainFrame(): unknown;
18
+ on(event: "close", handler: () => void): unknown;
19
+ };
20
+ export type AttachPageOptions = {
21
+ /** The result of `link({ target: "web", url })` for the page's address. */
22
+ link: RelayLink;
23
+ };
24
+ /**
25
+ * Connects the page, which must already load `@appduct/web`, to the daemon through its Playwright
26
+ * binding. Resolves once the session is claimed. A reload resumes the session over the same binding.
27
+ *
28
+ * Attaching a page that is already attached closes the first link's socket, so the daemon suspends
29
+ * the first session for its grace period and it stays listed as `suspended`. While it is, a
30
+ * `connect()` with no selector finds two sessions and fails with `ambiguous_session`: select the new
31
+ * one with `connect({ selector: link.sessionId })` or `waitForSession`. An invalid `link` rejects
32
+ * before anything is dropped.
33
+ *
34
+ * ```ts
35
+ * await page.goto("https://staging.example.com");
36
+ * await attachPage(page, { link: await link({ target: "web", url: page.url() }) });
37
+ * ```
38
+ */
39
+ export declare const attachPage: (page: AttachablePage, options: AttachPageOptions) => Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import { type ExecFn, type OpenTarget } from "../cli/open-target.js";
2
- import { type MintLinkResult } from "../link.js";
2
+ import { type MintLinkResult, type WebLinkResult } from "../link.js";
3
3
  import { type SpawnFn } from "../rpc/client.js";
4
4
  import { type AppClient, type ToolMap } from "./app-client.js";
5
5
  export type LinkOptions = {
@@ -32,9 +32,23 @@ export type LinkOptions = {
32
32
  schemeEnv?: NodeJS.ProcessEnv;
33
33
  };
34
34
  export type LinkResult = MintLinkResult;
35
+ export type WebLinkOptions = {
36
+ stateDir?: string;
37
+ spawn?: SpawnFn;
38
+ /** Auto-spawn a daemon on a missing connection. Defaults to `true`. */
39
+ autoSpawn?: boolean;
40
+ ttlSeconds?: number;
41
+ target: "web";
42
+ /** The page the link will be opened in. */
43
+ url: string;
44
+ };
45
+ export type { WebLinkResult };
35
46
  /** Mints a session link via `link.create` and composes the deep link. With `target` set, delivers
36
- * it directly to a booted emulator/simulator instead of leaving delivery to the caller. */
37
- export declare const link: (options?: LinkOptions) => Promise<LinkResult>;
47
+ * it directly to a booted emulator/simulator instead of leaving delivery to the caller. With
48
+ * `target: "web"` and the page's `url`, returns `{ url, script }` for a web page instead: `url`
49
+ * with `#appduct=<payload>` appended, and the script that connects an already-open page. */
50
+ export declare function link(options: WebLinkOptions): Promise<WebLinkResult>;
51
+ export declare function link(options?: LinkOptions): Promise<LinkResult>;
38
52
  export type WaitForSessionOptions = {
39
53
  stateDir?: string;
40
54
  spawn?: SpawnFn;
@@ -11,8 +11,9 @@
11
11
  * await app.close();
12
12
  * ```
13
13
  */
14
+ export { attachPage, type AttachablePage, type AttachPageOptions } from "./attach-page.js";
14
15
  export { connect, type ConnectOptions } from "./connect.js";
15
- export { link, waitForSession, type LinkOptions, type LinkResult, type WaitForSessionOptions } from "./bootstrap.js";
16
+ export { link, waitForSession, type LinkOptions, type LinkResult, type WaitForSessionOptions, type WebLinkOptions, type WebLinkResult, } from "./bootstrap.js";
16
17
  export { AppductError, type AppductErrorType } from "./errors.js";
17
18
  export type { AppClient, AppEvent, CallOptions, EventsOptions, EventsResult, FullAppEvent, FullEventsResult, ToolMap, WaitForEventOptions, } from "./app-client.js";
18
19
  export type { TruncatedAppEvent } from "@appduct/shared";