@undefineds.co/xpod 0.3.14 → 0.3.16

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.
Files changed (76) hide show
  1. package/config/local.json +5 -5
  2. package/config/xpod.json +24 -10
  3. package/dist/cli/commands/auth.d.ts +1 -0
  4. package/dist/cli/commands/auth.js +117 -37
  5. package/dist/cli/commands/auth.js.map +1 -1
  6. package/dist/cli/commands/login.js +16 -23
  7. package/dist/cli/commands/login.js.map +1 -1
  8. package/dist/cli/commands/logs.d.ts +2 -0
  9. package/dist/cli/commands/logs.js +20 -5
  10. package/dist/cli/commands/logs.js.map +1 -1
  11. package/dist/cli/commands/obj.d.ts +44 -0
  12. package/dist/cli/commands/obj.js +1059 -0
  13. package/dist/cli/commands/obj.js.map +1 -0
  14. package/dist/cli/commands/rdf.d.ts +14 -0
  15. package/dist/cli/commands/rdf.js +235 -0
  16. package/dist/cli/commands/rdf.js.map +1 -0
  17. package/dist/cli/commands/resource.d.ts +31 -0
  18. package/dist/cli/commands/resource.js +191 -0
  19. package/dist/cli/commands/resource.js.map +1 -0
  20. package/dist/cli/commands/secret.d.ts +36 -0
  21. package/dist/cli/commands/secret.js +285 -0
  22. package/dist/cli/commands/secret.js.map +1 -0
  23. package/dist/cli/commands/server.d.ts +11 -0
  24. package/dist/cli/commands/server.js +168 -0
  25. package/dist/cli/commands/server.js.map +1 -0
  26. package/dist/cli/commands/start.d.ts +1 -0
  27. package/dist/cli/commands/start.js +5 -0
  28. package/dist/cli/commands/start.js.map +1 -1
  29. package/dist/cli/commands/status.d.ts +1 -0
  30. package/dist/cli/commands/status.js +21 -6
  31. package/dist/cli/commands/status.js.map +1 -1
  32. package/dist/cli/commands/stop.d.ts +3 -0
  33. package/dist/cli/commands/stop.js +40 -6
  34. package/dist/cli/commands/stop.js.map +1 -1
  35. package/dist/cli/index.js +23 -8
  36. package/dist/cli/index.js.map +1 -1
  37. package/dist/cli/lib/auth-context.d.ts +24 -0
  38. package/dist/cli/lib/auth-context.js +70 -0
  39. package/dist/cli/lib/auth-context.js.map +1 -0
  40. package/dist/cli/lib/output.d.ts +23 -0
  41. package/dist/cli/lib/output.js +63 -0
  42. package/dist/cli/lib/output.js.map +1 -0
  43. package/dist/cli/lib/resource.d.ts +29 -0
  44. package/dist/cli/lib/resource.js +114 -0
  45. package/dist/cli/lib/resource.js.map +1 -0
  46. package/dist/components/context.jsonld +6 -0
  47. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.d.ts +11 -10
  48. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js +13 -24
  49. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js.map +1 -1
  50. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.jsonld +4 -4
  51. package/dist/identity/oidc/AutoDetectOidcHandler.d.ts +8 -4
  52. package/dist/identity/oidc/AutoDetectOidcHandler.js +10 -6
  53. package/dist/identity/oidc/AutoDetectOidcHandler.js.map +1 -1
  54. package/dist/identity/oidc/AutoDetectOidcHandler.jsonld +3 -3
  55. package/dist/storage/accessors/MixDataAccessor.js +3 -0
  56. package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
  57. package/dist/storage/quint/SqliteQuintStore.d.ts +26 -1
  58. package/dist/storage/quint/SqliteQuintStore.js +551 -318
  59. package/dist/storage/quint/SqliteQuintStore.js.map +1 -1
  60. package/dist/storage/quint/SqliteQuintStore.jsonld +102 -2
  61. package/dist/storage/quint/schema.d.ts +76 -0
  62. package/dist/storage/quint/schema.js +13 -7
  63. package/dist/storage/quint/schema.js.map +1 -1
  64. package/dist/storage/rdf/RdfLocalQueryEngine.d.ts +4 -1
  65. package/dist/storage/rdf/RdfLocalQueryEngine.js +77 -8
  66. package/dist/storage/rdf/RdfLocalQueryEngine.js.map +1 -1
  67. package/dist/storage/rdf/SolidRdfEngine.d.ts +5 -0
  68. package/dist/storage/rdf/SolidRdfEngine.js +31 -3
  69. package/dist/storage/rdf/SolidRdfEngine.js.map +1 -1
  70. package/dist/storage/rdf/SolidRdfEngine.jsonld +34 -0
  71. package/dist/storage/sparql/ComunicaQuintEngine.js +16 -3
  72. package/dist/storage/sparql/ComunicaQuintEngine.js.map +1 -1
  73. package/package.json +1 -1
  74. package/dist/cli/commands/config.d.ts +0 -42
  75. package/dist/cli/commands/config.js +0 -289
  76. package/dist/cli/commands/config.js.map +0 -1
@@ -1,289 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.configCommand = exports.PROVIDER_BASE_URLS = exports.CREDENTIAL_NS = exports.AI_NS = void 0;
4
- exports.maskSecret = maskSecret;
5
- exports.credentialId = credentialId;
6
- exports.buildProviderSparql = buildProviderSparql;
7
- exports.buildCredentialSparql = buildCredentialSparql;
8
- exports.buildResetSparql = buildResetSparql;
9
- const models_1 = require("@undefineds.co/models");
10
- const credentials_store_1 = require("../lib/credentials-store");
11
- const solid_auth_1 = require("../lib/solid-auth");
12
- /**
13
- * Config 子命令:配置 Pod 中的 AI provider。
14
- *
15
- * 写入 Pod 的两个资源,与服务端 PodChatKitStore.getAiConfig() 对齐:
16
- * /settings/providers/{provider}.ttl — Provider + optional Model
17
- * /settings/credentials.ttl#cred-{provider} — Credential (apiKey, service=ai, status=active, provider link)
18
- *
19
- * 用法:
20
- * xpod config set --provider openai --model gpt-4o --api-key sk-xxx
21
- * xpod config set --api-key sk-new-key # 更新已有 provider 的 key
22
- * xpod config show
23
- * xpod config reset
24
- */
25
- exports.AI_NS = models_1.XPOD_AI.NAMESPACE;
26
- exports.CREDENTIAL_NS = models_1.XPOD_CREDENTIAL.NAMESPACE;
27
- /** provider name → default baseUrl */
28
- exports.PROVIDER_BASE_URLS = {
29
- openai: 'https://api.openai.com/v1',
30
- google: 'https://generativelanguage.googleapis.com/v1beta/openai',
31
- anthropic: 'https://api.anthropic.com/v1',
32
- deepseek: 'https://api.deepseek.com/v1',
33
- openrouter: 'https://openrouter.ai/api/v1',
34
- ollama: 'http://localhost:11434/v1',
35
- mistral: 'https://api.mistral.ai/v1',
36
- cohere: 'https://api.cohere.ai/v1',
37
- zhipu: 'https://open.bigmodel.cn/api/paas/v4',
38
- codebuddy: 'https://api.codebuddy.ai/v1',
39
- };
40
- function maskSecret(value) {
41
- if (value.length <= 8)
42
- return '****';
43
- return value.slice(0, 4) + '****' + value.slice(-4);
44
- }
45
- function credentialId(provider) {
46
- return `cred-${provider.toLowerCase()}`;
47
- }
48
- async function resolveAuth(argv) {
49
- const creds = (0, credentials_store_1.loadCredentials)();
50
- if (!creds) {
51
- console.error('No credentials found. Run `xpod auth create-credentials` first.');
52
- process.exit(1);
53
- }
54
- const clientCreds = (0, credentials_store_1.getClientCredentials)(creds);
55
- if (!clientCreds) {
56
- console.error('OAuth authentication not yet supported. Please use client credentials.');
57
- process.exit(1);
58
- }
59
- const baseUrl = (argv.url ?? creds.url).replace(/\/?$/, '/');
60
- const tokenResult = await (0, solid_auth_1.getAccessToken)(clientCreds.clientId, clientCreds.clientSecret, baseUrl);
61
- if (!tokenResult) {
62
- console.error('Failed to obtain access token. Credentials may be expired — run `xpod auth create-credentials` again.');
63
- process.exit(1);
64
- }
65
- const webIdUrl = new URL(creds.webId);
66
- const pathParts = webIdUrl.pathname.split('/').filter(Boolean);
67
- const podUrl = `${webIdUrl.origin}/${pathParts[0]}/`;
68
- return { accessToken: tokenResult.accessToken, podUrl };
69
- }
70
- /**
71
- * Build SPARQL UPDATE to upsert a Provider at /settings/providers/{id}.ttl
72
- */
73
- function buildProviderSparql(resourceUrl, providerId, fields = {}) {
74
- const baseUrl = exports.PROVIDER_BASE_URLS[providerId.toLowerCase()];
75
- const displayName = (0, models_1.getAIConfigProviderMetadata)(providerId).displayName
76
- ?? providerId.charAt(0).toUpperCase() + providerId.slice(1);
77
- const subject = `<${resourceUrl}>`;
78
- const deletes = [
79
- `${subject} ai:baseUrl ?oldBase .`,
80
- `${subject} ai:displayName ?oldName .`,
81
- ];
82
- const providerPredicates = [
83
- `a ai:Provider`,
84
- `ai:displayName "${displayName}"`,
85
- ];
86
- const insertTriples = [];
87
- const optionals = [
88
- `OPTIONAL { ${subject} ai:baseUrl ?oldBase }`,
89
- `OPTIONAL { ${subject} ai:displayName ?oldName }`,
90
- ];
91
- if (baseUrl) {
92
- providerPredicates.push(`ai:baseUrl "${baseUrl}"`);
93
- }
94
- if (fields.model) {
95
- const modelRef = (0, models_1.aiConfigModelRef)(providerId, fields.model);
96
- const fragmentIndex = modelRef.indexOf('#');
97
- const modelSubject = fragmentIndex >= 0
98
- ? `<${resourceUrl}#${modelRef.slice(fragmentIndex + 1)}>`
99
- : `<${resourceUrl}#${fields.model}>`;
100
- deletes.push(`${subject} ai:defaultModel ?oldDefaultModel .`);
101
- deletes.push(`${subject} ai:hasModel ?oldHasModel .`);
102
- optionals.push(`OPTIONAL { ${subject} ai:defaultModel ?oldDefaultModel }`);
103
- optionals.push(`OPTIONAL { ${subject} ai:hasModel ?oldHasModel }`);
104
- providerPredicates.push(`ai:defaultModel ${modelSubject}`);
105
- providerPredicates.push(`ai:hasModel ${modelSubject}`);
106
- insertTriples.push(`${modelSubject} a ai:Model ; ai:displayName "${fields.model}" ; ai:modelType "chat" ; ai:isProvidedBy ${subject} ; ai:status "active" .`);
107
- }
108
- insertTriples.unshift(`${subject} ${providerPredicates.join(' ;\n ')} .`);
109
- return `PREFIX ai: <${exports.AI_NS}>
110
- DELETE { ${deletes.join(' ')} }
111
- INSERT { ${insertTriples.join('\n ')} }
112
- WHERE { ${optionals.join(' ')} }`;
113
- }
114
- /**
115
- * Build SPARQL UPDATE to upsert a Credential at /settings/credentials.ttl#cred-{provider}
116
- */
117
- function buildCredentialSparql(resourceUrl, podUrl, provider, fields) {
118
- const credId = credentialId(provider);
119
- const subject = `<${resourceUrl}#${credId}>`;
120
- const providerResource = `<${podUrl.replace(/\/$/, '')}${(0, models_1.aiConfigProviderRef)(provider)}>`;
121
- const deletes = [];
122
- const inserts = [
123
- `${subject} a cred:Credential`,
124
- `cred:service "ai"`,
125
- `cred:status "active"`,
126
- `cred:provider ${providerResource}`,
127
- ];
128
- const optionals = [];
129
- // Always delete+reinsert provider link
130
- deletes.push(`${subject} cred:provider ?oldProv .`);
131
- optionals.push(`OPTIONAL { ${subject} cred:provider ?oldProv }`);
132
- if (fields.apiKey) {
133
- deletes.push(`${subject} cred:apiKey ?oldKey .`);
134
- optionals.push(`OPTIONAL { ${subject} cred:apiKey ?oldKey }`);
135
- inserts.push(`cred:apiKey "${fields.apiKey}"`);
136
- }
137
- return `PREFIX cred: <${exports.CREDENTIAL_NS}>
138
- DELETE { ${deletes.join(' ')} }
139
- INSERT { ${inserts.join(' ;\n ')} }
140
- WHERE { ${optionals.join(' ')} }`;
141
- }
142
- /**
143
- * Build SPARQL UPDATE to delete a Credential
144
- */
145
- function buildResetSparql(resourceUrl, provider) {
146
- const credId = credentialId(provider);
147
- const subject = `<${resourceUrl}#${credId}>`;
148
- return `PREFIX cred: <${exports.CREDENTIAL_NS}>\nDELETE WHERE { ${subject} ?p ?o }`;
149
- }
150
- async function writeProvider(podUrl, accessToken, providerId, fields) {
151
- const resource = `${podUrl.replace(/\/$/, '')}${(0, models_1.aiConfigProviderRef)(providerId)}`;
152
- const sparql = buildProviderSparql(resource, providerId, fields);
153
- const res = await (0, solid_auth_1.authenticatedFetch)(resource, accessToken, {
154
- method: 'PATCH',
155
- headers: { 'Content-Type': 'application/sparql-update' },
156
- body: sparql,
157
- });
158
- return res.ok;
159
- }
160
- async function writeCredential(podUrl, accessToken, provider, fields) {
161
- const resource = `${podUrl}settings/credentials.ttl`;
162
- const sparql = buildCredentialSparql(resource, podUrl, provider, fields);
163
- const res = await (0, solid_auth_1.authenticatedFetch)(resource, accessToken, {
164
- method: 'PATCH',
165
- headers: { 'Content-Type': 'application/sparql-update' },
166
- body: sparql,
167
- });
168
- return res.ok;
169
- }
170
- const setCommand = {
171
- command: 'set',
172
- describe: 'Configure Pod AI provider (provider, model, api-key — one or more)',
173
- builder: (yargs) => yargs
174
- .option('provider', { type: 'string', description: `AI provider (${Object.keys(exports.PROVIDER_BASE_URLS).join(', ')})` })
175
- .option('model', { type: 'string', description: 'Default model name' })
176
- .option('api-key', { type: 'string', description: 'API key' })
177
- .check((argv) => {
178
- if (!argv.provider && !argv.model && !argv['api-key']) {
179
- throw new Error('Specify at least one of --provider, --model, or --api-key');
180
- }
181
- if ((argv.model || argv['api-key']) && !argv.provider) {
182
- throw new Error('--provider is required when setting --model or --api-key');
183
- }
184
- return true;
185
- }),
186
- handler: async (argv) => {
187
- const { accessToken, podUrl } = await resolveAuth(argv);
188
- const provider = argv.provider;
189
- // Write provider
190
- const provOk = await writeProvider(podUrl, accessToken, provider, { model: argv.model });
191
- if (!provOk) {
192
- console.error('Failed to write provider config.');
193
- process.exit(1);
194
- }
195
- // Write credential
196
- const credOk = await writeCredential(podUrl, accessToken, provider, {
197
- apiKey: argv['api-key'],
198
- model: argv.model,
199
- });
200
- if (!credOk) {
201
- console.error('Failed to write credential config.');
202
- process.exit(1);
203
- }
204
- console.log(` provider: ${provider}`);
205
- if (argv.model)
206
- console.log(` model: ${argv.model}`);
207
- if (argv['api-key'])
208
- console.log(` api-key: ${maskSecret(argv['api-key'])}`);
209
- console.log('Pod AI provider config saved.');
210
- },
211
- };
212
- const showCommand = {
213
- command: 'show',
214
- describe: 'Show current Pod AI provider config',
215
- builder: (yargs) => yargs,
216
- handler: async (argv) => {
217
- const { accessToken, podUrl } = await resolveAuth(argv);
218
- const resource = `${podUrl}settings/credentials.ttl`;
219
- const res = await (0, solid_auth_1.authenticatedFetch)(resource, accessToken, {
220
- headers: { Accept: 'text/turtle' },
221
- });
222
- if (!res.ok) {
223
- console.log('No Pod AI provider config found. Use `xpod config set --provider openai --api-key sk-xxx` to configure.');
224
- return;
225
- }
226
- const turtle = await res.text();
227
- // Parse simple triples from turtle to find AI credentials
228
- const credBlocks = turtle.split(/(?=<[^>]*#cred-)/);
229
- let found = false;
230
- for (const block of credBlocks) {
231
- if (!block.includes('service') || !block.includes('"ai"'))
232
- continue;
233
- found = true;
234
- const providerMatch = block.match(/settings\/providers\/([^/\s>]+)\.ttl/);
235
- const apiKeyMatch = block.match(/apiKey\s+"([^"]+)"/);
236
- if (providerMatch)
237
- console.log(` provider: ${providerMatch[1]}`);
238
- if (apiKeyMatch)
239
- console.log(` api-key: ${maskSecret(apiKeyMatch[1])}`);
240
- }
241
- if (!found) {
242
- console.log('No Pod AI provider config found. Use `xpod config set --provider openai --api-key sk-xxx` to configure.');
243
- }
244
- },
245
- };
246
- const resetCommand = {
247
- command: 'reset',
248
- describe: 'Remove Pod AI provider config',
249
- builder: (yargs) => yargs.option('provider', {
250
- type: 'string',
251
- description: 'Provider to remove',
252
- demandOption: true,
253
- }),
254
- handler: async (argv) => {
255
- const { accessToken, podUrl } = await resolveAuth(argv);
256
- const provider = argv.provider;
257
- const resource = `${podUrl}settings/credentials.ttl`;
258
- const sparql = buildResetSparql(resource, provider);
259
- const res = await (0, solid_auth_1.authenticatedFetch)(resource, accessToken, {
260
- method: 'PATCH',
261
- headers: { 'Content-Type': 'application/sparql-update' },
262
- body: sparql,
263
- });
264
- if (res.ok) {
265
- console.log(`Pod AI provider config for ${provider} removed.`);
266
- }
267
- else {
268
- const text = await res.text();
269
- console.error(`Failed to reset config: ${res.status} ${text.slice(0, 200)}`);
270
- process.exit(1);
271
- }
272
- },
273
- };
274
- exports.configCommand = {
275
- command: 'config',
276
- describe: 'Pod AI provider configuration (provider, model, api-key)',
277
- builder: (yargs) => yargs
278
- .option('url', {
279
- alias: 'u',
280
- type: 'string',
281
- description: 'Server base URL (default: from ~/.xpod/)',
282
- })
283
- .command(setCommand)
284
- .command(showCommand)
285
- .command(resetCommand)
286
- .demandCommand(1, 'Please specify a config subcommand'),
287
- handler: () => { },
288
- };
289
- //# sourceMappingURL=config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CommandModule } from 'yargs';\nimport { aiConfigModelRef, aiConfigProviderRef, getAIConfigProviderMetadata, XPOD_AI, XPOD_CREDENTIAL } from '@undefineds.co/models';\nimport { loadCredentials, getClientCredentials } from '../lib/credentials-store';\nimport { getAccessToken, authenticatedFetch } from '../lib/solid-auth';\n\ninterface ConfigArgs {\n url?: string;\n}\n\ninterface SetArgs extends ConfigArgs {\n provider?: string;\n model?: string;\n 'api-key'?: string;\n}\n\n/**\n * Config 子命令:配置 Pod 中的 AI provider。\n *\n * 写入 Pod 的两个资源,与服务端 PodChatKitStore.getAiConfig() 对齐:\n * /settings/providers/{provider}.ttl — Provider + optional Model\n * /settings/credentials.ttl#cred-{provider} — Credential (apiKey, service=ai, status=active, provider link)\n *\n * 用法:\n * xpod config set --provider openai --model gpt-4o --api-key sk-xxx\n * xpod config set --api-key sk-new-key # 更新已有 provider 的 key\n * xpod config show\n * xpod config reset\n */\n\nexport const AI_NS = XPOD_AI.NAMESPACE;\nexport const CREDENTIAL_NS = XPOD_CREDENTIAL.NAMESPACE;\n\n/** provider name → default baseUrl */\nexport const PROVIDER_BASE_URLS: Record<string, string> = {\n openai: 'https://api.openai.com/v1',\n google: 'https://generativelanguage.googleapis.com/v1beta/openai',\n anthropic: 'https://api.anthropic.com/v1',\n deepseek: 'https://api.deepseek.com/v1',\n openrouter: 'https://openrouter.ai/api/v1',\n ollama: 'http://localhost:11434/v1',\n mistral: 'https://api.mistral.ai/v1',\n cohere: 'https://api.cohere.ai/v1',\n zhipu: 'https://open.bigmodel.cn/api/paas/v4',\n codebuddy: 'https://api.codebuddy.ai/v1',\n};\n\nexport function maskSecret(value: string): string {\n if (value.length <= 8) return '****';\n return value.slice(0, 4) + '****' + value.slice(-4);\n}\n\nexport function credentialId(provider: string): string {\n return `cred-${provider.toLowerCase()}`;\n}\n\nasync function resolveAuth(argv: ConfigArgs): Promise<{ accessToken: string; podUrl: string }> {\n const creds = loadCredentials();\n if (!creds) {\n console.error('No credentials found. Run `xpod auth create-credentials` first.');\n process.exit(1);\n }\n\n const clientCreds = getClientCredentials(creds);\n if (!clientCreds) {\n console.error('OAuth authentication not yet supported. Please use client credentials.');\n process.exit(1);\n }\n\n const baseUrl = (argv.url ?? creds.url).replace(/\\/?$/, '/');\n\n const tokenResult = await getAccessToken(clientCreds.clientId, clientCreds.clientSecret, baseUrl);\n if (!tokenResult) {\n console.error('Failed to obtain access token. Credentials may be expired — run `xpod auth create-credentials` again.');\n process.exit(1);\n }\n\n const webIdUrl = new URL(creds.webId);\n const pathParts = webIdUrl.pathname.split('/').filter(Boolean);\n const podUrl = `${webIdUrl.origin}/${pathParts[0]}/`;\n\n return { accessToken: tokenResult.accessToken, podUrl };\n}\n\n/**\n * Build SPARQL UPDATE to upsert a Provider at /settings/providers/{id}.ttl\n */\nexport function buildProviderSparql(resourceUrl: string, providerId: string, fields: { model?: string } = {}): string {\n const baseUrl = PROVIDER_BASE_URLS[providerId.toLowerCase()];\n const displayName = getAIConfigProviderMetadata(providerId).displayName\n ?? providerId.charAt(0).toUpperCase() + providerId.slice(1);\n const subject = `<${resourceUrl}>`;\n const deletes = [\n `${subject} ai:baseUrl ?oldBase .`,\n `${subject} ai:displayName ?oldName .`,\n ];\n const providerPredicates = [\n `a ai:Provider`,\n `ai:displayName \"${displayName}\"`,\n ];\n const insertTriples: string[] = [];\n const optionals = [\n `OPTIONAL { ${subject} ai:baseUrl ?oldBase }`,\n `OPTIONAL { ${subject} ai:displayName ?oldName }`,\n ];\n\n if (baseUrl) {\n providerPredicates.push(`ai:baseUrl \"${baseUrl}\"`);\n }\n\n if (fields.model) {\n const modelRef = aiConfigModelRef(providerId, fields.model);\n const fragmentIndex = modelRef.indexOf('#');\n const modelSubject = fragmentIndex >= 0\n ? `<${resourceUrl}#${modelRef.slice(fragmentIndex + 1)}>`\n : `<${resourceUrl}#${fields.model}>`;\n deletes.push(`${subject} ai:defaultModel ?oldDefaultModel .`);\n deletes.push(`${subject} ai:hasModel ?oldHasModel .`);\n optionals.push(`OPTIONAL { ${subject} ai:defaultModel ?oldDefaultModel }`);\n optionals.push(`OPTIONAL { ${subject} ai:hasModel ?oldHasModel }`);\n providerPredicates.push(`ai:defaultModel ${modelSubject}`);\n providerPredicates.push(`ai:hasModel ${modelSubject}`);\n insertTriples.push(`${modelSubject} a ai:Model ; ai:displayName \"${fields.model}\" ; ai:modelType \"chat\" ; ai:isProvidedBy ${subject} ; ai:status \"active\" .`);\n }\n insertTriples.unshift(`${subject} ${providerPredicates.join(' ;\\n ')} .`);\n\n return `PREFIX ai: <${AI_NS}>\nDELETE { ${deletes.join(' ')} }\nINSERT { ${insertTriples.join('\\n ')} }\nWHERE { ${optionals.join(' ')} }`;\n}\n\n/**\n * Build SPARQL UPDATE to upsert a Credential at /settings/credentials.ttl#cred-{provider}\n */\nexport function buildCredentialSparql(\n resourceUrl: string,\n podUrl: string,\n provider: string,\n fields: { apiKey?: string; model?: string },\n): string {\n const credId = credentialId(provider);\n const subject = `<${resourceUrl}#${credId}>`;\n const providerResource = `<${podUrl.replace(/\\/$/, '')}${aiConfigProviderRef(provider)}>`;\n\n const deletes: string[] = [];\n const inserts: string[] = [\n `${subject} a cred:Credential`,\n `cred:service \"ai\"`,\n `cred:status \"active\"`,\n `cred:provider ${providerResource}`,\n ];\n const optionals: string[] = [];\n\n // Always delete+reinsert provider link\n deletes.push(`${subject} cred:provider ?oldProv .`);\n optionals.push(`OPTIONAL { ${subject} cred:provider ?oldProv }`);\n\n if (fields.apiKey) {\n deletes.push(`${subject} cred:apiKey ?oldKey .`);\n optionals.push(`OPTIONAL { ${subject} cred:apiKey ?oldKey }`);\n inserts.push(`cred:apiKey \"${fields.apiKey}\"`);\n }\n\n return `PREFIX cred: <${CREDENTIAL_NS}>\nDELETE { ${deletes.join(' ')} }\nINSERT { ${inserts.join(' ;\\n ')} }\nWHERE { ${optionals.join(' ')} }`;\n}\n\n/**\n * Build SPARQL UPDATE to delete a Credential\n */\nexport function buildResetSparql(resourceUrl: string, provider: string): string {\n const credId = credentialId(provider);\n const subject = `<${resourceUrl}#${credId}>`;\n return `PREFIX cred: <${CREDENTIAL_NS}>\\nDELETE WHERE { ${subject} ?p ?o }`;\n}\n\nasync function writeProvider(podUrl: string, accessToken: string, providerId: string, fields: { model?: string }): Promise<boolean> {\n const resource = `${podUrl.replace(/\\/$/, '')}${aiConfigProviderRef(providerId)}`;\n const sparql = buildProviderSparql(resource, providerId, fields);\n const res = await authenticatedFetch(resource, accessToken, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/sparql-update' },\n body: sparql,\n });\n return res.ok;\n}\n\nasync function writeCredential(\n podUrl: string,\n accessToken: string,\n provider: string,\n fields: { apiKey?: string; model?: string },\n): Promise<boolean> {\n const resource = `${podUrl}settings/credentials.ttl`;\n const sparql = buildCredentialSparql(resource, podUrl, provider, fields);\n const res = await authenticatedFetch(resource, accessToken, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/sparql-update' },\n body: sparql,\n });\n return res.ok;\n}\n\nconst setCommand: CommandModule<ConfigArgs, SetArgs> = {\n command: 'set',\n describe: 'Configure Pod AI provider (provider, model, api-key — one or more)',\n builder: (yargs) =>\n yargs\n .option('provider', { type: 'string', description: `AI provider (${Object.keys(PROVIDER_BASE_URLS).join(', ')})` })\n .option('model', { type: 'string', description: 'Default model name' })\n .option('api-key', { type: 'string', description: 'API key' })\n .check((argv) => {\n if (!argv.provider && !argv.model && !argv['api-key']) {\n throw new Error('Specify at least one of --provider, --model, or --api-key');\n }\n if ((argv.model || argv['api-key']) && !argv.provider) {\n throw new Error('--provider is required when setting --model or --api-key');\n }\n return true;\n }),\n handler: async (argv) => {\n const { accessToken, podUrl } = await resolveAuth(argv);\n const provider = argv.provider!;\n\n // Write provider\n const provOk = await writeProvider(podUrl, accessToken, provider, { model: argv.model });\n if (!provOk) {\n console.error('Failed to write provider config.');\n process.exit(1);\n }\n\n // Write credential\n const credOk = await writeCredential(podUrl, accessToken, provider, {\n apiKey: argv['api-key'],\n model: argv.model,\n });\n if (!credOk) {\n console.error('Failed to write credential config.');\n process.exit(1);\n }\n\n console.log(` provider: ${provider}`);\n if (argv.model) console.log(` model: ${argv.model}`);\n if (argv['api-key']) console.log(` api-key: ${maskSecret(argv['api-key'])}`);\n console.log('Pod AI provider config saved.');\n },\n};\n\nconst showCommand: CommandModule<ConfigArgs, ConfigArgs> = {\n command: 'show',\n describe: 'Show current Pod AI provider config',\n builder: (yargs) => yargs,\n handler: async (argv) => {\n const { accessToken, podUrl } = await resolveAuth(argv);\n const resource = `${podUrl}settings/credentials.ttl`;\n const res = await authenticatedFetch(resource, accessToken, {\n headers: { Accept: 'text/turtle' },\n });\n\n if (!res.ok) {\n console.log('No Pod AI provider config found. Use `xpod config set --provider openai --api-key sk-xxx` to configure.');\n return;\n }\n\n const turtle = await res.text();\n // Parse simple triples from turtle to find AI credentials\n const credBlocks = turtle.split(/(?=<[^>]*#cred-)/);\n let found = false;\n for (const block of credBlocks) {\n if (!block.includes('service') || !block.includes('\"ai\"')) continue;\n found = true;\n\n const providerMatch = block.match(/settings\\/providers\\/([^/\\s>]+)\\.ttl/);\n const apiKeyMatch = block.match(/apiKey\\s+\"([^\"]+)\"/);\n\n if (providerMatch) console.log(` provider: ${providerMatch[1]}`);\n if (apiKeyMatch) console.log(` api-key: ${maskSecret(apiKeyMatch[1])}`);\n }\n\n if (!found) {\n console.log('No Pod AI provider config found. Use `xpod config set --provider openai --api-key sk-xxx` to configure.');\n }\n },\n};\n\nconst resetCommand: CommandModule<ConfigArgs, SetArgs> = {\n command: 'reset',\n describe: 'Remove Pod AI provider config',\n builder: (yargs) =>\n yargs.option('provider', {\n type: 'string',\n description: 'Provider to remove',\n demandOption: true,\n }),\n handler: async (argv) => {\n const { accessToken, podUrl } = await resolveAuth(argv);\n const provider = argv.provider!;\n const resource = `${podUrl}settings/credentials.ttl`;\n const sparql = buildResetSparql(resource, provider);\n\n const res = await authenticatedFetch(resource, accessToken, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/sparql-update' },\n body: sparql,\n });\n\n if (res.ok) {\n console.log(`Pod AI provider config for ${provider} removed.`);\n } else {\n const text = await res.text();\n console.error(`Failed to reset config: ${res.status} ${text.slice(0, 200)}`);\n process.exit(1);\n }\n },\n};\n\nexport const configCommand: CommandModule<object, ConfigArgs> = {\n command: 'config',\n describe: 'Pod AI provider configuration (provider, model, api-key)',\n builder: (yargs) =>\n yargs\n .option('url', {\n alias: 'u',\n type: 'string',\n description: 'Server base URL (default: from ~/.xpod/)',\n })\n .command(setCommand)\n .command(showCommand)\n .command(resetCommand)\n .demandCommand(1, 'Please specify a config subcommand'),\n handler: () => {},\n};\n"]}