@pi-orca/core 0.0.2-dev.20260413162335
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 +228 -0
- package/defaults/config.yaml +28 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/types/agent.d.ts +72 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +6 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/archivable.d.ts +23 -0
- package/dist/types/archivable.d.ts.map +1 -0
- package/dist/types/archivable.js +47 -0
- package/dist/types/archivable.js.map +1 -0
- package/dist/types/config.d.ts +42 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +42 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/labels.d.ts +28 -0
- package/dist/types/labels.d.ts.map +1 -0
- package/dist/types/labels.js +45 -0
- package/dist/types/labels.js.map +1 -0
- package/dist/types/lock.d.ts +16 -0
- package/dist/types/lock.d.ts.map +1 -0
- package/dist/types/lock.js +6 -0
- package/dist/types/lock.js.map +1 -0
- package/dist/types/message.d.ts +29 -0
- package/dist/types/message.d.ts.map +1 -0
- package/dist/types/message.js +6 -0
- package/dist/types/message.js.map +1 -0
- package/dist/types/task.d.ts +45 -0
- package/dist/types/task.d.ts.map +1 -0
- package/dist/types/task.js +27 -0
- package/dist/types/task.js.map +1 -0
- package/dist/types/team.d.ts +38 -0
- package/dist/types/team.d.ts.map +1 -0
- package/dist/types/team.js +6 -0
- package/dist/types/team.js.map +1 -0
- package/dist/utils/autocomplete.d.ts +42 -0
- package/dist/utils/autocomplete.d.ts.map +1 -0
- package/dist/utils/autocomplete.js +111 -0
- package/dist/utils/autocomplete.js.map +1 -0
- package/dist/utils/config-loader.d.ts +17 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +81 -0
- package/dist/utils/config-loader.js.map +1 -0
- package/dist/utils/dag.d.ts +34 -0
- package/dist/utils/dag.d.ts.map +1 -0
- package/dist/utils/dag.js +96 -0
- package/dist/utils/dag.js.map +1 -0
- package/dist/utils/duration.d.ts +21 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +47 -0
- package/dist/utils/duration.js.map +1 -0
- package/dist/utils/filesystem.d.ts +53 -0
- package/dist/utils/filesystem.d.ts.map +1 -0
- package/dist/utils/filesystem.js +99 -0
- package/dist/utils/filesystem.js.map +1 -0
- package/dist/utils/heartbeat.d.ts +23 -0
- package/dist/utils/heartbeat.d.ts.map +1 -0
- package/dist/utils/heartbeat.js +53 -0
- package/dist/utils/heartbeat.js.map +1 -0
- package/dist/utils/index.d.ts +14 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +14 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/lock.d.ts +43 -0
- package/dist/utils/lock.d.ts.map +1 -0
- package/dist/utils/lock.js +89 -0
- package/dist/utils/lock.js.map +1 -0
- package/dist/utils/path.d.ts +88 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +128 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/process.d.ts +20 -0
- package/dist/utils/process.d.ts.map +1 -0
- package/dist/utils/process.js +40 -0
- package/dist/utils/process.js.map +1 -0
- package/dist/utils/yaml.d.ts +30 -0
- package/dist/utils/yaml.d.ts.map +1 -0
- package/dist/utils/yaml.js +58 -0
- package/dist/utils/yaml.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# @pi-orca/core
|
|
2
|
+
|
|
3
|
+
**Shared types and utilities for Pi Orca extension suite**
|
|
4
|
+
|
|
5
|
+
Version: 0.0.1
|
|
6
|
+
License: MIT
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
`@pi-orca/core` is the foundation package for the Pi Orca multi-agent extension suite. It provides shared TypeScript types, utilities, and configuration helpers used by all Pi Orca extensions.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Type Definitions** — Complete TypeScript types for tasks, agents, messages, teams, and configuration
|
|
15
|
+
- **Label Filtering** — Flexible label matching with special values (`_unset`, `_empty`, `_blank`)
|
|
16
|
+
- **Filesystem Utilities** — Atomic writes, path resolution, YAML frontmatter parsing
|
|
17
|
+
- **Lock Management** — Multi-session lock files with PID-based staleness detection
|
|
18
|
+
- **DAG Utilities** — Cycle detection and transitive dependency walking for task graphs
|
|
19
|
+
- **Heartbeat System** — Periodic status file writes with cost tracking
|
|
20
|
+
- **Duration Parsing** — Human-readable duration strings (`30s`, `5m`, `2h`, `7d`)
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @pi-orca/core
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### Types
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { Task, TaskStatus, AgentTemplate, Labels } from "@pi-orca/core";
|
|
34
|
+
|
|
35
|
+
const task: Task = {
|
|
36
|
+
id: "task-001",
|
|
37
|
+
title: "Implement feature",
|
|
38
|
+
status: "pending",
|
|
39
|
+
// ... other fields
|
|
40
|
+
};
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Label Filtering
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { matchLabels, LABEL_BLANK } from "@pi-orca/core";
|
|
47
|
+
|
|
48
|
+
const entityLabels = { team: "frontend", priority: "high" };
|
|
49
|
+
const filter = { team: "frontend" };
|
|
50
|
+
|
|
51
|
+
if (matchLabels(entityLabels, filter)) {
|
|
52
|
+
console.log("Labels match!");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Find unassigned tasks
|
|
56
|
+
const unassignedFilter = { assignedTo: LABEL_BLANK };
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Filesystem Utilities
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import {
|
|
63
|
+
atomicWrite,
|
|
64
|
+
parseFrontmatter,
|
|
65
|
+
serializeFrontmatter,
|
|
66
|
+
bootstrapFile
|
|
67
|
+
} from "@pi-orca/core";
|
|
68
|
+
|
|
69
|
+
// Atomic file write
|
|
70
|
+
await atomicWrite("/path/to/file.md", content);
|
|
71
|
+
|
|
72
|
+
// Parse markdown with YAML frontmatter
|
|
73
|
+
const { frontmatter, body } = parseFrontmatter<Task>(content);
|
|
74
|
+
|
|
75
|
+
// Serialize to markdown
|
|
76
|
+
const markdown = serializeFrontmatter(frontmatter, body);
|
|
77
|
+
|
|
78
|
+
// Bootstrap config file with defaults
|
|
79
|
+
const { created, content } = await bootstrapFile(
|
|
80
|
+
"/path/to/config.yaml",
|
|
81
|
+
defaultConfig,
|
|
82
|
+
{ notify: true }
|
|
83
|
+
);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Lock Management
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { acquireLock, releaseLock, isLockStale } from "@pi-orca/core";
|
|
90
|
+
|
|
91
|
+
const lockPath = "/path/to/task.lock";
|
|
92
|
+
const lockInfo = {
|
|
93
|
+
sessionId: "abc123",
|
|
94
|
+
sessionPath: "project/session.jsonl",
|
|
95
|
+
pid: process.pid,
|
|
96
|
+
acquiredAt: new Date().toISOString(),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Acquire lock
|
|
100
|
+
if (await acquireLock(lockPath, lockInfo)) {
|
|
101
|
+
console.log("Lock acquired");
|
|
102
|
+
} else {
|
|
103
|
+
console.log("Lock held by another session");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Check if lock is stale
|
|
107
|
+
if (await isLockStale(lockPath)) {
|
|
108
|
+
console.log("Lock is stale (PID dead)");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Release lock
|
|
112
|
+
await releaseLock(lockPath, "abc123");
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### DAG Utilities
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { detectCycle, getTransitiveDependents } from "@pi-orca/core";
|
|
119
|
+
|
|
120
|
+
const dependencies = new Map([
|
|
121
|
+
["task-001", ["task-002"]],
|
|
122
|
+
["task-002", ["task-003"]],
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
// Detect cycles
|
|
126
|
+
const cycle = detectCycle("task-003", "task-001", dependencies);
|
|
127
|
+
if (cycle) {
|
|
128
|
+
console.log("Cycle detected:", cycle);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Get transitive dependents
|
|
132
|
+
const dependents = getTransitiveDependents("task-002", dependentsMap);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Heartbeat
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { createHeartbeat } from "@pi-orca/core";
|
|
139
|
+
|
|
140
|
+
const heartbeat = createHeartbeat({
|
|
141
|
+
filePath: "/path/to/status.yaml",
|
|
142
|
+
intervalMs: 30000, // 30 seconds
|
|
143
|
+
updateFn: (data) => ({
|
|
144
|
+
...data,
|
|
145
|
+
status: "running",
|
|
146
|
+
// Additional status fields
|
|
147
|
+
}),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
heartbeat.start();
|
|
151
|
+
|
|
152
|
+
// Update accumulated data
|
|
153
|
+
heartbeat.update({ inputTokens: 1000, outputTokens: 500 });
|
|
154
|
+
|
|
155
|
+
// Stop heartbeat
|
|
156
|
+
heartbeat.stop();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Path Utilities
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import {
|
|
163
|
+
getOrcaRuntimeDir,
|
|
164
|
+
getOrcaTasksDir,
|
|
165
|
+
getUserOrcaDir,
|
|
166
|
+
getProjectOrcaDir
|
|
167
|
+
} from "@pi-orca/core";
|
|
168
|
+
|
|
169
|
+
const runtimeDir = getOrcaRuntimeDir(); // ~/.pi/agent/sessions/<slug>/orca/
|
|
170
|
+
const tasksDir = getOrcaTasksDir(); // .../orca/tasks/
|
|
171
|
+
const userDir = getUserOrcaDir(); // ~/.pi/agent/orca/
|
|
172
|
+
const projectDir = getProjectOrcaDir(); // <project>/.pi/orca/
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## API Documentation
|
|
176
|
+
|
|
177
|
+
### Types
|
|
178
|
+
|
|
179
|
+
- `Labels` — Key-value string pairs
|
|
180
|
+
- `LabelFilter` — Filter with special values support
|
|
181
|
+
- `Archivable` — Interface for cleanup-eligible entities
|
|
182
|
+
- `OrcaConfig` — Shared configuration schema
|
|
183
|
+
- `Task`, `TaskStatus` — Task types and state machine
|
|
184
|
+
- `AgentTemplate`, `AgentStatus`, `AgentStatusFile` — Agent types
|
|
185
|
+
- `Message`, `InboxRef` — Message types
|
|
186
|
+
- `TeamTemplate` — Team composition schema
|
|
187
|
+
- `LockInfo` — Lock file schema
|
|
188
|
+
|
|
189
|
+
### Utilities
|
|
190
|
+
|
|
191
|
+
- **Duration**: `parseDuration()`, `formatDuration()`
|
|
192
|
+
- **Path**: `getOrcaRuntimeDir()`, `getOrcaTasksDir()`, `getUserOrcaDir()`, etc.
|
|
193
|
+
- **YAML**: `parseFrontmatter()`, `serializeFrontmatter()`
|
|
194
|
+
- **Filesystem**: `atomicWrite()`, `ensureDir()`, `bootstrapFile()`
|
|
195
|
+
- **Lock**: `acquireLock()`, `releaseLock()`, `isLockStale()`
|
|
196
|
+
- **Process**: `isPidAlive()`, `getCurrentPid()`
|
|
197
|
+
- **DAG**: `detectCycle()`, `getTransitiveDependents()`, `getTransitiveDependencies()`
|
|
198
|
+
- **Heartbeat**: `createHeartbeat()`
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Build
|
|
204
|
+
npm run build
|
|
205
|
+
|
|
206
|
+
# Run tests
|
|
207
|
+
npm test
|
|
208
|
+
|
|
209
|
+
# Watch mode
|
|
210
|
+
npm run test:watch
|
|
211
|
+
|
|
212
|
+
# Type check
|
|
213
|
+
npm run typecheck
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Related Packages
|
|
217
|
+
|
|
218
|
+
- `@pi-orca/tasks` — Task management extension
|
|
219
|
+
- `@pi-orca/messages` — Inter-session messaging
|
|
220
|
+
- `@pi-orca/models` — Model alias management
|
|
221
|
+
- `@pi-orca/agents` — Subagent lifecycle management
|
|
222
|
+
- `@pi-orca/teams` — Team orchestration
|
|
223
|
+
- `@pi-orca/cleanup` — Retention and cleanup
|
|
224
|
+
- `@pi-orca/profiles` — Extension profiles
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT License — see [LICENSE](../../LICENSE) for details.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Pi Orca — Shared Configuration
|
|
2
|
+
# Spec: §2.2 — Shared Configuration
|
|
3
|
+
#
|
|
4
|
+
# This file contains cross-cutting settings shared by multiple extensions.
|
|
5
|
+
# Project config (.pi/orca/config.yaml) overrides user config (~/.pi/agent/orca/config.yaml).
|
|
6
|
+
|
|
7
|
+
# Polling intervals and heartbeat configuration
|
|
8
|
+
polling:
|
|
9
|
+
# Message + agent status polling when session is idle (seconds)
|
|
10
|
+
idleIntervalSeconds: 30
|
|
11
|
+
|
|
12
|
+
# Agent heartbeat write interval (seconds)
|
|
13
|
+
heartbeatIntervalSeconds: 30
|
|
14
|
+
|
|
15
|
+
# Heartbeat staleness threshold = dead agent (seconds)
|
|
16
|
+
# If no heartbeat for this duration, agent is considered stuck
|
|
17
|
+
stuckThresholdSeconds: 120
|
|
18
|
+
|
|
19
|
+
# Parent liveness and shutdown protocol
|
|
20
|
+
parentLiveness:
|
|
21
|
+
# Prompt before orphaning children on shutdown
|
|
22
|
+
shutdownGuard: true
|
|
23
|
+
|
|
24
|
+
# Children detect dead parents and shut down gracefully
|
|
25
|
+
childSelfRescue: true
|
|
26
|
+
|
|
27
|
+
# Grace period to finish current task before shutting down (seconds)
|
|
28
|
+
orphanGracePeriodSeconds: 60
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent types and templates.
|
|
3
|
+
* Spec: §5 — pi-orca-agents Extension
|
|
4
|
+
*/
|
|
5
|
+
import { Labels } from "./labels.js";
|
|
6
|
+
/**
|
|
7
|
+
* Agent status values.
|
|
8
|
+
* Spec: §5.5 — Per-Agent Status Files
|
|
9
|
+
*/
|
|
10
|
+
export type AgentStatus = "running" | "completed" | "failed" | "orphaned";
|
|
11
|
+
/**
|
|
12
|
+
* Agent isolation mode.
|
|
13
|
+
* Spec: §5.2 — Agent Template Schema
|
|
14
|
+
*/
|
|
15
|
+
export type AgentIsolation = "sdk" | "process" | "tmux";
|
|
16
|
+
/**
|
|
17
|
+
* Agent context mode.
|
|
18
|
+
* Spec: §5.2
|
|
19
|
+
*/
|
|
20
|
+
export type AgentContext = "fresh" | "fork";
|
|
21
|
+
/**
|
|
22
|
+
* Restrictions mode for tools.
|
|
23
|
+
* Spec: §5.2
|
|
24
|
+
*/
|
|
25
|
+
export type RestrictionsMode = "override" | "extend";
|
|
26
|
+
/**
|
|
27
|
+
* Agent template schema (YAML frontmatter).
|
|
28
|
+
* Spec: §5.2 — Agent Template Schema
|
|
29
|
+
*/
|
|
30
|
+
export interface AgentTemplate {
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
model: string;
|
|
34
|
+
thinking?: string;
|
|
35
|
+
context: AgentContext;
|
|
36
|
+
tools?: string[];
|
|
37
|
+
skills?: string[];
|
|
38
|
+
restrictions?: string[];
|
|
39
|
+
restrictionsMode: RestrictionsMode;
|
|
40
|
+
isolation: AgentIsolation;
|
|
41
|
+
completionNotify: "parent";
|
|
42
|
+
labels: Labels;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Per-agent status file schema.
|
|
46
|
+
* Spec: §5.5 — Per-Agent Status Files
|
|
47
|
+
*/
|
|
48
|
+
export interface AgentStatusFile {
|
|
49
|
+
agentId: string;
|
|
50
|
+
templateName: string;
|
|
51
|
+
sessionId: string;
|
|
52
|
+
sessionPath: string;
|
|
53
|
+
parentSessionId: string;
|
|
54
|
+
parentSessionPath: string;
|
|
55
|
+
rootSessionPath: string;
|
|
56
|
+
pid: number;
|
|
57
|
+
isolation: AgentIsolation;
|
|
58
|
+
model: string;
|
|
59
|
+
context: AgentContext;
|
|
60
|
+
spawnedAt: string;
|
|
61
|
+
status: AgentStatus;
|
|
62
|
+
lastHeartbeat: string;
|
|
63
|
+
exitReason: string;
|
|
64
|
+
result: string;
|
|
65
|
+
labels: Labels;
|
|
66
|
+
cost: {
|
|
67
|
+
inputTokens: number;
|
|
68
|
+
outputTokens: number;
|
|
69
|
+
totalCost: number;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/types/agent.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE1E;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,SAAS,EAAE,cAAc,CAAC;IAC1B,gBAAgB,EAAE,QAAQ,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,cAAc,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QACJ,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/types/agent.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Archivable convention for cleanup.
|
|
3
|
+
* Spec: §2.4 — Archivable Convention
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Entities that support archival must implement this interface.
|
|
7
|
+
*/
|
|
8
|
+
export interface Archivable {
|
|
9
|
+
/** Eligible for cleanup sweep */
|
|
10
|
+
archivable: boolean;
|
|
11
|
+
/** ISO 8601 timestamp when entity reached terminal state */
|
|
12
|
+
completedAt: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Check if an entity is eligible for archival based on age.
|
|
16
|
+
* Spec: §2.4
|
|
17
|
+
*
|
|
18
|
+
* @param entity - Entity with archivable fields
|
|
19
|
+
* @param maxAge - Duration string (e.g., "7d", "14d", "3d")
|
|
20
|
+
* @returns true if archivable and older than maxAge
|
|
21
|
+
*/
|
|
22
|
+
export declare function isArchivable(entity: Archivable, maxAge: string): boolean;
|
|
23
|
+
//# sourceMappingURL=archivable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archivable.d.ts","sourceRoot":"","sources":["../../src/types/archivable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,UAAU,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAcxE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Archivable convention for cleanup.
|
|
3
|
+
* Spec: §2.4 — Archivable Convention
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Check if an entity is eligible for archival based on age.
|
|
7
|
+
* Spec: §2.4
|
|
8
|
+
*
|
|
9
|
+
* @param entity - Entity with archivable fields
|
|
10
|
+
* @param maxAge - Duration string (e.g., "7d", "14d", "3d")
|
|
11
|
+
* @returns true if archivable and older than maxAge
|
|
12
|
+
*/
|
|
13
|
+
export function isArchivable(entity, maxAge) {
|
|
14
|
+
if (!entity.archivable || !entity.completedAt) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const completedDate = new Date(entity.completedAt);
|
|
18
|
+
if (isNaN(completedDate.getTime())) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const maxAgeMs = parseDuration(maxAge);
|
|
22
|
+
const age = Date.now() - completedDate.getTime();
|
|
23
|
+
return age >= maxAgeMs;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse a duration string to milliseconds.
|
|
27
|
+
* Supports: 30s, 5m, 2h, 7d
|
|
28
|
+
*
|
|
29
|
+
* @param input - Duration string
|
|
30
|
+
* @returns Milliseconds
|
|
31
|
+
*/
|
|
32
|
+
function parseDuration(input) {
|
|
33
|
+
const match = input.match(/^(\d+)([smhd])$/);
|
|
34
|
+
if (!match) {
|
|
35
|
+
throw new Error(`Invalid duration format: ${input}`);
|
|
36
|
+
}
|
|
37
|
+
const value = parseInt(match[1], 10);
|
|
38
|
+
const unit = match[2];
|
|
39
|
+
const multipliers = {
|
|
40
|
+
s: 1000,
|
|
41
|
+
m: 60 * 1000,
|
|
42
|
+
h: 60 * 60 * 1000,
|
|
43
|
+
d: 24 * 60 * 60 * 1000,
|
|
44
|
+
};
|
|
45
|
+
return value * multipliers[unit];
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=archivable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archivable.js","sourceRoot":"","sources":["../../src/types/archivable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,MAAkB,EAAE,MAAc;IAC7D,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC;IAEjD,OAAO,GAAG,IAAI,QAAQ,CAAC;AACzB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,KAAa;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAEtB,MAAM,WAAW,GAA2B;QAC1C,CAAC,EAAE,IAAI;QACP,CAAC,EAAE,EAAE,GAAG,IAAI;QACZ,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;QACjB,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;KACvB,CAAC;IAEF,OAAO,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared configuration types.
|
|
3
|
+
* Spec: §2.2 — Shared Configuration
|
|
4
|
+
*/
|
|
5
|
+
export interface PollingConfig {
|
|
6
|
+
/** Message + agent status polling when idle (seconds) */
|
|
7
|
+
idleIntervalSeconds: number;
|
|
8
|
+
/** Agent heartbeat write interval (seconds) */
|
|
9
|
+
heartbeatIntervalSeconds: number;
|
|
10
|
+
/** Heartbeat staleness threshold = dead agent (seconds) */
|
|
11
|
+
stuckThresholdSeconds: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ParentLivenessConfig {
|
|
14
|
+
/** Prompt before orphaning children on shutdown */
|
|
15
|
+
shutdownGuard: boolean;
|
|
16
|
+
/** Children detect dead parents and shut down */
|
|
17
|
+
childSelfRescue: boolean;
|
|
18
|
+
/** Finish current task before shutting down (seconds) */
|
|
19
|
+
orphanGracePeriodSeconds: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Cross-cutting settings shared by multiple extensions.
|
|
23
|
+
* Spec: §2.2
|
|
24
|
+
*/
|
|
25
|
+
export interface OrcaConfig {
|
|
26
|
+
polling: PollingConfig;
|
|
27
|
+
parentLiveness: ParentLivenessConfig;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Default configuration values.
|
|
31
|
+
*/
|
|
32
|
+
export declare const DEFAULT_CONFIG: OrcaConfig;
|
|
33
|
+
/**
|
|
34
|
+
* Merge user and project configs (project overrides user).
|
|
35
|
+
* Spec: §2.2
|
|
36
|
+
*
|
|
37
|
+
* @param userConfig - User-level config
|
|
38
|
+
* @param projectConfig - Project-level config (optional)
|
|
39
|
+
* @returns Merged configuration
|
|
40
|
+
*/
|
|
41
|
+
export declare function mergeConfig(userConfig: Partial<OrcaConfig>, projectConfig?: Partial<OrcaConfig>): OrcaConfig;
|
|
42
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,+CAA+C;IAC/C,wBAAwB,EAAE,MAAM,CAAC;IACjC,2DAA2D;IAC3D,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,aAAa,EAAE,OAAO,CAAC;IACvB,iDAAiD;IACjD,eAAe,EAAE,OAAO,CAAC;IACzB,yDAAyD;IACzD,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,cAAc,EAAE,oBAAoB,CAAC;CACtC;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,UAW5B,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,EAC/B,aAAa,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAClC,UAAU,CAaZ"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared configuration types.
|
|
3
|
+
* Spec: §2.2 — Shared Configuration
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Default configuration values.
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_CONFIG = {
|
|
9
|
+
polling: {
|
|
10
|
+
idleIntervalSeconds: 30,
|
|
11
|
+
heartbeatIntervalSeconds: 30,
|
|
12
|
+
stuckThresholdSeconds: 120,
|
|
13
|
+
},
|
|
14
|
+
parentLiveness: {
|
|
15
|
+
shutdownGuard: true,
|
|
16
|
+
childSelfRescue: true,
|
|
17
|
+
orphanGracePeriodSeconds: 60,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Merge user and project configs (project overrides user).
|
|
22
|
+
* Spec: §2.2
|
|
23
|
+
*
|
|
24
|
+
* @param userConfig - User-level config
|
|
25
|
+
* @param projectConfig - Project-level config (optional)
|
|
26
|
+
* @returns Merged configuration
|
|
27
|
+
*/
|
|
28
|
+
export function mergeConfig(userConfig, projectConfig) {
|
|
29
|
+
return {
|
|
30
|
+
polling: {
|
|
31
|
+
...DEFAULT_CONFIG.polling,
|
|
32
|
+
...userConfig.polling,
|
|
33
|
+
...projectConfig?.polling,
|
|
34
|
+
},
|
|
35
|
+
parentLiveness: {
|
|
36
|
+
...DEFAULT_CONFIG.parentLiveness,
|
|
37
|
+
...userConfig.parentLiveness,
|
|
38
|
+
...projectConfig?.parentLiveness,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6BH;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,OAAO,EAAE;QACP,mBAAmB,EAAE,EAAE;QACvB,wBAAwB,EAAE,EAAE;QAC5B,qBAAqB,EAAE,GAAG;KAC3B;IACD,cAAc,EAAE;QACd,aAAa,EAAE,IAAI;QACnB,eAAe,EAAE,IAAI;QACrB,wBAAwB,EAAE,EAAE;KAC7B;CACF,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,UAA+B,EAC/B,aAAmC;IAEnC,OAAO;QACL,OAAO,EAAE;YACP,GAAG,cAAc,CAAC,OAAO;YACzB,GAAG,UAAU,CAAC,OAAO;YACrB,GAAG,aAAa,EAAE,OAAO;SAC1B;QACD,cAAc,EAAE;YACd,GAAG,cAAc,CAAC,cAAc;YAChC,GAAG,UAAU,CAAC,cAAc;YAC5B,GAAG,aAAa,EAAE,cAAc;SACjC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Orca Core Types
|
|
3
|
+
*/
|
|
4
|
+
export * from "./labels.js";
|
|
5
|
+
export * from "./archivable.js";
|
|
6
|
+
export * from "./config.js";
|
|
7
|
+
export * from "./task.js";
|
|
8
|
+
export * from "./agent.js";
|
|
9
|
+
export * from "./message.js";
|
|
10
|
+
export * from "./team.js";
|
|
11
|
+
export * from "./lock.js";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Orca Core Types
|
|
3
|
+
*/
|
|
4
|
+
export * from "./labels.js";
|
|
5
|
+
export * from "./archivable.js";
|
|
6
|
+
export * from "./config.js";
|
|
7
|
+
export * from "./task.js";
|
|
8
|
+
export * from "./agent.js";
|
|
9
|
+
export * from "./message.js";
|
|
10
|
+
export * from "./team.js";
|
|
11
|
+
export * from "./lock.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Label types and filtering for Pi Orca entities.
|
|
3
|
+
* Spec: §2.3 — Labels and Tags
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Labels are arbitrary key-value pairs attached to entities.
|
|
7
|
+
*/
|
|
8
|
+
export type Labels = Record<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* Special filter values for matching absent or empty labels.
|
|
11
|
+
*/
|
|
12
|
+
export declare const LABEL_UNSET = "_unset";
|
|
13
|
+
export declare const LABEL_EMPTY = "_empty";
|
|
14
|
+
export declare const LABEL_BLANK = "_blank";
|
|
15
|
+
/**
|
|
16
|
+
* Label filter with support for special matching values.
|
|
17
|
+
*/
|
|
18
|
+
export type LabelFilter = Record<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* Check if entity labels match the filter (conjunctive AND).
|
|
21
|
+
* Spec: §2.3
|
|
22
|
+
*
|
|
23
|
+
* @param entityLabels - Labels attached to the entity
|
|
24
|
+
* @param filter - Label filter to match against
|
|
25
|
+
* @returns true if all filter labels match
|
|
26
|
+
*/
|
|
27
|
+
export declare function matchLabels(entityLabels: Labels, filter: LabelFilter): boolean;
|
|
28
|
+
//# sourceMappingURL=labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/types/labels.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE5C;;GAEG;AACH,eAAO,MAAM,WAAW,WAAW,CAAC;AACpC,eAAO,MAAM,WAAW,WAAW,CAAC;AACpC,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CA0B9E"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Label types and filtering for Pi Orca entities.
|
|
3
|
+
* Spec: §2.3 — Labels and Tags
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Special filter values for matching absent or empty labels.
|
|
7
|
+
*/
|
|
8
|
+
export const LABEL_UNSET = "_unset"; // Label key is not present
|
|
9
|
+
export const LABEL_EMPTY = "_empty"; // Label key is present but empty string
|
|
10
|
+
export const LABEL_BLANK = "_blank"; // Either not present OR empty string
|
|
11
|
+
/**
|
|
12
|
+
* Check if entity labels match the filter (conjunctive AND).
|
|
13
|
+
* Spec: §2.3
|
|
14
|
+
*
|
|
15
|
+
* @param entityLabels - Labels attached to the entity
|
|
16
|
+
* @param filter - Label filter to match against
|
|
17
|
+
* @returns true if all filter labels match
|
|
18
|
+
*/
|
|
19
|
+
export function matchLabels(entityLabels, filter) {
|
|
20
|
+
for (const [key, filterValue] of Object.entries(filter)) {
|
|
21
|
+
const entityValue = entityLabels[key];
|
|
22
|
+
const isPresent = key in entityLabels;
|
|
23
|
+
// Handle special filter values
|
|
24
|
+
if (filterValue === LABEL_UNSET) {
|
|
25
|
+
if (isPresent)
|
|
26
|
+
return false;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (filterValue === LABEL_EMPTY) {
|
|
30
|
+
if (!isPresent || entityValue !== "")
|
|
31
|
+
return false;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (filterValue === LABEL_BLANK) {
|
|
35
|
+
if (isPresent && entityValue !== "")
|
|
36
|
+
return false;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// Exact match required
|
|
40
|
+
if (entityValue !== filterValue)
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=labels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../src/types/labels.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,2BAA2B;AAChE,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,wCAAwC;AAC7E,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,qCAAqC;AAO1E;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,YAAoB,EAAE,MAAmB;IACnE,KAAK,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,MAAM,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,SAAS,GAAG,GAAG,IAAI,YAAY,CAAC;QAEtC,+BAA+B;QAC/B,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;YAChC,IAAI,SAAS;gBAAE,OAAO,KAAK,CAAC;YAC5B,SAAS;QACX,CAAC;QAED,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;YAChC,IAAI,CAAC,SAAS,IAAI,WAAW,KAAK,EAAE;gBAAE,OAAO,KAAK,CAAC;YACnD,SAAS;QACX,CAAC;QAED,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;YAChC,IAAI,SAAS,IAAI,WAAW,KAAK,EAAE;gBAAE,OAAO,KAAK,CAAC;YAClD,SAAS;QACX,CAAC;QAED,uBAAuB;QACvB,IAAI,WAAW,KAAK,WAAW;YAAE,OAAO,KAAK,CAAC;IAChD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|