git mv packages/business -> packages/organization (@mosaic/organization). The old package stays as one-line re-export shims of each moved module, with src/business.mjs re-exporting src/organization-config.mjs and src/cli.mjs forwarding to the moved executable. BusinessError is OrganizationConfigError (one constructor, name kept BusinessError); OrganizationError stays distinct. The bus adapter gains organization-context.mjs (busOrganization), with business.mjs (busBusiness) as the same function. Import consumers, manifests, the lockfile, scripts/mosaic, stack-copy.sh COPY_PATHS (both roots), seat sandbox RUNNER_FILES and the copy closure walker (organization-imports.mjs, both roots required) move to the new path. v1 serialization, v1 writers and the stage A fixtures are unchanged; the stage A contract tests stay where they were and pass as before.
69 lines
6.3 KiB
JavaScript
69 lines
6.3 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, rmSync, readFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { Store } from '../src/store.mjs';
|
|
import { createLimits, modelUsageKey } from '../src/limits.mjs';
|
|
import * as token from '../../plugins/limit-check/token-limit/index.mjs';
|
|
import * as daily from '../../plugins/limit-check/daily-quota/index.mjs';
|
|
import { launchSettings } from '../../organization/src/launch-settings.mjs';
|
|
import { businessDoc, rolesCopy, writeJson, systemFor } from '../../organization/tests/helpers.mjs';
|
|
const key='abcdefabcdef',other='012345abcdef',since='2026-01-01T00:00:00.000Z';
|
|
const quota={limit:10,since,reset:'00:00',timeZone:'UTC'};
|
|
const native={at:'2026-10-26T12:00:00.000Z',run:'legacy-run',turn:'turn',account:key,modelKeyId:other,status:'known',input:10,output:0,cacheWrite:0,cacheRead:0};
|
|
test('P5 R1 plain twelve-hex account leaves retain matching and original account report even with another model ID',()=>{
|
|
const a={limits:{tokens:{keys:{[key]:{limit:10,since}}}},subject:{account:key,modelKeyId:other},records:[native]};
|
|
assert.equal(token.mayLaunch(a).allow,false);assert.equal(token.mustStop(a).stop,true);
|
|
assert.equal(token.report(a)[0].count,10);assert.deepEqual(token.report(a)[0].path,['tokens','keys',key]);
|
|
assert.equal(token.mayLaunch({...a,records:[{...native,input:9}]}).allow,true);
|
|
assert.deepEqual(daily.report({...a,now:native.at}),[]);
|
|
});
|
|
for(const proxy of [false,true])test(`P5 R1 legacy account core lookup stays independent of ${proxy?'unrelated proxy metadata':'missing proxy metadata'}`,async t=>{
|
|
const root=mkdtempSync(join(tmpdir(),'p5-legacy-')),store=new Store(root);t.after(()=>{store.close();rmSync(root,{recursive:true,force:true});});
|
|
let lookups=0;
|
|
const b={launch:{account:{name:key,credential:'FAKE_TOKEN_NAME'},limits:{tokens:{keys:{[key]:{limit:10,since}}}}},plugins:{'limit-check':['token-limit']}};
|
|
const limits=createLimits({store,read:()=>b,log:()=>{},keyForSubject:(...args)=>{lookups++;return proxy?modelUsageKey([{service:'model',instance:'demo/coder',keyId:other,retiring:[]}],new Map(),'demo',args[1]):null;}});
|
|
assert.deepEqual(await limits.mayLaunch('demo',{instance:'coder',account:key}),{allow:true});
|
|
store.run(`INSERT INTO usage_records(at,business,run,instance,account,turn,source,phase,status,cursor,input,output,cache_write,cache_read)
|
|
VALUES (?,'demo','legacy-run','coder',?,'turn','transcript','end','known',?,10,0,0,0)`,native.at,key,JSON.stringify({modelKeyId:other}));
|
|
const hit={path:['tokens','keys',key],limit:10,count:10,unknownTurns:0};
|
|
assert.deepEqual(await limits.mayLaunch('demo',{instance:'coder',account:key}),{allow:false,check:'token-limit',hit});
|
|
assert.deepEqual(await limits.mustStop('demo',{instance:'coder',account:key}),{stop:true,check:'token-limit',hit});
|
|
assert.equal(limits.report('demo')[0].count,10);assert.equal(lookups,0);
|
|
});
|
|
for(const leaf of [{limit:0,since},quota])test(`P5 R1 empty checks bypass model metadata for ${leaf.reset?'daily model':'legacy hex account'} leaves`,async t=>{
|
|
const root=mkdtempSync(join(tmpdir(),'p5-no-checks-')),store=new Store(root);t.after(()=>{store.close();rmSync(root,{recursive:true,force:true});});
|
|
let lookups=0;const limits=createLimits({store,read:()=>({plugins:{'limit-check':[]},launch:{limits:{tokens:{keys:{[key]:leaf}}}}}),log:()=>{},keyForSubject:()=>{lookups++;throw Error('MUST-NOT-LOOK-UP');}});
|
|
assert.deepEqual(await limits.mayLaunch('demo',{instance:'coder',account:key}),{allow:true});
|
|
assert.deepEqual(await limits.mustStop('demo',{instance:'coder',account:key}),{stop:false});assert.equal(lookups,0);
|
|
});
|
|
test('P5 R1 legacy hex account chief resets stay allowed; daily model old/new/removal/reset transitions stay human-only',t=>{
|
|
const root=mkdtempSync(join(tmpdir(),'p5-fence-'));t.after(()=>rmSync(root,{recursive:true,force:true}));
|
|
const rolesDir=rolesCopy(root),doc=businessDoc(root,'demo'),file=join(root,'businesses/demo.json');
|
|
doc.launch.account={name:key,credential:'FAKE_TOKEN_NAME'};doc.launch.limits={tokens:{keys:{[key]:{limit:10,since}}}};writeJson(file,doc);
|
|
let now='2026-10-26T12:00:00.000Z';const settings=launchSettings({dir:root,rolesDir,system:systemFor(root),now:()=>now});let v=settings.read('demo');
|
|
v=settings.set('demo',{revision:v.revision,reset:[['tokens','keys',key]]},'chief');
|
|
assert.equal(JSON.parse(readFileSync(file,'utf8')).launch.limits.tokens.keys[key].since,now);
|
|
const dailyLeaf={limit:10,reset:'00:00',timeZone:'UTC'};
|
|
assert.throws(()=>settings.set('demo',{revision:v.revision,limits:{tokens:{keys:{[key]:dailyLeaf}}}},'chief'),{code:'organization-model-quota-human-only'});
|
|
v=settings.set('demo',{revision:v.revision,limits:{tokens:{keys:{[key]:dailyLeaf}}}},'jason');now='2026-10-26T13:00:00.000Z';
|
|
for(const args of [{reset:[['tokens','keys',key]]},{limits:{}},{limits:{tokens:{keys:{[key]:{limit:10}}}}}])
|
|
assert.throws(()=>settings.set('demo',{revision:v.revision,...args},'chief'),{code:'organization-model-quota-human-only'});
|
|
v=settings.set('demo',{revision:v.revision,limits:{tokens:{keys:{[key]:{limit:10}}}}},'jason');
|
|
v=settings.set('demo',{revision:v.revision,reset:[['tokens','keys',key]]},'chief');
|
|
assert.equal(JSON.parse(readFileSync(file,'utf8')).launch.limits.tokens.keys[key].since,now);
|
|
});
|
|
|
|
test('P5 R1 daily model matching/report is separate from legacy account and schema refuses partial or invalid daily shapes',async()=>{
|
|
const {checkTokenLimits}=await import('../../organization/src/launch.mjs');
|
|
assert.deepEqual(checkTokenLimits({keys:{[key]:quota}}).keys[key],quota);
|
|
const args={limits:{tokens:{keys:{[key]:quota}}},records:[native],subject:{account:key,modelKeyId:other},now:native.at,lineages:[[key],[other]]};
|
|
assert.deepEqual(token.report(args),[]);
|
|
assert.equal(daily.mayLaunch(args).allow,true);assert.equal(daily.report(args)[0].count,0);
|
|
const owned={...args,subject:{account:'other-account',modelKeyId:key},records:[{...native,account:'other-account',modelKeyId:key}]};
|
|
assert.equal(daily.mayLaunch(owned).allow,false);assert.equal(daily.mustStop(owned).stop,true);assert.equal(daily.report(owned)[0].count,10);
|
|
for(const leaf of [{...quota,reset:'25:00'},{...quota,timeZone:'Mars/Phobos'},{limit:10,since,reset:'00:00'},{limit:10,since,timeZone:'UTC'}])
|
|
assert.throws(()=>checkTokenLimits({keys:{[key]:leaf}}));
|
|
});
|