@task0/cli 0.8.0 → 0.9.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.
Files changed (3) hide show
  1. package/README.md +14 -6
  2. package/dist/main.js +62 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -65,15 +65,19 @@ task0 run <id> # drive the full triage → plan → refine → exec lo
65
65
  ## Run as a service (autostart on login / boot)
66
66
 
67
67
  `task0 daemon register` installs an OS-level autostart service in addition to
68
- recording the daemon identity. After registering you can sign out, reboot, and
69
- the daemon reconnects on its own.
68
+ recording the daemon identity. The server URL comes from the active profile's
69
+ `api_url` set it once per profile, then `register` reads it. After registering
70
+ you can sign out, reboot, and the daemon reconnects on its own.
70
71
 
71
72
  ```sh
72
- # user-level (default) starts at user login, no sudo needed
73
- task0 daemon register --server https://central.example.com:4318
73
+ # 1. Tell the active profile which server to talk to (once per profile)
74
+ task0 profile set api_url https://central.example.com:4318
74
75
 
75
- # system-level — starts at boot, runs without an active login
76
- sudo -E task0 daemon register --system --server https://central.example.com:4318
76
+ # 2a. user-level (default) — starts at user login, no sudo needed
77
+ task0 daemon register
78
+
79
+ # 2b. system-level — starts at boot, runs without an active login
80
+ sudo -E task0 daemon register --system
77
81
 
78
82
  # pause / resume without forgetting the identity
79
83
  task0 daemon stop
@@ -83,6 +87,10 @@ task0 daemon start
83
87
  task0 daemon logout
84
88
  ```
85
89
 
90
+ To target a different server for one invocation without writing it to the
91
+ profile, export `TASK0_API_URL` — it overrides the profile's `api_url` for
92
+ that shell session.
93
+
86
94
  Per platform:
87
95
 
88
96
  | Platform | Scope | File written |
package/dist/main.js CHANGED
@@ -1306,6 +1306,9 @@ function profileExists(name) {
1306
1306
  }
1307
1307
  }
1308
1308
  var activeProfileCache = null;
1309
+ function getActiveProfile() {
1310
+ return activeProfileCache;
1311
+ }
1309
1312
  function activateProfile(argv) {
1310
1313
  ensureDefaultProfile();
1311
1314
  const flagValue = parseProfileFlag(argv);
@@ -1329,9 +1332,14 @@ function activateProfile(argv) {
1329
1332
  }
1330
1333
  }
1331
1334
  process.env.TASK0_PROFILE = name;
1335
+ const envApiUrlOverride = process.env.TASK0_API_URL?.trim() || void 0;
1332
1336
  const apiUrl = getApiUrl();
1333
- activeProfileCache = { name, ...apiUrl ? { apiUrl } : {} };
1334
- if (apiUrl && !process.env.TASK0_API_URL) {
1337
+ activeProfileCache = {
1338
+ name,
1339
+ ...apiUrl ? { apiUrl } : {},
1340
+ ...envApiUrlOverride ? { envApiUrlOverride } : {}
1341
+ };
1342
+ if (apiUrl && !envApiUrlOverride) {
1335
1343
  process.env.TASK0_API_URL = apiUrl;
1336
1344
  }
1337
1345
  }
@@ -1695,7 +1703,7 @@ function requireLocalDaemon() {
1695
1703
  const id = localDaemonId();
1696
1704
  if (!id) {
1697
1705
  console.error(chalk.red("This host is not registered as a daemon."));
1698
- console.error(chalk.dim("Run `task0 daemon register --server <url>` first."));
1706
+ console.error(chalk.dim("Run `task0 daemon register` first (set api_url with `task0 profile set api_url <url>` if needed)."));
1699
1707
  process.exit(1);
1700
1708
  }
1701
1709
  return id;
@@ -5787,10 +5795,20 @@ function fail6(message, code = 1) {
5787
5795
  function loadRequiredIdentity() {
5788
5796
  const identity = readDaemonIdentity();
5789
5797
  if (!identity) {
5790
- fail6(`No daemon identity at ${daemonConfigPath()}. Run \`task0 daemon register --server <url>\` first.`);
5798
+ fail6(`No daemon identity at ${daemonConfigPath()}. Run \`task0 daemon register\` first (set api_url with \`task0 profile set api_url <url>\` if it isn't already configured).`);
5791
5799
  }
5792
5800
  return identity;
5793
5801
  }
5802
+ function resolveTargetServerUrl() {
5803
+ const url = process.env.TASK0_API_URL?.trim();
5804
+ if (url) return url.replace(/\/$/, "");
5805
+ fail6(
5806
+ `No server URL configured for the active profile.
5807
+ Set it with:
5808
+ task0 profile set api_url <url>
5809
+ \u2026or export TASK0_API_URL for a one-off override.`
5810
+ );
5811
+ }
5794
5812
  function serverBase(identity) {
5795
5813
  if (identity) return identity.server_url.replace(/\/$/, "");
5796
5814
  return (process.env.TASK0_API_URL || "http://127.0.0.1:4318").replace(/\/$/, "");
@@ -5828,7 +5846,7 @@ function rerunArgv() {
5828
5846
  return ["task0", ...process.argv.slice(2)].join(" ");
5829
5847
  }
5830
5848
  var daemonCmd = new Command19("daemon").description("Manage this host as a task0 daemon registered with a central server");
5831
- daemonCmd.command("register").description("Register this host with a central server, install the autostart service, and start it").requiredOption("-s, --server <url>", "Central server URL (e.g. https://central.example.com:4318)").option("-n, --name <name>", "Display name for this daemon (defaults to hostname)").option("-t, --token <token>", "API token (apit_...) for registration. Falls back to TASK0_API_TOKEN, then admin token.").option("--force", "Overwrite existing identity if present").option("--system", "Install at the system layer (LaunchDaemons / /etc/systemd/system, requires sudo)").option("--no-install", "Skip installing the autostart service unit").option("--no-start", "Install the service unit but do not start it now").action(async (opts) => {
5849
+ daemonCmd.command("register").description("Register this host with the server configured in the active profile, install the autostart service, and start it").option("-n, --name <name>", "Display name for this daemon (defaults to hostname)").option("-t, --token <token>", "API token (apit_...) for registration. Falls back to TASK0_API_TOKEN, then admin token.").option("--force", "Overwrite existing identity if present").option("--system", "Install at the system layer (LaunchDaemons / /etc/systemd/system, requires sudo)").option("--no-install", "Skip installing the autostart service unit").option("--no-start", "Install the service unit but do not start it now").action(async (opts) => {
5832
5850
  const scope = opts.system ? "system" : "user";
5833
5851
  if (opts.install) {
5834
5852
  requireRootIfSystem(scope, rerunArgv());
@@ -5837,7 +5855,7 @@ daemonCmd.command("register").description("Register this host with a central ser
5837
5855
  if (existing && !opts.force) {
5838
5856
  fail6(`Already registered as ${existing.daemon_id}. Pass --force to re-register.`);
5839
5857
  }
5840
- const base = opts.server.replace(/\/$/, "");
5858
+ const base = resolveTargetServerUrl();
5841
5859
  let authHeader;
5842
5860
  try {
5843
5861
  const choice = pickRegisterAuth(opts.token);
@@ -6666,6 +6684,10 @@ import path31 from "path";
6666
6684
  import { Command as Command23 } from "commander";
6667
6685
  import chalk23 from "chalk";
6668
6686
  import yaml10 from "js-yaml";
6687
+ var PROFILE_SCALAR_KEYS = ["api_url"];
6688
+ function isProfileScalarKey(key) {
6689
+ return PROFILE_SCALAR_KEYS.includes(key);
6690
+ }
6669
6691
  function fail9(msg) {
6670
6692
  console.error(chalk23.red(msg));
6671
6693
  process.exit(1);
@@ -6767,6 +6789,25 @@ profile2.command("use <name>").description("Set the current profile").action((na
6767
6789
  profile2.command("current").description('Print the current profile name (always succeeds; defaults to "default")').action(() => {
6768
6790
  console.log(currentProfileName());
6769
6791
  });
6792
+ profile2.command("set <key> <value>").description(`Set a config field on the active profile. Supported keys: ${PROFILE_SCALAR_KEYS.join(", ")}.`).action((key, value) => {
6793
+ if (!isProfileScalarKey(key)) {
6794
+ fail9(`Unknown key "${key}". Supported keys: ${PROFILE_SCALAR_KEYS.join(", ")}.`);
6795
+ }
6796
+ const config = loadConfig();
6797
+ config[key] = value;
6798
+ saveConfig(config);
6799
+ console.log(chalk23.green(`${currentProfileName()}.${key} = ${value}`));
6800
+ });
6801
+ profile2.command("get <key>").description(`Read a config field from the active profile. Supported keys: ${PROFILE_SCALAR_KEYS.join(", ")}.`).action((key) => {
6802
+ if (!isProfileScalarKey(key)) {
6803
+ fail9(`Unknown key "${key}". Supported keys: ${PROFILE_SCALAR_KEYS.join(", ")}.`);
6804
+ }
6805
+ const value = loadConfig()[key];
6806
+ if (typeof value !== "string" || value.length === 0) {
6807
+ process.exit(1);
6808
+ }
6809
+ console.log(value);
6810
+ });
6770
6811
  profile2.command("show [name]").description("Show a profile's configuration and daemon registration").action((name) => {
6771
6812
  const target = name ?? currentProfileName();
6772
6813
  if (!isValidProfileName(target)) {
@@ -6779,19 +6820,30 @@ profile2.command("show [name]").description("Show a profile's configuration and
6779
6820
  const current = currentProfileName();
6780
6821
  const apiUrl = readProfileApiUrl(target);
6781
6822
  const identity = readDaemonAt(dir);
6823
+ const envOverride = target === current ? getActiveProfile()?.envApiUrlOverride : void 0;
6824
+ const effective = envOverride ?? apiUrl;
6782
6825
  console.log(`${chalk23.bold(target)}${current === target ? chalk23.green(" (current)") : ""}`);
6783
6826
  console.log(` dir: ${dir}`);
6784
6827
  console.log(` config: ${path31.join(dir, "config.yml")}`);
6785
- console.log(` api_url: ${apiUrl ?? chalk23.dim("(unset)")}`);
6828
+ if (envOverride && apiUrl && envOverride !== apiUrl) {
6829
+ console.log(` api_url: ${envOverride} ${chalk23.dim("(TASK0_API_URL env, overrides profile)")}`);
6830
+ console.log(` ${chalk23.dim(`profile: ${apiUrl}`)}`);
6831
+ } else if (envOverride && !apiUrl) {
6832
+ console.log(` api_url: ${envOverride} ${chalk23.dim("(TASK0_API_URL env; profile config.yml is unset)")}`);
6833
+ } else if (apiUrl) {
6834
+ console.log(` api_url: ${apiUrl} ${chalk23.dim("(profile config.yml)")}`);
6835
+ } else {
6836
+ console.log(` api_url: ${chalk23.dim("(unset \u2014 run `task0 profile set api_url <url>`)")}`);
6837
+ }
6786
6838
  if (!identity) {
6787
6839
  console.log(chalk23.dim(" daemon.json: (not registered yet)"));
6788
6840
  return;
6789
6841
  }
6790
6842
  console.log(` daemon_id: ${identity.daemon_id ?? chalk23.dim("(missing)")}`);
6791
6843
  console.log(` daemon.server_url: ${identity.server_url ?? chalk23.dim("(missing)")}`);
6792
- if (apiUrl && identity.server_url && apiUrl !== identity.server_url) {
6793
- console.log(chalk23.yellow(` warn: profile.api_url and daemon.json.server_url disagree.`));
6794
- console.log(chalk23.dim(` Re-register the daemon to align: task0 --profile ${target} daemon register --server ${apiUrl}`));
6844
+ if (effective && identity.server_url && effective !== identity.server_url) {
6845
+ console.log(chalk23.yellow(` warn: effective api_url and daemon.json.server_url disagree.`));
6846
+ console.log(chalk23.dim(` Re-register the daemon to align: task0 --profile ${target} daemon register --force`));
6795
6847
  }
6796
6848
  });
6797
6849
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@task0/cli",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "task0 — task-centric agent workflow CLI",
5
5
  "homepage": "https://github.com/cy0-labs/task0#readme",
6
6
  "repository": {