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
|
@@ -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.");
|
|
@@ -1079,6 +1087,7 @@ var handleEventsCommand = async (options, context) => {
|
|
|
1079
1087
|
import pc from "picocolors";
|
|
1080
1088
|
import {
|
|
1081
1089
|
formatAgentWebSocketUrl,
|
|
1090
|
+
renderEventSignature,
|
|
1082
1091
|
renderToolSignature,
|
|
1083
1092
|
summarizeToolDescription
|
|
1084
1093
|
} from "@appduct/shared";
|
|
@@ -1179,6 +1188,13 @@ var formatDevice = (device) => {
|
|
|
1179
1188
|
const label = parts.length > 0 ? parts.join(" ") : "unknown device";
|
|
1180
1189
|
return device.os ? `${label} (${device.os})` : label;
|
|
1181
1190
|
};
|
|
1191
|
+
var SUSPEND_REASON_LABELS = {
|
|
1192
|
+
app_backgrounded: "app in background",
|
|
1193
|
+
connection_lost: "connection lost"
|
|
1194
|
+
};
|
|
1195
|
+
var formatSessionState = (session) => {
|
|
1196
|
+
return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
|
|
1197
|
+
};
|
|
1182
1198
|
var renderLsData = (colors, data, now) => {
|
|
1183
1199
|
if (data.length === 0) {
|
|
1184
1200
|
return [colors.dim("Sessions"), " No Appduct sessions are registered."];
|
|
@@ -1187,7 +1203,7 @@ var renderLsData = (colors, data, now) => {
|
|
|
1187
1203
|
const headers = ["Alias", "State", "Device", "Tools", "Age"];
|
|
1188
1204
|
const rows = data.map((session) => [
|
|
1189
1205
|
session.alias,
|
|
1190
|
-
session
|
|
1206
|
+
formatSessionState(session),
|
|
1191
1207
|
formatDevice(session.device),
|
|
1192
1208
|
String(session.toolCount),
|
|
1193
1209
|
formatAge(session.claimedAt ?? session.createdAt, nowIso)
|
|
@@ -1361,10 +1377,87 @@ var renderToolsListData = (colors, data, flags, full) => {
|
|
|
1361
1377
|
}
|
|
1362
1378
|
return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
|
|
1363
1379
|
};
|
|
1380
|
+
var renderEventsTruncationLine = (data) => {
|
|
1381
|
+
const offset = data.offset ?? 0;
|
|
1382
|
+
if (data.events.length >= data.total) {
|
|
1383
|
+
return [];
|
|
1384
|
+
}
|
|
1385
|
+
return [
|
|
1386
|
+
"",
|
|
1387
|
+
`Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
|
|
1388
|
+
];
|
|
1389
|
+
};
|
|
1390
|
+
var renderEventsListData = (colors, data, flags) => {
|
|
1391
|
+
const [only] = data.events;
|
|
1392
|
+
if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
|
|
1393
|
+
return renderFields(
|
|
1394
|
+
colors.green(`Event: ${only.name}`),
|
|
1395
|
+
[
|
|
1396
|
+
["Signature", renderEventSignature(only)],
|
|
1397
|
+
["Description", only.description],
|
|
1398
|
+
["Payload schema", only.payload_schema]
|
|
1399
|
+
],
|
|
1400
|
+
flags
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
if (data.events.length === 0 && data.total > 0) {
|
|
1404
|
+
return [
|
|
1405
|
+
colors.green("Events"),
|
|
1406
|
+
` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
|
|
1407
|
+
];
|
|
1408
|
+
}
|
|
1409
|
+
if (data.events.length === 0) {
|
|
1410
|
+
return [
|
|
1411
|
+
colors.green("Events"),
|
|
1412
|
+
data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
|
|
1413
|
+
];
|
|
1414
|
+
}
|
|
1415
|
+
const lines = data.events.flatMap((event) => [
|
|
1416
|
+
` ${renderEventSignature(event)}`,
|
|
1417
|
+
` ${summarizeToolDescription(event.description)}`
|
|
1418
|
+
]);
|
|
1419
|
+
return [
|
|
1420
|
+
colors.green("Events"),
|
|
1421
|
+
...lines,
|
|
1422
|
+
...renderEventsTruncationLine(data),
|
|
1423
|
+
"",
|
|
1424
|
+
"Run `appduct events ls --name <name>` for an event's full payload schema."
|
|
1425
|
+
];
|
|
1426
|
+
};
|
|
1364
1427
|
var renderInvokeData = (colors, data, flags) => {
|
|
1365
1428
|
return [colors.green("Result"), formatScalar(data, flags)];
|
|
1366
1429
|
};
|
|
1367
1430
|
var renderLinkData = (colors, data, flags, qr) => {
|
|
1431
|
+
if ("attached" in data) {
|
|
1432
|
+
return [
|
|
1433
|
+
colors.green("Page Attached"),
|
|
1434
|
+
...renderFields(
|
|
1435
|
+
"Page",
|
|
1436
|
+
[
|
|
1437
|
+
["Session", data.sessionId],
|
|
1438
|
+
["Tab", data.targetId],
|
|
1439
|
+
["URL", data.url],
|
|
1440
|
+
["Expires", new Date(data.expiresAt * 1e3).toISOString()]
|
|
1441
|
+
],
|
|
1442
|
+
flags
|
|
1443
|
+
)
|
|
1444
|
+
];
|
|
1445
|
+
}
|
|
1446
|
+
if ("script" in data) {
|
|
1447
|
+
return [
|
|
1448
|
+
colors.green("Link Created"),
|
|
1449
|
+
...renderFields(
|
|
1450
|
+
"Link",
|
|
1451
|
+
[
|
|
1452
|
+
["Session", data.sessionId],
|
|
1453
|
+
["URL", data.url],
|
|
1454
|
+
["Script", data.script],
|
|
1455
|
+
["Expires", new Date(data.expiresAt * 1e3).toISOString()]
|
|
1456
|
+
],
|
|
1457
|
+
flags
|
|
1458
|
+
)
|
|
1459
|
+
];
|
|
1460
|
+
}
|
|
1368
1461
|
const lines = [
|
|
1369
1462
|
colors.green("Link Created"),
|
|
1370
1463
|
...renderFields(
|
|
@@ -1485,6 +1578,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
|
|
|
1485
1578
|
["PID", data.daemon.pid],
|
|
1486
1579
|
["Started at", data.daemon.started_at],
|
|
1487
1580
|
["WSS port", data.daemon.wss_port],
|
|
1581
|
+
["Web port", data.daemon.web_port],
|
|
1488
1582
|
["Pinned keys", data.daemon.pinned_keys.length],
|
|
1489
1583
|
["Sessions", data.daemon.session_count]
|
|
1490
1584
|
],
|
|
@@ -1552,6 +1646,8 @@ var renderSuccessData = (colors, command, data, options) => {
|
|
|
1552
1646
|
return renderToolsListData(colors, data, flags, options.full);
|
|
1553
1647
|
case "tools describe":
|
|
1554
1648
|
return renderToolDetail(colors, data, flags);
|
|
1649
|
+
case "events ls":
|
|
1650
|
+
return renderEventsListData(colors, data, flags);
|
|
1555
1651
|
case "tools call":
|
|
1556
1652
|
return renderInvokeData(colors, data, flags);
|
|
1557
1653
|
case "sessions revoke":
|
|
@@ -1648,7 +1744,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
|
|
|
1648
1744
|
// EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
|
|
1649
1745
|
// missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
|
|
1650
1746
|
// code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
|
|
1651
|
-
// assertion failure
|
|
1747
|
+
// assertion failure.
|
|
1652
1748
|
inspection_error: 66,
|
|
1653
1749
|
connection_error: 70,
|
|
1654
1750
|
session_error: 71,
|
|
@@ -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.");
|
|
@@ -1079,6 +1087,7 @@ var handleEventsCommand = async (options, context) => {
|
|
|
1079
1087
|
import pc from "picocolors";
|
|
1080
1088
|
import {
|
|
1081
1089
|
formatAgentWebSocketUrl,
|
|
1090
|
+
renderEventSignature,
|
|
1082
1091
|
renderToolSignature,
|
|
1083
1092
|
summarizeToolDescription
|
|
1084
1093
|
} from "@appduct/shared";
|
|
@@ -1179,6 +1188,13 @@ var formatDevice = (device) => {
|
|
|
1179
1188
|
const label = parts.length > 0 ? parts.join(" ") : "unknown device";
|
|
1180
1189
|
return device.os ? `${label} (${device.os})` : label;
|
|
1181
1190
|
};
|
|
1191
|
+
var SUSPEND_REASON_LABELS = {
|
|
1192
|
+
app_backgrounded: "app in background",
|
|
1193
|
+
connection_lost: "connection lost"
|
|
1194
|
+
};
|
|
1195
|
+
var formatSessionState = (session) => {
|
|
1196
|
+
return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
|
|
1197
|
+
};
|
|
1182
1198
|
var renderLsData = (colors, data, now) => {
|
|
1183
1199
|
if (data.length === 0) {
|
|
1184
1200
|
return [colors.dim("Sessions"), " No Appduct sessions are registered."];
|
|
@@ -1187,7 +1203,7 @@ var renderLsData = (colors, data, now) => {
|
|
|
1187
1203
|
const headers = ["Alias", "State", "Device", "Tools", "Age"];
|
|
1188
1204
|
const rows = data.map((session) => [
|
|
1189
1205
|
session.alias,
|
|
1190
|
-
session
|
|
1206
|
+
formatSessionState(session),
|
|
1191
1207
|
formatDevice(session.device),
|
|
1192
1208
|
String(session.toolCount),
|
|
1193
1209
|
formatAge(session.claimedAt ?? session.createdAt, nowIso)
|
|
@@ -1361,10 +1377,87 @@ var renderToolsListData = (colors, data, flags, full) => {
|
|
|
1361
1377
|
}
|
|
1362
1378
|
return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
|
|
1363
1379
|
};
|
|
1380
|
+
var renderEventsTruncationLine = (data) => {
|
|
1381
|
+
const offset = data.offset ?? 0;
|
|
1382
|
+
if (data.events.length >= data.total) {
|
|
1383
|
+
return [];
|
|
1384
|
+
}
|
|
1385
|
+
return [
|
|
1386
|
+
"",
|
|
1387
|
+
`Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
|
|
1388
|
+
];
|
|
1389
|
+
};
|
|
1390
|
+
var renderEventsListData = (colors, data, flags) => {
|
|
1391
|
+
const [only] = data.events;
|
|
1392
|
+
if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
|
|
1393
|
+
return renderFields(
|
|
1394
|
+
colors.green(`Event: ${only.name}`),
|
|
1395
|
+
[
|
|
1396
|
+
["Signature", renderEventSignature(only)],
|
|
1397
|
+
["Description", only.description],
|
|
1398
|
+
["Payload schema", only.payload_schema]
|
|
1399
|
+
],
|
|
1400
|
+
flags
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
if (data.events.length === 0 && data.total > 0) {
|
|
1404
|
+
return [
|
|
1405
|
+
colors.green("Events"),
|
|
1406
|
+
` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
|
|
1407
|
+
];
|
|
1408
|
+
}
|
|
1409
|
+
if (data.events.length === 0) {
|
|
1410
|
+
return [
|
|
1411
|
+
colors.green("Events"),
|
|
1412
|
+
data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
|
|
1413
|
+
];
|
|
1414
|
+
}
|
|
1415
|
+
const lines = data.events.flatMap((event) => [
|
|
1416
|
+
` ${renderEventSignature(event)}`,
|
|
1417
|
+
` ${summarizeToolDescription(event.description)}`
|
|
1418
|
+
]);
|
|
1419
|
+
return [
|
|
1420
|
+
colors.green("Events"),
|
|
1421
|
+
...lines,
|
|
1422
|
+
...renderEventsTruncationLine(data),
|
|
1423
|
+
"",
|
|
1424
|
+
"Run `appduct events ls --name <name>` for an event's full payload schema."
|
|
1425
|
+
];
|
|
1426
|
+
};
|
|
1364
1427
|
var renderInvokeData = (colors, data, flags) => {
|
|
1365
1428
|
return [colors.green("Result"), formatScalar(data, flags)];
|
|
1366
1429
|
};
|
|
1367
1430
|
var renderLinkData = (colors, data, flags, qr) => {
|
|
1431
|
+
if ("attached" in data) {
|
|
1432
|
+
return [
|
|
1433
|
+
colors.green("Page Attached"),
|
|
1434
|
+
...renderFields(
|
|
1435
|
+
"Page",
|
|
1436
|
+
[
|
|
1437
|
+
["Session", data.sessionId],
|
|
1438
|
+
["Tab", data.targetId],
|
|
1439
|
+
["URL", data.url],
|
|
1440
|
+
["Expires", new Date(data.expiresAt * 1e3).toISOString()]
|
|
1441
|
+
],
|
|
1442
|
+
flags
|
|
1443
|
+
)
|
|
1444
|
+
];
|
|
1445
|
+
}
|
|
1446
|
+
if ("script" in data) {
|
|
1447
|
+
return [
|
|
1448
|
+
colors.green("Link Created"),
|
|
1449
|
+
...renderFields(
|
|
1450
|
+
"Link",
|
|
1451
|
+
[
|
|
1452
|
+
["Session", data.sessionId],
|
|
1453
|
+
["URL", data.url],
|
|
1454
|
+
["Script", data.script],
|
|
1455
|
+
["Expires", new Date(data.expiresAt * 1e3).toISOString()]
|
|
1456
|
+
],
|
|
1457
|
+
flags
|
|
1458
|
+
)
|
|
1459
|
+
];
|
|
1460
|
+
}
|
|
1368
1461
|
const lines = [
|
|
1369
1462
|
colors.green("Link Created"),
|
|
1370
1463
|
...renderFields(
|
|
@@ -1485,6 +1578,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
|
|
|
1485
1578
|
["PID", data.daemon.pid],
|
|
1486
1579
|
["Started at", data.daemon.started_at],
|
|
1487
1580
|
["WSS port", data.daemon.wss_port],
|
|
1581
|
+
["Web port", data.daemon.web_port],
|
|
1488
1582
|
["Pinned keys", data.daemon.pinned_keys.length],
|
|
1489
1583
|
["Sessions", data.daemon.session_count]
|
|
1490
1584
|
],
|
|
@@ -1552,6 +1646,8 @@ var renderSuccessData = (colors, command, data, options) => {
|
|
|
1552
1646
|
return renderToolsListData(colors, data, flags, options.full);
|
|
1553
1647
|
case "tools describe":
|
|
1554
1648
|
return renderToolDetail(colors, data, flags);
|
|
1649
|
+
case "events ls":
|
|
1650
|
+
return renderEventsListData(colors, data, flags);
|
|
1555
1651
|
case "tools call":
|
|
1556
1652
|
return renderInvokeData(colors, data, flags);
|
|
1557
1653
|
case "sessions revoke":
|
|
@@ -1632,7 +1728,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
|
|
|
1632
1728
|
// EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
|
|
1633
1729
|
// missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
|
|
1634
1730
|
// code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
|
|
1635
|
-
// assertion failure
|
|
1731
|
+
// assertion failure.
|
|
1636
1732
|
inspection_error: 66,
|
|
1637
1733
|
connection_error: 70,
|
|
1638
1734
|
session_error: 71,
|