open-research-protocol 0.4.14 → 0.4.16
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/AGENT_INTEGRATION.md +50 -0
- package/README.md +273 -144
- package/bin/orp.js +14 -1
- package/cli/orp.py +14846 -9925
- package/docs/AGENT_LOOP.md +13 -0
- package/docs/AGENT_MODES.md +79 -0
- package/docs/CANONICAL_CLI_BOUNDARY.md +15 -0
- package/docs/EXCHANGE.md +94 -0
- package/docs/LAUNCH_KIT.md +107 -0
- package/docs/ORP_HOSTED_WORKSPACE_CONTRACT.md +295 -0
- package/docs/ORP_PUBLIC_LAUNCH_CHECKLIST.md +5 -0
- package/docs/START_HERE.md +567 -0
- package/package.json +4 -2
- package/packages/lifeops-orp/README.md +67 -0
- package/packages/lifeops-orp/package.json +48 -0
- package/packages/lifeops-orp/src/index.d.ts +106 -0
- package/packages/lifeops-orp/src/index.js +7 -0
- package/packages/lifeops-orp/src/mapping.js +309 -0
- package/packages/lifeops-orp/src/workspace.js +108 -0
- package/packages/lifeops-orp/test/orp.test.js +187 -0
- package/packages/orp-workspace-launcher/README.md +82 -0
- package/packages/orp-workspace-launcher/package.json +39 -0
- package/packages/orp-workspace-launcher/src/commands.js +77 -0
- package/packages/orp-workspace-launcher/src/core-plan.js +506 -0
- package/packages/orp-workspace-launcher/src/hosted-state.js +208 -0
- package/packages/orp-workspace-launcher/src/index.js +82 -0
- package/packages/orp-workspace-launcher/src/ledger.js +745 -0
- package/packages/orp-workspace-launcher/src/list.js +488 -0
- package/packages/orp-workspace-launcher/src/orp-command.js +126 -0
- package/packages/orp-workspace-launcher/src/orp.js +912 -0
- package/packages/orp-workspace-launcher/src/registry.js +558 -0
- package/packages/orp-workspace-launcher/src/slot.js +188 -0
- package/packages/orp-workspace-launcher/src/sync.js +363 -0
- package/packages/orp-workspace-launcher/src/tabs.js +166 -0
- package/packages/orp-workspace-launcher/test/commands.test.js +164 -0
- package/packages/orp-workspace-launcher/test/core-plan.test.js +253 -0
- package/packages/orp-workspace-launcher/test/fixtures/smoke-notes.txt +2 -0
- package/packages/orp-workspace-launcher/test/fixtures/workspace-manifest.json +17 -0
- package/packages/orp-workspace-launcher/test/ledger.test.js +244 -0
- package/packages/orp-workspace-launcher/test/list.test.js +299 -0
- package/packages/orp-workspace-launcher/test/orp-command.test.js +44 -0
- package/packages/orp-workspace-launcher/test/orp.test.js +224 -0
- package/packages/orp-workspace-launcher/test/tabs.test.js +168 -0
- package/scripts/orp-kernel-agent-pilot.py +10 -1
- package/scripts/orp-kernel-agent-replication.py +10 -1
- package/scripts/orp-kernel-canonical-continuation.py +10 -1
- package/scripts/orp-kernel-continuation-pilot.py +10 -1
- package/scripts/render-terminal-demo.py +416 -0
- package/spec/v1/exchange-report.schema.json +105 -0
- package/spec/v1/hosted-workspace-event.schema.json +102 -0
- package/spec/v1/hosted-workspace.schema.json +332 -0
- package/spec/v1/workspace.schema.json +108 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export interface OrpCommandInput {
|
|
2
|
+
repoRoot: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface OrpWorkspace {
|
|
7
|
+
repoRoot: string;
|
|
8
|
+
status: Record<string, unknown>;
|
|
9
|
+
frontierState: Record<string, unknown> | null;
|
|
10
|
+
frontierRoadmap: Record<string, unknown> | null;
|
|
11
|
+
frontierChecklist: Record<string, unknown> | null;
|
|
12
|
+
about: Record<string, unknown> | null;
|
|
13
|
+
collectedAt: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface LifeOpsLikeItem {
|
|
17
|
+
id: string;
|
|
18
|
+
kind: "event" | "communication" | "routine" | "task" | "alert" | "document";
|
|
19
|
+
title: string;
|
|
20
|
+
summary?: string;
|
|
21
|
+
priority?: "urgent" | "high" | "normal" | "low";
|
|
22
|
+
organization?: string | null;
|
|
23
|
+
tags?: string[];
|
|
24
|
+
source?: {
|
|
25
|
+
connector?: string;
|
|
26
|
+
id?: string;
|
|
27
|
+
account?: string | null;
|
|
28
|
+
};
|
|
29
|
+
metadata?: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ProjectShareInput {
|
|
33
|
+
name: string;
|
|
34
|
+
summary: string;
|
|
35
|
+
whyNow: string;
|
|
36
|
+
highlights: string[];
|
|
37
|
+
proofPoints: string[];
|
|
38
|
+
links: Array<{ label: string; url: string }>;
|
|
39
|
+
codebases: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export declare function runOrpJson(options: {
|
|
43
|
+
repoRoot: string;
|
|
44
|
+
args: string[];
|
|
45
|
+
orpCommand?: string;
|
|
46
|
+
}): Promise<Record<string, unknown>>;
|
|
47
|
+
|
|
48
|
+
export declare function collectOrpWorkspace(options: {
|
|
49
|
+
repoRoot: string;
|
|
50
|
+
orpCommand?: string;
|
|
51
|
+
includeAbout?: boolean;
|
|
52
|
+
commandRunner?: (input: OrpCommandInput) => Promise<Record<string, unknown>>;
|
|
53
|
+
}): Promise<OrpWorkspace>;
|
|
54
|
+
|
|
55
|
+
export declare function mapOrpWorkspaceToItems(options: {
|
|
56
|
+
workspace: OrpWorkspace;
|
|
57
|
+
projectName?: string;
|
|
58
|
+
organization?: string;
|
|
59
|
+
}): LifeOpsLikeItem[];
|
|
60
|
+
|
|
61
|
+
export declare function createOrpProjectShareInput(options: {
|
|
62
|
+
workspace: OrpWorkspace;
|
|
63
|
+
projectName?: string;
|
|
64
|
+
summary?: string;
|
|
65
|
+
repoUrl?: string;
|
|
66
|
+
liveUrl?: string;
|
|
67
|
+
extraHighlights?: string[];
|
|
68
|
+
extraProofPoints?: string[];
|
|
69
|
+
extraCodebases?: string[];
|
|
70
|
+
}): ProjectShareInput;
|
|
71
|
+
|
|
72
|
+
export declare function buildOrpProjectSharePacket(options: {
|
|
73
|
+
repoRoot: string;
|
|
74
|
+
recipients: Array<Record<string, unknown>>;
|
|
75
|
+
buildProjectSharePacket: (input: Record<string, unknown>) => unknown;
|
|
76
|
+
orpCommand?: string;
|
|
77
|
+
includeAbout?: boolean;
|
|
78
|
+
commandRunner?: (input: OrpCommandInput) => Promise<Record<string, unknown>>;
|
|
79
|
+
projectName?: string;
|
|
80
|
+
summary?: string;
|
|
81
|
+
repoUrl?: string;
|
|
82
|
+
liveUrl?: string;
|
|
83
|
+
extraHighlights?: string[];
|
|
84
|
+
extraProofPoints?: string[];
|
|
85
|
+
extraCodebases?: string[];
|
|
86
|
+
senderName?: string;
|
|
87
|
+
baseTime?: string | Date | number;
|
|
88
|
+
}): Promise<unknown>;
|
|
89
|
+
|
|
90
|
+
export declare function createOrpConnector(options: {
|
|
91
|
+
repoRoot: string;
|
|
92
|
+
orpCommand?: string;
|
|
93
|
+
name?: string;
|
|
94
|
+
projectName?: string;
|
|
95
|
+
organization?: string;
|
|
96
|
+
includeAbout?: boolean;
|
|
97
|
+
commandRunner?: (input: OrpCommandInput) => Promise<Record<string, unknown>>;
|
|
98
|
+
}): {
|
|
99
|
+
name: string;
|
|
100
|
+
pull(): Promise<{
|
|
101
|
+
items: LifeOpsLikeItem[];
|
|
102
|
+
meta: {
|
|
103
|
+
workspace: OrpWorkspace;
|
|
104
|
+
};
|
|
105
|
+
}>;
|
|
106
|
+
};
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { collectOrpWorkspace } from "./workspace.js";
|
|
2
|
+
|
|
3
|
+
function slugify(value) {
|
|
4
|
+
return String(value ?? "")
|
|
5
|
+
.trim()
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
8
|
+
.replace(/^-+|-+$/g, "");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function pushItem(items, item) {
|
|
12
|
+
if (!item) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
items.push(item);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function projectLabel(workspace, projectName) {
|
|
19
|
+
return (
|
|
20
|
+
projectName ||
|
|
21
|
+
workspace.frontierState?.label ||
|
|
22
|
+
workspace.frontierState?.program_id ||
|
|
23
|
+
workspace.status?.repo_root?.split("/").filter(Boolean).pop() ||
|
|
24
|
+
"ORP Project"
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readinessItem(workspace, name, organization) {
|
|
29
|
+
const readiness = workspace.status?.readiness ?? {};
|
|
30
|
+
if (!readiness.local_ready && !readiness.remote_ready) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const scope = readiness.scope || "local_only";
|
|
34
|
+
return {
|
|
35
|
+
id: `orp-ready:${slugify(name)}`,
|
|
36
|
+
kind: "alert",
|
|
37
|
+
title: `${name} is ready to share`,
|
|
38
|
+
summary: `ORP readiness is marked true (${scope}).`,
|
|
39
|
+
priority: "high",
|
|
40
|
+
organization,
|
|
41
|
+
tags: ["orp", "readiness", "share"],
|
|
42
|
+
source: {
|
|
43
|
+
connector: "orp",
|
|
44
|
+
id: "ready",
|
|
45
|
+
},
|
|
46
|
+
metadata: {
|
|
47
|
+
readiness,
|
|
48
|
+
latestRun: workspace.status?.runtime?.last_ready ?? {},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function frontierItem(workspace, name, organization) {
|
|
54
|
+
const frontier = workspace.frontierState ?? {};
|
|
55
|
+
if (!frontier.active_milestone && !frontier.next_action) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const milestone = frontier.active_milestone || frontier.active_version || "frontier";
|
|
59
|
+
const title = frontier.next_action
|
|
60
|
+
? `ORP frontier: ${frontier.next_action}`
|
|
61
|
+
: `Advance frontier milestone ${milestone}`;
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
id: `orp-frontier:${slugify(name)}:${slugify(milestone) || "state"}`,
|
|
65
|
+
kind: "task",
|
|
66
|
+
title,
|
|
67
|
+
summary: [
|
|
68
|
+
frontier.active_version ? `version ${frontier.active_version}` : "",
|
|
69
|
+
frontier.active_milestone ? `milestone ${frontier.active_milestone}` : "",
|
|
70
|
+
frontier.active_phase ? `phase ${frontier.active_phase}` : "",
|
|
71
|
+
]
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
.join(" / "),
|
|
74
|
+
priority: "high",
|
|
75
|
+
organization,
|
|
76
|
+
tags: ["orp", "frontier"],
|
|
77
|
+
source: {
|
|
78
|
+
connector: "orp",
|
|
79
|
+
id: "frontier-state",
|
|
80
|
+
},
|
|
81
|
+
metadata: {
|
|
82
|
+
frontier,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function nextActionItems(workspace, name, organization) {
|
|
88
|
+
const actions = Array.isArray(workspace.status?.next_actions) ? workspace.status.next_actions : [];
|
|
89
|
+
return actions.slice(0, 5).map((action, index) => ({
|
|
90
|
+
id: `orp-next:${slugify(name)}:${index + 1}`,
|
|
91
|
+
kind: "task",
|
|
92
|
+
title: `ORP next action: ${action}`,
|
|
93
|
+
summary: `Suggested by ORP status for ${name}.`,
|
|
94
|
+
priority: index === 0 ? "high" : "normal",
|
|
95
|
+
organization,
|
|
96
|
+
tags: ["orp", "next-action"],
|
|
97
|
+
source: {
|
|
98
|
+
connector: "orp",
|
|
99
|
+
id: `next-action-${index + 1}`,
|
|
100
|
+
},
|
|
101
|
+
metadata: {
|
|
102
|
+
action,
|
|
103
|
+
},
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function checklistItems(workspace, name, organization) {
|
|
108
|
+
const exact = Array.isArray(workspace.frontierChecklist?.exact)
|
|
109
|
+
? workspace.frontierChecklist.exact
|
|
110
|
+
: [];
|
|
111
|
+
return exact.slice(0, 10).map((row, index) => {
|
|
112
|
+
const milestone = row.milestone_id || row.milestone || `item-${index + 1}`;
|
|
113
|
+
const label = row.label || row.phase_label || row.next_action || milestone;
|
|
114
|
+
return {
|
|
115
|
+
id: `orp-checklist:${slugify(name)}:${slugify(milestone)}`,
|
|
116
|
+
kind: "task",
|
|
117
|
+
title: `ORP checklist: ${label}`,
|
|
118
|
+
summary: [
|
|
119
|
+
row.version_id ? `version ${row.version_id}` : "",
|
|
120
|
+
row.milestone_id ? `milestone ${row.milestone_id}` : "",
|
|
121
|
+
row.phase_id ? `phase ${row.phase_id}` : "",
|
|
122
|
+
]
|
|
123
|
+
.filter(Boolean)
|
|
124
|
+
.join(" / "),
|
|
125
|
+
priority: "normal",
|
|
126
|
+
organization,
|
|
127
|
+
tags: ["orp", "checklist"],
|
|
128
|
+
source: {
|
|
129
|
+
connector: "orp",
|
|
130
|
+
id: `checklist-${index + 1}`,
|
|
131
|
+
},
|
|
132
|
+
metadata: {
|
|
133
|
+
checklistRow: row,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function validationAlert(workspace, name, organization) {
|
|
140
|
+
const latestRun = workspace.status?.runtime?.latest_run ?? {};
|
|
141
|
+
const overall = latestRun.overall || "";
|
|
142
|
+
if (!overall || overall === "PASS") {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
id: `orp-validation:${slugify(name)}`,
|
|
147
|
+
kind: "alert",
|
|
148
|
+
title: `${name} has a non-passing ORP validation state`,
|
|
149
|
+
summary: `Latest ORP run overall status: ${overall}.`,
|
|
150
|
+
priority: "urgent",
|
|
151
|
+
organization,
|
|
152
|
+
tags: ["orp", "validation"],
|
|
153
|
+
source: {
|
|
154
|
+
connector: "orp",
|
|
155
|
+
id: "validation",
|
|
156
|
+
},
|
|
157
|
+
metadata: {
|
|
158
|
+
latestRun,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function mapOrpWorkspaceToItems({
|
|
164
|
+
workspace,
|
|
165
|
+
projectName,
|
|
166
|
+
organization = "ORP",
|
|
167
|
+
} = {}) {
|
|
168
|
+
if (!workspace || typeof workspace !== "object") {
|
|
169
|
+
throw new Error("mapOrpWorkspaceToItems requires a workspace object.");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const name = projectLabel(workspace, projectName);
|
|
173
|
+
const items = [];
|
|
174
|
+
|
|
175
|
+
pushItem(items, readinessItem(workspace, name, organization));
|
|
176
|
+
pushItem(items, frontierItem(workspace, name, organization));
|
|
177
|
+
pushItem(items, validationAlert(workspace, name, organization));
|
|
178
|
+
|
|
179
|
+
items.push(...nextActionItems(workspace, name, organization));
|
|
180
|
+
items.push(...checklistItems(workspace, name, organization));
|
|
181
|
+
|
|
182
|
+
return items;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function createOrpProjectShareInput({
|
|
186
|
+
workspace,
|
|
187
|
+
projectName,
|
|
188
|
+
summary = "",
|
|
189
|
+
repoUrl = "",
|
|
190
|
+
liveUrl = "",
|
|
191
|
+
extraHighlights = [],
|
|
192
|
+
extraProofPoints = [],
|
|
193
|
+
extraCodebases = [],
|
|
194
|
+
} = {}) {
|
|
195
|
+
if (!workspace || typeof workspace !== "object") {
|
|
196
|
+
throw new Error("createOrpProjectShareInput requires a workspace object.");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const name = projectLabel(workspace, projectName);
|
|
200
|
+
const readiness = workspace.status?.readiness ?? {};
|
|
201
|
+
const frontier = workspace.frontierState ?? {};
|
|
202
|
+
const latestRun = workspace.status?.runtime?.latest_run ?? {};
|
|
203
|
+
|
|
204
|
+
const whyNow = readiness.local_ready || readiness.remote_ready
|
|
205
|
+
? "ORP marks this repo as ready, so this is a strong moment to share it with selected collaborators."
|
|
206
|
+
: "The ORP frontier is active and concrete next actions are already defined, so this is a good moment to share progress in a structured way.";
|
|
207
|
+
|
|
208
|
+
const highlights = [
|
|
209
|
+
frontier.next_action ? `Current frontier next action: ${frontier.next_action}` : "",
|
|
210
|
+
frontier.active_milestone ? `Active frontier milestone: ${frontier.active_milestone}` : "",
|
|
211
|
+
Array.isArray(workspace.status?.next_actions) && workspace.status.next_actions[0]
|
|
212
|
+
? `Immediate ORP next action: ${workspace.status.next_actions[0]}`
|
|
213
|
+
: "",
|
|
214
|
+
...extraHighlights,
|
|
215
|
+
].filter(Boolean);
|
|
216
|
+
|
|
217
|
+
const proofPoints = [
|
|
218
|
+
latestRun.overall ? `Latest ORP validation run: ${latestRun.overall}` : "",
|
|
219
|
+
readiness.local_ready ? "Local ORP readiness is marked true." : "",
|
|
220
|
+
readiness.remote_ready ? "Remote ORP readiness is marked true." : "",
|
|
221
|
+
workspace.status?.validation?.checkpoint_after_validation
|
|
222
|
+
? "Checkpoint captured after validation."
|
|
223
|
+
: "",
|
|
224
|
+
...extraProofPoints,
|
|
225
|
+
].filter(Boolean);
|
|
226
|
+
|
|
227
|
+
const links = [
|
|
228
|
+
repoUrl ? { label: "Repository", url: repoUrl } : null,
|
|
229
|
+
liveUrl ? { label: "Live URL", url: liveUrl } : null,
|
|
230
|
+
].filter(Boolean);
|
|
231
|
+
|
|
232
|
+
const codebases = [
|
|
233
|
+
"open-research-protocol",
|
|
234
|
+
...extraCodebases,
|
|
235
|
+
].filter(Boolean);
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
name,
|
|
239
|
+
summary: summary || `An ORP-governed project with machine-readable frontier, readiness, and workflow state.`,
|
|
240
|
+
whyNow,
|
|
241
|
+
highlights,
|
|
242
|
+
proofPoints,
|
|
243
|
+
links,
|
|
244
|
+
codebases,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function buildOrpProjectSharePacket({
|
|
249
|
+
repoRoot,
|
|
250
|
+
buildProjectSharePacket,
|
|
251
|
+
recipients,
|
|
252
|
+
orpCommand = "orp",
|
|
253
|
+
includeAbout = true,
|
|
254
|
+
commandRunner,
|
|
255
|
+
...shareOptions
|
|
256
|
+
} = {}) {
|
|
257
|
+
if (typeof buildProjectSharePacket !== "function") {
|
|
258
|
+
throw new Error("buildOrpProjectSharePacket requires a buildProjectSharePacket function.");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const workspace = await collectOrpWorkspace({
|
|
262
|
+
repoRoot,
|
|
263
|
+
orpCommand,
|
|
264
|
+
includeAbout,
|
|
265
|
+
commandRunner,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
return buildProjectSharePacket({
|
|
269
|
+
project: createOrpProjectShareInput({
|
|
270
|
+
workspace,
|
|
271
|
+
...shareOptions,
|
|
272
|
+
}),
|
|
273
|
+
recipients,
|
|
274
|
+
senderName: shareOptions.senderName,
|
|
275
|
+
baseTime: shareOptions.baseTime,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function createOrpConnector({
|
|
280
|
+
repoRoot,
|
|
281
|
+
orpCommand = "orp",
|
|
282
|
+
name = "orp",
|
|
283
|
+
projectName,
|
|
284
|
+
organization = "ORP",
|
|
285
|
+
includeAbout = false,
|
|
286
|
+
commandRunner,
|
|
287
|
+
} = {}) {
|
|
288
|
+
return {
|
|
289
|
+
name,
|
|
290
|
+
async pull() {
|
|
291
|
+
const workspace = await collectOrpWorkspace({
|
|
292
|
+
repoRoot,
|
|
293
|
+
orpCommand,
|
|
294
|
+
includeAbout,
|
|
295
|
+
commandRunner,
|
|
296
|
+
});
|
|
297
|
+
return {
|
|
298
|
+
items: mapOrpWorkspaceToItems({
|
|
299
|
+
workspace,
|
|
300
|
+
projectName,
|
|
301
|
+
organization,
|
|
302
|
+
}),
|
|
303
|
+
meta: {
|
|
304
|
+
workspace,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
|
|
6
|
+
function withJsonFlag(args = []) {
|
|
7
|
+
return args.includes("--json") ? args : [...args, "--json"];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function normalizeCommandError(error) {
|
|
11
|
+
if (error instanceof Error) {
|
|
12
|
+
return error;
|
|
13
|
+
}
|
|
14
|
+
return new Error(String(error));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function runOrpJson({
|
|
18
|
+
repoRoot,
|
|
19
|
+
args = [],
|
|
20
|
+
orpCommand = "orp",
|
|
21
|
+
} = {}) {
|
|
22
|
+
if (!repoRoot) {
|
|
23
|
+
throw new Error("runOrpJson requires a repoRoot.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const result = await execFileAsync(orpCommand, withJsonFlag(args), {
|
|
27
|
+
cwd: repoRoot,
|
|
28
|
+
env: process.env,
|
|
29
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const stdout = String(result.stdout ?? "").trim();
|
|
33
|
+
if (!stdout) {
|
|
34
|
+
throw new Error(`ORP command returned no JSON output: ${args.join(" ")}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(stdout);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Failed to parse ORP JSON output for "${args.join(" ")}": ${
|
|
42
|
+
normalizeCommandError(error).message
|
|
43
|
+
}`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function tryCommand(commandRunner, input) {
|
|
49
|
+
try {
|
|
50
|
+
return await commandRunner(input);
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function collectOrpWorkspace({
|
|
57
|
+
repoRoot,
|
|
58
|
+
orpCommand = "orp",
|
|
59
|
+
includeAbout = true,
|
|
60
|
+
commandRunner,
|
|
61
|
+
} = {}) {
|
|
62
|
+
if (!repoRoot) {
|
|
63
|
+
throw new Error("collectOrpWorkspace requires a repoRoot.");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const runner =
|
|
67
|
+
commandRunner ??
|
|
68
|
+
((input) =>
|
|
69
|
+
runOrpJson({
|
|
70
|
+
repoRoot: input.repoRoot,
|
|
71
|
+
args: input.args,
|
|
72
|
+
orpCommand,
|
|
73
|
+
}));
|
|
74
|
+
|
|
75
|
+
const status = await runner({
|
|
76
|
+
repoRoot,
|
|
77
|
+
args: ["status"],
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const frontierState = await tryCommand(runner, {
|
|
81
|
+
repoRoot,
|
|
82
|
+
args: ["frontier", "state"],
|
|
83
|
+
});
|
|
84
|
+
const frontierRoadmap = await tryCommand(runner, {
|
|
85
|
+
repoRoot,
|
|
86
|
+
args: ["frontier", "roadmap"],
|
|
87
|
+
});
|
|
88
|
+
const frontierChecklist = await tryCommand(runner, {
|
|
89
|
+
repoRoot,
|
|
90
|
+
args: ["frontier", "checklist"],
|
|
91
|
+
});
|
|
92
|
+
const about = includeAbout
|
|
93
|
+
? await tryCommand(runner, {
|
|
94
|
+
repoRoot,
|
|
95
|
+
args: ["about"],
|
|
96
|
+
})
|
|
97
|
+
: null;
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
repoRoot,
|
|
101
|
+
status,
|
|
102
|
+
frontierState,
|
|
103
|
+
frontierRoadmap,
|
|
104
|
+
frontierChecklist,
|
|
105
|
+
about,
|
|
106
|
+
collectedAt: new Date().toISOString(),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
buildOrpProjectSharePacket,
|
|
6
|
+
collectOrpWorkspace,
|
|
7
|
+
createOrpConnector,
|
|
8
|
+
createOrpProjectShareInput,
|
|
9
|
+
mapOrpWorkspaceToItems,
|
|
10
|
+
} from "../src/index.js";
|
|
11
|
+
|
|
12
|
+
function createWorkspace() {
|
|
13
|
+
return {
|
|
14
|
+
repoRoot: "/tmp/demo-repo",
|
|
15
|
+
collectedAt: "2026-03-26T00:00:00.000Z",
|
|
16
|
+
status: {
|
|
17
|
+
next_actions: [
|
|
18
|
+
"orp checkpoint create -m \"capture passing validation\" --json",
|
|
19
|
+
"orp ready --json",
|
|
20
|
+
],
|
|
21
|
+
readiness: {
|
|
22
|
+
scope: "local_only",
|
|
23
|
+
local_ready: true,
|
|
24
|
+
remote_ready: false,
|
|
25
|
+
},
|
|
26
|
+
runtime: {
|
|
27
|
+
latest_run: {
|
|
28
|
+
overall: "PASS",
|
|
29
|
+
},
|
|
30
|
+
last_ready: {
|
|
31
|
+
run_id: "run-123",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
validation: {
|
|
35
|
+
checkpoint_after_validation: true,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
frontierState: {
|
|
39
|
+
program_id: "lifeops",
|
|
40
|
+
label: "Life Ops",
|
|
41
|
+
active_version: "v1",
|
|
42
|
+
active_milestone: "v1.2",
|
|
43
|
+
active_phase: "395",
|
|
44
|
+
next_action: "execute phase 395",
|
|
45
|
+
},
|
|
46
|
+
frontierRoadmap: {
|
|
47
|
+
active_milestone: "v1.2",
|
|
48
|
+
},
|
|
49
|
+
frontierChecklist: {
|
|
50
|
+
exact: [
|
|
51
|
+
{
|
|
52
|
+
version_id: "v1",
|
|
53
|
+
milestone_id: "v1.2",
|
|
54
|
+
phase_id: "395",
|
|
55
|
+
label: "Execute phase 395",
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
about: {
|
|
60
|
+
tool: {
|
|
61
|
+
name: "orp",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
test("collectOrpWorkspace gathers core ORP json surfaces", async () => {
|
|
68
|
+
const calls = [];
|
|
69
|
+
const workspace = await collectOrpWorkspace({
|
|
70
|
+
repoRoot: "/tmp/demo-repo",
|
|
71
|
+
commandRunner: async ({ args }) => {
|
|
72
|
+
calls.push(args.join(" "));
|
|
73
|
+
const [first, second] = args;
|
|
74
|
+
if (first === "status") {
|
|
75
|
+
return createWorkspace().status;
|
|
76
|
+
}
|
|
77
|
+
if (first === "frontier" && second === "state") {
|
|
78
|
+
return createWorkspace().frontierState;
|
|
79
|
+
}
|
|
80
|
+
if (first === "frontier" && second === "roadmap") {
|
|
81
|
+
return createWorkspace().frontierRoadmap;
|
|
82
|
+
}
|
|
83
|
+
if (first === "frontier" && second === "checklist") {
|
|
84
|
+
return createWorkspace().frontierChecklist;
|
|
85
|
+
}
|
|
86
|
+
if (first === "about") {
|
|
87
|
+
return createWorkspace().about;
|
|
88
|
+
}
|
|
89
|
+
throw new Error(`unexpected args: ${args.join(" ")}`);
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
assert.equal(calls[0], "status");
|
|
94
|
+
assert.equal(workspace.frontierState.active_milestone, "v1.2");
|
|
95
|
+
assert.equal(workspace.about.tool.name, "orp");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("mapOrpWorkspaceToItems creates readiness, frontier, and checklist items", () => {
|
|
99
|
+
const items = mapOrpWorkspaceToItems({
|
|
100
|
+
workspace: createWorkspace(),
|
|
101
|
+
projectName: "Life Ops Research",
|
|
102
|
+
organization: "ORP",
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
assert.ok(items.some((item) => item.title === "Life Ops Research is ready to share"));
|
|
106
|
+
assert.ok(items.some((item) => item.title.includes("ORP frontier: execute phase 395")));
|
|
107
|
+
assert.ok(items.some((item) => item.title.includes("ORP checklist: Execute phase 395")));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("createOrpProjectShareInput derives share context from ORP workspace", () => {
|
|
111
|
+
const shareInput = createOrpProjectShareInput({
|
|
112
|
+
workspace: createWorkspace(),
|
|
113
|
+
projectName: "Life Ops Research",
|
|
114
|
+
repoUrl: "https://github.com/example/repo",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
assert.equal(shareInput.name, "Life Ops Research");
|
|
118
|
+
assert.match(shareInput.whyNow, /ready/i);
|
|
119
|
+
assert.ok(shareInput.highlights.some((line) => line.includes("execute phase 395")));
|
|
120
|
+
assert.ok(shareInput.proofPoints.some((line) => line.includes("PASS")));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("buildOrpProjectSharePacket delegates to injected Life Ops share builder", async () => {
|
|
124
|
+
let captured = null;
|
|
125
|
+
const result = await buildOrpProjectSharePacket({
|
|
126
|
+
repoRoot: "/tmp/demo-repo",
|
|
127
|
+
projectName: "Life Ops Research",
|
|
128
|
+
recipients: [{ email: "alicia@example.com" }],
|
|
129
|
+
buildProjectSharePacket(input) {
|
|
130
|
+
captured = input;
|
|
131
|
+
return { ok: true, input };
|
|
132
|
+
},
|
|
133
|
+
orpCommand: "orp",
|
|
134
|
+
includeAbout: false,
|
|
135
|
+
commandRunner: async ({ args }) => {
|
|
136
|
+
const [first, second] = args;
|
|
137
|
+
if (first === "status") {
|
|
138
|
+
return createWorkspace().status;
|
|
139
|
+
}
|
|
140
|
+
if (first === "frontier" && second === "state") {
|
|
141
|
+
return createWorkspace().frontierState;
|
|
142
|
+
}
|
|
143
|
+
if (first === "frontier" && second === "roadmap") {
|
|
144
|
+
return createWorkspace().frontierRoadmap;
|
|
145
|
+
}
|
|
146
|
+
if (first === "frontier" && second === "checklist") {
|
|
147
|
+
return createWorkspace().frontierChecklist;
|
|
148
|
+
}
|
|
149
|
+
throw new Error(`unexpected args: ${args.join(" ")}`);
|
|
150
|
+
},
|
|
151
|
+
extraHighlights: ["Agent-first workflow"],
|
|
152
|
+
extraCodebases: ["@lifeops/core"],
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
assert.equal(result.ok, true);
|
|
156
|
+
assert.equal(captured.project.name, "Life Ops Research");
|
|
157
|
+
assert.ok(captured.project.codebases.includes("@lifeops/core"));
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("createOrpConnector exposes a Life Ops-compatible pull contract", async () => {
|
|
161
|
+
const connector = createOrpConnector({
|
|
162
|
+
repoRoot: "/tmp/demo-repo",
|
|
163
|
+
projectName: "Life Ops Research",
|
|
164
|
+
includeAbout: false,
|
|
165
|
+
commandRunner: async ({ args }) => {
|
|
166
|
+
const [first, second] = args;
|
|
167
|
+
if (first === "status") {
|
|
168
|
+
return createWorkspace().status;
|
|
169
|
+
}
|
|
170
|
+
if (first === "frontier" && second === "state") {
|
|
171
|
+
return createWorkspace().frontierState;
|
|
172
|
+
}
|
|
173
|
+
if (first === "frontier" && second === "roadmap") {
|
|
174
|
+
return createWorkspace().frontierRoadmap;
|
|
175
|
+
}
|
|
176
|
+
if (first === "frontier" && second === "checklist") {
|
|
177
|
+
return createWorkspace().frontierChecklist;
|
|
178
|
+
}
|
|
179
|
+
throw new Error(`unexpected args: ${args.join(" ")}`);
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const payload = await connector.pull();
|
|
184
|
+
assert.ok(Array.isArray(payload.items));
|
|
185
|
+
assert.ok(payload.items.length >= 3);
|
|
186
|
+
assert.equal(payload.meta.workspace.repoRoot, "/tmp/demo-repo");
|
|
187
|
+
});
|