@quo-systems/dock 0.2.3 → 0.2.5
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/beings/carry.ts +7 -3
- package/beings/desk.ts +5 -1
- package/beings/look.ts +12 -3
- package/beings/quo-dock.md +22 -13
- package/beings/user.ts +20 -6
- package/dist/beings/carry.js +10 -4
- package/dist/beings/desk.d.ts +0 -1
- package/dist/beings/desk.js +5 -1
- package/dist/beings/look.d.ts +2 -0
- package/dist/beings/look.js +9 -0
- package/dist/beings/user.d.ts +24 -0
- package/dist/beings/user.js +20 -6
- package/dist/harbor/browser.d.ts +4 -4
- package/dist/harbor/browser.js +18 -9
- package/dist/harbor/capacitor.d.ts +19 -0
- package/dist/harbor/capacitor.js +151 -0
- package/dist/harbor/disk.js +8 -3
- package/dist/harbor/edge/edge.js +2 -1
- package/dist/harbor/edge/exercise.js +2 -1
- package/dist/harbor/edge/storage.d.ts +0 -3
- package/dist/harbor/edge/storage.js +6 -25
- package/dist/harbor/files.d.ts +2 -1
- package/dist/harbor/files.js +87 -22
- package/dist/harbor/seal.d.ts +3 -0
- package/dist/harbor/seal.js +25 -0
- package/dist/human/html.d.ts +1 -0
- package/dist/human/html.js +40 -19
- package/dist/human/local.d.ts +10 -0
- package/dist/human/local.js +10 -0
- package/dist/human/screen.js +32 -18
- package/dist/human/tab.d.ts +1 -0
- package/dist/human/tab.js +59 -3
- package/dist/human/web.d.ts +1 -0
- package/dist/human/web.js +34 -4
- package/dist/mcp/runner.js +5 -3
- package/dist/mcp/server.js +5 -3
- package/harbor/browser.ts +28 -17
- package/harbor/capacitor.ts +159 -0
- package/harbor/disk.ts +8 -3
- package/harbor/edge/edge.ts +2 -1
- package/harbor/edge/exercise.ts +2 -1
- package/harbor/edge/storage.ts +6 -25
- package/harbor/files.ts +79 -19
- package/harbor/quo-harbor.md +66 -14
- package/harbor/seal.ts +26 -0
- package/human/html.ts +39 -18
- package/human/local.ts +26 -0
- package/human/quo-human.md +70 -13
- package/human/screen.ts +27 -15
- package/human/tab.ts +50 -5
- package/human/web.ts +34 -5
- package/mcp/runner.ts +5 -3
- package/mcp/server.ts +5 -3
- package/package.json +14 -2
package/human/web.ts
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
24
24
|
import { existsSync } from 'node:fs';
|
|
25
25
|
import { fileURLToPath } from 'node:url';
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import process from 'node:process';
|
|
26
28
|
import { build } from 'esbuild';
|
|
27
29
|
import type { DiskHarbor, Hosted } from '../harbor/disk.ts';
|
|
28
30
|
import { door } from './door.ts';
|
|
@@ -33,16 +35,28 @@ export type WebOptions = {
|
|
|
33
35
|
};
|
|
34
36
|
|
|
35
37
|
// The paths under `/web` that are the exchange's, never a ward's name.
|
|
36
|
-
const RESERVED_PATHS = new Set(['allow', 'tab.js']);
|
|
38
|
+
const RESERVED_PATHS = new Set(['allow', 'tab.js', 'beings.js']);
|
|
39
|
+
|
|
40
|
+
// The world's code for the tab: a module in the harbor folder, the twin of
|
|
41
|
+
// `classes/index.ts`, exporting by name the classes the tab boots into its
|
|
42
|
+
// local ward for a world of this harbor. Served bundled as `beings.js` on
|
|
43
|
+
// this origin, and one origin is one world's code.
|
|
44
|
+
export const TAB_CODE = 'tab/index.ts';
|
|
37
45
|
|
|
38
46
|
export function webRoute(harbor: DiskHarbor, o: WebOptions): (req: IncomingMessage, res: ServerResponse, rest: string) => Promise<boolean> {
|
|
39
47
|
const tabEntry = () => {
|
|
40
48
|
const js = fileURLToPath(new URL('./tab.js', import.meta.url));
|
|
41
49
|
return existsSync(js) ? js : fileURLToPath(new URL('./tab.ts', import.meta.url));
|
|
42
50
|
};
|
|
51
|
+
const beingsEntry = join(harbor.dir, TAB_CODE);
|
|
52
|
+
const bundled = (entry: string) =>
|
|
53
|
+
// the entry may sit in a harbor folder with no node_modules of its own, as on a droplet whose
|
|
54
|
+
// estate folder is the working directory: what it imports is resolved from there too
|
|
55
|
+
build({ entryPoints: [entry], bundle: true, format: 'esm', platform: 'browser', target: 'es2023', write: false, nodePaths: [join(process.cwd(), 'node_modules')] }).then((out) => out.outputFiles[0]!.text);
|
|
43
56
|
let bundle: Promise<string> | undefined;
|
|
44
|
-
const built = () =>
|
|
45
|
-
|
|
57
|
+
const built = () => (bundle ??= bundled(tabEntry()));
|
|
58
|
+
let beings: Promise<string> | undefined;
|
|
59
|
+
const builtBeings = () => (beings ??= bundled(beingsEntry));
|
|
46
60
|
const quoOrigin = (() => {
|
|
47
61
|
try {
|
|
48
62
|
const u = new URL(o.at.quo);
|
|
@@ -71,7 +85,7 @@ export function webRoute(harbor: DiskHarbor, o: WebOptions): (req: IncomingMessa
|
|
|
71
85
|
};
|
|
72
86
|
const wards = () => Object.fromEntries([...harbor.wards].map(([n, h]) => [n, { pk: h.pk, public: publicOf(h) !== null }]));
|
|
73
87
|
const tab = (ward?: string) => {
|
|
74
|
-
const cfg = { quo: o.at.quo, web: o.at.web, wards: wards(), ...(ward ? { ward } : {}) };
|
|
88
|
+
const cfg = { quo: o.at.quo, web: o.at.web, wards: wards(), beings: existsSync(beingsEntry), ...(ward ? { ward } : {}) };
|
|
75
89
|
return `<script id="quo" type="application/json">${JSON.stringify(cfg).replace(/</g, '\\u003c')}</script><script type="module" src="${o.at.web}/tab.js"></script>`;
|
|
76
90
|
};
|
|
77
91
|
return async (req, res, rest) => {
|
|
@@ -88,6 +102,13 @@ export function webRoute(harbor: DiskHarbor, o: WebOptions): (req: IncomingMessa
|
|
|
88
102
|
res.end(await built());
|
|
89
103
|
return true;
|
|
90
104
|
}
|
|
105
|
+
// the world's code for the tab, when the harbor folder holds any; a harbor with none has no such path
|
|
106
|
+
if (rest === '/beings.js' && req.method === 'GET') {
|
|
107
|
+
if (!existsSync(beingsEntry)) return false;
|
|
108
|
+
res.writeHead(200, { 'content-type': 'text/javascript; charset=utf-8', 'cache-control': 'no-store' });
|
|
109
|
+
res.end(await builtBeings());
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
91
112
|
const wardName = parts[0] ?? '';
|
|
92
113
|
if (!wardName || RESERVED_PATHS.has(wardName)) return false;
|
|
93
114
|
const hosted = harbor.wards.get(wardName);
|
|
@@ -104,18 +125,26 @@ const CSS = [
|
|
|
104
125
|
':root{color-scheme:light dark;--accent:#3b6ef5;--bg:transparent;--fg:inherit;--font:system-ui,sans-serif;--radius:6px;--ink:#1c1b22;--paper:#fbfaf7;--mute:#6b6a73}',
|
|
105
126
|
'@media(prefers-color-scheme:dark){:root{--ink:#ecebe6;--paper:#141318;--mute:#9a99a2}}',
|
|
106
127
|
'html{background:var(--paper);color:var(--ink)}',
|
|
107
|
-
|
|
128
|
+
// one column that is a phone first and a desk after: the same page, more air
|
|
129
|
+
'body{font:16px/1.5 system-ui,sans-serif;max-width:40rem;margin:1rem auto;padding:0 1rem}@media(min-width:40rem){body{margin:2rem auto}}',
|
|
130
|
+
'input:not([type=checkbox]),select,textarea{width:100%;max-width:24rem;box-sizing:border-box}',
|
|
108
131
|
'nav.worlds{display:flex;flex-wrap:wrap;gap:.5rem 1rem;font-size:.9rem;opacity:.8}nav.worlds a[aria-current]{font-weight:600}',
|
|
109
132
|
'nav.relations{display:flex;flex-wrap:wrap;gap:.25rem;margin:.5rem 0}nav.relations button{background:transparent;color:inherit;border:1px solid color-mix(in srgb,currentColor 30%,transparent)}nav.relations button[aria-current]{border-color:var(--accent);font-weight:600}',
|
|
110
133
|
'header,main{background:var(--bg);color:var(--fg);font-family:var(--font)}header{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem 1rem}header .notice{width:100%;margin:0}',
|
|
111
134
|
'input,select,textarea,button{font:inherit;padding:.4rem;margin:.2rem;border-radius:var(--radius)}button{background:var(--accent);color:#fff;border:0;padding:.4rem .9rem}',
|
|
112
135
|
'fieldset{border:1px solid color-mix(in srgb,currentColor 20%,transparent);border-radius:var(--radius);margin:.5rem 0}',
|
|
136
|
+
// a being of the world running in the tab: her own section under the far page
|
|
137
|
+
'section.local{border-top:1px solid color-mix(in srgb,currentColor 15%,transparent);margin:1.5rem 0;padding-top:.5rem}section.local header:empty{display:none}',
|
|
113
138
|
'section.standing{background:var(--bg);color:var(--fg);font-family:var(--font);border-left:4px solid var(--accent);border-radius:var(--radius);padding:.25rem 1rem;margin:1.5rem 0}section.standing h2{display:flex;align-items:center;gap:.5rem;font-size:1.1rem}img.logo{height:1.6rem}',
|
|
114
139
|
'table{border-collapse:collapse}td,th{padding:.15rem .5rem;text-align:left}.answer{margin:.5rem 0 1rem;padding:.5rem;border-left:3px solid var(--accent)}.answer.error{border-color:#c33}.answer.silence,.answer.word,.answer.unreached{border-color:#c93}pre{padding:.75rem;overflow:auto}',
|
|
115
140
|
// a page in the grammar: regions, roles, cards
|
|
116
141
|
'.page .stack{display:flex;flex-direction:column;gap:.5rem}.page .row{display:flex;flex-wrap:wrap;gap:.5rem 1rem;align-items:baseline}',
|
|
117
142
|
'.page .t-title{font-size:2rem;font-weight:600;letter-spacing:-.02em;margin:.5rem 0}.page .t-lead{font-size:1.25rem;margin:.25rem 0}.page .t-label{font-size:.8rem;letter-spacing:.06em;text-transform:uppercase;color:var(--mute);margin:0}.page .t-quiet{color:var(--mute);font-size:.9rem}.page .t-body{margin:.25rem 0}',
|
|
118
143
|
'.page .picture{max-height:6rem}.cards{display:flex;flex-wrap:wrap;gap:.75rem}.card{border:1px solid color-mix(in srgb,currentColor 20%,transparent);border-radius:var(--radius);padding:.5rem .75rem}',
|
|
144
|
+
// a row with a picture is a line about someone or something: the picture small and the words beside it, centred
|
|
145
|
+
'.page .row:has(>.picture){align-items:center;flex-wrap:nowrap}.page .row>.picture{max-height:2.75rem;flex:none}.page .row>.stack{gap:0;min-width:0}.page .row>.stack>p{margin:0}',
|
|
146
|
+
// a far page inside a standing's section: her title is a section's, not the page's
|
|
147
|
+
'section.standing .page{padding:.5rem 0}section.standing .page .t-title{font-size:1.3rem;margin:.25rem 0}',
|
|
119
148
|
// the door page: one column, generous air, the mark, a word, a sentence
|
|
120
149
|
'main.door{min-height:70vh;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;max-width:32rem;margin:0 auto;padding:3rem 0;font-family:ui-serif,Georgia,"Times New Roman",serif}',
|
|
121
150
|
'main.door .picture{width:3.5rem;height:3.5rem;color:var(--ink);opacity:.9;margin-bottom:1.25rem}',
|
package/mcp/runner.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import type { Blueprint, JsonObject } from '@quo-systems/quo';
|
|
14
14
|
import type { Avatar } from '../beings/avatar.ts';
|
|
15
15
|
import { word, wordText, SILENCE_TEXT, UNREACHED_TEXT, type Serving } from '../beings/side.ts';
|
|
16
|
+
import { presentation } from '../beings/look.ts';
|
|
16
17
|
|
|
17
18
|
// Where the model is. `key` is a bearer for endpoints that want one, read
|
|
18
19
|
// from the device and never from cells. `turns` is the ceiling of model
|
|
@@ -32,11 +33,12 @@ type Completion = { choices: { message: { role: 'assistant'; content: string | n
|
|
|
32
33
|
// Her describe, spoken as a tools array. Name, description and input are
|
|
33
34
|
// verbatim, with the one narrowing an endpoint has asked for: an ask that
|
|
34
35
|
// declares no properties is sent with an empty `properties`, because
|
|
35
|
-
// LM Studio refuses a parameters schema without one.
|
|
36
|
-
// presentation, for a screen, and are not functions.
|
|
36
|
+
// LM Studio refuses a parameters schema without one. Her look, her page and
|
|
37
|
+
// every carried page are presentation, for a screen, and are not functions.
|
|
37
38
|
export function tools(bp: Blueprint): Tool[] {
|
|
39
|
+
const shown = presentation(bp);
|
|
38
40
|
return bp.asks
|
|
39
|
-
.filter((a) =>
|
|
41
|
+
.filter((a) => !shown.has(a.name))
|
|
40
42
|
.map((a) => {
|
|
41
43
|
const t: Tool = { type: 'function', function: { name: a.name, parameters: { properties: {}, ...a.input, type: 'object' } } };
|
|
42
44
|
if (a.description !== undefined) t.function.description = a.description;
|
package/mcp/server.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
|
13
13
|
import { ListToolsRequestSchema, CallToolRequestSchema, type CallToolResult, type Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
14
14
|
import type { Blueprint, JsonObject } from '@quo-systems/quo';
|
|
15
15
|
import { word, wordText, SILENCE_TEXT, UNREACHED_TEXT, type Serving, type Subject } from '../beings/side.ts';
|
|
16
|
-
import { hintFor, sanitise, type Look } from '../beings/look.ts';
|
|
16
|
+
import { hintFor, presentation, sanitise, type Look } from '../beings/look.ts';
|
|
17
17
|
import { isSilence, isWord, digest } from '@quo-systems/quo';
|
|
18
18
|
import type { Json } from '@quo-systems/quo';
|
|
19
19
|
|
|
@@ -28,10 +28,12 @@ export const DESCRIBE: Tool = { name: 'describe', description: 'the empty ask: h
|
|
|
28
28
|
// Her describe, spoken as tools, the empty ask first. Name, description and
|
|
29
29
|
// input are verbatim; an output schema crosses when she declared one. Her
|
|
30
30
|
// look, when she has one, is the hints: a title, and the annotations a host
|
|
31
|
-
// reads.
|
|
31
|
+
// reads. Her look, her page and every carried page are presentation, for a
|
|
32
|
+
// screen, and are not tools.
|
|
32
33
|
export function tools(bp: Blueprint, look: Look = {}): Tool[] {
|
|
34
|
+
const shown = presentation(bp);
|
|
33
35
|
const asks = bp.asks
|
|
34
|
-
.filter((a) =>
|
|
36
|
+
.filter((a) => !shown.has(a.name))
|
|
35
37
|
.map((a) => {
|
|
36
38
|
const t: Tool = { name: a.name, inputSchema: { ...a.input, type: 'object' } };
|
|
37
39
|
if (a.description !== undefined) t.description = a.description;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quo-systems/dock",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "The dock: what every estate on Quo needs and nobody writes twice. A daemon and the quo command, the front desk, the user being and the avatar, harbors on disk, in a tab and on the edge, the model sides and the screen.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quo",
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"types": "./dist/harbor/browser.d.ts",
|
|
32
32
|
"default": "./dist/harbor/browser.js"
|
|
33
33
|
},
|
|
34
|
+
"./harbor/capacitor": {
|
|
35
|
+
"types": "./dist/harbor/capacitor.d.ts",
|
|
36
|
+
"default": "./dist/harbor/capacitor.js"
|
|
37
|
+
},
|
|
34
38
|
"./cli/daemon": {
|
|
35
39
|
"types": "./dist/cli/daemon.d.ts",
|
|
36
40
|
"default": "./dist/cli/daemon.js"
|
|
@@ -55,6 +59,10 @@
|
|
|
55
59
|
"types": "./dist/human/html.d.ts",
|
|
56
60
|
"default": "./dist/human/html.js"
|
|
57
61
|
},
|
|
62
|
+
"./human/local": {
|
|
63
|
+
"types": "./dist/human/local.d.ts",
|
|
64
|
+
"default": "./dist/human/local.js"
|
|
65
|
+
},
|
|
58
66
|
"./harbor/edge/worker": {
|
|
59
67
|
"types": "./dist/harbor/edge/worker.d.ts",
|
|
60
68
|
"default": "./dist/harbor/edge/worker.js"
|
|
@@ -67,8 +75,12 @@
|
|
|
67
75
|
"prepublishOnly": "cd ../.. && npm run check && npm run check:terrain"
|
|
68
76
|
},
|
|
69
77
|
"dependencies": {
|
|
78
|
+
"@aparajita/capacitor-secure-storage": "^8.0.0",
|
|
79
|
+
"@capacitor/app": "^8.1.1",
|
|
80
|
+
"@capacitor/core": "^8.5.1",
|
|
81
|
+
"@capacitor/filesystem": "^8.1.3",
|
|
70
82
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
71
|
-
"@quo-systems/quo": "^0.2.
|
|
83
|
+
"@quo-systems/quo": "^0.2.5",
|
|
72
84
|
"esbuild": "^0.28.2",
|
|
73
85
|
"ws": "^8.21.3"
|
|
74
86
|
},
|