atris 3.63.0 → 3.64.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.
- package/atris/skills/commerce/SKILL.md +24 -0
- package/atris/skills/x-search/SKILL.md +3 -1
- package/ax +4 -3
- package/bin/atris.js +11 -2
- package/cli/atris_code.py +25 -1
- package/commands/auth.js +79 -10
- package/commands/autoland.js +11 -1
- package/commands/browse.js +2 -2
- package/commands/business.js +3 -3
- package/commands/buy.js +3 -3
- package/commands/deck.js +2 -2
- package/commands/engine.js +122 -13
- package/commands/feedback.js +2 -2
- package/commands/fork.js +2 -2
- package/commands/gm.js +2 -1
- package/commands/improve.js +142 -8
- package/commands/integrations.js +4 -3
- package/commands/loops.js +2 -1
- package/commands/member.js +47 -9
- package/commands/mission.js +1 -1
- package/commands/pack-craft.js +2 -2
- package/commands/publish.js +2 -2
- package/commands/radar.js +3 -2
- package/commands/router.js +2 -2
- package/commands/task.js +84 -2
- package/commands/transactions.js +3 -3
- package/commands/truth.js +2 -2
- package/commands/x-search.js +81 -10
- package/commands/youtube.js +2 -1
- package/lib/autoland.js +3 -2
- package/lib/ax-auto-lane.js +2 -2
- package/lib/brief-rules.js +11 -0
- package/lib/chat-log-scan.js +2 -1
- package/lib/ci-runner.js +3 -2
- package/lib/codex-flight.js +2 -1
- package/lib/commerce.js +17 -2
- package/lib/deck-history.js +2 -2
- package/lib/deck-review.js +3 -3
- package/lib/design-api.js +2 -2
- package/lib/engine-registry.js +85 -18
- package/lib/first-minute.js +2 -2
- package/lib/fleet.js +8 -36
- package/lib/git-spawn.js +5 -2
- package/lib/ignored-cache.js +51 -0
- package/lib/manifest.js +2 -2
- package/lib/map-refs.js +70 -4
- package/lib/pack-capabilities.js +2 -1
- package/lib/permission-grants.js +2 -2
- package/lib/pulse.js +5 -5
- package/lib/roster-runs.js +13 -2
- package/lib/runner-command.js +22 -6
- package/lib/sandbox-pids.js +113 -0
- package/lib/secret-gateway.js +3 -13
- package/lib/state-home.js +106 -0
- package/lib/task-db.js +4 -3
- package/lib/task-prune.js +462 -0
- package/lib/x-post-check.js +497 -0
- package/package.json +1 -1
- package/scripts/det/data/xsearch-answer.json +24 -0
- package/scripts/det/data/xsearch-live-2026-09-29.json +93 -0
- package/scripts/det/xsearch-bench.js +307 -0
- package/scripts/det/ytnotes +61 -15
- package/scripts/det/ytrail-bench.js +2 -1
- package/utils/auth.js +3 -3
- package/utils/update-check.js +2 -2
|
@@ -13,6 +13,30 @@ You can quote and ask. Only the human can pay. Every dollar leaves only after
|
|
|
13
13
|
the person acts themselves: a tap in their Link app, a payment page they
|
|
14
14
|
fill in, or a text from their own phone.
|
|
15
15
|
|
|
16
|
+
## Get a key
|
|
17
|
+
|
|
18
|
+
The person mints a commerce key from their own signed-in CLI. It can ask for
|
|
19
|
+
prices and start their approval. It can never pay.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
atris login --agent --commerce --commerce-max-usd 150
|
|
23
|
+
# scopes: commerce:quote, transactions:read
|
|
24
|
+
# largest single quote: $150 (1 to 500, default 200)
|
|
25
|
+
# this key can ask for prices and start your approval, it can never pay.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- Same machine: nothing else to do. The key is stored in its own slot
|
|
29
|
+
(`commerce_agent_token` in `~/.atris/credentials.json`), and `atris buy` and
|
|
30
|
+
`atris transactions` use it. Minting an x-search or YouTube key later does not
|
|
31
|
+
replace it.
|
|
32
|
+
- Agent on another machine: add `--print-key` to show the key once. The person
|
|
33
|
+
hands it to the agent as `ATRIS_TOKEN` (CLI) or `ATRIS_API_KEY` (curl below).
|
|
34
|
+
Never paste it into a chat.
|
|
35
|
+
- Narrower key: `--scopes commerce:quote` (quote, approve, status) or
|
|
36
|
+
`--scopes transactions:read` (history only).
|
|
37
|
+
- Quotes over the key's cap are refused, and each key gets 50 quotes a day (UTC). A developer key (`atris api-key`) or the
|
|
38
|
+
person's own login also works, without those per-key limits.
|
|
39
|
+
|
|
16
40
|
## The loop
|
|
17
41
|
|
|
18
42
|
1. Quote. `atris buy quote --kind <kind> [fields]`.
|
|
@@ -49,7 +49,9 @@ atris x-search person --name "Leah Bonvissuto" --handle leahbon
|
|
|
49
49
|
atris x-search --help
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Every cited post is looked up on X for free (the public embed endpoint, 5s cap), whatever layout the answer uses. A cited post whose words appear in the answer is checked: its `https://x.com/<handle>/status/<id> (checked)` link prints under that post, or in a `Checked posts:` list when the answer is a table or prose. A quote of 40+ characters that matches no real post says `(could not find this post on X, treat as unverified)`; if X could not be reached it says so and is never called fake. Citations no post uses print as `Other sources:`. The last line is `posts: N checked, N unverified, N unknown`. `--no-check` skips the lookup. Bench it with `atris x-search bench --free` (0 credits) or `--quick` (5 credits).
|
|
53
|
+
|
|
54
|
+
Add `--json` for the raw API payload (plus a `checks` list). Curl below is the raw API for debugging only.
|
|
53
55
|
|
|
54
56
|
## Bootstrap (ALWAYS Run First)
|
|
55
57
|
|
package/ax
CHANGED
|
@@ -5,6 +5,7 @@ const http = require('http');
|
|
|
5
5
|
const https = require('https');
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const path = require('path');
|
|
8
|
+
const { stateHome } = require('./lib/state-home');
|
|
8
9
|
const readline = require('readline');
|
|
9
10
|
const crypto = require('crypto');
|
|
10
11
|
const { Readable } = require('stream');
|
|
@@ -230,7 +231,7 @@ function createRunLogger({ cwd = process.cwd(), mode = 'pro', kind = 'play', out
|
|
|
230
231
|
|
|
231
232
|
const baseDir = fs.existsSync(path.join(cwd, 'atris'))
|
|
232
233
|
? path.join(cwd, 'atris', 'runs')
|
|
233
|
-
: path.join(
|
|
234
|
+
: path.join(stateHome(), '.atris', 'runs');
|
|
234
235
|
fs.mkdirSync(baseDir, { recursive: true });
|
|
235
236
|
const logPath = path.join(baseDir, `ax-${kind}-${timestampForFile()}.log`);
|
|
236
237
|
fs.appendFileSync(logPath, [
|
|
@@ -995,7 +996,7 @@ function normalizeMode(mode) {
|
|
|
995
996
|
function autoPickLogPath(options = {}) {
|
|
996
997
|
return options.pickLogPath
|
|
997
998
|
|| process.env.ATRIS_ROUTER_PICKS_PATH
|
|
998
|
-
|| path.join(
|
|
999
|
+
|| path.join(stateHome(), '.atris', 'ax-auto-picks.jsonl');
|
|
999
1000
|
}
|
|
1000
1001
|
|
|
1001
1002
|
function autoMessageWords(message) {
|
|
@@ -2328,7 +2329,7 @@ function formatTaskPreviewRows(receipt, options = {}) {
|
|
|
2328
2329
|
}
|
|
2329
2330
|
|
|
2330
2331
|
function approvalStorePath(options = {}) {
|
|
2331
|
-
return options.storePath || process.env.AX_APPROVAL_STORE || path.join(
|
|
2332
|
+
return options.storePath || process.env.AX_APPROVAL_STORE || path.join(stateHome(), '.atris', 'ax-approvals.json');
|
|
2332
2333
|
}
|
|
2333
2334
|
|
|
2334
2335
|
function approvalMaxAgeMs(options = {}) {
|
package/bin/atris.js
CHANGED
|
@@ -1011,6 +1011,9 @@ function showAuthHelp(commandName) {
|
|
|
1011
1011
|
console.log(' --agent Mint a scoped agent token from the stored login.');
|
|
1012
1012
|
console.log(' --scopes <list> Comma-separated scopes (default: x-search,youtube).');
|
|
1013
1013
|
console.log(' --daily-credit-cap <n> Daily credit cap (default: 50).');
|
|
1014
|
+
console.log(' --commerce Commerce key: scopes commerce:quote,transactions:read. Can quote, never pay.');
|
|
1015
|
+
console.log(' --commerce-max-usd <n> Largest single quote a commerce key may ask for, 1 to 500 (default: 200).');
|
|
1016
|
+
console.log(' --print-key Print the minted key once, for an agent on another machine.');
|
|
1014
1017
|
} else if (commandName === 'switch') {
|
|
1015
1018
|
console.log(' --global, -g Switch the account for all terminals.');
|
|
1016
1019
|
}
|
|
@@ -1856,9 +1859,15 @@ function showWelcomeVisualization() {
|
|
|
1856
1859
|
// Every new chat sees which tool and model does each job, from the same
|
|
1857
1860
|
// resolver as atris engine roster. A roster that cannot be read drops the
|
|
1858
1861
|
// line; it never stops the boot.
|
|
1862
|
+
// A roster worker that ends within seven days gets one line, so the job
|
|
1863
|
+
// does not quietly fall to its backup when the date passes.
|
|
1859
1864
|
try {
|
|
1860
|
-
const
|
|
1865
|
+
const engineView = require('../commands/engine');
|
|
1866
|
+
const jobs = engineView.jobRosterView(cwd);
|
|
1867
|
+
const teamLine = require('../lib/team-lineup').bootTeamLine(cwd, { width: 80 - 11, jobs });
|
|
1861
1868
|
if (teamLine) console.log(row('team', teamLine));
|
|
1869
|
+
const endsLine = engineView.bootExpiringLine(engineView.expiringWorkers(jobs));
|
|
1870
|
+
if (endsLine) console.log(row('roster', endsLine));
|
|
1862
1871
|
} catch {
|
|
1863
1872
|
// Silent: the lineup is a hint, not a gate.
|
|
1864
1873
|
}
|
|
@@ -2981,7 +2990,7 @@ if (command === 'guide') {
|
|
|
2981
2990
|
const subcommand = process.argv[3];
|
|
2982
2991
|
const args = process.argv.slice(4);
|
|
2983
2992
|
Promise.resolve(require('../commands/member').memberCommand(subcommand, ...args))
|
|
2984
|
-
.then(() => process.exit(
|
|
2993
|
+
.then(() => process.exit(process.exitCode || 0))
|
|
2985
2994
|
.catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
|
|
2986
2995
|
} else if (command === 'app') {
|
|
2987
2996
|
const subcommand = process.argv[3];
|
package/cli/atris_code.py
CHANGED
|
@@ -12,11 +12,13 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
import argparse
|
|
14
14
|
import asyncio
|
|
15
|
+
import getpass
|
|
15
16
|
import json
|
|
16
17
|
import logging
|
|
17
18
|
import os
|
|
18
19
|
import subprocess
|
|
19
20
|
import sys
|
|
21
|
+
import tempfile
|
|
20
22
|
import time
|
|
21
23
|
from dataclasses import dataclass
|
|
22
24
|
from pathlib import Path
|
|
@@ -36,7 +38,29 @@ RED = "\033[91m"
|
|
|
36
38
|
|
|
37
39
|
PACKAGE_ROOT = Path(__file__).resolve().parent.parent
|
|
38
40
|
TEAM_DIR = PACKAGE_ROOT / "atris" / "team"
|
|
39
|
-
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _same_dir(a: str, b: str) -> bool:
|
|
44
|
+
try:
|
|
45
|
+
return os.path.samefile(a, b)
|
|
46
|
+
except OSError:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _state_home() -> Path:
|
|
51
|
+
"""Mirror lib/state-home.js: a test run never writes the real ~/.atris."""
|
|
52
|
+
env, home = os.environ, Path.home()
|
|
53
|
+
under_test = env.get("NODE_TEST_CONTEXT") or env.get("ATRIS_TEST_STATE_DIR")
|
|
54
|
+
if env.get("ATRIS_TEST_REAL_HOME") == "1" or not under_test:
|
|
55
|
+
return home
|
|
56
|
+
real = [os.path.expanduser("~" + getpass.getuser()), env.get("ATRIS_TEST_PROTECTED_HOME", "")]
|
|
57
|
+
if not any(r and _same_dir(str(home), r) for r in real):
|
|
58
|
+
return home
|
|
59
|
+
user = os.getuid() if hasattr(os, "getuid") else getpass.getuser()
|
|
60
|
+
return Path(env.get("ATRIS_TEST_STATE_DIR") or os.path.join(tempfile.gettempdir(), f"atris-test-home-{user}"))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
STATE_DIR = _state_home() / ".atris"
|
|
40
64
|
SESSION_STATE_FILE = STATE_DIR / "computer_sessions.json"
|
|
41
65
|
AUDIT_LOG_FILE = STATE_DIR / "computer_audit.jsonl"
|
|
42
66
|
RESUME_MAX_AGE_SECONDS = 6 * 60 * 60
|
package/commands/auth.js
CHANGED
|
@@ -8,6 +8,13 @@ const path = require('path');
|
|
|
8
8
|
const AGENT_TOKEN_PATH = '/auth/agent-token';
|
|
9
9
|
const DEFAULT_AGENT_SCOPES = ['x-search', 'youtube'];
|
|
10
10
|
const DEFAULT_DAILY_CREDIT_CAP = 50;
|
|
11
|
+
// Commerce keys can ask for prices and start the owner's approval. They can
|
|
12
|
+
// never pay: every dollar still needs the owner's own tap or text. They live
|
|
13
|
+
// in their own slot so an x-search or YouTube mint never overwrites them.
|
|
14
|
+
const COMMERCE_QUOTE_SCOPE = 'commerce:quote';
|
|
15
|
+
const COMMERCE_SCOPES = [COMMERCE_QUOTE_SCOPE, 'transactions:read'];
|
|
16
|
+
const COMMERCE_MAX_USD_LIMIT = 500;
|
|
17
|
+
const COMMERCE_NEVER_PAYS = 'this key can ask for prices and start your approval, it can never pay.';
|
|
11
18
|
const NO_STORED_JWT_MESSAGE = 'not signed in. run atris login first.';
|
|
12
19
|
|
|
13
20
|
function firstNonEmptyString(...values) {
|
|
@@ -23,8 +30,12 @@ function wantsAgentToken(args = []) {
|
|
|
23
30
|
return first === 'agent-token';
|
|
24
31
|
}
|
|
25
32
|
|
|
26
|
-
function
|
|
27
|
-
|
|
33
|
+
function isCommerceKey(scopes = []) {
|
|
34
|
+
return Array.isArray(scopes) && scopes.some((scope) => COMMERCE_SCOPES.includes(String(scope)));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parseScopeList(raw, fallback = DEFAULT_AGENT_SCOPES) {
|
|
38
|
+
if (!raw) return [...fallback];
|
|
28
39
|
const scopes = String(raw).split(',').map((part) => part.trim()).filter(Boolean);
|
|
29
40
|
if (scopes.length === 0) {
|
|
30
41
|
throw new Error('scopes must list at least one value');
|
|
@@ -43,11 +54,29 @@ function parseDailyCreditCap(args) {
|
|
|
43
54
|
return value;
|
|
44
55
|
}
|
|
45
56
|
|
|
57
|
+
function parseCommerceMaxUsd(args, scopes) {
|
|
58
|
+
const present = args.some((arg) => arg === '--commerce-max-usd' || String(arg).startsWith('--commerce-max-usd='));
|
|
59
|
+
if (!present) return null;
|
|
60
|
+
const raw = String(readFlag(args, '--commerce-max-usd', '')).replace(/^\$/, '');
|
|
61
|
+
const value = Number(raw);
|
|
62
|
+
if (!Number.isInteger(value) || value < 1 || value > COMMERCE_MAX_USD_LIMIT) {
|
|
63
|
+
throw new Error(`commerce max must be a whole dollar amount from 1 to ${COMMERCE_MAX_USD_LIMIT}`);
|
|
64
|
+
}
|
|
65
|
+
if (!scopes.includes(COMMERCE_QUOTE_SCOPE)) {
|
|
66
|
+
throw new Error('--commerce-max-usd needs the commerce:quote scope (try --commerce)');
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
46
71
|
function parseAgentTokenArgs(args = []) {
|
|
72
|
+
const commerce = hasFlag(args, '--commerce');
|
|
73
|
+
const scopes = parseScopeList(readFlag(args, '--scopes', ''), commerce ? COMMERCE_SCOPES : DEFAULT_AGENT_SCOPES);
|
|
47
74
|
return {
|
|
48
75
|
agent: wantsAgentToken(args),
|
|
49
|
-
scopes
|
|
76
|
+
scopes,
|
|
50
77
|
dailyCreditCap: parseDailyCreditCap(args),
|
|
78
|
+
commerceMaxUsd: parseCommerceMaxUsd(args, scopes),
|
|
79
|
+
printKey: hasFlag(args, '--print-key'),
|
|
51
80
|
json: wantsJson(args),
|
|
52
81
|
help: hasFlag(args, '--help') || hasFlag(args, '-h') || args[0] === 'help',
|
|
53
82
|
};
|
|
@@ -82,7 +111,14 @@ function extractAgentTokenMeta(data, requested, token) {
|
|
|
82
111
|
const dailyCreditCap = Number.isFinite(Number(rawCap)) ? Number(rawCap) : requested.dailyCreditCap;
|
|
83
112
|
const expiresAt = expiryFromValue(payload.expires_at ?? payload.expiry ?? payload.expires ?? payload.exp)
|
|
84
113
|
|| expiryFromValue(getTokenExpiryEpochSeconds(token));
|
|
85
|
-
|
|
114
|
+
const rawMax = payload.commerce_max_quote_usd;
|
|
115
|
+
const commerceMaxUsd = Number.isFinite(Number(rawMax)) && rawMax !== null && rawMax !== undefined
|
|
116
|
+
? Number(rawMax)
|
|
117
|
+
: (requested.commerceMaxUsd || null);
|
|
118
|
+
const commerceDailyQuotes = Number.isFinite(Number(payload.commerce_daily_quotes)) && payload.commerce_daily_quotes != null
|
|
119
|
+
? Number(payload.commerce_daily_quotes)
|
|
120
|
+
: null;
|
|
121
|
+
return { scopes, dailyCreditCap, expiresAt, commerceMaxUsd, commerceDailyQuotes };
|
|
86
122
|
}
|
|
87
123
|
|
|
88
124
|
function isAgentAccessToken(token) {
|
|
@@ -116,12 +152,22 @@ function persistMintedAgentToken(credentials, token, extras = {}) {
|
|
|
116
152
|
if (isAgentAccessToken(credentials.token)) {
|
|
117
153
|
throw new Error('Refusing to save a scoped agent token as the login token; keep it under agent_token');
|
|
118
154
|
}
|
|
155
|
+
const slot = isCommerceKey(extras.scopes)
|
|
156
|
+
? {
|
|
157
|
+
commerce_agent_token: token,
|
|
158
|
+
commerce_agent_token_scopes: extras.scopes || [],
|
|
159
|
+
commerce_agent_token_expires_at: extras.expiresAt || null,
|
|
160
|
+
commerce_max_quote_usd: extras.commerceMaxUsd || null,
|
|
161
|
+
}
|
|
162
|
+
: {
|
|
163
|
+
agent_token: token,
|
|
164
|
+
agent_token_scopes: extras.scopes || [],
|
|
165
|
+
agent_token_expires_at: extras.expiresAt || null,
|
|
166
|
+
};
|
|
119
167
|
const next = {
|
|
120
168
|
...credentials,
|
|
121
169
|
refresh_token: extras.refresh_token || credentials.refresh_token || null,
|
|
122
|
-
|
|
123
|
-
agent_token_scopes: extras.scopes || [],
|
|
124
|
-
agent_token_expires_at: extras.expiresAt || null,
|
|
170
|
+
...slot,
|
|
125
171
|
};
|
|
126
172
|
if (credentials.source_profile) {
|
|
127
173
|
saveProfile(credentials.source_profile, next);
|
|
@@ -132,10 +178,14 @@ function persistMintedAgentToken(credentials, token, extras = {}) {
|
|
|
132
178
|
}
|
|
133
179
|
|
|
134
180
|
function printAgentTokenMint(meta, output) {
|
|
135
|
-
|
|
181
|
+
const commerce = isCommerceKey(meta.scopes);
|
|
182
|
+
output(commerce ? 'minted commerce agent key' : 'minted scoped agent token');
|
|
136
183
|
output(`scopes: ${meta.scopes.join(', ')}`);
|
|
184
|
+
if (meta.commerceMaxUsd) output(`largest single quote: $${meta.commerceMaxUsd}`);
|
|
185
|
+
if (meta.commerceDailyQuotes) output(`quotes per day: ${meta.commerceDailyQuotes}`);
|
|
137
186
|
output(`daily credit cap: ${meta.dailyCreditCap}`);
|
|
138
187
|
if (meta.expiresAt) output(`expires: ${meta.expiresAt}`);
|
|
188
|
+
if (commerce) output(COMMERCE_NEVER_PAYS);
|
|
139
189
|
}
|
|
140
190
|
|
|
141
191
|
function redactSecret(text, secret) {
|
|
@@ -175,10 +225,16 @@ async function mintScopedAgentToken(requested = {}, deps = {}) {
|
|
|
175
225
|
return { ok: false, code: 'not_logged_in', error: NO_STORED_JWT_MESSAGE };
|
|
176
226
|
}
|
|
177
227
|
|
|
228
|
+
const commerceMaxUsd = Number.isInteger(requested.commerceMaxUsd) ? requested.commerceMaxUsd : null;
|
|
229
|
+
if (commerceMaxUsd !== null && !scopes.includes(COMMERCE_QUOTE_SCOPE)) {
|
|
230
|
+
return { ok: false, code: 'invalid_scopes', error: '--commerce-max-usd needs the commerce:quote scope' };
|
|
231
|
+
}
|
|
232
|
+
|
|
178
233
|
const body = {
|
|
179
234
|
scopes,
|
|
180
235
|
daily_credit_cap: dailyCreditCap,
|
|
181
236
|
};
|
|
237
|
+
if (commerceMaxUsd !== null) body.commerce_max_quote_usd = commerceMaxUsd;
|
|
182
238
|
let authToken = isAgentAccessToken(accessToken) ? refreshToken : (accessToken || refreshToken);
|
|
183
239
|
let result = await postAgentToken(api, authToken, body);
|
|
184
240
|
if (!result.ok && result.status === 401 && refreshToken && authToken !== refreshToken) {
|
|
@@ -201,7 +257,7 @@ async function mintScopedAgentToken(requested = {}, deps = {}) {
|
|
|
201
257
|
return { ok: false, code: 'missing_token', error: 'backend did not return an agent token' };
|
|
202
258
|
}
|
|
203
259
|
|
|
204
|
-
const meta = extractAgentTokenMeta(result.data, { scopes, dailyCreditCap }, minted);
|
|
260
|
+
const meta = extractAgentTokenMeta(result.data, { scopes, dailyCreditCap, commerceMaxUsd }, minted);
|
|
205
261
|
persist(credentials, minted, {
|
|
206
262
|
...meta,
|
|
207
263
|
refresh_token: firstNonEmptyString(result.data && result.data.refresh_token) || refreshToken,
|
|
@@ -212,7 +268,7 @@ async function mintScopedAgentToken(requested = {}, deps = {}) {
|
|
|
212
268
|
ok: true,
|
|
213
269
|
token: minted,
|
|
214
270
|
meta,
|
|
215
|
-
storedIn: credentials.source_profile ? `profile ${credentials.source_profile}
|
|
271
|
+
storedIn: `${credentials.source_profile ? `profile ${credentials.source_profile}` : '~/.atris/credentials.json'}, ${isCommerceKey(meta.scopes) ? 'commerce_agent_token' : 'agent_token'}`,
|
|
216
272
|
};
|
|
217
273
|
}
|
|
218
274
|
|
|
@@ -276,6 +332,7 @@ async function mintAgentToken(args = [], deps = {}) {
|
|
|
276
332
|
const minted = await mintScopedAgentToken({
|
|
277
333
|
scopes: options.scopes,
|
|
278
334
|
dailyCreditCap: options.dailyCreditCap,
|
|
335
|
+
commerceMaxUsd: options.commerceMaxUsd,
|
|
279
336
|
}, deps);
|
|
280
337
|
|
|
281
338
|
if (!minted.ok) {
|
|
@@ -302,12 +359,24 @@ async function mintAgentToken(args = [], deps = {}) {
|
|
|
302
359
|
scopes: minted.meta.scopes,
|
|
303
360
|
daily_credit_cap: minted.meta.dailyCreditCap,
|
|
304
361
|
expires_at: minted.meta.expiresAt,
|
|
362
|
+
...(isCommerceKey(minted.meta.scopes) ? {
|
|
363
|
+
commerce_max_quote_usd: minted.meta.commerceMaxUsd,
|
|
364
|
+
commerce_daily_quotes: minted.meta.commerceDailyQuotes,
|
|
365
|
+
can_pay: false,
|
|
366
|
+
note: COMMERCE_NEVER_PAYS,
|
|
367
|
+
} : {}),
|
|
368
|
+
...(options.printKey ? { key: minted.token } : {}),
|
|
305
369
|
}, null, 2));
|
|
306
370
|
return 0;
|
|
307
371
|
}
|
|
308
372
|
|
|
309
373
|
printAgentTokenMint(minted.meta, output);
|
|
310
374
|
output(`stored in ${minted.storedIn}`);
|
|
375
|
+
if (options.printKey) {
|
|
376
|
+
// Only on request: an outside agent needs the raw key once.
|
|
377
|
+
output('key (shown once, give it to the agent as ATRIS_TOKEN, do not paste it into chat):');
|
|
378
|
+
output(minted.token);
|
|
379
|
+
}
|
|
311
380
|
return 0;
|
|
312
381
|
}
|
|
313
382
|
|
package/commands/autoland.js
CHANGED
|
@@ -135,7 +135,16 @@ function runOwnCli(root, cliArgs, timeoutMs = 300000) {
|
|
|
135
135
|
// target root or a global `atris` breaks whenever the tick runs a project
|
|
136
136
|
// that isn't the CLI repo on a machine without a global install (CI, cron).
|
|
137
137
|
const bin = path.resolve(__dirname, '..', 'bin', 'atris.js');
|
|
138
|
-
|
|
138
|
+
return spawnCapture([bin, ...cliArgs], { cwd: root, timeoutMs });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// spawnSync keeps 1 MB of stdout by default and kills the child past it
|
|
142
|
+
// (ENOBUFS). A certify-and-land sweep over 150+ review rows prints more than
|
|
143
|
+
// that, so the accept step read as unreadable on most ticks. Allow 64 MB.
|
|
144
|
+
const OWN_CLI_MAX_BUFFER = 64 * 1024 * 1024;
|
|
145
|
+
|
|
146
|
+
function spawnCapture(nodeArgs, { cwd, timeoutMs = 300000 } = {}) {
|
|
147
|
+
const result = spawnSync(process.execPath, nodeArgs, { cwd, encoding: 'utf8', timeout: timeoutMs, maxBuffer: OWN_CLI_MAX_BUFFER });
|
|
139
148
|
return {
|
|
140
149
|
status: result.status,
|
|
141
150
|
stdout: String(result.stdout || ''),
|
|
@@ -1139,6 +1148,7 @@ module.exports = {
|
|
|
1139
1148
|
digestNextMoves,
|
|
1140
1149
|
acceptSweepTimeoutMs,
|
|
1141
1150
|
describeAcceptKillCause,
|
|
1151
|
+
spawnCapture,
|
|
1142
1152
|
digestStoryRows,
|
|
1143
1153
|
digestTickStatus,
|
|
1144
1154
|
engineAnswerValidationLine,
|
package/commands/browse.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const
|
|
3
|
+
const { stateHome } = require('../lib/state-home');
|
|
4
4
|
const { loadCredentials } = require('../utils/auth');
|
|
5
5
|
const { apiRequestJson } = require('../utils/api');
|
|
6
6
|
|
|
@@ -34,7 +34,7 @@ async function browseAtris() {
|
|
|
34
34
|
if (!result.ok) {
|
|
35
35
|
// API failed, try local templates
|
|
36
36
|
console.log(' API unavailable, checking local templates...');
|
|
37
|
-
const localDir = path.join(
|
|
37
|
+
const localDir = path.join(stateHome(), '.atris', 'templates');
|
|
38
38
|
if (!fs.existsSync(localDir)) {
|
|
39
39
|
console.error(' No templates found locally or from API.');
|
|
40
40
|
process.exit(1);
|
package/commands/business.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { stateHome } = require('../lib/state-home');
|
|
3
4
|
const os = require('os');
|
|
4
5
|
const { loadCredentials } = require('../utils/auth');
|
|
5
6
|
const { apiRequestJson } = require('../utils/api');
|
|
@@ -18,8 +19,7 @@ function businessWorkspaceBase() {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
function getBusinessConfigPath() {
|
|
21
|
-
const
|
|
22
|
-
const dir = path.join(home, '.atris');
|
|
22
|
+
const dir = path.join(stateHome(), '.atris');
|
|
23
23
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
24
24
|
return path.join(dir, 'businesses.json');
|
|
25
25
|
}
|
|
@@ -3139,7 +3139,7 @@ async function connectService(connector, ...flags) {
|
|
|
3139
3139
|
}
|
|
3140
3140
|
|
|
3141
3141
|
// Create local secrets directory
|
|
3142
|
-
const secretsDir = path.join(
|
|
3142
|
+
const secretsDir = path.join(stateHome(), '.atris', 'secrets', connector);
|
|
3143
3143
|
if (!fs.existsSync(secretsDir)) {
|
|
3144
3144
|
fs.mkdirSync(secretsDir, { recursive: true });
|
|
3145
3145
|
console.log(`\n Created secrets dir: ${secretsDir}/`);
|
package/commands/buy.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
const { apiRequestJson } = require('../utils/api');
|
|
14
14
|
const { loadCredentials } = require('../utils/auth');
|
|
15
|
-
const { NOT_LOGGED_IN,
|
|
16
|
-
const { parseFlags, formatCents, untilText, errorFrom } = require('../lib/commerce');
|
|
15
|
+
const { NOT_LOGGED_IN, oneLine, printResult } = require('../lib/developer-api');
|
|
16
|
+
const { parseFlags, formatCents, untilText, errorFrom, commerceToken } = require('../lib/commerce');
|
|
17
17
|
|
|
18
18
|
const KINDS = ['ticket', 'shop', 'trade', 'flight'];
|
|
19
19
|
const NOTHING_CHARGED = 'Nothing is charged until the person approves it themselves.';
|
|
@@ -218,7 +218,7 @@ async function buyCommand(args = [], deps = {}) {
|
|
|
218
218
|
|
|
219
219
|
const load = deps.loadCredentials || loadCredentials;
|
|
220
220
|
const request = deps.apiRequestJson || apiRequestJson;
|
|
221
|
-
const token =
|
|
221
|
+
const token = commerceToken(load(), 'commerce:quote', deps.now ? deps.now() : Date.now());
|
|
222
222
|
if (!token) return fail(NOT_LOGGED_IN);
|
|
223
223
|
|
|
224
224
|
try {
|
package/commands/deck.js
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const https = require('https');
|
|
18
|
-
const os = require('os');
|
|
19
18
|
const path = require('path');
|
|
19
|
+
const { stateHome } = require('../lib/state-home');
|
|
20
20
|
const { hasFlag } = require('../lib/arg-parser');
|
|
21
21
|
const { buildDeck, THEMES, notesRequests } = require('../lib/slides-deck');
|
|
22
22
|
const {
|
|
@@ -34,7 +34,7 @@ const BASE = 'api.atris.ai';
|
|
|
34
34
|
const PFX = '/api/integrations/google-slides';
|
|
35
35
|
|
|
36
36
|
function token() {
|
|
37
|
-
try { return require(path.join(
|
|
37
|
+
try { return require(path.join(stateHome(), '.atris/credentials.json')).token; }
|
|
38
38
|
catch { return null; }
|
|
39
39
|
}
|
|
40
40
|
|