@prismshadow/penguin-server 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/types.d.ts +335 -25
- package/dist/chunk-4SXGU7RO.js +50 -0
- package/dist/chunk-4SXGU7RO.js.map +1 -0
- package/dist/chunk-TTGGJDNA.js +14 -0
- package/dist/chunk-TTGGJDNA.js.map +1 -0
- package/dist/{chunk-KQQ3HSTK.js → chunk-WKJ3ANJP.js} +87 -6
- package/dist/chunk-WKJ3ANJP.js.map +1 -0
- package/dist/hmr/manifest.d.ts +24 -1
- package/dist/hmr/manifest.js +6 -22
- package/dist/hmr/manifest.js.map +1 -1
- package/dist/index.js +15699 -12968
- package/dist/index.js.map +1 -1
- package/dist/reset-admin-password.js +1 -1
- package/dist/version-report.d.ts +12 -0
- package/dist/version-report.js +8 -0
- package/dist/version-report.js.map +1 -0
- package/package.json +8 -3
- package/web-dist/assets/{addon-fit-ClUHZ6NU.js → addon-fit-CE-3eCvv.js} +1 -1
- package/web-dist/assets/{addon-web-links-BKMSMMvN.js → addon-web-links-DeODgawX.js} +1 -1
- package/web-dist/assets/{highlighter-Cp5vYdaX.js → highlighter-DpnM5FfD.js} +1 -1
- package/web-dist/assets/index-aRsvImZt.css +1 -0
- package/web-dist/assets/index-gNzir_kH.js +189 -0
- package/web-dist/assets/{xterm-j28bzvOQ.js → xterm-BjU-ZsL5.js} +1 -1
- package/web-dist/index.html +2 -2
- package/dist/chunk-KQQ3HSTK.js.map +0 -1
- package/web-dist/assets/index-BJNmFD1m.js +0 -187
- package/web-dist/assets/index-BneOZQwC.css +0 -1
package/dist/api/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OmniMessage, ToolCallPayload, CompactionMode } from '@prismshadow/penguin-core/omnimessage';
|
|
2
2
|
import { ThinkingLevelName, ToolDefinitionConfig, MCPServerConfig } from '@prismshadow/penguin-core/interfaces';
|
|
3
|
+
import { VersionReport } from '@prismshadow/penguin-core';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Web API DTO contract — request/response types shared between server routes and the
|
|
@@ -69,9 +70,10 @@ interface MeResponse {
|
|
|
69
70
|
* How THIS session was established. Distinct from desktopMode: a browser signed into a
|
|
70
71
|
* desktop-mode server holds a "password" session and must still provide the old
|
|
71
72
|
* password when changing it — only "desktop" sessions (opened by the shell's one-shot
|
|
72
|
-
* token) may omit it.
|
|
73
|
+
* token) may omit it. "token" marks a request authenticated by the local API token's
|
|
74
|
+
* Bearer header (the CLI and agent-driven calls).
|
|
73
75
|
*/
|
|
74
|
-
sessionVia: "password" | "desktop";
|
|
76
|
+
sessionVia: "password" | "desktop" | "token";
|
|
75
77
|
/**
|
|
76
78
|
* The upload limits currently in force, so the composer can refuse an oversize pick before
|
|
77
79
|
* reading it and can name the real number in the message. They are admin-settable and ride
|
|
@@ -216,12 +218,6 @@ interface UiPrefs {
|
|
|
216
218
|
lastProjectId?: string;
|
|
217
219
|
/** Whether the "no API key configured" guide has already been shown: once ever (on first visit to the chat page). */
|
|
218
220
|
credentialGuideSeen?: boolean;
|
|
219
|
-
/**
|
|
220
|
-
* Also list CLI-created Sessions in the sidebar (`cli=1` on the sessions list). Default
|
|
221
|
-
* off: the list then serves web rows straight from the DB, with no Trace-directory
|
|
222
|
-
* scanning (#139).
|
|
223
|
-
*/
|
|
224
|
-
showCliSessions?: boolean;
|
|
225
221
|
/** The initial-password notice banner (app layout) was permanently dismissed by the user. */
|
|
226
222
|
initialPasswordBannerDismissed?: boolean;
|
|
227
223
|
/**
|
|
@@ -565,6 +561,53 @@ interface DefaultModelUpdateRequest {
|
|
|
565
561
|
interface DefaultModelResponse {
|
|
566
562
|
defaultModel: ModelRefDto;
|
|
567
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* How the authorization code travels back. `callback` sends the browser to a harness URL
|
|
566
|
+
* the provider redirects to; `manual` asks the provider for a one-time code the user copies
|
|
567
|
+
* back by hand, for the case where that redirect cannot reach the harness.
|
|
568
|
+
*/
|
|
569
|
+
type ModelOAuthMode = "callback" | "manual";
|
|
570
|
+
/**
|
|
571
|
+
* POST /api/projects/:p/model-oauth/start (owner): opens a flow for one provider group.
|
|
572
|
+
* The group must declare an authorization flow in the built-in catalog — the endpoints it
|
|
573
|
+
* uses come from there, never from this request.
|
|
574
|
+
*/
|
|
575
|
+
interface ModelOAuthStartRequest {
|
|
576
|
+
/** Provider group id (must be a catalog group that publishes a key-minting flow). */
|
|
577
|
+
provider: string;
|
|
578
|
+
/** Defaults to `callback`. */
|
|
579
|
+
mode?: ModelOAuthMode;
|
|
580
|
+
}
|
|
581
|
+
/** The opaque flow handle plus the page to send the user to. No secret of the flow's is included. */
|
|
582
|
+
interface ModelOAuthStartResponse {
|
|
583
|
+
flowId: string;
|
|
584
|
+
authorizeUrl: string;
|
|
585
|
+
}
|
|
586
|
+
/** Why a flow failed, as a code the frontend phrases; never carries a code, a verifier or a key. */
|
|
587
|
+
type ModelOAuthErrorCode = "invalid_request" | "code_rejected" | "upstream_failed" | "unreachable" | "apply_failed";
|
|
588
|
+
/**
|
|
589
|
+
* GET /api/projects/:p/model-oauth/:flowId (owner): where a flow stands. Unknown, expired,
|
|
590
|
+
* and other users' flows are all 404 alike.
|
|
591
|
+
*/
|
|
592
|
+
interface ModelOAuthStatusResponse {
|
|
593
|
+
status: "pending" | "done" | "error";
|
|
594
|
+
/** The provider group the flow mints a key for. */
|
|
595
|
+
provider: string;
|
|
596
|
+
error?: ModelOAuthErrorCode;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* POST /api/projects/:p/model-oauth/:flowId/code (owner): redeems a code the user pasted,
|
|
600
|
+
* the manual counterpart of the redirect callback. A flow is single-use either way.
|
|
601
|
+
*/
|
|
602
|
+
interface ModelOAuthCodeRequest {
|
|
603
|
+
code: string;
|
|
604
|
+
}
|
|
605
|
+
/** Redemption outcome; `applied` counts the models the minted key was written to. */
|
|
606
|
+
interface ModelOAuthCodeResponse {
|
|
607
|
+
ok: boolean;
|
|
608
|
+
applied?: number;
|
|
609
|
+
error?: ModelOAuthErrorCode;
|
|
610
|
+
}
|
|
568
611
|
/**
|
|
569
612
|
* Per-Project new-chat defaults: prefill for the chat draft page. Every key is optional —
|
|
570
613
|
* an absent key means "not set" (the pre-existing behavior). Serves as the GET response,
|
|
@@ -673,6 +716,28 @@ interface AgentCreateRequest {
|
|
|
673
716
|
/** Display name; defaults to agentId. */
|
|
674
717
|
name?: string;
|
|
675
718
|
description?: string;
|
|
719
|
+
/**
|
|
720
|
+
* Library Skill names installed into the new Agent, seeding it at creation. Every name must
|
|
721
|
+
* exist in the library (404 `unknown_skill` otherwise, before anything is created); omitted or
|
|
722
|
+
* empty leaves the Agent with no Skills, which is what a plain Agent gets by default.
|
|
723
|
+
*/
|
|
724
|
+
skills?: string[];
|
|
725
|
+
/**
|
|
726
|
+
* Skills imported from a directory on disk instead of the library. `skillsDirectory` is the
|
|
727
|
+
* absolute path the user picked and `directorySkills` are the names to install from it, read
|
|
728
|
+
* back from `.agents/skills` / `.claude/skills` at creation time. Both are required together,
|
|
729
|
+
* and every name must still be there (404 `unknown_skill` otherwise, before anything is
|
|
730
|
+
* created) — the client sends names, never Skill content.
|
|
731
|
+
*/
|
|
732
|
+
skillsDirectory?: string;
|
|
733
|
+
directorySkills?: string[];
|
|
734
|
+
/**
|
|
735
|
+
* Base64 of an exported Agent State snapshot package (`.tar.gz`): the new Agent is
|
|
736
|
+
* initialized from the package instead of the default template. Mutually exclusive with
|
|
737
|
+
* skill seeding (`skills` / `skillsDirectory`) — the package carries its own skills.
|
|
738
|
+
* Explicit `name` / `description` override the package's values; absent ones keep them.
|
|
739
|
+
*/
|
|
740
|
+
dataBase64?: string;
|
|
676
741
|
}
|
|
677
742
|
interface AgentCreateResponse {
|
|
678
743
|
agent: AgentSummary;
|
|
@@ -765,13 +830,13 @@ interface AgentConfigResponse {
|
|
|
765
830
|
}
|
|
766
831
|
/**
|
|
767
832
|
* POST …/config/kernel-update result: the smart merge's outcome (core's applyKernelUpdate).
|
|
768
|
-
*
|
|
769
|
-
* in
|
|
833
|
+
* The entries are Agent settings **tabs** (`prompt`, `runtime`, `tools`, `skills`, `memory`,
|
|
834
|
+
* `vault`, `schedules`) in the settings page's tab order; the client maps them to tab labels.
|
|
770
835
|
*/
|
|
771
836
|
interface AgentKernelUpdateResponse {
|
|
772
|
-
/**
|
|
837
|
+
/** Tabs advanced to the current defaults (previously absent, or an untouched old default). */
|
|
773
838
|
advanced: string[];
|
|
774
|
-
/**
|
|
839
|
+
/** Tabs kept whole because they match no recorded default (customized, kept conservatively). */
|
|
775
840
|
kept: string[];
|
|
776
841
|
/** The kernel stamp written (the current defaults generation). */
|
|
777
842
|
kernelVersion: string;
|
|
@@ -979,6 +1044,8 @@ interface SessionInfo {
|
|
|
979
1044
|
* history itself when it needs it.
|
|
980
1045
|
*/
|
|
981
1046
|
tracePath?: string;
|
|
1047
|
+
/** Present when the Session has an ENABLED messaging binding: its channel (the sidebar row's per-channel indicator). */
|
|
1048
|
+
messagingChannel?: MessagingChannel;
|
|
982
1049
|
}
|
|
983
1050
|
/**
|
|
984
1051
|
* Session list category, the sidebar's four-way split applied server-side: archived wins
|
|
@@ -1014,6 +1081,17 @@ interface DirListResponse {
|
|
|
1014
1081
|
/** Subdirectory list (sorted by name, files excluded). */
|
|
1015
1082
|
entries: DirEntryInfo[];
|
|
1016
1083
|
}
|
|
1084
|
+
/** One Skill found in a picked directory: metadata plus which of the two layouts it came from. */
|
|
1085
|
+
interface DirectorySkillItem extends SkillMetadataItem {
|
|
1086
|
+
/** `.agents/skills` or `.claude/skills` — shown so the origin of an offered Skill is visible. */
|
|
1087
|
+
source: string;
|
|
1088
|
+
}
|
|
1089
|
+
interface DirectorySkillsResponse {
|
|
1090
|
+
/** Absolute path that was scanned (realpath). */
|
|
1091
|
+
path: string;
|
|
1092
|
+
/** Installable Skills found under the directory's Skill layouts; empty when it carries none. */
|
|
1093
|
+
skills: DirectorySkillItem[];
|
|
1094
|
+
}
|
|
1017
1095
|
interface SessionCreateRequest {
|
|
1018
1096
|
/** Upstream id of the session's model; always sent together with provider. Omit both for the Project's default Model. */
|
|
1019
1097
|
modelId?: string;
|
|
@@ -1027,6 +1105,12 @@ interface SessionCreateRequest {
|
|
|
1027
1105
|
workspace?: string;
|
|
1028
1106
|
/** Defaults to allow-all. */
|
|
1029
1107
|
approvalMode?: ApprovalMode;
|
|
1108
|
+
/**
|
|
1109
|
+
* Creating-client hint stored on the Session row: "cli" when the CLI creates the
|
|
1110
|
+
* Session through the API; defaults to "web". Informational provenance only — lists
|
|
1111
|
+
* serve every row regardless of client.
|
|
1112
|
+
*/
|
|
1113
|
+
client?: "web" | "cli";
|
|
1030
1114
|
}
|
|
1031
1115
|
interface SessionCreateResponse {
|
|
1032
1116
|
session: SessionInfo;
|
|
@@ -1284,6 +1368,30 @@ interface PendingFollowUpInfo {
|
|
|
1284
1368
|
/** Number of file attachments in the queued input. */
|
|
1285
1369
|
files: number;
|
|
1286
1370
|
}
|
|
1371
|
+
/**
|
|
1372
|
+
* One live subagent child of a session's runtime, carried on `task_state` events and the SSE
|
|
1373
|
+
* subscribe snapshot: the child Session id (the origin hop the stream already correlates by),
|
|
1374
|
+
* its background registry handle (null while it only lives inside a foreground collect
|
|
1375
|
+
* window), and whether a round is currently running. Only an ACTIVE parent runtime reports
|
|
1376
|
+
* children — after a server restart the in-process children are gone, and the empty list is
|
|
1377
|
+
* the truth.
|
|
1378
|
+
*/
|
|
1379
|
+
interface SubagentRuntimeInfo {
|
|
1380
|
+
sessionId: string;
|
|
1381
|
+
subagentId: string | null;
|
|
1382
|
+
running: boolean;
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Response of POST /api/sessions/:sessionId/subagents/:childSessionId/message — a user input
|
|
1386
|
+
* on the child, whatever its state: `steered` = queued as a mid-run interjection, `started` =
|
|
1387
|
+
* began a follow-up run on the idle child, `resumed` = the released child session was revived
|
|
1388
|
+
* (resume-session semantics) and the message began its next round. The failure shapes are
|
|
1389
|
+
* HTTP statuses instead: 404 when the child's session record does not exist or cannot be
|
|
1390
|
+
* revived, 409 when the child cannot take the message right now.
|
|
1391
|
+
*/
|
|
1392
|
+
interface SubagentMessageResponse {
|
|
1393
|
+
outcome: "steered" | "started" | "resumed";
|
|
1394
|
+
}
|
|
1287
1395
|
/**
|
|
1288
1396
|
* Response of the two recall endpoints — DELETE /api/sessions/:id/steer/:steerId and
|
|
1289
1397
|
* DELETE /api/sessions/:id/follow-ups/:followUpId: the withdrawn message's original content,
|
|
@@ -1336,6 +1444,159 @@ interface SessionProcessInfo {
|
|
|
1336
1444
|
interface SessionProcessesResponse {
|
|
1337
1445
|
processes: SessionProcessInfo[];
|
|
1338
1446
|
}
|
|
1447
|
+
/** Messaging channels a Session can bind to. */
|
|
1448
|
+
type MessagingChannel = "feishu" | "telegram";
|
|
1449
|
+
/** Event-connection runtime state of one binding (kept in memory, not persisted). */
|
|
1450
|
+
type MessagingRuntimeState = "disconnected" | "connecting" | "connected" | "error";
|
|
1451
|
+
interface MessagingRuntimeStatus {
|
|
1452
|
+
state: MessagingRuntimeState;
|
|
1453
|
+
/** Failure detail; present only in the `error` state. */
|
|
1454
|
+
lastError?: string;
|
|
1455
|
+
/** When the state last changed (ISO 8601); absent for a binding that never connected. */
|
|
1456
|
+
changedAt?: string;
|
|
1457
|
+
}
|
|
1458
|
+
/** The stored Feishu config, secret masked (plaintext never leaves the server). */
|
|
1459
|
+
interface FeishuBindingInfo {
|
|
1460
|
+
channel: "feishu";
|
|
1461
|
+
sessionId: string;
|
|
1462
|
+
appId: string;
|
|
1463
|
+
/**
|
|
1464
|
+
* Masked app secret (site-wide mask rule: `***`, or `first4…last4` for long values);
|
|
1465
|
+
* absent when no secret is stored (never entered, or cleared) — the binding cannot be
|
|
1466
|
+
* enabled until one is saved.
|
|
1467
|
+
*/
|
|
1468
|
+
appSecretMasked?: string;
|
|
1469
|
+
baseDomain: string;
|
|
1470
|
+
/** Connection INTENT (the state toggle's value); new bindings start disabled, and at most one of a Session's channels is enabled. */
|
|
1471
|
+
enabled: boolean;
|
|
1472
|
+
/**
|
|
1473
|
+
* Whether an inbound Feishu chat is known (the bot has been messaged at least once).
|
|
1474
|
+
* Replies and test messages target that chat; until it exists nothing can be sent.
|
|
1475
|
+
*/
|
|
1476
|
+
lastChatKnown: boolean;
|
|
1477
|
+
createdAt: string;
|
|
1478
|
+
updatedAt: string;
|
|
1479
|
+
}
|
|
1480
|
+
/** The stored Telegram config, token masked (plaintext never leaves the server). */
|
|
1481
|
+
interface TelegramBindingInfo {
|
|
1482
|
+
channel: "telegram";
|
|
1483
|
+
sessionId: string;
|
|
1484
|
+
/** The numeric bot id (the token's half before the colon) — the channel-scoped account identity, never secret. */
|
|
1485
|
+
botId: string;
|
|
1486
|
+
/**
|
|
1487
|
+
* Masked bot token (site-wide mask rule: `***`, or `first4…last4` for long values);
|
|
1488
|
+
* absent when no token is stored (cleared) — the binding cannot be enabled until one
|
|
1489
|
+
* is saved.
|
|
1490
|
+
*/
|
|
1491
|
+
botTokenMasked?: string;
|
|
1492
|
+
/** Connection INTENT (the state toggle's value); new bindings start disabled, and at most one of a Session's channels is enabled. */
|
|
1493
|
+
enabled: boolean;
|
|
1494
|
+
/**
|
|
1495
|
+
* Whether an inbound Telegram chat is known (the bot has been messaged at least once).
|
|
1496
|
+
* Replies and test messages target that chat; until it exists nothing can be sent.
|
|
1497
|
+
*/
|
|
1498
|
+
lastChatKnown: boolean;
|
|
1499
|
+
createdAt: string;
|
|
1500
|
+
updatedAt: string;
|
|
1501
|
+
}
|
|
1502
|
+
/** A Session's saved config for one messaging channel (`channel` is the discriminant). */
|
|
1503
|
+
type MessagingBindingInfo = FeishuBindingInfo | TelegramBindingInfo;
|
|
1504
|
+
/** One saved channel config with its event-connection runtime status. */
|
|
1505
|
+
interface MessagingChannelState {
|
|
1506
|
+
binding: MessagingBindingInfo;
|
|
1507
|
+
/** Only the enabled channel's connection is ever anything but disconnected. */
|
|
1508
|
+
status: MessagingRuntimeStatus;
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* GET …/messaging response — the channel-agnostic read the channel-aware binding editor
|
|
1512
|
+
* loads: EVERY saved channel config (masked) with its runtime status. A Session may keep
|
|
1513
|
+
* both channels saved; at most one of them is enabled.
|
|
1514
|
+
*/
|
|
1515
|
+
interface MessagingBindingsResponse {
|
|
1516
|
+
bindings: MessagingChannelState[];
|
|
1517
|
+
}
|
|
1518
|
+
/** GET / PUT …/messaging/feishu response: the Feishu config (null = not saved) plus its runtime status. */
|
|
1519
|
+
interface FeishuBindingResponse {
|
|
1520
|
+
binding: FeishuBindingInfo | null;
|
|
1521
|
+
status: MessagingRuntimeStatus;
|
|
1522
|
+
}
|
|
1523
|
+
/** GET / PUT …/messaging/telegram response (the Telegram narrowing of the same envelope). */
|
|
1524
|
+
interface TelegramBindingResponse {
|
|
1525
|
+
binding: TelegramBindingInfo | null;
|
|
1526
|
+
status: MessagingRuntimeStatus;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* PUT …/messaging/feishu — saves credentials/config ONLY, never flipping the connection
|
|
1530
|
+
* (exception: an enabled binding's connector restarts with the new credentials so stored
|
|
1531
|
+
* config and live connection never diverge). The connection toggle is POST …/state.
|
|
1532
|
+
*/
|
|
1533
|
+
interface FeishuBindingPutRequest {
|
|
1534
|
+
appId: string;
|
|
1535
|
+
/** Omitted or blank keeps the stored secret (the masked value never round-trips). */
|
|
1536
|
+
appSecret?: string;
|
|
1537
|
+
/** Defaults to https://open.feishu.cn when omitted or blank. */
|
|
1538
|
+
baseDomain?: string;
|
|
1539
|
+
/**
|
|
1540
|
+
* Drops the STORED secret (the models-page clear idiom; a typed `appSecret` wins over
|
|
1541
|
+
* it). Refused with 409 `messaging_disable_before_clear` while the binding is enabled.
|
|
1542
|
+
*/
|
|
1543
|
+
clearAppSecret?: boolean;
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* PUT …/messaging/telegram — saves the credential ONLY, same contract as the Feishu PUT
|
|
1547
|
+
* (an enabled binding's connector restarts with the new token; the connection toggle is
|
|
1548
|
+
* POST …/state). 409 `telegram_bot_in_use` when the token's bot id is bound elsewhere.
|
|
1549
|
+
*/
|
|
1550
|
+
interface TelegramBindingPutRequest {
|
|
1551
|
+
/** Omitted or blank keeps the stored token (the masked value never round-trips). */
|
|
1552
|
+
botToken?: string;
|
|
1553
|
+
/**
|
|
1554
|
+
* Drops the STORED token (the models-page clear idiom; a typed `botToken` wins over
|
|
1555
|
+
* it — and the row keeps its bot identity). Refused with 409
|
|
1556
|
+
* `messaging_disable_before_clear` while the binding is enabled.
|
|
1557
|
+
*/
|
|
1558
|
+
clearBotToken?: boolean;
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* POST …/messaging/<channel>/state — enable connects with the STORED credentials,
|
|
1562
|
+
* disable terminates. Enabling is mutually exclusive per Session: while another channel
|
|
1563
|
+
* is enabled it answers 409 `another_channel_enabled` (turn that one off first), and a
|
|
1564
|
+
* config whose secret is missing answers its channel's 400 `*_required`.
|
|
1565
|
+
*/
|
|
1566
|
+
interface MessagingBindingStateRequest {
|
|
1567
|
+
enabled: boolean;
|
|
1568
|
+
}
|
|
1569
|
+
/** POST …/messaging/feishu/test — draft values; each omitted field falls back to the stored binding. */
|
|
1570
|
+
interface FeishuTestRequest {
|
|
1571
|
+
appId?: string;
|
|
1572
|
+
appSecret?: string;
|
|
1573
|
+
baseDomain?: string;
|
|
1574
|
+
}
|
|
1575
|
+
/** Credential-test outcome (an unreachable/rejected credential is `ok:false`, not an HTTP error). */
|
|
1576
|
+
interface FeishuTestResponse {
|
|
1577
|
+
ok: boolean;
|
|
1578
|
+
latencyMs?: number;
|
|
1579
|
+
error?: string;
|
|
1580
|
+
}
|
|
1581
|
+
/** POST …/messaging/telegram/test — a draft token, falling back to the stored binding when omitted. */
|
|
1582
|
+
interface TelegramTestRequest {
|
|
1583
|
+
botToken?: string;
|
|
1584
|
+
}
|
|
1585
|
+
/** Telegram credential-test outcome: success additionally names the bot the token signs in as. */
|
|
1586
|
+
interface TelegramTestResponse {
|
|
1587
|
+
ok: boolean;
|
|
1588
|
+
latencyMs?: number;
|
|
1589
|
+
/** The bot's `@username`, when the API reports one. */
|
|
1590
|
+
botUsername?: string;
|
|
1591
|
+
error?: string;
|
|
1592
|
+
}
|
|
1593
|
+
/**
|
|
1594
|
+
* POST …/messaging/<channel>/test-message — sent to the last known chat (409
|
|
1595
|
+
* `feishu_no_chat` / `telegram_no_chat` before one exists).
|
|
1596
|
+
*/
|
|
1597
|
+
interface MessagingTestMessageResponse {
|
|
1598
|
+
ok: true;
|
|
1599
|
+
}
|
|
1339
1600
|
/** Docs: /docs/server-api § "Streaming (SSE)". */
|
|
1340
1601
|
type ServerEvent =
|
|
1341
1602
|
/**
|
|
@@ -1356,6 +1617,12 @@ type ServerEvent =
|
|
|
1356
1617
|
pendingSteering?: PendingSteeringInfo[];
|
|
1357
1618
|
/** Queued follow-up tasks awaiting auto-start (absent = none): per-entry content + recall handle, alongside the `queued` count. */
|
|
1358
1619
|
pendingFollowUps?: PendingFollowUpInfo[];
|
|
1620
|
+
/**
|
|
1621
|
+
* Live subagent children of this session's runtime (absent = none): the panel renders
|
|
1622
|
+
* child running marks from this structural liveness instead of parsing tool-output
|
|
1623
|
+
* text. Refreshed on every child run start/settle.
|
|
1624
|
+
*/
|
|
1625
|
+
subagents?: SubagentRuntimeInfo[];
|
|
1359
1626
|
}
|
|
1360
1627
|
/** The model-generated title after the first turn has been persisted (for in-place list updates). */
|
|
1361
1628
|
| {
|
|
@@ -1474,6 +1741,51 @@ interface TraceFileInfo {
|
|
|
1474
1741
|
interface SessionTracesResponse {
|
|
1475
1742
|
files: TraceFileInfo[];
|
|
1476
1743
|
}
|
|
1744
|
+
/** One tool's share of the context: its calls plus their results. Its *definition* is counted in `toolDefs`, not here. */
|
|
1745
|
+
interface ContextToolShare {
|
|
1746
|
+
name: string;
|
|
1747
|
+
tokens: number;
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* What the Session's current model context is made of — the part derived from its messages.
|
|
1751
|
+
*
|
|
1752
|
+
* Every token figure is an **estimate** from a character heuristic, not a tokenizer: the
|
|
1753
|
+
* authoritative occupancy is the last `token_usage`'s `request.total`, which says how large the
|
|
1754
|
+
* context is but not what fills it. Consumers should present these as shares of that measured
|
|
1755
|
+
* occupancy rather than as counts of their own.
|
|
1756
|
+
*
|
|
1757
|
+
* The six parts partition the context and sum to `total`; `topTools` is a ranking inside
|
|
1758
|
+
* `toolRequests + toolResults` and can sum to less than those two (a result whose call was not
|
|
1759
|
+
* recorded in the same Trace shard has no tool to be attributed to).
|
|
1760
|
+
*/
|
|
1761
|
+
interface SessionContextParts {
|
|
1762
|
+
systemPrompt: number;
|
|
1763
|
+
toolDefs: number;
|
|
1764
|
+
userMessages: number;
|
|
1765
|
+
assistantMessages: number;
|
|
1766
|
+
toolRequests: number;
|
|
1767
|
+
toolResults: number;
|
|
1768
|
+
/** Sum of the six parts. */
|
|
1769
|
+
total: number;
|
|
1770
|
+
/** Tools ranked by the context their traffic occupies, descending; at most five. */
|
|
1771
|
+
topTools: ContextToolShare[];
|
|
1772
|
+
/**
|
|
1773
|
+
* A completed compaction closed the context these figures describe, and the next one has not
|
|
1774
|
+
* been written yet: the composition is of what was compacted away, not of what the model now
|
|
1775
|
+
* carries. The same state in which the chat page's context ring shows `—`.
|
|
1776
|
+
*/
|
|
1777
|
+
contextClosed: boolean;
|
|
1778
|
+
}
|
|
1779
|
+
/** `GET /api/sessions/:id/context`: the message-derived composition plus where compaction will fire. */
|
|
1780
|
+
interface SessionContextResponse extends SessionContextParts {
|
|
1781
|
+
/**
|
|
1782
|
+
* Occupancy (tokens) at which this Session's next Request triggers context compaction: the
|
|
1783
|
+
* Agent's configured `compaction.max_context_length`, capped by what the model's context window
|
|
1784
|
+
* leaves room for. Null when compaction is disabled, when the Agent's config could not be read,
|
|
1785
|
+
* or when the derived threshold is not below the window — nothing to mark inside the gauge.
|
|
1786
|
+
*/
|
|
1787
|
+
compactionThreshold: number | null;
|
|
1788
|
+
}
|
|
1477
1789
|
interface TraceEventsResponse {
|
|
1478
1790
|
events: OmniMessage[];
|
|
1479
1791
|
offset: number;
|
|
@@ -2053,7 +2365,7 @@ interface SkillMetadataItem {
|
|
|
2053
2365
|
icon?: string;
|
|
2054
2366
|
/** Version number (natural number, frontmatter version; falls back to 1 if invalid). */
|
|
2055
2367
|
version: number;
|
|
2056
|
-
/** Update
|
|
2368
|
+
/** Update timestamp (frontmatter updated, ISO 8601 UTC by convention; defaults to an empty string). */
|
|
2057
2369
|
updated: string;
|
|
2058
2370
|
}
|
|
2059
2371
|
interface SkillGroupItem {
|
|
@@ -2087,17 +2399,15 @@ interface SkillArchiveInstallRequest {
|
|
|
2087
2399
|
/** Replace an already-installed Skill of the same name (deletes its directory first). */
|
|
2088
2400
|
overwrite?: boolean;
|
|
2089
2401
|
}
|
|
2090
|
-
/**
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
buildDate: string | null;
|
|
2100
|
-
}
|
|
2402
|
+
/**
|
|
2403
|
+
* GET /api/version: the running build's identity plus this root's pushed harness, verbatim
|
|
2404
|
+
* from `versionReport()` — the same record `penguin version --json` prints, so the two
|
|
2405
|
+
* cannot drift apart. Field meanings live on {@link VersionReport} and {@link HarnessInfo};
|
|
2406
|
+
* the ones the web reads are `version` and `buildDate` (the stamped release date behind the
|
|
2407
|
+
* sidebar's "last updated", needing no network, and null for a source build or a release
|
|
2408
|
+
* predating the stamping — v0.1.2 and earlier — where the UI shows the version alone).
|
|
2409
|
+
*/
|
|
2410
|
+
type VersionResponse = VersionReport;
|
|
2101
2411
|
/**
|
|
2102
2412
|
* GET /api/version/update-check: newest published release vs the running version.
|
|
2103
2413
|
* Always HTTP 200 (fail-soft): a lookup failure sets `error` and leaves `latestVersion`
|
|
@@ -2181,4 +2491,4 @@ interface UpdateRunResponse {
|
|
|
2181
2491
|
needsRestart: boolean;
|
|
2182
2492
|
}
|
|
2183
2493
|
|
|
2184
|
-
export type { AdminPasswordResetRequest, AdminUserCreateRequest, AdminUserCreateResponse, AdminUsersResponse, AgentCompactionConfigDto, AgentConfigDto, AgentConfigResponse, AgentConfigUpdateRequest, AgentCreateRequest, AgentCreateResponse, AgentImportRequest, AgentImportResponse, AgentKernelUpdateResponse, AgentMemoryConfigDto, AgentModelConfigDto, AgentSchedulesConfigDto, AgentSkillsConfigDto, AgentSkillsResponse, AgentSummary, AgentTraceDateGroup, AgentTraceFileRef, AgentTraceSessionEntry, AgentTraceSessionFile, AgentTraceSessionGroup, AgentTracesResponse, AgentVaultConfigDto, AgentsResponse, ApprovalDecisionRequest, ApprovalMode, AuthLoginRequest, AuthResponse, BenchmarkCaseScore, BenchmarkCaseSummary, BenchmarkCasesResponse, BenchmarkEvaluation, BenchmarkRunScore, BenchmarkSummary, BenchmarksResponse, CaseMaterial, ChatDefaultsDto, CommandPolicyDto, CommandPolicyRuleDto, CredentialInfo, DefaultModelResponse, DefaultModelUpdateRequest, DesktopUpdateStatus, DesktopUpdateStatusResponse, DesktopUpdaterCommandMessage, DesktopUpdaterStatusMessage, DirEntryInfo, DirListResponse, DraftShortcut, EndpointModelListRequest, EndpointModelListResponse, ErrorBody, FilesStatRequest, FilesStatResponse, GoalResponse, GoalServerEvent, GoalStateView, HistoryMessage, McpServerTestResponse, MeResponse, MemberAddRequest, MemberAddResponse, MemberInfo, MembersResponse, MemoryFileInfo, MemoryFileResponse, MemoryFilesResponse, MemoryImportMode, MemoryImportRequest, MemoryImportResponse, MemoryOverviewResponse, MemoryScopeExport, MemoryScopeInfo, MemoryTransferFile, MessagesLiveTail, MessagesPageInfo, MessagesResponse, ModelInfo, ModelPricingDto, ModelProtocolDetectRequest, ModelProtocolDetectResponse, ModelProtocolProbeDto, ModelRefDto, ModelTestRequest, ModelTestResponse, ModelUpdateEntry, ModelVisionDetectRequest, ModelVisionDetectResponse, ModelVisionOutcome, ModelsResponse, ModelsUpdateRequest, PasswordChangeRequest, PendingFollowUpInfo, PendingSteeringInfo, PrefsResponse, ProjectCreateRequest, ProjectCreateResponse, ProjectRole, ProjectSummary, ProjectUpdateRequest, ProjectUpdateResponse, ProjectsResponse, ProtocolProbeOutcome, RecalledMessageResponse, RequestSpan, RetryNowResponse, ScheduleItem, ScheduleServerEvent, ScheduleStatus, ScheduleUpsertRequest, SchedulesResponse, ServerEvent, ServerSettings, ServerSettingsResponse, ServerSettingsUpdateRequest, SessionCategory, SessionCategoryCounts, SessionCreateRequest, SessionCreateResponse, SessionForkRequest, SessionForkResponse, SessionInfo, SessionPatchRequest, SessionProcessInfo, SessionProcessesResponse, SessionResponse, SessionSource, SessionStatus, SessionTracesResponse, SessionsResponse, SkillArchiveInstallRequest, SkillGroupItem, SkillInstallRequest, SkillLibraryResponse, SkillMetadataItem, SteerRequest, TaskCreateRequest, TaskCreateResponse, TaskInputPart, ToolCallSpan, TraceAnalysisResponse, TraceEventsResponse, TraceFileInfo, TraceImportRequest, TraceImportResponse, TraceModelSegment, TraceOtherSpan, TracePosition, TraceTaskStats, TraceToolSpan, UiPrefs, UpdateCheckResponse, UpdateRunResponse, UploadLimits, UsageAgentSeries, UsageBucket, UsageEntitySeriesCounts, UsageErrorCount, UsageErrorItem, UsageErrors, UsageErrorsPage, UsageGranularity, UsageGroupBy, UsageGroupRow, UsageModelSeries, UsageResponse, UsageSeriesPoint, UsageTrendPointInTrace, UserInfo, VaultEntryInfo, VaultEntryUpdate, VaultResponse, VaultUpdateRequest, VersionResponse, WorkspaceFileEntry, WorkspaceFilesResponse };
|
|
2494
|
+
export type { AdminPasswordResetRequest, AdminUserCreateRequest, AdminUserCreateResponse, AdminUsersResponse, AgentCompactionConfigDto, AgentConfigDto, AgentConfigResponse, AgentConfigUpdateRequest, AgentCreateRequest, AgentCreateResponse, AgentImportRequest, AgentImportResponse, AgentKernelUpdateResponse, AgentMemoryConfigDto, AgentModelConfigDto, AgentSchedulesConfigDto, AgentSkillsConfigDto, AgentSkillsResponse, AgentSummary, AgentTraceDateGroup, AgentTraceFileRef, AgentTraceSessionEntry, AgentTraceSessionFile, AgentTraceSessionGroup, AgentTracesResponse, AgentVaultConfigDto, AgentsResponse, ApprovalDecisionRequest, ApprovalMode, AuthLoginRequest, AuthResponse, BenchmarkCaseScore, BenchmarkCaseSummary, BenchmarkCasesResponse, BenchmarkEvaluation, BenchmarkRunScore, BenchmarkSummary, BenchmarksResponse, CaseMaterial, ChatDefaultsDto, CommandPolicyDto, CommandPolicyRuleDto, ContextToolShare, CredentialInfo, DefaultModelResponse, DefaultModelUpdateRequest, DesktopUpdateStatus, DesktopUpdateStatusResponse, DesktopUpdaterCommandMessage, DesktopUpdaterStatusMessage, DirEntryInfo, DirListResponse, DirectorySkillItem, DirectorySkillsResponse, DraftShortcut, EndpointModelListRequest, EndpointModelListResponse, ErrorBody, FeishuBindingInfo, FeishuBindingPutRequest, FeishuBindingResponse, FeishuTestRequest, FeishuTestResponse, FilesStatRequest, FilesStatResponse, GoalResponse, GoalServerEvent, GoalStateView, HistoryMessage, McpServerTestResponse, MeResponse, MemberAddRequest, MemberAddResponse, MemberInfo, MembersResponse, MemoryFileInfo, MemoryFileResponse, MemoryFilesResponse, MemoryImportMode, MemoryImportRequest, MemoryImportResponse, MemoryOverviewResponse, MemoryScopeExport, MemoryScopeInfo, MemoryTransferFile, MessagesLiveTail, MessagesPageInfo, MessagesResponse, MessagingBindingInfo, MessagingBindingStateRequest, MessagingBindingsResponse, MessagingChannel, MessagingChannelState, MessagingRuntimeState, MessagingRuntimeStatus, MessagingTestMessageResponse, ModelInfo, ModelOAuthCodeRequest, ModelOAuthCodeResponse, ModelOAuthErrorCode, ModelOAuthMode, ModelOAuthStartRequest, ModelOAuthStartResponse, ModelOAuthStatusResponse, ModelPricingDto, ModelProtocolDetectRequest, ModelProtocolDetectResponse, ModelProtocolProbeDto, ModelRefDto, ModelTestRequest, ModelTestResponse, ModelUpdateEntry, ModelVisionDetectRequest, ModelVisionDetectResponse, ModelVisionOutcome, ModelsResponse, ModelsUpdateRequest, PasswordChangeRequest, PendingFollowUpInfo, PendingSteeringInfo, PrefsResponse, ProjectCreateRequest, ProjectCreateResponse, ProjectRole, ProjectSummary, ProjectUpdateRequest, ProjectUpdateResponse, ProjectsResponse, ProtocolProbeOutcome, RecalledMessageResponse, RequestSpan, RetryNowResponse, ScheduleItem, ScheduleServerEvent, ScheduleStatus, ScheduleUpsertRequest, SchedulesResponse, ServerEvent, ServerSettings, ServerSettingsResponse, ServerSettingsUpdateRequest, SessionCategory, SessionCategoryCounts, SessionContextParts, SessionContextResponse, SessionCreateRequest, SessionCreateResponse, SessionForkRequest, SessionForkResponse, SessionInfo, SessionPatchRequest, SessionProcessInfo, SessionProcessesResponse, SessionResponse, SessionSource, SessionStatus, SessionTracesResponse, SessionsResponse, SkillArchiveInstallRequest, SkillGroupItem, SkillInstallRequest, SkillLibraryResponse, SkillMetadataItem, SteerRequest, SubagentMessageResponse, SubagentRuntimeInfo, TaskCreateRequest, TaskCreateResponse, TaskInputPart, TelegramBindingInfo, TelegramBindingPutRequest, TelegramBindingResponse, TelegramTestRequest, TelegramTestResponse, ToolCallSpan, TraceAnalysisResponse, TraceEventsResponse, TraceFileInfo, TraceImportRequest, TraceImportResponse, TraceModelSegment, TraceOtherSpan, TracePosition, TraceTaskStats, TraceToolSpan, UiPrefs, UpdateCheckResponse, UpdateRunResponse, UploadLimits, UsageAgentSeries, UsageBucket, UsageEntitySeriesCounts, UsageErrorCount, UsageErrorItem, UsageErrors, UsageErrorsPage, UsageGranularity, UsageGroupBy, UsageGroupRow, UsageModelSeries, UsageResponse, UsageSeriesPoint, UsageTrendPointInTrace, UserInfo, VaultEntryInfo, VaultEntryUpdate, VaultResponse, VaultUpdateRequest, VersionResponse, WorkspaceFileEntry, WorkspaceFilesResponse };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/hmr/manifest.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import fsp from "fs/promises";
|
|
4
|
+
import path from "path";
|
|
5
|
+
function str(value) {
|
|
6
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
7
|
+
}
|
|
8
|
+
async function readHarnessInfo(root) {
|
|
9
|
+
const manifest = await readManifest(root);
|
|
10
|
+
if (manifest === null) return null;
|
|
11
|
+
const bundles = {
|
|
12
|
+
platform: str(manifest.platform?.bundle),
|
|
13
|
+
cli: str(manifest.cli?.bundle),
|
|
14
|
+
web: str(manifest.web?.manifest)
|
|
15
|
+
};
|
|
16
|
+
if (bundles.platform === null && bundles.cli === null && bundles.web === null) return null;
|
|
17
|
+
const repo = str(manifest.source?.repo);
|
|
18
|
+
const revision = str(manifest.source?.revision);
|
|
19
|
+
return {
|
|
20
|
+
// Both halves or neither: half a provenance names nothing.
|
|
21
|
+
source: repo !== null && revision !== null ? { repo, revision } : null,
|
|
22
|
+
pushedAt: str(manifest.pushedAt),
|
|
23
|
+
bundles
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function readManifest(root) {
|
|
27
|
+
try {
|
|
28
|
+
const raw = await fsp.readFile(path.join(root, "hmr", "harness.json"), "utf8");
|
|
29
|
+
return JSON.parse(raw);
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function resolveCliBundlePath(root) {
|
|
35
|
+
const manifest = await readManifest(root);
|
|
36
|
+
const bundle = manifest?.cli?.bundle;
|
|
37
|
+
if (typeof bundle !== "string" || bundle.length === 0) return null;
|
|
38
|
+
const hmrDir = path.join(root, "hmr");
|
|
39
|
+
const abs = path.resolve(hmrDir, bundle);
|
|
40
|
+
const withinHmrDir = abs === hmrDir || abs.startsWith(hmrDir + path.sep);
|
|
41
|
+
if (!withinHmrDir) return null;
|
|
42
|
+
return fs.existsSync(abs) ? abs : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
readHarnessInfo,
|
|
47
|
+
readManifest,
|
|
48
|
+
resolveCliBundlePath
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=chunk-4SXGU7RO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hmr/manifest.ts"],"sourcesContent":["/**\n * The harness.json shape (`<root>/hmr/harness.json`) and pure readers over it.\n *\n * Split out of host.ts so a reader never needs a running HmrHost: `penguin` is a\n * separate, short-lived process per invocation (see packages/cli/src/index.ts) — it\n * has no host instance to ask, only the data root's own harness.json on disk.\n * `resolveCliBundlePath` is that reader: a plain disk read, no HTTP, no boot, no\n * in-memory state.\n *\n * ONE atomic version, THREE independent artifacts: `platform`, `cli`, and `web` are\n * always written together by HmrHost's single merged upgrade path (see host.ts's\n * persistVersion), but each is content-addressed and stored on its own — a push to\n * POST /api/hmr/upgrade carries `platform` and `cli` as two separate single-file ESM\n * sources, so `cli.bundle` points at its OWN file, a different sha from\n * `platform.bundle`. They are read back together too (see host.ts's restore): a\n * partial record (e.g. `platform` present but `web` or `cli` missing) is never\n * trusted — the whole version is treated as absent instead of partially applied.\n */\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { HarnessInfo } from \"@prismshadow/penguin-core\";\n\n/**\n * The committed on-disk record: a runtime restart resumes exactly this CODE (platform,\n * cli, web). It carries no STATE — a restart always boots the resumed platform bundle\n * against its own fresh initial context (bundle.context, see host.ts's PlatformBundle),\n * never a document parked from a previous run (see host.ts's module doc for why: a\n * parked doc's live-resource handles die with the process anyway, so resuming one could\n * only ever produce handles that fail to reclaim). Paths are relative to hmrDir\n * (`<root>/hmr`).\n */\nexport interface Manifest {\n platform?: { bundle: string };\n /**\n * The CLI's own bundle pointer — its own independent file (a different sha from\n * `platform.bundle`; the two are separately compiled artifacts), kept as its own\n * field so a reader that only wants \"which bundle does the CLI load right now\"\n * never has to reach into `platform` at all.\n */\n cli?: { bundle: string };\n /** One gzip(JSON.stringify({ files })) artifact, restored straight into memory. */\n web?: { manifest: string };\n /**\n * Files the pushed platform needs as REAL files on disk, unpacked under this directory\n * (relative to hmrDir). The web artifact stays in memory because it is only ever served\n * as bytes; an asset is something the platform hands to the OS — a native `.node` the\n * loader must resolve by path, a helper binary it execs — so it has to exist on the\n * filesystem, with its exec bit intact.\n */\n assets?: { dir: string };\n /**\n * Provenance of the push that produced this version, exactly as the pushing client sent\n * it (scripts/deploy.mjs fills it from its own checkout). Recorded, never executed or\n * resolved. Absent for a version pushed without it, and for every version committed\n * before the field existed — which is why every reader treats it as optional.\n */\n source?: { repo: string; revision: string };\n /** When this version was committed to the store (ISO 8601). Absent on older records. */\n pushedAt?: string;\n}\n\n/** A string field of an untrusted manifest, or null unless it is a non-empty string. */\nfunction str(value: unknown): string | null {\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\n/**\n * What the store has committed, for version reporting: provenance, commit time, and the\n * three artifact pointers. Null when nothing has ever been pushed to this root.\n *\n * Read defensively rather than cast: harness.json is written only by persistVersion, but a\n * truncated or hand-edited file must degrade a `penguin version` to missing fields, never\n * crash it. A record missing every artifact pointer counts as nothing pushed.\n */\nexport async function readHarnessInfo(root: string): Promise<HarnessInfo | null> {\n const manifest = await readManifest(root);\n if (manifest === null) return null;\n\n const bundles = {\n platform: str(manifest.platform?.bundle),\n cli: str(manifest.cli?.bundle),\n web: str(manifest.web?.manifest),\n };\n if (bundles.platform === null && bundles.cli === null && bundles.web === null) return null;\n\n const repo = str(manifest.source?.repo);\n const revision = str(manifest.source?.revision);\n return {\n // Both halves or neither: half a provenance names nothing.\n source: repo !== null && revision !== null ? { repo, revision } : null,\n pushedAt: str(manifest.pushedAt),\n bundles,\n };\n}\n\n/** Reads and parses `<root>/hmr/harness.json`; null when missing or corrupt (nothing committed yet). */\nexport async function readManifest(root: string): Promise<Manifest | null> {\n try {\n const raw = await fsp.readFile(path.join(root, \"hmr\", \"harness.json\"), \"utf8\");\n return JSON.parse(raw) as Manifest;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolves the current CLI bundle's absolute path from a data root, or null when\n * nothing has been pushed yet: a fresh root, a missing/corrupt manifest, no `cli`\n * entry, a `cli.bundle` that isn't a non-empty string (e.g. `{\"cli\":{}}` — a\n * hand-edited or truncated harness.json), a path that resolves outside `<root>/hmr/`\n * (defense in depth: `manifest.cli.bundle` is trusted content today, written only by\n * this same process's persistVersion, but a reader must never let a malformed or\n * malicious manifest value walk it out of the store directory via `..` segments), or\n * a referenced file that no longer exists (e.g. pruned from the store — see host.ts's\n * pruneStore). A null return means \"use the packaged default\" to every caller;\n * nothing here ever throws for an ordinary unconfigured or malformed root.\n */\nexport async function resolveCliBundlePath(root: string): Promise<string | null> {\n const manifest = await readManifest(root);\n const bundle = manifest?.cli?.bundle;\n if (typeof bundle !== \"string\" || bundle.length === 0) return null;\n const hmrDir = path.join(root, \"hmr\");\n const abs = path.resolve(hmrDir, bundle);\n const withinHmrDir = abs === hmrDir || abs.startsWith(hmrDir + path.sep);\n if (!withinHmrDir) return null;\n return fs.existsSync(abs) ? abs : null;\n}\n"],"mappings":";AAkBA,OAAO,QAAQ;AACf,OAAO,SAAS;AAChB,OAAO,UAAU;AA2CjB,SAAS,IAAI,OAA+B;AAC1C,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAUA,eAAsB,gBAAgB,MAA2C;AAC/E,QAAM,WAAW,MAAM,aAAa,IAAI;AACxC,MAAI,aAAa,KAAM,QAAO;AAE9B,QAAM,UAAU;AAAA,IACd,UAAU,IAAI,SAAS,UAAU,MAAM;AAAA,IACvC,KAAK,IAAI,SAAS,KAAK,MAAM;AAAA,IAC7B,KAAK,IAAI,SAAS,KAAK,QAAQ;AAAA,EACjC;AACA,MAAI,QAAQ,aAAa,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,KAAM,QAAO;AAEtF,QAAM,OAAO,IAAI,SAAS,QAAQ,IAAI;AACtC,QAAM,WAAW,IAAI,SAAS,QAAQ,QAAQ;AAC9C,SAAO;AAAA;AAAA,IAEL,QAAQ,SAAS,QAAQ,aAAa,OAAO,EAAE,MAAM,SAAS,IAAI;AAAA,IAClE,UAAU,IAAI,SAAS,QAAQ;AAAA,IAC/B;AAAA,EACF;AACF;AAGA,eAAsB,aAAa,MAAwC;AACzE,MAAI;AACF,UAAM,MAAM,MAAM,IAAI,SAAS,KAAK,KAAK,MAAM,OAAO,cAAc,GAAG,MAAM;AAC7E,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAcA,eAAsB,qBAAqB,MAAsC;AAC/E,QAAM,WAAW,MAAM,aAAa,IAAI;AACxC,QAAM,SAAS,UAAU,KAAK;AAC9B,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,EAAG,QAAO;AAC9D,QAAM,SAAS,KAAK,KAAK,MAAM,KAAK;AACpC,QAAM,MAAM,KAAK,QAAQ,QAAQ,MAAM;AACvC,QAAM,eAAe,QAAQ,UAAU,IAAI,WAAW,SAAS,KAAK,GAAG;AACvE,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,GAAG,WAAW,GAAG,IAAI,MAAM;AACpC;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readHarnessInfo
|
|
3
|
+
} from "./chunk-4SXGU7RO.js";
|
|
4
|
+
|
|
5
|
+
// src/version-report.ts
|
|
6
|
+
import { buildInfo } from "@prismshadow/penguin-core";
|
|
7
|
+
async function versionReport(root) {
|
|
8
|
+
return { ...buildInfo(), harness: await readHarnessInfo(root) };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
versionReport
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=chunk-TTGGJDNA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/version-report.ts"],"sourcesContent":["/**\n * The one assembler behind `penguin version --json` and GET /api/version.\n *\n * The report has two halves that no single layer can produce. Core knows the artifact this\n * process is executing and nothing about data roots; the HMR store knows what harness was\n * pushed to a root and nothing about the process reading it. Joining them here — rather\n * than in each caller — is what makes \"the CLI and the API report the same thing\" a\n * structural fact instead of two spreads that have to be kept in step.\n *\n * It lives in the server package because that is where the harness.json format is owned\n * (see ./hmr/manifest.ts), and is exported as its own entry point so the CLI can reach it\n * without pulling in a server: the module graph here is core plus a `node:fs` read.\n */\nimport { buildInfo } from \"@prismshadow/penguin-core\";\nimport type { VersionReport } from \"@prismshadow/penguin-core\";\nimport { readHarnessInfo } from \"./hmr/manifest.js\";\n\n/**\n * The running build's identity plus the harness committed to `root`'s HMR store.\n *\n * Never throws: an unreadable or malformed store reports `harness: null`, which is also\n * what a root with nothing ever pushed to it reports. Version reporting degrades to fewer\n * facts, never to a failure.\n */\nexport async function versionReport(root: string): Promise<VersionReport> {\n return { ...buildInfo(), harness: await readHarnessInfo(root) };\n}\n"],"mappings":";;;;;AAaA,SAAS,iBAAiB;AAW1B,eAAsB,cAAc,MAAsC;AACxE,SAAO,EAAE,GAAG,UAAU,GAAG,SAAS,MAAM,gBAAgB,IAAI,EAAE;AAChE;","names":[]}
|