@oneaddress/setup 2.8.0 → 2.9.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/dist/index.js +270 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -856,7 +856,7 @@ var _R = ["\u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588"
|
|
|
856
856
|
var _S = [" \u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 ", "\u2588\u2588 ", " \u2588\u2588\u2588\u2588\u2588 ", " \u2588\u2588", " \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588\u2588 "];
|
|
857
857
|
var ONE_ROWS = Array.from({ length: 7 }, (_3, i) => [_O[i], _N[i], _E[i]].join(" "));
|
|
858
858
|
var ADDR_ROWS = Array.from({ length: 7 }, (_3, i) => [_A[i], _D2[i], _D2[i], _R[i], _E[i], _S[i], _S[i]].join(" "));
|
|
859
|
-
var WIZARD_VERSION = true ? "2.
|
|
859
|
+
var WIZARD_VERSION = true ? "2.9.0" : "?";
|
|
860
860
|
function printCompactHeader() {
|
|
861
861
|
const INNER = 42;
|
|
862
862
|
const TOP = fn("\u250C") + dm("\u2500".repeat(INNER)) + fn("\u2510");
|
|
@@ -984,6 +984,7 @@ data.db-shm
|
|
|
984
984
|
"build": "tsup src/index.ts --format esm --no-dts --outDir dist",
|
|
985
985
|
"type-check": "tsc --noEmit",
|
|
986
986
|
"show": "tsx scripts/show.ts",
|
|
987
|
+
"audit": "tsx scripts/audit.ts",
|
|
987
988
|
"test": "tsx scripts/test.ts"
|
|
988
989
|
},
|
|
989
990
|
"dependencies": {
|
|
@@ -4924,6 +4925,24 @@ db.exec(\`
|
|
|
4924
4925
|
-- rather than only what it became.
|
|
4925
4926
|
prev_address TEXT NOT NULL DEFAULT '{}',
|
|
4926
4927
|
address TEXT NOT NULL,
|
|
4928
|
+
-- ON WHOSE AUTHORITY. The reference of the signed authorisation that
|
|
4929
|
+
-- produced this change, and the dispatch it arrived under.
|
|
4930
|
+
--
|
|
4931
|
+
-- WHY THEY ARE HERE AND NOT ONLY ON THE DASHBOARD. Both were computed on
|
|
4932
|
+
-- arrival and shown on the LAST CHANGE panel, and nowhere else: a second
|
|
4933
|
+
-- dispatch overwrote the panel and a restart cleared it. So "on whose
|
|
4934
|
+
-- authority did you change this customer's address on the 15th" could not
|
|
4935
|
+
-- be answered from this receiver at all, which is the first question an
|
|
4936
|
+
-- inspection asks about an applied change.
|
|
4937
|
+
--
|
|
4938
|
+
-- NOT ENCRYPTED, deliberately, unlike the addresses beside them. Neither is
|
|
4939
|
+
-- personal information: the reference is a digest of an authorisation and
|
|
4940
|
+
-- the dispatch id is an integer. Encrypting them would make the audit trail
|
|
4941
|
+
-- unreadable without the password in exactly the situation it exists for,
|
|
4942
|
+
-- and would buy nothing, since an attacker who can read this file can read
|
|
4943
|
+
-- the addresses too once they have what they came for.
|
|
4944
|
+
loa_ref TEXT,
|
|
4945
|
+
dispatch_id TEXT,
|
|
4927
4946
|
recorded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4928
4947
|
);
|
|
4929
4948
|
CREATE INDEX IF NOT EXISTS idx_history_account ON address_history(account_key, id DESC);
|
|
@@ -4939,6 +4958,11 @@ ensureColumn('customers', 'updated_at', 'TEXT'); // nullable on migrate; set on
|
|
|
4939
4958
|
ensureColumn('customers', 'account_key', 'TEXT');
|
|
4940
4959
|
ensureColumn('address_history', 'account_key', 'TEXT');
|
|
4941
4960
|
ensureColumn('address_history', 'prev_address', "TEXT NOT NULL DEFAULT '{}'");
|
|
4961
|
+
// NULLABLE WITH NO DEFAULT, because a row written before this column existed
|
|
4962
|
+
// genuinely has no authority recorded and must not claim one. Null reads as
|
|
4963
|
+
// "not recorded", which is the truth; a default would read as a value.
|
|
4964
|
+
ensureColumn('address_history', 'loa_ref', 'TEXT');
|
|
4965
|
+
ensureColumn('address_history', 'dispatch_id', 'TEXT');
|
|
4942
4966
|
|
|
4943
4967
|
// A database written before at-rest encryption existed holds plaintext rows and
|
|
4944
4968
|
// no account_key. Backfill the key and encrypt in place.
|
|
@@ -5187,6 +5211,10 @@ export interface AddressChange {
|
|
|
5187
5211
|
name: string;
|
|
5188
5212
|
prev_address: Address | null;
|
|
5189
5213
|
address: Address;
|
|
5214
|
+
/** The authorisation this change was made under. Null on an older row. */
|
|
5215
|
+
loa_ref: string | null;
|
|
5216
|
+
/** The dispatch it arrived under. Null on an older row. */
|
|
5217
|
+
dispatch_id: string | null;
|
|
5190
5218
|
recorded_at: string;
|
|
5191
5219
|
}
|
|
5192
5220
|
|
|
@@ -5213,6 +5241,8 @@ export function addressHistory(limit = 20): AddressChange[] {
|
|
|
5213
5241
|
const rows = db.prepare(
|
|
5214
5242
|
\`SELECT h.prev_address AS prev_address,
|
|
5215
5243
|
h.address AS address,
|
|
5244
|
+
h.loa_ref AS loa_ref,
|
|
5245
|
+
h.dispatch_id AS dispatch_id,
|
|
5216
5246
|
h.recorded_at AS recorded_at,
|
|
5217
5247
|
c.account_number AS account_number,
|
|
5218
5248
|
c.name AS name
|
|
@@ -5236,11 +5266,62 @@ export function addressHistory(limit = 20): AddressChange[] {
|
|
|
5236
5266
|
name: dec('name', r.name) ?? '(unknown)',
|
|
5237
5267
|
prev_address: prev,
|
|
5238
5268
|
address: JSON.parse(dec('address', r.address) ?? '{}') as Address,
|
|
5269
|
+
// NOT decrypted, because they were never encrypted. See the schema.
|
|
5270
|
+
loa_ref: r.loa_ref ?? null,
|
|
5271
|
+
dispatch_id: r.dispatch_id ?? null,
|
|
5239
5272
|
recorded_at: r.recorded_at ?? '',
|
|
5240
5273
|
};
|
|
5241
5274
|
});
|
|
5242
5275
|
}
|
|
5243
5276
|
|
|
5277
|
+
/**
|
|
5278
|
+
* Drop applied-change history older than \`days\`. Returns how many rows went.
|
|
5279
|
+
*
|
|
5280
|
+
* ## Why this exists, and why it is NOT wired to a timer
|
|
5281
|
+
*
|
|
5282
|
+
* The quarantine and the confirm queue both age out on their own, because both
|
|
5283
|
+
* hold work in progress and an unbounded hold is a retention decision made on
|
|
5284
|
+
* a consumer's behalf by nobody. \`address_history\` is different: it is the
|
|
5285
|
+
* AUDIT TRAIL, and the same argument runs the other way. Deleting the record
|
|
5286
|
+
* of a change you applied is not hygiene, it is losing the answer to "on whose
|
|
5287
|
+
* authority", which is the first thing an inspection asks.
|
|
5288
|
+
*
|
|
5289
|
+
* So the default is to keep it, and that default is now a DECISION rather than
|
|
5290
|
+
* an omission - which is the only thing that changed here. Before this
|
|
5291
|
+
* function existed, keeping it forever was simply what happened, and a partner
|
|
5292
|
+
* whose own retention policy required deletion had no lever to pull at all.
|
|
5293
|
+
*
|
|
5294
|
+
* ## If you do call it
|
|
5295
|
+
*
|
|
5296
|
+
* Your retention obligation and your evidentiary one point in opposite
|
|
5297
|
+
* directions and only you know which governs your business. Australian
|
|
5298
|
+
* financial-services records commonly run seven years; APP 11.2 says destroy
|
|
5299
|
+
* or de-identify personal information you no longer need. Those are compatible
|
|
5300
|
+
* only once someone decides what "no longer need" means here, and that someone
|
|
5301
|
+
* is not this file.
|
|
5302
|
+
*
|
|
5303
|
+
* Nothing calls this by default. Wire it to your own schedule, or run it by
|
|
5304
|
+
* hand, deliberately.
|
|
5305
|
+
*/
|
|
5306
|
+
export function purgeAddressHistory(days: number): number {
|
|
5307
|
+
if (!Number.isFinite(days) || days <= 0) return 0;
|
|
5308
|
+
// THE COMPARISON HAPPENS INSIDE SQLITE, in SQLite's own datetime format,
|
|
5309
|
+
// for the same reason \`purgeQuarantine\` does it: \`recorded_at\` is written by
|
|
5310
|
+
// the column default \`datetime('now')\`, and comparing that to a JavaScript
|
|
5311
|
+
// ISO string works right up until it does not.
|
|
5312
|
+
const info = db.prepare(
|
|
5313
|
+
\`DELETE FROM address_history WHERE recorded_at < datetime('now', ?)\`,
|
|
5314
|
+
).run(\`-\${Math.floor(days)} days\`);
|
|
5315
|
+
const n = Number(info.changes ?? 0);
|
|
5316
|
+
if (n > 0) {
|
|
5317
|
+
report.warn(
|
|
5318
|
+
\`[store] purged \${n} applied-change record(s) older than \${Math.floor(days)}d. \` +
|
|
5319
|
+
'That is audit history and it is gone.',
|
|
5320
|
+
);
|
|
5321
|
+
}
|
|
5322
|
+
return n;
|
|
5323
|
+
}
|
|
5324
|
+
|
|
5244
5325
|
/** Is the file on disk protected? Surfaced in the dashboard, in both states. */
|
|
5245
5326
|
export const storeEncrypted = encrypted;
|
|
5246
5327
|
|
|
@@ -5301,7 +5382,18 @@ export async function verifyAddress(customer: Customer, incoming: Address): Prom
|
|
|
5301
5382
|
* ours \u2014 but be aware two customers who share a name share a row. If that is
|
|
5302
5383
|
* possible in your data, give this a key of your own instead.
|
|
5303
5384
|
*/
|
|
5304
|
-
export async function saveAddress(
|
|
5385
|
+
export async function saveAddress(
|
|
5386
|
+
customer: Customer,
|
|
5387
|
+
incoming: Address,
|
|
5388
|
+
/**
|
|
5389
|
+
* On whose authority, recorded ALONGSIDE the change rather than beside it.
|
|
5390
|
+
*
|
|
5391
|
+
* Optional so an existing caller compiles, and the absence is honest: a row
|
|
5392
|
+
* written without it says "not recorded" rather than claiming an authority
|
|
5393
|
+
* it never had.
|
|
5394
|
+
*/
|
|
5395
|
+
provenance?: { loaRef?: string | null; dispatchId?: string | null },
|
|
5396
|
+
): Promise<Address> {
|
|
5305
5397
|
const acct = (customer.accountNumber ?? '').trim() || customer.name.trim();
|
|
5306
5398
|
const addressJson = JSON.stringify(incoming);
|
|
5307
5399
|
const key = accountKey(acct);
|
|
@@ -5329,12 +5421,14 @@ export async function saveAddress(customer: Customer, incoming: Address): Promis
|
|
|
5329
5421
|
});
|
|
5330
5422
|
|
|
5331
5423
|
db.prepare(\`
|
|
5332
|
-
INSERT INTO address_history (account_key, prev_address, address)
|
|
5333
|
-
VALUES ($account_key, $prev_address, $address)
|
|
5424
|
+
INSERT INTO address_history (account_key, prev_address, address, loa_ref, dispatch_id)
|
|
5425
|
+
VALUES ($account_key, $prev_address, $address, $loa_ref, $dispatch_id)
|
|
5334
5426
|
\`).run({
|
|
5335
5427
|
account_key: key,
|
|
5336
5428
|
prev_address: enc('address', JSON.stringify(previous)),
|
|
5337
5429
|
address: enc('address', addressJson),
|
|
5430
|
+
loa_ref: provenance?.loaRef ?? null,
|
|
5431
|
+
dispatch_id: provenance?.dispatchId ?? null,
|
|
5338
5432
|
});
|
|
5339
5433
|
|
|
5340
5434
|
// Metadata only \u2014 the address itself is personal information, so the key is
|
|
@@ -5386,8 +5480,8 @@ export const store = {
|
|
|
5386
5480
|
verifyAddress,
|
|
5387
5481
|
saveAddress,
|
|
5388
5482
|
find: findByAccount,
|
|
5389
|
-
//
|
|
5390
|
-
// return null here; the contract file says why.
|
|
5483
|
+
// A demo roster, so a count is free. A store over a real customer table
|
|
5484
|
+
// should return null here; the contract file says why.
|
|
5391
5485
|
count: customerCount,
|
|
5392
5486
|
} satisfies CustomerStore;
|
|
5393
5487
|
`
|
|
@@ -6186,7 +6280,10 @@ app.post('/webhook', async (req: Request, res: Response) => {
|
|
|
6186
6280
|
// than marking test dispatches as safe to display: a marker is something an
|
|
6187
6281
|
// attacker can try to forge onto a real dispatch, and a panel that never
|
|
6188
6282
|
// renders an address has nothing to forge it into.
|
|
6189
|
-
|
|
6283
|
+
// PROVENANCE GOES IN WITH THE CHANGE. Both values were already computed
|
|
6284
|
+
// here and went only to the dashboard panel, which one more dispatch or one
|
|
6285
|
+
// restart wiped.
|
|
6286
|
+
const replaced = await store.saveAddress(ctx, address, { loaRef, dispatchId: dispatch || null });
|
|
6190
6287
|
noteChange({
|
|
6191
6288
|
accountNumber: ctx.accountNumber || '(no account reference)',
|
|
6192
6289
|
accountChecked: config.verifiesAccountReference,
|
|
@@ -6574,6 +6671,135 @@ export async function ask(prompt: string): Promise<string> {
|
|
|
6574
6671
|
stdin.on('data', onData);
|
|
6575
6672
|
});
|
|
6576
6673
|
}
|
|
6674
|
+
`
|
|
6675
|
+
},
|
|
6676
|
+
{
|
|
6677
|
+
name: "scripts/audit.ts",
|
|
6678
|
+
content: `/**
|
|
6679
|
+
* Export the applied-change record: what changed, when, and on whose authority.
|
|
6680
|
+
*
|
|
6681
|
+
* Usage:
|
|
6682
|
+
* npm run audit \u2014 every applied change, as CSV
|
|
6683
|
+
* npm run audit -- --json \u2014 the same as JSON
|
|
6684
|
+
* npm run audit -- --from 2026-07-01 --to 2026-09-30
|
|
6685
|
+
* npm run audit -- --out C:\\reports\\q3.csv
|
|
6686
|
+
*
|
|
6687
|
+
* ## Why this exists
|
|
6688
|
+
*
|
|
6689
|
+
* \`npm run show\` answers "did it land", for a person, now. This answers "show
|
|
6690
|
+
* me every address change you applied last quarter and what authorised each
|
|
6691
|
+
* one", for somebody who is not you and is not at your desk. Those are
|
|
6692
|
+
* different questions and the second one is the one an inspection asks.
|
|
6693
|
+
*
|
|
6694
|
+
* ## What is in it, and what deliberately is not
|
|
6695
|
+
*
|
|
6696
|
+
* The account reference, the name, both sides of the change, the timestamp, the
|
|
6697
|
+
* LOA reference and the dispatch id. That is the full record of a change this
|
|
6698
|
+
* receiver applied.
|
|
6699
|
+
*
|
|
6700
|
+
* It is NOT redacted, unlike \`npm run show\`, and that is the point: this file
|
|
6701
|
+
* is the thing you hand to an auditor, and a redacted audit trail is not one.
|
|
6702
|
+
* It therefore contains personal information in cleartext the moment it is
|
|
6703
|
+
* written, which is why it writes where you tell it, says where it went, and
|
|
6704
|
+
* says so out loud.
|
|
6705
|
+
*
|
|
6706
|
+
* ## The one thing it cannot do
|
|
6707
|
+
*
|
|
6708
|
+
* It cannot prove the authority is genuine. \`loa_ref\` is recomputed by THIS
|
|
6709
|
+
* receiver from the LOA it decrypted, so it is evidence this receiver saw a
|
|
6710
|
+
* valid authorisation, not a signature anybody else can re-verify from the CSV.
|
|
6711
|
+
* Verifying it independently needs OneAddress's LOA public key, which is a
|
|
6712
|
+
* different exercise and is not what this file claims to be.
|
|
6713
|
+
*/
|
|
6714
|
+
import 'dotenv/config';
|
|
6715
|
+
import { writeFileSync } from 'node:fs';
|
|
6716
|
+
import { resolve } from 'node:path';
|
|
6717
|
+
import { config } from '../src/config.js';
|
|
6718
|
+
import { ask, databaseIsLocked } from '../src/unlock.js';
|
|
6719
|
+
|
|
6720
|
+
function arg(name: string): string | null {
|
|
6721
|
+
const i = process.argv.indexOf(\`--\${name}\`);
|
|
6722
|
+
return i >= 0 && process.argv[i + 1] ? String(process.argv[i + 1]) : null;
|
|
6723
|
+
}
|
|
6724
|
+
|
|
6725
|
+
/** CSV with the quoting rules that stop a comma in a street name shifting every column. */
|
|
6726
|
+
function csvCell(v: unknown): string {
|
|
6727
|
+
const s = String(v ?? '');
|
|
6728
|
+
return /[",\\n]/.test(s) ? \`"\${s.replace(/"/g, '""')}"\` : s;
|
|
6729
|
+
}
|
|
6730
|
+
|
|
6731
|
+
function fmtAddress(a: Record<string, string> | null): string {
|
|
6732
|
+
if (!a) return '';
|
|
6733
|
+
return [a.street, a.suburb, a.state, a.postcode, a.country].filter(Boolean).join(', ');
|
|
6734
|
+
}
|
|
6735
|
+
|
|
6736
|
+
async function askPassword(): Promise<void> {
|
|
6737
|
+
if (process.env.ONEADDRESS_DB_PASSPHRASE?.trim()) return;
|
|
6738
|
+
if (!process.stdin.isTTY) return;
|
|
6739
|
+
if (!(await databaseIsLocked())) return;
|
|
6740
|
+
const answer = await ask(' Password to unlock: ');
|
|
6741
|
+
if (answer) process.env.ONEADDRESS_DB_PASSPHRASE = answer;
|
|
6742
|
+
}
|
|
6743
|
+
|
|
6744
|
+
async function main(): Promise<void> {
|
|
6745
|
+
if (config.mode === 'inbox') {
|
|
6746
|
+
console.log('\\n This receiver is in inbox mode: your connector applies changes and holds the record.');
|
|
6747
|
+
console.log(' Export from there instead.\\n');
|
|
6748
|
+
return;
|
|
6749
|
+
}
|
|
6750
|
+
|
|
6751
|
+
await askPassword();
|
|
6752
|
+
const store = await import('../src/store.js');
|
|
6753
|
+
|
|
6754
|
+
const from = arg('from');
|
|
6755
|
+
const to = arg('to');
|
|
6756
|
+
// EVERY row, then filtered here. The store's reader takes a limit rather than
|
|
6757
|
+
// a date range, and a range that silently truncated at 20 would be worse than
|
|
6758
|
+
// useless in an audit file.
|
|
6759
|
+
const all = store.addressHistory(Number.MAX_SAFE_INTEGER);
|
|
6760
|
+
const rows = all.filter((h) => {
|
|
6761
|
+
const at = String(h.recorded_at ?? '');
|
|
6762
|
+
if (from && at < from) return false;
|
|
6763
|
+
// Inclusive of the whole end day: a --to of 2026-09-30 must include changes
|
|
6764
|
+
// applied at 14:00 that day, and 'YYYY-MM-DD HH:MM:SS' > 'YYYY-MM-DD'.
|
|
6765
|
+
if (to && at > \`\${to} 99\`) return false;
|
|
6766
|
+
return true;
|
|
6767
|
+
});
|
|
6768
|
+
|
|
6769
|
+
const asJson = process.argv.includes('--json');
|
|
6770
|
+
const body = asJson
|
|
6771
|
+
? JSON.stringify({
|
|
6772
|
+
exported_at: new Date().toISOString(),
|
|
6773
|
+
range: { from: from ?? null, to: to ?? null },
|
|
6774
|
+
note: 'Applied address changes recorded by this receiver. loa_ref is recomputed by this receiver from the authorisation it decrypted; verifying it independently needs the OneAddress LOA public key.',
|
|
6775
|
+
changes: rows,
|
|
6776
|
+
}, null, 2)
|
|
6777
|
+
: [
|
|
6778
|
+
['recorded_at', 'account_number', 'name', 'previous_address', 'new_address', 'loa_ref', 'dispatch_id']
|
|
6779
|
+
.join(','),
|
|
6780
|
+
...rows.map((h) => [
|
|
6781
|
+
h.recorded_at, h.account_number, h.name,
|
|
6782
|
+
fmtAddress(h.prev_address as unknown as Record<string, string> | null),
|
|
6783
|
+
fmtAddress(h.address as unknown as Record<string, string>),
|
|
6784
|
+
h.loa_ref ?? '', h.dispatch_id ?? '',
|
|
6785
|
+
].map(csvCell).join(',')),
|
|
6786
|
+
].join('\\n');
|
|
6787
|
+
|
|
6788
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
6789
|
+
const target = resolve(arg('out') ?? config.exportDir ?? '', arg('out')
|
|
6790
|
+
? ''
|
|
6791
|
+
: \`oneaddress-audit-\${stamp}.\${asJson ? 'json' : 'csv'}\`);
|
|
6792
|
+
writeFileSync(target, body);
|
|
6793
|
+
|
|
6794
|
+
console.log(\`\\n Exported \${rows.length} applied change(s) to\\n \${target}\\n\`);
|
|
6795
|
+
console.log(' THIS FILE CONTAINS CUSTOMER ADDRESSES IN CLEARTEXT. It is the audit record,');
|
|
6796
|
+
console.log(' so it is deliberately not redacted. Store and send it accordingly.\\n');
|
|
6797
|
+
}
|
|
6798
|
+
|
|
6799
|
+
void main().catch((err: unknown) => {
|
|
6800
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
6801
|
+
process.exit(1);
|
|
6802
|
+
});
|
|
6577
6803
|
`
|
|
6578
6804
|
},
|
|
6579
6805
|
{
|
|
@@ -6582,9 +6808,11 @@ export async function ask(prompt: string): Promise<string> {
|
|
|
6582
6808
|
* Show what this receiver holds: the roster, and every change it has applied.
|
|
6583
6809
|
*
|
|
6584
6810
|
* Usage:
|
|
6585
|
-
* npm run show
|
|
6586
|
-
* npm run show 100
|
|
6587
|
-
* npm run show
|
|
6811
|
+
* npm run show \u2014 a LIVE branded view, refreshing as changes land
|
|
6812
|
+
* npm run show 100 \u2014 keep 100 changes on screen instead of 20
|
|
6813
|
+
* npm run show -- --reveal \u2014 show street lines (hidden by default)
|
|
6814
|
+
* npm run show > f.txt \u2014 plain one-shot text, for a file or a pipe
|
|
6815
|
+
* npm run audit \u2014 export the applied-change record (see scripts/audit.ts)
|
|
6588
6816
|
*
|
|
6589
6817
|
* ## Why this script exists
|
|
6590
6818
|
*
|
|
@@ -6634,10 +6862,32 @@ const DIM = '#8a7f6a';
|
|
|
6634
6862
|
/** Escape blessed's tag syntax so a customer's name can never inject markup. */
|
|
6635
6863
|
const esc = (s: unknown): string => String(s ?? '').replace(/[{}]/g, '');
|
|
6636
6864
|
|
|
6865
|
+
/**
|
|
6866
|
+
* REDACTED BY DEFAULT, revealed on purpose.
|
|
6867
|
+
*
|
|
6868
|
+
* This script's whole job is to show what the receiver holds, so refusing to
|
|
6869
|
+
* print addresses would defeat it. But the default run prints EVERY customer's
|
|
6870
|
+
* address into a terminal, where it lands in scrollback, in a \`| tee\` file and
|
|
6871
|
+
* in any screen recording, and the commonest reason to run it is to check one
|
|
6872
|
+
* record or prove one dispatch landed - neither of which needs the other 23.
|
|
6873
|
+
*
|
|
6874
|
+
* So the default shows enough to recognise a record (suburb, state, postcode)
|
|
6875
|
+
* and withholds the street line, and \`--reveal\` prints the lot. The suburb is
|
|
6876
|
+
* deliberately NOT withheld: an audit row you cannot tell apart from the next
|
|
6877
|
+
* one is not an audit row, and the suburb is what makes a change legible as a
|
|
6878
|
+
* change.
|
|
6879
|
+
*/
|
|
6880
|
+
const REVEAL = process.argv.includes('--reveal') || process.env.ONEADDRESS_SHOW_REVEAL === '1';
|
|
6881
|
+
|
|
6637
6882
|
function fmt(a: Record<string, string> | null): string {
|
|
6638
6883
|
if (!a) return '(nothing on file)';
|
|
6639
|
-
const
|
|
6640
|
-
|
|
6884
|
+
const rest = [a.suburb, a.state, a.postcode, a.country].filter(Boolean).join(', ');
|
|
6885
|
+
if (REVEAL) {
|
|
6886
|
+
const parts = [a.street, a.suburb, a.state, a.postcode, a.country].filter(Boolean);
|
|
6887
|
+
return parts.join(', ') || '(empty)';
|
|
6888
|
+
}
|
|
6889
|
+
if (!a.street) return rest || '(empty)';
|
|
6890
|
+
return \`[street hidden], \${rest}\` || '(empty)';
|
|
6641
6891
|
}
|
|
6642
6892
|
|
|
6643
6893
|
async function askPassword(): Promise<void> {
|
|
@@ -6684,6 +6934,12 @@ function changeRows(store: Store, limit: number, query: string): string[] {
|
|
|
6684
6934
|
out.push(\` {\${DIM}-fg}\${esc(h.recorded_at)}{/} {\${CREAM}-fg}\${esc(h.account_number)}{/} \${esc(h.name)}\`);
|
|
6685
6935
|
out.push(\` {\${DIM}-fg}was:{/} \${esc(fmt(prev))}\`);
|
|
6686
6936
|
out.push(\` {\${AMBER}-fg}now:{/} \${esc(fmt(now))}\`);
|
|
6937
|
+
// ON WHOSE AUTHORITY, on its own line. Null means the row predates the
|
|
6938
|
+
// column, which is not the same as a change nobody authorised, so it says
|
|
6939
|
+
// so rather than printing an empty field.
|
|
6940
|
+
out.push(h.loa_ref
|
|
6941
|
+
? \` {\${DIM}-fg}auth:{/} \${esc(h.loa_ref)}\${h.dispatch_id ? \` {\${DIM}-fg}dispatch{/} \${esc(h.dispatch_id)}\` : ''}\`
|
|
6942
|
+
: \` {\${DIM}-fg}auth: not recorded (applied before this receiver kept it){/}\`);
|
|
6687
6943
|
}
|
|
6688
6944
|
return out;
|
|
6689
6945
|
}
|
|
@@ -6781,7 +7037,7 @@ function stream(store: Store, limit: number): void {
|
|
|
6781
7037
|
const drawFooter = (): void => {
|
|
6782
7038
|
footer.setContent(query
|
|
6783
7039
|
? \`{\${AMBER}-fg}filter: \${esc(query)}{/} {\${CREAM}-fg}[/]{/} change {\${CREAM}-fg}[esc]{/} clear {\${CREAM}-fg}[q]{/} quit\`
|
|
6784
|
-
: \`{\${DIM}-fg}live \u2014
|
|
7040
|
+
: \`{\${DIM}-fg}live\${REVEAL ? '' : ' \u2014 streets hidden, --reveal to show'}{/} {\${CREAM}-fg}[/]{/} search {\${CREAM}-fg}[q]{/} quit\`);
|
|
6785
7041
|
};
|
|
6786
7042
|
|
|
6787
7043
|
const tick = (force = false): void => {
|
|
@@ -12246,7 +12502,7 @@ async function scaffold(platform, outputDir, partnerId, webhookSecret, webhookUr
|
|
|
12246
12502
|
|
|
12247
12503
|
// src/register.ts
|
|
12248
12504
|
var import_node_crypto2 = require("crypto");
|
|
12249
|
-
var PKG_VERSION = true ? "2.
|
|
12505
|
+
var PKG_VERSION = true ? "2.9.0" : "dev";
|
|
12250
12506
|
var REGISTER_URL = "https://partners.oneaddress.io/api/partner/installs";
|
|
12251
12507
|
function hmacSha256(secret, message) {
|
|
12252
12508
|
return (0, import_node_crypto2.createHmac)("sha256", secret).update(message).digest("hex");
|