@av-pi-studio/server 0.0.72 → 0.0.74

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 (54) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/agent/pi-home.d.ts +33 -0
  3. package/dist/agent/pi-home.d.ts.map +1 -0
  4. package/dist/agent/pi-home.js +49 -0
  5. package/dist/agent/pi-home.js.map +1 -0
  6. package/dist/agent/provider-registry.d.ts +6 -0
  7. package/dist/agent/provider-registry.d.ts.map +1 -1
  8. package/dist/agent/provider-registry.js +7 -14
  9. package/dist/agent/provider-registry.js.map +1 -1
  10. package/dist/config/daemon-config.d.ts +39 -0
  11. package/dist/config/daemon-config.d.ts.map +1 -1
  12. package/dist/config/daemon-config.js +59 -1
  13. package/dist/config/daemon-config.js.map +1 -1
  14. package/dist/daemon/bootstrap.d.ts +4 -0
  15. package/dist/daemon/bootstrap.d.ts.map +1 -1
  16. package/dist/daemon/bootstrap.js +20 -0
  17. package/dist/daemon/bootstrap.js.map +1 -1
  18. package/dist/extensions/curated-packs.d.ts +70 -0
  19. package/dist/extensions/curated-packs.d.ts.map +1 -0
  20. package/dist/extensions/curated-packs.js +206 -0
  21. package/dist/extensions/curated-packs.js.map +1 -0
  22. package/dist/extensions/extensions-rpc.d.ts +15 -0
  23. package/dist/extensions/extensions-rpc.d.ts.map +1 -0
  24. package/dist/extensions/extensions-rpc.js +76 -0
  25. package/dist/extensions/extensions-rpc.js.map +1 -0
  26. package/dist/extensions/extensions-service.d.ts +60 -0
  27. package/dist/extensions/extensions-service.d.ts.map +1 -0
  28. package/dist/extensions/extensions-service.js +145 -0
  29. package/dist/extensions/extensions-service.js.map +1 -0
  30. package/dist/extensions/extensions-state.d.ts +490 -0
  31. package/dist/extensions/extensions-state.d.ts.map +1 -0
  32. package/dist/extensions/extensions-state.js +114 -0
  33. package/dist/extensions/extensions-state.js.map +1 -0
  34. package/dist/extensions/index.d.ts +5 -0
  35. package/dist/extensions/index.d.ts.map +1 -0
  36. package/dist/extensions/index.js +10 -0
  37. package/dist/extensions/index.js.map +1 -0
  38. package/dist/extensions/sync-executor.d.ts +55 -0
  39. package/dist/extensions/sync-executor.d.ts.map +1 -0
  40. package/dist/extensions/sync-executor.js +158 -0
  41. package/dist/extensions/sync-executor.js.map +1 -0
  42. package/dist/extensions/sync-planner.d.ts +62 -0
  43. package/dist/extensions/sync-planner.d.ts.map +1 -0
  44. package/dist/extensions/sync-planner.js +103 -0
  45. package/dist/extensions/sync-planner.js.map +1 -0
  46. package/dist/extensions/wire.d.ts +7 -0
  47. package/dist/extensions/wire.d.ts.map +1 -0
  48. package/dist/extensions/wire.js +51 -0
  49. package/dist/extensions/wire.js.map +1 -0
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +3 -0
  53. package/dist/index.js.map +1 -1
  54. package/package.json +4 -4
@@ -0,0 +1,55 @@
1
+ import type { PersistedConfig } from "../config/daemon-config.js";
2
+ import type { Logger } from "../logging/logger.js";
3
+ import type { SyncPlan } from "./sync-planner.js";
4
+ /**
5
+ * Runs a `SyncPlan` by spawning `pi install <spec>` per action, one package succeeding or failing
6
+ * never affecting another (swe/features/preinstalled-extensions.md § Executor — tenet 5). Every
7
+ * planned action is attempted regardless of how many earlier ones failed; state is committed after
8
+ * **each** action so a crash mid-sync loses neither the successes already achieved nor the failure
9
+ * diagnostics already gathered.
10
+ */
11
+ export type ExtensionFailureReason = "not_found" | "unauthorized" | "network" | "timeout" | "install_failed" | "spawn_failed" | "unknown";
12
+ export interface SyncReport {
13
+ at: string;
14
+ outcome: "ok" | "noop" | "partial" | "failed" | "skipped";
15
+ installed: string[];
16
+ failures: {
17
+ identity: string;
18
+ source: string;
19
+ pack: string;
20
+ reason: ExtensionFailureReason;
21
+ message: string;
22
+ }[];
23
+ }
24
+ /** Injectable process seam — tests pass a fake; production spawns the bundled pi. The seam owns
25
+ * the per-package timeout and the process-tree kill; the executor only interprets the result. */
26
+ export interface InstallSpawn {
27
+ (args: {
28
+ command: string[];
29
+ env: Record<string, string>;
30
+ timeoutMs: number;
31
+ }): Promise<{
32
+ exitCode: number | null;
33
+ stderr: string;
34
+ timedOut?: boolean;
35
+ }>;
36
+ }
37
+ /** The production seam: spawns `command`, captures stderr (unbounded here — the executor bounds
38
+ * what it stores), and kills the whole process tree on timeout rather than just the direct child,
39
+ * so a `pi install` that itself forked a build/postinstall step doesn't linger. */
40
+ export declare const defaultInstallSpawn: InstallSpawn;
41
+ /** Maps a spawn result (non-zero exit / timeout) or a caught throw to the reason taxonomy.
42
+ * Best-effort and cosmetic — misclassification never affects whether the run continues, whether
43
+ * other packages are attempted, or whether the entry is retried later (all three unconditional). */
44
+ export declare function classify(input: unknown): ExtensionFailureReason;
45
+ export declare function executePlan(plan: SyncPlan, deps: {
46
+ home: string;
47
+ /** State-file key for this pi-home (`effectivePiHomeKey(config)` — kept separate from the env
48
+ * derivation below so a caller mistake in this value can never redirect installs). */
49
+ piHomeKey: string;
50
+ config: PersistedConfig;
51
+ spawn?: InstallSpawn;
52
+ logger: Logger;
53
+ now?: () => Date;
54
+ }): Promise<SyncReport>;
55
+ //# sourceMappingURL=sync-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-executor.d.ts","sourceRoot":"","sources":["../../src/extensions/sync-executor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAQnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;;GAMG;AAEH,MAAM,MAAM,sBAAsB,GAC9B,WAAW,GACX,cAAc,GACd,SAAS,GACT,SAAS,GACT,gBAAgB,GAChB,cAAc,GACd,SAAS,CAAC;AAEd,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,sBAAsB,CAAC;QAC/B,OAAO,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;CACL;AAED;kGACkG;AAClG,MAAM,WAAW,YAAY;IAC3B,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACrF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ;AAKD;;oFAEoF;AACpF,eAAO,MAAM,mBAAmB,EAAE,YAgC9B,CAAC;AAEL;;qGAEqG;AACrG,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,CAW/D;AAoCD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE;IACJ,IAAI,EAAE,MAAM,CAAC;IACb;2FACuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,GACA,OAAO,CAAC,UAAU,CAAC,CAqFrB"}
@@ -0,0 +1,158 @@
1
+ import { spawn } from "node:child_process";
2
+ import treeKill from "tree-kill";
3
+ import { resolveBundledPiCli } from "../agent/providers/pi/rpc-transport.js";
4
+ import { SERVER_VERSION } from "./curated-packs.js";
5
+ import { effectivePiHomeKey, loadExtensionsState, saveExtensionsState, } from "./extensions-state.js";
6
+ const INSTALL_TIMEOUT_MS = 180_000;
7
+ const STDERR_TAIL_MAX = 2048;
8
+ /** The production seam: spawns `command`, captures stderr (unbounded here — the executor bounds
9
+ * what it stores), and kills the whole process tree on timeout rather than just the direct child,
10
+ * so a `pi install` that itself forked a build/postinstall step doesn't linger. */
11
+ export const defaultInstallSpawn = ({ command, env, timeoutMs }) => new Promise((resolve, reject) => {
12
+ const [bin, ...args] = command;
13
+ const child = spawn(bin, args, { env, stdio: ["ignore", "ignore", "pipe"] });
14
+ let stderr = "";
15
+ let timedOut = false;
16
+ let settled = false;
17
+ const timer = setTimeout(() => {
18
+ timedOut = true;
19
+ if (child.pid !== undefined)
20
+ treeKill(child.pid, "SIGKILL", () => undefined);
21
+ else
22
+ child.kill("SIGKILL");
23
+ }, timeoutMs);
24
+ child.stderr?.on("data", (chunk) => {
25
+ stderr += chunk.toString("utf8");
26
+ });
27
+ child.on("error", (err) => {
28
+ if (settled)
29
+ return;
30
+ settled = true;
31
+ clearTimeout(timer);
32
+ reject(err);
33
+ });
34
+ child.on("exit", (code) => {
35
+ if (settled)
36
+ return;
37
+ settled = true;
38
+ clearTimeout(timer);
39
+ resolve({ exitCode: code, stderr, timedOut });
40
+ });
41
+ });
42
+ /** Maps a spawn result (non-zero exit / timeout) or a caught throw to the reason taxonomy.
43
+ * Best-effort and cosmetic — misclassification never affects whether the run continues, whether
44
+ * other packages are attempted, or whether the entry is retried later (all three unconditional). */
45
+ export function classify(input) {
46
+ if (input && typeof input === "object" && "exitCode" in input) {
47
+ if ("timedOut" in input && input.timedOut === true)
48
+ return "timeout";
49
+ const stderr = "stderr" in input && typeof input.stderr === "string" ? input.stderr : "";
50
+ if (/\b404\b|E404/.test(stderr))
51
+ return "not_found";
52
+ if (/\b40[13]\b|E40[13]/.test(stderr))
53
+ return "unauthorized";
54
+ if (/ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|\b5\d\d\b/.test(stderr))
55
+ return "network";
56
+ return "install_failed";
57
+ }
58
+ const message = input instanceof Error ? input.message : String(input);
59
+ return /ENOENT/.test(message) ? "spawn_failed" : "unknown";
60
+ }
61
+ function recordFailure(piHomeState, report, action, reason, message, at) {
62
+ const attempts = (piHomeState.failures[action.identity]?.attempts ?? 0) + 1;
63
+ piHomeState.failures[action.identity] = { source: action.source, reason, message, attempts, at };
64
+ report.failures.push({
65
+ identity: action.identity,
66
+ source: action.source,
67
+ pack: action.pack,
68
+ reason,
69
+ message,
70
+ });
71
+ }
72
+ function deriveOutcome(report) {
73
+ if (report.failures.length === 0)
74
+ return "ok";
75
+ return report.installed.length > 0 ? "partial" : "failed";
76
+ }
77
+ /** A `process.env` snapshot with `undefined` entries dropped, so callers get a plain
78
+ * `Record<string, string>` without an unchecked cast. */
79
+ function processEnvSnapshot() {
80
+ const env = {};
81
+ for (const [key, value] of Object.entries(process.env)) {
82
+ if (value !== undefined)
83
+ env[key] = value;
84
+ }
85
+ return env;
86
+ }
87
+ export async function executePlan(plan, deps) {
88
+ const now = deps.now ?? (() => new Date());
89
+ if (plan.actions.length === 0) {
90
+ return { at: now().toISOString(), outcome: "noop", installed: [], failures: [] };
91
+ }
92
+ const cli = resolveBundledPiCli();
93
+ if (!cli) {
94
+ deps.logger.warn("no bundled pi CLI resolvable — extensions sync skipped");
95
+ return { at: now().toISOString(), outcome: "skipped", installed: [], failures: [] };
96
+ }
97
+ const loaded = await loadExtensionsState(deps.home);
98
+ if (loaded === "unreadable") {
99
+ deps.logger.error("extensions-state.json is unreadable — extensions sync skipped");
100
+ return { at: now().toISOString(), outcome: "skipped", installed: [], failures: [] };
101
+ }
102
+ const state = loaded;
103
+ const piHomeState = state.piHomes[deps.piHomeKey] ?? { offered: {}, failures: {} };
104
+ state.piHomes[deps.piHomeKey] = piHomeState;
105
+ const spawnFn = deps.spawn ?? defaultInstallSpawn;
106
+ // SAME derivation as the state key and the spawn path (features/preinstalled-extensions.md §
107
+ // Public Contract — Effective pi-home key) — computed fresh from `config` rather than trusted
108
+ // from `piHomeKey`, so this env var can never diverge from what a live agent would receive.
109
+ const env = {
110
+ ...processEnvSnapshot(),
111
+ PI_CODING_AGENT_DIR: effectivePiHomeKey(deps.config),
112
+ GIT_TERMINAL_PROMPT: "0",
113
+ GIT_SSH_COMMAND: "ssh -oBatchMode=yes",
114
+ npm_config_yes: "true",
115
+ };
116
+ const report = { at: "", outcome: "ok", installed: [], failures: [] };
117
+ for (const action of plan.actions) {
118
+ const at = now().toISOString();
119
+ try {
120
+ const result = await spawnFn({
121
+ command: [process.execPath, cli, "install", action.source],
122
+ env,
123
+ timeoutMs: INSTALL_TIMEOUT_MS,
124
+ });
125
+ if (result.exitCode === 0) {
126
+ piHomeState.offered[action.identity] = {
127
+ installedSpec: action.source,
128
+ atVersion: SERVER_VERSION,
129
+ at,
130
+ };
131
+ delete piHomeState.failures[action.identity];
132
+ report.installed.push(action.identity);
133
+ deps.logger.info({ identity: action.identity, source: action.source }, "installed recommended extension");
134
+ }
135
+ else {
136
+ const reason = classify(result);
137
+ const message = result.stderr.length > STDERR_TAIL_MAX
138
+ ? result.stderr.slice(-STDERR_TAIL_MAX)
139
+ : result.stderr;
140
+ recordFailure(piHomeState, report, action, reason, message, at);
141
+ deps.logger.warn({ identity: action.identity, source: action.source, reason }, "failed to install recommended extension");
142
+ }
143
+ }
144
+ catch (error) {
145
+ const reason = classify(error);
146
+ const message = error instanceof Error ? error.message : String(error);
147
+ recordFailure(piHomeState, report, action, reason, message, at);
148
+ deps.logger.warn({ identity: action.identity, source: action.source, reason }, "failed to install recommended extension");
149
+ }
150
+ finally {
151
+ await saveExtensionsState(deps.home, state);
152
+ }
153
+ }
154
+ report.at = now().toISOString();
155
+ report.outcome = deriveOutcome(report);
156
+ return report;
157
+ }
158
+ //# sourceMappingURL=sync-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-executor.js","sourceRoot":"","sources":["../../src/extensions/sync-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { CuratedPackCatalog } from "./curated-packs.js";
2
+ import type { PiHomeState } from "./extensions-state.js";
3
+ /**
4
+ * The pure sync planner: a single three-way merge over the manifest, the state file, and pi's live
5
+ * `settings.json` (swe/features/preinstalled-extensions.md § Planner). No filesystem, no process,
6
+ * no clock — this is the one code path behind both "what sync would do" (task 005's executor) and
7
+ * "what we report" (a future `extensions list`, run in dry-run mode), so the two can never drift.
8
+ *
9
+ * The load-bearing rule (tenet 1): sync's only action is installing an identity it has never
10
+ * successfully installed before **and** that isn't already present in `settings.json` under any
11
+ * form — the latter matters because pi's own `pi install` matches an existing entry by
12
+ * version-insensitive identity and rewrites it in place (see `findByIdentity` below), so
13
+ * installing over an entry the user already added themselves — pinned, filtered, or otherwise —
14
+ * would silently destroy it. Once an identity enters `offered`, or is found already present before
15
+ * ever being offered, no future plan ever contains an action for it again — `user_removed`/
16
+ * `user_modified` are terminal, reporting-only statuses.
17
+ */
18
+ export type EntryStatus = "installed" | "pending" | "failed" | "user_removed" | "user_modified" | "deprecated";
19
+ export interface PlannedEntry {
20
+ identity: string;
21
+ pack: string;
22
+ source: string;
23
+ addedIn: string;
24
+ deprecated?: boolean;
25
+ status: EntryStatus;
26
+ }
27
+ export interface SyncPlan {
28
+ /** Installs, in manifest order. */
29
+ actions: {
30
+ identity: string;
31
+ pack: string;
32
+ source: string;
33
+ }[];
34
+ /** Every selected entry, including no-action ones. */
35
+ entries: PlannedEntry[];
36
+ }
37
+ /** A `PlannedEntry` with its last recorded failure attached, when one exists — shared by
38
+ * `ExtensionsService.describe()` (the daemon path) and the CLI's `extensions list --local`
39
+ * (sprint-057/task-005), so the two report identical data from the same state, never a
40
+ * hand-rolled second derivation. */
41
+ export interface DescribedEntry extends PlannedEntry {
42
+ lastError?: {
43
+ at: string;
44
+ attempts: number;
45
+ reason: string;
46
+ message: string;
47
+ };
48
+ }
49
+ /** Attaches each entry's `state.failures[identity]`, when present, as `lastError` — one snapshot,
50
+ * one shape, never a second `loadExtensionsState` read by a caller (that would risk interleaving
51
+ * with the mutex-guarded sync writing the same file on the daemon path). */
52
+ export declare function attachLastErrors(entries: readonly PlannedEntry[], state: PiHomeState | "unreadable"): DescribedEntry[];
53
+ export declare function planSync(input: {
54
+ catalog: CuratedPackCatalog;
55
+ /** Selected slugs; `core` is implicit (added by `selectEntries`). */
56
+ packs: readonly string[];
57
+ /** Per-pi-home slice, or the corrupt-state fail-safe sentinel. */
58
+ state: PiHomeState | "unreadable";
59
+ /** Pi's live `settings.json` `packages` array — string or object entries. */
60
+ settingsPackages: readonly unknown[];
61
+ }): SyncPlan;
62
+ //# sourceMappingURL=sync-planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-planner.d.ts","sourceRoot":"","sources":["../../src/extensions/sync-planner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,SAAS,GACT,QAAQ,GACR,cAAc,GACd,eAAe,GACf,YAAY,CAAC;AAEjB,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,mCAAmC;IACnC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9D,sDAAsD;IACtD,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;;qCAGqC;AACrC,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,SAAS,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E;AAED;;6EAE6E;AAC7E,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,KAAK,EAAE,WAAW,GAAG,YAAY,GAChC,cAAc,EAAE,CAgBlB;AAiCD,wBAAgB,QAAQ,CAAC,KAAK,EAAE;IAC9B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,qEAAqE;IACrE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,kEAAkE;IAClE,KAAK,EAAE,WAAW,GAAG,YAAY,CAAC;IAClC,6EAA6E;IAC7E,gBAAgB,EAAE,SAAS,OAAO,EAAE,CAAC;CACtC,GAAG,QAAQ,CAwDX"}
@@ -0,0 +1,103 @@
1
+ import { identityOf, selectEntries } from "./curated-packs.js";
2
+ /** Attaches each entry's `state.failures[identity]`, when present, as `lastError` — one snapshot,
3
+ * one shape, never a second `loadExtensionsState` read by a caller (that would risk interleaving
4
+ * with the mutex-guarded sync writing the same file on the daemon path). */
5
+ export function attachLastErrors(entries, state) {
6
+ const failures = state === "unreadable" ? {} : state.failures;
7
+ return entries.map((entry) => {
8
+ const failure = failures[entry.identity];
9
+ return failure
10
+ ? {
11
+ ...entry,
12
+ lastError: {
13
+ at: failure.at,
14
+ attempts: failure.attempts,
15
+ reason: failure.reason,
16
+ message: failure.message,
17
+ },
18
+ }
19
+ : entry;
20
+ });
21
+ }
22
+ /** Pi's own rule (`getPackageSourceString`, package-manager.js): a settings package entry is
23
+ * either the source string itself, or an object carrying it under `.source` (the user's own
24
+ * per-package filter form). */
25
+ function packageSourceString(entry) {
26
+ if (typeof entry === "string")
27
+ return entry;
28
+ if (entry && typeof entry === "object" && "source" in entry && typeof entry.source === "string") {
29
+ return entry.source;
30
+ }
31
+ return undefined;
32
+ }
33
+ /** Find the settings entry matching `identity`, string or object form, without ever touching
34
+ * entries that don't belong to us (unparseable/local-path entries are simply skipped). */
35
+ function findByIdentity(settingsPackages, identity) {
36
+ for (const raw of settingsPackages) {
37
+ const sourceString = packageSourceString(raw);
38
+ if (sourceString === undefined)
39
+ continue;
40
+ try {
41
+ if (identityOf(sourceString) === identity) {
42
+ return { isObjectForm: typeof raw !== "string", sourceString };
43
+ }
44
+ }
45
+ catch {
46
+ continue; // Not an npm:/git: spec we understand — not one of ours, never examined.
47
+ }
48
+ }
49
+ return undefined;
50
+ }
51
+ export function planSync(input) {
52
+ const { catalog, packs, state, settingsPackages } = input;
53
+ const { entries: selected } = selectEntries(catalog, packs);
54
+ const actions = [];
55
+ const entries = [];
56
+ for (const { pack, entry } of selected) {
57
+ const identity = identityOf(entry.source);
58
+ const base = { identity, pack, source: entry.source, addedIn: entry.addedIn };
59
+ if (entry.deprecated) {
60
+ entries.push({ ...base, deprecated: true, status: "deprecated" });
61
+ continue; // Tombstone: no action, ever — checked first, ahead of any offered/failure lookup.
62
+ }
63
+ if (state === "unreadable") {
64
+ // Fail-safe: plan zero actions. Statuses are unreliable in this mode by design — report as
65
+ // if everything were already offered (wrong-and-quiet), never as "pending" (which would
66
+ // wrongly imply the next sync retries it).
67
+ entries.push({ ...base, status: "installed" });
68
+ continue;
69
+ }
70
+ const offered = state.offered[identity];
71
+ if (!offered) {
72
+ // Never install over an entry the user already has, even one Pi-Studio never offered: pi's
73
+ // own `addSourceToSettings` matches by version-insensitive identity and rewrites an existing
74
+ // entry in place (package-manager.js's `packageSourcesMatch`/`getSourceMatchKeyForSettings`),
75
+ // so `pi install npm:<name>` against a user's own `npm:<name>@1.2.3` would silently destroy
76
+ // their pin. Adopt it as theirs instead of installing over it — tenet 1, no exceptions for
77
+ // "we never touched this one yet".
78
+ const preexisting = findByIdentity(settingsPackages, identity);
79
+ if (preexisting) {
80
+ entries.push({ ...base, status: "user_modified" });
81
+ continue;
82
+ }
83
+ actions.push({ identity, pack, source: entry.source });
84
+ const failed = Boolean(state.failures[identity]);
85
+ entries.push({ ...base, status: failed ? "failed" : "pending" });
86
+ continue;
87
+ }
88
+ const match = findByIdentity(settingsPackages, identity);
89
+ let status;
90
+ if (!match) {
91
+ status = "user_removed"; // Offered, absent from settings — a `pi remove` sticks forever.
92
+ }
93
+ else if (match.isObjectForm || match.sourceString !== offered.installedSpec) {
94
+ status = "user_modified"; // Any edit — repin, object-filter form — permanently theirs.
95
+ }
96
+ else {
97
+ status = "installed";
98
+ }
99
+ entries.push({ ...base, status });
100
+ }
101
+ return { actions, entries };
102
+ }
103
+ //# sourceMappingURL=sync-planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-planner.js","sourceRoot":"","sources":["../../src/extensions/sync-planner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ExtensionPackInfo } from "@av-pi-studio/protocol";
2
+ import type { CuratedPackCatalog } from "./curated-packs.js";
3
+ import type { DescribedEntry } from "./sync-planner.js";
4
+ /** Groups the planner's flat `entries` by pack, in manifest order, and attaches each pack's
5
+ * `title`/`description` from the catalog — the wire shape groups by pack, the planner doesn't. */
6
+ export declare function toExtensionPackInfoList(catalog: CuratedPackCatalog, entries: readonly DescribedEntry[]): ExtensionPackInfo[];
7
+ //# sourceMappingURL=wire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/extensions/wire.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEpF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAqC,MAAM,mBAAmB,CAAC;AAmC3F;mGACmG;AACnG,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,SAAS,cAAc,EAAE,GACjC,iBAAiB,EAAE,CAerB"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Maps `DescribedEntry[]` (the planner's own output, `lastError`-attached) to the wire's
3
+ * `ExtensionPackInfo[]` shape (task-001) — the ONE implementation, shared by the daemon's
4
+ * `extension_packs_list_request`/`_set_request` handlers (`extensions-rpc.ts`) and the CLI's
5
+ * `extensions list --local` (sprint-057/task-005), so the two can never render different data for
6
+ * the same state.
7
+ */
8
+ /**
9
+ * Total mapping from the planner's `EntryStatus` to the wire's (plain-string) status. A `Record`
10
+ * keyed by the imported planner type means a build error, not a silent runtime gap, the moment
11
+ * either side gains a value the other doesn't handle.
12
+ */
13
+ const ENTRY_STATUS_TO_WIRE = {
14
+ installed: "installed",
15
+ pending: "pending",
16
+ failed: "failed",
17
+ user_removed: "user_removed",
18
+ user_modified: "user_modified",
19
+ deprecated: "deprecated",
20
+ };
21
+ function toWireEntry(entry) {
22
+ return {
23
+ source: entry.source,
24
+ identity: entry.identity,
25
+ addedIn: entry.addedIn,
26
+ ...(entry.deprecated !== undefined ? { deprecated: entry.deprecated } : {}),
27
+ status: ENTRY_STATUS_TO_WIRE[entry.status],
28
+ ...(entry.lastError ? { lastError: entry.lastError } : {}),
29
+ };
30
+ }
31
+ /** Groups the planner's flat `entries` by pack, in manifest order, and attaches each pack's
32
+ * `title`/`description` from the catalog — the wire shape groups by pack, the planner doesn't. */
33
+ export function toExtensionPackInfoList(catalog, entries) {
34
+ const byPack = new Map();
35
+ for (const entry of entries) {
36
+ const list = byPack.get(entry.pack);
37
+ if (list)
38
+ list.push(entry);
39
+ else
40
+ byPack.set(entry.pack, [entry]);
41
+ }
42
+ return Object.entries(catalog)
43
+ .filter(([slug]) => byPack.has(slug))
44
+ .map(([slug, pack]) => ({
45
+ id: slug,
46
+ title: pack.title,
47
+ description: pack.description,
48
+ packages: (byPack.get(slug) ?? []).map(toWireEntry),
49
+ }));
50
+ }
51
+ //# sourceMappingURL=wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../src/extensions/wire.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AAEH;;;;GAIG;AACH,MAAM,oBAAoB,GAAuC;IAC/D,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;IAChB,YAAY,EAAE,cAAc;IAC5B,aAAa,EAAE,eAAe;IAC9B,UAAU,EAAE,YAAY;CACzB,CAAC;AAEF,SAAS,WAAW,CAAC,KAAqB;IACxC,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,MAAM,EAAE,oBAAoB,CAAC,KAAK,CAAC,MAAM,CAAC;QAC1C,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC3D,CAAC;AACJ,CAAC;AAED;mGACmG;AACnG,MAAM,UAAU,uBAAuB,CACrC,OAA2B,EAC3B,OAAkC;IAElC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;IACnD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;YACtB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;SAC3B,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;SACpC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,EAAE,EAAE,IAAI;QACR,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,QAAQ,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC;KACpD,CAAC,CAAC,CAAC;AACR,CAAC"}
package/dist/index.d.ts CHANGED
@@ -15,4 +15,5 @@ export * from "./files/index.js";
15
15
  export * from "./orchestration/index.js";
16
16
  export * from "./logging/index.js";
17
17
  export * from "./util/index.js";
18
+ export * from "./extensions/index.js";
18
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,EAAG,sBAA+B,CAAC;AAE9D,4EAA4E;AAC5E,eAAO,MAAM,WAAW,gEAAiD,CAAC;AAG1E,cAAc,wBAAwB,CAAC;AAGvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,eAAe,CAAC;AAG9B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,0BAA0B,CAAC;AAGzC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,EAAG,sBAA+B,CAAC;AAE9D,4EAA4E;AAC5E,eAAO,MAAM,WAAW,gEAAiD,CAAC;AAG1E,cAAc,wBAAwB,CAAC;AAGvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,eAAe,CAAC;AAG9B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,0BAA0B,CAAC;AAGzC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,iBAAiB,CAAC;AAIhC,cAAc,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -32,4 +32,7 @@ export * from "./orchestration/index.js";
32
32
  export * from "./logging/index.js";
33
33
  // Shared utilities (bounded-concurrency helpers).
34
34
  export * from "./util/index.js";
35
+ // Preinstalled-extensions sync engine: pure planning surface only (manifest, planner, state
36
+ // reader) — see extensions/index.ts for why the orchestration/executor modules are excluded.
37
+ export * from "./extensions/index.js";
35
38
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,MAAM,CAAC,MAAM,cAAc,GAAG,sBAA+B,CAAC;AAE9D,4EAA4E;AAC5E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,gBAAgB,EAAE,iBAAiB,CAAU,CAAC;AAE1E,wEAAwE;AACxE,cAAc,wBAAwB,CAAC;AAEvC,mEAAmE;AACnE,cAAc,mBAAmB,CAAC;AAElC,+BAA+B;AAC/B,cAAc,mBAAmB,CAAC;AAElC,6DAA6D;AAC7D,cAAc,iBAAiB,CAAC;AAEhC,6DAA6D;AAC7D,cAAc,iBAAiB,CAAC;AAEhC,yEAAyE;AACzE,cAAc,eAAe,CAAC;AAE9B,8DAA8D;AAC9D,cAAc,kBAAkB,CAAC;AAEjC,kEAAkE;AAClE,cAAc,qBAAqB,CAAC;AAEpC,wCAAwC;AACxC,cAAc,qBAAqB,CAAC;AAEpC,sDAAsD;AACtD,cAAc,kBAAkB,CAAC;AAEjC,wCAAwC;AACxC,cAAc,kBAAkB,CAAC;AAEjC,0DAA0D;AAC1D,cAAc,0BAA0B,CAAC;AAEzC,8CAA8C;AAC9C,cAAc,oBAAoB,CAAC;AAEnC,kDAAkD;AAClD,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,MAAM,CAAC,MAAM,cAAc,GAAG,sBAA+B,CAAC;AAE9D,4EAA4E;AAC5E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,gBAAgB,EAAE,iBAAiB,CAAU,CAAC;AAE1E,wEAAwE;AACxE,cAAc,wBAAwB,CAAC;AAEvC,mEAAmE;AACnE,cAAc,mBAAmB,CAAC;AAElC,+BAA+B;AAC/B,cAAc,mBAAmB,CAAC;AAElC,6DAA6D;AAC7D,cAAc,iBAAiB,CAAC;AAEhC,6DAA6D;AAC7D,cAAc,iBAAiB,CAAC;AAEhC,yEAAyE;AACzE,cAAc,eAAe,CAAC;AAE9B,8DAA8D;AAC9D,cAAc,kBAAkB,CAAC;AAEjC,kEAAkE;AAClE,cAAc,qBAAqB,CAAC;AAEpC,wCAAwC;AACxC,cAAc,qBAAqB,CAAC;AAEpC,sDAAsD;AACtD,cAAc,kBAAkB,CAAC;AAEjC,wCAAwC;AACxC,cAAc,kBAAkB,CAAC;AAEjC,0DAA0D;AAC1D,cAAc,0BAA0B,CAAC;AAEzC,8CAA8C;AAC9C,cAAc,oBAAoB,CAAC;AAEnC,kDAAkD;AAClD,cAAc,iBAAiB,CAAC;AAEhC,4FAA4F;AAC5F,6FAA6F;AAC7F,cAAc,uBAAuB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@av-pi-studio/server",
3
- "version": "0.0.72",
3
+ "version": "0.0.74",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -22,9 +22,9 @@
22
22
  "clean": "rm -rf dist *.tsbuildinfo"
23
23
  },
24
24
  "dependencies": {
25
- "@av-pi-studio/highlight": "^0.0.72",
26
- "@av-pi-studio/protocol": "^0.0.72",
27
- "@av-pi-studio/relay": "^0.0.72",
25
+ "@av-pi-studio/highlight": "^0.0.74",
26
+ "@av-pi-studio/protocol": "^0.0.74",
27
+ "@av-pi-studio/relay": "^0.0.74",
28
28
  "@earendil-works/pi-coding-agent": "^0.84.1",
29
29
  "@xterm/headless": "^6.0.0",
30
30
  "bcryptjs": "^3.0.3",