@prismshadow/penguin-server 0.2.1 → 0.2.2
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 +233 -6
- package/dist/{chunk-V6KH4FRG.js → chunk-OMBFPEGQ.js} +1 -1
- package/dist/chunk-OMBFPEGQ.js.map +1 -0
- package/dist/chunk-UUJCF46V.js +50 -0
- package/dist/chunk-UUJCF46V.js.map +1 -0
- package/dist/index.js +1081 -207
- package/dist/index.js.map +1 -1
- package/dist/initial-password.d.ts +17 -0
- package/dist/initial-password.js +15 -0
- package/dist/initial-password.js.map +1 -0
- package/dist/lock.js +1 -1
- package/package.json +8 -3
- package/web-dist/assets/{index-DTZ3P5wj.js → index-B3puu-Hr.js} +1 -1
- package/web-dist/assets/index-CaDuyrN0.css +1 -0
- package/web-dist/assets/index-rgHg3m6g.js +181 -0
- package/web-dist/index.html +2 -2
- package/dist/chunk-V6KH4FRG.js.map +0 -1
- package/web-dist/assets/index-BvejhGl5.css +0 -1
- package/web-dist/assets/index-yzk1uoHO.js +0 -172
package/dist/api/types.d.ts
CHANGED
|
@@ -49,8 +49,9 @@ interface AuthResponse {
|
|
|
49
49
|
interface MeResponse {
|
|
50
50
|
user: UserInfo;
|
|
51
51
|
/**
|
|
52
|
-
* Whether Workspace HTML previews open on a separate origin (
|
|
53
|
-
*
|
|
52
|
+
* Whether Workspace HTML previews open on a separate origin (the loopback
|
|
53
|
+
* counterpart of the App host, or PENGUIN_PREVIEW_ORIGIN when set). False means this
|
|
54
|
+
* deployment has no usable preview origin —
|
|
54
55
|
* the App is reached on something other than a loopback name and
|
|
55
56
|
* PENGUIN_PREVIEW_ORIGIN is unset — so previews fall back to the same-origin sandbox,
|
|
56
57
|
* where `localStorage`, cookies and third-party embeds do not work. Computed per
|
|
@@ -61,7 +62,7 @@ interface MeResponse {
|
|
|
61
62
|
* Whether this server runs in desktop mode (spawned by the desktop shell with
|
|
62
63
|
* PENGUIN_DESKTOP_TOKEN). The web app then hides the logout entry, the
|
|
63
64
|
* initial-password banner and the self-update entry, and omits the old-password
|
|
64
|
-
* field when changing the password.
|
|
65
|
+
* field when changing the password.
|
|
65
66
|
*/
|
|
66
67
|
desktopMode: boolean;
|
|
67
68
|
/**
|
|
@@ -94,6 +95,51 @@ interface AdminPasswordResetRequest {
|
|
|
94
95
|
/** New initial password (at least 8 characters); resets invalidate all of the user's sessions. */
|
|
95
96
|
password: string;
|
|
96
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Admin-level server-global settings (SQLite server_settings):
|
|
100
|
+
* two independent proxy switches sharing one optional explicit address. In every
|
|
101
|
+
* on-state the effective NO_PROXY always includes localhost/127.0.0.1/::1 (loopback is
|
|
102
|
+
* never proxied), and changes apply to newly initiated connections/spawns immediately —
|
|
103
|
+
* no restart.
|
|
104
|
+
*/
|
|
105
|
+
interface ServerSettings {
|
|
106
|
+
/**
|
|
107
|
+
* "Application uses the proxy" (default on): the server's own outbound traffic (LLM
|
|
108
|
+
* requests, the update check, image fetches). On with `proxyUrl` set = that address
|
|
109
|
+
* for both http and https; on without an address = the proxy environment variables
|
|
110
|
+
* HTTP_PROXY / HTTPS_PROXY (both spellings); off = always direct.
|
|
111
|
+
*/
|
|
112
|
+
proxyForApp: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* "Agent environment uses the proxy" (default on): agent command subprocess
|
|
115
|
+
* environments. On with `proxyUrl` set = HTTP_PROXY / HTTPS_PROXY (plus lowercase
|
|
116
|
+
* twins) injected as that address with the merged NO_PROXY, overriding inherited
|
|
117
|
+
* values; on without an address = the host environment passes through unchanged;
|
|
118
|
+
* off = the proxy variables are stripped (NO_PROXY kept).
|
|
119
|
+
*/
|
|
120
|
+
proxyForAgent: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* The shared explicit proxy address (canonical `http(s)://host[:port]`), or null =
|
|
123
|
+
* follow the proxy environment variables. When set it takes precedence over
|
|
124
|
+
* HTTP_PROXY / HTTPS_PROXY wherever the owning switch is on.
|
|
125
|
+
*/
|
|
126
|
+
proxyUrl: string | null;
|
|
127
|
+
}
|
|
128
|
+
interface ServerSettingsResponse {
|
|
129
|
+
settings: ServerSettings;
|
|
130
|
+
}
|
|
131
|
+
/** PUT body: every field optional, omitted fields keep their current value (mirrors prefs). */
|
|
132
|
+
interface ServerSettingsUpdateRequest {
|
|
133
|
+
proxyForApp?: boolean;
|
|
134
|
+
proxyForAgent?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* New proxy address. Accepted forms: `http://host[:port]`, `https://host[:port]`, or
|
|
137
|
+
* bare `host[:port]` (normalized to `http://…` — only normalized values are stored,
|
|
138
|
+
* and the response echoes the stored form). Empty/whitespace-only or null clears the
|
|
139
|
+
* address (follow the environment variables); anything else is 400 `invalid_proxy_url`.
|
|
140
|
+
*/
|
|
141
|
+
proxyUrl?: string | null;
|
|
142
|
+
}
|
|
97
143
|
/** User UI preferences (SQLite ui_prefs, free-form JSON; known keys declared here). */
|
|
98
144
|
interface UiPrefs {
|
|
99
145
|
theme?: "light" | "dark";
|
|
@@ -106,6 +152,8 @@ interface UiPrefs {
|
|
|
106
152
|
* scanning (#139).
|
|
107
153
|
*/
|
|
108
154
|
showCliSessions?: boolean;
|
|
155
|
+
/** The initial-password notice banner (app layout) was permanently dismissed by the user. */
|
|
156
|
+
initialPasswordBannerDismissed?: boolean;
|
|
109
157
|
[key: string]: unknown;
|
|
110
158
|
}
|
|
111
159
|
interface PrefsResponse {
|
|
@@ -330,7 +378,7 @@ interface DefaultModelResponse {
|
|
|
330
378
|
interface ChatDefaultsDto {
|
|
331
379
|
/** Preselected Agent; must reference an existing Agent of the Project (400 unknown_agent). */
|
|
332
380
|
agentId?: string;
|
|
333
|
-
/** Prefilled Workspace directory; absent/empty =
|
|
381
|
+
/** Prefilled Workspace directory; absent/empty = a temporary workspace. */
|
|
334
382
|
workspace?: string;
|
|
335
383
|
/** Prefilled approval mode; absent = the built-in "allow-all". */
|
|
336
384
|
approvalMode?: ApprovalMode;
|
|
@@ -377,12 +425,16 @@ interface AgentSummary {
|
|
|
377
425
|
toolCount: number;
|
|
378
426
|
/** Agent State version number (the `version` in system_config.yaml; treated as 1 if missing). */
|
|
379
427
|
version: number;
|
|
428
|
+
/** Whether the config's kernel stamp is behind the current defaults generation (a missing stamp counts as outdated) — drives the list card's update hint. */
|
|
429
|
+
kernelOutdated: boolean;
|
|
380
430
|
/** Vault key count (number of keys in agent_state/.vault.toml). */
|
|
381
431
|
vaultKeyCount: number;
|
|
382
432
|
/** Schedule count (number of .toml files under agent_state/schedule/, including invalid ones). */
|
|
383
433
|
scheduleCount: number;
|
|
384
434
|
/** Installed Skill count (number of agent_state/skills/<name>/ directories with a SKILL.md). */
|
|
385
435
|
skillCount: number;
|
|
436
|
+
/** Memory count (topic files summed over the scope directories under agent_state/memory/, independent of the memory switch). */
|
|
437
|
+
memoryCount: number;
|
|
386
438
|
}
|
|
387
439
|
interface AgentsResponse {
|
|
388
440
|
agents: AgentSummary[];
|
|
@@ -408,16 +460,69 @@ interface AgentCompactionConfigDto {
|
|
|
408
460
|
mode?: "summarize" | "discard";
|
|
409
461
|
prompt?: string;
|
|
410
462
|
}
|
|
463
|
+
/** Memory config. All fields report effective values (a config with no `memory` section reads as enabled with the built-in prompts, matching core); the prompts are edited on the Memory tab. */
|
|
464
|
+
interface AgentMemoryConfigDto {
|
|
465
|
+
enabled: boolean;
|
|
466
|
+
/** The always-injected half of the `{{MEMORY}}` block (carries `{{USER_MEMORY_INDEX}}`; the User directory is literal text). */
|
|
467
|
+
prompt: string;
|
|
468
|
+
/** Appended only in a persistent Workspace (carries `{{WORKSPACE_MEMORY_INDEX}}` and the rendered `{{WORKSPACE_MEMORY_DIR}}` directory). */
|
|
469
|
+
workspacePrompt: string;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Vault prompt-injection config, edited on the Vault tab. `enabled` / `prompt` report
|
|
473
|
+
* effective values (a config with no `vault` section reads as enabled with the built-in
|
|
474
|
+
* prompt, matching core); the last two are read-only facts computed from the stored template.
|
|
475
|
+
*/
|
|
476
|
+
interface AgentVaultConfigDto {
|
|
477
|
+
/** Whether the Vault section enters the model context (values are injected into subprocesses regardless). */
|
|
478
|
+
enabled: boolean;
|
|
479
|
+
/** The `{{VAULT}}` block (carries `{{VAULT_KEYS}}`). */
|
|
480
|
+
prompt: string;
|
|
481
|
+
/** Whether the stored template carries `{{VAULT}}`; POST …/vault/template-placeholder inserts (or migrates to) it explicitly. */
|
|
482
|
+
templateHasPlaceholder: boolean;
|
|
483
|
+
/** Whether the stored template still carries the legacy hardcoded # Vault section verbatim (a pre-`{{VAULT}}` Agent) — the migration case of the insert endpoint. */
|
|
484
|
+
legacySectionPresent: boolean;
|
|
485
|
+
}
|
|
486
|
+
/** Skills prompt-injection config, edited on the Skills tab; same field semantics as AgentVaultConfigDto, for `{{SKILLS}}` / `{{SKILL_METADATA}}` and the legacy # Skills section. */
|
|
487
|
+
interface AgentSkillsConfigDto {
|
|
488
|
+
/** Whether the Skills section enters the model context (installed skills remain explicitly invocable regardless). */
|
|
489
|
+
enabled: boolean;
|
|
490
|
+
/** The `{{SKILLS}}` block (carries `{{SKILL_METADATA}}`). */
|
|
491
|
+
prompt: string;
|
|
492
|
+
/** Whether the stored template carries `{{SKILLS}}`; POST …/skills/template-placeholder inserts (or migrates to) it explicitly. */
|
|
493
|
+
templateHasPlaceholder: boolean;
|
|
494
|
+
/** Whether the stored template still carries the legacy hardcoded # Skills section verbatim — the migration case of the insert endpoint. */
|
|
495
|
+
legacySectionPresent: boolean;
|
|
496
|
+
}
|
|
497
|
+
/** Schedules prompt-injection config, edited on the Schedules tab. No legacy field: Schedules never had a hardcoded template section. */
|
|
498
|
+
interface AgentSchedulesConfigDto {
|
|
499
|
+
/** Whether the Scheduled Tasks section enters the model context (the server fires configured tasks regardless). */
|
|
500
|
+
enabled: boolean;
|
|
501
|
+
/** The `{{SCHEDULES}}` block (carries `{{SCHEDULE_LIST}}`). */
|
|
502
|
+
prompt: string;
|
|
503
|
+
/** Whether the stored template carries `{{SCHEDULES}}`; POST …/schedules/template-placeholder inserts it explicitly. */
|
|
504
|
+
templateHasPlaceholder: boolean;
|
|
505
|
+
}
|
|
411
506
|
/** Structured view of system_config.yaml (for the edit form). */
|
|
412
507
|
interface AgentConfigDto {
|
|
413
508
|
name?: string;
|
|
414
509
|
description?: string;
|
|
415
510
|
/** Agent State version number (treated as 1 if missing; shown in the settings page overview). */
|
|
416
511
|
version: number;
|
|
512
|
+
/** The stored kernel stamp (`kernel_version`): which defaults generation the config is based on; null when the config predates the kernel-version mechanism. */
|
|
513
|
+
kernelVersion: string | null;
|
|
514
|
+
/** The current defaults generation (core's KERNEL_VERSION) — what a kernel update would stamp. */
|
|
515
|
+
kernelLatest: string;
|
|
516
|
+
/** Whether the stamp is behind kernelLatest (a missing stamp counts as outdated). */
|
|
517
|
+
kernelOutdated: boolean;
|
|
417
518
|
systemPrompt: string;
|
|
418
519
|
maxTurns?: number;
|
|
419
520
|
model?: AgentModelConfigDto;
|
|
420
521
|
compaction?: AgentCompactionConfigDto;
|
|
522
|
+
memory: AgentMemoryConfigDto;
|
|
523
|
+
vault: AgentVaultConfigDto;
|
|
524
|
+
skills: AgentSkillsConfigDto;
|
|
525
|
+
schedules: AgentSchedulesConfigDto;
|
|
421
526
|
toolsBuiltin: ToolDefinitionConfig[];
|
|
422
527
|
mcpServers: MCPServerConfig[];
|
|
423
528
|
}
|
|
@@ -430,6 +535,29 @@ interface AgentConfigResponse {
|
|
|
430
535
|
stateDir: string;
|
|
431
536
|
activeSessionCount: number;
|
|
432
537
|
}
|
|
538
|
+
/**
|
|
539
|
+
* POST …/config/kernel-update result: the smart merge's outcome (core's applyKernelUpdate).
|
|
540
|
+
* Paths are dotted config leaves (`system_prompt`, `memory.prompt`, `tools.builtin.<name>`…)
|
|
541
|
+
* in defaults-traversal order; the client maps them to display names.
|
|
542
|
+
*/
|
|
543
|
+
interface AgentKernelUpdateResponse {
|
|
544
|
+
/** Leaves advanced to the new default (previously missing, or an untouched old default). */
|
|
545
|
+
advanced: string[];
|
|
546
|
+
/** Leaves kept because the stored value matches no recorded defaults generation (user customizations, kept conservatively). */
|
|
547
|
+
kept: string[];
|
|
548
|
+
/** The kernel stamp written (the current defaults generation). */
|
|
549
|
+
kernelVersion: string;
|
|
550
|
+
}
|
|
551
|
+
/** POST …/config/mcp-test result: reachability of one MCP Server entry. */
|
|
552
|
+
interface McpServerTestResponse {
|
|
553
|
+
ok: boolean;
|
|
554
|
+
/** Discovered tool names (`mcp__<server>__<tool>`), present on success. */
|
|
555
|
+
tools?: string[];
|
|
556
|
+
/** Failure detail (connect error, timeout, server stderr tail), present on failure. */
|
|
557
|
+
error?: string;
|
|
558
|
+
/** Connect + discovery wall time (both outcomes) — the models test reports latency, this matches. */
|
|
559
|
+
latencyMs?: number;
|
|
560
|
+
}
|
|
433
561
|
/** PUT any subset: only provided keys are updated (remaining YAML content and comments preserved); agentsMd overwrites the whole file. */
|
|
434
562
|
interface AgentConfigUpdateRequest {
|
|
435
563
|
agentsMd?: string;
|
|
@@ -440,10 +568,73 @@ interface AgentConfigUpdateRequest {
|
|
|
440
568
|
maxTurns?: number;
|
|
441
569
|
model?: AgentModelConfigDto;
|
|
442
570
|
compaction?: AgentCompactionConfigDto;
|
|
571
|
+
memory?: Partial<AgentMemoryConfigDto>;
|
|
572
|
+
/** Only the writable half of the DTO — the template facts (templateHasPlaceholder / legacySectionPresent) are computed, never written. */
|
|
573
|
+
vault?: {
|
|
574
|
+
enabled?: boolean;
|
|
575
|
+
prompt?: string;
|
|
576
|
+
};
|
|
577
|
+
skills?: {
|
|
578
|
+
enabled?: boolean;
|
|
579
|
+
prompt?: string;
|
|
580
|
+
};
|
|
581
|
+
schedules?: {
|
|
582
|
+
enabled?: boolean;
|
|
583
|
+
prompt?: string;
|
|
584
|
+
};
|
|
443
585
|
toolsBuiltin?: ToolDefinitionConfig[];
|
|
444
586
|
mcpServers?: MCPServerConfig[];
|
|
445
587
|
};
|
|
446
588
|
}
|
|
589
|
+
/** One Memory scope directory: `agent_state/memory/user/` or `agent_state/memory/<workspaceKey>/`. */
|
|
590
|
+
interface MemoryScopeInfo {
|
|
591
|
+
/** Directory name under `memory/`: `user`, or a Workspace's `<safe-basename>-<hash>` key. */
|
|
592
|
+
scopeKey: string;
|
|
593
|
+
/** `user` — the scope every Session reads, temporary Workspaces included; `workspace` — one Workspace's scope. */
|
|
594
|
+
kind: "user" | "workspace";
|
|
595
|
+
/** Workspace path the key was derived from, read from the directory's `.workspace` marker; unset on the user scope (it stands for no path) and for a directory edited by hand. */
|
|
596
|
+
workspacePath?: string;
|
|
597
|
+
/** Number of Markdown topic files in the directory (the `MEMORY.md` index not counted). */
|
|
598
|
+
fileCount: number;
|
|
599
|
+
/** Most recent topic-file mtime in the directory (ISO 8601); unset when the directory holds no topic file. */
|
|
600
|
+
updatedAt?: string;
|
|
601
|
+
}
|
|
602
|
+
/** One Memory topic file, as listed (frontmatter only — the body is fetched per file). */
|
|
603
|
+
interface MemoryFileInfo {
|
|
604
|
+
/** File name inside the scope directory, e.g. `prefers-pnpm.md`. */
|
|
605
|
+
name: string;
|
|
606
|
+
/** Frontmatter `name`; falls back to the file name. */
|
|
607
|
+
title: string;
|
|
608
|
+
/** Frontmatter `description`; empty when the file declares none. */
|
|
609
|
+
description: string;
|
|
610
|
+
/** Frontmatter `updated_at`, verbatim. */
|
|
611
|
+
updatedAt?: string;
|
|
612
|
+
/** File size in bytes. */
|
|
613
|
+
size: number;
|
|
614
|
+
/** File mtime (ISO 8601). */
|
|
615
|
+
modifiedAt: string;
|
|
616
|
+
}
|
|
617
|
+
/** GET …/memory — the tab's landing payload: the switch and every scope group, user scope first. */
|
|
618
|
+
interface MemoryOverviewResponse {
|
|
619
|
+
/** Whether Memory reaches the model context (the Agent-level switch). */
|
|
620
|
+
enabled: boolean;
|
|
621
|
+
/** Whether the prompt template carries the `{{MEMORY}}` placeholder. An Agent created before Memory has none and injects nothing; POST …/memory/template-placeholder inserts it explicitly. */
|
|
622
|
+
templateHasMemory: boolean;
|
|
623
|
+
/** Absolute path of `agent_state/memory/`. */
|
|
624
|
+
memoryDir: string;
|
|
625
|
+
scopes: MemoryScopeInfo[];
|
|
626
|
+
}
|
|
627
|
+
/** GET …/memory/scopes/:key/files */
|
|
628
|
+
interface MemoryFilesResponse {
|
|
629
|
+
scopeKey: string;
|
|
630
|
+
files: MemoryFileInfo[];
|
|
631
|
+
}
|
|
632
|
+
/** GET …/memory/scopes/:key/files/:name */
|
|
633
|
+
interface MemoryFileResponse {
|
|
634
|
+
scopeKey: string;
|
|
635
|
+
file: MemoryFileInfo;
|
|
636
|
+
content: string;
|
|
637
|
+
}
|
|
447
638
|
interface SessionInfo {
|
|
448
639
|
sessionId: string;
|
|
449
640
|
projectId: string;
|
|
@@ -581,7 +772,7 @@ interface MessagesPageInfo {
|
|
|
581
772
|
before?: string;
|
|
582
773
|
/**
|
|
583
774
|
* Outline turns (the Web conversation outline's entry rule) opened BEFORE this
|
|
584
|
-
* window: the client offsets its global
|
|
775
|
+
* window: the client offsets its global "round N" numbering by this, so a partial
|
|
585
776
|
* window never mis-numbers. 0 when the window starts at the beginning.
|
|
586
777
|
*/
|
|
587
778
|
earlierTurns: number;
|
|
@@ -740,6 +931,23 @@ interface ApprovalDecisionRequest {
|
|
|
740
931
|
interface RetryNowResponse {
|
|
741
932
|
skipped: boolean;
|
|
742
933
|
}
|
|
934
|
+
/**
|
|
935
|
+
* One background command process started by the Session (an exec_command promoted past
|
|
936
|
+
* its yield window). Served from the ACTIVE runtime only: a session whose runtime entry
|
|
937
|
+
* is gone truthfully reports an empty list.
|
|
938
|
+
*/
|
|
939
|
+
interface SessionProcessInfo {
|
|
940
|
+
processId: string;
|
|
941
|
+
/** OS pid of the process-group leader; null when the spawn itself failed. */
|
|
942
|
+
pid: number | null;
|
|
943
|
+
cmd: string;
|
|
944
|
+
cwd: string;
|
|
945
|
+
startedAt: string;
|
|
946
|
+
running: boolean;
|
|
947
|
+
}
|
|
948
|
+
interface SessionProcessesResponse {
|
|
949
|
+
processes: SessionProcessInfo[];
|
|
950
|
+
}
|
|
743
951
|
/** Docs: /docs/server-api § "Streaming (SSE)". */
|
|
744
952
|
type ServerEvent =
|
|
745
953
|
/**
|
|
@@ -1007,6 +1215,23 @@ interface TraceToolSpan {
|
|
|
1007
1215
|
/** The Task that initiated this tool (grouped with its tool_call segment): async output belongs to this Task even if it arrives after request_end. */
|
|
1008
1216
|
taskIndex: number;
|
|
1009
1217
|
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Non-tool auxiliary phase on the timeline: rendered in its own lane under the "other"
|
|
1220
|
+
* legend category — deliberately not a tool span, since nothing was called. Currently the
|
|
1221
|
+
* first run's MCP connect + discovery (from the mcp_connect_begin/end event pair).
|
|
1222
|
+
*/
|
|
1223
|
+
interface TraceOtherSpan {
|
|
1224
|
+
/** Unique bar key (e.g. `mcp-connect-<beginTs>`). */
|
|
1225
|
+
key: string;
|
|
1226
|
+
/** Lane label, e.g. "mcp connect". */
|
|
1227
|
+
name: string;
|
|
1228
|
+
startTs: string;
|
|
1229
|
+
endTs: string;
|
|
1230
|
+
/** Attached to the Task that follows the phase (same grouping convention as toolSpans). */
|
|
1231
|
+
taskIndex: number;
|
|
1232
|
+
/** True when the phase ended with failures (some servers unreachable) or was aborted. */
|
|
1233
|
+
failed?: boolean;
|
|
1234
|
+
}
|
|
1010
1235
|
interface UsageTrendPointInTrace {
|
|
1011
1236
|
ts: string;
|
|
1012
1237
|
requestTotal: number;
|
|
@@ -1028,6 +1253,8 @@ interface TraceAnalysisResponse {
|
|
|
1028
1253
|
modelSegments: TraceModelSegment[];
|
|
1029
1254
|
/** Execution timeline: each tool's approval/execution phases (independent lane, can overlap with model decoding). */
|
|
1030
1255
|
toolSpans: TraceToolSpan[];
|
|
1256
|
+
/** Execution timeline: non-tool auxiliary phases (their own "other" lanes — currently the first run's MCP connect + discovery). */
|
|
1257
|
+
otherSpans: TraceOtherSpan[];
|
|
1031
1258
|
/** Number of request_end events with status ∈ {timeout, malformed}. */
|
|
1032
1259
|
reconnectCount: number;
|
|
1033
1260
|
/** Number of compaction_begin events. */
|
|
@@ -1451,4 +1678,4 @@ interface UpdateRunResponse {
|
|
|
1451
1678
|
needsRestart: boolean;
|
|
1452
1679
|
}
|
|
1453
1680
|
|
|
1454
|
-
export type { AdminPasswordResetRequest, AdminUserCreateRequest, AdminUserCreateResponse, AdminUsersResponse, AgentCompactionConfigDto, AgentConfigDto, AgentConfigResponse, AgentConfigUpdateRequest, AgentCreateRequest, AgentCreateResponse, AgentImportRequest, AgentImportResponse, AgentModelConfigDto, AgentSkillsResponse, AgentSummary, AgentTraceDateGroup, AgentTraceFileRef, AgentTraceSessionEntry, AgentTraceSessionFile, AgentTraceSessionGroup, AgentTracesResponse, AgentsResponse, ApprovalDecisionRequest, ApprovalMode, AuthLoginRequest, AuthResponse, BenchmarkCaseScore, BenchmarkCaseSummary, BenchmarkCasesResponse, BenchmarkEvaluation, BenchmarkRunScore, BenchmarkSummary, BenchmarksResponse, CaseMaterial, ChatDefaultsDto, CredentialInfo, DefaultModelResponse, DefaultModelUpdateRequest, DirEntryInfo, DirListResponse, ErrorBody, FilesStatRequest, FilesStatResponse, GoalResponse, GoalServerEvent, GoalStateView, MeResponse, MemberAddRequest, MemberAddResponse, MemberInfo, MembersResponse, MessagesLiveTail, MessagesPageInfo, MessagesResponse, ModelInfo, ModelPricingDto, ModelRefDto, ModelTestRequest, ModelTestResponse, ModelUpdateEntry, ModelsResponse, ModelsUpdateRequest, PasswordChangeRequest, PendingSteeringInfo, PrefsResponse, ProjectCreateRequest, ProjectCreateResponse, ProjectRole, ProjectSummary, ProjectUpdateRequest, ProjectUpdateResponse, ProjectsResponse, RequestSpan, RetryNowResponse, ScheduleItem, ScheduleServerEvent, ScheduleStatus, ScheduleUpsertRequest, SchedulesResponse, ServerEvent, SessionCategory, SessionCategoryCounts, SessionCreateRequest, SessionCreateResponse, SessionInfo, SessionPatchRequest, SessionResponse, SessionSource, SessionStatus, SessionTracesResponse, SessionsResponse, SkillArchiveInstallRequest, SkillGroupItem, SkillInstallRequest, SkillLibraryResponse, SkillMetadataItem, SteerRequest, TaskCreateRequest, TaskCreateResponse, TaskInputPart, ToolCallSpan, TraceAnalysisResponse, TraceEventsResponse, TraceFileInfo, TraceImportRequest, TraceImportResponse, TraceModelSegment, TraceTaskStats, TraceToolSpan, UiPrefs, UpdateCheckResponse, UpdateRunResponse, UsageAgentCount, UsageBucket, UsageErrorCount, UsageErrorItem, UsageErrors, UsageErrorsPage, UsageGroupBy, UsageGroupRow, UsageResponse, UsageSuccessRate, UsageTrendPoint, UsageTrendPointInTrace, UserInfo, VaultEntryInfo, VaultEntryUpdate, VaultResponse, VaultUpdateRequest, VersionResponse, WorkspaceFileEntry, WorkspaceFilesResponse };
|
|
1681
|
+
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, CredentialInfo, DefaultModelResponse, DefaultModelUpdateRequest, DirEntryInfo, DirListResponse, ErrorBody, FilesStatRequest, FilesStatResponse, GoalResponse, GoalServerEvent, GoalStateView, McpServerTestResponse, MeResponse, MemberAddRequest, MemberAddResponse, MemberInfo, MembersResponse, MemoryFileInfo, MemoryFileResponse, MemoryFilesResponse, MemoryOverviewResponse, MemoryScopeInfo, MessagesLiveTail, MessagesPageInfo, MessagesResponse, ModelInfo, ModelPricingDto, ModelRefDto, ModelTestRequest, ModelTestResponse, ModelUpdateEntry, ModelsResponse, ModelsUpdateRequest, PasswordChangeRequest, PendingSteeringInfo, PrefsResponse, ProjectCreateRequest, ProjectCreateResponse, ProjectRole, ProjectSummary, ProjectUpdateRequest, ProjectUpdateResponse, ProjectsResponse, RequestSpan, RetryNowResponse, ScheduleItem, ScheduleServerEvent, ScheduleStatus, ScheduleUpsertRequest, SchedulesResponse, ServerEvent, ServerSettings, ServerSettingsResponse, ServerSettingsUpdateRequest, SessionCategory, SessionCategoryCounts, SessionCreateRequest, SessionCreateResponse, 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, TraceTaskStats, TraceToolSpan, UiPrefs, UpdateCheckResponse, UpdateRunResponse, UsageAgentCount, UsageBucket, UsageErrorCount, UsageErrorItem, UsageErrors, UsageErrorsPage, UsageGroupBy, UsageGroupRow, UsageResponse, UsageSuccessRate, UsageTrendPoint, UsageTrendPointInTrace, UserInfo, VaultEntryInfo, VaultEntryUpdate, VaultResponse, VaultUpdateRequest, VersionResponse, WorkspaceFileEntry, WorkspaceFilesResponse };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lock.ts"],"sourcesContent":["/**\n * Root-level server instance lock (`<root>/server.lock`).\n *\n * web.db is single-process / single-writer (see db/database.ts), and two servers on one\n * data root would also double-run the schedule scheduler — so a data root admits one\n * server at a time. The lock records {pid, port, startedAt}; liveness requires BOTH the\n * pid to be alive AND the recorded port to accept a TCP connection, because either signal\n * alone false-positives (pids get recycled, ports get taken by unrelated processes). A\n * lock that fails the liveness check is stale and is simply overwritten by the next\n * server.\n *\n * Published as `@prismshadow/penguin-server/lock` (side-effect-free) so the CLI and the\n * desktop shell can pre-check a root without importing the package entry, which starts\n * listening.\n */\nimport fs from \"node:fs\";\nimport net from \"node:net\";\nimport path from \"node:path\";\n\nexport interface ServerLock {\n pid: number;\n port: number;\n startedAt: string;\n}\n\n/** TCP probe budget: loopback either connects immediately or the port is dead. */\nconst PROBE_TIMEOUT_MS = 500;\n\nexport function serverLockPath(root: string): string {\n return path.join(root, \"server.lock\");\n}\n\n/** Reads the lock file; a missing or malformed file reads as \"no lock\". */\nexport function readServerLock(root: string): ServerLock | null {\n let raw: string;\n try {\n raw = fs.readFileSync(serverLockPath(root), \"utf8\");\n } catch {\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as Partial<ServerLock>;\n if (\n typeof parsed.pid !== \"number\" ||\n !Number.isInteger(parsed.pid) ||\n typeof parsed.port !== \"number\" ||\n !Number.isInteger(parsed.port)\n ) {\n return null;\n }\n return { pid: parsed.pid, port: parsed.port, startedAt: String(parsed.startedAt ?? \"\") };\n } catch {\n return null;\n }\n}\n\nfunction pidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // EPERM = the process exists but belongs to another user — still alive.\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nfunction portAccepts(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n // 127.0.0.1 rather than localhost: this is a raw TCP liveness probe, not an App\n // request, and the server binds 127.0.0.1 (plus ::1) on loopback setups.\n const socket = net.connect({ host: \"127.0.0.1\", port, timeout: PROBE_TIMEOUT_MS });\n const done = (ok: boolean) => {\n socket.destroy();\n resolve(ok);\n };\n socket.once(\"connect\", () => done(true));\n socket.once(\"timeout\", () => done(false));\n socket.once(\"error\", () => done(false));\n });\n}\n\n/** True when the lock's process is alive AND its port accepts connections. */\nexport async function isServerLockAlive(lock: ServerLock): Promise<boolean> {\n return pidAlive(lock.pid) && (await portAccepts(lock.port));\n}\n\n/** Convenience for pre-checks: the live lock on this root, or null (absent or stale). */\nexport async function liveServerLock(root: string): Promise<ServerLock | null> {\n const lock = readServerLock(root);\n if (!lock) return null;\n return (await isServerLockAlive(lock)) ? lock : null;\n}\n\n/** Writes the lock atomically (tmp + rename; the parent directory must already exist). */\nexport function acquireServerLock(root: string, lock: ServerLock): void {\n const target = serverLockPath(root);\n const tmp = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(tmp, JSON.stringify(lock) + \"\\n\");\n fs.renameSync(tmp, target);\n}\n\n/** Removes the lock if it is still ours (best-effort; never throws on shutdown paths). */\nexport function releaseServerLock(root: string): void {\n try {\n const lock = readServerLock(root);\n if (lock && lock.pid === process.pid) fs.rmSync(serverLockPath(root));\n } catch {\n // Best-effort: a stale leftover is overwritten by the next server anyway.\n }\n}\n"],"mappings":";AAeA,OAAO,QAAQ;AACf,OAAO,SAAS;AAChB,OAAO,UAAU;AASjB,IAAM,mBAAmB;AAElB,SAAS,eAAe,MAAsB;AACnD,SAAO,KAAK,KAAK,MAAM,aAAa;AACtC;AAGO,SAAS,eAAe,MAAiC;AAC9D,MAAI;AACJ,MAAI;AACF,UAAM,GAAG,aAAa,eAAe,IAAI,GAAG,MAAM;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QACE,OAAO,OAAO,QAAQ,YACtB,CAAC,OAAO,UAAU,OAAO,GAAG,KAC5B,OAAO,OAAO,SAAS,YACvB,CAAC,OAAO,UAAU,OAAO,IAAI,GAC7B;AACA,aAAO;AAAA,IACT;AACA,WAAO,EAAE,KAAK,OAAO,KAAK,MAAM,OAAO,MAAM,WAAW,OAAO,OAAO,aAAa,EAAE,EAAE;AAAA,EACzF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAS,KAAsB;AACtC,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,KAAK;AAEZ,WAAQ,IAA8B,SAAS;AAAA,EACjD;AACF;AAEA,SAAS,YAAY,MAAgC;AACnD,SAAO,IAAI,QAAQ,CAAC,YAAY;AAG9B,UAAM,SAAS,IAAI,QAAQ,EAAE,MAAM,aAAa,MAAM,SAAS,iBAAiB,CAAC;AACjF,UAAM,OAAO,CAAC,OAAgB;AAC5B,aAAO,QAAQ;AACf,cAAQ,EAAE;AAAA,IACZ;AACA,WAAO,KAAK,WAAW,MAAM,KAAK,IAAI,CAAC;AACvC,WAAO,KAAK,WAAW,MAAM,KAAK,KAAK,CAAC;AACxC,WAAO,KAAK,SAAS,MAAM,KAAK,KAAK,CAAC;AAAA,EACxC,CAAC;AACH;AAGA,eAAsB,kBAAkB,MAAoC;AAC1E,SAAO,SAAS,KAAK,GAAG,KAAM,MAAM,YAAY,KAAK,IAAI;AAC3D;AAGA,eAAsB,eAAe,MAA0C;AAC7E,QAAM,OAAO,eAAe,IAAI;AAChC,MAAI,CAAC,KAAM,QAAO;AAClB,SAAQ,MAAM,kBAAkB,IAAI,IAAK,OAAO;AAClD;AAGO,SAAS,kBAAkB,MAAc,MAAwB;AACtE,QAAM,SAAS,eAAe,IAAI;AAClC,QAAM,MAAM,GAAG,MAAM,IAAI,QAAQ,GAAG;AACpC,KAAG,cAAc,KAAK,KAAK,UAAU,IAAI,IAAI,IAAI;AACjD,KAAG,WAAW,KAAK,MAAM;AAC3B;AAGO,SAAS,kBAAkB,MAAoB;AACpD,MAAI;AACF,UAAM,OAAO,eAAe,IAAI;AAChC,QAAI,QAAQ,KAAK,QAAQ,QAAQ,IAAK,IAAG,OAAO,eAAe,IAAI,CAAC;AAAA,EACtE,QAAQ;AAAA,EAER;AACF;","names":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/initial-password.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
function initialAdminPasswordPath(root) {
|
|
5
|
+
return path.join(root, "initial-admin-password");
|
|
6
|
+
}
|
|
7
|
+
function storeInitialAdminPassword(root, password) {
|
|
8
|
+
try {
|
|
9
|
+
fs.writeFileSync(initialAdminPasswordPath(root), `${password}
|
|
10
|
+
`, { mode: 384 });
|
|
11
|
+
} catch {
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function readInitialAdminPassword(root) {
|
|
15
|
+
try {
|
|
16
|
+
const value = fs.readFileSync(initialAdminPasswordPath(root), "utf8").trim();
|
|
17
|
+
return value === "" ? null : value;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function clearInitialAdminPassword(root) {
|
|
23
|
+
try {
|
|
24
|
+
fs.rmSync(initialAdminPasswordPath(root), { force: true });
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
var NOTICE_PADDING = 3;
|
|
29
|
+
function renderInitialPasswordNotice(userId, password) {
|
|
30
|
+
const lines = [
|
|
31
|
+
`Web sign-in: ${userId} / ${password}`,
|
|
32
|
+
"",
|
|
33
|
+
"This is the initial password and it has not been changed yet.",
|
|
34
|
+
"Please sign in and change it soon (user menu -> Change password)."
|
|
35
|
+
];
|
|
36
|
+
const width = Math.max(...lines.map((line) => line.length));
|
|
37
|
+
const bar = `+${"-".repeat(width + NOTICE_PADDING * 2)}+`;
|
|
38
|
+
const pad = " ".repeat(NOTICE_PADDING);
|
|
39
|
+
const body = lines.map((line) => `|${pad}${line.padEnd(width)}${pad}|`);
|
|
40
|
+
return [bar, ...body, bar].join("\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
initialAdminPasswordPath,
|
|
45
|
+
storeInitialAdminPassword,
|
|
46
|
+
readInitialAdminPassword,
|
|
47
|
+
clearInitialAdminPassword,
|
|
48
|
+
renderInitialPasswordNotice
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=chunk-UUJCF46V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/initial-password.ts"],"sourcesContent":["/**\n * Initial-admin-password persistence (`<root>/initial-admin-password`) and the startup\n * reminder notice.\n *\n * The seeded admin password used to be printed exactly once, at first seed; if that line\n * scrolled away unread, the credential was unrecoverable. The plaintext is now kept in\n * the data root FOR AS LONG AS the admin password remains the initial one, so every\n * server start — and `penguin web` attaching to an already-running instance — can\n * re-print it inside a hard-to-miss ASCII frame together with the change-it reminder.\n *\n * The file is removed the moment the admin's password is updated by any path (self\n * change, desktop set, or an admin reset picking a new value — the reset re-arms the\n * initial FLAG, but the file's content would be stale, and an admin-chosen password is\n * never persisted). Only the auto-generated / PENGUIN_SEED_ADMIN_PASSWORD seed value is\n * ever stored, with owner-only permissions; the SQLite db next to it already holds\n * strictly more sensitive data. Legacy roots seeded before this file existed simply have\n * no file: the reminder then stays silent (the plaintext is genuinely unrecoverable).\n *\n * Published as `@prismshadow/penguin-server/initial-password` (side-effect-free, like\n * ./lock) so the CLI can print the same notice when it attaches to a live instance.\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport function initialAdminPasswordPath(root: string): string {\n return path.join(root, \"initial-admin-password\");\n}\n\n/** Persists the seeded plaintext (owner-only file). Best-effort: the seed itself must not fail on an exotic filesystem — the reminder just degrades to first-start-only. */\nexport function storeInitialAdminPassword(root: string, password: string): void {\n try {\n fs.writeFileSync(initialAdminPasswordPath(root), `${password}\\n`, { mode: 0o600 });\n } catch {\n // e.g. a read-only root: the password was still printed once by the seed path.\n }\n}\n\n/** The stored plaintext, or null when absent/unreadable/empty. */\nexport function readInitialAdminPassword(root: string): string | null {\n try {\n const value = fs.readFileSync(initialAdminPasswordPath(root), \"utf8\").trim();\n return value === \"\" ? null : value;\n } catch {\n return null;\n }\n}\n\n/** Removes the stored plaintext (idempotent, best-effort). */\nexport function clearInitialAdminPassword(root: string): void {\n try {\n fs.rmSync(initialAdminPasswordPath(root), { force: true });\n } catch {\n // Best-effort: a leftover file only re-prints a password that no longer works.\n }\n}\n\n/** Interior padding (spaces) between the frame bars and the text on each side. */\nconst NOTICE_PADDING = 3;\n\n/**\n * The framed reminder block, ready for console.log. Plain ASCII (`+`/`-`/`|`) rather\n * than box-drawing characters: the server may run under terminals and log collectors\n * with non-UTF-8 code pages (legacy Windows consoles above all), and a misrendered frame\n * would bury exactly the line it exists to highlight. Width follows the longest line, so\n * a long pinned password widens the frame instead of breaking it.\n */\nexport function renderInitialPasswordNotice(userId: string, password: string): string {\n const lines = [\n `Web sign-in: ${userId} / ${password}`,\n \"\",\n \"This is the initial password and it has not been changed yet.\",\n \"Please sign in and change it soon (user menu -> Change password).\",\n ];\n const width = Math.max(...lines.map((line) => line.length));\n const bar = `+${\"-\".repeat(width + NOTICE_PADDING * 2)}+`;\n const pad = \" \".repeat(NOTICE_PADDING);\n const body = lines.map((line) => `|${pad}${line.padEnd(width)}${pad}|`);\n return [bar, ...body, bar].join(\"\\n\");\n}\n"],"mappings":";AAqBA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,SAAS,yBAAyB,MAAsB;AAC7D,SAAO,KAAK,KAAK,MAAM,wBAAwB;AACjD;AAGO,SAAS,0BAA0B,MAAc,UAAwB;AAC9E,MAAI;AACF,OAAG,cAAc,yBAAyB,IAAI,GAAG,GAAG,QAAQ;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAAA,EACnF,QAAQ;AAAA,EAER;AACF;AAGO,SAAS,yBAAyB,MAA6B;AACpE,MAAI;AACF,UAAM,QAAQ,GAAG,aAAa,yBAAyB,IAAI,GAAG,MAAM,EAAE,KAAK;AAC3E,WAAO,UAAU,KAAK,OAAO;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,0BAA0B,MAAoB;AAC5D,MAAI;AACF,OAAG,OAAO,yBAAyB,IAAI,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,EAC3D,QAAQ;AAAA,EAER;AACF;AAGA,IAAM,iBAAiB;AAShB,SAAS,4BAA4B,QAAgB,UAA0B;AACpF,QAAM,QAAQ;AAAA,IACZ,iBAAiB,MAAM,MAAM,QAAQ;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,KAAK,MAAM,CAAC;AAC1D,QAAM,MAAM,IAAI,IAAI,OAAO,QAAQ,iBAAiB,CAAC,CAAC;AACtD,QAAM,MAAM,IAAI,OAAO,cAAc;AACrC,QAAM,OAAO,MAAM,IAAI,CAAC,SAAS,IAAI,GAAG,GAAG,KAAK,OAAO,KAAK,CAAC,GAAG,GAAG,GAAG;AACtE,SAAO,CAAC,KAAK,GAAG,MAAM,GAAG,EAAE,KAAK,IAAI;AACtC;","names":[]}
|