appduct 0.13.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.
- package/README.md +55 -24
- package/dist/artifact-inspect.d.ts +38 -39
- package/dist/bin.js +106 -7
- package/dist/cli/result-types.d.ts +35 -2
- package/dist/cli/routes/daemon/index.js +90 -2
- package/dist/cli/routes/daemon/run.js +1088 -517
- package/dist/cli/routes/daemon/start.js +98 -2
- package/dist/cli/routes/daemon/status.js +99 -2
- package/dist/cli/routes/daemon/stop.js +98 -2
- package/dist/cli/routes/doctor.js +98 -3
- package/dist/cli/routes/events/index.d.ts +0 -3
- package/dist/cli/routes/events/index.js +92 -3
- package/dist/cli/routes/events/ls.d.ts +5 -0
- package/dist/cli/routes/events/ls.js +1837 -0
- package/dist/cli/routes/events/since.js +98 -2
- package/dist/cli/routes/events/tail.js +98 -2
- package/dist/cli/routes/init.js +265 -108
- package/dist/cli/routes/keygen.js +90 -2
- package/dist/cli/routes/mcp.js +243 -25
- package/dist/cli/routes/sessions/index.js +90 -2
- package/dist/cli/routes/sessions/link.js +199 -22
- package/dist/cli/routes/sessions/ls.js +98 -2
- package/dist/cli/routes/sessions/revoke.js +98 -2
- package/dist/cli/routes/tools/call.js +98 -2
- package/dist/cli/routes/tools/describe.js +98 -2
- package/dist/cli/routes/tools/index.js +90 -2
- package/dist/cli/routes/tools/ls.js +98 -2
- package/dist/client/attach-page.d.ts +39 -0
- package/dist/client/bootstrap.d.ts +17 -3
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.js +286 -112
- package/dist/commands/doctor.d.ts +3 -4
- package/dist/commands/events-ls.d.ts +21 -0
- package/dist/commands/init.d.ts +8 -2
- package/dist/commands/link.d.ts +10 -1
- package/dist/daemon/calls.d.ts +3 -1
- package/dist/daemon/config.d.ts +6 -0
- package/dist/daemon/daemon.d.ts +6 -1
- package/dist/daemon/event-bus.d.ts +10 -0
- package/dist/daemon/links.d.ts +9 -5
- package/dist/daemon/listener.d.ts +28 -1
- package/dist/daemon/registry.d.ts +20 -12
- package/dist/daemon/rpc-errors.d.ts +6 -0
- package/dist/daemon/sessions.d.ts +14 -8
- package/dist/daemon/tls.d.ts +0 -3
- package/dist/devtools-relay/attach-tab.d.ts +33 -0
- package/dist/devtools-relay/index.d.ts +8 -0
- package/dist/devtools-relay/memory-daemon-sockets.d.ts +20 -0
- package/dist/devtools-relay/memory-devtools-browser.d.ts +10 -0
- package/dist/devtools-relay/memory-page-channel.d.ts +16 -0
- package/dist/devtools-relay/node-daemon-socket.d.ts +3 -0
- package/dist/devtools-relay/node-devtools-browser.d.ts +3 -0
- package/dist/devtools-relay/ports.d.ts +48 -0
- package/dist/devtools-relay/relay.d.ts +16 -0
- package/dist/index.js +106 -7
- package/dist/link.d.ts +35 -1
- package/dist/mcp/app-tools.d.ts +19 -0
- package/dist/mcp/connect-tool.d.ts +15 -2
- package/dist/mcp/events-tool.d.ts +39 -0
- package/dist/native-scheme.d.ts +11 -7
- package/dist/scheme.d.ts +5 -1
- package/package.json +15 -5
|
@@ -27,6 +27,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
|
27
27
|
"daemonLogMaxBytes",
|
|
28
28
|
"eventsLogMaxBytes",
|
|
29
29
|
"policy",
|
|
30
|
+
"webOrigins",
|
|
30
31
|
"advertisedIp",
|
|
31
32
|
"scheme",
|
|
32
33
|
"restartDaemonOnVersionMismatch"
|
|
@@ -87,6 +88,7 @@ var defaultConfig = (paths) => {
|
|
|
87
88
|
daemonLogMaxBytes: DEFAULT_DAEMON_LOG_MAX_BYTES,
|
|
88
89
|
eventsLogMaxBytes: 10 * 1024 * 1024,
|
|
89
90
|
restartDaemonOnVersionMismatch: false,
|
|
91
|
+
webOrigins: [],
|
|
90
92
|
policy: {
|
|
91
93
|
default: "allow",
|
|
92
94
|
destructive: "allow"
|
|
@@ -157,6 +159,12 @@ var loadConfig = async (paths, options = {}) => {
|
|
|
157
159
|
"restartDaemonOnVersionMismatch"
|
|
158
160
|
);
|
|
159
161
|
}
|
|
162
|
+
if (parsed.webOrigins !== void 0) {
|
|
163
|
+
if (!Array.isArray(parsed.webOrigins)) {
|
|
164
|
+
throw configError("webOrigins", "must be an array of origin strings.");
|
|
165
|
+
}
|
|
166
|
+
config.webOrigins = parsed.webOrigins.map((origin) => requireNonEmptyString(origin, "webOrigins"));
|
|
167
|
+
}
|
|
160
168
|
if (parsed.policy !== void 0) {
|
|
161
169
|
if (typeof parsed.policy !== "object" || parsed.policy === null || Array.isArray(parsed.policy)) {
|
|
162
170
|
throw configError("policy", "must be an object.");
|
|
@@ -916,7 +924,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
|
|
|
916
924
|
// EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
|
|
917
925
|
// missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
|
|
918
926
|
// code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
|
|
919
|
-
// assertion failure
|
|
927
|
+
// assertion failure.
|
|
920
928
|
inspection_error: 66,
|
|
921
929
|
connection_error: 70,
|
|
922
930
|
session_error: 71,
|
|
@@ -1643,31 +1651,42 @@ var probeIosProjectYml = async (root) => {
|
|
|
1643
1651
|
};
|
|
1644
1652
|
};
|
|
1645
1653
|
var PROBES = [
|
|
1646
|
-
probeAndroidGradle,
|
|
1647
|
-
probeAndroidManifest,
|
|
1648
|
-
probeIosInfoPlist,
|
|
1649
|
-
probeIosProjectYml
|
|
1654
|
+
["android", probeAndroidGradle],
|
|
1655
|
+
["android", probeAndroidManifest],
|
|
1656
|
+
["ios", probeIosInfoPlist],
|
|
1657
|
+
["ios", probeIosProjectYml]
|
|
1650
1658
|
];
|
|
1659
|
+
var isFlutterProject = async (root) => {
|
|
1660
|
+
return (await readCappedText(join4(root, "pubspec.yaml"))).ok;
|
|
1661
|
+
};
|
|
1651
1662
|
var discoverNativeScheme = async (root) => {
|
|
1652
1663
|
const tried = [];
|
|
1653
1664
|
const results = [];
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1665
|
+
const flutter = await isFlutterProject(root);
|
|
1666
|
+
const androidRoot = flutter ? join4(root, "android") : root;
|
|
1667
|
+
const iosRoots = flutter ? [join4(root, "ios"), join4(root, "macos")] : [root];
|
|
1668
|
+
for (const [platform, probe] of PROBES) {
|
|
1669
|
+
let roots = [root];
|
|
1670
|
+
if (platform === "android") {
|
|
1671
|
+
roots = [androidRoot];
|
|
1672
|
+
} else if (probe === probeIosInfoPlist) {
|
|
1673
|
+
roots = iosRoots;
|
|
1674
|
+
}
|
|
1675
|
+
for (const probeRoot of roots) {
|
|
1676
|
+
const outcome = await probe(probeRoot);
|
|
1677
|
+
tried.push(...outcome.tried);
|
|
1678
|
+
if (outcome.result !== void 0) {
|
|
1679
|
+
results.push(outcome.result);
|
|
1680
|
+
}
|
|
1659
1681
|
}
|
|
1660
1682
|
}
|
|
1661
|
-
if (results.length === 0) {
|
|
1662
|
-
return { tried };
|
|
1663
|
-
}
|
|
1664
1683
|
const distinctSchemes = new Set(results.map((entry) => entry.scheme));
|
|
1665
1684
|
if (distinctSchemes.size > 1) {
|
|
1666
1685
|
throw usageError(
|
|
1667
1686
|
"Conflicting deep-link schemes found in this project: " + results.map((entry) => `${entry.origin} says ${JSON.stringify(entry.scheme)}`).join(", but ") + ". Appduct will not guess which one is right \u2014 pass --scheme <scheme> to pick one."
|
|
1668
1687
|
);
|
|
1669
1688
|
}
|
|
1670
|
-
return {
|
|
1689
|
+
return { results, tried };
|
|
1671
1690
|
};
|
|
1672
1691
|
|
|
1673
1692
|
// src/scheme.ts
|
|
@@ -1730,19 +1749,21 @@ var discoverStaticProjectScheme = async (cwd) => {
|
|
|
1730
1749
|
const tried = [`${appJsonPath} ("expo.scheme")`];
|
|
1731
1750
|
const appJsonScheme = await discoverExpoScheme(cwd);
|
|
1732
1751
|
if (appJsonScheme !== void 0) {
|
|
1733
|
-
return { scheme: appJsonScheme, source: "app-json", origin: appJsonPath, tried };
|
|
1752
|
+
return { scheme: appJsonScheme, source: "app-json", origin: appJsonPath, nativeResults: [], tried };
|
|
1734
1753
|
}
|
|
1735
1754
|
const native = await discoverNativeScheme(resolve(cwd));
|
|
1736
1755
|
tried.push(...native.tried);
|
|
1737
|
-
|
|
1756
|
+
const [first] = native.results;
|
|
1757
|
+
if (first !== void 0) {
|
|
1738
1758
|
return {
|
|
1739
|
-
scheme:
|
|
1740
|
-
source:
|
|
1741
|
-
origin:
|
|
1759
|
+
scheme: first.scheme,
|
|
1760
|
+
source: first.source,
|
|
1761
|
+
origin: first.origin,
|
|
1762
|
+
nativeResults: native.results,
|
|
1742
1763
|
tried
|
|
1743
1764
|
};
|
|
1744
1765
|
}
|
|
1745
|
-
return { tried };
|
|
1766
|
+
return { nativeResults: [], tried };
|
|
1746
1767
|
};
|
|
1747
1768
|
var globalConfigDirs = (options = {}) => {
|
|
1748
1769
|
const excluded = /* @__PURE__ */ new Set([join5(resolve(options.homeDir ?? homedir()), PROJECT_CONFIG_DIR)]);
|
|
@@ -1926,6 +1947,39 @@ ${tried.map((location, index) => ` ${index + 1}. ${location}`).join("\n")}
|
|
|
1926
1947
|
// src/link.ts
|
|
1927
1948
|
var OPEN_TARGET_ADDRESS_OVERRIDE = "127.0.0.1";
|
|
1928
1949
|
var composeDeepLink = (scheme, result) => `${scheme}:///?appduct=${result.deepLinkPayload}&pin=${encodeURIComponent(result.pin)}`;
|
|
1950
|
+
var assertWebPageUrl = (pageUrl) => {
|
|
1951
|
+
let protocol;
|
|
1952
|
+
try {
|
|
1953
|
+
protocol = new URL(pageUrl).protocol;
|
|
1954
|
+
} catch {
|
|
1955
|
+
}
|
|
1956
|
+
if (protocol !== "http:" && protocol !== "https:") {
|
|
1957
|
+
throw usageError(`"url" must be an http(s) URL (got "${pageUrl}").`);
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
var composeWebLink = (pageUrl, result) => {
|
|
1961
|
+
const payload = result.deepLinkPayload;
|
|
1962
|
+
return {
|
|
1963
|
+
sessionId: result.sessionId,
|
|
1964
|
+
url: `${pageUrl.split("#")[0]}#appduct=${payload}`,
|
|
1965
|
+
script: `window.__APPDUCT__.connect("${payload}")`,
|
|
1966
|
+
expiresAt: result.expiresAt
|
|
1967
|
+
};
|
|
1968
|
+
};
|
|
1969
|
+
var mintWebLink = async (options) => {
|
|
1970
|
+
assertWebPageUrl(options.url);
|
|
1971
|
+
const result = await callDaemon(
|
|
1972
|
+
RPC_METHODS2.linkCreate,
|
|
1973
|
+
{ ttlSeconds: options.ttlSeconds, transport: "web" },
|
|
1974
|
+
{ stateDir: options.stateDir, spawn: options.spawn, autoSpawn: options.autoSpawn }
|
|
1975
|
+
);
|
|
1976
|
+
return composeWebLink(options.url, result);
|
|
1977
|
+
};
|
|
1978
|
+
var attachWebTab = async ({ stateDir, spawn, autoSpawn, ...params }) => {
|
|
1979
|
+
assertWebPageUrl(params.url);
|
|
1980
|
+
const result = await callDaemon(RPC_METHODS2.webAttach, params, { stateDir, spawn, autoSpawn, requestTimeoutMs: 3e4 });
|
|
1981
|
+
return { ...result, attached: true };
|
|
1982
|
+
};
|
|
1929
1983
|
var mintLink = async (options) => {
|
|
1930
1984
|
if (options.target !== void 0 && !isOpenTarget(options.target)) {
|
|
1931
1985
|
throw usageError(
|
|
@@ -2013,6 +2067,9 @@ var mintLink = async (options) => {
|
|
|
2013
2067
|
// src/commands/link.ts
|
|
2014
2068
|
var handleLinkCommand = async (options, context) => {
|
|
2015
2069
|
let openTarget;
|
|
2070
|
+
if (options.browserUrl !== void 0 || options.targetId !== void 0) {
|
|
2071
|
+
throw usageError('"--browser-url" and "--target-id" only apply with "--open web".');
|
|
2072
|
+
}
|
|
2016
2073
|
if (options.open !== void 0) {
|
|
2017
2074
|
if (!isOpenTarget(options.open)) {
|
|
2018
2075
|
throw usageError(
|
|
@@ -2046,6 +2103,35 @@ var handleLinkCommand = async (options, context) => {
|
|
|
2046
2103
|
});
|
|
2047
2104
|
return { ok: true, data: result };
|
|
2048
2105
|
};
|
|
2106
|
+
var handleWebLinkCommand = async (options, context) => {
|
|
2107
|
+
if (options.url === void 0) {
|
|
2108
|
+
throw usageError('"--open web" needs the page URL: appduct sessions link --open web <url>.');
|
|
2109
|
+
}
|
|
2110
|
+
if (options.device !== void 0 || options.appId !== void 0 || options.relaunch !== void 0) {
|
|
2111
|
+
throw usageError('"--device", "--app-id" and "--relaunch" do not apply with "--open web".');
|
|
2112
|
+
}
|
|
2113
|
+
if (options.targetId !== void 0 && options.browserUrl === void 0) {
|
|
2114
|
+
throw usageError('"--target-id" needs "--browser-url": it names a tab of that browser.');
|
|
2115
|
+
}
|
|
2116
|
+
if (options.browserUrl !== void 0) {
|
|
2117
|
+
const attached = await attachWebTab({
|
|
2118
|
+
stateDir: context.stateDir,
|
|
2119
|
+
spawn: context.spawn,
|
|
2120
|
+
ttlSeconds: options.ttlSeconds,
|
|
2121
|
+
url: options.url,
|
|
2122
|
+
browserUrl: options.browserUrl,
|
|
2123
|
+
targetId: options.targetId
|
|
2124
|
+
});
|
|
2125
|
+
return { ok: true, data: attached };
|
|
2126
|
+
}
|
|
2127
|
+
const result = await mintWebLink({
|
|
2128
|
+
stateDir: context.stateDir,
|
|
2129
|
+
spawn: context.spawn,
|
|
2130
|
+
ttlSeconds: options.ttlSeconds,
|
|
2131
|
+
url: options.url
|
|
2132
|
+
});
|
|
2133
|
+
return { ok: true, data: result };
|
|
2134
|
+
};
|
|
2049
2135
|
|
|
2050
2136
|
// src/cli/command-options.ts
|
|
2051
2137
|
var parsePositiveIntegerOption = (value, flagName) => {
|
|
@@ -2068,6 +2154,7 @@ var formatJson = (value, flags) => {
|
|
|
2068
2154
|
import pc from "picocolors";
|
|
2069
2155
|
import {
|
|
2070
2156
|
formatAgentWebSocketUrl,
|
|
2157
|
+
renderEventSignature,
|
|
2071
2158
|
renderToolSignature,
|
|
2072
2159
|
summarizeToolDescription
|
|
2073
2160
|
} from "@appduct/shared";
|
|
@@ -2163,6 +2250,13 @@ var formatDevice = (device) => {
|
|
|
2163
2250
|
const label = parts.length > 0 ? parts.join(" ") : "unknown device";
|
|
2164
2251
|
return device.os ? `${label} (${device.os})` : label;
|
|
2165
2252
|
};
|
|
2253
|
+
var SUSPEND_REASON_LABELS = {
|
|
2254
|
+
app_backgrounded: "app in background",
|
|
2255
|
+
connection_lost: "connection lost"
|
|
2256
|
+
};
|
|
2257
|
+
var formatSessionState = (session) => {
|
|
2258
|
+
return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
|
|
2259
|
+
};
|
|
2166
2260
|
var renderLsData = (colors, data, now) => {
|
|
2167
2261
|
if (data.length === 0) {
|
|
2168
2262
|
return [colors.dim("Sessions"), " No Appduct sessions are registered."];
|
|
@@ -2171,7 +2265,7 @@ var renderLsData = (colors, data, now) => {
|
|
|
2171
2265
|
const headers = ["Alias", "State", "Device", "Tools", "Age"];
|
|
2172
2266
|
const rows = data.map((session) => [
|
|
2173
2267
|
session.alias,
|
|
2174
|
-
session
|
|
2268
|
+
formatSessionState(session),
|
|
2175
2269
|
formatDevice(session.device),
|
|
2176
2270
|
String(session.toolCount),
|
|
2177
2271
|
formatAge(session.claimedAt ?? session.createdAt, nowIso)
|
|
@@ -2345,10 +2439,87 @@ var renderToolsListData = (colors, data, flags, full) => {
|
|
|
2345
2439
|
}
|
|
2346
2440
|
return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
|
|
2347
2441
|
};
|
|
2442
|
+
var renderEventsTruncationLine = (data) => {
|
|
2443
|
+
const offset = data.offset ?? 0;
|
|
2444
|
+
if (data.events.length >= data.total) {
|
|
2445
|
+
return [];
|
|
2446
|
+
}
|
|
2447
|
+
return [
|
|
2448
|
+
"",
|
|
2449
|
+
`Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
|
|
2450
|
+
];
|
|
2451
|
+
};
|
|
2452
|
+
var renderEventsListData = (colors, data, flags) => {
|
|
2453
|
+
const [only] = data.events;
|
|
2454
|
+
if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
|
|
2455
|
+
return renderFields(
|
|
2456
|
+
colors.green(`Event: ${only.name}`),
|
|
2457
|
+
[
|
|
2458
|
+
["Signature", renderEventSignature(only)],
|
|
2459
|
+
["Description", only.description],
|
|
2460
|
+
["Payload schema", only.payload_schema]
|
|
2461
|
+
],
|
|
2462
|
+
flags
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2465
|
+
if (data.events.length === 0 && data.total > 0) {
|
|
2466
|
+
return [
|
|
2467
|
+
colors.green("Events"),
|
|
2468
|
+
` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
|
|
2469
|
+
];
|
|
2470
|
+
}
|
|
2471
|
+
if (data.events.length === 0) {
|
|
2472
|
+
return [
|
|
2473
|
+
colors.green("Events"),
|
|
2474
|
+
data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
|
|
2475
|
+
];
|
|
2476
|
+
}
|
|
2477
|
+
const lines = data.events.flatMap((event) => [
|
|
2478
|
+
` ${renderEventSignature(event)}`,
|
|
2479
|
+
` ${summarizeToolDescription(event.description)}`
|
|
2480
|
+
]);
|
|
2481
|
+
return [
|
|
2482
|
+
colors.green("Events"),
|
|
2483
|
+
...lines,
|
|
2484
|
+
...renderEventsTruncationLine(data),
|
|
2485
|
+
"",
|
|
2486
|
+
"Run `appduct events ls --name <name>` for an event's full payload schema."
|
|
2487
|
+
];
|
|
2488
|
+
};
|
|
2348
2489
|
var renderInvokeData = (colors, data, flags) => {
|
|
2349
2490
|
return [colors.green("Result"), formatScalar(data, flags)];
|
|
2350
2491
|
};
|
|
2351
2492
|
var renderLinkData = (colors, data, flags, qr) => {
|
|
2493
|
+
if ("attached" in data) {
|
|
2494
|
+
return [
|
|
2495
|
+
colors.green("Page Attached"),
|
|
2496
|
+
...renderFields(
|
|
2497
|
+
"Page",
|
|
2498
|
+
[
|
|
2499
|
+
["Session", data.sessionId],
|
|
2500
|
+
["Tab", data.targetId],
|
|
2501
|
+
["URL", data.url],
|
|
2502
|
+
["Expires", new Date(data.expiresAt * 1e3).toISOString()]
|
|
2503
|
+
],
|
|
2504
|
+
flags
|
|
2505
|
+
)
|
|
2506
|
+
];
|
|
2507
|
+
}
|
|
2508
|
+
if ("script" in data) {
|
|
2509
|
+
return [
|
|
2510
|
+
colors.green("Link Created"),
|
|
2511
|
+
...renderFields(
|
|
2512
|
+
"Link",
|
|
2513
|
+
[
|
|
2514
|
+
["Session", data.sessionId],
|
|
2515
|
+
["URL", data.url],
|
|
2516
|
+
["Script", data.script],
|
|
2517
|
+
["Expires", new Date(data.expiresAt * 1e3).toISOString()]
|
|
2518
|
+
],
|
|
2519
|
+
flags
|
|
2520
|
+
)
|
|
2521
|
+
];
|
|
2522
|
+
}
|
|
2352
2523
|
const lines = [
|
|
2353
2524
|
colors.green("Link Created"),
|
|
2354
2525
|
...renderFields(
|
|
@@ -2469,6 +2640,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
|
|
|
2469
2640
|
["PID", data.daemon.pid],
|
|
2470
2641
|
["Started at", data.daemon.started_at],
|
|
2471
2642
|
["WSS port", data.daemon.wss_port],
|
|
2643
|
+
["Web port", data.daemon.web_port],
|
|
2472
2644
|
["Pinned keys", data.daemon.pinned_keys.length],
|
|
2473
2645
|
["Sessions", data.daemon.session_count]
|
|
2474
2646
|
],
|
|
@@ -2536,6 +2708,8 @@ var renderSuccessData = (colors, command, data, options) => {
|
|
|
2536
2708
|
return renderToolsListData(colors, data, flags, options.full);
|
|
2537
2709
|
case "tools describe":
|
|
2538
2710
|
return renderToolDetail(colors, data, flags);
|
|
2711
|
+
case "events ls":
|
|
2712
|
+
return renderEventsListData(colors, data, flags);
|
|
2539
2713
|
case "tools call":
|
|
2540
2714
|
return renderInvokeData(colors, data, flags);
|
|
2541
2715
|
case "sessions revoke":
|
|
@@ -2690,10 +2864,13 @@ var route = async (context) => {
|
|
|
2690
2864
|
return executeCommand(
|
|
2691
2865
|
commandName(context),
|
|
2692
2866
|
guarded(context)(
|
|
2693
|
-
() => handleLinkCommand(
|
|
2867
|
+
() => (options.open === "web" ? handleWebLinkCommand : handleLinkCommand)(
|
|
2694
2868
|
{
|
|
2695
2869
|
ttlSeconds: parsePositiveIntegerOption(options.ttl, "--ttl"),
|
|
2696
2870
|
scheme: typeof options.scheme === "string" ? options.scheme : void 0,
|
|
2871
|
+
url: context.args[0],
|
|
2872
|
+
browserUrl: typeof options.browserUrl === "string" ? options.browserUrl : void 0,
|
|
2873
|
+
targetId: typeof options.targetId === "string" ? options.targetId : void 0,
|
|
2697
2874
|
open: typeof options.open === "string" ? options.open : void 0,
|
|
2698
2875
|
device: typeof options.device === "string" ? options.device : void 0,
|
|
2699
2876
|
// cac camelCases `--app-id`; the dashed spelling is kept as a fallback so a
|
|
@@ -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
|
|
934
|
+
// assertion failure.
|
|
927
935
|
inspection_error: 66,
|
|
928
936
|
connection_error: 70,
|
|
929
937
|
session_error: 71,
|
|
@@ -1040,6 +1048,7 @@ var formatJson = (value, flags) => {
|
|
|
1040
1048
|
import pc from "picocolors";
|
|
1041
1049
|
import {
|
|
1042
1050
|
formatAgentWebSocketUrl,
|
|
1051
|
+
renderEventSignature,
|
|
1043
1052
|
renderToolSignature,
|
|
1044
1053
|
summarizeToolDescription
|
|
1045
1054
|
} from "@appduct/shared";
|
|
@@ -1135,6 +1144,13 @@ var formatDevice = (device) => {
|
|
|
1135
1144
|
const label = parts.length > 0 ? parts.join(" ") : "unknown device";
|
|
1136
1145
|
return device.os ? `${label} (${device.os})` : label;
|
|
1137
1146
|
};
|
|
1147
|
+
var SUSPEND_REASON_LABELS = {
|
|
1148
|
+
app_backgrounded: "app in background",
|
|
1149
|
+
connection_lost: "connection lost"
|
|
1150
|
+
};
|
|
1151
|
+
var formatSessionState = (session) => {
|
|
1152
|
+
return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
|
|
1153
|
+
};
|
|
1138
1154
|
var renderLsData = (colors, data, now) => {
|
|
1139
1155
|
if (data.length === 0) {
|
|
1140
1156
|
return [colors.dim("Sessions"), " No Appduct sessions are registered."];
|
|
@@ -1143,7 +1159,7 @@ var renderLsData = (colors, data, now) => {
|
|
|
1143
1159
|
const headers = ["Alias", "State", "Device", "Tools", "Age"];
|
|
1144
1160
|
const rows = data.map((session) => [
|
|
1145
1161
|
session.alias,
|
|
1146
|
-
session
|
|
1162
|
+
formatSessionState(session),
|
|
1147
1163
|
formatDevice(session.device),
|
|
1148
1164
|
String(session.toolCount),
|
|
1149
1165
|
formatAge(session.claimedAt ?? session.createdAt, nowIso)
|
|
@@ -1317,10 +1333,87 @@ var renderToolsListData = (colors, data, flags, full) => {
|
|
|
1317
1333
|
}
|
|
1318
1334
|
return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
|
|
1319
1335
|
};
|
|
1336
|
+
var renderEventsTruncationLine = (data) => {
|
|
1337
|
+
const offset = data.offset ?? 0;
|
|
1338
|
+
if (data.events.length >= data.total) {
|
|
1339
|
+
return [];
|
|
1340
|
+
}
|
|
1341
|
+
return [
|
|
1342
|
+
"",
|
|
1343
|
+
`Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
|
|
1344
|
+
];
|
|
1345
|
+
};
|
|
1346
|
+
var renderEventsListData = (colors, data, flags) => {
|
|
1347
|
+
const [only] = data.events;
|
|
1348
|
+
if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
|
|
1349
|
+
return renderFields(
|
|
1350
|
+
colors.green(`Event: ${only.name}`),
|
|
1351
|
+
[
|
|
1352
|
+
["Signature", renderEventSignature(only)],
|
|
1353
|
+
["Description", only.description],
|
|
1354
|
+
["Payload schema", only.payload_schema]
|
|
1355
|
+
],
|
|
1356
|
+
flags
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
if (data.events.length === 0 && data.total > 0) {
|
|
1360
|
+
return [
|
|
1361
|
+
colors.green("Events"),
|
|
1362
|
+
` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
|
|
1363
|
+
];
|
|
1364
|
+
}
|
|
1365
|
+
if (data.events.length === 0) {
|
|
1366
|
+
return [
|
|
1367
|
+
colors.green("Events"),
|
|
1368
|
+
data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
|
|
1369
|
+
];
|
|
1370
|
+
}
|
|
1371
|
+
const lines = data.events.flatMap((event) => [
|
|
1372
|
+
` ${renderEventSignature(event)}`,
|
|
1373
|
+
` ${summarizeToolDescription(event.description)}`
|
|
1374
|
+
]);
|
|
1375
|
+
return [
|
|
1376
|
+
colors.green("Events"),
|
|
1377
|
+
...lines,
|
|
1378
|
+
...renderEventsTruncationLine(data),
|
|
1379
|
+
"",
|
|
1380
|
+
"Run `appduct events ls --name <name>` for an event's full payload schema."
|
|
1381
|
+
];
|
|
1382
|
+
};
|
|
1320
1383
|
var renderInvokeData = (colors, data, flags) => {
|
|
1321
1384
|
return [colors.green("Result"), formatScalar(data, flags)];
|
|
1322
1385
|
};
|
|
1323
1386
|
var renderLinkData = (colors, data, flags, qr) => {
|
|
1387
|
+
if ("attached" in data) {
|
|
1388
|
+
return [
|
|
1389
|
+
colors.green("Page Attached"),
|
|
1390
|
+
...renderFields(
|
|
1391
|
+
"Page",
|
|
1392
|
+
[
|
|
1393
|
+
["Session", data.sessionId],
|
|
1394
|
+
["Tab", data.targetId],
|
|
1395
|
+
["URL", data.url],
|
|
1396
|
+
["Expires", new Date(data.expiresAt * 1e3).toISOString()]
|
|
1397
|
+
],
|
|
1398
|
+
flags
|
|
1399
|
+
)
|
|
1400
|
+
];
|
|
1401
|
+
}
|
|
1402
|
+
if ("script" in data) {
|
|
1403
|
+
return [
|
|
1404
|
+
colors.green("Link Created"),
|
|
1405
|
+
...renderFields(
|
|
1406
|
+
"Link",
|
|
1407
|
+
[
|
|
1408
|
+
["Session", data.sessionId],
|
|
1409
|
+
["URL", data.url],
|
|
1410
|
+
["Script", data.script],
|
|
1411
|
+
["Expires", new Date(data.expiresAt * 1e3).toISOString()]
|
|
1412
|
+
],
|
|
1413
|
+
flags
|
|
1414
|
+
)
|
|
1415
|
+
];
|
|
1416
|
+
}
|
|
1324
1417
|
const lines = [
|
|
1325
1418
|
colors.green("Link Created"),
|
|
1326
1419
|
...renderFields(
|
|
@@ -1441,6 +1534,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
|
|
|
1441
1534
|
["PID", data.daemon.pid],
|
|
1442
1535
|
["Started at", data.daemon.started_at],
|
|
1443
1536
|
["WSS port", data.daemon.wss_port],
|
|
1537
|
+
["Web port", data.daemon.web_port],
|
|
1444
1538
|
["Pinned keys", data.daemon.pinned_keys.length],
|
|
1445
1539
|
["Sessions", data.daemon.session_count]
|
|
1446
1540
|
],
|
|
@@ -1508,6 +1602,8 @@ var renderSuccessData = (colors, command, data, options) => {
|
|
|
1508
1602
|
return renderToolsListData(colors, data, flags, options.full);
|
|
1509
1603
|
case "tools describe":
|
|
1510
1604
|
return renderToolDetail(colors, data, flags);
|
|
1605
|
+
case "events ls":
|
|
1606
|
+
return renderEventsListData(colors, data, flags);
|
|
1511
1607
|
case "tools call":
|
|
1512
1608
|
return renderInvokeData(colors, data, flags);
|
|
1513
1609
|
case "sessions revoke":
|
|
@@ -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
|
|
934
|
+
// assertion failure.
|
|
927
935
|
inspection_error: 66,
|
|
928
936
|
connection_error: 70,
|
|
929
937
|
session_error: 71,
|
|
@@ -1051,6 +1059,7 @@ var formatJson = (value, flags) => {
|
|
|
1051
1059
|
import pc from "picocolors";
|
|
1052
1060
|
import {
|
|
1053
1061
|
formatAgentWebSocketUrl,
|
|
1062
|
+
renderEventSignature,
|
|
1054
1063
|
renderToolSignature,
|
|
1055
1064
|
summarizeToolDescription
|
|
1056
1065
|
} from "@appduct/shared";
|
|
@@ -1146,6 +1155,13 @@ var formatDevice = (device) => {
|
|
|
1146
1155
|
const label = parts.length > 0 ? parts.join(" ") : "unknown device";
|
|
1147
1156
|
return device.os ? `${label} (${device.os})` : label;
|
|
1148
1157
|
};
|
|
1158
|
+
var SUSPEND_REASON_LABELS = {
|
|
1159
|
+
app_backgrounded: "app in background",
|
|
1160
|
+
connection_lost: "connection lost"
|
|
1161
|
+
};
|
|
1162
|
+
var formatSessionState = (session) => {
|
|
1163
|
+
return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
|
|
1164
|
+
};
|
|
1149
1165
|
var renderLsData = (colors, data, now) => {
|
|
1150
1166
|
if (data.length === 0) {
|
|
1151
1167
|
return [colors.dim("Sessions"), " No Appduct sessions are registered."];
|
|
@@ -1154,7 +1170,7 @@ var renderLsData = (colors, data, now) => {
|
|
|
1154
1170
|
const headers = ["Alias", "State", "Device", "Tools", "Age"];
|
|
1155
1171
|
const rows = data.map((session) => [
|
|
1156
1172
|
session.alias,
|
|
1157
|
-
session
|
|
1173
|
+
formatSessionState(session),
|
|
1158
1174
|
formatDevice(session.device),
|
|
1159
1175
|
String(session.toolCount),
|
|
1160
1176
|
formatAge(session.claimedAt ?? session.createdAt, nowIso)
|
|
@@ -1328,10 +1344,87 @@ var renderToolsListData = (colors, data, flags, full) => {
|
|
|
1328
1344
|
}
|
|
1329
1345
|
return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
|
|
1330
1346
|
};
|
|
1347
|
+
var renderEventsTruncationLine = (data) => {
|
|
1348
|
+
const offset = data.offset ?? 0;
|
|
1349
|
+
if (data.events.length >= data.total) {
|
|
1350
|
+
return [];
|
|
1351
|
+
}
|
|
1352
|
+
return [
|
|
1353
|
+
"",
|
|
1354
|
+
`Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
|
|
1355
|
+
];
|
|
1356
|
+
};
|
|
1357
|
+
var renderEventsListData = (colors, data, flags) => {
|
|
1358
|
+
const [only] = data.events;
|
|
1359
|
+
if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
|
|
1360
|
+
return renderFields(
|
|
1361
|
+
colors.green(`Event: ${only.name}`),
|
|
1362
|
+
[
|
|
1363
|
+
["Signature", renderEventSignature(only)],
|
|
1364
|
+
["Description", only.description],
|
|
1365
|
+
["Payload schema", only.payload_schema]
|
|
1366
|
+
],
|
|
1367
|
+
flags
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
if (data.events.length === 0 && data.total > 0) {
|
|
1371
|
+
return [
|
|
1372
|
+
colors.green("Events"),
|
|
1373
|
+
` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
|
|
1374
|
+
];
|
|
1375
|
+
}
|
|
1376
|
+
if (data.events.length === 0) {
|
|
1377
|
+
return [
|
|
1378
|
+
colors.green("Events"),
|
|
1379
|
+
data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
|
|
1380
|
+
];
|
|
1381
|
+
}
|
|
1382
|
+
const lines = data.events.flatMap((event) => [
|
|
1383
|
+
` ${renderEventSignature(event)}`,
|
|
1384
|
+
` ${summarizeToolDescription(event.description)}`
|
|
1385
|
+
]);
|
|
1386
|
+
return [
|
|
1387
|
+
colors.green("Events"),
|
|
1388
|
+
...lines,
|
|
1389
|
+
...renderEventsTruncationLine(data),
|
|
1390
|
+
"",
|
|
1391
|
+
"Run `appduct events ls --name <name>` for an event's full payload schema."
|
|
1392
|
+
];
|
|
1393
|
+
};
|
|
1331
1394
|
var renderInvokeData = (colors, data, flags) => {
|
|
1332
1395
|
return [colors.green("Result"), formatScalar(data, flags)];
|
|
1333
1396
|
};
|
|
1334
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
|
+
}
|
|
1335
1428
|
const lines = [
|
|
1336
1429
|
colors.green("Link Created"),
|
|
1337
1430
|
...renderFields(
|
|
@@ -1452,6 +1545,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
|
|
|
1452
1545
|
["PID", data.daemon.pid],
|
|
1453
1546
|
["Started at", data.daemon.started_at],
|
|
1454
1547
|
["WSS port", data.daemon.wss_port],
|
|
1548
|
+
["Web port", data.daemon.web_port],
|
|
1455
1549
|
["Pinned keys", data.daemon.pinned_keys.length],
|
|
1456
1550
|
["Sessions", data.daemon.session_count]
|
|
1457
1551
|
],
|
|
@@ -1519,6 +1613,8 @@ var renderSuccessData = (colors, command, data, options) => {
|
|
|
1519
1613
|
return renderToolsListData(colors, data, flags, options.full);
|
|
1520
1614
|
case "tools describe":
|
|
1521
1615
|
return renderToolDetail(colors, data, flags);
|
|
1616
|
+
case "events ls":
|
|
1617
|
+
return renderEventsListData(colors, data, flags);
|
|
1522
1618
|
case "tools call":
|
|
1523
1619
|
return renderInvokeData(colors, data, flags);
|
|
1524
1620
|
case "sessions revoke":
|