atris 3.56.0 → 3.56.1

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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const knownCommands = ['init', 'doctor', 'install', 'workspace', 'log', 'logs', 'wish', 'ask', 'approve', 'stop', 'ready', 'check', 'drill', 'dream', 'now', 'goal', 'wtf', 'founder', 'orb', 'radar', 'who', 'stream', 'ctop', 'launchpad', 'status', 'analytics', 'revisions', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', 'plan', 'do', 'review', 'release',
3
+ const knownCommands = ['init', 'doctor', 'install', 'workspace', 'log', 'logs', 'later', 'wish', 'ask', 'approve', 'stop', 'ready', 'check', 'drill', 'dream', 'now', 'goal', 'wtf', 'founder', 'orb', 'radar', 'who', 'stream', 'ctop', 'launchpad', 'status', 'analytics', 'revisions', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', 'plan', 'do', 'review', 'release',
4
4
  'activate', '_activate', 'agent', 'team', 'chat', 'fast', 'ax', 'console', 'serve', 'login', 'logout', 'whoami', 'switch', 'use', 'accounts', '_resolve', '_profile-email', '_switch-session', 'shell-init', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
5
5
  'clean', 'close', 'harvest', 'verify', 'recover', 'search', 'scout', 'skill', 'member', 'codex-goal', 'app', 'apps', 'learn', 'lesson', 'taste', 'teach', 'plugin', 'experiments', 'bench', 'router', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'watch', 'cloud', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync', 'youtube', 'x-search',
6
6
  'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'decide', 'agents', 'probe', 'worktree', 'land', 'caretaker', 'autoland', 'drive', 'aeo', 'slop', 'voice', 'strings', 'write', 'security-review', 'secure', 'deck', 'site', 'theme', 'card', 'reel', 'improve', 'study', 'rainmaker', 'xp', 'play', 'gm', 'game', 'x', 'recap', 'report', 'signup', 'clarity', 'interview', 'meet', 'moves', 'unknowns', 'avail', 'sync-checkout',
@@ -9,6 +9,7 @@
9
9
  // first in precedence. Precedence: explicit model -> ATRIS_RUNNER_MODEL env ->
10
10
  // ATRIS_RUNNER_PROFILE -> legacy ATRIS_CLAUDE_MODEL env -> pinned default.
11
11
  const DEFAULT_CLAUDE_RUNNER_MODEL = 'claude-opus-4-8';
12
+ const DEFAULT_FABLE_RUNNER_MODEL = 'claude-fable-5';
12
13
  const DEFAULT_CLAUDE_RUNNER_BIN = 'claude';
13
14
 
14
15
  // Canonical runner profiles. Each entry is the config an operator would pick
@@ -40,11 +41,11 @@ const RUNNER_PROFILE_DEFS = Object.freeze({
40
41
  model: '',
41
42
  commandTemplate: '{bin} --trust -p {prompt}',
42
43
  }),
43
- // Compatibility names for fleet rosters. These map to real installed
44
- // runners instead of retired or UI-only model names.
44
+ // Fable is a distinct Claude Code model, not a friendly name for whatever
45
+ // Claude model happens to be selected locally.
45
46
  fable: Object.freeze({
46
47
  bin: 'claude',
47
- model: '',
48
+ model: DEFAULT_FABLE_RUNNER_MODEL,
48
49
  commandTemplate: '',
49
50
  }),
50
51
  composer: Object.freeze({
@@ -220,6 +221,7 @@ function buildRunnerCommand({ promptFile, allowedTools, model } = {}) {
220
221
 
221
222
  module.exports = {
222
223
  DEFAULT_CLAUDE_RUNNER_MODEL,
224
+ DEFAULT_FABLE_RUNNER_MODEL,
223
225
  DEFAULT_CLAUDE_RUNNER_BIN,
224
226
  RUNNER_PROFILES,
225
227
  RUNNER_PROFILE_DEFS,
@@ -43,6 +43,50 @@ function isGenericScratchRoot(dir) {
43
43
  }
44
44
  }
45
45
 
46
+ function keyUnderScratchRoot(key, roots) {
47
+ for (const root of roots) {
48
+ if (key === root || key.startsWith(`${root}/`)) return true;
49
+ }
50
+ return false;
51
+ }
52
+
53
+ function isUnderGenericScratchRoot(dir) {
54
+ if (!dir) return false;
55
+ const roots = genericScratchRoots();
56
+ const key = pathKey(dir);
57
+ if (keyUnderScratchRoot(key, roots)) return true;
58
+ try {
59
+ return keyUnderScratchRoot(pathKey(fs.realpathSync(dir)), roots);
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ function hasWorkspaceRoom(dir) {
66
+ const root = path.resolve(dir || '.');
67
+ return fs.existsSync(path.join(root, 'atris'))
68
+ || fs.existsSync(path.join(root, '.atris', 'business.json'));
69
+ }
70
+
71
+ // An empty child under /tmp (or another generic scratch root) is not a room.
72
+ // --yes is consent to start, not a workspace unlock. A workspace that already
73
+ // lives here (dogfood) still has atris/ or a business binding.
74
+ function isUnboundScratchFolder(dir) {
75
+ if (!isUnderGenericScratchRoot(dir)) return false;
76
+ return !hasWorkspaceRoom(dir);
77
+ }
78
+
79
+ const UNBOUND_SCRATCH_MESSAGE = 'this folder is not a room';
80
+
81
+ function refuseUnboundScratch(write = console.error) {
82
+ write(UNBOUND_SCRATCH_MESSAGE);
83
+ return 2;
84
+ }
85
+
46
86
  module.exports = {
87
+ UNBOUND_SCRATCH_MESSAGE,
47
88
  isGenericScratchRoot,
89
+ isUnboundScratchFolder,
90
+ isUnderGenericScratchRoot,
91
+ refuseUnboundScratch,
48
92
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.56.0",
3
+ "version": "3.56.1",
4
4
  "description": "you say what you want in plain words. atris builds it, checks it, and shows you proof.",
5
5
  "main": "bin/atris.js",
6
6
  "bin": {
package/utils/auth.js CHANGED
@@ -546,6 +546,15 @@ async function ensureValidCredentials(apiRequestJson, options = {}) {
546
546
  return { error: 'not_logged_in' };
547
547
  }
548
548
 
549
+ const claims = decodeJwtClaims(credentials.token);
550
+ if (claims?.type === 'agent_access') {
551
+ if (typeof claims.exp === 'number' && claims.exp <= Math.floor(Date.now() / 1000)) {
552
+ return { error: 'token_invalid', detail: 'Agent token expired. Mint a new one: atris login --agent' };
553
+ }
554
+ // Server-side scope enforcement on every real request is unchanged and remains the actual security boundary.
555
+ return { credentials, user: null, source: 'agent_token' };
556
+ }
557
+
549
558
  if (credentials.refresh_token && shouldRefreshToken(credentials.token)) {
550
559
  const proactive = await performTokenRefresh(credentials, apiRequestJson);
551
560
  if (proactive.ok) {