@artil-ai/server 0.2.2 → 0.2.3

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/bin/main.js CHANGED
@@ -22326,7 +22326,7 @@ var channel = {
22326
22326
  create: ({ webhookUrl, secret, fetch: fetch2 }) => new TelegramChannel(createBotApi({ webhook: webhookUrl ? { url: webhookUrl, secret } : undefined, fetch: fetch2 }))
22327
22327
  };
22328
22328
  // package.json
22329
- var version2 = "0.2.2";
22329
+ var version2 = "0.2.3";
22330
22330
 
22331
22331
  // src/app/cli.ts
22332
22332
  import { parseArgs } from "util";
@@ -52142,7 +52142,9 @@ async function startServer({
52142
52142
  store = config2.s3 ? createS3Store(config2.s3) : undefined,
52143
52143
  fetch: fetch2
52144
52144
  }) {
52145
+ await using startup = new AsyncDisposableStack;
52145
52146
  const database = openDatabase({ path: databasePath, migrationsFolder, secret: config2.secret });
52147
+ startup.defer(() => database.close());
52146
52148
  let handleRequest = startingResponse;
52147
52149
  const server = Bun.serve({
52148
52150
  hostname: "127.0.0.1",
@@ -52150,12 +52152,11 @@ async function startServer({
52150
52152
  fetch: (request) => handleRequest(request),
52151
52153
  idleTimeout: 60
52152
52154
  });
52155
+ startup.defer(() => server.stop(true));
52153
52156
  const port = server.port ?? config2.port;
52154
- const publicUrl = config2.public && tailscale ? await tailscale.open(port).catch(async (cause) => {
52155
- await server.stop(true);
52156
- database.close();
52157
- throw cause;
52158
- }) : undefined;
52157
+ const publicUrl = config2.public && tailscale ? await tailscale.open(port) : undefined;
52158
+ if (publicUrl)
52159
+ startup.defer(() => tailscale?.close());
52159
52160
  const baseUrl = publicUrl ?? `http://localhost:${port}`;
52160
52161
  const channels = createChannels({
52161
52162
  definitions,
@@ -52190,6 +52191,10 @@ async function startServer({
52190
52191
  for (const channelReceivers of receivers)
52191
52192
  channelReceivers.sync();
52192
52193
  }
52194
+ startup.defer(() => {
52195
+ for (const channelReceivers of receivers)
52196
+ channelReceivers.stop();
52197
+ });
52193
52198
  syncReceivers();
52194
52199
  let dataVersion = database.dataVersion();
52195
52200
  const databaseWatcher = setInterval(() => {
@@ -52199,28 +52204,193 @@ async function startServer({
52199
52204
  syncReceivers();
52200
52205
  }
52201
52206
  }, 2 * second);
52207
+ startup.defer(() => clearInterval(databaseWatcher));
52202
52208
  function removeExpiredFiles() {
52203
52209
  services.files.removeExpired().catch((cause) => log2(`Could not remove expired files: ${messageOf(cause, String(cause))}`));
52204
52210
  }
52205
52211
  removeExpiredFiles();
52206
52212
  const expiredFilesTimer = setInterval(removeExpiredFiles, hour);
52213
+ startup.defer(() => clearInterval(expiredFilesTimer));
52214
+ const resources = startup.move();
52207
52215
  return {
52208
52216
  baseUrl,
52209
52217
  port,
52210
52218
  services,
52211
- async stop() {
52212
- clearInterval(databaseWatcher);
52213
- clearInterval(expiredFilesTimer);
52214
- for (const channelReceivers of receivers)
52215
- channelReceivers.stop();
52216
- await server.stop(true);
52217
- if (publicUrl)
52218
- await tailscale?.close();
52219
- database.close();
52220
- }
52219
+ stop: () => resources.disposeAsync()
52221
52220
  };
52222
52221
  }
52223
52222
 
52223
+ // src/app/cli.ts
52224
+ var usage = `artil-server: a self-hosted Artil Server
52225
+
52226
+ Usage:
52227
+ artil-server Start the server, setting it up first if it is new
52228
+ artil-server setup Set up the accounts, channels, files and who reaches the server
52229
+ artil-server account create <name> Create an agent account
52230
+ artil-server account list List the accounts
52231
+ artil-server account remove <name> Remove an account with its messages
52232
+ artil-server account token <name> Make a token, for a computer without a browser
52233
+ artil-server account revoke <name> Sign the account out of every client
52234
+ artil-server <channel> connect <account> <credentials> Connect an address of a channel to an account, with what the channel asks for
52235
+ artil-server <channel> disconnect <account> Disconnect the account's address of the channel
52236
+ artil-server access add <account> <channel> <sender> Let someone wake the agent on the channel
52237
+ artil-server access remove <account> <channel> <sender> Take them off the list
52238
+ artil-server access set <account> <channel> <who> Choose who wakes the agent on the channel: anyone, team or list
52239
+ artil-server password Set the password the sign-in page asks for
52240
+
52241
+ Options:
52242
+ --public Open the server to the internet through Tailscale Funnel
52243
+ --local Keep the server on this computer
52244
+ -h, --help Show this help
52245
+ -v, --version Show the version`;
52246
+ function usageError(message) {
52247
+ return new AppError({ code: "invalid_input", message: `${message}
52248
+
52249
+ ${usage}` });
52250
+ }
52251
+ async function serve({ paths, config: config2, environment }) {
52252
+ const { migrationsFolder, tailscale, out } = environment;
52253
+ if (config2.public && !tailscale) {
52254
+ throw new AppError({
52255
+ code: "unavailable",
52256
+ message: "Public mode needs Tailscale: install it from https://tailscale.com/download, or start with --local"
52257
+ });
52258
+ }
52259
+ if (config2.public && config2.password === null) {
52260
+ throw new AppError({
52261
+ code: "invalid_input",
52262
+ message: "Public mode needs a password for the sign-in page: set one with artil-server password"
52263
+ });
52264
+ }
52265
+ const server = await startServer({
52266
+ config: config2,
52267
+ databasePath: paths.database,
52268
+ migrationsFolder,
52269
+ tailscale,
52270
+ channels: await loadChannels({
52271
+ specifiers: config2.channels,
52272
+ dataDir: paths.dataDir,
52273
+ builtInChannels: environment.builtInChannels
52274
+ }),
52275
+ password: async () => (await readConfig(paths))?.password ?? null,
52276
+ log: out
52277
+ });
52278
+ out(`Artil Server is at ${server.baseUrl}${config2.public ? " (public, through Tailscale Funnel)" : ""}.`);
52279
+ out(`Sign an agent in with: ${loginCommand(server.baseUrl)}`);
52280
+ out(`MCP clients connect to ${server.baseUrl}/mcp`);
52281
+ await environment.waitForStop();
52282
+ out("Stopping");
52283
+ await server.stop();
52284
+ }
52285
+ async function setPassword({ paths, config: config2, out }) {
52286
+ const newPassword = await password({
52287
+ message: "A password the sign-in page asks for",
52288
+ validate: validatePassword
52289
+ });
52290
+ if (isCancel(newPassword))
52291
+ return;
52292
+ await writeConfig(paths, { ...config2, password: await hashPassword(newPassword) });
52293
+ out("The sign-in page asks for it from now on.");
52294
+ }
52295
+ async function runCli(environment) {
52296
+ const { values, positionals } = parseArgs({
52297
+ args: environment.argv,
52298
+ allowPositionals: true,
52299
+ options: {
52300
+ public: { type: "boolean" },
52301
+ local: { type: "boolean" },
52302
+ help: { type: "boolean", short: "h" },
52303
+ version: { type: "boolean", short: "v" }
52304
+ }
52305
+ });
52306
+ if (values.help)
52307
+ return environment.out(usage);
52308
+ if (values.version)
52309
+ return environment.out(environment.version);
52310
+ const paths = pathsIn(environment.dataDir);
52311
+ const savedConfig = await readConfig(paths);
52312
+ async function applyFlags(config3) {
52313
+ const changed = {
52314
+ ...config3,
52315
+ ...values.public && { public: true },
52316
+ ...values.local && { public: false }
52317
+ };
52318
+ if (changed.public !== config3.public)
52319
+ await writeConfig(paths, changed);
52320
+ return changed;
52321
+ }
52322
+ const [command = "", subcommand = "", ...args] = positionals;
52323
+ const { migrationsFolder, tailscale, artil, runSetup, out, interactive, builtInChannels } = environment;
52324
+ const builtInKinds = builtInChannels.map(({ kind }) => kind);
52325
+ async function hasAgents(config3) {
52326
+ const context2 = await openCommandContext({
52327
+ paths,
52328
+ config: config3,
52329
+ builtInChannels,
52330
+ migrationsFolder,
52331
+ tailscale,
52332
+ out
52333
+ });
52334
+ try {
52335
+ return context2.agents.list().length > 0;
52336
+ } finally {
52337
+ context2.close();
52338
+ }
52339
+ }
52340
+ if (command === "" || command === "start") {
52341
+ const config3 = interactive && (!savedConfig || !await hasAgents(savedConfig)) ? await runSetup({
52342
+ paths,
52343
+ config: savedConfig,
52344
+ builtInChannels,
52345
+ artil,
52346
+ migrationsFolder,
52347
+ tailscale,
52348
+ out
52349
+ }) : savedConfig ?? newConfig({ channels: builtInKinds });
52350
+ if (!savedConfig && !interactive)
52351
+ await writeConfig(paths, config3);
52352
+ return serve({ paths, config: await applyFlags(config3), environment });
52353
+ }
52354
+ if (command === "setup") {
52355
+ await runSetup({
52356
+ paths,
52357
+ config: savedConfig,
52358
+ builtInChannels,
52359
+ artil,
52360
+ migrationsFolder,
52361
+ tailscale,
52362
+ out
52363
+ });
52364
+ return;
52365
+ }
52366
+ const config2 = savedConfig ?? newConfig({ channels: builtInKinds });
52367
+ if (!savedConfig)
52368
+ await writeConfig(paths, config2);
52369
+ if (command === "password")
52370
+ return setPassword({ paths, config: config2, out });
52371
+ const context = await openCommandContext({
52372
+ paths,
52373
+ config: config2,
52374
+ builtInChannels,
52375
+ migrationsFolder,
52376
+ tailscale,
52377
+ out
52378
+ });
52379
+ try {
52380
+ const run2 = new Map([...createAgentCommands(context), ...createAccessCommands(context)]).get(`${command} ${subcommand}`) ?? createAddressCommands(context).get(subcommand)?.(command);
52381
+ if (!run2) {
52382
+ throw usageError(`There is no command ${[command, subcommand].filter(Boolean).join(" ")}`);
52383
+ }
52384
+ if (args.length !== run2.takes.length) {
52385
+ const expected = run2.takes.map((name) => `<${name}>`).join(" ") || "no arguments";
52386
+ throw usageError(`This command takes ${expected}`);
52387
+ }
52388
+ await run2.run(Object.fromEntries(run2.takes.map((name, index2) => [name, args[index2] ?? ""])));
52389
+ } finally {
52390
+ context.close();
52391
+ }
52392
+ }
52393
+
52224
52394
  // src/app/setup.ts
52225
52395
  class SetupStopped extends Error {
52226
52396
  name = "SetupStopped";
@@ -52456,180 +52626,6 @@ async function runSetup({
52456
52626
  }
52457
52627
  }
52458
52628
 
52459
- // src/app/cli.ts
52460
- var usage = `artil-server: a self-hosted Artil Server
52461
-
52462
- Usage:
52463
- artil-server Start the server, setting it up first if it is new
52464
- artil-server setup Set up the accounts, channels, files and who reaches the server
52465
- artil-server account create <name> Create an agent account
52466
- artil-server account list List the accounts
52467
- artil-server account remove <name> Remove an account with its messages
52468
- artil-server account token <name> Make a token, for a computer without a browser
52469
- artil-server account revoke <name> Sign the account out of every client
52470
- artil-server <channel> connect <account> <credentials> Connect an address of a channel to an account, with what the channel asks for
52471
- artil-server <channel> disconnect <account> Disconnect the account's address of the channel
52472
- artil-server access add <account> <channel> <sender> Let someone wake the agent on the channel
52473
- artil-server access remove <account> <channel> <sender> Take them off the list
52474
- artil-server access set <account> <channel> <who> Choose who wakes the agent on the channel: anyone, team or list
52475
- artil-server password Set the password the sign-in page asks for
52476
-
52477
- Options:
52478
- --public Open the server to the internet through Tailscale Funnel
52479
- --local Keep the server on this computer
52480
- -h, --help Show this help
52481
- -v, --version Show the version`;
52482
- function usageError(message) {
52483
- return new AppError({ code: "invalid_input", message: `${message}
52484
-
52485
- ${usage}` });
52486
- }
52487
- async function serve({ paths, config: config2, environment }) {
52488
- const { migrationsFolder, tailscale, out } = environment;
52489
- if (config2.public && !tailscale) {
52490
- throw new AppError({
52491
- code: "unavailable",
52492
- message: "Public mode needs Tailscale: install it from https://tailscale.com/download, or start with --local"
52493
- });
52494
- }
52495
- if (config2.public && config2.password === null) {
52496
- throw new AppError({
52497
- code: "invalid_input",
52498
- message: "Public mode needs a password for the sign-in page: set one with artil-server password"
52499
- });
52500
- }
52501
- const server = await startServer({
52502
- config: config2,
52503
- databasePath: paths.database,
52504
- migrationsFolder,
52505
- tailscale,
52506
- channels: await loadChannels({
52507
- specifiers: config2.channels,
52508
- dataDir: paths.dataDir,
52509
- builtInChannels: environment.builtInChannels
52510
- }),
52511
- password: async () => (await readConfig(paths))?.password ?? null,
52512
- log: out
52513
- });
52514
- out(`Artil Server is at ${server.baseUrl}${config2.public ? " (public, through Tailscale Funnel)" : ""}.`);
52515
- out(`Sign an agent in with: ${loginCommand(server.baseUrl)}`);
52516
- out(`MCP clients connect to ${server.baseUrl}/mcp`);
52517
- const { promise: promise3, resolve } = Promise.withResolvers();
52518
- process.once("SIGINT", resolve);
52519
- process.once("SIGTERM", resolve);
52520
- await promise3;
52521
- out("Stopping");
52522
- await server.stop();
52523
- }
52524
- async function setPassword({ paths, config: config2, out }) {
52525
- const newPassword = await password({
52526
- message: "A password the sign-in page asks for",
52527
- validate: validatePassword
52528
- });
52529
- if (isCancel(newPassword))
52530
- return;
52531
- await writeConfig(paths, { ...config2, password: await hashPassword(newPassword) });
52532
- out("The sign-in page asks for it from now on.");
52533
- }
52534
- async function runCli(environment) {
52535
- const { values, positionals } = parseArgs({
52536
- args: environment.argv,
52537
- allowPositionals: true,
52538
- options: {
52539
- public: { type: "boolean" },
52540
- local: { type: "boolean" },
52541
- help: { type: "boolean", short: "h" },
52542
- version: { type: "boolean", short: "v" }
52543
- }
52544
- });
52545
- if (values.help)
52546
- return environment.out(usage);
52547
- if (values.version)
52548
- return environment.out(environment.version);
52549
- const paths = pathsIn(environment.dataDir);
52550
- const savedConfig = await readConfig(paths);
52551
- async function applyFlags(config3) {
52552
- const changed = {
52553
- ...config3,
52554
- ...values.public && { public: true },
52555
- ...values.local && { public: false }
52556
- };
52557
- if (changed.public !== config3.public)
52558
- await writeConfig(paths, changed);
52559
- return changed;
52560
- }
52561
- const [command = "", subcommand = "", ...args] = positionals;
52562
- const { migrationsFolder, tailscale, artil, out, interactive, builtInChannels } = environment;
52563
- const builtInKinds = builtInChannels.map(({ kind }) => kind);
52564
- async function hasAgents(config3) {
52565
- const context2 = await openCommandContext({
52566
- paths,
52567
- config: config3,
52568
- builtInChannels,
52569
- migrationsFolder,
52570
- tailscale,
52571
- out
52572
- });
52573
- try {
52574
- return context2.agents.list().length > 0;
52575
- } finally {
52576
- context2.close();
52577
- }
52578
- }
52579
- if (command === "" || command === "start") {
52580
- const config3 = interactive && (!savedConfig || !await hasAgents(savedConfig)) ? await runSetup({
52581
- paths,
52582
- config: savedConfig,
52583
- builtInChannels,
52584
- artil,
52585
- migrationsFolder,
52586
- tailscale,
52587
- out
52588
- }) : savedConfig ?? newConfig({ channels: builtInKinds });
52589
- if (!savedConfig && !interactive)
52590
- await writeConfig(paths, config3);
52591
- return serve({ paths, config: await applyFlags(config3), environment });
52592
- }
52593
- if (command === "setup") {
52594
- await runSetup({
52595
- paths,
52596
- config: savedConfig,
52597
- builtInChannels,
52598
- artil,
52599
- migrationsFolder,
52600
- tailscale,
52601
- out
52602
- });
52603
- return;
52604
- }
52605
- const config2 = savedConfig ?? newConfig({ channels: builtInKinds });
52606
- if (!savedConfig)
52607
- await writeConfig(paths, config2);
52608
- if (command === "password")
52609
- return setPassword({ paths, config: config2, out });
52610
- const context = await openCommandContext({
52611
- paths,
52612
- config: config2,
52613
- builtInChannels,
52614
- migrationsFolder,
52615
- tailscale,
52616
- out
52617
- });
52618
- try {
52619
- const run2 = new Map([...createAgentCommands(context), ...createAccessCommands(context)]).get(`${command} ${subcommand}`) ?? createAddressCommands(context).get(subcommand)?.(command);
52620
- if (!run2) {
52621
- throw usageError(`There is no command ${[command, subcommand].filter(Boolean).join(" ")}`);
52622
- }
52623
- if (args.length !== run2.takes.length) {
52624
- const expected = run2.takes.map((name) => `<${name}>`).join(" ") || "no arguments";
52625
- throw usageError(`This command takes ${expected}`);
52626
- }
52627
- await run2.run(Object.fromEntries(run2.takes.map((name, index2) => [name, args[index2] ?? ""])));
52628
- } finally {
52629
- context.close();
52630
- }
52631
- }
52632
-
52633
52629
  // src/platform/tailscale.ts
52634
52630
  import { existsSync } from "fs";
52635
52631
  var statusSchema = exports_external2.object({
@@ -52709,6 +52705,12 @@ function runWith(binary) {
52709
52705
  var migrationsFolder = fileURLToPath(new URL("../migrations", import.meta.url));
52710
52706
  process.umask(63);
52711
52707
  var binary = findTailscale(process.platform);
52708
+ function waitForStop() {
52709
+ const { promise: promise3, resolve } = Promise.withResolvers();
52710
+ process.once("SIGINT", resolve);
52711
+ process.once("SIGTERM", resolve);
52712
+ return promise3;
52713
+ }
52712
52714
  try {
52713
52715
  await runCli({
52714
52716
  argv: process.argv.slice(2),
@@ -52719,6 +52721,8 @@ try {
52719
52721
  migrationsFolder,
52720
52722
  tailscale: binary === undefined ? undefined : createTailscale({ run: runWith(binary) }),
52721
52723
  artil: findArtilCli(),
52724
+ runSetup,
52725
+ waitForStop,
52722
52726
  out: (line) => console.log(line)
52723
52727
  });
52724
52728
  } catch (cause) {
@@ -52728,5 +52732,5 @@ try {
52728
52732
  process.exitCode = 1;
52729
52733
  }
52730
52734
 
52731
- //# debugId=79538CBFE86D9F9064756E2164756E21
52735
+ //# debugId=874EED01CEF8F5FE64756E2164756E21
52732
52736
  //# sourceMappingURL=main.js.map