@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/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ var init_package = __esm({
|
|
|
30
30
|
"package.json"() {
|
|
31
31
|
package_default = {
|
|
32
32
|
name: "@agentrific/server",
|
|
33
|
-
version: "0.
|
|
33
|
+
version: "0.7.0",
|
|
34
34
|
description: "Self-hosted control plane for long-running coding agents",
|
|
35
35
|
keywords: [
|
|
36
36
|
"agentrific",
|
|
@@ -56,20 +56,28 @@ var init_package = __esm({
|
|
|
56
56
|
registry: "https://registry.npmjs.org"
|
|
57
57
|
},
|
|
58
58
|
scripts: {
|
|
59
|
-
dev: "tsx watch src/index.ts",
|
|
59
|
+
dev: "node scripts/build-claude-bridge.mjs && tsx watch src/index.ts",
|
|
60
60
|
start: "node dist/index.js",
|
|
61
61
|
typecheck: "tsc --noEmit",
|
|
62
62
|
build: "node scripts/build.mjs",
|
|
63
|
-
cli: "AGENTRIFIC_DATA_DIR=./data tsx src/cli.ts"
|
|
63
|
+
cli: "node scripts/build-claude-bridge.mjs && AGENTRIFIC_DATA_DIR=./data tsx src/cli.ts",
|
|
64
|
+
"build:claude": "node scripts/build-claude-bridge.mjs"
|
|
64
65
|
},
|
|
65
66
|
dependencies: {
|
|
67
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.296",
|
|
68
|
+
"@anthropic-ai/sdk": "^0.124.0",
|
|
69
|
+
"@earendil-works/pi-ai": "1.0.0",
|
|
66
70
|
"@earendil-works/pi-coding-agent": "1.0.0",
|
|
67
71
|
"@fastify/cors": "^11.3.0",
|
|
68
72
|
"@fastify/multipart": "^10.1.2",
|
|
69
73
|
"@inquirer/select": "^5.2.5",
|
|
74
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
75
|
+
"cc-session-io": "^0.4.0",
|
|
76
|
+
"change-case": "^5.4.4",
|
|
70
77
|
diff: "^9.0.0",
|
|
71
78
|
effect: "4.0.0-rc.117",
|
|
72
79
|
fastify: "^5.6.2",
|
|
80
|
+
"pi-claude-agent-sdk": "0.9.2",
|
|
73
81
|
"pino-pretty": "^13.1.3",
|
|
74
82
|
"qrcode-terminal": "^0.12.0",
|
|
75
83
|
semver: "^7.8.5",
|
|
@@ -786,6 +794,7 @@ var init_control = __esm({
|
|
|
786
794
|
serverId: z3.string().uuid(),
|
|
787
795
|
publicUrl: z3.string().optional(),
|
|
788
796
|
phoneProvider: ProviderSchema.nullable().optional(),
|
|
797
|
+
phoneProgress: z3.string().nullable().optional(),
|
|
789
798
|
localUrl: z3.string().optional(),
|
|
790
799
|
serverVersion: z3.string().optional(),
|
|
791
800
|
connectedDevices: z3.number().optional(),
|
|
@@ -795,7 +804,11 @@ var init_control = __esm({
|
|
|
795
804
|
maintenance: MaintenanceSchema.optional()
|
|
796
805
|
});
|
|
797
806
|
ControlResultSchema = z3.union([
|
|
798
|
-
z3.object({
|
|
807
|
+
z3.object({
|
|
808
|
+
provider: ProviderSchema.nullable(),
|
|
809
|
+
publicUrl: z3.string().url().nullable(),
|
|
810
|
+
progress: z3.string().nullable().optional()
|
|
811
|
+
}),
|
|
799
812
|
z3.array(z3.object({ provider: ProviderSchema, available: z3.boolean(), reason: z3.string() })),
|
|
800
813
|
z3.object({
|
|
801
814
|
pairingId: z3.string(),
|
|
@@ -1330,6 +1343,10 @@ var init_opentunnel = __esm({
|
|
|
1330
1343
|
client;
|
|
1331
1344
|
connection;
|
|
1332
1345
|
stopping = false;
|
|
1346
|
+
stage = "reading-identity";
|
|
1347
|
+
progress() {
|
|
1348
|
+
return this.connection?.status().state ?? this.stage;
|
|
1349
|
+
}
|
|
1333
1350
|
running() {
|
|
1334
1351
|
return this.connection?.status().state === "connected" && !this.stopping;
|
|
1335
1352
|
}
|
|
@@ -1359,13 +1376,32 @@ var init_opentunnel = __esm({
|
|
|
1359
1376
|
let canceled = () => {
|
|
1360
1377
|
};
|
|
1361
1378
|
try {
|
|
1362
|
-
const
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1379
|
+
const client = this.client;
|
|
1380
|
+
this.stage = "reading-identity";
|
|
1381
|
+
const connecting = (async () => {
|
|
1382
|
+
const options = {
|
|
1383
|
+
profile: "agentrific",
|
|
1384
|
+
onProgress: (stage) => {
|
|
1385
|
+
this.stage = stage;
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
const identity2 = await client.tunnel.get(options);
|
|
1389
|
+
abort.throwIfAborted();
|
|
1390
|
+
if (!identity2) {
|
|
1391
|
+
const resumed = await client.tunnel.resume(options);
|
|
1392
|
+
abort.throwIfAborted();
|
|
1393
|
+
if (!resumed) await client.tunnel.create(options);
|
|
1394
|
+
}
|
|
1395
|
+
abort.throwIfAborted();
|
|
1396
|
+
this.stage = "connecting";
|
|
1397
|
+
return client.tunnel.connect({
|
|
1398
|
+
profile: "agentrific",
|
|
1399
|
+
routes: {
|
|
1400
|
+
[route]: `${endpoint.hostname === "[::1]" ? "[::1]" : "127.0.0.1"}:${endpoint.port || "80"}`
|
|
1401
|
+
},
|
|
1402
|
+
signal
|
|
1403
|
+
});
|
|
1404
|
+
})();
|
|
1369
1405
|
const cancellation = new Promise((_resolve, reject) => {
|
|
1370
1406
|
canceled = () => reject(new Error("OpenTunnel connection canceled or timed out."));
|
|
1371
1407
|
abort.addEventListener("abort", canceled, { once: true });
|
|
@@ -2181,7 +2217,11 @@ var init_desktop_phone = __esm({
|
|
|
2181
2217
|
publicUrl = null;
|
|
2182
2218
|
pending = Promise.resolve();
|
|
2183
2219
|
snapshot() {
|
|
2184
|
-
return {
|
|
2220
|
+
return {
|
|
2221
|
+
provider: this.provider,
|
|
2222
|
+
publicUrl: this.publicUrl,
|
|
2223
|
+
progress: this.tunnel?.progress?.() ?? null
|
|
2224
|
+
};
|
|
2185
2225
|
}
|
|
2186
2226
|
async options() {
|
|
2187
2227
|
return (await this.detect()).map(({ provider, available, reason }) => ({
|
|
@@ -3525,14 +3565,17 @@ import { z as z13 } from "zod";
|
|
|
3525
3565
|
function deliveryWriter(models2, createSession = createAgentSession) {
|
|
3526
3566
|
return async (run, input, diff, signal) => {
|
|
3527
3567
|
signal?.throwIfAborted();
|
|
3568
|
+
const selected = run.model ?? await models2.defaultReference();
|
|
3569
|
+
const sessionOptions = await models2.sessionOptions(run.workspacePath);
|
|
3528
3570
|
const { session } = await createSession({
|
|
3529
3571
|
cwd: run.workspacePath,
|
|
3530
|
-
model: await models2.resolve(
|
|
3531
|
-
|
|
3572
|
+
model: await models2.resolve(selected),
|
|
3573
|
+
...sessionOptions,
|
|
3532
3574
|
sessionManager: SessionManager.inMemory(),
|
|
3533
3575
|
tools: [],
|
|
3534
3576
|
noTools: "all"
|
|
3535
3577
|
});
|
|
3578
|
+
if (sessionOptions.resourceLoader) await session.bindExtensions({});
|
|
3536
3579
|
const timer = setTimeout(() => {
|
|
3537
3580
|
void session.abort();
|
|
3538
3581
|
}, 6e4);
|
|
@@ -3566,12 +3609,116 @@ var init_delivery_writer = __esm({
|
|
|
3566
3609
|
}
|
|
3567
3610
|
});
|
|
3568
3611
|
|
|
3612
|
+
// src/claude-code.ts
|
|
3613
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
3614
|
+
import { homedir as homedir3 } from "node:os";
|
|
3615
|
+
import { join as join2 } from "node:path";
|
|
3616
|
+
import { promisify as promisify3 } from "node:util";
|
|
3617
|
+
import { z as z14 } from "zod";
|
|
3618
|
+
import {
|
|
3619
|
+
createAgentSessionServices
|
|
3620
|
+
} from "@earendil-works/pi-coding-agent";
|
|
3621
|
+
async function discoverClaudeCode() {
|
|
3622
|
+
if (process.env.AGENTRIFIC_CLAUDE_CODE === "off") return;
|
|
3623
|
+
const candidates = [
|
|
3624
|
+
"claude",
|
|
3625
|
+
join2(homedir3(), ".local/bin/claude"),
|
|
3626
|
+
join2(homedir3(), ".claude/local/claude")
|
|
3627
|
+
];
|
|
3628
|
+
for (const executable of candidates) {
|
|
3629
|
+
try {
|
|
3630
|
+
await execute3(executable, ["--version"], { timeout: 5e3, maxBuffer: 64 * 1024 });
|
|
3631
|
+
} catch {
|
|
3632
|
+
continue;
|
|
3633
|
+
}
|
|
3634
|
+
try {
|
|
3635
|
+
const { stdout } = await execute3(executable, ["auth", "status", "--json"], {
|
|
3636
|
+
timeout: 5e3,
|
|
3637
|
+
maxBuffer: 64 * 1024
|
|
3638
|
+
});
|
|
3639
|
+
const status = AuthStatusSchema.parse(JSON.parse(stdout));
|
|
3640
|
+
return { executable, ready: status.loggedIn };
|
|
3641
|
+
} catch {
|
|
3642
|
+
return { executable, ready: false };
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
var claudePlatform, execute3, AuthStatusSchema, ClaudeCode;
|
|
3647
|
+
var init_claude_code = __esm({
|
|
3648
|
+
"src/claude-code.ts"() {
|
|
3649
|
+
"use strict";
|
|
3650
|
+
claudePlatform = "claude-bridge";
|
|
3651
|
+
execute3 = promisify3(execFile3);
|
|
3652
|
+
AuthStatusSchema = z14.object({ loggedIn: z14.boolean() });
|
|
3653
|
+
ClaudeCode = class {
|
|
3654
|
+
constructor(probe = discoverClaudeCode, query) {
|
|
3655
|
+
this.probe = probe;
|
|
3656
|
+
this.query = query;
|
|
3657
|
+
}
|
|
3658
|
+
probe;
|
|
3659
|
+
query;
|
|
3660
|
+
installation;
|
|
3661
|
+
registered = false;
|
|
3662
|
+
get ready() {
|
|
3663
|
+
return this.installation?.ready === true;
|
|
3664
|
+
}
|
|
3665
|
+
get warning() {
|
|
3666
|
+
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;
|
|
3667
|
+
}
|
|
3668
|
+
async refresh(runtime2) {
|
|
3669
|
+
this.installation = await this.probe();
|
|
3670
|
+
if (this.installation && !this.registered) {
|
|
3671
|
+
await this.services(process.cwd(), runtime2);
|
|
3672
|
+
this.registered = true;
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
async services(cwd, modelRuntime2) {
|
|
3676
|
+
const module = await import(new URL("./claude-bridge.js", import.meta.url).href);
|
|
3677
|
+
const extension = module.createClaudeExtension(this.query);
|
|
3678
|
+
const services = await createAgentSessionServices({
|
|
3679
|
+
cwd,
|
|
3680
|
+
modelRuntime: modelRuntime2,
|
|
3681
|
+
resourceLoaderOptions: {
|
|
3682
|
+
// Match Pipeline: do not execute unrelated global Pi extensions.
|
|
3683
|
+
noExtensions: true,
|
|
3684
|
+
extensionFactories: [
|
|
3685
|
+
{
|
|
3686
|
+
name: "agentrific-claude-code",
|
|
3687
|
+
factory: (pi) => extension({
|
|
3688
|
+
...pi,
|
|
3689
|
+
// SAFETY: The pinned bridge uses only the name/config registration overload.
|
|
3690
|
+
registerProvider: ((id, config) => pi.registerProvider(id, {
|
|
3691
|
+
...config,
|
|
3692
|
+
// Bind the working directory to this provider,
|
|
3693
|
+
// without changing the process cwd or global Claude configuration.
|
|
3694
|
+
streamSimple: config.streamSimple && ((model, context, options) => {
|
|
3695
|
+
const request = { ...options, cwd };
|
|
3696
|
+
return config.streamSimple(model, context, request);
|
|
3697
|
+
})
|
|
3698
|
+
}))
|
|
3699
|
+
})
|
|
3700
|
+
}
|
|
3701
|
+
]
|
|
3702
|
+
}
|
|
3703
|
+
});
|
|
3704
|
+
const errors = [
|
|
3705
|
+
...services.diagnostics.filter((item) => item.type === "error").map((item) => item.message),
|
|
3706
|
+
...services.resourceLoader.getExtensions().errors.map((item) => item.error)
|
|
3707
|
+
];
|
|
3708
|
+
if (errors.length) throw new Error(errors.join("\n"));
|
|
3709
|
+
return services;
|
|
3710
|
+
}
|
|
3711
|
+
};
|
|
3712
|
+
}
|
|
3713
|
+
});
|
|
3714
|
+
|
|
3569
3715
|
// src/models.ts
|
|
3570
3716
|
import { Effect as Effect11 } from "effect";
|
|
3571
3717
|
var legacyModel, makers, ServerModels;
|
|
3572
3718
|
var init_models = __esm({
|
|
3573
3719
|
"src/models.ts"() {
|
|
3574
3720
|
"use strict";
|
|
3721
|
+
init_claude_code();
|
|
3575
3722
|
legacyModel = { platformId: "openai-codex", modelId: "gpt-6-luna" };
|
|
3576
3723
|
makers = [
|
|
3577
3724
|
{ id: "openai", name: "OpenAI", pattern: /(?:^|[/.:\s-])(?:gpt-|o[134](?:-|$))/i },
|
|
@@ -3588,12 +3735,14 @@ var init_models = __esm({
|
|
|
3588
3735
|
{ id: "amazon", name: "Amazon", pattern: /amazon\.nova|nova-/i }
|
|
3589
3736
|
];
|
|
3590
3737
|
ServerModels = class {
|
|
3591
|
-
constructor(runtime2, store2) {
|
|
3738
|
+
constructor(runtime2, store2, claude) {
|
|
3592
3739
|
this.runtime = runtime2;
|
|
3593
3740
|
this.store = store2;
|
|
3741
|
+
this.claude = claude;
|
|
3594
3742
|
}
|
|
3595
3743
|
runtime;
|
|
3596
3744
|
store;
|
|
3745
|
+
claude;
|
|
3597
3746
|
refreshing;
|
|
3598
3747
|
refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3599
3748
|
warning = null;
|
|
@@ -3615,18 +3764,20 @@ var init_models = __esm({
|
|
|
3615
3764
|
platformId,
|
|
3616
3765
|
modelId,
|
|
3617
3766
|
name: model?.name ?? modelId,
|
|
3618
|
-
platformName: this.runtime.getProvider(platformId)?.name ?? platformId,
|
|
3767
|
+
platformName: platformId === claudePlatform ? "Claude Code" : this.runtime.getProvider(platformId)?.name ?? platformId,
|
|
3619
3768
|
providerId: namespace === "openrouter" ? "unspecified" : maker?.id ?? namespace ?? "unspecified",
|
|
3620
3769
|
providerName: namespace === "openrouter" ? "Other / custom" : maker?.name ?? publisherName ?? "Other / custom"
|
|
3621
3770
|
};
|
|
3622
3771
|
}
|
|
3623
3772
|
async refresh(network) {
|
|
3624
3773
|
if (!this.refreshing) {
|
|
3625
|
-
this.refreshing = this.runtime.
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3774
|
+
this.refreshing = Promise.resolve(this.claude?.refresh(this.runtime)).then(
|
|
3775
|
+
() => this.runtime.refresh({
|
|
3776
|
+
allowNetwork: network,
|
|
3777
|
+
force: network,
|
|
3778
|
+
signal: AbortSignal.timeout(15e3)
|
|
3779
|
+
})
|
|
3780
|
+
).then((result) => {
|
|
3630
3781
|
this.refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3631
3782
|
this.warning = result.aborted || result.errors.size || this.runtime.getError() ? "Some providers could not refresh. Showing the catalogue currently available on this server." : null;
|
|
3632
3783
|
}).finally(() => {
|
|
@@ -3636,7 +3787,10 @@ var init_models = __esm({
|
|
|
3636
3787
|
await this.refreshing;
|
|
3637
3788
|
}
|
|
3638
3789
|
async defaultReference() {
|
|
3639
|
-
|
|
3790
|
+
const saved = await Effect11.runPromise(this.store.defaultModel());
|
|
3791
|
+
if (saved) return saved;
|
|
3792
|
+
const preferred = this.claude?.ready ? this.runtime.getModels().find((model) => model.provider === claudePlatform && /sonnet/i.test(model.id)) : void 0;
|
|
3793
|
+
return preferred ? { platformId: preferred.provider, modelId: preferred.id } : legacyModel;
|
|
3640
3794
|
}
|
|
3641
3795
|
async defaultIdentity() {
|
|
3642
3796
|
const selected = await this.defaultReference();
|
|
@@ -3649,17 +3803,24 @@ var init_models = __esm({
|
|
|
3649
3803
|
return {
|
|
3650
3804
|
models: this.runtime.getModels().map((model) => ({
|
|
3651
3805
|
...this.identity(model.provider, model.id),
|
|
3652
|
-
available: available.has(`${model.provider}\0${model.id}`),
|
|
3806
|
+
available: available.has(`${model.provider}\0${model.id}`) && (model.provider !== claudePlatform || this.claude?.ready === true),
|
|
3653
3807
|
reasoning: model.reasoning,
|
|
3654
3808
|
input: model.input,
|
|
3655
3809
|
contextWindow: model.contextWindow
|
|
3656
3810
|
})),
|
|
3657
3811
|
defaultModel: await this.defaultIdentity(),
|
|
3658
3812
|
refreshedAt: this.refreshedAt,
|
|
3659
|
-
warning: this.warning
|
|
3813
|
+
warning: this.claude?.warning ?? this.warning
|
|
3660
3814
|
};
|
|
3661
3815
|
}
|
|
3662
3816
|
async resolve(reference) {
|
|
3817
|
+
if (reference.platformId === claudePlatform && this.claude) {
|
|
3818
|
+
await this.claude.refresh(this.runtime);
|
|
3819
|
+
if (!this.claude.ready)
|
|
3820
|
+
throw new Error(
|
|
3821
|
+
this.claude.warning ?? "Claude Code is unavailable on this server. Install it and run claude auth login as the server user."
|
|
3822
|
+
);
|
|
3823
|
+
}
|
|
3663
3824
|
const available = await this.runtime.getAvailable(reference.platformId, {
|
|
3664
3825
|
signal: AbortSignal.timeout(1e4)
|
|
3665
3826
|
});
|
|
@@ -3670,6 +3831,17 @@ var init_models = __esm({
|
|
|
3670
3831
|
);
|
|
3671
3832
|
return model;
|
|
3672
3833
|
}
|
|
3834
|
+
async sessionOptions(cwd = process.cwd()) {
|
|
3835
|
+
if (this.claude && this.runtime.getProvider(claudePlatform)) {
|
|
3836
|
+
const services = await this.claude.services(cwd);
|
|
3837
|
+
return {
|
|
3838
|
+
modelRuntime: services.modelRuntime,
|
|
3839
|
+
resourceLoader: services.resourceLoader,
|
|
3840
|
+
settingsManager: services.settingsManager
|
|
3841
|
+
};
|
|
3842
|
+
}
|
|
3843
|
+
return { modelRuntime: this.runtime };
|
|
3844
|
+
}
|
|
3673
3845
|
async setDefault(reference) {
|
|
3674
3846
|
await this.resolve(reference);
|
|
3675
3847
|
await Effect11.runPromise(this.store.setDefaultModel(reference));
|
|
@@ -3720,7 +3892,7 @@ var init_delivery_tool = __esm({
|
|
|
3720
3892
|
import { createWriteToolDefinition, defineTool as defineTool2 } from "@earendil-works/pi-coding-agent";
|
|
3721
3893
|
import { createTwoFilesPatch } from "diff";
|
|
3722
3894
|
import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
|
|
3723
|
-
import { z as
|
|
3895
|
+
import { z as z15 } from "zod";
|
|
3724
3896
|
function boundFileChange(change) {
|
|
3725
3897
|
if ((change.patch?.length ?? 0) + (change.content?.length ?? 0) > previewLimit)
|
|
3726
3898
|
return { path: change.path, omitted: true };
|
|
@@ -3778,8 +3950,8 @@ var init_file_change = __esm({
|
|
|
3778
3950
|
"use strict";
|
|
3779
3951
|
init_src();
|
|
3780
3952
|
previewLimit = 256e3;
|
|
3781
|
-
EditResultSchema =
|
|
3782
|
-
details:
|
|
3953
|
+
EditResultSchema = z15.object({
|
|
3954
|
+
details: z15.object({ patch: z15.string().optional(), fileChange: FileChangeSchema.optional() }).optional()
|
|
3783
3955
|
});
|
|
3784
3956
|
}
|
|
3785
3957
|
});
|
|
@@ -3791,7 +3963,7 @@ import {
|
|
|
3791
3963
|
createAgentSession as createAgentSession2,
|
|
3792
3964
|
SessionManager as SessionManager2
|
|
3793
3965
|
} from "@earendil-works/pi-coding-agent";
|
|
3794
|
-
import { z as
|
|
3966
|
+
import { z as z16 } from "zod";
|
|
3795
3967
|
function toolOutput(value) {
|
|
3796
3968
|
const parsed = ToolResultSchema.safeParse(value);
|
|
3797
3969
|
const text = parsed.success ? parsed.data.content?.filter((part) => part.type === "text").map((part) => part.text || "").join("\n") || "Completed without text output." : boundedJson(value, 4e3);
|
|
@@ -3801,16 +3973,18 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3801
3973
|
return {
|
|
3802
3974
|
async open(sessionId, cwd, sessionDirectory, selected = legacyModel, run) {
|
|
3803
3975
|
const model = await models2.resolve(selected);
|
|
3976
|
+
const sessionOptions = await models2.sessionOptions(cwd);
|
|
3804
3977
|
const { session } = await createSession({
|
|
3805
3978
|
cwd,
|
|
3806
3979
|
model,
|
|
3807
|
-
|
|
3980
|
+
...sessionOptions,
|
|
3808
3981
|
sessionManager: SessionManager2.continueRecent(cwd, sessionDirectory),
|
|
3809
3982
|
customTools: [
|
|
3810
3983
|
fileWriteTool(cwd),
|
|
3811
3984
|
...run?.gitFlow && delivery2 ? [deliveryTool(sessionId, delivery2)] : []
|
|
3812
3985
|
]
|
|
3813
3986
|
});
|
|
3987
|
+
if (sessionOptions.resourceLoader) await session.bindExtensions({});
|
|
3814
3988
|
return {
|
|
3815
3989
|
get model() {
|
|
3816
3990
|
const current = session.model;
|
|
@@ -3905,7 +4079,7 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3905
4079
|
return false;
|
|
3906
4080
|
}
|
|
3907
4081
|
const filePaths = /* @__PURE__ */ new Map();
|
|
3908
|
-
const FileArgsSchema =
|
|
4082
|
+
const FileArgsSchema = z16.object({ path: z16.string() });
|
|
3909
4083
|
function handleTool(event) {
|
|
3910
4084
|
if (event.type === "tool_execution_start") {
|
|
3911
4085
|
flushText();
|
|
@@ -3970,8 +4144,8 @@ var init_pi = __esm({
|
|
|
3970
4144
|
init_models();
|
|
3971
4145
|
init_delivery_tool();
|
|
3972
4146
|
init_file_change();
|
|
3973
|
-
ToolResultSchema =
|
|
3974
|
-
content:
|
|
4147
|
+
ToolResultSchema = z16.object({
|
|
4148
|
+
content: z16.array(z16.object({ type: z16.string(), text: z16.string().optional() })).optional()
|
|
3975
4149
|
}).passthrough();
|
|
3976
4150
|
boundedJson = (value, limit) => {
|
|
3977
4151
|
let text;
|
|
@@ -3988,16 +4162,16 @@ var init_pi = __esm({
|
|
|
3988
4162
|
// src/projects.ts
|
|
3989
4163
|
import { access as access2, readdir as readdir2, realpath, stat as stat3, readFile as readFile6, writeFile as writeFile5 } from "node:fs/promises";
|
|
3990
4164
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
3991
|
-
import { z as
|
|
4165
|
+
import { z as z17 } from "zod";
|
|
3992
4166
|
import { constants as constants2 } from "node:fs";
|
|
3993
|
-
import { homedir as
|
|
4167
|
+
import { homedir as homedir4 } from "node:os";
|
|
3994
4168
|
import path10 from "node:path";
|
|
3995
4169
|
import { Effect as Effect13, Semaphore as Semaphore3 } from "effect";
|
|
3996
4170
|
function resolveProjectsRoot(input) {
|
|
3997
4171
|
return Effect13.tryPromise({
|
|
3998
4172
|
try: async () => {
|
|
3999
4173
|
const trimmed = input.trim();
|
|
4000
|
-
const expanded = trimmed === "~" ?
|
|
4174
|
+
const expanded = trimmed === "~" ? homedir4() : trimmed.startsWith("~/") ? path10.join(homedir4(), trimmed.slice(2)) : trimmed;
|
|
4001
4175
|
if (!path10.isAbsolute(expanded))
|
|
4002
4176
|
throw new Error("Use an absolute server path or a path beginning with ~/.");
|
|
4003
4177
|
const resolved = await realpath(expanded);
|
|
@@ -4035,10 +4209,10 @@ async function identity(directory, preferred) {
|
|
|
4035
4209
|
try {
|
|
4036
4210
|
return ProjectId.parse(await readFile6(filename, "utf8"));
|
|
4037
4211
|
} catch (error) {
|
|
4038
|
-
if (!
|
|
4212
|
+
if (!z17.object({ code: z17.literal("ENOENT") }).safeParse(error).success) throw error;
|
|
4039
4213
|
const legacyFilename = path10.join(await gitDirectory(directory), "chatplans-project-id");
|
|
4040
4214
|
const legacy = await readFile6(legacyFilename, "utf8").catch((cause) => {
|
|
4041
|
-
if (!
|
|
4215
|
+
if (!z17.object({ code: z17.literal("ENOENT") }).safeParse(cause).success) throw cause;
|
|
4042
4216
|
return null;
|
|
4043
4217
|
});
|
|
4044
4218
|
const id = legacy === null ? preferred ?? randomUUID6() : ProjectId.parse(legacy);
|
|
@@ -4046,7 +4220,7 @@ async function identity(directory, preferred) {
|
|
|
4046
4220
|
await writeFile5(filename, id + "\n", { flag: "wx" });
|
|
4047
4221
|
return id;
|
|
4048
4222
|
} catch (writeError) {
|
|
4049
|
-
if (!
|
|
4223
|
+
if (!z17.object({ code: z17.literal("EEXIST") }).safeParse(writeError).success) throw writeError;
|
|
4050
4224
|
return ProjectId.parse(await readFile6(filename, "utf8"));
|
|
4051
4225
|
}
|
|
4052
4226
|
}
|
|
@@ -4123,13 +4297,13 @@ var init_projects = __esm({
|
|
|
4123
4297
|
"src/projects.ts"() {
|
|
4124
4298
|
"use strict";
|
|
4125
4299
|
locks2 = /* @__PURE__ */ new WeakMap();
|
|
4126
|
-
ProjectId =
|
|
4300
|
+
ProjectId = z17.string().trim().min(1).max(255);
|
|
4127
4301
|
}
|
|
4128
4302
|
});
|
|
4129
4303
|
|
|
4130
4304
|
// src/project-git.ts
|
|
4131
|
-
import { execFile as
|
|
4132
|
-
import { promisify as
|
|
4305
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
4306
|
+
import { promisify as promisify4 } from "node:util";
|
|
4133
4307
|
function repositoryIdentity(remote) {
|
|
4134
4308
|
const input = remote.trim();
|
|
4135
4309
|
if (!input || input.startsWith("/") || input.startsWith(".")) return null;
|
|
@@ -4150,7 +4324,7 @@ function repositoryIdentity(remote) {
|
|
|
4150
4324
|
return { key, display: key };
|
|
4151
4325
|
}
|
|
4152
4326
|
async function git(directory, args) {
|
|
4153
|
-
const { stdout } = await
|
|
4327
|
+
const { stdout } = await execute4("git", ["-C", directory, ...args], {
|
|
4154
4328
|
timeout: 3e3,
|
|
4155
4329
|
maxBuffer: 1024 * 1024,
|
|
4156
4330
|
env: { ...workflowEnvironment(), GIT_OPTIONAL_LOCKS: "0", GIT_TERMINAL_PROMPT: "0" }
|
|
@@ -4176,7 +4350,7 @@ async function inspect(directory) {
|
|
|
4176
4350
|
git(directory, ["symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => null),
|
|
4177
4351
|
// NUL-separated records avoid ambiguous filenames/newlines. --no-renames
|
|
4178
4352
|
// keeps each entry one record, so we only need its two status characters.
|
|
4179
|
-
|
|
4353
|
+
execute4(
|
|
4180
4354
|
"git",
|
|
4181
4355
|
[
|
|
4182
4356
|
"-C",
|
|
@@ -4224,37 +4398,37 @@ function projectGitStatus(directory) {
|
|
|
4224
4398
|
observations.set(directory, { expires: Date.now() + 15e3, value });
|
|
4225
4399
|
return value;
|
|
4226
4400
|
}
|
|
4227
|
-
var
|
|
4401
|
+
var execute4, observations;
|
|
4228
4402
|
var init_project_git = __esm({
|
|
4229
4403
|
"src/project-git.ts"() {
|
|
4230
4404
|
"use strict";
|
|
4231
4405
|
init_workflow_command();
|
|
4232
|
-
|
|
4406
|
+
execute4 = promisify4(execFile4);
|
|
4233
4407
|
observations = /* @__PURE__ */ new Map();
|
|
4234
4408
|
}
|
|
4235
4409
|
});
|
|
4236
4410
|
|
|
4237
4411
|
// src/push-schema.ts
|
|
4238
|
-
import { z as
|
|
4412
|
+
import { z as z18 } from "zod";
|
|
4239
4413
|
var PushBindingSchema, PushStateSchema, PendingPushSchema;
|
|
4240
4414
|
var init_push_schema = __esm({
|
|
4241
4415
|
"src/push-schema.ts"() {
|
|
4242
4416
|
"use strict";
|
|
4243
4417
|
init_push();
|
|
4244
|
-
PushBindingSchema =
|
|
4245
|
-
bindingId:
|
|
4246
|
-
installationId:
|
|
4247
|
-
deviceId:
|
|
4418
|
+
PushBindingSchema = z18.object({
|
|
4419
|
+
bindingId: z18.string().uuid(),
|
|
4420
|
+
installationId: z18.string().uuid(),
|
|
4421
|
+
deviceId: z18.string().uuid()
|
|
4248
4422
|
});
|
|
4249
|
-
PushStateSchema =
|
|
4250
|
-
bindings:
|
|
4251
|
-
error:
|
|
4423
|
+
PushStateSchema = z18.object({
|
|
4424
|
+
bindings: z18.array(PushBindingSchema).max(10),
|
|
4425
|
+
error: z18.string().nullable()
|
|
4252
4426
|
});
|
|
4253
4427
|
PendingPushSchema = PushEventSchema.extend({
|
|
4254
|
-
remaining:
|
|
4255
|
-
createdAt:
|
|
4256
|
-
attempts:
|
|
4257
|
-
nextAt:
|
|
4428
|
+
remaining: z18.array(PushBindingSchema),
|
|
4429
|
+
createdAt: z18.number(),
|
|
4430
|
+
attempts: z18.number().int().nonnegative(),
|
|
4431
|
+
nextAt: z18.number()
|
|
4258
4432
|
});
|
|
4259
4433
|
}
|
|
4260
4434
|
});
|
|
@@ -4264,7 +4438,7 @@ import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "n
|
|
|
4264
4438
|
import path11 from "node:path";
|
|
4265
4439
|
import { DatabaseSync } from "node:sqlite";
|
|
4266
4440
|
import { Context as Context3, Data as Data3, Effect as Effect14, Layer as Layer3 } from "effect";
|
|
4267
|
-
import { z as
|
|
4441
|
+
import { z as z19 } from "zod";
|
|
4268
4442
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
4269
4443
|
var StorageError, PayloadRow, EventRow, SqliteSessionStore, SessionStore;
|
|
4270
4444
|
var init_store = __esm({
|
|
@@ -4277,8 +4451,8 @@ var init_store = __esm({
|
|
|
4277
4451
|
init_src();
|
|
4278
4452
|
StorageError = class extends Data3.TaggedError("StorageError") {
|
|
4279
4453
|
};
|
|
4280
|
-
PayloadRow =
|
|
4281
|
-
EventRow =
|
|
4454
|
+
PayloadRow = z19.object({ payload: z19.string() });
|
|
4455
|
+
EventRow = z19.object({ sequence: z19.number(), recorded_at: z19.string(), payload: z19.string() });
|
|
4282
4456
|
SqliteSessionStore = class {
|
|
4283
4457
|
constructor(db) {
|
|
4284
4458
|
this.db = db;
|
|
@@ -4329,7 +4503,7 @@ var init_store = __esm({
|
|
|
4329
4503
|
const row = PayloadRow.parse(
|
|
4330
4504
|
this.db.prepare("SELECT payload FROM settings WHERE id = 'server-id'").get()
|
|
4331
4505
|
);
|
|
4332
|
-
return
|
|
4506
|
+
return z19.string().uuid().parse(JSON.parse(row.payload));
|
|
4333
4507
|
});
|
|
4334
4508
|
}
|
|
4335
4509
|
saveDevice(device, tokenHash) {
|
|
@@ -4417,7 +4591,7 @@ var init_store = __esm({
|
|
|
4417
4591
|
`).all();
|
|
4418
4592
|
const insert = this.db.prepare("INSERT OR IGNORE INTO projects(id, payload) VALUES (?, ?)");
|
|
4419
4593
|
for (const row of legacy) {
|
|
4420
|
-
const id =
|
|
4594
|
+
const id = z19.object({ id: z19.string() }).parse(row).id;
|
|
4421
4595
|
insert.run(
|
|
4422
4596
|
id,
|
|
4423
4597
|
JSON.stringify({ id, name: id, path: path11.join(root, id), available: false })
|
|
@@ -4627,7 +4801,7 @@ var init_store = __esm({
|
|
|
4627
4801
|
if (event.type === "message.user") this.claimAttachments(event.attachments ?? []);
|
|
4628
4802
|
const result = this.db.prepare("INSERT INTO events(session_id, recorded_at, payload) VALUES (?, ?, ?)").run(sessionId, recordedAt, JSON.stringify(event));
|
|
4629
4803
|
if (notification) {
|
|
4630
|
-
const count =
|
|
4804
|
+
const count = z19.object({ total: z19.number() }).parse(this.db.prepare("SELECT COUNT(*) AS total FROM push_outbox").get());
|
|
4631
4805
|
if (count.total >= 1e3)
|
|
4632
4806
|
this.db.prepare(
|
|
4633
4807
|
"DELETE FROM push_outbox WHERE rowid IN (SELECT rowid FROM push_outbox ORDER BY rowid LIMIT 1)"
|
|
@@ -4679,7 +4853,7 @@ var init_store = __esm({
|
|
|
4679
4853
|
for (const suffix of ["-wal", "-shm"])
|
|
4680
4854
|
if (existsSync2(filename + suffix)) chmodSync(filename + suffix, 384);
|
|
4681
4855
|
try {
|
|
4682
|
-
const schema =
|
|
4856
|
+
const schema = z19.object({ user_version: z19.number() }).parse(db.prepare("PRAGMA user_version").get());
|
|
4683
4857
|
if (schema.user_version > 1)
|
|
4684
4858
|
throw new Error("This database requires a newer Agentrific server.");
|
|
4685
4859
|
db.exec(`
|
|
@@ -4831,14 +5005,14 @@ import { styleText as styleText4 } from "node:util";
|
|
|
4831
5005
|
import cors from "@fastify/cors";
|
|
4832
5006
|
import multipart from "@fastify/multipart";
|
|
4833
5007
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
4834
|
-
import { execFile as
|
|
4835
|
-
import { promisify as
|
|
5008
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
5009
|
+
import { promisify as promisify5 } from "node:util";
|
|
4836
5010
|
import { realpath as realpath2 } from "node:fs/promises";
|
|
4837
|
-
import { homedir as
|
|
5011
|
+
import { homedir as homedir5 } from "node:os";
|
|
4838
5012
|
import path13 from "node:path";
|
|
4839
5013
|
import { WebSocket } from "ws";
|
|
4840
5014
|
import { Effect as Effect17, ManagedRuntime } from "effect";
|
|
4841
|
-
import { z as
|
|
5015
|
+
import { z as z20 } from "zod";
|
|
4842
5016
|
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
4843
5017
|
function publish(event, workflowUpdate = false) {
|
|
4844
5018
|
const previous = event.type === "session.updated" ? Effect17.runSync(store.get(event.session.id)) : void 0;
|
|
@@ -5046,10 +5220,10 @@ async function withSessionTaskLock(id, operation) {
|
|
|
5046
5220
|
return withTaskLock(lockId, operation);
|
|
5047
5221
|
}
|
|
5048
5222
|
function localServerUrl() {
|
|
5049
|
-
const address =
|
|
5223
|
+
const address = z20.object({ port: z20.number() }).safeParse(app.server.address());
|
|
5050
5224
|
return address.success ? `http://${host}:${address.data.port}` : void 0;
|
|
5051
5225
|
}
|
|
5052
|
-
var host, port, defaultProjectsRoot, worktreesRoot, execFileAsync, app, dataRoot, releaseLock, effects, store, security, sandboxPushUrl, testPushUrl, push, allowedOrigins, attachmentEffects, attachments, attachmentCleanup, models, delivery, preparation, agentEffects, runtime, worktreeCleanup, maxWorktreeAgeDays, pruneWorktrees, worktreeCleanupTimer, updates, maintenance, stopUpdateDiscovery, HistoryQuery, StartTaskBody, automation, automationTimer, phoneOrigin, phone, closeControl;
|
|
5226
|
+
var host, port, defaultProjectsRoot, worktreesRoot, execFileAsync, app, dataRoot, releaseLock, effects, store, security, sandboxPushUrl, testPushUrl, push, allowedOrigins, attachmentEffects, attachments, attachmentCleanup, claudeCode, modelRuntime, models, delivery, preparation, agentEffects, runtime, worktreeCleanup, maxWorktreeAgeDays, pruneWorktrees, worktreeCleanupTimer, updates, maintenance, stopUpdateDiscovery, HistoryQuery, StartTaskBody, automation, automationTimer, phoneOrigin, phone, closeControl;
|
|
5053
5227
|
var init_index = __esm({
|
|
5054
5228
|
async "src/index.ts"() {
|
|
5055
5229
|
"use strict";
|
|
@@ -5070,6 +5244,7 @@ var init_index = __esm({
|
|
|
5070
5244
|
init_delivery();
|
|
5071
5245
|
init_preparation();
|
|
5072
5246
|
init_delivery_writer();
|
|
5247
|
+
init_claude_code();
|
|
5073
5248
|
init_models();
|
|
5074
5249
|
init_pi();
|
|
5075
5250
|
init_projects();
|
|
@@ -5082,9 +5257,9 @@ var init_index = __esm({
|
|
|
5082
5257
|
port = Number(process.env.AGENTRIFIC_PORT ?? 4318);
|
|
5083
5258
|
defaultProjectsRoot = path13.resolve(process.env.AGENTRIFIC_PROJECTS_DIR ?? "./projects");
|
|
5084
5259
|
worktreesRoot = path13.resolve(
|
|
5085
|
-
process.env.AGENTRIFIC_WORKTREES_DIR ?? path13.join(
|
|
5260
|
+
process.env.AGENTRIFIC_WORKTREES_DIR ?? path13.join(homedir5(), ".agentrific", "workspaces")
|
|
5086
5261
|
);
|
|
5087
|
-
execFileAsync =
|
|
5262
|
+
execFileAsync = promisify5(execFile5);
|
|
5088
5263
|
if (host !== "127.0.0.1" && host !== "::1")
|
|
5089
5264
|
throw new Error("Bind Agentrific to loopback and expose it with Tailscale Serve or cloudflared.");
|
|
5090
5265
|
app = Fastify({
|
|
@@ -5185,7 +5360,10 @@ var init_index = __esm({
|
|
|
5185
5360
|
return reply.type(preview ? "image/png" : attachment.mimeType).send(attachments.content(attachment, preview));
|
|
5186
5361
|
}
|
|
5187
5362
|
);
|
|
5188
|
-
|
|
5363
|
+
claudeCode = new ClaudeCode();
|
|
5364
|
+
modelRuntime = await ModelRuntime.create();
|
|
5365
|
+
await claudeCode.refresh(modelRuntime);
|
|
5366
|
+
models = new ServerModels(modelRuntime, store, claudeCode);
|
|
5189
5367
|
delivery = new DeliveryCoordinator(store, {
|
|
5190
5368
|
publish: (event) => publish(event, true),
|
|
5191
5369
|
draft: deliveryWriter(models),
|
|
@@ -5365,7 +5543,7 @@ var init_index = __esm({
|
|
|
5365
5543
|
return project ?? reply.code(404).send({ error: "Project not found on this server." });
|
|
5366
5544
|
});
|
|
5367
5545
|
app.post("/api/projects/:id/reconnect", async (request, reply) => {
|
|
5368
|
-
const parsed =
|
|
5546
|
+
const parsed = z20.object({ path: z20.string().trim().min(1).max(4096) }).safeParse(request.body);
|
|
5369
5547
|
if (!parsed.success)
|
|
5370
5548
|
return reply.code(400).send({ error: "Enter the project's new folder path." });
|
|
5371
5549
|
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
@@ -5451,7 +5629,7 @@ var init_index = __esm({
|
|
|
5451
5629
|
}
|
|
5452
5630
|
});
|
|
5453
5631
|
app.post("/api/tasks/:id/discard-unrun", async (request, reply) => {
|
|
5454
|
-
const parsed =
|
|
5632
|
+
const parsed = z20.object({ updatedAt: z20.string().datetime() }).safeParse(request.body);
|
|
5455
5633
|
if (!parsed.success) return reply.code(400).send({ error: "The task revision is required." });
|
|
5456
5634
|
try {
|
|
5457
5635
|
await deleteTask(store, request.params.id, void 0, parsed.data.updatedAt);
|
|
@@ -5521,9 +5699,9 @@ var init_index = __esm({
|
|
|
5521
5699
|
}
|
|
5522
5700
|
})
|
|
5523
5701
|
);
|
|
5524
|
-
HistoryQuery =
|
|
5525
|
-
after:
|
|
5526
|
-
limit:
|
|
5702
|
+
HistoryQuery = z20.object({
|
|
5703
|
+
after: z20.coerce.number().int().nonnegative().default(0),
|
|
5704
|
+
limit: z20.coerce.number().int().min(1).max(1e3).default(500)
|
|
5527
5705
|
});
|
|
5528
5706
|
app.get("/api/sessions/:id/events", async (request, reply) => {
|
|
5529
5707
|
const query = HistoryQuery.safeParse(request.query);
|
|
@@ -5537,7 +5715,7 @@ var init_index = __esm({
|
|
|
5537
5715
|
const location = await resolveSessionProject(request.params.id);
|
|
5538
5716
|
if (location.error !== void 0)
|
|
5539
5717
|
return reply.code(location.status).send({ error: location.error });
|
|
5540
|
-
const parsed =
|
|
5718
|
+
const parsed = z20.object({ reset: z20.boolean().optional() }).safeParse(request.body ?? {});
|
|
5541
5719
|
if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
|
|
5542
5720
|
try {
|
|
5543
5721
|
return await openRepoChat(
|
|
@@ -5561,7 +5739,7 @@ var init_index = __esm({
|
|
|
5561
5739
|
if (!result.session) return reply.code(result.status).send({ error: result.error });
|
|
5562
5740
|
return reply.code(201).send(result.session);
|
|
5563
5741
|
});
|
|
5564
|
-
StartTaskBody =
|
|
5742
|
+
StartTaskBody = z20.object({ prompt: z20.string().trim().max(2e4).optional() }).default({});
|
|
5565
5743
|
app.post("/api/tasks/:id/start", async (request, reply) => {
|
|
5566
5744
|
const body = StartTaskBody.safeParse(request.body ?? {});
|
|
5567
5745
|
if (!body.success) return reply.code(400).send({ error: "The task prompt is too long." });
|
|
@@ -5596,7 +5774,7 @@ var init_index = __esm({
|
|
|
5596
5774
|
return automation.configure(parsed.data);
|
|
5597
5775
|
});
|
|
5598
5776
|
app.patch("/api/automation/queue", async (request, reply) => {
|
|
5599
|
-
const parsed =
|
|
5777
|
+
const parsed = z20.object({ taskId: z20.string().min(1), beforeId: z20.string().min(1).nullable() }).safeParse(request.body);
|
|
5600
5778
|
if (!parsed.success)
|
|
5601
5779
|
return reply.code(400).send({ error: "Choose a task and a queue position." });
|
|
5602
5780
|
try {
|
|
@@ -5677,6 +5855,7 @@ var init_index = __esm({
|
|
|
5677
5855
|
serverId: security.serverId,
|
|
5678
5856
|
publicUrl: process.env.AGENTRIFIC_PUBLIC_URL,
|
|
5679
5857
|
phoneProvider: phone?.snapshot().provider,
|
|
5858
|
+
phoneProgress: phone?.snapshot().progress,
|
|
5680
5859
|
localUrl: localServerUrl(),
|
|
5681
5860
|
serverVersion,
|
|
5682
5861
|
connectedDevices: new Set(security.sockets.values()).size,
|
|
@@ -5728,7 +5907,7 @@ var init_index = __esm({
|
|
|
5728
5907
|
// src/cli.ts
|
|
5729
5908
|
init_warnings();
|
|
5730
5909
|
import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
|
|
5731
|
-
import { homedir as
|
|
5910
|
+
import { homedir as homedir6 } from "node:os";
|
|
5732
5911
|
import path14 from "node:path";
|
|
5733
5912
|
import { fileURLToPath } from "node:url";
|
|
5734
5913
|
|
|
@@ -5828,7 +6007,7 @@ init_version();
|
|
|
5828
6007
|
init_pairing();
|
|
5829
6008
|
import { spawn as spawn2 } from "node:child_process";
|
|
5830
6009
|
import { parseArgs } from "node:util";
|
|
5831
|
-
import { z as
|
|
6010
|
+
import { z as z21 } from "zod";
|
|
5832
6011
|
|
|
5833
6012
|
// src/terminal.ts
|
|
5834
6013
|
init_pairing();
|
|
@@ -6057,11 +6236,11 @@ function startTerminal(dataDir, publicUrl, tunnel) {
|
|
|
6057
6236
|
init_opentunnel();
|
|
6058
6237
|
init_tunnels();
|
|
6059
6238
|
init_cli_output();
|
|
6060
|
-
var ConfigSchema =
|
|
6061
|
-
dataDir:
|
|
6062
|
-
projectsDir:
|
|
6063
|
-
publicUrl:
|
|
6064
|
-
connection:
|
|
6239
|
+
var ConfigSchema = z21.object({
|
|
6240
|
+
dataDir: z21.string(),
|
|
6241
|
+
projectsDir: z21.string(),
|
|
6242
|
+
publicUrl: z21.string().url().optional(),
|
|
6243
|
+
connection: z21.enum(["opentunnel", "tailscale", "cloudflared", "existing", "local"]).optional()
|
|
6065
6244
|
});
|
|
6066
6245
|
function parseCommandArgs() {
|
|
6067
6246
|
return parseArgs({
|
|
@@ -6091,14 +6270,14 @@ async function main() {
|
|
|
6091
6270
|
return;
|
|
6092
6271
|
}
|
|
6093
6272
|
const dataDir = path14.resolve(
|
|
6094
|
-
values["data-dir"] ?? process.env.AGENTRIFIC_DATA_DIR ?? path14.join(
|
|
6273
|
+
values["data-dir"] ?? process.env.AGENTRIFIC_DATA_DIR ?? path14.join(homedir6(), ".agentrific")
|
|
6095
6274
|
);
|
|
6096
6275
|
const filename = path14.join(dataDir, "config.json");
|
|
6097
6276
|
const config = ConfigSchema.parse(
|
|
6098
6277
|
JSON.parse(
|
|
6099
6278
|
await readFile7(filename, "utf8").catch((error) => {
|
|
6100
|
-
if (!
|
|
6101
|
-
return JSON.stringify({ dataDir, projectsDir: path14.join(
|
|
6279
|
+
if (!z21.object({ code: z21.literal("ENOENT") }).safeParse(error).success) throw error;
|
|
6280
|
+
return JSON.stringify({ dataDir, projectsDir: path14.join(homedir6(), "projects") });
|
|
6102
6281
|
})
|
|
6103
6282
|
)
|
|
6104
6283
|
);
|
|
@@ -6133,7 +6312,7 @@ async function startCommand({ values, dataDir, filename, config }) {
|
|
|
6133
6312
|
process.stdin.isTTY && process.stdout.isTTY && !values["no-interactive"]
|
|
6134
6313
|
);
|
|
6135
6314
|
const explicitUrl = values["public-url"] ?? process.env.AGENTRIFIC_PUBLIC_URL;
|
|
6136
|
-
const method = values.connection ?
|
|
6315
|
+
const method = values.connection ? z21.enum(["opentunnel", "tailscale", "cloudflared", "existing", "local"]).parse(values.connection) : explicitUrl ? "existing" : config.connection;
|
|
6137
6316
|
const origin = `http://${process.env.AGENTRIFIC_HOST ?? "127.0.0.1"}:${process.env.AGENTRIFIC_PORT ?? "4318"}`;
|
|
6138
6317
|
if (interactive) {
|
|
6139
6318
|
process.env.AGENTRIFIC_INTERACTIVE = "1";
|
|
@@ -6225,11 +6404,11 @@ async function pairCommand({ values, dataDir, config }) {
|
|
|
6225
6404
|
return;
|
|
6226
6405
|
}
|
|
6227
6406
|
async function maintenanceCommand({ values, positionals, dataDir }) {
|
|
6228
|
-
const action =
|
|
6407
|
+
const action = z21.enum(["enter", "resume", "drain"]).parse(positionals[1]);
|
|
6229
6408
|
const result = await controlRequest(dataDir, {
|
|
6230
6409
|
command: "maintenance",
|
|
6231
6410
|
action,
|
|
6232
|
-
timeoutMs:
|
|
6411
|
+
timeoutMs: z21.coerce.number().int().min(0).max(6e4).parse(values["timeout-ms"] ?? 3e4)
|
|
6233
6412
|
});
|
|
6234
6413
|
console.log(JSON.stringify(result, null, 2));
|
|
6235
6414
|
if (action === "drain" && "drained" in result && !result.drained) process.exitCode = 1;
|
|
@@ -6238,7 +6417,7 @@ async function maintenanceCommand({ values, positionals, dataDir }) {
|
|
|
6238
6417
|
async function devicesCommand({ dataDir, command }) {
|
|
6239
6418
|
console.log(
|
|
6240
6419
|
JSON.stringify(
|
|
6241
|
-
await controlRequest(dataDir, { command:
|
|
6420
|
+
await controlRequest(dataDir, { command: z21.enum(["devices", "status"]).parse(command) }),
|
|
6242
6421
|
null,
|
|
6243
6422
|
2
|
|
6244
6423
|
)
|
|
@@ -6246,7 +6425,7 @@ async function devicesCommand({ dataDir, command }) {
|
|
|
6246
6425
|
return;
|
|
6247
6426
|
}
|
|
6248
6427
|
async function revokeCommand({ positionals, dataDir }) {
|
|
6249
|
-
const id =
|
|
6428
|
+
const id = z21.string().uuid().parse(positionals[1]);
|
|
6250
6429
|
console.log(JSON.stringify(await controlRequest(dataDir, { command: "revoke", id })));
|
|
6251
6430
|
return;
|
|
6252
6431
|
}
|