atris 3.82.14 → 3.82.15
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/commands/business.js +20 -19
- package/commands/memory-live.js +16 -1
- package/commands/push.js +17 -3
- package/lib/memory-live.js +27 -3
- package/package.json +1 -1
package/commands/business.js
CHANGED
|
@@ -52,38 +52,38 @@ const {
|
|
|
52
52
|
findAtrisDir,
|
|
53
53
|
} = require('../lib/business-core');
|
|
54
54
|
|
|
55
|
-
async function findExistingBusinessBySlug(slug, token) {
|
|
55
|
+
async function findExistingBusinessBySlug(slug, token, { api = apiRequestJson, loadLocal = loadBusinesses } = {}) {
|
|
56
56
|
if (!slug) return null;
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return { id: v.business_id, name: v.name, slug, source: 'local' };
|
|
66
|
-
}
|
|
67
|
-
}
|
|
58
|
+
const local = loadLocal();
|
|
59
|
+
const localMatch = local[slug]
|
|
60
|
+
? { id: local[slug].business_id, name: local[slug].name, slug, source: 'local' }
|
|
61
|
+
: (() => {
|
|
62
|
+
const v = Object.values(local).find((b) => businessMatchesSlug(b, slug));
|
|
63
|
+
return v ? { id: v.business_id, name: v.name, slug, source: 'local' } : null;
|
|
64
|
+
})();
|
|
68
65
|
|
|
69
|
-
if (!token) return
|
|
66
|
+
if (!token) return localMatch;
|
|
70
67
|
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
token,
|
|
75
|
-
});
|
|
68
|
+
// The cloud is the truth. The Mac's list can hold a business that was
|
|
69
|
+
// deleted, and trusting it refused to recreate one that no longer existed.
|
|
70
|
+
const direct = await api(`/business/by-slug/${encodeURIComponent(slug)}`, { method: 'GET', token });
|
|
76
71
|
if (direct.ok && direct.data && direct.data.id) {
|
|
77
72
|
return { id: direct.data.id, name: direct.data.name, slug: direct.data.slug || slug, source: 'cloud' };
|
|
78
73
|
}
|
|
79
74
|
|
|
80
|
-
const list = await
|
|
75
|
+
const list = await api('/business/', { method: 'GET', token });
|
|
81
76
|
if (list.ok && Array.isArray(list.data)) {
|
|
82
77
|
const match = list.data.find(b => businessMatchesSlug(b, slug));
|
|
83
78
|
if (match) return { id: match.id, name: match.name, slug: match.slug, source: 'cloud' };
|
|
79
|
+
if (localMatch) {
|
|
80
|
+
console.warn(` Note: this Mac still lists "${slug}", but the cloud has no such business. Run: atris business doctor --fix`);
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
84
83
|
}
|
|
85
84
|
|
|
86
|
-
|
|
85
|
+
// Cloud unreachable: fall back to the Mac's list rather than risk a duplicate.
|
|
86
|
+
return localMatch;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
async function addBusiness(slug) {
|
|
@@ -1665,6 +1665,7 @@ async function businessCommand(subcommand, ...args) {
|
|
|
1665
1665
|
}
|
|
1666
1666
|
|
|
1667
1667
|
module.exports = {
|
|
1668
|
+
findExistingBusinessBySlug,
|
|
1668
1669
|
businessCommand,
|
|
1669
1670
|
businessWorkspaceBase,
|
|
1670
1671
|
loadBusinesses,
|
package/commands/memory-live.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// atris memory show, search, read, ls, remember, write, rm, usage, log, lesson
|
|
3
|
+
// atris memory show, search, ask, read, ls, remember, write, rm, usage, log, lesson
|
|
4
4
|
// and agents: the person's Atris memory from any terminal, so any agent can
|
|
5
5
|
// read it, search it and save to it. One request per command. The login and
|
|
6
6
|
// network modules load only when a command needs the server.
|
|
@@ -13,6 +13,7 @@ const { NOT_LOGGED_IN, oneLine, tokenFrom } = require('../lib/developer-api');
|
|
|
13
13
|
const MAX_WRITE_BYTES = 1000000;
|
|
14
14
|
const STDIN_WAIT_MS = 5000;
|
|
15
15
|
const REMEMBER_MAX_CHARS = 1000;
|
|
16
|
+
const ASK_MAX_CHARS = 500;
|
|
16
17
|
const REMEMBER_FILES = ['journal', 'now', 'learning'];
|
|
17
18
|
const LEARNING_SECTIONS = ['want', 'understand', 'shaky'];
|
|
18
19
|
|
|
@@ -138,6 +139,20 @@ const COMMANDS = {
|
|
|
138
139
|
},
|
|
139
140
|
},
|
|
140
141
|
|
|
142
|
+
ask: {
|
|
143
|
+
check(ctx) {
|
|
144
|
+
const question = ctx.words.join(' ').replace(/\s+/g, ' ').trim();
|
|
145
|
+
if (!question) throw new UsageError('ask a question, like atris memory ask "where does Sam work?"');
|
|
146
|
+
if (question.length > ASK_MAX_CHARS) throw new UsageError('keep the question under 500 characters');
|
|
147
|
+
return { question, folder: ctx.flags['--in'] || '' };
|
|
148
|
+
},
|
|
149
|
+
async run(ctx, { question, folder }) {
|
|
150
|
+
// The server reads for up to 75 seconds; asking twice would count twice.
|
|
151
|
+
const data = await ctx.call(`/memory/ask?q=${enc(question)}${folder ? `&folder=${enc(folder)}` : ''}`, { timeoutMs: 90000, retries: 0 });
|
|
152
|
+
return { data, lines: live.askLines(data) };
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
|
|
141
156
|
read: {
|
|
142
157
|
check(ctx) {
|
|
143
158
|
const rel = onePath(ctx, 'file to read');
|
package/commands/push.js
CHANGED
|
@@ -9,6 +9,17 @@ const { normalizeWikiOnlyPrefix } = require('../lib/wiki');
|
|
|
9
9
|
const { emitSyncEvent, startTimer } = require('../lib/sync-telemetry');
|
|
10
10
|
const { assertSafeWorkspaceRoot } = require('../lib/workspace-safety');
|
|
11
11
|
|
|
12
|
+
// The server's own words for a refused request. It used to be dropped, so a
|
|
13
|
+
// refusal for any reason read as "you can only push to your team/ folder".
|
|
14
|
+
function refusalText(result) {
|
|
15
|
+
if (!result) return '';
|
|
16
|
+
const raw = result.errorMessage || result.error || (result.data && result.data.detail) || '';
|
|
17
|
+
if (!raw) return '';
|
|
18
|
+
if (typeof raw === 'string') return raw === 'Request failed' ? '' : raw;
|
|
19
|
+
if (typeof raw === 'object' && typeof raw.message === 'string') return raw.message;
|
|
20
|
+
try { return JSON.stringify(raw); } catch { return String(raw); }
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
function resolvePushSourceDir({ slug, argv = process.argv, cwd = process.cwd() }) {
|
|
13
24
|
const fromIdx = argv.indexOf('--from');
|
|
14
25
|
if (fromIdx !== -1 && argv[fromIdx + 1]) {
|
|
@@ -858,13 +869,15 @@ async function pushAtris() {
|
|
|
858
869
|
pushed = landedPaths.size;
|
|
859
870
|
break;
|
|
860
871
|
} else {
|
|
861
|
-
console.error(`\n Push failed: ${retry
|
|
872
|
+
console.error(`\n Push failed: ${refusalText(retry) || retry.status}`);
|
|
873
|
+
if (refusalText(result)) console.error(` The first refusal said: ${refusalText(result)}`);
|
|
862
874
|
await emit('access_denied', { error_detail: `403 retry failed: ${retry.status}` });
|
|
863
875
|
process.exit(1);
|
|
864
876
|
}
|
|
865
877
|
} else {
|
|
866
|
-
|
|
867
|
-
|
|
878
|
+
const reason = refusalText(result);
|
|
879
|
+
console.error(`\n Access denied: ${reason || 'the server refused this push and gave no reason.'}`);
|
|
880
|
+
await emit('access_denied', reason ? { error_detail: reason } : undefined);
|
|
868
881
|
process.exit(1);
|
|
869
882
|
}
|
|
870
883
|
} else if (result.status === 409) {
|
|
@@ -1052,6 +1065,7 @@ async function pushAtris() {
|
|
|
1052
1065
|
}
|
|
1053
1066
|
|
|
1054
1067
|
module.exports = {
|
|
1068
|
+
refusalText,
|
|
1055
1069
|
pushAtris,
|
|
1056
1070
|
buildPushChangePlan,
|
|
1057
1071
|
buildPushUploadBatches,
|
package/lib/memory-live.js
CHANGED
|
@@ -9,7 +9,7 @@ const fs = require('fs');
|
|
|
9
9
|
const path = require('path');
|
|
10
10
|
|
|
11
11
|
const LIVE_COMMANDS = new Set([
|
|
12
|
-
'show', 'search', 'read', 'ls', 'remember', 'write', 'rm', 'usage', 'log', 'lesson', 'agents', 'setup',
|
|
12
|
+
'show', 'search', 'ask', 'read', 'ls', 'remember', 'write', 'rm', 'usage', 'log', 'lesson', 'agents', 'setup',
|
|
13
13
|
]);
|
|
14
14
|
|
|
15
15
|
function isLiveCommand(word) {
|
|
@@ -19,10 +19,11 @@ function isLiveCommand(word) {
|
|
|
19
19
|
// One line per command, shown together by `atris memory` and one at a time by
|
|
20
20
|
// `atris memory <command> --help`.
|
|
21
21
|
const COLUMN = 38;
|
|
22
|
-
const row = (form, ...what) => [` ${form}`.padEnd(COLUMN) + what[0], ...what.slice(1).map((more) => ' '.repeat(COLUMN) + more)].join('\n');
|
|
22
|
+
const row = (form, ...what) => [` ${form} `.padEnd(COLUMN) + what[0], ...what.slice(1).map((more) => ' '.repeat(COLUMN) + more)].join('\n');
|
|
23
23
|
const USAGE_LINES = {
|
|
24
24
|
show: row('show', 'who they are and what matters now; read it before helping'),
|
|
25
25
|
search: row('search "rent|lease" [--in folder]', 'lines with any of the words, split by |'),
|
|
26
|
+
ask: row('ask "a question" [--in folder]', 'answered from their memory, with the lines it rests on'),
|
|
26
27
|
read: row('read <path> [--from N] [--lines N]', 'numbered lines of one file; --raw prints it as stored'),
|
|
27
28
|
ls: row('ls [folder]', 'what is in a folder; MAP.md says what is where'),
|
|
28
29
|
remember: row('remember "one short line"', 'a lasting fact or preference about the person, when they',
|
|
@@ -43,6 +44,7 @@ const LIVE_HELP = [
|
|
|
43
44
|
'read',
|
|
44
45
|
USAGE_LINES.show,
|
|
45
46
|
USAGE_LINES.search,
|
|
47
|
+
USAGE_LINES.ask,
|
|
46
48
|
USAGE_LINES.read,
|
|
47
49
|
USAGE_LINES.ls,
|
|
48
50
|
'',
|
|
@@ -62,6 +64,7 @@ const LIVE_HELP = [
|
|
|
62
64
|
// Flags that take a value, per command. --json, --raw and --help take none.
|
|
63
65
|
const VALUE_FLAGS = {
|
|
64
66
|
search: ['--in'],
|
|
67
|
+
ask: ['--in'],
|
|
65
68
|
read: ['--from', '--lines'],
|
|
66
69
|
ls: [],
|
|
67
70
|
show: [],
|
|
@@ -205,6 +208,26 @@ function searchLines(query, data) {
|
|
|
205
208
|
return lines;
|
|
206
209
|
}
|
|
207
210
|
|
|
211
|
+
// The answer first, then each line it rests on, then how sure it is. With no
|
|
212
|
+
// answer (the model was busy) the search hits it started from are shown.
|
|
213
|
+
function askLines(data) {
|
|
214
|
+
const sources = (data && data.sources) || [];
|
|
215
|
+
const cite = sources.map((s) => ` ${s.path}:${s.n}: ${s.text}`);
|
|
216
|
+
if (data && data.answer && data.found) {
|
|
217
|
+
return [String(data.answer), ...(cite.length ? ['', 'From:', ...cite] : [])];
|
|
218
|
+
}
|
|
219
|
+
if (data && data.answer) {
|
|
220
|
+
const lines = [String(data.answer)];
|
|
221
|
+
if (cite.length) lines.push('', 'Closest lines:', ...cite);
|
|
222
|
+
lines.push('', 'Not found for certain. Search it yourself: atris memory search "word|other word"');
|
|
223
|
+
return lines;
|
|
224
|
+
}
|
|
225
|
+
const hits = (data && data.search && data.search.hits) || [];
|
|
226
|
+
const lines = [String((data && data.note) || 'No answer right now.')];
|
|
227
|
+
if (hits.length) lines.push('', 'Lines that may hold it:', ...hits.slice(0, 8).map((h) => ` ${h.path}:${h.n}: ${h.text}`));
|
|
228
|
+
return lines;
|
|
229
|
+
}
|
|
230
|
+
|
|
208
231
|
function readLines(data) {
|
|
209
232
|
const page = (data && data.lines) || [];
|
|
210
233
|
const total = Number(data && data.total) || 0;
|
|
@@ -244,7 +267,7 @@ function rulesBlock(agent) {
|
|
|
244
267
|
'',
|
|
245
268
|
'This person keeps one memory in Atris that every app and agent shares. Use it.',
|
|
246
269
|
'- Start each task with `atris memory show`: who they are and what matters now.',
|
|
247
|
-
'- Before you ask them anything they may have said before, search: `atris memory search "word|other word"`.',
|
|
270
|
+
'- Before you ask them anything they may have said before, search: `atris memory search "word|other word"`, or ask in plain words: `atris memory ask "..."`.',
|
|
248
271
|
'- Open what you find with `atris memory read <path>`. `atris memory ls` shows the folder.',
|
|
249
272
|
'- When they say remember, note, save or don\'t forget, save one short line: `atris memory remember "..."`.',
|
|
250
273
|
'- Save only lasting facts about them, never logs or your own work. Never rewrite their about.md.',
|
|
@@ -338,6 +361,7 @@ module.exports = {
|
|
|
338
361
|
END,
|
|
339
362
|
LIVE_HELP,
|
|
340
363
|
USAGE_LINES,
|
|
364
|
+
askLines,
|
|
341
365
|
isLiveCommand,
|
|
342
366
|
lsLines,
|
|
343
367
|
mcpLines,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atris",
|
|
3
|
-
"version": "3.82.
|
|
3
|
+
"version": "3.82.15",
|
|
4
4
|
"description": "you say what you want in plain words. atris builds it, checks it, and shows you proof. project memory your coding agents share across sessions.",
|
|
5
5
|
"mcpName": "io.github.atrislabs/atris",
|
|
6
6
|
"main": "bin/atris.js",
|