@saadnvd1/agent-os 0.3.680 → 0.3.691
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/README.md +2 -2
- package/app/api/dev-servers/detect/route.ts +1 -2
- package/app/api/projects/[id]/dev-servers/[dsId]/route.ts +2 -4
- package/app/page.tsx +1 -2
- package/components/Artifacts/ArtifactsPanel.tsx +1 -1
- package/components/FileExplorer/FileTabs.tsx +1 -1
- package/components/Pane/DesktopTabBar.tsx +2 -2
- package/components/QuickSwitcher.tsx +24 -21
- package/components/SessionList/SessionRowMenu.tsx +0 -1
- package/components/Settings/ExperimentsSection.test.ts +7 -1
- package/components/Settings/NoApprovalsSection.test.ts +7 -1
- package/components/Terminal/TerminalToolbar.tsx +1 -1
- package/components/Terminal/VirtualKeyboard.tsx +1 -1
- package/components/WorkerCard.tsx +1 -7
- package/components/Workspaces/WorkspaceNameDialog.tsx +5 -3
- package/components/a/AButton.tsx +1 -6
- package/components/a/AIconButton.tsx +1 -5
- package/contexts/PaneContext.tsx +1 -6
- package/data/lumifyhub/index.ts +2 -4
- package/hooks/useDirectoryBrowser.ts +5 -4
- package/hooks/useDrawerAnimation.ts +11 -14
- package/hooks/useFileDrop.ts +2 -6
- package/hooks/useKeybarVisibility.ts +10 -29
- package/hooks/useSidebarPinned.ts +7 -23
- package/hooks/useSpeechRecognition.ts +19 -5
- package/hooks/useStoredFlag.ts +47 -0
- package/hooks/useViewport.ts +11 -17
- package/lib/approvals/no-approvals.ts +1 -7
- package/lib/artifacts/store.ts +1 -2
- package/lib/away/report.ts +1 -2
- package/lib/away/run.ts +1 -2
- package/lib/away/state.ts +1 -2
- package/lib/bookmarks/store.ts +1 -2
- package/lib/bus/delivery.ts +1 -2
- package/lib/bus/index.ts +1 -2
- package/lib/chat/compact-context.ts +1 -2
- package/lib/chat/stop.ts +1 -2
- package/lib/chat/worker/host.ts +1 -1
- package/lib/code-health/reports.ts +39 -3
- package/lib/code-health/source.ts +1 -1
- package/lib/db/lumifyhub.ts +2 -4
- package/lib/db/schedules-move.ts +1 -2
- package/lib/db/session-parts/tasks.ts +1 -2
- package/lib/db/stacks.ts +3 -9
- package/lib/db/types.ts +1 -7
- package/lib/dev-instance/spawnServer.ts +10 -6
- package/lib/doctor/checks.ts +8 -10
- package/lib/done/archive.ts +1 -2
- package/lib/env-setup.ts +2 -0
- package/lib/experiments/meter.ts +1 -7
- package/lib/files.ts +1 -0
- package/lib/health/env/classes.ts +0 -2
- package/lib/health/env/recordHit.ts +1 -2
- package/lib/health/spin.ts +1 -4
- package/lib/health/store.ts +1 -2
- package/lib/hosts/index.ts +1 -2
- package/lib/hosts/peer-sync.ts +2 -4
- package/lib/hosts/remote-api.ts +1 -2
- package/lib/lumifyhub/types.ts +2 -8
- package/lib/notify/index.ts +1 -2
- package/lib/notify/push/index.ts +2 -7
- package/lib/orchestrator/brief.ts +1 -1
- package/lib/orchestrator/external-pr.ts +1 -2
- package/lib/orchestrator/gates.ts +1 -6
- package/lib/orchestrator/needs-you-items.ts +16 -20
- package/lib/orchestrator/pr-wake.ts +1 -2
- package/lib/orchestrator/rebase.ts +1 -2
- package/lib/orchestrator/review.ts +1 -2
- package/lib/orchestrator/usage.ts +1 -2
- package/lib/plans/claim.ts +1 -2
- package/lib/plans/features.ts +4 -11
- package/lib/plans/release.ts +2 -4
- package/lib/ports.ts +1 -2
- package/lib/program-status/records.ts +1 -1
- package/lib/project-config/database.ts +1 -2
- package/lib/projects.ts +1 -2
- package/lib/recipes/settle.ts +2 -4
- package/lib/recipes/store.ts +2 -4
- package/lib/schedules/store.ts +2 -4
- package/lib/search/conversations/backfill.ts +2 -4
- package/lib/security/auth.ts +1 -2
- package/lib/security/passkeys.ts +1 -2
- package/lib/security/presence.ts +1 -7
- package/lib/session-artifacts/types.ts +1 -8
- package/lib/session-audit/transcript.ts +1 -3
- package/lib/session-logs/index.ts +1 -2
- package/lib/sessions/launch.ts +1 -2
- package/lib/share/pdf.ts +32 -1
- package/lib/share/snapshot.ts +1 -7
- package/lib/share/store.ts +1 -2
- package/lib/sidebar/orchestrator-row.ts +1 -6
- package/lib/sidebar/row-menu.ts +1 -2
- package/lib/sidebar/shelves.ts +1 -6
- package/lib/simulator/index.ts +1 -2
- package/lib/simulator/lease.ts +1 -5
- package/lib/stacks/index.ts +1 -1
- package/lib/stacks/plan.ts +1 -1
- package/lib/stacks/restack.ts +1 -2
- package/lib/stacks/tick.ts +1 -2
- package/lib/status/socket.ts +1 -2
- package/lib/tasks/contract/authorize.ts +1 -2
- package/lib/tasks/contract/plan.ts +1 -2
- package/lib/tasks/contract/sync.ts +1 -2
- package/lib/tasks/holds.ts +1 -5
- package/lib/tasks/index.ts +1 -2
- package/lib/tasks/loop-types.ts +1 -6
- package/lib/tasks/move-guard.ts +1 -2
- package/lib/tasks/move.ts +1 -2
- package/lib/tasks/moved-away.ts +1 -2
- package/lib/tasks/pause-hold.ts +1 -2
- package/lib/tasks/pause.ts +3 -6
- package/lib/tasks/pr-activity.ts +1 -3
- package/lib/tasks/queue-rows.ts +2 -4
- package/lib/tasks/queue-types.ts +1 -5
- package/lib/tasks/queue.ts +1 -2
- package/lib/tasks/refresh-base.ts +1 -2
- package/lib/tasks/setup.ts +9 -2
- package/lib/tasks/start.ts +2 -4
- package/lib/tasks/starter.ts +1 -2
- package/lib/tasks/state.ts +1 -6
- package/lib/theme-config.ts +1 -8
- package/lib/updates/banner.ts +1 -6
- package/lib/updates/run.ts +1 -2
- package/lib/usage/aggregate.ts +1 -1
- package/lib/usage/nightly.ts +1 -2
- package/lib/usage/rate-limits.ts +1 -2
- package/lib/usage/turns.ts +1 -2
- package/lib/usage/windows.ts +5 -7
- package/lib/workspaces.ts +1 -2
- package/lib/worktree-deps/clone.ts +10 -0
- package/lib/worktree-deps/fastcopy.ts +8 -1
- package/lib/worktree-deps/index.ts +14 -19
- package/lib/worktree-deps/install.ts +17 -24
- package/lib/worktree-sweep/databases.ts +0 -1
- package/package.json +10 -17
- package/scripts/check-native.mjs +86 -0
- package/scripts/code-health/prepass.ts +3 -3
- package/scripts/install-deps +24 -0
- package/scripts/lib/commands.sh +2 -2
- package/scripts/lib/deps-swap.sh +68 -0
- package/scripts/lib/deps-swap.test.ts +115 -0
- package/scripts/lib/pnpm.sh +36 -0
- package/scripts/lib/prerequisites.sh +9 -9
- package/scripts/publish +3 -2
- package/scripts/redeploy +25 -8
- package/scripts/setup.sh +4 -4
- package/tsconfig.json +1 -0
- package/scripts/ensure-native.mjs +0 -23
package/lib/tasks/pause.ts
CHANGED
|
@@ -74,13 +74,11 @@ const nudge = () =>
|
|
|
74
74
|
|
|
75
75
|
const sessionOf = (id: string) =>
|
|
76
76
|
db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
|
|
77
|
-
|
|
|
78
|
-
| undefined;
|
|
77
|
+
Session | undefined;
|
|
79
78
|
|
|
80
79
|
const pauseRow = (id: number) =>
|
|
81
80
|
db.prepare(`SELECT * FROM task_pauses WHERE id = ?`).get(id) as
|
|
82
|
-
|
|
|
83
|
-
| undefined;
|
|
81
|
+
TaskPause | undefined;
|
|
84
82
|
|
|
85
83
|
// SQLite's datetime('now') is UTC without a zone.
|
|
86
84
|
const ms = (v: string | null | undefined) =>
|
|
@@ -400,8 +398,7 @@ export function setPauseNote(sessionId: string, note: string | null): void {
|
|
|
400
398
|
}
|
|
401
399
|
|
|
402
400
|
export type ResumeOutcome =
|
|
403
|
-
|
|
404
|
-
| { outcome: "held"; note: string };
|
|
401
|
+
{ outcome: "resumed" | "cancelled" } | { outcome: "held"; note: string };
|
|
405
402
|
|
|
406
403
|
// A deliberate Resume, by a person or the orchestrator: starts at once, past
|
|
407
404
|
// the slot limit, like Start now. A machine in the red still holds it: it is
|
package/lib/tasks/pr-activity.ts
CHANGED
|
@@ -59,9 +59,7 @@ export async function fingerprints(
|
|
|
59
59
|
);
|
|
60
60
|
for (const n of prs) {
|
|
61
61
|
const pr = res.data?.repository?.[`p${n}`] as
|
|
62
|
-
|
|
63
|
-
| null
|
|
64
|
-
| undefined;
|
|
62
|
+
{ createdAt: string; reviews: Last; comments: Last } | null | undefined;
|
|
65
63
|
if (!pr) continue;
|
|
66
64
|
const part = (l: Last) => `${l.totalCount}:${l.nodes[0]?.id ?? ""}`;
|
|
67
65
|
out.set(n, {
|
package/lib/tasks/queue-rows.ts
CHANGED
|
@@ -14,13 +14,11 @@ export const IN_FLIGHT = ["running", "moving"];
|
|
|
14
14
|
|
|
15
15
|
export const row = (id: string) =>
|
|
16
16
|
db.prepare(`SELECT * FROM task_queue WHERE id = ?`).get(id) as
|
|
17
|
-
|
|
|
18
|
-
| undefined;
|
|
17
|
+
QueueRow | undefined;
|
|
19
18
|
|
|
20
19
|
export const sessionOf = (id: string) =>
|
|
21
20
|
db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
|
|
22
|
-
|
|
|
23
|
-
| undefined;
|
|
21
|
+
Session | undefined;
|
|
24
22
|
|
|
25
23
|
export function patch(id: string, fields: Partial<QueueRow>): void {
|
|
26
24
|
const keys = Object.keys(fields);
|
package/lib/tasks/queue-types.ts
CHANGED
|
@@ -6,11 +6,7 @@ import type { Priority, WaitCode } from "./schedule-types";
|
|
|
6
6
|
import type { StartVia } from "./starter";
|
|
7
7
|
|
|
8
8
|
export type QueueStatus =
|
|
9
|
-
| "
|
|
10
|
-
| "starting"
|
|
11
|
-
| "started"
|
|
12
|
-
| "failed"
|
|
13
|
-
| "removed";
|
|
9
|
+
"queued" | "starting" | "started" | "failed" | "removed";
|
|
14
10
|
|
|
15
11
|
export interface QueueRow {
|
|
16
12
|
id: string;
|
package/lib/tasks/queue.ts
CHANGED
|
@@ -313,8 +313,7 @@ export function moveQueued(id: string, by: -1 | 1): void {
|
|
|
313
313
|
ORDER BY q.position ${by < 0 ? "DESC" : "ASC"} LIMIT 1`
|
|
314
314
|
)
|
|
315
315
|
.get({ position: r.position, ws: workspaceId, project: r.project_id }) as
|
|
316
|
-
|
|
|
317
|
-
| undefined;
|
|
316
|
+
QueueRow | undefined;
|
|
318
317
|
if (!other) return;
|
|
319
318
|
patch(r.id, { position: other.position });
|
|
320
319
|
patch(other.id, { position: r.position });
|
|
@@ -16,8 +16,7 @@ import { restackBranch, outputOf, type Runner } from "../stacks/git";
|
|
|
16
16
|
import { run } from "./gh";
|
|
17
17
|
|
|
18
18
|
export type RefreshResult =
|
|
19
|
-
|
|
20
|
-
| { moved: true; how: "reset" | "rebase" };
|
|
19
|
+
{ moved: false; note?: string } | { moved: true; how: "reset" | "rebase" };
|
|
21
20
|
|
|
22
21
|
export interface RefreshTarget {
|
|
23
22
|
name: string;
|
package/lib/tasks/setup.ts
CHANGED
|
@@ -20,8 +20,10 @@ const tail = (s: string, n: number) => (s.length > n ? `...${s.slice(-n)}` : s);
|
|
|
20
20
|
export function setupOutcome(result: SetupResult | Error): TaskSetup {
|
|
21
21
|
if (result instanceof Error)
|
|
22
22
|
return { status: "failed", ms: null, error: redact(result.message) };
|
|
23
|
-
if (result.success)
|
|
24
|
-
|
|
23
|
+
if (result.success) {
|
|
24
|
+
const warning = result.steps.find((s) => s.warning)?.warning;
|
|
25
|
+
return { status: "ok", ms: result.durationMs, error: warning ?? null };
|
|
26
|
+
}
|
|
25
27
|
const failed = result.steps.filter((s) => !s.success);
|
|
26
28
|
const last = failed[failed.length - 1];
|
|
27
29
|
const error = last
|
|
@@ -31,6 +33,11 @@ export function setupOutcome(result: SetupResult | Error): TaskSetup {
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export function setupNote(setup: TaskSetup): string {
|
|
36
|
+
if (setup.status === "ok" && setup.error)
|
|
37
|
+
return `
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
Note from AgentOS: ${setup.error}`;
|
|
34
41
|
if (setup.status !== "failed") return "";
|
|
35
42
|
return `
|
|
36
43
|
|
package/lib/tasks/start.ts
CHANGED
|
@@ -60,8 +60,7 @@ export function launchHold(sessionId: string): string | null {
|
|
|
60
60
|
LIMIT 1`
|
|
61
61
|
)
|
|
62
62
|
.get(sessionId, sessionId, sessionId) as
|
|
63
|
-
|
|
64
|
-
| undefined;
|
|
63
|
+
{ workspace_id: string } | undefined;
|
|
65
64
|
if (!row) return null;
|
|
66
65
|
if (isPaused(row.workspace_id)) return "the orchestrator is paused by Saad";
|
|
67
66
|
const window = brakesEnabled() ? windowRefusal(readUsage()) : null;
|
|
@@ -135,8 +134,7 @@ export async function finishTaskStart(
|
|
|
135
134
|
const now = db
|
|
136
135
|
.prepare(`SELECT task_status, archived_at FROM session_rows WHERE id = ?`)
|
|
137
136
|
.get(sessionId) as
|
|
138
|
-
|
|
139
|
-
| undefined;
|
|
137
|
+
{ task_status: string | null; archived_at: string | null } | undefined;
|
|
140
138
|
if (now?.task_status !== "running" || now.archived_at)
|
|
141
139
|
return done({
|
|
142
140
|
status: "failed",
|
package/lib/tasks/starter.ts
CHANGED
|
@@ -102,8 +102,7 @@ export function orchestratorOf(startedBy: string | null | undefined) {
|
|
|
102
102
|
const row = db
|
|
103
103
|
.prepare(`SELECT role, started_by FROM sessions WHERE id = ?`)
|
|
104
104
|
.get(id) as
|
|
105
|
-
|
|
106
|
-
| undefined;
|
|
105
|
+
{ role: string | null; started_by: string | null } | undefined;
|
|
107
106
|
if (!row) return null;
|
|
108
107
|
if (row.role === "orchestrator") return id;
|
|
109
108
|
id = row.started_by;
|
package/lib/tasks/state.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import type { AttributionKind } from "./code-review";
|
|
2
2
|
|
|
3
3
|
export type TaskStatus =
|
|
4
|
-
| "
|
|
5
|
-
| "merged"
|
|
6
|
-
| "dropped"
|
|
7
|
-
| "done"
|
|
8
|
-
| "moving"
|
|
9
|
-
| "moved";
|
|
4
|
+
"running" | "merged" | "dropped" | "done" | "moving" | "moved";
|
|
10
5
|
|
|
11
6
|
export type ChecksVerdict = "pass" | "fail" | "pending" | "none";
|
|
12
7
|
|
package/lib/theme-config.ts
CHANGED
|
@@ -30,14 +30,7 @@ export type DarkThemeVariant =
|
|
|
30
30
|
| "ocean"
|
|
31
31
|
| "mocha";
|
|
32
32
|
export type LightThemeVariant =
|
|
33
|
-
| "
|
|
34
|
-
| "warm"
|
|
35
|
-
| "cool"
|
|
36
|
-
| "soft"
|
|
37
|
-
| "rose"
|
|
38
|
-
| "lavender"
|
|
39
|
-
| "mint"
|
|
40
|
-
| "peach";
|
|
33
|
+
"default" | "warm" | "cool" | "soft" | "rose" | "lavender" | "mint" | "peach";
|
|
41
34
|
export type Theme =
|
|
42
35
|
| "light"
|
|
43
36
|
| `light-${LightThemeVariant}`
|
package/lib/updates/banner.ts
CHANGED
|
@@ -68,12 +68,7 @@ export interface UpdateView {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export type BannerState =
|
|
71
|
-
| "
|
|
72
|
-
| "available"
|
|
73
|
-
| "pending"
|
|
74
|
-
| "running"
|
|
75
|
-
| "failed"
|
|
76
|
-
| "built";
|
|
71
|
+
"hidden" | "available" | "pending" | "running" | "failed" | "built";
|
|
77
72
|
|
|
78
73
|
export const DISMISSED_VERSION_KEY = "agentos.update.dismissed";
|
|
79
74
|
export const DISMISSED_RUN_KEY = "agentos.update.dismissedRun";
|
package/lib/updates/run.ts
CHANGED
|
@@ -264,8 +264,7 @@ export async function preflight(
|
|
|
264
264
|
// ── starting ─────────────────────────────────────────────────────────────
|
|
265
265
|
|
|
266
266
|
export type StartResult =
|
|
267
|
-
|
|
268
|
-
| ({ ok: false } & Refusal);
|
|
267
|
+
{ ok: true; pending: boolean } | ({ ok: false } & Refusal);
|
|
269
268
|
|
|
270
269
|
/** Takes the right to start, atomically, so two requests can't both run. */
|
|
271
270
|
function claimStart(deps: Pick<RunDeps, "stateFile" | "now">): boolean {
|
package/lib/usage/aggregate.ts
CHANGED
|
@@ -76,7 +76,7 @@ export function aggregateUsage(
|
|
|
76
76
|
const since = rangeStart(range, now);
|
|
77
77
|
const total = acc();
|
|
78
78
|
const days = new Map<string, Acc>();
|
|
79
|
-
for (let t = since; t <= now;
|
|
79
|
+
for (let t = since; t <= now;) {
|
|
80
80
|
days.set(dayKey(t), acc());
|
|
81
81
|
const next = new Date(t);
|
|
82
82
|
next.setDate(next.getDate() + 1);
|
package/lib/usage/nightly.ts
CHANGED
|
@@ -45,8 +45,7 @@ const addDays = (day: string, n: number) =>
|
|
|
45
45
|
|
|
46
46
|
function setting(key: string): string | null {
|
|
47
47
|
const r = db.prepare(`SELECT value FROM settings WHERE key = ?`).get(key) as
|
|
48
|
-
|
|
49
|
-
| undefined;
|
|
48
|
+
{ value: string } | undefined;
|
|
50
49
|
return r?.value ?? null;
|
|
51
50
|
}
|
|
52
51
|
|
package/lib/usage/rate-limits.ts
CHANGED
|
@@ -160,8 +160,7 @@ export function lastReading(
|
|
|
160
160
|
WHERE window = ? AND at <= ? ORDER BY at DESC, id DESC LIMIT 1`
|
|
161
161
|
)
|
|
162
162
|
.get(window, before) as
|
|
163
|
-
|
|
164
|
-
| undefined;
|
|
163
|
+
{ at: number; utilization: number; resets_at: number | null } | undefined;
|
|
165
164
|
return r
|
|
166
165
|
? { at: r.at, utilization: r.utilization, resetsAt: r.resets_at }
|
|
167
166
|
: null;
|
package/lib/usage/turns.ts
CHANGED
|
@@ -111,8 +111,7 @@ export function recordCompaction(
|
|
|
111
111
|
const current = db
|
|
112
112
|
.prepare(`SELECT name, workspace_id FROM sessions WHERE id = ?`)
|
|
113
113
|
.get(session.id) as
|
|
114
|
-
|
|
115
|
-
| undefined;
|
|
114
|
+
{ name: string; workspace_id: string | null } | undefined;
|
|
116
115
|
db.prepare(
|
|
117
116
|
`INSERT INTO chat_compactions (session_id, session_name, workspace_id, at, trigger, phase, before_tokens, after_tokens)
|
|
118
117
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
package/lib/usage/windows.ts
CHANGED
|
@@ -101,13 +101,11 @@ async function planLimits(): Promise<Cached> {
|
|
|
101
101
|
if (cache && Date.now() - cache.at < TTL_MS) return cache;
|
|
102
102
|
inflight ??= askAgent()
|
|
103
103
|
.then((limits): Cached => ({ at: Date.now(), limits }))
|
|
104
|
-
.catch(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
})
|
|
110
|
-
)
|
|
104
|
+
.catch((error: unknown): Cached => ({
|
|
105
|
+
at: Date.now(),
|
|
106
|
+
limits: null,
|
|
107
|
+
error: error instanceof Error ? error.message : String(error),
|
|
108
|
+
}))
|
|
111
109
|
.then((c) => (cache = c))
|
|
112
110
|
.finally(() => {
|
|
113
111
|
inflight = null;
|
package/lib/workspaces.ts
CHANGED
|
@@ -53,8 +53,7 @@ export function listWorkspaces(): Workspace[] {
|
|
|
53
53
|
|
|
54
54
|
export function getWorkspace(id: string): Workspace | null {
|
|
55
55
|
const row = db.prepare(`SELECT * FROM workspaces WHERE id = ?`).get(id) as
|
|
56
|
-
|
|
|
57
|
-
| undefined;
|
|
56
|
+
WorkspaceRow | undefined;
|
|
58
57
|
return row ? toWorkspace(row) : null;
|
|
59
58
|
}
|
|
60
59
|
|
|
@@ -236,6 +236,16 @@ export async function cloneDeps(opts: {
|
|
|
236
236
|
// not, and cloning it would hand the worktree missing dependencies. Other
|
|
237
237
|
// package managers have no hidden lockfile to check, so they pass.
|
|
238
238
|
export async function nodeModulesMatchLock(dir: string): Promise<boolean> {
|
|
239
|
+
// pnpm copies the lockfile it installed from into node_modules/.pnpm.
|
|
240
|
+
const pnpmLock = await fs.promises
|
|
241
|
+
.readFile(path.join(dir, "pnpm-lock.yaml"), "utf-8")
|
|
242
|
+
.catch(() => null);
|
|
243
|
+
if (pnpmLock !== null) {
|
|
244
|
+
const installed = await fs.promises
|
|
245
|
+
.readFile(path.join(dir, "node_modules", ".pnpm", "lock.yaml"), "utf-8")
|
|
246
|
+
.catch(() => null);
|
|
247
|
+
return installed === pnpmLock;
|
|
248
|
+
}
|
|
239
249
|
const read = (file: string) =>
|
|
240
250
|
fs.promises
|
|
241
251
|
.readFile(file, "utf-8")
|
|
@@ -22,7 +22,14 @@ export const STORE_ROOT = agentosHome("deps-store");
|
|
|
22
22
|
|
|
23
23
|
// Files tools rewrite in place. A hardlink would carry the write back into
|
|
24
24
|
// the store, so these are real copies in every worktree.
|
|
25
|
-
export const MUTABLE_IN_MODULES = [
|
|
25
|
+
export const MUTABLE_IN_MODULES = [
|
|
26
|
+
".package-lock.json",
|
|
27
|
+
".modules.yaml",
|
|
28
|
+
".pnpm/lock.yaml",
|
|
29
|
+
".pnpm-workspace-state-v1.json",
|
|
30
|
+
".cache",
|
|
31
|
+
".vite",
|
|
32
|
+
];
|
|
26
33
|
|
|
27
34
|
const probed = new Map<string, boolean>();
|
|
28
35
|
|
|
@@ -7,7 +7,7 @@ import { exec } from "child_process";
|
|
|
7
7
|
import { promisify } from "util";
|
|
8
8
|
import type { SetupResult } from "../env-setup";
|
|
9
9
|
import { NO_FAST_COPY, cloneDeps } from "./clone";
|
|
10
|
-
import { detectPackageManager, setupEnv } from "./install";
|
|
10
|
+
import { detectPackageManager, setupEnv, shellCommand } from "./install";
|
|
11
11
|
|
|
12
12
|
const execAsync = promisify(exec);
|
|
13
13
|
|
|
@@ -47,8 +47,8 @@ export async function runCommand(
|
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Clone the main checkout's node_modules (or exactly agentos.json's `clone`
|
|
50
|
-
* paths), or else install: the lockfile's frozen install first, then
|
|
51
|
-
* plain one.
|
|
50
|
+
* paths), or else install: the lockfile's frozen install first, then (for
|
|
51
|
+
* a project other than AgentOS) a plain one.
|
|
52
52
|
*/
|
|
53
53
|
export async function bringDependencies(
|
|
54
54
|
result: SetupResult,
|
|
@@ -81,20 +81,15 @@ export async function bringDependencies(
|
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
if (run.success) return;
|
|
97
|
-
error = run.error;
|
|
98
|
-
}
|
|
99
|
-
if (error !== undefined) result.success = false;
|
|
84
|
+
const run = await deps.run(shellCommand(pm.install), worktreePath, envVars);
|
|
85
|
+
result.steps.push({
|
|
86
|
+
name: `Install dependencies (${pm.name})`,
|
|
87
|
+
command: pm.install,
|
|
88
|
+
success: run.success,
|
|
89
|
+
output: [clone.reason && `Not cloned: ${clone.reason}`, run.output]
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join("\n"),
|
|
92
|
+
error: run.error,
|
|
93
|
+
});
|
|
94
|
+
if (!run.success) result.success = false;
|
|
100
95
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Installing a worktree's dependencies when they can't be cloned: the
|
|
3
|
-
* lockfile's own frozen install
|
|
3
|
+
* lockfile's own frozen install, and nothing else. A plain install would
|
|
4
|
+
* resolve ranges to versions newer than the lockfile, so a lockfile that
|
|
5
|
+
* disagrees with package.json fails setup and the agent is told.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
import * as fs from "fs";
|
|
@@ -8,8 +10,7 @@ import * as path from "path";
|
|
|
8
10
|
|
|
9
11
|
export interface PackageManager {
|
|
10
12
|
name: "bun" | "pnpm" | "yarn" | "npm";
|
|
11
|
-
|
|
12
|
-
installs: string[];
|
|
13
|
+
install: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
// Each install includes devDependencies: the agent needs typecheck, tests
|
|
@@ -17,44 +18,29 @@ export interface PackageManager {
|
|
|
17
18
|
const BY_LOCKFILE: Array<{ file: string; pm: PackageManager }> = [
|
|
18
19
|
{
|
|
19
20
|
file: "bun.lock",
|
|
20
|
-
pm: {
|
|
21
|
-
name: "bun",
|
|
22
|
-
installs: ["bun install --frozen-lockfile", "bun install"],
|
|
23
|
-
},
|
|
21
|
+
pm: { name: "bun", install: "bun install --frozen-lockfile" },
|
|
24
22
|
},
|
|
25
23
|
{
|
|
26
24
|
file: "bun.lockb",
|
|
27
|
-
pm: {
|
|
28
|
-
name: "bun",
|
|
29
|
-
installs: ["bun install --frozen-lockfile", "bun install"],
|
|
30
|
-
},
|
|
25
|
+
pm: { name: "bun", install: "bun install --frozen-lockfile" },
|
|
31
26
|
},
|
|
32
27
|
{
|
|
33
28
|
file: "pnpm-lock.yaml",
|
|
34
29
|
pm: {
|
|
35
30
|
name: "pnpm",
|
|
36
|
-
|
|
37
|
-
"pnpm install --frozen-lockfile --prod=false",
|
|
38
|
-
"pnpm install --prod=false",
|
|
39
|
-
],
|
|
31
|
+
install: "pnpm install --frozen-lockfile --prod=false",
|
|
40
32
|
},
|
|
41
33
|
},
|
|
42
34
|
{
|
|
43
35
|
file: "yarn.lock",
|
|
44
36
|
pm: {
|
|
45
37
|
name: "yarn",
|
|
46
|
-
|
|
47
|
-
"yarn install --frozen-lockfile --production=false",
|
|
48
|
-
"yarn install --production=false",
|
|
49
|
-
],
|
|
38
|
+
install: "yarn install --frozen-lockfile --production=false",
|
|
50
39
|
},
|
|
51
40
|
},
|
|
52
41
|
{
|
|
53
42
|
file: "package-lock.json",
|
|
54
|
-
pm: {
|
|
55
|
-
name: "npm",
|
|
56
|
-
installs: ["npm ci --include=dev", "npm install --include=dev"],
|
|
57
|
-
},
|
|
43
|
+
pm: { name: "npm", install: "npm ci --include=dev" },
|
|
58
44
|
},
|
|
59
45
|
];
|
|
60
46
|
|
|
@@ -68,11 +54,18 @@ export function detectPackageManager(
|
|
|
68
54
|
}
|
|
69
55
|
if (fs.existsSync(path.join(projectPath, "package.json"))) {
|
|
70
56
|
// No lockfile, so nothing to be frozen to.
|
|
71
|
-
return { name: "npm",
|
|
57
|
+
return { name: "npm", install: "npm install --include=dev" };
|
|
72
58
|
}
|
|
73
59
|
return null;
|
|
74
60
|
}
|
|
75
61
|
|
|
62
|
+
// pnpm may exist only as a corepack shim that was never enabled; the shell
|
|
63
|
+
// defines `pnpm` through corepack when the command is missing.
|
|
64
|
+
export function shellCommand(command: string): string {
|
|
65
|
+
if (!command.startsWith("pnpm ")) return command;
|
|
66
|
+
return `if ! command -v pnpm >/dev/null 2>&1; then pnpm() { COREPACK_ENABLE_DOWNLOAD_PROMPT=0 corepack pnpm "$@"; }; fi; ${command}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
76
69
|
// What setup commands run under: the server's environment without the
|
|
77
70
|
// settings that make an install skip devDependencies.
|
|
78
71
|
export function setupEnv(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saadnvd1/agent-os",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.691",
|
|
4
4
|
"description": "Self-hosted web UI for managing Claude Code sessions",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-os": "scripts/agent-os"
|
|
@@ -24,29 +24,24 @@
|
|
|
24
24
|
"tsconfig.json",
|
|
25
25
|
"postcss.config.mjs",
|
|
26
26
|
"tailwind.config.ts",
|
|
27
|
-
"package-lock.json",
|
|
28
27
|
"README.md",
|
|
29
28
|
"LICENSE"
|
|
30
29
|
],
|
|
31
30
|
"scripts": {
|
|
32
|
-
"predev": "node scripts/
|
|
31
|
+
"predev": "node scripts/check-native.mjs --rebuild && node scripts/gen-migrations-index.mjs",
|
|
33
32
|
"dev": "AGENTOS_DEV=1 tsx server.ts",
|
|
34
33
|
"dev:task": "tsx scripts/dev-task.ts",
|
|
35
34
|
"dev:stop": "tsx scripts/dev-stop.ts",
|
|
36
35
|
"disk:dry-run": "tsx scripts/disk-dry-run.ts",
|
|
37
36
|
"prebuild": "node scripts/gen-migrations-index.mjs",
|
|
38
37
|
"build": "next build",
|
|
39
|
-
"prestart": "node scripts/
|
|
38
|
+
"prestart": "node scripts/check-native.mjs --rebuild && node scripts/gen-migrations-index.mjs",
|
|
40
39
|
"start": "NODE_ENV=production AGENTOS_SERVER=live node --import tsx server.ts",
|
|
41
40
|
"pretypecheck": "node scripts/gen-migrations-index.mjs",
|
|
42
41
|
"typecheck": "tsc --noEmit",
|
|
43
42
|
"setup": "bash scripts/setup.sh",
|
|
44
43
|
"postinstall": "chmod +x node_modules/node-pty/prebuilds/*/spawn-helper 2>/dev/null || true",
|
|
45
44
|
"prepare": "husky",
|
|
46
|
-
"tauri": "tauri",
|
|
47
|
-
"tauri:dev": "tauri dev",
|
|
48
|
-
"tauri:build": "tauri build",
|
|
49
|
-
"tauri:build:linux": "docker build -t agentos-linux -f src-tauri/Dockerfile.linux src-tauri && docker run --rm -v $(pwd)/src-tauri/target:/app/target agentos-linux",
|
|
50
45
|
"prelint": "node scripts/gen-migrations-index.mjs",
|
|
51
46
|
"lint": "eslint .",
|
|
52
47
|
"format": "prettier --write .",
|
|
@@ -71,7 +66,7 @@
|
|
|
71
66
|
"author": "Saad Naveed",
|
|
72
67
|
"license": "MIT",
|
|
73
68
|
"engines": {
|
|
74
|
-
"node": ">=
|
|
69
|
+
"node": ">=22"
|
|
75
70
|
},
|
|
76
71
|
"bugs": {
|
|
77
72
|
"url": "https://github.com/saadnvd1/agent-os/issues"
|
|
@@ -155,15 +150,15 @@
|
|
|
155
150
|
"serwist": "^9.5.7",
|
|
156
151
|
"sonner": "^2.0.7",
|
|
157
152
|
"tailwind-merge": "^3.4.0",
|
|
153
|
+
"unified": "^11.0.5",
|
|
158
154
|
"valtio": "^2.3.0",
|
|
159
155
|
"ws": "^8.19.0",
|
|
160
156
|
"zod": "^4.6.5"
|
|
161
157
|
},
|
|
162
158
|
"devDependencies": {
|
|
163
159
|
"@tailwindcss/postcss": "^4.1.18",
|
|
164
|
-
"@
|
|
165
|
-
"@
|
|
166
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
160
|
+
"@types/better-sqlite3": "^9.6.0",
|
|
161
|
+
"@types/hast": "^3.0.4",
|
|
167
162
|
"@types/jsdom": "^30.0.0",
|
|
168
163
|
"@types/mdast": "^4.0.4",
|
|
169
164
|
"@types/node": "^20.19.27",
|
|
@@ -176,7 +171,8 @@
|
|
|
176
171
|
"husky": "^9.1.7",
|
|
177
172
|
"jsdom": "^30.1.2",
|
|
178
173
|
"prettier": "^3.7.4",
|
|
179
|
-
"prettier-plugin-tailwindcss": "^0.
|
|
174
|
+
"prettier-plugin-tailwindcss": "^0.8.1",
|
|
175
|
+
"remark-parse": "^11.0.0",
|
|
180
176
|
"sharp": "^0.35.5",
|
|
181
177
|
"tailwindcss": "^4.1.18",
|
|
182
178
|
"tsx": "^4.23.15",
|
|
@@ -184,8 +180,5 @@
|
|
|
184
180
|
"typescript": "^5.9.3",
|
|
185
181
|
"vitest": "^4.1.11"
|
|
186
182
|
},
|
|
187
|
-
"
|
|
188
|
-
"dompurify": "^3.4.16",
|
|
189
|
-
"postcss-selector-parser": "^7.1.6"
|
|
190
|
-
}
|
|
183
|
+
"packageManager": "pnpm@11.28.5+sha512.484563d30b4c2ad27cb48aa94ff2b302dbcebc8ff6bf28816c2589886e146ce7f76f354f3bc1a438e6f5e9768c302aa0a6f7add8232903cd4cf81426cbc14d9e"
|
|
191
184
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Proves the native modules work in a node_modules tree: open a SQLite
|
|
2
|
+
// database with better-sqlite3 and spawn a pty with node-pty. Used before a
|
|
3
|
+
// deploy swaps an install in, and by predev/prestart.
|
|
4
|
+
// node scripts/check-native.mjs [node_modules dir] (default: this checkout's)
|
|
5
|
+
// --rebuild on a Node ABI mismatch, run `pnpm rebuild` once and check again
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
import { execFileSync } from "child_process";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import os from "os";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
|
|
13
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const rebuild = args.includes("--rebuild");
|
|
16
|
+
const modules = path.resolve(
|
|
17
|
+
args.find((a) => !a.startsWith("--")) ?? path.join(here, "..", "node_modules")
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
async function sqlite(require) {
|
|
21
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "aos-native-"));
|
|
22
|
+
try {
|
|
23
|
+
const db = require("better-sqlite3")(path.join(dir, "check.db"));
|
|
24
|
+
db.exec("create table t (v text); insert into t values ('ok')");
|
|
25
|
+
const row = db.prepare("select v from t").get();
|
|
26
|
+
db.close();
|
|
27
|
+
if (row?.v !== "ok") throw new Error("better-sqlite3 read back nothing");
|
|
28
|
+
} finally {
|
|
29
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function pty(require) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const p = require("node-pty").spawn("/bin/sh", ["-c", "echo pty-ok"], {});
|
|
36
|
+
let out = "";
|
|
37
|
+
const timer = setTimeout(() => {
|
|
38
|
+
p.kill();
|
|
39
|
+
reject(new Error("node-pty: no exit within 10s"));
|
|
40
|
+
}, 10_000);
|
|
41
|
+
p.onData((d) => (out += d));
|
|
42
|
+
p.onExit(({ exitCode }) => {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
if (exitCode === 0 && out.includes("pty-ok")) resolve();
|
|
45
|
+
else reject(new Error(`node-pty: exit ${exitCode}, output ${out}`));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function failures() {
|
|
51
|
+
const require = createRequire(path.join(modules, "noop.js"));
|
|
52
|
+
const found = [];
|
|
53
|
+
for (const [name, run] of [
|
|
54
|
+
["better-sqlite3", sqlite],
|
|
55
|
+
["node-pty", pty],
|
|
56
|
+
]) {
|
|
57
|
+
try {
|
|
58
|
+
await run(require);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
found.push({ name, message: String(err?.message ?? err) });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return found;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let bad = await failures();
|
|
67
|
+
if (
|
|
68
|
+
bad.length &&
|
|
69
|
+
rebuild &&
|
|
70
|
+
bad.some((b) => b.message.includes("NODE_MODULE_VERSION"))
|
|
71
|
+
) {
|
|
72
|
+
const names = bad.map((b) => b.name);
|
|
73
|
+
console.log(`Rebuilding ${names.join(", ")} for Node ${process.version}...`);
|
|
74
|
+
execFileSync("pnpm", ["rebuild", ...names], {
|
|
75
|
+
cwd: path.dirname(modules),
|
|
76
|
+
stdio: "inherit",
|
|
77
|
+
});
|
|
78
|
+
bad = await failures();
|
|
79
|
+
}
|
|
80
|
+
if (bad.length) {
|
|
81
|
+
for (const b of bad)
|
|
82
|
+
console.error(`native check failed: ${b.name}: ${b.message}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
console.log(`native modules ok (${modules})`);
|
|
86
|
+
process.exit(0);
|
|
@@ -142,11 +142,11 @@ async function prepass(args: string[]) {
|
|
|
142
142
|
const steps: Record<Collector, () => string | void> = {
|
|
143
143
|
deps: () => {
|
|
144
144
|
const prod = JSON.parse(
|
|
145
|
-
run("
|
|
145
|
+
run("pnpm", ["audit", "--prod", "--json"], root) || "{}"
|
|
146
146
|
);
|
|
147
|
-
const all = JSON.parse(run("
|
|
147
|
+
const all = JSON.parse(run("pnpm", ["audit", "--json"], root) || "{}");
|
|
148
148
|
const outdated = JSON.parse(
|
|
149
|
-
run("
|
|
149
|
+
run("pnpm", ["outdated", "--format", "json"], root) || "{}"
|
|
150
150
|
);
|
|
151
151
|
result.deps = {
|
|
152
152
|
prod: summarizeNpmAudit(prod),
|