@phyzess/pappra 0.1.0-beta.2 → 0.1.0-beta.4

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.
Files changed (36) hide show
  1. package/README.md +24 -7
  2. package/dist/app/doctor.js +2 -2
  3. package/dist/app/init.d.ts +28 -0
  4. package/dist/app/init.js +419 -88
  5. package/dist/cli/commands/docs.d.ts +1 -0
  6. package/dist/cli/commands/docs.js +104 -3
  7. package/dist/cli/commands/host.d.ts +3 -1
  8. package/dist/cli/commands/host.js +50 -2
  9. package/dist/cli/commands/init.d.ts +8 -0
  10. package/dist/cli/commands/init.js +186 -9
  11. package/dist/cli/commands/runtime.d.ts +14 -0
  12. package/dist/cli/commands/runtime.js +263 -0
  13. package/dist/cli/run.js +134 -7
  14. package/dist/hosts/detect.d.ts +1 -0
  15. package/dist/hosts/detect.js +1 -1
  16. package/dist/hosts/integration/bundle-export.d.ts +22 -0
  17. package/dist/hosts/integration/bundle-export.js +114 -0
  18. package/dist/hosts/integration/bundle.d.ts +4 -1
  19. package/dist/hosts/integration/bundle.js +20 -2
  20. package/dist/hosts/integration/pappra-extension-runtime.mjs +24605 -0
  21. package/dist/hosts/integration/pappra-hook.mjs +24723 -0
  22. package/dist/hosts/integration/service.d.ts +8 -0
  23. package/dist/hosts/integration/service.js +62 -30
  24. package/dist/hosts/integration/templates.d.ts +3 -3
  25. package/dist/hosts/integration/templates.js +15 -10
  26. package/dist/hosts/pilot.d.ts +77 -77
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/install/runtime.d.ts +1 -1
  30. package/dist/install/runtime.js +117 -10
  31. package/dist/package-info.d.ts +2 -0
  32. package/dist/package-info.js +6 -0
  33. package/dist/schemas/host.js +4 -2
  34. package/dist/schemas/install.d.ts +14 -0
  35. package/dist/schemas/install.js +8 -0
  36. package/package.json +8 -2
@@ -26,13 +26,8 @@ export declare function readClaudeSessionId(file: string): string | null;
26
26
  export declare function readPiSessionId(file: string): string | null;
27
27
  export declare function runNativePilot({ harness, profile, cwd, promptFile, evidenceDir, sessionId, resume, timeoutMs, maxBudgetUsd, outputName, phase, trustProject, extraDirs, clock, assignmentFile, capabilitiesFile, taskRoot, maxAgeMinutes, launchKind, discovery, }: PilotOptions): Promise<{
28
28
  reservation?: never;
29
- settlement?: never;
30
- reservation_id?: never;
31
29
  refused: boolean;
32
30
  admission: {
33
- owner_epoch?: never;
34
- detail?: never;
35
- state?: never;
36
31
  ok: false;
37
32
  reason: string;
38
33
  checks: Record<string, unknown>[];
@@ -41,6 +36,10 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
41
36
  required_skills?: never;
42
37
  required_tools?: never;
43
38
  preflight?: never;
39
+ state?: never;
40
+ detail?: never;
41
+ state_revision?: never;
42
+ owner_epoch?: never;
44
43
  attempt_state?: never;
45
44
  resources?: never;
46
45
  mode?: never;
@@ -51,12 +50,7 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
51
50
  capability_evidence?: never;
52
51
  assignment_digest?: never;
53
52
  input_revision?: never;
54
- state_revision?: never;
55
53
  } | {
56
- expected?: never;
57
- actual?: never;
58
- owner_epoch?: never;
59
- state?: never;
60
54
  ok: false;
61
55
  reason: string;
62
56
  checks: Record<string, unknown>[];
@@ -64,6 +58,11 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
64
58
  required_skills?: never;
65
59
  required_tools?: never;
66
60
  preflight?: never;
61
+ state?: never;
62
+ expected?: never;
63
+ actual?: never;
64
+ state_revision?: never;
65
+ owner_epoch?: never;
67
66
  attempt_state?: never;
68
67
  resources?: never;
69
68
  mode?: never;
@@ -74,19 +73,19 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
74
73
  capability_evidence?: never;
75
74
  assignment_digest?: never;
76
75
  input_revision?: never;
77
- state_revision?: never;
78
76
  } | {
79
- expected?: never;
80
- actual?: never;
81
- owner_epoch?: never;
82
- detail?: never;
83
- state?: never;
84
77
  ok: false;
85
78
  reason: string;
86
79
  checks: Record<string, unknown>[];
87
80
  required_skills?: never;
88
81
  required_tools?: never;
89
82
  preflight?: never;
83
+ state?: never;
84
+ detail?: never;
85
+ expected?: never;
86
+ actual?: never;
87
+ state_revision?: never;
88
+ owner_epoch?: never;
90
89
  attempt_state?: never;
91
90
  resources?: never;
92
91
  mode?: never;
@@ -97,11 +96,7 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
97
96
  capability_evidence?: never;
98
97
  assignment_digest?: never;
99
98
  input_revision?: never;
100
- state_revision?: never;
101
99
  } | {
102
- owner_epoch?: never;
103
- detail?: never;
104
- state?: never;
105
100
  ok: false;
106
101
  reason: string;
107
102
  checks: Record<string, unknown>[];
@@ -110,6 +105,10 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
110
105
  required_skills?: never;
111
106
  required_tools?: never;
112
107
  preflight?: never;
108
+ state?: never;
109
+ detail?: never;
110
+ state_revision?: never;
111
+ owner_epoch?: never;
113
112
  attempt_state?: never;
114
113
  resources?: never;
115
114
  mode?: never;
@@ -120,11 +119,7 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
120
119
  capability_evidence?: never;
121
120
  assignment_digest?: never;
122
121
  input_revision?: never;
123
- state_revision?: never;
124
122
  } | {
125
- owner_epoch?: never;
126
- detail?: never;
127
- state?: never;
128
123
  ok: false;
129
124
  reason: string;
130
125
  checks: Record<string, unknown>[];
@@ -138,6 +133,10 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
138
133
  required_skills?: never;
139
134
  required_tools?: never;
140
135
  preflight?: never;
136
+ state?: never;
137
+ detail?: never;
138
+ state_revision?: never;
139
+ owner_epoch?: never;
141
140
  attempt_state?: never;
142
141
  resources?: never;
143
142
  mode?: never;
@@ -148,10 +147,7 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
148
147
  capability_evidence?: never;
149
148
  assignment_digest?: never;
150
149
  input_revision?: never;
151
- state_revision?: never;
152
150
  } | {
153
- owner_epoch?: never;
154
- state?: never;
155
151
  ok: false;
156
152
  reason: string;
157
153
  checks: Record<string, unknown>[];
@@ -161,6 +157,9 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
161
157
  required_skills?: never;
162
158
  required_tools?: never;
163
159
  preflight?: never;
160
+ state?: never;
161
+ state_revision?: never;
162
+ owner_epoch?: never;
164
163
  attempt_state?: never;
165
164
  resources?: never;
166
165
  mode?: never;
@@ -171,19 +170,19 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
171
170
  capability_evidence?: never;
172
171
  assignment_digest?: never;
173
172
  input_revision?: never;
174
- state_revision?: never;
175
173
  } | {
176
- expected?: never;
177
- actual?: never;
178
- owner_epoch?: never;
179
- detail?: never;
180
- state?: never;
181
174
  ok: false;
182
175
  reason: string;
183
176
  checks: Record<string, unknown>[];
184
177
  required_skills: number;
185
178
  required_tools: number;
186
179
  preflight?: never;
180
+ state?: never;
181
+ detail?: never;
182
+ expected?: never;
183
+ actual?: never;
184
+ state_revision?: never;
185
+ owner_epoch?: never;
187
186
  attempt_state?: never;
188
187
  resources?: never;
189
188
  mode?: never;
@@ -194,13 +193,7 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
194
193
  capability_evidence?: never;
195
194
  assignment_digest?: never;
196
195
  input_revision?: never;
197
- state_revision?: never;
198
196
  } | {
199
- expected?: never;
200
- actual?: never;
201
- owner_epoch?: never;
202
- detail?: never;
203
- state?: never;
204
197
  required_skills?: never;
205
198
  required_tools?: never;
206
199
  ok: false;
@@ -213,9 +206,9 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
213
206
  warnings: never[];
214
207
  task_id?: never;
215
208
  assignment_id?: never;
209
+ attempt?: never;
216
210
  checked_at?: never;
217
211
  limits?: never;
218
- attempt?: never;
219
212
  } | {
220
213
  status: string;
221
214
  task_id: string;
@@ -227,6 +220,12 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
227
220
  warnings: import("../collab/preflight.js").Check[];
228
221
  limits: string[];
229
222
  };
223
+ state?: never;
224
+ detail?: never;
225
+ expected?: never;
226
+ actual?: never;
227
+ state_revision?: never;
228
+ owner_epoch?: never;
230
229
  attempt_state?: never;
231
230
  resources?: never;
232
231
  mode?: never;
@@ -237,12 +236,7 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
237
236
  capability_evidence?: never;
238
237
  assignment_digest?: never;
239
238
  input_revision?: never;
240
- state_revision?: never;
241
239
  } | {
242
- expected?: never;
243
- actual?: never;
244
- owner_epoch?: never;
245
- detail?: never;
246
240
  required_skills?: never;
247
241
  required_tools?: never;
248
242
  preflight?: never;
@@ -250,6 +244,11 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
250
244
  reason: string;
251
245
  checks: Record<string, unknown>[];
252
246
  state: string;
247
+ detail?: never;
248
+ expected?: never;
249
+ actual?: never;
250
+ state_revision?: never;
251
+ owner_epoch?: never;
253
252
  attempt_state?: never;
254
253
  resources?: never;
255
254
  mode?: never;
@@ -260,19 +259,19 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
260
259
  capability_evidence?: never;
261
260
  assignment_digest?: never;
262
261
  input_revision?: never;
263
- state_revision?: never;
264
262
  } | {
265
- owner_epoch?: never;
266
- detail?: never;
267
- state?: never;
268
263
  required_skills?: never;
269
264
  required_tools?: never;
270
265
  preflight?: never;
266
+ state?: never;
271
267
  ok: false;
272
268
  reason: string;
273
269
  checks: Record<string, unknown>[];
274
270
  expected: (string | null)[];
275
271
  actual: string;
272
+ detail?: never;
273
+ state_revision?: never;
274
+ owner_epoch?: never;
276
275
  attempt_state?: never;
277
276
  resources?: never;
278
277
  mode?: never;
@@ -283,20 +282,20 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
283
282
  capability_evidence?: never;
284
283
  assignment_digest?: never;
285
284
  input_revision?: never;
286
- state_revision?: never;
287
285
  } | {
288
- expected?: never;
289
- actual?: never;
290
- owner_epoch?: never;
291
- state?: never;
292
286
  required_skills?: never;
293
287
  required_tools?: never;
294
288
  preflight?: never;
289
+ state?: never;
295
290
  ok: false;
296
291
  reason: string;
297
292
  checks: Record<string, unknown>[];
298
293
  resources: Record<string, unknown>[];
299
294
  detail: string;
295
+ expected?: never;
296
+ actual?: never;
297
+ state_revision?: never;
298
+ owner_epoch?: never;
300
299
  attempt_state?: never;
301
300
  mode?: never;
302
301
  launch_kind?: never;
@@ -306,20 +305,20 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
306
305
  capability_evidence?: never;
307
306
  assignment_digest?: never;
308
307
  input_revision?: never;
309
- state_revision?: never;
310
308
  } | {
311
- owner_epoch?: never;
312
- detail?: never;
313
- state?: never;
314
309
  required_skills?: never;
315
310
  required_tools?: never;
316
311
  preflight?: never;
312
+ state?: never;
313
+ detail?: never;
317
314
  ok: false;
318
315
  reason: string;
319
316
  checks: Record<string, unknown>[];
320
317
  resources: Record<string, unknown>[];
321
318
  expected: number | undefined;
322
319
  actual: number;
320
+ state_revision?: never;
321
+ owner_epoch?: never;
323
322
  attempt_state?: never;
324
323
  mode?: never;
325
324
  launch_kind?: never;
@@ -329,20 +328,20 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
329
328
  capability_evidence?: never;
330
329
  assignment_digest?: never;
331
330
  input_revision?: never;
332
- state_revision?: never;
333
331
  } | {
334
- expected?: never;
335
- actual?: never;
336
- owner_epoch?: never;
337
- detail?: never;
338
- state?: never;
339
332
  required_skills?: never;
340
333
  required_tools?: never;
341
334
  preflight?: never;
335
+ state?: never;
336
+ detail?: never;
337
+ expected?: never;
338
+ actual?: never;
342
339
  ok: false;
343
340
  reason: string;
344
341
  checks: Record<string, unknown>[];
345
342
  resources: Record<string, unknown>[];
343
+ state_revision?: never;
344
+ owner_epoch?: never;
346
345
  attempt_state?: never;
347
346
  mode?: never;
348
347
  launch_kind?: never;
@@ -352,7 +351,6 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
352
351
  capability_evidence?: never;
353
352
  assignment_digest?: never;
354
353
  input_revision?: never;
355
- state_revision?: never;
356
354
  };
357
355
  reason: string;
358
356
  process?: never;
@@ -363,19 +361,19 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
363
361
  stdoutFile?: never;
364
362
  stderrFile?: never;
365
363
  nativeEvidence?: never;
366
- } | {
367
- settlement?: never;
368
364
  reservation_id?: never;
365
+ settlement?: never;
366
+ } | {
369
367
  refused: boolean;
370
368
  admission: {
371
- reason?: never;
372
- expected?: never;
373
- actual?: never;
374
- detail?: never;
375
- state?: never;
376
369
  required_skills?: never;
377
370
  required_tools?: never;
378
371
  preflight?: never;
372
+ state?: never;
373
+ detail?: never;
374
+ expected?: never;
375
+ actual?: never;
376
+ reason?: never;
379
377
  ok: true;
380
378
  checks: Record<string, unknown>[];
381
379
  state_revision: number;
@@ -405,12 +403,13 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
405
403
  stdoutFile?: never;
406
404
  stderrFile?: never;
407
405
  nativeEvidence?: never;
406
+ reservation_id?: never;
407
+ settlement?: never;
408
408
  } | {
409
409
  reason?: never;
410
410
  reservation?: never;
411
- admission?: never;
412
411
  refused?: never;
413
- reservation_id?: never;
412
+ admission?: never;
414
413
  execution: null;
415
414
  evidenceError: string;
416
415
  settlement: {
@@ -436,12 +435,12 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
436
435
  stdoutFile?: never;
437
436
  stderrFile?: never;
438
437
  nativeEvidence?: never;
438
+ reservation_id?: never;
439
439
  } | {
440
440
  reason?: never;
441
441
  reservation?: never;
442
- admission?: never;
443
442
  refused?: never;
444
- reservation_id?: never;
443
+ admission?: never;
445
444
  process?: never;
446
445
  execution: Record<string, unknown> | null;
447
446
  evidenceError: string;
@@ -459,11 +458,12 @@ export declare function runNativePilot({ harness, profile, cwd, promptFile, evid
459
458
  stdoutFile?: never;
460
459
  stderrFile?: never;
461
460
  nativeEvidence?: never;
461
+ reservation_id?: never;
462
462
  } | {
463
463
  reason?: never;
464
464
  reservation?: never;
465
- admission?: never;
466
465
  refused?: never;
466
+ admission?: never;
467
467
  process?: never;
468
468
  evidenceError?: never;
469
469
  execution: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { getPackageInfo } from './package-info.js';
1
+ export { getPackageInfo, getPackageRoot } from './package-info.js';
2
2
  export type { PackageInfo } from './package-info.js';
3
3
  export { HostAdapter, HostIntegrationService, type HostIntegrationOptions, type HostTargetOptions, } from './hosts/integration/service.js';
4
4
  export { mapNativeHookEvent, runHostHook, type HostHookOptions, type NativeHookMapping, } from './hosts/integration/hook.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { getPackageInfo } from './package-info.js';
1
+ export { getPackageInfo, getPackageRoot } from './package-info.js';
2
2
  export { HostAdapter, HostIntegrationService, } from './hosts/integration/service.js';
3
3
  export { mapNativeHookEvent, runHostHook, } from './hosts/integration/hook.js';
4
4
  export * from './app/collaboration.js';
@@ -12,7 +12,7 @@ export interface RuntimeInstallResult {
12
12
  readonly residualPaths: readonly string[];
13
13
  }
14
14
  export interface RuntimeInstallSource {
15
- readonly kind: 'version' | 'tarball';
15
+ readonly kind: 'version' | 'tarball' | 'self';
16
16
  /** Required for tarball sources. */
17
17
  readonly path?: string;
18
18
  }
@@ -1,9 +1,10 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { spawn } from 'node:child_process';
3
- import { mkdir, readFile, readdir, readlink, rename, rm, writeFile, } from 'node:fs/promises';
3
+ import { cp, mkdir, mkdtemp, readFile, readdir, readlink, rename, rm, writeFile, } from 'node:fs/promises';
4
4
  import { existsSync } from 'node:fs';
5
5
  import { dirname, join, relative, resolve, sep } from 'node:path';
6
- import { homedir } from 'node:os';
6
+ import { homedir, tmpdir } from 'node:os';
7
+ import { getPackageInfo } from '../package-info.js';
7
8
  import { createDiagnostic } from '../schemas/diagnostics.js';
8
9
  import { diagnosticCodes } from '../schemas/errors.js';
9
10
  import { installJournalSchema, runtimeManifestSchema, runtimePackageManifestSchema, runtimeSourceSchema, } from '../schemas/install.js';
@@ -156,6 +157,81 @@ function npmCommand() {
156
157
  args: [],
157
158
  };
158
159
  }
160
+ /**
161
+ * Packs the package that is currently executing so it can be installed as a
162
+ * durable runtime before an ephemeral (npx/dlx) checkout is collected. The
163
+ * existing build output is packed as-is; scripts never run here.
164
+ */
165
+ async function packSelfPackage(packageRoot, packageName, version) {
166
+ const manifestRead = await readFileBounded(join(packageRoot, 'package.json'), 1024 * 1024);
167
+ if (!manifestRead.ok) {
168
+ throw new Error(`Cannot read the running package manifest: ${manifestRead.code}`);
169
+ }
170
+ const decoded = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(manifestRead.value.bytes));
171
+ if (typeof decoded !== 'object' || decoded === null) {
172
+ throw new Error('The running package manifest is not an object.');
173
+ }
174
+ const manifest = decoded;
175
+ if (manifest.name !== packageName || manifest.version !== version) {
176
+ throw new Error(`Running package ${String(manifest.name)}@${String(manifest.version)} does not match ${packageName}@${version}.`);
177
+ }
178
+ const directory = await mkdtemp(join(tmpdir(), 'pappra-self-'));
179
+ // Pack a staging copy: npm refuses to pack reliably from inside a dependency
180
+ // tree (a known "Exit handler never called!" failure on Linux CI).
181
+ const staging = join(directory, 'package');
182
+ await mkdir(staging, { recursive: true });
183
+ for (const entry of [
184
+ 'package.json',
185
+ 'README.md',
186
+ 'LICENSE',
187
+ 'THIRD_PARTY_NOTICES.md',
188
+ 'dist',
189
+ 'assets',
190
+ 'skills',
191
+ ]) {
192
+ const source = join(packageRoot, entry);
193
+ if ((await observePath(source)).kind === 'missing')
194
+ continue;
195
+ await cp(source, join(staging, entry), {
196
+ recursive: true,
197
+ dereference: true,
198
+ force: true,
199
+ });
200
+ }
201
+ const npm = npmCommand();
202
+ const packed = await runCommand(npm.executable, [
203
+ ...npm.args,
204
+ 'pack',
205
+ '--ignore-scripts',
206
+ '--json',
207
+ '--pack-destination',
208
+ directory,
209
+ staging,
210
+ ], { cwd: directory, timeoutMs: 120_000 });
211
+ if (packed.timedOut || packed.code !== 0) {
212
+ throw new Error(`Cannot pack the running package: ${packed.stderr.slice(0, 500)}`);
213
+ }
214
+ let report;
215
+ try {
216
+ report = JSON.parse(packed.stdout.trim());
217
+ }
218
+ catch {
219
+ throw new Error('npm pack did not return a JSON report.');
220
+ }
221
+ const first = Array.isArray(report) ? report[0] : null;
222
+ const filename = typeof first === 'object' &&
223
+ first !== null &&
224
+ 'filename' in first &&
225
+ typeof first.filename === 'string'
226
+ ? first.filename
227
+ : null;
228
+ if (filename === null || filename.includes('/') || filename.includes('\\')) {
229
+ throw new Error('npm pack did not report a safe tarball filename.');
230
+ }
231
+ const tarball = join(directory, filename);
232
+ const bytes = await readFile(tarball);
233
+ return { directory, tarball, digest: sha256Digest(bytes) };
234
+ }
159
235
  async function readPackageManifest(directory, boundaryRoot) {
160
236
  const manifestPath = join(directory, 'package.json');
161
237
  const real = await resolveRealpath(manifestPath);
@@ -343,7 +419,7 @@ function updateJournal(journal, status, step, detail, failed = false) {
343
419
  }
344
420
  export async function installRuntime(options) {
345
421
  const version = options.version;
346
- const packageName = options.packageName ?? 'pappra';
422
+ const packageName = options.packageName ?? getPackageInfo().name;
347
423
  if (!validVersion(version)) {
348
424
  return failureResult('failed', `Invalid runtime version: ${version}`, {
349
425
  version,
@@ -408,6 +484,7 @@ export async function installRuntime(options) {
408
484
  await releaseInstallLock(paths.lockPath, lock.lock.token);
409
485
  return afterLock;
410
486
  }
487
+ let selfPackage = null;
411
488
  let source;
412
489
  try {
413
490
  if (options.source.kind === 'tarball') {
@@ -425,6 +502,18 @@ export async function installRuntime(options) {
425
502
  digest: sha256Digest(bytes),
426
503
  };
427
504
  }
505
+ else if (options.source.kind === 'self') {
506
+ const packageRoot = resolve(options.source.path ?? '');
507
+ selfPackage = await packSelfPackage(packageRoot, packageName, version);
508
+ source = {
509
+ kind: 'self',
510
+ packageName,
511
+ version,
512
+ packageRoot,
513
+ tarball: selfPackage.tarball,
514
+ digest: selfPackage.digest,
515
+ };
516
+ }
428
517
  else {
429
518
  source = {
430
519
  kind: 'version',
@@ -486,6 +575,9 @@ export async function installRuntime(options) {
486
575
  let published = false;
487
576
  const fail = async (message, stageResidual) => {
488
577
  let residual = [];
578
+ if (selfPackage !== null) {
579
+ await rm(selfPackage.directory, { recursive: true, force: true }).catch(() => undefined);
580
+ }
489
581
  if (stageResidual) {
490
582
  try {
491
583
  await rm(stage, { recursive: true, force: true });
@@ -528,8 +620,10 @@ export async function installRuntime(options) {
528
620
  '--no-package-lock',
529
621
  '--save-exact',
530
622
  ...(options.offline === true ? ['--offline'] : []),
531
- source.kind === 'tarball'
532
- ? source.path
623
+ source.kind === 'tarball' || source.kind === 'self'
624
+ ? source.kind === 'tarball'
625
+ ? source.path
626
+ : source.tarball
533
627
  : `${source.packageName}@${source.version}`,
534
628
  ];
535
629
  const installed = await runCommand(npm.executable, installArgs, {
@@ -539,6 +633,9 @@ export async function installRuntime(options) {
539
633
  if (installed.timedOut || installed.code !== 0) {
540
634
  throw new Error(`Package manager install failed (${installed.timedOut ? 'timeout' : `exit ${String(installed.code)}`}): ${installed.stderr.slice(0, 1000)}`);
541
635
  }
636
+ if (selfPackage !== null) {
637
+ await rm(selfPackage.directory, { recursive: true, force: true }).catch(() => undefined);
638
+ }
542
639
  journal = updateJournal(journal, 'applying', 'package-installed', null);
543
640
  if (!(await writeJournal()))
544
641
  throw new Error('Cannot update install journal.');
@@ -643,6 +740,10 @@ export async function installRuntime(options) {
643
740
  return fail(errorMessage(error), true);
644
741
  }
645
742
  }
743
+ async function runtimePackageName(runtimeRoot) {
744
+ const read = await readJsonDocument(join(runtimeRoot, 'install-manifest.json'), runtimeManifestSchema);
745
+ return read.value?.packageName ?? getPackageInfo().name;
746
+ }
646
747
  export async function inspectRuntimeState(version, dataDir = resolveUserDataDir()) {
647
748
  const paths = runtimePaths(version, dataDir);
648
749
  if (!validVersion(version)) {
@@ -720,7 +821,8 @@ export async function inspectRuntimeState(version, dataDir = resolveUserDataDir(
720
821
  ],
721
822
  };
722
823
  }
723
- const complete = await completeExistingRuntime(version, paths, 'pappra', 30_000);
824
+ const packageName = await runtimePackageName(paths.runtimeRoot);
825
+ const complete = await completeExistingRuntime(version, paths, packageName, 30_000);
724
826
  if (complete === null) {
725
827
  return {
726
828
  version,
@@ -1042,8 +1144,13 @@ export async function inspectRuntimeStatePassive(version, dataDir = resolveUserD
1042
1144
  ],
1043
1145
  };
1044
1146
  }
1045
- if (manifest.packageName !== 'pappra' ||
1046
- manifest.runtimeRoot !== paths.runtimeRoot) {
1147
+ const packageSegments = manifest.packageName.split('/');
1148
+ const safePackageName = packageSegments.length <= 2 &&
1149
+ packageSegments.every((segment) => segment !== '' &&
1150
+ segment !== '.' &&
1151
+ segment !== '..' &&
1152
+ /^[A-Za-z0-9@._-]+$/.test(segment));
1153
+ if (!safePackageName || manifest.runtimeRoot !== paths.runtimeRoot) {
1047
1154
  return {
1048
1155
  version,
1049
1156
  status: 'corrupt',
@@ -1065,7 +1172,7 @@ export async function inspectRuntimeStatePassive(version, dataDir = resolveUserD
1065
1172
  ],
1066
1173
  };
1067
1174
  }
1068
- const packageRoot = join(paths.runtimeRoot, 'node_modules', 'pappra');
1175
+ const packageRoot = join(paths.runtimeRoot, 'node_modules', ...manifest.packageName.split('/'));
1069
1176
  const packageObservation = await observePath(packageRoot);
1070
1177
  const packageReal = packageObservation.kind === 'directory'
1071
1178
  ? await resolveRealpath(packageRoot)
@@ -1105,7 +1212,7 @@ export async function inspectRuntimeStatePassive(version, dataDir = resolveUserD
1105
1212
  digest === manifest.packageDigest &&
1106
1213
  packageObservation.kind === 'directory' &&
1107
1214
  packageManifest !== null &&
1108
- packageManifest.name === 'pappra' &&
1215
+ packageManifest.name === manifest.packageName &&
1109
1216
  packageManifest.version === version;
1110
1217
  const executableExists = executableInsideRuntime && executableObservation.kind === 'file';
1111
1218
  if (!packageDigestMatch || !executableExists) {
@@ -4,3 +4,5 @@ export interface PackageInfo {
4
4
  readonly description: string;
5
5
  }
6
6
  export declare function getPackageInfo(): PackageInfo;
7
+ /** Absolute root of the package that is currently executing. */
8
+ export declare function getPackageRoot(): string;
@@ -1,4 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
2
4
  export function getPackageInfo() {
3
5
  const value = JSON.parse(readFileSync(new URL(import.meta.resolve('@phyzess/pappra/package.json')), 'utf8'));
4
6
  if (typeof value !== 'object' ||
@@ -17,3 +19,7 @@ export function getPackageInfo() {
17
19
  description: value.description,
18
20
  };
19
21
  }
22
+ /** Absolute root of the package that is currently executing. */
23
+ export function getPackageRoot() {
24
+ return dirname(fileURLToPath(import.meta.resolve('@phyzess/pappra/package.json')));
25
+ }
@@ -7,6 +7,8 @@ export const HOST_HOOK_INPUT_SCHEMA = 'pappra/host-hook-input/1';
7
7
  export const HOST_HOOK_RESULT_SCHEMA = 'pappra/host-hook-result/1';
8
8
  const digestSchema = z.string().regex(/^sha256:[0-9a-f]{64}$/);
9
9
  const digestOrNull = digestSchema.nullable();
10
+ /** A host hook path may be absolute or a host placeholder expression. */
11
+ const hookPathSchema = z.string().min(1).max(4096);
10
12
  const absolutePathSchema = z
11
13
  .string()
12
14
  .min(1)
@@ -75,7 +77,7 @@ export const hostIntegrationPlanSchema = z.strictObject({
75
77
  host: hostNameSchema,
76
78
  workspaceRoot: absolutePathSchema,
77
79
  hostRoot: absolutePathSchema,
78
- runtimeEntry: absolutePathSchema,
80
+ runtimeEntry: hookPathSchema,
79
81
  bundleDigest: digestSchema,
80
82
  files: z.array(hostPlanFileSchema).max(64),
81
83
  reloadRequired: z.boolean(),
@@ -189,7 +191,7 @@ export const hostInstallManifestSchema = z.strictObject({
189
191
  bundleDigest: digestSchema,
190
192
  installedAt: z.iso.datetime({ offset: true }),
191
193
  hostRoot: absolutePathSchema,
192
- runtimeEntry: absolutePathSchema,
194
+ runtimeEntry: hookPathSchema,
193
195
  configPath: z.string().min(1).max(2048).nullable(),
194
196
  files: z.array(z.strictObject({
195
197
  path: z.string().min(1).max(2048),