@perceo/observer-engine 0.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/dist/index.d.ts +149 -0
- package/dist/index.js +169 -0
- package/package.json +33 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
interface ObserverEngineConfig {
|
|
2
|
+
observer: {
|
|
3
|
+
watch: {
|
|
4
|
+
paths: string[];
|
|
5
|
+
ignore?: string[];
|
|
6
|
+
debounceMs?: number;
|
|
7
|
+
autoTest?: boolean;
|
|
8
|
+
};
|
|
9
|
+
ci?: {
|
|
10
|
+
strategy?: "affected-flows" | string;
|
|
11
|
+
parallelism?: number;
|
|
12
|
+
};
|
|
13
|
+
analysis?: {
|
|
14
|
+
useLLM?: boolean;
|
|
15
|
+
llmThreshold?: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Optional base URL and API key for managed Observer Engine APIs.
|
|
19
|
+
* When present, bootstrap and analysis operations will be delegated
|
|
20
|
+
* to the remote service.
|
|
21
|
+
*/
|
|
22
|
+
apiBaseUrl?: string;
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
};
|
|
25
|
+
flowGraph?: {
|
|
26
|
+
endpoint: string;
|
|
27
|
+
database?: string;
|
|
28
|
+
};
|
|
29
|
+
eventBus?: {
|
|
30
|
+
type: "in-memory" | "redis";
|
|
31
|
+
redisUrl?: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
interface ChangeAnalysisFile {
|
|
35
|
+
path: string;
|
|
36
|
+
status: "added" | "modified" | "deleted" | "renamed";
|
|
37
|
+
}
|
|
38
|
+
interface ChangeAnalysis {
|
|
39
|
+
baseSha: string;
|
|
40
|
+
headSha: string;
|
|
41
|
+
files: ChangeAnalysisFile[];
|
|
42
|
+
}
|
|
43
|
+
interface ImpactedFlow {
|
|
44
|
+
name: string;
|
|
45
|
+
confidence: number;
|
|
46
|
+
riskScore: number;
|
|
47
|
+
priority?: "critical" | "high" | "medium" | "low";
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Shape aligned with the ANALYSIS_COMPLETE event described in docs/cli_architecture.md.
|
|
51
|
+
*/
|
|
52
|
+
interface ImpactReport {
|
|
53
|
+
changeId: string;
|
|
54
|
+
baseSha: string;
|
|
55
|
+
headSha: string;
|
|
56
|
+
flows: ImpactedFlow[];
|
|
57
|
+
changes: ChangeAnalysisFile[];
|
|
58
|
+
createdAt: number;
|
|
59
|
+
}
|
|
60
|
+
interface BootstrapResult {
|
|
61
|
+
projectName: string;
|
|
62
|
+
framework: string;
|
|
63
|
+
flowsInitialized: number;
|
|
64
|
+
personasInitialized: number;
|
|
65
|
+
warnings?: string[];
|
|
66
|
+
}
|
|
67
|
+
interface BootstrapOptions {
|
|
68
|
+
projectDir: string;
|
|
69
|
+
projectName: string;
|
|
70
|
+
framework: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface PerceoEvent<T = any> {
|
|
74
|
+
id: string;
|
|
75
|
+
type: string;
|
|
76
|
+
timestamp: number;
|
|
77
|
+
source: "observer" | "coordinator" | "analytics" | "analyzer" | "graph";
|
|
78
|
+
data: T;
|
|
79
|
+
metadata?: {
|
|
80
|
+
userId?: string;
|
|
81
|
+
projectId?: string;
|
|
82
|
+
environment?: string;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
interface EventBusLike {
|
|
86
|
+
publish<T>(event: PerceoEvent<T>): Promise<void> | void;
|
|
87
|
+
}
|
|
88
|
+
interface FlowGraphClientLike {
|
|
89
|
+
upsertFlows?(flows: unknown[]): Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
interface ObserverEngineDeps {
|
|
92
|
+
eventBus?: EventBusLike;
|
|
93
|
+
flowGraph?: FlowGraphClientLike;
|
|
94
|
+
}
|
|
95
|
+
declare class ObserverEngine {
|
|
96
|
+
private readonly config;
|
|
97
|
+
private readonly deps;
|
|
98
|
+
private readonly apiClient;
|
|
99
|
+
constructor(config: ObserverEngineConfig, deps?: ObserverEngineDeps);
|
|
100
|
+
/**
|
|
101
|
+
* Bootstrap flows/personas for the current project.
|
|
102
|
+
*
|
|
103
|
+
* Delegates to the managed Observer API when configured, otherwise returns
|
|
104
|
+
* a fast local no-op result so init remains non-blocking.
|
|
105
|
+
*/
|
|
106
|
+
bootstrapProject(options: BootstrapOptions): Promise<BootstrapResult>;
|
|
107
|
+
/**
|
|
108
|
+
* Analyze changes between two Git refs and return an ImpactReport.
|
|
109
|
+
*
|
|
110
|
+
* Uses local Git diff to build a ChangeAnalysis, then delegates to the
|
|
111
|
+
* managed Observer API when configured. If no API is configured, returns
|
|
112
|
+
* a minimal, local-only report that still captures the changed files.
|
|
113
|
+
*/
|
|
114
|
+
analyzeChanges(params: {
|
|
115
|
+
baseSha: string;
|
|
116
|
+
headSha: string;
|
|
117
|
+
projectRoot: string;
|
|
118
|
+
}): Promise<ImpactReport>;
|
|
119
|
+
/**
|
|
120
|
+
* Core entry point for watch-mode integration.
|
|
121
|
+
*
|
|
122
|
+
* The CLI is expected to provide a stream of file changes; this method
|
|
123
|
+
* will wire those into the Observer pipeline and return a disposer to
|
|
124
|
+
* stop processing. The detailed implementation will be filled in when
|
|
125
|
+
* watch support is built out.
|
|
126
|
+
*/
|
|
127
|
+
startWatchCore(_options: {
|
|
128
|
+
onChange: (change: {
|
|
129
|
+
path: string;
|
|
130
|
+
type: "add" | "change" | "unlink";
|
|
131
|
+
}) => void | Promise<void>;
|
|
132
|
+
}): Promise<() => Promise<void>>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
interface ObserverApiClientOptions {
|
|
136
|
+
baseUrl: string;
|
|
137
|
+
apiKey?: string;
|
|
138
|
+
}
|
|
139
|
+
declare class ObserverApiClient {
|
|
140
|
+
private readonly baseUrl;
|
|
141
|
+
private readonly apiKey?;
|
|
142
|
+
constructor(options: ObserverApiClientOptions);
|
|
143
|
+
static fromConfig(config: ObserverEngineConfig): ObserverApiClient | null;
|
|
144
|
+
bootstrapProject(options: BootstrapOptions): Promise<BootstrapResult>;
|
|
145
|
+
analyzeChanges(change: ChangeAnalysis): Promise<ImpactReport>;
|
|
146
|
+
private post;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export { type BootstrapOptions, type BootstrapResult, type ChangeAnalysis, type ChangeAnalysisFile, type EventBusLike, type FlowGraphClientLike, type ImpactReport, type ImpactedFlow, ObserverApiClient, type ObserverApiClientOptions, ObserverEngine, type ObserverEngineConfig, type ObserverEngineDeps, type PerceoEvent };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
var ObserverApiClient = class _ObserverApiClient {
|
|
3
|
+
baseUrl;
|
|
4
|
+
apiKey;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
7
|
+
this.apiKey = options.apiKey;
|
|
8
|
+
}
|
|
9
|
+
static fromConfig(config) {
|
|
10
|
+
const baseUrl = config.observer.apiBaseUrl;
|
|
11
|
+
if (!baseUrl) return null;
|
|
12
|
+
return new _ObserverApiClient({
|
|
13
|
+
baseUrl,
|
|
14
|
+
apiKey: config.observer.apiKey
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async bootstrapProject(options) {
|
|
18
|
+
const res = await this.post("/observer/bootstrap", options);
|
|
19
|
+
return res;
|
|
20
|
+
}
|
|
21
|
+
async analyzeChanges(change) {
|
|
22
|
+
const res = await this.post("/observer/analyze", change);
|
|
23
|
+
return res;
|
|
24
|
+
}
|
|
25
|
+
async post(path, body) {
|
|
26
|
+
const url = `${this.baseUrl}${path}`;
|
|
27
|
+
const headers = {
|
|
28
|
+
"Content-Type": "application/json"
|
|
29
|
+
};
|
|
30
|
+
if (this.apiKey) {
|
|
31
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
32
|
+
}
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers,
|
|
36
|
+
body: JSON.stringify(body)
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const text = await response.text().catch(() => "");
|
|
40
|
+
throw new Error(`Observer API request failed (${response.status}): ${text || response.statusText}`);
|
|
41
|
+
}
|
|
42
|
+
return await response.json();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/git.ts
|
|
47
|
+
import { exec as _exec } from "child_process";
|
|
48
|
+
import { promisify } from "util";
|
|
49
|
+
var exec = promisify(_exec);
|
|
50
|
+
async function computeChangeAnalysis(projectRoot, baseSha, headSha) {
|
|
51
|
+
const { stdout } = await exec(`git diff --name-status ${baseSha}...${headSha}`, {
|
|
52
|
+
cwd: projectRoot
|
|
53
|
+
});
|
|
54
|
+
const files = stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
|
|
55
|
+
const [status, filePath] = line.split(/\s+/, 2);
|
|
56
|
+
return {
|
|
57
|
+
path: normalizePath(filePath),
|
|
58
|
+
status: mapStatus(status)
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
baseSha,
|
|
63
|
+
headSha,
|
|
64
|
+
files
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function mapStatus(raw) {
|
|
68
|
+
switch (raw) {
|
|
69
|
+
case "A":
|
|
70
|
+
return "added";
|
|
71
|
+
case "D":
|
|
72
|
+
return "deleted";
|
|
73
|
+
case "R":
|
|
74
|
+
case "R100":
|
|
75
|
+
case "R099":
|
|
76
|
+
return "renamed";
|
|
77
|
+
case "M":
|
|
78
|
+
default:
|
|
79
|
+
return "modified";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function normalizePath(p) {
|
|
83
|
+
if (!p) return "";
|
|
84
|
+
return p.split("\\").join("/");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/engine.ts
|
|
88
|
+
var ObserverEngine = class {
|
|
89
|
+
config;
|
|
90
|
+
deps;
|
|
91
|
+
apiClient;
|
|
92
|
+
constructor(config, deps = {}) {
|
|
93
|
+
this.config = config;
|
|
94
|
+
this.deps = deps;
|
|
95
|
+
this.apiClient = ObserverApiClient.fromConfig(config);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Bootstrap flows/personas for the current project.
|
|
99
|
+
*
|
|
100
|
+
* Delegates to the managed Observer API when configured, otherwise returns
|
|
101
|
+
* a fast local no-op result so init remains non-blocking.
|
|
102
|
+
*/
|
|
103
|
+
async bootstrapProject(options) {
|
|
104
|
+
if (!this.apiClient) {
|
|
105
|
+
return {
|
|
106
|
+
projectName: options.projectName,
|
|
107
|
+
framework: options.framework,
|
|
108
|
+
flowsInitialized: 0,
|
|
109
|
+
personasInitialized: 0,
|
|
110
|
+
warnings: ["Observer managed API is not configured; skipping remote bootstrap."]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const result = await this.apiClient.bootstrapProject(options);
|
|
114
|
+
if (this.deps.flowGraph && Array.isArray(result.flows)) {
|
|
115
|
+
await this.deps.flowGraph.upsertFlows?.(result.flows);
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Analyze changes between two Git refs and return an ImpactReport.
|
|
121
|
+
*
|
|
122
|
+
* Uses local Git diff to build a ChangeAnalysis, then delegates to the
|
|
123
|
+
* managed Observer API when configured. If no API is configured, returns
|
|
124
|
+
* a minimal, local-only report that still captures the changed files.
|
|
125
|
+
*/
|
|
126
|
+
async analyzeChanges(params) {
|
|
127
|
+
const change = await computeChangeAnalysis(params.projectRoot, params.baseSha, params.headSha);
|
|
128
|
+
let report;
|
|
129
|
+
if (this.apiClient) {
|
|
130
|
+
report = await this.apiClient.analyzeChanges(change);
|
|
131
|
+
} else {
|
|
132
|
+
report = {
|
|
133
|
+
changeId: `${params.baseSha}...${params.headSha}`,
|
|
134
|
+
baseSha: params.baseSha,
|
|
135
|
+
headSha: params.headSha,
|
|
136
|
+
flows: [],
|
|
137
|
+
changes: change.files,
|
|
138
|
+
createdAt: Date.now()
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (this.deps.eventBus) {
|
|
142
|
+
const event = {
|
|
143
|
+
id: report.changeId,
|
|
144
|
+
type: "observer.analysis.complete",
|
|
145
|
+
timestamp: report.createdAt,
|
|
146
|
+
source: "observer",
|
|
147
|
+
data: report
|
|
148
|
+
};
|
|
149
|
+
await this.deps.eventBus.publish(event);
|
|
150
|
+
}
|
|
151
|
+
return report;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Core entry point for watch-mode integration.
|
|
155
|
+
*
|
|
156
|
+
* The CLI is expected to provide a stream of file changes; this method
|
|
157
|
+
* will wire those into the Observer pipeline and return a disposer to
|
|
158
|
+
* stop processing. The detailed implementation will be filled in when
|
|
159
|
+
* watch support is built out.
|
|
160
|
+
*/
|
|
161
|
+
async startWatchCore(_options) {
|
|
162
|
+
return async () => {
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
export {
|
|
167
|
+
ObserverApiClient,
|
|
168
|
+
ObserverEngine
|
|
169
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@perceo/observer-engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Observer Engine core used by the Perceo CLI for flow/persona bootstrap and change impact analysis.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"perceo",
|
|
7
|
+
"observer",
|
|
8
|
+
"analysis",
|
|
9
|
+
"flows",
|
|
10
|
+
"ci"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^22.15.3",
|
|
21
|
+
"tsup": "^8.0.1",
|
|
22
|
+
"typescript": "5.9.2",
|
|
23
|
+
"@repo/typescript-config": "0.1.0"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@perceo/perceo": "0.x"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
30
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
31
|
+
"check-types": "tsc --noEmit"
|
|
32
|
+
}
|
|
33
|
+
}
|