@todoforai/cli 0.1.59 → 0.1.61

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.
@@ -42884,6 +42884,15 @@ class ApiClient {
42884
42884
  renameDevice(id, name) {
42885
42885
  return this.request("PATCH", `/api/v1/devices/${id}/name`, { id, name });
42886
42886
  }
42887
+ getDeviceAccess(id) {
42888
+ return this.request("GET", `/api/v1/devices/${id}/access`);
42889
+ }
42890
+ shareDevice(id, email) {
42891
+ return this.request("POST", `/api/v1/devices/${id}/share`, { id, email });
42892
+ }
42893
+ unshareDevice(id, email) {
42894
+ return this.request("POST", `/api/v1/devices/${id}/unshare`, { id, email });
42895
+ }
42887
42896
  setDeviceWallpaperMode(id, mode) {
42888
42897
  return this.request("POST", `/api/v1/devices/${id}/wallpaper-mode`, { id, mode });
42889
42898
  }
@@ -43183,6 +43192,9 @@ import WebSocket from "ws";
43183
43192
  import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
43184
43193
  import { join, dirname } from "node:path";
43185
43194
  import { homedir, platform } from "node:os";
43195
+ function runActorToken() {
43196
+ return process.env.TODOFORAI_USER_ID ? process.env.TODOFORAI_API_TOKEN || "" : "";
43197
+ }
43186
43198
  function normalizeApiUrl(url) {
43187
43199
  if (url.startsWith("localhost"))
43188
43200
  return `http://${url}`;
@@ -44610,7 +44622,7 @@ var tool_catalog_default = {
44610
44622
  statusCmd: "ntn doctor 2>&1 | grep -E 'Token valid\\s+✔' >/dev/null && echo authenticated",
44611
44623
  loginCmd: "ntn login",
44612
44624
  loginNeedsPty: true,
44613
- oauthUrlPattern: "(https://www\\.notion\\.so/workers/cli-login\\S+)",
44625
+ oauthUrlPattern: "(https://(?:www\\.notion\\.so|app\\.notion\\.com)/workers/cli-login\\S+)",
44614
44626
  sessionIdPattern: "verificationCode=([A-Z0-9-]{5,12})",
44615
44627
  credentialPaths: ["~/.config/notion"],
44616
44628
  versionCmd: "ntn --version 2>/dev/null | sed -n 1p",
@@ -46123,7 +46135,7 @@ import { parseArgs } from "util";
46123
46135
  // package.json
46124
46136
  var package_default = {
46125
46137
  name: "@todoforai/cli",
46126
- version: "0.1.59",
46138
+ version: "0.1.61",
46127
46139
  type: "module",
46128
46140
  bin: {
46129
46141
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -46302,7 +46314,6 @@ function parseCliArgs() {
46302
46314
  link: { type: "boolean", default: false },
46303
46315
  direction: { type: "string" },
46304
46316
  url: { type: "string" },
46305
- "pasted-file": { type: "string" },
46306
46317
  account: { type: "string" },
46307
46318
  "from-company": { type: "boolean", default: false },
46308
46319
  "dry-run": { type: "boolean", default: false },
@@ -49487,7 +49498,7 @@ var ADAPTERS = { Gmail: gmail, Outlook: outlook, Chat: chat };
49487
49498
  var DEVICE_CHANNELS = Object.keys(ADAPTERS);
49488
49499
  function checkChannel(channel) {
49489
49500
  const a = ADAPTERS[channel];
49490
- return a ? a.check() : `${channel} is not read on the device (server-side: pass --url, or paste)`;
49501
+ return a ? a.check() : `${channel} is not read on the device (server-side: pass --url)`;
49491
49502
  }
49492
49503
  function collectChannel(channel, max = 40) {
49493
49504
  const a = ADAPTERS[channel];
@@ -49767,12 +49778,12 @@ Usage:
49767
49778
  tfa-cli brand rename <name>
49768
49779
  tfa-cli brand voice Learned profile + sources
49769
49780
  tfa-cli brand voice answers [<q>=<a>…] Show / set the Manual source: answers to the style questions
49770
- tfa-cli brand voice collect <channel> [--url U | --pasted-file <F|-> | --max N]
49781
+ tfa-cli brand voice collect <channel> [--url U | --max N]
49771
49782
  Add a writing sample source.
49772
49783
  On this device (signed-in CLI, reply pairs):
49773
49784
  Gmail (zele) Outlook (outlook-api) Chat (tfa-memory)
49774
- Server-side (--url or paste):
49775
- X LinkedIn Facebook Instagram Slack Teams
49785
+ Server-side (--url):
49786
+ X LinkedIn Facebook Instagram
49776
49787
  --dry-run print the samples as JSONL, store nothing
49777
49788
  tfa-cli brand voice check [<channel>|--all] Can this device read the channel? tool, login, sample count
49778
49789
  tfa-cli brand voice remove <channel>
@@ -49780,7 +49791,6 @@ Usage:
49780
49791
  tfa-cli brand voice correct "<what is off>" Tell the learner what it got wrong; profile is updated
49781
49792
  in one pass and the correction is kept for every re-learn
49782
49793
 
49783
- --pasted-file - reads stdin. Delete is UI-only.
49784
49794
 
49785
49795
  Questions the UI asks (keys for 'answers'):
49786
49796
  "What makes you different from competitors?"
@@ -49880,7 +49890,7 @@ async function voiceDeviceCommand(rest, args) {
49880
49890
  async function voiceCommand(api, projectId, rest, args) {
49881
49891
  const [verb, ...vargs] = rest;
49882
49892
  if (verb === "collect" && !vargs[0])
49883
- fail("Usage: tfa-cli brand voice collect <channel> [--url U | --pasted-file F|- | --account <page-id> | --max N] [--dry-run]");
49893
+ fail("Usage: tfa-cli brand voice collect <channel> [--url U | --account <page-id> | --max N] [--dry-run]");
49884
49894
  if (verb === "answers") {
49885
49895
  const answers = manualAnswersOf((await api.listVoiceSources(projectId)).sources);
49886
49896
  if (vargs.length) {
@@ -49926,12 +49936,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
49926
49936
  }
49927
49937
  if (verb === "collect") {
49928
49938
  const channel = vargs[0];
49929
- let pasted;
49930
- if (args["pasted-file"]) {
49931
- const { readFileSync: readFileSync3 } = await import("node:fs");
49932
- pasted = readFileSync3(args["pasted-file"] === "-" ? 0 : args["pasted-file"], "utf8");
49933
- }
49934
- const onDevice = !pasted && !args.url && ADAPTERS[channel];
49939
+ const onDevice = !args.url && ADAPTERS[channel];
49935
49940
  if (onDevice) {
49936
49941
  const reason = checkChannel(channel);
49937
49942
  if (reason)
@@ -49946,7 +49951,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
49946
49951
  `);
49947
49952
  return;
49948
49953
  }
49949
- const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, pasted, account: args.account });
49954
+ const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, account: args.account });
49950
49955
  process.stderr.write(`${GREEN}✅ ${channel} collected (${sources.length} source(s))${RESET}
49951
49956
  `);
49952
49957
  return;
@@ -50006,9 +50011,13 @@ Usage:
50006
50011
  tfa-cli device list
50007
50012
  tfa-cli device rename <device> <name> name: [a-zA-Z0-9][a-zA-Z0-9_]*, ≤64
50008
50013
  tfa-cli device wallpaper <device> auto|off desktop-wallpaper capture (off deletes stored images)
50014
+ tfa-cli device access <device> who you shared the device with
50015
+ tfa-cli device share <device> <email> let a teammate use it (their runs act as THEM)
50016
+ tfa-cli device unshare <device> <email>
50009
50017
 
50010
50018
  <device> is an id, name or hostname (unique partial works).
50011
- exec / reboot / update / unpair / workspace paths are UI-only from an agent shell.
50019
+ Devices shared with you are marked "shared"; only the owner can rename/share them.
50020
+ exec / reboot / update / unpair / workspace paths / share are UI-only from an agent shell.
50012
50021
  `);
50013
50022
  }
50014
50023
  async function deviceCommand(api, positionals, args) {
@@ -50019,9 +50028,11 @@ async function deviceCommand(api, positionals, args) {
50019
50028
  console.log(JSON.stringify(devices, null, 2));
50020
50029
  return;
50021
50030
  }
50022
- for (const d of devices)
50023
- process.stderr.write(`${d.status === "ONLINE" ? GREEN + "●" : DIM + "○"}${RESET} ${d.name} ${DIM}${d.deviceType} ${d.metadata?.identity?.hostname ?? ""} ${d.id}${RESET}
50031
+ for (const d of devices) {
50032
+ const sharing = !d.accessIds ? " shared with you" : d.accessIds.length ? ` shared ×${d.accessIds.length}` : "";
50033
+ process.stderr.write(`${d.status === "ONLINE" ? GREEN + "●" : DIM + "○"}${RESET} ${d.name} ${DIM}${d.deviceType} ${d.metadata?.identity?.hostname ?? ""} ${d.id}${sharing}${RESET}
50024
50034
  `);
50035
+ }
50025
50036
  return;
50026
50037
  }
50027
50038
  if (!query)
@@ -50039,6 +50050,22 @@ async function deviceCommand(api, positionals, args) {
50039
50050
  fail("Usage: tfa-cli device rename <device> <name>");
50040
50051
  await api.renameDevice(found.id, value);
50041
50052
  process.stderr.write(`${GREEN}✅ ${found.name} → ${value}${RESET}
50053
+ `);
50054
+ return;
50055
+ }
50056
+ if (sub === "access" || sub === "share" || sub === "unshare") {
50057
+ if (sub !== "access" && !value)
50058
+ fail(`Usage: tfa-cli device ${sub} <device> <email>`);
50059
+ const list = sub === "access" ? await api.getDeviceAccess(found.id) : sub === "share" ? await api.shareDevice(found.id, value) : await api.unshareDevice(found.id, value);
50060
+ if (args.json) {
50061
+ console.log(JSON.stringify(list, null, 2));
50062
+ return;
50063
+ }
50064
+ if (sub !== "access")
50065
+ process.stderr.write(`${GREEN}✅ ${found.name} ${sub === "share" ? "shared with" : "no longer shared with"} ${value}${RESET}
50066
+ `);
50067
+ process.stderr.write(list.length ? list.map((u) => ` ${u.email}${u.name ? ` ${DIM}(${u.name})${RESET}` : ""}
50068
+ `).join("") : ` ${DIM}not shared${RESET}
50042
50069
  `);
50043
50070
  return;
50044
50071
  }
@@ -75312,7 +75339,7 @@ async function main() {
75312
75339
  await deviceLogin();
75313
75340
  return;
75314
75341
  }
75315
- let apiKey = args["api-key"] || readCredential(apiUrl) || getEnv("API_TOKEN") || "";
75342
+ let apiKey = args["api-key"] || runActorToken() || readCredential(apiUrl) || getEnv("API_TOKEN") || "";
75316
75343
  if (!apiKey && positionals[0] !== "acp")
75317
75344
  apiKey = await deviceLogin();
75318
75345
  if (args["debug-dump"]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.59",
3
+ "version": "0.1.61",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "bin/todoforai-cli.js",