@danypops/papyrus 0.38.1 → 0.38.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/papyrus",
3
- "version": "0.38.1",
3
+ "version": "0.38.3",
4
4
  "description": "Daemon-backed graph artifacts, evidence-bearing tasks, rules, skills, and native TUI workflows for Pi",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package"],
@@ -34,8 +34,7 @@
34
34
  },
35
35
  "files": ["src", "README.md"],
36
36
  "dependencies": {
37
- "@danypops/daemon-kit": "^0.10.0",
38
- "@danypops/vehicle-core": "^0.1.0",
39
- "@danypops/vehicle-server": "^0.1.0"
37
+ "@danypops/vehicle-core": "^0.1.1",
38
+ "@danypops/vehicle-server": "^0.3.2"
40
39
  }
41
40
  }
package/src/cli.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env bun
2
2
  import { execFileSync } from "node:child_process";
3
- import { copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { copyFileSync, existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
- import { dirname, join } from "node:path";
5
+ import { join } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
+ import { createNodeServiceInstallDeps, generateSystemdUnit, installUserService, type ServiceSpec } from "@danypops/vehicle-server/service";
7
8
  import { connectPapyrusClient, type PapyrusClient } from "./client.ts";
8
9
  import { DAEMON_UNIT_NAME, TASK_EXECUTION_MAX_NODES, dbPath } from "./constants.ts";
9
10
  import { serveMain } from "./daemon.ts";
@@ -18,20 +19,36 @@ export interface SystemdUnitOptions {
18
19
  cliPath: string;
19
20
  }
20
21
 
21
- export function renderSystemdUnit(options: SystemdUnitOptions): string {
22
- return `[Unit]
23
- Description=Papyrus graph artifact service
24
- After=default.target
25
-
26
- [Service]
27
- Type=simple
28
- ExecStart=${options.bunBin} ${options.cliPath} serve
29
- Restart=always
30
- RestartSec=2
22
+ /**
23
+ * Pure text generator, delegating to vehicle-server's shared generateSystemdUnit -- kept as its
24
+ * own named export with the same options shape since this package's own tests (and any external
25
+ * caller) call it directly.
26
+ *
27
+ * Papyrus's own daemon.ts does not (yet) use vehicle-server's startDaemon/runDaemonProcess -- it's
28
+ * a bespoke Bun.serve() with its own state-file layout (daemon-state.ts) and maintenance-timer
29
+ * scheduling, predating that shared substrate. Only unit *generation* is migrated here; unitPath()
30
+ * stays a manual XDG_CONFIG_HOME construction rather than pulling in resolveDaemonPaths() for a
31
+ * database/token/handle layout Papyrus doesn't actually use. DAEMON_KIT_LAUNCH_PROVENANCE=service
32
+ * is emitted (generateSystemdUnit always adds it) but is currently inert -- Papyrus's daemon never
33
+ * reads it, since it has no idle-shutdown concept of its own. Migrating daemon.ts's own substrate
34
+ * is tracked separately.
35
+ */
36
+ function papyrusServiceSpec(options: SystemdUnitOptions): ServiceSpec {
37
+ return {
38
+ name: "papyrus",
39
+ displayName: "Papyrus graph artifact service",
40
+ binPath: options.bunBin,
41
+ args: [options.cliPath, "serve"],
42
+ descriptorPath: unitPath(),
43
+ // Restart=always/RestartSec=2 already unconditional in the prior hand-rolled unit --
44
+ // preserved exactly, not a new opt-in.
45
+ restartOnFailure: true,
46
+ restartSec: 2,
47
+ };
48
+ }
31
49
 
32
- [Install]
33
- WantedBy=default.target
34
- `;
50
+ export function renderSystemdUnit(options: SystemdUnitOptions): string {
51
+ return generateSystemdUnit(papyrusServiceSpec(options));
35
52
  }
36
53
 
37
54
  function unitPath(): string {
@@ -53,14 +70,12 @@ function isDaemonActive(): boolean {
53
70
  }
54
71
 
55
72
  function installService(): void {
56
- const path = unitPath();
57
- mkdirSync(dirname(path), { recursive: true });
58
- writeFileSync(path, renderSystemdUnit({
59
- bunBin: process.execPath,
60
- cliPath: fileURLToPath(import.meta.url),
61
- }));
62
- systemctl("daemon-reload");
63
- systemctl("enable", DAEMON_UNIT_NAME);
73
+ const spec = papyrusServiceSpec({ bunBin: process.execPath, cliPath: fileURLToPath(import.meta.url) });
74
+ const result = installUserService(spec, createNodeServiceInstallDeps());
75
+ if (!result.installed) throw new Error(`failed to install the Papyrus service: ${result.reason}`);
76
+ // installUserService's Linux path is `enable --now` (starts if not already running) --
77
+ // an explicit restart on top ensures a re-install after an upgrade actually picks up the
78
+ // freshly-generated unit's new ExecStart path, not just re-enables the old one.
64
79
  systemctl("restart", DAEMON_UNIT_NAME);
65
80
  }
66
81
 
package/src/client.ts CHANGED
@@ -59,7 +59,7 @@ export async function connectPapyrusClient(dir: string = daemonStateDir()): Prom
59
59
  }
60
60
 
61
61
  export interface PushChannelTarget {
62
- /** ws:// URL for the daemon's push-invalidation channel (see push-channel.ts in daemon-kit). */
62
+ /** ws:// URL for the daemon's push-invalidation channel (see push-channel.ts in vehicle-server). */
63
63
  url: string;
64
64
  token: string;
65
65
  }
package/src/daemon.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { PushChannel } from "@danypops/daemon-kit/push-channel";
1
+ import { PushChannel } from "@danypops/vehicle-server/push-channel";
2
2
  import { DAEMON_HOST, DB_OPTIMIZE_INTERVAL_MS, WAL_CHECKPOINT_INTERVAL_MS, dbPath } from "./constants.ts";
3
3
  import { clearDaemonPort, daemonStateDir, loadOrCreateToken, writeDaemonPort } from "./daemon-state.ts";
4
4
  import { createApp, createPapyrusService } from "./service.ts";
package/src/db.ts CHANGED
@@ -7,7 +7,7 @@ import { createHash } from "node:crypto";
7
7
  import { createRequire } from "node:module";
8
8
  import { mkdirSync } from "node:fs";
9
9
  import { join, dirname } from "node:path";
10
- import { runMigrations, type SqliteMigrationRunner } from "@danypops/daemon-kit/storage";
10
+ import { runMigrations, type SqliteMigrationRunner } from "@danypops/vehicle-server/storage";
11
11
  import { SQLITE_BUSY_TIMEOUT_MS, SQLITE_SCHEMA_VERSION } from "./constants.ts";
12
12
 
13
13
  const require_ = createRequire(import.meta.url);
@@ -449,9 +449,10 @@ const LEGACY_MIGRATION_CHAIN_TARGET_VERSION = 13;
449
449
 
450
450
  /**
451
451
  * A migration beyond LEGACY_MIGRATION_CHAIN_TARGET_VERSION. Runs through @danypops/
452
- * daemon-kit's generic runMigrations engine (one transaction per migration, daemon-kit's
452
+ * vehicle-server's generic runMigrations engine (one transaction per migration, its
453
453
  * default) via dbMigrationRunner below, instead of a new branch appended to the legacy
454
- * if-chain -- the exact reuse daemon-kit's storage module was refactored (v0.2.1) to allow,
454
+ * if-chain -- the exact reuse the storage module was refactored (originally daemon-kit
455
+ * v0.2.1, now absorbed into vehicle-server) to allow,
455
456
  * since Papyrus's dual bun:sqlite/node:sqlite Db abstraction could never satisfy that
456
457
  * engine's original bun:sqlite-only signature.
457
458
  */
@@ -658,7 +659,7 @@ const FUTURE_MIGRATIONS: ReadonlyArray<PapyrusMigration> = [
658
659
  ];
659
660
 
660
661
  /**
661
- * Adapts Papyrus's own Db/inTransaction to daemon-kit's storage-agnostic
662
+ * Adapts Papyrus's own Db/inTransaction to vehicle-server's storage-agnostic
662
663
  * SqliteMigrationRunner port, so its runMigrations engine (written against bun:sqlite's
663
664
  * concrete Database) runs unmodified against Papyrus's dual-runtime Db abstraction instead.
664
665
  */
@@ -17,11 +17,13 @@
17
17
  *
18
18
  * The actual cryptographic primitive (secret generation, hashing, constant-time verify,
19
19
  * first-touch registration semantics) is NOT reimplemented here -- it lives in
20
- * @danypops/daemon-kit's session-identity module. That gap (a shared bearer token cannot
20
+ * @danypops/vehicle-server's session-identity module. That gap (a shared bearer token cannot
21
21
  * distinguish callers; a session id needs a real credential once it becomes behavior-
22
- * affecting) is generic to every daemon-kit-shaped daemon, not Papyrus-specific -- Papyrus's
23
- * own daemon.ts/service.ts predates daemon-kit and has not migrated onto it, but this one
24
- * capability is adopted narrowly regardless (daemon-kit's exports map is designed for
22
+ * affecting) is generic to every Vehicle-shaped daemon, not Papyrus-specific -- Papyrus's
23
+ * own daemon.ts/service.ts is a fully custom Bun.serve() composition root that has never
24
+ * adopted vehicle-server's own startDaemon()/paths conventions (its own daemon-state.ts
25
+ * hand-rolls path/token/port management instead), but this one narrow capability is adopted
26
+ * regardless (vehicle-server's exports map is designed for
25
27
  * exactly this: "a consumer only pulls in what it uses"). This file only wires that generic
26
28
  * primitive to Papyrus's own SQLite storage (see adapters/sqlite-session-identity-store.ts)
27
29
  * and to Task Focus specifically, the one place session_id is behavior-affecting today.
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export type { DiscussionAndRounds } from "./discussion-service.ts";
24
24
  export { NOTE_DISPOSITIONS } from "./note-service.ts";
25
25
  export type { OperationName, SchemaState } from "./service.ts";
26
26
  export type { WorkflowRunResult } from "./workflow-execution.ts";
27
+ export type { PlaybookInvocationResult, PlaybookMissingArguments } from "./playbook-execution.ts";
27
28
  export { projectArtifactRelationships } from "./artifact-relationship-view.ts";
28
29
  export { taskContext } from "./task-context.ts";
29
30
  export { projectTaskExecution, type TaskExecutionPlan, type TaskExecutionState } from "./task-execution.ts";
@@ -1,9 +1,9 @@
1
- import type { SessionIdentityRecord, SessionIdentityStore as DaemonKitSessionIdentityStore } from "@danypops/daemon-kit/session-identity";
1
+ import type { SessionIdentityRecord, SessionIdentityStore as VehicleSessionIdentityStore } from "@danypops/vehicle-server/session-identity";
2
2
 
3
3
  /**
4
- * Papyrus's persistence port for @danypops/daemon-kit's storage-agnostic session-identity
4
+ * Papyrus's persistence port for @danypops/vehicle-server's storage-agnostic session-identity
5
5
  * primitive -- re-exported under this project's own port naming convention (src/ports/*)
6
- * rather than importing the daemon-kit interface name directly at every call site.
6
+ * rather than importing the vehicle-server interface name directly at every call site.
7
7
  */
8
- export type SessionIdentityStore = DaemonKitSessionIdentityStore;
8
+ export type SessionIdentityStore = VehicleSessionIdentityStore;
9
9
  export type { SessionIdentityRecord };
@@ -1,4 +1,4 @@
1
- import { isSessionRegistered, registerSessionIdentity, releaseSessionIdentity, verifySessionSecret } from "@danypops/daemon-kit/session-identity";
1
+ import { isSessionRegistered, registerSessionIdentity, releaseSessionIdentity, verifySessionSecret } from "@danypops/vehicle-server/session-identity";
2
2
  import { assertValidSessionId } from "./domain/session-identity.ts";
3
3
  import type { SessionIdentityStore } from "./ports/session-identity-store.ts";
4
4
 
@@ -11,7 +11,7 @@ export interface RegisterSessionIdentityResult {
11
11
  export class InvalidSessionSecretError extends Error {}
12
12
 
13
13
  /**
14
- * Thin Papyrus-side wrapper over @danypops/daemon-kit's storage-agnostic session-identity
14
+ * Thin Papyrus-side wrapper over @danypops/vehicle-server's storage-agnostic session-identity
15
15
  * primitive: validates input shape, binds it to Papyrus's own SQLite-backed store, and
16
16
  * exposes the exact three operations Task Focus enforcement needs (see
17
17
  * assertAuthorizedForFocus in src/modules/tasks.ts). See domain/session-identity.ts for the