@agentrific/server 0.6.0 → 0.7.0
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/CHANGELOG.md +12 -0
- package/CLAUDE-BRIDGE-LICENSE.txt +21 -0
- package/README.md +10 -0
- package/claude-bridge.js +2682 -0
- package/cli.js +275 -96
- package/cli.js.map +4 -4
- package/index.js +250 -79
- package/index.js.map +4 -4
- package/opentunnel-sdk.js +211 -211
- package/opentunnel-sdk.js.map +1 -1
- package/package.json +10 -2
package/index.js
CHANGED
|
@@ -885,10 +885,10 @@ var Attachments = class extends Context.Service()(
|
|
|
885
885
|
|
|
886
886
|
// src/index.ts
|
|
887
887
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
888
|
-
import { execFile as
|
|
889
|
-
import { promisify as
|
|
888
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
889
|
+
import { promisify as promisify4 } from "node:util";
|
|
890
890
|
import { realpath as realpath2 } from "node:fs/promises";
|
|
891
|
-
import { homedir as
|
|
891
|
+
import { homedir as homedir4 } from "node:os";
|
|
892
892
|
import path11 from "node:path";
|
|
893
893
|
import { WebSocket } from "ws";
|
|
894
894
|
|
|
@@ -993,6 +993,7 @@ var ControlStatusSchema = z4.object({
|
|
|
993
993
|
serverId: z4.string().uuid(),
|
|
994
994
|
publicUrl: z4.string().optional(),
|
|
995
995
|
phoneProvider: ProviderSchema.nullable().optional(),
|
|
996
|
+
phoneProgress: z4.string().nullable().optional(),
|
|
996
997
|
localUrl: z4.string().optional(),
|
|
997
998
|
serverVersion: z4.string().optional(),
|
|
998
999
|
connectedDevices: z4.number().optional(),
|
|
@@ -1002,7 +1003,11 @@ var ControlStatusSchema = z4.object({
|
|
|
1002
1003
|
maintenance: MaintenanceSchema.optional()
|
|
1003
1004
|
});
|
|
1004
1005
|
var ControlResultSchema = z4.union([
|
|
1005
|
-
z4.object({
|
|
1006
|
+
z4.object({
|
|
1007
|
+
provider: ProviderSchema.nullable(),
|
|
1008
|
+
publicUrl: z4.string().url().nullable(),
|
|
1009
|
+
progress: z4.string().nullable().optional()
|
|
1010
|
+
}),
|
|
1006
1011
|
z4.array(z4.object({ provider: ProviderSchema, available: z4.boolean(), reason: z4.string() })),
|
|
1007
1012
|
z4.object({
|
|
1008
1013
|
pairingId: z4.string(),
|
|
@@ -1173,7 +1178,7 @@ import { styleText } from "node:util";
|
|
|
1173
1178
|
// package.json
|
|
1174
1179
|
var package_default = {
|
|
1175
1180
|
name: "@agentrific/server",
|
|
1176
|
-
version: "0.
|
|
1181
|
+
version: "0.7.0",
|
|
1177
1182
|
description: "Self-hosted control plane for long-running coding agents",
|
|
1178
1183
|
keywords: [
|
|
1179
1184
|
"agentrific",
|
|
@@ -1199,20 +1204,28 @@ var package_default = {
|
|
|
1199
1204
|
registry: "https://registry.npmjs.org"
|
|
1200
1205
|
},
|
|
1201
1206
|
scripts: {
|
|
1202
|
-
dev: "tsx watch src/index.ts",
|
|
1207
|
+
dev: "node scripts/build-claude-bridge.mjs && tsx watch src/index.ts",
|
|
1203
1208
|
start: "node dist/index.js",
|
|
1204
1209
|
typecheck: "tsc --noEmit",
|
|
1205
1210
|
build: "node scripts/build.mjs",
|
|
1206
|
-
cli: "AGENTRIFIC_DATA_DIR=./data tsx src/cli.ts"
|
|
1211
|
+
cli: "node scripts/build-claude-bridge.mjs && AGENTRIFIC_DATA_DIR=./data tsx src/cli.ts",
|
|
1212
|
+
"build:claude": "node scripts/build-claude-bridge.mjs"
|
|
1207
1213
|
},
|
|
1208
1214
|
dependencies: {
|
|
1215
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.296",
|
|
1216
|
+
"@anthropic-ai/sdk": "^0.124.0",
|
|
1217
|
+
"@earendil-works/pi-ai": "1.0.0",
|
|
1209
1218
|
"@earendil-works/pi-coding-agent": "1.0.0",
|
|
1210
1219
|
"@fastify/cors": "^11.3.0",
|
|
1211
1220
|
"@fastify/multipart": "^10.1.2",
|
|
1212
1221
|
"@inquirer/select": "^5.2.5",
|
|
1222
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
1223
|
+
"cc-session-io": "^0.4.0",
|
|
1224
|
+
"change-case": "^5.4.4",
|
|
1213
1225
|
diff: "^9.0.0",
|
|
1214
1226
|
effect: "4.0.0-rc.117",
|
|
1215
1227
|
fastify: "^5.6.2",
|
|
1228
|
+
"pi-claude-agent-sdk": "0.9.2",
|
|
1216
1229
|
"pino-pretty": "^13.1.3",
|
|
1217
1230
|
"qrcode-terminal": "^0.12.0",
|
|
1218
1231
|
semver: "^7.8.5",
|
|
@@ -1712,6 +1725,10 @@ var OpenTunnel = class {
|
|
|
1712
1725
|
client;
|
|
1713
1726
|
connection;
|
|
1714
1727
|
stopping = false;
|
|
1728
|
+
stage = "reading-identity";
|
|
1729
|
+
progress() {
|
|
1730
|
+
return this.connection?.status().state ?? this.stage;
|
|
1731
|
+
}
|
|
1715
1732
|
running() {
|
|
1716
1733
|
return this.connection?.status().state === "connected" && !this.stopping;
|
|
1717
1734
|
}
|
|
@@ -1741,13 +1758,32 @@ var OpenTunnel = class {
|
|
|
1741
1758
|
let canceled = () => {
|
|
1742
1759
|
};
|
|
1743
1760
|
try {
|
|
1744
|
-
const
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1761
|
+
const client = this.client;
|
|
1762
|
+
this.stage = "reading-identity";
|
|
1763
|
+
const connecting = (async () => {
|
|
1764
|
+
const options = {
|
|
1765
|
+
profile: "agentrific",
|
|
1766
|
+
onProgress: (stage) => {
|
|
1767
|
+
this.stage = stage;
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
const identity2 = await client.tunnel.get(options);
|
|
1771
|
+
abort.throwIfAborted();
|
|
1772
|
+
if (!identity2) {
|
|
1773
|
+
const resumed = await client.tunnel.resume(options);
|
|
1774
|
+
abort.throwIfAborted();
|
|
1775
|
+
if (!resumed) await client.tunnel.create(options);
|
|
1776
|
+
}
|
|
1777
|
+
abort.throwIfAborted();
|
|
1778
|
+
this.stage = "connecting";
|
|
1779
|
+
return client.tunnel.connect({
|
|
1780
|
+
profile: "agentrific",
|
|
1781
|
+
routes: {
|
|
1782
|
+
[route]: `${endpoint.hostname === "[::1]" ? "[::1]" : "127.0.0.1"}:${endpoint.port || "80"}`
|
|
1783
|
+
},
|
|
1784
|
+
signal
|
|
1785
|
+
});
|
|
1786
|
+
})();
|
|
1751
1787
|
const cancellation = new Promise((_resolve, reject) => {
|
|
1752
1788
|
canceled = () => reject(new Error("OpenTunnel connection canceled or timed out."));
|
|
1753
1789
|
abort.addEventListener("abort", canceled, { once: true });
|
|
@@ -1799,7 +1835,11 @@ var DesktopPhoneConnection = class {
|
|
|
1799
1835
|
publicUrl = null;
|
|
1800
1836
|
pending = Promise.resolve();
|
|
1801
1837
|
snapshot() {
|
|
1802
|
-
return {
|
|
1838
|
+
return {
|
|
1839
|
+
provider: this.provider,
|
|
1840
|
+
publicUrl: this.publicUrl,
|
|
1841
|
+
progress: this.tunnel?.progress?.() ?? null
|
|
1842
|
+
};
|
|
1803
1843
|
}
|
|
1804
1844
|
async options() {
|
|
1805
1845
|
return (await this.detect()).map(({ provider, available, reason }) => ({
|
|
@@ -2013,7 +2053,7 @@ var UpdateDiscovery = class {
|
|
|
2013
2053
|
|
|
2014
2054
|
// src/index.ts
|
|
2015
2055
|
import { Effect as Effect17, ManagedRuntime } from "effect";
|
|
2016
|
-
import { z as
|
|
2056
|
+
import { z as z20 } from "zod";
|
|
2017
2057
|
|
|
2018
2058
|
// src/automation.ts
|
|
2019
2059
|
import { Effect as Effect7 } from "effect";
|
|
@@ -3179,14 +3219,17 @@ var DraftSchema = z13.object({ title: z13.string().trim().min(1).max(120), body:
|
|
|
3179
3219
|
function deliveryWriter(models2, createSession = createAgentSession) {
|
|
3180
3220
|
return async (run, input, diff, signal) => {
|
|
3181
3221
|
signal?.throwIfAborted();
|
|
3222
|
+
const selected = run.model ?? await models2.defaultReference();
|
|
3223
|
+
const sessionOptions = await models2.sessionOptions(run.workspacePath);
|
|
3182
3224
|
const { session } = await createSession({
|
|
3183
3225
|
cwd: run.workspacePath,
|
|
3184
|
-
model: await models2.resolve(
|
|
3185
|
-
|
|
3226
|
+
model: await models2.resolve(selected),
|
|
3227
|
+
...sessionOptions,
|
|
3186
3228
|
sessionManager: SessionManager.inMemory(),
|
|
3187
3229
|
tools: [],
|
|
3188
3230
|
noTools: "all"
|
|
3189
3231
|
});
|
|
3232
|
+
if (sessionOptions.resourceLoader) await session.bindExtensions({});
|
|
3190
3233
|
const timer = setTimeout(() => {
|
|
3191
3234
|
void session.abort();
|
|
3192
3235
|
}, 6e4);
|
|
@@ -3216,6 +3259,103 @@ ${diff}`
|
|
|
3216
3259
|
// src/index.ts
|
|
3217
3260
|
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
3218
3261
|
|
|
3262
|
+
// src/claude-code.ts
|
|
3263
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
3264
|
+
import { homedir as homedir2 } from "node:os";
|
|
3265
|
+
import { join as join2 } from "node:path";
|
|
3266
|
+
import { promisify as promisify2 } from "node:util";
|
|
3267
|
+
import { z as z14 } from "zod";
|
|
3268
|
+
import {
|
|
3269
|
+
createAgentSessionServices
|
|
3270
|
+
} from "@earendil-works/pi-coding-agent";
|
|
3271
|
+
var claudePlatform = "claude-bridge";
|
|
3272
|
+
var execute2 = promisify2(execFile2);
|
|
3273
|
+
var AuthStatusSchema = z14.object({ loggedIn: z14.boolean() });
|
|
3274
|
+
async function discoverClaudeCode() {
|
|
3275
|
+
if (process.env.AGENTRIFIC_CLAUDE_CODE === "off") return;
|
|
3276
|
+
const candidates = [
|
|
3277
|
+
"claude",
|
|
3278
|
+
join2(homedir2(), ".local/bin/claude"),
|
|
3279
|
+
join2(homedir2(), ".claude/local/claude")
|
|
3280
|
+
];
|
|
3281
|
+
for (const executable of candidates) {
|
|
3282
|
+
try {
|
|
3283
|
+
await execute2(executable, ["--version"], { timeout: 5e3, maxBuffer: 64 * 1024 });
|
|
3284
|
+
} catch {
|
|
3285
|
+
continue;
|
|
3286
|
+
}
|
|
3287
|
+
try {
|
|
3288
|
+
const { stdout } = await execute2(executable, ["auth", "status", "--json"], {
|
|
3289
|
+
timeout: 5e3,
|
|
3290
|
+
maxBuffer: 64 * 1024
|
|
3291
|
+
});
|
|
3292
|
+
const status = AuthStatusSchema.parse(JSON.parse(stdout));
|
|
3293
|
+
return { executable, ready: status.loggedIn };
|
|
3294
|
+
} catch {
|
|
3295
|
+
return { executable, ready: false };
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
var ClaudeCode = class {
|
|
3300
|
+
constructor(probe = discoverClaudeCode, query) {
|
|
3301
|
+
this.probe = probe;
|
|
3302
|
+
this.query = query;
|
|
3303
|
+
}
|
|
3304
|
+
probe;
|
|
3305
|
+
query;
|
|
3306
|
+
installation;
|
|
3307
|
+
registered = false;
|
|
3308
|
+
get ready() {
|
|
3309
|
+
return this.installation?.ready === true;
|
|
3310
|
+
}
|
|
3311
|
+
get warning() {
|
|
3312
|
+
return this.installation && !this.ready ? "Claude Code is installed but its login could not be verified. Run claude auth login as the server user, then refresh the model list." : null;
|
|
3313
|
+
}
|
|
3314
|
+
async refresh(runtime2) {
|
|
3315
|
+
this.installation = await this.probe();
|
|
3316
|
+
if (this.installation && !this.registered) {
|
|
3317
|
+
await this.services(process.cwd(), runtime2);
|
|
3318
|
+
this.registered = true;
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
async services(cwd, modelRuntime2) {
|
|
3322
|
+
const module = await import(new URL("./claude-bridge.js", import.meta.url).href);
|
|
3323
|
+
const extension = module.createClaudeExtension(this.query);
|
|
3324
|
+
const services = await createAgentSessionServices({
|
|
3325
|
+
cwd,
|
|
3326
|
+
modelRuntime: modelRuntime2,
|
|
3327
|
+
resourceLoaderOptions: {
|
|
3328
|
+
// Match Pipeline: do not execute unrelated global Pi extensions.
|
|
3329
|
+
noExtensions: true,
|
|
3330
|
+
extensionFactories: [
|
|
3331
|
+
{
|
|
3332
|
+
name: "agentrific-claude-code",
|
|
3333
|
+
factory: (pi) => extension({
|
|
3334
|
+
...pi,
|
|
3335
|
+
// SAFETY: The pinned bridge uses only the name/config registration overload.
|
|
3336
|
+
registerProvider: ((id, config) => pi.registerProvider(id, {
|
|
3337
|
+
...config,
|
|
3338
|
+
// Bind the working directory to this provider,
|
|
3339
|
+
// without changing the process cwd or global Claude configuration.
|
|
3340
|
+
streamSimple: config.streamSimple && ((model, context, options) => {
|
|
3341
|
+
const request = { ...options, cwd };
|
|
3342
|
+
return config.streamSimple(model, context, request);
|
|
3343
|
+
})
|
|
3344
|
+
}))
|
|
3345
|
+
})
|
|
3346
|
+
}
|
|
3347
|
+
]
|
|
3348
|
+
}
|
|
3349
|
+
});
|
|
3350
|
+
const errors = [
|
|
3351
|
+
...services.diagnostics.filter((item) => item.type === "error").map((item) => item.message),
|
|
3352
|
+
...services.resourceLoader.getExtensions().errors.map((item) => item.error)
|
|
3353
|
+
];
|
|
3354
|
+
if (errors.length) throw new Error(errors.join("\n"));
|
|
3355
|
+
return services;
|
|
3356
|
+
}
|
|
3357
|
+
};
|
|
3358
|
+
|
|
3219
3359
|
// src/models.ts
|
|
3220
3360
|
import { Effect as Effect11 } from "effect";
|
|
3221
3361
|
var legacyModel = { platformId: "openai-codex", modelId: "gpt-6-luna" };
|
|
@@ -3234,12 +3374,14 @@ var makers = [
|
|
|
3234
3374
|
{ id: "amazon", name: "Amazon", pattern: /amazon\.nova|nova-/i }
|
|
3235
3375
|
];
|
|
3236
3376
|
var ServerModels = class {
|
|
3237
|
-
constructor(runtime2, store2) {
|
|
3377
|
+
constructor(runtime2, store2, claude) {
|
|
3238
3378
|
this.runtime = runtime2;
|
|
3239
3379
|
this.store = store2;
|
|
3380
|
+
this.claude = claude;
|
|
3240
3381
|
}
|
|
3241
3382
|
runtime;
|
|
3242
3383
|
store;
|
|
3384
|
+
claude;
|
|
3243
3385
|
refreshing;
|
|
3244
3386
|
refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3245
3387
|
warning = null;
|
|
@@ -3261,18 +3403,20 @@ var ServerModels = class {
|
|
|
3261
3403
|
platformId,
|
|
3262
3404
|
modelId,
|
|
3263
3405
|
name: model?.name ?? modelId,
|
|
3264
|
-
platformName: this.runtime.getProvider(platformId)?.name ?? platformId,
|
|
3406
|
+
platformName: platformId === claudePlatform ? "Claude Code" : this.runtime.getProvider(platformId)?.name ?? platformId,
|
|
3265
3407
|
providerId: namespace === "openrouter" ? "unspecified" : maker?.id ?? namespace ?? "unspecified",
|
|
3266
3408
|
providerName: namespace === "openrouter" ? "Other / custom" : maker?.name ?? publisherName ?? "Other / custom"
|
|
3267
3409
|
};
|
|
3268
3410
|
}
|
|
3269
3411
|
async refresh(network) {
|
|
3270
3412
|
if (!this.refreshing) {
|
|
3271
|
-
this.refreshing = this.runtime.
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3413
|
+
this.refreshing = Promise.resolve(this.claude?.refresh(this.runtime)).then(
|
|
3414
|
+
() => this.runtime.refresh({
|
|
3415
|
+
allowNetwork: network,
|
|
3416
|
+
force: network,
|
|
3417
|
+
signal: AbortSignal.timeout(15e3)
|
|
3418
|
+
})
|
|
3419
|
+
).then((result) => {
|
|
3276
3420
|
this.refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3277
3421
|
this.warning = result.aborted || result.errors.size || this.runtime.getError() ? "Some providers could not refresh. Showing the catalogue currently available on this server." : null;
|
|
3278
3422
|
}).finally(() => {
|
|
@@ -3282,7 +3426,10 @@ var ServerModels = class {
|
|
|
3282
3426
|
await this.refreshing;
|
|
3283
3427
|
}
|
|
3284
3428
|
async defaultReference() {
|
|
3285
|
-
|
|
3429
|
+
const saved = await Effect11.runPromise(this.store.defaultModel());
|
|
3430
|
+
if (saved) return saved;
|
|
3431
|
+
const preferred = this.claude?.ready ? this.runtime.getModels().find((model) => model.provider === claudePlatform && /sonnet/i.test(model.id)) : void 0;
|
|
3432
|
+
return preferred ? { platformId: preferred.provider, modelId: preferred.id } : legacyModel;
|
|
3286
3433
|
}
|
|
3287
3434
|
async defaultIdentity() {
|
|
3288
3435
|
const selected = await this.defaultReference();
|
|
@@ -3295,17 +3442,24 @@ var ServerModels = class {
|
|
|
3295
3442
|
return {
|
|
3296
3443
|
models: this.runtime.getModels().map((model) => ({
|
|
3297
3444
|
...this.identity(model.provider, model.id),
|
|
3298
|
-
available: available.has(`${model.provider}\0${model.id}`),
|
|
3445
|
+
available: available.has(`${model.provider}\0${model.id}`) && (model.provider !== claudePlatform || this.claude?.ready === true),
|
|
3299
3446
|
reasoning: model.reasoning,
|
|
3300
3447
|
input: model.input,
|
|
3301
3448
|
contextWindow: model.contextWindow
|
|
3302
3449
|
})),
|
|
3303
3450
|
defaultModel: await this.defaultIdentity(),
|
|
3304
3451
|
refreshedAt: this.refreshedAt,
|
|
3305
|
-
warning: this.warning
|
|
3452
|
+
warning: this.claude?.warning ?? this.warning
|
|
3306
3453
|
};
|
|
3307
3454
|
}
|
|
3308
3455
|
async resolve(reference) {
|
|
3456
|
+
if (reference.platformId === claudePlatform && this.claude) {
|
|
3457
|
+
await this.claude.refresh(this.runtime);
|
|
3458
|
+
if (!this.claude.ready)
|
|
3459
|
+
throw new Error(
|
|
3460
|
+
this.claude.warning ?? "Claude Code is unavailable on this server. Install it and run claude auth login as the server user."
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3309
3463
|
const available = await this.runtime.getAvailable(reference.platformId, {
|
|
3310
3464
|
signal: AbortSignal.timeout(1e4)
|
|
3311
3465
|
});
|
|
@@ -3316,6 +3470,17 @@ var ServerModels = class {
|
|
|
3316
3470
|
);
|
|
3317
3471
|
return model;
|
|
3318
3472
|
}
|
|
3473
|
+
async sessionOptions(cwd = process.cwd()) {
|
|
3474
|
+
if (this.claude && this.runtime.getProvider(claudePlatform)) {
|
|
3475
|
+
const services = await this.claude.services(cwd);
|
|
3476
|
+
return {
|
|
3477
|
+
modelRuntime: services.modelRuntime,
|
|
3478
|
+
resourceLoader: services.resourceLoader,
|
|
3479
|
+
settingsManager: services.settingsManager
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
return { modelRuntime: this.runtime };
|
|
3483
|
+
}
|
|
3319
3484
|
async setDefault(reference) {
|
|
3320
3485
|
await this.resolve(reference);
|
|
3321
3486
|
await Effect11.runPromise(this.store.setDefaultModel(reference));
|
|
@@ -3330,7 +3495,7 @@ import {
|
|
|
3330
3495
|
createAgentSession as createAgentSession2,
|
|
3331
3496
|
SessionManager as SessionManager2
|
|
3332
3497
|
} from "@earendil-works/pi-coding-agent";
|
|
3333
|
-
import { z as
|
|
3498
|
+
import { z as z16 } from "zod";
|
|
3334
3499
|
|
|
3335
3500
|
// src/delivery-tool.ts
|
|
3336
3501
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
@@ -3368,15 +3533,15 @@ function deliveryTool(sessionId, delivery2) {
|
|
|
3368
3533
|
import { createWriteToolDefinition, defineTool as defineTool2 } from "@earendil-works/pi-coding-agent";
|
|
3369
3534
|
import { createTwoFilesPatch } from "diff";
|
|
3370
3535
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
|
|
3371
|
-
import { z as
|
|
3536
|
+
import { z as z15 } from "zod";
|
|
3372
3537
|
var previewLimit = 256e3;
|
|
3373
3538
|
function boundFileChange(change) {
|
|
3374
3539
|
if ((change.patch?.length ?? 0) + (change.content?.length ?? 0) > previewLimit)
|
|
3375
3540
|
return { path: change.path, omitted: true };
|
|
3376
3541
|
return change;
|
|
3377
3542
|
}
|
|
3378
|
-
var EditResultSchema =
|
|
3379
|
-
details:
|
|
3543
|
+
var EditResultSchema = z15.object({
|
|
3544
|
+
details: z15.object({ patch: z15.string().optional(), fileChange: FileChangeSchema.optional() }).optional()
|
|
3380
3545
|
});
|
|
3381
3546
|
function completedFileChange(result, path12) {
|
|
3382
3547
|
const parsed = EditResultSchema.safeParse(result);
|
|
@@ -3426,8 +3591,8 @@ function fileWriteTool(cwd) {
|
|
|
3426
3591
|
}
|
|
3427
3592
|
|
|
3428
3593
|
// src/pi.ts
|
|
3429
|
-
var ToolResultSchema =
|
|
3430
|
-
content:
|
|
3594
|
+
var ToolResultSchema = z16.object({
|
|
3595
|
+
content: z16.array(z16.object({ type: z16.string(), text: z16.string().optional() })).optional()
|
|
3431
3596
|
}).passthrough();
|
|
3432
3597
|
var boundedJson = (value, limit) => {
|
|
3433
3598
|
let text;
|
|
@@ -3447,16 +3612,18 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3447
3612
|
return {
|
|
3448
3613
|
async open(sessionId, cwd, sessionDirectory, selected = legacyModel, run) {
|
|
3449
3614
|
const model = await models2.resolve(selected);
|
|
3615
|
+
const sessionOptions = await models2.sessionOptions(cwd);
|
|
3450
3616
|
const { session } = await createSession({
|
|
3451
3617
|
cwd,
|
|
3452
3618
|
model,
|
|
3453
|
-
|
|
3619
|
+
...sessionOptions,
|
|
3454
3620
|
sessionManager: SessionManager2.continueRecent(cwd, sessionDirectory),
|
|
3455
3621
|
customTools: [
|
|
3456
3622
|
fileWriteTool(cwd),
|
|
3457
3623
|
...run?.gitFlow && delivery2 ? [deliveryTool(sessionId, delivery2)] : []
|
|
3458
3624
|
]
|
|
3459
3625
|
});
|
|
3626
|
+
if (sessionOptions.resourceLoader) await session.bindExtensions({});
|
|
3460
3627
|
return {
|
|
3461
3628
|
get model() {
|
|
3462
3629
|
const current = session.model;
|
|
@@ -3551,7 +3718,7 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3551
3718
|
return false;
|
|
3552
3719
|
}
|
|
3553
3720
|
const filePaths = /* @__PURE__ */ new Map();
|
|
3554
|
-
const FileArgsSchema =
|
|
3721
|
+
const FileArgsSchema = z16.object({ path: z16.string() });
|
|
3555
3722
|
function handleTool(event) {
|
|
3556
3723
|
if (event.type === "tool_execution_start") {
|
|
3557
3724
|
flushText();
|
|
@@ -3613,16 +3780,16 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3613
3780
|
// src/projects.ts
|
|
3614
3781
|
import { access as access2, readdir as readdir2, realpath, stat as stat3, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
3615
3782
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
3616
|
-
import { z as
|
|
3783
|
+
import { z as z17 } from "zod";
|
|
3617
3784
|
import { constants as constants2 } from "node:fs";
|
|
3618
|
-
import { homedir as
|
|
3785
|
+
import { homedir as homedir3 } from "node:os";
|
|
3619
3786
|
import path8 from "node:path";
|
|
3620
3787
|
import { Effect as Effect13, Semaphore as Semaphore3 } from "effect";
|
|
3621
3788
|
function resolveProjectsRoot(input) {
|
|
3622
3789
|
return Effect13.tryPromise({
|
|
3623
3790
|
try: async () => {
|
|
3624
3791
|
const trimmed = input.trim();
|
|
3625
|
-
const expanded = trimmed === "~" ?
|
|
3792
|
+
const expanded = trimmed === "~" ? homedir3() : trimmed.startsWith("~/") ? path8.join(homedir3(), trimmed.slice(2)) : trimmed;
|
|
3626
3793
|
if (!path8.isAbsolute(expanded))
|
|
3627
3794
|
throw new Error("Use an absolute server path or a path beginning with ~/.");
|
|
3628
3795
|
const resolved = await realpath(expanded);
|
|
@@ -3645,7 +3812,7 @@ function projectLock(store2) {
|
|
|
3645
3812
|
}
|
|
3646
3813
|
return lock;
|
|
3647
3814
|
}
|
|
3648
|
-
var ProjectId =
|
|
3815
|
+
var ProjectId = z17.string().trim().min(1).max(255);
|
|
3649
3816
|
async function gitDirectory(directory) {
|
|
3650
3817
|
const location = path8.join(directory, ".git");
|
|
3651
3818
|
const info = await stat3(location);
|
|
@@ -3662,10 +3829,10 @@ async function identity(directory, preferred) {
|
|
|
3662
3829
|
try {
|
|
3663
3830
|
return ProjectId.parse(await readFile6(filename, "utf8"));
|
|
3664
3831
|
} catch (error) {
|
|
3665
|
-
if (!
|
|
3832
|
+
if (!z17.object({ code: z17.literal("ENOENT") }).safeParse(error).success) throw error;
|
|
3666
3833
|
const legacyFilename = path8.join(await gitDirectory(directory), "chatplans-project-id");
|
|
3667
3834
|
const legacy = await readFile6(legacyFilename, "utf8").catch((cause) => {
|
|
3668
|
-
if (!
|
|
3835
|
+
if (!z17.object({ code: z17.literal("ENOENT") }).safeParse(cause).success) throw cause;
|
|
3669
3836
|
return null;
|
|
3670
3837
|
});
|
|
3671
3838
|
const id = legacy === null ? preferred ?? randomUUID6() : ProjectId.parse(legacy);
|
|
@@ -3673,7 +3840,7 @@ async function identity(directory, preferred) {
|
|
|
3673
3840
|
await writeFile4(filename, id + "\n", { flag: "wx" });
|
|
3674
3841
|
return id;
|
|
3675
3842
|
} catch (writeError) {
|
|
3676
|
-
if (!
|
|
3843
|
+
if (!z17.object({ code: z17.literal("EEXIST") }).safeParse(writeError).success) throw writeError;
|
|
3677
3844
|
return ProjectId.parse(await readFile6(filename, "utf8"));
|
|
3678
3845
|
}
|
|
3679
3846
|
}
|
|
@@ -3747,9 +3914,9 @@ function reconnectProject(root, id, input, store2) {
|
|
|
3747
3914
|
}
|
|
3748
3915
|
|
|
3749
3916
|
// src/project-git.ts
|
|
3750
|
-
import { execFile as
|
|
3751
|
-
import { promisify as
|
|
3752
|
-
var
|
|
3917
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
3918
|
+
import { promisify as promisify3 } from "node:util";
|
|
3919
|
+
var execute3 = promisify3(execFile3);
|
|
3753
3920
|
function repositoryIdentity(remote) {
|
|
3754
3921
|
const input = remote.trim();
|
|
3755
3922
|
if (!input || input.startsWith("/") || input.startsWith(".")) return null;
|
|
@@ -3770,7 +3937,7 @@ function repositoryIdentity(remote) {
|
|
|
3770
3937
|
return { key, display: key };
|
|
3771
3938
|
}
|
|
3772
3939
|
async function git(directory, args) {
|
|
3773
|
-
const { stdout } = await
|
|
3940
|
+
const { stdout } = await execute3("git", ["-C", directory, ...args], {
|
|
3774
3941
|
timeout: 3e3,
|
|
3775
3942
|
maxBuffer: 1024 * 1024,
|
|
3776
3943
|
env: { ...workflowEnvironment(), GIT_OPTIONAL_LOCKS: "0", GIT_TERMINAL_PROMPT: "0" }
|
|
@@ -3796,7 +3963,7 @@ async function inspect(directory) {
|
|
|
3796
3963
|
git(directory, ["symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => null),
|
|
3797
3964
|
// NUL-separated records avoid ambiguous filenames/newlines. --no-renames
|
|
3798
3965
|
// keeps each entry one record, so we only need its two status characters.
|
|
3799
|
-
|
|
3966
|
+
execute3(
|
|
3800
3967
|
"git",
|
|
3801
3968
|
[
|
|
3802
3969
|
"-C",
|
|
@@ -3851,32 +4018,32 @@ import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "n
|
|
|
3851
4018
|
import path9 from "node:path";
|
|
3852
4019
|
|
|
3853
4020
|
// src/push-schema.ts
|
|
3854
|
-
import { z as
|
|
3855
|
-
var PushBindingSchema =
|
|
3856
|
-
bindingId:
|
|
3857
|
-
installationId:
|
|
3858
|
-
deviceId:
|
|
4021
|
+
import { z as z18 } from "zod";
|
|
4022
|
+
var PushBindingSchema = z18.object({
|
|
4023
|
+
bindingId: z18.string().uuid(),
|
|
4024
|
+
installationId: z18.string().uuid(),
|
|
4025
|
+
deviceId: z18.string().uuid()
|
|
3859
4026
|
});
|
|
3860
|
-
var PushStateSchema =
|
|
3861
|
-
bindings:
|
|
3862
|
-
error:
|
|
4027
|
+
var PushStateSchema = z18.object({
|
|
4028
|
+
bindings: z18.array(PushBindingSchema).max(10),
|
|
4029
|
+
error: z18.string().nullable()
|
|
3863
4030
|
});
|
|
3864
4031
|
var PendingPushSchema = PushEventSchema.extend({
|
|
3865
|
-
remaining:
|
|
3866
|
-
createdAt:
|
|
3867
|
-
attempts:
|
|
3868
|
-
nextAt:
|
|
4032
|
+
remaining: z18.array(PushBindingSchema),
|
|
4033
|
+
createdAt: z18.number(),
|
|
4034
|
+
attempts: z18.number().int().nonnegative(),
|
|
4035
|
+
nextAt: z18.number()
|
|
3869
4036
|
});
|
|
3870
4037
|
|
|
3871
4038
|
// src/store.ts
|
|
3872
4039
|
import { DatabaseSync } from "node:sqlite";
|
|
3873
4040
|
import { Context as Context3, Data as Data3, Effect as Effect14, Layer as Layer3 } from "effect";
|
|
3874
|
-
import { z as
|
|
4041
|
+
import { z as z19 } from "zod";
|
|
3875
4042
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
3876
4043
|
var StorageError = class extends Data3.TaggedError("StorageError") {
|
|
3877
4044
|
};
|
|
3878
|
-
var PayloadRow =
|
|
3879
|
-
var EventRow =
|
|
4045
|
+
var PayloadRow = z19.object({ payload: z19.string() });
|
|
4046
|
+
var EventRow = z19.object({ sequence: z19.number(), recorded_at: z19.string(), payload: z19.string() });
|
|
3880
4047
|
var SqliteSessionStore = class {
|
|
3881
4048
|
constructor(db) {
|
|
3882
4049
|
this.db = db;
|
|
@@ -3927,7 +4094,7 @@ var SqliteSessionStore = class {
|
|
|
3927
4094
|
const row = PayloadRow.parse(
|
|
3928
4095
|
this.db.prepare("SELECT payload FROM settings WHERE id = 'server-id'").get()
|
|
3929
4096
|
);
|
|
3930
|
-
return
|
|
4097
|
+
return z19.string().uuid().parse(JSON.parse(row.payload));
|
|
3931
4098
|
});
|
|
3932
4099
|
}
|
|
3933
4100
|
saveDevice(device, tokenHash) {
|
|
@@ -4015,7 +4182,7 @@ var SqliteSessionStore = class {
|
|
|
4015
4182
|
`).all();
|
|
4016
4183
|
const insert = this.db.prepare("INSERT OR IGNORE INTO projects(id, payload) VALUES (?, ?)");
|
|
4017
4184
|
for (const row of legacy) {
|
|
4018
|
-
const id =
|
|
4185
|
+
const id = z19.object({ id: z19.string() }).parse(row).id;
|
|
4019
4186
|
insert.run(
|
|
4020
4187
|
id,
|
|
4021
4188
|
JSON.stringify({ id, name: id, path: path9.join(root, id), available: false })
|
|
@@ -4225,7 +4392,7 @@ var SqliteSessionStore = class {
|
|
|
4225
4392
|
if (event.type === "message.user") this.claimAttachments(event.attachments ?? []);
|
|
4226
4393
|
const result = this.db.prepare("INSERT INTO events(session_id, recorded_at, payload) VALUES (?, ?, ?)").run(sessionId, recordedAt, JSON.stringify(event));
|
|
4227
4394
|
if (notification) {
|
|
4228
|
-
const count =
|
|
4395
|
+
const count = z19.object({ total: z19.number() }).parse(this.db.prepare("SELECT COUNT(*) AS total FROM push_outbox").get());
|
|
4229
4396
|
if (count.total >= 1e3)
|
|
4230
4397
|
this.db.prepare(
|
|
4231
4398
|
"DELETE FROM push_outbox WHERE rowid IN (SELECT rowid FROM push_outbox ORDER BY rowid LIMIT 1)"
|
|
@@ -4277,7 +4444,7 @@ var SessionStore = class extends Context3.Service()(
|
|
|
4277
4444
|
for (const suffix of ["-wal", "-shm"])
|
|
4278
4445
|
if (existsSync2(filename + suffix)) chmodSync(filename + suffix, 384);
|
|
4279
4446
|
try {
|
|
4280
|
-
const schema =
|
|
4447
|
+
const schema = z19.object({ user_version: z19.number() }).parse(db.prepare("PRAGMA user_version").get());
|
|
4281
4448
|
if (schema.user_version > 1)
|
|
4282
4449
|
throw new Error("This database requires a newer Agentrific server.");
|
|
4283
4450
|
db.exec(`
|
|
@@ -4410,9 +4577,9 @@ var host = process.env.AGENTRIFIC_HOST ?? "127.0.0.1";
|
|
|
4410
4577
|
var port = Number(process.env.AGENTRIFIC_PORT ?? 4318);
|
|
4411
4578
|
var defaultProjectsRoot = path11.resolve(process.env.AGENTRIFIC_PROJECTS_DIR ?? "./projects");
|
|
4412
4579
|
var worktreesRoot = path11.resolve(
|
|
4413
|
-
process.env.AGENTRIFIC_WORKTREES_DIR ?? path11.join(
|
|
4580
|
+
process.env.AGENTRIFIC_WORKTREES_DIR ?? path11.join(homedir4(), ".agentrific", "workspaces")
|
|
4414
4581
|
);
|
|
4415
|
-
var execFileAsync =
|
|
4582
|
+
var execFileAsync = promisify4(execFile4);
|
|
4416
4583
|
if (host !== "127.0.0.1" && host !== "::1")
|
|
4417
4584
|
throw new Error("Bind Agentrific to loopback and expose it with Tailscale Serve or cloudflared.");
|
|
4418
4585
|
var app = Fastify({
|
|
@@ -4532,7 +4699,10 @@ function publish(event, workflowUpdate = false) {
|
|
|
4532
4699
|
else client.send(message);
|
|
4533
4700
|
}
|
|
4534
4701
|
}
|
|
4535
|
-
var
|
|
4702
|
+
var claudeCode = new ClaudeCode();
|
|
4703
|
+
var modelRuntime = await ModelRuntime.create();
|
|
4704
|
+
await claudeCode.refresh(modelRuntime);
|
|
4705
|
+
var models = new ServerModels(modelRuntime, store, claudeCode);
|
|
4536
4706
|
var delivery = new DeliveryCoordinator(store, {
|
|
4537
4707
|
publish: (event) => publish(event, true),
|
|
4538
4708
|
draft: deliveryWriter(models),
|
|
@@ -4726,7 +4896,7 @@ app.get("/api/projects/:id", async (request, reply) => {
|
|
|
4726
4896
|
return project ?? reply.code(404).send({ error: "Project not found on this server." });
|
|
4727
4897
|
});
|
|
4728
4898
|
app.post("/api/projects/:id/reconnect", async (request, reply) => {
|
|
4729
|
-
const parsed =
|
|
4899
|
+
const parsed = z20.object({ path: z20.string().trim().min(1).max(4096) }).safeParse(request.body);
|
|
4730
4900
|
if (!parsed.success)
|
|
4731
4901
|
return reply.code(400).send({ error: "Enter the project's new folder path." });
|
|
4732
4902
|
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
@@ -4812,7 +4982,7 @@ app.patch("/api/tasks/:id", async (request, reply) => {
|
|
|
4812
4982
|
}
|
|
4813
4983
|
});
|
|
4814
4984
|
app.post("/api/tasks/:id/discard-unrun", async (request, reply) => {
|
|
4815
|
-
const parsed =
|
|
4985
|
+
const parsed = z20.object({ updatedAt: z20.string().datetime() }).safeParse(request.body);
|
|
4816
4986
|
if (!parsed.success) return reply.code(400).send({ error: "The task revision is required." });
|
|
4817
4987
|
try {
|
|
4818
4988
|
await deleteTask(store, request.params.id, void 0, parsed.data.updatedAt);
|
|
@@ -4882,9 +5052,9 @@ app.post(
|
|
|
4882
5052
|
}
|
|
4883
5053
|
})
|
|
4884
5054
|
);
|
|
4885
|
-
var HistoryQuery =
|
|
4886
|
-
after:
|
|
4887
|
-
limit:
|
|
5055
|
+
var HistoryQuery = z20.object({
|
|
5056
|
+
after: z20.coerce.number().int().nonnegative().default(0),
|
|
5057
|
+
limit: z20.coerce.number().int().min(1).max(1e3).default(500)
|
|
4888
5058
|
});
|
|
4889
5059
|
app.get("/api/sessions/:id/events", async (request, reply) => {
|
|
4890
5060
|
const query = HistoryQuery.safeParse(request.query);
|
|
@@ -5022,7 +5192,7 @@ app.post("/api/projects/:id/chat", async (request, reply) => {
|
|
|
5022
5192
|
const location = await resolveSessionProject(request.params.id);
|
|
5023
5193
|
if (location.error !== void 0)
|
|
5024
5194
|
return reply.code(location.status).send({ error: location.error });
|
|
5025
|
-
const parsed =
|
|
5195
|
+
const parsed = z20.object({ reset: z20.boolean().optional() }).safeParse(request.body ?? {});
|
|
5026
5196
|
if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
|
|
5027
5197
|
try {
|
|
5028
5198
|
return await openRepoChat(
|
|
@@ -5046,7 +5216,7 @@ app.post("/api/sessions", async (request, reply) => {
|
|
|
5046
5216
|
if (!result.session) return reply.code(result.status).send({ error: result.error });
|
|
5047
5217
|
return reply.code(201).send(result.session);
|
|
5048
5218
|
});
|
|
5049
|
-
var StartTaskBody =
|
|
5219
|
+
var StartTaskBody = z20.object({ prompt: z20.string().trim().max(2e4).optional() }).default({});
|
|
5050
5220
|
function startTask(id, instructionsOverride, automated = false) {
|
|
5051
5221
|
return withTaskLock(
|
|
5052
5222
|
"agent-dispatch",
|
|
@@ -5124,7 +5294,7 @@ app.patch("/api/automation", async (request, reply) => {
|
|
|
5124
5294
|
return automation.configure(parsed.data);
|
|
5125
5295
|
});
|
|
5126
5296
|
app.patch("/api/automation/queue", async (request, reply) => {
|
|
5127
|
-
const parsed =
|
|
5297
|
+
const parsed = z20.object({ taskId: z20.string().min(1), beforeId: z20.string().min(1).nullable() }).safeParse(request.body);
|
|
5128
5298
|
if (!parsed.success)
|
|
5129
5299
|
return reply.code(400).send({ error: "Choose a task and a queue position." });
|
|
5130
5300
|
try {
|
|
@@ -5187,7 +5357,7 @@ attachEventServer(app, security, allowedOrigins, () => ({
|
|
|
5187
5357
|
sessions: Effect17.runSync(store.list())
|
|
5188
5358
|
}));
|
|
5189
5359
|
function localServerUrl() {
|
|
5190
|
-
const address =
|
|
5360
|
+
const address = z20.object({ port: z20.number() }).safeParse(app.server.address());
|
|
5191
5361
|
return address.success ? `http://${host}:${address.data.port}` : void 0;
|
|
5192
5362
|
}
|
|
5193
5363
|
var phoneOrigin;
|
|
@@ -5215,6 +5385,7 @@ var closeControl = await startControl(
|
|
|
5215
5385
|
serverId: security.serverId,
|
|
5216
5386
|
publicUrl: process.env.AGENTRIFIC_PUBLIC_URL,
|
|
5217
5387
|
phoneProvider: phone?.snapshot().provider,
|
|
5388
|
+
phoneProgress: phone?.snapshot().progress,
|
|
5218
5389
|
localUrl: localServerUrl(),
|
|
5219
5390
|
serverVersion,
|
|
5220
5391
|
connectedDevices: new Set(security.sockets.values()).size,
|