@lampdotdev/lamp 0.36.2-nightly.19 → 0.36.2-nightly.21
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
|
@@ -33,7 +33,7 @@ Mac app, `projectOf`), or any folder with an `index.html`; without one, the fold
|
|
|
33
33
|
| `lamp unshare` | Takes the link down. |
|
|
34
34
|
| `lamp account [username] [--email address]` | The maker's account on lamp.dev, which their links go under (`lamp.dev/<username>/<app>`): with a username and an email it asks the site for one (`askAccount`), whose key lamp keeps with its state (`account.json`, by site) and sends with every share (`X-Lamp-Account`); the link in the email the site sends confirms it. With only an email that has an account, it signs in to it, with a key of its own. On its own, it says which it's signed in as. A share before the email's link is opened goes out as links always have (`/w/<id>-<name>`), and moves under the username at its next share. |
|
|
35
35
|
| `lamp play <link> [--to folder] [--newest]` | Plays an app shared on lamp.dev on this computer: the link's file (`latestOf`, its `.lampapp`), unpacked as `lamp unpack` does into a folder of its own in `~/Lamp Games` (or `--to`), with Lamp's own files beside its page, then opened as `lamp open` opens any folder. Its AI is answered by `lamp dev` (`ai.mjs`) in any browser, Safari included, where a shared page can't reach the computer. lamp remembers which folder each link went into (a file for each link under `played/` in its state), so playing it again goes on where it was; `--newest` takes the link's latest version into a new folder. |
|
|
36
|
-
| `lamp ai [--forget]` | Answers the AI of pages shared on lamp.dev, played in a browser on this computer, with the person's own Claude Code, Codex or OpenRouter key (`cli/local-ai.mjs`, the same answers as `ai.mjs` gives `lamp dev`), until it's stopped. It listens on this computer alone at a port lamp.js knows (`LOCAL_AI_PORT`, `NEARBY` in lamp.js), and answers only a link's own page (`<id in hex>.play.lamp.dev`, `playOrigin`): the first time a page uses the AI, the person is asked in the terminal, and the page keeps the token it's given. Whether there's an AI is answered without asking. The browser asks the person too, once, before a page may reach this computer, so the link's frame on lamp.dev is given that permission. Safari never lets the page reach it (Safari 27, checked against a stand-in on the port), so lamp.js tells someone playing in Safari to use Chrome, Lamp or `lamp play` instead. `--forget` takes back every page allowed. Lamp's app runs the same server while it's open (`sharedPages` in `calls.mjs`, `SharedPagesAI`): it asks the person in a dialog and answers with the AI they set up in Lamp, and the pages allowed are the same list; whichever of the two starts first answers. |
|
|
36
|
+
| `lamp ai [--forget]` | Answers the AI of pages shared on lamp.dev, played in a browser on this computer, with the person's own Claude Code, Codex or OpenRouter key (`cli/local-ai.mjs`, the same answers as `ai.mjs` gives `lamp dev`), until it's stopped. It listens on this computer alone at a port lamp.js knows (`LOCAL_AI_PORT`, `NEARBY` in lamp.js), and answers only a link's own page (`<id in hex>.play.lamp.dev`, `playOrigin`): the first time a page uses the AI, the person is asked in the terminal, and the page keeps the token it's given. Whether there's an AI is answered without asking. The browser asks the person too, once, before a page may reach this computer, so the link's frame on lamp.dev is given that permission; lamp.js reaches the port only once that's granted, the page has a token, or the person asks (`LampAI.connect()` from a button, or a real AI call), so the browser doesn't ask as the page loads. Safari never lets the page reach it (Safari 27, checked against a stand-in on the port), so lamp.js tells someone playing in Safari to use Chrome, Lamp or `lamp play` instead. `--forget` takes back every page allowed. Lamp's app runs the same server while it's open (`sharedPages` in `calls.mjs`, `SharedPagesAI`): it asks the person in a dialog and answers with the AI they set up in Lamp, and the pages allowed are the same list; whichever of the two starts first answers. |
|
|
37
37
|
| `lamp devices [add \| forget <name>]` | The person's devices lamp puts a page on: an Xbox in Developer Mode (those set up and those found on the network), and Android (lamp's emulators, and the phones, tablets and TVs plugged in with USB debugging on). `add` sets one up, a step at a time; `add android` downloads Android's tools. |
|
|
38
38
|
| `lamp open --on xbox` | Packages the page, installs it on the device and opens it there. `lamp look --on xbox` is the device's screen and the app's log there, `lamp stop --on xbox` ends it, and `lamp remove --on xbox` takes it off. |
|
|
39
39
|
| `lamp open --on android [--hidden]` | Builds Lamp's Android app around the page and opens it in lamp's Android emulator (`--on android-tv`: its TV emulator; a device of the person's by its name), with `look`, `stop` and `remove` as on any device, and `lamp use --on android` to tap, type and press there. `--hidden` starts the emulator with no window. |
|
|
@@ -20,7 +20,10 @@ opening comment for the whole list. What it gives:
|
|
|
20
20
|
on it, check `LampAI.status('text')` and, when it can't answer, show its `reason` as it's given: lamp.js words it
|
|
21
21
|
for where the page is (away from the Mac's Wi‑Fi, Lamp closed, a link on lamp.dev), so don't write your own. Where it
|
|
22
22
|
gives `help` (a page away from Lamp), add a link to it beside the reason, "How to turn on AI", so someone who has never
|
|
23
|
-
heard of Lamp finds out how.
|
|
23
|
+
heard of Lamp finds out how. Where it answers `connect: true` (a page on lamp.dev that hasn't reached this computer
|
|
24
|
+
yet), also show a small "Connect AI" button that calls `LampAI.connect(capability)` with the same kind of AI you asked
|
|
25
|
+
`status` about (`LampAI.connect('pictures')` for pictures) and use the answer it gives as `status`'s;
|
|
26
|
+
never call `connect()` as the page loads, since it makes the browser ask the person.
|
|
24
27
|
- **LampAI.act**: the person asks the app to do something in their own words, and the app's own reads and writes do
|
|
25
28
|
it; "Doing things for the person" below, with **LampVoice** and **LampAI.hear** for saying it.
|
|
26
29
|
- **LampStore**: what the app saves, as JSON on the device. Once something is saved, only ever add to it: a new field
|
|
@@ -254,6 +254,26 @@
|
|
|
254
254
|
var ua = (window.navigator && navigator.userAgent) || '';
|
|
255
255
|
return /Safari\//.test(ua) && !/Chrome|Chromium|CriOS|Edg|OPR|Firefox|FxiOS/.test(ua);
|
|
256
256
|
}
|
|
257
|
+
var SAFARI_AI = 'AI here works in Chrome or in Lamp, or with Lamp\'s command line: lamp play and this page\'s link.';
|
|
258
|
+
|
|
259
|
+
// Reaching this computer makes the browser ask the person about it, so a shared page doesn't reach out just to
|
|
260
|
+
// see whether there's an AI: only once the browser already lets it, the page has paired before, or the person
|
|
261
|
+
// has asked for it (LampAI.connect, from a button, or a real AI call).
|
|
262
|
+
var nearbyAsked = false;
|
|
263
|
+
function nearbyAllowed() {
|
|
264
|
+
// Connected before in this browser (kept with the page's saved things): the browser has had its say already.
|
|
265
|
+
if (nearbyAsked || read('nearby.token', null) || read('nearby.connected', false)) return Promise.resolve(true);
|
|
266
|
+
var permissions = window.navigator && navigator.permissions;
|
|
267
|
+
if (!permissions || typeof permissions.query !== 'function') return Promise.resolve(false);
|
|
268
|
+
// Chrome has named this permission more than one way; any that's granted will do, and one it doesn't know is unknown.
|
|
269
|
+
var names = ['local-network-access', 'loopback-network', 'local-network'];
|
|
270
|
+
return Promise.all(names.map(function (name) {
|
|
271
|
+
try {
|
|
272
|
+
return Promise.resolve(permissions.query({ name: name })).then(function (s) { return !!s && s.state === 'granted'; }, function () { return false; });
|
|
273
|
+
} catch (e) { return false; }
|
|
274
|
+
})).then(function (states) { return states.indexOf(true) !== -1; });
|
|
275
|
+
}
|
|
276
|
+
|
|
257
277
|
function nearby(path, body, timeout, deadline) {
|
|
258
278
|
var text = JSON.stringify(body);
|
|
259
279
|
function go(t) { return exchange(NEARBY, path, t, text, deadline ? Math.max(1000, deadline - Date.now()) : timeout); }
|
|
@@ -276,7 +296,7 @@
|
|
|
276
296
|
return done.then(null, function (e) {
|
|
277
297
|
if (e instanceof Failure) throw e;
|
|
278
298
|
// Safari never lets a page on the web reach this computer this way, so Lamp or lamp ai open here wouldn't help.
|
|
279
|
-
if (safari()) throw new Failure(
|
|
299
|
+
if (safari()) throw new Failure(SAFARI_AI, null, AI_HELP);
|
|
280
300
|
throw new Failure('AI isn\'t available here. Open Lamp on this computer, or with Lamp\'s command line, run lamp ai.', null, AI_HELP);
|
|
281
301
|
});
|
|
282
302
|
}
|
|
@@ -738,31 +758,58 @@
|
|
|
738
758
|
* a feature depends on it, and show the feature with its `reason` as it's given rather than words of the app's
|
|
739
759
|
* own, or hiding it or failing at it, and where it gives `help` (a page away from Lamp), a link to it beside the
|
|
740
760
|
* reason: "How to turn on AI". A call that fails for that reason has `needsSetup`, or `help`, set on its error.
|
|
761
|
+
* On a page shared on lamp.dev that hasn't reached this computer yet, `status` asks nothing and answers
|
|
762
|
+
* `connect: true`: show a small "Connect AI" button beside its reason that calls `LampAI.connect(capability)` with
|
|
763
|
+
* the same kind of AI (`connect('pictures')` for pictures), and use what that gives as `status`'s answer. Never
|
|
764
|
+
* call `connect()` as the page loads.
|
|
741
765
|
*/
|
|
742
766
|
var LampAI = {
|
|
743
767
|
Failure: Failure,
|
|
744
768
|
|
|
745
769
|
/** Whether this kind of AI ('text', 'decisions', 'pictures', 'act' or 'hear') can answer now: { available, reason,
|
|
746
|
-
* help }, where `reason` is the short line to show when it can't, written for where the page is,
|
|
747
|
-
* it's there, the page that says how to turn AI on, to link to beside it
|
|
770
|
+
* help, connect }, where `reason` is the short line to show when it can't, written for where the page is, `help`,
|
|
771
|
+
* when it's there, the page that says how to turn AI on, to link to beside it, and `connect`, when true, that
|
|
772
|
+
* a button calling `LampAI.connect()` would find out (a page on lamp.dev, before it has reached this computer). */
|
|
748
773
|
status: function (capability) {
|
|
749
774
|
var no = function (reason, help) {
|
|
750
775
|
var said = { available: false, reason: reason || 'AI isn\'t set up for this here.' };
|
|
751
776
|
if (help) said.help = help;
|
|
752
777
|
return said;
|
|
753
778
|
};
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
return
|
|
779
|
+
if (!bridge && shared()) {
|
|
780
|
+
return nearbyAllowed().then(function (allowed) {
|
|
781
|
+
if (allowed) return ask(capability);
|
|
782
|
+
if (safari()) return no(SAFARI_AI, AI_HELP);
|
|
783
|
+
return { available: false, connect: true, reason: 'Connect to the AI on this computer to use this.', help: AI_HELP };
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
return ask(capability);
|
|
787
|
+
function ask(capability) {
|
|
788
|
+
// 'act' is asked of /ai/act itself: available only when this lamp.js has it, the app around the page lets the
|
|
789
|
+
// route through (an older one answers Not found) and the AI that answers the app can take it. 'hear' the same.
|
|
790
|
+
if (capability === 'act' || capability === 'hear') {
|
|
791
|
+
return post('/ai/' + capability, { probe: true }, 10000).then(function (reply) {
|
|
792
|
+
return reply[capability] === true ? { available: true, reason: '' } : no(reply.reason || reply.why);
|
|
793
|
+
}, function (e) { return no(e && e.message, e && e.help); });
|
|
794
|
+
}
|
|
795
|
+
return post('/ai/status', {}, 10000).then(function (reply) {
|
|
796
|
+
if (reply[capability] === true) return { available: true, reason: '' };
|
|
797
|
+
var detail = reply.details && reply.details[capability];
|
|
798
|
+
return no(detail && detail.reason);
|
|
759
799
|
}, function (e) { return no(e && e.message, e && e.help); });
|
|
760
800
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
801
|
+
},
|
|
802
|
+
|
|
803
|
+
/** On a page shared on lamp.dev, reaches the AI on this computer, so the browser asks the person then: call it
|
|
804
|
+
* from a button ("Connect AI") shown when `status` answers `connect: true`, never as the page loads. Gives what
|
|
805
|
+
* `status` gives for that kind of AI ('text' unless named). Anywhere else it's just `status`. */
|
|
806
|
+
connect: function (capability) {
|
|
807
|
+
nearbyAsked = true;
|
|
808
|
+
return LampAI.status(capability || 'text').then(function (said) {
|
|
809
|
+
// Reached it (whatever it said of this kind of AI): later visits look without asking first.
|
|
810
|
+
if (said.available || !said.help) write('nearby.connected', true);
|
|
811
|
+
return said;
|
|
812
|
+
});
|
|
766
813
|
},
|
|
767
814
|
|
|
768
815
|
/** Whether this kind of AI can answer now, as `status` says. */
|
package/cli/lamps/lamp.js
CHANGED
|
@@ -254,6 +254,26 @@
|
|
|
254
254
|
var ua = (window.navigator && navigator.userAgent) || '';
|
|
255
255
|
return /Safari\//.test(ua) && !/Chrome|Chromium|CriOS|Edg|OPR|Firefox|FxiOS/.test(ua);
|
|
256
256
|
}
|
|
257
|
+
var SAFARI_AI = 'AI here works in Chrome or in Lamp, or with Lamp\'s command line: lamp play and this page\'s link.';
|
|
258
|
+
|
|
259
|
+
// Reaching this computer makes the browser ask the person about it, so a shared page doesn't reach out just to
|
|
260
|
+
// see whether there's an AI: only once the browser already lets it, the page has paired before, or the person
|
|
261
|
+
// has asked for it (LampAI.connect, from a button, or a real AI call).
|
|
262
|
+
var nearbyAsked = false;
|
|
263
|
+
function nearbyAllowed() {
|
|
264
|
+
// Connected before in this browser (kept with the page's saved things): the browser has had its say already.
|
|
265
|
+
if (nearbyAsked || read('nearby.token', null) || read('nearby.connected', false)) return Promise.resolve(true);
|
|
266
|
+
var permissions = window.navigator && navigator.permissions;
|
|
267
|
+
if (!permissions || typeof permissions.query !== 'function') return Promise.resolve(false);
|
|
268
|
+
// Chrome has named this permission more than one way; any that's granted will do, and one it doesn't know is unknown.
|
|
269
|
+
var names = ['local-network-access', 'loopback-network', 'local-network'];
|
|
270
|
+
return Promise.all(names.map(function (name) {
|
|
271
|
+
try {
|
|
272
|
+
return Promise.resolve(permissions.query({ name: name })).then(function (s) { return !!s && s.state === 'granted'; }, function () { return false; });
|
|
273
|
+
} catch (e) { return false; }
|
|
274
|
+
})).then(function (states) { return states.indexOf(true) !== -1; });
|
|
275
|
+
}
|
|
276
|
+
|
|
257
277
|
function nearby(path, body, timeout, deadline) {
|
|
258
278
|
var text = JSON.stringify(body);
|
|
259
279
|
function go(t) { return exchange(NEARBY, path, t, text, deadline ? Math.max(1000, deadline - Date.now()) : timeout); }
|
|
@@ -276,7 +296,7 @@
|
|
|
276
296
|
return done.then(null, function (e) {
|
|
277
297
|
if (e instanceof Failure) throw e;
|
|
278
298
|
// Safari never lets a page on the web reach this computer this way, so Lamp or lamp ai open here wouldn't help.
|
|
279
|
-
if (safari()) throw new Failure(
|
|
299
|
+
if (safari()) throw new Failure(SAFARI_AI, null, AI_HELP);
|
|
280
300
|
throw new Failure('AI isn\'t available here. Open Lamp on this computer, or with Lamp\'s command line, run lamp ai.', null, AI_HELP);
|
|
281
301
|
});
|
|
282
302
|
}
|
|
@@ -738,31 +758,58 @@
|
|
|
738
758
|
* a feature depends on it, and show the feature with its `reason` as it's given rather than words of the app's
|
|
739
759
|
* own, or hiding it or failing at it, and where it gives `help` (a page away from Lamp), a link to it beside the
|
|
740
760
|
* reason: "How to turn on AI". A call that fails for that reason has `needsSetup`, or `help`, set on its error.
|
|
761
|
+
* On a page shared on lamp.dev that hasn't reached this computer yet, `status` asks nothing and answers
|
|
762
|
+
* `connect: true`: show a small "Connect AI" button beside its reason that calls `LampAI.connect(capability)` with
|
|
763
|
+
* the same kind of AI (`connect('pictures')` for pictures), and use what that gives as `status`'s answer. Never
|
|
764
|
+
* call `connect()` as the page loads.
|
|
741
765
|
*/
|
|
742
766
|
var LampAI = {
|
|
743
767
|
Failure: Failure,
|
|
744
768
|
|
|
745
769
|
/** Whether this kind of AI ('text', 'decisions', 'pictures', 'act' or 'hear') can answer now: { available, reason,
|
|
746
|
-
* help }, where `reason` is the short line to show when it can't, written for where the page is,
|
|
747
|
-
* it's there, the page that says how to turn AI on, to link to beside it
|
|
770
|
+
* help, connect }, where `reason` is the short line to show when it can't, written for where the page is, `help`,
|
|
771
|
+
* when it's there, the page that says how to turn AI on, to link to beside it, and `connect`, when true, that
|
|
772
|
+
* a button calling `LampAI.connect()` would find out (a page on lamp.dev, before it has reached this computer). */
|
|
748
773
|
status: function (capability) {
|
|
749
774
|
var no = function (reason, help) {
|
|
750
775
|
var said = { available: false, reason: reason || 'AI isn\'t set up for this here.' };
|
|
751
776
|
if (help) said.help = help;
|
|
752
777
|
return said;
|
|
753
778
|
};
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
return
|
|
779
|
+
if (!bridge && shared()) {
|
|
780
|
+
return nearbyAllowed().then(function (allowed) {
|
|
781
|
+
if (allowed) return ask(capability);
|
|
782
|
+
if (safari()) return no(SAFARI_AI, AI_HELP);
|
|
783
|
+
return { available: false, connect: true, reason: 'Connect to the AI on this computer to use this.', help: AI_HELP };
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
return ask(capability);
|
|
787
|
+
function ask(capability) {
|
|
788
|
+
// 'act' is asked of /ai/act itself: available only when this lamp.js has it, the app around the page lets the
|
|
789
|
+
// route through (an older one answers Not found) and the AI that answers the app can take it. 'hear' the same.
|
|
790
|
+
if (capability === 'act' || capability === 'hear') {
|
|
791
|
+
return post('/ai/' + capability, { probe: true }, 10000).then(function (reply) {
|
|
792
|
+
return reply[capability] === true ? { available: true, reason: '' } : no(reply.reason || reply.why);
|
|
793
|
+
}, function (e) { return no(e && e.message, e && e.help); });
|
|
794
|
+
}
|
|
795
|
+
return post('/ai/status', {}, 10000).then(function (reply) {
|
|
796
|
+
if (reply[capability] === true) return { available: true, reason: '' };
|
|
797
|
+
var detail = reply.details && reply.details[capability];
|
|
798
|
+
return no(detail && detail.reason);
|
|
759
799
|
}, function (e) { return no(e && e.message, e && e.help); });
|
|
760
800
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
801
|
+
},
|
|
802
|
+
|
|
803
|
+
/** On a page shared on lamp.dev, reaches the AI on this computer, so the browser asks the person then: call it
|
|
804
|
+
* from a button ("Connect AI") shown when `status` answers `connect: true`, never as the page loads. Gives what
|
|
805
|
+
* `status` gives for that kind of AI ('text' unless named). Anywhere else it's just `status`. */
|
|
806
|
+
connect: function (capability) {
|
|
807
|
+
nearbyAsked = true;
|
|
808
|
+
return LampAI.status(capability || 'text').then(function (said) {
|
|
809
|
+
// Reached it (whatever it said of this kind of AI): later visits look without asking first.
|
|
810
|
+
if (said.available || !said.help) write('nearby.connected', true);
|
|
811
|
+
return said;
|
|
812
|
+
});
|
|
766
813
|
},
|
|
767
814
|
|
|
768
815
|
/** Whether this kind of AI can answer now, as `status` says. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"
|
|
1
|
+
{"exe":"ZqxAppName","name":"Zqx App Name","instance":"66001C8E-04AF-4B31-9585-64F312C02625","platform":"mac"}
|
|
@@ -254,6 +254,26 @@
|
|
|
254
254
|
var ua = (window.navigator && navigator.userAgent) || '';
|
|
255
255
|
return /Safari\//.test(ua) && !/Chrome|Chromium|CriOS|Edg|OPR|Firefox|FxiOS/.test(ua);
|
|
256
256
|
}
|
|
257
|
+
var SAFARI_AI = 'AI here works in Chrome or in Lamp, or with Lamp\'s command line: lamp play and this page\'s link.';
|
|
258
|
+
|
|
259
|
+
// Reaching this computer makes the browser ask the person about it, so a shared page doesn't reach out just to
|
|
260
|
+
// see whether there's an AI: only once the browser already lets it, the page has paired before, or the person
|
|
261
|
+
// has asked for it (LampAI.connect, from a button, or a real AI call).
|
|
262
|
+
var nearbyAsked = false;
|
|
263
|
+
function nearbyAllowed() {
|
|
264
|
+
// Connected before in this browser (kept with the page's saved things): the browser has had its say already.
|
|
265
|
+
if (nearbyAsked || read('nearby.token', null) || read('nearby.connected', false)) return Promise.resolve(true);
|
|
266
|
+
var permissions = window.navigator && navigator.permissions;
|
|
267
|
+
if (!permissions || typeof permissions.query !== 'function') return Promise.resolve(false);
|
|
268
|
+
// Chrome has named this permission more than one way; any that's granted will do, and one it doesn't know is unknown.
|
|
269
|
+
var names = ['local-network-access', 'loopback-network', 'local-network'];
|
|
270
|
+
return Promise.all(names.map(function (name) {
|
|
271
|
+
try {
|
|
272
|
+
return Promise.resolve(permissions.query({ name: name })).then(function (s) { return !!s && s.state === 'granted'; }, function () { return false; });
|
|
273
|
+
} catch (e) { return false; }
|
|
274
|
+
})).then(function (states) { return states.indexOf(true) !== -1; });
|
|
275
|
+
}
|
|
276
|
+
|
|
257
277
|
function nearby(path, body, timeout, deadline) {
|
|
258
278
|
var text = JSON.stringify(body);
|
|
259
279
|
function go(t) { return exchange(NEARBY, path, t, text, deadline ? Math.max(1000, deadline - Date.now()) : timeout); }
|
|
@@ -276,7 +296,7 @@
|
|
|
276
296
|
return done.then(null, function (e) {
|
|
277
297
|
if (e instanceof Failure) throw e;
|
|
278
298
|
// Safari never lets a page on the web reach this computer this way, so Lamp or lamp ai open here wouldn't help.
|
|
279
|
-
if (safari()) throw new Failure(
|
|
299
|
+
if (safari()) throw new Failure(SAFARI_AI, null, AI_HELP);
|
|
280
300
|
throw new Failure('AI isn\'t available here. Open Lamp on this computer, or with Lamp\'s command line, run lamp ai.', null, AI_HELP);
|
|
281
301
|
});
|
|
282
302
|
}
|
|
@@ -738,31 +758,58 @@
|
|
|
738
758
|
* a feature depends on it, and show the feature with its `reason` as it's given rather than words of the app's
|
|
739
759
|
* own, or hiding it or failing at it, and where it gives `help` (a page away from Lamp), a link to it beside the
|
|
740
760
|
* reason: "How to turn on AI". A call that fails for that reason has `needsSetup`, or `help`, set on its error.
|
|
761
|
+
* On a page shared on lamp.dev that hasn't reached this computer yet, `status` asks nothing and answers
|
|
762
|
+
* `connect: true`: show a small "Connect AI" button beside its reason that calls `LampAI.connect(capability)` with
|
|
763
|
+
* the same kind of AI (`connect('pictures')` for pictures), and use what that gives as `status`'s answer. Never
|
|
764
|
+
* call `connect()` as the page loads.
|
|
741
765
|
*/
|
|
742
766
|
var LampAI = {
|
|
743
767
|
Failure: Failure,
|
|
744
768
|
|
|
745
769
|
/** Whether this kind of AI ('text', 'decisions', 'pictures', 'act' or 'hear') can answer now: { available, reason,
|
|
746
|
-
* help }, where `reason` is the short line to show when it can't, written for where the page is,
|
|
747
|
-
* it's there, the page that says how to turn AI on, to link to beside it
|
|
770
|
+
* help, connect }, where `reason` is the short line to show when it can't, written for where the page is, `help`,
|
|
771
|
+
* when it's there, the page that says how to turn AI on, to link to beside it, and `connect`, when true, that
|
|
772
|
+
* a button calling `LampAI.connect()` would find out (a page on lamp.dev, before it has reached this computer). */
|
|
748
773
|
status: function (capability) {
|
|
749
774
|
var no = function (reason, help) {
|
|
750
775
|
var said = { available: false, reason: reason || 'AI isn\'t set up for this here.' };
|
|
751
776
|
if (help) said.help = help;
|
|
752
777
|
return said;
|
|
753
778
|
};
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
return
|
|
779
|
+
if (!bridge && shared()) {
|
|
780
|
+
return nearbyAllowed().then(function (allowed) {
|
|
781
|
+
if (allowed) return ask(capability);
|
|
782
|
+
if (safari()) return no(SAFARI_AI, AI_HELP);
|
|
783
|
+
return { available: false, connect: true, reason: 'Connect to the AI on this computer to use this.', help: AI_HELP };
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
return ask(capability);
|
|
787
|
+
function ask(capability) {
|
|
788
|
+
// 'act' is asked of /ai/act itself: available only when this lamp.js has it, the app around the page lets the
|
|
789
|
+
// route through (an older one answers Not found) and the AI that answers the app can take it. 'hear' the same.
|
|
790
|
+
if (capability === 'act' || capability === 'hear') {
|
|
791
|
+
return post('/ai/' + capability, { probe: true }, 10000).then(function (reply) {
|
|
792
|
+
return reply[capability] === true ? { available: true, reason: '' } : no(reply.reason || reply.why);
|
|
793
|
+
}, function (e) { return no(e && e.message, e && e.help); });
|
|
794
|
+
}
|
|
795
|
+
return post('/ai/status', {}, 10000).then(function (reply) {
|
|
796
|
+
if (reply[capability] === true) return { available: true, reason: '' };
|
|
797
|
+
var detail = reply.details && reply.details[capability];
|
|
798
|
+
return no(detail && detail.reason);
|
|
759
799
|
}, function (e) { return no(e && e.message, e && e.help); });
|
|
760
800
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
801
|
+
},
|
|
802
|
+
|
|
803
|
+
/** On a page shared on lamp.dev, reaches the AI on this computer, so the browser asks the person then: call it
|
|
804
|
+
* from a button ("Connect AI") shown when `status` answers `connect: true`, never as the page loads. Gives what
|
|
805
|
+
* `status` gives for that kind of AI ('text' unless named). Anywhere else it's just `status`. */
|
|
806
|
+
connect: function (capability) {
|
|
807
|
+
nearbyAsked = true;
|
|
808
|
+
return LampAI.status(capability || 'text').then(function (said) {
|
|
809
|
+
// Reached it (whatever it said of this kind of AI): later visits look without asking first.
|
|
810
|
+
if (said.available || !said.help) write('nearby.connected', true);
|
|
811
|
+
return said;
|
|
812
|
+
});
|
|
766
813
|
},
|
|
767
814
|
|
|
768
815
|
/** Whether this kind of AI can answer now, as `status` says. */
|