@bongos/core 1.24.16 → 1.24.18
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/.bongos-core.json +53 -33
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +7 -1
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/api/openapi.json +119 -4
- package/docs/api-reference.md +4 -2
- package/docs/architecture.md +15 -0
- package/docs/copy-inventory.md +4 -6
- package/docs/copy-registry.json +5 -23
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +8 -8
- package/migrations/core_290_builder_payouts.sql +86 -0
- package/modules/ai-studio/keys.js +4 -0
- package/modules/ai-studio/routes/ai-studio.js +1 -1
- package/modules/economy/module.json +1 -1
- package/modules/economy/routes/project-account.js +10 -1
- package/modules/hall-ui/public/ai-studio-lib.js +16 -10
- package/modules/hall-ui/public/ai-studio.js +8 -8
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/bongos/run-unit-tests.js +4 -0
- package/src/bongos/builder-payouts.js +264 -0
- package/src/bongos/routes/payout-approvals.js +41 -1
- package/src/module-api.js +1 -1
- package/tests/ai_studio_generate.mjs +2 -0
- package/tests/builder_payouts.mjs +295 -0
- package/tests/builder_payouts_db.mjs +109 -0
- package/tests/hall_ai_studio.mjs +15 -0
package/docs/copy-registry.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"generator": "scripts/bongos/copy-inventory.js",
|
|
4
4
|
"goal": 1000074,
|
|
5
5
|
"counts": {
|
|
6
|
-
"strings":
|
|
7
|
-
"unique_ids":
|
|
8
|
-
"certain":
|
|
6
|
+
"strings": 3276,
|
|
7
|
+
"unique_ids": 2593,
|
|
8
|
+
"certain": 3276,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 275
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 172,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 2163
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -7694,7 +7694,7 @@
|
|
|
7694
7694
|
"surface": "builders-hall",
|
|
7695
7695
|
"text": "Free here. {…} bills you for it directly.",
|
|
7696
7696
|
"file": "modules/hall-ui/public/ai-studio.js",
|
|
7697
|
-
"line":
|
|
7697
|
+
"line": 336,
|
|
7698
7698
|
"origin": "js-markup",
|
|
7699
7699
|
"confidence": "certain"
|
|
7700
7700
|
},
|
|
@@ -10200,15 +10200,6 @@
|
|
|
10200
10200
|
"origin": "js-markup",
|
|
10201
10201
|
"confidence": "certain"
|
|
10202
10202
|
},
|
|
10203
|
-
{
|
|
10204
|
-
"id": "b7d4eeb3f762",
|
|
10205
|
-
"surface": "builders-hall",
|
|
10206
|
-
"text": "My own key",
|
|
10207
|
-
"file": "modules/hall-ui/public/ai-studio.js",
|
|
10208
|
-
"line": 334,
|
|
10209
|
-
"origin": "js-markup",
|
|
10210
|
-
"confidence": "certain"
|
|
10211
|
-
},
|
|
10212
10203
|
{
|
|
10213
10204
|
"id": "3d0d554cd684",
|
|
10214
10205
|
"surface": "builders-hall",
|
|
@@ -21693,15 +21684,6 @@
|
|
|
21693
21684
|
"origin": "js-markup",
|
|
21694
21685
|
"confidence": "certain"
|
|
21695
21686
|
},
|
|
21696
|
-
{
|
|
21697
|
-
"id": "c80c3434ae36",
|
|
21698
|
-
"surface": "builders-hall",
|
|
21699
|
-
"text": "{…} key ending {…}",
|
|
21700
|
-
"file": "modules/hall-ui/public/ai-studio.js",
|
|
21701
|
-
"line": 334,
|
|
21702
|
-
"origin": "js-markup",
|
|
21703
|
-
"confidence": "certain"
|
|
21704
|
-
},
|
|
21705
21687
|
{
|
|
21706
21688
|
"id": "5ad81ff61920",
|
|
21707
21689
|
"surface": "builders-hall",
|
|
@@ -3288,5 +3288,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3288
3288
|
landed since 1.24.13 with no explicit bump. run 41110951037. (task 1002620)
|
|
3289
3289
|
1.24.16 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3290
3290
|
landed since 1.24.15 with no explicit bump. run 41124515103. (task 1002620)
|
|
3291
|
+
1.24.17 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3292
|
+
landed since 1.24.16 with no explicit bump. run 41129472650. (task 1002620)
|
|
3293
|
+
1.24.18 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3294
|
+
landed since 1.24.17 with no explicit bump. run 41131020363. (task 1002620)
|
|
3291
3295
|
---------------------------------------------------------------------------
|
|
3292
3296
|
```
|
package/docs/page-readings.json
CHANGED
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
"adr": "0341 D3",
|
|
6
6
|
"reread": "node scripts/bongos/page-reader.js --stale",
|
|
7
7
|
"how": ["Generated by node scripts/bongos/page-reader.js (BV2.TW04, task 1004315; ADR 0341 D3). Do not hand-edit.","One entry per page of docs/page-inventory.json. Lines are in page order: the default state first, and a line only another state shows sits where that state shows it, with its state named.","placement: placed = a docs/copy-registry.json row renders it (file, line, string_id); shared = shell text every page of the surface carries (top bar, sidebar, footer), with its row when the registry has one; unplaced = the registry cannot place it (a rewrite becomes an engineer task, ADR 0341 D4).","text keeps {…} where the registry row has a hole, or where a value the page read from its API was filled in. A string that is only such data is counted under omitted.data, not listed.","reading_hash is over the non-shared lines (key, section, text); files_hash is over the page's inventory files. --check compares files_hash only and never opens a browser.","Rendered through the hall-preview harness (hall) and the ui-design kit stub (landing, status), at 1280x800 with the clock pinned, so a reading is the fixture world's page."],
|
|
8
|
-
"counts": {"pages":52,"lines":5612,"placed":
|
|
8
|
+
"counts": {"pages":52,"lines":5612,"placed":1753,"shared":461,"unplaced":3398,"omitted_data":3766},
|
|
9
9
|
"pages": [
|
|
10
10
|
{
|
|
11
11
|
"id": "builders:ai-studio",
|
|
12
12
|
"surface": "builders",
|
|
13
13
|
"title": "Studio",
|
|
14
14
|
"states_read": ["pick","describe","make","waiting","result","result-own","failed","balance","balance-all","out"],
|
|
15
|
-
"files_hash": "
|
|
16
|
-
"reading_hash": "
|
|
17
|
-
"counts": {"lines":102,"placed":
|
|
15
|
+
"files_hash": "48c0f2c2c104a63b",
|
|
16
|
+
"reading_hash": "475acccd2681bd7f",
|
|
17
|
+
"counts": {"lines":102,"placed":38,"shared":8,"unplaced":56},
|
|
18
18
|
"omitted": {"data":15},
|
|
19
19
|
"notes": [],
|
|
20
20
|
"lines": [
|
|
@@ -84,9 +84,9 @@
|
|
|
84
84
|
{"key":"L0064","section":"How do you want to pay?","text":"PDF","placement":"unplaced","state":"make"},
|
|
85
85
|
{"key":"L0065","section":"How do you want to pay?","text":"“Turn the sketch into a finished hero picture for the landing pa…”","placement":"unplaced","state":"make"},
|
|
86
86
|
{"key":"L0066","section":"How do you want to pay?","text":"change","placement":"unplaced","state":"make"},
|
|
87
|
-
{"key":"L0067","section":"How do you want to pay?","text":"My own key","placement":"
|
|
88
|
-
{"key":"L0068","section":"How do you want to pay?","text":"Free here. {…} bills you for it directly.","placement":"placed","file":"modules/hall-ui/public/ai-studio.js","line":
|
|
89
|
-
{"key":"L0069","section":"How do you want to pay?","text":"
|
|
87
|
+
{"key":"L0067","section":"How do you want to pay?","text":"My own key","placement":"unplaced","state":"make"},
|
|
88
|
+
{"key":"L0068","section":"How do you want to pay?","text":"Free here. {…} bills you for it directly.","placement":"placed","file":"modules/hall-ui/public/ai-studio.js","line":336,"string_id":"e9a1af5dc515","state":"make"},
|
|
89
|
+
{"key":"L0069","section":"How do you want to pay?","text":"Gemini key ending {…}","placement":"unplaced","state":"make"},
|
|
90
90
|
{"key":"L0070","section":"How do you want to pay?","text":"My balance","placement":"placed","file":"modules/hall-ui/public/ai-studio.js","line":340,"string_id":"39afdc03594a","state":"make"},
|
|
91
91
|
{"key":"L0071","section":"How do you want to pay?","text":"Usually a few cents. Up to {…} is set aside, and the rest comes back.","placement":"placed","file":"modules/hall-ui/public/ai-studio.js","line":340,"string_id":"664555b65658","state":"make"},
|
|
92
92
|
{"key":"L0072","section":"How do you want to pay?","text":"{…} left","placement":"placed","file":"modules/hall-ui/public/market-watch.js","line":73,"string_id":"92b79edf21f6","state":"make"},
|
|
@@ -4216,7 +4216,7 @@
|
|
|
4216
4216
|
"surface": "builders",
|
|
4217
4217
|
"title": "Studio",
|
|
4218
4218
|
"states_read": ["home","out","flow","flow-pick","status","words-offer","words-edit","reviews","full","full-back","approvals","approvals-before","approvals-phone","approvals-note","approved","sent-back","approvals-page"],
|
|
4219
|
-
"files_hash": "
|
|
4219
|
+
"files_hash": "6c591072e20de9f0",
|
|
4220
4220
|
"reading_hash": "bcba11e6cef56218",
|
|
4221
4221
|
"counts": {"lines":31,"placed":16,"shared":6,"unplaced":9},
|
|
4222
4222
|
"omitted": {"data":4},
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
-- core_290_builder_payouts.sql — a builder is paid for a task, exactly once, and a payout
|
|
2
|
+
-- that did not go through still says it is owed (task 1003183, goal 1000092 M04;
|
|
3
|
+
-- ADR 0385 decisions 1 and 4, ADR 0397 decisions 2, 5 and 7).
|
|
4
|
+
--
|
|
5
|
+
-- TWO TABLES, BOTH APPEND-ONLY.
|
|
6
|
+
--
|
|
7
|
+
-- builder_payouts_owed one row per (builder, task): what is owed, and the
|
|
8
|
+
-- personal approval that authorises it. Written BEFORE
|
|
9
|
+
-- the processor is asked to move anything, in its own
|
|
10
|
+
-- committed step, so a crash, a refusal or a timeout
|
|
11
|
+
-- afterwards still leaves the debt on record. The failure
|
|
12
|
+
-- this exists to prevent is a builder who was silently
|
|
13
|
+
-- never paid, with no trace anything was due.
|
|
14
|
+
-- builder_payout_attempts every try at paying one owed row: sent (with the
|
|
15
|
+
-- processor's transfer reference), failed (with the
|
|
16
|
+
-- reason), or held (not sent, with the reason: a gate,
|
|
17
|
+
-- no processor connected, a missing approval).
|
|
18
|
+
--
|
|
19
|
+
-- EXACTLY ONCE is three rules, each enforced here, not only by the code:
|
|
20
|
+
-- * one owed row per (builder, task), and one per approval (an approval pays one
|
|
21
|
+
-- thing, never two tasks);
|
|
22
|
+
-- * at most one `sent` attempt per owed row;
|
|
23
|
+
-- * the processor is called with an idempotency key derived from the owed row's id,
|
|
24
|
+
-- so a retry after an unknown outcome is answered by the processor with the same
|
|
25
|
+
-- transfer, not a second one.
|
|
26
|
+
--
|
|
27
|
+
-- Amounts are integer cents, as the approval that authorises them is. The money
|
|
28
|
+
-- itself is held by the processor (ADR 0397); the matching `payout` movement is booked
|
|
29
|
+
-- through the economy module's `economy.account` port, in micro-dollars, in the same step as `sent`.
|
|
30
|
+
|
|
31
|
+
CREATE TABLE IF NOT EXISTS builder_payouts_owed (
|
|
32
|
+
id BIGSERIAL PRIMARY KEY,
|
|
33
|
+
builder_id BIGINT NOT NULL REFERENCES builders(id),
|
|
34
|
+
task_id BIGINT NOT NULL REFERENCES tasks(id),
|
|
35
|
+
approval_id BIGINT NOT NULL REFERENCES payout_approvals(id),
|
|
36
|
+
amount_cents BIGINT NOT NULL,
|
|
37
|
+
currency TEXT NOT NULL,
|
|
38
|
+
recorded_by BIGINT NOT NULL REFERENCES builders(id),
|
|
39
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
40
|
+
CONSTRAINT builder_payouts_owed_amount_chk CHECK (amount_cents > 0),
|
|
41
|
+
CONSTRAINT builder_payouts_owed_currency_chk CHECK (currency ~ '^[A-Z]{3}$')
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
-- one payout per builder per task, and one task per approval
|
|
45
|
+
CREATE UNIQUE INDEX IF NOT EXISTS builder_payouts_owed_one_per_task
|
|
46
|
+
ON builder_payouts_owed (builder_id, task_id);
|
|
47
|
+
CREATE UNIQUE INDEX IF NOT EXISTS builder_payouts_owed_one_per_approval
|
|
48
|
+
ON builder_payouts_owed (approval_id);
|
|
49
|
+
CREATE INDEX IF NOT EXISTS builder_payouts_owed_by_time ON builder_payouts_owed (created_at DESC, id DESC);
|
|
50
|
+
|
|
51
|
+
CREATE TABLE IF NOT EXISTS builder_payout_attempts (
|
|
52
|
+
id BIGSERIAL PRIMARY KEY,
|
|
53
|
+
owed_id BIGINT NOT NULL REFERENCES builder_payouts_owed(id),
|
|
54
|
+
outcome TEXT NOT NULL,
|
|
55
|
+
processor_ref TEXT,
|
|
56
|
+
reason TEXT,
|
|
57
|
+
actor_id BIGINT REFERENCES builders(id),
|
|
58
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
59
|
+
CONSTRAINT builder_payout_attempts_outcome_chk CHECK (outcome IN ('sent', 'failed', 'held')),
|
|
60
|
+
CONSTRAINT builder_payout_attempts_reason_len_chk CHECK (reason IS NULL OR length(reason) <= 300),
|
|
61
|
+
-- a sent attempt names the processor transfer; a failed or held one says why
|
|
62
|
+
CONSTRAINT builder_payout_attempts_cause_chk CHECK (
|
|
63
|
+
(outcome = 'sent' AND processor_ref IS NOT NULL) OR
|
|
64
|
+
(outcome IN ('failed', 'held') AND reason IS NOT NULL AND length(reason) > 0))
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
-- paid at most once
|
|
68
|
+
CREATE UNIQUE INDEX IF NOT EXISTS builder_payout_attempts_one_sent
|
|
69
|
+
ON builder_payout_attempts (owed_id) WHERE outcome = 'sent';
|
|
70
|
+
CREATE INDEX IF NOT EXISTS builder_payout_attempts_by_owed ON builder_payout_attempts (owed_id, id DESC);
|
|
71
|
+
|
|
72
|
+
CREATE OR REPLACE FUNCTION builder_payouts_append_only() RETURNS trigger AS $$
|
|
73
|
+
BEGIN
|
|
74
|
+
RAISE EXCEPTION '% is append-only: a payout is never edited or deleted, a new attempt is recorded instead', TG_TABLE_NAME;
|
|
75
|
+
END;
|
|
76
|
+
$$ LANGUAGE plpgsql;
|
|
77
|
+
|
|
78
|
+
DROP TRIGGER IF EXISTS builder_payouts_owed_no_rewrite ON builder_payouts_owed;
|
|
79
|
+
CREATE TRIGGER builder_payouts_owed_no_rewrite BEFORE UPDATE OR DELETE ON builder_payouts_owed
|
|
80
|
+
FOR EACH ROW EXECUTE FUNCTION builder_payouts_append_only();
|
|
81
|
+
DROP TRIGGER IF EXISTS builder_payout_attempts_no_rewrite ON builder_payout_attempts;
|
|
82
|
+
CREATE TRIGGER builder_payout_attempts_no_rewrite BEFORE UPDATE OR DELETE ON builder_payout_attempts
|
|
83
|
+
FOR EACH ROW EXECUTE FUNCTION builder_payouts_append_only();
|
|
84
|
+
|
|
85
|
+
INSERT INTO schema_migrations (version) VALUES ('core_290_builder_payouts')
|
|
86
|
+
ON CONFLICT DO NOTHING;
|
|
@@ -65,6 +65,10 @@ function listPublic(rows) {
|
|
|
65
65
|
function chooseKey(stored, payWith) {
|
|
66
66
|
const own = ['openrouter', 'gemini'].find((p) => (stored || []).includes(p));
|
|
67
67
|
if (payWith === 'ours') return { source: 'ours' };
|
|
68
|
+
// A named provider picks that key when both are saved.
|
|
69
|
+
if (isProvider(payWith)) {
|
|
70
|
+
return (stored || []).includes(payWith) ? { source: 'own', provider: payWith } : { error: 'no_own_key' };
|
|
71
|
+
}
|
|
68
72
|
if (payWith === 'own' && !own) return { error: 'no_own_key' };
|
|
69
73
|
if (payWith !== undefined && payWith !== null && payWith !== 'own') return { error: 'bad_pay_with' };
|
|
70
74
|
return own ? { source: 'own', provider: own } : { source: 'ours' };
|
|
@@ -173,7 +173,7 @@ module.exports = function buildAiStudioRouter() {
|
|
|
173
173
|
if (api.validateOrRespond(req, res, {
|
|
174
174
|
prompt: { required: true, type: 'string', maxLength: S.MAX_PROMPT + 200 },
|
|
175
175
|
file_ids: { required: true, type: 'array', maxItems: S.MAX_FILES },
|
|
176
|
-
pay_with: { type: 'string', enum: ['own', 'ours'] },
|
|
176
|
+
pay_with: { type: 'string', enum: ['own', 'ours', 'openrouter', 'gemini'] },
|
|
177
177
|
})) return;
|
|
178
178
|
if (!S.isAdult(await db.latestAgeTier(req.builder.id))) return res.fail('adults_only', { status: 403, message: ADULTS_ONLY });
|
|
179
179
|
const checked = S.checkRequest(req.body);
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"default": true,
|
|
9
9
|
"maintenance": { "status": "core-maintained" },
|
|
10
10
|
"contributes": { "migrations": true, "routes": ["cost", "credits", "earnings", "leaderboard", "project-account", "reward"] },
|
|
11
|
-
"provides": ["reward"],
|
|
11
|
+
"provides": ["reward", "economy.account"],
|
|
12
12
|
"consumes": []
|
|
13
13
|
}
|
|
@@ -15,11 +15,20 @@
|
|
|
15
15
|
|
|
16
16
|
const express = require('express');
|
|
17
17
|
const api = require('../../../src/module-api');
|
|
18
|
-
const { readAccount } = require('../project-account');
|
|
18
|
+
const { readAccount, recordMovement } = require('../project-account');
|
|
19
19
|
|
|
20
20
|
const log = api.logger('economy');
|
|
21
21
|
|
|
22
|
+
// The `economy.account` PORT (task 1003183): core's payout path (src/bongos/builder-payouts.js)
|
|
23
|
+
// books its `payout` movement through this, inside its own transaction, without importing the
|
|
24
|
+
// module. Registered here for the reason routes/reward.js gives: the loader runs this factory in
|
|
25
|
+
// the live server and whenever a test rebuilds the router. hasProvider-guarded for re-runs.
|
|
26
|
+
function registerSeams() {
|
|
27
|
+
if (!api.hasProvider('economy.account')) api.registerProvider('economy.account', { recordMovement });
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
module.exports = function buildProjectAccountRouter({ processor = null } = {}) {
|
|
31
|
+
registerSeams();
|
|
23
32
|
const router = express.Router();
|
|
24
33
|
|
|
25
34
|
// rank: payout.approve (archon floor) — the owner. The account's balance and who was paid what
|
|
@@ -107,25 +107,31 @@
|
|
|
107
107
|
|
|
108
108
|
// The own key the server will run on: OpenRouter first, as keys.chooseKey
|
|
109
109
|
// prefers it (it reports its cost). null when none is saved.
|
|
110
|
-
function ownKey(keys) {
|
|
110
|
+
function ownKey(keys) { return ownKeys(keys)[0] || null; }
|
|
111
|
+
|
|
112
|
+
// Every own key that is saved, OpenRouter first: step 3 offers one card each,
|
|
113
|
+
// so a builder with both picks which one runs.
|
|
114
|
+
function ownKeys(keys) {
|
|
111
115
|
const list = (keys && keys.keys) || [];
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
return ['openrouter', 'gemini']
|
|
117
|
+
.map((p) => list.find((k) => k && k.provider === p && k.set))
|
|
118
|
+
.filter(Boolean)
|
|
119
|
+
.map((k) => (k.provider === 'openrouter'
|
|
120
|
+
? { provider: 'openrouter', name: 'OpenRouter', billedBy: 'OpenRouter', last4: k.last4 }
|
|
121
|
+
: { provider: 'gemini', name: 'Gemini', billedBy: 'Google', last4: k.last4 }));
|
|
118
122
|
}
|
|
119
123
|
|
|
120
|
-
// Step 3's
|
|
124
|
+
// Step 3's cards. balance is GET /ai-studio/balance's body.
|
|
121
125
|
function payChoices(keys, balance) {
|
|
122
|
-
const
|
|
126
|
+
const owns = ownKeys(keys);
|
|
127
|
+
const own = owns[0] || null;
|
|
123
128
|
const b = balance || {};
|
|
124
129
|
const avail = b.balance ? b.balance.available_micro : null;
|
|
125
130
|
const max = isMicro(b.max_cost_micro) ? b.max_cost_micro : null;
|
|
126
131
|
const payments = !!b.balance;
|
|
127
132
|
return {
|
|
128
133
|
own,
|
|
134
|
+
owns,
|
|
129
135
|
payments,
|
|
130
136
|
setAside: max === null ? '' : usd(max),
|
|
131
137
|
left: payments ? `${balanceUsd(avail)} left` : '',
|
|
@@ -260,7 +266,7 @@
|
|
|
260
266
|
|
|
261
267
|
const lib = {
|
|
262
268
|
MAX_FILES, MAX_IMAGE_BYTES, MAX_PDF_BYTES, IMAGE_TYPES, PDF_TYPE, STEPS, TOPUP_CHOICES, ANSWER_AFTER_SEC,
|
|
263
|
-
usd, balanceUsd, sizeText, routeOf, checkFile, isImage, stepMarks, quoteLine, ownKey, payChoices,
|
|
269
|
+
usd, balanceUsd, sizeText, routeOf, checkFile, isImage, stepMarks, quoteLine, ownKey, ownKeys, payChoices,
|
|
264
270
|
waitSteps, clock, isFinished, receipt, againLine, monthSummary, ledgerLine, replyHtml, homeLine,
|
|
265
271
|
};
|
|
266
272
|
if (typeof module !== 'undefined' && module.exports) module.exports = lib;
|
|
@@ -331,12 +331,12 @@
|
|
|
331
331
|
const files = readyFiles();
|
|
332
332
|
const c = lib.payChoices(state.keys, state.balance);
|
|
333
333
|
const thumbs = files.slice(0, 3).map((f) => thumb(f)).join('');
|
|
334
|
-
const ownCard = c.
|
|
335
|
-
<button type="button" class="st-action ais-card ais-card--own" data-pay="
|
|
336
|
-
<span class="st-action__label">My own key</span>
|
|
337
|
-
<span class="ais-card__line">Free here. ${esc(
|
|
338
|
-
<span class="ais-card__foot">${esc(
|
|
339
|
-
</button>`
|
|
334
|
+
const ownCard = c.owns.map((k) => `
|
|
335
|
+
<button type="button" class="st-action ais-card ais-card--own" data-pay="${esc(k.provider)}">
|
|
336
|
+
<span class="st-action__label">${c.owns.length > 1 ? `My ${esc(k.name)} key` : 'My own key'}</span>
|
|
337
|
+
<span class="ais-card__line">Free here. ${esc(k.billedBy)} bills you for it directly.</span>
|
|
338
|
+
<span class="ais-card__foot">${c.owns.length > 1 ? '' : `${esc(k.name)} `}key ending ${esc(k.last4 || '')}</span>
|
|
339
|
+
</button>`).join('');
|
|
340
340
|
const balanceCard = c.payments ? `
|
|
341
341
|
<button type="button" class="st-action ais-card ais-card--ours" data-pay="ours" ${c.short ? 'disabled' : ''}>
|
|
342
342
|
<span class="st-action__label">My balance</span>
|
|
@@ -346,8 +346,8 @@
|
|
|
346
346
|
let foot = 'You only pay for what it really cost. If it fails, nothing is taken.';
|
|
347
347
|
let extra = '';
|
|
348
348
|
if (c.payments && c.short) extra = `<p class="ais-faint">Your balance is under ${esc(c.setAside)}. <a href="/builders/ai-studio?view=balance">Add money</a> to pay this way.</p>`;
|
|
349
|
-
if (!c.
|
|
350
|
-
if (!c.
|
|
349
|
+
if (!c.owns.length) extra += `<p class="ais-faint">Have your own OpenRouter or Gemini key? <a href="${SETTINGS_KEYS}">Add it in Settings</a> and make things for free here.</p>`;
|
|
350
|
+
if (!c.owns.length && !c.payments) foot = 'Paying from a balance is not set up here yet, so add your own key in Settings to make something.';
|
|
351
351
|
page.innerHTML = `${stepHead('make')}
|
|
352
352
|
<h1 class="ais-ask" data-head tabindex="-1">How do you want to pay?</h1>
|
|
353
353
|
<div class="ais-row ais-row--summary">${thumbs}<span class="ais-grow ais-soft">${esc(lib.quoteLine(state.draft.prompt))}</span><a class="ais-link" href="/builders/ai-studio?step=describe" data-nav="step=describe">change</a></div>
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.18",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.18",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.18",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -10981,5 +10981,17 @@
|
|
|
10981
10981
|
"id": "1005028",
|
|
10982
10982
|
"text": "Settings now has a Your age section where you record your age group, which switches on AI Studio for adults."
|
|
10983
10983
|
}
|
|
10984
|
+
],
|
|
10985
|
+
"1.24.17": [
|
|
10986
|
+
{
|
|
10987
|
+
"id": "1005029",
|
|
10988
|
+
"text": "Artists with both an OpenRouter and a Gemini key saved can now choose which one runs their AI request."
|
|
10989
|
+
}
|
|
10990
|
+
],
|
|
10991
|
+
"1.24.18": [
|
|
10992
|
+
{
|
|
10993
|
+
"id": "1003183",
|
|
10994
|
+
"text": "Builders can now be paid for a task, and never twice. Each payment is written down as owed before anything is sent, so a failed payment still leaves a record. No money moves yet: the Stripe connection comes next."
|
|
10995
|
+
}
|
|
10984
10996
|
]
|
|
10985
10997
|
}
|
|
@@ -285,6 +285,10 @@ const INTEGRATION = new Set([
|
|
|
285
285
|
// causeless or wrongly-directed movement, any UPDATE/DELETE/TRUNCATE, and a second row
|
|
286
286
|
// for one processor reference.
|
|
287
287
|
'project_account_db',
|
|
288
|
+
// task 1003183: builder payouts from the real migrations — Postgres refuses a second debt
|
|
289
|
+
// for one task or approval, a second sent attempt, a causeless attempt, and any rewrite;
|
|
290
|
+
// the real payForTask pays once and books one payout movement.
|
|
291
|
+
'builder_payouts_db',
|
|
288
292
|
// task 1004408: migration platform_identity_028 deletes the old hub invite's
|
|
289
293
|
// pending rows before a cutoff it reads from the core_upgrades ledger. Only a real
|
|
290
294
|
// engine runs the DO block, the numeric version compare and the one-shot guard.
|