@pasko70/pibo 1.0.4 → 1.1.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 +5 -1
- package/dist/apps/chat/agent-profiles.js +30 -4
- package/dist/apps/chat/agent-store.js +46 -3
- package/dist/apps/chat/model-catalog.js +42 -0
- package/dist/apps/chat/web-app.js +232 -7
- package/dist/apps/chat-ui/assets/{dist-DHE-yBLH.js → dist-8e4L8ciw.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BvO6SpaM.js → dist-B7bbI3Qz.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-_DPv0-QF.js → dist-CMZOa5ik.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-_oM7g0eh.js → dist-CNLWH2qh.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CG_gRSwv.js → dist-CUqpST5w.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-laadbMWg.js → dist-CqqUC8Ce.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-L1B-KWZO.js → dist-CspjOeBj.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-nFui3f-r.js → dist-D2N4-LIc.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BPv5MdlL.js → dist-D9L4MSRF.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D4HhiFWB.js → dist-DNcnQRPF.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-B5rcjVCj.js → dist-v4pjHlB7.js} +1 -1
- package/dist/apps/chat-ui/assets/index-D2Qxambd.js +151 -0
- package/dist/apps/chat-ui/assets/{index-Dk-opWl3.css → index-DXx368XK.css} +1 -1
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/core/model-defaults.js +53 -0
- package/dist/core/profiles.js +16 -0
- package/dist/core/runtime.js +8 -6
- package/dist/core/session-router.js +2 -0
- package/dist/gateway/server.js +2 -0
- package/dist/plugins/builtin.js +2 -1
- package/dist/plugins/registry.js +20 -1
- package/dist/user-skills/installer.js +211 -0
- package/dist/user-skills/manager.js +35 -0
- package/dist/user-skills/store.js +228 -0
- package/dist/user-skills/types.js +1 -0
- package/package.json +3 -3
- package/dist/apps/chat-ui/assets/index-BdcqTugD.js +0 -151
- /package/{.codex/skills → skills/builtin}/pi-agent-harness/SKILL.md +0 -0
- /package/{.codex/skills → skills/builtin}/pi-agent-harness/agents/openai.yaml +0 -0
package/README.md
CHANGED
|
@@ -111,6 +111,10 @@ Custom agents can be archived before deletion. Archived custom agents are remove
|
|
|
111
111
|
|
|
112
112
|
The designer configures native Pibo agent capabilities only: plugin-registered tools, skills, context files, subagents, automatic local context-file loading, built-in Pi tool visibility, and capability packages such as `pibo-run-control`. Curated external CLI tools from `pibo tools` remain global operator tooling and are not selected per agent.
|
|
113
113
|
|
|
114
|
+
The Chat Web App also supports User Skills. Operators can create local skills or import them from `skills.sh` or GitHub, and Pibo stores them under `.pibo/user-skills` plus `.pibo/user-skills.json`. Enabled user skills are registered live so they are selectable in the Agent Designer and available to routed sessions without a manual restart.
|
|
115
|
+
|
|
116
|
+
User Skill names are guarded at two layers: the web app no longer re-registers an already-synced skill on subsequent requests, and create/rename/enable/import flows reject names that would collide with an already registered skill. Imports also refuse to silently overwrite an existing skill name.
|
|
117
|
+
|
|
114
118
|
The Chat Web App now also has a dedicated Context area at `/apps/chat/context`. It reuses the managed context-file APIs inside the main Chat UI shell so operators can create, edit, relocate, and remove managed context files without leaving the authenticated Chat Web App. Plugin context files are shown there as read-only sources, and both the Context area and Agent Designer can create linked managed copies when a user wants to customize shipped content safely.
|
|
115
119
|
|
|
116
120
|
The Context area also exposes the Pibo Base Prompt. The library prompt lives at `context/pibo-system-prompt.md`; the Chat Web App can switch between that read-only library prompt and a persisted custom prompt stored under `.pibo/base-prompt.md`. Runtime prompt templates replace `{{availableTools}}` and `{{guidelines}}` from the active Pi/Pibo tool surface before project context and skills are appended.
|
|
@@ -119,7 +123,7 @@ The Context area also exposes the Pibo Compaction Prompt. The library prompt liv
|
|
|
119
123
|
|
|
120
124
|
## Profiles
|
|
121
125
|
|
|
122
|
-
The default profile is registered by the core plugin. It loads the
|
|
126
|
+
The default profile is registered by the core plugin. It loads the built-in `pi-agent-harness` skill from `skills/builtin/pi-agent-harness/SKILL.md` and uses Pi Coding Agent's built-in tools for normal coding work.
|
|
123
127
|
|
|
124
128
|
The `codex` alias resolves to the `codex-compat-openai-web` profile. It keeps the Pibo runtime boundary while exposing Codex-like coding affordances: Pi/Pibo `read`, `edit`, and `write`; Pibo run-control `bash`; native `web_search`; `apply_patch` and `view_image`; generated `pibo_subagent_default`, `pibo_subagent_explorer`, and `pibo_subagent_worker`; and the `pibo_run_*` lifecycle tools. `web_search` is a normal Pibo native tool with a provider adapter; the default adapter injects OpenAI Responses hosted `web_search` into the model request instead of using a local DuckDuckGo function tool. It intentionally does not expose Pi's separate `grep`, `find`, or `ls` tools by default; codebase search remains Codex-style through `bash` and commands such as `rg`.
|
|
125
129
|
|
|
@@ -12,10 +12,30 @@ export function createCustomAgentProfileDefinition(agent) {
|
|
|
12
12
|
.withMcpServers(agent.mcpServers)
|
|
13
13
|
.withPiPackages(agent.piPackages.map((id) => ({ id })))
|
|
14
14
|
.withToolPackages({ runControl: agent.runControl });
|
|
15
|
-
|
|
16
|
-
builder.
|
|
17
|
-
|
|
18
|
-
builder.
|
|
15
|
+
if (agent.mainModel)
|
|
16
|
+
builder.withMainModel(agent.mainModel);
|
|
17
|
+
if (agent.subagentModel)
|
|
18
|
+
builder.withSubagentModel(agent.subagentModel);
|
|
19
|
+
for (const skillName of agent.skills) {
|
|
20
|
+
try {
|
|
21
|
+
builder.addSkill(context.getSkill(skillName));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (!isUnknownSkillError(error, skillName))
|
|
25
|
+
throw error;
|
|
26
|
+
console.warn(`Skipping unknown skill "${skillName}" for custom agent "${agent.profileName}"`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const contextFileKey of agent.contextFiles) {
|
|
30
|
+
try {
|
|
31
|
+
builder.addContextFile(context.getContextFile(contextFileKey));
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (!isUnknownContextFileError(error, contextFileKey))
|
|
35
|
+
throw error;
|
|
36
|
+
console.warn(`Skipping unknown context file "${contextFileKey}" for custom agent "${agent.profileName}"`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
19
39
|
for (const toolName of agent.nativeTools)
|
|
20
40
|
builder.addTool(context.getTool(toolName));
|
|
21
41
|
for (const subagent of agent.subagents)
|
|
@@ -24,3 +44,9 @@ export function createCustomAgentProfileDefinition(agent) {
|
|
|
24
44
|
},
|
|
25
45
|
};
|
|
26
46
|
}
|
|
47
|
+
function isUnknownContextFileError(error, contextFileKey) {
|
|
48
|
+
return error instanceof Error && error.message === `Unknown context file "${contextFileKey}"`;
|
|
49
|
+
}
|
|
50
|
+
function isUnknownSkillError(error, skillName) {
|
|
51
|
+
return error instanceof Error && error.message === `Unknown skill "${skillName}"`;
|
|
52
|
+
}
|
|
@@ -28,6 +28,8 @@ export class CustomAgentStore {
|
|
|
28
28
|
subagents_json TEXT NOT NULL,
|
|
29
29
|
mcp_servers_json TEXT NOT NULL DEFAULT '[]',
|
|
30
30
|
pi_packages_json TEXT NOT NULL DEFAULT '[]',
|
|
31
|
+
main_model_json TEXT,
|
|
32
|
+
subagent_model_json TEXT,
|
|
31
33
|
builtin_tools TEXT NOT NULL,
|
|
32
34
|
builtin_tool_names_json TEXT NOT NULL DEFAULT '["read","bash","edit","write"]',
|
|
33
35
|
auto_context_files INTEGER NOT NULL DEFAULT 1,
|
|
@@ -44,6 +46,7 @@ export class CustomAgentStore {
|
|
|
44
46
|
this.migrateAutoContextFilesColumn();
|
|
45
47
|
this.migrateMcpServersColumn();
|
|
46
48
|
this.migratePiPackagesColumn();
|
|
49
|
+
this.migrateModelColumns();
|
|
47
50
|
this.migrateBuiltinToolNamesColumn();
|
|
48
51
|
this.migrateLegacyProfileNames();
|
|
49
52
|
}
|
|
@@ -78,6 +81,8 @@ export class CustomAgentStore {
|
|
|
78
81
|
subagents: sanitizeSubagents(input.subagents ?? []),
|
|
79
82
|
mcpServers: uniqueStrings(input.mcpServers ?? []),
|
|
80
83
|
piPackages: sanitizePiPackages(input.piPackages ?? []),
|
|
84
|
+
mainModel: sanitizeModelProfile(input.mainModel),
|
|
85
|
+
subagentModel: sanitizeModelProfile(input.subagentModel),
|
|
81
86
|
builtinTools: input.builtinTools ?? "default",
|
|
82
87
|
builtinToolNames: sanitizeBuiltinToolNames(input.builtinToolNames),
|
|
83
88
|
autoContextFiles: input.autoContextFiles ?? true,
|
|
@@ -109,6 +114,8 @@ export class CustomAgentStore {
|
|
|
109
114
|
subagents: input.subagents ? sanitizeSubagents(input.subagents) : existing.subagents,
|
|
110
115
|
mcpServers: input.mcpServers ? uniqueStrings(input.mcpServers) : existing.mcpServers,
|
|
111
116
|
piPackages: input.piPackages ? sanitizePiPackages(input.piPackages) : existing.piPackages,
|
|
117
|
+
mainModel: input.mainModel === undefined ? existing.mainModel : sanitizeModelProfile(input.mainModel),
|
|
118
|
+
subagentModel: input.subagentModel === undefined ? existing.subagentModel : sanitizeModelProfile(input.subagentModel),
|
|
112
119
|
builtinTools: input.builtinTools ?? existing.builtinTools,
|
|
113
120
|
builtinToolNames: input.builtinToolNames ? sanitizeBuiltinToolNames(input.builtinToolNames) : existing.builtinToolNames,
|
|
114
121
|
autoContextFiles: input.autoContextFiles ?? existing.autoContextFiles,
|
|
@@ -127,6 +134,8 @@ export class CustomAgentStore {
|
|
|
127
134
|
subagents_json = ?,
|
|
128
135
|
mcp_servers_json = ?,
|
|
129
136
|
pi_packages_json = ?,
|
|
137
|
+
main_model_json = ?,
|
|
138
|
+
subagent_model_json = ?,
|
|
130
139
|
builtin_tools = ?,
|
|
131
140
|
builtin_tool_names_json = ?,
|
|
132
141
|
auto_context_files = ?,
|
|
@@ -134,7 +143,7 @@ export class CustomAgentStore {
|
|
|
134
143
|
updated_at = ?
|
|
135
144
|
WHERE id = ?
|
|
136
145
|
`)
|
|
137
|
-
.run(updated.profileName, updated.displayName, updated.description ?? null, JSON.stringify(updated.nativeTools), JSON.stringify(updated.skills), JSON.stringify(updated.contextFiles), JSON.stringify(sanitizeSubagents(updated.subagents)), JSON.stringify(updated.mcpServers), JSON.stringify(updated.piPackages), updated.builtinTools, JSON.stringify(updated.builtinToolNames), updated.autoContextFiles ? 1 : 0, updated.runControl ? 1 : 0, updated.updatedAt, id);
|
|
146
|
+
.run(updated.profileName, updated.displayName, updated.description ?? null, JSON.stringify(updated.nativeTools), JSON.stringify(updated.skills), JSON.stringify(updated.contextFiles), JSON.stringify(sanitizeSubagents(updated.subagents)), JSON.stringify(updated.mcpServers), JSON.stringify(updated.piPackages), updated.mainModel ? JSON.stringify(updated.mainModel) : null, updated.subagentModel ? JSON.stringify(updated.subagentModel) : null, updated.builtinTools, JSON.stringify(updated.builtinToolNames), updated.autoContextFiles ? 1 : 0, updated.runControl ? 1 : 0, updated.updatedAt, id);
|
|
138
147
|
return this.get(id);
|
|
139
148
|
}
|
|
140
149
|
setArchived(id, archived) {
|
|
@@ -170,6 +179,8 @@ export class CustomAgentStore {
|
|
|
170
179
|
subagents_json,
|
|
171
180
|
mcp_servers_json,
|
|
172
181
|
pi_packages_json,
|
|
182
|
+
main_model_json,
|
|
183
|
+
subagent_model_json,
|
|
173
184
|
builtin_tools,
|
|
174
185
|
builtin_tool_names_json,
|
|
175
186
|
auto_context_files,
|
|
@@ -177,9 +188,9 @@ export class CustomAgentStore {
|
|
|
177
188
|
created_at,
|
|
178
189
|
updated_at,
|
|
179
190
|
archived_at
|
|
180
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
191
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
181
192
|
`)
|
|
182
|
-
.run(agent.id, agent.profileName, agent.ownerScope, agent.displayName, agent.description ?? null, JSON.stringify(agent.nativeTools), JSON.stringify(agent.skills), JSON.stringify(agent.contextFiles), JSON.stringify(sanitizeSubagents(agent.subagents)), JSON.stringify(agent.mcpServers), JSON.stringify(agent.piPackages), agent.builtinTools, JSON.stringify(agent.builtinToolNames), agent.autoContextFiles ? 1 : 0, agent.runControl ? 1 : 0, agent.createdAt, agent.updatedAt, agent.archivedAt ?? null);
|
|
193
|
+
.run(agent.id, agent.profileName, agent.ownerScope, agent.displayName, agent.description ?? null, JSON.stringify(agent.nativeTools), JSON.stringify(agent.skills), JSON.stringify(agent.contextFiles), JSON.stringify(sanitizeSubagents(agent.subagents)), JSON.stringify(agent.mcpServers), JSON.stringify(agent.piPackages), agent.mainModel ? JSON.stringify(agent.mainModel) : null, agent.subagentModel ? JSON.stringify(agent.subagentModel) : null, agent.builtinTools, JSON.stringify(agent.builtinToolNames), agent.autoContextFiles ? 1 : 0, agent.runControl ? 1 : 0, agent.createdAt, agent.updatedAt, agent.archivedAt ?? null);
|
|
183
194
|
}
|
|
184
195
|
requireProfileNameAvailable(profileName, currentId) {
|
|
185
196
|
const row = this.db.prepare("SELECT id FROM chat_agents WHERE profile_name = ?").get(profileName);
|
|
@@ -224,6 +235,15 @@ export class CustomAgentStore {
|
|
|
224
235
|
this.db.prepare("ALTER TABLE chat_agents ADD COLUMN pi_packages_json TEXT NOT NULL DEFAULT '[]'").run();
|
|
225
236
|
}
|
|
226
237
|
}
|
|
238
|
+
migrateModelColumns() {
|
|
239
|
+
const columns = new Set(this.db.prepare("PRAGMA table_info(chat_agents)").all().map((column) => column.name));
|
|
240
|
+
if (!columns.has("main_model_json")) {
|
|
241
|
+
this.db.prepare("ALTER TABLE chat_agents ADD COLUMN main_model_json TEXT").run();
|
|
242
|
+
}
|
|
243
|
+
if (!columns.has("subagent_model_json")) {
|
|
244
|
+
this.db.prepare("ALTER TABLE chat_agents ADD COLUMN subagent_model_json TEXT").run();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
227
247
|
migrateBuiltinToolNamesColumn() {
|
|
228
248
|
const columns = new Set(this.db.prepare("PRAGMA table_info(chat_agents)").all().map((column) => column.name));
|
|
229
249
|
if (!columns.has("builtin_tool_names_json")) {
|
|
@@ -247,6 +267,8 @@ function agentFromRow(row) {
|
|
|
247
267
|
subagents: parseSubagents(row.subagents_json),
|
|
248
268
|
mcpServers: parseStringArray(row.mcp_servers_json),
|
|
249
269
|
piPackages: parseStringArray(row.pi_packages_json),
|
|
270
|
+
mainModel: parseModelProfile(row.main_model_json),
|
|
271
|
+
subagentModel: parseModelProfile(row.subagent_model_json),
|
|
250
272
|
builtinTools: row.builtin_tools,
|
|
251
273
|
builtinToolNames: sanitizeBuiltinToolNames(parseStringArray(row.builtin_tool_names_json)),
|
|
252
274
|
autoContextFiles: row.auto_context_files !== 0,
|
|
@@ -291,6 +313,27 @@ function parseSubagents(value) {
|
|
|
291
313
|
return [];
|
|
292
314
|
}
|
|
293
315
|
}
|
|
316
|
+
function parseModelProfile(value) {
|
|
317
|
+
if (!value)
|
|
318
|
+
return undefined;
|
|
319
|
+
try {
|
|
320
|
+
return sanitizeModelProfile(JSON.parse(value));
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function sanitizeModelProfile(value) {
|
|
327
|
+
if (!value)
|
|
328
|
+
return undefined;
|
|
329
|
+
if (typeof value.provider !== "string" || typeof value.id !== "string")
|
|
330
|
+
return undefined;
|
|
331
|
+
const provider = value.provider.trim();
|
|
332
|
+
const id = value.id.trim();
|
|
333
|
+
if (!provider || !id)
|
|
334
|
+
return undefined;
|
|
335
|
+
return { provider, id };
|
|
336
|
+
}
|
|
294
337
|
function sanitizeSubagents(value) {
|
|
295
338
|
return value.flatMap((item) => {
|
|
296
339
|
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createAgentSessionServices } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
export function buildModelCatalogFromRegistry(registry) {
|
|
3
|
+
const providers = new Map();
|
|
4
|
+
for (const model of registry.getAll()) {
|
|
5
|
+
const providerId = model.provider;
|
|
6
|
+
let provider = providers.get(providerId);
|
|
7
|
+
if (!provider) {
|
|
8
|
+
const authConfigured = registry.getProviderAuthStatus?.(providerId).configured ?? false;
|
|
9
|
+
provider = {
|
|
10
|
+
id: providerId,
|
|
11
|
+
label: registry.getProviderDisplayName?.(providerId) ?? providerId,
|
|
12
|
+
authConfigured,
|
|
13
|
+
models: [],
|
|
14
|
+
};
|
|
15
|
+
providers.set(providerId, provider);
|
|
16
|
+
}
|
|
17
|
+
provider.models.push({
|
|
18
|
+
provider: providerId,
|
|
19
|
+
id: model.id,
|
|
20
|
+
label: model.name || model.id,
|
|
21
|
+
authConfigured: provider.authConfigured,
|
|
22
|
+
supportsReasoning: model.reasoning || undefined,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
providers: [...providers.values()]
|
|
27
|
+
.sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id))
|
|
28
|
+
.map((provider) => ({
|
|
29
|
+
...provider,
|
|
30
|
+
models: [...provider.models].sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id)),
|
|
31
|
+
})),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export async function loadModelCatalog(cwd = process.cwd()) {
|
|
35
|
+
try {
|
|
36
|
+
const services = await createAgentSessionServices({ cwd });
|
|
37
|
+
return buildModelCatalogFromRegistry(services.modelRegistry);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return { providers: [] };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -11,7 +11,9 @@ import { chatStreamFramesFromOutputEvent, createChatStreamState } from "./stream
|
|
|
11
11
|
import { buildSessionNodes, buildTraceView, createTraceViewVersion, loadPiSessionMetadata } from "./trace.js";
|
|
12
12
|
import { isChatWebSessionArchived, withChatWebArchived } from "./session-metadata.js";
|
|
13
13
|
import { CustomAgentStore, createDefaultCustomAgentStore, isValidCustomAgentName, } from "./agent-store.js";
|
|
14
|
+
import { loadPiboModelDefaults, savePiboModelDefaults, } from "../../core/model-defaults.js";
|
|
14
15
|
import { createCustomAgentProfileDefinition } from "./agent-profiles.js";
|
|
16
|
+
import { loadModelCatalog } from "./model-catalog.js";
|
|
15
17
|
import { createDefaultPiboReliabilityStore, PiboReliabilityStore } from "../../reliability/store.js";
|
|
16
18
|
import { listMcpServerInfos, setMcpServerDescription } from "../../mcp/agent-context.js";
|
|
17
19
|
import { readPiboBasePrompt, savePiboCustomBasePrompt, setPiboBasePromptMode, } from "../../core/base-prompt.js";
|
|
@@ -19,6 +21,7 @@ import { readPiboCompactionPrompt, savePiboCustomCompactionPrompt, setPiboCompac
|
|
|
19
21
|
import { getDefaultPiboWorkspace } from "../../core/workspace.js";
|
|
20
22
|
import { inspectPiPackageSource } from "../../pi-packages/metadata.js";
|
|
21
23
|
import { findPiPackage, listPiPackages, removePiPackage, setPiPackageEnabled, upsertPiPackage } from "../../pi-packages/store.js";
|
|
24
|
+
import { UserSkillManager } from "../../user-skills/manager.js";
|
|
22
25
|
export const CHAT_WEB_APP_NAME = "pibo.chat-web";
|
|
23
26
|
export const CHAT_WEB_CHANNEL = "pibo.chat-web";
|
|
24
27
|
export const CHAT_WEB_MOUNT_PATH = "/apps/chat";
|
|
@@ -342,6 +345,23 @@ function normalizeRunControl(value) {
|
|
|
342
345
|
throw new PiboWebHttpError("runControl must be a boolean", 400);
|
|
343
346
|
return value;
|
|
344
347
|
}
|
|
348
|
+
function normalizeModelProfile(value, fieldName) {
|
|
349
|
+
if (value === undefined || value === null)
|
|
350
|
+
return undefined;
|
|
351
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
352
|
+
throw new PiboWebHttpError(`${fieldName} must be an object`, 400);
|
|
353
|
+
}
|
|
354
|
+
const raw = value;
|
|
355
|
+
if (typeof raw.provider !== "string" || typeof raw.id !== "string") {
|
|
356
|
+
throw new PiboWebHttpError(`${fieldName} must include provider and id`, 400);
|
|
357
|
+
}
|
|
358
|
+
const provider = raw.provider.trim();
|
|
359
|
+
const id = raw.id.trim();
|
|
360
|
+
if (!provider || !id) {
|
|
361
|
+
throw new PiboWebHttpError(`${fieldName} must include provider and id`, 400);
|
|
362
|
+
}
|
|
363
|
+
return { provider, id };
|
|
364
|
+
}
|
|
345
365
|
function normalizeMcpServerDescriptionBody(value) {
|
|
346
366
|
if (typeof value !== "string")
|
|
347
367
|
throw new PiboWebHttpError("MCP server description must be a string", 400);
|
|
@@ -372,6 +392,59 @@ function normalizeCompactionPromptMarkdown(value) {
|
|
|
372
392
|
throw new PiboWebHttpError("markdown must be a string", 400);
|
|
373
393
|
return value;
|
|
374
394
|
}
|
|
395
|
+
function normalizeUserSkillName(value) {
|
|
396
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
397
|
+
throw new PiboWebHttpError("Skill name is required", 400);
|
|
398
|
+
}
|
|
399
|
+
const name = value.trim();
|
|
400
|
+
if (name.length > 64)
|
|
401
|
+
throw new PiboWebHttpError("Skill name is too long", 400);
|
|
402
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
403
|
+
throw new PiboWebHttpError("Skill name must be lowercase kebab-case, e.g. my-skill", 400);
|
|
404
|
+
}
|
|
405
|
+
return name;
|
|
406
|
+
}
|
|
407
|
+
function normalizeUserSkillDescription(value) {
|
|
408
|
+
if (typeof value !== "string")
|
|
409
|
+
throw new PiboWebHttpError("Skill description must be a string", 400);
|
|
410
|
+
return value.trim();
|
|
411
|
+
}
|
|
412
|
+
function normalizeUserSkillMarkdown(value) {
|
|
413
|
+
if (typeof value !== "string")
|
|
414
|
+
throw new PiboWebHttpError("Skill markdown must be a string", 400);
|
|
415
|
+
return value;
|
|
416
|
+
}
|
|
417
|
+
function normalizeUserSkillEnabled(value) {
|
|
418
|
+
if (value === undefined)
|
|
419
|
+
return undefined;
|
|
420
|
+
if (typeof value !== "boolean")
|
|
421
|
+
throw new PiboWebHttpError("enabled must be a boolean", 400);
|
|
422
|
+
return value;
|
|
423
|
+
}
|
|
424
|
+
function normalizeUserSkillUrl(value) {
|
|
425
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
426
|
+
throw new PiboWebHttpError("Skill URL is required", 400);
|
|
427
|
+
}
|
|
428
|
+
const url = value.trim();
|
|
429
|
+
if (!url.startsWith("http://") && !url.startsWith("https://") && !url.includes("/")) {
|
|
430
|
+
throw new PiboWebHttpError("Skill URL must be a valid URL or owner/repo shorthand", 400);
|
|
431
|
+
}
|
|
432
|
+
return url;
|
|
433
|
+
}
|
|
434
|
+
function userSkillResourceId(pathname) {
|
|
435
|
+
const prefix = `${CHAT_WEB_API_PREFIX}/user-skills/`;
|
|
436
|
+
if (!pathname.startsWith(prefix))
|
|
437
|
+
return undefined;
|
|
438
|
+
const encodedId = pathname.slice(prefix.length);
|
|
439
|
+
if (!encodedId || encodedId.includes("/"))
|
|
440
|
+
return undefined;
|
|
441
|
+
try {
|
|
442
|
+
return decodeURIComponent(encodedId);
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
throw new PiboWebHttpError("Invalid user skill id", 400);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
375
448
|
function normalizeAgentArchived(value) {
|
|
376
449
|
if (value === undefined)
|
|
377
450
|
return undefined;
|
|
@@ -456,6 +529,15 @@ function createRoomStore(path) {
|
|
|
456
529
|
function createAgentStore(path) {
|
|
457
530
|
return path ? new CustomAgentStore(path) : createDefaultCustomAgentStore();
|
|
458
531
|
}
|
|
532
|
+
function loadChatModelDefaults(cwd = process.cwd()) {
|
|
533
|
+
return loadPiboModelDefaults(cwd);
|
|
534
|
+
}
|
|
535
|
+
function updateChatModelDefaults(body, cwd = process.cwd()) {
|
|
536
|
+
return savePiboModelDefaults({
|
|
537
|
+
main: normalizeModelProfile(body.main, "main"),
|
|
538
|
+
subagent: normalizeModelProfile(body.subagent, "subagent"),
|
|
539
|
+
}, cwd);
|
|
540
|
+
}
|
|
459
541
|
function createReliabilityStore(path) {
|
|
460
542
|
return path ? new PiboReliabilityStore(path) : createDefaultPiboReliabilityStore();
|
|
461
543
|
}
|
|
@@ -716,6 +798,52 @@ function ensureCustomAgentProfiles(state, context) {
|
|
|
716
798
|
context.channelContext.upsertProfile(createCustomAgentProfileDefinition(agent));
|
|
717
799
|
}
|
|
718
800
|
}
|
|
801
|
+
function serializeCustomAgent(agent, context) {
|
|
802
|
+
return {
|
|
803
|
+
...agent,
|
|
804
|
+
brokenContextFiles: listBrokenContextFiles(agent.contextFiles, context),
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
function serializeCustomAgents(agents, context) {
|
|
808
|
+
return agents.map((agent) => serializeCustomAgent(agent, context));
|
|
809
|
+
}
|
|
810
|
+
function listBrokenContextFiles(keys, context) {
|
|
811
|
+
const catalog = context.channelContext.getCapabilityCatalog?.();
|
|
812
|
+
if (!catalog)
|
|
813
|
+
return [];
|
|
814
|
+
const knownKeys = new Set(catalog.contextFiles.map((contextFile) => contextFile.key));
|
|
815
|
+
return keys.filter((key) => !knownKeys.has(key));
|
|
816
|
+
}
|
|
817
|
+
function syncUserSkills(state, context) {
|
|
818
|
+
const registerSkill = context.channelContext.registerSkill;
|
|
819
|
+
const unregisterSkill = context.channelContext.unregisterSkill;
|
|
820
|
+
if (!registerSkill || !unregisterSkill)
|
|
821
|
+
return;
|
|
822
|
+
const userSkills = state.userSkillManager.list();
|
|
823
|
+
const enabledNames = new Set(userSkills.filter((s) => s.enabled).map((s) => s.name));
|
|
824
|
+
const previouslySyncedNames = state.syncedUserSkillNames ?? new Set();
|
|
825
|
+
// Unregister disabled or removed user skills
|
|
826
|
+
for (const name of previouslySyncedNames) {
|
|
827
|
+
if (!enabledNames.has(name)) {
|
|
828
|
+
unregisterSkill(name);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
// Register only newly enabled user skills. Re-registering an already synced
|
|
832
|
+
// skill would trip the registry duplicate-skill guard and break the web UI.
|
|
833
|
+
for (const skill of userSkills) {
|
|
834
|
+
if (skill.enabled && !previouslySyncedNames.has(skill.name)) {
|
|
835
|
+
registerSkill({ name: skill.name, path: skill.path, enabled: true });
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
state.syncedUserSkillNames = enabledNames;
|
|
839
|
+
}
|
|
840
|
+
function assertUserSkillNameIsAvailable(state, context, name, currentSkillId) {
|
|
841
|
+
const currentSkill = currentSkillId ? state.userSkillManager.get(currentSkillId) : undefined;
|
|
842
|
+
const conflict = (context.channelContext.getCapabilityCatalog?.().skills ?? []).find((skill) => (skill.name === name && (!currentSkill || currentSkill.name !== name)));
|
|
843
|
+
if (conflict) {
|
|
844
|
+
throw new PiboWebHttpError(`Skill name "${name}" conflicts with an existing registered skill`, 409);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
719
847
|
function createAgentInput(ownerScope, body) {
|
|
720
848
|
return {
|
|
721
849
|
ownerScope,
|
|
@@ -727,6 +855,8 @@ function createAgentInput(ownerScope, body) {
|
|
|
727
855
|
subagents: normalizeAgentSubagents(body.subagents),
|
|
728
856
|
mcpServers: normalizeNameArray(body.mcpServers, "mcpServers"),
|
|
729
857
|
piPackages: normalizeRegisteredPiPackages(body.piPackages),
|
|
858
|
+
mainModel: normalizeModelProfile(body.mainModel, "mainModel"),
|
|
859
|
+
subagentModel: normalizeModelProfile(body.subagentModel, "subagentModel"),
|
|
730
860
|
builtinTools: normalizeBuiltinTools(body.builtinTools),
|
|
731
861
|
builtinToolNames: normalizeBuiltinToolNames(body.builtinToolNames),
|
|
732
862
|
autoContextFiles: normalizeAutoContextFiles(body.autoContextFiles),
|
|
@@ -751,6 +881,10 @@ function createAgentUpdate(body) {
|
|
|
751
881
|
update.mcpServers = normalizeNameArray(body.mcpServers, "mcpServers");
|
|
752
882
|
if (body.piPackages !== undefined)
|
|
753
883
|
update.piPackages = normalizeRegisteredPiPackages(body.piPackages);
|
|
884
|
+
if (body.mainModel !== undefined)
|
|
885
|
+
update.mainModel = normalizeModelProfile(body.mainModel, "mainModel");
|
|
886
|
+
if (body.subagentModel !== undefined)
|
|
887
|
+
update.subagentModel = normalizeModelProfile(body.subagentModel, "subagentModel");
|
|
754
888
|
if (body.builtinTools !== undefined)
|
|
755
889
|
update.builtinTools = normalizeBuiltinTools(body.builtinTools);
|
|
756
890
|
if (body.builtinToolNames !== undefined)
|
|
@@ -783,7 +917,7 @@ function requireOwnedAgent(agent, webSession) {
|
|
|
783
917
|
}
|
|
784
918
|
return agent;
|
|
785
919
|
}
|
|
786
|
-
async function buildAgentCatalog(context) {
|
|
920
|
+
async function buildAgentCatalog(context, state) {
|
|
787
921
|
return {
|
|
788
922
|
...(context.channelContext.getCapabilityCatalog?.() ?? {
|
|
789
923
|
nativeTools: [],
|
|
@@ -797,6 +931,7 @@ async function buildAgentCatalog(context) {
|
|
|
797
931
|
}),
|
|
798
932
|
mcpServers: await listMcpServerInfos(),
|
|
799
933
|
piPackages: listPiPackages(),
|
|
934
|
+
userSkills: state.userSkillManager.list(),
|
|
800
935
|
};
|
|
801
936
|
}
|
|
802
937
|
function agentsSelectingPiPackage(state, packageId) {
|
|
@@ -2070,6 +2205,7 @@ export function createChatWebApp(options = {}) {
|
|
|
2070
2205
|
reliabilityStore: createReliabilityStore(options.reliabilityStorePath),
|
|
2071
2206
|
traceCache: new Map(),
|
|
2072
2207
|
liveListeners: new Set(),
|
|
2208
|
+
userSkillManager: new UserSkillManager(process.cwd()),
|
|
2073
2209
|
};
|
|
2074
2210
|
const requireSession = (request, context) => context.requireSession({
|
|
2075
2211
|
request,
|
|
@@ -2082,6 +2218,7 @@ export function createChatWebApp(options = {}) {
|
|
|
2082
2218
|
const url = new URL(request.url);
|
|
2083
2219
|
ensureEventIndexing(state, context);
|
|
2084
2220
|
ensureCustomAgentProfiles(state, context);
|
|
2221
|
+
syncUserSkills(state, context);
|
|
2085
2222
|
const builtAsset = responseBuiltChatAsset(request, url.pathname);
|
|
2086
2223
|
if (builtAsset)
|
|
2087
2224
|
return builtAsset;
|
|
@@ -2129,8 +2266,10 @@ export function createChatWebApp(options = {}) {
|
|
|
2129
2266
|
rooms,
|
|
2130
2267
|
sessions,
|
|
2131
2268
|
agents: context.channelContext.getProfiles?.() ?? [],
|
|
2132
|
-
customAgents: state.agentStore.list(webSession.ownerScope, { includeArchived: true }),
|
|
2133
|
-
|
|
2269
|
+
customAgents: serializeCustomAgents(state.agentStore.list(webSession.ownerScope, { includeArchived: true }), context),
|
|
2270
|
+
modelDefaults: loadChatModelDefaults(process.cwd()),
|
|
2271
|
+
modelCatalog: await loadModelCatalog(process.cwd()),
|
|
2272
|
+
agentCatalog: await buildAgentCatalog(context, state),
|
|
2134
2273
|
capabilities: {
|
|
2135
2274
|
actions: context.channelContext.getGatewayActions(),
|
|
2136
2275
|
},
|
|
@@ -2139,10 +2278,16 @@ export function createChatWebApp(options = {}) {
|
|
|
2139
2278
|
if (url.pathname === `${CHAT_WEB_API_PREFIX}/agent-catalog` && request.method === "GET") {
|
|
2140
2279
|
await requireSession(request, context);
|
|
2141
2280
|
return responseJson({
|
|
2142
|
-
catalog: await buildAgentCatalog(context),
|
|
2281
|
+
catalog: await buildAgentCatalog(context, state),
|
|
2143
2282
|
profiles: context.channelContext.getProfiles?.() ?? [],
|
|
2144
2283
|
});
|
|
2145
2284
|
}
|
|
2285
|
+
if (url.pathname === `${CHAT_WEB_API_PREFIX}/model-defaults` && request.method === "PATCH") {
|
|
2286
|
+
requireSameOriginJsonRequest(request);
|
|
2287
|
+
await requireSession(request, context);
|
|
2288
|
+
const body = await readJsonBody(request);
|
|
2289
|
+
return responseJson({ modelDefaults: updateChatModelDefaults(body, process.cwd()) });
|
|
2290
|
+
}
|
|
2146
2291
|
if (url.pathname === `${CHAT_WEB_API_PREFIX}/pi-packages` && request.method === "GET") {
|
|
2147
2292
|
await requireSession(request, context);
|
|
2148
2293
|
return responseJson({ packages: listPiPackages() });
|
|
@@ -2203,6 +2348,86 @@ export function createChatWebApp(options = {}) {
|
|
|
2203
2348
|
const removed = removePiPackage(piPackageId);
|
|
2204
2349
|
return responseJson({ removedPackage: removed });
|
|
2205
2350
|
}
|
|
2351
|
+
if (url.pathname === `${CHAT_WEB_API_PREFIX}/user-skills` && request.method === "GET") {
|
|
2352
|
+
await requireSession(request, context);
|
|
2353
|
+
return responseJson({ skills: state.userSkillManager.list() });
|
|
2354
|
+
}
|
|
2355
|
+
if (url.pathname === `${CHAT_WEB_API_PREFIX}/user-skills` && request.method === "POST") {
|
|
2356
|
+
requireSameOriginJsonRequest(request);
|
|
2357
|
+
await requireSession(request, context);
|
|
2358
|
+
const body = await readJsonBody(request);
|
|
2359
|
+
const name = normalizeUserSkillName(body.name);
|
|
2360
|
+
assertUserSkillNameIsAvailable(state, context, name);
|
|
2361
|
+
const skill = state.userSkillManager.create({
|
|
2362
|
+
name,
|
|
2363
|
+
description: normalizeUserSkillDescription(body.description ?? ""),
|
|
2364
|
+
markdown: normalizeUserSkillMarkdown(body.markdown ?? ""),
|
|
2365
|
+
});
|
|
2366
|
+
syncUserSkills(state, context);
|
|
2367
|
+
return responseJson({ skill }, { status: 201 });
|
|
2368
|
+
}
|
|
2369
|
+
if (url.pathname === `${CHAT_WEB_API_PREFIX}/user-skills/install` && request.method === "POST") {
|
|
2370
|
+
requireSameOriginJsonRequest(request);
|
|
2371
|
+
await requireSession(request, context);
|
|
2372
|
+
const body = await readJsonBody(request);
|
|
2373
|
+
const skill = await state.userSkillManager.installFromUrl(normalizeUserSkillUrl(body.url));
|
|
2374
|
+
try {
|
|
2375
|
+
assertUserSkillNameIsAvailable(state, context, skill.name, skill.id);
|
|
2376
|
+
}
|
|
2377
|
+
catch (error) {
|
|
2378
|
+
state.userSkillManager.remove(skill.id);
|
|
2379
|
+
throw error;
|
|
2380
|
+
}
|
|
2381
|
+
syncUserSkills(state, context);
|
|
2382
|
+
return responseJson({ skill }, { status: 201 });
|
|
2383
|
+
}
|
|
2384
|
+
const userSkillId = userSkillResourceId(url.pathname);
|
|
2385
|
+
if (userSkillId && request.method === "GET") {
|
|
2386
|
+
await requireSession(request, context);
|
|
2387
|
+
const skill = state.userSkillManager.get(userSkillId);
|
|
2388
|
+
if (!skill)
|
|
2389
|
+
throw new PiboWebHttpError("Skill not found", 404);
|
|
2390
|
+
const markdown = state.userSkillManager.getSkillMarkdown(skill.id);
|
|
2391
|
+
return responseJson({ skill, markdown });
|
|
2392
|
+
}
|
|
2393
|
+
if (userSkillId && request.method === "PATCH") {
|
|
2394
|
+
requireSameOriginJsonRequest(request);
|
|
2395
|
+
await requireSession(request, context);
|
|
2396
|
+
const existing = state.userSkillManager.get(userSkillId);
|
|
2397
|
+
if (!existing)
|
|
2398
|
+
throw new PiboWebHttpError("Skill not found", 404);
|
|
2399
|
+
const body = await readJsonBody(request);
|
|
2400
|
+
const input = {};
|
|
2401
|
+
if (body.name !== undefined)
|
|
2402
|
+
input.name = normalizeUserSkillName(body.name);
|
|
2403
|
+
if (body.description !== undefined)
|
|
2404
|
+
input.description = normalizeUserSkillDescription(body.description);
|
|
2405
|
+
if (body.markdown !== undefined)
|
|
2406
|
+
input.markdown = normalizeUserSkillMarkdown(body.markdown);
|
|
2407
|
+
if (body.enabled !== undefined)
|
|
2408
|
+
input.enabled = normalizeUserSkillEnabled(body.enabled);
|
|
2409
|
+
if (Object.keys(input).length === 0) {
|
|
2410
|
+
throw new PiboWebHttpError("No skill update fields provided", 400);
|
|
2411
|
+
}
|
|
2412
|
+
const nextName = input.name ?? existing.name;
|
|
2413
|
+
const nextEnabled = input.enabled ?? existing.enabled;
|
|
2414
|
+
if (nextEnabled) {
|
|
2415
|
+
assertUserSkillNameIsAvailable(state, context, nextName, existing.id);
|
|
2416
|
+
}
|
|
2417
|
+
const skill = state.userSkillManager.update(existing.id, input);
|
|
2418
|
+
syncUserSkills(state, context);
|
|
2419
|
+
return responseJson({ skill });
|
|
2420
|
+
}
|
|
2421
|
+
if (userSkillId && request.method === "DELETE") {
|
|
2422
|
+
requireSameOriginJsonRequest(request);
|
|
2423
|
+
await requireSession(request, context);
|
|
2424
|
+
const existing = state.userSkillManager.get(userSkillId);
|
|
2425
|
+
if (!existing)
|
|
2426
|
+
throw new PiboWebHttpError("Skill not found", 404);
|
|
2427
|
+
state.userSkillManager.remove(existing.id);
|
|
2428
|
+
syncUserSkills(state, context);
|
|
2429
|
+
return responseJson({ removedSkillId: existing.id });
|
|
2430
|
+
}
|
|
2206
2431
|
if (url.pathname === `${CHAT_WEB_API_PREFIX}/base-prompt` && request.method === "GET") {
|
|
2207
2432
|
await requireSession(request, context);
|
|
2208
2433
|
return responseJson({ basePrompt: await readPiboBasePrompt(process.cwd()) });
|
|
@@ -2246,7 +2471,7 @@ export function createChatWebApp(options = {}) {
|
|
|
2246
2471
|
if (url.pathname === `${CHAT_WEB_API_PREFIX}/agents` && request.method === "GET") {
|
|
2247
2472
|
const webSession = await requireSession(request, context);
|
|
2248
2473
|
const includeArchived = parseBooleanSearchParam(url, "includeArchived");
|
|
2249
|
-
return responseJson({ agents: state.agentStore.list(webSession.ownerScope, { includeArchived }) });
|
|
2474
|
+
return responseJson({ agents: serializeCustomAgents(state.agentStore.list(webSession.ownerScope, { includeArchived }), context) });
|
|
2250
2475
|
}
|
|
2251
2476
|
if (url.pathname === `${CHAT_WEB_API_PREFIX}/agents` && request.method === "POST") {
|
|
2252
2477
|
requireSameOriginJsonRequest(request);
|
|
@@ -2256,7 +2481,7 @@ export function createChatWebApp(options = {}) {
|
|
|
2256
2481
|
requireAgentProfileNameAvailable(state, context, input.displayName);
|
|
2257
2482
|
const agent = state.agentStore.create(input);
|
|
2258
2483
|
context.channelContext.upsertProfile?.(createCustomAgentProfileDefinition(agent));
|
|
2259
|
-
return responseJson({ agent }, { status: 201 });
|
|
2484
|
+
return responseJson({ agent: serializeCustomAgent(agent, context) }, { status: 201 });
|
|
2260
2485
|
}
|
|
2261
2486
|
const patchAgentId = agentResourceId(url.pathname);
|
|
2262
2487
|
if (patchAgentId && request.method === "PATCH") {
|
|
@@ -2280,7 +2505,7 @@ export function createChatWebApp(options = {}) {
|
|
|
2280
2505
|
else {
|
|
2281
2506
|
context.channelContext.upsertProfile?.(createCustomAgentProfileDefinition(owned));
|
|
2282
2507
|
}
|
|
2283
|
-
return responseJson({ agent: owned });
|
|
2508
|
+
return responseJson({ agent: serializeCustomAgent(owned, context) });
|
|
2284
2509
|
}
|
|
2285
2510
|
if (patchAgentId && request.method === "DELETE") {
|
|
2286
2511
|
requireSameOriginJsonRequest(request);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{N as e,P as t,U as n,o as r,r as i}from"./dist-SVPsM5Oi.js";import{n as a,r as o}from"./dist-tGfufz3b.js";import{r as s}from"./dist-
|
|
1
|
+
import{N as e,P as t,U as n,o as r,r as i}from"./dist-SVPsM5Oi.js";import{n as a,r as o}from"./dist-tGfufz3b.js";import{r as s}from"./dist-CUqpST5w.js";import{html as c}from"./dist-CqqUC8Ce.js";var l=1,u=33,d=34,f=35,p=36,m=new a(e=>{let t=e.pos;for(;;){if(e.next==10){e.advance();break}else if(e.next==123&&e.peek(1)==123||e.next<0)break;e.advance()}e.pos>t&&e.acceptToken(l)});function h(e,t,n){return new a(r=>{let i=r.pos;for(;r.next!=e&&r.next>=0&&(n||r.next!=38&&(r.next!=123||r.peek(1)!=123));)r.advance();r.pos>i&&r.acceptToken(t)})}var g=h(39,u,!1),_=h(34,d,!1),v=h(39,f,!0),y=h(34,p,!0),b=o.deserialize({version:14,states:"(jOVOqOOOeQpOOOvO!bO'#CaOOOP'#Cx'#CxQVOqOOO!OQpO'#CfO!WQpO'#ClO!]QpO'#CrO!bQpO'#CsOOQO'#Cv'#CvQ!gQpOOQ!lQpOOQ!qQpOOOOOV,58{,58{O!vOpO,58{OOOP-E6v-E6vO!{QpO,59QO#TQpO,59QOOQO,59W,59WO#YQpO,59^OOQO,59_,59_O#_QpOOO#_QpOOO#gQpOOOOOV1G.g1G.gO#oQpO'#CyO#tQpO1G.lOOQO1G.l1G.lO#|QpO1G.lOOQO1G.x1G.xO$UO`O'#DUO$ZOWO'#DUOOQO'#Co'#CoQOQpOOOOQO'#Cu'#CuO$`OtO'#CwO$qOrO'#CwOOQO,59e,59eOOQO-E6w-E6wOOQO7+$W7+$WO%SQpO7+$WO%[QpO7+$WOOOO'#Cp'#CpO%aOpO,59pOOOO'#Cq'#CqO%fOpO,59pOOOS'#Cz'#CzO%kOtO,59cOOQO,59c,59cOOOQ'#C{'#C{O%|OrO,59cO&_QpO<<GrOOQO<<Gr<<GrOOQO1G/[1G/[OOOS-E6x-E6xOOQO1G.}1G.}OOOQ-E6y-E6yOOQOAN=^AN=^",stateData:"&d~OvOS~OPROSQOVROWRO~OZTO[XO^VOaUOhWO~OR]OU^O~O[`O^aO~O[bO~O[cO~O[dO~ObeO~ObfO~ObgO~ORhO~O]kOwiO~O[lO~O_mO~OynOzoO~OysOztO~O[uO~O]wOwiO~O_yOwiO~OtzO~Os|O~OSQOV!OOW!OOr!OOy!QO~OSQOV!ROW!ROq!ROz!QO~O_!TOwiO~O]!UO~Oy!VO~Oz!VO~OSQOV!OOW!OOr!OOy!XO~OSQOV!ROW!ROq!ROz!XO~O]!ZO~O",goto:"#dyPPPPPzPPPP!WPPPPP!WPP!Z!^!a!d!dP!g!j!m!p!v#Q#WPPPPPPPP#^SROSS!Os!PT!Rt!SRYPRqeR{nR}oRZPRqfR[PRqgQSOR_SQj`SvjxRxlQ!PsR!W!PQ!StR!Y!SQpeRrf",nodeNames:`⚠ Text Content }} {{ Interpolation InterpolationContent Entity InvalidEntity Attribute BoundAttributeName [ Identifier ] ( ) ReferenceName # Is ExpressionAttributeValue AttributeInterpolation AttributeInterpolation EventName DirectiveName * StatementAttributeValue AttributeName AttributeValue`,maxTerm:42,nodeProps:[[`openedBy`,3,`{{`,15,`(`],[`closedBy`,4,`}}`,14,`)`],[`isolate`,-4,5,19,25,27,``]],skippedNodes:[0],repeatNodeCount:4,tokenData:"0r~RyOX#rXY$mYZ$mZ]#r]^$m^p#rpq$mqr#rrs%jst&Qtv#rvw&hwx)zxy*byz*xz{+`{}#r}!O+v!O!P-]!P!Q#r!Q![+v![!]+v!]!_#r!_!`-s!`!c#r!c!}+v!}#O.Z#O#P#r#P#Q.q#Q#R#r#R#S+v#S#T#r#T#o+v#o#p/X#p#q#r#q#r0Z#r%W#r%W;'S+v;'S;:j-V;:j;=`$g<%lO+vQ#wTUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rQ$ZSO#q#r#r;'S#r;'S;=`$g<%lO#rQ$jP;=`<%l#rR$t[UQvPOX#rXY$mYZ$mZ]#r]^$m^p#rpq$mq#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR%qTyPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR&XTaPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR&oXUQWPOp'[pq#rq!]'[!]!^#r!^#q'[#q#r(d#r;'S'[;'S;=`)t<%lO'[R'aXUQOp'[pq#rq!]'[!]!^'|!^#q'[#q#r(d#r;'S'[;'S;=`)t<%lO'[R(TTVPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR(gXOp'[pq#rq!]'[!]!^'|!^#q'[#q#r)S#r;'S'[;'S;=`)t<%lO'[P)VUOp)Sq!])S!]!^)i!^;'S)S;'S;=`)n<%lO)SP)nOVPP)qP;=`<%l)SR)wP;=`<%l'[R*RTzPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR*iT^PUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR+PT_PUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR+gThPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR+}b[PUQO}#r}!O+v!O!Q#r!Q![+v![!]+v!]!c#r!c!}+v!}#R#r#R#S+v#S#T#r#T#o+v#o#q#r#q#r$W#r%W#r%W;'S+v;'S;:j-V;:j;=`$g<%lO+vR-YP;=`<%l+vR-dTwPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR-zTUQbPO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR.bTZPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR.xT]PUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR/^VUQO#o#r#o#p/s#p#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR/zTSPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#r~0^TO#q#r#q#r0m#r;'S#r;'S;=`$g<%lO#r~0rOR~",tokenizers:[m,g,_,v,y,0,1],topRules:{Content:[0,2],Attribute:[1,9]},tokenPrec:0}),x=s.parser.configure({top:`SingleExpression`}),S=b.configure({props:[e({Text:t.content,Is:t.definitionOperator,AttributeName:t.attributeName,"AttributeValue ExpressionAttributeValue StatementAttributeValue":t.attributeValue,Entity:t.character,InvalidEntity:t.invalid,"BoundAttributeName/Identifier":t.attributeName,"EventName/Identifier":t.special(t.attributeName),"ReferenceName/Identifier":t.variableName,"DirectiveName/Identifier":t.keyword,"{{ }}":t.brace,"( )":t.paren,"[ ]":t.bracket,"# '*'":t.punctuation})]}),C={parser:x},w={parser:s.parser},T=S.configure({wrap:n((e,t)=>e.name==`InterpolationContent`?C:null)}),E=S.configure({wrap:n((e,t)=>e.name==`InterpolationContent`?C:e.name==`AttributeInterpolation`?e.node.parent?.name==`StatementAttributeValue`?w:C:null),top:`Attribute`}),D={parser:T},O={parser:E},k=c({selfClosingTags:!0});function A(e){return e.configure({wrap:n(M)},`angular`)}var j=A(k.language);function M(e,t){switch(e.name){case`Attribute`:return/^[*#(\[]|\{\{/.test(t.read(e.from,e.to))?O:null;case`Text`:return D}return null}function N(e={}){let t=k;if(e.base){if(e.base.language.name!=`html`||!(e.base.language instanceof i))throw RangeError(`The base option must be the result of calling html(...)`);t=e.base}return new r(t.language==k.language?j:A(t.language),[t.support,t.language.data.of({closeBrackets:{brackets:[`[`,`{`,`"`]},indentOnInput:/^\s*[\}\]]$/})])}export{N as angular};
|