@saadnvd1/agent-os 0.3.765 → 0.3.768
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 +6 -5
- package/components/DemoNotice.tsx +16 -0
- package/components/Devices/ConnectCard.tsx +20 -2
- package/components/Models/ProviderCard.tsx +1 -1
- package/components/SessionList/SidebarBody.tsx +14 -4
- package/components/Welcome/ConnectBlock.tsx +8 -4
- package/components/Welcome/WelcomeDialog.tsx +1 -1
- package/lib/connect/view.ts +1 -0
- package/lib/tasks/contract/plan.ts +8 -1
- package/lib/tasks/placement.ts +22 -0
- package/lib/tasks/queue-placement.ts +15 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ and check on all of it from your phone.
|
|
|
50
50
|
|
|
51
51
|
The iPhone app, [AgentOS: Coding Agents](https://apps.apple.com/us/app/agentos-coding-agents/id6820980797), is on the App Store.
|
|
52
52
|
|
|
53
|
-
You need Node.js 22+, tmux, and at least one agent CLI (the installer offers to set up what's missing).
|
|
53
|
+
You need Node.js 22+, git, tmux, and at least one agent CLI (the installer offers to set up what's missing).
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
56
|
npm install -g @saadnvd1/agent-os
|
|
@@ -69,11 +69,12 @@ Then open AgentOS, add a project from ⌘K, and press ⌥N. The [install guide](
|
|
|
69
69
|
|
|
70
70
|
## Connect
|
|
71
71
|
|
|
72
|
-
Connect
|
|
72
|
+
AgentOS Connect is live. It gives your machine a private address at runagentos.com, so your phone reaches it from any network with nothing to open on your router and no VPN.
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
- **Free ($0):** Connect for one machine, including phone access from anywhere, on up to 3 devices.
|
|
75
|
+
- **Pro ($4/mo billed yearly, or $6 monthly):** link up to 10 machines, with unlimited devices and 100 GB of traffic a month.
|
|
76
|
+
|
|
77
|
+
[Sign up](https://runagentos.com/sign-in?mode=sign-up), then run `agent-os connect` on the machine and enter the code. The [phone guide](docs/guides/phone.md) has the steps.
|
|
77
78
|
|
|
78
79
|
Reach it from your phone with the [AgentOS iPhone app](https://apps.apple.com/us/app/agentos-coding-agents/id6820980797), now on the App Store.
|
|
79
80
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { Info } from "lucide-react";
|
|
2
|
+
import { Button } from "@/components/ui/button";
|
|
3
|
+
import { CONNECT_SIGN_UP } from "@/lib/connect/view";
|
|
4
|
+
|
|
5
|
+
const INSTALL_URL = "https://github.com/saadnvd1/agent-os#install";
|
|
2
6
|
|
|
3
7
|
// Where a terminal or a new session would be, in a demo: what this is and
|
|
4
8
|
// what to look at instead. Fixed text; nothing runs.
|
|
@@ -18,6 +22,18 @@ export function DemoNotice() {
|
|
|
18
22
|
Open a session from the sidebar to read its chat, its changes and its
|
|
19
23
|
pull request; its terminal stays closed.
|
|
20
24
|
</p>
|
|
25
|
+
<div className="flex flex-wrap gap-2 pt-1">
|
|
26
|
+
<Button asChild className="min-h-11 sm:min-h-9">
|
|
27
|
+
<a href={INSTALL_URL} target="_blank" rel="noopener noreferrer">
|
|
28
|
+
Install AgentOS
|
|
29
|
+
</a>
|
|
30
|
+
</Button>
|
|
31
|
+
<Button asChild variant="outline" className="min-h-11 sm:min-h-9">
|
|
32
|
+
<a href={CONNECT_SIGN_UP} target="_blank" rel="noopener noreferrer">
|
|
33
|
+
Get Connect
|
|
34
|
+
</a>
|
|
35
|
+
</Button>
|
|
36
|
+
</div>
|
|
21
37
|
</div>
|
|
22
38
|
</div>
|
|
23
39
|
);
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
import { Cloud } from "lucide-react";
|
|
4
4
|
import { Switch } from "@/components/ui/switch";
|
|
5
5
|
import type { ConnectStatus } from "@/lib/connect/serve";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
CONNECT_SIGN_UP,
|
|
8
|
+
CONNECT_STATE as STATE,
|
|
9
|
+
connectView,
|
|
10
|
+
} from "@/lib/connect/view";
|
|
7
11
|
|
|
8
12
|
export function ConnectCard({
|
|
9
13
|
connect,
|
|
@@ -38,7 +42,21 @@ export function ConnectCard({
|
|
|
38
42
|
) : enrolled ? (
|
|
39
43
|
<>Linked. The address appears once the tunnel starts.</>
|
|
40
44
|
) : (
|
|
41
|
-
<>
|
|
45
|
+
<>
|
|
46
|
+
Not set up on this machine. Run{" "}
|
|
47
|
+
<code className="text-foreground font-mono">
|
|
48
|
+
agent-os connect
|
|
49
|
+
</code>{" "}
|
|
50
|
+
in a terminal.{" "}
|
|
51
|
+
<a
|
|
52
|
+
href={CONNECT_SIGN_UP}
|
|
53
|
+
target="_blank"
|
|
54
|
+
rel="noopener noreferrer"
|
|
55
|
+
className="text-foreground flex min-h-11 w-fit items-center underline underline-offset-2 sm:min-h-6"
|
|
56
|
+
>
|
|
57
|
+
Set up Connect (free)
|
|
58
|
+
</a>
|
|
59
|
+
</>
|
|
42
60
|
)}
|
|
43
61
|
{connect.reason && connect.state !== "off"
|
|
44
62
|
? ` ${connect.reason.replace(/\.$/, "")}.`
|
|
@@ -73,7 +73,7 @@ export function ProviderCard({
|
|
|
73
73
|
const builtIn = provider.id === ANTHROPIC;
|
|
74
74
|
const [open, setOpen] = useState(false);
|
|
75
75
|
const [kind, setKind] = useState<SecretRef["kind"]>(
|
|
76
|
-
provider.auth?.kind ?? "
|
|
76
|
+
provider.auth?.kind ?? "env"
|
|
77
77
|
);
|
|
78
78
|
const [a0, b0] = refFields(provider.auth);
|
|
79
79
|
const [a, setA] = useState(a0);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { AlertCircle, FolderPlus, Plus, X } from "lucide-react";
|
|
4
4
|
import { Button } from "@/components/ui/button";
|
|
5
5
|
import { ShimmeringLoader } from "@/components/ui/skeleton";
|
|
6
|
+
import { chordHint } from "@/lib/keybindings/chord";
|
|
6
7
|
import { sidebarUiActions } from "@/stores/sidebarUi";
|
|
7
8
|
import type { SidebarData } from "./useSidebarData";
|
|
8
9
|
import { MachineShelves, SessionShelves } from "./SessionShelves";
|
|
@@ -99,10 +100,19 @@ export function SidebarBody({
|
|
|
99
100
|
<Empty
|
|
100
101
|
text="Create a project to start your first session"
|
|
101
102
|
action={
|
|
102
|
-
<
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
<div className="flex flex-col items-center gap-3">
|
|
104
|
+
<Button onClick={onNewProject} className="h-11 gap-2 md:h-9">
|
|
105
|
+
<FolderPlus className="h-4 w-4" />
|
|
106
|
+
Add project
|
|
107
|
+
</Button>
|
|
108
|
+
<p className="text-muted-foreground/70 text-xs">
|
|
109
|
+
Then press{" "}
|
|
110
|
+
<kbd className="bg-muted text-foreground rounded px-1.5 py-0.5 font-mono">
|
|
111
|
+
{chordHint("alt+n")}
|
|
112
|
+
</kbd>{" "}
|
|
113
|
+
to start a session
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
106
116
|
}
|
|
107
117
|
/>
|
|
108
118
|
) : (
|
|
@@ -13,7 +13,11 @@ import {
|
|
|
13
13
|
useNetworkQuery,
|
|
14
14
|
useUpdateNetwork,
|
|
15
15
|
} from "@/data/devices";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
CONNECT_SIGN_UP,
|
|
18
|
+
CONNECT_STATE,
|
|
19
|
+
connectView,
|
|
20
|
+
} from "@/lib/connect/view";
|
|
17
21
|
import { Block } from "./Block";
|
|
18
22
|
|
|
19
23
|
const COMMAND = "agent-os connect";
|
|
@@ -40,7 +44,7 @@ export function LinkForm() {
|
|
|
40
44
|
<p className="text-muted-foreground text-xs">
|
|
41
45
|
Get a code at{" "}
|
|
42
46
|
<a
|
|
43
|
-
href={
|
|
47
|
+
href={CONNECT_SIGN_UP}
|
|
44
48
|
target="_blank"
|
|
45
49
|
rel="noopener noreferrer"
|
|
46
50
|
className="text-foreground inline-flex items-center gap-1 underline underline-offset-2"
|
|
@@ -48,7 +52,7 @@ export function LinkForm() {
|
|
|
48
52
|
runagentos.com
|
|
49
53
|
<ExternalLink className="size-3" />
|
|
50
54
|
</a>{" "}
|
|
51
|
-
(Connect a machine), then paste it here.
|
|
55
|
+
(free account, then Connect a machine), then paste it here.
|
|
52
56
|
</p>
|
|
53
57
|
<form
|
|
54
58
|
className="flex gap-2"
|
|
@@ -104,7 +108,7 @@ export function ConnectBlock() {
|
|
|
104
108
|
<Block
|
|
105
109
|
icon={Globe}
|
|
106
110
|
title="Use it away from home"
|
|
107
|
-
detail="Connect gives this machine a private address at runagentos.com, so your phone reaches it from anywhere. Nothing to open on your router."
|
|
111
|
+
detail="Connect is free for one machine. It gives this machine a private address at runagentos.com, so your phone reaches it from anywhere. Nothing to open on your router."
|
|
108
112
|
>
|
|
109
113
|
{network.isPending ? (
|
|
110
114
|
<div className="bg-muted/40 h-12 animate-pulse rounded-lg" />
|
|
@@ -28,7 +28,7 @@ const STEPS: Record<
|
|
|
28
28
|
agents: {
|
|
29
29
|
label: "Agents",
|
|
30
30
|
title: "Connect your agents",
|
|
31
|
-
hint: "Install and sign in the coding agents you want to use. You need one.",
|
|
31
|
+
hint: "Install and sign in the coding agents you want to use. You need one, with an account such as Claude or Codex.",
|
|
32
32
|
},
|
|
33
33
|
project: {
|
|
34
34
|
label: "Project",
|
package/lib/connect/view.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ConnectStatus } from "./serve";
|
|
2
2
|
|
|
3
3
|
export const CONNECT_SITE = "https://runagentos.com";
|
|
4
|
+
export const CONNECT_SIGN_UP = `${CONNECT_SITE}/sign-in?mode=sign-up`;
|
|
4
5
|
|
|
5
6
|
export const CONNECT_STATE: Record<ConnectStatus["state"], string> = {
|
|
6
7
|
up: "Connected",
|
|
@@ -25,7 +25,7 @@ import { getProject } from "../../projects";
|
|
|
25
25
|
import { planFor, splitOf } from "../../plans/features";
|
|
26
26
|
import { moveTask } from "../move-flow";
|
|
27
27
|
import * as client from "./client";
|
|
28
|
-
import { meetsNeeds, needsOf } from "../placement";
|
|
28
|
+
import { meetsNeeds, needsOf, taskPinnedToMac } from "../placement";
|
|
29
29
|
import { remotePlanPath } from "./plan-path";
|
|
30
30
|
import { DEFAULT_PRIORITY, isPriority } from "../schedule-types";
|
|
31
31
|
import { localHolds } from "../hold-checks";
|
|
@@ -55,6 +55,9 @@ function checkedSplit(s: Session, text: string | null) {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
const pinned = (s: Session, project: { working_directory: string }) =>
|
|
59
|
+
taskPinnedToMac(s.id, expandHome(project.working_directory));
|
|
60
|
+
|
|
58
61
|
/**
|
|
59
62
|
* Where a written plan is built: "there" (approved on the machine that
|
|
60
63
|
* planned it) or "home" (moved here first). Pieces are checked against this
|
|
@@ -70,6 +73,9 @@ export function implementTarget(
|
|
|
70
73
|
// The project lives there: nothing here to build it in.
|
|
71
74
|
if (!project || isRemoteHost(project.host_id)) return "there";
|
|
72
75
|
if (split) return "home";
|
|
76
|
+
// An explicit Mac pin builds on the Mac, wherever the plan ran.
|
|
77
|
+
if (pinned(s, project))
|
|
78
|
+
return meetsNeeds(["mac"], machineCapabilities()) ? "home" : "there";
|
|
73
79
|
const { needs } = needsOf({
|
|
74
80
|
projectId: project.id,
|
|
75
81
|
projectPath: expandHome(project.working_directory),
|
|
@@ -229,6 +235,7 @@ export async function handoffHome(
|
|
|
229
235
|
projectPath: expandHome(project.working_directory),
|
|
230
236
|
prompt: s.task_prompt ?? "",
|
|
231
237
|
});
|
|
238
|
+
if (pinned(s, project) && !needs.includes("mac")) needs.push("mac");
|
|
232
239
|
const holds = localHolds(
|
|
233
240
|
{
|
|
234
241
|
needs,
|
package/lib/tasks/placement.ts
CHANGED
|
@@ -79,6 +79,28 @@ export function inferNeeds(projectPath: string): Need[] {
|
|
|
79
79
|
return [];
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// Whether the starter asked for a Mac outright: the request's `machine`
|
|
83
|
+
// (stored on the queue row, which shares the task's id), a recipe's, or the
|
|
84
|
+
// project's own machine setting. A need read from the brief or the files
|
|
85
|
+
// doesn't count: that one a plan can ignore.
|
|
86
|
+
export function pinnedToMac(
|
|
87
|
+
stored: string | null,
|
|
88
|
+
projectPath: string
|
|
89
|
+
): boolean {
|
|
90
|
+
const asked = parseNeeds(stored);
|
|
91
|
+
if (asked) return asked.includes("mac");
|
|
92
|
+
return !!loadProjectConfig(projectPath).config.machine?.needs?.includes(
|
|
93
|
+
"mac"
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function taskPinnedToMac(taskId: string, projectPath: string): boolean {
|
|
98
|
+
const row = db
|
|
99
|
+
.prepare(`SELECT machine_needs FROM task_queue WHERE id = ?`)
|
|
100
|
+
.get(taskId) as { machine_needs: string | null } | undefined;
|
|
101
|
+
return pinnedToMac(row?.machine_needs ?? null, projectPath);
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
export function needsOf(input: {
|
|
83
105
|
projectId: string;
|
|
84
106
|
projectPath: string;
|
|
@@ -13,6 +13,7 @@ import { usesGitCrypt } from "../worktree-add";
|
|
|
13
13
|
import { list as hostTasks } from "./contract/client";
|
|
14
14
|
import {
|
|
15
15
|
asPlanPhase,
|
|
16
|
+
pinnedToMac,
|
|
16
17
|
place,
|
|
17
18
|
unplaceable,
|
|
18
19
|
wantsPlacing,
|
|
@@ -90,11 +91,22 @@ async function peerOptions(
|
|
|
90
91
|
return out;
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
// A start that plans first goes by its plan phase: it needs no
|
|
94
|
-
// simulator, and a plan-only one that no machine can take starts here
|
|
94
|
+
// A start that plans first goes by its plan phase: it needs no simulator, and a plan-only one that no machine can take starts here
|
|
95
95
|
// as ever (it never waits on load).
|
|
96
96
|
const plansFirst = (r: QueueRow) => !!(r.plan || r.plan_only);
|
|
97
97
|
|
|
98
|
+
// Planning needs no simulator or Xcode, and a need read from the files or the
|
|
99
|
+
// brief doesn't hold it. Only an explicit Mac pin does: the work is Mac-only
|
|
100
|
+
// (often because the other machine can't reach what it touches).
|
|
101
|
+
const planNeeds = (r: QueueRow, needs: Need[]): Need[] => {
|
|
102
|
+
const project = getProject(r.project_id);
|
|
103
|
+
return project &&
|
|
104
|
+
needs.includes("mac") &&
|
|
105
|
+
pinnedToMac(r.machine_needs, expandHome(project.working_directory))
|
|
106
|
+
? ["mac"]
|
|
107
|
+
: [];
|
|
108
|
+
};
|
|
109
|
+
|
|
98
110
|
// Marks each start the home machine's load rules out as remote and returns
|
|
99
111
|
// where it goes. "stale": the line moved while the machines were asked, so
|
|
100
112
|
// this look is over, and the one a change during it asks for sees the line
|
|
@@ -143,7 +155,7 @@ export async function placeCandidates(
|
|
|
143
155
|
asked(c),
|
|
144
156
|
home,
|
|
145
157
|
options,
|
|
146
|
-
plan ? [] : (needsOf.get(c.id) ?? []),
|
|
158
|
+
plan ? planNeeds(r, needsOf.get(c.id) ?? []) : (needsOf.get(c.id) ?? []),
|
|
147
159
|
now
|
|
148
160
|
);
|
|
149
161
|
if (!spot) continue;
|