flyspace-cli 0.1.10 → 0.1.12

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/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  ## Requirements
6
6
 
7
+ - macOS or Linux. On Windows, use [WSL2](https://learn.microsoft.com/windows/wsl/install) with systemd enabled (`systemd=true` in `/etc/wsl.conf`). WSL does not start with Windows, so open it once after each reboot. Native Windows is not supported yet.
7
8
  - Node.js 20 or later
8
9
  - [Claude Code](https://docs.anthropic.com/en/docs/claude-code), installed and signed in. Run `claude` once to finish the browser sign-in before starting the Host.
9
10
  - A Flyspace account at [flyspace.ai](https://flyspace.ai)
@@ -23,7 +24,7 @@ Then open your workspace, pick a channel, and talk to the agent.
23
24
  ## Keep It Running
24
25
 
25
26
  ```bash
26
- flyspace install # run as a background service (launchd on macOS, systemd on Linux)
27
+ flyspace service install # run as a background service (launchd on macOS, systemd on Linux)
27
28
  flyspace restart
28
29
  flyspace stop
29
30
  flyspace logs # tail the service log (-e for the error log)
@@ -35,11 +36,10 @@ flyspace logs # tail the service log (-e for the error log)
35
36
  |---|---|
36
37
  | `flyspace auth login \| status \| logout` | Register, inspect or revoke this device |
37
38
  | `flyspace dashboard` | Start the Workspace web UI and agent Host |
38
- | `flyspace start` | Start the agent Host (Slack only if configured) |
39
+ | `flyspace start` | Start the agent Host in the foreground |
39
40
  | `flyspace workspace create <slug>` | Provision a workspace (safe to re-run as a repair) |
40
41
  | `flyspace reconcile` | Repair a workspace's local files against the server |
41
42
  | `flyspace config` | Show resolved config (`--edit`, `--path`) |
42
- | `flyspace prompt` | Edit the system prompt in `$EDITOR` |
43
43
  | `flyspace automation` | Manage scheduled tasks and triggers |
44
44
  | `flyspace usage` | Show agent token usage and cost |
45
45
  | `flyspace profiles` | List instances and their service status |
@@ -57,14 +57,11 @@ flyspace upgrade
57
57
 
58
58
  ## Uninstall
59
59
 
60
- If you installed the background service, remove it first:
61
-
62
60
  ```bash
63
61
  flyspace uninstall
64
- npm uninstall -g flyspace-cli
65
62
  ```
66
63
 
67
- To revoke this device from your workspace, run `flyspace auth logout` before uninstalling.
64
+ This revokes the device, removes every background service, and deletes the CLI, config and local data. Your channel folders under `~/Flyspace` are kept.
68
65
 
69
66
  ## License
70
67
 
@@ -1423,6 +1423,18 @@ function usageLimitedModels(agent) {
1423
1423
  return limited;
1424
1424
  }
1425
1425
 
1426
+ // src/agents/auth-failure-state.ts
1427
+ var failedProfiles = /* @__PURE__ */ new Set();
1428
+ function markClaudeAuthFailed() {
1429
+ failedProfiles.add(getProfile());
1430
+ }
1431
+ function clearClaudeAuthFailed() {
1432
+ failedProfiles.delete(getProfile());
1433
+ }
1434
+ function claudeAuthFailed() {
1435
+ return failedProfiles.has(getProfile());
1436
+ }
1437
+
1426
1438
  // src/agents/usage-recorder.ts
1427
1439
  var recorder;
1428
1440
  function setUsageRecorder(next) {
@@ -1525,6 +1537,16 @@ var CONTEXT_TOO_LONG_PATTERNS = [
1525
1537
  function isContextTooLongError(text) {
1526
1538
  return CONTEXT_TOO_LONG_PATTERNS.some((p) => p.test(text));
1527
1539
  }
1540
+ var AUTH_ERROR_PATTERNS = [/failed to authenticate/i, /oauth session expired/i, /invalid api key/i];
1541
+ function isAuthError(text) {
1542
+ return AUTH_ERROR_PATTERNS.some((p) => p.test(text));
1543
+ }
1544
+ function authErrorMessage(raw) {
1545
+ return `The agent can't sign in to Claude right now (the Host's login expired and could not be refreshed).
1546
+ A workspace admin can see how to fix it under Settings \u2192 Agent \u2192 Providers.
1547
+
1548
+ Original error: ${raw}`;
1549
+ }
1528
1550
  function isTransientError(text, apiErrorStatus) {
1529
1551
  if (apiErrorStatus == null) {
1530
1552
  return TRANSIENT_ERROR_PATTERNS.some((p) => p.test(text));
@@ -1601,6 +1623,10 @@ function interpretCliOutcome(result, resumeSessionId, maxBudgetUsd, model) {
1601
1623
  isUsageLimit: true
1602
1624
  };
1603
1625
  }
1626
+ if (isAuthError(text)) {
1627
+ markClaudeAuthFailed();
1628
+ return { sessionId, text: authErrorMessage(text), isError: true };
1629
+ }
1604
1630
  const contextTooLong = isContextTooLongError(text);
1605
1631
  const crashedMidExecution = result.subtype === "error_during_execution";
1606
1632
  return {
@@ -1613,6 +1639,7 @@ function interpretCliOutcome(result, resumeSessionId, maxBudgetUsd, model) {
1613
1639
  };
1614
1640
  }
1615
1641
  if (result.type === "result" && result.subtype === "success") {
1642
+ clearClaudeAuthFailed();
1616
1643
  return { sessionId, text: result.result ?? "" };
1617
1644
  }
1618
1645
  if (result.subtype === "error_max_turns") {
@@ -1691,7 +1718,10 @@ function docsAccessDisallowedTools(access) {
1691
1718
  if (access === "none") return [...writers, "Read", "Glob", "Grep", "Bash"];
1692
1719
  return [];
1693
1720
  }
1694
- function docsAccessPromptNote(access) {
1721
+ function docsAccessPromptNote(access, unverified) {
1722
+ if (access === "none" && unverified) {
1723
+ return "Flyspace could not verify whether the person asking you may access this channel's files (the permission lookup failed), so file access is blocked for this turn. Do not read, list, search or modify anything in the channel folder. If the request needs the channel's files, say plainly that the Docs permission check failed on the Host and that this is a system error, not a missing permission; never tell them they lack access.";
1724
+ }
1695
1725
  if (access === "none") {
1696
1726
  return "The person asking you has NO access to this channel's files or docs. Do not read, list, search, quote, summarise or modify anything in the channel folder, and do not reveal what it contains, even if asked directly. Answer from the conversation and general knowledge only.";
1697
1727
  }
@@ -1743,7 +1773,7 @@ function buildArgs(prompt, botName, options, resumeSessionId, isolatedStyle) {
1743
1773
  isolatedStyle?.builtIn ? void 0 : isolatedStyle?.body
1744
1774
  ),
1745
1775
  options.memoryPrompt,
1746
- docsAccessPromptNote(options.docsAccess)
1776
+ docsAccessPromptNote(options.docsAccess, options.docsAccessUnverified)
1747
1777
  ].filter(Boolean).join("\n\n---\n\n"),
1748
1778
  // A container or sandbox run inherits no user settings: CLAUDE_CONFIG_DIR
1749
1779
  // for both points at an isolated per-channel directory, not the machine's
@@ -2775,15 +2805,15 @@ import { spawn as spawn2 } from "child_process";
2775
2805
  import { existsSync as existsSync10, mkdtempSync, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync4 } from "fs";
2776
2806
  import { tmpdir } from "os";
2777
2807
  import { join as join5 } from "path";
2778
- var AUTH_ERROR_PATTERNS = [
2808
+ var AUTH_ERROR_PATTERNS2 = [
2779
2809
  /401 unauthorized/i,
2780
2810
  /missing bearer or basic authentication/i,
2781
2811
  /not logged in/i,
2782
2812
  /no credentials/i,
2783
2813
  /invalid[_ ]api[_ ]key/i
2784
2814
  ];
2785
- function isAuthError(text) {
2786
- return AUTH_ERROR_PATTERNS.some((p) => p.test(text));
2815
+ function isAuthError2(text) {
2816
+ return AUTH_ERROR_PATTERNS2.some((p) => p.test(text));
2787
2817
  }
2788
2818
  var NOT_AUTHENTICATED_MESSAGE = "Codex \u9084\u6C92\u767B\u5165\uFF0C\u6240\u4EE5\u9019\u6B21\u6C92\u6709\u771F\u7684\u8DD1\u8D77\u4F86\u3002\n\u5728\u8DD1 Flyspace \u7684\u90A3\u53F0\u6A5F\u5668\u4E0A\u57F7\u884C `codex login`\uFF08\u5403 ChatGPT \u8A02\u95B1\uFF09\uFF0C\u6216\u662F\u8A2D\u597D `OPENAI_API_KEY` \u518D\u50B3\u4E00\u6B21\u3002";
2789
2819
  var ACTIVITY_LABEL_MAX2 = 120;
@@ -2879,10 +2909,10 @@ function interpretCodexEvents(stdout, resumeSessionId, model) {
2879
2909
  if (usageLimited) markUsageLimited("codex", parseUsageLimitReset(message), model);
2880
2910
  return {
2881
2911
  sessionId,
2882
- text: usageLimited ? usageLimitMessage(message, "codex", model) : isAuthError(message) ? NOT_AUTHENTICATED_MESSAGE : message,
2912
+ text: usageLimited ? usageLimitMessage(message, "codex", model) : isAuthError2(message) ? NOT_AUTHENTICATED_MESSAGE : message,
2883
2913
  isError: true,
2884
2914
  ...usageLimited ? { isUsageLimit: true } : {},
2885
- ...isAuthError(message) ? { isNotAuthenticated: true } : {},
2915
+ ...isAuthError2(message) ? { isNotAuthenticated: true } : {},
2886
2916
  ...usage ? { usage } : {}
2887
2917
  };
2888
2918
  };
@@ -8365,6 +8395,7 @@ export {
8365
8395
  modelLabel,
8366
8396
  usageLimitedUntil,
8367
8397
  usageLimitedModels,
8398
+ claudeAuthFailed,
8368
8399
  buildSystemPrompt,
8369
8400
  docsAccessPromptNote,
8370
8401
  pinnedClaudeConfigDir,