@cursor/july 0.1.2 → 0.1.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.
- package/AGENTS.md +16 -5
- package/README.md +8 -9
- package/dist/bin/agent-serve.d.ts +3 -1
- package/dist/bin/agent-serve.d.ts.map +1 -1
- package/dist/bin/agent-serve.js +416 -155
- package/dist/channels/slack/log.d.ts +1 -1
- package/dist/channels/slack/log.d.ts.map +1 -1
- package/dist/channels/slack/log.js +4 -5
- package/dist/channels/slack/setup.js +1 -1
- package/dist/channels/slack/socket-mode.d.ts.map +1 -1
- package/dist/channels/slack/socket-mode.js +10 -9
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.Oje4vhlk.js → app.DF-Yx4OP.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.0XrheBv2.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.H5XZ2zCB.js → VPLocalSearchBox.DSeJSudL.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CTR_TuaE.js → theme.DZVLTXOC.js} +2 -2
- package/dist/docs/assets/{index.md.m81y7TY7.js → index.md.Cylk70gg.js} +1 -1
- package/dist/docs/assets/{quickstart.md.CfU8_uTC.js → quickstart.md.BU6Iwi_9.js} +18 -6
- package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
- package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +5 -5
- package/dist/docs/quickstart.html +22 -10
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +14 -9
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/internal/ab-collector.js +3 -3
- package/dist/internal/ab-fold.js +1 -1
- package/dist/internal/chat-client.d.ts +29 -3
- package/dist/internal/chat-client.d.ts.map +1 -1
- package/dist/internal/chat-client.js +180 -27
- package/dist/internal/cli-ax.d.ts +83 -2
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +626 -52
- package/dist/internal/cli-cursor.d.ts.map +1 -1
- package/dist/internal/cli-cursor.js +8 -36
- package/dist/internal/cli-deploy.d.ts +59 -3
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +316 -38
- package/dist/internal/cli-github.js +13 -13
- package/dist/internal/cursor/account-mcp.js +3 -3
- package/dist/internal/cursor/backend-client.js +1 -1
- package/dist/internal/cursor/credentials.d.ts +14 -3
- package/dist/internal/cursor/credentials.d.ts.map +1 -1
- package/dist/internal/cursor/credentials.js +21 -6
- package/dist/internal/cursor/github-credentials.js +3 -3
- package/dist/internal/cursor-event-relay.js +1 -1
- package/dist/internal/cursor-relay-core.d.ts +1 -1
- package/dist/internal/cursor-relay-core.d.ts.map +1 -1
- package/dist/internal/cursor-relay-core.js +2 -1
- package/dist/internal/cursor-slack-relay.js +1 -1
- package/dist/internal/deploy-client.d.ts +9 -1
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +23 -3
- package/dist/internal/deploy-source.d.ts +35 -0
- package/dist/internal/deploy-source.d.ts.map +1 -0
- package/dist/internal/deploy-source.js +118 -0
- package/dist/internal/discovery.d.ts +11 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +43 -14
- package/dist/internal/distribution.d.ts +7 -0
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +7 -0
- package/dist/internal/docs-site.js +3 -3
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +10 -9
- package/dist/internal/eval-runner.d.ts +2 -0
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/eval-runner.js +2 -0
- package/dist/internal/event-mapper.d.ts +54 -1
- package/dist/internal/event-mapper.d.ts.map +1 -1
- package/dist/internal/event-mapper.js +151 -41
- package/dist/internal/github-fanout.js +2 -2
- package/dist/internal/host-platforms.js +5 -5
- package/dist/internal/init-project.d.ts +88 -1
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +221 -31
- package/dist/internal/install-cursor-skills.d.ts +64 -0
- package/dist/internal/install-cursor-skills.d.ts.map +1 -0
- package/dist/internal/install-cursor-skills.js +274 -0
- package/dist/internal/logs-client.d.ts +60 -0
- package/dist/internal/logs-client.d.ts.map +1 -0
- package/dist/internal/logs-client.js +311 -0
- package/dist/internal/mcp-endpoint.d.ts.map +1 -1
- package/dist/internal/mcp-endpoint.js +1 -1
- package/dist/internal/open-browser.d.ts +5 -0
- package/dist/internal/open-browser.d.ts.map +1 -0
- package/dist/internal/open-browser.js +34 -0
- package/dist/internal/playground/toolchain.d.ts.map +1 -1
- package/dist/internal/playground/toolchain.js +19 -14
- package/dist/internal/playground-proxy.d.ts +69 -0
- package/dist/internal/playground-proxy.d.ts.map +1 -0
- package/dist/internal/playground-proxy.js +468 -0
- package/dist/internal/prompt-context.d.ts +35 -0
- package/dist/internal/prompt-context.d.ts.map +1 -0
- package/dist/internal/prompt-context.js +71 -0
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +11 -11
- package/dist/internal/request-headers.d.ts +11 -0
- package/dist/internal/request-headers.d.ts.map +1 -0
- package/dist/internal/request-headers.js +14 -0
- package/dist/internal/resolve-prod-target.d.ts +44 -0
- package/dist/internal/resolve-prod-target.d.ts.map +1 -0
- package/dist/internal/resolve-prod-target.js +134 -0
- package/dist/internal/resolved-connections.js +5 -5
- package/dist/internal/run-client.d.ts +2 -2
- package/dist/internal/run-client.d.ts.map +1 -1
- package/dist/internal/run-client.js +38 -23
- package/dist/internal/schedule-runner.js +1 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +23 -17
- package/dist/internal/session-engine.d.ts +1 -1
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +34 -19
- package/dist/internal/sessions-client.d.ts +34 -0
- package/dist/internal/sessions-client.d.ts.map +1 -0
- package/dist/internal/sessions-client.js +193 -0
- package/dist/internal/storage-coordinator.js +6 -6
- package/dist/internal/stream-progress.d.ts.map +1 -1
- package/dist/internal/stream-progress.js +31 -3
- package/dist/internal/terminal-style.d.ts +22 -0
- package/dist/internal/terminal-style.d.ts.map +1 -0
- package/dist/internal/terminal-style.js +49 -0
- package/dist/internal/trajectory.d.ts +10 -5
- package/dist/internal/trajectory.d.ts.map +1 -1
- package/dist/internal/trajectory.js +82 -10
- package/dist/internal/update-check.d.ts +93 -0
- package/dist/internal/update-check.d.ts.map +1 -0
- package/dist/internal/update-check.js +431 -0
- package/dist/internal/workspace.d.ts +11 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +38 -4
- package/dist/playground/assets/index-Ct_Usdm8.css +1 -0
- package/dist/playground/assets/index-Dsyb-Vaj.js +319 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +29 -2
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +0 -1
- package/docs/quickstart.md +22 -7
- package/docs/reference/cli.md +51 -3
- package/package.json +4 -1
- package/skills/ab/SKILL.md +8 -8
- package/skills/create-agent/SKILL.md +28 -22
- package/skills/debug/SKILL.md +11 -11
- package/skills/evals/SKILL.md +16 -16
- package/skills/framework-map/SKILL.md +6 -6
- package/skills/github/SKILL.md +13 -13
- package/skills/hillclimb/SKILL.md +10 -10
- package/skills/setup-slack/SKILL.md +14 -14
- package/src/bin/agent-serve.ts +451 -191
- package/src/channels/slack/log.ts +5 -5
- package/src/channels/slack/setup.ts +1 -1
- package/src/channels/slack/socket-mode.ts +11 -9
- package/src/internal/ab-collector.ts +3 -3
- package/src/internal/ab-fold.ts +1 -1
- package/src/internal/chat-client.ts +252 -37
- package/src/internal/cli-ax.ts +761 -72
- package/src/internal/cli-cursor.ts +14 -42
- package/src/internal/cli-deploy.ts +428 -49
- package/src/internal/cli-github.ts +13 -15
- package/src/internal/cursor/account-mcp.ts +3 -3
- package/src/internal/cursor/backend-client.ts +1 -1
- package/src/internal/cursor/credentials.ts +26 -7
- package/src/internal/cursor/github-credentials.ts +3 -3
- package/src/internal/cursor-event-relay.ts +1 -1
- package/src/internal/cursor-relay-core.ts +4 -2
- package/src/internal/cursor-slack-relay.ts +1 -1
- package/src/internal/deploy-client.ts +45 -4
- package/src/internal/deploy-source.ts +133 -0
- package/src/internal/discovery.ts +53 -16
- package/src/internal/distribution.ts +8 -0
- package/src/internal/docs-site.ts +3 -3
- package/src/internal/eval-run-store.ts +10 -11
- package/src/internal/eval-runner.ts +5 -0
- package/src/internal/event-mapper.ts +222 -42
- package/src/internal/github-fanout.ts +2 -2
- package/src/internal/host-platforms.ts +5 -5
- package/src/internal/init-project.ts +333 -51
- package/src/internal/install-cursor-skills.ts +327 -0
- package/src/internal/logs-client.ts +442 -0
- package/src/internal/mcp-endpoint.ts +1 -3
- package/src/internal/open-browser.ts +41 -0
- package/src/internal/playground/toolchain.ts +18 -16
- package/src/internal/playground-proxy.ts +576 -0
- package/src/internal/prompt-context.ts +95 -0
- package/src/internal/reminder-runner.ts +11 -17
- package/src/internal/request-headers.ts +23 -0
- package/src/internal/resolve-prod-target.ts +182 -0
- package/src/internal/resolved-connections.ts +5 -5
- package/src/internal/run-client.ts +39 -36
- package/src/internal/schedule-runner.ts +1 -1
- package/src/internal/server.ts +25 -16
- package/src/internal/session-engine.ts +35 -18
- package/src/internal/sessions-client.ts +216 -0
- package/src/internal/storage-coordinator.ts +6 -6
- package/src/internal/stream-progress.ts +36 -2
- package/src/internal/terminal-style.ts +74 -0
- package/src/internal/trajectory.ts +91 -13
- package/src/internal/update-check.ts +552 -0
- package/src/internal/workspace.ts +42 -4
- package/src/types.ts +45 -7
- package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +0 -1
- package/dist/playground/assets/index-B1Qc9h2u.css +0 -1
- package/dist/playground/assets/index-CpDYCj8W.js +0 -79
- /package/dist/docs/assets/{index.md.m81y7TY7.lean.js → index.md.Cylk70gg.lean.js} +0 -0
- /package/dist/docs/assets/{quickstart.md.CfU8_uTC.lean.js → quickstart.md.BU6Iwi_9.lean.js} +0 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outdated-version warning + `agentkit update`.
|
|
3
|
+
*
|
|
4
|
+
* Every CLI run of a published install compares its own version against the
|
|
5
|
+
* npm registry's `latest` dist-tag and warns on stderr when behind. The
|
|
6
|
+
* registry lookup is cached under the config dir
|
|
7
|
+
* (`~/.config/agent-serve/update-check.json`) so most runs never touch the
|
|
8
|
+
* network; a stale cache refreshes inline under a short timeout, and any
|
|
9
|
+
* network failure is silent (the warning is best-effort, never a blocker).
|
|
10
|
+
*
|
|
11
|
+
* `agentkit update` re-runs the lookup fresh, figures out how this copy was
|
|
12
|
+
* installed (global npm/pnpm/yarn/bun, or a project dependency), and runs the
|
|
13
|
+
* matching install command.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { spawn } from "node:child_process";
|
|
17
|
+
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
18
|
+
import { dirname, join, sep } from "node:path";
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
import { configDirPath } from "./cursor/credentials.js";
|
|
21
|
+
import { describeError } from "./describe-error.js";
|
|
22
|
+
import {
|
|
23
|
+
CLI_COMMAND_NAME,
|
|
24
|
+
isCompiledDistribution,
|
|
25
|
+
PACKAGE_NAME,
|
|
26
|
+
packageRootDir,
|
|
27
|
+
packageVersion,
|
|
28
|
+
} from "./distribution.js";
|
|
29
|
+
import { stderrPalette } from "./terminal-style.js";
|
|
30
|
+
|
|
31
|
+
const NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
32
|
+
|
|
33
|
+
/** Refresh the registry lookup at most this often. */
|
|
34
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
35
|
+
|
|
36
|
+
/** Inline refresh budget; a slow registry must not stall the command. */
|
|
37
|
+
const WARN_FETCH_TIMEOUT_MS = 1500;
|
|
38
|
+
|
|
39
|
+
/** `agentkit update` runs interactively, so it can afford a real timeout. */
|
|
40
|
+
const UPDATE_FETCH_TIMEOUT_MS = 10_000;
|
|
41
|
+
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Version comparison (plain semver triples + prerelease, no ranges)
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
interface ParsedVersion {
|
|
47
|
+
release: [number, number, number];
|
|
48
|
+
prerelease: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseVersion(version: string): ParsedVersion | undefined {
|
|
52
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.*)?$/.exec(
|
|
53
|
+
version.trim()
|
|
54
|
+
);
|
|
55
|
+
if (match === null) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
release: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
60
|
+
prerelease: match[4] === undefined ? [] : match[4].split("."),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Semver-style ordering: negative when `a < b`, 0 when equal, positive when
|
|
66
|
+
* `a > b`. Unparseable versions compare equal (never warn on garbage).
|
|
67
|
+
*/
|
|
68
|
+
export function compareVersions(a: string, b: string): number {
|
|
69
|
+
const pa = parseVersion(a);
|
|
70
|
+
const pb = parseVersion(b);
|
|
71
|
+
if (pa === undefined || pb === undefined) {
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
for (let i = 0; i < 3; i++) {
|
|
75
|
+
const diff = pa.release[i]! - pb.release[i]!;
|
|
76
|
+
if (diff !== 0) {
|
|
77
|
+
return diff;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// A release outranks any prerelease of the same triple.
|
|
81
|
+
if (pa.prerelease.length === 0 || pb.prerelease.length === 0) {
|
|
82
|
+
return pb.prerelease.length - pa.prerelease.length;
|
|
83
|
+
}
|
|
84
|
+
const len = Math.max(pa.prerelease.length, pb.prerelease.length);
|
|
85
|
+
for (let i = 0; i < len; i++) {
|
|
86
|
+
const ia = pa.prerelease[i];
|
|
87
|
+
const ib = pb.prerelease[i];
|
|
88
|
+
// A longer prerelease list outranks its prefix (1.0.0-a.1 > 1.0.0-a).
|
|
89
|
+
if (ia === undefined) {
|
|
90
|
+
return -1;
|
|
91
|
+
}
|
|
92
|
+
if (ib === undefined) {
|
|
93
|
+
return 1;
|
|
94
|
+
}
|
|
95
|
+
const na = /^\d+$/.test(ia) ? Number(ia) : undefined;
|
|
96
|
+
const nb = /^\d+$/.test(ib) ? Number(ib) : undefined;
|
|
97
|
+
if (na !== undefined && nb !== undefined) {
|
|
98
|
+
if (na !== nb) {
|
|
99
|
+
return na - nb;
|
|
100
|
+
}
|
|
101
|
+
} else if (na !== undefined) {
|
|
102
|
+
return -1; // numeric identifiers sort before alphanumeric ones
|
|
103
|
+
} else if (nb !== undefined) {
|
|
104
|
+
return 1;
|
|
105
|
+
} else if (ia !== ib) {
|
|
106
|
+
return ia < ib ? -1 : 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// Registry lookup
|
|
114
|
+
// ============================================================================
|
|
115
|
+
|
|
116
|
+
const distTagResponseSchema = z.object({ version: z.string() });
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Latest published version of the package, from the registry's `latest`
|
|
120
|
+
* dist-tag. Returns undefined on any failure (offline, 404 pre-publish, …).
|
|
121
|
+
*/
|
|
122
|
+
export async function fetchLatestPublishedVersion(options?: {
|
|
123
|
+
fetchImpl?: typeof fetch;
|
|
124
|
+
timeoutMs?: number;
|
|
125
|
+
packageName?: string;
|
|
126
|
+
}): Promise<string | undefined> {
|
|
127
|
+
const fetchImpl = options?.fetchImpl ?? fetch;
|
|
128
|
+
const packageName = options?.packageName ?? PACKAGE_NAME;
|
|
129
|
+
// Scoped names keep the `@` but encode the `/` for the registry path.
|
|
130
|
+
const url = `${NPM_REGISTRY_URL}/${packageName.replace("/", "%2F")}/latest`;
|
|
131
|
+
try {
|
|
132
|
+
const response = await fetchImpl(url, {
|
|
133
|
+
headers: { accept: "application/json" },
|
|
134
|
+
signal: AbortSignal.timeout(options?.timeoutMs ?? WARN_FETCH_TIMEOUT_MS),
|
|
135
|
+
});
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const body = distTagResponseSchema.safeParse(await response.json());
|
|
140
|
+
if (!body.success || parseVersion(body.data.version) === undefined) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
return body.data.version;
|
|
144
|
+
} catch {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// Cache (one JSON file next to credentials.json)
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
const cacheSchema: z.ZodObject<
|
|
154
|
+
{ checkedAt: z.ZodNumber; latestVersion: z.ZodOptional<z.ZodString> },
|
|
155
|
+
"strip"
|
|
156
|
+
> = z.object({
|
|
157
|
+
/** Epoch ms of the last registry lookup attempt (success or not). */
|
|
158
|
+
checkedAt: z.number(),
|
|
159
|
+
/** Last version the registry reported; absent when lookups keep failing. */
|
|
160
|
+
latestVersion: z.string().optional(),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
export type UpdateCheckCache = z.infer<typeof cacheSchema>;
|
|
164
|
+
|
|
165
|
+
export function updateCheckCachePath(configDir?: string): string {
|
|
166
|
+
return join(configDir ?? configDirPath(), "update-check.json");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function readUpdateCheckCache(
|
|
170
|
+
configDir?: string
|
|
171
|
+
): Promise<UpdateCheckCache | undefined> {
|
|
172
|
+
let raw: string;
|
|
173
|
+
try {
|
|
174
|
+
raw = await readFile(updateCheckCachePath(configDir), "utf8");
|
|
175
|
+
} catch {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const parsed = cacheSchema.safeParse(JSON.parse(raw));
|
|
180
|
+
return parsed.success ? parsed.data : undefined;
|
|
181
|
+
} catch {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export async function writeUpdateCheckCache(
|
|
187
|
+
cache: UpdateCheckCache,
|
|
188
|
+
configDir?: string
|
|
189
|
+
): Promise<void> {
|
|
190
|
+
const path = updateCheckCachePath(configDir);
|
|
191
|
+
try {
|
|
192
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
193
|
+
await writeFile(path, `${JSON.stringify(cache, null, 2)}\n`);
|
|
194
|
+
} catch {
|
|
195
|
+
// Best-effort; a read-only config dir just means re-checking next run.
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ============================================================================
|
|
200
|
+
// The per-run warning
|
|
201
|
+
// ============================================================================
|
|
202
|
+
|
|
203
|
+
export interface WarnIfOutdatedOptions {
|
|
204
|
+
/** Defaults to the running package's own version. */
|
|
205
|
+
currentVersion?: string;
|
|
206
|
+
/** Test seam; defaults to the shared config dir. */
|
|
207
|
+
configDir?: string;
|
|
208
|
+
fetchImpl?: typeof fetch;
|
|
209
|
+
/** Test seam; defaults to Date.now(). */
|
|
210
|
+
now?: number;
|
|
211
|
+
env?: NodeJS.ProcessEnv;
|
|
212
|
+
/** Defaults to process.stderr.write. */
|
|
213
|
+
err?: (text: string) => void;
|
|
214
|
+
/** Skip the source-checkout guard (tests exercise the full path). */
|
|
215
|
+
force?: boolean;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Print an outdated-version warning to stderr when a newer release exists.
|
|
220
|
+
*
|
|
221
|
+
* Never throws and never blocks longer than {@link WARN_FETCH_TIMEOUT_MS}.
|
|
222
|
+
* Suppressed on source-checkout runs (there is no installed copy to update),
|
|
223
|
+
* in CI, and via NO_UPDATE_NOTIFIER / AGENT_SERVE_NO_UPDATE_CHECK.
|
|
224
|
+
*/
|
|
225
|
+
export async function warnIfOutdated(
|
|
226
|
+
options?: WarnIfOutdatedOptions
|
|
227
|
+
): Promise<void> {
|
|
228
|
+
const env = options?.env ?? process.env;
|
|
229
|
+
if (
|
|
230
|
+
isTruthyEnv(env.AGENT_SERVE_NO_UPDATE_CHECK) ||
|
|
231
|
+
isTruthyEnv(env.NO_UPDATE_NOTIFIER) ||
|
|
232
|
+
isTruthyEnv(env.CI)
|
|
233
|
+
) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (options?.force !== true && !isCompiledDistribution()) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const now = options?.now ?? Date.now();
|
|
240
|
+
const currentVersion = options?.currentVersion ?? packageVersion();
|
|
241
|
+
|
|
242
|
+
const cached = await readUpdateCheckCache(options?.configDir);
|
|
243
|
+
let latestVersion = cached?.latestVersion;
|
|
244
|
+
const fresh =
|
|
245
|
+
cached !== undefined && now - cached.checkedAt < CHECK_INTERVAL_MS;
|
|
246
|
+
if (!fresh) {
|
|
247
|
+
const fetched = await fetchLatestPublishedVersion({
|
|
248
|
+
fetchImpl: options?.fetchImpl,
|
|
249
|
+
timeoutMs: WARN_FETCH_TIMEOUT_MS,
|
|
250
|
+
});
|
|
251
|
+
latestVersion = fetched ?? latestVersion;
|
|
252
|
+
// checkedAt always advances so a flaky registry backs off to one
|
|
253
|
+
// attempt per interval instead of one per command.
|
|
254
|
+
await writeUpdateCheckCache(
|
|
255
|
+
{ checkedAt: now, latestVersion },
|
|
256
|
+
options?.configDir
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (
|
|
261
|
+
latestVersion === undefined ||
|
|
262
|
+
compareVersions(currentVersion, latestVersion) >= 0
|
|
263
|
+
) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const c = stderrPalette();
|
|
267
|
+
const err =
|
|
268
|
+
options?.err ?? ((text: string) => void process.stderr.write(text));
|
|
269
|
+
err(
|
|
270
|
+
`${c.yellow("warning:")} ${CLI_COMMAND_NAME} ${currentVersion} is outdated (latest is ${latestVersion}). Run \`${CLI_COMMAND_NAME} update\` to upgrade.\n`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function isTruthyEnv(value: string | undefined): boolean {
|
|
275
|
+
return value !== undefined && value !== "" && value !== "0";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ============================================================================
|
|
279
|
+
// `agentkit update`
|
|
280
|
+
// ============================================================================
|
|
281
|
+
|
|
282
|
+
export interface UpdateCommand {
|
|
283
|
+
cmd: string;
|
|
284
|
+
args: string[];
|
|
285
|
+
/** Working directory for local project installs. */
|
|
286
|
+
cwd?: string;
|
|
287
|
+
/** One-line human description of what will run and why. */
|
|
288
|
+
reason: string;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Figure out how the running copy was installed and which command upgrades
|
|
293
|
+
* it. Detection is path-shape based (global stores have recognizable
|
|
294
|
+
* layouts), then falls back to walking up to the project that declares the
|
|
295
|
+
* dependency. Returns undefined for layouts we cannot safely upgrade
|
|
296
|
+
* (npx cache, unrecognized stores).
|
|
297
|
+
*/
|
|
298
|
+
export async function detectUpdateCommand(args: {
|
|
299
|
+
packageRoot: string;
|
|
300
|
+
packageName?: string;
|
|
301
|
+
}): Promise<UpdateCommand | undefined> {
|
|
302
|
+
const packageName = args.packageName ?? PACKAGE_NAME;
|
|
303
|
+
const spec = `${packageName}@latest`;
|
|
304
|
+
const posixRoot = args.packageRoot.split(sep).join("/");
|
|
305
|
+
|
|
306
|
+
// npx / pnpm dlx caches are ephemeral; a fresh `npx agentkit` already
|
|
307
|
+
// resolves latest, and upgrading the cache entry in place helps nobody.
|
|
308
|
+
if (posixRoot.includes("/_npx/") || posixRoot.includes("/.pnpm-store/")) {
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
if (posixRoot.includes("/pnpm/global/")) {
|
|
312
|
+
return {
|
|
313
|
+
cmd: "pnpm",
|
|
314
|
+
args: ["add", "-g", spec],
|
|
315
|
+
reason: "installed with pnpm --global",
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
if (posixRoot.includes("/.bun/install/global/")) {
|
|
319
|
+
return {
|
|
320
|
+
cmd: "bun",
|
|
321
|
+
args: ["add", "-g", spec],
|
|
322
|
+
reason: "installed with bun --global",
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
if (
|
|
326
|
+
posixRoot.includes("/yarn/global/") ||
|
|
327
|
+
posixRoot.includes("/.config/yarn/global/")
|
|
328
|
+
) {
|
|
329
|
+
return {
|
|
330
|
+
cmd: "yarn",
|
|
331
|
+
args: ["global", "add", spec],
|
|
332
|
+
reason: "installed with yarn global",
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// A project that depends on the package: upgrade the dependency there,
|
|
337
|
+
// with whichever package manager the lockfile says the project uses.
|
|
338
|
+
const projectDir = await findDependingProjectDir(
|
|
339
|
+
args.packageRoot,
|
|
340
|
+
packageName
|
|
341
|
+
);
|
|
342
|
+
if (projectDir !== undefined) {
|
|
343
|
+
const pm = await detectProjectPackageManager(projectDir);
|
|
344
|
+
const command =
|
|
345
|
+
pm === "pnpm"
|
|
346
|
+
? { cmd: "pnpm", args: ["add", spec] }
|
|
347
|
+
: pm === "yarn"
|
|
348
|
+
? { cmd: "yarn", args: ["add", spec] }
|
|
349
|
+
: pm === "bun"
|
|
350
|
+
? { cmd: "bun", args: ["add", spec] }
|
|
351
|
+
: { cmd: "npm", args: ["install", spec] };
|
|
352
|
+
return {
|
|
353
|
+
...command,
|
|
354
|
+
cwd: projectDir,
|
|
355
|
+
reason: `project dependency in ${projectDir}`,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Global npm layout: <prefix>/lib/node_modules/<name> (or <prefix>\
|
|
360
|
+
// node_modules\<name> on Windows), with no depending project above it.
|
|
361
|
+
if (posixRoot.includes("/node_modules/")) {
|
|
362
|
+
return {
|
|
363
|
+
cmd: "npm",
|
|
364
|
+
args: ["install", "-g", spec],
|
|
365
|
+
reason: "installed with npm --global",
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Nearest ancestor with a package.json that declares `packageName` as a
|
|
373
|
+
* dependency. Skips directories inside node_modules (pnpm's virtual store
|
|
374
|
+
* nests package.json files that are not the user's project).
|
|
375
|
+
*/
|
|
376
|
+
async function findDependingProjectDir(
|
|
377
|
+
packageRoot: string,
|
|
378
|
+
packageName: string
|
|
379
|
+
): Promise<string | undefined> {
|
|
380
|
+
let dir = dirname(packageRoot);
|
|
381
|
+
for (let depth = 0; depth < 30; depth++) {
|
|
382
|
+
const parent = dirname(dir);
|
|
383
|
+
if (parent === dir) {
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
const inNodeModules = dir
|
|
387
|
+
.split(sep)
|
|
388
|
+
.some((part) => part === "node_modules" || part === ".pnpm");
|
|
389
|
+
if (!inNodeModules && (await declaresDependency(dir, packageName))) {
|
|
390
|
+
return dir;
|
|
391
|
+
}
|
|
392
|
+
dir = parent;
|
|
393
|
+
}
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async function declaresDependency(
|
|
398
|
+
dir: string,
|
|
399
|
+
packageName: string
|
|
400
|
+
): Promise<boolean> {
|
|
401
|
+
let raw: string;
|
|
402
|
+
try {
|
|
403
|
+
raw = await readFile(join(dir, "package.json"), "utf8");
|
|
404
|
+
} catch {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
const manifestSchema = z.object({
|
|
408
|
+
dependencies: z.record(z.string()).optional(),
|
|
409
|
+
devDependencies: z.record(z.string()).optional(),
|
|
410
|
+
});
|
|
411
|
+
try {
|
|
412
|
+
const parsed = manifestSchema.safeParse(JSON.parse(raw));
|
|
413
|
+
if (!parsed.success) {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
return (
|
|
417
|
+
parsed.data.dependencies?.[packageName] !== undefined ||
|
|
418
|
+
parsed.data.devDependencies?.[packageName] !== undefined
|
|
419
|
+
);
|
|
420
|
+
} catch {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async function detectProjectPackageManager(
|
|
426
|
+
projectDir: string
|
|
427
|
+
): Promise<"pnpm" | "yarn" | "bun" | "npm"> {
|
|
428
|
+
const exists = async (file: string): Promise<boolean> => {
|
|
429
|
+
try {
|
|
430
|
+
await stat(join(projectDir, file));
|
|
431
|
+
return true;
|
|
432
|
+
} catch {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
if (await exists("pnpm-lock.yaml")) {
|
|
437
|
+
return "pnpm";
|
|
438
|
+
}
|
|
439
|
+
if (await exists("yarn.lock")) {
|
|
440
|
+
return "yarn";
|
|
441
|
+
}
|
|
442
|
+
if ((await exists("bun.lock")) || (await exists("bun.lockb"))) {
|
|
443
|
+
return "bun";
|
|
444
|
+
}
|
|
445
|
+
return "npm";
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export interface UpdateCliOptions {
|
|
449
|
+
/** Test seams. */
|
|
450
|
+
fetchImpl?: typeof fetch;
|
|
451
|
+
currentVersion?: string;
|
|
452
|
+
packageRoot?: string;
|
|
453
|
+
configDir?: string;
|
|
454
|
+
runCommand?: (command: UpdateCommand) => Promise<number>;
|
|
455
|
+
compiled?: boolean;
|
|
456
|
+
out?: (text: string) => void;
|
|
457
|
+
err?: (text: string) => void;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/** `agentkit update` — upgrade this install to the latest published version. */
|
|
461
|
+
export async function cmdUpdate(options?: UpdateCliOptions): Promise<number> {
|
|
462
|
+
const c = stderrPalette();
|
|
463
|
+
const out =
|
|
464
|
+
options?.out ?? ((text: string) => void process.stdout.write(text));
|
|
465
|
+
const err =
|
|
466
|
+
options?.err ?? ((text: string) => void process.stderr.write(text));
|
|
467
|
+
const compiled = options?.compiled ?? isCompiledDistribution();
|
|
468
|
+
if (!compiled) {
|
|
469
|
+
err(
|
|
470
|
+
`${CLI_COMMAND_NAME} is running from a source checkout, not an installed package; update it with git instead.\n`
|
|
471
|
+
);
|
|
472
|
+
return 1;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const currentVersion = options?.currentVersion ?? packageVersion();
|
|
476
|
+
const latestVersion = await fetchLatestPublishedVersion({
|
|
477
|
+
fetchImpl: options?.fetchImpl,
|
|
478
|
+
timeoutMs: UPDATE_FETCH_TIMEOUT_MS,
|
|
479
|
+
});
|
|
480
|
+
if (latestVersion === undefined) {
|
|
481
|
+
err(
|
|
482
|
+
`Could not reach the npm registry to look up the latest ${PACKAGE_NAME} version. Check your network and retry.\n`
|
|
483
|
+
);
|
|
484
|
+
return 1;
|
|
485
|
+
}
|
|
486
|
+
if (compareVersions(currentVersion, latestVersion) >= 0) {
|
|
487
|
+
out(
|
|
488
|
+
`${CLI_COMMAND_NAME} ${currentVersion} is already the latest version.\n`
|
|
489
|
+
);
|
|
490
|
+
await writeUpdateCheckCache(
|
|
491
|
+
{ checkedAt: Date.now(), latestVersion },
|
|
492
|
+
options?.configDir
|
|
493
|
+
);
|
|
494
|
+
return 0;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const command = await detectUpdateCommand({
|
|
498
|
+
packageRoot: options?.packageRoot ?? packageRootDir(),
|
|
499
|
+
});
|
|
500
|
+
if (command === undefined) {
|
|
501
|
+
err(
|
|
502
|
+
`${CLI_COMMAND_NAME} ${currentVersion} is outdated (latest is ${latestVersion}), but this install's layout was not recognized.\nUpgrade it yourself, e.g. \`npm install -g ${PACKAGE_NAME}@latest\` for a global install, or bump the ${PACKAGE_NAME} dependency in your project.\n`
|
|
503
|
+
);
|
|
504
|
+
return 1;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const rendered = [command.cmd, ...command.args].join(" ");
|
|
508
|
+
err(
|
|
509
|
+
c.dim(
|
|
510
|
+
`Updating ${CLI_COMMAND_NAME} ${currentVersion} → ${latestVersion} (${command.reason}):\n ${rendered}\n`
|
|
511
|
+
)
|
|
512
|
+
);
|
|
513
|
+
const run = options?.runCommand ?? runUpdateCommand;
|
|
514
|
+
let code: number;
|
|
515
|
+
try {
|
|
516
|
+
code = await run(command);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
err(`\`${rendered}\` failed: ${describeError(error)}\n`);
|
|
519
|
+
return 1;
|
|
520
|
+
}
|
|
521
|
+
if (code !== 0) {
|
|
522
|
+
err(`\`${rendered}\` exited with code ${code}.\n`);
|
|
523
|
+
return code;
|
|
524
|
+
}
|
|
525
|
+
await writeUpdateCheckCache(
|
|
526
|
+
{ checkedAt: Date.now(), latestVersion },
|
|
527
|
+
options?.configDir
|
|
528
|
+
);
|
|
529
|
+
out(
|
|
530
|
+
`Updated ${CLI_COMMAND_NAME} to ${latestVersion}. New commands run the new version.\n`
|
|
531
|
+
);
|
|
532
|
+
return 0;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function runUpdateCommand(command: UpdateCommand): Promise<number> {
|
|
536
|
+
return new Promise((resolve, reject) => {
|
|
537
|
+
// Windows-only shell: npm/pnpm/yarn/bun are .cmd shims there, and Node
|
|
538
|
+
// refuses to spawn .cmd files without a shell (EINVAL, CVE-2024-27980
|
|
539
|
+
// hardening). Injection-safe: cmd and args are module-level constants
|
|
540
|
+
// from detectUpdateCommand (never user or registry input).
|
|
541
|
+
// nosemgrep: javascript.lang.security.audit.spawn-shell-true.spawn-shell-true
|
|
542
|
+
const child = spawn(command.cmd, command.args, {
|
|
543
|
+
stdio: "inherit",
|
|
544
|
+
cwd: command.cwd,
|
|
545
|
+
shell: process.platform === "win32",
|
|
546
|
+
});
|
|
547
|
+
child.on("error", reject);
|
|
548
|
+
child.on("close", (code) => {
|
|
549
|
+
resolve(code ?? 1);
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
}
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
ResolvedAgent,
|
|
16
16
|
} from "../types.js";
|
|
17
17
|
import { parseFrontmatter, serializeFrontmatter } from "./frontmatter.js";
|
|
18
|
+
import { buildIdentitySection } from "./prompt-context.js";
|
|
18
19
|
|
|
19
20
|
export interface MaterializeWorkspaceOptions {
|
|
20
21
|
agent: ResolvedAgent;
|
|
@@ -31,6 +32,41 @@ export function agentToolScriptPath(toolName: string): string {
|
|
|
31
32
|
return `.agent-serve/tools/${toolName}.sh`;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Always-on identity line prepended when composing instructions. Sourced from
|
|
37
|
+
* {@link ResolvedAgent.name} (`defineAgent({ name })`, else package/dir name)
|
|
38
|
+
* so agents know who they are without restating it in authored prose.
|
|
39
|
+
*/
|
|
40
|
+
export function buildIdentityPreamble(name: string): string {
|
|
41
|
+
const trimmed = name.trim();
|
|
42
|
+
if (trimmed === "") {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
return `You are ${trimmed}. That is your name; use it when people ask who you are.`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Prepend {@link buildIdentityPreamble} to authored instructions. Used when
|
|
50
|
+
* loading `instructions.md` / `.ts` / dir into {@link ResolvedAgent.instructions}.
|
|
51
|
+
*/
|
|
52
|
+
export function withIdentityPreamble(
|
|
53
|
+
name: string,
|
|
54
|
+
instructions: string | undefined
|
|
55
|
+
): string | undefined {
|
|
56
|
+
const preamble = buildIdentityPreamble(name);
|
|
57
|
+
const body = instructions?.trim() ?? "";
|
|
58
|
+
if (preamble === "" && body === "") {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
if (preamble === "") {
|
|
62
|
+
return body === "" ? undefined : body;
|
|
63
|
+
}
|
|
64
|
+
if (body === "") {
|
|
65
|
+
return preamble;
|
|
66
|
+
}
|
|
67
|
+
return `${preamble}\n\n${body}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
34
70
|
/**
|
|
35
71
|
* Markdown catalog of agent-side tools for AGENTS.md / cloud first prompt.
|
|
36
72
|
*
|
|
@@ -96,12 +132,14 @@ export async function materializeWorkspace(
|
|
|
96
132
|
const agentToolsSection = buildAgentToolsCatalog(agent.tools, {
|
|
97
133
|
includeScripts: false,
|
|
98
134
|
});
|
|
99
|
-
const agentsMd = [
|
|
135
|
+
const agentsMd = [
|
|
136
|
+
agent.instructions?.trim() ?? "",
|
|
137
|
+
buildIdentitySection(agent),
|
|
138
|
+
agentToolsSection,
|
|
139
|
+
]
|
|
100
140
|
.filter((part) => part !== "")
|
|
101
141
|
.join("\n\n");
|
|
102
|
-
|
|
103
|
-
await writeFile(join(workspaceDir, "AGENTS.md"), agentsMd, "utf8");
|
|
104
|
-
}
|
|
142
|
+
await writeFile(join(workspaceDir, "AGENTS.md"), agentsMd, "utf8");
|
|
105
143
|
|
|
106
144
|
for (const tool of agent.tools) {
|
|
107
145
|
if (tool.execution !== "agent" || tool.script === undefined) {
|