@tomato414941/foundation 0.23.0 → 0.25.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/package.json +1 -1
- package/runtime.mjs +20 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomato414941/foundation",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Foundation CLI: give this machine a WebAuthn credential, and hand what is kept to a command without it passing through the agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
package/runtime.mjs
CHANGED
|
@@ -230,7 +230,7 @@ async function main() {
|
|
|
230
230
|
// A lent machine's key stays as it is; it ends with the machine.
|
|
231
231
|
async function upgrade(label) {
|
|
232
232
|
const me = await send('/v1/principals/me', undefined, { method: 'GET' });
|
|
233
|
-
if (
|
|
233
|
+
if ((await send('/v1/session', undefined, { method: 'GET' })).current?.via.environment) return;
|
|
234
234
|
const { options } = await send('/v1/principals/' + encodeURIComponent(me.principal.id) + '/credentials', { kind: 'webauthn' });
|
|
235
235
|
const made = createCredential(options, url.origin);
|
|
236
236
|
await send('/v1/principals/' + encodeURIComponent(me.principal.id) + '/credentials', { kind: 'webauthn', name: label, credential: made.response }, { method: 'PUT' });
|
|
@@ -288,23 +288,32 @@ async function main() {
|
|
|
288
288
|
} else if (key?.token) { await upgrade(wanted); token = await prove(); await publishKey(); }
|
|
289
289
|
if (serverGiven !== undefined) await saveUrl(url.origin);
|
|
290
290
|
if (!me) me = await send('/v1/principals/me', undefined, { method: 'GET' });
|
|
291
|
-
console.log(JSON.stringify({ principal: me.principal, acts_for: me.acts_for ?? [] }, null, 2));
|
|
291
|
+
console.log(JSON.stringify({ principal: { id: me.principal.id, name: me.principal.name, created_at: me.principal.created_at }, acts_for: me.principal.acts_for ?? [] }, null, 2));
|
|
292
292
|
console.log('\nKey file: ' + keyPath + '\nServer: ' + url.origin + (serverGiven !== undefined ? ' (saved to ' + configPath() + ')' : '') + '\nTo act for someone: foundation join\nEverything else is HTTP: Authorization: Bearer $(foundation token)');
|
|
293
293
|
return;
|
|
294
294
|
}
|
|
295
295
|
// Asking a person to make this machine their agent. One they already approved has nothing to ask.
|
|
296
296
|
if (action === 'join') {
|
|
297
297
|
const me = await send('/v1/principals/me', undefined, { method: 'GET', accept: data => data.error?.code === 'not_approved' });
|
|
298
|
-
if (me.acts_for?.length) { console.log('Already approved on ' + url.origin + '.'); return; }
|
|
298
|
+
if (me.principal?.acts_for?.length) { console.log('Already approved on ' + url.origin + '.'); return; }
|
|
299
299
|
const answer = await send('/v1/requests', { authorization_details: [{ type: 'relation', relation: 'agent' }] });
|
|
300
300
|
console.log(JSON.stringify(answer, null, 2));
|
|
301
301
|
return;
|
|
302
302
|
}
|
|
303
|
+
// What others hold and this machine has a line onto: the things at the far end of the lines it is at the near end of.
|
|
304
|
+
const shownToMe = async () => {
|
|
305
|
+
const things = [];
|
|
306
|
+
for (let after = null; ;) {
|
|
307
|
+
const page = await send('/v1/principals/me/relations?direction=from&limit=200' + (after ? '&after=' + encodeURIComponent(after) : ''), undefined, { method: 'GET', accept: () => true });
|
|
308
|
+
things.push(...(page.relations ?? []).filter(line => line.resource).map(line => line.resource));
|
|
309
|
+
if (!(after = page.next)) return things;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
303
312
|
// Reading a secret this machine was handed an envelope for: its own, or one shown to it along a line.
|
|
304
313
|
if (action === 'read') {
|
|
305
314
|
if (!key.own) throw new Error('This machine has no key of its own here, so nothing sealed for it can be opened.');
|
|
306
315
|
const own = await send('/v1/principals/me/resources?kind=secret&name=' + encodeURIComponent(call.name), undefined, { method: 'GET', accept: () => true });
|
|
307
|
-
const resource = own.resource ?? (await
|
|
316
|
+
const resource = own.resource ?? (await shownToMe()).find(item => item.kind === 'secret' && item.name === call.name);
|
|
308
317
|
if (!resource) throw new Error('No secret named ' + JSON.stringify(call.name) + ' is kept by this machine or shown to it.');
|
|
309
318
|
const kept = await send('/v1/resources/' + resource.id + '/content', undefined, { method: 'GET' });
|
|
310
319
|
if (!kept.envelope) throw new Error('No envelope was made for this machine: it may read about this secret, but was not handed its key.');
|
|
@@ -314,11 +323,14 @@ async function main() {
|
|
|
314
323
|
}
|
|
315
324
|
// Nothing runs before someone has accepted this key: a key that acts for nobody reaches only its own empty resources,
|
|
316
325
|
// and the person it asked has yet to answer.
|
|
317
|
-
const current = await send('/v1/principals/me', undefined, { method: 'GET' });
|
|
326
|
+
const current = (await send('/v1/principals/me', undefined, { method: 'GET' })).principal;
|
|
318
327
|
// A key given to a lent machine acts as its principal's own self. Any other key acts for someone once approved;
|
|
319
328
|
// until then, whether waiting or refused, it has nothing to run with.
|
|
320
|
-
const own = Boolean(current.
|
|
321
|
-
if (!own && !current.acts_for?.length)
|
|
329
|
+
const own = Boolean((await send('/v1/session', undefined, { method: 'GET' })).current?.via.environment);
|
|
330
|
+
if (!own && !current.acts_for?.length) {
|
|
331
|
+
const waiting = (await send('/v1/requests?status=pending', undefined, { method: 'GET', accept: () => true })).requests?.[0];
|
|
332
|
+
throw new Error('Foundation request failed (401, not_approved). This key acts for nobody yet' + (waiting ? '; it is waiting for approval at ' + waiting.verification_uri : '') + '.');
|
|
333
|
+
}
|
|
322
334
|
// Whose resources a run reaches: the one this key acts for, the one named when it acts for several, or its own.
|
|
323
335
|
const acting = current.acts_for ?? [];
|
|
324
336
|
const owner = process.env.FOUNDATION_AS || (acting.length === 1 ? acting[0] : null);
|
|
@@ -344,7 +356,7 @@ async function main() {
|
|
|
344
356
|
const handed = { environment: {}, files: [] };
|
|
345
357
|
let asked = names;
|
|
346
358
|
if (key.own && names.some(item => typeof item.name === 'string')) {
|
|
347
|
-
const shown = owner ?
|
|
359
|
+
const shown = owner ? await shownToMe() : [];
|
|
348
360
|
asked = [];
|
|
349
361
|
for (const item of names) {
|
|
350
362
|
let resource = null;
|