appduct 0.12.0 → 0.14.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/README.md +43 -37
- package/dist/bin.js +102 -37
- package/dist/cli/command-options.d.ts +7 -17
- package/dist/cli/result-types.d.ts +19 -9
- package/dist/cli/routes/daemon/index.js +70 -17
- package/dist/cli/routes/daemon/run.js +288 -50
- package/dist/cli/routes/daemon/start.js +70 -17
- package/dist/cli/routes/daemon/status.js +70 -17
- package/dist/cli/routes/daemon/stop.js +70 -17
- package/dist/cli/routes/doctor.js +70 -17
- package/dist/cli/routes/events/index.d.ts +6 -0
- package/dist/cli/routes/events/index.js +900 -0
- package/dist/cli/routes/events/ls.d.ts +5 -0
- package/dist/cli/routes/events/ls.js +1798 -0
- package/dist/cli/routes/events/shared-options.d.ts +12 -0
- package/dist/cli/routes/events/shared-options.js +102 -0
- package/dist/cli/routes/events/since.d.ts +6 -0
- package/dist/cli/routes/events/since.js +2034 -0
- package/dist/cli/routes/events/tail.d.ts +6 -0
- package/dist/cli/routes/{events.js → events/tail.js} +235 -163
- package/dist/cli/routes/init.js +72 -19
- package/dist/cli/routes/keygen.js +70 -17
- package/dist/cli/routes/mcp.js +289 -166
- package/dist/cli/routes/sessions/index.d.ts +7 -0
- package/dist/cli/routes/sessions/index.js +900 -0
- package/dist/cli/routes/sessions/link.d.ts +4 -0
- package/dist/cli/routes/{link.js → sessions/link.js} +71 -18
- package/dist/cli/routes/sessions/ls.d.ts +4 -0
- package/dist/cli/routes/{ls.js → sessions/ls.js} +71 -18
- package/dist/cli/routes/sessions/revoke.d.ts +4 -0
- package/dist/cli/routes/{revoke.js → sessions/revoke.js} +72 -19
- package/dist/cli/routes/tools/call.d.ts +4 -0
- package/dist/cli/routes/{invoke.js → tools/call.js} +73 -20
- package/dist/cli/routes/tools/describe.d.ts +6 -0
- package/dist/cli/routes/tools/describe.js +1894 -0
- package/dist/cli/routes/tools/index.d.ts +6 -0
- package/dist/cli/routes/tools/index.js +900 -0
- package/dist/cli/routes/tools/ls.d.ts +4 -0
- package/dist/cli/routes/{tools.js → tools/ls.js} +98 -74
- package/dist/cli/routes/tools/shared-options.d.ts +16 -0
- package/dist/cli/routes/tools/shared-options.js +122 -0
- package/dist/client/app-client.d.ts +96 -37
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.js +178 -138
- package/dist/commands/events-ls.d.ts +21 -0
- package/dist/commands/events.d.ts +31 -14
- package/dist/commands/invoke.d.ts +1 -1
- package/dist/commands/link.d.ts +1 -1
- package/dist/commands/ls.d.ts +1 -1
- package/dist/commands/revoke.d.ts +1 -1
- package/dist/commands/tools.d.ts +12 -13
- package/dist/daemon/calls.d.ts +3 -1
- package/dist/daemon/config.d.ts +1 -1
- package/dist/daemon/event-bus.d.ts +42 -0
- package/dist/daemon/registry.d.ts +20 -12
- package/dist/daemon/rpc-errors.d.ts +4 -1
- package/dist/daemon/sessions.d.ts +4 -2
- package/dist/errors.d.ts +1 -1
- package/dist/events/index.d.ts +60 -0
- package/dist/index.js +114 -40
- package/dist/link.d.ts +1 -1
- package/dist/mcp/app-tools.d.ts +22 -2
- package/dist/mcp/connect-tool.d.ts +1 -1
- package/dist/mcp/events-tool.d.ts +67 -27
- package/dist/native-scheme.d.ts +1 -1
- package/dist/output.d.ts +29 -6
- package/dist/rpc/client.d.ts +1 -1
- package/dist/scheme.d.ts +4 -4
- package/package.json +2 -2
- package/dist/cli/routes/events.d.ts +0 -3
- package/dist/cli/routes/invoke.d.ts +0 -3
- package/dist/cli/routes/link.d.ts +0 -3
- package/dist/cli/routes/ls.d.ts +0 -3
- package/dist/cli/routes/revoke.d.ts +0 -3
- package/dist/cli/routes/tools.d.ts +0 -3
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Per-session
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Per-session registry of what an app declared, by name (ARCHITECTURE.md §7): one instance holds a
|
|
3
|
+
* session's tools, another its events (issue #124). Callers (sessions.ts) must validate incoming
|
|
4
|
+
* `tool_registry_*`/`event_registry_*` wire messages with the shared guards *before* calling into
|
|
5
|
+
* this module — v1 crashed by indexing into an unvalidated `tools: [null]` snapshot; this module
|
|
6
|
+
* only ever stores already-validated descriptors.
|
|
6
7
|
*/
|
|
7
|
-
import type { ToolDescriptor } from "@appduct/shared";
|
|
8
|
-
export type
|
|
8
|
+
import type { EventDescriptor, ToolDescriptor } from "@appduct/shared";
|
|
9
|
+
export type Registry<T extends {
|
|
10
|
+
name: string;
|
|
11
|
+
}> = {
|
|
9
12
|
/** Authoritative replace: used for the initial snapshot and every post-resume re-sync. */
|
|
10
|
-
snapshot: (
|
|
11
|
-
upsert: (
|
|
13
|
+
snapshot: (items: T[]) => void;
|
|
14
|
+
upsert: (item: T) => void;
|
|
12
15
|
remove: (name: string) => void;
|
|
13
|
-
list: () =>
|
|
14
|
-
get: (name: string) =>
|
|
16
|
+
list: () => T[];
|
|
17
|
+
get: (name: string) => T | undefined;
|
|
15
18
|
count: () => number;
|
|
16
19
|
};
|
|
17
|
-
|
|
18
|
-
export
|
|
20
|
+
export type ToolRegistry = Registry<ToolDescriptor>;
|
|
21
|
+
export type EventRegistry = Registry<EventDescriptor>;
|
|
22
|
+
/** `onChange` fires after every mutation (snapshot/upsert/remove) — used to emit `tools_changed`
|
|
23
|
+
* and `events_changed`. */
|
|
24
|
+
export declare const createRegistry: <T extends {
|
|
25
|
+
name: string;
|
|
26
|
+
}>(onChange?: () => void) => Registry<T>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The error the daemon's RPC handlers throw to answer a request with a typed wire error
|
|
3
3
|
* (ARCHITECTURE.md §5). It lives apart from `rpc-server.ts` so the modules that *raise* it — the
|
|
4
4
|
* session engine, the call engine — don't drag the socket server itself into every bundle that
|
|
5
|
-
* needs, say, a call-timeout constant from `calls.ts` (the CLI's `
|
|
5
|
+
* needs, say, a call-timeout constant from `calls.ts` (the CLI's `tools call` route does).
|
|
6
6
|
*/
|
|
7
7
|
import type { ErrorType } from "@appduct/shared";
|
|
8
8
|
export declare class RpcApplicationError extends Error {
|
|
@@ -14,3 +14,6 @@ export declare class RpcApplicationError extends Error {
|
|
|
14
14
|
/** JSON-RPC error code; defaults to the shared "server error" range. */
|
|
15
15
|
code?: number, details?: unknown | undefined);
|
|
16
16
|
}
|
|
17
|
+
/** The `session_suspended` message for an app that closed its socket because it went to the
|
|
18
|
+
* background: says why and how to recover. */
|
|
19
|
+
export declare const appBackgroundedMessage: (alias: string) => string;
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
* §6: "terminal states free the alias" — there is no persisted history, matching §3/§13's "no
|
|
11
11
|
* per-session files, all state in-daemon-memory").
|
|
12
12
|
*/
|
|
13
|
-
import { type EventMessage, type SessionClaimMessage, type SessionResumeMessage, type SessionSummary, type ToolCallMessage, type ToolCallProgressMessage, type ToolCancelMessage, type ToolErrorMessage, type ToolResultMessage } from "@appduct/shared";
|
|
13
|
+
import { type EventMessage, type SessionClaimMessage, type SessionResumeMessage, type SessionSummary, type SessionSuspendReason, type ToolCallMessage, type ToolCallProgressMessage, type ToolCancelMessage, type ToolErrorMessage, type ToolResultMessage } from "@appduct/shared";
|
|
14
14
|
import type { WebSocket } from "ws";
|
|
15
15
|
import type { Clock } from "../cli/types.js";
|
|
16
16
|
import type { EventBus } from "./event-bus.js";
|
|
17
17
|
import { type CreatedLink } from "./links.js";
|
|
18
|
-
import { type ToolRegistry } from "./registry.js";
|
|
18
|
+
import { type EventRegistry, type ToolRegistry } from "./registry.js";
|
|
19
19
|
import { type TimerFns } from "./timers.js";
|
|
20
20
|
/** Wire message types allowed *after* claim (session_claim/session_resume are pre-claim only). */
|
|
21
21
|
export declare const POST_CLAIM_MESSAGE_TYPES: Set<string>;
|
|
@@ -69,7 +69,9 @@ export type SessionManager = {
|
|
|
69
69
|
sessionId: string;
|
|
70
70
|
alias: string;
|
|
71
71
|
state: "active" | "suspended";
|
|
72
|
+
suspendReason?: SessionSuspendReason;
|
|
72
73
|
registry: ToolRegistry;
|
|
74
|
+
eventRegistry: EventRegistry;
|
|
73
75
|
};
|
|
74
76
|
/** Sends a `tool_call` frame to the session's active socket; `false` if the session has no
|
|
75
77
|
* active socket right now (caller has typically already checked ACTIVE state via
|
package/dist/errors.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare const isAppductCliError: (value: unknown) => value is AppductCliE
|
|
|
18
18
|
* Converts any error thrown from a command handler into the CLI's rendered `CliError` shape.
|
|
19
19
|
* RPC/app-originated failures (`DaemonRpcError` with a preserved `data.type`) keep that type
|
|
20
20
|
* **verbatim** in `type` — never re-wrapped under a generic bucket (ARCHITECTURE.md §5) — so e.g.
|
|
21
|
-
* `
|
|
21
|
+
* `tools call --json` on a failing tool call renders `error.type === "tool_execution_error"`, not
|
|
22
22
|
* `"tool_error"`. `getExitCodeForError` still buckets that same error into a sysexits class.
|
|
23
23
|
*/
|
|
24
24
|
export declare const toCliError: (error: unknown) => CliError;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `waitForAppEvent` (issue #114): the one drain-then-live wait implementation shared by the
|
|
3
|
+
* built-in `appduct_wait_for_event` MCP tool (`mcp/events-tool.ts`) and `appduct/client`'s
|
|
4
|
+
* `AppClient.waitForEvent` (`client/app-client.ts`), which used to each carry their own copy.
|
|
5
|
+
*
|
|
6
|
+
* Filtering by `name` (a whole-name, case-sensitive glob — issue #112) and truncating a payload
|
|
7
|
+
* over `payloadMaxBytes` (issue #113) both happen daemon-side, via the same `projectAppEvent`
|
|
8
|
+
* (`daemon/event-bus.ts`) that `events.since`'s drain and `events.subscribe`'s live fan-out both
|
|
9
|
+
* already call — this module forwards `name`/`payloadMaxBytes` to both RPCs and never re-checks
|
|
10
|
+
* a name or re-truncates a payload itself.
|
|
11
|
+
*
|
|
12
|
+
* The drain-then-live shape exists to close the race `appduct_wait_for_session` doesn't have to
|
|
13
|
+
* worry about: a session is either claimed or not, but an app event can fire between "the caller
|
|
14
|
+
* decides to wait" and "the wait subscription lands". The notification listener is registered
|
|
15
|
+
* *before* `events.subscribe` is even sent — not merely before it resolves — because the
|
|
16
|
+
* daemon's response to that call and a `notify()` for a brand-new event can arrive in the same
|
|
17
|
+
* socket chunk; `rpc/client.ts`'s `DaemonStream` fans a chunk's notification lines out to
|
|
18
|
+
* whatever listeners exist at the moment it's processed, so a listener added only after `await`ing
|
|
19
|
+
* that response could miss a notification line from that same chunk. Arrivals before the drain
|
|
20
|
+
* has been merged into the backlog are buffered, then merged in (deduped by `seq`) before falling
|
|
21
|
+
* through to the live phase.
|
|
22
|
+
*
|
|
23
|
+
* `stream` is the caller's: opened and closed by the caller. Every caller of this module opens
|
|
24
|
+
* one stream per `waitForAppEvent` call — the MCP tool always did (`openDaemonStream` per
|
|
25
|
+
* `tools/call`), and the SDK now does too, so concurrent `waitForEvent` calls with different
|
|
26
|
+
* `name`s never clobber a shared connection's single `events.subscribe` filter.
|
|
27
|
+
*/
|
|
28
|
+
import { type AppEvent } from "@appduct/shared";
|
|
29
|
+
import type { DaemonStream } from "../rpc/client.js";
|
|
30
|
+
export type WaitForAppEventOptions = {
|
|
31
|
+
sessionId: string;
|
|
32
|
+
/** Whole-name, case-sensitive glob (issue #112): `*` matches any run of characters, a pattern
|
|
33
|
+
* with no `*` is an exact name — forwarded verbatim to `events.since`/`events.subscribe`. */
|
|
34
|
+
name: string;
|
|
35
|
+
/** Exclusive lower bound on `AppEvent.seq`; omitted drains the whole retained buffer. */
|
|
36
|
+
since?: number;
|
|
37
|
+
timeoutMs: number;
|
|
38
|
+
/** Caps the resolved event's payload to this many UTF-8 bytes of its JSON, daemon-side, on
|
|
39
|
+
* both the drain and the live half. Omitted, payloads are delivered whole. */
|
|
40
|
+
payloadMaxBytes?: number;
|
|
41
|
+
};
|
|
42
|
+
export type WaitForAppEventResult = {
|
|
43
|
+
event: AppEvent;
|
|
44
|
+
/** `events.since`'s own `dropped` from the drain: app events after `since` that were evicted
|
|
45
|
+
* before the drain could return them (`0` once nothing has fallen off). Reflects only the
|
|
46
|
+
* drain — an event that resolves this wait live was delivered, not dropped. */
|
|
47
|
+
dropped: number;
|
|
48
|
+
};
|
|
49
|
+
/** Raised when nothing matching `name` arrived within `timeoutMs`. */
|
|
50
|
+
export declare class WaitForAppEventTimeoutError extends Error {
|
|
51
|
+
readonly eventName: string;
|
|
52
|
+
readonly timeoutMs: number;
|
|
53
|
+
constructor(eventName: string, timeoutMs: number);
|
|
54
|
+
}
|
|
55
|
+
/** Raised when `stream` closes (daemon gone, `stop()`, etc.) while a live wait was in flight. */
|
|
56
|
+
export declare class WaitForAppEventConnectionClosedError extends Error {
|
|
57
|
+
readonly eventName: string;
|
|
58
|
+
constructor(eventName: string);
|
|
59
|
+
}
|
|
60
|
+
export declare const waitForAppEvent: (stream: DaemonStream, options: WaitForAppEventOptions) => Promise<WaitForAppEventResult>;
|
package/dist/index.js
CHANGED
|
@@ -121,27 +121,30 @@ var createCli = () => {
|
|
|
121
121
|
'Android package name to write as "appId.android" (needed to deliver a link with --open android).'
|
|
122
122
|
);
|
|
123
123
|
cli.command("keygen", "Generate an Appduct host private key and print its app fingerprint.").option("--out <path>", "Destination path (default: <state-dir>/key.pem).").option("--force", "Overwrite an existing key at the destination path.");
|
|
124
|
-
cli.command("
|
|
124
|
+
cli.command("sessions [...args]", "Manage Appduct sessions: ls, revoke, or link.").usage("sessions <ls|revoke|link> [selector] [args]").option("--ttl <seconds>", "link: time-to-live in seconds (default: from config.json).").option("--qr", "link: also render the deep link as a terminal QR code.").option(
|
|
125
125
|
"--scheme <scheme>",
|
|
126
|
-
`
|
|
126
|
+
`link: deep-link URI scheme (also: APPDUCT_SCHEME; default: .appduct/config.json, then app.json's "expo.scheme", then the Android/iOS project files in <cwd>).`
|
|
127
127
|
).option(
|
|
128
128
|
"--open <target>",
|
|
129
|
-
"
|
|
130
|
-
).option("--device <id>", "adb serial, simulator udid or paired-device udid to target when --open is ambiguous.").option(
|
|
129
|
+
"link: deliver the link automatically via adb/simctl/devicectl (android|ios-sim|ios-device; ios-device is experimental)."
|
|
130
|
+
).option("--device <id>", "link: adb serial, simulator udid or paired-device udid to target when --open is ambiguous.").option(
|
|
131
131
|
"--app-id <id>",
|
|
132
|
-
`
|
|
132
|
+
`link: installed app id for --open android/ios-device (default: .appduct/config.json's "appId.<platform>").`
|
|
133
133
|
).option(
|
|
134
134
|
"--relaunch",
|
|
135
|
-
"
|
|
135
|
+
"link: with --open ios-device, terminate a running instance first (try this if delivery to an already-running app does nothing)."
|
|
136
136
|
);
|
|
137
|
-
cli.command("
|
|
138
|
-
cli.command("tools [selector] [name]", "List a session's tools, or show one tool's full schema.").option("--full", "Render full schemas/annotations for every listed tool.").option("--group <name>", 'Only tools in this group ("checkout" includes "checkout/payment").').option("--groups", "List the session's groups with tool counts instead of its tools.").option("--filter <text>", "Only tools whose name or description contains this text (case-insensitive).").option("--limit <n>", "Show at most n tools.").option("--offset <n>", "Skip the first n tools of the sorted list.");
|
|
139
|
-
cli.command("invoke [selector] [tool]", "Call a tool on a session.").option("--input <json>", "Tool input arguments as a JSON object.").option(
|
|
137
|
+
cli.command("tools [...args]", "List a session's tools, describe one, or call one: ls, describe, or call.").usage("tools <ls|describe|call> [selector] [args]").option("--full", "ls: render full schemas/annotations for every listed tool.").option("--group <name>", 'ls: only tools in this group ("checkout" includes "checkout/payment").').option("--groups", "ls: list the session's groups with tool counts instead of its tools.").option("--filter <text>", "ls: only tools whose name or description contains this text (case-insensitive).").option("--limit <n>", "ls: show at most n tools.").option("--offset <n>", "ls: skip the first n tools of the sorted list.").option("--input <json>", "call: tool input arguments as a JSON object.").option(
|
|
140
138
|
"--timeout <ms>",
|
|
141
|
-
"
|
|
139
|
+
"call: call timeout in milliseconds. Shortens the deadline; it cannot extend one past the app's own timer, which is the tool's declared timeoutMs (else 10000)."
|
|
140
|
+
);
|
|
141
|
+
cli.command("events [...args]", "List the events an app declares, stream them, or replay them since a cursor: ls, tail, or since.").usage("events <ls|tail|since> [selector] [args]").option("--follow", "tail: accepted for script readability; the default behavior already follows.").option(
|
|
142
|
+
"--name <glob>",
|
|
143
|
+
`Only events whose name matches this whole-name, case-sensitive glob ("*" matches any run of characters). ls: an exact name prints that event's full payload schema.`
|
|
144
|
+
).option("--limit <n>", "ls: show at most n events.").option("--offset <n>", "ls: skip the first n events of the name-sorted list.").option(
|
|
145
|
+
"--payload-max-bytes <n>",
|
|
146
|
+
"Cap each event's payload at this many UTF-8 bytes of its JSON; over the cap, the event carries payloadPreview, payloadBytes and truncated: true instead of payload."
|
|
142
147
|
);
|
|
143
|
-
cli.command("events [selector]", "Stream the events the app posts until interrupted.").option("--follow", "Accepted for script readability; the default behavior already follows.").option("--since <cursor>", "One-shot: print the app events retained since this cursor instead of streaming live.");
|
|
144
|
-
cli.command("revoke [selector]", "Revoke a session.");
|
|
145
148
|
cli.command("mcp", "Start a stdio MCP server that gives MCP clients access to connected apps' tools.").option(
|
|
146
149
|
"--scheme <scheme>",
|
|
147
150
|
"Deep-link URI scheme for appduct_connect (also: APPDUCT_SCHEME)."
|
|
@@ -543,6 +546,7 @@ var getExitCodeForError = (error) => {
|
|
|
543
546
|
import pc from "picocolors";
|
|
544
547
|
import {
|
|
545
548
|
formatAgentWebSocketUrl,
|
|
549
|
+
renderEventSignature,
|
|
546
550
|
renderToolSignature,
|
|
547
551
|
summarizeToolDescription
|
|
548
552
|
} from "@appduct/shared";
|
|
@@ -638,6 +642,13 @@ var formatDevice = (device) => {
|
|
|
638
642
|
const label = parts.length > 0 ? parts.join(" ") : "unknown device";
|
|
639
643
|
return device.os ? `${label} (${device.os})` : label;
|
|
640
644
|
};
|
|
645
|
+
var SUSPEND_REASON_LABELS = {
|
|
646
|
+
app_backgrounded: "app in background",
|
|
647
|
+
connection_lost: "connection lost"
|
|
648
|
+
};
|
|
649
|
+
var formatSessionState = (session) => {
|
|
650
|
+
return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
|
|
651
|
+
};
|
|
641
652
|
var renderLsData = (colors, data, now) => {
|
|
642
653
|
if (data.length === 0) {
|
|
643
654
|
return [colors.dim("Sessions"), " No Appduct sessions are registered."];
|
|
@@ -646,7 +657,7 @@ var renderLsData = (colors, data, now) => {
|
|
|
646
657
|
const headers = ["Alias", "State", "Device", "Tools", "Age"];
|
|
647
658
|
const rows = data.map((session) => [
|
|
648
659
|
session.alias,
|
|
649
|
-
session
|
|
660
|
+
formatSessionState(session),
|
|
650
661
|
formatDevice(session.device),
|
|
651
662
|
String(session.toolCount),
|
|
652
663
|
formatAge(session.claimedAt ?? session.createdAt, nowIso)
|
|
@@ -660,9 +671,6 @@ var renderLsData = (colors, data, now) => {
|
|
|
660
671
|
...rows.map((row) => ` ${row.map((cell, index) => cell.padEnd(widths[index])).join(" ")}`)
|
|
661
672
|
];
|
|
662
673
|
};
|
|
663
|
-
var isToolsListing = (data) => {
|
|
664
|
-
return typeof data === "object" && data !== null && Array.isArray(data.tools);
|
|
665
|
-
};
|
|
666
674
|
var isToolGroupsListing = (data) => {
|
|
667
675
|
return typeof data === "object" && data !== null && Array.isArray(data.groups) && !Array.isArray(data.tools);
|
|
668
676
|
};
|
|
@@ -680,7 +688,7 @@ var renderEmptyToolsLine = (data) => {
|
|
|
680
688
|
}
|
|
681
689
|
if (data.group !== void 0) {
|
|
682
690
|
const match = data.filter === void 0 ? "" : ` match ${JSON.stringify(data.filter)}`;
|
|
683
|
-
return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools --groups\` to see the session's groups.`;
|
|
691
|
+
return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools ls --groups\` to see the session's groups.`;
|
|
684
692
|
}
|
|
685
693
|
return data.filter === void 0 ? " No tools registered." : ` No tools match ${JSON.stringify(data.filter)}.`;
|
|
686
694
|
};
|
|
@@ -762,7 +770,7 @@ var renderToolSummaryTable = (colors, data) => {
|
|
|
762
770
|
...grouped ? renderGroupedToolLines(colors, data.tools) : data.tools.flatMap((tool) => renderToolSummaryLines(tool, " ")),
|
|
763
771
|
...renderTruncationLine(data),
|
|
764
772
|
"",
|
|
765
|
-
"Run `appduct tools <name>` for a tool's full schema."
|
|
773
|
+
"Run `appduct tools describe <name>` for a tool's full schema."
|
|
766
774
|
];
|
|
767
775
|
};
|
|
768
776
|
var renderToolDetail = (colors, tool, flags) => {
|
|
@@ -814,18 +822,62 @@ var renderToolGroups = (colors, data) => {
|
|
|
814
822
|
colors.green("Groups"),
|
|
815
823
|
...data.groups.map((entry) => ` ${label(entry).padEnd(width)} ${String(entry.total).padStart(countWidth)}`),
|
|
816
824
|
"",
|
|
817
|
-
`${data.total} tool${data.total === 1 ? "" : "s"} in total. ` + (hasAnyGroup(data.groups) ? "Run `appduct tools --group <name>` to list one group's tools." : "No tool declares a group.")
|
|
825
|
+
`${data.total} tool${data.total === 1 ? "" : "s"} in total. ` + (hasAnyGroup(data.groups) ? "Run `appduct tools ls --group <name>` to list one group's tools." : "No tool declares a group.")
|
|
818
826
|
];
|
|
819
827
|
};
|
|
820
|
-
var
|
|
828
|
+
var renderToolsListData = (colors, data, flags, full) => {
|
|
821
829
|
if (isToolGroupsListing(data)) {
|
|
822
830
|
return renderToolGroups(colors, data);
|
|
823
831
|
}
|
|
824
|
-
if (!isToolsListing(data)) {
|
|
825
|
-
return renderToolDetail(colors, data, flags);
|
|
826
|
-
}
|
|
827
832
|
return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
|
|
828
833
|
};
|
|
834
|
+
var renderEventsTruncationLine = (data) => {
|
|
835
|
+
const offset = data.offset ?? 0;
|
|
836
|
+
if (data.events.length >= data.total) {
|
|
837
|
+
return [];
|
|
838
|
+
}
|
|
839
|
+
return [
|
|
840
|
+
"",
|
|
841
|
+
`Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
|
|
842
|
+
];
|
|
843
|
+
};
|
|
844
|
+
var renderEventsListData = (colors, data, flags) => {
|
|
845
|
+
const [only] = data.events;
|
|
846
|
+
if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
|
|
847
|
+
return renderFields(
|
|
848
|
+
colors.green(`Event: ${only.name}`),
|
|
849
|
+
[
|
|
850
|
+
["Signature", renderEventSignature(only)],
|
|
851
|
+
["Description", only.description],
|
|
852
|
+
["Payload schema", only.payload_schema]
|
|
853
|
+
],
|
|
854
|
+
flags
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
if (data.events.length === 0 && data.total > 0) {
|
|
858
|
+
return [
|
|
859
|
+
colors.green("Events"),
|
|
860
|
+
` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
|
|
861
|
+
];
|
|
862
|
+
}
|
|
863
|
+
if (data.events.length === 0) {
|
|
864
|
+
return [
|
|
865
|
+
colors.green("Events"),
|
|
866
|
+
data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
|
|
867
|
+
];
|
|
868
|
+
}
|
|
869
|
+
const lines = data.events.flatMap((event) => [
|
|
870
|
+
` ${renderEventSignature(event)}`,
|
|
871
|
+
` ${summarizeToolDescription(event.description)}`
|
|
872
|
+
]);
|
|
873
|
+
return [
|
|
874
|
+
colors.green("Events"),
|
|
875
|
+
...lines,
|
|
876
|
+
...renderEventsTruncationLine(data),
|
|
877
|
+
"",
|
|
878
|
+
"Run `appduct events ls --name <name>` for an event's full payload schema."
|
|
879
|
+
];
|
|
880
|
+
};
|
|
829
881
|
var renderInvokeData = (colors, data, flags) => {
|
|
830
882
|
return [colors.green("Result"), formatScalar(data, flags)];
|
|
831
883
|
};
|
|
@@ -1009,15 +1061,19 @@ var renderSuccessData = (colors, command, data, options) => {
|
|
|
1009
1061
|
return renderInitData(colors, data, flags);
|
|
1010
1062
|
case "keygen":
|
|
1011
1063
|
return renderKeygenData(colors, data, flags);
|
|
1012
|
-
case "link":
|
|
1064
|
+
case "sessions link":
|
|
1013
1065
|
return renderLinkData(colors, data, flags, options.qr);
|
|
1014
|
-
case "ls":
|
|
1066
|
+
case "sessions ls":
|
|
1015
1067
|
return renderLsData(colors, data, options.now ?? /* @__PURE__ */ new Date());
|
|
1016
|
-
case "tools":
|
|
1017
|
-
return
|
|
1018
|
-
case "
|
|
1068
|
+
case "tools ls":
|
|
1069
|
+
return renderToolsListData(colors, data, flags, options.full);
|
|
1070
|
+
case "tools describe":
|
|
1071
|
+
return renderToolDetail(colors, data, flags);
|
|
1072
|
+
case "events ls":
|
|
1073
|
+
return renderEventsListData(colors, data, flags);
|
|
1074
|
+
case "tools call":
|
|
1019
1075
|
return renderInvokeData(colors, data, flags);
|
|
1020
|
-
case "revoke":
|
|
1076
|
+
case "sessions revoke":
|
|
1021
1077
|
return renderRevokeData(colors, data);
|
|
1022
1078
|
case "daemon run":
|
|
1023
1079
|
return renderDaemonRunData(colors, data, flags);
|
|
@@ -1087,12 +1143,21 @@ var renderEventLine = (event, flags) => {
|
|
|
1087
1143
|
const dataSuffix = event.data === void 0 ? "" : ` ${formatJson(event.data, flags)}`;
|
|
1088
1144
|
return `${colors.dim(timestamp)} ${colors.green(event.kind)}${target ? ` ${target}` : ""}${dataSuffix}`;
|
|
1089
1145
|
};
|
|
1090
|
-
var
|
|
1146
|
+
var shellQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1147
|
+
var renderEventsCursorLine = (cursor, flags, selector, filters) => {
|
|
1091
1148
|
if (flags.json) {
|
|
1092
|
-
return JSON.stringify({ cursor });
|
|
1149
|
+
return JSON.stringify({ cursor: cursor.cursor, dropped: cursor.dropped, remaining: cursor.remaining });
|
|
1093
1150
|
}
|
|
1094
1151
|
const colors = pc.createColors(flags.color);
|
|
1095
|
-
|
|
1152
|
+
const target = selector === void 0 ? `${cursor.cursor}` : `${selector} ${cursor.cursor}`;
|
|
1153
|
+
const flagTokens = [
|
|
1154
|
+
...filters?.name === void 0 ? [] : [`--name ${shellQuote(filters.name)}`],
|
|
1155
|
+
...filters?.payloadMaxBytes === void 0 ? [] : [`--payload-max-bytes ${filters.payloadMaxBytes}`]
|
|
1156
|
+
];
|
|
1157
|
+
const command = [`appduct events since ${target}`, ...flagTokens].join(" ");
|
|
1158
|
+
return colors.dim(
|
|
1159
|
+
`cursor: ${cursor.cursor}, dropped: ${cursor.dropped}, remaining: ${cursor.remaining} (run "${command}" to resume from here)`
|
|
1160
|
+
);
|
|
1096
1161
|
};
|
|
1097
1162
|
|
|
1098
1163
|
// src/cli/envelope.ts
|
|
@@ -1189,21 +1254,30 @@ var DAEMON_RESTART_ENV = "APPDUCT_DAEMON_RESTART";
|
|
|
1189
1254
|
var isEnvTruthy = (value) => {
|
|
1190
1255
|
return value === "1" || value?.toLowerCase() === "true";
|
|
1191
1256
|
};
|
|
1257
|
+
var REMOVED_COMMANDS = {
|
|
1258
|
+
ls: "sessions ls",
|
|
1259
|
+
revoke: "sessions revoke",
|
|
1260
|
+
link: "sessions link",
|
|
1261
|
+
invoke: "tools call"
|
|
1262
|
+
};
|
|
1263
|
+
var rootUnknownCommandError = (word) => {
|
|
1264
|
+
if (word !== void 0 && Object.hasOwn(REMOVED_COMMANDS, word)) {
|
|
1265
|
+
return usageError(`Unknown command "${word}"; use "appduct ${REMOVED_COMMANDS[word]}".`);
|
|
1266
|
+
}
|
|
1267
|
+
return unknownCommandError(word);
|
|
1268
|
+
};
|
|
1192
1269
|
var rootRouter = createRouter(
|
|
1193
1270
|
{
|
|
1194
1271
|
init: () => import("./cli/routes/init.js"),
|
|
1195
1272
|
keygen: () => import("./cli/routes/keygen.js"),
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
invoke: () => import("./cli/routes/invoke.js"),
|
|
1200
|
-
revoke: () => import("./cli/routes/revoke.js"),
|
|
1201
|
-
events: () => import("./cli/routes/events.js"),
|
|
1273
|
+
sessions: () => import("./cli/routes/sessions/index.js"),
|
|
1274
|
+
tools: () => import("./cli/routes/tools/index.js"),
|
|
1275
|
+
events: () => import("./cli/routes/events/index.js"),
|
|
1202
1276
|
mcp: () => import("./cli/routes/mcp.js"),
|
|
1203
1277
|
doctor: () => import("./cli/routes/doctor.js"),
|
|
1204
1278
|
daemon: () => import("./cli/routes/daemon/index.js")
|
|
1205
1279
|
},
|
|
1206
|
-
{ unknown:
|
|
1280
|
+
{ unknown: rootUnknownCommandError }
|
|
1207
1281
|
);
|
|
1208
1282
|
var runCli = async (argv, options = {}) => {
|
|
1209
1283
|
const writers = {
|
|
@@ -1242,7 +1316,7 @@ var runCli = async (argv, options = {}) => {
|
|
|
1242
1316
|
return executeCommand(
|
|
1243
1317
|
"cli",
|
|
1244
1318
|
() => {
|
|
1245
|
-
throw
|
|
1319
|
+
throw rootUnknownCommandError(parsedArgs[0]);
|
|
1246
1320
|
},
|
|
1247
1321
|
{
|
|
1248
1322
|
flags,
|
package/dist/link.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared core of `link.create` + deep-link composition + optional emulator/simulator delivery
|
|
3
3
|
* (ARCHITECTURE.md §8, §10): mints a pending session via `link.create`, then composes
|
|
4
4
|
* `<scheme>:///?appduct=<payload>&pin=<spki-pin>` and optionally delivers it to a booted
|
|
5
|
-
* Android emulator/device or iOS simulator. Used by `commands/link.ts` (`appduct link`) and
|
|
5
|
+
* Android emulator/device or iOS simulator. Used by `commands/link.ts` (`appduct sessions link`) and
|
|
6
6
|
* `client/bootstrap.ts` (`appduct/client`'s `link()`) so this shape — scheme resolution, the
|
|
7
7
|
* `pin` query param, the `127.0.0.1` emulator/simulator address override (which the experimental
|
|
8
8
|
* `ios-device` target deliberately opts out of) — can't drift between the CLI and the programmatic
|
package/dist/mcp/app-tools.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The built-in `appduct_list_tools` / `appduct_describe_tool` / `appduct_call_tool` MCP tools
|
|
3
3
|
* (ARCHITECTURE.md §9). The app's own tools are never listed as MCP tools. An agent reaches them
|
|
4
|
-
* the way the CLI does: compact signatures first (`appduct tools`), one full schema on demand
|
|
5
|
-
* (`appduct tools <name>`), then a call by name (`appduct
|
|
4
|
+
* the way the CLI does: compact signatures first (`appduct tools ls`), one full schema on demand
|
|
5
|
+
* (`appduct tools describe <name>`), then a call by name (`appduct tools call`). A registry of
|
|
6
|
+
* hundreds of tools
|
|
6
7
|
* therefore costs a client three fixed tool definitions, and `tools/list` never changes while an
|
|
7
8
|
* agent works.
|
|
8
9
|
*
|
|
@@ -106,12 +107,30 @@ export type ResolvedAppTool = {
|
|
|
106
107
|
descriptor: ListedToolDescriptor;
|
|
107
108
|
policy: EffectivePolicyDecision;
|
|
108
109
|
};
|
|
110
|
+
type ResolvedSession = {
|
|
111
|
+
sessionId: string;
|
|
112
|
+
alias: string;
|
|
113
|
+
};
|
|
109
114
|
export type CallToolArgs = {
|
|
110
115
|
selector?: string;
|
|
111
116
|
name: string;
|
|
112
117
|
args: Record<string, unknown>;
|
|
113
118
|
timeoutMs?: number;
|
|
114
119
|
};
|
|
120
|
+
/** Every key an agent sends must be one the tool declares. A misspelled key (`arguments` for
|
|
121
|
+
* `args`, `timeout_ms` for `timeoutMs`) would otherwise be dropped silently, and the tool would run
|
|
122
|
+
* without what the agent meant to pass. */
|
|
123
|
+
export declare const rejectUnknownKeys: (args: Record<string, unknown>, tool: string, allowed: readonly string[]) => void;
|
|
124
|
+
/** `null` counts as absent for every optional field: some clients fill unset optional
|
|
125
|
+
* parameters with `null` rather than leaving them out. */
|
|
126
|
+
export declare const asOptionalString: (value: unknown, field: string) => string | undefined;
|
|
127
|
+
/** Resolves `selector` (an alias, a session id, or nothing) to one concrete session first, with
|
|
128
|
+
* the daemon's own rules and errors (`no_session`, `ambiguous_session`, `unknown_session`).
|
|
129
|
+
* Everything after that is routed by the **session id**, never the alias: the daemon frees an
|
|
130
|
+
* alias when a session ends and gives it to the next device of the same model, so routing by alias
|
|
131
|
+
* could land a call — one the user may already have approved — on a different device. If the
|
|
132
|
+
* session goes away mid-call, the next daemon call fails with `unknown_session` instead. */
|
|
133
|
+
export declare const resolveSession: (call: DaemonCall, selector: string | undefined) => Promise<ResolvedSession>;
|
|
115
134
|
export declare const handleListToolsTool: (rawArgs: unknown, call: DaemonCall) => Promise<{
|
|
116
135
|
groups?: import("@appduct/shared").ToolGroupSummary[] | undefined;
|
|
117
136
|
tools: {
|
|
@@ -151,3 +170,4 @@ export declare const parseCallToolArgs: (rawArgs: unknown) => CallToolArgs;
|
|
|
151
170
|
*/
|
|
152
171
|
export declare const resolveCallDeadline: (tool: ResolvedAppTool, requestedTimeoutMs: number | undefined) => number;
|
|
153
172
|
export declare const resolveAppTool: (call: DaemonCall, selector: string | undefined, name: string) => Promise<ResolvedAppTool>;
|
|
173
|
+
export {};
|
|
@@ -71,7 +71,7 @@ export declare class McpBuiltinToolError extends Error {
|
|
|
71
71
|
export type ConnectToolDeps = {
|
|
72
72
|
call: DaemonCall;
|
|
73
73
|
/** The scheme composing the deep link (`<scheme>:///?appduct=<payload>`), resolved once at
|
|
74
|
-
* server startup against the shared order in `scheme.ts` — the same one `appduct link` uses,
|
|
74
|
+
* server startup against the shared order in `scheme.ts` — the same one `appduct sessions link` uses,
|
|
75
75
|
* so an app root with an `app.json` needs no configuration at all. */
|
|
76
76
|
scheme?: string;
|
|
77
77
|
/** Every location `scheme.ts` consulted, named in the failure below so an agent can tell its
|
|
@@ -4,27 +4,48 @@
|
|
|
4
4
|
* per-session retention buffer on the daemon side — see `daemon/event-bus.ts`). MCP is
|
|
5
5
|
* request/response, so an agent otherwise has no way to observe anything the app pushes.
|
|
6
6
|
*
|
|
7
|
-
* `appduct_wait_for_event`
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* listener is registered *before* the `events.since` drain call is even sent — not merely before
|
|
12
|
-
* it resolves — because the daemon's response to that call and a subsequent `notify()` for a new
|
|
13
|
-
* event can arrive in the same socket chunk; `rpc/client.ts` fans a chunk's notification lines out
|
|
14
|
-
* to whatever listeners exist at the moment it's processed, so a listener added only after `await
|
|
15
|
-
* stream.call(eventsSince, …)` returns can miss an event that was already in that same chunk.
|
|
16
|
-
* Arrivals before the drain has been merged into the backlog are buffered, then merged in
|
|
17
|
-
* (deduped by `seq`) before falling through to the live phase — see `handleWaitForEventTool` below.
|
|
7
|
+
* `appduct_wait_for_event`'s drain-then-live wait is `events/index.ts`'s `waitForAppEvent`
|
|
8
|
+
* (issue #114) — the same implementation `appduct/client`'s `AppClient.waitForEvent` uses — this
|
|
9
|
+
* module only resolves the selector, applies MCP-specific defaults (`payloadMaxBytes`), and maps
|
|
10
|
+
* that module's typed errors onto this tool's `tool_timeout`/`tool_execution_error`.
|
|
18
11
|
*/
|
|
19
|
-
import { type
|
|
12
|
+
import { type AppEvent } from "@appduct/shared";
|
|
20
13
|
import { type SpawnFn } from "../rpc/client.js";
|
|
21
14
|
import type { DaemonCall } from "./daemon-tools.js";
|
|
15
|
+
export declare const LIST_EVENTS_TOOL_NAME = "appduct_list_events";
|
|
22
16
|
export declare const EVENTS_TOOL_NAME = "appduct_events";
|
|
23
17
|
export declare const WAIT_FOR_EVENT_TOOL_NAME = "appduct_wait_for_event";
|
|
24
18
|
/** Server-side cap on `timeoutMs` (issue #6's "Cap timeoutMs server-side below the idle window
|
|
25
19
|
* rather than trusting the caller") — a pure function so the cap's boundary behavior is
|
|
26
20
|
* unit-testable without spinning up a daemon or actually waiting out either bound. */
|
|
27
21
|
export declare const clampWaitForEventTimeoutMs: (requestedMs: number | undefined) => number;
|
|
22
|
+
export declare const LIST_EVENTS_TOOL_DESCRIPTOR: {
|
|
23
|
+
readonly name: "appduct_list_events";
|
|
24
|
+
readonly description: string;
|
|
25
|
+
readonly inputSchema: {
|
|
26
|
+
readonly type: "object";
|
|
27
|
+
readonly properties: {
|
|
28
|
+
readonly selector: {
|
|
29
|
+
readonly type: "string";
|
|
30
|
+
readonly minLength: 1;
|
|
31
|
+
readonly description: "Session alias or id. Omit to target the sole active/suspended session.";
|
|
32
|
+
};
|
|
33
|
+
readonly name: {
|
|
34
|
+
readonly type: "string";
|
|
35
|
+
readonly minLength: 1;
|
|
36
|
+
};
|
|
37
|
+
readonly limit: {
|
|
38
|
+
readonly type: "integer";
|
|
39
|
+
readonly exclusiveMinimum: 0;
|
|
40
|
+
};
|
|
41
|
+
readonly offset: {
|
|
42
|
+
readonly type: "integer";
|
|
43
|
+
readonly minimum: 0;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
readonly additionalProperties: false;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
28
49
|
export declare const EVENTS_TOOL_DESCRIPTOR: {
|
|
29
50
|
readonly name: "appduct_events";
|
|
30
51
|
readonly description: string;
|
|
@@ -42,6 +63,13 @@ export declare const EVENTS_TOOL_DESCRIPTOR: {
|
|
|
42
63
|
readonly type: "integer";
|
|
43
64
|
readonly exclusiveMinimum: 0;
|
|
44
65
|
};
|
|
66
|
+
readonly name: {
|
|
67
|
+
readonly type: "string";
|
|
68
|
+
};
|
|
69
|
+
readonly payloadMaxBytes: {
|
|
70
|
+
readonly type: "integer";
|
|
71
|
+
readonly exclusiveMinimum: 0;
|
|
72
|
+
};
|
|
45
73
|
};
|
|
46
74
|
readonly additionalProperties: false;
|
|
47
75
|
};
|
|
@@ -58,12 +86,6 @@ export declare const WAIT_FOR_EVENT_TOOL_DESCRIPTOR: {
|
|
|
58
86
|
readonly name: {
|
|
59
87
|
readonly type: "string";
|
|
60
88
|
};
|
|
61
|
-
readonly match: {
|
|
62
|
-
readonly type: "object";
|
|
63
|
-
readonly additionalProperties: {
|
|
64
|
-
readonly type: readonly ["string", "number", "boolean", "null"];
|
|
65
|
-
};
|
|
66
|
-
};
|
|
67
89
|
readonly since: {
|
|
68
90
|
readonly type: "integer";
|
|
69
91
|
readonly minimum: 0;
|
|
@@ -72,6 +94,10 @@ export declare const WAIT_FOR_EVENT_TOOL_DESCRIPTOR: {
|
|
|
72
94
|
readonly type: "number";
|
|
73
95
|
readonly exclusiveMinimum: 0;
|
|
74
96
|
};
|
|
97
|
+
readonly payloadMaxBytes: {
|
|
98
|
+
readonly type: "integer";
|
|
99
|
+
readonly exclusiveMinimum: 0;
|
|
100
|
+
};
|
|
75
101
|
};
|
|
76
102
|
readonly required: readonly ["name"];
|
|
77
103
|
readonly additionalProperties: false;
|
|
@@ -80,7 +106,24 @@ export declare const WAIT_FOR_EVENT_TOOL_DESCRIPTOR: {
|
|
|
80
106
|
export type EventsToolDeps = {
|
|
81
107
|
call: DaemonCall;
|
|
82
108
|
};
|
|
83
|
-
export declare const
|
|
109
|
+
export declare const handleListEventsTool: (rawArgs: unknown, call: DaemonCall) => Promise<{
|
|
110
|
+
session: string;
|
|
111
|
+
total: number;
|
|
112
|
+
limit: {};
|
|
113
|
+
events: {
|
|
114
|
+
payload_schema?: import("@appduct/shared").EventSchemaDescriptor | undefined;
|
|
115
|
+
name: string;
|
|
116
|
+
signature: string;
|
|
117
|
+
description: string;
|
|
118
|
+
}[];
|
|
119
|
+
}>;
|
|
120
|
+
export type EventsToolResult = {
|
|
121
|
+
events: AppEvent[];
|
|
122
|
+
cursor: number;
|
|
123
|
+
dropped: number;
|
|
124
|
+
remaining: number;
|
|
125
|
+
};
|
|
126
|
+
export declare const handleEventsTool: (rawArgs: unknown, deps: EventsToolDeps) => Promise<EventsToolResult>;
|
|
84
127
|
export type WaitForEventToolDeps = {
|
|
85
128
|
stateDir: string;
|
|
86
129
|
spawn?: SpawnFn;
|
|
@@ -91,13 +134,10 @@ export type WaitForEventToolDeps = {
|
|
|
91
134
|
sendNotification: (notification: unknown) => Promise<void>;
|
|
92
135
|
};
|
|
93
136
|
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
/** Unix ms. */
|
|
100
|
-
ts: number;
|
|
101
|
-
seq: number;
|
|
137
|
+
/** An `AppEvent` (full or truncated — issue #113) plus `dropped` from the drain (issue #114). */
|
|
138
|
+
export type WaitForEventToolResult = AppEvent & {
|
|
139
|
+
/** App events after `since` that were evicted before this call could return them (`0` once
|
|
140
|
+
* nothing has fallen off). */
|
|
141
|
+
dropped: number;
|
|
102
142
|
};
|
|
103
143
|
export declare const handleWaitForEventTool: (rawArgs: unknown, deps: WaitForEventToolDeps) => Promise<WaitForEventToolResult>;
|
package/dist/native-scheme.d.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* **Disagreement is not guessed away.** If more than one probe below resolves to a *different*
|
|
26
26
|
* scheme (e.g. a monorepo whose `android/` and `ios/` declare different values), discovery throws
|
|
27
27
|
* a usage error naming every conflicting source instead of picking one — silently choosing would
|
|
28
|
-
* mean `appduct link` opens the wrong app on whichever platform lost.
|
|
28
|
+
* mean `appduct sessions link` opens the wrong app on whichever platform lost.
|
|
29
29
|
*/
|
|
30
30
|
export type NativeSchemeSource = "android-gradle" | "android-manifest" | "ios-info-plist" | "ios-project-yml";
|
|
31
31
|
export type NativeSchemeProbeResult = {
|